This commit is contained in:
Masato Imai
2025-04-20 18:11:27 +00:00
parent c262761fbb
commit 6611e96e3f
9 changed files with 364 additions and 13 deletions

56
Cargo.lock generated
View File

@ -14,6 +14,26 @@ version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
[[package]]
name = "bitfield"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "786e53b0c071573a28956cec19a92653e42de34c683e2f6e86c197a349fba318"
dependencies = [
"bitfield-macros",
]
[[package]]
name = "bitfield-macros"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07805405d3f1f3a55aab895718b488821d40458f9188059909091ae0935c344a"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "bitflags"
version = "1.3.2"
@ -64,6 +84,7 @@ dependencies = [
name = "nel_os"
version = "0.1.0"
dependencies = [
"bitfield",
"bootloader",
"lazy_static",
"linked_list_allocator",
@ -91,6 +112,24 @@ dependencies = [
"x86_64 0.15.2",
]
[[package]]
name = "proc-macro2"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[package]]
name = "raw-cpuid"
version = "10.7.0"
@ -133,6 +172,17 @@ dependencies = [
"lock_api",
]
[[package]]
name = "syn"
version = "2.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "uart_16550"
version = "0.2.19"
@ -144,6 +194,12 @@ dependencies = [
"x86_64 0.14.13",
]
[[package]]
name = "unicode-ident"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "volatile"
version = "0.2.7"

View File

@ -13,6 +13,7 @@ pic8259 = "0.11.0"
pc-keyboard = "0.8.0"
linked_list_allocator = "0.9.0"
x86 = "0.52.0"
bitfield = "0.19.0"
[dependencies.lazy_static]
version = "1.0"
@ -29,12 +30,15 @@ run-args = [
"-enable-kvm"
]
test-args = [
"-device",
"isa-debug-exit,iobase=0xf4,iosize=0x04",
"-serial",
"stdio",
"-display",
"none",
"-device",
"isa-debug-exit,iobase=0xf4,iosize=0x04",
"-serial",
"stdio",
"-display",
"none",
"-cpu",
"host",
"-enable-kvm"
]
test-success-exit-code = 33
test-timeout = 300

View File

@ -6,7 +6,7 @@ use x86_64::{
VirtAddr,
};
use crate::println;
use crate::{info, println};
pub const HEAP_START: usize = 0x_4444_4444_0000;
pub const HEAP_SIZE: usize = 100 * 1024;
@ -18,7 +18,7 @@ pub fn init_heap(
mapper: &mut impl Mapper<Size4KiB>,
frame_allocator: &mut impl FrameAllocator<Size4KiB>,
) -> Result<(), MapToError<Size4KiB>> {
println!(
info!(
"Initializing heap: 0x{:x} - 0x{:x}",
HEAP_START,
HEAP_START + HEAP_SIZE - 1

View File

@ -14,6 +14,7 @@ lazy_static! {
.set_stack_index(gdt::DOUBLE_FAULT_IST_INDEX);
}
idt.page_fault.set_handler_fn(page_fault_handler);
idt.invalid_opcode.set_handler_fn(invalid_opcode_handler);
idt[InterruptIndex::Timer.as_usize()].set_handler_fn(timer_interrupt_handler);
idt[InterruptIndex::Keyboard.as_usize()].set_handler_fn(keyboard_interrupt_handler);
@ -37,6 +38,10 @@ extern "x86-interrupt" fn double_fault_handler(
panic!("EXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame);
}
extern "x86-interrupt" fn invalid_opcode_handler(stack_frame: InterruptStackFrame) {
panic!("EXCEPTION: INVALID OPCODE\n{:#?}", stack_frame);
}
use crate::hlt_loop;
use x86_64::structures::idt::PageFaultErrorCode;

View File

@ -1,20 +1,32 @@
#![no_std]
#![no_main]
#![feature(custom_test_frameworks)]
#![feature(new_zeroed_alloc)]
#![test_runner(nel_os::test_runner)]
#![reexport_test_harness_main = "test_main"]
extern crate alloc;
use alloc::boxed::Box;
use bootloader::{entry_point, BootInfo};
use core::arch::asm;
use core::panic::PanicInfo;
use nel_os::{
allocator,
allocator, info,
memory::{self, BootInfoFrameAllocator},
println,
vmm::support::{has_intel_cpu, has_vmx_support},
vmm::{
support::{has_intel_cpu, has_vmx_support},
vmxon::Vmxon,
},
};
use x86::bits64::{paging::BASE_PAGE_SIZE, rflags};
use x86_64::structures::paging::FrameAllocator;
use x86_64::{
registers::{control::Cr0Flags, segmentation::Segment},
structures::paging::Translate,
VirtAddr,
};
use x86_64::VirtAddr;
#[cfg(not(test))]
#[panic_handler]
@ -42,8 +54,62 @@ fn kernel_main(boot_info: &'static BootInfo) -> ! {
allocator::init_heap(&mut mapper, &mut frame_allocator).expect("heap initialization failed");
println!("has_intel_cpu: {}", has_intel_cpu());
println!("has_vmx_support: {}", has_vmx_support());
let mut vmxon = unsafe { Box::<Vmxon>::new_zeroed().assume_init() };
if has_intel_cpu() && has_vmx_support() {
info!("Intel CPU with VMX support detected");
} else {
panic!("VMX not supported");
}
vmxon.init();
vmxon.activate_vmxon(mapper).unwrap();
info!("Checking vmlaunch requirements...");
{
let mut success = true;
let cr0 = x86_64::registers::control::Cr0::read();
if cr0.contains(Cr0Flags::PROTECTED_MODE_ENABLE) {
info!("Protected mode is enabled");
} else {
info!("Protected mode is not enabled");
success = false;
}
let rflags = rflags::read();
if rflags.contains(rflags::RFlags::FLAGS_VM) {
info!("VM flag is enabled");
success = false;
} else {
info!("VM flag is not enabled");
}
let ia32_efer = unsafe { x86::msr::rdmsr(x86::msr::IA32_EFER) };
if (ia32_efer & 1 << 10) != 0 {
info!("IA32_EFER.LMA is enabled");
let cs = x86_64::registers::segmentation::CS::get_reg().0;
if cs & 0x1 == 0 {
info!("CS.L is enabled");
} else {
info!("CS.L is not enabled");
success = false;
}
} else {
info!("IA32_EFER.LMA is not enabled");
}
if success {
info!("vmlaunch requirements are met");
} else {
panic!("vmlaunch requirements are not met");
}
}
info!("vmlaunch...");
unsafe {
asm!("vmlaunch");
}
#[cfg(test)]
test_main();

View File

@ -25,6 +25,11 @@ macro_rules! println {
($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*)));
}
#[macro_export]
macro_rules! info {
($($arg:tt)*) => ($crate::print!("[info] {}\n", format_args!($($arg)*)));
}
#[doc(hidden)]
pub fn _print(args: fmt::Arguments) {
use core::fmt::Write;

1
src/vmm/error.rs Normal file
View File

@ -0,0 +1 @@
pub enum VMMError {}

View File

@ -1 +1,3 @@
pub mod error;
pub mod support;
pub mod vmxon;

212
src/vmm/vmxon.rs Normal file
View File

@ -0,0 +1,212 @@
use core::arch::asm;
use x86::bits64::rflags::{self, RFlags};
use x86::bits64::vmx;
use x86::controlregs::{cr4, Cr4};
use x86::vmx::{Result, VmFail};
use x86::{msr, msr::rdmsr};
use x86_64::registers::control::Cr0;
use x86_64::structures::paging::{OffsetPageTable, Translate};
use x86_64::VirtAddr;
use crate::info;
#[repr(C, align(4096))]
pub struct Vmxon {
pub revision_id: u32,
pub data: [u8; 4092],
}
impl Vmxon {
pub fn check_vmxon_requirements(&mut self, mapper: OffsetPageTable) -> bool {
info!("");
info!("Checking VMXON requirements...");
let cr4 = unsafe { x86::controlregs::cr4() };
info!("CR4: {:?}", cr4);
if !cr4.contains(Cr4::CR4_ENABLE_VMX) {
info!("VMX operation not enabled in CR4");
return false;
}
info!("VMX operation enabled in CR4");
let ia32_feature_control = unsafe { rdmsr(x86::msr::IA32_FEATURE_CONTROL) };
info!("IA32_FEATURE_CONTROL: {:#x}", ia32_feature_control);
if (ia32_feature_control & (1 << 0)) == 0 {
info!("VMX operation not enabled in IA32_FEATURE_CONTROL");
return false;
}
if (ia32_feature_control & (1 << 2)) == 0 {
info!("VMX operation not enabled outside of SMX");
return false;
}
info!("VMX operation enabled in IA32_FEATURE_CONTROL");
let ia32_vmx_cr0_fixed0 = unsafe { rdmsr(x86::msr::IA32_VMX_CR0_FIXED0) };
let ia32_vmx_cr0_fixed1 = unsafe { rdmsr(x86::msr::IA32_VMX_CR0_FIXED1) };
info!(
"IA32_VMX_CR0_FIXED0: {:#x}, IA32_VMX_CR0_FIXED1: {:#x}",
ia32_vmx_cr0_fixed0, ia32_vmx_cr0_fixed1
);
let cr0 = unsafe { Cr0::read_raw() };
info!("CR0: {:#x}", cr0);
if (cr0 & ia32_vmx_cr0_fixed0) != ia32_vmx_cr0_fixed0 {
info!("CR0 does not meet VMX requirements");
return false;
}
if (cr0 & !ia32_vmx_cr0_fixed1) != 0 {
info!("CR0 does not meet VMX requirements");
return false;
}
info!("CR0 meets VMX requirements");
let ia32_vmx_cr4_fixed0 = unsafe { rdmsr(x86::msr::IA32_VMX_CR4_FIXED0) };
let ia32_vmx_cr4_fixed1 = unsafe { rdmsr(x86::msr::IA32_VMX_CR4_FIXED1) };
info!(
"IA32_VMX_CR4_FIXED0: {:#x}, IA32_VMX_CR4_FIXED1: {:#x}",
ia32_vmx_cr4_fixed0, ia32_vmx_cr4_fixed1
);
let cr4 = unsafe { x86::controlregs::cr4().bits() as u64 };
info!("CR4: {:#x}", cr4);
if (cr4 & ia32_vmx_cr4_fixed0) != ia32_vmx_cr4_fixed0 {
info!("CR4 does not meet VMX requirements");
return false;
}
if (cr4 & !ia32_vmx_cr4_fixed1) != 0 {
info!("CR4 does not meet VMX requirements");
return false;
}
info!("CR4 meets VMX requirements");
// check self data(VMXON region) is aligned to 4K
let vmxon_region = mapper
.translate_addr(VirtAddr::from_ptr(&self))
.unwrap()
.as_u64();
info!("VMXON region: {:#x}", vmxon_region);
if vmxon_region & 0xFFF != 0 {
info!("VMXON region is not aligned to 4K");
return false;
}
info!("VMXON region is aligned to 4K");
true
}
pub fn zeroed() -> Self {
Vmxon {
revision_id: 0,
data: [0; 4092],
}
}
pub fn init(&mut self) {
self.revision_id = unsafe { rdmsr(x86::msr::IA32_VMX_BASIC) } as u32;
}
pub fn enable_vmx_operation() {
unsafe {
x86::controlregs::cr4_write(cr4() | Cr4::CR4_ENABLE_VMX);
}
}
pub fn adjust_feature_control_msr() -> core::result::Result<(), ()> {
const VMX_LOCK_BIT: u64 = 1 << 0;
const VMXON_OUTSIDE_SMX: u64 = 1 << 2;
let ia32_feature_control = unsafe { x86::msr::rdmsr(x86::msr::IA32_FEATURE_CONTROL) };
if (ia32_feature_control & VMX_LOCK_BIT) == 0 {
unsafe {
x86::msr::wrmsr(
x86::msr::IA32_FEATURE_CONTROL,
ia32_feature_control | VMXON_OUTSIDE_SMX | VMX_LOCK_BIT,
);
}
}
Ok(())
}
pub fn set_cr0_bits() {
let ia32_vmx_cr0_fixed0 = unsafe { msr::rdmsr(msr::IA32_VMX_CR0_FIXED0) };
let ia32_vmx_cr0_fixed1 = unsafe { msr::rdmsr(msr::IA32_VMX_CR0_FIXED1) };
let mut cr0 = Cr0::read_raw();
cr0 |= ia32_vmx_cr0_fixed0;
cr0 &= ia32_vmx_cr0_fixed1;
unsafe { Cr0::write_raw(cr0) };
}
/*pub fn set_cr4_bits() {
let ia32_vmx_cr4_fixed0 = unsafe { x86::msr::rdmsr(x86::msr::IA32_VMX_CR4_FIXED0) };
let ia32_vmx_cr4_fixed1 = unsafe { x86::msr::rdmsr(x86::msr::IA32_VMX_CR4_FIXED1) };
let mut cr4 = Cr4::read_raw();
cr4 |= ia32_vmx_cr4_fixed0;
cr4 &= ia32_vmx_cr4_fixed1;
unsafe {
Cr4::write_raw(cr4);
}
}*/
fn vmx_capture_status() -> Result<()> {
let flags = rflags::read();
info!("RFlags: {:?}", flags);
if flags.contains(RFlags::FLAGS_ZF) {
Err(VmFail::VmFailValid)
} else if flags.contains(RFlags::FLAGS_CF) {
Err(VmFail::VmFailInvalid)
} else {
Ok(())
}
}
pub unsafe fn vmxon(&mut self) {
//asm!("vmxon ({0})", in(reg) addr, options(att_syntax));
x86::bits64::vmx::vmxon(core::ptr::from_mut(self) as u64).unwrap()
}
pub fn activate_vmxon(&mut self, mapper: OffsetPageTable) -> core::result::Result<(), ()> {
info!("activating vmxon...");
self.setup_vmxon()?;
info!("VMXON region at virtual address: {:p}", &self.data);
let phys_addr = mapper
.translate_addr(VirtAddr::from_ptr(&self))
.unwrap()
.as_u64();
info!("VMXON region at physical address: {:#x}", phys_addr);
info!("VMXON revision ID: {:#x}", self.revision_id);
if self.check_vmxon_requirements(mapper) {
info!("VMXON requirements met");
} else {
panic!("VMXON requirements not met");
}
unsafe {
vmx::vmxon(phys_addr).unwrap();
//self.vmxon();
};
info!("vmxon success");
Ok(())
}
fn setup_vmxon(&mut self) -> core::result::Result<(), ()> {
Vmxon::enable_vmx_operation();
info!("VMX operation enabled");
Vmxon::adjust_feature_control_msr()?;
info!("Feature control MSR adjusted");
Vmxon::set_cr0_bits();
//Vmxon::set_cr4_bits();
info!("CR0 and CR4 bits set");
Ok(())
}
}