vmxon
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
{
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user