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;
10 use std::io::{Read, Write};
11 use std::path::{Path, PathBuf};
12 use std::process::{Command, Stdio};
13 use std::thread::sleep;
14 use std::time::Duration;
15
16 use base::{ioctl, AsRawDescriptor};
17 use rand_ish::urandom_str;
18 use tempfile::tempfile;
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(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: AsRawDescriptor>(f: &F)129 fn keep_fd_on_exec<F: AsRawDescriptor>(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 file with the assembly source code in it.
138 let mut in_file = tempfile().expect("failed to create tempfile");
139 keep_fd_on_exec(&in_file);
140 in_file.write_all(src.as_bytes()).unwrap();
141
142 // Creates a file that will hold the nasm output.
143 let mut out_file = tempfile().expect("failed to create tempfile");
144 keep_fd_on_exec(&out_file);
145
146 // Runs nasm with the input and output files set to the FDs of the above shared memory regions,
147 // which we have preserved accross exec.
148 let status = Command::new("nasm")
149 .arg(format!("/proc/self/fd/{}", in_file.as_raw_descriptor()))
150 .args(&["-f", "bin", "-o"])
151 .arg(format!("/proc/self/fd/{}", out_file.as_raw_descriptor()))
152 .status()
153 .expect("failed to spawn assembler");
154 assert!(status.success());
155
156 let mut out_bytes = Vec::new();
157 out_file.read_to_end(&mut out_bytes).unwrap();
158 out_bytes
159 }
160
161 // Converts the input bytes to an output string in the format "0x01,0x02,0x03...".
format_as_hex(data: &[u8]) -> String162 fn format_as_hex(data: &[u8]) -> String {
163 let mut out = String::new();
164 for (i, d) in data.iter().enumerate() {
165 out.push_str(&format!("0x{:02x}", d));
166 if i < data.len() - 1 {
167 out.push(',')
168 }
169 }
170 out
171 }
172
173 // A testing framework for creating simple plugins.
174 struct MiniPlugin {
175 // The size in bytes of the guest memory based at 0x0000.
176 mem_size: u64,
177 // The address in guest memory to load the assembly code.
178 load_address: u32,
179 // The nasm syntax 16-bit assembly code that will assembled and loaded into guest memory.
180 assembly_src: &'static str,
181 // The C source code that will be included in the mini_plugin_template.c file. This code must
182 // define the forward declarations above the {src} line so that the completed plugin source will
183 // compile.
184 src: &'static str,
185 }
186
187 impl Default for MiniPlugin {
default() -> Self188 fn default() -> Self {
189 MiniPlugin {
190 mem_size: 0x2000,
191 load_address: 0x1000,
192 assembly_src: "hlt",
193 src: "",
194 }
195 }
196 }
197
198 // Builds and tests the given MiniPlugin definiton.
test_mini_plugin(plugin: &MiniPlugin)199 fn test_mini_plugin(plugin: &MiniPlugin) {
200 // Adds a preamble to ensure the output opcodes are 16-bit real mode and the lables start at the
201 // load address.
202 let assembly_src = format!(
203 "org 0x{:x}\nbits 16\n{}",
204 plugin.load_address, plugin.assembly_src
205 );
206
207 // Builds the assembly and convert it to a C literal array format.
208 let assembly = build_assembly(&assembly_src);
209 let assembly_hex = format_as_hex(&assembly);
210
211 // Glues the pieces of this plugin together and tests the completed plugin.
212 let generated_src = format!(
213 include_str!("mini_plugin_template.c"),
214 mem_size = plugin.mem_size,
215 load_address = plugin.load_address,
216 assembly_code = assembly_hex,
217 src = plugin.src
218 );
219 test_plugin(&generated_src);
220 }
221
222 #[test]
test_adder()223 fn test_adder() {
224 test_plugin(include_str!("plugin_adder.c"));
225 }
226
227 #[test]
test_hint()228 fn test_hint() {
229 test_plugin(include_str!("plugin_hint.c"));
230 }
231
232 #[test]
test_async_write()233 fn test_async_write() {
234 test_plugin(include_str!("plugin_async_write.c"));
235 }
236
237 #[test]
test_dirty_log()238 fn test_dirty_log() {
239 test_plugin(include_str!("plugin_dirty_log.c"));
240 }
241
242 #[test]
test_ioevent()243 fn test_ioevent() {
244 test_plugin(include_str!("plugin_ioevent.c"));
245 }
246
247 #[test]
test_irqfd()248 fn test_irqfd() {
249 test_plugin(include_str!("plugin_irqfd.c"));
250 }
251
252 #[test]
test_extensions()253 fn test_extensions() {
254 test_plugin(include_str!("plugin_extensions.c"));
255 }
256
257 #[test]
test_supported_cpuid()258 fn test_supported_cpuid() {
259 test_plugin(include_str!("plugin_supported_cpuid.c"));
260 }
261
262 #[test]
test_enable_cap()263 fn test_enable_cap() {
264 test_plugin(include_str!("plugin_enable_cap.c"));
265 }
266
267 #[test]
test_msr_index_list()268 fn test_msr_index_list() {
269 test_plugin(include_str!("plugin_msr_index_list.c"));
270 }
271
272 #[test]
test_vm_state_manipulation()273 fn test_vm_state_manipulation() {
274 test_plugin(include_str!("plugin_vm_state.c"));
275 }
276
277 #[test]
test_vcpu_pause()278 fn test_vcpu_pause() {
279 test_plugin(include_str!("plugin_vcpu_pause.c"));
280 }
281
282 #[test]
test_net_config()283 fn test_net_config() {
284 test_plugin(include_str!("plugin_net_config.c"));
285 }
286
287 #[test]
test_debugregs()288 fn test_debugregs() {
289 let mini_plugin = MiniPlugin {
290 assembly_src: "org 0x1000
291 bits 16
292 mov dr0, ebx
293 mov eax, dr1
294 mov byte [0x3000], 1",
295 src: r#"
296 #define DR1_VALUE 0x12
297 #define RBX_VALUE 0xabcdef00
298 #define KILL_ADDRESS 0x3000
299
300 int g_kill_evt;
301 struct kvm_regs g_regs;
302 struct kvm_debugregs g_dregs;
303
304 int setup_vm(struct crosvm *crosvm, void *mem) {
305 g_kill_evt = crosvm_get_shutdown_eventfd(crosvm);
306 crosvm_reserve_range(crosvm, CROSVM_ADDRESS_SPACE_MMIO, KILL_ADDRESS, 1);
307 return 0;
308 }
309
310 int handle_vpcu_init(struct crosvm_vcpu *vcpu, struct kvm_regs *regs,
311 struct kvm_sregs *sregs)
312 {
313 regs->rbx = RBX_VALUE;
314 struct kvm_debugregs dregs;
315 crosvm_vcpu_get_debugregs(vcpu, &dregs);
316 dregs.db[1] = DR1_VALUE;
317 crosvm_vcpu_set_debugregs(vcpu, &dregs);
318 return 0;
319 }
320
321 int handle_vpcu_evt(struct crosvm_vcpu *vcpu, struct crosvm_vcpu_event evt) {
322 if (evt.kind == CROSVM_VCPU_EVENT_KIND_IO_ACCESS &&
323 evt.io_access.address_space == CROSVM_ADDRESS_SPACE_MMIO &&
324 evt.io_access.address == KILL_ADDRESS &&
325 evt.io_access.is_write &&
326 evt.io_access.length == 1 &&
327 evt.io_access.data[0] == 1)
328 {
329 uint64_t dummy = 1;
330 crosvm_vcpu_get_debugregs(vcpu, &g_dregs);
331 crosvm_vcpu_get_regs(vcpu, &g_regs);
332 write(g_kill_evt, &dummy, sizeof(dummy));
333 return 1;
334 }
335 return 0;
336 }
337
338 int check_result(struct crosvm *vcpu, void *mem) {
339 if (g_dregs.db[1] != DR1_VALUE) {
340 fprintf(stderr, "dr1 register has unexpected value: 0x%x\n", g_dregs.db[1]);
341 return 1;
342 }
343 if (g_dregs.db[0] != RBX_VALUE) {
344 fprintf(stderr, "dr0 register has unexpected value: 0x%x\n", g_dregs.db[0]);
345 return 1;
346 }
347 if (g_regs.rax != DR1_VALUE) {
348 fprintf(stderr, "eax register has unexpected value: 0x%x\n", g_regs.rax);
349 return 1;
350 }
351 return 0;
352 }"#,
353 ..Default::default()
354 };
355 test_mini_plugin(&mini_plugin);
356 }
357
358 #[test]
test_xcrs()359 fn test_xcrs() {
360 let mini_plugin = MiniPlugin {
361 assembly_src: "org 0x1000
362 bits 16
363 mov byte [0x3000], 1",
364 src: r#"
365 #define XCR0_VALUE 0x1
366 #define KILL_ADDRESS 0x3000
367
368 int g_kill_evt;
369 struct kvm_xcrs g_xcrs;
370
371 int setup_vm(struct crosvm *crosvm, void *mem) {
372 g_kill_evt = crosvm_get_shutdown_eventfd(crosvm);
373 crosvm_reserve_range(crosvm, CROSVM_ADDRESS_SPACE_MMIO, KILL_ADDRESS, 1);
374 return 0;
375 }
376
377 int handle_vpcu_init(struct crosvm_vcpu *vcpu, struct kvm_regs *regs,
378 struct kvm_sregs *sregs)
379 {
380 struct kvm_xcrs xcrs = {};
381 xcrs.nr_xcrs = 1;
382 xcrs.xcrs[0].value = XCR0_VALUE;
383 crosvm_vcpu_set_xcrs(vcpu, &xcrs);
384 return 0;
385 }
386
387 int handle_vpcu_evt(struct crosvm_vcpu *vcpu, struct crosvm_vcpu_event evt) {
388 if (evt.kind == CROSVM_VCPU_EVENT_KIND_IO_ACCESS &&
389 evt.io_access.address_space == CROSVM_ADDRESS_SPACE_MMIO &&
390 evt.io_access.address == KILL_ADDRESS &&
391 evt.io_access.is_write &&
392 evt.io_access.length == 1 &&
393 evt.io_access.data[0] == 1)
394 {
395 uint64_t dummy = 1;
396 crosvm_vcpu_get_xcrs(vcpu, &g_xcrs);
397 write(g_kill_evt, &dummy, sizeof(dummy));
398 return 1;
399 }
400 return 0;
401 }
402
403 int check_result(struct crosvm *vcpu, void *mem) {
404 if (g_xcrs.xcrs[0].value != XCR0_VALUE) {
405 fprintf(stderr, "xcr0 register has unexpected value: 0x%x\n",
406 g_xcrs.xcrs[0].value);
407 return 1;
408 }
409 return 0;
410 }"#,
411 ..Default::default()
412 };
413 test_mini_plugin(&mini_plugin);
414 }
415
416 #[test]
test_msrs()417 fn test_msrs() {
418 let mini_plugin = MiniPlugin {
419 assembly_src: "org 0x1000
420 bits 16
421 rdmsr
422 mov [0x0], eax
423 mov [0x4], edx
424 mov ecx, ebx
425 mov eax, [0x8]
426 mov edx, [0xc]
427 wrmsr
428 mov byte [es:0], 1",
429 src: r#"
430 #define MSR1_INDEX 0x00000174
431 #define MSR1_DATA 1
432 #define MSR2_INDEX 0x00000175
433 #define MSR2_DATA 2
434 #define KILL_ADDRESS 0x3000
435
436 int g_kill_evt;
437 uint32_t g_msr2_count;
438 struct kvm_msr_entry g_msr2;
439
440 int setup_vm(struct crosvm *crosvm, void *mem) {
441 g_kill_evt = crosvm_get_shutdown_eventfd(crosvm);
442 crosvm_reserve_range(crosvm, CROSVM_ADDRESS_SPACE_MMIO, KILL_ADDRESS, 1);
443 ((uint64_t*)mem)[1] = MSR2_DATA;
444 return 0;
445 }
446
447 int handle_vpcu_init(struct crosvm_vcpu *vcpu, struct kvm_regs *regs,
448 struct kvm_sregs *sregs)
449 {
450 regs->rcx = MSR1_INDEX;
451 regs->rbx = MSR2_INDEX;
452 sregs->es.base = KILL_ADDRESS;
453
454 struct kvm_msr_entry msr1 = {0};
455 msr1.index = MSR1_INDEX;
456 msr1.data = MSR1_DATA;
457 crosvm_vcpu_set_msrs(vcpu, 1, &msr1);
458
459 return 0;
460 }
461
462 int handle_vpcu_evt(struct crosvm_vcpu *vcpu, struct crosvm_vcpu_event evt) {
463 if (evt.kind == CROSVM_VCPU_EVENT_KIND_IO_ACCESS &&
464 evt.io_access.address_space == CROSVM_ADDRESS_SPACE_MMIO &&
465 evt.io_access.address == KILL_ADDRESS &&
466 evt.io_access.is_write &&
467 evt.io_access.length == 1 &&
468 evt.io_access.data[0] == 1)
469 {
470 uint64_t dummy = 1;
471 g_msr2.index = MSR2_INDEX;
472 crosvm_vcpu_get_msrs(vcpu, 1, &g_msr2, &g_msr2_count);
473 write(g_kill_evt, &dummy, sizeof(dummy));
474 return 1;
475 }
476 return 0;
477 }
478
479 int check_result(struct crosvm *vcpu, void *mem) {
480 uint64_t msr1_data = ((uint64_t*)mem)[0];
481 if (msr1_data != MSR1_DATA) {
482 fprintf(stderr, "msr1 has unexpected value: 0x%x\n", msr1_data);
483 return 1;
484 }
485 if (g_msr2_count != 1) {
486 fprintf(stderr, "incorrect number of returned MSRSs: %d\n", g_msr2_count);
487 return 1;
488 }
489 if (g_msr2.data != MSR2_DATA) {
490 fprintf(stderr, "msr2 has unexpected value: 0x%x\n", g_msr2.data);
491 return 1;
492 }
493 return 0;
494 }"#,
495 ..Default::default()
496 };
497 test_mini_plugin(&mini_plugin);
498 }
499
500 #[test]
test_cpuid()501 fn test_cpuid() {
502 let mini_plugin = MiniPlugin {
503 assembly_src: "org 0x1000
504 bits 16
505 push eax
506 push ecx
507 cpuid
508 mov [0x0], eax
509 mov [0x4], ebx
510 mov [0x8], ecx
511 mov [0xc], edx
512 pop ecx
513 pop eax
514 add ecx, 1
515 cpuid
516 mov [0x10], eax
517 mov [0x14], ebx
518 mov [0x18], ecx
519 mov [0x1c], edx
520 mov byte [es:0], 1",
521 src: r#"
522 #define ENTRY1_INDEX 0
523 #define ENTRY1_EAX 0x40414243
524 #define ENTRY1_EBX 0x50515253
525 #define ENTRY1_ECX 0x60616263
526 #define ENTRY1_EDX 0x71727374
527 #define ENTRY2_INDEX 1
528 #define ENTRY2_EAX 0xAABBCCDD
529 #define ENTRY2_EBX 0xEEFF0011
530 #define ENTRY2_ECX 0x22334455
531 #define ENTRY2_EDX 0x66778899
532 #define KILL_ADDRESS 0x3000
533
534 int g_kill_evt;
535 struct kvm_msr_entry g_msr2;
536
537 int setup_vm(struct crosvm *crosvm, void *mem) {
538 g_kill_evt = crosvm_get_shutdown_eventfd(crosvm);
539 crosvm_reserve_range(crosvm, CROSVM_ADDRESS_SPACE_MMIO, KILL_ADDRESS, 1);
540 return 0;
541 }
542
543 int handle_vpcu_init(struct crosvm_vcpu *vcpu, struct kvm_regs *regs,
544 struct kvm_sregs *sregs)
545 {
546 regs->rax = ENTRY1_INDEX;
547 regs->rcx = 0;
548 regs->rsp = 0x1000;
549 sregs->es.base = KILL_ADDRESS;
550
551 struct kvm_cpuid_entry2 entries[2];
552 entries[0].function = 0;
553 entries[0].index = ENTRY1_INDEX;
554 entries[0].flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
555 entries[0].eax = ENTRY1_EAX;
556 entries[0].ebx = ENTRY1_EBX;
557 entries[0].ecx = ENTRY1_ECX;
558 entries[0].edx = ENTRY1_EDX;
559 entries[1].function = 0;
560 entries[1].index = ENTRY2_INDEX;
561 entries[1].flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
562 entries[1].eax = ENTRY2_EAX;
563 entries[1].ebx = ENTRY2_EBX;
564 entries[1].ecx = ENTRY2_ECX;
565 entries[1].edx = ENTRY2_EDX;
566 return crosvm_vcpu_set_cpuid(vcpu, 2, entries);
567 }
568
569 int handle_vpcu_evt(struct crosvm_vcpu *vcpu, struct crosvm_vcpu_event evt) {
570 if (evt.kind == CROSVM_VCPU_EVENT_KIND_IO_ACCESS &&
571 evt.io_access.address_space == CROSVM_ADDRESS_SPACE_MMIO &&
572 evt.io_access.address == KILL_ADDRESS &&
573 evt.io_access.is_write &&
574 evt.io_access.length == 1 &&
575 evt.io_access.data[0] == 1)
576 {
577 uint64_t dummy = 1;
578 write(g_kill_evt, &dummy, sizeof(dummy));
579 return 1;
580 }
581 return 0;
582 }
583
584 int check_result(struct crosvm *vcpu, void *memory) {
585 uint32_t *mem = (uint32_t*)memory;
586 if (mem[0] != ENTRY1_EAX) {
587 fprintf(stderr, "entry 1 eax has unexpected value: 0x%x\n", mem[0]);
588 return 1;
589 }
590 if (mem[1] != ENTRY1_EBX) {
591 fprintf(stderr, "entry 1 ebx has unexpected value: 0x%x\n", mem[1]);
592 return 1;
593 }
594 if (mem[2] != ENTRY1_ECX) {
595 fprintf(stderr, "entry 1 ecx has unexpected value: 0x%x\n", mem[2]);
596 return 1;
597 }
598 if (mem[3] != ENTRY1_EDX) {
599 fprintf(stderr, "entry 1 edx has unexpected value: 0x%x\n", mem[3]);
600 return 1;
601 }
602 if (mem[4] != ENTRY2_EAX) {
603 fprintf(stderr, "entry 2 eax has unexpected value: 0x%x\n", mem[4]);
604 return 1;
605 }
606 if (mem[5] != ENTRY2_EBX) {
607 fprintf(stderr, "entry 2 ebx has unexpected value: 0x%x\n", mem[5]);
608 return 1;
609 }
610 if (mem[6] != ENTRY2_ECX) {
611 fprintf(stderr, "entry 2 ecx has unexpected value: 0x%x\n", mem[6]);
612 return 1;
613 }
614 if (mem[7] != ENTRY2_EDX) {
615 fprintf(stderr, "entry 2 edx has unexpected value: 0x%x\n", mem[7]);
616 return 1;
617 }
618 return 0;
619 }"#,
620 ..Default::default()
621 };
622 test_mini_plugin(&mini_plugin);
623 }
624
625 #[test]
test_vcpu_state_manipulation()626 fn test_vcpu_state_manipulation() {
627 let mini_plugin = MiniPlugin {
628 assembly_src: "org 0x1000
629 bits 16
630 mov byte [0x3000], 1",
631 src: r#"
632 #define KILL_ADDRESS 0x3000
633
634 int g_kill_evt;
635 bool success = false;
636
637 int setup_vm(struct crosvm *crosvm, void *mem) {
638 g_kill_evt = crosvm_get_shutdown_eventfd(crosvm);
639 crosvm_reserve_range(crosvm, CROSVM_ADDRESS_SPACE_MMIO, KILL_ADDRESS, 1);
640 return 0;
641 }
642
643 int handle_vpcu_init(struct crosvm_vcpu *vcpu, struct kvm_regs *regs,
644 struct kvm_sregs *sregs)
645 {
646 int ret;
647
648 struct kvm_lapic_state lapic;
649 ret = crosvm_vcpu_get_lapic_state(vcpu, &lapic);
650 if (ret < 0) {
651 fprintf(stderr, "failed to get initial LAPIC state: %d\n", ret);
652 return 1;
653 }
654
655 ret = crosvm_vcpu_set_lapic_state(vcpu, &lapic);
656 if (ret < 0) {
657 fprintf(stderr, "failed to update LAPIC state: %d\n", ret);
658 return 1;
659 }
660
661 ret = crosvm_vcpu_get_lapic_state(vcpu, &lapic);
662 if (ret < 0) {
663 fprintf(stderr, "failed to get updated LAPIC state: %d\n", ret);
664 return 1;
665 }
666
667 struct kvm_mp_state mp_state;
668 ret = crosvm_vcpu_get_mp_state(vcpu, &mp_state);
669 if (ret < 0) {
670 fprintf(stderr, "failed to get initial MP state: %d\n", ret);
671 return 1;
672 }
673
674 ret = crosvm_vcpu_set_mp_state(vcpu, &mp_state);
675 if (ret < 0) {
676 fprintf(stderr, "failed to update MP state: %d\n", ret);
677 return 1;
678 }
679
680 struct kvm_vcpu_events events;
681 ret = crosvm_vcpu_get_vcpu_events(vcpu, &events);
682 if (ret < 0) {
683 fprintf(stderr, "failed to get VCPU events: %d\n", ret);
684 return 1;
685 }
686
687 ret = crosvm_vcpu_set_vcpu_events(vcpu, &events);
688 if (ret < 0) {
689 fprintf(stderr, "failed to set VCPU events: %d\n", ret);
690 return 1;
691 }
692
693 success = true;
694 return 0;
695 }
696
697 int handle_vpcu_evt(struct crosvm_vcpu *vcpu, struct crosvm_vcpu_event evt) {
698 if (evt.kind == CROSVM_VCPU_EVENT_KIND_IO_ACCESS &&
699 evt.io_access.address_space == CROSVM_ADDRESS_SPACE_MMIO &&
700 evt.io_access.address == KILL_ADDRESS &&
701 evt.io_access.is_write &&
702 evt.io_access.length == 1 &&
703 evt.io_access.data[0] == 1)
704 {
705 uint64_t dummy = 1;
706 write(g_kill_evt, &dummy, sizeof(dummy));
707 return 1;
708 }
709 return 0;
710 }
711
712 int check_result(struct crosvm *vcpu, void *mem) {
713 if (!success) {
714 fprintf(stderr, "test failed\n");
715 return 1;
716 }
717 return 0;
718 }"#,
719 ..Default::default()
720 };
721 test_mini_plugin(&mini_plugin);
722 }
723