diff options
Diffstat (limited to 'test')
| -rw-r--r-- | test/long_mode/behavior.rs | 218 |
1 files changed, 200 insertions, 18 deletions
diff --git a/test/long_mode/behavior.rs b/test/long_mode/behavior.rs index 4cabe28..2478b9c 100644 --- a/test/long_mode/behavior.rs +++ b/test/long_mode/behavior.rs @@ -107,6 +107,7 @@ mod kvm { unsafe { this.configure_identity_paging(&mut vcpu_sregs); this.configure_selectors(&mut vcpu_sregs); + this.configure_idt(&mut vcpu_sregs); } vcpu_sregs.efer = 0x0000_0500; // LME | LMA @@ -138,7 +139,15 @@ mod kvm { } fn gdt_addr(&self) -> GuestAddress { - GuestAddress(0) + GuestAddress(0x1000) + } + + fn idt_addr(&self) -> GuestAddress { + GuestAddress(0x2000) + } + + fn interrupt_handlers_start(&self) -> GuestAddress { + GuestAddress(0x3000) } fn page_table_addr(&self) -> GuestAddress { @@ -182,8 +191,8 @@ mod kvm { // > descriptor table is variable in length and can contain up to 8192 (2^13) 8-byte // > descriptors. + assert!(idx < 4096 / 8); let addr = GuestAddress(self.gdt_addr().0 + (idx as u64 * 8)); - assert!(idx < 8192); self.check_range(addr, std::mem::size_of::<u64>() as u64); // SAFETY: idx * 8 can't overflow isize, and we've asserted the end of the pointer is @@ -193,6 +202,18 @@ mod kvm { } } + // note this returns a u32, but an IDT is four u32. the u32 this points at is the first of + // the four for the entry. + fn idt_entry_mut(&mut self, idx: u16) -> *mut u32 { + let addr = GuestAddress(self.idt_addr().0 + (idx as u64 * 16)); + assert!(idx < 256); + self.check_range(addr, std::mem::size_of::<[u64; 2]>() as u64); + + unsafe { + self.host_ptr(addr) as *mut u32 + } + } + fn page_tables(&self) -> VmPageTables<'_> { let base = self.page_table_addr(); @@ -274,19 +295,19 @@ mod kvm { sregs.cs.base = 0; sregs.cs.limit = 0; sregs.cs.selector = 4 * 8; - sregs.cs.type_ = 0b1010; // see SDM table 3-1 Code- and Data-Segment Types + sregs.cs.type_ = 0b1011; // see SDM table 3-1 Code- and Data-Segment Types sregs.cs.present = 1; sregs.cs.dpl = 0; sregs.cs.db = 0; sregs.cs.s = 1; sregs.cs.l = 1; - sregs.cs.g = 0; - sregs.cs.avl = 1; + sregs.cs.g = 1; + sregs.cs.avl = 0; sregs.ds.base = 0; sregs.ds.limit = 0; sregs.ds.selector = 5 * 8; - sregs.ds.type_ = 0b0010; // see SDM table 3-1 Code- and Data-Segment Types + sregs.ds.type_ = 0b0011; // see SDM table 3-1 Code- and Data-Segment Types sregs.ds.present = 1; sregs.ds.dpl = 0; sregs.ds.db = 1; @@ -311,26 +332,85 @@ mod kvm { let base_mid = (base >> 16) & 0xff; let base_high = (base >> 24) & 0xff; + let access_byte = (seg.type_ as u64) + | (seg.s as u64) << 4 + | (seg.dpl as u64) << 5 + | (seg.present as u64) << 7; + let flaglim_byte = lim_high + | (seg.avl as u64) << 4 + | (seg.l as u64) << 5 + | (seg.db as u64) << 6 + | (seg.g as u64) << 7; let desc_high = base_mid - | (seg.type_ as u64) << 8 - | (seg.s as u64) << 12 - | (seg.dpl as u64) << 13 - | (seg.present as u64) << 15 - | lim_high << 16 - | (seg.avl as u64) << 20 - | (seg.l as u64) << 21 - | (seg.db as u64) << 22 - | (seg.g as u64) << 23 + | access_byte << 8 + | flaglim_byte << 16 | base_high << 24; desc_low | (desc_high << 32) } sregs.gdt.base = self.gdt_addr().0; - sregs.gdt.limit = 0xffff; + sregs.gdt.limit = 0x256 * 8 - 1; - self.gdt_entry_mut(4).write(encode_segment(&sregs.cs)); + for i in 0..256 { + self.gdt_entry_mut(i).write(encode_segment(&sregs.cs)); + } + let buf = unsafe { (self.gdt_entry_mut(4) as *const [u8; 8]).read() }; + eprint!("programmed gdt entry ({:016x}) to bytes: ", self.gdt_entry_mut(4) as u64); + for b in buf { + eprint!("{:02x} ", b); + } + eprintln!(""); self.gdt_entry_mut(5).write(encode_segment(&sregs.ds)); + let buf = unsafe { (self.gdt_entry_mut(5) as *const [u8; 8]).read() }; + eprint!("programmed gdt entry ({:016x}) to bytes: ", self.gdt_entry_mut(5) as u64); + for b in buf { + eprint!("{:02x} ", b); + } + eprintln!(""); + } + + fn configure_idt(&mut self, sregs: &mut kvm_sregs) { + sregs.idt.base = self.idt_addr().0; + sregs.idt.limit = 256 * 16 - 1; // IDT is 256 entries of 16 bytes each + + let write_idt_entry = |idt_ptr: *mut u32, interrupt_handler_addr: GuestAddress| { + let low_hi = interrupt_handler_addr.0 as u32 & 0xffff_0000 | + 1 << 15 | + 0b00 << 13 | + 0 << 12 | + 0b1110 << 8 | + 0 << 3 | + 0; + let low_lo = ((4 * 8) << 16 | interrupt_handler_addr.0 & 0x0000_ffff) as u32; + unsafe { + idt_ptr.offset(0).write(low_lo); + idt_ptr.offset(1).write(low_hi); + idt_ptr.offset(2).write((interrupt_handler_addr.0 >> 32) as u32); + idt_ptr.offset(3).write(0); // reserved + } + unsafe { + if false { + let buf = (idt_ptr as *const [u8; 16]).read(); + eprint!("programmed idt entry ({:016x}) to handler at {:08x}, bytes: ", idt_ptr as u64, interrupt_handler_addr.0); + for b in buf { + eprint!("{:02x} ", b); + } + eprintln!(""); + } + } + }; + + // TODO + for i in 0..32 { + let interrupt_handler_addr = GuestAddress(self.interrupt_handlers_start().0 + i as u64); + write_idt_entry(self.idt_entry_mut(i), interrupt_handler_addr); + } + + unsafe { + std::slice::from_raw_parts_mut(self.host_ptr(self.interrupt_handlers_start()), 256) + .fill(0xf4); + } } } @@ -448,11 +528,58 @@ mod kvm { } } + fn run(vm: &mut TestVm) { + let mut exits = 0; + let end_pc = loop { + eprintln!("about to run! here's some state:"); + let regs = vm.vcpu.get_regs().unwrap(); + eprintln!("regs: {:?}", regs); + let sregs = vm.vcpu.get_sregs().unwrap(); + eprintln!("sregs: {:?}", sregs); + let exit = vm.run(); + exits += 1; + match exit { + VcpuExit::MmioRead(addr, buf) => { + eprintln!("mmio: [{:08x}:{}] <- ..", addr, buf.len()); + // TODO: better + buf.fill(1); + } + VcpuExit::MmioWrite(addr, buf) => { + eprintln!("mmio: .. -> [{:08x}:{}]", addr, buf.len()); + } + VcpuExit::Debug(info) => { + break info.pc; + } + VcpuExit::Hlt => { + eprintln!("hit hlt"); + let regs = vm.vcpu.get_regs().unwrap(); + break regs.rip; + } + other => { + eprintln!("unhandled exit: {:?} ... after {}", other, exits); + let regs = vm.vcpu.get_regs().unwrap(); + eprintln!("regs: {:?}", regs); + let sregs = vm.vcpu.get_sregs().unwrap(); + // eprintln!("sregs: {:?}", sregs); + panic!("stop"); + } + } + }; + + eprintln!("run exits at {:08x}", end_pc); + return; + } + fn run_with_mem_checks(vm: &mut TestVm, expected_end: u64, expected_mem: &[ExpectedMemAccess]) { let mut expected_mem = expected_mem.to_vec(); let mut unexpected_mem = Vec::new(); let mut exits = 0; let end_pc = loop { + eprintln!("about to run! here's some state:"); + let regs = vm.vcpu.get_regs().unwrap(); + eprintln!("regs: {:?}", regs); + let sregs = vm.vcpu.get_sregs().unwrap(); + eprintln!("sregs: {:?}", sregs); let exit = vm.run(); exits += 1; match exit { @@ -490,7 +617,12 @@ mod kvm { break regs.rip; } other => { - panic!("unhandled exit: {:?} ... after {}", other, exits); + eprintln!("unhandled exit: {:?} ... after {}", other, exits); + let regs = vm.vcpu.get_regs().unwrap(); + eprintln!("regs: {:?}", regs); + let sregs = vm.vcpu.get_sregs().unwrap(); + eprintln!("sregs: {:?}", sregs); + panic!("stop"); } } }; @@ -839,6 +971,7 @@ mod kvm { permute_dontcares(dontcare_regs.as_slice(), &mut regs); + eprintln!("setting regs to: {:?}", regs); vm.vcpu.set_regs(®s).unwrap(); run_with_mem_checks(vm, regs.rip + insts.len() as u64, expected_mem.as_slice()); @@ -904,6 +1037,55 @@ mod kvm { } #[test] + fn kvm_verify_ins() { + let mut vm = TestVm::create(); + + // `ins byte [rdi], dl` + let inst: &'static [u8] = &[0x6c]; + check_behavior(&mut vm, inst); + } + + #[test] + fn test_pf() { + use yaxpeax_arch::{Decoder, U8Reader}; + use yaxpeax_x86::long_mode::{Instruction, InstDecoder}; + + let mut vm = TestVm::create(); + + let decoder = InstDecoder::default(); + let mut buf = Instruction::default(); + +// let inst = &[0xcc, 0xc7, 0x01, 0x0a, 0x00, 0x00, 0x00]; + let inst = &[ + // jmp to jmpf below.. + 0xeb, 0x0e, + 0xf4, // hlt in case we didn't jump (??) + // set up a far call destination in the next 10 bytes here.. + 0x20, 0x00, // GDT entry 4 (4 * 8 == 0x20) + 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // address 0x3000 + 0x00, 0x00, 0x00, + 0x48, 0xff, 0x28, // jmpf [rax] + ]; + let mut reader = U8Reader::new(inst.as_slice()); + let decoded = decoder.decode(&mut reader).expect("can decode"); + + let before_sregs = vm.vcpu.get_sregs().unwrap(); + let mut regs = vm.vcpu.get_regs().unwrap(); + + vm.program(inst.as_slice(), &mut regs); + + regs.rax = regs.rip + 3; + // regs.rip = 0x3001; + vm.vcpu.set_regs(®s).unwrap(); + + // vm.set_single_step(true); + + run(&mut vm); + + panic!("hi"); + } + + #[test] fn behavior_verify_kvm() { use yaxpeax_arch::{Decoder, U8Reader}; use yaxpeax_x86::long_mode::{Instruction, InstDecoder}; |
