summary refs log tree commit diff
path: root/devices/src/ioapic.rs
diff options
context:
space:
mode:
Diffstat (limited to 'devices/src/ioapic.rs')
-rw-r--r--devices/src/ioapic.rs102
1 files changed, 81 insertions, 21 deletions
diff --git a/devices/src/ioapic.rs b/devices/src/ioapic.rs
index 6f8e358..09ccb89 100644
--- a/devices/src/ioapic.rs
+++ b/devices/src/ioapic.rs
@@ -8,7 +8,11 @@
 use crate::split_irqchip_common::*;
 use crate::BusDevice;
 use bit_field::*;
-use sys_util::warn;
+use kvm::Vm;
+use msg_socket::{MsgReceiver, MsgSender};
+use std::sync::Arc;
+use sys_util::{error, warn, EventFd, Result};
+use vm_control::{VmIrqRequest, VmIrqRequestSocket, VmIrqResponse};
 
 #[bitfield]
 #[derive(Clone, Copy, PartialEq)]
@@ -37,11 +41,9 @@ pub enum DeliveryStatus {
 }
 
 const IOAPIC_VERSION_ID: u32 = 0x00170011;
-#[allow(dead_code)]
-const IOAPIC_BASE_ADDRESS: u32 = 0xfec00000;
+pub const IOAPIC_BASE_ADDRESS: u64 = 0xfec00000;
 // The Intel manual does not specify this size, but KVM uses it.
-#[allow(dead_code)]
-const IOAPIC_MEM_LENGTH_BYTES: usize = 0x100;
+pub const IOAPIC_MEM_LENGTH_BYTES: u64 = 0x100;
 
 // Constants for IOAPIC direct register offset.
 const IOAPIC_REG_ID: u8 = 0x00;
@@ -49,10 +51,10 @@ const IOAPIC_REG_VERSION: u8 = 0x01;
 const IOAPIC_REG_ARBITRATION_ID: u8 = 0x02;
 
 // Register offsets
-pub const IOREGSEL_OFF: u8 = 0x0;
-pub const IOREGSEL_DUMMY_UPPER_32_BITS_OFF: u8 = 0x4;
-pub const IOWIN_OFF: u8 = 0x10;
-pub const IOWIN_SCALE: u8 = 0x2;
+const IOREGSEL_OFF: u8 = 0x0;
+const IOREGSEL_DUMMY_UPPER_32_BITS_OFF: u8 = 0x4;
+const IOWIN_OFF: u8 = 0x10;
+const IOWIN_SCALE: u8 = 0x2;
 
 /// Given an IRQ and whether or not the selector should refer to the high bits, return a selector
 /// suitable to use as an offset to read to/write from.
@@ -88,6 +90,9 @@ pub struct Ioapic {
     redirect_table: [RedirectionTableEntry; kvm::NUM_IOAPIC_PINS],
     // IOREGSEL is technically 32 bits, but only bottom 8 are writable: all others are fixed to 0.
     ioregsel: u8,
+    relay: Arc<GsiRelay>,
+    irqfd: Vec<EventFd>,
+    socket: VmIrqRequestSocket,
 }
 
 impl BusDevice for Ioapic {
@@ -148,17 +153,29 @@ impl BusDevice for Ioapic {
 }
 
 impl Ioapic {
-    pub fn new() -> Ioapic {
+    pub fn new(vm: &mut Vm, socket: VmIrqRequestSocket) -> Result<Ioapic> {
         let mut entry = RedirectionTableEntry::new();
         entry.set_interrupt_mask(true);
         let entries = [entry; kvm::NUM_IOAPIC_PINS];
-        Ioapic {
+        let mut irqfd = vec![];
+        for i in 0..kvm::NUM_IOAPIC_PINS {
+            irqfd.push(EventFd::new()?);
+            vm.register_irqfd(&irqfd[i], i as u32)?;
+        }
+        Ok(Ioapic {
             id: 0,
             rtc_remote_irr: false,
             current_interrupt_level_bitmap: 0,
             redirect_table: entries,
             ioregsel: 0,
-        }
+            relay: Default::default(),
+            irqfd,
+            socket,
+        })
+    }
+
+    pub fn register_relay(&mut self, relay: Arc<GsiRelay>) {
+        self.relay = relay;
     }
 
     // The ioapic must be informed about EOIs in order to avoid sending multiple interrupts of the
@@ -173,6 +190,12 @@ impl Ioapic {
             if self.redirect_table[i].get_vector() == vector
                 && self.redirect_table[i].get_trigger_mode() == TriggerMode::Level
             {
+                if self.relay.irqfd_resample[i].is_some() {
+                    self.service_irq(i, false);
+                }
+                if let Some(resample_evt) = &self.relay.irqfd_resample[i] {
+                    resample_evt.write(1).unwrap();
+                }
                 self.redirect_table[i].set_remote_irr(false);
             }
             // There is an inherent race condition in hardware if the OS is finished processing an
@@ -218,8 +241,7 @@ impl Ioapic {
             return false;
         }
 
-        // TODO(mutexlox): Pulse (assert and deassert) interrupt
-        let injected = true;
+        let injected = self.irqfd[irq].write(1).is_ok();
 
         if entry.get_trigger_mode() == TriggerMode::Level && level && injected {
             entry.set_remote_irr(true);
@@ -267,13 +289,42 @@ impl Ioapic {
                     // is the fix for this.
                 }
 
-                // TODO(mutexlox): route MSI.
                 if self.redirect_table[index].get_trigger_mode() == TriggerMode::Level
                     && self.current_interrupt_level_bitmap & (1 << index) != 0
                     && !self.redirect_table[index].get_interrupt_mask()
                 {
                     self.service_irq(index, true);
                 }
+
+                let mut address = MsiAddressMessage::new();
+                let mut data = MsiDataMessage::new();
+                let entry = &self.redirect_table[index];
+                address.set_destination_mode(entry.get_dest_mode());
+                address.set_destination_id(entry.get_dest_id());
+                address.set_always_0xfee(0xfee);
+                data.set_vector(entry.get_vector());
+                data.set_delivery_mode(entry.get_delivery_mode());
+                data.set_trigger(entry.get_trigger_mode());
+
+                let request = VmIrqRequest::AddMsiRoute {
+                    gsi: index as u32,
+                    msi_address: address.get(0, 32),
+                    msi_data: data.get(0, 32) as u32,
+                };
+                if let Err(e) = self.socket.send(&request) {
+                    error!("IOAPIC: failed to send AddMsiRoute request: {}", e);
+                    return;
+                }
+                match self.socket.recv() {
+                    Ok(response) => {
+                        if let VmIrqResponse::Err(e) = response {
+                            error!("IOAPIC: failed to add msi route: {}", e);
+                        }
+                    }
+                    Err(e) => {
+                        error!("IOAPIC: failed to receive AddMsiRoute response: {}", e);
+                    }
+                }
             }
         }
     }
@@ -307,6 +358,15 @@ mod tests {
     const DEFAULT_VECTOR: u8 = 0x3a;
     const DEFAULT_DESTINATION_ID: u8 = 0x5f;
 
+    fn new() -> Ioapic {
+        let kvm = kvm::Kvm::new().unwrap();
+        let gm = sys_util::GuestMemory::new(&vec![(sys_util::GuestAddress(0), 0x1000)]).unwrap();
+        let mut vm = Vm::new(&kvm, gm).unwrap();
+        vm.enable_split_irqchip().unwrap();
+        let (_, device_socket) = msg_socket::pair::<VmIrqResponse, VmIrqRequest>().unwrap();
+        Ioapic::new(&mut vm, device_socket).unwrap()
+    }
+
     fn set_up(trigger: TriggerMode) -> (Ioapic, usize) {
         let irq = kvm::NUM_IOAPIC_PINS - 1;
         let ioapic = set_up_with_irq(irq, trigger);
@@ -314,7 +374,7 @@ mod tests {
     }
 
     fn set_up_with_irq(irq: usize, trigger: TriggerMode) -> Ioapic {
-        let mut ioapic = Ioapic::new();
+        let mut ioapic = self::new();
         set_up_redirection_table_entry(&mut ioapic, irq, trigger);
         ioapic
     }
@@ -377,7 +437,7 @@ mod tests {
 
     #[test]
     fn write_read_ioregsel() {
-        let mut ioapic = Ioapic::new();
+        let mut ioapic = self::new();
         let data_write = [0x0f, 0xf0, 0x01, 0xff];
         let mut data_read = [0; 4];
 
@@ -391,7 +451,7 @@ mod tests {
     // Verify that version register is actually read-only.
     #[test]
     fn write_read_ioaic_reg_version() {
-        let mut ioapic = Ioapic::new();
+        let mut ioapic = self::new();
         let before = read_reg(&mut ioapic, IOAPIC_REG_VERSION);
         let data_write = !before;
 
@@ -402,7 +462,7 @@ mod tests {
     // Verify that only bits 27:24 of the IOAPICID are readable/writable.
     #[test]
     fn write_read_ioapic_reg_id() {
-        let mut ioapic = Ioapic::new();
+        let mut ioapic = self::new();
 
         write_reg(&mut ioapic, IOAPIC_REG_ID, 0x1f3e5d7c);
         assert_eq!(read_reg(&mut ioapic, IOAPIC_REG_ID), 0x0f000000);
@@ -411,7 +471,7 @@ mod tests {
     // Write to read-only register IOAPICARB.
     #[test]
     fn write_read_ioapic_arbitration_id() {
-        let mut ioapic = Ioapic::new();
+        let mut ioapic = self::new();
 
         let data_write_id = 0x1f3e5d7c;
         let expected_result = 0x0f000000;
@@ -436,7 +496,7 @@ mod tests {
     #[test]
     #[should_panic(expected = "index out of bounds: the len is 24 but the index is 24")]
     fn service_invalid_irq() {
-        let mut ioapic = Ioapic::new();
+        let mut ioapic = self::new();
         ioapic.service_irq(kvm::NUM_IOAPIC_PINS, false);
     }