|
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 | +} |
2 | 119 |
|
3 | 120 | #[derive(Debug)]
|
4 | 121 | pub struct KvmTestMetaData {
|
5 | 122 | pub mbz: u64,
|
6 | 123 | 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 |
7 | 158 | }
|
8 | 159 |
|
| 160 | +/// Start the test harness. |
9 | 161 | pub fn test_start(ntests: usize) {
|
10 |
| - println!("KVM testing: running {} tests", ntests) |
| 162 | + println!("running {} tests (using KVM support)", ntests) |
11 | 163 | }
|
12 | 164 |
|
| 165 | +/// Signals that given test is ignored. |
13 | 166 | pub fn test_ignored(name: &str) {
|
14 | 167 | println!("test {} ... ignored", name);
|
15 | 168 | }
|
16 | 169 |
|
| 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(®s).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 | + |
17 | 322 | pub fn test_before_run(name: &str) {
|
18 | 323 | print!("test {} ... ", name);
|
| 324 | + |
| 325 | + let meta_data = find_meta_data(name).unwrap(); |
| 326 | + |
19 | 327 | }
|
20 | 328 |
|
21 | 329 | pub fn test_panic_fmt(args: std::fmt::Arguments, file: &'static str, line: u32) {
|
|
0 commit comments