# x86test custom test runner x86test is a custom test runner that allows you to write unit tests which use privileged (x86) instructions. It achieves that as follows: for every unit test it creates a tiny VM (using kvm) which mirrors the address space of the current test process inside the guest VM. Next the VM is initialized and jumps to the unit test function which is now executed in guest ring 0 (and here you can use all your fancy instructions). Finally, once the test returns (or panics), control is transferred back from the VM to our test runner. Funky? Yes. Is it hard to use? No! It integrates neatly with rust thanks to the rust custom test framework and procedural macros. See the example below. Does it work? It has limitations (this is expected you're running on bare-metal x86), so don't expect much infrastructure. For panic and assert you have to use special versions, also you can't use anything that does system calls (like println!, but a custom sprintln! macro is provided). ## An example This is particularly helpful to test the [x86 crate](https://github.com/gz/rust-x86). For example say we have a function like this: ```rust /// Read 16 bits from port #[inline] pub unsafe fn inw(port: u16) -> u16 { let ret: u16; asm!("inw %dx, %ax", in("dx") port, out("ax") ret, options(att_syntax)); ret } ``` The problem with `inw` is that it needs IO privilege level in E/RFlags to not cause an exception (and as a result crash the process). A regular Linux process will not run with this privilege level, however we can now write a x86test: ```rust #[x86test(ioport(0x1, 0xfe))] fn check_inw_port_read() { unsafe { kassert!( x86::io::inw(0x1) == 0xfe, "`inw` instruction didn't read the correct value" ); } } ``` A few things are happening here that warrant some explaining: First, instead of `#[test]` we used `#[x86test]` to tell the system we don't want to use regular unit tests. `x86test` supports a few arguments (more on that later), here we just tell the "hypervisor" of the test runner to install an ioport with port number 1 that shall always return 0xfe when being read. Next, comes our function declaration -- nothing special here -- followed by unsafe, just because `inw` is unsafe. Finally, we use `kassert!`, a custom assert macro that works in guest ring 0 for our hypervisor, to check that `inw` does the right thing. You'll find more example tests among the [x86 tests](../tests/kvm/bin.rs). Note that running a x86test currently works only on Linux and requires some linking magic. Setting `RUSTFLAGS="-C relocation-model=dynamic-no-pic -C code-model=kernel"` should do. I expect the custom `RUSTFLAGS` to not be necessary in the future. ## x86test reference The x86test attribute currently supports the following parameters: * `ioport(port, val)`: Reads to `port` will return `val`, writes to `port` other than `val` will fail the test. * `ram(from, to)`: Adds physical memory in address range `from` -- `to` * `should_halt`: To tell the hypervisor that the test will halt (note: use like this `#[x86test(should_halt)]`). * `#[should_panic]`: Can be added if a test is expected to panic. ## Code Organization * [x86test_macro](x86test_macro): contains a procedural macro implementation of `x86test`. * [x86test_types](x86test_types): contains implementations of kassert, kpanic and the X86TestFn struct. * [src](src): contains the custom test runner implementation. ## Updating Should be done in the following order: * Release new version of `x86test-types` * Release new version of `x86test-macro` (adjust version dependency of x86test-types) * Release new version of `x86test` (adjust version dependency of x86test-types and x86test-macro) * Tag with `git tag x86test-0.0.x`