use kvm_rs::kvm::Kvm; use kvm_rs::kvm_sys; use kvm_rs::vcpu::KvmExit; use kvm_rs::x86_64::*; use kvm_rs::{PhysAddr, UserMem}; fn setup_long_mode_segments(sregs: &mut kvm_sys::kvm_sregs) { let code_seg = |seg: &mut kvm_sys::kvm_segment| { // Segment base address (unused in 64bit). seg.base = 0x0; // Limit (unused in 64bit). seg.limit = 0x0; // Segment selector -> Index=1, Table=GDT, RPL=0. seg.selector = 0x8; // Code read + execute. seg.type_ = 10; // Segment present. seg.present = 1; // Descriptor privilege level. seg.dpl = 0; // Operand/Pointer size (unused in 64bit, but must be 0 in 64bit mode). seg.db = 0; // Code/data segment. seg.s = 1; // Native 64 bit code segment. seg.l = 1; // Granularity (unused in 64bit). seg.g = 0; }; let data_seg = |seg: &mut kvm_sys::kvm_segment| { // Segment base address (unused in 64bit). seg.base = 0x0; // Limit (unused in 64bit). seg.limit = 0x0; // Segment selector -> Index=2, Table=GDT, RPL=0. seg.selector = 0x10; // Data read + write. seg.type_ = 2; // Segment present. seg.present = 1; // Descriptor privilege level. seg.dpl = 0; // Operand/Pointer size (unused in 64bit, but must be 0 in 64bit mode). seg.db = 0; // Code/data segment. seg.s = 1; // Native 64 bit code segment. seg.l = 0; // Granularity (unused in 64bit). seg.g = 0; }; code_seg(&mut sregs.cs); data_seg(&mut sregs.ds); data_seg(&mut sregs.ss); data_seg(&mut sregs.fs); data_seg(&mut sregs.gs); data_seg(&mut sregs.es); } #[rustfmt::skip] fn setup_long_mode_4level_paging(mem: &mut UserMem) -> PhysAddr { assert_eq!(0x8000, mem.as_ref().len()); // As a small exercise we create the following 4-level virtual address mapping using 4K pages: // VirtAddr [0x0000:0x3fff] -> PhysAddr [0x4000:0x7fff] // // The required paging structures we'll place at physical address [0x0000:0x3ffff]. // // PhysAddr // +-------+ // 0x0000 | PML4 | // 0x1000 | PDP | // 0x2000 | PD | // 0x3000 | PT | VirtAddr // 0x4000 +-------+ <----- +-------+ 0x0000 // | Guest | | Guest | // | (16K) | | (16K) | // 0x8000 +-------+ <----- +-------+ 0x4000 // // PML4 : Page Map Level 4 // PDP : Page Directory Pointer // PD : Page Directory // PT : Page Table // // PML4, PDP, PD will contain a single entry at index 0. // PT will contain 4 page table entries (PTE) at index {0,1,2,3} -> 4 * 4K = 16K. let mut w = |addr: PhysAddr, val: u64| mem.load(addr, &val.to_le_bytes()); // PML4E[0] refers to PDPE[0:4095]. w(PhysAddr(0x0000), PAGE_ENTRY_PRESENT | PAGE_ENTRY_RW | 0x1000); // PDPE[0] refers to PDE[0:4095]. w(PhysAddr(0x1000), PAGE_ENTRY_PRESENT | PAGE_ENTRY_RW | 0x2000); // PDE[0] refers to PTE[0:4095]. w(PhysAddr(0x2000), PAGE_ENTRY_PRESENT | PAGE_ENTRY_RW | 0x3000); // PTE[0] maps Virt [0x0000:0x0fff] -> Phys [0x4000:0x4fff]. // Just because we can, map this page readonly, as we loaded our guest sw here. w(PhysAddr(0x3000), PAGE_ENTRY_PRESENT | 0x4000); // PTE[1] maps Virt [0x1000:0x1fff] -> Phys [0x5000:0x5fff]. w(PhysAddr(0x3008), PAGE_ENTRY_PRESENT | PAGE_ENTRY_RW | 0x5000); // PTE[2] maps Virt [0x2000:0x2fff] -> Phys [0x6000:0x6fff]. w(PhysAddr(0x3010), PAGE_ENTRY_PRESENT | PAGE_ENTRY_RW | 0x6000); // PTE[3] maps Virt [0x3000:0x3fff] -> Phys [0x7000:0x7fff]. w(PhysAddr(0x3018), PAGE_ENTRY_PRESENT | PAGE_ENTRY_RW | 0x7000); // Return address of PML4. PhysAddr(0x0000) } fn setup_long_mode(sregs: &mut kvm_sys::kvm_sregs, mem: &mut UserMem) { // Setup segment descriptors for long mode. setup_long_mode_segments(sregs); // Setup paging structures. let pml4_base = setup_long_mode_4level_paging(mem); // Setup physical address of first paging structure (PML4). sregs.cr3 = pml4_base.0; // Enable paging (PG) and protection (PE). sregs.cr0 = CR0_PG | CR0_PE; // Enable physical address extension (PAE). sregs.cr4 = CR4_PAE; // Set long mode active (LMA) and long mode enabled (LME). sregs.efer = EFER_LMA | EFER_LME; } fn main() -> std::io::Result<()> { // Create VM & VCPU. let vm = Kvm::new()?.create_vm()?; let mut vcpu = vm.create_vpcu(0)?; // Map memory for guest VM. let mut mem = UserMem::new(0x8000)?; unsafe { vm.set_user_memory_region(PhysAddr(0x0), &mem)?; } // Load guest image at physical address starting from 0x4000. mem.load(PhysAddr(0x4000), include_bytes!("../guest/guest64")); // Initialize VPCU registers. let mut regs = vcpu.get_regs()?; // Set `rip` to 0 as we want to start executing from virtual address 0. regs.rip = 0; regs.rflags = 0x2; vcpu.set_regs(regs)?; // Initialize VPCU special registers. let mut sregs = vcpu.get_sregs()?; // Setup long mode and paging to map: // VirtAddr [0x0000:0x3fff] -> PhysAddr [0x4000:0x7fff] setup_long_mode(&mut sregs, &mut mem); vcpu.set_sregs(sregs)?; // Run VCPU until `hlt` instruction. while let Ok(exit) = vcpu.run() { match exit { KvmExit::Halt => break, KvmExit::IoIn(port, data) => { println!("IO_IN: port={} len={}", port, data.len()); // Provide some input data. data.fill(0xaa); } KvmExit::IoOut(_port, data) => { let s = std::str::from_utf8(data).unwrap(); print!("{}", s); } KvmExit::MmioRead(addr, data) => { println!("MMIO_READ: addr={:#x} len={}", addr, data.len()); // Provide some read data. data.fill(0xbb); } KvmExit::MmioWrite(addr, data) => { println!( "MMIO_WRITE: addr={:#x} len={} data={:#x?}", addr, data.len(), data ); } KvmExit::Debug(_pc) => {} }; } // The guest writes at virtual address [0x2000 - 0x2003] which will be visible in physical // memory at [0x6000 - 0x6003] due to the paging structure we setup. // See `setup_long_mode_4level_paging` above for details. assert_eq!( &mem.as_ref()[0x4000 + 0x2000..][..4], &[0xaa, 0xbb, 0xcc, 0xdd] ); Ok(()) }