• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 cfg_if::cfg_if! {
6     if #[cfg(unix)] {
7         use std::net;
8 
9         use base::RawDescriptor;
10         use devices::virtio::vhost::user::device::parse_wayland_sock;
11 
12         use super::sys::config::VfioOption;
13         use super::config::SharedDir;
14     } else if #[cfg(windows)] {
15         use crate::crosvm::sys::config::IrqChipKind;
16 
17     }
18 }
19 
20 use std::collections::BTreeMap;
21 #[cfg(feature = "config-file")]
22 use std::path::Path;
23 use std::path::PathBuf;
24 use std::str::FromStr;
25 use std::sync::atomic::AtomicUsize;
26 use std::sync::atomic::Ordering;
27 
28 use arch::CpuSet;
29 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
30 use arch::MsrConfig;
31 use arch::Pstore;
32 use arch::VcpuAffinity;
33 use argh::FromArgs;
34 use base::getpid;
35 use cros_async::ExecutorKind;
36 use devices::virtio::block::block::DiskOption;
37 #[cfg(any(feature = "video-decoder", feature = "video-encoder"))]
38 use devices::virtio::device_constants::video::VideoDeviceConfig;
39 #[cfg(feature = "audio")]
40 use devices::virtio::snd::parameters::Parameters as SndParameters;
41 use devices::virtio::vhost::user::device;
42 use devices::virtio::vsock::VsockConfig;
43 #[cfg(feature = "gpu")]
44 use devices::virtio::GpuDisplayParameters;
45 #[cfg(feature = "gpu")]
46 use devices::virtio::GpuParameters;
47 #[cfg(unix)]
48 use devices::virtio::NetParameters;
49 #[cfg(unix)]
50 use devices::virtio::NetParametersMode;
51 #[cfg(feature = "audio")]
52 use devices::Ac97Parameters;
53 use devices::PflashParameters;
54 use devices::SerialHardware;
55 use devices::SerialParameters;
56 use devices::StubPciParameters;
57 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
58 use hypervisor::CpuHybridType;
59 use hypervisor::ProtectionType;
60 use merge::bool::overwrite_false;
61 use merge::vec::append;
62 use resources::AddressRange;
63 use serde::Deserialize;
64 #[cfg(feature = "gpu")]
65 use serde_keyvalue::FromKeyValues;
66 
67 #[cfg(feature = "gpu")]
68 use super::gpu_config::fixup_gpu_display_options;
69 #[cfg(feature = "gpu")]
70 use super::gpu_config::fixup_gpu_options;
71 #[cfg(all(feature = "gpu", feature = "virgl_renderer_next"))]
72 use super::sys::GpuRenderServerParameters;
73 use crate::crosvm::config::from_key_values;
74 #[cfg(feature = "audio")]
75 use crate::crosvm::config::parse_ac97_options;
76 use crate::crosvm::config::parse_bus_id_addr;
77 use crate::crosvm::config::parse_cpu_affinity;
78 use crate::crosvm::config::parse_cpu_capacity;
79 #[cfg(feature = "direct")]
80 use crate::crosvm::config::parse_direct_io_options;
81 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
82 use crate::crosvm::config::parse_memory_region;
83 use crate::crosvm::config::parse_mmio_address_range;
84 #[cfg(feature = "direct")]
85 use crate::crosvm::config::parse_pcie_root_port_params;
86 use crate::crosvm::config::parse_pflash_parameters;
87 #[cfg(feature = "plugin")]
88 use crate::crosvm::config::parse_plugin_mount_option;
89 use crate::crosvm::config::parse_serial_options;
90 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
91 use crate::crosvm::config::parse_userspace_msr_options;
92 use crate::crosvm::config::BatteryConfig;
93 #[cfg(feature = "plugin")]
94 use crate::crosvm::config::BindMount;
95 use crate::crosvm::config::CpuOptions;
96 #[cfg(feature = "direct")]
97 use crate::crosvm::config::DirectIoOption;
98 use crate::crosvm::config::Executable;
99 use crate::crosvm::config::FileBackedMappingParameters;
100 #[cfg(feature = "plugin")]
101 use crate::crosvm::config::GidMap;
102 #[cfg(feature = "direct")]
103 use crate::crosvm::config::HostPcieRootPortParameters;
104 use crate::crosvm::config::HypervisorKind;
105 use crate::crosvm::config::MemOptions;
106 use crate::crosvm::config::TouchDeviceOption;
107 use crate::crosvm::config::VhostUserFsOption;
108 use crate::crosvm::config::VhostUserOption;
109 use crate::crosvm::config::VvuOption;
110 
111 #[derive(FromArgs)]
112 /// crosvm
113 pub struct CrosvmCmdlineArgs {
114     #[argh(switch)]
115     /// use extended exit status
116     pub extended_status: bool,
117     #[argh(option, default = r#"String::from("info")"#)]
118     /// specify log level, eg "off", "error", "debug,disk=off", etc
119     pub log_level: String,
120     #[argh(option, arg_name = "TAG")]
121     /// when logging to syslog, use the provided tag
122     pub syslog_tag: Option<String>,
123     #[argh(switch)]
124     /// disable output to syslog
125     pub no_syslog: bool,
126     #[argh(subcommand)]
127     pub command: Command,
128 }
129 
130 #[allow(clippy::large_enum_variant)]
131 #[derive(FromArgs)]
132 #[argh(subcommand)]
133 pub enum CrossPlatformCommands {
134     #[cfg(feature = "balloon")]
135     Balloon(BalloonCommand),
136     #[cfg(feature = "balloon")]
137     BalloonStats(BalloonStatsCommand),
138     Battery(BatteryCommand),
139     #[cfg(feature = "composite-disk")]
140     CreateComposite(CreateCompositeCommand),
141     #[cfg(feature = "qcow")]
142     CreateQcow2(CreateQcow2Command),
143     Device(DeviceCommand),
144     Disk(DiskCommand),
145     #[cfg(feature = "gpu")]
146     Gpu(GpuCommand),
147     MakeRT(MakeRTCommand),
148     Resume(ResumeCommand),
149     Run(RunCommand),
150     Stop(StopCommand),
151     Suspend(SuspendCommand),
152     Swap(SwapCommand),
153     Powerbtn(PowerbtnCommand),
154     Sleepbtn(SleepCommand),
155     Gpe(GpeCommand),
156     Usb(UsbCommand),
157     Version(VersionCommand),
158     Vfio(VfioCrosvmCommand),
159     Snapshot(SnapshotCommand),
160 }
161 
162 #[allow(clippy::large_enum_variant)]
163 #[derive(argh_helpers::FlattenSubcommand)]
164 pub enum Command {
165     CrossPlatform(CrossPlatformCommands),
166     Sys(super::sys::cmdline::Commands),
167 }
168 
169 #[derive(FromArgs)]
170 #[argh(subcommand, name = "balloon")]
171 /// Set balloon size of the crosvm instance to `SIZE` bytes
172 pub struct BalloonCommand {
173     #[argh(positional, arg_name = "SIZE")]
174     /// amount of bytes
175     pub num_bytes: u64,
176     #[argh(positional, arg_name = "VM_SOCKET")]
177     /// VM Socket path
178     pub socket_path: String,
179 }
180 
181 #[derive(argh::FromArgs)]
182 #[argh(subcommand, name = "balloon_stats")]
183 /// Prints virtio balloon statistics for a `VM_SOCKET`
184 pub struct BalloonStatsCommand {
185     #[argh(positional, arg_name = "VM_SOCKET")]
186     /// VM Socket path
187     pub socket_path: String,
188 }
189 
190 #[derive(FromArgs)]
191 #[argh(subcommand, name = "battery")]
192 /// Modify battery
193 pub struct BatteryCommand {
194     #[argh(positional, arg_name = "BATTERY_TYPE")]
195     /// battery type
196     pub battery_type: String,
197     #[argh(positional)]
198     /// battery property
199     /// status | present | health | capacity | aconline
200     pub property: String,
201     #[argh(positional)]
202     /// battery property target
203     /// STATUS | PRESENT | HEALTH | CAPACITY | ACONLINE
204     pub target: String,
205     #[argh(positional, arg_name = "VM_SOCKET")]
206     /// VM Socket path
207     pub socket_path: String,
208 }
209 
210 #[cfg(feature = "composite-disk")]
211 #[derive(FromArgs)]
212 #[argh(subcommand, name = "create_composite")]
213 /// Create a new composite disk image file
214 pub struct CreateCompositeCommand {
215     #[argh(positional, arg_name = "PATH")]
216     /// image path
217     pub path: String,
218     #[argh(positional, arg_name = "LABEL:PARTITION")]
219     /// partitions
220     pub partitions: Vec<String>,
221 }
222 
223 #[cfg(feature = "qcow")]
224 #[derive(FromArgs)]
225 #[argh(subcommand, name = "create_qcow2")]
226 /// Create Qcow2 image given path and size
227 pub struct CreateQcow2Command {
228     #[argh(positional, arg_name = "PATH")]
229     /// path to the new qcow2 file to create
230     pub file_path: String,
231     #[argh(positional, arg_name = "SIZE")]
232     /// desired size of the image in bytes; required if not using --backing-file
233     pub size: Option<u64>,
234     #[argh(option)]
235     /// path to backing file; if specified, the image will be the same size as the backing file, and
236     /// SIZE may not be specified
237     pub backing_file: Option<String>,
238 }
239 
240 #[derive(FromArgs)]
241 #[argh(subcommand)]
242 pub enum DiskSubcommand {
243     Resize(ResizeDiskSubcommand),
244 }
245 
246 #[derive(FromArgs)]
247 /// resize disk
248 #[argh(subcommand, name = "resize")]
249 pub struct ResizeDiskSubcommand {
250     #[argh(positional, arg_name = "DISK_INDEX")]
251     /// disk index
252     pub disk_index: usize,
253     #[argh(positional, arg_name = "NEW_SIZE")]
254     /// new disk size
255     pub disk_size: u64,
256     #[argh(positional, arg_name = "VM_SOCKET")]
257     /// VM Socket path
258     pub socket_path: String,
259 }
260 
261 #[derive(FromArgs)]
262 #[argh(subcommand, name = "disk")]
263 /// Manage attached virtual disk devices
264 pub struct DiskCommand {
265     #[argh(subcommand)]
266     pub command: DiskSubcommand,
267 }
268 
269 #[derive(FromArgs)]
270 #[argh(subcommand, name = "make_rt")]
271 /// Enables real-time vcpu priority for crosvm instances started with `--delay-rt`
272 pub struct MakeRTCommand {
273     #[argh(positional, arg_name = "VM_SOCKET")]
274     /// VM Socket path
275     pub socket_path: String,
276 }
277 
278 #[derive(FromArgs)]
279 #[argh(subcommand, name = "resume")]
280 /// Resumes the crosvm instance
281 pub struct ResumeCommand {
282     #[argh(positional, arg_name = "VM_SOCKET")]
283     /// VM Socket path
284     pub socket_path: String,
285 }
286 
287 #[derive(FromArgs)]
288 #[argh(subcommand, name = "stop")]
289 /// Stops crosvm instances via their control sockets
290 pub struct StopCommand {
291     #[argh(positional, arg_name = "VM_SOCKET")]
292     /// VM Socket path
293     pub socket_path: String,
294 }
295 
296 #[derive(FromArgs)]
297 #[argh(subcommand, name = "suspend")]
298 /// Suspends the crosvm instance
299 pub struct SuspendCommand {
300     #[argh(positional, arg_name = "VM_SOCKET")]
301     /// VM Socket path
302     pub socket_path: String,
303 }
304 
305 #[derive(FromArgs)]
306 #[argh(subcommand, name = "enable")]
307 /// Enable vmm-swap of a VM. The guest memory is moved to staging memory
308 pub struct SwapEnableCommand {
309     #[argh(positional, arg_name = "VM_SOCKET")]
310     /// VM Socket path
311     pub socket_path: String,
312 }
313 
314 #[derive(FromArgs)]
315 #[argh(subcommand, name = "trim")]
316 /// Trim pages in the staging memory
317 pub struct SwapTrimCommand {
318     #[argh(positional, arg_name = "VM_SOCKET")]
319     /// VM Socket path
320     pub socket_path: String,
321 }
322 
323 #[derive(FromArgs)]
324 #[argh(subcommand, name = "out")]
325 /// Swap out staging memory to swap file
326 pub struct SwapOutCommand {
327     #[argh(positional, arg_name = "VM_SOCKET")]
328     /// VM Socket path
329     pub socket_path: String,
330 }
331 
332 #[derive(FromArgs)]
333 #[argh(subcommand, name = "disable")]
334 /// Disable vmm-swap of a VM
335 pub struct SwapDisableCommand {
336     #[argh(positional, arg_name = "VM_SOCKET")]
337     /// VM Socket path
338     pub socket_path: String,
339 }
340 
341 #[derive(FromArgs)]
342 #[argh(subcommand, name = "status")]
343 /// Get vmm-swap status of a VM
344 pub struct SwapStatusCommand {
345     #[argh(positional, arg_name = "VM_SOCKET")]
346     /// VM Socket path
347     pub socket_path: String,
348 }
349 
350 /// Vmm-swap commands
351 #[derive(FromArgs)]
352 #[argh(subcommand, name = "swap")]
353 pub struct SwapCommand {
354     #[argh(subcommand)]
355     pub nested: SwapSubcommands,
356 }
357 
358 #[derive(FromArgs)]
359 #[argh(subcommand)]
360 pub enum SwapSubcommands {
361     Enable(SwapEnableCommand),
362     Trim(SwapTrimCommand),
363     SwapOut(SwapOutCommand),
364     Disable(SwapDisableCommand),
365     Status(SwapStatusCommand),
366 }
367 
368 #[derive(FromArgs)]
369 #[argh(subcommand, name = "powerbtn")]
370 /// Triggers a power button event in the crosvm instance
371 pub struct PowerbtnCommand {
372     #[argh(positional, arg_name = "VM_SOCKET")]
373     /// VM Socket path
374     pub socket_path: String,
375 }
376 
377 #[derive(FromArgs)]
378 #[argh(subcommand, name = "sleepbtn")]
379 /// Triggers a sleep button event in the crosvm instance
380 pub struct SleepCommand {
381     #[argh(positional, arg_name = "VM_SOCKET")]
382     /// VM Socket path
383     pub socket_path: String,
384 }
385 
386 #[derive(FromArgs)]
387 #[argh(subcommand, name = "gpe")]
388 /// Injects a general-purpose event into the crosvm instance
389 pub struct GpeCommand {
390     #[argh(positional)]
391     /// GPE #
392     pub gpe: u32,
393     #[argh(positional, arg_name = "VM_SOCKET")]
394     /// VM Socket path
395     pub socket_path: String,
396 }
397 
398 #[derive(FromArgs)]
399 #[argh(subcommand, name = "usb")]
400 /// Manage attached virtual USB devices.
401 pub struct UsbCommand {
402     #[argh(subcommand)]
403     pub command: UsbSubCommand,
404 }
405 
406 #[cfg(feature = "gpu")]
407 #[derive(FromArgs)]
408 #[argh(subcommand, name = "gpu")]
409 /// Manage attached virtual GPU device.
410 pub struct GpuCommand {
411     #[argh(subcommand)]
412     pub command: GpuSubCommand,
413 }
414 
415 #[derive(FromArgs)]
416 #[argh(subcommand, name = "version")]
417 /// Show package version.
418 pub struct VersionCommand {}
419 
420 #[derive(FromArgs)]
421 #[argh(subcommand, name = "add")]
422 /// ADD
423 pub struct VfioAddSubCommand {
424     #[argh(positional)]
425     /// path to host's vfio sysfs
426     pub vfio_path: PathBuf,
427     #[argh(positional, arg_name = "VM_SOCKET")]
428     /// VM Socket path
429     pub socket_path: String,
430 }
431 
432 #[derive(FromArgs)]
433 #[argh(subcommand, name = "remove")]
434 /// REMOVE
435 pub struct VfioRemoveSubCommand {
436     #[argh(positional)]
437     /// path to host's vfio sysfs
438     pub vfio_path: PathBuf,
439     #[argh(positional, arg_name = "VM_SOCKET")]
440     /// VM Socket path
441     pub socket_path: String,
442 }
443 
444 #[derive(FromArgs)]
445 #[argh(subcommand)]
446 pub enum VfioSubCommand {
447     Add(VfioAddSubCommand),
448     Remove(VfioRemoveSubCommand),
449 }
450 
451 #[derive(FromArgs)]
452 #[argh(subcommand, name = "vfio")]
453 /// add/remove host vfio pci device into guest
454 pub struct VfioCrosvmCommand {
455     #[argh(subcommand)]
456     pub command: VfioSubCommand,
457 }
458 
459 #[derive(FromArgs)]
460 #[argh(subcommand, name = "device")]
461 /// Start a device process
462 pub struct DeviceCommand {
463     /// configure async executor backend; "uring" or "epoll" on Linux, "handle" on Windows.
464     /// If this option is omitted on Linux, "epoll" is used by default.
465     #[argh(option, arg_name = "EXECUTOR")]
466     pub async_executor: Option<ExecutorKind>,
467 
468     #[argh(subcommand)]
469     pub command: DeviceSubcommand,
470 }
471 
472 #[derive(FromArgs)]
473 #[argh(subcommand)]
474 /// Cross-platform Devices
475 pub enum CrossPlatformDevicesCommands {
476     Block(device::BlockOptions),
477     #[cfg(feature = "gpu")]
478     Gpu(device::GpuOptions),
479     Net(device::NetOptions),
480     #[cfg(feature = "audio")]
481     Snd(device::SndOptions),
482 }
483 
484 #[derive(argh_helpers::FlattenSubcommand)]
485 pub enum DeviceSubcommand {
486     CrossPlatform(CrossPlatformDevicesCommands),
487     Sys(super::sys::cmdline::DeviceSubcommand),
488 }
489 
490 #[cfg(feature = "gpu")]
491 #[derive(FromArgs)]
492 #[argh(subcommand)]
493 pub enum GpuSubCommand {
494     AddDisplays(GpuAddDisplaysCommand),
495     ListDisplays(GpuListDisplaysCommand),
496     RemoveDisplays(GpuRemoveDisplaysCommand),
497 }
498 
499 #[cfg(feature = "gpu")]
500 #[derive(FromArgs)]
501 /// Attach a new display to the GPU device.
502 #[argh(subcommand, name = "add-displays")]
503 pub struct GpuAddDisplaysCommand {
504     #[argh(option)]
505     /// displays
506     pub gpu_display: Vec<GpuDisplayParameters>,
507 
508     #[argh(positional, arg_name = "VM_SOCKET")]
509     /// VM Socket path
510     pub socket_path: String,
511 }
512 
513 #[cfg(feature = "gpu")]
514 #[derive(FromArgs)]
515 /// List the displays currently attached to the GPU device.
516 #[argh(subcommand, name = "list-displays")]
517 pub struct GpuListDisplaysCommand {
518     #[argh(positional, arg_name = "VM_SOCKET")]
519     /// VM Socket path
520     pub socket_path: String,
521 }
522 
523 #[cfg(feature = "gpu")]
524 #[derive(FromArgs)]
525 /// Detach an existing display from the GPU device.
526 #[argh(subcommand, name = "remove-displays")]
527 pub struct GpuRemoveDisplaysCommand {
528     #[argh(option)]
529     /// display id
530     pub display_id: Vec<u32>,
531     #[argh(positional, arg_name = "VM_SOCKET")]
532     /// VM Socket path
533     pub socket_path: String,
534 }
535 
536 #[derive(FromArgs)]
537 #[argh(subcommand)]
538 pub enum UsbSubCommand {
539     Attach(UsbAttachCommand),
540     Detach(UsbDetachCommand),
541     List(UsbListCommand),
542 }
543 
544 #[derive(FromArgs)]
545 /// Attach usb device
546 #[argh(subcommand, name = "attach")]
547 pub struct UsbAttachCommand {
548     #[argh(
549         positional,
550         arg_name = "BUS_ID:ADDR:BUS_NUM:DEV_NUM",
551         from_str_fn(parse_bus_id_addr)
552     )]
553     pub addr: (u8, u8, u16, u16),
554     #[argh(positional)]
555     /// usb device path
556     pub dev_path: String,
557     #[argh(positional, arg_name = "VM_SOCKET")]
558     /// VM Socket path
559     pub socket_path: String,
560 }
561 
562 #[derive(FromArgs)]
563 /// Detach usb device
564 #[argh(subcommand, name = "detach")]
565 pub struct UsbDetachCommand {
566     #[argh(positional, arg_name = "PORT")]
567     /// usb port
568     pub port: u8,
569     #[argh(positional, arg_name = "VM_SOCKET")]
570     /// VM Socket path
571     pub socket_path: String,
572 }
573 
574 #[derive(FromArgs)]
575 /// Detach usb device
576 #[argh(subcommand, name = "list")]
577 pub struct UsbListCommand {
578     #[argh(positional, arg_name = "VM_SOCKET")]
579     /// VM Socket path
580     pub socket_path: String,
581 }
582 
583 /// Structure containing the parameters for a single disk as well as a unique counter increasing
584 /// each time a new disk parameter is parsed.
585 ///
586 /// This allows the letters assigned to each disk to reflect the order of their declaration, as
587 /// we have several options for specifying disks (rwroot, root, etc) and order can thus be lost
588 /// when they are aggregated.
589 #[derive(Deserialize, Debug)]
590 #[serde(deny_unknown_fields, from = "DiskOption")]
591 struct DiskOptionWithId {
592     disk_option: DiskOption,
593     index: usize,
594 }
595 
596 /// FromStr implementation for argh.
597 impl FromStr for DiskOptionWithId {
598     type Err = String;
599 
from_str(s: &str) -> Result<Self, Self::Err>600     fn from_str(s: &str) -> Result<Self, Self::Err> {
601         let disk_option: DiskOption = from_key_values(s)?;
602         Ok(Self::from(disk_option))
603     }
604 }
605 
606 /// Assign the next id to `disk_option`.
607 impl From<DiskOption> for DiskOptionWithId {
from(disk_option: DiskOption) -> Self608     fn from(disk_option: DiskOption) -> Self {
609         static DISK_COUNTER: AtomicUsize = AtomicUsize::new(0);
610         Self {
611             disk_option,
612             index: DISK_COUNTER.fetch_add(1, Ordering::Relaxed),
613         }
614     }
615 }
616 
617 #[derive(FromArgs)]
618 #[argh(subcommand, name = "snapshot", description = "Snapshot commands")]
619 /// Snapshot commands
620 pub struct SnapshotCommand {
621     #[argh(subcommand)]
622     pub snapshot_command: SnapshotSubCommands,
623 }
624 
625 #[derive(FromArgs)]
626 #[argh(subcommand, name = "take")]
627 /// Take a snapshot of the VM
628 pub struct SnapshotTakeCommand {
629     #[argh(positional, arg_name = "snapshot_path")]
630     /// VM Image path
631     pub snapshot_path: PathBuf,
632     #[argh(positional, arg_name = "VM_SOCKET")]
633     /// VM Socket path
634     pub socket_path: String,
635 }
636 
637 #[derive(FromArgs)]
638 #[argh(subcommand, name = "restore")]
639 /// Restore VM state from a snapshot created by take
640 pub struct SnapshotRestoreCommand {
641     #[argh(positional)]
642     /// path to snapshot to restore
643     pub snapshot_path: PathBuf,
644     #[argh(positional, arg_name = "VM_SOCKET")]
645     /// VM Socket path
646     pub socket_path: String,
647 }
648 
649 #[derive(FromArgs)]
650 #[argh(subcommand)]
651 /// Snapshot commands
652 pub enum SnapshotSubCommands {
653     Take(SnapshotTakeCommand),
654     Restore(SnapshotRestoreCommand),
655 }
656 
657 /// Container for GpuParameters that have been fixed after parsing using serde.
658 ///
659 /// This deserializes as a regular `GpuParameters` and applies validation.
660 #[cfg(feature = "gpu")]
661 #[derive(Debug, Deserialize, FromKeyValues)]
662 #[serde(try_from = "GpuParameters")]
663 pub struct FixedGpuParameters(pub GpuParameters);
664 
665 #[cfg(feature = "gpu")]
666 impl TryFrom<GpuParameters> for FixedGpuParameters {
667     type Error = String;
668 
try_from(gpu_params: GpuParameters) -> Result<Self, Self::Error>669     fn try_from(gpu_params: GpuParameters) -> Result<Self, Self::Error> {
670         fixup_gpu_options(gpu_params)
671     }
672 }
673 
674 /// Container for `GpuDisplayParameters` that have been fixed after parsing using serde.
675 ///
676 /// This deserializes as a regular `GpuDisplayParameters` and applies validation.
677 /// TODO(b/260101753): Remove this once the old syntax for specifying DPI is deprecated.
678 #[cfg(feature = "gpu")]
679 #[derive(Debug, Deserialize, FromKeyValues)]
680 #[serde(try_from = "GpuDisplayParameters")]
681 pub struct FixedGpuDisplayParameters(pub GpuDisplayParameters);
682 
683 #[cfg(feature = "gpu")]
684 impl TryFrom<GpuDisplayParameters> for FixedGpuDisplayParameters {
685     type Error = String;
686 
try_from(gpu_display_params: GpuDisplayParameters) -> Result<Self, Self::Error>687     fn try_from(gpu_display_params: GpuDisplayParameters) -> Result<Self, Self::Error> {
688         fixup_gpu_display_options(gpu_display_params)
689     }
690 }
691 
692 /// Deserialize `config_file` into a `RunCommand`.
693 #[cfg(feature = "config-file")]
load_config_file<P: AsRef<Path>>(config_file: P) -> Result<Box<RunCommand>, String>694 fn load_config_file<P: AsRef<Path>>(config_file: P) -> Result<Box<RunCommand>, String> {
695     let config = std::fs::read_to_string(config_file).map_err(|e| e.to_string())?;
696 
697     serde_json::from_str(&config)
698         .map_err(|e| e.to_string())
699         .map(Box::new)
700 }
701 
702 /// Overwrite an `Option<T>` if the right member is set.
703 ///
704 /// The default merge strategy for `Option<T>` is to merge `right` into `left` iff `left.is_none()`.
705 /// This doesn't play well with our need to overwrite options that have already been set.
706 ///
707 /// `overwrite_option` merges `right` into `left` iff `right.is_some()`, which allows us to override
708 /// previously-set options.
overwrite_option<T>(left: &mut Option<T>, right: Option<T>)709 fn overwrite_option<T>(left: &mut Option<T>, right: Option<T>) {
710     if right.is_some() {
711         *left = right;
712     }
713 }
714 
715 #[allow(dead_code)]
overwrite<T>(left: &mut T, right: T)716 fn overwrite<T>(left: &mut T, right: T) {
717     let _ = std::mem::replace(left, right);
718 }
719 
bool_default_true() -> bool720 fn bool_default_true() -> bool {
721     true
722 }
723 
724 /// Each field of this structure has a dual use:
725 ///
726 /// 1) As a command-line parameter, controlled by the `#[argh]` helper attribute.
727 /// 2) As a configuration file parameter, controlled by the `#[serde]` helper attribute.
728 ///
729 /// For consistency, the names should be the same and use kebab-case for both uses, so please
730 /// refrain from using renaming directives and give the field the desired parameter name (it will
731 /// automatically be converted to kebab-case).
732 ///
733 /// For consistency and convenience, all parameters should be deserializable by `serde_keyvalue`, as
734 /// this will automatically provide the same schema for both the command-line and configuration
735 /// file. This is particularly important for fields that are enums or structs, for which extra
736 /// parameters can be specified. Make sure to annotate your struct/enum with
737 /// `#[serde(deny_unknown_fields, rename_all = "kebab-case")]` so invalid fields are properly
738 /// rejected and all members are converted to kebab-case.
739 ///
740 /// Each member should also have a `#[merge]` helper attribute, which defines the strategy to use
741 /// when merging two configurations into one. This happens when e.g. the user has specified extra
742 /// command-line arguments along with a configuration file. In this case, the `RunCommand` created
743 /// from the command-line arguments will be merged into the `RunCommand` deserialized from the
744 /// configuration file.
745 ///
746 /// The rule of thumb for `#[merge]` attributes is that parameters that can only be specified once
747 /// (typically of `Option` type) should be overridden (`#[merge(strategy = overwrite_option)]`),
748 /// while parameters that can be specified several times (typically of `Vec` type) should be
749 /// appended (`#[merge(strategy = append)]`), but there might also be exceptions.
750 ///
751 /// The doccomment of the member will be displayed as its help message with `--help`.
752 ///
753 /// Note that many parameters are marked with `#[serde(skip)]` and annotated with b/255223604. This
754 /// is because we only want to enable parameters in the config file after they undergo a proper
755 /// review to make sure they won't be obsoleted.
756 #[remain::sorted]
757 #[argh_helpers::pad_description_for_argh]
758 #[derive(FromArgs, Deserialize, merge::Merge)]
759 #[argh(subcommand, name = "run", description = "Start a new crosvm instance")]
760 #[serde(deny_unknown_fields, rename_all = "kebab-case")]
761 pub struct RunCommand {
762     #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), unix))]
763     #[argh(switch)]
764     #[serde(default)]
765     #[merge(strategy = overwrite_false)]
766     /// enable AC adapter device
767     /// It purpose is to emulate ACPI ACPI0003 device, replicate and propagate the
768     /// ac adapter status from the host to the guest.
769     pub ac_adapter: bool,
770 
771     #[cfg(feature = "audio")]
772     #[argh(
773         option,
774         from_str_fn(parse_ac97_options),
775         arg_name = "[backend=BACKEND,capture=true,capture_effect=EFFECT,client_type=TYPE,shm-fd=FD,client-fd=FD,server-fd=FD]"
776     )]
777     #[serde(skip)] // TODO(b/255223604)
778     #[merge(strategy = append)]
779     /// comma separated key=value pairs for setting up Ac97 devices.
780     /// Can be given more than once.
781     /// Possible key values:
782     ///     backend=(null, cras) - Where to route the audio
783     ///          device. If not provided, backend will default to
784     ///          null. `null` for /dev/null, cras for CRAS server.
785     ///     capture - Enable audio capture
786     ///     capture_effects - | separated effects to be enabled for
787     ///         recording. The only supported effect value now is
788     ///         EchoCancellation or aec.
789     ///     client_type - Set specific client type for cras backend.
790     ///     socket_type - Set specific socket type for cras backend.
791     pub ac97: Vec<Ac97Parameters>,
792 
793     #[argh(option, arg_name = "PATH")]
794     #[serde(skip)] // TODO(b/255223604)
795     #[merge(strategy = append)]
796     /// path to user provided ACPI table
797     pub acpi_table: Vec<PathBuf>,
798 
799     #[argh(option)]
800     #[serde(skip)] // TODO(b/255223604)
801     #[merge(strategy = overwrite_option)]
802     /// path to Android fstab
803     pub android_fstab: Option<PathBuf>,
804 
805     /// configure async executor backend; "uring" or "epoll" on Linux, "handle" on Windows.
806     /// If this option is omitted on Linux, "epoll" is used by default.
807     #[argh(option, arg_name = "EXECUTOR")]
808     #[serde(skip)] // TODO(b/255223604)
809     pub async_executor: Option<ExecutorKind>,
810 
811     #[argh(option, arg_name = "N")]
812     #[serde(skip)] // TODO(b/255223604)
813     #[merge(strategy = overwrite_option)]
814     /// amount to bias balance of memory between host and guest as the balloon inflates, in mib.
815     pub balloon_bias_mib: Option<i64>,
816 
817     #[argh(option, arg_name = "PATH")]
818     #[serde(skip)] // TODO(b/255223604)
819     #[merge(strategy = overwrite_option)]
820     /// path for balloon controller socket.
821     pub balloon_control: Option<PathBuf>,
822 
823     #[argh(switch)]
824     #[serde(skip)] // TODO(b/255223604)
825     #[merge(strategy = overwrite_false)]
826     /// enable page reporting in balloon.
827     pub balloon_page_reporting: bool,
828 
829     #[argh(switch)]
830     #[serde(skip)] // TODO(b/255223604)
831     #[merge(strategy = overwrite_false)]
832     /// enable working set size reporting in balloon.
833     pub balloon_wss_reporting: bool,
834 
835     #[argh(option)]
836     /// comma separated key=value pairs for setting up battery
837     /// device
838     /// Possible key values:
839     ///     type=goldfish - type of battery emulation, defaults to
840     ///     goldfish
841     #[merge(strategy = overwrite_option)]
842     pub battery: Option<BatteryConfig>,
843 
844     #[argh(option)]
845     #[serde(skip)] // TODO(b/255223604)
846     #[merge(strategy = overwrite_option)]
847     /// path to BIOS/firmware ROM
848     pub bios: Option<PathBuf>,
849 
850     #[argh(option, short = 'b', arg_name = "PATH[,key=value[,key=value[,...]]]")]
851     #[serde(default)]
852     #[merge(strategy = append)]
853     /// parameters for setting up a block device.
854     /// Valid keys:
855     ///     path=PATH - Path to the disk image. Can be specified
856     ///         without the key as the first argument.
857     ///     ro=BOOL - Whether the block should be read-only.
858     ///         (default: false)
859     ///     root=BOOL - Whether the block device should be mounted
860     ///         as the root filesystem. This will add the required
861     ///         parameters to the kernel command-line. Can only be
862     ///         specified once. (default: false)
863     ///     sparse=BOOL - Indicates whether the disk should support
864     ///         the discard operation. (default: true)
865     ///     block-size=BYTES - Set the reported block size of the
866     ///         disk. (default: 512)
867     ///     id=STRING - Set the block device identifier to an ASCII
868     ///         string, up to 20 characters. (default: no ID)
869     ///     direct=BOOL - Use O_DIRECT mode to bypass page cache.
870     ///         (default: false)
871     ///     async-executor=epoll|uring - set the async executor kind
872     ///         to simulate the block device with. This takes
873     ///         precedence over the global --async-executor option.
874     ///     multiple-workers=BOOL - (Experimental) run multiple
875     ///         worker threads in parallel. this option is not
876     ///         effective for vhost-user blk device.
877     ///         (default: false)
878     block: Vec<DiskOptionWithId>,
879 
880     /// ratelimit enforced on detected bus locks in guest.
881     /// The default value of the bus_lock_ratelimit is 0 per second,
882     /// which means no limitation on the guest's bus locks.
883     #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), unix))]
884     #[argh(option)]
885     pub bus_lock_ratelimit: Option<u64>,
886 
887     #[cfg(feature = "config-file")]
888     #[argh(option, arg_name = "CONFIG_FILE", from_str_fn(load_config_file))]
889     // TODO(b/218223240) We only allow one configuration file because accurate merging is not
890     // possible on boolean fields. We would need to convert these to `Option<bool>` but
891     // unfortunately argh is currently unable to recognize these as switches.
892     //
893     // For now, we only allow one configuration file to be specified. Since argh's switches can
894     // only be true, and the command-line parameters are merged last, merging using
895     // `merging::overwrite_false` is guaranteed to be accurate since only true values can be
896     // explicitly specified through the command-line.
897     //
898     // Eventually we will want to turn this into a `Vec<Self>` so several configuration files can
899     // be passed, and remove the `#[serde(skip)]` attribute so they can be included from other
900     // configuration files as well.
901     #[serde(skip)]
902     #[merge(skip)]
903     /// path to a JSON configuration file to load.
904     ///
905     /// The options specified in the file can be overridden or augmented by other command-line
906     /// parameters.
907     cfg: Option<Box<Self>>,
908 
909     #[argh(option, arg_name = "CID")]
910     #[serde(skip)] // Deprecated - use `vsock` instead.
911     #[merge(strategy = overwrite_option)]
912     /// context ID for virtual sockets.
913     pub cid: Option<u64>,
914 
915     #[cfg(unix)]
916     #[argh(
917         option,
918         arg_name = "unpin_policy=POLICY,unpin_interval=NUM,unpin_limit=NUM,unpin_gen_threshold=NUM"
919     )]
920     #[serde(skip)] // TODO(b/255223604)
921     #[merge(strategy = overwrite_option)]
922     /// comma separated key=value pairs for setting up coiommu
923     /// devices.
924     /// Possible key values:
925     ///     unpin_policy=lru - LRU unpin policy.
926     ///     unpin_interval=NUM - Unpin interval time in seconds.
927     ///     unpin_limit=NUM - Unpin limit for each unpin cycle, in
928     ///        unit of page count. 0 is invalid.
929     ///     unpin_gen_threshold=NUM -  Number of unpin intervals a
930     ///        pinned page must be busy for to be aged into the
931     ///        older which is less frequently checked generation.
932     pub coiommu: Option<devices::CoIommuParameters>,
933 
934     #[argh(option, default = "true")]
935     #[merge(strategy = overwrite)]
936     #[serde(default = "bool_default_true")]
937     /// protect VM threads from hyperthreading-based attacks by scheduling them on different cores.
938     /// Enabled by default, and required for per_vm_core_scheduling.
939     pub core_scheduling: bool,
940 
941     #[argh(option, arg_name = "CPUSET", from_str_fn(parse_cpu_affinity))]
942     #[serde(skip)] // TODO(b/255223604)
943     #[merge(strategy = overwrite_option)]
944     /// comma-separated list of CPUs or CPU ranges to run VCPUs on (e.g. 0,1-3,5)
945     /// or colon-separated list of assignments of guest to host CPU assignments (e.g. 0=0:1=1:2=2) (default: no mask)
946     pub cpu_affinity: Option<VcpuAffinity>,
947 
948     #[argh(
949         option,
950         arg_name = "CPU=CAP[,CPU=CAP[,...]]",
951         from_str_fn(parse_cpu_capacity)
952     )]
953     #[serde(skip)] // TODO(b/255223604)
954     #[merge(strategy = overwrite_option)]
955     /// set the relative capacity of the given CPU (default: no capacity)
956     pub cpu_capacity: Option<BTreeMap<usize, u32>>, // CPU index -> capacity
957 
958     #[argh(option, arg_name = "CPUSET")]
959     #[serde(skip)] // Deprecated - use `cpu clusters=[...]` instead.
960     #[merge(strategy = append)]
961     /// group the given CPUs into a cluster (default: no clusters)
962     pub cpu_cluster: Vec<CpuSet>,
963 
964     #[argh(option, short = 'c')]
965     #[merge(strategy = overwrite_option)]
966     /// cpu parameters.
967     /// Possible key values:
968     ///     num-cores=NUM - number of VCPUs. (default: 1)
969     ///     clusters=[[CLUSTER],...] - CPU clusters (default: None)
970     ///       Each CLUSTER is a set containing a list of CPUs
971     ///       that should belong to the same cluster. Individual
972     ///       CPU ids or ranges can be specified, comma-separated.
973     ///       Examples:
974     ///       clusters=[[0],[1],[2],[3]] - creates 4 clusters, one
975     ///         for each specified core.
976     ///       clusters=[[0-3]] - creates a cluster for cores 0 to 3
977     ///         included.
978     ///       clusters=[[0,2],[1,3],[4-7,12]] - creates one cluster
979     ///         for cores 0 and 2, another one for cores 1 and 3,
980     ///         and one last for cores 4, 5, 6, 7 and 12.
981     ///     core-types=[atom=[CPUSET],core=[CPUSET]] - Hybrid core types. (default: None)
982     ///       Set the type of virtual hybrid CPUs. Now it supports
983     ///       to set intel Atom or intel Core types.
984     ///       Examples:
985     ///       core-types=[atom=[0,1],core=[2,3]] - set vCPU 0 and
986     ///       vCPU 1 as intel Atom type, also set vCPU 2 and vCPU 3
987     ///       as intel Core type.
988     pub cpus: Option<CpuOptions>,
989 
990     #[cfg(feature = "crash-report")]
991     #[argh(option, arg_name = "\\\\.\\pipe\\PIPE_NAME")]
992     #[serde(skip)] // TODO(b/255223604)
993     #[merge(strategy = overwrite_option)]
994     /// the crash handler ipc pipe name.
995     pub crash_pipe_name: Option<String>,
996 
997     #[argh(switch)]
998     #[serde(skip)] // TODO(b/255223604)
999     #[merge(strategy = overwrite_false)]
1000     /// don't set VCPUs real-time until make-rt command is run
1001     pub delay_rt: bool,
1002 
1003     #[cfg(feature = "direct")]
1004     #[argh(option, arg_name = "irq")]
1005     #[serde(skip)] // TODO(b/255223604)
1006     #[merge(strategy = overwrite)]
1007     /// enable interrupt passthrough
1008     pub direct_edge_irq: Vec<u32>,
1009 
1010     #[cfg(feature = "direct")]
1011     #[argh(option, arg_name = "event=gbllock|powerbtn|sleepbtn|rtc")]
1012     #[serde(skip)] // TODO(b/255223604)
1013     #[merge(strategy = overwrite)]
1014     /// enable ACPI fixed event interrupt and register access passthrough
1015     pub direct_fixed_event: Vec<devices::ACPIPMFixedEvent>,
1016 
1017     #[cfg(feature = "direct")]
1018     #[argh(option, arg_name = "gpe")]
1019     #[serde(skip)] // TODO(b/255223604)
1020     #[merge(strategy = overwrite)]
1021     /// enable GPE interrupt and register access passthrough
1022     pub direct_gpe: Vec<u32>,
1023 
1024     #[cfg(feature = "direct")]
1025     #[argh(option, arg_name = "irq")]
1026     #[serde(skip)] // TODO(b/255223604)
1027     #[merge(strategy = overwrite)]
1028     /// enable interrupt passthrough
1029     pub direct_level_irq: Vec<u32>,
1030 
1031     #[cfg(feature = "direct")]
1032     #[argh(
1033         option,
1034         arg_name = "PATH@RANGE[,RANGE[,...]]",
1035         from_str_fn(parse_direct_io_options)
1036     )]
1037     #[serde(skip)] // TODO(b/255223604)
1038     #[merge(strategy = overwrite_option)]
1039     /// path and ranges for direct memory mapped I/O access. RANGE may be decimal or hex (starting with 0x)
1040     pub direct_mmio: Option<DirectIoOption>,
1041 
1042     #[cfg(feature = "direct")]
1043     #[argh(
1044         option,
1045         arg_name = "PATH@RANGE[,RANGE[,...]]",
1046         from_str_fn(parse_direct_io_options)
1047     )]
1048     #[serde(skip)] // TODO(b/255223604)
1049     #[merge(strategy = overwrite_option)]
1050     /// path and ranges for direct port mapped I/O access. RANGE may be decimal or hex (starting with 0x)
1051     pub direct_pmio: Option<DirectIoOption>,
1052 
1053     #[argh(switch)]
1054     #[serde(skip)] // TODO(b/255223604)
1055     #[merge(strategy = overwrite_false)]
1056     /// run all devices in one, non-sandboxed process
1057     pub disable_sandbox: bool,
1058 
1059     #[argh(switch)]
1060     #[serde(skip)] // TODO(b/255223604)
1061     #[merge(strategy = overwrite_false)]
1062     /// disable INTx in virtio devices
1063     pub disable_virtio_intx: bool,
1064 
1065     #[argh(option, short = 'd', arg_name = "PATH[,key=value[,key=value[,...]]]")]
1066     #[serde(skip)] // Deprecated - use `block` instead.
1067     #[merge(strategy = append)]
1068     /// path to a disk image followed by optional comma-separated
1069     /// options.
1070     /// Valid keys:
1071     ///    sparse=BOOL - Indicates whether the disk should support
1072     ///        the discard operation (default: true)
1073     ///    block_size=BYTES - Set the reported block size of the
1074     ///        disk (default: 512)
1075     ///    id=STRING - Set the block device identifier to an ASCII
1076     ///        string, up to 20 characters (default: no ID)
1077     ///    o_direct=BOOL - Use O_DIRECT mode to bypass page cache"
1078     disk: Vec<DiskOptionWithId>,
1079 
1080     #[argh(switch)]
1081     #[serde(skip)] // TODO(b/255223604)
1082     #[merge(strategy = overwrite_false)]
1083     /// capture keyboard input from the display window
1084     pub display_window_keyboard: bool,
1085 
1086     #[argh(switch)]
1087     #[serde(skip)] // TODO(b/255223604)
1088     #[merge(strategy = overwrite_false)]
1089     /// capture keyboard input from the display window
1090     pub display_window_mouse: bool,
1091 
1092     #[argh(option, arg_name = "DIR")]
1093     #[serde(skip)] // TODO(b/255223604)
1094     #[merge(strategy = overwrite_option)]
1095     /// directory with smbios_entry_point/DMI files
1096     pub dmi: Option<PathBuf>,
1097 
1098     #[argh(option, long = "dump-device-tree-blob", arg_name = "FILE")]
1099     #[serde(skip)] // TODO(b/255223604)
1100     #[merge(strategy = overwrite_option)]
1101     /// dump generated device tree as a DTB file
1102     pub dump_device_tree_blob: Option<PathBuf>,
1103 
1104     #[argh(switch)]
1105     #[serde(skip)] // TODO(b/255223604)
1106     #[merge(strategy = overwrite_false)]
1107     /// expose HWP feature to the guest
1108     pub enable_hwp: bool,
1109 
1110     #[argh(switch)]
1111     #[serde(skip)] // TODO(b/255223604)
1112     #[merge(strategy = overwrite_false)]
1113     /// expose Power and Perfomance (PnP) data to guest and guest can show these PnP data
1114     pub enable_pnp_data: bool,
1115 
1116     #[argh(option, arg_name = "PATH")]
1117     #[serde(skip)] // TODO(b/255223604)
1118     #[merge(strategy = append)]
1119     /// 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
1120     pub evdev: Vec<PathBuf>,
1121 
1122     #[cfg(windows)]
1123     #[argh(switch)]
1124     #[serde(skip)] // TODO(b/255223604)
1125     #[merge(strategy = overwrite_false)]
1126     /// gather and display statistics on Vm Exits and Bus Reads/Writes.
1127     pub exit_stats: bool,
1128 
1129     #[argh(
1130         option,
1131         arg_name = "addr=NUM,size=SIZE,path=PATH[,offset=NUM][,rw][,sync]"
1132     )]
1133     #[serde(skip)] // TODO(b/255223604)
1134     #[merge(strategy = append)]
1135     /// map the given file into guest memory at the specified
1136     /// address.
1137     /// Parameters (addr, size, path are required):
1138     ///     addr=NUM - guest physical address to map at
1139     ///     size=NUM - amount of memory to map
1140     ///     path=PATH - path to backing file/device to map
1141     ///     offset=NUM - offset in backing file (default 0)
1142     ///     rw - make the mapping writable (default readonly)
1143     ///     sync - open backing file with O_SYNC
1144     ///     align - whether to adjust addr and size to page
1145     ///        boundaries implicitly
1146     pub file_backed_mapping: Vec<FileBackedMappingParameters>,
1147 
1148     #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
1149     #[argh(switch)]
1150     #[serde(skip)] // TODO(b/255223604)
1151     #[merge(strategy = overwrite_false)]
1152     /// force use of a calibrated TSC cpuid leaf (0x15) even if the hypervisor
1153     /// doesn't require one.
1154     pub force_calibrated_tsc_leaf: bool,
1155 
1156     #[cfg(feature = "gdb")]
1157     #[argh(option, arg_name = "PORT")]
1158     #[merge(strategy = overwrite_option)]
1159     /// (EXPERIMENTAL) gdb on the given port
1160     pub gdb: Option<u32>,
1161 
1162     #[cfg(feature = "gpu")]
1163     #[argh(option)]
1164     // Although `gpu` is a vector, we are currently limited to a single GPU device due to the
1165     // resource bridge and interaction with other video devices. We do use a vector so the GPU
1166     // device can be specified like other device classes in the configuration file, and because we
1167     // hope to lift this limitation eventually.
1168     #[serde(skip)] // TODO(b/255223604)
1169     #[merge(strategy = append)]
1170     /// (EXPERIMENTAL) Comma separated key=value pairs for setting
1171     /// up a virtio-gpu device
1172     /// Possible key values:
1173     ///     backend=(2d|virglrenderer|gfxstream) - Which backend to
1174     ///        use for virtio-gpu (determining rendering protocol)
1175     ///     displays=[[GpuDisplayParameters]] - The list of virtual
1176     ///         displays to create. See the possible key values for
1177     ///         GpuDisplayParameters in the section below.
1178     ///     context-types=LIST - The list of supported context
1179     ///       types, separated by ':' (default: no contexts enabled)
1180     ///     width=INT - The width of the virtual display connected
1181     ///        to the virtio-gpu.
1182     ///        Deprecated - use `displays` instead.
1183     ///     height=INT - The height of the virtual display
1184     ///        connected to the virtio-gpu.
1185     ///        Deprecated - use `displays` instead.
1186     ///     egl[=true|=false] - If the backend should use a EGL
1187     ///        context for rendering.
1188     ///     glx[=true|=false] - If the backend should use a GLX
1189     ///        context for rendering.
1190     ///     surfaceless[=true|=false] - If the backend should use a
1191     ///         surfaceless context for rendering.
1192     ///     angle[=true|=false] - If the gfxstream backend should
1193     ///        use ANGLE (OpenGL on Vulkan) as its native OpenGL
1194     ///        driver.
1195     ///     vulkan[=true|=false] - If the backend should support
1196     ///        vulkan
1197     ///     wsi=vk - If the gfxstream backend should use the Vulkan
1198     ///        swapchain to draw on a window
1199     ///     cache-path=PATH - The path to the virtio-gpu device
1200     ///        shader cache.
1201     ///     cache-size=SIZE - The maximum size of the shader cache.
1202     ///     pci-bar-size=SIZE - The size for the PCI BAR in bytes
1203     ///        (default 8gb).
1204     ///
1205     /// Possible key values for GpuDisplayParameters:
1206     ///     mode=(borderless_full_screen|windowed[width,height]) -
1207     ///        Whether to show the window on the host in full
1208     ///        screen or windowed mode. If not specified, windowed
1209     ///        mode is used by default. "windowed" can also be
1210     ///        specified explicitly to use a window size different
1211     ///        from the default one.
1212     ///     hidden[=true|=false] - If the display window is
1213     ///        initially hidden (default: false).
1214     ///     refresh-rate=INT - Force a specific vsync generation
1215     ///        rate in hertz on the guest (default: 60)
1216     ///     dpi=[INT,INT] - The horizontal and vertical DPI of the
1217     ///        display (default: [320,320])
1218     ///     horizontal-dpi=INT - The horizontal DPI of the display
1219     ///        (default: 320)
1220     ///        Deprecated - use `dpi` instead.
1221     ///     vertical-dpi=INT - The vertical DPI of the display
1222     ///        (default: 320)
1223     ///        Deprecated - use `dpi` instead.
1224     pub gpu: Vec<FixedGpuParameters>,
1225 
1226     #[cfg(all(unix, feature = "gpu"))]
1227     #[argh(option, arg_name = "PATH")]
1228     #[serde(skip)] // TODO(b/255223604)
1229     #[merge(strategy = overwrite_option)]
1230     /// move all vGPU threads to this Cgroup (default: nothing moves)
1231     pub gpu_cgroup_path: Option<PathBuf>,
1232 
1233     #[cfg(feature = "gpu")]
1234     #[argh(option)]
1235     #[serde(skip)] // TODO(b/255223604). Deprecated - use `gpu` instead.
1236     #[merge(strategy = append)]
1237     /// (EXPERIMENTAL) Comma separated key=value pairs for setting
1238     /// up a display on the virtio-gpu device. See comments for `gpu`
1239     /// for possible key values of GpuDisplayParameters.
1240     pub gpu_display: Vec<FixedGpuDisplayParameters>,
1241 
1242     #[cfg(all(unix, feature = "gpu", feature = "virgl_renderer_next"))]
1243     #[argh(option)]
1244     #[serde(skip)] // TODO(b/255223604)
1245     #[merge(strategy = overwrite_option)]
1246     /// (EXPERIMENTAL) Comma separated key=value pairs for setting
1247     /// up a render server for the virtio-gpu device
1248     /// Possible key values:
1249     ///     path=PATH - The path to the render server executable.
1250     ///     cache-path=PATH - The path to the render server shader
1251     ///         cache.
1252     ///     cache-size=SIZE - The maximum size of the shader cache
1253     ///     foz-db-list-path=PATH - The path to GPU foz db list
1254     ///         file for dynamically loading RO caches.
1255     pub gpu_render_server: Option<GpuRenderServerParameters>,
1256 
1257     #[cfg(all(unix, feature = "gpu"))]
1258     #[argh(option, arg_name = "PATH")]
1259     #[serde(skip)] // TODO(b/255223604)
1260     #[merge(strategy = overwrite_option)]
1261     /// move all vGPU server threads to this Cgroup (default: nothing moves)
1262     pub gpu_server_cgroup_path: Option<PathBuf>,
1263 
1264     #[argh(switch)]
1265     #[serde(skip)] // TODO(b/255223604)
1266     #[merge(strategy = overwrite_false)]
1267     /// use mirror cpu topology of Host for Guest VM, also copy some cpu feature to Guest VM
1268     pub host_cpu_topology: bool,
1269 
1270     #[cfg(windows)]
1271     #[argh(option, arg_name = "PATH")]
1272     #[serde(skip)] // TODO(b/255223604)
1273     #[merge(strategy = overwrite_option)]
1274     /// string representation of the host guid in registry format, for namespacing vsock connections.
1275     pub host_guid: Option<String>,
1276 
1277     #[cfg(unix)]
1278     #[argh(option, arg_name = "IP")]
1279     #[serde(skip)] // Deprecated - use `net` instead.
1280     #[merge(strategy = overwrite_option)]
1281     /// IP address to assign to host tap interface
1282     pub host_ip: Option<net::Ipv4Addr>,
1283 
1284     #[argh(switch)]
1285     #[serde(skip)] // TODO(b/255223604)
1286     #[merge(strategy = overwrite_false)]
1287     /// advise the kernel to use Huge Pages for guest memory mappings
1288     pub hugepages: bool,
1289 
1290     /// hypervisor backend
1291     #[argh(option)]
1292     #[merge(strategy = overwrite_option)]
1293     pub hypervisor: Option<HypervisorKind>,
1294 
1295     #[argh(option, arg_name = "N")]
1296     #[serde(skip)] // TODO(b/255223604)
1297     #[merge(strategy = overwrite_option)]
1298     /// amount of guest memory outside the balloon at boot in MiB. (default: --mem)
1299     pub init_mem: Option<u64>,
1300 
1301     #[argh(option, short = 'i', arg_name = "PATH")]
1302     #[merge(strategy = overwrite_option)]
1303     /// initial ramdisk to load
1304     pub initrd: Option<PathBuf>,
1305 
1306     #[cfg(windows)]
1307     #[argh(option, arg_name = "kernel|split|userspace")]
1308     #[serde(skip)] // TODO(b/255223604)
1309     #[merge(strategy = overwrite_option)]
1310     /// type of interrupt controller emulation.  \"split\" is only available for x86 KVM.
1311     pub irqchip: Option<IrqChipKind>,
1312 
1313     #[argh(switch)]
1314     #[serde(skip)] // TODO(b/255223604)
1315     #[merge(strategy = overwrite_false)]
1316     /// allow to enable ITMT scheduling feature in VM. The success of enabling depends on HWP and ACPI CPPC support on hardware
1317     pub itmt: bool,
1318 
1319     #[argh(positional, arg_name = "KERNEL")]
1320     #[merge(strategy = overwrite_option)]
1321     /// bzImage of kernel to run
1322     pub kernel: Option<PathBuf>,
1323 
1324     #[cfg(windows)]
1325     #[argh(option, arg_name = "PATH")]
1326     #[serde(skip)] // TODO(b/255223604)
1327     #[merge(strategy = overwrite_option)]
1328     /// forward hypervisor kernel driver logs for this VM to a file.
1329     pub kernel_log_file: Option<String>,
1330 
1331     #[argh(option, arg_name = "PATH")]
1332     #[serde(skip)] // TODO(b/255223604)
1333     #[merge(strategy = append)]
1334     /// path to a socket from where to read keyboard input events and write status updates to
1335     pub keyboard: Vec<PathBuf>,
1336 
1337     #[cfg(unix)]
1338     #[argh(option, arg_name = "PATH")]
1339     #[serde(skip)] // Deprecated - use `hypervisor` instead.
1340     #[merge(strategy = overwrite_option)]
1341     /// path to the KVM device. (default /dev/kvm)
1342     pub kvm_device: Option<PathBuf>,
1343 
1344     #[cfg(unix)]
1345     #[argh(switch)]
1346     #[serde(skip)] // TODO(b/255223604)
1347     #[merge(strategy = overwrite_false)]
1348     /// disable host swap on guest VM pages.
1349     pub lock_guest_memory: bool,
1350 
1351     #[cfg(windows)]
1352     #[argh(option, arg_name = "PATH")]
1353     #[serde(skip)] // TODO(b/255223604)
1354     #[merge(strategy = overwrite_option)]
1355     /// redirect logs to the supplied log file at PATH rather than stderr. For multi-process mode, use --logs-directory instead
1356     pub log_file: Option<String>,
1357 
1358     #[cfg(windows)]
1359     #[argh(option, arg_name = "PATH")]
1360     #[serde(skip)] // TODO(b/255223604)
1361     #[merge(strategy = overwrite_option)]
1362     /// path to the logs directory used for crosvm processes. Logs will be sent to stderr if unset, and stderr/stdout will be uncaptured
1363     pub logs_directory: Option<String>,
1364 
1365     #[cfg(unix)]
1366     #[argh(option, arg_name = "MAC", long = "mac")]
1367     #[serde(skip)] // Deprecated - use `net` instead.
1368     #[merge(strategy = overwrite_option)]
1369     /// MAC address for VM
1370     pub mac_address: Option<net_util::MacAddress>,
1371 
1372     #[argh(option, short = 'm', arg_name = "N")]
1373     #[merge(strategy = overwrite_option)]
1374     /// memory parameters.
1375     /// Possible key values:
1376     ///     size=NUM - amount of guest memory in MiB. (default: 256)
1377     pub mem: Option<MemOptions>,
1378 
1379     #[argh(option, from_str_fn(parse_mmio_address_range))]
1380     #[serde(skip)] // TODO(b/255223604)
1381     #[merge(strategy = overwrite_option)]
1382     /// MMIO address ranges
1383     pub mmio_address_range: Option<Vec<AddressRange>>,
1384 
1385     #[argh(option, arg_name = "PATH")]
1386     #[serde(skip)] // TODO(b/255223604)
1387     #[merge(strategy = append)]
1388     /// path to a socket from where to read mouse input events and write status updates to
1389     pub mouse: Vec<PathBuf>,
1390 
1391     #[cfg(target_arch = "aarch64")]
1392     #[argh(switch)]
1393     #[serde(skip)] // TODO(b/255223604)
1394     #[merge(strategy = overwrite_false)]
1395     /// enable the Memory Tagging Extension in the guest
1396     pub mte: bool,
1397 
1398     #[argh(option, arg_name = "PATH:WIDTH:HEIGHT")]
1399     #[serde(skip)] // TODO(b/255223604)
1400     #[merge(strategy = append)]
1401     /// 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)
1402     pub multi_touch: Vec<TouchDeviceOption>,
1403 
1404     #[cfg(unix)]
1405     #[argh(
1406         option,
1407         arg_name = "(tap-name=TAP_NAME,mac=MAC_ADDRESS|tap-fd=TAP_FD,mac=MAC_ADDRESS|host-ip=IP,netmask=NETMASK,mac=MAC_ADDRESS),vhost-net=VHOST_NET,vq-pairs=N"
1408     )]
1409     #[serde(default)]
1410     #[merge(strategy = append)]
1411     /// comma separated key=value pairs for setting up a network
1412     /// device.
1413     /// Possible key values:
1414     ///   (
1415     ///      tap-name=STRING - name of a configured persistent TAP
1416     ///                          interface to use for networking.
1417     ///      mac=STRING      - MAC address for VM. [Optional]
1418     ///    OR
1419     ///      tap-fd=INT      - File descriptor for configured tap
1420     ///                          device.
1421     ///      mac=STRING      - MAC address for VM. [Optional]
1422     ///    OR
1423     ///      (
1424     ///         host-ip=STRING  - IP address to assign to host tap
1425     ///                             interface.
1426     ///       AND
1427     ///         netmask=STRING  - Netmask for VM subnet.
1428     ///       AND
1429     ///         mac=STRING      - MAC address for VM.
1430     ///      )
1431     ///   )
1432     /// AND
1433     ///   vhost-net=BOOL  - whether enable vhost_net or not.
1434     ///                       Default: false.  [Optional]
1435     ///   vq-pairs=N      - number of rx/tx queue pairs.
1436     ///                       Default: 1.      [Optional]
1437     ///
1438     /// Either one tap_name, one tap_fd or a triplet of host_ip,
1439     /// netmask and mac must be specified.
1440     pub net: Vec<NetParameters>,
1441 
1442     #[cfg(unix)]
1443     #[argh(option, arg_name = "N")]
1444     #[serde(skip)] // Deprecated - use `net` instead.
1445     #[merge(strategy = overwrite_option)]
1446     /// virtio net virtual queue pairs. (default: 1)
1447     pub net_vq_pairs: Option<u16>,
1448 
1449     #[cfg(unix)]
1450     #[argh(option, arg_name = "NETMASK")]
1451     #[serde(skip)] // Deprecated - use `net` instead.
1452     #[merge(strategy = overwrite_option)]
1453     /// netmask for VM subnet
1454     pub netmask: Option<net::Ipv4Addr>,
1455 
1456     #[argh(switch)]
1457     #[serde(skip)] // TODO(b/255223604)
1458     #[merge(strategy = overwrite_false)]
1459     /// don't use virtio-balloon device in the guest
1460     pub no_balloon: bool,
1461 
1462     #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
1463     #[argh(switch)]
1464     #[serde(skip)] // TODO(b/255223604)
1465     #[merge(strategy = overwrite_false)]
1466     /// don't use legacy KBD devices emulation
1467     pub no_i8042: bool,
1468 
1469     #[argh(switch)]
1470     #[serde(skip)] // TODO(b/255223604)
1471     #[merge(strategy = overwrite_false)]
1472     /// don't create RNG device in the guest
1473     pub no_rng: bool,
1474 
1475     #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
1476     #[argh(switch)]
1477     #[serde(skip)] // TODO(b/255223604)
1478     #[merge(strategy = overwrite_false)]
1479     /// don't use legacy RTC devices emulation
1480     pub no_rtc: bool,
1481 
1482     #[argh(switch)]
1483     #[serde(skip)] // TODO(b/255223604)
1484     #[merge(strategy = overwrite_false)]
1485     /// don't use SMT in the guest
1486     pub no_smt: bool,
1487 
1488     #[argh(switch)]
1489     #[serde(skip)] // TODO(b/255223604)
1490     #[merge(strategy = overwrite_false)]
1491     /// don't use usb devices in the guest
1492     pub no_usb: bool,
1493 
1494     #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
1495     #[argh(option, arg_name = "OEM_STRING")]
1496     #[serde(skip)] // TODO(b/255223604)
1497     #[merge(strategy = append)]
1498     /// SMBIOS OEM string values to add to the DMI tables
1499     pub oem_strings: Vec<String>,
1500 
1501     #[argh(option, short = 'p', arg_name = "PARAMS")]
1502     #[serde(default)]
1503     #[merge(strategy = append)]
1504     /// extra kernel or plugin command line arguments. Can be given more than once
1505     pub params: Vec<String>,
1506 
1507     #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
1508     #[argh(option, arg_name = "pci_low_mmio_start")]
1509     #[serde(skip)] // TODO(b/255223604)
1510     #[merge(strategy = overwrite_option)]
1511     /// the pci mmio start address below 4G
1512     pub pci_start: Option<u64>,
1513 
1514     #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
1515     #[argh(
1516         option,
1517         arg_name = "mmio_base,mmio_length",
1518         from_str_fn(parse_memory_region)
1519     )]
1520     #[serde(skip)] // TODO(b/255223604)
1521     #[merge(strategy = overwrite_option)]
1522     /// region for PCIe Enhanced Configuration Access Mechanism
1523     pub pcie_ecam: Option<AddressRange>,
1524 
1525     #[cfg(feature = "direct")]
1526     #[argh(
1527         option,
1528         arg_name = "PATH[,hp_gpe=NUM]",
1529         from_str_fn(parse_pcie_root_port_params)
1530     )]
1531     #[serde(skip)] // TODO(b/255223604)
1532     #[merge(strategy = overwrite)]
1533     /// path to sysfs of host pcie root port and host pcie root port hotplug gpe number
1534     pub pcie_root_port: Vec<HostPcieRootPortParameters>,
1535 
1536     #[argh(switch)]
1537     #[serde(skip)] // TODO(b/255223604)
1538     #[merge(strategy = overwrite_false)]
1539     /// enable per-VM core scheduling intead of the default one (per-vCPU core scheduing) by
1540     /// making all vCPU threads share same cookie for core scheduling.
1541     /// This option is no-op on devices that have neither MDS nor L1TF vulnerability
1542     pub per_vm_core_scheduling: bool,
1543 
1544     #[argh(
1545         option,
1546         arg_name = "path=PATH,[block_size=SIZE]",
1547         from_str_fn(parse_pflash_parameters)
1548     )]
1549     #[serde(skip)] // TODO(b/255223604)
1550     #[merge(strategy = overwrite_option)]
1551     /// comma-seperated key-value pair for setting up the pflash device, which provides space to store UEFI variables.
1552     /// block_size defaults to 4K.
1553     /// [--pflash <path=PATH,[block_size=SIZE]>]
1554     pub pflash: Option<PflashParameters>,
1555 
1556     #[argh(option, arg_name = "PATH")]
1557     #[serde(skip)] // TODO(b/255223604)
1558     #[merge(strategy = overwrite_option)]
1559     /// path to empty directory to use for sandbox pivot root
1560     pub pivot_root: Option<PathBuf>,
1561 
1562     #[cfg(feature = "plugin")]
1563     #[argh(option, arg_name = "PATH")]
1564     #[serde(skip)] // TODO(b/255223604)
1565     #[merge(strategy = overwrite_option)]
1566     /// absolute path to plugin process to run under crosvm
1567     pub plugin: Option<PathBuf>,
1568 
1569     #[cfg(feature = "plugin")]
1570     #[argh(option, arg_name = "GID:GID:INT")]
1571     #[serde(skip)] // TODO(b/255223604)
1572     #[merge(strategy = append)]
1573     /// supplemental GIDs that should be mapped in plugin jail.  Can be given more than once
1574     pub plugin_gid_map: Vec<GidMap>,
1575 
1576     #[cfg(feature = "plugin")]
1577     #[argh(option)]
1578     #[serde(skip)] // TODO(b/255223604)
1579     #[merge(strategy = overwrite_option)]
1580     /// path to the file listing supplemental GIDs that should be mapped in plugin jail.  Can be given more than once
1581     pub plugin_gid_map_file: Option<PathBuf>,
1582 
1583     #[cfg(feature = "plugin")]
1584     #[argh(option, arg_name = "PATH:PATH:BOOL")]
1585     #[serde(skip)] // TODO(b/255223604)
1586     #[merge(strategy = append)]
1587     /// path to be mounted into the plugin's root filesystem.  Can be given more than once
1588     pub plugin_mount: Vec<BindMount>,
1589 
1590     #[cfg(feature = "plugin")]
1591     #[argh(option, arg_name = "PATH")]
1592     #[serde(skip)] // TODO(b/255223604)
1593     #[merge(strategy = overwrite_option)]
1594     /// path to the file listing paths be mounted into the plugin's root filesystem.  Can be given more than once
1595     pub plugin_mount_file: Option<PathBuf>,
1596 
1597     #[cfg(feature = "plugin")]
1598     #[argh(option, arg_name = "PATH")]
1599     #[serde(skip)] // TODO(b/255223604)
1600     #[merge(strategy = overwrite_option)]
1601     /// absolute path to a directory that will become root filesystem for the plugin process.
1602     pub plugin_root: Option<PathBuf>,
1603 
1604     #[argh(option, arg_name = "PATH")]
1605     #[serde(skip)] // TODO(b/255223604)
1606     #[merge(strategy = append)]
1607     /// path to a disk image
1608     pub pmem_device: Vec<DiskOption>,
1609 
1610     #[argh(switch)]
1611     #[serde(skip)] // TODO(b/255223604)
1612     #[merge(strategy = overwrite_false)]
1613     /// grant this Guest VM certain privileges to manage Host resources, such as power management
1614     pub privileged_vm: bool,
1615 
1616     #[cfg(feature = "process-invariants")]
1617     #[argh(option, arg_name = "PATH")]
1618     #[serde(skip)] // TODO(b/255223604)
1619     #[merge(strategy = overwrite_option)]
1620     /// shared read-only memory address for a serialized EmulatorProcessInvariants proto
1621     pub process_invariants_handle: Option<u64>,
1622 
1623     #[cfg(feature = "process-invariants")]
1624     #[argh(option, arg_name = "PATH")]
1625     #[serde(skip)] // TODO(b/255223604)
1626     #[merge(strategy = overwrite_option)]
1627     /// size of the serialized EmulatorProcessInvariants proto pointed at by process-invariants-handle
1628     pub process_invariants_size: Option<usize>,
1629 
1630     #[cfg(windows)]
1631     #[argh(option)]
1632     #[serde(skip)] // TODO(b/255223604)
1633     #[merge(strategy = overwrite_option)]
1634     /// product channel
1635     pub product_channel: Option<String>,
1636 
1637     #[cfg(windows)]
1638     #[argh(option)]
1639     #[serde(skip)] // TODO(b/255223604)
1640     #[merge(strategy = overwrite_option)]
1641     /// the product name for file paths.
1642     pub product_name: Option<String>,
1643 
1644     #[cfg(windows)]
1645     #[argh(option)]
1646     #[serde(skip)] // TODO(b/255223604)
1647     #[merge(strategy = overwrite_option)]
1648     /// product version
1649     pub product_version: Option<String>,
1650 
1651     #[argh(switch)]
1652     #[serde(skip)] // TODO(b/255223604)
1653     #[merge(strategy = overwrite_false)]
1654     /// prevent host access to guest memory
1655     pub protected_vm: bool,
1656 
1657     #[argh(option, arg_name = "PATH")]
1658     #[serde(skip)] // TODO(b/255223604)
1659     #[merge(strategy = overwrite_option)]
1660     /// (EXPERIMENTAL/FOR DEBUGGING) Use custom VM firmware to run in protected mode
1661     pub protected_vm_with_firmware: Option<PathBuf>,
1662 
1663     #[argh(switch)]
1664     #[serde(skip)] // TODO(b/255223604)
1665     #[merge(strategy = overwrite_false)]
1666     /// (EXPERIMENTAL) prevent host access to guest memory, but don't use protected VM firmware
1667     protected_vm_without_firmware: bool,
1668 
1669     #[argh(option, arg_name = "path=PATH,size=SIZE")]
1670     #[serde(skip)] // TODO(b/255223604)
1671     #[merge(strategy = overwrite_option)]
1672     /// path to pstore buffer backend file followed by size
1673     ///     [--pstore <path=PATH,size=SIZE>]
1674     pub pstore: Option<Pstore>,
1675 
1676     #[cfg(windows)]
1677     #[argh(switch)]
1678     #[serde(skip)] // TODO(b/255223604)
1679     #[merge(strategy = overwrite_false)]
1680     /// enable virtio-pvclock.
1681     pub pvclock: bool,
1682 
1683     #[argh(option, long = "restore", arg_name = "PATH")]
1684     #[serde(skip)] // TODO(b/255223604)
1685     #[merge(strategy = overwrite_option)]
1686     /// path of the snapshot that is used to restore the VM on startup.
1687     pub restore: Option<PathBuf>,
1688 
1689     #[argh(option, arg_name = "PATH[,key=value[,key=value[,...]]]", short = 'r')]
1690     #[serde(skip)] // Deprecated - use `block` instead.
1691     #[merge(strategy = overwrite_option)]
1692     /// path to a disk image followed by optional comma-separated
1693     /// options.
1694     /// Valid keys:
1695     ///     sparse=BOOL - Indicates whether the disk should support
1696     ///         the discard operation (default: true)
1697     ///     block_size=BYTES - Set the reported block size of the
1698     ///        disk (default: 512)
1699     ///     id=STRING - Set the block device identifier to an ASCII
1700     ///     string, up to 20 characters (default: no ID)
1701     ///     o_direct=BOOL - Use O_DIRECT mode to bypass page cache
1702     root: Option<DiskOptionWithId>,
1703 
1704     #[argh(option, arg_name = "CPUSET")]
1705     #[serde(skip)] // TODO(b/255223604)
1706     #[merge(strategy = overwrite_option)]
1707     /// comma-separated list of CPUs or CPU ranges to run VCPUs on. (e.g. 0,1-3,5) (default: none)
1708     pub rt_cpus: Option<CpuSet>,
1709 
1710     #[argh(option, arg_name = "PATH")]
1711     #[serde(skip)] // TODO(b/255223604)
1712     #[merge(strategy = append)]
1713     /// path to a writable disk image
1714     rw_pmem_device: Vec<DiskOption>,
1715 
1716     #[argh(option, arg_name = "PATH[,key=value[,key=value[,...]]]")]
1717     #[serde(skip)] // Deprecated - use `block` instead.
1718     #[merge(strategy = append)]
1719     /// path to a read-write disk image followed by optional
1720     /// comma-separated options.
1721     /// Valid keys:
1722     ///     sparse=BOOL - Indicates whether the disk should support
1723     ///        the discard operation (default: true)
1724     ///     block_size=BYTES - Set the reported block size of the
1725     ///        disk (default: 512)
1726     ///     id=STRING - Set the block device identifier to an ASCII
1727     ///       string, up to 20 characters (default: no ID)
1728     ///     o_direct=BOOL - Use O_DIRECT mode to bypass page cache
1729     rwdisk: Vec<DiskOptionWithId>,
1730 
1731     #[argh(option, arg_name = "PATH[,key=value[,key=value[,...]]]")]
1732     #[serde(skip)] // Deprecated - use `block` instead.
1733     #[merge(strategy = overwrite_option)]
1734     /// path to a read-write root disk image followed by optional
1735     /// comma-separated options.
1736     /// Valid keys:
1737     ///     sparse=BOOL - Indicates whether the disk should support
1738     ///       the discard operation (default: true)
1739     ///     block_size=BYTES - Set the reported block size of the
1740     ///        disk (default: 512)
1741     ///     id=STRING - Set the block device identifier to an ASCII
1742     ///        string, up to 20 characters (default: no ID)
1743     ///     o_direct=BOOL - Use O_DIRECT mode to bypass page cache
1744     rwroot: Option<DiskOptionWithId>,
1745 
1746     #[argh(switch)]
1747     #[serde(skip)] // TODO(b/255223604)
1748     #[merge(strategy = overwrite_false)]
1749     /// set Low Power S0 Idle Capable Flag for guest Fixed ACPI
1750     /// Description Table, additionally use enhanced crosvm suspend and resume
1751     /// routines to perform full guest suspension/resumption
1752     pub s2idle: bool,
1753 
1754     #[cfg(unix)]
1755     #[argh(switch)]
1756     #[serde(skip)] // TODO(b/255223604)
1757     #[merge(strategy = overwrite_false)]
1758     /// instead of seccomp filter failures being fatal, they will be logged instead
1759     pub seccomp_log_failures: bool,
1760 
1761     #[cfg(unix)]
1762     #[argh(option, arg_name = "PATH")]
1763     #[serde(skip)] // TODO(b/255223604)
1764     #[merge(strategy = overwrite_option)]
1765     /// path to seccomp .policy files
1766     pub seccomp_policy_dir: Option<PathBuf>,
1767 
1768     #[argh(
1769         option,
1770         arg_name = "type=TYPE,[hardware=HW,num=NUM,path=PATH,input=PATH,console,earlycon,stdin]",
1771         from_str_fn(parse_serial_options)
1772     )]
1773     #[serde(default)]
1774     #[merge(strategy = append)]
1775     /// comma separated key=value pairs for setting up serial
1776     /// devices. Can be given more than once.
1777     /// Possible key values:
1778     ///     type=(stdout,syslog,sink,file) - Where to route the
1779     ///        serial device
1780     ///     hardware=(serial,virtio-console,debugcon) - Which type
1781     ///        of serial hardware to emulate. Defaults to 8250 UART
1782     ///        (serial).
1783     ///     num=(1,2,3,4) - Serial Device Number. If not provided,
1784     ///        num will default to 1.
1785     ///     debugcon_port=PORT - Port for the debugcon device to
1786     ///        listen to. Defaults to 0x402, which is what OVMF
1787     ///        expects.
1788     ///     path=PATH - The path to the file to write to when
1789     ///        type=file
1790     ///     input=PATH - The path to the file to read from when not
1791     ///        stdin
1792     ///     console - Use this serial device as the guest console.
1793     ///        Can only be given once. Will default to first
1794     ///        serial port if not provided.
1795     ///     earlycon - Use this serial device as the early console.
1796     ///        Can only be given once.
1797     ///     stdin - Direct standard input to this serial device.
1798     ///        Can only be given once. Will default to first serial
1799     ///        port if not provided.
1800     pub serial: Vec<SerialParameters>,
1801 
1802     #[cfg(windows)]
1803     #[argh(option, arg_name = "PIPE_NAME")]
1804     #[serde(skip)] // TODO(b/255223604)
1805     #[merge(strategy = overwrite_option)]
1806     /// the service ipc pipe name. (Prefix \\\\.\\pipe\\ not needed.
1807     pub service_pipe_name: Option<String>,
1808 
1809     #[cfg(unix)]
1810     #[argh(
1811         option,
1812         arg_name = "PATH:TAG[:type=TYPE:writeback=BOOL:timeout=SECONDS:uidmap=UIDMAP:gidmap=GIDMAP:cache=CACHE:dax=BOOL,posix_acl=BOOL]"
1813     )]
1814     // TODO(b/218223240) add Deserialize implementation for SharedDir so it can be supported by the
1815     // config file.
1816     #[serde(skip)]
1817     #[merge(strategy = append)]
1818     /// colon-separated options for configuring a directory to be
1819     /// shared with the VM. The first field is the directory to be
1820     /// shared and the second field is the tag that the VM can use
1821     /// to identify the device. The remaining fields are key=value
1822     /// pairs that may appear in any order.
1823     ///  Valid keys are:
1824     ///     type=(p9, fs) - Indicates whether the directory should
1825     ///        be shared via virtio-9p or virtio-fs (default: p9).
1826     ///     uidmap=UIDMAP - The uid map to use for the device's
1827     ///        jail in the format "inner outer
1828     ///        count[,inner outer count]"
1829     ///        (default: 0 <current euid> 1).
1830     ///     gidmap=GIDMAP - The gid map to use for the device's
1831     ///        jail in the format "inner outer
1832     ///        count[,inner outer count]"
1833     ///        (default: 0 <current egid> 1).
1834     ///     cache=(never, auto, always) - Indicates whether the VM
1835     ///        can cache the contents of the shared directory
1836     ///        (default: auto).  When set to "auto" and the type
1837     ///        is "fs", the VM will use close-to-open consistency
1838     ///        for file contents.
1839     ///     timeout=SECONDS - How long the VM should consider file
1840     ///        attributes and directory entries to be valid
1841     ///        (default: 5).  If the VM has exclusive access to the
1842     ///        directory, then this should be a large value.  If
1843     ///        the directory can be modified by other processes,
1844     ///        then this should be 0.
1845     ///     writeback=BOOL - Enables writeback caching
1846     ///        (default: false).  This is only safe to do when the
1847     ///        VM has exclusive access to the files in a directory.
1848     ///        Additionally, the server should have read
1849     ///        permission for all files as the VM may issue read
1850     ///        requests even for files that are opened write-only.
1851     ///     dax=BOOL - Enables DAX support.  Enabling DAX can
1852     ///        improve performance for frequently accessed files
1853     ///        by mapping regions of the file directly into the
1854     ///        VM's memory. There is a cost of slightly increased
1855     ///        latency the first time the file is accessed.  Since
1856     ///        the mapping is shared directly from the host kernel's
1857     ///        file cache, enabling DAX can improve performance even
1858     ///         when the guest cache policy is "Never".  The default
1859     ///         value for this option is "false".
1860     ///     posix_acl=BOOL - Indicates whether the shared directory
1861     ///        supports POSIX ACLs.  This should only be enabled
1862     ///        when the underlying file system supports POSIX ACLs.
1863     ///        The default value for this option is "true".
1864     pub shared_dir: Vec<SharedDir>,
1865 
1866     #[argh(option, arg_name = "PATH:WIDTH:HEIGHT")]
1867     #[serde(skip)] // TODO(b/255223604)
1868     #[merge(strategy = append)]
1869     /// 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)
1870     pub single_touch: Vec<TouchDeviceOption>,
1871 
1872     #[cfg(feature = "slirp-ring-capture")]
1873     #[argh(option, arg_name = "PATH")]
1874     #[serde(skip)] // TODO(b/255223604)
1875     #[merge(strategy = overwrite_option)]
1876     /// Redirects slirp network packets to the supplied log file rather than the current directory as `slirp_capture_packets.pcap`
1877     pub slirp_capture_file: Option<String>,
1878 
1879     #[argh(option, short = 's', arg_name = "PATH")]
1880     #[merge(strategy = overwrite_option)]
1881     /// path to put the control socket. If PATH is a directory, a name will be generated
1882     pub socket: Option<PathBuf>,
1883 
1884     #[cfg(feature = "tpm")]
1885     #[argh(switch)]
1886     #[serde(skip)] // TODO(b/255223604)
1887     #[merge(strategy = overwrite_false)]
1888     /// enable a software emulated trusted platform module device
1889     pub software_tpm: bool,
1890 
1891     #[cfg(feature = "audio")]
1892     #[argh(option, arg_name = "PATH")]
1893     #[serde(skip)] // TODO(b/255223604)
1894     #[merge(strategy = overwrite_option)]
1895     /// path to the VioS server socket for setting up virtio-snd devices
1896     pub sound: Option<PathBuf>,
1897 
1898     #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
1899     #[argh(switch)]
1900     #[serde(skip)] // TODO(b/255223604)
1901     #[merge(strategy = overwrite_false)]
1902     /// (EXPERIMENTAL) enable split-irqchip support
1903     pub split_irqchip: bool,
1904 
1905     #[argh(switch)]
1906     #[serde(skip)] // TODO(b/255223604)
1907     #[merge(strategy = overwrite_false)]
1908     /// don't allow guest to use pages from the balloon
1909     pub strict_balloon: bool,
1910 
1911     #[argh(
1912         option,
1913         arg_name = "DOMAIN:BUS:DEVICE.FUNCTION[,vendor=NUM][,device=NUM][,class=NUM][,subsystem_vendor=NUM][,subsystem_device=NUM][,revision=NUM]"
1914     )]
1915     #[serde(skip)] // TODO(b/255223604)
1916     #[merge(strategy = append)]
1917     /// comma-separated key=value pairs for setting up a stub PCI
1918     /// device that just enumerates. The first option in the list
1919     /// must specify a PCI address to claim.
1920     /// Optional further parameters
1921     ///     vendor=NUM - PCI vendor ID
1922     ///     device=NUM - PCI device ID
1923     ///     class=NUM - PCI class (including class code, subclass,
1924     ///        and programming interface)
1925     ///     subsystem_vendor=NUM - PCI subsystem vendor ID
1926     ///     subsystem_device=NUM - PCI subsystem device ID
1927     ///     revision=NUM - revision
1928     pub stub_pci_device: Vec<StubPciParameters>,
1929 
1930     #[argh(option, long = "swap", arg_name = "PATH")]
1931     #[serde(skip)] // TODO(b/255223604)
1932     #[merge(strategy = overwrite_option)]
1933     /// enable vmm-swap via an unnamed temporary file on the filesystem which contains the specified
1934     /// directory.
1935     pub swap_dir: Option<PathBuf>,
1936 
1937     #[argh(option, arg_name = "N")]
1938     #[serde(skip)] // TODO(b/255223604)
1939     #[merge(strategy = overwrite_option)]
1940     /// (EXPERIMENTAL) Size of virtio swiotlb buffer in MiB (default: 64 if `--protected-vm` or `--protected-vm-without-firmware` is present)
1941     pub swiotlb: Option<u64>,
1942 
1943     #[argh(option, arg_name = "PATH")]
1944     #[serde(skip)] // TODO(b/255223604)
1945     #[merge(strategy = append)]
1946     /// path to a socket from where to read switch input events and write status updates to
1947     pub switches: Vec<PathBuf>,
1948 
1949     #[argh(option, arg_name = "TAG")]
1950     #[serde(skip)] // TODO(b/255223604)
1951     #[merge(strategy = overwrite_option)]
1952     /// when logging to syslog, use the provided tag
1953     pub syslog_tag: Option<String>,
1954 
1955     #[cfg(unix)]
1956     #[argh(option)]
1957     #[serde(skip)] // Deprecated - use `net` instead.
1958     #[merge(strategy = append)]
1959     /// file descriptor for configured tap device. A different virtual network card will be added each time this argument is given
1960     pub tap_fd: Vec<RawDescriptor>,
1961 
1962     #[cfg(unix)]
1963     #[argh(option)]
1964     #[serde(skip)] // Deprecated - use `net` instead.
1965     #[merge(strategy = append)]
1966     /// 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
1967     pub tap_name: Vec<String>,
1968 
1969     #[cfg(target_os = "android")]
1970     #[argh(option, arg_name = "NAME[,...]")]
1971     #[serde(skip)] // TODO(b/255223604)
1972     #[merge(strategy = append)]
1973     /// comma-separated names of the task profiles to apply to all threads in crosvm including the vCPU threads
1974     pub task_profiles: Vec<String>,
1975 
1976     #[argh(option, arg_name = "PATH:WIDTH:HEIGHT")]
1977     #[serde(skip)] // TODO(b/255223604)
1978     #[merge(strategy = append)]
1979     /// 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)
1980     pub trackpad: Vec<TouchDeviceOption>,
1981 
1982     #[cfg(unix)]
1983     #[argh(switch)]
1984     #[serde(skip)] // TODO(b/255223604)
1985     #[merge(strategy = overwrite_false)]
1986     /// set MADV_DONTFORK on guest memory
1987     ///
1988     /// Intended for use in combination with --protected-vm, where the guest memory can be
1989     /// dangerous to access. Some systems, e.g. Android, have tools that fork processes and examine
1990     /// their memory. This flag effectively hides the guest memory from those tools.
1991     ///
1992     /// Not compatible with sandboxing or vvu devices.
1993     pub unmap_guest_memory_on_fork: bool,
1994 
1995     // Must be `Some` iff `protection_type == ProtectionType::UnprotectedWithFirmware`.
1996     #[argh(option, arg_name = "PATH")]
1997     #[serde(skip)] // TODO(b/255223604)
1998     #[merge(strategy = overwrite_option)]
1999     /// (EXPERIMENTAL/FOR DEBUGGING) Use VM firmware, but allow host access to guest memory
2000     pub unprotected_vm_with_firmware: Option<PathBuf>,
2001 
2002     #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
2003     #[argh(
2004         option,
2005         arg_name = "INDEX,type=TYPE,action=ACTION,[from=FROM],[filter=FILTER]",
2006         from_str_fn(parse_userspace_msr_options)
2007     )]
2008     #[serde(skip)] // TODO(b/255223604)
2009     #[merge(strategy = append)]
2010     /// userspace MSR handling. Takes INDEX of the MSR and how they
2011     ///  are handled.
2012     ///     type=(r|w|rw|wr) - read/write permission control.
2013     ///     action=(pass|emu) - if the control of msr is effective
2014     ///        on host.
2015     ///     from=(cpu0) - source of msr value. if not set, the
2016     ///        source is running CPU.
2017     ///     filter=(yes|no) - if the msr is filtered in KVM.
2018     pub userspace_msr: Vec<(u32, MsrConfig)>,
2019 
2020     #[argh(option, arg_name = "PATH")]
2021     #[serde(skip)] // TODO(b/255223604)
2022     #[merge(strategy = overwrite_option)]
2023     /// move all vCPU threads to this CGroup (default: nothing moves)
2024     pub vcpu_cgroup_path: Option<PathBuf>,
2025 
2026     #[cfg(unix)]
2027     #[argh(
2028         option,
2029         arg_name = "PATH[,guest-address=<BUS:DEVICE.FUNCTION>][,iommu=viommu|coiommu|off]"
2030     )]
2031     #[serde(default)]
2032     #[merge(strategy = append)]
2033     /// path to sysfs of VFIO device.
2034     ///     guest-address=<BUS:DEVICE.FUNCTION> - PCI address
2035     ///        that the device will be assigned in the guest.
2036     ///        If not specified, the device will be assigned an
2037     ///        address that mirrors its address in the host.
2038     ///        Only valid for PCI devices.
2039     ///     iommu=viommu|coiommu|off - indicates which type of IOMMU
2040     ///        to use for this device.
2041     pub vfio: Vec<VfioOption>,
2042 
2043     #[cfg(unix)]
2044     #[argh(switch)]
2045     #[serde(skip)] // TODO(b/255223604)
2046     #[merge(strategy = overwrite_false)]
2047     /// isolate all hotplugged passthrough vfio device behind virtio-iommu
2048     pub vfio_isolate_hotplug: bool,
2049 
2050     #[cfg(unix)]
2051     #[argh(option, arg_name = "PATH")]
2052     #[serde(skip)] // Deprecated - use `vfio` instead.
2053     #[merge(strategy = append)]
2054     /// path to sysfs of platform pass through
2055     pub vfio_platform: Vec<VfioOption>,
2056 
2057     #[cfg(unix)]
2058     #[argh(switch)]
2059     #[serde(skip)] // Deprecated - use `net` instead.
2060     #[merge(strategy = overwrite_false)]
2061     /// use vhost for networking
2062     pub vhost_net: bool,
2063 
2064     #[cfg(unix)]
2065     #[argh(option, arg_name = "PATH")]
2066     #[serde(skip)] // TODO(b/255223604)
2067     #[merge(strategy = overwrite_option)]
2068     /// path to the vhost-net device. (default /dev/vhost-net)
2069     pub vhost_net_device: Option<PathBuf>,
2070 
2071     #[argh(option, arg_name = "SOCKET_PATH")]
2072     #[serde(skip)] // TODO(b/255223604)
2073     #[merge(strategy = append)]
2074     /// path to a socket for vhost-user block
2075     pub vhost_user_blk: Vec<VhostUserOption>,
2076 
2077     #[argh(option, arg_name = "SOCKET_PATH")]
2078     #[serde(skip)] // TODO(b/255223604)
2079     #[merge(strategy = append)]
2080     /// path to a socket for vhost-user console
2081     pub vhost_user_console: Vec<VhostUserOption>,
2082 
2083     #[argh(option, arg_name = "SOCKET_PATH:TAG")]
2084     #[serde(skip)] // TODO(b/255223604)
2085     #[merge(strategy = append)]
2086     /// path to a socket path for vhost-user fs, and tag for the shared dir
2087     pub vhost_user_fs: Vec<VhostUserFsOption>,
2088 
2089     #[argh(option, arg_name = "SOCKET_PATH")]
2090     #[serde(skip)] // TODO(b/255223604)
2091     #[merge(strategy = append)]
2092     /// paths to a vhost-user socket for gpu
2093     pub vhost_user_gpu: Vec<VhostUserOption>,
2094 
2095     #[argh(option, arg_name = "SOCKET_PATH")]
2096     #[serde(skip)] // TODO(b/255223604)
2097     #[merge(strategy = overwrite_option)]
2098     /// path to a socket for vhost-user mac80211_hwsim
2099     pub vhost_user_mac80211_hwsim: Option<VhostUserOption>,
2100 
2101     #[argh(option, arg_name = "SOCKET_PATH")]
2102     #[serde(skip)] // TODO(b/255223604)
2103     #[merge(strategy = append)]
2104     /// path to a socket for vhost-user net
2105     pub vhost_user_net: Vec<VhostUserOption>,
2106 
2107     #[argh(option, arg_name = "SOCKET_PATH")]
2108     #[serde(skip)] // TODO(b/255223604)
2109     #[merge(strategy = append)]
2110     /// path to a socket for vhost-user snd
2111     pub vhost_user_snd: Vec<VhostUserOption>,
2112 
2113     #[argh(option, arg_name = "SOCKET_PATH")]
2114     #[serde(default)]
2115     #[merge(strategy = append)]
2116     /// path to a socket for vhost-user video decoder
2117     pub vhost_user_video_decoder: Vec<VhostUserOption>,
2118 
2119     #[argh(option, arg_name = "SOCKET_PATH")]
2120     #[serde(skip)] // TODO(b/255223604)
2121     #[merge(strategy = append)]
2122     /// path to a socket for vhost-user vsock
2123     pub vhost_user_vsock: Vec<VhostUserOption>,
2124 
2125     #[argh(option, arg_name = "SOCKET_PATH")]
2126     #[serde(skip)] // TODO(b/255223604)
2127     #[merge(strategy = overwrite_option)]
2128     /// path to a vhost-user socket for wayland
2129     pub vhost_user_wl: Option<VhostUserOption>,
2130 
2131     #[cfg(unix)]
2132     #[argh(option, arg_name = "SOCKET_PATH")]
2133     #[serde(skip)] // Deprecated - use `vsock` instead.
2134     #[merge(strategy = overwrite_option)]
2135     /// path to the vhost-vsock device. (default /dev/vhost-vsock)
2136     pub vhost_vsock_device: Option<PathBuf>,
2137 
2138     #[cfg(unix)]
2139     #[argh(option, arg_name = "FD")]
2140     #[serde(skip)] // Deprecated - use `vsock` instead.
2141     #[merge(strategy = overwrite_option)]
2142     /// open FD to the vhost-vsock device, mutually exclusive with vhost-vsock-device
2143     pub vhost_vsock_fd: Option<RawDescriptor>,
2144 
2145     #[cfg(feature = "video-decoder")]
2146     #[argh(option, arg_name = "[backend]")]
2147     #[serde(default)]
2148     #[merge(strategy = append)]
2149     /// (EXPERIMENTAL) enable virtio-video decoder device
2150     /// Possible backend values: libvda, ffmpeg, vaapi
2151     pub video_decoder: Vec<VideoDeviceConfig>,
2152 
2153     #[cfg(feature = "video-encoder")]
2154     #[argh(option, arg_name = "[backend]")]
2155     #[serde(default)]
2156     #[merge(strategy = append)]
2157     /// (EXPERIMENTAL) enable virtio-video encoder device
2158     /// Possible backend values: libvda
2159     pub video_encoder: Vec<VideoDeviceConfig>,
2160 
2161     #[cfg(feature = "audio")]
2162     #[argh(
2163         option,
2164         arg_name = "[capture=true,backend=BACKEND,num_output_devices=1,
2165         num_input_devices=1,num_output_streams=1,num_input_streams=1]"
2166     )]
2167     #[serde(skip)] // TODO(b/255223604)
2168     #[merge(strategy = append)]
2169     /// comma separated key=value pairs for setting up virtio snd
2170     /// devices.
2171     /// Possible key values:
2172     ///     capture=(false,true) - Disable/enable audio capture.
2173     ///         Default is false.
2174     ///     backend=(null,file,[cras]) - Which backend to use for
2175     ///         virtio-snd.
2176     ///     client_type=(crosvm,arcvm,borealis) - Set specific
2177     ///         client type for cras backend. Default is crosvm.
2178     ///     socket_type=(legacy,unified) Set specific socket type
2179     ///         for cras backend. Default is unified.
2180     ///     playback_path=STR - Set directory of output streams
2181     ///         for file backend.
2182     ///     playback_size=INT - Set size of the output streams
2183     ///         from file backend.
2184     ///     num_output_devices=INT - Set number of output PCM
2185     ///         devices.
2186     ///     num_input_devices=INT - Set number of input PCM devices.
2187     ///     num_output_streams=INT - Set number of output PCM
2188     ///         streams per device.
2189     ///     num_input_streams=INT - Set number of input PCM streams
2190     ///         per device.
2191     pub virtio_snd: Vec<SndParameters>,
2192 
2193     #[argh(option, arg_name = "cid=CID[,device=VHOST_DEVICE]")]
2194     #[serde(default)]
2195     #[merge(strategy = overwrite_option)]
2196     /// add a vsock device. Since a guest can only have one CID,
2197     /// this option can only be specified once.
2198     ///     cid=CID - CID to use for the device.
2199     ///     device=VHOST_DEVICE - path to the vhost-vsock device to
2200     ///         use (Linux only). Defaults to /dev/vhost-vsock.
2201     pub vsock: Option<VsockConfig>,
2202 
2203     #[cfg(all(feature = "vtpm", target_arch = "x86_64"))]
2204     #[argh(switch)]
2205     #[serde(skip)] // TODO(b/255223604)
2206     #[merge(strategy = overwrite_false)]
2207     /// enable the virtio-tpm connection to vtpm daemon
2208     pub vtpm_proxy: bool,
2209 
2210     #[argh(
2211         option,
2212         arg_name = "SOCKET_PATH[,addr=DOMAIN:BUS:DEVICE.FUNCTION,uuid=UUID]"
2213     )]
2214     #[serde(skip)] // TODO(b/255223604)
2215     #[merge(strategy = append)]
2216     /// socket path for the Virtio Vhost User proxy device.
2217     /// Parameters
2218     ///     addr=BUS:DEVICE.FUNCTION - PCI address that the proxy
2219     ///        device will be allocated
2220     ///        (default: automatically allocated)
2221     ///     uuid=UUID - UUID which will be stored in VVU PCI config
2222     ///        space that is readable from guest userspace
2223     pub vvu_proxy: Vec<VvuOption>,
2224 
2225     #[cfg(unix)]
2226     #[argh(option, arg_name = "PATH[,name=NAME]", from_str_fn(parse_wayland_sock))]
2227     #[serde(skip)] // TODO(b/255223604)
2228     #[merge(strategy = append)]
2229     /// path to the Wayland socket to use. The unnamed one is used for displaying virtual screens. Named ones are only for IPC
2230     pub wayland_sock: Vec<(String, PathBuf)>,
2231 
2232     #[cfg(unix)]
2233     #[argh(option, arg_name = "DISPLAY")]
2234     #[serde(skip)] // TODO(b/255223604)
2235     #[merge(strategy = overwrite_option)]
2236     /// X11 display name to use
2237     pub x_display: Option<String>,
2238 }
2239 
2240 #[cfg(feature = "config-file")]
2241 impl RunCommand {
2242     /// Merge the content of `self` into `self.cfg` if it exists, and return the merged
2243     /// configuration which `self.cfg` is empty.
squash(mut self) -> Self2244     pub fn squash(mut self) -> Self {
2245         use merge::Merge;
2246 
2247         self.cfg
2248             .take()
2249             .map(|b| *b)
2250             .into_iter()
2251             .chain(std::iter::once(self))
2252             .reduce(|mut acc: Self, cfg| {
2253                 acc.merge(cfg);
2254                 acc
2255             })
2256             .unwrap()
2257     }
2258 }
2259 
2260 impl TryFrom<RunCommand> for super::config::Config {
2261     type Error = String;
2262 
try_from(cmd: RunCommand) -> Result<Self, Self::Error>2263     fn try_from(cmd: RunCommand) -> Result<Self, Self::Error> {
2264         // Squash the configuration file (if any) and command-line arguments together.
2265         #[cfg(feature = "config-file")]
2266         let cmd = {
2267             if cmd.cfg.is_some() {
2268                 log::warn!(
2269                     "`--cfg` is still experimental and the configuration file format may change"
2270                 );
2271             }
2272             cmd.squash()
2273         };
2274 
2275         let mut cfg = Self::default();
2276         // TODO: we need to factor out some(?) of the checks into config::validate_config
2277 
2278         // Process arguments
2279         if let Some(p) = cmd.kernel {
2280             cfg.executable_path = Some(Executable::Kernel(p));
2281         }
2282 
2283         #[cfg(unix)]
2284         if let Some(p) = cmd.kvm_device {
2285             log::warn!(
2286                 "`--kvm-device <PATH>` is deprecated; use `--hypervisor kvm[device=<PATH>]` instead"
2287             );
2288 
2289             if cmd.hypervisor.is_some() {
2290                 return Err("cannot specify both --hypervisor and --kvm-device".to_string());
2291             }
2292 
2293             cfg.hypervisor = Some(crate::crosvm::config::HypervisorKind::Kvm { device: Some(p) });
2294         }
2295 
2296         #[cfg(unix)]
2297         if let Some(p) = cmd.vhost_net_device {
2298             if !p.exists() {
2299                 return Err(format!("vhost-net-device path {:?} does not exist", p));
2300             }
2301             cfg.vhost_net_device_path = p;
2302         }
2303 
2304         cfg.android_fstab = cmd.android_fstab;
2305 
2306         cfg.async_executor = cmd.async_executor;
2307 
2308         #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), unix))]
2309         if let Some(p) = cmd.bus_lock_ratelimit {
2310             cfg.bus_lock_ratelimit = p;
2311         }
2312 
2313         cfg.params.extend(cmd.params);
2314 
2315         cfg.core_scheduling = cmd.core_scheduling;
2316         cfg.per_vm_core_scheduling = cmd.per_vm_core_scheduling;
2317 
2318         // `--cpu` parameters.
2319         {
2320             let cpus = cmd.cpus.unwrap_or_default();
2321             cfg.vcpu_count = cpus.num_cores;
2322 
2323             // Only allow deprecated `--cpu-cluster` option only if `--cpu clusters=[...]` is not
2324             // used.
2325             cfg.cpu_clusters = match (&cpus.clusters.is_empty(), &cmd.cpu_cluster.is_empty()) {
2326                 (_, true) => cpus.clusters,
2327                 (true, false) => cmd.cpu_cluster,
2328                 (false, false) => {
2329                     return Err(
2330                         "cannot specify both --cpu clusters=[...] and --cpu_cluster".to_string()
2331                     )
2332                 }
2333             };
2334 
2335             #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
2336             if let Some(cpu_types) = cpus.core_types {
2337                 for cpu in cpu_types.atom {
2338                     if cfg
2339                         .vcpu_hybrid_type
2340                         .insert(cpu, CpuHybridType::Atom)
2341                         .is_some()
2342                     {
2343                         return Err(format!("vCPU index must be unique {}", cpu));
2344                     }
2345                 }
2346                 for cpu in cpu_types.core {
2347                     if cfg
2348                         .vcpu_hybrid_type
2349                         .insert(cpu, CpuHybridType::Core)
2350                         .is_some()
2351                     {
2352                         return Err(format!("vCPU index must be unique {}", cpu));
2353                     }
2354                 }
2355             }
2356         }
2357 
2358         cfg.vcpu_affinity = cmd.cpu_affinity;
2359 
2360         if let Some(capacity) = cmd.cpu_capacity {
2361             cfg.cpu_capacity = capacity;
2362         }
2363 
2364         cfg.vcpu_cgroup_path = cmd.vcpu_cgroup_path;
2365 
2366         cfg.no_smt = cmd.no_smt;
2367 
2368         if let Some(rt_cpus) = cmd.rt_cpus {
2369             cfg.rt_cpus = rt_cpus;
2370         }
2371 
2372         cfg.delay_rt = cmd.delay_rt;
2373 
2374         let mem = cmd.mem.unwrap_or_default();
2375         cfg.memory = mem.size;
2376 
2377         #[cfg(target_arch = "aarch64")]
2378         {
2379             if cmd.mte
2380                 && !(cmd.pmem_device.is_empty()
2381                     && cmd.pstore.is_none()
2382                     && cmd.rw_pmem_device.is_empty())
2383             {
2384                 return Err(
2385                     "--mte cannot be specified together with --pmem-device, --pstore or --rw-pmem-device"
2386                         .to_string(),
2387                 );
2388             }
2389             cfg.mte = cmd.mte;
2390             cfg.swiotlb = cmd.swiotlb;
2391         }
2392 
2393         cfg.hugepages = cmd.hugepages;
2394 
2395         // `cfg.hypervisor` may have been set by the deprecated `--kvm-device` option above.
2396         // TODO(b/274817652): remove this workaround when `--kvm-device` is removed.
2397         if cfg.hypervisor.is_none() {
2398             cfg.hypervisor = cmd.hypervisor;
2399         }
2400 
2401         #[cfg(unix)]
2402         {
2403             cfg.lock_guest_memory = cmd.lock_guest_memory;
2404         }
2405 
2406         #[cfg(feature = "audio")]
2407         {
2408             cfg.ac97_parameters = cmd.ac97;
2409             cfg.sound = cmd.sound;
2410         }
2411         cfg.vhost_user_snd = cmd.vhost_user_snd;
2412 
2413         for serial_params in cmd.serial {
2414             super::sys::config::check_serial_params(&serial_params)?;
2415 
2416             let num = serial_params.num;
2417             let key = (serial_params.hardware, num);
2418 
2419             if cfg.serial_parameters.contains_key(&key) {
2420                 return Err(format!(
2421                     "serial hardware {} num {}",
2422                     serial_params.hardware, num,
2423                 ));
2424             }
2425 
2426             if serial_params.console {
2427                 for params in cfg.serial_parameters.values() {
2428                     if params.console {
2429                         return Err(format!(
2430                             "{} device {} already set as console",
2431                             params.hardware, params.num,
2432                         ));
2433                     }
2434                 }
2435             }
2436 
2437             if serial_params.earlycon {
2438                 // Only SerialHardware::Serial supports earlycon= currently.
2439                 match serial_params.hardware {
2440                     SerialHardware::Serial => {}
2441                     _ => {
2442                         return Err(super::config::invalid_value_err(
2443                             serial_params.hardware.to_string(),
2444                             String::from("earlycon not supported for hardware"),
2445                         ));
2446                     }
2447                 }
2448                 for params in cfg.serial_parameters.values() {
2449                     if params.earlycon {
2450                         return Err(format!(
2451                             "{} device {} already set as earlycon",
2452                             params.hardware, params.num,
2453                         ));
2454                     }
2455                 }
2456             }
2457 
2458             if serial_params.stdin {
2459                 if let Some(previous_stdin) = cfg.serial_parameters.values().find(|sp| sp.stdin) {
2460                     return Err(format!(
2461                         "{} device {} already connected to standard input",
2462                         previous_stdin.hardware, previous_stdin.num,
2463                     ));
2464                 }
2465             }
2466 
2467             cfg.serial_parameters.insert(key, serial_params);
2468         }
2469 
2470         // Aggregate all the disks with the expected read-only and root values according to the
2471         // option they have been passed with.
2472         let mut disks = cmd
2473             .root
2474             .into_iter()
2475             .map(|mut d| {
2476                 d.disk_option.read_only = true;
2477                 d.disk_option.root = true;
2478                 d
2479             })
2480             .chain(cmd.rwroot.into_iter().map(|mut d| {
2481                 d.disk_option.read_only = false;
2482                 d.disk_option.root = true;
2483                 d
2484             }))
2485             .chain(cmd.disk.into_iter().map(|mut d| {
2486                 d.disk_option.read_only = true;
2487                 d.disk_option.root = false;
2488                 d
2489             }))
2490             .chain(cmd.rwdisk.into_iter().map(|mut d| {
2491                 d.disk_option.read_only = false;
2492                 d.disk_option.root = false;
2493                 d
2494             }))
2495             .chain(cmd.block.into_iter())
2496             .collect::<Vec<_>>();
2497 
2498         // Sort all our disks by index.
2499         disks.sort_by_key(|d| d.index);
2500 
2501         // Check that we don't have more than one root disk.
2502         if disks.iter().filter(|d| d.disk_option.root).count() > 1 {
2503             return Err("only one root disk can be specified".to_string());
2504         }
2505 
2506         // If we have a root disk, add the corresponding command-line parameters.
2507         if let Some(d) = disks.iter().find(|d| d.disk_option.root) {
2508             if d.index >= 26 {
2509                 return Err("ran out of letters for to assign to root disk".to_string());
2510             }
2511             cfg.params.push(format!(
2512                 "root=/dev/vd{} {}",
2513                 char::from(b'a' + d.index as u8),
2514                 if d.disk_option.read_only { "ro" } else { "rw" }
2515             ));
2516         }
2517 
2518         // Pass the sorted disks to the VM config.
2519         cfg.disks = disks.into_iter().map(|d| d.disk_option).collect();
2520 
2521         for (mut pmem, read_only) in cmd
2522             .pmem_device
2523             .into_iter()
2524             .map(|p| (p, true))
2525             .chain(cmd.rw_pmem_device.into_iter().map(|p| (p, false)))
2526         {
2527             pmem.read_only = read_only;
2528             cfg.pmem_devices.push(pmem);
2529         }
2530 
2531         #[cfg(windows)]
2532         {
2533             #[cfg(feature = "crash-report")]
2534             {
2535                 cfg.crash_pipe_name = cmd.crash_pipe_name;
2536             }
2537             cfg.product_name = cmd.product_name;
2538             cfg.exit_stats = cmd.exit_stats;
2539             cfg.host_guid = cmd.host_guid;
2540             cfg.irq_chip = cmd.irqchip;
2541             cfg.kernel_log_file = cmd.kernel_log_file;
2542             cfg.log_file = cmd.log_file;
2543             cfg.logs_directory = cmd.logs_directory;
2544             #[cfg(feature = "process-invariants")]
2545             {
2546                 cfg.process_invariants_data_handle = cmd.process_invariants_handle;
2547 
2548                 cfg.process_invariants_data_size = cmd.process_invariants_size;
2549             }
2550             cfg.pvclock = cmd.pvclock;
2551             #[cfg(windows)]
2552             {
2553                 cfg.service_pipe_name = cmd.service_pipe_name;
2554             }
2555             #[cfg(feature = "slirp-ring-capture")]
2556             {
2557                 cfg.slirp_capture_file = cmd.slirp_capture_file;
2558             }
2559             cfg.syslog_tag = cmd.syslog_tag;
2560             cfg.product_channel = cmd.product_channel;
2561             cfg.product_version = cmd.product_version;
2562         }
2563         cfg.pstore = cmd.pstore;
2564 
2565         #[cfg(unix)]
2566         for (name, params) in cmd.wayland_sock {
2567             if cfg.wayland_socket_paths.contains_key(&name) {
2568                 return Err(format!("wayland socket name already used: '{}'", name));
2569             }
2570             cfg.wayland_socket_paths.insert(name, params);
2571         }
2572 
2573         #[cfg(unix)]
2574         {
2575             cfg.x_display = cmd.x_display;
2576         }
2577 
2578         cfg.display_window_keyboard = cmd.display_window_keyboard;
2579         cfg.display_window_mouse = cmd.display_window_mouse;
2580 
2581         cfg.swap_dir = cmd.swap_dir;
2582         cfg.restore_path = cmd.restore;
2583 
2584         if let Some(mut socket_path) = cmd.socket {
2585             if socket_path.is_dir() {
2586                 socket_path.push(format!("crosvm-{}.sock", getpid()));
2587             }
2588             cfg.socket_path = Some(socket_path);
2589         }
2590 
2591         cfg.balloon_control = cmd.balloon_control;
2592 
2593         cfg.vsock = cmd.vsock;
2594 
2595         // Legacy vsock options.
2596         if let Some(cid) = cmd.cid {
2597             if cfg.vsock.is_some() {
2598                 return Err(
2599                     "`cid` and `vsock` cannot be specified together. Use `vsock` only.".to_string(),
2600                 );
2601             }
2602 
2603             let legacy_vsock_config = VsockConfig::new(
2604                 cid,
2605                 #[cfg(unix)]
2606                 match (cmd.vhost_vsock_device, cmd.vhost_vsock_fd) {
2607                     (Some(_), Some(_)) => {
2608                         return Err(
2609                             "Only one of vhost-vsock-device vhost-vsock-fd has to be specified"
2610                                 .to_string(),
2611                         )
2612                     }
2613                     (Some(path), None) => Some(path),
2614                     (None, Some(fd)) => Some(PathBuf::from(format!("/proc/self/fd/{}", fd))),
2615                     (None, None) => None,
2616                 },
2617             );
2618 
2619             cfg.vsock = Some(legacy_vsock_config);
2620         }
2621 
2622         #[cfg(feature = "plugin")]
2623         {
2624             use std::fs::File;
2625             use std::io::BufRead;
2626             use std::io::BufReader;
2627 
2628             if let Some(p) = cmd.plugin {
2629                 if cfg.executable_path.is_some() {
2630                     return Err(format!(
2631                         "A VM executable was already specified: {:?}",
2632                         cfg.executable_path
2633                     ));
2634                 }
2635                 cfg.executable_path = Some(Executable::Plugin(p));
2636             }
2637             cfg.plugin_root = cmd.plugin_root;
2638             cfg.plugin_mounts = cmd.plugin_mount;
2639 
2640             if let Some(path) = cmd.plugin_mount_file {
2641                 let file = File::open(path)
2642                     .map_err(|_| String::from("unable to open `plugin-mount-file` file"))?;
2643                 let reader = BufReader::new(file);
2644                 for l in reader.lines() {
2645                     let line = l.unwrap();
2646                     let trimmed_line = line.split_once('#').map_or(&*line, |x| x.0).trim();
2647                     if !trimmed_line.is_empty() {
2648                         let mount = parse_plugin_mount_option(trimmed_line)?;
2649                         cfg.plugin_mounts.push(mount);
2650                     }
2651                 }
2652             }
2653 
2654             cfg.plugin_gid_maps = cmd.plugin_gid_map;
2655 
2656             if let Some(path) = cmd.plugin_gid_map_file {
2657                 let file = File::open(path)
2658                     .map_err(|_| String::from("unable to open `plugin-gid-map-file` file"))?;
2659                 let reader = BufReader::new(file);
2660                 for l in reader.lines() {
2661                     let line = l.unwrap();
2662                     let trimmed_line = line.split_once('#').map_or(&*line, |x| x.0).trim();
2663                     if !trimmed_line.is_empty() {
2664                         let map = trimmed_line.parse()?;
2665                         cfg.plugin_gid_maps.push(map);
2666                     }
2667                 }
2668             }
2669         }
2670 
2671         #[cfg(feature = "tpm")]
2672         {
2673             cfg.software_tpm = cmd.software_tpm;
2674         }
2675 
2676         #[cfg(all(feature = "vtpm", target_arch = "x86_64"))]
2677         {
2678             cfg.vtpm_proxy = cmd.vtpm_proxy;
2679         }
2680 
2681         cfg.virtio_single_touch = cmd.single_touch;
2682         cfg.virtio_multi_touch = cmd.multi_touch;
2683         cfg.virtio_trackpad = cmd.trackpad;
2684         cfg.virtio_mice = cmd.mouse;
2685         cfg.virtio_keyboard = cmd.keyboard;
2686         cfg.virtio_switches = cmd.switches;
2687         cfg.virtio_input_evdevs = cmd.evdev;
2688 
2689         #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
2690         {
2691             cfg.split_irqchip = cmd.split_irqchip;
2692         }
2693 
2694         cfg.initrd_path = cmd.initrd;
2695 
2696         if let Some(p) = cmd.bios {
2697             if cfg.executable_path.is_some() {
2698                 return Err(format!(
2699                     "A VM executable was already specified: {:?}",
2700                     cfg.executable_path
2701                 ));
2702             }
2703             cfg.executable_path = Some(Executable::Bios(p));
2704         }
2705         cfg.pflash_parameters = cmd.pflash;
2706 
2707         #[cfg(feature = "video-decoder")]
2708         {
2709             cfg.video_dec = cmd.video_decoder;
2710         }
2711         #[cfg(feature = "video-encoder")]
2712         {
2713             cfg.video_enc = cmd.video_encoder;
2714         }
2715 
2716         cfg.acpi_tables = cmd.acpi_table;
2717 
2718         cfg.usb = !cmd.no_usb;
2719         cfg.rng = !cmd.no_rng;
2720         cfg.balloon = !cmd.no_balloon;
2721         cfg.balloon_page_reporting = cmd.balloon_page_reporting;
2722         cfg.balloon_wss_reporting = cmd.balloon_wss_reporting;
2723         #[cfg(feature = "audio")]
2724         {
2725             cfg.virtio_snds = cmd.virtio_snd;
2726         }
2727 
2728         #[cfg(feature = "gpu")]
2729         {
2730             // Due to the resource bridge, we can only create a single GPU device at the moment.
2731             if cmd.gpu.len() > 1 {
2732                 return Err("at most one GPU device can currently be created".to_string());
2733             }
2734             cfg.gpu_parameters = cmd.gpu.into_iter().map(|p| p.0).take(1).next();
2735             if !cmd.gpu_display.is_empty() {
2736                 cfg.gpu_parameters
2737                     .get_or_insert_with(Default::default)
2738                     .display_params
2739                     .extend(cmd.gpu_display.into_iter().map(|p| p.0));
2740             }
2741 
2742             #[cfg(windows)]
2743             if let Some(gpu_parameters) = &cfg.gpu_parameters {
2744                 let num_displays = gpu_parameters.display_params.len();
2745                 if num_displays > 1 {
2746                     return Err(format!(
2747                         "Only one display is supported (supplied {})",
2748                         num_displays
2749                     ));
2750                 }
2751             }
2752 
2753             #[cfg(unix)]
2754             {
2755                 cfg.gpu_cgroup_path = cmd.gpu_cgroup_path;
2756                 cfg.gpu_server_cgroup_path = cmd.gpu_server_cgroup_path;
2757             }
2758         }
2759 
2760         #[cfg(unix)]
2761         {
2762             cfg.shared_dirs = cmd.shared_dir;
2763 
2764             cfg.net = cmd.net;
2765 
2766             let vhost_net_msg = match cmd.vhost_net {
2767                 true => ",vhost-net=true",
2768                 false => "",
2769             };
2770             let vq_pairs_msg = match cmd.net_vq_pairs {
2771                 Some(n) => format!(",vq-pairs={}", n),
2772                 None => "".to_string(),
2773             };
2774 
2775             for tap_name in cmd.tap_name {
2776                 log::warn!(
2777                     "`--tap-name` is deprecated; please use \
2778                     `--net tap-name={tap_name}{vhost_net_msg}{vq_pairs_msg}`"
2779                 );
2780                 cfg.net.push(NetParameters {
2781                     mode: NetParametersMode::TapName {
2782                         tap_name,
2783                         mac: None,
2784                     },
2785                     vhost_net: cmd.vhost_net,
2786                     vq_pairs: cmd.net_vq_pairs,
2787                 });
2788             }
2789 
2790             for tap_fd in cmd.tap_fd {
2791                 log::warn!(
2792                     "`--tap-fd` is deprecated; please use \
2793                     `--net tap-fd={tap_fd}{vhost_net_msg}{vq_pairs_msg}`"
2794                 );
2795                 cfg.net.push(NetParameters {
2796                     mode: NetParametersMode::TapFd { tap_fd, mac: None },
2797                     vhost_net: cmd.vhost_net,
2798                     vq_pairs: cmd.net_vq_pairs,
2799                 });
2800             }
2801 
2802             if cmd.host_ip.is_some() || cmd.netmask.is_some() || cmd.mac_address.is_some() {
2803                 let host_ip = match cmd.host_ip {
2804                     Some(host_ip) => host_ip,
2805                     None => return Err("`host-ip` missing from network config".to_string()),
2806                 };
2807                 let netmask = match cmd.netmask {
2808                     Some(netmask) => netmask,
2809                     None => return Err("`netmask` missing from network config".to_string()),
2810                 };
2811                 let mac = match cmd.mac_address {
2812                     Some(mac) => mac,
2813                     None => return Err("`mac` missing from network config".to_string()),
2814                 };
2815 
2816                 if !cmd.vhost_user_net.is_empty() {
2817                     return Err(
2818                         "vhost-user-net cannot be used with any of --host-ip, --netmask or --mac"
2819                             .to_string(),
2820                     );
2821                 }
2822 
2823                 log::warn!(
2824                     "`--host-ip`, `--netmask`, and `--mac` are deprecated; please use \
2825                     `--net host-ip={host_ip},netmask={netmask},mac={mac}{vhost_net_msg}{vq_pairs_msg}`"
2826                 );
2827 
2828                 cfg.net.push(NetParameters {
2829                     mode: NetParametersMode::RawConfig {
2830                         host_ip,
2831                         netmask,
2832                         mac,
2833                     },
2834                     vhost_net: cmd.vhost_net,
2835                     vq_pairs: cmd.net_vq_pairs,
2836                 });
2837             }
2838 
2839             cfg.coiommu_param = cmd.coiommu;
2840 
2841             #[cfg(all(feature = "gpu", feature = "virgl_renderer_next"))]
2842             {
2843                 cfg.gpu_render_server_parameters = cmd.gpu_render_server;
2844             }
2845 
2846             if let Some(d) = cmd.seccomp_policy_dir {
2847                 cfg.jail_config
2848                     .get_or_insert_with(Default::default)
2849                     .seccomp_policy_dir = Some(d);
2850             }
2851 
2852             if cmd.seccomp_log_failures {
2853                 cfg.jail_config
2854                     .get_or_insert_with(Default::default)
2855                     .seccomp_log_failures = true;
2856             }
2857 
2858             if let Some(p) = cmd.pivot_root {
2859                 cfg.jail_config
2860                     .get_or_insert_with(Default::default)
2861                     .pivot_root = p;
2862             }
2863         }
2864 
2865         let protection_flags = [
2866             cmd.protected_vm,
2867             cmd.protected_vm_with_firmware.is_some(),
2868             cmd.protected_vm_without_firmware,
2869             cmd.unprotected_vm_with_firmware.is_some(),
2870         ];
2871 
2872         if protection_flags.into_iter().filter(|b| *b).count() > 1 {
2873             return Err("Only one protection mode has to be specified".to_string());
2874         }
2875 
2876         cfg.protection_type = if cmd.protected_vm {
2877             ProtectionType::Protected
2878         } else if cmd.protected_vm_without_firmware {
2879             ProtectionType::ProtectedWithoutFirmware
2880         } else if let Some(p) = cmd.protected_vm_with_firmware {
2881             if !p.exists() || !p.is_file() {
2882                 return Err(
2883                     "protected-vm-with-firmware path should be an existing file".to_string()
2884                 );
2885             }
2886             cfg.pvm_fw = Some(p);
2887             ProtectionType::ProtectedWithCustomFirmware
2888         } else if let Some(p) = cmd.unprotected_vm_with_firmware {
2889             if !p.exists() || !p.is_file() {
2890                 return Err(
2891                     "unprotected-vm-with-firmware path should be an existing file".to_string(),
2892                 );
2893             }
2894             cfg.pvm_fw = Some(p);
2895             ProtectionType::UnprotectedWithFirmware
2896         } else {
2897             ProtectionType::Unprotected
2898         };
2899 
2900         if !matches!(cfg.protection_type, ProtectionType::Unprotected) {
2901             // USB devices only work for unprotected VMs.
2902             cfg.usb = false;
2903             // Protected VMs can't trust the RNG device, so don't provide it.
2904             cfg.rng = false;
2905         }
2906 
2907         cfg.battery_config = cmd.battery;
2908         #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), unix))]
2909         {
2910             cfg.ac_adapter = cmd.ac_adapter;
2911         }
2912 
2913         #[cfg(feature = "gdb")]
2914         {
2915             cfg.gdb = cmd.gdb;
2916         }
2917 
2918         cfg.host_cpu_topology = cmd.host_cpu_topology;
2919 
2920         #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
2921         {
2922             cfg.enable_hwp = cmd.enable_hwp;
2923             cfg.force_s2idle = cmd.s2idle;
2924             cfg.pcie_ecam = cmd.pcie_ecam;
2925             cfg.pci_low_start = cmd.pci_start;
2926             cfg.no_i8042 = cmd.no_i8042;
2927             cfg.no_rtc = cmd.no_rtc;
2928             cfg.oem_strings = cmd.oem_strings;
2929 
2930             if !cfg.oem_strings.is_empty() && cfg.dmi_path.is_some() {
2931                 return Err("unable to use oem-strings and dmi-path together".to_string());
2932             }
2933             for (index, msr_config) in cmd.userspace_msr {
2934                 if cfg.userspace_msr.insert(index, msr_config).is_some() {
2935                     return Err(String::from("msr must be unique"));
2936                 }
2937             }
2938         }
2939 
2940         // cfg.balloon_bias is in bytes.
2941         if let Some(b) = cmd.balloon_bias_mib {
2942             cfg.balloon_bias = b * 1024 * 1024;
2943         }
2944 
2945         cfg.vhost_user_blk = cmd.vhost_user_blk;
2946         cfg.vhost_user_console = cmd.vhost_user_console;
2947         cfg.vhost_user_fs = cmd.vhost_user_fs;
2948         cfg.vhost_user_gpu = cmd.vhost_user_gpu;
2949         cfg.vhost_user_mac80211_hwsim = cmd.vhost_user_mac80211_hwsim;
2950         cfg.vhost_user_net = cmd.vhost_user_net;
2951         cfg.vhost_user_video_dec = cmd.vhost_user_video_decoder;
2952         cfg.vhost_user_vsock = cmd.vhost_user_vsock;
2953         cfg.vhost_user_wl = cmd.vhost_user_wl;
2954 
2955         #[cfg(feature = "direct")]
2956         {
2957             cfg.direct_pmio = cmd.direct_pmio;
2958             cfg.direct_mmio = cmd.direct_mmio;
2959             cfg.direct_level_irq = cmd.direct_level_irq;
2960             cfg.direct_edge_irq = cmd.direct_edge_irq;
2961             cfg.direct_gpe = cmd.direct_gpe;
2962             cfg.direct_fixed_evts = cmd.direct_fixed_event;
2963             cfg.pcie_rp = cmd.pcie_root_port;
2964             cfg.mmio_address_ranges = cmd.mmio_address_range.unwrap_or_default();
2965         }
2966 
2967         cfg.disable_virtio_intx = cmd.disable_virtio_intx;
2968 
2969         cfg.dmi_path = cmd.dmi;
2970 
2971         cfg.dump_device_tree_blob = cmd.dump_device_tree_blob;
2972 
2973         cfg.itmt = cmd.itmt;
2974 
2975         #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
2976         if cmd.enable_pnp_data && cmd.force_calibrated_tsc_leaf {
2977             return Err(
2978                 "Only one of [enable_pnp_data,force_calibrated_tsc_leaf] can be specified"
2979                     .to_string(),
2980             );
2981         }
2982 
2983         cfg.enable_pnp_data = cmd.enable_pnp_data;
2984 
2985         #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
2986         {
2987             cfg.force_calibrated_tsc_leaf = cmd.force_calibrated_tsc_leaf;
2988         }
2989 
2990         cfg.privileged_vm = cmd.privileged_vm;
2991 
2992         cfg.stub_pci_devices = cmd.stub_pci_device;
2993 
2994         cfg.vvu_proxy = cmd.vvu_proxy;
2995 
2996         cfg.file_backed_mappings = cmd.file_backed_mapping;
2997 
2998         cfg.init_memory = cmd.init_mem;
2999 
3000         cfg.strict_balloon = cmd.strict_balloon;
3001 
3002         #[cfg(target_os = "android")]
3003         {
3004             cfg.task_profiles = cmd.task_profiles;
3005         }
3006 
3007         #[cfg(unix)]
3008         {
3009             if cmd.unmap_guest_memory_on_fork && !cmd.disable_sandbox {
3010                 return Err("--unmap-guest-memory-on-fork requires --disable-sandbox".to_string());
3011             }
3012             cfg.unmap_guest_memory_on_fork = cmd.unmap_guest_memory_on_fork;
3013         }
3014 
3015         #[cfg(unix)]
3016         {
3017             cfg.vfio.extend(cmd.vfio);
3018             cfg.vfio.extend(cmd.vfio_platform);
3019             cfg.vfio_isolate_hotplug = cmd.vfio_isolate_hotplug;
3020         }
3021 
3022         // `--disable-sandbox` has the effect of disabling sandboxing altogether, so make sure
3023         // to handle it after other sandboxing options since they implicitly enable it.
3024         if cmd.disable_sandbox {
3025             cfg.jail_config = None;
3026         }
3027 
3028         // Now do validation of constructed config
3029         super::config::validate_config(&mut cfg)?;
3030 
3031         Ok(cfg)
3032     }
3033 }
3034