aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.toml5
-rw-r--r--test_harness/Cargo.toml3
-rw-r--r--test_harness/src/lib.rs206
-rw-r--r--test_macros/src/lib.rs43
-rw-r--r--tests/kvm/bin.rs21
5 files changed, 192 insertions, 86 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 8535e13..31a5e72 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -46,11 +46,6 @@ version = "0.7.7"
optional = true
features = ["core"]
-#[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]
klogger = { git = "https://github.com/gz/rust-klogger.git" }
test = { path = "test_harness" }
diff --git a/test_harness/Cargo.toml b/test_harness/Cargo.toml
index cac40e8..2e9e739 100644
--- a/test_harness/Cargo.toml
+++ b/test_harness/Cargo.toml
@@ -8,4 +8,5 @@ syn = "0.11.*"
quote = "0.3.*"
x86 = "0.*"
memmap = "0.2.1"
-kvm = { path = "../../kvm" }
+kvm = { git = "https://github.com/gz/kvm.git" }
+#kvm = { path = "../../kvm" }
diff --git a/test_harness/src/lib.rs b/test_harness/src/lib.rs
index 9e2d745..4456cbb 100644
--- a/test_harness/src/lib.rs
+++ b/test_harness/src/lib.rs
@@ -7,17 +7,23 @@ 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 std::io::{BufRead, BufReader, Write};
+use std::io;
use x86::shared::control_regs::*;
use x86::shared::paging::*;
use x86::bits64::paging::*;
+#[no_mangle]
+#[used]
+pub static mut __TEST_PANICKED: bool = false;
struct PageTable {
backing_memory: Mmap
}
+
type PageTableMemoryLayout = (PML4, [PDPT; 512]);
+
static PAGE_TABLE_P: PAddr = PAddr::from_u64(0x1000); // XXX:
impl PageTable {
@@ -73,6 +79,7 @@ impl PageTable {
struct Stack {
backing_memory: Mmap
}
+
static STACK_BASE_T: PAddr = PAddr::from_u64(0x2000000);
impl Stack {
@@ -227,6 +234,7 @@ pub struct KvmTestMetaData {
pub meta: &'static str,
pub identity_map: bool,
pub physical_memory: (u64, u64),
+ pub ioport_reads: (u16, u32),
}
/// Linker generates symbols that are inserted at the start and end of the kvm section.
@@ -272,7 +280,7 @@ pub fn test_ignored(name: &str) {
}
pub fn test_before_run(name: &str) -> Option<&KvmTestMetaData> {
- print!("test {} ... ", name);
+ println!("test {} ... ", name);
find_meta_data(name)
}
@@ -303,77 +311,143 @@ pub fn test_summary(passed: usize, failed: usize, ignored: usize) {
}
}
-#[no_mangle]
-#[used]
-pub static mut __TEST_PANICKED: bool = false;
+struct SerialPrinter {
+ buffer: String
+}
+
+impl Write for SerialPrinter {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ assert!(buf.len() == 1);
+ self.buffer.push(buf[0] as char);
+ match buf[0] as char {
+ '\n' => {
+ std::io::stdout().write(self.buffer.as_bytes());
+ self.buffer.clear();
+ }
+ _ => {}
+ }
+
+ Ok(1)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ std::io::stdout().write(self.buffer.as_bytes());
+ self.buffer.clear();
+ Ok(())
+ }
+}
+
+impl SerialPrinter {
+ fn new() -> SerialPrinter {
+ SerialPrinter { buffer: String::new() }
+ }
+}
+
+#[derive(Debug)]
+enum IoHandleError {
+ UnexpectedWrite(u16, u32),
+ UnexpectedRead(u16)
+}
+
+enum IoHandleStatus {
+ Handled,
+ TestCompleted,
+}
+
+fn handle_ioexit(meta: &KvmTestMetaData, cpu: &mut Vcpu, run: &kvm::Run, printer: &mut SerialPrinter) -> Result<IoHandleStatus, IoHandleError> {
+ let io = unsafe { *run.io() };
+
+ match io.direction {
+ IoDirection::In => {
+ let mut regs = cpu.get_regs().unwrap();
+ if io.port == 0x3fd {
+ regs.rax = 0x20; // Mark serial line ready to write
+ cpu.set_regs(&regs).unwrap();
+ return Ok(IoHandleStatus::Handled);
+ }
+ else if io.port == meta.ioport_reads.0 {
+ regs.rax = meta.ioport_reads.1 as u64;
+ cpu.set_regs(&regs).unwrap();
+ return Ok(IoHandleStatus::Handled);
+ }
+ return Err(IoHandleError::UnexpectedRead(io.port));
+ }
+ IoDirection::Out => {
+ let regs = cpu.get_regs().unwrap();
+ if io.port == 0x3f8 {
+ printer.write(&[regs.rax as u8]);
+ return Ok(IoHandleStatus::Handled);
+ }
+ else if io.port == 0xf4 && regs.rax as u8 == 0x0 {
+ // Magic shutdown command for exiting the test.
+ // The line unsafe { x86::shared::io::outw(0xf4, 0x00); }
+ // is automatically inserted at the end of every test!
+ return Ok(IoHandleStatus::TestCompleted);
+ }
+
+ return Err(IoHandleError::UnexpectedWrite(io.port, regs.rax as u32));
+ }
+ };
+}
pub fn test_main_static(tests: &[TestDescAndFn]) {
- unsafe {
- test_start(tests.len());
-
- let mut failed = 0;
- let mut ignored = 0;
- let mut passed = 0;
- for test in tests {
- if test.desc.ignore {
- ignored += 1;
- test_ignored(test.desc.name.0);
- } else {
- let meta_data = test_before_run(test.desc.name.0);
-
- __TEST_PANICKED = false;
- match meta_data {
- Some(mtd) => {
- let sys = System::initialize().unwrap();
- let mut st = Stack::new();
- let mut pt = PageTable::new();
- pt.setup_identity_mapping();
-
- let mut test_environment = TestEnvironment::new(&sys, &mut st, &mut pt);
- let test_fn_vaddr = VAddr::from_usize(test.testfn.0 as *const () as usize);
- let mut vcpu = test_environment.create_vcpu(test_fn_vaddr);
-
- let mut vm_is_done = false;
- let mut new_regs = kvm::Regs::default();
- while !vm_is_done {
- {
- let (run, mut regs) = unsafe { vcpu.run_regs() }.unwrap();
- match run.exit_reason {
- Exit::Io => {
- let io = unsafe { *run.io() };
- match io.direction {
- IoDirection::In => {
- if io.port == 0x3fd {
- regs.rax = 0x20; // Mark serial line as ready to write
- } else {
- println!("IO on unknown port: {}", io.port);
- }
- }
- IoDirection::Out => {
- if io.port == 0x3f8 {
- println!("got char {:#?}", regs.rax as u8 as char);
- }
- }
- }
- }
- Exit::Shutdown => {
- println!("Shutting down");
+ test_start(tests.len());
+
+ let mut failed = 0;
+ let mut ignored = 0;
+ let mut passed = 0;
+ for test in tests {
+ if test.desc.ignore {
+ ignored += 1;
+ test_ignored(test.desc.name.0);
+ } else {
+ let meta_data = test_before_run(test.desc.name.0);
+
+ unsafe { __TEST_PANICKED = false; }
+
+ match meta_data {
+ // Run this in a Virtual machine
+ Some(mtd) => {
+ let sys = System::initialize().unwrap();
+ let mut st = Stack::new();
+ let mut pt = PageTable::new();
+ pt.setup_identity_mapping();
+ let mut test_environment = TestEnvironment::new(&sys, &mut st, &mut pt);
+ let mut printer: SerialPrinter = SerialPrinter::new();
+
+ let test_fn_vaddr = VAddr::from_usize(test.testfn.0 as *const () as usize);
+ let mut vcpu = test_environment.create_vcpu(test_fn_vaddr);
+
+ let mut vm_is_done = false;
+ while !vm_is_done {
+ let run = unsafe { vcpu.run() }.unwrap();
+ match run.exit_reason {
+ Exit::Io => {
+ match handle_ioexit(&mtd, &mut vcpu, &run, &mut printer) {
+ Result::Ok(IoHandleStatus::Handled) => {/* Continue */}
+ Result::Ok(IoHandleStatus::TestCompleted) => vm_is_done = true,
+ Result::Err(err) => {
+ println!("Test failed due to unexpected IO: {:?}", err);
vm_is_done = true;
}
- _ => {
- println!("Unknown exit reason: {:?}", run.exit_reason);
- }
}
-
- new_regs = regs;
+ },
+ Exit::Shutdown => {
+ println!("Exit::Shutdown");
+ vm_is_done = true;
+ }
+ _ => {
+ println!("Unknown exit reason: {:?}", run.exit_reason);
}
- vcpu.set_regs(&new_regs).unwrap();
}
+ }
- },
- _ => test.testfn.0() // Regular test, not running inside virtual machine
- }
+ },
+ // Regular test, execute as usual:
+ _ => test.testfn.0() // Regular test, not running inside virtual machine
+ }
+ unsafe {
if __TEST_PANICKED == (test.desc.should_panic == ShouldPanic::Yes) {
passed += 1;
test_success(test.desc.name.0);
@@ -382,11 +456,11 @@ pub fn test_main_static(tests: &[TestDescAndFn]) {
test_failed(test.desc.name.0);
}
}
-
}
- test_summary(passed, failed, ignored);
}
+
+ test_summary(passed, failed, ignored);
}
// required for compatibility with the `rustc --test` interface
diff --git a/test_macros/src/lib.rs b/test_macros/src/lib.rs
index a211112..bd89f87 100644
--- a/test_macros/src/lib.rs
+++ b/test_macros/src/lib.rs
@@ -31,8 +31,9 @@ use syn::parse::IResult;
use std::string;
use proc_macro::TokenStream;
use quote::ToTokens;
+use std::collections::HashMap;
-fn parse_kvmtest_args(args: &syn::DeriveInput) -> ((u64, u64), bool) {
+fn parse_kvmtest_args(args: &syn::DeriveInput) -> ((u64, u64), (u16, u32), bool) {
if args.ident.as_ref() != "Dummy" {
panic!("Get rid of this hack!");
}
@@ -40,6 +41,8 @@ fn parse_kvmtest_args(args: &syn::DeriveInput) -> ((u64, u64), bool) {
// If syn ever implements visitor for MetaItems, this can probably be written simpler:
let mut identity_map: bool = false;
let mut physical_memory: Vec<u64> = Vec::with_capacity(2);
+ let mut ioport_reads: Vec<u32> = vec![0,0];
+
for attr in &args.attrs {
match attr.value {
syn::MetaItem::List(ref name, ref kvmattrs) => {
@@ -59,7 +62,18 @@ fn parse_kvmtest_args(args: &syn::DeriveInput) -> ((u64, u64), bool) {
&syn::NestedMetaItem::Literal(syn::Lit::Int(n, _)) => {
physical_memory.push(n);
},
- _ => panic!("Type mismatch in ram().")
+ _ => panic!("Type mismatch in ram() arguments.")
+ }
+ }
+ }
+ "ioport" => {
+ ioport_reads.clear();
+ for (idx, innerattr) in innerattrs.iter().enumerate() {
+ match innerattr {
+ &syn::NestedMetaItem::Literal(syn::Lit::Int(n, _)) => {
+ ioport_reads.push(n as u32);
+ },
+ _ => panic!("Type mismatch in ioport() arguments.")
}
}
}
@@ -87,8 +101,11 @@ fn parse_kvmtest_args(args: &syn::DeriveInput) -> ((u64, u64), bool) {
if physical_memory.len() != 2 {
panic!("ram() takes two values (x,y)");
}
+ if ioport_reads.len() != 2 {
+ panic!("in() takes two values (x,y)");
+ }
- ((physical_memory[0], physical_memory[1]) , identity_map)
+ ((physical_memory[0], physical_memory[1]), (ioport_reads[0] as u16, ioport_reads[1]), identity_map)
}
/// Add a additional meta-data and setup functions for a KVM based test.
@@ -98,7 +115,7 @@ fn generate_kvmtest_meta_data(test_ident: &syn::Ident, args: &syn::DeriveInput)
let setup_fn_ident = syn::Ident::new(String::from(test_name) + "_setup");
let struct_ident = syn::Ident::new(String::from(test_name) + "_kvm_meta_data");
- let (physical_memory, identity_map) = parse_kvmtest_args(args);
+ let (physical_memory, ioport_reads, identity_map) = parse_kvmtest_args(args);
(struct_ident.clone(),
quote! {
@@ -108,7 +125,8 @@ fn generate_kvmtest_meta_data(test_ident: &syn::Ident, args: &syn::DeriveInput)
mbz: 0,
meta: #test_name,
identity_map: #identity_map,
- physical_memory: #physical_memory
+ physical_memory: #physical_memory,
+ ioport_reads: #ioport_reads
};
})
@@ -130,6 +148,19 @@ fn insert_meta_data_reference(struct_ident: &syn::Ident, test_block: &mut syn::B
test_block.stmts.insert(0, stmt);
}
+/// Inserts an IO out (outw) instruction at the end of the test function
+/// that writes to port 0xf4 with value 0x0. outw will cause a vmexit.
+/// This particular port, payload combination is handled as a special case
+/// in the test runner to signal that the test has completed.
+fn insert_test_shutdown(struct_ident: &syn::Ident, test_block: &mut syn::Block) {
+ let stmt_exit_test = String::from("unsafe { x86::shared::io::outw(0xf4, 0x00); }");
+ let stmt = match syn::parse::stmt(stmt_exit_test.as_str()) {
+ IResult::Done(stmt_str, stmt) => stmt,
+ IResult::Error => panic!("Unable to generate test exit instruction."),
+ };
+ test_block.stmts.push(stmt);
+}
+
#[proc_macro_attribute]
pub fn kvmattrs(args: TokenStream, input: TokenStream) -> TokenStream {
@@ -141,6 +172,7 @@ pub fn kvmattrs(args: TokenStream, input: TokenStream) -> TokenStream {
// FIXME, see also https://github.com/dtolnay/syn/issues/86
let derive_input_hack = format!("#[kvmattrs{}] struct Dummy;", args);
let args_ast = syn::parse_derive_input(&derive_input_hack).unwrap();
+ println!("1");
// Generate meta-data struct
let ident = ast.ident.clone();
@@ -150,6 +182,7 @@ pub fn kvmattrs(args: TokenStream, input: TokenStream) -> TokenStream {
match &mut ast.node {
&mut syn::ItemKind::Fn(_, _, _, _, _, ref mut block) => {
insert_meta_data_reference(&meta_data_ident, block);
+ insert_test_shutdown(&meta_data_ident, block);
}
_ => panic!("Not a function!"),
};
diff --git a/tests/kvm/bin.rs b/tests/kvm/bin.rs
index edd4095..dd16dfa 100644
--- a/tests/kvm/bin.rs
+++ b/tests/kvm/bin.rs
@@ -1,31 +1,34 @@
-#![feature(linkage, naked_functions, asm, const_fn, proc_macro, used)]
-//RUSTFLAGS="-C relocation-model=dynamic-no-pic -C code-model=kernel" RUST_BACKTRACE=1 cargo test --verbose --test kvm -- --nocapture
+#![feature(proc_macro)]
+// RUSTFLAGS="-C relocation-model=dynamic-no-pic -C code-model=kernel" RUST_BACKTRACE=1 cargo test --verbose --test kvm -- --nocapture
extern crate x86;
extern crate core;
-
#[macro_use]
extern crate klogger;
extern crate test_macros;
use test_macros::kvmattrs;
-
-// Needed for code generated by `kvmattrs`
+// Needed for code generated by `kvmattrs`:
extern crate test;
use self::test::KvmTestMetaData;
#[test]
-#[kvmattrs(identity_map, ram(0x30000000, 0x31000000))]
+#[kvmattrs(identity_map, ram(0x30000000, 0x31000000), ioport(0x1, 0xfe))]
fn use_the_port() {
- log!("1");
+ sprintln!("1");
unsafe {
- asm!("inb $0, %al" :: "i"(0x01) :: "volatile");
+ if (x86::shared::io::inw(0x1) == 0xfe) {
+ sprintln!("worked");
+ }
}
+ sprintln!("2");
}
#[test]
#[kvmattrs(identity_map, ram(0x30000000, 0x31000000))]
fn io_example2() {
- assert!(1==1);
+ sprintln!("1");
+ //assert!(1 == 0);
+ sprintln!("2");
}