diff --git a/Cargo.toml b/Cargo.toml index a7776206..9f868788 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,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 } @@ -37,3 +41,7 @@ serde_json = { version = "0.6.0", optional = true } bitflags = "0.7" raw-cpuid = "2.*" phf = { version = "0.7.7", optional = true, features = ["core"] } + +[dev-dependencies] +kvm = "0.2" +memmap = "0.2.1" diff --git a/test.sh b/test.sh new file mode 100644 index 00000000..95242582 --- /dev/null +++ b/test.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +rm target/debug/kvm-1204d1237e8c62b6 +export CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER="$(dirname $0)/tests/kvm/linker.sh" +cargo test --test kvm -- "$@" diff --git a/tests/kvm/bin.rs b/tests/kvm/bin.rs new file mode 100644 index 00000000..97d57c44 --- /dev/null +++ b/tests/kvm/bin.rs @@ -0,0 +1,112 @@ +#![feature(linkage, naked_functions, asm)] +// In this example we will construct a single CPU x86 VM which will execute +// "inb 0x01" at ring 0 with paging disabled + +extern crate kvm; +extern crate memmap; + +use kvm::{Capability, Exit, IoDirection, System, Vcpu, VirtualMachine}; +use memmap::{Mmap, Protection}; + +//#[allow(improper_ctypes)] +extern { + static kvmtest_begin_loc: u8; + static kvmtest_end_loc: u8; +} + +#[naked] +//#[allow(dead_code)] +#[link_section = ".kvmtest"] +#[linkage = "external" ] +unsafe extern "C" fn use_the_port() { + asm!("inb $0, %al" :: "i"(0x01) :: "volatile"); +} + +#[test] +fn io_example() { + // Allocate 2MB for the guest memory + let mut anon_mmap = Mmap::anonymous(2 * (1 << 20), Protection::ReadWrite) + .unwrap(); + let slice = unsafe { anon_mmap.as_mut_slice() }; + + let slice2: &mut [u8] = { + let kvmtest_begin: *mut u8 = &kvmtest_begin_loc as *const _ as _; + let kvmtest_end: *mut u8 = &kvmtest_end_loc as *const _ as _; + unsafe { + ::std::slice::from_raw_parts_mut( + kvmtest_begin, + (kvmtest_end as usize) - (kvmtest_begin as usize)) + } + }; + + slice[0x100000..][..slice2.len()].copy_from_slice(slice2); + + println!("fn: {:x}", use_the_port as usize); // make sure is used + + println!("slice: {:?}", slice2.as_ptr()); + println!("len: {:x}", slice2.len()); + println!("{:x}", slice[0]); + println!("{:x}", slice[1]); + println!("{:?}", &slice[..10]); + + // 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); + // Set the 2 MB range to start at physical address 0 + vm.set_user_memory_region(0, 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(); + 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) + sregs.cs.base = 0x0; + sregs.cs.limit = 0xffffffff; + sregs.cs.selector = 0x8; + sregs.cs._type = 0xb; + sregs.cs.present = 1; + sregs.cs.dpl = 0; + sregs.cs.db = 0; + sregs.cs.s = 1; + sregs.cs.l = 0; + sregs.cs.g = 1; + sregs.cs.avl = 0; + + // We don't need to populate the GDT if we have our segments setup + // cr0 - protected mode on, paging disabled + sregs.cr0 = 0x50033; + + // 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 = 0x100000; + regs.rflags = 0x2; + 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 + 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()); + } +} diff --git a/tests/kvm/link.ld b/tests/kvm/link.ld new file mode 100644 index 00000000..39937782 --- /dev/null +++ b/tests/kvm/link.ld @@ -0,0 +1,16 @@ +/*PHDRS +{ + test PT_LOAD FLAGS(0x7); /* rwx 0b111 *//* +}*/ + +SECTIONS +{ + .kvmtest : + { + kvmtest_begin_loc = .; + *(.kvmtest*) + kvmtest_end_loc = .; + } /* :test */ +} + +INSERT AFTER .rodata; /* do not override default script */ diff --git a/tests/kvm/linker.sh b/tests/kvm/linker.sh new file mode 100644 index 00000000..3bd2f5dc --- /dev/null +++ b/tests/kvm/linker.sh @@ -0,0 +1,5 @@ +#!/bin/sh +set -x + +${CC:=cc} +$CC -T $(dirname $0)/link.ld "$@"