diff --git a/nel_os_kernel/Cargo.lock b/nel_os_kernel/Cargo.lock index 6c5b737..fc56691 100644 --- a/nel_os_kernel/Cargo.lock +++ b/nel_os_kernel/Cargo.lock @@ -145,6 +145,7 @@ dependencies = [ "raw-cpuid 11.5.0", "spin 0.10.0", "uart_16550", + "x86", "x86_64", ] diff --git a/nel_os_kernel/Cargo.toml b/nel_os_kernel/Cargo.toml index 9774775..60cac4e 100644 --- a/nel_os_kernel/Cargo.toml +++ b/nel_os_kernel/Cargo.toml @@ -15,3 +15,4 @@ raw-cpuid = "11.5.0" acpi = "5.2.0" modular-bitfield = "0.12.0" numeric-enum-macro = "0.2.0" +x86 = "0.52.0" diff --git a/nel_os_kernel/src/vmm/mod.rs b/nel_os_kernel/src/vmm/mod.rs index 2365368..1b15462 100644 --- a/nel_os_kernel/src/vmm/mod.rs +++ b/nel_os_kernel/src/vmm/mod.rs @@ -3,7 +3,7 @@ use alloc::boxed::Box; use crate::{ platform, - vmm::x86_64::{amd::AMDVCpu, intel::IntelVCpu}, + vmm::x86_64::{amd::vcpu::AMDVCpu, intel::vcpu::IntelVCpu}, }; pub mod x86_64; diff --git a/nel_os_kernel/src/vmm/x86_64/amd/mod.rs b/nel_os_kernel/src/vmm/x86_64/amd/mod.rs index fbc1efb..b088157 100644 --- a/nel_os_kernel/src/vmm/x86_64/amd/mod.rs +++ b/nel_os_kernel/src/vmm/x86_64/amd/mod.rs @@ -1,45 +1 @@ -use raw_cpuid::cpuid; -use x86_64::structures::paging::{FrameAllocator, Size4KiB}; - -use crate::{ - error, info, - vmm::{x86_64::common, VCpu}, -}; - -pub struct AMDVCpu; - -impl VCpu for AMDVCpu { - fn run(&mut self) -> Result<(), &'static str> { - info!("VCpu on AMD"); - - Ok(()) - } - - fn new(_frame_allocator: &mut impl FrameAllocator) -> Result - where - Self: Sized, - { - let mut efer = common::read_msr(0xc000_0080); - efer |= 1 << 12; - common::write_msr(0xc000_0080, efer); - - Ok(AMDVCpu) - } - - fn is_supported() -> bool - where - Self: Sized, - { - if cpuid!(0x8000_0001).ecx & (1 << 2) == 0 { - error!("SVM not supported by CPU"); - return false; - } - - if common::read_msr(0xc001_0114) & (1 << 4) != 0 { - error!("SVM disabled by BIOS"); - return false; - } - - true - } -} +pub mod vcpu; diff --git a/nel_os_kernel/src/vmm/x86_64/amd/vcpu.rs b/nel_os_kernel/src/vmm/x86_64/amd/vcpu.rs new file mode 100644 index 0000000..fbc1efb --- /dev/null +++ b/nel_os_kernel/src/vmm/x86_64/amd/vcpu.rs @@ -0,0 +1,45 @@ +use raw_cpuid::cpuid; +use x86_64::structures::paging::{FrameAllocator, Size4KiB}; + +use crate::{ + error, info, + vmm::{x86_64::common, VCpu}, +}; + +pub struct AMDVCpu; + +impl VCpu for AMDVCpu { + fn run(&mut self) -> Result<(), &'static str> { + info!("VCpu on AMD"); + + Ok(()) + } + + fn new(_frame_allocator: &mut impl FrameAllocator) -> Result + where + Self: Sized, + { + let mut efer = common::read_msr(0xc000_0080); + efer |= 1 << 12; + common::write_msr(0xc000_0080, efer); + + Ok(AMDVCpu) + } + + fn is_supported() -> bool + where + Self: Sized, + { + if cpuid!(0x8000_0001).ecx & (1 << 2) == 0 { + error!("SVM not supported by CPU"); + return false; + } + + if common::read_msr(0xc001_0114) & (1 << 4) != 0 { + error!("SVM disabled by BIOS"); + return false; + } + + true + } +} diff --git a/nel_os_kernel/src/vmm/x86_64/intel/mod.rs b/nel_os_kernel/src/vmm/x86_64/intel/mod.rs index eaf046e..764ef13 100644 --- a/nel_os_kernel/src/vmm/x86_64/intel/mod.rs +++ b/nel_os_kernel/src/vmm/x86_64/intel/mod.rs @@ -1,97 +1,11 @@ mod controls; +pub mod vcpu; mod vmcs; mod vmxon; use core::arch::asm; -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 { - activated: bool, - vmxon: vmxon::Vmxon, - vmcs: vmcs::Vmcs, -} - -impl IntelVCpu { - fn activate(&mut self) -> Result<(), &'static str> { - let revision_id = common::read_msr(0x480) as u32; - self.vmcs.write_revision_id(revision_id); - self.vmcs.reset()?; - controls::setup_exec_controls()?; - controls::setup_entry_controls()?; - controls::setup_exit_controls()?; - - Ok(()) - } -} - -impl VCpu for IntelVCpu { - fn run(&mut self) -> Result<(), &'static str> { - info!("VCpu on Intel"); - - if !self.activated { - self.activate()?; - self.activated = true; - } - - Ok(()) - } - - fn new(frame_allocator: &mut impl FrameAllocator) -> Result - where - Self: Sized, - { - let mut msr = common::read_msr(0x3a); - if msr & (1 << 2) == 0 { - msr |= 1 << 2; - msr |= 1; - common::write_msr(0x3a, msr); - } - - let msr = common::read_msr(0x3a); - if msr & (1 << 2) == 0 { - return Err("VMX is not enabled in the BIOS"); - } - - let mut vmxon = vmxon::Vmxon::new(frame_allocator)?; - - vmxon.activate()?; - - let vmcs = vmcs::Vmcs::new(frame_allocator)?; - - Ok(IntelVCpu { - activated: false, - vmxon, - vmcs, - }) - } - - fn is_supported() -> bool - where - Self: Sized, - { - if cpuid!(0x1).ecx & (1 << 5) == 0 { - info!("Intel CPU does not support VMX"); - return false; - } - - let msr = common::read_msr(0x3a); - if msr & (1 << 2) == 0 && msr & 1 != 0 { - info!("VMX is not enabled in the BIOS"); - return false; - } - true - } -} +use x86_64::registers::rflags::{self, RFlags}; pub fn vmx_capture_status() -> Result<(), &'static str> { let flags = rflags::read(); diff --git a/nel_os_kernel/src/vmm/x86_64/intel/vcpu.rs b/nel_os_kernel/src/vmm/x86_64/intel/vcpu.rs new file mode 100644 index 0000000..6f9f57b --- /dev/null +++ b/nel_os_kernel/src/vmm/x86_64/intel/vcpu.rs @@ -0,0 +1,135 @@ +use raw_cpuid::cpuid; +use x86_64::{ + registers::control::Cr4Flags, + structures::paging::{FrameAllocator, Size4KiB}, +}; + +use crate::{ + info, + vmm::{ + x86_64::{ + common::{self, read_msr}, + intel::{controls, vmcs, vmwrite, vmxon}, + }, + VCpu, + }, +}; + +pub struct IntelVCpu { + activated: bool, + vmxon: vmxon::Vmxon, + vmcs: vmcs::Vmcs, +} + +impl IntelVCpu { + fn activate(&mut self) -> Result<(), &'static str> { + let revision_id = common::read_msr(0x480) as u32; + self.vmcs.write_revision_id(revision_id); + self.vmcs.reset()?; + controls::setup_exec_controls()?; + controls::setup_entry_controls()?; + controls::setup_exit_controls()?; + Self::setup_host_state()?; + + Ok(()) + } + + fn setup_host_state() -> Result<(), &'static str> { + use x86::{ + controlregs::*, dtables, dtables::DescriptorTablePointer, segmentation::*, vmx::vmcs, + }; + vmwrite(vmcs::host::CR0, unsafe { cr0() }.bits() as u64)?; + vmwrite(vmcs::host::CR3, unsafe { cr3() })?; + vmwrite( + vmcs::host::CR4, + unsafe { cr4() }.bits() as u64 | Cr4Flags::OSXSAVE.bits(), + )?; + + // TODO: set RIP to VMExit handler and stack + + vmwrite(vmcs::host::ES_SELECTOR, es().bits() as u64)?; + vmwrite(vmcs::host::CS_SELECTOR, cs().bits() as u64)?; + vmwrite(vmcs::host::SS_SELECTOR, ss().bits() as u64)?; + vmwrite(vmcs::host::DS_SELECTOR, ds().bits() as u64)?; + vmwrite(vmcs::host::FS_SELECTOR, fs().bits() as u64)?; + vmwrite(vmcs::host::GS_SELECTOR, gs().bits() as u64)?; + + vmwrite(vmcs::host::FS_BASE, read_msr(x86::msr::IA32_FS_BASE))?; + vmwrite(vmcs::host::GS_BASE, read_msr(x86::msr::IA32_GS_BASE))?; + + let tr = unsafe { x86::task::tr() }; + let mut gdtp = DescriptorTablePointer::::default(); + let mut idtp = DescriptorTablePointer::::default(); + unsafe { + 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, read_msr(x86::msr::IA32_EFER))?; + + Ok(()) + } +} + +impl VCpu for IntelVCpu { + fn run(&mut self) -> Result<(), &'static str> { + info!("VCpu on Intel"); + + if !self.activated { + self.activate()?; + self.activated = true; + } + + Ok(()) + } + + fn new(frame_allocator: &mut impl FrameAllocator) -> Result + where + Self: Sized, + { + let mut msr = common::read_msr(0x3a); + if msr & (1 << 2) == 0 { + msr |= 1 << 2; + msr |= 1; + common::write_msr(0x3a, msr); + } + + let msr = common::read_msr(0x3a); + if msr & (1 << 2) == 0 { + return Err("VMX is not enabled in the BIOS"); + } + + let mut vmxon = vmxon::Vmxon::new(frame_allocator)?; + + vmxon.activate()?; + + let vmcs = vmcs::Vmcs::new(frame_allocator)?; + + Ok(IntelVCpu { + activated: false, + vmxon, + vmcs, + }) + } + + fn is_supported() -> bool + where + Self: Sized, + { + if cpuid!(0x1).ecx & (1 << 5) == 0 { + info!("Intel CPU does not support VMX"); + return false; + } + + let msr = common::read_msr(0x3a); + if msr & (1 << 2) == 0 && msr & 1 != 0 { + info!("VMX is not enabled in the BIOS"); + return false; + } + true + } +}