1 // Copyright 2017 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // TODO(b/237714823): Currently, only kvm is enabled for this test once LUCI can run windows.
6 #![cfg(any(target_os = "android", target_os = "linux"))]
7 #![cfg(target_arch = "x86_64")]
8 #![cfg(any(feature = "whpx", feature = "gvm", feature = "haxm", unix))]
9
10 use base::MemoryMappingBuilder;
11 use base::SharedMemory;
12 use hypervisor::*;
13 use vm_memory::GuestAddress;
14 use vm_memory::GuestMemory;
15
16 #[test]
17 #[cfg(any(target_os = "android", target_os = "linux"))]
test_kvm_dirty_log()18 fn test_kvm_dirty_log() {
19 use hypervisor::kvm::*;
20 test_dirty_log(|guest_mem| {
21 let kvm = Kvm::new().expect("failed to create kvm");
22 let vm = KvmVm::new(&kvm, guest_mem, Default::default()).expect("failed to create vm");
23 (kvm, vm)
24 });
25 }
26
27 #[test]
28 #[cfg(all(windows, feature = "haxm"))]
test_haxm_dirty_log_not_supported()29 fn test_haxm_dirty_log_not_supported() {
30 // HAXM does not support dirty log, so we simply test that the capability
31 // returns false.
32
33 use hypervisor::haxm::*;
34 let haxm = Haxm::new().expect("failed to create haxm");
35 let guest_mem = GuestMemory::new(&[(GuestAddress(0x20000), 0x1000)]).unwrap();
36 let vm = HaxmVm::new(&haxm, guest_mem).expect("failed to create vm");
37
38 assert!(!vm.check_capability(VmCap::DirtyLog));
39 }
40
41 #[test]
42 #[cfg(feature = "gvm")]
test_gvm_dirty_log()43 fn test_gvm_dirty_log() {
44 use hypervisor::gvm::*;
45 test_dirty_log(|guest_mem| {
46 let gvm = Gvm::new().expect("failed to create gvm");
47 let vm = GvmVm::new(&gvm, guest_mem).expect("failed to create vm");
48 (gvm, vm)
49 });
50 }
51
52 #[test]
53 #[cfg(all(windows, feature = "whpx"))]
test_whpx_dirty_log()54 fn test_whpx_dirty_log() {
55 use hypervisor::whpx::*;
56 if !Whpx::is_enabled() {
57 return;
58 }
59 test_dirty_log(|guest_mem| {
60 let whpx = Whpx::new().expect("failed to create whpx");
61 let vm =
62 WhpxVm::new(&whpx, 1, guest_mem, CpuId::new(0), false).expect("failed to create vm");
63 (whpx, vm)
64 });
65 }
66
test_dirty_log<CreateVm, HypervisorT, VmT>(create_vm: CreateVm) where CreateVm: FnOnce(GuestMemory) -> (HypervisorT, VmT), HypervisorT: Hypervisor, VmT: VmX86_64,67 fn test_dirty_log<CreateVm, HypervisorT, VmT>(create_vm: CreateVm)
68 where
69 CreateVm: FnOnce(GuestMemory) -> (HypervisorT, VmT),
70 HypervisorT: Hypervisor,
71 VmT: VmX86_64,
72 {
73 /*
74 0000 881C mov [si],bl
75 0002 F4 hlt
76 */
77 let code = [0x88, 0x1c, 0xf4];
78 let mem_size = 0x10000;
79 let load_addr = GuestAddress(0x1000);
80 // GuestMemory requires an initial set of memory, so we just
81 // setup some at 0x20000, it won't be used though.
82 let guest_mem = GuestMemory::new(&[(GuestAddress(0x20000), 0x1000)]).unwrap();
83 let mem = SharedMemory::new("test", mem_size).expect("failed to create shared memory");
84 let mmap = MemoryMappingBuilder::new(mem_size as usize)
85 .from_shared_memory(&mem)
86 .build()
87 .expect("failed to create memory mapping");
88
89 mmap.write_slice(&code[..], load_addr.offset() as usize)
90 .expect("Writing code to memory failed.");
91
92 let (_hyp, mut vm) = create_vm(guest_mem);
93 let mut vcpu = vm.create_vcpu(0).expect("new vcpu failed");
94 let mut vcpu_sregs = vcpu.get_sregs().expect("get sregs failed");
95 vcpu_sregs.cs.base = 0;
96 vcpu_sregs.cs.selector = 0;
97 vcpu.set_sregs(&vcpu_sregs).expect("set sregs failed");
98
99 let vcpu_regs = Regs {
100 rip: load_addr.offset(),
101 rflags: 2,
102 // Write 0x12 to the beginning of the 9th page.
103 rsi: 0x8000,
104 rbx: 0x12,
105 ..Default::default()
106 };
107 vcpu.set_regs(&vcpu_regs).expect("set regs failed");
108 let slot = vm
109 .add_memory_region(
110 GuestAddress(0),
111 Box::new(
112 MemoryMappingBuilder::new(mem_size as usize)
113 .from_shared_memory(&mem)
114 .build()
115 .expect("failed to create memory mapping"),
116 ),
117 false,
118 true,
119 MemCacheType::CacheCoherent,
120 )
121 .expect("failed to register memory");
122
123 loop {
124 match vcpu.run().expect("run failed") {
125 // Continue on external interrupt or signal
126 VcpuExit::Intr => continue,
127 VcpuExit::Hlt => break,
128 r => panic!("unexpected exit reason: {:?}", r),
129 }
130 }
131
132 let mut dirty_log = [0x0, 0x0];
133 vm.get_dirty_log(slot, &mut dirty_log[..])
134 .expect("failed to get dirty log");
135 // Tests the 9th page was written to.
136 assert_eq!(dirty_log[1], 0x1);
137 assert_eq!(
138 mmap.read_obj::<u64>(vcpu_regs.rsi as usize).unwrap(),
139 vcpu_regs.rbx
140 );
141 }
142