diff --git a/Cargo.lock b/Cargo.lock index a089812..9aed4c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,18 +25,18 @@ checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" [[package]] name = "bitfield" -version = "0.19.0" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "786e53b0c071573a28956cec19a92653e42de34c683e2f6e86c197a349fba318" +checksum = "db1bcd90f88eabbf0cadbfb87a45bceeaebcd3b4bc9e43da379cd2ef0162590d" dependencies = [ "bitfield-macros", ] [[package]] name = "bitfield-macros" -version = "0.19.0" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07805405d3f1f3a55aab895718b488821d40458f9188059909091ae0935c344a" +checksum = "3787a07661997bfc05dd3431e379c0188573f78857080cf682e1393ab8e4d64c" dependencies = [ "proc-macro2", "quote", @@ -51,9 +51,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "bootloader" @@ -260,7 +260,7 @@ version = "11.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6df7ab838ed27997ba19a4664507e6f82b41fe6e20be42929332156e5e85146" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", ] [[package]] @@ -298,9 +298,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.100" +version = "2.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", "quote", @@ -376,7 +376,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c101112411baafbb4bf8d33e4c4a80ab5b02d74d2612331c61e8192fc9710491" dependencies = [ "bit_field", - "bitflags 2.9.0", + "bitflags 2.9.1", "rustversion", "volatile 0.4.6", ] @@ -388,7 +388,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f042214de98141e9c8706e8192b73f56494087cc55ebec28ce10f26c5c364ae" dependencies = [ "bit_field", - "bitflags 2.9.0", + "bitflags 2.9.1", "rustversion", "volatile 0.4.6", ] diff --git a/bzImage b/bzImage index 4f4e08c..71e422e 100644 Binary files a/bzImage and b/bzImage differ diff --git a/src/main.rs b/src/main.rs index a87a375..f30fc6b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -57,7 +57,7 @@ fn kernel_main(boot_info: &'static BootInfo) -> ! { } let mut vcpu = VCpu::new(phys_mem_offset.as_u64(), &mut frame_allocator); - vcpu.activate(&mut frame_allocator); + vcpu.activate(&mut frame_allocator, &mapper); #[cfg(not(test))] vcpu.vm_loop(); diff --git a/src/vmm/cpuid.rs b/src/vmm/cpuid.rs index 85c493f..063a3b4 100644 --- a/src/vmm/cpuid.rs +++ b/src/vmm/cpuid.rs @@ -1,17 +1,14 @@ use raw_cpuid::cpuid; -use crate::info; - use super::{vcpu::VCpu, vmcs::VmxLeaf}; pub fn handle_cpuid_exit(vcpu: &mut VCpu) { let regs = &mut vcpu.guest_registers; - let vendor: &[u8; 12] = b"miimiimiimii"; + let vendor: &[u8; 12] = b"miHypervisor"; let vendor = unsafe { core::mem::transmute::<&[u8; 12], &[u32; 3]>(vendor) }; match VmxLeaf::from(regs.rax) { VmxLeaf::EXTENDED_FEATURE => match regs.rcx { 0 => { - info!("CPUID extended feature"); let mut ebx = ExtFeatureEbx0::default(); ebx.smep = true; ebx.invpcid = false; @@ -32,7 +29,6 @@ pub fn handle_cpuid_exit(vcpu: &mut VCpu) { } }, VmxLeaf::EXTENDED_PROCESSOR_SIGNATURE => { - info!("CPUID extended processor signature"); let signature = cpuid!(0x80000001, 0); regs.rax = 0x00000000; regs.rbx = 0x00000000; @@ -40,21 +36,18 @@ pub fn handle_cpuid_exit(vcpu: &mut VCpu) { regs.rdx = signature.edx as u64; } VmxLeaf::EXTENDED_FUNCTION => { - info!("CPUID extended function"); regs.rax = 0x80000000 + 1; regs.rbx = 0x00000000; regs.rcx = 0x00000000; regs.rdx = 0x00000000; } VmxLeaf::MAXIMUM_INPUT => { - info!("CPUID max input"); regs.rax = 0x20; regs.rbx = vendor[0] as u64; - regs.rcx = vendor[1] as u64; - regs.rdx = vendor[2] as u64; + regs.rcx = vendor[2] as u64; + regs.rdx = vendor[1] as u64; } VmxLeaf::VERSION_AND_FEATURE_INFO => { - info!("CPUID version and feature info"); let ecx = FeatureInfoEcx { sse3: false, pclmulqdq: false, @@ -130,7 +123,6 @@ pub fn handle_cpuid_exit(vcpu: &mut VCpu) { regs.rdx = edx.to_u32() as u64; } _ => { - info!("Unhandled CPUID leaf: {:#x}", regs.rax); invalid(vcpu); } }; diff --git a/src/vmm/cr.rs b/src/vmm/cr.rs new file mode 100644 index 0000000..46468dd --- /dev/null +++ b/src/vmm/cr.rs @@ -0,0 +1,160 @@ +use core::convert::TryFrom; + +use x86::{ + bits64::vmx::{vmread, vmwrite}, + msr::rdmsr, + vmx::vmcs, +}; + +use crate::info; + +use super::{ + qual::{AccessType, QualCr, Register}, + vcpu::VCpu, +}; + +pub fn handle_cr_access(vcpu: &mut VCpu, qual: &QualCr) { + match AccessType::try_from(qual.access_type().unwrap()).unwrap() { + AccessType::MovTo => match qual.index() { + 0 | 4 => { + passthrough_write(vcpu, qual); + update_ia32e(vcpu); + } + _ => panic!("Unsupported CR index: {}", qual.index()), + }, + AccessType::MovFrom => passthrough_read(vcpu, qual), + _ => { + panic!("Unsupported CR access type: {:?}", qual.access_type()); + } + } +} + +fn passthrough_read(vcpu: &mut VCpu, qual: &QualCr) { + let value = match qual.index() { + 3 => unsafe { vmread(x86::vmx::vmcs::guest::CR3).unwrap() }, + _ => panic!("Unsupported CR index: {}", qual.index()), + }; + + set_value(vcpu, qual, value); +} + +fn passthrough_write(vcpu: &mut VCpu, qual: &QualCr) { + let value = get_value(vcpu, qual); + match qual.index() { + 0 => unsafe { + vmwrite(vmcs::guest::CR0, adjust_cr0(value)).unwrap(); + vmwrite(vmcs::control::CR0_READ_SHADOW, value).unwrap(); + }, + 4 => unsafe { + vmwrite(vmcs::guest::CR4, adjust_cr4(value)).unwrap(); + vmwrite(vmcs::control::CR4_READ_SHADOW, value).unwrap(); + }, + _ => { + panic!("Unsupported CR index: {}", qual.index()); + } + } +} + +pub fn update_ia32e(vcpu: &mut VCpu) { + let cr0 = unsafe { vmread(x86::vmx::vmcs::guest::CR0).unwrap() }; + let cr4 = unsafe { vmread(x86::vmx::vmcs::guest::CR4).unwrap() }; + let ia32e_enabled = (cr0 & 1 << 31) != 0 && (cr4 & 1 << 5) != 0; + + vcpu.ia32e_enabled = ia32e_enabled; + + let mut entry_ctrl = super::vmcs::EntryControls::read(); + entry_ctrl.set_ia32e_mode_guest(ia32e_enabled); + entry_ctrl.write(); + + let mut efer = unsafe { vmread(x86::vmx::vmcs::guest::IA32_EFER_FULL).unwrap() }; + + let lma = (vcpu.ia32e_enabled as u64) << 10; + if lma != 0 { + efer |= lma; + } else { + efer &= !lma; + } + + let lme = if cr0 & (1 << 31) != 0 { + efer & (1 << 10) + } else { + efer & !(1 << 8) + }; + if lme != 0 { + efer |= lme; + } else { + efer &= lme; + } + + unsafe { vmwrite(x86::vmx::vmcs::guest::IA32_EFER_FULL, efer).unwrap() }; +} + +pub fn adjust_cr0(value: u64) -> u64 { + let mut result = value; + + let cr0_fixed0 = unsafe { rdmsr(x86::msr::IA32_VMX_CR0_FIXED0) }; + let cr0_fixed1 = unsafe { rdmsr(x86::msr::IA32_VMX_CR0_FIXED1) }; + + result |= cr0_fixed0; + result &= cr0_fixed1; + + result +} + +pub fn adjust_cr4(value: u64) -> u64 { + let mut result = value; + + let cr4_fixed0 = unsafe { rdmsr(x86::msr::IA32_VMX_CR4_FIXED0) }; + let cr4_fixed1 = unsafe { rdmsr(x86::msr::IA32_VMX_CR4_FIXED1) }; + + result |= cr4_fixed0; + result &= cr4_fixed1; + + result +} + +fn set_value(vcpu: &mut VCpu, qual: &QualCr, value: u64) { + let guest_regs = &mut vcpu.guest_registers; + + match qual.register().unwrap() { + Register::Rax => guest_regs.rax = value, + Register::Rcx => guest_regs.rcx = value, + Register::Rdx => guest_regs.rdx = value, + Register::Rbx => guest_regs.rbx = value, + Register::Rbp => guest_regs.rbp = value, + Register::Rsi => guest_regs.rsi = value, + Register::Rdi => guest_regs.rdi = value, + Register::R8 => guest_regs.r8 = value, + Register::R9 => guest_regs.r9 = value, + Register::R10 => guest_regs.r10 = value, + Register::R11 => guest_regs.r11 = value, + Register::R12 => guest_regs.r12 = value, + Register::R13 => guest_regs.r13 = value, + Register::R14 => guest_regs.r14 = value, + Register::R15 => guest_regs.r15 = value, + Register::Rsp => unsafe { vmwrite(x86::vmx::vmcs::guest::RSP, value).unwrap() }, + } +} + +fn get_value(vcpu: &mut VCpu, qual: &QualCr) -> u64 { + let guest_regs = &mut vcpu.guest_registers; + + match qual.register().unwrap() { + Register::Rax => guest_regs.rax, + Register::Rcx => guest_regs.rcx, + Register::Rdx => guest_regs.rdx, + Register::Rbx => guest_regs.rbx, + Register::Rbp => guest_regs.rbp, + Register::Rsi => guest_regs.rsi, + Register::Rdi => guest_regs.rdi, + Register::R8 => guest_regs.r8, + Register::R9 => guest_regs.r9, + Register::R10 => guest_regs.r10, + Register::R11 => guest_regs.r11, + Register::R12 => guest_regs.r12, + Register::R13 => guest_regs.r13, + Register::R14 => guest_regs.r14, + Register::R15 => guest_regs.r15, + Register::Rsp => unsafe { vmread(x86::vmx::vmcs::guest::RSP).unwrap() }, + } +} diff --git a/src/vmm/mod.rs b/src/vmm/mod.rs index 01ba660..b9997a5 100644 --- a/src/vmm/mod.rs +++ b/src/vmm/mod.rs @@ -1,9 +1,11 @@ pub mod asm; pub mod cpuid; +pub mod cr; pub mod ept; pub mod error; pub mod linux; pub mod msr; +pub mod qual; pub mod register; pub mod support; pub mod vcpu; diff --git a/src/vmm/msr.rs b/src/vmm/msr.rs index e266d59..686ba01 100644 --- a/src/vmm/msr.rs +++ b/src/vmm/msr.rs @@ -44,7 +44,7 @@ pub enum MsrError { impl ShadowMsr { pub fn new() -> Self { - let ents = vec![SavedMsr::default(); MAX_NUM_ENTS]; + let ents = vec![]; ShadowMsr { ents } } @@ -108,7 +108,7 @@ impl ShadowMsr { .set(msr_kind, Self::concat(regs.rdx, regs.rax)) .unwrap(); } else { - panic!("MSR not found"); + panic!("MSR not found: {:#x}", msr_kind); } } diff --git a/src/vmm/qual.rs b/src/vmm/qual.rs new file mode 100644 index 0000000..11f9bbe --- /dev/null +++ b/src/vmm/qual.rs @@ -0,0 +1,127 @@ +use bitfield::bitfield; +use core::convert::TryFrom; +use core::fmt::Debug; + +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum AccessType { + MovTo = 0, + MovFrom = 1, + Clts = 2, + Lmsw = 3, +} + +impl TryFrom for AccessType { + type Error = &'static str; + fn try_from(value: u8) -> Result { + match value { + 0 => Ok(AccessType::MovTo), + 1 => Ok(AccessType::MovFrom), + 2 => Ok(AccessType::Clts), + 3 => Ok(AccessType::Lmsw), + _ => Err("Invalid AccessType value"), + } + } +} + +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum LmswOperandType { + Reg = 0, + Mem = 1, +} + +impl TryFrom for LmswOperandType { + type Error = &'static str; + fn try_from(value: u8) -> Result { + match value { + 0 => Ok(LmswOperandType::Reg), + 1 => Ok(LmswOperandType::Mem), + _ => Err("Invalid LmswOperandType value"), + } + } +} + +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Register { + Rax = 0, + Rcx = 1, + Rdx = 2, + Rbx = 3, + Rsp = 4, + Rbp = 5, + Rsi = 6, + Rdi = 7, + R8 = 8, + R9 = 9, + R10 = 10, + R11 = 11, + R12 = 12, + R13 = 13, + R14 = 14, + R15 = 15, +} + +impl TryFrom for Register { + type Error = &'static str; + fn try_from(value: u8) -> Result { + match value { + 0 => Ok(Register::Rax), + 1 => Ok(Register::Rcx), + 2 => Ok(Register::Rdx), + 3 => Ok(Register::Rbx), + 4 => Ok(Register::Rsp), + 5 => Ok(Register::Rbp), + 6 => Ok(Register::Rsi), + 7 => Ok(Register::Rdi), + 8 => Ok(Register::R8), + 9 => Ok(Register::R9), + 10 => Ok(Register::R10), + 11 => Ok(Register::R11), + 12 => Ok(Register::R12), + 13 => Ok(Register::R13), + 14 => Ok(Register::R14), + 15 => Ok(Register::R15), + _ => Err("Invalid Register value"), + } + } +} + +bitfield! { + #[derive(Clone, Copy)] + pub struct QualCr(u64); + impl Debug; + + pub u8, index, set_index: 3, 0; + u8, access_type_raw, set_access_type_raw: 5, 4; + u8, lmsw_operand_type_raw, set_lmsw_operand_type_raw: 6, 6; + u8, register_raw, set_register_raw: 11, 8; + u16, lmsw_source, set_lmsw_source: 31, 16; +} + +impl QualCr { + pub fn access_type(&self) -> Result { + AccessType::try_from(self.access_type_raw()) + } + + pub fn set_access_type(&mut self, val: AccessType) { + self.set_access_type_raw(val as u8); + } + + pub fn lmsw_operand_type(&self) -> Result { + LmswOperandType::try_from(self.lmsw_operand_type_raw()) + } + + pub fn set_lmsw_operand_type(&mut self, val: LmswOperandType) { + self.set_lmsw_operand_type_raw(val as u8); + } + + pub fn register(&self) -> Result { + Register::try_from(self.register_raw()) + } + + pub fn set_register(&mut self, val: Register) { + self.set_register_raw(val as u8); + } +} diff --git a/src/vmm/vcpu.rs b/src/vmm/vcpu.rs index a9c1279..f33c07d 100644 --- a/src/vmm/vcpu.rs +++ b/src/vmm/vcpu.rs @@ -1,4 +1,5 @@ -use rand::{Rng, SeedableRng}; +use core::u64; + use x86::{ bits64::vmx::{vmread, vmwrite}, controlregs::{cr0, cr3, cr4, Cr0}, @@ -12,7 +13,8 @@ use crate::{ info, memory::BootInfoFrameAllocator, vmm::{ - cpuid, msr, + cpuid, cr, msr, + qual::QualCr, vmcs::{ DescriptorType, EntryControls, Granularity, PrimaryExitControls, PrimaryProcessorBasedVmExecutionControls, SecondaryProcessorBasedVmExecutionControls, @@ -41,6 +43,7 @@ pub struct VCpu { pub eptp: EPTP, pub host_msr: ShadowMsr, pub guest_msr: ShadowMsr, + pub ia32e_enabled: bool, } const TEMP_STACK_SIZE: usize = 4096; @@ -64,10 +67,15 @@ impl VCpu { eptp, host_msr: ShadowMsr::new(), guest_msr: ShadowMsr::new(), + ia32e_enabled: false, } } - pub fn activate(&mut self, frame_allocator: &mut BootInfoFrameAllocator) { + pub fn activate( + &mut self, + frame_allocator: &mut BootInfoFrameAllocator, + mapper: &OffsetPageTable<'static>, + ) { self.vmxon.activate_vmxon().unwrap(); let revision_id = unsafe { rdmsr(x86::msr::IA32_VMX_BASIC) } as u32; @@ -80,6 +88,7 @@ impl VCpu { self.setup_host_state().unwrap(); self.setup_guest_state().unwrap(); self.setup_guest_memory(frame_allocator); + self.register_msrs(&mapper); } pub fn load_kernel(&mut self, kernel: &[u8]) { @@ -144,33 +153,6 @@ impl VCpu { } } - pub fn test_guest_memory(&mut self) { - for x in 1..=1024 { - let mut gpa = 0; - let start = 100 * 1024 * (x - 1); - - let mut random_data = [0u8; 100 * 1024]; - let mut rng = rand::rngs::SmallRng::from_seed([0u8; 16]); - for byte in random_data.iter_mut() { - *byte = rng.gen(); - } - - self.load_image(&random_data, start); - - while gpa < 100 * 1024 { - let value = self.ept.get((gpa + start) as u64).unwrap(); - if value != random_data[gpa] { - panic!("Guest memory test failed at {:#x}", gpa); - } - gpa += 1; - } - } - - let start = 100 * 1024 * 0; - let end = 100 * 1024 * 1024; - self.ept.set_range(start as u64, end as u64, 0).unwrap(); - } - pub fn setup_guest_memory(&mut self, frame_allocator: &mut BootInfoFrameAllocator) { let mut pages = 100; let mut gpa = 0; @@ -188,15 +170,13 @@ impl VCpu { } info!("Guest memory setup complete"); - self.test_guest_memory(); - self.load_kernel(linux::BZIMAGE); let eptp = EPTP::new(&self.ept.root_table); unsafe { vmwrite(vmcs::control::EPTP_FULL, eptp.0).unwrap() }; } - pub fn register_msrs(&mut self, mapper: OffsetPageTable<'static>) { + pub fn register_msrs(&mut self, mapper: &OffsetPageTable<'static>) { unsafe { // tsc_aux, star, lstar, cstar, fmask, kernel_gs_base. self.host_msr @@ -306,7 +286,7 @@ 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); @@ -330,6 +310,11 @@ impl VCpu { secondary_exec_ctrl.write(); + unsafe { + vmwrite(vmcs::control::CR0_GUEST_HOST_MASK, u64::MAX).unwrap(); + vmwrite(vmcs::control::CR4_GUEST_HOST_MASK, u64::MAX).unwrap(); + } + Ok(()) } @@ -558,7 +543,11 @@ impl VCpu { vmwrite(vmcs::guest::RIP, linux::LAYOUT_KERNEL_BASE as u64)?; self.guest_registers.rsi = linux::LAYOUT_BOOTPARAM as u64; - info!("Guest RIP: {:#x}", linux::LAYOUT_KERNEL_BASE as u64); + + let cr0 = vmread(vmcs::guest::CR0)?; + let cr4 = vmread(vmcs::guest::CR4)?; + vmwrite(vmcs::control::CR0_READ_SHADOW, cr0)?; + vmwrite(vmcs::control::CR4_READ_SHADOW, cr4)?; } Ok(()) @@ -643,6 +632,7 @@ impl VCpu { info!("HLT instruction executed"); } VmxExitReason::CPUID => { + info!("CPUID instruction executed"); cpuid::handle_cpuid_exit(self); self.step_next_inst().unwrap(); } @@ -656,6 +646,13 @@ impl VCpu { msr::ShadowMsr::handle_wrmsr_vmexit(self); self.step_next_inst().unwrap(); } + VmxExitReason::CONTROL_REGISTER_ACCESSES => { + info!("Control register access"); + let qual = unsafe { vmread(vmcs::ro::EXIT_QUALIFICATION).unwrap() }; + let qual = QualCr(qual); + cr::handle_cr_access(self, &qual); + self.step_next_inst().unwrap(); + } _ => { panic!("VMExit reason: {:?}", info.get_reason()); }