xref: /aosp_15_r20/external/crosvm/hypervisor/tests/read_only_memory.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 
9 use std::sync::atomic::AtomicU16;
10 use std::sync::atomic::Ordering;
11 
12 use base::MemoryMappingBuilder;
13 use base::SharedMemory;
14 use hypervisor::*;
15 use vm_memory::GuestAddress;
16 use vm_memory::GuestMemory;
17 
18 #[test]
19 #[cfg(any(target_os = "android", target_os = "linux"))]
test_kvm_read_only_memory()20 fn test_kvm_read_only_memory() {
21     use hypervisor::kvm::*;
22     test_read_only_memory(|guest_mem| {
23         let kvm = Kvm::new().expect("failed to create kvm");
24         let vm = KvmVm::new(&kvm, guest_mem, Default::default()).expect("failed to create vm");
25         (kvm, vm)
26     });
27 }
28 
29 // TODO(b/163163457): HAXM has a bug where the mmio write for read only memory
30 //  does not happen to the correct address.
31 // #[test]
32 // #[cfg(all(windows, feature = "haxm"))]
33 // fn test_haxm_read_only_memory() {
34 //     use hypervisor::haxm::*;
35 //     test_read_only_memory(|guest_mem| {
36 //         let haxm = Haxm::new().expect("failed to create haxm");
37 //         let vm = HaxmVm::new(&haxm, guest_mem).expect("failed to create vm");
38 //         (haxm, vm)
39 //     });
40 // }
41 
42 #[test]
43 #[cfg(feature = "gvm")]
test_gvm_read_only_memory()44 fn test_gvm_read_only_memory() {
45     use hypervisor::gvm::*;
46     test_read_only_memory(|guest_mem| {
47         let gvm = Gvm::new().expect("failed to create gvm");
48         let vm = GvmVm::new(&gvm, guest_mem).expect("failed to create vm");
49         (gvm, vm)
50     });
51 }
52 
53 // TODO(b/163163457): whpx also fails with guest cannot be faulted
54 /*#[test]
55 #[cfg(all(windows, feature = "whpx"))]
56 fn test_whpx_read_only_memory() {
57     use hypervisor::whpx::*;
58     if !Whpx::is_enabled() { return; }
59     test_read_only_memory(|guest_mem| {
60         let whpx = Whpx::new().expect("failed to create whpx");
61         let vm = WhpxVm::new(&whpx, 1, guest_mem, CpuId::new(0), false)
62             .expect("failed to create vm");
63         (whpx, vm)
64     });
65 }*/
66 
test_read_only_memory<CreateVm, HypervisorT, VmT>(create_vm: CreateVm) where CreateVm: FnOnce(GuestMemory) -> (HypervisorT, VmT), HypervisorT: Hypervisor, VmT: VmX86_64,67 fn test_read_only_memory<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  268A07  mov al,[es:bx]
75     0003  0401    add al,0x1
76     0005  268807  mov [es:bx],al
77     0008  F4      hlt
78     */
79     let code = [0x26, 0x8a, 0x07, 0x04, 0x01, 0x26, 0x88, 0x07, 0xf4];
80     let mem_size = 0x2000;
81     let load_addr = GuestAddress(0x1000);
82 
83     // GuestMemory requires an initial set of memory, so we just
84     // setup some at 0x8000, it won't be used though.
85     let guest_mem =
86         GuestMemory::new(&[(GuestAddress(0x8000), 0x1000)]).expect("failed to create guest mem");
87     let mem = SharedMemory::new("test", mem_size).expect("failed to create shared memory");
88     let mmap = MemoryMappingBuilder::new(mem_size as usize)
89         .from_shared_memory(&mem)
90         .build()
91         .expect("failed to create memory mapping");
92 
93     mmap.write_slice(&code[..], load_addr.offset() as usize)
94         .expect("Writing code to memory failed.");
95 
96     let (_, mut vm) = create_vm(guest_mem);
97     let mut vcpu = vm.create_vcpu(0).expect("new vcpu failed");
98     let mut vcpu_sregs = vcpu.get_sregs().expect("get sregs failed");
99     vcpu_sregs.cs.base = 0;
100     vcpu_sregs.cs.selector = 0;
101     vcpu_sregs.cs.s = 1;
102     vcpu_sregs.cs.type_ = 0b1011;
103     vcpu_sregs.es.base = 0x3000;
104     vcpu_sregs.es.selector = 0;
105     vcpu_sregs.es.s = 1;
106     vcpu_sregs.es.type_ = 0b1011;
107 
108     vcpu.set_sregs(&vcpu_sregs).expect("set sregs failed");
109 
110     let vcpu_regs = Regs {
111         rip: load_addr.offset(),
112         rflags: 2,
113         rax: 0,
114         rbx: 0,
115         ..Default::default()
116     };
117     vcpu.set_regs(&vcpu_regs).expect("set regs failed");
118     vm.add_memory_region(
119         GuestAddress(0),
120         Box::new(
121             MemoryMappingBuilder::new(mem_size as usize)
122                 .from_shared_memory(&mem)
123                 .build()
124                 .expect("failed to create memory mapping"),
125         ),
126         false,
127         false,
128         MemCacheType::CacheCoherent,
129     )
130     .expect("failed to register memory");
131 
132     // Give some read only memory for the test code to read from and force a vcpu exit when it reads
133     // from it.
134     let mem_ro = SharedMemory::new("test", 0x1000).expect("failed to create shared memory");
135 
136     let mmap_ro = MemoryMappingBuilder::new(0x1000)
137         .from_shared_memory(&mem_ro)
138         .build()
139         .expect("failed to create memory mapping");
140     mmap_ro
141         .write_obj(0x66, 0)
142         .expect("failed writing data to ro memory");
143     vm.add_memory_region(
144         GuestAddress(vcpu_sregs.es.base),
145         Box::new(
146             MemoryMappingBuilder::new(0x1000)
147                 .from_shared_memory(&mem_ro)
148                 .build()
149                 .expect("failed to create memory mapping"),
150         ),
151         true,
152         false,
153         MemCacheType::CacheCoherent,
154     )
155     .expect("failed to register memory");
156 
157     // Ensure we get exactly 1 exit from attempting to write to read only memory.
158     let exits = AtomicU16::new(0);
159 
160     loop {
161         match vcpu.run().expect("run failed") {
162             // Continue on external interrupt or signal
163             VcpuExit::Intr => continue,
164             VcpuExit::Hlt => break,
165             VcpuExit::Mmio => {
166                 vcpu.handle_mmio(&mut |IoParams { address, operation }| match operation {
167                     IoOperation::Read(_) => {
168                         panic!("unexpected mmio read call");
169                     }
170                     IoOperation::Write(data) => {
171                         assert_eq!(data.len(), 1);
172                         assert_eq!(address, vcpu_sregs.es.base);
173                         assert_eq!(data[0], 0x67);
174                         exits.fetch_add(1, Ordering::SeqCst);
175                         Ok(())
176                     }
177                 })
178                 .expect("failed to set the data");
179             }
180             r => panic!("unexpected exit reason: {:?}", r),
181         }
182     }
183 
184     // Check that exactly 1 attempt to write to read only memory was made, and that the memory is
185     // unchanged after that attempt.
186     assert_eq!(exits.load(Ordering::SeqCst), 1);
187     assert_eq!(
188         mmap_ro
189             .read_obj::<u8>(0)
190             .expect("failed to read data from ro memory"),
191         0x66
192     );
193 
194     let vcpu_regs = vcpu.get_regs().expect("failed to get regs");
195     assert_eq!(vcpu_regs.rax, 0x67);
196 }
197