diff --git a/Cargo.lock b/Cargo.lock index 09e7d7f..a089812 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -107,6 +107,7 @@ dependencies = [ "pc-keyboard", "pic8259", "rand", + "raw-cpuid 11.5.0", "spin 0.5.2", "uart_16550", "volatile 0.2.7", @@ -253,6 +254,15 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "raw-cpuid" +version = "11.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6df7ab838ed27997ba19a4664507e6f82b41fe6e20be42929332156e5e85146" +dependencies = [ + "bitflags 2.9.0", +] + [[package]] name = "rustversion" version = "1.0.20" @@ -356,7 +366,7 @@ checksum = "2781db97787217ad2a2845c396a5efe286f87467a5810836db6d74926e94a385" dependencies = [ "bit_field", "bitflags 1.3.2", - "raw-cpuid", + "raw-cpuid 10.7.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 1f1f9eb..eeb02d4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ x86 = "0.52.0" bitfield = "0.19.0" numeric-enum-macro = "0.2.0" rand = { version = "0.6.5", default-features = false } +raw-cpuid = "11.5.0" [dependencies.lazy_static] version = "1.0" diff --git a/bzImage b/bzImage index 8c06e1c..4f4e08c 100644 Binary files a/bzImage and b/bzImage differ diff --git a/src/vmm/cpuid.rs b/src/vmm/cpuid.rs new file mode 100644 index 0000000..138a15f --- /dev/null +++ b/src/vmm/cpuid.rs @@ -0,0 +1,257 @@ +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"NelogikaNelo"; + let vendor = unsafe { core::mem::transmute::<&[u8; 12], &[u32; 3]>(vendor) }; + match VmxLeaf::from(regs.rax) { + 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; + } + VmxLeaf::VERSION_AND_FEATURE_INFO => { + info!("CPUID version and feature info"); + let ecx = FeatureInfoEcx { + sse3: false, + pclmulqdq: false, + dtes64: false, + monitor: false, + ds_cpl: false, + vmx: false, + smx: false, + eist: false, + tm2: false, + ssse3: false, + cnxt_id: false, + sdbg: false, + fma: false, + cmpxchg16b: false, + xtpr: false, + pdcm: false, + _reserved_0: false, + pcid: true, + dca: false, + sse4_1: false, + sse4_2: false, + x2apic: false, + movbe: false, + popcnt: false, + tsc_deadline: false, + aesni: false, + xsave: false, + osxsave: false, + avx: false, + f16c: false, + rdrand: false, + hypervisor: false, + }; + let edx = FeatureInfoEdx { + fpu: true, + vme: true, + de: true, + pse: true, + tsc: false, + msr: true, + pae: true, + mce: false, + cx8: true, + apic: false, + _reserved_0: false, + sep: true, + mtrr: false, + pge: true, + mca: false, + cmov: true, + pat: false, + pse36: true, + psn: false, + clfsh: false, + _reserved_1: false, + ds: false, + acpi: true, + mmx: false, + fxsr: true, + sse: false, + sse2: false, + ss: false, + htt: false, + tm: false, + _reserved_2: false, + pbe: false, + }; + let cpuid = cpuid!(0x1, 0); + regs.rax = cpuid.eax as u64; + regs.rbx = cpuid.ebx as u64; + regs.rcx = ecx.to_u32() as u64; + regs.rdx = edx.to_u32() as u64; + } + _ => { + info!("Unhandled CPUID leaf: {:#x}", regs.rax); + invalid(vcpu); + } + }; +} + +fn invalid(vcpu: &mut VCpu) { + let regs = &mut vcpu.guest_registers; + + regs.rax = 0; + regs.rbx = 0; + regs.rcx = 0; + regs.rdx = 0; +} + +#[derive(Default)] +#[repr(C, packed)] +pub struct FeatureInfoEcx { + pub sse3: bool, + pub pclmulqdq: bool, + pub dtes64: bool, + pub monitor: bool, + pub ds_cpl: bool, + pub vmx: bool, + pub smx: bool, + pub eist: bool, + pub tm2: bool, + pub ssse3: bool, + pub cnxt_id: bool, + pub sdbg: bool, + pub fma: bool, + pub cmpxchg16b: bool, + pub xtpr: bool, + pub pdcm: bool, + pub _reserved_0: bool, + pub pcid: bool, + pub dca: bool, + pub sse4_1: bool, + pub sse4_2: bool, + pub x2apic: bool, + pub movbe: bool, + pub popcnt: bool, + pub tsc_deadline: bool, + pub aesni: bool, + pub xsave: bool, + pub osxsave: bool, + pub avx: bool, + pub f16c: bool, + pub rdrand: bool, + pub hypervisor: bool, +} + +impl FeatureInfoEcx { + pub fn to_u32(&self) -> u32 { + (self.sse3 as u32) << 0 + | (self.pclmulqdq as u32) << 1 + | (self.dtes64 as u32) << 2 + | (self.monitor as u32) << 3 + | (self.ds_cpl as u32) << 4 + | (self.vmx as u32) << 5 + | (self.smx as u32) << 6 + | (self.eist as u32) << 7 + | (self.tm2 as u32) << 8 + | (self.ssse3 as u32) << 9 + | (self.cnxt_id as u32) << 10 + | (self.sdbg as u32) << 11 + | (self.fma as u32) << 12 + | (self.cmpxchg16b as u32) << 13 + | (self.xtpr as u32) << 14 + | (self.pdcm as u32) << 15 + | (self._reserved_0 as u32) << 16 + | (self.pcid as u32) << 17 + | (self.dca as u32) << 18 + | (self.sse4_1 as u32) << 19 + | (self.sse4_2 as u32) << 20 + | (self.x2apic as u32) << 21 + | (self.movbe as u32) << 22 + | (self.popcnt as u32) << 23 + | (self.tsc_deadline as u32) << 24 + | (self.aesni as u32) << 25 + | (self.xsave as u32) << 26 + | (self.osxsave as u32) << 27 + | (self.avx as u32) << 28 + | (self.f16c as u32) << 29 + | (self.rdrand as u32) << 30 + | (self.hypervisor as u32) << 31 + } +} + +#[derive(Default)] +#[repr(C, packed)] +pub struct FeatureInfoEdx { + pub fpu: bool, + pub vme: bool, + pub de: bool, + pub pse: bool, + pub tsc: bool, + pub msr: bool, + pub pae: bool, + pub mce: bool, + pub cx8: bool, + pub apic: bool, + pub _reserved_0: bool, + pub sep: bool, + pub mtrr: bool, + pub pge: bool, + pub mca: bool, + pub cmov: bool, + pub pat: bool, + pub pse36: bool, + pub psn: bool, + pub clfsh: bool, + pub _reserved_1: bool, + pub ds: bool, + pub acpi: bool, + pub mmx: bool, + pub fxsr: bool, + pub sse: bool, + pub sse2: bool, + pub ss: bool, + pub htt: bool, + pub tm: bool, + pub _reserved_2: bool, + pub pbe: bool, +} + +impl FeatureInfoEdx { + pub fn to_u32(&self) -> u32 { + (self.fpu as u32) << 0 + | (self.vme as u32) << 1 + | (self.de as u32) << 2 + | (self.pse as u32) << 3 + | (self.tsc as u32) << 4 + | (self.msr as u32) << 5 + | (self.pae as u32) << 6 + | (self.mce as u32) << 7 + | (self.cx8 as u32) << 8 + | (self.apic as u32) << 9 + | (self._reserved_0 as u32) << 10 + | (self.sep as u32) << 11 + | (self.mtrr as u32) << 12 + | (self.pge as u32) << 13 + | (self.mca as u32) << 14 + | (self.cmov as u32) << 15 + | (self.pat as u32) << 16 + | (self.pse36 as u32) << 17 + | (self.psn as u32) << 18 + | (self.clfsh as u32) << 19 + | (self._reserved_1 as u32) << 20 + | (self.ds as u32) << 21 + | (self.acpi as u32) << 22 + | (self.mmx as u32) << 23 + | (self.fxsr as u32) << 24 + | (self.sse as u32) << 25 + | (self.sse2 as u32) << 26 + | (self.ss as u32) << 27 + | (self.htt as u32) << 28 + | (self.tm as u32) << 29 + | (self._reserved_2 as u32) << 30 + | (self.pbe as u32) << 31 + } +} diff --git a/src/vmm/mod.rs b/src/vmm/mod.rs index c7e13be..3951177 100644 --- a/src/vmm/mod.rs +++ b/src/vmm/mod.rs @@ -1,4 +1,5 @@ pub mod asm; +pub mod cpuid; pub mod ept; pub mod error; pub mod linux; diff --git a/src/vmm/vcpu.rs b/src/vmm/vcpu.rs index 447205b..2c525e5 100644 --- a/src/vmm/vcpu.rs +++ b/src/vmm/vcpu.rs @@ -11,10 +11,13 @@ use x86_64::VirtAddr; use crate::{ info, memory::BootInfoFrameAllocator, - vmm::vmcs::{ - DescriptorType, EntryControls, Granularity, PrimaryExitControls, - PrimaryProcessorBasedVmExecutionControls, SecondaryProcessorBasedVmExecutionControls, - SegmentRights, VmxExitInfo, VmxExitReason, + vmm::{ + cpuid, + vmcs::{ + DescriptorType, EntryControls, Granularity, PrimaryExitControls, + PrimaryProcessorBasedVmExecutionControls, SecondaryProcessorBasedVmExecutionControls, + SegmentRights, VmxExitInfo, VmxExitReason, + }, }, }; @@ -215,7 +218,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(true); + primary_exec_ctrl.set_hlt(false); primary_exec_ctrl.set_activate_secondary_controls(true); primary_exec_ctrl.write(); @@ -256,6 +259,8 @@ impl VCpu { entry_ctrl.0 |= (reserved_bits & 0xFFFFFFFF) as u32; entry_ctrl.0 &= (reserved_bits >> 32) as u32; entry_ctrl.set_ia32e_mode_guest(false); + entry_ctrl.set_load_ia32_efer(true); + entry_ctrl.set_load_ia32_pat(true); entry_ctrl.write(); @@ -279,6 +284,9 @@ impl VCpu { exit_ctrl.0 &= (reserved_bits >> 32) as u32; exit_ctrl.set_host_addr_space_size(true); exit_ctrl.set_load_ia32_efer(true); + exit_ctrl.set_save_ia32_efer(true); + exit_ctrl.set_load_ia32_pat(true); + exit_ctrl.set_save_ia32_pat(true); exit_ctrl.write(); @@ -355,8 +363,6 @@ impl VCpu { vmwrite(vmcs::guest::SS_BASE, 0)?; vmwrite(vmcs::guest::DS_BASE, 0)?; vmwrite(vmcs::guest::ES_BASE, 0)?; - vmwrite(vmcs::guest::FS_BASE, 0)?; - vmwrite(vmcs::guest::GS_BASE, 0)?; vmwrite(vmcs::guest::TR_BASE, 0)?; vmwrite(vmcs::guest::GDTR_BASE, 0)?; vmwrite(vmcs::guest::IDTR_BASE, 0)?; @@ -373,15 +379,6 @@ impl VCpu { vmwrite(vmcs::guest::IDTR_LIMIT, 0)?; vmwrite(vmcs::guest::LDTR_LIMIT, 0)?; - vmwrite(vmcs::guest::CS_SELECTOR, 0)?; - vmwrite(vmcs::guest::SS_SELECTOR, 0)?; - vmwrite(vmcs::guest::DS_SELECTOR, 0)?; - vmwrite(vmcs::guest::ES_SELECTOR, 0)?; - vmwrite(vmcs::guest::FS_SELECTOR, 0)?; - vmwrite(vmcs::guest::GS_SELECTOR, 0)?; - vmwrite(vmcs::guest::TR_SELECTOR, 0)?; - vmwrite(vmcs::guest::LDTR_SELECTOR, 0)?; - let cs_right = { let mut rights = SegmentRights::default(); rights.set_rw(true); @@ -448,7 +445,19 @@ impl VCpu { vmwrite(vmcs::guest::TR_ACCESS_RIGHTS, tr_right.0 as u64)?; vmwrite(vmcs::guest::LDTR_ACCESS_RIGHTS, ldtr_right.0 as u64)?; + vmwrite(vmcs::guest::CS_SELECTOR, 0)?; + vmwrite(vmcs::guest::SS_SELECTOR, 0)?; + vmwrite(vmcs::guest::DS_SELECTOR, 0)?; + vmwrite(vmcs::guest::ES_SELECTOR, 0)?; + vmwrite(vmcs::guest::FS_SELECTOR, 0)?; + vmwrite(vmcs::guest::GS_SELECTOR, 0)?; + vmwrite(vmcs::guest::TR_SELECTOR, 0)?; + vmwrite(vmcs::guest::LDTR_SELECTOR, 0)?; + vmwrite(vmcs::guest::FS_BASE, 0)?; + vmwrite(vmcs::guest::GS_BASE, 0)?; + vmwrite(vmcs::guest::IA32_EFER_FULL, 0)?; + vmwrite(vmcs::guest::IA32_EFER_HIGH, 0)?; vmwrite(vmcs::guest::RFLAGS, 0x2)?; vmwrite(vmcs::guest::LINK_PTR_FULL, u64::MAX)?; @@ -477,9 +486,29 @@ impl VCpu { } } + fn print_guest_regs(&self) { + info!("Guest Registers:"); + info!("RAX: {:#x}", self.guest_registers.rax); + info!("RBX: {:#x}", self.guest_registers.rbx); + info!("RCX: {:#x}", self.guest_registers.rcx); + info!("RDX: {:#x}", self.guest_registers.rdx); + info!("RSI: {:#x}", self.guest_registers.rsi); + info!("RDI: {:#x}", self.guest_registers.rdi); + info!("RBP: {:#x}", self.guest_registers.rbp); + info!("R8: {:#x}", self.guest_registers.r8); + info!("R9: {:#x}", self.guest_registers.r9); + info!("R10: {:#x}", self.guest_registers.r10); + info!("R11: {:#x}", self.guest_registers.r11); + info!("R12: {:#x}", self.guest_registers.r12); + info!("R13: {:#x}", self.guest_registers.r13); + info!("R14: {:#x}", self.guest_registers.r14); + info!("R15: {:#x}", self.guest_registers.r15); + } + fn vmentry(&mut self) -> Result<(), InstructionError> { let success = { let result: u16; + self.print_guest_regs(); if !self.launch_done { unsafe { result = crate::vmm::asm::asm_vm_entry(self as *mut _); @@ -512,6 +541,16 @@ impl VCpu { vmwrite(vmcs::host::RSP, rsp).unwrap(); } + fn step_next_inst(&mut self) -> Result<(), VmFail> { + unsafe { + let rip = vmread(vmcs::guest::RIP)?; + vmwrite( + vmcs::guest::RIP, + rip + vmread(vmcs::ro::VMEXIT_INSTRUCTION_LEN)?, + ) + } + } + fn vmexit_handler(&mut self) { let info = VmxExitInfo::read(); @@ -530,14 +569,16 @@ impl VCpu { _ => {} } } else { - info!( - "vcpu RIP: {:#x}", - unsafe { vmread(vmcs::guest::RIP) }.unwrap() - ); + info!("RIP: {:#x}", unsafe { vmread(vmcs::guest::RIP) }.unwrap()); match info.get_reason() { VmxExitReason::HLT => { info!("HLT instruction executed"); } + VmxExitReason::CPUID => { + info!("CPUID instruction executed"); + cpuid::handle_cpuid_exit(self); + self.step_next_inst().unwrap(); + } _ => { panic!("VMExit reason: {:?}", info.get_reason()); } diff --git a/src/vmm/vmcs.rs b/src/vmm/vmcs.rs index d969889..f904b93 100644 --- a/src/vmm/vmcs.rs +++ b/src/vmm/vmcs.rs @@ -629,3 +629,27 @@ impl VmxExitInfo { reason.try_into().unwrap() } } + +pub enum VmxLeaf { + MAXIMUM_INPUT = 0x0, + VERSION_AND_FEATURE_INFO = 0x1, + EXTENDED_FEATURE = 0x7, + EXTENDED_ENUMERATION = 0xD, + EXTENDED_FUNCTION = 0x80000000, + EXTENDED_PROCESSOR_SIGNATURE = 0x80000001, + UNKNOWN = 0xFFFFFFFF, +} + +impl VmxLeaf { + pub fn from(rax: u64) -> VmxLeaf { + match rax { + 0x0 => VmxLeaf::MAXIMUM_INPUT, + 0x1 => VmxLeaf::VERSION_AND_FEATURE_INFO, + 0x7 => VmxLeaf::EXTENDED_FEATURE, + 0xD => VmxLeaf::EXTENDED_ENUMERATION, + 0x80000000 => VmxLeaf::EXTENDED_FUNCTION, + 0x80000001 => VmxLeaf::EXTENDED_PROCESSOR_SIGNATURE, + _ => VmxLeaf::UNKNOWN, + } + } +}