• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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