1 // 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
5 //! Runs a virtual machine
6
7 pub mod panic_hook;
8
9 use std::collections::BTreeMap;
10 use std::default::Default;
11 use std::fs::{File, OpenOptions};
12 use std::io::{BufRead, BufReader};
13 use std::path::{Path, PathBuf};
14 use std::str::FromStr;
15 use std::string::String;
16 use std::thread::sleep;
17 use std::time::Duration;
18
19 use arch::{
20 set_default_serial_parameters, Pstore, SerialHardware, SerialParameters, SerialType,
21 VcpuAffinity,
22 };
23 use base::{debug, error, getpid, info, kill_process_group, reap_child, syslog, warn};
24 #[cfg(feature = "direct")]
25 use crosvm::DirectIoOption;
26 use crosvm::{
27 argument::{self, print_help, set_arguments, Argument},
28 platform, BindMount, Config, DiskOption, Executable, GidMap, SharedDir, TouchDeviceOption,
29 VhostUserFsOption, VhostUserOption, DISK_ID_LEN,
30 };
31 #[cfg(feature = "gpu")]
32 use devices::virtio::gpu::{GpuMode, GpuParameters};
33 use devices::ProtectionType;
34 #[cfg(feature = "audio")]
35 use devices::{Ac97Backend, Ac97Parameters};
36 use disk::QcowFile;
37 use vm_control::{
38 client::{
39 do_modify_battery, do_usb_attach, do_usb_detach, do_usb_list, handle_request, vms_request,
40 ModifyUsbError, ModifyUsbResult,
41 },
42 BalloonControlCommand, BatteryType, DiskControlCommand, UsbControlResult, VmRequest,
43 };
44
executable_is_plugin(executable: &Option<Executable>) -> bool45 fn executable_is_plugin(executable: &Option<Executable>) -> bool {
46 matches!(executable, Some(Executable::Plugin(_)))
47 }
48
49 // Wait for all children to exit. Return true if they have all exited, false
50 // otherwise.
wait_all_children() -> bool51 fn wait_all_children() -> bool {
52 const CHILD_WAIT_MAX_ITER: isize = 100;
53 const CHILD_WAIT_MS: u64 = 10;
54 for _ in 0..CHILD_WAIT_MAX_ITER {
55 loop {
56 match reap_child() {
57 Ok(0) => break,
58 // We expect ECHILD which indicates that there were no children left.
59 Err(e) if e.errno() == libc::ECHILD => return true,
60 Err(e) => {
61 warn!("error while waiting for children: {}", e);
62 return false;
63 }
64 // We reaped one child, so continue reaping.
65 _ => {}
66 }
67 }
68 // There's no timeout option for waitpid which reap_child calls internally, so our only
69 // recourse is to sleep while waiting for the children to exit.
70 sleep(Duration::from_millis(CHILD_WAIT_MS));
71 }
72
73 // If we've made it to this point, not all of the children have exited.
74 false
75 }
76
77 /// Parse a comma-separated list of CPU numbers and ranges and convert it to a Vec of CPU numbers.
parse_cpu_set(s: &str) -> argument::Result<Vec<usize>>78 fn parse_cpu_set(s: &str) -> argument::Result<Vec<usize>> {
79 let mut cpuset = Vec::new();
80 for part in s.split(',') {
81 let range: Vec<&str> = part.split('-').collect();
82 if range.is_empty() || range.len() > 2 {
83 return Err(argument::Error::InvalidValue {
84 value: part.to_owned(),
85 expected: String::from("invalid list syntax"),
86 });
87 }
88 let first_cpu: usize = range[0]
89 .parse()
90 .map_err(|_| argument::Error::InvalidValue {
91 value: part.to_owned(),
92 expected: String::from("CPU index must be a non-negative integer"),
93 })?;
94 let last_cpu: usize = if range.len() == 2 {
95 range[1]
96 .parse()
97 .map_err(|_| argument::Error::InvalidValue {
98 value: part.to_owned(),
99 expected: String::from("CPU index must be a non-negative integer"),
100 })?
101 } else {
102 first_cpu
103 };
104
105 if last_cpu < first_cpu {
106 return Err(argument::Error::InvalidValue {
107 value: part.to_owned(),
108 expected: String::from("CPU ranges must be from low to high"),
109 });
110 }
111
112 for cpu in first_cpu..=last_cpu {
113 cpuset.push(cpu);
114 }
115 }
116 Ok(cpuset)
117 }
118
119 /// Parse a list of guest to host CPU mappings.
120 ///
121 /// Each mapping consists of a single guest CPU index mapped to one or more host CPUs in the form
122 /// accepted by `parse_cpu_set`:
123 ///
124 /// `<GUEST-CPU>=<HOST-CPU-SET>[:<GUEST-CPU>=<HOST-CPU-SET>[:...]]`
parse_cpu_affinity(s: &str) -> argument::Result<VcpuAffinity>125 fn parse_cpu_affinity(s: &str) -> argument::Result<VcpuAffinity> {
126 if s.contains('=') {
127 let mut affinity_map = BTreeMap::new();
128 for cpu_pair in s.split(':') {
129 let assignment: Vec<&str> = cpu_pair.split('=').collect();
130 if assignment.len() != 2 {
131 return Err(argument::Error::InvalidValue {
132 value: cpu_pair.to_owned(),
133 expected: String::from("invalid VCPU assignment syntax"),
134 });
135 }
136 let guest_cpu = assignment[0]
137 .parse()
138 .map_err(|_| argument::Error::InvalidValue {
139 value: assignment[0].to_owned(),
140 expected: String::from("CPU index must be a non-negative integer"),
141 })?;
142 let host_cpu_set = parse_cpu_set(assignment[1])?;
143 if affinity_map.insert(guest_cpu, host_cpu_set).is_some() {
144 return Err(argument::Error::InvalidValue {
145 value: cpu_pair.to_owned(),
146 expected: String::from("VCPU index must be unique"),
147 });
148 }
149 }
150 Ok(VcpuAffinity::PerVcpu(affinity_map))
151 } else {
152 Ok(VcpuAffinity::Global(parse_cpu_set(s)?))
153 }
154 }
155
156 #[cfg(feature = "gpu")]
parse_gpu_options(s: Option<&str>) -> argument::Result<GpuParameters>157 fn parse_gpu_options(s: Option<&str>) -> argument::Result<GpuParameters> {
158 let mut gpu_params: GpuParameters = Default::default();
159 #[cfg(feature = "gfxstream")]
160 let mut vulkan_specified = false;
161 #[cfg(feature = "gfxstream")]
162 let mut syncfd_specified = false;
163 #[cfg(feature = "gfxstream")]
164 let mut angle_specified = false;
165
166 if let Some(s) = s {
167 let opts = s
168 .split(',')
169 .map(|frag| frag.split('='))
170 .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
171
172 for (k, v) in opts {
173 match k {
174 // Deprecated: Specifying --gpu=<mode> Not great as the mode can be set multiple
175 // times if the user specifies several modes (--gpu=2d,virglrenderer,gfxstream)
176 "2d" | "2D" => {
177 gpu_params.mode = GpuMode::Mode2D;
178 }
179 "3d" | "3D" | "virglrenderer" => {
180 gpu_params.mode = GpuMode::ModeVirglRenderer;
181 }
182 #[cfg(feature = "gfxstream")]
183 "gfxstream" => {
184 gpu_params.mode = GpuMode::ModeGfxstream;
185 }
186 // Preferred: Specifying --gpu,backend=<mode>
187 "backend" => match v {
188 "2d" | "2D" => {
189 gpu_params.mode = GpuMode::Mode2D;
190 }
191 "3d" | "3D" | "virglrenderer" => {
192 gpu_params.mode = GpuMode::ModeVirglRenderer;
193 }
194 #[cfg(feature = "gfxstream")]
195 "gfxstream" => {
196 gpu_params.mode = GpuMode::ModeGfxstream;
197 }
198 _ => {
199 return Err(argument::Error::InvalidValue {
200 value: v.to_string(),
201 expected: String::from(
202 "gpu parameter 'backend' should be one of (2d|virglrenderer|gfxstream)",
203 ),
204 });
205 }
206 },
207 "egl" => match v {
208 "true" | "" => {
209 gpu_params.renderer_use_egl = true;
210 }
211 "false" => {
212 gpu_params.renderer_use_egl = false;
213 }
214 _ => {
215 return Err(argument::Error::InvalidValue {
216 value: v.to_string(),
217 expected: String::from("gpu parameter 'egl' should be a boolean"),
218 });
219 }
220 },
221 "gles" => match v {
222 "true" | "" => {
223 gpu_params.renderer_use_gles = true;
224 }
225 "false" => {
226 gpu_params.renderer_use_gles = false;
227 }
228 _ => {
229 return Err(argument::Error::InvalidValue {
230 value: v.to_string(),
231 expected: String::from("gpu parameter 'gles' should be a boolean"),
232 });
233 }
234 },
235 "glx" => match v {
236 "true" | "" => {
237 gpu_params.renderer_use_glx = true;
238 }
239 "false" => {
240 gpu_params.renderer_use_glx = false;
241 }
242 _ => {
243 return Err(argument::Error::InvalidValue {
244 value: v.to_string(),
245 expected: String::from("gpu parameter 'glx' should be a boolean"),
246 });
247 }
248 },
249 "surfaceless" => match v {
250 "true" | "" => {
251 gpu_params.renderer_use_surfaceless = true;
252 }
253 "false" => {
254 gpu_params.renderer_use_surfaceless = false;
255 }
256 _ => {
257 return Err(argument::Error::InvalidValue {
258 value: v.to_string(),
259 expected: String::from(
260 "gpu parameter 'surfaceless' should be a boolean",
261 ),
262 });
263 }
264 },
265 #[cfg(feature = "gfxstream")]
266 "syncfd" => {
267 syncfd_specified = true;
268 match v {
269 "true" | "" => {
270 gpu_params.gfxstream_use_syncfd = true;
271 }
272 "false" => {
273 gpu_params.gfxstream_use_syncfd = false;
274 }
275 _ => {
276 return Err(argument::Error::InvalidValue {
277 value: v.to_string(),
278 expected: String::from(
279 "gpu parameter 'syncfd' should be a boolean",
280 ),
281 });
282 }
283 }
284 }
285 #[cfg(feature = "gfxstream")]
286 "angle" => {
287 angle_specified = true;
288 match v {
289 "true" | "" => {
290 gpu_params.gfxstream_use_guest_angle = true;
291 }
292 "false" => {
293 gpu_params.gfxstream_use_guest_angle = false;
294 }
295 _ => {
296 return Err(argument::Error::InvalidValue {
297 value: v.to_string(),
298 expected: String::from("gpu parameter 'angle' should be a boolean"),
299 });
300 }
301 }
302 }
303 "vulkan" => {
304 #[cfg(feature = "gfxstream")]
305 {
306 vulkan_specified = true;
307 }
308 match v {
309 "true" | "" => {
310 gpu_params.use_vulkan = true;
311 }
312 "false" => {
313 gpu_params.use_vulkan = false;
314 }
315 _ => {
316 return Err(argument::Error::InvalidValue {
317 value: v.to_string(),
318 expected: String::from(
319 "gpu parameter 'vulkan' should be a boolean",
320 ),
321 });
322 }
323 }
324 }
325 "width" => {
326 gpu_params.display_width =
327 v.parse::<u32>()
328 .map_err(|_| argument::Error::InvalidValue {
329 value: v.to_string(),
330 expected: String::from(
331 "gpu parameter 'width' must be a valid integer",
332 ),
333 })?;
334 }
335 "height" => {
336 gpu_params.display_height =
337 v.parse::<u32>()
338 .map_err(|_| argument::Error::InvalidValue {
339 value: v.to_string(),
340 expected: String::from(
341 "gpu parameter 'height' must be a valid integer",
342 ),
343 })?;
344 }
345 "cache-path" => gpu_params.cache_path = Some(v.to_string()),
346 "cache-size" => gpu_params.cache_size = Some(v.to_string()),
347 "udmabuf" => match v {
348 "true" | "" => {
349 gpu_params.udmabuf = true;
350 }
351 "false" => {
352 gpu_params.udmabuf = false;
353 }
354 _ => {
355 return Err(argument::Error::InvalidValue {
356 value: v.to_string(),
357 expected: String::from("gpu parameter 'udmabuf' should be a boolean"),
358 });
359 }
360 },
361 "" => {}
362 _ => {
363 return Err(argument::Error::UnknownArgument(format!(
364 "gpu parameter {}",
365 k
366 )));
367 }
368 }
369 }
370 }
371
372 #[cfg(feature = "gfxstream")]
373 {
374 if !vulkan_specified && gpu_params.mode == GpuMode::ModeGfxstream {
375 gpu_params.use_vulkan = true;
376 }
377
378 if syncfd_specified || angle_specified {
379 match gpu_params.mode {
380 GpuMode::ModeGfxstream => {}
381 _ => {
382 return Err(argument::Error::UnknownArgument(
383 "gpu parameter syncfd and angle are only supported for gfxstream backend"
384 .to_string(),
385 ));
386 }
387 }
388 }
389 }
390
391 Ok(gpu_params)
392 }
393
394 #[cfg(feature = "audio")]
parse_ac97_options(s: &str) -> argument::Result<Ac97Parameters>395 fn parse_ac97_options(s: &str) -> argument::Result<Ac97Parameters> {
396 let mut ac97_params: Ac97Parameters = Default::default();
397
398 let opts = s
399 .split(',')
400 .map(|frag| frag.split('='))
401 .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
402
403 for (k, v) in opts {
404 match k {
405 "backend" => {
406 ac97_params.backend =
407 v.parse::<Ac97Backend>()
408 .map_err(|e| argument::Error::InvalidValue {
409 value: v.to_string(),
410 expected: e.to_string(),
411 })?;
412 }
413 "capture" => {
414 ac97_params.capture = v.parse::<bool>().map_err(|e| {
415 argument::Error::Syntax(format!("invalid capture option: {}", e))
416 })?;
417 }
418 "client_type" => {
419 ac97_params
420 .set_client_type(v)
421 .map_err(|e| argument::Error::InvalidValue {
422 value: v.to_string(),
423 expected: e.to_string(),
424 })?;
425 }
426 #[cfg(any(target_os = "linux", target_os = "android"))]
427 "server" => {
428 ac97_params.vios_server_path =
429 Some(
430 PathBuf::from_str(v).map_err(|e| argument::Error::InvalidValue {
431 value: v.to_string(),
432 expected: e.to_string(),
433 })?,
434 );
435 }
436 _ => {
437 return Err(argument::Error::UnknownArgument(format!(
438 "unknown ac97 parameter {}",
439 k
440 )));
441 }
442 }
443 }
444
445 // server is required for and exclusive to vios backend
446 #[cfg(any(target_os = "linux", target_os = "android"))]
447 match ac97_params.backend {
448 Ac97Backend::VIOS => {
449 if ac97_params.vios_server_path.is_none() {
450 return Err(argument::Error::ExpectedArgument(String::from(
451 "server argument is required for VIOS backend",
452 )));
453 }
454 }
455 _ => {
456 if ac97_params.vios_server_path.is_some() {
457 return Err(argument::Error::UnexpectedValue(String::from(
458 "server argument is exclusive to the VIOS backend",
459 )));
460 }
461 }
462 }
463
464 Ok(ac97_params)
465 }
466
parse_serial_options(s: &str) -> argument::Result<SerialParameters>467 fn parse_serial_options(s: &str) -> argument::Result<SerialParameters> {
468 let mut serial_setting = SerialParameters {
469 type_: SerialType::Sink,
470 hardware: SerialHardware::Serial,
471 path: None,
472 input: None,
473 num: 1,
474 console: false,
475 earlycon: false,
476 stdin: false,
477 };
478
479 let opts = s
480 .split(',')
481 .map(|frag| frag.splitn(2, '='))
482 .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
483
484 for (k, v) in opts {
485 match k {
486 "hardware" => {
487 serial_setting.hardware = v
488 .parse::<SerialHardware>()
489 .map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?
490 }
491 "type" => {
492 serial_setting.type_ = v
493 .parse::<SerialType>()
494 .map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?
495 }
496 "num" => {
497 let num = v.parse::<u8>().map_err(|e| {
498 argument::Error::Syntax(format!("serial device number is not parsable: {}", e))
499 })?;
500 if num < 1 {
501 return Err(argument::Error::InvalidValue {
502 value: num.to_string(),
503 expected: String::from("Serial port num must be at least 1"),
504 });
505 }
506 serial_setting.num = num;
507 }
508 "console" => {
509 serial_setting.console = v.parse::<bool>().map_err(|e| {
510 argument::Error::Syntax(format!(
511 "serial device console is not parseable: {}",
512 e
513 ))
514 })?
515 }
516 "earlycon" => {
517 serial_setting.earlycon = v.parse::<bool>().map_err(|e| {
518 argument::Error::Syntax(format!(
519 "serial device earlycon is not parseable: {}",
520 e,
521 ))
522 })?
523 }
524 "stdin" => {
525 serial_setting.stdin = v.parse::<bool>().map_err(|e| {
526 argument::Error::Syntax(format!("serial device stdin is not parseable: {}", e))
527 })?;
528 if serial_setting.stdin && serial_setting.input.is_some() {
529 return Err(argument::Error::TooManyArguments(
530 "Cannot specify both stdin and input options".to_string(),
531 ));
532 }
533 }
534 "path" => serial_setting.path = Some(PathBuf::from(v)),
535 "input" => {
536 if serial_setting.stdin {
537 return Err(argument::Error::TooManyArguments(
538 "Cannot specify both stdin and input options".to_string(),
539 ));
540 }
541 serial_setting.input = Some(PathBuf::from(v));
542 }
543 _ => {
544 return Err(argument::Error::UnknownArgument(format!(
545 "serial parameter {}",
546 k
547 )));
548 }
549 }
550 }
551
552 if serial_setting.hardware == SerialHardware::Serial && serial_setting.num > 4 {
553 return Err(argument::Error::InvalidValue {
554 value: serial_setting.num.to_string(),
555 expected: String::from("Serial port num must be 4 or less"),
556 });
557 }
558
559 Ok(serial_setting)
560 }
561
parse_plugin_mount_option(value: &str) -> argument::Result<BindMount>562 fn parse_plugin_mount_option(value: &str) -> argument::Result<BindMount> {
563 let components: Vec<&str> = value.split(':').collect();
564 if components.is_empty() || components.len() > 3 || components[0].is_empty() {
565 return Err(argument::Error::InvalidValue {
566 value: value.to_owned(),
567 expected: String::from(
568 "`plugin-mount` should be in a form of: <src>[:[<dst>][:<writable>]]",
569 ),
570 });
571 }
572
573 let src = PathBuf::from(components[0]);
574 if src.is_relative() {
575 return Err(argument::Error::InvalidValue {
576 value: components[0].to_owned(),
577 expected: String::from("the source path for `plugin-mount` must be absolute"),
578 });
579 }
580 if !src.exists() {
581 return Err(argument::Error::InvalidValue {
582 value: components[0].to_owned(),
583 expected: String::from("the source path for `plugin-mount` does not exist"),
584 });
585 }
586
587 let dst = PathBuf::from(match components.get(1) {
588 None | Some(&"") => components[0],
589 Some(path) => path,
590 });
591 if dst.is_relative() {
592 return Err(argument::Error::InvalidValue {
593 value: components[1].to_owned(),
594 expected: String::from("the destination path for `plugin-mount` must be absolute"),
595 });
596 }
597
598 let writable: bool = match components.get(2) {
599 None => false,
600 Some(s) => s.parse().map_err(|_| argument::Error::InvalidValue {
601 value: components[2].to_owned(),
602 expected: String::from("the <writable> component for `plugin-mount` is not valid bool"),
603 })?,
604 };
605
606 Ok(BindMount { src, dst, writable })
607 }
608
parse_plugin_gid_map_option(value: &str) -> argument::Result<GidMap>609 fn parse_plugin_gid_map_option(value: &str) -> argument::Result<GidMap> {
610 let components: Vec<&str> = value.split(':').collect();
611 if components.is_empty() || components.len() > 3 || components[0].is_empty() {
612 return Err(argument::Error::InvalidValue {
613 value: value.to_owned(),
614 expected: String::from(
615 "`plugin-gid-map` must have exactly 3 components: <inner>[:[<outer>][:<count>]]",
616 ),
617 });
618 }
619
620 let inner: libc::gid_t = components[0]
621 .parse()
622 .map_err(|_| argument::Error::InvalidValue {
623 value: components[0].to_owned(),
624 expected: String::from("the <inner> component for `plugin-gid-map` is not valid gid"),
625 })?;
626
627 let outer: libc::gid_t = match components.get(1) {
628 None | Some(&"") => inner,
629 Some(s) => s.parse().map_err(|_| argument::Error::InvalidValue {
630 value: components[1].to_owned(),
631 expected: String::from("the <outer> component for `plugin-gid-map` is not valid gid"),
632 })?,
633 };
634
635 let count: u32 = match components.get(2) {
636 None => 1,
637 Some(s) => s.parse().map_err(|_| argument::Error::InvalidValue {
638 value: components[2].to_owned(),
639 expected: String::from(
640 "the <count> component for `plugin-gid-map` is not valid number",
641 ),
642 })?,
643 };
644
645 Ok(GidMap {
646 inner,
647 outer,
648 count,
649 })
650 }
651
parse_battery_options(s: Option<&str>) -> argument::Result<BatteryType>652 fn parse_battery_options(s: Option<&str>) -> argument::Result<BatteryType> {
653 let mut battery_type: BatteryType = Default::default();
654
655 if let Some(s) = s {
656 let opts = s
657 .split(',')
658 .map(|frag| frag.split('='))
659 .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
660
661 for (k, v) in opts {
662 match k {
663 "type" => match v.parse::<BatteryType>() {
664 Ok(type_) => battery_type = type_,
665 Err(e) => {
666 return Err(argument::Error::InvalidValue {
667 value: v.to_string(),
668 expected: e.to_string(),
669 });
670 }
671 },
672 "" => {}
673 _ => {
674 return Err(argument::Error::UnknownArgument(format!(
675 "battery parameter {}",
676 k
677 )));
678 }
679 }
680 }
681 }
682
683 Ok(battery_type)
684 }
685
686 #[cfg(feature = "direct")]
parse_direct_io_options(s: Option<&str>) -> argument::Result<DirectIoOption>687 fn parse_direct_io_options(s: Option<&str>) -> argument::Result<DirectIoOption> {
688 let s = s.ok_or(argument::Error::ExpectedValue(String::from(
689 "expected path@range[,range] value",
690 )))?;
691 let parts: Vec<&str> = s.splitn(2, '@').collect();
692 if parts.len() != 2 {
693 return Err(argument::Error::InvalidValue {
694 value: s.to_string(),
695 expected: String::from("missing port range, use /path@X-Y,Z,.. syntax"),
696 });
697 }
698 let path = PathBuf::from(parts[0]);
699 if !path.exists() {
700 return Err(argument::Error::InvalidValue {
701 value: parts[0].to_owned(),
702 expected: String::from("the path does not exist"),
703 });
704 };
705 let ranges: argument::Result<Vec<(u64, u64)>> = parts[1]
706 .split(',')
707 .map(|frag| frag.split('-'))
708 .map(|mut range| {
709 let base = range
710 .next()
711 .map(|v| v.parse::<u64>())
712 .map_or(Ok(None), |r| r.map(Some));
713 let last = range
714 .next()
715 .map(|v| v.parse::<u64>())
716 .map_or(Ok(None), |r| r.map(Some));
717 (base, last)
718 })
719 .map(|range| match range {
720 (Ok(Some(base)), Ok(None)) => Ok((base, 1)),
721 (Ok(Some(base)), Ok(Some(last))) => {
722 Ok((base, last.saturating_sub(base).saturating_add(1)))
723 }
724 (Err(e), _) => Err(argument::Error::InvalidValue {
725 value: e.to_string(),
726 expected: String::from("invalid base range value"),
727 }),
728 (_, Err(e)) => Err(argument::Error::InvalidValue {
729 value: e.to_string(),
730 expected: String::from("invalid last range value"),
731 }),
732 _ => Err(argument::Error::InvalidValue {
733 value: s.to_owned(),
734 expected: String::from("invalid range format"),
735 }),
736 })
737 .collect();
738 Ok(DirectIoOption {
739 path,
740 ranges: ranges?,
741 })
742 }
743
set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::Result<()>744 fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::Result<()> {
745 match name {
746 "" => {
747 if cfg.executable_path.is_some() {
748 return Err(argument::Error::TooManyArguments(format!(
749 "A VM executable was already specified: {:?}",
750 cfg.executable_path
751 )));
752 }
753 let kernel_path = PathBuf::from(value.unwrap());
754 if !kernel_path.exists() {
755 return Err(argument::Error::InvalidValue {
756 value: value.unwrap().to_owned(),
757 expected: String::from("this kernel path does not exist"),
758 });
759 }
760 cfg.executable_path = Some(Executable::Kernel(kernel_path));
761 }
762 "kvm-device" => {
763 let kvm_device_path = PathBuf::from(value.unwrap());
764 if !kvm_device_path.exists() {
765 return Err(argument::Error::InvalidValue {
766 value: value.unwrap().to_owned(),
767 expected: String::from("this kvm device path does not exist"),
768 });
769 }
770
771 cfg.kvm_device_path = kvm_device_path;
772 }
773 "vhost-vsock-device" => {
774 let vhost_vsock_device_path = PathBuf::from(value.unwrap());
775 if !vhost_vsock_device_path.exists() {
776 return Err(argument::Error::InvalidValue {
777 value: value.unwrap().to_owned(),
778 expected: String::from("this vhost-vsock device path does not exist"),
779 });
780 }
781
782 cfg.vhost_vsock_device_path = vhost_vsock_device_path;
783 }
784 "vhost-net-device" => {
785 let vhost_net_device_path = PathBuf::from(value.unwrap());
786 if !vhost_net_device_path.exists() {
787 return Err(argument::Error::InvalidValue {
788 value: value.unwrap().to_owned(),
789 expected: String::from("this vhost-vsock device path does not exist"),
790 });
791 }
792
793 cfg.vhost_net_device_path = vhost_net_device_path;
794 }
795 "android-fstab" => {
796 if cfg.android_fstab.is_some()
797 && !cfg.android_fstab.as_ref().unwrap().as_os_str().is_empty()
798 {
799 return Err(argument::Error::TooManyArguments(
800 "expected exactly one android fstab path".to_owned(),
801 ));
802 } else {
803 let android_fstab = PathBuf::from(value.unwrap());
804 if !android_fstab.exists() {
805 return Err(argument::Error::InvalidValue {
806 value: value.unwrap().to_owned(),
807 expected: String::from("this android fstab path does not exist"),
808 });
809 }
810 cfg.android_fstab = Some(android_fstab);
811 }
812 }
813 "params" => {
814 cfg.params.push(value.unwrap().to_owned());
815 }
816 "cpus" => {
817 if cfg.vcpu_count.is_some() {
818 return Err(argument::Error::TooManyArguments(
819 "`cpus` already given".to_owned(),
820 ));
821 }
822 cfg.vcpu_count =
823 Some(
824 value
825 .unwrap()
826 .parse()
827 .map_err(|_| argument::Error::InvalidValue {
828 value: value.unwrap().to_owned(),
829 expected: String::from("this value for `cpus` needs to be integer"),
830 })?,
831 )
832 }
833 "cpu-affinity" => {
834 if cfg.vcpu_affinity.is_some() {
835 return Err(argument::Error::TooManyArguments(
836 "`cpu-affinity` already given".to_owned(),
837 ));
838 }
839 cfg.vcpu_affinity = Some(parse_cpu_affinity(value.unwrap())?);
840 }
841 "no-smt" => {
842 cfg.no_smt = true;
843 }
844 "rt-cpus" => {
845 if !cfg.rt_cpus.is_empty() {
846 return Err(argument::Error::TooManyArguments(
847 "`rt-cpus` already given".to_owned(),
848 ));
849 }
850 cfg.rt_cpus = parse_cpu_set(value.unwrap())?;
851 }
852 "mem" => {
853 if cfg.memory.is_some() {
854 return Err(argument::Error::TooManyArguments(
855 "`mem` already given".to_owned(),
856 ));
857 }
858 cfg.memory =
859 Some(
860 value
861 .unwrap()
862 .parse()
863 .map_err(|_| argument::Error::InvalidValue {
864 value: value.unwrap().to_owned(),
865 expected: String::from("this value for `mem` needs to be integer"),
866 })?,
867 )
868 }
869 "hugepages" => {
870 cfg.hugepages = true;
871 }
872 #[cfg(feature = "audio")]
873 "ac97" => {
874 let ac97_params = parse_ac97_options(value.unwrap())?;
875 // Add kernel parameters related to the intel8x0 driver for ac97 devices once.
876 if cfg.ac97_parameters.is_empty() {
877 // Set `inside_vm=1` to save some register read ops in the driver.
878 cfg.params.push("snd_intel8x0.inside_vm=1".to_string());
879 // Set `ac97_clock=48000` to save intel8x0_measure_ac97_clock call in the driver.
880 cfg.params.push("snd_intel8x0.ac97_clock=48000".to_string());
881 }
882 cfg.ac97_parameters.push(ac97_params);
883 }
884 "serial" => {
885 let serial_params = parse_serial_options(value.unwrap())?;
886 let num = serial_params.num;
887 let key = (serial_params.hardware, num);
888 if cfg.serial_parameters.contains_key(&key) {
889 return Err(argument::Error::TooManyArguments(format!(
890 "serial hardware {} num {}",
891 serial_params.hardware, num,
892 )));
893 }
894
895 if serial_params.console {
896 for params in cfg.serial_parameters.values() {
897 if params.console {
898 return Err(argument::Error::TooManyArguments(format!(
899 "{} device {} already set as console",
900 params.hardware, params.num,
901 )));
902 }
903 }
904 }
905
906 if serial_params.earlycon {
907 // Only SerialHardware::Serial supports earlycon= currently.
908 match serial_params.hardware {
909 SerialHardware::Serial => {}
910 _ => {
911 return Err(argument::Error::InvalidValue {
912 value: serial_params.hardware.to_string(),
913 expected: String::from("earlycon not supported for hardware"),
914 });
915 }
916 }
917 for params in cfg.serial_parameters.values() {
918 if params.earlycon {
919 return Err(argument::Error::TooManyArguments(format!(
920 "{} device {} already set as earlycon",
921 params.hardware, params.num,
922 )));
923 }
924 }
925 }
926
927 if serial_params.stdin {
928 if let Some(previous_stdin) = cfg.serial_parameters.values().find(|sp| sp.stdin) {
929 return Err(argument::Error::TooManyArguments(format!(
930 "{} device {} already connected to standard input",
931 previous_stdin.hardware, previous_stdin.num,
932 )));
933 }
934 }
935
936 cfg.serial_parameters.insert(key, serial_params);
937 }
938 "syslog-tag" => {
939 if cfg.syslog_tag.is_some() {
940 return Err(argument::Error::TooManyArguments(
941 "`syslog-tag` already given".to_owned(),
942 ));
943 }
944 syslog::set_proc_name(value.unwrap());
945 cfg.syslog_tag = Some(value.unwrap().to_owned());
946 }
947 "root" | "rwroot" | "disk" | "rwdisk" => {
948 let param = value.unwrap();
949 let mut components = param.split(',');
950 let read_only = !name.starts_with("rw");
951 let disk_path =
952 PathBuf::from(
953 components
954 .next()
955 .ok_or_else(|| argument::Error::InvalidValue {
956 value: param.to_owned(),
957 expected: String::from("missing disk path"),
958 })?,
959 );
960 if !disk_path.exists() {
961 return Err(argument::Error::InvalidValue {
962 value: param.to_owned(),
963 expected: String::from("this disk path does not exist"),
964 });
965 }
966 if name.ends_with("root") {
967 if cfg.disks.len() >= 26 {
968 return Err(argument::Error::TooManyArguments(
969 "ran out of letters for to assign to root disk".to_owned(),
970 ));
971 }
972 cfg.params.push(format!(
973 "root=/dev/vd{} {}",
974 char::from(b'a' + cfg.disks.len() as u8),
975 if read_only { "ro" } else { "rw" }
976 ));
977 }
978
979 let mut disk = DiskOption {
980 path: disk_path,
981 read_only,
982 sparse: true,
983 block_size: 512,
984 id: None,
985 };
986
987 for opt in components {
988 let mut o = opt.splitn(2, '=');
989 let kind = o.next().ok_or_else(|| argument::Error::InvalidValue {
990 value: opt.to_owned(),
991 expected: String::from("disk options must not be empty"),
992 })?;
993 let value = o.next().ok_or_else(|| argument::Error::InvalidValue {
994 value: opt.to_owned(),
995 expected: String::from("disk options must be of the form `kind=value`"),
996 })?;
997
998 match kind {
999 "sparse" => {
1000 let sparse = value.parse().map_err(|_| argument::Error::InvalidValue {
1001 value: value.to_owned(),
1002 expected: String::from("`sparse` must be a boolean"),
1003 })?;
1004 disk.sparse = sparse;
1005 }
1006 "block_size" => {
1007 let block_size =
1008 value.parse().map_err(|_| argument::Error::InvalidValue {
1009 value: value.to_owned(),
1010 expected: String::from("`block_size` must be an integer"),
1011 })?;
1012 disk.block_size = block_size;
1013 }
1014 "id" => {
1015 if value.len() > DISK_ID_LEN {
1016 return Err(argument::Error::InvalidValue {
1017 value: value.to_owned(),
1018 expected: format!(
1019 "`id` must be {} or fewer characters",
1020 DISK_ID_LEN
1021 ),
1022 });
1023 }
1024 let mut id = [0u8; DISK_ID_LEN];
1025 // Slicing id to value's length will never panic
1026 // because we checked that value will fit into id above.
1027 id[..value.len()].copy_from_slice(value.as_bytes());
1028 disk.id = Some(id);
1029 }
1030 _ => {
1031 return Err(argument::Error::InvalidValue {
1032 value: kind.to_owned(),
1033 expected: String::from("unrecognized disk option"),
1034 });
1035 }
1036 }
1037 }
1038
1039 cfg.disks.push(disk);
1040 }
1041 "pmem-device" | "rw-pmem-device" => {
1042 let disk_path = PathBuf::from(value.unwrap());
1043 if !disk_path.exists() {
1044 return Err(argument::Error::InvalidValue {
1045 value: value.unwrap().to_owned(),
1046 expected: String::from("this disk path does not exist"),
1047 });
1048 }
1049
1050 cfg.pmem_devices.push(DiskOption {
1051 path: disk_path,
1052 read_only: !name.starts_with("rw"),
1053 sparse: false,
1054 block_size: base::pagesize() as u32,
1055 id: None,
1056 });
1057 }
1058 "pstore" => {
1059 if cfg.pstore.is_some() {
1060 return Err(argument::Error::TooManyArguments(
1061 "`pstore` already given".to_owned(),
1062 ));
1063 }
1064
1065 let value = value.unwrap();
1066 let components: Vec<&str> = value.split(',').collect();
1067 if components.len() != 2 {
1068 return Err(argument::Error::InvalidValue {
1069 value: value.to_owned(),
1070 expected: String::from(
1071 "pstore must have exactly 2 components: path=<path>,size=<size>",
1072 ),
1073 });
1074 }
1075 cfg.pstore = Some(Pstore {
1076 path: {
1077 if components[0].len() <= 5 || !components[0].starts_with("path=") {
1078 return Err(argument::Error::InvalidValue {
1079 value: components[0].to_owned(),
1080 expected: String::from("pstore path must follow with `path=`"),
1081 });
1082 };
1083 PathBuf::from(&components[0][5..])
1084 },
1085 size: {
1086 if components[1].len() <= 5 || !components[1].starts_with("size=") {
1087 return Err(argument::Error::InvalidValue {
1088 value: components[1].to_owned(),
1089 expected: String::from("pstore size must follow with `size=`"),
1090 });
1091 };
1092 components[1][5..]
1093 .parse()
1094 .map_err(|_| argument::Error::InvalidValue {
1095 value: value.to_owned(),
1096 expected: String::from("pstore size must be an integer"),
1097 })?
1098 },
1099 });
1100 }
1101 "host_ip" => {
1102 if cfg.host_ip.is_some() {
1103 return Err(argument::Error::TooManyArguments(
1104 "`host_ip` already given".to_owned(),
1105 ));
1106 }
1107 cfg.host_ip =
1108 Some(
1109 value
1110 .unwrap()
1111 .parse()
1112 .map_err(|_| argument::Error::InvalidValue {
1113 value: value.unwrap().to_owned(),
1114 expected: String::from("`host_ip` needs to be in the form \"x.x.x.x\""),
1115 })?,
1116 )
1117 }
1118 "netmask" => {
1119 if cfg.netmask.is_some() {
1120 return Err(argument::Error::TooManyArguments(
1121 "`netmask` already given".to_owned(),
1122 ));
1123 }
1124 cfg.netmask =
1125 Some(
1126 value
1127 .unwrap()
1128 .parse()
1129 .map_err(|_| argument::Error::InvalidValue {
1130 value: value.unwrap().to_owned(),
1131 expected: String::from("`netmask` needs to be in the form \"x.x.x.x\""),
1132 })?,
1133 )
1134 }
1135 "mac" => {
1136 if cfg.mac_address.is_some() {
1137 return Err(argument::Error::TooManyArguments(
1138 "`mac` already given".to_owned(),
1139 ));
1140 }
1141 cfg.mac_address =
1142 Some(
1143 value
1144 .unwrap()
1145 .parse()
1146 .map_err(|_| argument::Error::InvalidValue {
1147 value: value.unwrap().to_owned(),
1148 expected: String::from(
1149 "`mac` needs to be in the form \"XX:XX:XX:XX:XX:XX\"",
1150 ),
1151 })?,
1152 )
1153 }
1154 "net-vq-pairs" => {
1155 if cfg.net_vq_pairs.is_some() {
1156 return Err(argument::Error::TooManyArguments(
1157 "`net-vq-pairs` already given".to_owned(),
1158 ));
1159 }
1160 cfg.net_vq_pairs =
1161 Some(
1162 value
1163 .unwrap()
1164 .parse()
1165 .map_err(|_| argument::Error::InvalidValue {
1166 value: value.unwrap().to_owned(),
1167 expected: String::from(
1168 "this value for `net-vq-pairs` needs to be integer",
1169 ),
1170 })?,
1171 )
1172 }
1173
1174 "wayland-sock" => {
1175 let mut components = value.unwrap().split(',');
1176 let path =
1177 PathBuf::from(
1178 components
1179 .next()
1180 .ok_or_else(|| argument::Error::InvalidValue {
1181 value: value.unwrap().to_owned(),
1182 expected: String::from("missing socket path"),
1183 })?,
1184 );
1185 let mut name = "";
1186 for c in components {
1187 let mut kv = c.splitn(2, '=');
1188 let (kind, value) = match (kv.next(), kv.next()) {
1189 (Some(kind), Some(value)) => (kind, value),
1190 _ => {
1191 return Err(argument::Error::InvalidValue {
1192 value: c.to_owned(),
1193 expected: String::from("option must be of the form `kind=value`"),
1194 })
1195 }
1196 };
1197 match kind {
1198 "name" => name = value,
1199 _ => {
1200 return Err(argument::Error::InvalidValue {
1201 value: kind.to_owned(),
1202 expected: String::from("unrecognized option"),
1203 })
1204 }
1205 }
1206 }
1207 if cfg.wayland_socket_paths.contains_key(name) {
1208 return Err(argument::Error::TooManyArguments(format!(
1209 "wayland socket name already used: '{}'",
1210 name
1211 )));
1212 }
1213 cfg.wayland_socket_paths.insert(name.to_string(), path);
1214 }
1215 #[cfg(feature = "wl-dmabuf")]
1216 "wayland-dmabuf" => cfg.wayland_dmabuf = true,
1217 "x-display" => {
1218 if cfg.x_display.is_some() {
1219 return Err(argument::Error::TooManyArguments(
1220 "`x-display` already given".to_owned(),
1221 ));
1222 }
1223 cfg.x_display = Some(value.unwrap().to_owned());
1224 }
1225 "display-window-keyboard" => {
1226 cfg.display_window_keyboard = true;
1227 }
1228 "display-window-mouse" => {
1229 cfg.display_window_mouse = true;
1230 }
1231 "socket" => {
1232 if cfg.socket_path.is_some() {
1233 return Err(argument::Error::TooManyArguments(
1234 "`socket` already given".to_owned(),
1235 ));
1236 }
1237 let mut socket_path = PathBuf::from(value.unwrap());
1238 if socket_path.is_dir() {
1239 socket_path.push(format!("crosvm-{}.sock", getpid()));
1240 }
1241 if socket_path.exists() {
1242 return Err(argument::Error::InvalidValue {
1243 value: socket_path.to_string_lossy().into_owned(),
1244 expected: String::from("this socket path already exists"),
1245 });
1246 }
1247 cfg.socket_path = Some(socket_path);
1248 }
1249 "disable-sandbox" => {
1250 cfg.sandbox = false;
1251 }
1252 "cid" => {
1253 if cfg.cid.is_some() {
1254 return Err(argument::Error::TooManyArguments(
1255 "`cid` alread given".to_owned(),
1256 ));
1257 }
1258 cfg.cid = Some(
1259 value
1260 .unwrap()
1261 .parse()
1262 .map_err(|_| argument::Error::InvalidValue {
1263 value: value.unwrap().to_owned(),
1264 expected: String::from("this value for `cid` must be an unsigned integer"),
1265 })?,
1266 );
1267 }
1268 "shared-dir" => {
1269 // This is formatted as multiple fields, each separated by ":". The first 2 fields are
1270 // fixed (src:tag). The rest may appear in any order:
1271 //
1272 // * type=TYPE - must be one of "p9" or "fs" (default: p9)
1273 // * uidmap=UIDMAP - a uid map in the format "inner outer count[,inner outer count]"
1274 // (default: "0 <current euid> 1")
1275 // * gidmap=GIDMAP - a gid map in the same format as uidmap
1276 // (default: "0 <current egid> 1")
1277 // * timeout=TIMEOUT - a timeout value in seconds, which indicates how long attributes
1278 // and directory contents should be considered valid (default: 5)
1279 // * cache=CACHE - one of "never", "always", or "auto" (default: auto)
1280 // * writeback=BOOL - indicates whether writeback caching should be enabled (default: false)
1281 let param = value.unwrap();
1282 let mut components = param.split(':');
1283 let src =
1284 PathBuf::from(
1285 components
1286 .next()
1287 .ok_or_else(|| argument::Error::InvalidValue {
1288 value: param.to_owned(),
1289 expected: String::from("missing source path for `shared-dir`"),
1290 })?,
1291 );
1292 let tag = components
1293 .next()
1294 .ok_or_else(|| argument::Error::InvalidValue {
1295 value: param.to_owned(),
1296 expected: String::from("missing tag for `shared-dir`"),
1297 })?
1298 .to_owned();
1299
1300 if !src.is_dir() {
1301 return Err(argument::Error::InvalidValue {
1302 value: param.to_owned(),
1303 expected: String::from("source path for `shared-dir` must be a directory"),
1304 });
1305 }
1306
1307 let mut shared_dir = SharedDir {
1308 src,
1309 tag,
1310 ..Default::default()
1311 };
1312 for opt in components {
1313 let mut o = opt.splitn(2, '=');
1314 let kind = o.next().ok_or_else(|| argument::Error::InvalidValue {
1315 value: opt.to_owned(),
1316 expected: String::from("`shared-dir` options must not be empty"),
1317 })?;
1318 let value = o.next().ok_or_else(|| argument::Error::InvalidValue {
1319 value: opt.to_owned(),
1320 expected: String::from("`shared-dir` options must be of the form `kind=value`"),
1321 })?;
1322
1323 match kind {
1324 "type" => {
1325 shared_dir.kind =
1326 value.parse().map_err(|_| argument::Error::InvalidValue {
1327 value: value.to_owned(),
1328 expected: String::from("`type` must be one of `fs` or `9p`"),
1329 })?
1330 }
1331 "uidmap" => shared_dir.uid_map = value.into(),
1332 "gidmap" => shared_dir.gid_map = value.into(),
1333 "timeout" => {
1334 let seconds = value.parse().map_err(|_| argument::Error::InvalidValue {
1335 value: value.to_owned(),
1336 expected: String::from("`timeout` must be an integer"),
1337 })?;
1338
1339 let dur = Duration::from_secs(seconds);
1340 shared_dir.fs_cfg.entry_timeout = dur;
1341 shared_dir.fs_cfg.attr_timeout = dur;
1342 }
1343 "cache" => {
1344 let policy = value.parse().map_err(|_| argument::Error::InvalidValue {
1345 value: value.to_owned(),
1346 expected: String::from(
1347 "`cache` must be one of `never`, `always`, or `auto`",
1348 ),
1349 })?;
1350 shared_dir.fs_cfg.cache_policy = policy;
1351 }
1352 "writeback" => {
1353 let writeback =
1354 value.parse().map_err(|_| argument::Error::InvalidValue {
1355 value: value.to_owned(),
1356 expected: String::from("`writeback` must be a boolean"),
1357 })?;
1358 shared_dir.fs_cfg.writeback = writeback;
1359 }
1360 "rewrite-security-xattrs" => {
1361 let rewrite_security_xattrs =
1362 value.parse().map_err(|_| argument::Error::InvalidValue {
1363 value: value.to_owned(),
1364 expected: String::from(
1365 "`rewrite-security-xattrs` must be a boolean",
1366 ),
1367 })?;
1368 shared_dir.fs_cfg.rewrite_security_xattrs = rewrite_security_xattrs;
1369 }
1370 "ascii_casefold" => {
1371 let ascii_casefold =
1372 value.parse().map_err(|_| argument::Error::InvalidValue {
1373 value: value.to_owned(),
1374 expected: String::from("`ascii_casefold` must be a boolean"),
1375 })?;
1376 shared_dir.fs_cfg.ascii_casefold = ascii_casefold;
1377 shared_dir.p9_cfg.ascii_casefold = ascii_casefold;
1378 }
1379 _ => {
1380 return Err(argument::Error::InvalidValue {
1381 value: kind.to_owned(),
1382 expected: String::from("unrecognized option for `shared-dir`"),
1383 })
1384 }
1385 }
1386 }
1387 cfg.shared_dirs.push(shared_dir);
1388 }
1389 "seccomp-policy-dir" => {
1390 // `value` is Some because we are in this match so it's safe to unwrap.
1391 cfg.seccomp_policy_dir = PathBuf::from(value.unwrap());
1392 }
1393 "seccomp-log-failures" => {
1394 // A side-effect of this flag is to force the use of .policy files
1395 // instead of .bpf files (.bpf files are expected and assumed to be
1396 // compiled to fail an unpermitted action with "trap").
1397 // Normally crosvm will first attempt to use a .bpf file, and if
1398 // not present it will then try to use a .policy file. It's up
1399 // to the build to decide which of these files is present for
1400 // crosvm to use (for CrOS the build will use .bpf files for
1401 // x64 builds and .policy files for arm/arm64 builds).
1402 //
1403 // This flag will likely work as expected for builds that use
1404 // .policy files. For builds that only use .bpf files the initial
1405 // result when using this flag is likely to be a file-not-found
1406 // error (since the .policy files are not present).
1407 // For .bpf builds you can either 1) manually add the .policy files,
1408 // or 2) do not use this command-line parameter and instead
1409 // temporarily change the build by passing "log" rather than
1410 // "trap" as the "--default-action" to compile_seccomp_policy.py.
1411 cfg.seccomp_log_failures = true;
1412 }
1413 "plugin" => {
1414 if cfg.executable_path.is_some() {
1415 return Err(argument::Error::TooManyArguments(format!(
1416 "A VM executable was already specified: {:?}",
1417 cfg.executable_path
1418 )));
1419 }
1420 let plugin = PathBuf::from(value.unwrap().to_owned());
1421 if plugin.is_relative() {
1422 return Err(argument::Error::InvalidValue {
1423 value: plugin.to_string_lossy().into_owned(),
1424 expected: String::from("the plugin path must be an absolute path"),
1425 });
1426 }
1427 cfg.executable_path = Some(Executable::Plugin(plugin));
1428 }
1429 "plugin-root" => {
1430 cfg.plugin_root = Some(PathBuf::from(value.unwrap().to_owned()));
1431 }
1432 "plugin-mount" => {
1433 let mount = parse_plugin_mount_option(value.unwrap())?;
1434 cfg.plugin_mounts.push(mount);
1435 }
1436 "plugin-mount-file" => {
1437 let file = File::open(value.unwrap()).map_err(|_| argument::Error::InvalidValue {
1438 value: value.unwrap().to_owned(),
1439 expected: String::from("unable to open `plugin-mount-file` file"),
1440 })?;
1441 let reader = BufReader::new(file);
1442 for l in reader.lines() {
1443 let line = l.unwrap();
1444 let trimmed_line = line.splitn(2, '#').next().unwrap().trim();
1445 if !trimmed_line.is_empty() {
1446 let mount = parse_plugin_mount_option(trimmed_line)?;
1447 cfg.plugin_mounts.push(mount);
1448 }
1449 }
1450 }
1451 "plugin-gid-map" => {
1452 let map = parse_plugin_gid_map_option(value.unwrap())?;
1453 cfg.plugin_gid_maps.push(map);
1454 }
1455 "plugin-gid-map-file" => {
1456 let file = File::open(value.unwrap()).map_err(|_| argument::Error::InvalidValue {
1457 value: value.unwrap().to_owned(),
1458 expected: String::from("unable to open `plugin-gid-map-file` file"),
1459 })?;
1460 let reader = BufReader::new(file);
1461 for l in reader.lines() {
1462 let line = l.unwrap();
1463 let trimmed_line = line.splitn(2, '#').next().unwrap().trim();
1464 if !trimmed_line.is_empty() {
1465 let map = parse_plugin_gid_map_option(trimmed_line)?;
1466 cfg.plugin_gid_maps.push(map);
1467 }
1468 }
1469 }
1470 "vhost-net" => cfg.vhost_net = true,
1471 "tap-fd" => {
1472 cfg.tap_fd.push(
1473 value
1474 .unwrap()
1475 .parse()
1476 .map_err(|_| argument::Error::InvalidValue {
1477 value: value.unwrap().to_owned(),
1478 expected: String::from(
1479 "this value for `tap-fd` must be an unsigned integer",
1480 ),
1481 })?,
1482 );
1483 }
1484 #[cfg(feature = "gpu")]
1485 "gpu" => {
1486 let params = parse_gpu_options(value)?;
1487 cfg.gpu_parameters = Some(params);
1488 }
1489 "software-tpm" => {
1490 cfg.software_tpm = true;
1491 }
1492 "single-touch" => {
1493 if cfg.virtio_single_touch.is_some() {
1494 return Err(argument::Error::TooManyArguments(
1495 "`single-touch` already given".to_owned(),
1496 ));
1497 }
1498 let mut it = value.unwrap().split(':');
1499
1500 let mut single_touch_spec =
1501 TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned()));
1502 if let Some(width) = it.next() {
1503 single_touch_spec.set_width(width.trim().parse().unwrap());
1504 }
1505 if let Some(height) = it.next() {
1506 single_touch_spec.set_height(height.trim().parse().unwrap());
1507 }
1508 cfg.virtio_single_touch = Some(single_touch_spec);
1509 }
1510 "multi-touch" => {
1511 if cfg.virtio_multi_touch.is_some() {
1512 return Err(argument::Error::TooManyArguments(
1513 "`multi-touch` already given".to_owned(),
1514 ));
1515 }
1516 let mut it = value.unwrap().split(':');
1517
1518 let mut multi_touch_spec =
1519 TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned()));
1520 if let Some(width) = it.next() {
1521 multi_touch_spec.set_width(width.trim().parse().unwrap());
1522 }
1523 if let Some(height) = it.next() {
1524 multi_touch_spec.set_height(height.trim().parse().unwrap());
1525 }
1526 cfg.virtio_multi_touch = Some(multi_touch_spec);
1527 }
1528 "trackpad" => {
1529 if cfg.virtio_trackpad.is_some() {
1530 return Err(argument::Error::TooManyArguments(
1531 "`trackpad` already given".to_owned(),
1532 ));
1533 }
1534 let mut it = value.unwrap().split(':');
1535
1536 let mut trackpad_spec =
1537 TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned()));
1538 if let Some(width) = it.next() {
1539 trackpad_spec.set_width(width.trim().parse().unwrap());
1540 }
1541 if let Some(height) = it.next() {
1542 trackpad_spec.set_height(height.trim().parse().unwrap());
1543 }
1544 cfg.virtio_trackpad = Some(trackpad_spec);
1545 }
1546 "mouse" => {
1547 if cfg.virtio_mouse.is_some() {
1548 return Err(argument::Error::TooManyArguments(
1549 "`mouse` already given".to_owned(),
1550 ));
1551 }
1552 cfg.virtio_mouse = Some(PathBuf::from(value.unwrap().to_owned()));
1553 }
1554 "keyboard" => {
1555 if cfg.virtio_keyboard.is_some() {
1556 return Err(argument::Error::TooManyArguments(
1557 "`keyboard` already given".to_owned(),
1558 ));
1559 }
1560 cfg.virtio_keyboard = Some(PathBuf::from(value.unwrap().to_owned()));
1561 }
1562 "switches" => {
1563 if cfg.virtio_switches.is_some() {
1564 return Err(argument::Error::TooManyArguments(
1565 "`switches` already given".to_owned(),
1566 ));
1567 }
1568 cfg.virtio_switches = Some(PathBuf::from(value.unwrap().to_owned()));
1569 }
1570 "evdev" => {
1571 let dev_path = PathBuf::from(value.unwrap());
1572 if !dev_path.exists() {
1573 return Err(argument::Error::InvalidValue {
1574 value: value.unwrap().to_owned(),
1575 expected: String::from("this input device path does not exist"),
1576 });
1577 }
1578 cfg.virtio_input_evdevs.push(dev_path);
1579 }
1580 "split-irqchip" => {
1581 cfg.split_irqchip = true;
1582 }
1583 "initrd" => {
1584 cfg.initrd_path = Some(PathBuf::from(value.unwrap().to_owned()));
1585 }
1586 "bios" => {
1587 if cfg.executable_path.is_some() {
1588 return Err(argument::Error::TooManyArguments(format!(
1589 "A VM executable was already specified: {:?}",
1590 cfg.executable_path
1591 )));
1592 }
1593 cfg.executable_path = Some(Executable::Bios(PathBuf::from(value.unwrap().to_owned())));
1594 }
1595 "vfio" => {
1596 let vfio_path = PathBuf::from(value.unwrap());
1597 if !vfio_path.exists() {
1598 return Err(argument::Error::InvalidValue {
1599 value: value.unwrap().to_owned(),
1600 expected: String::from("the vfio path does not exist"),
1601 });
1602 }
1603 if !vfio_path.is_dir() {
1604 return Err(argument::Error::InvalidValue {
1605 value: value.unwrap().to_owned(),
1606 expected: String::from("the vfio path should be directory"),
1607 });
1608 }
1609
1610 cfg.vfio.push(vfio_path);
1611 }
1612 "video-decoder" => {
1613 cfg.video_dec = true;
1614 }
1615 "video-encoder" => {
1616 cfg.video_enc = true;
1617 }
1618 "acpi-table" => {
1619 let acpi_table = PathBuf::from(value.unwrap());
1620 if !acpi_table.exists() {
1621 return Err(argument::Error::InvalidValue {
1622 value: value.unwrap().to_owned(),
1623 expected: String::from("the acpi-table path does not exist"),
1624 });
1625 }
1626 if !acpi_table.is_file() {
1627 return Err(argument::Error::InvalidValue {
1628 value: value.unwrap().to_owned(),
1629 expected: String::from("the acpi-table path should be a file"),
1630 });
1631 }
1632
1633 cfg.acpi_tables.push(acpi_table);
1634 }
1635 "protected-vm" => {
1636 cfg.protected_vm = ProtectionType::Protected;
1637 cfg.params.push("swiotlb=force".to_string());
1638 }
1639 "battery" => {
1640 let params = parse_battery_options(value)?;
1641 cfg.battery_type = Some(params);
1642 }
1643 #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
1644 "gdb" => {
1645 let port = value
1646 .unwrap()
1647 .parse()
1648 .map_err(|_| argument::Error::InvalidValue {
1649 value: value.unwrap().to_owned(),
1650 expected: String::from("expected a valid port number"),
1651 })?;
1652 cfg.gdb = Some(port);
1653 }
1654 "balloon_bias_mib" => {
1655 cfg.balloon_bias =
1656 value
1657 .unwrap()
1658 .parse::<i64>()
1659 .map_err(|_| argument::Error::InvalidValue {
1660 value: value.unwrap().to_owned(),
1661 expected: String::from("expected a valid ballon bias in MiB"),
1662 })?
1663 * 1024
1664 * 1024; // cfg.balloon_bias is in bytes.
1665 }
1666 "vhost-user-blk" => cfg.vhost_user_blk.push(VhostUserOption {
1667 socket: PathBuf::from(value.unwrap()),
1668 }),
1669 "vhost-user-net" => cfg.vhost_user_net.push(VhostUserOption {
1670 socket: PathBuf::from(value.unwrap()),
1671 }),
1672 "vhost-user-fs" => {
1673 // (socket:tag)
1674 let param = value.unwrap();
1675 let mut components = param.split(':');
1676 let socket =
1677 PathBuf::from(
1678 components
1679 .next()
1680 .ok_or_else(|| argument::Error::InvalidValue {
1681 value: param.to_owned(),
1682 expected: String::from("missing socket path for `vhost-user-fs`"),
1683 })?,
1684 );
1685 let tag = components
1686 .next()
1687 .ok_or_else(|| argument::Error::InvalidValue {
1688 value: param.to_owned(),
1689 expected: String::from("missing tag for `vhost-user-fs`"),
1690 })?
1691 .to_owned();
1692 cfg.vhost_user_fs.push(VhostUserFsOption { socket, tag });
1693 }
1694 #[cfg(feature = "direct")]
1695 "direct-pmio" => {
1696 if cfg.direct_pmio.is_some() {
1697 return Err(argument::Error::TooManyArguments(
1698 "`direct_pmio` already given".to_owned(),
1699 ));
1700 }
1701 cfg.direct_pmio = Some(parse_direct_io_options(value)?);
1702 }
1703 #[cfg(feature = "direct")]
1704 "direct-level-irq" => {
1705 cfg.direct_level_irq
1706 .push(
1707 value
1708 .unwrap()
1709 .parse()
1710 .map_err(|_| argument::Error::InvalidValue {
1711 value: value.unwrap().to_owned(),
1712 expected: String::from(
1713 "this value for `direct-level-irq` must be an unsigned integer",
1714 ),
1715 })?,
1716 );
1717 }
1718 #[cfg(feature = "direct")]
1719 "direct-edge-irq" => {
1720 cfg.direct_edge_irq
1721 .push(
1722 value
1723 .unwrap()
1724 .parse()
1725 .map_err(|_| argument::Error::InvalidValue {
1726 value: value.unwrap().to_owned(),
1727 expected: String::from(
1728 "this value for `direct-edge-irq` must be an unsigned integer",
1729 ),
1730 })?,
1731 );
1732 }
1733 "dmi" => {
1734 if cfg.dmi_path.is_some() {
1735 return Err(argument::Error::TooManyArguments(
1736 "`dmi` already given".to_owned(),
1737 ));
1738 }
1739 let dmi_path = PathBuf::from(value.unwrap());
1740 if !dmi_path.exists() {
1741 return Err(argument::Error::InvalidValue {
1742 value: value.unwrap().to_owned(),
1743 expected: String::from("the dmi path does not exist"),
1744 });
1745 }
1746 if !dmi_path.is_dir() {
1747 return Err(argument::Error::InvalidValue {
1748 value: value.unwrap().to_owned(),
1749 expected: String::from("the dmi path should be directory"),
1750 });
1751 }
1752 cfg.dmi_path = Some(dmi_path);
1753 }
1754 "help" => return Err(argument::Error::PrintHelp),
1755 _ => unreachable!(),
1756 }
1757 Ok(())
1758 }
1759
validate_arguments(cfg: &mut Config) -> std::result::Result<(), argument::Error>1760 fn validate_arguments(cfg: &mut Config) -> std::result::Result<(), argument::Error> {
1761 if cfg.executable_path.is_none() {
1762 return Err(argument::Error::ExpectedArgument("`KERNEL`".to_owned()));
1763 }
1764 if cfg.host_ip.is_some() || cfg.netmask.is_some() || cfg.mac_address.is_some() {
1765 if cfg.host_ip.is_none() {
1766 return Err(argument::Error::ExpectedArgument(
1767 "`host_ip` missing from network config".to_owned(),
1768 ));
1769 }
1770 if cfg.netmask.is_none() {
1771 return Err(argument::Error::ExpectedArgument(
1772 "`netmask` missing from network config".to_owned(),
1773 ));
1774 }
1775 if cfg.mac_address.is_none() {
1776 return Err(argument::Error::ExpectedArgument(
1777 "`mac` missing from network config".to_owned(),
1778 ));
1779 }
1780 }
1781 if cfg.plugin_root.is_some() && !executable_is_plugin(&cfg.executable_path) {
1782 return Err(argument::Error::ExpectedArgument(
1783 "`plugin-root` requires `plugin`".to_owned(),
1784 ));
1785 }
1786 #[cfg(feature = "gpu")]
1787 {
1788 if let Some(gpu_parameters) = cfg.gpu_parameters.as_ref() {
1789 let (width, height) = (gpu_parameters.display_width, gpu_parameters.display_height);
1790 if let Some(virtio_multi_touch) = cfg.virtio_multi_touch.as_mut() {
1791 virtio_multi_touch.set_default_size(width, height);
1792 }
1793 if let Some(virtio_single_touch) = cfg.virtio_single_touch.as_mut() {
1794 virtio_single_touch.set_default_size(width, height);
1795 }
1796 }
1797 }
1798 #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
1799 if cfg.gdb.is_some() {
1800 if cfg.vcpu_count.unwrap_or(1) != 1 {
1801 return Err(argument::Error::ExpectedArgument(
1802 "`gdb` requires the number of vCPU to be 1".to_owned(),
1803 ));
1804 }
1805 }
1806 set_default_serial_parameters(&mut cfg.serial_parameters);
1807 Ok(())
1808 }
1809
run_vm(args: std::env::Args) -> std::result::Result<(), ()>1810 fn run_vm(args: std::env::Args) -> std::result::Result<(), ()> {
1811 let arguments =
1812 &[Argument::positional("KERNEL", "bzImage of kernel to run"),
1813 Argument::value("kvm-device", "PATH", "Path to the KVM device. (default /dev/kvm)"),
1814 Argument::value("vhost-vsock-device", "PATH", "Path to the vhost-vsock device. (default /dev/vhost-vsock)"),
1815 Argument::value("vhost-net-device", "PATH", "Path to the vhost-net device. (default /dev/vhost-net)"),
1816 Argument::value("android-fstab", "PATH", "Path to Android fstab"),
1817 Argument::short_value('i', "initrd", "PATH", "Initial ramdisk to load."),
1818 Argument::short_value('p',
1819 "params",
1820 "PARAMS",
1821 "Extra kernel or plugin command line arguments. Can be given more than once."),
1822 Argument::short_value('c', "cpus", "N", "Number of VCPUs. (default: 1)"),
1823 Argument::value("cpu-affinity", "CPUSET", "Comma-separated list of CPUs or CPU ranges to run VCPUs on (e.g. 0,1-3,5)
1824 or colon-separated list of assignments of guest to host CPU assignments (e.g. 0=0:1=1:2=2) (default: no mask)"),
1825 Argument::flag("no-smt", "Don't use SMT in the guest"),
1826 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)"),
1827 Argument::short_value('m',
1828 "mem",
1829 "N",
1830 "Amount of guest memory in MiB. (default: 256)"),
1831 Argument::flag("hugepages", "Advise the kernel to use Huge Pages for guest memory mappings."),
1832 Argument::short_value('r',
1833 "root",
1834 "PATH[,key=value[,key=value[,...]]",
1835 "Path to a root disk image followed by optional comma-separated options.
1836 Like `--disk` but adds appropriate kernel command line option.
1837 See --disk for valid options."),
1838 Argument::value("rwroot", "PATH[,key=value[,key=value[,...]]", "Path to a writable root disk image followed by optional comma-separated options.
1839 See --disk for valid options."),
1840 Argument::short_value('d', "disk", "PATH[,key=value[,key=value[,...]]", "Path to a disk image followed by optional comma-separated options.
1841 Valid keys:
1842 sparse=BOOL - Indicates whether the disk should support the discard operation (default: true)
1843 block_size=BYTES - Set the reported block size of the disk (default: 512)
1844 id=STRING - Set the block device identifier to an ASCII string, up to 20 characters (default: no ID)"),
1845 Argument::value("rwdisk", "PATH[,key=value[,key=value[,...]]", "Path to a writable disk image followed by optional comma-separated options.
1846 See --disk for valid options."),
1847 Argument::value("rw-pmem-device", "PATH", "Path to a writable disk image."),
1848 Argument::value("pmem-device", "PATH", "Path to a disk image."),
1849 Argument::value("pstore", "path=PATH,size=SIZE", "Path to pstore buffer backend file follewed by size."),
1850 Argument::value("host_ip",
1851 "IP",
1852 "IP address to assign to host tap interface."),
1853 Argument::value("netmask", "NETMASK", "Netmask for VM subnet."),
1854 Argument::value("mac", "MAC", "MAC address for VM."),
1855 Argument::value("net-vq-pairs", "N", "virtio net virtual queue paris. (default: 1)"),
1856 #[cfg(feature = "audio")]
1857 Argument::value("ac97",
1858 "[backend=BACKEND,capture=true,capture_effect=EFFECT,client_type=TYPE,shm-fd=FD,client-fd=FD,server-fd=FD]",
1859 "Comma separated key=value pairs for setting up Ac97 devices. Can be given more than once .
1860 Possible key values:
1861 backend=(null, cras, vios) - Where to route the audio device. If not provided, backend will default to null.
1862 `null` for /dev/null, cras for CRAS server and vios for VioS server.
1863 capture - Enable audio capture
1864 capture_effects - | separated effects to be enabled for recording. The only supported effect value now is EchoCancellation or aec.
1865 client_type - Set specific client type for cras backend.
1866 server - The to the VIOS server (unix socket)."),
1867 Argument::value("serial",
1868 "type=TYPE,[hardware=HW,num=NUM,path=PATH,input=PATH,console,earlycon,stdin]",
1869 "Comma separated key=value pairs for setting up serial devices. Can be given more than once.
1870 Possible key values:
1871 type=(stdout,syslog,sink,file) - Where to route the serial device
1872 hardware=(serial,virtio-console) - Which type of serial hardware to emulate. Defaults to 8250 UART (serial).
1873 num=(1,2,3,4) - Serial Device Number. If not provided, num will default to 1.
1874 path=PATH - The path to the file to write to when type=file
1875 input=PATH - The path to the file to read from when not stdin
1876 console - Use this serial device as the guest console. Can only be given once. Will default to first serial port if not provided.
1877 earlycon - Use this serial device as the early console. Can only be given once.
1878 stdin - Direct standard input to this serial device. Can only be given once. Will default to first serial port if not provided.
1879 "),
1880 Argument::value("syslog-tag", "TAG", "When logging to syslog, use the provided tag."),
1881 Argument::value("x-display", "DISPLAY", "X11 display name to use."),
1882 Argument::flag("display-window-keyboard", "Capture keyboard input from the display window."),
1883 Argument::flag("display-window-mouse", "Capture keyboard input from the display window."),
1884 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."),
1885 #[cfg(feature = "wl-dmabuf")]
1886 Argument::flag("wayland-dmabuf", "Enable support for DMABufs in Wayland device."),
1887 Argument::short_value('s',
1888 "socket",
1889 "PATH",
1890 "Path to put the control socket. If PATH is a directory, a name will be generated."),
1891 Argument::flag("disable-sandbox", "Run all devices in one, non-sandboxed process."),
1892 Argument::value("cid", "CID", "Context ID for virtual sockets."),
1893 Argument::value("shared-dir", "PATH:TAG[:type=TYPE:writeback=BOOL:timeout=SECONDS:uidmap=UIDMAP:gidmap=GIDMAP:cache=CACHE]",
1894 "Colon-separated options for configuring a directory to be shared with the VM.
1895 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.
1896 The remaining fields are key=value pairs that may appear in any order. Valid keys are:
1897 type=(p9, fs) - Indicates whether the directory should be shared via virtio-9p or virtio-fs (default: p9).
1898 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).
1899 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).
1900 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.
1901 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.
1902 writeback=BOOL - Indicates whether the VM can use writeback caching (default: false). This is only safe to do when the VM has exclusive access to the files in a directory. Additionally, the server should have read permission for all files as the VM may issue read requests even for files that are opened write-only.
1903 "),
1904 Argument::value("seccomp-policy-dir", "PATH", "Path to seccomp .policy files."),
1905 Argument::flag("seccomp-log-failures", "Instead of seccomp filter failures being fatal, they will be logged instead."),
1906 #[cfg(feature = "plugin")]
1907 Argument::value("plugin", "PATH", "Absolute path to plugin process to run under crosvm."),
1908 #[cfg(feature = "plugin")]
1909 Argument::value("plugin-root", "PATH", "Absolute path to a directory that will become root filesystem for the plugin process."),
1910 #[cfg(feature = "plugin")]
1911 Argument::value("plugin-mount", "PATH:PATH:BOOL", "Path to be mounted into the plugin's root filesystem. Can be given more than once."),
1912 #[cfg(feature = "plugin")]
1913 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."),
1914 #[cfg(feature = "plugin")]
1915 Argument::value("plugin-gid-map", "GID:GID:INT", "Supplemental GIDs that should be mapped in plugin jail. Can be given more than once."),
1916 #[cfg(feature = "plugin")]
1917 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."),
1918 Argument::flag("vhost-net", "Use vhost for networking."),
1919 Argument::value("tap-fd",
1920 "fd",
1921 "File descriptor for configured tap device. A different virtual network card will be added each time this argument is given."),
1922 #[cfg(feature = "gpu")]
1923 Argument::flag_or_value("gpu",
1924 "[width=INT,height=INT]",
1925 "(EXPERIMENTAL) Comma separated key=value pairs for setting up a virtio-gpu device
1926 Possible key values:
1927 backend=(2d|virglrenderer|gfxstream) - Which backend to use for virtio-gpu (determining rendering protocol)
1928 width=INT - The width of the virtual display connected to the virtio-gpu.
1929 height=INT - The height of the virtual display connected to the virtio-gpu.
1930 egl[=true|=false] - If the backend should use a EGL context for rendering.
1931 glx[=true|=false] - If the backend should use a GLX context for rendering.
1932 surfaceless[=true|=false] - If the backend should use a surfaceless context for rendering.
1933 angle[=true|=false] - If the gfxstream backend should use ANGLE (OpenGL on Vulkan) as its native OpenGL driver.
1934 syncfd[=true|=false] - If the gfxstream backend should support EGL_ANDROID_native_fence_sync
1935 vulkan[=true|=false] - If the backend should support vulkan
1936 "),
1937 #[cfg(feature = "tpm")]
1938 Argument::flag("software-tpm", "enable a software emulated trusted platform module device"),
1939 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"),
1940 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)."),
1941 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)."),
1942 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)."),
1943 Argument::value("mouse", "PATH", "Path to a socket from where to read mouse input events and write status updates to."),
1944 Argument::value("keyboard", "PATH", "Path to a socket from where to read keyboard input events and write status updates to."),
1945 Argument::value("switches", "PATH", "Path to a socket from where to read switch input events and write status updates to."),
1946 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
1947 Argument::flag("split-irqchip", "(EXPERIMENTAL) enable split-irqchip support"),
1948 Argument::value("bios", "PATH", "Path to BIOS/firmware ROM"),
1949 Argument::value("vfio", "PATH", "Path to sysfs of pass through or mdev device"),
1950 #[cfg(feature = "video-decoder")]
1951 Argument::flag("video-decoder", "(EXPERIMENTAL) enable virtio-video decoder device"),
1952 #[cfg(feature = "video-encoder")]
1953 Argument::flag("video-encoder", "(EXPERIMENTAL) enable virtio-video encoder device"),
1954 Argument::value("acpi-table", "PATH", "Path to user provided ACPI table"),
1955 Argument::flag("protected-vm", "(EXPERIMENTAL) prevent host access to guest memory"),
1956 Argument::flag_or_value("battery",
1957 "[type=TYPE]",
1958 "Comma separated key=value pairs for setting up battery device
1959 Possible key values:
1960 type=goldfish - type of battery emulation, defaults to goldfish
1961 "),
1962 Argument::value("gdb", "PORT", "(EXPERIMENTAL) gdb on the given port"),
1963 Argument::value("balloon_bias_mib", "N", "Amount to bias balance of memory between host and guest as the balloon inflates, in MiB."),
1964 Argument::value("vhost-user-blk", "SOCKET_PATH", "Path to a socket for vhost-user block"),
1965 Argument::value("vhost-user-net", "SOCKET_PATH", "Path to a socket for vhost-user net"),
1966 Argument::value("vhost-user-fs", "SOCKET_PATH:TAG",
1967 "Path to a socket path for vhost-user fs, and tag for the shared dir"),
1968 #[cfg(feature = "direct")]
1969 Argument::value("direct-pmio", "PATH@RANGE[,RANGE[,...]]", "Path and ranges for direct port I/O access"),
1970 #[cfg(feature = "direct")]
1971 Argument::value("direct-level-irq", "irq", "Enable interrupt passthrough"),
1972 #[cfg(feature = "direct")]
1973 Argument::value("direct-edge-irq", "irq", "Enable interrupt passthrough"),
1974 Argument::value("dmi", "DIR", "Directory with smbios_entry_point/DMI files"),
1975 Argument::short_flag('h', "help", "Print help message.")];
1976
1977 let mut cfg = Config::default();
1978 let match_res = set_arguments(args, &arguments[..], |name, value| {
1979 set_argument(&mut cfg, name, value)
1980 })
1981 .and_then(|_| validate_arguments(&mut cfg));
1982
1983 match match_res {
1984 #[cfg(feature = "plugin")]
1985 Ok(()) if executable_is_plugin(&cfg.executable_path) => {
1986 match crosvm::plugin::run_config(cfg) {
1987 Ok(_) => {
1988 info!("crosvm and plugin have exited normally");
1989 Ok(())
1990 }
1991 Err(e) => {
1992 error!("{}", e);
1993 Err(())
1994 }
1995 }
1996 }
1997 Ok(()) => match platform::run_config(cfg) {
1998 Ok(_) => {
1999 info!("crosvm has exited normally");
2000 Ok(())
2001 }
2002 Err(e) => {
2003 error!("crosvm has exited with error: {}", e);
2004 Err(())
2005 }
2006 },
2007 Err(argument::Error::PrintHelp) => {
2008 print_help("crosvm run", "KERNEL", &arguments[..]);
2009 Ok(())
2010 }
2011 Err(e) => {
2012 error!("{}", e);
2013 Err(())
2014 }
2015 }
2016 }
2017
stop_vms(mut args: std::env::Args) -> std::result::Result<(), ()>2018 fn stop_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
2019 if args.len() == 0 {
2020 print_help("crosvm stop", "VM_SOCKET...", &[]);
2021 println!("Stops the crosvm instance listening on each `VM_SOCKET` given.");
2022 return Err(());
2023 }
2024 let socket_path = &args.next().unwrap();
2025 let socket_path = Path::new(&socket_path);
2026 vms_request(&VmRequest::Exit, socket_path)
2027 }
2028
suspend_vms(mut args: std::env::Args) -> std::result::Result<(), ()>2029 fn suspend_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
2030 if args.len() == 0 {
2031 print_help("crosvm suspend", "VM_SOCKET...", &[]);
2032 println!("Suspends the crosvm instance listening on each `VM_SOCKET` given.");
2033 return Err(());
2034 }
2035 let socket_path = &args.next().unwrap();
2036 let socket_path = Path::new(&socket_path);
2037 vms_request(&VmRequest::Suspend, socket_path)
2038 }
2039
resume_vms(mut args: std::env::Args) -> std::result::Result<(), ()>2040 fn resume_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
2041 if args.len() == 0 {
2042 print_help("crosvm resume", "VM_SOCKET...", &[]);
2043 println!("Resumes the crosvm instance listening on each `VM_SOCKET` given.");
2044 return Err(());
2045 }
2046 let socket_path = &args.next().unwrap();
2047 let socket_path = Path::new(&socket_path);
2048 vms_request(&VmRequest::Resume, socket_path)
2049 }
2050
balloon_vms(mut args: std::env::Args) -> std::result::Result<(), ()>2051 fn balloon_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
2052 if args.len() < 2 {
2053 print_help("crosvm balloon", "SIZE VM_SOCKET...", &[]);
2054 println!("Set the ballon size of the crosvm instance to `SIZE` bytes.");
2055 return Err(());
2056 }
2057 let num_bytes = match args.next().unwrap().parse::<u64>() {
2058 Ok(n) => n,
2059 Err(_) => {
2060 error!("Failed to parse number of bytes");
2061 return Err(());
2062 }
2063 };
2064
2065 let command = BalloonControlCommand::Adjust { num_bytes };
2066 let socket_path = &args.next().unwrap();
2067 let socket_path = Path::new(&socket_path);
2068 vms_request(&VmRequest::BalloonCommand(command), socket_path)
2069 }
2070
balloon_stats(mut args: std::env::Args) -> std::result::Result<(), ()>2071 fn balloon_stats(mut args: std::env::Args) -> std::result::Result<(), ()> {
2072 if args.len() != 1 {
2073 print_help("crosvm balloon_stats", "VM_SOCKET", &[]);
2074 println!("Prints virtio balloon statistics for a `VM_SOCKET`.");
2075 return Err(());
2076 }
2077 let command = BalloonControlCommand::Stats {};
2078 let request = &VmRequest::BalloonCommand(command);
2079 let socket_path = &args.next().unwrap();
2080 let socket_path = Path::new(&socket_path);
2081 let response = handle_request(request, socket_path)?;
2082 println!("{}", response);
2083 Ok(())
2084 }
2085
create_qcow2(args: std::env::Args) -> std::result::Result<(), ()>2086 fn create_qcow2(args: std::env::Args) -> std::result::Result<(), ()> {
2087 let arguments = [
2088 Argument::positional("PATH", "where to create the qcow2 image"),
2089 Argument::positional("[SIZE]", "the expanded size of the image"),
2090 Argument::value(
2091 "backing_file",
2092 "path/to/file",
2093 " the file to back the image",
2094 ),
2095 ];
2096 let mut positional_index = 0;
2097 let mut file_path = String::from("");
2098 let mut size: Option<u64> = None;
2099 let mut backing_file: Option<String> = None;
2100 set_arguments(args, &arguments[..], |name, value| {
2101 match (name, positional_index) {
2102 ("", 0) => {
2103 // NAME
2104 positional_index += 1;
2105 file_path = value.unwrap().to_owned();
2106 }
2107 ("", 1) => {
2108 // [SIZE]
2109 positional_index += 1;
2110 size = Some(value.unwrap().parse::<u64>().map_err(|_| {
2111 argument::Error::InvalidValue {
2112 value: value.unwrap().to_owned(),
2113 expected: String::from("SIZE should be a nonnegative integer"),
2114 }
2115 })?);
2116 }
2117 ("", _) => {
2118 return Err(argument::Error::TooManyArguments(
2119 "Expected at most 2 positional arguments".to_owned(),
2120 ));
2121 }
2122 ("backing_file", _) => {
2123 backing_file = value.map(|x| x.to_owned());
2124 }
2125 _ => unreachable!(),
2126 };
2127 Ok(())
2128 })
2129 .map_err(|e| {
2130 error!("Unable to parse command line arguments: {}", e);
2131 })?;
2132 if file_path.is_empty() || !(size.is_some() ^ backing_file.is_some()) {
2133 print_help("crosvm create_qcow2", "PATH [SIZE]", &arguments);
2134 println!(
2135 "Create a new QCOW2 image at `PATH` of either the specified `SIZE` in bytes or
2136 with a '--backing_file'."
2137 );
2138 return Err(());
2139 }
2140
2141 let file = OpenOptions::new()
2142 .create(true)
2143 .read(true)
2144 .write(true)
2145 .truncate(true)
2146 .open(&file_path)
2147 .map_err(|e| {
2148 error!("Failed opening qcow file at '{}': {}", file_path, e);
2149 })?;
2150
2151 match (size, backing_file) {
2152 (Some(size), None) => QcowFile::new(file, size).map_err(|e| {
2153 error!("Failed to create qcow file at '{}': {}", file_path, e);
2154 })?,
2155 (None, Some(backing_file)) => {
2156 QcowFile::new_from_backing(file, &backing_file).map_err(|e| {
2157 error!("Failed to create qcow file at '{}': {}", file_path, e);
2158 })?
2159 }
2160 _ => unreachable!(),
2161 };
2162 Ok(())
2163 }
2164
disk_cmd(mut args: std::env::Args) -> std::result::Result<(), ()>2165 fn disk_cmd(mut args: std::env::Args) -> std::result::Result<(), ()> {
2166 if args.len() < 2 {
2167 print_help("crosvm disk", "SUBCOMMAND VM_SOCKET...", &[]);
2168 println!("Manage attached virtual disk devices.");
2169 println!("Subcommands:");
2170 println!(" resize DISK_INDEX NEW_SIZE VM_SOCKET");
2171 return Err(());
2172 }
2173 let subcommand: &str = &args.next().unwrap();
2174
2175 let request = match subcommand {
2176 "resize" => {
2177 let disk_index = match args.next().unwrap().parse::<usize>() {
2178 Ok(n) => n,
2179 Err(_) => {
2180 error!("Failed to parse disk index");
2181 return Err(());
2182 }
2183 };
2184
2185 let new_size = match args.next().unwrap().parse::<u64>() {
2186 Ok(n) => n,
2187 Err(_) => {
2188 error!("Failed to parse disk size");
2189 return Err(());
2190 }
2191 };
2192
2193 VmRequest::DiskCommand {
2194 disk_index,
2195 command: DiskControlCommand::Resize { new_size },
2196 }
2197 }
2198 _ => {
2199 error!("Unknown disk subcommand '{}'", subcommand);
2200 return Err(());
2201 }
2202 };
2203
2204 let socket_path = &args.next().unwrap();
2205 let socket_path = Path::new(&socket_path);
2206 vms_request(&request, socket_path)
2207 }
2208
parse_bus_id_addr(v: &str) -> ModifyUsbResult<(u8, u8, u16, u16)>2209 fn parse_bus_id_addr(v: &str) -> ModifyUsbResult<(u8, u8, u16, u16)> {
2210 debug!("parse_bus_id_addr: {}", v);
2211 let mut ids = v.split(':');
2212 match (ids.next(), ids.next(), ids.next(), ids.next()) {
2213 (Some(bus_id), Some(addr), Some(vid), Some(pid)) => {
2214 let bus_id = bus_id
2215 .parse::<u8>()
2216 .map_err(|e| ModifyUsbError::ArgParseInt("bus_id", bus_id.to_owned(), e))?;
2217 let addr = addr
2218 .parse::<u8>()
2219 .map_err(|e| ModifyUsbError::ArgParseInt("addr", addr.to_owned(), e))?;
2220 let vid = u16::from_str_radix(&vid, 16)
2221 .map_err(|e| ModifyUsbError::ArgParseInt("vid", vid.to_owned(), e))?;
2222 let pid = u16::from_str_radix(&pid, 16)
2223 .map_err(|e| ModifyUsbError::ArgParseInt("pid", pid.to_owned(), e))?;
2224 Ok((bus_id, addr, vid, pid))
2225 }
2226 _ => Err(ModifyUsbError::ArgParse(
2227 "BUS_ID_ADDR_BUS_NUM_DEV_NUM",
2228 v.to_owned(),
2229 )),
2230 }
2231 }
2232
usb_attach(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult>2233 fn usb_attach(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
2234 let val = args
2235 .next()
2236 .ok_or(ModifyUsbError::ArgMissing("BUS_ID_ADDR_BUS_NUM_DEV_NUM"))?;
2237 let (bus, addr, vid, pid) = parse_bus_id_addr(&val)?;
2238 let dev_path = PathBuf::from(
2239 args.next()
2240 .ok_or(ModifyUsbError::ArgMissing("usb device path"))?,
2241 );
2242
2243 let socket_path = args
2244 .next()
2245 .ok_or(ModifyUsbError::ArgMissing("control socket path"))?;
2246 let socket_path = Path::new(&socket_path);
2247
2248 do_usb_attach(&socket_path, bus, addr, vid, pid, &dev_path)
2249 }
2250
usb_detach(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult>2251 fn usb_detach(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
2252 let port: u8 = args
2253 .next()
2254 .map_or(Err(ModifyUsbError::ArgMissing("PORT")), |p| {
2255 p.parse::<u8>()
2256 .map_err(|e| ModifyUsbError::ArgParseInt("PORT", p.to_owned(), e))
2257 })?;
2258 let socket_path = args
2259 .next()
2260 .ok_or(ModifyUsbError::ArgMissing("control socket path"))?;
2261 let socket_path = Path::new(&socket_path);
2262 do_usb_detach(&socket_path, port)
2263 }
2264
usb_list(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult>2265 fn usb_list(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
2266 let socket_path = args
2267 .next()
2268 .ok_or(ModifyUsbError::ArgMissing("control socket path"))?;
2269 let socket_path = Path::new(&socket_path);
2270 do_usb_list(&socket_path)
2271 }
2272
modify_usb(mut args: std::env::Args) -> std::result::Result<(), ()>2273 fn modify_usb(mut args: std::env::Args) -> std::result::Result<(), ()> {
2274 if args.len() < 2 {
2275 print_help("crosvm usb",
2276 "[attach BUS_ID:ADDR:VENDOR_ID:PRODUCT_ID [USB_DEVICE_PATH|-] | detach PORT | list] VM_SOCKET...", &[]);
2277 return Err(());
2278 }
2279
2280 // This unwrap will not panic because of the above length check.
2281 let command = &args.next().unwrap();
2282 let result = match command.as_ref() {
2283 "attach" => usb_attach(args),
2284 "detach" => usb_detach(args),
2285 "list" => usb_list(args),
2286 other => Err(ModifyUsbError::UnknownCommand(other.to_owned())),
2287 };
2288 match result {
2289 Ok(response) => {
2290 println!("{}", response);
2291 Ok(())
2292 }
2293 Err(e) => {
2294 println!("error {}", e);
2295 Err(())
2296 }
2297 }
2298 }
2299
print_usage()2300 fn print_usage() {
2301 print_help("crosvm", "[command]", &[]);
2302 println!("Commands:");
2303 println!(" balloon - Set balloon size of the crosvm instance.");
2304 println!(" balloon_stats - Prints virtio balloon statistics.");
2305 println!(" battery - Modify battery.");
2306 println!(" create_qcow2 - Create a new qcow2 disk image file.");
2307 println!(" disk - Manage attached virtual disk devices.");
2308 println!(" resume - Resumes the crosvm instance.");
2309 println!(" run - Start a new crosvm instance.");
2310 println!(" stop - Stops crosvm instances via their control sockets.");
2311 println!(" suspend - Suspends the crosvm instance.");
2312 println!(" usb - Manage attached virtual USB devices.");
2313 println!(" version - Show package version.");
2314 }
2315
2316 #[allow(clippy::unnecessary_wraps)]
pkg_version() -> std::result::Result<(), ()>2317 fn pkg_version() -> std::result::Result<(), ()> {
2318 const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION");
2319 const PKG_VERSION: Option<&'static str> = option_env!("PKG_VERSION");
2320
2321 print!("crosvm {}", VERSION.unwrap_or("UNKNOWN"));
2322 match PKG_VERSION {
2323 Some(v) => println!("-{}", v),
2324 None => println!(),
2325 }
2326 Ok(())
2327 }
2328
modify_battery(mut args: std::env::Args) -> std::result::Result<(), ()>2329 fn modify_battery(mut args: std::env::Args) -> std::result::Result<(), ()> {
2330 if args.len() < 4 {
2331 print_help("crosvm battery BATTERY_TYPE ",
2332 "[status STATUS | present PRESENT | health HEALTH | capacity CAPACITY | aconline ACONLINE ] VM_SOCKET...", &[]);
2333 return Err(());
2334 }
2335
2336 // This unwrap will not panic because of the above length check.
2337 let battery_type = args.next().unwrap();
2338 let property = args.next().unwrap();
2339 let target = args.next().unwrap();
2340
2341 let socket_path = args.next().unwrap();
2342 let socket_path = Path::new(&socket_path);
2343
2344 do_modify_battery(&socket_path, &*battery_type, &*property, &*target)
2345 }
2346
crosvm_main() -> std::result::Result<(), ()>2347 fn crosvm_main() -> std::result::Result<(), ()> {
2348 if let Err(e) = syslog::init() {
2349 println!("failed to initialize syslog: {}", e);
2350 return Err(());
2351 }
2352
2353 panic_hook::set_panic_hook();
2354
2355 let mut args = std::env::args();
2356 if args.next().is_none() {
2357 error!("expected executable name");
2358 return Err(());
2359 }
2360
2361 // Past this point, usage of exit is in danger of leaking zombie processes.
2362 let ret = match args.next().as_ref().map(|a| a.as_ref()) {
2363 None => {
2364 print_usage();
2365 Ok(())
2366 }
2367 Some("stop") => stop_vms(args),
2368 Some("suspend") => suspend_vms(args),
2369 Some("resume") => resume_vms(args),
2370 Some("run") => run_vm(args),
2371 Some("balloon") => balloon_vms(args),
2372 Some("balloon_stats") => balloon_stats(args),
2373 Some("create_qcow2") => create_qcow2(args),
2374 Some("disk") => disk_cmd(args),
2375 Some("usb") => modify_usb(args),
2376 Some("version") => pkg_version(),
2377 Some("battery") => modify_battery(args),
2378 Some(c) => {
2379 println!("invalid subcommand: {:?}", c);
2380 print_usage();
2381 Err(())
2382 }
2383 };
2384
2385 // Reap exit status from any child device processes. At this point, all devices should have been
2386 // dropped in the main process and told to shutdown. Try over a period of 100ms, since it may
2387 // take some time for the processes to shut down.
2388 if !wait_all_children() {
2389 // We gave them a chance, and it's too late.
2390 warn!("not all child processes have exited; sending SIGKILL");
2391 if let Err(e) = kill_process_group() {
2392 // We're now at the mercy of the OS to clean up after us.
2393 warn!("unable to kill all child processes: {}", e);
2394 }
2395 }
2396
2397 // WARNING: Any code added after this point is not guaranteed to run
2398 // since we may forcibly kill this process (and its children) above.
2399 ret
2400 }
2401
main()2402 fn main() {
2403 std::process::exit(if crosvm_main().is_ok() { 0 } else { 1 });
2404 }
2405
2406 #[cfg(test)]
2407 mod tests {
2408 use super::*;
2409 use crosvm::{DEFAULT_TOUCH_DEVICE_HEIGHT, DEFAULT_TOUCH_DEVICE_WIDTH};
2410
2411 #[test]
parse_cpu_set_single()2412 fn parse_cpu_set_single() {
2413 assert_eq!(parse_cpu_set("123").expect("parse failed"), vec![123]);
2414 }
2415
2416 #[test]
parse_cpu_set_list()2417 fn parse_cpu_set_list() {
2418 assert_eq!(
2419 parse_cpu_set("0,1,2,3").expect("parse failed"),
2420 vec![0, 1, 2, 3]
2421 );
2422 }
2423
2424 #[test]
parse_cpu_set_range()2425 fn parse_cpu_set_range() {
2426 assert_eq!(
2427 parse_cpu_set("0-3").expect("parse failed"),
2428 vec![0, 1, 2, 3]
2429 );
2430 }
2431
2432 #[test]
parse_cpu_set_list_of_ranges()2433 fn parse_cpu_set_list_of_ranges() {
2434 assert_eq!(
2435 parse_cpu_set("3-4,7-9,18").expect("parse failed"),
2436 vec![3, 4, 7, 8, 9, 18]
2437 );
2438 }
2439
2440 #[test]
parse_cpu_set_repeated()2441 fn parse_cpu_set_repeated() {
2442 // For now, allow duplicates - they will be handled gracefully by the vec to cpu_set_t conversion.
2443 assert_eq!(parse_cpu_set("1,1,1").expect("parse failed"), vec![1, 1, 1]);
2444 }
2445
2446 #[test]
parse_cpu_set_negative()2447 fn parse_cpu_set_negative() {
2448 // Negative CPU numbers are not allowed.
2449 parse_cpu_set("-3").expect_err("parse should have failed");
2450 }
2451
2452 #[test]
parse_cpu_set_reverse_range()2453 fn parse_cpu_set_reverse_range() {
2454 // Ranges must be from low to high.
2455 parse_cpu_set("5-2").expect_err("parse should have failed");
2456 }
2457
2458 #[test]
parse_cpu_set_open_range()2459 fn parse_cpu_set_open_range() {
2460 parse_cpu_set("3-").expect_err("parse should have failed");
2461 }
2462
2463 #[test]
parse_cpu_set_extra_comma()2464 fn parse_cpu_set_extra_comma() {
2465 parse_cpu_set("0,1,2,").expect_err("parse should have failed");
2466 }
2467
2468 #[test]
parse_cpu_affinity_global()2469 fn parse_cpu_affinity_global() {
2470 assert_eq!(
2471 parse_cpu_affinity("0,5-7,9").expect("parse failed"),
2472 VcpuAffinity::Global(vec![0, 5, 6, 7, 9]),
2473 );
2474 }
2475
2476 #[test]
parse_cpu_affinity_per_vcpu_one_to_one()2477 fn parse_cpu_affinity_per_vcpu_one_to_one() {
2478 let mut expected_map = BTreeMap::new();
2479 expected_map.insert(0, vec![0]);
2480 expected_map.insert(1, vec![1]);
2481 expected_map.insert(2, vec![2]);
2482 expected_map.insert(3, vec![3]);
2483 assert_eq!(
2484 parse_cpu_affinity("0=0:1=1:2=2:3=3").expect("parse failed"),
2485 VcpuAffinity::PerVcpu(expected_map),
2486 );
2487 }
2488
2489 #[test]
parse_cpu_affinity_per_vcpu_sets()2490 fn parse_cpu_affinity_per_vcpu_sets() {
2491 let mut expected_map = BTreeMap::new();
2492 expected_map.insert(0, vec![0, 1, 2]);
2493 expected_map.insert(1, vec![3, 4, 5]);
2494 expected_map.insert(2, vec![6, 7, 8]);
2495 assert_eq!(
2496 parse_cpu_affinity("0=0,1,2:1=3-5:2=6,7-8").expect("parse failed"),
2497 VcpuAffinity::PerVcpu(expected_map),
2498 );
2499 }
2500
2501 #[cfg(feature = "audio")]
2502 #[test]
parse_ac97_vaild()2503 fn parse_ac97_vaild() {
2504 parse_ac97_options("backend=cras").expect("parse should have succeded");
2505 }
2506
2507 #[cfg(feature = "audio")]
2508 #[test]
parse_ac97_null_vaild()2509 fn parse_ac97_null_vaild() {
2510 parse_ac97_options("backend=null").expect("parse should have succeded");
2511 }
2512
2513 #[cfg(feature = "audio")]
2514 #[test]
parse_ac97_capture_vaild()2515 fn parse_ac97_capture_vaild() {
2516 parse_ac97_options("backend=cras,capture=true").expect("parse should have succeded");
2517 }
2518
2519 #[cfg(feature = "audio")]
2520 #[test]
parse_ac97_client_type()2521 fn parse_ac97_client_type() {
2522 parse_ac97_options("backend=cras,capture=true,client_type=crosvm")
2523 .expect("parse should have succeded");
2524 parse_ac97_options("backend=cras,capture=true,client_type=arcvm")
2525 .expect("parse should have succeded");
2526 parse_ac97_options("backend=cras,capture=true,client_type=none")
2527 .expect_err("parse should have failed");
2528 }
2529
2530 #[cfg(feature = "audio")]
2531 #[test]
parse_ac97_vios_valid()2532 fn parse_ac97_vios_valid() {
2533 parse_ac97_options("backend=vios,server=/path/to/server")
2534 .expect("parse should have succeded");
2535 }
2536
2537 #[test]
parse_serial_vaild()2538 fn parse_serial_vaild() {
2539 parse_serial_options("type=syslog,num=1,console=true,stdin=true")
2540 .expect("parse should have succeded");
2541 }
2542
2543 #[test]
parse_serial_virtio_console_vaild()2544 fn parse_serial_virtio_console_vaild() {
2545 parse_serial_options("type=syslog,num=5,console=true,stdin=true,hardware=virtio-console")
2546 .expect("parse should have succeded");
2547 }
2548
2549 #[test]
parse_serial_valid_no_num()2550 fn parse_serial_valid_no_num() {
2551 parse_serial_options("type=syslog").expect("parse should have succeded");
2552 }
2553
2554 #[test]
parse_serial_equals_in_value()2555 fn parse_serial_equals_in_value() {
2556 let parsed = parse_serial_options("type=syslog,path=foo=bar==.log")
2557 .expect("parse should have succeded");
2558 assert_eq!(parsed.path, Some(PathBuf::from("foo=bar==.log")));
2559 }
2560
2561 #[test]
parse_serial_invalid_type()2562 fn parse_serial_invalid_type() {
2563 parse_serial_options("type=wormhole,num=1").expect_err("parse should have failed");
2564 }
2565
2566 #[test]
parse_serial_invalid_num_upper()2567 fn parse_serial_invalid_num_upper() {
2568 parse_serial_options("type=syslog,num=5").expect_err("parse should have failed");
2569 }
2570
2571 #[test]
parse_serial_invalid_num_lower()2572 fn parse_serial_invalid_num_lower() {
2573 parse_serial_options("type=syslog,num=0").expect_err("parse should have failed");
2574 }
2575
2576 #[test]
parse_serial_virtio_console_invalid_num_lower()2577 fn parse_serial_virtio_console_invalid_num_lower() {
2578 parse_serial_options("type=syslog,hardware=virtio-console,num=0")
2579 .expect_err("parse should have failed");
2580 }
2581
2582 #[test]
parse_serial_invalid_num_string()2583 fn parse_serial_invalid_num_string() {
2584 parse_serial_options("type=syslog,num=number3").expect_err("parse should have failed");
2585 }
2586
2587 #[test]
parse_serial_invalid_option()2588 fn parse_serial_invalid_option() {
2589 parse_serial_options("type=syslog,speed=lightspeed").expect_err("parse should have failed");
2590 }
2591
2592 #[test]
parse_serial_invalid_two_stdin()2593 fn parse_serial_invalid_two_stdin() {
2594 let mut config = Config::default();
2595 set_argument(&mut config, "serial", Some("num=1,type=stdout,stdin=true"))
2596 .expect("should parse the first serial argument");
2597 set_argument(&mut config, "serial", Some("num=2,type=stdout,stdin=true"))
2598 .expect_err("should fail to parse a second serial port connected to stdin");
2599 }
2600
2601 #[test]
parse_plugin_mount_valid()2602 fn parse_plugin_mount_valid() {
2603 let mut config = Config::default();
2604 set_argument(
2605 &mut config,
2606 "plugin-mount",
2607 Some("/dev/null:/dev/zero:true"),
2608 )
2609 .expect("parse should succeed");
2610 assert_eq!(config.plugin_mounts[0].src, PathBuf::from("/dev/null"));
2611 assert_eq!(config.plugin_mounts[0].dst, PathBuf::from("/dev/zero"));
2612 assert_eq!(config.plugin_mounts[0].writable, true);
2613 }
2614
2615 #[test]
parse_plugin_mount_valid_shorthand()2616 fn parse_plugin_mount_valid_shorthand() {
2617 let mut config = Config::default();
2618 set_argument(&mut config, "plugin-mount", Some("/dev/null")).expect("parse should succeed");
2619 assert_eq!(config.plugin_mounts[0].dst, PathBuf::from("/dev/null"));
2620 assert_eq!(config.plugin_mounts[0].writable, false);
2621 set_argument(&mut config, "plugin-mount", Some("/dev/null:/dev/zero"))
2622 .expect("parse should succeed");
2623 assert_eq!(config.plugin_mounts[1].dst, PathBuf::from("/dev/zero"));
2624 assert_eq!(config.plugin_mounts[1].writable, false);
2625 set_argument(&mut config, "plugin-mount", Some("/dev/null::true"))
2626 .expect("parse should succeed");
2627 assert_eq!(config.plugin_mounts[2].dst, PathBuf::from("/dev/null"));
2628 assert_eq!(config.plugin_mounts[2].writable, true);
2629 }
2630
2631 #[test]
parse_plugin_mount_invalid()2632 fn parse_plugin_mount_invalid() {
2633 let mut config = Config::default();
2634 set_argument(&mut config, "plugin-mount", Some("")).expect_err("parse should fail");
2635 set_argument(
2636 &mut config,
2637 "plugin-mount",
2638 Some("/dev/null:/dev/null:true:false"),
2639 )
2640 .expect_err("parse should fail because too many arguments");
2641 set_argument(&mut config, "plugin-mount", Some("null:/dev/null:true"))
2642 .expect_err("parse should fail because source is not absolute");
2643 set_argument(&mut config, "plugin-mount", Some("/dev/null:null:true"))
2644 .expect_err("parse should fail because source is not absolute");
2645 set_argument(&mut config, "plugin-mount", Some("/dev/null:null:blah"))
2646 .expect_err("parse should fail because flag is not boolean");
2647 }
2648
2649 #[test]
parse_plugin_gid_map_valid()2650 fn parse_plugin_gid_map_valid() {
2651 let mut config = Config::default();
2652 set_argument(&mut config, "plugin-gid-map", Some("1:2:3")).expect("parse should succeed");
2653 assert_eq!(config.plugin_gid_maps[0].inner, 1);
2654 assert_eq!(config.plugin_gid_maps[0].outer, 2);
2655 assert_eq!(config.plugin_gid_maps[0].count, 3);
2656 }
2657
2658 #[test]
parse_plugin_gid_map_valid_shorthand()2659 fn parse_plugin_gid_map_valid_shorthand() {
2660 let mut config = Config::default();
2661 set_argument(&mut config, "plugin-gid-map", Some("1")).expect("parse should succeed");
2662 assert_eq!(config.plugin_gid_maps[0].inner, 1);
2663 assert_eq!(config.plugin_gid_maps[0].outer, 1);
2664 assert_eq!(config.plugin_gid_maps[0].count, 1);
2665 set_argument(&mut config, "plugin-gid-map", Some("1:2")).expect("parse should succeed");
2666 assert_eq!(config.plugin_gid_maps[1].inner, 1);
2667 assert_eq!(config.plugin_gid_maps[1].outer, 2);
2668 assert_eq!(config.plugin_gid_maps[1].count, 1);
2669 set_argument(&mut config, "plugin-gid-map", Some("1::3")).expect("parse should succeed");
2670 assert_eq!(config.plugin_gid_maps[2].inner, 1);
2671 assert_eq!(config.plugin_gid_maps[2].outer, 1);
2672 assert_eq!(config.plugin_gid_maps[2].count, 3);
2673 }
2674
2675 #[test]
parse_plugin_gid_map_invalid()2676 fn parse_plugin_gid_map_invalid() {
2677 let mut config = Config::default();
2678 set_argument(&mut config, "plugin-gid-map", Some("")).expect_err("parse should fail");
2679 set_argument(&mut config, "plugin-gid-map", Some("1:2:3:4"))
2680 .expect_err("parse should fail because too many arguments");
2681 set_argument(&mut config, "plugin-gid-map", Some("blah:2:3"))
2682 .expect_err("parse should fail because inner is not a number");
2683 set_argument(&mut config, "plugin-gid-map", Some("1:blah:3"))
2684 .expect_err("parse should fail because outer is not a number");
2685 set_argument(&mut config, "plugin-gid-map", Some("1:2:blah"))
2686 .expect_err("parse should fail because count is not a number");
2687 }
2688
2689 #[test]
single_touch_spec_and_track_pad_spec_default_size()2690 fn single_touch_spec_and_track_pad_spec_default_size() {
2691 let mut config = Config::default();
2692 config
2693 .executable_path
2694 .replace(Executable::Kernel(PathBuf::from("kernel")));
2695 set_argument(&mut config, "single-touch", Some("/dev/single-touch-test")).unwrap();
2696 set_argument(&mut config, "trackpad", Some("/dev/single-touch-test")).unwrap();
2697 validate_arguments(&mut config).unwrap();
2698 assert_eq!(
2699 config.virtio_single_touch.unwrap().get_size(),
2700 (DEFAULT_TOUCH_DEVICE_WIDTH, DEFAULT_TOUCH_DEVICE_HEIGHT)
2701 );
2702 assert_eq!(
2703 config.virtio_trackpad.unwrap().get_size(),
2704 (DEFAULT_TOUCH_DEVICE_WIDTH, DEFAULT_TOUCH_DEVICE_HEIGHT)
2705 );
2706 }
2707
2708 #[cfg(feature = "gpu")]
2709 #[test]
single_touch_spec_default_size_from_gpu()2710 fn single_touch_spec_default_size_from_gpu() {
2711 let width = 12345u32;
2712 let height = 54321u32;
2713 let mut config = Config::default();
2714 config
2715 .executable_path
2716 .replace(Executable::Kernel(PathBuf::from("kernel")));
2717 set_argument(&mut config, "single-touch", Some("/dev/single-touch-test")).unwrap();
2718 set_argument(
2719 &mut config,
2720 "gpu",
2721 Some(&format!("width={},height={}", width, height)),
2722 )
2723 .unwrap();
2724 validate_arguments(&mut config).unwrap();
2725 assert_eq!(
2726 config.virtio_single_touch.unwrap().get_size(),
2727 (width, height)
2728 );
2729 }
2730
2731 #[test]
single_touch_spec_and_track_pad_spec_with_size()2732 fn single_touch_spec_and_track_pad_spec_with_size() {
2733 let width = 12345u32;
2734 let height = 54321u32;
2735 let mut config = Config::default();
2736 config
2737 .executable_path
2738 .replace(Executable::Kernel(PathBuf::from("kernel")));
2739 set_argument(
2740 &mut config,
2741 "single-touch",
2742 Some(&format!("/dev/single-touch-test:{}:{}", width, height)),
2743 )
2744 .unwrap();
2745 set_argument(
2746 &mut config,
2747 "trackpad",
2748 Some(&format!("/dev/single-touch-test:{}:{}", width, height)),
2749 )
2750 .unwrap();
2751 validate_arguments(&mut config).unwrap();
2752 assert_eq!(
2753 config.virtio_single_touch.unwrap().get_size(),
2754 (width, height)
2755 );
2756 assert_eq!(config.virtio_trackpad.unwrap().get_size(), (width, height));
2757 }
2758
2759 #[cfg(feature = "gpu")]
2760 #[test]
single_touch_spec_with_size_independent_from_gpu()2761 fn single_touch_spec_with_size_independent_from_gpu() {
2762 let touch_width = 12345u32;
2763 let touch_height = 54321u32;
2764 let display_width = 1234u32;
2765 let display_height = 5432u32;
2766 let mut config = Config::default();
2767 config
2768 .executable_path
2769 .replace(Executable::Kernel(PathBuf::from("kernel")));
2770 set_argument(
2771 &mut config,
2772 "single-touch",
2773 Some(&format!(
2774 "/dev/single-touch-test:{}:{}",
2775 touch_width, touch_height
2776 )),
2777 )
2778 .unwrap();
2779 set_argument(
2780 &mut config,
2781 "gpu",
2782 Some(&format!(
2783 "width={},height={}",
2784 display_width, display_height
2785 )),
2786 )
2787 .unwrap();
2788 validate_arguments(&mut config).unwrap();
2789 assert_eq!(
2790 config.virtio_single_touch.unwrap().get_size(),
2791 (touch_width, touch_height)
2792 );
2793 }
2794
2795 #[test]
virtio_switches()2796 fn virtio_switches() {
2797 let mut config = Config::default();
2798 config
2799 .executable_path
2800 .replace(Executable::Kernel(PathBuf::from("kernel")));
2801 set_argument(&mut config, "switches", Some("/dev/switches-test")).unwrap();
2802 validate_arguments(&mut config).unwrap();
2803 assert_eq!(
2804 config.virtio_switches.unwrap(),
2805 PathBuf::from("/dev/switches-test")
2806 );
2807 }
2808
2809 #[cfg(feature = "gpu")]
2810 #[test]
parse_gpu_options_default_vulkan_support()2811 fn parse_gpu_options_default_vulkan_support() {
2812 assert!(
2813 !parse_gpu_options(Some("backend=virglrenderer"))
2814 .unwrap()
2815 .use_vulkan
2816 );
2817
2818 #[cfg(feature = "gfxstream")]
2819 assert!(
2820 parse_gpu_options(Some("backend=gfxstream"))
2821 .unwrap()
2822 .use_vulkan
2823 );
2824 }
2825
2826 #[cfg(feature = "gpu")]
2827 #[test]
parse_gpu_options_with_vulkan_specified()2828 fn parse_gpu_options_with_vulkan_specified() {
2829 assert!(parse_gpu_options(Some("vulkan=true")).unwrap().use_vulkan);
2830 assert!(
2831 parse_gpu_options(Some("backend=virglrenderer,vulkan=true"))
2832 .unwrap()
2833 .use_vulkan
2834 );
2835 assert!(
2836 parse_gpu_options(Some("vulkan=true,backend=virglrenderer"))
2837 .unwrap()
2838 .use_vulkan
2839 );
2840 assert!(!parse_gpu_options(Some("vulkan=false")).unwrap().use_vulkan);
2841 assert!(
2842 !parse_gpu_options(Some("backend=virglrenderer,vulkan=false"))
2843 .unwrap()
2844 .use_vulkan
2845 );
2846 assert!(
2847 !parse_gpu_options(Some("vulkan=false,backend=virglrenderer"))
2848 .unwrap()
2849 .use_vulkan
2850 );
2851 assert!(parse_gpu_options(Some("backend=virglrenderer,vulkan=invalid_value")).is_err());
2852 assert!(parse_gpu_options(Some("vulkan=invalid_value,backend=virglrenderer")).is_err());
2853 }
2854
2855 #[cfg(all(feature = "gpu", feature = "gfxstream"))]
2856 #[test]
parse_gpu_options_gfxstream_with_syncfd_specified()2857 fn parse_gpu_options_gfxstream_with_syncfd_specified() {
2858 assert!(
2859 parse_gpu_options(Some("backend=gfxstream,syncfd=true"))
2860 .unwrap()
2861 .gfxstream_use_syncfd
2862 );
2863 assert!(
2864 parse_gpu_options(Some("syncfd=true,backend=gfxstream"))
2865 .unwrap()
2866 .gfxstream_use_syncfd
2867 );
2868 assert!(
2869 !parse_gpu_options(Some("backend=gfxstream,syncfd=false"))
2870 .unwrap()
2871 .gfxstream_use_syncfd
2872 );
2873 assert!(
2874 !parse_gpu_options(Some("syncfd=false,backend=gfxstream"))
2875 .unwrap()
2876 .gfxstream_use_syncfd
2877 );
2878 assert!(parse_gpu_options(Some("backend=gfxstream,syncfd=invalid_value")).is_err());
2879 assert!(parse_gpu_options(Some("syncfd=invalid_value,backend=gfxstream")).is_err());
2880 }
2881
2882 #[cfg(all(feature = "gpu", feature = "gfxstream"))]
2883 #[test]
parse_gpu_options_not_gfxstream_with_syncfd_specified()2884 fn parse_gpu_options_not_gfxstream_with_syncfd_specified() {
2885 assert!(parse_gpu_options(Some("backend=virglrenderer,syncfd=true")).is_err());
2886 assert!(parse_gpu_options(Some("syncfd=true,backend=virglrenderer")).is_err());
2887 }
2888
2889 #[test]
parse_battery_vaild()2890 fn parse_battery_vaild() {
2891 parse_battery_options(Some("type=goldfish")).expect("parse should have succeded");
2892 }
2893
2894 #[test]
parse_battery_vaild_no_type()2895 fn parse_battery_vaild_no_type() {
2896 parse_battery_options(None).expect("parse should have succeded");
2897 }
2898
2899 #[test]
parse_battery_invaild_parameter()2900 fn parse_battery_invaild_parameter() {
2901 parse_battery_options(Some("tyep=goldfish")).expect_err("parse should have failed");
2902 }
2903
2904 #[test]
parse_battery_invaild_type_value()2905 fn parse_battery_invaild_type_value() {
2906 parse_battery_options(Some("type=xxx")).expect_err("parse should have failed");
2907 }
2908 }
2909