diff options
-rw-r--r-- | devices/src/pic.rs | 20 | ||||
-rw-r--r-- | kvm/src/lib.rs | 20 | ||||
-rw-r--r-- | src/linux.rs | 25 |
3 files changed, 58 insertions, 7 deletions
diff --git a/devices/src/pic.rs b/devices/src/pic.rs index 9b8235f..c18abef 100644 --- a/devices/src/pic.rs +++ b/devices/src/pic.rs @@ -7,10 +7,10 @@ // modern OSs that use a legacy BIOS. // The PIC is connected to the Local APIC on CPU0. -// Terminology note: The 8259A spec refers to "master" and "slave" PITs; the "slave"s are daisy +// Terminology note: The 8259A spec refers to "master" and "slave" PICs; the "slave"s are daisy // chained to the "master"s. // For the purposes of both using more descriptive terms and avoiding terms with lots of charged -// emotional context, this file refers to them instead as "primary" and "secondary" PITs. +// emotional context, this file refers to them instead as "primary" and "secondary" PICs. use crate::BusDevice; use sys_util::{debug, warn}; @@ -56,9 +56,9 @@ struct PicState { } pub struct Pic { - // TODO(mutexlox): Implement an APIC and add necessary state to the Pic. - - // index 0 (aka PicSelect::Primary) is the primary pic, the rest are secondary. + // Indicates a pending INTR signal to LINT0 of vCPU, checked by vCPU thread. + interrupt_request: bool, + // Index 0 (aka PicSelect::Primary) is the primary pic, the rest are secondary. pics: [PicState; 2], } @@ -175,9 +175,9 @@ impl Pic { // The secondary PIC has IRQs 8-15, so we subtract 8 from the IRQ number to get the bit // that should be masked here. In this case, bits 8 - 8 = 0 and 13 - 8 = 5. secondary_pic.elcr_mask = !((1 << 0) | (1 << 5)); - // TODO(mutexlox): Add logic to initialize APIC interrupt-related fields. Pic { + interrupt_request: false, pics: [primary_pic, secondary_pic], } } @@ -205,8 +205,14 @@ impl Pic { self.get_irq(PicSelect::Primary).is_some() } + /// Determines whether the PIC has fired an interrupt to LAPIC. + pub fn interrupt_requested(&self) -> bool { + self.interrupt_request + } + /// Determines the external interrupt number that the PIC is prepared to inject, if any. pub fn get_external_interrupt(&mut self) -> Option<u8> { + self.interrupt_request = false; let irq_primary = if let Some(irq) = self.get_irq(PicSelect::Primary) { irq } else { @@ -403,7 +409,7 @@ impl Pic { } if self.get_irq(PicSelect::Primary).is_some() { - // TODO(mutexlox): Signal local interrupt on APIC bus. + self.interrupt_request = true; // Note: this does not check if the interrupt is succesfully injected into // the CPU, just whether or not one is fired. true diff --git a/kvm/src/lib.rs b/kvm/src/lib.rs index 66ebd20..2d951f2 100644 --- a/kvm/src/lib.rs +++ b/kvm/src/lib.rs @@ -1313,6 +1313,26 @@ impl Vcpu { }); } + /// Request the VCPU to exit when it becomes possible to inject interrupts into the guest. + #[allow(clippy::cast_ptr_alignment)] + pub fn request_interrupt_window(&self) { + // Safe because we know we mapped enough memory to hold the kvm_run struct because the + // kernel told us how large it was. The pointer is page aligned so casting to a different + // type is well defined, hence the clippy allow attribute. + let run = unsafe { &mut *(self.run_mmap.as_ptr() as *mut kvm_run) }; + run.request_interrupt_window = 1; + } + + /// Checks if we can inject an interrupt into the VCPU. + #[allow(clippy::cast_ptr_alignment)] + pub fn ready_for_interrupt(&self) -> bool { + // Safe because we know we mapped enough memory to hold the kvm_run struct because the + // kernel told us how large it was. The pointer is page aligned so casting to a different + // type is well defined, hence the clippy allow attribute. + let run = unsafe { &mut *(self.run_mmap.as_ptr() as *mut kvm_run) }; + run.ready_for_interrupt_injection != 0 && run.if_flag != 0 + } + /// Gets the VCPU registers. #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] pub fn get_regs(&self) -> Result<kvm_regs> { diff --git a/src/linux.rs b/src/linux.rs index ad35407..a181e52 100644 --- a/src/linux.rs +++ b/src/linux.rs @@ -1349,6 +1349,26 @@ fn runnable_vcpu(vcpu: Vcpu, use_kvm_signals: bool, cpu_id: u32) -> Option<Runna } } +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn inject_interrupt(pic: &Arc<Mutex<devices::Pic>>, vcpu: &RunnableVcpu) { + let mut pic = pic.lock(); + if pic.interrupt_requested() && vcpu.ready_for_interrupt() { + if let Some(vector) = pic.get_external_interrupt() { + if let Err(e) = vcpu.interrupt(vector as u32) { + error!("PIC: failed to inject interrupt to vCPU0: {}", e); + } + } + // The second interrupt request should be handled immediately, so ask + // vCPU to exit as soon as possible. + if pic.interrupt_requested() { + vcpu.request_interrupt_window(); + } + } +} + +#[cfg(any(target_arch = "arm", target_arch = "aarch64"))] +fn inject_interrupt(pic: &Arc<Mutex<devices::Pic>>, vcpu: &RunnableVcpu) {} + fn run_vcpu( vcpu: Vcpu, cpu_id: u32, @@ -1480,6 +1500,11 @@ fn run_vcpu( run_mode_lock = run_mode_arc.cvar.wait(run_mode_lock); } } + + if cpu_id != 0 { continue; } + if let Some((pic, _)) = &split_irqchip { + inject_interrupt(pic, &vcpu); + } } } }) |