This commit is contained in:
Masato Imai
2025-08-04 09:00:33 +00:00
parent 0cf622d388
commit c6cf712cc0
5 changed files with 168 additions and 12 deletions

View File

@@ -157,8 +157,10 @@ pub extern "sysv64" fn main(boot_info: &nel_os_common::BootInfo) {
info!("Interrupts enabled");
let mut vcpu = vmm::get_vcpu().unwrap();
let mut vcpu = vmm::get_vcpu(&mut bitmap_table).unwrap();
vcpu.run();
info!("VCpu initialized");
hlt_loop();
}

View File

@@ -1,3 +1,4 @@
use ::x86_64::structures::paging::{FrameAllocator, Size4KiB};
use alloc::boxed::Box;
use crate::{
@@ -8,7 +9,7 @@ use crate::{
pub mod x86_64;
pub trait VCpu {
fn new() -> Result<Self, &'static str>
fn new(frame_allocator: &mut impl FrameAllocator<Size4KiB>) -> Result<Self, &'static str>
where
Self: Sized;
fn is_supported() -> bool
@@ -17,11 +18,13 @@ pub trait VCpu {
fn run(&mut self);
}
pub fn get_vcpu() -> Result<Box<dyn VCpu>, &'static str> {
pub fn get_vcpu(
frame_allocator: &mut impl FrameAllocator<Size4KiB>,
) -> Result<Box<dyn VCpu>, &'static str> {
if platform::is_amd() && AMDVCpu::is_supported() {
Ok(Box::new(AMDVCpu::new()?))
Ok(Box::new(AMDVCpu::new(frame_allocator)?))
} else if platform::is_intel() && IntelVCpu::is_supported() {
Ok(Box::new(IntelVCpu::new()?))
Ok(Box::new(IntelVCpu::new(frame_allocator)?))
} else {
Err("Unsupported CPU architecture")
}

View File

@@ -1,4 +1,5 @@
use raw_cpuid::cpuid;
use x86_64::structures::paging::{FrameAllocator, Size4KiB};
use crate::{
error, info,
@@ -12,7 +13,7 @@ impl VCpu for AMDVCpu {
info!("VCpu on AMD");
}
fn new() -> Result<Self, &'static str>
fn new(_frame_allocator: &mut impl FrameAllocator<Size4KiB>) -> Result<Self, &'static str>
where
Self: Sized,
{

View File

@@ -1,20 +1,26 @@
mod vmxon;
use raw_cpuid::cpuid;
use x86_64::{
registers::rflags::{self, RFlags},
structures::paging::{FrameAllocator, Size4KiB},
};
use crate::{
info,
vmm::{x86_64::common, VCpu},
};
pub struct IntelVCpu;
pub struct IntelVCpu {
vmxon: vmxon::Vmxon,
}
impl VCpu for IntelVCpu {
fn run(&mut self) {
info!("VCpu on Intel");
}
fn new() -> Result<Self, &'static str>
fn new(frame_allocator: &mut impl FrameAllocator<Size4KiB>) -> Result<Self, &'static str>
where
Self: Sized,
{
@@ -30,7 +36,11 @@ impl VCpu for IntelVCpu {
return Err("VMX is not enabled in the BIOS");
}
Ok(IntelVCpu)
let mut vmxon = vmxon::Vmxon::new(frame_allocator)?;
vmxon.activate()?;
Ok(IntelVCpu { vmxon })
}
fn is_supported() -> bool
@@ -50,3 +60,14 @@ impl VCpu for IntelVCpu {
true
}
}
pub fn vmx_capture_status() -> Result<(), &'static str> {
let flags = rflags::read();
if flags.contains(RFlags::ZERO_FLAG) {
Err("VM fail valid")
} else if flags.contains(RFlags::CARRY_FLAG) {
Err("VM fail invalid")
} else {
Ok(())
}
}

View File

@@ -1,8 +1,21 @@
use x86_64::structures::paging::{FrameAllocator, PhysFrame, Size4KiB};
use core::arch::asm;
use x86_64::{
registers::control::{Cr0, Cr4, Cr4Flags},
structures::paging::{FrameAllocator, PhysFrame, Size4KiB},
};
use crate::{
error, info,
vmm::x86_64::{
common::{read_msr, write_msr},
intel::vmx_capture_status,
},
};
#[repr(C, align(4096))]
pub struct Vmxon {
frame: PhysFrame,
pub frame: PhysFrame,
}
impl Vmxon {
@@ -11,6 +24,122 @@ impl Vmxon {
.allocate_frame()
.ok_or("Failed to allocate frame for VMXON")?;
Ok(Vmxon { frame })
let mut vmxon = Vmxon { frame };
vmxon.init();
Ok(vmxon)
}
fn init(&mut self) {
let revision_id = read_msr(0x480) as u32;
let vmxon_region = self.frame.start_address().as_u64();
unsafe {
core::ptr::write_volatile(vmxon_region as *mut u32, revision_id);
}
}
pub fn activate(&mut self) -> Result<(), &'static str> {
Self::setup_vmxon();
if !self.check_requirements() {
return Err("VMX requirements not met");
}
let vmxon_region = self.frame.start_address().as_u64();
unsafe {
asm!(
"vmxon ({})",
in(reg) &vmxon_region,
options(att_syntax)
);
vmx_capture_status()?;
}
Ok(())
}
fn setup_vmxon() {
Self::enable_vmx_operation();
Self::adjust_feature_control_msr();
Self::set_cr0_bits();
}
fn enable_vmx_operation() {
unsafe {
Cr4::write_raw(Cr4::read_raw() | Cr4Flags::VIRTUAL_MACHINE_EXTENSIONS.bits());
}
}
fn adjust_feature_control_msr() {
const VMX_LOCK_BIT: u64 = 1 << 0;
const VMXON_OUTSIDE_SMX: u64 = 1 << 2;
let ia32_feature_control = read_msr(0x3a);
if ia32_feature_control & VMX_LOCK_BIT == 0 {
write_msr(
0x3a,
ia32_feature_control | VMXON_OUTSIDE_SMX | VMX_LOCK_BIT,
);
}
}
fn set_cr0_bits() {
let ia32_vmx_cr0_fixed0 = read_msr(0x486);
let ia32_vmx_cr0_fixed1 = read_msr(0x487);
let mut cr0 = Cr0::read_raw();
cr0 |= ia32_vmx_cr0_fixed0;
cr0 &= ia32_vmx_cr0_fixed1;
unsafe { Cr0::write_raw(cr0) };
}
fn check_requirements(&mut self) -> bool {
let cr4 = Cr4::read();
if !cr4.contains(Cr4Flags::VIRTUAL_MACHINE_EXTENSIONS) {
error!("VMX is not enabled in CR4");
return false;
}
let ia32_feature_control = read_msr(0x3a);
if ia32_feature_control & (1 << 2) == 0 {
error!("VMX operation not enabled outside of SMX");
return false;
}
let ia32_vmx_cr0_fixed0 = read_msr(0x486);
let ia32_vmx_cr0_fixed1 = read_msr(0x487);
let cr0 = Cr0::read_raw();
if cr0 & ia32_vmx_cr0_fixed0 != ia32_vmx_cr0_fixed0 {
error!("CR0 does not meet VMX requirements");
return false;
}
if cr0 & !ia32_vmx_cr0_fixed1 != 0 {
error!("CR0 does not meet VMX requirements");
return false;
}
let ia32_vmx_cr4_fixed0 = read_msr(0x488);
let ia32_vmx_cr4_fixed1 = read_msr(0x489);
let cr4 = Cr4::read_raw();
if cr4 & ia32_vmx_cr4_fixed0 != ia32_vmx_cr4_fixed0 {
error!("CR4 does not meet VMX requirements");
return false;
}
if cr4 & !ia32_vmx_cr4_fixed1 != 0 {
error!("CR4 does not meet VMX requirements");
return false;
}
let vmxon_region = self.frame.start_address().as_u64();
if vmxon_region & 0xfff != 0 {
error!("VMXON region is not aligned to 4KiB");
return false;
}
true
}
}