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 #![cfg(feature = "plugin")]
6
7 use std::env::current_exe;
8 use std::env::var_os;
9 use std::ffi::OsString;
10 use std::fs::remove_file;
11 use std::io::Read;
12 use std::io::Write;
13 use std::path::Path;
14 use std::path::PathBuf;
15 use std::process::Command;
16 use std::process::Stdio;
17 use std::thread::sleep;
18 use std::time::Duration;
19
20 use base::ioctl;
21 use base::AsRawDescriptor;
22 use net_util::TapTCommon;
23 use once_cell::sync::Lazy;
24 use rand::random;
25 use tempfile::tempfile;
26
27 static TAP_AVAILABLE: Lazy<bool> =
28 Lazy::new(|| net_util::sys::linux::Tap::new(true, false).is_ok());
29
30 struct RemovePath(PathBuf);
31 impl Drop for RemovePath {
drop(&mut self)32 fn drop(&mut self) {
33 if let Err(e) = remove_file(&self.0) {
34 eprintln!("failed to remove path: {}", e);
35 }
36 }
37 }
38
get_target_path() -> PathBuf39 fn get_target_path() -> PathBuf {
40 current_exe()
41 .ok()
42 .map(|mut path| {
43 path.pop();
44 path
45 })
46 .expect("failed to get crosvm binary directory")
47 }
48
get_crosvm_path() -> PathBuf49 fn get_crosvm_path() -> PathBuf {
50 current_exe()
51 .ok()
52 .map(|mut path| {
53 path.pop();
54 if path.ends_with("deps") {
55 path.pop();
56 }
57 path
58 })
59 .expect("failed to get crosvm binary directory")
60 }
61
build_plugin(src: &str) -> RemovePath62 fn build_plugin(src: &str) -> RemovePath {
63 let libcrosvm_plugin_dir = get_target_path();
64 let mut out_bin = libcrosvm_plugin_dir.clone();
65 out_bin.push(format!("plugin-test-{:08X}", random::<u32>()));
66 let mut child = Command::new(var_os("CC").unwrap_or(OsString::from("cc")))
67 .args(["-Icrosvm_plugin", "-pthread", "-o"]) // crosvm.h location and set output path.
68 .arg(&out_bin)
69 .arg("-L") // Path of shared object to link to.
70 .arg(&libcrosvm_plugin_dir)
71 .arg("-Wl,-rpath") // Search for shared object in the same path when exec'd.
72 .arg(&libcrosvm_plugin_dir)
73 .args(["-Wl,-rpath", "."]) // Also check current directory in case of sandboxing.
74 .args(["-xc", "-"]) // Read source code from piped stdin.
75 .arg("-lcrosvm_plugin")
76 .stdin(Stdio::piped())
77 .spawn()
78 .expect("failed to spawn compiler");
79 let stdin = child.stdin.as_mut().expect("failed to open stdin");
80 stdin
81 .write_all(src.as_bytes())
82 .expect("failed to write source to stdin");
83
84 let status = child.wait().expect("failed to wait for compiler");
85 assert!(status.success(), "failed to build plugin");
86
87 RemovePath(out_bin)
88 }
89
run_plugin(bin_path: &Path, with_sandbox: bool)90 fn run_plugin(bin_path: &Path, with_sandbox: bool) {
91 let mut crosvm_path = get_crosvm_path();
92 crosvm_path.push("crosvm");
93 let mut cmd = Command::new(crosvm_path);
94 cmd.args([
95 "run",
96 "-c",
97 "1",
98 "--seccomp-policy-dir",
99 "tests",
100 "--plugin",
101 ])
102 .arg(
103 bin_path
104 .canonicalize()
105 .expect("failed to canonicalize plugin path"),
106 );
107
108 if *TAP_AVAILABLE {
109 cmd.args([
110 "--host-ip",
111 "100.115.92.5",
112 "--netmask",
113 "255.255.255.252",
114 "--mac",
115 "de:21:e8:47:6b:6a",
116 ]);
117 }
118 if !with_sandbox {
119 cmd.arg("--disable-sandbox");
120 }
121
122 let mut child = cmd.spawn().expect("failed to spawn crosvm");
123 for _ in 0..12 {
124 match child.try_wait().expect("failed to wait for crosvm") {
125 Some(status) => {
126 assert!(status.success());
127 return;
128 }
129 None => sleep(Duration::from_millis(100)),
130 }
131 }
132 child.kill().expect("failed to kill crosvm");
133 panic!("crosvm process has timed out");
134 }
135
test_plugin(src: &str)136 fn test_plugin(src: &str) {
137 let bin_path = build_plugin(src);
138 // Run with and without the sandbox enabled.
139 run_plugin(&bin_path.0, false);
140 run_plugin(&bin_path.0, true);
141 }
142
keep_fd_on_exec<F: AsRawDescriptor>(f: &F)143 fn keep_fd_on_exec<F: AsRawDescriptor>(f: &F) {
144 // SAFETY: safe because function doesn't modify memory and we don't care about the return
145 // value.
146 unsafe {
147 ioctl(f, 0x5450 /* FIONCLEX */);
148 }
149 }
150
151 /// Takes assembly source code and returns the resulting assembly code.
build_assembly(src: &str) -> Vec<u8>152 fn build_assembly(src: &str) -> Vec<u8> {
153 // Creates a file with the assembly source code in it.
154 let mut in_file = tempfile().expect("failed to create tempfile");
155 keep_fd_on_exec(&in_file);
156 in_file.write_all(src.as_bytes()).unwrap();
157
158 // Creates a file that will hold the nasm output.
159 let mut out_file = tempfile().expect("failed to create tempfile");
160 keep_fd_on_exec(&out_file);
161
162 // Runs nasm with the input and output files set to the FDs of the above shared memory regions,
163 // which we have preserved accross exec.
164 let status = Command::new("nasm")
165 .arg(format!("/proc/self/fd/{}", in_file.as_raw_descriptor()))
166 .args(["-f", "bin", "-o"])
167 .arg(format!("/proc/self/fd/{}", out_file.as_raw_descriptor()))
168 .status()
169 .expect("failed to spawn assembler");
170 assert!(status.success());
171
172 let mut out_bytes = Vec::new();
173 out_file.read_to_end(&mut out_bytes).unwrap();
174 out_bytes
175 }
176
177 // Converts the input bytes to an output string in the format "0x01,0x02,0x03...".
format_as_hex(data: &[u8]) -> String178 fn format_as_hex(data: &[u8]) -> String {
179 let mut out = String::new();
180 for (i, d) in data.iter().enumerate() {
181 #[allow(clippy::format_push_string)]
182 out.push_str(&format!("0x{:02x}", d));
183 if i < data.len() - 1 {
184 out.push(',')
185 }
186 }
187 out
188 }
189
190 // A testing framework for creating simple plugins.
191 struct MiniPlugin {
192 // The size in bytes of the guest memory based at 0x0000.
193 mem_size: u64,
194 // The address in guest memory to load the assembly code.
195 load_address: u32,
196 // The nasm syntax 16-bit assembly code that will assembled and loaded into guest memory.
197 assembly_src: &'static str,
198 // The C source code that will be included in the mini_plugin_template.c file. This code must
199 // define the forward declarations above the {src} line so that the completed plugin source
200 // will compile.
201 src: &'static str,
202 }
203
204 impl Default for MiniPlugin {
default() -> Self205 fn default() -> Self {
206 MiniPlugin {
207 mem_size: 0x2000,
208 load_address: 0x1000,
209 assembly_src: "hlt",
210 src: "",
211 }
212 }
213 }
214
215 // Builds and tests the given MiniPlugin definiton.
test_mini_plugin(plugin: &MiniPlugin)216 fn test_mini_plugin(plugin: &MiniPlugin) {
217 // Adds a preamble to ensure the output opcodes are 16-bit real mode and the lables start at the
218 // load address.
219 let assembly_src = format!(
220 "org 0x{:x}\nbits 16\n{}",
221 plugin.load_address, plugin.assembly_src
222 );
223
224 // Builds the assembly and convert it to a C literal array format.
225 let assembly = build_assembly(&assembly_src);
226 let assembly_hex = format_as_hex(&assembly);
227
228 // Glues the pieces of this plugin together and tests the completed plugin.
229 let generated_src = format!(
230 include_str!("mini_plugin_template.c"),
231 mem_size = plugin.mem_size,
232 load_address = plugin.load_address,
233 assembly_code = assembly_hex,
234 src = plugin.src
235 );
236 test_plugin(&generated_src);
237 }
238
239 #[test]
test_adder()240 fn test_adder() {
241 test_plugin(include_str!("plugin_adder.c"));
242 }
243
244 #[ignore] // TODO(b/239094055): fix the SIGSTOP usage that stops the cargo test runner
245 #[test]
test_hint()246 fn test_hint() {
247 test_plugin(include_str!("plugin_hint.c"));
248 }
249
250 #[test]
test_async_write()251 fn test_async_write() {
252 test_plugin(include_str!("plugin_async_write.c"));
253 }
254
255 #[test]
test_dirty_log()256 fn test_dirty_log() {
257 test_plugin(include_str!("plugin_dirty_log.c"));
258 }
259
260 #[test]
test_ioevent()261 fn test_ioevent() {
262 test_plugin(include_str!("plugin_ioevent.c"));
263 }
264
265 #[test]
test_irqfd()266 fn test_irqfd() {
267 test_plugin(include_str!("plugin_irqfd.c"));
268 }
269
270 #[test]
test_extensions()271 fn test_extensions() {
272 test_plugin(include_str!("plugin_extensions.c"));
273 }
274
275 #[test]
test_supported_cpuid()276 fn test_supported_cpuid() {
277 test_plugin(include_str!("plugin_supported_cpuid.c"));
278 }
279
280 // b:223675792
281 #[ignore]
282 #[test]
test_enable_cap()283 fn test_enable_cap() {
284 test_plugin(include_str!("plugin_enable_cap.c"));
285 }
286
287 #[test]
test_msr_index_list()288 fn test_msr_index_list() {
289 test_plugin(include_str!("plugin_msr_index_list.c"));
290 }
291
292 #[test]
test_vm_state_manipulation()293 fn test_vm_state_manipulation() {
294 test_plugin(include_str!("plugin_vm_state.c"));
295 }
296
297 #[test]
test_vcpu_pause()298 fn test_vcpu_pause() {
299 test_plugin(include_str!("plugin_vcpu_pause.c"));
300 }
301
302 #[test]
test_net_config()303 fn test_net_config() {
304 if *TAP_AVAILABLE {
305 test_plugin(include_str!("plugin_net_config.c"));
306 }
307 }
308
309 #[test]
test_debugregs()310 fn test_debugregs() {
311 let mini_plugin = MiniPlugin {
312 assembly_src: "org 0x1000
313 bits 16
314 mov dr0, ebx
315 mov eax, dr1
316 mov byte [0x3000], 1",
317 src: r#"
318 #define DR1_VALUE 0x12
319 #define RBX_VALUE 0xabcdef00
320 #define KILL_ADDRESS 0x3000
321
322 int g_kill_evt;
323 struct kvm_regs g_regs;
324 struct kvm_debugregs g_dregs;
325
326 int setup_vm(struct crosvm *crosvm, void *mem) {
327 g_kill_evt = crosvm_get_shutdown_eventfd(crosvm);
328 crosvm_reserve_range(crosvm, CROSVM_ADDRESS_SPACE_MMIO, KILL_ADDRESS, 1);
329 return 0;
330 }
331
332 int handle_vpcu_init(struct crosvm_vcpu *vcpu, struct kvm_regs *regs,
333 struct kvm_sregs *sregs)
334 {
335 regs->rbx = RBX_VALUE;
336 struct kvm_debugregs dregs;
337 crosvm_vcpu_get_debugregs(vcpu, &dregs);
338 dregs.db[1] = DR1_VALUE;
339 crosvm_vcpu_set_debugregs(vcpu, &dregs);
340 return 0;
341 }
342
343 int handle_vpcu_evt(struct crosvm_vcpu *vcpu, struct crosvm_vcpu_event evt) {
344 if (evt.kind == CROSVM_VCPU_EVENT_KIND_IO_ACCESS &&
345 evt.io_access.address_space == CROSVM_ADDRESS_SPACE_MMIO &&
346 evt.io_access.address == KILL_ADDRESS &&
347 evt.io_access.is_write &&
348 evt.io_access.length == 1 &&
349 evt.io_access.data[0] == 1)
350 {
351 uint64_t dummy = 1;
352 crosvm_vcpu_get_debugregs(vcpu, &g_dregs);
353 crosvm_vcpu_get_regs(vcpu, &g_regs);
354 write(g_kill_evt, &dummy, sizeof(dummy));
355 return 1;
356 }
357 return 0;
358 }
359
360 int check_result(struct crosvm *vcpu, void *mem) {
361 if (g_dregs.db[1] != DR1_VALUE) {
362 fprintf(stderr, "dr1 register has unexpected value: 0x%x\n", g_dregs.db[1]);
363 return 1;
364 }
365 if (g_dregs.db[0] != RBX_VALUE) {
366 fprintf(stderr, "dr0 register has unexpected value: 0x%x\n", g_dregs.db[0]);
367 return 1;
368 }
369 if (g_regs.rax != DR1_VALUE) {
370 fprintf(stderr, "eax register has unexpected value: 0x%x\n", g_regs.rax);
371 return 1;
372 }
373 return 0;
374 }"#,
375 ..Default::default()
376 };
377 test_mini_plugin(&mini_plugin);
378 }
379
380 #[test]
test_xcrs()381 fn test_xcrs() {
382 let mini_plugin = MiniPlugin {
383 assembly_src: "org 0x1000
384 bits 16
385 mov byte [0x3000], 1",
386 src: r#"
387 #define XCR0_VALUE 0x1
388 #define KILL_ADDRESS 0x3000
389
390 int g_kill_evt;
391 struct kvm_xcrs g_xcrs;
392
393 int setup_vm(struct crosvm *crosvm, void *mem) {
394 g_kill_evt = crosvm_get_shutdown_eventfd(crosvm);
395 crosvm_reserve_range(crosvm, CROSVM_ADDRESS_SPACE_MMIO, KILL_ADDRESS, 1);
396 return 0;
397 }
398
399 int handle_vpcu_init(struct crosvm_vcpu *vcpu, struct kvm_regs *regs,
400 struct kvm_sregs *sregs)
401 {
402 struct kvm_xcrs xcrs = {};
403 xcrs.nr_xcrs = 1;
404 xcrs.xcrs[0].value = XCR0_VALUE;
405 crosvm_vcpu_set_xcrs(vcpu, &xcrs);
406 return 0;
407 }
408
409 int handle_vpcu_evt(struct crosvm_vcpu *vcpu, struct crosvm_vcpu_event evt) {
410 if (evt.kind == CROSVM_VCPU_EVENT_KIND_IO_ACCESS &&
411 evt.io_access.address_space == CROSVM_ADDRESS_SPACE_MMIO &&
412 evt.io_access.address == KILL_ADDRESS &&
413 evt.io_access.is_write &&
414 evt.io_access.length == 1 &&
415 evt.io_access.data[0] == 1)
416 {
417 uint64_t dummy = 1;
418 crosvm_vcpu_get_xcrs(vcpu, &g_xcrs);
419 write(g_kill_evt, &dummy, sizeof(dummy));
420 return 1;
421 }
422 return 0;
423 }
424
425 int check_result(struct crosvm *vcpu, void *mem) {
426 if (g_xcrs.xcrs[0].value != XCR0_VALUE) {
427 fprintf(stderr, "xcr0 register has unexpected value: 0x%x\n",
428 g_xcrs.xcrs[0].value);
429 return 1;
430 }
431 return 0;
432 }"#,
433 ..Default::default()
434 };
435 test_mini_plugin(&mini_plugin);
436 }
437
438 #[test]
test_msrs()439 fn test_msrs() {
440 let mini_plugin = MiniPlugin {
441 assembly_src: "org 0x1000
442 bits 16
443 rdmsr
444 mov [0x0], eax
445 mov [0x4], edx
446 mov ecx, ebx
447 mov eax, [0x8]
448 mov edx, [0xc]
449 wrmsr
450 mov byte [es:0], 1",
451 src: r#"
452 #define MSR1_INDEX 0x00000174
453 #define MSR1_DATA 1
454 #define MSR2_INDEX 0x00000175
455 #define MSR2_DATA 2
456 #define KILL_ADDRESS 0x3000
457
458 int g_kill_evt;
459 uint32_t g_msr2_count;
460 struct kvm_msr_entry g_msr2;
461
462 int setup_vm(struct crosvm *crosvm, void *mem) {
463 g_kill_evt = crosvm_get_shutdown_eventfd(crosvm);
464 crosvm_reserve_range(crosvm, CROSVM_ADDRESS_SPACE_MMIO, KILL_ADDRESS, 1);
465 ((uint64_t*)mem)[1] = MSR2_DATA;
466 return 0;
467 }
468
469 int handle_vpcu_init(struct crosvm_vcpu *vcpu, struct kvm_regs *regs,
470 struct kvm_sregs *sregs)
471 {
472 regs->rcx = MSR1_INDEX;
473 regs->rbx = MSR2_INDEX;
474 sregs->es.base = KILL_ADDRESS;
475
476 struct kvm_msr_entry msr1 = {0};
477 msr1.index = MSR1_INDEX;
478 msr1.data = MSR1_DATA;
479 crosvm_vcpu_set_msrs(vcpu, 1, &msr1);
480
481 return 0;
482 }
483
484 int handle_vpcu_evt(struct crosvm_vcpu *vcpu, struct crosvm_vcpu_event evt) {
485 if (evt.kind == CROSVM_VCPU_EVENT_KIND_IO_ACCESS &&
486 evt.io_access.address_space == CROSVM_ADDRESS_SPACE_MMIO &&
487 evt.io_access.address == KILL_ADDRESS &&
488 evt.io_access.is_write &&
489 evt.io_access.length == 1 &&
490 evt.io_access.data[0] == 1)
491 {
492 uint64_t dummy = 1;
493 g_msr2.index = MSR2_INDEX;
494 crosvm_vcpu_get_msrs(vcpu, 1, &g_msr2, &g_msr2_count);
495 write(g_kill_evt, &dummy, sizeof(dummy));
496 return 1;
497 }
498 return 0;
499 }
500
501 int check_result(struct crosvm *vcpu, void *mem) {
502 uint64_t msr1_data = ((uint64_t*)mem)[0];
503 if (msr1_data != MSR1_DATA) {
504 fprintf(stderr, "msr1 has unexpected value: 0x%x\n", msr1_data);
505 return 1;
506 }
507 if (g_msr2_count != 1) {
508 fprintf(stderr, "incorrect number of returned MSRSs: %d\n", g_msr2_count);
509 return 1;
510 }
511 if (g_msr2.data != MSR2_DATA) {
512 fprintf(stderr, "msr2 has unexpected value: 0x%x\n", g_msr2.data);
513 return 1;
514 }
515 return 0;
516 }"#,
517 ..Default::default()
518 };
519 test_mini_plugin(&mini_plugin);
520 }
521
522 #[test]
test_cpuid()523 fn test_cpuid() {
524 let mini_plugin = MiniPlugin {
525 assembly_src: "org 0x1000
526 bits 16
527 push eax
528 push ecx
529 cpuid
530 mov [0x0], eax
531 mov [0x4], ebx
532 mov [0x8], ecx
533 mov [0xc], edx
534 pop ecx
535 pop eax
536 add ecx, 1
537 cpuid
538 mov [0x10], eax
539 mov [0x14], ebx
540 mov [0x18], ecx
541 mov [0x1c], edx
542 mov byte [es:0], 1",
543 src: r#"
544 #define ENTRY1_INDEX 0
545 #define ENTRY1_EAX 0x40414243
546 #define ENTRY1_EBX 0x50515253
547 #define ENTRY1_ECX 0x60616263
548 #define ENTRY1_EDX 0x71727374
549 #define ENTRY2_INDEX 1
550 #define ENTRY2_EAX 0xAABBCCDD
551 #define ENTRY2_EBX 0xEEFF0011
552 #define ENTRY2_ECX 0x22334455
553 #define ENTRY2_EDX 0x66778899
554 #define KILL_ADDRESS 0x3000
555
556 int g_kill_evt;
557 struct kvm_msr_entry g_msr2;
558
559 int setup_vm(struct crosvm *crosvm, void *mem) {
560 g_kill_evt = crosvm_get_shutdown_eventfd(crosvm);
561 crosvm_reserve_range(crosvm, CROSVM_ADDRESS_SPACE_MMIO, KILL_ADDRESS, 1);
562 return 0;
563 }
564
565 int handle_vpcu_init(struct crosvm_vcpu *vcpu, struct kvm_regs *regs,
566 struct kvm_sregs *sregs)
567 {
568 regs->rax = ENTRY1_INDEX;
569 regs->rcx = 0;
570 regs->rsp = 0x1000;
571 sregs->es.base = KILL_ADDRESS;
572
573 struct kvm_cpuid_entry2 entries[2];
574 entries[0].function = 0;
575 entries[0].index = ENTRY1_INDEX;
576 entries[0].flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
577 entries[0].eax = ENTRY1_EAX;
578 entries[0].ebx = ENTRY1_EBX;
579 entries[0].ecx = ENTRY1_ECX;
580 entries[0].edx = ENTRY1_EDX;
581 entries[1].function = 0;
582 entries[1].index = ENTRY2_INDEX;
583 entries[1].flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
584 entries[1].eax = ENTRY2_EAX;
585 entries[1].ebx = ENTRY2_EBX;
586 entries[1].ecx = ENTRY2_ECX;
587 entries[1].edx = ENTRY2_EDX;
588 return crosvm_vcpu_set_cpuid(vcpu, 2, entries);
589 }
590
591 int handle_vpcu_evt(struct crosvm_vcpu *vcpu, struct crosvm_vcpu_event evt) {
592 if (evt.kind == CROSVM_VCPU_EVENT_KIND_IO_ACCESS &&
593 evt.io_access.address_space == CROSVM_ADDRESS_SPACE_MMIO &&
594 evt.io_access.address == KILL_ADDRESS &&
595 evt.io_access.is_write &&
596 evt.io_access.length == 1 &&
597 evt.io_access.data[0] == 1)
598 {
599 uint64_t dummy = 1;
600 write(g_kill_evt, &dummy, sizeof(dummy));
601 return 1;
602 }
603 return 0;
604 }
605
606 int check_result(struct crosvm *vcpu, void *memory) {
607 uint32_t *mem = (uint32_t*)memory;
608 if (mem[0] != ENTRY1_EAX) {
609 fprintf(stderr, "entry 1 eax has unexpected value: 0x%x\n", mem[0]);
610 return 1;
611 }
612 if (mem[1] != ENTRY1_EBX) {
613 fprintf(stderr, "entry 1 ebx has unexpected value: 0x%x\n", mem[1]);
614 return 1;
615 }
616 if (mem[2] != ENTRY1_ECX) {
617 fprintf(stderr, "entry 1 ecx has unexpected value: 0x%x\n", mem[2]);
618 return 1;
619 }
620 if (mem[3] != ENTRY1_EDX) {
621 fprintf(stderr, "entry 1 edx has unexpected value: 0x%x\n", mem[3]);
622 return 1;
623 }
624 if (mem[4] != ENTRY2_EAX) {
625 fprintf(stderr, "entry 2 eax has unexpected value: 0x%x\n", mem[4]);
626 return 1;
627 }
628 if (mem[5] != ENTRY2_EBX) {
629 fprintf(stderr, "entry 2 ebx has unexpected value: 0x%x\n", mem[5]);
630 return 1;
631 }
632 if (mem[6] != ENTRY2_ECX) {
633 fprintf(stderr, "entry 2 ecx has unexpected value: 0x%x\n", mem[6]);
634 return 1;
635 }
636 if (mem[7] != ENTRY2_EDX) {
637 fprintf(stderr, "entry 2 edx has unexpected value: 0x%x\n", mem[7]);
638 return 1;
639 }
640 return 0;
641 }"#,
642 ..Default::default()
643 };
644 test_mini_plugin(&mini_plugin);
645 }
646
647 #[test]
test_vcpu_state_manipulation()648 fn test_vcpu_state_manipulation() {
649 let mini_plugin = MiniPlugin {
650 assembly_src: "org 0x1000
651 bits 16
652 mov byte [0x3000], 1",
653 src: r#"
654 #define KILL_ADDRESS 0x3000
655
656 int g_kill_evt;
657 bool success = false;
658
659 int setup_vm(struct crosvm *crosvm, void *mem) {
660 g_kill_evt = crosvm_get_shutdown_eventfd(crosvm);
661 crosvm_reserve_range(crosvm, CROSVM_ADDRESS_SPACE_MMIO, KILL_ADDRESS, 1);
662 return 0;
663 }
664
665 int handle_vpcu_init(struct crosvm_vcpu *vcpu, struct kvm_regs *regs,
666 struct kvm_sregs *sregs)
667 {
668 int ret;
669
670 struct kvm_lapic_state lapic;
671 ret = crosvm_vcpu_get_lapic_state(vcpu, &lapic);
672 if (ret < 0) {
673 fprintf(stderr, "failed to get initial LAPIC state: %d\n", ret);
674 return 1;
675 }
676
677 ret = crosvm_vcpu_set_lapic_state(vcpu, &lapic);
678 if (ret < 0) {
679 fprintf(stderr, "failed to update LAPIC state: %d\n", ret);
680 return 1;
681 }
682
683 ret = crosvm_vcpu_get_lapic_state(vcpu, &lapic);
684 if (ret < 0) {
685 fprintf(stderr, "failed to get updated LAPIC state: %d\n", ret);
686 return 1;
687 }
688
689 struct kvm_mp_state mp_state;
690 ret = crosvm_vcpu_get_mp_state(vcpu, &mp_state);
691 if (ret < 0) {
692 fprintf(stderr, "failed to get initial MP state: %d\n", ret);
693 return 1;
694 }
695
696 ret = crosvm_vcpu_set_mp_state(vcpu, &mp_state);
697 if (ret < 0) {
698 fprintf(stderr, "failed to update MP state: %d\n", ret);
699 return 1;
700 }
701
702 struct kvm_vcpu_events events;
703 ret = crosvm_vcpu_get_vcpu_events(vcpu, &events);
704 if (ret < 0) {
705 fprintf(stderr, "failed to get VCPU events: %d\n", ret);
706 return 1;
707 }
708
709 ret = crosvm_vcpu_set_vcpu_events(vcpu, &events);
710 if (ret < 0) {
711 fprintf(stderr, "failed to set VCPU events: %d\n", ret);
712 return 1;
713 }
714
715 success = true;
716 return 0;
717 }
718
719 int handle_vpcu_evt(struct crosvm_vcpu *vcpu, struct crosvm_vcpu_event evt) {
720 if (evt.kind == CROSVM_VCPU_EVENT_KIND_IO_ACCESS &&
721 evt.io_access.address_space == CROSVM_ADDRESS_SPACE_MMIO &&
722 evt.io_access.address == KILL_ADDRESS &&
723 evt.io_access.is_write &&
724 evt.io_access.length == 1 &&
725 evt.io_access.data[0] == 1)
726 {
727 uint64_t dummy = 1;
728 write(g_kill_evt, &dummy, sizeof(dummy));
729 return 1;
730 }
731 return 0;
732 }
733
734 int check_result(struct crosvm *vcpu, void *mem) {
735 if (!success) {
736 fprintf(stderr, "test failed\n");
737 return 1;
738 }
739 return 0;
740 }"#,
741 ..Default::default()
742 };
743 test_mini_plugin(&mini_plugin);
744 }
745