Skip to content

Commit 4216e68

Browse files
committed
Working on test harness setup for KVM #20.
Signed-off-by: Gerd Zellweger <[email protected]>
1 parent 3ba6f9d commit 4216e68

File tree

5 files changed

+402
-234
lines changed

5 files changed

+402
-234
lines changed

Cargo.toml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,6 @@ features = ["core"]
5252
#path = "../utest/x86-64-qemu"
5353

5454
[dev-dependencies]
55-
memmap = "0.2.1"
56-
kvm = { git = "https://github.com/gz/kvm.git" }
5755
klogger = { git = "https://github.com/gz/rust-klogger.git" }
5856
test = { path = "test_harness" }
5957
test-macros = { path = "test_macros" }

test_harness/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,6 @@ authors = ["Gerd Zellweger <[email protected]>"]
66
[dependencies]
77
syn = "0.11.*"
88
quote = "0.3.*"
9+
x86 = "0.*"
10+
memmap = "0.2.1"
11+
kvm = { path = "../../kvm" }

test_harness/src/lib.rs

Lines changed: 310 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,329 @@
1-
#![feature(used, lang_items)]
1+
#![feature(used, lang_items, const_fn)]
2+
3+
extern crate kvm;
4+
extern crate memmap;
5+
extern crate x86;
6+
7+
use kvm::{Capability, Exit, IoDirection, Segment, System, Vcpu, VirtualMachine};
8+
use memmap::{Mmap, Protection};
9+
use std::fs::File;
10+
use std::io::{BufRead, BufReader};
11+
12+
use x86::shared::control_regs::*;
13+
use x86::shared::paging::*;
14+
use x86::bits64::paging::*;
15+
16+
17+
struct PageTable {
18+
backing_memory: Mmap
19+
}
20+
type PageTableMemoryLayout = (PML4, [PDPT; 512]);
21+
static PAGE_TABLE_P: PAddr = PAddr::from_u64(0x1000); // XXX:
22+
23+
impl PageTable {
24+
25+
/// Allocated a chunk of memory to contain a basic page-table setup that covers the whole
26+
/// address space
27+
fn new() -> PageTable {
28+
let mut anon_mmap = Mmap::anonymous(3 * (1 << 20), Protection::ReadWrite).unwrap(); // Map 1 MiB
29+
PageTable { backing_memory: anon_mmap }
30+
}
31+
32+
fn as_mut_slice<'a>(&'a mut self) -> &'a mut [u8] {
33+
unsafe { self.backing_memory.as_mut_slice() }
34+
}
35+
36+
/// Constructs a simple page-table that identity maps
37+
/// the whole address space (guest virtual <-> guest physical).
38+
fn setup_identity_mapping(&mut self) {
39+
let page_table_memory = self.as_mut_slice();
40+
41+
// "physical" layout of PageTable is:
42+
// PML4Table at 0x1000
43+
// 1st PDPT at 0x2000
44+
// 2nd PDPT at 0x3000 ...
45+
// 512th PDPT at 0x1000+512*0x1000
46+
// XXX: Can this be simpler why the offset?
47+
let page_table: &mut PageTableMemoryLayout =
48+
unsafe { ::std::mem::transmute(&mut page_table_memory[PAGE_TABLE_P.as_u64() as usize]) };
49+
50+
// Identity map everything in our PML4:
51+
let (ref mut pml4, ref mut pdpts) = *page_table;
52+
for (i, mut pdpt) in pdpts.iter_mut().enumerate() {
53+
let offset = 0x2000 + 0x1000 * i;
54+
pml4[i] = PML4Entry::new(PAddr::from_u64(offset as _), PML4_P | PML4_RW);
55+
56+
for j in 0..512 {
57+
// Set-up 1 GiB page-mappings:
58+
pdpt[j] = PDPTEntry::new(PAddr::from_u64(((512 * i + j) as u64) << 30),
59+
PDPT_P | PDPT_RW | PDPT_PS);
60+
}
61+
}
62+
}
63+
}
64+
65+
struct Stack {
66+
backing_memory: Mmap
67+
}
68+
static STACK_BASE_T: PAddr = PAddr::from_u64(0x2000000);
69+
70+
impl Stack {
71+
72+
/// Allocated a chunk of memory to contain a basic page-table setup that covers the whole
73+
/// address space
74+
fn new() -> Stack {
75+
let mut stack_mmap = Mmap::anonymous(65536, Protection::ReadWrite).unwrap();
76+
Stack { backing_memory: stack_mmap }
77+
}
78+
79+
fn as_mut_slice<'a>(&'a mut self) -> &'a mut [u8]{
80+
unsafe { self.backing_memory.as_mut_slice() }
81+
}
82+
83+
fn size(&self) -> usize {
84+
self.backing_memory.len()
85+
}
86+
87+
fn base(&self) -> VAddr {
88+
VAddr::from_usize(self.backing_memory.ptr() as usize)
89+
}
90+
}
91+
92+
struct TestEnvironment<'a> {
93+
sys: &'a System,
94+
pt: &'a mut PageTable,
95+
st: &'a mut Stack,
96+
vm: VirtualMachine<'a>,
97+
}
98+
99+
impl<'a> TestEnvironment<'a> {
100+
101+
fn new(sys: &'a System, st: &'a mut Stack, pt: &'a mut PageTable) -> TestEnvironment<'a> {
102+
let mut vm = VirtualMachine::create(sys).unwrap();
103+
// Ensure that the VM supports memory backing with user memory
104+
assert!(vm.check_capability(Capability::UserMemory) > 0);
105+
106+
/*let mut pt = PageTable::new();
107+
pt.setup_identity_mapping();
108+
let mut st = Stack::new();*/
109+
110+
TestEnvironment { pt: pt, st: st, sys: sys, vm: vm }
111+
}
112+
113+
/// Map the page table memory and stack memory
114+
fn map_memory(mut self) {
115+
self.vm.set_user_memory_region(0, self.pt.as_mut_slice(), 0).unwrap();
116+
self.vm.set_user_memory_region(STACK_BASE_T.as_u64(), self.st.as_mut_slice(), 0).unwrap();
117+
}
118+
}
2119

3120
#[derive(Debug)]
4121
pub struct KvmTestMetaData {
5122
pub mbz: u64,
6123
pub meta: &'static str,
124+
pub identity_map: bool,
125+
pub physical_memory: (u64, u64),
126+
}
127+
128+
/// Linker generates symbols that are inserted at the start and end of the kvm section.
129+
extern "C" {
130+
static __start_kvm: std::os::raw::c_void;
131+
static __stop_kvm: std::os::raw::c_void;
132+
}
133+
134+
/// Walks the kvm section to find see if there is a metha-data struct
135+
/// lying around for the given test name.
136+
fn find_meta_data(name: &str) -> Option<&KvmTestMetaData> {
137+
let (baseptr, size);
138+
139+
// Safe: the linker will take care of initializing these symbols
140+
unsafe {
141+
baseptr = &__start_kvm as *const _ as *const KvmTestMetaData;
142+
size = &__stop_kvm as *const _ as usize - baseptr as usize;
143+
}
144+
145+
let count = size / std::mem::size_of::<KvmTestMetaData>();
146+
147+
// Safe: The section points to (static) KvmTestMetaData descriptions
148+
unsafe {
149+
let mods = std::slice::from_raw_parts(baseptr, count);
150+
for m in mods.iter() {
151+
if m.meta == name {
152+
return Some(&m);
153+
}
154+
}
155+
}
156+
157+
None
7158
}
8159

160+
/// Start the test harness.
9161
pub fn test_start(ntests: usize) {
10-
println!("KVM testing: running {} tests", ntests)
162+
println!("running {} tests (using KVM support)", ntests)
11163
}
12164

165+
/// Signals that given test is ignored.
13166
pub fn test_ignored(name: &str) {
14167
println!("test {} ... ignored", name);
15168
}
16169

170+
171+
/*
172+
fn io_example() {
173+
174+
175+
// Initialize the KVM system
176+
let sys = System::initialize().unwrap();
177+
178+
// Create a Virtual Machine
179+
let mut vm = VirtualMachine::create(&sys).unwrap();
180+
181+
// Ensure that the VM supports memory backing with user memory
182+
assert!(vm.check_capability(Capability::UserMemory) > 0);
183+
184+
// Once the memory is set we can't even call length.
185+
let page_table_memory_limit = page_table_memory.len() - 1;
186+
187+
// Map the page table memory
188+
vm.set_user_memory_region(0, page_table_memory, 0).unwrap();
189+
// Map stack space
190+
vm.set_user_memory_region(STACK_BASE_T.as_u64(), stack_memory, 0).unwrap();
191+
192+
// Map the process
193+
let f = File::open("/proc/self/maps").unwrap();
194+
let reader = BufReader::new(f);
195+
196+
for line in reader.lines() {
197+
let line = line.unwrap();
198+
println!("{}", line);
199+
let mut s = line.split(' ');
200+
let mut s2 = s.next().unwrap().split('-');
201+
let begin = usize::from_str_radix(s2.next().unwrap(), 16).unwrap();
202+
let end = usize::from_str_radix(s2.next().unwrap(), 16).unwrap();
203+
if end < 0x800000000000 {
204+
let perm = s.next().unwrap();
205+
//println!("{:#X}-{:#X} {}", begin, end, perm);
206+
let slice = {
207+
let begin_ptr: *mut u8 = begin as *const u8 as _;
208+
unsafe { ::std::slice::from_raw_parts_mut(begin_ptr, end - begin) }
209+
};
210+
// Make sure process doesn't overlap with page table
211+
assert!(begin > page_table_memory_limit);
212+
vm.set_user_memory_region(begin as _, slice, 0).unwrap();
213+
}
214+
}
215+
216+
let mut vcpu = Vcpu::create(&mut vm).unwrap();
217+
// Set supported CPUID (KVM fails without doing this)
218+
let mut cpuid = sys.get_supported_cpuid().unwrap();
219+
vcpu.set_cpuid2(&mut cpuid).unwrap();
220+
221+
// Setup the special registers
222+
let mut sregs = vcpu.get_sregs().unwrap();
223+
// Set the code segment to have base 0, limit 4GB (flat segmentation)
224+
let segment_template = Segment {
225+
base: 0x0,
226+
limit: 0xffffffff,
227+
selector: 0,
228+
_type: 0,
229+
present: 0,
230+
dpl: 0,
231+
db: 1,
232+
s: 0,
233+
l: 0,
234+
g: 1,
235+
avl: 0,
236+
..Default::default()
237+
};
238+
sregs.cs = Segment {
239+
selector: 0x8,
240+
_type: 0xb,
241+
present: 1,
242+
db: 0,
243+
s: 1,
244+
l: 1,
245+
..segment_template
246+
};
247+
sregs.ss = Segment { ..segment_template };
248+
sregs.ds = Segment { ..segment_template };
249+
sregs.es = Segment { ..segment_template };
250+
sregs.fs = Segment { ..segment_template };
251+
sregs.gs = Segment { ..segment_template };
252+
// We don't need to populate the GDT if we have our segments setup
253+
// cr0 - protected mode on, paging enabled
254+
sregs.cr0 = (CR0_PROTECTED_MODE | CR0_MONITOR_COPROCESSOR | CR0_EXTENSION_TYPE |
255+
CR0_ENABLE_PAGING | CR0_NUMERIC_ERROR | CR0_WRITE_PROTECT |
256+
CR0_ALIGNMENT_MASK | CR0_ENABLE_PAGING)
257+
.bits() as u64;
258+
sregs.cr3 = PAGE_TABLE_P.as_u64();
259+
sregs.cr4 = (CR4_ENABLE_PSE | CR4_ENABLE_PAE | CR4_ENABLE_GLOBAL_PAGES | CR4_ENABLE_SSE |
260+
CR4_UNMASKED_SSE |
261+
CR4_ENABLE_OS_XSAVE | CR4_ENABLE_SMEP | CR4_ENABLE_VME)
262+
.bits() as u64;
263+
sregs.efer = 0xd01;
264+
265+
// Set the special registers
266+
vcpu.set_sregs(&sregs).unwrap();
267+
268+
let mut regs = vcpu.get_regs().unwrap();
269+
// set the instruction pointer to 1 MB
270+
regs.rip = vaddr.as_usize() as u64;
271+
println!("regs.rip = 0x{:x}", regs.rip); // but is at: 0x40cd60
272+
//println!("regs.rip = 0x{:x}", unsafe { *(regs.rip as *const u64) }); // but is at: 0x40cd60
273+
//regs.rip = 0x40cd60;
274+
regs.rflags = 0x246;
275+
regs.rsp = STACK_BASE_T.as_u64() + stack_size as u64;
276+
regs.rbp = regs.rsp;
277+
vcpu.set_regs(&regs).unwrap();
278+
279+
// Actually run the VCPU
280+
281+
println!("size of run {:?}", std::mem::size_of::<kvm::Run>());
282+
let mut vm_is_done = false;
283+
let mut new_regs = kvm::Regs::default();
284+
while !vm_is_done {
285+
{
286+
let (run, mut regs) = unsafe { vcpu.run_regs() }.unwrap();
287+
match run.exit_reason {
288+
Exit::Io => {
289+
let io = unsafe { *run.io() };
290+
match io.direction {
291+
IoDirection::In => {
292+
if io.port == 0x3fd {
293+
regs.rax = 0x20; // Mark serial line as ready to write
294+
} else {
295+
println!("IO on unknown port: {}", io.port);
296+
}
297+
}
298+
IoDirection::Out => {
299+
if io.port == 0x3f8 {
300+
println!("got char {:#?}", regs.rax as u8 as char);
301+
}
302+
}
303+
}
304+
}
305+
Exit::Shutdown => {
306+
println!("Shutting down");
307+
vm_is_done = true;
308+
}
309+
_ => {
310+
println!("Unknown exit reason: {:?}", run.exit_reason);
311+
}
312+
}
313+
314+
new_regs = regs;
315+
}
316+
vcpu.set_regs(&new_regs).unwrap();
317+
}
318+
319+
}*/
320+
321+
17322
pub fn test_before_run(name: &str) {
18323
print!("test {} ... ", name);
324+
325+
let meta_data = find_meta_data(name).unwrap();
326+
19327
}
20328

21329
pub fn test_panic_fmt(args: std::fmt::Arguments, file: &'static str, line: u32) {

0 commit comments

Comments
 (0)