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,
|
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();
|
||||||
|
|
||||||
|
@ -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()?;
|
||||||
|
|
||||||
|
@ -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() {
|
||||||
|
@ -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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user