diff --git a/Cargo.toml b/Cargo.toml index 842453b..b0e2064 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,8 @@ run-args = [ "stdio", "-display", "none", + "-m", + "512M", "-cpu", "host", "-enable-kvm", diff --git a/src/main.rs b/src/main.rs index aff8d86..cfcbc59 100644 --- a/src/main.rs +++ b/src/main.rs @@ -59,7 +59,7 @@ fn kernel_main(boot_info: &'static BootInfo) -> ! { } let mut vcpu = VCpu::new(phys_mem_offset.as_u64(), &mut frame_allocator); - vcpu.activate(); + vcpu.activate(&mut frame_allocator); #[cfg(not(test))] vcpu.vm_loop(); diff --git a/src/memory.rs b/src/memory.rs index f23224b..c1f8720 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -28,6 +28,44 @@ impl BootInfoFrameAllocator { let frame_addresses = addr_ranges.flat_map(|r| r.step_by(4096)); frame_addresses.map(|addr| PhysFrame::containing_address(PhysAddr::new(addr))) } + + pub fn allocate_2mib_aligned(&mut self) -> Option { + self.allocate_2mib_frame() + .map(|frame| frame.start_address()) + } + + pub fn allocate_2mib_frame(&mut self) -> Option> { + 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 for BootInfoFrameAllocator { diff --git a/src/vmm/ept.rs b/src/vmm/ept.rs index 0050b41..e0dc06f 100644 --- a/src/vmm/ept.rs +++ b/src/vmm/ept.rs @@ -7,11 +7,10 @@ use x86_64::{ PhysAddr, }; -use crate::memory; +use crate::{info, memory}; pub struct EPT { pub root_table: PhysFrame, - pub tables: Vec, } impl EPT { @@ -22,7 +21,6 @@ impl EPT { Self { root_table: root_frame, - tables: Vec::new(), } } @@ -48,7 +46,7 @@ impl EPT { unsafe { &mut *(table_ptr as *mut [EntryBase; 512]) } } - fn map_2m( + pub fn map_2m( &mut self, gpa: u64, hpa: u64, @@ -69,7 +67,6 @@ impl EPT { lv4_entry.set_phys(frame.start_address().as_u64() >> 12); lv4_entry.set_map_memory(false); lv4_entry.set_typ(0); - self.tables.push(frame); table_ptr } else { let frame = @@ -87,7 +84,6 @@ impl EPT { lv3_entry.set_phys(frame.start_address().as_u64() >> 12); lv3_entry.set_map_memory(false); lv3_entry.set_typ(0); - self.tables.push(frame); table_ptr } else { let frame = @@ -98,9 +94,36 @@ impl EPT { let lv2_entry = &mut lv2_table[lv2_index as usize]; lv2_entry.set_phys(hpa >> 12); lv2_entry.set_map_memory(true); + info!("{:#x}", lv2_entry as *const _ as u64); Ok(()) } + + pub fn get_phys_addr(&self, gpa: u64) -> Option { + 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! { diff --git a/src/vmm/vcpu.rs b/src/vmm/vcpu.rs index 84ef83e..e03f9e0 100644 --- a/src/vmm/vcpu.rs +++ b/src/vmm/vcpu.rs @@ -1,6 +1,6 @@ use x86::{ bits64::vmx::vmwrite, - controlregs::{cr0, cr3, cr4}, + controlregs::{cr0, cr3, cr4, Cr0}, dtables::{self, DescriptorTablePointer}, msr::{rdmsr, IA32_EFER, IA32_FS_BASE}, vmx::{vmcs, VmFail}, @@ -17,11 +17,13 @@ use crate::{ memory::BootInfoFrameAllocator, vmm::vmcs::{ DescriptorType, EntryControls, Granularity, PrimaryExitControls, - PrimaryProcessorBasedVmExecutionControls, SegmentRights, VmxExitInfo, VmxExitReason, + PrimaryProcessorBasedVmExecutionControls, SecondaryProcessorBasedVmExecutionControls, + SegmentRights, VmxExitInfo, VmxExitReason, }, }; use super::{ + ept::{EPT, EPTP}, register::GuestRegisters, vmcs::{InstructionError, PinBasedVmExecutionControls, Vmcs}, vmxon::Vmxon, @@ -33,6 +35,8 @@ pub struct VCpu { pub phys_mem_offset: u64, pub guest_registers: GuestRegisters, pub launch_done: bool, + pub ept: EPT, + pub eptp: EPTP, } const TEMP_STACK_SIZE: usize = 4096; @@ -43,16 +47,21 @@ impl VCpu { let mut vmxon = Vmxon::new(frame_allocator); vmxon.init(phys_mem_offset); let vmcs = Vmcs::new(frame_allocator); + let ept = EPT::new(frame_allocator); + let eptp = EPTP::new(&ept.root_table); + VCpu { vmxon, vmcs, phys_mem_offset, guest_registers: GuestRegisters::default(), launch_done: false, + ept, + eptp, } } - pub fn activate(&mut self) { + pub fn activate(&mut self, frame_allocator: &mut BootInfoFrameAllocator) { self.vmxon.activate_vmxon().unwrap(); 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_host_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> { - info!("Setting up execution controls"); + info!("Setting up pin based execution controls"); let basic_msr = unsafe { rdmsr(x86::msr::IA32_VMX_BASIC) }; let mut pin_exec_ctrl = PinBasedVmExecutionControls::read(); @@ -82,6 +113,8 @@ impl VCpu { pin_exec_ctrl.write(); + info!("Setting up primary execution controls"); + let mut primary_exec_ctrl = PrimaryProcessorBasedVmExecutionControls::read(); 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 >> 32) as u32; - primary_exec_ctrl.set_hlt(true); - primary_exec_ctrl.set_activate_secondary_controls(false); + primary_exec_ctrl.set_hlt(false); + primary_exec_ctrl.set_activate_secondary_controls(true); 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(()) } @@ -115,7 +165,7 @@ impl VCpu { entry_ctrl.0 |= (reserved_bits & 0xFFFFFFFF) 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(); @@ -204,7 +254,10 @@ impl VCpu { info!("Setting up guest state"); 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::CR4, cr4().bits() as u64)?; @@ -326,6 +379,13 @@ impl VCpu { pub fn vm_loop(&mut self) -> ! { 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 { if let Err(err) = self.vmentry() { info!("VMEntry failed: {}", err.as_str()); diff --git a/src/vmm/vmcs.rs b/src/vmm/vmcs.rs index 484d968..d969889 100644 --- a/src/vmm/vmcs.rs +++ b/src/vmm/vmcs.rs @@ -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 { System = 0, Code = 1,