mirror of
https://github.com/mii443/nel_os.git
synced 2025-08-22 16:15:38 +00:00
EPT
This commit is contained in:
@ -26,6 +26,8 @@ run-args = [
|
|||||||
"stdio",
|
"stdio",
|
||||||
"-display",
|
"-display",
|
||||||
"none",
|
"none",
|
||||||
|
"-m",
|
||||||
|
"512M",
|
||||||
"-cpu",
|
"-cpu",
|
||||||
"host",
|
"host",
|
||||||
"-enable-kvm",
|
"-enable-kvm",
|
||||||
|
@ -59,7 +59,7 @@ fn kernel_main(boot_info: &'static BootInfo) -> ! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut vcpu = VCpu::new(phys_mem_offset.as_u64(), &mut frame_allocator);
|
let mut vcpu = VCpu::new(phys_mem_offset.as_u64(), &mut frame_allocator);
|
||||||
vcpu.activate();
|
vcpu.activate(&mut frame_allocator);
|
||||||
|
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
vcpu.vm_loop();
|
vcpu.vm_loop();
|
||||||
|
@ -28,6 +28,44 @@ impl BootInfoFrameAllocator {
|
|||||||
let frame_addresses = addr_ranges.flat_map(|r| r.step_by(4096));
|
let frame_addresses = addr_ranges.flat_map(|r| r.step_by(4096));
|
||||||
frame_addresses.map(|addr| PhysFrame::containing_address(PhysAddr::new(addr)))
|
frame_addresses.map(|addr| PhysFrame::containing_address(PhysAddr::new(addr)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn allocate_2mib_aligned(&mut self) -> Option<PhysAddr> {
|
||||||
|
self.allocate_2mib_frame()
|
||||||
|
.map(|frame| frame.start_address())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn allocate_2mib_frame(&mut self) -> Option<PhysFrame<Size4KiB>> {
|
||||||
|
let mut frames = self.usable_frames().skip(self.next);
|
||||||
|
|
||||||
|
let base_frame = frames.find(|frame| frame.start_address().as_u64() & 0x1F_FFFF == 0)?;
|
||||||
|
|
||||||
|
let base_idx = self.next
|
||||||
|
+ frames
|
||||||
|
.enumerate()
|
||||||
|
.find(|(_, frame)| frame.start_address() == base_frame.start_address())
|
||||||
|
.map(|(idx, _)| idx)
|
||||||
|
.unwrap_or(0);
|
||||||
|
|
||||||
|
let frame_count = 512;
|
||||||
|
let frames_are_available = self
|
||||||
|
.usable_frames()
|
||||||
|
.skip(base_idx)
|
||||||
|
.take(frame_count)
|
||||||
|
.enumerate()
|
||||||
|
.all(|(idx, frame)| {
|
||||||
|
let expected_addr = base_frame.start_address().as_u64() + (idx as u64 * 4096);
|
||||||
|
frame.start_address().as_u64() == expected_addr
|
||||||
|
});
|
||||||
|
|
||||||
|
if !frames_are_available {
|
||||||
|
self.next = base_idx + 1;
|
||||||
|
return self.allocate_2mib_frame();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.next = base_idx + frame_count;
|
||||||
|
|
||||||
|
Some(base_frame)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl FrameAllocator<Size4KiB> for BootInfoFrameAllocator {
|
unsafe impl FrameAllocator<Size4KiB> for BootInfoFrameAllocator {
|
||||||
|
@ -7,11 +7,10 @@ use x86_64::{
|
|||||||
PhysAddr,
|
PhysAddr,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::memory;
|
use crate::{info, memory};
|
||||||
|
|
||||||
pub struct EPT {
|
pub struct EPT {
|
||||||
pub root_table: PhysFrame,
|
pub root_table: PhysFrame,
|
||||||
pub tables: Vec<PhysFrame>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EPT {
|
impl EPT {
|
||||||
@ -22,7 +21,6 @@ impl EPT {
|
|||||||
|
|
||||||
Self {
|
Self {
|
||||||
root_table: root_frame,
|
root_table: root_frame,
|
||||||
tables: Vec::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,7 +46,7 @@ impl EPT {
|
|||||||
unsafe { &mut *(table_ptr as *mut [EntryBase; 512]) }
|
unsafe { &mut *(table_ptr as *mut [EntryBase; 512]) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn map_2m(
|
pub fn map_2m(
|
||||||
&mut self,
|
&mut self,
|
||||||
gpa: u64,
|
gpa: u64,
|
||||||
hpa: u64,
|
hpa: u64,
|
||||||
@ -69,7 +67,6 @@ impl EPT {
|
|||||||
lv4_entry.set_phys(frame.start_address().as_u64() >> 12);
|
lv4_entry.set_phys(frame.start_address().as_u64() >> 12);
|
||||||
lv4_entry.set_map_memory(false);
|
lv4_entry.set_map_memory(false);
|
||||||
lv4_entry.set_typ(0);
|
lv4_entry.set_typ(0);
|
||||||
self.tables.push(frame);
|
|
||||||
table_ptr
|
table_ptr
|
||||||
} else {
|
} else {
|
||||||
let frame =
|
let frame =
|
||||||
@ -87,7 +84,6 @@ impl EPT {
|
|||||||
lv3_entry.set_phys(frame.start_address().as_u64() >> 12);
|
lv3_entry.set_phys(frame.start_address().as_u64() >> 12);
|
||||||
lv3_entry.set_map_memory(false);
|
lv3_entry.set_map_memory(false);
|
||||||
lv3_entry.set_typ(0);
|
lv3_entry.set_typ(0);
|
||||||
self.tables.push(frame);
|
|
||||||
table_ptr
|
table_ptr
|
||||||
} else {
|
} else {
|
||||||
let frame =
|
let frame =
|
||||||
@ -98,9 +94,36 @@ impl EPT {
|
|||||||
let lv2_entry = &mut lv2_table[lv2_index as usize];
|
let lv2_entry = &mut lv2_table[lv2_index as usize];
|
||||||
lv2_entry.set_phys(hpa >> 12);
|
lv2_entry.set_phys(hpa >> 12);
|
||||||
lv2_entry.set_map_memory(true);
|
lv2_entry.set_map_memory(true);
|
||||||
|
info!("{:#x}", lv2_entry as *const _ as u64);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_phys_addr(&self, gpa: u64) -> Option<u64> {
|
||||||
|
let lv4_index = (gpa >> 39) & 0x1FF;
|
||||||
|
let lv3_index = (gpa >> 30) & 0x1FF;
|
||||||
|
let lv2_index = (gpa >> 21) & 0x1FF;
|
||||||
|
|
||||||
|
let lv4_table = Self::frame_to_table_ptr(&self.root_table);
|
||||||
|
let lv4_entry = &lv4_table[lv4_index as usize];
|
||||||
|
|
||||||
|
let frame = PhysFrame::from_start_address(PhysAddr::new(lv4_entry.phys() << 12)).unwrap();
|
||||||
|
let lv3_table = Self::frame_to_table_ptr(&frame);
|
||||||
|
let lv3_entry = &lv3_table[lv3_index as usize];
|
||||||
|
|
||||||
|
let frame = PhysFrame::from_start_address(PhysAddr::new(lv3_entry.phys() << 12)).unwrap();
|
||||||
|
let lv2_table = Self::frame_to_table_ptr(&frame);
|
||||||
|
let lv2_entry = &lv2_table[lv2_index as usize];
|
||||||
|
|
||||||
|
if !lv2_entry.map_memory() {
|
||||||
|
info!("EPT: No mapping found for GPA: {:#x}", gpa);
|
||||||
|
info!("{:#x}", lv2_entry.address().as_u64());
|
||||||
|
info!("{:#x}", lv2_entry as *const _ as u64);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(lv2_entry.address().as_u64())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bitfield! {
|
bitfield! {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use x86::{
|
use x86::{
|
||||||
bits64::vmx::vmwrite,
|
bits64::vmx::vmwrite,
|
||||||
controlregs::{cr0, cr3, cr4},
|
controlregs::{cr0, cr3, cr4, Cr0},
|
||||||
dtables::{self, DescriptorTablePointer},
|
dtables::{self, DescriptorTablePointer},
|
||||||
msr::{rdmsr, IA32_EFER, IA32_FS_BASE},
|
msr::{rdmsr, IA32_EFER, IA32_FS_BASE},
|
||||||
vmx::{vmcs, VmFail},
|
vmx::{vmcs, VmFail},
|
||||||
@ -17,11 +17,13 @@ use crate::{
|
|||||||
memory::BootInfoFrameAllocator,
|
memory::BootInfoFrameAllocator,
|
||||||
vmm::vmcs::{
|
vmm::vmcs::{
|
||||||
DescriptorType, EntryControls, Granularity, PrimaryExitControls,
|
DescriptorType, EntryControls, Granularity, PrimaryExitControls,
|
||||||
PrimaryProcessorBasedVmExecutionControls, SegmentRights, VmxExitInfo, VmxExitReason,
|
PrimaryProcessorBasedVmExecutionControls, SecondaryProcessorBasedVmExecutionControls,
|
||||||
|
SegmentRights, VmxExitInfo, VmxExitReason,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
|
ept::{EPT, EPTP},
|
||||||
register::GuestRegisters,
|
register::GuestRegisters,
|
||||||
vmcs::{InstructionError, PinBasedVmExecutionControls, Vmcs},
|
vmcs::{InstructionError, PinBasedVmExecutionControls, Vmcs},
|
||||||
vmxon::Vmxon,
|
vmxon::Vmxon,
|
||||||
@ -33,6 +35,8 @@ pub struct VCpu {
|
|||||||
pub phys_mem_offset: u64,
|
pub phys_mem_offset: u64,
|
||||||
pub guest_registers: GuestRegisters,
|
pub guest_registers: GuestRegisters,
|
||||||
pub launch_done: bool,
|
pub launch_done: bool,
|
||||||
|
pub ept: EPT,
|
||||||
|
pub eptp: EPTP,
|
||||||
}
|
}
|
||||||
|
|
||||||
const TEMP_STACK_SIZE: usize = 4096;
|
const TEMP_STACK_SIZE: usize = 4096;
|
||||||
@ -43,16 +47,21 @@ impl VCpu {
|
|||||||
let mut vmxon = Vmxon::new(frame_allocator);
|
let mut vmxon = Vmxon::new(frame_allocator);
|
||||||
vmxon.init(phys_mem_offset);
|
vmxon.init(phys_mem_offset);
|
||||||
let vmcs = Vmcs::new(frame_allocator);
|
let vmcs = Vmcs::new(frame_allocator);
|
||||||
|
let ept = EPT::new(frame_allocator);
|
||||||
|
let eptp = EPTP::new(&ept.root_table);
|
||||||
|
|
||||||
VCpu {
|
VCpu {
|
||||||
vmxon,
|
vmxon,
|
||||||
vmcs,
|
vmcs,
|
||||||
phys_mem_offset,
|
phys_mem_offset,
|
||||||
guest_registers: GuestRegisters::default(),
|
guest_registers: GuestRegisters::default(),
|
||||||
launch_done: false,
|
launch_done: false,
|
||||||
|
ept,
|
||||||
|
eptp,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn activate(&mut self) {
|
pub fn activate(&mut self, frame_allocator: &mut BootInfoFrameAllocator) {
|
||||||
self.vmxon.activate_vmxon().unwrap();
|
self.vmxon.activate_vmxon().unwrap();
|
||||||
|
|
||||||
let revision_id = unsafe { rdmsr(x86::msr::IA32_VMX_BASIC) } as u32;
|
let revision_id = unsafe { rdmsr(x86::msr::IA32_VMX_BASIC) } as u32;
|
||||||
@ -64,10 +73,32 @@ impl VCpu {
|
|||||||
self.setup_exit_ctrls().unwrap();
|
self.setup_exit_ctrls().unwrap();
|
||||||
self.setup_host_state().unwrap();
|
self.setup_host_state().unwrap();
|
||||||
self.setup_guest_state().unwrap();
|
self.setup_guest_state().unwrap();
|
||||||
|
self.setup_guest_memory(frame_allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup_guest_memory(&mut self, frame_allocator: &mut BootInfoFrameAllocator) {
|
||||||
|
let mut pages = 50;
|
||||||
|
let mut gpa = 0;
|
||||||
|
|
||||||
|
info!("Setting up guest memory...");
|
||||||
|
while pages > 0 {
|
||||||
|
let frame = frame_allocator
|
||||||
|
.allocate_2mib_frame()
|
||||||
|
.expect("Failed to allocate frame");
|
||||||
|
let hpa = frame.start_address().as_u64();
|
||||||
|
|
||||||
|
self.ept.map_2m(gpa, hpa, frame_allocator).unwrap();
|
||||||
|
gpa += (4 * 1024) << 9;
|
||||||
|
pages -= 1;
|
||||||
|
}
|
||||||
|
info!("Guest memory setup complete");
|
||||||
|
|
||||||
|
let eptp = EPTP::new(&self.ept.root_table);
|
||||||
|
unsafe { vmwrite(vmcs::control::EPTP_FULL, eptp.0).unwrap() };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setup_exec_ctrls(&mut self) -> Result<(), VmFail> {
|
pub fn setup_exec_ctrls(&mut self) -> Result<(), VmFail> {
|
||||||
info!("Setting up execution controls");
|
info!("Setting up pin based execution controls");
|
||||||
let basic_msr = unsafe { rdmsr(x86::msr::IA32_VMX_BASIC) };
|
let basic_msr = unsafe { rdmsr(x86::msr::IA32_VMX_BASIC) };
|
||||||
let mut pin_exec_ctrl = PinBasedVmExecutionControls::read();
|
let mut pin_exec_ctrl = PinBasedVmExecutionControls::read();
|
||||||
|
|
||||||
@ -82,6 +113,8 @@ impl VCpu {
|
|||||||
|
|
||||||
pin_exec_ctrl.write();
|
pin_exec_ctrl.write();
|
||||||
|
|
||||||
|
info!("Setting up primary execution controls");
|
||||||
|
|
||||||
let mut primary_exec_ctrl = PrimaryProcessorBasedVmExecutionControls::read();
|
let mut primary_exec_ctrl = PrimaryProcessorBasedVmExecutionControls::read();
|
||||||
|
|
||||||
let reserved_bits = if basic_msr & (1 << 55) != 0 {
|
let reserved_bits = if basic_msr & (1 << 55) != 0 {
|
||||||
@ -92,11 +125,28 @@ impl VCpu {
|
|||||||
|
|
||||||
primary_exec_ctrl.0 |= (reserved_bits & 0xFFFFFFFF) as u32;
|
primary_exec_ctrl.0 |= (reserved_bits & 0xFFFFFFFF) as u32;
|
||||||
primary_exec_ctrl.0 &= (reserved_bits >> 32) as u32;
|
primary_exec_ctrl.0 &= (reserved_bits >> 32) as u32;
|
||||||
primary_exec_ctrl.set_hlt(true);
|
primary_exec_ctrl.set_hlt(false);
|
||||||
primary_exec_ctrl.set_activate_secondary_controls(false);
|
primary_exec_ctrl.set_activate_secondary_controls(true);
|
||||||
|
|
||||||
primary_exec_ctrl.write();
|
primary_exec_ctrl.write();
|
||||||
|
|
||||||
|
info!("Setting up secondary execution controls");
|
||||||
|
|
||||||
|
let mut secondary_exec_ctrl = SecondaryProcessorBasedVmExecutionControls::read();
|
||||||
|
|
||||||
|
let reserved_bits = if basic_msr & (1 << 55) != 0 {
|
||||||
|
unsafe { rdmsr(x86::msr::IA32_VMX_PROCBASED_CTLS2) }
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
secondary_exec_ctrl.0 |= (reserved_bits & 0xFFFFFFFF) as u32;
|
||||||
|
secondary_exec_ctrl.0 &= (reserved_bits >> 32) as u32;
|
||||||
|
secondary_exec_ctrl.set_ept(true);
|
||||||
|
secondary_exec_ctrl.set_unrestricted_guest(true);
|
||||||
|
|
||||||
|
secondary_exec_ctrl.write();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,7 +165,7 @@ impl VCpu {
|
|||||||
|
|
||||||
entry_ctrl.0 |= (reserved_bits & 0xFFFFFFFF) as u32;
|
entry_ctrl.0 |= (reserved_bits & 0xFFFFFFFF) as u32;
|
||||||
entry_ctrl.0 &= (reserved_bits >> 32) as u32;
|
entry_ctrl.0 &= (reserved_bits >> 32) as u32;
|
||||||
entry_ctrl.set_ia32e_mode_guest(true);
|
entry_ctrl.set_ia32e_mode_guest(false);
|
||||||
|
|
||||||
entry_ctrl.write();
|
entry_ctrl.write();
|
||||||
|
|
||||||
@ -204,7 +254,10 @@ impl VCpu {
|
|||||||
info!("Setting up guest state");
|
info!("Setting up guest state");
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
vmwrite(vmcs::guest::CR0, cr0().bits() as u64)?;
|
let cr0 = Cr0::empty()
|
||||||
|
| Cr0::CR0_PROTECTED_MODE
|
||||||
|
| Cr0::CR0_NUMERIC_ERROR & !Cr0::CR0_ENABLE_PAGING;
|
||||||
|
vmwrite(vmcs::guest::CR0, cr0.bits() as u64)?;
|
||||||
vmwrite(vmcs::guest::CR3, cr3())?;
|
vmwrite(vmcs::guest::CR3, cr3())?;
|
||||||
vmwrite(vmcs::guest::CR4, cr4().bits() as u64)?;
|
vmwrite(vmcs::guest::CR4, cr4().bits() as u64)?;
|
||||||
|
|
||||||
@ -326,6 +379,13 @@ impl VCpu {
|
|||||||
pub fn vm_loop(&mut self) -> ! {
|
pub fn vm_loop(&mut self) -> ! {
|
||||||
info!("Entering VM loop");
|
info!("Entering VM loop");
|
||||||
|
|
||||||
|
let guest_ptr = Self::guest as u64;
|
||||||
|
let guest_addr = 0x18000801000u64; //self.ept.get_phys_addr(0).unwrap();
|
||||||
|
unsafe {
|
||||||
|
core::ptr::copy_nonoverlapping(guest_ptr as *const u8, guest_addr as *mut u8, 200);
|
||||||
|
vmwrite(vmcs::guest::RIP, 0).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if let Err(err) = self.vmentry() {
|
if let Err(err) = self.vmentry() {
|
||||||
info!("VMEntry failed: {}", err.as_str());
|
info!("VMEntry failed: {}", err.as_str());
|
||||||
|
@ -219,6 +219,60 @@ impl PrimaryProcessorBasedVmExecutionControls {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bitfield! {
|
||||||
|
pub struct SecondaryProcessorBasedVmExecutionControls(u32);
|
||||||
|
impl Debug;
|
||||||
|
|
||||||
|
pub virtualize_apic_accesses, set_virtualize_apic_accesses: 0;
|
||||||
|
pub ept, set_ept: 1;
|
||||||
|
pub descriptor_table, set_descriptor_table: 2;
|
||||||
|
pub rdtscp, set_rdtscp: 3;
|
||||||
|
pub virtualize_x2apic_mode, set_virtualize_x2apic_mode: 4;
|
||||||
|
pub vpid, set_vpid: 5;
|
||||||
|
pub wbinvd, set_wbinvd: 6;
|
||||||
|
pub unrestricted_guest, set_unrestricted_guest: 7;
|
||||||
|
pub apic_register_virtualization, set_apic_register_virtualization: 8;
|
||||||
|
pub virtual_interrupt_delivery, set_virtual_interrupt_delivery: 9;
|
||||||
|
pub pause_loop, set_pause_loop: 10;
|
||||||
|
pub rdrand, set_rdrand: 11;
|
||||||
|
pub enable_invpcid, set_enable_invpcid: 12;
|
||||||
|
pub enable_vmfunc, set_enable_vmfunc: 13;
|
||||||
|
pub vmcs_shadowing, set_vmcs_shadowing: 14;
|
||||||
|
pub enable_encls, set_enable_encls: 15;
|
||||||
|
pub rdseed, set_rdseed: 16;
|
||||||
|
pub enable_pml, set_enable_pml: 17;
|
||||||
|
pub ept_violation, set_ept_violation: 18;
|
||||||
|
pub conceal_vmx_from_pt, set_conceal_vmx_from_pt: 19;
|
||||||
|
pub enable_xsaves_xrstors, set_enable_xsaves_xrstors: 20;
|
||||||
|
pub pasid_translation, set_pasid_translation: 21;
|
||||||
|
pub mode_based_control_ept, set_mode_based_control_ept: 22;
|
||||||
|
pub subpage_write_eptr, set_subpage_write_eptr: 23;
|
||||||
|
pub pt_guest_pa, set_pt_guest_pa: 24;
|
||||||
|
pub tsc_scaling, set_tsc_scaling: 25;
|
||||||
|
pub enable_user_wait_pause, set_enable_user_wait_pause: 26;
|
||||||
|
pub enable_pconfig, set_enable_pconfig: 27;
|
||||||
|
pub enable_enclv, set_enable_enclv: 28;
|
||||||
|
pub vmm_buslock_detect, set_vmm_buslock_detect: 29;
|
||||||
|
pub instruction_timeout, set_instruction_timeout: 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SecondaryProcessorBasedVmExecutionControls {
|
||||||
|
pub fn read() -> Self {
|
||||||
|
let err = VmcsControl32::SECONDARY_PROCESSOR_BASED_VM_EXECUTION_CONTROLS.read();
|
||||||
|
if err.is_err() {
|
||||||
|
panic!("Failed to read Secondary Processor Based VM Execution Controls");
|
||||||
|
}
|
||||||
|
let err = err.unwrap();
|
||||||
|
SecondaryProcessorBasedVmExecutionControls(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(&self) {
|
||||||
|
VmcsControl32::SECONDARY_PROCESSOR_BASED_VM_EXECUTION_CONTROLS
|
||||||
|
.write(self.0)
|
||||||
|
.expect("Failed to write Secondary Processor Based VM Execution Controls");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub enum DescriptorType {
|
pub enum DescriptorType {
|
||||||
System = 0,
|
System = 0,
|
||||||
Code = 1,
|
Code = 1,
|
||||||
|
Reference in New Issue
Block a user