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