diff --git a/src/serial.rs b/src/serial.rs index cb1a4f6..919ad95 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -1,5 +1,3 @@ -use core::fmt::Write; - use lazy_static::lazy_static; use spin::Mutex; use uart_16550::SerialPort; @@ -25,19 +23,6 @@ pub fn _print(args: ::core::fmt::Arguments) { }); } -#[inline(always)] -pub fn write_byte(byte: u8) { - use x86_64::instructions::interrupts; - - if interrupts::are_enabled() { - interrupts::without_interrupts(|| { - SERIAL1.lock().send(byte); - }); - } else { - SERIAL1.lock().send(byte); - } -} - #[macro_export] macro_rules! serial_print { ($($arg:tt)*) => { diff --git a/src/vga_buffer.rs b/src/vga_buffer.rs index 55d7ff0..4e6d6b7 100644 --- a/src/vga_buffer.rs +++ b/src/vga_buffer.rs @@ -4,7 +4,6 @@ use lazy_static::lazy_static; use spin::Mutex; use volatile::Volatile; -use crate::serial::SERIAL1; lazy_static! { pub static ref WRITER: Mutex = Mutex::new(Writer { @@ -37,15 +36,11 @@ macro_rules! error { #[doc(hidden)] pub fn _print(args: fmt::Arguments) { - use core::fmt::Write; use x86_64::instructions::interrupts; interrupts::without_interrupts(|| { //WRITER.lock().write_fmt(args).unwrap(); - SERIAL1 - .lock() - .write_fmt(args) - .expect("Printing to serial failed"); + crate::serial::_print(args); }); } diff --git a/src/vmm/cpuid.rs b/src/vmm/cpuid.rs index 5e2f5f8..23ec100 100644 --- a/src/vmm/cpuid.rs +++ b/src/vmm/cpuid.rs @@ -125,7 +125,7 @@ pub fn handle_cpuid_exit(vcpu: &mut VCpu) { pae: true, mce: false, cx8: true, - apic: true, + apic: false, _reserved_0: false, sep: true, mtrr: false, diff --git a/src/vmm/io.rs b/src/vmm/io.rs index b980e96..14519ca 100644 --- a/src/vmm/io.rs +++ b/src/vmm/io.rs @@ -1,9 +1,4 @@ -use x86::io::inb; - -use crate::{ - serial, - vmm::{qual::QualIo, vcpu::VCpu}, -}; +use crate::vmm::{qual::QualIo, vcpu::VCpu}; #[derive(Default)] pub struct Serial { @@ -11,6 +6,36 @@ pub struct Serial { pub mcr: u8, } +pub enum InitPhase { + Uninitialized, + Phase1, + Phase2, + Phase3, + Initialized, +} + +pub struct PIC { + pub primary_mask: u8, + pub secondary_mask: u8, + pub primary_phase: InitPhase, + pub secondary_phase: InitPhase, + pub primary_base: u8, + pub secondary_base: u8, +} + +impl PIC { + pub fn new() -> Self { + Self { + primary_mask: 0xFF, + secondary_mask: 0xFF, + primary_phase: InitPhase::Uninitialized, + secondary_phase: InitPhase::Uninitialized, + primary_base: 0, + secondary_base: 0, + } + } +} + pub fn handle_io(vcpu: &mut VCpu, qual: QualIo) { match qual.direction() { 0 => { @@ -26,59 +51,82 @@ pub fn handle_io(vcpu: &mut VCpu, qual: QualIo) { pub fn handle_io_in(vcpu: &mut VCpu, qual: QualIo) { let regs = &mut vcpu.guest_registers; match qual.port() { - 0x0CF8..0x0CFF => { - regs.rax = 0; - } - 0xC000..0xCFFF => {} //ignore - - 0x03F..0x03FF => handle_serial_in(vcpu, qual), - _ => { - panic!("IO in: invalid port: {:#x}", qual.port()); - } + 0x0CF8..=0x0CFF => regs.rax = 0, + 0xC000..=0xCFFF => {} //ignore + 0x20..=0x21 => handle_pic_in(vcpu, qual), + 0xA0..=0xA1 => handle_pic_in(vcpu, qual), + 0x0070..=0x0071 => regs.rax = 0, + _ => regs.rax = 0, } } pub fn handle_io_out(vcpu: &mut VCpu, qual: QualIo) { - let regs = &vcpu.guest_registers; match qual.port() { - 0x0CF8..0x0CFF => {} //ignore - 0xC000..0xCFFF => {} //ignore - 0x03F8..0x03FF => handle_serial_out(vcpu, qual), - _ => { - panic!("IO out: invalid port: {:#x}", qual.port()); - } + 0x0CF8..=0x0CFF => {} //ignore + 0xC000..=0xCFFF => {} //ignore + 0x20..=0x21 => handle_pic_out(vcpu, qual), + 0xA0..=0xA1 => handle_pic_out(vcpu, qual), + 0x0070..=0x0071 => {} //ignore + _ => {} } } -fn handle_serial_in(vcpu: &mut VCpu, qual: QualIo) { +pub fn handle_pic_in(vcpu: &mut VCpu, qual: QualIo) { let regs = &mut vcpu.guest_registers; match qual.port() { - 0x3F8 => regs.rax = unsafe { inb(qual.port()).into() }, - 0x3F9 => regs.rax = vcpu.serial.ier as u64, - 0x3FA => regs.rax = unsafe { inb(qual.port()).into() }, - 0x3FB => regs.rax = 0, - 0x3FC => regs.rax = vcpu.serial.mcr as u64, - 0x3FD => regs.rax = unsafe { inb(qual.port()).into() }, - 0x3FE => regs.rax = unsafe { inb(qual.port()).into() }, - 0x3FF => regs.rax = 0, - _ => { - panic!("Serial in: invalid port: {:#x}", qual.port()); - } + 0x21 => match vcpu.pic.primary_phase { + InitPhase::Uninitialized | InitPhase::Initialized => { + regs.rax = vcpu.pic.primary_mask as u64; + } + _ => {} + }, + 0xA1 => match vcpu.pic.secondary_phase { + InitPhase::Uninitialized | InitPhase::Initialized => { + regs.rax = vcpu.pic.secondary_mask as u64; + } + _ => {} + }, + _ => {} } } -fn handle_serial_out(vcpu: &mut VCpu, qual: QualIo) { +pub fn handle_pic_out(vcpu: &mut VCpu, qual: QualIo) { let regs = &mut vcpu.guest_registers; + let pic = &mut vcpu.pic; + let dx = regs.rax as u8; match qual.port() { - 0x3F8 => serial::write_byte(regs.rax as u8), - 0x3F9 => vcpu.serial.ier = regs.rax as u8, - 0x3FA => {} - 0x3FB => {} - 0x3FC => vcpu.serial.mcr = regs.rax as u8, - 0x3FD => {} - 0x3FF => {} - _ => { - panic!("Serial out: invalid port: {:#x}", qual.port()); - } + 0x20 => match dx { + 0x11 => pic.primary_phase = InitPhase::Phase1, + 0x60..=0x67 => {} + _ => panic!("Primary PIC command: {:#x}", dx), + }, + 0x21 => match pic.primary_phase { + InitPhase::Uninitialized | InitPhase::Initialized => pic.primary_mask = dx, + InitPhase::Phase1 => { + pic.primary_base = dx; + pic.primary_phase = InitPhase::Phase2; + } + InitPhase::Phase2 => { + pic.primary_phase = InitPhase::Phase3; + } + InitPhase::Phase3 => pic.primary_phase = InitPhase::Initialized, + }, + 0xA0 => match dx { + 0x11 => pic.secondary_phase = InitPhase::Phase1, + 0x60..=0x67 => {} + _ => panic!("Secondary PIC command: {:#x}", dx), + }, + 0xA1 => match pic.secondary_phase { + InitPhase::Uninitialized | InitPhase::Initialized => pic.secondary_mask = dx, + InitPhase::Phase1 => { + pic.secondary_base = dx; + pic.secondary_phase = InitPhase::Phase2; + } + InitPhase::Phase2 => { + pic.secondary_phase = InitPhase::Phase3; + } + InitPhase::Phase3 => pic.secondary_phase = InitPhase::Initialized, + }, + _ => {} } } diff --git a/src/vmm/vcpu.rs b/src/vmm/vcpu.rs index d327a4e..ee4da48 100644 --- a/src/vmm/vcpu.rs +++ b/src/vmm/vcpu.rs @@ -1,6 +1,7 @@ use core::{ arch::x86_64::{_xgetbv, _xsetbv}, - u64, + convert::TryInto, + u64, u8, }; use x86::{ @@ -10,20 +11,24 @@ use x86::{ msr::{rdmsr, IA32_EFER, IA32_FS_BASE}, vmx::{vmcs, VmFail}, }; -use x86_64::{registers::control::Cr4Flags, structures::paging::OffsetPageTable, VirtAddr}; +use x86_64::{ + registers::control::Cr4Flags, + structures::paging::{FrameAllocator, OffsetPageTable}, + VirtAddr, +}; use crate::{ info, memory::BootInfoFrameAllocator, vmm::{ cpuid, cr, fpu, - io::{self, Serial}, + io::{self, Serial, PIC}, msr, qual::{QualCr, QualIo}, vmcs::{ DescriptorType, EntryControls, Granularity, PrimaryExitControls, PrimaryProcessorBasedVmExecutionControls, SecondaryProcessorBasedVmExecutionControls, - SegmentRights, VmxExitInfo, VmxExitReason, + SegmentRights, VmxExitReason, }, }, }; @@ -53,6 +58,9 @@ pub struct VCpu { pub xcr0: XCR0, pub host_xcr0: u64, pub serial: Serial, + pub io_bitmap_a: x86_64::structures::paging::PhysFrame, + pub io_bitmap_b: x86_64::structures::paging::PhysFrame, + pub pic: PIC, } const TEMP_STACK_SIZE: usize = 4096; @@ -66,6 +74,10 @@ impl VCpu { let ept = EPT::new(frame_allocator); let eptp = EPTP::new(&ept.root_table); + // Allocate I/O bitmaps (4KB each) + let io_bitmap_a = frame_allocator.allocate_frame().unwrap(); + let io_bitmap_b = frame_allocator.allocate_frame().unwrap(); + VCpu { vmxon, vmcs, @@ -80,6 +92,9 @@ impl VCpu { xcr0: XCR0(3), host_xcr0: 0, serial: Serial::default(), + io_bitmap_a, + io_bitmap_b, + pic: PIC::new(), } } @@ -99,6 +114,7 @@ impl VCpu { self.setup_exit_ctrls().unwrap(); self.setup_host_state().unwrap(); self.setup_guest_state().unwrap(); + self.setup_io_bitmaps(); self.setup_guest_memory(frame_allocator); self.register_msrs(&mapper); } @@ -304,11 +320,12 @@ impl VCpu { primary_exec_ctrl.0 |= (reserved_bits & 0xFFFFFFFF) as u32; primary_exec_ctrl.0 &= (reserved_bits >> 32) as u32; - primary_exec_ctrl.set_hlt(false); + primary_exec_ctrl.set_hlt(true); primary_exec_ctrl.set_activate_secondary_controls(true); primary_exec_ctrl.set_use_tpr_shadow(true); primary_exec_ctrl.set_use_msr_bitmap(false); - primary_exec_ctrl.set_unconditional_io(true); + primary_exec_ctrl.set_unconditional_io(false); + primary_exec_ctrl.set_use_io_bitmap(true); primary_exec_ctrl.write(); @@ -395,6 +412,59 @@ impl VCpu { Ok(()) } + pub fn setup_io_bitmaps(&mut self) { + info!("Setting up I/O bitmaps"); + + let bitmap_a_vaddr = self.io_bitmap_a.start_address().as_u64() + self.phys_mem_offset; + let bitmap_b_vaddr = self.io_bitmap_b.start_address().as_u64() + self.phys_mem_offset; + + unsafe { + core::ptr::write_bytes(bitmap_a_vaddr as *mut u8, u8::MAX, 4096); + core::ptr::write_bytes(bitmap_b_vaddr as *mut u8, u8::MAX, 4096); + } + + let bitmap_a = unsafe { core::slice::from_raw_parts_mut(bitmap_a_vaddr as *mut u8, 4096) }; + let bitmap_b = unsafe { core::slice::from_raw_parts_mut(bitmap_b_vaddr as *mut u8, 4096) }; + + self.set_io_ports(bitmap_a, bitmap_b, 0x02F8..=0x03FF); + self.set_io_ports(bitmap_a, bitmap_b, 0x0040..=0x0047); + + unsafe { + vmwrite( + vmcs::control::IO_BITMAP_A_ADDR_FULL, + self.io_bitmap_a.start_address().as_u64(), + ) + .unwrap(); + vmwrite( + vmcs::control::IO_BITMAP_B_ADDR_FULL, + self.io_bitmap_b.start_address().as_u64(), + ) + .unwrap(); + } + + info!("I/O bitmaps configured - PCI ports 0xC000-0xCFFF will trigger VM exits"); + } + + fn set_io_ports( + &self, + bitmap_a: &mut [u8], + bitmap_b: &mut [u8], + 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; + 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; + bitmap_b[byte_index] &= !(1 << bit_index); + } + } + } + pub fn setup_host_state(&mut self) -> Result<(), VmFail> { info!("Setting up host state"); unsafe { @@ -681,10 +751,11 @@ impl VCpu { } fn vmexit_handler(&mut self) { - let info = VmxExitInfo::read(); + let exit_reason_raw = unsafe { vmread(vmcs::ro::EXIT_REASON).unwrap() as u32 }; - if info.entry_failure() { - let reason = info.0 & 0xFF; + if (exit_reason_raw & (1 << 31)) != 0 { + // VM-entry failure + let reason = exit_reason_raw & 0xFF; match reason { 33 => { info!(" Reason: VM-entry failure due to invalid guest state"); @@ -698,7 +769,9 @@ impl VCpu { _ => {} } } else { - match info.get_reason() { + let basic_reason = (exit_reason_raw & 0xFFFF) as u16; + let exit_reason: VmxExitReason = basic_reason.try_into().unwrap(); + match exit_reason { VmxExitReason::HLT => { info!("HLT instruction executed"); } @@ -734,12 +807,13 @@ impl VCpu { } VmxExitReason::IO_INSTRUCTION => { let qual = unsafe { vmread(vmcs::ro::EXIT_QUALIFICATION).unwrap() }; - let qual = QualIo(qual); - io::handle_io(self, qual); + let qual_io = QualIo(qual); + + io::handle_io(self, qual_io); self.step_next_inst().unwrap(); } _ => { - panic!("VMExit reason: {:?}", info.get_reason()); + panic!("VMExit reason: {:?}", exit_reason); } } }