CPUID VMExit handler

This commit is contained in:
Masato Imai
2025-08-10 13:59:54 +00:00
parent ab26b4bf45
commit 0f0aeda267
3 changed files with 306 additions and 1 deletions

View File

@ -0,0 +1,288 @@
use modular_bitfield::bitfield;
use raw_cpuid::cpuid;
use crate::vmm::x86_64::intel::vcpu::IntelVCpu;
pub fn handle_cpuid_vmexit(vcpu: &mut IntelVCpu) {
let regs = &mut vcpu.guest_registers;
let vendor: &[u8; 12] = b"miHypervisor";
let brand_string: &[u8; 48] = b"mii Hypervisor CPU on Intel VT-x \0";
let vendor = unsafe { core::mem::transmute::<&[u8; 12], &[u32; 3]>(vendor) };
let brand_string = unsafe { core::mem::transmute::<&[u8; 48], &[u32; 12]>(brand_string) };
match VmxLeaf::from(regs.rax) {
VmxLeaf::EXTENDED_FEATURE_2 => {
regs.rax = brand_string[0] as u64;
regs.rbx = brand_string[1] as u64;
regs.rcx = brand_string[2] as u64;
regs.rdx = brand_string[3] as u64;
}
VmxLeaf::EXTENDED_FEATURE_3 => {
regs.rax = brand_string[4] as u64;
regs.rbx = brand_string[5] as u64;
regs.rcx = brand_string[6] as u64;
regs.rdx = brand_string[7] as u64;
}
VmxLeaf::EXTENDED_FEATURE_4 => {
regs.rax = brand_string[8] as u64;
regs.rbx = brand_string[9] as u64;
regs.rcx = brand_string[10] as u64;
regs.rdx = brand_string[11] as u64;
}
VmxLeaf::EXTENDED_ENUMERATION => match regs.rcx {
0 => {
regs.rax = 0b11;
regs.rbx = 576;
regs.rcx = 576;
regs.rdx = 0x00000000;
}
1 => {
regs.rax = 0x00000001;
regs.rbx = 0;
regs.rcx = 0;
regs.rdx = 0;
}
2 => {
regs.rax = 512;
regs.rbx = 0;
regs.rcx = 0;
regs.rdx = 0;
}
_ => {
invalid(vcpu);
}
},
VmxLeaf::EXTENDED_FEATURE => match regs.rcx {
0 => {
let mut ebx = ExtFeatureEbx0::new()
.with_fsgsbase(false)
.with_smep(true)
.with_invpcid(false)
.with_smap(true);
regs.rax = 1;
regs.rbx = u32::from(ebx) as u64;
regs.rcx = 0;
regs.rdx = 0;
}
1 => {
invalid(vcpu);
}
2 => {
invalid(vcpu);
}
_ => {
panic!("Unhandled CPUID leaf: {:#x}.{:#x}", regs.rax, regs.rcx);
}
},
VmxLeaf::EXTENDED_PROCESSOR_SIGNATURE => {
let signature = cpuid!(0x80000001, 0);
regs.rax = 0x00000000;
regs.rbx = 0x00000000;
regs.rcx = signature.ecx as u64;
regs.rdx = signature.edx as u64;
}
VmxLeaf::EXTENDED_FUNCTION => {
regs.rax = 0x80000000 + 4;
regs.rbx = 0x00000000;
regs.rcx = 0x00000000;
regs.rdx = 0x00000000;
}
VmxLeaf::MAXIMUM_INPUT => {
regs.rax = 0x20;
regs.rbx = vendor[0] as u64;
regs.rcx = vendor[2] as u64;
regs.rdx = vendor[1] as u64;
}
VmxLeaf::VERSION_AND_FEATURE_INFO => {
let ecx = FeatureInfoEcx::new()
.with_pcid(true)
.with_sse4_1(true)
.with_sse4_2(true)
.with_xsave(true)
.with_osxsave(true);
let edx = FeatureInfoEdx::new()
.with_fpu(true)
.with_vme(true)
.with_de(true)
.with_pse(true)
.with_msr(true)
.with_pae(true)
.with_cx8(true)
.with_sep(true)
.with_pge(true)
.with_cmov(true)
.with_pse36(true)
.with_acpi(true)
.with_fxsr(true)
.with_sse(true)
.with_sse2(true);
let mut version_and_feature_info = cpuid!(0x1, 0);
let feature = cpuid!(0x0, 0);
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 = u32::from(ecx) as u64;
regs.rdx = u32::from(edx) as u64;
}
_ => {
invalid(vcpu);
}
}
}
fn invalid(vcpu: &mut IntelVCpu) {
let regs = &mut vcpu.guest_registers;
regs.rax = 0;
regs.rbx = 0;
regs.rcx = 0;
regs.rdx = 0;
}
#[bitfield]
#[repr(u32)]
#[derive(Debug, Clone, Copy)]
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,
}
#[bitfield]
#[repr(u32)]
#[derive(Debug, Clone, Copy)]
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,
}
#[bitfield]
#[repr(u32)]
#[derive(Debug, Clone, Copy)]
pub struct ExtFeatureEbx0 {
pub fsgsbase: bool,
pub tsc_adjust: bool,
pub sgx: bool,
pub bmi1: bool,
pub hle: bool,
pub avx2: bool,
pub fdp: bool,
pub smep: bool,
pub bmi2: bool,
pub erms: bool,
pub invpcid: bool,
pub rtm: bool,
pub rdtm: bool,
pub fpucsds: bool,
pub mpx: bool,
pub rdta: bool,
pub avx512f: bool,
pub avx512dq: bool,
pub rdseed: bool,
pub adx: bool,
pub smap: bool,
pub avx512ifma: bool,
pub _reserved1: bool,
pub clflushopt: bool,
pub clwb: bool,
pub pt: bool,
pub avx512pf: bool,
pub avx512er: bool,
pub avx512cd: bool,
pub sha: bool,
pub avx512bw: bool,
pub avx512vl: bool,
}
pub enum VmxLeaf {
MAXIMUM_INPUT = 0x0,
VERSION_AND_FEATURE_INFO = 0x1,
EXTENDED_FEATURE = 0x7,
EXTENDED_ENUMERATION = 0xD,
EXTENDED_FUNCTION = 0x80000000,
EXTENDED_PROCESSOR_SIGNATURE = 0x80000001,
EXTENDED_FEATURE_2 = 0x80000002,
EXTENDED_FEATURE_3 = 0x80000003,
EXTENDED_FEATURE_4 = 0x80000004,
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,
0x80000002 => VmxLeaf::EXTENDED_FEATURE_2,
0x80000003 => VmxLeaf::EXTENDED_FEATURE_3,
0x80000004 => VmxLeaf::EXTENDED_FEATURE_4,
_ => VmxLeaf::UNKNOWN,
}
}
}

View File

@ -1,5 +1,6 @@
pub mod asm; pub mod asm;
mod controls; mod controls;
mod cpuid;
mod ept; mod ept;
mod register; mod register;
pub mod vcpu; pub mod vcpu;

View File

@ -12,7 +12,7 @@ use crate::{
x86_64::{ x86_64::{
common::{self, read_msr}, common::{self, read_msr},
intel::{ intel::{
controls, ept, controls, cpuid, ept,
register::GuestRegisters, register::GuestRegisters,
vmcs::{ vmcs::{
self, self,
@ -76,6 +76,11 @@ impl IntelVCpu {
VmxExitReason::HLT => { VmxExitReason::HLT => {
//info!("VM hlt"); //info!("VM hlt");
} }
VmxExitReason::CPUID => {
info!("VM exit reason: CPUID");
cpuid::handle_cpuid_vmexit(self);
self.step_next_inst()?;
}
VmxExitReason::EPT_VIOLATION => { VmxExitReason::EPT_VIOLATION => {
let guest_address = vmread(vmcs::ro::GUEST_PHYSICAL_ADDR_FULL)?; let guest_address = vmread(vmcs::ro::GUEST_PHYSICAL_ADDR_FULL)?;
info!("EPT Violation at guest address: {:#x}", guest_address); info!("EPT Violation at guest address: {:#x}", guest_address);
@ -83,6 +88,7 @@ impl IntelVCpu {
} }
_ => { _ => {
info!("VM exit reason: {:?}", exit_reason); info!("VM exit reason: {:?}", exit_reason);
return Err("Unhandled VM exit reason");
} }
} }
} }
@ -90,6 +96,16 @@ impl IntelVCpu {
Ok(()) Ok(())
} }
fn step_next_inst(&mut self) -> Result<(), &'static str> {
use x86::vmx::vmcs;
let rip = vmread(vmcs::guest::RIP)?;
vmwrite(
vmcs::guest::RIP,
rip + vmread(vmcs::ro::VMEXIT_INSTRUCTION_LEN)?,
)?;
Ok(())
}
fn vmentry(&mut self) -> Result<(), InstructionError> { fn vmentry(&mut self) -> Result<(), InstructionError> {
let success = { let success = {
let result: u16; let result: u16;