crosvm/crosvm/
cmdline.rs

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