2019-01-16 00:23:48 +00:00
|
|
|
|
// Copyright 2019 The Chromium OS Authors. All rights reserved.
|
|
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
|
// found in the LICENSE file.
|
|
|
|
|
// Based heavily on GCE VMM's pit.cc.
|
|
|
|
|
|
2019-03-02 02:07:56 +00:00
|
|
|
|
use std::fmt::{self, Display};
|
2019-01-16 00:23:48 +00:00
|
|
|
|
use std::io::Error as IoError;
|
|
|
|
|
use std::sync::Arc;
|
|
|
|
|
use std::thread;
|
|
|
|
|
use std::time::Duration;
|
|
|
|
|
|
2020-10-07 10:29:24 +00:00
|
|
|
|
use base::{
|
|
|
|
|
error, warn, AsRawDescriptor, Descriptor, Error as SysError, Event, PollToken, WaitContext,
|
|
|
|
|
};
|
2019-01-16 00:23:48 +00:00
|
|
|
|
use bit_field::BitField1;
|
|
|
|
|
use bit_field::*;
|
2020-06-17 21:00:37 +00:00
|
|
|
|
use hypervisor::{PitChannelState, PitRWMode, PitRWState, PitState};
|
2019-01-16 00:23:48 +00:00
|
|
|
|
use sync::Mutex;
|
|
|
|
|
|
|
|
|
|
#[cfg(not(test))]
|
2020-08-03 03:09:41 +00:00
|
|
|
|
use base::Clock;
|
2019-01-16 00:23:48 +00:00
|
|
|
|
#[cfg(test)]
|
2020-08-03 03:09:41 +00:00
|
|
|
|
use base::FakeClock as Clock;
|
2019-01-16 00:23:48 +00:00
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
2020-08-19 21:45:21 +00:00
|
|
|
|
use base::FakeTimer as Timer;
|
2019-01-16 00:23:48 +00:00
|
|
|
|
#[cfg(not(test))]
|
2020-08-19 21:45:21 +00:00
|
|
|
|
use base::Timer;
|
2019-01-16 00:23:48 +00:00
|
|
|
|
|
2020-10-08 22:02:20 +00:00
|
|
|
|
use crate::bus::BusAccessInfo;
|
2019-03-08 23:57:49 +00:00
|
|
|
|
use crate::BusDevice;
|
2019-01-16 00:23:48 +00:00
|
|
|
|
|
|
|
|
|
// Bitmask for areas of standard (non-ReadBack) Control Word Format. Constant
|
|
|
|
|
// names are kept the same as Intel PIT data sheet.
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, enumn::N)]
|
|
|
|
|
enum CommandBit {
|
|
|
|
|
CommandBCD = 0x01, // Binary/BCD input. x86 only uses binary mode.
|
|
|
|
|
CommandMode = 0x0e, // Operating Mode (mode 0-5).
|
|
|
|
|
CommandRW = 0x30, // Access mode: Choose high/low byte(s) to Read/Write.
|
|
|
|
|
CommandSC = 0xc0, // Select Counter/Read-back command.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Selects which counter is to be used by the associated command in the lower
|
|
|
|
|
// six bits of the byte. However, if 0xc0 is specified, it indicates that the
|
|
|
|
|
// command is a "Read-Back", which can latch count and/or status of the
|
|
|
|
|
// counters selected in the lower bits. See Intel 8254 data sheet for details.
|
2019-02-05 19:30:54 +00:00
|
|
|
|
#[allow(dead_code)]
|
2019-01-16 00:23:48 +00:00
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, enumn::N)]
|
|
|
|
|
enum CommandCounter {
|
|
|
|
|
CommandCounter0 = 0x00, // Select counter 0.
|
|
|
|
|
CommandCounter1 = 0x40, // Select counter 1.
|
|
|
|
|
CommandCounter2 = 0x80, // Select counter 2.
|
|
|
|
|
CommandReadBack = 0xc0, // Execute Read-Back.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Used for both CommandRW and ReadBackAccess.
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, enumn::N)]
|
|
|
|
|
enum CommandAccess {
|
|
|
|
|
CommandLatch = 0x00, // Latch specified counter.
|
|
|
|
|
CommandRWLeast = 0x10, // Read/Write least significant byte.
|
|
|
|
|
CommandRWMost = 0x20, // Read/Write most significant byte.
|
|
|
|
|
CommandRWBoth = 0x30, // Read/Write both bytes.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Used for both CommandMode and ReadBackMode.
|
|
|
|
|
// For mode 2 & 3, bit 3 is don't care bit (does not matter to be 0 or 1) but
|
|
|
|
|
// per 8254 spec, should be 0 to insure compatibility with future Intel
|
|
|
|
|
// products.
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, enumn::N)]
|
|
|
|
|
enum CommandMode {
|
|
|
|
|
// NOTE: No h/w modes are currently implemented.
|
|
|
|
|
CommandInterrupt = 0x00, // Mode 0, interrupt on terminal count.
|
|
|
|
|
CommandHWOneShot = 0x02, // Mode 1, h/w re-triggerable one-shot.
|
|
|
|
|
CommandRateGen = 0x04, // Mode 2, rate generator.
|
|
|
|
|
CommandSquareWaveGen = 0x06, // Mode 3, square wave generator.
|
|
|
|
|
CommandSWStrobe = 0x08, // Mode 4, s/w triggered strobe.
|
|
|
|
|
CommandHWStrobe = 0x0a, // Mode 5, h/w triggered strobe.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Bitmask for the latch portion of the ReadBack command.
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, enumn::N)]
|
|
|
|
|
#[rustfmt::skip] // rustfmt mangles comment indentation for trailing line comments.
|
|
|
|
|
enum CommandReadBackLatch {
|
|
|
|
|
CommandRBLatchBits = 0x30, // Mask bits that determine latching.
|
|
|
|
|
CommandRBLatchBoth = 0x00, // Latch both count and status. This should
|
|
|
|
|
// never happen in device, since bit 4 and 5 in
|
|
|
|
|
// read back command are inverted.
|
|
|
|
|
CommandRBLatchCount = 0x10, // Latch count.
|
|
|
|
|
CommandRBLatchStatus = 0x20, // Latch status.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Bitmask for the counter portion of the ReadBack command.
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, enumn::N)]
|
|
|
|
|
enum CommandReadBackCounters {
|
|
|
|
|
//CommandRBCounters = 0x0e, // Counters for which to provide ReadBack info.
|
|
|
|
|
CommandRBCounter2 = 0x08,
|
|
|
|
|
CommandRBCounter1 = 0x04,
|
|
|
|
|
CommandRBCounter0 = 0x02,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Bitmask for the ReadBack status command.
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, enumn::N)]
|
|
|
|
|
#[rustfmt::skip] // rustfmt mangles comment indentation for last line of this enum.
|
|
|
|
|
enum ReadBackData {
|
|
|
|
|
// Output format for ReadBack command.
|
|
|
|
|
ReadBackOutput = 0x80, // Output pin status.
|
|
|
|
|
ReadBackNullCount = 0x40, // Whether counter has value.
|
|
|
|
|
// ReadBackAccess, ReadBackMode, and ReadBackBCD intentionally omitted.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// I/O Port mappings in I/O bus.
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, enumn::N)]
|
|
|
|
|
enum PortIOSpace {
|
|
|
|
|
PortCounter0Data = 0x40, // Read/write.
|
|
|
|
|
PortCounter1Data = 0x41, // Read/write.
|
|
|
|
|
PortCounter2Data = 0x42, // Read/write.
|
|
|
|
|
PortCommand = 0x43, // Write only.
|
|
|
|
|
PortSpeaker = 0x61, // Read/write.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[bitfield]
|
|
|
|
|
#[derive(Clone, Copy, PartialEq)]
|
|
|
|
|
pub struct SpeakerPortFields {
|
|
|
|
|
// This field is documented in the chipset spec as NMI status and control
|
|
|
|
|
// register. Bits 2, 3, 6, 7 and low level hardware bits that need no
|
|
|
|
|
// emulation for virtualized environments. We call it speaker port because
|
|
|
|
|
// kvm, qemu, linux, and plan9 still call it speaker port, even though it
|
|
|
|
|
// has these other uses and is called something differently in the spec.
|
|
|
|
|
gate: BitField1,
|
|
|
|
|
speaker_on: BitField1,
|
|
|
|
|
pic_serr: BitField1,
|
|
|
|
|
iochk_enable: BitField1,
|
|
|
|
|
// This value changes as part of the refresh frequency of the board for
|
|
|
|
|
// piix4, this is about 1/15us.
|
|
|
|
|
refresh_clock: BitField1,
|
|
|
|
|
output: BitField1,
|
|
|
|
|
iochk_nmi: BitField1,
|
|
|
|
|
serr_nmi: BitField1,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// PIT frequency (in Hertz). See http://wiki.osdev.org/pit.
|
|
|
|
|
const FREQUENCY_HZ: u64 = 1193182;
|
|
|
|
|
|
|
|
|
|
const NUM_OF_COUNTERS: usize = 3;
|
|
|
|
|
|
|
|
|
|
const NANOS_PER_SEC: u64 = 1_000_000_000;
|
|
|
|
|
|
|
|
|
|
const MAX_TIMER_FREQ: u32 = 65536;
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub enum PitError {
|
2020-08-19 21:45:21 +00:00
|
|
|
|
TimerCreateError(SysError),
|
2020-10-07 10:29:24 +00:00
|
|
|
|
/// Creating WaitContext failed.
|
|
|
|
|
CreateWaitContext(SysError),
|
|
|
|
|
/// Error while waiting for events.
|
|
|
|
|
WaitError(SysError),
|
2019-01-16 00:23:48 +00:00
|
|
|
|
/// Error while trying to create worker thread.
|
|
|
|
|
SpawnThread(IoError),
|
2020-09-16 22:29:20 +00:00
|
|
|
|
/// Error while creating event.
|
|
|
|
|
CreateEvent(SysError),
|
|
|
|
|
/// Error while cloning event for worker thread.
|
|
|
|
|
CloneEvent(SysError),
|
2019-01-16 00:23:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-02 02:07:56 +00:00
|
|
|
|
impl Display for PitError {
|
2019-01-16 00:23:48 +00:00
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
2019-02-13 01:51:26 +00:00
|
|
|
|
use self::PitError::*;
|
|
|
|
|
|
2019-01-16 00:23:48 +00:00
|
|
|
|
match self {
|
2020-08-19 21:45:21 +00:00
|
|
|
|
TimerCreateError(e) => write!(f, "failed to create pit counter due to timer fd: {}", e),
|
2020-10-07 10:29:24 +00:00
|
|
|
|
CreateWaitContext(e) => write!(f, "failed to create poll context: {}", e),
|
|
|
|
|
WaitError(err) => write!(f, "failed to wait for events: {}", err),
|
2019-02-13 01:51:26 +00:00
|
|
|
|
SpawnThread(err) => write!(f, "failed to spawn thread: {}", err),
|
2020-09-16 22:29:20 +00:00
|
|
|
|
CreateEvent(err) => write!(f, "failed to create event: {}", err),
|
|
|
|
|
CloneEvent(err) => write!(f, "failed to clone event: {}", err),
|
2019-01-16 00:23:48 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-02 02:07:56 +00:00
|
|
|
|
impl std::error::Error for PitError {}
|
2019-01-16 00:23:48 +00:00
|
|
|
|
|
|
|
|
|
type PitResult<T> = std::result::Result<T, PitError>;
|
|
|
|
|
|
|
|
|
|
pub struct Pit {
|
|
|
|
|
// Structs that store each counter's state.
|
|
|
|
|
counters: Vec<Arc<Mutex<PitCounter>>>,
|
|
|
|
|
// Worker thread to update counter 0's state asynchronously. Counter 0 needs to send interrupts
|
|
|
|
|
// when timers expire, so it needs asynchronous updates. All other counters need only update
|
|
|
|
|
// when queried directly by the guest.
|
|
|
|
|
worker_thread: Option<thread::JoinHandle<PitResult<()>>>,
|
2020-09-16 22:29:20 +00:00
|
|
|
|
kill_evt: Event,
|
2019-01-16 00:23:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Drop for Pit {
|
|
|
|
|
fn drop(&mut self) {
|
|
|
|
|
if let Err(e) = self.kill_evt.write(1) {
|
2019-02-13 01:51:26 +00:00
|
|
|
|
error!("failed to kill PIT worker threads: {}", e);
|
2019-01-16 00:23:48 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if let Some(thread) = self.worker_thread.take() {
|
|
|
|
|
match thread.join() {
|
|
|
|
|
Ok(r) => {
|
|
|
|
|
if let Err(e) = r {
|
|
|
|
|
error!("pit worker thread exited with error: {}", e)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Err(e) => error!("pit worker thread panicked: {:?}", e),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl BusDevice for Pit {
|
|
|
|
|
fn debug_label(&self) -> String {
|
|
|
|
|
"userspace PIT".to_string()
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-08 22:02:20 +00:00
|
|
|
|
fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
|
2020-07-17 21:16:19 +00:00
|
|
|
|
self.ensure_started();
|
|
|
|
|
|
2019-01-16 00:23:48 +00:00
|
|
|
|
if data.len() != 1 {
|
|
|
|
|
warn!("Bad write size for Pit: {}", data.len());
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-10-08 22:02:20 +00:00
|
|
|
|
match PortIOSpace::n(info.address as i64) {
|
2019-01-16 00:23:48 +00:00
|
|
|
|
Some(PortIOSpace::PortCounter0Data) => self.counters[0].lock().write_counter(data[0]),
|
|
|
|
|
Some(PortIOSpace::PortCounter1Data) => self.counters[1].lock().write_counter(data[0]),
|
|
|
|
|
Some(PortIOSpace::PortCounter2Data) => self.counters[2].lock().write_counter(data[0]),
|
|
|
|
|
Some(PortIOSpace::PortCommand) => self.command_write(data[0]),
|
|
|
|
|
Some(PortIOSpace::PortSpeaker) => self.counters[2].lock().write_speaker(data[0]),
|
2020-10-08 22:02:20 +00:00
|
|
|
|
None => warn!("PIT: bad write to {}", info),
|
2019-01-16 00:23:48 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-08 22:02:20 +00:00
|
|
|
|
fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
|
2020-07-17 21:16:19 +00:00
|
|
|
|
self.ensure_started();
|
|
|
|
|
|
2019-01-16 00:23:48 +00:00
|
|
|
|
if data.len() != 1 {
|
|
|
|
|
warn!("Bad read size for Pit: {}", data.len());
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-10-08 22:02:20 +00:00
|
|
|
|
data[0] = match PortIOSpace::n(info.address as i64) {
|
2019-01-16 00:23:48 +00:00
|
|
|
|
Some(PortIOSpace::PortCounter0Data) => self.counters[0].lock().read_counter(),
|
|
|
|
|
Some(PortIOSpace::PortCounter1Data) => self.counters[1].lock().read_counter(),
|
|
|
|
|
Some(PortIOSpace::PortCounter2Data) => self.counters[2].lock().read_counter(),
|
|
|
|
|
// This should function as a no-op, since the specification doesn't allow the
|
|
|
|
|
// command register to be read. However, software is free to ask for it to
|
|
|
|
|
// to be read.
|
|
|
|
|
Some(PortIOSpace::PortCommand) => {
|
|
|
|
|
warn!("Ignoring read to command reg");
|
|
|
|
|
0
|
|
|
|
|
}
|
|
|
|
|
Some(PortIOSpace::PortSpeaker) => self.counters[2].lock().read_speaker(),
|
2019-02-25 23:14:31 +00:00
|
|
|
|
None => {
|
2020-10-08 22:02:20 +00:00
|
|
|
|
warn!("PIT: bad read from {}", info);
|
2019-02-25 23:14:31 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
2019-01-16 00:23:48 +00:00
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Pit {
|
2020-09-16 22:29:20 +00:00
|
|
|
|
pub fn new(interrupt_evt: Event, clock: Arc<Mutex<Clock>>) -> PitResult<Pit> {
|
2019-01-16 00:23:48 +00:00
|
|
|
|
let mut counters = Vec::new();
|
|
|
|
|
let mut interrupt = Some(interrupt_evt);
|
|
|
|
|
for i in 0..NUM_OF_COUNTERS {
|
|
|
|
|
let pit_counter = PitCounter::new(i, interrupt, clock.clone())?;
|
|
|
|
|
counters.push(Arc::new(Mutex::new(pit_counter)));
|
|
|
|
|
// pass interrupt IrqFd ONLY to counter 0; the rest do not deliver interrupts.
|
|
|
|
|
interrupt = None;
|
|
|
|
|
}
|
|
|
|
|
// We asssert here because:
|
|
|
|
|
// (a) this code only gets invoked at VM startup
|
|
|
|
|
// (b) the assert is very loud and would be easy to notice in tests
|
|
|
|
|
// (c) if we have the wrong number of counters, something is very wrong with the PIT and it
|
|
|
|
|
// may not make sense to continue operation.
|
|
|
|
|
assert_eq!(counters.len(), NUM_OF_COUNTERS);
|
2020-07-17 21:16:19 +00:00
|
|
|
|
|
2020-09-16 22:29:20 +00:00
|
|
|
|
let kill_evt = Event::new().map_err(PitError::CreateEvent)?;
|
2020-07-17 21:16:19 +00:00
|
|
|
|
|
2019-01-16 00:23:48 +00:00
|
|
|
|
Ok(Pit {
|
|
|
|
|
counters,
|
2020-07-17 21:16:19 +00:00
|
|
|
|
worker_thread: None,
|
|
|
|
|
kill_evt,
|
2019-01-16 00:23:48 +00:00
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-17 21:16:19 +00:00
|
|
|
|
fn ensure_started(&mut self) {
|
|
|
|
|
if self.worker_thread.is_some() {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if let Err(e) = self.start() {
|
|
|
|
|
error!("failed to start PIT: {}", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn start(&mut self) -> PitResult<()> {
|
|
|
|
|
let mut worker = Worker {
|
|
|
|
|
pit_counter: self.counters[0].clone(),
|
2020-10-07 10:29:24 +00:00
|
|
|
|
fd: Descriptor(self.counters[0].lock().timer.as_raw_descriptor()),
|
2020-07-17 21:16:19 +00:00
|
|
|
|
};
|
2020-09-16 22:29:20 +00:00
|
|
|
|
let evt = self.kill_evt.try_clone().map_err(PitError::CloneEvent)?;
|
2020-07-17 21:16:19 +00:00
|
|
|
|
|
|
|
|
|
self.worker_thread = Some(
|
|
|
|
|
thread::Builder::new()
|
|
|
|
|
.name("pit counter worker".to_string())
|
|
|
|
|
.spawn(move || worker.run(evt))
|
|
|
|
|
.map_err(PitError::SpawnThread)?,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-16 00:23:48 +00:00
|
|
|
|
fn command_write(&mut self, control_word: u8) {
|
|
|
|
|
let command: u16 = (control_word & CommandBit::CommandSC as u8).into();
|
|
|
|
|
let counter_index: usize = (command >> 6).into();
|
|
|
|
|
if command == (CommandCounter::CommandReadBack as u16) {
|
|
|
|
|
// ReadBack commands can apply to multiple counters.
|
|
|
|
|
if (control_word & (CommandReadBackCounters::CommandRBCounter0 as u8)) != 0 {
|
|
|
|
|
self.counters[0].lock().read_back_command(control_word);
|
|
|
|
|
}
|
|
|
|
|
if (control_word & (CommandReadBackCounters::CommandRBCounter1 as u8)) != 0 {
|
|
|
|
|
self.counters[1].lock().read_back_command(control_word);
|
|
|
|
|
}
|
|
|
|
|
if (control_word & (CommandReadBackCounters::CommandRBCounter2 as u8)) != 0 {
|
|
|
|
|
self.counters[2].lock().read_back_command(control_word);
|
|
|
|
|
}
|
|
|
|
|
} else if (control_word & (CommandBit::CommandRW as u8))
|
|
|
|
|
== (CommandAccess::CommandLatch as u8)
|
|
|
|
|
{
|
|
|
|
|
self.counters[counter_index].lock().latch_counter();
|
|
|
|
|
} else {
|
|
|
|
|
self.counters[counter_index]
|
|
|
|
|
.lock()
|
|
|
|
|
.store_command(control_word);
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-06-17 21:00:37 +00:00
|
|
|
|
|
|
|
|
|
pub fn get_pit_state(&self) -> PitState {
|
|
|
|
|
PitState {
|
|
|
|
|
channels: [
|
|
|
|
|
self.counters[0].lock().get_channel_state(),
|
|
|
|
|
self.counters[1].lock().get_channel_state(),
|
|
|
|
|
self.counters[2].lock().get_channel_state(),
|
|
|
|
|
],
|
|
|
|
|
flags: 0,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn set_pit_state(&mut self, state: &PitState) {
|
|
|
|
|
self.counters[0]
|
|
|
|
|
.lock()
|
|
|
|
|
.set_channel_state(&state.channels[0]);
|
|
|
|
|
self.counters[1]
|
|
|
|
|
.lock()
|
|
|
|
|
.set_channel_state(&state.channels[1]);
|
|
|
|
|
self.counters[2]
|
|
|
|
|
.lock()
|
|
|
|
|
.set_channel_state(&state.channels[2]);
|
|
|
|
|
}
|
2019-01-16 00:23:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Each instance of this represents one of the PIT counters. They are used to
|
|
|
|
|
// implement one-shot and repeating timer alarms. An 8254 has three counters.
|
|
|
|
|
struct PitCounter {
|
2020-09-16 22:29:20 +00:00
|
|
|
|
// Event to write when asserting an interrupt.
|
|
|
|
|
interrupt_evt: Option<Event>,
|
2019-01-16 00:23:48 +00:00
|
|
|
|
// Stores the value with which the counter was initialized. Counters are 16-
|
|
|
|
|
// bit values with an effective range of 1-65536 (65536 represented by 0).
|
|
|
|
|
reload_value: u16,
|
|
|
|
|
// Stores value when latch was called.
|
|
|
|
|
latched_value: u16,
|
|
|
|
|
// Stores last command from command register.
|
|
|
|
|
command: u8,
|
|
|
|
|
// Stores status from readback command
|
|
|
|
|
status: u8,
|
|
|
|
|
// Stores time of starting timer. Used for calculating remaining count, if an alarm is
|
|
|
|
|
// scheduled.
|
|
|
|
|
start: Option<Clock>,
|
|
|
|
|
// Current time.
|
|
|
|
|
clock: Arc<Mutex<Clock>>,
|
|
|
|
|
// Time when object was created. Used for a 15us counter.
|
|
|
|
|
creation_time: Clock,
|
|
|
|
|
// The number of the counter. The behavior for each counter is slightly different.
|
|
|
|
|
// Note that once a PitCounter is created, this value should never change.
|
|
|
|
|
counter_id: usize,
|
|
|
|
|
// Indicates if the low byte has been written in RWBoth.
|
|
|
|
|
wrote_low_byte: bool,
|
|
|
|
|
// Indicates if the low byte has been read in RWBoth.
|
|
|
|
|
read_low_byte: bool,
|
|
|
|
|
// Indicates whether counter has been latched.
|
|
|
|
|
latched: bool,
|
|
|
|
|
// Indicates whether ReadBack status has been latched.
|
|
|
|
|
status_latched: bool,
|
|
|
|
|
// Only should be used for counter 2. See http://wiki.osdev.org/PIT.
|
|
|
|
|
gate: bool,
|
|
|
|
|
speaker_on: bool,
|
|
|
|
|
// The starting value for the counter.
|
|
|
|
|
count: u32,
|
|
|
|
|
// Indicates whether the current timer is valid.
|
|
|
|
|
timer_valid: bool,
|
|
|
|
|
// Timer to set and receive periodic notifications.
|
2020-08-19 21:45:21 +00:00
|
|
|
|
timer: Timer,
|
2019-01-16 00:23:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Drop for PitCounter {
|
|
|
|
|
fn drop(&mut self) {
|
|
|
|
|
if self.timer_valid {
|
2019-02-06 00:09:28 +00:00
|
|
|
|
// This should not fail - timer.clear() only fails if timerfd_settime fails, which
|
2019-01-16 00:23:48 +00:00
|
|
|
|
// only happens due to invalid arguments or bad file descriptors. The arguments to
|
|
|
|
|
// timerfd_settime are constant, so its arguments won't be invalid, and it manages
|
|
|
|
|
// the file descriptor safely (we don't use the unsafe FromRawFd) so its file
|
|
|
|
|
// descriptor will be valid.
|
|
|
|
|
self.timer.clear().unwrap();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn adjust_count(count: u32) -> u32 {
|
|
|
|
|
// As per spec 0 means max.
|
|
|
|
|
if count == 0 {
|
|
|
|
|
MAX_TIMER_FREQ
|
|
|
|
|
} else {
|
|
|
|
|
count
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-17 21:00:37 +00:00
|
|
|
|
/// Get the current monotonic time of host in nanoseconds
|
|
|
|
|
fn get_monotonic_time() -> u64 {
|
|
|
|
|
let mut time = libc::timespec {
|
|
|
|
|
tv_sec: 0,
|
|
|
|
|
tv_nsec: 0,
|
|
|
|
|
};
|
|
|
|
|
// Safe because time struct is local to this function and we check the returncode
|
|
|
|
|
let ret = unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut time) };
|
|
|
|
|
if ret != 0 {
|
|
|
|
|
0
|
|
|
|
|
} else {
|
|
|
|
|
time.tv_sec as u64 * 1_000_000_000u64 + time.tv_nsec as u64
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-16 00:23:48 +00:00
|
|
|
|
impl PitCounter {
|
|
|
|
|
fn new(
|
|
|
|
|
counter_id: usize,
|
2020-09-16 22:29:20 +00:00
|
|
|
|
interrupt_evt: Option<Event>,
|
2019-01-16 00:23:48 +00:00
|
|
|
|
clock: Arc<Mutex<Clock>>,
|
|
|
|
|
) -> PitResult<PitCounter> {
|
|
|
|
|
#[cfg(not(test))]
|
2020-08-19 21:45:21 +00:00
|
|
|
|
let timer = Timer::new().map_err(PitError::TimerCreateError)?;
|
2019-01-16 00:23:48 +00:00
|
|
|
|
#[cfg(test)]
|
2020-08-19 21:45:21 +00:00
|
|
|
|
let timer = Timer::new(clock.clone());
|
2019-01-16 00:23:48 +00:00
|
|
|
|
Ok(PitCounter {
|
|
|
|
|
interrupt_evt,
|
|
|
|
|
reload_value: 0,
|
|
|
|
|
latched_value: 0,
|
|
|
|
|
command: 0,
|
|
|
|
|
status: 0,
|
|
|
|
|
start: None,
|
|
|
|
|
clock: clock.clone(),
|
|
|
|
|
creation_time: clock.lock().now(),
|
|
|
|
|
counter_id,
|
|
|
|
|
wrote_low_byte: false,
|
|
|
|
|
read_low_byte: false,
|
|
|
|
|
latched: false,
|
|
|
|
|
status_latched: false,
|
|
|
|
|
gate: false,
|
|
|
|
|
speaker_on: false,
|
|
|
|
|
// `count` is undefined in real hardware and can't ever be programmed to 0, so we
|
|
|
|
|
// initialize it to max to prevent a misbehaving guest from triggering a divide by 0.
|
|
|
|
|
count: MAX_TIMER_FREQ,
|
|
|
|
|
timer_valid: false,
|
|
|
|
|
timer,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-17 21:00:37 +00:00
|
|
|
|
fn get_channel_state(&self) -> PitChannelState {
|
|
|
|
|
// Crosvm Pit stores start as an Instant. We do our best to convert to the host's
|
|
|
|
|
// monotonic clock by taking the current monotonic time and subtracting the elapsed
|
|
|
|
|
// time since self.start.
|
|
|
|
|
let load_time = match &self.start {
|
|
|
|
|
Some(t) => get_monotonic_time() - t.elapsed().as_nanos() as u64,
|
|
|
|
|
None => 0,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let mut state = PitChannelState {
|
|
|
|
|
count: self.count,
|
|
|
|
|
latched_count: self.latched_value,
|
|
|
|
|
status_latched: self.status_latched,
|
|
|
|
|
status: self.status,
|
|
|
|
|
reload_value: self.reload_value,
|
|
|
|
|
mode: (self.command & CommandBit::CommandMode as u8) >> 1,
|
|
|
|
|
bcd: false,
|
|
|
|
|
gate: self.gate,
|
|
|
|
|
count_load_time: load_time,
|
|
|
|
|
rw_mode: PitRWMode::None,
|
|
|
|
|
read_state: PitRWState::None,
|
|
|
|
|
write_state: PitRWState::None,
|
|
|
|
|
count_latched: PitRWState::None,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
match self.get_access_mode() {
|
|
|
|
|
Some(CommandAccess::CommandRWLeast) => {
|
|
|
|
|
// If access mode is least, RWStates are always LSB
|
|
|
|
|
state.rw_mode = PitRWMode::Least;
|
|
|
|
|
state.read_state = PitRWState::LSB;
|
|
|
|
|
state.write_state = PitRWState::LSB;
|
|
|
|
|
}
|
|
|
|
|
Some(CommandAccess::CommandRWMost) => {
|
|
|
|
|
// If access mode is most, RWStates are always MSB
|
|
|
|
|
state.rw_mode = PitRWMode::Most;
|
|
|
|
|
state.read_state = PitRWState::MSB;
|
|
|
|
|
state.write_state = PitRWState::MSB;
|
|
|
|
|
}
|
|
|
|
|
Some(CommandAccess::CommandRWBoth) => {
|
|
|
|
|
state.rw_mode = PitRWMode::Both;
|
|
|
|
|
// read_state depends on whether or not we've read the low byte already
|
|
|
|
|
state.read_state = if self.read_low_byte {
|
|
|
|
|
PitRWState::Word1
|
|
|
|
|
} else {
|
|
|
|
|
PitRWState::Word0
|
|
|
|
|
};
|
|
|
|
|
// write_state depends on whether or not we've written the low byte already
|
|
|
|
|
state.write_state = if self.wrote_low_byte {
|
|
|
|
|
PitRWState::Word1
|
|
|
|
|
} else {
|
|
|
|
|
PitRWState::Word0
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
_ => {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Count_latched should be PitRWSTate::None unless we're latched
|
|
|
|
|
if self.latched {
|
|
|
|
|
state.count_latched = state.read_state;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
state
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn set_channel_state(&mut self, state: &PitChannelState) {
|
|
|
|
|
self.count = state.count;
|
|
|
|
|
self.latched_value = state.latched_count;
|
|
|
|
|
self.status_latched = state.status_latched;
|
|
|
|
|
self.status = state.status;
|
|
|
|
|
self.reload_value = state.reload_value;
|
|
|
|
|
|
|
|
|
|
// the command consists of:
|
|
|
|
|
// - 1 bcd bit, which we don't care about because we don't support non-binary mode
|
|
|
|
|
// - 3 mode bits
|
|
|
|
|
// - 2 access mode bits
|
|
|
|
|
// - 2 counter select bits, which aren't used by the counter/channel itself
|
|
|
|
|
self.command = (state.mode << 1) | ((state.rw_mode as u8) << 4);
|
|
|
|
|
self.gate = state.gate;
|
2020-11-04 22:39:31 +00:00
|
|
|
|
self.latched = state.count_latched != PitRWState::None;
|
|
|
|
|
self.read_low_byte = state.read_state == PitRWState::Word1;
|
|
|
|
|
self.wrote_low_byte = state.write_state == PitRWState::Word1;
|
2020-06-17 21:00:37 +00:00
|
|
|
|
|
|
|
|
|
// To convert the count_load_time to an instant we have to convert it to a
|
|
|
|
|
// duration by comparing it to get_monotonic_time. Then subtract that duration from
|
|
|
|
|
// a "now" instant.
|
|
|
|
|
self.start = self
|
|
|
|
|
.clock
|
|
|
|
|
.lock()
|
|
|
|
|
.now()
|
|
|
|
|
.checked_sub(std::time::Duration::from_nanos(
|
|
|
|
|
get_monotonic_time() - state.count_load_time,
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-16 00:23:48 +00:00
|
|
|
|
fn get_access_mode(&self) -> Option<CommandAccess> {
|
|
|
|
|
CommandAccess::n(self.command & (CommandBit::CommandRW as u8))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn get_command_mode(&self) -> Option<CommandMode> {
|
|
|
|
|
CommandMode::n(self.command & CommandBit::CommandMode as u8)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn read_counter(&mut self) -> u8 {
|
|
|
|
|
if self.status_latched {
|
|
|
|
|
self.status_latched = false;
|
|
|
|
|
return self.status;
|
|
|
|
|
};
|
|
|
|
|
let data_value: u16 = if self.latched {
|
|
|
|
|
self.latched_value
|
|
|
|
|
} else {
|
|
|
|
|
self.get_read_value()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let access_mode = self.get_access_mode();
|
|
|
|
|
// Latch may be true without being indicated by the access mode if
|
|
|
|
|
// a ReadBack was issued.
|
|
|
|
|
match (access_mode, self.read_low_byte) {
|
|
|
|
|
(Some(CommandAccess::CommandRWLeast), _) => {
|
|
|
|
|
self.latched = false; // Unlatch if only reading the low byte.
|
|
|
|
|
(data_value & 0xff) as u8
|
|
|
|
|
}
|
|
|
|
|
(Some(CommandAccess::CommandRWBoth), false) => {
|
|
|
|
|
self.read_low_byte = true;
|
|
|
|
|
(data_value & 0xff) as u8
|
|
|
|
|
}
|
|
|
|
|
(Some(CommandAccess::CommandRWBoth), true)
|
|
|
|
|
| (Some(CommandAccess::CommandRWMost), _) => {
|
|
|
|
|
self.read_low_byte = false; // Allow for future reads for RWBoth.
|
|
|
|
|
self.latched = false;
|
|
|
|
|
(data_value >> 8) as u8
|
|
|
|
|
}
|
|
|
|
|
(_, _) => 0, // Default for erroneous call
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn write_counter(&mut self, written_datum: u8) {
|
|
|
|
|
let access_mode = self.get_access_mode();
|
|
|
|
|
let datum: u16 = written_datum.into();
|
|
|
|
|
let mut should_start_timer = true;
|
|
|
|
|
self.reload_value = match access_mode {
|
|
|
|
|
Some(CommandAccess::CommandRWLeast) => datum,
|
|
|
|
|
Some(CommandAccess::CommandRWMost) => datum << 8,
|
|
|
|
|
Some(CommandAccess::CommandRWBoth) => {
|
|
|
|
|
// In kCommandRWBoth mode, the first guest write is the low byte and the
|
|
|
|
|
// the second guest write is the high byte. The timer isn't started
|
|
|
|
|
// until after the second byte is written.
|
|
|
|
|
if self.wrote_low_byte {
|
|
|
|
|
self.wrote_low_byte = false;
|
|
|
|
|
self.reload_value | (datum << 8)
|
|
|
|
|
} else {
|
|
|
|
|
self.wrote_low_byte = true;
|
|
|
|
|
should_start_timer = false; // Don't start until high byte written.
|
|
|
|
|
datum
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
_ => {
|
|
|
|
|
should_start_timer = false;
|
|
|
|
|
self.reload_value
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
if should_start_timer {
|
|
|
|
|
let reload: u32 = self.reload_value.into();
|
|
|
|
|
self.load_and_start_timer(reload);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn get_output(&self) -> bool {
|
|
|
|
|
let ticks_passed = self.get_ticks_passed();
|
|
|
|
|
let count: u64 = self.count.into();
|
|
|
|
|
match self.get_command_mode() {
|
|
|
|
|
Some(CommandMode::CommandInterrupt) => ticks_passed >= count,
|
|
|
|
|
Some(CommandMode::CommandHWOneShot) => ticks_passed < count,
|
|
|
|
|
Some(CommandMode::CommandRateGen) => ticks_passed != 0 && ticks_passed % count == 0,
|
|
|
|
|
Some(CommandMode::CommandSquareWaveGen) => ticks_passed < (count + 1) / 2,
|
|
|
|
|
Some(CommandMode::CommandSWStrobe) | Some(CommandMode::CommandHWStrobe) => {
|
|
|
|
|
ticks_passed == count
|
|
|
|
|
}
|
|
|
|
|
None => {
|
|
|
|
|
warn!("Invalid command mode based on command: {:#x}", self.command);
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn read_speaker(&self) -> u8 {
|
|
|
|
|
// Refresh clock is a value independent of the actual
|
|
|
|
|
// counter that goes up and down approx every 15 us (~66000/s).
|
|
|
|
|
let us = self
|
|
|
|
|
.clock
|
|
|
|
|
.lock()
|
|
|
|
|
.now()
|
|
|
|
|
.duration_since(&self.creation_time)
|
|
|
|
|
.subsec_micros();
|
|
|
|
|
let refresh_clock = us % 15 == 0;
|
|
|
|
|
let mut speaker = SpeakerPortFields::new();
|
|
|
|
|
speaker.set_gate(self.gate.into());
|
|
|
|
|
speaker.set_speaker_on(self.speaker_on.into());
|
|
|
|
|
speaker.set_iochk_enable(0);
|
|
|
|
|
speaker.set_refresh_clock(refresh_clock.into());
|
|
|
|
|
speaker.set_output(self.get_output().into());
|
|
|
|
|
speaker.set_iochk_nmi(0);
|
|
|
|
|
speaker.set_serr_nmi(0);
|
|
|
|
|
speaker.get(/*offset=*/ 0, /*width=*/ 8) as u8
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn write_speaker(&mut self, datum: u8) {
|
|
|
|
|
let mut speaker = SpeakerPortFields::new();
|
|
|
|
|
speaker.set(/*offset=*/ 0, /*width=*/ 8, datum.into());
|
|
|
|
|
let new_gate = speaker.get_gate() != 0;
|
|
|
|
|
match self.get_command_mode() {
|
|
|
|
|
Some(CommandMode::CommandInterrupt) | Some(CommandMode::CommandSWStrobe) => (),
|
|
|
|
|
Some(_) => {
|
|
|
|
|
if new_gate && !self.gate {
|
|
|
|
|
self.start = Some(self.clock.lock().now());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
None => {
|
|
|
|
|
warn!("Invalid command mode based on command {:#x}", self.command);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
self.speaker_on = speaker.get_speaker_on() != 0;
|
|
|
|
|
self.gate = new_gate;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn load_and_start_timer(&mut self, initial_count: u32) {
|
|
|
|
|
self.count = adjust_count(initial_count);
|
|
|
|
|
self.start_timer();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn start_timer(&mut self) {
|
|
|
|
|
self.start = Some(self.clock.lock().now());
|
|
|
|
|
|
|
|
|
|
// Counter 0 is the only counter that generates interrupts, so we
|
|
|
|
|
// don't need to set a timer for the other two counters.
|
|
|
|
|
if self.counter_id != 0 {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let timer_len = Duration::from_nanos(u64::from(self.count) * NANOS_PER_SEC / FREQUENCY_HZ);
|
|
|
|
|
|
|
|
|
|
let period_ns = match self.get_command_mode() {
|
|
|
|
|
Some(CommandMode::CommandInterrupt)
|
|
|
|
|
| Some(CommandMode::CommandHWOneShot)
|
|
|
|
|
| Some(CommandMode::CommandSWStrobe)
|
|
|
|
|
| Some(CommandMode::CommandHWStrobe) => Duration::new(0, 0),
|
|
|
|
|
Some(CommandMode::CommandRateGen) | Some(CommandMode::CommandSquareWaveGen) => {
|
|
|
|
|
timer_len
|
|
|
|
|
}
|
|
|
|
|
// Don't arm timer if invalid mode.
|
|
|
|
|
None => {
|
2019-02-06 00:09:28 +00:00
|
|
|
|
// This will still result in start being set to the current time.
|
|
|
|
|
// Per spec:
|
|
|
|
|
// A new initial count may be written to a Counter at any time without affecting
|
|
|
|
|
// the Counter’s programmed Mode in any way. Counting will be affected as
|
|
|
|
|
// described in the Mode definitions. The new count must follow the programmed
|
|
|
|
|
// count format
|
|
|
|
|
// It's unclear whether setting `self.start` in this case is entirely compliant,
|
|
|
|
|
// but the spec is fairly quiet on expected behavior in error cases, so OSs
|
|
|
|
|
// shouldn't enter invalid modes in the first place. If they do, and then try to
|
|
|
|
|
// get out of it by first setting the counter then the command, this behavior will
|
|
|
|
|
// (perhaps) be minimally surprising, but arguments can be made for other behavior.
|
|
|
|
|
// It's uncertain if this behavior matches real PIT hardware.
|
2019-01-16 00:23:48 +00:00
|
|
|
|
warn!("Invalid command mode based on command {:#x}", self.command);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
self.safe_arm_timer(timer_len, period_ns);
|
|
|
|
|
self.timer_valid = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn read_back_command(&mut self, control_word: u8) {
|
|
|
|
|
let latch_cmd =
|
|
|
|
|
CommandReadBackLatch::n(control_word & CommandReadBackLatch::CommandRBLatchBits as u8);
|
|
|
|
|
match latch_cmd {
|
|
|
|
|
Some(CommandReadBackLatch::CommandRBLatchCount) => {
|
|
|
|
|
self.latch_counter();
|
|
|
|
|
}
|
|
|
|
|
Some(CommandReadBackLatch::CommandRBLatchStatus) => {
|
|
|
|
|
self.latch_status();
|
|
|
|
|
}
|
|
|
|
|
_ => warn!(
|
|
|
|
|
"Unexpected ReadBackLatch. control_word: {:#x}",
|
|
|
|
|
control_word
|
|
|
|
|
),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn latch_counter(&mut self) {
|
|
|
|
|
if self.latched {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.latched_value = self.get_read_value();
|
|
|
|
|
self.latched = true;
|
|
|
|
|
self.read_low_byte = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn latch_status(&mut self) {
|
|
|
|
|
// Including BCD here, even though it currently never gets used.
|
|
|
|
|
self.status = self.command
|
|
|
|
|
& (CommandBit::CommandRW as u8
|
|
|
|
|
| CommandBit::CommandMode as u8
|
|
|
|
|
| CommandBit::CommandBCD as u8);
|
|
|
|
|
if self.start.is_none() {
|
|
|
|
|
self.status |= ReadBackData::ReadBackNullCount as u8;
|
|
|
|
|
}
|
|
|
|
|
if self.get_output() {
|
|
|
|
|
self.status |= ReadBackData::ReadBackOutput as u8;
|
|
|
|
|
}
|
|
|
|
|
self.status_latched = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn store_command(&mut self, datum: u8) {
|
|
|
|
|
self.command = datum;
|
|
|
|
|
self.latched = false;
|
|
|
|
|
|
|
|
|
|
// If a new RW command is written, cancel the current timer.
|
|
|
|
|
if self.timer_valid {
|
|
|
|
|
self.start = None;
|
|
|
|
|
self.timer_valid = false;
|
|
|
|
|
// See the comment in the impl of Drop for PitCounter for justification of the unwrap()
|
|
|
|
|
self.timer.clear().unwrap();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.wrote_low_byte = false;
|
|
|
|
|
self.read_low_byte = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn timer_handler(&mut self) {
|
|
|
|
|
if let Err(e) = self.timer.wait() {
|
2020-08-19 21:45:21 +00:00
|
|
|
|
// Under the current Timer implementation (as of Jan 2019), this failure shouldn't
|
2019-01-16 00:23:48 +00:00
|
|
|
|
// happen but implementation details may change in the future, and the failure
|
|
|
|
|
// cases are complex to reason about. Because of this, avoid unwrap().
|
|
|
|
|
error!("pit: timer wait unexpectedly failed: {}", e);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
let mode = self.get_command_mode();
|
|
|
|
|
if mode == Some(CommandMode::CommandRateGen)
|
|
|
|
|
|| mode == Some(CommandMode::CommandSquareWaveGen)
|
|
|
|
|
{
|
|
|
|
|
// Reset the start time for timer modes that repeat.
|
|
|
|
|
self.start = Some(self.clock.lock().now());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// For square wave mode, this isn't quite accurate to the spec, but the
|
|
|
|
|
// difference isn't meaningfully visible to the guest in any important way,
|
|
|
|
|
// and the code is simpler without the special case.
|
2019-04-15 22:56:35 +00:00
|
|
|
|
if let Some(interrupt) = &mut self.interrupt_evt {
|
2019-01-16 00:23:48 +00:00
|
|
|
|
// This is safe because the file descriptor is nonblocking and we're writing 1.
|
|
|
|
|
interrupt.write(1).unwrap();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn safe_arm_timer(&mut self, mut due: Duration, period: Duration) {
|
|
|
|
|
if due == Duration::new(0, 0) {
|
|
|
|
|
due = Duration::from_nanos(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if let Err(e) = self.timer.reset(due, Some(period)) {
|
|
|
|
|
error!("failed to reset timer: {}", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn get_ticks_passed(&self) -> u64 {
|
2019-04-15 22:56:35 +00:00
|
|
|
|
match &self.start {
|
2019-01-16 00:23:48 +00:00
|
|
|
|
None => 0,
|
2019-04-15 22:56:35 +00:00
|
|
|
|
Some(t) => {
|
2019-01-16 00:23:48 +00:00
|
|
|
|
let dur = self.clock.lock().now().duration_since(t);
|
|
|
|
|
let dur_ns: u64 = dur.as_secs() * NANOS_PER_SEC + u64::from(dur.subsec_nanos());
|
2020-03-16 04:49:21 +00:00
|
|
|
|
dur_ns * FREQUENCY_HZ / NANOS_PER_SEC
|
2019-01-16 00:23:48 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn get_read_value(&self) -> u16 {
|
|
|
|
|
match self.start {
|
|
|
|
|
None => 0,
|
|
|
|
|
Some(_) => {
|
|
|
|
|
let count: u64 = adjust_count(self.reload_value.into()).into();
|
|
|
|
|
let ticks_passed = self.get_ticks_passed();
|
|
|
|
|
match self.get_command_mode() {
|
|
|
|
|
Some(CommandMode::CommandInterrupt)
|
|
|
|
|
| Some(CommandMode::CommandHWOneShot)
|
|
|
|
|
| Some(CommandMode::CommandSWStrobe)
|
|
|
|
|
| Some(CommandMode::CommandHWStrobe) => {
|
|
|
|
|
if ticks_passed > count {
|
|
|
|
|
// Some risk of raciness here in that the count may return a value
|
|
|
|
|
// indicating that the count has expired when the interrupt hasn't
|
|
|
|
|
// yet been injected.
|
|
|
|
|
0
|
|
|
|
|
} else {
|
|
|
|
|
((count - ticks_passed) & 0xFFFF) as u16
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Some(CommandMode::CommandRateGen) => (count - (ticks_passed % count)) as u16,
|
|
|
|
|
Some(CommandMode::CommandSquareWaveGen) => {
|
|
|
|
|
(count - ((ticks_passed * 2) % count)) as u16
|
|
|
|
|
}
|
|
|
|
|
None => {
|
|
|
|
|
warn!("Invalid command mode: command = {:#x}", self.command);
|
|
|
|
|
0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct Worker {
|
|
|
|
|
pit_counter: Arc<Mutex<PitCounter>>,
|
2020-10-07 10:29:24 +00:00
|
|
|
|
fd: Descriptor,
|
2019-01-16 00:23:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Worker {
|
2020-09-16 22:29:20 +00:00
|
|
|
|
fn run(&mut self, kill_evt: Event) -> PitResult<()> {
|
2019-01-16 00:23:48 +00:00
|
|
|
|
#[derive(PollToken)]
|
|
|
|
|
enum Token {
|
|
|
|
|
// The timer expired.
|
|
|
|
|
TimerExpire,
|
|
|
|
|
// The parent thread requested an exit.
|
|
|
|
|
Kill,
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-07 10:29:24 +00:00
|
|
|
|
let wait_ctx: WaitContext<Token> =
|
|
|
|
|
WaitContext::build_with(&[(&self.fd, Token::TimerExpire), (&kill_evt, Token::Kill)])
|
|
|
|
|
.map_err(PitError::CreateWaitContext)?;
|
2019-01-16 00:23:48 +00:00
|
|
|
|
|
|
|
|
|
loop {
|
2020-10-07 10:29:24 +00:00
|
|
|
|
let events = wait_ctx.wait().map_err(PitError::WaitError)?;
|
|
|
|
|
for event in events.iter().filter(|e| e.is_readable) {
|
|
|
|
|
match event.token {
|
2019-01-16 00:23:48 +00:00
|
|
|
|
Token::TimerExpire => {
|
|
|
|
|
let mut pit = self.pit_counter.lock();
|
|
|
|
|
pit.timer_handler();
|
|
|
|
|
}
|
|
|
|
|
Token::Kill => return Ok(()),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::*;
|
|
|
|
|
struct TestData {
|
|
|
|
|
pit: Pit,
|
2020-09-16 22:29:20 +00:00
|
|
|
|
irqfd: Event,
|
2019-01-16 00:23:48 +00:00
|
|
|
|
clock: Arc<Mutex<Clock>>,
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-08 22:02:20 +00:00
|
|
|
|
fn pit_bus_address(address: PortIOSpace) -> BusAccessInfo {
|
|
|
|
|
// The PIT is added to the io_bus in two locations, so the offset depends on which
|
|
|
|
|
// address range the address is in. The PIT implementation currently does not use the
|
|
|
|
|
// offset, but we're setting it accurately here in case it does in the future.
|
|
|
|
|
let offset = match address as u64 {
|
|
|
|
|
x if x >= PortIOSpace::PortCounter0Data as u64
|
|
|
|
|
&& x < PortIOSpace::PortCounter0Data as u64 + 0x8 =>
|
|
|
|
|
{
|
|
|
|
|
address as u64 - PortIOSpace::PortCounter0Data as u64
|
|
|
|
|
}
|
|
|
|
|
x if x == PortIOSpace::PortSpeaker as u64 => 0,
|
|
|
|
|
_ => panic!("invalid PIT address: {:#x}", address as u64),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
BusAccessInfo {
|
|
|
|
|
offset,
|
|
|
|
|
address: address as u64,
|
|
|
|
|
id: 0,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-16 00:23:48 +00:00
|
|
|
|
/// Utility method for writing a command word to a command register.
|
|
|
|
|
fn write_command(pit: &mut Pit, command: u8) {
|
2020-10-08 22:02:20 +00:00
|
|
|
|
pit.write(pit_bus_address(PortIOSpace::PortCommand), &[command])
|
2019-01-16 00:23:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Utility method for writing a command word to the speaker register.
|
|
|
|
|
fn write_speaker(pit: &mut Pit, command: u8) {
|
2020-10-08 22:02:20 +00:00
|
|
|
|
pit.write(pit_bus_address(PortIOSpace::PortSpeaker), &[command])
|
2019-01-16 00:23:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Utility method for writing to a counter.
|
|
|
|
|
fn write_counter(pit: &mut Pit, counter_idx: usize, data: u16, access_mode: CommandAccess) {
|
|
|
|
|
let port = match counter_idx {
|
|
|
|
|
0 => PortIOSpace::PortCounter0Data,
|
|
|
|
|
1 => PortIOSpace::PortCounter1Data,
|
|
|
|
|
2 => PortIOSpace::PortCounter2Data,
|
|
|
|
|
_ => panic!("Invalid counter_idx: {}", counter_idx),
|
2020-10-08 22:02:20 +00:00
|
|
|
|
};
|
2019-01-16 00:23:48 +00:00
|
|
|
|
// Write the least, then the most, significant byte.
|
|
|
|
|
if access_mode == CommandAccess::CommandRWLeast
|
|
|
|
|
|| access_mode == CommandAccess::CommandRWBoth
|
|
|
|
|
{
|
2020-10-08 22:02:20 +00:00
|
|
|
|
pit.write(pit_bus_address(port), &[(data & 0xff) as u8]);
|
2019-01-16 00:23:48 +00:00
|
|
|
|
}
|
|
|
|
|
if access_mode == CommandAccess::CommandRWMost
|
|
|
|
|
|| access_mode == CommandAccess::CommandRWBoth
|
|
|
|
|
{
|
2020-10-08 22:02:20 +00:00
|
|
|
|
pit.write(pit_bus_address(port), &[(data >> 8) as u8]);
|
2019-01-16 00:23:48 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Utility method for reading a counter. Check if the read value matches expected_value.
|
|
|
|
|
fn read_counter(pit: &mut Pit, counter_idx: usize, expected: u16, access_mode: CommandAccess) {
|
|
|
|
|
let port = match counter_idx {
|
|
|
|
|
0 => PortIOSpace::PortCounter0Data,
|
|
|
|
|
1 => PortIOSpace::PortCounter1Data,
|
|
|
|
|
2 => PortIOSpace::PortCounter2Data,
|
|
|
|
|
_ => panic!("Invalid counter_idx: {}", counter_idx),
|
2020-10-08 22:02:20 +00:00
|
|
|
|
};
|
2019-01-16 00:23:48 +00:00
|
|
|
|
let mut result: u16 = 0;
|
|
|
|
|
if access_mode == CommandAccess::CommandRWLeast
|
|
|
|
|
|| access_mode == CommandAccess::CommandRWBoth
|
|
|
|
|
{
|
|
|
|
|
let mut buffer = [0];
|
2020-10-08 22:02:20 +00:00
|
|
|
|
pit.read(pit_bus_address(port), &mut buffer);
|
2019-01-16 00:23:48 +00:00
|
|
|
|
result = buffer[0].into();
|
|
|
|
|
}
|
|
|
|
|
if access_mode == CommandAccess::CommandRWMost
|
|
|
|
|
|| access_mode == CommandAccess::CommandRWBoth
|
|
|
|
|
{
|
|
|
|
|
let mut buffer = [0];
|
2020-10-08 22:02:20 +00:00
|
|
|
|
pit.read(pit_bus_address(port), &mut buffer);
|
2019-01-16 00:23:48 +00:00
|
|
|
|
result |= u16::from(buffer[0]) << 8;
|
|
|
|
|
}
|
|
|
|
|
assert_eq!(result, expected);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn set_up() -> TestData {
|
2020-09-16 22:29:20 +00:00
|
|
|
|
let irqfd = Event::new().unwrap();
|
2019-01-16 00:23:48 +00:00
|
|
|
|
let clock = Arc::new(Mutex::new(Clock::new()));
|
|
|
|
|
TestData {
|
|
|
|
|
pit: Pit::new(irqfd.try_clone().unwrap(), clock.clone()).unwrap(),
|
|
|
|
|
irqfd,
|
|
|
|
|
clock,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn advance_by_tick(data: &mut TestData) {
|
|
|
|
|
advance_by_ticks(data, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn advance_by_ticks(data: &mut TestData, ticks: u64) {
|
|
|
|
|
println!(
|
|
|
|
|
"Advancing by {:#x} ticks ({} ns)",
|
|
|
|
|
ticks,
|
|
|
|
|
(NANOS_PER_SEC * ticks) / FREQUENCY_HZ + 1
|
|
|
|
|
);
|
|
|
|
|
let mut lock = data.clock.lock();
|
|
|
|
|
lock.add_ns((NANOS_PER_SEC * ticks) / FREQUENCY_HZ + 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Tests the ability to write a command and data and read the data back using latch.
|
|
|
|
|
#[test]
|
|
|
|
|
fn write_and_latch() {
|
|
|
|
|
let mut data = set_up();
|
|
|
|
|
let both_interrupt =
|
|
|
|
|
CommandAccess::CommandRWBoth as u8 | CommandMode::CommandInterrupt as u8;
|
|
|
|
|
// Issue a command to write both digits of counter 0 in interrupt mode.
|
|
|
|
|
write_command(
|
|
|
|
|
&mut data.pit,
|
|
|
|
|
CommandCounter::CommandCounter0 as u8 | both_interrupt,
|
|
|
|
|
);
|
|
|
|
|
write_counter(&mut data.pit, 0, 24, CommandAccess::CommandRWBoth);
|
|
|
|
|
// Advance time by one tick -- value read back should decrease.
|
|
|
|
|
advance_by_tick(&mut data);
|
|
|
|
|
|
|
|
|
|
// Latch and read back the value written.
|
|
|
|
|
write_command(
|
|
|
|
|
&mut data.pit,
|
|
|
|
|
CommandCounter::CommandCounter0 as u8 | CommandAccess::CommandLatch as u8,
|
|
|
|
|
);
|
|
|
|
|
// Advance again after latching to verify that value read back doesn't change.
|
|
|
|
|
advance_by_tick(&mut data);
|
|
|
|
|
read_counter(&mut data.pit, 0, 23, CommandAccess::CommandRWBoth);
|
|
|
|
|
|
|
|
|
|
// Repeat with counter 1.
|
|
|
|
|
write_command(
|
|
|
|
|
&mut data.pit,
|
|
|
|
|
CommandCounter::CommandCounter1 as u8 | both_interrupt,
|
|
|
|
|
);
|
|
|
|
|
write_counter(&mut data.pit, 1, 314, CommandAccess::CommandRWBoth);
|
|
|
|
|
advance_by_tick(&mut data);
|
|
|
|
|
write_command(
|
|
|
|
|
&mut data.pit,
|
|
|
|
|
CommandCounter::CommandCounter1 as u8 | CommandAccess::CommandLatch as u8,
|
|
|
|
|
);
|
|
|
|
|
advance_by_tick(&mut data);
|
|
|
|
|
read_counter(&mut data.pit, 1, 313, CommandAccess::CommandRWBoth);
|
|
|
|
|
|
|
|
|
|
// Repeat with counter 2.
|
|
|
|
|
write_command(
|
|
|
|
|
&mut data.pit,
|
|
|
|
|
CommandCounter::CommandCounter2 as u8 | both_interrupt,
|
|
|
|
|
);
|
|
|
|
|
write_counter(&mut data.pit, 2, 0xffff, CommandAccess::CommandRWBoth);
|
|
|
|
|
advance_by_tick(&mut data);
|
|
|
|
|
write_command(
|
|
|
|
|
&mut data.pit,
|
|
|
|
|
CommandCounter::CommandCounter2 as u8 | CommandAccess::CommandLatch as u8,
|
|
|
|
|
);
|
|
|
|
|
advance_by_tick(&mut data);
|
|
|
|
|
read_counter(&mut data.pit, 2, 0xfffe, CommandAccess::CommandRWBoth);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Tests the ability to read only the least significant byte.
|
|
|
|
|
#[test]
|
|
|
|
|
fn write_and_read_least() {
|
|
|
|
|
let mut data = set_up();
|
|
|
|
|
write_command(
|
|
|
|
|
&mut data.pit,
|
|
|
|
|
CommandCounter::CommandCounter0 as u8
|
|
|
|
|
| CommandAccess::CommandRWLeast as u8
|
|
|
|
|
| CommandMode::CommandInterrupt as u8,
|
|
|
|
|
);
|
|
|
|
|
write_counter(&mut data.pit, 0, 0x3424, CommandAccess::CommandRWLeast);
|
|
|
|
|
read_counter(&mut data.pit, 0, 0x0024, CommandAccess::CommandRWLeast);
|
|
|
|
|
write_command(
|
|
|
|
|
&mut data.pit,
|
|
|
|
|
CommandCounter::CommandCounter0 as u8 | CommandAccess::CommandLatch as u8,
|
|
|
|
|
);
|
|
|
|
|
advance_by_tick(&mut data);
|
|
|
|
|
read_counter(&mut data.pit, 0, 0x0024, CommandAccess::CommandRWLeast);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Tests the ability to read only the most significant byte.
|
|
|
|
|
#[test]
|
|
|
|
|
fn write_and_read_most() {
|
|
|
|
|
let mut data = set_up();
|
|
|
|
|
write_command(
|
|
|
|
|
&mut data.pit,
|
|
|
|
|
CommandCounter::CommandCounter0 as u8
|
|
|
|
|
| CommandAccess::CommandRWMost as u8
|
|
|
|
|
| CommandMode::CommandInterrupt as u8,
|
|
|
|
|
);
|
|
|
|
|
write_counter(&mut data.pit, 0, 0x3424, CommandAccess::CommandRWMost);
|
|
|
|
|
read_counter(&mut data.pit, 0, 0x3400, CommandAccess::CommandRWMost);
|
|
|
|
|
write_command(
|
|
|
|
|
&mut data.pit,
|
|
|
|
|
CommandCounter::CommandCounter0 as u8 | CommandAccess::CommandLatch as u8,
|
|
|
|
|
);
|
|
|
|
|
advance_by_tick(&mut data);
|
|
|
|
|
read_counter(&mut data.pit, 0, 0x3400, CommandAccess::CommandRWMost);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Tests that reading the command register does nothing.
|
|
|
|
|
#[test]
|
|
|
|
|
fn read_command() {
|
|
|
|
|
let mut data = set_up();
|
|
|
|
|
let mut buf = [0];
|
2020-10-08 22:02:20 +00:00
|
|
|
|
data.pit
|
|
|
|
|
.read(pit_bus_address(PortIOSpace::PortCommand), &mut buf);
|
2019-01-16 00:23:48 +00:00
|
|
|
|
assert_eq!(buf, [0]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Tests that latching prevents the read time from actually advancing.
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_timed_latch() {
|
|
|
|
|
let mut data = set_up();
|
|
|
|
|
write_command(
|
|
|
|
|
&mut data.pit,
|
|
|
|
|
CommandCounter::CommandCounter0 as u8
|
|
|
|
|
| CommandAccess::CommandRWBoth as u8
|
|
|
|
|
| CommandMode::CommandInterrupt as u8,
|
|
|
|
|
);
|
|
|
|
|
write_counter(&mut data.pit, 0, 0xffff, CommandAccess::CommandRWBoth);
|
|
|
|
|
write_command(
|
|
|
|
|
&mut data.pit,
|
|
|
|
|
CommandCounter::CommandCounter0 as u8 | CommandAccess::CommandLatch as u8,
|
|
|
|
|
);
|
|
|
|
|
data.clock.lock().add_ns(25_000_000);
|
|
|
|
|
// The counter should ignore this second latch.
|
|
|
|
|
write_command(
|
|
|
|
|
&mut data.pit,
|
|
|
|
|
CommandCounter::CommandCounter0 as u8 | CommandAccess::CommandLatch as u8,
|
|
|
|
|
);
|
|
|
|
|
read_counter(&mut data.pit, 0, 0xffff, CommandAccess::CommandRWBoth);
|
|
|
|
|
// It should, however, store the count for this latch.
|
|
|
|
|
write_command(
|
|
|
|
|
&mut data.pit,
|
|
|
|
|
CommandCounter::CommandCounter0 as u8 | CommandAccess::CommandLatch as u8,
|
|
|
|
|
);
|
|
|
|
|
read_counter(
|
|
|
|
|
&mut data.pit,
|
|
|
|
|
0,
|
|
|
|
|
0xffff - ((25_000_000 * FREQUENCY_HZ) / NANOS_PER_SEC) as u16,
|
|
|
|
|
CommandAccess::CommandRWBoth,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Tests Mode 0 (Interrupt on terminal count); checks whether IRQ has been asserted.
|
|
|
|
|
#[test]
|
|
|
|
|
fn interrupt_mode() {
|
|
|
|
|
let mut data = set_up();
|
|
|
|
|
write_command(
|
|
|
|
|
&mut data.pit,
|
|
|
|
|
CommandCounter::CommandCounter0 as u8
|
|
|
|
|
| CommandAccess::CommandRWBoth as u8
|
|
|
|
|
| CommandMode::CommandInterrupt as u8,
|
|
|
|
|
);
|
|
|
|
|
write_counter(&mut data.pit, 0, 0xffff, CommandAccess::CommandRWBoth);
|
|
|
|
|
// Advance clock enough to trigger interrupt.
|
|
|
|
|
advance_by_ticks(&mut data, 0xffff);
|
|
|
|
|
assert_eq!(data.irqfd.read().unwrap(), 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Tests that Rate Generator mode (mode 2) handls the interrupt properly when the timer
|
|
|
|
|
/// expires and that it resets the timer properly.
|
|
|
|
|
#[test]
|
|
|
|
|
fn rate_gen_mode() {
|
|
|
|
|
let mut data = set_up();
|
|
|
|
|
write_command(
|
|
|
|
|
&mut data.pit,
|
|
|
|
|
CommandCounter::CommandCounter0 as u8
|
|
|
|
|
| CommandAccess::CommandRWBoth as u8
|
|
|
|
|
| CommandMode::CommandRateGen as u8,
|
|
|
|
|
);
|
|
|
|
|
write_counter(&mut data.pit, 0, 0xffff, CommandAccess::CommandRWBoth);
|
|
|
|
|
// Repatedly advance clock and expect interrupt.
|
|
|
|
|
advance_by_ticks(&mut data, 0xffff);
|
|
|
|
|
assert_eq!(data.irqfd.read().unwrap(), 1);
|
|
|
|
|
|
|
|
|
|
// Repatedly advance clock and expect interrupt.
|
|
|
|
|
advance_by_ticks(&mut data, 0xffff);
|
|
|
|
|
assert_eq!(data.irqfd.read().unwrap(), 1);
|
|
|
|
|
|
|
|
|
|
// Repatedly advance clock and expect interrupt.
|
|
|
|
|
advance_by_ticks(&mut data, 0xffff);
|
|
|
|
|
assert_eq!(data.irqfd.read().unwrap(), 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Tests that square wave mode advances the counter correctly.
|
|
|
|
|
#[test]
|
|
|
|
|
fn square_wave_counter_read() {
|
|
|
|
|
let mut data = set_up();
|
|
|
|
|
write_command(
|
|
|
|
|
&mut data.pit,
|
|
|
|
|
CommandCounter::CommandCounter0 as u8
|
|
|
|
|
| CommandAccess::CommandRWBoth as u8
|
|
|
|
|
| CommandMode::CommandSquareWaveGen as u8,
|
|
|
|
|
);
|
|
|
|
|
write_counter(&mut data.pit, 0, 0xffff, CommandAccess::CommandRWBoth);
|
|
|
|
|
|
|
|
|
|
advance_by_ticks(&mut data, 10_000);
|
|
|
|
|
read_counter(
|
|
|
|
|
&mut data.pit,
|
|
|
|
|
0,
|
|
|
|
|
0xffff - 10_000 * 2,
|
|
|
|
|
CommandAccess::CommandRWBoth,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Tests that rategen mode updates the counter correctly.
|
|
|
|
|
#[test]
|
|
|
|
|
fn rate_gen_counter_read() {
|
|
|
|
|
let mut data = set_up();
|
|
|
|
|
write_command(
|
|
|
|
|
&mut data.pit,
|
|
|
|
|
CommandCounter::CommandCounter0 as u8
|
|
|
|
|
| CommandAccess::CommandRWBoth as u8
|
|
|
|
|
| CommandMode::CommandRateGen as u8,
|
|
|
|
|
);
|
|
|
|
|
write_counter(&mut data.pit, 0, 0xffff, CommandAccess::CommandRWBoth);
|
|
|
|
|
|
|
|
|
|
advance_by_ticks(&mut data, 10_000);
|
|
|
|
|
read_counter(
|
|
|
|
|
&mut data.pit,
|
|
|
|
|
0,
|
|
|
|
|
0xffff - 10_000,
|
|
|
|
|
CommandAccess::CommandRWBoth,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Tests that interrupt counter mode updates the counter correctly.
|
|
|
|
|
#[test]
|
|
|
|
|
fn interrupt_counter_read() {
|
|
|
|
|
let mut data = set_up();
|
|
|
|
|
write_command(
|
|
|
|
|
&mut data.pit,
|
|
|
|
|
CommandCounter::CommandCounter0 as u8
|
|
|
|
|
| CommandAccess::CommandRWBoth as u8
|
|
|
|
|
| CommandMode::CommandInterrupt as u8,
|
|
|
|
|
);
|
|
|
|
|
write_counter(&mut data.pit, 0, 0xffff, CommandAccess::CommandRWBoth);
|
|
|
|
|
|
|
|
|
|
advance_by_ticks(&mut data, 10_000);
|
|
|
|
|
read_counter(
|
|
|
|
|
&mut data.pit,
|
|
|
|
|
0,
|
|
|
|
|
0xffff - 10_000,
|
|
|
|
|
CommandAccess::CommandRWBoth,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
advance_by_ticks(&mut data, (3 * FREQUENCY_HZ).into());
|
|
|
|
|
read_counter(&mut data.pit, 0, 0, CommandAccess::CommandRWBoth);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Tests that ReadBack count works properly for `low` access mode.
|
|
|
|
|
#[test]
|
|
|
|
|
fn read_back_count_access_low() {
|
|
|
|
|
let mut data = set_up();
|
|
|
|
|
write_command(
|
|
|
|
|
&mut data.pit,
|
|
|
|
|
CommandCounter::CommandCounter0 as u8
|
|
|
|
|
| CommandAccess::CommandRWLeast as u8
|
|
|
|
|
| CommandMode::CommandInterrupt as u8,
|
|
|
|
|
);
|
|
|
|
|
write_counter(&mut data.pit, 0, 0xffff, CommandAccess::CommandRWLeast);
|
|
|
|
|
write_command(
|
|
|
|
|
&mut data.pit,
|
|
|
|
|
CommandCounter::CommandReadBack as u8
|
|
|
|
|
| CommandReadBackLatch::CommandRBLatchCount as u8
|
|
|
|
|
| CommandReadBackCounters::CommandRBCounter0 as u8,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Advance 100 ticks and verify that low byte of counter is appropriately updated.
|
|
|
|
|
advance_by_ticks(&mut data, 100);
|
|
|
|
|
write_command(
|
|
|
|
|
&mut data.pit,
|
|
|
|
|
CommandCounter::CommandReadBack as u8
|
|
|
|
|
| CommandReadBackLatch::CommandRBLatchCount as u8
|
|
|
|
|
| CommandReadBackCounters::CommandRBCounter0 as u8,
|
|
|
|
|
);
|
|
|
|
|
read_counter(&mut data.pit, 0, 0x00ff, CommandAccess::CommandRWLeast);
|
|
|
|
|
write_command(
|
|
|
|
|
&mut data.pit,
|
|
|
|
|
CommandCounter::CommandReadBack as u8
|
|
|
|
|
| CommandReadBackLatch::CommandRBLatchCount as u8
|
|
|
|
|
| CommandReadBackCounters::CommandRBCounter0 as u8,
|
|
|
|
|
);
|
|
|
|
|
read_counter(
|
|
|
|
|
&mut data.pit,
|
|
|
|
|
0,
|
|
|
|
|
(0xffff - 100) & 0x00ff,
|
|
|
|
|
CommandAccess::CommandRWLeast,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Tests that ReadBack count works properly for `high` access mode.
|
|
|
|
|
#[test]
|
|
|
|
|
fn read_back_count_access_high() {
|
|
|
|
|
let mut data = set_up();
|
|
|
|
|
write_command(
|
|
|
|
|
&mut data.pit,
|
|
|
|
|
CommandCounter::CommandCounter0 as u8
|
|
|
|
|
| CommandAccess::CommandRWMost as u8
|
|
|
|
|
| CommandMode::CommandInterrupt as u8,
|
|
|
|
|
);
|
|
|
|
|
write_counter(&mut data.pit, 0, 0xffff, CommandAccess::CommandRWLeast);
|
|
|
|
|
write_command(
|
|
|
|
|
&mut data.pit,
|
|
|
|
|
CommandCounter::CommandReadBack as u8
|
|
|
|
|
| CommandReadBackLatch::CommandRBLatchCount as u8
|
|
|
|
|
| CommandReadBackCounters::CommandRBCounter0 as u8,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Advance 100 ticks and verify that low byte of counter is appropriately updated.
|
|
|
|
|
advance_by_ticks(&mut data, 512);
|
|
|
|
|
write_command(
|
|
|
|
|
&mut data.pit,
|
|
|
|
|
CommandCounter::CommandReadBack as u8
|
|
|
|
|
| CommandReadBackLatch::CommandRBLatchCount as u8
|
|
|
|
|
| CommandReadBackCounters::CommandRBCounter0 as u8,
|
|
|
|
|
);
|
|
|
|
|
read_counter(&mut data.pit, 0, 0xff00, CommandAccess::CommandRWMost);
|
|
|
|
|
write_command(
|
|
|
|
|
&mut data.pit,
|
|
|
|
|
CommandCounter::CommandReadBack as u8
|
|
|
|
|
| CommandReadBackLatch::CommandRBLatchCount as u8
|
|
|
|
|
| CommandReadBackCounters::CommandRBCounter0 as u8,
|
|
|
|
|
);
|
|
|
|
|
read_counter(
|
|
|
|
|
&mut data.pit,
|
|
|
|
|
0,
|
|
|
|
|
(0xffff - 512) & 0xff00,
|
|
|
|
|
CommandAccess::CommandRWMost,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Tests that ReadBack status returns the expected values.
|
|
|
|
|
#[test]
|
|
|
|
|
fn read_back_status() {
|
|
|
|
|
let mut data = set_up();
|
|
|
|
|
write_command(
|
|
|
|
|
&mut data.pit,
|
|
|
|
|
CommandCounter::CommandCounter0 as u8
|
|
|
|
|
| CommandAccess::CommandRWBoth as u8
|
|
|
|
|
| CommandMode::CommandSWStrobe as u8,
|
|
|
|
|
);
|
|
|
|
|
write_counter(&mut data.pit, 0, 0xffff, CommandAccess::CommandRWBoth);
|
|
|
|
|
write_command(
|
|
|
|
|
&mut data.pit,
|
|
|
|
|
CommandCounter::CommandReadBack as u8
|
|
|
|
|
| CommandReadBackLatch::CommandRBLatchStatus as u8
|
|
|
|
|
| CommandReadBackCounters::CommandRBCounter0 as u8,
|
|
|
|
|
);
|
2019-02-06 00:09:28 +00:00
|
|
|
|
|
2019-01-16 00:23:48 +00:00
|
|
|
|
read_counter(
|
|
|
|
|
&mut data.pit,
|
|
|
|
|
0,
|
|
|
|
|
CommandAccess::CommandRWBoth as u16 | CommandMode::CommandSWStrobe as u16,
|
|
|
|
|
CommandAccess::CommandRWLeast,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn speaker_square_wave() {
|
|
|
|
|
let mut data = set_up();
|
|
|
|
|
write_command(
|
|
|
|
|
&mut data.pit,
|
|
|
|
|
CommandCounter::CommandCounter2 as u8
|
|
|
|
|
| CommandAccess::CommandRWBoth as u8
|
|
|
|
|
| CommandMode::CommandSquareWaveGen as u8,
|
|
|
|
|
);
|
|
|
|
|
write_counter(&mut data.pit, 2, 0xffff, CommandAccess::CommandRWBoth);
|
|
|
|
|
|
|
|
|
|
advance_by_ticks(&mut data, 128);
|
|
|
|
|
read_counter(
|
|
|
|
|
&mut data.pit,
|
|
|
|
|
2,
|
|
|
|
|
0xffff - 128 * 2,
|
|
|
|
|
CommandAccess::CommandRWBoth,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn speaker_rate_gen() {
|
|
|
|
|
let mut data = set_up();
|
|
|
|
|
write_command(
|
|
|
|
|
&mut data.pit,
|
|
|
|
|
CommandCounter::CommandCounter2 as u8
|
|
|
|
|
| CommandAccess::CommandRWBoth as u8
|
|
|
|
|
| CommandMode::CommandRateGen as u8,
|
|
|
|
|
);
|
|
|
|
|
write_counter(&mut data.pit, 2, 0xffff, CommandAccess::CommandRWBoth);
|
|
|
|
|
|
|
|
|
|
// In Rate Gen mode, the counter should start over when the gate is
|
|
|
|
|
// set to high using SpeakerWrite.
|
|
|
|
|
advance_by_ticks(&mut data, 128);
|
|
|
|
|
read_counter(&mut data.pit, 2, 0xffff - 128, CommandAccess::CommandRWBoth);
|
|
|
|
|
|
|
|
|
|
write_speaker(&mut data.pit, 0x1);
|
|
|
|
|
advance_by_ticks(&mut data, 128);
|
|
|
|
|
read_counter(&mut data.pit, 2, 0xffff - 128, CommandAccess::CommandRWBoth);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn speaker_interrupt() {
|
|
|
|
|
let mut data = set_up();
|
|
|
|
|
|
|
|
|
|
write_command(
|
|
|
|
|
&mut data.pit,
|
|
|
|
|
CommandCounter::CommandCounter2 as u8
|
|
|
|
|
| CommandAccess::CommandRWBoth as u8
|
|
|
|
|
| CommandMode::CommandInterrupt as u8,
|
|
|
|
|
);
|
|
|
|
|
write_counter(&mut data.pit, 2, 0xffff, CommandAccess::CommandRWBoth);
|
|
|
|
|
|
|
|
|
|
// In Interrupt mode, the counter should NOT start over when the gate is
|
|
|
|
|
// set to high using SpeakerWrite.
|
|
|
|
|
advance_by_ticks(&mut data, 128);
|
|
|
|
|
read_counter(&mut data.pit, 2, 0xffff - 128, CommandAccess::CommandRWBoth);
|
|
|
|
|
|
|
|
|
|
write_speaker(&mut data.pit, 0x1);
|
|
|
|
|
advance_by_ticks(&mut data, 128);
|
|
|
|
|
read_counter(&mut data.pit, 2, 0xffff - 256, CommandAccess::CommandRWBoth);
|
|
|
|
|
}
|
2019-02-25 23:14:31 +00:00
|
|
|
|
|
|
|
|
|
/// Verify that invalid reads and writes do not cause crashes.
|
|
|
|
|
#[test]
|
|
|
|
|
fn invalid_write_and_read() {
|
|
|
|
|
let mut data = set_up();
|
2020-10-08 22:02:20 +00:00
|
|
|
|
data.pit.write(
|
|
|
|
|
BusAccessInfo {
|
|
|
|
|
address: 0x44,
|
|
|
|
|
offset: 0x4,
|
|
|
|
|
id: 0,
|
|
|
|
|
},
|
|
|
|
|
&[0],
|
|
|
|
|
);
|
|
|
|
|
data.pit.read(
|
|
|
|
|
BusAccessInfo {
|
|
|
|
|
address: 0x55,
|
|
|
|
|
offset: 0x15,
|
|
|
|
|
id: 0,
|
|
|
|
|
},
|
|
|
|
|
&mut [0],
|
|
|
|
|
);
|
2019-02-25 23:14:31 +00:00
|
|
|
|
}
|
2019-01-16 00:23:48 +00:00
|
|
|
|
}
|