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