diff options
| -rw-r--r-- | src/x86_64.rs | 677 |
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(®s).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(®s).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(®s).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(®s).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(®s).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(®s).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(®s).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(®s).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(®s).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(®s).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(®s).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(®s).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(®s).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(®s).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(®s).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(®s).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(®s).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(®s).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(®s).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(®s).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"); +} |
