diff --git a/bzImage b/bzImage index 71e422e..e7ebb49 100644 Binary files a/bzImage and b/bzImage differ diff --git a/src/vga_buffer.rs b/src/vga_buffer.rs index 3aa41c4..55d7ff0 100644 --- a/src/vga_buffer.rs +++ b/src/vga_buffer.rs @@ -30,6 +30,11 @@ macro_rules! info { ($($arg:tt)*) => ($crate::print!("[info] {}\n", format_args!($($arg)*))); } +#[macro_export] +macro_rules! error { + ($($arg:tt)*) => ($crate::print!("[error] {}\n", format_args!($($arg)*))); +} + #[doc(hidden)] pub fn _print(args: fmt::Arguments) { use core::fmt::Write; diff --git a/src/vmm/cpuid.rs b/src/vmm/cpuid.rs index a44084f..5e2f5f8 100644 --- a/src/vmm/cpuid.rs +++ b/src/vmm/cpuid.rs @@ -1,17 +1,48 @@ +use crate::info; use raw_cpuid::cpuid; 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"miHypervisor"; - let vendor: &[u8; 12] = b"KVMKVMKVMKVM"; + let vendor: &[u8; 12] = b"miHypervisor"; let vendor = unsafe { core::mem::transmute::<&[u8; 12], &[u32; 3]>(vendor) }; + match VmxLeaf::from(regs.rax) { + VmxLeaf::EXTENDED_ENUMERATION => { + info!("CPUID: {:#x}.{:#x}", regs.rax, regs.rcx); + match regs.rcx { + 0 => { + // EAX: supported XSAVE features (x87=bit0, SSE=bit1) + regs.rax = 0b11; // Only x87 and SSE supported + regs.rbx = 576; // Size required for enabled features + regs.rcx = 576; // Size required for all features + regs.rdx = 0x00000000; + } + 1 => { + // EAX: XSAVEOPT and extended features + regs.rax = 0x00000001; // XSAVEOPT supported + regs.rbx = 0; // Size of enabled features in compacted format + regs.rcx = 0; // Lower 32 bits of XCR0 | IA32_XSS + regs.rdx = 0; // Upper 32 bits of XCR0 | IA32_XSS + } + 2 => { + // x87 state component + regs.rax = 512; // Size of x87 state + regs.rbx = 0; // Offset in save area + regs.rcx = 0; // Not used for legacy components + regs.rdx = 0; // Not used for legacy components + } + _ => { + invalid(vcpu); + } + } + } VmxLeaf::EXTENDED_FEATURE => match regs.rcx { 0 => { let mut ebx = ExtFeatureEbx0::default(); + ebx.fsgsbase = false; ebx.smep = true; ebx.invpcid = false; ebx.smap = true; @@ -70,15 +101,15 @@ pub fn handle_cpuid_exit(vcpu: &mut VCpu) { _reserved_0: false, pcid: true, dca: false, - sse4_1: false, - sse4_2: false, + sse4_1: true, + sse4_2: true, x2apic: false, movbe: false, popcnt: false, tsc_deadline: false, aesni: false, - xsave: false, - osxsave: false, + xsave: true, + osxsave: true, avx: false, f16c: false, rdrand: false, @@ -94,7 +125,7 @@ pub fn handle_cpuid_exit(vcpu: &mut VCpu) { pae: true, mce: false, cx8: true, - apic: false, + apic: true, _reserved_0: false, sep: true, mtrr: false, @@ -118,9 +149,14 @@ pub fn handle_cpuid_exit(vcpu: &mut VCpu) { _reserved_2: false, pbe: false, }; - let cpuid = cpuid!(0x1, 0); - regs.rax = cpuid.eax as u64; - regs.rbx = cpuid.ebx as u64; + let mut version_and_feature_info = cpuid!(0x1, 0); + let feature = cpuid!(0x0, 0); + if !((feature.ecx & (1 << 17)) == 1) { + version_and_feature_info.ecx &= !(1 << 17); + } + + regs.rax = version_and_feature_info.eax as u64; + regs.rbx = version_and_feature_info.ebx as u64; regs.rcx = ecx.to_u32() as u64; regs.rdx = edx.to_u32() as u64; } @@ -132,6 +168,7 @@ pub fn handle_cpuid_exit(vcpu: &mut VCpu) { fn invalid(vcpu: &mut VCpu) { let regs = &mut vcpu.guest_registers; + //info!("Invalid CPUID: {:#x}.{:#x}", regs.rax, regs.rcx); regs.rax = 0; regs.rbx = 0; diff --git a/src/vmm/cr.rs b/src/vmm/cr.rs index 46468dd..0ea9449 100644 --- a/src/vmm/cr.rs +++ b/src/vmm/cr.rs @@ -6,8 +6,6 @@ use x86::{ vmx::vmcs, }; -use crate::info; - use super::{ qual::{AccessType, QualCr, Register}, vcpu::VCpu, diff --git a/src/vmm/fpu.rs b/src/vmm/fpu.rs new file mode 100644 index 0000000..21ae09c --- /dev/null +++ b/src/vmm/fpu.rs @@ -0,0 +1,75 @@ +use bitfield::bitfield; + +use crate::{error, info}; + +use super::vcpu::VCpu; + +bitfield! { + pub struct XCR0(u64); + + pub x87, set_x87: 0, 1; + pub sse, set_sse: 1, 1; + pub avx, set_avx: 2, 1; + pub bndreg, set_bndreg: 3, 1; + pub bndcsr, set_bndcsr: 4, 1; + pub opmask, set_opmask: 5, 1; + pub zmm_hi256, set_zmm_hi256: 6, 1; + pub hi16_zmm, set_hi16_zmm: 7, 1; + pub pt, set_pt: 8, 1; + pub pkru, set_pkru: 9, 1; + pub pasid, set_pasid: 10, 1; + pub cet_u, set_cet_u: 11, 1; + pub cet_s, set_cet_s: 12, 1; + pub hdc, set_hdc: 13, 1; + pub intr, set_intr: 14, 1; + pub lbr, set_lbr: 15, 1; + pub hwp, set_hwp: 16, 1; + pub xtilecfg, set_xtilecfg: 17, 1; + pub xtiledata, set_xtiledata: 18, 1; + pub apx, set_apx: 19, 1; + pub reserved, set_reserved: 20, 44; +} + +pub fn set_xcr(vcpu: &mut VCpu, index: u32, xcr: u64) -> Result<(), ()> { + info!("Setting XCR0: index={}, xcr={:x}", index, xcr); + if index != 0 { + error!("Invalid XCR index: {}", index); + return Err(()); + } + + if !(xcr & 0b1 != 0) { + error!("X87 is not enabled"); + return Err(()); + } + + if (xcr & 0b100 != 0) && !(xcr & 0b10 != 0) { + error!("SSE is not enabled"); + return Err(()); + } + + if !(xcr & 0b1000) != (!(xcr & 0b10000)) { + error!("BNDREGS and BNDCSR are not both enabled"); + return Err(()); + } + + if xcr & 0b11100000 != 0 { + if !(xcr & 0b100 != 0) { + error!("YMM bits are not enabled"); + return Err(()); + } + + if (xcr & 0b11100000) != 0b11100000 { + error!("Invalid bits set in XCR0"); + return Err(()); + } + } + + if (xcr & 0b1000000000000 != 0) && (xcr & 0b1000000000000 != 0b1000000000000) { + error!("xtile bits are not both enabled"); + return Err(()); + } + + vcpu.xcr0 = XCR0(xcr); + + Ok(()) +} diff --git a/src/vmm/mod.rs b/src/vmm/mod.rs index b9997a5..0b73ad7 100644 --- a/src/vmm/mod.rs +++ b/src/vmm/mod.rs @@ -3,6 +3,7 @@ pub mod cpuid; pub mod cr; pub mod ept; pub mod error; +pub mod fpu; pub mod linux; pub mod msr; pub mod qual; diff --git a/src/vmm/msr.rs b/src/vmm/msr.rs index 686ba01..83fa2c3 100644 --- a/src/vmm/msr.rs +++ b/src/vmm/msr.rs @@ -128,7 +128,7 @@ impl ShadowMsr { } x86::msr::IA32_KERNEL_GSBASE => Self::shadow_read(vcpu, msr_kind), _ => { - panic!("Unhandled RDMSR: {}", msr_kind); + panic!("Unhandled RDMSR: {:#x}", msr_kind); } } } @@ -145,6 +145,7 @@ impl ShadowMsr { x86::msr::IA32_TSC_AUX => Self::shadow_write(vcpu, msr_kind), x86::msr::IA32_FMASK => Self::shadow_write(vcpu, msr_kind), x86::msr::IA32_KERNEL_GSBASE => Self::shadow_write(vcpu, msr_kind), + x86::msr::MSR_C5_PMON_BOX_CTRL => Self::shadow_write(vcpu, msr_kind), x86::msr::SYSENTER_CS_MSR => unsafe { vmwrite(vmcs::guest::IA32_SYSENTER_CS, value).unwrap() }, @@ -157,8 +158,9 @@ impl ShadowMsr { x86::msr::IA32_EFER => unsafe { vmwrite(vmcs::guest::IA32_EFER_FULL, value).unwrap() }, x86::msr::IA32_FS_BASE => unsafe { vmwrite(vmcs::guest::FS_BASE, value).unwrap() }, x86::msr::IA32_GS_BASE => unsafe { vmwrite(vmcs::guest::GS_BASE, value).unwrap() }, + _ => { - panic!("Unhandled WRMSR: {}", msr_kind); + panic!("Unhandled WRMSR: {:#x}", msr_kind); } } } diff --git a/src/vmm/qual.rs b/src/vmm/qual.rs index 11f9bbe..3b01842 100644 --- a/src/vmm/qual.rs +++ b/src/vmm/qual.rs @@ -125,3 +125,16 @@ impl QualCr { self.set_register_raw(val as u8); } } + +bitfield! { + #[derive(Clone, Copy)] + pub struct QualIo(u64); + impl Debug; + + u8, size, set_size: 3, 0; + u8, direction, set_direction: 4, 4; + u8, string, set_string: 5, 5; + u8, rep, set_rep: 6, 6; + u8, operand_encoding, set_operand_encoding: 7, 7; + u16, port, set_port: 31, 16; +} diff --git a/src/vmm/vcpu.rs b/src/vmm/vcpu.rs index 8a4d254..7ce1097 100644 --- a/src/vmm/vcpu.rs +++ b/src/vmm/vcpu.rs @@ -1,4 +1,7 @@ -use core::u64; +use core::{ + arch::x86_64::{_xgetbv, _xsetbv}, + u64, +}; use x86::{ bits64::vmx::{vmread, vmwrite}, @@ -13,7 +16,7 @@ use crate::{ info, memory::BootInfoFrameAllocator, vmm::{ - cpuid, cr, msr, + cpuid, cr, fpu, msr, qual::QualCr, vmcs::{ DescriptorType, EntryControls, Granularity, PrimaryExitControls, @@ -25,6 +28,7 @@ use crate::{ use super::{ ept::{EPT, EPTP}, + fpu::XCR0, linux::{self, BootParams, E820Type}, msr::ShadowMsr, register::GuestRegisters, @@ -44,6 +48,8 @@ pub struct VCpu { pub host_msr: ShadowMsr, pub guest_msr: ShadowMsr, pub ia32e_enabled: bool, + pub xcr0: XCR0, + pub host_xcr0: u64, } const TEMP_STACK_SIZE: usize = 4096; @@ -68,6 +74,8 @@ impl VCpu { host_msr: ShadowMsr::new(), guest_msr: ShadowMsr::new(), ia32e_enabled: false, + xcr0: XCR0(3), + host_xcr0: 0, } } @@ -200,6 +208,9 @@ impl VCpu { rdmsr(x86::msr::IA32_KERNEL_GSBASE) as u64, ) .unwrap(); + self.host_msr + .set(x86::msr::MSR_C5_PMON_BOX_CTRL, 0) + .unwrap(); self.guest_msr.set(x86::msr::IA32_TSC_AUX, 0).unwrap(); self.guest_msr.set(x86::msr::IA32_STAR, 0).unwrap(); @@ -207,6 +218,9 @@ impl VCpu { self.guest_msr.set(x86::msr::IA32_CSTAR, 0).unwrap(); self.guest_msr.set(x86::msr::IA32_FMASK, 0).unwrap(); self.guest_msr.set(x86::msr::IA32_KERNEL_GSBASE, 0).unwrap(); + self.guest_msr + .set(x86::msr::MSR_C5_PMON_BOX_CTRL, 0) + .unwrap(); vmwrite( vmcs::control::VMEXIT_MSR_LOAD_ADDR_FULL, @@ -286,10 +300,11 @@ 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.set_use_tpr_shadow(true); primary_exec_ctrl.set_use_msr_bitmap(false); + primary_exec_ctrl.set_unconditional_io(true); primary_exec_ctrl.write(); @@ -365,6 +380,14 @@ impl VCpu { exit_ctrl.write(); + unsafe { + vmwrite( + vmcs::control::EXCEPTION_BITMAP, + 0, /*(1u64 << irq::INVALID_OPCODE_VECTOR)*/ + ) + .unwrap(); + }; + Ok(()) } @@ -373,7 +396,10 @@ impl VCpu { unsafe { vmwrite(vmcs::host::CR0, cr0().bits() as u64)?; vmwrite(vmcs::host::CR3, cr3())?; - vmwrite(vmcs::host::CR4, cr4().bits() as u64)?; + vmwrite( + vmcs::host::CR4, + cr4().bits() as u64 | Cr4Flags::OSXSAVE.bits(), + )?; vmwrite(vmcs::host::RIP, crate::vmm::asm::asm_vmexit_handler as u64)?; vmwrite( @@ -433,10 +459,7 @@ impl VCpu { vmwrite(vmcs::guest::CR3, cr3())?; vmwrite( vmcs::guest::CR4, - vmread(vmcs::guest::CR4)? - | 1 << 5 - | 1 << 7 - | Cr4Flags::VIRTUAL_MACHINE_EXTENSIONS.bits(), + vmread(vmcs::guest::CR4)? | Cr4Flags::VIRTUAL_MACHINE_EXTENSIONS.bits(), )?; vmwrite(vmcs::guest::CS_BASE, 0)?; @@ -570,13 +593,57 @@ impl VCpu { } } + fn load_guest_xcr0(&mut self) -> Result<(), VmFail> { + let host_cr4 = unsafe { cr4() }; + if (host_cr4.bits() & Cr4Flags::OSXSAVE.bits() as usize) == 0 { + return Ok(()); + } + + if self.host_xcr0 == 0 { + self.host_xcr0 = unsafe { _xgetbv(0) }; + } + + let guest_cr4 = unsafe { vmread(vmcs::guest::CR4)? }; + + if guest_cr4 & Cr4Flags::OSXSAVE.bits() != 0 && self.xcr0.0 != self.host_xcr0 { + unsafe { + _xsetbv(0, self.xcr0.0); + } + } + + Ok(()) + } + + fn load_host_xcr0(&mut self) -> Result<(), VmFail> { + let host_cr4 = unsafe { cr4() }; + if (host_cr4.bits() & Cr4Flags::OSXSAVE.bits() as usize) == 0 { + return Ok(()); + } + + let guest_cr4 = unsafe { vmread(vmcs::guest::CR4)? }; + + if guest_cr4 & Cr4Flags::OSXSAVE.bits() != 0 { + let current_xcr0 = unsafe { _xgetbv(0) }; + if current_xcr0 != self.host_xcr0 { + self.xcr0 = XCR0(current_xcr0); + unsafe { + _xsetbv(0, self.host_xcr0); + } + } + } + + Ok(()) + } + fn vmentry(&mut self) -> Result<(), InstructionError> { let success = { let result: u16; + self.load_guest_xcr0().unwrap(); unsafe { result = crate::vmm::asm::asm_vm_entry(self as *mut _); }; + self.load_host_xcr0().unwrap(); result == 0 }; @@ -627,6 +694,7 @@ impl VCpu { _ => {} } } else { + info!("RIP: {:#x}", unsafe { vmread(vmcs::guest::RIP) }.unwrap()); match info.get_reason() { VmxExitReason::HLT => { info!("HLT instruction executed"); @@ -649,6 +717,22 @@ impl VCpu { cr::handle_cr_access(self, &qual); self.step_next_inst().unwrap(); } + VmxExitReason::XSETBV => { + fpu::set_xcr( + self, + self.guest_registers.rcx as u32, + self.guest_registers.rax, + ) + .unwrap(); + self.step_next_inst().unwrap(); + } + VmxExitReason::EXCEPTION => { + self.step_next_inst().unwrap(); + } + VmxExitReason::IO_INSTRUCTION => { + info!("IO instruction executed"); + self.step_next_inst().unwrap(); + } _ => { panic!("VMExit reason: {:?}", info.get_reason()); }