This commit is contained in:
Masato Imai
2025-05-16 10:42:49 +00:00
parent 01723d6c44
commit 71cfacfd1d
4 changed files with 400 additions and 3 deletions

View File

@ -9,6 +9,28 @@ pub fn handle_cpuid_exit(vcpu: &mut VCpu) {
let vendor: &[u8; 12] = b"miimiimiimii"; let vendor: &[u8; 12] = b"miimiimiimii";
let vendor = unsafe { core::mem::transmute::<&[u8; 12], &[u32; 3]>(vendor) }; let vendor = unsafe { core::mem::transmute::<&[u8; 12], &[u32; 3]>(vendor) };
match VmxLeaf::from(regs.rax) { 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 => { VmxLeaf::EXTENDED_PROCESSOR_SIGNATURE => {
info!("CPUID extended processor signature"); info!("CPUID extended processor signature");
let signature = cpuid!(0x80000001, 0); let signature = cpuid!(0x80000001, 0);
@ -270,3 +292,115 @@ impl FeatureInfoEdx {
| (self.pbe as u32) << 31 | (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
}
}

View File

@ -3,6 +3,7 @@ pub mod cpuid;
pub mod ept; pub mod ept;
pub mod error; pub mod error;
pub mod linux; pub mod linux;
pub mod msr;
pub mod register; pub mod register;
pub mod support; pub mod support;
pub mod vcpu; pub mod vcpu;

165
src/vmm/msr.rs Normal file
View File

@ -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<SavedMsr>,
}
#[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);
}
}
}
}

View File

@ -6,13 +6,13 @@ use x86::{
msr::{rdmsr, IA32_EFER, IA32_FS_BASE}, msr::{rdmsr, IA32_EFER, IA32_FS_BASE},
vmx::{vmcs, VmFail}, vmx::{vmcs, VmFail},
}; };
use x86_64::{registers::control::Cr4Flags, VirtAddr}; use x86_64::{registers::control::Cr4Flags, structures::paging::OffsetPageTable, VirtAddr};
use crate::{ use crate::{
info, info,
memory::BootInfoFrameAllocator, memory::BootInfoFrameAllocator,
vmm::{ vmm::{
cpuid, cpuid, msr,
vmcs::{ vmcs::{
DescriptorType, EntryControls, Granularity, PrimaryExitControls, DescriptorType, EntryControls, Granularity, PrimaryExitControls,
PrimaryProcessorBasedVmExecutionControls, SecondaryProcessorBasedVmExecutionControls, PrimaryProcessorBasedVmExecutionControls, SecondaryProcessorBasedVmExecutionControls,
@ -24,6 +24,7 @@ use crate::{
use super::{ use super::{
ept::{EPT, EPTP}, ept::{EPT, EPTP},
linux::{self, BootParams, E820Type}, linux::{self, BootParams, E820Type},
msr::ShadowMsr,
register::GuestRegisters, register::GuestRegisters,
vmcs::{InstructionError, PinBasedVmExecutionControls, Vmcs}, vmcs::{InstructionError, PinBasedVmExecutionControls, Vmcs},
vmxon::Vmxon, vmxon::Vmxon,
@ -38,6 +39,8 @@ pub struct VCpu {
pub launch_done: bool, pub launch_done: bool,
pub ept: EPT, pub ept: EPT,
pub eptp: EPTP, pub eptp: EPTP,
pub host_msr: ShadowMsr,
pub guest_msr: ShadowMsr,
} }
const TEMP_STACK_SIZE: usize = 4096; const TEMP_STACK_SIZE: usize = 4096;
@ -59,6 +62,8 @@ impl VCpu {
launch_done: false, launch_done: false,
ept, ept,
eptp, eptp,
host_msr: ShadowMsr::new(),
guest_msr: ShadowMsr::new(),
} }
} }
@ -191,6 +196,88 @@ impl VCpu {
unsafe { vmwrite(vmcs::control::EPTP_FULL, eptp.0).unwrap() }; 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<u32> = 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> { pub fn setup_exec_ctrls(&mut self) -> Result<(), VmFail> {
info!("Setting up pin based execution controls"); info!("Setting up pin based execution controls");
let basic_msr = unsafe { rdmsr(x86::msr::IA32_VMX_BASIC) }; 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_hlt(false);
primary_exec_ctrl.set_activate_secondary_controls(true); primary_exec_ctrl.set_activate_secondary_controls(true);
primary_exec_ctrl.set_use_tpr_shadow(true); primary_exec_ctrl.set_use_tpr_shadow(true);
primary_exec_ctrl.set_use_msr_bitmap(false);
primary_exec_ctrl.write(); primary_exec_ctrl.write();
@ -555,10 +643,19 @@ impl VCpu {
info!("HLT instruction executed"); info!("HLT instruction executed");
} }
VmxExitReason::CPUID => { VmxExitReason::CPUID => {
info!("CPUID instruction executed");
cpuid::handle_cpuid_exit(self); cpuid::handle_cpuid_exit(self);
self.step_next_inst().unwrap(); 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()); panic!("VMExit reason: {:?}", info.get_reason());
} }