xref: /aosp_15_r20/external/crosvm/hypervisor/tests/dirty_log.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
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