diff --git a/nel_os_kernel/src/memory/memory.rs b/nel_os_kernel/src/memory/memory.rs index 23fc73b..654a79b 100644 --- a/nel_os_kernel/src/memory/memory.rs +++ b/nel_os_kernel/src/memory/memory.rs @@ -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 = Once::new(); diff --git a/nel_os_kernel/src/vmm/x86_64/intel/controls.rs b/nel_os_kernel/src/vmm/x86_64/intel/controls.rs index 8edfd18..d5806a6 100644 --- a/nel_os_kernel/src/vmm/x86_64/intel/controls.rs +++ b/nel_os_kernel/src/vmm/x86_64/intel/controls.rs @@ -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()?; diff --git a/nel_os_kernel/src/vmm/x86_64/intel/io.rs b/nel_os_kernel/src/vmm/x86_64/intel/io.rs index aab4578..fbb85a7 100644 --- a/nel_os_kernel/src/vmm/x86_64/intel/io.rs +++ b/nel_os_kernel/src/vmm/x86_64/intel/io.rs @@ -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 { + 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() { diff --git a/nel_os_kernel/src/vmm/x86_64/intel/vcpu.rs b/nel_os_kernel/src/vmm/x86_64/intel/vcpu.rs index b081390..d0246c3 100644 --- a/nel_os_kernel/src/vmm/x86_64/intel/vcpu.rs +++ b/nel_os_kernel/src/vmm/x86_64/intel/vcpu.rs @@ -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, }) } diff --git a/nel_os_kernel/src/vmm/x86_64/intel/vmcs/controls.rs b/nel_os_kernel/src/vmm/x86_64/intel/vmcs/controls.rs index 7977384..e83734f 100644 --- a/nel_os_kernel/src/vmm/x86_64/intel/vmcs/controls.rs +++ b/nel_os_kernel/src/vmm/x86_64/intel/vmcs/controls.rs @@ -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, +}