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