add external interrupt
All checks were successful
Check / Build ISO (nightly-2025-04-27) (push) Successful in 46s

This commit is contained in:
Masato Imai
2025-08-22 13:32:40 +00:00
parent b215f0010f
commit df56e251e4
5 changed files with 146 additions and 14 deletions

View File

@ -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();

View File

@ -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()?;

View File

@ -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() {

View File

@ -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,
})
}

View File

@ -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,
}