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