• 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::convert::TryFrom;
11 use std::default::Default;
12 use std::fs::{File, OpenOptions};
13 use std::io::{BufRead, BufReader};
14 use std::ops::Deref;
15 #[cfg(feature = "direct")]
16 use std::ops::RangeInclusive;
17 use std::path::{Path, PathBuf};
18 use std::str::FromStr;
19 use std::string::String;
20 use std::thread::sleep;
21 use std::time::Duration;
22 
23 use arch::{set_default_serial_parameters, Pstore, VcpuAffinity};
24 use base::{debug, error, getpid, info, kill_process_group, pagesize, reap_child, syslog, warn};
25 #[cfg(all(feature = "gpu", feature = "virgl_renderer_next"))]
26 use crosvm::platform::GpuRenderServerParameters;
27 #[cfg(feature = "direct")]
28 use crosvm::{argument::parse_hex_or_decimal, DirectIoOption, HostPcieRootPortParameters};
29 use crosvm::{
30     argument::{self, print_help, set_arguments, Argument},
31     platform, BindMount, Config, Executable, FileBackedMappingParameters, GidMap, SharedDir,
32     TouchDeviceOption, VfioCommand, VhostUserFsOption, VhostUserOption, VhostUserWlOption,
33     VvuOption,
34 };
35 use devices::serial_device::{SerialHardware, SerialParameters};
36 use devices::virtio::block::block::DiskOption;
37 #[cfg(feature = "audio_cras")]
38 use devices::virtio::snd::cras_backend::Error as CrasSndError;
39 #[cfg(feature = "audio_cras")]
40 use devices::virtio::vhost::user::device::run_cras_snd_device;
41 use devices::virtio::vhost::user::device::{
42     run_block_device, run_console_device, run_fs_device, run_net_device, run_vsock_device,
43     run_wl_device,
44 };
45 use devices::virtio::vhost::vsock::VhostVsockDeviceParameter;
46 #[cfg(any(feature = "video-decoder", feature = "video-encoder"))]
47 use devices::virtio::VideoBackendType;
48 #[cfg(feature = "gpu")]
49 use devices::virtio::{
50     gpu::{
51         GpuDisplayParameters, GpuMode, GpuParameters, DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH,
52     },
53     vhost::user::device::run_gpu_device,
54 };
55 #[cfg(feature = "direct")]
56 use devices::BusRange;
57 #[cfg(feature = "audio")]
58 use devices::{Ac97Backend, Ac97Parameters};
59 use devices::{PciAddress, PciClassCode, StubPciParameters};
60 use disk::{self, QcowFile};
61 #[cfg(feature = "composite-disk")]
62 use disk::{
63     create_composite_disk, create_disk_file, create_zero_filler, ImagePartitionType, PartitionInfo,
64 };
65 use hypervisor::ProtectionType;
66 use serde_keyvalue::from_key_values;
67 use uuid::Uuid;
68 use vm_control::{
69     client::{
70         do_modify_battery, do_usb_attach, do_usb_detach, do_usb_list, handle_request, vms_request,
71         ModifyUsbError, ModifyUsbResult,
72     },
73     BalloonControlCommand, BatteryType, DiskControlCommand, UsbControlResult, VmRequest,
74     VmResponse,
75 };
76 
77 #[cfg(feature = "scudo")]
78 #[global_allocator]
79 static ALLOCATOR: scudo::GlobalScudoAllocator = scudo::GlobalScudoAllocator;
80 
executable_is_plugin(executable: &Option<Executable>) -> bool81 fn executable_is_plugin(executable: &Option<Executable>) -> bool {
82     matches!(executable, Some(Executable::Plugin(_)))
83 }
84 
85 // Wait for all children to exit. Return true if they have all exited, false
86 // otherwise.
wait_all_children() -> bool87 fn wait_all_children() -> bool {
88     const CHILD_WAIT_MAX_ITER: isize = 100;
89     const CHILD_WAIT_MS: u64 = 10;
90     for _ in 0..CHILD_WAIT_MAX_ITER {
91         loop {
92             match reap_child() {
93                 Ok(0) => break,
94                 // We expect ECHILD which indicates that there were no children left.
95                 Err(e) if e.errno() == libc::ECHILD => return true,
96                 Err(e) => {
97                     warn!("error while waiting for children: {}", e);
98                     return false;
99                 }
100                 // We reaped one child, so continue reaping.
101                 _ => {}
102             }
103         }
104         // There's no timeout option for waitpid which reap_child calls internally, so our only
105         // recourse is to sleep while waiting for the children to exit.
106         sleep(Duration::from_millis(CHILD_WAIT_MS));
107     }
108 
109     // If we've made it to this point, not all of the children have exited.
110     false
111 }
112 
113 /// 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>>114 fn parse_cpu_set(s: &str) -> argument::Result<Vec<usize>> {
115     let mut cpuset = Vec::new();
116     for part in s.split(',') {
117         let range: Vec<&str> = part.split('-').collect();
118         if range.is_empty() || range.len() > 2 {
119             return Err(argument::Error::InvalidValue {
120                 value: part.to_owned(),
121                 expected: String::from("invalid list syntax"),
122             });
123         }
124         let first_cpu: usize = range[0]
125             .parse()
126             .map_err(|_| argument::Error::InvalidValue {
127                 value: part.to_owned(),
128                 expected: String::from("CPU index must be a non-negative integer"),
129             })?;
130         let last_cpu: usize = if range.len() == 2 {
131             range[1]
132                 .parse()
133                 .map_err(|_| argument::Error::InvalidValue {
134                     value: part.to_owned(),
135                     expected: String::from("CPU index must be a non-negative integer"),
136                 })?
137         } else {
138             first_cpu
139         };
140 
141         if last_cpu < first_cpu {
142             return Err(argument::Error::InvalidValue {
143                 value: part.to_owned(),
144                 expected: String::from("CPU ranges must be from low to high"),
145             });
146         }
147 
148         for cpu in first_cpu..=last_cpu {
149             cpuset.push(cpu);
150         }
151     }
152     Ok(cpuset)
153 }
154 
155 /// Parse a list of guest to host CPU mappings.
156 ///
157 /// Each mapping consists of a single guest CPU index mapped to one or more host CPUs in the form
158 /// accepted by `parse_cpu_set`:
159 ///
160 ///  `<GUEST-CPU>=<HOST-CPU-SET>[:<GUEST-CPU>=<HOST-CPU-SET>[:...]]`
parse_cpu_affinity(s: &str) -> argument::Result<VcpuAffinity>161 fn parse_cpu_affinity(s: &str) -> argument::Result<VcpuAffinity> {
162     if s.contains('=') {
163         let mut affinity_map = BTreeMap::new();
164         for cpu_pair in s.split(':') {
165             let assignment: Vec<&str> = cpu_pair.split('=').collect();
166             if assignment.len() != 2 {
167                 return Err(argument::Error::InvalidValue {
168                     value: cpu_pair.to_owned(),
169                     expected: String::from("invalid VCPU assignment syntax"),
170                 });
171             }
172             let guest_cpu = assignment[0]
173                 .parse()
174                 .map_err(|_| argument::Error::InvalidValue {
175                     value: assignment[0].to_owned(),
176                     expected: String::from("CPU index must be a non-negative integer"),
177                 })?;
178             let host_cpu_set = parse_cpu_set(assignment[1])?;
179             if affinity_map.insert(guest_cpu, host_cpu_set).is_some() {
180                 return Err(argument::Error::InvalidValue {
181                     value: cpu_pair.to_owned(),
182                     expected: String::from("VCPU index must be unique"),
183                 });
184             }
185         }
186         Ok(VcpuAffinity::PerVcpu(affinity_map))
187     } else {
188         Ok(VcpuAffinity::Global(parse_cpu_set(s)?))
189     }
190 }
191 
parse_cpu_capacity(s: &str, cpu_capacity: &mut BTreeMap<usize, u32>) -> argument::Result<()>192 fn parse_cpu_capacity(s: &str, cpu_capacity: &mut BTreeMap<usize, u32>) -> argument::Result<()> {
193     for cpu_pair in s.split(',') {
194         let assignment: Vec<&str> = cpu_pair.split('=').collect();
195         if assignment.len() != 2 {
196             return Err(argument::Error::InvalidValue {
197                 value: cpu_pair.to_owned(),
198                 expected: String::from("invalid CPU capacity syntax"),
199             });
200         }
201         let cpu = assignment[0]
202             .parse()
203             .map_err(|_| argument::Error::InvalidValue {
204                 value: assignment[0].to_owned(),
205                 expected: String::from("CPU index must be a non-negative integer"),
206             })?;
207         let capacity = assignment[1]
208             .parse()
209             .map_err(|_| argument::Error::InvalidValue {
210                 value: assignment[1].to_owned(),
211                 expected: String::from("CPU capacity must be a non-negative integer"),
212             })?;
213         if cpu_capacity.insert(cpu, capacity).is_some() {
214             return Err(argument::Error::InvalidValue {
215                 value: cpu_pair.to_owned(),
216                 expected: String::from("CPU index must be unique"),
217             });
218         }
219     }
220     Ok(())
221 }
222 
223 #[cfg(feature = "gpu")]
parse_gpu_options(s: Option<&str>, gpu_params: &mut GpuParameters) -> argument::Result<()>224 fn parse_gpu_options(s: Option<&str>, gpu_params: &mut GpuParameters) -> argument::Result<()> {
225     #[cfg(feature = "gfxstream")]
226     let mut vulkan_specified = false;
227     #[cfg(feature = "gfxstream")]
228     let mut syncfd_specified = false;
229     #[cfg(feature = "gfxstream")]
230     let mut angle_specified = false;
231 
232     let mut display_w: Option<u32> = None;
233     let mut display_h: Option<u32> = None;
234 
235     if let Some(s) = s {
236         let opts = s
237             .split(',')
238             .map(|frag| frag.split('='))
239             .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
240 
241         for (k, v) in opts {
242             match k {
243                 // Deprecated: Specifying --gpu=<mode> Not great as the mode can be set multiple
244                 // times if the user specifies several modes (--gpu=2d,virglrenderer,gfxstream)
245                 "2d" | "2D" => {
246                     gpu_params.mode = GpuMode::Mode2D;
247                 }
248                 "3d" | "3D" | "virglrenderer" => {
249                     gpu_params.mode = GpuMode::ModeVirglRenderer;
250                 }
251                 #[cfg(feature = "gfxstream")]
252                 "gfxstream" => {
253                     gpu_params.mode = GpuMode::ModeGfxstream;
254                 }
255                 // Preferred: Specifying --gpu,backend=<mode>
256                 "backend" => match v {
257                     "2d" | "2D" => {
258                         gpu_params.mode = GpuMode::Mode2D;
259                     }
260                     "3d" | "3D" | "virglrenderer" => {
261                         gpu_params.mode = GpuMode::ModeVirglRenderer;
262                     }
263                     #[cfg(feature = "gfxstream")]
264                     "gfxstream" => {
265                         gpu_params.mode = GpuMode::ModeGfxstream;
266                     }
267                     _ => {
268                         return Err(argument::Error::InvalidValue {
269                             value: v.to_string(),
270                             expected: String::from(
271                                 "gpu parameter 'backend' should be one of (2d|virglrenderer|gfxstream)",
272                             ),
273                         });
274                     }
275                 },
276                 "egl" => match v {
277                     "true" | "" => {
278                         gpu_params.renderer_use_egl = true;
279                     }
280                     "false" => {
281                         gpu_params.renderer_use_egl = false;
282                     }
283                     _ => {
284                         return Err(argument::Error::InvalidValue {
285                             value: v.to_string(),
286                             expected: String::from("gpu parameter 'egl' should be a boolean"),
287                         });
288                     }
289                 },
290                 "gles" => match v {
291                     "true" | "" => {
292                         gpu_params.renderer_use_gles = true;
293                     }
294                     "false" => {
295                         gpu_params.renderer_use_gles = false;
296                     }
297                     _ => {
298                         return Err(argument::Error::InvalidValue {
299                             value: v.to_string(),
300                             expected: String::from("gpu parameter 'gles' should be a boolean"),
301                         });
302                     }
303                 },
304                 "glx" => match v {
305                     "true" | "" => {
306                         gpu_params.renderer_use_glx = true;
307                     }
308                     "false" => {
309                         gpu_params.renderer_use_glx = false;
310                     }
311                     _ => {
312                         return Err(argument::Error::InvalidValue {
313                             value: v.to_string(),
314                             expected: String::from("gpu parameter 'glx' should be a boolean"),
315                         });
316                     }
317                 },
318                 "surfaceless" => match v {
319                     "true" | "" => {
320                         gpu_params.renderer_use_surfaceless = true;
321                     }
322                     "false" => {
323                         gpu_params.renderer_use_surfaceless = false;
324                     }
325                     _ => {
326                         return Err(argument::Error::InvalidValue {
327                             value: v.to_string(),
328                             expected: String::from(
329                                 "gpu parameter 'surfaceless' should be a boolean",
330                             ),
331                         });
332                     }
333                 },
334                 #[cfg(feature = "gfxstream")]
335                 "syncfd" => {
336                     syncfd_specified = true;
337                     match v {
338                         "true" | "" => {
339                             gpu_params.gfxstream_use_syncfd = true;
340                         }
341                         "false" => {
342                             gpu_params.gfxstream_use_syncfd = false;
343                         }
344                         _ => {
345                             return Err(argument::Error::InvalidValue {
346                                 value: v.to_string(),
347                                 expected: String::from(
348                                     "gpu parameter 'syncfd' should be a boolean",
349                                 ),
350                             });
351                         }
352                     }
353                 }
354                 #[cfg(feature = "gfxstream")]
355                 "angle" => {
356                     angle_specified = true;
357                     match v {
358                         "true" | "" => {
359                             gpu_params.gfxstream_use_guest_angle = true;
360                         }
361                         "false" => {
362                             gpu_params.gfxstream_use_guest_angle = false;
363                         }
364                         _ => {
365                             return Err(argument::Error::InvalidValue {
366                                 value: v.to_string(),
367                                 expected: String::from("gpu parameter 'angle' should be a boolean"),
368                             });
369                         }
370                     }
371                 }
372                 "vulkan" => {
373                     #[cfg(feature = "gfxstream")]
374                     {
375                         vulkan_specified = true;
376                     }
377                     match v {
378                         "true" | "" => {
379                             gpu_params.use_vulkan = true;
380                         }
381                         "false" => {
382                             gpu_params.use_vulkan = false;
383                         }
384                         _ => {
385                             return Err(argument::Error::InvalidValue {
386                                 value: v.to_string(),
387                                 expected: String::from(
388                                     "gpu parameter 'vulkan' should be a boolean",
389                                 ),
390                             });
391                         }
392                     }
393                 }
394                 "width" => {
395                     let width = v
396                         .parse::<u32>()
397                         .map_err(|_| argument::Error::InvalidValue {
398                             value: v.to_string(),
399                             expected: String::from("gpu parameter 'width' must be a valid integer"),
400                         })?;
401                     display_w = Some(width);
402                 }
403                 "height" => {
404                     let height = v
405                         .parse::<u32>()
406                         .map_err(|_| argument::Error::InvalidValue {
407                             value: v.to_string(),
408                             expected: String::from(
409                                 "gpu parameter 'height' must be a valid integer",
410                             ),
411                         })?;
412                     display_h = Some(height);
413                 }
414                 "cache-path" => gpu_params.cache_path = Some(v.to_string()),
415                 "cache-size" => gpu_params.cache_size = Some(v.to_string()),
416                 "udmabuf" => match v {
417                     "true" | "" => {
418                         gpu_params.udmabuf = true;
419                     }
420                     "false" => {
421                         gpu_params.udmabuf = false;
422                     }
423                     _ => {
424                         return Err(argument::Error::InvalidValue {
425                             value: v.to_string(),
426                             expected: String::from("gpu parameter 'udmabuf' should be a boolean"),
427                         });
428                     }
429                 },
430                 "" => {}
431                 _ => {
432                     return Err(argument::Error::UnknownArgument(format!(
433                         "gpu parameter {}",
434                         k
435                     )));
436                 }
437             }
438         }
439     }
440 
441     if display_w.is_some() || display_h.is_some() {
442         if display_w.is_none() || display_h.is_none() {
443             return Err(argument::Error::InvalidValue {
444                 value: s.unwrap_or("").to_string(),
445                 expected: String::from(
446                     "gpu must include both 'width' and 'height' if either is supplied",
447                 ),
448             });
449         }
450 
451         gpu_params.displays.push(GpuDisplayParameters {
452             width: display_w.unwrap(),
453             height: display_h.unwrap(),
454         });
455     }
456 
457     #[cfg(feature = "gfxstream")]
458     {
459         if !vulkan_specified && gpu_params.mode == GpuMode::ModeGfxstream {
460             gpu_params.use_vulkan = true;
461         }
462 
463         if syncfd_specified || angle_specified {
464             match gpu_params.mode {
465                 GpuMode::ModeGfxstream => {}
466                 _ => {
467                     return Err(argument::Error::UnknownArgument(
468                         "gpu parameter syncfd and angle are only supported for gfxstream backend"
469                             .to_string(),
470                     ));
471                 }
472             }
473         }
474     }
475 
476     Ok(())
477 }
478 
479 #[cfg(any(feature = "video-decoder", feature = "video-encoder"))]
parse_video_options(s: Option<&str>) -> argument::Result<VideoBackendType>480 fn parse_video_options(s: Option<&str>) -> argument::Result<VideoBackendType> {
481     const VALID_VIDEO_BACKENDS: &[&str] = &[
482         #[cfg(feature = "libvda")]
483         "libvda",
484     ];
485 
486     match s {
487         None => {
488             cfg_if::cfg_if! {
489                 if #[cfg(feature = "libvda")] {
490                     Ok(VideoBackendType::Libvda)
491                 }
492             }
493         }
494         #[cfg(feature = "libvda")]
495         Some("libvda") => Ok(VideoBackendType::Libvda),
496         #[cfg(feature = "libvda")]
497         Some("libvda-vd") => Ok(VideoBackendType::LibvdaVd),
498         Some(s) => Err(argument::Error::InvalidValue {
499             value: s.to_owned(),
500             expected: format!("should be one of ({})", VALID_VIDEO_BACKENDS.join("|")),
501         }),
502     }
503 }
504 
505 #[cfg(feature = "gpu")]
parse_gpu_display_options( s: Option<&str>, gpu_params: &mut GpuParameters, ) -> argument::Result<()>506 fn parse_gpu_display_options(
507     s: Option<&str>,
508     gpu_params: &mut GpuParameters,
509 ) -> argument::Result<()> {
510     let mut display_w: Option<u32> = None;
511     let mut display_h: Option<u32> = None;
512 
513     if let Some(s) = s {
514         let opts = s
515             .split(',')
516             .map(|frag| frag.split('='))
517             .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
518 
519         for (k, v) in opts {
520             match k {
521                 "width" => {
522                     let width = v
523                         .parse::<u32>()
524                         .map_err(|_| argument::Error::InvalidValue {
525                             value: v.to_string(),
526                             expected: String::from("gpu parameter 'width' must be a valid integer"),
527                         })?;
528                     display_w = Some(width);
529                 }
530                 "height" => {
531                     let height = v
532                         .parse::<u32>()
533                         .map_err(|_| argument::Error::InvalidValue {
534                             value: v.to_string(),
535                             expected: String::from(
536                                 "gpu parameter 'height' must be a valid integer",
537                             ),
538                         })?;
539                     display_h = Some(height);
540                 }
541                 "" => {}
542                 _ => {
543                     return Err(argument::Error::UnknownArgument(format!(
544                         "gpu-display parameter {}",
545                         k
546                     )));
547                 }
548             }
549         }
550     }
551 
552     if display_w.is_none() || display_h.is_none() {
553         return Err(argument::Error::InvalidValue {
554             value: s.unwrap_or("").to_string(),
555             expected: String::from("gpu-display must include both 'width' and 'height'"),
556         });
557     }
558 
559     gpu_params.displays.push(GpuDisplayParameters {
560         width: display_w.unwrap(),
561         height: display_h.unwrap(),
562     });
563 
564     Ok(())
565 }
566 
567 #[cfg(all(feature = "gpu", feature = "virgl_renderer_next"))]
parse_gpu_render_server_options(s: Option<&str>) -> argument::Result<GpuRenderServerParameters>568 fn parse_gpu_render_server_options(s: Option<&str>) -> argument::Result<GpuRenderServerParameters> {
569     let mut path: Option<PathBuf> = None;
570     let mut cache_path = None;
571     let mut cache_size = None;
572 
573     if let Some(s) = s {
574         let opts = s
575             .split(',')
576             .map(|frag| frag.split('='))
577             .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
578 
579         for (k, v) in opts {
580             match k {
581                 "path" => {
582                     path =
583                         Some(
584                             PathBuf::from_str(v).map_err(|e| argument::Error::InvalidValue {
585                                 value: v.to_string(),
586                                 expected: e.to_string(),
587                             })?,
588                         )
589                 }
590                 "cache-path" => cache_path = Some(v.to_string()),
591                 "cache-size" => cache_size = Some(v.to_string()),
592                 "" => {}
593                 _ => {
594                     return Err(argument::Error::UnknownArgument(format!(
595                         "gpu-render-server parameter {}",
596                         k
597                     )));
598                 }
599             }
600         }
601     }
602 
603     if let Some(p) = path {
604         Ok(GpuRenderServerParameters {
605             path: p,
606             cache_path,
607             cache_size,
608         })
609     } else {
610         Err(argument::Error::InvalidValue {
611             value: s.unwrap_or("").to_string(),
612             expected: String::from("gpu-render-server must include 'path'"),
613         })
614     }
615 }
616 
617 #[cfg(feature = "audio")]
parse_ac97_options(s: &str) -> argument::Result<Ac97Parameters>618 fn parse_ac97_options(s: &str) -> argument::Result<Ac97Parameters> {
619     let mut ac97_params: Ac97Parameters = Default::default();
620 
621     let opts = s
622         .split(',')
623         .map(|frag| frag.split('='))
624         .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
625 
626     for (k, v) in opts {
627         match k {
628             "backend" => {
629                 ac97_params.backend =
630                     v.parse::<Ac97Backend>()
631                         .map_err(|e| argument::Error::InvalidValue {
632                             value: v.to_string(),
633                             expected: e.to_string(),
634                         })?;
635             }
636             "capture" => {
637                 ac97_params.capture = v.parse::<bool>().map_err(|e| {
638                     argument::Error::Syntax(format!("invalid capture option: {}", e))
639                 })?;
640             }
641             #[cfg(feature = "audio_cras")]
642             "client_type" => {
643                 ac97_params
644                     .set_client_type(v)
645                     .map_err(|e| argument::Error::InvalidValue {
646                         value: v.to_string(),
647                         expected: e.to_string(),
648                     })?;
649             }
650             #[cfg(feature = "audio_cras")]
651             "socket_type" => {
652                 ac97_params
653                     .set_socket_type(v)
654                     .map_err(|e| argument::Error::InvalidValue {
655                         value: v.to_string(),
656                         expected: e.to_string(),
657                     })?;
658             }
659             #[cfg(any(target_os = "linux", target_os = "android"))]
660             "server" => {
661                 ac97_params.vios_server_path =
662                     Some(
663                         PathBuf::from_str(v).map_err(|e| argument::Error::InvalidValue {
664                             value: v.to_string(),
665                             expected: e.to_string(),
666                         })?,
667                     );
668             }
669             _ => {
670                 return Err(argument::Error::UnknownArgument(format!(
671                     "unknown ac97 parameter {}",
672                     k
673                 )));
674             }
675         }
676     }
677 
678     // server is required for and exclusive to vios backend
679     #[cfg(any(target_os = "linux", target_os = "android"))]
680     match ac97_params.backend {
681         Ac97Backend::VIOS => {
682             if ac97_params.vios_server_path.is_none() {
683                 return Err(argument::Error::ExpectedArgument(String::from(
684                     "server argument is required for VIOS backend",
685                 )));
686             }
687         }
688         _ => {
689             if ac97_params.vios_server_path.is_some() {
690                 return Err(argument::Error::UnexpectedValue(String::from(
691                     "server argument is exclusive to the VIOS backend",
692                 )));
693             }
694         }
695     }
696 
697     Ok(ac97_params)
698 }
699 
700 enum MsrAction {
701     Invalid,
702     /// Read MSR value from host CPU0 regardless of current vcpu.
703     ReadFromCPU0,
704 }
705 
parse_userspace_msr_options(value: &str) -> argument::Result<u32>706 fn parse_userspace_msr_options(value: &str) -> argument::Result<u32> {
707     // TODO(b/215297064): Implement different type of operations, such
708     // as write or reading from the correct CPU.
709     let mut options = argument::parse_key_value_options("userspace-msr", value, ',');
710     let index: u32 = options
711         .next()
712         .ok_or(argument::Error::ExpectedValue(String::from(
713             "userspace-msr: expected index",
714         )))?
715         .key_numeric()?;
716     let mut msr_config = MsrAction::Invalid;
717     for opt in options {
718         match opt.key() {
719             "action" => match opt.value()? {
720                 "r0" => msr_config = MsrAction::ReadFromCPU0,
721                 _ => return Err(opt.invalid_value_err(String::from("bad action"))),
722             },
723             _ => return Err(opt.invalid_key_err()),
724         }
725     }
726 
727     match msr_config {
728         MsrAction::ReadFromCPU0 => Ok(index),
729         _ => Err(argument::Error::UnknownArgument(
730             "userspace-msr action not specified".to_string(),
731         )),
732     }
733 }
734 
parse_serial_options(s: &str) -> argument::Result<SerialParameters>735 fn parse_serial_options(s: &str) -> argument::Result<SerialParameters> {
736     let serial_setting: SerialParameters =
737         from_key_values(s).map_err(|e| argument::Error::ConfigParserError(e.to_string()))?;
738 
739     if serial_setting.stdin && serial_setting.input.is_some() {
740         return Err(argument::Error::TooManyArguments(
741             "Cannot specify both stdin and input options".to_string(),
742         ));
743     }
744     if serial_setting.num < 1 {
745         return Err(argument::Error::InvalidValue {
746             value: serial_setting.num.to_string(),
747             expected: String::from("Serial port num must be at least 1"),
748         });
749     }
750 
751     if serial_setting.hardware == SerialHardware::Serial && serial_setting.num > 4 {
752         return Err(argument::Error::InvalidValue {
753             value: serial_setting.num.to_string(),
754             expected: String::from("Serial port num must be 4 or less"),
755         });
756     }
757 
758     Ok(serial_setting)
759 }
760 
parse_plugin_mount_option(value: &str) -> argument::Result<BindMount>761 fn parse_plugin_mount_option(value: &str) -> argument::Result<BindMount> {
762     let components: Vec<&str> = value.split(':').collect();
763     if components.is_empty() || components.len() > 3 || components[0].is_empty() {
764         return Err(argument::Error::InvalidValue {
765             value: value.to_owned(),
766             expected: String::from(
767                 "`plugin-mount` should be in a form of: <src>[:[<dst>][:<writable>]]",
768             ),
769         });
770     }
771 
772     let src = PathBuf::from(components[0]);
773     if src.is_relative() {
774         return Err(argument::Error::InvalidValue {
775             value: components[0].to_owned(),
776             expected: String::from("the source path for `plugin-mount` must be absolute"),
777         });
778     }
779     if !src.exists() {
780         return Err(argument::Error::InvalidValue {
781             value: components[0].to_owned(),
782             expected: String::from("the source path for `plugin-mount` does not exist"),
783         });
784     }
785 
786     let dst = PathBuf::from(match components.get(1) {
787         None | Some(&"") => components[0],
788         Some(path) => path,
789     });
790     if dst.is_relative() {
791         return Err(argument::Error::InvalidValue {
792             value: components[1].to_owned(),
793             expected: String::from("the destination path for `plugin-mount` must be absolute"),
794         });
795     }
796 
797     let writable: bool = match components.get(2) {
798         None => false,
799         Some(s) => s.parse().map_err(|_| argument::Error::InvalidValue {
800             value: components[2].to_owned(),
801             expected: String::from("the <writable> component for `plugin-mount` is not valid bool"),
802         })?,
803     };
804 
805     Ok(BindMount { src, dst, writable })
806 }
807 
parse_plugin_gid_map_option(value: &str) -> argument::Result<GidMap>808 fn parse_plugin_gid_map_option(value: &str) -> argument::Result<GidMap> {
809     let components: Vec<&str> = value.split(':').collect();
810     if components.is_empty() || components.len() > 3 || components[0].is_empty() {
811         return Err(argument::Error::InvalidValue {
812             value: value.to_owned(),
813             expected: String::from(
814                 "`plugin-gid-map` must have exactly 3 components: <inner>[:[<outer>][:<count>]]",
815             ),
816         });
817     }
818 
819     let inner: libc::gid_t = components[0]
820         .parse()
821         .map_err(|_| argument::Error::InvalidValue {
822             value: components[0].to_owned(),
823             expected: String::from("the <inner> component for `plugin-gid-map` is not valid gid"),
824         })?;
825 
826     let outer: libc::gid_t = match components.get(1) {
827         None | Some(&"") => inner,
828         Some(s) => s.parse().map_err(|_| argument::Error::InvalidValue {
829             value: components[1].to_owned(),
830             expected: String::from("the <outer> component for `plugin-gid-map` is not valid gid"),
831         })?,
832     };
833 
834     let count: u32 = match components.get(2) {
835         None => 1,
836         Some(s) => s.parse().map_err(|_| argument::Error::InvalidValue {
837             value: components[2].to_owned(),
838             expected: String::from(
839                 "the <count> component for `plugin-gid-map` is not valid number",
840             ),
841         })?,
842     };
843 
844     Ok(GidMap {
845         inner,
846         outer,
847         count,
848     })
849 }
850 
parse_battery_options(s: Option<&str>) -> argument::Result<BatteryType>851 fn parse_battery_options(s: Option<&str>) -> argument::Result<BatteryType> {
852     let mut battery_type: BatteryType = Default::default();
853 
854     if let Some(s) = s {
855         let opts = s
856             .split(',')
857             .map(|frag| frag.split('='))
858             .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
859 
860         for (k, v) in opts {
861             match k {
862                 "type" => match v.parse::<BatteryType>() {
863                     Ok(type_) => battery_type = type_,
864                     Err(e) => {
865                         return Err(argument::Error::InvalidValue {
866                             value: v.to_string(),
867                             expected: e.to_string(),
868                         });
869                     }
870                 },
871                 "" => {}
872                 _ => {
873                     return Err(argument::Error::UnknownArgument(format!(
874                         "battery parameter {}",
875                         k
876                     )));
877                 }
878             }
879         }
880     }
881 
882     Ok(battery_type)
883 }
884 
885 #[cfg(feature = "direct")]
parse_direct_io_options(s: Option<&str>) -> argument::Result<DirectIoOption>886 fn parse_direct_io_options(s: Option<&str>) -> argument::Result<DirectIoOption> {
887     let s = s.ok_or(argument::Error::ExpectedValue(String::from(
888         "expected path@range[,range] value",
889     )))?;
890     let parts: Vec<&str> = s.splitn(2, '@').collect();
891     if parts.len() != 2 {
892         return Err(argument::Error::InvalidValue {
893             value: s.to_string(),
894             expected: String::from("missing port range, use /path@X-Y,Z,.. syntax"),
895         });
896     }
897     let path = PathBuf::from(parts[0]);
898     if !path.exists() {
899         return Err(argument::Error::InvalidValue {
900             value: parts[0].to_owned(),
901             expected: String::from("the path does not exist"),
902         });
903     };
904     let ranges: argument::Result<Vec<BusRange>> = parts[1]
905         .split(',')
906         .map(|frag| frag.split('-'))
907         .map(|mut range| {
908             let base = range
909                 .next()
910                 .map(|v| parse_hex_or_decimal(v))
911                 .map_or(Ok(None), |r| r.map(Some));
912             let last = range
913                 .next()
914                 .map(|v| parse_hex_or_decimal(v))
915                 .map_or(Ok(None), |r| r.map(Some));
916             (base, last)
917         })
918         .map(|range| match range {
919             (Ok(Some(base)), Ok(None)) => Ok(BusRange { base, len: 1 }),
920             (Ok(Some(base)), Ok(Some(last))) => Ok(BusRange {
921                 base,
922                 len: last.saturating_sub(base).saturating_add(1),
923             }),
924             (Err(_), _) => Err(argument::Error::InvalidValue {
925                 value: s.to_owned(),
926                 expected: String::from("invalid base range value"),
927             }),
928             (_, Err(_)) => Err(argument::Error::InvalidValue {
929                 value: s.to_owned(),
930                 expected: String::from("invalid last range value"),
931             }),
932             _ => Err(argument::Error::InvalidValue {
933                 value: s.to_owned(),
934                 expected: String::from("invalid range format"),
935             }),
936         })
937         .collect();
938     Ok(DirectIoOption {
939         path,
940         ranges: ranges?,
941     })
942 }
943 
parse_stub_pci_parameters(s: Option<&str>) -> argument::Result<StubPciParameters>944 fn parse_stub_pci_parameters(s: Option<&str>) -> argument::Result<StubPciParameters> {
945     let s = s.ok_or(argument::Error::ExpectedValue(String::from(
946         "stub-pci-device configuration expected",
947     )))?;
948 
949     let mut options = argument::parse_key_value_options("stub-pci-device", s, ',');
950     let addr = options
951         .next()
952         .ok_or(argument::Error::ExpectedValue(String::from(
953             "stub-pci-device: expected device address",
954         )))?
955         .key();
956     let mut params = StubPciParameters {
957         address: PciAddress::from_str(addr).map_err(|e| argument::Error::InvalidValue {
958             value: addr.to_owned(),
959             expected: format!("stub-pci-device: expected PCI address: {}", e),
960         })?,
961         vendor_id: 0,
962         device_id: 0,
963         class: PciClassCode::Other,
964         subclass: 0,
965         programming_interface: 0,
966         subsystem_device_id: 0,
967         subsystem_vendor_id: 0,
968         revision_id: 0,
969     };
970     for opt in options {
971         match opt.key() {
972             "vendor" => params.vendor_id = opt.parse_numeric::<u16>()?,
973             "device" => params.device_id = opt.parse_numeric::<u16>()?,
974             "class" => {
975                 let class = opt.parse_numeric::<u32>()?;
976                 params.class = PciClassCode::try_from((class >> 16) as u8)
977                     .map_err(|_| opt.invalid_value_err(String::from("Unknown class code")))?;
978                 params.subclass = (class >> 8) as u8;
979                 params.programming_interface = class as u8;
980             }
981             "multifunction" => {} // Ignore but allow the multifunction option for compatibility.
982             "subsystem_vendor" => params.subsystem_vendor_id = opt.parse_numeric::<u16>()?,
983             "subsystem_device" => params.subsystem_device_id = opt.parse_numeric::<u16>()?,
984             "revision" => params.revision_id = opt.parse_numeric::<u8>()?,
985             _ => return Err(opt.invalid_key_err()),
986         }
987     }
988 
989     Ok(params)
990 }
991 
parse_file_backed_mapping(s: Option<&str>) -> argument::Result<FileBackedMappingParameters>992 fn parse_file_backed_mapping(s: Option<&str>) -> argument::Result<FileBackedMappingParameters> {
993     let s = s.ok_or(argument::Error::ExpectedValue(String::from(
994         "file-backed-mapping: memory mapping option value required",
995     )))?;
996 
997     let mut address = None;
998     let mut size = None;
999     let mut path = None;
1000     let mut offset = None;
1001     let mut writable = false;
1002     let mut sync = false;
1003     let mut align = false;
1004     for opt in argument::parse_key_value_options("file-backed-mapping", s, ',') {
1005         match opt.key() {
1006             "addr" => address = Some(opt.parse_numeric::<u64>()?),
1007             "size" => size = Some(opt.parse_numeric::<u64>()?),
1008             "path" => path = Some(PathBuf::from(opt.value()?)),
1009             "offset" => offset = Some(opt.parse_numeric::<u64>()?),
1010             "ro" => writable = !opt.parse_or::<bool>(true)?,
1011             "rw" => writable = opt.parse_or::<bool>(true)?,
1012             "sync" => sync = opt.parse_or::<bool>(true)?,
1013             "align" => align = opt.parse_or::<bool>(true)?,
1014             _ => return Err(opt.invalid_key_err()),
1015         }
1016     }
1017 
1018     let (address, path, size) = match (address, path, size) {
1019         (Some(a), Some(p), Some(s)) => (a, p, s),
1020         _ => {
1021             return Err(argument::Error::ExpectedValue(String::from(
1022                 "file-backed-mapping: address, size, and path parameters are required",
1023             )))
1024         }
1025     };
1026 
1027     let pagesize_mask = pagesize() as u64 - 1;
1028     let aligned_address = address & !pagesize_mask;
1029     let aligned_size = ((address + size + pagesize_mask) & !pagesize_mask) - aligned_address;
1030 
1031     if !align && (aligned_address != address || aligned_size != size) {
1032         return Err(argument::Error::InvalidValue {
1033             value: s.to_owned(),
1034             expected: String::from("addr and size parameters must be page size aligned"),
1035         });
1036     }
1037 
1038     Ok(FileBackedMappingParameters {
1039         address: aligned_address,
1040         size: aligned_size,
1041         path,
1042         offset: offset.unwrap_or(0),
1043         writable,
1044         sync,
1045     })
1046 }
1047 
set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::Result<()>1048 fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::Result<()> {
1049     match name {
1050         "" => {
1051             if cfg.executable_path.is_some() {
1052                 return Err(argument::Error::TooManyArguments(format!(
1053                     "A VM executable was already specified: {:?}",
1054                     cfg.executable_path
1055                 )));
1056             }
1057             let kernel_path = PathBuf::from(value.unwrap());
1058             if !kernel_path.exists() {
1059                 return Err(argument::Error::InvalidValue {
1060                     value: value.unwrap().to_owned(),
1061                     expected: String::from("this kernel path does not exist"),
1062                 });
1063             }
1064             cfg.executable_path = Some(Executable::Kernel(kernel_path));
1065         }
1066         "kvm-device" => {
1067             let kvm_device_path = PathBuf::from(value.unwrap());
1068             if !kvm_device_path.exists() {
1069                 return Err(argument::Error::InvalidValue {
1070                     value: value.unwrap().to_owned(),
1071                     expected: String::from("this kvm device path does not exist"),
1072                 });
1073             }
1074 
1075             cfg.kvm_device_path = kvm_device_path;
1076         }
1077         "vhost-vsock-fd" => {
1078             if cfg.vhost_vsock_device.is_some() {
1079                 return Err(argument::Error::InvalidValue {
1080                     value: value.unwrap().to_owned(),
1081                     expected: String::from("A vhost-vsock device was already specified"),
1082                 });
1083             }
1084             cfg.vhost_vsock_device = Some(VhostVsockDeviceParameter::Fd(
1085                 value
1086                     .unwrap()
1087                     .parse()
1088                     .map_err(|_| argument::Error::InvalidValue {
1089                         value: value.unwrap().to_owned(),
1090                         expected: String::from(
1091                             "this value for `vhost-vsock-fd` needs to be integer",
1092                         ),
1093                     })?,
1094             ));
1095         }
1096         "vhost-vsock-device" => {
1097             if cfg.vhost_vsock_device.is_some() {
1098                 return Err(argument::Error::InvalidValue {
1099                     value: value.unwrap().to_owned(),
1100                     expected: String::from("A vhost-vsock device was already specified"),
1101                 });
1102             }
1103             let vhost_vsock_device_path = PathBuf::from(value.unwrap());
1104             if !vhost_vsock_device_path.exists() {
1105                 return Err(argument::Error::InvalidValue {
1106                     value: value.unwrap().to_owned(),
1107                     expected: String::from("this vhost-vsock device path does not exist"),
1108                 });
1109             }
1110 
1111             cfg.vhost_vsock_device = Some(VhostVsockDeviceParameter::Path(vhost_vsock_device_path));
1112         }
1113         "vhost-net-device" => {
1114             let vhost_net_device_path = PathBuf::from(value.unwrap());
1115             if !vhost_net_device_path.exists() {
1116                 return Err(argument::Error::InvalidValue {
1117                     value: value.unwrap().to_owned(),
1118                     expected: String::from("this vhost-vsock device path does not exist"),
1119                 });
1120             }
1121 
1122             cfg.vhost_net_device_path = vhost_net_device_path;
1123         }
1124         "android-fstab" => {
1125             if cfg.android_fstab.is_some()
1126                 && !cfg.android_fstab.as_ref().unwrap().as_os_str().is_empty()
1127             {
1128                 return Err(argument::Error::TooManyArguments(
1129                     "expected exactly one android fstab path".to_owned(),
1130                 ));
1131             } else {
1132                 let android_fstab = PathBuf::from(value.unwrap());
1133                 if !android_fstab.exists() {
1134                     return Err(argument::Error::InvalidValue {
1135                         value: value.unwrap().to_owned(),
1136                         expected: String::from("this android fstab path does not exist"),
1137                     });
1138                 }
1139                 cfg.android_fstab = Some(android_fstab);
1140             }
1141         }
1142         "params" => {
1143             cfg.params.push(value.unwrap().to_owned());
1144         }
1145         "cpus" => {
1146             if cfg.vcpu_count.is_some() {
1147                 return Err(argument::Error::TooManyArguments(
1148                     "`cpus` already given".to_owned(),
1149                 ));
1150             }
1151             cfg.vcpu_count =
1152                 Some(
1153                     value
1154                         .unwrap()
1155                         .parse()
1156                         .map_err(|_| argument::Error::InvalidValue {
1157                             value: value.unwrap().to_owned(),
1158                             expected: String::from("this value for `cpus` needs to be integer"),
1159                         })?,
1160                 )
1161         }
1162         "cpu-affinity" => {
1163             if cfg.vcpu_affinity.is_some() {
1164                 return Err(argument::Error::TooManyArguments(
1165                     "`cpu-affinity` already given".to_owned(),
1166                 ));
1167             }
1168             cfg.vcpu_affinity = Some(parse_cpu_affinity(value.unwrap())?);
1169         }
1170         "cpu-cluster" => {
1171             cfg.cpu_clusters.push(parse_cpu_set(value.unwrap())?);
1172         }
1173         "cpu-capacity" => {
1174             parse_cpu_capacity(value.unwrap(), &mut cfg.cpu_capacity)?;
1175         }
1176         "per-vm-core-scheduling" => {
1177             cfg.per_vm_core_scheduling = true;
1178         }
1179         "vcpu-cgroup-path" => {
1180             let vcpu_cgroup_path = PathBuf::from(value.unwrap());
1181             if !vcpu_cgroup_path.exists() {
1182                 return Err(argument::Error::InvalidValue {
1183                     value: value.unwrap().to_owned(),
1184                     expected: String::from("This vcpu_cgroup_path path does not exist"),
1185                 });
1186             }
1187 
1188             cfg.vcpu_cgroup_path = Some(vcpu_cgroup_path);
1189         }
1190         #[cfg(feature = "audio_cras")]
1191         "cras-snd" => {
1192             cfg.cras_snds.push(
1193                 value
1194                     .unwrap()
1195                     .parse()
1196                     .map_err(|e: CrasSndError| argument::Error::Syntax(e.to_string()))?,
1197             );
1198         }
1199         "no-smt" => {
1200             cfg.no_smt = true;
1201         }
1202         "rt-cpus" => {
1203             if !cfg.rt_cpus.is_empty() {
1204                 return Err(argument::Error::TooManyArguments(
1205                     "`rt-cpus` already given".to_owned(),
1206                 ));
1207             }
1208             cfg.rt_cpus = parse_cpu_set(value.unwrap())?;
1209         }
1210         "delay-rt" => {
1211             cfg.delay_rt = true;
1212         }
1213         "mem" => {
1214             if cfg.memory.is_some() {
1215                 return Err(argument::Error::TooManyArguments(
1216                     "`mem` already given".to_owned(),
1217                 ));
1218             }
1219             cfg.memory =
1220                 Some(
1221                     value
1222                         .unwrap()
1223                         .parse()
1224                         .map_err(|_| argument::Error::InvalidValue {
1225                             value: value.unwrap().to_owned(),
1226                             expected: String::from("this value for `mem` needs to be integer"),
1227                         })?,
1228                 )
1229         }
1230         #[cfg(target_arch = "aarch64")]
1231         "swiotlb" => {
1232             if cfg.swiotlb.is_some() {
1233                 return Err(argument::Error::TooManyArguments(
1234                     "`swiotlb` already given".to_owned(),
1235                 ));
1236             }
1237             cfg.swiotlb =
1238                 Some(
1239                     value
1240                         .unwrap()
1241                         .parse()
1242                         .map_err(|_| argument::Error::InvalidValue {
1243                             value: value.unwrap().to_owned(),
1244                             expected: String::from("this value for `swiotlb` needs to be integer"),
1245                         })?,
1246                 )
1247         }
1248         "hugepages" => {
1249             cfg.hugepages = true;
1250         }
1251         #[cfg(feature = "audio")]
1252         "ac97" => {
1253             let ac97_params = parse_ac97_options(value.unwrap())?;
1254             // Add kernel parameters related to the intel8x0 driver for ac97 devices once.
1255             if cfg.ac97_parameters.is_empty() {
1256                 // Set `inside_vm=1` to save some register read ops in the driver.
1257                 cfg.params.push("snd_intel8x0.inside_vm=1".to_string());
1258                 // Set `ac97_clock=48000` to save intel8x0_measure_ac97_clock call in the driver.
1259                 cfg.params.push("snd_intel8x0.ac97_clock=48000".to_string());
1260             }
1261             cfg.ac97_parameters.push(ac97_params);
1262         }
1263         #[cfg(feature = "audio")]
1264         "sound" => {
1265             let client_path = PathBuf::from(value.unwrap());
1266             cfg.sound = Some(client_path);
1267         }
1268         "serial" => {
1269             let serial_params = parse_serial_options(value.unwrap())?;
1270             let num = serial_params.num;
1271             let key = (serial_params.hardware, num);
1272             if cfg.serial_parameters.contains_key(&key) {
1273                 return Err(argument::Error::TooManyArguments(format!(
1274                     "serial hardware {} num {}",
1275                     serial_params.hardware, num,
1276                 )));
1277             }
1278 
1279             if serial_params.console {
1280                 for params in cfg.serial_parameters.values() {
1281                     if params.console {
1282                         return Err(argument::Error::TooManyArguments(format!(
1283                             "{} device {} already set as console",
1284                             params.hardware, params.num,
1285                         )));
1286                     }
1287                 }
1288             }
1289 
1290             if serial_params.earlycon {
1291                 // Only SerialHardware::Serial supports earlycon= currently.
1292                 match serial_params.hardware {
1293                     SerialHardware::Serial => {}
1294                     _ => {
1295                         return Err(argument::Error::InvalidValue {
1296                             value: serial_params.hardware.to_string(),
1297                             expected: String::from("earlycon not supported for hardware"),
1298                         });
1299                     }
1300                 }
1301                 for params in cfg.serial_parameters.values() {
1302                     if params.earlycon {
1303                         return Err(argument::Error::TooManyArguments(format!(
1304                             "{} device {} already set as earlycon",
1305                             params.hardware, params.num,
1306                         )));
1307                     }
1308                 }
1309             }
1310 
1311             if serial_params.stdin {
1312                 if let Some(previous_stdin) = cfg.serial_parameters.values().find(|sp| sp.stdin) {
1313                     return Err(argument::Error::TooManyArguments(format!(
1314                         "{} device {} already connected to standard input",
1315                         previous_stdin.hardware, previous_stdin.num,
1316                     )));
1317                 }
1318             }
1319 
1320             cfg.serial_parameters.insert(key, serial_params);
1321         }
1322         "syslog-tag" => {
1323             if cfg.syslog_tag.is_some() {
1324                 return Err(argument::Error::TooManyArguments(
1325                     "`syslog-tag` already given".to_owned(),
1326                 ));
1327             }
1328             syslog::set_proc_name(value.unwrap());
1329             cfg.syslog_tag = Some(value.unwrap().to_owned());
1330         }
1331         "root" | "rwroot" | "disk" | "rwdisk" => {
1332             let value = value.ok_or(argument::Error::ExpectedArgument(
1333                 "path to the disk image is missing".to_owned(),
1334             ))?;
1335             let mut params: DiskOption = from_key_values(value).map_err(|e| {
1336                 argument::Error::Syntax(format!("while parsing \"{}\" parameter: {}", name, e))
1337             })?;
1338 
1339             if !name.starts_with("rw") {
1340                 params.read_only = true;
1341             }
1342 
1343             let disk_path = &params.path;
1344             if !disk_path.exists() {
1345                 return Err(argument::Error::InvalidValue {
1346                     value: disk_path.to_string_lossy().into_owned(),
1347                     expected: String::from("this disk path does not exist"),
1348                 });
1349             }
1350             if name.ends_with("root") {
1351                 if cfg.disks.len() >= 26 {
1352                     return Err(argument::Error::TooManyArguments(
1353                         "ran out of letters for to assign to root disk".to_owned(),
1354                     ));
1355                 }
1356                 cfg.params.push(format!(
1357                     "root=/dev/vd{} {}",
1358                     char::from(b'a' + cfg.disks.len() as u8),
1359                     if params.read_only { "ro" } else { "rw" }
1360                 ));
1361             }
1362 
1363             cfg.disks.push(params);
1364         }
1365         "pmem-device" | "rw-pmem-device" => {
1366             let disk_path = PathBuf::from(value.unwrap());
1367             if !disk_path.exists() {
1368                 return Err(argument::Error::InvalidValue {
1369                     value: value.unwrap().to_owned(),
1370                     expected: String::from("this disk path does not exist"),
1371                 });
1372             }
1373 
1374             cfg.pmem_devices.push(DiskOption {
1375                 path: disk_path,
1376                 read_only: !name.starts_with("rw"),
1377                 sparse: false,
1378                 o_direct: false,
1379                 block_size: base::pagesize() as u32,
1380                 id: None,
1381             });
1382         }
1383         "pstore" => {
1384             if cfg.pstore.is_some() {
1385                 return Err(argument::Error::TooManyArguments(
1386                     "`pstore` already given".to_owned(),
1387                 ));
1388             }
1389 
1390             let value = value.unwrap();
1391             let components: Vec<&str> = value.split(',').collect();
1392             if components.len() != 2 {
1393                 return Err(argument::Error::InvalidValue {
1394                     value: value.to_owned(),
1395                     expected: String::from(
1396                         "pstore must have exactly 2 components: path=<path>,size=<size>",
1397                     ),
1398                 });
1399             }
1400             cfg.pstore = Some(Pstore {
1401                 path: {
1402                     if components[0].len() <= 5 || !components[0].starts_with("path=") {
1403                         return Err(argument::Error::InvalidValue {
1404                             value: components[0].to_owned(),
1405                             expected: String::from("pstore path must follow with `path=`"),
1406                         });
1407                     };
1408                     PathBuf::from(&components[0][5..])
1409                 },
1410                 size: {
1411                     if components[1].len() <= 5 || !components[1].starts_with("size=") {
1412                         return Err(argument::Error::InvalidValue {
1413                             value: components[1].to_owned(),
1414                             expected: String::from("pstore size must follow with `size=`"),
1415                         });
1416                     };
1417                     components[1][5..]
1418                         .parse()
1419                         .map_err(|_| argument::Error::InvalidValue {
1420                             value: value.to_owned(),
1421                             expected: String::from("pstore size must be an integer"),
1422                         })?
1423                 },
1424             });
1425         }
1426         "host_ip" => {
1427             if cfg.host_ip.is_some() {
1428                 return Err(argument::Error::TooManyArguments(
1429                     "`host_ip` already given".to_owned(),
1430                 ));
1431             }
1432             cfg.host_ip =
1433                 Some(
1434                     value
1435                         .unwrap()
1436                         .parse()
1437                         .map_err(|_| argument::Error::InvalidValue {
1438                             value: value.unwrap().to_owned(),
1439                             expected: String::from("`host_ip` needs to be in the form \"x.x.x.x\""),
1440                         })?,
1441                 )
1442         }
1443         "netmask" => {
1444             if cfg.netmask.is_some() {
1445                 return Err(argument::Error::TooManyArguments(
1446                     "`netmask` already given".to_owned(),
1447                 ));
1448             }
1449             cfg.netmask =
1450                 Some(
1451                     value
1452                         .unwrap()
1453                         .parse()
1454                         .map_err(|_| argument::Error::InvalidValue {
1455                             value: value.unwrap().to_owned(),
1456                             expected: String::from("`netmask` needs to be in the form \"x.x.x.x\""),
1457                         })?,
1458                 )
1459         }
1460         "mac" => {
1461             if cfg.mac_address.is_some() {
1462                 return Err(argument::Error::TooManyArguments(
1463                     "`mac` already given".to_owned(),
1464                 ));
1465             }
1466             cfg.mac_address =
1467                 Some(
1468                     value
1469                         .unwrap()
1470                         .parse()
1471                         .map_err(|_| argument::Error::InvalidValue {
1472                             value: value.unwrap().to_owned(),
1473                             expected: String::from(
1474                                 "`mac` needs to be in the form \"XX:XX:XX:XX:XX:XX\"",
1475                             ),
1476                         })?,
1477                 )
1478         }
1479         "net-vq-pairs" => {
1480             if cfg.net_vq_pairs.is_some() {
1481                 return Err(argument::Error::TooManyArguments(
1482                     "`net-vq-pairs` already given".to_owned(),
1483                 ));
1484             }
1485             cfg.net_vq_pairs =
1486                 Some(
1487                     value
1488                         .unwrap()
1489                         .parse()
1490                         .map_err(|_| argument::Error::InvalidValue {
1491                             value: value.unwrap().to_owned(),
1492                             expected: String::from(
1493                                 "this value for `net-vq-pairs` needs to be integer",
1494                             ),
1495                         })?,
1496                 )
1497         }
1498 
1499         "wayland-sock" => {
1500             let mut components = value.unwrap().split(',');
1501             let path =
1502                 PathBuf::from(
1503                     components
1504                         .next()
1505                         .ok_or_else(|| argument::Error::InvalidValue {
1506                             value: value.unwrap().to_owned(),
1507                             expected: String::from("missing socket path"),
1508                         })?,
1509                 );
1510             let mut name = "";
1511             for c in components {
1512                 let mut kv = c.splitn(2, '=');
1513                 let (kind, value) = match (kv.next(), kv.next()) {
1514                     (Some(kind), Some(value)) => (kind, value),
1515                     _ => {
1516                         return Err(argument::Error::InvalidValue {
1517                             value: c.to_owned(),
1518                             expected: String::from("option must be of the form `kind=value`"),
1519                         })
1520                     }
1521                 };
1522                 match kind {
1523                     "name" => name = value,
1524                     _ => {
1525                         return Err(argument::Error::InvalidValue {
1526                             value: kind.to_owned(),
1527                             expected: String::from("unrecognized option"),
1528                         })
1529                     }
1530                 }
1531             }
1532             if cfg.wayland_socket_paths.contains_key(name) {
1533                 return Err(argument::Error::TooManyArguments(format!(
1534                     "wayland socket name already used: '{}'",
1535                     name
1536                 )));
1537             }
1538             cfg.wayland_socket_paths.insert(name.to_string(), path);
1539         }
1540         #[cfg(feature = "wl-dmabuf")]
1541         "wayland-dmabuf" => {}
1542         "x-display" => {
1543             if cfg.x_display.is_some() {
1544                 return Err(argument::Error::TooManyArguments(
1545                     "`x-display` already given".to_owned(),
1546                 ));
1547             }
1548             cfg.x_display = Some(value.unwrap().to_owned());
1549         }
1550         "display-window-keyboard" => {
1551             cfg.display_window_keyboard = true;
1552         }
1553         "display-window-mouse" => {
1554             cfg.display_window_mouse = true;
1555         }
1556         "socket" => {
1557             if cfg.socket_path.is_some() {
1558                 return Err(argument::Error::TooManyArguments(
1559                     "`socket` already given".to_owned(),
1560                 ));
1561             }
1562             let mut socket_path = PathBuf::from(value.unwrap());
1563             if socket_path.is_dir() {
1564                 socket_path.push(format!("crosvm-{}.sock", getpid()));
1565             }
1566             if socket_path.exists() {
1567                 return Err(argument::Error::InvalidValue {
1568                     value: socket_path.to_string_lossy().into_owned(),
1569                     expected: String::from("this socket path already exists"),
1570                 });
1571             }
1572             cfg.socket_path = Some(socket_path);
1573         }
1574         "balloon-control" => {
1575             if cfg.balloon_control.is_some() {
1576                 return Err(argument::Error::TooManyArguments(
1577                     "`balloon-control` already given".to_owned(),
1578                 ));
1579             }
1580             let path = PathBuf::from(value.unwrap());
1581             if path.is_dir() || !path.exists() {
1582                 return Err(argument::Error::InvalidValue {
1583                     value: path.to_string_lossy().into_owned(),
1584                     expected: String::from("path is directory or missing"),
1585                 });
1586             }
1587             cfg.balloon_control = Some(path);
1588         }
1589         "disable-sandbox" => {
1590             cfg.jail_config = None;
1591         }
1592         "cid" => {
1593             if cfg.cid.is_some() {
1594                 return Err(argument::Error::TooManyArguments(
1595                     "`cid` alread given".to_owned(),
1596                 ));
1597             }
1598             cfg.cid = Some(
1599                 value
1600                     .unwrap()
1601                     .parse()
1602                     .map_err(|_| argument::Error::InvalidValue {
1603                         value: value.unwrap().to_owned(),
1604                         expected: String::from("this value for `cid` must be an unsigned integer"),
1605                     })?,
1606             );
1607         }
1608         "shared-dir" => {
1609             // This is formatted as multiple fields, each separated by ":". The first 2 fields are
1610             // fixed (src:tag).  The rest may appear in any order:
1611             //
1612             // * type=TYPE - must be one of "p9" or "fs" (default: p9)
1613             // * uidmap=UIDMAP - a uid map in the format "inner outer count[,inner outer count]"
1614             //   (default: "0 <current euid> 1")
1615             // * gidmap=GIDMAP - a gid map in the same format as uidmap
1616             //   (default: "0 <current egid> 1")
1617             // * privileged_quota_uids=UIDS - Space-separated list of privileged uid values. When
1618             //   performing quota-related operations, these UIDs are treated as if they have
1619             //   CAP_FOWNER.
1620             // * timeout=TIMEOUT - a timeout value in seconds, which indicates how long attributes
1621             //   and directory contents should be considered valid (default: 5)
1622             // * cache=CACHE - one of "never", "always", or "auto" (default: auto)
1623             // * writeback=BOOL - indicates whether writeback caching should be enabled (default: false)
1624             let param = value.unwrap();
1625             let mut components = param.split(':');
1626             let src =
1627                 PathBuf::from(
1628                     components
1629                         .next()
1630                         .ok_or_else(|| argument::Error::InvalidValue {
1631                             value: param.to_owned(),
1632                             expected: String::from("missing source path for `shared-dir`"),
1633                         })?,
1634                 );
1635             let tag = components
1636                 .next()
1637                 .ok_or_else(|| argument::Error::InvalidValue {
1638                     value: param.to_owned(),
1639                     expected: String::from("missing tag for `shared-dir`"),
1640                 })?
1641                 .to_owned();
1642 
1643             if !src.is_dir() {
1644                 return Err(argument::Error::InvalidValue {
1645                     value: param.to_owned(),
1646                     expected: String::from("source path for `shared-dir` must be a directory"),
1647                 });
1648             }
1649 
1650             let mut shared_dir = SharedDir {
1651                 src,
1652                 tag,
1653                 ..Default::default()
1654             };
1655             for opt in components {
1656                 let mut o = opt.splitn(2, '=');
1657                 let kind = o.next().ok_or_else(|| argument::Error::InvalidValue {
1658                     value: opt.to_owned(),
1659                     expected: String::from("`shared-dir` options must not be empty"),
1660                 })?;
1661                 let value = o.next().ok_or_else(|| argument::Error::InvalidValue {
1662                     value: opt.to_owned(),
1663                     expected: String::from("`shared-dir` options must be of the form `kind=value`"),
1664                 })?;
1665 
1666                 match kind {
1667                     "type" => {
1668                         shared_dir.kind =
1669                             value.parse().map_err(|_| argument::Error::InvalidValue {
1670                                 value: value.to_owned(),
1671                                 expected: String::from("`type` must be one of `fs` or `9p`"),
1672                             })?
1673                     }
1674                     "uidmap" => shared_dir.uid_map = value.into(),
1675                     "gidmap" => shared_dir.gid_map = value.into(),
1676                     #[cfg(feature = "chromeos")]
1677                     "privileged_quota_uids" => {
1678                         shared_dir.fs_cfg.privileged_quota_uids =
1679                             value.split(' ').map(|s| s.parse().unwrap()).collect();
1680                     }
1681                     "timeout" => {
1682                         let seconds = value.parse().map_err(|_| argument::Error::InvalidValue {
1683                             value: value.to_owned(),
1684                             expected: String::from("`timeout` must be an integer"),
1685                         })?;
1686 
1687                         let dur = Duration::from_secs(seconds);
1688                         shared_dir.fs_cfg.entry_timeout = dur;
1689                         shared_dir.fs_cfg.attr_timeout = dur;
1690                     }
1691                     "cache" => {
1692                         let policy = value.parse().map_err(|_| argument::Error::InvalidValue {
1693                             value: value.to_owned(),
1694                             expected: String::from(
1695                                 "`cache` must be one of `never`, `always`, or `auto`",
1696                             ),
1697                         })?;
1698                         shared_dir.fs_cfg.cache_policy = policy;
1699                     }
1700                     "writeback" => {
1701                         let writeback =
1702                             value.parse().map_err(|_| argument::Error::InvalidValue {
1703                                 value: value.to_owned(),
1704                                 expected: String::from("`writeback` must be a boolean"),
1705                             })?;
1706                         shared_dir.fs_cfg.writeback = writeback;
1707                     }
1708                     "rewrite-security-xattrs" => {
1709                         let rewrite_security_xattrs =
1710                             value.parse().map_err(|_| argument::Error::InvalidValue {
1711                                 value: value.to_owned(),
1712                                 expected: String::from(
1713                                     "`rewrite-security-xattrs` must be a boolean",
1714                                 ),
1715                             })?;
1716                         shared_dir.fs_cfg.rewrite_security_xattrs = rewrite_security_xattrs;
1717                     }
1718                     "ascii_casefold" => {
1719                         let ascii_casefold =
1720                             value.parse().map_err(|_| argument::Error::InvalidValue {
1721                                 value: value.to_owned(),
1722                                 expected: String::from("`ascii_casefold` must be a boolean"),
1723                             })?;
1724                         shared_dir.fs_cfg.ascii_casefold = ascii_casefold;
1725                         shared_dir.p9_cfg.ascii_casefold = ascii_casefold;
1726                     }
1727                     "dax" => {
1728                         let use_dax = value.parse().map_err(|_| argument::Error::InvalidValue {
1729                             value: value.to_owned(),
1730                             expected: String::from("`dax` must be a boolean"),
1731                         })?;
1732                         shared_dir.fs_cfg.use_dax = use_dax;
1733                     }
1734                     "posix_acl" => {
1735                         let posix_acl =
1736                             value.parse().map_err(|_| argument::Error::InvalidValue {
1737                                 value: value.to_owned(),
1738                                 expected: String::from("`posix_acl` must be a boolean"),
1739                             })?;
1740                         shared_dir.fs_cfg.posix_acl = posix_acl;
1741                     }
1742                     _ => {
1743                         return Err(argument::Error::InvalidValue {
1744                             value: kind.to_owned(),
1745                             expected: String::from("unrecognized option for `shared-dir`"),
1746                         })
1747                     }
1748                 }
1749             }
1750             cfg.shared_dirs.push(shared_dir);
1751         }
1752         "seccomp-policy-dir" => {
1753             if let Some(jail_config) = &mut cfg.jail_config {
1754                 // `value` is Some because we are in this match so it's safe to unwrap.
1755                 jail_config.seccomp_policy_dir = PathBuf::from(value.unwrap());
1756             }
1757         }
1758         "seccomp-log-failures" => {
1759             // A side-effect of this flag is to force the use of .policy files
1760             // instead of .bpf files (.bpf files are expected and assumed to be
1761             // compiled to fail an unpermitted action with "trap").
1762             // Normally crosvm will first attempt to use a .bpf file, and if
1763             // not present it will then try to use a .policy file.  It's up
1764             // to the build to decide which of these files is present for
1765             // crosvm to use (for CrOS the build will use .bpf files for
1766             // x64 builds and .policy files for arm/arm64 builds).
1767             //
1768             // This flag will likely work as expected for builds that use
1769             // .policy files.  For builds that only use .bpf files the initial
1770             // result when using this flag is likely to be a file-not-found
1771             // error (since the .policy files are not present).
1772             // For .bpf builds you can either 1) manually add the .policy files,
1773             // or 2) do not use this command-line parameter and instead
1774             // temporarily change the build by passing "log" rather than
1775             // "trap" as the "--default-action" to compile_seccomp_policy.py.
1776             if let Some(jail_config) = &mut cfg.jail_config {
1777                 jail_config.seccomp_log_failures = true;
1778             }
1779         }
1780         "plugin" => {
1781             if cfg.executable_path.is_some() {
1782                 return Err(argument::Error::TooManyArguments(format!(
1783                     "A VM executable was already specified: {:?}",
1784                     cfg.executable_path
1785                 )));
1786             }
1787             let plugin = PathBuf::from(value.unwrap().to_owned());
1788             if plugin.is_relative() {
1789                 return Err(argument::Error::InvalidValue {
1790                     value: plugin.to_string_lossy().into_owned(),
1791                     expected: String::from("the plugin path must be an absolute path"),
1792                 });
1793             }
1794             cfg.executable_path = Some(Executable::Plugin(plugin));
1795         }
1796         "plugin-root" => {
1797             cfg.plugin_root = Some(PathBuf::from(value.unwrap().to_owned()));
1798         }
1799         "plugin-mount" => {
1800             let mount = parse_plugin_mount_option(value.unwrap())?;
1801             cfg.plugin_mounts.push(mount);
1802         }
1803         "plugin-mount-file" => {
1804             let file = File::open(value.unwrap()).map_err(|_| argument::Error::InvalidValue {
1805                 value: value.unwrap().to_owned(),
1806                 expected: String::from("unable to open `plugin-mount-file` file"),
1807             })?;
1808             let reader = BufReader::new(file);
1809             for l in reader.lines() {
1810                 let line = l.unwrap();
1811                 let trimmed_line = line.split_once('#').map_or(&*line, |x| x.0).trim();
1812                 if !trimmed_line.is_empty() {
1813                     let mount = parse_plugin_mount_option(trimmed_line)?;
1814                     cfg.plugin_mounts.push(mount);
1815                 }
1816             }
1817         }
1818         "plugin-gid-map" => {
1819             let map = parse_plugin_gid_map_option(value.unwrap())?;
1820             cfg.plugin_gid_maps.push(map);
1821         }
1822         "plugin-gid-map-file" => {
1823             let file = File::open(value.unwrap()).map_err(|_| argument::Error::InvalidValue {
1824                 value: value.unwrap().to_owned(),
1825                 expected: String::from("unable to open `plugin-gid-map-file` file"),
1826             })?;
1827             let reader = BufReader::new(file);
1828             for l in reader.lines() {
1829                 let line = l.unwrap();
1830                 let trimmed_line = line.split_once('#').map_or(&*line, |x| x.0).trim();
1831                 if !trimmed_line.is_empty() {
1832                     let map = parse_plugin_gid_map_option(trimmed_line)?;
1833                     cfg.plugin_gid_maps.push(map);
1834                 }
1835             }
1836         }
1837         "vhost-net" => cfg.vhost_net = true,
1838         "tap-fd" => {
1839             cfg.tap_fd.push(
1840                 value
1841                     .unwrap()
1842                     .parse()
1843                     .map_err(|_| argument::Error::InvalidValue {
1844                         value: value.unwrap().to_owned(),
1845                         expected: String::from(
1846                             "this value for `tap-fd` must be an unsigned integer",
1847                         ),
1848                     })?,
1849             );
1850         }
1851         "tap-name" => {
1852             cfg.tap_name.push(value.unwrap().to_owned());
1853         }
1854         #[cfg(feature = "gpu")]
1855         "gpu" => {
1856             let gpu_parameters = cfg.gpu_parameters.get_or_insert_with(Default::default);
1857             parse_gpu_options(value, gpu_parameters)?;
1858         }
1859         #[cfg(feature = "gpu")]
1860         "gpu-display" => {
1861             let gpu_parameters = cfg.gpu_parameters.get_or_insert_with(Default::default);
1862             parse_gpu_display_options(value, gpu_parameters)?;
1863         }
1864         #[cfg(all(feature = "gpu", feature = "virgl_renderer_next"))]
1865         "gpu-render-server" => {
1866             cfg.gpu_render_server_parameters = Some(parse_gpu_render_server_options(value)?);
1867         }
1868         "software-tpm" => {
1869             cfg.software_tpm = true;
1870         }
1871         "single-touch" => {
1872             let mut it = value.unwrap().split(':');
1873 
1874             let mut single_touch_spec =
1875                 TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned()));
1876             if let Some(width) = it.next() {
1877                 single_touch_spec.set_width(width.trim().parse().unwrap());
1878             }
1879             if let Some(height) = it.next() {
1880                 single_touch_spec.set_height(height.trim().parse().unwrap());
1881             }
1882             cfg.virtio_single_touch.push(single_touch_spec);
1883         }
1884         "multi-touch" => {
1885             let mut it = value.unwrap().split(':');
1886 
1887             let mut multi_touch_spec =
1888                 TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned()));
1889             if let Some(width) = it.next() {
1890                 multi_touch_spec.set_width(width.trim().parse().unwrap());
1891             }
1892             if let Some(height) = it.next() {
1893                 multi_touch_spec.set_height(height.trim().parse().unwrap());
1894             }
1895             cfg.virtio_multi_touch.push(multi_touch_spec);
1896         }
1897         "trackpad" => {
1898             let mut it = value.unwrap().split(':');
1899 
1900             let mut trackpad_spec =
1901                 TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned()));
1902             if let Some(width) = it.next() {
1903                 trackpad_spec.set_width(width.trim().parse().unwrap());
1904             }
1905             if let Some(height) = it.next() {
1906                 trackpad_spec.set_height(height.trim().parse().unwrap());
1907             }
1908             cfg.virtio_trackpad.push(trackpad_spec);
1909         }
1910         "mouse" => {
1911             cfg.virtio_mice
1912                 .push(PathBuf::from(value.unwrap().to_owned()));
1913         }
1914         "keyboard" => {
1915             cfg.virtio_keyboard
1916                 .push(PathBuf::from(value.unwrap().to_owned()));
1917         }
1918         "switches" => {
1919             cfg.virtio_switches
1920                 .push(PathBuf::from(value.unwrap().to_owned()));
1921         }
1922         "evdev" => {
1923             let dev_path = PathBuf::from(value.unwrap());
1924             if !dev_path.exists() {
1925                 return Err(argument::Error::InvalidValue {
1926                     value: value.unwrap().to_owned(),
1927                     expected: String::from("this input device path does not exist"),
1928                 });
1929             }
1930             cfg.virtio_input_evdevs.push(dev_path);
1931         }
1932         "split-irqchip" => {
1933             cfg.split_irqchip = true;
1934         }
1935         "initrd" => {
1936             cfg.initrd_path = Some(PathBuf::from(value.unwrap().to_owned()));
1937         }
1938         "bios" => {
1939             if cfg.executable_path.is_some() {
1940                 return Err(argument::Error::TooManyArguments(format!(
1941                     "A VM executable was already specified: {:?}",
1942                     cfg.executable_path
1943                 )));
1944             }
1945             cfg.executable_path = Some(Executable::Bios(PathBuf::from(value.unwrap().to_owned())));
1946         }
1947         "vfio" | "vfio-platform" => {
1948             let vfio_type = name.parse().unwrap();
1949             let vfio_dev = VfioCommand::new(vfio_type, value.unwrap())?;
1950             cfg.vfio.push(vfio_dev);
1951         }
1952         "virtio-iommu" => {
1953             cfg.virtio_iommu = true;
1954         }
1955         #[cfg(feature = "video-decoder")]
1956         "video-decoder" => {
1957             cfg.video_dec = Some(parse_video_options(value)?);
1958         }
1959         #[cfg(feature = "video-encoder")]
1960         "video-encoder" => {
1961             cfg.video_enc = Some(parse_video_options(value)?);
1962         }
1963         "acpi-table" => {
1964             let acpi_table = PathBuf::from(value.unwrap());
1965             if !acpi_table.exists() {
1966                 return Err(argument::Error::InvalidValue {
1967                     value: value.unwrap().to_owned(),
1968                     expected: String::from("the acpi-table path does not exist"),
1969                 });
1970             }
1971             if !acpi_table.is_file() {
1972                 return Err(argument::Error::InvalidValue {
1973                     value: value.unwrap().to_owned(),
1974                     expected: String::from("the acpi-table path should be a file"),
1975                 });
1976             }
1977 
1978             cfg.acpi_tables.push(acpi_table);
1979         }
1980         "protected-vm" => {
1981             cfg.protected_vm = ProtectionType::Protected;
1982             // Balloon and USB devices only work for unprotected VMs.
1983             cfg.balloon = false;
1984             cfg.usb = false;
1985             // Protected VMs can't trust the RNG device, so don't provide it.
1986             cfg.rng = false;
1987         }
1988         "protected-vm-without-firmware" => {
1989             cfg.protected_vm = ProtectionType::ProtectedWithoutFirmware;
1990             // Balloon and USB devices only work for unprotected VMs.
1991             cfg.balloon = false;
1992             cfg.usb = false;
1993             // Protected VMs can't trust the RNG device, so don't provide it.
1994             cfg.rng = false;
1995         }
1996         "battery" => {
1997             let params = parse_battery_options(value)?;
1998             cfg.battery_type = Some(params);
1999         }
2000         #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
2001         "gdb" => {
2002             let port = value
2003                 .unwrap()
2004                 .parse()
2005                 .map_err(|_| argument::Error::InvalidValue {
2006                     value: value.unwrap().to_owned(),
2007                     expected: String::from("expected a valid port number"),
2008                 })?;
2009             cfg.gdb = Some(port);
2010         }
2011         "no-balloon" => {
2012             cfg.balloon = false;
2013         }
2014         "no-rng" => {
2015             cfg.rng = false;
2016         }
2017         "no-usb" => {
2018             cfg.usb = false;
2019         }
2020         "balloon_bias_mib" => {
2021             cfg.balloon_bias =
2022                 value
2023                     .unwrap()
2024                     .parse::<i64>()
2025                     .map_err(|_| argument::Error::InvalidValue {
2026                         value: value.unwrap().to_owned(),
2027                         expected: String::from("expected a valid ballon bias in MiB"),
2028                     })?
2029                     * 1024
2030                     * 1024; // cfg.balloon_bias is in bytes.
2031         }
2032         "vhost-user-blk" => cfg.vhost_user_blk.push(VhostUserOption {
2033             socket: PathBuf::from(value.unwrap()),
2034         }),
2035         "vhost-user-console" => cfg.vhost_user_console.push(VhostUserOption {
2036             socket: PathBuf::from(value.unwrap()),
2037         }),
2038         "vhost-user-gpu" => cfg.vhost_user_gpu.push(VhostUserOption {
2039             socket: PathBuf::from(value.unwrap()),
2040         }),
2041         "vhost-user-mac80211-hwsim" => {
2042             cfg.vhost_user_mac80211_hwsim = Some(VhostUserOption {
2043                 socket: PathBuf::from(value.unwrap()),
2044             });
2045         }
2046         "vhost-user-net" => cfg.vhost_user_net.push(VhostUserOption {
2047             socket: PathBuf::from(value.unwrap()),
2048         }),
2049         #[cfg(feature = "audio")]
2050         "vhost-user-snd" => cfg.vhost_user_snd.push(VhostUserOption {
2051             socket: PathBuf::from(value.unwrap()),
2052         }),
2053         "vhost-user-vsock" => cfg.vhost_user_vsock.push(VhostUserOption {
2054             socket: PathBuf::from(value.unwrap()),
2055         }),
2056         "vhost-user-wl" => {
2057             let mut components = value.unwrap().splitn(2, ":");
2058             let socket = components.next().map(PathBuf::from).ok_or_else(|| {
2059                 argument::Error::InvalidValue {
2060                     value: value.unwrap().to_owned(),
2061                     expected: String::from("missing socket path"),
2062                 }
2063             })?;
2064             let vm_tube = components.next().map(PathBuf::from).ok_or_else(|| {
2065                 argument::Error::InvalidValue {
2066                     value: value.unwrap().to_owned(),
2067                     expected: String::from("missing vm tube path"),
2068                 }
2069             })?;
2070             cfg.vhost_user_wl
2071                 .push(VhostUserWlOption { socket, vm_tube });
2072         }
2073         "vhost-user-fs" => {
2074             // (socket:tag)
2075             let param = value.unwrap();
2076             let mut components = param.split(':');
2077             let socket =
2078                 PathBuf::from(
2079                     components
2080                         .next()
2081                         .ok_or_else(|| argument::Error::InvalidValue {
2082                             value: param.to_owned(),
2083                             expected: String::from("missing socket path for `vhost-user-fs`"),
2084                         })?,
2085                 );
2086             let tag = components
2087                 .next()
2088                 .ok_or_else(|| argument::Error::InvalidValue {
2089                     value: param.to_owned(),
2090                     expected: String::from("missing tag for `vhost-user-fs`"),
2091                 })?
2092                 .to_owned();
2093             cfg.vhost_user_fs.push(VhostUserFsOption { socket, tag });
2094         }
2095         #[cfg(feature = "direct")]
2096         "direct-pmio" => {
2097             if cfg.direct_pmio.is_some() {
2098                 return Err(argument::Error::TooManyArguments(
2099                     "`direct_pmio` already given".to_owned(),
2100                 ));
2101             }
2102             cfg.direct_pmio = Some(parse_direct_io_options(value)?);
2103         }
2104         #[cfg(feature = "direct")]
2105         "direct-mmio" => {
2106             if cfg.direct_mmio.is_some() {
2107                 return Err(argument::Error::TooManyArguments(
2108                     "`direct_mmio` already given".to_owned(),
2109                 ));
2110             }
2111             cfg.direct_mmio = Some(parse_direct_io_options(value)?);
2112         }
2113         #[cfg(feature = "direct")]
2114         "direct-level-irq" => {
2115             cfg.direct_level_irq
2116                 .push(
2117                     value
2118                         .unwrap()
2119                         .parse()
2120                         .map_err(|_| argument::Error::InvalidValue {
2121                             value: value.unwrap().to_owned(),
2122                             expected: String::from(
2123                                 "this value for `direct-level-irq` must be an unsigned integer",
2124                             ),
2125                         })?,
2126                 );
2127         }
2128         #[cfg(feature = "direct")]
2129         "direct-edge-irq" => {
2130             cfg.direct_edge_irq
2131                 .push(
2132                     value
2133                         .unwrap()
2134                         .parse()
2135                         .map_err(|_| argument::Error::InvalidValue {
2136                             value: value.unwrap().to_owned(),
2137                             expected: String::from(
2138                                 "this value for `direct-edge-irq` must be an unsigned integer",
2139                             ),
2140                         })?,
2141                 );
2142         }
2143         #[cfg(feature = "direct")]
2144         "direct-wake-irq" => {
2145             cfg.direct_wake_irq
2146                 .push(
2147                     value
2148                         .unwrap()
2149                         .parse()
2150                         .map_err(|_| argument::Error::InvalidValue {
2151                             value: value.unwrap().to_owned(),
2152                             expected: String::from(
2153                                 "this value for `direct-wake-irq` must be an unsigned integer",
2154                             ),
2155                         })?,
2156                 );
2157         }
2158         #[cfg(feature = "direct")]
2159         "direct-gpe" => {
2160             cfg.direct_gpe.push(value.unwrap().parse().map_err(|_| {
2161                 argument::Error::InvalidValue {
2162                     value: value.unwrap().to_owned(),
2163                     expected: String::from(
2164                         "this value for `direct-gpe` must be an unsigned integer",
2165                     ),
2166                 }
2167             })?);
2168         }
2169         "dmi" => {
2170             if cfg.dmi_path.is_some() {
2171                 return Err(argument::Error::TooManyArguments(
2172                     "`dmi` already given".to_owned(),
2173                 ));
2174             }
2175             let dmi_path = PathBuf::from(value.unwrap());
2176             if !dmi_path.exists() {
2177                 return Err(argument::Error::InvalidValue {
2178                     value: value.unwrap().to_owned(),
2179                     expected: String::from("the dmi path does not exist"),
2180                 });
2181             }
2182             if !dmi_path.is_dir() {
2183                 return Err(argument::Error::InvalidValue {
2184                     value: value.unwrap().to_owned(),
2185                     expected: String::from("the dmi path should be directory"),
2186                 });
2187             }
2188             cfg.dmi_path = Some(dmi_path);
2189         }
2190         "no-legacy" => {
2191             cfg.no_legacy = true;
2192         }
2193         "userspace-msr" => {
2194             let index = parse_userspace_msr_options(value.unwrap())?;
2195             cfg.userspace_msr.insert(index);
2196         }
2197         #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
2198         "host-cpu-topology" => {
2199             cfg.host_cpu_topology = true;
2200         }
2201         "privileged-vm" => {
2202             cfg.privileged_vm = true;
2203         }
2204         "stub-pci-device" => {
2205             cfg.stub_pci_devices.push(parse_stub_pci_parameters(value)?);
2206         }
2207         "vvu-proxy" => {
2208             let opts: Vec<_> = value.unwrap().splitn(2, ',').collect();
2209             let socket = PathBuf::from(opts[0]);
2210             let mut vvu_opt = VvuOption {
2211                 socket,
2212                 addr: None,
2213                 uuid: Default::default(),
2214             };
2215 
2216             if let Some(kvs) = opts.get(1) {
2217                 for kv in argument::parse_key_value_options("vvu-proxy", kvs, ',') {
2218                     match kv.key() {
2219                         "addr" => {
2220                             let pci_address = kv.value()?;
2221                             if vvu_opt.addr.is_some() {
2222                                 return Err(argument::Error::TooManyArguments(
2223                                     "`addr` already given".to_owned(),
2224                                 ));
2225                             }
2226 
2227                             vvu_opt.addr =
2228                                 Some(PciAddress::from_str(pci_address).map_err(|e| {
2229                                     argument::Error::InvalidValue {
2230                                         value: pci_address.to_string(),
2231                                         expected: format!("vvu-proxy PCI address: {}", e),
2232                                     }
2233                                 })?);
2234                         }
2235                         "uuid" => {
2236                             let value = kv.value()?;
2237                             if vvu_opt.uuid.is_some() {
2238                                 return Err(argument::Error::TooManyArguments(
2239                                     "`uuid` already given".to_owned(),
2240                                 ));
2241                             }
2242                             let uuid = Uuid::parse_str(value).map_err(|e| {
2243                                 argument::Error::InvalidValue {
2244                                     value: value.to_string(),
2245                                     expected: format!("invalid UUID is given for vvu-proxy: {}", e),
2246                                 }
2247                             })?;
2248                             vvu_opt.uuid = Some(uuid);
2249                         }
2250                         _ => {
2251                             kv.invalid_key_err();
2252                         }
2253                     }
2254                 }
2255             }
2256 
2257             cfg.vvu_proxy.push(vvu_opt);
2258         }
2259         "coiommu" => {
2260             let mut params: devices::CoIommuParameters = Default::default();
2261             if let Some(v) = value {
2262                 let opts = v
2263                     .split(',')
2264                     .map(|frag| frag.splitn(2, '='))
2265                     .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
2266 
2267                 for (k, v) in opts {
2268                     match k {
2269                         "unpin_policy" => {
2270                             params.unpin_policy = v
2271                                 .parse::<devices::CoIommuUnpinPolicy>()
2272                                 .map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?
2273                         }
2274                         "unpin_interval" => {
2275                             params.unpin_interval =
2276                                 Duration::from_secs(v.parse::<u64>().map_err(|e| {
2277                                     argument::Error::UnknownArgument(format!("{}", e))
2278                                 })?)
2279                         }
2280                         "unpin_limit" => {
2281                             let limit = v
2282                                 .parse::<u64>()
2283                                 .map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?;
2284 
2285                             if limit == 0 {
2286                                 return Err(argument::Error::InvalidValue {
2287                                     value: v.to_owned(),
2288                                     expected: String::from("Please use non-zero unpin_limit value"),
2289                                 });
2290                             }
2291 
2292                             params.unpin_limit = Some(limit)
2293                         }
2294                         "unpin_gen_threshold" => {
2295                             params.unpin_gen_threshold = v
2296                                 .parse::<u64>()
2297                                 .map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?
2298                         }
2299                         _ => {
2300                             return Err(argument::Error::UnknownArgument(format!(
2301                                 "coiommu parameter {}",
2302                                 k
2303                             )));
2304                         }
2305                     }
2306                 }
2307             }
2308 
2309             if cfg.coiommu_param.is_some() {
2310                 return Err(argument::Error::TooManyArguments(
2311                     "coiommu param already given".to_owned(),
2312                 ));
2313             }
2314             cfg.coiommu_param = Some(params);
2315         }
2316         "file-backed-mapping" => {
2317             cfg.file_backed_mappings
2318                 .push(parse_file_backed_mapping(value)?);
2319         }
2320         "init-mem" => {
2321             if cfg.init_memory.is_some() {
2322                 return Err(argument::Error::TooManyArguments(
2323                     "`init-mem` already given".to_owned(),
2324                 ));
2325             }
2326             cfg.init_memory =
2327                 Some(
2328                     value
2329                         .unwrap()
2330                         .parse()
2331                         .map_err(|_| argument::Error::InvalidValue {
2332                             value: value.unwrap().to_owned(),
2333                             expected: String::from("this value for `init-mem` needs to be integer"),
2334                         })?,
2335                 )
2336         }
2337         #[cfg(feature = "direct")]
2338         "pcie-root-port" => {
2339             let opts: Vec<_> = value.unwrap().split(',').collect();
2340             if opts.len() > 2 {
2341                 return Err(argument::Error::TooManyArguments(
2342                     "pcie-root-port has maxmimum two arguments".to_owned(),
2343                 ));
2344             }
2345             let pcie_path = PathBuf::from(opts[0]);
2346             if !pcie_path.exists() {
2347                 return Err(argument::Error::InvalidValue {
2348                     value: opts[0].to_owned(),
2349                     expected: String::from("the pcie root port path does not exist"),
2350                 });
2351             }
2352             if !pcie_path.is_dir() {
2353                 return Err(argument::Error::InvalidValue {
2354                     value: opts[0].to_owned(),
2355                     expected: String::from("the pcie root port path should be directory"),
2356                 });
2357             }
2358 
2359             let hp_gpe = if opts.len() == 2 {
2360                 let gpes: Vec<&str> = opts[1].split('=').collect();
2361                 if gpes.len() != 2 || gpes[0] != "hp_gpe" {
2362                     return Err(argument::Error::InvalidValue {
2363                         value: opts[1].to_owned(),
2364                         expected: String::from("it should be hp_gpe=Num"),
2365                     });
2366                 }
2367                 match gpes[1].parse::<u32>() {
2368                     Ok(gpe) => Some(gpe),
2369                     Err(_) => {
2370                         return Err(argument::Error::InvalidValue {
2371                             value: gpes[1].to_owned(),
2372                             expected: String::from("host hp gpe must be a non-negative integer"),
2373                         });
2374                     }
2375                 }
2376             } else {
2377                 None
2378             };
2379 
2380             cfg.pcie_rp.push(HostPcieRootPortParameters {
2381                 host_path: pcie_path,
2382                 hp_gpe,
2383             });
2384         }
2385         "pivot-root" => {
2386             if let Some(jail_config) = &mut cfg.jail_config {
2387                 jail_config.pivot_root = PathBuf::from(value.unwrap());
2388             }
2389         }
2390         #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
2391         "s2idle" => {
2392             cfg.force_s2idle = true;
2393         }
2394         "strict-balloon" => {
2395             cfg.strict_balloon = true;
2396         }
2397         #[cfg(feature = "direct")]
2398         "mmio-address-range" => {
2399             let ranges: argument::Result<Vec<RangeInclusive<u64>>> = value
2400                 .unwrap()
2401                 .split(",")
2402                 .map(|s| {
2403                     let r: Vec<&str> = s.split("-").collect();
2404                     if r.len() != 2 {
2405                         return Err(argument::Error::InvalidValue {
2406                             value: s.to_string(),
2407                             expected: String::from("invalid range"),
2408                         });
2409                     }
2410                     let parse = |s: &str| -> argument::Result<u64> {
2411                         match parse_hex_or_decimal(s) {
2412                             Ok(v) => Ok(v),
2413                             Err(_) => {
2414                                 return Err(argument::Error::InvalidValue {
2415                                     value: s.to_owned(),
2416                                     expected: String::from("expected u64 value"),
2417                                 });
2418                             }
2419                         }
2420                     };
2421                     Ok(RangeInclusive::new(parse(r[0])?, parse(r[1])?))
2422                 })
2423                 .collect();
2424             cfg.mmio_address_ranges = ranges?;
2425         }
2426         #[cfg(target_os = "android")]
2427         "task-profiles" => {
2428             for name in value.unwrap().split(',') {
2429                 cfg.task_profiles.push(name.to_owned());
2430             }
2431         }
2432         "help" => return Err(argument::Error::PrintHelp),
2433         _ => unreachable!(),
2434     }
2435     Ok(())
2436 }
2437 
validate_arguments(cfg: &mut Config) -> std::result::Result<(), argument::Error>2438 fn validate_arguments(cfg: &mut Config) -> std::result::Result<(), argument::Error> {
2439     if cfg.executable_path.is_none() {
2440         return Err(argument::Error::ExpectedArgument("`KERNEL`".to_owned()));
2441     }
2442     if cfg.host_ip.is_some() || cfg.netmask.is_some() || cfg.mac_address.is_some() {
2443         if cfg.host_ip.is_none() {
2444             return Err(argument::Error::ExpectedArgument(
2445                 "`host_ip` missing from network config".to_owned(),
2446             ));
2447         }
2448         if cfg.netmask.is_none() {
2449             return Err(argument::Error::ExpectedArgument(
2450                 "`netmask` missing from network config".to_owned(),
2451             ));
2452         }
2453         if cfg.mac_address.is_none() {
2454             return Err(argument::Error::ExpectedArgument(
2455                 "`mac` missing from network config".to_owned(),
2456             ));
2457         }
2458     }
2459     if cfg.plugin_root.is_some() && !executable_is_plugin(&cfg.executable_path) {
2460         return Err(argument::Error::ExpectedArgument(
2461             "`plugin-root` requires `plugin`".to_owned(),
2462         ));
2463     }
2464     #[cfg(feature = "gpu")]
2465     {
2466         if let Some(gpu_parameters) = cfg.gpu_parameters.as_mut() {
2467             if gpu_parameters.displays.is_empty() {
2468                 gpu_parameters.displays.push(GpuDisplayParameters {
2469                     width: DEFAULT_DISPLAY_WIDTH,
2470                     height: DEFAULT_DISPLAY_HEIGHT,
2471                 });
2472             }
2473 
2474             let width = gpu_parameters.displays[0].width;
2475             let height = gpu_parameters.displays[0].height;
2476 
2477             if let Some(virtio_multi_touch) = cfg.virtio_multi_touch.first_mut() {
2478                 virtio_multi_touch.set_default_size(width, height);
2479             }
2480             if let Some(virtio_single_touch) = cfg.virtio_single_touch.first_mut() {
2481                 virtio_single_touch.set_default_size(width, height);
2482             }
2483         }
2484     }
2485     #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
2486     if cfg.gdb.is_some() && cfg.vcpu_count.unwrap_or(1) != 1 {
2487         return Err(argument::Error::ExpectedArgument(
2488             "`gdb` requires the number of vCPU to be 1".to_owned(),
2489         ));
2490     }
2491     if cfg.host_cpu_topology {
2492         if cfg.no_smt {
2493             return Err(argument::Error::ExpectedArgument(
2494                 "`host-cpu-topology` cannot be set at the same time as `no_smt`, since \
2495                 the smt of the Guest is the same as that of the Host when \
2496                 `host-cpu-topology` is set."
2497                     .to_owned(),
2498             ));
2499         }
2500 
2501         // Safe because we pass a flag for this call and the host supports this system call
2502         let pcpu_count = unsafe { libc::sysconf(libc::_SC_NPROCESSORS_CONF) } as usize;
2503         if cfg.vcpu_count.is_some() {
2504             if pcpu_count != cfg.vcpu_count.unwrap() {
2505                 return Err(argument::Error::ExpectedArgument(format!(
2506                     "`host-cpu-topology` requires the count of vCPUs({}) to equal the \
2507                             count of CPUs({}) on host.",
2508                     cfg.vcpu_count.unwrap(),
2509                     pcpu_count
2510                 )));
2511             }
2512         } else {
2513             cfg.vcpu_count = Some(pcpu_count);
2514         }
2515 
2516         match &cfg.vcpu_affinity {
2517             None => {
2518                 let mut affinity_map = BTreeMap::new();
2519                 for cpu_id in 0..cfg.vcpu_count.unwrap() {
2520                     affinity_map.insert(cpu_id, vec![cpu_id]);
2521                 }
2522                 cfg.vcpu_affinity = Some(VcpuAffinity::PerVcpu(affinity_map));
2523             }
2524             _ => {
2525                 return Err(argument::Error::ExpectedArgument(
2526                     "`host-cpu-topology` requires not to set `cpu-affinity` at the same time"
2527                         .to_owned(),
2528                 ));
2529             }
2530         }
2531     }
2532     if !cfg.balloon && cfg.balloon_control.is_some() {
2533         return Err(argument::Error::ExpectedArgument(
2534             "'balloon-control' requires enabled balloon".to_owned(),
2535         ));
2536     }
2537 
2538     set_default_serial_parameters(
2539         &mut cfg.serial_parameters,
2540         !cfg.vhost_user_console.is_empty(),
2541     );
2542 
2543     // Remove jail configuration if it has not been enabled.
2544     if !cfg.jail_enabled {
2545         cfg.jail_config = None;
2546     }
2547 
2548     Ok(())
2549 }
2550 
2551 enum CommandStatus {
2552     Success,
2553     VmReset,
2554     VmStop,
2555     VmCrash,
2556     GuestPanic,
2557 }
2558 
run_vm(args: std::env::Args) -> std::result::Result<CommandStatus, ()>2559 fn run_vm(args: std::env::Args) -> std::result::Result<CommandStatus, ()> {
2560     let arguments =
2561         &[Argument::positional("KERNEL", "bzImage of kernel to run"),
2562           Argument::value("kvm-device", "PATH", "Path to the KVM device. (default /dev/kvm)"),
2563           Argument::value("vhost-vsock-fd", "FD", "Open FD to the vhost-vsock device, mutually exclusive with vhost-vsock-device."),
2564           Argument::value("vhost-vsock-device", "PATH", "Path to the vhost-vsock device. (default /dev/vhost-vsock)"),
2565           Argument::value("vhost-net-device", "PATH", "Path to the vhost-net device. (default /dev/vhost-net)"),
2566           Argument::value("android-fstab", "PATH", "Path to Android fstab"),
2567           Argument::short_value('i', "initrd", "PATH", "Initial ramdisk to load."),
2568           Argument::short_value('p',
2569                                 "params",
2570                                 "PARAMS",
2571                                 "Extra kernel or plugin command line arguments. Can be given more than once."),
2572           Argument::short_value('c', "cpus", "N", "Number of VCPUs. (default: 1)"),
2573           Argument::value("cpu-affinity", "CPUSET", "Comma-separated list of CPUs or CPU ranges to run VCPUs on (e.g. 0,1-3,5)
2574                               or colon-separated list of assignments of guest to host CPU assignments (e.g. 0=0:1=1:2=2) (default: no mask)"),
2575           Argument::value("cpu-cluster", "CPUSET", "Group the given CPUs into a cluster (default: no clusters)"),
2576           Argument::value("cpu-capacity", "CPU=CAP[,CPU=CAP[,...]]", "Set the relative capacity of the given CPU (default: no capacity)"),
2577           Argument::flag("per-vm-core-scheduling", "Enable per-VM core scheduling intead of the default one (per-vCPU core scheduing) by
2578               making all vCPU threads share same cookie for core scheduling.
2579               This option is no-op on devices that have neither MDS nor L1TF vulnerability."),
2580           Argument::value("vcpu-cgroup-path", "PATH", "Move all vCPU threads to this CGroup (default: nothing moves)."),
2581 #[cfg(feature = "audio_cras")]
2582           Argument::value("cras-snd",
2583           "[capture=true,client=crosvm,socket=unified,num_output_streams=1,num_input_streams=1]",
2584           "Comma separated key=value pairs for setting up cras snd devices.
2585               Possible key values:
2586               capture - Enable audio capture. Default to false.
2587               client_type - Set specific client type for cras backend.
2588               num_output_streams - Set number of output PCM streams
2589               num_input_streams - Set number of input PCM streams"),
2590           Argument::flag("no-smt", "Don't use SMT in the guest"),
2591           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)"),
2592           Argument::flag("delay-rt", "Don't set VCPUs real-time until make-rt command is run"),
2593           Argument::short_value('m',
2594                                 "mem",
2595                                 "N",
2596                                 "Amount of guest memory in MiB. (default: 256)"),
2597           Argument::value("init-mem",
2598                           "N",
2599                           "Amount of guest memory outside the balloon at boot in MiB. (default: --mem)"),
2600           Argument::flag("hugepages", "Advise the kernel to use Huge Pages for guest memory mappings."),
2601           Argument::short_value('r',
2602                                 "root",
2603                                 "PATH[,key=value[,key=value[,...]]",
2604                                 "Path to a root disk image followed by optional comma-separated options.
2605                               Like `--disk` but adds appropriate kernel command line option.
2606                               See --disk for valid options."),
2607           Argument::value("rwroot", "PATH[,key=value[,key=value[,...]]", "Path to a writable root disk image followed by optional comma-separated options.
2608                               See --disk for valid options."),
2609           Argument::short_value('d', "disk", "PATH[,key=value[,key=value[,...]]", "Path to a disk image followed by optional comma-separated options.
2610                               Valid keys:
2611                               sparse=BOOL - Indicates whether the disk should support the discard operation (default: true)
2612                               block_size=BYTES - Set the reported block size of the disk (default: 512)
2613                               id=STRING - Set the block device identifier to an ASCII string, up to 20 characters (default: no ID)
2614                               o_direct=BOOL - Use O_DIRECT mode to bypass page cache"),
2615           Argument::value("rwdisk", "PATH[,key=value[,key=value[,...]]", "Path to a writable disk image followed by optional comma-separated options.
2616                               See --disk for valid options."),
2617           Argument::value("rw-pmem-device", "PATH", "Path to a writable disk image."),
2618           Argument::value("pmem-device", "PATH", "Path to a disk image."),
2619           Argument::value("pstore", "path=PATH,size=SIZE", "Path to pstore buffer backend file followed by size."),
2620           Argument::value("host_ip",
2621                           "IP",
2622                           "IP address to assign to host tap interface."),
2623           Argument::value("netmask", "NETMASK", "Netmask for VM subnet."),
2624           Argument::value("mac", "MAC", "MAC address for VM."),
2625           Argument::value("net-vq-pairs", "N", "virtio net virtual queue paris. (default: 1)"),
2626           #[cfg(feature = "audio")]
2627           Argument::value("ac97",
2628                           "[backend=BACKEND,capture=true,capture_effect=EFFECT,client_type=TYPE,shm-fd=FD,client-fd=FD,server-fd=FD]",
2629                           "Comma separated key=value pairs for setting up Ac97 devices. Can be given more than once .
2630                               Possible key values:
2631                               backend=(null, cras, vios) - Where to route the audio device. If not provided, backend will default to null.
2632                               `null` for /dev/null, cras for CRAS server and vios for VioS server.
2633                               capture - Enable audio capture
2634                               capture_effects - | separated effects to be enabled for recording. The only supported effect value now is EchoCancellation or aec.
2635                               client_type - Set specific client type for cras backend.
2636                               socket_type - Set specific socket type for cras backend.
2637                               server - The to the VIOS server (unix socket)."),
2638           #[cfg(feature = "audio")]
2639           Argument::value("sound", "[PATH]", "Path to the VioS server socket for setting up virtio-snd devices."),
2640           Argument::value("serial",
2641                           "type=TYPE,[hardware=HW,num=NUM,path=PATH,input=PATH,console,earlycon,stdin]",
2642                           "Comma separated key=value pairs for setting up serial devices. Can be given more than once.
2643                               Possible key values:
2644                               type=(stdout,syslog,sink,file) - Where to route the serial device
2645                               hardware=(serial,virtio-console) - Which type of serial hardware to emulate. Defaults to 8250 UART (serial).
2646                               num=(1,2,3,4) - Serial Device Number. If not provided, num will default to 1.
2647                               path=PATH - The path to the file to write to when type=file
2648                               input=PATH - The path to the file to read from when not stdin
2649                               console - Use this serial device as the guest console. Can only be given once. Will default to first serial port if not provided.
2650                               earlycon - Use this serial device as the early console. Can only be given once.
2651                               stdin - Direct standard input to this serial device. Can only be given once. Will default to first serial port if not provided.
2652                               "),
2653           Argument::value("syslog-tag", "TAG", "When logging to syslog, use the provided tag."),
2654           Argument::value("x-display", "DISPLAY", "X11 display name to use."),
2655           Argument::flag("display-window-keyboard", "Capture keyboard input from the display window."),
2656           Argument::flag("display-window-mouse", "Capture keyboard input from the display window."),
2657           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."),
2658           #[cfg(feature = "wl-dmabuf")]
2659           Argument::flag("wayland-dmabuf", "DEPRECATED: Enable support for DMABufs in Wayland device."),
2660           Argument::short_value('s',
2661                                 "socket",
2662                                 "PATH",
2663                                 "Path to put the control socket. If PATH is a directory, a name will be generated."),
2664           Argument::value("balloon-control", "PATH", "Path for balloon controller socket."),
2665           Argument::flag("disable-sandbox", "Run all devices in one, non-sandboxed process."),
2666           Argument::value("cid", "CID", "Context ID for virtual sockets."),
2667           Argument::value("shared-dir", "PATH:TAG[:type=TYPE:writeback=BOOL:timeout=SECONDS:uidmap=UIDMAP:gidmap=GIDMAP:cache=CACHE:dax=BOOL,posix_acl=BOOL]",
2668                           "Colon-separated options for configuring a directory to be shared with the VM.
2669                               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.
2670                               The remaining fields are key=value pairs that may appear in any order.  Valid keys are:
2671                               type=(p9, fs) - Indicates whether the directory should be shared via virtio-9p or virtio-fs (default: p9).
2672                               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).
2673                               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).
2674                               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.
2675                               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.
2676                               writeback=BOOL - Enables writeback caching (default: false).  This is only safe to do when the VM has exclusive access to the files in a directory.  Additionally, the server should have read permission for all files as the VM may issue read requests even for files that are opened write-only.
2677                               dax=BOOL - Enables DAX support.  Enabling DAX can improve performance for frequently accessed files by mapping regions of the file directly into the VM's memory.  There is a cost of slightly increased latency the first time the file is accessed.  Since the mapping is shared directly from the host kernel's file cache, enabling DAX can improve performance even when the guest cache policy is \"Never\".  The default value for this option is \"false\".
2678                               posix_acl=BOOL - Indicates whether the shared directory supports POSIX ACLs.  This should only be enabled when the underlying file system supports POSIX ACLs.  The default value for this option is \"true\".
2679 "),
2680           Argument::value("seccomp-policy-dir", "PATH", "Path to seccomp .policy files."),
2681           Argument::flag("seccomp-log-failures", "Instead of seccomp filter failures being fatal, they will be logged instead."),
2682           #[cfg(feature = "plugin")]
2683           Argument::value("plugin", "PATH", "Absolute path to plugin process to run under crosvm."),
2684           #[cfg(feature = "plugin")]
2685           Argument::value("plugin-root", "PATH", "Absolute path to a directory that will become root filesystem for the plugin process."),
2686           #[cfg(feature = "plugin")]
2687           Argument::value("plugin-mount", "PATH:PATH:BOOL", "Path to be mounted into the plugin's root filesystem.  Can be given more than once."),
2688           #[cfg(feature = "plugin")]
2689           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."),
2690           #[cfg(feature = "plugin")]
2691           Argument::value("plugin-gid-map", "GID:GID:INT", "Supplemental GIDs that should be mapped in plugin jail.  Can be given more than once."),
2692           #[cfg(feature = "plugin")]
2693           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."),
2694           Argument::flag("vhost-net", "Use vhost for networking."),
2695           Argument::value("tap-name",
2696                           "NAME",
2697                           "Name of a configured persistent TAP interface to use for networking. A different virtual network card will be added each time this argument is given."),
2698           Argument::value("tap-fd",
2699                           "fd",
2700                           "File descriptor for configured tap device. A different virtual network card will be added each time this argument is given."),
2701           #[cfg(feature = "gpu")]
2702           Argument::flag_or_value("gpu",
2703                                   "[width=INT,height=INT]",
2704                                   "(EXPERIMENTAL) Comma separated key=value pairs for setting up a virtio-gpu device
2705                               Possible key values:
2706                               backend=(2d|virglrenderer|gfxstream) - Which backend to use for virtio-gpu (determining rendering protocol)
2707                               width=INT - The width of the virtual display connected to the virtio-gpu.
2708                               height=INT - The height of the virtual display connected to the virtio-gpu.
2709                               egl[=true|=false] - If the backend should use a EGL context for rendering.
2710                               glx[=true|=false] - If the backend should use a GLX context for rendering.
2711                               surfaceless[=true|=false] - If the backend should use a surfaceless context for rendering.
2712                               angle[=true|=false] - If the gfxstream backend should use ANGLE (OpenGL on Vulkan) as its native OpenGL driver.
2713                               syncfd[=true|=false] - If the gfxstream backend should support EGL_ANDROID_native_fence_sync
2714                               vulkan[=true|=false] - If the backend should support vulkan
2715                               cache-path=PATH - The path to the virtio-gpu device shader cache.
2716                               cache-size=SIZE - The maximum size of the shader cache."),
2717           #[cfg(feature = "gpu")]
2718           Argument::flag_or_value("gpu-display",
2719                                   "[width=INT,height=INT]",
2720                                   "(EXPERIMENTAL) Comma separated key=value pairs for setting up a display on the virtio-gpu device
2721                               Possible key values:
2722                               width=INT - The width of the virtual display connected to the virtio-gpu.
2723                               height=INT - The height of the virtual display connected to the virtio-gpu."),
2724           #[cfg(all(feature = "gpu", feature = "virgl_renderer_next"))]
2725           Argument::flag_or_value("gpu-render-server",
2726                                   "[path=PATH]",
2727                                   "(EXPERIMENTAL) Comma separated key=value pairs for setting up a render server for the virtio-gpu device
2728                               Possible key values:
2729                               path=PATH - The path to the render server executable.
2730                               cache-path=PATH - The path to the render server shader cache.
2731                               cache-size=SIZE - The maximum size of the shader cache."),
2732           #[cfg(feature = "tpm")]
2733           Argument::flag("software-tpm", "enable a software emulated trusted platform module device"),
2734           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"),
2735           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)."),
2736           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)."),
2737           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)."),
2738           Argument::value("mouse", "PATH", "Path to a socket from where to read mouse input events and write status updates to."),
2739           Argument::value("keyboard", "PATH", "Path to a socket from where to read keyboard input events and write status updates to."),
2740           Argument::value("switches", "PATH", "Path to a socket from where to read switch input events and write status updates to."),
2741           #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
2742           Argument::flag("split-irqchip", "(EXPERIMENTAL) enable split-irqchip support"),
2743           Argument::value("bios", "PATH", "Path to BIOS/firmware ROM"),
2744           Argument::value("vfio", "PATH[,guest-address=auto|<BUS:DEVICE.FUNCTION>][,iommu=on|off]", "Path to sysfs of PCI pass through or mdev device.
2745 guest-address=auto|<BUS:DEVICE.FUNCTION> - PCI address that the device will be assigned in the guest (default: auto).  When set to \"auto\", the device will be assigned an address that mirrors its address in the host.
2746 iommu=on|off - indicates whether to enable virtio IOMMU for this device"),
2747           Argument::value("vfio-platform", "PATH", "Path to sysfs of platform pass through"),
2748           Argument::flag("virtio-iommu", "Add a virtio-iommu device"),
2749           #[cfg(feature = "video-decoder")]
2750           Argument::flag_or_value("video-decoder", "[backend]", "(EXPERIMENTAL) enable virtio-video decoder device
2751                               Possible backend values: libvda"),
2752           #[cfg(feature = "video-encoder")]
2753           Argument::flag_or_value("video-encoder", "[backend]", "(EXPERIMENTAL) enable virtio-video encoder device
2754                               Possible backend values: libvda"),
2755           Argument::value("acpi-table", "PATH", "Path to user provided ACPI table"),
2756           Argument::flag("protected-vm", "(EXPERIMENTAL) prevent host access to guest memory"),
2757           Argument::flag("protected-vm-without-firmware", "(EXPERIMENTAL) prevent host access to guest memory, but don't use protected VM firmware"),
2758           #[cfg(target_arch = "aarch64")]
2759           Argument::value("swiotlb", "N", "(EXPERIMENTAL) Size of virtio swiotlb buffer in MiB (default: 64 if `--protected-vm` or `--protected-vm-without-firmware` is present)."),
2760           Argument::flag_or_value("battery",
2761                                   "[type=TYPE]",
2762                                   "Comma separated key=value pairs for setting up battery device
2763                               Possible key values:
2764                               type=goldfish - type of battery emulation, defaults to goldfish"),
2765           Argument::value("gdb", "PORT", "(EXPERIMENTAL) gdb on the given port"),
2766           Argument::flag("no-balloon", "Don't use virtio-balloon device in the guest"),
2767           #[cfg(feature = "usb")]
2768           Argument::flag("no-usb", "Don't use usb devices in the guest"),
2769           Argument::flag("no-rng", "Don't create RNG device in the guest"),
2770           Argument::value("balloon_bias_mib", "N", "Amount to bias balance of memory between host and guest as the balloon inflates, in MiB."),
2771           Argument::value("vhost-user-blk", "SOCKET_PATH", "Path to a socket for vhost-user block"),
2772           Argument::value("vhost-user-console", "SOCKET_PATH", "Path to a socket for vhost-user console"),
2773           Argument::value("vhost-user-gpu", "SOCKET_PATH", "Paths to a vhost-user socket for gpu"),
2774           Argument::value("vhost-user-mac80211-hwsim", "SOCKET_PATH", "Path to a socket for vhost-user mac80211_hwsim"),
2775           Argument::value("vhost-user-net", "SOCKET_PATH", "Path to a socket for vhost-user net"),
2776           #[cfg(feature = "audio")]
2777           Argument::value("vhost-user-snd", "SOCKET_PATH", "Path to a socket for vhost-user snd"),
2778           Argument::value("vhost-user-vsock", "SOCKET_PATH", "Path to a socket for vhost-user vsock"),
2779           Argument::value("vhost-user-wl", "SOCKET_PATH:TUBE_PATH", "Paths to a vhost-user socket for wayland and a Tube socket for additional wayland-specific messages"),
2780           Argument::value("vhost-user-fs", "SOCKET_PATH:TAG",
2781                           "Path to a socket path for vhost-user fs, and tag for the shared dir"),
2782           Argument::value("vvu-proxy", "SOCKET_PATH[,addr=DOMAIN:BUS:DEVICE.FUNCTION,uuid=UUID]", "Socket path for the Virtio Vhost User proxy device.
2783                               Parameters
2784                               addr=BUS:DEVICE.FUNCTION - PCI address that the proxy device will be allocated (default: automatically allocated)
2785                               uuid=UUID - UUID which will be stored in VVU PCI config space that is readable from guest userspace"),
2786           #[cfg(feature = "direct")]
2787           Argument::value("direct-pmio", "PATH@RANGE[,RANGE[,...]]", "Path and ranges for direct port mapped I/O access. RANGE may be decimal or hex (starting with 0x)."),
2788           #[cfg(feature = "direct")]
2789           Argument::value("direct-mmio", "PATH@RANGE[,RANGE[,...]]", "Path and ranges for direct memory mapped I/O access. RANGE may be decimal or hex (starting with 0x)."),
2790 #[cfg(feature = "direct")]
2791           Argument::value("direct-level-irq", "irq", "Enable interrupt passthrough"),
2792 #[cfg(feature = "direct")]
2793           Argument::value("direct-edge-irq", "irq", "Enable interrupt passthrough"),
2794 #[cfg(feature = "direct")]
2795           Argument::value("direct-wake-irq", "irq", "Enable wakeup interrupt for host"),
2796 #[cfg(feature = "direct")]
2797           Argument::value("direct-gpe", "gpe", "Enable GPE interrupt and register access passthrough"),
2798           Argument::value("dmi", "DIR", "Directory with smbios_entry_point/DMI files"),
2799           Argument::flag("no-legacy", "Don't use legacy KBD/RTC devices emulation"),
2800           Argument::value("userspace-msr", "INDEX,action=r0", "Userspace MSR handling. Takes INDEX of the MSR and how they are handled.
2801                               action=r0 - forward RDMSR to host kernel cpu0.
2802 "),
2803 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
2804           Argument::flag("host-cpu-topology", "Use mirror cpu topology of Host for Guest VM"),
2805           Argument::flag("privileged-vm", "Grant this Guest VM certian privileges to manage Host resources, such as power management."),
2806           Argument::value("stub-pci-device", "DOMAIN:BUS:DEVICE.FUNCTION[,vendor=NUM][,device=NUM][,class=NUM][,subsystem_vendor=NUM][,subsystem_device=NUM][,revision=NUM]", "Comma-separated key=value pairs for setting up a stub PCI device that just enumerates. The first option in the list must specify a PCI address to claim.
2807                               Optional further parameters
2808                               vendor=NUM - PCI vendor ID
2809                               device=NUM - PCI device ID
2810                               class=NUM - PCI class (including class code, subclass, and programming interface)
2811                               subsystem_vendor=NUM - PCI subsystem vendor ID
2812                               subsystem_device=NUM - PCI subsystem device ID
2813                               revision=NUM - revision"),
2814           Argument::flag_or_value("coiommu",
2815                           "unpin_policy=POLICY,unpin_interval=NUM,unpin_limit=NUM,unpin_gen_threshold=NUM ",
2816                           "Comma separated key=value pairs for setting up coiommu devices.
2817                               Possible key values:
2818                               unpin_policy=lru - LRU unpin policy.
2819                               unpin_interval=NUM - Unpin interval time in seconds.
2820                               unpin_limit=NUM - Unpin limit for each unpin cycle, in unit of page count. 0 is invalid.
2821                               unpin_gen_threshold=NUM -  Number of unpin intervals a pinned page must be busy for to be aged into the older which is less frequently checked generation."),
2822           Argument::value("file-backed-mapping", "addr=NUM,size=SIZE,path=PATH[,offset=NUM][,ro][,rw][,sync]", "Map the given file into guest memory at the specified address.
2823                               Parameters (addr, size, path are required):
2824                               addr=NUM - guest physical address to map at
2825                               size=NUM - amount of memory to map
2826                               path=PATH - path to backing file/device to map
2827                               offset=NUM - offset in backing file (default 0)
2828                               ro - make the mapping readonly (default)
2829                               rw - make the mapping writable
2830                               sync - open backing file with O_SYNC
2831                               align - whether to adjust addr and size to page boundaries implicitly"),
2832           #[cfg(feature = "direct")]
2833           Argument::value("pcie-root-port", "PATH[,hp_gpe=NUM]", "Path to sysfs of host pcie root port and host pcie root port hotplug gpe number"),
2834           Argument::value("pivot-root", "PATH", "Path to empty directory to use for sandbox pivot root."),
2835           #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
2836           Argument::flag("s2idle", "Set Low Power S0 Idle Capable Flag for guest Fixed ACPI Description Table"),
2837           Argument::flag("strict-balloon", "Don't allow guest to use pages from the balloon"),
2838           Argument::value("mmio-address-range", "STARTADDR-ENDADDR[,STARTADDR-ENDADDR]*",
2839                           "Ranges (inclusive) into which to limit guest mmio addresses. Note that
2840                            this this may cause mmio allocations to fail if the specified ranges are
2841                            incompatible with the default ranges calculated by crosvm."),
2842           #[cfg(target_os = "android")]
2843           Argument::value("task-profiles", "NAME[,...]", "Comma-separated names of the task profiles to apply to all threads in crosvm including the vCPU threads."),
2844           Argument::short_flag('h', "help", "Print help message.")];
2845 
2846     let mut cfg = Config::default();
2847     let match_res = set_arguments(args, &arguments[..], |name, value| {
2848         set_argument(&mut cfg, name, value)
2849     })
2850     .and_then(|_| validate_arguments(&mut cfg));
2851 
2852     match match_res {
2853         #[cfg(feature = "plugin")]
2854         Ok(()) if executable_is_plugin(&cfg.executable_path) => {
2855             match crosvm::plugin::run_config(cfg) {
2856                 Ok(_) => {
2857                     info!("crosvm and plugin have exited normally");
2858                     Ok(CommandStatus::VmStop)
2859                 }
2860                 Err(e) => {
2861                     error!("{:#}", e);
2862                     Err(())
2863                 }
2864             }
2865         }
2866         Ok(()) => match platform::run_config(cfg) {
2867             Ok(platform::ExitState::Stop) => {
2868                 info!("crosvm has exited normally");
2869                 Ok(CommandStatus::VmStop)
2870             }
2871             Ok(platform::ExitState::Reset) => {
2872                 info!("crosvm has exited normally due to reset request");
2873                 Ok(CommandStatus::VmReset)
2874             }
2875             Ok(platform::ExitState::Crash) => {
2876                 info!("crosvm has exited due to a VM crash");
2877                 Ok(CommandStatus::VmCrash)
2878             }
2879             Ok(platform::ExitState::GuestPanic) => {
2880                 info!("crosvm has exited due to a kernel panic in guest");
2881                 Ok(CommandStatus::GuestPanic)
2882             }
2883             Err(e) => {
2884                 error!("crosvm has exited with error: {:#}", e);
2885                 Err(())
2886             }
2887         },
2888         Err(argument::Error::PrintHelp) => {
2889             print_help("crosvm run", "KERNEL", &arguments[..]);
2890             Ok(CommandStatus::Success)
2891         }
2892         Err(e) => {
2893             error!("{}", e);
2894             Err(())
2895         }
2896     }
2897 }
2898 
stop_vms(mut args: std::env::Args) -> std::result::Result<(), ()>2899 fn stop_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
2900     if args.len() == 0 {
2901         print_help("crosvm stop", "VM_SOCKET...", &[]);
2902         println!("Stops the crosvm instance listening on each `VM_SOCKET` given.");
2903         return Err(());
2904     }
2905     let socket_path = &args.next().unwrap();
2906     let socket_path = Path::new(&socket_path);
2907     vms_request(&VmRequest::Exit, socket_path)
2908 }
2909 
suspend_vms(mut args: std::env::Args) -> std::result::Result<(), ()>2910 fn suspend_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
2911     if args.len() == 0 {
2912         print_help("crosvm suspend", "VM_SOCKET...", &[]);
2913         println!("Suspends the crosvm instance listening on each `VM_SOCKET` given.");
2914         return Err(());
2915     }
2916     let socket_path = &args.next().unwrap();
2917     let socket_path = Path::new(&socket_path);
2918     vms_request(&VmRequest::Suspend, socket_path)
2919 }
2920 
resume_vms(mut args: std::env::Args) -> std::result::Result<(), ()>2921 fn resume_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
2922     if args.len() == 0 {
2923         print_help("crosvm resume", "VM_SOCKET...", &[]);
2924         println!("Resumes the crosvm instance listening on each `VM_SOCKET` given.");
2925         return Err(());
2926     }
2927     let socket_path = &args.next().unwrap();
2928     let socket_path = Path::new(&socket_path);
2929     vms_request(&VmRequest::Resume, socket_path)
2930 }
2931 
powerbtn_vms(mut args: std::env::Args) -> std::result::Result<(), ()>2932 fn powerbtn_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
2933     if args.len() == 0 {
2934         print_help("crosvm powerbtn", "VM_SOCKET...", &[]);
2935         println!("Triggers a power button event in the crosvm instance listening on each `VM_SOCKET` given.");
2936         return Err(());
2937     }
2938     let socket_path = &args.next().unwrap();
2939     let socket_path = Path::new(&socket_path);
2940     vms_request(&VmRequest::Powerbtn, socket_path)
2941 }
2942 
inject_gpe(mut args: std::env::Args) -> std::result::Result<(), ()>2943 fn inject_gpe(mut args: std::env::Args) -> std::result::Result<(), ()> {
2944     if args.len() < 2 {
2945         print_help("crosvm gpe", "GPE# VM_SOCKET...", &[]);
2946         println!("Injects a general-purpose event (GPE#) into the crosvm instance listening on each `VM_SOCKET` given.");
2947         return Err(());
2948     }
2949     let gpe = match args.next().unwrap().parse::<u32>() {
2950         Ok(n) => n,
2951         Err(_) => {
2952             error!("Failed to parse GPE#");
2953             return Err(());
2954         }
2955     };
2956 
2957     let socket_path = &args.next().unwrap();
2958     let socket_path = Path::new(&socket_path);
2959     vms_request(&VmRequest::Gpe(gpe), socket_path)
2960 }
2961 
balloon_vms(mut args: std::env::Args) -> std::result::Result<(), ()>2962 fn balloon_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
2963     if args.len() < 2 {
2964         print_help("crosvm balloon", "SIZE VM_SOCKET...", &[]);
2965         println!("Set the ballon size of the crosvm instance to `SIZE` bytes.");
2966         return Err(());
2967     }
2968     let num_bytes = match args.next().unwrap().parse::<u64>() {
2969         Ok(n) => n,
2970         Err(_) => {
2971             error!("Failed to parse number of bytes");
2972             return Err(());
2973         }
2974     };
2975 
2976     let command = BalloonControlCommand::Adjust { num_bytes };
2977     let socket_path = &args.next().unwrap();
2978     let socket_path = Path::new(&socket_path);
2979     vms_request(&VmRequest::BalloonCommand(command), socket_path)
2980 }
2981 
balloon_stats(mut args: std::env::Args) -> std::result::Result<(), ()>2982 fn balloon_stats(mut args: std::env::Args) -> std::result::Result<(), ()> {
2983     if args.len() != 1 {
2984         print_help("crosvm balloon_stats", "VM_SOCKET", &[]);
2985         println!("Prints virtio balloon statistics for a `VM_SOCKET`.");
2986         return Err(());
2987     }
2988     let command = BalloonControlCommand::Stats {};
2989     let request = &VmRequest::BalloonCommand(command);
2990     let socket_path = &args.next().unwrap();
2991     let socket_path = Path::new(&socket_path);
2992     let response = handle_request(request, socket_path)?;
2993     match serde_json::to_string_pretty(&response) {
2994         Ok(response_json) => println!("{}", response_json),
2995         Err(e) => {
2996             error!("Failed to serialize into JSON: {}", e);
2997             return Err(());
2998         }
2999     }
3000     match response {
3001         VmResponse::BalloonStats { .. } => Ok(()),
3002         _ => Err(()),
3003     }
3004 }
3005 
modify_battery(mut args: std::env::Args) -> std::result::Result<(), ()>3006 fn modify_battery(mut args: std::env::Args) -> std::result::Result<(), ()> {
3007     if args.len() < 4 {
3008         print_help(
3009             "crosvm battery BATTERY_TYPE ",
3010             "[status STATUS | \
3011              present PRESENT | \
3012              health HEALTH | \
3013              capacity CAPACITY | \
3014              aconline ACONLINE ] \
3015              VM_SOCKET...",
3016             &[],
3017         );
3018         return Err(());
3019     }
3020 
3021     // This unwrap will not panic because of the above length check.
3022     let battery_type = args.next().unwrap();
3023     let property = args.next().unwrap();
3024     let target = args.next().unwrap();
3025 
3026     let socket_path = args.next().unwrap();
3027     let socket_path = Path::new(&socket_path);
3028 
3029     do_modify_battery(socket_path, &*battery_type, &*property, &*target)
3030 }
3031 
modify_vfio(mut args: std::env::Args) -> std::result::Result<(), ()>3032 fn modify_vfio(mut args: std::env::Args) -> std::result::Result<(), ()> {
3033     if args.len() < 3 {
3034         print_help(
3035             "crosvm vfio",
3036             "[add | remove host_vfio_sysfs] VM_SOCKET...",
3037             &[],
3038         );
3039         return Err(());
3040     }
3041 
3042     // This unwrap will not panic because of the above length check.
3043     let command = args.next().unwrap();
3044     let path_str = args.next().unwrap();
3045     let vfio_path = PathBuf::from(&path_str);
3046     if !vfio_path.exists() || !vfio_path.is_dir() {
3047         error!("Invalid host sysfs path: {}", path_str);
3048         return Err(());
3049     }
3050 
3051     let socket_path = args.next().unwrap();
3052     let socket_path = Path::new(&socket_path);
3053 
3054     let add = match command.as_ref() {
3055         "add" => true,
3056         "remove" => false,
3057         other => {
3058             error!("Invalid vfio command {}", other);
3059             return Err(());
3060         }
3061     };
3062 
3063     let request = VmRequest::VfioCommand { vfio_path, add };
3064     handle_request(&request, socket_path)?;
3065     Ok(())
3066 }
3067 
3068 #[cfg(feature = "composite-disk")]
create_composite(mut args: std::env::Args) -> std::result::Result<(), ()>3069 fn create_composite(mut args: std::env::Args) -> std::result::Result<(), ()> {
3070     if args.len() < 1 {
3071         print_help("crosvm create_composite", "PATH [LABEL:PARTITION]..", &[]);
3072         println!("Creates a new composite disk image containing the given partition images");
3073         return Err(());
3074     }
3075 
3076     let composite_image_path = args.next().unwrap();
3077     let zero_filler_path = format!("{}.filler", composite_image_path);
3078     let header_path = format!("{}.header", composite_image_path);
3079     let footer_path = format!("{}.footer", composite_image_path);
3080 
3081     let mut composite_image_file = OpenOptions::new()
3082         .create(true)
3083         .read(true)
3084         .write(true)
3085         .truncate(true)
3086         .open(&composite_image_path)
3087         .map_err(|e| {
3088             error!(
3089                 "Failed opening composite disk image file at '{}': {}",
3090                 composite_image_path, e
3091             );
3092         })?;
3093     create_zero_filler(&zero_filler_path).map_err(|e| {
3094         error!(
3095             "Failed to create zero filler file at '{}': {}",
3096             &zero_filler_path, e
3097         );
3098     })?;
3099     let mut header_file = OpenOptions::new()
3100         .create(true)
3101         .read(true)
3102         .write(true)
3103         .truncate(true)
3104         .open(&header_path)
3105         .map_err(|e| {
3106             error!(
3107                 "Failed opening header image file at '{}': {}",
3108                 header_path, e
3109             );
3110         })?;
3111     let mut footer_file = OpenOptions::new()
3112         .create(true)
3113         .read(true)
3114         .write(true)
3115         .truncate(true)
3116         .open(&footer_path)
3117         .map_err(|e| {
3118             error!(
3119                 "Failed opening footer image file at '{}': {}",
3120                 footer_path, e
3121             );
3122         })?;
3123 
3124     let partitions = args
3125         .into_iter()
3126         .map(|partition_arg| {
3127             if let [label, path] = partition_arg.split(":").collect::<Vec<_>>()[..] {
3128                 let partition_file = File::open(path)
3129                     .map_err(|e| error!("Failed to open partition image: {}", e))?;
3130                 let size =
3131                     create_disk_file(partition_file, disk::MAX_NESTING_DEPTH, Path::new(path))
3132                         .map_err(|e| error!("Failed to create DiskFile instance: {}", e))?
3133                         .get_len()
3134                         .map_err(|e| error!("Failed to get length of partition image: {}", e))?;
3135                 Ok(PartitionInfo {
3136                     label: label.to_owned(),
3137                     path: Path::new(path).to_owned(),
3138                     partition_type: ImagePartitionType::LinuxFilesystem,
3139                     writable: false,
3140                     size,
3141                 })
3142             } else {
3143                 error!(
3144                     "Must specify label and path for partition '{}', like LABEL:PATH",
3145                     partition_arg
3146                 );
3147                 Err(())
3148             }
3149         })
3150         .collect::<Result<Vec<_>, _>>()?;
3151 
3152     create_composite_disk(
3153         &partitions,
3154         &PathBuf::from(zero_filler_path),
3155         &PathBuf::from(header_path),
3156         &mut header_file,
3157         &PathBuf::from(footer_path),
3158         &mut footer_file,
3159         &mut composite_image_file,
3160     )
3161     .map_err(|e| {
3162         error!(
3163             "Failed to create composite disk image at '{}': {}",
3164             composite_image_path, e
3165         );
3166     })?;
3167 
3168     Ok(())
3169 }
3170 
create_qcow2(args: std::env::Args) -> std::result::Result<(), ()>3171 fn create_qcow2(args: std::env::Args) -> std::result::Result<(), ()> {
3172     let arguments = [
3173         Argument::positional("PATH", "where to create the qcow2 image"),
3174         Argument::positional("[SIZE]", "the expanded size of the image"),
3175         Argument::value(
3176             "backing_file",
3177             "path/to/file",
3178             " the file to back the image",
3179         ),
3180     ];
3181     let mut positional_index = 0;
3182     let mut file_path = String::from("");
3183     let mut size: Option<u64> = None;
3184     let mut backing_file: Option<String> = None;
3185     set_arguments(args, &arguments[..], |name, value| {
3186         match (name, positional_index) {
3187             ("", 0) => {
3188                 // NAME
3189                 positional_index += 1;
3190                 file_path = value.unwrap().to_owned();
3191             }
3192             ("", 1) => {
3193                 // [SIZE]
3194                 positional_index += 1;
3195                 size = Some(value.unwrap().parse::<u64>().map_err(|_| {
3196                     argument::Error::InvalidValue {
3197                         value: value.unwrap().to_owned(),
3198                         expected: String::from("SIZE should be a nonnegative integer"),
3199                     }
3200                 })?);
3201             }
3202             ("", _) => {
3203                 return Err(argument::Error::TooManyArguments(
3204                     "Expected at most 2 positional arguments".to_owned(),
3205                 ));
3206             }
3207             ("backing_file", _) => {
3208                 backing_file = value.map(|x| x.to_owned());
3209             }
3210             _ => unreachable!(),
3211         };
3212         Ok(())
3213     })
3214     .map_err(|e| {
3215         error!("Unable to parse command line arguments: {}", e);
3216     })?;
3217     if file_path.is_empty() || !(size.is_some() ^ backing_file.is_some()) {
3218         print_help("crosvm create_qcow2", "PATH [SIZE]", &arguments);
3219         println!(
3220             "Create a new QCOW2 image at `PATH` of either the specified `SIZE` in bytes or
3221 with a '--backing_file'."
3222         );
3223         return Err(());
3224     }
3225 
3226     let file = OpenOptions::new()
3227         .create(true)
3228         .read(true)
3229         .write(true)
3230         .truncate(true)
3231         .open(&file_path)
3232         .map_err(|e| {
3233             error!("Failed opening qcow file at '{}': {}", file_path, e);
3234         })?;
3235 
3236     match (size, backing_file) {
3237         (Some(size), None) => QcowFile::new(file, size).map_err(|e| {
3238             error!("Failed to create qcow file at '{}': {}", file_path, e);
3239         })?,
3240         (None, Some(backing_file)) => {
3241             QcowFile::new_from_backing(file, &backing_file, disk::MAX_NESTING_DEPTH).map_err(
3242                 |e| {
3243                     error!("Failed to create qcow file at '{}': {}", file_path, e);
3244                 },
3245             )?
3246         }
3247         _ => unreachable!(),
3248     };
3249     Ok(())
3250 }
3251 
start_device(mut args: std::env::Args) -> std::result::Result<(), ()>3252 fn start_device(mut args: std::env::Args) -> std::result::Result<(), ()> {
3253     let print_usage = || {
3254         print_help(
3255             "crosvm device",
3256             " (block|console|cras-snd|fs|gpu|net|wl) <device-specific arguments>",
3257             &[],
3258         );
3259     };
3260 
3261     if args.len() == 0 {
3262         print_usage();
3263         return Err(());
3264     }
3265 
3266     let device = args.next().unwrap();
3267 
3268     let program_name = format!("crosvm device {}", device);
3269 
3270     let args = args.collect::<Vec<_>>();
3271     let args = args.iter().map(Deref::deref).collect::<Vec<_>>();
3272     let args = args.as_slice();
3273 
3274     let result = match device.as_str() {
3275         "block" => run_block_device(&program_name, args),
3276         "console" => run_console_device(&program_name, args),
3277         #[cfg(feature = "audio_cras")]
3278         "cras-snd" => run_cras_snd_device(&program_name, args),
3279         "fs" => run_fs_device(&program_name, args),
3280         #[cfg(feature = "gpu")]
3281         "gpu" => run_gpu_device(&program_name, args),
3282         "net" => run_net_device(&program_name, args),
3283         "vsock" => run_vsock_device(&program_name, args),
3284         "wl" => run_wl_device(&program_name, args),
3285         _ => {
3286             println!("Unknown device name: {}", device);
3287             print_usage();
3288             return Err(());
3289         }
3290     };
3291 
3292     result.map_err(|e| {
3293         error!("Failed to run {} device: {:#}", device, e);
3294     })
3295 }
3296 
disk_cmd(mut args: std::env::Args) -> std::result::Result<(), ()>3297 fn disk_cmd(mut args: std::env::Args) -> std::result::Result<(), ()> {
3298     if args.len() < 2 {
3299         print_help("crosvm disk", "SUBCOMMAND VM_SOCKET...", &[]);
3300         println!("Manage attached virtual disk devices.");
3301         println!("Subcommands:");
3302         println!("  resize DISK_INDEX NEW_SIZE VM_SOCKET");
3303         return Err(());
3304     }
3305     let subcommand: &str = &args.next().unwrap();
3306 
3307     let request = match subcommand {
3308         "resize" => {
3309             let disk_index = match args.next().unwrap().parse::<usize>() {
3310                 Ok(n) => n,
3311                 Err(_) => {
3312                     error!("Failed to parse disk index");
3313                     return Err(());
3314                 }
3315             };
3316 
3317             let new_size = match args.next().unwrap().parse::<u64>() {
3318                 Ok(n) => n,
3319                 Err(_) => {
3320                     error!("Failed to parse disk size");
3321                     return Err(());
3322                 }
3323             };
3324 
3325             VmRequest::DiskCommand {
3326                 disk_index,
3327                 command: DiskControlCommand::Resize { new_size },
3328             }
3329         }
3330         _ => {
3331             error!("Unknown disk subcommand '{}'", subcommand);
3332             return Err(());
3333         }
3334     };
3335 
3336     let socket_path = &args.next().unwrap();
3337     let socket_path = Path::new(&socket_path);
3338     vms_request(&request, socket_path)
3339 }
3340 
make_rt(mut args: std::env::Args) -> std::result::Result<(), ()>3341 fn make_rt(mut args: std::env::Args) -> std::result::Result<(), ()> {
3342     if args.len() == 0 {
3343         print_help("crosvm make_rt", "VM_SOCKET...", &[]);
3344         println!("Makes the crosvm instance listening on each `VM_SOCKET` given RT.");
3345         return Err(());
3346     }
3347     let socket_path = &args.next().unwrap();
3348     let socket_path = Path::new(&socket_path);
3349     vms_request(&VmRequest::MakeRT, socket_path)
3350 }
3351 
parse_bus_id_addr(v: &str) -> ModifyUsbResult<(u8, u8, u16, u16)>3352 fn parse_bus_id_addr(v: &str) -> ModifyUsbResult<(u8, u8, u16, u16)> {
3353     debug!("parse_bus_id_addr: {}", v);
3354     let mut ids = v.split(':');
3355     match (ids.next(), ids.next(), ids.next(), ids.next()) {
3356         (Some(bus_id), Some(addr), Some(vid), Some(pid)) => {
3357             let bus_id = bus_id
3358                 .parse::<u8>()
3359                 .map_err(|e| ModifyUsbError::ArgParseInt("bus_id", bus_id.to_owned(), e))?;
3360             let addr = addr
3361                 .parse::<u8>()
3362                 .map_err(|e| ModifyUsbError::ArgParseInt("addr", addr.to_owned(), e))?;
3363             let vid = u16::from_str_radix(vid, 16)
3364                 .map_err(|e| ModifyUsbError::ArgParseInt("vid", vid.to_owned(), e))?;
3365             let pid = u16::from_str_radix(pid, 16)
3366                 .map_err(|e| ModifyUsbError::ArgParseInt("pid", pid.to_owned(), e))?;
3367             Ok((bus_id, addr, vid, pid))
3368         }
3369         _ => Err(ModifyUsbError::ArgParse(
3370             "BUS_ID_ADDR_BUS_NUM_DEV_NUM",
3371             v.to_owned(),
3372         )),
3373     }
3374 }
3375 
usb_attach(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult>3376 fn usb_attach(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
3377     let val = args
3378         .next()
3379         .ok_or(ModifyUsbError::ArgMissing("BUS_ID_ADDR_BUS_NUM_DEV_NUM"))?;
3380     let (bus, addr, vid, pid) = parse_bus_id_addr(&val)?;
3381     let dev_path = PathBuf::from(
3382         args.next()
3383             .ok_or(ModifyUsbError::ArgMissing("usb device path"))?,
3384     );
3385 
3386     let socket_path = args
3387         .next()
3388         .ok_or(ModifyUsbError::ArgMissing("control socket path"))?;
3389     let socket_path = Path::new(&socket_path);
3390 
3391     do_usb_attach(socket_path, bus, addr, vid, pid, &dev_path)
3392 }
3393 
usb_detach(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult>3394 fn usb_detach(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
3395     let port: u8 = args
3396         .next()
3397         .map_or(Err(ModifyUsbError::ArgMissing("PORT")), |p| {
3398             p.parse::<u8>()
3399                 .map_err(|e| ModifyUsbError::ArgParseInt("PORT", p.to_owned(), e))
3400         })?;
3401     let socket_path = args
3402         .next()
3403         .ok_or(ModifyUsbError::ArgMissing("control socket path"))?;
3404     let socket_path = Path::new(&socket_path);
3405     do_usb_detach(socket_path, port)
3406 }
3407 
usb_list(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult>3408 fn usb_list(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
3409     let socket_path = args
3410         .next()
3411         .ok_or(ModifyUsbError::ArgMissing("control socket path"))?;
3412     let socket_path = Path::new(&socket_path);
3413     do_usb_list(socket_path)
3414 }
3415 
modify_usb(mut args: std::env::Args) -> std::result::Result<(), ()>3416 fn modify_usb(mut args: std::env::Args) -> std::result::Result<(), ()> {
3417     if args.len() < 2 {
3418         print_help("crosvm usb",
3419                    "[attach BUS_ID:ADDR:VENDOR_ID:PRODUCT_ID [USB_DEVICE_PATH|-] | detach PORT | list] VM_SOCKET...", &[]);
3420         return Err(());
3421     }
3422 
3423     // This unwrap will not panic because of the above length check.
3424     let command = &args.next().unwrap();
3425     let result = match command.as_ref() {
3426         "attach" => usb_attach(args),
3427         "detach" => usb_detach(args),
3428         "list" => usb_list(args),
3429         other => Err(ModifyUsbError::UnknownCommand(other.to_owned())),
3430     };
3431     match result {
3432         Ok(response) => {
3433             println!("{}", response);
3434             Ok(())
3435         }
3436         Err(e) => {
3437             println!("error {}", e);
3438             Err(())
3439         }
3440     }
3441 }
3442 
3443 #[allow(clippy::unnecessary_wraps)]
pkg_version() -> std::result::Result<(), ()>3444 fn pkg_version() -> std::result::Result<(), ()> {
3445     const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION");
3446     const PKG_VERSION: Option<&'static str> = option_env!("PKG_VERSION");
3447 
3448     print!("crosvm {}", VERSION.unwrap_or("UNKNOWN"));
3449     match PKG_VERSION {
3450         Some(v) => println!("-{}", v),
3451         None => println!(),
3452     }
3453     Ok(())
3454 }
3455 
print_usage()3456 fn print_usage() {
3457     print_help("crosvm", "[--extended-status] [command]", &[]);
3458     println!("Commands:");
3459     println!("    balloon - Set balloon size of the crosvm instance.");
3460     println!("    balloon_stats - Prints virtio balloon statistics.");
3461     println!("    battery - Modify battery.");
3462     #[cfg(feature = "composite-disk")]
3463     println!("    create_composite  - Create a new composite disk image file.");
3464     println!("    create_qcow2  - Create a new qcow2 disk image file.");
3465     println!("    device - Start a device process.");
3466     println!("    disk - Manage attached virtual disk devices.");
3467     println!(
3468         "    make_rt - Enables real-time vcpu priority for crosvm instances started with \
3469          `--delay-rt`."
3470     );
3471     println!("    resume - Resumes the crosvm instance.");
3472     println!("    run - Start a new crosvm instance.");
3473     println!("    stop - Stops crosvm instances via their control sockets.");
3474     println!("    suspend - Suspends the crosvm instance.");
3475     println!("    powerbtn - Triggers a power button event in the crosvm instance.");
3476     println!("    gpe - Injects a general-purpose event into the crosvm instance.");
3477     println!("    usb - Manage attached virtual USB devices.");
3478     println!("    version - Show package version.");
3479     println!("    vfio - add/remove host vfio pci device into guest.");
3480 }
3481 
crosvm_main() -> std::result::Result<CommandStatus, ()>3482 fn crosvm_main() -> std::result::Result<CommandStatus, ()> {
3483     if let Err(e) = syslog::init() {
3484         println!("failed to initialize syslog: {}", e);
3485         return Err(());
3486     }
3487 
3488     panic_hook::set_panic_hook();
3489 
3490     let mut args = std::env::args();
3491     if args.next().is_none() {
3492         error!("expected executable name");
3493         return Err(());
3494     }
3495 
3496     let mut cmd_arg = args.next();
3497     let extended_status = match cmd_arg.as_ref().map(|s| s.as_ref()) {
3498         Some("--extended-status") => {
3499             cmd_arg = args.next();
3500             true
3501         }
3502         _ => false,
3503     };
3504 
3505     let command = match cmd_arg {
3506         Some(c) => c,
3507         None => {
3508             print_usage();
3509             return Ok(CommandStatus::Success);
3510         }
3511     };
3512 
3513     // Past this point, usage of exit is in danger of leaking zombie processes.
3514     let ret = if command == "run" {
3515         // We handle run_vm separately because it does not simply signal success/error
3516         // but also indicates whether the guest requested reset or stop.
3517         run_vm(args)
3518     } else {
3519         match &command[..] {
3520             "balloon" => balloon_vms(args),
3521             "balloon_stats" => balloon_stats(args),
3522             "battery" => modify_battery(args),
3523             #[cfg(feature = "composite-disk")]
3524             "create_composite" => create_composite(args),
3525             "create_qcow2" => create_qcow2(args),
3526             "device" => start_device(args),
3527             "disk" => disk_cmd(args),
3528             "make_rt" => make_rt(args),
3529             "resume" => resume_vms(args),
3530             "stop" => stop_vms(args),
3531             "suspend" => suspend_vms(args),
3532             "powerbtn" => powerbtn_vms(args),
3533             "gpe" => inject_gpe(args),
3534             "usb" => modify_usb(args),
3535             "version" => pkg_version(),
3536             "vfio" => modify_vfio(args),
3537             c => {
3538                 println!("invalid subcommand: {:?}", c);
3539                 print_usage();
3540                 Err(())
3541             }
3542         }
3543         .map(|_| CommandStatus::Success)
3544     };
3545 
3546     // Reap exit status from any child device processes. At this point, all devices should have been
3547     // dropped in the main process and told to shutdown. Try over a period of 100ms, since it may
3548     // take some time for the processes to shut down.
3549     if !wait_all_children() {
3550         // We gave them a chance, and it's too late.
3551         warn!("not all child processes have exited; sending SIGKILL");
3552         if let Err(e) = kill_process_group() {
3553             // We're now at the mercy of the OS to clean up after us.
3554             warn!("unable to kill all child processes: {}", e);
3555         }
3556     }
3557 
3558     // WARNING: Any code added after this point is not guaranteed to run
3559     // since we may forcibly kill this process (and its children) above.
3560     ret.map(|s| {
3561         if extended_status {
3562             s
3563         } else {
3564             CommandStatus::Success
3565         }
3566     })
3567 }
3568 
main()3569 fn main() {
3570     let exit_code = match crosvm_main() {
3571         Ok(CommandStatus::Success | CommandStatus::VmStop) => 0,
3572         Ok(CommandStatus::VmReset) => 32,
3573         Ok(CommandStatus::VmCrash) => 33,
3574         Ok(CommandStatus::GuestPanic) => 34,
3575         Err(_) => 1,
3576     };
3577     std::process::exit(exit_code);
3578 }
3579 
3580 #[cfg(test)]
3581 mod tests {
3582     use super::*;
3583     use crosvm::{DEFAULT_TOUCH_DEVICE_HEIGHT, DEFAULT_TOUCH_DEVICE_WIDTH};
3584 
3585     #[test]
parse_cpu_set_single()3586     fn parse_cpu_set_single() {
3587         assert_eq!(parse_cpu_set("123").expect("parse failed"), vec![123]);
3588     }
3589 
3590     #[test]
parse_cpu_set_list()3591     fn parse_cpu_set_list() {
3592         assert_eq!(
3593             parse_cpu_set("0,1,2,3").expect("parse failed"),
3594             vec![0, 1, 2, 3]
3595         );
3596     }
3597 
3598     #[test]
parse_cpu_set_range()3599     fn parse_cpu_set_range() {
3600         assert_eq!(
3601             parse_cpu_set("0-3").expect("parse failed"),
3602             vec![0, 1, 2, 3]
3603         );
3604     }
3605 
3606     #[test]
parse_cpu_set_list_of_ranges()3607     fn parse_cpu_set_list_of_ranges() {
3608         assert_eq!(
3609             parse_cpu_set("3-4,7-9,18").expect("parse failed"),
3610             vec![3, 4, 7, 8, 9, 18]
3611         );
3612     }
3613 
3614     #[test]
parse_cpu_set_repeated()3615     fn parse_cpu_set_repeated() {
3616         // For now, allow duplicates - they will be handled gracefully by the vec to cpu_set_t conversion.
3617         assert_eq!(parse_cpu_set("1,1,1").expect("parse failed"), vec![1, 1, 1]);
3618     }
3619 
3620     #[test]
parse_cpu_set_negative()3621     fn parse_cpu_set_negative() {
3622         // Negative CPU numbers are not allowed.
3623         parse_cpu_set("-3").expect_err("parse should have failed");
3624     }
3625 
3626     #[test]
parse_cpu_set_reverse_range()3627     fn parse_cpu_set_reverse_range() {
3628         // Ranges must be from low to high.
3629         parse_cpu_set("5-2").expect_err("parse should have failed");
3630     }
3631 
3632     #[test]
parse_cpu_set_open_range()3633     fn parse_cpu_set_open_range() {
3634         parse_cpu_set("3-").expect_err("parse should have failed");
3635     }
3636 
3637     #[test]
parse_cpu_set_extra_comma()3638     fn parse_cpu_set_extra_comma() {
3639         parse_cpu_set("0,1,2,").expect_err("parse should have failed");
3640     }
3641 
3642     #[test]
parse_cpu_affinity_global()3643     fn parse_cpu_affinity_global() {
3644         assert_eq!(
3645             parse_cpu_affinity("0,5-7,9").expect("parse failed"),
3646             VcpuAffinity::Global(vec![0, 5, 6, 7, 9]),
3647         );
3648     }
3649 
3650     #[test]
parse_cpu_affinity_per_vcpu_one_to_one()3651     fn parse_cpu_affinity_per_vcpu_one_to_one() {
3652         let mut expected_map = BTreeMap::new();
3653         expected_map.insert(0, vec![0]);
3654         expected_map.insert(1, vec![1]);
3655         expected_map.insert(2, vec![2]);
3656         expected_map.insert(3, vec![3]);
3657         assert_eq!(
3658             parse_cpu_affinity("0=0:1=1:2=2:3=3").expect("parse failed"),
3659             VcpuAffinity::PerVcpu(expected_map),
3660         );
3661     }
3662 
3663     #[test]
parse_cpu_affinity_per_vcpu_sets()3664     fn parse_cpu_affinity_per_vcpu_sets() {
3665         let mut expected_map = BTreeMap::new();
3666         expected_map.insert(0, vec![0, 1, 2]);
3667         expected_map.insert(1, vec![3, 4, 5]);
3668         expected_map.insert(2, vec![6, 7, 8]);
3669         assert_eq!(
3670             parse_cpu_affinity("0=0,1,2:1=3-5:2=6,7-8").expect("parse failed"),
3671             VcpuAffinity::PerVcpu(expected_map),
3672         );
3673     }
3674 
3675     #[cfg(feature = "audio_cras")]
3676     #[test]
parse_ac97_vaild()3677     fn parse_ac97_vaild() {
3678         parse_ac97_options("backend=cras").expect("parse should have succeded");
3679     }
3680 
3681     #[cfg(feature = "audio")]
3682     #[test]
parse_ac97_null_vaild()3683     fn parse_ac97_null_vaild() {
3684         parse_ac97_options("backend=null").expect("parse should have succeded");
3685     }
3686 
3687     #[cfg(feature = "audio_cras")]
3688     #[test]
parse_ac97_capture_vaild()3689     fn parse_ac97_capture_vaild() {
3690         parse_ac97_options("backend=cras,capture=true").expect("parse should have succeded");
3691     }
3692 
3693     #[cfg(feature = "audio_cras")]
3694     #[test]
parse_ac97_client_type()3695     fn parse_ac97_client_type() {
3696         parse_ac97_options("backend=cras,capture=true,client_type=crosvm")
3697             .expect("parse should have succeded");
3698         parse_ac97_options("backend=cras,capture=true,client_type=arcvm")
3699             .expect("parse should have succeded");
3700         parse_ac97_options("backend=cras,capture=true,client_type=none")
3701             .expect_err("parse should have failed");
3702     }
3703 
3704     #[cfg(feature = "audio_cras")]
3705     #[test]
parse_ac97_socket_type()3706     fn parse_ac97_socket_type() {
3707         parse_ac97_options("socket_type=unified").expect("parse should have succeded");
3708         parse_ac97_options("socket_type=legacy").expect("parse should have succeded");
3709     }
3710 
3711     #[cfg(feature = "audio")]
3712     #[test]
parse_ac97_vios_valid()3713     fn parse_ac97_vios_valid() {
3714         parse_ac97_options("backend=vios,server=/path/to/server")
3715             .expect("parse should have succeded");
3716     }
3717 
3718     #[test]
parse_serial_vaild()3719     fn parse_serial_vaild() {
3720         parse_serial_options("type=syslog,num=1,console=true,stdin=true")
3721             .expect("parse should have succeded");
3722     }
3723 
3724     #[test]
parse_serial_virtio_console_vaild()3725     fn parse_serial_virtio_console_vaild() {
3726         parse_serial_options("type=syslog,num=5,console=true,stdin=true,hardware=virtio-console")
3727             .expect("parse should have succeded");
3728     }
3729 
3730     #[test]
parse_serial_valid_no_num()3731     fn parse_serial_valid_no_num() {
3732         parse_serial_options("type=syslog").expect("parse should have succeded");
3733     }
3734 
3735     #[test]
parse_serial_equals_in_value()3736     fn parse_serial_equals_in_value() {
3737         let parsed = parse_serial_options("type=syslog,path=foo=bar==.log")
3738             .expect("parse should have succeded");
3739         assert_eq!(parsed.path, Some(PathBuf::from("foo=bar==.log")));
3740     }
3741 
3742     #[test]
parse_serial_invalid_type()3743     fn parse_serial_invalid_type() {
3744         parse_serial_options("type=wormhole,num=1").expect_err("parse should have failed");
3745     }
3746 
3747     #[test]
parse_serial_invalid_num_upper()3748     fn parse_serial_invalid_num_upper() {
3749         parse_serial_options("type=syslog,num=5").expect_err("parse should have failed");
3750     }
3751 
3752     #[test]
parse_serial_invalid_num_lower()3753     fn parse_serial_invalid_num_lower() {
3754         parse_serial_options("type=syslog,num=0").expect_err("parse should have failed");
3755     }
3756 
3757     #[test]
parse_serial_virtio_console_invalid_num_lower()3758     fn parse_serial_virtio_console_invalid_num_lower() {
3759         parse_serial_options("type=syslog,hardware=virtio-console,num=0")
3760             .expect_err("parse should have failed");
3761     }
3762 
3763     #[test]
parse_serial_invalid_num_string()3764     fn parse_serial_invalid_num_string() {
3765         parse_serial_options("type=syslog,num=number3").expect_err("parse should have failed");
3766     }
3767 
3768     #[test]
parse_serial_invalid_option()3769     fn parse_serial_invalid_option() {
3770         parse_serial_options("type=syslog,speed=lightspeed").expect_err("parse should have failed");
3771     }
3772 
3773     #[test]
parse_serial_invalid_two_stdin()3774     fn parse_serial_invalid_two_stdin() {
3775         let mut config = Config::default();
3776         set_argument(&mut config, "serial", Some("num=1,type=stdout,stdin=true"))
3777             .expect("should parse the first serial argument");
3778         set_argument(&mut config, "serial", Some("num=2,type=stdout,stdin=true"))
3779             .expect_err("should fail to parse a second serial port connected to stdin");
3780     }
3781 
3782     #[test]
parse_plugin_mount_valid()3783     fn parse_plugin_mount_valid() {
3784         let mut config = Config::default();
3785         set_argument(
3786             &mut config,
3787             "plugin-mount",
3788             Some("/dev/null:/dev/zero:true"),
3789         )
3790         .expect("parse should succeed");
3791         assert_eq!(config.plugin_mounts[0].src, PathBuf::from("/dev/null"));
3792         assert_eq!(config.plugin_mounts[0].dst, PathBuf::from("/dev/zero"));
3793         assert!(config.plugin_mounts[0].writable);
3794     }
3795 
3796     #[test]
parse_plugin_mount_valid_shorthand()3797     fn parse_plugin_mount_valid_shorthand() {
3798         let mut config = Config::default();
3799         set_argument(&mut config, "plugin-mount", Some("/dev/null")).expect("parse should succeed");
3800         assert_eq!(config.plugin_mounts[0].dst, PathBuf::from("/dev/null"));
3801         assert!(!config.plugin_mounts[0].writable);
3802         set_argument(&mut config, "plugin-mount", Some("/dev/null:/dev/zero"))
3803             .expect("parse should succeed");
3804         assert_eq!(config.plugin_mounts[1].dst, PathBuf::from("/dev/zero"));
3805         assert!(!config.plugin_mounts[1].writable);
3806         set_argument(&mut config, "plugin-mount", Some("/dev/null::true"))
3807             .expect("parse should succeed");
3808         assert_eq!(config.plugin_mounts[2].dst, PathBuf::from("/dev/null"));
3809         assert!(config.plugin_mounts[2].writable);
3810     }
3811 
3812     #[test]
parse_plugin_mount_invalid()3813     fn parse_plugin_mount_invalid() {
3814         let mut config = Config::default();
3815         set_argument(&mut config, "plugin-mount", Some("")).expect_err("parse should fail");
3816         set_argument(
3817             &mut config,
3818             "plugin-mount",
3819             Some("/dev/null:/dev/null:true:false"),
3820         )
3821         .expect_err("parse should fail because too many arguments");
3822         set_argument(&mut config, "plugin-mount", Some("null:/dev/null:true"))
3823             .expect_err("parse should fail because source is not absolute");
3824         set_argument(&mut config, "plugin-mount", Some("/dev/null:null:true"))
3825             .expect_err("parse should fail because source is not absolute");
3826         set_argument(&mut config, "plugin-mount", Some("/dev/null:null:blah"))
3827             .expect_err("parse should fail because flag is not boolean");
3828     }
3829 
3830     #[test]
parse_plugin_gid_map_valid()3831     fn parse_plugin_gid_map_valid() {
3832         let mut config = Config::default();
3833         set_argument(&mut config, "plugin-gid-map", Some("1:2:3")).expect("parse should succeed");
3834         assert_eq!(config.plugin_gid_maps[0].inner, 1);
3835         assert_eq!(config.plugin_gid_maps[0].outer, 2);
3836         assert_eq!(config.plugin_gid_maps[0].count, 3);
3837     }
3838 
3839     #[test]
parse_plugin_gid_map_valid_shorthand()3840     fn parse_plugin_gid_map_valid_shorthand() {
3841         let mut config = Config::default();
3842         set_argument(&mut config, "plugin-gid-map", Some("1")).expect("parse should succeed");
3843         assert_eq!(config.plugin_gid_maps[0].inner, 1);
3844         assert_eq!(config.plugin_gid_maps[0].outer, 1);
3845         assert_eq!(config.plugin_gid_maps[0].count, 1);
3846         set_argument(&mut config, "plugin-gid-map", Some("1:2")).expect("parse should succeed");
3847         assert_eq!(config.plugin_gid_maps[1].inner, 1);
3848         assert_eq!(config.plugin_gid_maps[1].outer, 2);
3849         assert_eq!(config.plugin_gid_maps[1].count, 1);
3850         set_argument(&mut config, "plugin-gid-map", Some("1::3")).expect("parse should succeed");
3851         assert_eq!(config.plugin_gid_maps[2].inner, 1);
3852         assert_eq!(config.plugin_gid_maps[2].outer, 1);
3853         assert_eq!(config.plugin_gid_maps[2].count, 3);
3854     }
3855 
3856     #[test]
parse_plugin_gid_map_invalid()3857     fn parse_plugin_gid_map_invalid() {
3858         let mut config = Config::default();
3859         set_argument(&mut config, "plugin-gid-map", Some("")).expect_err("parse should fail");
3860         set_argument(&mut config, "plugin-gid-map", Some("1:2:3:4"))
3861             .expect_err("parse should fail because too many arguments");
3862         set_argument(&mut config, "plugin-gid-map", Some("blah:2:3"))
3863             .expect_err("parse should fail because inner is not a number");
3864         set_argument(&mut config, "plugin-gid-map", Some("1:blah:3"))
3865             .expect_err("parse should fail because outer is not a number");
3866         set_argument(&mut config, "plugin-gid-map", Some("1:2:blah"))
3867             .expect_err("parse should fail because count is not a number");
3868     }
3869 
3870     #[test]
single_touch_spec_and_track_pad_spec_default_size()3871     fn single_touch_spec_and_track_pad_spec_default_size() {
3872         let mut config = Config::default();
3873         config
3874             .executable_path
3875             .replace(Executable::Kernel(PathBuf::from("kernel")));
3876         set_argument(&mut config, "single-touch", Some("/dev/single-touch-test")).unwrap();
3877         set_argument(&mut config, "trackpad", Some("/dev/single-touch-test")).unwrap();
3878         validate_arguments(&mut config).unwrap();
3879         assert_eq!(
3880             config.virtio_single_touch.first().unwrap().get_size(),
3881             (DEFAULT_TOUCH_DEVICE_WIDTH, DEFAULT_TOUCH_DEVICE_HEIGHT)
3882         );
3883         assert_eq!(
3884             config.virtio_trackpad.first().unwrap().get_size(),
3885             (DEFAULT_TOUCH_DEVICE_WIDTH, DEFAULT_TOUCH_DEVICE_HEIGHT)
3886         );
3887     }
3888 
3889     #[cfg(feature = "gpu")]
3890     #[test]
single_touch_spec_default_size_from_gpu()3891     fn single_touch_spec_default_size_from_gpu() {
3892         let width = 12345u32;
3893         let height = 54321u32;
3894         let mut config = Config::default();
3895         config
3896             .executable_path
3897             .replace(Executable::Kernel(PathBuf::from("kernel")));
3898         set_argument(&mut config, "single-touch", Some("/dev/single-touch-test")).unwrap();
3899         set_argument(
3900             &mut config,
3901             "gpu",
3902             Some(&format!("width={},height={}", width, height)),
3903         )
3904         .unwrap();
3905         validate_arguments(&mut config).unwrap();
3906         assert_eq!(
3907             config.virtio_single_touch.first().unwrap().get_size(),
3908             (width, height)
3909         );
3910     }
3911 
3912     #[test]
single_touch_spec_and_track_pad_spec_with_size()3913     fn single_touch_spec_and_track_pad_spec_with_size() {
3914         let width = 12345u32;
3915         let height = 54321u32;
3916         let mut config = Config::default();
3917         config
3918             .executable_path
3919             .replace(Executable::Kernel(PathBuf::from("kernel")));
3920         set_argument(
3921             &mut config,
3922             "single-touch",
3923             Some(&format!("/dev/single-touch-test:{}:{}", width, height)),
3924         )
3925         .unwrap();
3926         set_argument(
3927             &mut config,
3928             "trackpad",
3929             Some(&format!("/dev/single-touch-test:{}:{}", width, height)),
3930         )
3931         .unwrap();
3932         validate_arguments(&mut config).unwrap();
3933         assert_eq!(
3934             config.virtio_single_touch.first().unwrap().get_size(),
3935             (width, height)
3936         );
3937         assert_eq!(
3938             config.virtio_trackpad.first().unwrap().get_size(),
3939             (width, height)
3940         );
3941     }
3942 
3943     #[cfg(feature = "gpu")]
3944     #[test]
single_touch_spec_with_size_independent_from_gpu()3945     fn single_touch_spec_with_size_independent_from_gpu() {
3946         let touch_width = 12345u32;
3947         let touch_height = 54321u32;
3948         let display_width = 1234u32;
3949         let display_height = 5432u32;
3950         let mut config = Config::default();
3951         config
3952             .executable_path
3953             .replace(Executable::Kernel(PathBuf::from("kernel")));
3954         set_argument(
3955             &mut config,
3956             "single-touch",
3957             Some(&format!(
3958                 "/dev/single-touch-test:{}:{}",
3959                 touch_width, touch_height
3960             )),
3961         )
3962         .unwrap();
3963         set_argument(
3964             &mut config,
3965             "gpu",
3966             Some(&format!(
3967                 "width={},height={}",
3968                 display_width, display_height
3969             )),
3970         )
3971         .unwrap();
3972         validate_arguments(&mut config).unwrap();
3973         assert_eq!(
3974             config.virtio_single_touch.first().unwrap().get_size(),
3975             (touch_width, touch_height)
3976         );
3977     }
3978 
3979     #[test]
virtio_switches()3980     fn virtio_switches() {
3981         let mut config = Config::default();
3982         config
3983             .executable_path
3984             .replace(Executable::Kernel(PathBuf::from("kernel")));
3985         set_argument(&mut config, "switches", Some("/dev/switches-test")).unwrap();
3986         validate_arguments(&mut config).unwrap();
3987         assert_eq!(
3988             config.virtio_switches.pop().unwrap(),
3989             PathBuf::from("/dev/switches-test")
3990         );
3991     }
3992 
3993     #[cfg(feature = "gpu")]
3994     #[test]
parse_gpu_options_default_vulkan_support()3995     fn parse_gpu_options_default_vulkan_support() {
3996         {
3997             let mut gpu_params: GpuParameters = Default::default();
3998             assert!(parse_gpu_options(Some("backend=virglrenderer"), &mut gpu_params).is_ok());
3999             assert!(!gpu_params.use_vulkan);
4000         }
4001 
4002         #[cfg(feature = "gfxstream")]
4003         {
4004             let mut gpu_params: GpuParameters = Default::default();
4005             assert!(parse_gpu_options(Some("backend=gfxstream"), &mut gpu_params).is_ok());
4006             assert!(gpu_params.use_vulkan);
4007         }
4008     }
4009 
4010     #[cfg(feature = "gpu")]
4011     #[test]
parse_gpu_options_with_vulkan_specified()4012     fn parse_gpu_options_with_vulkan_specified() {
4013         {
4014             let mut gpu_params: GpuParameters = Default::default();
4015             assert!(parse_gpu_options(Some("vulkan=true"), &mut gpu_params).is_ok());
4016             assert!(gpu_params.use_vulkan);
4017         }
4018         {
4019             let mut gpu_params: GpuParameters = Default::default();
4020             assert!(
4021                 parse_gpu_options(Some("backend=virglrenderer,vulkan=true"), &mut gpu_params)
4022                     .is_ok()
4023             );
4024             assert!(gpu_params.use_vulkan);
4025         }
4026         {
4027             let mut gpu_params: GpuParameters = Default::default();
4028             assert!(
4029                 parse_gpu_options(Some("vulkan=true,backend=virglrenderer"), &mut gpu_params)
4030                     .is_ok()
4031             );
4032             assert!(gpu_params.use_vulkan);
4033         }
4034         {
4035             let mut gpu_params: GpuParameters = Default::default();
4036             assert!(parse_gpu_options(Some("vulkan=false"), &mut gpu_params).is_ok());
4037             assert!(!gpu_params.use_vulkan);
4038         }
4039         {
4040             let mut gpu_params: GpuParameters = Default::default();
4041             assert!(
4042                 parse_gpu_options(Some("backend=virglrenderer,vulkan=false"), &mut gpu_params)
4043                     .is_ok()
4044             );
4045             assert!(!gpu_params.use_vulkan);
4046         }
4047         {
4048             let mut gpu_params: GpuParameters = Default::default();
4049             assert!(
4050                 parse_gpu_options(Some("vulkan=false,backend=virglrenderer"), &mut gpu_params)
4051                     .is_ok()
4052             );
4053             assert!(!gpu_params.use_vulkan);
4054         }
4055         {
4056             let mut gpu_params: GpuParameters = Default::default();
4057             assert!(parse_gpu_options(
4058                 Some("backend=virglrenderer,vulkan=invalid_value"),
4059                 &mut gpu_params
4060             )
4061             .is_err());
4062         }
4063         {
4064             let mut gpu_params: GpuParameters = Default::default();
4065             assert!(parse_gpu_options(
4066                 Some("vulkan=invalid_value,backend=virglrenderer"),
4067                 &mut gpu_params
4068             )
4069             .is_err());
4070         }
4071     }
4072 
4073     #[cfg(all(feature = "gpu", feature = "gfxstream"))]
4074     #[test]
parse_gpu_options_gfxstream_with_syncfd_specified()4075     fn parse_gpu_options_gfxstream_with_syncfd_specified() {
4076         {
4077             let mut gpu_params: GpuParameters = Default::default();
4078             assert!(
4079                 parse_gpu_options(Some("backend=gfxstream,syncfd=true"), &mut gpu_params).is_ok()
4080             );
4081             assert!(gpu_params.gfxstream_use_syncfd);
4082         }
4083         {
4084             let mut gpu_params: GpuParameters = Default::default();
4085             assert!(
4086                 parse_gpu_options(Some("syncfd=true,backend=gfxstream"), &mut gpu_params).is_ok()
4087             );
4088             assert!(gpu_params.gfxstream_use_syncfd);
4089         }
4090         {
4091             let mut gpu_params: GpuParameters = Default::default();
4092             assert!(
4093                 parse_gpu_options(Some("backend=gfxstream,syncfd=false"), &mut gpu_params).is_ok()
4094             );
4095             assert!(!gpu_params.gfxstream_use_syncfd);
4096         }
4097         {
4098             let mut gpu_params: GpuParameters = Default::default();
4099             assert!(
4100                 parse_gpu_options(Some("syncfd=false,backend=gfxstream"), &mut gpu_params).is_ok()
4101             );
4102             assert!(!gpu_params.gfxstream_use_syncfd);
4103         }
4104         {
4105             let mut gpu_params: GpuParameters = Default::default();
4106             assert!(parse_gpu_options(
4107                 Some("backend=gfxstream,syncfd=invalid_value"),
4108                 &mut gpu_params
4109             )
4110             .is_err());
4111         }
4112         {
4113             let mut gpu_params: GpuParameters = Default::default();
4114             assert!(parse_gpu_options(
4115                 Some("syncfd=invalid_value,backend=gfxstream"),
4116                 &mut gpu_params
4117             )
4118             .is_err());
4119         }
4120     }
4121 
4122     #[cfg(all(feature = "gpu", feature = "gfxstream"))]
4123     #[test]
parse_gpu_options_not_gfxstream_with_syncfd_specified()4124     fn parse_gpu_options_not_gfxstream_with_syncfd_specified() {
4125         {
4126             let mut gpu_params: GpuParameters = Default::default();
4127             assert!(
4128                 parse_gpu_options(Some("backend=virglrenderer,syncfd=true"), &mut gpu_params)
4129                     .is_err()
4130             );
4131         }
4132         {
4133             let mut gpu_params: GpuParameters = Default::default();
4134             assert!(
4135                 parse_gpu_options(Some("syncfd=true,backend=virglrenderer"), &mut gpu_params)
4136                     .is_err()
4137             );
4138         }
4139     }
4140 
4141     #[cfg(feature = "gpu")]
4142     #[test]
parse_gpu_display_options_valid()4143     fn parse_gpu_display_options_valid() {
4144         {
4145             let mut gpu_params: GpuParameters = Default::default();
4146             assert!(
4147                 parse_gpu_display_options(Some("width=500,height=600"), &mut gpu_params).is_ok()
4148             );
4149             assert_eq!(gpu_params.displays.len(), 1);
4150             assert_eq!(gpu_params.displays[0].width, 500);
4151             assert_eq!(gpu_params.displays[0].height, 600);
4152         }
4153     }
4154 
4155     #[cfg(feature = "gpu")]
4156     #[test]
parse_gpu_display_options_invalid()4157     fn parse_gpu_display_options_invalid() {
4158         {
4159             let mut gpu_params: GpuParameters = Default::default();
4160             assert!(parse_gpu_display_options(Some("width=500"), &mut gpu_params).is_err());
4161         }
4162         {
4163             let mut gpu_params: GpuParameters = Default::default();
4164             assert!(parse_gpu_display_options(Some("height=500"), &mut gpu_params).is_err());
4165         }
4166         {
4167             let mut gpu_params: GpuParameters = Default::default();
4168             assert!(parse_gpu_display_options(Some("width"), &mut gpu_params).is_err());
4169         }
4170         {
4171             let mut gpu_params: GpuParameters = Default::default();
4172             assert!(parse_gpu_display_options(Some("blah"), &mut gpu_params).is_err());
4173         }
4174     }
4175 
4176     #[cfg(feature = "gpu")]
4177     #[test]
parse_gpu_options_and_gpu_display_options_valid()4178     fn parse_gpu_options_and_gpu_display_options_valid() {
4179         {
4180             let mut gpu_params: GpuParameters = Default::default();
4181             assert!(parse_gpu_options(Some("2D,width=500,height=600"), &mut gpu_params).is_ok());
4182             assert!(
4183                 parse_gpu_display_options(Some("width=700,height=800"), &mut gpu_params).is_ok()
4184             );
4185             assert_eq!(gpu_params.displays.len(), 2);
4186             assert_eq!(gpu_params.displays[0].width, 500);
4187             assert_eq!(gpu_params.displays[0].height, 600);
4188             assert_eq!(gpu_params.displays[1].width, 700);
4189             assert_eq!(gpu_params.displays[1].height, 800);
4190         }
4191         {
4192             let mut gpu_params: GpuParameters = Default::default();
4193             assert!(parse_gpu_options(Some("2D"), &mut gpu_params).is_ok());
4194             assert!(
4195                 parse_gpu_display_options(Some("width=700,height=800"), &mut gpu_params).is_ok()
4196             );
4197             assert_eq!(gpu_params.displays.len(), 1);
4198             assert_eq!(gpu_params.displays[0].width, 700);
4199             assert_eq!(gpu_params.displays[0].height, 800);
4200         }
4201     }
4202 
4203     #[test]
parse_battery_vaild()4204     fn parse_battery_vaild() {
4205         parse_battery_options(Some("type=goldfish")).expect("parse should have succeded");
4206     }
4207 
4208     #[test]
parse_battery_vaild_no_type()4209     fn parse_battery_vaild_no_type() {
4210         parse_battery_options(None).expect("parse should have succeded");
4211     }
4212 
4213     #[test]
parse_battery_invaild_parameter()4214     fn parse_battery_invaild_parameter() {
4215         parse_battery_options(Some("tyep=goldfish")).expect_err("parse should have failed");
4216     }
4217 
4218     #[test]
parse_battery_invaild_type_value()4219     fn parse_battery_invaild_type_value() {
4220         parse_battery_options(Some("type=xxx")).expect_err("parse should have failed");
4221     }
4222 
4223     #[test]
parse_stub_pci()4224     fn parse_stub_pci() {
4225         let params = parse_stub_pci_parameters(Some("0000:01:02.3,vendor=0xfffe,device=0xfffd,class=0xffc1c2,subsystem_vendor=0xfffc,subsystem_device=0xfffb,revision=0xa")).unwrap();
4226         assert_eq!(params.address.bus, 1);
4227         assert_eq!(params.address.dev, 2);
4228         assert_eq!(params.address.func, 3);
4229         assert_eq!(params.vendor_id, 0xfffe);
4230         assert_eq!(params.device_id, 0xfffd);
4231         assert_eq!(params.class as u8, PciClassCode::Other as u8);
4232         assert_eq!(params.subclass, 0xc1);
4233         assert_eq!(params.programming_interface, 0xc2);
4234         assert_eq!(params.subsystem_vendor_id, 0xfffc);
4235         assert_eq!(params.subsystem_device_id, 0xfffb);
4236         assert_eq!(params.revision_id, 0xa);
4237     }
4238 
4239     #[cfg(feature = "direct")]
4240     #[test]
parse_direct_io_options_valid()4241     fn parse_direct_io_options_valid() {
4242         let params = parse_direct_io_options(Some("/dev/mem@1,100-110")).unwrap();
4243         assert_eq!(params.path.to_str(), Some("/dev/mem"));
4244         assert_eq!(params.ranges[0], BusRange { base: 1, len: 1 });
4245         assert_eq!(params.ranges[1], BusRange { base: 100, len: 11 });
4246     }
4247 
4248     #[cfg(feature = "direct")]
4249     #[test]
parse_direct_io_options_hex()4250     fn parse_direct_io_options_hex() {
4251         let params = parse_direct_io_options(Some("/dev/mem@1,0x10,100-110,0x10-0x20")).unwrap();
4252         assert_eq!(params.path.to_str(), Some("/dev/mem"));
4253         assert_eq!(params.ranges[0], BusRange { base: 1, len: 1 });
4254         assert_eq!(params.ranges[1], BusRange { base: 0x10, len: 1 });
4255         assert_eq!(params.ranges[2], BusRange { base: 100, len: 11 });
4256         assert_eq!(
4257             params.ranges[3],
4258             BusRange {
4259                 base: 0x10,
4260                 len: 0x11
4261             }
4262         );
4263     }
4264 
4265     #[cfg(feature = "direct")]
4266     #[test]
parse_direct_io_options_invalid()4267     fn parse_direct_io_options_invalid() {
4268         assert!(parse_direct_io_options(Some("/dev/mem@0y10"))
4269             .unwrap_err()
4270             .to_string()
4271             .contains("invalid base range value"));
4272 
4273         assert!(parse_direct_io_options(Some("/dev/mem@"))
4274             .unwrap_err()
4275             .to_string()
4276             .contains("invalid base range value"));
4277     }
4278 
4279     #[test]
parse_file_backed_mapping_valid()4280     fn parse_file_backed_mapping_valid() {
4281         let params = parse_file_backed_mapping(Some(
4282             "addr=0x1000,size=0x2000,path=/dev/mem,offset=0x3000,ro,rw,sync",
4283         ))
4284         .unwrap();
4285         assert_eq!(params.address, 0x1000);
4286         assert_eq!(params.size, 0x2000);
4287         assert_eq!(params.path, PathBuf::from("/dev/mem"));
4288         assert_eq!(params.offset, 0x3000);
4289         assert!(params.writable);
4290         assert!(params.sync);
4291     }
4292 
4293     #[test]
parse_file_backed_mapping_incomplete()4294     fn parse_file_backed_mapping_incomplete() {
4295         assert!(parse_file_backed_mapping(Some("addr=0x1000,size=0x2000"))
4296             .unwrap_err()
4297             .to_string()
4298             .contains("required"));
4299         assert!(parse_file_backed_mapping(Some("size=0x2000,path=/dev/mem"))
4300             .unwrap_err()
4301             .to_string()
4302             .contains("required"));
4303         assert!(parse_file_backed_mapping(Some("addr=0x1000,path=/dev/mem"))
4304             .unwrap_err()
4305             .to_string()
4306             .contains("required"));
4307     }
4308 
4309     #[test]
parse_file_backed_mapping_unaligned()4310     fn parse_file_backed_mapping_unaligned() {
4311         assert!(
4312             parse_file_backed_mapping(Some("addr=0x1001,size=0x2000,path=/dev/mem"))
4313                 .unwrap_err()
4314                 .to_string()
4315                 .contains("aligned")
4316         );
4317         assert!(
4318             parse_file_backed_mapping(Some("addr=0x1000,size=0x2001,path=/dev/mem"))
4319                 .unwrap_err()
4320                 .to_string()
4321                 .contains("aligned")
4322         );
4323     }
4324 
4325     #[test]
parse_file_backed_mapping_align()4326     fn parse_file_backed_mapping_align() {
4327         let params =
4328             parse_file_backed_mapping(Some("addr=0x3042,size=0xff0,path=/dev/mem,align")).unwrap();
4329         assert_eq!(params.address, 0x3000);
4330         assert_eq!(params.size, 0x2000);
4331     }
4332 
4333     #[test]
parse_userspace_msr_options_test()4334     fn parse_userspace_msr_options_test() {
4335         let index = parse_userspace_msr_options("0x10,action=r0").unwrap();
4336         assert_eq!(index, 0x10);
4337         assert!(parse_userspace_msr_options("0x10,action=none").is_err());
4338         assert!(parse_userspace_msr_options("0x10").is_err());
4339         assert!(parse_userspace_msr_options("hoge").is_err());
4340     }
4341 }
4342