aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/x86_64.rs677
1 files changed, 339 insertions, 338 deletions
diff --git a/src/x86_64.rs b/src/x86_64.rs
index ff23b34..0e08446 100644
--- a/src/x86_64.rs
+++ b/src/x86_64.rs
@@ -437,344 +437,6 @@ fn test_check_range_exact() {
assert!(mapping.check_range(GuestAddress(0x4000), 0x1000));
}
-#[test]
-fn test_xor_runs() {
- let mut vm = Vm::create(128 * 1024).expect("can create vm");
- let mut regs = vm.get_regs().expect("can get regs");
-
- vm.program(&[0x33, 0xc0], &mut regs);
-
- regs.rax = 0x1234;
- let rip_before = regs.rip;
-
- vm.set_regs(&regs).expect("can set regs");
-
- vm.set_single_step(true).expect("can set single-step");
-
- let res = vm.run().expect("can run vm");
-
- let expected_rip = rip_before + 2;
- match res {
- VcpuExit::Debug { pc: rip_after, .. } => {
- assert_eq!(expected_rip, rip_after);
- }
- other => {
- panic!("unexpected exit: {:?}", other);
- }
- };
-
- let regs_after = vm.get_regs().expect("can get regs");
- assert_eq!(regs_after.rax, 0);
-}
-
-#[test]
-fn test_protected_mode_runs() {
- let settings = VmSettings::new(128 * 1024, IsaMode::Protected);
- let mut vm = Vm::create_by_settings(settings).expect("can create vm");
- let mut regs = vm.get_regs().expect("can get regs");
-
- let buf = &[
- 0xc5, 0xe0, 0x54, 0xc3, // vandps xmm0, xmm3, xmm3
- 0x33, 0xc0, // xor eax, eax
- 0x8b, 0x09, // mov ecx, [ecx]
- 0xf4 // hlt
- ];
- vm.program(buf, &mut regs);
-
- regs.rax = 0x1234;
- regs.rcx = 0x4;
-
- vm.set_regs(&regs).expect("can set regs");
-
- let res = vm.run().expect("can run vm");
-
- match res {
- VcpuExit::Hlt => {
- // expected exit from the `0xf4` above.
- }
- other => {
- panic!("unexpected exit: {:?}", other);
- }
- };
-
- let regs_after = vm.get_regs().expect("can get regs");
- assert_eq!(regs_after.rax, 0);
- assert_eq!(regs_after.rcx, 0);
-}
-
-#[test]
-fn test_pusha_runs() {
- let settings = VmSettings::new(128 * 1024, IsaMode::Real);
- let mut vm = Vm::create_by_settings(settings).expect("can create vm");
- let mut regs = vm.get_regs().expect("can get regs");
-
- vm.program(&[0x60], &mut regs);
-
- regs.rip = 0;
- regs.rax = 0x1234;
- eprintln!("{:?}", regs);
-
- vm.set_regs(&regs).expect("can set regs");
-
- vm.set_single_step(true).expect("can set single-step");
- let expected_rip = vm.code_addr().0 + 1;
-
- let res = vm.run().expect("can run vm");
-
- match res {
- VcpuExit::Debug { pc: rip_after, .. } => {
- eprintln!("rip after: {:08x}", rip_after);
- assert_eq!(expected_rip, rip_after);
- }
- other => {
- panic!("unexpected exit: {:?}", other);
- }
- };
-
- let regs_after = vm.get_regs().expect("can get regs");
- assert_eq!(regs_after.rax, 0x1234);
- assert_eq!(regs_after.rsp, 0x1000 - 0x80 - (8 * 2));
-
- let mut regs = vm.get_regs().expect("can get regs");
-
- vm.program(&[0x66, 0x60], &mut regs);
-
- regs.rip = 0;
- regs.rax = 0x1234;
- regs.rsp = 0x1000 - 0x80;
- eprintln!("{:?}", regs);
-
- vm.set_regs(&regs).expect("can set regs");
-
- vm.set_single_step(true).expect("can set single-step");
- let expected_rip = vm.code_addr().0 + 2;
-
- let res = vm.run().expect("can run vm");
-
- match res {
- VcpuExit::Debug { pc: rip_after, .. } => {
- eprintln!("rip after: {:08x}", rip_after);
- assert_eq!(expected_rip, rip_after);
- }
- other => {
- panic!("unexpected exit: {:?}", other);
- }
- };
-
- let regs_after = vm.get_regs().expect("can get regs");
- assert_eq!(regs_after.rax, 0x1234);
- assert_eq!(regs_after.rsp, 0x1000 - 0x80 - (8 * 4));
-}
-
-#[test]
-fn test_syscall() {
- let mut vm = Vm::create(128 * 1024).expect("can create vm");
- let mut regs = vm.get_regs().expect("can get regs");
-
- vm.program(&[0x0f, 0x05], &mut regs);
- eprintln!("rip before: {:08x}", regs.rip);
-
- vm.set_regs(&regs).expect("can set regs");
-
-// vm.set_single_step(true).expect("can set single-step");
-
- let res = vm.run().expect("can run vm");
- match res {
- VcpuExit::Syscall => { /* expected */ }
- VcpuExit::Debug { pc, .. } => {
- if pc == vm.syscall_addr().0 {
- panic!(
- "VM exited at syscall target. \
- syscall hlt stub not executed. \
- is the VM being single-stepped?"
- );
- }
- panic!("unexpected debug exit at rip={:08x}", pc);
- }
- other => {
- panic!("unexpected exit: {:?}", other);
- }
- };
-
- let regs_after = vm.get_regs().expect("can get regs");
-
- let expected_rip = vm.syscall_addr().0 + 1;
- assert_eq!(expected_rip, regs_after.rip);
-}
-
-#[test]
-fn test_xorps_runs() {
- let mut vm = Vm::create(128 * 1024).expect("can create vm");
- let mut regs = vm.get_regs().expect("can get regs");
-
- vm.program(&[0x0f, 0x57, 0xc0], &mut regs);
-
- let rip_before = regs.rip;
-
- vm.set_regs(&regs).expect("can set regs");
-
- vm.set_single_step(true).expect("can set single-step");
-
- let res = vm.run().expect("can run vm");
-
- let expected_rip = rip_before + 3;
- eprintln!("exit: {:?}", res);
- match res {
- VcpuExit::Debug { pc: rip_after, .. } => {
- assert_eq!(expected_rip, rip_after);
- }
- other => {
- panic!("unexpected exit: {:?}", other);
- }
- };
-}
-
-#[test]
-fn test_vex_vandps_runs() {
- let mut vm = Vm::create(128 * 1024).expect("can create vm");
-
- if !vm.cpuid_supports(Feature::StateAVX) {
- panic!("host CPU does not support AVX");
- }
-
- let mut regs = vm.get_regs().expect("can get regs");
-
- vm.program(&[0xc5, 0xe0, 0x54, 0x03], &mut regs);
-
- regs.rbx = regs.rip;
- let rip_before = regs.rip;
-
- vm.set_regs(&regs).expect("can set regs");
-
- vm.set_single_step(true).expect("can set single-step");
-
- let res = vm.run().expect("can run vm");
-
- let expected_rip = rip_before + 4;
- eprintln!("exit: {:?}", res);
- match res {
- VcpuExit::Debug { pc: rip_after, .. } => {
- assert_eq!(expected_rip, rip_after);
- }
- other => {
- panic!("unexpected exit: {:?}", other);
- }
- };
-}
-
-#[test]
-fn test_vex_vandps_runs_32b() {
- let settings = VmSettings::new(128 * 1024, IsaMode::Protected);
- let mut vm = Vm::create_by_settings(settings).expect("can create vm");
-
- if !vm.cpuid_supports(Feature::StateAVX) {
- panic!("host CPU does not support AVX");
- }
-
- let mut regs = vm.get_regs().expect("can get regs");
-
- vm.program(&[0xc5, 0xe0, 0x54, 0x03], &mut regs);
-
- regs.rbx = regs.rip;
- let rip_before = regs.rip;
-
- vm.set_regs(&regs).expect("can set regs");
-
- vm.set_single_step(true).expect("can set single-step");
-
- let res = vm.run().expect("can run vm");
-
- let expected_rip = rip_before + 4;
- eprintln!("exit: {:?}", res);
- match res {
- VcpuExit::Debug { pc: rip_after, .. } => {
- assert_eq!(expected_rip, rip_after);
- }
- other => {
- panic!("unexpected exit: {:?}", other);
- }
- };
-}
-
-#[test]
-fn test_evex_vandps_runs() {
- let mut vm = Vm::create(128 * 1024).expect("can create vm");
-
- if !vm.cpuid_supports(Feature::StateAVX512) {
- panic!("host CPU does not support AVX512");
- }
-
- let mut regs = vm.get_regs().expect("can get regs");
-
- vm.program(&[0x62, 0xf1, 0x7c, 0xbd, 0x54, 0x0a], &mut regs);
-
- regs.rbx = regs.rip;
- let rip_before = regs.rip;
-
- vm.set_regs(&regs).expect("can set regs");
-
- vm.set_single_step(true).expect("can set single-step");
-
- let res = vm.run().expect("can run vm");
-
- let expected_rip = rip_before + 6;
- eprintln!("exit: {:?}", res);
- match res {
- VcpuExit::Debug { pc: rip_after, .. } => {
- assert_eq!(expected_rip, rip_after);
- }
- other => {
- panic!("unexpected exit: {:?}", other);
- }
- };
-}
-
-
-// this function will sit and loop in the kernel after trying to fulfill the MMIO exit.
-//
-// not great! don't do that! it's responsive to EINTR at least.
-// #[test]
-#[allow(dead_code)]
-fn kvm_hugepage_bug() {
- let mut vm = Vm::create(1024 * 1024).expect("can create vm");
- vm.add_memory(GuestAddress(0x1_0000_0000), 128 * 1024).expect("can add test mem region");
- unsafe {
- vm.configure_identity_paging(None);
- }
-
- // `add [rsp], al; add [rcx], al; pop [rcx]; hlt`
- // the first instruction runs fine. the second instruction runs fine.
- // the third instruction gets a page fault at 0xf800? which worked fine for the add.
- // this turns out to be an issue in linux' paging64_gva_to_gpa() when the va is mapped with
- // huge pages.
- let inst: &'static [u8] = &[0x00, 0x04, 0x24, 0x00, 0x01, 0x8f, 0x01, 0xf4];
- let mut regs = vm.get_regs().unwrap();
- regs.rax = 0x00000002_00100000;
- regs.rcx = 0x00000002_00100000;
- vm.program(inst, &mut regs);
- vm.set_regs(&regs).unwrap();
- vm.set_single_step(true).expect("can enable single-step");
- vm.run().expect("can run vm");
-
- let vm_regs = vm.get_regs().unwrap();
- let vm_sregs = vm.get_sregs().unwrap();
- let mut prev_rip = [0u8; 8];
- vm.read_mem(GuestAddress(vm_regs.rsp + 8), &mut prev_rip[..]);
- let mut buf = [0u8; 8];
- vm.read_mem(GuestAddress(vm_regs.rsp), &mut buf[..]);
- eprintln!(
- "error code: {:#08x} accessing {:016x} @ rip={:#016x} (cr3={:016x})",
- u64::from_le_bytes(buf), vm_sregs.cr2,
- u64::from_le_bytes(prev_rip), vm_sregs.cr3
- );
- if vm_regs.rip == 0x300f {
- let mut pdpt = [0u8; 4096];
- vm.read_mem(vm.page_tables().pdpt_addr(), &mut pdpt[..]);
- eprintln!("pdpt: {:x?}", &pdpt[..8]);
- }
- panic!("no");
-}
-
/// a selector for the execution mode the VM should be initialized to.
///
/// different `IsaMode` will configure the VM wildly differently; generally any VM/vCPU state not
@@ -2156,3 +1818,342 @@ impl Vm {
self.syscall_configured = true;
}
}
+
+
+#[test]
+fn test_xor_runs() {
+ let mut vm = Vm::create(128 * 1024).expect("can create vm");
+ let mut regs = vm.get_regs().expect("can get regs");
+
+ vm.program(&[0x33, 0xc0], &mut regs);
+
+ regs.rax = 0x1234;
+ let rip_before = regs.rip;
+
+ vm.set_regs(&regs).expect("can set regs");
+
+ vm.set_single_step(true).expect("can set single-step");
+
+ let res = vm.run().expect("can run vm");
+
+ let expected_rip = rip_before + 2;
+ match res {
+ VcpuExit::Debug { pc: rip_after, .. } => {
+ assert_eq!(expected_rip, rip_after);
+ }
+ other => {
+ panic!("unexpected exit: {:?}", other);
+ }
+ };
+
+ let regs_after = vm.get_regs().expect("can get regs");
+ assert_eq!(regs_after.rax, 0);
+}
+
+#[test]
+fn test_protected_mode_runs() {
+ let settings = VmSettings::new(128 * 1024, IsaMode::Protected);
+ let mut vm = Vm::create_by_settings(settings).expect("can create vm");
+ let mut regs = vm.get_regs().expect("can get regs");
+
+ let buf = &[
+ 0xc5, 0xe0, 0x54, 0xc3, // vandps xmm0, xmm3, xmm3
+ 0x33, 0xc0, // xor eax, eax
+ 0x8b, 0x09, // mov ecx, [ecx]
+ 0xf4 // hlt
+ ];
+ vm.program(buf, &mut regs);
+
+ regs.rax = 0x1234;
+ regs.rcx = 0x4;
+
+ vm.set_regs(&regs).expect("can set regs");
+
+ let res = vm.run().expect("can run vm");
+
+ match res {
+ VcpuExit::Hlt => {
+ // expected exit from the `0xf4` above.
+ }
+ other => {
+ panic!("unexpected exit: {:?}", other);
+ }
+ };
+
+ let regs_after = vm.get_regs().expect("can get regs");
+ assert_eq!(regs_after.rax, 0);
+ assert_eq!(regs_after.rcx, 0);
+}
+
+#[test]
+fn test_pusha_runs() {
+ let settings = VmSettings::new(128 * 1024, IsaMode::Real);
+ let mut vm = Vm::create_by_settings(settings).expect("can create vm");
+ let mut regs = vm.get_regs().expect("can get regs");
+
+ vm.program(&[0x60], &mut regs);
+
+ regs.rip = 0;
+ regs.rax = 0x1234;
+ eprintln!("{:?}", regs);
+
+ vm.set_regs(&regs).expect("can set regs");
+
+ vm.set_single_step(true).expect("can set single-step");
+ let expected_rip = vm.code_addr().0 + 1;
+
+ let res = vm.run().expect("can run vm");
+
+ match res {
+ VcpuExit::Debug { pc: rip_after, .. } => {
+ eprintln!("rip after: {:08x}", rip_after);
+ assert_eq!(expected_rip, rip_after);
+ }
+ other => {
+ panic!("unexpected exit: {:?}", other);
+ }
+ };
+
+ let regs_after = vm.get_regs().expect("can get regs");
+ assert_eq!(regs_after.rax, 0x1234);
+ assert_eq!(regs_after.rsp, 0x1000 - 0x80 - (8 * 2));
+
+ let mut regs = vm.get_regs().expect("can get regs");
+
+ vm.program(&[0x66, 0x60], &mut regs);
+
+ regs.rip = 0;
+ regs.rax = 0x1234;
+ regs.rsp = 0x1000 - 0x80;
+ eprintln!("{:?}", regs);
+
+ vm.set_regs(&regs).expect("can set regs");
+
+ vm.set_single_step(true).expect("can set single-step");
+ let expected_rip = vm.code_addr().0 + 2;
+
+ let res = vm.run().expect("can run vm");
+
+ match res {
+ VcpuExit::Debug { pc: rip_after, .. } => {
+ eprintln!("rip after: {:08x}", rip_after);
+ assert_eq!(expected_rip, rip_after);
+ }
+ other => {
+ panic!("unexpected exit: {:?}", other);
+ }
+ };
+
+ let regs_after = vm.get_regs().expect("can get regs");
+ assert_eq!(regs_after.rax, 0x1234);
+ assert_eq!(regs_after.rsp, 0x1000 - 0x80 - (8 * 4));
+}
+
+#[test]
+fn test_syscall() {
+ let mut vm = Vm::create(128 * 1024).expect("can create vm");
+ let mut regs = vm.get_regs().expect("can get regs");
+
+ vm.program(&[0x0f, 0x05], &mut regs);
+ eprintln!("rip before: {:08x}", regs.rip);
+
+ vm.set_regs(&regs).expect("can set regs");
+
+// vm.set_single_step(true).expect("can set single-step");
+
+ let res = vm.run().expect("can run vm");
+ match res {
+ VcpuExit::Syscall => { /* expected */ }
+ VcpuExit::Debug { pc, .. } => {
+ if pc == vm.syscall_addr().0 {
+ panic!(
+ "VM exited at syscall target. \
+ syscall hlt stub not executed. \
+ is the VM being single-stepped?"
+ );
+ }
+ panic!("unexpected debug exit at rip={:08x}", pc);
+ }
+ other => {
+ panic!("unexpected exit: {:?}", other);
+ }
+ };
+
+ let regs_after = vm.get_regs().expect("can get regs");
+
+ let expected_rip = vm.syscall_addr().0 + 1;
+ assert_eq!(expected_rip, regs_after.rip);
+}
+
+#[test]
+fn test_xorps_runs() {
+ let mut vm = Vm::create(128 * 1024).expect("can create vm");
+ let mut regs = vm.get_regs().expect("can get regs");
+
+ vm.program(&[0x0f, 0x57, 0xc0], &mut regs);
+
+ let rip_before = regs.rip;
+
+ vm.set_regs(&regs).expect("can set regs");
+
+ vm.set_single_step(true).expect("can set single-step");
+
+ let res = vm.run().expect("can run vm");
+
+ let expected_rip = rip_before + 3;
+ eprintln!("exit: {:?}", res);
+ match res {
+ VcpuExit::Debug { pc: rip_after, .. } => {
+ assert_eq!(expected_rip, rip_after);
+ }
+ other => {
+ panic!("unexpected exit: {:?}", other);
+ }
+ };
+}
+
+#[test]
+fn test_vex_vandps_runs() {
+ let mut vm = Vm::create(128 * 1024).expect("can create vm");
+
+ if !vm.cpuid_supports(Feature::StateAVX) {
+ panic!("host CPU does not support AVX");
+ }
+
+ let mut regs = vm.get_regs().expect("can get regs");
+
+ vm.program(&[0xc5, 0xe0, 0x54, 0x03], &mut regs);
+
+ regs.rbx = regs.rip;
+ let rip_before = regs.rip;
+
+ vm.set_regs(&regs).expect("can set regs");
+
+ vm.set_single_step(true).expect("can set single-step");
+
+ let res = vm.run().expect("can run vm");
+
+ let expected_rip = rip_before + 4;
+ eprintln!("exit: {:?}", res);
+ match res {
+ VcpuExit::Debug { pc: rip_after, .. } => {
+ assert_eq!(expected_rip, rip_after);
+ }
+ other => {
+ panic!("unexpected exit: {:?}", other);
+ }
+ };
+}
+
+#[test]
+fn test_vex_vandps_runs_32b() {
+ let settings = VmSettings::new(128 * 1024, IsaMode::Protected);
+ let mut vm = Vm::create_by_settings(settings).expect("can create vm");
+
+ if !vm.cpuid_supports(Feature::StateAVX) {
+ panic!("host CPU does not support AVX");
+ }
+
+ let mut regs = vm.get_regs().expect("can get regs");
+
+ vm.program(&[0xc5, 0xe0, 0x54, 0x03], &mut regs);
+
+ regs.rbx = regs.rip;
+ let rip_before = regs.rip;
+
+ vm.set_regs(&regs).expect("can set regs");
+
+ vm.set_single_step(true).expect("can set single-step");
+
+ let res = vm.run().expect("can run vm");
+
+ let expected_rip = rip_before + 4;
+ eprintln!("exit: {:?}", res);
+ match res {
+ VcpuExit::Debug { pc: rip_after, .. } => {
+ assert_eq!(expected_rip, rip_after);
+ }
+ other => {
+ panic!("unexpected exit: {:?}", other);
+ }
+ };
+}
+
+#[test]
+fn test_evex_vandps_runs() {
+ let mut vm = Vm::create(128 * 1024).expect("can create vm");
+
+ if !vm.cpuid_supports(Feature::StateAVX512) {
+ panic!("host CPU does not support AVX512");
+ }
+
+ let mut regs = vm.get_regs().expect("can get regs");
+
+ vm.program(&[0x62, 0xf1, 0x7c, 0xbd, 0x54, 0x0a], &mut regs);
+
+ regs.rbx = regs.rip;
+ let rip_before = regs.rip;
+
+ vm.set_regs(&regs).expect("can set regs");
+
+ vm.set_single_step(true).expect("can set single-step");
+
+ let res = vm.run().expect("can run vm");
+
+ let expected_rip = rip_before + 6;
+ eprintln!("exit: {:?}", res);
+ match res {
+ VcpuExit::Debug { pc: rip_after, .. } => {
+ assert_eq!(expected_rip, rip_after);
+ }
+ other => {
+ panic!("unexpected exit: {:?}", other);
+ }
+ };
+}
+
+
+// this function will sit and loop in the kernel after trying to fulfill the MMIO exit.
+//
+// not great! don't do that! it's responsive to EINTR at least.
+// #[test]
+#[allow(dead_code)]
+fn kvm_hugepage_bug() {
+ let mut vm = Vm::create(1024 * 1024).expect("can create vm");
+ vm.add_memory(GuestAddress(0x1_0000_0000), 128 * 1024).expect("can add test mem region");
+ unsafe {
+ vm.configure_identity_paging(None);
+ }
+
+ // `add [rsp], al; add [rcx], al; pop [rcx]; hlt`
+ // the first instruction runs fine. the second instruction runs fine.
+ // the third instruction gets a page fault at 0xf800? which worked fine for the add.
+ // this turns out to be an issue in linux' paging64_gva_to_gpa() when the va is mapped with
+ // huge pages.
+ let inst: &'static [u8] = &[0x00, 0x04, 0x24, 0x00, 0x01, 0x8f, 0x01, 0xf4];
+ let mut regs = vm.get_regs().unwrap();
+ regs.rax = 0x00000002_00100000;
+ regs.rcx = 0x00000002_00100000;
+ vm.program(inst, &mut regs);
+ vm.set_regs(&regs).unwrap();
+ vm.set_single_step(true).expect("can enable single-step");
+ vm.run().expect("can run vm");
+
+ let vm_regs = vm.get_regs().unwrap();
+ let vm_sregs = vm.get_sregs().unwrap();
+ let mut prev_rip = [0u8; 8];
+ vm.read_mem(GuestAddress(vm_regs.rsp + 8), &mut prev_rip[..]);
+ let mut buf = [0u8; 8];
+ vm.read_mem(GuestAddress(vm_regs.rsp), &mut buf[..]);
+ eprintln!(
+ "error code: {:#08x} accessing {:016x} @ rip={:#016x} (cr3={:016x})",
+ u64::from_le_bytes(buf), vm_sregs.cr2,
+ u64::from_le_bytes(prev_rip), vm_sregs.cr3
+ );
+ if vm_regs.rip == 0x300f {
+ let mut pdpt = [0u8; 4096];
+ vm.read_mem(vm.page_tables().pdpt_addr(), &mut pdpt[..]);
+ eprintln!("pdpt: {:x?}", &pdpt[..8]);
+ }
+ panic!("no");
+}