Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/e2e_tests/tests/boot.rs
5394 views
1
// Copyright 2020 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
use std::time::Duration;
6
7
use fixture::vm::Config;
8
use fixture::vm::TestVm;
9
10
#[test]
11
fn boot_test_vm() -> anyhow::Result<()> {
12
let mut vm = TestVm::new(Config::new()).unwrap();
13
assert_eq!(vm.exec_in_guest("echo 42")?.stdout.trim(), "42");
14
Ok(())
15
}
16
17
#[test]
18
fn boot_custom_vm_kernel_initrd() -> anyhow::Result<()> {
19
let cfg = Config::new()
20
.with_kernel("https://storage.googleapis.com/crosvm/integration_tests/benchmarks/custom-guest-bzimage-x86_64-r0001")
21
.with_initrd("https://storage.googleapis.com/crosvm/integration_tests/benchmarks/custom-initramfs.cpio.gz-r0005")
22
// Use a non-sense file as rootfs to prove delegate correctly function in initrd
23
.with_rootfs("https://storage.googleapis.com/crosvm/integration_tests/guest-bzimage-aarch64-r0007")
24
.with_stdout_hardware("serial").extra_args(vec!["--mem".to_owned(), "512".to_owned()]);
25
let mut vm = TestVm::new(cfg).unwrap();
26
assert_eq!(
27
vm.exec_in_guest_async("echo 42")?
28
.with_timeout(Duration::from_secs(500))
29
.wait_ok(&mut vm)?
30
.stdout
31
.trim(),
32
"42"
33
);
34
Ok(())
35
}
36
37
#[test]
38
fn boot_test_vm_uring() -> anyhow::Result<()> {
39
let mut vm = TestVm::new(
40
Config::new().extra_args(vec!["--async-executor".to_string(), "uring".to_string()]),
41
)
42
.unwrap();
43
assert_eq!(vm.exec_in_guest("echo 42")?.stdout.trim(), "42");
44
Ok(())
45
}
46
47
#[cfg(any(target_os = "android", target_os = "linux"))]
48
#[test]
49
fn boot_test_vm_odirect() {
50
let mut vm = TestVm::new(Config::new().o_direct()).unwrap();
51
assert_eq!(vm.exec_in_guest("echo 42").unwrap().stdout.trim(), "42");
52
}
53
54
/*
55
* VCPU-level suspend/resume tests (which does NOT suspend the devices)
56
*/
57
58
#[cfg(any(target_os = "android", target_os = "linux"))]
59
#[test]
60
fn vcpu_suspend_resume_succeeds() {
61
// There is no easy way for us to check if the VM is actually suspended. But at
62
// least exercise the code-path.
63
let mut vm = TestVm::new(Config::new()).unwrap();
64
vm.suspend().unwrap();
65
vm.resume().unwrap();
66
assert_eq!(vm.exec_in_guest("echo 42").unwrap().stdout.trim(), "42");
67
}
68
69
#[cfg(any(target_os = "android", target_os = "linux"))]
70
#[test]
71
fn vcpu_suspend_resume_succeeds_with_pvclock() {
72
// There is no easy way for us to check if the VM is actually suspended. But at
73
// least exercise the code-path.
74
let mut config = Config::new();
75
config = config.extra_args(vec!["--pvclock".to_string()]);
76
let mut vm = TestVm::new(config).unwrap();
77
vm.suspend().unwrap();
78
vm.resume().unwrap();
79
assert_eq!(vm.exec_in_guest("echo 42").unwrap().stdout.trim(), "42");
80
}
81
82
/*
83
* Full suspend/resume tests (which suspend the devices and vcpus)
84
*/
85
86
#[cfg(any(target_os = "android", target_os = "linux"))]
87
#[test]
88
fn full_suspend_resume_test_suspend_resume_full() {
89
// There is no easy way for us to check if the VM is actually suspended. But at
90
// least exercise the code-path.
91
let mut config = Config::new();
92
// Why this test is called "full"? Can anyone explain...?
93
config = config.extra_args(vec![
94
"--no-usb".to_string(),
95
"--no-balloon".to_string(),
96
"--no-rng".to_string(),
97
]);
98
let mut vm = TestVm::new(config).unwrap();
99
vm.suspend_full().unwrap();
100
vm.resume_full().unwrap();
101
assert_eq!(vm.exec_in_guest("echo 42").unwrap().stdout.trim(), "42");
102
}
103
104
#[cfg(any(target_os = "android", target_os = "linux"))]
105
#[test]
106
fn full_suspend_resume_with_pvclock() {
107
// There is no easy way for us to check if the VM is actually suspended. But at
108
// least exercise the code-path.
109
let mut config = Config::new();
110
config = config.extra_args(vec![
111
"--no-usb".to_string(),
112
"--no-balloon".to_string(),
113
"--no-rng".to_string(),
114
"--pvclock".to_string(),
115
]);
116
let mut vm = TestVm::new(config).unwrap();
117
vm.suspend_full().unwrap();
118
vm.resume_full().unwrap();
119
assert_eq!(vm.exec_in_guest("echo 42").unwrap().stdout.trim(), "42");
120
}
121
122
#[cfg(any(target_os = "android", target_os = "linux"))]
123
#[test]
124
fn vcpu_suspend_resume_with_pvclock_adjusts_guest_clocks() {
125
use readclock::ClockValues;
126
127
// SUSPEND_DURATION defines how long the VM should be suspended
128
const SUSPEND_DURATION: Duration = Duration::from_secs(2);
129
const ALLOWANCE: Duration = Duration::from_secs(1);
130
131
// Launch a VM with pvclock option
132
let mut config = Config::new();
133
config = config.extra_args(vec![
134
"--no-usb".to_string(),
135
"--no-balloon".to_string(),
136
"--no-rng".to_string(),
137
"--pvclock".to_string(),
138
]);
139
let mut vm = TestVm::new(config).unwrap();
140
141
// Mount the proc fs
142
vm.exec_in_guest("mount proc /proc -t proc").unwrap();
143
// Ensure that the kernel has virtio-pvclock
144
assert_eq!(
145
vm.exec_in_guest("cat /proc/config.gz | gunzip | grep '^CONFIG_VIRTIO_PVCLOCK'")
146
.unwrap()
147
.stdout
148
.trim(),
149
"CONFIG_VIRTIO_PVCLOCK=y"
150
);
151
152
let guest_clocks_before = vm.guest_clock_values().unwrap();
153
let host_clocks_before = ClockValues::now();
154
vm.suspend().unwrap();
155
println!("Sleeping {SUSPEND_DURATION:?}...");
156
std::thread::sleep(SUSPEND_DURATION);
157
vm.resume().unwrap();
158
// Sleep a bit, to give the guest a chance to move the CLOCK_BOOTTIME value forward.
159
std::thread::sleep(SUSPEND_DURATION);
160
let guest_clocks_after = vm.guest_clock_values().unwrap();
161
let host_clocks_after = ClockValues::now();
162
// Calculating in f64 since the result may be negative
163
let guest_mono_diff = guest_clocks_after.clock_monotonic().as_secs_f64()
164
- guest_clocks_before.clock_monotonic().as_secs_f64();
165
let guest_boot_diff = guest_clocks_after.clock_boottime().as_secs_f64()
166
- guest_clocks_before.clock_boottime().as_secs_f64();
167
let host_boot_diff = host_clocks_after.clock_boottime().as_secs_f64()
168
- host_clocks_before.clock_boottime().as_secs_f64();
169
170
assert!(host_boot_diff > SUSPEND_DURATION.as_secs_f64());
171
// Although the BOOTTIME and MONOTONIC behavior varies in general for some real-world factors
172
// like the implementation of the kernel, the virtualization platforms and hardware issues,
173
// when virtio-pvclock is in use, crosvm does its best effort to maintain the following
174
// invariants to make the guest's userland peaceful:
175
176
// Invariants 1: Guest's MONOTONIC behaves as if they are stopped during the VM is suspended in
177
// terms of crosvm's VM instance running state. In other words, the guest's monotonic
178
// difference is smaller than the "real" time experienced by the host by SUSPEND_DURATION.
179
let monotonic_error = guest_mono_diff + SUSPEND_DURATION.as_secs_f64() - host_boot_diff;
180
assert!(monotonic_error < ALLOWANCE.as_secs_f64());
181
182
// Invariants 2: Subtracting Guest's MONOTONIC from the Guest's BOOTTIME should be
183
// equal to the total duration that the VM was in the "suspended" state as noted
184
// in the Invariants 1.
185
let guest_suspend_duration = guest_boot_diff - guest_mono_diff;
186
let boottime_error = (guest_suspend_duration - SUSPEND_DURATION.as_secs_f64()).abs();
187
assert!(boottime_error < ALLOWANCE.as_secs_f64());
188
}
189
190
#[cfg(any(target_os = "android", target_os = "linux"))]
191
#[test]
192
fn boot_test_vm_disable_sandbox() {
193
let mut vm = TestVm::new(Config::new().disable_sandbox()).unwrap();
194
assert_eq!(vm.exec_in_guest("echo 42").unwrap().stdout.trim(), "42");
195
}
196
197
#[cfg(any(target_os = "android", target_os = "linux"))]
198
#[test]
199
fn boot_test_vm_disable_sandbox_odirect() {
200
let mut vm = TestVm::new(Config::new().disable_sandbox().o_direct()).unwrap();
201
assert_eq!(vm.exec_in_guest("echo 42").unwrap().stdout.trim(), "42");
202
}
203
204
#[cfg(any(target_os = "android", target_os = "linux"))]
205
#[test]
206
fn boot_test_disable_sandbox_suspend_resume() {
207
// There is no easy way for us to check if the VM is actually suspended. But at
208
// least exercise the code-path.
209
let mut vm = TestVm::new(Config::new().disable_sandbox()).unwrap();
210
vm.suspend().unwrap();
211
vm.resume().unwrap();
212
assert_eq!(vm.exec_in_guest("echo 42").unwrap().stdout.trim(), "42");
213
}
214
215