blob: dcdc689e3813cceb66b7ebdfcd24e9320bf38ac2 [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};
Kevin Hamacher6fc5f202021-03-18 12:41:23 +010022use base::{debug, error, getpid, info, kill_process_group, reap_child, syslog, warn};
Tomasz Jeznach3ce74762021-02-26 01:01:53 -080023#[cfg(feature = "direct")]
24use crosvm::DirectIoOption;
Zach Reizner267f2c82019-07-31 17:07:27 -070025use crosvm::{
26 argument::{self, print_help, set_arguments, Argument},
Michael Hoylee47a5002020-10-15 16:24:13 -070027 platform, BindMount, Config, DiskOption, Executable, GidMap, SharedDir, TouchDeviceOption,
Christian Blichmann50f95912021-11-05 16:59:39 +010028 VfioCommand, VhostUserFsOption, VhostUserOption, VhostUserWlOption, VhostVsockDeviceParameter,
29 DISK_ID_LEN,
Zach Reizner267f2c82019-07-31 17:07:27 -070030};
Keiichi Watanabe553d2192021-08-16 16:42:27 +090031use devices::serial_device::{SerialHardware, SerialParameters, SerialType};
Chia-I Wucba95db2021-12-09 15:18:58 -080032#[cfg(all(feature = "gpu", feature = "virgl_renderer_next"))]
33use devices::virtio::gpu::GpuRenderServerParameters;
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"))]
557fn parse_gpu_render_server_options(
558 s: Option<&str>,
559 gpu_params: &mut GpuParameters,
560) -> argument::Result<()> {
561 let mut path: Option<PathBuf> = None;
Chia-I Wu7f0f7c12022-01-12 10:42:18 -0800562 let mut cache_path = None;
563 let mut cache_size = None;
Chia-I Wu16fb6592021-11-10 11:45:32 -0800564
565 if let Some(s) = s {
566 let opts = s
567 .split(',')
568 .map(|frag| frag.split('='))
569 .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
570
571 for (k, v) in opts {
572 match k {
573 "path" => {
574 path =
575 Some(
576 PathBuf::from_str(v).map_err(|e| argument::Error::InvalidValue {
577 value: v.to_string(),
578 expected: e.to_string(),
579 })?,
580 )
581 }
Chia-I Wu7f0f7c12022-01-12 10:42:18 -0800582 "cache-path" => cache_path = Some(v.to_string()),
583 "cache-size" => cache_size = Some(v.to_string()),
Chia-I Wu16fb6592021-11-10 11:45:32 -0800584 "" => {}
585 _ => {
586 return Err(argument::Error::UnknownArgument(format!(
587 "gpu-render-server parameter {}",
588 k
589 )));
590 }
591 }
592 }
593 }
594
595 if let Some(p) = path {
Chia-I Wu7f0f7c12022-01-12 10:42:18 -0800596 gpu_params.render_server = Some(GpuRenderServerParameters {
597 path: p,
598 cache_path,
599 cache_size,
600 });
Chia-I Wu16fb6592021-11-10 11:45:32 -0800601 Ok(())
602 } else {
603 Err(argument::Error::InvalidValue {
604 value: s.unwrap_or("").to_string(),
605 expected: String::from("gpu-render-server must include 'path'"),
606 })
607 }
608}
609
Andrew Scull1590e6f2020-03-18 18:00:47 +0000610#[cfg(feature = "audio")]
Judy Hsiaod5c1e962020-02-04 12:30:01 +0800611fn parse_ac97_options(s: &str) -> argument::Result<Ac97Parameters> {
612 let mut ac97_params: Ac97Parameters = Default::default();
613
614 let opts = s
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +0900615 .split(',')
616 .map(|frag| frag.split('='))
Judy Hsiaod5c1e962020-02-04 12:30:01 +0800617 .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
618
619 for (k, v) in opts {
620 match k {
621 "backend" => {
622 ac97_params.backend =
623 v.parse::<Ac97Backend>()
624 .map_err(|e| argument::Error::InvalidValue {
625 value: v.to_string(),
626 expected: e.to_string(),
627 })?;
628 }
629 "capture" => {
630 ac97_params.capture = v.parse::<bool>().map_err(|e| {
631 argument::Error::Syntax(format!("invalid capture option: {}", e))
632 })?;
633 }
Dennis Kempin50a58f92021-06-23 11:34:31 -0700634 #[cfg(feature = "audio_cras")]
paulhsia83d51602021-03-09 17:13:14 +0800635 "client_type" => {
636 ac97_params
637 .set_client_type(v)
638 .map_err(|e| argument::Error::InvalidValue {
639 value: v.to_string(),
640 expected: e.to_string(),
641 })?;
642 }
Woody Chowb27dea42021-09-08 15:51:22 +0900643 #[cfg(feature = "audio_cras")]
644 "socket_type" => {
645 ac97_params
646 .set_socket_type(v)
647 .map_err(|e| argument::Error::InvalidValue {
648 value: v.to_string(),
649 expected: e.to_string(),
650 })?;
651 }
Jorge E. Moreira6a88a5d2021-03-12 15:34:46 -0800652 #[cfg(any(target_os = "linux", target_os = "android"))]
Jorge E. Moreira359e7de2020-12-02 18:25:53 -0800653 "server" => {
654 ac97_params.vios_server_path =
655 Some(
656 PathBuf::from_str(v).map_err(|e| argument::Error::InvalidValue {
657 value: v.to_string(),
658 expected: e.to_string(),
659 })?,
660 );
661 }
Judy Hsiaod5c1e962020-02-04 12:30:01 +0800662 _ => {
663 return Err(argument::Error::UnknownArgument(format!(
664 "unknown ac97 parameter {}",
665 k
666 )));
667 }
668 }
669 }
670
Jorge E. Moreira359e7de2020-12-02 18:25:53 -0800671 // server is required for and exclusive to vios backend
Jorge E. Moreira6a88a5d2021-03-12 15:34:46 -0800672 #[cfg(any(target_os = "linux", target_os = "android"))]
Jorge E. Moreira359e7de2020-12-02 18:25:53 -0800673 match ac97_params.backend {
674 Ac97Backend::VIOS => {
675 if ac97_params.vios_server_path.is_none() {
676 return Err(argument::Error::ExpectedArgument(String::from(
677 "server argument is required for VIOS backend",
678 )));
679 }
680 }
681 _ => {
682 if ac97_params.vios_server_path.is_some() {
683 return Err(argument::Error::UnexpectedValue(String::from(
684 "server argument is exclusive to the VIOS backend",
685 )));
686 }
687 }
688 }
689
Judy Hsiaod5c1e962020-02-04 12:30:01 +0800690 Ok(ac97_params)
691}
692
Trent Begin17ccaad2019-04-17 13:51:25 -0600693fn parse_serial_options(s: &str) -> argument::Result<SerialParameters> {
694 let mut serial_setting = SerialParameters {
695 type_: SerialType::Sink,
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -0700696 hardware: SerialHardware::Serial,
Trent Begin17ccaad2019-04-17 13:51:25 -0600697 path: None,
Iliyan Malchev2c1417b2020-04-14 09:40:41 -0700698 input: None,
Trent Begin923bab02019-06-17 13:48:06 -0600699 num: 1,
Trent Begin17ccaad2019-04-17 13:51:25 -0600700 console: false,
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -0700701 earlycon: false,
Jorge E. Moreira1e262302019-08-01 14:40:03 -0700702 stdin: false,
Trent Begin17ccaad2019-04-17 13:51:25 -0600703 };
704
705 let opts = s
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +0900706 .split(',')
Nicholas Hollingumc76c2da2020-09-18 15:53:16 +1000707 .map(|frag| frag.splitn(2, '='))
Trent Begin17ccaad2019-04-17 13:51:25 -0600708 .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
709
710 for (k, v) in opts {
711 match k {
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -0700712 "hardware" => {
713 serial_setting.hardware = v
714 .parse::<SerialHardware>()
715 .map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?
716 }
Trent Begin17ccaad2019-04-17 13:51:25 -0600717 "type" => {
718 serial_setting.type_ = v
719 .parse::<SerialType>()
720 .map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?
721 }
722 "num" => {
723 let num = v.parse::<u8>().map_err(|e| {
724 argument::Error::Syntax(format!("serial device number is not parsable: {}", e))
725 })?;
A. Cody Schuffelen3faab5a2020-10-05 17:29:16 -0700726 if num < 1 {
Trent Begin17ccaad2019-04-17 13:51:25 -0600727 return Err(argument::Error::InvalidValue {
728 value: num.to_string(),
A. Cody Schuffelen3faab5a2020-10-05 17:29:16 -0700729 expected: String::from("Serial port num must be at least 1"),
Trent Begin17ccaad2019-04-17 13:51:25 -0600730 });
731 }
732 serial_setting.num = num;
733 }
734 "console" => {
735 serial_setting.console = v.parse::<bool>().map_err(|e| {
736 argument::Error::Syntax(format!(
737 "serial device console is not parseable: {}",
738 e
739 ))
740 })?
741 }
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -0700742 "earlycon" => {
743 serial_setting.earlycon = v.parse::<bool>().map_err(|e| {
744 argument::Error::Syntax(format!(
745 "serial device earlycon is not parseable: {}",
746 e,
747 ))
748 })?
749 }
Jorge E. Moreira1e262302019-08-01 14:40:03 -0700750 "stdin" => {
751 serial_setting.stdin = v.parse::<bool>().map_err(|e| {
752 argument::Error::Syntax(format!("serial device stdin is not parseable: {}", e))
Iliyan Malchev2c1417b2020-04-14 09:40:41 -0700753 })?;
754 if serial_setting.stdin && serial_setting.input.is_some() {
755 return Err(argument::Error::TooManyArguments(
756 "Cannot specify both stdin and input options".to_string(),
757 ));
758 }
Jorge E. Moreira1e262302019-08-01 14:40:03 -0700759 }
Jorge E. Moreira9c9e0e72019-05-17 13:57:04 -0700760 "path" => serial_setting.path = Some(PathBuf::from(v)),
Iliyan Malchev2c1417b2020-04-14 09:40:41 -0700761 "input" => {
762 if serial_setting.stdin {
763 return Err(argument::Error::TooManyArguments(
764 "Cannot specify both stdin and input options".to_string(),
765 ));
766 }
767 serial_setting.input = Some(PathBuf::from(v));
768 }
Trent Begin17ccaad2019-04-17 13:51:25 -0600769 _ => {
770 return Err(argument::Error::UnknownArgument(format!(
771 "serial parameter {}",
772 k
773 )));
774 }
775 }
776 }
777
A. Cody Schuffelen3faab5a2020-10-05 17:29:16 -0700778 if serial_setting.hardware == SerialHardware::Serial && serial_setting.num > 4 {
779 return Err(argument::Error::InvalidValue {
780 value: serial_setting.num.to_string(),
781 expected: String::from("Serial port num must be 4 or less"),
782 });
783 }
784
Trent Begin17ccaad2019-04-17 13:51:25 -0600785 Ok(serial_setting)
786}
787
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800788fn parse_plugin_mount_option(value: &str) -> argument::Result<BindMount> {
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +0900789 let components: Vec<&str> = value.split(':').collect();
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800790 if components.is_empty() || components.len() > 3 || components[0].is_empty() {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800791 return Err(argument::Error::InvalidValue {
792 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800793 expected: String::from(
794 "`plugin-mount` should be in a form of: <src>[:[<dst>][:<writable>]]",
795 ),
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800796 });
797 }
798
799 let src = PathBuf::from(components[0]);
800 if src.is_relative() {
801 return Err(argument::Error::InvalidValue {
802 value: components[0].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800803 expected: String::from("the source path for `plugin-mount` must be absolute"),
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800804 });
805 }
806 if !src.exists() {
807 return Err(argument::Error::InvalidValue {
808 value: components[0].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800809 expected: String::from("the source path for `plugin-mount` does not exist"),
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800810 });
811 }
812
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800813 let dst = PathBuf::from(match components.get(1) {
814 None | Some(&"") => components[0],
815 Some(path) => path,
816 });
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800817 if dst.is_relative() {
818 return Err(argument::Error::InvalidValue {
819 value: components[1].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800820 expected: String::from("the destination path for `plugin-mount` must be absolute"),
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800821 });
822 }
823
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800824 let writable: bool = match components.get(2) {
825 None => false,
826 Some(s) => s.parse().map_err(|_| argument::Error::InvalidValue {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800827 value: components[2].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800828 expected: String::from("the <writable> component for `plugin-mount` is not valid bool"),
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800829 })?,
830 };
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800831
832 Ok(BindMount { src, dst, writable })
833}
834
835fn parse_plugin_gid_map_option(value: &str) -> argument::Result<GidMap> {
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +0900836 let components: Vec<&str> = value.split(':').collect();
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800837 if components.is_empty() || components.len() > 3 || components[0].is_empty() {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800838 return Err(argument::Error::InvalidValue {
839 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800840 expected: String::from(
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800841 "`plugin-gid-map` must have exactly 3 components: <inner>[:[<outer>][:<count>]]",
Judy Hsiao59343052020-03-16 15:58:03 +0800842 ),
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800843 });
844 }
845
846 let inner: libc::gid_t = components[0]
847 .parse()
848 .map_err(|_| argument::Error::InvalidValue {
849 value: components[0].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800850 expected: String::from("the <inner> component for `plugin-gid-map` is not valid gid"),
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800851 })?;
852
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800853 let outer: libc::gid_t = match components.get(1) {
854 None | Some(&"") => inner,
855 Some(s) => s.parse().map_err(|_| argument::Error::InvalidValue {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800856 value: components[1].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800857 expected: String::from("the <outer> component for `plugin-gid-map` is not valid gid"),
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800858 })?,
859 };
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800860
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800861 let count: u32 = match components.get(2) {
862 None => 1,
863 Some(s) => s.parse().map_err(|_| argument::Error::InvalidValue {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800864 value: components[2].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +0800865 expected: String::from(
866 "the <count> component for `plugin-gid-map` is not valid number",
867 ),
Dmitry Torokhov458bb642019-12-13 11:47:52 -0800868 })?,
869 };
Dmitry Torokhovc689fd92019-12-11 13:36:08 -0800870
871 Ok(GidMap {
872 inner,
873 outer,
874 count,
875 })
876}
877
Chuanxiao Dongfd5626c2020-04-27 16:35:33 +0800878fn parse_battery_options(s: Option<&str>) -> argument::Result<BatteryType> {
879 let mut battery_type: BatteryType = Default::default();
880
881 if let Some(s) = s {
882 let opts = s
Andrew Walbran9cfdbd92021-01-11 17:40:34 +0000883 .split(',')
884 .map(|frag| frag.split('='))
Chuanxiao Dongfd5626c2020-04-27 16:35:33 +0800885 .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
886
887 for (k, v) in opts {
888 match k {
889 "type" => match v.parse::<BatteryType>() {
890 Ok(type_) => battery_type = type_,
891 Err(e) => {
892 return Err(argument::Error::InvalidValue {
893 value: v.to_string(),
894 expected: e.to_string(),
895 });
896 }
897 },
898 "" => {}
899 _ => {
900 return Err(argument::Error::UnknownArgument(format!(
901 "battery parameter {}",
902 k
903 )));
904 }
905 }
906 }
907 }
908
909 Ok(battery_type)
910}
911
Tomasz Jeznach3ce74762021-02-26 01:01:53 -0800912#[cfg(feature = "direct")]
Junichi Uekawab6a6e942021-12-07 05:49:30 +0900913fn parse_hex_or_decimal(maybe_hex_string: &str) -> Result<u64, std::num::ParseIntError> {
914 // Parse string starting with 0x as hex and others as numbers.
915 let without_prefix = maybe_hex_string.strip_prefix("0x");
916 match without_prefix {
917 Some(hex_string) => u64::from_str_radix(hex_string, 16),
918 None => u64::from_str(maybe_hex_string),
919 }
920}
921
922#[cfg(feature = "direct")]
Tomasz Jeznach3ce74762021-02-26 01:01:53 -0800923fn parse_direct_io_options(s: Option<&str>) -> argument::Result<DirectIoOption> {
924 let s = s.ok_or(argument::Error::ExpectedValue(String::from(
925 "expected path@range[,range] value",
926 )))?;
927 let parts: Vec<&str> = s.splitn(2, '@').collect();
928 if parts.len() != 2 {
929 return Err(argument::Error::InvalidValue {
930 value: s.to_string(),
931 expected: String::from("missing port range, use /path@X-Y,Z,.. syntax"),
932 });
933 }
934 let path = PathBuf::from(parts[0]);
935 if !path.exists() {
936 return Err(argument::Error::InvalidValue {
937 value: parts[0].to_owned(),
938 expected: String::from("the path does not exist"),
939 });
940 };
Junichi Uekawab180f9c2021-12-07 09:21:36 +0900941 let ranges: argument::Result<Vec<BusRange>> = parts[1]
Tomasz Jeznach3ce74762021-02-26 01:01:53 -0800942 .split(',')
943 .map(|frag| frag.split('-'))
944 .map(|mut range| {
945 let base = 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 let last = range
950 .next()
Junichi Uekawab6a6e942021-12-07 05:49:30 +0900951 .map(|v| parse_hex_or_decimal(v))
Tomasz Jeznach3ce74762021-02-26 01:01:53 -0800952 .map_or(Ok(None), |r| r.map(Some));
953 (base, last)
954 })
955 .map(|range| match range {
Junichi Uekawab180f9c2021-12-07 09:21:36 +0900956 (Ok(Some(base)), Ok(None)) => Ok(BusRange { base, len: 1 }),
957 (Ok(Some(base)), Ok(Some(last))) => Ok(BusRange {
958 base,
959 len: last.saturating_sub(base).saturating_add(1),
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 base range value"),
964 }),
Junichi Uekawa4d312052021-12-08 16:59:38 +0900965 (_, Err(_)) => Err(argument::Error::InvalidValue {
966 value: s.to_owned(),
Tomasz Jeznach3ce74762021-02-26 01:01:53 -0800967 expected: String::from("invalid last range value"),
968 }),
969 _ => Err(argument::Error::InvalidValue {
970 value: s.to_owned(),
971 expected: String::from("invalid range format"),
972 }),
973 })
974 .collect();
975 Ok(DirectIoOption {
976 path,
977 ranges: ranges?,
978 })
979}
980
Mattias Nisslerde2c6402021-10-21 12:05:29 +0000981fn parse_stub_pci_parameters(s: Option<&str>) -> argument::Result<StubPciParameters> {
982 let s = s.ok_or(argument::Error::ExpectedValue(String::from(
983 "stub-pci-device configuration expected",
984 )))?;
985
986 let mut options = argument::parse_key_value_options("stub-pci-device", s, ',');
987 let addr = options
988 .next()
989 .ok_or(argument::Error::ExpectedValue(String::from(
990 "stub-pci-device: expected device address",
991 )))?
992 .key();
993 let mut params = StubPciParameters {
994 address: PciAddress::from_string(addr),
995 vendor_id: 0,
996 device_id: 0,
997 class: PciClassCode::Other,
998 subclass: 0,
999 programming_interface: 0,
Mattias Nisslerde2c6402021-10-21 12:05:29 +00001000 subsystem_device_id: 0,
1001 subsystem_vendor_id: 0,
1002 revision_id: 0,
1003 };
1004 for opt in options {
1005 match opt.key() {
1006 "vendor" => params.vendor_id = opt.parse_numeric::<u16>()?,
1007 "device" => params.device_id = opt.parse_numeric::<u16>()?,
1008 "class" => {
1009 let class = opt.parse_numeric::<u32>()?;
1010 params.class = PciClassCode::try_from((class >> 16) as u8)
1011 .map_err(|_| opt.invalid_value_err(String::from("Unknown class code")))?;
1012 params.subclass = (class >> 8) as u8;
1013 params.programming_interface = class as u8;
1014 }
Mattias Nisslerde2c6402021-10-21 12:05:29 +00001015 "subsystem_vendor" => params.subsystem_vendor_id = opt.parse_numeric::<u16>()?,
1016 "subsystem_device" => params.subsystem_device_id = opt.parse_numeric::<u16>()?,
1017 "revision" => params.revision_id = opt.parse_numeric::<u8>()?,
1018 _ => return Err(opt.invalid_key_err()),
1019 }
1020 }
1021
1022 Ok(params)
1023}
1024
Zach Reiznerefe95782017-08-26 18:05:48 -07001025fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::Result<()> {
1026 match name {
1027 "" => {
Cody Schuffelen6d1ab502019-05-21 12:12:38 -07001028 if cfg.executable_path.is_some() {
1029 return Err(argument::Error::TooManyArguments(format!(
1030 "A VM executable was already specified: {:?}",
1031 cfg.executable_path
1032 )));
Zach Reiznerefe95782017-08-26 18:05:48 -07001033 }
Cody Schuffelen6d1ab502019-05-21 12:12:38 -07001034 let kernel_path = PathBuf::from(value.unwrap());
1035 if !kernel_path.exists() {
1036 return Err(argument::Error::InvalidValue {
1037 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001038 expected: String::from("this kernel path does not exist"),
Cody Schuffelen6d1ab502019-05-21 12:12:38 -07001039 });
1040 }
1041 cfg.executable_path = Some(Executable::Kernel(kernel_path));
Zach Reiznerefe95782017-08-26 18:05:48 -07001042 }
Christian Blichmann33d56772021-03-04 19:03:54 +01001043 "kvm-device" => {
1044 let kvm_device_path = PathBuf::from(value.unwrap());
1045 if !kvm_device_path.exists() {
1046 return Err(argument::Error::InvalidValue {
1047 value: value.unwrap().to_owned(),
1048 expected: String::from("this kvm device path does not exist"),
1049 });
1050 }
1051
1052 cfg.kvm_device_path = kvm_device_path;
1053 }
Christian Blichmann50f95912021-11-05 16:59:39 +01001054 "vhost-vsock-fd" => {
1055 if cfg.vhost_vsock_device.is_some() {
1056 return Err(argument::Error::InvalidValue {
1057 value: value.unwrap().to_owned(),
1058 expected: String::from("A vhost-vsock device was already specified"),
1059 });
1060 }
1061 cfg.vhost_vsock_device = Some(VhostVsockDeviceParameter::Fd(
1062 value
1063 .unwrap()
1064 .parse()
1065 .map_err(|_| argument::Error::InvalidValue {
1066 value: value.unwrap().to_owned(),
1067 expected: String::from(
1068 "this value for `vhost-vsock-fd` needs to be integer",
1069 ),
1070 })?,
1071 ));
1072 }
Christian Blichmann2f5d4b62021-03-10 18:08:08 +01001073 "vhost-vsock-device" => {
Christian Blichmann50f95912021-11-05 16:59:39 +01001074 if cfg.vhost_vsock_device.is_some() {
1075 return Err(argument::Error::InvalidValue {
1076 value: value.unwrap().to_owned(),
1077 expected: String::from("A vhost-vsock device was already specified"),
1078 });
1079 }
Christian Blichmann2f5d4b62021-03-10 18:08:08 +01001080 let vhost_vsock_device_path = PathBuf::from(value.unwrap());
1081 if !vhost_vsock_device_path.exists() {
1082 return Err(argument::Error::InvalidValue {
1083 value: value.unwrap().to_owned(),
1084 expected: String::from("this vhost-vsock device path does not exist"),
1085 });
1086 }
1087
Christian Blichmann50f95912021-11-05 16:59:39 +01001088 cfg.vhost_vsock_device = Some(VhostVsockDeviceParameter::Path(vhost_vsock_device_path));
Christian Blichmann2f5d4b62021-03-10 18:08:08 +01001089 }
1090 "vhost-net-device" => {
1091 let vhost_net_device_path = PathBuf::from(value.unwrap());
1092 if !vhost_net_device_path.exists() {
1093 return Err(argument::Error::InvalidValue {
1094 value: value.unwrap().to_owned(),
1095 expected: String::from("this vhost-vsock device path does not exist"),
1096 });
1097 }
1098
1099 cfg.vhost_net_device_path = vhost_net_device_path;
1100 }
Tristan Muntsinger4133b012018-12-21 16:01:56 -08001101 "android-fstab" => {
1102 if cfg.android_fstab.is_some()
1103 && !cfg.android_fstab.as_ref().unwrap().as_os_str().is_empty()
1104 {
1105 return Err(argument::Error::TooManyArguments(
1106 "expected exactly one android fstab path".to_owned(),
1107 ));
1108 } else {
1109 let android_fstab = PathBuf::from(value.unwrap());
1110 if !android_fstab.exists() {
1111 return Err(argument::Error::InvalidValue {
1112 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001113 expected: String::from("this android fstab path does not exist"),
Tristan Muntsinger4133b012018-12-21 16:01:56 -08001114 });
1115 }
1116 cfg.android_fstab = Some(android_fstab);
1117 }
1118 }
Zach Reiznerefe95782017-08-26 18:05:48 -07001119 "params" => {
Zach Reiznerbb678712018-01-30 18:13:04 -08001120 cfg.params.push(value.unwrap().to_owned());
Zach Reiznerefe95782017-08-26 18:05:48 -07001121 }
1122 "cpus" => {
1123 if cfg.vcpu_count.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -07001124 return Err(argument::Error::TooManyArguments(
1125 "`cpus` already given".to_owned(),
1126 ));
Zach Reiznerefe95782017-08-26 18:05:48 -07001127 }
1128 cfg.vcpu_count =
Zach Reizner55a9e502018-10-03 10:22:32 -07001129 Some(
1130 value
1131 .unwrap()
1132 .parse()
1133 .map_err(|_| argument::Error::InvalidValue {
1134 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001135 expected: String::from("this value for `cpus` needs to be integer"),
Zach Reizner55a9e502018-10-03 10:22:32 -07001136 })?,
1137 )
Zach Reiznerefe95782017-08-26 18:05:48 -07001138 }
Daniel Verkamp107edb32019-04-05 09:58:48 -07001139 "cpu-affinity" => {
Daniel Verkampc677fb42020-09-08 13:47:49 -07001140 if cfg.vcpu_affinity.is_some() {
Daniel Verkamp107edb32019-04-05 09:58:48 -07001141 return Err(argument::Error::TooManyArguments(
1142 "`cpu-affinity` already given".to_owned(),
1143 ));
1144 }
Daniel Verkampc677fb42020-09-08 13:47:49 -07001145 cfg.vcpu_affinity = Some(parse_cpu_affinity(value.unwrap())?);
Daniel Verkamp107edb32019-04-05 09:58:48 -07001146 }
Daniel Verkamp8a72afc2021-03-15 17:55:52 -07001147 "cpu-cluster" => {
1148 cfg.cpu_clusters.push(parse_cpu_set(value.unwrap())?);
1149 }
1150 "cpu-capacity" => {
1151 parse_cpu_capacity(value.unwrap(), &mut cfg.cpu_capacity)?;
1152 }
Yusuke Sato31e136a2021-08-18 11:51:38 -07001153 "per-vm-core-scheduling" => {
1154 cfg.per_vm_core_scheduling = true;
1155 }
Woody Chow737ff122021-03-22 17:49:57 +09001156 #[cfg(feature = "audio_cras")]
1157 "cras-snd" => {
Chih-Yang Hsia41dc04f2021-12-08 16:04:23 +08001158 cfg.cras_snds.push(
Woody Chow0b2b6062021-09-03 15:40:02 +09001159 value
1160 .unwrap()
1161 .parse()
1162 .map_err(|e: CrasSndError| argument::Error::Syntax(e.to_string()))?,
1163 );
Woody Chow737ff122021-03-22 17:49:57 +09001164 }
Suleiman Souhlal015c3c12020-10-07 14:15:41 +09001165 "no-smt" => {
1166 cfg.no_smt = true;
1167 }
Kansho Nishidaab205af2020-08-13 18:17:50 +09001168 "rt-cpus" => {
1169 if !cfg.rt_cpus.is_empty() {
1170 return Err(argument::Error::TooManyArguments(
1171 "`rt-cpus` already given".to_owned(),
1172 ));
1173 }
1174 cfg.rt_cpus = parse_cpu_set(value.unwrap())?;
1175 }
Suleiman Souhlal63630e82021-02-18 11:53:11 +09001176 "delay-rt" => {
1177 cfg.delay_rt = true;
1178 }
Zach Reiznerefe95782017-08-26 18:05:48 -07001179 "mem" => {
1180 if cfg.memory.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -07001181 return Err(argument::Error::TooManyArguments(
1182 "`mem` already given".to_owned(),
1183 ));
Zach Reiznerefe95782017-08-26 18:05:48 -07001184 }
1185 cfg.memory =
Zach Reizner55a9e502018-10-03 10:22:32 -07001186 Some(
1187 value
1188 .unwrap()
1189 .parse()
1190 .map_err(|_| argument::Error::InvalidValue {
1191 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001192 expected: String::from("this value for `mem` needs to be integer"),
Zach Reizner55a9e502018-10-03 10:22:32 -07001193 })?,
1194 )
Zach Reiznerefe95782017-08-26 18:05:48 -07001195 }
Will Deaconc48e7832021-07-30 19:03:06 +01001196 #[cfg(target_arch = "aarch64")]
1197 "swiotlb" => {
1198 if cfg.swiotlb.is_some() {
1199 return Err(argument::Error::TooManyArguments(
1200 "`swiotlb` already given".to_owned(),
1201 ));
1202 }
1203 cfg.swiotlb =
1204 Some(
1205 value
1206 .unwrap()
1207 .parse()
1208 .map_err(|_| argument::Error::InvalidValue {
1209 value: value.unwrap().to_owned(),
1210 expected: String::from("this value for `swiotlb` needs to be integer"),
1211 })?,
1212 )
1213 }
Sergey Senozhatsky1e369c52021-04-13 20:23:51 +09001214 "hugepages" => {
1215 cfg.hugepages = true;
1216 }
Andrew Scull1590e6f2020-03-18 18:00:47 +00001217 #[cfg(feature = "audio")]
Judy Hsiaod5c1e962020-02-04 12:30:01 +08001218 "ac97" => {
1219 let ac97_params = parse_ac97_options(value.unwrap())?;
paulhsia16478242020-11-27 14:04:58 +08001220 // Add kernel parameters related to the intel8x0 driver for ac97 devices once.
1221 if cfg.ac97_parameters.is_empty() {
1222 // Set `inside_vm=1` to save some register read ops in the driver.
1223 cfg.params.push("snd_intel8x0.inside_vm=1".to_string());
1224 // Set `ac97_clock=48000` to save intel8x0_measure_ac97_clock call in the driver.
1225 cfg.params.push("snd_intel8x0.ac97_clock=48000".to_string());
1226 }
Judy Hsiaod5c1e962020-02-04 12:30:01 +08001227 cfg.ac97_parameters.push(ac97_params);
Dylan Reid3082e8e2019-01-07 10:33:48 -08001228 }
Jorge E. Moreirad4562d02021-06-28 16:21:12 -07001229 #[cfg(feature = "audio")]
1230 "sound" => {
1231 let client_path = PathBuf::from(value.unwrap());
1232 cfg.sound = Some(client_path);
1233 }
Trent Begin17ccaad2019-04-17 13:51:25 -06001234 "serial" => {
1235 let serial_params = parse_serial_options(value.unwrap())?;
1236 let num = serial_params.num;
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07001237 let key = (serial_params.hardware, num);
1238 if cfg.serial_parameters.contains_key(&key) {
Trent Begin17ccaad2019-04-17 13:51:25 -06001239 return Err(argument::Error::TooManyArguments(format!(
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07001240 "serial hardware {} num {}",
1241 serial_params.hardware, num,
Trent Begin17ccaad2019-04-17 13:51:25 -06001242 )));
1243 }
1244
1245 if serial_params.console {
Jakub Staronb6515a92019-06-05 15:18:25 -07001246 for params in cfg.serial_parameters.values() {
Trent Begin17ccaad2019-04-17 13:51:25 -06001247 if params.console {
1248 return Err(argument::Error::TooManyArguments(format!(
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07001249 "{} device {} already set as console",
1250 params.hardware, params.num,
1251 )));
1252 }
1253 }
1254 }
1255
1256 if serial_params.earlycon {
1257 // Only SerialHardware::Serial supports earlycon= currently.
1258 match serial_params.hardware {
1259 SerialHardware::Serial => {}
1260 _ => {
1261 return Err(argument::Error::InvalidValue {
Andrew Walbran9cfdbd92021-01-11 17:40:34 +00001262 value: serial_params.hardware.to_string(),
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07001263 expected: String::from("earlycon not supported for hardware"),
1264 });
1265 }
1266 }
1267 for params in cfg.serial_parameters.values() {
1268 if params.earlycon {
1269 return Err(argument::Error::TooManyArguments(format!(
1270 "{} device {} already set as earlycon",
1271 params.hardware, params.num,
Trent Begin17ccaad2019-04-17 13:51:25 -06001272 )));
1273 }
1274 }
1275 }
1276
Jorge E. Moreira1e262302019-08-01 14:40:03 -07001277 if serial_params.stdin {
1278 if let Some(previous_stdin) = cfg.serial_parameters.values().find(|sp| sp.stdin) {
1279 return Err(argument::Error::TooManyArguments(format!(
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07001280 "{} device {} already connected to standard input",
1281 previous_stdin.hardware, previous_stdin.num,
Jorge E. Moreira1e262302019-08-01 14:40:03 -07001282 )));
1283 }
1284 }
1285
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07001286 cfg.serial_parameters.insert(key, serial_params);
Trent Begin17ccaad2019-04-17 13:51:25 -06001287 }
1288 "syslog-tag" => {
1289 if cfg.syslog_tag.is_some() {
1290 return Err(argument::Error::TooManyArguments(
1291 "`syslog-tag` already given".to_owned(),
1292 ));
1293 }
1294 syslog::set_proc_name(value.unwrap());
1295 cfg.syslog_tag = Some(value.unwrap().to_owned());
1296 }
Daniel Verkamp4b62cd92019-11-08 13:09:27 -08001297 "root" | "rwroot" | "disk" | "rwdisk" => {
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001298 let param = value.unwrap();
1299 let mut components = param.split(',');
Daniel Verkamp6a8cd102019-06-26 15:17:46 -07001300 let read_only = !name.starts_with("rw");
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001301 let disk_path =
1302 PathBuf::from(
1303 components
1304 .next()
1305 .ok_or_else(|| argument::Error::InvalidValue {
1306 value: param.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001307 expected: String::from("missing disk path"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001308 })?,
1309 );
Zach Reiznerefe95782017-08-26 18:05:48 -07001310 if !disk_path.exists() {
1311 return Err(argument::Error::InvalidValue {
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001312 value: param.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001313 expected: String::from("this disk path does not exist"),
Zach Reizner55a9e502018-10-03 10:22:32 -07001314 });
Zach Reiznerefe95782017-08-26 18:05:48 -07001315 }
Daniel Verkamp6a8cd102019-06-26 15:17:46 -07001316 if name.ends_with("root") {
Daniel Verkampaac28132018-10-15 14:58:48 -07001317 if cfg.disks.len() >= 26 {
Zach Reizner55a9e502018-10-03 10:22:32 -07001318 return Err(argument::Error::TooManyArguments(
1319 "ran out of letters for to assign to root disk".to_owned(),
1320 ));
Zach Reiznerefe95782017-08-26 18:05:48 -07001321 }
Zach Reizner55a9e502018-10-03 10:22:32 -07001322 cfg.params.push(format!(
Daniel Verkamp6a8cd102019-06-26 15:17:46 -07001323 "root=/dev/vd{} {}",
1324 char::from(b'a' + cfg.disks.len() as u8),
1325 if read_only { "ro" } else { "rw" }
Zach Reizner55a9e502018-10-03 10:22:32 -07001326 ));
Zach Reiznerefe95782017-08-26 18:05:48 -07001327 }
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001328
1329 let mut disk = DiskOption {
Zach Reizner55a9e502018-10-03 10:22:32 -07001330 path: disk_path,
Daniel Verkamp6a8cd102019-06-26 15:17:46 -07001331 read_only,
Junichi Uekawa7bea39f2021-07-16 14:05:06 +09001332 o_direct: false,
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001333 sparse: true,
Daniel Verkamp27672232019-12-06 17:26:55 +11001334 block_size: 512,
Daniel Verkamp4e1f99a2020-06-01 12:47:21 -07001335 id: None,
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001336 };
1337
1338 for opt in components {
1339 let mut o = opt.splitn(2, '=');
1340 let kind = o.next().ok_or_else(|| argument::Error::InvalidValue {
1341 value: opt.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001342 expected: String::from("disk options must not be empty"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001343 })?;
1344 let value = o.next().ok_or_else(|| argument::Error::InvalidValue {
1345 value: opt.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001346 expected: String::from("disk options must be of the form `kind=value`"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001347 })?;
1348
1349 match kind {
1350 "sparse" => {
1351 let sparse = value.parse().map_err(|_| argument::Error::InvalidValue {
1352 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001353 expected: String::from("`sparse` must be a boolean"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001354 })?;
1355 disk.sparse = sparse;
1356 }
Junichi Uekawa7bea39f2021-07-16 14:05:06 +09001357 "o_direct" => {
1358 let o_direct =
1359 value.parse().map_err(|_| argument::Error::InvalidValue {
1360 value: value.to_owned(),
1361 expected: String::from("`o_direct` must be a boolean"),
1362 })?;
1363 disk.o_direct = o_direct;
1364 }
Daniel Verkamp27672232019-12-06 17:26:55 +11001365 "block_size" => {
1366 let block_size =
1367 value.parse().map_err(|_| argument::Error::InvalidValue {
1368 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001369 expected: String::from("`block_size` must be an integer"),
Daniel Verkamp27672232019-12-06 17:26:55 +11001370 })?;
1371 disk.block_size = block_size;
1372 }
Daniel Verkamp4e1f99a2020-06-01 12:47:21 -07001373 "id" => {
1374 if value.len() > DISK_ID_LEN {
1375 return Err(argument::Error::InvalidValue {
1376 value: value.to_owned(),
1377 expected: format!(
1378 "`id` must be {} or fewer characters",
1379 DISK_ID_LEN
1380 ),
1381 });
1382 }
1383 let mut id = [0u8; DISK_ID_LEN];
1384 // Slicing id to value's length will never panic
1385 // because we checked that value will fit into id above.
1386 id[..value.len()].copy_from_slice(value.as_bytes());
1387 disk.id = Some(id);
1388 }
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001389 _ => {
1390 return Err(argument::Error::InvalidValue {
1391 value: kind.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001392 expected: String::from("unrecognized disk option"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001393 });
1394 }
1395 }
1396 }
1397
1398 cfg.disks.push(disk);
Zach Reiznerefe95782017-08-26 18:05:48 -07001399 }
Jakub Starona3411ea2019-04-24 10:55:25 -07001400 "pmem-device" | "rw-pmem-device" => {
1401 let disk_path = PathBuf::from(value.unwrap());
1402 if !disk_path.exists() {
1403 return Err(argument::Error::InvalidValue {
1404 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001405 expected: String::from("this disk path does not exist"),
Jakub Starona3411ea2019-04-24 10:55:25 -07001406 });
1407 }
1408
1409 cfg.pmem_devices.push(DiskOption {
1410 path: disk_path,
1411 read_only: !name.starts_with("rw"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -08001412 sparse: false,
Junichi Uekawa7bea39f2021-07-16 14:05:06 +09001413 o_direct: false,
Michael Hoyle6b196952020-08-02 20:09:41 -07001414 block_size: base::pagesize() as u32,
Daniel Verkamp4e1f99a2020-06-01 12:47:21 -07001415 id: None,
Jakub Starona3411ea2019-04-24 10:55:25 -07001416 });
1417 }
Kansho Nishida282115b2019-12-18 13:13:14 +09001418 "pstore" => {
1419 if cfg.pstore.is_some() {
1420 return Err(argument::Error::TooManyArguments(
1421 "`pstore` already given".to_owned(),
1422 ));
1423 }
1424
1425 let value = value.unwrap();
1426 let components: Vec<&str> = value.split(',').collect();
1427 if components.len() != 2 {
1428 return Err(argument::Error::InvalidValue {
1429 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001430 expected: String::from(
1431 "pstore must have exactly 2 components: path=<path>,size=<size>",
1432 ),
Kansho Nishida282115b2019-12-18 13:13:14 +09001433 });
1434 }
1435 cfg.pstore = Some(Pstore {
1436 path: {
1437 if components[0].len() <= 5 || !components[0].starts_with("path=") {
1438 return Err(argument::Error::InvalidValue {
1439 value: components[0].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001440 expected: String::from("pstore path must follow with `path=`"),
Kansho Nishida282115b2019-12-18 13:13:14 +09001441 });
1442 };
1443 PathBuf::from(&components[0][5..])
1444 },
1445 size: {
1446 if components[1].len() <= 5 || !components[1].starts_with("size=") {
1447 return Err(argument::Error::InvalidValue {
1448 value: components[1].to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001449 expected: String::from("pstore size must follow with `size=`"),
Kansho Nishida282115b2019-12-18 13:13:14 +09001450 });
1451 };
1452 components[1][5..]
1453 .parse()
1454 .map_err(|_| argument::Error::InvalidValue {
1455 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001456 expected: String::from("pstore size must be an integer"),
Kansho Nishida282115b2019-12-18 13:13:14 +09001457 })?
1458 },
1459 });
1460 }
Zach Reiznerefe95782017-08-26 18:05:48 -07001461 "host_ip" => {
Daniel Verkampaac28132018-10-15 14:58:48 -07001462 if cfg.host_ip.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -07001463 return Err(argument::Error::TooManyArguments(
1464 "`host_ip` already given".to_owned(),
1465 ));
Zach Reiznerefe95782017-08-26 18:05:48 -07001466 }
Daniel Verkampaac28132018-10-15 14:58:48 -07001467 cfg.host_ip =
Zach Reizner55a9e502018-10-03 10:22:32 -07001468 Some(
1469 value
1470 .unwrap()
1471 .parse()
1472 .map_err(|_| argument::Error::InvalidValue {
1473 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001474 expected: String::from("`host_ip` needs to be in the form \"x.x.x.x\""),
Zach Reizner55a9e502018-10-03 10:22:32 -07001475 })?,
1476 )
Zach Reiznerefe95782017-08-26 18:05:48 -07001477 }
1478 "netmask" => {
Daniel Verkampaac28132018-10-15 14:58:48 -07001479 if cfg.netmask.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -07001480 return Err(argument::Error::TooManyArguments(
1481 "`netmask` already given".to_owned(),
1482 ));
Zach Reiznerefe95782017-08-26 18:05:48 -07001483 }
Daniel Verkampaac28132018-10-15 14:58:48 -07001484 cfg.netmask =
Zach Reizner55a9e502018-10-03 10:22:32 -07001485 Some(
1486 value
1487 .unwrap()
1488 .parse()
1489 .map_err(|_| argument::Error::InvalidValue {
1490 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001491 expected: String::from("`netmask` needs to be in the form \"x.x.x.x\""),
Zach Reizner55a9e502018-10-03 10:22:32 -07001492 })?,
1493 )
Zach Reiznerefe95782017-08-26 18:05:48 -07001494 }
1495 "mac" => {
Daniel Verkampaac28132018-10-15 14:58:48 -07001496 if cfg.mac_address.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -07001497 return Err(argument::Error::TooManyArguments(
1498 "`mac` already given".to_owned(),
1499 ));
Zach Reiznerefe95782017-08-26 18:05:48 -07001500 }
Daniel Verkampaac28132018-10-15 14:58:48 -07001501 cfg.mac_address =
Zach Reizner55a9e502018-10-03 10:22:32 -07001502 Some(
1503 value
1504 .unwrap()
1505 .parse()
1506 .map_err(|_| argument::Error::InvalidValue {
1507 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001508 expected: String::from(
1509 "`mac` needs to be in the form \"XX:XX:XX:XX:XX:XX\"",
1510 ),
Zach Reizner55a9e502018-10-03 10:22:32 -07001511 })?,
1512 )
Zach Reiznerefe95782017-08-26 18:05:48 -07001513 }
Xiong Zhang773c7072020-03-20 10:39:55 +08001514 "net-vq-pairs" => {
1515 if cfg.net_vq_pairs.is_some() {
1516 return Err(argument::Error::TooManyArguments(
1517 "`net-vq-pairs` already given".to_owned(),
1518 ));
1519 }
1520 cfg.net_vq_pairs =
1521 Some(
1522 value
1523 .unwrap()
1524 .parse()
1525 .map_err(|_| argument::Error::InvalidValue {
1526 value: value.unwrap().to_owned(),
1527 expected: String::from(
1528 "this value for `net-vq-pairs` needs to be integer",
1529 ),
1530 })?,
1531 )
1532 }
1533
Stephen Barber28a5a612017-10-20 17:15:30 -07001534 "wayland-sock" => {
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09001535 let mut components = value.unwrap().split(',');
1536 let path =
1537 PathBuf::from(
1538 components
1539 .next()
1540 .ok_or_else(|| argument::Error::InvalidValue {
1541 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001542 expected: String::from("missing socket path"),
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09001543 })?,
1544 );
1545 let mut name = "";
1546 for c in components {
1547 let mut kv = c.splitn(2, '=');
1548 let (kind, value) = match (kv.next(), kv.next()) {
1549 (Some(kind), Some(value)) => (kind, value),
1550 _ => {
1551 return Err(argument::Error::InvalidValue {
1552 value: c.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001553 expected: String::from("option must be of the form `kind=value`"),
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09001554 })
1555 }
1556 };
1557 match kind {
1558 "name" => name = value,
1559 _ => {
1560 return Err(argument::Error::InvalidValue {
1561 value: kind.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001562 expected: String::from("unrecognized option"),
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09001563 })
1564 }
1565 }
Stephen Barber28a5a612017-10-20 17:15:30 -07001566 }
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09001567 if cfg.wayland_socket_paths.contains_key(name) {
1568 return Err(argument::Error::TooManyArguments(format!(
1569 "wayland socket name already used: '{}'",
1570 name
1571 )));
Stephen Barber28a5a612017-10-20 17:15:30 -07001572 }
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09001573 cfg.wayland_socket_paths.insert(name.to_string(), path);
Stephen Barber28a5a612017-10-20 17:15:30 -07001574 }
David Reveman52ba4e52018-04-22 21:42:09 -04001575 #[cfg(feature = "wl-dmabuf")]
Nicholas Vernefde29972021-07-06 22:02:05 +10001576 "wayland-dmabuf" => {}
Zach Reizner0f2cfb02019-06-19 17:46:03 -07001577 "x-display" => {
1578 if cfg.x_display.is_some() {
1579 return Err(argument::Error::TooManyArguments(
1580 "`x-display` already given".to_owned(),
1581 ));
1582 }
1583 cfg.x_display = Some(value.unwrap().to_owned());
1584 }
Zach Reizner65b98f12019-11-22 17:34:58 -08001585 "display-window-keyboard" => {
1586 cfg.display_window_keyboard = true;
1587 }
1588 "display-window-mouse" => {
1589 cfg.display_window_mouse = true;
1590 }
Zach Reiznerefe95782017-08-26 18:05:48 -07001591 "socket" => {
1592 if cfg.socket_path.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -07001593 return Err(argument::Error::TooManyArguments(
1594 "`socket` already given".to_owned(),
1595 ));
Zach Reiznerefe95782017-08-26 18:05:48 -07001596 }
1597 let mut socket_path = PathBuf::from(value.unwrap());
1598 if socket_path.is_dir() {
1599 socket_path.push(format!("crosvm-{}.sock", getpid()));
1600 }
1601 if socket_path.exists() {
1602 return Err(argument::Error::InvalidValue {
Zach Reizner55a9e502018-10-03 10:22:32 -07001603 value: socket_path.to_string_lossy().into_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001604 expected: String::from("this socket path already exists"),
Zach Reizner55a9e502018-10-03 10:22:32 -07001605 });
Zach Reiznerefe95782017-08-26 18:05:48 -07001606 }
1607 cfg.socket_path = Some(socket_path);
1608 }
Dylan Reidd0c9adc2017-10-02 19:04:50 -07001609 "disable-sandbox" => {
Lepton Wu9105e9f2019-03-14 11:38:31 -07001610 cfg.sandbox = false;
Dylan Reidd0c9adc2017-10-02 19:04:50 -07001611 }
Chirantan Ekbote88f9cba2017-08-28 09:51:18 -07001612 "cid" => {
Daniel Verkampaac28132018-10-15 14:58:48 -07001613 if cfg.cid.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -07001614 return Err(argument::Error::TooManyArguments(
1615 "`cid` alread given".to_owned(),
1616 ));
Chirantan Ekbote88f9cba2017-08-28 09:51:18 -07001617 }
Daniel Verkampaac28132018-10-15 14:58:48 -07001618 cfg.cid = Some(
1619 value
1620 .unwrap()
1621 .parse()
1622 .map_err(|_| argument::Error::InvalidValue {
1623 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001624 expected: String::from("this value for `cid` must be an unsigned integer"),
Daniel Verkampaac28132018-10-15 14:58:48 -07001625 })?,
1626 );
Chirantan Ekbote88f9cba2017-08-28 09:51:18 -07001627 }
Chirantan Ekboteebd56812018-04-16 19:32:04 -07001628 "shared-dir" => {
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001629 // This is formatted as multiple fields, each separated by ":". The first 2 fields are
1630 // fixed (src:tag). The rest may appear in any order:
1631 //
1632 // * type=TYPE - must be one of "p9" or "fs" (default: p9)
1633 // * uidmap=UIDMAP - a uid map in the format "inner outer count[,inner outer count]"
1634 // (default: "0 <current euid> 1")
1635 // * gidmap=GIDMAP - a gid map in the same format as uidmap
1636 // (default: "0 <current egid> 1")
Ryo Hashimoto6d924382021-06-21 20:59:04 +09001637 // * privileged_quota_uids=UIDS - Space-separated list of privileged uid values. When
1638 // performing quota-related operations, these UIDs are treated as if they have
1639 // CAP_FOWNER.
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001640 // * timeout=TIMEOUT - a timeout value in seconds, which indicates how long attributes
1641 // and directory contents should be considered valid (default: 5)
1642 // * cache=CACHE - one of "never", "always", or "auto" (default: auto)
1643 // * writeback=BOOL - indicates whether writeback caching should be enabled (default: false)
Chirantan Ekboteebd56812018-04-16 19:32:04 -07001644 let param = value.unwrap();
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001645 let mut components = param.split(':');
Zach Reizner55a9e502018-10-03 10:22:32 -07001646 let src =
1647 PathBuf::from(
1648 components
1649 .next()
1650 .ok_or_else(|| argument::Error::InvalidValue {
1651 value: param.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001652 expected: String::from("missing source path for `shared-dir`"),
Zach Reizner55a9e502018-10-03 10:22:32 -07001653 })?,
1654 );
1655 let tag = components
1656 .next()
1657 .ok_or_else(|| argument::Error::InvalidValue {
Chirantan Ekboteebd56812018-04-16 19:32:04 -07001658 value: param.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001659 expected: String::from("missing tag for `shared-dir`"),
David Tolnay2bac1e72018-12-12 14:33:42 -08001660 })?
1661 .to_owned();
Chirantan Ekboteebd56812018-04-16 19:32:04 -07001662
1663 if !src.is_dir() {
1664 return Err(argument::Error::InvalidValue {
1665 value: param.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001666 expected: String::from("source path for `shared-dir` must be a directory"),
Chirantan Ekboteebd56812018-04-16 19:32:04 -07001667 });
1668 }
1669
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001670 let mut shared_dir = SharedDir {
1671 src,
1672 tag,
1673 ..Default::default()
1674 };
1675 for opt in components {
1676 let mut o = opt.splitn(2, '=');
1677 let kind = o.next().ok_or_else(|| argument::Error::InvalidValue {
1678 value: opt.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001679 expected: String::from("`shared-dir` options must not be empty"),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001680 })?;
1681 let value = o.next().ok_or_else(|| argument::Error::InvalidValue {
1682 value: opt.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001683 expected: String::from("`shared-dir` options must be of the form `kind=value`"),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001684 })?;
1685
1686 match kind {
1687 "type" => {
1688 shared_dir.kind =
1689 value.parse().map_err(|_| argument::Error::InvalidValue {
1690 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001691 expected: String::from("`type` must be one of `fs` or `9p`"),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001692 })?
1693 }
1694 "uidmap" => shared_dir.uid_map = value.into(),
1695 "gidmap" => shared_dir.gid_map = value.into(),
Ryo Hashimoto6d924382021-06-21 20:59:04 +09001696 #[cfg(feature = "chromeos")]
1697 "privileged_quota_uids" => {
1698 shared_dir.fs_cfg.privileged_quota_uids =
1699 value.split(' ').map(|s| s.parse().unwrap()).collect();
1700 }
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001701 "timeout" => {
1702 let seconds = value.parse().map_err(|_| argument::Error::InvalidValue {
1703 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001704 expected: String::from("`timeout` must be an integer"),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001705 })?;
1706
1707 let dur = Duration::from_secs(seconds);
Andrew Walbran9cfdbd92021-01-11 17:40:34 +00001708 shared_dir.fs_cfg.entry_timeout = dur;
Chirantan Ekbote75ba8752020-10-27 18:33:02 +09001709 shared_dir.fs_cfg.attr_timeout = dur;
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001710 }
1711 "cache" => {
1712 let policy = value.parse().map_err(|_| argument::Error::InvalidValue {
1713 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001714 expected: String::from(
1715 "`cache` must be one of `never`, `always`, or `auto`",
1716 ),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001717 })?;
Chirantan Ekbote75ba8752020-10-27 18:33:02 +09001718 shared_dir.fs_cfg.cache_policy = policy;
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001719 }
1720 "writeback" => {
1721 let writeback =
1722 value.parse().map_err(|_| argument::Error::InvalidValue {
1723 value: value.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001724 expected: String::from("`writeback` must be a boolean"),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001725 })?;
Chirantan Ekbote75ba8752020-10-27 18:33:02 +09001726 shared_dir.fs_cfg.writeback = writeback;
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001727 }
Chirantan Ekboted994e512020-06-12 18:46:52 +09001728 "rewrite-security-xattrs" => {
1729 let rewrite_security_xattrs =
1730 value.parse().map_err(|_| argument::Error::InvalidValue {
1731 value: value.to_owned(),
1732 expected: String::from(
1733 "`rewrite-security-xattrs` must be a boolean",
1734 ),
1735 })?;
Chirantan Ekbote75ba8752020-10-27 18:33:02 +09001736 shared_dir.fs_cfg.rewrite_security_xattrs = rewrite_security_xattrs;
Chirantan Ekboted994e512020-06-12 18:46:52 +09001737 }
Chirantan Ekbote09357c82020-09-10 20:08:28 +09001738 "ascii_casefold" => {
1739 let ascii_casefold =
1740 value.parse().map_err(|_| argument::Error::InvalidValue {
1741 value: value.to_owned(),
1742 expected: String::from("`ascii_casefold` must be a boolean"),
1743 })?;
Chirantan Ekbote75ba8752020-10-27 18:33:02 +09001744 shared_dir.fs_cfg.ascii_casefold = ascii_casefold;
1745 shared_dir.p9_cfg.ascii_casefold = ascii_casefold;
Chirantan Ekbote09357c82020-09-10 20:08:28 +09001746 }
Chirantan Ekbote1b2d8dc2021-09-21 17:25:29 +09001747 "dax" => {
1748 let use_dax = value.parse().map_err(|_| argument::Error::InvalidValue {
1749 value: value.to_owned(),
1750 expected: String::from("`dax` must be a boolean"),
1751 })?;
1752 shared_dir.fs_cfg.use_dax = use_dax;
1753 }
Chirantan Ekbotef1cd8d72021-09-21 17:33:58 +09001754 "posix_acl" => {
1755 let posix_acl =
1756 value.parse().map_err(|_| argument::Error::InvalidValue {
1757 value: value.to_owned(),
1758 expected: String::from("`posix_acl` must be a boolean"),
1759 })?;
1760 shared_dir.fs_cfg.posix_acl = posix_acl;
1761 }
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001762 _ => {
1763 return Err(argument::Error::InvalidValue {
1764 value: kind.to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001765 expected: String::from("unrecognized option for `shared-dir`"),
Chirantan Ekbotebd4723b2019-07-17 10:50:30 +09001766 })
1767 }
1768 }
1769 }
1770 cfg.shared_dirs.push(shared_dir);
Chirantan Ekboteebd56812018-04-16 19:32:04 -07001771 }
Dylan Reide026ef02017-10-02 19:03:52 -07001772 "seccomp-policy-dir" => {
Dylan Reidd0c9adc2017-10-02 19:04:50 -07001773 // `value` is Some because we are in this match so it's safe to unwrap.
Daniel Verkampaac28132018-10-15 14:58:48 -07001774 cfg.seccomp_policy_dir = PathBuf::from(value.unwrap());
Zach Reizner55a9e502018-10-03 10:22:32 -07001775 }
Zach Reizner44863792019-06-26 14:22:08 -07001776 "seccomp-log-failures" => {
Matt Delco45caf912019-11-13 08:11:09 -08001777 // A side-effect of this flag is to force the use of .policy files
1778 // instead of .bpf files (.bpf files are expected and assumed to be
1779 // compiled to fail an unpermitted action with "trap").
1780 // Normally crosvm will first attempt to use a .bpf file, and if
1781 // not present it will then try to use a .policy file. It's up
1782 // to the build to decide which of these files is present for
1783 // crosvm to use (for CrOS the build will use .bpf files for
1784 // x64 builds and .policy files for arm/arm64 builds).
1785 //
1786 // This flag will likely work as expected for builds that use
1787 // .policy files. For builds that only use .bpf files the initial
1788 // result when using this flag is likely to be a file-not-found
1789 // error (since the .policy files are not present).
1790 // For .bpf builds you can either 1) manually add the .policy files,
1791 // or 2) do not use this command-line parameter and instead
1792 // temporarily change the build by passing "log" rather than
1793 // "trap" as the "--default-action" to compile_seccomp_policy.py.
Zach Reizner44863792019-06-26 14:22:08 -07001794 cfg.seccomp_log_failures = true;
1795 }
Zach Reizner8864cb02018-01-16 17:59:03 -08001796 "plugin" => {
Cody Schuffelen6d1ab502019-05-21 12:12:38 -07001797 if cfg.executable_path.is_some() {
1798 return Err(argument::Error::TooManyArguments(format!(
1799 "A VM executable was already specified: {:?}",
1800 cfg.executable_path
1801 )));
Zach Reizner8864cb02018-01-16 17:59:03 -08001802 }
Zach Reiznercc30d582018-01-23 21:16:42 -08001803 let plugin = PathBuf::from(value.unwrap().to_owned());
1804 if plugin.is_relative() {
1805 return Err(argument::Error::InvalidValue {
Zach Reizner55a9e502018-10-03 10:22:32 -07001806 value: plugin.to_string_lossy().into_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001807 expected: String::from("the plugin path must be an absolute path"),
Zach Reizner55a9e502018-10-03 10:22:32 -07001808 });
Zach Reiznercc30d582018-01-23 21:16:42 -08001809 }
Cody Schuffelen6d1ab502019-05-21 12:12:38 -07001810 cfg.executable_path = Some(Executable::Plugin(plugin));
Zach Reizner55a9e502018-10-03 10:22:32 -07001811 }
Dmitry Torokhov0f1770d2018-05-11 10:57:16 -07001812 "plugin-root" => {
1813 cfg.plugin_root = Some(PathBuf::from(value.unwrap().to_owned()));
Zach Reizner55a9e502018-10-03 10:22:32 -07001814 }
Chirantan Ekboted41d7262018-11-16 16:37:45 -08001815 "plugin-mount" => {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -08001816 let mount = parse_plugin_mount_option(value.unwrap())?;
1817 cfg.plugin_mounts.push(mount);
Chirantan Ekboted41d7262018-11-16 16:37:45 -08001818 }
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08001819 "plugin-mount-file" => {
1820 let file = File::open(value.unwrap()).map_err(|_| argument::Error::InvalidValue {
1821 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001822 expected: String::from("unable to open `plugin-mount-file` file"),
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08001823 })?;
1824 let reader = BufReader::new(file);
1825 for l in reader.lines() {
1826 let line = l.unwrap();
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09001827 let trimmed_line = line.splitn(2, '#').next().unwrap().trim();
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08001828 if !trimmed_line.is_empty() {
1829 let mount = parse_plugin_mount_option(trimmed_line)?;
1830 cfg.plugin_mounts.push(mount);
1831 }
1832 }
1833 }
Dmitry Torokhov1a6262b2019-03-01 00:34:03 -08001834 "plugin-gid-map" => {
Dmitry Torokhovc689fd92019-12-11 13:36:08 -08001835 let map = parse_plugin_gid_map_option(value.unwrap())?;
1836 cfg.plugin_gid_maps.push(map);
Dmitry Torokhov1a6262b2019-03-01 00:34:03 -08001837 }
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08001838 "plugin-gid-map-file" => {
1839 let file = File::open(value.unwrap()).map_err(|_| argument::Error::InvalidValue {
1840 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001841 expected: String::from("unable to open `plugin-gid-map-file` file"),
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08001842 })?;
1843 let reader = BufReader::new(file);
1844 for l in reader.lines() {
1845 let line = l.unwrap();
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09001846 let trimmed_line = line.splitn(2, '#').next().unwrap().trim();
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08001847 if !trimmed_line.is_empty() {
1848 let map = parse_plugin_gid_map_option(trimmed_line)?;
1849 cfg.plugin_gid_maps.push(map);
1850 }
1851 }
1852 }
Daniel Verkampaac28132018-10-15 14:58:48 -07001853 "vhost-net" => cfg.vhost_net = true,
Chirantan Ekbote5f787212018-05-31 15:31:31 -07001854 "tap-fd" => {
Jorge E. Moreirab7952802019-02-12 16:43:05 -08001855 cfg.tap_fd.push(
1856 value
1857 .unwrap()
1858 .parse()
1859 .map_err(|_| argument::Error::InvalidValue {
1860 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001861 expected: String::from(
1862 "this value for `tap-fd` must be an unsigned integer",
1863 ),
Jorge E. Moreirab7952802019-02-12 16:43:05 -08001864 })?,
1865 );
Chirantan Ekbote5f787212018-05-31 15:31:31 -07001866 }
Alexandre Courbot993aa7f2021-12-09 14:51:29 +09001867 "tap-name" => {
1868 cfg.tap_name.push(value.unwrap().to_owned());
1869 }
Jason Macnakcc7070b2019-11-06 14:48:12 -08001870 #[cfg(feature = "gpu")]
Zach Reizner3a8100a2017-09-13 19:15:43 -07001871 "gpu" => {
Chia-I Wu16fb6592021-11-10 11:45:32 -08001872 let gpu_parameters = cfg.gpu_parameters.get_or_insert_with(Default::default);
1873 parse_gpu_options(value, gpu_parameters)?;
Jason Macnakd659a0d2021-03-15 15:33:01 -07001874 }
1875 #[cfg(feature = "gpu")]
1876 "gpu-display" => {
Chia-I Wu16fb6592021-11-10 11:45:32 -08001877 let gpu_parameters = cfg.gpu_parameters.get_or_insert_with(Default::default);
1878 parse_gpu_display_options(value, gpu_parameters)?;
1879 }
1880 #[cfg(all(feature = "gpu", feature = "virgl_renderer_next"))]
1881 "gpu-render-server" => {
1882 let gpu_parameters = cfg.gpu_parameters.get_or_insert_with(Default::default);
1883 parse_gpu_render_server_options(value, gpu_parameters)?;
Zach Reizner3a8100a2017-09-13 19:15:43 -07001884 }
David Tolnay43f8e212019-02-13 17:28:16 -08001885 "software-tpm" => {
1886 cfg.software_tpm = true;
1887 }
Jorge E. Moreira99d3f082019-03-07 10:59:54 -08001888 "single-touch" => {
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09001889 let mut it = value.unwrap().split(':');
Jorge E. Moreira99d3f082019-03-07 10:59:54 -08001890
1891 let mut single_touch_spec =
1892 TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned()));
1893 if let Some(width) = it.next() {
Kaiyi Libccb4eb2020-02-06 17:53:11 -08001894 single_touch_spec.set_width(width.trim().parse().unwrap());
Jorge E. Moreira99d3f082019-03-07 10:59:54 -08001895 }
1896 if let Some(height) = it.next() {
Kaiyi Libccb4eb2020-02-06 17:53:11 -08001897 single_touch_spec.set_height(height.trim().parse().unwrap());
Jorge E. Moreira99d3f082019-03-07 10:59:54 -08001898 }
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07001899 cfg.virtio_single_touch.push(single_touch_spec);
Jorge E. Moreira99d3f082019-03-07 10:59:54 -08001900 }
Tristan Muntsinger486cffc2020-09-29 22:05:41 +00001901 "multi-touch" => {
Tristan Muntsinger486cffc2020-09-29 22:05:41 +00001902 let mut it = value.unwrap().split(':');
1903
1904 let mut multi_touch_spec =
1905 TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned()));
1906 if let Some(width) = it.next() {
1907 multi_touch_spec.set_width(width.trim().parse().unwrap());
1908 }
1909 if let Some(height) = it.next() {
1910 multi_touch_spec.set_height(height.trim().parse().unwrap());
1911 }
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07001912 cfg.virtio_multi_touch.push(multi_touch_spec);
Tristan Muntsinger486cffc2020-09-29 22:05:41 +00001913 }
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001914 "trackpad" => {
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09001915 let mut it = value.unwrap().split(':');
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001916
1917 let mut trackpad_spec =
Jorge E. Moreira99d3f082019-03-07 10:59:54 -08001918 TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned()));
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001919 if let Some(width) = it.next() {
Kaiyi Libccb4eb2020-02-06 17:53:11 -08001920 trackpad_spec.set_width(width.trim().parse().unwrap());
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001921 }
1922 if let Some(height) = it.next() {
Kaiyi Libccb4eb2020-02-06 17:53:11 -08001923 trackpad_spec.set_height(height.trim().parse().unwrap());
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001924 }
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07001925 cfg.virtio_trackpad.push(trackpad_spec);
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001926 }
1927 "mouse" => {
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07001928 cfg.virtio_mice
1929 .push(PathBuf::from(value.unwrap().to_owned()));
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001930 }
1931 "keyboard" => {
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07001932 cfg.virtio_keyboard
1933 .push(PathBuf::from(value.unwrap().to_owned()));
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001934 }
Daniel Norman5e23df72021-03-11 10:11:02 -08001935 "switches" => {
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07001936 cfg.virtio_switches
1937 .push(PathBuf::from(value.unwrap().to_owned()));
Daniel Norman5e23df72021-03-11 10:11:02 -08001938 }
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001939 "evdev" => {
1940 let dev_path = PathBuf::from(value.unwrap());
1941 if !dev_path.exists() {
1942 return Err(argument::Error::InvalidValue {
1943 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08001944 expected: String::from("this input device path does not exist"),
Jorge E. Moreiradffec502019-01-14 18:44:49 -08001945 });
1946 }
1947 cfg.virtio_input_evdevs.push(dev_path);
1948 }
Miriam Zimmerman26ac9282019-01-29 21:21:48 -08001949 "split-irqchip" => {
1950 cfg.split_irqchip = true;
1951 }
Daniel Verkampe403f5c2018-12-11 16:29:26 -08001952 "initrd" => {
1953 cfg.initrd_path = Some(PathBuf::from(value.unwrap().to_owned()));
1954 }
Cody Schuffelen6d1ab502019-05-21 12:12:38 -07001955 "bios" => {
1956 if cfg.executable_path.is_some() {
1957 return Err(argument::Error::TooManyArguments(format!(
1958 "A VM executable was already specified: {:?}",
1959 cfg.executable_path
1960 )));
1961 }
1962 cfg.executable_path = Some(Executable::Bios(PathBuf::from(value.unwrap().to_owned())));
1963 }
Tomasz Nowicki344eb142021-09-22 05:51:58 +00001964 "vfio" | "vfio-platform" => {
Tomasz Nowicki71aca792021-06-09 18:53:49 +00001965 let vfio_type = name.parse().unwrap();
1966 let vfio_dev = VfioCommand::new(vfio_type, value.unwrap())?;
1967 cfg.vfio.push(vfio_dev);
Xiong Zhang17b0daf2019-04-23 17:14:50 +08001968 }
Alexandre Courbot8230abf2021-06-26 22:49:26 +09001969 #[cfg(feature = "video-decoder")]
Keiichi Watanabe57df6a02019-12-06 22:24:40 +09001970 "video-decoder" => {
Alexandre Courbotb42b3e52021-07-09 23:38:57 +09001971 cfg.video_dec = Some(parse_video_options(value)?);
Keiichi Watanabe57df6a02019-12-06 22:24:40 +09001972 }
Alexandre Courbot8230abf2021-06-26 22:49:26 +09001973 #[cfg(feature = "video-encoder")]
Keiichi Watanabe57df6a02019-12-06 22:24:40 +09001974 "video-encoder" => {
Alexandre Courbotb42b3e52021-07-09 23:38:57 +09001975 cfg.video_enc = Some(parse_video_options(value)?);
Keiichi Watanabe57df6a02019-12-06 22:24:40 +09001976 }
Tomasz Jeznach42644642020-05-20 23:27:59 -07001977 "acpi-table" => {
1978 let acpi_table = PathBuf::from(value.unwrap());
1979 if !acpi_table.exists() {
1980 return Err(argument::Error::InvalidValue {
1981 value: value.unwrap().to_owned(),
1982 expected: String::from("the acpi-table path does not exist"),
1983 });
1984 }
1985 if !acpi_table.is_file() {
1986 return Err(argument::Error::InvalidValue {
1987 value: value.unwrap().to_owned(),
1988 expected: String::from("the acpi-table path should be a file"),
1989 });
1990 }
1991
1992 cfg.acpi_tables.push(acpi_table);
1993 }
Will Deacon6560c182020-10-06 18:47:18 +01001994 "protected-vm" => {
Andrew Walbran413f8542021-01-08 13:29:03 +00001995 cfg.protected_vm = ProtectionType::Protected;
Will Deacon6560c182020-10-06 18:47:18 +01001996 }
Andrew Walbran0bbbb682021-12-13 13:42:07 +00001997 "protected-vm-without-firmware" => {
1998 cfg.protected_vm = ProtectionType::ProtectedWithoutFirmware;
1999 }
Chuanxiao Dongfd5626c2020-04-27 16:35:33 +08002000 "battery" => {
2001 let params = parse_battery_options(value)?;
2002 cfg.battery_type = Some(params);
2003 }
Keiichi Watanabec5262e92020-10-21 15:57:33 +09002004 #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
2005 "gdb" => {
2006 let port = value
2007 .unwrap()
2008 .parse()
2009 .map_err(|_| argument::Error::InvalidValue {
2010 value: value.unwrap().to_owned(),
2011 expected: String::from("expected a valid port number"),
2012 })?;
2013 cfg.gdb = Some(port);
2014 }
Charles William Dick0e3d4b62020-12-14 12:16:46 +09002015 "balloon_bias_mib" => {
2016 cfg.balloon_bias =
2017 value
2018 .unwrap()
2019 .parse::<i64>()
2020 .map_err(|_| argument::Error::InvalidValue {
2021 value: value.unwrap().to_owned(),
2022 expected: String::from("expected a valid ballon bias in MiB"),
2023 })?
2024 * 1024
2025 * 1024; // cfg.balloon_bias is in bytes.
2026 }
Keiichi Watanabef3a37f42021-01-21 15:41:11 +09002027 "vhost-user-blk" => cfg.vhost_user_blk.push(VhostUserOption {
2028 socket: PathBuf::from(value.unwrap()),
2029 }),
Federico 'Morg' Pareschi70fc7de2021-04-08 15:43:13 +09002030 "vhost-user-console" => cfg.vhost_user_console.push(VhostUserOption {
2031 socket: PathBuf::from(value.unwrap()),
2032 }),
Chirantan Ekbote44292f52021-06-25 18:31:41 +09002033 "vhost-user-gpu" => cfg.vhost_user_gpu.push(VhostUserOption {
2034 socket: PathBuf::from(value.unwrap()),
2035 }),
JaeMan Parkeb9cc532021-07-02 15:02:59 +09002036 "vhost-user-mac80211-hwsim" => {
2037 cfg.vhost_user_mac80211_hwsim = Some(VhostUserOption {
2038 socket: PathBuf::from(value.unwrap()),
2039 });
2040 }
Keiichi Watanabe60686582021-03-12 04:53:51 +09002041 "vhost-user-net" => cfg.vhost_user_net.push(VhostUserOption {
2042 socket: PathBuf::from(value.unwrap()),
2043 }),
Woody Chow1b16db12021-04-02 16:59:59 +09002044 #[cfg(feature = "audio")]
2045 "vhost-user-snd" => cfg.vhost_user_snd.push(VhostUserOption {
2046 socket: PathBuf::from(value.unwrap()),
2047 }),
Chirantan Ekbote84091e52021-09-10 18:43:17 +09002048 "vhost-user-vsock" => cfg.vhost_user_vsock.push(VhostUserOption {
2049 socket: PathBuf::from(value.unwrap()),
2050 }),
Chirantan Ekbote2ee9dcd2021-05-26 18:21:44 +09002051 "vhost-user-wl" => {
2052 let mut components = value.unwrap().splitn(2, ":");
2053 let socket = components.next().map(PathBuf::from).ok_or_else(|| {
2054 argument::Error::InvalidValue {
2055 value: value.unwrap().to_owned(),
2056 expected: String::from("missing socket path"),
2057 }
2058 })?;
2059 let vm_tube = components.next().map(PathBuf::from).ok_or_else(|| {
2060 argument::Error::InvalidValue {
2061 value: value.unwrap().to_owned(),
2062 expected: String::from("missing vm tube path"),
2063 }
2064 })?;
2065 cfg.vhost_user_wl
2066 .push(VhostUserWlOption { socket, vm_tube });
2067 }
Woody Chow5890b702021-02-12 14:57:02 +09002068 "vhost-user-fs" => {
2069 // (socket:tag)
2070 let param = value.unwrap();
2071 let mut components = param.split(':');
2072 let socket =
2073 PathBuf::from(
2074 components
2075 .next()
2076 .ok_or_else(|| argument::Error::InvalidValue {
2077 value: param.to_owned(),
2078 expected: String::from("missing socket path for `vhost-user-fs`"),
2079 })?,
2080 );
2081 let tag = components
2082 .next()
2083 .ok_or_else(|| argument::Error::InvalidValue {
2084 value: param.to_owned(),
2085 expected: String::from("missing tag for `vhost-user-fs`"),
2086 })?
2087 .to_owned();
2088 cfg.vhost_user_fs.push(VhostUserFsOption { socket, tag });
2089 }
Tomasz Jeznach3ce74762021-02-26 01:01:53 -08002090 #[cfg(feature = "direct")]
2091 "direct-pmio" => {
2092 if cfg.direct_pmio.is_some() {
2093 return Err(argument::Error::TooManyArguments(
2094 "`direct_pmio` already given".to_owned(),
2095 ));
2096 }
2097 cfg.direct_pmio = Some(parse_direct_io_options(value)?);
2098 }
Tomasz Jeznach7271f752021-03-04 01:44:06 -08002099 #[cfg(feature = "direct")]
Tomasz Jeznach9e6c6332021-05-27 21:49:14 -07002100 "direct-mmio" => {
2101 if cfg.direct_mmio.is_some() {
2102 return Err(argument::Error::TooManyArguments(
2103 "`direct_mmio` already given".to_owned(),
2104 ));
2105 }
2106 cfg.direct_mmio = Some(parse_direct_io_options(value)?);
2107 }
2108 #[cfg(feature = "direct")]
Tomasz Jeznach7271f752021-03-04 01:44:06 -08002109 "direct-level-irq" => {
2110 cfg.direct_level_irq
2111 .push(
2112 value
2113 .unwrap()
2114 .parse()
2115 .map_err(|_| argument::Error::InvalidValue {
2116 value: value.unwrap().to_owned(),
2117 expected: String::from(
2118 "this value for `direct-level-irq` must be an unsigned integer",
2119 ),
2120 })?,
2121 );
2122 }
2123 #[cfg(feature = "direct")]
2124 "direct-edge-irq" => {
2125 cfg.direct_edge_irq
2126 .push(
2127 value
2128 .unwrap()
2129 .parse()
2130 .map_err(|_| argument::Error::InvalidValue {
2131 value: value.unwrap().to_owned(),
2132 expected: String::from(
2133 "this value for `direct-edge-irq` must be an unsigned integer",
2134 ),
2135 })?,
2136 );
2137 }
Tomasz Jeznachccb26942021-03-30 22:44:11 -07002138 "dmi" => {
2139 if cfg.dmi_path.is_some() {
2140 return Err(argument::Error::TooManyArguments(
2141 "`dmi` already given".to_owned(),
2142 ));
2143 }
2144 let dmi_path = PathBuf::from(value.unwrap());
2145 if !dmi_path.exists() {
2146 return Err(argument::Error::InvalidValue {
2147 value: value.unwrap().to_owned(),
2148 expected: String::from("the dmi path does not exist"),
2149 });
2150 }
2151 if !dmi_path.is_dir() {
2152 return Err(argument::Error::InvalidValue {
2153 value: value.unwrap().to_owned(),
2154 expected: String::from("the dmi path should be directory"),
2155 });
2156 }
2157 cfg.dmi_path = Some(dmi_path);
2158 }
Tomasz Jeznachd93c29f2021-04-12 11:00:24 -07002159 "no-legacy" => {
2160 cfg.no_legacy = true;
2161 }
ZhaoLiu2aaf7ad2021-10-10 18:22:29 +08002162 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
2163 "host-cpu-topology" => {
2164 cfg.host_cpu_topology = true;
2165 }
Mattias Nisslerde2c6402021-10-21 12:05:29 +00002166 "stub-pci-device" => {
2167 cfg.stub_pci_devices.push(parse_stub_pci_parameters(value)?);
2168 }
Abhishek Bhardwaj103c1b72021-11-01 15:52:23 -07002169 "vvu-proxy" => cfg.vvu_proxy.push(VhostUserOption {
2170 socket: PathBuf::from(value.unwrap()),
2171 }),
Chuanxiao Dongd4468612022-01-14 14:21:17 +08002172 "coiommu" => {
2173 let mut params: devices::CoIommuParameters = Default::default();
2174 if let Some(v) = value {
2175 let opts = v
2176 .split(',')
2177 .map(|frag| frag.splitn(2, '='))
2178 .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
2179
2180 for (k, v) in opts {
2181 match k {
2182 "unpin_policy" => {
2183 params.unpin_policy = v
2184 .parse::<devices::CoIommuUnpinPolicy>()
2185 .map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?
2186 }
2187 "unpin_interval" => {
2188 params.unpin_interval =
2189 Duration::from_secs(v.parse::<u64>().map_err(|e| {
2190 argument::Error::UnknownArgument(format!("{}", e))
2191 })?)
2192 }
2193 "unpin_limit" => {
2194 let limit = v
2195 .parse::<u64>()
2196 .map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?;
2197
2198 if limit == 0 {
2199 return Err(argument::Error::InvalidValue {
2200 value: v.to_owned(),
2201 expected: String::from("Please use non-zero unpin_limit value"),
2202 });
2203 }
2204
2205 params.unpin_limit = Some(limit)
2206 }
2207 _ => {
2208 return Err(argument::Error::UnknownArgument(format!(
2209 "coiommu parameter {}",
2210 k
2211 )));
2212 }
2213 }
2214 }
2215 }
2216
2217 if cfg.coiommu_param.is_some() {
2218 return Err(argument::Error::TooManyArguments(
2219 "coiommu param already given".to_owned(),
2220 ));
2221 }
2222 cfg.coiommu_param = Some(params);
2223 }
Zach Reiznerefe95782017-08-26 18:05:48 -07002224 "help" => return Err(argument::Error::PrintHelp),
2225 _ => unreachable!(),
2226 }
2227 Ok(())
2228}
2229
Kaiyi Libccb4eb2020-02-06 17:53:11 -08002230fn validate_arguments(cfg: &mut Config) -> std::result::Result<(), argument::Error> {
2231 if cfg.executable_path.is_none() {
2232 return Err(argument::Error::ExpectedArgument("`KERNEL`".to_owned()));
2233 }
2234 if cfg.host_ip.is_some() || cfg.netmask.is_some() || cfg.mac_address.is_some() {
2235 if cfg.host_ip.is_none() {
2236 return Err(argument::Error::ExpectedArgument(
2237 "`host_ip` missing from network config".to_owned(),
2238 ));
2239 }
2240 if cfg.netmask.is_none() {
2241 return Err(argument::Error::ExpectedArgument(
2242 "`netmask` missing from network config".to_owned(),
2243 ));
2244 }
2245 if cfg.mac_address.is_none() {
2246 return Err(argument::Error::ExpectedArgument(
2247 "`mac` missing from network config".to_owned(),
2248 ));
2249 }
2250 }
2251 if cfg.plugin_root.is_some() && !executable_is_plugin(&cfg.executable_path) {
2252 return Err(argument::Error::ExpectedArgument(
2253 "`plugin-root` requires `plugin`".to_owned(),
2254 ));
2255 }
2256 #[cfg(feature = "gpu")]
2257 {
Jason Macnakd659a0d2021-03-15 15:33:01 -07002258 if let Some(gpu_parameters) = cfg.gpu_parameters.as_mut() {
2259 if gpu_parameters.displays.is_empty() {
2260 gpu_parameters.displays.push(GpuDisplayParameters {
2261 width: DEFAULT_DISPLAY_WIDTH,
2262 height: DEFAULT_DISPLAY_HEIGHT,
2263 });
2264 }
2265
2266 let width = gpu_parameters.displays[0].width;
2267 let height = gpu_parameters.displays[0].height;
2268
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07002269 if let Some(virtio_multi_touch) = cfg.virtio_multi_touch.first_mut() {
Tristan Muntsinger486cffc2020-09-29 22:05:41 +00002270 virtio_multi_touch.set_default_size(width, height);
2271 }
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07002272 if let Some(virtio_single_touch) = cfg.virtio_single_touch.first_mut() {
Kaiyi Libccb4eb2020-02-06 17:53:11 -08002273 virtio_single_touch.set_default_size(width, height);
2274 }
2275 }
2276 }
Keiichi Watanabec5262e92020-10-21 15:57:33 +09002277 #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
Dennis Kempinc3dedf32021-11-12 14:42:28 -08002278 if cfg.gdb.is_some() && cfg.vcpu_count.unwrap_or(1) != 1 {
2279 return Err(argument::Error::ExpectedArgument(
2280 "`gdb` requires the number of vCPU to be 1".to_owned(),
2281 ));
Keiichi Watanabec5262e92020-10-21 15:57:33 +09002282 }
ZhaoLiu2aaf7ad2021-10-10 18:22:29 +08002283 if cfg.host_cpu_topology {
ZhaoLiu1e6e7b22021-12-06 17:23:21 +08002284 if cfg.no_smt {
2285 return Err(argument::Error::ExpectedArgument(
2286 "`host-cpu-topology` cannot be set at the same time as `no_smt`, since \
2287 the smt of the Guest is the same as that of the Host when \
2288 `host-cpu-topology` is set."
2289 .to_owned(),
2290 ));
2291 }
2292
ZhaoLiu2aaf7ad2021-10-10 18:22:29 +08002293 // Safe because we pass a flag for this call and the host supports this system call
2294 let pcpu_count = unsafe { libc::sysconf(libc::_SC_NPROCESSORS_CONF) } as usize;
2295 if cfg.vcpu_count.is_some() {
2296 if pcpu_count != cfg.vcpu_count.unwrap() {
2297 return Err(argument::Error::ExpectedArgument(format!(
2298 "`host-cpu-topology` requires the count of vCPUs({}) to equal the \
2299 count of CPUs({}) on host.",
2300 cfg.vcpu_count.unwrap(),
2301 pcpu_count
2302 )));
2303 }
2304 } else {
2305 cfg.vcpu_count = Some(pcpu_count);
2306 }
2307
2308 match &cfg.vcpu_affinity {
2309 None => {
2310 let mut affinity_map = BTreeMap::new();
2311 for cpu_id in 0..cfg.vcpu_count.unwrap() {
2312 affinity_map.insert(cpu_id, vec![cpu_id]);
2313 }
2314 cfg.vcpu_affinity = Some(VcpuAffinity::PerVcpu(affinity_map));
2315 }
2316 _ => {
2317 return Err(argument::Error::ExpectedArgument(
2318 "`host-cpu-topology` requires not to set `cpu-affinity` at the same time"
2319 .to_owned(),
2320 ));
2321 }
2322 }
2323 }
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07002324 set_default_serial_parameters(&mut cfg.serial_parameters);
Kaiyi Libccb4eb2020-02-06 17:53:11 -08002325 Ok(())
2326}
2327
Dmitry Torokhovf75699f2021-12-03 11:19:13 -08002328enum CommandStatus {
2329 Success,
2330 VmReset,
2331 VmStop,
2332}
2333
2334fn run_vm(args: std::env::Args) -> std::result::Result<CommandStatus, ()> {
Zach Reiznerefe95782017-08-26 18:05:48 -07002335 let arguments =
2336 &[Argument::positional("KERNEL", "bzImage of kernel to run"),
Christian Blichmann33d56772021-03-04 19:03:54 +01002337 Argument::value("kvm-device", "PATH", "Path to the KVM device. (default /dev/kvm)"),
Christian Blichmann50f95912021-11-05 16:59:39 +01002338 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 +01002339 Argument::value("vhost-vsock-device", "PATH", "Path to the vhost-vsock device. (default /dev/vhost-vsock)"),
2340 Argument::value("vhost-net-device", "PATH", "Path to the vhost-net device. (default /dev/vhost-net)"),
Tristan Muntsinger4133b012018-12-21 16:01:56 -08002341 Argument::value("android-fstab", "PATH", "Path to Android fstab"),
Daniel Verkampe403f5c2018-12-11 16:29:26 -08002342 Argument::short_value('i', "initrd", "PATH", "Initial ramdisk to load."),
Zach Reiznerefe95782017-08-26 18:05:48 -07002343 Argument::short_value('p',
2344 "params",
2345 "PARAMS",
Zach Reiznerbb678712018-01-30 18:13:04 -08002346 "Extra kernel or plugin command line arguments. Can be given more than once."),
Zach Reiznerefe95782017-08-26 18:05:48 -07002347 Argument::short_value('c', "cpus", "N", "Number of VCPUs. (default: 1)"),
Daniel Verkampc677fb42020-09-08 13:47:49 -07002348 Argument::value("cpu-affinity", "CPUSET", "Comma-separated list of CPUs or CPU ranges to run VCPUs on (e.g. 0,1-3,5)
2349 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 -07002350 Argument::value("cpu-cluster", "CPUSET", "Group the given CPUs into a cluster (default: no clusters)"),
2351 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 -07002352 Argument::flag("per-vm-core-scheduling", "Enable per-VM core scheduling intead of the default one (per-vCPU core scheduing) by
2353 making all vCPU threads share same cookie for core scheduling.
2354 This option is no-op on devices that have neither MDS nor L1TF vulnerability."),
Woody Chow737ff122021-03-22 17:49:57 +09002355 #[cfg(feature = "audio_cras")]
Woody Chow0b2b6062021-09-03 15:40:02 +09002356 Argument::value("cras-snd",
Chih-Yang Hsia99829b32021-12-08 22:00:48 +08002357 "[capture=true,client=crosvm,socket=unified,num_output_streams=1,num_input_streams=1]",
Woody Chow0b2b6062021-09-03 15:40:02 +09002358 "Comma separated key=value pairs for setting up cras snd devices.
2359 Possible key values:
Chih-Yang Hsiaa2b4fc02021-12-12 12:43:56 +08002360 capture - Enable audio capture. Default to false.
Woody Chow0b2b6062021-09-03 15:40:02 +09002361 client_type - Set specific client type for cras backend.
Chih-Yang Hsia99829b32021-12-08 22:00:48 +08002362 num_output_streams - Set number of output PCM streams
2363 num_input_streams - Set number of input PCM streams"),
Suleiman Souhlal015c3c12020-10-07 14:15:41 +09002364 Argument::flag("no-smt", "Don't use SMT in the guest"),
Kansho Nishidaab205af2020-08-13 18:17:50 +09002365 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 +09002366 Argument::flag("delay-rt", "Don't set VCPUs real-time until make-rt command is run"),
Zach Reiznerefe95782017-08-26 18:05:48 -07002367 Argument::short_value('m',
2368 "mem",
2369 "N",
2370 "Amount of guest memory in MiB. (default: 256)"),
Sergey Senozhatsky1e369c52021-04-13 20:23:51 +09002371 Argument::flag("hugepages", "Advise the kernel to use Huge Pages for guest memory mappings."),
Zach Reiznerefe95782017-08-26 18:05:48 -07002372 Argument::short_value('r',
2373 "root",
Daniel Verkampe73c80f2019-11-08 10:11:16 -08002374 "PATH[,key=value[,key=value[,...]]",
2375 "Path to a root disk image followed by optional comma-separated options.
2376 Like `--disk` but adds appropriate kernel command line option.
2377 See --disk for valid options."),
2378 Argument::value("rwroot", "PATH[,key=value[,key=value[,...]]", "Path to a writable root disk image followed by optional comma-separated options.
2379 See --disk for valid options."),
2380 Argument::short_value('d', "disk", "PATH[,key=value[,key=value[,...]]", "Path to a disk image followed by optional comma-separated options.
2381 Valid keys:
Daniel Verkamp27672232019-12-06 17:26:55 +11002382 sparse=BOOL - Indicates whether the disk should support the discard operation (default: true)
Daniel Verkamp4e1f99a2020-06-01 12:47:21 -07002383 block_size=BYTES - Set the reported block size of the disk (default: 512)
Junichi Uekawa7bea39f2021-07-16 14:05:06 +09002384 id=STRING - Set the block device identifier to an ASCII string, up to 20 characters (default: no ID)
2385 o_direct=BOOL - Use O_DIRECT mode to bypass page cache"),
Daniel Verkampe73c80f2019-11-08 10:11:16 -08002386 Argument::value("rwdisk", "PATH[,key=value[,key=value[,...]]", "Path to a writable disk image followed by optional comma-separated options.
2387 See --disk for valid options."),
Jakub Starona3411ea2019-04-24 10:55:25 -07002388 Argument::value("rw-pmem-device", "PATH", "Path to a writable disk image."),
2389 Argument::value("pmem-device", "PATH", "Path to a disk image."),
Kansho Nishida282115b2019-12-18 13:13:14 +09002390 Argument::value("pstore", "path=PATH,size=SIZE", "Path to pstore buffer backend file follewed by size."),
Zach Reiznerefe95782017-08-26 18:05:48 -07002391 Argument::value("host_ip",
2392 "IP",
2393 "IP address to assign to host tap interface."),
2394 Argument::value("netmask", "NETMASK", "Netmask for VM subnet."),
2395 Argument::value("mac", "MAC", "MAC address for VM."),
Xiong Zhang773c7072020-03-20 10:39:55 +08002396 Argument::value("net-vq-pairs", "N", "virtio net virtual queue paris. (default: 1)"),
Andrew Scull1590e6f2020-03-18 18:00:47 +00002397 #[cfg(feature = "audio")]
Judy Hsiaod5c1e962020-02-04 12:30:01 +08002398 Argument::value("ac97",
paulhsia83d51602021-03-09 17:13:14 +08002399 "[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 +08002400 "Comma separated key=value pairs for setting up Ac97 devices. Can be given more than once .
Junichi Uekawaaab74e42021-07-19 16:12:46 +09002401 Possible key values:
2402 backend=(null, cras, vios) - Where to route the audio device. If not provided, backend will default to null.
2403 `null` for /dev/null, cras for CRAS server and vios for VioS server.
2404 capture - Enable audio capture
2405 capture_effects - | separated effects to be enabled for recording. The only supported effect value now is EchoCancellation or aec.
2406 client_type - Set specific client type for cras backend.
Woody Chowb27dea42021-09-08 15:51:22 +09002407 socket_type - Set specific socket type for cras backend.
Junichi Uekawaaab74e42021-07-19 16:12:46 +09002408 server - The to the VIOS server (unix socket)."),
Jorge E. Moreirad4562d02021-06-28 16:21:12 -07002409 #[cfg(feature = "audio")]
2410 Argument::value("sound", "[PATH]", "Path to the VioS server socket for setting up virtio-snd devices."),
Trent Begin17ccaad2019-04-17 13:51:25 -06002411 Argument::value("serial",
Daniel Verkampa7b6a1c2020-03-09 13:16:46 -07002412 "type=TYPE,[hardware=HW,num=NUM,path=PATH,input=PATH,console,earlycon,stdin]",
Jason Macnakcc7070b2019-11-06 14:48:12 -08002413 "Comma separated key=value pairs for setting up serial devices. Can be given more than once.
Junichi Uekawaaab74e42021-07-19 16:12:46 +09002414 Possible key values:
2415 type=(stdout,syslog,sink,file) - Where to route the serial device
2416 hardware=(serial,virtio-console) - Which type of serial hardware to emulate. Defaults to 8250 UART (serial).
2417 num=(1,2,3,4) - Serial Device Number. If not provided, num will default to 1.
2418 path=PATH - The path to the file to write to when type=file
2419 input=PATH - The path to the file to read from when not stdin
2420 console - Use this serial device as the guest console. Can only be given once. Will default to first serial port if not provided.
2421 earlycon - Use this serial device as the early console. Can only be given once.
2422 stdin - Direct standard input to this serial device. Can only be given once. Will default to first serial port if not provided.
2423 "),
Trent Begin17ccaad2019-04-17 13:51:25 -06002424 Argument::value("syslog-tag", "TAG", "When logging to syslog, use the provided tag."),
Zach Reizner0f2cfb02019-06-19 17:46:03 -07002425 Argument::value("x-display", "DISPLAY", "X11 display name to use."),
Zach Reizner65b98f12019-11-22 17:34:58 -08002426 Argument::flag("display-window-keyboard", "Capture keyboard input from the display window."),
2427 Argument::flag("display-window-mouse", "Capture keyboard input from the display window."),
Ryo Hashimoto0b788de2019-12-10 17:14:13 +09002428 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 -04002429 #[cfg(feature = "wl-dmabuf")]
Nicholas Vernefde29972021-07-06 22:02:05 +10002430 Argument::flag("wayland-dmabuf", "DEPRECATED: Enable support for DMABufs in Wayland device."),
Zach Reiznerefe95782017-08-26 18:05:48 -07002431 Argument::short_value('s',
2432 "socket",
2433 "PATH",
2434 "Path to put the control socket. If PATH is a directory, a name will be generated."),
Dylan Reidd0c9adc2017-10-02 19:04:50 -07002435 Argument::flag("disable-sandbox", "Run all devices in one, non-sandboxed process."),
Chirantan Ekboteebd56812018-04-16 19:32:04 -07002436 Argument::value("cid", "CID", "Context ID for virtual sockets."),
Chirantan Ekbotef1cd8d72021-09-21 17:33:58 +09002437 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 +09002438 "Colon-separated options for configuring a directory to be shared with the VM.
Junichi Uekawaaab74e42021-07-19 16:12:46 +09002439 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.
2440 The remaining fields are key=value pairs that may appear in any order. Valid keys are:
2441 type=(p9, fs) - Indicates whether the directory should be shared via virtio-9p or virtio-fs (default: p9).
2442 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).
2443 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).
2444 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.
2445 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 +09002446 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.
2447 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 +09002448 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 +09002449"),
Dylan Reide026ef02017-10-02 19:03:52 -07002450 Argument::value("seccomp-policy-dir", "PATH", "Path to seccomp .policy files."),
Zach Reizner44863792019-06-26 14:22:08 -07002451 Argument::flag("seccomp-log-failures", "Instead of seccomp filter failures being fatal, they will be logged instead."),
Zach Reizner8864cb02018-01-16 17:59:03 -08002452 #[cfg(feature = "plugin")]
Zach Reiznercc30d582018-01-23 21:16:42 -08002453 Argument::value("plugin", "PATH", "Absolute path to plugin process to run under crosvm."),
Daniel Verkampbd1a0842019-01-08 15:50:34 -08002454 #[cfg(feature = "plugin")]
Dmitry Torokhov0f1770d2018-05-11 10:57:16 -07002455 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 -08002456 #[cfg(feature = "plugin")]
Chirantan Ekboted41d7262018-11-16 16:37:45 -08002457 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 -08002458 #[cfg(feature = "plugin")]
Dmitry Torokhov0f4c5ff2019-12-11 13:36:08 -08002459 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."),
2460 #[cfg(feature = "plugin")]
Dmitry Torokhov1a6262b2019-03-01 00:34:03 -08002461 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 -08002462 #[cfg(feature = "plugin")]
2463 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 +00002464 Argument::flag("vhost-net", "Use vhost for networking."),
Alexandre Courbot993aa7f2021-12-09 14:51:29 +09002465 Argument::value("tap-name",
2466 "NAME",
2467 "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 -07002468 Argument::value("tap-fd",
2469 "fd",
Jorge E. Moreirab7952802019-02-12 16:43:05 -08002470 "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 -07002471 #[cfg(feature = "gpu")]
Jason Macnakcc7070b2019-11-06 14:48:12 -08002472 Argument::flag_or_value("gpu",
2473 "[width=INT,height=INT]",
2474 "(EXPERIMENTAL) Comma separated key=value pairs for setting up a virtio-gpu device
Junichi Uekawaaab74e42021-07-19 16:12:46 +09002475 Possible key values:
2476 backend=(2d|virglrenderer|gfxstream) - Which backend to use for virtio-gpu (determining rendering protocol)
2477 width=INT - The width of the virtual display connected to the virtio-gpu.
2478 height=INT - The height of the virtual display connected to the virtio-gpu.
2479 egl[=true|=false] - If the backend should use a EGL context for rendering.
2480 glx[=true|=false] - If the backend should use a GLX context for rendering.
2481 surfaceless[=true|=false] - If the backend should use a surfaceless context for rendering.
2482 angle[=true|=false] - If the gfxstream backend should use ANGLE (OpenGL on Vulkan) as its native OpenGL driver.
2483 syncfd[=true|=false] - If the gfxstream backend should support EGL_ANDROID_native_fence_sync
Chia-I Wu7f0f7c12022-01-12 10:42:18 -08002484 vulkan[=true|=false] - If the backend should support vulkan
2485 cache-path=PATH - The path to the virtio-gpu device shader cache.
2486 cache-size=SIZE - The maximum size of the shader cache."),
Jason Macnakd659a0d2021-03-15 15:33:01 -07002487 #[cfg(feature = "gpu")]
2488 Argument::flag_or_value("gpu-display",
2489 "[width=INT,height=INT]",
2490 "(EXPERIMENTAL) Comma separated key=value pairs for setting up a display on the virtio-gpu device
Junichi Uekawaaab74e42021-07-19 16:12:46 +09002491 Possible key values:
2492 width=INT - The width of the virtual display connected to the virtio-gpu.
2493 height=INT - The height of the virtual display connected to the virtio-gpu."),
Chia-I Wu16fb6592021-11-10 11:45:32 -08002494 #[cfg(all(feature = "gpu", feature = "virgl_renderer_next"))]
2495 Argument::flag_or_value("gpu-render-server",
2496 "[path=PATH]",
2497 "(EXPERIMENTAL) Comma separated key=value pairs for setting up a render server for the virtio-gpu device
2498 Possible key values:
Chia-I Wu7f0f7c12022-01-12 10:42:18 -08002499 path=PATH - The path to the render server executable.
2500 cache-path=PATH - The path to the render server shader cache.
2501 cache-size=SIZE - The maximum size of the shader cache."),
David Tolnay43f8e212019-02-13 17:28:16 -08002502 #[cfg(feature = "tpm")]
2503 Argument::flag("software-tpm", "enable a software emulated trusted platform module device"),
Jorge E. Moreiradffec502019-01-14 18:44:49 -08002504 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 -08002505 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 +00002506 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 -08002507 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)."),
2508 Argument::value("mouse", "PATH", "Path to a socket from where to read mouse input events and write status updates to."),
2509 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 -08002510 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 -08002511 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
2512 Argument::flag("split-irqchip", "(EXPERIMENTAL) enable split-irqchip support"),
Cody Schuffelen6d1ab502019-05-21 12:12:38 -07002513 Argument::value("bios", "PATH", "Path to BIOS/firmware ROM"),
Tomasz Nowicki344eb142021-09-22 05:51:58 +00002514 Argument::value("vfio", "PATH[,iommu=on|off]", "Path to sysfs of PCI pass through or mdev device.
Zide Chendfc4b882021-03-10 16:35:37 -08002515iommu=on|off - indicates whether to enable virtio IOMMU for this device"),
Tomasz Nowicki344eb142021-09-22 05:51:58 +00002516 Argument::value("vfio-platform", "PATH", "Path to sysfs of platform pass through"),
Keiichi Watanabe57df6a02019-12-06 22:24:40 +09002517 #[cfg(feature = "video-decoder")]
Alexandre Courbotb42b3e52021-07-09 23:38:57 +09002518 Argument::flag_or_value("video-decoder", "[backend]", "(EXPERIMENTAL) enable virtio-video decoder device
2519 Possible backend values: libvda"),
Keiichi Watanabe57df6a02019-12-06 22:24:40 +09002520 #[cfg(feature = "video-encoder")]
Alexandre Courbotb42b3e52021-07-09 23:38:57 +09002521 Argument::flag_or_value("video-encoder", "[backend]", "(EXPERIMENTAL) enable virtio-video encoder device
2522 Possible backend values: libvda"),
Tomasz Jeznach42644642020-05-20 23:27:59 -07002523 Argument::value("acpi-table", "PATH", "Path to user provided ACPI table"),
Will Deacon6560c182020-10-06 18:47:18 +01002524 Argument::flag("protected-vm", "(EXPERIMENTAL) prevent host access to guest memory"),
Andrew Walbran0bbbb682021-12-13 13:42:07 +00002525 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 +01002526 #[cfg(target_arch = "aarch64")]
Andrew Walbran0bbbb682021-12-13 13:42:07 +00002527 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 +08002528 Argument::flag_or_value("battery",
2529 "[type=TYPE]",
2530 "Comma separated key=value pairs for setting up battery device
Junichi Uekawaaab74e42021-07-19 16:12:46 +09002531 Possible key values:
2532 type=goldfish - type of battery emulation, defaults to goldfish"),
Keiichi Watanabec5262e92020-10-21 15:57:33 +09002533 Argument::value("gdb", "PORT", "(EXPERIMENTAL) gdb on the given port"),
Charles William Dick0e3d4b62020-12-14 12:16:46 +09002534 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 +09002535 Argument::value("vhost-user-blk", "SOCKET_PATH", "Path to a socket for vhost-user block"),
Federico 'Morg' Pareschi70fc7de2021-04-08 15:43:13 +09002536 Argument::value("vhost-user-console", "SOCKET_PATH", "Path to a socket for vhost-user console"),
Chirantan Ekbote44292f52021-06-25 18:31:41 +09002537 Argument::value("vhost-user-gpu", "SOCKET_PATH", "Paths to a vhost-user socket for gpu"),
JaeMan Parkeb9cc532021-07-02 15:02:59 +09002538 Argument::value("vhost-user-mac80211-hwsim", "SOCKET_PATH", "Path to a socket for vhost-user mac80211_hwsim"),
Keiichi Watanabe60686582021-03-12 04:53:51 +09002539 Argument::value("vhost-user-net", "SOCKET_PATH", "Path to a socket for vhost-user net"),
Woody Chow1b16db12021-04-02 16:59:59 +09002540 #[cfg(feature = "audio")]
2541 Argument::value("vhost-user-snd", "SOCKET_PATH", "Path to a socket for vhost-user snd"),
Chirantan Ekbote84091e52021-09-10 18:43:17 +09002542 Argument::value("vhost-user-vsock", "SOCKET_PATH", "Path to a socket for vhost-user vsock"),
Chirantan Ekbote2ee9dcd2021-05-26 18:21:44 +09002543 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 +09002544 Argument::value("vhost-user-fs", "SOCKET_PATH:TAG",
2545 "Path to a socket path for vhost-user fs, and tag for the shared dir"),
Abhishek Bhardwaj103c1b72021-11-01 15:52:23 -07002546 Argument::value("vvu-proxy", "SOCKET_PATH", "Socket path for the Virtio Vhost User proxy device"),
Tomasz Jeznach3ce74762021-02-26 01:01:53 -08002547 #[cfg(feature = "direct")]
Junichi Uekawab6a6e942021-12-07 05:49:30 +09002548 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 -07002549 #[cfg(feature = "direct")]
Junichi Uekawab6a6e942021-12-07 05:49:30 +09002550 Argument::value("direct-mmio", "PATH@RANGE[,RANGE[,...]]", "Path and ranges for direct memory mapped I/O access. RANGE may be decimal or hex (starting with 0x)."),
Tomasz Jeznach7271f752021-03-04 01:44:06 -08002551 #[cfg(feature = "direct")]
2552 Argument::value("direct-level-irq", "irq", "Enable interrupt passthrough"),
2553 #[cfg(feature = "direct")]
2554 Argument::value("direct-edge-irq", "irq", "Enable interrupt passthrough"),
Tomasz Jeznachccb26942021-03-30 22:44:11 -07002555 Argument::value("dmi", "DIR", "Directory with smbios_entry_point/DMI files"),
Tomasz Jeznachd93c29f2021-04-12 11:00:24 -07002556 Argument::flag("no-legacy", "Don't use legacy KBD/RTC devices emulation"),
ZhaoLiu2aaf7ad2021-10-10 18:22:29 +08002557 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
2558 Argument::flag("host-cpu-topology", "Use mirror cpu topology of Host for Guest VM"),
Daniel Verkamp42b33602021-12-15 15:35:08 -08002559 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 +00002560 Optional further parameters
2561 vendor=NUM - PCI vendor ID
2562 device=NUM - PCI device ID
2563 class=NUM - PCI class (including class code, subclass, and programming interface)
Mattias Nisslerde2c6402021-10-21 12:05:29 +00002564 subsystem_vendor=NUM - PCI subsystem vendor ID
2565 subsystem_device=NUM - PCI subsystem device ID
2566 revision=NUM - revision"),
Chuanxiao Dongd4468612022-01-14 14:21:17 +08002567 Argument::flag_or_value("coiommu",
2568 "unpin_policy=POLICY,unpin_interval=NUM,unpin_limit=NUM",
2569 "Comma separated key=value pairs for setting up coiommu devices.
2570 Possible key values:
2571 unpin_policy=lru - LRU unpin policy.
2572 unpin_interval=NUM - Unpin interval time in seconds.
2573 unpin_limit=NUM - Unpin limit for each unpin cycle, in unit of page count. 0 is invalid."),
Zach Reiznerefe95782017-08-26 18:05:48 -07002574 Argument::short_flag('h', "help", "Print help message.")];
2575
2576 let mut cfg = Config::default();
Zach Reizner55a9e502018-10-03 10:22:32 -07002577 let match_res = set_arguments(args, &arguments[..], |name, value| {
2578 set_argument(&mut cfg, name, value)
David Tolnay2bac1e72018-12-12 14:33:42 -08002579 })
Kaiyi Libccb4eb2020-02-06 17:53:11 -08002580 .and_then(|_| validate_arguments(&mut cfg));
Zach Reiznerefe95782017-08-26 18:05:48 -07002581
2582 match match_res {
Zach Reizner8864cb02018-01-16 17:59:03 -08002583 #[cfg(feature = "plugin")]
Zach Reizner267f2c82019-07-31 17:07:27 -07002584 Ok(()) if executable_is_plugin(&cfg.executable_path) => {
2585 match crosvm::plugin::run_config(cfg) {
2586 Ok(_) => {
2587 info!("crosvm and plugin have exited normally");
Dmitry Torokhovf75699f2021-12-03 11:19:13 -08002588 Ok(CommandStatus::VmStop)
Zach Reizner267f2c82019-07-31 17:07:27 -07002589 }
2590 Err(e) => {
2591 error!("{}", e);
2592 Err(())
2593 }
Zach Reizner8864cb02018-01-16 17:59:03 -08002594 }
Zach Reizner267f2c82019-07-31 17:07:27 -07002595 }
Michael Hoylee47a5002020-10-15 16:24:13 -07002596 Ok(()) => match platform::run_config(cfg) {
Dmitry Torokhovf75699f2021-12-03 11:19:13 -08002597 Ok(platform::ExitState::Stop) => {
Zach Reizner55a9e502018-10-03 10:22:32 -07002598 info!("crosvm has exited normally");
Dmitry Torokhovf75699f2021-12-03 11:19:13 -08002599 Ok(CommandStatus::VmStop)
2600 }
2601 Ok(platform::ExitState::Reset) => {
2602 info!("crosvm has exited normally due to reset request");
2603 Ok(CommandStatus::VmReset)
Zach Reiznerefe95782017-08-26 18:05:48 -07002604 }
Zach Reizner55a9e502018-10-03 10:22:32 -07002605 Err(e) => {
Alexandre Courbota184e312021-12-08 13:18:27 +09002606 error!("crosvm has exited with error: {:#}", e);
Zach Reizner55a9e502018-10-03 10:22:32 -07002607 Err(())
2608 }
2609 },
Dylan Reidbfba9932018-02-05 15:51:59 -08002610 Err(argument::Error::PrintHelp) => {
2611 print_help("crosvm run", "KERNEL", &arguments[..]);
Dmitry Torokhovf75699f2021-12-03 11:19:13 -08002612 Ok(CommandStatus::Success)
Dylan Reidbfba9932018-02-05 15:51:59 -08002613 }
Zach Reizner8864cb02018-01-16 17:59:03 -08002614 Err(e) => {
Dmitry Torokhov470b1e72020-01-15 12:46:49 -08002615 error!("{}", e);
Dylan Reidbfba9932018-02-05 15:51:59 -08002616 Err(())
Zach Reizner8864cb02018-01-16 17:59:03 -08002617 }
Zach Reiznerefe95782017-08-26 18:05:48 -07002618 }
2619}
2620
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002621fn stop_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
Zach Reizner78986322019-02-21 20:43:21 -08002622 if args.len() == 0 {
2623 print_help("crosvm stop", "VM_SOCKET...", &[]);
2624 println!("Stops the crosvm instance listening on each `VM_SOCKET` given.");
Jianxun Zhang56497d22019-03-04 14:38:24 -08002625 return Err(());
Zach Reizner78986322019-02-21 20:43:21 -08002626 }
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002627 let socket_path = &args.next().unwrap();
2628 let socket_path = Path::new(&socket_path);
2629 vms_request(&VmRequest::Exit, socket_path)
Zach Reizner78986322019-02-21 20:43:21 -08002630}
2631
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002632fn suspend_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
Zach Reizner78986322019-02-21 20:43:21 -08002633 if args.len() == 0 {
2634 print_help("crosvm suspend", "VM_SOCKET...", &[]);
2635 println!("Suspends the crosvm instance listening on each `VM_SOCKET` given.");
Jianxun Zhang56497d22019-03-04 14:38:24 -08002636 return Err(());
Zach Reizner78986322019-02-21 20:43:21 -08002637 }
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002638 let socket_path = &args.next().unwrap();
2639 let socket_path = Path::new(&socket_path);
2640 vms_request(&VmRequest::Suspend, socket_path)
Zach Reizner78986322019-02-21 20:43:21 -08002641}
2642
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002643fn resume_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
Zach Reizner78986322019-02-21 20:43:21 -08002644 if args.len() == 0 {
2645 print_help("crosvm resume", "VM_SOCKET...", &[]);
2646 println!("Resumes the crosvm instance listening on each `VM_SOCKET` given.");
Jianxun Zhang56497d22019-03-04 14:38:24 -08002647 return Err(());
Zach Reizner78986322019-02-21 20:43:21 -08002648 }
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002649 let socket_path = &args.next().unwrap();
2650 let socket_path = Path::new(&socket_path);
2651 vms_request(&VmRequest::Resume, socket_path)
Zach Reizner78986322019-02-21 20:43:21 -08002652}
2653
2654fn balloon_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
2655 if args.len() < 2 {
2656 print_help("crosvm balloon", "SIZE VM_SOCKET...", &[]);
2657 println!("Set the ballon size of the crosvm instance to `SIZE` bytes.");
Jianxun Zhang56497d22019-03-04 14:38:24 -08002658 return Err(());
Zach Reizner78986322019-02-21 20:43:21 -08002659 }
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09002660 let num_bytes = match args.next().unwrap().parse::<u64>() {
Zach Reizner78986322019-02-21 20:43:21 -08002661 Ok(n) => n,
2662 Err(_) => {
2663 error!("Failed to parse number of bytes");
2664 return Err(());
2665 }
2666 };
2667
Jakub Staron1f828d72019-04-11 12:49:29 -07002668 let command = BalloonControlCommand::Adjust { num_bytes };
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002669 let socket_path = &args.next().unwrap();
2670 let socket_path = Path::new(&socket_path);
2671 vms_request(&VmRequest::BalloonCommand(command), socket_path)
Zach Reizner78986322019-02-21 20:43:21 -08002672}
2673
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002674fn balloon_stats(mut args: std::env::Args) -> std::result::Result<(), ()> {
Charles William Dicked22f6b2020-04-08 11:05:24 +09002675 if args.len() != 1 {
2676 print_help("crosvm balloon_stats", "VM_SOCKET", &[]);
2677 println!("Prints virtio balloon statistics for a `VM_SOCKET`.");
2678 return Err(());
2679 }
2680 let command = BalloonControlCommand::Stats {};
2681 let request = &VmRequest::BalloonCommand(command);
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01002682 let socket_path = &args.next().unwrap();
2683 let socket_path = Path::new(&socket_path);
2684 let response = handle_request(request, socket_path)?;
Hikaru Nishidaa0e381b2021-05-24 17:13:45 +09002685 match serde_json::to_string_pretty(&response) {
2686 Ok(response_json) => println!("{}", response_json),
2687 Err(e) => {
2688 error!("Failed to serialize into JSON: {}", e);
2689 return Err(());
2690 }
2691 }
Hikaru Nishida6b51c752021-05-21 12:37:43 +09002692 match response {
2693 VmResponse::BalloonStats { .. } => Ok(()),
2694 _ => Err(()),
2695 }
Charles William Dicked22f6b2020-04-08 11:05:24 +09002696}
2697
Keiichi Watanabe2400bd52021-08-04 19:18:02 +09002698fn modify_battery(mut args: std::env::Args) -> std::result::Result<(), ()> {
2699 if args.len() < 4 {
2700 print_help(
2701 "crosvm battery BATTERY_TYPE ",
2702 "[status STATUS | \
2703 present PRESENT | \
2704 health HEALTH | \
2705 capacity CAPACITY | \
2706 aconline ACONLINE ] \
2707 VM_SOCKET...",
2708 &[],
2709 );
2710 return Err(());
2711 }
2712
2713 // This unwrap will not panic because of the above length check.
2714 let battery_type = args.next().unwrap();
2715 let property = args.next().unwrap();
2716 let target = args.next().unwrap();
2717
2718 let socket_path = args.next().unwrap();
2719 let socket_path = Path::new(&socket_path);
2720
Daniel Verkamp166d1dd2021-08-19 17:05:29 -07002721 do_modify_battery(socket_path, &*battery_type, &*property, &*target)
Keiichi Watanabe2400bd52021-08-04 19:18:02 +09002722}
2723
Xiong Zhangc78e72b2021-04-08 11:31:41 +08002724fn modify_vfio(mut args: std::env::Args) -> std::result::Result<(), ()> {
2725 if args.len() < 3 {
2726 print_help(
2727 "crosvm vfio",
2728 "[add | remove host_vfio_sysfs] VM_SOCKET...",
2729 &[],
2730 );
2731 return Err(());
2732 }
2733
2734 // This unwrap will not panic because of the above length check.
2735 let command = args.next().unwrap();
2736 let path_str = args.next().unwrap();
2737 let vfio_path = PathBuf::from(&path_str);
2738 if !vfio_path.exists() || !vfio_path.is_dir() {
2739 error!("Invalid host sysfs path: {}", path_str);
2740 return Err(());
2741 }
2742
2743 let socket_path = args.next().unwrap();
2744 let socket_path = Path::new(&socket_path);
2745
2746 let add = match command.as_ref() {
2747 "add" => true,
2748 "remove" => false,
2749 other => {
2750 error!("Invalid vfio command {}", other);
2751 return Err(());
2752 }
2753 };
2754
2755 let request = VmRequest::VfioCommand { vfio_path, add };
2756 handle_request(&request, socket_path)?;
2757 Ok(())
2758}
2759
Andrew Walbranfb7c1082021-06-17 18:12:14 +01002760#[cfg(feature = "composite-disk")]
2761fn create_composite(mut args: std::env::Args) -> std::result::Result<(), ()> {
2762 if args.len() < 1 {
2763 print_help("crosvm create_composite", "PATH [LABEL:PARTITION]..", &[]);
2764 println!("Creates a new composite disk image containing the given partition images");
2765 return Err(());
2766 }
2767
2768 let composite_image_path = args.next().unwrap();
Jooyung Han2e14c732021-07-29 13:27:54 +09002769 let zero_filler_path = format!("{}.filler", composite_image_path);
Andrew Walbranfb7c1082021-06-17 18:12:14 +01002770 let header_path = format!("{}.header", composite_image_path);
2771 let footer_path = format!("{}.footer", composite_image_path);
2772
2773 let mut composite_image_file = OpenOptions::new()
2774 .create(true)
2775 .read(true)
2776 .write(true)
2777 .truncate(true)
2778 .open(&composite_image_path)
2779 .map_err(|e| {
2780 error!(
2781 "Failed opening composite disk image file at '{}': {}",
2782 composite_image_path, e
2783 );
2784 })?;
Jooyung Han2e14c732021-07-29 13:27:54 +09002785 create_zero_filler(&zero_filler_path).map_err(|e| {
2786 error!(
2787 "Failed to create zero filler file at '{}': {}",
2788 &zero_filler_path, e
2789 );
2790 })?;
Andrew Walbranfb7c1082021-06-17 18:12:14 +01002791 let mut header_file = OpenOptions::new()
2792 .create(true)
2793 .read(true)
2794 .write(true)
2795 .truncate(true)
2796 .open(&header_path)
2797 .map_err(|e| {
2798 error!(
2799 "Failed opening header image file at '{}': {}",
2800 header_path, e
2801 );
2802 })?;
2803 let mut footer_file = OpenOptions::new()
2804 .create(true)
2805 .read(true)
2806 .write(true)
2807 .truncate(true)
2808 .open(&footer_path)
2809 .map_err(|e| {
2810 error!(
2811 "Failed opening footer image file at '{}': {}",
2812 footer_path, e
2813 );
2814 })?;
2815
2816 let partitions = args
2817 .into_iter()
2818 .map(|partition_arg| {
2819 if let [label, path] = partition_arg.split(":").collect::<Vec<_>>()[..] {
2820 let partition_file = File::open(path)
2821 .map_err(|e| error!("Failed to open partition image: {}", e))?;
Daniel Verkampeb1640e2021-09-07 14:09:31 -07002822 let size = create_disk_file(partition_file, disk::MAX_NESTING_DEPTH)
Andrew Walbranfb7c1082021-06-17 18:12:14 +01002823 .map_err(|e| error!("Failed to create DiskFile instance: {}", e))?
2824 .get_len()
2825 .map_err(|e| error!("Failed to get length of partition image: {}", e))?;
2826 Ok(PartitionInfo {
2827 label: label.to_owned(),
Jooyung Hand7e56ba2021-07-29 13:26:48 +09002828 path: Path::new(path).to_owned(),
Andrew Walbranfb7c1082021-06-17 18:12:14 +01002829 partition_type: ImagePartitionType::LinuxFilesystem,
2830 writable: false,
Jooyung Hand7e56ba2021-07-29 13:26:48 +09002831 size,
Andrew Walbranfb7c1082021-06-17 18:12:14 +01002832 })
2833 } else {
2834 error!(
2835 "Must specify label and path for partition '{}', like LABEL:PATH",
2836 partition_arg
2837 );
2838 Err(())
2839 }
2840 })
2841 .collect::<Result<Vec<_>, _>>()?;
2842
2843 create_composite_disk(
2844 &partitions,
Jooyung Han2e14c732021-07-29 13:27:54 +09002845 &PathBuf::from(zero_filler_path),
Andrew Walbranfb7c1082021-06-17 18:12:14 +01002846 &PathBuf::from(header_path),
2847 &mut header_file,
2848 &PathBuf::from(footer_path),
2849 &mut footer_file,
2850 &mut composite_image_file,
2851 )
2852 .map_err(|e| {
2853 error!(
2854 "Failed to create composite disk image at '{}': {}",
2855 composite_image_path, e
2856 );
2857 })?;
2858
2859 Ok(())
2860}
2861
A. Cody Schuffelen9ca60392019-12-23 18:27:11 -08002862fn create_qcow2(args: std::env::Args) -> std::result::Result<(), ()> {
2863 let arguments = [
2864 Argument::positional("PATH", "where to create the qcow2 image"),
2865 Argument::positional("[SIZE]", "the expanded size of the image"),
2866 Argument::value(
2867 "backing_file",
2868 "path/to/file",
2869 " the file to back the image",
2870 ),
2871 ];
2872 let mut positional_index = 0;
2873 let mut file_path = String::from("");
2874 let mut size: Option<u64> = None;
2875 let mut backing_file: Option<String> = None;
2876 set_arguments(args, &arguments[..], |name, value| {
2877 match (name, positional_index) {
2878 ("", 0) => {
2879 // NAME
2880 positional_index += 1;
2881 file_path = value.unwrap().to_owned();
2882 }
2883 ("", 1) => {
2884 // [SIZE]
2885 positional_index += 1;
2886 size = Some(value.unwrap().parse::<u64>().map_err(|_| {
2887 argument::Error::InvalidValue {
2888 value: value.unwrap().to_owned(),
Judy Hsiao59343052020-03-16 15:58:03 +08002889 expected: String::from("SIZE should be a nonnegative integer"),
A. Cody Schuffelen9ca60392019-12-23 18:27:11 -08002890 }
2891 })?);
2892 }
2893 ("", _) => {
2894 return Err(argument::Error::TooManyArguments(
2895 "Expected at most 2 positional arguments".to_owned(),
2896 ));
2897 }
2898 ("backing_file", _) => {
2899 backing_file = value.map(|x| x.to_owned());
2900 }
2901 _ => unreachable!(),
2902 };
2903 Ok(())
2904 })
2905 .map_err(|e| {
2906 error!("Unable to parse command line arguments: {}", e);
2907 })?;
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09002908 if file_path.is_empty() || !(size.is_some() ^ backing_file.is_some()) {
A. Cody Schuffelen9ca60392019-12-23 18:27:11 -08002909 print_help("crosvm create_qcow2", "PATH [SIZE]", &arguments);
2910 println!(
2911 "Create a new QCOW2 image at `PATH` of either the specified `SIZE` in bytes or
2912with a '--backing_file'."
2913 );
Jianxun Zhang56497d22019-03-04 14:38:24 -08002914 return Err(());
Dylan Reid2dcb6322018-07-13 10:42:48 -07002915 }
Dylan Reid2dcb6322018-07-13 10:42:48 -07002916
2917 let file = OpenOptions::new()
2918 .create(true)
2919 .read(true)
2920 .write(true)
A. Cody Schuffelen9ca60392019-12-23 18:27:11 -08002921 .truncate(true)
Dylan Reid2dcb6322018-07-13 10:42:48 -07002922 .open(&file_path)
2923 .map_err(|e| {
David Tolnayb4bd00f2019-02-12 17:51:26 -08002924 error!("Failed opening qcow file at '{}': {}", file_path, e);
Dylan Reid2dcb6322018-07-13 10:42:48 -07002925 })?;
2926
A. Cody Schuffelen9ca60392019-12-23 18:27:11 -08002927 match (size, backing_file) {
2928 (Some(size), None) => QcowFile::new(file, size).map_err(|e| {
2929 error!("Failed to create qcow file at '{}': {}", file_path, e);
2930 })?,
2931 (None, Some(backing_file)) => {
Daniel Verkampeb1640e2021-09-07 14:09:31 -07002932 QcowFile::new_from_backing(file, &backing_file, disk::MAX_NESTING_DEPTH).map_err(
2933 |e| {
2934 error!("Failed to create qcow file at '{}': {}", file_path, e);
2935 },
2936 )?
A. Cody Schuffelen9ca60392019-12-23 18:27:11 -08002937 }
2938 _ => unreachable!(),
2939 };
Dylan Reid2dcb6322018-07-13 10:42:48 -07002940 Ok(())
2941}
2942
Keiichi Watanabeee4b58e2021-08-17 19:34:01 +09002943fn start_device(mut args: std::env::Args) -> std::result::Result<(), ()> {
2944 let print_usage = || {
2945 print_help(
2946 "crosvm device",
Woody Chowf364d022021-09-17 13:37:32 +09002947 " (block|console|cras-snd|fs|gpu|net|wl) <device-specific arguments>",
Keiichi Watanabeee4b58e2021-08-17 19:34:01 +09002948 &[],
2949 );
2950 };
2951
Keiichi Watanabeee4b58e2021-08-17 19:34:01 +09002952 if args.len() == 0 {
2953 print_usage();
2954 return Err(());
2955 }
2956
2957 let device = args.next().unwrap();
2958
2959 let program_name = format!("crosvm device {}", device);
Anton Romanovd48d4602021-12-13 23:41:47 +00002960
2961 let args = args.collect::<Vec<_>>();
2962 let args = args.iter().map(Deref::deref).collect::<Vec<_>>();
2963 let args = args.as_slice();
2964
Keiichi Watanabeee4b58e2021-08-17 19:34:01 +09002965 let result = match device.as_str() {
2966 "block" => run_block_device(&program_name, args),
Federico 'Morg' Pareschia1184822021-09-09 10:52:58 +09002967 "console" => run_console_device(&program_name, args),
Woody Chowf364d022021-09-17 13:37:32 +09002968 #[cfg(feature = "audio_cras")]
2969 "cras-snd" => run_cras_snd_device(&program_name, args),
Federico 'Morg' Pareschia1184822021-09-09 10:52:58 +09002970 "fs" => run_fs_device(&program_name, args),
Chirantan Ekbote78225292021-06-25 18:30:34 +09002971 #[cfg(feature = "gpu")]
2972 "gpu" => run_gpu_device(&program_name, args),
Keiichi Watanabeee4b58e2021-08-17 19:34:01 +09002973 "net" => run_net_device(&program_name, args),
Chirantan Ekbotef08bddd2021-09-10 18:41:06 +09002974 "vsock" => run_vsock_device(&program_name, args),
Keiichi Watanabeee4b58e2021-08-17 19:34:01 +09002975 "wl" => run_wl_device(&program_name, args),
2976 _ => {
2977 println!("Unknown device name: {}", device);
2978 print_usage();
2979 return Err(());
2980 }
2981 };
2982
2983 result.map_err(|e| {
Alexandre Courbot3abfaa52021-12-10 16:29:34 +09002984 error!("Failed to run {} device: {:#}", device, e);
Keiichi Watanabeee4b58e2021-08-17 19:34:01 +09002985 })
2986}
2987
Daniel Verkamp92f73d72018-12-04 13:17:46 -08002988fn disk_cmd(mut args: std::env::Args) -> std::result::Result<(), ()> {
2989 if args.len() < 2 {
2990 print_help("crosvm disk", "SUBCOMMAND VM_SOCKET...", &[]);
2991 println!("Manage attached virtual disk devices.");
2992 println!("Subcommands:");
2993 println!(" resize DISK_INDEX NEW_SIZE VM_SOCKET");
Jianxun Zhang56497d22019-03-04 14:38:24 -08002994 return Err(());
Daniel Verkamp92f73d72018-12-04 13:17:46 -08002995 }
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09002996 let subcommand: &str = &args.next().unwrap();
Daniel Verkamp92f73d72018-12-04 13:17:46 -08002997
2998 let request = match subcommand {
2999 "resize" => {
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09003000 let disk_index = match args.next().unwrap().parse::<usize>() {
Daniel Verkamp92f73d72018-12-04 13:17:46 -08003001 Ok(n) => n,
3002 Err(_) => {
3003 error!("Failed to parse disk index");
3004 return Err(());
3005 }
3006 };
3007
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09003008 let new_size = match args.next().unwrap().parse::<u64>() {
Daniel Verkamp92f73d72018-12-04 13:17:46 -08003009 Ok(n) => n,
3010 Err(_) => {
3011 error!("Failed to parse disk size");
3012 return Err(());
3013 }
3014 };
3015
Jakub Staronecf81e02019-04-11 11:43:39 -07003016 VmRequest::DiskCommand {
Daniel Verkamp92f73d72018-12-04 13:17:46 -08003017 disk_index,
Jakub Staronecf81e02019-04-11 11:43:39 -07003018 command: DiskControlCommand::Resize { new_size },
Daniel Verkamp92f73d72018-12-04 13:17:46 -08003019 }
3020 }
3021 _ => {
3022 error!("Unknown disk subcommand '{}'", subcommand);
3023 return Err(());
3024 }
3025 };
3026
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01003027 let socket_path = &args.next().unwrap();
3028 let socket_path = Path::new(&socket_path);
3029 vms_request(&request, socket_path)
Daniel Verkamp92f73d72018-12-04 13:17:46 -08003030}
3031
Keiichi Watanabe2400bd52021-08-04 19:18:02 +09003032fn make_rt(mut args: std::env::Args) -> std::result::Result<(), ()> {
3033 if args.len() == 0 {
3034 print_help("crosvm make_rt", "VM_SOCKET...", &[]);
3035 println!("Makes the crosvm instance listening on each `VM_SOCKET` given RT.");
3036 return Err(());
3037 }
3038 let socket_path = &args.next().unwrap();
3039 let socket_path = Path::new(&socket_path);
3040 vms_request(&VmRequest::MakeRT, socket_path)
3041}
3042
Jingkui Wang100e6e42019-03-08 20:41:57 -08003043fn parse_bus_id_addr(v: &str) -> ModifyUsbResult<(u8, u8, u16, u16)> {
3044 debug!("parse_bus_id_addr: {}", v);
Keiichi Watanabe275c1ef2020-04-10 21:49:10 +09003045 let mut ids = v.split(':');
Jingkui Wang100e6e42019-03-08 20:41:57 -08003046 match (ids.next(), ids.next(), ids.next(), ids.next()) {
3047 (Some(bus_id), Some(addr), Some(vid), Some(pid)) => {
3048 let bus_id = bus_id
3049 .parse::<u8>()
3050 .map_err(|e| ModifyUsbError::ArgParseInt("bus_id", bus_id.to_owned(), e))?;
3051 let addr = addr
3052 .parse::<u8>()
3053 .map_err(|e| ModifyUsbError::ArgParseInt("addr", addr.to_owned(), e))?;
Daniel Verkamp166d1dd2021-08-19 17:05:29 -07003054 let vid = u16::from_str_radix(vid, 16)
Jingkui Wang100e6e42019-03-08 20:41:57 -08003055 .map_err(|e| ModifyUsbError::ArgParseInt("vid", vid.to_owned(), e))?;
Daniel Verkamp166d1dd2021-08-19 17:05:29 -07003056 let pid = u16::from_str_radix(pid, 16)
Jingkui Wang100e6e42019-03-08 20:41:57 -08003057 .map_err(|e| ModifyUsbError::ArgParseInt("pid", pid.to_owned(), e))?;
3058 Ok((bus_id, addr, vid, pid))
3059 }
3060 _ => Err(ModifyUsbError::ArgParse(
3061 "BUS_ID_ADDR_BUS_NUM_DEV_NUM",
3062 v.to_owned(),
3063 )),
3064 }
3065}
3066
Jingkui Wang100e6e42019-03-08 20:41:57 -08003067fn usb_attach(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
3068 let val = args
3069 .next()
3070 .ok_or(ModifyUsbError::ArgMissing("BUS_ID_ADDR_BUS_NUM_DEV_NUM"))?;
3071 let (bus, addr, vid, pid) = parse_bus_id_addr(&val)?;
3072 let dev_path = PathBuf::from(
3073 args.next()
3074 .ok_or(ModifyUsbError::ArgMissing("usb device path"))?,
3075 );
Jingkui Wang100e6e42019-03-08 20:41:57 -08003076
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01003077 let socket_path = args
3078 .next()
3079 .ok_or(ModifyUsbError::ArgMissing("control socket path"))?;
3080 let socket_path = Path::new(&socket_path);
3081
Daniel Verkamp166d1dd2021-08-19 17:05:29 -07003082 do_usb_attach(socket_path, bus, addr, vid, pid, &dev_path)
Jingkui Wang100e6e42019-03-08 20:41:57 -08003083}
3084
3085fn usb_detach(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
3086 let port: u8 = args
3087 .next()
3088 .map_or(Err(ModifyUsbError::ArgMissing("PORT")), |p| {
3089 p.parse::<u8>()
3090 .map_err(|e| ModifyUsbError::ArgParseInt("PORT", p.to_owned(), e))
3091 })?;
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01003092 let socket_path = args
3093 .next()
3094 .ok_or(ModifyUsbError::ArgMissing("control socket path"))?;
3095 let socket_path = Path::new(&socket_path);
Daniel Verkamp166d1dd2021-08-19 17:05:29 -07003096 do_usb_detach(socket_path, port)
Jingkui Wang100e6e42019-03-08 20:41:57 -08003097}
3098
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01003099fn usb_list(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
3100 let socket_path = args
3101 .next()
3102 .ok_or(ModifyUsbError::ArgMissing("control socket path"))?;
3103 let socket_path = Path::new(&socket_path);
Daniel Verkamp166d1dd2021-08-19 17:05:29 -07003104 do_usb_list(socket_path)
Jingkui Wang100e6e42019-03-08 20:41:57 -08003105}
3106
3107fn modify_usb(mut args: std::env::Args) -> std::result::Result<(), ()> {
Zach Reizneraff94ca2019-03-18 20:58:31 -07003108 if args.len() < 2 {
Jingkui Wang100e6e42019-03-08 20:41:57 -08003109 print_help("crosvm usb",
Zach Reizneraff94ca2019-03-18 20:58:31 -07003110 "[attach BUS_ID:ADDR:VENDOR_ID:PRODUCT_ID [USB_DEVICE_PATH|-] | detach PORT | list] VM_SOCKET...", &[]);
Jingkui Wang100e6e42019-03-08 20:41:57 -08003111 return Err(());
3112 }
3113
3114 // This unwrap will not panic because of the above length check.
Kevin Hamacher6fc5f202021-03-18 12:41:23 +01003115 let command = &args.next().unwrap();
Jingkui Wang100e6e42019-03-08 20:41:57 -08003116 let result = match command.as_ref() {
3117 "attach" => usb_attach(args),
3118 "detach" => usb_detach(args),
3119 "list" => usb_list(args),
3120 other => Err(ModifyUsbError::UnknownCommand(other.to_owned())),
3121 };
3122 match result {
3123 Ok(response) => {
3124 println!("{}", response);
3125 Ok(())
3126 }
3127 Err(e) => {
3128 println!("error {}", e);
3129 Err(())
3130 }
3131 }
3132}
3133
Keiichi Watanabe2400bd52021-08-04 19:18:02 +09003134#[allow(clippy::unnecessary_wraps)]
3135fn pkg_version() -> std::result::Result<(), ()> {
3136 const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION");
3137 const PKG_VERSION: Option<&'static str> = option_env!("PKG_VERSION");
3138
3139 print!("crosvm {}", VERSION.unwrap_or("UNKNOWN"));
3140 match PKG_VERSION {
3141 Some(v) => println!("-{}", v),
3142 None => println!(),
3143 }
3144 Ok(())
3145}
3146
Zach Reiznerefe95782017-08-26 18:05:48 -07003147fn print_usage() {
Dmitry Torokhovdacf1042021-12-09 15:36:01 -08003148 print_help("crosvm", "[--extended-status] [command]", &[]);
Zach Reiznerefe95782017-08-26 18:05:48 -07003149 println!("Commands:");
Junichi Uekawaf0a77232021-04-08 16:44:44 +09003150 println!(" balloon - Set balloon size of the crosvm instance.");
3151 println!(" balloon_stats - Prints virtio balloon statistics.");
3152 println!(" battery - Modify battery.");
Andrew Walbranfb7c1082021-06-17 18:12:14 +01003153 #[cfg(feature = "composite-disk")]
3154 println!(" create_composite - Create a new composite disk image file.");
Dylan Reid2dcb6322018-07-13 10:42:48 -07003155 println!(" create_qcow2 - Create a new qcow2 disk image file.");
Keiichi Watanabeee4b58e2021-08-17 19:34:01 +09003156 println!(" device - Start a device process.");
Jingkui Wang100e6e42019-03-08 20:41:57 -08003157 println!(" disk - Manage attached virtual disk devices.");
Keiichi Watanabe9568bb42021-08-16 17:59:30 +09003158 println!(
3159 " make_rt - Enables real-time vcpu priority for crosvm instances started with \
3160 `--delay-rt`."
3161 );
Junichi Uekawaf0a77232021-04-08 16:44:44 +09003162 println!(" resume - Resumes the crosvm instance.");
3163 println!(" run - Start a new crosvm instance.");
3164 println!(" stop - Stops crosvm instances via their control sockets.");
3165 println!(" suspend - Suspends the crosvm instance.");
Jingkui Wang100e6e42019-03-08 20:41:57 -08003166 println!(" usb - Manage attached virtual USB devices.");
Yi Sun54305cd2020-01-04 00:19:37 +08003167 println!(" version - Show package version.");
Xiong Zhangc78e72b2021-04-08 11:31:41 +08003168 println!(" vfio - add/remove host vfio pci device into guest.");
Yi Sun54305cd2020-01-04 00:19:37 +08003169}
3170
Dmitry Torokhovf75699f2021-12-03 11:19:13 -08003171fn crosvm_main() -> std::result::Result<CommandStatus, ()> {
Stephen Barber56fbf092017-06-29 16:12:14 -07003172 if let Err(e) = syslog::init() {
David Tolnayb4bd00f2019-02-12 17:51:26 -08003173 println!("failed to initialize syslog: {}", e);
Dylan Reidbfba9932018-02-05 15:51:59 -08003174 return Err(());
Stephen Barber56fbf092017-06-29 16:12:14 -07003175 }
Zach Reizner639d9672017-05-01 17:57:18 -07003176
Zach Reiznerb3fa5c92019-01-28 14:05:23 -08003177 panic_hook::set_panic_hook();
3178
Zach Reiznerefe95782017-08-26 18:05:48 -07003179 let mut args = std::env::args();
3180 if args.next().is_none() {
3181 error!("expected executable name");
Dylan Reidbfba9932018-02-05 15:51:59 -08003182 return Err(());
Zach Reizner639d9672017-05-01 17:57:18 -07003183 }
Zach Reiznerefe95782017-08-26 18:05:48 -07003184
Dmitry Torokhovdacf1042021-12-09 15:36:01 -08003185 let mut cmd_arg = args.next();
3186 let extended_status = match cmd_arg.as_ref().map(|s| s.as_ref()) {
3187 Some("--extended-status") => {
3188 cmd_arg = args.next();
3189 true
3190 }
3191 _ => false,
Dmitry Torokhovdd8a12c2021-12-09 15:36:01 -08003192 };
Dmitry Torokhovdacf1042021-12-09 15:36:01 -08003193
3194 let command = match cmd_arg {
Dmitry Torokhovf75699f2021-12-03 11:19:13 -08003195 Some(c) => c,
Dylan Reidbfba9932018-02-05 15:51:59 -08003196 None => {
3197 print_usage();
Dmitry Torokhovf75699f2021-12-03 11:19:13 -08003198 return Ok(CommandStatus::Success);
Dylan Reidbfba9932018-02-05 15:51:59 -08003199 }
Dmitry Torokhovf75699f2021-12-03 11:19:13 -08003200 };
3201
3202 // Past this point, usage of exit is in danger of leaking zombie processes.
3203 let ret = if command == "run" {
Dmitry Torokhovdacf1042021-12-09 15:36:01 -08003204 // We handle run_vm separately because it does not simply signal success/error
Dmitry Torokhovf75699f2021-12-03 11:19:13 -08003205 // but also indicates whether the guest requested reset or stop.
3206 run_vm(args)
3207 } else {
3208 match &command[..] {
3209 "balloon" => balloon_vms(args),
3210 "balloon_stats" => balloon_stats(args),
3211 "battery" => modify_battery(args),
3212 #[cfg(feature = "composite-disk")]
3213 "create_composite" => create_composite(args),
3214 "create_qcow2" => create_qcow2(args),
3215 "device" => start_device(args),
3216 "disk" => disk_cmd(args),
3217 "make_rt" => make_rt(args),
3218 "resume" => resume_vms(args),
3219 "stop" => stop_vms(args),
3220 "suspend" => suspend_vms(args),
3221 "usb" => modify_usb(args),
3222 "version" => pkg_version(),
Xiong Zhangc78e72b2021-04-08 11:31:41 +08003223 "vfio" => modify_vfio(args),
Dmitry Torokhovf75699f2021-12-03 11:19:13 -08003224 c => {
3225 println!("invalid subcommand: {:?}", c);
3226 print_usage();
3227 Err(())
3228 }
Zach Reiznerefe95782017-08-26 18:05:48 -07003229 }
Dmitry Torokhovf75699f2021-12-03 11:19:13 -08003230 .map(|_| CommandStatus::Success)
Dylan Reidbfba9932018-02-05 15:51:59 -08003231 };
Zach Reiznerefe95782017-08-26 18:05:48 -07003232
3233 // Reap exit status from any child device processes. At this point, all devices should have been
3234 // dropped in the main process and told to shutdown. Try over a period of 100ms, since it may
3235 // take some time for the processes to shut down.
3236 if !wait_all_children() {
3237 // We gave them a chance, and it's too late.
3238 warn!("not all child processes have exited; sending SIGKILL");
3239 if let Err(e) = kill_process_group() {
3240 // We're now at the mercy of the OS to clean up after us.
David Tolnayb4bd00f2019-02-12 17:51:26 -08003241 warn!("unable to kill all child processes: {}", e);
Zach Reiznerefe95782017-08-26 18:05:48 -07003242 }
3243 }
3244
3245 // WARNING: Any code added after this point is not guaranteed to run
3246 // since we may forcibly kill this process (and its children) above.
Dmitry Torokhovdacf1042021-12-09 15:36:01 -08003247 ret.map(|s| {
3248 if extended_status {
3249 s
3250 } else {
3251 CommandStatus::Success
3252 }
3253 })
Dylan Reidbfba9932018-02-05 15:51:59 -08003254}
3255
3256fn main() {
Dmitry Torokhovf75699f2021-12-03 11:19:13 -08003257 let exit_code = match crosvm_main() {
3258 Ok(CommandStatus::Success | CommandStatus::VmStop) => 0,
3259 Ok(CommandStatus::VmReset) => 32,
3260 Err(_) => 1,
3261 };
3262 std::process::exit(exit_code);
Zach Reizner639d9672017-05-01 17:57:18 -07003263}
Daniel Verkamp107edb32019-04-05 09:58:48 -07003264
3265#[cfg(test)]
3266mod tests {
3267 use super::*;
Kaiyi Libccb4eb2020-02-06 17:53:11 -08003268 use crosvm::{DEFAULT_TOUCH_DEVICE_HEIGHT, DEFAULT_TOUCH_DEVICE_WIDTH};
Daniel Verkamp107edb32019-04-05 09:58:48 -07003269
3270 #[test]
3271 fn parse_cpu_set_single() {
3272 assert_eq!(parse_cpu_set("123").expect("parse failed"), vec![123]);
3273 }
3274
3275 #[test]
3276 fn parse_cpu_set_list() {
3277 assert_eq!(
3278 parse_cpu_set("0,1,2,3").expect("parse failed"),
3279 vec![0, 1, 2, 3]
3280 );
3281 }
3282
3283 #[test]
3284 fn parse_cpu_set_range() {
3285 assert_eq!(
3286 parse_cpu_set("0-3").expect("parse failed"),
3287 vec![0, 1, 2, 3]
3288 );
3289 }
3290
3291 #[test]
3292 fn parse_cpu_set_list_of_ranges() {
3293 assert_eq!(
3294 parse_cpu_set("3-4,7-9,18").expect("parse failed"),
3295 vec![3, 4, 7, 8, 9, 18]
3296 );
3297 }
3298
3299 #[test]
3300 fn parse_cpu_set_repeated() {
3301 // For now, allow duplicates - they will be handled gracefully by the vec to cpu_set_t conversion.
3302 assert_eq!(parse_cpu_set("1,1,1").expect("parse failed"), vec![1, 1, 1]);
3303 }
3304
3305 #[test]
3306 fn parse_cpu_set_negative() {
3307 // Negative CPU numbers are not allowed.
3308 parse_cpu_set("-3").expect_err("parse should have failed");
3309 }
3310
3311 #[test]
3312 fn parse_cpu_set_reverse_range() {
3313 // Ranges must be from low to high.
3314 parse_cpu_set("5-2").expect_err("parse should have failed");
3315 }
3316
3317 #[test]
3318 fn parse_cpu_set_open_range() {
3319 parse_cpu_set("3-").expect_err("parse should have failed");
3320 }
3321
3322 #[test]
3323 fn parse_cpu_set_extra_comma() {
3324 parse_cpu_set("0,1,2,").expect_err("parse should have failed");
3325 }
Trent Begin17ccaad2019-04-17 13:51:25 -06003326
Daniel Verkampc677fb42020-09-08 13:47:49 -07003327 #[test]
3328 fn parse_cpu_affinity_global() {
3329 assert_eq!(
3330 parse_cpu_affinity("0,5-7,9").expect("parse failed"),
3331 VcpuAffinity::Global(vec![0, 5, 6, 7, 9]),
3332 );
3333 }
3334
3335 #[test]
3336 fn parse_cpu_affinity_per_vcpu_one_to_one() {
3337 let mut expected_map = BTreeMap::new();
3338 expected_map.insert(0, vec![0]);
3339 expected_map.insert(1, vec![1]);
3340 expected_map.insert(2, vec![2]);
3341 expected_map.insert(3, vec![3]);
3342 assert_eq!(
3343 parse_cpu_affinity("0=0:1=1:2=2:3=3").expect("parse failed"),
3344 VcpuAffinity::PerVcpu(expected_map),
3345 );
3346 }
3347
3348 #[test]
3349 fn parse_cpu_affinity_per_vcpu_sets() {
3350 let mut expected_map = BTreeMap::new();
3351 expected_map.insert(0, vec![0, 1, 2]);
3352 expected_map.insert(1, vec![3, 4, 5]);
3353 expected_map.insert(2, vec![6, 7, 8]);
3354 assert_eq!(
3355 parse_cpu_affinity("0=0,1,2:1=3-5:2=6,7-8").expect("parse failed"),
3356 VcpuAffinity::PerVcpu(expected_map),
3357 );
3358 }
3359
Dennis Kempin50a58f92021-06-23 11:34:31 -07003360 #[cfg(feature = "audio_cras")]
Trent Begin17ccaad2019-04-17 13:51:25 -06003361 #[test]
Judy Hsiaod5c1e962020-02-04 12:30:01 +08003362 fn parse_ac97_vaild() {
3363 parse_ac97_options("backend=cras").expect("parse should have succeded");
3364 }
3365
Andrew Scull1590e6f2020-03-18 18:00:47 +00003366 #[cfg(feature = "audio")]
Judy Hsiaod5c1e962020-02-04 12:30:01 +08003367 #[test]
3368 fn parse_ac97_null_vaild() {
3369 parse_ac97_options("backend=null").expect("parse should have succeded");
3370 }
3371
Dennis Kempin50a58f92021-06-23 11:34:31 -07003372 #[cfg(feature = "audio_cras")]
Judy Hsiaod5c1e962020-02-04 12:30:01 +08003373 #[test]
Judy Hsiaob4b94c72020-09-07 15:56:00 +08003374 fn parse_ac97_capture_vaild() {
3375 parse_ac97_options("backend=cras,capture=true").expect("parse should have succeded");
Judy Hsiaod5c1e962020-02-04 12:30:01 +08003376 }
3377
Dennis Kempin50a58f92021-06-23 11:34:31 -07003378 #[cfg(feature = "audio_cras")]
Jorge E. Moreira359e7de2020-12-02 18:25:53 -08003379 #[test]
paulhsia83d51602021-03-09 17:13:14 +08003380 fn parse_ac97_client_type() {
3381 parse_ac97_options("backend=cras,capture=true,client_type=crosvm")
3382 .expect("parse should have succeded");
3383 parse_ac97_options("backend=cras,capture=true,client_type=arcvm")
3384 .expect("parse should have succeded");
3385 parse_ac97_options("backend=cras,capture=true,client_type=none")
3386 .expect_err("parse should have failed");
3387 }
3388
Woody Chowb27dea42021-09-08 15:51:22 +09003389 #[cfg(feature = "audio_cras")]
3390 #[test]
3391 fn parse_ac97_socket_type() {
3392 parse_ac97_options("socket_type=unified").expect("parse should have succeded");
3393 parse_ac97_options("socket_type=legacy").expect("parse should have succeded");
3394 }
3395
paulhsia83d51602021-03-09 17:13:14 +08003396 #[cfg(feature = "audio")]
3397 #[test]
Jorge E. Moreira359e7de2020-12-02 18:25:53 -08003398 fn parse_ac97_vios_valid() {
3399 parse_ac97_options("backend=vios,server=/path/to/server")
3400 .expect("parse should have succeded");
3401 }
3402
Judy Hsiaod5c1e962020-02-04 12:30:01 +08003403 #[test]
Trent Begin17ccaad2019-04-17 13:51:25 -06003404 fn parse_serial_vaild() {
Jorge E. Moreira1e262302019-08-01 14:40:03 -07003405 parse_serial_options("type=syslog,num=1,console=true,stdin=true")
3406 .expect("parse should have succeded");
Trent Begin17ccaad2019-04-17 13:51:25 -06003407 }
3408
3409 #[test]
A. Cody Schuffelen3faab5a2020-10-05 17:29:16 -07003410 fn parse_serial_virtio_console_vaild() {
3411 parse_serial_options("type=syslog,num=5,console=true,stdin=true,hardware=virtio-console")
3412 .expect("parse should have succeded");
3413 }
3414
3415 #[test]
Trent Begin923bab02019-06-17 13:48:06 -06003416 fn parse_serial_valid_no_num() {
3417 parse_serial_options("type=syslog").expect("parse should have succeded");
3418 }
3419
3420 #[test]
Nicholas Hollingumc76c2da2020-09-18 15:53:16 +10003421 fn parse_serial_equals_in_value() {
3422 let parsed = parse_serial_options("type=syslog,path=foo=bar==.log")
3423 .expect("parse should have succeded");
3424 assert_eq!(parsed.path, Some(PathBuf::from("foo=bar==.log")));
3425 }
3426
3427 #[test]
Trent Begin17ccaad2019-04-17 13:51:25 -06003428 fn parse_serial_invalid_type() {
3429 parse_serial_options("type=wormhole,num=1").expect_err("parse should have failed");
3430 }
3431
3432 #[test]
3433 fn parse_serial_invalid_num_upper() {
3434 parse_serial_options("type=syslog,num=5").expect_err("parse should have failed");
3435 }
3436
3437 #[test]
3438 fn parse_serial_invalid_num_lower() {
3439 parse_serial_options("type=syslog,num=0").expect_err("parse should have failed");
3440 }
3441
3442 #[test]
A. Cody Schuffelen3faab5a2020-10-05 17:29:16 -07003443 fn parse_serial_virtio_console_invalid_num_lower() {
3444 parse_serial_options("type=syslog,hardware=virtio-console,num=0")
3445 .expect_err("parse should have failed");
3446 }
3447
3448 #[test]
Trent Begin17ccaad2019-04-17 13:51:25 -06003449 fn parse_serial_invalid_num_string() {
3450 parse_serial_options("type=syslog,num=number3").expect_err("parse should have failed");
3451 }
3452
3453 #[test]
3454 fn parse_serial_invalid_option() {
3455 parse_serial_options("type=syslog,speed=lightspeed").expect_err("parse should have failed");
3456 }
Jorge E. Moreira1e262302019-08-01 14:40:03 -07003457
3458 #[test]
3459 fn parse_serial_invalid_two_stdin() {
3460 let mut config = Config::default();
3461 set_argument(&mut config, "serial", Some("num=1,type=stdout,stdin=true"))
3462 .expect("should parse the first serial argument");
3463 set_argument(&mut config, "serial", Some("num=2,type=stdout,stdin=true"))
3464 .expect_err("should fail to parse a second serial port connected to stdin");
3465 }
Dmitry Torokhov458bb642019-12-13 11:47:52 -08003466
3467 #[test]
3468 fn parse_plugin_mount_valid() {
3469 let mut config = Config::default();
3470 set_argument(
3471 &mut config,
3472 "plugin-mount",
3473 Some("/dev/null:/dev/zero:true"),
3474 )
3475 .expect("parse should succeed");
3476 assert_eq!(config.plugin_mounts[0].src, PathBuf::from("/dev/null"));
3477 assert_eq!(config.plugin_mounts[0].dst, PathBuf::from("/dev/zero"));
Daniel Verkamp5e9959e2021-08-19 17:09:59 -07003478 assert!(config.plugin_mounts[0].writable);
Dmitry Torokhov458bb642019-12-13 11:47:52 -08003479 }
3480
3481 #[test]
3482 fn parse_plugin_mount_valid_shorthand() {
3483 let mut config = Config::default();
3484 set_argument(&mut config, "plugin-mount", Some("/dev/null")).expect("parse should succeed");
3485 assert_eq!(config.plugin_mounts[0].dst, PathBuf::from("/dev/null"));
Daniel Verkamp5e9959e2021-08-19 17:09:59 -07003486 assert!(!config.plugin_mounts[0].writable);
Dmitry Torokhov458bb642019-12-13 11:47:52 -08003487 set_argument(&mut config, "plugin-mount", Some("/dev/null:/dev/zero"))
3488 .expect("parse should succeed");
3489 assert_eq!(config.plugin_mounts[1].dst, PathBuf::from("/dev/zero"));
Daniel Verkamp5e9959e2021-08-19 17:09:59 -07003490 assert!(!config.plugin_mounts[1].writable);
Dmitry Torokhov458bb642019-12-13 11:47:52 -08003491 set_argument(&mut config, "plugin-mount", Some("/dev/null::true"))
3492 .expect("parse should succeed");
3493 assert_eq!(config.plugin_mounts[2].dst, PathBuf::from("/dev/null"));
Daniel Verkamp5e9959e2021-08-19 17:09:59 -07003494 assert!(config.plugin_mounts[2].writable);
Dmitry Torokhov458bb642019-12-13 11:47:52 -08003495 }
3496
3497 #[test]
3498 fn parse_plugin_mount_invalid() {
3499 let mut config = Config::default();
3500 set_argument(&mut config, "plugin-mount", Some("")).expect_err("parse should fail");
3501 set_argument(
3502 &mut config,
3503 "plugin-mount",
3504 Some("/dev/null:/dev/null:true:false"),
3505 )
3506 .expect_err("parse should fail because too many arguments");
3507 set_argument(&mut config, "plugin-mount", Some("null:/dev/null:true"))
3508 .expect_err("parse should fail because source is not absolute");
3509 set_argument(&mut config, "plugin-mount", Some("/dev/null:null:true"))
3510 .expect_err("parse should fail because source is not absolute");
3511 set_argument(&mut config, "plugin-mount", Some("/dev/null:null:blah"))
3512 .expect_err("parse should fail because flag is not boolean");
3513 }
3514
3515 #[test]
3516 fn parse_plugin_gid_map_valid() {
3517 let mut config = Config::default();
3518 set_argument(&mut config, "plugin-gid-map", Some("1:2:3")).expect("parse should succeed");
3519 assert_eq!(config.plugin_gid_maps[0].inner, 1);
3520 assert_eq!(config.plugin_gid_maps[0].outer, 2);
3521 assert_eq!(config.plugin_gid_maps[0].count, 3);
3522 }
3523
3524 #[test]
3525 fn parse_plugin_gid_map_valid_shorthand() {
3526 let mut config = Config::default();
3527 set_argument(&mut config, "plugin-gid-map", Some("1")).expect("parse should succeed");
3528 assert_eq!(config.plugin_gid_maps[0].inner, 1);
3529 assert_eq!(config.plugin_gid_maps[0].outer, 1);
3530 assert_eq!(config.plugin_gid_maps[0].count, 1);
3531 set_argument(&mut config, "plugin-gid-map", Some("1:2")).expect("parse should succeed");
3532 assert_eq!(config.plugin_gid_maps[1].inner, 1);
3533 assert_eq!(config.plugin_gid_maps[1].outer, 2);
3534 assert_eq!(config.plugin_gid_maps[1].count, 1);
3535 set_argument(&mut config, "plugin-gid-map", Some("1::3")).expect("parse should succeed");
3536 assert_eq!(config.plugin_gid_maps[2].inner, 1);
3537 assert_eq!(config.plugin_gid_maps[2].outer, 1);
3538 assert_eq!(config.plugin_gid_maps[2].count, 3);
3539 }
3540
3541 #[test]
3542 fn parse_plugin_gid_map_invalid() {
3543 let mut config = Config::default();
3544 set_argument(&mut config, "plugin-gid-map", Some("")).expect_err("parse should fail");
3545 set_argument(&mut config, "plugin-gid-map", Some("1:2:3:4"))
3546 .expect_err("parse should fail because too many arguments");
3547 set_argument(&mut config, "plugin-gid-map", Some("blah:2:3"))
3548 .expect_err("parse should fail because inner is not a number");
3549 set_argument(&mut config, "plugin-gid-map", Some("1:blah:3"))
3550 .expect_err("parse should fail because outer is not a number");
3551 set_argument(&mut config, "plugin-gid-map", Some("1:2:blah"))
3552 .expect_err("parse should fail because count is not a number");
3553 }
Kaiyi Libccb4eb2020-02-06 17:53:11 -08003554
3555 #[test]
3556 fn single_touch_spec_and_track_pad_spec_default_size() {
3557 let mut config = Config::default();
3558 config
3559 .executable_path
3560 .replace(Executable::Kernel(PathBuf::from("kernel")));
3561 set_argument(&mut config, "single-touch", Some("/dev/single-touch-test")).unwrap();
3562 set_argument(&mut config, "trackpad", Some("/dev/single-touch-test")).unwrap();
3563 validate_arguments(&mut config).unwrap();
3564 assert_eq!(
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07003565 config.virtio_single_touch.first().unwrap().get_size(),
Kaiyi Libccb4eb2020-02-06 17:53:11 -08003566 (DEFAULT_TOUCH_DEVICE_WIDTH, DEFAULT_TOUCH_DEVICE_HEIGHT)
3567 );
3568 assert_eq!(
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07003569 config.virtio_trackpad.first().unwrap().get_size(),
Kaiyi Libccb4eb2020-02-06 17:53:11 -08003570 (DEFAULT_TOUCH_DEVICE_WIDTH, DEFAULT_TOUCH_DEVICE_HEIGHT)
3571 );
3572 }
3573
3574 #[cfg(feature = "gpu")]
3575 #[test]
3576 fn single_touch_spec_default_size_from_gpu() {
3577 let width = 12345u32;
3578 let height = 54321u32;
3579 let mut config = Config::default();
3580 config
3581 .executable_path
3582 .replace(Executable::Kernel(PathBuf::from("kernel")));
3583 set_argument(&mut config, "single-touch", Some("/dev/single-touch-test")).unwrap();
3584 set_argument(
3585 &mut config,
3586 "gpu",
3587 Some(&format!("width={},height={}", width, height)),
3588 )
3589 .unwrap();
3590 validate_arguments(&mut config).unwrap();
3591 assert_eq!(
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07003592 config.virtio_single_touch.first().unwrap().get_size(),
Kaiyi Libccb4eb2020-02-06 17:53:11 -08003593 (width, height)
3594 );
3595 }
3596
3597 #[test]
3598 fn single_touch_spec_and_track_pad_spec_with_size() {
3599 let width = 12345u32;
3600 let height = 54321u32;
3601 let mut config = Config::default();
3602 config
3603 .executable_path
3604 .replace(Executable::Kernel(PathBuf::from("kernel")));
3605 set_argument(
3606 &mut config,
3607 "single-touch",
3608 Some(&format!("/dev/single-touch-test:{}:{}", width, height)),
3609 )
3610 .unwrap();
3611 set_argument(
3612 &mut config,
3613 "trackpad",
3614 Some(&format!("/dev/single-touch-test:{}:{}", width, height)),
3615 )
3616 .unwrap();
3617 validate_arguments(&mut config).unwrap();
3618 assert_eq!(
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07003619 config.virtio_single_touch.first().unwrap().get_size(),
Kaiyi Libccb4eb2020-02-06 17:53:11 -08003620 (width, height)
3621 );
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07003622 assert_eq!(
3623 config.virtio_trackpad.first().unwrap().get_size(),
3624 (width, height)
3625 );
Kaiyi Libccb4eb2020-02-06 17:53:11 -08003626 }
3627
3628 #[cfg(feature = "gpu")]
3629 #[test]
3630 fn single_touch_spec_with_size_independent_from_gpu() {
3631 let touch_width = 12345u32;
3632 let touch_height = 54321u32;
3633 let display_width = 1234u32;
3634 let display_height = 5432u32;
3635 let mut config = Config::default();
3636 config
3637 .executable_path
3638 .replace(Executable::Kernel(PathBuf::from("kernel")));
3639 set_argument(
3640 &mut config,
3641 "single-touch",
3642 Some(&format!(
3643 "/dev/single-touch-test:{}:{}",
3644 touch_width, touch_height
3645 )),
3646 )
3647 .unwrap();
3648 set_argument(
3649 &mut config,
3650 "gpu",
3651 Some(&format!(
3652 "width={},height={}",
3653 display_width, display_height
3654 )),
3655 )
3656 .unwrap();
3657 validate_arguments(&mut config).unwrap();
3658 assert_eq!(
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07003659 config.virtio_single_touch.first().unwrap().get_size(),
Kaiyi Libccb4eb2020-02-06 17:53:11 -08003660 (touch_width, touch_height)
3661 );
3662 }
Kaiyi Lidd348a42020-07-13 11:49:46 -07003663
Daniel Norman5e23df72021-03-11 10:11:02 -08003664 #[test]
3665 fn virtio_switches() {
3666 let mut config = Config::default();
3667 config
3668 .executable_path
3669 .replace(Executable::Kernel(PathBuf::from("kernel")));
3670 set_argument(&mut config, "switches", Some("/dev/switches-test")).unwrap();
3671 validate_arguments(&mut config).unwrap();
3672 assert_eq!(
Jorge E. Moreira6635ca42021-04-28 13:11:41 -07003673 config.virtio_switches.pop().unwrap(),
Keiichi Watanabed56a2f42021-03-18 20:16:23 +09003674 PathBuf::from("/dev/switches-test")
3675 );
Daniel Norman5e23df72021-03-11 10:11:02 -08003676 }
3677
Chia-I Wu6d473b32021-04-12 10:14:24 -07003678 #[cfg(feature = "gpu")]
Kaiyi Lidd348a42020-07-13 11:49:46 -07003679 #[test]
Chia-I Wu91df6562021-04-12 09:47:38 -07003680 fn parse_gpu_options_default_vulkan_support() {
Jason Macnakd659a0d2021-03-15 15:33:01 -07003681 {
3682 let mut gpu_params: GpuParameters = Default::default();
3683 assert!(parse_gpu_options(Some("backend=virglrenderer"), &mut gpu_params).is_ok());
3684 assert!(!gpu_params.use_vulkan);
3685 }
Chia-I Wu6d473b32021-04-12 10:14:24 -07003686
3687 #[cfg(feature = "gfxstream")]
Jason Macnakd659a0d2021-03-15 15:33:01 -07003688 {
3689 let mut gpu_params: GpuParameters = Default::default();
3690 assert!(parse_gpu_options(Some("backend=gfxstream"), &mut gpu_params).is_ok());
3691 assert!(gpu_params.use_vulkan);
3692 }
Chia-I Wu91df6562021-04-12 09:47:38 -07003693 }
3694
Chia-I Wu6d473b32021-04-12 10:14:24 -07003695 #[cfg(feature = "gpu")]
Chia-I Wu91df6562021-04-12 09:47:38 -07003696 #[test]
Chia-I Wu6d473b32021-04-12 10:14:24 -07003697 fn parse_gpu_options_with_vulkan_specified() {
Jason Macnakd659a0d2021-03-15 15:33:01 -07003698 {
3699 let mut gpu_params: GpuParameters = Default::default();
3700 assert!(parse_gpu_options(Some("vulkan=true"), &mut gpu_params).is_ok());
3701 assert!(gpu_params.use_vulkan);
3702 }
3703 {
3704 let mut gpu_params: GpuParameters = Default::default();
3705 assert!(
3706 parse_gpu_options(Some("backend=virglrenderer,vulkan=true"), &mut gpu_params)
3707 .is_ok()
3708 );
3709 assert!(gpu_params.use_vulkan);
3710 }
3711 {
3712 let mut gpu_params: GpuParameters = Default::default();
3713 assert!(
3714 parse_gpu_options(Some("vulkan=true,backend=virglrenderer"), &mut gpu_params)
3715 .is_ok()
3716 );
3717 assert!(gpu_params.use_vulkan);
3718 }
3719 {
3720 let mut gpu_params: GpuParameters = Default::default();
3721 assert!(parse_gpu_options(Some("vulkan=false"), &mut gpu_params).is_ok());
3722 assert!(!gpu_params.use_vulkan);
3723 }
3724 {
3725 let mut gpu_params: GpuParameters = Default::default();
3726 assert!(
3727 parse_gpu_options(Some("backend=virglrenderer,vulkan=false"), &mut gpu_params)
3728 .is_ok()
3729 );
3730 assert!(!gpu_params.use_vulkan);
3731 }
3732 {
3733 let mut gpu_params: GpuParameters = Default::default();
3734 assert!(
3735 parse_gpu_options(Some("vulkan=false,backend=virglrenderer"), &mut gpu_params)
3736 .is_ok()
3737 );
3738 assert!(!gpu_params.use_vulkan);
3739 }
3740 {
3741 let mut gpu_params: GpuParameters = Default::default();
3742 assert!(parse_gpu_options(
3743 Some("backend=virglrenderer,vulkan=invalid_value"),
3744 &mut gpu_params
3745 )
3746 .is_err());
3747 }
3748 {
3749 let mut gpu_params: GpuParameters = Default::default();
3750 assert!(parse_gpu_options(
3751 Some("vulkan=invalid_value,backend=virglrenderer"),
3752 &mut gpu_params
3753 )
3754 .is_err());
3755 }
Kaiyi Lidd348a42020-07-13 11:49:46 -07003756 }
3757
3758 #[cfg(all(feature = "gpu", feature = "gfxstream"))]
3759 #[test]
3760 fn parse_gpu_options_gfxstream_with_syncfd_specified() {
Jason Macnakd659a0d2021-03-15 15:33:01 -07003761 {
3762 let mut gpu_params: GpuParameters = Default::default();
3763 assert!(
3764 parse_gpu_options(Some("backend=gfxstream,syncfd=true"), &mut gpu_params).is_ok()
3765 );
3766 assert!(gpu_params.gfxstream_use_syncfd);
3767 }
3768 {
3769 let mut gpu_params: GpuParameters = Default::default();
3770 assert!(
3771 parse_gpu_options(Some("syncfd=true,backend=gfxstream"), &mut gpu_params).is_ok()
3772 );
3773 assert!(gpu_params.gfxstream_use_syncfd);
3774 }
3775 {
3776 let mut gpu_params: GpuParameters = Default::default();
3777 assert!(
3778 parse_gpu_options(Some("backend=gfxstream,syncfd=false"), &mut gpu_params).is_ok()
3779 );
3780 assert!(!gpu_params.gfxstream_use_syncfd);
3781 }
3782 {
3783 let mut gpu_params: GpuParameters = Default::default();
3784 assert!(
3785 parse_gpu_options(Some("syncfd=false,backend=gfxstream"), &mut gpu_params).is_ok()
3786 );
3787 assert!(!gpu_params.gfxstream_use_syncfd);
3788 }
3789 {
3790 let mut gpu_params: GpuParameters = Default::default();
3791 assert!(parse_gpu_options(
3792 Some("backend=gfxstream,syncfd=invalid_value"),
3793 &mut gpu_params
3794 )
3795 .is_err());
3796 }
3797 {
3798 let mut gpu_params: GpuParameters = Default::default();
3799 assert!(parse_gpu_options(
3800 Some("syncfd=invalid_value,backend=gfxstream"),
3801 &mut gpu_params
3802 )
3803 .is_err());
3804 }
Kaiyi Lidd348a42020-07-13 11:49:46 -07003805 }
3806
3807 #[cfg(all(feature = "gpu", feature = "gfxstream"))]
3808 #[test]
3809 fn parse_gpu_options_not_gfxstream_with_syncfd_specified() {
Jason Macnakd659a0d2021-03-15 15:33:01 -07003810 {
3811 let mut gpu_params: GpuParameters = Default::default();
3812 assert!(
3813 parse_gpu_options(Some("backend=virglrenderer,syncfd=true"), &mut gpu_params)
3814 .is_err()
3815 );
3816 }
3817 {
3818 let mut gpu_params: GpuParameters = Default::default();
3819 assert!(
3820 parse_gpu_options(Some("syncfd=true,backend=virglrenderer"), &mut gpu_params)
3821 .is_err()
3822 );
3823 }
3824 }
3825
3826 #[cfg(feature = "gpu")]
3827 #[test]
3828 fn parse_gpu_display_options_valid() {
3829 {
3830 let mut gpu_params: GpuParameters = Default::default();
3831 assert!(
3832 parse_gpu_display_options(Some("width=500,height=600"), &mut gpu_params).is_ok()
3833 );
3834 assert_eq!(gpu_params.displays.len(), 1);
3835 assert_eq!(gpu_params.displays[0].width, 500);
3836 assert_eq!(gpu_params.displays[0].height, 600);
3837 }
3838 }
3839
3840 #[cfg(feature = "gpu")]
3841 #[test]
3842 fn parse_gpu_display_options_invalid() {
3843 {
3844 let mut gpu_params: GpuParameters = Default::default();
3845 assert!(parse_gpu_display_options(Some("width=500"), &mut gpu_params).is_err());
3846 }
3847 {
3848 let mut gpu_params: GpuParameters = Default::default();
3849 assert!(parse_gpu_display_options(Some("height=500"), &mut gpu_params).is_err());
3850 }
3851 {
3852 let mut gpu_params: GpuParameters = Default::default();
3853 assert!(parse_gpu_display_options(Some("width"), &mut gpu_params).is_err());
3854 }
3855 {
3856 let mut gpu_params: GpuParameters = Default::default();
3857 assert!(parse_gpu_display_options(Some("blah"), &mut gpu_params).is_err());
3858 }
3859 }
3860
3861 #[cfg(feature = "gpu")]
3862 #[test]
3863 fn parse_gpu_options_and_gpu_display_options_valid() {
3864 {
3865 let mut gpu_params: GpuParameters = Default::default();
3866 assert!(parse_gpu_options(Some("2D,width=500,height=600"), &mut gpu_params).is_ok());
3867 assert!(
3868 parse_gpu_display_options(Some("width=700,height=800"), &mut gpu_params).is_ok()
3869 );
3870 assert_eq!(gpu_params.displays.len(), 2);
3871 assert_eq!(gpu_params.displays[0].width, 500);
3872 assert_eq!(gpu_params.displays[0].height, 600);
3873 assert_eq!(gpu_params.displays[1].width, 700);
3874 assert_eq!(gpu_params.displays[1].height, 800);
3875 }
3876 {
3877 let mut gpu_params: GpuParameters = Default::default();
3878 assert!(parse_gpu_options(Some("2D"), &mut gpu_params).is_ok());
3879 assert!(
3880 parse_gpu_display_options(Some("width=700,height=800"), &mut gpu_params).is_ok()
3881 );
3882 assert_eq!(gpu_params.displays.len(), 1);
3883 assert_eq!(gpu_params.displays[0].width, 700);
3884 assert_eq!(gpu_params.displays[0].height, 800);
3885 }
Kaiyi Lidd348a42020-07-13 11:49:46 -07003886 }
Chuanxiao Dongfd5626c2020-04-27 16:35:33 +08003887
3888 #[test]
3889 fn parse_battery_vaild() {
3890 parse_battery_options(Some("type=goldfish")).expect("parse should have succeded");
3891 }
3892
3893 #[test]
3894 fn parse_battery_vaild_no_type() {
3895 parse_battery_options(None).expect("parse should have succeded");
3896 }
3897
3898 #[test]
3899 fn parse_battery_invaild_parameter() {
3900 parse_battery_options(Some("tyep=goldfish")).expect_err("parse should have failed");
3901 }
3902
3903 #[test]
3904 fn parse_battery_invaild_type_value() {
3905 parse_battery_options(Some("type=xxx")).expect_err("parse should have failed");
3906 }
Mattias Nisslerde2c6402021-10-21 12:05:29 +00003907
3908 #[test]
3909 fn parse_stub_pci() {
Daniel Verkamp42b33602021-12-15 15:35:08 -08003910 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 +00003911 assert_eq!(params.address.bus, 1);
3912 assert_eq!(params.address.dev, 2);
3913 assert_eq!(params.address.func, 3);
3914 assert_eq!(params.vendor_id, 0xfffe);
3915 assert_eq!(params.device_id, 0xfffd);
3916 assert_eq!(params.class as u8, PciClassCode::Other as u8);
3917 assert_eq!(params.subclass, 0xc1);
3918 assert_eq!(params.programming_interface, 0xc2);
Mattias Nisslerde2c6402021-10-21 12:05:29 +00003919 assert_eq!(params.subsystem_vendor_id, 0xfffc);
3920 assert_eq!(params.subsystem_device_id, 0xfffb);
3921 assert_eq!(params.revision_id, 0xa);
3922 }
Junichi Uekawab6a6e942021-12-07 05:49:30 +09003923
3924 #[cfg(feature = "direct")]
3925 #[test]
3926 fn parse_direct_io_options_valid() {
3927 let params = parse_direct_io_options(Some("/dev/mem@1,100-110")).unwrap();
3928 assert_eq!(params.path.to_str(), Some("/dev/mem"));
Junichi Uekawab180f9c2021-12-07 09:21:36 +09003929 assert_eq!(params.ranges[0], BusRange { base: 1, len: 1 });
3930 assert_eq!(params.ranges[1], BusRange { base: 100, len: 11 });
Junichi Uekawab6a6e942021-12-07 05:49:30 +09003931 }
3932
3933 #[cfg(feature = "direct")]
3934 #[test]
3935 fn parse_direct_io_options_hex() {
3936 let params = parse_direct_io_options(Some("/dev/mem@1,0x10,100-110,0x10-0x20")).unwrap();
3937 assert_eq!(params.path.to_str(), Some("/dev/mem"));
Junichi Uekawab180f9c2021-12-07 09:21:36 +09003938 assert_eq!(params.ranges[0], BusRange { base: 1, len: 1 });
3939 assert_eq!(params.ranges[1], BusRange { base: 0x10, len: 1 });
3940 assert_eq!(params.ranges[2], BusRange { base: 100, len: 11 });
3941 assert_eq!(
3942 params.ranges[3],
3943 BusRange {
3944 base: 0x10,
3945 len: 0x11
3946 }
3947 );
Junichi Uekawab6a6e942021-12-07 05:49:30 +09003948 }
3949
3950 #[cfg(feature = "direct")]
3951 #[test]
3952 fn parse_direct_io_options_invalid() {
Junichi Uekawa4d312052021-12-08 16:59:38 +09003953 assert!(parse_direct_io_options(Some("/dev/mem@0y10"))
3954 .unwrap_err()
3955 .to_string()
3956 .contains("invalid base range value"));
3957
3958 assert!(parse_direct_io_options(Some("/dev/mem@"))
3959 .unwrap_err()
3960 .to_string()
3961 .contains("invalid base range value"));
Junichi Uekawab6a6e942021-12-07 05:49:30 +09003962 }
Daniel Verkamp107edb32019-04-05 09:58:48 -07003963}