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