Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/e2e_tests/fixture/src/sys/windows.rs
5394 views
1
// Copyright 2022 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
// TODO(b/262270352): This file is build-only upstream as crosvm.exe cannot yet
6
// start a VM on windows. Enable e2e tests on windows and remove this comment.
7
8
use std::env;
9
use std::fs::OpenOptions;
10
use std::io::BufReader;
11
use std::path::Path;
12
use std::path::PathBuf;
13
use std::process::Child;
14
use std::process::Command;
15
use std::sync::Arc;
16
use std::sync::Mutex;
17
use std::time::Duration;
18
19
use anyhow::Result;
20
use base::named_pipes;
21
use base::PipeConnection;
22
use delegate::wire_format::DelegateMessage;
23
use serde_json::StreamDeserializer;
24
25
use crate::utils::find_crosvm_binary;
26
use crate::vm::local_path_from_url;
27
use crate::vm::Config;
28
29
const GUEST_EARLYCON: &str = "guest_earlycon.log";
30
const GUEST_CONSOLE: &str = "guest_latecon.log";
31
const HYPERVISOR_LOG: &str = "hypervisor.log";
32
// SLEEP_TIMEOUT is somewhat arbitrarily chosen by looking at a few downstream
33
// presubmit runs.
34
const SLEEP_TIMEOUT: Duration = Duration::from_millis(500);
35
// RETRY_COUNT is somewhat arbitrarily chosen by looking at a few downstream
36
// presubmit runs.
37
const RETRY_COUNT: u16 = 600;
38
39
pub struct SerialArgs {
40
// This pipe is used to communicate to/from guest.
41
from_guest_pipe: PathBuf,
42
logs_dir: PathBuf,
43
}
44
45
/// Returns the name of crosvm binary.
46
pub fn binary_name() -> &'static str {
47
"crosvm.exe"
48
}
49
50
// Generates random pipe name in device folder.
51
fn generate_pipe_name() -> String {
52
format!(r"\\.\pipe\test-ipc-pipe-name.rand{}", rand::random::<u64>())
53
}
54
55
// Gets custom hypervisor from `CROSVM_TEST_HYPERVISOR` environment variable or
56
// return `whpx` as default.
57
fn get_hypervisor() -> String {
58
env::var("CROSVM_TEST_HYPERVISOR").unwrap_or("whpx".to_string())
59
}
60
61
// If the hypervisor is haxm derivative, then returns `userspace` else returns
62
// None.
63
fn get_irqchip(hypervisor: &str) -> Option<String> {
64
if hypervisor == "haxm" || hypervisor == "ghaxm" {
65
Some("userspace".to_string())
66
} else {
67
None
68
}
69
}
70
71
// Ruturns hypervisor related args.
72
fn get_hypervisor_args() -> Vec<String> {
73
let hypervisor = get_hypervisor();
74
let mut args = if let Some(irqchip) = get_irqchip(&hypervisor) {
75
vec!["--irqchip".to_owned(), irqchip]
76
} else {
77
vec![]
78
};
79
args.extend_from_slice(&["--hypervisor".to_owned(), hypervisor]);
80
args
81
}
82
83
// Dumps logs found in `logs_dir` created by crosvm run.
84
fn dump_logs(logs_dir: &str) {
85
let dir = Path::new(logs_dir);
86
if dir.is_dir() {
87
for entry in std::fs::read_dir(dir).unwrap() {
88
let entry = entry.unwrap();
89
let path = entry.path();
90
if !path.is_dir() {
91
let data = std::fs::read_to_string(&path)
92
.unwrap_or_else(|e| panic!("Unable to read file {:?}: {:?}", &path, e));
93
eprintln!("---------- {:?}", &path);
94
eprintln!("{}", &data);
95
eprintln!("---------- {:?}", &path);
96
}
97
}
98
}
99
}
100
101
fn create_client_pipe_helper(from_guest_pipe: &str, logs_dir: &str) -> PipeConnection {
102
for _ in 0..RETRY_COUNT {
103
std::thread::sleep(SLEEP_TIMEOUT);
104
// Open pipes. Panic if we cannot connect after a timeout.
105
if let Ok(pipe) = named_pipes::create_client_pipe(
106
from_guest_pipe,
107
&named_pipes::FramingMode::Byte,
108
&named_pipes::BlockingMode::Wait,
109
false,
110
) {
111
return pipe;
112
}
113
}
114
115
dump_logs(logs_dir);
116
panic!("Failed to open pipe from guest");
117
}
118
119
pub struct TestVmSys {
120
pub(crate) from_guest_reader: Arc<
121
Mutex<
122
StreamDeserializer<
123
'static,
124
serde_json::de::IoRead<BufReader<PipeConnection>>,
125
DelegateMessage,
126
>,
127
>,
128
>,
129
pub(crate) to_guest: Arc<Mutex<PipeConnection>>,
130
pub(crate) process: Option<Child>, /* Use `Option` to allow taking the ownership in
131
* `Drop::drop()`. */
132
}
133
134
impl TestVmSys {
135
// Check if the test file system is a known compatible one.
136
pub fn check_rootfs_file(rootfs_path: &Path) {
137
// Check if the test file system is a known compatible one.
138
if let Err(e) = OpenOptions::new().write(false).read(true).open(rootfs_path) {
139
panic!("File open expected to work but did not: {e}");
140
}
141
}
142
143
// Adds 2 serial devices:
144
// - ttyS0: Console device which prints kernel log / debug output of the delegate binary.
145
// - ttyS1: Serial device attached to the named pipes.
146
fn configure_serial_devices(
147
command: &mut Command,
148
stdout_hardware_type: &str,
149
from_guest_pipe: &Path,
150
logs_dir: &Path,
151
) {
152
let earlycon_path = Path::new(logs_dir).join(GUEST_EARLYCON);
153
let earlycon_str = earlycon_path.to_str().unwrap();
154
155
command.args([
156
r"--serial",
157
&format!("hardware=serial,num=1,type=file,path={earlycon_str},earlycon=true"),
158
]);
159
160
let console_path = Path::new(logs_dir).join(GUEST_CONSOLE);
161
let console_str = console_path.to_str().unwrap();
162
command.args([
163
r"--serial",
164
&format!(
165
"hardware={stdout_hardware_type},num=1,type=file,path={console_str},console=true"
166
),
167
]);
168
169
// Setup channel for communication with the delegate.
170
let serial_params = format!(
171
"hardware=serial,type=namedpipe,path={},num=2",
172
from_guest_pipe.display(),
173
);
174
command.args(["--serial", &serial_params]);
175
}
176
177
/// Configures the VM rootfs to load from the guest_under_test assets.
178
fn configure_rootfs(command: &mut Command, _o_direct: bool, path: &Path) {
179
let rootfs_and_option = format!(
180
"{},ro,root,sparse=false",
181
path.as_os_str().to_str().unwrap(),
182
);
183
command.args(["--root", &rootfs_and_option]).args([
184
"--params",
185
"init=/bin/delegate noxsaves noxsave nopat nopti tsc=reliable",
186
]);
187
}
188
189
pub fn new_generic<F>(f: F, cfg: Config, _sudo: bool) -> Result<TestVmSys>
190
where
191
F: FnOnce(&mut Command, &SerialArgs, &Config) -> Result<()>,
192
{
193
let logs_dir = "emulator_logs";
194
let mut logs_path = PathBuf::new();
195
logs_path.push(logs_dir);
196
std::fs::create_dir_all(logs_dir)?;
197
// Create named pipe to communicate with the guest.
198
let from_guest_path = generate_pipe_name();
199
let from_guest_pipe = Path::new(&from_guest_path);
200
201
let mut command = Command::new(find_crosvm_binary());
202
command.args(["--log-level", "INFO", "run-mp"]);
203
204
f(
205
&mut command,
206
&SerialArgs {
207
from_guest_pipe: from_guest_pipe.to_path_buf(),
208
logs_dir: logs_path,
209
},
210
&cfg,
211
)?;
212
213
let hypervisor_log_path = Path::new(logs_dir).join(HYPERVISOR_LOG);
214
let hypervisor_log_str = hypervisor_log_path.to_str().unwrap();
215
command.args([
216
"--logs-directory",
217
logs_dir,
218
"--kernel-log-file",
219
hypervisor_log_str,
220
]);
221
command.args(get_hypervisor_args());
222
command.args(cfg.extra_args);
223
224
println!("Running command: {command:?}");
225
226
let process = Some(command.spawn().unwrap());
227
228
let to_guest = create_client_pipe_helper(&from_guest_path, logs_dir);
229
let from_guest_reader = BufReader::new(to_guest.try_clone().unwrap());
230
231
Ok(TestVmSys {
232
from_guest_reader: Arc::new(Mutex::new(
233
serde_json::Deserializer::from_reader(from_guest_reader).into_iter(),
234
)),
235
to_guest: Arc::new(Mutex::new(to_guest)),
236
process,
237
})
238
}
239
240
// Generates a config file from cfg and appends the command to use the config file.
241
pub fn append_config_args(
242
command: &mut Command,
243
serial_args: &SerialArgs,
244
cfg: &Config,
245
) -> Result<()> {
246
TestVmSys::configure_serial_devices(
247
command,
248
&cfg.console_hardware,
249
&serial_args.from_guest_pipe,
250
&serial_args.logs_dir,
251
);
252
if let Some(rootfs_url) = &cfg.rootfs_url {
253
TestVmSys::configure_rootfs(command, cfg.o_direct, &local_path_from_url(rootfs_url));
254
};
255
256
// Set initrd if being requested
257
if let Some(initrd_url) = &cfg.initrd_url {
258
command.arg("--initrd");
259
command.arg(local_path_from_url(initrd_url));
260
}
261
262
// Set kernel as the last argument.
263
command.arg(local_path_from_url(&cfg.kernel_url));
264
265
Ok(())
266
}
267
268
pub fn crosvm_command(
269
&mut self,
270
_command: &str,
271
mut _args: Vec<String>,
272
_sudo: bool,
273
) -> Result<Vec<u8>> {
274
unimplemented!()
275
}
276
}
277
278