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