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