blob: e1235035cd04ad9b69273f14673604ec32a16921 [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;
Andrew Walbranfb7c1082021-06-17 18:12:14 +010039#[cfg(feature = "composite-disk")]
Jooyung Han2e14c732021-07-29 13:27:54 +090040use disk::{
41 create_composite_disk, create_disk_file, create_zero_filler, ImagePartitionType, PartitionInfo,
42};
Jakub Starone7c59052019-04-09 12:31:14 -070043use vm_control::{
Kevin Hamacher6fc5f202021-03-18 12:41:23 +010044 client::{
45 do_modify_battery, do_usb_attach, do_usb_detach, do_usb_list, handle_request, vms_request,
46 ModifyUsbError, ModifyUsbResult,
47 },
48 BalloonControlCommand, BatteryType, DiskControlCommand, UsbControlResult, VmRequest,
Hikaru Nishida6b51c752021-05-21 12:37:43 +090049 VmResponse,
Jakub Starone7c59052019-04-09 12:31:14 -070050};
Zach Reizner639d9672017-05-01 17:57:18 -070051
Cody Schuffelen6d1ab502019-05-21 12:12:38 -070052fn executable_is_plugin(executable: &Option<Executable>) -> bool {
Daniel Verkampc26d20b2020-11-04 14:39:31 -080053 matches!(executable, Some(Executable::Plugin(_)))
Cody Schuffelen6d1ab502019-05-21 12:12:38 -070054}
55
Stephen Barbera00753b2017-07-18 13:57:26 -070056// Wait for all children to exit. Return true if they have all exited, false
57// otherwise.
58fn wait_all_children() -> bool {
Stephen Barber49dd2e22018-10-29 18:29:58 -070059 const CHILD_WAIT_MAX_ITER: isize = 100;
Stephen Barbera00753b2017-07-18 13:57:26 -070060 const CHILD_WAIT_MS: u64 = 10;
61 for _ in 0..CHILD_WAIT_MAX_ITER {
Stephen Barbera00753b2017-07-18 13:57:26 -070062 loop {
Zach Reizner56158c82017-08-24 13:50:14 -070063 match reap_child() {
64 Ok(0) => break,
65 // We expect ECHILD which indicates that there were no children left.
66 Err(e) if e.errno() == libc::ECHILD => return true,
67 Err(e) => {
David Tolnayb4bd00f2019-02-12 17:51:26 -080068 warn!("error while waiting for children: {}", e);
Zach Reizner56158c82017-08-24 13:50:14 -070069 return false;
Stephen Barbera00753b2017-07-18 13:57:26 -070070 }
Zach Reizner56158c82017-08-24 13:50:14 -070071 // We reaped one child, so continue reaping.
Zach Reizner55a9e502018-10-03 10:22:32 -070072 _ => {}
Stephen Barbera00753b2017-07-18 13:57:26 -070073 }
74 }
Zach Reizner56158c82017-08-24 13:50:14 -070075 // There's no timeout option for waitpid which reap_child calls internally, so our only
76 // recourse is to sleep while waiting for the children to exit.
Stephen Barbera00753b2017-07-18 13:57:26 -070077 sleep(Duration::from_millis(CHILD_WAIT_MS));
78 }
79
80 // If we've made it to this point, not all of the children have exited.
David Tolnay5bbbf612018-12-01 17:49:30 -080081 false
Stephen Barbera00753b2017-07-18 13:57:26 -070082}
83
Daniel Verkamp107edb32019-04-05 09:58:48 -070084/// Parse a comma-separated list of CPU numbers and ranges and convert it to a Vec of CPU numbers.
85fn parse_cpu_set(s: &str) -> argument::Result<Vec<usize>> {
86 let mut cpuset = Vec::new();
87 for part in s.split(',') {
88 let range: Vec<&str> = part.split('-').collect();
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +090089 if range.is_empty() || range.len() > 2 {
Daniel Verkamp107edb32019-04-05 09:58:48 -070090 return Err(argument::Error::InvalidValue {
91 value: part.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +080092 expected: String::from("invalid list syntax"),
Daniel Verkamp107edb32019-04-05 09:58:48 -070093 });
94 }
95 let first_cpu: usize = range[0]
96 .parse()
97 .map_err(|_| argument::Error::InvalidValue {
98 value: part.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +080099 expected: String::from("CPU index must be a non-negative integer"),
Daniel Verkamp107edb32019-04-05 09:58:48 -0700100 })?;
101 let last_cpu: usize = if range.len() == 2 {
102 range[1]
103 .parse()
104 .map_err(|_| argument::Error::InvalidValue {
105 value: part.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800106 expected: String::from("CPU index must be a non-negative integer"),
Daniel Verkamp107edb32019-04-05 09:58:48 -0700107 })?
108 } else {
109 first_cpu
110 };
111
112 if last_cpu < first_cpu {
113 return Err(argument::Error::InvalidValue {
114 value: part.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800115 expected: String::from("CPU ranges must be from low to high"),
Daniel Verkamp107edb32019-04-05 09:58:48 -0700116 });
117 }
118
119 for cpu in first_cpu..=last_cpu {
120 cpuset.push(cpu);
121 }
122 }
123 Ok(cpuset)
124}
125
Daniel Verkampc677fb42020-09-08 13:47:49 -0700126/// Parse a list of guest to host CPU mappings.
127///
128/// Each mapping consists of a single guest CPU index mapped to one or more host CPUs in the form
129/// accepted by `parse_cpu_set`:
130///
131/// `<GUEST-CPU>=<HOST-CPU-SET>[:<GUEST-CPU>=<HOST-CPU-SET>[:...]]`
132fn parse_cpu_affinity(s: &str) -> argument::Result<VcpuAffinity> {
133 if s.contains('=') {
134 let mut affinity_map = BTreeMap::new();
135 for cpu_pair in s.split(':') {
136 let assignment: Vec<&str> = cpu_pair.split('=').collect();
137 if assignment.len() != 2 {
138 return Err(argument::Error::InvalidValue {
139 value: cpu_pair.to_owned(),
140 expected: String::from("invalid VCPU assignment syntax"),
141 });
142 }
143 let guest_cpu = assignment[0]
144 .parse()
145 .map_err(|_| argument::Error::InvalidValue {
146 value: assignment[0].to_owned(),
147 expected: String::from("CPU index must be a non-negative integer"),
148 })?;
149 let host_cpu_set = parse_cpu_set(assignment[1])?;
150 if affinity_map.insert(guest_cpu, host_cpu_set).is_some() {
151 return Err(argument::Error::InvalidValue {
152 value: cpu_pair.to_owned(),
153 expected: String::from("VCPU index must be unique"),
154 });
155 }
156 }
157 Ok(VcpuAffinity::PerVcpu(affinity_map))
158 } else {
159 Ok(VcpuAffinity::Global(parse_cpu_set(s)?))
160 }
161}
162
Daniel Verkamp8a72afc2021-03-15 17:55:52 -0700163fn parse_cpu_capacity(s: &str, cpu_capacity: &mut BTreeMap<usize, u32>) -> argument::Result<()> {
164 for cpu_pair in s.split(',') {
165 let assignment: Vec<&str> = cpu_pair.split('=').collect();
166 if assignment.len() != 2 {
167 return Err(argument::Error::InvalidValue {
168 value: cpu_pair.to_owned(),
169 expected: String::from("invalid CPU capacity syntax"),
170 });
171 }
172 let cpu = assignment[0]
173 .parse()
174 .map_err(|_| argument::Error::InvalidValue {
175 value: assignment[0].to_owned(),
176 expected: String::from("CPU index must be a non-negative integer"),
177 })?;
178 let capacity = assignment[1]
179 .parse()
180 .map_err(|_| argument::Error::InvalidValue {
181 value: assignment[1].to_owned(),
182 expected: String::from("CPU capacity must be a non-negative integer"),
183 })?;
184 if cpu_capacity.insert(cpu, capacity).is_some() {
185 return Err(argument::Error::InvalidValue {
186 value: cpu_pair.to_owned(),
187 expected: String::from("CPU index must be unique"),
188 });
189 }
190 }
191 Ok(())
192}
193
Jason Macnakcc7070b2019-11-06 14:48:12 -0800194#[cfg(feature = "gpu")]
Jason Macnakd659a0d2021-03-15 15:33:01 -0700195fn parse_gpu_options(s: Option<&str>, gpu_params: &mut GpuParameters) -> argument::Result<()> {
Kaiyi Lidd348a42020-07-13 11:49:46 -0700196 #[cfg(feature = "gfxstream")]
197 let mut vulkan_specified = false;
198 #[cfg(feature = "gfxstream")]
199 let mut syncfd_specified = false;
Jason Macnak046ed142020-10-08 09:11:53 -0700200 #[cfg(feature = "gfxstream")]
201 let mut angle_specified = false;
Jason Macnakcc7070b2019-11-06 14:48:12 -0800202
Jason Macnakd659a0d2021-03-15 15:33:01 -0700203 let mut display_w: Option<u32> = None;
204 let mut display_h: Option<u32> = None;
205
Jason Macnakcc7070b2019-11-06 14:48:12 -0800206 if let Some(s) = s {
207 let opts = s
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +0900208 .split(',')
209 .map(|frag| frag.split('='))
Jason Macnakcc7070b2019-11-06 14:48:12 -0800210 .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
211
212 for (k, v) in opts {
213 match k {
Lingfeng Yangddbe8b72020-01-30 10:00:36 -0800214 // Deprecated: Specifying --gpu=<mode> Not great as the mode can be set multiple
Chia-I Wue25859b2021-04-14 13:57:55 -0700215 // times if the user specifies several modes (--gpu=2d,virglrenderer,gfxstream)
Jason Macnak327fc242020-01-10 12:45:36 -0800216 "2d" | "2D" => {
217 gpu_params.mode = GpuMode::Mode2D;
218 }
Chia-I Wue25859b2021-04-14 13:57:55 -0700219 "3d" | "3D" | "virglrenderer" => {
220 gpu_params.mode = GpuMode::ModeVirglRenderer;
Jason Macnak327fc242020-01-10 12:45:36 -0800221 }
Lingfeng Yangddbe8b72020-01-30 10:00:36 -0800222 #[cfg(feature = "gfxstream")]
223 "gfxstream" => {
Gurchetan Singhb1394f72020-10-19 18:31:13 -0700224 gpu_params.mode = GpuMode::ModeGfxstream;
Lingfeng Yangddbe8b72020-01-30 10:00:36 -0800225 }
226 // Preferred: Specifying --gpu,backend=<mode>
227 "backend" => match v {
228 "2d" | "2D" => {
229 gpu_params.mode = GpuMode::Mode2D;
230 }
Chia-I Wue25859b2021-04-14 13:57:55 -0700231 "3d" | "3D" | "virglrenderer" => {
232 gpu_params.mode = GpuMode::ModeVirglRenderer;
Lingfeng Yangddbe8b72020-01-30 10:00:36 -0800233 }
234 #[cfg(feature = "gfxstream")]
235 "gfxstream" => {
Gurchetan Singhb1394f72020-10-19 18:31:13 -0700236 gpu_params.mode = GpuMode::ModeGfxstream;
Lingfeng Yangddbe8b72020-01-30 10:00:36 -0800237 }
238 _ => {
239 return Err(argument::Error::InvalidValue {
240 value: v.to_string(),
Judy Hsiao59343052020-03-16 15:58:03 +0800241 expected: String::from(
Chia-I Wue25859b2021-04-14 13:57:55 -0700242 "gpu parameter 'backend' should be one of (2d|virglrenderer|gfxstream)",
Judy Hsiao59343052020-03-16 15:58:03 +0800243 ),
Lingfeng Yangddbe8b72020-01-30 10:00:36 -0800244 });
245 }
246 },
Jason Macnakbf195582019-11-20 16:25:49 -0800247 "egl" => match v {
248 "true" | "" => {
249 gpu_params.renderer_use_egl = true;
250 }
251 "false" => {
252 gpu_params.renderer_use_egl = false;
253 }
254 _ => {
255 return Err(argument::Error::InvalidValue {
256 value: v.to_string(),
Judy Hsiao59343052020-03-16 15:58:03 +0800257 expected: String::from("gpu parameter 'egl' should be a boolean"),
Jason Macnakbf195582019-11-20 16:25:49 -0800258 });
259 }
260 },
261 "gles" => match v {
262 "true" | "" => {
263 gpu_params.renderer_use_gles = true;
264 }
265 "false" => {
266 gpu_params.renderer_use_gles = false;
267 }
268 _ => {
269 return Err(argument::Error::InvalidValue {
270 value: v.to_string(),
Judy Hsiao59343052020-03-16 15:58:03 +0800271 expected: String::from("gpu parameter 'gles' should be a boolean"),
Jason Macnakbf195582019-11-20 16:25:49 -0800272 });
273 }
274 },
275 "glx" => match v {
276 "true" | "" => {
277 gpu_params.renderer_use_glx = true;
278 }
279 "false" => {
280 gpu_params.renderer_use_glx = false;
281 }
282 _ => {
283 return Err(argument::Error::InvalidValue {
284 value: v.to_string(),
Judy Hsiao59343052020-03-16 15:58:03 +0800285 expected: String::from("gpu parameter 'glx' should be a boolean"),
Jason Macnakbf195582019-11-20 16:25:49 -0800286 });
287 }
288 },
289 "surfaceless" => match v {
290 "true" | "" => {
291 gpu_params.renderer_use_surfaceless = true;
292 }
293 "false" => {
294 gpu_params.renderer_use_surfaceless = false;
295 }
296 _ => {
297 return Err(argument::Error::InvalidValue {
298 value: v.to_string(),
Judy Hsiao59343052020-03-16 15:58:03 +0800299 expected: String::from(
300 "gpu parameter 'surfaceless' should be a boolean",
301 ),
Jason Macnakbf195582019-11-20 16:25:49 -0800302 });
303 }
304 },
Kaiyi Li6404e452020-07-07 19:12:03 -0700305 #[cfg(feature = "gfxstream")]
Kaiyi Lidd348a42020-07-13 11:49:46 -0700306 "syncfd" => {
307 syncfd_specified = true;
308 match v {
309 "true" | "" => {
310 gpu_params.gfxstream_use_syncfd = true;
311 }
312 "false" => {
313 gpu_params.gfxstream_use_syncfd = false;
314 }
315 _ => {
316 return Err(argument::Error::InvalidValue {
317 value: v.to_string(),
318 expected: String::from(
319 "gpu parameter 'syncfd' should be a boolean",
320 ),
321 });
322 }
Kaiyi Li6404e452020-07-07 19:12:03 -0700323 }
Kaiyi Lidd348a42020-07-13 11:49:46 -0700324 }
Kaiyi Li6c52a2e2020-07-07 19:28:32 -0700325 #[cfg(feature = "gfxstream")]
Jason Macnak046ed142020-10-08 09:11:53 -0700326 "angle" => {
327 angle_specified = true;
328 match v {
329 "true" | "" => {
330 gpu_params.gfxstream_use_guest_angle = true;
331 }
332 "false" => {
333 gpu_params.gfxstream_use_guest_angle = false;
334 }
335 _ => {
336 return Err(argument::Error::InvalidValue {
337 value: v.to_string(),
338 expected: String::from("gpu parameter 'angle' should be a boolean"),
339 });
340 }
341 }
342 }
Kaiyi Lidd348a42020-07-13 11:49:46 -0700343 "vulkan" => {
Chia-I Wu6d473b32021-04-12 10:14:24 -0700344 #[cfg(feature = "gfxstream")]
345 {
346 vulkan_specified = true;
347 }
Kaiyi Lidd348a42020-07-13 11:49:46 -0700348 match v {
349 "true" | "" => {
Chia-I Wu0beb2462021-04-12 09:36:08 -0700350 gpu_params.use_vulkan = true;
Kaiyi Lidd348a42020-07-13 11:49:46 -0700351 }
352 "false" => {
Chia-I Wu0beb2462021-04-12 09:36:08 -0700353 gpu_params.use_vulkan = false;
Kaiyi Lidd348a42020-07-13 11:49:46 -0700354 }
355 _ => {
356 return Err(argument::Error::InvalidValue {
357 value: v.to_string(),
358 expected: String::from(
359 "gpu parameter 'vulkan' should be a boolean",
360 ),
361 });
362 }
Kaiyi Li6c52a2e2020-07-07 19:28:32 -0700363 }
Kaiyi Lidd348a42020-07-13 11:49:46 -0700364 }
Jason Macnakcc7070b2019-11-06 14:48:12 -0800365 "width" => {
Jason Macnakd659a0d2021-03-15 15:33:01 -0700366 let width = v
367 .parse::<u32>()
368 .map_err(|_| argument::Error::InvalidValue {
369 value: v.to_string(),
370 expected: String::from("gpu parameter 'width' must be a valid integer"),
371 })?;
372 display_w = Some(width);
Jason Macnakcc7070b2019-11-06 14:48:12 -0800373 }
374 "height" => {
Jason Macnakd659a0d2021-03-15 15:33:01 -0700375 let height = v
376 .parse::<u32>()
377 .map_err(|_| argument::Error::InvalidValue {
378 value: v.to_string(),
379 expected: String::from(
380 "gpu parameter 'height' must be a valid integer",
381 ),
382 })?;
383 display_h = Some(height);
Jason Macnakcc7070b2019-11-06 14:48:12 -0800384 }
John Batesb220eac2020-09-14 17:03:02 -0700385 "cache-path" => gpu_params.cache_path = Some(v.to_string()),
386 "cache-size" => gpu_params.cache_size = Some(v.to_string()),
Gurchetan Singh401340e2021-03-23 09:34:00 -0700387 "udmabuf" => match v {
388 "true" | "" => {
389 gpu_params.udmabuf = true;
390 }
391 "false" => {
392 gpu_params.udmabuf = false;
393 }
394 _ => {
395 return Err(argument::Error::InvalidValue {
396 value: v.to_string(),
397 expected: String::from("gpu parameter 'udmabuf' should be a boolean"),
398 });
399 }
400 },
Jason Macnakcc7070b2019-11-06 14:48:12 -0800401 "" => {}
402 _ => {
403 return Err(argument::Error::UnknownArgument(format!(
404 "gpu parameter {}",
405 k
406 )));
407 }
408 }
409 }
410 }
411
Jason Macnakd659a0d2021-03-15 15:33:01 -0700412 if display_w.is_some() || display_h.is_some() {
413 if display_w.is_none() || display_h.is_none() {
414 return Err(argument::Error::InvalidValue {
415 value: s.unwrap_or("").to_string(),
416 expected: String::from(
417 "gpu must include both 'width' and 'height' if either is supplied",
418 ),
419 });
420 }
421
422 gpu_params.displays.push(GpuDisplayParameters {
423 width: display_w.unwrap(),
424 height: display_h.unwrap(),
425 });
426 }
427
Kaiyi Lidd348a42020-07-13 11:49:46 -0700428 #[cfg(feature = "gfxstream")]
429 {
Chia-I Wu91df6562021-04-12 09:47:38 -0700430 if !vulkan_specified && gpu_params.mode == GpuMode::ModeGfxstream {
431 gpu_params.use_vulkan = true;
432 }
433
Chia-I Wu6d473b32021-04-12 10:14:24 -0700434 if syncfd_specified || angle_specified {
Kaiyi Lidd348a42020-07-13 11:49:46 -0700435 match gpu_params.mode {
Gurchetan Singhb1394f72020-10-19 18:31:13 -0700436 GpuMode::ModeGfxstream => {}
Kaiyi Lidd348a42020-07-13 11:49:46 -0700437 _ => {
438 return Err(argument::Error::UnknownArgument(
Chia-I Wu6d473b32021-04-12 10:14:24 -0700439 "gpu parameter syncfd and angle are only supported for gfxstream backend"
Kaiyi Lidd348a42020-07-13 11:49:46 -0700440 .to_string(),
441 ));
442 }
443 }
444 }
445 }
446
Jason Macnakd659a0d2021-03-15 15:33:01 -0700447 Ok(())
448}
449
450#[cfg(feature = "gpu")]
451fn parse_gpu_display_options(
452 s: Option<&str>,
453 gpu_params: &mut GpuParameters,
454) -> argument::Result<()> {
455 let mut display_w: Option<u32> = None;
456 let mut display_h: Option<u32> = None;
457
458 if let Some(s) = s {
459 let opts = s
460 .split(',')
461 .map(|frag| frag.split('='))
462 .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
463
464 for (k, v) in opts {
465 match k {
466 "width" => {
467 let width = v
468 .parse::<u32>()
469 .map_err(|_| argument::Error::InvalidValue {
470 value: v.to_string(),
471 expected: String::from("gpu parameter 'width' must be a valid integer"),
472 })?;
473 display_w = Some(width);
474 }
475 "height" => {
476 let height = v
477 .parse::<u32>()
478 .map_err(|_| argument::Error::InvalidValue {
479 value: v.to_string(),
480 expected: String::from(
481 "gpu parameter 'height' must be a valid integer",
482 ),
483 })?;
484 display_h = Some(height);
485 }
486 "" => {}
487 _ => {
488 return Err(argument::Error::UnknownArgument(format!(
489 "gpu-display parameter {}",
490 k
491 )));
492 }
493 }
494 }
495 }
496
497 if display_w.is_none() || display_h.is_none() {
498 return Err(argument::Error::InvalidValue {
499 value: s.unwrap_or("").to_string(),
500 expected: String::from("gpu-display must include both 'width' and 'height'"),
501 });
502 }
503
504 gpu_params.displays.push(GpuDisplayParameters {
505 width: display_w.unwrap(),
506 height: display_h.unwrap(),
507 });
508
509 Ok(())
Jason Macnakcc7070b2019-11-06 14:48:12 -0800510}
511
Andrew Scull1590e6f2020-03-18 18:00:47 +0000512#[cfg(feature = "audio")]
Judy Hsiaod5c1e962020-02-04 12:30:01 +0800513fn parse_ac97_options(s: &str) -> argument::Result<Ac97Parameters> {
514 let mut ac97_params: Ac97Parameters = Default::default();
515
516 let opts = s
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +0900517 .split(',')
518 .map(|frag| frag.split('='))
Judy Hsiaod5c1e962020-02-04 12:30:01 +0800519 .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
520
521 for (k, v) in opts {
522 match k {
523 "backend" => {
524 ac97_params.backend =
525 v.parse::<Ac97Backend>()
526 .map_err(|e| argument::Error::InvalidValue {
527 value: v.to_string(),
528 expected: e.to_string(),
529 })?;
530 }
531 "capture" => {
532 ac97_params.capture = v.parse::<bool>().map_err(|e| {
533 argument::Error::Syntax(format!("invalid capture option: {}", e))
534 })?;
535 }
Dennis Kempin50a58f92021-06-23 11:34:31 -0700536 #[cfg(feature = "audio_cras")]
paulhsia83d51602021-03-09 17:13:14 +0800537 "client_type" => {
538 ac97_params
539 .set_client_type(v)
540 .map_err(|e| argument::Error::InvalidValue {
541 value: v.to_string(),
542 expected: e.to_string(),
543 })?;
544 }
Jorge E. Moreira6a88a5d2021-03-12 15:34:46 -0800545 #[cfg(any(target_os = "linux", target_os = "android"))]
Jorge E. Moreira359e7de2020-12-02 18:25:53 -0800546 "server" => {
547 ac97_params.vios_server_path =
548 Some(
549 PathBuf::from_str(v).map_err(|e| argument::Error::InvalidValue {
550 value: v.to_string(),
551 expected: e.to_string(),
552 })?,
553 );
554 }
Judy Hsiaod5c1e962020-02-04 12:30:01 +0800555 _ => {
556 return Err(argument::Error::UnknownArgument(format!(
557 "unknown ac97 parameter {}",
558 k
559 )));
560 }
561 }
562 }
563
Jorge E. Moreira359e7de2020-12-02 18:25:53 -0800564 // server is required for and exclusive to vios backend
Jorge E. Moreira6a88a5d2021-03-12 15:34:46 -0800565 #[cfg(any(target_os = "linux", target_os = "android"))]
Jorge E. Moreira359e7de2020-12-02 18:25:53 -0800566 match ac97_params.backend {
567 Ac97Backend::VIOS => {
568 if ac97_params.vios_server_path.is_none() {
569 return Err(argument::Error::ExpectedArgument(String::from(
570 "server argument is required for VIOS backend",
571 )));
572 }
573 }
574 _ => {
575 if ac97_params.vios_server_path.is_some() {
576 return Err(argument::Error::UnexpectedValue(String::from(
577 "server argument is exclusive to the VIOS backend",
578 )));
579 }
580 }
581 }
582
Judy Hsiaod5c1e962020-02-04 12:30:01 +0800583 Ok(ac97_params)
584}
585
Trent Begin17ccaad2019-04-17 13:51:25 -0600586fn parse_serial_options(s: &str) -> argument::Result<SerialParameters> {
587 let mut serial_setting = SerialParameters {
588 type_: SerialType::Sink,
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -0700589 hardware: SerialHardware::Serial,
Trent Begin17ccaad2019-04-17 13:51:25 -0600590 path: None,
Iliyan Malchev2c1417b2020-04-14 09:40:41 -0700591 input: None,
Trent Begin923bab02019-06-17 13:48:06 -0600592 num: 1,
Trent Begin17ccaad2019-04-17 13:51:25 -0600593 console: false,
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -0700594 earlycon: false,
Jorge E. Moreira1e262302019-08-01 14:40:03 -0700595 stdin: false,
Trent Begin17ccaad2019-04-17 13:51:25 -0600596 };
597
598 let opts = s
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +0900599 .split(',')
Nicholas Hollingumc76c2da2020-09-18 15:53:16 +1000600 .map(|frag| frag.splitn(2, '='))
Trent Begin17ccaad2019-04-17 13:51:25 -0600601 .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
602
603 for (k, v) in opts {
604 match k {
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -0700605 "hardware" => {
606 serial_setting.hardware = v
607 .parse::<SerialHardware>()
608 .map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?
609 }
Trent Begin17ccaad2019-04-17 13:51:25 -0600610 "type" => {
611 serial_setting.type_ = v
612 .parse::<SerialType>()
613 .map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?
614 }
615 "num" => {
616 let num = v.parse::<u8>().map_err(|e| {
617 argument::Error::Syntax(format!("serial device number is not parsable: {}", e))
618 })?;
A. Cody Schuffelen3faab5a2020-10-05 17:29:16 -0700619 if num < 1 {
Trent Begin17ccaad2019-04-17 13:51:25 -0600620 return Err(argument::Error::InvalidValue {
621 value: num.to_string(),
A. Cody Schuffelen3faab5a2020-10-05 17:29:16 -0700622 expected: String::from("Serial port num must be at least 1"),
Trent Begin17ccaad2019-04-17 13:51:25 -0600623 });
624 }
625 serial_setting.num = num;
626 }
627 "console" => {
628 serial_setting.console = v.parse::<bool>().map_err(|e| {
629 argument::Error::Syntax(format!(
630 "serial device console is not parseable: {}",
631 e
632 ))
633 })?
634 }
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -0700635 "earlycon" => {
636 serial_setting.earlycon = v.parse::<bool>().map_err(|e| {
637 argument::Error::Syntax(format!(
638 "serial device earlycon is not parseable: {}",
639 e,
640 ))
641 })?
642 }
Jorge E. Moreira1e262302019-08-01 14:40:03 -0700643 "stdin" => {
644 serial_setting.stdin = v.parse::<bool>().map_err(|e| {
645 argument::Error::Syntax(format!("serial device stdin is not parseable: {}", e))
Iliyan Malchev2c1417b2020-04-14 09:40:41 -0700646 })?;
647 if serial_setting.stdin && serial_setting.input.is_some() {
648 return Err(argument::Error::TooManyArguments(
649 "Cannot specify both stdin and input options".to_string(),
650 ));
651 }
Jorge E. Moreira1e262302019-08-01 14:40:03 -0700652 }
Jorge E. Moreira9c9e0e72019-05-17 13:57:04 -0700653 "path" => serial_setting.path = Some(PathBuf::from(v)),
Iliyan Malchev2c1417b2020-04-14 09:40:41 -0700654 "input" => {
655 if serial_setting.stdin {
656 return Err(argument::Error::TooManyArguments(
657 "Cannot specify both stdin and input options".to_string(),
658 ));
659 }
660 serial_setting.input = Some(PathBuf::from(v));
661 }
Trent Begin17ccaad2019-04-17 13:51:25 -0600662 _ => {
663 return Err(argument::Error::UnknownArgument(format!(
664 "serial parameter {}",
665 k
666 )));
667 }
668 }
669 }
670
A. Cody Schuffelen3faab5a2020-10-05 17:29:16 -0700671 if serial_setting.hardware == SerialHardware::Serial && serial_setting.num > 4 {
672 return Err(argument::Error::InvalidValue {
673 value: serial_setting.num.to_string(),
674 expected: String::from("Serial port num must be 4 or less"),
675 });
676 }
677
Trent Begin17ccaad2019-04-17 13:51:25 -0600678 Ok(serial_setting)
679}
680
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800681fn parse_plugin_mount_option(value: &str) -> argument::Result<BindMount> {
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +0900682 let components: Vec<&str> = value.split(':').collect();
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800683 if components.is_empty() || components.len() > 3 || components[0].is_empty() {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800684 return Err(argument::Error::InvalidValue {
685 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800686 expected: String::from(
687 "`plugin-mount` should be in a form of: <src>[:[<dst>][:<writable>]]",
688 ),
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800689 });
690 }
691
692 let src = PathBuf::from(components[0]);
693 if src.is_relative() {
694 return Err(argument::Error::InvalidValue {
695 value: components[0].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800696 expected: String::from("the source path for `plugin-mount` must be absolute"),
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800697 });
698 }
699 if !src.exists() {
700 return Err(argument::Error::InvalidValue {
701 value: components[0].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800702 expected: String::from("the source path for `plugin-mount` does not exist"),
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800703 });
704 }
705
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800706 let dst = PathBuf::from(match components.get(1) {
707 None | Some(&"") => components[0],
708 Some(path) => path,
709 });
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800710 if dst.is_relative() {
711 return Err(argument::Error::InvalidValue {
712 value: components[1].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800713 expected: String::from("the destination path for `plugin-mount` must be absolute"),
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800714 });
715 }
716
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800717 let writable: bool = match components.get(2) {
718 None => false,
719 Some(s) => s.parse().map_err(|_| argument::Error::InvalidValue {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800720 value: components[2].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800721 expected: String::from("the <writable> component for `plugin-mount` is not valid bool"),
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800722 })?,
723 };
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800724
725 Ok(BindMount { src, dst, writable })
726}
727
728fn parse_plugin_gid_map_option(value: &str) -> argument::Result<GidMap> {
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +0900729 let components: Vec<&str> = value.split(':').collect();
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800730 if components.is_empty() || components.len() > 3 || components[0].is_empty() {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800731 return Err(argument::Error::InvalidValue {
732 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800733 expected: String::from(
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800734 "`plugin-gid-map` must have exactly 3 components: <inner>[:[<outer>][:<count>]]",
Judy Hsiao59343052020-03-16 15:58:03 +0800735 ),
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800736 });
737 }
738
739 let inner: libc::gid_t = components[0]
740 .parse()
741 .map_err(|_| argument::Error::InvalidValue {
742 value: components[0].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800743 expected: String::from("the <inner> component for `plugin-gid-map` is not valid gid"),
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800744 })?;
745
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800746 let outer: libc::gid_t = match components.get(1) {
747 None | Some(&"") => inner,
748 Some(s) => s.parse().map_err(|_| argument::Error::InvalidValue {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800749 value: components[1].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800750 expected: String::from("the <outer> component for `plugin-gid-map` is not valid gid"),
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800751 })?,
752 };
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800753
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800754 let count: u32 = match components.get(2) {
755 None => 1,
756 Some(s) => s.parse().map_err(|_| argument::Error::InvalidValue {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800757 value: components[2].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800758 expected: String::from(
759 "the <count> component for `plugin-gid-map` is not valid number",
760 ),
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800761 })?,
762 };
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800763
764 Ok(GidMap {
765 inner,
766 outer,
767 count,
768 })
769}
770
Chuanxiao Dongfd5626c2020-04-27 16:35:33 +0800771fn parse_battery_options(s: Option<&str>) -> argument::Result<BatteryType> {
772 let mut battery_type: BatteryType = Default::default();
773
774 if let Some(s) = s {
775 let opts = s
Andrew Walbran9cfdbd92021-01-11 17:40:34 +0000776 .split(',')
777 .map(|frag| frag.split('='))
Chuanxiao Dongfd5626c2020-04-27 16:35:33 +0800778 .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
779
780 for (k, v) in opts {
781 match k {
782 "type" => match v.parse::<BatteryType>() {
783 Ok(type_) => battery_type = type_,
784 Err(e) => {
785 return Err(argument::Error::InvalidValue {
786 value: v.to_string(),
787 expected: e.to_string(),
788 });
789 }
790 },
791 "" => {}
792 _ => {
793 return Err(argument::Error::UnknownArgument(format!(
794 "battery parameter {}",
795 k
796 )));
797 }
798 }
799 }
800 }
801
802 Ok(battery_type)
803}
804
Tomasz Jeznach3ce74762021-02-26 01:01:53 -0800805#[cfg(feature = "direct")]
806fn parse_direct_io_options(s: Option<&str>) -> argument::Result<DirectIoOption> {
807 let s = s.ok_or(argument::Error::ExpectedValue(String::from(
808 "expected path@range[,range] value",
809 )))?;
810 let parts: Vec<&str> = s.splitn(2, '@').collect();
811 if parts.len() != 2 {
812 return Err(argument::Error::InvalidValue {
813 value: s.to_string(),
814 expected: String::from("missing port range, use /path@X-Y,Z,.. syntax"),
815 });
816 }
817 let path = PathBuf::from(parts[0]);
818 if !path.exists() {
819 return Err(argument::Error::InvalidValue {
820 value: parts[0].to_owned(),
821 expected: String::from("the path does not exist"),
822 });
823 };
824 let ranges: argument::Result<Vec<(u64, u64)>> = parts[1]
825 .split(',')
826 .map(|frag| frag.split('-'))
827 .map(|mut range| {
828 let base = range
829 .next()
830 .map(|v| v.parse::<u64>())
831 .map_or(Ok(None), |r| r.map(Some));
832 let last = range
833 .next()
834 .map(|v| v.parse::<u64>())
835 .map_or(Ok(None), |r| r.map(Some));
836 (base, last)
837 })
838 .map(|range| match range {
839 (Ok(Some(base)), Ok(None)) => Ok((base, 1)),
840 (Ok(Some(base)), Ok(Some(last))) => {
841 Ok((base, last.saturating_sub(base).saturating_add(1)))
842 }
843 (Err(e), _) => Err(argument::Error::InvalidValue {
844 value: e.to_string(),
845 expected: String::from("invalid base range value"),
846 }),
847 (_, Err(e)) => Err(argument::Error::InvalidValue {
848 value: e.to_string(),
849 expected: String::from("invalid last range value"),
850 }),
851 _ => Err(argument::Error::InvalidValue {
852 value: s.to_owned(),
853 expected: String::from("invalid range format"),
854 }),
855 })
856 .collect();
857 Ok(DirectIoOption {
858 path,
859 ranges: ranges?,
860 })
861}
862
Zach Reiznerefe95782017-08-26 18:05:48 -0700863fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::Result<()> {
864 match name {
865 "" => {
Cody Schuffelen6d1ab502019-05-21 12:12:38 -0700866 if cfg.executable_path.is_some() {
867 return Err(argument::Error::TooManyArguments(format!(
868 "A VM executable was already specified: {:?}",
869 cfg.executable_path
870 )));
Zach Reiznerefe95782017-08-26 18:05:48 -0700871 }
Cody Schuffelen6d1ab502019-05-21 12:12:38 -0700872 let kernel_path = PathBuf::from(value.unwrap());
873 if !kernel_path.exists() {
874 return Err(argument::Error::InvalidValue {
875 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800876 expected: String::from("this kernel path does not exist"),
Cody Schuffelen6d1ab502019-05-21 12:12:38 -0700877 });
878 }
879 cfg.executable_path = Some(Executable::Kernel(kernel_path));
Zach Reiznerefe95782017-08-26 18:05:48 -0700880 }
Christian Blichmann33d56772021-03-04 19:03:54 +0100881 "kvm-device" => {
882 let kvm_device_path = PathBuf::from(value.unwrap());
883 if !kvm_device_path.exists() {
884 return Err(argument::Error::InvalidValue {
885 value: value.unwrap().to_owned(),
886 expected: String::from("this kvm device path does not exist"),
887 });
888 }
889
890 cfg.kvm_device_path = kvm_device_path;
891 }
Christian Blichmann2f5d4b62021-03-10 18:08:08 +0100892 "vhost-vsock-device" => {
893 let vhost_vsock_device_path = PathBuf::from(value.unwrap());
894 if !vhost_vsock_device_path.exists() {
895 return Err(argument::Error::InvalidValue {
896 value: value.unwrap().to_owned(),
897 expected: String::from("this vhost-vsock device path does not exist"),
898 });
899 }
900
901 cfg.vhost_vsock_device_path = vhost_vsock_device_path;
902 }
903 "vhost-net-device" => {
904 let vhost_net_device_path = PathBuf::from(value.unwrap());
905 if !vhost_net_device_path.exists() {
906 return Err(argument::Error::InvalidValue {
907 value: value.unwrap().to_owned(),
908 expected: String::from("this vhost-vsock device path does not exist"),
909 });
910 }
911
912 cfg.vhost_net_device_path = vhost_net_device_path;
913 }
Tristan Muntsinger4133b012018-12-21 16:01:56 -0800914 "android-fstab" => {
915 if cfg.android_fstab.is_some()
916 && !cfg.android_fstab.as_ref().unwrap().as_os_str().is_empty()
917 {
918 return Err(argument::Error::TooManyArguments(
919 "expected exactly one android fstab path".to_owned(),
920 ));
921 } else {
922 let android_fstab = PathBuf::from(value.unwrap());
923 if !android_fstab.exists() {
924 return Err(argument::Error::InvalidValue {
925 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800926 expected: String::from("this android fstab path does not exist"),
Tristan Muntsinger4133b012018-12-21 16:01:56 -0800927 });
928 }
929 cfg.android_fstab = Some(android_fstab);
930 }
931 }
Zach Reiznerefe95782017-08-26 18:05:48 -0700932 "params" => {
Zach Reiznerbb678712018-01-30 18:13:04 -0800933 cfg.params.push(value.unwrap().to_owned());
Zach Reiznerefe95782017-08-26 18:05:48 -0700934 }
935 "cpus" => {
936 if cfg.vcpu_count.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700937 return Err(argument::Error::TooManyArguments(
938 "`cpus` already given".to_owned(),
939 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700940 }
941 cfg.vcpu_count =
Zach Reizner55a9e502018-10-03 10:22:32 -0700942 Some(
943 value
944 .unwrap()
945 .parse()
946 .map_err(|_| argument::Error::InvalidValue {
947 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800948 expected: String::from("this value for `cpus` needs to be integer"),
Zach Reizner55a9e502018-10-03 10:22:32 -0700949 })?,
950 )
Zach Reiznerefe95782017-08-26 18:05:48 -0700951 }
Daniel Verkamp107edb32019-04-05 09:58:48 -0700952 "cpu-affinity" => {
Daniel Verkampc677fb42020-09-08 13:47:49 -0700953 if cfg.vcpu_affinity.is_some() {
Daniel Verkamp107edb32019-04-05 09:58:48 -0700954 return Err(argument::Error::TooManyArguments(
955 "`cpu-affinity` already given".to_owned(),
956 ));
957 }
Daniel Verkampc677fb42020-09-08 13:47:49 -0700958 cfg.vcpu_affinity = Some(parse_cpu_affinity(value.unwrap())?);
Daniel Verkamp107edb32019-04-05 09:58:48 -0700959 }
Daniel Verkamp8a72afc2021-03-15 17:55:52 -0700960 "cpu-cluster" => {
961 cfg.cpu_clusters.push(parse_cpu_set(value.unwrap())?);
962 }
963 "cpu-capacity" => {
964 parse_cpu_capacity(value.unwrap(), &mut cfg.cpu_capacity)?;
965 }
Suleiman Souhlal015c3c12020-10-07 14:15:41 +0900966 "no-smt" => {
967 cfg.no_smt = true;
968 }
Kansho Nishidaab205af2020-08-13 18:17:50 +0900969 "rt-cpus" => {
970 if !cfg.rt_cpus.is_empty() {
971 return Err(argument::Error::TooManyArguments(
972 "`rt-cpus` already given".to_owned(),
973 ));
974 }
975 cfg.rt_cpus = parse_cpu_set(value.unwrap())?;
976 }
Suleiman Souhlal63630e82021-02-18 11:53:11 +0900977 "delay-rt" => {
978 cfg.delay_rt = true;
979 }
Zach Reiznerefe95782017-08-26 18:05:48 -0700980 "mem" => {
981 if cfg.memory.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700982 return Err(argument::Error::TooManyArguments(
983 "`mem` already given".to_owned(),
984 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700985 }
986 cfg.memory =
Zach Reizner55a9e502018-10-03 10:22:32 -0700987 Some(
988 value
989 .unwrap()
990 .parse()
991 .map_err(|_| argument::Error::InvalidValue {
992 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800993 expected: String::from("this value for `mem` needs to be integer"),
Zach Reizner55a9e502018-10-03 10:22:32 -0700994 })?,
995 )
Zach Reiznerefe95782017-08-26 18:05:48 -0700996 }
Will Deaconc48e7832021-07-30 19:03:06 +0100997 #[cfg(target_arch = "aarch64")]
998 "swiotlb" => {
999 if cfg.swiotlb.is_some() {
1000 return Err(argument::Error::TooManyArguments(
1001 "`swiotlb` already given".to_owned(),
1002 ));
1003 }
1004 cfg.swiotlb =
1005 Some(
1006 value
1007 .unwrap()
1008 .parse()
1009 .map_err(|_| argument::Error::InvalidValue {
1010 value: value.unwrap().to_owned(),
1011 expected: String::from("this value for `swiotlb` needs to be integer"),
1012 })?,
1013 )
1014 }
Sergey Senozhatsky1e369c52021-04-13 20:23:51 +09001015 "hugepages" => {
1016 cfg.hugepages = true;
1017 }
Andrew Scull1590e6f2020-03-18 18:00:47 +00001018 #[cfg(feature = "audio")]
Judy Hsiaod5c1e962020-02-04 12:30:01 +08001019 "ac97" => {
1020 let ac97_params = parse_ac97_options(value.unwrap())?;
paulhsia16478242020-11-27 14:04:58 +08001021 // Add kernel parameters related to the intel8x0 driver for ac97 devices once.
1022 if cfg.ac97_parameters.is_empty() {
1023 // Set `inside_vm=1` to save some register read ops in the driver.
1024 cfg.params.push("snd_intel8x0.inside_vm=1".to_string());
1025 // Set `ac97_clock=48000` to save intel8x0_measure_ac97_clock call in the driver.
1026 cfg.params.push("snd_intel8x0.ac97_clock=48000".to_string());
1027 }
Judy Hsiaod5c1e962020-02-04 12:30:01 +08001028 cfg.ac97_parameters.push(ac97_params);
Dylan Reid3082e8e2019-01-07 10:33:48 -08001029 }
Jorge E. Moreirad4562d02021-06-28 16:21:12 -07001030 #[cfg(feature = "audio")]
1031 "sound" => {
1032 let client_path = PathBuf::from(value.unwrap());
1033 cfg.sound = Some(client_path);
1034 }
Trent Begin17ccaad2019-04-17 13:51:25 -06001035 "serial" => {
1036 let serial_params = parse_serial_options(value.unwrap())?;
1037 let num = serial_params.num;
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07001038 let key = (serial_params.hardware, num);
1039 if cfg.serial_parameters.contains_key(&key) {
Trent Begin17ccaad2019-04-17 13:51:25 -06001040 return Err(argument::Error::TooManyArguments(format!(
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07001041 "serial hardware {} num {}",
1042 serial_params.hardware, num,
Trent Begin17ccaad2019-04-17 13:51:25 -06001043 )));
1044 }
1045
1046 if serial_params.console {
Jakub Staronb6515a92019-06-05 15:18:25 -07001047 for params in cfg.serial_parameters.values() {
Trent Begin17ccaad2019-04-17 13:51:25 -06001048 if params.console {
1049 return Err(argument::Error::TooManyArguments(format!(
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07001050 "{} device {} already set as console",
1051 params.hardware, params.num,
1052 )));
1053 }
1054 }
1055 }
1056
1057 if serial_params.earlycon {
1058 // Only SerialHardware::Serial supports earlycon= currently.
1059 match serial_params.hardware {
1060 SerialHardware::Serial => {}
1061 _ => {
1062 return Err(argument::Error::InvalidValue {
Andrew Walbran9cfdbd92021-01-11 17:40:34 +00001063 value: serial_params.hardware.to_string(),
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07001064 expected: String::from("earlycon not supported for hardware"),
1065 });
1066 }
1067 }
1068 for params in cfg.serial_parameters.values() {
1069 if params.earlycon {
1070 return Err(argument::Error::TooManyArguments(format!(
1071 "{} device {} already set as earlycon",
1072 params.hardware, params.num,
Trent Begin17ccaad2019-04-17 13:51:25 -06001073 )));
1074 }
1075 }
1076 }
1077
Jorge E. Moreira1e262302019-08-01 14:40:03 -07001078 if serial_params.stdin {
1079 if let Some(previous_stdin) = cfg.serial_parameters.values().find(|sp| sp.stdin) {
1080 return Err(argument::Error::TooManyArguments(format!(
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07001081 "{} device {} already connected to standard input",
1082 previous_stdin.hardware, previous_stdin.num,
Jorge E. Moreira1e262302019-08-01 14:40:03 -07001083 )));
1084 }
1085 }
1086
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07001087 cfg.serial_parameters.insert(key, serial_params);
Trent Begin17ccaad2019-04-17 13:51:25 -06001088 }
1089 "syslog-tag" => {
1090 if cfg.syslog_tag.is_some() {
1091 return Err(argument::Error::TooManyArguments(
1092 "`syslog-tag` already given".to_owned(),
1093 ));
1094 }
1095 syslog::set_proc_name(value.unwrap());
1096 cfg.syslog_tag = Some(value.unwrap().to_owned());
1097 }
Daniel Verkamp4b62cd92019-11-08 13:09:27 -08001098 "root" | "rwroot" | "disk" | "rwdisk" => {
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001099 let param = value.unwrap();
1100 let mut components = param.split(',');
Daniel Verkamp6a8cd102019-06-26 15:17:46 -07001101 let read_only = !name.starts_with("rw");
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001102 let disk_path =
1103 PathBuf::from(
1104 components
1105 .next()
1106 .ok_or_else(|| argument::Error::InvalidValue {
1107 value: param.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001108 expected: String::from("missing disk path"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001109 })?,
1110 );
Zach Reiznerefe95782017-08-26 18:05:48 -07001111 if !disk_path.exists() {
1112 return Err(argument::Error::InvalidValue {
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001113 value: param.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001114 expected: String::from("this disk path does not exist"),
Zach Reizner55a9e502018-10-03 10:22:32 -07001115 });
Zach Reiznerefe95782017-08-26 18:05:48 -07001116 }
Daniel Verkamp6a8cd102019-06-26 15:17:46 -07001117 if name.ends_with("root") {
Daniel Verkampaac28132018-10-15 14:58:48 -07001118 if cfg.disks.len() >= 26 {
Zach Reizner55a9e502018-10-03 10:22:32 -07001119 return Err(argument::Error::TooManyArguments(
1120 "ran out of letters for to assign to root disk".to_owned(),
1121 ));
Zach Reiznerefe95782017-08-26 18:05:48 -07001122 }
Zach Reizner55a9e502018-10-03 10:22:32 -07001123 cfg.params.push(format!(
Daniel Verkamp6a8cd102019-06-26 15:17:46 -07001124 "root=/dev/vd{} {}",
1125 char::from(b'a' + cfg.disks.len() as u8),
1126 if read_only { "ro" } else { "rw" }
Zach Reizner55a9e502018-10-03 10:22:32 -07001127 ));
Zach Reiznerefe95782017-08-26 18:05:48 -07001128 }
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001129
1130 let mut disk = DiskOption {
Zach Reizner55a9e502018-10-03 10:22:32 -07001131 path: disk_path,
Daniel Verkamp6a8cd102019-06-26 15:17:46 -07001132 read_only,
Junichi Uekawa7bea39f2021-07-16 14:05:06 +09001133 o_direct: false,
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001134 sparse: true,
Daniel Verkamp27672232019-12-06 17:26:55 +11001135 block_size: 512,
Daniel Verkamp4e1f99a2020-06-01 12:47:21 -07001136 id: None,
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001137 };
1138
1139 for opt in components {
1140 let mut o = opt.splitn(2, '=');
1141 let kind = o.next().ok_or_else(|| argument::Error::InvalidValue {
1142 value: opt.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001143 expected: String::from("disk options must not be empty"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001144 })?;
1145 let value = o.next().ok_or_else(|| argument::Error::InvalidValue {
1146 value: opt.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001147 expected: String::from("disk options must be of the form `kind=value`"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001148 })?;
1149
1150 match kind {
1151 "sparse" => {
1152 let sparse = value.parse().map_err(|_| argument::Error::InvalidValue {
1153 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001154 expected: String::from("`sparse` must be a boolean"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001155 })?;
1156 disk.sparse = sparse;
1157 }
Junichi Uekawa7bea39f2021-07-16 14:05:06 +09001158 "o_direct" => {
1159 let o_direct =
1160 value.parse().map_err(|_| argument::Error::InvalidValue {
1161 value: value.to_owned(),
1162 expected: String::from("`o_direct` must be a boolean"),
1163 })?;
1164 disk.o_direct = o_direct;
1165 }
Daniel Verkamp27672232019-12-06 17:26:55 +11001166 "block_size" => {
1167 let block_size =
1168 value.parse().map_err(|_| argument::Error::InvalidValue {
1169 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001170 expected: String::from("`block_size` must be an integer"),
Daniel Verkamp27672232019-12-06 17:26:55 +11001171 })?;
1172 disk.block_size = block_size;
1173 }
Daniel Verkamp4e1f99a2020-06-01 12:47:21 -07001174 "id" => {
1175 if value.len() > DISK_ID_LEN {
1176 return Err(argument::Error::InvalidValue {
1177 value: value.to_owned(),
1178 expected: format!(
1179 "`id` must be {} or fewer characters",
1180 DISK_ID_LEN
1181 ),
1182 });
1183 }
1184 let mut id = [0u8; DISK_ID_LEN];
1185 // Slicing id to value's length will never panic
1186 // because we checked that value will fit into id above.
1187 id[..value.len()].copy_from_slice(value.as_bytes());
1188 disk.id = Some(id);
1189 }
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001190 _ => {
1191 return Err(argument::Error::InvalidValue {
1192 value: kind.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001193 expected: String::from("unrecognized disk option"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001194 });
1195 }
1196 }
1197 }
1198
1199 cfg.disks.push(disk);
Zach Reiznerefe95782017-08-26 18:05:48 -07001200 }
Jakub Starona3411ea2019-04-24 10:55:25 -07001201 "pmem-device" | "rw-pmem-device" => {
1202 let disk_path = PathBuf::from(value.unwrap());
1203 if !disk_path.exists() {
1204 return Err(argument::Error::InvalidValue {
1205 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001206 expected: String::from("this disk path does not exist"),
Jakub Starona3411ea2019-04-24 10:55:25 -07001207 });
1208 }
1209
1210 cfg.pmem_devices.push(DiskOption {
1211 path: disk_path,
1212 read_only: !name.starts_with("rw"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001213 sparse: false,
Junichi Uekawa7bea39f2021-07-16 14:05:06 +09001214 o_direct: false,
Michael Hoyle6b196952020-08-02 20:09:41 -07001215 block_size: base::pagesize() as u32,
Daniel Verkamp4e1f99a2020-06-01 12:47:21 -07001216 id: None,
Jakub Starona3411ea2019-04-24 10:55:25 -07001217 });
1218 }
Kansho Nishida282115b2019-12-18 13:13:14 +09001219 "pstore" => {
1220 if cfg.pstore.is_some() {
1221 return Err(argument::Error::TooManyArguments(
1222 "`pstore` already given".to_owned(),
1223 ));
1224 }
1225
1226 let value = value.unwrap();
1227 let components: Vec<&str> = value.split(',').collect();
1228 if components.len() != 2 {
1229 return Err(argument::Error::InvalidValue {
1230 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001231 expected: String::from(
1232 "pstore must have exactly 2 components: path=<path>,size=<size>",
1233 ),
Kansho Nishida282115b2019-12-18 13:13:14 +09001234 });
1235 }
1236 cfg.pstore = Some(Pstore {
1237 path: {
1238 if components[0].len() <= 5 || !components[0].starts_with("path=") {
1239 return Err(argument::Error::InvalidValue {
1240 value: components[0].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001241 expected: String::from("pstore path must follow with `path=`"),
Kansho Nishida282115b2019-12-18 13:13:14 +09001242 });
1243 };
1244 PathBuf::from(&components[0][5..])
1245 },
1246 size: {
1247 if components[1].len() <= 5 || !components[1].starts_with("size=") {
1248 return Err(argument::Error::InvalidValue {
1249 value: components[1].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001250 expected: String::from("pstore size must follow with `size=`"),
Kansho Nishida282115b2019-12-18 13:13:14 +09001251 });
1252 };
1253 components[1][5..]
1254 .parse()
1255 .map_err(|_| argument::Error::InvalidValue {
1256 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001257 expected: String::from("pstore size must be an integer"),
Kansho Nishida282115b2019-12-18 13:13:14 +09001258 })?
1259 },
1260 });
1261 }
Zach Reiznerefe95782017-08-26 18:05:48 -07001262 "host_ip" => {
Daniel Verkampaac28132018-10-15 14:58:48 -07001263 if cfg.host_ip.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -07001264 return Err(argument::Error::TooManyArguments(
1265 "`host_ip` already given".to_owned(),
1266 ));
Zach Reiznerefe95782017-08-26 18:05:48 -07001267 }
Daniel Verkampaac28132018-10-15 14:58:48 -07001268 cfg.host_ip =
Zach Reizner55a9e502018-10-03 10:22:32 -07001269 Some(
1270 value
1271 .unwrap()
1272 .parse()
1273 .map_err(|_| argument::Error::InvalidValue {
1274 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001275 expected: String::from("`host_ip` needs to be in the form \"x.x.x.x\""),
Zach Reizner55a9e502018-10-03 10:22:32 -07001276 })?,
1277 )
Zach Reiznerefe95782017-08-26 18:05:48 -07001278 }
1279 "netmask" => {
Daniel Verkampaac28132018-10-15 14:58:48 -07001280 if cfg.netmask.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -07001281 return Err(argument::Error::TooManyArguments(
1282 "`netmask` already given".to_owned(),
1283 ));
Zach Reiznerefe95782017-08-26 18:05:48 -07001284 }
Daniel Verkampaac28132018-10-15 14:58:48 -07001285 cfg.netmask =
Zach Reizner55a9e502018-10-03 10:22:32 -07001286 Some(
1287 value
1288 .unwrap()
1289 .parse()
1290 .map_err(|_| argument::Error::InvalidValue {
1291 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001292 expected: String::from("`netmask` needs to be in the form \"x.x.x.x\""),
Zach Reizner55a9e502018-10-03 10:22:32 -07001293 })?,
1294 )
Zach Reiznerefe95782017-08-26 18:05:48 -07001295 }
1296 "mac" => {
Daniel Verkampaac28132018-10-15 14:58:48 -07001297 if cfg.mac_address.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -07001298 return Err(argument::Error::TooManyArguments(
1299 "`mac` already given".to_owned(),
1300 ));
Zach Reiznerefe95782017-08-26 18:05:48 -07001301 }
Daniel Verkampaac28132018-10-15 14:58:48 -07001302 cfg.mac_address =
Zach Reizner55a9e502018-10-03 10:22:32 -07001303 Some(
1304 value
1305 .unwrap()
1306 .parse()
1307 .map_err(|_| argument::Error::InvalidValue {
1308 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001309 expected: String::from(
1310 "`mac` needs to be in the form \"XX:XX:XX:XX:XX:XX\"",
1311 ),
Zach Reizner55a9e502018-10-03 10:22:32 -07001312 })?,
1313 )
Zach Reiznerefe95782017-08-26 18:05:48 -07001314 }
Xiong Zhang773c7072020-03-20 10:39:55 +08001315 "net-vq-pairs" => {
1316 if cfg.net_vq_pairs.is_some() {
1317 return Err(argument::Error::TooManyArguments(
1318 "`net-vq-pairs` already given".to_owned(),
1319 ));
1320 }
1321 cfg.net_vq_pairs =
1322 Some(
1323 value
1324 .unwrap()
1325 .parse()
1326 .map_err(|_| argument::Error::InvalidValue {
1327 value: value.unwrap().to_owned(),
1328 expected: String::from(
1329 "this value for `net-vq-pairs` needs to be integer",
1330 ),
1331 })?,
1332 )
1333 }
1334
Stephen Barber28a5a612017-10-20 17:15:30 -07001335 "wayland-sock" => {
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09001336 let mut components = value.unwrap().split(',');
1337 let path =
1338 PathBuf::from(
1339 components
1340 .next()
1341 .ok_or_else(|| argument::Error::InvalidValue {
1342 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001343 expected: String::from("missing socket path"),
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09001344 })?,
1345 );
1346 let mut name = "";
1347 for c in components {
1348 let mut kv = c.splitn(2, '=');
1349 let (kind, value) = match (kv.next(), kv.next()) {
1350 (Some(kind), Some(value)) => (kind, value),
1351 _ => {
1352 return Err(argument::Error::InvalidValue {
1353 value: c.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001354 expected: String::from("option must be of the form `kind=value`"),
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09001355 })
1356 }
1357 };
1358 match kind {
1359 "name" => name = value,
1360 _ => {
1361 return Err(argument::Error::InvalidValue {
1362 value: kind.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001363 expected: String::from("unrecognized option"),
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09001364 })
1365 }
1366 }
Stephen Barber28a5a612017-10-20 17:15:30 -07001367 }
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09001368 if cfg.wayland_socket_paths.contains_key(name) {
1369 return Err(argument::Error::TooManyArguments(format!(
1370 "wayland socket name already used: '{}'",
1371 name
1372 )));
Stephen Barber28a5a612017-10-20 17:15:30 -07001373 }
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09001374 cfg.wayland_socket_paths.insert(name.to_string(), path);
Stephen Barber28a5a612017-10-20 17:15:30 -07001375 }
David Reveman52ba4e52018-04-22 21:42:09 -04001376 #[cfg(feature = "wl-dmabuf")]
Nicholas Vernefde29972021-07-06 22:02:05 +10001377 "wayland-dmabuf" => {}
Zach Reizner0f2cfb02019-06-19 17:46:03 -07001378 "x-display" => {
1379 if cfg.x_display.is_some() {
1380 return Err(argument::Error::TooManyArguments(
1381 "`x-display` already given".to_owned(),
1382 ));
1383 }
1384 cfg.x_display = Some(value.unwrap().to_owned());
1385 }
Zach Reizner65b98f12019-11-22 17:34:58 -08001386 "display-window-keyboard" => {
1387 cfg.display_window_keyboard = true;
1388 }
1389 "display-window-mouse" => {
1390 cfg.display_window_mouse = true;
1391 }
Zach Reiznerefe95782017-08-26 18:05:48 -07001392 "socket" => {
1393 if cfg.socket_path.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -07001394 return Err(argument::Error::TooManyArguments(
1395 "`socket` already given".to_owned(),
1396 ));
Zach Reiznerefe95782017-08-26 18:05:48 -07001397 }
1398 let mut socket_path = PathBuf::from(value.unwrap());
1399 if socket_path.is_dir() {
1400 socket_path.push(format!("crosvm-{}.sock", getpid()));
1401 }
1402 if socket_path.exists() {
1403 return Err(argument::Error::InvalidValue {
Zach Reizner55a9e502018-10-03 10:22:32 -07001404 value: socket_path.to_string_lossy().into_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001405 expected: String::from("this socket path already exists"),
Zach Reizner55a9e502018-10-03 10:22:32 -07001406 });
Zach Reiznerefe95782017-08-26 18:05:48 -07001407 }
1408 cfg.socket_path = Some(socket_path);
1409 }
Dylan Reidd0c9adc2017-10-02 19:04:50 -07001410 "disable-sandbox" => {
Lepton Wu9105e9f2019-03-14 11:38:31 -07001411 cfg.sandbox = false;
Dylan Reidd0c9adc2017-10-02 19:04:50 -07001412 }
Chirantan Ekbote88f9cba2017-08-28 09:51:18 -07001413 "cid" => {
Daniel Verkampaac28132018-10-15 14:58:48 -07001414 if cfg.cid.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -07001415 return Err(argument::Error::TooManyArguments(
1416 "`cid` alread given".to_owned(),
1417 ));
Chirantan Ekbote88f9cba2017-08-28 09:51:18 -07001418 }
Daniel Verkampaac28132018-10-15 14:58:48 -07001419 cfg.cid = Some(
1420 value
1421 .unwrap()
1422 .parse()
1423 .map_err(|_| argument::Error::InvalidValue {
1424 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001425 expected: String::from("this value for `cid` must be an unsigned integer"),
Daniel Verkampaac28132018-10-15 14:58:48 -07001426 })?,
1427 );
Chirantan Ekbote88f9cba2017-08-28 09:51:18 -07001428 }
Chirantan Ekboteebd56812018-04-16 19:32:04 -07001429 "shared-dir" => {
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001430 // This is formatted as multiple fields, each separated by ":". The first 2 fields are
1431 // fixed (src:tag). The rest may appear in any order:
1432 //
1433 // * type=TYPE - must be one of "p9" or "fs" (default: p9)
1434 // * uidmap=UIDMAP - a uid map in the format "inner outer count[,inner outer count]"
1435 // (default: "0 <current euid> 1")
1436 // * gidmap=GIDMAP - a gid map in the same format as uidmap
1437 // (default: "0 <current egid> 1")
1438 // * timeout=TIMEOUT - a timeout value in seconds, which indicates how long attributes
1439 // and directory contents should be considered valid (default: 5)
1440 // * cache=CACHE - one of "never", "always", or "auto" (default: auto)
1441 // * writeback=BOOL - indicates whether writeback caching should be enabled (default: false)
Chirantan Ekboteebd56812018-04-16 19:32:04 -07001442 let param = value.unwrap();
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001443 let mut components = param.split(':');
Zach Reizner55a9e502018-10-03 10:22:32 -07001444 let src =
1445 PathBuf::from(
1446 components
1447 .next()
1448 .ok_or_else(|| argument::Error::InvalidValue {
1449 value: param.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001450 expected: String::from("missing source path for `shared-dir`"),
Zach Reizner55a9e502018-10-03 10:22:32 -07001451 })?,
1452 );
1453 let tag = components
1454 .next()
1455 .ok_or_else(|| argument::Error::InvalidValue {
Chirantan Ekboteebd56812018-04-16 19:32:04 -07001456 value: param.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001457 expected: String::from("missing tag for `shared-dir`"),
David Tolnay2bac1e72018-12-12 14:33:42 -08001458 })?
1459 .to_owned();
Chirantan Ekboteebd56812018-04-16 19:32:04 -07001460
1461 if !src.is_dir() {
1462 return Err(argument::Error::InvalidValue {
1463 value: param.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001464 expected: String::from("source path for `shared-dir` must be a directory"),
Chirantan Ekboteebd56812018-04-16 19:32:04 -07001465 });
1466 }
1467
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001468 let mut shared_dir = SharedDir {
1469 src,
1470 tag,
1471 ..Default::default()
1472 };
1473 for opt in components {
1474 let mut o = opt.splitn(2, '=');
1475 let kind = o.next().ok_or_else(|| argument::Error::InvalidValue {
1476 value: opt.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001477 expected: String::from("`shared-dir` options must not be empty"),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001478 })?;
1479 let value = o.next().ok_or_else(|| argument::Error::InvalidValue {
1480 value: opt.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001481 expected: String::from("`shared-dir` options must be of the form `kind=value`"),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001482 })?;
1483
1484 match kind {
1485 "type" => {
1486 shared_dir.kind =
1487 value.parse().map_err(|_| argument::Error::InvalidValue {
1488 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001489 expected: String::from("`type` must be one of `fs` or `9p`"),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001490 })?
1491 }
1492 "uidmap" => shared_dir.uid_map = value.into(),
1493 "gidmap" => shared_dir.gid_map = value.into(),
1494 "timeout" => {
1495 let seconds = value.parse().map_err(|_| argument::Error::InvalidValue {
1496 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001497 expected: String::from("`timeout` must be an integer"),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001498 })?;
1499
1500 let dur = Duration::from_secs(seconds);
Andrew Walbran9cfdbd92021-01-11 17:40:34 +00001501 shared_dir.fs_cfg.entry_timeout = dur;
Chirantan Ekbote75ba8752020-10-27 18:33:02 +09001502 shared_dir.fs_cfg.attr_timeout = dur;
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001503 }
1504 "cache" => {
1505 let policy = value.parse().map_err(|_| argument::Error::InvalidValue {
1506 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001507 expected: String::from(
1508 "`cache` must be one of `never`, `always`, or `auto`",
1509 ),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001510 })?;
Chirantan Ekbote75ba8752020-10-27 18:33:02 +09001511 shared_dir.fs_cfg.cache_policy = policy;
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001512 }
1513 "writeback" => {
1514 let writeback =
1515 value.parse().map_err(|_| argument::Error::InvalidValue {
1516 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001517 expected: String::from("`writeback` must be a boolean"),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001518 })?;
Chirantan Ekbote75ba8752020-10-27 18:33:02 +09001519 shared_dir.fs_cfg.writeback = writeback;
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001520 }
Chirantan Ekboted994e512020-06-12 18:46:52 +09001521 "rewrite-security-xattrs" => {
1522 let rewrite_security_xattrs =
1523 value.parse().map_err(|_| argument::Error::InvalidValue {
1524 value: value.to_owned(),
1525 expected: String::from(
1526 "`rewrite-security-xattrs` must be a boolean",
1527 ),
1528 })?;
Chirantan Ekbote75ba8752020-10-27 18:33:02 +09001529 shared_dir.fs_cfg.rewrite_security_xattrs = rewrite_security_xattrs;
Chirantan Ekboted994e512020-06-12 18:46:52 +09001530 }
Chirantan Ekbote09357c82020-09-10 20:08:28 +09001531 "ascii_casefold" => {
1532 let ascii_casefold =
1533 value.parse().map_err(|_| argument::Error::InvalidValue {
1534 value: value.to_owned(),
1535 expected: String::from("`ascii_casefold` must be a boolean"),
1536 })?;
Chirantan Ekbote75ba8752020-10-27 18:33:02 +09001537 shared_dir.fs_cfg.ascii_casefold = ascii_casefold;
1538 shared_dir.p9_cfg.ascii_casefold = ascii_casefold;
Chirantan Ekbote09357c82020-09-10 20:08:28 +09001539 }
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001540 _ => {
1541 return Err(argument::Error::InvalidValue {
1542 value: kind.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001543 expected: String::from("unrecognized option for `shared-dir`"),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001544 })
1545 }
1546 }
1547 }
1548 cfg.shared_dirs.push(shared_dir);
Chirantan Ekboteebd56812018-04-16 19:32:04 -07001549 }
Dylan Reide026ef02017-10-02 19:03:52 -07001550 "seccomp-policy-dir" => {
Dylan Reidd0c9adc2017-10-02 19:04:50 -07001551 // `value` is Some because we are in this match so it's safe to unwrap.
Daniel Verkampaac28132018-10-15 14:58:48 -07001552 cfg.seccomp_policy_dir = PathBuf::from(value.unwrap());
Zach Reizner55a9e502018-10-03 10:22:32 -07001553 }
Zach Reizner44863792019-06-26 14:22:08 -07001554 "seccomp-log-failures" => {
Matt Delco45caf912019-11-13 08:11:09 -08001555 // A side-effect of this flag is to force the use of .policy files
1556 // instead of .bpf files (.bpf files are expected and assumed to be
1557 // compiled to fail an unpermitted action with "trap").
1558 // Normally crosvm will first attempt to use a .bpf file, and if
1559 // not present it will then try to use a .policy file. It's up
1560 // to the build to decide which of these files is present for
1561 // crosvm to use (for CrOS the build will use .bpf files for
1562 // x64 builds and .policy files for arm/arm64 builds).
1563 //
1564 // This flag will likely work as expected for builds that use
1565 // .policy files. For builds that only use .bpf files the initial
1566 // result when using this flag is likely to be a file-not-found
1567 // error (since the .policy files are not present).
1568 // For .bpf builds you can either 1) manually add the .policy files,
1569 // or 2) do not use this command-line parameter and instead
1570 // temporarily change the build by passing "log" rather than
1571 // "trap" as the "--default-action" to compile_seccomp_policy.py.
Zach Reizner44863792019-06-26 14:22:08 -07001572 cfg.seccomp_log_failures = true;
1573 }
Zach Reizner8864cb02018-01-16 17:59:03 -08001574 "plugin" => {
Cody Schuffelen6d1ab502019-05-21 12:12:38 -07001575 if cfg.executable_path.is_some() {
1576 return Err(argument::Error::TooManyArguments(format!(
1577 "A VM executable was already specified: {:?}",
1578 cfg.executable_path
1579 )));
Zach Reizner8864cb02018-01-16 17:59:03 -08001580 }
Zach Reiznercc30d582018-01-23 21:16:42 -08001581 let plugin = PathBuf::from(value.unwrap().to_owned());
1582 if plugin.is_relative() {
1583 return Err(argument::Error::InvalidValue {
Zach Reizner55a9e502018-10-03 10:22:32 -07001584 value: plugin.to_string_lossy().into_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001585 expected: String::from("the plugin path must be an absolute path"),
Zach Reizner55a9e502018-10-03 10:22:32 -07001586 });
Zach Reiznercc30d582018-01-23 21:16:42 -08001587 }
Cody Schuffelen6d1ab502019-05-21 12:12:38 -07001588 cfg.executable_path = Some(Executable::Plugin(plugin));
Zach Reizner55a9e502018-10-03 10:22:32 -07001589 }
Dmitry Torokhov0f1770d2018-05-11 10:57:16 -07001590 "plugin-root" => {
1591 cfg.plugin_root = Some(PathBuf::from(value.unwrap().to_owned()));
Zach Reizner55a9e502018-10-03 10:22:32 -07001592 }
Chirantan Ekboted41d7262018-11-16 16:37:45 -08001593 "plugin-mount" => {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -08001594 let mount = parse_plugin_mount_option(value.unwrap())?;
1595 cfg.plugin_mounts.push(mount);
Chirantan Ekboted41d7262018-11-16 16:37:45 -08001596 }
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08001597 "plugin-mount-file" => {
1598 let file = File::open(value.unwrap()).map_err(|_| argument::Error::InvalidValue {
1599 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001600 expected: String::from("unable to open `plugin-mount-file` file"),
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08001601 })?;
1602 let reader = BufReader::new(file);
1603 for l in reader.lines() {
1604 let line = l.unwrap();
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09001605 let trimmed_line = line.splitn(2, '#').next().unwrap().trim();
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08001606 if !trimmed_line.is_empty() {
1607 let mount = parse_plugin_mount_option(trimmed_line)?;
1608 cfg.plugin_mounts.push(mount);
1609 }
1610 }
1611 }
Dmitry Torokhov1a6262b2019-03-01 00:34:03 -08001612 "plugin-gid-map" => {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -08001613 let map = parse_plugin_gid_map_option(value.unwrap())?;
1614 cfg.plugin_gid_maps.push(map);
Dmitry Torokhov1a6262b2019-03-01 00:34:03 -08001615 }
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08001616 "plugin-gid-map-file" => {
1617 let file = File::open(value.unwrap()).map_err(|_| argument::Error::InvalidValue {
1618 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001619 expected: String::from("unable to open `plugin-gid-map-file` file"),
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08001620 })?;
1621 let reader = BufReader::new(file);
1622 for l in reader.lines() {
1623 let line = l.unwrap();
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09001624 let trimmed_line = line.splitn(2, '#').next().unwrap().trim();
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08001625 if !trimmed_line.is_empty() {
1626 let map = parse_plugin_gid_map_option(trimmed_line)?;
1627 cfg.plugin_gid_maps.push(map);
1628 }
1629 }
1630 }
Daniel Verkampaac28132018-10-15 14:58:48 -07001631 "vhost-net" => cfg.vhost_net = true,
Chirantan Ekbote5f787212018-05-31 15:31:31 -07001632 "tap-fd" => {
Jorge E. Moreirab7952802019-02-12 16:43:05 -08001633 cfg.tap_fd.push(
1634 value
1635 .unwrap()
1636 .parse()
1637 .map_err(|_| argument::Error::InvalidValue {
1638 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001639 expected: String::from(
1640 "this value for `tap-fd` must be an unsigned integer",
1641 ),
Jorge E. Moreirab7952802019-02-12 16:43:05 -08001642 })?,
1643 );
Chirantan Ekbote5f787212018-05-31 15:31:31 -07001644 }
Jason Macnakcc7070b2019-11-06 14:48:12 -08001645 #[cfg(feature = "gpu")]
Zach Reizner3a8100a2017-09-13 19:15:43 -07001646 "gpu" => {
Jason Macnakd659a0d2021-03-15 15:33:01 -07001647 if cfg.gpu_parameters.is_none() {
1648 cfg.gpu_parameters = Some(Default::default());
1649 }
1650 parse_gpu_options(value, cfg.gpu_parameters.as_mut().unwrap())?;
1651 }
1652 #[cfg(feature = "gpu")]
1653 "gpu-display" => {
1654 if cfg.gpu_parameters.is_none() {
1655 cfg.gpu_parameters = Some(Default::default());
1656 }
1657 parse_gpu_display_options(value, cfg.gpu_parameters.as_mut().unwrap())?;
Zach Reizner3a8100a2017-09-13 19:15:43 -07001658 }
David Tolnay43f8e212019-02-13 17:28:16 -08001659 "software-tpm" => {
1660 cfg.software_tpm = true;
1661 }
Jorge E. Moreira99d3f082019-03-07 10:59:54 -08001662 "single-touch" => {
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09001663 let mut it = value.unwrap().split(':');
Jorge E. Moreira99d3f082019-03-07 10:59:54 -08001664
1665 let mut single_touch_spec =
1666 TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned()));
1667 if let Some(width) = it.next() {
Kaiyi Libccb4eb2020-02-06 17:53:11 -08001668 single_touch_spec.set_width(width.trim().parse().unwrap());
Jorge E. Moreira99d3f082019-03-07 10:59:54 -08001669 }
1670 if let Some(height) = it.next() {
Kaiyi Libccb4eb2020-02-06 17:53:11 -08001671 single_touch_spec.set_height(height.trim().parse().unwrap());
Jorge E. Moreira99d3f082019-03-07 10:59:54 -08001672 }
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07001673 cfg.virtio_single_touch.push(single_touch_spec);
Jorge E. Moreira99d3f082019-03-07 10:59:54 -08001674 }
Tristan Muntsinger486cffc2020-09-29 22:05:41 +00001675 "multi-touch" => {
Tristan Muntsinger486cffc2020-09-29 22:05:41 +00001676 let mut it = value.unwrap().split(':');
1677
1678 let mut multi_touch_spec =
1679 TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned()));
1680 if let Some(width) = it.next() {
1681 multi_touch_spec.set_width(width.trim().parse().unwrap());
1682 }
1683 if let Some(height) = it.next() {
1684 multi_touch_spec.set_height(height.trim().parse().unwrap());
1685 }
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07001686 cfg.virtio_multi_touch.push(multi_touch_spec);
Tristan Muntsinger486cffc2020-09-29 22:05:41 +00001687 }
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001688 "trackpad" => {
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09001689 let mut it = value.unwrap().split(':');
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001690
1691 let mut trackpad_spec =
Jorge E. Moreira99d3f082019-03-07 10:59:54 -08001692 TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned()));
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001693 if let Some(width) = it.next() {
Kaiyi Libccb4eb2020-02-06 17:53:11 -08001694 trackpad_spec.set_width(width.trim().parse().unwrap());
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001695 }
1696 if let Some(height) = it.next() {
Kaiyi Libccb4eb2020-02-06 17:53:11 -08001697 trackpad_spec.set_height(height.trim().parse().unwrap());
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001698 }
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07001699 cfg.virtio_trackpad.push(trackpad_spec);
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001700 }
1701 "mouse" => {
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07001702 cfg.virtio_mice
1703 .push(PathBuf::from(value.unwrap().to_owned()));
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001704 }
1705 "keyboard" => {
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07001706 cfg.virtio_keyboard
1707 .push(PathBuf::from(value.unwrap().to_owned()));
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001708 }
Daniel Norman5e23df72021-03-11 10:11:02 -08001709 "switches" => {
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07001710 cfg.virtio_switches
1711 .push(PathBuf::from(value.unwrap().to_owned()));
Daniel Norman5e23df72021-03-11 10:11:02 -08001712 }
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001713 "evdev" => {
1714 let dev_path = PathBuf::from(value.unwrap());
1715 if !dev_path.exists() {
1716 return Err(argument::Error::InvalidValue {
1717 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001718 expected: String::from("this input device path does not exist"),
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001719 });
1720 }
1721 cfg.virtio_input_evdevs.push(dev_path);
1722 }
Miriam Zimmerman26ac9282019-01-29 21:21:48 -08001723 "split-irqchip" => {
1724 cfg.split_irqchip = true;
1725 }
Daniel Verkampe403f5c2018-12-11 16:29:26 -08001726 "initrd" => {
1727 cfg.initrd_path = Some(PathBuf::from(value.unwrap().to_owned()));
1728 }
Cody Schuffelen6d1ab502019-05-21 12:12:38 -07001729 "bios" => {
1730 if cfg.executable_path.is_some() {
1731 return Err(argument::Error::TooManyArguments(format!(
1732 "A VM executable was already specified: {:?}",
1733 cfg.executable_path
1734 )));
1735 }
1736 cfg.executable_path = Some(Executable::Bios(PathBuf::from(value.unwrap().to_owned())));
1737 }
Xiong Zhang17b0daf2019-04-23 17:14:50 +08001738 "vfio" => {
Zide Chendfc4b882021-03-10 16:35:37 -08001739 let mut param = value.unwrap().split(',');
1740 let vfio_path =
1741 PathBuf::from(param.next().ok_or_else(|| argument::Error::InvalidValue {
1742 value: value.unwrap().to_owned(),
1743 expected: String::from("missing vfio path"),
1744 })?);
Xiong Zhang17b0daf2019-04-23 17:14:50 +08001745 if !vfio_path.exists() {
1746 return Err(argument::Error::InvalidValue {
1747 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001748 expected: String::from("the vfio path does not exist"),
Xiong Zhang17b0daf2019-04-23 17:14:50 +08001749 });
1750 }
1751 if !vfio_path.is_dir() {
1752 return Err(argument::Error::InvalidValue {
1753 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001754 expected: String::from("the vfio path should be directory"),
Xiong Zhang17b0daf2019-04-23 17:14:50 +08001755 });
1756 }
1757
Zide Chendfc4b882021-03-10 16:35:37 -08001758 let mut enable_iommu = false;
1759 if let Some(p) = param.next() {
1760 let mut kv = p.splitn(2, '=');
1761 if let (Some(kind), Some(value)) = (kv.next(), kv.next()) {
1762 match kind {
1763 "iommu" => (),
1764 _ => {
1765 return Err(argument::Error::InvalidValue {
1766 value: p.to_owned(),
1767 expected: String::from("option must be `iommu=on|off`"),
1768 })
1769 }
1770 }
1771 match value {
1772 "on" => enable_iommu = true,
1773 "off" => (),
1774 _ => {
1775 return Err(argument::Error::InvalidValue {
1776 value: p.to_owned(),
1777 expected: String::from("option must be `iommu=on|off`"),
1778 })
1779 }
1780 }
1781 };
1782 }
1783
1784 cfg.vfio.insert(vfio_path, enable_iommu);
Xiong Zhang17b0daf2019-04-23 17:14:50 +08001785 }
Keiichi Watanabe57df6a02019-12-06 22:24:40 +09001786 "video-decoder" => {
1787 cfg.video_dec = true;
1788 }
1789 "video-encoder" => {
1790 cfg.video_enc = true;
1791 }
Tomasz Jeznach42644642020-05-20 23:27:59 -07001792 "acpi-table" => {
1793 let acpi_table = PathBuf::from(value.unwrap());
1794 if !acpi_table.exists() {
1795 return Err(argument::Error::InvalidValue {
1796 value: value.unwrap().to_owned(),
1797 expected: String::from("the acpi-table path does not exist"),
1798 });
1799 }
1800 if !acpi_table.is_file() {
1801 return Err(argument::Error::InvalidValue {
1802 value: value.unwrap().to_owned(),
1803 expected: String::from("the acpi-table path should be a file"),
1804 });
1805 }
1806
1807 cfg.acpi_tables.push(acpi_table);
1808 }
Will Deacon6560c182020-10-06 18:47:18 +01001809 "protected-vm" => {
Andrew Walbran413f8542021-01-08 13:29:03 +00001810 cfg.protected_vm = ProtectionType::Protected;
Will Deacon6560c182020-10-06 18:47:18 +01001811 }
Chuanxiao Dongfd5626c2020-04-27 16:35:33 +08001812 "battery" => {
1813 let params = parse_battery_options(value)?;
1814 cfg.battery_type = Some(params);
1815 }
Keiichi Watanabec5262e92020-10-21 15:57:33 +09001816 #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
1817 "gdb" => {
1818 let port = value
1819 .unwrap()
1820 .parse()
1821 .map_err(|_| argument::Error::InvalidValue {
1822 value: value.unwrap().to_owned(),
1823 expected: String::from("expected a valid port number"),
1824 })?;
1825 cfg.gdb = Some(port);
1826 }
Charles William Dick0e3d4b62020-12-14 12:16:46 +09001827 "balloon_bias_mib" => {
1828 cfg.balloon_bias =
1829 value
1830 .unwrap()
1831 .parse::<i64>()
1832 .map_err(|_| argument::Error::InvalidValue {
1833 value: value.unwrap().to_owned(),
1834 expected: String::from("expected a valid ballon bias in MiB"),
1835 })?
1836 * 1024
1837 * 1024; // cfg.balloon_bias is in bytes.
1838 }
Keiichi Watanabef3a37f42021-01-21 15:41:11 +09001839 "vhost-user-blk" => cfg.vhost_user_blk.push(VhostUserOption {
1840 socket: PathBuf::from(value.unwrap()),
1841 }),
Federico 'Morg' Pareschi70fc7de2021-04-08 15:43:13 +09001842 "vhost-user-console" => cfg.vhost_user_console.push(VhostUserOption {
1843 socket: PathBuf::from(value.unwrap()),
1844 }),
JaeMan Parkeb9cc532021-07-02 15:02:59 +09001845 "vhost-user-mac80211-hwsim" => {
1846 cfg.vhost_user_mac80211_hwsim = Some(VhostUserOption {
1847 socket: PathBuf::from(value.unwrap()),
1848 });
1849 }
Keiichi Watanabe60686582021-03-12 04:53:51 +09001850 "vhost-user-net" => cfg.vhost_user_net.push(VhostUserOption {
1851 socket: PathBuf::from(value.unwrap()),
1852 }),
Chirantan Ekbote2ee9dcd2021-05-26 18:21:44 +09001853 "vhost-user-wl" => {
1854 let mut components = value.unwrap().splitn(2, ":");
1855 let socket = components.next().map(PathBuf::from).ok_or_else(|| {
1856 argument::Error::InvalidValue {
1857 value: value.unwrap().to_owned(),
1858 expected: String::from("missing socket path"),
1859 }
1860 })?;
1861 let vm_tube = components.next().map(PathBuf::from).ok_or_else(|| {
1862 argument::Error::InvalidValue {
1863 value: value.unwrap().to_owned(),
1864 expected: String::from("missing vm tube path"),
1865 }
1866 })?;
1867 cfg.vhost_user_wl
1868 .push(VhostUserWlOption { socket, vm_tube });
1869 }
Woody Chow5890b702021-02-12 14:57:02 +09001870 "vhost-user-fs" => {
1871 // (socket:tag)
1872 let param = value.unwrap();
1873 let mut components = param.split(':');
1874 let socket =
1875 PathBuf::from(
1876 components
1877 .next()
1878 .ok_or_else(|| argument::Error::InvalidValue {
1879 value: param.to_owned(),
1880 expected: String::from("missing socket path for `vhost-user-fs`"),
1881 })?,
1882 );
1883 let tag = components
1884 .next()
1885 .ok_or_else(|| argument::Error::InvalidValue {
1886 value: param.to_owned(),
1887 expected: String::from("missing tag for `vhost-user-fs`"),
1888 })?
1889 .to_owned();
1890 cfg.vhost_user_fs.push(VhostUserFsOption { socket, tag });
1891 }
Tomasz Jeznach3ce74762021-02-26 01:01:53 -08001892 #[cfg(feature = "direct")]
1893 "direct-pmio" => {
1894 if cfg.direct_pmio.is_some() {
1895 return Err(argument::Error::TooManyArguments(
1896 "`direct_pmio` already given".to_owned(),
1897 ));
1898 }
1899 cfg.direct_pmio = Some(parse_direct_io_options(value)?);
1900 }
Tomasz Jeznach7271f752021-03-04 01:44:06 -08001901 #[cfg(feature = "direct")]
1902 "direct-level-irq" => {
1903 cfg.direct_level_irq
1904 .push(
1905 value
1906 .unwrap()
1907 .parse()
1908 .map_err(|_| argument::Error::InvalidValue {
1909 value: value.unwrap().to_owned(),
1910 expected: String::from(
1911 "this value for `direct-level-irq` must be an unsigned integer",
1912 ),
1913 })?,
1914 );
1915 }
1916 #[cfg(feature = "direct")]
1917 "direct-edge-irq" => {
1918 cfg.direct_edge_irq
1919 .push(
1920 value
1921 .unwrap()
1922 .parse()
1923 .map_err(|_| argument::Error::InvalidValue {
1924 value: value.unwrap().to_owned(),
1925 expected: String::from(
1926 "this value for `direct-edge-irq` must be an unsigned integer",
1927 ),
1928 })?,
1929 );
1930 }
Tomasz Jeznachccb26942021-03-30 22:44:11 -07001931 "dmi" => {
1932 if cfg.dmi_path.is_some() {
1933 return Err(argument::Error::TooManyArguments(
1934 "`dmi` already given".to_owned(),
1935 ));
1936 }
1937 let dmi_path = PathBuf::from(value.unwrap());
1938 if !dmi_path.exists() {
1939 return Err(argument::Error::InvalidValue {
1940 value: value.unwrap().to_owned(),
1941 expected: String::from("the dmi path does not exist"),
1942 });
1943 }
1944 if !dmi_path.is_dir() {
1945 return Err(argument::Error::InvalidValue {
1946 value: value.unwrap().to_owned(),
1947 expected: String::from("the dmi path should be directory"),
1948 });
1949 }
1950 cfg.dmi_path = Some(dmi_path);
1951 }
Tomasz Jeznachd93c29f2021-04-12 11:00:24 -07001952 "no-legacy" => {
1953 cfg.no_legacy = true;
1954 }
Zach Reiznerefe95782017-08-26 18:05:48 -07001955 "help" => return Err(argument::Error::PrintHelp),
1956 _ => unreachable!(),
1957 }
1958 Ok(())
1959}
1960
Kaiyi Libccb4eb2020-02-06 17:53:11 -08001961fn validate_arguments(cfg: &mut Config) -> std::result::Result<(), argument::Error> {
1962 if cfg.executable_path.is_none() {
1963 return Err(argument::Error::ExpectedArgument("`KERNEL`".to_owned()));
1964 }
1965 if cfg.host_ip.is_some() || cfg.netmask.is_some() || cfg.mac_address.is_some() {
1966 if cfg.host_ip.is_none() {
1967 return Err(argument::Error::ExpectedArgument(
1968 "`host_ip` missing from network config".to_owned(),
1969 ));
1970 }
1971 if cfg.netmask.is_none() {
1972 return Err(argument::Error::ExpectedArgument(
1973 "`netmask` missing from network config".to_owned(),
1974 ));
1975 }
1976 if cfg.mac_address.is_none() {
1977 return Err(argument::Error::ExpectedArgument(
1978 "`mac` missing from network config".to_owned(),
1979 ));
1980 }
1981 }
1982 if cfg.plugin_root.is_some() && !executable_is_plugin(&cfg.executable_path) {
1983 return Err(argument::Error::ExpectedArgument(
1984 "`plugin-root` requires `plugin`".to_owned(),
1985 ));
1986 }
1987 #[cfg(feature = "gpu")]
1988 {
Jason Macnakd659a0d2021-03-15 15:33:01 -07001989 if let Some(gpu_parameters) = cfg.gpu_parameters.as_mut() {
1990 if gpu_parameters.displays.is_empty() {
1991 gpu_parameters.displays.push(GpuDisplayParameters {
1992 width: DEFAULT_DISPLAY_WIDTH,
1993 height: DEFAULT_DISPLAY_HEIGHT,
1994 });
1995 }
1996
1997 let width = gpu_parameters.displays[0].width;
1998 let height = gpu_parameters.displays[0].height;
1999
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07002000 if let Some(virtio_multi_touch) = cfg.virtio_multi_touch.first_mut() {
Tristan Muntsinger486cffc2020-09-29 22:05:41 +00002001 virtio_multi_touch.set_default_size(width, height);
2002 }
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07002003 if let Some(virtio_single_touch) = cfg.virtio_single_touch.first_mut() {
Kaiyi Libccb4eb2020-02-06 17:53:11 -08002004 virtio_single_touch.set_default_size(width, height);
2005 }
2006 }
2007 }
Keiichi Watanabec5262e92020-10-21 15:57:33 +09002008 #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
2009 if cfg.gdb.is_some() {
2010 if cfg.vcpu_count.unwrap_or(1) != 1 {
2011 return Err(argument::Error::ExpectedArgument(
2012 "`gdb` requires the number of vCPU to be 1".to_owned(),
2013 ));
2014 }
2015 }
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07002016 set_default_serial_parameters(&mut cfg.serial_parameters);
Kaiyi Libccb4eb2020-02-06 17:53:11 -08002017 Ok(())
2018}
2019
Dylan Reidbfba9932018-02-05 15:51:59 -08002020fn run_vm(args: std::env::Args) -> std::result::Result<(), ()> {
Zach Reiznerefe95782017-08-26 18:05:48 -07002021 let arguments =
2022 &[Argument::positional("KERNEL", "bzImage of kernel to run"),
Christian Blichmann33d56772021-03-04 19:03:54 +01002023 Argument::value("kvm-device", "PATH", "Path to the KVM device. (default /dev/kvm)"),
Christian Blichmann2f5d4b62021-03-10 18:08:08 +01002024 Argument::value("vhost-vsock-device", "PATH", "Path to the vhost-vsock device. (default /dev/vhost-vsock)"),
2025 Argument::value("vhost-net-device", "PATH", "Path to the vhost-net device. (default /dev/vhost-net)"),
Tristan Muntsinger4133b012018-12-21 16:01:56 -08002026 Argument::value("android-fstab", "PATH", "Path to Android fstab"),
Daniel Verkampe403f5c2018-12-11 16:29:26 -08002027 Argument::short_value('i', "initrd", "PATH", "Initial ramdisk to load."),
Zach Reiznerefe95782017-08-26 18:05:48 -07002028 Argument::short_value('p',
2029 "params",
2030 "PARAMS",
Zach Reiznerbb678712018-01-30 18:13:04 -08002031 "Extra kernel or plugin command line arguments. Can be given more than once."),
Zach Reiznerefe95782017-08-26 18:05:48 -07002032 Argument::short_value('c', "cpus", "N", "Number of VCPUs. (default: 1)"),
Daniel Verkampc677fb42020-09-08 13:47:49 -07002033 Argument::value("cpu-affinity", "CPUSET", "Comma-separated list of CPUs or CPU ranges to run VCPUs on (e.g. 0,1-3,5)
2034 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 -07002035 Argument::value("cpu-cluster", "CPUSET", "Group the given CPUs into a cluster (default: no clusters)"),
2036 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 +09002037 Argument::flag("no-smt", "Don't use SMT in the guest"),
Kansho Nishidaab205af2020-08-13 18:17:50 +09002038 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)"),
Suleiman Souhlal63630e82021-02-18 11:53:11 +09002039 Argument::flag("delay-rt", "Don't set VCPUs real-time until make-rt command is run"),
Zach Reiznerefe95782017-08-26 18:05:48 -07002040 Argument::short_value('m',
2041 "mem",
2042 "N",
2043 "Amount of guest memory in MiB. (default: 256)"),
Sergey Senozhatsky1e369c52021-04-13 20:23:51 +09002044 Argument::flag("hugepages", "Advise the kernel to use Huge Pages for guest memory mappings."),
Zach Reiznerefe95782017-08-26 18:05:48 -07002045 Argument::short_value('r',
2046 "root",
Daniel Verkampe73c80f2019-11-08 10:11:16 -08002047 "PATH[,key=value[,key=value[,...]]",
2048 "Path to a root disk image followed by optional comma-separated options.
2049 Like `--disk` but adds appropriate kernel command line option.
2050 See --disk for valid options."),
2051 Argument::value("rwroot", "PATH[,key=value[,key=value[,...]]", "Path to a writable root disk image followed by optional comma-separated options.
2052 See --disk for valid options."),
2053 Argument::short_value('d', "disk", "PATH[,key=value[,key=value[,...]]", "Path to a disk image followed by optional comma-separated options.
2054 Valid keys:
Daniel Verkamp27672232019-12-06 17:26:55 +11002055 sparse=BOOL - Indicates whether the disk should support the discard operation (default: true)
Daniel Verkamp4e1f99a2020-06-01 12:47:21 -07002056 block_size=BYTES - Set the reported block size of the disk (default: 512)
Junichi Uekawa7bea39f2021-07-16 14:05:06 +09002057 id=STRING - Set the block device identifier to an ASCII string, up to 20 characters (default: no ID)
2058 o_direct=BOOL - Use O_DIRECT mode to bypass page cache"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -08002059 Argument::value("rwdisk", "PATH[,key=value[,key=value[,...]]", "Path to a writable disk image followed by optional comma-separated options.
2060 See --disk for valid options."),
Jakub Starona3411ea2019-04-24 10:55:25 -07002061 Argument::value("rw-pmem-device", "PATH", "Path to a writable disk image."),
2062 Argument::value("pmem-device", "PATH", "Path to a disk image."),
Kansho Nishida282115b2019-12-18 13:13:14 +09002063 Argument::value("pstore", "path=PATH,size=SIZE", "Path to pstore buffer backend file follewed by size."),
Zach Reiznerefe95782017-08-26 18:05:48 -07002064 Argument::value("host_ip",
2065 "IP",
2066 "IP address to assign to host tap interface."),
2067 Argument::value("netmask", "NETMASK", "Netmask for VM subnet."),
2068 Argument::value("mac", "MAC", "MAC address for VM."),
Xiong Zhang773c7072020-03-20 10:39:55 +08002069 Argument::value("net-vq-pairs", "N", "virtio net virtual queue paris. (default: 1)"),
Andrew Scull1590e6f2020-03-18 18:00:47 +00002070 #[cfg(feature = "audio")]
Judy Hsiaod5c1e962020-02-04 12:30:01 +08002071 Argument::value("ac97",
paulhsia83d51602021-03-09 17:13:14 +08002072 "[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 +08002073 "Comma separated key=value pairs for setting up Ac97 devices. Can be given more than once .
Junichi Uekawaaab74e42021-07-19 16:12:46 +09002074 Possible key values:
2075 backend=(null, cras, vios) - Where to route the audio device. If not provided, backend will default to null.
2076 `null` for /dev/null, cras for CRAS server and vios for VioS server.
2077 capture - Enable audio capture
2078 capture_effects - | separated effects to be enabled for recording. The only supported effect value now is EchoCancellation or aec.
2079 client_type - Set specific client type for cras backend.
2080 server - The to the VIOS server (unix socket)."),
Jorge E. Moreirad4562d02021-06-28 16:21:12 -07002081 #[cfg(feature = "audio")]
2082 Argument::value("sound", "[PATH]", "Path to the VioS server socket for setting up virtio-snd devices."),
Trent Begin17ccaad2019-04-17 13:51:25 -06002083 Argument::value("serial",
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07002084 "type=TYPE,[hardware=HW,num=NUM,path=PATH,input=PATH,console,earlycon,stdin]",
Jason Macnakcc7070b2019-11-06 14:48:12 -08002085 "Comma separated key=value pairs for setting up serial devices. Can be given more than once.
Junichi Uekawaaab74e42021-07-19 16:12:46 +09002086 Possible key values:
2087 type=(stdout,syslog,sink,file) - Where to route the serial device
2088 hardware=(serial,virtio-console) - Which type of serial hardware to emulate. Defaults to 8250 UART (serial).
2089 num=(1,2,3,4) - Serial Device Number. If not provided, num will default to 1.
2090 path=PATH - The path to the file to write to when type=file
2091 input=PATH - The path to the file to read from when not stdin
2092 console - Use this serial device as the guest console. Can only be given once. Will default to first serial port if not provided.
2093 earlycon - Use this serial device as the early console. Can only be given once.
2094 stdin - Direct standard input to this serial device. Can only be given once. Will default to first serial port if not provided.
2095 "),
Trent Begin17ccaad2019-04-17 13:51:25 -06002096 Argument::value("syslog-tag", "TAG", "When logging to syslog, use the provided tag."),
Zach Reizner0f2cfb02019-06-19 17:46:03 -07002097 Argument::value("x-display", "DISPLAY", "X11 display name to use."),
Zach Reizner65b98f12019-11-22 17:34:58 -08002098 Argument::flag("display-window-keyboard", "Capture keyboard input from the display window."),
2099 Argument::flag("display-window-mouse", "Capture keyboard input from the display window."),
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09002100 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 -04002101 #[cfg(feature = "wl-dmabuf")]
Nicholas Vernefde29972021-07-06 22:02:05 +10002102 Argument::flag("wayland-dmabuf", "DEPRECATED: Enable support for DMABufs in Wayland device."),
Zach Reiznerefe95782017-08-26 18:05:48 -07002103 Argument::short_value('s',
2104 "socket",
2105 "PATH",
2106 "Path to put the control socket. If PATH is a directory, a name will be generated."),
Dylan Reidd0c9adc2017-10-02 19:04:50 -07002107 Argument::flag("disable-sandbox", "Run all devices in one, non-sandboxed process."),
Chirantan Ekboteebd56812018-04-16 19:32:04 -07002108 Argument::value("cid", "CID", "Context ID for virtual sockets."),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09002109 Argument::value("shared-dir", "PATH:TAG[:type=TYPE:writeback=BOOL:timeout=SECONDS:uidmap=UIDMAP:gidmap=GIDMAP:cache=CACHE]",
2110 "Colon-separated options for configuring a directory to be shared with the VM.
Junichi Uekawaaab74e42021-07-19 16:12:46 +09002111 The first field is the directory to be shared and the second field is the tag that the VM can use to identify the device.
2112 The remaining fields are key=value pairs that may appear in any order. Valid keys are:
2113 type=(p9, fs) - Indicates whether the directory should be shared via virtio-9p or virtio-fs (default: p9).
2114 uidmap=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).
2115 gidmap=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).
2116 cache=(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.
2117 timeout=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.
2118 writeback=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.
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09002119"),
Dylan Reide026ef02017-10-02 19:03:52 -07002120 Argument::value("seccomp-policy-dir", "PATH", "Path to seccomp .policy files."),
Zach Reizner44863792019-06-26 14:22:08 -07002121 Argument::flag("seccomp-log-failures", "Instead of seccomp filter failures being fatal, they will be logged instead."),
Zach Reizner8864cb02018-01-16 17:59:03 -08002122 #[cfg(feature = "plugin")]
Zach Reiznercc30d582018-01-23 21:16:42 -08002123 Argument::value("plugin", "PATH", "Absolute path to plugin process to run under crosvm."),
Daniel Verkampbd1a0842019-01-08 15:50:34 -08002124 #[cfg(feature = "plugin")]
Dmitry Torokhov0f1770d2018-05-11 10:57:16 -07002125 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 -08002126 #[cfg(feature = "plugin")]
Chirantan Ekboted41d7262018-11-16 16:37:45 -08002127 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 -08002128 #[cfg(feature = "plugin")]
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08002129 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."),
2130 #[cfg(feature = "plugin")]
Dmitry Torokhov1a6262b2019-03-01 00:34:03 -08002131 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 -08002132 #[cfg(feature = "plugin")]
2133 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 +00002134 Argument::flag("vhost-net", "Use vhost for networking."),
Chirantan Ekbote5f787212018-05-31 15:31:31 -07002135 Argument::value("tap-fd",
2136 "fd",
Jorge E. Moreirab7952802019-02-12 16:43:05 -08002137 "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 -07002138 #[cfg(feature = "gpu")]
Jason Macnakcc7070b2019-11-06 14:48:12 -08002139 Argument::flag_or_value("gpu",
2140 "[width=INT,height=INT]",
2141 "(EXPERIMENTAL) Comma separated key=value pairs for setting up a virtio-gpu device
Junichi Uekawaaab74e42021-07-19 16:12:46 +09002142 Possible key values:
2143 backend=(2d|virglrenderer|gfxstream) - Which backend to use for virtio-gpu (determining rendering protocol)
2144 width=INT - The width of the virtual display connected to the virtio-gpu.
2145 height=INT - The height of the virtual display connected to the virtio-gpu.
2146 egl[=true|=false] - If the backend should use a EGL context for rendering.
2147 glx[=true|=false] - If the backend should use a GLX context for rendering.
2148 surfaceless[=true|=false] - If the backend should use a surfaceless context for rendering.
2149 angle[=true|=false] - If the gfxstream backend should use ANGLE (OpenGL on Vulkan) as its native OpenGL driver.
2150 syncfd[=true|=false] - If the gfxstream backend should support EGL_ANDROID_native_fence_sync
2151 vulkan[=true|=false] - If the backend should support vulkan"),
Jason Macnakd659a0d2021-03-15 15:33:01 -07002152 #[cfg(feature = "gpu")]
2153 Argument::flag_or_value("gpu-display",
2154 "[width=INT,height=INT]",
2155 "(EXPERIMENTAL) Comma separated key=value pairs for setting up a display on the virtio-gpu device
Junichi Uekawaaab74e42021-07-19 16:12:46 +09002156 Possible key values:
2157 width=INT - The width of the virtual display connected to the virtio-gpu.
2158 height=INT - The height of the virtual display connected to the virtio-gpu."),
David Tolnay43f8e212019-02-13 17:28:16 -08002159 #[cfg(feature = "tpm")]
2160 Argument::flag("software-tpm", "enable a software emulated trusted platform module device"),
Jorge E. Moreiradffec502019-01-14 18:44:49 -08002161 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 -08002162 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 +00002163 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 -08002164 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)."),
2165 Argument::value("mouse", "PATH", "Path to a socket from where to read mouse input events and write status updates to."),
2166 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 -08002167 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 -08002168 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
2169 Argument::flag("split-irqchip", "(EXPERIMENTAL) enable split-irqchip support"),
Cody Schuffelen6d1ab502019-05-21 12:12:38 -07002170 Argument::value("bios", "PATH", "Path to BIOS/firmware ROM"),
Zide Chendfc4b882021-03-10 16:35:37 -08002171 Argument::value("vfio", "PATH[,iommu=on|off]", "Path to sysfs of pass through or mdev device.
2172iommu=on|off - indicates whether to enable virtio IOMMU for this device"),
Keiichi Watanabe57df6a02019-12-06 22:24:40 +09002173 #[cfg(feature = "video-decoder")]
2174 Argument::flag("video-decoder", "(EXPERIMENTAL) enable virtio-video decoder device"),
2175 #[cfg(feature = "video-encoder")]
2176 Argument::flag("video-encoder", "(EXPERIMENTAL) enable virtio-video encoder device"),
Tomasz Jeznach42644642020-05-20 23:27:59 -07002177 Argument::value("acpi-table", "PATH", "Path to user provided ACPI table"),
Will Deacon6560c182020-10-06 18:47:18 +01002178 Argument::flag("protected-vm", "(EXPERIMENTAL) prevent host access to guest memory"),
Will Deaconc48e7832021-07-30 19:03:06 +01002179 #[cfg(target_arch = "aarch64")]
2180 Argument::value("swiotlb", "N", "(EXPERIMENTAL) Size of virtio swiotlb buffer in MiB (default: 64 if `--protected-vm` is present)."),
Chuanxiao Dongfd5626c2020-04-27 16:35:33 +08002181 Argument::flag_or_value("battery",
2182 "[type=TYPE]",
2183 "Comma separated key=value pairs for setting up battery device
Junichi Uekawaaab74e42021-07-19 16:12:46 +09002184 Possible key values:
2185 type=goldfish - type of battery emulation, defaults to goldfish"),
Keiichi Watanabec5262e92020-10-21 15:57:33 +09002186 Argument::value("gdb", "PORT", "(EXPERIMENTAL) gdb on the given port"),
Charles William Dick0e3d4b62020-12-14 12:16:46 +09002187 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 +09002188 Argument::value("vhost-user-blk", "SOCKET_PATH", "Path to a socket for vhost-user block"),
Federico 'Morg' Pareschi70fc7de2021-04-08 15:43:13 +09002189 Argument::value("vhost-user-console", "SOCKET_PATH", "Path to a socket for vhost-user console"),
JaeMan Parkeb9cc532021-07-02 15:02:59 +09002190 Argument::value("vhost-user-mac80211-hwsim", "SOCKET_PATH", "Path to a socket for vhost-user mac80211_hwsim"),
Keiichi Watanabe60686582021-03-12 04:53:51 +09002191 Argument::value("vhost-user-net", "SOCKET_PATH", "Path to a socket for vhost-user net"),
Chirantan Ekbote2ee9dcd2021-05-26 18:21:44 +09002192 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 +09002193 Argument::value("vhost-user-fs", "SOCKET_PATH:TAG",
2194 "Path to a socket path for vhost-user fs, and tag for the shared dir"),
Tomasz Jeznach3ce74762021-02-26 01:01:53 -08002195 #[cfg(feature = "direct")]
2196 Argument::value("direct-pmio", "PATH@RANGE[,RANGE[,...]]", "Path and ranges for direct port I/O access"),
Tomasz Jeznach7271f752021-03-04 01:44:06 -08002197 #[cfg(feature = "direct")]
2198 Argument::value("direct-level-irq", "irq", "Enable interrupt passthrough"),
2199 #[cfg(feature = "direct")]
2200 Argument::value("direct-edge-irq", "irq", "Enable interrupt passthrough"),
Tomasz Jeznachccb26942021-03-30 22:44:11 -07002201 Argument::value("dmi", "DIR", "Directory with smbios_entry_point/DMI files"),
Tomasz Jeznachd93c29f2021-04-12 11:00:24 -07002202 Argument::flag("no-legacy", "Don't use legacy KBD/RTC devices emulation"),
Zach Reiznerefe95782017-08-26 18:05:48 -07002203 Argument::short_flag('h', "help", "Print help message.")];
2204
2205 let mut cfg = Config::default();
Zach Reizner55a9e502018-10-03 10:22:32 -07002206 let match_res = set_arguments(args, &arguments[..], |name, value| {
2207 set_argument(&mut cfg, name, value)
David Tolnay2bac1e72018-12-12 14:33:42 -08002208 })
Kaiyi Libccb4eb2020-02-06 17:53:11 -08002209 .and_then(|_| validate_arguments(&mut cfg));
Zach Reiznerefe95782017-08-26 18:05:48 -07002210
2211 match match_res {
Zach Reizner8864cb02018-01-16 17:59:03 -08002212 #[cfg(feature = "plugin")]
Zach Reizner267f2c82019-07-31 17:07:27 -07002213 Ok(()) if executable_is_plugin(&cfg.executable_path) => {
2214 match crosvm::plugin::run_config(cfg) {
2215 Ok(_) => {
2216 info!("crosvm and plugin have exited normally");
2217 Ok(())
2218 }
2219 Err(e) => {
2220 error!("{}", e);
2221 Err(())
2222 }
Zach Reizner8864cb02018-01-16 17:59:03 -08002223 }
Zach Reizner267f2c82019-07-31 17:07:27 -07002224 }
Michael Hoylee47a5002020-10-15 16:24:13 -07002225 Ok(()) => match platform::run_config(cfg) {
Zach Reizner55a9e502018-10-03 10:22:32 -07002226 Ok(_) => {
2227 info!("crosvm has exited normally");
2228 Ok(())
Zach Reiznerefe95782017-08-26 18:05:48 -07002229 }
Zach Reizner55a9e502018-10-03 10:22:32 -07002230 Err(e) => {
Junichi Uekawac7aa5232020-09-07 14:58:36 +09002231 error!("crosvm has exited with error: {}", e);
Zach Reizner55a9e502018-10-03 10:22:32 -07002232 Err(())
2233 }
2234 },
Dylan Reidbfba9932018-02-05 15:51:59 -08002235 Err(argument::Error::PrintHelp) => {
2236 print_help("crosvm run", "KERNEL", &arguments[..]);
2237 Ok(())
2238 }
Zach Reizner8864cb02018-01-16 17:59:03 -08002239 Err(e) => {
Dmitry Torokhov470b1e72020-01-15 12:46:49 -08002240 error!("{}", e);
Dylan Reidbfba9932018-02-05 15:51:59 -08002241 Err(())
Zach Reizner8864cb02018-01-16 17:59:03 -08002242 }
Zach Reiznerefe95782017-08-26 18:05:48 -07002243 }
2244}
2245
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002246fn stop_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
Zach Reizner78986322019-02-21 20:43:21 -08002247 if args.len() == 0 {
2248 print_help("crosvm stop", "VM_SOCKET...", &[]);
2249 println!("Stops the crosvm instance listening on each `VM_SOCKET` given.");
Jianxun Zhang56497d22019-03-04 14:38:24 -08002250 return Err(());
Zach Reizner78986322019-02-21 20:43:21 -08002251 }
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002252 let socket_path = &args.next().unwrap();
2253 let socket_path = Path::new(&socket_path);
2254 vms_request(&VmRequest::Exit, socket_path)
Zach Reizner78986322019-02-21 20:43:21 -08002255}
2256
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002257fn suspend_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
Zach Reizner78986322019-02-21 20:43:21 -08002258 if args.len() == 0 {
2259 print_help("crosvm suspend", "VM_SOCKET...", &[]);
2260 println!("Suspends the crosvm instance listening on each `VM_SOCKET` given.");
Jianxun Zhang56497d22019-03-04 14:38:24 -08002261 return Err(());
Zach Reizner78986322019-02-21 20:43:21 -08002262 }
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002263 let socket_path = &args.next().unwrap();
2264 let socket_path = Path::new(&socket_path);
2265 vms_request(&VmRequest::Suspend, socket_path)
Zach Reizner78986322019-02-21 20:43:21 -08002266}
2267
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002268fn resume_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
Zach Reizner78986322019-02-21 20:43:21 -08002269 if args.len() == 0 {
2270 print_help("crosvm resume", "VM_SOCKET...", &[]);
2271 println!("Resumes the crosvm instance listening on each `VM_SOCKET` given.");
Jianxun Zhang56497d22019-03-04 14:38:24 -08002272 return Err(());
Zach Reizner78986322019-02-21 20:43:21 -08002273 }
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002274 let socket_path = &args.next().unwrap();
2275 let socket_path = Path::new(&socket_path);
2276 vms_request(&VmRequest::Resume, socket_path)
Zach Reizner78986322019-02-21 20:43:21 -08002277}
2278
2279fn balloon_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
2280 if args.len() < 2 {
2281 print_help("crosvm balloon", "SIZE VM_SOCKET...", &[]);
2282 println!("Set the ballon size of the crosvm instance to `SIZE` bytes.");
Jianxun Zhang56497d22019-03-04 14:38:24 -08002283 return Err(());
Zach Reizner78986322019-02-21 20:43:21 -08002284 }
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09002285 let num_bytes = match args.next().unwrap().parse::<u64>() {
Zach Reizner78986322019-02-21 20:43:21 -08002286 Ok(n) => n,
2287 Err(_) => {
2288 error!("Failed to parse number of bytes");
2289 return Err(());
2290 }
2291 };
2292
Jakub Staron1f828d72019-04-11 12:49:29 -07002293 let command = BalloonControlCommand::Adjust { num_bytes };
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002294 let socket_path = &args.next().unwrap();
2295 let socket_path = Path::new(&socket_path);
2296 vms_request(&VmRequest::BalloonCommand(command), socket_path)
Zach Reizner78986322019-02-21 20:43:21 -08002297}
2298
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002299fn balloon_stats(mut args: std::env::Args) -> std::result::Result<(), ()> {
Charles William Dicked22f6b2020-04-08 11:05:24 +09002300 if args.len() != 1 {
2301 print_help("crosvm balloon_stats", "VM_SOCKET", &[]);
2302 println!("Prints virtio balloon statistics for a `VM_SOCKET`.");
2303 return Err(());
2304 }
2305 let command = BalloonControlCommand::Stats {};
2306 let request = &VmRequest::BalloonCommand(command);
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002307 let socket_path = &args.next().unwrap();
2308 let socket_path = Path::new(&socket_path);
2309 let response = handle_request(request, socket_path)?;
Hikaru Nishidaa0e381b2021-05-24 17:13:45 +09002310 match serde_json::to_string_pretty(&response) {
2311 Ok(response_json) => println!("{}", response_json),
2312 Err(e) => {
2313 error!("Failed to serialize into JSON: {}", e);
2314 return Err(());
2315 }
2316 }
Hikaru Nishida6b51c752021-05-21 12:37:43 +09002317 match response {
2318 VmResponse::BalloonStats { .. } => Ok(()),
2319 _ => Err(()),
2320 }
Charles William Dicked22f6b2020-04-08 11:05:24 +09002321}
2322
Keiichi Watanabe2400bd52021-08-04 19:18:02 +09002323fn modify_battery(mut args: std::env::Args) -> std::result::Result<(), ()> {
2324 if args.len() < 4 {
2325 print_help(
2326 "crosvm battery BATTERY_TYPE ",
2327 "[status STATUS | \
2328 present PRESENT | \
2329 health HEALTH | \
2330 capacity CAPACITY | \
2331 aconline ACONLINE ] \
2332 VM_SOCKET...",
2333 &[],
2334 );
2335 return Err(());
2336 }
2337
2338 // This unwrap will not panic because of the above length check.
2339 let battery_type = args.next().unwrap();
2340 let property = args.next().unwrap();
2341 let target = args.next().unwrap();
2342
2343 let socket_path = args.next().unwrap();
2344 let socket_path = Path::new(&socket_path);
2345
2346 do_modify_battery(&socket_path, &*battery_type, &*property, &*target)
2347}
2348
Andrew Walbranfb7c1082021-06-17 18:12:14 +01002349#[cfg(feature = "composite-disk")]
2350fn create_composite(mut args: std::env::Args) -> std::result::Result<(), ()> {
2351 if args.len() < 1 {
2352 print_help("crosvm create_composite", "PATH [LABEL:PARTITION]..", &[]);
2353 println!("Creates a new composite disk image containing the given partition images");
2354 return Err(());
2355 }
2356
2357 let composite_image_path = args.next().unwrap();
Jooyung Han2e14c732021-07-29 13:27:54 +09002358 let zero_filler_path = format!("{}.filler", composite_image_path);
Andrew Walbranfb7c1082021-06-17 18:12:14 +01002359 let header_path = format!("{}.header", composite_image_path);
2360 let footer_path = format!("{}.footer", composite_image_path);
2361
2362 let mut composite_image_file = OpenOptions::new()
2363 .create(true)
2364 .read(true)
2365 .write(true)
2366 .truncate(true)
2367 .open(&composite_image_path)
2368 .map_err(|e| {
2369 error!(
2370 "Failed opening composite disk image file at '{}': {}",
2371 composite_image_path, e
2372 );
2373 })?;
Jooyung Han2e14c732021-07-29 13:27:54 +09002374 create_zero_filler(&zero_filler_path).map_err(|e| {
2375 error!(
2376 "Failed to create zero filler file at '{}': {}",
2377 &zero_filler_path, e
2378 );
2379 })?;
Andrew Walbranfb7c1082021-06-17 18:12:14 +01002380 let mut header_file = OpenOptions::new()
2381 .create(true)
2382 .read(true)
2383 .write(true)
2384 .truncate(true)
2385 .open(&header_path)
2386 .map_err(|e| {
2387 error!(
2388 "Failed opening header image file at '{}': {}",
2389 header_path, e
2390 );
2391 })?;
2392 let mut footer_file = OpenOptions::new()
2393 .create(true)
2394 .read(true)
2395 .write(true)
2396 .truncate(true)
2397 .open(&footer_path)
2398 .map_err(|e| {
2399 error!(
2400 "Failed opening footer image file at '{}': {}",
2401 footer_path, e
2402 );
2403 })?;
2404
2405 let partitions = args
2406 .into_iter()
2407 .map(|partition_arg| {
2408 if let [label, path] = partition_arg.split(":").collect::<Vec<_>>()[..] {
2409 let partition_file = File::open(path)
2410 .map_err(|e| error!("Failed to open partition image: {}", e))?;
2411 let size = create_disk_file(partition_file)
2412 .map_err(|e| error!("Failed to create DiskFile instance: {}", e))?
2413 .get_len()
2414 .map_err(|e| error!("Failed to get length of partition image: {}", e))?;
2415 Ok(PartitionInfo {
2416 label: label.to_owned(),
Jooyung Hand7e56ba2021-07-29 13:26:48 +09002417 path: Path::new(path).to_owned(),
Andrew Walbranfb7c1082021-06-17 18:12:14 +01002418 partition_type: ImagePartitionType::LinuxFilesystem,
2419 writable: false,
Jooyung Hand7e56ba2021-07-29 13:26:48 +09002420 size,
Andrew Walbranfb7c1082021-06-17 18:12:14 +01002421 })
2422 } else {
2423 error!(
2424 "Must specify label and path for partition '{}', like LABEL:PATH",
2425 partition_arg
2426 );
2427 Err(())
2428 }
2429 })
2430 .collect::<Result<Vec<_>, _>>()?;
2431
2432 create_composite_disk(
2433 &partitions,
Jooyung Han2e14c732021-07-29 13:27:54 +09002434 &PathBuf::from(zero_filler_path),
Andrew Walbranfb7c1082021-06-17 18:12:14 +01002435 &PathBuf::from(header_path),
2436 &mut header_file,
2437 &PathBuf::from(footer_path),
2438 &mut footer_file,
2439 &mut composite_image_file,
2440 )
2441 .map_err(|e| {
2442 error!(
2443 "Failed to create composite disk image at '{}': {}",
2444 composite_image_path, e
2445 );
2446 })?;
2447
2448 Ok(())
2449}
2450
A. Cody Schuffelen9ca60392019-12-23 18:27:11 -08002451fn create_qcow2(args: std::env::Args) -> std::result::Result<(), ()> {
2452 let arguments = [
2453 Argument::positional("PATH", "where to create the qcow2 image"),
2454 Argument::positional("[SIZE]", "the expanded size of the image"),
2455 Argument::value(
2456 "backing_file",
2457 "path/to/file",
2458 " the file to back the image",
2459 ),
2460 ];
2461 let mut positional_index = 0;
2462 let mut file_path = String::from("");
2463 let mut size: Option<u64> = None;
2464 let mut backing_file: Option<String> = None;
2465 set_arguments(args, &arguments[..], |name, value| {
2466 match (name, positional_index) {
2467 ("", 0) => {
2468 // NAME
2469 positional_index += 1;
2470 file_path = value.unwrap().to_owned();
2471 }
2472 ("", 1) => {
2473 // [SIZE]
2474 positional_index += 1;
2475 size = Some(value.unwrap().parse::<u64>().map_err(|_| {
2476 argument::Error::InvalidValue {
2477 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08002478 expected: String::from("SIZE should be a nonnegative integer"),
A. Cody Schuffelen9ca60392019-12-23 18:27:11 -08002479 }
2480 })?);
2481 }
2482 ("", _) => {
2483 return Err(argument::Error::TooManyArguments(
2484 "Expected at most 2 positional arguments".to_owned(),
2485 ));
2486 }
2487 ("backing_file", _) => {
2488 backing_file = value.map(|x| x.to_owned());
2489 }
2490 _ => unreachable!(),
2491 };
2492 Ok(())
2493 })
2494 .map_err(|e| {
2495 error!("Unable to parse command line arguments: {}", e);
2496 })?;
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09002497 if file_path.is_empty() || !(size.is_some() ^ backing_file.is_some()) {
A. Cody Schuffelen9ca60392019-12-23 18:27:11 -08002498 print_help("crosvm create_qcow2", "PATH [SIZE]", &arguments);
2499 println!(
2500 "Create a new QCOW2 image at `PATH` of either the specified `SIZE` in bytes or
2501with a '--backing_file'."
2502 );
Jianxun Zhang56497d22019-03-04 14:38:24 -08002503 return Err(());
Dylan Reid2dcb6322018-07-13 10:42:48 -07002504 }
Dylan Reid2dcb6322018-07-13 10:42:48 -07002505
2506 let file = OpenOptions::new()
2507 .create(true)
2508 .read(true)
2509 .write(true)
A. Cody Schuffelen9ca60392019-12-23 18:27:11 -08002510 .truncate(true)
Dylan Reid2dcb6322018-07-13 10:42:48 -07002511 .open(&file_path)
2512 .map_err(|e| {
David Tolnayb4bd00f2019-02-12 17:51:26 -08002513 error!("Failed opening qcow file at '{}': {}", file_path, e);
Dylan Reid2dcb6322018-07-13 10:42:48 -07002514 })?;
2515
A. Cody Schuffelen9ca60392019-12-23 18:27:11 -08002516 match (size, backing_file) {
2517 (Some(size), None) => QcowFile::new(file, size).map_err(|e| {
2518 error!("Failed to create qcow file at '{}': {}", file_path, e);
2519 })?,
2520 (None, Some(backing_file)) => {
2521 QcowFile::new_from_backing(file, &backing_file).map_err(|e| {
2522 error!("Failed to create qcow file at '{}': {}", file_path, e);
2523 })?
2524 }
2525 _ => unreachable!(),
2526 };
Dylan Reid2dcb6322018-07-13 10:42:48 -07002527 Ok(())
2528}
2529
Daniel Verkamp92f73d72018-12-04 13:17:46 -08002530fn disk_cmd(mut args: std::env::Args) -> std::result::Result<(), ()> {
2531 if args.len() < 2 {
2532 print_help("crosvm disk", "SUBCOMMAND VM_SOCKET...", &[]);
2533 println!("Manage attached virtual disk devices.");
2534 println!("Subcommands:");
2535 println!(" resize DISK_INDEX NEW_SIZE VM_SOCKET");
Jianxun Zhang56497d22019-03-04 14:38:24 -08002536 return Err(());
Daniel Verkamp92f73d72018-12-04 13:17:46 -08002537 }
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09002538 let subcommand: &str = &args.next().unwrap();
Daniel Verkamp92f73d72018-12-04 13:17:46 -08002539
2540 let request = match subcommand {
2541 "resize" => {
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09002542 let disk_index = match args.next().unwrap().parse::<usize>() {
Daniel Verkamp92f73d72018-12-04 13:17:46 -08002543 Ok(n) => n,
2544 Err(_) => {
2545 error!("Failed to parse disk index");
2546 return Err(());
2547 }
2548 };
2549
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09002550 let new_size = match args.next().unwrap().parse::<u64>() {
Daniel Verkamp92f73d72018-12-04 13:17:46 -08002551 Ok(n) => n,
2552 Err(_) => {
2553 error!("Failed to parse disk size");
2554 return Err(());
2555 }
2556 };
2557
Jakub Staronecf81e02019-04-11 11:43:39 -07002558 VmRequest::DiskCommand {
Daniel Verkamp92f73d72018-12-04 13:17:46 -08002559 disk_index,
Jakub Staronecf81e02019-04-11 11:43:39 -07002560 command: DiskControlCommand::Resize { new_size },
Daniel Verkamp92f73d72018-12-04 13:17:46 -08002561 }
2562 }
2563 _ => {
2564 error!("Unknown disk subcommand '{}'", subcommand);
2565 return Err(());
2566 }
2567 };
2568
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002569 let socket_path = &args.next().unwrap();
2570 let socket_path = Path::new(&socket_path);
2571 vms_request(&request, socket_path)
Daniel Verkamp92f73d72018-12-04 13:17:46 -08002572}
2573
Keiichi Watanabe2400bd52021-08-04 19:18:02 +09002574fn make_rt(mut args: std::env::Args) -> std::result::Result<(), ()> {
2575 if args.len() == 0 {
2576 print_help("crosvm make_rt", "VM_SOCKET...", &[]);
2577 println!("Makes the crosvm instance listening on each `VM_SOCKET` given RT.");
2578 return Err(());
2579 }
2580 let socket_path = &args.next().unwrap();
2581 let socket_path = Path::new(&socket_path);
2582 vms_request(&VmRequest::MakeRT, socket_path)
2583}
2584
Jingkui Wang100e6e42019-03-08 20:41:57 -08002585fn parse_bus_id_addr(v: &str) -> ModifyUsbResult<(u8, u8, u16, u16)> {
2586 debug!("parse_bus_id_addr: {}", v);
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09002587 let mut ids = v.split(':');
Jingkui Wang100e6e42019-03-08 20:41:57 -08002588 match (ids.next(), ids.next(), ids.next(), ids.next()) {
2589 (Some(bus_id), Some(addr), Some(vid), Some(pid)) => {
2590 let bus_id = bus_id
2591 .parse::<u8>()
2592 .map_err(|e| ModifyUsbError::ArgParseInt("bus_id", bus_id.to_owned(), e))?;
2593 let addr = addr
2594 .parse::<u8>()
2595 .map_err(|e| ModifyUsbError::ArgParseInt("addr", addr.to_owned(), e))?;
2596 let vid = u16::from_str_radix(&vid, 16)
2597 .map_err(|e| ModifyUsbError::ArgParseInt("vid", vid.to_owned(), e))?;
2598 let pid = u16::from_str_radix(&pid, 16)
2599 .map_err(|e| ModifyUsbError::ArgParseInt("pid", pid.to_owned(), e))?;
2600 Ok((bus_id, addr, vid, pid))
2601 }
2602 _ => Err(ModifyUsbError::ArgParse(
2603 "BUS_ID_ADDR_BUS_NUM_DEV_NUM",
2604 v.to_owned(),
2605 )),
2606 }
2607}
2608
Jingkui Wang100e6e42019-03-08 20:41:57 -08002609fn usb_attach(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
2610 let val = args
2611 .next()
2612 .ok_or(ModifyUsbError::ArgMissing("BUS_ID_ADDR_BUS_NUM_DEV_NUM"))?;
2613 let (bus, addr, vid, pid) = parse_bus_id_addr(&val)?;
2614 let dev_path = PathBuf::from(
2615 args.next()
2616 .ok_or(ModifyUsbError::ArgMissing("usb device path"))?,
2617 );
Jingkui Wang100e6e42019-03-08 20:41:57 -08002618
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002619 let socket_path = args
2620 .next()
2621 .ok_or(ModifyUsbError::ArgMissing("control socket path"))?;
2622 let socket_path = Path::new(&socket_path);
2623
2624 do_usb_attach(&socket_path, bus, addr, vid, pid, &dev_path)
Jingkui Wang100e6e42019-03-08 20:41:57 -08002625}
2626
2627fn usb_detach(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
2628 let port: u8 = args
2629 .next()
2630 .map_or(Err(ModifyUsbError::ArgMissing("PORT")), |p| {
2631 p.parse::<u8>()
2632 .map_err(|e| ModifyUsbError::ArgParseInt("PORT", p.to_owned(), e))
2633 })?;
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002634 let socket_path = args
2635 .next()
2636 .ok_or(ModifyUsbError::ArgMissing("control socket path"))?;
2637 let socket_path = Path::new(&socket_path);
2638 do_usb_detach(&socket_path, port)
Jingkui Wang100e6e42019-03-08 20:41:57 -08002639}
2640
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002641fn usb_list(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
2642 let socket_path = args
2643 .next()
2644 .ok_or(ModifyUsbError::ArgMissing("control socket path"))?;
2645 let socket_path = Path::new(&socket_path);
2646 do_usb_list(&socket_path)
Jingkui Wang100e6e42019-03-08 20:41:57 -08002647}
2648
2649fn modify_usb(mut args: std::env::Args) -> std::result::Result<(), ()> {
Zach Reizneraff94ca2019-03-18 20:58:31 -07002650 if args.len() < 2 {
Jingkui Wang100e6e42019-03-08 20:41:57 -08002651 print_help("crosvm usb",
Zach Reizneraff94ca2019-03-18 20:58:31 -07002652 "[attach BUS_ID:ADDR:VENDOR_ID:PRODUCT_ID [USB_DEVICE_PATH|-] | detach PORT | list] VM_SOCKET...", &[]);
Jingkui Wang100e6e42019-03-08 20:41:57 -08002653 return Err(());
2654 }
2655
2656 // This unwrap will not panic because of the above length check.
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002657 let command = &args.next().unwrap();
Jingkui Wang100e6e42019-03-08 20:41:57 -08002658 let result = match command.as_ref() {
2659 "attach" => usb_attach(args),
2660 "detach" => usb_detach(args),
2661 "list" => usb_list(args),
2662 other => Err(ModifyUsbError::UnknownCommand(other.to_owned())),
2663 };
2664 match result {
2665 Ok(response) => {
2666 println!("{}", response);
2667 Ok(())
2668 }
2669 Err(e) => {
2670 println!("error {}", e);
2671 Err(())
2672 }
2673 }
2674}
2675
Keiichi Watanabe2400bd52021-08-04 19:18:02 +09002676#[allow(clippy::unnecessary_wraps)]
2677fn pkg_version() -> std::result::Result<(), ()> {
2678 const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION");
2679 const PKG_VERSION: Option<&'static str> = option_env!("PKG_VERSION");
2680
2681 print!("crosvm {}", VERSION.unwrap_or("UNKNOWN"));
2682 match PKG_VERSION {
2683 Some(v) => println!("-{}", v),
2684 None => println!(),
2685 }
2686 Ok(())
2687}
2688
Zach Reiznerefe95782017-08-26 18:05:48 -07002689fn print_usage() {
Junichi Uekawaf0a77232021-04-08 16:44:44 +09002690 print_help("crosvm", "[command]", &[]);
Zach Reiznerefe95782017-08-26 18:05:48 -07002691 println!("Commands:");
Junichi Uekawaf0a77232021-04-08 16:44:44 +09002692 println!(" balloon - Set balloon size of the crosvm instance.");
2693 println!(" balloon_stats - Prints virtio balloon statistics.");
2694 println!(" battery - Modify battery.");
Andrew Walbranfb7c1082021-06-17 18:12:14 +01002695 #[cfg(feature = "composite-disk")]
2696 println!(" create_composite - Create a new composite disk image file.");
Dylan Reid2dcb6322018-07-13 10:42:48 -07002697 println!(" create_qcow2 - Create a new qcow2 disk image file.");
Jingkui Wang100e6e42019-03-08 20:41:57 -08002698 println!(" disk - Manage attached virtual disk devices.");
Junichi Uekawaf0a77232021-04-08 16:44:44 +09002699 println!(" resume - Resumes the crosvm instance.");
2700 println!(" run - Start a new crosvm instance.");
2701 println!(" stop - Stops crosvm instances via their control sockets.");
2702 println!(" suspend - Suspends the crosvm instance.");
Jingkui Wang100e6e42019-03-08 20:41:57 -08002703 println!(" usb - Manage attached virtual USB devices.");
Yi Sun54305cd2020-01-04 00:19:37 +08002704 println!(" version - Show package version.");
2705}
2706
Dylan Reidbfba9932018-02-05 15:51:59 -08002707fn crosvm_main() -> std::result::Result<(), ()> {
Stephen Barber56fbf092017-06-29 16:12:14 -07002708 if let Err(e) = syslog::init() {
David Tolnayb4bd00f2019-02-12 17:51:26 -08002709 println!("failed to initialize syslog: {}", e);
Dylan Reidbfba9932018-02-05 15:51:59 -08002710 return Err(());
Stephen Barber56fbf092017-06-29 16:12:14 -07002711 }
Zach Reizner639d9672017-05-01 17:57:18 -07002712
Zach Reiznerb3fa5c92019-01-28 14:05:23 -08002713 panic_hook::set_panic_hook();
2714
Zach Reiznerefe95782017-08-26 18:05:48 -07002715 let mut args = std::env::args();
2716 if args.next().is_none() {
2717 error!("expected executable name");
Dylan Reidbfba9932018-02-05 15:51:59 -08002718 return Err(());
Zach Reizner639d9672017-05-01 17:57:18 -07002719 }
Zach Reiznerefe95782017-08-26 18:05:48 -07002720
Zach Reizner8864cb02018-01-16 17:59:03 -08002721 // Past this point, usage of exit is in danger of leaking zombie processes.
Dylan Reidbfba9932018-02-05 15:51:59 -08002722 let ret = match args.next().as_ref().map(|a| a.as_ref()) {
2723 None => {
2724 print_usage();
2725 Ok(())
2726 }
Zach Reizner55a9e502018-10-03 10:22:32 -07002727 Some("balloon") => balloon_vms(args),
Charles William Dicked22f6b2020-04-08 11:05:24 +09002728 Some("balloon_stats") => balloon_stats(args),
Keiichi Watanabe2400bd52021-08-04 19:18:02 +09002729 Some("battery") => modify_battery(args),
Andrew Walbranfb7c1082021-06-17 18:12:14 +01002730 #[cfg(feature = "composite-disk")]
2731 Some("create_composite") => create_composite(args),
Zach Reizner55a9e502018-10-03 10:22:32 -07002732 Some("create_qcow2") => create_qcow2(args),
Daniel Verkamp92f73d72018-12-04 13:17:46 -08002733 Some("disk") => disk_cmd(args),
Keiichi Watanabe2400bd52021-08-04 19:18:02 +09002734 Some("make_rt") => make_rt(args),
2735 Some("resume") => resume_vms(args),
2736 Some("run") => run_vm(args),
2737 Some("stop") => stop_vms(args),
2738 Some("suspend") => suspend_vms(args),
Jingkui Wang100e6e42019-03-08 20:41:57 -08002739 Some("usb") => modify_usb(args),
Yi Sun54305cd2020-01-04 00:19:37 +08002740 Some("version") => pkg_version(),
Zach Reiznerefe95782017-08-26 18:05:48 -07002741 Some(c) => {
2742 println!("invalid subcommand: {:?}", c);
2743 print_usage();
Dylan Reidbfba9932018-02-05 15:51:59 -08002744 Err(())
Zach Reiznerefe95782017-08-26 18:05:48 -07002745 }
Dylan Reidbfba9932018-02-05 15:51:59 -08002746 };
Zach Reiznerefe95782017-08-26 18:05:48 -07002747
2748 // Reap exit status from any child device processes. At this point, all devices should have been
2749 // dropped in the main process and told to shutdown. Try over a period of 100ms, since it may
2750 // take some time for the processes to shut down.
2751 if !wait_all_children() {
2752 // We gave them a chance, and it's too late.
2753 warn!("not all child processes have exited; sending SIGKILL");
2754 if let Err(e) = kill_process_group() {
2755 // We're now at the mercy of the OS to clean up after us.
David Tolnayb4bd00f2019-02-12 17:51:26 -08002756 warn!("unable to kill all child processes: {}", e);
Zach Reiznerefe95782017-08-26 18:05:48 -07002757 }
2758 }
2759
2760 // WARNING: Any code added after this point is not guaranteed to run
2761 // since we may forcibly kill this process (and its children) above.
Dylan Reidbfba9932018-02-05 15:51:59 -08002762 ret
2763}
2764
2765fn main() {
2766 std::process::exit(if crosvm_main().is_ok() { 0 } else { 1 });
Zach Reizner639d9672017-05-01 17:57:18 -07002767}
Daniel Verkamp107edb32019-04-05 09:58:48 -07002768
2769#[cfg(test)]
2770mod tests {
2771 use super::*;
Kaiyi Libccb4eb2020-02-06 17:53:11 -08002772 use crosvm::{DEFAULT_TOUCH_DEVICE_HEIGHT, DEFAULT_TOUCH_DEVICE_WIDTH};
Daniel Verkamp107edb32019-04-05 09:58:48 -07002773
2774 #[test]
2775 fn parse_cpu_set_single() {
2776 assert_eq!(parse_cpu_set("123").expect("parse failed"), vec![123]);
2777 }
2778
2779 #[test]
2780 fn parse_cpu_set_list() {
2781 assert_eq!(
2782 parse_cpu_set("0,1,2,3").expect("parse failed"),
2783 vec![0, 1, 2, 3]
2784 );
2785 }
2786
2787 #[test]
2788 fn parse_cpu_set_range() {
2789 assert_eq!(
2790 parse_cpu_set("0-3").expect("parse failed"),
2791 vec![0, 1, 2, 3]
2792 );
2793 }
2794
2795 #[test]
2796 fn parse_cpu_set_list_of_ranges() {
2797 assert_eq!(
2798 parse_cpu_set("3-4,7-9,18").expect("parse failed"),
2799 vec![3, 4, 7, 8, 9, 18]
2800 );
2801 }
2802
2803 #[test]
2804 fn parse_cpu_set_repeated() {
2805 // For now, allow duplicates - they will be handled gracefully by the vec to cpu_set_t conversion.
2806 assert_eq!(parse_cpu_set("1,1,1").expect("parse failed"), vec![1, 1, 1]);
2807 }
2808
2809 #[test]
2810 fn parse_cpu_set_negative() {
2811 // Negative CPU numbers are not allowed.
2812 parse_cpu_set("-3").expect_err("parse should have failed");
2813 }
2814
2815 #[test]
2816 fn parse_cpu_set_reverse_range() {
2817 // Ranges must be from low to high.
2818 parse_cpu_set("5-2").expect_err("parse should have failed");
2819 }
2820
2821 #[test]
2822 fn parse_cpu_set_open_range() {
2823 parse_cpu_set("3-").expect_err("parse should have failed");
2824 }
2825
2826 #[test]
2827 fn parse_cpu_set_extra_comma() {
2828 parse_cpu_set("0,1,2,").expect_err("parse should have failed");
2829 }
Trent Begin17ccaad2019-04-17 13:51:25 -06002830
Daniel Verkampc677fb42020-09-08 13:47:49 -07002831 #[test]
2832 fn parse_cpu_affinity_global() {
2833 assert_eq!(
2834 parse_cpu_affinity("0,5-7,9").expect("parse failed"),
2835 VcpuAffinity::Global(vec![0, 5, 6, 7, 9]),
2836 );
2837 }
2838
2839 #[test]
2840 fn parse_cpu_affinity_per_vcpu_one_to_one() {
2841 let mut expected_map = BTreeMap::new();
2842 expected_map.insert(0, vec![0]);
2843 expected_map.insert(1, vec![1]);
2844 expected_map.insert(2, vec![2]);
2845 expected_map.insert(3, vec![3]);
2846 assert_eq!(
2847 parse_cpu_affinity("0=0:1=1:2=2:3=3").expect("parse failed"),
2848 VcpuAffinity::PerVcpu(expected_map),
2849 );
2850 }
2851
2852 #[test]
2853 fn parse_cpu_affinity_per_vcpu_sets() {
2854 let mut expected_map = BTreeMap::new();
2855 expected_map.insert(0, vec![0, 1, 2]);
2856 expected_map.insert(1, vec![3, 4, 5]);
2857 expected_map.insert(2, vec![6, 7, 8]);
2858 assert_eq!(
2859 parse_cpu_affinity("0=0,1,2:1=3-5:2=6,7-8").expect("parse failed"),
2860 VcpuAffinity::PerVcpu(expected_map),
2861 );
2862 }
2863
Dennis Kempin50a58f92021-06-23 11:34:31 -07002864 #[cfg(feature = "audio_cras")]
Trent Begin17ccaad2019-04-17 13:51:25 -06002865 #[test]
Judy Hsiaod5c1e962020-02-04 12:30:01 +08002866 fn parse_ac97_vaild() {
2867 parse_ac97_options("backend=cras").expect("parse should have succeded");
2868 }
2869
Andrew Scull1590e6f2020-03-18 18:00:47 +00002870 #[cfg(feature = "audio")]
Judy Hsiaod5c1e962020-02-04 12:30:01 +08002871 #[test]
2872 fn parse_ac97_null_vaild() {
2873 parse_ac97_options("backend=null").expect("parse should have succeded");
2874 }
2875
Dennis Kempin50a58f92021-06-23 11:34:31 -07002876 #[cfg(feature = "audio_cras")]
Judy Hsiaod5c1e962020-02-04 12:30:01 +08002877 #[test]
Judy Hsiaob4b94c72020-09-07 15:56:00 +08002878 fn parse_ac97_capture_vaild() {
2879 parse_ac97_options("backend=cras,capture=true").expect("parse should have succeded");
Judy Hsiaod5c1e962020-02-04 12:30:01 +08002880 }
2881
Dennis Kempin50a58f92021-06-23 11:34:31 -07002882 #[cfg(feature = "audio_cras")]
Jorge E. Moreira359e7de2020-12-02 18:25:53 -08002883 #[test]
paulhsia83d51602021-03-09 17:13:14 +08002884 fn parse_ac97_client_type() {
2885 parse_ac97_options("backend=cras,capture=true,client_type=crosvm")
2886 .expect("parse should have succeded");
2887 parse_ac97_options("backend=cras,capture=true,client_type=arcvm")
2888 .expect("parse should have succeded");
2889 parse_ac97_options("backend=cras,capture=true,client_type=none")
2890 .expect_err("parse should have failed");
2891 }
2892
2893 #[cfg(feature = "audio")]
2894 #[test]
Jorge E. Moreira359e7de2020-12-02 18:25:53 -08002895 fn parse_ac97_vios_valid() {
2896 parse_ac97_options("backend=vios,server=/path/to/server")
2897 .expect("parse should have succeded");
2898 }
2899
Judy Hsiaod5c1e962020-02-04 12:30:01 +08002900 #[test]
Trent Begin17ccaad2019-04-17 13:51:25 -06002901 fn parse_serial_vaild() {
Jorge E. Moreira1e262302019-08-01 14:40:03 -07002902 parse_serial_options("type=syslog,num=1,console=true,stdin=true")
2903 .expect("parse should have succeded");
Trent Begin17ccaad2019-04-17 13:51:25 -06002904 }
2905
2906 #[test]
A. Cody Schuffelen3faab5a2020-10-05 17:29:16 -07002907 fn parse_serial_virtio_console_vaild() {
2908 parse_serial_options("type=syslog,num=5,console=true,stdin=true,hardware=virtio-console")
2909 .expect("parse should have succeded");
2910 }
2911
2912 #[test]
Trent Begin923bab02019-06-17 13:48:06 -06002913 fn parse_serial_valid_no_num() {
2914 parse_serial_options("type=syslog").expect("parse should have succeded");
2915 }
2916
2917 #[test]
Nicholas Hollingumc76c2da2020-09-18 15:53:16 +10002918 fn parse_serial_equals_in_value() {
2919 let parsed = parse_serial_options("type=syslog,path=foo=bar==.log")
2920 .expect("parse should have succeded");
2921 assert_eq!(parsed.path, Some(PathBuf::from("foo=bar==.log")));
2922 }
2923
2924 #[test]
Trent Begin17ccaad2019-04-17 13:51:25 -06002925 fn parse_serial_invalid_type() {
2926 parse_serial_options("type=wormhole,num=1").expect_err("parse should have failed");
2927 }
2928
2929 #[test]
2930 fn parse_serial_invalid_num_upper() {
2931 parse_serial_options("type=syslog,num=5").expect_err("parse should have failed");
2932 }
2933
2934 #[test]
2935 fn parse_serial_invalid_num_lower() {
2936 parse_serial_options("type=syslog,num=0").expect_err("parse should have failed");
2937 }
2938
2939 #[test]
A. Cody Schuffelen3faab5a2020-10-05 17:29:16 -07002940 fn parse_serial_virtio_console_invalid_num_lower() {
2941 parse_serial_options("type=syslog,hardware=virtio-console,num=0")
2942 .expect_err("parse should have failed");
2943 }
2944
2945 #[test]
Trent Begin17ccaad2019-04-17 13:51:25 -06002946 fn parse_serial_invalid_num_string() {
2947 parse_serial_options("type=syslog,num=number3").expect_err("parse should have failed");
2948 }
2949
2950 #[test]
2951 fn parse_serial_invalid_option() {
2952 parse_serial_options("type=syslog,speed=lightspeed").expect_err("parse should have failed");
2953 }
Jorge E. Moreira1e262302019-08-01 14:40:03 -07002954
2955 #[test]
2956 fn parse_serial_invalid_two_stdin() {
2957 let mut config = Config::default();
2958 set_argument(&mut config, "serial", Some("num=1,type=stdout,stdin=true"))
2959 .expect("should parse the first serial argument");
2960 set_argument(&mut config, "serial", Some("num=2,type=stdout,stdin=true"))
2961 .expect_err("should fail to parse a second serial port connected to stdin");
2962 }
Dmitry Torokhov458bb642019-12-13 11:47:52 -08002963
2964 #[test]
2965 fn parse_plugin_mount_valid() {
2966 let mut config = Config::default();
2967 set_argument(
2968 &mut config,
2969 "plugin-mount",
2970 Some("/dev/null:/dev/zero:true"),
2971 )
2972 .expect("parse should succeed");
2973 assert_eq!(config.plugin_mounts[0].src, PathBuf::from("/dev/null"));
2974 assert_eq!(config.plugin_mounts[0].dst, PathBuf::from("/dev/zero"));
2975 assert_eq!(config.plugin_mounts[0].writable, true);
2976 }
2977
2978 #[test]
2979 fn parse_plugin_mount_valid_shorthand() {
2980 let mut config = Config::default();
2981 set_argument(&mut config, "plugin-mount", Some("/dev/null")).expect("parse should succeed");
2982 assert_eq!(config.plugin_mounts[0].dst, PathBuf::from("/dev/null"));
2983 assert_eq!(config.plugin_mounts[0].writable, false);
2984 set_argument(&mut config, "plugin-mount", Some("/dev/null:/dev/zero"))
2985 .expect("parse should succeed");
2986 assert_eq!(config.plugin_mounts[1].dst, PathBuf::from("/dev/zero"));
2987 assert_eq!(config.plugin_mounts[1].writable, false);
2988 set_argument(&mut config, "plugin-mount", Some("/dev/null::true"))
2989 .expect("parse should succeed");
2990 assert_eq!(config.plugin_mounts[2].dst, PathBuf::from("/dev/null"));
2991 assert_eq!(config.plugin_mounts[2].writable, true);
2992 }
2993
2994 #[test]
2995 fn parse_plugin_mount_invalid() {
2996 let mut config = Config::default();
2997 set_argument(&mut config, "plugin-mount", Some("")).expect_err("parse should fail");
2998 set_argument(
2999 &mut config,
3000 "plugin-mount",
3001 Some("/dev/null:/dev/null:true:false"),
3002 )
3003 .expect_err("parse should fail because too many arguments");
3004 set_argument(&mut config, "plugin-mount", Some("null:/dev/null:true"))
3005 .expect_err("parse should fail because source is not absolute");
3006 set_argument(&mut config, "plugin-mount", Some("/dev/null:null:true"))
3007 .expect_err("parse should fail because source is not absolute");
3008 set_argument(&mut config, "plugin-mount", Some("/dev/null:null:blah"))
3009 .expect_err("parse should fail because flag is not boolean");
3010 }
3011
3012 #[test]
3013 fn parse_plugin_gid_map_valid() {
3014 let mut config = Config::default();
3015 set_argument(&mut config, "plugin-gid-map", Some("1:2:3")).expect("parse should succeed");
3016 assert_eq!(config.plugin_gid_maps[0].inner, 1);
3017 assert_eq!(config.plugin_gid_maps[0].outer, 2);
3018 assert_eq!(config.plugin_gid_maps[0].count, 3);
3019 }
3020
3021 #[test]
3022 fn parse_plugin_gid_map_valid_shorthand() {
3023 let mut config = Config::default();
3024 set_argument(&mut config, "plugin-gid-map", Some("1")).expect("parse should succeed");
3025 assert_eq!(config.plugin_gid_maps[0].inner, 1);
3026 assert_eq!(config.plugin_gid_maps[0].outer, 1);
3027 assert_eq!(config.plugin_gid_maps[0].count, 1);
3028 set_argument(&mut config, "plugin-gid-map", Some("1:2")).expect("parse should succeed");
3029 assert_eq!(config.plugin_gid_maps[1].inner, 1);
3030 assert_eq!(config.plugin_gid_maps[1].outer, 2);
3031 assert_eq!(config.plugin_gid_maps[1].count, 1);
3032 set_argument(&mut config, "plugin-gid-map", Some("1::3")).expect("parse should succeed");
3033 assert_eq!(config.plugin_gid_maps[2].inner, 1);
3034 assert_eq!(config.plugin_gid_maps[2].outer, 1);
3035 assert_eq!(config.plugin_gid_maps[2].count, 3);
3036 }
3037
3038 #[test]
3039 fn parse_plugin_gid_map_invalid() {
3040 let mut config = Config::default();
3041 set_argument(&mut config, "plugin-gid-map", Some("")).expect_err("parse should fail");
3042 set_argument(&mut config, "plugin-gid-map", Some("1:2:3:4"))
3043 .expect_err("parse should fail because too many arguments");
3044 set_argument(&mut config, "plugin-gid-map", Some("blah:2:3"))
3045 .expect_err("parse should fail because inner is not a number");
3046 set_argument(&mut config, "plugin-gid-map", Some("1:blah:3"))
3047 .expect_err("parse should fail because outer is not a number");
3048 set_argument(&mut config, "plugin-gid-map", Some("1:2:blah"))
3049 .expect_err("parse should fail because count is not a number");
3050 }
Kaiyi Libccb4eb2020-02-06 17:53:11 -08003051
3052 #[test]
3053 fn single_touch_spec_and_track_pad_spec_default_size() {
3054 let mut config = Config::default();
3055 config
3056 .executable_path
3057 .replace(Executable::Kernel(PathBuf::from("kernel")));
3058 set_argument(&mut config, "single-touch", Some("/dev/single-touch-test")).unwrap();
3059 set_argument(&mut config, "trackpad", Some("/dev/single-touch-test")).unwrap();
3060 validate_arguments(&mut config).unwrap();
3061 assert_eq!(
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07003062 config.virtio_single_touch.first().unwrap().get_size(),
Kaiyi Libccb4eb2020-02-06 17:53:11 -08003063 (DEFAULT_TOUCH_DEVICE_WIDTH, DEFAULT_TOUCH_DEVICE_HEIGHT)
3064 );
3065 assert_eq!(
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07003066 config.virtio_trackpad.first().unwrap().get_size(),
Kaiyi Libccb4eb2020-02-06 17:53:11 -08003067 (DEFAULT_TOUCH_DEVICE_WIDTH, DEFAULT_TOUCH_DEVICE_HEIGHT)
3068 );
3069 }
3070
3071 #[cfg(feature = "gpu")]
3072 #[test]
3073 fn single_touch_spec_default_size_from_gpu() {
3074 let width = 12345u32;
3075 let height = 54321u32;
3076 let mut config = Config::default();
3077 config
3078 .executable_path
3079 .replace(Executable::Kernel(PathBuf::from("kernel")));
3080 set_argument(&mut config, "single-touch", Some("/dev/single-touch-test")).unwrap();
3081 set_argument(
3082 &mut config,
3083 "gpu",
3084 Some(&format!("width={},height={}", width, height)),
3085 )
3086 .unwrap();
3087 validate_arguments(&mut config).unwrap();
3088 assert_eq!(
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07003089 config.virtio_single_touch.first().unwrap().get_size(),
Kaiyi Libccb4eb2020-02-06 17:53:11 -08003090 (width, height)
3091 );
3092 }
3093
3094 #[test]
3095 fn single_touch_spec_and_track_pad_spec_with_size() {
3096 let width = 12345u32;
3097 let height = 54321u32;
3098 let mut config = Config::default();
3099 config
3100 .executable_path
3101 .replace(Executable::Kernel(PathBuf::from("kernel")));
3102 set_argument(
3103 &mut config,
3104 "single-touch",
3105 Some(&format!("/dev/single-touch-test:{}:{}", width, height)),
3106 )
3107 .unwrap();
3108 set_argument(
3109 &mut config,
3110 "trackpad",
3111 Some(&format!("/dev/single-touch-test:{}:{}", width, height)),
3112 )
3113 .unwrap();
3114 validate_arguments(&mut config).unwrap();
3115 assert_eq!(
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07003116 config.virtio_single_touch.first().unwrap().get_size(),
Kaiyi Libccb4eb2020-02-06 17:53:11 -08003117 (width, height)
3118 );
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07003119 assert_eq!(
3120 config.virtio_trackpad.first().unwrap().get_size(),
3121 (width, height)
3122 );
Kaiyi Libccb4eb2020-02-06 17:53:11 -08003123 }
3124
3125 #[cfg(feature = "gpu")]
3126 #[test]
3127 fn single_touch_spec_with_size_independent_from_gpu() {
3128 let touch_width = 12345u32;
3129 let touch_height = 54321u32;
3130 let display_width = 1234u32;
3131 let display_height = 5432u32;
3132 let mut config = Config::default();
3133 config
3134 .executable_path
3135 .replace(Executable::Kernel(PathBuf::from("kernel")));
3136 set_argument(
3137 &mut config,
3138 "single-touch",
3139 Some(&format!(
3140 "/dev/single-touch-test:{}:{}",
3141 touch_width, touch_height
3142 )),
3143 )
3144 .unwrap();
3145 set_argument(
3146 &mut config,
3147 "gpu",
3148 Some(&format!(
3149 "width={},height={}",
3150 display_width, display_height
3151 )),
3152 )
3153 .unwrap();
3154 validate_arguments(&mut config).unwrap();
3155 assert_eq!(
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07003156 config.virtio_single_touch.first().unwrap().get_size(),
Kaiyi Libccb4eb2020-02-06 17:53:11 -08003157 (touch_width, touch_height)
3158 );
3159 }
Kaiyi Lidd348a42020-07-13 11:49:46 -07003160
Daniel Norman5e23df72021-03-11 10:11:02 -08003161 #[test]
3162 fn virtio_switches() {
3163 let mut config = Config::default();
3164 config
3165 .executable_path
3166 .replace(Executable::Kernel(PathBuf::from("kernel")));
3167 set_argument(&mut config, "switches", Some("/dev/switches-test")).unwrap();
3168 validate_arguments(&mut config).unwrap();
3169 assert_eq!(
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07003170 config.virtio_switches.pop().unwrap(),
Keiichi Watanabed56a2f42021-03-18 20:16:23 +09003171 PathBuf::from("/dev/switches-test")
3172 );
Daniel Norman5e23df72021-03-11 10:11:02 -08003173 }
3174
Chia-I Wu6d473b32021-04-12 10:14:24 -07003175 #[cfg(feature = "gpu")]
Kaiyi Lidd348a42020-07-13 11:49:46 -07003176 #[test]
Chia-I Wu91df6562021-04-12 09:47:38 -07003177 fn parse_gpu_options_default_vulkan_support() {
Jason Macnakd659a0d2021-03-15 15:33:01 -07003178 {
3179 let mut gpu_params: GpuParameters = Default::default();
3180 assert!(parse_gpu_options(Some("backend=virglrenderer"), &mut gpu_params).is_ok());
3181 assert!(!gpu_params.use_vulkan);
3182 }
Chia-I Wu6d473b32021-04-12 10:14:24 -07003183
3184 #[cfg(feature = "gfxstream")]
Jason Macnakd659a0d2021-03-15 15:33:01 -07003185 {
3186 let mut gpu_params: GpuParameters = Default::default();
3187 assert!(parse_gpu_options(Some("backend=gfxstream"), &mut gpu_params).is_ok());
3188 assert!(gpu_params.use_vulkan);
3189 }
Chia-I Wu91df6562021-04-12 09:47:38 -07003190 }
3191
Chia-I Wu6d473b32021-04-12 10:14:24 -07003192 #[cfg(feature = "gpu")]
Chia-I Wu91df6562021-04-12 09:47:38 -07003193 #[test]
Chia-I Wu6d473b32021-04-12 10:14:24 -07003194 fn parse_gpu_options_with_vulkan_specified() {
Jason Macnakd659a0d2021-03-15 15:33:01 -07003195 {
3196 let mut gpu_params: GpuParameters = Default::default();
3197 assert!(parse_gpu_options(Some("vulkan=true"), &mut gpu_params).is_ok());
3198 assert!(gpu_params.use_vulkan);
3199 }
3200 {
3201 let mut gpu_params: GpuParameters = Default::default();
3202 assert!(
3203 parse_gpu_options(Some("backend=virglrenderer,vulkan=true"), &mut gpu_params)
3204 .is_ok()
3205 );
3206 assert!(gpu_params.use_vulkan);
3207 }
3208 {
3209 let mut gpu_params: GpuParameters = Default::default();
3210 assert!(
3211 parse_gpu_options(Some("vulkan=true,backend=virglrenderer"), &mut gpu_params)
3212 .is_ok()
3213 );
3214 assert!(gpu_params.use_vulkan);
3215 }
3216 {
3217 let mut gpu_params: GpuParameters = Default::default();
3218 assert!(parse_gpu_options(Some("vulkan=false"), &mut gpu_params).is_ok());
3219 assert!(!gpu_params.use_vulkan);
3220 }
3221 {
3222 let mut gpu_params: GpuParameters = Default::default();
3223 assert!(
3224 parse_gpu_options(Some("backend=virglrenderer,vulkan=false"), &mut gpu_params)
3225 .is_ok()
3226 );
3227 assert!(!gpu_params.use_vulkan);
3228 }
3229 {
3230 let mut gpu_params: GpuParameters = Default::default();
3231 assert!(
3232 parse_gpu_options(Some("vulkan=false,backend=virglrenderer"), &mut gpu_params)
3233 .is_ok()
3234 );
3235 assert!(!gpu_params.use_vulkan);
3236 }
3237 {
3238 let mut gpu_params: GpuParameters = Default::default();
3239 assert!(parse_gpu_options(
3240 Some("backend=virglrenderer,vulkan=invalid_value"),
3241 &mut gpu_params
3242 )
3243 .is_err());
3244 }
3245 {
3246 let mut gpu_params: GpuParameters = Default::default();
3247 assert!(parse_gpu_options(
3248 Some("vulkan=invalid_value,backend=virglrenderer"),
3249 &mut gpu_params
3250 )
3251 .is_err());
3252 }
Kaiyi Lidd348a42020-07-13 11:49:46 -07003253 }
3254
3255 #[cfg(all(feature = "gpu", feature = "gfxstream"))]
3256 #[test]
3257 fn parse_gpu_options_gfxstream_with_syncfd_specified() {
Jason Macnakd659a0d2021-03-15 15:33:01 -07003258 {
3259 let mut gpu_params: GpuParameters = Default::default();
3260 assert!(
3261 parse_gpu_options(Some("backend=gfxstream,syncfd=true"), &mut gpu_params).is_ok()
3262 );
3263 assert!(gpu_params.gfxstream_use_syncfd);
3264 }
3265 {
3266 let mut gpu_params: GpuParameters = Default::default();
3267 assert!(
3268 parse_gpu_options(Some("syncfd=true,backend=gfxstream"), &mut gpu_params).is_ok()
3269 );
3270 assert!(gpu_params.gfxstream_use_syncfd);
3271 }
3272 {
3273 let mut gpu_params: GpuParameters = Default::default();
3274 assert!(
3275 parse_gpu_options(Some("backend=gfxstream,syncfd=false"), &mut gpu_params).is_ok()
3276 );
3277 assert!(!gpu_params.gfxstream_use_syncfd);
3278 }
3279 {
3280 let mut gpu_params: GpuParameters = Default::default();
3281 assert!(
3282 parse_gpu_options(Some("syncfd=false,backend=gfxstream"), &mut gpu_params).is_ok()
3283 );
3284 assert!(!gpu_params.gfxstream_use_syncfd);
3285 }
3286 {
3287 let mut gpu_params: GpuParameters = Default::default();
3288 assert!(parse_gpu_options(
3289 Some("backend=gfxstream,syncfd=invalid_value"),
3290 &mut gpu_params
3291 )
3292 .is_err());
3293 }
3294 {
3295 let mut gpu_params: GpuParameters = Default::default();
3296 assert!(parse_gpu_options(
3297 Some("syncfd=invalid_value,backend=gfxstream"),
3298 &mut gpu_params
3299 )
3300 .is_err());
3301 }
Kaiyi Lidd348a42020-07-13 11:49:46 -07003302 }
3303
3304 #[cfg(all(feature = "gpu", feature = "gfxstream"))]
3305 #[test]
3306 fn parse_gpu_options_not_gfxstream_with_syncfd_specified() {
Jason Macnakd659a0d2021-03-15 15:33:01 -07003307 {
3308 let mut gpu_params: GpuParameters = Default::default();
3309 assert!(
3310 parse_gpu_options(Some("backend=virglrenderer,syncfd=true"), &mut gpu_params)
3311 .is_err()
3312 );
3313 }
3314 {
3315 let mut gpu_params: GpuParameters = Default::default();
3316 assert!(
3317 parse_gpu_options(Some("syncfd=true,backend=virglrenderer"), &mut gpu_params)
3318 .is_err()
3319 );
3320 }
3321 }
3322
3323 #[cfg(feature = "gpu")]
3324 #[test]
3325 fn parse_gpu_display_options_valid() {
3326 {
3327 let mut gpu_params: GpuParameters = Default::default();
3328 assert!(
3329 parse_gpu_display_options(Some("width=500,height=600"), &mut gpu_params).is_ok()
3330 );
3331 assert_eq!(gpu_params.displays.len(), 1);
3332 assert_eq!(gpu_params.displays[0].width, 500);
3333 assert_eq!(gpu_params.displays[0].height, 600);
3334 }
3335 }
3336
3337 #[cfg(feature = "gpu")]
3338 #[test]
3339 fn parse_gpu_display_options_invalid() {
3340 {
3341 let mut gpu_params: GpuParameters = Default::default();
3342 assert!(parse_gpu_display_options(Some("width=500"), &mut gpu_params).is_err());
3343 }
3344 {
3345 let mut gpu_params: GpuParameters = Default::default();
3346 assert!(parse_gpu_display_options(Some("height=500"), &mut gpu_params).is_err());
3347 }
3348 {
3349 let mut gpu_params: GpuParameters = Default::default();
3350 assert!(parse_gpu_display_options(Some("width"), &mut gpu_params).is_err());
3351 }
3352 {
3353 let mut gpu_params: GpuParameters = Default::default();
3354 assert!(parse_gpu_display_options(Some("blah"), &mut gpu_params).is_err());
3355 }
3356 }
3357
3358 #[cfg(feature = "gpu")]
3359 #[test]
3360 fn parse_gpu_options_and_gpu_display_options_valid() {
3361 {
3362 let mut gpu_params: GpuParameters = Default::default();
3363 assert!(parse_gpu_options(Some("2D,width=500,height=600"), &mut gpu_params).is_ok());
3364 assert!(
3365 parse_gpu_display_options(Some("width=700,height=800"), &mut gpu_params).is_ok()
3366 );
3367 assert_eq!(gpu_params.displays.len(), 2);
3368 assert_eq!(gpu_params.displays[0].width, 500);
3369 assert_eq!(gpu_params.displays[0].height, 600);
3370 assert_eq!(gpu_params.displays[1].width, 700);
3371 assert_eq!(gpu_params.displays[1].height, 800);
3372 }
3373 {
3374 let mut gpu_params: GpuParameters = Default::default();
3375 assert!(parse_gpu_options(Some("2D"), &mut gpu_params).is_ok());
3376 assert!(
3377 parse_gpu_display_options(Some("width=700,height=800"), &mut gpu_params).is_ok()
3378 );
3379 assert_eq!(gpu_params.displays.len(), 1);
3380 assert_eq!(gpu_params.displays[0].width, 700);
3381 assert_eq!(gpu_params.displays[0].height, 800);
3382 }
Kaiyi Lidd348a42020-07-13 11:49:46 -07003383 }
Chuanxiao Dongfd5626c2020-04-27 16:35:33 +08003384
3385 #[test]
3386 fn parse_battery_vaild() {
3387 parse_battery_options(Some("type=goldfish")).expect("parse should have succeded");
3388 }
3389
3390 #[test]
3391 fn parse_battery_vaild_no_type() {
3392 parse_battery_options(None).expect("parse should have succeded");
3393 }
3394
3395 #[test]
3396 fn parse_battery_invaild_parameter() {
3397 parse_battery_options(Some("tyep=goldfish")).expect_err("parse should have failed");
3398 }
3399
3400 #[test]
3401 fn parse_battery_invaild_type_value() {
3402 parse_battery_options(Some("type=xxx")).expect_err("parse should have failed");
3403 }
Daniel Verkamp107edb32019-04-05 09:58:48 -07003404}