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, PhysAddr,
}; };
use crate::{ use crate::constant::{BITS_PER_ENTRY, PAGE_SIZE};
constant::{BITS_PER_ENTRY, PAGE_SIZE},
info,
};
pub static MAX_MEMORY: Once<usize> = Once::new(); 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; raw_pin_exec_ctrl &= (reserved_bits >> 32) as u32;
let mut pin_exec_ctrl = vmcs::controls::PinBasedVmExecutionControls::from(raw_pin_exec_ctrl); 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()?; 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 x86_64::structures::paging::{FrameAllocator, PhysFrame, Size4KiB};
use super::qual::QualIo; use super::qual::QualIo;
use crate::{ use crate::{
info, 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)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InitPhase { pub enum InitPhase {
Uninitialized, 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() { match qual.port() {
0x0CF8..=0x0CFF => regs.rax = 0, 0x0CF8..=0x0CFF => regs.rax = 0,
0xC000..=0xCFFF => {} //ignore 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() { match qual.port() {
0x0CF8..=0x0CFF => {} //ignore 0x0CF8..=0x0CFF => {} //ignore
0xC000..=0xCFFF => {} //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() { match qual.port() {
0x20 => { 0x20 => {
let v = match self.primary_read_sel { 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 pic = self;
let dx = regs.rax as u8; let dx = regs.rax as u8;
match qual.port() { match qual.port() {

View File

@ -1,3 +1,5 @@
use core::arch::asm;
use raw_cpuid::cpuid; use raw_cpuid::cpuid;
use x86_64::{ use x86_64::{
registers::control::Cr4Flags, registers::control::Cr4Flags,
@ -6,13 +8,13 @@ use x86_64::{
}; };
use crate::{ use crate::{
info, info, interrupt,
vmm::{ vmm::{
x86_64::{ x86_64::{
common::{self, read_msr}, common::{self, read_msr},
intel::{ intel::{
auditor, controls, cpuid, ept, auditor, controls, cpuid, ept,
io::IOBitmap, io::{vmm_interrupt_subscriber, IOBitmap},
msr::{self, ShadowMsr}, msr::{self, ShadowMsr},
qual::{QualCr, QualIo}, qual::{QualCr, QualIo},
register::GuestRegisters, register::GuestRegisters,
@ -47,6 +49,7 @@ pub struct IntelVCpu {
pub ia32e_enabled: bool, pub ia32e_enabled: bool,
pic: super::io::PIC, pic: super::io::PIC,
io_bitmap: IOBitmap, io_bitmap: IOBitmap,
pub pending_irq: u16,
} }
impl IntelVCpu { impl IntelVCpu {
@ -77,7 +80,22 @@ impl IntelVCpu {
match exit_reason { match exit_reason {
VmxExitReason::HLT => { 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()?; self.step_next_inst()?;
} }
VmxExitReason::CPUID => { VmxExitReason::CPUID => {
@ -108,6 +126,17 @@ impl IntelVCpu {
self.step_next_inst()?; 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 => { VmxExitReason::EPT_VIOLATION => {
let guest_address = vmread(vmcs::ro::GUEST_PHYSICAL_ADDR_FULL)?; let guest_address = vmread(vmcs::ro::GUEST_PHYSICAL_ADDR_FULL)?;
info!("EPT Violation at guest address: {:#x}", guest_address); info!("EPT Violation at guest address: {:#x}", guest_address);
@ -198,6 +227,11 @@ impl IntelVCpu {
self.setup_guest_state()?; self.setup_guest_state()?;
self.io_bitmap.setup()?; 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)?; self.init_guest_memory(frame_allocator)?;
common::linux::load_kernel(self)?; common::linux::load_kernel(self)?;
@ -690,6 +724,7 @@ impl VCpu for IntelVCpu {
ia32e_enabled: false, ia32e_enabled: false,
pic: super::io::PIC::new(), pic: super::io::PIC::new(),
io_bitmap: IOBitmap::new(frame_allocator), 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)) 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,
}