• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     // GuestMemory requires an initial set of memory, so we just
85     // setup some at 0x8000, it won't be used though.
86     let guest_mem =
87         GuestMemory::new(&[(GuestAddress(0), mem_size)]).expect("failed to create guest mem");
88     guest_mem
89         .write_at_addr(&code[..], load_addr)
90         .expect("failed to write to guest memory");
91 
92     let (_, 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 
98     vcpu.set_sregs(&vcpu_sregs).expect("set sregs failed");
99 
100     let vcpu_regs = Regs {
101         rip: load_addr.offset(),
102         rflags: 2,
103         rax: 0x33,
104         rbx: 0x3000,
105         rcx: 0x3010,
106         ..Default::default()
107     };
108     vcpu.set_regs(&vcpu_regs).expect("set regs failed");
109 
110     // Ensure we get exactly 2 exits for the mmio and the pio.
111     let exits = AtomicU16::new(0);
112 
113     loop {
114         match vcpu.run().expect("run failed") {
115             VcpuExit::Mmio => {
116                 vcpu.handle_mmio(&mut |IoParams {
117                                            address,
118                                            size,
119                                            operation,
120                                        }| {
121                     match operation {
122                         IoOperation::Read => {
123                             let mut data = [0u8; 8];
124                             assert_eq!(address, 0x3010);
125                             assert_eq!(size, 1);
126                             exits.fetch_add(1, Ordering::SeqCst);
127                             // this number will be read into al register
128                             data.copy_from_slice(&0x66_u64.to_ne_bytes());
129                             Some(data)
130                         }
131                         IoOperation::Write { data } => {
132                             assert_eq!(address, 0x3000);
133                             assert_eq!(data[0], 0x33);
134                             assert_eq!(size, 1);
135                             exits.fetch_add(1, Ordering::SeqCst);
136                             None
137                         }
138                     }
139                 })
140                 .expect("failed to set the data");
141             }
142             VcpuExit::Io => {
143                 vcpu.handle_io(&mut |IoParams {
144                                          address,
145                                          size,
146                                          operation,
147                                      }| {
148                     match operation {
149                         IoOperation::Read => {
150                             let mut data = [0u8; 8];
151                             assert_eq!(address, 0x20);
152                             assert_eq!(size, 1);
153                             exits.fetch_add(1, Ordering::SeqCst);
154                             // this number will be read into the al register
155                             data.copy_from_slice(&0x77_u64.to_ne_bytes());
156                             Some(data)
157                         }
158                         IoOperation::Write { data } => {
159                             assert_eq!(address, 0x19);
160                             assert_eq!(size, 1);
161                             assert_eq!(data[0], 0x66);
162                             exits.fetch_add(1, Ordering::SeqCst);
163                             None
164                         }
165                     }
166                 })
167                 .expect("failed to set the data");
168             }
169             VcpuExit::Hlt => {
170                 break;
171             }
172             // Continue on external interrupt or signal
173             VcpuExit::Intr => continue,
174             r => panic!("unexpected exit reason: {:?}", r),
175         }
176     }
177 
178     assert_eq!(exits.load(Ordering::SeqCst), 4);
179     let regs: Regs = vcpu.get_regs().expect("failed to get regs");
180     assert_eq!(regs.rax, 0x77);
181 }
182