xref: /aosp_15_r20/external/crosvm/hypervisor/tests/mmio_and_pio.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 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_mmio_and_pio()18 fn test_kvm_mmio_and_pio() {
19     use hypervisor::kvm::*;
20     test_mmio_and_pio(|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_mmio_and_pio()29 fn test_haxm_mmio_and_pio() {
30     use hypervisor::haxm::*;
31     test_mmio_and_pio(|guest_mem| {
32         let haxm = Haxm::new().expect("failed to create haxm");
33         let vm = HaxmVm::new(&haxm, guest_mem).expect("failed to create vm");
34         (haxm, vm)
35     });
36 }
37 
38 #[test]
39 #[cfg(all(windows, feature = "whpx"))]
test_whpx_mmio_and_pio()40 fn test_whpx_mmio_and_pio() {
41     use hypervisor::whpx::*;
42     if !Whpx::is_enabled() {
43         return;
44     }
45     test_mmio_and_pio(|guest_mem| {
46         let whpx = Whpx::new().expect("failed to create whpx");
47         let vm =
48             WhpxVm::new(&whpx, 1, guest_mem, CpuId::new(0), false).expect("failed to create vm");
49         (whpx, vm)
50     });
51 }
52 
53 #[test]
54 #[cfg(feature = "gvm")]
test_gvm_mmio_and_pio()55 fn test_gvm_mmio_and_pio() {
56     use hypervisor::gvm::*;
57     test_mmio_and_pio(|guest_mem| {
58         let gvm = Gvm::new().expect("failed to create gvm");
59         let vm = GvmVm::new(&gvm, guest_mem).expect("failed to create vm");
60         (gvm, vm)
61     });
62 }
63 
test_mmio_and_pio<CreateVm, HypervisorT, VmT>(create_vm: CreateVm) where CreateVm: FnOnce(GuestMemory) -> (HypervisorT, VmT), HypervisorT: Hypervisor, VmT: VmX86_64,64 fn test_mmio_and_pio<CreateVm, HypervisorT, VmT>(create_vm: CreateVm)
65 where
66     CreateVm: FnOnce(GuestMemory) -> (HypervisorT, VmT),
67     HypervisorT: Hypervisor,
68     VmT: VmX86_64,
69 {
70     /*
71     0x0000000000000000:  67 88 03    mov byte ptr [ebx], al
72     0x0000000000000003:  67 8A 01    mov al, byte ptr [ecx]
73     0x0000000000000006:  E6 19       out 0x19, al
74     0x0000000000000008:  E4 20       in  al, 0x20
75     0x000000000000000a:  F4          hlt
76     */
77 
78     let code: [u8; 11] = [
79         0x67, 0x88, 0x03, 0x67, 0x8a, 0x01, 0xe6, 0x19, 0xe4, 0x20, 0xf4,
80     ];
81     let mem_size = 0x2000;
82     let load_addr = GuestAddress(0x1000);
83 
84     let guest_mem =
85         GuestMemory::new(&[(GuestAddress(0), mem_size)]).expect("failed to create guest mem");
86     guest_mem
87         .write_at_addr(&code[..], load_addr)
88         .expect("failed to write to guest memory");
89 
90     let (_, vm) = create_vm(guest_mem);
91     let mut vcpu = vm.create_vcpu(0).expect("new vcpu failed");
92     let mut vcpu_sregs = vcpu.get_sregs().expect("get sregs failed");
93     vcpu_sregs.cs.base = 0;
94     vcpu_sregs.cs.selector = 0;
95 
96     vcpu.set_sregs(&vcpu_sregs).expect("set sregs failed");
97 
98     let vcpu_regs = Regs {
99         rip: load_addr.offset(),
100         rflags: 2,
101         rax: 0x33,
102         rbx: 0x3000,
103         rcx: 0x3010,
104         ..Default::default()
105     };
106     vcpu.set_regs(&vcpu_regs).expect("set regs failed");
107 
108     // Ensure we get exactly 2 exits for the mmio and the pio.
109     let exits = AtomicU16::new(0);
110 
111     loop {
112         match vcpu.run().expect("run failed") {
113             VcpuExit::Mmio => {
114                 vcpu.handle_mmio(&mut |IoParams { address, operation }| {
115                     match operation {
116                         IoOperation::Read(data) => {
117                             assert_eq!(address, 0x3010);
118                             assert_eq!(data.len(), 1);
119                             exits.fetch_add(1, Ordering::SeqCst);
120                             // this number will be read into al register
121                             data.copy_from_slice(&[0x66]);
122                             Ok(())
123                         }
124                         IoOperation::Write(data) => {
125                             assert_eq!(address, 0x3000);
126                             assert_eq!(data[0], 0x33);
127                             assert_eq!(data.len(), 1);
128                             exits.fetch_add(1, Ordering::SeqCst);
129                             Ok(())
130                         }
131                     }
132                 })
133                 .expect("failed to set the data");
134             }
135             VcpuExit::Io => {
136                 vcpu.handle_io(&mut |IoParams { address, operation }| {
137                     match operation {
138                         IoOperation::Read(data) => {
139                             assert_eq!(address, 0x20);
140                             assert_eq!(data.len(), 1);
141                             exits.fetch_add(1, Ordering::SeqCst);
142                             // this number will be read into the al register
143                             data.copy_from_slice(&[0x77]);
144                         }
145                         IoOperation::Write(data) => {
146                             assert_eq!(address, 0x19);
147                             assert_eq!(data.len(), 1);
148                             assert_eq!(data[0], 0x66);
149                             exits.fetch_add(1, Ordering::SeqCst);
150                         }
151                     }
152                 })
153                 .expect("failed to set the data");
154             }
155             VcpuExit::Hlt => {
156                 break;
157             }
158             // Continue on external interrupt or signal
159             VcpuExit::Intr => continue,
160             r => panic!("unexpected exit reason: {:?}", r),
161         }
162     }
163 
164     assert_eq!(exits.load(Ordering::SeqCst), 4);
165     let regs: Regs = vcpu.get_regs().expect("failed to get regs");
166     assert_eq!(regs.rax, 0x77);
167 }
168 
169 #[test]
170 #[cfg(any(target_os = "android", target_os = "linux"))]
test_kvm_pio_out()171 fn test_kvm_pio_out() {
172     use hypervisor::kvm::*;
173     test_pio_out(|guest_mem| {
174         let kvm = Kvm::new().expect("failed to create kvm");
175         let vm = KvmVm::new(&kvm, guest_mem, Default::default()).expect("failed to create vm");
176         (kvm, vm)
177     });
178 }
179 
180 #[test]
181 #[cfg(all(windows, feature = "haxm"))]
test_haxm_pio_out()182 fn test_haxm_pio_out() {
183     use hypervisor::haxm::*;
184     test_pio_out(|guest_mem| {
185         let haxm = Haxm::new().expect("failed to create haxm");
186         let vm = HaxmVm::new(&haxm, guest_mem).expect("failed to create vm");
187         (haxm, vm)
188     });
189 }
190 
191 #[test]
192 #[cfg(all(windows, feature = "whpx"))]
test_whpx_pio_out()193 fn test_whpx_pio_out() {
194     use hypervisor::whpx::*;
195     if !Whpx::is_enabled() {
196         return;
197     }
198     test_pio_out(|guest_mem| {
199         let whpx = Whpx::new().expect("failed to create whpx");
200         let vm =
201             WhpxVm::new(&whpx, 1, guest_mem, CpuId::new(0), false).expect("failed to create vm");
202         (whpx, vm)
203     });
204 }
205 
206 #[test]
207 #[cfg(feature = "gvm")]
test_gvm_pio_out()208 fn test_gvm_pio_out() {
209     use hypervisor::gvm::*;
210     test_pio_out(|guest_mem| {
211         let gvm = Gvm::new().expect("failed to create gvm");
212         let vm = GvmVm::new(&gvm, guest_mem).expect("failed to create vm");
213         (gvm, vm)
214     });
215 }
216 
test_pio_out<CreateVm, HypervisorT, VmT>(create_vm: CreateVm) where CreateVm: FnOnce(GuestMemory) -> (HypervisorT, VmT), HypervisorT: Hypervisor, VmT: VmX86_64,217 fn test_pio_out<CreateVm, HypervisorT, VmT>(create_vm: CreateVm)
218 where
219     CreateVm: FnOnce(GuestMemory) -> (HypervisorT, VmT),
220     HypervisorT: Hypervisor,
221     VmT: VmX86_64,
222 {
223     /*
224       0x00: 31 c0      xor ax, ax   (ax = 0)
225       0x02: 40         inc ax       (ax = 1)
226       0x03: e7 01      out 0x1, ax  (OUT 0x0001 to port 1)
227       0x05: 40         inc ax       (ax = 2)
228       0x06: e6 02      out 0x2, al  (OUT 0x02 to port 2)
229       0x08: 40         inc ax       (ax = 3)
230       0x09: 89 c2      mov dx, ax   (dx = 3)
231       0x0b: ef         out dx, ax   (OUT 0x0003 to port 3)
232       0x0c: 40         inc ax       (ax = 4)
233       0x0d: 42         inc dx       (dx = 4)
234       0x0e: ee         out dx, al   (OUT 0x04 to port 4)
235       0x0f: f4         hlt
236     */
237 
238     let code: [u8; 16] = [
239         0x31, 0xc0, 0x40, 0xe7, 0x01, 0x40, 0xe6, 0x02, 0x40, 0x89, 0xc2, 0xef, 0x40, 0x42, 0xee,
240         0xf4,
241     ];
242     let mem_size = 0x2000;
243     let load_addr = GuestAddress(0x1000);
244 
245     let guest_mem =
246         GuestMemory::new(&[(GuestAddress(0), mem_size)]).expect("failed to create guest mem");
247     guest_mem
248         .write_at_addr(&code[..], load_addr)
249         .expect("failed to write to guest memory");
250 
251     let (_, vm) = create_vm(guest_mem);
252     let mut vcpu = vm.create_vcpu(0).expect("new vcpu failed");
253     let mut vcpu_sregs = vcpu.get_sregs().expect("get sregs failed");
254     vcpu_sregs.cs.base = 0;
255     vcpu_sregs.cs.selector = 0;
256 
257     vcpu.set_sregs(&vcpu_sregs).expect("set sregs failed");
258 
259     let vcpu_regs = Regs {
260         rip: load_addr.offset(),
261         rflags: 2,
262         ..Default::default()
263     };
264     vcpu.set_regs(&vcpu_regs).expect("set regs failed");
265 
266     // Ensure we get the expected PIO exits
267     let exit_count = AtomicU16::new(0);
268     let exit_bits = AtomicU16::new(0);
269 
270     loop {
271         match vcpu.run().expect("run failed") {
272             VcpuExit::Io => {
273                 vcpu.handle_io(&mut |IoParams { address, operation }| match operation {
274                     IoOperation::Read(_) => panic!("unexpected PIO read"),
275                     IoOperation::Write(data) => {
276                         assert!((1..=4).contains(&address));
277                         if address % 2 == 0 {
278                             assert_eq!(data.len(), 1);
279                             assert_eq!(data[0], address as u8);
280                         } else {
281                             assert_eq!(data.len(), 2);
282                             assert_eq!(data[0], address as u8);
283                             assert_eq!(data[1], 0);
284                         }
285                         exit_bits.fetch_or(1 << (address - 1), Ordering::SeqCst);
286                         exit_count.fetch_add(1, Ordering::SeqCst);
287                     }
288                 })
289                 .expect("failed to set the data");
290             }
291             VcpuExit::Hlt => {
292                 break;
293             }
294             // Continue on external interrupt or signal
295             VcpuExit::Intr => continue,
296             r => panic!("unexpected exit reason: {:?}", r),
297         }
298     }
299 
300     // bits 0 through 3 have been set
301     assert_eq!(exit_bits.load(Ordering::SeqCst), 0xf);
302     assert_eq!(exit_count.load(Ordering::SeqCst), 4);
303 }
304 
305 #[test]
306 #[cfg(any(target_os = "android", target_os = "linux"))]
test_kvm_pio_in()307 fn test_kvm_pio_in() {
308     use hypervisor::kvm::*;
309     test_pio_in(|guest_mem| {
310         let kvm = Kvm::new().expect("failed to create kvm");
311         let vm = KvmVm::new(&kvm, guest_mem, Default::default()).expect("failed to create vm");
312         (kvm, vm)
313     });
314 }
315 
316 #[test]
317 #[cfg(all(windows, feature = "haxm"))]
test_haxm_pio_in()318 fn test_haxm_pio_in() {
319     use hypervisor::haxm::*;
320     test_pio_in(|guest_mem| {
321         let haxm = Haxm::new().expect("failed to create haxm");
322         let vm = HaxmVm::new(&haxm, guest_mem).expect("failed to create vm");
323         (haxm, vm)
324     });
325 }
326 
327 #[test]
328 #[cfg(all(windows, feature = "whpx"))]
test_whpx_pio_in()329 fn test_whpx_pio_in() {
330     use hypervisor::whpx::*;
331     if !Whpx::is_enabled() {
332         return;
333     }
334     test_pio_in(|guest_mem| {
335         let whpx = Whpx::new().expect("failed to create whpx");
336         let vm =
337             WhpxVm::new(&whpx, 1, guest_mem, CpuId::new(0), false).expect("failed to create vm");
338         (whpx, vm)
339     });
340 }
341 
342 #[test]
343 #[cfg(feature = "gvm")]
test_gvm_pio_in()344 fn test_gvm_pio_in() {
345     use hypervisor::gvm::*;
346     test_pio_in(|guest_mem| {
347         let gvm = Gvm::new().expect("failed to create gvm");
348         let vm = GvmVm::new(&gvm, guest_mem).expect("failed to create vm");
349         (gvm, vm)
350     });
351 }
352 
test_pio_in<CreateVm, HypervisorT, VmT>(create_vm: CreateVm) where CreateVm: FnOnce(GuestMemory) -> (HypervisorT, VmT), HypervisorT: Hypervisor, VmT: VmX86_64,353 fn test_pio_in<CreateVm, HypervisorT, VmT>(create_vm: CreateVm)
354 where
355     CreateVm: FnOnce(GuestMemory) -> (HypervisorT, VmT),
356     HypervisorT: Hypervisor,
357     VmT: VmX86_64,
358 {
359     /*
360       0x00: e5 01      in ax, 0x1   (IN 16 bits from port 1)
361       0x02: 89 c6      mov si, ax   (si = value from port 1)
362       0x04: 31 c0      xor ax, ax   (clear ax, since IN will only write the lower byte)
363       0x06: e4 02      in al, 0x02  (IN 8 bits from port 2)
364       0x08: 89 c3      mov bx, ax   (bx = value from port 2)
365       0x0a: ba 03 00   mov dx, 0x03 (dx = 3)
366       0x0d: ed         in ax, dx    (IN 16 bits from port 3)
367       0x0e: 89 c1      mov cx, ax   (cx = value from port 3)
368       0x10: 42         inc dx       (dx = 4)
369       0x11: 31 c0      xor ax, ax   (clear ax, since IN will only write the lower byte)
370       0x13: ec         in al, dx    (IN 8 bits from port 4)
371       0x14: 89 c2      mov dx, ax   (dx = value from port 4)
372       0x16: 89 f0      mov ax, si   (ax = value from port 1)
373       0x18: f4         hlt
374     */
375 
376     let code: [u8; 25] = [
377         0xe5, 0x01, 0x89, 0xc6, 0x31, 0xc0, 0xe4, 0x02, 0x89, 0xc3, 0xba, 0x03, 0x00, 0xed, 0x89,
378         0xc1, 0x42, 0x31, 0xc0, 0xec, 0x89, 0xc2, 0x89, 0xf0, 0xf4,
379     ];
380     let mem_size = 0x2000;
381     let load_addr = GuestAddress(0x1000);
382 
383     let guest_mem =
384         GuestMemory::new(&[(GuestAddress(0), mem_size)]).expect("failed to create guest mem");
385     guest_mem
386         .write_at_addr(&code[..], load_addr)
387         .expect("failed to write to guest memory");
388 
389     let (_, vm) = create_vm(guest_mem);
390     let mut vcpu = vm.create_vcpu(0).expect("new vcpu failed");
391     let mut vcpu_sregs = vcpu.get_sregs().expect("get sregs failed");
392     vcpu_sregs.cs.base = 0;
393     vcpu_sregs.cs.selector = 0;
394 
395     vcpu.set_sregs(&vcpu_sregs).expect("set sregs failed");
396 
397     let vcpu_regs = Regs {
398         rip: load_addr.offset(),
399         rflags: 2,
400         ..Default::default()
401     };
402     vcpu.set_regs(&vcpu_regs).expect("set regs failed");
403 
404     // Ensure we get the expected PIO exits
405     let exit_count = AtomicU16::new(0);
406     let exit_bits = AtomicU16::new(0);
407 
408     loop {
409         match vcpu.run().expect("run failed") {
410             VcpuExit::Io => {
411                 vcpu.handle_io(&mut |IoParams { address, operation }| match operation {
412                     IoOperation::Read(data) => {
413                         assert!((1..=4).contains(&address));
414 
415                         if address % 2 == 0 {
416                             assert_eq!(data.len(), 1);
417                             data[0] = address as u8;
418                         } else {
419                             assert_eq!(data.len(), 2);
420                             data[0] = address as u8;
421                             data[1] = address as u8;
422                         }
423 
424                         exit_bits.fetch_or(1 << (address - 1), Ordering::SeqCst);
425                         exit_count.fetch_add(1, Ordering::SeqCst);
426                     }
427                     IoOperation::Write(_) => panic!("unexpected PIO write"),
428                 })
429                 .expect("failed to set the data");
430             }
431             VcpuExit::Hlt => {
432                 break;
433             }
434             // Continue on external interrupt or signal
435             VcpuExit::Intr => continue,
436             r => panic!("unexpected exit reason: {:?}", r),
437         }
438     }
439 
440     // bits 0 through 3 have been set
441     assert_eq!(exit_bits.load(Ordering::SeqCst), 0xf);
442     assert_eq!(exit_count.load(Ordering::SeqCst), 4);
443     let regs: Regs = vcpu.get_regs().expect("failed to get regs");
444     assert_eq!(regs.rax, 0x0101);
445     assert_eq!(regs.rbx, 0x02);
446     assert_eq!(regs.rcx, 0x0303);
447     assert_eq!(regs.rdx, 0x04);
448 }
449