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 59474a3..8edfd18 100644 --- a/nel_os_kernel/src/vmm/x86_64/intel/controls.rs +++ b/nel_os_kernel/src/vmm/x86_64/intel/controls.rs @@ -36,6 +36,8 @@ pub fn setup_exec_controls() -> Result<(), &'static str> { primary_exec_ctrl.set_hlt(true); primary_exec_ctrl.set_activate_secondary_controls(true); primary_exec_ctrl.set_use_msr_bitmap(false); + primary_exec_ctrl.set_unconditional_io(false); + primary_exec_ctrl.set_use_io_bitmap(true); primary_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 fb19e22..87ef4c4 100644 --- a/nel_os_kernel/src/vmm/x86_64/intel/io.rs +++ b/nel_os_kernel/src/vmm/x86_64/intel/io.rs @@ -1,5 +1,11 @@ +use x86::vmx::vmcs; +use x86_64::structures::paging::{FrameAllocator, PhysFrame, Size4KiB}; + use super::qual::QualIo; -use crate::{info, vmm::x86_64::intel::register::GuestRegisters}; +use crate::{ + info, + vmm::x86_64::intel::{register::GuestRegisters, vmwrite}, +}; #[derive(Default)] pub struct Serial { @@ -17,6 +23,11 @@ pub enum InitPhase { Initialized, } +enum ReadSel { + IRR, + ISR, +} + pub struct PIC { pub primary_mask: u8, pub secondary_mask: u8, @@ -24,6 +35,12 @@ pub struct PIC { pub secondary_phase: InitPhase, pub primary_base: u8, pub secondary_base: u8, + pub primary_irr: u8, + pub primary_isr: u8, + pub secondary_irr: u8, + pub secondary_isr: u8, + pub primary_read_sel: ReadSel, + pub secondary_read_sel: ReadSel, } impl PIC { @@ -35,6 +52,12 @@ impl PIC { secondary_phase: InitPhase::Uninitialized, primary_base: 0, secondary_base: 0, + primary_irr: 0, + primary_isr: 0, + secondary_irr: 0, + secondary_isr: 0, + primary_read_sel: ReadSel::IRR, + secondary_read_sel: ReadSel::IRR, } } @@ -74,6 +97,20 @@ impl PIC { pub fn handle_pic_in(&self, regs: &mut GuestRegisters, qual: QualIo) { match qual.port() { + 0x20 => { + let v = match self.primary_read_sel { + ReadSel::IRR => self.primary_irr, + ReadSel::ISR => self.primary_isr, + }; + regs.rax = v as u64; + } + 0xA0 => { + let v = match self.secondary_read_sel { + ReadSel::IRR => self.secondary_irr, + ReadSel::ISR => self.secondary_isr, + }; + regs.rax = v as u64; + } 0x21 => match self.primary_phase { InitPhase::Uninitialized | InitPhase::Initialized => { regs.rax = self.primary_mask as u64; @@ -96,7 +133,15 @@ impl PIC { match qual.port() { 0x20 => match dx { 0x11 => pic.primary_phase = InitPhase::Phase1, - 0x60..=0x67 => {} + 0x0A => pic.primary_read_sel = ReadSel::ISR, + 0x0B => pic.primary_read_sel = ReadSel::IRR, + 0x20 => { + pic.primary_isr = 0; + } + 0x60..=0x67 => { + let irq = dx & 0x7; + pic.primary_isr &= !(1 << irq); + } _ => panic!("Primary PIC command: {:#x}", dx), }, 0x21 => match pic.primary_phase { @@ -115,7 +160,15 @@ impl PIC { }, 0xA0 => match dx { 0x11 => pic.secondary_phase = InitPhase::Phase1, - 0x60..=0x67 => {} + 0x0A => pic.secondary_read_sel = ReadSel::ISR, + 0x0B => pic.secondary_read_sel = ReadSel::IRR, + 0x20 => { + pic.secondary_isr = 0; + } + 0x60..=0x67 => { + let irq = dx & 0x7; + pic.secondary_isr &= !(1 << irq); + } _ => panic!("Secondary PIC command: {:#x}", dx), }, 0xA1 => match pic.secondary_phase { @@ -136,3 +189,68 @@ impl PIC { } } } + +pub struct IOBitmap { + pub bitmap_a: PhysFrame, + pub bitmap_b: PhysFrame, +} + +impl IOBitmap { + pub fn new(frame_allocator: &mut impl FrameAllocator) -> Self { + let bitmap_a = frame_allocator + .allocate_frame() + .expect("Failed to allocate I/O bitmap A"); + let bitmap_b = frame_allocator + .allocate_frame() + .expect("Failed to allocate I/O bitmap B"); + + Self { bitmap_a, bitmap_b } + } + + pub fn setup(&mut self) -> Result<(), &'static str> { + let bitmap_a_addr = self.bitmap_a.start_address().as_u64() as usize; + let bitmap_b_addr = self.bitmap_b.start_address().as_u64() as usize; + + unsafe { + core::ptr::write_bytes(bitmap_a_addr as *mut u8, u8::MAX, 4096); + core::ptr::write_bytes(bitmap_b_addr as *mut u8, u8::MAX, 4096); + } + + self.set_io_ports(0x02F8..=0x03FF); + self.set_io_ports(0x0040..=0x0047); + + vmwrite(vmcs::control::IO_BITMAP_A_ADDR_FULL, bitmap_a_addr as u64)?; + vmwrite(vmcs::control::IO_BITMAP_B_ADDR_FULL, bitmap_b_addr as u64)?; + + Ok(()) + } + + pub fn set_io_ports(&mut self, ports: core::ops::RangeInclusive) { + for port in ports { + if port <= 0x7FFF { + let byte_index = port as usize / 8; + let bit_index = port as usize % 8; + + self.get_bitmap_a()[byte_index] &= !(1 << bit_index); + } else { + let adjusted_port = port - 0x8000; + let byte_index = adjusted_port as usize / 8; + let bit_index = adjusted_port as usize % 8; + + self.get_bitmap_b()[byte_index] &= !(1 << bit_index); + } + } + } + + fn get_bitmap_a(&self) -> &mut [u8] { + unsafe { + core::slice::from_raw_parts_mut(self.bitmap_a.start_address().as_u64() as *mut u8, 4096) + } + } + + fn get_bitmap_b(&self) -> &mut [u8] { + unsafe { + core::slice::from_raw_parts_mut(self.bitmap_b.start_address().as_u64() as *mut u8, 4096) + } + } +} 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 fcd12ba..b081390 100644 --- a/nel_os_kernel/src/vmm/x86_64/intel/vcpu.rs +++ b/nel_os_kernel/src/vmm/x86_64/intel/vcpu.rs @@ -12,8 +12,9 @@ use crate::{ common::{self, read_msr}, intel::{ auditor, controls, cpuid, ept, + io::IOBitmap, msr::{self, ShadowMsr}, - qual::QualCr, + qual::{QualCr, QualIo}, register::GuestRegisters, vmcs::{ self, @@ -44,6 +45,8 @@ pub struct IntelVCpu { pub host_msr: ShadowMsr, pub guest_msr: ShadowMsr, pub ia32e_enabled: bool, + pic: super::io::PIC, + io_bitmap: IOBitmap, } impl IntelVCpu { @@ -97,6 +100,14 @@ impl IntelVCpu { self.step_next_inst()?; } + VmxExitReason::IO_INSTRUCTION => { + let qual = vmread(vmcs::ro::EXIT_QUALIFICATION)?; + let qual_io = QualIo::from(qual); + + self.pic.handle_io(&mut self.guest_registers, qual_io); + + self.step_next_inst()?; + } VmxExitReason::EPT_VIOLATION => { let guest_address = vmread(vmcs::ro::GUEST_PHYSICAL_ADDR_FULL)?; info!("EPT Violation at guest address: {:#x}", guest_address); @@ -185,6 +196,7 @@ impl IntelVCpu { controls::setup_exit_controls()?; Self::setup_host_state()?; self.setup_guest_state()?; + self.io_bitmap.setup()?; self.init_guest_memory(frame_allocator)?; @@ -676,6 +688,8 @@ impl VCpu for IntelVCpu { host_msr: ShadowMsr::new(), guest_msr: ShadowMsr::new(), ia32e_enabled: false, + pic: super::io::PIC::new(), + io_bitmap: IOBitmap::new(frame_allocator), }) }