summary refs log tree commit diff
diff options
context:
space:
mode:
authorMiriam Zimmerman <mutexlox@google.com>2019-03-13 11:14:45 -0700
committerchrome-bot <chrome-bot@chromium.org>2019-03-16 01:53:44 -0700
commitc698769b42779a8f621b0df48583c4307c596cb6 (patch)
tree6fa58592366686257b158a58138f73492ab457f4
parentc249a9fb84a600ebb4f8adace86952307b55ea58 (diff)
downloadcrosvm-c698769b42779a8f621b0df48583c4307c596cb6.tar
crosvm-c698769b42779a8f621b0df48583c4307c596cb6.tar.gz
crosvm-c698769b42779a8f621b0df48583c4307c596cb6.tar.bz2
crosvm-c698769b42779a8f621b0df48583c4307c596cb6.tar.lz
crosvm-c698769b42779a8f621b0df48583c4307c596cb6.tar.xz
crosvm-c698769b42779a8f621b0df48583c4307c596cb6.tar.zst
crosvm-c698769b42779a8f621b0df48583c4307c596cb6.zip
Add IOAPIC device skeleton.
This CL adds some necessary constants and types, as well as a few
skeleton function declarations, for an IOAPIC device.

I'm sending this CL first in the interest of minimizing CL size and
making future CLs easier to review.

TEST=Built
BUG=chromium:908689

Change-Id: Ib8ae37e0092c31d7cb8073070f9592baed236323
Reviewed-on: https://chromium-review.googlesource.com/1520809
Commit-Ready: David Tolnay <dtolnay@chromium.org>
Tested-by: Miriam Zimmerman <mutexlox@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: David Tolnay <dtolnay@chromium.org>
-rw-r--r--devices/src/ioapic.rs167
-rw-r--r--devices/src/lib.rs2
-rw-r--r--kvm/src/lib.rs6
3 files changed, 173 insertions, 2 deletions
diff --git a/devices/src/ioapic.rs b/devices/src/ioapic.rs
new file mode 100644
index 0000000..a4bb98e
--- /dev/null
+++ b/devices/src/ioapic.rs
@@ -0,0 +1,167 @@
+// 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.
+
+// Implementation of an intel 82093AA Input/Output Advanced Programmable Interrupt Controller
+// See https://pdos.csail.mit.edu/6.828/2016/readings/ia32/ioapic.pdf for a specification.
+
+use bit_field::*;
+use BusDevice;
+
+// TODO(mutexlox): once https://crrev.com/c/1519686 has landed, refactor these bitfields to use
+// better types where applicable.
+#[bitfield]
+#[derive(Clone, Copy, PartialEq)]
+pub struct RedirectionTableEntry {
+    vector: BitField8,
+    delivery_mode: BitField3,
+    dest_mode: BitField1,
+    delivery_status: BitField1,
+    polarity: BitField1,
+    remote_irr: BitField1,
+    trigger_mode: BitField1,
+    interrupt_mask: BitField1,
+    reserved: BitField39,
+    dest_id: BitField8,
+}
+
+#[allow(dead_code)]
+#[derive(Clone, Copy, PartialEq)]
+pub enum DeliveryStatus {
+    Idle = 0,
+    Pending = 1,
+}
+
+#[allow(dead_code)]
+#[derive(Clone, Copy, PartialEq)]
+pub enum InterruptRemappingFormat {
+    Compatibility = 0,
+    Remappable = 1,
+}
+
+#[allow(dead_code)]
+const IOAPIC_VERSION_ID: u32 = 0x00170011;
+#[allow(dead_code)]
+const IOAPIC_BASE_ADDRESS: u32 = 0xfec00000;
+// The Intel manual does not specify this size, but KVM uses it.
+#[allow(dead_code)]
+const IOAPIC_MEM_LENGTH_BYTES: usize = 0x100;
+
+// Constants for IOAPIC direct register offset.
+#[allow(dead_code)]
+const IOAPIC_REG_ID: u32 = 0x00;
+#[allow(dead_code)]
+const IOAPIC_REG_VERSION: u32 = 0x01;
+#[allow(dead_code)]
+const IOAPIC_REG_ARBITRATION_ID: u32 = 0x02;
+
+// Register offsets
+pub const IOREGSEL_OFF: u64 = 0x0;
+pub const IOREGSEL_DUMMY_UPPER_32_BITS_OFF: u64 = 0x4;
+pub const IOWIN_OFF: u64 = 0x10;
+
+// The RTC needs special treatment to work properly for Windows (or other OSs that use tick
+// stuffing). In order to avoid time drift, we need to guarantee that the correct number of RTC
+// interrupts are injected into the guest. This hack essentialy treats RTC interrupts as level
+// triggered, which allows the IOAPIC to be responsible for interrupt coalescing and allows the
+// IOAPIC to pass back whether or not the interrupt was coalesced to the CMOS (which allows the
+// CMOS to perform tick stuffing). This deviates from the IOAPIC spec in ways very similar to (but
+// not exactly the same as) KVM's IOAPIC.
+#[allow(dead_code)]
+const RTC_IRQ: u32 = 0x8;
+
+#[allow(dead_code)]
+pub struct Ioapic {
+    id: usize,
+    // Remote IRR for Edge Triggered Real Time Clock interrupts, which allows the CMOS to know when
+    // one of its interrupts is being coalesced.
+    rtc_remote_irr: bool,
+    current_interrupt_level_bitmap: u32,
+    redirect_table: [RedirectionTableEntry; kvm::NUM_IOAPIC_PINS],
+    ioregsel: u8,
+}
+
+impl BusDevice for Ioapic {
+    fn debug_label(&self) -> String {
+        "userspace IOAPIC".to_string()
+    }
+
+    fn read(&mut self, offset: u64, data: &mut [u8]) {
+        if data.len() > 8 || data.len() == 0 {
+            warn!("IOAPIC: Bad read size: {}", data.len());
+            return;
+        }
+        let out = match offset {
+            IOREGSEL_OFF => self.ioregsel.into(),
+            IOREGSEL_DUMMY_UPPER_32_BITS_OFF => 0,
+            IOWIN_OFF => self.ioapic_read(),
+            _ => {
+                warn!("IOAPIC: Bad read from offset {}", offset);
+                return;
+            }
+        };
+        let out_arr = out.to_ne_bytes();
+        for i in 0..4 {
+            if i < data.len() {
+                data[i] = out_arr[i];
+            }
+        }
+    }
+
+    fn write(&mut self, offset: u64, data: &[u8]) {
+        if data.len() > 8 || data.len() == 0 {
+            warn!("IOAPIC: Bad write size: {}", data.len());
+            return;
+        }
+        match offset {
+            IOREGSEL_OFF => self.ioregsel = data[0],
+            IOREGSEL_DUMMY_UPPER_32_BITS_OFF => {} // Ignored.
+            IOWIN_OFF => {
+                if data.len() != 4 {
+                    warn!("IOAPIC: Bad write size for iowin: {}", data.len());
+                    return;
+                }
+                let data_arr = [data[0], data[1], data[2], data[3]];
+                let val = u32::from_ne_bytes(data_arr);
+                self.ioapic_write(val);
+            }
+            _ => {
+                warn!("IOAPIC: Bad write to offset {}", offset);
+                return;
+            }
+        }
+    }
+}
+
+impl Ioapic {
+    pub fn new() -> Ioapic {
+        let mut entry = RedirectionTableEntry::new();
+        entry.set_interrupt_mask(1);
+        let entries = [entry; kvm::NUM_IOAPIC_PINS];
+        Ioapic {
+            id: 0,
+            rtc_remote_irr: false,
+            current_interrupt_level_bitmap: 0,
+            redirect_table: entries,
+            ioregsel: 0,
+        }
+    }
+
+    // The ioapic must be informed about EOIs in order to avoid sending multiple interrupts of the
+    // same type at the same time.
+    pub fn end_of_interrupt(&mut self, _vector: u8) {
+        unimplemented!();
+    }
+
+    pub fn service_irq(&mut self, _irq: u32, _level: bool) -> bool {
+        unimplemented!();
+    }
+
+    fn ioapic_write(&mut self, _val: u32) {
+        unimplemented!();
+    }
+
+    fn ioapic_read(&mut self) -> u32 {
+        unimplemented!();
+    }
+}
diff --git a/devices/src/lib.rs b/devices/src/lib.rs
index d4166eb..442939e 100644
--- a/devices/src/lib.rs
+++ b/devices/src/lib.rs
@@ -34,6 +34,7 @@ mod register_space;
 mod bus;
 mod cmos;
 mod i8042;
+mod ioapic;
 mod pci;
 mod pit;
 pub mod pl030;
@@ -49,6 +50,7 @@ pub use self::bus::Error as BusError;
 pub use self::bus::{Bus, BusDevice, BusRange};
 pub use self::cmos::Cmos;
 pub use self::i8042::I8042Device;
+pub use self::ioapic::Ioapic;
 pub use self::pci::{
     Ac97Dev, PciConfigIo, PciConfigMmio, PciDevice, PciDeviceError, PciInterruptPin, PciRoot,
 };
diff --git a/kvm/src/lib.rs b/kvm/src/lib.rs
index 6aa1532..3112fa0 100644
--- a/kvm/src/lib.rs
+++ b/kvm/src/lib.rs
@@ -274,6 +274,9 @@ pub enum PicId {
     Secondary = 1,
 }
 
+/// Number of pins on the IOAPIC.
+pub const NUM_IOAPIC_PINS: usize = 24;
+
 // Used to invert the order when stored in a max-heap.
 #[derive(Copy, Clone, Eq, PartialEq)]
 struct MemSlot(u32);
@@ -937,8 +940,7 @@ impl Vm {
     pub fn enable_split_irqchip(&self) -> Result<()> {
         let mut cap: kvm_enable_cap = Default::default();
         cap.cap = KVM_CAP_SPLIT_IRQCHIP;
-        // TODO(mutexlox): When the IOAPIC is implemented, refer to its "number of pins" constant.
-        cap.args[0] = 24;
+        cap.args[0] = NUM_IOAPIC_PINS as u64;
         self.kvm_enable_cap(&cap)
     }