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(unix)]
7 #![cfg(any(target_arch = "x86", 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(unix)]
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(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() as u64,
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 )
129 .expect("failed to register memory");
130
131 // Give some read only memory for the test code to read from and force a vcpu exit when it reads
132 // from it.
133 let mem_ro = SharedMemory::new("test", 0x1000).expect("failed to create shared memory");
134
135 let mmap_ro = MemoryMappingBuilder::new(0x1000)
136 .from_shared_memory(&mem_ro)
137 .build()
138 .expect("failed to create memory mapping");
139 mmap_ro
140 .write_obj(0x66, 0)
141 .expect("failed writing data to ro memory");
142 vm.add_memory_region(
143 GuestAddress(vcpu_sregs.es.base),
144 Box::new(
145 MemoryMappingBuilder::new(0x1000)
146 .from_shared_memory(&mem_ro)
147 .build()
148 .expect("failed to create memory mapping"),
149 ),
150 true,
151 false,
152 )
153 .expect("failed to register memory");
154
155 // Ensure we get exactly 1 exit from attempting to write to read only memory.
156 let exits = AtomicU16::new(0);
157
158 let run_handle = vcpu.take_run_handle(None).unwrap();
159 loop {
160 match vcpu.run(&run_handle).expect("run failed") {
161 // Continue on external interrupt or signal
162 VcpuExit::Intr => continue,
163 VcpuExit::Hlt => break,
164 VcpuExit::Mmio => {
165 vcpu.handle_mmio(&mut |IoParams {
166 address,
167 size,
168 operation,
169 }| match operation {
170 IoOperation::Read => {
171 panic!("unexpected mmio read call");
172 }
173 IoOperation::Write { data } => {
174 assert_eq!(size, 1);
175 assert_eq!(address, vcpu_sregs.es.base);
176 assert_eq!(data[0], 0x67);
177 exits.fetch_add(1, Ordering::SeqCst);
178 None
179 }
180 })
181 .expect("failed to set the data");
182 }
183 r => panic!("unexpected exit reason: {:?}", r),
184 }
185 }
186
187 // Check that exactly 1 attempt to write to read only memory was made, and that the memory is
188 // unchanged after that attempt.
189 assert_eq!(exits.load(Ordering::SeqCst), 1);
190 assert_eq!(
191 mmap_ro
192 .read_obj::<u8>(0)
193 .expect("failed to read data from ro memory"),
194 0x66
195 );
196
197 let vcpu_regs = vcpu.get_regs().expect("failed to get regs");
198 assert_eq!(vcpu_regs.rax, 0x67);
199 }
200