• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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