mirror of
https://github.com/mii443/nel_os.git
synced 2025-08-22 16:15:38 +00:00
@ -6,7 +6,12 @@ use super::{vcpu::VCpu, vmcs::VmxLeaf};
|
||||
pub fn handle_cpuid_exit(vcpu: &mut VCpu) {
|
||||
let regs = &mut vcpu.guest_registers;
|
||||
|
||||
let vendor: &[u8; 12] = b"miHypervisor";
|
||||
let vendor: &[u8; 12] = if vcpu.emulate_amd {
|
||||
b"AuthenticAMD"
|
||||
} else {
|
||||
b"miHypervisor"
|
||||
};
|
||||
|
||||
let brand_string: &[u8; 48] = b"mii Hypervisor CPU on Intel VT-x \0";
|
||||
|
||||
let vendor = unsafe { core::mem::transmute::<&[u8; 12], &[u32; 3]>(vendor) };
|
||||
|
1
src/vmm/emulation/mod.rs
Normal file
1
src/vmm/emulation/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod opcode;
|
174
src/vmm/emulation/opcode.rs
Normal file
174
src/vmm/emulation/opcode.rs
Normal file
@ -0,0 +1,174 @@
|
||||
use crate::{
|
||||
info,
|
||||
vmm::{
|
||||
vcpu::VCpu,
|
||||
vmcs::{DescriptorType, EntryControls, Granularity, SegmentRights},
|
||||
},
|
||||
};
|
||||
use x86::current::vmx::{vmread, vmwrite};
|
||||
use x86::vmx::vmcs;
|
||||
|
||||
const OPCODE_TWO_BYTE_ESCAPE: u8 = 0x0F;
|
||||
const OPCODE_GROUP_7: u8 = 0x01;
|
||||
const OPCODE_CLAC: u8 = 0xCA;
|
||||
const OPCODE_STAC: u8 = 0xCB;
|
||||
const OPCODE_SYSCALL: u8 = 0x05;
|
||||
|
||||
const RFLAGS_AC_BIT: u64 = 1 << 18;
|
||||
|
||||
pub struct OpcodeEmulator {
|
||||
pub original_opcode: Option<[u8; 16]>,
|
||||
pub replaced_address: Option<u64>,
|
||||
}
|
||||
|
||||
impl OpcodeEmulator {
|
||||
pub fn new() -> Self {
|
||||
OpcodeEmulator {
|
||||
original_opcode: None,
|
||||
replaced_address: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn emulate_opcode(vcpu: &mut VCpu, instruction_bytes: [u8; 16], valid_bytes: u64) -> bool {
|
||||
if instruction_bytes[0] != OPCODE_TWO_BYTE_ESCAPE || valid_bytes < 2 {
|
||||
return false;
|
||||
}
|
||||
|
||||
match instruction_bytes[1] {
|
||||
OPCODE_SYSCALL => {
|
||||
return emulate_syscall(vcpu);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
match (instruction_bytes[1], instruction_bytes[2]) {
|
||||
(OPCODE_GROUP_7, OPCODE_CLAC) => emulate_clac(vcpu),
|
||||
(OPCODE_GROUP_7, OPCODE_STAC) => emulate_stac(vcpu),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn emulate_syscall(vcpu: &mut VCpu) -> bool {
|
||||
if !vcpu.emulate_amd {
|
||||
return false;
|
||||
}
|
||||
|
||||
let current_rip = unsafe { vmread(vmcs::guest::RIP).unwrap() };
|
||||
let return_address = current_rip + 2;
|
||||
|
||||
let rflags = unsafe { vmread(vmcs::guest::RFLAGS).unwrap() };
|
||||
|
||||
vcpu.guest_registers.rcx = return_address;
|
||||
vcpu.guest_registers.r11 = rflags;
|
||||
|
||||
let lstar = vcpu.guest_msr.find(0xc0000082).unwrap().data;
|
||||
let star = vcpu.guest_msr.find(0xc0000081).unwrap().data;
|
||||
let sfmask = vcpu.guest_msr.find(0xc0000084).unwrap().data;
|
||||
|
||||
let cs_selector = (star >> 32) as u16;
|
||||
let ss_selector = cs_selector + 8;
|
||||
|
||||
let cs_rights = {
|
||||
let mut rights = SegmentRights::default();
|
||||
rights.set_rw(true);
|
||||
rights.set_dc(false);
|
||||
rights.set_executable(true);
|
||||
rights.set_desc_type_raw(DescriptorType::Code as u8);
|
||||
rights.set_dpl(0);
|
||||
rights.set_granularity_raw(Granularity::KByte as u8);
|
||||
rights.set_long(true);
|
||||
rights.set_db(false);
|
||||
rights
|
||||
};
|
||||
|
||||
let ss_rights = {
|
||||
let mut rights = SegmentRights::default();
|
||||
rights.set_rw(true);
|
||||
rights.set_dc(false);
|
||||
rights.set_executable(false);
|
||||
rights.set_desc_type_raw(DescriptorType::Code as u8);
|
||||
rights.set_dpl(0);
|
||||
rights.set_granularity_raw(Granularity::KByte as u8);
|
||||
rights.set_long(false);
|
||||
rights.set_db(true);
|
||||
rights
|
||||
};
|
||||
|
||||
info!("Setting RIP:{:x} to {:x}", current_rip, lstar);
|
||||
unsafe {
|
||||
// Set segment registers for kernel mode
|
||||
vmwrite(vmcs::guest::RIP, lstar).unwrap();
|
||||
vmwrite(vmcs::guest::CS_SELECTOR, cs_selector as u64).unwrap();
|
||||
vmwrite(vmcs::guest::SS_SELECTOR, ss_selector as u64).unwrap();
|
||||
vmwrite(vmcs::guest::CS_BASE, 0).unwrap();
|
||||
vmwrite(vmcs::guest::SS_BASE, 0).unwrap();
|
||||
vmwrite(vmcs::guest::CS_LIMIT, 0xFFFFFFFF).unwrap();
|
||||
vmwrite(vmcs::guest::SS_LIMIT, 0xFFFFFFFF).unwrap();
|
||||
vmwrite(vmcs::guest::CS_ACCESS_RIGHTS, cs_rights.0 as u64).unwrap();
|
||||
vmwrite(vmcs::guest::SS_ACCESS_RIGHTS, ss_rights.0 as u64).unwrap();
|
||||
|
||||
// Set CR0 and CR4 for Long Mode
|
||||
let mut cr0 = vmread(vmcs::guest::CR0).unwrap();
|
||||
cr0 |= (1 << 31) | (1 << 0); // Set PG (Paging) and PE (Protection Enable)
|
||||
vmwrite(vmcs::guest::CR0, cr0).unwrap();
|
||||
|
||||
let mut cr4 = vmread(vmcs::guest::CR4).unwrap();
|
||||
cr4 |= 1 << 5; // Set PAE (Physical Address Extension)
|
||||
vmwrite(vmcs::guest::CR4, cr4).unwrap();
|
||||
|
||||
// Set EFER for Long Mode
|
||||
let mut efer = vmread(vmcs::guest::IA32_EFER_FULL).unwrap();
|
||||
efer |= (1 << 8) | (1 << 10); // Set LME (Long Mode Enable) and LMA (Long Mode Active)
|
||||
vmwrite(vmcs::guest::IA32_EFER_FULL, efer).unwrap();
|
||||
|
||||
// Set VM-Entry controls for Long Mode
|
||||
let mut entry_ctrls = EntryControls::read();
|
||||
entry_ctrls.set_ia32e_mode_guest(true);
|
||||
entry_ctrls.write();
|
||||
}
|
||||
|
||||
let new_rflags = rflags & !sfmask;
|
||||
unsafe {
|
||||
vmwrite(vmcs::guest::RFLAGS, new_rflags).unwrap();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
fn emulate_clac(vcpu: &mut VCpu) -> bool {
|
||||
if let Err(_) = modify_rflags_ac(false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Err(_) = vcpu.step_next_inst() {
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn emulate_stac(vcpu: &mut VCpu) -> bool {
|
||||
if let Err(_) = modify_rflags_ac(true) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Err(_) = vcpu.step_next_inst() {
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn modify_rflags_ac(set: bool) -> x86::vmx::Result<()> {
|
||||
unsafe {
|
||||
let rflags = vmread(vmcs::guest::RFLAGS)?;
|
||||
let new_rflags = if set {
|
||||
rflags | RFLAGS_AC_BIT
|
||||
} else {
|
||||
rflags & !RFLAGS_AC_BIT
|
||||
};
|
||||
vmwrite(vmcs::guest::RFLAGS, new_rflags)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
pub mod asm;
|
||||
pub mod cpuid;
|
||||
pub mod cr;
|
||||
pub mod emulation;
|
||||
pub mod ept;
|
||||
pub mod error;
|
||||
pub mod fpu;
|
||||
|
@ -133,6 +133,13 @@ impl ShadowMsr {
|
||||
}
|
||||
x86::msr::IA32_KERNEL_GSBASE => Self::shadow_read(vcpu, msr_kind),
|
||||
0x1b => Self::shadow_read(vcpu, msr_kind),
|
||||
0x8b => Self::set_ret_val(vcpu, 0x8701021),
|
||||
0xc0011029 => Self::set_ret_val(vcpu, 0x3000310e08202),
|
||||
0xc0010000 => Self::set_ret_val(vcpu, 0x130076),
|
||||
0xc0010001 => Self::set_ret_val(vcpu, 0),
|
||||
0xc0010002 => Self::set_ret_val(vcpu, 0),
|
||||
0xc0010003 => Self::set_ret_val(vcpu, 0),
|
||||
0xc0010007 => Self::set_ret_val(vcpu, 0),
|
||||
_ => {
|
||||
panic!("Unhandled RDMSR: {:#x}", msr_kind);
|
||||
}
|
||||
@ -172,6 +179,7 @@ impl ShadowMsr {
|
||||
x86::msr::IA32_FS_BASE => unsafe { vmwrite(vmcs::guest::FS_BASE, value).unwrap() },
|
||||
x86::msr::IA32_GS_BASE => unsafe { vmwrite(vmcs::guest::GS_BASE, value).unwrap() },
|
||||
0x1b => Self::shadow_write(vcpu, msr_kind),
|
||||
0xc0010007 => Self::shadow_write(vcpu, msr_kind),
|
||||
|
||||
_ => {
|
||||
panic!("Unhandled WRMSR: {:#x}", msr_kind);
|
||||
|
@ -26,7 +26,9 @@ use crate::{
|
||||
memory::BootInfoFrameAllocator,
|
||||
subscribe_with_context,
|
||||
vmm::{
|
||||
cpuid, cr, fpu,
|
||||
cpuid, cr,
|
||||
emulation::opcode::{emulate_opcode, OpcodeEmulator},
|
||||
fpu,
|
||||
io::{self, InitPhase, Serial, PIC},
|
||||
msr,
|
||||
qual::{QualCr, QualIo},
|
||||
@ -49,8 +51,10 @@ use super::{
|
||||
};
|
||||
|
||||
const SIZE_2MIB: u64 = 2 * 1024 * 1024;
|
||||
const GUEST_MEMORY_SIZE: u64 = 2 * 1024 * 1024 * 1024;
|
||||
|
||||
static EPT_FRAME_ALLOCATOR: AtomicPtr<BootInfoFrameAllocator> = AtomicPtr::new(core::ptr::null_mut());
|
||||
static EPT_FRAME_ALLOCATOR: AtomicPtr<BootInfoFrameAllocator> =
|
||||
AtomicPtr::new(core::ptr::null_mut());
|
||||
|
||||
#[repr(C)]
|
||||
pub struct VCpu {
|
||||
@ -71,6 +75,8 @@ pub struct VCpu {
|
||||
pub io_bitmap_b: x86_64::structures::paging::PhysFrame,
|
||||
pub pic: PIC,
|
||||
pub pending_irq: u16,
|
||||
pub opcode_emulator: OpcodeEmulator,
|
||||
pub emulate_amd: bool,
|
||||
}
|
||||
|
||||
const TEMP_STACK_SIZE: usize = 4096;
|
||||
@ -184,6 +190,8 @@ impl VCpu {
|
||||
io_bitmap_b,
|
||||
pic: PIC::new(),
|
||||
pending_irq: 0,
|
||||
opcode_emulator: OpcodeEmulator::new(),
|
||||
emulate_amd: false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -286,7 +294,9 @@ impl VCpu {
|
||||
if self.ept.get_phys_addr(current_page as u64).is_none() {
|
||||
if let Some(frame) = frame_allocator.allocate_frame() {
|
||||
let hpa = frame.start_address().as_u64();
|
||||
self.ept.map_4k(current_page as u64, hpa, frame_allocator).unwrap();
|
||||
self.ept
|
||||
.map_4k(current_page as u64, hpa, frame_allocator)
|
||||
.unwrap();
|
||||
} else {
|
||||
panic!("Failed to allocate frame for image at {:#x}", current_page);
|
||||
}
|
||||
@ -303,17 +313,17 @@ impl VCpu {
|
||||
}
|
||||
|
||||
pub fn setup_guest_memory(&mut self, frame_allocator: &mut BootInfoFrameAllocator) -> u64 {
|
||||
let guest_memory_size = 2 * 1024 * 1024 * 1024;
|
||||
info!(
|
||||
"Setting up guest memory with on-demand allocation (reported size: {}MB)",
|
||||
GUEST_MEMORY_SIZE / (1024 * 1024)
|
||||
);
|
||||
|
||||
info!("Setting up guest memory with on-demand allocation (reported size: {}MB)",
|
||||
guest_memory_size / (1024 * 1024));
|
||||
|
||||
self.load_kernel(linux::BZIMAGE, guest_memory_size);
|
||||
self.load_kernel(linux::BZIMAGE, GUEST_MEMORY_SIZE);
|
||||
|
||||
let eptp = EPTP::new(&self.ept.root_table);
|
||||
unsafe { vmwrite(vmcs::control::EPTP_FULL, eptp.0).unwrap() };
|
||||
|
||||
guest_memory_size
|
||||
GUEST_MEMORY_SIZE
|
||||
}
|
||||
|
||||
pub fn register_msrs(&mut self, mapper: &OffsetPageTable<'static>) {
|
||||
@ -354,6 +364,7 @@ impl VCpu {
|
||||
.set(x86::msr::MSR_C5_PMON_BOX_CTRL, 0)
|
||||
.unwrap();
|
||||
self.guest_msr.set(0x1b, 0).unwrap();
|
||||
self.guest_msr.set(0xc0010007, 0).unwrap();
|
||||
|
||||
vmwrite(
|
||||
vmcs::control::VMEXIT_MSR_LOAD_ADDR_FULL,
|
||||
@ -969,7 +980,7 @@ impl VCpu {
|
||||
vmwrite(vmcs::host::RSP, rsp).unwrap();
|
||||
}
|
||||
|
||||
fn step_next_inst(&mut self) -> Result<(), VmFail> {
|
||||
pub fn step_next_inst(&mut self) -> Result<(), VmFail> {
|
||||
unsafe {
|
||||
let rip = vmread(vmcs::guest::RIP)?;
|
||||
vmwrite(
|
||||
@ -980,8 +991,11 @@ impl VCpu {
|
||||
}
|
||||
|
||||
fn handle_ept_violation(&mut self, gpa: u64) {
|
||||
if gpa >= 2 * 1024 * 1024 * 1024 {
|
||||
panic!("EPT Violation: Guest tried to access memory beyond 2GB at {:#x}", gpa);
|
||||
if gpa >= GUEST_MEMORY_SIZE {
|
||||
panic!(
|
||||
"EPT Violation: Guest tried to access memory beyond 2GB at {:#x}",
|
||||
gpa
|
||||
);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
@ -1001,7 +1015,10 @@ impl VCpu {
|
||||
}
|
||||
}
|
||||
None => {
|
||||
panic!("EPT Violation: Out of memory! Cannot allocate frame for GPA {:#x}", gpa);
|
||||
panic!(
|
||||
"EPT Violation: Out of memory! Cannot allocate frame for GPA {:#x}",
|
||||
gpa
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1146,39 +1163,13 @@ impl VCpu {
|
||||
}
|
||||
|
||||
if valid_bytes > 0 {
|
||||
match instruction_bytes[0] {
|
||||
0x0F => {
|
||||
if valid_bytes > 1 {
|
||||
match instruction_bytes[1] {
|
||||
0x01 => match instruction_bytes[2] {
|
||||
0xCA => {
|
||||
unsafe {
|
||||
let rflags = vmread(vmcs::guest::RFLAGS).unwrap();
|
||||
vmwrite(vmcs::guest::RFLAGS, rflags & !(1 << 18)).unwrap();
|
||||
}
|
||||
self.step_next_inst().unwrap();
|
||||
}
|
||||
0xCB => {
|
||||
unsafe {
|
||||
let rflags = vmread(vmcs::guest::RFLAGS).unwrap();
|
||||
vmwrite(vmcs::guest::RFLAGS, rflags | (1 << 18)).unwrap();
|
||||
}
|
||||
self.step_next_inst().unwrap();
|
||||
}
|
||||
_ => {
|
||||
if !emulate_opcode(self, instruction_bytes, valid_bytes) {
|
||||
info!(
|
||||
"VMExit: Exception {} at RIP {:#x} with instruction bytes: {:?}",
|
||||
vector, rip, instruction_bytes
|
||||
);
|
||||
self.inject_exception(vector, error_code).unwrap();
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
self.inject_exception(vector, error_code).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.inject_exception(vector, error_code).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
VmxExitReason::IO_INSTRUCTION => {
|
||||
|
Reference in New Issue
Block a user