blob: dd10c4037dfc961b7000c4ef60ad43c15a65beec [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;
Mattias Nisslerde2c6402021-10-21 12:05:29 +000010use std::convert::TryFrom;
Judy Hsiaod5c1e962020-02-04 12:30:01 +080011use std::default::Default;
Jingkui Wang100e6e42019-03-08 20:41:57 -080012use std::fs::{File, OpenOptions};
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -080013use std::io::{BufRead, BufReader};
Jingkui Wang100e6e42019-03-08 20:41:57 -080014use std::path::{Path, PathBuf};
Jorge E. Moreira359e7de2020-12-02 18:25:53 -080015use std::str::FromStr;
Zach Reizner639d9672017-05-01 17:57:18 -070016use std::string::String;
Zach Reizner39aa26b2017-12-12 18:03:23 -080017use std::thread::sleep;
Stephen Barber56fbf092017-06-29 16:12:14 -070018use std::time::Duration;
Zach Reizner639d9672017-05-01 17:57:18 -070019
Keiichi Watanabe553d2192021-08-16 16:42:27 +090020use arch::{set_default_serial_parameters, Pstore, VcpuAffinity};
Kevin Hamacher6fc5f202021-03-18 12:41:23 +010021use base::{debug, error, getpid, info, kill_process_group, reap_child, syslog, warn};
Tomasz Jeznach3ce74762021-02-26 01:01:53 -080022#[cfg(feature = "direct")]
23use crosvm::DirectIoOption;
Zach Reizner267f2c82019-07-31 17:07:27 -070024use crosvm::{
25 argument::{self, print_help, set_arguments, Argument},
Michael Hoylee47a5002020-10-15 16:24:13 -070026 platform, BindMount, Config, DiskOption, Executable, GidMap, SharedDir, TouchDeviceOption,
Christian Blichmann50f95912021-11-05 16:59:39 +010027 VfioCommand, VhostUserFsOption, VhostUserOption, VhostUserWlOption, VhostVsockDeviceParameter,
28 DISK_ID_LEN,
Zach Reizner267f2c82019-07-31 17:07:27 -070029};
Keiichi Watanabe553d2192021-08-16 16:42:27 +090030use devices::serial_device::{SerialHardware, SerialParameters, SerialType};
Chia-I Wucba95db2021-12-09 15:18:58 -080031#[cfg(all(feature = "gpu", feature = "virgl_renderer_next"))]
32use devices::virtio::gpu::GpuRenderServerParameters;
Woody Chow0b2b6062021-09-03 15:40:02 +090033#[cfg(feature = "audio_cras")]
34use devices::virtio::snd::cras_backend::Error as CrasSndError;
Keiichi Watanabeee4b58e2021-08-17 19:34:01 +090035use devices::virtio::vhost::user::device::{
Chirantan Ekbotef08bddd2021-09-10 18:41:06 +090036 run_block_device, run_console_device, run_fs_device, run_net_device, run_vsock_device,
37 run_wl_device,
Keiichi Watanabeee4b58e2021-08-17 19:34:01 +090038};
Alexandre Courbotb42b3e52021-07-09 23:38:57 +090039#[cfg(any(feature = "video-decoder", feature = "video-encoder"))]
40use devices::virtio::VideoBackendType;
Chirantan Ekbote78225292021-06-25 18:30:34 +090041#[cfg(feature = "gpu")]
42use devices::virtio::{
43 gpu::{
Chia-I Wucba95db2021-12-09 15:18:58 -080044 GpuDisplayParameters, GpuMode, GpuParameters, DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH,
Chirantan Ekbote78225292021-06-25 18:30:34 +090045 },
46 vhost::user::device::run_gpu_device,
47};
Junichi Uekawab180f9c2021-12-07 09:21:36 +090048#[cfg(feature = "direct")]
49use devices::BusRange;
Andrew Scull1590e6f2020-03-18 18:00:47 +000050#[cfg(feature = "audio")]
Daniel Verkampfbd61222020-02-14 16:46:36 -080051use devices::{Ac97Backend, Ac97Parameters};
Andrew Walbran00f1c9f2021-12-10 17:13:08 +000052use devices::{PciAddress, PciClassCode, StubPciParameters};
Daniel Verkampeb1640e2021-09-07 14:09:31 -070053use disk::{self, QcowFile};
Andrew Walbranfb7c1082021-06-17 18:12:14 +010054#[cfg(feature = "composite-disk")]
Jooyung Han2e14c732021-07-29 13:27:54 +090055use disk::{
56 create_composite_disk, create_disk_file, create_zero_filler, ImagePartitionType, PartitionInfo,
57};
Andrew Walbran00f1c9f2021-12-10 17:13:08 +000058use hypervisor::ProtectionType;
Jakub Starone7c59052019-04-09 12:31:14 -070059use vm_control::{
Kevin Hamacher6fc5f202021-03-18 12:41:23 +010060 client::{
61 do_modify_battery, do_usb_attach, do_usb_detach, do_usb_list, handle_request, vms_request,
62 ModifyUsbError, ModifyUsbResult,
63 },
64 BalloonControlCommand, BatteryType, DiskControlCommand, UsbControlResult, VmRequest,
Hikaru Nishida6b51c752021-05-21 12:37:43 +090065 VmResponse,
Jakub Starone7c59052019-04-09 12:31:14 -070066};
Zach Reizner639d9672017-05-01 17:57:18 -070067
Chirantan Ekbote520ad432021-12-03 17:43:37 +090068#[cfg(feature = "scudo")]
69#[global_allocator]
70static ALLOCATOR: scudo::GlobalScudoAllocator = scudo::GlobalScudoAllocator;
71
Cody Schuffelen6d1ab502019-05-21 12:12:38 -070072fn executable_is_plugin(executable: &Option<Executable>) -> bool {
Daniel Verkampc26d20b2020-11-04 14:39:31 -080073 matches!(executable, Some(Executable::Plugin(_)))
Cody Schuffelen6d1ab502019-05-21 12:12:38 -070074}
75
Stephen Barbera00753b2017-07-18 13:57:26 -070076// Wait for all children to exit. Return true if they have all exited, false
77// otherwise.
78fn wait_all_children() -> bool {
Stephen Barber49dd2e22018-10-29 18:29:58 -070079 const CHILD_WAIT_MAX_ITER: isize = 100;
Stephen Barbera00753b2017-07-18 13:57:26 -070080 const CHILD_WAIT_MS: u64 = 10;
81 for _ in 0..CHILD_WAIT_MAX_ITER {
Stephen Barbera00753b2017-07-18 13:57:26 -070082 loop {
Zach Reizner56158c82017-08-24 13:50:14 -070083 match reap_child() {
84 Ok(0) => break,
85 // We expect ECHILD which indicates that there were no children left.
86 Err(e) if e.errno() == libc::ECHILD => return true,
87 Err(e) => {
David Tolnayb4bd00f2019-02-12 17:51:26 -080088 warn!("error while waiting for children: {}", e);
Zach Reizner56158c82017-08-24 13:50:14 -070089 return false;
Stephen Barbera00753b2017-07-18 13:57:26 -070090 }
Zach Reizner56158c82017-08-24 13:50:14 -070091 // We reaped one child, so continue reaping.
Zach Reizner55a9e502018-10-03 10:22:32 -070092 _ => {}
Stephen Barbera00753b2017-07-18 13:57:26 -070093 }
94 }
Zach Reizner56158c82017-08-24 13:50:14 -070095 // There's no timeout option for waitpid which reap_child calls internally, so our only
96 // recourse is to sleep while waiting for the children to exit.
Stephen Barbera00753b2017-07-18 13:57:26 -070097 sleep(Duration::from_millis(CHILD_WAIT_MS));
98 }
99
100 // If we've made it to this point, not all of the children have exited.
David Tolnay5bbbf612018-12-01 17:49:30 -0800101 false
Stephen Barbera00753b2017-07-18 13:57:26 -0700102}
103
Daniel Verkamp107edb32019-04-05 09:58:48 -0700104/// Parse a comma-separated list of CPU numbers and ranges and convert it to a Vec of CPU numbers.
105fn parse_cpu_set(s: &str) -> argument::Result<Vec<usize>> {
106 let mut cpuset = Vec::new();
107 for part in s.split(',') {
108 let range: Vec<&str> = part.split('-').collect();
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +0900109 if range.is_empty() || range.len() > 2 {
Daniel Verkamp107edb32019-04-05 09:58:48 -0700110 return Err(argument::Error::InvalidValue {
111 value: part.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800112 expected: String::from("invalid list syntax"),
Daniel Verkamp107edb32019-04-05 09:58:48 -0700113 });
114 }
115 let first_cpu: usize = range[0]
116 .parse()
117 .map_err(|_| argument::Error::InvalidValue {
118 value: part.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800119 expected: String::from("CPU index must be a non-negative integer"),
Daniel Verkamp107edb32019-04-05 09:58:48 -0700120 })?;
121 let last_cpu: usize = if range.len() == 2 {
122 range[1]
123 .parse()
124 .map_err(|_| argument::Error::InvalidValue {
125 value: part.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800126 expected: String::from("CPU index must be a non-negative integer"),
Daniel Verkamp107edb32019-04-05 09:58:48 -0700127 })?
128 } else {
129 first_cpu
130 };
131
132 if last_cpu < first_cpu {
133 return Err(argument::Error::InvalidValue {
134 value: part.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800135 expected: String::from("CPU ranges must be from low to high"),
Daniel Verkamp107edb32019-04-05 09:58:48 -0700136 });
137 }
138
139 for cpu in first_cpu..=last_cpu {
140 cpuset.push(cpu);
141 }
142 }
143 Ok(cpuset)
144}
145
Daniel Verkampc677fb42020-09-08 13:47:49 -0700146/// Parse a list of guest to host CPU mappings.
147///
148/// Each mapping consists of a single guest CPU index mapped to one or more host CPUs in the form
149/// accepted by `parse_cpu_set`:
150///
151/// `<GUEST-CPU>=<HOST-CPU-SET>[:<GUEST-CPU>=<HOST-CPU-SET>[:...]]`
152fn parse_cpu_affinity(s: &str) -> argument::Result<VcpuAffinity> {
153 if s.contains('=') {
154 let mut affinity_map = BTreeMap::new();
155 for cpu_pair in s.split(':') {
156 let assignment: Vec<&str> = cpu_pair.split('=').collect();
157 if assignment.len() != 2 {
158 return Err(argument::Error::InvalidValue {
159 value: cpu_pair.to_owned(),
160 expected: String::from("invalid VCPU assignment syntax"),
161 });
162 }
163 let guest_cpu = assignment[0]
164 .parse()
165 .map_err(|_| argument::Error::InvalidValue {
166 value: assignment[0].to_owned(),
167 expected: String::from("CPU index must be a non-negative integer"),
168 })?;
169 let host_cpu_set = parse_cpu_set(assignment[1])?;
170 if affinity_map.insert(guest_cpu, host_cpu_set).is_some() {
171 return Err(argument::Error::InvalidValue {
172 value: cpu_pair.to_owned(),
173 expected: String::from("VCPU index must be unique"),
174 });
175 }
176 }
177 Ok(VcpuAffinity::PerVcpu(affinity_map))
178 } else {
179 Ok(VcpuAffinity::Global(parse_cpu_set(s)?))
180 }
181}
182
Daniel Verkamp8a72afc2021-03-15 17:55:52 -0700183fn parse_cpu_capacity(s: &str, cpu_capacity: &mut BTreeMap<usize, u32>) -> argument::Result<()> {
184 for cpu_pair in s.split(',') {
185 let assignment: Vec<&str> = cpu_pair.split('=').collect();
186 if assignment.len() != 2 {
187 return Err(argument::Error::InvalidValue {
188 value: cpu_pair.to_owned(),
189 expected: String::from("invalid CPU capacity syntax"),
190 });
191 }
192 let cpu = assignment[0]
193 .parse()
194 .map_err(|_| argument::Error::InvalidValue {
195 value: assignment[0].to_owned(),
196 expected: String::from("CPU index must be a non-negative integer"),
197 })?;
198 let capacity = assignment[1]
199 .parse()
200 .map_err(|_| argument::Error::InvalidValue {
201 value: assignment[1].to_owned(),
202 expected: String::from("CPU capacity must be a non-negative integer"),
203 })?;
204 if cpu_capacity.insert(cpu, capacity).is_some() {
205 return Err(argument::Error::InvalidValue {
206 value: cpu_pair.to_owned(),
207 expected: String::from("CPU index must be unique"),
208 });
209 }
210 }
211 Ok(())
212}
213
Jason Macnakcc7070b2019-11-06 14:48:12 -0800214#[cfg(feature = "gpu")]
Jason Macnakd659a0d2021-03-15 15:33:01 -0700215fn parse_gpu_options(s: Option<&str>, gpu_params: &mut GpuParameters) -> argument::Result<()> {
Kaiyi Lidd348a42020-07-13 11:49:46 -0700216 #[cfg(feature = "gfxstream")]
217 let mut vulkan_specified = false;
218 #[cfg(feature = "gfxstream")]
219 let mut syncfd_specified = false;
Jason Macnak046ed142020-10-08 09:11:53 -0700220 #[cfg(feature = "gfxstream")]
221 let mut angle_specified = false;
Jason Macnakcc7070b2019-11-06 14:48:12 -0800222
Jason Macnakd659a0d2021-03-15 15:33:01 -0700223 let mut display_w: Option<u32> = None;
224 let mut display_h: Option<u32> = None;
225
Jason Macnakcc7070b2019-11-06 14:48:12 -0800226 if let Some(s) = s {
227 let opts = s
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +0900228 .split(',')
229 .map(|frag| frag.split('='))
Jason Macnakcc7070b2019-11-06 14:48:12 -0800230 .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
231
232 for (k, v) in opts {
233 match k {
Lingfeng Yangddbe8b72020-01-30 10:00:36 -0800234 // Deprecated: Specifying --gpu=<mode> Not great as the mode can be set multiple
Chia-I Wue25859b2021-04-14 13:57:55 -0700235 // times if the user specifies several modes (--gpu=2d,virglrenderer,gfxstream)
Jason Macnak327fc242020-01-10 12:45:36 -0800236 "2d" | "2D" => {
237 gpu_params.mode = GpuMode::Mode2D;
238 }
Chia-I Wue25859b2021-04-14 13:57:55 -0700239 "3d" | "3D" | "virglrenderer" => {
240 gpu_params.mode = GpuMode::ModeVirglRenderer;
Jason Macnak327fc242020-01-10 12:45:36 -0800241 }
Lingfeng Yangddbe8b72020-01-30 10:00:36 -0800242 #[cfg(feature = "gfxstream")]
243 "gfxstream" => {
Gurchetan Singhb1394f72020-10-19 18:31:13 -0700244 gpu_params.mode = GpuMode::ModeGfxstream;
Lingfeng Yangddbe8b72020-01-30 10:00:36 -0800245 }
246 // Preferred: Specifying --gpu,backend=<mode>
247 "backend" => match v {
248 "2d" | "2D" => {
249 gpu_params.mode = GpuMode::Mode2D;
250 }
Chia-I Wue25859b2021-04-14 13:57:55 -0700251 "3d" | "3D" | "virglrenderer" => {
252 gpu_params.mode = GpuMode::ModeVirglRenderer;
Lingfeng Yangddbe8b72020-01-30 10:00:36 -0800253 }
254 #[cfg(feature = "gfxstream")]
255 "gfxstream" => {
Gurchetan Singhb1394f72020-10-19 18:31:13 -0700256 gpu_params.mode = GpuMode::ModeGfxstream;
Lingfeng Yangddbe8b72020-01-30 10:00:36 -0800257 }
258 _ => {
259 return Err(argument::Error::InvalidValue {
260 value: v.to_string(),
Judy Hsiao59343052020-03-16 15:58:03 +0800261 expected: String::from(
Chia-I Wue25859b2021-04-14 13:57:55 -0700262 "gpu parameter 'backend' should be one of (2d|virglrenderer|gfxstream)",
Judy Hsiao59343052020-03-16 15:58:03 +0800263 ),
Lingfeng Yangddbe8b72020-01-30 10:00:36 -0800264 });
265 }
266 },
Jason Macnakbf195582019-11-20 16:25:49 -0800267 "egl" => match v {
268 "true" | "" => {
269 gpu_params.renderer_use_egl = true;
270 }
271 "false" => {
272 gpu_params.renderer_use_egl = false;
273 }
274 _ => {
275 return Err(argument::Error::InvalidValue {
276 value: v.to_string(),
Judy Hsiao59343052020-03-16 15:58:03 +0800277 expected: String::from("gpu parameter 'egl' should be a boolean"),
Jason Macnakbf195582019-11-20 16:25:49 -0800278 });
279 }
280 },
281 "gles" => match v {
282 "true" | "" => {
283 gpu_params.renderer_use_gles = true;
284 }
285 "false" => {
286 gpu_params.renderer_use_gles = false;
287 }
288 _ => {
289 return Err(argument::Error::InvalidValue {
290 value: v.to_string(),
Judy Hsiao59343052020-03-16 15:58:03 +0800291 expected: String::from("gpu parameter 'gles' should be a boolean"),
Jason Macnakbf195582019-11-20 16:25:49 -0800292 });
293 }
294 },
295 "glx" => match v {
296 "true" | "" => {
297 gpu_params.renderer_use_glx = true;
298 }
299 "false" => {
300 gpu_params.renderer_use_glx = false;
301 }
302 _ => {
303 return Err(argument::Error::InvalidValue {
304 value: v.to_string(),
Judy Hsiao59343052020-03-16 15:58:03 +0800305 expected: String::from("gpu parameter 'glx' should be a boolean"),
Jason Macnakbf195582019-11-20 16:25:49 -0800306 });
307 }
308 },
309 "surfaceless" => match v {
310 "true" | "" => {
311 gpu_params.renderer_use_surfaceless = true;
312 }
313 "false" => {
314 gpu_params.renderer_use_surfaceless = false;
315 }
316 _ => {
317 return Err(argument::Error::InvalidValue {
318 value: v.to_string(),
Judy Hsiao59343052020-03-16 15:58:03 +0800319 expected: String::from(
320 "gpu parameter 'surfaceless' should be a boolean",
321 ),
Jason Macnakbf195582019-11-20 16:25:49 -0800322 });
323 }
324 },
Kaiyi Li6404e452020-07-07 19:12:03 -0700325 #[cfg(feature = "gfxstream")]
Kaiyi Lidd348a42020-07-13 11:49:46 -0700326 "syncfd" => {
327 syncfd_specified = true;
328 match v {
329 "true" | "" => {
330 gpu_params.gfxstream_use_syncfd = true;
331 }
332 "false" => {
333 gpu_params.gfxstream_use_syncfd = false;
334 }
335 _ => {
336 return Err(argument::Error::InvalidValue {
337 value: v.to_string(),
338 expected: String::from(
339 "gpu parameter 'syncfd' should be a boolean",
340 ),
341 });
342 }
Kaiyi Li6404e452020-07-07 19:12:03 -0700343 }
Kaiyi Lidd348a42020-07-13 11:49:46 -0700344 }
Kaiyi Li6c52a2e2020-07-07 19:28:32 -0700345 #[cfg(feature = "gfxstream")]
Jason Macnak046ed142020-10-08 09:11:53 -0700346 "angle" => {
347 angle_specified = true;
348 match v {
349 "true" | "" => {
350 gpu_params.gfxstream_use_guest_angle = true;
351 }
352 "false" => {
353 gpu_params.gfxstream_use_guest_angle = false;
354 }
355 _ => {
356 return Err(argument::Error::InvalidValue {
357 value: v.to_string(),
358 expected: String::from("gpu parameter 'angle' should be a boolean"),
359 });
360 }
361 }
362 }
Kaiyi Lidd348a42020-07-13 11:49:46 -0700363 "vulkan" => {
Chia-I Wu6d473b32021-04-12 10:14:24 -0700364 #[cfg(feature = "gfxstream")]
365 {
366 vulkan_specified = true;
367 }
Kaiyi Lidd348a42020-07-13 11:49:46 -0700368 match v {
369 "true" | "" => {
Chia-I Wu0beb2462021-04-12 09:36:08 -0700370 gpu_params.use_vulkan = true;
Kaiyi Lidd348a42020-07-13 11:49:46 -0700371 }
372 "false" => {
Chia-I Wu0beb2462021-04-12 09:36:08 -0700373 gpu_params.use_vulkan = false;
Kaiyi Lidd348a42020-07-13 11:49:46 -0700374 }
375 _ => {
376 return Err(argument::Error::InvalidValue {
377 value: v.to_string(),
378 expected: String::from(
379 "gpu parameter 'vulkan' should be a boolean",
380 ),
381 });
382 }
Kaiyi Li6c52a2e2020-07-07 19:28:32 -0700383 }
Kaiyi Lidd348a42020-07-13 11:49:46 -0700384 }
Jason Macnakcc7070b2019-11-06 14:48:12 -0800385 "width" => {
Jason Macnakd659a0d2021-03-15 15:33:01 -0700386 let width = v
387 .parse::<u32>()
388 .map_err(|_| argument::Error::InvalidValue {
389 value: v.to_string(),
390 expected: String::from("gpu parameter 'width' must be a valid integer"),
391 })?;
392 display_w = Some(width);
Jason Macnakcc7070b2019-11-06 14:48:12 -0800393 }
394 "height" => {
Jason Macnakd659a0d2021-03-15 15:33:01 -0700395 let height = v
396 .parse::<u32>()
397 .map_err(|_| argument::Error::InvalidValue {
398 value: v.to_string(),
399 expected: String::from(
400 "gpu parameter 'height' must be a valid integer",
401 ),
402 })?;
403 display_h = Some(height);
Jason Macnakcc7070b2019-11-06 14:48:12 -0800404 }
John Batesb220eac2020-09-14 17:03:02 -0700405 "cache-path" => gpu_params.cache_path = Some(v.to_string()),
406 "cache-size" => gpu_params.cache_size = Some(v.to_string()),
Gurchetan Singh401340e2021-03-23 09:34:00 -0700407 "udmabuf" => match v {
408 "true" | "" => {
409 gpu_params.udmabuf = true;
410 }
411 "false" => {
412 gpu_params.udmabuf = false;
413 }
414 _ => {
415 return Err(argument::Error::InvalidValue {
416 value: v.to_string(),
417 expected: String::from("gpu parameter 'udmabuf' should be a boolean"),
418 });
419 }
420 },
Jason Macnakcc7070b2019-11-06 14:48:12 -0800421 "" => {}
422 _ => {
423 return Err(argument::Error::UnknownArgument(format!(
424 "gpu parameter {}",
425 k
426 )));
427 }
428 }
429 }
430 }
431
Jason Macnakd659a0d2021-03-15 15:33:01 -0700432 if display_w.is_some() || display_h.is_some() {
433 if display_w.is_none() || display_h.is_none() {
434 return Err(argument::Error::InvalidValue {
435 value: s.unwrap_or("").to_string(),
436 expected: String::from(
437 "gpu must include both 'width' and 'height' if either is supplied",
438 ),
439 });
440 }
441
442 gpu_params.displays.push(GpuDisplayParameters {
443 width: display_w.unwrap(),
444 height: display_h.unwrap(),
445 });
446 }
447
Kaiyi Lidd348a42020-07-13 11:49:46 -0700448 #[cfg(feature = "gfxstream")]
449 {
Chia-I Wu91df6562021-04-12 09:47:38 -0700450 if !vulkan_specified && gpu_params.mode == GpuMode::ModeGfxstream {
451 gpu_params.use_vulkan = true;
452 }
453
Chia-I Wu6d473b32021-04-12 10:14:24 -0700454 if syncfd_specified || angle_specified {
Kaiyi Lidd348a42020-07-13 11:49:46 -0700455 match gpu_params.mode {
Gurchetan Singhb1394f72020-10-19 18:31:13 -0700456 GpuMode::ModeGfxstream => {}
Kaiyi Lidd348a42020-07-13 11:49:46 -0700457 _ => {
458 return Err(argument::Error::UnknownArgument(
Chia-I Wu6d473b32021-04-12 10:14:24 -0700459 "gpu parameter syncfd and angle are only supported for gfxstream backend"
Kaiyi Lidd348a42020-07-13 11:49:46 -0700460 .to_string(),
461 ));
462 }
463 }
464 }
465 }
466
Jason Macnakd659a0d2021-03-15 15:33:01 -0700467 Ok(())
468}
469
Alexandre Courbotb42b3e52021-07-09 23:38:57 +0900470#[cfg(any(feature = "video-decoder", feature = "video-encoder"))]
471fn parse_video_options(s: Option<&str>) -> argument::Result<VideoBackendType> {
Alexandre Courbotc02960d2021-07-11 23:06:30 +0900472 const VALID_VIDEO_BACKENDS: &[&str] = &[
473 #[cfg(feature = "libvda")]
474 "libvda",
475 ];
Alexandre Courbotb42b3e52021-07-09 23:38:57 +0900476
477 match s {
Alexandre Courbotc02960d2021-07-11 23:06:30 +0900478 #[cfg(feature = "libvda")]
Alexandre Courbotb42b3e52021-07-09 23:38:57 +0900479 None => Ok(VideoBackendType::Libvda),
Alexandre Courbotc02960d2021-07-11 23:06:30 +0900480 #[cfg(feature = "libvda")]
Alexandre Courbotb42b3e52021-07-09 23:38:57 +0900481 Some("libvda") => Ok(VideoBackendType::Libvda),
David Staessensa78ae7a2021-07-15 09:48:56 +0900482 #[cfg(feature = "libvda")]
Alexandre Courbot54cf8342021-12-20 18:10:08 +0900483 Some("libvda-vd") => Ok(VideoBackendType::LibvdaVd),
Alexandre Courbotb42b3e52021-07-09 23:38:57 +0900484 Some(s) => Err(argument::Error::InvalidValue {
485 value: s.to_owned(),
486 expected: format!("should be one of ({})", VALID_VIDEO_BACKENDS.join("|")),
487 }),
488 }
489}
490
Jason Macnakd659a0d2021-03-15 15:33:01 -0700491#[cfg(feature = "gpu")]
492fn parse_gpu_display_options(
493 s: Option<&str>,
494 gpu_params: &mut GpuParameters,
495) -> argument::Result<()> {
496 let mut display_w: Option<u32> = None;
497 let mut display_h: Option<u32> = None;
498
499 if let Some(s) = s {
500 let opts = s
501 .split(',')
502 .map(|frag| frag.split('='))
503 .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
504
505 for (k, v) in opts {
506 match k {
507 "width" => {
508 let width = v
509 .parse::<u32>()
510 .map_err(|_| argument::Error::InvalidValue {
511 value: v.to_string(),
512 expected: String::from("gpu parameter 'width' must be a valid integer"),
513 })?;
514 display_w = Some(width);
515 }
516 "height" => {
517 let height = v
518 .parse::<u32>()
519 .map_err(|_| argument::Error::InvalidValue {
520 value: v.to_string(),
521 expected: String::from(
522 "gpu parameter 'height' must be a valid integer",
523 ),
524 })?;
525 display_h = Some(height);
526 }
527 "" => {}
528 _ => {
529 return Err(argument::Error::UnknownArgument(format!(
530 "gpu-display parameter {}",
531 k
532 )));
533 }
534 }
535 }
536 }
537
538 if display_w.is_none() || display_h.is_none() {
539 return Err(argument::Error::InvalidValue {
540 value: s.unwrap_or("").to_string(),
541 expected: String::from("gpu-display must include both 'width' and 'height'"),
542 });
543 }
544
545 gpu_params.displays.push(GpuDisplayParameters {
546 width: display_w.unwrap(),
547 height: display_h.unwrap(),
548 });
549
550 Ok(())
Jason Macnakcc7070b2019-11-06 14:48:12 -0800551}
552
Chia-I Wu16fb6592021-11-10 11:45:32 -0800553#[cfg(all(feature = "gpu", feature = "virgl_renderer_next"))]
554fn parse_gpu_render_server_options(
555 s: Option<&str>,
556 gpu_params: &mut GpuParameters,
557) -> argument::Result<()> {
558 let mut path: Option<PathBuf> = None;
559
560 if let Some(s) = s {
561 let opts = s
562 .split(',')
563 .map(|frag| frag.split('='))
564 .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
565
566 for (k, v) in opts {
567 match k {
568 "path" => {
569 path =
570 Some(
571 PathBuf::from_str(v).map_err(|e| argument::Error::InvalidValue {
572 value: v.to_string(),
573 expected: e.to_string(),
574 })?,
575 )
576 }
577 "" => {}
578 _ => {
579 return Err(argument::Error::UnknownArgument(format!(
580 "gpu-render-server parameter {}",
581 k
582 )));
583 }
584 }
585 }
586 }
587
588 if let Some(p) = path {
589 gpu_params.render_server = Some(GpuRenderServerParameters { path: p });
590 Ok(())
591 } else {
592 Err(argument::Error::InvalidValue {
593 value: s.unwrap_or("").to_string(),
594 expected: String::from("gpu-render-server must include 'path'"),
595 })
596 }
597}
598
Andrew Scull1590e6f2020-03-18 18:00:47 +0000599#[cfg(feature = "audio")]
Judy Hsiaod5c1e962020-02-04 12:30:01 +0800600fn parse_ac97_options(s: &str) -> argument::Result<Ac97Parameters> {
601 let mut ac97_params: Ac97Parameters = Default::default();
602
603 let opts = s
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +0900604 .split(',')
605 .map(|frag| frag.split('='))
Judy Hsiaod5c1e962020-02-04 12:30:01 +0800606 .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
607
608 for (k, v) in opts {
609 match k {
610 "backend" => {
611 ac97_params.backend =
612 v.parse::<Ac97Backend>()
613 .map_err(|e| argument::Error::InvalidValue {
614 value: v.to_string(),
615 expected: e.to_string(),
616 })?;
617 }
618 "capture" => {
619 ac97_params.capture = v.parse::<bool>().map_err(|e| {
620 argument::Error::Syntax(format!("invalid capture option: {}", e))
621 })?;
622 }
Dennis Kempin50a58f92021-06-23 11:34:31 -0700623 #[cfg(feature = "audio_cras")]
paulhsia83d51602021-03-09 17:13:14 +0800624 "client_type" => {
625 ac97_params
626 .set_client_type(v)
627 .map_err(|e| argument::Error::InvalidValue {
628 value: v.to_string(),
629 expected: e.to_string(),
630 })?;
631 }
Woody Chowb27dea42021-09-08 15:51:22 +0900632 #[cfg(feature = "audio_cras")]
633 "socket_type" => {
634 ac97_params
635 .set_socket_type(v)
636 .map_err(|e| argument::Error::InvalidValue {
637 value: v.to_string(),
638 expected: e.to_string(),
639 })?;
640 }
Jorge E. Moreira6a88a5d2021-03-12 15:34:46 -0800641 #[cfg(any(target_os = "linux", target_os = "android"))]
Jorge E. Moreira359e7de2020-12-02 18:25:53 -0800642 "server" => {
643 ac97_params.vios_server_path =
644 Some(
645 PathBuf::from_str(v).map_err(|e| argument::Error::InvalidValue {
646 value: v.to_string(),
647 expected: e.to_string(),
648 })?,
649 );
650 }
Judy Hsiaod5c1e962020-02-04 12:30:01 +0800651 _ => {
652 return Err(argument::Error::UnknownArgument(format!(
653 "unknown ac97 parameter {}",
654 k
655 )));
656 }
657 }
658 }
659
Jorge E. Moreira359e7de2020-12-02 18:25:53 -0800660 // server is required for and exclusive to vios backend
Jorge E. Moreira6a88a5d2021-03-12 15:34:46 -0800661 #[cfg(any(target_os = "linux", target_os = "android"))]
Jorge E. Moreira359e7de2020-12-02 18:25:53 -0800662 match ac97_params.backend {
663 Ac97Backend::VIOS => {
664 if ac97_params.vios_server_path.is_none() {
665 return Err(argument::Error::ExpectedArgument(String::from(
666 "server argument is required for VIOS backend",
667 )));
668 }
669 }
670 _ => {
671 if ac97_params.vios_server_path.is_some() {
672 return Err(argument::Error::UnexpectedValue(String::from(
673 "server argument is exclusive to the VIOS backend",
674 )));
675 }
676 }
677 }
678
Judy Hsiaod5c1e962020-02-04 12:30:01 +0800679 Ok(ac97_params)
680}
681
Trent Begin17ccaad2019-04-17 13:51:25 -0600682fn parse_serial_options(s: &str) -> argument::Result<SerialParameters> {
683 let mut serial_setting = SerialParameters {
684 type_: SerialType::Sink,
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -0700685 hardware: SerialHardware::Serial,
Trent Begin17ccaad2019-04-17 13:51:25 -0600686 path: None,
Iliyan Malchev2c1417b2020-04-14 09:40:41 -0700687 input: None,
Trent Begin923bab02019-06-17 13:48:06 -0600688 num: 1,
Trent Begin17ccaad2019-04-17 13:51:25 -0600689 console: false,
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -0700690 earlycon: false,
Jorge E. Moreira1e262302019-08-01 14:40:03 -0700691 stdin: false,
Trent Begin17ccaad2019-04-17 13:51:25 -0600692 };
693
694 let opts = s
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +0900695 .split(',')
Nicholas Hollingumc76c2da2020-09-18 15:53:16 +1000696 .map(|frag| frag.splitn(2, '='))
Trent Begin17ccaad2019-04-17 13:51:25 -0600697 .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
698
699 for (k, v) in opts {
700 match k {
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -0700701 "hardware" => {
702 serial_setting.hardware = v
703 .parse::<SerialHardware>()
704 .map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?
705 }
Trent Begin17ccaad2019-04-17 13:51:25 -0600706 "type" => {
707 serial_setting.type_ = v
708 .parse::<SerialType>()
709 .map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?
710 }
711 "num" => {
712 let num = v.parse::<u8>().map_err(|e| {
713 argument::Error::Syntax(format!("serial device number is not parsable: {}", e))
714 })?;
A. Cody Schuffelen3faab5a2020-10-05 17:29:16 -0700715 if num < 1 {
Trent Begin17ccaad2019-04-17 13:51:25 -0600716 return Err(argument::Error::InvalidValue {
717 value: num.to_string(),
A. Cody Schuffelen3faab5a2020-10-05 17:29:16 -0700718 expected: String::from("Serial port num must be at least 1"),
Trent Begin17ccaad2019-04-17 13:51:25 -0600719 });
720 }
721 serial_setting.num = num;
722 }
723 "console" => {
724 serial_setting.console = v.parse::<bool>().map_err(|e| {
725 argument::Error::Syntax(format!(
726 "serial device console is not parseable: {}",
727 e
728 ))
729 })?
730 }
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -0700731 "earlycon" => {
732 serial_setting.earlycon = v.parse::<bool>().map_err(|e| {
733 argument::Error::Syntax(format!(
734 "serial device earlycon is not parseable: {}",
735 e,
736 ))
737 })?
738 }
Jorge E. Moreira1e262302019-08-01 14:40:03 -0700739 "stdin" => {
740 serial_setting.stdin = v.parse::<bool>().map_err(|e| {
741 argument::Error::Syntax(format!("serial device stdin is not parseable: {}", e))
Iliyan Malchev2c1417b2020-04-14 09:40:41 -0700742 })?;
743 if serial_setting.stdin && serial_setting.input.is_some() {
744 return Err(argument::Error::TooManyArguments(
745 "Cannot specify both stdin and input options".to_string(),
746 ));
747 }
Jorge E. Moreira1e262302019-08-01 14:40:03 -0700748 }
Jorge E. Moreira9c9e0e72019-05-17 13:57:04 -0700749 "path" => serial_setting.path = Some(PathBuf::from(v)),
Iliyan Malchev2c1417b2020-04-14 09:40:41 -0700750 "input" => {
751 if serial_setting.stdin {
752 return Err(argument::Error::TooManyArguments(
753 "Cannot specify both stdin and input options".to_string(),
754 ));
755 }
756 serial_setting.input = Some(PathBuf::from(v));
757 }
Trent Begin17ccaad2019-04-17 13:51:25 -0600758 _ => {
759 return Err(argument::Error::UnknownArgument(format!(
760 "serial parameter {}",
761 k
762 )));
763 }
764 }
765 }
766
A. Cody Schuffelen3faab5a2020-10-05 17:29:16 -0700767 if serial_setting.hardware == SerialHardware::Serial && serial_setting.num > 4 {
768 return Err(argument::Error::InvalidValue {
769 value: serial_setting.num.to_string(),
770 expected: String::from("Serial port num must be 4 or less"),
771 });
772 }
773
Trent Begin17ccaad2019-04-17 13:51:25 -0600774 Ok(serial_setting)
775}
776
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800777fn parse_plugin_mount_option(value: &str) -> argument::Result<BindMount> {
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +0900778 let components: Vec<&str> = value.split(':').collect();
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800779 if components.is_empty() || components.len() > 3 || components[0].is_empty() {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800780 return Err(argument::Error::InvalidValue {
781 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800782 expected: String::from(
783 "`plugin-mount` should be in a form of: <src>[:[<dst>][:<writable>]]",
784 ),
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800785 });
786 }
787
788 let src = PathBuf::from(components[0]);
789 if src.is_relative() {
790 return Err(argument::Error::InvalidValue {
791 value: components[0].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800792 expected: String::from("the source path for `plugin-mount` must be absolute"),
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800793 });
794 }
795 if !src.exists() {
796 return Err(argument::Error::InvalidValue {
797 value: components[0].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800798 expected: String::from("the source path for `plugin-mount` does not exist"),
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800799 });
800 }
801
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800802 let dst = PathBuf::from(match components.get(1) {
803 None | Some(&"") => components[0],
804 Some(path) => path,
805 });
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800806 if dst.is_relative() {
807 return Err(argument::Error::InvalidValue {
808 value: components[1].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800809 expected: String::from("the destination path for `plugin-mount` must be absolute"),
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800810 });
811 }
812
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800813 let writable: bool = match components.get(2) {
814 None => false,
815 Some(s) => s.parse().map_err(|_| argument::Error::InvalidValue {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800816 value: components[2].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800817 expected: String::from("the <writable> component for `plugin-mount` is not valid bool"),
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800818 })?,
819 };
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800820
821 Ok(BindMount { src, dst, writable })
822}
823
824fn parse_plugin_gid_map_option(value: &str) -> argument::Result<GidMap> {
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +0900825 let components: Vec<&str> = value.split(':').collect();
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800826 if components.is_empty() || components.len() > 3 || components[0].is_empty() {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800827 return Err(argument::Error::InvalidValue {
828 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800829 expected: String::from(
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800830 "`plugin-gid-map` must have exactly 3 components: <inner>[:[<outer>][:<count>]]",
Judy Hsiao59343052020-03-16 15:58:03 +0800831 ),
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800832 });
833 }
834
835 let inner: libc::gid_t = components[0]
836 .parse()
837 .map_err(|_| argument::Error::InvalidValue {
838 value: components[0].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800839 expected: String::from("the <inner> component for `plugin-gid-map` is not valid gid"),
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800840 })?;
841
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800842 let outer: libc::gid_t = match components.get(1) {
843 None | Some(&"") => inner,
844 Some(s) => s.parse().map_err(|_| argument::Error::InvalidValue {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800845 value: components[1].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800846 expected: String::from("the <outer> component for `plugin-gid-map` is not valid gid"),
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800847 })?,
848 };
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800849
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800850 let count: u32 = match components.get(2) {
851 None => 1,
852 Some(s) => s.parse().map_err(|_| argument::Error::InvalidValue {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800853 value: components[2].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800854 expected: String::from(
855 "the <count> component for `plugin-gid-map` is not valid number",
856 ),
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800857 })?,
858 };
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800859
860 Ok(GidMap {
861 inner,
862 outer,
863 count,
864 })
865}
866
Chuanxiao Dongfd5626c2020-04-27 16:35:33 +0800867fn parse_battery_options(s: Option<&str>) -> argument::Result<BatteryType> {
868 let mut battery_type: BatteryType = Default::default();
869
870 if let Some(s) = s {
871 let opts = s
Andrew Walbran9cfdbd92021-01-11 17:40:34 +0000872 .split(',')
873 .map(|frag| frag.split('='))
Chuanxiao Dongfd5626c2020-04-27 16:35:33 +0800874 .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
875
876 for (k, v) in opts {
877 match k {
878 "type" => match v.parse::<BatteryType>() {
879 Ok(type_) => battery_type = type_,
880 Err(e) => {
881 return Err(argument::Error::InvalidValue {
882 value: v.to_string(),
883 expected: e.to_string(),
884 });
885 }
886 },
887 "" => {}
888 _ => {
889 return Err(argument::Error::UnknownArgument(format!(
890 "battery parameter {}",
891 k
892 )));
893 }
894 }
895 }
896 }
897
898 Ok(battery_type)
899}
900
Tomasz Jeznach3ce74762021-02-26 01:01:53 -0800901#[cfg(feature = "direct")]
Junichi Uekawab6a6e942021-12-07 05:49:30 +0900902fn parse_hex_or_decimal(maybe_hex_string: &str) -> Result<u64, std::num::ParseIntError> {
903 // Parse string starting with 0x as hex and others as numbers.
904 let without_prefix = maybe_hex_string.strip_prefix("0x");
905 match without_prefix {
906 Some(hex_string) => u64::from_str_radix(hex_string, 16),
907 None => u64::from_str(maybe_hex_string),
908 }
909}
910
911#[cfg(feature = "direct")]
Tomasz Jeznach3ce74762021-02-26 01:01:53 -0800912fn parse_direct_io_options(s: Option<&str>) -> argument::Result<DirectIoOption> {
913 let s = s.ok_or(argument::Error::ExpectedValue(String::from(
914 "expected path@range[,range] value",
915 )))?;
916 let parts: Vec<&str> = s.splitn(2, '@').collect();
917 if parts.len() != 2 {
918 return Err(argument::Error::InvalidValue {
919 value: s.to_string(),
920 expected: String::from("missing port range, use /path@X-Y,Z,.. syntax"),
921 });
922 }
923 let path = PathBuf::from(parts[0]);
924 if !path.exists() {
925 return Err(argument::Error::InvalidValue {
926 value: parts[0].to_owned(),
927 expected: String::from("the path does not exist"),
928 });
929 };
Junichi Uekawab180f9c2021-12-07 09:21:36 +0900930 let ranges: argument::Result<Vec<BusRange>> = parts[1]
Tomasz Jeznach3ce74762021-02-26 01:01:53 -0800931 .split(',')
932 .map(|frag| frag.split('-'))
933 .map(|mut range| {
934 let base = range
935 .next()
Junichi Uekawab6a6e942021-12-07 05:49:30 +0900936 .map(|v| parse_hex_or_decimal(v))
Tomasz Jeznach3ce74762021-02-26 01:01:53 -0800937 .map_or(Ok(None), |r| r.map(Some));
938 let last = range
939 .next()
Junichi Uekawab6a6e942021-12-07 05:49:30 +0900940 .map(|v| parse_hex_or_decimal(v))
Tomasz Jeznach3ce74762021-02-26 01:01:53 -0800941 .map_or(Ok(None), |r| r.map(Some));
942 (base, last)
943 })
944 .map(|range| match range {
Junichi Uekawab180f9c2021-12-07 09:21:36 +0900945 (Ok(Some(base)), Ok(None)) => Ok(BusRange { base, len: 1 }),
946 (Ok(Some(base)), Ok(Some(last))) => Ok(BusRange {
947 base,
948 len: last.saturating_sub(base).saturating_add(1),
949 }),
Junichi Uekawa4d312052021-12-08 16:59:38 +0900950 (Err(_), _) => Err(argument::Error::InvalidValue {
951 value: s.to_owned(),
Tomasz Jeznach3ce74762021-02-26 01:01:53 -0800952 expected: String::from("invalid base range value"),
953 }),
Junichi Uekawa4d312052021-12-08 16:59:38 +0900954 (_, Err(_)) => Err(argument::Error::InvalidValue {
955 value: s.to_owned(),
Tomasz Jeznach3ce74762021-02-26 01:01:53 -0800956 expected: String::from("invalid last range value"),
957 }),
958 _ => Err(argument::Error::InvalidValue {
959 value: s.to_owned(),
960 expected: String::from("invalid range format"),
961 }),
962 })
963 .collect();
964 Ok(DirectIoOption {
965 path,
966 ranges: ranges?,
967 })
968}
969
Mattias Nisslerde2c6402021-10-21 12:05:29 +0000970fn parse_stub_pci_parameters(s: Option<&str>) -> argument::Result<StubPciParameters> {
971 let s = s.ok_or(argument::Error::ExpectedValue(String::from(
972 "stub-pci-device configuration expected",
973 )))?;
974
975 let mut options = argument::parse_key_value_options("stub-pci-device", s, ',');
976 let addr = options
977 .next()
978 .ok_or(argument::Error::ExpectedValue(String::from(
979 "stub-pci-device: expected device address",
980 )))?
981 .key();
982 let mut params = StubPciParameters {
983 address: PciAddress::from_string(addr),
984 vendor_id: 0,
985 device_id: 0,
986 class: PciClassCode::Other,
987 subclass: 0,
988 programming_interface: 0,
989 multifunction: false,
990 subsystem_device_id: 0,
991 subsystem_vendor_id: 0,
992 revision_id: 0,
993 };
994 for opt in options {
995 match opt.key() {
996 "vendor" => params.vendor_id = opt.parse_numeric::<u16>()?,
997 "device" => params.device_id = opt.parse_numeric::<u16>()?,
998 "class" => {
999 let class = opt.parse_numeric::<u32>()?;
1000 params.class = PciClassCode::try_from((class >> 16) as u8)
1001 .map_err(|_| opt.invalid_value_err(String::from("Unknown class code")))?;
1002 params.subclass = (class >> 8) as u8;
1003 params.programming_interface = class as u8;
1004 }
1005 "multifunction" => params.multifunction = opt.parse_or::<bool>(true)?,
1006 "subsystem_vendor" => params.subsystem_vendor_id = opt.parse_numeric::<u16>()?,
1007 "subsystem_device" => params.subsystem_device_id = opt.parse_numeric::<u16>()?,
1008 "revision" => params.revision_id = opt.parse_numeric::<u8>()?,
1009 _ => return Err(opt.invalid_key_err()),
1010 }
1011 }
1012
1013 Ok(params)
1014}
1015
Zach Reiznerefe95782017-08-26 18:05:48 -07001016fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::Result<()> {
1017 match name {
1018 "" => {
Cody Schuffelen6d1ab502019-05-21 12:12:38 -07001019 if cfg.executable_path.is_some() {
1020 return Err(argument::Error::TooManyArguments(format!(
1021 "A VM executable was already specified: {:?}",
1022 cfg.executable_path
1023 )));
Zach Reiznerefe95782017-08-26 18:05:48 -07001024 }
Cody Schuffelen6d1ab502019-05-21 12:12:38 -07001025 let kernel_path = PathBuf::from(value.unwrap());
1026 if !kernel_path.exists() {
1027 return Err(argument::Error::InvalidValue {
1028 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001029 expected: String::from("this kernel path does not exist"),
Cody Schuffelen6d1ab502019-05-21 12:12:38 -07001030 });
1031 }
1032 cfg.executable_path = Some(Executable::Kernel(kernel_path));
Zach Reiznerefe95782017-08-26 18:05:48 -07001033 }
Christian Blichmann33d56772021-03-04 19:03:54 +01001034 "kvm-device" => {
1035 let kvm_device_path = PathBuf::from(value.unwrap());
1036 if !kvm_device_path.exists() {
1037 return Err(argument::Error::InvalidValue {
1038 value: value.unwrap().to_owned(),
1039 expected: String::from("this kvm device path does not exist"),
1040 });
1041 }
1042
1043 cfg.kvm_device_path = kvm_device_path;
1044 }
Christian Blichmann50f95912021-11-05 16:59:39 +01001045 "vhost-vsock-fd" => {
1046 if cfg.vhost_vsock_device.is_some() {
1047 return Err(argument::Error::InvalidValue {
1048 value: value.unwrap().to_owned(),
1049 expected: String::from("A vhost-vsock device was already specified"),
1050 });
1051 }
1052 cfg.vhost_vsock_device = Some(VhostVsockDeviceParameter::Fd(
1053 value
1054 .unwrap()
1055 .parse()
1056 .map_err(|_| argument::Error::InvalidValue {
1057 value: value.unwrap().to_owned(),
1058 expected: String::from(
1059 "this value for `vhost-vsock-fd` needs to be integer",
1060 ),
1061 })?,
1062 ));
1063 }
Christian Blichmann2f5d4b62021-03-10 18:08:08 +01001064 "vhost-vsock-device" => {
Christian Blichmann50f95912021-11-05 16:59:39 +01001065 if cfg.vhost_vsock_device.is_some() {
1066 return Err(argument::Error::InvalidValue {
1067 value: value.unwrap().to_owned(),
1068 expected: String::from("A vhost-vsock device was already specified"),
1069 });
1070 }
Christian Blichmann2f5d4b62021-03-10 18:08:08 +01001071 let vhost_vsock_device_path = PathBuf::from(value.unwrap());
1072 if !vhost_vsock_device_path.exists() {
1073 return Err(argument::Error::InvalidValue {
1074 value: value.unwrap().to_owned(),
1075 expected: String::from("this vhost-vsock device path does not exist"),
1076 });
1077 }
1078
Christian Blichmann50f95912021-11-05 16:59:39 +01001079 cfg.vhost_vsock_device = Some(VhostVsockDeviceParameter::Path(vhost_vsock_device_path));
Christian Blichmann2f5d4b62021-03-10 18:08:08 +01001080 }
1081 "vhost-net-device" => {
1082 let vhost_net_device_path = PathBuf::from(value.unwrap());
1083 if !vhost_net_device_path.exists() {
1084 return Err(argument::Error::InvalidValue {
1085 value: value.unwrap().to_owned(),
1086 expected: String::from("this vhost-vsock device path does not exist"),
1087 });
1088 }
1089
1090 cfg.vhost_net_device_path = vhost_net_device_path;
1091 }
Tristan Muntsinger4133b012018-12-21 16:01:56 -08001092 "android-fstab" => {
1093 if cfg.android_fstab.is_some()
1094 && !cfg.android_fstab.as_ref().unwrap().as_os_str().is_empty()
1095 {
1096 return Err(argument::Error::TooManyArguments(
1097 "expected exactly one android fstab path".to_owned(),
1098 ));
1099 } else {
1100 let android_fstab = PathBuf::from(value.unwrap());
1101 if !android_fstab.exists() {
1102 return Err(argument::Error::InvalidValue {
1103 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001104 expected: String::from("this android fstab path does not exist"),
Tristan Muntsinger4133b012018-12-21 16:01:56 -08001105 });
1106 }
1107 cfg.android_fstab = Some(android_fstab);
1108 }
1109 }
Zach Reiznerefe95782017-08-26 18:05:48 -07001110 "params" => {
Zach Reiznerbb678712018-01-30 18:13:04 -08001111 cfg.params.push(value.unwrap().to_owned());
Zach Reiznerefe95782017-08-26 18:05:48 -07001112 }
1113 "cpus" => {
1114 if cfg.vcpu_count.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -07001115 return Err(argument::Error::TooManyArguments(
1116 "`cpus` already given".to_owned(),
1117 ));
Zach Reiznerefe95782017-08-26 18:05:48 -07001118 }
1119 cfg.vcpu_count =
Zach Reizner55a9e502018-10-03 10:22:32 -07001120 Some(
1121 value
1122 .unwrap()
1123 .parse()
1124 .map_err(|_| argument::Error::InvalidValue {
1125 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001126 expected: String::from("this value for `cpus` needs to be integer"),
Zach Reizner55a9e502018-10-03 10:22:32 -07001127 })?,
1128 )
Zach Reiznerefe95782017-08-26 18:05:48 -07001129 }
Daniel Verkamp107edb32019-04-05 09:58:48 -07001130 "cpu-affinity" => {
Daniel Verkampc677fb42020-09-08 13:47:49 -07001131 if cfg.vcpu_affinity.is_some() {
Daniel Verkamp107edb32019-04-05 09:58:48 -07001132 return Err(argument::Error::TooManyArguments(
1133 "`cpu-affinity` already given".to_owned(),
1134 ));
1135 }
Daniel Verkampc677fb42020-09-08 13:47:49 -07001136 cfg.vcpu_affinity = Some(parse_cpu_affinity(value.unwrap())?);
Daniel Verkamp107edb32019-04-05 09:58:48 -07001137 }
Daniel Verkamp8a72afc2021-03-15 17:55:52 -07001138 "cpu-cluster" => {
1139 cfg.cpu_clusters.push(parse_cpu_set(value.unwrap())?);
1140 }
1141 "cpu-capacity" => {
1142 parse_cpu_capacity(value.unwrap(), &mut cfg.cpu_capacity)?;
1143 }
Yusuke Sato31e136a2021-08-18 11:51:38 -07001144 "per-vm-core-scheduling" => {
1145 cfg.per_vm_core_scheduling = true;
1146 }
Woody Chow737ff122021-03-22 17:49:57 +09001147 #[cfg(feature = "audio_cras")]
1148 "cras-snd" => {
Chih-Yang Hsia41dc04f2021-12-08 16:04:23 +08001149 cfg.cras_snds.push(
Woody Chow0b2b6062021-09-03 15:40:02 +09001150 value
1151 .unwrap()
1152 .parse()
1153 .map_err(|e: CrasSndError| argument::Error::Syntax(e.to_string()))?,
1154 );
Woody Chow737ff122021-03-22 17:49:57 +09001155 }
Suleiman Souhlal015c3c12020-10-07 14:15:41 +09001156 "no-smt" => {
1157 cfg.no_smt = true;
1158 }
Kansho Nishidaab205af2020-08-13 18:17:50 +09001159 "rt-cpus" => {
1160 if !cfg.rt_cpus.is_empty() {
1161 return Err(argument::Error::TooManyArguments(
1162 "`rt-cpus` already given".to_owned(),
1163 ));
1164 }
1165 cfg.rt_cpus = parse_cpu_set(value.unwrap())?;
1166 }
Suleiman Souhlal63630e82021-02-18 11:53:11 +09001167 "delay-rt" => {
1168 cfg.delay_rt = true;
1169 }
Zach Reiznerefe95782017-08-26 18:05:48 -07001170 "mem" => {
1171 if cfg.memory.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -07001172 return Err(argument::Error::TooManyArguments(
1173 "`mem` already given".to_owned(),
1174 ));
Zach Reiznerefe95782017-08-26 18:05:48 -07001175 }
1176 cfg.memory =
Zach Reizner55a9e502018-10-03 10:22:32 -07001177 Some(
1178 value
1179 .unwrap()
1180 .parse()
1181 .map_err(|_| argument::Error::InvalidValue {
1182 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001183 expected: String::from("this value for `mem` needs to be integer"),
Zach Reizner55a9e502018-10-03 10:22:32 -07001184 })?,
1185 )
Zach Reiznerefe95782017-08-26 18:05:48 -07001186 }
Will Deaconc48e7832021-07-30 19:03:06 +01001187 #[cfg(target_arch = "aarch64")]
1188 "swiotlb" => {
1189 if cfg.swiotlb.is_some() {
1190 return Err(argument::Error::TooManyArguments(
1191 "`swiotlb` already given".to_owned(),
1192 ));
1193 }
1194 cfg.swiotlb =
1195 Some(
1196 value
1197 .unwrap()
1198 .parse()
1199 .map_err(|_| argument::Error::InvalidValue {
1200 value: value.unwrap().to_owned(),
1201 expected: String::from("this value for `swiotlb` needs to be integer"),
1202 })?,
1203 )
1204 }
Sergey Senozhatsky1e369c52021-04-13 20:23:51 +09001205 "hugepages" => {
1206 cfg.hugepages = true;
1207 }
Andrew Scull1590e6f2020-03-18 18:00:47 +00001208 #[cfg(feature = "audio")]
Judy Hsiaod5c1e962020-02-04 12:30:01 +08001209 "ac97" => {
1210 let ac97_params = parse_ac97_options(value.unwrap())?;
paulhsia16478242020-11-27 14:04:58 +08001211 // Add kernel parameters related to the intel8x0 driver for ac97 devices once.
1212 if cfg.ac97_parameters.is_empty() {
1213 // Set `inside_vm=1` to save some register read ops in the driver.
1214 cfg.params.push("snd_intel8x0.inside_vm=1".to_string());
1215 // Set `ac97_clock=48000` to save intel8x0_measure_ac97_clock call in the driver.
1216 cfg.params.push("snd_intel8x0.ac97_clock=48000".to_string());
1217 }
Judy Hsiaod5c1e962020-02-04 12:30:01 +08001218 cfg.ac97_parameters.push(ac97_params);
Dylan Reid3082e8e2019-01-07 10:33:48 -08001219 }
Jorge E. Moreirad4562d02021-06-28 16:21:12 -07001220 #[cfg(feature = "audio")]
1221 "sound" => {
1222 let client_path = PathBuf::from(value.unwrap());
1223 cfg.sound = Some(client_path);
1224 }
Trent Begin17ccaad2019-04-17 13:51:25 -06001225 "serial" => {
1226 let serial_params = parse_serial_options(value.unwrap())?;
1227 let num = serial_params.num;
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07001228 let key = (serial_params.hardware, num);
1229 if cfg.serial_parameters.contains_key(&key) {
Trent Begin17ccaad2019-04-17 13:51:25 -06001230 return Err(argument::Error::TooManyArguments(format!(
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07001231 "serial hardware {} num {}",
1232 serial_params.hardware, num,
Trent Begin17ccaad2019-04-17 13:51:25 -06001233 )));
1234 }
1235
1236 if serial_params.console {
Jakub Staronb6515a92019-06-05 15:18:25 -07001237 for params in cfg.serial_parameters.values() {
Trent Begin17ccaad2019-04-17 13:51:25 -06001238 if params.console {
1239 return Err(argument::Error::TooManyArguments(format!(
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07001240 "{} device {} already set as console",
1241 params.hardware, params.num,
1242 )));
1243 }
1244 }
1245 }
1246
1247 if serial_params.earlycon {
1248 // Only SerialHardware::Serial supports earlycon= currently.
1249 match serial_params.hardware {
1250 SerialHardware::Serial => {}
1251 _ => {
1252 return Err(argument::Error::InvalidValue {
Andrew Walbran9cfdbd92021-01-11 17:40:34 +00001253 value: serial_params.hardware.to_string(),
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07001254 expected: String::from("earlycon not supported for hardware"),
1255 });
1256 }
1257 }
1258 for params in cfg.serial_parameters.values() {
1259 if params.earlycon {
1260 return Err(argument::Error::TooManyArguments(format!(
1261 "{} device {} already set as earlycon",
1262 params.hardware, params.num,
Trent Begin17ccaad2019-04-17 13:51:25 -06001263 )));
1264 }
1265 }
1266 }
1267
Jorge E. Moreira1e262302019-08-01 14:40:03 -07001268 if serial_params.stdin {
1269 if let Some(previous_stdin) = cfg.serial_parameters.values().find(|sp| sp.stdin) {
1270 return Err(argument::Error::TooManyArguments(format!(
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07001271 "{} device {} already connected to standard input",
1272 previous_stdin.hardware, previous_stdin.num,
Jorge E. Moreira1e262302019-08-01 14:40:03 -07001273 )));
1274 }
1275 }
1276
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07001277 cfg.serial_parameters.insert(key, serial_params);
Trent Begin17ccaad2019-04-17 13:51:25 -06001278 }
1279 "syslog-tag" => {
1280 if cfg.syslog_tag.is_some() {
1281 return Err(argument::Error::TooManyArguments(
1282 "`syslog-tag` already given".to_owned(),
1283 ));
1284 }
1285 syslog::set_proc_name(value.unwrap());
1286 cfg.syslog_tag = Some(value.unwrap().to_owned());
1287 }
Daniel Verkamp4b62cd92019-11-08 13:09:27 -08001288 "root" | "rwroot" | "disk" | "rwdisk" => {
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001289 let param = value.unwrap();
1290 let mut components = param.split(',');
Daniel Verkamp6a8cd102019-06-26 15:17:46 -07001291 let read_only = !name.starts_with("rw");
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001292 let disk_path =
1293 PathBuf::from(
1294 components
1295 .next()
1296 .ok_or_else(|| argument::Error::InvalidValue {
1297 value: param.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001298 expected: String::from("missing disk path"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001299 })?,
1300 );
Zach Reiznerefe95782017-08-26 18:05:48 -07001301 if !disk_path.exists() {
1302 return Err(argument::Error::InvalidValue {
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001303 value: param.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001304 expected: String::from("this disk path does not exist"),
Zach Reizner55a9e502018-10-03 10:22:32 -07001305 });
Zach Reiznerefe95782017-08-26 18:05:48 -07001306 }
Daniel Verkamp6a8cd102019-06-26 15:17:46 -07001307 if name.ends_with("root") {
Daniel Verkampaac28132018-10-15 14:58:48 -07001308 if cfg.disks.len() >= 26 {
Zach Reizner55a9e502018-10-03 10:22:32 -07001309 return Err(argument::Error::TooManyArguments(
1310 "ran out of letters for to assign to root disk".to_owned(),
1311 ));
Zach Reiznerefe95782017-08-26 18:05:48 -07001312 }
Zach Reizner55a9e502018-10-03 10:22:32 -07001313 cfg.params.push(format!(
Daniel Verkamp6a8cd102019-06-26 15:17:46 -07001314 "root=/dev/vd{} {}",
1315 char::from(b'a' + cfg.disks.len() as u8),
1316 if read_only { "ro" } else { "rw" }
Zach Reizner55a9e502018-10-03 10:22:32 -07001317 ));
Zach Reiznerefe95782017-08-26 18:05:48 -07001318 }
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001319
1320 let mut disk = DiskOption {
Zach Reizner55a9e502018-10-03 10:22:32 -07001321 path: disk_path,
Daniel Verkamp6a8cd102019-06-26 15:17:46 -07001322 read_only,
Junichi Uekawa7bea39f2021-07-16 14:05:06 +09001323 o_direct: false,
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001324 sparse: true,
Daniel Verkamp27672232019-12-06 17:26:55 +11001325 block_size: 512,
Daniel Verkamp4e1f99a2020-06-01 12:47:21 -07001326 id: None,
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001327 };
1328
1329 for opt in components {
1330 let mut o = opt.splitn(2, '=');
1331 let kind = o.next().ok_or_else(|| argument::Error::InvalidValue {
1332 value: opt.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001333 expected: String::from("disk options must not be empty"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001334 })?;
1335 let value = o.next().ok_or_else(|| argument::Error::InvalidValue {
1336 value: opt.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001337 expected: String::from("disk options must be of the form `kind=value`"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001338 })?;
1339
1340 match kind {
1341 "sparse" => {
1342 let sparse = value.parse().map_err(|_| argument::Error::InvalidValue {
1343 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001344 expected: String::from("`sparse` must be a boolean"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001345 })?;
1346 disk.sparse = sparse;
1347 }
Junichi Uekawa7bea39f2021-07-16 14:05:06 +09001348 "o_direct" => {
1349 let o_direct =
1350 value.parse().map_err(|_| argument::Error::InvalidValue {
1351 value: value.to_owned(),
1352 expected: String::from("`o_direct` must be a boolean"),
1353 })?;
1354 disk.o_direct = o_direct;
1355 }
Daniel Verkamp27672232019-12-06 17:26:55 +11001356 "block_size" => {
1357 let block_size =
1358 value.parse().map_err(|_| argument::Error::InvalidValue {
1359 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001360 expected: String::from("`block_size` must be an integer"),
Daniel Verkamp27672232019-12-06 17:26:55 +11001361 })?;
1362 disk.block_size = block_size;
1363 }
Daniel Verkamp4e1f99a2020-06-01 12:47:21 -07001364 "id" => {
1365 if value.len() > DISK_ID_LEN {
1366 return Err(argument::Error::InvalidValue {
1367 value: value.to_owned(),
1368 expected: format!(
1369 "`id` must be {} or fewer characters",
1370 DISK_ID_LEN
1371 ),
1372 });
1373 }
1374 let mut id = [0u8; DISK_ID_LEN];
1375 // Slicing id to value's length will never panic
1376 // because we checked that value will fit into id above.
1377 id[..value.len()].copy_from_slice(value.as_bytes());
1378 disk.id = Some(id);
1379 }
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001380 _ => {
1381 return Err(argument::Error::InvalidValue {
1382 value: kind.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001383 expected: String::from("unrecognized disk option"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001384 });
1385 }
1386 }
1387 }
1388
1389 cfg.disks.push(disk);
Zach Reiznerefe95782017-08-26 18:05:48 -07001390 }
Jakub Starona3411ea2019-04-24 10:55:25 -07001391 "pmem-device" | "rw-pmem-device" => {
1392 let disk_path = PathBuf::from(value.unwrap());
1393 if !disk_path.exists() {
1394 return Err(argument::Error::InvalidValue {
1395 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001396 expected: String::from("this disk path does not exist"),
Jakub Starona3411ea2019-04-24 10:55:25 -07001397 });
1398 }
1399
1400 cfg.pmem_devices.push(DiskOption {
1401 path: disk_path,
1402 read_only: !name.starts_with("rw"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001403 sparse: false,
Junichi Uekawa7bea39f2021-07-16 14:05:06 +09001404 o_direct: false,
Michael Hoyle6b196952020-08-02 20:09:41 -07001405 block_size: base::pagesize() as u32,
Daniel Verkamp4e1f99a2020-06-01 12:47:21 -07001406 id: None,
Jakub Starona3411ea2019-04-24 10:55:25 -07001407 });
1408 }
Kansho Nishida282115b2019-12-18 13:13:14 +09001409 "pstore" => {
1410 if cfg.pstore.is_some() {
1411 return Err(argument::Error::TooManyArguments(
1412 "`pstore` already given".to_owned(),
1413 ));
1414 }
1415
1416 let value = value.unwrap();
1417 let components: Vec<&str> = value.split(',').collect();
1418 if components.len() != 2 {
1419 return Err(argument::Error::InvalidValue {
1420 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001421 expected: String::from(
1422 "pstore must have exactly 2 components: path=<path>,size=<size>",
1423 ),
Kansho Nishida282115b2019-12-18 13:13:14 +09001424 });
1425 }
1426 cfg.pstore = Some(Pstore {
1427 path: {
1428 if components[0].len() <= 5 || !components[0].starts_with("path=") {
1429 return Err(argument::Error::InvalidValue {
1430 value: components[0].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001431 expected: String::from("pstore path must follow with `path=`"),
Kansho Nishida282115b2019-12-18 13:13:14 +09001432 });
1433 };
1434 PathBuf::from(&components[0][5..])
1435 },
1436 size: {
1437 if components[1].len() <= 5 || !components[1].starts_with("size=") {
1438 return Err(argument::Error::InvalidValue {
1439 value: components[1].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001440 expected: String::from("pstore size must follow with `size=`"),
Kansho Nishida282115b2019-12-18 13:13:14 +09001441 });
1442 };
1443 components[1][5..]
1444 .parse()
1445 .map_err(|_| argument::Error::InvalidValue {
1446 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001447 expected: String::from("pstore size must be an integer"),
Kansho Nishida282115b2019-12-18 13:13:14 +09001448 })?
1449 },
1450 });
1451 }
Zach Reiznerefe95782017-08-26 18:05:48 -07001452 "host_ip" => {
Daniel Verkampaac28132018-10-15 14:58:48 -07001453 if cfg.host_ip.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -07001454 return Err(argument::Error::TooManyArguments(
1455 "`host_ip` already given".to_owned(),
1456 ));
Zach Reiznerefe95782017-08-26 18:05:48 -07001457 }
Daniel Verkampaac28132018-10-15 14:58:48 -07001458 cfg.host_ip =
Zach Reizner55a9e502018-10-03 10:22:32 -07001459 Some(
1460 value
1461 .unwrap()
1462 .parse()
1463 .map_err(|_| argument::Error::InvalidValue {
1464 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001465 expected: String::from("`host_ip` needs to be in the form \"x.x.x.x\""),
Zach Reizner55a9e502018-10-03 10:22:32 -07001466 })?,
1467 )
Zach Reiznerefe95782017-08-26 18:05:48 -07001468 }
1469 "netmask" => {
Daniel Verkampaac28132018-10-15 14:58:48 -07001470 if cfg.netmask.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -07001471 return Err(argument::Error::TooManyArguments(
1472 "`netmask` already given".to_owned(),
1473 ));
Zach Reiznerefe95782017-08-26 18:05:48 -07001474 }
Daniel Verkampaac28132018-10-15 14:58:48 -07001475 cfg.netmask =
Zach Reizner55a9e502018-10-03 10:22:32 -07001476 Some(
1477 value
1478 .unwrap()
1479 .parse()
1480 .map_err(|_| argument::Error::InvalidValue {
1481 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001482 expected: String::from("`netmask` needs to be in the form \"x.x.x.x\""),
Zach Reizner55a9e502018-10-03 10:22:32 -07001483 })?,
1484 )
Zach Reiznerefe95782017-08-26 18:05:48 -07001485 }
1486 "mac" => {
Daniel Verkampaac28132018-10-15 14:58:48 -07001487 if cfg.mac_address.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -07001488 return Err(argument::Error::TooManyArguments(
1489 "`mac` already given".to_owned(),
1490 ));
Zach Reiznerefe95782017-08-26 18:05:48 -07001491 }
Daniel Verkampaac28132018-10-15 14:58:48 -07001492 cfg.mac_address =
Zach Reizner55a9e502018-10-03 10:22:32 -07001493 Some(
1494 value
1495 .unwrap()
1496 .parse()
1497 .map_err(|_| argument::Error::InvalidValue {
1498 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001499 expected: String::from(
1500 "`mac` needs to be in the form \"XX:XX:XX:XX:XX:XX\"",
1501 ),
Zach Reizner55a9e502018-10-03 10:22:32 -07001502 })?,
1503 )
Zach Reiznerefe95782017-08-26 18:05:48 -07001504 }
Xiong Zhang773c7072020-03-20 10:39:55 +08001505 "net-vq-pairs" => {
1506 if cfg.net_vq_pairs.is_some() {
1507 return Err(argument::Error::TooManyArguments(
1508 "`net-vq-pairs` already given".to_owned(),
1509 ));
1510 }
1511 cfg.net_vq_pairs =
1512 Some(
1513 value
1514 .unwrap()
1515 .parse()
1516 .map_err(|_| argument::Error::InvalidValue {
1517 value: value.unwrap().to_owned(),
1518 expected: String::from(
1519 "this value for `net-vq-pairs` needs to be integer",
1520 ),
1521 })?,
1522 )
1523 }
1524
Stephen Barber28a5a612017-10-20 17:15:30 -07001525 "wayland-sock" => {
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09001526 let mut components = value.unwrap().split(',');
1527 let path =
1528 PathBuf::from(
1529 components
1530 .next()
1531 .ok_or_else(|| argument::Error::InvalidValue {
1532 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001533 expected: String::from("missing socket path"),
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09001534 })?,
1535 );
1536 let mut name = "";
1537 for c in components {
1538 let mut kv = c.splitn(2, '=');
1539 let (kind, value) = match (kv.next(), kv.next()) {
1540 (Some(kind), Some(value)) => (kind, value),
1541 _ => {
1542 return Err(argument::Error::InvalidValue {
1543 value: c.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001544 expected: String::from("option must be of the form `kind=value`"),
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09001545 })
1546 }
1547 };
1548 match kind {
1549 "name" => name = value,
1550 _ => {
1551 return Err(argument::Error::InvalidValue {
1552 value: kind.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001553 expected: String::from("unrecognized option"),
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09001554 })
1555 }
1556 }
Stephen Barber28a5a612017-10-20 17:15:30 -07001557 }
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09001558 if cfg.wayland_socket_paths.contains_key(name) {
1559 return Err(argument::Error::TooManyArguments(format!(
1560 "wayland socket name already used: '{}'",
1561 name
1562 )));
Stephen Barber28a5a612017-10-20 17:15:30 -07001563 }
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09001564 cfg.wayland_socket_paths.insert(name.to_string(), path);
Stephen Barber28a5a612017-10-20 17:15:30 -07001565 }
David Reveman52ba4e52018-04-22 21:42:09 -04001566 #[cfg(feature = "wl-dmabuf")]
Nicholas Vernefde29972021-07-06 22:02:05 +10001567 "wayland-dmabuf" => {}
Zach Reizner0f2cfb02019-06-19 17:46:03 -07001568 "x-display" => {
1569 if cfg.x_display.is_some() {
1570 return Err(argument::Error::TooManyArguments(
1571 "`x-display` already given".to_owned(),
1572 ));
1573 }
1574 cfg.x_display = Some(value.unwrap().to_owned());
1575 }
Zach Reizner65b98f12019-11-22 17:34:58 -08001576 "display-window-keyboard" => {
1577 cfg.display_window_keyboard = true;
1578 }
1579 "display-window-mouse" => {
1580 cfg.display_window_mouse = true;
1581 }
Zach Reiznerefe95782017-08-26 18:05:48 -07001582 "socket" => {
1583 if cfg.socket_path.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -07001584 return Err(argument::Error::TooManyArguments(
1585 "`socket` already given".to_owned(),
1586 ));
Zach Reiznerefe95782017-08-26 18:05:48 -07001587 }
1588 let mut socket_path = PathBuf::from(value.unwrap());
1589 if socket_path.is_dir() {
1590 socket_path.push(format!("crosvm-{}.sock", getpid()));
1591 }
1592 if socket_path.exists() {
1593 return Err(argument::Error::InvalidValue {
Zach Reizner55a9e502018-10-03 10:22:32 -07001594 value: socket_path.to_string_lossy().into_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001595 expected: String::from("this socket path already exists"),
Zach Reizner55a9e502018-10-03 10:22:32 -07001596 });
Zach Reiznerefe95782017-08-26 18:05:48 -07001597 }
1598 cfg.socket_path = Some(socket_path);
1599 }
Dylan Reidd0c9adc2017-10-02 19:04:50 -07001600 "disable-sandbox" => {
Lepton Wu9105e9f2019-03-14 11:38:31 -07001601 cfg.sandbox = false;
Dylan Reidd0c9adc2017-10-02 19:04:50 -07001602 }
Chirantan Ekbote88f9cba2017-08-28 09:51:18 -07001603 "cid" => {
Daniel Verkampaac28132018-10-15 14:58:48 -07001604 if cfg.cid.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -07001605 return Err(argument::Error::TooManyArguments(
1606 "`cid` alread given".to_owned(),
1607 ));
Chirantan Ekbote88f9cba2017-08-28 09:51:18 -07001608 }
Daniel Verkampaac28132018-10-15 14:58:48 -07001609 cfg.cid = Some(
1610 value
1611 .unwrap()
1612 .parse()
1613 .map_err(|_| argument::Error::InvalidValue {
1614 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001615 expected: String::from("this value for `cid` must be an unsigned integer"),
Daniel Verkampaac28132018-10-15 14:58:48 -07001616 })?,
1617 );
Chirantan Ekbote88f9cba2017-08-28 09:51:18 -07001618 }
Chirantan Ekboteebd56812018-04-16 19:32:04 -07001619 "shared-dir" => {
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001620 // This is formatted as multiple fields, each separated by ":". The first 2 fields are
1621 // fixed (src:tag). The rest may appear in any order:
1622 //
1623 // * type=TYPE - must be one of "p9" or "fs" (default: p9)
1624 // * uidmap=UIDMAP - a uid map in the format "inner outer count[,inner outer count]"
1625 // (default: "0 <current euid> 1")
1626 // * gidmap=GIDMAP - a gid map in the same format as uidmap
1627 // (default: "0 <current egid> 1")
Ryo Hashimoto6d924382021-06-21 20:59:04 +09001628 // * privileged_quota_uids=UIDS - Space-separated list of privileged uid values. When
1629 // performing quota-related operations, these UIDs are treated as if they have
1630 // CAP_FOWNER.
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001631 // * timeout=TIMEOUT - a timeout value in seconds, which indicates how long attributes
1632 // and directory contents should be considered valid (default: 5)
1633 // * cache=CACHE - one of "never", "always", or "auto" (default: auto)
1634 // * writeback=BOOL - indicates whether writeback caching should be enabled (default: false)
Chirantan Ekboteebd56812018-04-16 19:32:04 -07001635 let param = value.unwrap();
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001636 let mut components = param.split(':');
Zach Reizner55a9e502018-10-03 10:22:32 -07001637 let src =
1638 PathBuf::from(
1639 components
1640 .next()
1641 .ok_or_else(|| argument::Error::InvalidValue {
1642 value: param.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001643 expected: String::from("missing source path for `shared-dir`"),
Zach Reizner55a9e502018-10-03 10:22:32 -07001644 })?,
1645 );
1646 let tag = components
1647 .next()
1648 .ok_or_else(|| argument::Error::InvalidValue {
Chirantan Ekboteebd56812018-04-16 19:32:04 -07001649 value: param.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001650 expected: String::from("missing tag for `shared-dir`"),
David Tolnay2bac1e72018-12-12 14:33:42 -08001651 })?
1652 .to_owned();
Chirantan Ekboteebd56812018-04-16 19:32:04 -07001653
1654 if !src.is_dir() {
1655 return Err(argument::Error::InvalidValue {
1656 value: param.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001657 expected: String::from("source path for `shared-dir` must be a directory"),
Chirantan Ekboteebd56812018-04-16 19:32:04 -07001658 });
1659 }
1660
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001661 let mut shared_dir = SharedDir {
1662 src,
1663 tag,
1664 ..Default::default()
1665 };
1666 for opt in components {
1667 let mut o = opt.splitn(2, '=');
1668 let kind = o.next().ok_or_else(|| argument::Error::InvalidValue {
1669 value: opt.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001670 expected: String::from("`shared-dir` options must not be empty"),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001671 })?;
1672 let value = o.next().ok_or_else(|| argument::Error::InvalidValue {
1673 value: opt.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001674 expected: String::from("`shared-dir` options must be of the form `kind=value`"),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001675 })?;
1676
1677 match kind {
1678 "type" => {
1679 shared_dir.kind =
1680 value.parse().map_err(|_| argument::Error::InvalidValue {
1681 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001682 expected: String::from("`type` must be one of `fs` or `9p`"),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001683 })?
1684 }
1685 "uidmap" => shared_dir.uid_map = value.into(),
1686 "gidmap" => shared_dir.gid_map = value.into(),
Ryo Hashimoto6d924382021-06-21 20:59:04 +09001687 #[cfg(feature = "chromeos")]
1688 "privileged_quota_uids" => {
1689 shared_dir.fs_cfg.privileged_quota_uids =
1690 value.split(' ').map(|s| s.parse().unwrap()).collect();
1691 }
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001692 "timeout" => {
1693 let seconds = value.parse().map_err(|_| argument::Error::InvalidValue {
1694 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001695 expected: String::from("`timeout` must be an integer"),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001696 })?;
1697
1698 let dur = Duration::from_secs(seconds);
Andrew Walbran9cfdbd92021-01-11 17:40:34 +00001699 shared_dir.fs_cfg.entry_timeout = dur;
Chirantan Ekbote75ba8752020-10-27 18:33:02 +09001700 shared_dir.fs_cfg.attr_timeout = dur;
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001701 }
1702 "cache" => {
1703 let policy = value.parse().map_err(|_| argument::Error::InvalidValue {
1704 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001705 expected: String::from(
1706 "`cache` must be one of `never`, `always`, or `auto`",
1707 ),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001708 })?;
Chirantan Ekbote75ba8752020-10-27 18:33:02 +09001709 shared_dir.fs_cfg.cache_policy = policy;
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001710 }
1711 "writeback" => {
1712 let writeback =
1713 value.parse().map_err(|_| argument::Error::InvalidValue {
1714 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001715 expected: String::from("`writeback` must be a boolean"),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001716 })?;
Chirantan Ekbote75ba8752020-10-27 18:33:02 +09001717 shared_dir.fs_cfg.writeback = writeback;
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001718 }
Chirantan Ekboted994e512020-06-12 18:46:52 +09001719 "rewrite-security-xattrs" => {
1720 let rewrite_security_xattrs =
1721 value.parse().map_err(|_| argument::Error::InvalidValue {
1722 value: value.to_owned(),
1723 expected: String::from(
1724 "`rewrite-security-xattrs` must be a boolean",
1725 ),
1726 })?;
Chirantan Ekbote75ba8752020-10-27 18:33:02 +09001727 shared_dir.fs_cfg.rewrite_security_xattrs = rewrite_security_xattrs;
Chirantan Ekboted994e512020-06-12 18:46:52 +09001728 }
Chirantan Ekbote09357c82020-09-10 20:08:28 +09001729 "ascii_casefold" => {
1730 let ascii_casefold =
1731 value.parse().map_err(|_| argument::Error::InvalidValue {
1732 value: value.to_owned(),
1733 expected: String::from("`ascii_casefold` must be a boolean"),
1734 })?;
Chirantan Ekbote75ba8752020-10-27 18:33:02 +09001735 shared_dir.fs_cfg.ascii_casefold = ascii_casefold;
1736 shared_dir.p9_cfg.ascii_casefold = ascii_casefold;
Chirantan Ekbote09357c82020-09-10 20:08:28 +09001737 }
Chirantan Ekbote1b2d8dc2021-09-21 17:25:29 +09001738 "dax" => {
1739 let use_dax = value.parse().map_err(|_| argument::Error::InvalidValue {
1740 value: value.to_owned(),
1741 expected: String::from("`dax` must be a boolean"),
1742 })?;
1743 shared_dir.fs_cfg.use_dax = use_dax;
1744 }
Chirantan Ekbotef1cd8d72021-09-21 17:33:58 +09001745 "posix_acl" => {
1746 let posix_acl =
1747 value.parse().map_err(|_| argument::Error::InvalidValue {
1748 value: value.to_owned(),
1749 expected: String::from("`posix_acl` must be a boolean"),
1750 })?;
1751 shared_dir.fs_cfg.posix_acl = posix_acl;
1752 }
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001753 _ => {
1754 return Err(argument::Error::InvalidValue {
1755 value: kind.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001756 expected: String::from("unrecognized option for `shared-dir`"),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001757 })
1758 }
1759 }
1760 }
1761 cfg.shared_dirs.push(shared_dir);
Chirantan Ekboteebd56812018-04-16 19:32:04 -07001762 }
Dylan Reide026ef02017-10-02 19:03:52 -07001763 "seccomp-policy-dir" => {
Dylan Reidd0c9adc2017-10-02 19:04:50 -07001764 // `value` is Some because we are in this match so it's safe to unwrap.
Daniel Verkampaac28132018-10-15 14:58:48 -07001765 cfg.seccomp_policy_dir = PathBuf::from(value.unwrap());
Zach Reizner55a9e502018-10-03 10:22:32 -07001766 }
Zach Reizner44863792019-06-26 14:22:08 -07001767 "seccomp-log-failures" => {
Matt Delco45caf912019-11-13 08:11:09 -08001768 // A side-effect of this flag is to force the use of .policy files
1769 // instead of .bpf files (.bpf files are expected and assumed to be
1770 // compiled to fail an unpermitted action with "trap").
1771 // Normally crosvm will first attempt to use a .bpf file, and if
1772 // not present it will then try to use a .policy file. It's up
1773 // to the build to decide which of these files is present for
1774 // crosvm to use (for CrOS the build will use .bpf files for
1775 // x64 builds and .policy files for arm/arm64 builds).
1776 //
1777 // This flag will likely work as expected for builds that use
1778 // .policy files. For builds that only use .bpf files the initial
1779 // result when using this flag is likely to be a file-not-found
1780 // error (since the .policy files are not present).
1781 // For .bpf builds you can either 1) manually add the .policy files,
1782 // or 2) do not use this command-line parameter and instead
1783 // temporarily change the build by passing "log" rather than
1784 // "trap" as the "--default-action" to compile_seccomp_policy.py.
Zach Reizner44863792019-06-26 14:22:08 -07001785 cfg.seccomp_log_failures = true;
1786 }
Zach Reizner8864cb02018-01-16 17:59:03 -08001787 "plugin" => {
Cody Schuffelen6d1ab502019-05-21 12:12:38 -07001788 if cfg.executable_path.is_some() {
1789 return Err(argument::Error::TooManyArguments(format!(
1790 "A VM executable was already specified: {:?}",
1791 cfg.executable_path
1792 )));
Zach Reizner8864cb02018-01-16 17:59:03 -08001793 }
Zach Reiznercc30d582018-01-23 21:16:42 -08001794 let plugin = PathBuf::from(value.unwrap().to_owned());
1795 if plugin.is_relative() {
1796 return Err(argument::Error::InvalidValue {
Zach Reizner55a9e502018-10-03 10:22:32 -07001797 value: plugin.to_string_lossy().into_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001798 expected: String::from("the plugin path must be an absolute path"),
Zach Reizner55a9e502018-10-03 10:22:32 -07001799 });
Zach Reiznercc30d582018-01-23 21:16:42 -08001800 }
Cody Schuffelen6d1ab502019-05-21 12:12:38 -07001801 cfg.executable_path = Some(Executable::Plugin(plugin));
Zach Reizner55a9e502018-10-03 10:22:32 -07001802 }
Dmitry Torokhov0f1770d2018-05-11 10:57:16 -07001803 "plugin-root" => {
1804 cfg.plugin_root = Some(PathBuf::from(value.unwrap().to_owned()));
Zach Reizner55a9e502018-10-03 10:22:32 -07001805 }
Chirantan Ekboted41d7262018-11-16 16:37:45 -08001806 "plugin-mount" => {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -08001807 let mount = parse_plugin_mount_option(value.unwrap())?;
1808 cfg.plugin_mounts.push(mount);
Chirantan Ekboted41d7262018-11-16 16:37:45 -08001809 }
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08001810 "plugin-mount-file" => {
1811 let file = File::open(value.unwrap()).map_err(|_| argument::Error::InvalidValue {
1812 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001813 expected: String::from("unable to open `plugin-mount-file` file"),
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08001814 })?;
1815 let reader = BufReader::new(file);
1816 for l in reader.lines() {
1817 let line = l.unwrap();
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09001818 let trimmed_line = line.splitn(2, '#').next().unwrap().trim();
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08001819 if !trimmed_line.is_empty() {
1820 let mount = parse_plugin_mount_option(trimmed_line)?;
1821 cfg.plugin_mounts.push(mount);
1822 }
1823 }
1824 }
Dmitry Torokhov1a6262b2019-03-01 00:34:03 -08001825 "plugin-gid-map" => {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -08001826 let map = parse_plugin_gid_map_option(value.unwrap())?;
1827 cfg.plugin_gid_maps.push(map);
Dmitry Torokhov1a6262b2019-03-01 00:34:03 -08001828 }
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08001829 "plugin-gid-map-file" => {
1830 let file = File::open(value.unwrap()).map_err(|_| argument::Error::InvalidValue {
1831 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001832 expected: String::from("unable to open `plugin-gid-map-file` file"),
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08001833 })?;
1834 let reader = BufReader::new(file);
1835 for l in reader.lines() {
1836 let line = l.unwrap();
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09001837 let trimmed_line = line.splitn(2, '#').next().unwrap().trim();
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08001838 if !trimmed_line.is_empty() {
1839 let map = parse_plugin_gid_map_option(trimmed_line)?;
1840 cfg.plugin_gid_maps.push(map);
1841 }
1842 }
1843 }
Daniel Verkampaac28132018-10-15 14:58:48 -07001844 "vhost-net" => cfg.vhost_net = true,
Chirantan Ekbote5f787212018-05-31 15:31:31 -07001845 "tap-fd" => {
Jorge E. Moreirab7952802019-02-12 16:43:05 -08001846 cfg.tap_fd.push(
1847 value
1848 .unwrap()
1849 .parse()
1850 .map_err(|_| argument::Error::InvalidValue {
1851 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001852 expected: String::from(
1853 "this value for `tap-fd` must be an unsigned integer",
1854 ),
Jorge E. Moreirab7952802019-02-12 16:43:05 -08001855 })?,
1856 );
Chirantan Ekbote5f787212018-05-31 15:31:31 -07001857 }
Alexandre Courbot993aa7f2021-12-09 14:51:29 +09001858 "tap-name" => {
1859 cfg.tap_name.push(value.unwrap().to_owned());
1860 }
Jason Macnakcc7070b2019-11-06 14:48:12 -08001861 #[cfg(feature = "gpu")]
Zach Reizner3a8100a2017-09-13 19:15:43 -07001862 "gpu" => {
Chia-I Wu16fb6592021-11-10 11:45:32 -08001863 let gpu_parameters = cfg.gpu_parameters.get_or_insert_with(Default::default);
1864 parse_gpu_options(value, gpu_parameters)?;
Jason Macnakd659a0d2021-03-15 15:33:01 -07001865 }
1866 #[cfg(feature = "gpu")]
1867 "gpu-display" => {
Chia-I Wu16fb6592021-11-10 11:45:32 -08001868 let gpu_parameters = cfg.gpu_parameters.get_or_insert_with(Default::default);
1869 parse_gpu_display_options(value, gpu_parameters)?;
1870 }
1871 #[cfg(all(feature = "gpu", feature = "virgl_renderer_next"))]
1872 "gpu-render-server" => {
1873 let gpu_parameters = cfg.gpu_parameters.get_or_insert_with(Default::default);
1874 parse_gpu_render_server_options(value, gpu_parameters)?;
Zach Reizner3a8100a2017-09-13 19:15:43 -07001875 }
David Tolnay43f8e212019-02-13 17:28:16 -08001876 "software-tpm" => {
1877 cfg.software_tpm = true;
1878 }
Jorge E. Moreira99d3f082019-03-07 10:59:54 -08001879 "single-touch" => {
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09001880 let mut it = value.unwrap().split(':');
Jorge E. Moreira99d3f082019-03-07 10:59:54 -08001881
1882 let mut single_touch_spec =
1883 TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned()));
1884 if let Some(width) = it.next() {
Kaiyi Libccb4eb2020-02-06 17:53:11 -08001885 single_touch_spec.set_width(width.trim().parse().unwrap());
Jorge E. Moreira99d3f082019-03-07 10:59:54 -08001886 }
1887 if let Some(height) = it.next() {
Kaiyi Libccb4eb2020-02-06 17:53:11 -08001888 single_touch_spec.set_height(height.trim().parse().unwrap());
Jorge E. Moreira99d3f082019-03-07 10:59:54 -08001889 }
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07001890 cfg.virtio_single_touch.push(single_touch_spec);
Jorge E. Moreira99d3f082019-03-07 10:59:54 -08001891 }
Tristan Muntsinger486cffc2020-09-29 22:05:41 +00001892 "multi-touch" => {
Tristan Muntsinger486cffc2020-09-29 22:05:41 +00001893 let mut it = value.unwrap().split(':');
1894
1895 let mut multi_touch_spec =
1896 TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned()));
1897 if let Some(width) = it.next() {
1898 multi_touch_spec.set_width(width.trim().parse().unwrap());
1899 }
1900 if let Some(height) = it.next() {
1901 multi_touch_spec.set_height(height.trim().parse().unwrap());
1902 }
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07001903 cfg.virtio_multi_touch.push(multi_touch_spec);
Tristan Muntsinger486cffc2020-09-29 22:05:41 +00001904 }
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001905 "trackpad" => {
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09001906 let mut it = value.unwrap().split(':');
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001907
1908 let mut trackpad_spec =
Jorge E. Moreira99d3f082019-03-07 10:59:54 -08001909 TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned()));
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001910 if let Some(width) = it.next() {
Kaiyi Libccb4eb2020-02-06 17:53:11 -08001911 trackpad_spec.set_width(width.trim().parse().unwrap());
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001912 }
1913 if let Some(height) = it.next() {
Kaiyi Libccb4eb2020-02-06 17:53:11 -08001914 trackpad_spec.set_height(height.trim().parse().unwrap());
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001915 }
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07001916 cfg.virtio_trackpad.push(trackpad_spec);
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001917 }
1918 "mouse" => {
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07001919 cfg.virtio_mice
1920 .push(PathBuf::from(value.unwrap().to_owned()));
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001921 }
1922 "keyboard" => {
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07001923 cfg.virtio_keyboard
1924 .push(PathBuf::from(value.unwrap().to_owned()));
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001925 }
Daniel Norman5e23df72021-03-11 10:11:02 -08001926 "switches" => {
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07001927 cfg.virtio_switches
1928 .push(PathBuf::from(value.unwrap().to_owned()));
Daniel Norman5e23df72021-03-11 10:11:02 -08001929 }
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001930 "evdev" => {
1931 let dev_path = PathBuf::from(value.unwrap());
1932 if !dev_path.exists() {
1933 return Err(argument::Error::InvalidValue {
1934 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001935 expected: String::from("this input device path does not exist"),
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001936 });
1937 }
1938 cfg.virtio_input_evdevs.push(dev_path);
1939 }
Miriam Zimmerman26ac9282019-01-29 21:21:48 -08001940 "split-irqchip" => {
1941 cfg.split_irqchip = true;
1942 }
Daniel Verkampe403f5c2018-12-11 16:29:26 -08001943 "initrd" => {
1944 cfg.initrd_path = Some(PathBuf::from(value.unwrap().to_owned()));
1945 }
Cody Schuffelen6d1ab502019-05-21 12:12:38 -07001946 "bios" => {
1947 if cfg.executable_path.is_some() {
1948 return Err(argument::Error::TooManyArguments(format!(
1949 "A VM executable was already specified: {:?}",
1950 cfg.executable_path
1951 )));
1952 }
1953 cfg.executable_path = Some(Executable::Bios(PathBuf::from(value.unwrap().to_owned())));
1954 }
Tomasz Nowicki344eb142021-09-22 05:51:58 +00001955 "vfio" | "vfio-platform" => {
Tomasz Nowicki71aca792021-06-09 18:53:49 +00001956 let vfio_type = name.parse().unwrap();
1957 let vfio_dev = VfioCommand::new(vfio_type, value.unwrap())?;
1958 cfg.vfio.push(vfio_dev);
Xiong Zhang17b0daf2019-04-23 17:14:50 +08001959 }
Alexandre Courbot8230abf2021-06-26 22:49:26 +09001960 #[cfg(feature = "video-decoder")]
Keiichi Watanabe57df6a02019-12-06 22:24:40 +09001961 "video-decoder" => {
Alexandre Courbotb42b3e52021-07-09 23:38:57 +09001962 cfg.video_dec = Some(parse_video_options(value)?);
Keiichi Watanabe57df6a02019-12-06 22:24:40 +09001963 }
Alexandre Courbot8230abf2021-06-26 22:49:26 +09001964 #[cfg(feature = "video-encoder")]
Keiichi Watanabe57df6a02019-12-06 22:24:40 +09001965 "video-encoder" => {
Alexandre Courbotb42b3e52021-07-09 23:38:57 +09001966 cfg.video_enc = Some(parse_video_options(value)?);
Keiichi Watanabe57df6a02019-12-06 22:24:40 +09001967 }
Tomasz Jeznach42644642020-05-20 23:27:59 -07001968 "acpi-table" => {
1969 let acpi_table = PathBuf::from(value.unwrap());
1970 if !acpi_table.exists() {
1971 return Err(argument::Error::InvalidValue {
1972 value: value.unwrap().to_owned(),
1973 expected: String::from("the acpi-table path does not exist"),
1974 });
1975 }
1976 if !acpi_table.is_file() {
1977 return Err(argument::Error::InvalidValue {
1978 value: value.unwrap().to_owned(),
1979 expected: String::from("the acpi-table path should be a file"),
1980 });
1981 }
1982
1983 cfg.acpi_tables.push(acpi_table);
1984 }
Will Deacon6560c182020-10-06 18:47:18 +01001985 "protected-vm" => {
Andrew Walbran413f8542021-01-08 13:29:03 +00001986 cfg.protected_vm = ProtectionType::Protected;
Will Deacon6560c182020-10-06 18:47:18 +01001987 }
Andrew Walbran0bbbb682021-12-13 13:42:07 +00001988 "protected-vm-without-firmware" => {
1989 cfg.protected_vm = ProtectionType::ProtectedWithoutFirmware;
1990 }
Chuanxiao Dongfd5626c2020-04-27 16:35:33 +08001991 "battery" => {
1992 let params = parse_battery_options(value)?;
1993 cfg.battery_type = Some(params);
1994 }
Keiichi Watanabec5262e92020-10-21 15:57:33 +09001995 #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
1996 "gdb" => {
1997 let port = value
1998 .unwrap()
1999 .parse()
2000 .map_err(|_| argument::Error::InvalidValue {
2001 value: value.unwrap().to_owned(),
2002 expected: String::from("expected a valid port number"),
2003 })?;
2004 cfg.gdb = Some(port);
2005 }
Charles William Dick0e3d4b62020-12-14 12:16:46 +09002006 "balloon_bias_mib" => {
2007 cfg.balloon_bias =
2008 value
2009 .unwrap()
2010 .parse::<i64>()
2011 .map_err(|_| argument::Error::InvalidValue {
2012 value: value.unwrap().to_owned(),
2013 expected: String::from("expected a valid ballon bias in MiB"),
2014 })?
2015 * 1024
2016 * 1024; // cfg.balloon_bias is in bytes.
2017 }
Keiichi Watanabef3a37f42021-01-21 15:41:11 +09002018 "vhost-user-blk" => cfg.vhost_user_blk.push(VhostUserOption {
2019 socket: PathBuf::from(value.unwrap()),
2020 }),
Federico 'Morg' Pareschi70fc7de2021-04-08 15:43:13 +09002021 "vhost-user-console" => cfg.vhost_user_console.push(VhostUserOption {
2022 socket: PathBuf::from(value.unwrap()),
2023 }),
Chirantan Ekbote44292f52021-06-25 18:31:41 +09002024 "vhost-user-gpu" => cfg.vhost_user_gpu.push(VhostUserOption {
2025 socket: PathBuf::from(value.unwrap()),
2026 }),
JaeMan Parkeb9cc532021-07-02 15:02:59 +09002027 "vhost-user-mac80211-hwsim" => {
2028 cfg.vhost_user_mac80211_hwsim = Some(VhostUserOption {
2029 socket: PathBuf::from(value.unwrap()),
2030 });
2031 }
Keiichi Watanabe60686582021-03-12 04:53:51 +09002032 "vhost-user-net" => cfg.vhost_user_net.push(VhostUserOption {
2033 socket: PathBuf::from(value.unwrap()),
2034 }),
Woody Chow1b16db12021-04-02 16:59:59 +09002035 #[cfg(feature = "audio")]
2036 "vhost-user-snd" => cfg.vhost_user_snd.push(VhostUserOption {
2037 socket: PathBuf::from(value.unwrap()),
2038 }),
Chirantan Ekbote84091e52021-09-10 18:43:17 +09002039 "vhost-user-vsock" => cfg.vhost_user_vsock.push(VhostUserOption {
2040 socket: PathBuf::from(value.unwrap()),
2041 }),
Chirantan Ekbote2ee9dcd2021-05-26 18:21:44 +09002042 "vhost-user-wl" => {
2043 let mut components = value.unwrap().splitn(2, ":");
2044 let socket = components.next().map(PathBuf::from).ok_or_else(|| {
2045 argument::Error::InvalidValue {
2046 value: value.unwrap().to_owned(),
2047 expected: String::from("missing socket path"),
2048 }
2049 })?;
2050 let vm_tube = components.next().map(PathBuf::from).ok_or_else(|| {
2051 argument::Error::InvalidValue {
2052 value: value.unwrap().to_owned(),
2053 expected: String::from("missing vm tube path"),
2054 }
2055 })?;
2056 cfg.vhost_user_wl
2057 .push(VhostUserWlOption { socket, vm_tube });
2058 }
Woody Chow5890b702021-02-12 14:57:02 +09002059 "vhost-user-fs" => {
2060 // (socket:tag)
2061 let param = value.unwrap();
2062 let mut components = param.split(':');
2063 let socket =
2064 PathBuf::from(
2065 components
2066 .next()
2067 .ok_or_else(|| argument::Error::InvalidValue {
2068 value: param.to_owned(),
2069 expected: String::from("missing socket path for `vhost-user-fs`"),
2070 })?,
2071 );
2072 let tag = components
2073 .next()
2074 .ok_or_else(|| argument::Error::InvalidValue {
2075 value: param.to_owned(),
2076 expected: String::from("missing tag for `vhost-user-fs`"),
2077 })?
2078 .to_owned();
2079 cfg.vhost_user_fs.push(VhostUserFsOption { socket, tag });
2080 }
Tomasz Jeznach3ce74762021-02-26 01:01:53 -08002081 #[cfg(feature = "direct")]
2082 "direct-pmio" => {
2083 if cfg.direct_pmio.is_some() {
2084 return Err(argument::Error::TooManyArguments(
2085 "`direct_pmio` already given".to_owned(),
2086 ));
2087 }
2088 cfg.direct_pmio = Some(parse_direct_io_options(value)?);
2089 }
Tomasz Jeznach7271f752021-03-04 01:44:06 -08002090 #[cfg(feature = "direct")]
Tomasz Jeznach9e6c6332021-05-27 21:49:14 -07002091 "direct-mmio" => {
2092 if cfg.direct_mmio.is_some() {
2093 return Err(argument::Error::TooManyArguments(
2094 "`direct_mmio` already given".to_owned(),
2095 ));
2096 }
2097 cfg.direct_mmio = Some(parse_direct_io_options(value)?);
2098 }
2099 #[cfg(feature = "direct")]
Tomasz Jeznach7271f752021-03-04 01:44:06 -08002100 "direct-level-irq" => {
2101 cfg.direct_level_irq
2102 .push(
2103 value
2104 .unwrap()
2105 .parse()
2106 .map_err(|_| argument::Error::InvalidValue {
2107 value: value.unwrap().to_owned(),
2108 expected: String::from(
2109 "this value for `direct-level-irq` must be an unsigned integer",
2110 ),
2111 })?,
2112 );
2113 }
2114 #[cfg(feature = "direct")]
2115 "direct-edge-irq" => {
2116 cfg.direct_edge_irq
2117 .push(
2118 value
2119 .unwrap()
2120 .parse()
2121 .map_err(|_| argument::Error::InvalidValue {
2122 value: value.unwrap().to_owned(),
2123 expected: String::from(
2124 "this value for `direct-edge-irq` must be an unsigned integer",
2125 ),
2126 })?,
2127 );
2128 }
Tomasz Jeznachccb26942021-03-30 22:44:11 -07002129 "dmi" => {
2130 if cfg.dmi_path.is_some() {
2131 return Err(argument::Error::TooManyArguments(
2132 "`dmi` already given".to_owned(),
2133 ));
2134 }
2135 let dmi_path = PathBuf::from(value.unwrap());
2136 if !dmi_path.exists() {
2137 return Err(argument::Error::InvalidValue {
2138 value: value.unwrap().to_owned(),
2139 expected: String::from("the dmi path does not exist"),
2140 });
2141 }
2142 if !dmi_path.is_dir() {
2143 return Err(argument::Error::InvalidValue {
2144 value: value.unwrap().to_owned(),
2145 expected: String::from("the dmi path should be directory"),
2146 });
2147 }
2148 cfg.dmi_path = Some(dmi_path);
2149 }
Tomasz Jeznachd93c29f2021-04-12 11:00:24 -07002150 "no-legacy" => {
2151 cfg.no_legacy = true;
2152 }
ZhaoLiu2aaf7ad2021-10-10 18:22:29 +08002153 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
2154 "host-cpu-topology" => {
2155 cfg.host_cpu_topology = true;
2156 }
Mattias Nisslerde2c6402021-10-21 12:05:29 +00002157 "stub-pci-device" => {
2158 cfg.stub_pci_devices.push(parse_stub_pci_parameters(value)?);
2159 }
Zach Reiznerefe95782017-08-26 18:05:48 -07002160 "help" => return Err(argument::Error::PrintHelp),
2161 _ => unreachable!(),
2162 }
2163 Ok(())
2164}
2165
Kaiyi Libccb4eb2020-02-06 17:53:11 -08002166fn validate_arguments(cfg: &mut Config) -> std::result::Result<(), argument::Error> {
2167 if cfg.executable_path.is_none() {
2168 return Err(argument::Error::ExpectedArgument("`KERNEL`".to_owned()));
2169 }
2170 if cfg.host_ip.is_some() || cfg.netmask.is_some() || cfg.mac_address.is_some() {
2171 if cfg.host_ip.is_none() {
2172 return Err(argument::Error::ExpectedArgument(
2173 "`host_ip` missing from network config".to_owned(),
2174 ));
2175 }
2176 if cfg.netmask.is_none() {
2177 return Err(argument::Error::ExpectedArgument(
2178 "`netmask` missing from network config".to_owned(),
2179 ));
2180 }
2181 if cfg.mac_address.is_none() {
2182 return Err(argument::Error::ExpectedArgument(
2183 "`mac` missing from network config".to_owned(),
2184 ));
2185 }
2186 }
2187 if cfg.plugin_root.is_some() && !executable_is_plugin(&cfg.executable_path) {
2188 return Err(argument::Error::ExpectedArgument(
2189 "`plugin-root` requires `plugin`".to_owned(),
2190 ));
2191 }
2192 #[cfg(feature = "gpu")]
2193 {
Jason Macnakd659a0d2021-03-15 15:33:01 -07002194 if let Some(gpu_parameters) = cfg.gpu_parameters.as_mut() {
2195 if gpu_parameters.displays.is_empty() {
2196 gpu_parameters.displays.push(GpuDisplayParameters {
2197 width: DEFAULT_DISPLAY_WIDTH,
2198 height: DEFAULT_DISPLAY_HEIGHT,
2199 });
2200 }
2201
2202 let width = gpu_parameters.displays[0].width;
2203 let height = gpu_parameters.displays[0].height;
2204
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07002205 if let Some(virtio_multi_touch) = cfg.virtio_multi_touch.first_mut() {
Tristan Muntsinger486cffc2020-09-29 22:05:41 +00002206 virtio_multi_touch.set_default_size(width, height);
2207 }
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07002208 if let Some(virtio_single_touch) = cfg.virtio_single_touch.first_mut() {
Kaiyi Libccb4eb2020-02-06 17:53:11 -08002209 virtio_single_touch.set_default_size(width, height);
2210 }
2211 }
2212 }
Keiichi Watanabec5262e92020-10-21 15:57:33 +09002213 #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
Dennis Kempinc3dedf32021-11-12 14:42:28 -08002214 if cfg.gdb.is_some() && cfg.vcpu_count.unwrap_or(1) != 1 {
2215 return Err(argument::Error::ExpectedArgument(
2216 "`gdb` requires the number of vCPU to be 1".to_owned(),
2217 ));
Keiichi Watanabec5262e92020-10-21 15:57:33 +09002218 }
ZhaoLiu2aaf7ad2021-10-10 18:22:29 +08002219 if cfg.host_cpu_topology {
ZhaoLiu1e6e7b22021-12-06 17:23:21 +08002220 if cfg.no_smt {
2221 return Err(argument::Error::ExpectedArgument(
2222 "`host-cpu-topology` cannot be set at the same time as `no_smt`, since \
2223 the smt of the Guest is the same as that of the Host when \
2224 `host-cpu-topology` is set."
2225 .to_owned(),
2226 ));
2227 }
2228
ZhaoLiu2aaf7ad2021-10-10 18:22:29 +08002229 // Safe because we pass a flag for this call and the host supports this system call
2230 let pcpu_count = unsafe { libc::sysconf(libc::_SC_NPROCESSORS_CONF) } as usize;
2231 if cfg.vcpu_count.is_some() {
2232 if pcpu_count != cfg.vcpu_count.unwrap() {
2233 return Err(argument::Error::ExpectedArgument(format!(
2234 "`host-cpu-topology` requires the count of vCPUs({}) to equal the \
2235 count of CPUs({}) on host.",
2236 cfg.vcpu_count.unwrap(),
2237 pcpu_count
2238 )));
2239 }
2240 } else {
2241 cfg.vcpu_count = Some(pcpu_count);
2242 }
2243
2244 match &cfg.vcpu_affinity {
2245 None => {
2246 let mut affinity_map = BTreeMap::new();
2247 for cpu_id in 0..cfg.vcpu_count.unwrap() {
2248 affinity_map.insert(cpu_id, vec![cpu_id]);
2249 }
2250 cfg.vcpu_affinity = Some(VcpuAffinity::PerVcpu(affinity_map));
2251 }
2252 _ => {
2253 return Err(argument::Error::ExpectedArgument(
2254 "`host-cpu-topology` requires not to set `cpu-affinity` at the same time"
2255 .to_owned(),
2256 ));
2257 }
2258 }
2259 }
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07002260 set_default_serial_parameters(&mut cfg.serial_parameters);
Kaiyi Libccb4eb2020-02-06 17:53:11 -08002261 Ok(())
2262}
2263
Dmitry Torokhovf75699f2021-12-03 11:19:13 -08002264enum CommandStatus {
2265 Success,
2266 VmReset,
2267 VmStop,
2268}
2269
2270fn run_vm(args: std::env::Args) -> std::result::Result<CommandStatus, ()> {
Zach Reiznerefe95782017-08-26 18:05:48 -07002271 let arguments =
2272 &[Argument::positional("KERNEL", "bzImage of kernel to run"),
Christian Blichmann33d56772021-03-04 19:03:54 +01002273 Argument::value("kvm-device", "PATH", "Path to the KVM device. (default /dev/kvm)"),
Christian Blichmann50f95912021-11-05 16:59:39 +01002274 Argument::value("vhost-vsock-fd", "FD", "Open FD to the vhost-vsock device, mutually exclusive with vhost-vsock-device."),
Christian Blichmann2f5d4b62021-03-10 18:08:08 +01002275 Argument::value("vhost-vsock-device", "PATH", "Path to the vhost-vsock device. (default /dev/vhost-vsock)"),
2276 Argument::value("vhost-net-device", "PATH", "Path to the vhost-net device. (default /dev/vhost-net)"),
Tristan Muntsinger4133b012018-12-21 16:01:56 -08002277 Argument::value("android-fstab", "PATH", "Path to Android fstab"),
Daniel Verkampe403f5c2018-12-11 16:29:26 -08002278 Argument::short_value('i', "initrd", "PATH", "Initial ramdisk to load."),
Zach Reiznerefe95782017-08-26 18:05:48 -07002279 Argument::short_value('p',
2280 "params",
2281 "PARAMS",
Zach Reiznerbb678712018-01-30 18:13:04 -08002282 "Extra kernel or plugin command line arguments. Can be given more than once."),
Zach Reiznerefe95782017-08-26 18:05:48 -07002283 Argument::short_value('c', "cpus", "N", "Number of VCPUs. (default: 1)"),
Daniel Verkampc677fb42020-09-08 13:47:49 -07002284 Argument::value("cpu-affinity", "CPUSET", "Comma-separated list of CPUs or CPU ranges to run VCPUs on (e.g. 0,1-3,5)
2285 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 -07002286 Argument::value("cpu-cluster", "CPUSET", "Group the given CPUs into a cluster (default: no clusters)"),
2287 Argument::value("cpu-capacity", "CPU=CAP[,CPU=CAP[,...]]", "Set the relative capacity of the given CPU (default: no capacity)"),
Yusuke Sato31e136a2021-08-18 11:51:38 -07002288 Argument::flag("per-vm-core-scheduling", "Enable per-VM core scheduling intead of the default one (per-vCPU core scheduing) by
2289 making all vCPU threads share same cookie for core scheduling.
2290 This option is no-op on devices that have neither MDS nor L1TF vulnerability."),
Woody Chow737ff122021-03-22 17:49:57 +09002291 #[cfg(feature = "audio_cras")]
Woody Chow0b2b6062021-09-03 15:40:02 +09002292 Argument::value("cras-snd",
Chih-Yang Hsia99829b32021-12-08 22:00:48 +08002293 "[capture=true,client=crosvm,socket=unified,num_output_streams=1,num_input_streams=1]",
Woody Chow0b2b6062021-09-03 15:40:02 +09002294 "Comma separated key=value pairs for setting up cras snd devices.
2295 Possible key values:
Chih-Yang Hsiaa2b4fc02021-12-12 12:43:56 +08002296 capture - Enable audio capture. Default to false.
Woody Chow0b2b6062021-09-03 15:40:02 +09002297 client_type - Set specific client type for cras backend.
Chih-Yang Hsia99829b32021-12-08 22:00:48 +08002298 num_output_streams - Set number of output PCM streams
2299 num_input_streams - Set number of input PCM streams"),
Suleiman Souhlal015c3c12020-10-07 14:15:41 +09002300 Argument::flag("no-smt", "Don't use SMT in the guest"),
Kansho Nishidaab205af2020-08-13 18:17:50 +09002301 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 +09002302 Argument::flag("delay-rt", "Don't set VCPUs real-time until make-rt command is run"),
Zach Reiznerefe95782017-08-26 18:05:48 -07002303 Argument::short_value('m',
2304 "mem",
2305 "N",
2306 "Amount of guest memory in MiB. (default: 256)"),
Sergey Senozhatsky1e369c52021-04-13 20:23:51 +09002307 Argument::flag("hugepages", "Advise the kernel to use Huge Pages for guest memory mappings."),
Zach Reiznerefe95782017-08-26 18:05:48 -07002308 Argument::short_value('r',
2309 "root",
Daniel Verkampe73c80f2019-11-08 10:11:16 -08002310 "PATH[,key=value[,key=value[,...]]",
2311 "Path to a root disk image followed by optional comma-separated options.
2312 Like `--disk` but adds appropriate kernel command line option.
2313 See --disk for valid options."),
2314 Argument::value("rwroot", "PATH[,key=value[,key=value[,...]]", "Path to a writable root disk image followed by optional comma-separated options.
2315 See --disk for valid options."),
2316 Argument::short_value('d', "disk", "PATH[,key=value[,key=value[,...]]", "Path to a disk image followed by optional comma-separated options.
2317 Valid keys:
Daniel Verkamp27672232019-12-06 17:26:55 +11002318 sparse=BOOL - Indicates whether the disk should support the discard operation (default: true)
Daniel Verkamp4e1f99a2020-06-01 12:47:21 -07002319 block_size=BYTES - Set the reported block size of the disk (default: 512)
Junichi Uekawa7bea39f2021-07-16 14:05:06 +09002320 id=STRING - Set the block device identifier to an ASCII string, up to 20 characters (default: no ID)
2321 o_direct=BOOL - Use O_DIRECT mode to bypass page cache"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -08002322 Argument::value("rwdisk", "PATH[,key=value[,key=value[,...]]", "Path to a writable disk image followed by optional comma-separated options.
2323 See --disk for valid options."),
Jakub Starona3411ea2019-04-24 10:55:25 -07002324 Argument::value("rw-pmem-device", "PATH", "Path to a writable disk image."),
2325 Argument::value("pmem-device", "PATH", "Path to a disk image."),
Kansho Nishida282115b2019-12-18 13:13:14 +09002326 Argument::value("pstore", "path=PATH,size=SIZE", "Path to pstore buffer backend file follewed by size."),
Zach Reiznerefe95782017-08-26 18:05:48 -07002327 Argument::value("host_ip",
2328 "IP",
2329 "IP address to assign to host tap interface."),
2330 Argument::value("netmask", "NETMASK", "Netmask for VM subnet."),
2331 Argument::value("mac", "MAC", "MAC address for VM."),
Xiong Zhang773c7072020-03-20 10:39:55 +08002332 Argument::value("net-vq-pairs", "N", "virtio net virtual queue paris. (default: 1)"),
Andrew Scull1590e6f2020-03-18 18:00:47 +00002333 #[cfg(feature = "audio")]
Judy Hsiaod5c1e962020-02-04 12:30:01 +08002334 Argument::value("ac97",
paulhsia83d51602021-03-09 17:13:14 +08002335 "[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 +08002336 "Comma separated key=value pairs for setting up Ac97 devices. Can be given more than once .
Junichi Uekawaaab74e42021-07-19 16:12:46 +09002337 Possible key values:
2338 backend=(null, cras, vios) - Where to route the audio device. If not provided, backend will default to null.
2339 `null` for /dev/null, cras for CRAS server and vios for VioS server.
2340 capture - Enable audio capture
2341 capture_effects - | separated effects to be enabled for recording. The only supported effect value now is EchoCancellation or aec.
2342 client_type - Set specific client type for cras backend.
Woody Chowb27dea42021-09-08 15:51:22 +09002343 socket_type - Set specific socket type for cras backend.
Junichi Uekawaaab74e42021-07-19 16:12:46 +09002344 server - The to the VIOS server (unix socket)."),
Jorge E. Moreirad4562d02021-06-28 16:21:12 -07002345 #[cfg(feature = "audio")]
2346 Argument::value("sound", "[PATH]", "Path to the VioS server socket for setting up virtio-snd devices."),
Trent Begin17ccaad2019-04-17 13:51:25 -06002347 Argument::value("serial",
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07002348 "type=TYPE,[hardware=HW,num=NUM,path=PATH,input=PATH,console,earlycon,stdin]",
Jason Macnakcc7070b2019-11-06 14:48:12 -08002349 "Comma separated key=value pairs for setting up serial devices. Can be given more than once.
Junichi Uekawaaab74e42021-07-19 16:12:46 +09002350 Possible key values:
2351 type=(stdout,syslog,sink,file) - Where to route the serial device
2352 hardware=(serial,virtio-console) - Which type of serial hardware to emulate. Defaults to 8250 UART (serial).
2353 num=(1,2,3,4) - Serial Device Number. If not provided, num will default to 1.
2354 path=PATH - The path to the file to write to when type=file
2355 input=PATH - The path to the file to read from when not stdin
2356 console - Use this serial device as the guest console. Can only be given once. Will default to first serial port if not provided.
2357 earlycon - Use this serial device as the early console. Can only be given once.
2358 stdin - Direct standard input to this serial device. Can only be given once. Will default to first serial port if not provided.
2359 "),
Trent Begin17ccaad2019-04-17 13:51:25 -06002360 Argument::value("syslog-tag", "TAG", "When logging to syslog, use the provided tag."),
Zach Reizner0f2cfb02019-06-19 17:46:03 -07002361 Argument::value("x-display", "DISPLAY", "X11 display name to use."),
Zach Reizner65b98f12019-11-22 17:34:58 -08002362 Argument::flag("display-window-keyboard", "Capture keyboard input from the display window."),
2363 Argument::flag("display-window-mouse", "Capture keyboard input from the display window."),
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09002364 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 -04002365 #[cfg(feature = "wl-dmabuf")]
Nicholas Vernefde29972021-07-06 22:02:05 +10002366 Argument::flag("wayland-dmabuf", "DEPRECATED: Enable support for DMABufs in Wayland device."),
Zach Reiznerefe95782017-08-26 18:05:48 -07002367 Argument::short_value('s',
2368 "socket",
2369 "PATH",
2370 "Path to put the control socket. If PATH is a directory, a name will be generated."),
Dylan Reidd0c9adc2017-10-02 19:04:50 -07002371 Argument::flag("disable-sandbox", "Run all devices in one, non-sandboxed process."),
Chirantan Ekboteebd56812018-04-16 19:32:04 -07002372 Argument::value("cid", "CID", "Context ID for virtual sockets."),
Chirantan Ekbotef1cd8d72021-09-21 17:33:58 +09002373 Argument::value("shared-dir", "PATH:TAG[:type=TYPE:writeback=BOOL:timeout=SECONDS:uidmap=UIDMAP:gidmap=GIDMAP:cache=CACHE:dax=BOOL,posix_acl=BOOL]",
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09002374 "Colon-separated options for configuring a directory to be shared with the VM.
Junichi Uekawaaab74e42021-07-19 16:12:46 +09002375 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.
2376 The remaining fields are key=value pairs that may appear in any order. Valid keys are:
2377 type=(p9, fs) - Indicates whether the directory should be shared via virtio-9p or virtio-fs (default: p9).
2378 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).
2379 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).
2380 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.
2381 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.
Junichi Uekawa670f9982021-12-13 17:35:44 +09002382 writeback=BOOL - Enables 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.
2383 dax=BOOL - Enables DAX support. Enabling DAX can improve performance for frequently accessed files by mapping regions of the file directly into the VM's memory. There is a cost of slightly increased latency the first time the file is accessed. Since the mapping is shared directly from the host kernel's file cache, enabling DAX can improve performance even when the guest cache policy is \"Never\". The default value for this option is \"false\".
Chirantan Ekbotef1cd8d72021-09-21 17:33:58 +09002384 posix_acl=BOOL - Indicates whether the shared directory supports POSIX ACLs. This should only be enabled when the underlying file system supports POSIX ACLs. The default value for this option is \"true\".
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09002385"),
Dylan Reide026ef02017-10-02 19:03:52 -07002386 Argument::value("seccomp-policy-dir", "PATH", "Path to seccomp .policy files."),
Zach Reizner44863792019-06-26 14:22:08 -07002387 Argument::flag("seccomp-log-failures", "Instead of seccomp filter failures being fatal, they will be logged instead."),
Zach Reizner8864cb02018-01-16 17:59:03 -08002388 #[cfg(feature = "plugin")]
Zach Reiznercc30d582018-01-23 21:16:42 -08002389 Argument::value("plugin", "PATH", "Absolute path to plugin process to run under crosvm."),
Daniel Verkampbd1a0842019-01-08 15:50:34 -08002390 #[cfg(feature = "plugin")]
Dmitry Torokhov0f1770d2018-05-11 10:57:16 -07002391 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 -08002392 #[cfg(feature = "plugin")]
Chirantan Ekboted41d7262018-11-16 16:37:45 -08002393 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 -08002394 #[cfg(feature = "plugin")]
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08002395 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."),
2396 #[cfg(feature = "plugin")]
Dmitry Torokhov1a6262b2019-03-01 00:34:03 -08002397 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 -08002398 #[cfg(feature = "plugin")]
2399 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 +00002400 Argument::flag("vhost-net", "Use vhost for networking."),
Alexandre Courbot993aa7f2021-12-09 14:51:29 +09002401 Argument::value("tap-name",
2402 "NAME",
2403 "Name of a configured persistent TAP interface to use for networking. A different virtual network card will be added each time this argument is given."),
Chirantan Ekbote5f787212018-05-31 15:31:31 -07002404 Argument::value("tap-fd",
2405 "fd",
Jorge E. Moreirab7952802019-02-12 16:43:05 -08002406 "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 -07002407 #[cfg(feature = "gpu")]
Jason Macnakcc7070b2019-11-06 14:48:12 -08002408 Argument::flag_or_value("gpu",
2409 "[width=INT,height=INT]",
2410 "(EXPERIMENTAL) Comma separated key=value pairs for setting up a virtio-gpu device
Junichi Uekawaaab74e42021-07-19 16:12:46 +09002411 Possible key values:
2412 backend=(2d|virglrenderer|gfxstream) - Which backend to use for virtio-gpu (determining rendering protocol)
2413 width=INT - The width of the virtual display connected to the virtio-gpu.
2414 height=INT - The height of the virtual display connected to the virtio-gpu.
2415 egl[=true|=false] - If the backend should use a EGL context for rendering.
2416 glx[=true|=false] - If the backend should use a GLX context for rendering.
2417 surfaceless[=true|=false] - If the backend should use a surfaceless context for rendering.
2418 angle[=true|=false] - If the gfxstream backend should use ANGLE (OpenGL on Vulkan) as its native OpenGL driver.
2419 syncfd[=true|=false] - If the gfxstream backend should support EGL_ANDROID_native_fence_sync
2420 vulkan[=true|=false] - If the backend should support vulkan"),
Jason Macnakd659a0d2021-03-15 15:33:01 -07002421 #[cfg(feature = "gpu")]
2422 Argument::flag_or_value("gpu-display",
2423 "[width=INT,height=INT]",
2424 "(EXPERIMENTAL) Comma separated key=value pairs for setting up a display on the virtio-gpu device
Junichi Uekawaaab74e42021-07-19 16:12:46 +09002425 Possible key values:
2426 width=INT - The width of the virtual display connected to the virtio-gpu.
2427 height=INT - The height of the virtual display connected to the virtio-gpu."),
Chia-I Wu16fb6592021-11-10 11:45:32 -08002428 #[cfg(all(feature = "gpu", feature = "virgl_renderer_next"))]
2429 Argument::flag_or_value("gpu-render-server",
2430 "[path=PATH]",
2431 "(EXPERIMENTAL) Comma separated key=value pairs for setting up a render server for the virtio-gpu device
2432 Possible key values:
2433 path=PATH - The path to the render server executable."),
David Tolnay43f8e212019-02-13 17:28:16 -08002434 #[cfg(feature = "tpm")]
2435 Argument::flag("software-tpm", "enable a software emulated trusted platform module device"),
Jorge E. Moreiradffec502019-01-14 18:44:49 -08002436 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 -08002437 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 +00002438 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 -08002439 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)."),
2440 Argument::value("mouse", "PATH", "Path to a socket from where to read mouse input events and write status updates to."),
2441 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 -08002442 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 -08002443 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
2444 Argument::flag("split-irqchip", "(EXPERIMENTAL) enable split-irqchip support"),
Cody Schuffelen6d1ab502019-05-21 12:12:38 -07002445 Argument::value("bios", "PATH", "Path to BIOS/firmware ROM"),
Tomasz Nowicki344eb142021-09-22 05:51:58 +00002446 Argument::value("vfio", "PATH[,iommu=on|off]", "Path to sysfs of PCI pass through or mdev device.
Zide Chendfc4b882021-03-10 16:35:37 -08002447iommu=on|off - indicates whether to enable virtio IOMMU for this device"),
Tomasz Nowicki344eb142021-09-22 05:51:58 +00002448 Argument::value("vfio-platform", "PATH", "Path to sysfs of platform pass through"),
Keiichi Watanabe57df6a02019-12-06 22:24:40 +09002449 #[cfg(feature = "video-decoder")]
Alexandre Courbotb42b3e52021-07-09 23:38:57 +09002450 Argument::flag_or_value("video-decoder", "[backend]", "(EXPERIMENTAL) enable virtio-video decoder device
2451 Possible backend values: libvda"),
Keiichi Watanabe57df6a02019-12-06 22:24:40 +09002452 #[cfg(feature = "video-encoder")]
Alexandre Courbotb42b3e52021-07-09 23:38:57 +09002453 Argument::flag_or_value("video-encoder", "[backend]", "(EXPERIMENTAL) enable virtio-video encoder device
2454 Possible backend values: libvda"),
Tomasz Jeznach42644642020-05-20 23:27:59 -07002455 Argument::value("acpi-table", "PATH", "Path to user provided ACPI table"),
Will Deacon6560c182020-10-06 18:47:18 +01002456 Argument::flag("protected-vm", "(EXPERIMENTAL) prevent host access to guest memory"),
Andrew Walbran0bbbb682021-12-13 13:42:07 +00002457 Argument::flag("protected-vm-without-firmware", "(EXPERIMENTAL) prevent host access to guest memory, but don't use protected VM firmware"),
Will Deaconc48e7832021-07-30 19:03:06 +01002458 #[cfg(target_arch = "aarch64")]
Andrew Walbran0bbbb682021-12-13 13:42:07 +00002459 Argument::value("swiotlb", "N", "(EXPERIMENTAL) Size of virtio swiotlb buffer in MiB (default: 64 if `--protected-vm` or `--protected-vm-without-firmware` is present)."),
Chuanxiao Dongfd5626c2020-04-27 16:35:33 +08002460 Argument::flag_or_value("battery",
2461 "[type=TYPE]",
2462 "Comma separated key=value pairs for setting up battery device
Junichi Uekawaaab74e42021-07-19 16:12:46 +09002463 Possible key values:
2464 type=goldfish - type of battery emulation, defaults to goldfish"),
Keiichi Watanabec5262e92020-10-21 15:57:33 +09002465 Argument::value("gdb", "PORT", "(EXPERIMENTAL) gdb on the given port"),
Charles William Dick0e3d4b62020-12-14 12:16:46 +09002466 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 +09002467 Argument::value("vhost-user-blk", "SOCKET_PATH", "Path to a socket for vhost-user block"),
Federico 'Morg' Pareschi70fc7de2021-04-08 15:43:13 +09002468 Argument::value("vhost-user-console", "SOCKET_PATH", "Path to a socket for vhost-user console"),
Chirantan Ekbote44292f52021-06-25 18:31:41 +09002469 Argument::value("vhost-user-gpu", "SOCKET_PATH", "Paths to a vhost-user socket for gpu"),
JaeMan Parkeb9cc532021-07-02 15:02:59 +09002470 Argument::value("vhost-user-mac80211-hwsim", "SOCKET_PATH", "Path to a socket for vhost-user mac80211_hwsim"),
Keiichi Watanabe60686582021-03-12 04:53:51 +09002471 Argument::value("vhost-user-net", "SOCKET_PATH", "Path to a socket for vhost-user net"),
Woody Chow1b16db12021-04-02 16:59:59 +09002472 #[cfg(feature = "audio")]
2473 Argument::value("vhost-user-snd", "SOCKET_PATH", "Path to a socket for vhost-user snd"),
Chirantan Ekbote84091e52021-09-10 18:43:17 +09002474 Argument::value("vhost-user-vsock", "SOCKET_PATH", "Path to a socket for vhost-user vsock"),
Chirantan Ekbote2ee9dcd2021-05-26 18:21:44 +09002475 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 +09002476 Argument::value("vhost-user-fs", "SOCKET_PATH:TAG",
2477 "Path to a socket path for vhost-user fs, and tag for the shared dir"),
Tomasz Jeznach3ce74762021-02-26 01:01:53 -08002478 #[cfg(feature = "direct")]
Junichi Uekawab6a6e942021-12-07 05:49:30 +09002479 Argument::value("direct-pmio", "PATH@RANGE[,RANGE[,...]]", "Path and ranges for direct port mapped I/O access. RANGE may be decimal or hex (starting with 0x)."),
Tomasz Jeznach9e6c6332021-05-27 21:49:14 -07002480 #[cfg(feature = "direct")]
Junichi Uekawab6a6e942021-12-07 05:49:30 +09002481 Argument::value("direct-mmio", "PATH@RANGE[,RANGE[,...]]", "Path and ranges for direct memory mapped I/O access. RANGE may be decimal or hex (starting with 0x)."),
Tomasz Jeznach7271f752021-03-04 01:44:06 -08002482 #[cfg(feature = "direct")]
2483 Argument::value("direct-level-irq", "irq", "Enable interrupt passthrough"),
2484 #[cfg(feature = "direct")]
2485 Argument::value("direct-edge-irq", "irq", "Enable interrupt passthrough"),
Tomasz Jeznachccb26942021-03-30 22:44:11 -07002486 Argument::value("dmi", "DIR", "Directory with smbios_entry_point/DMI files"),
Tomasz Jeznachd93c29f2021-04-12 11:00:24 -07002487 Argument::flag("no-legacy", "Don't use legacy KBD/RTC devices emulation"),
ZhaoLiu2aaf7ad2021-10-10 18:22:29 +08002488 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
2489 Argument::flag("host-cpu-topology", "Use mirror cpu topology of Host for Guest VM"),
Mattias Nisslerde2c6402021-10-21 12:05:29 +00002490 Argument::value("stub-pci-device", "DOMAIN:BUS:DEVICE.FUNCTION[,vendor=NUM][,device=NUM][,class=NUM][,multifunction][,subsystem_vendor=NUM][,subsystem_device=NUM][,revision=NUM]", "Comma-separated key=value pairs for setting up a stub PCI device that just enumerates. The first option in the list must specify a PCI address to claim.
2491 Optional further parameters
2492 vendor=NUM - PCI vendor ID
2493 device=NUM - PCI device ID
2494 class=NUM - PCI class (including class code, subclass, and programming interface)
2495 multifunction - whether to set the multifunction flag
2496 subsystem_vendor=NUM - PCI subsystem vendor ID
2497 subsystem_device=NUM - PCI subsystem device ID
2498 revision=NUM - revision"),
Zach Reiznerefe95782017-08-26 18:05:48 -07002499 Argument::short_flag('h', "help", "Print help message.")];
2500
2501 let mut cfg = Config::default();
Zach Reizner55a9e502018-10-03 10:22:32 -07002502 let match_res = set_arguments(args, &arguments[..], |name, value| {
2503 set_argument(&mut cfg, name, value)
David Tolnay2bac1e72018-12-12 14:33:42 -08002504 })
Kaiyi Libccb4eb2020-02-06 17:53:11 -08002505 .and_then(|_| validate_arguments(&mut cfg));
Zach Reiznerefe95782017-08-26 18:05:48 -07002506
2507 match match_res {
Zach Reizner8864cb02018-01-16 17:59:03 -08002508 #[cfg(feature = "plugin")]
Zach Reizner267f2c82019-07-31 17:07:27 -07002509 Ok(()) if executable_is_plugin(&cfg.executable_path) => {
2510 match crosvm::plugin::run_config(cfg) {
2511 Ok(_) => {
2512 info!("crosvm and plugin have exited normally");
Dmitry Torokhovf75699f2021-12-03 11:19:13 -08002513 Ok(CommandStatus::VmStop)
Zach Reizner267f2c82019-07-31 17:07:27 -07002514 }
2515 Err(e) => {
2516 error!("{}", e);
2517 Err(())
2518 }
Zach Reizner8864cb02018-01-16 17:59:03 -08002519 }
Zach Reizner267f2c82019-07-31 17:07:27 -07002520 }
Michael Hoylee47a5002020-10-15 16:24:13 -07002521 Ok(()) => match platform::run_config(cfg) {
Dmitry Torokhovf75699f2021-12-03 11:19:13 -08002522 Ok(platform::ExitState::Stop) => {
Zach Reizner55a9e502018-10-03 10:22:32 -07002523 info!("crosvm has exited normally");
Dmitry Torokhovf75699f2021-12-03 11:19:13 -08002524 Ok(CommandStatus::VmStop)
2525 }
2526 Ok(platform::ExitState::Reset) => {
2527 info!("crosvm has exited normally due to reset request");
2528 Ok(CommandStatus::VmReset)
Zach Reiznerefe95782017-08-26 18:05:48 -07002529 }
Zach Reizner55a9e502018-10-03 10:22:32 -07002530 Err(e) => {
Alexandre Courbota184e312021-12-08 13:18:27 +09002531 error!("crosvm has exited with error: {:#}", e);
Zach Reizner55a9e502018-10-03 10:22:32 -07002532 Err(())
2533 }
2534 },
Dylan Reidbfba9932018-02-05 15:51:59 -08002535 Err(argument::Error::PrintHelp) => {
2536 print_help("crosvm run", "KERNEL", &arguments[..]);
Dmitry Torokhovf75699f2021-12-03 11:19:13 -08002537 Ok(CommandStatus::Success)
Dylan Reidbfba9932018-02-05 15:51:59 -08002538 }
Zach Reizner8864cb02018-01-16 17:59:03 -08002539 Err(e) => {
Dmitry Torokhov470b1e72020-01-15 12:46:49 -08002540 error!("{}", e);
Dylan Reidbfba9932018-02-05 15:51:59 -08002541 Err(())
Zach Reizner8864cb02018-01-16 17:59:03 -08002542 }
Zach Reiznerefe95782017-08-26 18:05:48 -07002543 }
2544}
2545
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002546fn stop_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
Zach Reizner78986322019-02-21 20:43:21 -08002547 if args.len() == 0 {
2548 print_help("crosvm stop", "VM_SOCKET...", &[]);
2549 println!("Stops the crosvm instance listening on each `VM_SOCKET` given.");
Jianxun Zhang56497d22019-03-04 14:38:24 -08002550 return Err(());
Zach Reizner78986322019-02-21 20:43:21 -08002551 }
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002552 let socket_path = &args.next().unwrap();
2553 let socket_path = Path::new(&socket_path);
2554 vms_request(&VmRequest::Exit, socket_path)
Zach Reizner78986322019-02-21 20:43:21 -08002555}
2556
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002557fn suspend_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
Zach Reizner78986322019-02-21 20:43:21 -08002558 if args.len() == 0 {
2559 print_help("crosvm suspend", "VM_SOCKET...", &[]);
2560 println!("Suspends the crosvm instance listening on each `VM_SOCKET` given.");
Jianxun Zhang56497d22019-03-04 14:38:24 -08002561 return Err(());
Zach Reizner78986322019-02-21 20:43:21 -08002562 }
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002563 let socket_path = &args.next().unwrap();
2564 let socket_path = Path::new(&socket_path);
2565 vms_request(&VmRequest::Suspend, socket_path)
Zach Reizner78986322019-02-21 20:43:21 -08002566}
2567
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002568fn resume_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
Zach Reizner78986322019-02-21 20:43:21 -08002569 if args.len() == 0 {
2570 print_help("crosvm resume", "VM_SOCKET...", &[]);
2571 println!("Resumes the crosvm instance listening on each `VM_SOCKET` given.");
Jianxun Zhang56497d22019-03-04 14:38:24 -08002572 return Err(());
Zach Reizner78986322019-02-21 20:43:21 -08002573 }
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002574 let socket_path = &args.next().unwrap();
2575 let socket_path = Path::new(&socket_path);
2576 vms_request(&VmRequest::Resume, socket_path)
Zach Reizner78986322019-02-21 20:43:21 -08002577}
2578
2579fn balloon_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
2580 if args.len() < 2 {
2581 print_help("crosvm balloon", "SIZE VM_SOCKET...", &[]);
2582 println!("Set the ballon size of the crosvm instance to `SIZE` bytes.");
Jianxun Zhang56497d22019-03-04 14:38:24 -08002583 return Err(());
Zach Reizner78986322019-02-21 20:43:21 -08002584 }
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09002585 let num_bytes = match args.next().unwrap().parse::<u64>() {
Zach Reizner78986322019-02-21 20:43:21 -08002586 Ok(n) => n,
2587 Err(_) => {
2588 error!("Failed to parse number of bytes");
2589 return Err(());
2590 }
2591 };
2592
Jakub Staron1f828d72019-04-11 12:49:29 -07002593 let command = BalloonControlCommand::Adjust { num_bytes };
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002594 let socket_path = &args.next().unwrap();
2595 let socket_path = Path::new(&socket_path);
2596 vms_request(&VmRequest::BalloonCommand(command), socket_path)
Zach Reizner78986322019-02-21 20:43:21 -08002597}
2598
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002599fn balloon_stats(mut args: std::env::Args) -> std::result::Result<(), ()> {
Charles William Dicked22f6b2020-04-08 11:05:24 +09002600 if args.len() != 1 {
2601 print_help("crosvm balloon_stats", "VM_SOCKET", &[]);
2602 println!("Prints virtio balloon statistics for a `VM_SOCKET`.");
2603 return Err(());
2604 }
2605 let command = BalloonControlCommand::Stats {};
2606 let request = &VmRequest::BalloonCommand(command);
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002607 let socket_path = &args.next().unwrap();
2608 let socket_path = Path::new(&socket_path);
2609 let response = handle_request(request, socket_path)?;
Hikaru Nishidaa0e381b2021-05-24 17:13:45 +09002610 match serde_json::to_string_pretty(&response) {
2611 Ok(response_json) => println!("{}", response_json),
2612 Err(e) => {
2613 error!("Failed to serialize into JSON: {}", e);
2614 return Err(());
2615 }
2616 }
Hikaru Nishida6b51c752021-05-21 12:37:43 +09002617 match response {
2618 VmResponse::BalloonStats { .. } => Ok(()),
2619 _ => Err(()),
2620 }
Charles William Dicked22f6b2020-04-08 11:05:24 +09002621}
2622
Keiichi Watanabe2400bd52021-08-04 19:18:02 +09002623fn modify_battery(mut args: std::env::Args) -> std::result::Result<(), ()> {
2624 if args.len() < 4 {
2625 print_help(
2626 "crosvm battery BATTERY_TYPE ",
2627 "[status STATUS | \
2628 present PRESENT | \
2629 health HEALTH | \
2630 capacity CAPACITY | \
2631 aconline ACONLINE ] \
2632 VM_SOCKET...",
2633 &[],
2634 );
2635 return Err(());
2636 }
2637
2638 // This unwrap will not panic because of the above length check.
2639 let battery_type = args.next().unwrap();
2640 let property = args.next().unwrap();
2641 let target = args.next().unwrap();
2642
2643 let socket_path = args.next().unwrap();
2644 let socket_path = Path::new(&socket_path);
2645
Daniel Verkamp166d1dd2021-08-19 17:05:29 -07002646 do_modify_battery(socket_path, &*battery_type, &*property, &*target)
Keiichi Watanabe2400bd52021-08-04 19:18:02 +09002647}
2648
Andrew Walbranfb7c1082021-06-17 18:12:14 +01002649#[cfg(feature = "composite-disk")]
2650fn create_composite(mut args: std::env::Args) -> std::result::Result<(), ()> {
2651 if args.len() < 1 {
2652 print_help("crosvm create_composite", "PATH [LABEL:PARTITION]..", &[]);
2653 println!("Creates a new composite disk image containing the given partition images");
2654 return Err(());
2655 }
2656
2657 let composite_image_path = args.next().unwrap();
Jooyung Han2e14c732021-07-29 13:27:54 +09002658 let zero_filler_path = format!("{}.filler", composite_image_path);
Andrew Walbranfb7c1082021-06-17 18:12:14 +01002659 let header_path = format!("{}.header", composite_image_path);
2660 let footer_path = format!("{}.footer", composite_image_path);
2661
2662 let mut composite_image_file = OpenOptions::new()
2663 .create(true)
2664 .read(true)
2665 .write(true)
2666 .truncate(true)
2667 .open(&composite_image_path)
2668 .map_err(|e| {
2669 error!(
2670 "Failed opening composite disk image file at '{}': {}",
2671 composite_image_path, e
2672 );
2673 })?;
Jooyung Han2e14c732021-07-29 13:27:54 +09002674 create_zero_filler(&zero_filler_path).map_err(|e| {
2675 error!(
2676 "Failed to create zero filler file at '{}': {}",
2677 &zero_filler_path, e
2678 );
2679 })?;
Andrew Walbranfb7c1082021-06-17 18:12:14 +01002680 let mut header_file = OpenOptions::new()
2681 .create(true)
2682 .read(true)
2683 .write(true)
2684 .truncate(true)
2685 .open(&header_path)
2686 .map_err(|e| {
2687 error!(
2688 "Failed opening header image file at '{}': {}",
2689 header_path, e
2690 );
2691 })?;
2692 let mut footer_file = OpenOptions::new()
2693 .create(true)
2694 .read(true)
2695 .write(true)
2696 .truncate(true)
2697 .open(&footer_path)
2698 .map_err(|e| {
2699 error!(
2700 "Failed opening footer image file at '{}': {}",
2701 footer_path, e
2702 );
2703 })?;
2704
2705 let partitions = args
2706 .into_iter()
2707 .map(|partition_arg| {
2708 if let [label, path] = partition_arg.split(":").collect::<Vec<_>>()[..] {
2709 let partition_file = File::open(path)
2710 .map_err(|e| error!("Failed to open partition image: {}", e))?;
Daniel Verkampeb1640e2021-09-07 14:09:31 -07002711 let size = create_disk_file(partition_file, disk::MAX_NESTING_DEPTH)
Andrew Walbranfb7c1082021-06-17 18:12:14 +01002712 .map_err(|e| error!("Failed to create DiskFile instance: {}", e))?
2713 .get_len()
2714 .map_err(|e| error!("Failed to get length of partition image: {}", e))?;
2715 Ok(PartitionInfo {
2716 label: label.to_owned(),
Jooyung Hand7e56ba2021-07-29 13:26:48 +09002717 path: Path::new(path).to_owned(),
Andrew Walbranfb7c1082021-06-17 18:12:14 +01002718 partition_type: ImagePartitionType::LinuxFilesystem,
2719 writable: false,
Jooyung Hand7e56ba2021-07-29 13:26:48 +09002720 size,
Andrew Walbranfb7c1082021-06-17 18:12:14 +01002721 })
2722 } else {
2723 error!(
2724 "Must specify label and path for partition '{}', like LABEL:PATH",
2725 partition_arg
2726 );
2727 Err(())
2728 }
2729 })
2730 .collect::<Result<Vec<_>, _>>()?;
2731
2732 create_composite_disk(
2733 &partitions,
Jooyung Han2e14c732021-07-29 13:27:54 +09002734 &PathBuf::from(zero_filler_path),
Andrew Walbranfb7c1082021-06-17 18:12:14 +01002735 &PathBuf::from(header_path),
2736 &mut header_file,
2737 &PathBuf::from(footer_path),
2738 &mut footer_file,
2739 &mut composite_image_file,
2740 )
2741 .map_err(|e| {
2742 error!(
2743 "Failed to create composite disk image at '{}': {}",
2744 composite_image_path, e
2745 );
2746 })?;
2747
2748 Ok(())
2749}
2750
A. Cody Schuffelen9ca60392019-12-23 18:27:11 -08002751fn create_qcow2(args: std::env::Args) -> std::result::Result<(), ()> {
2752 let arguments = [
2753 Argument::positional("PATH", "where to create the qcow2 image"),
2754 Argument::positional("[SIZE]", "the expanded size of the image"),
2755 Argument::value(
2756 "backing_file",
2757 "path/to/file",
2758 " the file to back the image",
2759 ),
2760 ];
2761 let mut positional_index = 0;
2762 let mut file_path = String::from("");
2763 let mut size: Option<u64> = None;
2764 let mut backing_file: Option<String> = None;
2765 set_arguments(args, &arguments[..], |name, value| {
2766 match (name, positional_index) {
2767 ("", 0) => {
2768 // NAME
2769 positional_index += 1;
2770 file_path = value.unwrap().to_owned();
2771 }
2772 ("", 1) => {
2773 // [SIZE]
2774 positional_index += 1;
2775 size = Some(value.unwrap().parse::<u64>().map_err(|_| {
2776 argument::Error::InvalidValue {
2777 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08002778 expected: String::from("SIZE should be a nonnegative integer"),
A. Cody Schuffelen9ca60392019-12-23 18:27:11 -08002779 }
2780 })?);
2781 }
2782 ("", _) => {
2783 return Err(argument::Error::TooManyArguments(
2784 "Expected at most 2 positional arguments".to_owned(),
2785 ));
2786 }
2787 ("backing_file", _) => {
2788 backing_file = value.map(|x| x.to_owned());
2789 }
2790 _ => unreachable!(),
2791 };
2792 Ok(())
2793 })
2794 .map_err(|e| {
2795 error!("Unable to parse command line arguments: {}", e);
2796 })?;
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09002797 if file_path.is_empty() || !(size.is_some() ^ backing_file.is_some()) {
A. Cody Schuffelen9ca60392019-12-23 18:27:11 -08002798 print_help("crosvm create_qcow2", "PATH [SIZE]", &arguments);
2799 println!(
2800 "Create a new QCOW2 image at `PATH` of either the specified `SIZE` in bytes or
2801with a '--backing_file'."
2802 );
Jianxun Zhang56497d22019-03-04 14:38:24 -08002803 return Err(());
Dylan Reid2dcb6322018-07-13 10:42:48 -07002804 }
Dylan Reid2dcb6322018-07-13 10:42:48 -07002805
2806 let file = OpenOptions::new()
2807 .create(true)
2808 .read(true)
2809 .write(true)
A. Cody Schuffelen9ca60392019-12-23 18:27:11 -08002810 .truncate(true)
Dylan Reid2dcb6322018-07-13 10:42:48 -07002811 .open(&file_path)
2812 .map_err(|e| {
David Tolnayb4bd00f2019-02-12 17:51:26 -08002813 error!("Failed opening qcow file at '{}': {}", file_path, e);
Dylan Reid2dcb6322018-07-13 10:42:48 -07002814 })?;
2815
A. Cody Schuffelen9ca60392019-12-23 18:27:11 -08002816 match (size, backing_file) {
2817 (Some(size), None) => QcowFile::new(file, size).map_err(|e| {
2818 error!("Failed to create qcow file at '{}': {}", file_path, e);
2819 })?,
2820 (None, Some(backing_file)) => {
Daniel Verkampeb1640e2021-09-07 14:09:31 -07002821 QcowFile::new_from_backing(file, &backing_file, disk::MAX_NESTING_DEPTH).map_err(
2822 |e| {
2823 error!("Failed to create qcow file at '{}': {}", file_path, e);
2824 },
2825 )?
A. Cody Schuffelen9ca60392019-12-23 18:27:11 -08002826 }
2827 _ => unreachable!(),
2828 };
Dylan Reid2dcb6322018-07-13 10:42:48 -07002829 Ok(())
2830}
2831
Keiichi Watanabeee4b58e2021-08-17 19:34:01 +09002832fn start_device(mut args: std::env::Args) -> std::result::Result<(), ()> {
2833 let print_usage = || {
2834 print_help(
2835 "crosvm device",
Federico 'Morg' Pareschia1184822021-09-09 10:52:58 +09002836 " (block|console|fs|gpu|net|wl) <device-specific arguments>",
Keiichi Watanabeee4b58e2021-08-17 19:34:01 +09002837 &[],
2838 );
2839 };
2840
Keiichi Watanabeee4b58e2021-08-17 19:34:01 +09002841 if args.len() == 0 {
2842 print_usage();
2843 return Err(());
2844 }
2845
2846 let device = args.next().unwrap();
2847
2848 let program_name = format!("crosvm device {}", device);
2849 let result = match device.as_str() {
2850 "block" => run_block_device(&program_name, args),
Federico 'Morg' Pareschia1184822021-09-09 10:52:58 +09002851 "console" => run_console_device(&program_name, args),
2852 "fs" => run_fs_device(&program_name, args),
Chirantan Ekbote78225292021-06-25 18:30:34 +09002853 #[cfg(feature = "gpu")]
2854 "gpu" => run_gpu_device(&program_name, args),
Keiichi Watanabeee4b58e2021-08-17 19:34:01 +09002855 "net" => run_net_device(&program_name, args),
Chirantan Ekbotef08bddd2021-09-10 18:41:06 +09002856 "vsock" => run_vsock_device(&program_name, args),
Keiichi Watanabeee4b58e2021-08-17 19:34:01 +09002857 "wl" => run_wl_device(&program_name, args),
2858 _ => {
2859 println!("Unknown device name: {}", device);
2860 print_usage();
2861 return Err(());
2862 }
2863 };
2864
2865 result.map_err(|e| {
Alexandre Courbot3abfaa52021-12-10 16:29:34 +09002866 error!("Failed to run {} device: {:#}", device, e);
Keiichi Watanabeee4b58e2021-08-17 19:34:01 +09002867 })
2868}
2869
Daniel Verkamp92f73d72018-12-04 13:17:46 -08002870fn disk_cmd(mut args: std::env::Args) -> std::result::Result<(), ()> {
2871 if args.len() < 2 {
2872 print_help("crosvm disk", "SUBCOMMAND VM_SOCKET...", &[]);
2873 println!("Manage attached virtual disk devices.");
2874 println!("Subcommands:");
2875 println!(" resize DISK_INDEX NEW_SIZE VM_SOCKET");
Jianxun Zhang56497d22019-03-04 14:38:24 -08002876 return Err(());
Daniel Verkamp92f73d72018-12-04 13:17:46 -08002877 }
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09002878 let subcommand: &str = &args.next().unwrap();
Daniel Verkamp92f73d72018-12-04 13:17:46 -08002879
2880 let request = match subcommand {
2881 "resize" => {
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09002882 let disk_index = match args.next().unwrap().parse::<usize>() {
Daniel Verkamp92f73d72018-12-04 13:17:46 -08002883 Ok(n) => n,
2884 Err(_) => {
2885 error!("Failed to parse disk index");
2886 return Err(());
2887 }
2888 };
2889
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09002890 let new_size = match args.next().unwrap().parse::<u64>() {
Daniel Verkamp92f73d72018-12-04 13:17:46 -08002891 Ok(n) => n,
2892 Err(_) => {
2893 error!("Failed to parse disk size");
2894 return Err(());
2895 }
2896 };
2897
Jakub Staronecf81e02019-04-11 11:43:39 -07002898 VmRequest::DiskCommand {
Daniel Verkamp92f73d72018-12-04 13:17:46 -08002899 disk_index,
Jakub Staronecf81e02019-04-11 11:43:39 -07002900 command: DiskControlCommand::Resize { new_size },
Daniel Verkamp92f73d72018-12-04 13:17:46 -08002901 }
2902 }
2903 _ => {
2904 error!("Unknown disk subcommand '{}'", subcommand);
2905 return Err(());
2906 }
2907 };
2908
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002909 let socket_path = &args.next().unwrap();
2910 let socket_path = Path::new(&socket_path);
2911 vms_request(&request, socket_path)
Daniel Verkamp92f73d72018-12-04 13:17:46 -08002912}
2913
Keiichi Watanabe2400bd52021-08-04 19:18:02 +09002914fn make_rt(mut args: std::env::Args) -> std::result::Result<(), ()> {
2915 if args.len() == 0 {
2916 print_help("crosvm make_rt", "VM_SOCKET...", &[]);
2917 println!("Makes the crosvm instance listening on each `VM_SOCKET` given RT.");
2918 return Err(());
2919 }
2920 let socket_path = &args.next().unwrap();
2921 let socket_path = Path::new(&socket_path);
2922 vms_request(&VmRequest::MakeRT, socket_path)
2923}
2924
Jingkui Wang100e6e42019-03-08 20:41:57 -08002925fn parse_bus_id_addr(v: &str) -> ModifyUsbResult<(u8, u8, u16, u16)> {
2926 debug!("parse_bus_id_addr: {}", v);
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09002927 let mut ids = v.split(':');
Jingkui Wang100e6e42019-03-08 20:41:57 -08002928 match (ids.next(), ids.next(), ids.next(), ids.next()) {
2929 (Some(bus_id), Some(addr), Some(vid), Some(pid)) => {
2930 let bus_id = bus_id
2931 .parse::<u8>()
2932 .map_err(|e| ModifyUsbError::ArgParseInt("bus_id", bus_id.to_owned(), e))?;
2933 let addr = addr
2934 .parse::<u8>()
2935 .map_err(|e| ModifyUsbError::ArgParseInt("addr", addr.to_owned(), e))?;
Daniel Verkamp166d1dd2021-08-19 17:05:29 -07002936 let vid = u16::from_str_radix(vid, 16)
Jingkui Wang100e6e42019-03-08 20:41:57 -08002937 .map_err(|e| ModifyUsbError::ArgParseInt("vid", vid.to_owned(), e))?;
Daniel Verkamp166d1dd2021-08-19 17:05:29 -07002938 let pid = u16::from_str_radix(pid, 16)
Jingkui Wang100e6e42019-03-08 20:41:57 -08002939 .map_err(|e| ModifyUsbError::ArgParseInt("pid", pid.to_owned(), e))?;
2940 Ok((bus_id, addr, vid, pid))
2941 }
2942 _ => Err(ModifyUsbError::ArgParse(
2943 "BUS_ID_ADDR_BUS_NUM_DEV_NUM",
2944 v.to_owned(),
2945 )),
2946 }
2947}
2948
Jingkui Wang100e6e42019-03-08 20:41:57 -08002949fn usb_attach(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
2950 let val = args
2951 .next()
2952 .ok_or(ModifyUsbError::ArgMissing("BUS_ID_ADDR_BUS_NUM_DEV_NUM"))?;
2953 let (bus, addr, vid, pid) = parse_bus_id_addr(&val)?;
2954 let dev_path = PathBuf::from(
2955 args.next()
2956 .ok_or(ModifyUsbError::ArgMissing("usb device path"))?,
2957 );
Jingkui Wang100e6e42019-03-08 20:41:57 -08002958
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002959 let socket_path = args
2960 .next()
2961 .ok_or(ModifyUsbError::ArgMissing("control socket path"))?;
2962 let socket_path = Path::new(&socket_path);
2963
Daniel Verkamp166d1dd2021-08-19 17:05:29 -07002964 do_usb_attach(socket_path, bus, addr, vid, pid, &dev_path)
Jingkui Wang100e6e42019-03-08 20:41:57 -08002965}
2966
2967fn usb_detach(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
2968 let port: u8 = args
2969 .next()
2970 .map_or(Err(ModifyUsbError::ArgMissing("PORT")), |p| {
2971 p.parse::<u8>()
2972 .map_err(|e| ModifyUsbError::ArgParseInt("PORT", p.to_owned(), e))
2973 })?;
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002974 let socket_path = args
2975 .next()
2976 .ok_or(ModifyUsbError::ArgMissing("control socket path"))?;
2977 let socket_path = Path::new(&socket_path);
Daniel Verkamp166d1dd2021-08-19 17:05:29 -07002978 do_usb_detach(socket_path, port)
Jingkui Wang100e6e42019-03-08 20:41:57 -08002979}
2980
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002981fn usb_list(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
2982 let socket_path = args
2983 .next()
2984 .ok_or(ModifyUsbError::ArgMissing("control socket path"))?;
2985 let socket_path = Path::new(&socket_path);
Daniel Verkamp166d1dd2021-08-19 17:05:29 -07002986 do_usb_list(socket_path)
Jingkui Wang100e6e42019-03-08 20:41:57 -08002987}
2988
2989fn modify_usb(mut args: std::env::Args) -> std::result::Result<(), ()> {
Zach Reizneraff94ca2019-03-18 20:58:31 -07002990 if args.len() < 2 {
Jingkui Wang100e6e42019-03-08 20:41:57 -08002991 print_help("crosvm usb",
Zach Reizneraff94ca2019-03-18 20:58:31 -07002992 "[attach BUS_ID:ADDR:VENDOR_ID:PRODUCT_ID [USB_DEVICE_PATH|-] | detach PORT | list] VM_SOCKET...", &[]);
Jingkui Wang100e6e42019-03-08 20:41:57 -08002993 return Err(());
2994 }
2995
2996 // This unwrap will not panic because of the above length check.
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002997 let command = &args.next().unwrap();
Jingkui Wang100e6e42019-03-08 20:41:57 -08002998 let result = match command.as_ref() {
2999 "attach" => usb_attach(args),
3000 "detach" => usb_detach(args),
3001 "list" => usb_list(args),
3002 other => Err(ModifyUsbError::UnknownCommand(other.to_owned())),
3003 };
3004 match result {
3005 Ok(response) => {
3006 println!("{}", response);
3007 Ok(())
3008 }
3009 Err(e) => {
3010 println!("error {}", e);
3011 Err(())
3012 }
3013 }
3014}
3015
Keiichi Watanabe2400bd52021-08-04 19:18:02 +09003016#[allow(clippy::unnecessary_wraps)]
3017fn pkg_version() -> std::result::Result<(), ()> {
3018 const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION");
3019 const PKG_VERSION: Option<&'static str> = option_env!("PKG_VERSION");
3020
3021 print!("crosvm {}", VERSION.unwrap_or("UNKNOWN"));
3022 match PKG_VERSION {
3023 Some(v) => println!("-{}", v),
3024 None => println!(),
3025 }
3026 Ok(())
3027}
3028
Zach Reiznerefe95782017-08-26 18:05:48 -07003029fn print_usage() {
Dmitry Torokhovdacf1042021-12-09 15:36:01 -08003030 print_help("crosvm", "[--extended-status] [command]", &[]);
Zach Reiznerefe95782017-08-26 18:05:48 -07003031 println!("Commands:");
Junichi Uekawaf0a77232021-04-08 16:44:44 +09003032 println!(" balloon - Set balloon size of the crosvm instance.");
3033 println!(" balloon_stats - Prints virtio balloon statistics.");
3034 println!(" battery - Modify battery.");
Andrew Walbranfb7c1082021-06-17 18:12:14 +01003035 #[cfg(feature = "composite-disk")]
3036 println!(" create_composite - Create a new composite disk image file.");
Dylan Reid2dcb6322018-07-13 10:42:48 -07003037 println!(" create_qcow2 - Create a new qcow2 disk image file.");
Keiichi Watanabeee4b58e2021-08-17 19:34:01 +09003038 println!(" device - Start a device process.");
Jingkui Wang100e6e42019-03-08 20:41:57 -08003039 println!(" disk - Manage attached virtual disk devices.");
Keiichi Watanabe9568bb42021-08-16 17:59:30 +09003040 println!(
3041 " make_rt - Enables real-time vcpu priority for crosvm instances started with \
3042 `--delay-rt`."
3043 );
Junichi Uekawaf0a77232021-04-08 16:44:44 +09003044 println!(" resume - Resumes the crosvm instance.");
3045 println!(" run - Start a new crosvm instance.");
3046 println!(" stop - Stops crosvm instances via their control sockets.");
3047 println!(" suspend - Suspends the crosvm instance.");
Jingkui Wang100e6e42019-03-08 20:41:57 -08003048 println!(" usb - Manage attached virtual USB devices.");
Yi Sun54305cd2020-01-04 00:19:37 +08003049 println!(" version - Show package version.");
3050}
3051
Dmitry Torokhovf75699f2021-12-03 11:19:13 -08003052fn crosvm_main() -> std::result::Result<CommandStatus, ()> {
Stephen Barber56fbf092017-06-29 16:12:14 -07003053 if let Err(e) = syslog::init() {
David Tolnayb4bd00f2019-02-12 17:51:26 -08003054 println!("failed to initialize syslog: {}", e);
Dylan Reidbfba9932018-02-05 15:51:59 -08003055 return Err(());
Stephen Barber56fbf092017-06-29 16:12:14 -07003056 }
Zach Reizner639d9672017-05-01 17:57:18 -07003057
Zach Reiznerb3fa5c92019-01-28 14:05:23 -08003058 panic_hook::set_panic_hook();
3059
Zach Reiznerefe95782017-08-26 18:05:48 -07003060 let mut args = std::env::args();
3061 if args.next().is_none() {
3062 error!("expected executable name");
Dylan Reidbfba9932018-02-05 15:51:59 -08003063 return Err(());
Zach Reizner639d9672017-05-01 17:57:18 -07003064 }
Zach Reiznerefe95782017-08-26 18:05:48 -07003065
Dmitry Torokhovdacf1042021-12-09 15:36:01 -08003066 let mut cmd_arg = args.next();
3067 let extended_status = match cmd_arg.as_ref().map(|s| s.as_ref()) {
3068 Some("--extended-status") => {
3069 cmd_arg = args.next();
3070 true
3071 }
3072 _ => false,
3073 } || cfg!(feature = "direct"); // TODO(dtor): remove default for crosvm-direct after transition
3074
3075 let command = match cmd_arg {
Dmitry Torokhovf75699f2021-12-03 11:19:13 -08003076 Some(c) => c,
Dylan Reidbfba9932018-02-05 15:51:59 -08003077 None => {
3078 print_usage();
Dmitry Torokhovf75699f2021-12-03 11:19:13 -08003079 return Ok(CommandStatus::Success);
Dylan Reidbfba9932018-02-05 15:51:59 -08003080 }
Dmitry Torokhovf75699f2021-12-03 11:19:13 -08003081 };
3082
3083 // Past this point, usage of exit is in danger of leaking zombie processes.
3084 let ret = if command == "run" {
Dmitry Torokhovdacf1042021-12-09 15:36:01 -08003085 // We handle run_vm separately because it does not simply signal success/error
Dmitry Torokhovf75699f2021-12-03 11:19:13 -08003086 // but also indicates whether the guest requested reset or stop.
3087 run_vm(args)
3088 } else {
3089 match &command[..] {
3090 "balloon" => balloon_vms(args),
3091 "balloon_stats" => balloon_stats(args),
3092 "battery" => modify_battery(args),
3093 #[cfg(feature = "composite-disk")]
3094 "create_composite" => create_composite(args),
3095 "create_qcow2" => create_qcow2(args),
3096 "device" => start_device(args),
3097 "disk" => disk_cmd(args),
3098 "make_rt" => make_rt(args),
3099 "resume" => resume_vms(args),
3100 "stop" => stop_vms(args),
3101 "suspend" => suspend_vms(args),
3102 "usb" => modify_usb(args),
3103 "version" => pkg_version(),
3104 c => {
3105 println!("invalid subcommand: {:?}", c);
3106 print_usage();
3107 Err(())
3108 }
Zach Reiznerefe95782017-08-26 18:05:48 -07003109 }
Dmitry Torokhovf75699f2021-12-03 11:19:13 -08003110 .map(|_| CommandStatus::Success)
Dylan Reidbfba9932018-02-05 15:51:59 -08003111 };
Zach Reiznerefe95782017-08-26 18:05:48 -07003112
3113 // Reap exit status from any child device processes. At this point, all devices should have been
3114 // dropped in the main process and told to shutdown. Try over a period of 100ms, since it may
3115 // take some time for the processes to shut down.
3116 if !wait_all_children() {
3117 // We gave them a chance, and it's too late.
3118 warn!("not all child processes have exited; sending SIGKILL");
3119 if let Err(e) = kill_process_group() {
3120 // We're now at the mercy of the OS to clean up after us.
David Tolnayb4bd00f2019-02-12 17:51:26 -08003121 warn!("unable to kill all child processes: {}", e);
Zach Reiznerefe95782017-08-26 18:05:48 -07003122 }
3123 }
3124
3125 // WARNING: Any code added after this point is not guaranteed to run
3126 // since we may forcibly kill this process (and its children) above.
Dmitry Torokhovdacf1042021-12-09 15:36:01 -08003127 ret.map(|s| {
3128 if extended_status {
3129 s
3130 } else {
3131 CommandStatus::Success
3132 }
3133 })
Dylan Reidbfba9932018-02-05 15:51:59 -08003134}
3135
3136fn main() {
Dmitry Torokhovf75699f2021-12-03 11:19:13 -08003137 let exit_code = match crosvm_main() {
3138 Ok(CommandStatus::Success | CommandStatus::VmStop) => 0,
3139 Ok(CommandStatus::VmReset) => 32,
3140 Err(_) => 1,
3141 };
3142 std::process::exit(exit_code);
Zach Reizner639d9672017-05-01 17:57:18 -07003143}
Daniel Verkamp107edb32019-04-05 09:58:48 -07003144
3145#[cfg(test)]
3146mod tests {
3147 use super::*;
Kaiyi Libccb4eb2020-02-06 17:53:11 -08003148 use crosvm::{DEFAULT_TOUCH_DEVICE_HEIGHT, DEFAULT_TOUCH_DEVICE_WIDTH};
Daniel Verkamp107edb32019-04-05 09:58:48 -07003149
3150 #[test]
3151 fn parse_cpu_set_single() {
3152 assert_eq!(parse_cpu_set("123").expect("parse failed"), vec![123]);
3153 }
3154
3155 #[test]
3156 fn parse_cpu_set_list() {
3157 assert_eq!(
3158 parse_cpu_set("0,1,2,3").expect("parse failed"),
3159 vec![0, 1, 2, 3]
3160 );
3161 }
3162
3163 #[test]
3164 fn parse_cpu_set_range() {
3165 assert_eq!(
3166 parse_cpu_set("0-3").expect("parse failed"),
3167 vec![0, 1, 2, 3]
3168 );
3169 }
3170
3171 #[test]
3172 fn parse_cpu_set_list_of_ranges() {
3173 assert_eq!(
3174 parse_cpu_set("3-4,7-9,18").expect("parse failed"),
3175 vec![3, 4, 7, 8, 9, 18]
3176 );
3177 }
3178
3179 #[test]
3180 fn parse_cpu_set_repeated() {
3181 // For now, allow duplicates - they will be handled gracefully by the vec to cpu_set_t conversion.
3182 assert_eq!(parse_cpu_set("1,1,1").expect("parse failed"), vec![1, 1, 1]);
3183 }
3184
3185 #[test]
3186 fn parse_cpu_set_negative() {
3187 // Negative CPU numbers are not allowed.
3188 parse_cpu_set("-3").expect_err("parse should have failed");
3189 }
3190
3191 #[test]
3192 fn parse_cpu_set_reverse_range() {
3193 // Ranges must be from low to high.
3194 parse_cpu_set("5-2").expect_err("parse should have failed");
3195 }
3196
3197 #[test]
3198 fn parse_cpu_set_open_range() {
3199 parse_cpu_set("3-").expect_err("parse should have failed");
3200 }
3201
3202 #[test]
3203 fn parse_cpu_set_extra_comma() {
3204 parse_cpu_set("0,1,2,").expect_err("parse should have failed");
3205 }
Trent Begin17ccaad2019-04-17 13:51:25 -06003206
Daniel Verkampc677fb42020-09-08 13:47:49 -07003207 #[test]
3208 fn parse_cpu_affinity_global() {
3209 assert_eq!(
3210 parse_cpu_affinity("0,5-7,9").expect("parse failed"),
3211 VcpuAffinity::Global(vec![0, 5, 6, 7, 9]),
3212 );
3213 }
3214
3215 #[test]
3216 fn parse_cpu_affinity_per_vcpu_one_to_one() {
3217 let mut expected_map = BTreeMap::new();
3218 expected_map.insert(0, vec![0]);
3219 expected_map.insert(1, vec![1]);
3220 expected_map.insert(2, vec![2]);
3221 expected_map.insert(3, vec![3]);
3222 assert_eq!(
3223 parse_cpu_affinity("0=0:1=1:2=2:3=3").expect("parse failed"),
3224 VcpuAffinity::PerVcpu(expected_map),
3225 );
3226 }
3227
3228 #[test]
3229 fn parse_cpu_affinity_per_vcpu_sets() {
3230 let mut expected_map = BTreeMap::new();
3231 expected_map.insert(0, vec![0, 1, 2]);
3232 expected_map.insert(1, vec![3, 4, 5]);
3233 expected_map.insert(2, vec![6, 7, 8]);
3234 assert_eq!(
3235 parse_cpu_affinity("0=0,1,2:1=3-5:2=6,7-8").expect("parse failed"),
3236 VcpuAffinity::PerVcpu(expected_map),
3237 );
3238 }
3239
Dennis Kempin50a58f92021-06-23 11:34:31 -07003240 #[cfg(feature = "audio_cras")]
Trent Begin17ccaad2019-04-17 13:51:25 -06003241 #[test]
Judy Hsiaod5c1e962020-02-04 12:30:01 +08003242 fn parse_ac97_vaild() {
3243 parse_ac97_options("backend=cras").expect("parse should have succeded");
3244 }
3245
Andrew Scull1590e6f2020-03-18 18:00:47 +00003246 #[cfg(feature = "audio")]
Judy Hsiaod5c1e962020-02-04 12:30:01 +08003247 #[test]
3248 fn parse_ac97_null_vaild() {
3249 parse_ac97_options("backend=null").expect("parse should have succeded");
3250 }
3251
Dennis Kempin50a58f92021-06-23 11:34:31 -07003252 #[cfg(feature = "audio_cras")]
Judy Hsiaod5c1e962020-02-04 12:30:01 +08003253 #[test]
Judy Hsiaob4b94c72020-09-07 15:56:00 +08003254 fn parse_ac97_capture_vaild() {
3255 parse_ac97_options("backend=cras,capture=true").expect("parse should have succeded");
Judy Hsiaod5c1e962020-02-04 12:30:01 +08003256 }
3257
Dennis Kempin50a58f92021-06-23 11:34:31 -07003258 #[cfg(feature = "audio_cras")]
Jorge E. Moreira359e7de2020-12-02 18:25:53 -08003259 #[test]
paulhsia83d51602021-03-09 17:13:14 +08003260 fn parse_ac97_client_type() {
3261 parse_ac97_options("backend=cras,capture=true,client_type=crosvm")
3262 .expect("parse should have succeded");
3263 parse_ac97_options("backend=cras,capture=true,client_type=arcvm")
3264 .expect("parse should have succeded");
3265 parse_ac97_options("backend=cras,capture=true,client_type=none")
3266 .expect_err("parse should have failed");
3267 }
3268
Woody Chowb27dea42021-09-08 15:51:22 +09003269 #[cfg(feature = "audio_cras")]
3270 #[test]
3271 fn parse_ac97_socket_type() {
3272 parse_ac97_options("socket_type=unified").expect("parse should have succeded");
3273 parse_ac97_options("socket_type=legacy").expect("parse should have succeded");
3274 }
3275
paulhsia83d51602021-03-09 17:13:14 +08003276 #[cfg(feature = "audio")]
3277 #[test]
Jorge E. Moreira359e7de2020-12-02 18:25:53 -08003278 fn parse_ac97_vios_valid() {
3279 parse_ac97_options("backend=vios,server=/path/to/server")
3280 .expect("parse should have succeded");
3281 }
3282
Judy Hsiaod5c1e962020-02-04 12:30:01 +08003283 #[test]
Trent Begin17ccaad2019-04-17 13:51:25 -06003284 fn parse_serial_vaild() {
Jorge E. Moreira1e262302019-08-01 14:40:03 -07003285 parse_serial_options("type=syslog,num=1,console=true,stdin=true")
3286 .expect("parse should have succeded");
Trent Begin17ccaad2019-04-17 13:51:25 -06003287 }
3288
3289 #[test]
A. Cody Schuffelen3faab5a2020-10-05 17:29:16 -07003290 fn parse_serial_virtio_console_vaild() {
3291 parse_serial_options("type=syslog,num=5,console=true,stdin=true,hardware=virtio-console")
3292 .expect("parse should have succeded");
3293 }
3294
3295 #[test]
Trent Begin923bab02019-06-17 13:48:06 -06003296 fn parse_serial_valid_no_num() {
3297 parse_serial_options("type=syslog").expect("parse should have succeded");
3298 }
3299
3300 #[test]
Nicholas Hollingumc76c2da2020-09-18 15:53:16 +10003301 fn parse_serial_equals_in_value() {
3302 let parsed = parse_serial_options("type=syslog,path=foo=bar==.log")
3303 .expect("parse should have succeded");
3304 assert_eq!(parsed.path, Some(PathBuf::from("foo=bar==.log")));
3305 }
3306
3307 #[test]
Trent Begin17ccaad2019-04-17 13:51:25 -06003308 fn parse_serial_invalid_type() {
3309 parse_serial_options("type=wormhole,num=1").expect_err("parse should have failed");
3310 }
3311
3312 #[test]
3313 fn parse_serial_invalid_num_upper() {
3314 parse_serial_options("type=syslog,num=5").expect_err("parse should have failed");
3315 }
3316
3317 #[test]
3318 fn parse_serial_invalid_num_lower() {
3319 parse_serial_options("type=syslog,num=0").expect_err("parse should have failed");
3320 }
3321
3322 #[test]
A. Cody Schuffelen3faab5a2020-10-05 17:29:16 -07003323 fn parse_serial_virtio_console_invalid_num_lower() {
3324 parse_serial_options("type=syslog,hardware=virtio-console,num=0")
3325 .expect_err("parse should have failed");
3326 }
3327
3328 #[test]
Trent Begin17ccaad2019-04-17 13:51:25 -06003329 fn parse_serial_invalid_num_string() {
3330 parse_serial_options("type=syslog,num=number3").expect_err("parse should have failed");
3331 }
3332
3333 #[test]
3334 fn parse_serial_invalid_option() {
3335 parse_serial_options("type=syslog,speed=lightspeed").expect_err("parse should have failed");
3336 }
Jorge E. Moreira1e262302019-08-01 14:40:03 -07003337
3338 #[test]
3339 fn parse_serial_invalid_two_stdin() {
3340 let mut config = Config::default();
3341 set_argument(&mut config, "serial", Some("num=1,type=stdout,stdin=true"))
3342 .expect("should parse the first serial argument");
3343 set_argument(&mut config, "serial", Some("num=2,type=stdout,stdin=true"))
3344 .expect_err("should fail to parse a second serial port connected to stdin");
3345 }
Dmitry Torokhov458bb642019-12-13 11:47:52 -08003346
3347 #[test]
3348 fn parse_plugin_mount_valid() {
3349 let mut config = Config::default();
3350 set_argument(
3351 &mut config,
3352 "plugin-mount",
3353 Some("/dev/null:/dev/zero:true"),
3354 )
3355 .expect("parse should succeed");
3356 assert_eq!(config.plugin_mounts[0].src, PathBuf::from("/dev/null"));
3357 assert_eq!(config.plugin_mounts[0].dst, PathBuf::from("/dev/zero"));
Daniel Verkamp5e9959e2021-08-19 17:09:59 -07003358 assert!(config.plugin_mounts[0].writable);
Dmitry Torokhov458bb642019-12-13 11:47:52 -08003359 }
3360
3361 #[test]
3362 fn parse_plugin_mount_valid_shorthand() {
3363 let mut config = Config::default();
3364 set_argument(&mut config, "plugin-mount", Some("/dev/null")).expect("parse should succeed");
3365 assert_eq!(config.plugin_mounts[0].dst, PathBuf::from("/dev/null"));
Daniel Verkamp5e9959e2021-08-19 17:09:59 -07003366 assert!(!config.plugin_mounts[0].writable);
Dmitry Torokhov458bb642019-12-13 11:47:52 -08003367 set_argument(&mut config, "plugin-mount", Some("/dev/null:/dev/zero"))
3368 .expect("parse should succeed");
3369 assert_eq!(config.plugin_mounts[1].dst, PathBuf::from("/dev/zero"));
Daniel Verkamp5e9959e2021-08-19 17:09:59 -07003370 assert!(!config.plugin_mounts[1].writable);
Dmitry Torokhov458bb642019-12-13 11:47:52 -08003371 set_argument(&mut config, "plugin-mount", Some("/dev/null::true"))
3372 .expect("parse should succeed");
3373 assert_eq!(config.plugin_mounts[2].dst, PathBuf::from("/dev/null"));
Daniel Verkamp5e9959e2021-08-19 17:09:59 -07003374 assert!(config.plugin_mounts[2].writable);
Dmitry Torokhov458bb642019-12-13 11:47:52 -08003375 }
3376
3377 #[test]
3378 fn parse_plugin_mount_invalid() {
3379 let mut config = Config::default();
3380 set_argument(&mut config, "plugin-mount", Some("")).expect_err("parse should fail");
3381 set_argument(
3382 &mut config,
3383 "plugin-mount",
3384 Some("/dev/null:/dev/null:true:false"),
3385 )
3386 .expect_err("parse should fail because too many arguments");
3387 set_argument(&mut config, "plugin-mount", Some("null:/dev/null:true"))
3388 .expect_err("parse should fail because source is not absolute");
3389 set_argument(&mut config, "plugin-mount", Some("/dev/null:null:true"))
3390 .expect_err("parse should fail because source is not absolute");
3391 set_argument(&mut config, "plugin-mount", Some("/dev/null:null:blah"))
3392 .expect_err("parse should fail because flag is not boolean");
3393 }
3394
3395 #[test]
3396 fn parse_plugin_gid_map_valid() {
3397 let mut config = Config::default();
3398 set_argument(&mut config, "plugin-gid-map", Some("1:2:3")).expect("parse should succeed");
3399 assert_eq!(config.plugin_gid_maps[0].inner, 1);
3400 assert_eq!(config.plugin_gid_maps[0].outer, 2);
3401 assert_eq!(config.plugin_gid_maps[0].count, 3);
3402 }
3403
3404 #[test]
3405 fn parse_plugin_gid_map_valid_shorthand() {
3406 let mut config = Config::default();
3407 set_argument(&mut config, "plugin-gid-map", Some("1")).expect("parse should succeed");
3408 assert_eq!(config.plugin_gid_maps[0].inner, 1);
3409 assert_eq!(config.plugin_gid_maps[0].outer, 1);
3410 assert_eq!(config.plugin_gid_maps[0].count, 1);
3411 set_argument(&mut config, "plugin-gid-map", Some("1:2")).expect("parse should succeed");
3412 assert_eq!(config.plugin_gid_maps[1].inner, 1);
3413 assert_eq!(config.plugin_gid_maps[1].outer, 2);
3414 assert_eq!(config.plugin_gid_maps[1].count, 1);
3415 set_argument(&mut config, "plugin-gid-map", Some("1::3")).expect("parse should succeed");
3416 assert_eq!(config.plugin_gid_maps[2].inner, 1);
3417 assert_eq!(config.plugin_gid_maps[2].outer, 1);
3418 assert_eq!(config.plugin_gid_maps[2].count, 3);
3419 }
3420
3421 #[test]
3422 fn parse_plugin_gid_map_invalid() {
3423 let mut config = Config::default();
3424 set_argument(&mut config, "plugin-gid-map", Some("")).expect_err("parse should fail");
3425 set_argument(&mut config, "plugin-gid-map", Some("1:2:3:4"))
3426 .expect_err("parse should fail because too many arguments");
3427 set_argument(&mut config, "plugin-gid-map", Some("blah:2:3"))
3428 .expect_err("parse should fail because inner is not a number");
3429 set_argument(&mut config, "plugin-gid-map", Some("1:blah:3"))
3430 .expect_err("parse should fail because outer is not a number");
3431 set_argument(&mut config, "plugin-gid-map", Some("1:2:blah"))
3432 .expect_err("parse should fail because count is not a number");
3433 }
Kaiyi Libccb4eb2020-02-06 17:53:11 -08003434
3435 #[test]
3436 fn single_touch_spec_and_track_pad_spec_default_size() {
3437 let mut config = Config::default();
3438 config
3439 .executable_path
3440 .replace(Executable::Kernel(PathBuf::from("kernel")));
3441 set_argument(&mut config, "single-touch", Some("/dev/single-touch-test")).unwrap();
3442 set_argument(&mut config, "trackpad", Some("/dev/single-touch-test")).unwrap();
3443 validate_arguments(&mut config).unwrap();
3444 assert_eq!(
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07003445 config.virtio_single_touch.first().unwrap().get_size(),
Kaiyi Libccb4eb2020-02-06 17:53:11 -08003446 (DEFAULT_TOUCH_DEVICE_WIDTH, DEFAULT_TOUCH_DEVICE_HEIGHT)
3447 );
3448 assert_eq!(
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07003449 config.virtio_trackpad.first().unwrap().get_size(),
Kaiyi Libccb4eb2020-02-06 17:53:11 -08003450 (DEFAULT_TOUCH_DEVICE_WIDTH, DEFAULT_TOUCH_DEVICE_HEIGHT)
3451 );
3452 }
3453
3454 #[cfg(feature = "gpu")]
3455 #[test]
3456 fn single_touch_spec_default_size_from_gpu() {
3457 let width = 12345u32;
3458 let height = 54321u32;
3459 let mut config = Config::default();
3460 config
3461 .executable_path
3462 .replace(Executable::Kernel(PathBuf::from("kernel")));
3463 set_argument(&mut config, "single-touch", Some("/dev/single-touch-test")).unwrap();
3464 set_argument(
3465 &mut config,
3466 "gpu",
3467 Some(&format!("width={},height={}", width, height)),
3468 )
3469 .unwrap();
3470 validate_arguments(&mut config).unwrap();
3471 assert_eq!(
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07003472 config.virtio_single_touch.first().unwrap().get_size(),
Kaiyi Libccb4eb2020-02-06 17:53:11 -08003473 (width, height)
3474 );
3475 }
3476
3477 #[test]
3478 fn single_touch_spec_and_track_pad_spec_with_size() {
3479 let width = 12345u32;
3480 let height = 54321u32;
3481 let mut config = Config::default();
3482 config
3483 .executable_path
3484 .replace(Executable::Kernel(PathBuf::from("kernel")));
3485 set_argument(
3486 &mut config,
3487 "single-touch",
3488 Some(&format!("/dev/single-touch-test:{}:{}", width, height)),
3489 )
3490 .unwrap();
3491 set_argument(
3492 &mut config,
3493 "trackpad",
3494 Some(&format!("/dev/single-touch-test:{}:{}", width, height)),
3495 )
3496 .unwrap();
3497 validate_arguments(&mut config).unwrap();
3498 assert_eq!(
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07003499 config.virtio_single_touch.first().unwrap().get_size(),
Kaiyi Libccb4eb2020-02-06 17:53:11 -08003500 (width, height)
3501 );
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07003502 assert_eq!(
3503 config.virtio_trackpad.first().unwrap().get_size(),
3504 (width, height)
3505 );
Kaiyi Libccb4eb2020-02-06 17:53:11 -08003506 }
3507
3508 #[cfg(feature = "gpu")]
3509 #[test]
3510 fn single_touch_spec_with_size_independent_from_gpu() {
3511 let touch_width = 12345u32;
3512 let touch_height = 54321u32;
3513 let display_width = 1234u32;
3514 let display_height = 5432u32;
3515 let mut config = Config::default();
3516 config
3517 .executable_path
3518 .replace(Executable::Kernel(PathBuf::from("kernel")));
3519 set_argument(
3520 &mut config,
3521 "single-touch",
3522 Some(&format!(
3523 "/dev/single-touch-test:{}:{}",
3524 touch_width, touch_height
3525 )),
3526 )
3527 .unwrap();
3528 set_argument(
3529 &mut config,
3530 "gpu",
3531 Some(&format!(
3532 "width={},height={}",
3533 display_width, display_height
3534 )),
3535 )
3536 .unwrap();
3537 validate_arguments(&mut config).unwrap();
3538 assert_eq!(
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07003539 config.virtio_single_touch.first().unwrap().get_size(),
Kaiyi Libccb4eb2020-02-06 17:53:11 -08003540 (touch_width, touch_height)
3541 );
3542 }
Kaiyi Lidd348a42020-07-13 11:49:46 -07003543
Daniel Norman5e23df72021-03-11 10:11:02 -08003544 #[test]
3545 fn virtio_switches() {
3546 let mut config = Config::default();
3547 config
3548 .executable_path
3549 .replace(Executable::Kernel(PathBuf::from("kernel")));
3550 set_argument(&mut config, "switches", Some("/dev/switches-test")).unwrap();
3551 validate_arguments(&mut config).unwrap();
3552 assert_eq!(
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07003553 config.virtio_switches.pop().unwrap(),
Keiichi Watanabed56a2f42021-03-18 20:16:23 +09003554 PathBuf::from("/dev/switches-test")
3555 );
Daniel Norman5e23df72021-03-11 10:11:02 -08003556 }
3557
Chia-I Wu6d473b32021-04-12 10:14:24 -07003558 #[cfg(feature = "gpu")]
Kaiyi Lidd348a42020-07-13 11:49:46 -07003559 #[test]
Chia-I Wu91df6562021-04-12 09:47:38 -07003560 fn parse_gpu_options_default_vulkan_support() {
Jason Macnakd659a0d2021-03-15 15:33:01 -07003561 {
3562 let mut gpu_params: GpuParameters = Default::default();
3563 assert!(parse_gpu_options(Some("backend=virglrenderer"), &mut gpu_params).is_ok());
3564 assert!(!gpu_params.use_vulkan);
3565 }
Chia-I Wu6d473b32021-04-12 10:14:24 -07003566
3567 #[cfg(feature = "gfxstream")]
Jason Macnakd659a0d2021-03-15 15:33:01 -07003568 {
3569 let mut gpu_params: GpuParameters = Default::default();
3570 assert!(parse_gpu_options(Some("backend=gfxstream"), &mut gpu_params).is_ok());
3571 assert!(gpu_params.use_vulkan);
3572 }
Chia-I Wu91df6562021-04-12 09:47:38 -07003573 }
3574
Chia-I Wu6d473b32021-04-12 10:14:24 -07003575 #[cfg(feature = "gpu")]
Chia-I Wu91df6562021-04-12 09:47:38 -07003576 #[test]
Chia-I Wu6d473b32021-04-12 10:14:24 -07003577 fn parse_gpu_options_with_vulkan_specified() {
Jason Macnakd659a0d2021-03-15 15:33:01 -07003578 {
3579 let mut gpu_params: GpuParameters = Default::default();
3580 assert!(parse_gpu_options(Some("vulkan=true"), &mut gpu_params).is_ok());
3581 assert!(gpu_params.use_vulkan);
3582 }
3583 {
3584 let mut gpu_params: GpuParameters = Default::default();
3585 assert!(
3586 parse_gpu_options(Some("backend=virglrenderer,vulkan=true"), &mut gpu_params)
3587 .is_ok()
3588 );
3589 assert!(gpu_params.use_vulkan);
3590 }
3591 {
3592 let mut gpu_params: GpuParameters = Default::default();
3593 assert!(
3594 parse_gpu_options(Some("vulkan=true,backend=virglrenderer"), &mut gpu_params)
3595 .is_ok()
3596 );
3597 assert!(gpu_params.use_vulkan);
3598 }
3599 {
3600 let mut gpu_params: GpuParameters = Default::default();
3601 assert!(parse_gpu_options(Some("vulkan=false"), &mut gpu_params).is_ok());
3602 assert!(!gpu_params.use_vulkan);
3603 }
3604 {
3605 let mut gpu_params: GpuParameters = Default::default();
3606 assert!(
3607 parse_gpu_options(Some("backend=virglrenderer,vulkan=false"), &mut gpu_params)
3608 .is_ok()
3609 );
3610 assert!(!gpu_params.use_vulkan);
3611 }
3612 {
3613 let mut gpu_params: GpuParameters = Default::default();
3614 assert!(
3615 parse_gpu_options(Some("vulkan=false,backend=virglrenderer"), &mut gpu_params)
3616 .is_ok()
3617 );
3618 assert!(!gpu_params.use_vulkan);
3619 }
3620 {
3621 let mut gpu_params: GpuParameters = Default::default();
3622 assert!(parse_gpu_options(
3623 Some("backend=virglrenderer,vulkan=invalid_value"),
3624 &mut gpu_params
3625 )
3626 .is_err());
3627 }
3628 {
3629 let mut gpu_params: GpuParameters = Default::default();
3630 assert!(parse_gpu_options(
3631 Some("vulkan=invalid_value,backend=virglrenderer"),
3632 &mut gpu_params
3633 )
3634 .is_err());
3635 }
Kaiyi Lidd348a42020-07-13 11:49:46 -07003636 }
3637
3638 #[cfg(all(feature = "gpu", feature = "gfxstream"))]
3639 #[test]
3640 fn parse_gpu_options_gfxstream_with_syncfd_specified() {
Jason Macnakd659a0d2021-03-15 15:33:01 -07003641 {
3642 let mut gpu_params: GpuParameters = Default::default();
3643 assert!(
3644 parse_gpu_options(Some("backend=gfxstream,syncfd=true"), &mut gpu_params).is_ok()
3645 );
3646 assert!(gpu_params.gfxstream_use_syncfd);
3647 }
3648 {
3649 let mut gpu_params: GpuParameters = Default::default();
3650 assert!(
3651 parse_gpu_options(Some("syncfd=true,backend=gfxstream"), &mut gpu_params).is_ok()
3652 );
3653 assert!(gpu_params.gfxstream_use_syncfd);
3654 }
3655 {
3656 let mut gpu_params: GpuParameters = Default::default();
3657 assert!(
3658 parse_gpu_options(Some("backend=gfxstream,syncfd=false"), &mut gpu_params).is_ok()
3659 );
3660 assert!(!gpu_params.gfxstream_use_syncfd);
3661 }
3662 {
3663 let mut gpu_params: GpuParameters = Default::default();
3664 assert!(
3665 parse_gpu_options(Some("syncfd=false,backend=gfxstream"), &mut gpu_params).is_ok()
3666 );
3667 assert!(!gpu_params.gfxstream_use_syncfd);
3668 }
3669 {
3670 let mut gpu_params: GpuParameters = Default::default();
3671 assert!(parse_gpu_options(
3672 Some("backend=gfxstream,syncfd=invalid_value"),
3673 &mut gpu_params
3674 )
3675 .is_err());
3676 }
3677 {
3678 let mut gpu_params: GpuParameters = Default::default();
3679 assert!(parse_gpu_options(
3680 Some("syncfd=invalid_value,backend=gfxstream"),
3681 &mut gpu_params
3682 )
3683 .is_err());
3684 }
Kaiyi Lidd348a42020-07-13 11:49:46 -07003685 }
3686
3687 #[cfg(all(feature = "gpu", feature = "gfxstream"))]
3688 #[test]
3689 fn parse_gpu_options_not_gfxstream_with_syncfd_specified() {
Jason Macnakd659a0d2021-03-15 15:33:01 -07003690 {
3691 let mut gpu_params: GpuParameters = Default::default();
3692 assert!(
3693 parse_gpu_options(Some("backend=virglrenderer,syncfd=true"), &mut gpu_params)
3694 .is_err()
3695 );
3696 }
3697 {
3698 let mut gpu_params: GpuParameters = Default::default();
3699 assert!(
3700 parse_gpu_options(Some("syncfd=true,backend=virglrenderer"), &mut gpu_params)
3701 .is_err()
3702 );
3703 }
3704 }
3705
3706 #[cfg(feature = "gpu")]
3707 #[test]
3708 fn parse_gpu_display_options_valid() {
3709 {
3710 let mut gpu_params: GpuParameters = Default::default();
3711 assert!(
3712 parse_gpu_display_options(Some("width=500,height=600"), &mut gpu_params).is_ok()
3713 );
3714 assert_eq!(gpu_params.displays.len(), 1);
3715 assert_eq!(gpu_params.displays[0].width, 500);
3716 assert_eq!(gpu_params.displays[0].height, 600);
3717 }
3718 }
3719
3720 #[cfg(feature = "gpu")]
3721 #[test]
3722 fn parse_gpu_display_options_invalid() {
3723 {
3724 let mut gpu_params: GpuParameters = Default::default();
3725 assert!(parse_gpu_display_options(Some("width=500"), &mut gpu_params).is_err());
3726 }
3727 {
3728 let mut gpu_params: GpuParameters = Default::default();
3729 assert!(parse_gpu_display_options(Some("height=500"), &mut gpu_params).is_err());
3730 }
3731 {
3732 let mut gpu_params: GpuParameters = Default::default();
3733 assert!(parse_gpu_display_options(Some("width"), &mut gpu_params).is_err());
3734 }
3735 {
3736 let mut gpu_params: GpuParameters = Default::default();
3737 assert!(parse_gpu_display_options(Some("blah"), &mut gpu_params).is_err());
3738 }
3739 }
3740
3741 #[cfg(feature = "gpu")]
3742 #[test]
3743 fn parse_gpu_options_and_gpu_display_options_valid() {
3744 {
3745 let mut gpu_params: GpuParameters = Default::default();
3746 assert!(parse_gpu_options(Some("2D,width=500,height=600"), &mut gpu_params).is_ok());
3747 assert!(
3748 parse_gpu_display_options(Some("width=700,height=800"), &mut gpu_params).is_ok()
3749 );
3750 assert_eq!(gpu_params.displays.len(), 2);
3751 assert_eq!(gpu_params.displays[0].width, 500);
3752 assert_eq!(gpu_params.displays[0].height, 600);
3753 assert_eq!(gpu_params.displays[1].width, 700);
3754 assert_eq!(gpu_params.displays[1].height, 800);
3755 }
3756 {
3757 let mut gpu_params: GpuParameters = Default::default();
3758 assert!(parse_gpu_options(Some("2D"), &mut gpu_params).is_ok());
3759 assert!(
3760 parse_gpu_display_options(Some("width=700,height=800"), &mut gpu_params).is_ok()
3761 );
3762 assert_eq!(gpu_params.displays.len(), 1);
3763 assert_eq!(gpu_params.displays[0].width, 700);
3764 assert_eq!(gpu_params.displays[0].height, 800);
3765 }
Kaiyi Lidd348a42020-07-13 11:49:46 -07003766 }
Chuanxiao Dongfd5626c2020-04-27 16:35:33 +08003767
3768 #[test]
3769 fn parse_battery_vaild() {
3770 parse_battery_options(Some("type=goldfish")).expect("parse should have succeded");
3771 }
3772
3773 #[test]
3774 fn parse_battery_vaild_no_type() {
3775 parse_battery_options(None).expect("parse should have succeded");
3776 }
3777
3778 #[test]
3779 fn parse_battery_invaild_parameter() {
3780 parse_battery_options(Some("tyep=goldfish")).expect_err("parse should have failed");
3781 }
3782
3783 #[test]
3784 fn parse_battery_invaild_type_value() {
3785 parse_battery_options(Some("type=xxx")).expect_err("parse should have failed");
3786 }
Mattias Nisslerde2c6402021-10-21 12:05:29 +00003787
3788 #[test]
3789 fn parse_stub_pci() {
3790 let params = parse_stub_pci_parameters(Some("0000:01:02.3,vendor=0xfffe,device=0xfffd,class=0xffc1c2,multifunction=true,subsystem_vendor=0xfffc,subsystem_device=0xfffb,revision=0xa")).unwrap();
3791 assert_eq!(params.address.bus, 1);
3792 assert_eq!(params.address.dev, 2);
3793 assert_eq!(params.address.func, 3);
3794 assert_eq!(params.vendor_id, 0xfffe);
3795 assert_eq!(params.device_id, 0xfffd);
3796 assert_eq!(params.class as u8, PciClassCode::Other as u8);
3797 assert_eq!(params.subclass, 0xc1);
3798 assert_eq!(params.programming_interface, 0xc2);
3799 assert!(params.multifunction);
3800 assert_eq!(params.subsystem_vendor_id, 0xfffc);
3801 assert_eq!(params.subsystem_device_id, 0xfffb);
3802 assert_eq!(params.revision_id, 0xa);
3803 }
Junichi Uekawab6a6e942021-12-07 05:49:30 +09003804
3805 #[cfg(feature = "direct")]
3806 #[test]
3807 fn parse_direct_io_options_valid() {
3808 let params = parse_direct_io_options(Some("/dev/mem@1,100-110")).unwrap();
3809 assert_eq!(params.path.to_str(), Some("/dev/mem"));
Junichi Uekawab180f9c2021-12-07 09:21:36 +09003810 assert_eq!(params.ranges[0], BusRange { base: 1, len: 1 });
3811 assert_eq!(params.ranges[1], BusRange { base: 100, len: 11 });
Junichi Uekawab6a6e942021-12-07 05:49:30 +09003812 }
3813
3814 #[cfg(feature = "direct")]
3815 #[test]
3816 fn parse_direct_io_options_hex() {
3817 let params = parse_direct_io_options(Some("/dev/mem@1,0x10,100-110,0x10-0x20")).unwrap();
3818 assert_eq!(params.path.to_str(), Some("/dev/mem"));
Junichi Uekawab180f9c2021-12-07 09:21:36 +09003819 assert_eq!(params.ranges[0], BusRange { base: 1, len: 1 });
3820 assert_eq!(params.ranges[1], BusRange { base: 0x10, len: 1 });
3821 assert_eq!(params.ranges[2], BusRange { base: 100, len: 11 });
3822 assert_eq!(
3823 params.ranges[3],
3824 BusRange {
3825 base: 0x10,
3826 len: 0x11
3827 }
3828 );
Junichi Uekawab6a6e942021-12-07 05:49:30 +09003829 }
3830
3831 #[cfg(feature = "direct")]
3832 #[test]
3833 fn parse_direct_io_options_invalid() {
Junichi Uekawa4d312052021-12-08 16:59:38 +09003834 assert!(parse_direct_io_options(Some("/dev/mem@0y10"))
3835 .unwrap_err()
3836 .to_string()
3837 .contains("invalid base range value"));
3838
3839 assert!(parse_direct_io_options(Some("/dev/mem@"))
3840 .unwrap_err()
3841 .to_string()
3842 .contains("invalid base range value"));
Junichi Uekawab6a6e942021-12-07 05:49:30 +09003843 }
Daniel Verkamp107edb32019-04-05 09:58:48 -07003844}