From 7acde708e88ab69acfe1b0827ec0158120dd9862 Mon Sep 17 00:00:00 2001 From: Gerd Zellweger Date: Mon, 22 May 2017 16:09:59 +0200 Subject: [PATCH] KVM test executing naked function. Signed-off-by: Gerd Zellweger --- Cargo.toml | 16 +++- tests/kvm/bin.rs | 194 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 209 insertions(+), 1 deletion(-) create mode 100644 tests/kvm/bin.rs diff --git a/Cargo.toml b/Cargo.toml index 43f1809a..304c180e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,4 @@ [package] - name = "x86" version = "0.9.0" authors = [ @@ -29,6 +28,10 @@ performance-counter = ["phf", "phf_codegen", "csv", "serde_json"] name = "no_std_build" harness = false +[[test]] +name = "kvm" +path = "tests/kvm/bin.rs" + [build-dependencies] phf_codegen = { version = "0.7.7", optional = true } csv = { version = "0.14.3", optional = true } @@ -42,3 +45,14 @@ raw-cpuid = "2.*" version = "0.7.7" optional = true features = ["core"] + +#[target.x86_64-utest-qemu.dev-dependencies.test] +#path = "../utest" +#[target.x86_64-utest-qemu.dev-dependencies.utest-macros] +#path = "../utest/macros" +#[target.x86_64-utest-qemu.dev-dependencies.utest-x86-64-qemu] +#path = "../utest/x86-64-qemu" + +[dev-dependencies] +kvm = "0.2" # { path = "../kvm/" } +memmap = "0.2.1" diff --git a/tests/kvm/bin.rs b/tests/kvm/bin.rs new file mode 100644 index 00000000..f03f83dd --- /dev/null +++ b/tests/kvm/bin.rs @@ -0,0 +1,194 @@ +#![feature(linkage, naked_functions, asm, const_fn)] +// In this example we will construct a single CPU x86 VM which will execute +// "inb 0x01" at ring 0 + +extern crate kvm; +extern crate memmap; +extern crate x86; + +use kvm::{Capability, Exit, IoDirection, Segment, System, Vcpu, VirtualMachine}; +use memmap::{Mmap, Protection}; +use std::fs::File; +use std::io::{BufRead, BufReader}; + +use x86::shared::control_regs::*; +use x86::shared::paging::*; +use x86::bits64::paging::*; + +#[naked] +#[no_mangle] +unsafe extern "C" fn use_the_port() { + asm!("inb $0, %al" :: "i"(0x01) :: "volatile"); +} + +#[test] +fn io_example() { + let vaddr = VAddr::from_usize(use_the_port as *const () as usize); + static PAGE_TABLE_P: PAddr = PAddr::from_u64(0x1000); + + // Set up a page table that identity maps the lower half of the address space + let mut anon_mmap = Mmap::anonymous(3 * (1 << 20), Protection::ReadWrite).unwrap(); // Map 1 MiB + let page_table_memory = unsafe { anon_mmap.as_mut_slice() }; + + // "physical" layout of PageTable is: PML4Table at 0x1000, 1st PDPT at 0x2000, 2nd PDPT at 0x3000 ... 512th PDPT at 0x1000+512*0x1000 + type PageTable = (PML4, [PDPT; 512]); + + let page_table: &mut PageTable = + unsafe { ::std::mem::transmute(&mut page_table_memory[PAGE_TABLE_P.as_u64() as usize]) }; + let (ref mut pml4, ref mut pdpts) = *page_table; + // Identity map everything in PML4 slots 0..256 + for i in 0..512 { + let offset = 0x2000 + 0x1000 * i; + pml4[i] = PML4Entry::new(PAddr::from_u64(offset as _), PML4_P | PML4_RW); + let pdpt = &mut pdpts[i]; + for j in 0..512 { + pdpt[j] = PDPTEntry::new(PAddr::from_u64(((512 * i + j) as u64) << 30), + PDPT_P | PDPT_RW | PDPT_PS); // Set-up 1 GiB page mappings + if i == pml4_index(vaddr) && j == pdpt_index(vaddr) { + println!("pml4_index(fn) {:x}", pml4[i].get_address()); + println!("pdpt_index(fn) {:x}", pdpt[j].get_address()); + } + } + } + let mut anon_mmap2 = Mmap::anonymous((1 << 20), Protection::ReadWrite).unwrap(); // Map 1 MiB + + // Initialize the KVM system + let sys = System::initialize().unwrap(); + + // Create a Virtual Machine + let mut vm = VirtualMachine::create(&sys).unwrap(); + + // Ensure that the VM supports memory backing with user memory + assert!(vm.check_capability(Capability::UserMemory) > 0); + + // Once the memory is set we can't even call length. + let page_table_memory_limit = page_table_memory.len() - 1; + + // Map the page table memory + vm.set_user_memory_region(0, page_table_memory, 0).unwrap(); + + + /*let code_phys: PAddr = PAddr::from_u64(PAGE_TABLE_P.as_u64() + page_table_memory_limit as u64 + + 1); + let code_memory = unsafe { anon_mmap2.as_mut_slice() }; + code_memory[0x0] = 0xe4; + code_memory[0x1] = 0x01; + // Map the code + println!("Code is at {:x}", code_phys); + //0x555555554000 + let code_phys = PAddr::from_u64(0x8000000000); + vm.set_user_memory_region(code_phys.as_u64(), code_memory, 0).unwrap();*/ + + + // Map the process + let f = File::open("/proc/self/maps").unwrap(); + let reader = BufReader::new(f); + + for line in reader.lines() { + let line = line.unwrap(); + println!("{}", line); + let mut s = line.split(' '); + let mut s2 = s.next().unwrap().split('-'); + let begin = usize::from_str_radix(s2.next().unwrap(), 16).unwrap(); + let end = usize::from_str_radix(s2.next().unwrap(), 16).unwrap(); + if end < 0x800000000000 { + let perm = s.next().unwrap(); + //println!("{:#X}-{:#X} {}", begin, end, perm); + let slice = { + let begin_ptr: *mut u8 = begin as *const u8 as _; + unsafe { ::std::slice::from_raw_parts_mut(begin_ptr, end - begin) } + }; + // Make sure process doesn't overlap with page table + assert!(begin > page_table_memory_limit); + vm.set_user_memory_region(begin as _, slice, 0).unwrap(); + } + } + + // Create a new VCPU + let mut vcpu = Vcpu::create(&mut vm).unwrap(); + + // Set supported CPUID (KVM fails without doing this) + let mut cpuid = sys.get_supported_cpuid().unwrap(); + /*for mut entry in cpuid.entries_mut() { + if entry.function == 0x80000008 { + entry.eax = entry.eax | 0xff; + } + println!("{:?}", entry); + }*/ + vcpu.set_cpuid2(&mut cpuid).unwrap(); + + // Setup the special registers + let mut sregs = vcpu.get_sregs().unwrap(); + + // Set the code segment to have base 0, limit 4GB (flat segmentation) + let segment_template = Segment { + base: 0x0, + limit: 0xffffffff, + selector: 0, + _type: 0, + present: 0, + dpl: 0, + db: 1, + s: 0, + l: 0, + g: 1, + avl: 0, + ..Default::default() + }; + + sregs.cs = Segment { + selector: 0x8, + _type: 0xb, + present: 1, + db: 0, + s: 1, + l: 1, + ..segment_template + }; + sregs.ss = Segment { ..segment_template }; + sregs.ds = Segment { ..segment_template }; + sregs.es = Segment { ..segment_template }; + sregs.fs = Segment { ..segment_template }; + sregs.gs = Segment { ..segment_template }; + + // We don't need to populate the GDT if we have our segments setup + // cr0 - protected mode on, paging enabled + sregs.cr0 = (CR0_PROTECTED_MODE | CR0_MONITOR_COPROCESSOR | CR0_EXTENSION_TYPE | + CR0_ENABLE_PAGING | CR0_NUMERIC_ERROR | CR0_WRITE_PROTECT | + CR0_ALIGNMENT_MASK | CR0_ENABLE_PAGING) + .bits() as u64; + sregs.cr3 = PAGE_TABLE_P.as_u64(); + sregs.cr4 = (CR4_ENABLE_PSE | CR4_ENABLE_PAE | CR4_ENABLE_GLOBAL_PAGES | CR4_ENABLE_SSE | + CR4_UNMASKED_SSE | + CR4_ENABLE_OS_XSAVE | CR4_ENABLE_SMEP | CR4_ENABLE_VME) + .bits() as u64; + sregs.efer = 0xd01; + + // Set the special registers + vcpu.set_sregs(&sregs).unwrap(); + + let mut regs = vcpu.get_regs().unwrap(); + // set the instruction pointer to 1 MB + regs.rip = vaddr.as_usize() as u64; + println!("regs.rip = 0x{:x}", regs.rip); // but is at: 0x40cd60 + //println!("regs.rip = 0x{:x}", unsafe { *(regs.rip as *const u64) }); // but is at: 0x40cd60 + //regs.rip = 0x40cd60; + regs.rflags = 0x246; + vcpu.set_regs(®s).unwrap(); + + // Actually run the VCPU + let run = unsafe { vcpu.run() }.unwrap(); + + // Ensure that the exit reason we get back indicates that the I/O + // instruction was executed + println!("run {:?}", run); + assert!(run.exit_reason == Exit::Io); + let io = unsafe { *run.io() }; + assert!(io.direction == IoDirection::In); + assert!(io.size == 1); + assert!(io.port == 0x1); + unsafe { + println!("{:#?}", *run.io()); + } + +}