• 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;
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