From 71cfacfd1db309b92352411541c56bd68efec71c Mon Sep 17 00:00:00 2001 From: Masato Imai Date: Fri, 16 May 2025 10:42:49 +0000 Subject: [PATCH] msr --- src/vmm/cpuid.rs | 134 ++++++++++++++++++++++++++++++++++++++ src/vmm/mod.rs | 1 + src/vmm/msr.rs | 165 +++++++++++++++++++++++++++++++++++++++++++++++ src/vmm/vcpu.rs | 103 ++++++++++++++++++++++++++++- 4 files changed, 400 insertions(+), 3 deletions(-) create mode 100644 src/vmm/msr.rs diff --git a/src/vmm/cpuid.rs b/src/vmm/cpuid.rs index ff4d0a8..85c493f 100644 --- a/src/vmm/cpuid.rs +++ b/src/vmm/cpuid.rs @@ -9,6 +9,28 @@ pub fn handle_cpuid_exit(vcpu: &mut VCpu) { let vendor: &[u8; 12] = b"miimiimiimii"; 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; + ebx.smap = true; + regs.rax = 1; + regs.rbx = ebx.as_u32() 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 => { info!("CPUID extended processor signature"); let signature = cpuid!(0x80000001, 0); @@ -270,3 +292,115 @@ impl FeatureInfoEdx { | (self.pbe as u32) << 31 } } + +#[derive(Debug)] +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, +} + +impl Default for ExtFeatureEbx0 { + fn default() -> Self { + Self { + fsgsbase: false, + tsc_adjust: false, + sgx: false, + bmi1: false, + hle: false, + avx2: false, + fdp: false, + smep: false, + bmi2: false, + erms: false, + invpcid: false, + rtm: false, + rdtm: false, + fpucsds: false, + mpx: false, + rdta: false, + avx512f: false, + avx512dq: false, + rdseed: false, + adx: false, + smap: false, + avx512ifma: false, + _reserved1: false, + clflushopt: false, + clwb: false, + pt: false, + avx512pf: false, + avx512er: false, + avx512cd: false, + sha: false, + avx512bw: false, + avx512vl: false, + } + } +} + +impl ExtFeatureEbx0 { + pub fn as_u32(&self) -> u32 { + (self.fsgsbase as u32) + | (self.tsc_adjust as u32) << 1 + | (self.sgx as u32) << 2 + | (self.bmi1 as u32) << 3 + | (self.hle as u32) << 4 + | (self.avx2 as u32) << 5 + | (self.fdp as u32) << 6 + | (self.smep as u32) << 7 + | (self.bmi2 as u32) << 8 + | (self.erms as u32) << 9 + | (self.invpcid as u32) << 10 + | (self.rtm as u32) << 11 + | (self.rdtm as u32) << 12 + | (self.fpucsds as u32) << 13 + | (self.mpx as u32) << 14 + | (self.rdta as u32) << 15 + | (self.avx512f as u32) << 16 + | (self.avx512dq as u32) << 17 + | (self.rdseed as u32) << 18 + | (self.adx as u32) << 19 + | (self.smap as u32) << 20 + | (self.avx512ifma as u32) << 21 + | (self._reserved1 as u32) << 22 + | (self.clflushopt as u32) << 23 + | (self.clwb as u32) << 24 + | (self.pt as u32) << 25 + | (self.avx512pf as u32) << 26 + | (self.avx512er as u32) << 27 + | (self.avx512cd as u32) << 28 + | (self.sha as u32) << 29 + | (self.avx512bw as u32) << 30 + | (self.avx512vl as u32) << 31 + } +} diff --git a/src/vmm/mod.rs b/src/vmm/mod.rs index 3951177..01ba660 100644 --- a/src/vmm/mod.rs +++ b/src/vmm/mod.rs @@ -3,6 +3,7 @@ pub mod cpuid; pub mod ept; pub mod error; pub mod linux; +pub mod msr; pub mod register; pub mod support; pub mod vcpu; diff --git a/src/vmm/msr.rs b/src/vmm/msr.rs new file mode 100644 index 0000000..e266d59 --- /dev/null +++ b/src/vmm/msr.rs @@ -0,0 +1,165 @@ +use core::u64; + +use alloc::vec; +use alloc::vec::Vec; +use x86::bits64::vmx::{vmread, vmwrite}; +use x86::vmx::vmcs; +use x86_64::structures::paging::{OffsetPageTable, Translate}; +use x86_64::{PhysAddr, VirtAddr}; + +use super::vcpu::VCpu; + +type MsrIndex = u32; + +const MAX_NUM_ENTS: usize = 512; + +#[derive(Debug, Clone, Copy)] +#[repr(C, packed)] +pub struct SavedMsr { + pub index: MsrIndex, + pub reserved: u32, + pub data: u64, +} + +impl Default for SavedMsr { + fn default() -> Self { + Self { + index: 0, + reserved: 0, + data: 0, + } + } +} + +#[derive(Debug)] +pub struct ShadowMsr { + ents: Vec, +} + +#[derive(Debug)] +pub enum MsrError { + TooManyEntries, + BitmapAllocationFailed, +} + +impl ShadowMsr { + pub fn new() -> Self { + let ents = vec![SavedMsr::default(); MAX_NUM_ENTS]; + + ShadowMsr { ents } + } + + pub fn set(&mut self, index: MsrIndex, data: u64) -> Result<(), MsrError> { + self.set_by_index(index, data) + } + + pub fn set_by_index(&mut self, index: MsrIndex, data: u64) -> Result<(), MsrError> { + if let Some(entry) = self.ents.iter_mut().find(|e| e.index == index) { + entry.data = data; + return Ok(()); + } + + if self.ents.len() >= MAX_NUM_ENTS { + return Err(MsrError::TooManyEntries); + } + self.ents.push(SavedMsr { + index, + reserved: 0, + data, + }); + Ok(()) + } + + pub fn saved_ents(&self) -> &[SavedMsr] { + &self.ents + } + + pub fn find(&self, index: MsrIndex) -> Option<&SavedMsr> { + self.ents.iter().find(|e| e.index == index) + } + + pub fn phys(&self, mapper: &OffsetPageTable<'static>) -> PhysAddr { + mapper + .translate_addr(VirtAddr::from_ptr(&self.ents)) + .unwrap() + } + + pub fn concat(r1: u64, r2: u64) -> u64 { + ((r1 & 0xFFFFFFFF) << 32) | (r2 & 0xFFFFFFFF) + } + + pub fn set_ret_val(vcpu: &mut VCpu, val: u64) { + vcpu.guest_registers.rdx = (val >> 32) as u32 as u64; + vcpu.guest_registers.rax = val as u32 as u64; + } + + pub fn shadow_read(vcpu: &mut VCpu, msr_kind: MsrIndex) { + if let Some(msr) = vcpu.guest_msr.find(msr_kind) { + Self::set_ret_val(vcpu, msr.data); + } else { + panic!("MSR not found"); + } + } + + pub fn shadow_write(vcpu: &mut VCpu, msr_kind: MsrIndex) { + let regs = &vcpu.guest_registers; + if vcpu.guest_msr.find(msr_kind).is_some() { + vcpu.guest_msr + .set(msr_kind, Self::concat(regs.rdx, regs.rax)) + .unwrap(); + } else { + panic!("MSR not found"); + } + } + + pub fn handle_rdmsr_vmexit(vcpu: &mut VCpu) { + let msr_kind = vcpu.guest_registers.rcx as u32; + + match msr_kind { + x86::msr::APIC_BASE => Self::set_ret_val(vcpu, u64::MAX), + x86::msr::IA32_EFER => Self::set_ret_val(vcpu, unsafe { + vmread(vmcs::guest::IA32_EFER_FULL).unwrap() + }), + x86::msr::IA32_FS_BASE => { + Self::set_ret_val(vcpu, unsafe { vmread(vmcs::guest::FS_BASE).unwrap() }) + } + x86::msr::IA32_GS_BASE => { + Self::set_ret_val(vcpu, unsafe { vmread(vmcs::guest::GS_BASE).unwrap() }) + } + x86::msr::IA32_KERNEL_GSBASE => Self::shadow_read(vcpu, msr_kind), + _ => { + panic!("Unhandled RDMSR: {}", msr_kind); + } + } + } + + pub fn handle_wrmsr_vmexit(vcpu: &mut VCpu) { + let regs = &vcpu.guest_registers; + let value = Self::concat(regs.rdx, regs.rax); + let msr_kind: MsrIndex = regs.rcx as MsrIndex; + + match msr_kind { + x86::msr::IA32_STAR => Self::shadow_write(vcpu, msr_kind), + x86::msr::IA32_LSTAR => Self::shadow_write(vcpu, msr_kind), + x86::msr::IA32_CSTAR => Self::shadow_write(vcpu, msr_kind), + 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::SYSENTER_CS_MSR => unsafe { + vmwrite(vmcs::guest::IA32_SYSENTER_CS, value).unwrap() + }, + x86::msr::SYSENTER_EIP_MSR => unsafe { + vmwrite(vmcs::guest::IA32_SYSENTER_EIP, value).unwrap() + }, + x86::msr::SYSENTER_ESP_MSR => unsafe { + vmwrite(vmcs::guest::IA32_SYSENTER_ESP, value).unwrap() + }, + 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); + } + } + } +} diff --git a/src/vmm/vcpu.rs b/src/vmm/vcpu.rs index bc4ff02..a9c1279 100644 --- a/src/vmm/vcpu.rs +++ b/src/vmm/vcpu.rs @@ -6,13 +6,13 @@ use x86::{ msr::{rdmsr, IA32_EFER, IA32_FS_BASE}, vmx::{vmcs, VmFail}, }; -use x86_64::{registers::control::Cr4Flags, VirtAddr}; +use x86_64::{registers::control::Cr4Flags, structures::paging::OffsetPageTable, VirtAddr}; use crate::{ info, memory::BootInfoFrameAllocator, vmm::{ - cpuid, + cpuid, msr, vmcs::{ DescriptorType, EntryControls, Granularity, PrimaryExitControls, PrimaryProcessorBasedVmExecutionControls, SecondaryProcessorBasedVmExecutionControls, @@ -24,6 +24,7 @@ use crate::{ use super::{ ept::{EPT, EPTP}, linux::{self, BootParams, E820Type}, + msr::ShadowMsr, register::GuestRegisters, vmcs::{InstructionError, PinBasedVmExecutionControls, Vmcs}, vmxon::Vmxon, @@ -38,6 +39,8 @@ pub struct VCpu { pub launch_done: bool, pub ept: EPT, pub eptp: EPTP, + pub host_msr: ShadowMsr, + pub guest_msr: ShadowMsr, } const TEMP_STACK_SIZE: usize = 4096; @@ -59,6 +62,8 @@ impl VCpu { launch_done: false, ept, eptp, + host_msr: ShadowMsr::new(), + guest_msr: ShadowMsr::new(), } } @@ -191,6 +196,88 @@ impl VCpu { unsafe { vmwrite(vmcs::control::EPTP_FULL, eptp.0).unwrap() }; } + pub fn register_msrs(&mut self, mapper: OffsetPageTable<'static>) { + unsafe { + // tsc_aux, star, lstar, cstar, fmask, kernel_gs_base. + self.host_msr + .set(x86::msr::IA32_TSC_AUX, rdmsr(x86::msr::IA32_TSC_AUX) as u64) + .unwrap(); + self.host_msr + .set(x86::msr::IA32_STAR, rdmsr(x86::msr::IA32_STAR) as u64) + .unwrap(); + self.host_msr + .set(x86::msr::IA32_LSTAR, rdmsr(x86::msr::IA32_LSTAR) as u64) + .unwrap(); + self.host_msr + .set(x86::msr::IA32_CSTAR, rdmsr(x86::msr::IA32_CSTAR) as u64) + .unwrap(); + self.host_msr + .set(x86::msr::IA32_FMASK, rdmsr(x86::msr::IA32_FMASK) as u64) + .unwrap(); + self.host_msr + .set( + x86::msr::IA32_KERNEL_GSBASE, + rdmsr(x86::msr::IA32_KERNEL_GSBASE) as u64, + ) + .unwrap(); + + self.guest_msr.set(x86::msr::IA32_TSC_AUX, 0).unwrap(); + self.guest_msr.set(x86::msr::IA32_STAR, 0).unwrap(); + self.guest_msr.set(x86::msr::IA32_LSTAR, 0).unwrap(); + 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(); + + vmwrite( + vmcs::control::VMEXIT_MSR_LOAD_ADDR_FULL, + self.host_msr.phys(&mapper).as_u64(), + ) + .unwrap(); + vmwrite( + vmcs::control::VMEXIT_MSR_STORE_ADDR_FULL, + self.guest_msr.phys(&mapper).as_u64(), + ) + .unwrap(); + vmwrite( + vmcs::control::VMENTRY_MSR_LOAD_ADDR_FULL, + self.guest_msr.phys(&mapper).as_u64(), + ) + .unwrap(); + } + } + + pub fn update_msrs(&mut self) { + let indices_to_update: alloc::vec::Vec = self + .host_msr + .saved_ents() + .iter() + .map(|entry| entry.index) + .collect(); + + for index in indices_to_update { + let value = unsafe { rdmsr(index) }; + self.host_msr.set_by_index(index, value).unwrap(); + } + + unsafe { + vmwrite( + vmcs::control::VMEXIT_MSR_LOAD_COUNT, + self.host_msr.saved_ents().len() as u64, + ) + .unwrap(); + vmwrite( + vmcs::control::VMEXIT_MSR_STORE_COUNT, + self.guest_msr.saved_ents().len() as u64, + ) + .unwrap(); + vmwrite( + vmcs::control::VMENTRY_MSR_LOAD_COUNT, + self.guest_msr.saved_ents().len() as u64, + ) + .unwrap(); + } + } + pub fn setup_exec_ctrls(&mut self) -> Result<(), VmFail> { info!("Setting up pin based execution controls"); let basic_msr = unsafe { rdmsr(x86::msr::IA32_VMX_BASIC) }; @@ -222,6 +309,7 @@ impl VCpu { 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.write(); @@ -555,10 +643,19 @@ impl VCpu { info!("HLT instruction executed"); } VmxExitReason::CPUID => { - info!("CPUID instruction executed"); cpuid::handle_cpuid_exit(self); self.step_next_inst().unwrap(); } + VmxExitReason::RDMSR => { + info!("RDMSR instruction executed"); + msr::ShadowMsr::handle_rdmsr_vmexit(self); + self.step_next_inst().unwrap(); + } + VmxExitReason::WRMSR => { + info!("WRMSR instruction executed"); + msr::ShadowMsr::handle_wrmsr_vmexit(self); + self.step_next_inst().unwrap(); + } _ => { panic!("VMExit reason: {:?}", info.get_reason()); }