blob: 055d746ae84708d51e574186b5b57d3bb1436372 [file] [log] [blame]
Zach Reizner639d9672017-05-01 17:57:18 -07001// 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
Steven Richmanf32d0b42020-06-20 21:45:32 -07005//! Runs a virtual machine
Zach Reizner639d9672017-05-01 17:57:18 -07006
Zach Reiznerb3fa5c92019-01-28 14:05:23 -08007pub mod panic_hook;
Zach Reizner29ad3c72017-08-04 15:12:58 -07008
Daniel Verkampc677fb42020-09-08 13:47:49 -07009use std::collections::BTreeMap;
Judy Hsiaod5c1e962020-02-04 12:30:01 +080010use std::default::Default;
Jingkui Wang100e6e42019-03-08 20:41:57 -080011use std::fs::{File, OpenOptions};
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -080012use std::io::{BufRead, BufReader};
Jingkui Wang100e6e42019-03-08 20:41:57 -080013use std::path::{Path, PathBuf};
Jorge E. Moreira359e7de2020-12-02 18:25:53 -080014use std::str::FromStr;
Zach Reizner639d9672017-05-01 17:57:18 -070015use std::string::String;
Zach Reizner39aa26b2017-12-12 18:03:23 -080016use std::thread::sleep;
Stephen Barber56fbf092017-06-29 16:12:14 -070017use std::time::Duration;
Zach Reizner639d9672017-05-01 17:57:18 -070018
Daniel Verkampc677fb42020-09-08 13:47:49 -070019use arch::{
20 set_default_serial_parameters, Pstore, SerialHardware, SerialParameters, SerialType,
21 VcpuAffinity,
22};
Kevin Hamacher6fc5f202021-03-18 12:41:23 +010023use base::{debug, error, getpid, info, kill_process_group, reap_child, syslog, warn};
Tomasz Jeznach3ce74762021-02-26 01:01:53 -080024#[cfg(feature = "direct")]
25use crosvm::DirectIoOption;
Zach Reizner267f2c82019-07-31 17:07:27 -070026use crosvm::{
27 argument::{self, print_help, set_arguments, Argument},
Michael Hoylee47a5002020-10-15 16:24:13 -070028 platform, BindMount, Config, DiskOption, Executable, GidMap, SharedDir, TouchDeviceOption,
Chirantan Ekbote2ee9dcd2021-05-26 18:21:44 +090029 VhostUserFsOption, VhostUserOption, VhostUserWlOption, DISK_ID_LEN,
Zach Reizner267f2c82019-07-31 17:07:27 -070030};
Jason Macnakcc7070b2019-11-06 14:48:12 -080031#[cfg(feature = "gpu")]
Jason Macnakd659a0d2021-03-15 15:33:01 -070032use devices::virtio::gpu::{
33 GpuDisplayParameters, GpuMode, GpuParameters, DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH,
34};
Andrew Walbran413f8542021-01-08 13:29:03 +000035use devices::ProtectionType;
Andrew Scull1590e6f2020-03-18 18:00:47 +000036#[cfg(feature = "audio")]
Daniel Verkampfbd61222020-02-14 16:46:36 -080037use devices::{Ac97Backend, Ac97Parameters};
Daniel Verkampf2eecc42019-12-17 17:04:58 -080038use disk::QcowFile;
Jakub Starone7c59052019-04-09 12:31:14 -070039use vm_control::{
Kevin Hamacher6fc5f202021-03-18 12:41:23 +010040 client::{
41 do_modify_battery, do_usb_attach, do_usb_detach, do_usb_list, handle_request, vms_request,
42 ModifyUsbError, ModifyUsbResult,
43 },
44 BalloonControlCommand, BatteryType, DiskControlCommand, UsbControlResult, VmRequest,
Hikaru Nishida6b51c752021-05-21 12:37:43 +090045 VmResponse,
Jakub Starone7c59052019-04-09 12:31:14 -070046};
Zach Reizner639d9672017-05-01 17:57:18 -070047
Cody Schuffelen6d1ab502019-05-21 12:12:38 -070048fn executable_is_plugin(executable: &Option<Executable>) -> bool {
Daniel Verkampc26d20b2020-11-04 14:39:31 -080049 matches!(executable, Some(Executable::Plugin(_)))
Cody Schuffelen6d1ab502019-05-21 12:12:38 -070050}
51
Stephen Barbera00753b2017-07-18 13:57:26 -070052// Wait for all children to exit. Return true if they have all exited, false
53// otherwise.
54fn wait_all_children() -> bool {
Stephen Barber49dd2e22018-10-29 18:29:58 -070055 const CHILD_WAIT_MAX_ITER: isize = 100;
Stephen Barbera00753b2017-07-18 13:57:26 -070056 const CHILD_WAIT_MS: u64 = 10;
57 for _ in 0..CHILD_WAIT_MAX_ITER {
Stephen Barbera00753b2017-07-18 13:57:26 -070058 loop {
Zach Reizner56158c82017-08-24 13:50:14 -070059 match reap_child() {
60 Ok(0) => break,
61 // We expect ECHILD which indicates that there were no children left.
62 Err(e) if e.errno() == libc::ECHILD => return true,
63 Err(e) => {
David Tolnayb4bd00f2019-02-12 17:51:26 -080064 warn!("error while waiting for children: {}", e);
Zach Reizner56158c82017-08-24 13:50:14 -070065 return false;
Stephen Barbera00753b2017-07-18 13:57:26 -070066 }
Zach Reizner56158c82017-08-24 13:50:14 -070067 // We reaped one child, so continue reaping.
Zach Reizner55a9e502018-10-03 10:22:32 -070068 _ => {}
Stephen Barbera00753b2017-07-18 13:57:26 -070069 }
70 }
Zach Reizner56158c82017-08-24 13:50:14 -070071 // There's no timeout option for waitpid which reap_child calls internally, so our only
72 // recourse is to sleep while waiting for the children to exit.
Stephen Barbera00753b2017-07-18 13:57:26 -070073 sleep(Duration::from_millis(CHILD_WAIT_MS));
74 }
75
76 // If we've made it to this point, not all of the children have exited.
David Tolnay5bbbf612018-12-01 17:49:30 -080077 false
Stephen Barbera00753b2017-07-18 13:57:26 -070078}
79
Daniel Verkamp107edb32019-04-05 09:58:48 -070080/// Parse a comma-separated list of CPU numbers and ranges and convert it to a Vec of CPU numbers.
81fn parse_cpu_set(s: &str) -> argument::Result<Vec<usize>> {
82 let mut cpuset = Vec::new();
83 for part in s.split(',') {
84 let range: Vec<&str> = part.split('-').collect();
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +090085 if range.is_empty() || range.len() > 2 {
Daniel Verkamp107edb32019-04-05 09:58:48 -070086 return Err(argument::Error::InvalidValue {
87 value: part.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +080088 expected: String::from("invalid list syntax"),
Daniel Verkamp107edb32019-04-05 09:58:48 -070089 });
90 }
91 let first_cpu: usize = range[0]
92 .parse()
93 .map_err(|_| argument::Error::InvalidValue {
94 value: part.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +080095 expected: String::from("CPU index must be a non-negative integer"),
Daniel Verkamp107edb32019-04-05 09:58:48 -070096 })?;
97 let last_cpu: usize = if range.len() == 2 {
98 range[1]
99 .parse()
100 .map_err(|_| argument::Error::InvalidValue {
101 value: part.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800102 expected: String::from("CPU index must be a non-negative integer"),
Daniel Verkamp107edb32019-04-05 09:58:48 -0700103 })?
104 } else {
105 first_cpu
106 };
107
108 if last_cpu < first_cpu {
109 return Err(argument::Error::InvalidValue {
110 value: part.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800111 expected: String::from("CPU ranges must be from low to high"),
Daniel Verkamp107edb32019-04-05 09:58:48 -0700112 });
113 }
114
115 for cpu in first_cpu..=last_cpu {
116 cpuset.push(cpu);
117 }
118 }
119 Ok(cpuset)
120}
121
Daniel Verkampc677fb42020-09-08 13:47:49 -0700122/// Parse a list of guest to host CPU mappings.
123///
124/// Each mapping consists of a single guest CPU index mapped to one or more host CPUs in the form
125/// accepted by `parse_cpu_set`:
126///
127/// `<GUEST-CPU>=<HOST-CPU-SET>[:<GUEST-CPU>=<HOST-CPU-SET>[:...]]`
128fn parse_cpu_affinity(s: &str) -> argument::Result<VcpuAffinity> {
129 if s.contains('=') {
130 let mut affinity_map = BTreeMap::new();
131 for cpu_pair in s.split(':') {
132 let assignment: Vec<&str> = cpu_pair.split('=').collect();
133 if assignment.len() != 2 {
134 return Err(argument::Error::InvalidValue {
135 value: cpu_pair.to_owned(),
136 expected: String::from("invalid VCPU assignment syntax"),
137 });
138 }
139 let guest_cpu = assignment[0]
140 .parse()
141 .map_err(|_| argument::Error::InvalidValue {
142 value: assignment[0].to_owned(),
143 expected: String::from("CPU index must be a non-negative integer"),
144 })?;
145 let host_cpu_set = parse_cpu_set(assignment[1])?;
146 if affinity_map.insert(guest_cpu, host_cpu_set).is_some() {
147 return Err(argument::Error::InvalidValue {
148 value: cpu_pair.to_owned(),
149 expected: String::from("VCPU index must be unique"),
150 });
151 }
152 }
153 Ok(VcpuAffinity::PerVcpu(affinity_map))
154 } else {
155 Ok(VcpuAffinity::Global(parse_cpu_set(s)?))
156 }
157}
158
Daniel Verkamp8a72afc2021-03-15 17:55:52 -0700159fn parse_cpu_capacity(s: &str, cpu_capacity: &mut BTreeMap<usize, u32>) -> argument::Result<()> {
160 for cpu_pair in s.split(',') {
161 let assignment: Vec<&str> = cpu_pair.split('=').collect();
162 if assignment.len() != 2 {
163 return Err(argument::Error::InvalidValue {
164 value: cpu_pair.to_owned(),
165 expected: String::from("invalid CPU capacity syntax"),
166 });
167 }
168 let cpu = assignment[0]
169 .parse()
170 .map_err(|_| argument::Error::InvalidValue {
171 value: assignment[0].to_owned(),
172 expected: String::from("CPU index must be a non-negative integer"),
173 })?;
174 let capacity = assignment[1]
175 .parse()
176 .map_err(|_| argument::Error::InvalidValue {
177 value: assignment[1].to_owned(),
178 expected: String::from("CPU capacity must be a non-negative integer"),
179 })?;
180 if cpu_capacity.insert(cpu, capacity).is_some() {
181 return Err(argument::Error::InvalidValue {
182 value: cpu_pair.to_owned(),
183 expected: String::from("CPU index must be unique"),
184 });
185 }
186 }
187 Ok(())
188}
189
Jason Macnakcc7070b2019-11-06 14:48:12 -0800190#[cfg(feature = "gpu")]
Jason Macnakd659a0d2021-03-15 15:33:01 -0700191fn parse_gpu_options(s: Option<&str>, gpu_params: &mut GpuParameters) -> argument::Result<()> {
Kaiyi Lidd348a42020-07-13 11:49:46 -0700192 #[cfg(feature = "gfxstream")]
193 let mut vulkan_specified = false;
194 #[cfg(feature = "gfxstream")]
195 let mut syncfd_specified = false;
Jason Macnak046ed142020-10-08 09:11:53 -0700196 #[cfg(feature = "gfxstream")]
197 let mut angle_specified = false;
Jason Macnakcc7070b2019-11-06 14:48:12 -0800198
Jason Macnakd659a0d2021-03-15 15:33:01 -0700199 let mut display_w: Option<u32> = None;
200 let mut display_h: Option<u32> = None;
201
Jason Macnakcc7070b2019-11-06 14:48:12 -0800202 if let Some(s) = s {
203 let opts = s
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +0900204 .split(',')
205 .map(|frag| frag.split('='))
Jason Macnakcc7070b2019-11-06 14:48:12 -0800206 .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
207
208 for (k, v) in opts {
209 match k {
Lingfeng Yangddbe8b72020-01-30 10:00:36 -0800210 // Deprecated: Specifying --gpu=<mode> Not great as the mode can be set multiple
Chia-I Wue25859b2021-04-14 13:57:55 -0700211 // times if the user specifies several modes (--gpu=2d,virglrenderer,gfxstream)
Jason Macnak327fc242020-01-10 12:45:36 -0800212 "2d" | "2D" => {
213 gpu_params.mode = GpuMode::Mode2D;
214 }
Chia-I Wue25859b2021-04-14 13:57:55 -0700215 "3d" | "3D" | "virglrenderer" => {
216 gpu_params.mode = GpuMode::ModeVirglRenderer;
Jason Macnak327fc242020-01-10 12:45:36 -0800217 }
Lingfeng Yangddbe8b72020-01-30 10:00:36 -0800218 #[cfg(feature = "gfxstream")]
219 "gfxstream" => {
Gurchetan Singhb1394f72020-10-19 18:31:13 -0700220 gpu_params.mode = GpuMode::ModeGfxstream;
Lingfeng Yangddbe8b72020-01-30 10:00:36 -0800221 }
222 // Preferred: Specifying --gpu,backend=<mode>
223 "backend" => match v {
224 "2d" | "2D" => {
225 gpu_params.mode = GpuMode::Mode2D;
226 }
Chia-I Wue25859b2021-04-14 13:57:55 -0700227 "3d" | "3D" | "virglrenderer" => {
228 gpu_params.mode = GpuMode::ModeVirglRenderer;
Lingfeng Yangddbe8b72020-01-30 10:00:36 -0800229 }
230 #[cfg(feature = "gfxstream")]
231 "gfxstream" => {
Gurchetan Singhb1394f72020-10-19 18:31:13 -0700232 gpu_params.mode = GpuMode::ModeGfxstream;
Lingfeng Yangddbe8b72020-01-30 10:00:36 -0800233 }
234 _ => {
235 return Err(argument::Error::InvalidValue {
236 value: v.to_string(),
Judy Hsiao59343052020-03-16 15:58:03 +0800237 expected: String::from(
Chia-I Wue25859b2021-04-14 13:57:55 -0700238 "gpu parameter 'backend' should be one of (2d|virglrenderer|gfxstream)",
Judy Hsiao59343052020-03-16 15:58:03 +0800239 ),
Lingfeng Yangddbe8b72020-01-30 10:00:36 -0800240 });
241 }
242 },
Jason Macnakbf195582019-11-20 16:25:49 -0800243 "egl" => match v {
244 "true" | "" => {
245 gpu_params.renderer_use_egl = true;
246 }
247 "false" => {
248 gpu_params.renderer_use_egl = false;
249 }
250 _ => {
251 return Err(argument::Error::InvalidValue {
252 value: v.to_string(),
Judy Hsiao59343052020-03-16 15:58:03 +0800253 expected: String::from("gpu parameter 'egl' should be a boolean"),
Jason Macnakbf195582019-11-20 16:25:49 -0800254 });
255 }
256 },
257 "gles" => match v {
258 "true" | "" => {
259 gpu_params.renderer_use_gles = true;
260 }
261 "false" => {
262 gpu_params.renderer_use_gles = false;
263 }
264 _ => {
265 return Err(argument::Error::InvalidValue {
266 value: v.to_string(),
Judy Hsiao59343052020-03-16 15:58:03 +0800267 expected: String::from("gpu parameter 'gles' should be a boolean"),
Jason Macnakbf195582019-11-20 16:25:49 -0800268 });
269 }
270 },
271 "glx" => match v {
272 "true" | "" => {
273 gpu_params.renderer_use_glx = true;
274 }
275 "false" => {
276 gpu_params.renderer_use_glx = false;
277 }
278 _ => {
279 return Err(argument::Error::InvalidValue {
280 value: v.to_string(),
Judy Hsiao59343052020-03-16 15:58:03 +0800281 expected: String::from("gpu parameter 'glx' should be a boolean"),
Jason Macnakbf195582019-11-20 16:25:49 -0800282 });
283 }
284 },
285 "surfaceless" => match v {
286 "true" | "" => {
287 gpu_params.renderer_use_surfaceless = true;
288 }
289 "false" => {
290 gpu_params.renderer_use_surfaceless = false;
291 }
292 _ => {
293 return Err(argument::Error::InvalidValue {
294 value: v.to_string(),
Judy Hsiao59343052020-03-16 15:58:03 +0800295 expected: String::from(
296 "gpu parameter 'surfaceless' should be a boolean",
297 ),
Jason Macnakbf195582019-11-20 16:25:49 -0800298 });
299 }
300 },
Kaiyi Li6404e452020-07-07 19:12:03 -0700301 #[cfg(feature = "gfxstream")]
Kaiyi Lidd348a42020-07-13 11:49:46 -0700302 "syncfd" => {
303 syncfd_specified = true;
304 match v {
305 "true" | "" => {
306 gpu_params.gfxstream_use_syncfd = true;
307 }
308 "false" => {
309 gpu_params.gfxstream_use_syncfd = false;
310 }
311 _ => {
312 return Err(argument::Error::InvalidValue {
313 value: v.to_string(),
314 expected: String::from(
315 "gpu parameter 'syncfd' should be a boolean",
316 ),
317 });
318 }
Kaiyi Li6404e452020-07-07 19:12:03 -0700319 }
Kaiyi Lidd348a42020-07-13 11:49:46 -0700320 }
Kaiyi Li6c52a2e2020-07-07 19:28:32 -0700321 #[cfg(feature = "gfxstream")]
Jason Macnak046ed142020-10-08 09:11:53 -0700322 "angle" => {
323 angle_specified = true;
324 match v {
325 "true" | "" => {
326 gpu_params.gfxstream_use_guest_angle = true;
327 }
328 "false" => {
329 gpu_params.gfxstream_use_guest_angle = false;
330 }
331 _ => {
332 return Err(argument::Error::InvalidValue {
333 value: v.to_string(),
334 expected: String::from("gpu parameter 'angle' should be a boolean"),
335 });
336 }
337 }
338 }
Kaiyi Lidd348a42020-07-13 11:49:46 -0700339 "vulkan" => {
Chia-I Wu6d473b32021-04-12 10:14:24 -0700340 #[cfg(feature = "gfxstream")]
341 {
342 vulkan_specified = true;
343 }
Kaiyi Lidd348a42020-07-13 11:49:46 -0700344 match v {
345 "true" | "" => {
Chia-I Wu0beb2462021-04-12 09:36:08 -0700346 gpu_params.use_vulkan = true;
Kaiyi Lidd348a42020-07-13 11:49:46 -0700347 }
348 "false" => {
Chia-I Wu0beb2462021-04-12 09:36:08 -0700349 gpu_params.use_vulkan = false;
Kaiyi Lidd348a42020-07-13 11:49:46 -0700350 }
351 _ => {
352 return Err(argument::Error::InvalidValue {
353 value: v.to_string(),
354 expected: String::from(
355 "gpu parameter 'vulkan' should be a boolean",
356 ),
357 });
358 }
Kaiyi Li6c52a2e2020-07-07 19:28:32 -0700359 }
Kaiyi Lidd348a42020-07-13 11:49:46 -0700360 }
Jason Macnakcc7070b2019-11-06 14:48:12 -0800361 "width" => {
Jason Macnakd659a0d2021-03-15 15:33:01 -0700362 let width = v
363 .parse::<u32>()
364 .map_err(|_| argument::Error::InvalidValue {
365 value: v.to_string(),
366 expected: String::from("gpu parameter 'width' must be a valid integer"),
367 })?;
368 display_w = Some(width);
Jason Macnakcc7070b2019-11-06 14:48:12 -0800369 }
370 "height" => {
Jason Macnakd659a0d2021-03-15 15:33:01 -0700371 let height = v
372 .parse::<u32>()
373 .map_err(|_| argument::Error::InvalidValue {
374 value: v.to_string(),
375 expected: String::from(
376 "gpu parameter 'height' must be a valid integer",
377 ),
378 })?;
379 display_h = Some(height);
Jason Macnakcc7070b2019-11-06 14:48:12 -0800380 }
John Batesb220eac2020-09-14 17:03:02 -0700381 "cache-path" => gpu_params.cache_path = Some(v.to_string()),
382 "cache-size" => gpu_params.cache_size = Some(v.to_string()),
Gurchetan Singh401340e2021-03-23 09:34:00 -0700383 "udmabuf" => match v {
384 "true" | "" => {
385 gpu_params.udmabuf = true;
386 }
387 "false" => {
388 gpu_params.udmabuf = false;
389 }
390 _ => {
391 return Err(argument::Error::InvalidValue {
392 value: v.to_string(),
393 expected: String::from("gpu parameter 'udmabuf' should be a boolean"),
394 });
395 }
396 },
Jason Macnakcc7070b2019-11-06 14:48:12 -0800397 "" => {}
398 _ => {
399 return Err(argument::Error::UnknownArgument(format!(
400 "gpu parameter {}",
401 k
402 )));
403 }
404 }
405 }
406 }
407
Jason Macnakd659a0d2021-03-15 15:33:01 -0700408 if display_w.is_some() || display_h.is_some() {
409 if display_w.is_none() || display_h.is_none() {
410 return Err(argument::Error::InvalidValue {
411 value: s.unwrap_or("").to_string(),
412 expected: String::from(
413 "gpu must include both 'width' and 'height' if either is supplied",
414 ),
415 });
416 }
417
418 gpu_params.displays.push(GpuDisplayParameters {
419 width: display_w.unwrap(),
420 height: display_h.unwrap(),
421 });
422 }
423
Kaiyi Lidd348a42020-07-13 11:49:46 -0700424 #[cfg(feature = "gfxstream")]
425 {
Chia-I Wu91df6562021-04-12 09:47:38 -0700426 if !vulkan_specified && gpu_params.mode == GpuMode::ModeGfxstream {
427 gpu_params.use_vulkan = true;
428 }
429
Chia-I Wu6d473b32021-04-12 10:14:24 -0700430 if syncfd_specified || angle_specified {
Kaiyi Lidd348a42020-07-13 11:49:46 -0700431 match gpu_params.mode {
Gurchetan Singhb1394f72020-10-19 18:31:13 -0700432 GpuMode::ModeGfxstream => {}
Kaiyi Lidd348a42020-07-13 11:49:46 -0700433 _ => {
434 return Err(argument::Error::UnknownArgument(
Chia-I Wu6d473b32021-04-12 10:14:24 -0700435 "gpu parameter syncfd and angle are only supported for gfxstream backend"
Kaiyi Lidd348a42020-07-13 11:49:46 -0700436 .to_string(),
437 ));
438 }
439 }
440 }
441 }
442
Jason Macnakd659a0d2021-03-15 15:33:01 -0700443 Ok(())
444}
445
446#[cfg(feature = "gpu")]
447fn parse_gpu_display_options(
448 s: Option<&str>,
449 gpu_params: &mut GpuParameters,
450) -> argument::Result<()> {
451 let mut display_w: Option<u32> = None;
452 let mut display_h: Option<u32> = None;
453
454 if let Some(s) = s {
455 let opts = s
456 .split(',')
457 .map(|frag| frag.split('='))
458 .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
459
460 for (k, v) in opts {
461 match k {
462 "width" => {
463 let width = v
464 .parse::<u32>()
465 .map_err(|_| argument::Error::InvalidValue {
466 value: v.to_string(),
467 expected: String::from("gpu parameter 'width' must be a valid integer"),
468 })?;
469 display_w = Some(width);
470 }
471 "height" => {
472 let height = v
473 .parse::<u32>()
474 .map_err(|_| argument::Error::InvalidValue {
475 value: v.to_string(),
476 expected: String::from(
477 "gpu parameter 'height' must be a valid integer",
478 ),
479 })?;
480 display_h = Some(height);
481 }
482 "" => {}
483 _ => {
484 return Err(argument::Error::UnknownArgument(format!(
485 "gpu-display parameter {}",
486 k
487 )));
488 }
489 }
490 }
491 }
492
493 if display_w.is_none() || display_h.is_none() {
494 return Err(argument::Error::InvalidValue {
495 value: s.unwrap_or("").to_string(),
496 expected: String::from("gpu-display must include both 'width' and 'height'"),
497 });
498 }
499
500 gpu_params.displays.push(GpuDisplayParameters {
501 width: display_w.unwrap(),
502 height: display_h.unwrap(),
503 });
504
505 Ok(())
Jason Macnakcc7070b2019-11-06 14:48:12 -0800506}
507
Andrew Scull1590e6f2020-03-18 18:00:47 +0000508#[cfg(feature = "audio")]
Judy Hsiaod5c1e962020-02-04 12:30:01 +0800509fn parse_ac97_options(s: &str) -> argument::Result<Ac97Parameters> {
510 let mut ac97_params: Ac97Parameters = Default::default();
511
512 let opts = s
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +0900513 .split(',')
514 .map(|frag| frag.split('='))
Judy Hsiaod5c1e962020-02-04 12:30:01 +0800515 .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
516
517 for (k, v) in opts {
518 match k {
519 "backend" => {
520 ac97_params.backend =
521 v.parse::<Ac97Backend>()
522 .map_err(|e| argument::Error::InvalidValue {
523 value: v.to_string(),
524 expected: e.to_string(),
525 })?;
526 }
527 "capture" => {
528 ac97_params.capture = v.parse::<bool>().map_err(|e| {
529 argument::Error::Syntax(format!("invalid capture option: {}", e))
530 })?;
531 }
paulhsia83d51602021-03-09 17:13:14 +0800532 "client_type" => {
533 ac97_params
534 .set_client_type(v)
535 .map_err(|e| argument::Error::InvalidValue {
536 value: v.to_string(),
537 expected: e.to_string(),
538 })?;
539 }
Jorge E. Moreira6a88a5d2021-03-12 15:34:46 -0800540 #[cfg(any(target_os = "linux", target_os = "android"))]
Jorge E. Moreira359e7de2020-12-02 18:25:53 -0800541 "server" => {
542 ac97_params.vios_server_path =
543 Some(
544 PathBuf::from_str(v).map_err(|e| argument::Error::InvalidValue {
545 value: v.to_string(),
546 expected: e.to_string(),
547 })?,
548 );
549 }
Judy Hsiaod5c1e962020-02-04 12:30:01 +0800550 _ => {
551 return Err(argument::Error::UnknownArgument(format!(
552 "unknown ac97 parameter {}",
553 k
554 )));
555 }
556 }
557 }
558
Jorge E. Moreira359e7de2020-12-02 18:25:53 -0800559 // server is required for and exclusive to vios backend
Jorge E. Moreira6a88a5d2021-03-12 15:34:46 -0800560 #[cfg(any(target_os = "linux", target_os = "android"))]
Jorge E. Moreira359e7de2020-12-02 18:25:53 -0800561 match ac97_params.backend {
562 Ac97Backend::VIOS => {
563 if ac97_params.vios_server_path.is_none() {
564 return Err(argument::Error::ExpectedArgument(String::from(
565 "server argument is required for VIOS backend",
566 )));
567 }
568 }
569 _ => {
570 if ac97_params.vios_server_path.is_some() {
571 return Err(argument::Error::UnexpectedValue(String::from(
572 "server argument is exclusive to the VIOS backend",
573 )));
574 }
575 }
576 }
577
Judy Hsiaod5c1e962020-02-04 12:30:01 +0800578 Ok(ac97_params)
579}
580
Trent Begin17ccaad2019-04-17 13:51:25 -0600581fn parse_serial_options(s: &str) -> argument::Result<SerialParameters> {
582 let mut serial_setting = SerialParameters {
583 type_: SerialType::Sink,
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -0700584 hardware: SerialHardware::Serial,
Trent Begin17ccaad2019-04-17 13:51:25 -0600585 path: None,
Iliyan Malchev2c1417b2020-04-14 09:40:41 -0700586 input: None,
Trent Begin923bab02019-06-17 13:48:06 -0600587 num: 1,
Trent Begin17ccaad2019-04-17 13:51:25 -0600588 console: false,
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -0700589 earlycon: false,
Jorge E. Moreira1e262302019-08-01 14:40:03 -0700590 stdin: false,
Trent Begin17ccaad2019-04-17 13:51:25 -0600591 };
592
593 let opts = s
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +0900594 .split(',')
Nicholas Hollingumc76c2da2020-09-18 15:53:16 +1000595 .map(|frag| frag.splitn(2, '='))
Trent Begin17ccaad2019-04-17 13:51:25 -0600596 .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
597
598 for (k, v) in opts {
599 match k {
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -0700600 "hardware" => {
601 serial_setting.hardware = v
602 .parse::<SerialHardware>()
603 .map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?
604 }
Trent Begin17ccaad2019-04-17 13:51:25 -0600605 "type" => {
606 serial_setting.type_ = v
607 .parse::<SerialType>()
608 .map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?
609 }
610 "num" => {
611 let num = v.parse::<u8>().map_err(|e| {
612 argument::Error::Syntax(format!("serial device number is not parsable: {}", e))
613 })?;
A. Cody Schuffelen3faab5a2020-10-05 17:29:16 -0700614 if num < 1 {
Trent Begin17ccaad2019-04-17 13:51:25 -0600615 return Err(argument::Error::InvalidValue {
616 value: num.to_string(),
A. Cody Schuffelen3faab5a2020-10-05 17:29:16 -0700617 expected: String::from("Serial port num must be at least 1"),
Trent Begin17ccaad2019-04-17 13:51:25 -0600618 });
619 }
620 serial_setting.num = num;
621 }
622 "console" => {
623 serial_setting.console = v.parse::<bool>().map_err(|e| {
624 argument::Error::Syntax(format!(
625 "serial device console is not parseable: {}",
626 e
627 ))
628 })?
629 }
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -0700630 "earlycon" => {
631 serial_setting.earlycon = v.parse::<bool>().map_err(|e| {
632 argument::Error::Syntax(format!(
633 "serial device earlycon is not parseable: {}",
634 e,
635 ))
636 })?
637 }
Jorge E. Moreira1e262302019-08-01 14:40:03 -0700638 "stdin" => {
639 serial_setting.stdin = v.parse::<bool>().map_err(|e| {
640 argument::Error::Syntax(format!("serial device stdin is not parseable: {}", e))
Iliyan Malchev2c1417b2020-04-14 09:40:41 -0700641 })?;
642 if serial_setting.stdin && serial_setting.input.is_some() {
643 return Err(argument::Error::TooManyArguments(
644 "Cannot specify both stdin and input options".to_string(),
645 ));
646 }
Jorge E. Moreira1e262302019-08-01 14:40:03 -0700647 }
Jorge E. Moreira9c9e0e72019-05-17 13:57:04 -0700648 "path" => serial_setting.path = Some(PathBuf::from(v)),
Iliyan Malchev2c1417b2020-04-14 09:40:41 -0700649 "input" => {
650 if serial_setting.stdin {
651 return Err(argument::Error::TooManyArguments(
652 "Cannot specify both stdin and input options".to_string(),
653 ));
654 }
655 serial_setting.input = Some(PathBuf::from(v));
656 }
Trent Begin17ccaad2019-04-17 13:51:25 -0600657 _ => {
658 return Err(argument::Error::UnknownArgument(format!(
659 "serial parameter {}",
660 k
661 )));
662 }
663 }
664 }
665
A. Cody Schuffelen3faab5a2020-10-05 17:29:16 -0700666 if serial_setting.hardware == SerialHardware::Serial && serial_setting.num > 4 {
667 return Err(argument::Error::InvalidValue {
668 value: serial_setting.num.to_string(),
669 expected: String::from("Serial port num must be 4 or less"),
670 });
671 }
672
Trent Begin17ccaad2019-04-17 13:51:25 -0600673 Ok(serial_setting)
674}
675
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800676fn parse_plugin_mount_option(value: &str) -> argument::Result<BindMount> {
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +0900677 let components: Vec<&str> = value.split(':').collect();
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800678 if components.is_empty() || components.len() > 3 || components[0].is_empty() {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800679 return Err(argument::Error::InvalidValue {
680 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800681 expected: String::from(
682 "`plugin-mount` should be in a form of: <src>[:[<dst>][:<writable>]]",
683 ),
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800684 });
685 }
686
687 let src = PathBuf::from(components[0]);
688 if src.is_relative() {
689 return Err(argument::Error::InvalidValue {
690 value: components[0].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800691 expected: String::from("the source path for `plugin-mount` must be absolute"),
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800692 });
693 }
694 if !src.exists() {
695 return Err(argument::Error::InvalidValue {
696 value: components[0].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800697 expected: String::from("the source path for `plugin-mount` does not exist"),
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800698 });
699 }
700
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800701 let dst = PathBuf::from(match components.get(1) {
702 None | Some(&"") => components[0],
703 Some(path) => path,
704 });
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800705 if dst.is_relative() {
706 return Err(argument::Error::InvalidValue {
707 value: components[1].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800708 expected: String::from("the destination path for `plugin-mount` must be absolute"),
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800709 });
710 }
711
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800712 let writable: bool = match components.get(2) {
713 None => false,
714 Some(s) => s.parse().map_err(|_| argument::Error::InvalidValue {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800715 value: components[2].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800716 expected: String::from("the <writable> component for `plugin-mount` is not valid bool"),
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800717 })?,
718 };
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800719
720 Ok(BindMount { src, dst, writable })
721}
722
723fn parse_plugin_gid_map_option(value: &str) -> argument::Result<GidMap> {
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +0900724 let components: Vec<&str> = value.split(':').collect();
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800725 if components.is_empty() || components.len() > 3 || components[0].is_empty() {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800726 return Err(argument::Error::InvalidValue {
727 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800728 expected: String::from(
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800729 "`plugin-gid-map` must have exactly 3 components: <inner>[:[<outer>][:<count>]]",
Judy Hsiao59343052020-03-16 15:58:03 +0800730 ),
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800731 });
732 }
733
734 let inner: libc::gid_t = components[0]
735 .parse()
736 .map_err(|_| argument::Error::InvalidValue {
737 value: components[0].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800738 expected: String::from("the <inner> component for `plugin-gid-map` is not valid gid"),
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800739 })?;
740
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800741 let outer: libc::gid_t = match components.get(1) {
742 None | Some(&"") => inner,
743 Some(s) => s.parse().map_err(|_| argument::Error::InvalidValue {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800744 value: components[1].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800745 expected: String::from("the <outer> component for `plugin-gid-map` is not valid gid"),
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800746 })?,
747 };
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800748
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800749 let count: u32 = match components.get(2) {
750 None => 1,
751 Some(s) => s.parse().map_err(|_| argument::Error::InvalidValue {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800752 value: components[2].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800753 expected: String::from(
754 "the <count> component for `plugin-gid-map` is not valid number",
755 ),
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800756 })?,
757 };
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800758
759 Ok(GidMap {
760 inner,
761 outer,
762 count,
763 })
764}
765
Chuanxiao Dongfd5626c2020-04-27 16:35:33 +0800766fn parse_battery_options(s: Option<&str>) -> argument::Result<BatteryType> {
767 let mut battery_type: BatteryType = Default::default();
768
769 if let Some(s) = s {
770 let opts = s
Andrew Walbran9cfdbd92021-01-11 17:40:34 +0000771 .split(',')
772 .map(|frag| frag.split('='))
Chuanxiao Dongfd5626c2020-04-27 16:35:33 +0800773 .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
774
775 for (k, v) in opts {
776 match k {
777 "type" => match v.parse::<BatteryType>() {
778 Ok(type_) => battery_type = type_,
779 Err(e) => {
780 return Err(argument::Error::InvalidValue {
781 value: v.to_string(),
782 expected: e.to_string(),
783 });
784 }
785 },
786 "" => {}
787 _ => {
788 return Err(argument::Error::UnknownArgument(format!(
789 "battery parameter {}",
790 k
791 )));
792 }
793 }
794 }
795 }
796
797 Ok(battery_type)
798}
799
Tomasz Jeznach3ce74762021-02-26 01:01:53 -0800800#[cfg(feature = "direct")]
801fn parse_direct_io_options(s: Option<&str>) -> argument::Result<DirectIoOption> {
802 let s = s.ok_or(argument::Error::ExpectedValue(String::from(
803 "expected path@range[,range] value",
804 )))?;
805 let parts: Vec<&str> = s.splitn(2, '@').collect();
806 if parts.len() != 2 {
807 return Err(argument::Error::InvalidValue {
808 value: s.to_string(),
809 expected: String::from("missing port range, use /path@X-Y,Z,.. syntax"),
810 });
811 }
812 let path = PathBuf::from(parts[0]);
813 if !path.exists() {
814 return Err(argument::Error::InvalidValue {
815 value: parts[0].to_owned(),
816 expected: String::from("the path does not exist"),
817 });
818 };
819 let ranges: argument::Result<Vec<(u64, u64)>> = parts[1]
820 .split(',')
821 .map(|frag| frag.split('-'))
822 .map(|mut range| {
823 let base = range
824 .next()
825 .map(|v| v.parse::<u64>())
826 .map_or(Ok(None), |r| r.map(Some));
827 let last = range
828 .next()
829 .map(|v| v.parse::<u64>())
830 .map_or(Ok(None), |r| r.map(Some));
831 (base, last)
832 })
833 .map(|range| match range {
834 (Ok(Some(base)), Ok(None)) => Ok((base, 1)),
835 (Ok(Some(base)), Ok(Some(last))) => {
836 Ok((base, last.saturating_sub(base).saturating_add(1)))
837 }
838 (Err(e), _) => Err(argument::Error::InvalidValue {
839 value: e.to_string(),
840 expected: String::from("invalid base range value"),
841 }),
842 (_, Err(e)) => Err(argument::Error::InvalidValue {
843 value: e.to_string(),
844 expected: String::from("invalid last range value"),
845 }),
846 _ => Err(argument::Error::InvalidValue {
847 value: s.to_owned(),
848 expected: String::from("invalid range format"),
849 }),
850 })
851 .collect();
852 Ok(DirectIoOption {
853 path,
854 ranges: ranges?,
855 })
856}
857
Zach Reiznerefe95782017-08-26 18:05:48 -0700858fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::Result<()> {
859 match name {
860 "" => {
Cody Schuffelen6d1ab502019-05-21 12:12:38 -0700861 if cfg.executable_path.is_some() {
862 return Err(argument::Error::TooManyArguments(format!(
863 "A VM executable was already specified: {:?}",
864 cfg.executable_path
865 )));
Zach Reiznerefe95782017-08-26 18:05:48 -0700866 }
Cody Schuffelen6d1ab502019-05-21 12:12:38 -0700867 let kernel_path = PathBuf::from(value.unwrap());
868 if !kernel_path.exists() {
869 return Err(argument::Error::InvalidValue {
870 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800871 expected: String::from("this kernel path does not exist"),
Cody Schuffelen6d1ab502019-05-21 12:12:38 -0700872 });
873 }
874 cfg.executable_path = Some(Executable::Kernel(kernel_path));
Zach Reiznerefe95782017-08-26 18:05:48 -0700875 }
Christian Blichmann33d56772021-03-04 19:03:54 +0100876 "kvm-device" => {
877 let kvm_device_path = PathBuf::from(value.unwrap());
878 if !kvm_device_path.exists() {
879 return Err(argument::Error::InvalidValue {
880 value: value.unwrap().to_owned(),
881 expected: String::from("this kvm device path does not exist"),
882 });
883 }
884
885 cfg.kvm_device_path = kvm_device_path;
886 }
Christian Blichmann2f5d4b62021-03-10 18:08:08 +0100887 "vhost-vsock-device" => {
888 let vhost_vsock_device_path = PathBuf::from(value.unwrap());
889 if !vhost_vsock_device_path.exists() {
890 return Err(argument::Error::InvalidValue {
891 value: value.unwrap().to_owned(),
892 expected: String::from("this vhost-vsock device path does not exist"),
893 });
894 }
895
896 cfg.vhost_vsock_device_path = vhost_vsock_device_path;
897 }
898 "vhost-net-device" => {
899 let vhost_net_device_path = PathBuf::from(value.unwrap());
900 if !vhost_net_device_path.exists() {
901 return Err(argument::Error::InvalidValue {
902 value: value.unwrap().to_owned(),
903 expected: String::from("this vhost-vsock device path does not exist"),
904 });
905 }
906
907 cfg.vhost_net_device_path = vhost_net_device_path;
908 }
Tristan Muntsinger4133b012018-12-21 16:01:56 -0800909 "android-fstab" => {
910 if cfg.android_fstab.is_some()
911 && !cfg.android_fstab.as_ref().unwrap().as_os_str().is_empty()
912 {
913 return Err(argument::Error::TooManyArguments(
914 "expected exactly one android fstab path".to_owned(),
915 ));
916 } else {
917 let android_fstab = PathBuf::from(value.unwrap());
918 if !android_fstab.exists() {
919 return Err(argument::Error::InvalidValue {
920 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800921 expected: String::from("this android fstab path does not exist"),
Tristan Muntsinger4133b012018-12-21 16:01:56 -0800922 });
923 }
924 cfg.android_fstab = Some(android_fstab);
925 }
926 }
Zach Reiznerefe95782017-08-26 18:05:48 -0700927 "params" => {
Zach Reiznerbb678712018-01-30 18:13:04 -0800928 cfg.params.push(value.unwrap().to_owned());
Zach Reiznerefe95782017-08-26 18:05:48 -0700929 }
930 "cpus" => {
931 if cfg.vcpu_count.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700932 return Err(argument::Error::TooManyArguments(
933 "`cpus` already given".to_owned(),
934 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700935 }
936 cfg.vcpu_count =
Zach Reizner55a9e502018-10-03 10:22:32 -0700937 Some(
938 value
939 .unwrap()
940 .parse()
941 .map_err(|_| argument::Error::InvalidValue {
942 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800943 expected: String::from("this value for `cpus` needs to be integer"),
Zach Reizner55a9e502018-10-03 10:22:32 -0700944 })?,
945 )
Zach Reiznerefe95782017-08-26 18:05:48 -0700946 }
Daniel Verkamp107edb32019-04-05 09:58:48 -0700947 "cpu-affinity" => {
Daniel Verkampc677fb42020-09-08 13:47:49 -0700948 if cfg.vcpu_affinity.is_some() {
Daniel Verkamp107edb32019-04-05 09:58:48 -0700949 return Err(argument::Error::TooManyArguments(
950 "`cpu-affinity` already given".to_owned(),
951 ));
952 }
Daniel Verkampc677fb42020-09-08 13:47:49 -0700953 cfg.vcpu_affinity = Some(parse_cpu_affinity(value.unwrap())?);
Daniel Verkamp107edb32019-04-05 09:58:48 -0700954 }
Daniel Verkamp8a72afc2021-03-15 17:55:52 -0700955 "cpu-cluster" => {
956 cfg.cpu_clusters.push(parse_cpu_set(value.unwrap())?);
957 }
958 "cpu-capacity" => {
959 parse_cpu_capacity(value.unwrap(), &mut cfg.cpu_capacity)?;
960 }
Suleiman Souhlal015c3c12020-10-07 14:15:41 +0900961 "no-smt" => {
962 cfg.no_smt = true;
963 }
Kansho Nishidaab205af2020-08-13 18:17:50 +0900964 "rt-cpus" => {
965 if !cfg.rt_cpus.is_empty() {
966 return Err(argument::Error::TooManyArguments(
967 "`rt-cpus` already given".to_owned(),
968 ));
969 }
970 cfg.rt_cpus = parse_cpu_set(value.unwrap())?;
971 }
Zach Reiznerefe95782017-08-26 18:05:48 -0700972 "mem" => {
973 if cfg.memory.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700974 return Err(argument::Error::TooManyArguments(
975 "`mem` already given".to_owned(),
976 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700977 }
978 cfg.memory =
Zach Reizner55a9e502018-10-03 10:22:32 -0700979 Some(
980 value
981 .unwrap()
982 .parse()
983 .map_err(|_| argument::Error::InvalidValue {
984 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800985 expected: String::from("this value for `mem` needs to be integer"),
Zach Reizner55a9e502018-10-03 10:22:32 -0700986 })?,
987 )
Zach Reiznerefe95782017-08-26 18:05:48 -0700988 }
Sergey Senozhatsky1e369c52021-04-13 20:23:51 +0900989 "hugepages" => {
990 cfg.hugepages = true;
991 }
Andrew Scull1590e6f2020-03-18 18:00:47 +0000992 #[cfg(feature = "audio")]
Judy Hsiaod5c1e962020-02-04 12:30:01 +0800993 "ac97" => {
994 let ac97_params = parse_ac97_options(value.unwrap())?;
paulhsia16478242020-11-27 14:04:58 +0800995 // Add kernel parameters related to the intel8x0 driver for ac97 devices once.
996 if cfg.ac97_parameters.is_empty() {
997 // Set `inside_vm=1` to save some register read ops in the driver.
998 cfg.params.push("snd_intel8x0.inside_vm=1".to_string());
999 // Set `ac97_clock=48000` to save intel8x0_measure_ac97_clock call in the driver.
1000 cfg.params.push("snd_intel8x0.ac97_clock=48000".to_string());
1001 }
Judy Hsiaod5c1e962020-02-04 12:30:01 +08001002 cfg.ac97_parameters.push(ac97_params);
Dylan Reid3082e8e2019-01-07 10:33:48 -08001003 }
Trent Begin17ccaad2019-04-17 13:51:25 -06001004 "serial" => {
1005 let serial_params = parse_serial_options(value.unwrap())?;
1006 let num = serial_params.num;
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07001007 let key = (serial_params.hardware, num);
1008 if cfg.serial_parameters.contains_key(&key) {
Trent Begin17ccaad2019-04-17 13:51:25 -06001009 return Err(argument::Error::TooManyArguments(format!(
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07001010 "serial hardware {} num {}",
1011 serial_params.hardware, num,
Trent Begin17ccaad2019-04-17 13:51:25 -06001012 )));
1013 }
1014
1015 if serial_params.console {
Jakub Staronb6515a92019-06-05 15:18:25 -07001016 for params in cfg.serial_parameters.values() {
Trent Begin17ccaad2019-04-17 13:51:25 -06001017 if params.console {
1018 return Err(argument::Error::TooManyArguments(format!(
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07001019 "{} device {} already set as console",
1020 params.hardware, params.num,
1021 )));
1022 }
1023 }
1024 }
1025
1026 if serial_params.earlycon {
1027 // Only SerialHardware::Serial supports earlycon= currently.
1028 match serial_params.hardware {
1029 SerialHardware::Serial => {}
1030 _ => {
1031 return Err(argument::Error::InvalidValue {
Andrew Walbran9cfdbd92021-01-11 17:40:34 +00001032 value: serial_params.hardware.to_string(),
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07001033 expected: String::from("earlycon not supported for hardware"),
1034 });
1035 }
1036 }
1037 for params in cfg.serial_parameters.values() {
1038 if params.earlycon {
1039 return Err(argument::Error::TooManyArguments(format!(
1040 "{} device {} already set as earlycon",
1041 params.hardware, params.num,
Trent Begin17ccaad2019-04-17 13:51:25 -06001042 )));
1043 }
1044 }
1045 }
1046
Jorge E. Moreira1e262302019-08-01 14:40:03 -07001047 if serial_params.stdin {
1048 if let Some(previous_stdin) = cfg.serial_parameters.values().find(|sp| sp.stdin) {
1049 return Err(argument::Error::TooManyArguments(format!(
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07001050 "{} device {} already connected to standard input",
1051 previous_stdin.hardware, previous_stdin.num,
Jorge E. Moreira1e262302019-08-01 14:40:03 -07001052 )));
1053 }
1054 }
1055
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07001056 cfg.serial_parameters.insert(key, serial_params);
Trent Begin17ccaad2019-04-17 13:51:25 -06001057 }
1058 "syslog-tag" => {
1059 if cfg.syslog_tag.is_some() {
1060 return Err(argument::Error::TooManyArguments(
1061 "`syslog-tag` already given".to_owned(),
1062 ));
1063 }
1064 syslog::set_proc_name(value.unwrap());
1065 cfg.syslog_tag = Some(value.unwrap().to_owned());
1066 }
Daniel Verkamp4b62cd92019-11-08 13:09:27 -08001067 "root" | "rwroot" | "disk" | "rwdisk" => {
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001068 let param = value.unwrap();
1069 let mut components = param.split(',');
Daniel Verkamp6a8cd102019-06-26 15:17:46 -07001070 let read_only = !name.starts_with("rw");
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001071 let disk_path =
1072 PathBuf::from(
1073 components
1074 .next()
1075 .ok_or_else(|| argument::Error::InvalidValue {
1076 value: param.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001077 expected: String::from("missing disk path"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001078 })?,
1079 );
Zach Reiznerefe95782017-08-26 18:05:48 -07001080 if !disk_path.exists() {
1081 return Err(argument::Error::InvalidValue {
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001082 value: param.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001083 expected: String::from("this disk path does not exist"),
Zach Reizner55a9e502018-10-03 10:22:32 -07001084 });
Zach Reiznerefe95782017-08-26 18:05:48 -07001085 }
Daniel Verkamp6a8cd102019-06-26 15:17:46 -07001086 if name.ends_with("root") {
Daniel Verkampaac28132018-10-15 14:58:48 -07001087 if cfg.disks.len() >= 26 {
Zach Reizner55a9e502018-10-03 10:22:32 -07001088 return Err(argument::Error::TooManyArguments(
1089 "ran out of letters for to assign to root disk".to_owned(),
1090 ));
Zach Reiznerefe95782017-08-26 18:05:48 -07001091 }
Zach Reizner55a9e502018-10-03 10:22:32 -07001092 cfg.params.push(format!(
Daniel Verkamp6a8cd102019-06-26 15:17:46 -07001093 "root=/dev/vd{} {}",
1094 char::from(b'a' + cfg.disks.len() as u8),
1095 if read_only { "ro" } else { "rw" }
Zach Reizner55a9e502018-10-03 10:22:32 -07001096 ));
Zach Reiznerefe95782017-08-26 18:05:48 -07001097 }
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001098
1099 let mut disk = DiskOption {
Zach Reizner55a9e502018-10-03 10:22:32 -07001100 path: disk_path,
Daniel Verkamp6a8cd102019-06-26 15:17:46 -07001101 read_only,
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001102 sparse: true,
Daniel Verkamp27672232019-12-06 17:26:55 +11001103 block_size: 512,
Daniel Verkamp4e1f99a2020-06-01 12:47:21 -07001104 id: None,
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001105 };
1106
1107 for opt in components {
1108 let mut o = opt.splitn(2, '=');
1109 let kind = o.next().ok_or_else(|| argument::Error::InvalidValue {
1110 value: opt.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001111 expected: String::from("disk options must not be empty"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001112 })?;
1113 let value = o.next().ok_or_else(|| argument::Error::InvalidValue {
1114 value: opt.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001115 expected: String::from("disk options must be of the form `kind=value`"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001116 })?;
1117
1118 match kind {
1119 "sparse" => {
1120 let sparse = value.parse().map_err(|_| argument::Error::InvalidValue {
1121 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001122 expected: String::from("`sparse` must be a boolean"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001123 })?;
1124 disk.sparse = sparse;
1125 }
Daniel Verkamp27672232019-12-06 17:26:55 +11001126 "block_size" => {
1127 let block_size =
1128 value.parse().map_err(|_| argument::Error::InvalidValue {
1129 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001130 expected: String::from("`block_size` must be an integer"),
Daniel Verkamp27672232019-12-06 17:26:55 +11001131 })?;
1132 disk.block_size = block_size;
1133 }
Daniel Verkamp4e1f99a2020-06-01 12:47:21 -07001134 "id" => {
1135 if value.len() > DISK_ID_LEN {
1136 return Err(argument::Error::InvalidValue {
1137 value: value.to_owned(),
1138 expected: format!(
1139 "`id` must be {} or fewer characters",
1140 DISK_ID_LEN
1141 ),
1142 });
1143 }
1144 let mut id = [0u8; DISK_ID_LEN];
1145 // Slicing id to value's length will never panic
1146 // because we checked that value will fit into id above.
1147 id[..value.len()].copy_from_slice(value.as_bytes());
1148 disk.id = Some(id);
1149 }
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001150 _ => {
1151 return Err(argument::Error::InvalidValue {
1152 value: kind.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001153 expected: String::from("unrecognized disk option"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001154 });
1155 }
1156 }
1157 }
1158
1159 cfg.disks.push(disk);
Zach Reiznerefe95782017-08-26 18:05:48 -07001160 }
Jakub Starona3411ea2019-04-24 10:55:25 -07001161 "pmem-device" | "rw-pmem-device" => {
1162 let disk_path = PathBuf::from(value.unwrap());
1163 if !disk_path.exists() {
1164 return Err(argument::Error::InvalidValue {
1165 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001166 expected: String::from("this disk path does not exist"),
Jakub Starona3411ea2019-04-24 10:55:25 -07001167 });
1168 }
1169
1170 cfg.pmem_devices.push(DiskOption {
1171 path: disk_path,
1172 read_only: !name.starts_with("rw"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001173 sparse: false,
Michael Hoyle6b196952020-08-02 20:09:41 -07001174 block_size: base::pagesize() as u32,
Daniel Verkamp4e1f99a2020-06-01 12:47:21 -07001175 id: None,
Jakub Starona3411ea2019-04-24 10:55:25 -07001176 });
1177 }
Kansho Nishida282115b2019-12-18 13:13:14 +09001178 "pstore" => {
1179 if cfg.pstore.is_some() {
1180 return Err(argument::Error::TooManyArguments(
1181 "`pstore` already given".to_owned(),
1182 ));
1183 }
1184
1185 let value = value.unwrap();
1186 let components: Vec<&str> = value.split(',').collect();
1187 if components.len() != 2 {
1188 return Err(argument::Error::InvalidValue {
1189 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001190 expected: String::from(
1191 "pstore must have exactly 2 components: path=<path>,size=<size>",
1192 ),
Kansho Nishida282115b2019-12-18 13:13:14 +09001193 });
1194 }
1195 cfg.pstore = Some(Pstore {
1196 path: {
1197 if components[0].len() <= 5 || !components[0].starts_with("path=") {
1198 return Err(argument::Error::InvalidValue {
1199 value: components[0].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001200 expected: String::from("pstore path must follow with `path=`"),
Kansho Nishida282115b2019-12-18 13:13:14 +09001201 });
1202 };
1203 PathBuf::from(&components[0][5..])
1204 },
1205 size: {
1206 if components[1].len() <= 5 || !components[1].starts_with("size=") {
1207 return Err(argument::Error::InvalidValue {
1208 value: components[1].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001209 expected: String::from("pstore size must follow with `size=`"),
Kansho Nishida282115b2019-12-18 13:13:14 +09001210 });
1211 };
1212 components[1][5..]
1213 .parse()
1214 .map_err(|_| argument::Error::InvalidValue {
1215 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001216 expected: String::from("pstore size must be an integer"),
Kansho Nishida282115b2019-12-18 13:13:14 +09001217 })?
1218 },
1219 });
1220 }
Zach Reiznerefe95782017-08-26 18:05:48 -07001221 "host_ip" => {
Daniel Verkampaac28132018-10-15 14:58:48 -07001222 if cfg.host_ip.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -07001223 return Err(argument::Error::TooManyArguments(
1224 "`host_ip` already given".to_owned(),
1225 ));
Zach Reiznerefe95782017-08-26 18:05:48 -07001226 }
Daniel Verkampaac28132018-10-15 14:58:48 -07001227 cfg.host_ip =
Zach Reizner55a9e502018-10-03 10:22:32 -07001228 Some(
1229 value
1230 .unwrap()
1231 .parse()
1232 .map_err(|_| argument::Error::InvalidValue {
1233 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001234 expected: String::from("`host_ip` needs to be in the form \"x.x.x.x\""),
Zach Reizner55a9e502018-10-03 10:22:32 -07001235 })?,
1236 )
Zach Reiznerefe95782017-08-26 18:05:48 -07001237 }
1238 "netmask" => {
Daniel Verkampaac28132018-10-15 14:58:48 -07001239 if cfg.netmask.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -07001240 return Err(argument::Error::TooManyArguments(
1241 "`netmask` already given".to_owned(),
1242 ));
Zach Reiznerefe95782017-08-26 18:05:48 -07001243 }
Daniel Verkampaac28132018-10-15 14:58:48 -07001244 cfg.netmask =
Zach Reizner55a9e502018-10-03 10:22:32 -07001245 Some(
1246 value
1247 .unwrap()
1248 .parse()
1249 .map_err(|_| argument::Error::InvalidValue {
1250 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001251 expected: String::from("`netmask` needs to be in the form \"x.x.x.x\""),
Zach Reizner55a9e502018-10-03 10:22:32 -07001252 })?,
1253 )
Zach Reiznerefe95782017-08-26 18:05:48 -07001254 }
1255 "mac" => {
Daniel Verkampaac28132018-10-15 14:58:48 -07001256 if cfg.mac_address.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -07001257 return Err(argument::Error::TooManyArguments(
1258 "`mac` already given".to_owned(),
1259 ));
Zach Reiznerefe95782017-08-26 18:05:48 -07001260 }
Daniel Verkampaac28132018-10-15 14:58:48 -07001261 cfg.mac_address =
Zach Reizner55a9e502018-10-03 10:22:32 -07001262 Some(
1263 value
1264 .unwrap()
1265 .parse()
1266 .map_err(|_| argument::Error::InvalidValue {
1267 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001268 expected: String::from(
1269 "`mac` needs to be in the form \"XX:XX:XX:XX:XX:XX\"",
1270 ),
Zach Reizner55a9e502018-10-03 10:22:32 -07001271 })?,
1272 )
Zach Reiznerefe95782017-08-26 18:05:48 -07001273 }
Xiong Zhang773c7072020-03-20 10:39:55 +08001274 "net-vq-pairs" => {
1275 if cfg.net_vq_pairs.is_some() {
1276 return Err(argument::Error::TooManyArguments(
1277 "`net-vq-pairs` already given".to_owned(),
1278 ));
1279 }
1280 cfg.net_vq_pairs =
1281 Some(
1282 value
1283 .unwrap()
1284 .parse()
1285 .map_err(|_| argument::Error::InvalidValue {
1286 value: value.unwrap().to_owned(),
1287 expected: String::from(
1288 "this value for `net-vq-pairs` needs to be integer",
1289 ),
1290 })?,
1291 )
1292 }
1293
Stephen Barber28a5a612017-10-20 17:15:30 -07001294 "wayland-sock" => {
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09001295 let mut components = value.unwrap().split(',');
1296 let path =
1297 PathBuf::from(
1298 components
1299 .next()
1300 .ok_or_else(|| argument::Error::InvalidValue {
1301 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001302 expected: String::from("missing socket path"),
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09001303 })?,
1304 );
1305 let mut name = "";
1306 for c in components {
1307 let mut kv = c.splitn(2, '=');
1308 let (kind, value) = match (kv.next(), kv.next()) {
1309 (Some(kind), Some(value)) => (kind, value),
1310 _ => {
1311 return Err(argument::Error::InvalidValue {
1312 value: c.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001313 expected: String::from("option must be of the form `kind=value`"),
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09001314 })
1315 }
1316 };
1317 match kind {
1318 "name" => name = value,
1319 _ => {
1320 return Err(argument::Error::InvalidValue {
1321 value: kind.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001322 expected: String::from("unrecognized option"),
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09001323 })
1324 }
1325 }
Stephen Barber28a5a612017-10-20 17:15:30 -07001326 }
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09001327 if cfg.wayland_socket_paths.contains_key(name) {
1328 return Err(argument::Error::TooManyArguments(format!(
1329 "wayland socket name already used: '{}'",
1330 name
1331 )));
Stephen Barber28a5a612017-10-20 17:15:30 -07001332 }
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09001333 cfg.wayland_socket_paths.insert(name.to_string(), path);
Stephen Barber28a5a612017-10-20 17:15:30 -07001334 }
David Reveman52ba4e52018-04-22 21:42:09 -04001335 #[cfg(feature = "wl-dmabuf")]
Daniel Verkampaac28132018-10-15 14:58:48 -07001336 "wayland-dmabuf" => cfg.wayland_dmabuf = true,
Zach Reizner0f2cfb02019-06-19 17:46:03 -07001337 "x-display" => {
1338 if cfg.x_display.is_some() {
1339 return Err(argument::Error::TooManyArguments(
1340 "`x-display` already given".to_owned(),
1341 ));
1342 }
1343 cfg.x_display = Some(value.unwrap().to_owned());
1344 }
Zach Reizner65b98f12019-11-22 17:34:58 -08001345 "display-window-keyboard" => {
1346 cfg.display_window_keyboard = true;
1347 }
1348 "display-window-mouse" => {
1349 cfg.display_window_mouse = true;
1350 }
Zach Reiznerefe95782017-08-26 18:05:48 -07001351 "socket" => {
1352 if cfg.socket_path.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -07001353 return Err(argument::Error::TooManyArguments(
1354 "`socket` already given".to_owned(),
1355 ));
Zach Reiznerefe95782017-08-26 18:05:48 -07001356 }
1357 let mut socket_path = PathBuf::from(value.unwrap());
1358 if socket_path.is_dir() {
1359 socket_path.push(format!("crosvm-{}.sock", getpid()));
1360 }
1361 if socket_path.exists() {
1362 return Err(argument::Error::InvalidValue {
Zach Reizner55a9e502018-10-03 10:22:32 -07001363 value: socket_path.to_string_lossy().into_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001364 expected: String::from("this socket path already exists"),
Zach Reizner55a9e502018-10-03 10:22:32 -07001365 });
Zach Reiznerefe95782017-08-26 18:05:48 -07001366 }
1367 cfg.socket_path = Some(socket_path);
1368 }
Dylan Reidd0c9adc2017-10-02 19:04:50 -07001369 "disable-sandbox" => {
Lepton Wu9105e9f2019-03-14 11:38:31 -07001370 cfg.sandbox = false;
Dylan Reidd0c9adc2017-10-02 19:04:50 -07001371 }
Chirantan Ekbote88f9cba2017-08-28 09:51:18 -07001372 "cid" => {
Daniel Verkampaac28132018-10-15 14:58:48 -07001373 if cfg.cid.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -07001374 return Err(argument::Error::TooManyArguments(
1375 "`cid` alread given".to_owned(),
1376 ));
Chirantan Ekbote88f9cba2017-08-28 09:51:18 -07001377 }
Daniel Verkampaac28132018-10-15 14:58:48 -07001378 cfg.cid = Some(
1379 value
1380 .unwrap()
1381 .parse()
1382 .map_err(|_| argument::Error::InvalidValue {
1383 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001384 expected: String::from("this value for `cid` must be an unsigned integer"),
Daniel Verkampaac28132018-10-15 14:58:48 -07001385 })?,
1386 );
Chirantan Ekbote88f9cba2017-08-28 09:51:18 -07001387 }
Chirantan Ekboteebd56812018-04-16 19:32:04 -07001388 "shared-dir" => {
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001389 // This is formatted as multiple fields, each separated by ":". The first 2 fields are
1390 // fixed (src:tag). The rest may appear in any order:
1391 //
1392 // * type=TYPE - must be one of "p9" or "fs" (default: p9)
1393 // * uidmap=UIDMAP - a uid map in the format "inner outer count[,inner outer count]"
1394 // (default: "0 <current euid> 1")
1395 // * gidmap=GIDMAP - a gid map in the same format as uidmap
1396 // (default: "0 <current egid> 1")
1397 // * timeout=TIMEOUT - a timeout value in seconds, which indicates how long attributes
1398 // and directory contents should be considered valid (default: 5)
1399 // * cache=CACHE - one of "never", "always", or "auto" (default: auto)
1400 // * writeback=BOOL - indicates whether writeback caching should be enabled (default: false)
Chirantan Ekboteebd56812018-04-16 19:32:04 -07001401 let param = value.unwrap();
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001402 let mut components = param.split(':');
Zach Reizner55a9e502018-10-03 10:22:32 -07001403 let src =
1404 PathBuf::from(
1405 components
1406 .next()
1407 .ok_or_else(|| argument::Error::InvalidValue {
1408 value: param.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001409 expected: String::from("missing source path for `shared-dir`"),
Zach Reizner55a9e502018-10-03 10:22:32 -07001410 })?,
1411 );
1412 let tag = components
1413 .next()
1414 .ok_or_else(|| argument::Error::InvalidValue {
Chirantan Ekboteebd56812018-04-16 19:32:04 -07001415 value: param.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001416 expected: String::from("missing tag for `shared-dir`"),
David Tolnay2bac1e72018-12-12 14:33:42 -08001417 })?
1418 .to_owned();
Chirantan Ekboteebd56812018-04-16 19:32:04 -07001419
1420 if !src.is_dir() {
1421 return Err(argument::Error::InvalidValue {
1422 value: param.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001423 expected: String::from("source path for `shared-dir` must be a directory"),
Chirantan Ekboteebd56812018-04-16 19:32:04 -07001424 });
1425 }
1426
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001427 let mut shared_dir = SharedDir {
1428 src,
1429 tag,
1430 ..Default::default()
1431 };
1432 for opt in components {
1433 let mut o = opt.splitn(2, '=');
1434 let kind = o.next().ok_or_else(|| argument::Error::InvalidValue {
1435 value: opt.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001436 expected: String::from("`shared-dir` options must not be empty"),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001437 })?;
1438 let value = o.next().ok_or_else(|| argument::Error::InvalidValue {
1439 value: opt.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001440 expected: String::from("`shared-dir` options must be of the form `kind=value`"),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001441 })?;
1442
1443 match kind {
1444 "type" => {
1445 shared_dir.kind =
1446 value.parse().map_err(|_| argument::Error::InvalidValue {
1447 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001448 expected: String::from("`type` must be one of `fs` or `9p`"),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001449 })?
1450 }
1451 "uidmap" => shared_dir.uid_map = value.into(),
1452 "gidmap" => shared_dir.gid_map = value.into(),
1453 "timeout" => {
1454 let seconds = value.parse().map_err(|_| argument::Error::InvalidValue {
1455 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001456 expected: String::from("`timeout` must be an integer"),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001457 })?;
1458
1459 let dur = Duration::from_secs(seconds);
Andrew Walbran9cfdbd92021-01-11 17:40:34 +00001460 shared_dir.fs_cfg.entry_timeout = dur;
Chirantan Ekbote75ba8752020-10-27 18:33:02 +09001461 shared_dir.fs_cfg.attr_timeout = dur;
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001462 }
1463 "cache" => {
1464 let policy = value.parse().map_err(|_| argument::Error::InvalidValue {
1465 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001466 expected: String::from(
1467 "`cache` must be one of `never`, `always`, or `auto`",
1468 ),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001469 })?;
Chirantan Ekbote75ba8752020-10-27 18:33:02 +09001470 shared_dir.fs_cfg.cache_policy = policy;
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001471 }
1472 "writeback" => {
1473 let writeback =
1474 value.parse().map_err(|_| argument::Error::InvalidValue {
1475 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001476 expected: String::from("`writeback` must be a boolean"),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001477 })?;
Chirantan Ekbote75ba8752020-10-27 18:33:02 +09001478 shared_dir.fs_cfg.writeback = writeback;
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001479 }
Chirantan Ekboted994e512020-06-12 18:46:52 +09001480 "rewrite-security-xattrs" => {
1481 let rewrite_security_xattrs =
1482 value.parse().map_err(|_| argument::Error::InvalidValue {
1483 value: value.to_owned(),
1484 expected: String::from(
1485 "`rewrite-security-xattrs` must be a boolean",
1486 ),
1487 })?;
Chirantan Ekbote75ba8752020-10-27 18:33:02 +09001488 shared_dir.fs_cfg.rewrite_security_xattrs = rewrite_security_xattrs;
Chirantan Ekboted994e512020-06-12 18:46:52 +09001489 }
Chirantan Ekbote09357c82020-09-10 20:08:28 +09001490 "ascii_casefold" => {
1491 let ascii_casefold =
1492 value.parse().map_err(|_| argument::Error::InvalidValue {
1493 value: value.to_owned(),
1494 expected: String::from("`ascii_casefold` must be a boolean"),
1495 })?;
Chirantan Ekbote75ba8752020-10-27 18:33:02 +09001496 shared_dir.fs_cfg.ascii_casefold = ascii_casefold;
1497 shared_dir.p9_cfg.ascii_casefold = ascii_casefold;
Chirantan Ekbote09357c82020-09-10 20:08:28 +09001498 }
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001499 _ => {
1500 return Err(argument::Error::InvalidValue {
1501 value: kind.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001502 expected: String::from("unrecognized option for `shared-dir`"),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001503 })
1504 }
1505 }
1506 }
1507 cfg.shared_dirs.push(shared_dir);
Chirantan Ekboteebd56812018-04-16 19:32:04 -07001508 }
Dylan Reide026ef02017-10-02 19:03:52 -07001509 "seccomp-policy-dir" => {
Dylan Reidd0c9adc2017-10-02 19:04:50 -07001510 // `value` is Some because we are in this match so it's safe to unwrap.
Daniel Verkampaac28132018-10-15 14:58:48 -07001511 cfg.seccomp_policy_dir = PathBuf::from(value.unwrap());
Zach Reizner55a9e502018-10-03 10:22:32 -07001512 }
Zach Reizner44863792019-06-26 14:22:08 -07001513 "seccomp-log-failures" => {
Matt Delco45caf912019-11-13 08:11:09 -08001514 // A side-effect of this flag is to force the use of .policy files
1515 // instead of .bpf files (.bpf files are expected and assumed to be
1516 // compiled to fail an unpermitted action with "trap").
1517 // Normally crosvm will first attempt to use a .bpf file, and if
1518 // not present it will then try to use a .policy file. It's up
1519 // to the build to decide which of these files is present for
1520 // crosvm to use (for CrOS the build will use .bpf files for
1521 // x64 builds and .policy files for arm/arm64 builds).
1522 //
1523 // This flag will likely work as expected for builds that use
1524 // .policy files. For builds that only use .bpf files the initial
1525 // result when using this flag is likely to be a file-not-found
1526 // error (since the .policy files are not present).
1527 // For .bpf builds you can either 1) manually add the .policy files,
1528 // or 2) do not use this command-line parameter and instead
1529 // temporarily change the build by passing "log" rather than
1530 // "trap" as the "--default-action" to compile_seccomp_policy.py.
Zach Reizner44863792019-06-26 14:22:08 -07001531 cfg.seccomp_log_failures = true;
1532 }
Zach Reizner8864cb02018-01-16 17:59:03 -08001533 "plugin" => {
Cody Schuffelen6d1ab502019-05-21 12:12:38 -07001534 if cfg.executable_path.is_some() {
1535 return Err(argument::Error::TooManyArguments(format!(
1536 "A VM executable was already specified: {:?}",
1537 cfg.executable_path
1538 )));
Zach Reizner8864cb02018-01-16 17:59:03 -08001539 }
Zach Reiznercc30d582018-01-23 21:16:42 -08001540 let plugin = PathBuf::from(value.unwrap().to_owned());
1541 if plugin.is_relative() {
1542 return Err(argument::Error::InvalidValue {
Zach Reizner55a9e502018-10-03 10:22:32 -07001543 value: plugin.to_string_lossy().into_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001544 expected: String::from("the plugin path must be an absolute path"),
Zach Reizner55a9e502018-10-03 10:22:32 -07001545 });
Zach Reiznercc30d582018-01-23 21:16:42 -08001546 }
Cody Schuffelen6d1ab502019-05-21 12:12:38 -07001547 cfg.executable_path = Some(Executable::Plugin(plugin));
Zach Reizner55a9e502018-10-03 10:22:32 -07001548 }
Dmitry Torokhov0f1770d2018-05-11 10:57:16 -07001549 "plugin-root" => {
1550 cfg.plugin_root = Some(PathBuf::from(value.unwrap().to_owned()));
Zach Reizner55a9e502018-10-03 10:22:32 -07001551 }
Chirantan Ekboted41d7262018-11-16 16:37:45 -08001552 "plugin-mount" => {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -08001553 let mount = parse_plugin_mount_option(value.unwrap())?;
1554 cfg.plugin_mounts.push(mount);
Chirantan Ekboted41d7262018-11-16 16:37:45 -08001555 }
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08001556 "plugin-mount-file" => {
1557 let file = File::open(value.unwrap()).map_err(|_| argument::Error::InvalidValue {
1558 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001559 expected: String::from("unable to open `plugin-mount-file` file"),
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08001560 })?;
1561 let reader = BufReader::new(file);
1562 for l in reader.lines() {
1563 let line = l.unwrap();
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09001564 let trimmed_line = line.splitn(2, '#').next().unwrap().trim();
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08001565 if !trimmed_line.is_empty() {
1566 let mount = parse_plugin_mount_option(trimmed_line)?;
1567 cfg.plugin_mounts.push(mount);
1568 }
1569 }
1570 }
Dmitry Torokhov1a6262b2019-03-01 00:34:03 -08001571 "plugin-gid-map" => {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -08001572 let map = parse_plugin_gid_map_option(value.unwrap())?;
1573 cfg.plugin_gid_maps.push(map);
Dmitry Torokhov1a6262b2019-03-01 00:34:03 -08001574 }
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08001575 "plugin-gid-map-file" => {
1576 let file = File::open(value.unwrap()).map_err(|_| argument::Error::InvalidValue {
1577 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001578 expected: String::from("unable to open `plugin-gid-map-file` file"),
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08001579 })?;
1580 let reader = BufReader::new(file);
1581 for l in reader.lines() {
1582 let line = l.unwrap();
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09001583 let trimmed_line = line.splitn(2, '#').next().unwrap().trim();
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08001584 if !trimmed_line.is_empty() {
1585 let map = parse_plugin_gid_map_option(trimmed_line)?;
1586 cfg.plugin_gid_maps.push(map);
1587 }
1588 }
1589 }
Daniel Verkampaac28132018-10-15 14:58:48 -07001590 "vhost-net" => cfg.vhost_net = true,
Chirantan Ekbote5f787212018-05-31 15:31:31 -07001591 "tap-fd" => {
Jorge E. Moreirab7952802019-02-12 16:43:05 -08001592 cfg.tap_fd.push(
1593 value
1594 .unwrap()
1595 .parse()
1596 .map_err(|_| argument::Error::InvalidValue {
1597 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001598 expected: String::from(
1599 "this value for `tap-fd` must be an unsigned integer",
1600 ),
Jorge E. Moreirab7952802019-02-12 16:43:05 -08001601 })?,
1602 );
Chirantan Ekbote5f787212018-05-31 15:31:31 -07001603 }
Jason Macnakcc7070b2019-11-06 14:48:12 -08001604 #[cfg(feature = "gpu")]
Zach Reizner3a8100a2017-09-13 19:15:43 -07001605 "gpu" => {
Jason Macnakd659a0d2021-03-15 15:33:01 -07001606 if cfg.gpu_parameters.is_none() {
1607 cfg.gpu_parameters = Some(Default::default());
1608 }
1609 parse_gpu_options(value, cfg.gpu_parameters.as_mut().unwrap())?;
1610 }
1611 #[cfg(feature = "gpu")]
1612 "gpu-display" => {
1613 if cfg.gpu_parameters.is_none() {
1614 cfg.gpu_parameters = Some(Default::default());
1615 }
1616 parse_gpu_display_options(value, cfg.gpu_parameters.as_mut().unwrap())?;
Zach Reizner3a8100a2017-09-13 19:15:43 -07001617 }
David Tolnay43f8e212019-02-13 17:28:16 -08001618 "software-tpm" => {
1619 cfg.software_tpm = true;
1620 }
Jorge E. Moreira99d3f082019-03-07 10:59:54 -08001621 "single-touch" => {
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09001622 let mut it = value.unwrap().split(':');
Jorge E. Moreira99d3f082019-03-07 10:59:54 -08001623
1624 let mut single_touch_spec =
1625 TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned()));
1626 if let Some(width) = it.next() {
Kaiyi Libccb4eb2020-02-06 17:53:11 -08001627 single_touch_spec.set_width(width.trim().parse().unwrap());
Jorge E. Moreira99d3f082019-03-07 10:59:54 -08001628 }
1629 if let Some(height) = it.next() {
Kaiyi Libccb4eb2020-02-06 17:53:11 -08001630 single_touch_spec.set_height(height.trim().parse().unwrap());
Jorge E. Moreira99d3f082019-03-07 10:59:54 -08001631 }
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07001632 cfg.virtio_single_touch.push(single_touch_spec);
Jorge E. Moreira99d3f082019-03-07 10:59:54 -08001633 }
Tristan Muntsinger486cffc2020-09-29 22:05:41 +00001634 "multi-touch" => {
Tristan Muntsinger486cffc2020-09-29 22:05:41 +00001635 let mut it = value.unwrap().split(':');
1636
1637 let mut multi_touch_spec =
1638 TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned()));
1639 if let Some(width) = it.next() {
1640 multi_touch_spec.set_width(width.trim().parse().unwrap());
1641 }
1642 if let Some(height) = it.next() {
1643 multi_touch_spec.set_height(height.trim().parse().unwrap());
1644 }
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07001645 cfg.virtio_multi_touch.push(multi_touch_spec);
Tristan Muntsinger486cffc2020-09-29 22:05:41 +00001646 }
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001647 "trackpad" => {
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09001648 let mut it = value.unwrap().split(':');
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001649
1650 let mut trackpad_spec =
Jorge E. Moreira99d3f082019-03-07 10:59:54 -08001651 TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned()));
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001652 if let Some(width) = it.next() {
Kaiyi Libccb4eb2020-02-06 17:53:11 -08001653 trackpad_spec.set_width(width.trim().parse().unwrap());
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001654 }
1655 if let Some(height) = it.next() {
Kaiyi Libccb4eb2020-02-06 17:53:11 -08001656 trackpad_spec.set_height(height.trim().parse().unwrap());
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001657 }
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07001658 cfg.virtio_trackpad.push(trackpad_spec);
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001659 }
1660 "mouse" => {
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07001661 cfg.virtio_mice
1662 .push(PathBuf::from(value.unwrap().to_owned()));
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001663 }
1664 "keyboard" => {
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07001665 cfg.virtio_keyboard
1666 .push(PathBuf::from(value.unwrap().to_owned()));
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001667 }
Daniel Norman5e23df72021-03-11 10:11:02 -08001668 "switches" => {
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07001669 cfg.virtio_switches
1670 .push(PathBuf::from(value.unwrap().to_owned()));
Daniel Norman5e23df72021-03-11 10:11:02 -08001671 }
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001672 "evdev" => {
1673 let dev_path = PathBuf::from(value.unwrap());
1674 if !dev_path.exists() {
1675 return Err(argument::Error::InvalidValue {
1676 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001677 expected: String::from("this input device path does not exist"),
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001678 });
1679 }
1680 cfg.virtio_input_evdevs.push(dev_path);
1681 }
Miriam Zimmerman26ac9282019-01-29 21:21:48 -08001682 "split-irqchip" => {
1683 cfg.split_irqchip = true;
1684 }
Daniel Verkampe403f5c2018-12-11 16:29:26 -08001685 "initrd" => {
1686 cfg.initrd_path = Some(PathBuf::from(value.unwrap().to_owned()));
1687 }
Cody Schuffelen6d1ab502019-05-21 12:12:38 -07001688 "bios" => {
1689 if cfg.executable_path.is_some() {
1690 return Err(argument::Error::TooManyArguments(format!(
1691 "A VM executable was already specified: {:?}",
1692 cfg.executable_path
1693 )));
1694 }
1695 cfg.executable_path = Some(Executable::Bios(PathBuf::from(value.unwrap().to_owned())));
1696 }
Xiong Zhang17b0daf2019-04-23 17:14:50 +08001697 "vfio" => {
1698 let vfio_path = PathBuf::from(value.unwrap());
1699 if !vfio_path.exists() {
1700 return Err(argument::Error::InvalidValue {
1701 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001702 expected: String::from("the vfio path does not exist"),
Xiong Zhang17b0daf2019-04-23 17:14:50 +08001703 });
1704 }
1705 if !vfio_path.is_dir() {
1706 return Err(argument::Error::InvalidValue {
1707 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001708 expected: String::from("the vfio path should be directory"),
Xiong Zhang17b0daf2019-04-23 17:14:50 +08001709 });
1710 }
1711
Xiong Zhang8bb4faa2019-11-12 10:06:13 +08001712 cfg.vfio.push(vfio_path);
Xiong Zhang17b0daf2019-04-23 17:14:50 +08001713 }
Keiichi Watanabe57df6a02019-12-06 22:24:40 +09001714 "video-decoder" => {
1715 cfg.video_dec = true;
1716 }
1717 "video-encoder" => {
1718 cfg.video_enc = true;
1719 }
Tomasz Jeznach42644642020-05-20 23:27:59 -07001720 "acpi-table" => {
1721 let acpi_table = PathBuf::from(value.unwrap());
1722 if !acpi_table.exists() {
1723 return Err(argument::Error::InvalidValue {
1724 value: value.unwrap().to_owned(),
1725 expected: String::from("the acpi-table path does not exist"),
1726 });
1727 }
1728 if !acpi_table.is_file() {
1729 return Err(argument::Error::InvalidValue {
1730 value: value.unwrap().to_owned(),
1731 expected: String::from("the acpi-table path should be a file"),
1732 });
1733 }
1734
1735 cfg.acpi_tables.push(acpi_table);
1736 }
Will Deacon6560c182020-10-06 18:47:18 +01001737 "protected-vm" => {
Andrew Walbran413f8542021-01-08 13:29:03 +00001738 cfg.protected_vm = ProtectionType::Protected;
Will Deacon6560c182020-10-06 18:47:18 +01001739 cfg.params.push("swiotlb=force".to_string());
1740 }
Chuanxiao Dongfd5626c2020-04-27 16:35:33 +08001741 "battery" => {
1742 let params = parse_battery_options(value)?;
1743 cfg.battery_type = Some(params);
1744 }
Keiichi Watanabec5262e92020-10-21 15:57:33 +09001745 #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
1746 "gdb" => {
1747 let port = value
1748 .unwrap()
1749 .parse()
1750 .map_err(|_| argument::Error::InvalidValue {
1751 value: value.unwrap().to_owned(),
1752 expected: String::from("expected a valid port number"),
1753 })?;
1754 cfg.gdb = Some(port);
1755 }
Charles William Dick0e3d4b62020-12-14 12:16:46 +09001756 "balloon_bias_mib" => {
1757 cfg.balloon_bias =
1758 value
1759 .unwrap()
1760 .parse::<i64>()
1761 .map_err(|_| argument::Error::InvalidValue {
1762 value: value.unwrap().to_owned(),
1763 expected: String::from("expected a valid ballon bias in MiB"),
1764 })?
1765 * 1024
1766 * 1024; // cfg.balloon_bias is in bytes.
1767 }
Keiichi Watanabef3a37f42021-01-21 15:41:11 +09001768 "vhost-user-blk" => cfg.vhost_user_blk.push(VhostUserOption {
1769 socket: PathBuf::from(value.unwrap()),
1770 }),
Keiichi Watanabe60686582021-03-12 04:53:51 +09001771 "vhost-user-net" => cfg.vhost_user_net.push(VhostUserOption {
1772 socket: PathBuf::from(value.unwrap()),
1773 }),
Chirantan Ekbote2ee9dcd2021-05-26 18:21:44 +09001774 "vhost-user-wl" => {
1775 let mut components = value.unwrap().splitn(2, ":");
1776 let socket = components.next().map(PathBuf::from).ok_or_else(|| {
1777 argument::Error::InvalidValue {
1778 value: value.unwrap().to_owned(),
1779 expected: String::from("missing socket path"),
1780 }
1781 })?;
1782 let vm_tube = components.next().map(PathBuf::from).ok_or_else(|| {
1783 argument::Error::InvalidValue {
1784 value: value.unwrap().to_owned(),
1785 expected: String::from("missing vm tube path"),
1786 }
1787 })?;
1788 cfg.vhost_user_wl
1789 .push(VhostUserWlOption { socket, vm_tube });
1790 }
Woody Chow5890b702021-02-12 14:57:02 +09001791 "vhost-user-fs" => {
1792 // (socket:tag)
1793 let param = value.unwrap();
1794 let mut components = param.split(':');
1795 let socket =
1796 PathBuf::from(
1797 components
1798 .next()
1799 .ok_or_else(|| argument::Error::InvalidValue {
1800 value: param.to_owned(),
1801 expected: String::from("missing socket path for `vhost-user-fs`"),
1802 })?,
1803 );
1804 let tag = components
1805 .next()
1806 .ok_or_else(|| argument::Error::InvalidValue {
1807 value: param.to_owned(),
1808 expected: String::from("missing tag for `vhost-user-fs`"),
1809 })?
1810 .to_owned();
1811 cfg.vhost_user_fs.push(VhostUserFsOption { socket, tag });
1812 }
Tomasz Jeznach3ce74762021-02-26 01:01:53 -08001813 #[cfg(feature = "direct")]
1814 "direct-pmio" => {
1815 if cfg.direct_pmio.is_some() {
1816 return Err(argument::Error::TooManyArguments(
1817 "`direct_pmio` already given".to_owned(),
1818 ));
1819 }
1820 cfg.direct_pmio = Some(parse_direct_io_options(value)?);
1821 }
Tomasz Jeznach7271f752021-03-04 01:44:06 -08001822 #[cfg(feature = "direct")]
1823 "direct-level-irq" => {
1824 cfg.direct_level_irq
1825 .push(
1826 value
1827 .unwrap()
1828 .parse()
1829 .map_err(|_| argument::Error::InvalidValue {
1830 value: value.unwrap().to_owned(),
1831 expected: String::from(
1832 "this value for `direct-level-irq` must be an unsigned integer",
1833 ),
1834 })?,
1835 );
1836 }
1837 #[cfg(feature = "direct")]
1838 "direct-edge-irq" => {
1839 cfg.direct_edge_irq
1840 .push(
1841 value
1842 .unwrap()
1843 .parse()
1844 .map_err(|_| argument::Error::InvalidValue {
1845 value: value.unwrap().to_owned(),
1846 expected: String::from(
1847 "this value for `direct-edge-irq` must be an unsigned integer",
1848 ),
1849 })?,
1850 );
1851 }
Tomasz Jeznachccb26942021-03-30 22:44:11 -07001852 "dmi" => {
1853 if cfg.dmi_path.is_some() {
1854 return Err(argument::Error::TooManyArguments(
1855 "`dmi` already given".to_owned(),
1856 ));
1857 }
1858 let dmi_path = PathBuf::from(value.unwrap());
1859 if !dmi_path.exists() {
1860 return Err(argument::Error::InvalidValue {
1861 value: value.unwrap().to_owned(),
1862 expected: String::from("the dmi path does not exist"),
1863 });
1864 }
1865 if !dmi_path.is_dir() {
1866 return Err(argument::Error::InvalidValue {
1867 value: value.unwrap().to_owned(),
1868 expected: String::from("the dmi path should be directory"),
1869 });
1870 }
1871 cfg.dmi_path = Some(dmi_path);
1872 }
Tomasz Jeznachd93c29f2021-04-12 11:00:24 -07001873 "no-legacy" => {
1874 cfg.no_legacy = true;
1875 }
Zach Reiznerefe95782017-08-26 18:05:48 -07001876 "help" => return Err(argument::Error::PrintHelp),
1877 _ => unreachable!(),
1878 }
1879 Ok(())
1880}
1881
Kaiyi Libccb4eb2020-02-06 17:53:11 -08001882fn validate_arguments(cfg: &mut Config) -> std::result::Result<(), argument::Error> {
1883 if cfg.executable_path.is_none() {
1884 return Err(argument::Error::ExpectedArgument("`KERNEL`".to_owned()));
1885 }
1886 if cfg.host_ip.is_some() || cfg.netmask.is_some() || cfg.mac_address.is_some() {
1887 if cfg.host_ip.is_none() {
1888 return Err(argument::Error::ExpectedArgument(
1889 "`host_ip` missing from network config".to_owned(),
1890 ));
1891 }
1892 if cfg.netmask.is_none() {
1893 return Err(argument::Error::ExpectedArgument(
1894 "`netmask` missing from network config".to_owned(),
1895 ));
1896 }
1897 if cfg.mac_address.is_none() {
1898 return Err(argument::Error::ExpectedArgument(
1899 "`mac` missing from network config".to_owned(),
1900 ));
1901 }
1902 }
1903 if cfg.plugin_root.is_some() && !executable_is_plugin(&cfg.executable_path) {
1904 return Err(argument::Error::ExpectedArgument(
1905 "`plugin-root` requires `plugin`".to_owned(),
1906 ));
1907 }
1908 #[cfg(feature = "gpu")]
1909 {
Jason Macnakd659a0d2021-03-15 15:33:01 -07001910 if let Some(gpu_parameters) = cfg.gpu_parameters.as_mut() {
1911 if gpu_parameters.displays.is_empty() {
1912 gpu_parameters.displays.push(GpuDisplayParameters {
1913 width: DEFAULT_DISPLAY_WIDTH,
1914 height: DEFAULT_DISPLAY_HEIGHT,
1915 });
1916 }
1917
1918 let width = gpu_parameters.displays[0].width;
1919 let height = gpu_parameters.displays[0].height;
1920
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07001921 if let Some(virtio_multi_touch) = cfg.virtio_multi_touch.first_mut() {
Tristan Muntsinger486cffc2020-09-29 22:05:41 +00001922 virtio_multi_touch.set_default_size(width, height);
1923 }
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07001924 if let Some(virtio_single_touch) = cfg.virtio_single_touch.first_mut() {
Kaiyi Libccb4eb2020-02-06 17:53:11 -08001925 virtio_single_touch.set_default_size(width, height);
1926 }
1927 }
1928 }
Keiichi Watanabec5262e92020-10-21 15:57:33 +09001929 #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
1930 if cfg.gdb.is_some() {
1931 if cfg.vcpu_count.unwrap_or(1) != 1 {
1932 return Err(argument::Error::ExpectedArgument(
1933 "`gdb` requires the number of vCPU to be 1".to_owned(),
1934 ));
1935 }
1936 }
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07001937 set_default_serial_parameters(&mut cfg.serial_parameters);
Kaiyi Libccb4eb2020-02-06 17:53:11 -08001938 Ok(())
1939}
1940
Dylan Reidbfba9932018-02-05 15:51:59 -08001941fn run_vm(args: std::env::Args) -> std::result::Result<(), ()> {
Zach Reiznerefe95782017-08-26 18:05:48 -07001942 let arguments =
1943 &[Argument::positional("KERNEL", "bzImage of kernel to run"),
Christian Blichmann33d56772021-03-04 19:03:54 +01001944 Argument::value("kvm-device", "PATH", "Path to the KVM device. (default /dev/kvm)"),
Christian Blichmann2f5d4b62021-03-10 18:08:08 +01001945 Argument::value("vhost-vsock-device", "PATH", "Path to the vhost-vsock device. (default /dev/vhost-vsock)"),
1946 Argument::value("vhost-net-device", "PATH", "Path to the vhost-net device. (default /dev/vhost-net)"),
Tristan Muntsinger4133b012018-12-21 16:01:56 -08001947 Argument::value("android-fstab", "PATH", "Path to Android fstab"),
Daniel Verkampe403f5c2018-12-11 16:29:26 -08001948 Argument::short_value('i', "initrd", "PATH", "Initial ramdisk to load."),
Zach Reiznerefe95782017-08-26 18:05:48 -07001949 Argument::short_value('p',
1950 "params",
1951 "PARAMS",
Zach Reiznerbb678712018-01-30 18:13:04 -08001952 "Extra kernel or plugin command line arguments. Can be given more than once."),
Zach Reiznerefe95782017-08-26 18:05:48 -07001953 Argument::short_value('c', "cpus", "N", "Number of VCPUs. (default: 1)"),
Daniel Verkampc677fb42020-09-08 13:47:49 -07001954 Argument::value("cpu-affinity", "CPUSET", "Comma-separated list of CPUs or CPU ranges to run VCPUs on (e.g. 0,1-3,5)
1955 or colon-separated list of assignments of guest to host CPU assignments (e.g. 0=0:1=1:2=2) (default: no mask)"),
Daniel Verkamp8a72afc2021-03-15 17:55:52 -07001956 Argument::value("cpu-cluster", "CPUSET", "Group the given CPUs into a cluster (default: no clusters)"),
1957 Argument::value("cpu-capacity", "CPU=CAP[,CPU=CAP[,...]]", "Set the relative capacity of the given CPU (default: no capacity)"),
Suleiman Souhlal015c3c12020-10-07 14:15:41 +09001958 Argument::flag("no-smt", "Don't use SMT in the guest"),
Kansho Nishidaab205af2020-08-13 18:17:50 +09001959 Argument::value("rt-cpus", "CPUSET", "Comma-separated list of CPUs or CPU ranges to run VCPUs on. (e.g. 0,1-3,5) (default: none)"),
Zach Reiznerefe95782017-08-26 18:05:48 -07001960 Argument::short_value('m',
1961 "mem",
1962 "N",
1963 "Amount of guest memory in MiB. (default: 256)"),
Sergey Senozhatsky1e369c52021-04-13 20:23:51 +09001964 Argument::flag("hugepages", "Advise the kernel to use Huge Pages for guest memory mappings."),
Zach Reiznerefe95782017-08-26 18:05:48 -07001965 Argument::short_value('r',
1966 "root",
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001967 "PATH[,key=value[,key=value[,...]]",
1968 "Path to a root disk image followed by optional comma-separated options.
1969 Like `--disk` but adds appropriate kernel command line option.
1970 See --disk for valid options."),
1971 Argument::value("rwroot", "PATH[,key=value[,key=value[,...]]", "Path to a writable root disk image followed by optional comma-separated options.
1972 See --disk for valid options."),
1973 Argument::short_value('d', "disk", "PATH[,key=value[,key=value[,...]]", "Path to a disk image followed by optional comma-separated options.
1974 Valid keys:
Daniel Verkamp27672232019-12-06 17:26:55 +11001975 sparse=BOOL - Indicates whether the disk should support the discard operation (default: true)
Daniel Verkamp4e1f99a2020-06-01 12:47:21 -07001976 block_size=BYTES - Set the reported block size of the disk (default: 512)
1977 id=STRING - Set the block device identifier to an ASCII string, up to 20 characters (default: no ID)"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001978 Argument::value("rwdisk", "PATH[,key=value[,key=value[,...]]", "Path to a writable disk image followed by optional comma-separated options.
1979 See --disk for valid options."),
Jakub Starona3411ea2019-04-24 10:55:25 -07001980 Argument::value("rw-pmem-device", "PATH", "Path to a writable disk image."),
1981 Argument::value("pmem-device", "PATH", "Path to a disk image."),
Kansho Nishida282115b2019-12-18 13:13:14 +09001982 Argument::value("pstore", "path=PATH,size=SIZE", "Path to pstore buffer backend file follewed by size."),
Zach Reiznerefe95782017-08-26 18:05:48 -07001983 Argument::value("host_ip",
1984 "IP",
1985 "IP address to assign to host tap interface."),
1986 Argument::value("netmask", "NETMASK", "Netmask for VM subnet."),
1987 Argument::value("mac", "MAC", "MAC address for VM."),
Xiong Zhang773c7072020-03-20 10:39:55 +08001988 Argument::value("net-vq-pairs", "N", "virtio net virtual queue paris. (default: 1)"),
Andrew Scull1590e6f2020-03-18 18:00:47 +00001989 #[cfg(feature = "audio")]
Judy Hsiaod5c1e962020-02-04 12:30:01 +08001990 Argument::value("ac97",
paulhsia83d51602021-03-09 17:13:14 +08001991 "[backend=BACKEND,capture=true,capture_effect=EFFECT,client_type=TYPE,shm-fd=FD,client-fd=FD,server-fd=FD]",
Judy Hsiaod5c1e962020-02-04 12:30:01 +08001992 "Comma separated key=value pairs for setting up Ac97 devices. Can be given more than once .
1993 Possible key values:
Jorge E. Moreira359e7de2020-12-02 18:25:53 -08001994 backend=(null, cras, vios) - Where to route the audio device. If not provided, backend will default to null.
1995 `null` for /dev/null, cras for CRAS server and vios for VioS server.
Judy Hsiaod5c1e962020-02-04 12:30:01 +08001996 capture - Enable audio capture
Jorge E. Moreira359e7de2020-12-02 18:25:53 -08001997 capture_effects - | separated effects to be enabled for recording. The only supported effect value now is EchoCancellation or aec.
paulhsia83d51602021-03-09 17:13:14 +08001998 client_type - Set specific client type for cras backend.
Jorge E. Moreira359e7de2020-12-02 18:25:53 -08001999 server - The to the VIOS server (unix socket)."),
Trent Begin17ccaad2019-04-17 13:51:25 -06002000 Argument::value("serial",
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07002001 "type=TYPE,[hardware=HW,num=NUM,path=PATH,input=PATH,console,earlycon,stdin]",
Jason Macnakcc7070b2019-11-06 14:48:12 -08002002 "Comma separated key=value pairs for setting up serial devices. Can be given more than once.
Trent Begin17ccaad2019-04-17 13:51:25 -06002003 Possible key values:
Jorge E. Moreira9c9e0e72019-05-17 13:57:04 -07002004 type=(stdout,syslog,sink,file) - Where to route the serial device
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07002005 hardware=(serial,virtio-console) - Which type of serial hardware to emulate. Defaults to 8250 UART (serial).
Trent Begin923bab02019-06-17 13:48:06 -06002006 num=(1,2,3,4) - Serial Device Number. If not provided, num will default to 1.
Jorge E. Moreira9c9e0e72019-05-17 13:57:04 -07002007 path=PATH - The path to the file to write to when type=file
Iliyan Malchev2c1417b2020-04-14 09:40:41 -07002008 input=PATH - The path to the file to read from when not stdin
Trent Begin17ccaad2019-04-17 13:51:25 -06002009 console - Use this serial device as the guest console. Can only be given once. Will default to first serial port if not provided.
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07002010 earlycon - Use this serial device as the early console. Can only be given once.
Jorge E. Moreira1e262302019-08-01 14:40:03 -07002011 stdin - Direct standard input to this serial device. Can only be given once. Will default to first serial port if not provided.
Trent Begin17ccaad2019-04-17 13:51:25 -06002012 "),
2013 Argument::value("syslog-tag", "TAG", "When logging to syslog, use the provided tag."),
Zach Reizner0f2cfb02019-06-19 17:46:03 -07002014 Argument::value("x-display", "DISPLAY", "X11 display name to use."),
Zach Reizner65b98f12019-11-22 17:34:58 -08002015 Argument::flag("display-window-keyboard", "Capture keyboard input from the display window."),
2016 Argument::flag("display-window-mouse", "Capture keyboard input from the display window."),
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09002017 Argument::value("wayland-sock", "PATH[,name=NAME]", "Path to the Wayland socket to use. The unnamed one is used for displaying virtual screens. Named ones are only for IPC."),
David Reveman52ba4e52018-04-22 21:42:09 -04002018 #[cfg(feature = "wl-dmabuf")]
2019 Argument::flag("wayland-dmabuf", "Enable support for DMABufs in Wayland device."),
Zach Reiznerefe95782017-08-26 18:05:48 -07002020 Argument::short_value('s',
2021 "socket",
2022 "PATH",
2023 "Path to put the control socket. If PATH is a directory, a name will be generated."),
Dylan Reidd0c9adc2017-10-02 19:04:50 -07002024 Argument::flag("disable-sandbox", "Run all devices in one, non-sandboxed process."),
Chirantan Ekboteebd56812018-04-16 19:32:04 -07002025 Argument::value("cid", "CID", "Context ID for virtual sockets."),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09002026 Argument::value("shared-dir", "PATH:TAG[:type=TYPE:writeback=BOOL:timeout=SECONDS:uidmap=UIDMAP:gidmap=GIDMAP:cache=CACHE]",
2027 "Colon-separated options for configuring a directory to be shared with the VM.
2028The first field is the directory to be shared and the second field is the tag that the VM can use to identify the device.
2029The remaining fields are key=value pairs that may appear in any order. Valid keys are:
2030type=(p9, fs) - Indicates whether the directory should be shared via virtio-9p or virtio-fs (default: p9).
2031uidmap=UIDMAP - The uid map to use for the device's jail in the format \"inner outer count[,inner outer count]\" (default: 0 <current euid> 1).
2032gidmap=GIDMAP - The gid map to use for the device's jail in the format \"inner outer count[,inner outer count]\" (default: 0 <current egid> 1).
2033cache=(never, auto, always) - Indicates whether the VM can cache the contents of the shared directory (default: auto). When set to \"auto\" and the type is \"fs\", the VM will use close-to-open consistency for file contents.
2034timeout=SECONDS - How long the VM should consider file attributes and directory entries to be valid (default: 5). If the VM has exclusive access to the directory, then this should be a large value. If the directory can be modified by other processes, then this should be 0.
2035writeback=BOOL - Indicates whether the VM can use writeback caching (default: false). This is only safe to do when the VM has exclusive access to the files in a directory. Additionally, the server should have read permission for all files as the VM may issue read requests even for files that are opened write-only.
2036"),
Dylan Reide026ef02017-10-02 19:03:52 -07002037 Argument::value("seccomp-policy-dir", "PATH", "Path to seccomp .policy files."),
Zach Reizner44863792019-06-26 14:22:08 -07002038 Argument::flag("seccomp-log-failures", "Instead of seccomp filter failures being fatal, they will be logged instead."),
Zach Reizner8864cb02018-01-16 17:59:03 -08002039 #[cfg(feature = "plugin")]
Zach Reiznercc30d582018-01-23 21:16:42 -08002040 Argument::value("plugin", "PATH", "Absolute path to plugin process to run under crosvm."),
Daniel Verkampbd1a0842019-01-08 15:50:34 -08002041 #[cfg(feature = "plugin")]
Dmitry Torokhov0f1770d2018-05-11 10:57:16 -07002042 Argument::value("plugin-root", "PATH", "Absolute path to a directory that will become root filesystem for the plugin process."),
Daniel Verkampbd1a0842019-01-08 15:50:34 -08002043 #[cfg(feature = "plugin")]
Chirantan Ekboted41d7262018-11-16 16:37:45 -08002044 Argument::value("plugin-mount", "PATH:PATH:BOOL", "Path to be mounted into the plugin's root filesystem. Can be given more than once."),
Dmitry Torokhov1a6262b2019-03-01 00:34:03 -08002045 #[cfg(feature = "plugin")]
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08002046 Argument::value("plugin-mount-file", "PATH", "Path to the file listing paths be mounted into the plugin's root filesystem. Can be given more than once."),
2047 #[cfg(feature = "plugin")]
Dmitry Torokhov1a6262b2019-03-01 00:34:03 -08002048 Argument::value("plugin-gid-map", "GID:GID:INT", "Supplemental GIDs that should be mapped in plugin jail. Can be given more than once."),
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08002049 #[cfg(feature = "plugin")]
2050 Argument::value("plugin-gid-map-file", "PATH", "Path to the file listing supplemental GIDs that should be mapped in plugin jail. Can be given more than once."),
Rob Bradford8f002f52018-02-19 16:31:11 +00002051 Argument::flag("vhost-net", "Use vhost for networking."),
Chirantan Ekbote5f787212018-05-31 15:31:31 -07002052 Argument::value("tap-fd",
2053 "fd",
Jorge E. Moreirab7952802019-02-12 16:43:05 -08002054 "File descriptor for configured tap device. A different virtual network card will be added each time this argument is given."),
Zach Reizner3a8100a2017-09-13 19:15:43 -07002055 #[cfg(feature = "gpu")]
Jason Macnakcc7070b2019-11-06 14:48:12 -08002056 Argument::flag_or_value("gpu",
2057 "[width=INT,height=INT]",
2058 "(EXPERIMENTAL) Comma separated key=value pairs for setting up a virtio-gpu device
2059 Possible key values:
Chia-I Wue25859b2021-04-14 13:57:55 -07002060 backend=(2d|virglrenderer|gfxstream) - Which backend to use for virtio-gpu (determining rendering protocol)
Jason Macnakcc7070b2019-11-06 14:48:12 -08002061 width=INT - The width of the virtual display connected to the virtio-gpu.
2062 height=INT - The height of the virtual display connected to the virtio-gpu.
Chia-I Wue25859b2021-04-14 13:57:55 -07002063 egl[=true|=false] - If the backend should use a EGL context for rendering.
2064 glx[=true|=false] - If the backend should use a GLX context for rendering.
2065 surfaceless[=true|=false] - If the backend should use a surfaceless context for rendering.
2066 angle[=true|=false] - If the gfxstream backend should use ANGLE (OpenGL on Vulkan) as its native OpenGL driver.
Kaiyi Li6404e452020-07-07 19:12:03 -07002067 syncfd[=true|=false] - If the gfxstream backend should support EGL_ANDROID_native_fence_sync
Chia-I Wu6d473b32021-04-12 10:14:24 -07002068 vulkan[=true|=false] - If the backend should support vulkan
Jason Macnakcc7070b2019-11-06 14:48:12 -08002069 "),
Jason Macnakd659a0d2021-03-15 15:33:01 -07002070 #[cfg(feature = "gpu")]
2071 Argument::flag_or_value("gpu-display",
2072 "[width=INT,height=INT]",
2073 "(EXPERIMENTAL) Comma separated key=value pairs for setting up a display on the virtio-gpu device
2074 Possible key values:
2075 width=INT - The width of the virtual display connected to the virtio-gpu.
2076 height=INT - The height of the virtual display connected to the virtio-gpu.
2077 "),
David Tolnay43f8e212019-02-13 17:28:16 -08002078 #[cfg(feature = "tpm")]
2079 Argument::flag("software-tpm", "enable a software emulated trusted platform module device"),
Jorge E. Moreiradffec502019-01-14 18:44:49 -08002080 Argument::value("evdev", "PATH", "Path to an event device node. The device will be grabbed (unusable from the host) and made available to the guest with the same configuration it shows on the host"),
Jorge E. Moreira99d3f082019-03-07 10:59:54 -08002081 Argument::value("single-touch", "PATH:WIDTH:HEIGHT", "Path to a socket from where to read single touch input events (such as those from a touchscreen) and write status updates to, optionally followed by width and height (defaults to 800x1280)."),
Tristan Muntsinger486cffc2020-09-29 22:05:41 +00002082 Argument::value("multi-touch", "PATH:WIDTH:HEIGHT", "Path to a socket from where to read multi touch input events (such as those from a touchscreen) and write status updates to, optionally followed by width and height (defaults to 800x1280)."),
Jorge E. Moreiradffec502019-01-14 18:44:49 -08002083 Argument::value("trackpad", "PATH:WIDTH:HEIGHT", "Path to a socket from where to read trackpad input events and write status updates to, optionally followed by screen width and height (defaults to 800x1280)."),
2084 Argument::value("mouse", "PATH", "Path to a socket from where to read mouse input events and write status updates to."),
2085 Argument::value("keyboard", "PATH", "Path to a socket from where to read keyboard input events and write status updates to."),
Daniel Norman5e23df72021-03-11 10:11:02 -08002086 Argument::value("switches", "PATH", "Path to a socket from where to read switch input events and write status updates to."),
Miriam Zimmerman26ac9282019-01-29 21:21:48 -08002087 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
2088 Argument::flag("split-irqchip", "(EXPERIMENTAL) enable split-irqchip support"),
Cody Schuffelen6d1ab502019-05-21 12:12:38 -07002089 Argument::value("bios", "PATH", "Path to BIOS/firmware ROM"),
Xiong Zhang17b0daf2019-04-23 17:14:50 +08002090 Argument::value("vfio", "PATH", "Path to sysfs of pass through or mdev device"),
Keiichi Watanabe57df6a02019-12-06 22:24:40 +09002091 #[cfg(feature = "video-decoder")]
2092 Argument::flag("video-decoder", "(EXPERIMENTAL) enable virtio-video decoder device"),
2093 #[cfg(feature = "video-encoder")]
2094 Argument::flag("video-encoder", "(EXPERIMENTAL) enable virtio-video encoder device"),
Tomasz Jeznach42644642020-05-20 23:27:59 -07002095 Argument::value("acpi-table", "PATH", "Path to user provided ACPI table"),
Will Deacon6560c182020-10-06 18:47:18 +01002096 Argument::flag("protected-vm", "(EXPERIMENTAL) prevent host access to guest memory"),
Chuanxiao Dongfd5626c2020-04-27 16:35:33 +08002097 Argument::flag_or_value("battery",
2098 "[type=TYPE]",
2099 "Comma separated key=value pairs for setting up battery device
2100 Possible key values:
2101 type=goldfish - type of battery emulation, defaults to goldfish
2102 "),
Keiichi Watanabec5262e92020-10-21 15:57:33 +09002103 Argument::value("gdb", "PORT", "(EXPERIMENTAL) gdb on the given port"),
Charles William Dick0e3d4b62020-12-14 12:16:46 +09002104 Argument::value("balloon_bias_mib", "N", "Amount to bias balance of memory between host and guest as the balloon inflates, in MiB."),
Keiichi Watanabef3a37f42021-01-21 15:41:11 +09002105 Argument::value("vhost-user-blk", "SOCKET_PATH", "Path to a socket for vhost-user block"),
Keiichi Watanabe60686582021-03-12 04:53:51 +09002106 Argument::value("vhost-user-net", "SOCKET_PATH", "Path to a socket for vhost-user net"),
Chirantan Ekbote2ee9dcd2021-05-26 18:21:44 +09002107 Argument::value("vhost-user-wl", "SOCKET_PATH:TUBE_PATH", "Paths to a vhost-user socket for wayland and a Tube socket for additional wayland-specific messages"),
Woody Chow5890b702021-02-12 14:57:02 +09002108 Argument::value("vhost-user-fs", "SOCKET_PATH:TAG",
2109 "Path to a socket path for vhost-user fs, and tag for the shared dir"),
Tomasz Jeznach3ce74762021-02-26 01:01:53 -08002110 #[cfg(feature = "direct")]
2111 Argument::value("direct-pmio", "PATH@RANGE[,RANGE[,...]]", "Path and ranges for direct port I/O access"),
Tomasz Jeznach7271f752021-03-04 01:44:06 -08002112 #[cfg(feature = "direct")]
2113 Argument::value("direct-level-irq", "irq", "Enable interrupt passthrough"),
2114 #[cfg(feature = "direct")]
2115 Argument::value("direct-edge-irq", "irq", "Enable interrupt passthrough"),
Tomasz Jeznachccb26942021-03-30 22:44:11 -07002116 Argument::value("dmi", "DIR", "Directory with smbios_entry_point/DMI files"),
Tomasz Jeznachd93c29f2021-04-12 11:00:24 -07002117 Argument::flag("no-legacy", "Don't use legacy KBD/RTC devices emulation"),
Zach Reiznerefe95782017-08-26 18:05:48 -07002118 Argument::short_flag('h', "help", "Print help message.")];
2119
2120 let mut cfg = Config::default();
Zach Reizner55a9e502018-10-03 10:22:32 -07002121 let match_res = set_arguments(args, &arguments[..], |name, value| {
2122 set_argument(&mut cfg, name, value)
David Tolnay2bac1e72018-12-12 14:33:42 -08002123 })
Kaiyi Libccb4eb2020-02-06 17:53:11 -08002124 .and_then(|_| validate_arguments(&mut cfg));
Zach Reiznerefe95782017-08-26 18:05:48 -07002125
2126 match match_res {
Zach Reizner8864cb02018-01-16 17:59:03 -08002127 #[cfg(feature = "plugin")]
Zach Reizner267f2c82019-07-31 17:07:27 -07002128 Ok(()) if executable_is_plugin(&cfg.executable_path) => {
2129 match crosvm::plugin::run_config(cfg) {
2130 Ok(_) => {
2131 info!("crosvm and plugin have exited normally");
2132 Ok(())
2133 }
2134 Err(e) => {
2135 error!("{}", e);
2136 Err(())
2137 }
Zach Reizner8864cb02018-01-16 17:59:03 -08002138 }
Zach Reizner267f2c82019-07-31 17:07:27 -07002139 }
Michael Hoylee47a5002020-10-15 16:24:13 -07002140 Ok(()) => match platform::run_config(cfg) {
Zach Reizner55a9e502018-10-03 10:22:32 -07002141 Ok(_) => {
2142 info!("crosvm has exited normally");
2143 Ok(())
Zach Reiznerefe95782017-08-26 18:05:48 -07002144 }
Zach Reizner55a9e502018-10-03 10:22:32 -07002145 Err(e) => {
Junichi Uekawac7aa5232020-09-07 14:58:36 +09002146 error!("crosvm has exited with error: {}", e);
Zach Reizner55a9e502018-10-03 10:22:32 -07002147 Err(())
2148 }
2149 },
Dylan Reidbfba9932018-02-05 15:51:59 -08002150 Err(argument::Error::PrintHelp) => {
2151 print_help("crosvm run", "KERNEL", &arguments[..]);
2152 Ok(())
2153 }
Zach Reizner8864cb02018-01-16 17:59:03 -08002154 Err(e) => {
Dmitry Torokhov470b1e72020-01-15 12:46:49 -08002155 error!("{}", e);
Dylan Reidbfba9932018-02-05 15:51:59 -08002156 Err(())
Zach Reizner8864cb02018-01-16 17:59:03 -08002157 }
Zach Reiznerefe95782017-08-26 18:05:48 -07002158 }
2159}
2160
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002161fn stop_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
Zach Reizner78986322019-02-21 20:43:21 -08002162 if args.len() == 0 {
2163 print_help("crosvm stop", "VM_SOCKET...", &[]);
2164 println!("Stops the crosvm instance listening on each `VM_SOCKET` given.");
Jianxun Zhang56497d22019-03-04 14:38:24 -08002165 return Err(());
Zach Reizner78986322019-02-21 20:43:21 -08002166 }
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002167 let socket_path = &args.next().unwrap();
2168 let socket_path = Path::new(&socket_path);
2169 vms_request(&VmRequest::Exit, socket_path)
Zach Reizner78986322019-02-21 20:43:21 -08002170}
2171
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002172fn suspend_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
Zach Reizner78986322019-02-21 20:43:21 -08002173 if args.len() == 0 {
2174 print_help("crosvm suspend", "VM_SOCKET...", &[]);
2175 println!("Suspends the crosvm instance listening on each `VM_SOCKET` given.");
Jianxun Zhang56497d22019-03-04 14:38:24 -08002176 return Err(());
Zach Reizner78986322019-02-21 20:43:21 -08002177 }
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002178 let socket_path = &args.next().unwrap();
2179 let socket_path = Path::new(&socket_path);
2180 vms_request(&VmRequest::Suspend, socket_path)
Zach Reizner78986322019-02-21 20:43:21 -08002181}
2182
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002183fn resume_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
Zach Reizner78986322019-02-21 20:43:21 -08002184 if args.len() == 0 {
2185 print_help("crosvm resume", "VM_SOCKET...", &[]);
2186 println!("Resumes the crosvm instance listening on each `VM_SOCKET` given.");
Jianxun Zhang56497d22019-03-04 14:38:24 -08002187 return Err(());
Zach Reizner78986322019-02-21 20:43:21 -08002188 }
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002189 let socket_path = &args.next().unwrap();
2190 let socket_path = Path::new(&socket_path);
2191 vms_request(&VmRequest::Resume, socket_path)
Zach Reizner78986322019-02-21 20:43:21 -08002192}
2193
2194fn balloon_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
2195 if args.len() < 2 {
2196 print_help("crosvm balloon", "SIZE VM_SOCKET...", &[]);
2197 println!("Set the ballon size of the crosvm instance to `SIZE` bytes.");
Jianxun Zhang56497d22019-03-04 14:38:24 -08002198 return Err(());
Zach Reizner78986322019-02-21 20:43:21 -08002199 }
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09002200 let num_bytes = match args.next().unwrap().parse::<u64>() {
Zach Reizner78986322019-02-21 20:43:21 -08002201 Ok(n) => n,
2202 Err(_) => {
2203 error!("Failed to parse number of bytes");
2204 return Err(());
2205 }
2206 };
2207
Jakub Staron1f828d72019-04-11 12:49:29 -07002208 let command = BalloonControlCommand::Adjust { num_bytes };
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002209 let socket_path = &args.next().unwrap();
2210 let socket_path = Path::new(&socket_path);
2211 vms_request(&VmRequest::BalloonCommand(command), socket_path)
Zach Reizner78986322019-02-21 20:43:21 -08002212}
2213
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002214fn balloon_stats(mut args: std::env::Args) -> std::result::Result<(), ()> {
Charles William Dicked22f6b2020-04-08 11:05:24 +09002215 if args.len() != 1 {
2216 print_help("crosvm balloon_stats", "VM_SOCKET", &[]);
2217 println!("Prints virtio balloon statistics for a `VM_SOCKET`.");
2218 return Err(());
2219 }
2220 let command = BalloonControlCommand::Stats {};
2221 let request = &VmRequest::BalloonCommand(command);
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002222 let socket_path = &args.next().unwrap();
2223 let socket_path = Path::new(&socket_path);
2224 let response = handle_request(request, socket_path)?;
Hikaru Nishidaa0e381b2021-05-24 17:13:45 +09002225 match serde_json::to_string_pretty(&response) {
2226 Ok(response_json) => println!("{}", response_json),
2227 Err(e) => {
2228 error!("Failed to serialize into JSON: {}", e);
2229 return Err(());
2230 }
2231 }
Hikaru Nishida6b51c752021-05-21 12:37:43 +09002232 match response {
2233 VmResponse::BalloonStats { .. } => Ok(()),
2234 _ => Err(()),
2235 }
Charles William Dicked22f6b2020-04-08 11:05:24 +09002236}
2237
A. Cody Schuffelen9ca60392019-12-23 18:27:11 -08002238fn create_qcow2(args: std::env::Args) -> std::result::Result<(), ()> {
2239 let arguments = [
2240 Argument::positional("PATH", "where to create the qcow2 image"),
2241 Argument::positional("[SIZE]", "the expanded size of the image"),
2242 Argument::value(
2243 "backing_file",
2244 "path/to/file",
2245 " the file to back the image",
2246 ),
2247 ];
2248 let mut positional_index = 0;
2249 let mut file_path = String::from("");
2250 let mut size: Option<u64> = None;
2251 let mut backing_file: Option<String> = None;
2252 set_arguments(args, &arguments[..], |name, value| {
2253 match (name, positional_index) {
2254 ("", 0) => {
2255 // NAME
2256 positional_index += 1;
2257 file_path = value.unwrap().to_owned();
2258 }
2259 ("", 1) => {
2260 // [SIZE]
2261 positional_index += 1;
2262 size = Some(value.unwrap().parse::<u64>().map_err(|_| {
2263 argument::Error::InvalidValue {
2264 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08002265 expected: String::from("SIZE should be a nonnegative integer"),
A. Cody Schuffelen9ca60392019-12-23 18:27:11 -08002266 }
2267 })?);
2268 }
2269 ("", _) => {
2270 return Err(argument::Error::TooManyArguments(
2271 "Expected at most 2 positional arguments".to_owned(),
2272 ));
2273 }
2274 ("backing_file", _) => {
2275 backing_file = value.map(|x| x.to_owned());
2276 }
2277 _ => unreachable!(),
2278 };
2279 Ok(())
2280 })
2281 .map_err(|e| {
2282 error!("Unable to parse command line arguments: {}", e);
2283 })?;
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09002284 if file_path.is_empty() || !(size.is_some() ^ backing_file.is_some()) {
A. Cody Schuffelen9ca60392019-12-23 18:27:11 -08002285 print_help("crosvm create_qcow2", "PATH [SIZE]", &arguments);
2286 println!(
2287 "Create a new QCOW2 image at `PATH` of either the specified `SIZE` in bytes or
2288with a '--backing_file'."
2289 );
Jianxun Zhang56497d22019-03-04 14:38:24 -08002290 return Err(());
Dylan Reid2dcb6322018-07-13 10:42:48 -07002291 }
Dylan Reid2dcb6322018-07-13 10:42:48 -07002292
2293 let file = OpenOptions::new()
2294 .create(true)
2295 .read(true)
2296 .write(true)
A. Cody Schuffelen9ca60392019-12-23 18:27:11 -08002297 .truncate(true)
Dylan Reid2dcb6322018-07-13 10:42:48 -07002298 .open(&file_path)
2299 .map_err(|e| {
David Tolnayb4bd00f2019-02-12 17:51:26 -08002300 error!("Failed opening qcow file at '{}': {}", file_path, e);
Dylan Reid2dcb6322018-07-13 10:42:48 -07002301 })?;
2302
A. Cody Schuffelen9ca60392019-12-23 18:27:11 -08002303 match (size, backing_file) {
2304 (Some(size), None) => QcowFile::new(file, size).map_err(|e| {
2305 error!("Failed to create qcow file at '{}': {}", file_path, e);
2306 })?,
2307 (None, Some(backing_file)) => {
2308 QcowFile::new_from_backing(file, &backing_file).map_err(|e| {
2309 error!("Failed to create qcow file at '{}': {}", file_path, e);
2310 })?
2311 }
2312 _ => unreachable!(),
2313 };
Dylan Reid2dcb6322018-07-13 10:42:48 -07002314 Ok(())
2315}
2316
Daniel Verkamp92f73d72018-12-04 13:17:46 -08002317fn disk_cmd(mut args: std::env::Args) -> std::result::Result<(), ()> {
2318 if args.len() < 2 {
2319 print_help("crosvm disk", "SUBCOMMAND VM_SOCKET...", &[]);
2320 println!("Manage attached virtual disk devices.");
2321 println!("Subcommands:");
2322 println!(" resize DISK_INDEX NEW_SIZE VM_SOCKET");
Jianxun Zhang56497d22019-03-04 14:38:24 -08002323 return Err(());
Daniel Verkamp92f73d72018-12-04 13:17:46 -08002324 }
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09002325 let subcommand: &str = &args.next().unwrap();
Daniel Verkamp92f73d72018-12-04 13:17:46 -08002326
2327 let request = match subcommand {
2328 "resize" => {
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09002329 let disk_index = match args.next().unwrap().parse::<usize>() {
Daniel Verkamp92f73d72018-12-04 13:17:46 -08002330 Ok(n) => n,
2331 Err(_) => {
2332 error!("Failed to parse disk index");
2333 return Err(());
2334 }
2335 };
2336
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09002337 let new_size = match args.next().unwrap().parse::<u64>() {
Daniel Verkamp92f73d72018-12-04 13:17:46 -08002338 Ok(n) => n,
2339 Err(_) => {
2340 error!("Failed to parse disk size");
2341 return Err(());
2342 }
2343 };
2344
Jakub Staronecf81e02019-04-11 11:43:39 -07002345 VmRequest::DiskCommand {
Daniel Verkamp92f73d72018-12-04 13:17:46 -08002346 disk_index,
Jakub Staronecf81e02019-04-11 11:43:39 -07002347 command: DiskControlCommand::Resize { new_size },
Daniel Verkamp92f73d72018-12-04 13:17:46 -08002348 }
2349 }
2350 _ => {
2351 error!("Unknown disk subcommand '{}'", subcommand);
2352 return Err(());
2353 }
2354 };
2355
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002356 let socket_path = &args.next().unwrap();
2357 let socket_path = Path::new(&socket_path);
2358 vms_request(&request, socket_path)
Daniel Verkamp92f73d72018-12-04 13:17:46 -08002359}
2360
Jingkui Wang100e6e42019-03-08 20:41:57 -08002361fn parse_bus_id_addr(v: &str) -> ModifyUsbResult<(u8, u8, u16, u16)> {
2362 debug!("parse_bus_id_addr: {}", v);
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09002363 let mut ids = v.split(':');
Jingkui Wang100e6e42019-03-08 20:41:57 -08002364 match (ids.next(), ids.next(), ids.next(), ids.next()) {
2365 (Some(bus_id), Some(addr), Some(vid), Some(pid)) => {
2366 let bus_id = bus_id
2367 .parse::<u8>()
2368 .map_err(|e| ModifyUsbError::ArgParseInt("bus_id", bus_id.to_owned(), e))?;
2369 let addr = addr
2370 .parse::<u8>()
2371 .map_err(|e| ModifyUsbError::ArgParseInt("addr", addr.to_owned(), e))?;
2372 let vid = u16::from_str_radix(&vid, 16)
2373 .map_err(|e| ModifyUsbError::ArgParseInt("vid", vid.to_owned(), e))?;
2374 let pid = u16::from_str_radix(&pid, 16)
2375 .map_err(|e| ModifyUsbError::ArgParseInt("pid", pid.to_owned(), e))?;
2376 Ok((bus_id, addr, vid, pid))
2377 }
2378 _ => Err(ModifyUsbError::ArgParse(
2379 "BUS_ID_ADDR_BUS_NUM_DEV_NUM",
2380 v.to_owned(),
2381 )),
2382 }
2383}
2384
Jingkui Wang100e6e42019-03-08 20:41:57 -08002385fn usb_attach(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
2386 let val = args
2387 .next()
2388 .ok_or(ModifyUsbError::ArgMissing("BUS_ID_ADDR_BUS_NUM_DEV_NUM"))?;
2389 let (bus, addr, vid, pid) = parse_bus_id_addr(&val)?;
2390 let dev_path = PathBuf::from(
2391 args.next()
2392 .ok_or(ModifyUsbError::ArgMissing("usb device path"))?,
2393 );
Jingkui Wang100e6e42019-03-08 20:41:57 -08002394
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002395 let socket_path = args
2396 .next()
2397 .ok_or(ModifyUsbError::ArgMissing("control socket path"))?;
2398 let socket_path = Path::new(&socket_path);
2399
2400 do_usb_attach(&socket_path, bus, addr, vid, pid, &dev_path)
Jingkui Wang100e6e42019-03-08 20:41:57 -08002401}
2402
2403fn usb_detach(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
2404 let port: u8 = args
2405 .next()
2406 .map_or(Err(ModifyUsbError::ArgMissing("PORT")), |p| {
2407 p.parse::<u8>()
2408 .map_err(|e| ModifyUsbError::ArgParseInt("PORT", p.to_owned(), e))
2409 })?;
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002410 let socket_path = args
2411 .next()
2412 .ok_or(ModifyUsbError::ArgMissing("control socket path"))?;
2413 let socket_path = Path::new(&socket_path);
2414 do_usb_detach(&socket_path, port)
Jingkui Wang100e6e42019-03-08 20:41:57 -08002415}
2416
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002417fn usb_list(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
2418 let socket_path = args
2419 .next()
2420 .ok_or(ModifyUsbError::ArgMissing("control socket path"))?;
2421 let socket_path = Path::new(&socket_path);
2422 do_usb_list(&socket_path)
Jingkui Wang100e6e42019-03-08 20:41:57 -08002423}
2424
2425fn modify_usb(mut args: std::env::Args) -> std::result::Result<(), ()> {
Zach Reizneraff94ca2019-03-18 20:58:31 -07002426 if args.len() < 2 {
Jingkui Wang100e6e42019-03-08 20:41:57 -08002427 print_help("crosvm usb",
Zach Reizneraff94ca2019-03-18 20:58:31 -07002428 "[attach BUS_ID:ADDR:VENDOR_ID:PRODUCT_ID [USB_DEVICE_PATH|-] | detach PORT | list] VM_SOCKET...", &[]);
Jingkui Wang100e6e42019-03-08 20:41:57 -08002429 return Err(());
2430 }
2431
2432 // This unwrap will not panic because of the above length check.
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002433 let command = &args.next().unwrap();
Jingkui Wang100e6e42019-03-08 20:41:57 -08002434 let result = match command.as_ref() {
2435 "attach" => usb_attach(args),
2436 "detach" => usb_detach(args),
2437 "list" => usb_list(args),
2438 other => Err(ModifyUsbError::UnknownCommand(other.to_owned())),
2439 };
2440 match result {
2441 Ok(response) => {
2442 println!("{}", response);
2443 Ok(())
2444 }
2445 Err(e) => {
2446 println!("error {}", e);
2447 Err(())
2448 }
2449 }
2450}
2451
Zach Reiznerefe95782017-08-26 18:05:48 -07002452fn print_usage() {
Junichi Uekawaf0a77232021-04-08 16:44:44 +09002453 print_help("crosvm", "[command]", &[]);
Zach Reiznerefe95782017-08-26 18:05:48 -07002454 println!("Commands:");
Junichi Uekawaf0a77232021-04-08 16:44:44 +09002455 println!(" balloon - Set balloon size of the crosvm instance.");
2456 println!(" balloon_stats - Prints virtio balloon statistics.");
2457 println!(" battery - Modify battery.");
Dylan Reid2dcb6322018-07-13 10:42:48 -07002458 println!(" create_qcow2 - Create a new qcow2 disk image file.");
Jingkui Wang100e6e42019-03-08 20:41:57 -08002459 println!(" disk - Manage attached virtual disk devices.");
Junichi Uekawaf0a77232021-04-08 16:44:44 +09002460 println!(" resume - Resumes the crosvm instance.");
2461 println!(" run - Start a new crosvm instance.");
2462 println!(" stop - Stops crosvm instances via their control sockets.");
2463 println!(" suspend - Suspends the crosvm instance.");
Jingkui Wang100e6e42019-03-08 20:41:57 -08002464 println!(" usb - Manage attached virtual USB devices.");
Yi Sun54305cd2020-01-04 00:19:37 +08002465 println!(" version - Show package version.");
2466}
2467
Zach Reiznerd09392e2021-03-12 12:03:48 -08002468#[allow(clippy::unnecessary_wraps)]
Yi Sun54305cd2020-01-04 00:19:37 +08002469fn pkg_version() -> std::result::Result<(), ()> {
2470 const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION");
2471 const PKG_VERSION: Option<&'static str> = option_env!("PKG_VERSION");
2472
2473 print!("crosvm {}", VERSION.unwrap_or("UNKNOWN"));
2474 match PKG_VERSION {
2475 Some(v) => println!("-{}", v),
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09002476 None => println!(),
Yi Sun54305cd2020-01-04 00:19:37 +08002477 }
2478 Ok(())
Zach Reiznerefe95782017-08-26 18:05:48 -07002479}
2480
Chuanxiao Dong256be3a2020-04-27 16:39:33 +08002481fn modify_battery(mut args: std::env::Args) -> std::result::Result<(), ()> {
2482 if args.len() < 4 {
2483 print_help("crosvm battery BATTERY_TYPE ",
2484 "[status STATUS | present PRESENT | health HEALTH | capacity CAPACITY | aconline ACONLINE ] VM_SOCKET...", &[]);
2485 return Err(());
2486 }
2487
2488 // This unwrap will not panic because of the above length check.
2489 let battery_type = args.next().unwrap();
2490 let property = args.next().unwrap();
2491 let target = args.next().unwrap();
2492
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002493 let socket_path = args.next().unwrap();
2494 let socket_path = Path::new(&socket_path);
Chuanxiao Dong256be3a2020-04-27 16:39:33 +08002495
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002496 do_modify_battery(&socket_path, &*battery_type, &*property, &*target)
Chuanxiao Dong256be3a2020-04-27 16:39:33 +08002497}
2498
Dylan Reidbfba9932018-02-05 15:51:59 -08002499fn crosvm_main() -> std::result::Result<(), ()> {
Stephen Barber56fbf092017-06-29 16:12:14 -07002500 if let Err(e) = syslog::init() {
David Tolnayb4bd00f2019-02-12 17:51:26 -08002501 println!("failed to initialize syslog: {}", e);
Dylan Reidbfba9932018-02-05 15:51:59 -08002502 return Err(());
Stephen Barber56fbf092017-06-29 16:12:14 -07002503 }
Zach Reizner639d9672017-05-01 17:57:18 -07002504
Zach Reiznerb3fa5c92019-01-28 14:05:23 -08002505 panic_hook::set_panic_hook();
2506
Zach Reiznerefe95782017-08-26 18:05:48 -07002507 let mut args = std::env::args();
2508 if args.next().is_none() {
2509 error!("expected executable name");
Dylan Reidbfba9932018-02-05 15:51:59 -08002510 return Err(());
Zach Reizner639d9672017-05-01 17:57:18 -07002511 }
Zach Reiznerefe95782017-08-26 18:05:48 -07002512
Zach Reizner8864cb02018-01-16 17:59:03 -08002513 // Past this point, usage of exit is in danger of leaking zombie processes.
Dylan Reidbfba9932018-02-05 15:51:59 -08002514 let ret = match args.next().as_ref().map(|a| a.as_ref()) {
2515 None => {
2516 print_usage();
2517 Ok(())
2518 }
Zach Reizner55a9e502018-10-03 10:22:32 -07002519 Some("stop") => stop_vms(args),
Zach Reizner6a8fdd92019-01-16 14:38:41 -08002520 Some("suspend") => suspend_vms(args),
2521 Some("resume") => resume_vms(args),
Zach Reizner55a9e502018-10-03 10:22:32 -07002522 Some("run") => run_vm(args),
2523 Some("balloon") => balloon_vms(args),
Charles William Dicked22f6b2020-04-08 11:05:24 +09002524 Some("balloon_stats") => balloon_stats(args),
Zach Reizner55a9e502018-10-03 10:22:32 -07002525 Some("create_qcow2") => create_qcow2(args),
Daniel Verkamp92f73d72018-12-04 13:17:46 -08002526 Some("disk") => disk_cmd(args),
Jingkui Wang100e6e42019-03-08 20:41:57 -08002527 Some("usb") => modify_usb(args),
Yi Sun54305cd2020-01-04 00:19:37 +08002528 Some("version") => pkg_version(),
Chuanxiao Dong256be3a2020-04-27 16:39:33 +08002529 Some("battery") => modify_battery(args),
Zach Reiznerefe95782017-08-26 18:05:48 -07002530 Some(c) => {
2531 println!("invalid subcommand: {:?}", c);
2532 print_usage();
Dylan Reidbfba9932018-02-05 15:51:59 -08002533 Err(())
Zach Reiznerefe95782017-08-26 18:05:48 -07002534 }
Dylan Reidbfba9932018-02-05 15:51:59 -08002535 };
Zach Reiznerefe95782017-08-26 18:05:48 -07002536
2537 // Reap exit status from any child device processes. At this point, all devices should have been
2538 // dropped in the main process and told to shutdown. Try over a period of 100ms, since it may
2539 // take some time for the processes to shut down.
2540 if !wait_all_children() {
2541 // We gave them a chance, and it's too late.
2542 warn!("not all child processes have exited; sending SIGKILL");
2543 if let Err(e) = kill_process_group() {
2544 // We're now at the mercy of the OS to clean up after us.
David Tolnayb4bd00f2019-02-12 17:51:26 -08002545 warn!("unable to kill all child processes: {}", e);
Zach Reiznerefe95782017-08-26 18:05:48 -07002546 }
2547 }
2548
2549 // WARNING: Any code added after this point is not guaranteed to run
2550 // since we may forcibly kill this process (and its children) above.
Dylan Reidbfba9932018-02-05 15:51:59 -08002551 ret
2552}
2553
2554fn main() {
2555 std::process::exit(if crosvm_main().is_ok() { 0 } else { 1 });
Zach Reizner639d9672017-05-01 17:57:18 -07002556}
Daniel Verkamp107edb32019-04-05 09:58:48 -07002557
2558#[cfg(test)]
2559mod tests {
2560 use super::*;
Kaiyi Libccb4eb2020-02-06 17:53:11 -08002561 use crosvm::{DEFAULT_TOUCH_DEVICE_HEIGHT, DEFAULT_TOUCH_DEVICE_WIDTH};
Daniel Verkamp107edb32019-04-05 09:58:48 -07002562
2563 #[test]
2564 fn parse_cpu_set_single() {
2565 assert_eq!(parse_cpu_set("123").expect("parse failed"), vec![123]);
2566 }
2567
2568 #[test]
2569 fn parse_cpu_set_list() {
2570 assert_eq!(
2571 parse_cpu_set("0,1,2,3").expect("parse failed"),
2572 vec![0, 1, 2, 3]
2573 );
2574 }
2575
2576 #[test]
2577 fn parse_cpu_set_range() {
2578 assert_eq!(
2579 parse_cpu_set("0-3").expect("parse failed"),
2580 vec![0, 1, 2, 3]
2581 );
2582 }
2583
2584 #[test]
2585 fn parse_cpu_set_list_of_ranges() {
2586 assert_eq!(
2587 parse_cpu_set("3-4,7-9,18").expect("parse failed"),
2588 vec![3, 4, 7, 8, 9, 18]
2589 );
2590 }
2591
2592 #[test]
2593 fn parse_cpu_set_repeated() {
2594 // For now, allow duplicates - they will be handled gracefully by the vec to cpu_set_t conversion.
2595 assert_eq!(parse_cpu_set("1,1,1").expect("parse failed"), vec![1, 1, 1]);
2596 }
2597
2598 #[test]
2599 fn parse_cpu_set_negative() {
2600 // Negative CPU numbers are not allowed.
2601 parse_cpu_set("-3").expect_err("parse should have failed");
2602 }
2603
2604 #[test]
2605 fn parse_cpu_set_reverse_range() {
2606 // Ranges must be from low to high.
2607 parse_cpu_set("5-2").expect_err("parse should have failed");
2608 }
2609
2610 #[test]
2611 fn parse_cpu_set_open_range() {
2612 parse_cpu_set("3-").expect_err("parse should have failed");
2613 }
2614
2615 #[test]
2616 fn parse_cpu_set_extra_comma() {
2617 parse_cpu_set("0,1,2,").expect_err("parse should have failed");
2618 }
Trent Begin17ccaad2019-04-17 13:51:25 -06002619
Daniel Verkampc677fb42020-09-08 13:47:49 -07002620 #[test]
2621 fn parse_cpu_affinity_global() {
2622 assert_eq!(
2623 parse_cpu_affinity("0,5-7,9").expect("parse failed"),
2624 VcpuAffinity::Global(vec![0, 5, 6, 7, 9]),
2625 );
2626 }
2627
2628 #[test]
2629 fn parse_cpu_affinity_per_vcpu_one_to_one() {
2630 let mut expected_map = BTreeMap::new();
2631 expected_map.insert(0, vec![0]);
2632 expected_map.insert(1, vec![1]);
2633 expected_map.insert(2, vec![2]);
2634 expected_map.insert(3, vec![3]);
2635 assert_eq!(
2636 parse_cpu_affinity("0=0:1=1:2=2:3=3").expect("parse failed"),
2637 VcpuAffinity::PerVcpu(expected_map),
2638 );
2639 }
2640
2641 #[test]
2642 fn parse_cpu_affinity_per_vcpu_sets() {
2643 let mut expected_map = BTreeMap::new();
2644 expected_map.insert(0, vec![0, 1, 2]);
2645 expected_map.insert(1, vec![3, 4, 5]);
2646 expected_map.insert(2, vec![6, 7, 8]);
2647 assert_eq!(
2648 parse_cpu_affinity("0=0,1,2:1=3-5:2=6,7-8").expect("parse failed"),
2649 VcpuAffinity::PerVcpu(expected_map),
2650 );
2651 }
2652
Andrew Scull1590e6f2020-03-18 18:00:47 +00002653 #[cfg(feature = "audio")]
Trent Begin17ccaad2019-04-17 13:51:25 -06002654 #[test]
Judy Hsiaod5c1e962020-02-04 12:30:01 +08002655 fn parse_ac97_vaild() {
2656 parse_ac97_options("backend=cras").expect("parse should have succeded");
2657 }
2658
Andrew Scull1590e6f2020-03-18 18:00:47 +00002659 #[cfg(feature = "audio")]
Judy Hsiaod5c1e962020-02-04 12:30:01 +08002660 #[test]
2661 fn parse_ac97_null_vaild() {
2662 parse_ac97_options("backend=null").expect("parse should have succeded");
2663 }
2664
Andrew Scull1590e6f2020-03-18 18:00:47 +00002665 #[cfg(feature = "audio")]
Judy Hsiaod5c1e962020-02-04 12:30:01 +08002666 #[test]
Judy Hsiaob4b94c72020-09-07 15:56:00 +08002667 fn parse_ac97_capture_vaild() {
2668 parse_ac97_options("backend=cras,capture=true").expect("parse should have succeded");
Judy Hsiaod5c1e962020-02-04 12:30:01 +08002669 }
2670
Jorge E. Moreira359e7de2020-12-02 18:25:53 -08002671 #[cfg(feature = "audio")]
2672 #[test]
paulhsia83d51602021-03-09 17:13:14 +08002673 fn parse_ac97_client_type() {
2674 parse_ac97_options("backend=cras,capture=true,client_type=crosvm")
2675 .expect("parse should have succeded");
2676 parse_ac97_options("backend=cras,capture=true,client_type=arcvm")
2677 .expect("parse should have succeded");
2678 parse_ac97_options("backend=cras,capture=true,client_type=none")
2679 .expect_err("parse should have failed");
2680 }
2681
2682 #[cfg(feature = "audio")]
2683 #[test]
Jorge E. Moreira359e7de2020-12-02 18:25:53 -08002684 fn parse_ac97_vios_valid() {
2685 parse_ac97_options("backend=vios,server=/path/to/server")
2686 .expect("parse should have succeded");
2687 }
2688
Judy Hsiaod5c1e962020-02-04 12:30:01 +08002689 #[test]
Trent Begin17ccaad2019-04-17 13:51:25 -06002690 fn parse_serial_vaild() {
Jorge E. Moreira1e262302019-08-01 14:40:03 -07002691 parse_serial_options("type=syslog,num=1,console=true,stdin=true")
2692 .expect("parse should have succeded");
Trent Begin17ccaad2019-04-17 13:51:25 -06002693 }
2694
2695 #[test]
A. Cody Schuffelen3faab5a2020-10-05 17:29:16 -07002696 fn parse_serial_virtio_console_vaild() {
2697 parse_serial_options("type=syslog,num=5,console=true,stdin=true,hardware=virtio-console")
2698 .expect("parse should have succeded");
2699 }
2700
2701 #[test]
Trent Begin923bab02019-06-17 13:48:06 -06002702 fn parse_serial_valid_no_num() {
2703 parse_serial_options("type=syslog").expect("parse should have succeded");
2704 }
2705
2706 #[test]
Nicholas Hollingumc76c2da2020-09-18 15:53:16 +10002707 fn parse_serial_equals_in_value() {
2708 let parsed = parse_serial_options("type=syslog,path=foo=bar==.log")
2709 .expect("parse should have succeded");
2710 assert_eq!(parsed.path, Some(PathBuf::from("foo=bar==.log")));
2711 }
2712
2713 #[test]
Trent Begin17ccaad2019-04-17 13:51:25 -06002714 fn parse_serial_invalid_type() {
2715 parse_serial_options("type=wormhole,num=1").expect_err("parse should have failed");
2716 }
2717
2718 #[test]
2719 fn parse_serial_invalid_num_upper() {
2720 parse_serial_options("type=syslog,num=5").expect_err("parse should have failed");
2721 }
2722
2723 #[test]
2724 fn parse_serial_invalid_num_lower() {
2725 parse_serial_options("type=syslog,num=0").expect_err("parse should have failed");
2726 }
2727
2728 #[test]
A. Cody Schuffelen3faab5a2020-10-05 17:29:16 -07002729 fn parse_serial_virtio_console_invalid_num_lower() {
2730 parse_serial_options("type=syslog,hardware=virtio-console,num=0")
2731 .expect_err("parse should have failed");
2732 }
2733
2734 #[test]
Trent Begin17ccaad2019-04-17 13:51:25 -06002735 fn parse_serial_invalid_num_string() {
2736 parse_serial_options("type=syslog,num=number3").expect_err("parse should have failed");
2737 }
2738
2739 #[test]
2740 fn parse_serial_invalid_option() {
2741 parse_serial_options("type=syslog,speed=lightspeed").expect_err("parse should have failed");
2742 }
Jorge E. Moreira1e262302019-08-01 14:40:03 -07002743
2744 #[test]
2745 fn parse_serial_invalid_two_stdin() {
2746 let mut config = Config::default();
2747 set_argument(&mut config, "serial", Some("num=1,type=stdout,stdin=true"))
2748 .expect("should parse the first serial argument");
2749 set_argument(&mut config, "serial", Some("num=2,type=stdout,stdin=true"))
2750 .expect_err("should fail to parse a second serial port connected to stdin");
2751 }
Dmitry Torokhov458bb642019-12-13 11:47:52 -08002752
2753 #[test]
2754 fn parse_plugin_mount_valid() {
2755 let mut config = Config::default();
2756 set_argument(
2757 &mut config,
2758 "plugin-mount",
2759 Some("/dev/null:/dev/zero:true"),
2760 )
2761 .expect("parse should succeed");
2762 assert_eq!(config.plugin_mounts[0].src, PathBuf::from("/dev/null"));
2763 assert_eq!(config.plugin_mounts[0].dst, PathBuf::from("/dev/zero"));
2764 assert_eq!(config.plugin_mounts[0].writable, true);
2765 }
2766
2767 #[test]
2768 fn parse_plugin_mount_valid_shorthand() {
2769 let mut config = Config::default();
2770 set_argument(&mut config, "plugin-mount", Some("/dev/null")).expect("parse should succeed");
2771 assert_eq!(config.plugin_mounts[0].dst, PathBuf::from("/dev/null"));
2772 assert_eq!(config.plugin_mounts[0].writable, false);
2773 set_argument(&mut config, "plugin-mount", Some("/dev/null:/dev/zero"))
2774 .expect("parse should succeed");
2775 assert_eq!(config.plugin_mounts[1].dst, PathBuf::from("/dev/zero"));
2776 assert_eq!(config.plugin_mounts[1].writable, false);
2777 set_argument(&mut config, "plugin-mount", Some("/dev/null::true"))
2778 .expect("parse should succeed");
2779 assert_eq!(config.plugin_mounts[2].dst, PathBuf::from("/dev/null"));
2780 assert_eq!(config.plugin_mounts[2].writable, true);
2781 }
2782
2783 #[test]
2784 fn parse_plugin_mount_invalid() {
2785 let mut config = Config::default();
2786 set_argument(&mut config, "plugin-mount", Some("")).expect_err("parse should fail");
2787 set_argument(
2788 &mut config,
2789 "plugin-mount",
2790 Some("/dev/null:/dev/null:true:false"),
2791 )
2792 .expect_err("parse should fail because too many arguments");
2793 set_argument(&mut config, "plugin-mount", Some("null:/dev/null:true"))
2794 .expect_err("parse should fail because source is not absolute");
2795 set_argument(&mut config, "plugin-mount", Some("/dev/null:null:true"))
2796 .expect_err("parse should fail because source is not absolute");
2797 set_argument(&mut config, "plugin-mount", Some("/dev/null:null:blah"))
2798 .expect_err("parse should fail because flag is not boolean");
2799 }
2800
2801 #[test]
2802 fn parse_plugin_gid_map_valid() {
2803 let mut config = Config::default();
2804 set_argument(&mut config, "plugin-gid-map", Some("1:2:3")).expect("parse should succeed");
2805 assert_eq!(config.plugin_gid_maps[0].inner, 1);
2806 assert_eq!(config.plugin_gid_maps[0].outer, 2);
2807 assert_eq!(config.plugin_gid_maps[0].count, 3);
2808 }
2809
2810 #[test]
2811 fn parse_plugin_gid_map_valid_shorthand() {
2812 let mut config = Config::default();
2813 set_argument(&mut config, "plugin-gid-map", Some("1")).expect("parse should succeed");
2814 assert_eq!(config.plugin_gid_maps[0].inner, 1);
2815 assert_eq!(config.plugin_gid_maps[0].outer, 1);
2816 assert_eq!(config.plugin_gid_maps[0].count, 1);
2817 set_argument(&mut config, "plugin-gid-map", Some("1:2")).expect("parse should succeed");
2818 assert_eq!(config.plugin_gid_maps[1].inner, 1);
2819 assert_eq!(config.plugin_gid_maps[1].outer, 2);
2820 assert_eq!(config.plugin_gid_maps[1].count, 1);
2821 set_argument(&mut config, "plugin-gid-map", Some("1::3")).expect("parse should succeed");
2822 assert_eq!(config.plugin_gid_maps[2].inner, 1);
2823 assert_eq!(config.plugin_gid_maps[2].outer, 1);
2824 assert_eq!(config.plugin_gid_maps[2].count, 3);
2825 }
2826
2827 #[test]
2828 fn parse_plugin_gid_map_invalid() {
2829 let mut config = Config::default();
2830 set_argument(&mut config, "plugin-gid-map", Some("")).expect_err("parse should fail");
2831 set_argument(&mut config, "plugin-gid-map", Some("1:2:3:4"))
2832 .expect_err("parse should fail because too many arguments");
2833 set_argument(&mut config, "plugin-gid-map", Some("blah:2:3"))
2834 .expect_err("parse should fail because inner is not a number");
2835 set_argument(&mut config, "plugin-gid-map", Some("1:blah:3"))
2836 .expect_err("parse should fail because outer is not a number");
2837 set_argument(&mut config, "plugin-gid-map", Some("1:2:blah"))
2838 .expect_err("parse should fail because count is not a number");
2839 }
Kaiyi Libccb4eb2020-02-06 17:53:11 -08002840
2841 #[test]
2842 fn single_touch_spec_and_track_pad_spec_default_size() {
2843 let mut config = Config::default();
2844 config
2845 .executable_path
2846 .replace(Executable::Kernel(PathBuf::from("kernel")));
2847 set_argument(&mut config, "single-touch", Some("/dev/single-touch-test")).unwrap();
2848 set_argument(&mut config, "trackpad", Some("/dev/single-touch-test")).unwrap();
2849 validate_arguments(&mut config).unwrap();
2850 assert_eq!(
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07002851 config.virtio_single_touch.first().unwrap().get_size(),
Kaiyi Libccb4eb2020-02-06 17:53:11 -08002852 (DEFAULT_TOUCH_DEVICE_WIDTH, DEFAULT_TOUCH_DEVICE_HEIGHT)
2853 );
2854 assert_eq!(
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07002855 config.virtio_trackpad.first().unwrap().get_size(),
Kaiyi Libccb4eb2020-02-06 17:53:11 -08002856 (DEFAULT_TOUCH_DEVICE_WIDTH, DEFAULT_TOUCH_DEVICE_HEIGHT)
2857 );
2858 }
2859
2860 #[cfg(feature = "gpu")]
2861 #[test]
2862 fn single_touch_spec_default_size_from_gpu() {
2863 let width = 12345u32;
2864 let height = 54321u32;
2865 let mut config = Config::default();
2866 config
2867 .executable_path
2868 .replace(Executable::Kernel(PathBuf::from("kernel")));
2869 set_argument(&mut config, "single-touch", Some("/dev/single-touch-test")).unwrap();
2870 set_argument(
2871 &mut config,
2872 "gpu",
2873 Some(&format!("width={},height={}", width, height)),
2874 )
2875 .unwrap();
2876 validate_arguments(&mut config).unwrap();
2877 assert_eq!(
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07002878 config.virtio_single_touch.first().unwrap().get_size(),
Kaiyi Libccb4eb2020-02-06 17:53:11 -08002879 (width, height)
2880 );
2881 }
2882
2883 #[test]
2884 fn single_touch_spec_and_track_pad_spec_with_size() {
2885 let width = 12345u32;
2886 let height = 54321u32;
2887 let mut config = Config::default();
2888 config
2889 .executable_path
2890 .replace(Executable::Kernel(PathBuf::from("kernel")));
2891 set_argument(
2892 &mut config,
2893 "single-touch",
2894 Some(&format!("/dev/single-touch-test:{}:{}", width, height)),
2895 )
2896 .unwrap();
2897 set_argument(
2898 &mut config,
2899 "trackpad",
2900 Some(&format!("/dev/single-touch-test:{}:{}", width, height)),
2901 )
2902 .unwrap();
2903 validate_arguments(&mut config).unwrap();
2904 assert_eq!(
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07002905 config.virtio_single_touch.first().unwrap().get_size(),
Kaiyi Libccb4eb2020-02-06 17:53:11 -08002906 (width, height)
2907 );
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07002908 assert_eq!(
2909 config.virtio_trackpad.first().unwrap().get_size(),
2910 (width, height)
2911 );
Kaiyi Libccb4eb2020-02-06 17:53:11 -08002912 }
2913
2914 #[cfg(feature = "gpu")]
2915 #[test]
2916 fn single_touch_spec_with_size_independent_from_gpu() {
2917 let touch_width = 12345u32;
2918 let touch_height = 54321u32;
2919 let display_width = 1234u32;
2920 let display_height = 5432u32;
2921 let mut config = Config::default();
2922 config
2923 .executable_path
2924 .replace(Executable::Kernel(PathBuf::from("kernel")));
2925 set_argument(
2926 &mut config,
2927 "single-touch",
2928 Some(&format!(
2929 "/dev/single-touch-test:{}:{}",
2930 touch_width, touch_height
2931 )),
2932 )
2933 .unwrap();
2934 set_argument(
2935 &mut config,
2936 "gpu",
2937 Some(&format!(
2938 "width={},height={}",
2939 display_width, display_height
2940 )),
2941 )
2942 .unwrap();
2943 validate_arguments(&mut config).unwrap();
2944 assert_eq!(
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07002945 config.virtio_single_touch.first().unwrap().get_size(),
Kaiyi Libccb4eb2020-02-06 17:53:11 -08002946 (touch_width, touch_height)
2947 );
2948 }
Kaiyi Lidd348a42020-07-13 11:49:46 -07002949
Daniel Norman5e23df72021-03-11 10:11:02 -08002950 #[test]
2951 fn virtio_switches() {
2952 let mut config = Config::default();
2953 config
2954 .executable_path
2955 .replace(Executable::Kernel(PathBuf::from("kernel")));
2956 set_argument(&mut config, "switches", Some("/dev/switches-test")).unwrap();
2957 validate_arguments(&mut config).unwrap();
2958 assert_eq!(
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07002959 config.virtio_switches.pop().unwrap(),
Keiichi Watanabed56a2f42021-03-18 20:16:23 +09002960 PathBuf::from("/dev/switches-test")
2961 );
Daniel Norman5e23df72021-03-11 10:11:02 -08002962 }
2963
Chia-I Wu6d473b32021-04-12 10:14:24 -07002964 #[cfg(feature = "gpu")]
Kaiyi Lidd348a42020-07-13 11:49:46 -07002965 #[test]
Chia-I Wu91df6562021-04-12 09:47:38 -07002966 fn parse_gpu_options_default_vulkan_support() {
Jason Macnakd659a0d2021-03-15 15:33:01 -07002967 {
2968 let mut gpu_params: GpuParameters = Default::default();
2969 assert!(parse_gpu_options(Some("backend=virglrenderer"), &mut gpu_params).is_ok());
2970 assert!(!gpu_params.use_vulkan);
2971 }
Chia-I Wu6d473b32021-04-12 10:14:24 -07002972
2973 #[cfg(feature = "gfxstream")]
Jason Macnakd659a0d2021-03-15 15:33:01 -07002974 {
2975 let mut gpu_params: GpuParameters = Default::default();
2976 assert!(parse_gpu_options(Some("backend=gfxstream"), &mut gpu_params).is_ok());
2977 assert!(gpu_params.use_vulkan);
2978 }
Chia-I Wu91df6562021-04-12 09:47:38 -07002979 }
2980
Chia-I Wu6d473b32021-04-12 10:14:24 -07002981 #[cfg(feature = "gpu")]
Chia-I Wu91df6562021-04-12 09:47:38 -07002982 #[test]
Chia-I Wu6d473b32021-04-12 10:14:24 -07002983 fn parse_gpu_options_with_vulkan_specified() {
Jason Macnakd659a0d2021-03-15 15:33:01 -07002984 {
2985 let mut gpu_params: GpuParameters = Default::default();
2986 assert!(parse_gpu_options(Some("vulkan=true"), &mut gpu_params).is_ok());
2987 assert!(gpu_params.use_vulkan);
2988 }
2989 {
2990 let mut gpu_params: GpuParameters = Default::default();
2991 assert!(
2992 parse_gpu_options(Some("backend=virglrenderer,vulkan=true"), &mut gpu_params)
2993 .is_ok()
2994 );
2995 assert!(gpu_params.use_vulkan);
2996 }
2997 {
2998 let mut gpu_params: GpuParameters = Default::default();
2999 assert!(
3000 parse_gpu_options(Some("vulkan=true,backend=virglrenderer"), &mut gpu_params)
3001 .is_ok()
3002 );
3003 assert!(gpu_params.use_vulkan);
3004 }
3005 {
3006 let mut gpu_params: GpuParameters = Default::default();
3007 assert!(parse_gpu_options(Some("vulkan=false"), &mut gpu_params).is_ok());
3008 assert!(!gpu_params.use_vulkan);
3009 }
3010 {
3011 let mut gpu_params: GpuParameters = Default::default();
3012 assert!(
3013 parse_gpu_options(Some("backend=virglrenderer,vulkan=false"), &mut gpu_params)
3014 .is_ok()
3015 );
3016 assert!(!gpu_params.use_vulkan);
3017 }
3018 {
3019 let mut gpu_params: GpuParameters = Default::default();
3020 assert!(
3021 parse_gpu_options(Some("vulkan=false,backend=virglrenderer"), &mut gpu_params)
3022 .is_ok()
3023 );
3024 assert!(!gpu_params.use_vulkan);
3025 }
3026 {
3027 let mut gpu_params: GpuParameters = Default::default();
3028 assert!(parse_gpu_options(
3029 Some("backend=virglrenderer,vulkan=invalid_value"),
3030 &mut gpu_params
3031 )
3032 .is_err());
3033 }
3034 {
3035 let mut gpu_params: GpuParameters = Default::default();
3036 assert!(parse_gpu_options(
3037 Some("vulkan=invalid_value,backend=virglrenderer"),
3038 &mut gpu_params
3039 )
3040 .is_err());
3041 }
Kaiyi Lidd348a42020-07-13 11:49:46 -07003042 }
3043
3044 #[cfg(all(feature = "gpu", feature = "gfxstream"))]
3045 #[test]
3046 fn parse_gpu_options_gfxstream_with_syncfd_specified() {
Jason Macnakd659a0d2021-03-15 15:33:01 -07003047 {
3048 let mut gpu_params: GpuParameters = Default::default();
3049 assert!(
3050 parse_gpu_options(Some("backend=gfxstream,syncfd=true"), &mut gpu_params).is_ok()
3051 );
3052 assert!(gpu_params.gfxstream_use_syncfd);
3053 }
3054 {
3055 let mut gpu_params: GpuParameters = Default::default();
3056 assert!(
3057 parse_gpu_options(Some("syncfd=true,backend=gfxstream"), &mut gpu_params).is_ok()
3058 );
3059 assert!(gpu_params.gfxstream_use_syncfd);
3060 }
3061 {
3062 let mut gpu_params: GpuParameters = Default::default();
3063 assert!(
3064 parse_gpu_options(Some("backend=gfxstream,syncfd=false"), &mut gpu_params).is_ok()
3065 );
3066 assert!(!gpu_params.gfxstream_use_syncfd);
3067 }
3068 {
3069 let mut gpu_params: GpuParameters = Default::default();
3070 assert!(
3071 parse_gpu_options(Some("syncfd=false,backend=gfxstream"), &mut gpu_params).is_ok()
3072 );
3073 assert!(!gpu_params.gfxstream_use_syncfd);
3074 }
3075 {
3076 let mut gpu_params: GpuParameters = Default::default();
3077 assert!(parse_gpu_options(
3078 Some("backend=gfxstream,syncfd=invalid_value"),
3079 &mut gpu_params
3080 )
3081 .is_err());
3082 }
3083 {
3084 let mut gpu_params: GpuParameters = Default::default();
3085 assert!(parse_gpu_options(
3086 Some("syncfd=invalid_value,backend=gfxstream"),
3087 &mut gpu_params
3088 )
3089 .is_err());
3090 }
Kaiyi Lidd348a42020-07-13 11:49:46 -07003091 }
3092
3093 #[cfg(all(feature = "gpu", feature = "gfxstream"))]
3094 #[test]
3095 fn parse_gpu_options_not_gfxstream_with_syncfd_specified() {
Jason Macnakd659a0d2021-03-15 15:33:01 -07003096 {
3097 let mut gpu_params: GpuParameters = Default::default();
3098 assert!(
3099 parse_gpu_options(Some("backend=virglrenderer,syncfd=true"), &mut gpu_params)
3100 .is_err()
3101 );
3102 }
3103 {
3104 let mut gpu_params: GpuParameters = Default::default();
3105 assert!(
3106 parse_gpu_options(Some("syncfd=true,backend=virglrenderer"), &mut gpu_params)
3107 .is_err()
3108 );
3109 }
3110 }
3111
3112 #[cfg(feature = "gpu")]
3113 #[test]
3114 fn parse_gpu_display_options_valid() {
3115 {
3116 let mut gpu_params: GpuParameters = Default::default();
3117 assert!(
3118 parse_gpu_display_options(Some("width=500,height=600"), &mut gpu_params).is_ok()
3119 );
3120 assert_eq!(gpu_params.displays.len(), 1);
3121 assert_eq!(gpu_params.displays[0].width, 500);
3122 assert_eq!(gpu_params.displays[0].height, 600);
3123 }
3124 }
3125
3126 #[cfg(feature = "gpu")]
3127 #[test]
3128 fn parse_gpu_display_options_invalid() {
3129 {
3130 let mut gpu_params: GpuParameters = Default::default();
3131 assert!(parse_gpu_display_options(Some("width=500"), &mut gpu_params).is_err());
3132 }
3133 {
3134 let mut gpu_params: GpuParameters = Default::default();
3135 assert!(parse_gpu_display_options(Some("height=500"), &mut gpu_params).is_err());
3136 }
3137 {
3138 let mut gpu_params: GpuParameters = Default::default();
3139 assert!(parse_gpu_display_options(Some("width"), &mut gpu_params).is_err());
3140 }
3141 {
3142 let mut gpu_params: GpuParameters = Default::default();
3143 assert!(parse_gpu_display_options(Some("blah"), &mut gpu_params).is_err());
3144 }
3145 }
3146
3147 #[cfg(feature = "gpu")]
3148 #[test]
3149 fn parse_gpu_options_and_gpu_display_options_valid() {
3150 {
3151 let mut gpu_params: GpuParameters = Default::default();
3152 assert!(parse_gpu_options(Some("2D,width=500,height=600"), &mut gpu_params).is_ok());
3153 assert!(
3154 parse_gpu_display_options(Some("width=700,height=800"), &mut gpu_params).is_ok()
3155 );
3156 assert_eq!(gpu_params.displays.len(), 2);
3157 assert_eq!(gpu_params.displays[0].width, 500);
3158 assert_eq!(gpu_params.displays[0].height, 600);
3159 assert_eq!(gpu_params.displays[1].width, 700);
3160 assert_eq!(gpu_params.displays[1].height, 800);
3161 }
3162 {
3163 let mut gpu_params: GpuParameters = Default::default();
3164 assert!(parse_gpu_options(Some("2D"), &mut gpu_params).is_ok());
3165 assert!(
3166 parse_gpu_display_options(Some("width=700,height=800"), &mut gpu_params).is_ok()
3167 );
3168 assert_eq!(gpu_params.displays.len(), 1);
3169 assert_eq!(gpu_params.displays[0].width, 700);
3170 assert_eq!(gpu_params.displays[0].height, 800);
3171 }
Kaiyi Lidd348a42020-07-13 11:49:46 -07003172 }
Chuanxiao Dongfd5626c2020-04-27 16:35:33 +08003173
3174 #[test]
3175 fn parse_battery_vaild() {
3176 parse_battery_options(Some("type=goldfish")).expect("parse should have succeded");
3177 }
3178
3179 #[test]
3180 fn parse_battery_vaild_no_type() {
3181 parse_battery_options(None).expect("parse should have succeded");
3182 }
3183
3184 #[test]
3185 fn parse_battery_invaild_parameter() {
3186 parse_battery_options(Some("tyep=goldfish")).expect_err("parse should have failed");
3187 }
3188
3189 #[test]
3190 fn parse_battery_invaild_type_value() {
3191 parse_battery_options(Some("type=xxx")).expect_err("parse should have failed");
3192 }
Daniel Verkamp107edb32019-04-05 09:58:48 -07003193}