mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2025-02-10 20:19:07 +00:00
devices: PIC: implement interrupt injection
TODO: Route irqfd to PIC, and use signal to kick vCPU thread when interrupt is triggered. BUG=chromium:908689 TEST=Unit tests in file. Change-Id: I9a87502da57e725d3bb26d746a337d0ba44ef337 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/1945797 Reviewed-by: Daniel Verkamp <dverkamp@chromium.org> Tested-by: kokoro <noreply+kokoro@google.com> Commit-Queue: Zhuocheng Ding <zhuocheng.ding@intel.corp-partner.google.com>
This commit is contained in:
parent
f2e90bf0b0
commit
db4c70d215
3 changed files with 58 additions and 7 deletions
|
@ -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
|
||||
|
|
|
@ -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> {
|
||||
|
|
25
src/linux.rs
25
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue