Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/hypervisor/tests/mmio_and_pio.rs
5394 views
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"))]
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"))]
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"))]
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")]
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
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"))]
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"))]
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"))]
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")]
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
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"))]
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"))]
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"))]
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")]
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
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
450