add external interrupt
All checks were successful
Check / Build ISO (nightly-2025-04-27) (push) Successful in 46s
All checks were successful
Check / Build ISO (nightly-2025-04-27) (push) Successful in 46s
This commit is contained in:
@ -7,10 +7,7 @@ use x86_64::{
|
||||
PhysAddr,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
constant::{BITS_PER_ENTRY, PAGE_SIZE},
|
||||
info,
|
||||
};
|
||||
use crate::constant::{BITS_PER_ENTRY, PAGE_SIZE};
|
||||
|
||||
pub static MAX_MEMORY: Once<usize> = Once::new();
|
||||
|
||||
|
@ -16,7 +16,7 @@ pub fn setup_exec_controls() -> Result<(), &'static str> {
|
||||
raw_pin_exec_ctrl &= (reserved_bits >> 32) as u32;
|
||||
|
||||
let mut pin_exec_ctrl = vmcs::controls::PinBasedVmExecutionControls::from(raw_pin_exec_ctrl);
|
||||
pin_exec_ctrl.set_external_interrupt_exiting(false);
|
||||
pin_exec_ctrl.set_external_interrupt_exiting(true);
|
||||
|
||||
pin_exec_ctrl.write()?;
|
||||
|
||||
|
@ -1,12 +1,28 @@
|
||||
use x86::vmx::vmcs;
|
||||
use x86::vmx::{self, vmcs};
|
||||
use x86_64::structures::paging::{FrameAllocator, PhysFrame, Size4KiB};
|
||||
|
||||
use super::qual::QualIo;
|
||||
use crate::{
|
||||
info,
|
||||
vmm::x86_64::intel::{register::GuestRegisters, vmwrite},
|
||||
interrupt::subscriber::InterruptContext,
|
||||
vmm::x86_64::intel::{
|
||||
register::GuestRegisters, vmcs::controls::EntryIntrInfo, vmread, vmwrite,
|
||||
},
|
||||
};
|
||||
|
||||
pub fn vmm_interrupt_subscriber(vcpu_ptr: *mut core::ffi::c_void, context: &InterruptContext) {
|
||||
if vcpu_ptr.is_null() {
|
||||
return;
|
||||
}
|
||||
|
||||
let vcpu = unsafe { &mut *(vcpu_ptr as *mut super::vcpu::IntelVCpu) };
|
||||
|
||||
if 0x20 <= context.vector && context.vector <= 0x20 + 16 {
|
||||
let irq = context.vector - 0x20;
|
||||
vcpu.pending_irq |= 1 << irq;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum InitPhase {
|
||||
Uninitialized,
|
||||
@ -66,7 +82,80 @@ impl PIC {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_io_in(&self, regs: &mut GuestRegisters, qual: QualIo) {
|
||||
pub fn inject_external_interrupt(
|
||||
&mut self,
|
||||
pending_irq: &mut u16,
|
||||
) -> Result<bool, &'static str> {
|
||||
let pending = *pending_irq;
|
||||
|
||||
if pending == 0 {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
if self.primary_phase != InitPhase::Initialized {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let eflags = vmread(vmx::vmcs::guest::RFLAGS)?;
|
||||
if eflags >> 9 & 1 == 0 {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let interruptibility = vmread(vmx::vmcs::guest::INTERRUPTIBILITY_STATE)?;
|
||||
if interruptibility & 0x3 != 0 {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let is_secondary_masked = (self.primary_mask >> 2) & 1 != 0;
|
||||
|
||||
for i in 0..16 {
|
||||
if is_secondary_masked && i >= 8 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let irq_bit = 1 << i;
|
||||
if pending & irq_bit == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let delta = if i < 8 { i } else { i - 8 };
|
||||
let is_masked = if i < 8 {
|
||||
(self.primary_mask >> delta) & 1 != 0
|
||||
} else {
|
||||
let is_irq_masked = (self.secondary_mask >> delta) & 1 != 0;
|
||||
is_secondary_masked || is_irq_masked
|
||||
};
|
||||
|
||||
if is_masked {
|
||||
continue;
|
||||
}
|
||||
|
||||
let interrupt_info = EntryIntrInfo::new()
|
||||
.with_vector(
|
||||
delta as u8
|
||||
+ if i < 8 {
|
||||
self.primary_base
|
||||
} else {
|
||||
self.secondary_base
|
||||
},
|
||||
)
|
||||
.with_typ(0)
|
||||
.with_ec_available(false)
|
||||
.with_valid(true);
|
||||
|
||||
vmwrite(
|
||||
vmx::vmcs::control::VMENTRY_INTERRUPTION_INFO_FIELD,
|
||||
u32::from(interrupt_info) as u64,
|
||||
)?;
|
||||
|
||||
*pending_irq &= !irq_bit;
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
fn handle_io_in(&self, regs: &mut GuestRegisters, qual: QualIo) {
|
||||
match qual.port() {
|
||||
0x0CF8..=0x0CFF => regs.rax = 0,
|
||||
0xC000..=0xCFFF => {} //ignore
|
||||
@ -77,7 +166,7 @@ impl PIC {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_io_out(&mut self, regs: &mut GuestRegisters, qual: QualIo) {
|
||||
fn handle_io_out(&mut self, regs: &mut GuestRegisters, qual: QualIo) {
|
||||
match qual.port() {
|
||||
0x0CF8..=0x0CFF => {} //ignore
|
||||
0xC000..=0xCFFF => {} //ignore
|
||||
@ -88,7 +177,7 @@ impl PIC {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_pic_in(&self, regs: &mut GuestRegisters, qual: QualIo) {
|
||||
fn handle_pic_in(&self, regs: &mut GuestRegisters, qual: QualIo) {
|
||||
match qual.port() {
|
||||
0x20 => {
|
||||
let v = match self.primary_read_sel {
|
||||
@ -120,7 +209,7 @@ impl PIC {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_pic_out(&mut self, regs: &mut GuestRegisters, qual: QualIo) {
|
||||
fn handle_pic_out(&mut self, regs: &mut GuestRegisters, qual: QualIo) {
|
||||
let pic = self;
|
||||
let dx = regs.rax as u8;
|
||||
match qual.port() {
|
||||
|
@ -1,3 +1,5 @@
|
||||
use core::arch::asm;
|
||||
|
||||
use raw_cpuid::cpuid;
|
||||
use x86_64::{
|
||||
registers::control::Cr4Flags,
|
||||
@ -6,13 +8,13 @@ use x86_64::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
info,
|
||||
info, interrupt,
|
||||
vmm::{
|
||||
x86_64::{
|
||||
common::{self, read_msr},
|
||||
intel::{
|
||||
auditor, controls, cpuid, ept,
|
||||
io::IOBitmap,
|
||||
io::{vmm_interrupt_subscriber, IOBitmap},
|
||||
msr::{self, ShadowMsr},
|
||||
qual::{QualCr, QualIo},
|
||||
register::GuestRegisters,
|
||||
@ -47,6 +49,7 @@ pub struct IntelVCpu {
|
||||
pub ia32e_enabled: bool,
|
||||
pic: super::io::PIC,
|
||||
io_bitmap: IOBitmap,
|
||||
pub pending_irq: u16,
|
||||
}
|
||||
|
||||
impl IntelVCpu {
|
||||
@ -77,7 +80,22 @@ impl IntelVCpu {
|
||||
|
||||
match exit_reason {
|
||||
VmxExitReason::HLT => {
|
||||
info!("VM hlt");
|
||||
let injected = self
|
||||
.pic
|
||||
.inject_external_interrupt(&mut self.pending_irq)
|
||||
.unwrap_or(false);
|
||||
|
||||
if !injected {
|
||||
unsafe {
|
||||
asm!("sti");
|
||||
asm!("nop");
|
||||
asm!("cli");
|
||||
}
|
||||
}
|
||||
|
||||
vmwrite(vmcs::guest::ACTIVITY_STATE, 0)?;
|
||||
vmwrite(vmcs::guest::INTERRUPTIBILITY_STATE, 0)?;
|
||||
|
||||
self.step_next_inst()?;
|
||||
}
|
||||
VmxExitReason::CPUID => {
|
||||
@ -108,6 +126,17 @@ impl IntelVCpu {
|
||||
|
||||
self.step_next_inst()?;
|
||||
}
|
||||
VmxExitReason::EXTERNAL_INTERRUPT => {
|
||||
vmwrite(vmcs::ro::VMEXIT_INTERRUPTION_INFO, 0)?;
|
||||
|
||||
unsafe {
|
||||
asm!("sti");
|
||||
asm!("nop");
|
||||
asm!("cli");
|
||||
}
|
||||
|
||||
self.pic.inject_external_interrupt(&mut self.pending_irq)?;
|
||||
}
|
||||
VmxExitReason::EPT_VIOLATION => {
|
||||
let guest_address = vmread(vmcs::ro::GUEST_PHYSICAL_ADDR_FULL)?;
|
||||
info!("EPT Violation at guest address: {:#x}", guest_address);
|
||||
@ -198,6 +227,11 @@ impl IntelVCpu {
|
||||
self.setup_guest_state()?;
|
||||
self.io_bitmap.setup()?;
|
||||
|
||||
interrupt::subscriber::subscribe(
|
||||
vmm_interrupt_subscriber,
|
||||
self as &mut IntelVCpu as *mut IntelVCpu as *mut core::ffi::c_void,
|
||||
)?;
|
||||
|
||||
self.init_guest_memory(frame_allocator)?;
|
||||
|
||||
common::linux::load_kernel(self)?;
|
||||
@ -690,6 +724,7 @@ impl VCpu for IntelVCpu {
|
||||
ia32e_enabled: false,
|
||||
pic: super::io::PIC::new(),
|
||||
io_bitmap: IOBitmap::new(frame_allocator),
|
||||
pending_irq: 0,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -208,3 +208,14 @@ impl PrimaryExitControls {
|
||||
vmcs::VmcsControl32::PRIMARY_VM_EXIT_CONTROLS.write(u32::from(*self))
|
||||
}
|
||||
}
|
||||
|
||||
#[bitfield]
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct EntryIntrInfo {
|
||||
pub vector: B8,
|
||||
pub typ: B3,
|
||||
pub ec_available: bool,
|
||||
_reserved: B19,
|
||||
pub valid: bool,
|
||||
}
|
||||
|
Reference in New Issue
Block a user