diff options
author | Colin Downs-Razouk <colindr@google.com> | 2020-05-04 10:45:55 -0700 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2020-05-28 08:00:30 +0000 |
commit | c31a7b9fc689854d0ce3d98a1ce0dd75e5977c2d (patch) | |
tree | 0362cbcbc9f8b50777792f6f3c20193ab2d472b1 | |
parent | 1a9f2a5454481a511257625f985d503c45fd8246 (diff) | |
download | crosvm-c31a7b9fc689854d0ce3d98a1ce0dd75e5977c2d.tar crosvm-c31a7b9fc689854d0ce3d98a1ce0dd75e5977c2d.tar.gz crosvm-c31a7b9fc689854d0ce3d98a1ce0dd75e5977c2d.tar.bz2 crosvm-c31a7b9fc689854d0ce3d98a1ce0dd75e5977c2d.tar.lz crosvm-c31a7b9fc689854d0ce3d98a1ce0dd75e5977c2d.tar.xz crosvm-c31a7b9fc689854d0ce3d98a1ce0dd75e5977c2d.tar.zst crosvm-c31a7b9fc689854d0ce3d98a1ce0dd75e5977c2d.zip |
hypervisor: x86 irqchip structs
Hypervisor-agnostic structures for the pic, ioapic, lapic, and pit. These are derived from existing structures in the pic, ioapic, and pit implementations, as well as from the kvm_sys bindings. Includes From implementations converting these structures to their associated KVM structures. Also includes tests for these conversion implementations. BUG=chromium:1077058 TEST=added tests to convert kvm structures to hypervisor-agnostic structures Change-Id: Ie2f254bf2dba3aed755008296c00cb6a49f845fd Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2197716 Tested-by: kokoro <noreply+kokoro@google.com> Commit-Queue: Colin Downs-Razouk <colindr@google.com> Reviewed-by: Zach Reizner <zachr@chromium.org>
-rw-r--r-- | Cargo.lock | 2 | ||||
-rw-r--r-- | hypervisor/Cargo.toml | 2 | ||||
-rw-r--r-- | hypervisor/src/kvm/x86_64.rs | 384 | ||||
-rw-r--r-- | hypervisor/src/x86_64.rs | 294 |
4 files changed, 679 insertions, 3 deletions
diff --git a/Cargo.lock b/Cargo.lock index 2443037..124d045 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -349,6 +349,8 @@ dependencies = [ name = "hypervisor" version = "0.1.0" dependencies = [ + "bit_field 0.1.0", + "enumn 0.1.0", "kvm 0.1.0", "kvm_sys 0.1.0", "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/hypervisor/Cargo.toml b/hypervisor/Cargo.toml index 8f07d5d..db3a095 100644 --- a/hypervisor/Cargo.toml +++ b/hypervisor/Cargo.toml @@ -5,6 +5,8 @@ authors = ["The Chromium OS Authors"] edition = "2018" [dependencies] +bit_field = { path = "../bit_field" } +enumn = { path = "../enumn" } libc = "*" kvm = { path = "../kvm" } kvm_sys = { path = "../kvm_sys" } diff --git a/hypervisor/src/kvm/x86_64.rs b/hypervisor/src/kvm/x86_64.rs index 43d6c97..06774f4 100644 --- a/hypervisor/src/kvm/x86_64.rs +++ b/hypervisor/src/kvm/x86_64.rs @@ -2,12 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +use std::convert::TryInto; + use kvm_sys::*; use libc::E2BIG; use sys_util::{ioctl_with_mut_ptr, Error, Result}; use super::{Kvm, KvmVcpu, KvmVm}; -use crate::{CpuId, CpuIdEntry, HypervisorX86_64, Regs, VcpuX86_64, VmX86_64}; +use crate::{ + CpuId, CpuIdEntry, HypervisorX86_64, IoapicRedirectionTableEntry, IoapicState, LapicState, + PicState, PitChannelState, PitState, Regs, VcpuX86_64, VmX86_64, +}; type KvmCpuId = kvm::CpuId; @@ -93,11 +98,222 @@ impl VcpuX86_64 for KvmVcpu { } } +impl From<&kvm_pic_state> for PicState { + fn from(item: &kvm_pic_state) -> Self { + PicState { + last_irr: item.last_irr, + irr: item.irr, + imr: item.imr, + isr: item.isr, + priority_add: item.priority_add, + irq_base: item.irq_base, + read_reg_select: item.read_reg_select != 0, + poll: item.poll != 0, + special_mask: item.special_mask != 0, + init_state: item.init_state.into(), + auto_eoi: item.auto_eoi != 0, + rotate_on_auto_eoi: item.rotate_on_auto_eoi != 0, + special_fully_nested_mode: item.special_fully_nested_mode != 0, + use_4_byte_icw: item.init4 != 0, + elcr: item.elcr, + elcr_mask: item.elcr_mask, + } + } +} + +impl From<&PicState> for kvm_pic_state { + fn from(item: &PicState) -> Self { + kvm_pic_state { + last_irr: item.last_irr, + irr: item.irr, + imr: item.imr, + isr: item.isr, + priority_add: item.priority_add, + irq_base: item.irq_base, + read_reg_select: item.read_reg_select as u8, + poll: item.poll as u8, + special_mask: item.special_mask as u8, + init_state: item.init_state as u8, + auto_eoi: item.auto_eoi as u8, + rotate_on_auto_eoi: item.rotate_on_auto_eoi as u8, + special_fully_nested_mode: item.special_fully_nested_mode as u8, + init4: item.use_4_byte_icw as u8, + elcr: item.elcr, + elcr_mask: item.elcr_mask, + } + } +} + +impl From<&kvm_ioapic_state> for IoapicState { + fn from(item: &kvm_ioapic_state) -> Self { + let mut state = IoapicState { + base_address: item.base_address, + ioregsel: item.ioregsel, + ioapicid: item.id, + current_interrupt_level_bitmap: item.irr, + redirect_table: [IoapicRedirectionTableEntry::default(); 24], + }; + for (in_state, out_state) in item.redirtbl.iter().zip(state.redirect_table.iter_mut()) { + *out_state = in_state.into(); + } + state + } +} + +impl From<&IoapicRedirectionTableEntry> for kvm_ioapic_state__bindgen_ty_1 { + fn from(item: &IoapicRedirectionTableEntry) -> Self { + kvm_ioapic_state__bindgen_ty_1 { + // IoapicRedirectionTableEntry layout matches the exact bit layout of a hardware + // ioapic redirection table entry, so we can simply do a 64-bit copy + bits: item.get(0, 64), + } + } +} + +impl From<&kvm_ioapic_state__bindgen_ty_1> for IoapicRedirectionTableEntry { + fn from(item: &kvm_ioapic_state__bindgen_ty_1) -> Self { + let mut entry = IoapicRedirectionTableEntry::default(); + // Safe because the 64-bit layout of the IoapicRedirectionTableEntry matches the kvm_sys + // table entry layout + entry.set(0, 64, unsafe { item.bits as u64 }); + entry + } +} + +impl From<&IoapicState> for kvm_ioapic_state { + fn from(item: &IoapicState) -> Self { + let mut state = kvm_ioapic_state { + base_address: item.base_address, + ioregsel: item.ioregsel, + id: item.ioapicid, + irr: item.current_interrupt_level_bitmap, + ..Default::default() + }; + for (in_state, out_state) in item.redirect_table.iter().zip(state.redirtbl.iter_mut()) { + *out_state = in_state.into(); + } + state + } +} + +impl From<&LapicState> for kvm_lapic_state { + fn from(item: &LapicState) -> Self { + let mut state = kvm_lapic_state::default(); + // There are 64 lapic registers + for (reg, value) in item.regs.iter().enumerate() { + // Each lapic register is 16 bytes, but only the first 4 are used + let reg_offset = 16 * reg; + let sliceu8 = unsafe { + // This array is only accessed as parts of a u32 word, so interpret it as a u8 array. + // to_le_bytes() produces an array of u8, not i8(c_char). + std::mem::transmute::<&mut [i8], &mut [u8]>( + &mut state.regs[reg_offset..reg_offset + 4], + ) + }; + sliceu8.copy_from_slice(&value.to_le_bytes()); + } + state + } +} + +impl From<&kvm_lapic_state> for LapicState { + fn from(item: &kvm_lapic_state) -> Self { + let mut state = LapicState { regs: [0; 64] }; + // There are 64 lapic registers + for reg in 0..64 { + // Each lapic register is 16 bytes, but only the first 4 are used + let reg_offset = 16 * reg; + let bytes = unsafe { + // This array is only accessed as parts of a u32 word, so interpret it as a u8 array. + // from_le_bytes() only works on arrays of u8, not i8(c_char). + std::mem::transmute::<&[i8], &[u8]>(&item.regs[reg_offset..reg_offset + 4]) + }; + state.regs[reg] = u32::from_le_bytes(bytes.try_into().unwrap()); + } + state + } +} + +impl From<&PitState> for kvm_pit_state2 { + fn from(item: &PitState) -> Self { + kvm_pit_state2 { + channels: [ + kvm_pit_channel_state::from(&item.channels[0]), + kvm_pit_channel_state::from(&item.channels[1]), + kvm_pit_channel_state::from(&item.channels[2]), + ], + flags: item.flags, + ..Default::default() + } + } +} + +impl From<&kvm_pit_state2> for PitState { + fn from(item: &kvm_pit_state2) -> Self { + PitState { + channels: [ + PitChannelState::from(&item.channels[0]), + PitChannelState::from(&item.channels[1]), + PitChannelState::from(&item.channels[2]), + ], + flags: item.flags, + } + } +} + +impl From<&PitChannelState> for kvm_pit_channel_state { + fn from(item: &PitChannelState) -> Self { + kvm_pit_channel_state { + count: item.count, + latched_count: item.latched_count, + count_latched: item.count_latched as u8, + status_latched: item.status_latched as u8, + status: item.status, + read_state: item.read_state as u8, + write_state: item.write_state as u8, + // kvm's write_latch only stores the low byte of the reload value + write_latch: item.reload_value as u8, + rw_mode: item.rw_mode as u8, + mode: item.mode, + bcd: item.bcd as u8, + gate: item.gate as u8, + count_load_time: item.count_load_time as i64, + } + } +} + +impl From<&kvm_pit_channel_state> for PitChannelState { + fn from(item: &kvm_pit_channel_state) -> Self { + PitChannelState { + count: item.count, + latched_count: item.latched_count, + count_latched: item.count_latched.into(), + status_latched: item.status_latched != 0, + status: item.status, + read_state: item.read_state.into(), + write_state: item.write_state.into(), + // kvm's write_latch only stores the low byte of the reload value + reload_value: item.write_latch as u16, + rw_mode: item.rw_mode.into(), + mode: item.mode, + bcd: item.bcd != 0, + gate: item.gate != 0, + count_load_time: item.count_load_time as u64, + } + } +} + #[cfg(test)] mod tests { + use crate::{ + DeliveryMode, DeliveryStatus, DestinationMode, IoapicRedirectionTableEntry, IoapicState, + LapicState, PicInitState, PicState, PitChannelState, PitRWMode, PitRWState, PitState, + TriggerMode, + }; + use kvm_sys::*; + use super::Kvm; use crate::HypervisorX86_64; - use kvm_sys::*; #[test] fn get_supported_cpuid() { @@ -121,4 +337,168 @@ mod tests { .unwrap(); assert!(cpuid.cpu_id_entries.len() > 4); } + + #[test] + fn pic_state() { + let state = PicState { + last_irr: 0b00000001, + irr: 0b00000010, + imr: 0b00000100, + isr: 0b00001000, + priority_add: 0b00010000, + irq_base: 0b00100000, + read_reg_select: false, + poll: true, + special_mask: true, + init_state: PicInitState::Icw3, + auto_eoi: true, + rotate_on_auto_eoi: false, + special_fully_nested_mode: true, + use_4_byte_icw: true, + elcr: 0b01000000, + elcr_mask: 0b10000000, + }; + + let kvm_state = kvm_pic_state::from(&state); + + assert_eq!(kvm_state.last_irr, 0b00000001); + assert_eq!(kvm_state.irr, 0b00000010); + assert_eq!(kvm_state.imr, 0b00000100); + assert_eq!(kvm_state.isr, 0b00001000); + assert_eq!(kvm_state.priority_add, 0b00010000); + assert_eq!(kvm_state.irq_base, 0b00100000); + assert_eq!(kvm_state.read_reg_select, 0); + assert_eq!(kvm_state.poll, 1); + assert_eq!(kvm_state.special_mask, 1); + assert_eq!(kvm_state.init_state, 0b10); + assert_eq!(kvm_state.auto_eoi, 1); + assert_eq!(kvm_state.rotate_on_auto_eoi, 0); + assert_eq!(kvm_state.special_fully_nested_mode, 1); + assert_eq!(kvm_state.auto_eoi, 1); + assert_eq!(kvm_state.elcr, 0b01000000); + assert_eq!(kvm_state.elcr_mask, 0b10000000); + + let orig_state = PicState::from(&kvm_state); + assert_eq!(state, orig_state); + } + + #[test] + fn ioapic_state() { + let mut entry = IoapicRedirectionTableEntry::default(); + // default entry should be 0 + assert_eq!(entry.get(0, 64), 0); + + // set some values on our entry + entry.set_vector(0b11111111); + entry.set_delivery_mode(DeliveryMode::SMI); + entry.set_dest_mode(DestinationMode::Physical); + entry.set_delivery_status(DeliveryStatus::Pending); + entry.set_polarity(1); + entry.set_remote_irr(true); + entry.set_trigger_mode(TriggerMode::Level); + entry.set_interrupt_mask(true); + entry.set_dest_id(0b10101010); + + // Bit repr as: destid-reserved--------------------------------flags----vector-- + let bit_repr = 0b1010101000000000000000000000000000000000000000011111001011111111; + // where flags is [interrupt_mask(1), trigger_mode(Level=1), remote_irr(1), polarity(1), + // delivery_status(Pending=1), dest_mode(Physical=0), delivery_mode(SMI=010)] + + assert_eq!(entry.get(0, 64), bit_repr); + + let state = IoapicState { + base_address: 1, + ioregsel: 2, + ioapicid: 4, + current_interrupt_level_bitmap: 8, + redirect_table: [entry; 24], + }; + + let kvm_state = kvm_ioapic_state::from(&state); + assert_eq!(kvm_state.base_address, 1); + assert_eq!(kvm_state.ioregsel, 2); + assert_eq!(kvm_state.id, 4); + assert_eq!(kvm_state.irr, 8); + assert_eq!(kvm_state.pad, 0); + // check our entries + for i in 0..24 { + assert_eq!(unsafe { kvm_state.redirtbl[i].bits }, bit_repr); + } + + // compare with a conversion back + assert_eq!(state, IoapicState::from(&kvm_state)); + } + + #[test] + fn lapic_state() { + let mut state = LapicState { regs: [0; 64] }; + // Apic id register, 4 bytes each with a different bit set + state.regs[2] = 1 | 2 << 8 | 4 << 16 | 8 << 24; + + let kvm_state = kvm_lapic_state::from(&state); + + // check little endian bytes in kvm_state + for i in 0..4 { + assert_eq!( + unsafe { std::mem::transmute::<i8, u8>(kvm_state.regs[32 + i]) } as u8, + 2u8.pow(i as u32) + ); + } + + // Test converting back to a LapicState + assert_eq!(state, LapicState::from(&kvm_state)); + } + + #[test] + fn pit_state() { + let channel = PitChannelState { + count: 256, + latched_count: 512, + count_latched: PitRWState::LSB, + status_latched: false, + status: 7, + read_state: PitRWState::MSB, + write_state: PitRWState::Word1, + reload_value: 8, + rw_mode: PitRWMode::Both, + mode: 5, + bcd: false, + gate: true, + count_load_time: 1024, + }; + + let kvm_channel = kvm_pit_channel_state::from(&channel); + + // compare the various field translations + assert_eq!(kvm_channel.count, 256); + assert_eq!(kvm_channel.latched_count, 512); + assert_eq!(kvm_channel.count_latched, 1); + assert_eq!(kvm_channel.status_latched, 0); + assert_eq!(kvm_channel.status, 7); + assert_eq!(kvm_channel.read_state, 2); + assert_eq!(kvm_channel.write_state, 4); + assert_eq!(kvm_channel.write_latch, 8); + assert_eq!(kvm_channel.rw_mode, 3); + assert_eq!(kvm_channel.mode, 5); + assert_eq!(kvm_channel.bcd, 0); + assert_eq!(kvm_channel.gate, 1); + assert_eq!(kvm_channel.count_load_time, 1024); + + // convert back and compare + assert_eq!(channel, PitChannelState::from(&kvm_channel)); + + // convert the full pitstate + let state = PitState { + channels: [channel, channel, channel], + flags: 255, + }; + let kvm_state = kvm_pit_state2::from(&state); + + assert_eq!(kvm_state.flags, 255); + + // compare a channel + assert_eq!(channel, PitChannelState::from(&kvm_state.channels[0])); + // convert back and compare + assert_eq!(state, PitState::from(&kvm_state)); + } } diff --git a/hypervisor/src/x86_64.rs b/hypervisor/src/x86_64.rs index 87f0777..c311924 100644 --- a/hypervisor/src/x86_64.rs +++ b/hypervisor/src/x86_64.rs @@ -2,7 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -use sys_util::Result; +use bit_field::*; +use sys_util::{error, Result}; use crate::{Hypervisor, Vcpu, Vm}; @@ -34,6 +35,8 @@ pub trait VcpuX86_64: Vcpu { /// about the hypervisor or vm. Information is returned in the eax, ebx, ecx and edx registers /// by the cpu for a given function and index/subfunction (passed into the cpu via the eax and ecx /// register respectively). +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub struct CpuIdEntry { pub function: u32, pub index: u32, @@ -50,3 +53,292 @@ pub struct CpuId { /// The state of a vcpu's general-purpose registers. pub struct Regs {} + +#[bitfield] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum DestinationMode { + Physical = 0, + Logical = 1, +} + +#[bitfield] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum TriggerMode { + Edge = 0, + Level = 1, +} + +#[bitfield] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum DeliveryMode { + Fixed = 0b000, + Lowest = 0b001, + SMI = 0b010, // System management interrupt + RemoteRead = 0b011, // This is no longer supported by intel. + NMI = 0b100, // Non maskable interrupt + Init = 0b101, + Startup = 0b110, + External = 0b111, +} + +#[bitfield] +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct MsiAddressMessage { + pub reserved: BitField2, + #[bits = 1] + pub destination_mode: DestinationMode, + pub redirection_hint: BitField1, + pub reserved_2: BitField8, + pub destination_id: BitField8, + // According to Intel's implementation of MSI, these bits must always be 0xfee. + pub always_0xfee: BitField12, +} + +#[bitfield] +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct MsiDataMessage { + pub vector: BitField8, + #[bits = 3] + pub delivery_mode: DeliveryMode, + pub reserved: BitField3, + pub level: BitField1, + #[bits = 1] + pub trigger: TriggerMode, + pub reserved2: BitField16, +} + +#[bitfield] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum DeliveryStatus { + Idle = 0, + Pending = 1, +} + +/// Represents a IOAPIC redirection table entry. +#[bitfield] +#[derive(Clone, Copy, Default, PartialEq, Eq)] +pub struct IoapicRedirectionTableEntry { + vector: BitField8, + #[bits = 3] + delivery_mode: DeliveryMode, + #[bits = 1] + dest_mode: DestinationMode, + #[bits = 1] + delivery_status: DeliveryStatus, + polarity: BitField1, + remote_irr: bool, + #[bits = 1] + trigger_mode: TriggerMode, + interrupt_mask: bool, // true iff interrupts are masked. + reserved: BitField39, + dest_id: BitField8, +} + +/// Number of pins on the IOAPIC. +pub const NUM_IOAPIC_PINS: usize = 24; + +/// Represents the state of the IOAPIC. +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub struct IoapicState { + /// base_address is the memory base address for this IOAPIC. It cannot be changed. + pub base_address: u64, + /// ioregsel register. Used for selecting which entry of the redirect table to read/write. + pub ioregsel: u32, + /// ioapicid register. Bits 24 - 27 contain the APIC ID for this device. + pub ioapicid: u32, + /// current_interrupt_level_bitmap represents a bitmap of the state of all of the irq lines + pub current_interrupt_level_bitmap: u32, + /// redirect_table contains the irq settings for each irq line + pub redirect_table: [IoapicRedirectionTableEntry; 24], +} + +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum PicSelect { + Primary = 0, + Secondary = 1, +} + +#[repr(C)] +#[derive(enumn::N, Debug, Clone, Copy, PartialEq, Eq)] +pub enum PicInitState { + Icw1 = 0, + Icw2 = 1, + Icw3 = 2, + Icw4 = 3, +} + +/// Convenience implementation for converting from a u8 +impl From<u8> for PicInitState { + fn from(item: u8) -> Self { + PicInitState::n(item).unwrap_or_else(|| { + error!("Invalid PicInitState {}, setting to 0", item); + PicInitState::Icw1 + }) + } +} + +/// Represents the state of the PIC. +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct PicState { + /// Edge detection. + pub last_irr: u8, + /// Interrupt Request Register. + pub irr: u8, + /// Interrupt Mask Register. + pub imr: u8, + /// Interrupt Service Register. + pub isr: u8, + /// Highest priority, for priority rotation. + pub priority_add: u8, + pub irq_base: u8, + pub read_reg_select: bool, + pub poll: bool, + pub special_mask: bool, + pub init_state: PicInitState, + pub auto_eoi: bool, + pub rotate_on_auto_eoi: bool, + pub special_fully_nested_mode: bool, + /// PIC takes either 3 or 4 bytes of initialization command word during + /// initialization. use_4_byte_icw is true if 4 bytes of ICW are needed. + pub use_4_byte_icw: bool, + /// "Edge/Level Control Registers", for edge trigger selection. + /// When a particular bit is set, the corresponding IRQ is in level-triggered mode. Otherwise it + /// is in edge-triggered mode. + pub elcr: u8, + pub elcr_mask: u8, +} + +/// The LapicState represents the state of an x86 CPU's Local APIC. +/// The Local APIC consists of 64 128-bit registers, but only the first 32-bits of each register +/// can be used, so this structure only stores the first 32-bits of each register. +#[repr(C)] +#[derive(Clone, Copy)] +pub struct LapicState { + pub regs: [LapicRegister; 64], +} + +pub type LapicRegister = u32; + +// rust arrays longer than 32 need custom implementations of Debug +impl std::fmt::Debug for LapicState { + fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + self.regs[..].fmt(formatter) + } +} + +// rust arrays longer than 32 need custom implementations of PartialEq +impl PartialEq for LapicState { + fn eq(&self, other: &LapicState) -> bool { + self.regs[..] == other.regs[..] + } +} + +// Lapic equality is reflexive, so we impl Eq +impl Eq for LapicState {} + +/// The PitState represents the state of the PIT (aka the Programmable Interval Timer). +/// The state is simply the state of it's three channels. +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct PitState { + pub channels: [PitChannelState; 3], + /// Hypervisor-specific flags for setting the pit state. + pub flags: u32, +} + +/// The PitRWMode enum represents the access mode of a PIT channel. +/// Reads and writes to the Pit happen over Port-mapped I/O, which happens one byte at a time, +/// but the count values and latch values are two bytes. So the access mode controls which of the +/// two bytes will be read when. +#[repr(C)] +#[derive(enumn::N, Clone, Copy, Debug, PartialEq, Eq)] +pub enum PitRWMode { + /// None mode means that no access mode has been set. + None = 0, + /// Least mode means all reads/writes will read/write the least significant byte. + Least = 1, + /// Most mode means all reads/writes will read/write the most significant byte. + Most = 2, + /// Both mode means first the least significant byte will be read/written, then the + /// next read/write will read/write the most significant byte. + Both = 3, +} + +/// Convenience implementation for converting from a u8 +impl From<u8> for PitRWMode { + fn from(item: u8) -> Self { + PitRWMode::n(item).unwrap_or_else(|| { + error!("Invalid PitRWMode value {}, setting to 0", item); + PitRWMode::None + }) + } +} + +/// The PitRWState enum represents the state of reading to or writing from a channel. +/// This is related to the PitRWMode, it mainly gives more detail about the state of the channel +/// with respect to PitRWMode::Both. +#[repr(C)] +#[derive(enumn::N, Clone, Copy, Debug, PartialEq, Eq)] +pub enum PitRWState { + /// None mode means that no access mode has been set. + None = 0, + /// LSB means that the channel is in PitRWMode::Least access mode. + LSB = 1, + /// MSB means that the channel is in PitRWMode::Most access mode. + MSB = 2, + /// Word0 means that the channel is in PitRWMode::Both mode, and the least sginificant byte + /// has not been read/written yet. + Word0 = 3, + /// Word1 means that the channel is in PitRWMode::Both mode and the least significant byte + /// has already been read/written, and the next byte to be read/written will be the most + /// significant byte. + Word1 = 4, +} + +/// Convenience implementation for converting from a u8 +impl From<u8> for PitRWState { + fn from(item: u8) -> Self { + PitRWState::n(item).unwrap_or_else(|| { + error!("Invalid PitRWState value {}, setting to 0", item); + PitRWState::None + }) + } +} + +/// The PitChannelState represents the state of one of the PIT's three counters. +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct PitChannelState { + /// The starting value for the counter. + pub count: u32, + /// Stores the channel count from the last time the count was latched. + pub latched_count: u16, + /// Indicates the PitRWState state of reading the latch value. + pub count_latched: PitRWState, + /// Indicates whether ReadBack status has been latched. + pub status_latched: bool, + /// Stores the channel status from the last time the status was latched. The status contains + /// information about the access mode of this channel, but changing those bits in the status + /// will not change the behavior of the pit. + pub status: u8, + /// Indicates the PitRWState state of reading the counter. + pub read_state: PitRWState, + /// Indicates the PitRWState state of writing the counter. + pub write_state: PitRWState, + /// 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). + pub reload_value: u16, + /// The command access mode of this channel. + pub rw_mode: PitRWMode, + /// The operation mode of this channel. + pub mode: u8, + /// Whether or not we are in bcd mode. Not supported by KVM or crosvm's PIT implementation. + pub bcd: bool, + /// Value of the gate input pin. This only applies to channel 2. + pub gate: bool, + /// Guest boot nanosecond timestamp of when the count value was loaded. + pub count_load_time: u64, +} |