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