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