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