Skip to content

Commit

Permalink
Step 1: enable VMX
Browse files Browse the repository at this point in the history
  • Loading branch information
equation314 committed Dec 17, 2022
1 parent 4afc124 commit cb87672
Show file tree
Hide file tree
Showing 19 changed files with 556 additions and 13 deletions.
14 changes: 14 additions & 0 deletions hypervisor/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions hypervisor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,13 @@ bitflags = "1.3"
buddy_system_allocator = "0.8"
lazy_static = { version = "1.4.0", features = ["spin_no_std"] }
bitmap-allocator = { git = "https://github.com/rcore-os/bitmap-allocator", rev = "88e871a" }
rvm = { path = "../rvm" }

[target.'cfg(target_arch = "x86_64")'.dependencies]
x86 = "0.52"
x86_64 = "0.14"
x2apic = "0.4"
raw-cpuid = "10.6"

[profile.release]
lto = true
2 changes: 1 addition & 1 deletion hypervisor/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ GDB := gdb-multiarch
qemu := qemu-system-$(ARCH)
qemu_args := -nographic -m 128M

qemu_args += -cpu host,+x2apic -accel kvm
qemu_args += -cpu host,+x2apic,+vmx -accel kvm

ifeq ($(ARCH), x86_64)
qemu_args += \
Expand Down
23 changes: 23 additions & 0 deletions hypervisor/src/hv/hal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use rvm::{HostPhysAddr, HostVirtAddr, RvmHal};

use crate::mm::{address, frame};

pub struct RvmHalImpl;

impl RvmHal for RvmHalImpl {
fn alloc_page() -> Option<HostPhysAddr> {
unsafe { frame::alloc_page() }
}

fn dealloc_page(paddr: HostPhysAddr) {
unsafe { frame::dealloc_page(paddr) }
}

fn phys_to_virt(paddr: HostPhysAddr) -> HostVirtAddr {
address::phys_to_virt(paddr)
}

fn virt_to_phys(vaddr: HostVirtAddr) -> HostPhysAddr {
address::virt_to_phys(vaddr)
}
}
14 changes: 14 additions & 0 deletions hypervisor/src/hv/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
mod hal;

use rvm::RvmPerCpu;

use self::hal::RvmHalImpl;

pub fn run() {
println!("Starting virtualization...");
println!("Hardware support: {:?}", rvm::has_hardware_support());

let mut percpu = RvmPerCpu::<RvmHalImpl>::new(0);
let res = percpu.hardware_enable();
println!("Hardware enable: {:?}", res);
}
4 changes: 4 additions & 0 deletions hypervisor/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ mod logging;

mod arch;
mod config;
mod hv;
mod mm;
mod timer;

Expand Down Expand Up @@ -74,6 +75,9 @@ fn main() -> ! {
INIT_OK.store(true, Ordering::SeqCst);
println!("Initialization completed.\n");

hv::run();
println!("Run OK!");

arch::instructions::enable_irqs();
loop {
arch::instructions::wait_for_ints();
Expand Down
2 changes: 0 additions & 2 deletions hypervisor/src/mm/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
#![allow(dead_code)]

mod heap;

pub mod address;
Expand Down
7 changes: 0 additions & 7 deletions rvm/Cargo.lock

This file was deleted.

13 changes: 13 additions & 0 deletions rvm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,17 @@ authors = ["Yuekai Jia <[email protected]>"]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[features]
default = ["vmx"]
vmx = []

[dependencies]
log = "0.4"
cfg-if = "1.0"
bitflags = "1.3"
bit_field = "0.10"

[target.'cfg(target_arch = "x86_64")'.dependencies]
x86 = "0.52"
x86_64 = "0.14"
raw-cpuid = "10.6"
8 changes: 8 additions & 0 deletions rvm/src/arch/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//! Architecture dependent structures.
cfg_if::cfg_if! {
if #[cfg(target_arch = "x86_64")] {
mod x86_64;
pub use self::x86_64::*;
}
}
10 changes: 10 additions & 0 deletions rvm/src/arch/x86_64/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
pub(crate) mod msr;

cfg_if::cfg_if! {
if #[cfg(feature = "vmx")] {
mod vmx;
use vmx as vender;
}
}

pub use vender::{has_hardware_support, ArchPerCpuState};
46 changes: 46 additions & 0 deletions rvm/src/arch/x86_64/msr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use x86::msr::{rdmsr, wrmsr};

/// X86 model-specific registers. (SDM Vol. 4)
#[repr(u32)]
#[derive(Debug, Copy, Clone)]
#[allow(non_camel_case_types)]
pub enum Msr {
IA32_FEATURE_CONTROL = 0x3a,
IA32_VMX_BASIC = 0x480,

IA32_VMX_CR0_FIXED0 = 0x486,
IA32_VMX_CR0_FIXED1 = 0x487,
IA32_VMX_CR4_FIXED0 = 0x488,
IA32_VMX_CR4_FIXED1 = 0x489,
}

impl Msr {
/// Read 64 bits msr register.
#[inline(always)]
pub fn read(self) -> u64 {
unsafe { rdmsr(self as _) }
}

/// Write 64 bits to msr register.
///
/// # Safety
///
/// The caller must ensure that this write operation has no unsafe side
/// effects.
#[inline(always)]
pub unsafe fn write(self, value: u64) {
wrmsr(self as _, value)
}
}

pub(super) trait MsrReadWrite {
const MSR: Msr;

fn read_raw() -> u64 {
Self::MSR.read()
}

unsafe fn write_raw(flags: u64) {
Self::MSR.write(flags);
}
}
129 changes: 129 additions & 0 deletions rvm/src/arch/x86_64/vmx/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
mod structs;

use raw_cpuid::CpuId;
use x86::{bits64::vmx, vmx::VmFail};
use x86_64::registers::control::{Cr0, Cr4, Cr4Flags};

use self::structs::{FeatureControl, FeatureControlFlags, VmxBasic, VmxRegion};
use crate::arch::msr::Msr;
use crate::error::{RvmError, RvmResult};
use crate::hal::RvmHal;

pub use self::VmxPerCpuState as ArchPerCpuState;

pub fn has_hardware_support() -> bool {
if let Some(feature) = CpuId::new().get_feature_info() {
feature.has_vmx()
} else {
false
}
}

pub struct VmxPerCpuState<H: RvmHal> {
vmcs_revision_id: u32,
vmx_region: VmxRegion<H>,
}

impl<H: RvmHal> VmxPerCpuState<H> {
pub const fn new() -> Self {
Self {
vmcs_revision_id: 0,
vmx_region: unsafe { VmxRegion::uninit() },
}
}

pub fn is_enabled(&self) -> bool {
Cr4::read().contains(Cr4Flags::VIRTUAL_MACHINE_EXTENSIONS)
}

pub fn hardware_enable(&mut self) -> RvmResult {
if !has_hardware_support() {
return rvm_err!(Unsupported, "CPU does not support feature VMX");
}
if self.is_enabled() {
return rvm_err!(ResourceBusy, "VMX is already turned on");
}

// Enable VMXON, if required.
let ctrl = FeatureControl::read();
let locked = ctrl.contains(FeatureControlFlags::LOCKED);
let vmxon_outside = ctrl.contains(FeatureControlFlags::VMXON_ENABLED_OUTSIDE_SMX);
if !locked {
FeatureControl::write(
ctrl | FeatureControlFlags::LOCKED | FeatureControlFlags::VMXON_ENABLED_OUTSIDE_SMX,
)
} else if !vmxon_outside {
return rvm_err!(Unsupported, "VMX disabled by BIOS");
}

// Check control registers are in a VMX-friendly state. (SDM Vol. 3C, Appendix A.7, A.8)
macro_rules! cr_is_valid {
($value: expr, $crx: ident) => {{
use Msr::*;
let value = $value;
let fixed0 = concat_idents!(IA32_VMX_, $crx, _FIXED0).read();
let fixed1 = concat_idents!(IA32_VMX_, $crx, _FIXED1).read();
(!fixed0 | value != 0) && (fixed1 | !value != 0)
}};
}
if !cr_is_valid!(Cr0::read().bits(), CR0) {
return rvm_err!(BadState, "host CR0 is not valid in VMX operation");
}
if !cr_is_valid!(Cr4::read().bits(), CR4) {
return rvm_err!(BadState, "host CR4 is not valid in VMX operation");
}

// Get VMCS revision identifier in IA32_VMX_BASIC MSR.
let vmx_basic = VmxBasic::read();
if vmx_basic.region_size as usize != crate::mm::PAGE_SIZE {
return rvm_err!(Unsupported);
}
if vmx_basic.mem_type != VmxBasic::VMX_MEMORY_TYPE_WRITE_BACK {
return rvm_err!(Unsupported);
}
if vmx_basic.is_32bit_address {
return rvm_err!(Unsupported);
}
if !vmx_basic.io_exit_info {
return rvm_err!(Unsupported);
}
if !vmx_basic.vmx_flex_controls {
return rvm_err!(Unsupported);
}
self.vmcs_revision_id = vmx_basic.revision_id;
self.vmx_region = VmxRegion::new(vmx_basic.revision_id, false)?;

unsafe {
// Enable VMX using the VMXE bit.
Cr4::write(Cr4::read() | Cr4Flags::VIRTUAL_MACHINE_EXTENSIONS);
// Execute VMXON.
vmx::vmxon(self.vmx_region.phys_addr() as _)?;
}
info!("[RVM] successed to turn on VMX.");

Ok(())
}

pub fn hardware_disable(&mut self) -> RvmResult {
if !self.is_enabled() {
return rvm_err!(BadState, "VMX is not enabled");
}

unsafe {
// Execute VMXOFF.
vmx::vmxoff()?;
// Remove VMXE bit in CR4.
Cr4::update(|cr4| cr4.remove(Cr4Flags::VIRTUAL_MACHINE_EXTENSIONS));
};
info!("[RVM] successed to turn off VMX.");

self.vmx_region = unsafe { VmxRegion::uninit() };
Ok(())
}
}

impl From<VmFail> for RvmError {
fn from(err: VmFail) -> Self {
rvm_err_type!(BadState, format_args!("VMX instruction failed: {:?}", err))
}
}
Loading

0 comments on commit cb87672

Please sign in to comment.