summary refs log tree commit diff
path: root/hypervisor/src/x86_64.rs
diff options
context:
space:
mode:
Diffstat (limited to 'hypervisor/src/x86_64.rs')
-rw-r--r--hypervisor/src/x86_64.rs294
1 files changed, 293 insertions, 1 deletions
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,
+}