diff options
Diffstat (limited to 'devices/src/ioapic.rs')
-rw-r--r-- | devices/src/ioapic.rs | 102 |
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); } |