Files
nel_os/src/vmm/vcpu.rs
2025-04-22 10:21:13 +00:00

319 lines
11 KiB
Rust

use x86::{
bits64::vmx::vmwrite,
controlregs::{cr0, cr3, cr4},
dtables::{self, DescriptorTablePointer},
halt,
msr::{rdmsr, IA32_EFER, IA32_FS_BASE},
vmx::{vmcs, VmFail},
};
use core::arch::naked_asm;
use crate::{
info,
memory::BootInfoFrameAllocator,
vmm::vmcs::{
DescriptorType, EntryControls, Granularity, PrimaryExitControls,
PrimaryProcessorBasedVmExecutionControls, SegmentRights,
},
};
use super::{
vmcs::{PinBasedVmExecutionControls, Vmcs},
vmxon::Vmxon,
};
pub struct VCpu {
pub vmxon: Vmxon,
pub vmcs: Vmcs,
pub phys_mem_offset: u64,
}
impl VCpu {
pub fn new(phys_mem_offset: u64, frame_allocator: &mut BootInfoFrameAllocator) -> Self {
let mut vmxon = Vmxon::new(frame_allocator);
vmxon.init(phys_mem_offset);
let vmcs = Vmcs::new(frame_allocator);
VCpu {
vmxon,
vmcs,
phys_mem_offset,
}
}
pub fn activate(&mut self) {
self.vmxon.activate_vmxon().unwrap();
let revision_id = unsafe { rdmsr(x86::msr::IA32_VMX_BASIC) } as u32;
self.vmcs
.write_revision_id(revision_id, self.phys_mem_offset);
self.reset_vmcs().unwrap();
self.setup_exec_ctrls().unwrap();
self.setup_entry_ctrls().unwrap();
self.setup_exit_ctrls().unwrap();
self.setup_host_state().unwrap();
self.setup_guest_state().unwrap();
}
pub fn setup_exec_ctrls(&mut self) -> Result<(), VmFail> {
info!("Setting up execution controls");
let basic_msr = unsafe { rdmsr(x86::msr::IA32_VMX_BASIC) };
let mut pin_exec_ctrl = PinBasedVmExecutionControls::read();
let reserved_bits = if basic_msr & (1 << 55) != 0 {
unsafe { rdmsr(x86::msr::IA32_VMX_TRUE_PINBASED_CTLS) }
} else {
unsafe { rdmsr(x86::msr::IA32_VMX_PINBASED_CTLS) }
};
pin_exec_ctrl.0 |= (reserved_bits & 0xFFFFFFFF) as u32;
pin_exec_ctrl.0 &= (reserved_bits >> 32) as u32;
pin_exec_ctrl.write();
let mut primary_exec_ctrl = PrimaryProcessorBasedVmExecutionControls::read();
let reserved_bits = if basic_msr & (1 << 55) != 0 {
unsafe { rdmsr(x86::msr::IA32_VMX_TRUE_PROCBASED_CTLS) }
} else {
unsafe { rdmsr(x86::msr::IA32_VMX_PROCBASED_CTLS) }
};
primary_exec_ctrl.0 |= (reserved_bits & 0xFFFFFFFF) as u32;
primary_exec_ctrl.0 &= (reserved_bits >> 32) as u32;
primary_exec_ctrl.set_hlt(false);
primary_exec_ctrl.set_activate_secondary_controls(false);
primary_exec_ctrl.write();
Ok(())
}
pub fn setup_entry_ctrls(&mut self) -> Result<(), VmFail> {
info!("Setting up entry controls");
let basic_msr = unsafe { rdmsr(x86::msr::IA32_VMX_BASIC) };
let mut entry_ctrl = EntryControls::read();
let reserved_bits = if basic_msr & (1 << 55) != 0 {
unsafe { rdmsr(x86::msr::IA32_VMX_TRUE_ENTRY_CTLS) }
} else {
unsafe { rdmsr(x86::msr::IA32_VMX_ENTRY_CTLS) }
};
entry_ctrl.0 |= (reserved_bits & 0xFFFFFFFF) as u32;
entry_ctrl.0 &= (reserved_bits >> 32) as u32;
entry_ctrl.set_ia32e_mode_guest(true);
entry_ctrl.write();
Ok(())
}
pub fn setup_exit_ctrls(&mut self) -> Result<(), VmFail> {
info!("Setting up exit controls");
let basic_msr = unsafe { rdmsr(x86::msr::IA32_VMX_BASIC) };
let mut exit_ctrl = PrimaryExitControls::read();
let reserved_bits = if basic_msr & (1 << 55) != 0 {
unsafe { rdmsr(x86::msr::IA32_VMX_TRUE_EXIT_CTLS) }
} else {
unsafe { rdmsr(x86::msr::IA32_VMX_EXIT_CTLS) }
};
exit_ctrl.0 |= (reserved_bits & 0xFFFFFFFF) as u32;
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.write();
Ok(())
}
pub fn setup_host_state(&mut self) -> Result<(), VmFail> {
info!("Setting up host state");
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::RIP, Self::vmexit as u64)?;
vmwrite(
vmcs::host::ES_SELECTOR,
x86::segmentation::es().bits() as u64,
)?;
vmwrite(
vmcs::host::CS_SELECTOR,
x86::segmentation::cs().bits() as u64,
)?;
vmwrite(
vmcs::host::SS_SELECTOR,
x86::segmentation::ss().bits() as u64,
)?;
vmwrite(
vmcs::host::DS_SELECTOR,
x86::segmentation::ds().bits() as u64,
)?;
vmwrite(
vmcs::host::FS_SELECTOR,
x86::segmentation::fs().bits() as u64,
)?;
vmwrite(
vmcs::host::GS_SELECTOR,
x86::segmentation::gs().bits() as u64,
)?;
vmwrite(vmcs::host::FS_BASE, rdmsr(IA32_FS_BASE))?;
vmwrite(vmcs::host::GS_BASE, rdmsr(IA32_FS_BASE))?;
let tr = x86::task::tr();
let mut gdtp = DescriptorTablePointer::<u64>::default();
let mut idtp = DescriptorTablePointer::<u64>::default();
dtables::sgdt(&mut gdtp);
dtables::sidt(&mut idtp);
vmwrite(vmcs::host::GDTR_BASE, gdtp.base as u64)?;
vmwrite(vmcs::host::IDTR_BASE, idtp.base as u64)?;
vmwrite(vmcs::host::TR_SELECTOR, tr.bits() as u64)?;
vmwrite(vmcs::host::TR_BASE, 0)?;
vmwrite(vmcs::host::IA32_EFER_FULL, rdmsr(IA32_EFER))?;
}
Ok(())
}
pub fn setup_guest_state(&mut self) -> Result<(), VmFail> {
info!("Setting up guest state");
unsafe {
vmwrite(vmcs::guest::CR0, cr0().bits() as u64)?;
vmwrite(vmcs::guest::CR3, cr3())?;
vmwrite(vmcs::guest::CR4, cr4().bits() as u64)?;
vmwrite(vmcs::guest::CS_BASE, 0)?;
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)?;
vmwrite(vmcs::guest::LDTR_BASE, 0xDEAD00)?;
vmwrite(vmcs::guest::CS_LIMIT, u32::MAX as u64)?;
vmwrite(vmcs::guest::SS_LIMIT, u32::MAX as u64)?;
vmwrite(vmcs::guest::DS_LIMIT, u32::MAX as u64)?;
vmwrite(vmcs::guest::ES_LIMIT, u32::MAX as u64)?;
vmwrite(vmcs::guest::FS_LIMIT, u32::MAX as u64)?;
vmwrite(vmcs::guest::GS_LIMIT, u32::MAX as u64)?;
vmwrite(vmcs::guest::TR_LIMIT, u32::MAX as u64)?;
vmwrite(vmcs::guest::GDTR_LIMIT, u32::MAX as u64)?;
vmwrite(vmcs::guest::IDTR_LIMIT, u32::MAX as u64)?;
vmwrite(vmcs::guest::LDTR_LIMIT, u32::MAX as u64)?;
vmwrite(
vmcs::guest::CS_SELECTOR,
x86::segmentation::cs().bits() as u64,
)?;
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_rights = {
let mut rights = SegmentRights(0);
rights.set_rw(true);
rights.set_dc(false);
rights.set_executable(true);
rights.set_desc_type_raw(DescriptorType::Code as u8);
rights.set_dpl(0);
rights.set_granularity_raw(Granularity::KByte as u8);
rights.set_long(true);
rights.set_db(false);
rights
};
vmwrite(vmcs::guest::CS_ACCESS_RIGHTS, cs_rights.0 as u64)?;
let ds_rights = {
let mut rights = SegmentRights(0);
rights.set_rw(true);
rights.set_dc(false);
rights.set_executable(false);
rights.set_desc_type_raw(DescriptorType::Code as u8);
rights.set_dpl(0);
rights.set_granularity_raw(Granularity::KByte as u8);
rights.set_long(false);
rights.set_db(true);
rights
};
vmwrite(vmcs::guest::DS_ACCESS_RIGHTS, ds_rights.0 as u64)?;
let tr_rights = {
let mut rights = SegmentRights(0);
rights.set_rw(true);
rights.set_dc(false);
rights.set_executable(true);
rights.set_desc_type_raw(DescriptorType::System as u8);
rights.set_dpl(0);
rights.set_granularity_raw(Granularity::Byte as u8);
rights.set_long(false);
rights.set_db(false);
rights
};
vmwrite(vmcs::guest::TR_ACCESS_RIGHTS, tr_rights.0 as u64)?;
let ldtr_rights = {
let mut rights = SegmentRights(0);
rights.set_accessed(false);
rights.set_rw(true);
rights.set_dc(false);
rights.set_executable(false);
rights.set_desc_type_raw(DescriptorType::System as u8);
rights.set_dpl(0);
rights.set_granularity_raw(Granularity::Byte as u8);
rights.set_long(false);
rights.set_db(false);
rights
};
vmwrite(vmcs::guest::LDTR_ACCESS_RIGHTS, ldtr_rights.0 as u64)?;
vmwrite(vmcs::guest::RIP, Self::guest as u64)?;
vmwrite(vmcs::guest::IA32_EFER_FULL, rdmsr(IA32_EFER))?;
vmwrite(vmcs::guest::RFLAGS, 0x2)?;
vmwrite(vmcs::guest::LINK_PTR_FULL, u64::MAX)?;
}
Ok(())
}
pub fn reset_vmcs(&mut self) -> Result<(), VmFail> {
info!("Resetting VMCS");
self.vmcs.reset()
}
#[naked]
unsafe extern "C" fn guest() -> ! {
naked_asm!("hlt");
}
fn vmexit_handler(&mut self) -> ! {
info!("VMExit occurred");
loop {
unsafe { halt() };
}
}
#[naked]
unsafe extern "C" fn vmexit(&mut self) -> ! {
naked_asm!("call {vmexit_handler}", vmexit_handler = sym Self::vmexit_handler);
}
}