Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/pit.rs
5394 views
1
// Copyright 2019 The ChromiumOS Authors
2
// Use of this source code is governed by a BSD-style license that can be
3
// found in the LICENSE file.
4
5
// Based heavily on GCE VMM's pit.cc.
6
7
use std::io::Error as IoError;
8
use std::sync::Arc;
9
use std::time::Duration;
10
use std::time::Instant;
11
12
use base::error;
13
use base::warn;
14
use base::Descriptor;
15
use base::Error as SysError;
16
use base::Event;
17
use base::EventToken;
18
use base::WaitContext;
19
use bit_field::BitField1;
20
use bit_field::*;
21
use hypervisor::PitChannelState;
22
use hypervisor::PitRWMode;
23
use hypervisor::PitRWState;
24
use hypervisor::PitState;
25
use remain::sorted;
26
use sync::Mutex;
27
use thiserror::Error;
28
use vm_control::DeviceId;
29
use vm_control::PlatformDeviceId;
30
31
cfg_if::cfg_if! {
32
if #[cfg(test)] {
33
use base::FakeClock as Clock;
34
use base::FakeTimer as Timer;
35
} else {
36
use base::Clock;
37
use base::Timer;
38
}
39
}
40
use base::TimerTrait;
41
use base::WorkerThread;
42
use snapshot::AnySnapshot;
43
44
use crate::bus::BusAccessInfo;
45
use crate::BusDevice;
46
use crate::IrqEdgeEvent;
47
use crate::Suspendable;
48
49
// Bitmask for areas of standard (non-ReadBack) Control Word Format. Constant
50
// names are kept the same as Intel PIT data sheet.
51
#[derive(Debug, Clone, Copy, PartialEq, Eq, enumn::N)]
52
enum CommandBit {
53
CommandBCD = 0x01, // Binary/BCD input. x86 only uses binary mode.
54
CommandMode = 0x0e, // Operating Mode (mode 0-5).
55
CommandRW = 0x30, // Access mode: Choose high/low byte(s) to Read/Write.
56
CommandSC = 0xc0, // Select Counter/Read-back command.
57
}
58
59
// Selects which counter is to be used by the associated command in the lower
60
// six bits of the byte. However, if 0xc0 is specified, it indicates that the
61
// command is a "Read-Back", which can latch count and/or status of the
62
// counters selected in the lower bits. See Intel 8254 data sheet for details.
63
#[allow(dead_code)]
64
#[derive(Debug, Clone, Copy, PartialEq, Eq, enumn::N)]
65
enum CommandCounter {
66
CommandCounter0 = 0x00, // Select counter 0.
67
CommandCounter1 = 0x40, // Select counter 1.
68
CommandCounter2 = 0x80, // Select counter 2.
69
CommandReadBack = 0xc0, // Execute Read-Back.
70
}
71
72
// Used for both CommandRW and ReadBackAccess.
73
#[derive(Debug, Clone, Copy, PartialEq, Eq, enumn::N)]
74
enum CommandAccess {
75
CommandLatch = 0x00, // Latch specified counter.
76
CommandRWLeast = 0x10, // Read/Write least significant byte.
77
CommandRWMost = 0x20, // Read/Write most significant byte.
78
CommandRWBoth = 0x30, // Read/Write both bytes.
79
}
80
81
// Used for both CommandMode and ReadBackMode.
82
// For mode 2 & 3, bit 3 is don't care bit (does not matter to be 0 or 1) but
83
// per 8254 spec, should be 0 to insure compatibility with future Intel
84
// products.
85
#[derive(Debug, Clone, Copy, PartialEq, Eq, enumn::N)]
86
enum CommandMode {
87
// NOTE: No h/w modes are currently implemented.
88
CommandInterrupt = 0x00, // Mode 0, interrupt on terminal count.
89
CommandHWOneShot = 0x02, // Mode 1, h/w re-triggerable one-shot.
90
CommandRateGen = 0x04, // Mode 2, rate generator.
91
CommandSquareWaveGen = 0x06, // Mode 3, square wave generator.
92
CommandSWStrobe = 0x08, // Mode 4, s/w triggered strobe.
93
CommandHWStrobe = 0x0a, // Mode 5, h/w triggered strobe.
94
}
95
96
// Bitmask for the latch portion of the ReadBack command.
97
#[derive(Debug, Clone, Copy, PartialEq, Eq, enumn::N)]
98
#[rustfmt::skip] // rustfmt mangles comment indentation for trailing line comments.
99
enum CommandReadBackLatch {
100
CommandRBLatchBits = 0x30, // Mask bits that determine latching.
101
CommandRBLatchBoth = 0x00, // Latch both count and status. This should
102
// never happen in device, since bit 4 and 5 in
103
// read back command are inverted.
104
CommandRBLatchCount = 0x10, // Latch count.
105
CommandRBLatchStatus = 0x20, // Latch status.
106
}
107
108
// Bitmask for the counter portion of the ReadBack command.
109
#[derive(Debug, Clone, Copy, PartialEq, Eq, enumn::N)]
110
enum CommandReadBackCounters {
111
//CommandRBCounters = 0x0e, // Counters for which to provide ReadBack info.
112
CommandRBCounter2 = 0x08,
113
CommandRBCounter1 = 0x04,
114
CommandRBCounter0 = 0x02,
115
}
116
117
// Bitmask for the ReadBack status command.
118
#[derive(Debug, Clone, Copy, PartialEq, Eq, enumn::N)]
119
#[rustfmt::skip] // rustfmt mangles comment indentation for last line of this enum.
120
enum ReadBackData {
121
// Output format for ReadBack command.
122
ReadBackOutput = 0x80, // Output pin status.
123
ReadBackNullCount = 0x40, // Whether counter has value.
124
// ReadBackAccess, ReadBackMode, and ReadBackBCD intentionally omitted.
125
}
126
127
// I/O Port mappings in I/O bus.
128
#[derive(Debug, Clone, Copy, PartialEq, Eq, enumn::N)]
129
enum PortIOSpace {
130
PortCounter0Data = 0x40, // Read/write.
131
PortCounter1Data = 0x41, // Read/write.
132
PortCounter2Data = 0x42, // Read/write.
133
PortCommand = 0x43, // Write only.
134
PortSpeaker = 0x61, // Read/write.
135
}
136
137
#[bitfield]
138
#[derive(Clone, Copy, PartialEq, Eq)]
139
pub struct SpeakerPortFields {
140
// This field is documented in the chipset spec as NMI status and control
141
// register. Bits 2, 3, 6, 7 and low level hardware bits that need no
142
// emulation for virtualized environments. We call it speaker port because
143
// kvm, qemu, linux, and plan9 still call it speaker port, even though it
144
// has these other uses and is called something differently in the spec.
145
gate: BitField1,
146
speaker_on: BitField1,
147
pic_serr: BitField1,
148
iochk_enable: BitField1,
149
// This value changes as part of the refresh frequency of the board for
150
// piix4, this is about 1/15us.
151
refresh_clock: BitField1,
152
output: BitField1,
153
iochk_nmi: BitField1,
154
serr_nmi: BitField1,
155
}
156
157
// PIT frequency (in Hertz). See http://wiki.osdev.org/pit.
158
const FREQUENCY_HZ: u64 = 1193182;
159
160
const NUM_OF_COUNTERS: usize = 3;
161
162
const NANOS_PER_SEC: u64 = 1_000_000_000;
163
164
const MAX_TIMER_FREQ: u32 = 65536;
165
166
#[derive(EventToken)]
167
enum Token {
168
// The timer expired.
169
TimerExpire,
170
// The parent thread requested an exit.
171
Kill,
172
}
173
174
#[sorted]
175
#[derive(Error, Debug)]
176
pub enum PitError {
177
/// Error while cloning event for worker thread.
178
#[error("failed to clone event: {0}")]
179
CloneEvent(SysError),
180
/// Error while creating event.
181
#[error("failed to create event: {0}")]
182
CreateEvent(SysError),
183
/// Creating WaitContext failed.
184
#[error("failed to create poll context: {0}")]
185
CreateWaitContext(SysError),
186
/// Error while trying to create worker thread.
187
#[error("failed to spawn thread: {0}")]
188
SpawnThread(IoError),
189
/// Error while trying to create timer.
190
#[error("failed to create pit counter due to timer fd: {0}")]
191
TimerCreateError(SysError),
192
/// Error while waiting for events.
193
#[error("failed to wait for events: {0}")]
194
WaitError(SysError),
195
}
196
197
type PitResult<T> = std::result::Result<T, PitError>;
198
199
pub struct Pit {
200
// Structs that store each counter's state.
201
counters: Vec<Arc<Mutex<PitCounter>>>,
202
// Worker thread to update counter 0's state asynchronously. Counter 0 needs to send interrupts
203
// when timers expire, so it needs asynchronous updates. All other counters need only update
204
// when queried directly by the guest.
205
worker_thread: Option<WorkerThread<()>>,
206
activated: bool,
207
}
208
209
impl BusDevice for Pit {
210
fn debug_label(&self) -> String {
211
"userspace PIT".to_string()
212
}
213
214
fn device_id(&self) -> DeviceId {
215
PlatformDeviceId::Pit.into()
216
}
217
218
fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
219
self.ensure_started();
220
221
if data.len() != 1 {
222
warn!("Bad write size for Pit: {}", data.len());
223
return;
224
}
225
match PortIOSpace::n(info.address as i64) {
226
Some(PortIOSpace::PortCounter0Data) => self.counters[0].lock().write_counter(data[0]),
227
Some(PortIOSpace::PortCounter1Data) => self.counters[1].lock().write_counter(data[0]),
228
Some(PortIOSpace::PortCounter2Data) => self.counters[2].lock().write_counter(data[0]),
229
Some(PortIOSpace::PortCommand) => self.command_write(data[0]),
230
Some(PortIOSpace::PortSpeaker) => self.counters[2].lock().write_speaker(data[0]),
231
None => warn!("PIT: bad write to {}", info),
232
}
233
}
234
235
fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
236
self.ensure_started();
237
238
if data.len() != 1 {
239
warn!("Bad read size for Pit: {}", data.len());
240
return;
241
}
242
data[0] = match PortIOSpace::n(info.address as i64) {
243
Some(PortIOSpace::PortCounter0Data) => self.counters[0].lock().read_counter(),
244
Some(PortIOSpace::PortCounter1Data) => self.counters[1].lock().read_counter(),
245
Some(PortIOSpace::PortCounter2Data) => self.counters[2].lock().read_counter(),
246
// This should function as a no-op, since the specification doesn't allow the
247
// command register to be read. However, software is free to ask for it to
248
// to be read.
249
Some(PortIOSpace::PortCommand) => {
250
warn!("Ignoring read to command reg");
251
0
252
}
253
Some(PortIOSpace::PortSpeaker) => self.counters[2].lock().read_speaker(),
254
None => {
255
warn!("PIT: bad read from {}", info);
256
return;
257
}
258
};
259
}
260
}
261
262
impl Pit {
263
pub fn new(interrupt_evt: IrqEdgeEvent, clock: Arc<Mutex<Clock>>) -> PitResult<Pit> {
264
let mut counters = Vec::new();
265
let mut interrupt = Some(interrupt_evt);
266
for i in 0..NUM_OF_COUNTERS {
267
let pit_counter = PitCounter::new(i, interrupt, clock.clone())?;
268
counters.push(Arc::new(Mutex::new(pit_counter)));
269
// pass interrupt IrqFd ONLY to counter 0; the rest do not deliver interrupts.
270
interrupt = None;
271
}
272
// We asssert here because:
273
// (a) this code only gets invoked at VM startup
274
// (b) the assert is very loud and would be easy to notice in tests
275
// (c) if we have the wrong number of counters, something is very wrong with the PIT and it
276
// may not make sense to continue operation.
277
assert_eq!(counters.len(), NUM_OF_COUNTERS);
278
279
Ok(Pit {
280
counters,
281
worker_thread: None,
282
activated: false,
283
})
284
}
285
286
fn ensure_started(&mut self) {
287
if self.worker_thread.is_some() {
288
return;
289
}
290
if let Err(e) = self.start() {
291
error!("failed to start PIT: {}", e);
292
}
293
}
294
295
fn start(&mut self) -> PitResult<()> {
296
let pit_counter = self.counters[0].clone();
297
self.worker_thread = Some(WorkerThread::start("pit counter worker", move |kill_evt| {
298
if let Err(e) = worker_run(kill_evt, pit_counter) {
299
error!("pit worker failed: {e:#}");
300
}
301
}));
302
self.activated = true;
303
Ok(())
304
}
305
306
fn command_write(&mut self, control_word: u8) {
307
let command: u16 = (control_word & CommandBit::CommandSC as u8).into();
308
let counter_index: usize = (command >> 6).into();
309
if command == (CommandCounter::CommandReadBack as u16) {
310
// ReadBack commands can apply to multiple counters.
311
if (control_word & (CommandReadBackCounters::CommandRBCounter0 as u8)) != 0 {
312
self.counters[0].lock().read_back_command(control_word);
313
}
314
if (control_word & (CommandReadBackCounters::CommandRBCounter1 as u8)) != 0 {
315
self.counters[1].lock().read_back_command(control_word);
316
}
317
if (control_word & (CommandReadBackCounters::CommandRBCounter2 as u8)) != 0 {
318
self.counters[2].lock().read_back_command(control_word);
319
}
320
} else if (control_word & (CommandBit::CommandRW as u8))
321
== (CommandAccess::CommandLatch as u8)
322
{
323
self.counters[counter_index].lock().latch_counter();
324
} else {
325
self.counters[counter_index]
326
.lock()
327
.store_command(control_word);
328
}
329
}
330
331
pub fn get_pit_state(&self) -> PitState {
332
PitState {
333
channels: [
334
self.counters[0].lock().get_channel_state(),
335
self.counters[1].lock().get_channel_state(),
336
self.counters[2].lock().get_channel_state(),
337
],
338
flags: 0,
339
}
340
}
341
342
pub fn set_pit_state(&mut self, state: &PitState) {
343
self.counters[0]
344
.lock()
345
.set_channel_state(&state.channels[0]);
346
self.counters[1]
347
.lock()
348
.set_channel_state(&state.channels[1]);
349
self.counters[2]
350
.lock()
351
.set_channel_state(&state.channels[2]);
352
}
353
}
354
355
impl Suspendable for Pit {
356
fn sleep(&mut self) -> anyhow::Result<()> {
357
if let Some(thread) = self.worker_thread.take() {
358
thread.stop();
359
}
360
Ok(())
361
}
362
363
fn wake(&mut self) -> anyhow::Result<()> {
364
if self.activated {
365
if let Err(e) = self.start() {
366
error!("failed to start PIT: {}", e);
367
}
368
}
369
Ok(())
370
}
371
372
/// The PIT is only used in very early boot on x86_64, and snapshots are not
373
/// generally taken during that time, so we can safely skip the PIT for now.
374
fn snapshot(&mut self) -> anyhow::Result<AnySnapshot> {
375
AnySnapshot::to_any(())
376
}
377
378
/// The PIT is only used in very early boot on x86_64, and snapshots are not
379
/// generally taken during that time, so we can safely skip the PIT for now.
380
fn restore(&mut self, _data: AnySnapshot) -> anyhow::Result<()> {
381
Ok(())
382
}
383
}
384
385
// Each instance of this represents one of the PIT counters. They are used to
386
// implement one-shot and repeating timer alarms. An 8254 has three counters.
387
struct PitCounter {
388
// Event to write when asserting an interrupt.
389
interrupt_evt: Option<IrqEdgeEvent>,
390
// Stores the value with which the counter was initialized. Counters are 16-
391
// bit values with an effective range of 1-65536 (65536 represented by 0).
392
reload_value: u16,
393
// Stores value when latch was called.
394
latched_value: u16,
395
// Stores last command from command register.
396
command: u8,
397
// Stores status from readback command
398
status: u8,
399
// Stores time of starting timer. Used for calculating remaining count, if an alarm is
400
// scheduled.
401
start: Option<Instant>,
402
// Current time.
403
clock: Arc<Mutex<Clock>>,
404
// Time when object was created. Used for a 15us counter.
405
creation_time: Instant,
406
// The number of the counter. The behavior for each counter is slightly different.
407
// Note that once a PitCounter is created, this value should never change.
408
counter_id: usize,
409
// Indicates if the low byte has been written in RWBoth.
410
wrote_low_byte: bool,
411
// Indicates if the low byte has been read in RWBoth.
412
read_low_byte: bool,
413
// Indicates whether counter has been latched.
414
latched: bool,
415
// Indicates whether ReadBack status has been latched.
416
status_latched: bool,
417
// Only should be used for counter 2. See http://wiki.osdev.org/PIT.
418
gate: bool,
419
speaker_on: bool,
420
// The starting value for the counter.
421
count: u32,
422
// Indicates whether the current timer is valid.
423
timer_valid: bool,
424
// Timer to set and receive periodic notifications.
425
timer: Box<dyn TimerTrait>,
426
}
427
428
impl Drop for PitCounter {
429
fn drop(&mut self) {
430
if self.timer_valid {
431
// This should not fail - timer.clear() only fails if timerfd_settime fails, which
432
// only happens due to invalid arguments or bad file descriptors. The arguments to
433
// timerfd_settime are constant, so its arguments won't be invalid, and it manages
434
// the file descriptor safely (we don't use the unsafe FromRawDescriptor) so its file
435
// descriptor will be valid.
436
self.timer.clear().unwrap();
437
}
438
}
439
}
440
441
fn adjust_count(count: u32) -> u32 {
442
// As per spec 0 means max.
443
if count == 0 {
444
MAX_TIMER_FREQ
445
} else {
446
count
447
}
448
}
449
450
impl PitCounter {
451
fn new(
452
counter_id: usize,
453
interrupt_evt: Option<IrqEdgeEvent>,
454
clock: Arc<Mutex<Clock>>,
455
) -> PitResult<PitCounter> {
456
#[cfg(not(test))]
457
let timer = Timer::new().map_err(PitError::TimerCreateError)?;
458
#[cfg(test)]
459
let timer = Timer::new(clock.clone());
460
Ok(PitCounter {
461
interrupt_evt,
462
reload_value: 0,
463
latched_value: 0,
464
command: 0,
465
status: 0,
466
start: None,
467
clock: clock.clone(),
468
creation_time: clock.lock().now(),
469
counter_id,
470
wrote_low_byte: false,
471
read_low_byte: false,
472
latched: false,
473
status_latched: false,
474
gate: false,
475
speaker_on: false,
476
// `count` is undefined in real hardware and can't ever be programmed to 0, so we
477
// initialize it to max to prevent a misbehaving guest from triggering a divide by 0.
478
count: MAX_TIMER_FREQ,
479
timer_valid: false,
480
timer: Box::new(timer),
481
})
482
}
483
484
fn get_channel_state(&self) -> PitChannelState {
485
let load_time = match &self.start {
486
Some(t) => t.saturating_duration_since(self.creation_time).as_nanos() as u64,
487
None => 0,
488
};
489
490
let mut state = PitChannelState {
491
count: self.count,
492
latched_count: self.latched_value,
493
status_latched: self.status_latched,
494
status: self.status,
495
reload_value: self.reload_value,
496
mode: (self.command & CommandBit::CommandMode as u8) >> 1,
497
bcd: false,
498
gate: self.gate,
499
count_load_time: load_time,
500
rw_mode: PitRWMode::None,
501
read_state: PitRWState::None,
502
write_state: PitRWState::None,
503
count_latched: PitRWState::None,
504
};
505
506
match self.get_access_mode() {
507
Some(CommandAccess::CommandRWLeast) => {
508
// If access mode is least, RWStates are always LSB
509
state.rw_mode = PitRWMode::Least;
510
state.read_state = PitRWState::LSB;
511
state.write_state = PitRWState::LSB;
512
}
513
Some(CommandAccess::CommandRWMost) => {
514
// If access mode is most, RWStates are always MSB
515
state.rw_mode = PitRWMode::Most;
516
state.read_state = PitRWState::MSB;
517
state.write_state = PitRWState::MSB;
518
}
519
Some(CommandAccess::CommandRWBoth) => {
520
state.rw_mode = PitRWMode::Both;
521
// read_state depends on whether or not we've read the low byte already
522
state.read_state = if self.read_low_byte {
523
PitRWState::Word1
524
} else {
525
PitRWState::Word0
526
};
527
// write_state depends on whether or not we've written the low byte already
528
state.write_state = if self.wrote_low_byte {
529
PitRWState::Word1
530
} else {
531
PitRWState::Word0
532
};
533
}
534
_ => {}
535
};
536
537
// Count_latched should be PitRWSTate::None unless we're latched
538
if self.latched {
539
state.count_latched = state.read_state;
540
}
541
542
state
543
}
544
545
fn set_channel_state(&mut self, state: &PitChannelState) {
546
self.count = state.count;
547
self.latched_value = state.latched_count;
548
self.status_latched = state.status_latched;
549
self.status = state.status;
550
self.reload_value = state.reload_value;
551
552
// the command consists of:
553
// - 1 bcd bit, which we don't care about because we don't support non-binary mode
554
// - 3 mode bits
555
// - 2 access mode bits
556
// - 2 counter select bits, which aren't used by the counter/channel itself
557
self.command = (state.mode << 1) | ((state.rw_mode as u8) << 4);
558
self.gate = state.gate;
559
self.latched = state.count_latched != PitRWState::None;
560
self.read_low_byte = state.read_state == PitRWState::Word1;
561
self.wrote_low_byte = state.write_state == PitRWState::Word1;
562
563
self.start = self
564
.creation_time
565
.checked_add(Duration::from_nanos(state.count_load_time));
566
}
567
568
fn get_access_mode(&self) -> Option<CommandAccess> {
569
CommandAccess::n(self.command & (CommandBit::CommandRW as u8))
570
}
571
572
fn get_command_mode(&self) -> Option<CommandMode> {
573
CommandMode::n(self.command & CommandBit::CommandMode as u8)
574
}
575
576
fn read_counter(&mut self) -> u8 {
577
if self.status_latched {
578
self.status_latched = false;
579
return self.status;
580
};
581
let data_value: u16 = if self.latched {
582
self.latched_value
583
} else {
584
self.get_read_value()
585
};
586
587
let access_mode = self.get_access_mode();
588
// Latch may be true without being indicated by the access mode if
589
// a ReadBack was issued.
590
match (access_mode, self.read_low_byte) {
591
(Some(CommandAccess::CommandRWLeast), _) => {
592
self.latched = false; // Unlatch if only reading the low byte.
593
(data_value & 0xff) as u8
594
}
595
(Some(CommandAccess::CommandRWBoth), false) => {
596
self.read_low_byte = true;
597
(data_value & 0xff) as u8
598
}
599
(Some(CommandAccess::CommandRWBoth), true)
600
| (Some(CommandAccess::CommandRWMost), _) => {
601
self.read_low_byte = false; // Allow for future reads for RWBoth.
602
self.latched = false;
603
(data_value >> 8) as u8
604
}
605
(_, _) => 0, // Default for erroneous call
606
}
607
}
608
609
fn write_counter(&mut self, written_datum: u8) {
610
let access_mode = self.get_access_mode();
611
let datum: u16 = written_datum.into();
612
let mut should_start_timer = true;
613
self.reload_value = match access_mode {
614
Some(CommandAccess::CommandRWLeast) => datum,
615
Some(CommandAccess::CommandRWMost) => datum << 8,
616
Some(CommandAccess::CommandRWBoth) => {
617
// In kCommandRWBoth mode, the first guest write is the low byte and the
618
// the second guest write is the high byte. The timer isn't started
619
// until after the second byte is written.
620
if self.wrote_low_byte {
621
self.wrote_low_byte = false;
622
self.reload_value | (datum << 8)
623
} else {
624
self.wrote_low_byte = true;
625
should_start_timer = false; // Don't start until high byte written.
626
datum
627
}
628
}
629
_ => {
630
should_start_timer = false;
631
self.reload_value
632
}
633
};
634
if should_start_timer {
635
let reload: u32 = self.reload_value.into();
636
self.load_and_start_timer(reload);
637
}
638
}
639
640
fn get_output(&self) -> bool {
641
let ticks_passed = self.get_ticks_passed();
642
let count: u64 = self.count.into();
643
match self.get_command_mode() {
644
Some(CommandMode::CommandInterrupt) => ticks_passed >= count,
645
Some(CommandMode::CommandHWOneShot) => ticks_passed < count,
646
Some(CommandMode::CommandRateGen) => ticks_passed != 0 && ticks_passed % count == 0,
647
Some(CommandMode::CommandSquareWaveGen) => ticks_passed < count.div_ceil(2),
648
Some(CommandMode::CommandSWStrobe) | Some(CommandMode::CommandHWStrobe) => {
649
ticks_passed == count
650
}
651
None => {
652
warn!("Invalid command mode based on command: {:#x}", self.command);
653
false
654
}
655
}
656
}
657
658
fn read_speaker(&self) -> u8 {
659
// Refresh clock is a value independent of the actual
660
// counter that goes up and down approx every 15 us (~66000/s).
661
let us = self
662
.clock
663
.lock()
664
.now()
665
.duration_since(self.creation_time)
666
.subsec_micros();
667
let refresh_clock = us % 15 == 0;
668
let mut speaker = SpeakerPortFields::new();
669
speaker.set_gate(self.gate.into());
670
speaker.set_speaker_on(self.speaker_on.into());
671
speaker.set_iochk_enable(0);
672
speaker.set_refresh_clock(refresh_clock.into());
673
speaker.set_output(self.get_output().into());
674
speaker.set_iochk_nmi(0);
675
speaker.set_serr_nmi(0);
676
speaker.get(/* offset= */ 0, /* width= */ 8) as u8
677
}
678
679
fn write_speaker(&mut self, datum: u8) {
680
let mut speaker = SpeakerPortFields::new();
681
speaker.set(/* offset= */ 0, /* width= */ 8, datum.into());
682
let new_gate = speaker.get_gate() != 0;
683
match self.get_command_mode() {
684
Some(CommandMode::CommandInterrupt) | Some(CommandMode::CommandSWStrobe) => (),
685
Some(_) => {
686
if new_gate && !self.gate {
687
self.start = Some(self.clock.lock().now());
688
}
689
}
690
None => {
691
warn!("Invalid command mode based on command {:#x}", self.command);
692
return;
693
}
694
}
695
self.speaker_on = speaker.get_speaker_on() != 0;
696
self.gate = new_gate;
697
}
698
699
fn load_and_start_timer(&mut self, initial_count: u32) {
700
self.count = adjust_count(initial_count);
701
self.start_timer();
702
}
703
704
fn start_timer(&mut self) {
705
self.start = Some(self.clock.lock().now());
706
707
// Counter 0 is the only counter that generates interrupts, so we
708
// don't need to set a timer for the other two counters.
709
if self.counter_id != 0 {
710
return;
711
}
712
713
let timer_len = Duration::from_nanos(u64::from(self.count) * NANOS_PER_SEC / FREQUENCY_HZ);
714
let safe_timer_len = if timer_len == Duration::new(0, 0) {
715
Duration::from_nanos(1)
716
} else {
717
timer_len
718
};
719
720
match self.get_command_mode() {
721
Some(CommandMode::CommandInterrupt)
722
| Some(CommandMode::CommandHWOneShot)
723
| Some(CommandMode::CommandSWStrobe)
724
| Some(CommandMode::CommandHWStrobe) => {
725
if let Err(e) = self.timer.reset_oneshot(safe_timer_len) {
726
error!("failed to reset oneshot timer: {}", e);
727
}
728
}
729
Some(CommandMode::CommandRateGen) | Some(CommandMode::CommandSquareWaveGen) => {
730
if let Err(e) = self.timer.reset_repeating(safe_timer_len) {
731
error!("failed to reset repeating timer: {}", e);
732
}
733
}
734
// Don't arm timer if invalid mode.
735
None => {
736
// This will still result in start being set to the current time.
737
// Per spec:
738
// A new initial count may be written to a Counter at any time without affecting
739
// the Counter’s programmed Mode in any way. Counting will be affected as
740
// described in the Mode definitions. The new count must follow the programmed
741
// count format
742
// It's unclear whether setting `self.start` in this case is entirely compliant,
743
// but the spec is fairly quiet on expected behavior in error cases, so OSs
744
// shouldn't enter invalid modes in the first place. If they do, and then try to
745
// get out of it by first setting the counter then the command, this behavior will
746
// (perhaps) be minimally surprising, but arguments can be made for other behavior.
747
// It's uncertain if this behavior matches real PIT hardware.
748
warn!("Invalid command mode based on command {:#x}", self.command);
749
return;
750
}
751
}
752
753
self.timer_valid = true;
754
}
755
756
fn read_back_command(&mut self, control_word: u8) {
757
let latch_cmd =
758
CommandReadBackLatch::n(control_word & CommandReadBackLatch::CommandRBLatchBits as u8);
759
match latch_cmd {
760
Some(CommandReadBackLatch::CommandRBLatchCount) => {
761
self.latch_counter();
762
}
763
Some(CommandReadBackLatch::CommandRBLatchStatus) => {
764
self.latch_status();
765
}
766
_ => warn!(
767
"Unexpected ReadBackLatch. control_word: {:#x}",
768
control_word
769
),
770
};
771
}
772
773
fn latch_counter(&mut self) {
774
if self.latched {
775
return;
776
}
777
778
self.latched_value = self.get_read_value();
779
self.latched = true;
780
self.read_low_byte = false;
781
}
782
783
fn latch_status(&mut self) {
784
// Including BCD here, even though it currently never gets used.
785
self.status = self.command
786
& (CommandBit::CommandRW as u8
787
| CommandBit::CommandMode as u8
788
| CommandBit::CommandBCD as u8);
789
if self.start.is_none() {
790
self.status |= ReadBackData::ReadBackNullCount as u8;
791
}
792
if self.get_output() {
793
self.status |= ReadBackData::ReadBackOutput as u8;
794
}
795
self.status_latched = true;
796
}
797
798
fn store_command(&mut self, datum: u8) {
799
self.command = datum;
800
self.latched = false;
801
802
// If a new RW command is written, cancel the current timer.
803
if self.timer_valid {
804
self.start = None;
805
self.timer_valid = false;
806
// See the comment in the impl of Drop for PitCounter for justification of the unwrap()
807
self.timer.clear().unwrap();
808
}
809
810
self.wrote_low_byte = false;
811
self.read_low_byte = false;
812
}
813
814
fn timer_handler(&mut self) {
815
if let Err(e) = self.timer.mark_waited() {
816
// Under the current Timer implementation (as of Jan 2019), this failure shouldn't
817
// happen but implementation details may change in the future, and the failure
818
// cases are complex to reason about. Because of this, avoid unwrap().
819
error!("pit: timer wait unexpectedly failed: {}", e);
820
return;
821
}
822
let mode = self.get_command_mode();
823
if mode == Some(CommandMode::CommandRateGen)
824
|| mode == Some(CommandMode::CommandSquareWaveGen)
825
{
826
// Reset the start time for timer modes that repeat.
827
self.start = Some(self.clock.lock().now());
828
}
829
830
// For square wave mode, this isn't quite accurate to the spec, but the
831
// difference isn't meaningfully visible to the guest in any important way,
832
// and the code is simpler without the special case.
833
if let Some(interrupt) = &mut self.interrupt_evt {
834
// This is safe because the file descriptor is nonblocking and we're writing 1.
835
interrupt.trigger().unwrap();
836
}
837
}
838
839
fn get_ticks_passed(&self) -> u64 {
840
match self.start {
841
None => 0,
842
Some(t) => {
843
let dur = self.clock.lock().now().duration_since(t);
844
let dur_ns: u64 = dur.as_secs() * NANOS_PER_SEC + u64::from(dur.subsec_nanos());
845
dur_ns * FREQUENCY_HZ / NANOS_PER_SEC
846
}
847
}
848
}
849
850
fn get_read_value(&self) -> u16 {
851
match self.start {
852
None => 0,
853
Some(_) => {
854
let count: u64 = adjust_count(self.reload_value.into()).into();
855
let ticks_passed = self.get_ticks_passed();
856
match self.get_command_mode() {
857
Some(CommandMode::CommandInterrupt)
858
| Some(CommandMode::CommandHWOneShot)
859
| Some(CommandMode::CommandSWStrobe)
860
| Some(CommandMode::CommandHWStrobe) => {
861
if ticks_passed > count {
862
// Some risk of raciness here in that the count may return a value
863
// indicating that the count has expired when the interrupt hasn't
864
// yet been injected.
865
0
866
} else {
867
((count - ticks_passed) & 0xFFFF) as u16
868
}
869
}
870
Some(CommandMode::CommandRateGen) => (count - (ticks_passed % count)) as u16,
871
Some(CommandMode::CommandSquareWaveGen) => {
872
(count - ((ticks_passed * 2) % count)) as u16
873
}
874
None => {
875
warn!("Invalid command mode: command = {:#x}", self.command);
876
0
877
}
878
}
879
}
880
}
881
}
882
}
883
884
fn worker_run(kill_evt: Event, pit_counter: Arc<Mutex<PitCounter>>) -> PitResult<()> {
885
let timer_descriptor = Descriptor(pit_counter.lock().timer.as_raw_descriptor());
886
let wait_ctx: WaitContext<Token> = WaitContext::build_with(&[
887
(&timer_descriptor, Token::TimerExpire),
888
(&kill_evt, Token::Kill),
889
])
890
.map_err(PitError::CreateWaitContext)?;
891
892
loop {
893
let events = wait_ctx.wait().map_err(PitError::WaitError)?;
894
for event in events.iter().filter(|e| e.is_readable) {
895
match event.token {
896
Token::TimerExpire => {
897
let mut pit = pit_counter.lock();
898
pit.timer_handler();
899
}
900
Token::Kill => return Ok(()),
901
}
902
}
903
}
904
}
905
906
#[cfg(test)]
907
mod tests {
908
use base::Event;
909
910
use super::*;
911
912
struct TestData {
913
pit: Pit,
914
irqfd: Event,
915
clock: Arc<Mutex<Clock>>,
916
}
917
918
fn pit_bus_address(address: PortIOSpace) -> BusAccessInfo {
919
// The PIT is added to the io_bus in two locations, so the offset depends on which
920
// address range the address is in. The PIT implementation currently does not use the
921
// offset, but we're setting it accurately here in case it does in the future.
922
let offset = match address as u64 {
923
x if x >= PortIOSpace::PortCounter0Data as u64
924
&& x < PortIOSpace::PortCounter0Data as u64 + 0x8 =>
925
{
926
address as u64 - PortIOSpace::PortCounter0Data as u64
927
}
928
x if x == PortIOSpace::PortSpeaker as u64 => 0,
929
_ => panic!("invalid PIT address: {:#x}", address as u64),
930
};
931
932
BusAccessInfo {
933
offset,
934
address: address as u64,
935
id: 0,
936
}
937
}
938
939
/// Utility method for writing a command word to a command register.
940
fn write_command(pit: &mut Pit, command: u8) {
941
pit.write(pit_bus_address(PortIOSpace::PortCommand), &[command])
942
}
943
944
/// Utility method for writing a command word to the speaker register.
945
fn write_speaker(pit: &mut Pit, command: u8) {
946
pit.write(pit_bus_address(PortIOSpace::PortSpeaker), &[command])
947
}
948
949
/// Utility method for writing to a counter.
950
fn write_counter(pit: &mut Pit, counter_idx: usize, data: u16, access_mode: CommandAccess) {
951
let port = match counter_idx {
952
0 => PortIOSpace::PortCounter0Data,
953
1 => PortIOSpace::PortCounter1Data,
954
2 => PortIOSpace::PortCounter2Data,
955
_ => panic!("Invalid counter_idx: {counter_idx}"),
956
};
957
// Write the least, then the most, significant byte.
958
if access_mode == CommandAccess::CommandRWLeast
959
|| access_mode == CommandAccess::CommandRWBoth
960
{
961
pit.write(pit_bus_address(port), &[(data & 0xff) as u8]);
962
}
963
if access_mode == CommandAccess::CommandRWMost
964
|| access_mode == CommandAccess::CommandRWBoth
965
{
966
pit.write(pit_bus_address(port), &[(data >> 8) as u8]);
967
}
968
}
969
970
/// Utility method for reading a counter. Check if the read value matches expected_value.
971
fn read_counter(pit: &mut Pit, counter_idx: usize, expected: u16, access_mode: CommandAccess) {
972
let port = match counter_idx {
973
0 => PortIOSpace::PortCounter0Data,
974
1 => PortIOSpace::PortCounter1Data,
975
2 => PortIOSpace::PortCounter2Data,
976
_ => panic!("Invalid counter_idx: {counter_idx}"),
977
};
978
let mut result: u16 = 0;
979
if access_mode == CommandAccess::CommandRWLeast
980
|| access_mode == CommandAccess::CommandRWBoth
981
{
982
let mut buffer = [0];
983
pit.read(pit_bus_address(port), &mut buffer);
984
result = buffer[0].into();
985
}
986
if access_mode == CommandAccess::CommandRWMost
987
|| access_mode == CommandAccess::CommandRWBoth
988
{
989
let mut buffer = [0];
990
pit.read(pit_bus_address(port), &mut buffer);
991
result |= u16::from(buffer[0]) << 8;
992
}
993
assert_eq!(result, expected);
994
}
995
996
fn set_up() -> TestData {
997
let evt = IrqEdgeEvent::new().unwrap();
998
let clock = Arc::new(Mutex::new(Clock::new()));
999
TestData {
1000
irqfd: evt.get_trigger().try_clone().unwrap(),
1001
pit: Pit::new(evt, clock.clone()).unwrap(),
1002
clock,
1003
}
1004
}
1005
1006
fn advance_by_tick(data: &mut TestData) {
1007
advance_by_ticks(data, 1);
1008
}
1009
1010
fn advance_by_ticks(data: &mut TestData, ticks: u64) {
1011
println!(
1012
"Advancing by {:#x} ticks ({} ns)",
1013
ticks,
1014
(NANOS_PER_SEC * ticks) / FREQUENCY_HZ + 1
1015
);
1016
let mut lock = data.clock.lock();
1017
lock.add_ns((NANOS_PER_SEC * ticks) / FREQUENCY_HZ + 1);
1018
}
1019
1020
/// Tests the ability to write a command and data and read the data back using latch.
1021
#[test]
1022
fn write_and_latch() {
1023
let mut data = set_up();
1024
let both_interrupt =
1025
CommandAccess::CommandRWBoth as u8 | CommandMode::CommandInterrupt as u8;
1026
// Issue a command to write both digits of counter 0 in interrupt mode.
1027
write_command(
1028
&mut data.pit,
1029
CommandCounter::CommandCounter0 as u8 | both_interrupt,
1030
);
1031
write_counter(&mut data.pit, 0, 24, CommandAccess::CommandRWBoth);
1032
// Advance time by one tick -- value read back should decrease.
1033
advance_by_tick(&mut data);
1034
1035
// Latch and read back the value written.
1036
write_command(
1037
&mut data.pit,
1038
CommandCounter::CommandCounter0 as u8 | CommandAccess::CommandLatch as u8,
1039
);
1040
// Advance again after latching to verify that value read back doesn't change.
1041
advance_by_tick(&mut data);
1042
read_counter(&mut data.pit, 0, 23, CommandAccess::CommandRWBoth);
1043
1044
// Repeat with counter 1.
1045
write_command(
1046
&mut data.pit,
1047
CommandCounter::CommandCounter1 as u8 | both_interrupt,
1048
);
1049
write_counter(&mut data.pit, 1, 314, CommandAccess::CommandRWBoth);
1050
advance_by_tick(&mut data);
1051
write_command(
1052
&mut data.pit,
1053
CommandCounter::CommandCounter1 as u8 | CommandAccess::CommandLatch as u8,
1054
);
1055
advance_by_tick(&mut data);
1056
read_counter(&mut data.pit, 1, 313, CommandAccess::CommandRWBoth);
1057
1058
// Repeat with counter 2.
1059
write_command(
1060
&mut data.pit,
1061
CommandCounter::CommandCounter2 as u8 | both_interrupt,
1062
);
1063
write_counter(&mut data.pit, 2, 0xffff, CommandAccess::CommandRWBoth);
1064
advance_by_tick(&mut data);
1065
write_command(
1066
&mut data.pit,
1067
CommandCounter::CommandCounter2 as u8 | CommandAccess::CommandLatch as u8,
1068
);
1069
advance_by_tick(&mut data);
1070
read_counter(&mut data.pit, 2, 0xfffe, CommandAccess::CommandRWBoth);
1071
}
1072
1073
/// Tests the ability to read only the least significant byte.
1074
#[test]
1075
fn write_and_read_least() {
1076
let mut data = set_up();
1077
write_command(
1078
&mut data.pit,
1079
CommandCounter::CommandCounter0 as u8
1080
| CommandAccess::CommandRWLeast as u8
1081
| CommandMode::CommandInterrupt as u8,
1082
);
1083
write_counter(&mut data.pit, 0, 0x3424, CommandAccess::CommandRWLeast);
1084
read_counter(&mut data.pit, 0, 0x0024, CommandAccess::CommandRWLeast);
1085
write_command(
1086
&mut data.pit,
1087
CommandCounter::CommandCounter0 as u8 | CommandAccess::CommandLatch as u8,
1088
);
1089
advance_by_tick(&mut data);
1090
read_counter(&mut data.pit, 0, 0x0024, CommandAccess::CommandRWLeast);
1091
}
1092
1093
/// Tests the ability to read only the most significant byte.
1094
#[test]
1095
fn write_and_read_most() {
1096
let mut data = set_up();
1097
write_command(
1098
&mut data.pit,
1099
CommandCounter::CommandCounter0 as u8
1100
| CommandAccess::CommandRWMost as u8
1101
| CommandMode::CommandInterrupt as u8,
1102
);
1103
write_counter(&mut data.pit, 0, 0x3424, CommandAccess::CommandRWMost);
1104
read_counter(&mut data.pit, 0, 0x3400, CommandAccess::CommandRWMost);
1105
write_command(
1106
&mut data.pit,
1107
CommandCounter::CommandCounter0 as u8 | CommandAccess::CommandLatch as u8,
1108
);
1109
advance_by_tick(&mut data);
1110
read_counter(&mut data.pit, 0, 0x3400, CommandAccess::CommandRWMost);
1111
}
1112
1113
/// Tests that reading the command register does nothing.
1114
#[test]
1115
fn read_command() {
1116
let mut data = set_up();
1117
let mut buf = [0];
1118
data.pit
1119
.read(pit_bus_address(PortIOSpace::PortCommand), &mut buf);
1120
assert_eq!(buf, [0]);
1121
}
1122
1123
/// Tests that latching prevents the read time from actually advancing.
1124
#[test]
1125
fn test_timed_latch() {
1126
let mut data = set_up();
1127
write_command(
1128
&mut data.pit,
1129
CommandCounter::CommandCounter0 as u8
1130
| CommandAccess::CommandRWBoth as u8
1131
| CommandMode::CommandInterrupt as u8,
1132
);
1133
write_counter(&mut data.pit, 0, 0xffff, CommandAccess::CommandRWBoth);
1134
write_command(
1135
&mut data.pit,
1136
CommandCounter::CommandCounter0 as u8 | CommandAccess::CommandLatch as u8,
1137
);
1138
data.clock.lock().add_ns(25_000_000);
1139
// The counter should ignore this second latch.
1140
write_command(
1141
&mut data.pit,
1142
CommandCounter::CommandCounter0 as u8 | CommandAccess::CommandLatch as u8,
1143
);
1144
read_counter(&mut data.pit, 0, 0xffff, CommandAccess::CommandRWBoth);
1145
// It should, however, store the count for this latch.
1146
write_command(
1147
&mut data.pit,
1148
CommandCounter::CommandCounter0 as u8 | CommandAccess::CommandLatch as u8,
1149
);
1150
read_counter(
1151
&mut data.pit,
1152
0,
1153
0xffff - ((25_000_000 * FREQUENCY_HZ) / NANOS_PER_SEC) as u16,
1154
CommandAccess::CommandRWBoth,
1155
);
1156
}
1157
1158
/// Tests Mode 0 (Interrupt on terminal count); checks whether IRQ has been asserted.
1159
#[test]
1160
fn interrupt_mode() {
1161
let mut data = set_up();
1162
write_command(
1163
&mut data.pit,
1164
CommandCounter::CommandCounter0 as u8
1165
| CommandAccess::CommandRWBoth as u8
1166
| CommandMode::CommandInterrupt as u8,
1167
);
1168
write_counter(&mut data.pit, 0, 0xffff, CommandAccess::CommandRWBoth);
1169
// Advance clock enough to trigger interrupt.
1170
advance_by_ticks(&mut data, 0xffff);
1171
data.irqfd.wait().unwrap();
1172
}
1173
1174
/// Tests that Rate Generator mode (mode 2) handls the interrupt properly when the timer
1175
/// expires and that it resets the timer properly.
1176
#[test]
1177
fn rate_gen_mode() {
1178
let mut data = set_up();
1179
write_command(
1180
&mut data.pit,
1181
CommandCounter::CommandCounter0 as u8
1182
| CommandAccess::CommandRWBoth as u8
1183
| CommandMode::CommandRateGen as u8,
1184
);
1185
write_counter(&mut data.pit, 0, 0xffff, CommandAccess::CommandRWBoth);
1186
// Repatedly advance clock and expect interrupt.
1187
advance_by_ticks(&mut data, 0xffff);
1188
data.irqfd.wait().unwrap();
1189
1190
// Repatedly advance clock and expect interrupt.
1191
advance_by_ticks(&mut data, 0xffff);
1192
data.irqfd.wait().unwrap();
1193
1194
// Repatedly advance clock and expect interrupt.
1195
advance_by_ticks(&mut data, 0xffff);
1196
data.irqfd.wait().unwrap();
1197
}
1198
1199
/// Tests that square wave mode advances the counter correctly.
1200
#[test]
1201
fn square_wave_counter_read() {
1202
let mut data = set_up();
1203
write_command(
1204
&mut data.pit,
1205
CommandCounter::CommandCounter0 as u8
1206
| CommandAccess::CommandRWBoth as u8
1207
| CommandMode::CommandSquareWaveGen as u8,
1208
);
1209
write_counter(&mut data.pit, 0, 0xffff, CommandAccess::CommandRWBoth);
1210
1211
advance_by_ticks(&mut data, 10_000);
1212
read_counter(
1213
&mut data.pit,
1214
0,
1215
0xffff - 10_000 * 2,
1216
CommandAccess::CommandRWBoth,
1217
);
1218
}
1219
1220
/// Tests that rategen mode updates the counter correctly.
1221
#[test]
1222
fn rate_gen_counter_read() {
1223
let mut data = set_up();
1224
write_command(
1225
&mut data.pit,
1226
CommandCounter::CommandCounter0 as u8
1227
| CommandAccess::CommandRWBoth as u8
1228
| CommandMode::CommandRateGen as u8,
1229
);
1230
write_counter(&mut data.pit, 0, 0xffff, CommandAccess::CommandRWBoth);
1231
1232
advance_by_ticks(&mut data, 10_000);
1233
read_counter(
1234
&mut data.pit,
1235
0,
1236
0xffff - 10_000,
1237
CommandAccess::CommandRWBoth,
1238
);
1239
}
1240
1241
/// Tests that interrupt counter mode updates the counter correctly.
1242
#[test]
1243
fn interrupt_counter_read() {
1244
let mut data = set_up();
1245
write_command(
1246
&mut data.pit,
1247
CommandCounter::CommandCounter0 as u8
1248
| CommandAccess::CommandRWBoth as u8
1249
| CommandMode::CommandInterrupt as u8,
1250
);
1251
write_counter(&mut data.pit, 0, 0xffff, CommandAccess::CommandRWBoth);
1252
1253
advance_by_ticks(&mut data, 10_000);
1254
read_counter(
1255
&mut data.pit,
1256
0,
1257
0xffff - 10_000,
1258
CommandAccess::CommandRWBoth,
1259
);
1260
1261
advance_by_ticks(&mut data, 3 * FREQUENCY_HZ);
1262
read_counter(&mut data.pit, 0, 0, CommandAccess::CommandRWBoth);
1263
}
1264
1265
/// Tests that ReadBack count works properly for `low` access mode.
1266
#[test]
1267
fn read_back_count_access_low() {
1268
let mut data = set_up();
1269
write_command(
1270
&mut data.pit,
1271
CommandCounter::CommandCounter0 as u8
1272
| CommandAccess::CommandRWLeast as u8
1273
| CommandMode::CommandInterrupt as u8,
1274
);
1275
write_counter(&mut data.pit, 0, 0xffff, CommandAccess::CommandRWLeast);
1276
write_command(
1277
&mut data.pit,
1278
CommandCounter::CommandReadBack as u8
1279
| CommandReadBackLatch::CommandRBLatchCount as u8
1280
| CommandReadBackCounters::CommandRBCounter0 as u8,
1281
);
1282
1283
// Advance 100 ticks and verify that low byte of counter is appropriately updated.
1284
advance_by_ticks(&mut data, 100);
1285
write_command(
1286
&mut data.pit,
1287
CommandCounter::CommandReadBack as u8
1288
| CommandReadBackLatch::CommandRBLatchCount as u8
1289
| CommandReadBackCounters::CommandRBCounter0 as u8,
1290
);
1291
read_counter(&mut data.pit, 0, 0x00ff, CommandAccess::CommandRWLeast);
1292
write_command(
1293
&mut data.pit,
1294
CommandCounter::CommandReadBack as u8
1295
| CommandReadBackLatch::CommandRBLatchCount as u8
1296
| CommandReadBackCounters::CommandRBCounter0 as u8,
1297
);
1298
read_counter(
1299
&mut data.pit,
1300
0,
1301
(0xffff - 100) & 0x00ff,
1302
CommandAccess::CommandRWLeast,
1303
);
1304
}
1305
1306
/// Tests that ReadBack count works properly for `high` access mode.
1307
#[test]
1308
fn read_back_count_access_high() {
1309
let mut data = set_up();
1310
write_command(
1311
&mut data.pit,
1312
CommandCounter::CommandCounter0 as u8
1313
| CommandAccess::CommandRWMost as u8
1314
| CommandMode::CommandInterrupt as u8,
1315
);
1316
write_counter(&mut data.pit, 0, 0xffff, CommandAccess::CommandRWLeast);
1317
write_command(
1318
&mut data.pit,
1319
CommandCounter::CommandReadBack as u8
1320
| CommandReadBackLatch::CommandRBLatchCount as u8
1321
| CommandReadBackCounters::CommandRBCounter0 as u8,
1322
);
1323
1324
// Advance 100 ticks and verify that low byte of counter is appropriately updated.
1325
advance_by_ticks(&mut data, 512);
1326
write_command(
1327
&mut data.pit,
1328
CommandCounter::CommandReadBack as u8
1329
| CommandReadBackLatch::CommandRBLatchCount as u8
1330
| CommandReadBackCounters::CommandRBCounter0 as u8,
1331
);
1332
read_counter(&mut data.pit, 0, 0xff00, CommandAccess::CommandRWMost);
1333
write_command(
1334
&mut data.pit,
1335
CommandCounter::CommandReadBack as u8
1336
| CommandReadBackLatch::CommandRBLatchCount as u8
1337
| CommandReadBackCounters::CommandRBCounter0 as u8,
1338
);
1339
read_counter(
1340
&mut data.pit,
1341
0,
1342
(0xffff - 512) & 0xff00,
1343
CommandAccess::CommandRWMost,
1344
);
1345
}
1346
1347
/// Tests that ReadBack status returns the expected values.
1348
#[test]
1349
fn read_back_status() {
1350
let mut data = set_up();
1351
write_command(
1352
&mut data.pit,
1353
CommandCounter::CommandCounter0 as u8
1354
| CommandAccess::CommandRWBoth as u8
1355
| CommandMode::CommandSWStrobe as u8,
1356
);
1357
write_counter(&mut data.pit, 0, 0xffff, CommandAccess::CommandRWBoth);
1358
write_command(
1359
&mut data.pit,
1360
CommandCounter::CommandReadBack as u8
1361
| CommandReadBackLatch::CommandRBLatchStatus as u8
1362
| CommandReadBackCounters::CommandRBCounter0 as u8,
1363
);
1364
1365
read_counter(
1366
&mut data.pit,
1367
0,
1368
CommandAccess::CommandRWBoth as u16 | CommandMode::CommandSWStrobe as u16,
1369
CommandAccess::CommandRWLeast,
1370
);
1371
}
1372
1373
#[test]
1374
fn speaker_square_wave() {
1375
let mut data = set_up();
1376
write_command(
1377
&mut data.pit,
1378
CommandCounter::CommandCounter2 as u8
1379
| CommandAccess::CommandRWBoth as u8
1380
| CommandMode::CommandSquareWaveGen as u8,
1381
);
1382
write_counter(&mut data.pit, 2, 0xffff, CommandAccess::CommandRWBoth);
1383
1384
advance_by_ticks(&mut data, 128);
1385
read_counter(
1386
&mut data.pit,
1387
2,
1388
0xffff - 128 * 2,
1389
CommandAccess::CommandRWBoth,
1390
);
1391
}
1392
1393
#[test]
1394
fn speaker_rate_gen() {
1395
let mut data = set_up();
1396
write_command(
1397
&mut data.pit,
1398
CommandCounter::CommandCounter2 as u8
1399
| CommandAccess::CommandRWBoth as u8
1400
| CommandMode::CommandRateGen as u8,
1401
);
1402
write_counter(&mut data.pit, 2, 0xffff, CommandAccess::CommandRWBoth);
1403
1404
// In Rate Gen mode, the counter should start over when the gate is
1405
// set to high using SpeakerWrite.
1406
advance_by_ticks(&mut data, 128);
1407
read_counter(&mut data.pit, 2, 0xffff - 128, CommandAccess::CommandRWBoth);
1408
1409
write_speaker(&mut data.pit, 0x1);
1410
advance_by_ticks(&mut data, 128);
1411
read_counter(&mut data.pit, 2, 0xffff - 128, CommandAccess::CommandRWBoth);
1412
}
1413
1414
#[test]
1415
fn speaker_interrupt() {
1416
let mut data = set_up();
1417
1418
write_command(
1419
&mut data.pit,
1420
CommandCounter::CommandCounter2 as u8
1421
| CommandAccess::CommandRWBoth as u8
1422
| CommandMode::CommandInterrupt as u8,
1423
);
1424
write_counter(&mut data.pit, 2, 0xffff, CommandAccess::CommandRWBoth);
1425
1426
// In Interrupt mode, the counter should NOT start over when the gate is
1427
// set to high using SpeakerWrite.
1428
advance_by_ticks(&mut data, 128);
1429
read_counter(&mut data.pit, 2, 0xffff - 128, CommandAccess::CommandRWBoth);
1430
1431
write_speaker(&mut data.pit, 0x1);
1432
advance_by_ticks(&mut data, 128);
1433
read_counter(&mut data.pit, 2, 0xffff - 256, CommandAccess::CommandRWBoth);
1434
}
1435
1436
/// Verify that invalid reads and writes do not cause crashes.
1437
#[test]
1438
fn invalid_write_and_read() {
1439
let mut data = set_up();
1440
data.pit.write(
1441
BusAccessInfo {
1442
address: 0x44,
1443
offset: 0x4,
1444
id: 0,
1445
},
1446
&[0],
1447
);
1448
data.pit.read(
1449
BusAccessInfo {
1450
address: 0x55,
1451
offset: 0x15,
1452
id: 0,
1453
},
1454
&mut [0],
1455
);
1456
}
1457
}
1458
1459