From aec46d128da371d9c1cf5bf263196e0949106413 Mon Sep 17 00:00:00 2001 From: Masato Imai Date: Wed, 23 Apr 2025 10:13:23 +0000 Subject: [PATCH] vmentry, vm loop --- src/main.rs | 26 +---- src/vmm/mod.rs | 1 + src/vmm/register.rs | 33 +++++++ src/vmm/vcpu.rs | 235 +++++++++++++++++++++++++++++++++++++++----- 4 files changed, 249 insertions(+), 46 deletions(-) create mode 100644 src/vmm/register.rs diff --git a/src/main.rs b/src/main.rs index 0f74684..d3ec8f7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,11 +5,11 @@ #![feature(naked_functions)] #![test_runner(nel_os::test_runner)] #![reexport_test_harness_main = "test_main"] +#![allow(unreachable_code)] extern crate alloc; use bootloader::{entry_point, BootInfo}; -use core::arch::asm; use core::panic::PanicInfo; use nel_os::{ allocator, info, @@ -18,13 +18,8 @@ use nel_os::{ vmm::{ support::{has_intel_cpu, has_vmx_support}, vcpu::VCpu, - vmcs::InstructionError, }, }; -use x86::{ - bits64::vmx::{self, vmread}, - vmx::vmcs, -}; use x86_64::VirtAddr; #[cfg(not(test))] @@ -62,23 +57,8 @@ fn kernel_main(boot_info: &'static BootInfo) -> ! { let mut vcpu = VCpu::new(phys_mem_offset.as_u64(), &mut frame_allocator); vcpu.activate(); - info!("Starting the Virtual Machine..."); - - unsafe { - asm!("cli"); - - let vmlaunch = vmx::vmlaunch(); - - info!("VMLaunch: {:?}", vmlaunch); - - if vmlaunch.is_err() { - let error = InstructionError::read(); - let error = error.as_str(); - info!("error: {}", error); - } else { - info!("vmlaunch success"); - } - } + #[cfg(not(test))] + vcpu.vm_loop(); #[cfg(test)] test_main(); diff --git a/src/vmm/mod.rs b/src/vmm/mod.rs index 1b3cbab..68f9713 100644 --- a/src/vmm/mod.rs +++ b/src/vmm/mod.rs @@ -1,4 +1,5 @@ pub mod error; +pub mod register; pub mod support; pub mod vcpu; pub mod vmcs; diff --git a/src/vmm/register.rs b/src/vmm/register.rs new file mode 100644 index 0000000..a961665 --- /dev/null +++ b/src/vmm/register.rs @@ -0,0 +1,33 @@ +#[repr(C)] +#[derive(Default, Debug)] +pub struct GuestRegisters { + pub rax: u64, + pub rcx: u64, + pub rdx: u64, + pub rbx: u64, + pub rbp: u64, + pub rsi: u64, + pub rdi: u64, + pub r8: u64, + pub r9: u64, + pub r10: u64, + pub r11: u64, + pub r12: u64, + pub r13: u64, + pub r14: u64, + pub r15: u64, + pub xmm0: M128, + pub xmm1: M128, + pub xmm2: M128, + pub xmm3: M128, + pub xmm4: M128, + pub xmm5: M128, + pub xmm6: M128, + pub xmm7: M128, +} + +#[repr(C, align(16))] +#[derive(Default, Debug)] +pub struct M128 { + pub data: u128, +} diff --git a/src/vmm/vcpu.rs b/src/vmm/vcpu.rs index 51360f7..52d50bd 100644 --- a/src/vmm/vcpu.rs +++ b/src/vmm/vcpu.rs @@ -1,26 +1,29 @@ use x86::{ - bits64::vmx::{vmread, vmwrite}, + bits64::vmx::vmwrite, controlregs::{cr0, cr3, cr4}, dtables::{self, DescriptorTablePointer}, - halt, msr::{rdmsr, IA32_EFER, IA32_FS_BASE}, vmx::{vmcs, VmFail}, }; use x86_64::VirtAddr; -use core::arch::{asm, naked_asm}; +use core::{ + arch::{asm, naked_asm}, + mem::offset_of, +}; use crate::{ info, memory::BootInfoFrameAllocator, vmm::vmcs::{ DescriptorType, EntryControls, Granularity, PrimaryExitControls, - PrimaryProcessorBasedVmExecutionControls, SegmentRights, VmxExitInfo, + PrimaryProcessorBasedVmExecutionControls, SegmentRights, VmxExitInfo, VmxExitReason, }, }; use super::{ - vmcs::{PinBasedVmExecutionControls, Vmcs}, + register::GuestRegisters, + vmcs::{InstructionError, PinBasedVmExecutionControls, Vmcs}, vmxon::Vmxon, }; @@ -28,6 +31,8 @@ pub struct VCpu { pub vmxon: Vmxon, pub vmcs: Vmcs, pub phys_mem_offset: u64, + pub guest_registers: GuestRegisters, + pub launch_done: bool, } const TEMP_STACK_SIZE: usize = 4096; @@ -42,6 +47,8 @@ impl VCpu { vmxon, vmcs, phys_mem_offset, + guest_registers: GuestRegisters::default(), + launch_done: false, } } @@ -85,7 +92,7 @@ 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(false); + primary_exec_ctrl.set_hlt(true); primary_exec_ctrl.set_activate_secondary_controls(false); primary_exec_ctrl.write(); @@ -316,17 +323,141 @@ impl VCpu { self.vmcs.reset() } + pub fn vm_loop(&mut self) -> ! { + info!("Entering VM loop"); + + loop { + if let Err(err) = self.vmentry() { + info!("VMEntry failed: {}", err.as_str()); + } + + self.vmexit_handler(); + } + } + + fn vmentry(&mut self) -> Result<(), InstructionError> { + let success = { + let result: u16; + unsafe { + asm!( + "mov {self}, rdi", + "call {asm_vm_entry}", + self = in(reg) self, + asm_vm_entry = sym Self::asm_vm_entry, + out("ax") result, + clobber_abi("C"), + ); + }; + result == 0 + }; + + self.launch_done = true; + + if success { + let error = InstructionError::read(); + if error.0 != 0 { + return Err(error); + } + } + + Ok(()) + } + + unsafe extern "C" fn set_host_stack(rsp: u64) { + vmwrite(vmcs::host::RSP, rsp).unwrap(); + } + + #[naked] + unsafe extern "C" fn asm_vm_entry() -> u16 { + const GUEST_REGS_OFFSET: usize = offset_of!(VCpu, guest_registers); + const LAUNCH_DONE: usize = offset_of!(VCpu, launch_done); + + const RAX_OFFSET: usize = offset_of!(GuestRegisters, rax); + const RCX_OFFSET: usize = offset_of!(GuestRegisters, rcx); + const RDX_OFFSET: usize = offset_of!(GuestRegisters, rdx); + const RBX_OFFSET: usize = offset_of!(GuestRegisters, rbx); + const RSI_OFFSET: usize = offset_of!(GuestRegisters, rsi); + const RDI_OFFSET: usize = offset_of!(GuestRegisters, rdi); + const RBP_OFFSET: usize = offset_of!(GuestRegisters, rbp); + const R8_OFFSET: usize = offset_of!(GuestRegisters, r8); + const R9_OFFSET: usize = offset_of!(GuestRegisters, r9); + const R10_OFFSET: usize = offset_of!(GuestRegisters, r10); + const R11_OFFSET: usize = offset_of!(GuestRegisters, r11); + const R12_OFFSET: usize = offset_of!(GuestRegisters, r12); + const R13_OFFSET: usize = offset_of!(GuestRegisters, r13); + const R14_OFFSET: usize = offset_of!(GuestRegisters, r14); + const R15_OFFSET: usize = offset_of!(GuestRegisters, r15); + + naked_asm!( + "push rbp", + "push r15", + "push r14", + "push r13", + "push r12", + "push rbx", + "lea rbx, [rdi + {0}]", + "push rbx", + "push rdi", + "lea rdi, [rsp + 8]", + "call {set_host_stack}", + "pop rdi", + "test byte ptr [rdi + {1}], 1", + "mov rax, rdi", + "mov rcx, [rax+{2}]", + "mov rdx, [rax+{3}]", + "mov rbx, [rax+{4}]", + "mov rsi, [rax+{5}]", + "mov rdi, [rax+{6}]", + "mov rbp, [rax+{7}]", + "mov r8, [rax+{8}]", + "mov r9, [rax+{9}]", + "mov r10, [rax+{10}]", + "mov r11, [rax+{11}]", + "mov r12, [rax+{12}]", + "mov r13, [rax+{13}]", + "mov r14, [rax+{14}]", + "mov r15, [rax+{15}]", + "mov rax, [rax+{16}]", + "jz 2f", + "vmresume", + "2:", + "vmlaunch", + "mov ax, 1", + "add rsp, 8", + "pop rbx", + "pop r12", + "pop r13", + "pop r14", + "pop r15", + "pop rbp", + "ret", + const GUEST_REGS_OFFSET, + const LAUNCH_DONE, + const RCX_OFFSET, + const RDX_OFFSET, + const RBX_OFFSET, + const RSI_OFFSET, + const RDI_OFFSET, + const RBP_OFFSET, + const R8_OFFSET, + const R9_OFFSET, + const R10_OFFSET, + const R11_OFFSET, + const R12_OFFSET, + const R13_OFFSET, + const R14_OFFSET, + const R15_OFFSET, + const RAX_OFFSET, + set_host_stack = sym Self::set_host_stack, + ); + } + #[naked] unsafe extern "C" fn guest() -> ! { naked_asm!("2: hlt; jmp 2b"); } - fn vmexit_handler(&mut self) -> ! { - info!("VMExit occurred"); - - let raw_info = unsafe { vmread(vmcs::ro::EXIT_REASON) }.unwrap(); - info!("VMExit reason: {:#b}", raw_info); - + fn vmexit_handler(&mut self) { let info = VmxExitInfo::read(); if info.entry_failure() { @@ -344,20 +475,78 @@ impl VCpu { _ => {} } } else { - info!( - " Reason: {:?} ({})", - info.get_reason().as_str(), - info.basic_reason() - ); - } - - loop { - unsafe { halt() }; + match info.get_reason() { + VmxExitReason::HLT => { + info!("HLT instruction executed"); + } + _ => { + panic!("VMExit reason: {:?}", info.get_reason()); + } + } } } #[naked] - unsafe extern "C" fn vmexit(&mut self) -> ! { - naked_asm!("call {vmexit_handler}", vmexit_handler = sym Self::vmexit_handler); + unsafe extern "C" fn vmexit() -> ! { + const RAX_OFFSET: usize = offset_of!(GuestRegisters, rax); + const RCX_OFFSET: usize = offset_of!(GuestRegisters, rcx); + const RDX_OFFSET: usize = offset_of!(GuestRegisters, rdx); + const RBX_OFFSET: usize = offset_of!(GuestRegisters, rbx); + const RSI_OFFSET: usize = offset_of!(GuestRegisters, rsi); + const RDI_OFFSET: usize = offset_of!(GuestRegisters, rdi); + const RBP_OFFSET: usize = offset_of!(GuestRegisters, rbp); + const R8_OFFSET: usize = offset_of!(GuestRegisters, r8); + const R9_OFFSET: usize = offset_of!(GuestRegisters, r9); + const R10_OFFSET: usize = offset_of!(GuestRegisters, r10); + const R11_OFFSET: usize = offset_of!(GuestRegisters, r11); + const R12_OFFSET: usize = offset_of!(GuestRegisters, r12); + const R13_OFFSET: usize = offset_of!(GuestRegisters, r13); + const R14_OFFSET: usize = offset_of!(GuestRegisters, r14); + const R15_OFFSET: usize = offset_of!(GuestRegisters, r15); + + naked_asm!( + "cli", + "push rax", + "mov rax, [rsp+8]", + "pop [rax+{0}]", + "add rsp, 8", + "mov [rax+{1}], rcx", + "mov [rax+{2}], rdx", + "mov [rax+{3}], rbx", + "mov [rax+{4}], rsi", + "mov [rax+{5}], rdi", + "mov [rax+{6}], rbp", + "mov [rax+{7}], r8", + "mov [rax+{8}], r9", + "mov [rax+{9}], r10", + "mov [rax+{10}], r11", + "mov [rax+{11}], r12", + "mov [rax+{12}], r13", + "mov [rax+{13}], r14", + "mov [rax+{14}], r15", + "pop rbx", + "pop r12", + "pop r13", + "pop r14", + "pop r15", + "pop rbp", + "mov rax, 1", + "ret", + const RAX_OFFSET, + const RCX_OFFSET, + const RDX_OFFSET, + const RBX_OFFSET, + const RSI_OFFSET, + const RDI_OFFSET, + const RBP_OFFSET, + const R8_OFFSET, + const R9_OFFSET, + const R10_OFFSET, + const R11_OFFSET, + const R12_OFFSET, + const R13_OFFSET, + const R14_OFFSET, + const R15_OFFSET, + ) } }