blob: 8b343d66679b853b831ec1146ba38a09638e8984 [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};
Anton Romanovd48d4602021-12-13 23:41:47 +000014use std::ops::Deref;
Jingkui Wang100e6e42019-03-08 20:41:57 -080015use std::path::{Path, PathBuf};
Jorge E. Moreira359e7de2020-12-02 18:25:53 -080016use std::str::FromStr;
Zach Reizner639d9672017-05-01 17:57:18 -070017use std::string::String;
Zach Reizner39aa26b2017-12-12 18:03:23 -080018use std::thread::sleep;
Stephen Barber56fbf092017-06-29 16:12:14 -070019use std::time::Duration;
Zach Reizner639d9672017-05-01 17:57:18 -070020
Keiichi Watanabe553d2192021-08-16 16:42:27 +090021use arch::{set_default_serial_parameters, Pstore, VcpuAffinity};
Mattias Nisslerbbd91d02021-12-07 08:57:45 +000022use base::{debug, error, getpid, info, kill_process_group, pagesize, reap_child, syslog, warn};
Dmitry Torokhove464a7a2022-01-26 13:29:36 -080023#[cfg(all(feature = "gpu", feature = "virgl_renderer_next"))]
24use crosvm::platform::GpuRenderServerParameters;
Tomasz Jeznach3ce74762021-02-26 01:01:53 -080025#[cfg(feature = "direct")]
26use crosvm::DirectIoOption;
Zach Reizner267f2c82019-07-31 17:07:27 -070027use crosvm::{
28 argument::{self, print_help, set_arguments, Argument},
Mattias Nisslerbbd91d02021-12-07 08:57:45 +000029 platform, BindMount, Config, DiskOption, Executable, FileBackedMappingParameters, GidMap,
30 SharedDir, TouchDeviceOption, VfioCommand, VhostUserFsOption, VhostUserOption,
31 VhostUserWlOption, VhostVsockDeviceParameter, DISK_ID_LEN,
Zach Reizner267f2c82019-07-31 17:07:27 -070032};
Keiichi Watanabe553d2192021-08-16 16:42:27 +090033use devices::serial_device::{SerialHardware, SerialParameters, SerialType};
Woody Chow0b2b6062021-09-03 15:40:02 +090034#[cfg(feature = "audio_cras")]
35use devices::virtio::snd::cras_backend::Error as CrasSndError;
Woody Chowf364d022021-09-17 13:37:32 +090036#[cfg(feature = "audio_cras")]
37use devices::virtio::vhost::user::device::run_cras_snd_device;
Keiichi Watanabeee4b58e2021-08-17 19:34:01 +090038use devices::virtio::vhost::user::device::{
Chirantan Ekbotef08bddd2021-09-10 18:41:06 +090039 run_block_device, run_console_device, run_fs_device, run_net_device, run_vsock_device,
40 run_wl_device,
Keiichi Watanabeee4b58e2021-08-17 19:34:01 +090041};
Alexandre Courbotb42b3e52021-07-09 23:38:57 +090042#[cfg(any(feature = "video-decoder", feature = "video-encoder"))]
43use devices::virtio::VideoBackendType;
Chirantan Ekbote78225292021-06-25 18:30:34 +090044#[cfg(feature = "gpu")]
45use devices::virtio::{
46 gpu::{
Chia-I Wucba95db2021-12-09 15:18:58 -080047 GpuDisplayParameters, GpuMode, GpuParameters, DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH,
Chirantan Ekbote78225292021-06-25 18:30:34 +090048 },
49 vhost::user::device::run_gpu_device,
50};
Junichi Uekawab180f9c2021-12-07 09:21:36 +090051#[cfg(feature = "direct")]
52use devices::BusRange;
Andrew Scull1590e6f2020-03-18 18:00:47 +000053#[cfg(feature = "audio")]
Daniel Verkampfbd61222020-02-14 16:46:36 -080054use devices::{Ac97Backend, Ac97Parameters};
Andrew Walbran00f1c9f2021-12-10 17:13:08 +000055use devices::{PciAddress, PciClassCode, StubPciParameters};
Daniel Verkampeb1640e2021-09-07 14:09:31 -070056use disk::{self, QcowFile};
Andrew Walbranfb7c1082021-06-17 18:12:14 +010057#[cfg(feature = "composite-disk")]
Jooyung Han2e14c732021-07-29 13:27:54 +090058use disk::{
59 create_composite_disk, create_disk_file, create_zero_filler, ImagePartitionType, PartitionInfo,
60};
Andrew Walbran00f1c9f2021-12-10 17:13:08 +000061use hypervisor::ProtectionType;
Jakub Starone7c59052019-04-09 12:31:14 -070062use vm_control::{
Kevin Hamacher6fc5f202021-03-18 12:41:23 +010063 client::{
64 do_modify_battery, do_usb_attach, do_usb_detach, do_usb_list, handle_request, vms_request,
65 ModifyUsbError, ModifyUsbResult,
66 },
67 BalloonControlCommand, BatteryType, DiskControlCommand, UsbControlResult, VmRequest,
Hikaru Nishida6b51c752021-05-21 12:37:43 +090068 VmResponse,
Jakub Starone7c59052019-04-09 12:31:14 -070069};
Zach Reizner639d9672017-05-01 17:57:18 -070070
Chirantan Ekbote520ad432021-12-03 17:43:37 +090071#[cfg(feature = "scudo")]
72#[global_allocator]
73static ALLOCATOR: scudo::GlobalScudoAllocator = scudo::GlobalScudoAllocator;
74
Cody Schuffelen6d1ab502019-05-21 12:12:38 -070075fn executable_is_plugin(executable: &Option<Executable>) -> bool {
Daniel Verkampc26d20b2020-11-04 14:39:31 -080076 matches!(executable, Some(Executable::Plugin(_)))
Cody Schuffelen6d1ab502019-05-21 12:12:38 -070077}
78
Stephen Barbera00753b2017-07-18 13:57:26 -070079// Wait for all children to exit. Return true if they have all exited, false
80// otherwise.
81fn wait_all_children() -> bool {
Stephen Barber49dd2e22018-10-29 18:29:58 -070082 const CHILD_WAIT_MAX_ITER: isize = 100;
Stephen Barbera00753b2017-07-18 13:57:26 -070083 const CHILD_WAIT_MS: u64 = 10;
84 for _ in 0..CHILD_WAIT_MAX_ITER {
Stephen Barbera00753b2017-07-18 13:57:26 -070085 loop {
Zach Reizner56158c82017-08-24 13:50:14 -070086 match reap_child() {
87 Ok(0) => break,
88 // We expect ECHILD which indicates that there were no children left.
89 Err(e) if e.errno() == libc::ECHILD => return true,
90 Err(e) => {
David Tolnayb4bd00f2019-02-12 17:51:26 -080091 warn!("error while waiting for children: {}", e);
Zach Reizner56158c82017-08-24 13:50:14 -070092 return false;
Stephen Barbera00753b2017-07-18 13:57:26 -070093 }
Zach Reizner56158c82017-08-24 13:50:14 -070094 // We reaped one child, so continue reaping.
Zach Reizner55a9e502018-10-03 10:22:32 -070095 _ => {}
Stephen Barbera00753b2017-07-18 13:57:26 -070096 }
97 }
Zach Reizner56158c82017-08-24 13:50:14 -070098 // There's no timeout option for waitpid which reap_child calls internally, so our only
99 // recourse is to sleep while waiting for the children to exit.
Stephen Barbera00753b2017-07-18 13:57:26 -0700100 sleep(Duration::from_millis(CHILD_WAIT_MS));
101 }
102
103 // If we've made it to this point, not all of the children have exited.
David Tolnay5bbbf612018-12-01 17:49:30 -0800104 false
Stephen Barbera00753b2017-07-18 13:57:26 -0700105}
106
Daniel Verkamp107edb32019-04-05 09:58:48 -0700107/// Parse a comma-separated list of CPU numbers and ranges and convert it to a Vec of CPU numbers.
108fn parse_cpu_set(s: &str) -> argument::Result<Vec<usize>> {
109 let mut cpuset = Vec::new();
110 for part in s.split(',') {
111 let range: Vec<&str> = part.split('-').collect();
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +0900112 if range.is_empty() || range.len() > 2 {
Daniel Verkamp107edb32019-04-05 09:58:48 -0700113 return Err(argument::Error::InvalidValue {
114 value: part.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800115 expected: String::from("invalid list syntax"),
Daniel Verkamp107edb32019-04-05 09:58:48 -0700116 });
117 }
118 let first_cpu: usize = range[0]
119 .parse()
120 .map_err(|_| argument::Error::InvalidValue {
121 value: part.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800122 expected: String::from("CPU index must be a non-negative integer"),
Daniel Verkamp107edb32019-04-05 09:58:48 -0700123 })?;
124 let last_cpu: usize = if range.len() == 2 {
125 range[1]
126 .parse()
127 .map_err(|_| argument::Error::InvalidValue {
128 value: part.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800129 expected: String::from("CPU index must be a non-negative integer"),
Daniel Verkamp107edb32019-04-05 09:58:48 -0700130 })?
131 } else {
132 first_cpu
133 };
134
135 if last_cpu < first_cpu {
136 return Err(argument::Error::InvalidValue {
137 value: part.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800138 expected: String::from("CPU ranges must be from low to high"),
Daniel Verkamp107edb32019-04-05 09:58:48 -0700139 });
140 }
141
142 for cpu in first_cpu..=last_cpu {
143 cpuset.push(cpu);
144 }
145 }
146 Ok(cpuset)
147}
148
Daniel Verkampc677fb42020-09-08 13:47:49 -0700149/// Parse a list of guest to host CPU mappings.
150///
151/// Each mapping consists of a single guest CPU index mapped to one or more host CPUs in the form
152/// accepted by `parse_cpu_set`:
153///
154/// `<GUEST-CPU>=<HOST-CPU-SET>[:<GUEST-CPU>=<HOST-CPU-SET>[:...]]`
155fn parse_cpu_affinity(s: &str) -> argument::Result<VcpuAffinity> {
156 if s.contains('=') {
157 let mut affinity_map = BTreeMap::new();
158 for cpu_pair in s.split(':') {
159 let assignment: Vec<&str> = cpu_pair.split('=').collect();
160 if assignment.len() != 2 {
161 return Err(argument::Error::InvalidValue {
162 value: cpu_pair.to_owned(),
163 expected: String::from("invalid VCPU assignment syntax"),
164 });
165 }
166 let guest_cpu = assignment[0]
167 .parse()
168 .map_err(|_| argument::Error::InvalidValue {
169 value: assignment[0].to_owned(),
170 expected: String::from("CPU index must be a non-negative integer"),
171 })?;
172 let host_cpu_set = parse_cpu_set(assignment[1])?;
173 if affinity_map.insert(guest_cpu, host_cpu_set).is_some() {
174 return Err(argument::Error::InvalidValue {
175 value: cpu_pair.to_owned(),
176 expected: String::from("VCPU index must be unique"),
177 });
178 }
179 }
180 Ok(VcpuAffinity::PerVcpu(affinity_map))
181 } else {
182 Ok(VcpuAffinity::Global(parse_cpu_set(s)?))
183 }
184}
185
Daniel Verkamp8a72afc2021-03-15 17:55:52 -0700186fn parse_cpu_capacity(s: &str, cpu_capacity: &mut BTreeMap<usize, u32>) -> argument::Result<()> {
187 for cpu_pair in s.split(',') {
188 let assignment: Vec<&str> = cpu_pair.split('=').collect();
189 if assignment.len() != 2 {
190 return Err(argument::Error::InvalidValue {
191 value: cpu_pair.to_owned(),
192 expected: String::from("invalid CPU capacity syntax"),
193 });
194 }
195 let cpu = assignment[0]
196 .parse()
197 .map_err(|_| argument::Error::InvalidValue {
198 value: assignment[0].to_owned(),
199 expected: String::from("CPU index must be a non-negative integer"),
200 })?;
201 let capacity = assignment[1]
202 .parse()
203 .map_err(|_| argument::Error::InvalidValue {
204 value: assignment[1].to_owned(),
205 expected: String::from("CPU capacity must be a non-negative integer"),
206 })?;
207 if cpu_capacity.insert(cpu, capacity).is_some() {
208 return Err(argument::Error::InvalidValue {
209 value: cpu_pair.to_owned(),
210 expected: String::from("CPU index must be unique"),
211 });
212 }
213 }
214 Ok(())
215}
216
Jason Macnakcc7070b2019-11-06 14:48:12 -0800217#[cfg(feature = "gpu")]
Jason Macnakd659a0d2021-03-15 15:33:01 -0700218fn parse_gpu_options(s: Option<&str>, gpu_params: &mut GpuParameters) -> argument::Result<()> {
Kaiyi Lidd348a42020-07-13 11:49:46 -0700219 #[cfg(feature = "gfxstream")]
220 let mut vulkan_specified = false;
221 #[cfg(feature = "gfxstream")]
222 let mut syncfd_specified = false;
Jason Macnak046ed142020-10-08 09:11:53 -0700223 #[cfg(feature = "gfxstream")]
224 let mut angle_specified = false;
Jason Macnakcc7070b2019-11-06 14:48:12 -0800225
Jason Macnakd659a0d2021-03-15 15:33:01 -0700226 let mut display_w: Option<u32> = None;
227 let mut display_h: Option<u32> = None;
228
Jason Macnakcc7070b2019-11-06 14:48:12 -0800229 if let Some(s) = s {
230 let opts = s
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +0900231 .split(',')
232 .map(|frag| frag.split('='))
Jason Macnakcc7070b2019-11-06 14:48:12 -0800233 .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
234
235 for (k, v) in opts {
236 match k {
Lingfeng Yangddbe8b72020-01-30 10:00:36 -0800237 // Deprecated: Specifying --gpu=<mode> Not great as the mode can be set multiple
Chia-I Wue25859b2021-04-14 13:57:55 -0700238 // times if the user specifies several modes (--gpu=2d,virglrenderer,gfxstream)
Jason Macnak327fc242020-01-10 12:45:36 -0800239 "2d" | "2D" => {
240 gpu_params.mode = GpuMode::Mode2D;
241 }
Chia-I Wue25859b2021-04-14 13:57:55 -0700242 "3d" | "3D" | "virglrenderer" => {
243 gpu_params.mode = GpuMode::ModeVirglRenderer;
Jason Macnak327fc242020-01-10 12:45:36 -0800244 }
Lingfeng Yangddbe8b72020-01-30 10:00:36 -0800245 #[cfg(feature = "gfxstream")]
246 "gfxstream" => {
Gurchetan Singhb1394f72020-10-19 18:31:13 -0700247 gpu_params.mode = GpuMode::ModeGfxstream;
Lingfeng Yangddbe8b72020-01-30 10:00:36 -0800248 }
249 // Preferred: Specifying --gpu,backend=<mode>
250 "backend" => match v {
251 "2d" | "2D" => {
252 gpu_params.mode = GpuMode::Mode2D;
253 }
Chia-I Wue25859b2021-04-14 13:57:55 -0700254 "3d" | "3D" | "virglrenderer" => {
255 gpu_params.mode = GpuMode::ModeVirglRenderer;
Lingfeng Yangddbe8b72020-01-30 10:00:36 -0800256 }
257 #[cfg(feature = "gfxstream")]
258 "gfxstream" => {
Gurchetan Singhb1394f72020-10-19 18:31:13 -0700259 gpu_params.mode = GpuMode::ModeGfxstream;
Lingfeng Yangddbe8b72020-01-30 10:00:36 -0800260 }
261 _ => {
262 return Err(argument::Error::InvalidValue {
263 value: v.to_string(),
Judy Hsiao59343052020-03-16 15:58:03 +0800264 expected: String::from(
Chia-I Wue25859b2021-04-14 13:57:55 -0700265 "gpu parameter 'backend' should be one of (2d|virglrenderer|gfxstream)",
Judy Hsiao59343052020-03-16 15:58:03 +0800266 ),
Lingfeng Yangddbe8b72020-01-30 10:00:36 -0800267 });
268 }
269 },
Jason Macnakbf195582019-11-20 16:25:49 -0800270 "egl" => match v {
271 "true" | "" => {
272 gpu_params.renderer_use_egl = true;
273 }
274 "false" => {
275 gpu_params.renderer_use_egl = false;
276 }
277 _ => {
278 return Err(argument::Error::InvalidValue {
279 value: v.to_string(),
Judy Hsiao59343052020-03-16 15:58:03 +0800280 expected: String::from("gpu parameter 'egl' should be a boolean"),
Jason Macnakbf195582019-11-20 16:25:49 -0800281 });
282 }
283 },
284 "gles" => match v {
285 "true" | "" => {
286 gpu_params.renderer_use_gles = true;
287 }
288 "false" => {
289 gpu_params.renderer_use_gles = false;
290 }
291 _ => {
292 return Err(argument::Error::InvalidValue {
293 value: v.to_string(),
Judy Hsiao59343052020-03-16 15:58:03 +0800294 expected: String::from("gpu parameter 'gles' should be a boolean"),
Jason Macnakbf195582019-11-20 16:25:49 -0800295 });
296 }
297 },
298 "glx" => match v {
299 "true" | "" => {
300 gpu_params.renderer_use_glx = true;
301 }
302 "false" => {
303 gpu_params.renderer_use_glx = false;
304 }
305 _ => {
306 return Err(argument::Error::InvalidValue {
307 value: v.to_string(),
Judy Hsiao59343052020-03-16 15:58:03 +0800308 expected: String::from("gpu parameter 'glx' should be a boolean"),
Jason Macnakbf195582019-11-20 16:25:49 -0800309 });
310 }
311 },
312 "surfaceless" => match v {
313 "true" | "" => {
314 gpu_params.renderer_use_surfaceless = true;
315 }
316 "false" => {
317 gpu_params.renderer_use_surfaceless = false;
318 }
319 _ => {
320 return Err(argument::Error::InvalidValue {
321 value: v.to_string(),
Judy Hsiao59343052020-03-16 15:58:03 +0800322 expected: String::from(
323 "gpu parameter 'surfaceless' should be a boolean",
324 ),
Jason Macnakbf195582019-11-20 16:25:49 -0800325 });
326 }
327 },
Kaiyi Li6404e452020-07-07 19:12:03 -0700328 #[cfg(feature = "gfxstream")]
Kaiyi Lidd348a42020-07-13 11:49:46 -0700329 "syncfd" => {
330 syncfd_specified = true;
331 match v {
332 "true" | "" => {
333 gpu_params.gfxstream_use_syncfd = true;
334 }
335 "false" => {
336 gpu_params.gfxstream_use_syncfd = false;
337 }
338 _ => {
339 return Err(argument::Error::InvalidValue {
340 value: v.to_string(),
341 expected: String::from(
342 "gpu parameter 'syncfd' should be a boolean",
343 ),
344 });
345 }
Kaiyi Li6404e452020-07-07 19:12:03 -0700346 }
Kaiyi Lidd348a42020-07-13 11:49:46 -0700347 }
Kaiyi Li6c52a2e2020-07-07 19:28:32 -0700348 #[cfg(feature = "gfxstream")]
Jason Macnak046ed142020-10-08 09:11:53 -0700349 "angle" => {
350 angle_specified = true;
351 match v {
352 "true" | "" => {
353 gpu_params.gfxstream_use_guest_angle = true;
354 }
355 "false" => {
356 gpu_params.gfxstream_use_guest_angle = false;
357 }
358 _ => {
359 return Err(argument::Error::InvalidValue {
360 value: v.to_string(),
361 expected: String::from("gpu parameter 'angle' should be a boolean"),
362 });
363 }
364 }
365 }
Kaiyi Lidd348a42020-07-13 11:49:46 -0700366 "vulkan" => {
Chia-I Wu6d473b32021-04-12 10:14:24 -0700367 #[cfg(feature = "gfxstream")]
368 {
369 vulkan_specified = true;
370 }
Kaiyi Lidd348a42020-07-13 11:49:46 -0700371 match v {
372 "true" | "" => {
Chia-I Wu0beb2462021-04-12 09:36:08 -0700373 gpu_params.use_vulkan = true;
Kaiyi Lidd348a42020-07-13 11:49:46 -0700374 }
375 "false" => {
Chia-I Wu0beb2462021-04-12 09:36:08 -0700376 gpu_params.use_vulkan = false;
Kaiyi Lidd348a42020-07-13 11:49:46 -0700377 }
378 _ => {
379 return Err(argument::Error::InvalidValue {
380 value: v.to_string(),
381 expected: String::from(
382 "gpu parameter 'vulkan' should be a boolean",
383 ),
384 });
385 }
Kaiyi Li6c52a2e2020-07-07 19:28:32 -0700386 }
Kaiyi Lidd348a42020-07-13 11:49:46 -0700387 }
Jason Macnakcc7070b2019-11-06 14:48:12 -0800388 "width" => {
Jason Macnakd659a0d2021-03-15 15:33:01 -0700389 let width = v
390 .parse::<u32>()
391 .map_err(|_| argument::Error::InvalidValue {
392 value: v.to_string(),
393 expected: String::from("gpu parameter 'width' must be a valid integer"),
394 })?;
395 display_w = Some(width);
Jason Macnakcc7070b2019-11-06 14:48:12 -0800396 }
397 "height" => {
Jason Macnakd659a0d2021-03-15 15:33:01 -0700398 let height = v
399 .parse::<u32>()
400 .map_err(|_| argument::Error::InvalidValue {
401 value: v.to_string(),
402 expected: String::from(
403 "gpu parameter 'height' must be a valid integer",
404 ),
405 })?;
406 display_h = Some(height);
Jason Macnakcc7070b2019-11-06 14:48:12 -0800407 }
John Batesb220eac2020-09-14 17:03:02 -0700408 "cache-path" => gpu_params.cache_path = Some(v.to_string()),
409 "cache-size" => gpu_params.cache_size = Some(v.to_string()),
Gurchetan Singh401340e2021-03-23 09:34:00 -0700410 "udmabuf" => match v {
411 "true" | "" => {
412 gpu_params.udmabuf = true;
413 }
414 "false" => {
415 gpu_params.udmabuf = false;
416 }
417 _ => {
418 return Err(argument::Error::InvalidValue {
419 value: v.to_string(),
420 expected: String::from("gpu parameter 'udmabuf' should be a boolean"),
421 });
422 }
423 },
Jason Macnakcc7070b2019-11-06 14:48:12 -0800424 "" => {}
425 _ => {
426 return Err(argument::Error::UnknownArgument(format!(
427 "gpu parameter {}",
428 k
429 )));
430 }
431 }
432 }
433 }
434
Jason Macnakd659a0d2021-03-15 15:33:01 -0700435 if display_w.is_some() || display_h.is_some() {
436 if display_w.is_none() || display_h.is_none() {
437 return Err(argument::Error::InvalidValue {
438 value: s.unwrap_or("").to_string(),
439 expected: String::from(
440 "gpu must include both 'width' and 'height' if either is supplied",
441 ),
442 });
443 }
444
445 gpu_params.displays.push(GpuDisplayParameters {
446 width: display_w.unwrap(),
447 height: display_h.unwrap(),
448 });
449 }
450
Kaiyi Lidd348a42020-07-13 11:49:46 -0700451 #[cfg(feature = "gfxstream")]
452 {
Chia-I Wu91df6562021-04-12 09:47:38 -0700453 if !vulkan_specified && gpu_params.mode == GpuMode::ModeGfxstream {
454 gpu_params.use_vulkan = true;
455 }
456
Chia-I Wu6d473b32021-04-12 10:14:24 -0700457 if syncfd_specified || angle_specified {
Kaiyi Lidd348a42020-07-13 11:49:46 -0700458 match gpu_params.mode {
Gurchetan Singhb1394f72020-10-19 18:31:13 -0700459 GpuMode::ModeGfxstream => {}
Kaiyi Lidd348a42020-07-13 11:49:46 -0700460 _ => {
461 return Err(argument::Error::UnknownArgument(
Chia-I Wu6d473b32021-04-12 10:14:24 -0700462 "gpu parameter syncfd and angle are only supported for gfxstream backend"
Kaiyi Lidd348a42020-07-13 11:49:46 -0700463 .to_string(),
464 ));
465 }
466 }
467 }
468 }
469
Jason Macnakd659a0d2021-03-15 15:33:01 -0700470 Ok(())
471}
472
Alexandre Courbotb42b3e52021-07-09 23:38:57 +0900473#[cfg(any(feature = "video-decoder", feature = "video-encoder"))]
474fn parse_video_options(s: Option<&str>) -> argument::Result<VideoBackendType> {
Alexandre Courbotc02960d2021-07-11 23:06:30 +0900475 const VALID_VIDEO_BACKENDS: &[&str] = &[
476 #[cfg(feature = "libvda")]
477 "libvda",
478 ];
Alexandre Courbotb42b3e52021-07-09 23:38:57 +0900479
480 match s {
Alexandre Courbotc02960d2021-07-11 23:06:30 +0900481 #[cfg(feature = "libvda")]
Alexandre Courbotb42b3e52021-07-09 23:38:57 +0900482 None => Ok(VideoBackendType::Libvda),
Alexandre Courbotc02960d2021-07-11 23:06:30 +0900483 #[cfg(feature = "libvda")]
Alexandre Courbotb42b3e52021-07-09 23:38:57 +0900484 Some("libvda") => Ok(VideoBackendType::Libvda),
David Staessensa78ae7a2021-07-15 09:48:56 +0900485 #[cfg(feature = "libvda")]
Alexandre Courbot54cf8342021-12-20 18:10:08 +0900486 Some("libvda-vd") => Ok(VideoBackendType::LibvdaVd),
Alexandre Courbotb42b3e52021-07-09 23:38:57 +0900487 Some(s) => Err(argument::Error::InvalidValue {
488 value: s.to_owned(),
489 expected: format!("should be one of ({})", VALID_VIDEO_BACKENDS.join("|")),
490 }),
491 }
492}
493
Jason Macnakd659a0d2021-03-15 15:33:01 -0700494#[cfg(feature = "gpu")]
495fn parse_gpu_display_options(
496 s: Option<&str>,
497 gpu_params: &mut GpuParameters,
498) -> argument::Result<()> {
499 let mut display_w: Option<u32> = None;
500 let mut display_h: Option<u32> = None;
501
502 if let Some(s) = s {
503 let opts = s
504 .split(',')
505 .map(|frag| frag.split('='))
506 .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
507
508 for (k, v) in opts {
509 match k {
510 "width" => {
511 let width = v
512 .parse::<u32>()
513 .map_err(|_| argument::Error::InvalidValue {
514 value: v.to_string(),
515 expected: String::from("gpu parameter 'width' must be a valid integer"),
516 })?;
517 display_w = Some(width);
518 }
519 "height" => {
520 let height = v
521 .parse::<u32>()
522 .map_err(|_| argument::Error::InvalidValue {
523 value: v.to_string(),
524 expected: String::from(
525 "gpu parameter 'height' must be a valid integer",
526 ),
527 })?;
528 display_h = Some(height);
529 }
530 "" => {}
531 _ => {
532 return Err(argument::Error::UnknownArgument(format!(
533 "gpu-display parameter {}",
534 k
535 )));
536 }
537 }
538 }
539 }
540
541 if display_w.is_none() || display_h.is_none() {
542 return Err(argument::Error::InvalidValue {
543 value: s.unwrap_or("").to_string(),
544 expected: String::from("gpu-display must include both 'width' and 'height'"),
545 });
546 }
547
548 gpu_params.displays.push(GpuDisplayParameters {
549 width: display_w.unwrap(),
550 height: display_h.unwrap(),
551 });
552
553 Ok(())
Jason Macnakcc7070b2019-11-06 14:48:12 -0800554}
555
Chia-I Wu16fb6592021-11-10 11:45:32 -0800556#[cfg(all(feature = "gpu", feature = "virgl_renderer_next"))]
Dmitry Torokhove464a7a2022-01-26 13:29:36 -0800557fn parse_gpu_render_server_options(s: Option<&str>) -> argument::Result<GpuRenderServerParameters> {
Chia-I Wu16fb6592021-11-10 11:45:32 -0800558 let mut path: Option<PathBuf> = None;
Chia-I Wu7f0f7c12022-01-12 10:42:18 -0800559 let mut cache_path = None;
560 let mut cache_size = None;
Chia-I Wu16fb6592021-11-10 11:45:32 -0800561
562 if let Some(s) = s {
563 let opts = s
564 .split(',')
565 .map(|frag| frag.split('='))
566 .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
567
568 for (k, v) in opts {
569 match k {
570 "path" => {
571 path =
572 Some(
573 PathBuf::from_str(v).map_err(|e| argument::Error::InvalidValue {
574 value: v.to_string(),
575 expected: e.to_string(),
576 })?,
577 )
578 }
Chia-I Wu7f0f7c12022-01-12 10:42:18 -0800579 "cache-path" => cache_path = Some(v.to_string()),
580 "cache-size" => cache_size = Some(v.to_string()),
Chia-I Wu16fb6592021-11-10 11:45:32 -0800581 "" => {}
582 _ => {
583 return Err(argument::Error::UnknownArgument(format!(
584 "gpu-render-server parameter {}",
585 k
586 )));
587 }
588 }
589 }
590 }
591
592 if let Some(p) = path {
Dmitry Torokhove464a7a2022-01-26 13:29:36 -0800593 Ok(GpuRenderServerParameters {
Chia-I Wu7f0f7c12022-01-12 10:42:18 -0800594 path: p,
595 cache_path,
596 cache_size,
Dmitry Torokhove464a7a2022-01-26 13:29:36 -0800597 })
Chia-I Wu16fb6592021-11-10 11:45:32 -0800598 } else {
599 Err(argument::Error::InvalidValue {
600 value: s.unwrap_or("").to_string(),
601 expected: String::from("gpu-render-server must include 'path'"),
602 })
603 }
604}
605
Andrew Scull1590e6f2020-03-18 18:00:47 +0000606#[cfg(feature = "audio")]
Judy Hsiaod5c1e962020-02-04 12:30:01 +0800607fn parse_ac97_options(s: &str) -> argument::Result<Ac97Parameters> {
608 let mut ac97_params: Ac97Parameters = Default::default();
609
610 let opts = s
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +0900611 .split(',')
612 .map(|frag| frag.split('='))
Judy Hsiaod5c1e962020-02-04 12:30:01 +0800613 .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
614
615 for (k, v) in opts {
616 match k {
617 "backend" => {
618 ac97_params.backend =
619 v.parse::<Ac97Backend>()
620 .map_err(|e| argument::Error::InvalidValue {
621 value: v.to_string(),
622 expected: e.to_string(),
623 })?;
624 }
625 "capture" => {
626 ac97_params.capture = v.parse::<bool>().map_err(|e| {
627 argument::Error::Syntax(format!("invalid capture option: {}", e))
628 })?;
629 }
Dennis Kempin50a58f92021-06-23 11:34:31 -0700630 #[cfg(feature = "audio_cras")]
paulhsia83d51602021-03-09 17:13:14 +0800631 "client_type" => {
632 ac97_params
633 .set_client_type(v)
634 .map_err(|e| argument::Error::InvalidValue {
635 value: v.to_string(),
636 expected: e.to_string(),
637 })?;
638 }
Woody Chowb27dea42021-09-08 15:51:22 +0900639 #[cfg(feature = "audio_cras")]
640 "socket_type" => {
641 ac97_params
642 .set_socket_type(v)
643 .map_err(|e| argument::Error::InvalidValue {
644 value: v.to_string(),
645 expected: e.to_string(),
646 })?;
647 }
Jorge E. Moreira6a88a5d2021-03-12 15:34:46 -0800648 #[cfg(any(target_os = "linux", target_os = "android"))]
Jorge E. Moreira359e7de2020-12-02 18:25:53 -0800649 "server" => {
650 ac97_params.vios_server_path =
651 Some(
652 PathBuf::from_str(v).map_err(|e| argument::Error::InvalidValue {
653 value: v.to_string(),
654 expected: e.to_string(),
655 })?,
656 );
657 }
Judy Hsiaod5c1e962020-02-04 12:30:01 +0800658 _ => {
659 return Err(argument::Error::UnknownArgument(format!(
660 "unknown ac97 parameter {}",
661 k
662 )));
663 }
664 }
665 }
666
Jorge E. Moreira359e7de2020-12-02 18:25:53 -0800667 // server is required for and exclusive to vios backend
Jorge E. Moreira6a88a5d2021-03-12 15:34:46 -0800668 #[cfg(any(target_os = "linux", target_os = "android"))]
Jorge E. Moreira359e7de2020-12-02 18:25:53 -0800669 match ac97_params.backend {
670 Ac97Backend::VIOS => {
671 if ac97_params.vios_server_path.is_none() {
672 return Err(argument::Error::ExpectedArgument(String::from(
673 "server argument is required for VIOS backend",
674 )));
675 }
676 }
677 _ => {
678 if ac97_params.vios_server_path.is_some() {
679 return Err(argument::Error::UnexpectedValue(String::from(
680 "server argument is exclusive to the VIOS backend",
681 )));
682 }
683 }
684 }
685
Judy Hsiaod5c1e962020-02-04 12:30:01 +0800686 Ok(ac97_params)
687}
688
Trent Begin17ccaad2019-04-17 13:51:25 -0600689fn parse_serial_options(s: &str) -> argument::Result<SerialParameters> {
690 let mut serial_setting = SerialParameters {
691 type_: SerialType::Sink,
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -0700692 hardware: SerialHardware::Serial,
Trent Begin17ccaad2019-04-17 13:51:25 -0600693 path: None,
Iliyan Malchev2c1417b2020-04-14 09:40:41 -0700694 input: None,
Trent Begin923bab02019-06-17 13:48:06 -0600695 num: 1,
Trent Begin17ccaad2019-04-17 13:51:25 -0600696 console: false,
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -0700697 earlycon: false,
Jorge E. Moreira1e262302019-08-01 14:40:03 -0700698 stdin: false,
Trent Begin17ccaad2019-04-17 13:51:25 -0600699 };
700
701 let opts = s
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +0900702 .split(',')
Nicholas Hollingumc76c2da2020-09-18 15:53:16 +1000703 .map(|frag| frag.splitn(2, '='))
Trent Begin17ccaad2019-04-17 13:51:25 -0600704 .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
705
706 for (k, v) in opts {
707 match k {
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -0700708 "hardware" => {
709 serial_setting.hardware = v
710 .parse::<SerialHardware>()
711 .map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?
712 }
Trent Begin17ccaad2019-04-17 13:51:25 -0600713 "type" => {
714 serial_setting.type_ = v
715 .parse::<SerialType>()
716 .map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?
717 }
718 "num" => {
719 let num = v.parse::<u8>().map_err(|e| {
720 argument::Error::Syntax(format!("serial device number is not parsable: {}", e))
721 })?;
A. Cody Schuffelen3faab5a2020-10-05 17:29:16 -0700722 if num < 1 {
Trent Begin17ccaad2019-04-17 13:51:25 -0600723 return Err(argument::Error::InvalidValue {
724 value: num.to_string(),
A. Cody Schuffelen3faab5a2020-10-05 17:29:16 -0700725 expected: String::from("Serial port num must be at least 1"),
Trent Begin17ccaad2019-04-17 13:51:25 -0600726 });
727 }
728 serial_setting.num = num;
729 }
730 "console" => {
731 serial_setting.console = v.parse::<bool>().map_err(|e| {
732 argument::Error::Syntax(format!(
733 "serial device console is not parseable: {}",
734 e
735 ))
736 })?
737 }
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -0700738 "earlycon" => {
739 serial_setting.earlycon = v.parse::<bool>().map_err(|e| {
740 argument::Error::Syntax(format!(
741 "serial device earlycon is not parseable: {}",
742 e,
743 ))
744 })?
745 }
Jorge E. Moreira1e262302019-08-01 14:40:03 -0700746 "stdin" => {
747 serial_setting.stdin = v.parse::<bool>().map_err(|e| {
748 argument::Error::Syntax(format!("serial device stdin is not parseable: {}", e))
Iliyan Malchev2c1417b2020-04-14 09:40:41 -0700749 })?;
750 if serial_setting.stdin && serial_setting.input.is_some() {
751 return Err(argument::Error::TooManyArguments(
752 "Cannot specify both stdin and input options".to_string(),
753 ));
754 }
Jorge E. Moreira1e262302019-08-01 14:40:03 -0700755 }
Jorge E. Moreira9c9e0e72019-05-17 13:57:04 -0700756 "path" => serial_setting.path = Some(PathBuf::from(v)),
Iliyan Malchev2c1417b2020-04-14 09:40:41 -0700757 "input" => {
758 if serial_setting.stdin {
759 return Err(argument::Error::TooManyArguments(
760 "Cannot specify both stdin and input options".to_string(),
761 ));
762 }
763 serial_setting.input = Some(PathBuf::from(v));
764 }
Trent Begin17ccaad2019-04-17 13:51:25 -0600765 _ => {
766 return Err(argument::Error::UnknownArgument(format!(
767 "serial parameter {}",
768 k
769 )));
770 }
771 }
772 }
773
A. Cody Schuffelen3faab5a2020-10-05 17:29:16 -0700774 if serial_setting.hardware == SerialHardware::Serial && serial_setting.num > 4 {
775 return Err(argument::Error::InvalidValue {
776 value: serial_setting.num.to_string(),
777 expected: String::from("Serial port num must be 4 or less"),
778 });
779 }
780
Trent Begin17ccaad2019-04-17 13:51:25 -0600781 Ok(serial_setting)
782}
783
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800784fn parse_plugin_mount_option(value: &str) -> argument::Result<BindMount> {
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +0900785 let components: Vec<&str> = value.split(':').collect();
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800786 if components.is_empty() || components.len() > 3 || components[0].is_empty() {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800787 return Err(argument::Error::InvalidValue {
788 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800789 expected: String::from(
790 "`plugin-mount` should be in a form of: <src>[:[<dst>][:<writable>]]",
791 ),
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800792 });
793 }
794
795 let src = PathBuf::from(components[0]);
796 if src.is_relative() {
797 return Err(argument::Error::InvalidValue {
798 value: components[0].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800799 expected: String::from("the source path for `plugin-mount` must be absolute"),
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800800 });
801 }
802 if !src.exists() {
803 return Err(argument::Error::InvalidValue {
804 value: components[0].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800805 expected: String::from("the source path for `plugin-mount` does not exist"),
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800806 });
807 }
808
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800809 let dst = PathBuf::from(match components.get(1) {
810 None | Some(&"") => components[0],
811 Some(path) => path,
812 });
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800813 if dst.is_relative() {
814 return Err(argument::Error::InvalidValue {
815 value: components[1].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800816 expected: String::from("the destination path for `plugin-mount` must be absolute"),
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800817 });
818 }
819
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800820 let writable: bool = match components.get(2) {
821 None => false,
822 Some(s) => s.parse().map_err(|_| argument::Error::InvalidValue {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800823 value: components[2].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800824 expected: String::from("the <writable> component for `plugin-mount` is not valid bool"),
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800825 })?,
826 };
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800827
828 Ok(BindMount { src, dst, writable })
829}
830
831fn parse_plugin_gid_map_option(value: &str) -> argument::Result<GidMap> {
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +0900832 let components: Vec<&str> = value.split(':').collect();
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800833 if components.is_empty() || components.len() > 3 || components[0].is_empty() {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800834 return Err(argument::Error::InvalidValue {
835 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800836 expected: String::from(
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800837 "`plugin-gid-map` must have exactly 3 components: <inner>[:[<outer>][:<count>]]",
Judy Hsiao59343052020-03-16 15:58:03 +0800838 ),
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800839 });
840 }
841
842 let inner: libc::gid_t = components[0]
843 .parse()
844 .map_err(|_| argument::Error::InvalidValue {
845 value: components[0].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800846 expected: String::from("the <inner> component for `plugin-gid-map` is not valid gid"),
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800847 })?;
848
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800849 let outer: libc::gid_t = match components.get(1) {
850 None | Some(&"") => inner,
851 Some(s) => s.parse().map_err(|_| argument::Error::InvalidValue {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800852 value: components[1].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800853 expected: String::from("the <outer> component for `plugin-gid-map` is not valid gid"),
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800854 })?,
855 };
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800856
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800857 let count: u32 = match components.get(2) {
858 None => 1,
859 Some(s) => s.parse().map_err(|_| argument::Error::InvalidValue {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800860 value: components[2].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800861 expected: String::from(
862 "the <count> component for `plugin-gid-map` is not valid number",
863 ),
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800864 })?,
865 };
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800866
867 Ok(GidMap {
868 inner,
869 outer,
870 count,
871 })
872}
873
Chuanxiao Dongfd5626c2020-04-27 16:35:33 +0800874fn parse_battery_options(s: Option<&str>) -> argument::Result<BatteryType> {
875 let mut battery_type: BatteryType = Default::default();
876
877 if let Some(s) = s {
878 let opts = s
Andrew Walbran9cfdbd92021-01-11 17:40:34 +0000879 .split(',')
880 .map(|frag| frag.split('='))
Chuanxiao Dongfd5626c2020-04-27 16:35:33 +0800881 .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
882
883 for (k, v) in opts {
884 match k {
885 "type" => match v.parse::<BatteryType>() {
886 Ok(type_) => battery_type = type_,
887 Err(e) => {
888 return Err(argument::Error::InvalidValue {
889 value: v.to_string(),
890 expected: e.to_string(),
891 });
892 }
893 },
894 "" => {}
895 _ => {
896 return Err(argument::Error::UnknownArgument(format!(
897 "battery parameter {}",
898 k
899 )));
900 }
901 }
902 }
903 }
904
905 Ok(battery_type)
906}
907
Tomasz Jeznach3ce74762021-02-26 01:01:53 -0800908#[cfg(feature = "direct")]
Junichi Uekawab6a6e942021-12-07 05:49:30 +0900909fn parse_hex_or_decimal(maybe_hex_string: &str) -> Result<u64, std::num::ParseIntError> {
910 // Parse string starting with 0x as hex and others as numbers.
911 let without_prefix = maybe_hex_string.strip_prefix("0x");
912 match without_prefix {
913 Some(hex_string) => u64::from_str_radix(hex_string, 16),
914 None => u64::from_str(maybe_hex_string),
915 }
916}
917
918#[cfg(feature = "direct")]
Tomasz Jeznach3ce74762021-02-26 01:01:53 -0800919fn parse_direct_io_options(s: Option<&str>) -> argument::Result<DirectIoOption> {
920 let s = s.ok_or(argument::Error::ExpectedValue(String::from(
921 "expected path@range[,range] value",
922 )))?;
923 let parts: Vec<&str> = s.splitn(2, '@').collect();
924 if parts.len() != 2 {
925 return Err(argument::Error::InvalidValue {
926 value: s.to_string(),
927 expected: String::from("missing port range, use /path@X-Y,Z,.. syntax"),
928 });
929 }
930 let path = PathBuf::from(parts[0]);
931 if !path.exists() {
932 return Err(argument::Error::InvalidValue {
933 value: parts[0].to_owned(),
934 expected: String::from("the path does not exist"),
935 });
936 };
Junichi Uekawab180f9c2021-12-07 09:21:36 +0900937 let ranges: argument::Result<Vec<BusRange>> = parts[1]
Tomasz Jeznach3ce74762021-02-26 01:01:53 -0800938 .split(',')
939 .map(|frag| frag.split('-'))
940 .map(|mut range| {
941 let base = range
942 .next()
Junichi Uekawab6a6e942021-12-07 05:49:30 +0900943 .map(|v| parse_hex_or_decimal(v))
Tomasz Jeznach3ce74762021-02-26 01:01:53 -0800944 .map_or(Ok(None), |r| r.map(Some));
945 let last = range
946 .next()
Junichi Uekawab6a6e942021-12-07 05:49:30 +0900947 .map(|v| parse_hex_or_decimal(v))
Tomasz Jeznach3ce74762021-02-26 01:01:53 -0800948 .map_or(Ok(None), |r| r.map(Some));
949 (base, last)
950 })
951 .map(|range| match range {
Junichi Uekawab180f9c2021-12-07 09:21:36 +0900952 (Ok(Some(base)), Ok(None)) => Ok(BusRange { base, len: 1 }),
953 (Ok(Some(base)), Ok(Some(last))) => Ok(BusRange {
954 base,
955 len: last.saturating_sub(base).saturating_add(1),
956 }),
Junichi Uekawa4d312052021-12-08 16:59:38 +0900957 (Err(_), _) => Err(argument::Error::InvalidValue {
958 value: s.to_owned(),
Tomasz Jeznach3ce74762021-02-26 01:01:53 -0800959 expected: String::from("invalid base range value"),
960 }),
Junichi Uekawa4d312052021-12-08 16:59:38 +0900961 (_, Err(_)) => Err(argument::Error::InvalidValue {
962 value: s.to_owned(),
Tomasz Jeznach3ce74762021-02-26 01:01:53 -0800963 expected: String::from("invalid last range value"),
964 }),
965 _ => Err(argument::Error::InvalidValue {
966 value: s.to_owned(),
967 expected: String::from("invalid range format"),
968 }),
969 })
970 .collect();
971 Ok(DirectIoOption {
972 path,
973 ranges: ranges?,
974 })
975}
976
Mattias Nisslerde2c6402021-10-21 12:05:29 +0000977fn parse_stub_pci_parameters(s: Option<&str>) -> argument::Result<StubPciParameters> {
978 let s = s.ok_or(argument::Error::ExpectedValue(String::from(
979 "stub-pci-device configuration expected",
980 )))?;
981
982 let mut options = argument::parse_key_value_options("stub-pci-device", s, ',');
983 let addr = options
984 .next()
985 .ok_or(argument::Error::ExpectedValue(String::from(
986 "stub-pci-device: expected device address",
987 )))?
988 .key();
989 let mut params = StubPciParameters {
990 address: PciAddress::from_string(addr),
991 vendor_id: 0,
992 device_id: 0,
993 class: PciClassCode::Other,
994 subclass: 0,
995 programming_interface: 0,
Mattias Nisslerde2c6402021-10-21 12:05:29 +0000996 subsystem_device_id: 0,
997 subsystem_vendor_id: 0,
998 revision_id: 0,
999 };
1000 for opt in options {
1001 match opt.key() {
1002 "vendor" => params.vendor_id = opt.parse_numeric::<u16>()?,
1003 "device" => params.device_id = opt.parse_numeric::<u16>()?,
1004 "class" => {
1005 let class = opt.parse_numeric::<u32>()?;
1006 params.class = PciClassCode::try_from((class >> 16) as u8)
1007 .map_err(|_| opt.invalid_value_err(String::from("Unknown class code")))?;
1008 params.subclass = (class >> 8) as u8;
1009 params.programming_interface = class as u8;
1010 }
Daniel Verkamp33f982e2022-01-27 10:48:52 -08001011 "multifunction" => {} // Ignore but allow the multifunction option for compatibility.
Mattias Nisslerde2c6402021-10-21 12:05:29 +00001012 "subsystem_vendor" => params.subsystem_vendor_id = opt.parse_numeric::<u16>()?,
1013 "subsystem_device" => params.subsystem_device_id = opt.parse_numeric::<u16>()?,
1014 "revision" => params.revision_id = opt.parse_numeric::<u8>()?,
1015 _ => return Err(opt.invalid_key_err()),
1016 }
1017 }
1018
1019 Ok(params)
1020}
1021
Mattias Nisslerbbd91d02021-12-07 08:57:45 +00001022fn parse_file_backed_mapping(s: Option<&str>) -> argument::Result<FileBackedMappingParameters> {
1023 let s = s.ok_or(argument::Error::ExpectedValue(String::from(
1024 "file-backed-mapping: memory mapping option value required",
1025 )))?;
1026
1027 let mut address = None;
1028 let mut size = None;
1029 let mut path = None;
1030 let mut offset = None;
1031 let mut writable = false;
1032 let mut sync = false;
1033 let mut align = false;
1034 for opt in argument::parse_key_value_options("file-backed-mapping", s, ',') {
1035 match opt.key() {
1036 "addr" => address = Some(opt.parse_numeric::<u64>()?),
1037 "size" => size = Some(opt.parse_numeric::<u64>()?),
1038 "path" => path = Some(PathBuf::from(opt.value()?)),
1039 "offset" => offset = Some(opt.parse_numeric::<u64>()?),
1040 "ro" => writable = !opt.parse_or::<bool>(true)?,
1041 "rw" => writable = opt.parse_or::<bool>(true)?,
1042 "sync" => sync = opt.parse_or::<bool>(true)?,
1043 "align" => align = opt.parse_or::<bool>(true)?,
1044 _ => return Err(opt.invalid_key_err()),
1045 }
1046 }
1047
1048 let (address, path, size) = match (address, path, size) {
1049 (Some(a), Some(p), Some(s)) => (a, p, s),
1050 _ => {
1051 return Err(argument::Error::ExpectedValue(String::from(
1052 "file-backed-mapping: address, size, and path parameters are required",
1053 )))
1054 }
1055 };
1056
1057 let pagesize_mask = pagesize() as u64 - 1;
1058 let aligned_address = address & !pagesize_mask;
1059 let aligned_size = ((address + size + pagesize_mask) & !pagesize_mask) - aligned_address;
1060
1061 if !align && (aligned_address != address || aligned_size != size) {
1062 return Err(argument::Error::InvalidValue {
1063 value: s.to_owned(),
1064 expected: String::from("addr and size parameters must be page size aligned"),
1065 });
1066 }
1067
1068 Ok(FileBackedMappingParameters {
1069 address: aligned_address,
1070 size: aligned_size,
1071 path,
1072 offset: offset.unwrap_or(0),
1073 writable,
1074 sync,
1075 })
1076}
1077
Zach Reiznerefe95782017-08-26 18:05:48 -07001078fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::Result<()> {
1079 match name {
1080 "" => {
Cody Schuffelen6d1ab502019-05-21 12:12:38 -07001081 if cfg.executable_path.is_some() {
1082 return Err(argument::Error::TooManyArguments(format!(
1083 "A VM executable was already specified: {:?}",
1084 cfg.executable_path
1085 )));
Zach Reiznerefe95782017-08-26 18:05:48 -07001086 }
Cody Schuffelen6d1ab502019-05-21 12:12:38 -07001087 let kernel_path = PathBuf::from(value.unwrap());
1088 if !kernel_path.exists() {
1089 return Err(argument::Error::InvalidValue {
1090 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001091 expected: String::from("this kernel path does not exist"),
Cody Schuffelen6d1ab502019-05-21 12:12:38 -07001092 });
1093 }
1094 cfg.executable_path = Some(Executable::Kernel(kernel_path));
Zach Reiznerefe95782017-08-26 18:05:48 -07001095 }
Christian Blichmann33d56772021-03-04 19:03:54 +01001096 "kvm-device" => {
1097 let kvm_device_path = PathBuf::from(value.unwrap());
1098 if !kvm_device_path.exists() {
1099 return Err(argument::Error::InvalidValue {
1100 value: value.unwrap().to_owned(),
1101 expected: String::from("this kvm device path does not exist"),
1102 });
1103 }
1104
1105 cfg.kvm_device_path = kvm_device_path;
1106 }
Christian Blichmann50f95912021-11-05 16:59:39 +01001107 "vhost-vsock-fd" => {
1108 if cfg.vhost_vsock_device.is_some() {
1109 return Err(argument::Error::InvalidValue {
1110 value: value.unwrap().to_owned(),
1111 expected: String::from("A vhost-vsock device was already specified"),
1112 });
1113 }
1114 cfg.vhost_vsock_device = Some(VhostVsockDeviceParameter::Fd(
1115 value
1116 .unwrap()
1117 .parse()
1118 .map_err(|_| argument::Error::InvalidValue {
1119 value: value.unwrap().to_owned(),
1120 expected: String::from(
1121 "this value for `vhost-vsock-fd` needs to be integer",
1122 ),
1123 })?,
1124 ));
1125 }
Christian Blichmann2f5d4b62021-03-10 18:08:08 +01001126 "vhost-vsock-device" => {
Christian Blichmann50f95912021-11-05 16:59:39 +01001127 if cfg.vhost_vsock_device.is_some() {
1128 return Err(argument::Error::InvalidValue {
1129 value: value.unwrap().to_owned(),
1130 expected: String::from("A vhost-vsock device was already specified"),
1131 });
1132 }
Christian Blichmann2f5d4b62021-03-10 18:08:08 +01001133 let vhost_vsock_device_path = PathBuf::from(value.unwrap());
1134 if !vhost_vsock_device_path.exists() {
1135 return Err(argument::Error::InvalidValue {
1136 value: value.unwrap().to_owned(),
1137 expected: String::from("this vhost-vsock device path does not exist"),
1138 });
1139 }
1140
Christian Blichmann50f95912021-11-05 16:59:39 +01001141 cfg.vhost_vsock_device = Some(VhostVsockDeviceParameter::Path(vhost_vsock_device_path));
Christian Blichmann2f5d4b62021-03-10 18:08:08 +01001142 }
1143 "vhost-net-device" => {
1144 let vhost_net_device_path = PathBuf::from(value.unwrap());
1145 if !vhost_net_device_path.exists() {
1146 return Err(argument::Error::InvalidValue {
1147 value: value.unwrap().to_owned(),
1148 expected: String::from("this vhost-vsock device path does not exist"),
1149 });
1150 }
1151
1152 cfg.vhost_net_device_path = vhost_net_device_path;
1153 }
Tristan Muntsinger4133b012018-12-21 16:01:56 -08001154 "android-fstab" => {
1155 if cfg.android_fstab.is_some()
1156 && !cfg.android_fstab.as_ref().unwrap().as_os_str().is_empty()
1157 {
1158 return Err(argument::Error::TooManyArguments(
1159 "expected exactly one android fstab path".to_owned(),
1160 ));
1161 } else {
1162 let android_fstab = PathBuf::from(value.unwrap());
1163 if !android_fstab.exists() {
1164 return Err(argument::Error::InvalidValue {
1165 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001166 expected: String::from("this android fstab path does not exist"),
Tristan Muntsinger4133b012018-12-21 16:01:56 -08001167 });
1168 }
1169 cfg.android_fstab = Some(android_fstab);
1170 }
1171 }
Zach Reiznerefe95782017-08-26 18:05:48 -07001172 "params" => {
Zach Reiznerbb678712018-01-30 18:13:04 -08001173 cfg.params.push(value.unwrap().to_owned());
Zach Reiznerefe95782017-08-26 18:05:48 -07001174 }
1175 "cpus" => {
1176 if cfg.vcpu_count.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -07001177 return Err(argument::Error::TooManyArguments(
1178 "`cpus` already given".to_owned(),
1179 ));
Zach Reiznerefe95782017-08-26 18:05:48 -07001180 }
1181 cfg.vcpu_count =
Zach Reizner55a9e502018-10-03 10:22:32 -07001182 Some(
1183 value
1184 .unwrap()
1185 .parse()
1186 .map_err(|_| argument::Error::InvalidValue {
1187 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001188 expected: String::from("this value for `cpus` needs to be integer"),
Zach Reizner55a9e502018-10-03 10:22:32 -07001189 })?,
1190 )
Zach Reiznerefe95782017-08-26 18:05:48 -07001191 }
Daniel Verkamp107edb32019-04-05 09:58:48 -07001192 "cpu-affinity" => {
Daniel Verkampc677fb42020-09-08 13:47:49 -07001193 if cfg.vcpu_affinity.is_some() {
Daniel Verkamp107edb32019-04-05 09:58:48 -07001194 return Err(argument::Error::TooManyArguments(
1195 "`cpu-affinity` already given".to_owned(),
1196 ));
1197 }
Daniel Verkampc677fb42020-09-08 13:47:49 -07001198 cfg.vcpu_affinity = Some(parse_cpu_affinity(value.unwrap())?);
Daniel Verkamp107edb32019-04-05 09:58:48 -07001199 }
Daniel Verkamp8a72afc2021-03-15 17:55:52 -07001200 "cpu-cluster" => {
1201 cfg.cpu_clusters.push(parse_cpu_set(value.unwrap())?);
1202 }
1203 "cpu-capacity" => {
1204 parse_cpu_capacity(value.unwrap(), &mut cfg.cpu_capacity)?;
1205 }
Yusuke Sato31e136a2021-08-18 11:51:38 -07001206 "per-vm-core-scheduling" => {
1207 cfg.per_vm_core_scheduling = true;
1208 }
Vineeth Pillai2b6855e2022-01-12 16:57:22 +00001209 "vcpu-cgroup-path" => {
1210 let vcpu_cgroup_path = PathBuf::from(value.unwrap());
1211 if !vcpu_cgroup_path.exists() {
1212 return Err(argument::Error::InvalidValue {
1213 value: value.unwrap().to_owned(),
1214 expected: String::from("This vcpu_cgroup_path path does not exist"),
1215 });
1216 }
1217
1218 cfg.vcpu_cgroup_path = Some(vcpu_cgroup_path);
1219 }
Woody Chow737ff122021-03-22 17:49:57 +09001220 #[cfg(feature = "audio_cras")]
1221 "cras-snd" => {
Chih-Yang Hsia41dc04f2021-12-08 16:04:23 +08001222 cfg.cras_snds.push(
Woody Chow0b2b6062021-09-03 15:40:02 +09001223 value
1224 .unwrap()
1225 .parse()
1226 .map_err(|e: CrasSndError| argument::Error::Syntax(e.to_string()))?,
1227 );
Woody Chow737ff122021-03-22 17:49:57 +09001228 }
Suleiman Souhlal015c3c12020-10-07 14:15:41 +09001229 "no-smt" => {
1230 cfg.no_smt = true;
1231 }
Kansho Nishidaab205af2020-08-13 18:17:50 +09001232 "rt-cpus" => {
1233 if !cfg.rt_cpus.is_empty() {
1234 return Err(argument::Error::TooManyArguments(
1235 "`rt-cpus` already given".to_owned(),
1236 ));
1237 }
1238 cfg.rt_cpus = parse_cpu_set(value.unwrap())?;
1239 }
Suleiman Souhlal63630e82021-02-18 11:53:11 +09001240 "delay-rt" => {
1241 cfg.delay_rt = true;
1242 }
Zach Reiznerefe95782017-08-26 18:05:48 -07001243 "mem" => {
1244 if cfg.memory.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -07001245 return Err(argument::Error::TooManyArguments(
1246 "`mem` already given".to_owned(),
1247 ));
Zach Reiznerefe95782017-08-26 18:05:48 -07001248 }
1249 cfg.memory =
Zach Reizner55a9e502018-10-03 10:22:32 -07001250 Some(
1251 value
1252 .unwrap()
1253 .parse()
1254 .map_err(|_| argument::Error::InvalidValue {
1255 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001256 expected: String::from("this value for `mem` needs to be integer"),
Zach Reizner55a9e502018-10-03 10:22:32 -07001257 })?,
1258 )
Zach Reiznerefe95782017-08-26 18:05:48 -07001259 }
Will Deaconc48e7832021-07-30 19:03:06 +01001260 #[cfg(target_arch = "aarch64")]
1261 "swiotlb" => {
1262 if cfg.swiotlb.is_some() {
1263 return Err(argument::Error::TooManyArguments(
1264 "`swiotlb` already given".to_owned(),
1265 ));
1266 }
1267 cfg.swiotlb =
1268 Some(
1269 value
1270 .unwrap()
1271 .parse()
1272 .map_err(|_| argument::Error::InvalidValue {
1273 value: value.unwrap().to_owned(),
1274 expected: String::from("this value for `swiotlb` needs to be integer"),
1275 })?,
1276 )
1277 }
Sergey Senozhatsky1e369c52021-04-13 20:23:51 +09001278 "hugepages" => {
1279 cfg.hugepages = true;
1280 }
Andrew Scull1590e6f2020-03-18 18:00:47 +00001281 #[cfg(feature = "audio")]
Judy Hsiaod5c1e962020-02-04 12:30:01 +08001282 "ac97" => {
1283 let ac97_params = parse_ac97_options(value.unwrap())?;
paulhsia16478242020-11-27 14:04:58 +08001284 // Add kernel parameters related to the intel8x0 driver for ac97 devices once.
1285 if cfg.ac97_parameters.is_empty() {
1286 // Set `inside_vm=1` to save some register read ops in the driver.
1287 cfg.params.push("snd_intel8x0.inside_vm=1".to_string());
1288 // Set `ac97_clock=48000` to save intel8x0_measure_ac97_clock call in the driver.
1289 cfg.params.push("snd_intel8x0.ac97_clock=48000".to_string());
1290 }
Judy Hsiaod5c1e962020-02-04 12:30:01 +08001291 cfg.ac97_parameters.push(ac97_params);
Dylan Reid3082e8e2019-01-07 10:33:48 -08001292 }
Jorge E. Moreirad4562d02021-06-28 16:21:12 -07001293 #[cfg(feature = "audio")]
1294 "sound" => {
1295 let client_path = PathBuf::from(value.unwrap());
1296 cfg.sound = Some(client_path);
1297 }
Trent Begin17ccaad2019-04-17 13:51:25 -06001298 "serial" => {
1299 let serial_params = parse_serial_options(value.unwrap())?;
1300 let num = serial_params.num;
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07001301 let key = (serial_params.hardware, num);
1302 if cfg.serial_parameters.contains_key(&key) {
Trent Begin17ccaad2019-04-17 13:51:25 -06001303 return Err(argument::Error::TooManyArguments(format!(
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07001304 "serial hardware {} num {}",
1305 serial_params.hardware, num,
Trent Begin17ccaad2019-04-17 13:51:25 -06001306 )));
1307 }
1308
1309 if serial_params.console {
Jakub Staronb6515a92019-06-05 15:18:25 -07001310 for params in cfg.serial_parameters.values() {
Trent Begin17ccaad2019-04-17 13:51:25 -06001311 if params.console {
1312 return Err(argument::Error::TooManyArguments(format!(
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07001313 "{} device {} already set as console",
1314 params.hardware, params.num,
1315 )));
1316 }
1317 }
1318 }
1319
1320 if serial_params.earlycon {
1321 // Only SerialHardware::Serial supports earlycon= currently.
1322 match serial_params.hardware {
1323 SerialHardware::Serial => {}
1324 _ => {
1325 return Err(argument::Error::InvalidValue {
Andrew Walbran9cfdbd92021-01-11 17:40:34 +00001326 value: serial_params.hardware.to_string(),
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07001327 expected: String::from("earlycon not supported for hardware"),
1328 });
1329 }
1330 }
1331 for params in cfg.serial_parameters.values() {
1332 if params.earlycon {
1333 return Err(argument::Error::TooManyArguments(format!(
1334 "{} device {} already set as earlycon",
1335 params.hardware, params.num,
Trent Begin17ccaad2019-04-17 13:51:25 -06001336 )));
1337 }
1338 }
1339 }
1340
Jorge E. Moreira1e262302019-08-01 14:40:03 -07001341 if serial_params.stdin {
1342 if let Some(previous_stdin) = cfg.serial_parameters.values().find(|sp| sp.stdin) {
1343 return Err(argument::Error::TooManyArguments(format!(
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07001344 "{} device {} already connected to standard input",
1345 previous_stdin.hardware, previous_stdin.num,
Jorge E. Moreira1e262302019-08-01 14:40:03 -07001346 )));
1347 }
1348 }
1349
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07001350 cfg.serial_parameters.insert(key, serial_params);
Trent Begin17ccaad2019-04-17 13:51:25 -06001351 }
1352 "syslog-tag" => {
1353 if cfg.syslog_tag.is_some() {
1354 return Err(argument::Error::TooManyArguments(
1355 "`syslog-tag` already given".to_owned(),
1356 ));
1357 }
1358 syslog::set_proc_name(value.unwrap());
1359 cfg.syslog_tag = Some(value.unwrap().to_owned());
1360 }
Daniel Verkamp4b62cd92019-11-08 13:09:27 -08001361 "root" | "rwroot" | "disk" | "rwdisk" => {
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001362 let param = value.unwrap();
1363 let mut components = param.split(',');
Daniel Verkamp6a8cd102019-06-26 15:17:46 -07001364 let read_only = !name.starts_with("rw");
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001365 let disk_path =
1366 PathBuf::from(
1367 components
1368 .next()
1369 .ok_or_else(|| argument::Error::InvalidValue {
1370 value: param.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001371 expected: String::from("missing disk path"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001372 })?,
1373 );
Zach Reiznerefe95782017-08-26 18:05:48 -07001374 if !disk_path.exists() {
1375 return Err(argument::Error::InvalidValue {
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001376 value: param.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001377 expected: String::from("this disk path does not exist"),
Zach Reizner55a9e502018-10-03 10:22:32 -07001378 });
Zach Reiznerefe95782017-08-26 18:05:48 -07001379 }
Daniel Verkamp6a8cd102019-06-26 15:17:46 -07001380 if name.ends_with("root") {
Daniel Verkampaac28132018-10-15 14:58:48 -07001381 if cfg.disks.len() >= 26 {
Zach Reizner55a9e502018-10-03 10:22:32 -07001382 return Err(argument::Error::TooManyArguments(
1383 "ran out of letters for to assign to root disk".to_owned(),
1384 ));
Zach Reiznerefe95782017-08-26 18:05:48 -07001385 }
Zach Reizner55a9e502018-10-03 10:22:32 -07001386 cfg.params.push(format!(
Daniel Verkamp6a8cd102019-06-26 15:17:46 -07001387 "root=/dev/vd{} {}",
1388 char::from(b'a' + cfg.disks.len() as u8),
1389 if read_only { "ro" } else { "rw" }
Zach Reizner55a9e502018-10-03 10:22:32 -07001390 ));
Zach Reiznerefe95782017-08-26 18:05:48 -07001391 }
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001392
1393 let mut disk = DiskOption {
Zach Reizner55a9e502018-10-03 10:22:32 -07001394 path: disk_path,
Daniel Verkamp6a8cd102019-06-26 15:17:46 -07001395 read_only,
Junichi Uekawa7bea39f2021-07-16 14:05:06 +09001396 o_direct: false,
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001397 sparse: true,
Daniel Verkamp27672232019-12-06 17:26:55 +11001398 block_size: 512,
Daniel Verkamp4e1f99a2020-06-01 12:47:21 -07001399 id: None,
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001400 };
1401
1402 for opt in components {
1403 let mut o = opt.splitn(2, '=');
1404 let kind = o.next().ok_or_else(|| argument::Error::InvalidValue {
1405 value: opt.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001406 expected: String::from("disk options must not be empty"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001407 })?;
1408 let value = o.next().ok_or_else(|| argument::Error::InvalidValue {
1409 value: opt.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001410 expected: String::from("disk options must be of the form `kind=value`"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001411 })?;
1412
1413 match kind {
1414 "sparse" => {
1415 let sparse = value.parse().map_err(|_| argument::Error::InvalidValue {
1416 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001417 expected: String::from("`sparse` must be a boolean"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001418 })?;
1419 disk.sparse = sparse;
1420 }
Junichi Uekawa7bea39f2021-07-16 14:05:06 +09001421 "o_direct" => {
1422 let o_direct =
1423 value.parse().map_err(|_| argument::Error::InvalidValue {
1424 value: value.to_owned(),
1425 expected: String::from("`o_direct` must be a boolean"),
1426 })?;
1427 disk.o_direct = o_direct;
1428 }
Daniel Verkamp27672232019-12-06 17:26:55 +11001429 "block_size" => {
1430 let block_size =
1431 value.parse().map_err(|_| argument::Error::InvalidValue {
1432 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001433 expected: String::from("`block_size` must be an integer"),
Daniel Verkamp27672232019-12-06 17:26:55 +11001434 })?;
1435 disk.block_size = block_size;
1436 }
Daniel Verkamp4e1f99a2020-06-01 12:47:21 -07001437 "id" => {
1438 if value.len() > DISK_ID_LEN {
1439 return Err(argument::Error::InvalidValue {
1440 value: value.to_owned(),
1441 expected: format!(
1442 "`id` must be {} or fewer characters",
1443 DISK_ID_LEN
1444 ),
1445 });
1446 }
1447 let mut id = [0u8; DISK_ID_LEN];
1448 // Slicing id to value's length will never panic
1449 // because we checked that value will fit into id above.
1450 id[..value.len()].copy_from_slice(value.as_bytes());
1451 disk.id = Some(id);
1452 }
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001453 _ => {
1454 return Err(argument::Error::InvalidValue {
1455 value: kind.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001456 expected: String::from("unrecognized disk option"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001457 });
1458 }
1459 }
1460 }
1461
1462 cfg.disks.push(disk);
Zach Reiznerefe95782017-08-26 18:05:48 -07001463 }
Jakub Starona3411ea2019-04-24 10:55:25 -07001464 "pmem-device" | "rw-pmem-device" => {
1465 let disk_path = PathBuf::from(value.unwrap());
1466 if !disk_path.exists() {
1467 return Err(argument::Error::InvalidValue {
1468 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001469 expected: String::from("this disk path does not exist"),
Jakub Starona3411ea2019-04-24 10:55:25 -07001470 });
1471 }
1472
1473 cfg.pmem_devices.push(DiskOption {
1474 path: disk_path,
1475 read_only: !name.starts_with("rw"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001476 sparse: false,
Junichi Uekawa7bea39f2021-07-16 14:05:06 +09001477 o_direct: false,
Michael Hoyle6b196952020-08-02 20:09:41 -07001478 block_size: base::pagesize() as u32,
Daniel Verkamp4e1f99a2020-06-01 12:47:21 -07001479 id: None,
Jakub Starona3411ea2019-04-24 10:55:25 -07001480 });
1481 }
Kansho Nishida282115b2019-12-18 13:13:14 +09001482 "pstore" => {
1483 if cfg.pstore.is_some() {
1484 return Err(argument::Error::TooManyArguments(
1485 "`pstore` already given".to_owned(),
1486 ));
1487 }
1488
1489 let value = value.unwrap();
1490 let components: Vec<&str> = value.split(',').collect();
1491 if components.len() != 2 {
1492 return Err(argument::Error::InvalidValue {
1493 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001494 expected: String::from(
1495 "pstore must have exactly 2 components: path=<path>,size=<size>",
1496 ),
Kansho Nishida282115b2019-12-18 13:13:14 +09001497 });
1498 }
1499 cfg.pstore = Some(Pstore {
1500 path: {
1501 if components[0].len() <= 5 || !components[0].starts_with("path=") {
1502 return Err(argument::Error::InvalidValue {
1503 value: components[0].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001504 expected: String::from("pstore path must follow with `path=`"),
Kansho Nishida282115b2019-12-18 13:13:14 +09001505 });
1506 };
1507 PathBuf::from(&components[0][5..])
1508 },
1509 size: {
1510 if components[1].len() <= 5 || !components[1].starts_with("size=") {
1511 return Err(argument::Error::InvalidValue {
1512 value: components[1].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001513 expected: String::from("pstore size must follow with `size=`"),
Kansho Nishida282115b2019-12-18 13:13:14 +09001514 });
1515 };
1516 components[1][5..]
1517 .parse()
1518 .map_err(|_| argument::Error::InvalidValue {
1519 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001520 expected: String::from("pstore size must be an integer"),
Kansho Nishida282115b2019-12-18 13:13:14 +09001521 })?
1522 },
1523 });
1524 }
Zach Reiznerefe95782017-08-26 18:05:48 -07001525 "host_ip" => {
Daniel Verkampaac28132018-10-15 14:58:48 -07001526 if cfg.host_ip.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -07001527 return Err(argument::Error::TooManyArguments(
1528 "`host_ip` already given".to_owned(),
1529 ));
Zach Reiznerefe95782017-08-26 18:05:48 -07001530 }
Daniel Verkampaac28132018-10-15 14:58:48 -07001531 cfg.host_ip =
Zach Reizner55a9e502018-10-03 10:22:32 -07001532 Some(
1533 value
1534 .unwrap()
1535 .parse()
1536 .map_err(|_| argument::Error::InvalidValue {
1537 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001538 expected: String::from("`host_ip` needs to be in the form \"x.x.x.x\""),
Zach Reizner55a9e502018-10-03 10:22:32 -07001539 })?,
1540 )
Zach Reiznerefe95782017-08-26 18:05:48 -07001541 }
1542 "netmask" => {
Daniel Verkampaac28132018-10-15 14:58:48 -07001543 if cfg.netmask.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -07001544 return Err(argument::Error::TooManyArguments(
1545 "`netmask` already given".to_owned(),
1546 ));
Zach Reiznerefe95782017-08-26 18:05:48 -07001547 }
Daniel Verkampaac28132018-10-15 14:58:48 -07001548 cfg.netmask =
Zach Reizner55a9e502018-10-03 10:22:32 -07001549 Some(
1550 value
1551 .unwrap()
1552 .parse()
1553 .map_err(|_| argument::Error::InvalidValue {
1554 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001555 expected: String::from("`netmask` needs to be in the form \"x.x.x.x\""),
Zach Reizner55a9e502018-10-03 10:22:32 -07001556 })?,
1557 )
Zach Reiznerefe95782017-08-26 18:05:48 -07001558 }
1559 "mac" => {
Daniel Verkampaac28132018-10-15 14:58:48 -07001560 if cfg.mac_address.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -07001561 return Err(argument::Error::TooManyArguments(
1562 "`mac` already given".to_owned(),
1563 ));
Zach Reiznerefe95782017-08-26 18:05:48 -07001564 }
Daniel Verkampaac28132018-10-15 14:58:48 -07001565 cfg.mac_address =
Zach Reizner55a9e502018-10-03 10:22:32 -07001566 Some(
1567 value
1568 .unwrap()
1569 .parse()
1570 .map_err(|_| argument::Error::InvalidValue {
1571 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001572 expected: String::from(
1573 "`mac` needs to be in the form \"XX:XX:XX:XX:XX:XX\"",
1574 ),
Zach Reizner55a9e502018-10-03 10:22:32 -07001575 })?,
1576 )
Zach Reiznerefe95782017-08-26 18:05:48 -07001577 }
Xiong Zhang773c7072020-03-20 10:39:55 +08001578 "net-vq-pairs" => {
1579 if cfg.net_vq_pairs.is_some() {
1580 return Err(argument::Error::TooManyArguments(
1581 "`net-vq-pairs` already given".to_owned(),
1582 ));
1583 }
1584 cfg.net_vq_pairs =
1585 Some(
1586 value
1587 .unwrap()
1588 .parse()
1589 .map_err(|_| argument::Error::InvalidValue {
1590 value: value.unwrap().to_owned(),
1591 expected: String::from(
1592 "this value for `net-vq-pairs` needs to be integer",
1593 ),
1594 })?,
1595 )
1596 }
1597
Stephen Barber28a5a612017-10-20 17:15:30 -07001598 "wayland-sock" => {
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09001599 let mut components = value.unwrap().split(',');
1600 let path =
1601 PathBuf::from(
1602 components
1603 .next()
1604 .ok_or_else(|| argument::Error::InvalidValue {
1605 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001606 expected: String::from("missing socket path"),
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09001607 })?,
1608 );
1609 let mut name = "";
1610 for c in components {
1611 let mut kv = c.splitn(2, '=');
1612 let (kind, value) = match (kv.next(), kv.next()) {
1613 (Some(kind), Some(value)) => (kind, value),
1614 _ => {
1615 return Err(argument::Error::InvalidValue {
1616 value: c.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001617 expected: String::from("option must be of the form `kind=value`"),
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09001618 })
1619 }
1620 };
1621 match kind {
1622 "name" => name = value,
1623 _ => {
1624 return Err(argument::Error::InvalidValue {
1625 value: kind.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001626 expected: String::from("unrecognized option"),
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09001627 })
1628 }
1629 }
Stephen Barber28a5a612017-10-20 17:15:30 -07001630 }
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09001631 if cfg.wayland_socket_paths.contains_key(name) {
1632 return Err(argument::Error::TooManyArguments(format!(
1633 "wayland socket name already used: '{}'",
1634 name
1635 )));
Stephen Barber28a5a612017-10-20 17:15:30 -07001636 }
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09001637 cfg.wayland_socket_paths.insert(name.to_string(), path);
Stephen Barber28a5a612017-10-20 17:15:30 -07001638 }
David Reveman52ba4e52018-04-22 21:42:09 -04001639 #[cfg(feature = "wl-dmabuf")]
Nicholas Vernefde29972021-07-06 22:02:05 +10001640 "wayland-dmabuf" => {}
Zach Reizner0f2cfb02019-06-19 17:46:03 -07001641 "x-display" => {
1642 if cfg.x_display.is_some() {
1643 return Err(argument::Error::TooManyArguments(
1644 "`x-display` already given".to_owned(),
1645 ));
1646 }
1647 cfg.x_display = Some(value.unwrap().to_owned());
1648 }
Zach Reizner65b98f12019-11-22 17:34:58 -08001649 "display-window-keyboard" => {
1650 cfg.display_window_keyboard = true;
1651 }
1652 "display-window-mouse" => {
1653 cfg.display_window_mouse = true;
1654 }
Zach Reiznerefe95782017-08-26 18:05:48 -07001655 "socket" => {
1656 if cfg.socket_path.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -07001657 return Err(argument::Error::TooManyArguments(
1658 "`socket` already given".to_owned(),
1659 ));
Zach Reiznerefe95782017-08-26 18:05:48 -07001660 }
1661 let mut socket_path = PathBuf::from(value.unwrap());
1662 if socket_path.is_dir() {
1663 socket_path.push(format!("crosvm-{}.sock", getpid()));
1664 }
1665 if socket_path.exists() {
1666 return Err(argument::Error::InvalidValue {
Zach Reizner55a9e502018-10-03 10:22:32 -07001667 value: socket_path.to_string_lossy().into_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001668 expected: String::from("this socket path already exists"),
Zach Reizner55a9e502018-10-03 10:22:32 -07001669 });
Zach Reiznerefe95782017-08-26 18:05:48 -07001670 }
1671 cfg.socket_path = Some(socket_path);
1672 }
David Stevens8be9ef02022-01-13 22:50:24 +09001673 "balloon-control" => {
1674 if cfg.balloon_control.is_some() {
1675 return Err(argument::Error::TooManyArguments(
1676 "`balloon-control` already given".to_owned(),
1677 ));
1678 }
1679 let path = PathBuf::from(value.unwrap());
1680 if path.is_dir() || !path.exists() {
1681 return Err(argument::Error::InvalidValue {
1682 value: path.to_string_lossy().into_owned(),
1683 expected: String::from("path is directory or missing"),
1684 });
1685 }
1686 cfg.balloon_control = Some(path);
1687 }
Dylan Reidd0c9adc2017-10-02 19:04:50 -07001688 "disable-sandbox" => {
Lepton Wu9105e9f2019-03-14 11:38:31 -07001689 cfg.sandbox = false;
Dylan Reidd0c9adc2017-10-02 19:04:50 -07001690 }
Chirantan Ekbote88f9cba2017-08-28 09:51:18 -07001691 "cid" => {
Daniel Verkampaac28132018-10-15 14:58:48 -07001692 if cfg.cid.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -07001693 return Err(argument::Error::TooManyArguments(
1694 "`cid` alread given".to_owned(),
1695 ));
Chirantan Ekbote88f9cba2017-08-28 09:51:18 -07001696 }
Daniel Verkampaac28132018-10-15 14:58:48 -07001697 cfg.cid = Some(
1698 value
1699 .unwrap()
1700 .parse()
1701 .map_err(|_| argument::Error::InvalidValue {
1702 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001703 expected: String::from("this value for `cid` must be an unsigned integer"),
Daniel Verkampaac28132018-10-15 14:58:48 -07001704 })?,
1705 );
Chirantan Ekbote88f9cba2017-08-28 09:51:18 -07001706 }
Chirantan Ekboteebd56812018-04-16 19:32:04 -07001707 "shared-dir" => {
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001708 // This is formatted as multiple fields, each separated by ":". The first 2 fields are
1709 // fixed (src:tag). The rest may appear in any order:
1710 //
1711 // * type=TYPE - must be one of "p9" or "fs" (default: p9)
1712 // * uidmap=UIDMAP - a uid map in the format "inner outer count[,inner outer count]"
1713 // (default: "0 <current euid> 1")
1714 // * gidmap=GIDMAP - a gid map in the same format as uidmap
1715 // (default: "0 <current egid> 1")
Ryo Hashimoto6d924382021-06-21 20:59:04 +09001716 // * privileged_quota_uids=UIDS - Space-separated list of privileged uid values. When
1717 // performing quota-related operations, these UIDs are treated as if they have
1718 // CAP_FOWNER.
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001719 // * timeout=TIMEOUT - a timeout value in seconds, which indicates how long attributes
1720 // and directory contents should be considered valid (default: 5)
1721 // * cache=CACHE - one of "never", "always", or "auto" (default: auto)
1722 // * writeback=BOOL - indicates whether writeback caching should be enabled (default: false)
Chirantan Ekboteebd56812018-04-16 19:32:04 -07001723 let param = value.unwrap();
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001724 let mut components = param.split(':');
Zach Reizner55a9e502018-10-03 10:22:32 -07001725 let src =
1726 PathBuf::from(
1727 components
1728 .next()
1729 .ok_or_else(|| argument::Error::InvalidValue {
1730 value: param.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001731 expected: String::from("missing source path for `shared-dir`"),
Zach Reizner55a9e502018-10-03 10:22:32 -07001732 })?,
1733 );
1734 let tag = components
1735 .next()
1736 .ok_or_else(|| argument::Error::InvalidValue {
Chirantan Ekboteebd56812018-04-16 19:32:04 -07001737 value: param.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001738 expected: String::from("missing tag for `shared-dir`"),
David Tolnay2bac1e72018-12-12 14:33:42 -08001739 })?
1740 .to_owned();
Chirantan Ekboteebd56812018-04-16 19:32:04 -07001741
1742 if !src.is_dir() {
1743 return Err(argument::Error::InvalidValue {
1744 value: param.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001745 expected: String::from("source path for `shared-dir` must be a directory"),
Chirantan Ekboteebd56812018-04-16 19:32:04 -07001746 });
1747 }
1748
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001749 let mut shared_dir = SharedDir {
1750 src,
1751 tag,
1752 ..Default::default()
1753 };
1754 for opt in components {
1755 let mut o = opt.splitn(2, '=');
1756 let kind = o.next().ok_or_else(|| argument::Error::InvalidValue {
1757 value: opt.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001758 expected: String::from("`shared-dir` options must not be empty"),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001759 })?;
1760 let value = o.next().ok_or_else(|| argument::Error::InvalidValue {
1761 value: opt.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001762 expected: String::from("`shared-dir` options must be of the form `kind=value`"),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001763 })?;
1764
1765 match kind {
1766 "type" => {
1767 shared_dir.kind =
1768 value.parse().map_err(|_| argument::Error::InvalidValue {
1769 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001770 expected: String::from("`type` must be one of `fs` or `9p`"),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001771 })?
1772 }
1773 "uidmap" => shared_dir.uid_map = value.into(),
1774 "gidmap" => shared_dir.gid_map = value.into(),
Ryo Hashimoto6d924382021-06-21 20:59:04 +09001775 #[cfg(feature = "chromeos")]
1776 "privileged_quota_uids" => {
1777 shared_dir.fs_cfg.privileged_quota_uids =
1778 value.split(' ').map(|s| s.parse().unwrap()).collect();
1779 }
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001780 "timeout" => {
1781 let seconds = value.parse().map_err(|_| argument::Error::InvalidValue {
1782 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001783 expected: String::from("`timeout` must be an integer"),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001784 })?;
1785
1786 let dur = Duration::from_secs(seconds);
Andrew Walbran9cfdbd92021-01-11 17:40:34 +00001787 shared_dir.fs_cfg.entry_timeout = dur;
Chirantan Ekbote75ba8752020-10-27 18:33:02 +09001788 shared_dir.fs_cfg.attr_timeout = dur;
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001789 }
1790 "cache" => {
1791 let policy = value.parse().map_err(|_| argument::Error::InvalidValue {
1792 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001793 expected: String::from(
1794 "`cache` must be one of `never`, `always`, or `auto`",
1795 ),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001796 })?;
Chirantan Ekbote75ba8752020-10-27 18:33:02 +09001797 shared_dir.fs_cfg.cache_policy = policy;
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001798 }
1799 "writeback" => {
1800 let writeback =
1801 value.parse().map_err(|_| argument::Error::InvalidValue {
1802 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001803 expected: String::from("`writeback` must be a boolean"),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001804 })?;
Chirantan Ekbote75ba8752020-10-27 18:33:02 +09001805 shared_dir.fs_cfg.writeback = writeback;
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001806 }
Chirantan Ekboted994e512020-06-12 18:46:52 +09001807 "rewrite-security-xattrs" => {
1808 let rewrite_security_xattrs =
1809 value.parse().map_err(|_| argument::Error::InvalidValue {
1810 value: value.to_owned(),
1811 expected: String::from(
1812 "`rewrite-security-xattrs` must be a boolean",
1813 ),
1814 })?;
Chirantan Ekbote75ba8752020-10-27 18:33:02 +09001815 shared_dir.fs_cfg.rewrite_security_xattrs = rewrite_security_xattrs;
Chirantan Ekboted994e512020-06-12 18:46:52 +09001816 }
Chirantan Ekbote09357c82020-09-10 20:08:28 +09001817 "ascii_casefold" => {
1818 let ascii_casefold =
1819 value.parse().map_err(|_| argument::Error::InvalidValue {
1820 value: value.to_owned(),
1821 expected: String::from("`ascii_casefold` must be a boolean"),
1822 })?;
Chirantan Ekbote75ba8752020-10-27 18:33:02 +09001823 shared_dir.fs_cfg.ascii_casefold = ascii_casefold;
1824 shared_dir.p9_cfg.ascii_casefold = ascii_casefold;
Chirantan Ekbote09357c82020-09-10 20:08:28 +09001825 }
Chirantan Ekbote1b2d8dc2021-09-21 17:25:29 +09001826 "dax" => {
1827 let use_dax = value.parse().map_err(|_| argument::Error::InvalidValue {
1828 value: value.to_owned(),
1829 expected: String::from("`dax` must be a boolean"),
1830 })?;
1831 shared_dir.fs_cfg.use_dax = use_dax;
1832 }
Chirantan Ekbotef1cd8d72021-09-21 17:33:58 +09001833 "posix_acl" => {
1834 let posix_acl =
1835 value.parse().map_err(|_| argument::Error::InvalidValue {
1836 value: value.to_owned(),
1837 expected: String::from("`posix_acl` must be a boolean"),
1838 })?;
1839 shared_dir.fs_cfg.posix_acl = posix_acl;
1840 }
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001841 _ => {
1842 return Err(argument::Error::InvalidValue {
1843 value: kind.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001844 expected: String::from("unrecognized option for `shared-dir`"),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001845 })
1846 }
1847 }
1848 }
1849 cfg.shared_dirs.push(shared_dir);
Chirantan Ekboteebd56812018-04-16 19:32:04 -07001850 }
Dylan Reide026ef02017-10-02 19:03:52 -07001851 "seccomp-policy-dir" => {
Dylan Reidd0c9adc2017-10-02 19:04:50 -07001852 // `value` is Some because we are in this match so it's safe to unwrap.
Daniel Verkampaac28132018-10-15 14:58:48 -07001853 cfg.seccomp_policy_dir = PathBuf::from(value.unwrap());
Zach Reizner55a9e502018-10-03 10:22:32 -07001854 }
Zach Reizner44863792019-06-26 14:22:08 -07001855 "seccomp-log-failures" => {
Matt Delco45caf912019-11-13 08:11:09 -08001856 // A side-effect of this flag is to force the use of .policy files
1857 // instead of .bpf files (.bpf files are expected and assumed to be
1858 // compiled to fail an unpermitted action with "trap").
1859 // Normally crosvm will first attempt to use a .bpf file, and if
1860 // not present it will then try to use a .policy file. It's up
1861 // to the build to decide which of these files is present for
1862 // crosvm to use (for CrOS the build will use .bpf files for
1863 // x64 builds and .policy files for arm/arm64 builds).
1864 //
1865 // This flag will likely work as expected for builds that use
1866 // .policy files. For builds that only use .bpf files the initial
1867 // result when using this flag is likely to be a file-not-found
1868 // error (since the .policy files are not present).
1869 // For .bpf builds you can either 1) manually add the .policy files,
1870 // or 2) do not use this command-line parameter and instead
1871 // temporarily change the build by passing "log" rather than
1872 // "trap" as the "--default-action" to compile_seccomp_policy.py.
Zach Reizner44863792019-06-26 14:22:08 -07001873 cfg.seccomp_log_failures = true;
1874 }
Zach Reizner8864cb02018-01-16 17:59:03 -08001875 "plugin" => {
Cody Schuffelen6d1ab502019-05-21 12:12:38 -07001876 if cfg.executable_path.is_some() {
1877 return Err(argument::Error::TooManyArguments(format!(
1878 "A VM executable was already specified: {:?}",
1879 cfg.executable_path
1880 )));
Zach Reizner8864cb02018-01-16 17:59:03 -08001881 }
Zach Reiznercc30d582018-01-23 21:16:42 -08001882 let plugin = PathBuf::from(value.unwrap().to_owned());
1883 if plugin.is_relative() {
1884 return Err(argument::Error::InvalidValue {
Zach Reizner55a9e502018-10-03 10:22:32 -07001885 value: plugin.to_string_lossy().into_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001886 expected: String::from("the plugin path must be an absolute path"),
Zach Reizner55a9e502018-10-03 10:22:32 -07001887 });
Zach Reiznercc30d582018-01-23 21:16:42 -08001888 }
Cody Schuffelen6d1ab502019-05-21 12:12:38 -07001889 cfg.executable_path = Some(Executable::Plugin(plugin));
Zach Reizner55a9e502018-10-03 10:22:32 -07001890 }
Dmitry Torokhov0f1770d2018-05-11 10:57:16 -07001891 "plugin-root" => {
1892 cfg.plugin_root = Some(PathBuf::from(value.unwrap().to_owned()));
Zach Reizner55a9e502018-10-03 10:22:32 -07001893 }
Chirantan Ekboted41d7262018-11-16 16:37:45 -08001894 "plugin-mount" => {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -08001895 let mount = parse_plugin_mount_option(value.unwrap())?;
1896 cfg.plugin_mounts.push(mount);
Chirantan Ekboted41d7262018-11-16 16:37:45 -08001897 }
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08001898 "plugin-mount-file" => {
1899 let file = File::open(value.unwrap()).map_err(|_| argument::Error::InvalidValue {
1900 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001901 expected: String::from("unable to open `plugin-mount-file` file"),
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08001902 })?;
1903 let reader = BufReader::new(file);
1904 for l in reader.lines() {
1905 let line = l.unwrap();
Anton Romanovcb3cabe2022-02-03 03:21:33 +00001906 let trimmed_line = line.split_once('#').map_or(&*line, |x| x.0).trim();
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08001907 if !trimmed_line.is_empty() {
1908 let mount = parse_plugin_mount_option(trimmed_line)?;
1909 cfg.plugin_mounts.push(mount);
1910 }
1911 }
1912 }
Dmitry Torokhov1a6262b2019-03-01 00:34:03 -08001913 "plugin-gid-map" => {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -08001914 let map = parse_plugin_gid_map_option(value.unwrap())?;
1915 cfg.plugin_gid_maps.push(map);
Dmitry Torokhov1a6262b2019-03-01 00:34:03 -08001916 }
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08001917 "plugin-gid-map-file" => {
1918 let file = File::open(value.unwrap()).map_err(|_| argument::Error::InvalidValue {
1919 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001920 expected: String::from("unable to open `plugin-gid-map-file` file"),
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08001921 })?;
1922 let reader = BufReader::new(file);
1923 for l in reader.lines() {
1924 let line = l.unwrap();
Anton Romanovcb3cabe2022-02-03 03:21:33 +00001925 let trimmed_line = line.split_once('#').map_or(&*line, |x| x.0).trim();
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08001926 if !trimmed_line.is_empty() {
1927 let map = parse_plugin_gid_map_option(trimmed_line)?;
1928 cfg.plugin_gid_maps.push(map);
1929 }
1930 }
1931 }
Daniel Verkampaac28132018-10-15 14:58:48 -07001932 "vhost-net" => cfg.vhost_net = true,
Chirantan Ekbote5f787212018-05-31 15:31:31 -07001933 "tap-fd" => {
Jorge E. Moreirab7952802019-02-12 16:43:05 -08001934 cfg.tap_fd.push(
1935 value
1936 .unwrap()
1937 .parse()
1938 .map_err(|_| argument::Error::InvalidValue {
1939 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001940 expected: String::from(
1941 "this value for `tap-fd` must be an unsigned integer",
1942 ),
Jorge E. Moreirab7952802019-02-12 16:43:05 -08001943 })?,
1944 );
Chirantan Ekbote5f787212018-05-31 15:31:31 -07001945 }
Alexandre Courbot993aa7f2021-12-09 14:51:29 +09001946 "tap-name" => {
1947 cfg.tap_name.push(value.unwrap().to_owned());
1948 }
Jason Macnakcc7070b2019-11-06 14:48:12 -08001949 #[cfg(feature = "gpu")]
Zach Reizner3a8100a2017-09-13 19:15:43 -07001950 "gpu" => {
Chia-I Wu16fb6592021-11-10 11:45:32 -08001951 let gpu_parameters = cfg.gpu_parameters.get_or_insert_with(Default::default);
1952 parse_gpu_options(value, gpu_parameters)?;
Jason Macnakd659a0d2021-03-15 15:33:01 -07001953 }
1954 #[cfg(feature = "gpu")]
1955 "gpu-display" => {
Chia-I Wu16fb6592021-11-10 11:45:32 -08001956 let gpu_parameters = cfg.gpu_parameters.get_or_insert_with(Default::default);
1957 parse_gpu_display_options(value, gpu_parameters)?;
1958 }
1959 #[cfg(all(feature = "gpu", feature = "virgl_renderer_next"))]
1960 "gpu-render-server" => {
Dmitry Torokhove464a7a2022-01-26 13:29:36 -08001961 cfg.gpu_render_server_parameters = Some(parse_gpu_render_server_options(value)?);
Zach Reizner3a8100a2017-09-13 19:15:43 -07001962 }
David Tolnay43f8e212019-02-13 17:28:16 -08001963 "software-tpm" => {
1964 cfg.software_tpm = true;
1965 }
Jorge E. Moreira99d3f082019-03-07 10:59:54 -08001966 "single-touch" => {
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09001967 let mut it = value.unwrap().split(':');
Jorge E. Moreira99d3f082019-03-07 10:59:54 -08001968
1969 let mut single_touch_spec =
1970 TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned()));
1971 if let Some(width) = it.next() {
Kaiyi Libccb4eb2020-02-06 17:53:11 -08001972 single_touch_spec.set_width(width.trim().parse().unwrap());
Jorge E. Moreira99d3f082019-03-07 10:59:54 -08001973 }
1974 if let Some(height) = it.next() {
Kaiyi Libccb4eb2020-02-06 17:53:11 -08001975 single_touch_spec.set_height(height.trim().parse().unwrap());
Jorge E. Moreira99d3f082019-03-07 10:59:54 -08001976 }
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07001977 cfg.virtio_single_touch.push(single_touch_spec);
Jorge E. Moreira99d3f082019-03-07 10:59:54 -08001978 }
Tristan Muntsinger486cffc2020-09-29 22:05:41 +00001979 "multi-touch" => {
Tristan Muntsinger486cffc2020-09-29 22:05:41 +00001980 let mut it = value.unwrap().split(':');
1981
1982 let mut multi_touch_spec =
1983 TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned()));
1984 if let Some(width) = it.next() {
1985 multi_touch_spec.set_width(width.trim().parse().unwrap());
1986 }
1987 if let Some(height) = it.next() {
1988 multi_touch_spec.set_height(height.trim().parse().unwrap());
1989 }
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07001990 cfg.virtio_multi_touch.push(multi_touch_spec);
Tristan Muntsinger486cffc2020-09-29 22:05:41 +00001991 }
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001992 "trackpad" => {
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09001993 let mut it = value.unwrap().split(':');
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001994
1995 let mut trackpad_spec =
Jorge E. Moreira99d3f082019-03-07 10:59:54 -08001996 TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned()));
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001997 if let Some(width) = it.next() {
Kaiyi Libccb4eb2020-02-06 17:53:11 -08001998 trackpad_spec.set_width(width.trim().parse().unwrap());
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001999 }
2000 if let Some(height) = it.next() {
Kaiyi Libccb4eb2020-02-06 17:53:11 -08002001 trackpad_spec.set_height(height.trim().parse().unwrap());
Jorge E. Moreiradffec502019-01-14 18:44:49 -08002002 }
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07002003 cfg.virtio_trackpad.push(trackpad_spec);
Jorge E. Moreiradffec502019-01-14 18:44:49 -08002004 }
2005 "mouse" => {
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07002006 cfg.virtio_mice
2007 .push(PathBuf::from(value.unwrap().to_owned()));
Jorge E. Moreiradffec502019-01-14 18:44:49 -08002008 }
2009 "keyboard" => {
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07002010 cfg.virtio_keyboard
2011 .push(PathBuf::from(value.unwrap().to_owned()));
Jorge E. Moreiradffec502019-01-14 18:44:49 -08002012 }
Daniel Norman5e23df72021-03-11 10:11:02 -08002013 "switches" => {
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07002014 cfg.virtio_switches
2015 .push(PathBuf::from(value.unwrap().to_owned()));
Daniel Norman5e23df72021-03-11 10:11:02 -08002016 }
Jorge E. Moreiradffec502019-01-14 18:44:49 -08002017 "evdev" => {
2018 let dev_path = PathBuf::from(value.unwrap());
2019 if !dev_path.exists() {
2020 return Err(argument::Error::InvalidValue {
2021 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08002022 expected: String::from("this input device path does not exist"),
Jorge E. Moreiradffec502019-01-14 18:44:49 -08002023 });
2024 }
2025 cfg.virtio_input_evdevs.push(dev_path);
2026 }
Miriam Zimmerman26ac9282019-01-29 21:21:48 -08002027 "split-irqchip" => {
2028 cfg.split_irqchip = true;
2029 }
Daniel Verkampe403f5c2018-12-11 16:29:26 -08002030 "initrd" => {
2031 cfg.initrd_path = Some(PathBuf::from(value.unwrap().to_owned()));
2032 }
Cody Schuffelen6d1ab502019-05-21 12:12:38 -07002033 "bios" => {
2034 if cfg.executable_path.is_some() {
2035 return Err(argument::Error::TooManyArguments(format!(
2036 "A VM executable was already specified: {:?}",
2037 cfg.executable_path
2038 )));
2039 }
2040 cfg.executable_path = Some(Executable::Bios(PathBuf::from(value.unwrap().to_owned())));
2041 }
Tomasz Nowicki344eb142021-09-22 05:51:58 +00002042 "vfio" | "vfio-platform" => {
Tomasz Nowicki71aca792021-06-09 18:53:49 +00002043 let vfio_type = name.parse().unwrap();
2044 let vfio_dev = VfioCommand::new(vfio_type, value.unwrap())?;
2045 cfg.vfio.push(vfio_dev);
Xiong Zhang17b0daf2019-04-23 17:14:50 +08002046 }
Alexandre Courbot8230abf2021-06-26 22:49:26 +09002047 #[cfg(feature = "video-decoder")]
Keiichi Watanabe57df6a02019-12-06 22:24:40 +09002048 "video-decoder" => {
Alexandre Courbotb42b3e52021-07-09 23:38:57 +09002049 cfg.video_dec = Some(parse_video_options(value)?);
Keiichi Watanabe57df6a02019-12-06 22:24:40 +09002050 }
Alexandre Courbot8230abf2021-06-26 22:49:26 +09002051 #[cfg(feature = "video-encoder")]
Keiichi Watanabe57df6a02019-12-06 22:24:40 +09002052 "video-encoder" => {
Alexandre Courbotb42b3e52021-07-09 23:38:57 +09002053 cfg.video_enc = Some(parse_video_options(value)?);
Keiichi Watanabe57df6a02019-12-06 22:24:40 +09002054 }
Tomasz Jeznach42644642020-05-20 23:27:59 -07002055 "acpi-table" => {
2056 let acpi_table = PathBuf::from(value.unwrap());
2057 if !acpi_table.exists() {
2058 return Err(argument::Error::InvalidValue {
2059 value: value.unwrap().to_owned(),
2060 expected: String::from("the acpi-table path does not exist"),
2061 });
2062 }
2063 if !acpi_table.is_file() {
2064 return Err(argument::Error::InvalidValue {
2065 value: value.unwrap().to_owned(),
2066 expected: String::from("the acpi-table path should be a file"),
2067 });
2068 }
2069
2070 cfg.acpi_tables.push(acpi_table);
2071 }
Will Deacon6560c182020-10-06 18:47:18 +01002072 "protected-vm" => {
Andrew Walbran413f8542021-01-08 13:29:03 +00002073 cfg.protected_vm = ProtectionType::Protected;
Andrew Walbran3cd93602022-01-25 13:59:23 +00002074 // Balloon device only works for unprotected VMs.
2075 cfg.balloon = false;
Will Deacon6560c182020-10-06 18:47:18 +01002076 }
Andrew Walbran0bbbb682021-12-13 13:42:07 +00002077 "protected-vm-without-firmware" => {
2078 cfg.protected_vm = ProtectionType::ProtectedWithoutFirmware;
Andrew Walbran3cd93602022-01-25 13:59:23 +00002079 // Balloon device only works for unprotected VMs.
2080 cfg.balloon = false;
Andrew Walbran0bbbb682021-12-13 13:42:07 +00002081 }
Chuanxiao Dongfd5626c2020-04-27 16:35:33 +08002082 "battery" => {
2083 let params = parse_battery_options(value)?;
2084 cfg.battery_type = Some(params);
2085 }
Keiichi Watanabec5262e92020-10-21 15:57:33 +09002086 #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
2087 "gdb" => {
2088 let port = value
2089 .unwrap()
2090 .parse()
2091 .map_err(|_| argument::Error::InvalidValue {
2092 value: value.unwrap().to_owned(),
2093 expected: String::from("expected a valid port number"),
2094 })?;
2095 cfg.gdb = Some(port);
2096 }
Andrew Walbran3cd93602022-01-25 13:59:23 +00002097 "no-balloon" => {
2098 cfg.balloon = false;
2099 }
Charles William Dick0e3d4b62020-12-14 12:16:46 +09002100 "balloon_bias_mib" => {
2101 cfg.balloon_bias =
2102 value
2103 .unwrap()
2104 .parse::<i64>()
2105 .map_err(|_| argument::Error::InvalidValue {
2106 value: value.unwrap().to_owned(),
2107 expected: String::from("expected a valid ballon bias in MiB"),
2108 })?
2109 * 1024
2110 * 1024; // cfg.balloon_bias is in bytes.
2111 }
Keiichi Watanabef3a37f42021-01-21 15:41:11 +09002112 "vhost-user-blk" => cfg.vhost_user_blk.push(VhostUserOption {
2113 socket: PathBuf::from(value.unwrap()),
2114 }),
Federico 'Morg' Pareschi70fc7de2021-04-08 15:43:13 +09002115 "vhost-user-console" => cfg.vhost_user_console.push(VhostUserOption {
2116 socket: PathBuf::from(value.unwrap()),
2117 }),
Chirantan Ekbote44292f52021-06-25 18:31:41 +09002118 "vhost-user-gpu" => cfg.vhost_user_gpu.push(VhostUserOption {
2119 socket: PathBuf::from(value.unwrap()),
2120 }),
JaeMan Parkeb9cc532021-07-02 15:02:59 +09002121 "vhost-user-mac80211-hwsim" => {
2122 cfg.vhost_user_mac80211_hwsim = Some(VhostUserOption {
2123 socket: PathBuf::from(value.unwrap()),
2124 });
2125 }
Keiichi Watanabe60686582021-03-12 04:53:51 +09002126 "vhost-user-net" => cfg.vhost_user_net.push(VhostUserOption {
2127 socket: PathBuf::from(value.unwrap()),
2128 }),
Woody Chow1b16db12021-04-02 16:59:59 +09002129 #[cfg(feature = "audio")]
2130 "vhost-user-snd" => cfg.vhost_user_snd.push(VhostUserOption {
2131 socket: PathBuf::from(value.unwrap()),
2132 }),
Chirantan Ekbote84091e52021-09-10 18:43:17 +09002133 "vhost-user-vsock" => cfg.vhost_user_vsock.push(VhostUserOption {
2134 socket: PathBuf::from(value.unwrap()),
2135 }),
Chirantan Ekbote2ee9dcd2021-05-26 18:21:44 +09002136 "vhost-user-wl" => {
2137 let mut components = value.unwrap().splitn(2, ":");
2138 let socket = components.next().map(PathBuf::from).ok_or_else(|| {
2139 argument::Error::InvalidValue {
2140 value: value.unwrap().to_owned(),
2141 expected: String::from("missing socket path"),
2142 }
2143 })?;
2144 let vm_tube = components.next().map(PathBuf::from).ok_or_else(|| {
2145 argument::Error::InvalidValue {
2146 value: value.unwrap().to_owned(),
2147 expected: String::from("missing vm tube path"),
2148 }
2149 })?;
2150 cfg.vhost_user_wl
2151 .push(VhostUserWlOption { socket, vm_tube });
2152 }
Woody Chow5890b702021-02-12 14:57:02 +09002153 "vhost-user-fs" => {
2154 // (socket:tag)
2155 let param = value.unwrap();
2156 let mut components = param.split(':');
2157 let socket =
2158 PathBuf::from(
2159 components
2160 .next()
2161 .ok_or_else(|| argument::Error::InvalidValue {
2162 value: param.to_owned(),
2163 expected: String::from("missing socket path for `vhost-user-fs`"),
2164 })?,
2165 );
2166 let tag = components
2167 .next()
2168 .ok_or_else(|| argument::Error::InvalidValue {
2169 value: param.to_owned(),
2170 expected: String::from("missing tag for `vhost-user-fs`"),
2171 })?
2172 .to_owned();
2173 cfg.vhost_user_fs.push(VhostUserFsOption { socket, tag });
2174 }
Tomasz Jeznach3ce74762021-02-26 01:01:53 -08002175 #[cfg(feature = "direct")]
2176 "direct-pmio" => {
2177 if cfg.direct_pmio.is_some() {
2178 return Err(argument::Error::TooManyArguments(
2179 "`direct_pmio` already given".to_owned(),
2180 ));
2181 }
2182 cfg.direct_pmio = Some(parse_direct_io_options(value)?);
2183 }
Tomasz Jeznach7271f752021-03-04 01:44:06 -08002184 #[cfg(feature = "direct")]
Tomasz Jeznach9e6c6332021-05-27 21:49:14 -07002185 "direct-mmio" => {
2186 if cfg.direct_mmio.is_some() {
2187 return Err(argument::Error::TooManyArguments(
2188 "`direct_mmio` already given".to_owned(),
2189 ));
2190 }
2191 cfg.direct_mmio = Some(parse_direct_io_options(value)?);
2192 }
2193 #[cfg(feature = "direct")]
Tomasz Jeznach7271f752021-03-04 01:44:06 -08002194 "direct-level-irq" => {
2195 cfg.direct_level_irq
2196 .push(
2197 value
2198 .unwrap()
2199 .parse()
2200 .map_err(|_| argument::Error::InvalidValue {
2201 value: value.unwrap().to_owned(),
2202 expected: String::from(
2203 "this value for `direct-level-irq` must be an unsigned integer",
2204 ),
2205 })?,
2206 );
2207 }
2208 #[cfg(feature = "direct")]
2209 "direct-edge-irq" => {
2210 cfg.direct_edge_irq
2211 .push(
2212 value
2213 .unwrap()
2214 .parse()
2215 .map_err(|_| argument::Error::InvalidValue {
2216 value: value.unwrap().to_owned(),
2217 expected: String::from(
2218 "this value for `direct-edge-irq` must be an unsigned integer",
2219 ),
2220 })?,
2221 );
2222 }
Tomasz Jeznachccb26942021-03-30 22:44:11 -07002223 "dmi" => {
2224 if cfg.dmi_path.is_some() {
2225 return Err(argument::Error::TooManyArguments(
2226 "`dmi` already given".to_owned(),
2227 ));
2228 }
2229 let dmi_path = PathBuf::from(value.unwrap());
2230 if !dmi_path.exists() {
2231 return Err(argument::Error::InvalidValue {
2232 value: value.unwrap().to_owned(),
2233 expected: String::from("the dmi path does not exist"),
2234 });
2235 }
2236 if !dmi_path.is_dir() {
2237 return Err(argument::Error::InvalidValue {
2238 value: value.unwrap().to_owned(),
2239 expected: String::from("the dmi path should be directory"),
2240 });
2241 }
2242 cfg.dmi_path = Some(dmi_path);
2243 }
Tomasz Jeznachd93c29f2021-04-12 11:00:24 -07002244 "no-legacy" => {
2245 cfg.no_legacy = true;
2246 }
ZhaoLiu2aaf7ad2021-10-10 18:22:29 +08002247 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
2248 "host-cpu-topology" => {
2249 cfg.host_cpu_topology = true;
2250 }
Mattias Nisslerde2c6402021-10-21 12:05:29 +00002251 "stub-pci-device" => {
2252 cfg.stub_pci_devices.push(parse_stub_pci_parameters(value)?);
2253 }
Abhishek Bhardwaj103c1b72021-11-01 15:52:23 -07002254 "vvu-proxy" => cfg.vvu_proxy.push(VhostUserOption {
2255 socket: PathBuf::from(value.unwrap()),
2256 }),
Chuanxiao Dongd4468612022-01-14 14:21:17 +08002257 "coiommu" => {
2258 let mut params: devices::CoIommuParameters = Default::default();
2259 if let Some(v) = value {
2260 let opts = v
2261 .split(',')
2262 .map(|frag| frag.splitn(2, '='))
2263 .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
2264
2265 for (k, v) in opts {
2266 match k {
2267 "unpin_policy" => {
2268 params.unpin_policy = v
2269 .parse::<devices::CoIommuUnpinPolicy>()
2270 .map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?
2271 }
2272 "unpin_interval" => {
2273 params.unpin_interval =
2274 Duration::from_secs(v.parse::<u64>().map_err(|e| {
2275 argument::Error::UnknownArgument(format!("{}", e))
2276 })?)
2277 }
2278 "unpin_limit" => {
2279 let limit = v
2280 .parse::<u64>()
2281 .map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?;
2282
2283 if limit == 0 {
2284 return Err(argument::Error::InvalidValue {
2285 value: v.to_owned(),
2286 expected: String::from("Please use non-zero unpin_limit value"),
2287 });
2288 }
2289
2290 params.unpin_limit = Some(limit)
2291 }
Chuanxiao Dong2fef3552022-01-20 09:20:53 +08002292 "unpin_gen_threshold" => {
2293 params.unpin_gen_threshold = v
2294 .parse::<u64>()
2295 .map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?
2296 }
Chuanxiao Dongd4468612022-01-14 14:21:17 +08002297 _ => {
2298 return Err(argument::Error::UnknownArgument(format!(
2299 "coiommu parameter {}",
2300 k
2301 )));
2302 }
2303 }
2304 }
2305 }
2306
2307 if cfg.coiommu_param.is_some() {
2308 return Err(argument::Error::TooManyArguments(
2309 "coiommu param already given".to_owned(),
2310 ));
2311 }
2312 cfg.coiommu_param = Some(params);
2313 }
Mattias Nisslerbbd91d02021-12-07 08:57:45 +00002314 "file-backed-mapping" => {
2315 cfg.file_backed_mappings
2316 .push(parse_file_backed_mapping(value)?);
2317 }
David Stevens06d157a2022-01-13 23:44:48 +09002318 "init-mem" => {
2319 if cfg.init_memory.is_some() {
2320 return Err(argument::Error::TooManyArguments(
2321 "`init-mem` already given".to_owned(),
2322 ));
2323 }
2324 cfg.init_memory =
2325 Some(
2326 value
2327 .unwrap()
2328 .parse()
2329 .map_err(|_| argument::Error::InvalidValue {
2330 value: value.unwrap().to_owned(),
2331 expected: String::from("this value for `init-mem` needs to be integer"),
2332 })?,
2333 )
2334 }
Xiong Zhangf7874712021-12-24 10:53:59 +08002335 #[cfg(feature = "direct")]
2336 "pcie-root-port" => {
2337 let pcie_path = PathBuf::from(value.unwrap());
2338 if !pcie_path.exists() {
2339 return Err(argument::Error::InvalidValue {
2340 value: value.unwrap().to_owned(),
2341 expected: String::from("the pcie root port path does not exist"),
2342 });
2343 }
2344 if !pcie_path.is_dir() {
2345 return Err(argument::Error::InvalidValue {
2346 value: value.unwrap().to_owned(),
2347 expected: String::from("the pcie root port path should be directory"),
2348 });
2349 }
2350
2351 cfg.pcie_rp.push(pcie_path);
2352 }
Zach Reiznerefe95782017-08-26 18:05:48 -07002353 "help" => return Err(argument::Error::PrintHelp),
2354 _ => unreachable!(),
2355 }
2356 Ok(())
2357}
2358
Kaiyi Libccb4eb2020-02-06 17:53:11 -08002359fn validate_arguments(cfg: &mut Config) -> std::result::Result<(), argument::Error> {
2360 if cfg.executable_path.is_none() {
2361 return Err(argument::Error::ExpectedArgument("`KERNEL`".to_owned()));
2362 }
2363 if cfg.host_ip.is_some() || cfg.netmask.is_some() || cfg.mac_address.is_some() {
2364 if cfg.host_ip.is_none() {
2365 return Err(argument::Error::ExpectedArgument(
2366 "`host_ip` missing from network config".to_owned(),
2367 ));
2368 }
2369 if cfg.netmask.is_none() {
2370 return Err(argument::Error::ExpectedArgument(
2371 "`netmask` missing from network config".to_owned(),
2372 ));
2373 }
2374 if cfg.mac_address.is_none() {
2375 return Err(argument::Error::ExpectedArgument(
2376 "`mac` missing from network config".to_owned(),
2377 ));
2378 }
2379 }
2380 if cfg.plugin_root.is_some() && !executable_is_plugin(&cfg.executable_path) {
2381 return Err(argument::Error::ExpectedArgument(
2382 "`plugin-root` requires `plugin`".to_owned(),
2383 ));
2384 }
2385 #[cfg(feature = "gpu")]
2386 {
Jason Macnakd659a0d2021-03-15 15:33:01 -07002387 if let Some(gpu_parameters) = cfg.gpu_parameters.as_mut() {
2388 if gpu_parameters.displays.is_empty() {
2389 gpu_parameters.displays.push(GpuDisplayParameters {
2390 width: DEFAULT_DISPLAY_WIDTH,
2391 height: DEFAULT_DISPLAY_HEIGHT,
2392 });
2393 }
2394
2395 let width = gpu_parameters.displays[0].width;
2396 let height = gpu_parameters.displays[0].height;
2397
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07002398 if let Some(virtio_multi_touch) = cfg.virtio_multi_touch.first_mut() {
Tristan Muntsinger486cffc2020-09-29 22:05:41 +00002399 virtio_multi_touch.set_default_size(width, height);
2400 }
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07002401 if let Some(virtio_single_touch) = cfg.virtio_single_touch.first_mut() {
Kaiyi Libccb4eb2020-02-06 17:53:11 -08002402 virtio_single_touch.set_default_size(width, height);
2403 }
2404 }
2405 }
Keiichi Watanabec5262e92020-10-21 15:57:33 +09002406 #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
Dennis Kempinc3dedf32021-11-12 14:42:28 -08002407 if cfg.gdb.is_some() && cfg.vcpu_count.unwrap_or(1) != 1 {
2408 return Err(argument::Error::ExpectedArgument(
2409 "`gdb` requires the number of vCPU to be 1".to_owned(),
2410 ));
Keiichi Watanabec5262e92020-10-21 15:57:33 +09002411 }
ZhaoLiu2aaf7ad2021-10-10 18:22:29 +08002412 if cfg.host_cpu_topology {
ZhaoLiu1e6e7b22021-12-06 17:23:21 +08002413 if cfg.no_smt {
2414 return Err(argument::Error::ExpectedArgument(
2415 "`host-cpu-topology` cannot be set at the same time as `no_smt`, since \
2416 the smt of the Guest is the same as that of the Host when \
2417 `host-cpu-topology` is set."
2418 .to_owned(),
2419 ));
2420 }
2421
ZhaoLiu2aaf7ad2021-10-10 18:22:29 +08002422 // Safe because we pass a flag for this call and the host supports this system call
2423 let pcpu_count = unsafe { libc::sysconf(libc::_SC_NPROCESSORS_CONF) } as usize;
2424 if cfg.vcpu_count.is_some() {
2425 if pcpu_count != cfg.vcpu_count.unwrap() {
2426 return Err(argument::Error::ExpectedArgument(format!(
2427 "`host-cpu-topology` requires the count of vCPUs({}) to equal the \
2428 count of CPUs({}) on host.",
2429 cfg.vcpu_count.unwrap(),
2430 pcpu_count
2431 )));
2432 }
2433 } else {
2434 cfg.vcpu_count = Some(pcpu_count);
2435 }
2436
2437 match &cfg.vcpu_affinity {
2438 None => {
2439 let mut affinity_map = BTreeMap::new();
2440 for cpu_id in 0..cfg.vcpu_count.unwrap() {
2441 affinity_map.insert(cpu_id, vec![cpu_id]);
2442 }
2443 cfg.vcpu_affinity = Some(VcpuAffinity::PerVcpu(affinity_map));
2444 }
2445 _ => {
2446 return Err(argument::Error::ExpectedArgument(
2447 "`host-cpu-topology` requires not to set `cpu-affinity` at the same time"
2448 .to_owned(),
2449 ));
2450 }
2451 }
2452 }
David Stevens8be9ef02022-01-13 22:50:24 +09002453 if !cfg.balloon && cfg.balloon_control.is_some() {
2454 return Err(argument::Error::ExpectedArgument(
2455 "'balloon-control' requires enabled balloon".to_owned(),
2456 ));
2457 }
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07002458 set_default_serial_parameters(&mut cfg.serial_parameters);
Kaiyi Libccb4eb2020-02-06 17:53:11 -08002459 Ok(())
2460}
2461
Dmitry Torokhovf75699f2021-12-03 11:19:13 -08002462enum CommandStatus {
2463 Success,
2464 VmReset,
2465 VmStop,
Andrew Walbran1a19c672022-01-24 17:24:10 +00002466 VmCrash,
Dmitry Torokhovf75699f2021-12-03 11:19:13 -08002467}
2468
2469fn run_vm(args: std::env::Args) -> std::result::Result<CommandStatus, ()> {
Zach Reiznerefe95782017-08-26 18:05:48 -07002470 let arguments =
2471 &[Argument::positional("KERNEL", "bzImage of kernel to run"),
Christian Blichmann33d56772021-03-04 19:03:54 +01002472 Argument::value("kvm-device", "PATH", "Path to the KVM device. (default /dev/kvm)"),
Christian Blichmann50f95912021-11-05 16:59:39 +01002473 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 +01002474 Argument::value("vhost-vsock-device", "PATH", "Path to the vhost-vsock device. (default /dev/vhost-vsock)"),
2475 Argument::value("vhost-net-device", "PATH", "Path to the vhost-net device. (default /dev/vhost-net)"),
Tristan Muntsinger4133b012018-12-21 16:01:56 -08002476 Argument::value("android-fstab", "PATH", "Path to Android fstab"),
Daniel Verkampe403f5c2018-12-11 16:29:26 -08002477 Argument::short_value('i', "initrd", "PATH", "Initial ramdisk to load."),
Zach Reiznerefe95782017-08-26 18:05:48 -07002478 Argument::short_value('p',
2479 "params",
2480 "PARAMS",
Zach Reiznerbb678712018-01-30 18:13:04 -08002481 "Extra kernel or plugin command line arguments. Can be given more than once."),
Zach Reiznerefe95782017-08-26 18:05:48 -07002482 Argument::short_value('c', "cpus", "N", "Number of VCPUs. (default: 1)"),
Daniel Verkampc677fb42020-09-08 13:47:49 -07002483 Argument::value("cpu-affinity", "CPUSET", "Comma-separated list of CPUs or CPU ranges to run VCPUs on (e.g. 0,1-3,5)
2484 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 -07002485 Argument::value("cpu-cluster", "CPUSET", "Group the given CPUs into a cluster (default: no clusters)"),
2486 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 -07002487 Argument::flag("per-vm-core-scheduling", "Enable per-VM core scheduling intead of the default one (per-vCPU core scheduing) by
2488 making all vCPU threads share same cookie for core scheduling.
2489 This option is no-op on devices that have neither MDS nor L1TF vulnerability."),
Vineeth Pillai2b6855e2022-01-12 16:57:22 +00002490 Argument::value("vcpu-cgroup-path", "PATH", "Move all vCPU threads to this CGroup (default: nothing moves)."),
Mattias Nisslerbbd91d02021-12-07 08:57:45 +00002491#[cfg(feature = "audio_cras")]
Woody Chow0b2b6062021-09-03 15:40:02 +09002492 Argument::value("cras-snd",
Chih-Yang Hsia99829b32021-12-08 22:00:48 +08002493 "[capture=true,client=crosvm,socket=unified,num_output_streams=1,num_input_streams=1]",
Woody Chow0b2b6062021-09-03 15:40:02 +09002494 "Comma separated key=value pairs for setting up cras snd devices.
2495 Possible key values:
Chih-Yang Hsiaa2b4fc02021-12-12 12:43:56 +08002496 capture - Enable audio capture. Default to false.
Woody Chow0b2b6062021-09-03 15:40:02 +09002497 client_type - Set specific client type for cras backend.
Chih-Yang Hsia99829b32021-12-08 22:00:48 +08002498 num_output_streams - Set number of output PCM streams
2499 num_input_streams - Set number of input PCM streams"),
Suleiman Souhlal015c3c12020-10-07 14:15:41 +09002500 Argument::flag("no-smt", "Don't use SMT in the guest"),
Kansho Nishidaab205af2020-08-13 18:17:50 +09002501 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 +09002502 Argument::flag("delay-rt", "Don't set VCPUs real-time until make-rt command is run"),
Zach Reiznerefe95782017-08-26 18:05:48 -07002503 Argument::short_value('m',
2504 "mem",
2505 "N",
2506 "Amount of guest memory in MiB. (default: 256)"),
David Stevens06d157a2022-01-13 23:44:48 +09002507 Argument::value("init-mem",
2508 "N",
2509 "Amount of guest memory outside the balloon at boot in MiB. (default: --mem)"),
Sergey Senozhatsky1e369c52021-04-13 20:23:51 +09002510 Argument::flag("hugepages", "Advise the kernel to use Huge Pages for guest memory mappings."),
Zach Reiznerefe95782017-08-26 18:05:48 -07002511 Argument::short_value('r',
2512 "root",
Daniel Verkampe73c80f2019-11-08 10:11:16 -08002513 "PATH[,key=value[,key=value[,...]]",
2514 "Path to a root disk image followed by optional comma-separated options.
2515 Like `--disk` but adds appropriate kernel command line option.
2516 See --disk for valid options."),
2517 Argument::value("rwroot", "PATH[,key=value[,key=value[,...]]", "Path to a writable root disk image followed by optional comma-separated options.
2518 See --disk for valid options."),
2519 Argument::short_value('d', "disk", "PATH[,key=value[,key=value[,...]]", "Path to a disk image followed by optional comma-separated options.
2520 Valid keys:
Daniel Verkamp27672232019-12-06 17:26:55 +11002521 sparse=BOOL - Indicates whether the disk should support the discard operation (default: true)
Daniel Verkamp4e1f99a2020-06-01 12:47:21 -07002522 block_size=BYTES - Set the reported block size of the disk (default: 512)
Junichi Uekawa7bea39f2021-07-16 14:05:06 +09002523 id=STRING - Set the block device identifier to an ASCII string, up to 20 characters (default: no ID)
2524 o_direct=BOOL - Use O_DIRECT mode to bypass page cache"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -08002525 Argument::value("rwdisk", "PATH[,key=value[,key=value[,...]]", "Path to a writable disk image followed by optional comma-separated options.
2526 See --disk for valid options."),
Jakub Starona3411ea2019-04-24 10:55:25 -07002527 Argument::value("rw-pmem-device", "PATH", "Path to a writable disk image."),
2528 Argument::value("pmem-device", "PATH", "Path to a disk image."),
Kansho Nishida282115b2019-12-18 13:13:14 +09002529 Argument::value("pstore", "path=PATH,size=SIZE", "Path to pstore buffer backend file follewed by size."),
Zach Reiznerefe95782017-08-26 18:05:48 -07002530 Argument::value("host_ip",
2531 "IP",
2532 "IP address to assign to host tap interface."),
2533 Argument::value("netmask", "NETMASK", "Netmask for VM subnet."),
2534 Argument::value("mac", "MAC", "MAC address for VM."),
Xiong Zhang773c7072020-03-20 10:39:55 +08002535 Argument::value("net-vq-pairs", "N", "virtio net virtual queue paris. (default: 1)"),
Andrew Scull1590e6f2020-03-18 18:00:47 +00002536 #[cfg(feature = "audio")]
Judy Hsiaod5c1e962020-02-04 12:30:01 +08002537 Argument::value("ac97",
paulhsia83d51602021-03-09 17:13:14 +08002538 "[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 +08002539 "Comma separated key=value pairs for setting up Ac97 devices. Can be given more than once .
Junichi Uekawaaab74e42021-07-19 16:12:46 +09002540 Possible key values:
2541 backend=(null, cras, vios) - Where to route the audio device. If not provided, backend will default to null.
2542 `null` for /dev/null, cras for CRAS server and vios for VioS server.
2543 capture - Enable audio capture
2544 capture_effects - | separated effects to be enabled for recording. The only supported effect value now is EchoCancellation or aec.
2545 client_type - Set specific client type for cras backend.
Woody Chowb27dea42021-09-08 15:51:22 +09002546 socket_type - Set specific socket type for cras backend.
Junichi Uekawaaab74e42021-07-19 16:12:46 +09002547 server - The to the VIOS server (unix socket)."),
Jorge E. Moreirad4562d02021-06-28 16:21:12 -07002548 #[cfg(feature = "audio")]
2549 Argument::value("sound", "[PATH]", "Path to the VioS server socket for setting up virtio-snd devices."),
Trent Begin17ccaad2019-04-17 13:51:25 -06002550 Argument::value("serial",
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07002551 "type=TYPE,[hardware=HW,num=NUM,path=PATH,input=PATH,console,earlycon,stdin]",
Jason Macnakcc7070b2019-11-06 14:48:12 -08002552 "Comma separated key=value pairs for setting up serial devices. Can be given more than once.
Junichi Uekawaaab74e42021-07-19 16:12:46 +09002553 Possible key values:
2554 type=(stdout,syslog,sink,file) - Where to route the serial device
2555 hardware=(serial,virtio-console) - Which type of serial hardware to emulate. Defaults to 8250 UART (serial).
2556 num=(1,2,3,4) - Serial Device Number. If not provided, num will default to 1.
2557 path=PATH - The path to the file to write to when type=file
2558 input=PATH - The path to the file to read from when not stdin
2559 console - Use this serial device as the guest console. Can only be given once. Will default to first serial port if not provided.
2560 earlycon - Use this serial device as the early console. Can only be given once.
2561 stdin - Direct standard input to this serial device. Can only be given once. Will default to first serial port if not provided.
2562 "),
Trent Begin17ccaad2019-04-17 13:51:25 -06002563 Argument::value("syslog-tag", "TAG", "When logging to syslog, use the provided tag."),
Zach Reizner0f2cfb02019-06-19 17:46:03 -07002564 Argument::value("x-display", "DISPLAY", "X11 display name to use."),
Zach Reizner65b98f12019-11-22 17:34:58 -08002565 Argument::flag("display-window-keyboard", "Capture keyboard input from the display window."),
2566 Argument::flag("display-window-mouse", "Capture keyboard input from the display window."),
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09002567 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 -04002568 #[cfg(feature = "wl-dmabuf")]
Nicholas Vernefde29972021-07-06 22:02:05 +10002569 Argument::flag("wayland-dmabuf", "DEPRECATED: Enable support for DMABufs in Wayland device."),
Zach Reiznerefe95782017-08-26 18:05:48 -07002570 Argument::short_value('s',
2571 "socket",
2572 "PATH",
2573 "Path to put the control socket. If PATH is a directory, a name will be generated."),
David Stevens8be9ef02022-01-13 22:50:24 +09002574 Argument::value("balloon-control", "PATH", "Path for balloon controller socket."),
Dylan Reidd0c9adc2017-10-02 19:04:50 -07002575 Argument::flag("disable-sandbox", "Run all devices in one, non-sandboxed process."),
Chirantan Ekboteebd56812018-04-16 19:32:04 -07002576 Argument::value("cid", "CID", "Context ID for virtual sockets."),
Chirantan Ekbotef1cd8d72021-09-21 17:33:58 +09002577 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 +09002578 "Colon-separated options for configuring a directory to be shared with the VM.
Junichi Uekawaaab74e42021-07-19 16:12:46 +09002579 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.
2580 The remaining fields are key=value pairs that may appear in any order. Valid keys are:
2581 type=(p9, fs) - Indicates whether the directory should be shared via virtio-9p or virtio-fs (default: p9).
2582 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).
2583 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).
2584 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.
2585 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 +09002586 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.
2587 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 +09002588 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 +09002589"),
Dylan Reide026ef02017-10-02 19:03:52 -07002590 Argument::value("seccomp-policy-dir", "PATH", "Path to seccomp .policy files."),
Zach Reizner44863792019-06-26 14:22:08 -07002591 Argument::flag("seccomp-log-failures", "Instead of seccomp filter failures being fatal, they will be logged instead."),
Zach Reizner8864cb02018-01-16 17:59:03 -08002592 #[cfg(feature = "plugin")]
Zach Reiznercc30d582018-01-23 21:16:42 -08002593 Argument::value("plugin", "PATH", "Absolute path to plugin process to run under crosvm."),
Daniel Verkampbd1a0842019-01-08 15:50:34 -08002594 #[cfg(feature = "plugin")]
Dmitry Torokhov0f1770d2018-05-11 10:57:16 -07002595 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 -08002596 #[cfg(feature = "plugin")]
Chirantan Ekboted41d7262018-11-16 16:37:45 -08002597 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 -08002598 #[cfg(feature = "plugin")]
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08002599 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."),
2600 #[cfg(feature = "plugin")]
Dmitry Torokhov1a6262b2019-03-01 00:34:03 -08002601 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 -08002602 #[cfg(feature = "plugin")]
2603 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 +00002604 Argument::flag("vhost-net", "Use vhost for networking."),
Alexandre Courbot993aa7f2021-12-09 14:51:29 +09002605 Argument::value("tap-name",
2606 "NAME",
2607 "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 -07002608 Argument::value("tap-fd",
2609 "fd",
Jorge E. Moreirab7952802019-02-12 16:43:05 -08002610 "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 -07002611 #[cfg(feature = "gpu")]
Jason Macnakcc7070b2019-11-06 14:48:12 -08002612 Argument::flag_or_value("gpu",
2613 "[width=INT,height=INT]",
2614 "(EXPERIMENTAL) Comma separated key=value pairs for setting up a virtio-gpu device
Junichi Uekawaaab74e42021-07-19 16:12:46 +09002615 Possible key values:
2616 backend=(2d|virglrenderer|gfxstream) - Which backend to use for virtio-gpu (determining rendering protocol)
2617 width=INT - The width of the virtual display connected to the virtio-gpu.
2618 height=INT - The height of the virtual display connected to the virtio-gpu.
2619 egl[=true|=false] - If the backend should use a EGL context for rendering.
2620 glx[=true|=false] - If the backend should use a GLX context for rendering.
2621 surfaceless[=true|=false] - If the backend should use a surfaceless context for rendering.
2622 angle[=true|=false] - If the gfxstream backend should use ANGLE (OpenGL on Vulkan) as its native OpenGL driver.
2623 syncfd[=true|=false] - If the gfxstream backend should support EGL_ANDROID_native_fence_sync
Chia-I Wu7f0f7c12022-01-12 10:42:18 -08002624 vulkan[=true|=false] - If the backend should support vulkan
2625 cache-path=PATH - The path to the virtio-gpu device shader cache.
2626 cache-size=SIZE - The maximum size of the shader cache."),
Jason Macnakd659a0d2021-03-15 15:33:01 -07002627 #[cfg(feature = "gpu")]
2628 Argument::flag_or_value("gpu-display",
2629 "[width=INT,height=INT]",
2630 "(EXPERIMENTAL) Comma separated key=value pairs for setting up a display on the virtio-gpu device
Junichi Uekawaaab74e42021-07-19 16:12:46 +09002631 Possible key values:
2632 width=INT - The width of the virtual display connected to the virtio-gpu.
2633 height=INT - The height of the virtual display connected to the virtio-gpu."),
Chia-I Wu16fb6592021-11-10 11:45:32 -08002634 #[cfg(all(feature = "gpu", feature = "virgl_renderer_next"))]
2635 Argument::flag_or_value("gpu-render-server",
2636 "[path=PATH]",
2637 "(EXPERIMENTAL) Comma separated key=value pairs for setting up a render server for the virtio-gpu device
2638 Possible key values:
Chia-I Wu7f0f7c12022-01-12 10:42:18 -08002639 path=PATH - The path to the render server executable.
2640 cache-path=PATH - The path to the render server shader cache.
2641 cache-size=SIZE - The maximum size of the shader cache."),
David Tolnay43f8e212019-02-13 17:28:16 -08002642 #[cfg(feature = "tpm")]
2643 Argument::flag("software-tpm", "enable a software emulated trusted platform module device"),
Jorge E. Moreiradffec502019-01-14 18:44:49 -08002644 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 -08002645 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 +00002646 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 -08002647 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)."),
2648 Argument::value("mouse", "PATH", "Path to a socket from where to read mouse input events and write status updates to."),
2649 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 -08002650 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 -08002651 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
2652 Argument::flag("split-irqchip", "(EXPERIMENTAL) enable split-irqchip support"),
Cody Schuffelen6d1ab502019-05-21 12:12:38 -07002653 Argument::value("bios", "PATH", "Path to BIOS/firmware ROM"),
Tomasz Nowicki344eb142021-09-22 05:51:58 +00002654 Argument::value("vfio", "PATH[,iommu=on|off]", "Path to sysfs of PCI pass through or mdev device.
Zide Chendfc4b882021-03-10 16:35:37 -08002655iommu=on|off - indicates whether to enable virtio IOMMU for this device"),
Tomasz Nowicki344eb142021-09-22 05:51:58 +00002656 Argument::value("vfio-platform", "PATH", "Path to sysfs of platform pass through"),
Keiichi Watanabe57df6a02019-12-06 22:24:40 +09002657 #[cfg(feature = "video-decoder")]
Alexandre Courbotb42b3e52021-07-09 23:38:57 +09002658 Argument::flag_or_value("video-decoder", "[backend]", "(EXPERIMENTAL) enable virtio-video decoder device
2659 Possible backend values: libvda"),
Keiichi Watanabe57df6a02019-12-06 22:24:40 +09002660 #[cfg(feature = "video-encoder")]
Alexandre Courbotb42b3e52021-07-09 23:38:57 +09002661 Argument::flag_or_value("video-encoder", "[backend]", "(EXPERIMENTAL) enable virtio-video encoder device
2662 Possible backend values: libvda"),
Tomasz Jeznach42644642020-05-20 23:27:59 -07002663 Argument::value("acpi-table", "PATH", "Path to user provided ACPI table"),
Will Deacon6560c182020-10-06 18:47:18 +01002664 Argument::flag("protected-vm", "(EXPERIMENTAL) prevent host access to guest memory"),
Andrew Walbran0bbbb682021-12-13 13:42:07 +00002665 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 +01002666 #[cfg(target_arch = "aarch64")]
Andrew Walbran0bbbb682021-12-13 13:42:07 +00002667 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 +08002668 Argument::flag_or_value("battery",
2669 "[type=TYPE]",
2670 "Comma separated key=value pairs for setting up battery device
Junichi Uekawaaab74e42021-07-19 16:12:46 +09002671 Possible key values:
2672 type=goldfish - type of battery emulation, defaults to goldfish"),
Keiichi Watanabec5262e92020-10-21 15:57:33 +09002673 Argument::value("gdb", "PORT", "(EXPERIMENTAL) gdb on the given port"),
Andrew Walbran3cd93602022-01-25 13:59:23 +00002674 Argument::flag("no-balloon", "Don't use virtio-balloon device in the guest"),
Charles William Dick0e3d4b62020-12-14 12:16:46 +09002675 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 +09002676 Argument::value("vhost-user-blk", "SOCKET_PATH", "Path to a socket for vhost-user block"),
Federico 'Morg' Pareschi70fc7de2021-04-08 15:43:13 +09002677 Argument::value("vhost-user-console", "SOCKET_PATH", "Path to a socket for vhost-user console"),
Chirantan Ekbote44292f52021-06-25 18:31:41 +09002678 Argument::value("vhost-user-gpu", "SOCKET_PATH", "Paths to a vhost-user socket for gpu"),
JaeMan Parkeb9cc532021-07-02 15:02:59 +09002679 Argument::value("vhost-user-mac80211-hwsim", "SOCKET_PATH", "Path to a socket for vhost-user mac80211_hwsim"),
Keiichi Watanabe60686582021-03-12 04:53:51 +09002680 Argument::value("vhost-user-net", "SOCKET_PATH", "Path to a socket for vhost-user net"),
Woody Chow1b16db12021-04-02 16:59:59 +09002681 #[cfg(feature = "audio")]
2682 Argument::value("vhost-user-snd", "SOCKET_PATH", "Path to a socket for vhost-user snd"),
Chirantan Ekbote84091e52021-09-10 18:43:17 +09002683 Argument::value("vhost-user-vsock", "SOCKET_PATH", "Path to a socket for vhost-user vsock"),
Chirantan Ekbote2ee9dcd2021-05-26 18:21:44 +09002684 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 +09002685 Argument::value("vhost-user-fs", "SOCKET_PATH:TAG",
2686 "Path to a socket path for vhost-user fs, and tag for the shared dir"),
Abhishek Bhardwaj103c1b72021-11-01 15:52:23 -07002687 Argument::value("vvu-proxy", "SOCKET_PATH", "Socket path for the Virtio Vhost User proxy device"),
Tomasz Jeznach3ce74762021-02-26 01:01:53 -08002688 #[cfg(feature = "direct")]
Junichi Uekawab6a6e942021-12-07 05:49:30 +09002689 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 -07002690 #[cfg(feature = "direct")]
Junichi Uekawab6a6e942021-12-07 05:49:30 +09002691 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)."),
Mattias Nisslerbbd91d02021-12-07 08:57:45 +00002692#[cfg(feature = "direct")]
Tomasz Jeznach7271f752021-03-04 01:44:06 -08002693 Argument::value("direct-level-irq", "irq", "Enable interrupt passthrough"),
Mattias Nisslerbbd91d02021-12-07 08:57:45 +00002694#[cfg(feature = "direct")]
Tomasz Jeznach7271f752021-03-04 01:44:06 -08002695 Argument::value("direct-edge-irq", "irq", "Enable interrupt passthrough"),
Tomasz Jeznachccb26942021-03-30 22:44:11 -07002696 Argument::value("dmi", "DIR", "Directory with smbios_entry_point/DMI files"),
Tomasz Jeznachd93c29f2021-04-12 11:00:24 -07002697 Argument::flag("no-legacy", "Don't use legacy KBD/RTC devices emulation"),
Mattias Nisslerbbd91d02021-12-07 08:57:45 +00002698#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
ZhaoLiu2aaf7ad2021-10-10 18:22:29 +08002699 Argument::flag("host-cpu-topology", "Use mirror cpu topology of Host for Guest VM"),
Daniel Verkamp42b33602021-12-15 15:35:08 -08002700 Argument::value("stub-pci-device", "DOMAIN:BUS:DEVICE.FUNCTION[,vendor=NUM][,device=NUM][,class=NUM][,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.
Mattias Nisslerde2c6402021-10-21 12:05:29 +00002701 Optional further parameters
2702 vendor=NUM - PCI vendor ID
2703 device=NUM - PCI device ID
2704 class=NUM - PCI class (including class code, subclass, and programming interface)
Mattias Nisslerde2c6402021-10-21 12:05:29 +00002705 subsystem_vendor=NUM - PCI subsystem vendor ID
2706 subsystem_device=NUM - PCI subsystem device ID
2707 revision=NUM - revision"),
Chuanxiao Dongd4468612022-01-14 14:21:17 +08002708 Argument::flag_or_value("coiommu",
Chuanxiao Dong2fef3552022-01-20 09:20:53 +08002709 "unpin_policy=POLICY,unpin_interval=NUM,unpin_limit=NUM,unpin_gen_threshold=NUM ",
Chuanxiao Dongd4468612022-01-14 14:21:17 +08002710 "Comma separated key=value pairs for setting up coiommu devices.
2711 Possible key values:
2712 unpin_policy=lru - LRU unpin policy.
2713 unpin_interval=NUM - Unpin interval time in seconds.
Chuanxiao Dong2fef3552022-01-20 09:20:53 +08002714 unpin_limit=NUM - Unpin limit for each unpin cycle, in unit of page count. 0 is invalid.
2715 unpin_gen_threshold=NUM - Number of unpin intervals a pinned page must be busy for to be aged into the older which is less frequently checked generation."),
Mattias Nisslerbbd91d02021-12-07 08:57:45 +00002716 Argument::value("file-backed-mapping", "addr=NUM,size=SIZE,path=PATH[,offset=NUM][,ro][,rw][,sync]", "Map the given file into guest memory at the specified address.
2717 Parameters (addr, size, path are required):
2718 addr=NUM - guest physical address to map at
2719 size=NUM - amount of memory to map
2720 path=PATH - path to backing file/device to map
2721 offset=NUM - offset in backing file (default 0)
2722 ro - make the mapping readonly (default)
2723 rw - make the mapping writable
2724 sync - open backing file with O_SYNC
2725 align - whether to adjust addr and size to page boundaries implicitly"),
Xiong Zhangf7874712021-12-24 10:53:59 +08002726 #[cfg(feature = "direct")]
2727 Argument::value("pcie-root-port", "PATH", "Path to sysfs of host pcie root port"),
Zach Reiznerefe95782017-08-26 18:05:48 -07002728 Argument::short_flag('h', "help", "Print help message.")];
2729
2730 let mut cfg = Config::default();
Zach Reizner55a9e502018-10-03 10:22:32 -07002731 let match_res = set_arguments(args, &arguments[..], |name, value| {
2732 set_argument(&mut cfg, name, value)
David Tolnay2bac1e72018-12-12 14:33:42 -08002733 })
Kaiyi Libccb4eb2020-02-06 17:53:11 -08002734 .and_then(|_| validate_arguments(&mut cfg));
Zach Reiznerefe95782017-08-26 18:05:48 -07002735
2736 match match_res {
Zach Reizner8864cb02018-01-16 17:59:03 -08002737 #[cfg(feature = "plugin")]
Zach Reizner267f2c82019-07-31 17:07:27 -07002738 Ok(()) if executable_is_plugin(&cfg.executable_path) => {
2739 match crosvm::plugin::run_config(cfg) {
2740 Ok(_) => {
2741 info!("crosvm and plugin have exited normally");
Dmitry Torokhovf75699f2021-12-03 11:19:13 -08002742 Ok(CommandStatus::VmStop)
Zach Reizner267f2c82019-07-31 17:07:27 -07002743 }
2744 Err(e) => {
Dmitry Torokhovfbc43372022-01-25 11:52:27 -08002745 error!("{:#}", e);
Zach Reizner267f2c82019-07-31 17:07:27 -07002746 Err(())
2747 }
Zach Reizner8864cb02018-01-16 17:59:03 -08002748 }
Zach Reizner267f2c82019-07-31 17:07:27 -07002749 }
Michael Hoylee47a5002020-10-15 16:24:13 -07002750 Ok(()) => match platform::run_config(cfg) {
Dmitry Torokhovf75699f2021-12-03 11:19:13 -08002751 Ok(platform::ExitState::Stop) => {
Zach Reizner55a9e502018-10-03 10:22:32 -07002752 info!("crosvm has exited normally");
Dmitry Torokhovf75699f2021-12-03 11:19:13 -08002753 Ok(CommandStatus::VmStop)
2754 }
2755 Ok(platform::ExitState::Reset) => {
2756 info!("crosvm has exited normally due to reset request");
2757 Ok(CommandStatus::VmReset)
Zach Reiznerefe95782017-08-26 18:05:48 -07002758 }
Andrew Walbran1a19c672022-01-24 17:24:10 +00002759 Ok(platform::ExitState::Crash) => {
2760 info!("crosvm has exited due to a VM crash");
2761 Ok(CommandStatus::VmCrash)
2762 }
Zach Reizner55a9e502018-10-03 10:22:32 -07002763 Err(e) => {
Alexandre Courbota184e312021-12-08 13:18:27 +09002764 error!("crosvm has exited with error: {:#}", e);
Zach Reizner55a9e502018-10-03 10:22:32 -07002765 Err(())
2766 }
2767 },
Dylan Reidbfba9932018-02-05 15:51:59 -08002768 Err(argument::Error::PrintHelp) => {
2769 print_help("crosvm run", "KERNEL", &arguments[..]);
Dmitry Torokhovf75699f2021-12-03 11:19:13 -08002770 Ok(CommandStatus::Success)
Dylan Reidbfba9932018-02-05 15:51:59 -08002771 }
Zach Reizner8864cb02018-01-16 17:59:03 -08002772 Err(e) => {
Dmitry Torokhov470b1e72020-01-15 12:46:49 -08002773 error!("{}", e);
Dylan Reidbfba9932018-02-05 15:51:59 -08002774 Err(())
Zach Reizner8864cb02018-01-16 17:59:03 -08002775 }
Zach Reiznerefe95782017-08-26 18:05:48 -07002776 }
2777}
2778
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002779fn stop_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
Zach Reizner78986322019-02-21 20:43:21 -08002780 if args.len() == 0 {
2781 print_help("crosvm stop", "VM_SOCKET...", &[]);
2782 println!("Stops the crosvm instance listening on each `VM_SOCKET` given.");
Jianxun Zhang56497d22019-03-04 14:38:24 -08002783 return Err(());
Zach Reizner78986322019-02-21 20:43:21 -08002784 }
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002785 let socket_path = &args.next().unwrap();
2786 let socket_path = Path::new(&socket_path);
2787 vms_request(&VmRequest::Exit, socket_path)
Zach Reizner78986322019-02-21 20:43:21 -08002788}
2789
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002790fn suspend_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
Zach Reizner78986322019-02-21 20:43:21 -08002791 if args.len() == 0 {
2792 print_help("crosvm suspend", "VM_SOCKET...", &[]);
2793 println!("Suspends the crosvm instance listening on each `VM_SOCKET` given.");
Jianxun Zhang56497d22019-03-04 14:38:24 -08002794 return Err(());
Zach Reizner78986322019-02-21 20:43:21 -08002795 }
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002796 let socket_path = &args.next().unwrap();
2797 let socket_path = Path::new(&socket_path);
2798 vms_request(&VmRequest::Suspend, socket_path)
Zach Reizner78986322019-02-21 20:43:21 -08002799}
2800
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002801fn resume_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
Zach Reizner78986322019-02-21 20:43:21 -08002802 if args.len() == 0 {
2803 print_help("crosvm resume", "VM_SOCKET...", &[]);
2804 println!("Resumes the crosvm instance listening on each `VM_SOCKET` given.");
Jianxun Zhang56497d22019-03-04 14:38:24 -08002805 return Err(());
Zach Reizner78986322019-02-21 20:43:21 -08002806 }
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002807 let socket_path = &args.next().unwrap();
2808 let socket_path = Path::new(&socket_path);
2809 vms_request(&VmRequest::Resume, socket_path)
Zach Reizner78986322019-02-21 20:43:21 -08002810}
2811
2812fn balloon_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
2813 if args.len() < 2 {
2814 print_help("crosvm balloon", "SIZE VM_SOCKET...", &[]);
2815 println!("Set the ballon size of the crosvm instance to `SIZE` bytes.");
Jianxun Zhang56497d22019-03-04 14:38:24 -08002816 return Err(());
Zach Reizner78986322019-02-21 20:43:21 -08002817 }
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09002818 let num_bytes = match args.next().unwrap().parse::<u64>() {
Zach Reizner78986322019-02-21 20:43:21 -08002819 Ok(n) => n,
2820 Err(_) => {
2821 error!("Failed to parse number of bytes");
2822 return Err(());
2823 }
2824 };
2825
Jakub Staron1f828d72019-04-11 12:49:29 -07002826 let command = BalloonControlCommand::Adjust { num_bytes };
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002827 let socket_path = &args.next().unwrap();
2828 let socket_path = Path::new(&socket_path);
2829 vms_request(&VmRequest::BalloonCommand(command), socket_path)
Zach Reizner78986322019-02-21 20:43:21 -08002830}
2831
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002832fn balloon_stats(mut args: std::env::Args) -> std::result::Result<(), ()> {
Charles William Dicked22f6b2020-04-08 11:05:24 +09002833 if args.len() != 1 {
2834 print_help("crosvm balloon_stats", "VM_SOCKET", &[]);
2835 println!("Prints virtio balloon statistics for a `VM_SOCKET`.");
2836 return Err(());
2837 }
2838 let command = BalloonControlCommand::Stats {};
2839 let request = &VmRequest::BalloonCommand(command);
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002840 let socket_path = &args.next().unwrap();
2841 let socket_path = Path::new(&socket_path);
2842 let response = handle_request(request, socket_path)?;
Hikaru Nishidaa0e381b2021-05-24 17:13:45 +09002843 match serde_json::to_string_pretty(&response) {
2844 Ok(response_json) => println!("{}", response_json),
2845 Err(e) => {
2846 error!("Failed to serialize into JSON: {}", e);
2847 return Err(());
2848 }
2849 }
Hikaru Nishida6b51c752021-05-21 12:37:43 +09002850 match response {
2851 VmResponse::BalloonStats { .. } => Ok(()),
2852 _ => Err(()),
2853 }
Charles William Dicked22f6b2020-04-08 11:05:24 +09002854}
2855
Keiichi Watanabe2400bd52021-08-04 19:18:02 +09002856fn modify_battery(mut args: std::env::Args) -> std::result::Result<(), ()> {
2857 if args.len() < 4 {
2858 print_help(
2859 "crosvm battery BATTERY_TYPE ",
2860 "[status STATUS | \
2861 present PRESENT | \
2862 health HEALTH | \
2863 capacity CAPACITY | \
2864 aconline ACONLINE ] \
2865 VM_SOCKET...",
2866 &[],
2867 );
2868 return Err(());
2869 }
2870
2871 // This unwrap will not panic because of the above length check.
2872 let battery_type = args.next().unwrap();
2873 let property = args.next().unwrap();
2874 let target = args.next().unwrap();
2875
2876 let socket_path = args.next().unwrap();
2877 let socket_path = Path::new(&socket_path);
2878
Daniel Verkamp166d1dd2021-08-19 17:05:29 -07002879 do_modify_battery(socket_path, &*battery_type, &*property, &*target)
Keiichi Watanabe2400bd52021-08-04 19:18:02 +09002880}
2881
Xiong Zhangc78e72b2021-04-08 11:31:41 +08002882fn modify_vfio(mut args: std::env::Args) -> std::result::Result<(), ()> {
2883 if args.len() < 3 {
2884 print_help(
2885 "crosvm vfio",
2886 "[add | remove host_vfio_sysfs] VM_SOCKET...",
2887 &[],
2888 );
2889 return Err(());
2890 }
2891
2892 // This unwrap will not panic because of the above length check.
2893 let command = args.next().unwrap();
2894 let path_str = args.next().unwrap();
2895 let vfio_path = PathBuf::from(&path_str);
2896 if !vfio_path.exists() || !vfio_path.is_dir() {
2897 error!("Invalid host sysfs path: {}", path_str);
2898 return Err(());
2899 }
2900
2901 let socket_path = args.next().unwrap();
2902 let socket_path = Path::new(&socket_path);
2903
2904 let add = match command.as_ref() {
2905 "add" => true,
2906 "remove" => false,
2907 other => {
2908 error!("Invalid vfio command {}", other);
2909 return Err(());
2910 }
2911 };
2912
2913 let request = VmRequest::VfioCommand { vfio_path, add };
2914 handle_request(&request, socket_path)?;
2915 Ok(())
2916}
2917
Andrew Walbranfb7c1082021-06-17 18:12:14 +01002918#[cfg(feature = "composite-disk")]
2919fn create_composite(mut args: std::env::Args) -> std::result::Result<(), ()> {
2920 if args.len() < 1 {
2921 print_help("crosvm create_composite", "PATH [LABEL:PARTITION]..", &[]);
2922 println!("Creates a new composite disk image containing the given partition images");
2923 return Err(());
2924 }
2925
2926 let composite_image_path = args.next().unwrap();
Jooyung Han2e14c732021-07-29 13:27:54 +09002927 let zero_filler_path = format!("{}.filler", composite_image_path);
Andrew Walbranfb7c1082021-06-17 18:12:14 +01002928 let header_path = format!("{}.header", composite_image_path);
2929 let footer_path = format!("{}.footer", composite_image_path);
2930
2931 let mut composite_image_file = OpenOptions::new()
2932 .create(true)
2933 .read(true)
2934 .write(true)
2935 .truncate(true)
2936 .open(&composite_image_path)
2937 .map_err(|e| {
2938 error!(
2939 "Failed opening composite disk image file at '{}': {}",
2940 composite_image_path, e
2941 );
2942 })?;
Jooyung Han2e14c732021-07-29 13:27:54 +09002943 create_zero_filler(&zero_filler_path).map_err(|e| {
2944 error!(
2945 "Failed to create zero filler file at '{}': {}",
2946 &zero_filler_path, e
2947 );
2948 })?;
Andrew Walbranfb7c1082021-06-17 18:12:14 +01002949 let mut header_file = OpenOptions::new()
2950 .create(true)
2951 .read(true)
2952 .write(true)
2953 .truncate(true)
2954 .open(&header_path)
2955 .map_err(|e| {
2956 error!(
2957 "Failed opening header image file at '{}': {}",
2958 header_path, e
2959 );
2960 })?;
2961 let mut footer_file = OpenOptions::new()
2962 .create(true)
2963 .read(true)
2964 .write(true)
2965 .truncate(true)
2966 .open(&footer_path)
2967 .map_err(|e| {
2968 error!(
2969 "Failed opening footer image file at '{}': {}",
2970 footer_path, e
2971 );
2972 })?;
2973
2974 let partitions = args
2975 .into_iter()
2976 .map(|partition_arg| {
2977 if let [label, path] = partition_arg.split(":").collect::<Vec<_>>()[..] {
2978 let partition_file = File::open(path)
2979 .map_err(|e| error!("Failed to open partition image: {}", e))?;
Daniel Verkampeb1640e2021-09-07 14:09:31 -07002980 let size = create_disk_file(partition_file, disk::MAX_NESTING_DEPTH)
Andrew Walbranfb7c1082021-06-17 18:12:14 +01002981 .map_err(|e| error!("Failed to create DiskFile instance: {}", e))?
2982 .get_len()
2983 .map_err(|e| error!("Failed to get length of partition image: {}", e))?;
2984 Ok(PartitionInfo {
2985 label: label.to_owned(),
Jooyung Hand7e56ba2021-07-29 13:26:48 +09002986 path: Path::new(path).to_owned(),
Andrew Walbranfb7c1082021-06-17 18:12:14 +01002987 partition_type: ImagePartitionType::LinuxFilesystem,
2988 writable: false,
Jooyung Hand7e56ba2021-07-29 13:26:48 +09002989 size,
Andrew Walbranfb7c1082021-06-17 18:12:14 +01002990 })
2991 } else {
2992 error!(
2993 "Must specify label and path for partition '{}', like LABEL:PATH",
2994 partition_arg
2995 );
2996 Err(())
2997 }
2998 })
2999 .collect::<Result<Vec<_>, _>>()?;
3000
3001 create_composite_disk(
3002 &partitions,
Jooyung Han2e14c732021-07-29 13:27:54 +09003003 &PathBuf::from(zero_filler_path),
Andrew Walbranfb7c1082021-06-17 18:12:14 +01003004 &PathBuf::from(header_path),
3005 &mut header_file,
3006 &PathBuf::from(footer_path),
3007 &mut footer_file,
3008 &mut composite_image_file,
3009 )
3010 .map_err(|e| {
3011 error!(
3012 "Failed to create composite disk image at '{}': {}",
3013 composite_image_path, e
3014 );
3015 })?;
3016
3017 Ok(())
3018}
3019
A. Cody Schuffelen9ca60392019-12-23 18:27:11 -08003020fn create_qcow2(args: std::env::Args) -> std::result::Result<(), ()> {
3021 let arguments = [
3022 Argument::positional("PATH", "where to create the qcow2 image"),
3023 Argument::positional("[SIZE]", "the expanded size of the image"),
3024 Argument::value(
3025 "backing_file",
3026 "path/to/file",
3027 " the file to back the image",
3028 ),
3029 ];
3030 let mut positional_index = 0;
3031 let mut file_path = String::from("");
3032 let mut size: Option<u64> = None;
3033 let mut backing_file: Option<String> = None;
3034 set_arguments(args, &arguments[..], |name, value| {
3035 match (name, positional_index) {
3036 ("", 0) => {
3037 // NAME
3038 positional_index += 1;
3039 file_path = value.unwrap().to_owned();
3040 }
3041 ("", 1) => {
3042 // [SIZE]
3043 positional_index += 1;
3044 size = Some(value.unwrap().parse::<u64>().map_err(|_| {
3045 argument::Error::InvalidValue {
3046 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08003047 expected: String::from("SIZE should be a nonnegative integer"),
A. Cody Schuffelen9ca60392019-12-23 18:27:11 -08003048 }
3049 })?);
3050 }
3051 ("", _) => {
3052 return Err(argument::Error::TooManyArguments(
3053 "Expected at most 2 positional arguments".to_owned(),
3054 ));
3055 }
3056 ("backing_file", _) => {
3057 backing_file = value.map(|x| x.to_owned());
3058 }
3059 _ => unreachable!(),
3060 };
3061 Ok(())
3062 })
3063 .map_err(|e| {
3064 error!("Unable to parse command line arguments: {}", e);
3065 })?;
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09003066 if file_path.is_empty() || !(size.is_some() ^ backing_file.is_some()) {
A. Cody Schuffelen9ca60392019-12-23 18:27:11 -08003067 print_help("crosvm create_qcow2", "PATH [SIZE]", &arguments);
3068 println!(
3069 "Create a new QCOW2 image at `PATH` of either the specified `SIZE` in bytes or
3070with a '--backing_file'."
3071 );
Jianxun Zhang56497d22019-03-04 14:38:24 -08003072 return Err(());
Dylan Reid2dcb6322018-07-13 10:42:48 -07003073 }
Dylan Reid2dcb6322018-07-13 10:42:48 -07003074
3075 let file = OpenOptions::new()
3076 .create(true)
3077 .read(true)
3078 .write(true)
A. Cody Schuffelen9ca60392019-12-23 18:27:11 -08003079 .truncate(true)
Dylan Reid2dcb6322018-07-13 10:42:48 -07003080 .open(&file_path)
3081 .map_err(|e| {
David Tolnayb4bd00f2019-02-12 17:51:26 -08003082 error!("Failed opening qcow file at '{}': {}", file_path, e);
Dylan Reid2dcb6322018-07-13 10:42:48 -07003083 })?;
3084
A. Cody Schuffelen9ca60392019-12-23 18:27:11 -08003085 match (size, backing_file) {
3086 (Some(size), None) => QcowFile::new(file, size).map_err(|e| {
3087 error!("Failed to create qcow file at '{}': {}", file_path, e);
3088 })?,
3089 (None, Some(backing_file)) => {
Daniel Verkampeb1640e2021-09-07 14:09:31 -07003090 QcowFile::new_from_backing(file, &backing_file, disk::MAX_NESTING_DEPTH).map_err(
3091 |e| {
3092 error!("Failed to create qcow file at '{}': {}", file_path, e);
3093 },
3094 )?
A. Cody Schuffelen9ca60392019-12-23 18:27:11 -08003095 }
3096 _ => unreachable!(),
3097 };
Dylan Reid2dcb6322018-07-13 10:42:48 -07003098 Ok(())
3099}
3100
Keiichi Watanabeee4b58e2021-08-17 19:34:01 +09003101fn start_device(mut args: std::env::Args) -> std::result::Result<(), ()> {
3102 let print_usage = || {
3103 print_help(
3104 "crosvm device",
Woody Chowf364d022021-09-17 13:37:32 +09003105 " (block|console|cras-snd|fs|gpu|net|wl) <device-specific arguments>",
Keiichi Watanabeee4b58e2021-08-17 19:34:01 +09003106 &[],
3107 );
3108 };
3109
Keiichi Watanabeee4b58e2021-08-17 19:34:01 +09003110 if args.len() == 0 {
3111 print_usage();
3112 return Err(());
3113 }
3114
3115 let device = args.next().unwrap();
3116
3117 let program_name = format!("crosvm device {}", device);
Anton Romanovd48d4602021-12-13 23:41:47 +00003118
3119 let args = args.collect::<Vec<_>>();
3120 let args = args.iter().map(Deref::deref).collect::<Vec<_>>();
3121 let args = args.as_slice();
3122
Keiichi Watanabeee4b58e2021-08-17 19:34:01 +09003123 let result = match device.as_str() {
3124 "block" => run_block_device(&program_name, args),
Federico 'Morg' Pareschia1184822021-09-09 10:52:58 +09003125 "console" => run_console_device(&program_name, args),
Woody Chowf364d022021-09-17 13:37:32 +09003126 #[cfg(feature = "audio_cras")]
3127 "cras-snd" => run_cras_snd_device(&program_name, args),
Federico 'Morg' Pareschia1184822021-09-09 10:52:58 +09003128 "fs" => run_fs_device(&program_name, args),
Chirantan Ekbote78225292021-06-25 18:30:34 +09003129 #[cfg(feature = "gpu")]
3130 "gpu" => run_gpu_device(&program_name, args),
Keiichi Watanabeee4b58e2021-08-17 19:34:01 +09003131 "net" => run_net_device(&program_name, args),
Chirantan Ekbotef08bddd2021-09-10 18:41:06 +09003132 "vsock" => run_vsock_device(&program_name, args),
Keiichi Watanabeee4b58e2021-08-17 19:34:01 +09003133 "wl" => run_wl_device(&program_name, args),
3134 _ => {
3135 println!("Unknown device name: {}", device);
3136 print_usage();
3137 return Err(());
3138 }
3139 };
3140
3141 result.map_err(|e| {
Alexandre Courbot3abfaa52021-12-10 16:29:34 +09003142 error!("Failed to run {} device: {:#}", device, e);
Keiichi Watanabeee4b58e2021-08-17 19:34:01 +09003143 })
3144}
3145
Daniel Verkamp92f73d72018-12-04 13:17:46 -08003146fn disk_cmd(mut args: std::env::Args) -> std::result::Result<(), ()> {
3147 if args.len() < 2 {
3148 print_help("crosvm disk", "SUBCOMMAND VM_SOCKET...", &[]);
3149 println!("Manage attached virtual disk devices.");
3150 println!("Subcommands:");
3151 println!(" resize DISK_INDEX NEW_SIZE VM_SOCKET");
Jianxun Zhang56497d22019-03-04 14:38:24 -08003152 return Err(());
Daniel Verkamp92f73d72018-12-04 13:17:46 -08003153 }
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09003154 let subcommand: &str = &args.next().unwrap();
Daniel Verkamp92f73d72018-12-04 13:17:46 -08003155
3156 let request = match subcommand {
3157 "resize" => {
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09003158 let disk_index = match args.next().unwrap().parse::<usize>() {
Daniel Verkamp92f73d72018-12-04 13:17:46 -08003159 Ok(n) => n,
3160 Err(_) => {
3161 error!("Failed to parse disk index");
3162 return Err(());
3163 }
3164 };
3165
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09003166 let new_size = match args.next().unwrap().parse::<u64>() {
Daniel Verkamp92f73d72018-12-04 13:17:46 -08003167 Ok(n) => n,
3168 Err(_) => {
3169 error!("Failed to parse disk size");
3170 return Err(());
3171 }
3172 };
3173
Jakub Staronecf81e02019-04-11 11:43:39 -07003174 VmRequest::DiskCommand {
Daniel Verkamp92f73d72018-12-04 13:17:46 -08003175 disk_index,
Jakub Staronecf81e02019-04-11 11:43:39 -07003176 command: DiskControlCommand::Resize { new_size },
Daniel Verkamp92f73d72018-12-04 13:17:46 -08003177 }
3178 }
3179 _ => {
3180 error!("Unknown disk subcommand '{}'", subcommand);
3181 return Err(());
3182 }
3183 };
3184
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01003185 let socket_path = &args.next().unwrap();
3186 let socket_path = Path::new(&socket_path);
3187 vms_request(&request, socket_path)
Daniel Verkamp92f73d72018-12-04 13:17:46 -08003188}
3189
Keiichi Watanabe2400bd52021-08-04 19:18:02 +09003190fn make_rt(mut args: std::env::Args) -> std::result::Result<(), ()> {
3191 if args.len() == 0 {
3192 print_help("crosvm make_rt", "VM_SOCKET...", &[]);
3193 println!("Makes the crosvm instance listening on each `VM_SOCKET` given RT.");
3194 return Err(());
3195 }
3196 let socket_path = &args.next().unwrap();
3197 let socket_path = Path::new(&socket_path);
3198 vms_request(&VmRequest::MakeRT, socket_path)
3199}
3200
Jingkui Wang100e6e42019-03-08 20:41:57 -08003201fn parse_bus_id_addr(v: &str) -> ModifyUsbResult<(u8, u8, u16, u16)> {
3202 debug!("parse_bus_id_addr: {}", v);
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09003203 let mut ids = v.split(':');
Jingkui Wang100e6e42019-03-08 20:41:57 -08003204 match (ids.next(), ids.next(), ids.next(), ids.next()) {
3205 (Some(bus_id), Some(addr), Some(vid), Some(pid)) => {
3206 let bus_id = bus_id
3207 .parse::<u8>()
3208 .map_err(|e| ModifyUsbError::ArgParseInt("bus_id", bus_id.to_owned(), e))?;
3209 let addr = addr
3210 .parse::<u8>()
3211 .map_err(|e| ModifyUsbError::ArgParseInt("addr", addr.to_owned(), e))?;
Daniel Verkamp166d1dd2021-08-19 17:05:29 -07003212 let vid = u16::from_str_radix(vid, 16)
Jingkui Wang100e6e42019-03-08 20:41:57 -08003213 .map_err(|e| ModifyUsbError::ArgParseInt("vid", vid.to_owned(), e))?;
Daniel Verkamp166d1dd2021-08-19 17:05:29 -07003214 let pid = u16::from_str_radix(pid, 16)
Jingkui Wang100e6e42019-03-08 20:41:57 -08003215 .map_err(|e| ModifyUsbError::ArgParseInt("pid", pid.to_owned(), e))?;
3216 Ok((bus_id, addr, vid, pid))
3217 }
3218 _ => Err(ModifyUsbError::ArgParse(
3219 "BUS_ID_ADDR_BUS_NUM_DEV_NUM",
3220 v.to_owned(),
3221 )),
3222 }
3223}
3224
Jingkui Wang100e6e42019-03-08 20:41:57 -08003225fn usb_attach(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
3226 let val = args
3227 .next()
3228 .ok_or(ModifyUsbError::ArgMissing("BUS_ID_ADDR_BUS_NUM_DEV_NUM"))?;
3229 let (bus, addr, vid, pid) = parse_bus_id_addr(&val)?;
3230 let dev_path = PathBuf::from(
3231 args.next()
3232 .ok_or(ModifyUsbError::ArgMissing("usb device path"))?,
3233 );
Jingkui Wang100e6e42019-03-08 20:41:57 -08003234
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01003235 let socket_path = args
3236 .next()
3237 .ok_or(ModifyUsbError::ArgMissing("control socket path"))?;
3238 let socket_path = Path::new(&socket_path);
3239
Daniel Verkamp166d1dd2021-08-19 17:05:29 -07003240 do_usb_attach(socket_path, bus, addr, vid, pid, &dev_path)
Jingkui Wang100e6e42019-03-08 20:41:57 -08003241}
3242
3243fn usb_detach(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
3244 let port: u8 = args
3245 .next()
3246 .map_or(Err(ModifyUsbError::ArgMissing("PORT")), |p| {
3247 p.parse::<u8>()
3248 .map_err(|e| ModifyUsbError::ArgParseInt("PORT", p.to_owned(), e))
3249 })?;
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01003250 let socket_path = args
3251 .next()
3252 .ok_or(ModifyUsbError::ArgMissing("control socket path"))?;
3253 let socket_path = Path::new(&socket_path);
Daniel Verkamp166d1dd2021-08-19 17:05:29 -07003254 do_usb_detach(socket_path, port)
Jingkui Wang100e6e42019-03-08 20:41:57 -08003255}
3256
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01003257fn usb_list(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
3258 let socket_path = args
3259 .next()
3260 .ok_or(ModifyUsbError::ArgMissing("control socket path"))?;
3261 let socket_path = Path::new(&socket_path);
Daniel Verkamp166d1dd2021-08-19 17:05:29 -07003262 do_usb_list(socket_path)
Jingkui Wang100e6e42019-03-08 20:41:57 -08003263}
3264
3265fn modify_usb(mut args: std::env::Args) -> std::result::Result<(), ()> {
Zach Reizneraff94ca2019-03-18 20:58:31 -07003266 if args.len() < 2 {
Jingkui Wang100e6e42019-03-08 20:41:57 -08003267 print_help("crosvm usb",
Zach Reizneraff94ca2019-03-18 20:58:31 -07003268 "[attach BUS_ID:ADDR:VENDOR_ID:PRODUCT_ID [USB_DEVICE_PATH|-] | detach PORT | list] VM_SOCKET...", &[]);
Jingkui Wang100e6e42019-03-08 20:41:57 -08003269 return Err(());
3270 }
3271
3272 // This unwrap will not panic because of the above length check.
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01003273 let command = &args.next().unwrap();
Jingkui Wang100e6e42019-03-08 20:41:57 -08003274 let result = match command.as_ref() {
3275 "attach" => usb_attach(args),
3276 "detach" => usb_detach(args),
3277 "list" => usb_list(args),
3278 other => Err(ModifyUsbError::UnknownCommand(other.to_owned())),
3279 };
3280 match result {
3281 Ok(response) => {
3282 println!("{}", response);
3283 Ok(())
3284 }
3285 Err(e) => {
3286 println!("error {}", e);
3287 Err(())
3288 }
3289 }
3290}
3291
Keiichi Watanabe2400bd52021-08-04 19:18:02 +09003292#[allow(clippy::unnecessary_wraps)]
3293fn pkg_version() -> std::result::Result<(), ()> {
3294 const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION");
3295 const PKG_VERSION: Option<&'static str> = option_env!("PKG_VERSION");
3296
3297 print!("crosvm {}", VERSION.unwrap_or("UNKNOWN"));
3298 match PKG_VERSION {
3299 Some(v) => println!("-{}", v),
3300 None => println!(),
3301 }
3302 Ok(())
3303}
3304
Zach Reiznerefe95782017-08-26 18:05:48 -07003305fn print_usage() {
Dmitry Torokhovdacf1042021-12-09 15:36:01 -08003306 print_help("crosvm", "[--extended-status] [command]", &[]);
Zach Reiznerefe95782017-08-26 18:05:48 -07003307 println!("Commands:");
Junichi Uekawaf0a77232021-04-08 16:44:44 +09003308 println!(" balloon - Set balloon size of the crosvm instance.");
3309 println!(" balloon_stats - Prints virtio balloon statistics.");
3310 println!(" battery - Modify battery.");
Andrew Walbranfb7c1082021-06-17 18:12:14 +01003311 #[cfg(feature = "composite-disk")]
3312 println!(" create_composite - Create a new composite disk image file.");
Dylan Reid2dcb6322018-07-13 10:42:48 -07003313 println!(" create_qcow2 - Create a new qcow2 disk image file.");
Keiichi Watanabeee4b58e2021-08-17 19:34:01 +09003314 println!(" device - Start a device process.");
Jingkui Wang100e6e42019-03-08 20:41:57 -08003315 println!(" disk - Manage attached virtual disk devices.");
Keiichi Watanabe9568bb42021-08-16 17:59:30 +09003316 println!(
3317 " make_rt - Enables real-time vcpu priority for crosvm instances started with \
3318 `--delay-rt`."
3319 );
Junichi Uekawaf0a77232021-04-08 16:44:44 +09003320 println!(" resume - Resumes the crosvm instance.");
3321 println!(" run - Start a new crosvm instance.");
3322 println!(" stop - Stops crosvm instances via their control sockets.");
3323 println!(" suspend - Suspends the crosvm instance.");
Jingkui Wang100e6e42019-03-08 20:41:57 -08003324 println!(" usb - Manage attached virtual USB devices.");
Yi Sun54305cd2020-01-04 00:19:37 +08003325 println!(" version - Show package version.");
Xiong Zhangc78e72b2021-04-08 11:31:41 +08003326 println!(" vfio - add/remove host vfio pci device into guest.");
Yi Sun54305cd2020-01-04 00:19:37 +08003327}
3328
Dmitry Torokhovf75699f2021-12-03 11:19:13 -08003329fn crosvm_main() -> std::result::Result<CommandStatus, ()> {
Stephen Barber56fbf092017-06-29 16:12:14 -07003330 if let Err(e) = syslog::init() {
David Tolnayb4bd00f2019-02-12 17:51:26 -08003331 println!("failed to initialize syslog: {}", e);
Dylan Reidbfba9932018-02-05 15:51:59 -08003332 return Err(());
Stephen Barber56fbf092017-06-29 16:12:14 -07003333 }
Zach Reizner639d9672017-05-01 17:57:18 -07003334
Zach Reiznerb3fa5c92019-01-28 14:05:23 -08003335 panic_hook::set_panic_hook();
3336
Zach Reiznerefe95782017-08-26 18:05:48 -07003337 let mut args = std::env::args();
3338 if args.next().is_none() {
3339 error!("expected executable name");
Dylan Reidbfba9932018-02-05 15:51:59 -08003340 return Err(());
Zach Reizner639d9672017-05-01 17:57:18 -07003341 }
Zach Reiznerefe95782017-08-26 18:05:48 -07003342
Dmitry Torokhovdacf1042021-12-09 15:36:01 -08003343 let mut cmd_arg = args.next();
3344 let extended_status = match cmd_arg.as_ref().map(|s| s.as_ref()) {
3345 Some("--extended-status") => {
3346 cmd_arg = args.next();
3347 true
3348 }
3349 _ => false,
Dmitry Torokhovdd8a12c2021-12-09 15:36:01 -08003350 };
Dmitry Torokhovdacf1042021-12-09 15:36:01 -08003351
3352 let command = match cmd_arg {
Dmitry Torokhovf75699f2021-12-03 11:19:13 -08003353 Some(c) => c,
Dylan Reidbfba9932018-02-05 15:51:59 -08003354 None => {
3355 print_usage();
Dmitry Torokhovf75699f2021-12-03 11:19:13 -08003356 return Ok(CommandStatus::Success);
Dylan Reidbfba9932018-02-05 15:51:59 -08003357 }
Dmitry Torokhovf75699f2021-12-03 11:19:13 -08003358 };
3359
3360 // Past this point, usage of exit is in danger of leaking zombie processes.
3361 let ret = if command == "run" {
Dmitry Torokhovdacf1042021-12-09 15:36:01 -08003362 // We handle run_vm separately because it does not simply signal success/error
Dmitry Torokhovf75699f2021-12-03 11:19:13 -08003363 // but also indicates whether the guest requested reset or stop.
3364 run_vm(args)
3365 } else {
3366 match &command[..] {
3367 "balloon" => balloon_vms(args),
3368 "balloon_stats" => balloon_stats(args),
3369 "battery" => modify_battery(args),
3370 #[cfg(feature = "composite-disk")]
3371 "create_composite" => create_composite(args),
3372 "create_qcow2" => create_qcow2(args),
3373 "device" => start_device(args),
3374 "disk" => disk_cmd(args),
3375 "make_rt" => make_rt(args),
3376 "resume" => resume_vms(args),
3377 "stop" => stop_vms(args),
3378 "suspend" => suspend_vms(args),
3379 "usb" => modify_usb(args),
3380 "version" => pkg_version(),
Xiong Zhangc78e72b2021-04-08 11:31:41 +08003381 "vfio" => modify_vfio(args),
Dmitry Torokhovf75699f2021-12-03 11:19:13 -08003382 c => {
3383 println!("invalid subcommand: {:?}", c);
3384 print_usage();
3385 Err(())
3386 }
Zach Reiznerefe95782017-08-26 18:05:48 -07003387 }
Dmitry Torokhovf75699f2021-12-03 11:19:13 -08003388 .map(|_| CommandStatus::Success)
Dylan Reidbfba9932018-02-05 15:51:59 -08003389 };
Zach Reiznerefe95782017-08-26 18:05:48 -07003390
3391 // Reap exit status from any child device processes. At this point, all devices should have been
3392 // dropped in the main process and told to shutdown. Try over a period of 100ms, since it may
3393 // take some time for the processes to shut down.
3394 if !wait_all_children() {
3395 // We gave them a chance, and it's too late.
3396 warn!("not all child processes have exited; sending SIGKILL");
3397 if let Err(e) = kill_process_group() {
3398 // We're now at the mercy of the OS to clean up after us.
David Tolnayb4bd00f2019-02-12 17:51:26 -08003399 warn!("unable to kill all child processes: {}", e);
Zach Reiznerefe95782017-08-26 18:05:48 -07003400 }
3401 }
3402
3403 // WARNING: Any code added after this point is not guaranteed to run
3404 // since we may forcibly kill this process (and its children) above.
Dmitry Torokhovdacf1042021-12-09 15:36:01 -08003405 ret.map(|s| {
3406 if extended_status {
3407 s
3408 } else {
3409 CommandStatus::Success
3410 }
3411 })
Dylan Reidbfba9932018-02-05 15:51:59 -08003412}
3413
3414fn main() {
Dmitry Torokhovf75699f2021-12-03 11:19:13 -08003415 let exit_code = match crosvm_main() {
3416 Ok(CommandStatus::Success | CommandStatus::VmStop) => 0,
3417 Ok(CommandStatus::VmReset) => 32,
Andrew Walbran1a19c672022-01-24 17:24:10 +00003418 Ok(CommandStatus::VmCrash) => 33,
Dmitry Torokhovf75699f2021-12-03 11:19:13 -08003419 Err(_) => 1,
3420 };
3421 std::process::exit(exit_code);
Zach Reizner639d9672017-05-01 17:57:18 -07003422}
Daniel Verkamp107edb32019-04-05 09:58:48 -07003423
3424#[cfg(test)]
3425mod tests {
3426 use super::*;
Kaiyi Libccb4eb2020-02-06 17:53:11 -08003427 use crosvm::{DEFAULT_TOUCH_DEVICE_HEIGHT, DEFAULT_TOUCH_DEVICE_WIDTH};
Daniel Verkamp107edb32019-04-05 09:58:48 -07003428
3429 #[test]
3430 fn parse_cpu_set_single() {
3431 assert_eq!(parse_cpu_set("123").expect("parse failed"), vec![123]);
3432 }
3433
3434 #[test]
3435 fn parse_cpu_set_list() {
3436 assert_eq!(
3437 parse_cpu_set("0,1,2,3").expect("parse failed"),
3438 vec![0, 1, 2, 3]
3439 );
3440 }
3441
3442 #[test]
3443 fn parse_cpu_set_range() {
3444 assert_eq!(
3445 parse_cpu_set("0-3").expect("parse failed"),
3446 vec![0, 1, 2, 3]
3447 );
3448 }
3449
3450 #[test]
3451 fn parse_cpu_set_list_of_ranges() {
3452 assert_eq!(
3453 parse_cpu_set("3-4,7-9,18").expect("parse failed"),
3454 vec![3, 4, 7, 8, 9, 18]
3455 );
3456 }
3457
3458 #[test]
3459 fn parse_cpu_set_repeated() {
3460 // For now, allow duplicates - they will be handled gracefully by the vec to cpu_set_t conversion.
3461 assert_eq!(parse_cpu_set("1,1,1").expect("parse failed"), vec![1, 1, 1]);
3462 }
3463
3464 #[test]
3465 fn parse_cpu_set_negative() {
3466 // Negative CPU numbers are not allowed.
3467 parse_cpu_set("-3").expect_err("parse should have failed");
3468 }
3469
3470 #[test]
3471 fn parse_cpu_set_reverse_range() {
3472 // Ranges must be from low to high.
3473 parse_cpu_set("5-2").expect_err("parse should have failed");
3474 }
3475
3476 #[test]
3477 fn parse_cpu_set_open_range() {
3478 parse_cpu_set("3-").expect_err("parse should have failed");
3479 }
3480
3481 #[test]
3482 fn parse_cpu_set_extra_comma() {
3483 parse_cpu_set("0,1,2,").expect_err("parse should have failed");
3484 }
Trent Begin17ccaad2019-04-17 13:51:25 -06003485
Daniel Verkampc677fb42020-09-08 13:47:49 -07003486 #[test]
3487 fn parse_cpu_affinity_global() {
3488 assert_eq!(
3489 parse_cpu_affinity("0,5-7,9").expect("parse failed"),
3490 VcpuAffinity::Global(vec![0, 5, 6, 7, 9]),
3491 );
3492 }
3493
3494 #[test]
3495 fn parse_cpu_affinity_per_vcpu_one_to_one() {
3496 let mut expected_map = BTreeMap::new();
3497 expected_map.insert(0, vec![0]);
3498 expected_map.insert(1, vec![1]);
3499 expected_map.insert(2, vec![2]);
3500 expected_map.insert(3, vec![3]);
3501 assert_eq!(
3502 parse_cpu_affinity("0=0:1=1:2=2:3=3").expect("parse failed"),
3503 VcpuAffinity::PerVcpu(expected_map),
3504 );
3505 }
3506
3507 #[test]
3508 fn parse_cpu_affinity_per_vcpu_sets() {
3509 let mut expected_map = BTreeMap::new();
3510 expected_map.insert(0, vec![0, 1, 2]);
3511 expected_map.insert(1, vec![3, 4, 5]);
3512 expected_map.insert(2, vec![6, 7, 8]);
3513 assert_eq!(
3514 parse_cpu_affinity("0=0,1,2:1=3-5:2=6,7-8").expect("parse failed"),
3515 VcpuAffinity::PerVcpu(expected_map),
3516 );
3517 }
3518
Dennis Kempin50a58f92021-06-23 11:34:31 -07003519 #[cfg(feature = "audio_cras")]
Trent Begin17ccaad2019-04-17 13:51:25 -06003520 #[test]
Judy Hsiaod5c1e962020-02-04 12:30:01 +08003521 fn parse_ac97_vaild() {
3522 parse_ac97_options("backend=cras").expect("parse should have succeded");
3523 }
3524
Andrew Scull1590e6f2020-03-18 18:00:47 +00003525 #[cfg(feature = "audio")]
Judy Hsiaod5c1e962020-02-04 12:30:01 +08003526 #[test]
3527 fn parse_ac97_null_vaild() {
3528 parse_ac97_options("backend=null").expect("parse should have succeded");
3529 }
3530
Dennis Kempin50a58f92021-06-23 11:34:31 -07003531 #[cfg(feature = "audio_cras")]
Judy Hsiaod5c1e962020-02-04 12:30:01 +08003532 #[test]
Judy Hsiaob4b94c72020-09-07 15:56:00 +08003533 fn parse_ac97_capture_vaild() {
3534 parse_ac97_options("backend=cras,capture=true").expect("parse should have succeded");
Judy Hsiaod5c1e962020-02-04 12:30:01 +08003535 }
3536
Dennis Kempin50a58f92021-06-23 11:34:31 -07003537 #[cfg(feature = "audio_cras")]
Jorge E. Moreira359e7de2020-12-02 18:25:53 -08003538 #[test]
paulhsia83d51602021-03-09 17:13:14 +08003539 fn parse_ac97_client_type() {
3540 parse_ac97_options("backend=cras,capture=true,client_type=crosvm")
3541 .expect("parse should have succeded");
3542 parse_ac97_options("backend=cras,capture=true,client_type=arcvm")
3543 .expect("parse should have succeded");
3544 parse_ac97_options("backend=cras,capture=true,client_type=none")
3545 .expect_err("parse should have failed");
3546 }
3547
Woody Chowb27dea42021-09-08 15:51:22 +09003548 #[cfg(feature = "audio_cras")]
3549 #[test]
3550 fn parse_ac97_socket_type() {
3551 parse_ac97_options("socket_type=unified").expect("parse should have succeded");
3552 parse_ac97_options("socket_type=legacy").expect("parse should have succeded");
3553 }
3554
paulhsia83d51602021-03-09 17:13:14 +08003555 #[cfg(feature = "audio")]
3556 #[test]
Jorge E. Moreira359e7de2020-12-02 18:25:53 -08003557 fn parse_ac97_vios_valid() {
3558 parse_ac97_options("backend=vios,server=/path/to/server")
3559 .expect("parse should have succeded");
3560 }
3561
Judy Hsiaod5c1e962020-02-04 12:30:01 +08003562 #[test]
Trent Begin17ccaad2019-04-17 13:51:25 -06003563 fn parse_serial_vaild() {
Jorge E. Moreira1e262302019-08-01 14:40:03 -07003564 parse_serial_options("type=syslog,num=1,console=true,stdin=true")
3565 .expect("parse should have succeded");
Trent Begin17ccaad2019-04-17 13:51:25 -06003566 }
3567
3568 #[test]
A. Cody Schuffelen3faab5a2020-10-05 17:29:16 -07003569 fn parse_serial_virtio_console_vaild() {
3570 parse_serial_options("type=syslog,num=5,console=true,stdin=true,hardware=virtio-console")
3571 .expect("parse should have succeded");
3572 }
3573
3574 #[test]
Trent Begin923bab02019-06-17 13:48:06 -06003575 fn parse_serial_valid_no_num() {
3576 parse_serial_options("type=syslog").expect("parse should have succeded");
3577 }
3578
3579 #[test]
Nicholas Hollingumc76c2da2020-09-18 15:53:16 +10003580 fn parse_serial_equals_in_value() {
3581 let parsed = parse_serial_options("type=syslog,path=foo=bar==.log")
3582 .expect("parse should have succeded");
3583 assert_eq!(parsed.path, Some(PathBuf::from("foo=bar==.log")));
3584 }
3585
3586 #[test]
Trent Begin17ccaad2019-04-17 13:51:25 -06003587 fn parse_serial_invalid_type() {
3588 parse_serial_options("type=wormhole,num=1").expect_err("parse should have failed");
3589 }
3590
3591 #[test]
3592 fn parse_serial_invalid_num_upper() {
3593 parse_serial_options("type=syslog,num=5").expect_err("parse should have failed");
3594 }
3595
3596 #[test]
3597 fn parse_serial_invalid_num_lower() {
3598 parse_serial_options("type=syslog,num=0").expect_err("parse should have failed");
3599 }
3600
3601 #[test]
A. Cody Schuffelen3faab5a2020-10-05 17:29:16 -07003602 fn parse_serial_virtio_console_invalid_num_lower() {
3603 parse_serial_options("type=syslog,hardware=virtio-console,num=0")
3604 .expect_err("parse should have failed");
3605 }
3606
3607 #[test]
Trent Begin17ccaad2019-04-17 13:51:25 -06003608 fn parse_serial_invalid_num_string() {
3609 parse_serial_options("type=syslog,num=number3").expect_err("parse should have failed");
3610 }
3611
3612 #[test]
3613 fn parse_serial_invalid_option() {
3614 parse_serial_options("type=syslog,speed=lightspeed").expect_err("parse should have failed");
3615 }
Jorge E. Moreira1e262302019-08-01 14:40:03 -07003616
3617 #[test]
3618 fn parse_serial_invalid_two_stdin() {
3619 let mut config = Config::default();
3620 set_argument(&mut config, "serial", Some("num=1,type=stdout,stdin=true"))
3621 .expect("should parse the first serial argument");
3622 set_argument(&mut config, "serial", Some("num=2,type=stdout,stdin=true"))
3623 .expect_err("should fail to parse a second serial port connected to stdin");
3624 }
Dmitry Torokhov458bb642019-12-13 11:47:52 -08003625
3626 #[test]
3627 fn parse_plugin_mount_valid() {
3628 let mut config = Config::default();
3629 set_argument(
3630 &mut config,
3631 "plugin-mount",
3632 Some("/dev/null:/dev/zero:true"),
3633 )
3634 .expect("parse should succeed");
3635 assert_eq!(config.plugin_mounts[0].src, PathBuf::from("/dev/null"));
3636 assert_eq!(config.plugin_mounts[0].dst, PathBuf::from("/dev/zero"));
Daniel Verkamp5e9959e2021-08-19 17:09:59 -07003637 assert!(config.plugin_mounts[0].writable);
Dmitry Torokhov458bb642019-12-13 11:47:52 -08003638 }
3639
3640 #[test]
3641 fn parse_plugin_mount_valid_shorthand() {
3642 let mut config = Config::default();
3643 set_argument(&mut config, "plugin-mount", Some("/dev/null")).expect("parse should succeed");
3644 assert_eq!(config.plugin_mounts[0].dst, PathBuf::from("/dev/null"));
Daniel Verkamp5e9959e2021-08-19 17:09:59 -07003645 assert!(!config.plugin_mounts[0].writable);
Dmitry Torokhov458bb642019-12-13 11:47:52 -08003646 set_argument(&mut config, "plugin-mount", Some("/dev/null:/dev/zero"))
3647 .expect("parse should succeed");
3648 assert_eq!(config.plugin_mounts[1].dst, PathBuf::from("/dev/zero"));
Daniel Verkamp5e9959e2021-08-19 17:09:59 -07003649 assert!(!config.plugin_mounts[1].writable);
Dmitry Torokhov458bb642019-12-13 11:47:52 -08003650 set_argument(&mut config, "plugin-mount", Some("/dev/null::true"))
3651 .expect("parse should succeed");
3652 assert_eq!(config.plugin_mounts[2].dst, PathBuf::from("/dev/null"));
Daniel Verkamp5e9959e2021-08-19 17:09:59 -07003653 assert!(config.plugin_mounts[2].writable);
Dmitry Torokhov458bb642019-12-13 11:47:52 -08003654 }
3655
3656 #[test]
3657 fn parse_plugin_mount_invalid() {
3658 let mut config = Config::default();
3659 set_argument(&mut config, "plugin-mount", Some("")).expect_err("parse should fail");
3660 set_argument(
3661 &mut config,
3662 "plugin-mount",
3663 Some("/dev/null:/dev/null:true:false"),
3664 )
3665 .expect_err("parse should fail because too many arguments");
3666 set_argument(&mut config, "plugin-mount", Some("null:/dev/null:true"))
3667 .expect_err("parse should fail because source is not absolute");
3668 set_argument(&mut config, "plugin-mount", Some("/dev/null:null:true"))
3669 .expect_err("parse should fail because source is not absolute");
3670 set_argument(&mut config, "plugin-mount", Some("/dev/null:null:blah"))
3671 .expect_err("parse should fail because flag is not boolean");
3672 }
3673
3674 #[test]
3675 fn parse_plugin_gid_map_valid() {
3676 let mut config = Config::default();
3677 set_argument(&mut config, "plugin-gid-map", Some("1:2:3")).expect("parse should succeed");
3678 assert_eq!(config.plugin_gid_maps[0].inner, 1);
3679 assert_eq!(config.plugin_gid_maps[0].outer, 2);
3680 assert_eq!(config.plugin_gid_maps[0].count, 3);
3681 }
3682
3683 #[test]
3684 fn parse_plugin_gid_map_valid_shorthand() {
3685 let mut config = Config::default();
3686 set_argument(&mut config, "plugin-gid-map", Some("1")).expect("parse should succeed");
3687 assert_eq!(config.plugin_gid_maps[0].inner, 1);
3688 assert_eq!(config.plugin_gid_maps[0].outer, 1);
3689 assert_eq!(config.plugin_gid_maps[0].count, 1);
3690 set_argument(&mut config, "plugin-gid-map", Some("1:2")).expect("parse should succeed");
3691 assert_eq!(config.plugin_gid_maps[1].inner, 1);
3692 assert_eq!(config.plugin_gid_maps[1].outer, 2);
3693 assert_eq!(config.plugin_gid_maps[1].count, 1);
3694 set_argument(&mut config, "plugin-gid-map", Some("1::3")).expect("parse should succeed");
3695 assert_eq!(config.plugin_gid_maps[2].inner, 1);
3696 assert_eq!(config.plugin_gid_maps[2].outer, 1);
3697 assert_eq!(config.plugin_gid_maps[2].count, 3);
3698 }
3699
3700 #[test]
3701 fn parse_plugin_gid_map_invalid() {
3702 let mut config = Config::default();
3703 set_argument(&mut config, "plugin-gid-map", Some("")).expect_err("parse should fail");
3704 set_argument(&mut config, "plugin-gid-map", Some("1:2:3:4"))
3705 .expect_err("parse should fail because too many arguments");
3706 set_argument(&mut config, "plugin-gid-map", Some("blah:2:3"))
3707 .expect_err("parse should fail because inner is not a number");
3708 set_argument(&mut config, "plugin-gid-map", Some("1:blah:3"))
3709 .expect_err("parse should fail because outer is not a number");
3710 set_argument(&mut config, "plugin-gid-map", Some("1:2:blah"))
3711 .expect_err("parse should fail because count is not a number");
3712 }
Kaiyi Libccb4eb2020-02-06 17:53:11 -08003713
3714 #[test]
3715 fn single_touch_spec_and_track_pad_spec_default_size() {
3716 let mut config = Config::default();
3717 config
3718 .executable_path
3719 .replace(Executable::Kernel(PathBuf::from("kernel")));
3720 set_argument(&mut config, "single-touch", Some("/dev/single-touch-test")).unwrap();
3721 set_argument(&mut config, "trackpad", Some("/dev/single-touch-test")).unwrap();
3722 validate_arguments(&mut config).unwrap();
3723 assert_eq!(
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07003724 config.virtio_single_touch.first().unwrap().get_size(),
Kaiyi Libccb4eb2020-02-06 17:53:11 -08003725 (DEFAULT_TOUCH_DEVICE_WIDTH, DEFAULT_TOUCH_DEVICE_HEIGHT)
3726 );
3727 assert_eq!(
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07003728 config.virtio_trackpad.first().unwrap().get_size(),
Kaiyi Libccb4eb2020-02-06 17:53:11 -08003729 (DEFAULT_TOUCH_DEVICE_WIDTH, DEFAULT_TOUCH_DEVICE_HEIGHT)
3730 );
3731 }
3732
3733 #[cfg(feature = "gpu")]
3734 #[test]
3735 fn single_touch_spec_default_size_from_gpu() {
3736 let width = 12345u32;
3737 let height = 54321u32;
3738 let mut config = Config::default();
3739 config
3740 .executable_path
3741 .replace(Executable::Kernel(PathBuf::from("kernel")));
3742 set_argument(&mut config, "single-touch", Some("/dev/single-touch-test")).unwrap();
3743 set_argument(
3744 &mut config,
3745 "gpu",
3746 Some(&format!("width={},height={}", width, height)),
3747 )
3748 .unwrap();
3749 validate_arguments(&mut config).unwrap();
3750 assert_eq!(
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07003751 config.virtio_single_touch.first().unwrap().get_size(),
Kaiyi Libccb4eb2020-02-06 17:53:11 -08003752 (width, height)
3753 );
3754 }
3755
3756 #[test]
3757 fn single_touch_spec_and_track_pad_spec_with_size() {
3758 let width = 12345u32;
3759 let height = 54321u32;
3760 let mut config = Config::default();
3761 config
3762 .executable_path
3763 .replace(Executable::Kernel(PathBuf::from("kernel")));
3764 set_argument(
3765 &mut config,
3766 "single-touch",
3767 Some(&format!("/dev/single-touch-test:{}:{}", width, height)),
3768 )
3769 .unwrap();
3770 set_argument(
3771 &mut config,
3772 "trackpad",
3773 Some(&format!("/dev/single-touch-test:{}:{}", width, height)),
3774 )
3775 .unwrap();
3776 validate_arguments(&mut config).unwrap();
3777 assert_eq!(
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07003778 config.virtio_single_touch.first().unwrap().get_size(),
Kaiyi Libccb4eb2020-02-06 17:53:11 -08003779 (width, height)
3780 );
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07003781 assert_eq!(
3782 config.virtio_trackpad.first().unwrap().get_size(),
3783 (width, height)
3784 );
Kaiyi Libccb4eb2020-02-06 17:53:11 -08003785 }
3786
3787 #[cfg(feature = "gpu")]
3788 #[test]
3789 fn single_touch_spec_with_size_independent_from_gpu() {
3790 let touch_width = 12345u32;
3791 let touch_height = 54321u32;
3792 let display_width = 1234u32;
3793 let display_height = 5432u32;
3794 let mut config = Config::default();
3795 config
3796 .executable_path
3797 .replace(Executable::Kernel(PathBuf::from("kernel")));
3798 set_argument(
3799 &mut config,
3800 "single-touch",
3801 Some(&format!(
3802 "/dev/single-touch-test:{}:{}",
3803 touch_width, touch_height
3804 )),
3805 )
3806 .unwrap();
3807 set_argument(
3808 &mut config,
3809 "gpu",
3810 Some(&format!(
3811 "width={},height={}",
3812 display_width, display_height
3813 )),
3814 )
3815 .unwrap();
3816 validate_arguments(&mut config).unwrap();
3817 assert_eq!(
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07003818 config.virtio_single_touch.first().unwrap().get_size(),
Kaiyi Libccb4eb2020-02-06 17:53:11 -08003819 (touch_width, touch_height)
3820 );
3821 }
Kaiyi Lidd348a42020-07-13 11:49:46 -07003822
Daniel Norman5e23df72021-03-11 10:11:02 -08003823 #[test]
3824 fn virtio_switches() {
3825 let mut config = Config::default();
3826 config
3827 .executable_path
3828 .replace(Executable::Kernel(PathBuf::from("kernel")));
3829 set_argument(&mut config, "switches", Some("/dev/switches-test")).unwrap();
3830 validate_arguments(&mut config).unwrap();
3831 assert_eq!(
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07003832 config.virtio_switches.pop().unwrap(),
Keiichi Watanabed56a2f42021-03-18 20:16:23 +09003833 PathBuf::from("/dev/switches-test")
3834 );
Daniel Norman5e23df72021-03-11 10:11:02 -08003835 }
3836
Chia-I Wu6d473b32021-04-12 10:14:24 -07003837 #[cfg(feature = "gpu")]
Kaiyi Lidd348a42020-07-13 11:49:46 -07003838 #[test]
Chia-I Wu91df6562021-04-12 09:47:38 -07003839 fn parse_gpu_options_default_vulkan_support() {
Jason Macnakd659a0d2021-03-15 15:33:01 -07003840 {
3841 let mut gpu_params: GpuParameters = Default::default();
3842 assert!(parse_gpu_options(Some("backend=virglrenderer"), &mut gpu_params).is_ok());
3843 assert!(!gpu_params.use_vulkan);
3844 }
Chia-I Wu6d473b32021-04-12 10:14:24 -07003845
3846 #[cfg(feature = "gfxstream")]
Jason Macnakd659a0d2021-03-15 15:33:01 -07003847 {
3848 let mut gpu_params: GpuParameters = Default::default();
3849 assert!(parse_gpu_options(Some("backend=gfxstream"), &mut gpu_params).is_ok());
3850 assert!(gpu_params.use_vulkan);
3851 }
Chia-I Wu91df6562021-04-12 09:47:38 -07003852 }
3853
Chia-I Wu6d473b32021-04-12 10:14:24 -07003854 #[cfg(feature = "gpu")]
Chia-I Wu91df6562021-04-12 09:47:38 -07003855 #[test]
Chia-I Wu6d473b32021-04-12 10:14:24 -07003856 fn parse_gpu_options_with_vulkan_specified() {
Jason Macnakd659a0d2021-03-15 15:33:01 -07003857 {
3858 let mut gpu_params: GpuParameters = Default::default();
3859 assert!(parse_gpu_options(Some("vulkan=true"), &mut gpu_params).is_ok());
3860 assert!(gpu_params.use_vulkan);
3861 }
3862 {
3863 let mut gpu_params: GpuParameters = Default::default();
3864 assert!(
3865 parse_gpu_options(Some("backend=virglrenderer,vulkan=true"), &mut gpu_params)
3866 .is_ok()
3867 );
3868 assert!(gpu_params.use_vulkan);
3869 }
3870 {
3871 let mut gpu_params: GpuParameters = Default::default();
3872 assert!(
3873 parse_gpu_options(Some("vulkan=true,backend=virglrenderer"), &mut gpu_params)
3874 .is_ok()
3875 );
3876 assert!(gpu_params.use_vulkan);
3877 }
3878 {
3879 let mut gpu_params: GpuParameters = Default::default();
3880 assert!(parse_gpu_options(Some("vulkan=false"), &mut gpu_params).is_ok());
3881 assert!(!gpu_params.use_vulkan);
3882 }
3883 {
3884 let mut gpu_params: GpuParameters = Default::default();
3885 assert!(
3886 parse_gpu_options(Some("backend=virglrenderer,vulkan=false"), &mut gpu_params)
3887 .is_ok()
3888 );
3889 assert!(!gpu_params.use_vulkan);
3890 }
3891 {
3892 let mut gpu_params: GpuParameters = Default::default();
3893 assert!(
3894 parse_gpu_options(Some("vulkan=false,backend=virglrenderer"), &mut gpu_params)
3895 .is_ok()
3896 );
3897 assert!(!gpu_params.use_vulkan);
3898 }
3899 {
3900 let mut gpu_params: GpuParameters = Default::default();
3901 assert!(parse_gpu_options(
3902 Some("backend=virglrenderer,vulkan=invalid_value"),
3903 &mut gpu_params
3904 )
3905 .is_err());
3906 }
3907 {
3908 let mut gpu_params: GpuParameters = Default::default();
3909 assert!(parse_gpu_options(
3910 Some("vulkan=invalid_value,backend=virglrenderer"),
3911 &mut gpu_params
3912 )
3913 .is_err());
3914 }
Kaiyi Lidd348a42020-07-13 11:49:46 -07003915 }
3916
3917 #[cfg(all(feature = "gpu", feature = "gfxstream"))]
3918 #[test]
3919 fn parse_gpu_options_gfxstream_with_syncfd_specified() {
Jason Macnakd659a0d2021-03-15 15:33:01 -07003920 {
3921 let mut gpu_params: GpuParameters = Default::default();
3922 assert!(
3923 parse_gpu_options(Some("backend=gfxstream,syncfd=true"), &mut gpu_params).is_ok()
3924 );
3925 assert!(gpu_params.gfxstream_use_syncfd);
3926 }
3927 {
3928 let mut gpu_params: GpuParameters = Default::default();
3929 assert!(
3930 parse_gpu_options(Some("syncfd=true,backend=gfxstream"), &mut gpu_params).is_ok()
3931 );
3932 assert!(gpu_params.gfxstream_use_syncfd);
3933 }
3934 {
3935 let mut gpu_params: GpuParameters = Default::default();
3936 assert!(
3937 parse_gpu_options(Some("backend=gfxstream,syncfd=false"), &mut gpu_params).is_ok()
3938 );
3939 assert!(!gpu_params.gfxstream_use_syncfd);
3940 }
3941 {
3942 let mut gpu_params: GpuParameters = Default::default();
3943 assert!(
3944 parse_gpu_options(Some("syncfd=false,backend=gfxstream"), &mut gpu_params).is_ok()
3945 );
3946 assert!(!gpu_params.gfxstream_use_syncfd);
3947 }
3948 {
3949 let mut gpu_params: GpuParameters = Default::default();
3950 assert!(parse_gpu_options(
3951 Some("backend=gfxstream,syncfd=invalid_value"),
3952 &mut gpu_params
3953 )
3954 .is_err());
3955 }
3956 {
3957 let mut gpu_params: GpuParameters = Default::default();
3958 assert!(parse_gpu_options(
3959 Some("syncfd=invalid_value,backend=gfxstream"),
3960 &mut gpu_params
3961 )
3962 .is_err());
3963 }
Kaiyi Lidd348a42020-07-13 11:49:46 -07003964 }
3965
3966 #[cfg(all(feature = "gpu", feature = "gfxstream"))]
3967 #[test]
3968 fn parse_gpu_options_not_gfxstream_with_syncfd_specified() {
Jason Macnakd659a0d2021-03-15 15:33:01 -07003969 {
3970 let mut gpu_params: GpuParameters = Default::default();
3971 assert!(
3972 parse_gpu_options(Some("backend=virglrenderer,syncfd=true"), &mut gpu_params)
3973 .is_err()
3974 );
3975 }
3976 {
3977 let mut gpu_params: GpuParameters = Default::default();
3978 assert!(
3979 parse_gpu_options(Some("syncfd=true,backend=virglrenderer"), &mut gpu_params)
3980 .is_err()
3981 );
3982 }
3983 }
3984
3985 #[cfg(feature = "gpu")]
3986 #[test]
3987 fn parse_gpu_display_options_valid() {
3988 {
3989 let mut gpu_params: GpuParameters = Default::default();
3990 assert!(
3991 parse_gpu_display_options(Some("width=500,height=600"), &mut gpu_params).is_ok()
3992 );
3993 assert_eq!(gpu_params.displays.len(), 1);
3994 assert_eq!(gpu_params.displays[0].width, 500);
3995 assert_eq!(gpu_params.displays[0].height, 600);
3996 }
3997 }
3998
3999 #[cfg(feature = "gpu")]
4000 #[test]
4001 fn parse_gpu_display_options_invalid() {
4002 {
4003 let mut gpu_params: GpuParameters = Default::default();
4004 assert!(parse_gpu_display_options(Some("width=500"), &mut gpu_params).is_err());
4005 }
4006 {
4007 let mut gpu_params: GpuParameters = Default::default();
4008 assert!(parse_gpu_display_options(Some("height=500"), &mut gpu_params).is_err());
4009 }
4010 {
4011 let mut gpu_params: GpuParameters = Default::default();
4012 assert!(parse_gpu_display_options(Some("width"), &mut gpu_params).is_err());
4013 }
4014 {
4015 let mut gpu_params: GpuParameters = Default::default();
4016 assert!(parse_gpu_display_options(Some("blah"), &mut gpu_params).is_err());
4017 }
4018 }
4019
4020 #[cfg(feature = "gpu")]
4021 #[test]
4022 fn parse_gpu_options_and_gpu_display_options_valid() {
4023 {
4024 let mut gpu_params: GpuParameters = Default::default();
4025 assert!(parse_gpu_options(Some("2D,width=500,height=600"), &mut gpu_params).is_ok());
4026 assert!(
4027 parse_gpu_display_options(Some("width=700,height=800"), &mut gpu_params).is_ok()
4028 );
4029 assert_eq!(gpu_params.displays.len(), 2);
4030 assert_eq!(gpu_params.displays[0].width, 500);
4031 assert_eq!(gpu_params.displays[0].height, 600);
4032 assert_eq!(gpu_params.displays[1].width, 700);
4033 assert_eq!(gpu_params.displays[1].height, 800);
4034 }
4035 {
4036 let mut gpu_params: GpuParameters = Default::default();
4037 assert!(parse_gpu_options(Some("2D"), &mut gpu_params).is_ok());
4038 assert!(
4039 parse_gpu_display_options(Some("width=700,height=800"), &mut gpu_params).is_ok()
4040 );
4041 assert_eq!(gpu_params.displays.len(), 1);
4042 assert_eq!(gpu_params.displays[0].width, 700);
4043 assert_eq!(gpu_params.displays[0].height, 800);
4044 }
Kaiyi Lidd348a42020-07-13 11:49:46 -07004045 }
Chuanxiao Dongfd5626c2020-04-27 16:35:33 +08004046
4047 #[test]
4048 fn parse_battery_vaild() {
4049 parse_battery_options(Some("type=goldfish")).expect("parse should have succeded");
4050 }
4051
4052 #[test]
4053 fn parse_battery_vaild_no_type() {
4054 parse_battery_options(None).expect("parse should have succeded");
4055 }
4056
4057 #[test]
4058 fn parse_battery_invaild_parameter() {
4059 parse_battery_options(Some("tyep=goldfish")).expect_err("parse should have failed");
4060 }
4061
4062 #[test]
4063 fn parse_battery_invaild_type_value() {
4064 parse_battery_options(Some("type=xxx")).expect_err("parse should have failed");
4065 }
Mattias Nisslerde2c6402021-10-21 12:05:29 +00004066
4067 #[test]
4068 fn parse_stub_pci() {
Daniel Verkamp42b33602021-12-15 15:35:08 -08004069 let params = parse_stub_pci_parameters(Some("0000:01:02.3,vendor=0xfffe,device=0xfffd,class=0xffc1c2,subsystem_vendor=0xfffc,subsystem_device=0xfffb,revision=0xa")).unwrap();
Mattias Nisslerde2c6402021-10-21 12:05:29 +00004070 assert_eq!(params.address.bus, 1);
4071 assert_eq!(params.address.dev, 2);
4072 assert_eq!(params.address.func, 3);
4073 assert_eq!(params.vendor_id, 0xfffe);
4074 assert_eq!(params.device_id, 0xfffd);
4075 assert_eq!(params.class as u8, PciClassCode::Other as u8);
4076 assert_eq!(params.subclass, 0xc1);
4077 assert_eq!(params.programming_interface, 0xc2);
Mattias Nisslerde2c6402021-10-21 12:05:29 +00004078 assert_eq!(params.subsystem_vendor_id, 0xfffc);
4079 assert_eq!(params.subsystem_device_id, 0xfffb);
4080 assert_eq!(params.revision_id, 0xa);
4081 }
Junichi Uekawab6a6e942021-12-07 05:49:30 +09004082
4083 #[cfg(feature = "direct")]
4084 #[test]
4085 fn parse_direct_io_options_valid() {
4086 let params = parse_direct_io_options(Some("/dev/mem@1,100-110")).unwrap();
4087 assert_eq!(params.path.to_str(), Some("/dev/mem"));
Junichi Uekawab180f9c2021-12-07 09:21:36 +09004088 assert_eq!(params.ranges[0], BusRange { base: 1, len: 1 });
4089 assert_eq!(params.ranges[1], BusRange { base: 100, len: 11 });
Junichi Uekawab6a6e942021-12-07 05:49:30 +09004090 }
4091
4092 #[cfg(feature = "direct")]
4093 #[test]
4094 fn parse_direct_io_options_hex() {
4095 let params = parse_direct_io_options(Some("/dev/mem@1,0x10,100-110,0x10-0x20")).unwrap();
4096 assert_eq!(params.path.to_str(), Some("/dev/mem"));
Junichi Uekawab180f9c2021-12-07 09:21:36 +09004097 assert_eq!(params.ranges[0], BusRange { base: 1, len: 1 });
4098 assert_eq!(params.ranges[1], BusRange { base: 0x10, len: 1 });
4099 assert_eq!(params.ranges[2], BusRange { base: 100, len: 11 });
4100 assert_eq!(
4101 params.ranges[3],
4102 BusRange {
4103 base: 0x10,
4104 len: 0x11
4105 }
4106 );
Junichi Uekawab6a6e942021-12-07 05:49:30 +09004107 }
4108
4109 #[cfg(feature = "direct")]
4110 #[test]
4111 fn parse_direct_io_options_invalid() {
Junichi Uekawa4d312052021-12-08 16:59:38 +09004112 assert!(parse_direct_io_options(Some("/dev/mem@0y10"))
4113 .unwrap_err()
4114 .to_string()
4115 .contains("invalid base range value"));
4116
4117 assert!(parse_direct_io_options(Some("/dev/mem@"))
4118 .unwrap_err()
4119 .to_string()
4120 .contains("invalid base range value"));
Junichi Uekawab6a6e942021-12-07 05:49:30 +09004121 }
Mattias Nisslerbbd91d02021-12-07 08:57:45 +00004122
4123 #[test]
4124 fn parse_file_backed_mapping_valid() {
4125 let params = parse_file_backed_mapping(Some(
4126 "addr=0x1000,size=0x2000,path=/dev/mem,offset=0x3000,ro,rw,sync",
4127 ))
4128 .unwrap();
4129 assert_eq!(params.address, 0x1000);
4130 assert_eq!(params.size, 0x2000);
4131 assert_eq!(params.path, PathBuf::from("/dev/mem"));
4132 assert_eq!(params.offset, 0x3000);
4133 assert!(params.writable);
4134 assert!(params.sync);
4135 }
4136
4137 #[test]
4138 fn parse_file_backed_mapping_incomplete() {
4139 assert!(parse_file_backed_mapping(Some("addr=0x1000,size=0x2000"))
4140 .unwrap_err()
4141 .to_string()
4142 .contains("required"));
4143 assert!(parse_file_backed_mapping(Some("size=0x2000,path=/dev/mem"))
4144 .unwrap_err()
4145 .to_string()
4146 .contains("required"));
4147 assert!(parse_file_backed_mapping(Some("addr=0x1000,path=/dev/mem"))
4148 .unwrap_err()
4149 .to_string()
4150 .contains("required"));
4151 }
4152
4153 #[test]
4154 fn parse_file_backed_mapping_unaligned() {
4155 assert!(
4156 parse_file_backed_mapping(Some("addr=0x1001,size=0x2000,path=/dev/mem"))
4157 .unwrap_err()
4158 .to_string()
4159 .contains("aligned")
4160 );
4161 assert!(
4162 parse_file_backed_mapping(Some("addr=0x1000,size=0x2001,path=/dev/mem"))
4163 .unwrap_err()
4164 .to_string()
4165 .contains("aligned")
4166 );
4167 }
4168
4169 #[test]
4170 fn parse_file_backed_mapping_align() {
4171 let params =
4172 parse_file_backed_mapping(Some("addr=0x3042,size=0xff0,path=/dev/mem,align")).unwrap();
4173 assert_eq!(params.address, 0x3000);
4174 assert_eq!(params.size, 0x2000);
4175 }
Daniel Verkamp107edb32019-04-05 09:58:48 -07004176}