• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #[cfg(target_arch = "x86_64")]
6 use std::arch::x86_64::__cpuid;
7 #[cfg(target_arch = "x86_64")]
8 use std::arch::x86_64::__cpuid_count;
9 use std::collections::BTreeMap;
10 use std::path::PathBuf;
11 use std::str::FromStr;
12 
13 use arch::set_default_serial_parameters;
14 use arch::CpuSet;
15 use arch::Pstore;
16 #[cfg(target_arch = "x86_64")]
17 use arch::SmbiosOptions;
18 use arch::VcpuAffinity;
19 use base::debug;
20 use base::pagesize;
21 use cros_async::ExecutorKind;
22 use devices::serial_device::SerialHardware;
23 use devices::serial_device::SerialParameters;
24 use devices::virtio::block::DiskOption;
25 #[cfg(any(feature = "video-decoder", feature = "video-encoder"))]
26 use devices::virtio::device_constants::video::VideoDeviceConfig;
27 #[cfg(feature = "gpu")]
28 use devices::virtio::gpu::GpuParameters;
29 use devices::virtio::scsi::ScsiOption;
30 #[cfg(feature = "audio")]
31 use devices::virtio::snd::parameters::Parameters as SndParameters;
32 #[cfg(all(windows, feature = "gpu"))]
33 use devices::virtio::vhost::user::device::gpu::sys::windows::GpuBackendConfig;
34 #[cfg(all(windows, feature = "gpu"))]
35 use devices::virtio::vhost::user::device::gpu::sys::windows::GpuVmmConfig;
36 #[cfg(all(windows, feature = "gpu"))]
37 use devices::virtio::vhost::user::device::gpu::sys::windows::InputEventSplitConfig;
38 #[cfg(all(windows, feature = "gpu"))]
39 use devices::virtio::vhost::user::device::gpu::sys::windows::WindowProcedureThreadSplitConfig;
40 #[cfg(all(windows, feature = "audio"))]
41 use devices::virtio::vhost::user::device::snd::sys::windows::SndSplitConfig;
42 use devices::virtio::vsock::VsockConfig;
43 use devices::virtio::DeviceType;
44 #[cfg(feature = "net")]
45 use devices::virtio::NetParameters;
46 use devices::FwCfgParameters;
47 use devices::PciAddress;
48 use devices::PflashParameters;
49 use devices::StubPciParameters;
50 #[cfg(target_arch = "x86_64")]
51 use hypervisor::CpuHybridType;
52 use hypervisor::ProtectionType;
53 use jail::JailConfig;
54 use resources::AddressRange;
55 use serde::Deserialize;
56 use serde::Serialize;
57 use serde_keyvalue::FromKeyValues;
58 use vm_control::BatteryType;
59 #[cfg(target_arch = "x86_64")]
60 use x86_64::check_host_hybrid_support;
61 #[cfg(target_arch = "x86_64")]
62 use x86_64::CpuIdCall;
63 
64 pub(crate) use super::sys::HypervisorKind;
65 #[cfg(any(target_os = "android", target_os = "linux"))]
66 use crate::crosvm::sys::config::SharedDir;
67 
68 cfg_if::cfg_if! {
69     if #[cfg(any(target_os = "android", target_os = "linux"))] {
70         #[cfg(feature = "gpu")]
71         use crate::crosvm::sys::GpuRenderServerParameters;
72 
73         #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
74         static VHOST_SCMI_PATH: &str = "/dev/vhost-scmi";
75     } else if #[cfg(windows)] {
76         use base::{Event, Tube};
77     }
78 }
79 
80 #[cfg(target_arch = "x86_64")]
81 const ONE_MB: u64 = 1 << 20;
82 #[cfg(target_arch = "x86_64")]
83 const MB_ALIGNED: u64 = ONE_MB - 1;
84 // the max bus number is 256 and each bus occupy 1MB, so the max pcie cfg mmio size = 256M
85 #[cfg(target_arch = "x86_64")]
86 const MAX_PCIE_ECAM_SIZE: u64 = ONE_MB * 256;
87 
88 // by default, if enabled, the balloon WS features will use 4 bins.
89 #[cfg(feature = "balloon")]
90 const VIRTIO_BALLOON_WS_DEFAULT_NUM_BINS: u8 = 4;
91 
92 /// Indicates the location and kind of executable kernel for a VM.
93 #[allow(dead_code)]
94 #[derive(Debug, Serialize, Deserialize)]
95 pub enum Executable {
96     /// An executable intended to be run as a BIOS directly.
97     Bios(PathBuf),
98     /// A elf linux kernel, loaded and executed by crosvm.
99     Kernel(PathBuf),
100     /// Path to a plugin executable that is forked by crosvm.
101     Plugin(PathBuf),
102 }
103 
104 #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, FromKeyValues)]
105 #[serde(deny_unknown_fields, rename_all = "kebab-case")]
106 pub enum IrqChipKind {
107     /// All interrupt controllers are emulated in the kernel.
108     Kernel,
109     /// APIC is emulated in the kernel.  All other interrupt controllers are in userspace.
110     Split,
111     /// All interrupt controllers are emulated in userspace.
112     Userspace,
113 }
114 
115 /// The core types in hybrid architecture.
116 #[cfg(target_arch = "x86_64")]
117 #[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
118 #[serde(deny_unknown_fields, rename_all = "kebab-case")]
119 pub struct CpuCoreType {
120     /// Intel Atom.
121     pub atom: CpuSet,
122     /// Intel Core.
123     pub core: CpuSet,
124 }
125 
126 #[derive(Debug, Default, PartialEq, Eq, Deserialize, Serialize, FromKeyValues)]
127 #[serde(deny_unknown_fields, rename_all = "kebab-case")]
128 pub struct CpuOptions {
129     /// Number of CPU cores.
130     #[serde(default)]
131     pub num_cores: Option<usize>,
132     /// Vector of CPU ids to be grouped into the same cluster.
133     #[serde(default)]
134     pub clusters: Vec<CpuSet>,
135     /// Core Type of CPUs.
136     #[cfg(target_arch = "x86_64")]
137     pub core_types: Option<CpuCoreType>,
138     /// Select which CPU to boot from.
139     #[serde(default)]
140     pub boot_cpu: Option<usize>,
141 }
142 
143 /// Device tree overlay configuration.
144 #[derive(Debug, Default, Serialize, Deserialize, FromKeyValues)]
145 #[serde(deny_unknown_fields, rename_all = "kebab-case")]
146 pub struct DtboOption {
147     /// Overlay file to apply to the base device tree.
148     pub path: PathBuf,
149     /// Whether to only apply device tree nodes which belong to a VFIO device.
150     #[serde(rename = "filter", default)]
151     pub filter_devs: bool,
152 }
153 
154 #[derive(Debug, Default, Deserialize, Serialize, FromKeyValues, PartialEq, Eq)]
155 #[serde(deny_unknown_fields, rename_all = "kebab-case")]
156 pub struct MemOptions {
157     /// Amount of guest memory in MiB.
158     #[serde(default)]
159     pub size: Option<u64>,
160 }
161 
162 #[derive(Serialize, Deserialize, FromKeyValues)]
163 #[serde(deny_unknown_fields, rename_all = "kebab-case")]
164 pub struct VhostUserOption {
165     pub socket: PathBuf,
166 
167     /// Maximum number of entries per queue (default: 32768)
168     pub max_queue_size: Option<u16>,
169 }
170 
171 #[derive(Serialize, Deserialize, FromKeyValues)]
172 #[serde(deny_unknown_fields, rename_all = "kebab-case")]
173 pub struct VhostUserFrontendOption {
174     /// Device type
175     #[serde(rename = "type")]
176     pub type_: devices::virtio::DeviceType,
177 
178     /// Path to the vhost-user backend socket to connect to
179     pub socket: PathBuf,
180 
181     /// Maximum number of entries per queue (default: 32768)
182     pub max_queue_size: Option<u16>,
183 
184     /// Preferred PCI address
185     pub pci_address: Option<PciAddress>,
186 }
187 
188 #[derive(Serialize, Deserialize, FromKeyValues)]
189 #[serde(deny_unknown_fields, rename_all = "kebab-case")]
190 pub struct VhostUserFsOption {
191     pub socket: PathBuf,
192     pub tag: Option<String>,
193 
194     /// Maximum number of entries per queue (default: 32768)
195     pub max_queue_size: Option<u16>,
196 }
197 
parse_vhost_user_fs_option(param: &str) -> Result<VhostUserFsOption, String>198 pub fn parse_vhost_user_fs_option(param: &str) -> Result<VhostUserFsOption, String> {
199     // Allow the previous `--vhost-user-fs /path/to/socket:fs-tag` format for compatibility.
200     // This will unfortunately prevent parsing of valid comma-separated FromKeyValues options that
201     // contain a ":" character (e.g. in a socket filename), but those were not supported in the old
202     // format either, so we can live with it until the deprecated format is removed.
203     // TODO(b/218223240): Remove support for the deprecated format (and use `FromKeyValues`
204     // directly instead of `from_str_fn`) once enough time has passed.
205     if param.contains(':') {
206         // (socket:tag)
207         let mut components = param.split(':');
208         let socket = PathBuf::from(
209             components
210                 .next()
211                 .ok_or("missing socket path for `vhost-user-fs`")?,
212         );
213         let tag = components
214             .next()
215             .ok_or("missing tag for `vhost-user-fs`")?
216             .to_owned();
217 
218         log::warn!(
219             "`--vhost-user-fs` with colon-separated options is deprecated; \
220             please use `--vhost-user-fs {},tag={}` instead",
221             socket.display(),
222             tag,
223         );
224 
225         Ok(VhostUserFsOption {
226             socket,
227             tag: Some(tag),
228             max_queue_size: None,
229         })
230     } else {
231         from_key_values::<VhostUserFsOption>(param)
232     }
233 }
234 
235 pub const DEFAULT_TOUCH_DEVICE_HEIGHT: u32 = 1024;
236 pub const DEFAULT_TOUCH_DEVICE_WIDTH: u32 = 1280;
237 
238 #[derive(Serialize, Deserialize, Debug, FromKeyValues)]
239 #[serde(deny_unknown_fields, rename_all = "kebab-case")]
240 pub struct TouchDeviceOption {
241     pub path: PathBuf,
242     pub width: Option<u32>,
243     pub height: Option<u32>,
244     pub name: Option<String>,
245 }
246 
247 /// Try to parse a colon-separated touch device option.
248 ///
249 /// The expected format is "PATH:WIDTH:HEIGHT:NAME", with all fields except PATH being optional.
parse_touch_device_option_legacy(s: &str) -> Option<TouchDeviceOption>250 fn parse_touch_device_option_legacy(s: &str) -> Option<TouchDeviceOption> {
251     let mut it = s.split(':');
252     let path = PathBuf::from(it.next()?.to_owned());
253     let width = if let Some(width) = it.next() {
254         Some(width.trim().parse().ok()?)
255     } else {
256         None
257     };
258     let height = if let Some(height) = it.next() {
259         Some(height.trim().parse().ok()?)
260     } else {
261         None
262     };
263     let name = it.next().map(|name| name.trim().to_string());
264     if it.next().is_some() {
265         return None;
266     }
267 
268     Some(TouchDeviceOption {
269         path,
270         width,
271         height,
272         name,
273     })
274 }
275 
276 /// Parse virtio-input touch device options from a string.
277 ///
278 /// This function only exists to enable the use of the deprecated colon-separated form
279 /// ("PATH:WIDTH:HEIGHT:NAME"); once the deprecation period is over, this function should be removed
280 /// in favor of using the derived `FromKeyValues` function directly.
parse_touch_device_option(s: &str) -> Result<TouchDeviceOption, String>281 pub fn parse_touch_device_option(s: &str) -> Result<TouchDeviceOption, String> {
282     if s.contains(':') {
283         if let Some(touch_spec) = parse_touch_device_option_legacy(s) {
284             log::warn!(
285                 "colon-separated touch device options are deprecated; \
286                 please use --input instead"
287             );
288             return Ok(touch_spec);
289         }
290     }
291 
292     from_key_values::<TouchDeviceOption>(s)
293 }
294 
295 /// virtio-input device configuration
296 #[derive(Serialize, Deserialize, Debug, FromKeyValues, Eq, PartialEq)]
297 #[serde(deny_unknown_fields, rename_all = "kebab-case")]
298 pub enum InputDeviceOption {
299     Evdev {
300         path: PathBuf,
301     },
302     Keyboard {
303         path: PathBuf,
304     },
305     Mouse {
306         path: PathBuf,
307     },
308     MultiTouch {
309         path: PathBuf,
310         width: Option<u32>,
311         height: Option<u32>,
312         name: Option<String>,
313     },
314     Rotary {
315         path: PathBuf,
316     },
317     SingleTouch {
318         path: PathBuf,
319         width: Option<u32>,
320         height: Option<u32>,
321         name: Option<String>,
322     },
323     Switches {
324         path: PathBuf,
325     },
326     Trackpad {
327         path: PathBuf,
328         width: Option<u32>,
329         height: Option<u32>,
330         name: Option<String>,
331     },
332 }
333 
334 #[derive(Debug, Serialize, Deserialize, FromKeyValues)]
335 #[serde(deny_unknown_fields)]
336 pub struct FileBackedMappingParameters {
337     pub path: PathBuf,
338     #[serde(rename = "addr")]
339     pub address: u64,
340     pub size: u64,
341     #[serde(default)]
342     pub offset: u64,
343     #[serde(rename = "rw", default)]
344     pub writable: bool,
345     #[serde(default)]
346     pub sync: bool,
347     #[serde(default)]
348     pub align: bool,
349 }
350 
parse_hex_or_decimal(maybe_hex_string: &str) -> Result<u64, String>351 fn parse_hex_or_decimal(maybe_hex_string: &str) -> Result<u64, String> {
352     // Parse string starting with 0x as hex and others as numbers.
353     if let Some(hex_string) = maybe_hex_string.strip_prefix("0x") {
354         u64::from_str_radix(hex_string, 16)
355     } else if let Some(hex_string) = maybe_hex_string.strip_prefix("0X") {
356         u64::from_str_radix(hex_string, 16)
357     } else {
358         u64::from_str(maybe_hex_string)
359     }
360     .map_err(|e| format!("invalid numeric value {}: {}", maybe_hex_string, e))
361 }
362 
parse_mmio_address_range(s: &str) -> Result<Vec<AddressRange>, String>363 pub fn parse_mmio_address_range(s: &str) -> Result<Vec<AddressRange>, String> {
364     s.split(",")
365         .map(|s| {
366             let r: Vec<&str> = s.split("-").collect();
367             if r.len() != 2 {
368                 return Err(invalid_value_err(s, "invalid range"));
369             }
370             let parse = |s: &str| -> Result<u64, String> {
371                 match parse_hex_or_decimal(s) {
372                     Ok(v) => Ok(v),
373                     Err(_) => Err(invalid_value_err(s, "expected u64 value")),
374                 }
375             };
376             Ok(AddressRange {
377                 start: parse(r[0])?,
378                 end: parse(r[1])?,
379             })
380         })
381         .collect()
382 }
383 
validate_serial_parameters(params: &SerialParameters) -> Result<(), String>384 pub fn validate_serial_parameters(params: &SerialParameters) -> Result<(), String> {
385     if params.stdin && params.input.is_some() {
386         return Err("Cannot specify both stdin and input options".to_string());
387     }
388     if params.num < 1 {
389         return Err(invalid_value_err(
390             params.num.to_string(),
391             "Serial port num must be at least 1",
392         ));
393     }
394 
395     if params.hardware == SerialHardware::Serial && params.num > 4 {
396         return Err(invalid_value_err(
397             format!("{}", params.num),
398             "Serial port num must be 4 or less",
399         ));
400     }
401 
402     if params.pci_address.is_some()
403         && params.hardware != SerialHardware::VirtioConsole
404         && params.hardware != SerialHardware::LegacyVirtioConsole
405     {
406         return Err(invalid_value_err(
407             params.pci_address.unwrap().to_string(),
408             "Providing serial PCI address is only supported for virtio-console hardware type",
409         ));
410     }
411 
412     Ok(())
413 }
414 
parse_serial_options(s: &str) -> Result<SerialParameters, String>415 pub fn parse_serial_options(s: &str) -> Result<SerialParameters, String> {
416     let params: SerialParameters = from_key_values(s)?;
417 
418     validate_serial_parameters(&params)?;
419 
420     Ok(params)
421 }
422 
423 #[cfg(target_arch = "x86_64")]
parse_memory_region(value: &str) -> Result<AddressRange, String>424 pub fn parse_memory_region(value: &str) -> Result<AddressRange, String> {
425     let paras: Vec<&str> = value.split(',').collect();
426     if paras.len() != 2 {
427         return Err(invalid_value_err(
428             value,
429             "pcie-ecam must have exactly 2 parameters: ecam_base,ecam_size",
430         ));
431     }
432     let base = parse_hex_or_decimal(paras[0]).map_err(|_| {
433         invalid_value_err(
434             value,
435             "pcie-ecam, the first parameter base should be integer",
436         )
437     })?;
438     let mut len = parse_hex_or_decimal(paras[1]).map_err(|_| {
439         invalid_value_err(
440             value,
441             "pcie-ecam, the second parameter size should be integer",
442         )
443     })?;
444 
445     if (base & MB_ALIGNED != 0) || (len & MB_ALIGNED != 0) {
446         return Err(invalid_value_err(
447             value,
448             "pcie-ecam, the base and len should be aligned to 1MB",
449         ));
450     }
451 
452     if len > MAX_PCIE_ECAM_SIZE {
453         len = MAX_PCIE_ECAM_SIZE;
454     }
455 
456     if base + len >= 0x1_0000_0000 {
457         return Err(invalid_value_err(
458             value,
459             "pcie-ecam, the end address couldn't beyond 4G",
460         ));
461     }
462 
463     if base % len != 0 {
464         return Err(invalid_value_err(
465             value,
466             "pcie-ecam, base should be multiple of len",
467         ));
468     }
469 
470     if let Some(range) = AddressRange::from_start_and_size(base, len) {
471         Ok(range)
472     } else {
473         Err(invalid_value_err(
474             value,
475             "pcie-ecam must be representable as AddressRange",
476         ))
477     }
478 }
479 
parse_bus_id_addr(v: &str) -> Result<(u8, u8, u16, u16), String>480 pub fn parse_bus_id_addr(v: &str) -> Result<(u8, u8, u16, u16), String> {
481     debug!("parse_bus_id_addr: {}", v);
482     let mut ids = v.split(':');
483     let errorre = move |item| move |e| format!("{}: {}", item, e);
484     match (ids.next(), ids.next(), ids.next(), ids.next()) {
485         (Some(bus_id), Some(addr), Some(vid), Some(pid)) => {
486             let bus_id = bus_id.parse::<u8>().map_err(errorre("bus_id"))?;
487             let addr = addr.parse::<u8>().map_err(errorre("addr"))?;
488             let vid = u16::from_str_radix(vid, 16).map_err(errorre("vid"))?;
489             let pid = u16::from_str_radix(pid, 16).map_err(errorre("pid"))?;
490             Ok((bus_id, addr, vid, pid))
491         }
492         _ => Err(String::from("BUS_ID:ADDR:BUS_NUM:DEV_NUM")),
493     }
494 }
495 
invalid_value_err<T: AsRef<str>, S: ToString>(value: T, expected: S) -> String496 pub fn invalid_value_err<T: AsRef<str>, S: ToString>(value: T, expected: S) -> String {
497     format!("invalid value {}: {}", value.as_ref(), expected.to_string())
498 }
499 
500 #[derive(Debug, Serialize, Deserialize, FromKeyValues)]
501 #[serde(deny_unknown_fields, rename_all = "kebab-case")]
502 pub struct BatteryConfig {
503     #[serde(rename = "type", default)]
504     pub type_: BatteryType,
505 }
506 
parse_cpu_capacity(s: &str) -> Result<BTreeMap<usize, u32>, String>507 pub fn parse_cpu_capacity(s: &str) -> Result<BTreeMap<usize, u32>, String> {
508     let mut cpu_capacity: BTreeMap<usize, u32> = BTreeMap::default();
509     for cpu_pair in s.split(',') {
510         let assignment: Vec<&str> = cpu_pair.split('=').collect();
511         if assignment.len() != 2 {
512             return Err(invalid_value_err(cpu_pair, "invalid CPU capacity syntax"));
513         }
514         let cpu = assignment[0].parse().map_err(|_| {
515             invalid_value_err(assignment[0], "CPU index must be a non-negative integer")
516         })?;
517         let capacity = assignment[1].parse().map_err(|_| {
518             invalid_value_err(assignment[1], "CPU capacity must be a non-negative integer")
519         })?;
520         if cpu_capacity.insert(cpu, capacity).is_some() {
521             return Err(invalid_value_err(cpu_pair, "CPU index must be unique"));
522         }
523     }
524     Ok(cpu_capacity)
525 }
526 
parse_dynamic_power_coefficient(s: &str) -> Result<BTreeMap<usize, u32>, String>527 pub fn parse_dynamic_power_coefficient(s: &str) -> Result<BTreeMap<usize, u32>, String> {
528     let mut dyn_power_coefficient: BTreeMap<usize, u32> = BTreeMap::default();
529     for cpu_pair in s.split(',') {
530         let assignment: Vec<&str> = cpu_pair.split('=').collect();
531         if assignment.len() != 2 {
532             return Err(invalid_value_err(
533                 cpu_pair,
534                 "invalid CPU dynamic power pair syntax",
535             ));
536         }
537         let cpu = assignment[0].parse().map_err(|_| {
538             invalid_value_err(assignment[0], "CPU index must be a non-negative integer")
539         })?;
540         let power_coefficient = assignment[1].parse().map_err(|_| {
541             invalid_value_err(
542                 assignment[1],
543                 "Power coefficient must be a non-negative integer",
544             )
545         })?;
546         if dyn_power_coefficient
547             .insert(cpu, power_coefficient)
548             .is_some()
549         {
550             return Err(invalid_value_err(cpu_pair, "CPU index must be unique"));
551         }
552     }
553     Ok(dyn_power_coefficient)
554 }
555 
from_key_values<'a, T: Deserialize<'a>>(value: &'a str) -> Result<T, String>556 pub fn from_key_values<'a, T: Deserialize<'a>>(value: &'a str) -> Result<T, String> {
557     serde_keyvalue::from_key_values(value).map_err(|e| e.to_string())
558 }
559 
560 /// Parse a list of guest to host CPU mappings.
561 ///
562 /// Each mapping consists of a single guest CPU index mapped to one or more host CPUs in the form
563 /// accepted by `CpuSet::from_str`:
564 ///
565 ///  `<GUEST-CPU>=<HOST-CPU-SET>[:<GUEST-CPU>=<HOST-CPU-SET>[:...]]`
parse_cpu_affinity(s: &str) -> Result<VcpuAffinity, String>566 pub fn parse_cpu_affinity(s: &str) -> Result<VcpuAffinity, String> {
567     if s.contains('=') {
568         let mut affinity_map = BTreeMap::new();
569         for cpu_pair in s.split(':') {
570             let assignment: Vec<&str> = cpu_pair.split('=').collect();
571             if assignment.len() != 2 {
572                 return Err(invalid_value_err(
573                     cpu_pair,
574                     "invalid VCPU assignment syntax",
575                 ));
576             }
577             let guest_cpu = assignment[0].parse().map_err(|_| {
578                 invalid_value_err(assignment[0], "CPU index must be a non-negative integer")
579             })?;
580             let host_cpu_set = CpuSet::from_str(assignment[1])?;
581             if affinity_map.insert(guest_cpu, host_cpu_set).is_some() {
582                 return Err(invalid_value_err(cpu_pair, "VCPU index must be unique"));
583             }
584         }
585         Ok(VcpuAffinity::PerVcpu(affinity_map))
586     } else {
587         Ok(VcpuAffinity::Global(CpuSet::from_str(s)?))
588     }
589 }
590 
executable_is_plugin(executable: &Option<Executable>) -> bool591 pub fn executable_is_plugin(executable: &Option<Executable>) -> bool {
592     matches!(executable, Some(Executable::Plugin(_)))
593 }
594 
parse_pflash_parameters(s: &str) -> Result<PflashParameters, String>595 pub fn parse_pflash_parameters(s: &str) -> Result<PflashParameters, String> {
596     let pflash_parameters: PflashParameters = from_key_values(s)?;
597 
598     Ok(pflash_parameters)
599 }
600 
601 // BTreeMaps serialize fine, as long as their keys are trivial types. A tuple does not
602 // work, hence the need to convert to/from a vector form.
603 mod serde_serial_params {
604     use std::iter::FromIterator;
605 
606     use serde::Deserializer;
607     use serde::Serializer;
608 
609     use super::*;
610 
serialize<S>( params: &BTreeMap<(SerialHardware, u8), SerialParameters>, ser: S, ) -> Result<S::Ok, S::Error> where S: Serializer,611     pub fn serialize<S>(
612         params: &BTreeMap<(SerialHardware, u8), SerialParameters>,
613         ser: S,
614     ) -> Result<S::Ok, S::Error>
615     where
616         S: Serializer,
617     {
618         let v: Vec<(&(SerialHardware, u8), &SerialParameters)> = params.iter().collect();
619         serde::Serialize::serialize(&v, ser)
620     }
621 
deserialize<'a, D>( de: D, ) -> Result<BTreeMap<(SerialHardware, u8), SerialParameters>, D::Error> where D: Deserializer<'a>,622     pub fn deserialize<'a, D>(
623         de: D,
624     ) -> Result<BTreeMap<(SerialHardware, u8), SerialParameters>, D::Error>
625     where
626         D: Deserializer<'a>,
627     {
628         let params: Vec<((SerialHardware, u8), SerialParameters)> =
629             serde::Deserialize::deserialize(de)?;
630         Ok(BTreeMap::from_iter(params))
631     }
632 }
633 
634 /// Aggregate of all configurable options for a running VM.
635 #[derive(Serialize, Deserialize)]
636 #[remain::sorted]
637 pub struct Config {
638     #[cfg(all(target_arch = "x86_64", unix))]
639     pub ac_adapter: bool,
640     pub acpi_tables: Vec<PathBuf>,
641     #[cfg(feature = "android_display")]
642     pub android_display_service: Option<String>,
643     pub android_fstab: Option<PathBuf>,
644     pub async_executor: Option<ExecutorKind>,
645     #[cfg(feature = "balloon")]
646     pub balloon: bool,
647     #[cfg(feature = "balloon")]
648     pub balloon_bias: i64,
649     #[cfg(feature = "balloon")]
650     pub balloon_control: Option<PathBuf>,
651     #[cfg(feature = "balloon")]
652     pub balloon_page_reporting: bool,
653     #[cfg(feature = "balloon")]
654     pub balloon_ws_num_bins: u8,
655     #[cfg(feature = "balloon")]
656     pub balloon_ws_reporting: bool,
657     pub battery_config: Option<BatteryConfig>,
658     #[cfg(windows)]
659     pub block_control_tube: Vec<Tube>,
660     #[cfg(windows)]
661     pub block_vhost_user_tube: Vec<Tube>,
662     #[cfg(any(target_os = "android", target_os = "linux"))]
663     pub boost_uclamp: bool,
664     pub boot_cpu: usize,
665     #[cfg(target_arch = "x86_64")]
666     pub break_linux_pci_config_io: bool,
667     #[cfg(windows)]
668     pub broker_shutdown_event: Option<Event>,
669     #[cfg(target_arch = "x86_64")]
670     pub bus_lock_ratelimit: u64,
671     #[cfg(any(target_os = "android", target_os = "linux"))]
672     pub coiommu_param: Option<devices::CoIommuParameters>,
673     pub core_scheduling: bool,
674     pub cpu_capacity: BTreeMap<usize, u32>, // CPU index -> capacity
675     pub cpu_clusters: Vec<CpuSet>,
676     #[cfg(feature = "crash-report")]
677     pub crash_pipe_name: Option<String>,
678     #[cfg(feature = "crash-report")]
679     pub crash_report_uuid: Option<String>,
680     pub delay_rt: bool,
681     pub device_tree_overlay: Vec<DtboOption>,
682     pub disable_virtio_intx: bool,
683     pub disks: Vec<DiskOption>,
684     pub display_input_height: Option<u32>,
685     pub display_input_width: Option<u32>,
686     pub display_window_keyboard: bool,
687     pub display_window_mouse: bool,
688     pub dump_device_tree_blob: Option<PathBuf>,
689     pub dynamic_power_coefficient: BTreeMap<usize, u32>,
690     pub enable_fw_cfg: bool,
691     pub enable_hwp: bool,
692     pub executable_path: Option<Executable>,
693     #[cfg(windows)]
694     pub exit_stats: bool,
695     pub file_backed_mappings: Vec<FileBackedMappingParameters>,
696     pub force_calibrated_tsc_leaf: bool,
697     pub force_s2idle: bool,
698     pub fw_cfg_parameters: Vec<FwCfgParameters>,
699     #[cfg(feature = "gdb")]
700     pub gdb: Option<u32>,
701     #[cfg(all(windows, feature = "gpu"))]
702     pub gpu_backend_config: Option<GpuBackendConfig>,
703     #[cfg(all(unix, feature = "gpu"))]
704     pub gpu_cgroup_path: Option<PathBuf>,
705     #[cfg(feature = "gpu")]
706     pub gpu_parameters: Option<GpuParameters>,
707     #[cfg(all(unix, feature = "gpu"))]
708     pub gpu_render_server_parameters: Option<GpuRenderServerParameters>,
709     #[cfg(all(unix, feature = "gpu"))]
710     pub gpu_server_cgroup_path: Option<PathBuf>,
711     #[cfg(all(windows, feature = "gpu"))]
712     pub gpu_vmm_config: Option<GpuVmmConfig>,
713     pub host_cpu_topology: bool,
714     #[cfg(windows)]
715     pub host_guid: Option<String>,
716     pub hugepages: bool,
717     pub hypervisor: Option<HypervisorKind>,
718     #[cfg(feature = "balloon")]
719     pub init_memory: Option<u64>,
720     pub initrd_path: Option<PathBuf>,
721     #[cfg(all(windows, feature = "gpu"))]
722     pub input_event_split_config: Option<InputEventSplitConfig>,
723     pub irq_chip: Option<IrqChipKind>,
724     pub itmt: bool,
725     pub jail_config: Option<JailConfig>,
726     #[cfg(windows)]
727     pub kernel_log_file: Option<String>,
728     #[cfg(any(target_os = "android", target_os = "linux"))]
729     pub lock_guest_memory: bool,
730     #[cfg(windows)]
731     pub log_file: Option<String>,
732     #[cfg(windows)]
733     pub logs_directory: Option<String>,
734     pub memory: Option<u64>,
735     pub memory_file: Option<PathBuf>,
736     pub mmio_address_ranges: Vec<AddressRange>,
737     #[cfg(target_arch = "aarch64")]
738     pub mte: bool,
739     #[cfg(feature = "net")]
740     pub net: Vec<NetParameters>,
741     #[cfg(windows)]
742     pub net_vhost_user_tube: Option<Tube>,
743     pub no_i8042: bool,
744     pub no_rtc: bool,
745     pub no_smt: bool,
746     pub params: Vec<String>,
747     #[cfg(feature = "pci-hotplug")]
748     pub pci_hotplug_slots: Option<u8>,
749     #[cfg(target_arch = "x86_64")]
750     pub pci_low_start: Option<u64>,
751     #[cfg(target_arch = "x86_64")]
752     pub pcie_ecam: Option<AddressRange>,
753     pub per_vm_core_scheduling: bool,
754     pub pflash_parameters: Option<PflashParameters>,
755     #[cfg(feature = "plugin")]
756     pub plugin_gid_maps: Vec<crate::crosvm::plugin::GidMap>,
757     #[cfg(feature = "plugin")]
758     pub plugin_mounts: Vec<crate::crosvm::plugin::BindMount>,
759     pub plugin_root: Option<PathBuf>,
760     pub pmem_devices: Vec<DiskOption>,
761     #[cfg(feature = "process-invariants")]
762     pub process_invariants_data_handle: Option<u64>,
763     #[cfg(feature = "process-invariants")]
764     pub process_invariants_data_size: Option<usize>,
765     #[cfg(windows)]
766     pub product_channel: Option<String>,
767     #[cfg(windows)]
768     pub product_name: Option<String>,
769     #[cfg(windows)]
770     pub product_version: Option<String>,
771     pub protection_type: ProtectionType,
772     pub pstore: Option<Pstore>,
773     pub pvclock: bool,
774     /// Must be `Some` iff `protection_type == ProtectionType::UnprotectedWithFirmware`.
775     pub pvm_fw: Option<PathBuf>,
776     pub restore_path: Option<PathBuf>,
777     pub rng: bool,
778     pub rt_cpus: CpuSet,
779     pub scsis: Vec<ScsiOption>,
780     #[serde(with = "serde_serial_params")]
781     pub serial_parameters: BTreeMap<(SerialHardware, u8), SerialParameters>,
782     #[cfg(windows)]
783     pub service_pipe_name: Option<String>,
784     #[cfg(any(target_os = "android", target_os = "linux"))]
785     #[serde(skip)]
786     pub shared_dirs: Vec<SharedDir>,
787     #[cfg(any(feature = "slirp-ring-capture", feature = "slirp-debug"))]
788     pub slirp_capture_file: Option<String>,
789     #[cfg(target_arch = "x86_64")]
790     pub smbios: SmbiosOptions,
791     #[cfg(all(windows, feature = "audio"))]
792     pub snd_split_config: Option<SndSplitConfig>,
793     pub socket_path: Option<PathBuf>,
794     #[cfg(feature = "audio")]
795     pub sound: Option<PathBuf>,
796     #[cfg(feature = "balloon")]
797     pub strict_balloon: bool,
798     pub stub_pci_devices: Vec<StubPciParameters>,
799     pub suspended: bool,
800     pub swap_dir: Option<PathBuf>,
801     pub swiotlb: Option<u64>,
802     #[cfg(target_os = "android")]
803     pub task_profiles: Vec<String>,
804     #[cfg(any(target_os = "android", target_os = "linux"))]
805     pub unmap_guest_memory_on_fork: bool,
806     pub usb: bool,
807     pub vcpu_affinity: Option<VcpuAffinity>,
808     pub vcpu_cgroup_path: Option<PathBuf>,
809     pub vcpu_count: Option<usize>,
810     #[cfg(target_arch = "x86_64")]
811     pub vcpu_hybrid_type: BTreeMap<usize, CpuHybridType>, // CPU index -> hybrid type
812     #[cfg(any(target_os = "android", target_os = "linux"))]
813     pub vfio: Vec<super::sys::config::VfioOption>,
814     #[cfg(any(target_os = "android", target_os = "linux"))]
815     pub vfio_isolate_hotplug: bool,
816     #[cfg(any(target_os = "android", target_os = "linux"))]
817     #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
818     pub vhost_scmi: bool,
819     #[cfg(any(target_os = "android", target_os = "linux"))]
820     #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
821     pub vhost_scmi_device: PathBuf,
822     pub vhost_user: Vec<VhostUserFrontendOption>,
823     pub vhost_user_fs: Vec<VhostUserFsOption>,
824     #[cfg(feature = "video-decoder")]
825     pub video_dec: Vec<VideoDeviceConfig>,
826     #[cfg(feature = "video-encoder")]
827     pub video_enc: Vec<VideoDeviceConfig>,
828     #[cfg(all(
829         any(target_arch = "arm", target_arch = "aarch64"),
830         any(target_os = "android", target_os = "linux")
831     ))]
832     pub virt_cpufreq: bool,
833     pub virt_cpufreq_socket: Option<PathBuf>,
834     pub virtio_input: Vec<InputDeviceOption>,
835     #[cfg(feature = "audio")]
836     #[serde(skip)]
837     pub virtio_snds: Vec<SndParameters>,
838     pub vsock: Option<VsockConfig>,
839     #[cfg(feature = "vtpm")]
840     pub vtpm_proxy: bool,
841     pub wayland_socket_paths: BTreeMap<String, PathBuf>,
842     #[cfg(all(windows, feature = "gpu"))]
843     pub window_procedure_thread_split_config: Option<WindowProcedureThreadSplitConfig>,
844     pub x_display: Option<String>,
845 }
846 
847 impl Default for Config {
default() -> Config848     fn default() -> Config {
849         Config {
850             #[cfg(all(target_arch = "x86_64", unix))]
851             ac_adapter: false,
852             acpi_tables: Vec::new(),
853             #[cfg(feature = "android_display")]
854             android_display_service: None,
855             android_fstab: None,
856             async_executor: None,
857             #[cfg(feature = "balloon")]
858             balloon: true,
859             #[cfg(feature = "balloon")]
860             balloon_bias: 0,
861             #[cfg(feature = "balloon")]
862             balloon_control: None,
863             #[cfg(feature = "balloon")]
864             balloon_page_reporting: false,
865             #[cfg(feature = "balloon")]
866             balloon_ws_num_bins: VIRTIO_BALLOON_WS_DEFAULT_NUM_BINS,
867             #[cfg(feature = "balloon")]
868             balloon_ws_reporting: false,
869             battery_config: None,
870             boot_cpu: 0,
871             #[cfg(windows)]
872             block_control_tube: Vec::new(),
873             #[cfg(windows)]
874             block_vhost_user_tube: Vec::new(),
875             #[cfg(target_arch = "x86_64")]
876             break_linux_pci_config_io: false,
877             #[cfg(windows)]
878             broker_shutdown_event: None,
879             #[cfg(target_arch = "x86_64")]
880             bus_lock_ratelimit: 0,
881             #[cfg(any(target_os = "android", target_os = "linux"))]
882             coiommu_param: None,
883             core_scheduling: true,
884             #[cfg(feature = "crash-report")]
885             crash_pipe_name: None,
886             #[cfg(feature = "crash-report")]
887             crash_report_uuid: None,
888             cpu_capacity: BTreeMap::new(),
889             cpu_clusters: Vec::new(),
890             delay_rt: false,
891             device_tree_overlay: Vec::new(),
892             disks: Vec::new(),
893             disable_virtio_intx: false,
894             display_input_height: None,
895             display_input_width: None,
896             display_window_keyboard: false,
897             display_window_mouse: false,
898             dump_device_tree_blob: None,
899             dynamic_power_coefficient: BTreeMap::new(),
900             enable_fw_cfg: false,
901             enable_hwp: false,
902             executable_path: None,
903             #[cfg(windows)]
904             exit_stats: false,
905             file_backed_mappings: Vec::new(),
906             force_calibrated_tsc_leaf: false,
907             force_s2idle: false,
908             fw_cfg_parameters: Vec::new(),
909             #[cfg(feature = "gdb")]
910             gdb: None,
911             #[cfg(all(windows, feature = "gpu"))]
912             gpu_backend_config: None,
913             #[cfg(feature = "gpu")]
914             gpu_parameters: None,
915             #[cfg(all(unix, feature = "gpu"))]
916             gpu_render_server_parameters: None,
917             #[cfg(all(unix, feature = "gpu"))]
918             gpu_cgroup_path: None,
919             #[cfg(all(unix, feature = "gpu"))]
920             gpu_server_cgroup_path: None,
921             #[cfg(all(windows, feature = "gpu"))]
922             gpu_vmm_config: None,
923             host_cpu_topology: false,
924             #[cfg(windows)]
925             host_guid: None,
926             #[cfg(windows)]
927             product_version: None,
928             #[cfg(windows)]
929             product_channel: None,
930             hugepages: false,
931             hypervisor: None,
932             #[cfg(feature = "balloon")]
933             init_memory: None,
934             initrd_path: None,
935             #[cfg(all(windows, feature = "gpu"))]
936             input_event_split_config: None,
937             irq_chip: None,
938             itmt: false,
939             jail_config: if !cfg!(feature = "default-no-sandbox") {
940                 Some(Default::default())
941             } else {
942                 None
943             },
944             #[cfg(windows)]
945             kernel_log_file: None,
946             #[cfg(any(target_os = "android", target_os = "linux"))]
947             lock_guest_memory: false,
948             #[cfg(windows)]
949             log_file: None,
950             #[cfg(windows)]
951             logs_directory: None,
952             #[cfg(any(target_os = "android", target_os = "linux"))]
953             boost_uclamp: false,
954             memory: None,
955             memory_file: None,
956             mmio_address_ranges: Vec::new(),
957             #[cfg(target_arch = "aarch64")]
958             mte: false,
959             #[cfg(feature = "net")]
960             net: Vec::new(),
961             #[cfg(windows)]
962             net_vhost_user_tube: None,
963             no_i8042: false,
964             no_rtc: false,
965             no_smt: false,
966             params: Vec::new(),
967             #[cfg(feature = "pci-hotplug")]
968             pci_hotplug_slots: None,
969             #[cfg(target_arch = "x86_64")]
970             pci_low_start: None,
971             #[cfg(target_arch = "x86_64")]
972             pcie_ecam: None,
973             per_vm_core_scheduling: false,
974             pflash_parameters: None,
975             #[cfg(feature = "plugin")]
976             plugin_gid_maps: Vec::new(),
977             #[cfg(feature = "plugin")]
978             plugin_mounts: Vec::new(),
979             plugin_root: None,
980             pmem_devices: Vec::new(),
981             #[cfg(feature = "process-invariants")]
982             process_invariants_data_handle: None,
983             #[cfg(feature = "process-invariants")]
984             process_invariants_data_size: None,
985             #[cfg(windows)]
986             product_name: None,
987             protection_type: ProtectionType::Unprotected,
988             pstore: None,
989             pvclock: false,
990             pvm_fw: None,
991             restore_path: None,
992             rng: true,
993             rt_cpus: Default::default(),
994             serial_parameters: BTreeMap::new(),
995             scsis: Vec::new(),
996             #[cfg(windows)]
997             service_pipe_name: None,
998             #[cfg(any(target_os = "android", target_os = "linux"))]
999             shared_dirs: Vec::new(),
1000             #[cfg(any(feature = "slirp-ring-capture", feature = "slirp-debug"))]
1001             slirp_capture_file: None,
1002             #[cfg(target_arch = "x86_64")]
1003             smbios: SmbiosOptions::default(),
1004             #[cfg(all(windows, feature = "audio"))]
1005             snd_split_config: None,
1006             socket_path: None,
1007             #[cfg(feature = "audio")]
1008             sound: None,
1009             #[cfg(feature = "balloon")]
1010             strict_balloon: false,
1011             stub_pci_devices: Vec::new(),
1012             suspended: false,
1013             swap_dir: None,
1014             swiotlb: None,
1015             #[cfg(target_os = "android")]
1016             task_profiles: Vec::new(),
1017             #[cfg(any(target_os = "android", target_os = "linux"))]
1018             unmap_guest_memory_on_fork: false,
1019             usb: true,
1020             vcpu_affinity: None,
1021             vcpu_cgroup_path: None,
1022             vcpu_count: None,
1023             #[cfg(target_arch = "x86_64")]
1024             vcpu_hybrid_type: BTreeMap::new(),
1025             #[cfg(any(target_os = "android", target_os = "linux"))]
1026             vfio: Vec::new(),
1027             #[cfg(any(target_os = "android", target_os = "linux"))]
1028             vfio_isolate_hotplug: false,
1029             #[cfg(any(target_os = "android", target_os = "linux"))]
1030             #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
1031             vhost_scmi: false,
1032             #[cfg(any(target_os = "android", target_os = "linux"))]
1033             #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
1034             vhost_scmi_device: PathBuf::from(VHOST_SCMI_PATH),
1035             vhost_user: Vec::new(),
1036             vhost_user_fs: Vec::new(),
1037             vsock: None,
1038             #[cfg(feature = "video-decoder")]
1039             video_dec: Vec::new(),
1040             #[cfg(feature = "video-encoder")]
1041             video_enc: Vec::new(),
1042             #[cfg(all(
1043                 any(target_arch = "arm", target_arch = "aarch64"),
1044                 any(target_os = "android", target_os = "linux")
1045             ))]
1046             virt_cpufreq: false,
1047             virt_cpufreq_socket: None,
1048             virtio_input: Vec::new(),
1049             #[cfg(feature = "audio")]
1050             virtio_snds: Vec::new(),
1051             #[cfg(feature = "vtpm")]
1052             vtpm_proxy: false,
1053             wayland_socket_paths: BTreeMap::new(),
1054             #[cfg(windows)]
1055             window_procedure_thread_split_config: None,
1056             x_display: None,
1057         }
1058     }
1059 }
1060 
validate_config(cfg: &mut Config) -> std::result::Result<(), String>1061 pub fn validate_config(cfg: &mut Config) -> std::result::Result<(), String> {
1062     if cfg.executable_path.is_none() {
1063         return Err("Executable is not specified".to_string());
1064     }
1065 
1066     if cfg.plugin_root.is_some() && !executable_is_plugin(&cfg.executable_path) {
1067         return Err("`plugin-root` requires `plugin`".to_string());
1068     }
1069 
1070     #[cfg(feature = "gpu")]
1071     {
1072         crate::crosvm::gpu_config::validate_gpu_config(cfg)?;
1073     }
1074     #[cfg(feature = "gdb")]
1075     if cfg.gdb.is_some() && cfg.vcpu_count.unwrap_or(1) != 1 {
1076         return Err("`gdb` requires the number of vCPU to be 1".to_string());
1077     }
1078     if cfg.host_cpu_topology {
1079         if cfg.no_smt {
1080             return Err(
1081                 "`host-cpu-topology` cannot be set at the same time as `no_smt`, since \
1082                 the smt of the Guest is the same as that of the Host when \
1083                 `host-cpu-topology` is set."
1084                     .to_string(),
1085             );
1086         }
1087 
1088         let pcpu_count =
1089             base::number_of_logical_cores().expect("Could not read number of logical cores");
1090         if let Some(vcpu_count) = cfg.vcpu_count {
1091             if pcpu_count != vcpu_count {
1092                 return Err(format!(
1093                     "`host-cpu-topology` requires the count of vCPUs({}) to equal the \
1094                             count of CPUs({}) on host.",
1095                     vcpu_count, pcpu_count
1096                 ));
1097             }
1098         } else {
1099             cfg.vcpu_count = Some(pcpu_count);
1100         }
1101 
1102         match &cfg.vcpu_affinity {
1103             None => {
1104                 let mut affinity_map = BTreeMap::new();
1105                 for cpu_id in 0..cfg.vcpu_count.unwrap() {
1106                     affinity_map.insert(cpu_id, CpuSet::new([cpu_id]));
1107                 }
1108                 cfg.vcpu_affinity = Some(VcpuAffinity::PerVcpu(affinity_map));
1109             }
1110             _ => {
1111                 return Err(
1112                     "`host-cpu-topology` requires not to set `cpu-affinity` at the same time"
1113                         .to_string(),
1114                 );
1115             }
1116         }
1117 
1118         if !cfg.cpu_capacity.is_empty() {
1119             return Err(
1120                 "`host-cpu-topology` requires not to set `cpu-capacity` at the same time"
1121                     .to_string(),
1122             );
1123         }
1124 
1125         if !cfg.cpu_clusters.is_empty() {
1126             return Err(
1127                 "`host-cpu-topology` requires not to set `cpu clusters` at the same time"
1128                     .to_string(),
1129             );
1130         }
1131     }
1132 
1133     if cfg.boot_cpu >= cfg.vcpu_count.unwrap_or(1) {
1134         log::warn!("boot_cpu selection cannot be higher than vCPUs available, defaulting to 0");
1135         cfg.boot_cpu = 0;
1136     }
1137 
1138     #[cfg(all(
1139         any(target_arch = "arm", target_arch = "aarch64"),
1140         any(target_os = "android", target_os = "linux")
1141     ))]
1142     if cfg.virt_cpufreq {
1143         if !cfg.host_cpu_topology && (cfg.vcpu_affinity.is_none() || cfg.cpu_capacity.is_empty()) {
1144             return Err("`virt-cpufreq` requires 'host-cpu-topology' enabled or \
1145                        have vcpu_affinity and cpu_capacity configured"
1146                 .to_string());
1147         }
1148     }
1149     #[cfg(target_arch = "x86_64")]
1150     if !cfg.vcpu_hybrid_type.is_empty() {
1151         if cfg.host_cpu_topology {
1152             return Err("`core-types` cannot be set with `host-cpu-topology`.".to_string());
1153         }
1154         check_host_hybrid_support(&CpuIdCall::new(__cpuid_count, __cpuid))
1155             .map_err(|e| format!("the cpu doesn't support `core-types`: {}", e))?;
1156         if cfg.vcpu_hybrid_type.len() != cfg.vcpu_count.unwrap_or(1) {
1157             return Err("`core-types` must be set for all virtual CPUs".to_string());
1158         }
1159         for cpu_id in 0..cfg.vcpu_count.unwrap_or(1) {
1160             if !cfg.vcpu_hybrid_type.contains_key(&cpu_id) {
1161                 return Err("`core-types` must be set for all virtual CPUs".to_string());
1162             }
1163         }
1164     }
1165     #[cfg(target_arch = "x86_64")]
1166     if cfg.enable_hwp && !cfg.host_cpu_topology {
1167         return Err("setting `enable-hwp` requires `host-cpu-topology` is set.".to_string());
1168     }
1169     #[cfg(target_arch = "x86_64")]
1170     if cfg.itmt {
1171         use std::collections::BTreeSet;
1172         // ITMT only works on the case each vCPU is 1:1 mapping to a pCPU.
1173         // `host-cpu-topology` has already set this 1:1 mapping. If no
1174         // `host-cpu-topology`, we need check the cpu affinity setting.
1175         if !cfg.host_cpu_topology {
1176             // only VcpuAffinity::PerVcpu supports setting cpu affinity
1177             // for each vCPU.
1178             if let Some(VcpuAffinity::PerVcpu(v)) = &cfg.vcpu_affinity {
1179                 // ITMT allows more pCPUs than vCPUs.
1180                 if v.len() != cfg.vcpu_count.unwrap_or(1) {
1181                     return Err("`itmt` requires affinity to be set for every vCPU.".to_string());
1182                 }
1183 
1184                 let mut pcpu_set = BTreeSet::new();
1185                 for cpus in v.values() {
1186                     if cpus.len() != 1 {
1187                         return Err(
1188                             "`itmt` requires affinity to be set 1 pCPU for 1 vCPU.".to_owned()
1189                         );
1190                     }
1191                     // Ensure that each vCPU corresponds to a different pCPU to avoid pCPU sharing,
1192                     // otherwise it will seriously affect the ITMT scheduling optimization effect.
1193                     if !pcpu_set.insert(cpus[0]) {
1194                         return Err(
1195                             "`cpu_host` requires affinity to be set different pVPU for each vCPU."
1196                                 .to_owned(),
1197                         );
1198                     }
1199                 }
1200             } else {
1201                 return Err("`itmt` requires affinity to be set for every vCPU.".to_string());
1202             }
1203         }
1204         if !cfg.enable_hwp {
1205             return Err("setting `itmt` requires `enable-hwp` is set.".to_string());
1206         }
1207     }
1208 
1209     #[cfg(feature = "balloon")]
1210     {
1211         if !cfg.balloon && cfg.balloon_control.is_some() {
1212             return Err("'balloon-control' requires enabled balloon".to_string());
1213         }
1214 
1215         if !cfg.balloon && cfg.balloon_page_reporting {
1216             return Err("'balloon_page_reporting' requires enabled balloon".to_string());
1217         }
1218     }
1219 
1220     #[cfg(any(target_os = "android", target_os = "linux"))]
1221     if cfg.lock_guest_memory && cfg.jail_config.is_none() {
1222         return Err("'lock-guest-memory' and 'disable-sandbox' are mutually exclusive".to_string());
1223     }
1224 
1225     // TODO(b/253386409): Vmm-swap only support sandboxed devices until vmm-swap use
1226     // `devices::Suspendable` to suspend devices.
1227     #[cfg(feature = "swap")]
1228     if cfg.swap_dir.is_some() && cfg.jail_config.is_none() {
1229         return Err("'swap' and 'disable-sandbox' are mutually exclusive".to_string());
1230     }
1231 
1232     set_default_serial_parameters(
1233         &mut cfg.serial_parameters,
1234         cfg.vhost_user
1235             .iter()
1236             .any(|opt| opt.type_ == DeviceType::Console),
1237     );
1238 
1239     for mapping in cfg.file_backed_mappings.iter_mut() {
1240         validate_file_backed_mapping(mapping)?;
1241     }
1242 
1243     // Validate platform specific things
1244     super::sys::config::validate_config(cfg)
1245 }
1246 
validate_file_backed_mapping(mapping: &mut FileBackedMappingParameters) -> Result<(), String>1247 fn validate_file_backed_mapping(mapping: &mut FileBackedMappingParameters) -> Result<(), String> {
1248     let pagesize_mask = pagesize() as u64 - 1;
1249     let aligned_address = mapping.address & !pagesize_mask;
1250     let aligned_size =
1251         ((mapping.address + mapping.size + pagesize_mask) & !pagesize_mask) - aligned_address;
1252 
1253     if mapping.align {
1254         mapping.address = aligned_address;
1255         mapping.size = aligned_size;
1256     } else if aligned_address != mapping.address || aligned_size != mapping.size {
1257         return Err(
1258             "--file-backed-mapping addr and size parameters must be page size aligned".to_string(),
1259         );
1260     }
1261 
1262     Ok(())
1263 }
1264 
1265 #[cfg(test)]
1266 #[allow(clippy::needless_update)]
1267 mod tests {
1268     use argh::FromArgs;
1269     use devices::PciClassCode;
1270     use devices::StubPciParameters;
1271     #[cfg(target_arch = "x86_64")]
1272     use uuid::uuid;
1273 
1274     use super::*;
1275 
1276     #[test]
parse_cpu_opts()1277     fn parse_cpu_opts() {
1278         let res: CpuOptions = from_key_values("").unwrap();
1279         assert_eq!(res, CpuOptions::default());
1280 
1281         // num_cores
1282         let res: CpuOptions = from_key_values("12").unwrap();
1283         assert_eq!(
1284             res,
1285             CpuOptions {
1286                 num_cores: Some(12),
1287                 ..Default::default()
1288             }
1289         );
1290 
1291         let res: CpuOptions = from_key_values("num-cores=16").unwrap();
1292         assert_eq!(
1293             res,
1294             CpuOptions {
1295                 num_cores: Some(16),
1296                 ..Default::default()
1297             }
1298         );
1299 
1300         // clusters
1301         let res: CpuOptions = from_key_values("clusters=[[0],[1],[2],[3]]").unwrap();
1302         assert_eq!(
1303             res,
1304             CpuOptions {
1305                 clusters: vec![
1306                     CpuSet::new([0]),
1307                     CpuSet::new([1]),
1308                     CpuSet::new([2]),
1309                     CpuSet::new([3])
1310                 ],
1311                 ..Default::default()
1312             }
1313         );
1314 
1315         let res: CpuOptions = from_key_values("clusters=[[0-3]]").unwrap();
1316         assert_eq!(
1317             res,
1318             CpuOptions {
1319                 clusters: vec![CpuSet::new([0, 1, 2, 3])],
1320                 ..Default::default()
1321             }
1322         );
1323 
1324         let res: CpuOptions = from_key_values("clusters=[[0,2],[1,3],[4-7,12]]").unwrap();
1325         assert_eq!(
1326             res,
1327             CpuOptions {
1328                 clusters: vec![
1329                     CpuSet::new([0, 2]),
1330                     CpuSet::new([1, 3]),
1331                     CpuSet::new([4, 5, 6, 7, 12])
1332                 ],
1333                 ..Default::default()
1334             }
1335         );
1336 
1337         #[cfg(target_arch = "x86_64")]
1338         {
1339             let res: CpuOptions = from_key_values("core-types=[atom=[1,3-7],core=[0,2]]").unwrap();
1340             assert_eq!(
1341                 res,
1342                 CpuOptions {
1343                     core_types: Some(CpuCoreType {
1344                         atom: CpuSet::new([1, 3, 4, 5, 6, 7]),
1345                         core: CpuSet::new([0, 2])
1346                     }),
1347                     ..Default::default()
1348                 }
1349             );
1350         }
1351 
1352         // All together
1353         let res: CpuOptions = from_key_values("16,clusters=[[0],[4-6],[7]]").unwrap();
1354         assert_eq!(
1355             res,
1356             CpuOptions {
1357                 num_cores: Some(16),
1358                 clusters: vec![CpuSet::new([0]), CpuSet::new([4, 5, 6]), CpuSet::new([7])],
1359                 ..Default::default()
1360             }
1361         );
1362 
1363         let res: CpuOptions = from_key_values("clusters=[[0-7],[30-31]],num-cores=32").unwrap();
1364         assert_eq!(
1365             res,
1366             CpuOptions {
1367                 num_cores: Some(32),
1368                 clusters: vec![CpuSet::new([0, 1, 2, 3, 4, 5, 6, 7]), CpuSet::new([30, 31])],
1369                 ..Default::default()
1370             }
1371         );
1372     }
1373 
1374     #[test]
parse_cpu_set_single()1375     fn parse_cpu_set_single() {
1376         assert_eq!(
1377             CpuSet::from_str("123").expect("parse failed"),
1378             CpuSet::new([123])
1379         );
1380     }
1381 
1382     #[test]
parse_cpu_set_list()1383     fn parse_cpu_set_list() {
1384         assert_eq!(
1385             CpuSet::from_str("0,1,2,3").expect("parse failed"),
1386             CpuSet::new([0, 1, 2, 3])
1387         );
1388     }
1389 
1390     #[test]
parse_cpu_set_range()1391     fn parse_cpu_set_range() {
1392         assert_eq!(
1393             CpuSet::from_str("0-3").expect("parse failed"),
1394             CpuSet::new([0, 1, 2, 3])
1395         );
1396     }
1397 
1398     #[test]
parse_cpu_set_list_of_ranges()1399     fn parse_cpu_set_list_of_ranges() {
1400         assert_eq!(
1401             CpuSet::from_str("3-4,7-9,18").expect("parse failed"),
1402             CpuSet::new([3, 4, 7, 8, 9, 18])
1403         );
1404     }
1405 
1406     #[test]
parse_cpu_set_repeated()1407     fn parse_cpu_set_repeated() {
1408         // For now, allow duplicates - they will be handled gracefully by the vec to cpu_set_t
1409         // conversion.
1410         assert_eq!(
1411             CpuSet::from_str("1,1,1").expect("parse failed"),
1412             CpuSet::new([1, 1, 1])
1413         );
1414     }
1415 
1416     #[test]
parse_cpu_set_negative()1417     fn parse_cpu_set_negative() {
1418         // Negative CPU numbers are not allowed.
1419         CpuSet::from_str("-3").expect_err("parse should have failed");
1420     }
1421 
1422     #[test]
parse_cpu_set_reverse_range()1423     fn parse_cpu_set_reverse_range() {
1424         // Ranges must be from low to high.
1425         CpuSet::from_str("5-2").expect_err("parse should have failed");
1426     }
1427 
1428     #[test]
parse_cpu_set_open_range()1429     fn parse_cpu_set_open_range() {
1430         CpuSet::from_str("3-").expect_err("parse should have failed");
1431     }
1432 
1433     #[test]
parse_cpu_set_extra_comma()1434     fn parse_cpu_set_extra_comma() {
1435         CpuSet::from_str("0,1,2,").expect_err("parse should have failed");
1436     }
1437 
1438     #[test]
parse_cpu_affinity_global()1439     fn parse_cpu_affinity_global() {
1440         assert_eq!(
1441             parse_cpu_affinity("0,5-7,9").expect("parse failed"),
1442             VcpuAffinity::Global(CpuSet::new([0, 5, 6, 7, 9])),
1443         );
1444     }
1445 
1446     #[test]
parse_cpu_affinity_per_vcpu_one_to_one()1447     fn parse_cpu_affinity_per_vcpu_one_to_one() {
1448         let mut expected_map = BTreeMap::new();
1449         expected_map.insert(0, CpuSet::new([0]));
1450         expected_map.insert(1, CpuSet::new([1]));
1451         expected_map.insert(2, CpuSet::new([2]));
1452         expected_map.insert(3, CpuSet::new([3]));
1453         assert_eq!(
1454             parse_cpu_affinity("0=0:1=1:2=2:3=3").expect("parse failed"),
1455             VcpuAffinity::PerVcpu(expected_map),
1456         );
1457     }
1458 
1459     #[test]
parse_cpu_affinity_per_vcpu_sets()1460     fn parse_cpu_affinity_per_vcpu_sets() {
1461         let mut expected_map = BTreeMap::new();
1462         expected_map.insert(0, CpuSet::new([0, 1, 2]));
1463         expected_map.insert(1, CpuSet::new([3, 4, 5]));
1464         expected_map.insert(2, CpuSet::new([6, 7, 8]));
1465         assert_eq!(
1466             parse_cpu_affinity("0=0,1,2:1=3-5:2=6,7-8").expect("parse failed"),
1467             VcpuAffinity::PerVcpu(expected_map),
1468         );
1469     }
1470 
1471     #[test]
parse_mem_opts()1472     fn parse_mem_opts() {
1473         let res: MemOptions = from_key_values("").unwrap();
1474         assert_eq!(res.size, None);
1475 
1476         let res: MemOptions = from_key_values("1024").unwrap();
1477         assert_eq!(res.size, Some(1024));
1478 
1479         let res: MemOptions = from_key_values("size=0x4000").unwrap();
1480         assert_eq!(res.size, Some(16384));
1481     }
1482 
1483     #[test]
parse_serial_vaild()1484     fn parse_serial_vaild() {
1485         parse_serial_options("type=syslog,num=1,console=true,stdin=true")
1486             .expect("parse should have succeded");
1487     }
1488 
1489     #[test]
parse_serial_virtio_console_vaild()1490     fn parse_serial_virtio_console_vaild() {
1491         parse_serial_options("type=syslog,num=5,console=true,stdin=true,hardware=virtio-console")
1492             .expect("parse should have succeded");
1493     }
1494 
1495     #[test]
parse_serial_valid_no_num()1496     fn parse_serial_valid_no_num() {
1497         parse_serial_options("type=syslog").expect("parse should have succeded");
1498     }
1499 
1500     #[test]
parse_serial_equals_in_value()1501     fn parse_serial_equals_in_value() {
1502         let parsed = parse_serial_options("type=syslog,path=foo=bar==.log")
1503             .expect("parse should have succeded");
1504         assert_eq!(parsed.path, Some(PathBuf::from("foo=bar==.log")));
1505     }
1506 
1507     #[test]
parse_serial_invalid_type()1508     fn parse_serial_invalid_type() {
1509         parse_serial_options("type=wormhole,num=1").expect_err("parse should have failed");
1510     }
1511 
1512     #[test]
parse_serial_invalid_num_upper()1513     fn parse_serial_invalid_num_upper() {
1514         parse_serial_options("type=syslog,num=5").expect_err("parse should have failed");
1515     }
1516 
1517     #[test]
parse_serial_invalid_num_lower()1518     fn parse_serial_invalid_num_lower() {
1519         parse_serial_options("type=syslog,num=0").expect_err("parse should have failed");
1520     }
1521 
1522     #[test]
parse_serial_virtio_console_invalid_num_lower()1523     fn parse_serial_virtio_console_invalid_num_lower() {
1524         parse_serial_options("type=syslog,hardware=virtio-console,num=0")
1525             .expect_err("parse should have failed");
1526     }
1527 
1528     #[test]
parse_serial_invalid_num_string()1529     fn parse_serial_invalid_num_string() {
1530         parse_serial_options("type=syslog,num=number3").expect_err("parse should have failed");
1531     }
1532 
1533     #[test]
parse_serial_invalid_option()1534     fn parse_serial_invalid_option() {
1535         parse_serial_options("type=syslog,speed=lightspeed").expect_err("parse should have failed");
1536     }
1537 
1538     #[test]
parse_serial_invalid_two_stdin()1539     fn parse_serial_invalid_two_stdin() {
1540         assert!(TryInto::<Config>::try_into(
1541             crate::crosvm::cmdline::RunCommand::from_args(
1542                 &[],
1543                 &[
1544                     "--serial",
1545                     "num=1,type=stdout,stdin=true",
1546                     "--serial",
1547                     "num=2,type=stdout,stdin=true"
1548                 ]
1549             )
1550             .unwrap()
1551         )
1552         .is_err())
1553     }
1554 
1555     #[test]
parse_serial_pci_address_valid_for_virtio()1556     fn parse_serial_pci_address_valid_for_virtio() {
1557         let parsed =
1558             parse_serial_options("type=syslog,hardware=virtio-console,pci-address=00:0e.0")
1559                 .expect("parse should have succeded");
1560         assert_eq!(
1561             parsed.pci_address,
1562             Some(PciAddress {
1563                 bus: 0,
1564                 dev: 14,
1565                 func: 0
1566             })
1567         );
1568     }
1569 
1570     #[test]
parse_serial_pci_address_valid_for_legacy_virtio()1571     fn parse_serial_pci_address_valid_for_legacy_virtio() {
1572         let parsed =
1573             parse_serial_options("type=syslog,hardware=legacy-virtio-console,pci-address=00:0e.0")
1574                 .expect("parse should have succeded");
1575         assert_eq!(
1576             parsed.pci_address,
1577             Some(PciAddress {
1578                 bus: 0,
1579                 dev: 14,
1580                 func: 0
1581             })
1582         );
1583     }
1584 
1585     #[test]
parse_serial_pci_address_failed_for_serial()1586     fn parse_serial_pci_address_failed_for_serial() {
1587         parse_serial_options("type=syslog,hardware=serial,pci-address=00:0e.0")
1588             .expect_err("expected pci-address error for serial hardware");
1589     }
1590 
1591     #[test]
parse_serial_pci_address_failed_for_debugcon()1592     fn parse_serial_pci_address_failed_for_debugcon() {
1593         parse_serial_options("type=syslog,hardware=debugcon,pci-address=00:0e.0")
1594             .expect_err("expected pci-address error for debugcon hardware");
1595     }
1596 
1597     #[test]
parse_battery_valid()1598     fn parse_battery_valid() {
1599         let bat_config: BatteryConfig = from_key_values("type=goldfish").unwrap();
1600         assert_eq!(bat_config.type_, BatteryType::Goldfish);
1601     }
1602 
1603     #[test]
parse_battery_valid_no_type()1604     fn parse_battery_valid_no_type() {
1605         let bat_config: BatteryConfig = from_key_values("").unwrap();
1606         assert_eq!(bat_config.type_, BatteryType::Goldfish);
1607     }
1608 
1609     #[test]
parse_battery_invalid_parameter()1610     fn parse_battery_invalid_parameter() {
1611         from_key_values::<BatteryConfig>("tyep=goldfish").expect_err("parse should have failed");
1612     }
1613 
1614     #[test]
parse_battery_invalid_type_value()1615     fn parse_battery_invalid_type_value() {
1616         from_key_values::<BatteryConfig>("type=xxx").expect_err("parse should have failed");
1617     }
1618 
1619     #[test]
parse_irqchip_kernel()1620     fn parse_irqchip_kernel() {
1621         let cfg = TryInto::<Config>::try_into(
1622             crate::crosvm::cmdline::RunCommand::from_args(
1623                 &[],
1624                 &["--irqchip", "kernel", "/dev/null"],
1625             )
1626             .unwrap(),
1627         )
1628         .unwrap();
1629 
1630         assert_eq!(cfg.irq_chip, Some(IrqChipKind::Kernel));
1631     }
1632 
1633     #[test]
parse_irqchip_split()1634     fn parse_irqchip_split() {
1635         let cfg = TryInto::<Config>::try_into(
1636             crate::crosvm::cmdline::RunCommand::from_args(
1637                 &[],
1638                 &["--irqchip", "split", "/dev/null"],
1639             )
1640             .unwrap(),
1641         )
1642         .unwrap();
1643 
1644         assert_eq!(cfg.irq_chip, Some(IrqChipKind::Split));
1645     }
1646 
1647     #[test]
parse_irqchip_userspace()1648     fn parse_irqchip_userspace() {
1649         let cfg = TryInto::<Config>::try_into(
1650             crate::crosvm::cmdline::RunCommand::from_args(
1651                 &[],
1652                 &["--irqchip", "userspace", "/dev/null"],
1653             )
1654             .unwrap(),
1655         )
1656         .unwrap();
1657 
1658         assert_eq!(cfg.irq_chip, Some(IrqChipKind::Userspace));
1659     }
1660 
1661     #[test]
parse_stub_pci()1662     fn parse_stub_pci() {
1663         let params = from_key_values::<StubPciParameters>("0000:01:02.3,vendor=0xfffe,device=0xfffd,class=0xffc1c2,subsystem_vendor=0xfffc,subsystem_device=0xfffb,revision=0xa").unwrap();
1664         assert_eq!(params.address.bus, 1);
1665         assert_eq!(params.address.dev, 2);
1666         assert_eq!(params.address.func, 3);
1667         assert_eq!(params.vendor, 0xfffe);
1668         assert_eq!(params.device, 0xfffd);
1669         assert_eq!(params.class.class as u8, PciClassCode::Other as u8);
1670         assert_eq!(params.class.subclass, 0xc1);
1671         assert_eq!(params.class.programming_interface, 0xc2);
1672         assert_eq!(params.subsystem_vendor, 0xfffc);
1673         assert_eq!(params.subsystem_device, 0xfffb);
1674         assert_eq!(params.revision, 0xa);
1675     }
1676 
1677     #[test]
parse_file_backed_mapping_valid()1678     fn parse_file_backed_mapping_valid() {
1679         let params = from_key_values::<FileBackedMappingParameters>(
1680             "addr=0x1000,size=0x2000,path=/dev/mem,offset=0x3000,rw,sync",
1681         )
1682         .unwrap();
1683         assert_eq!(params.address, 0x1000);
1684         assert_eq!(params.size, 0x2000);
1685         assert_eq!(params.path, PathBuf::from("/dev/mem"));
1686         assert_eq!(params.offset, 0x3000);
1687         assert!(params.writable);
1688         assert!(params.sync);
1689     }
1690 
1691     #[test]
parse_file_backed_mapping_incomplete()1692     fn parse_file_backed_mapping_incomplete() {
1693         assert!(
1694             from_key_values::<FileBackedMappingParameters>("addr=0x1000,size=0x2000")
1695                 .unwrap_err()
1696                 .contains("missing field `path`")
1697         );
1698         assert!(
1699             from_key_values::<FileBackedMappingParameters>("size=0x2000,path=/dev/mem")
1700                 .unwrap_err()
1701                 .contains("missing field `addr`")
1702         );
1703         assert!(
1704             from_key_values::<FileBackedMappingParameters>("addr=0x1000,path=/dev/mem")
1705                 .unwrap_err()
1706                 .contains("missing field `size`")
1707         );
1708     }
1709 
1710     #[test]
parse_file_backed_mapping_unaligned_addr()1711     fn parse_file_backed_mapping_unaligned_addr() {
1712         let mut params =
1713             from_key_values::<FileBackedMappingParameters>("addr=0x1001,size=0x2000,path=/dev/mem")
1714                 .unwrap();
1715         assert!(validate_file_backed_mapping(&mut params)
1716             .unwrap_err()
1717             .contains("aligned"));
1718     }
1719     #[test]
parse_file_backed_mapping_unaligned_size()1720     fn parse_file_backed_mapping_unaligned_size() {
1721         let mut params =
1722             from_key_values::<FileBackedMappingParameters>("addr=0x1000,size=0x2001,path=/dev/mem")
1723                 .unwrap();
1724         assert!(validate_file_backed_mapping(&mut params)
1725             .unwrap_err()
1726             .contains("aligned"));
1727     }
1728 
1729     #[test]
parse_file_backed_mapping_align()1730     fn parse_file_backed_mapping_align() {
1731         let addr = pagesize() as u64 * 3 + 42;
1732         let size = pagesize() as u64 - 0xf;
1733         let mut params = from_key_values::<FileBackedMappingParameters>(&format!(
1734             "addr={addr},size={size},path=/dev/mem,align",
1735         ))
1736         .unwrap();
1737         assert_eq!(params.address, addr);
1738         assert_eq!(params.size, size);
1739         validate_file_backed_mapping(&mut params).unwrap();
1740         assert_eq!(params.address, pagesize() as u64 * 3);
1741         assert_eq!(params.size, pagesize() as u64 * 2);
1742     }
1743 
1744     #[test]
parse_fw_cfg_valid_path()1745     fn parse_fw_cfg_valid_path() {
1746         let cfg = TryInto::<Config>::try_into(
1747             crate::crosvm::cmdline::RunCommand::from_args(
1748                 &[],
1749                 &["--fw-cfg", "name=bar,path=data.bin", "/dev/null"],
1750             )
1751             .unwrap(),
1752         )
1753         .unwrap();
1754 
1755         assert_eq!(cfg.fw_cfg_parameters.len(), 1);
1756         assert_eq!(cfg.fw_cfg_parameters[0].name, "bar".to_string());
1757         assert_eq!(cfg.fw_cfg_parameters[0].string, None);
1758         assert_eq!(cfg.fw_cfg_parameters[0].path, Some("data.bin".into()));
1759     }
1760 
1761     #[test]
parse_fw_cfg_valid_string()1762     fn parse_fw_cfg_valid_string() {
1763         let cfg = TryInto::<Config>::try_into(
1764             crate::crosvm::cmdline::RunCommand::from_args(
1765                 &[],
1766                 &["--fw-cfg", "name=bar,string=foo", "/dev/null"],
1767             )
1768             .unwrap(),
1769         )
1770         .unwrap();
1771 
1772         assert_eq!(cfg.fw_cfg_parameters.len(), 1);
1773         assert_eq!(cfg.fw_cfg_parameters[0].name, "bar".to_string());
1774         assert_eq!(cfg.fw_cfg_parameters[0].string, Some("foo".to_string()));
1775         assert_eq!(cfg.fw_cfg_parameters[0].path, None);
1776     }
1777 
1778     #[test]
parse_dtbo()1779     fn parse_dtbo() {
1780         let cfg: Config = crate::crosvm::cmdline::RunCommand::from_args(
1781             &[],
1782             &[
1783                 "--device-tree-overlay",
1784                 "/path/to/dtbo1",
1785                 "--device-tree-overlay",
1786                 "/path/to/dtbo2",
1787                 "/dev/null",
1788             ],
1789         )
1790         .unwrap()
1791         .try_into()
1792         .unwrap();
1793 
1794         assert_eq!(cfg.device_tree_overlay.len(), 2);
1795         for (opt, p) in cfg
1796             .device_tree_overlay
1797             .into_iter()
1798             .zip(["/path/to/dtbo1", "/path/to/dtbo2"])
1799         {
1800             assert_eq!(opt.path, PathBuf::from(p));
1801             assert!(!opt.filter_devs);
1802         }
1803     }
1804 
1805     #[test]
1806     #[cfg(any(target_os = "android", target_os = "linux"))]
parse_dtbo_filtered()1807     fn parse_dtbo_filtered() {
1808         let cfg: Config = crate::crosvm::cmdline::RunCommand::from_args(
1809             &[],
1810             &[
1811                 "--vfio",
1812                 "/path/to/dev,dt-symbol=mydev",
1813                 "--device-tree-overlay",
1814                 "/path/to/dtbo1,filter",
1815                 "--device-tree-overlay",
1816                 "/path/to/dtbo2,filter",
1817                 "/dev/null",
1818             ],
1819         )
1820         .unwrap()
1821         .try_into()
1822         .unwrap();
1823 
1824         assert_eq!(cfg.device_tree_overlay.len(), 2);
1825         for (opt, p) in cfg
1826             .device_tree_overlay
1827             .into_iter()
1828             .zip(["/path/to/dtbo1", "/path/to/dtbo2"])
1829         {
1830             assert_eq!(opt.path, PathBuf::from(p));
1831             assert!(opt.filter_devs);
1832         }
1833 
1834         assert!(TryInto::<Config>::try_into(
1835             crate::crosvm::cmdline::RunCommand::from_args(
1836                 &[],
1837                 &["--device-tree-overlay", "/path/to/dtbo,filter", "/dev/null"],
1838             )
1839             .unwrap(),
1840         )
1841         .is_err());
1842     }
1843 
1844     #[test]
parse_fw_cfg_invalid_no_name()1845     fn parse_fw_cfg_invalid_no_name() {
1846         assert!(
1847             crate::crosvm::cmdline::RunCommand::from_args(&[], &["--fw-cfg", "string=foo",])
1848                 .is_err()
1849         );
1850     }
1851 
1852     #[cfg(any(feature = "video-decoder", feature = "video-encoder"))]
1853     #[test]
parse_video()1854     fn parse_video() {
1855         use devices::virtio::device_constants::video::VideoBackendType;
1856 
1857         #[cfg(feature = "libvda")]
1858         {
1859             let params: VideoDeviceConfig = from_key_values("libvda").unwrap();
1860             assert_eq!(params.backend, VideoBackendType::Libvda);
1861 
1862             let params: VideoDeviceConfig = from_key_values("libvda-vd").unwrap();
1863             assert_eq!(params.backend, VideoBackendType::LibvdaVd);
1864         }
1865 
1866         #[cfg(feature = "ffmpeg")]
1867         {
1868             let params: VideoDeviceConfig = from_key_values("ffmpeg").unwrap();
1869             assert_eq!(params.backend, VideoBackendType::Ffmpeg);
1870         }
1871 
1872         #[cfg(feature = "vaapi")]
1873         {
1874             let params: VideoDeviceConfig = from_key_values("vaapi").unwrap();
1875             assert_eq!(params.backend, VideoBackendType::Vaapi);
1876         }
1877     }
1878 
1879     #[test]
parse_vhost_user_option()1880     fn parse_vhost_user_option() {
1881         let opt: VhostUserOption = from_key_values("/10mm").unwrap();
1882         assert_eq!(opt.socket.to_str(), Some("/10mm"));
1883         assert_eq!(opt.max_queue_size, None);
1884 
1885         let opt: VhostUserOption = from_key_values("/10mm,max-queue-size=256").unwrap();
1886         assert_eq!(opt.socket.to_str(), Some("/10mm"));
1887         assert_eq!(opt.max_queue_size, Some(256));
1888     }
1889 
1890     #[test]
parse_vhost_user_option_all_device_types()1891     fn parse_vhost_user_option_all_device_types() {
1892         fn test_device_type(type_string: &str, type_: DeviceType) {
1893             let vhost_user_arg = format!("{},socket=sock", type_string);
1894 
1895             let cfg = TryInto::<Config>::try_into(
1896                 crate::crosvm::cmdline::RunCommand::from_args(
1897                     &[],
1898                     &["--vhost-user", &vhost_user_arg, "/dev/null"],
1899                 )
1900                 .unwrap(),
1901             )
1902             .unwrap();
1903 
1904             assert_eq!(cfg.vhost_user.len(), 1);
1905             let vu = &cfg.vhost_user[0];
1906             assert_eq!(vu.type_, type_);
1907         }
1908 
1909         test_device_type("net", DeviceType::Net);
1910         test_device_type("block", DeviceType::Block);
1911         test_device_type("console", DeviceType::Console);
1912         test_device_type("rng", DeviceType::Rng);
1913         test_device_type("balloon", DeviceType::Balloon);
1914         test_device_type("scsi", DeviceType::Scsi);
1915         test_device_type("9p", DeviceType::P9);
1916         test_device_type("gpu", DeviceType::Gpu);
1917         test_device_type("input", DeviceType::Input);
1918         test_device_type("vsock", DeviceType::Vsock);
1919         test_device_type("iommu", DeviceType::Iommu);
1920         test_device_type("sound", DeviceType::Sound);
1921         test_device_type("fs", DeviceType::Fs);
1922         test_device_type("pmem", DeviceType::Pmem);
1923         test_device_type("mac80211-hwsim", DeviceType::Mac80211HwSim);
1924         test_device_type("video-encoder", DeviceType::VideoEncoder);
1925         test_device_type("video-decoder", DeviceType::VideoDecoder);
1926         test_device_type("scmi", DeviceType::Scmi);
1927         test_device_type("wl", DeviceType::Wl);
1928         test_device_type("tpm", DeviceType::Tpm);
1929         test_device_type("pvclock", DeviceType::Pvclock);
1930     }
1931 
1932     #[test]
parse_vhost_user_fs_deprecated()1933     fn parse_vhost_user_fs_deprecated() {
1934         let cfg = TryInto::<Config>::try_into(
1935             crate::crosvm::cmdline::RunCommand::from_args(
1936                 &[],
1937                 &["--vhost-user-fs", "my_socket:my_tag", "/dev/null"],
1938             )
1939             .unwrap(),
1940         )
1941         .unwrap();
1942 
1943         assert_eq!(cfg.vhost_user_fs.len(), 1);
1944         let fs = &cfg.vhost_user_fs[0];
1945         assert_eq!(fs.socket.to_str(), Some("my_socket"));
1946         assert_eq!(fs.tag, Some("my_tag".to_string()));
1947         assert_eq!(fs.max_queue_size, None);
1948     }
1949 
1950     #[test]
parse_vhost_user_fs()1951     fn parse_vhost_user_fs() {
1952         let cfg = TryInto::<Config>::try_into(
1953             crate::crosvm::cmdline::RunCommand::from_args(
1954                 &[],
1955                 &["--vhost-user-fs", "my_socket,tag=my_tag", "/dev/null"],
1956             )
1957             .unwrap(),
1958         )
1959         .unwrap();
1960 
1961         assert_eq!(cfg.vhost_user_fs.len(), 1);
1962         let fs = &cfg.vhost_user_fs[0];
1963         assert_eq!(fs.socket.to_str(), Some("my_socket"));
1964         assert_eq!(fs.tag, Some("my_tag".to_string()));
1965         assert_eq!(fs.max_queue_size, None);
1966     }
1967 
1968     #[test]
parse_vhost_user_fs_max_queue_size()1969     fn parse_vhost_user_fs_max_queue_size() {
1970         let cfg = TryInto::<Config>::try_into(
1971             crate::crosvm::cmdline::RunCommand::from_args(
1972                 &[],
1973                 &[
1974                     "--vhost-user-fs",
1975                     "my_socket,tag=my_tag,max-queue-size=256",
1976                     "/dev/null",
1977                 ],
1978             )
1979             .unwrap(),
1980         )
1981         .unwrap();
1982 
1983         assert_eq!(cfg.vhost_user_fs.len(), 1);
1984         let fs = &cfg.vhost_user_fs[0];
1985         assert_eq!(fs.socket.to_str(), Some("my_socket"));
1986         assert_eq!(fs.tag, Some("my_tag".to_string()));
1987         assert_eq!(fs.max_queue_size, Some(256));
1988     }
1989 
1990     #[test]
parse_vhost_user_fs_no_tag()1991     fn parse_vhost_user_fs_no_tag() {
1992         let cfg = TryInto::<Config>::try_into(
1993             crate::crosvm::cmdline::RunCommand::from_args(
1994                 &[],
1995                 &["--vhost-user-fs", "my_socket", "/dev/null"],
1996             )
1997             .unwrap(),
1998         )
1999         .unwrap();
2000 
2001         assert_eq!(cfg.vhost_user_fs.len(), 1);
2002         let fs = &cfg.vhost_user_fs[0];
2003         assert_eq!(fs.socket.to_str(), Some("my_socket"));
2004         assert_eq!(fs.tag, None);
2005         assert_eq!(fs.max_queue_size, None);
2006     }
2007 
2008     #[cfg(target_arch = "x86_64")]
2009     #[test]
parse_smbios_uuid()2010     fn parse_smbios_uuid() {
2011         let opt: SmbiosOptions =
2012             from_key_values("uuid=12e474af-2cc1-49d1-b0e5-d03a3e03ca03").unwrap();
2013         assert_eq!(
2014             opt.uuid,
2015             Some(uuid!("12e474af-2cc1-49d1-b0e5-d03a3e03ca03"))
2016         );
2017 
2018         from_key_values::<SmbiosOptions>("uuid=zzzz").expect_err("expected error parsing uuid");
2019     }
2020 
2021     #[test]
parse_touch_legacy()2022     fn parse_touch_legacy() {
2023         let cfg = TryInto::<Config>::try_into(
2024             crate::crosvm::cmdline::RunCommand::from_args(
2025                 &[],
2026                 &["--multi-touch", "my_socket:867:5309", "bzImage"],
2027             )
2028             .unwrap(),
2029         )
2030         .unwrap();
2031 
2032         assert_eq!(cfg.virtio_input.len(), 1);
2033         let multi_touch = cfg
2034             .virtio_input
2035             .iter()
2036             .find(|input| matches!(input, InputDeviceOption::MultiTouch { .. }))
2037             .unwrap();
2038         assert_eq!(
2039             *multi_touch,
2040             InputDeviceOption::MultiTouch {
2041                 path: PathBuf::from("my_socket"),
2042                 width: Some(867),
2043                 height: Some(5309),
2044                 name: None
2045             }
2046         );
2047     }
2048 
2049     #[test]
parse_touch()2050     fn parse_touch() {
2051         let cfg = TryInto::<Config>::try_into(
2052             crate::crosvm::cmdline::RunCommand::from_args(
2053                 &[],
2054                 &["--multi-touch", r"C:\path,width=867,height=5309", "bzImage"],
2055             )
2056             .unwrap(),
2057         )
2058         .unwrap();
2059 
2060         assert_eq!(cfg.virtio_input.len(), 1);
2061         let multi_touch = cfg
2062             .virtio_input
2063             .iter()
2064             .find(|input| matches!(input, InputDeviceOption::MultiTouch { .. }))
2065             .unwrap();
2066         assert_eq!(
2067             *multi_touch,
2068             InputDeviceOption::MultiTouch {
2069                 path: PathBuf::from(r"C:\path"),
2070                 width: Some(867),
2071                 height: Some(5309),
2072                 name: None
2073             }
2074         );
2075     }
2076 
2077     #[test]
single_touch_spec_and_track_pad_spec_default_size()2078     fn single_touch_spec_and_track_pad_spec_default_size() {
2079         let config: Config = crate::crosvm::cmdline::RunCommand::from_args(
2080             &[],
2081             &[
2082                 "--single-touch",
2083                 "/dev/single-touch-test",
2084                 "--trackpad",
2085                 "/dev/single-touch-test",
2086                 "/dev/null",
2087             ],
2088         )
2089         .unwrap()
2090         .try_into()
2091         .unwrap();
2092 
2093         let single_touch = config
2094             .virtio_input
2095             .iter()
2096             .find(|input| matches!(input, InputDeviceOption::SingleTouch { .. }))
2097             .unwrap();
2098         let trackpad = config
2099             .virtio_input
2100             .iter()
2101             .find(|input| matches!(input, InputDeviceOption::Trackpad { .. }))
2102             .unwrap();
2103 
2104         assert_eq!(
2105             *single_touch,
2106             InputDeviceOption::SingleTouch {
2107                 path: PathBuf::from("/dev/single-touch-test"),
2108                 width: None,
2109                 height: None,
2110                 name: None
2111             }
2112         );
2113         assert_eq!(
2114             *trackpad,
2115             InputDeviceOption::Trackpad {
2116                 path: PathBuf::from("/dev/single-touch-test"),
2117                 width: None,
2118                 height: None,
2119                 name: None
2120             }
2121         );
2122     }
2123 
2124     #[cfg(feature = "gpu")]
2125     #[test]
single_touch_spec_default_size_from_gpu()2126     fn single_touch_spec_default_size_from_gpu() {
2127         let config: Config = crate::crosvm::cmdline::RunCommand::from_args(
2128             &[],
2129             &[
2130                 "--single-touch",
2131                 "/dev/single-touch-test",
2132                 "--gpu",
2133                 "width=1024,height=768",
2134                 "/dev/null",
2135             ],
2136         )
2137         .unwrap()
2138         .try_into()
2139         .unwrap();
2140 
2141         let single_touch = config
2142             .virtio_input
2143             .iter()
2144             .find(|input| matches!(input, InputDeviceOption::SingleTouch { .. }))
2145             .unwrap();
2146         assert_eq!(
2147             *single_touch,
2148             InputDeviceOption::SingleTouch {
2149                 path: PathBuf::from("/dev/single-touch-test"),
2150                 width: None,
2151                 height: None,
2152                 name: None
2153             }
2154         );
2155 
2156         assert_eq!(config.display_input_width, Some(1024));
2157         assert_eq!(config.display_input_height, Some(768));
2158     }
2159 
2160     #[test]
single_touch_spec_and_track_pad_spec_with_size()2161     fn single_touch_spec_and_track_pad_spec_with_size() {
2162         let config: Config = crate::crosvm::cmdline::RunCommand::from_args(
2163             &[],
2164             &[
2165                 "--single-touch",
2166                 "/dev/single-touch-test:12345:54321",
2167                 "--trackpad",
2168                 "/dev/single-touch-test:5678:9876",
2169                 "/dev/null",
2170             ],
2171         )
2172         .unwrap()
2173         .try_into()
2174         .unwrap();
2175 
2176         let single_touch = config
2177             .virtio_input
2178             .iter()
2179             .find(|input| matches!(input, InputDeviceOption::SingleTouch { .. }))
2180             .unwrap();
2181         let trackpad = config
2182             .virtio_input
2183             .iter()
2184             .find(|input| matches!(input, InputDeviceOption::Trackpad { .. }))
2185             .unwrap();
2186 
2187         assert_eq!(
2188             *single_touch,
2189             InputDeviceOption::SingleTouch {
2190                 path: PathBuf::from("/dev/single-touch-test"),
2191                 width: Some(12345),
2192                 height: Some(54321),
2193                 name: None
2194             }
2195         );
2196         assert_eq!(
2197             *trackpad,
2198             InputDeviceOption::Trackpad {
2199                 path: PathBuf::from("/dev/single-touch-test"),
2200                 width: Some(5678),
2201                 height: Some(9876),
2202                 name: None
2203             }
2204         );
2205     }
2206 
2207     #[cfg(feature = "gpu")]
2208     #[test]
single_touch_spec_with_size_independent_from_gpu()2209     fn single_touch_spec_with_size_independent_from_gpu() {
2210         let config: Config = crate::crosvm::cmdline::RunCommand::from_args(
2211             &[],
2212             &[
2213                 "--single-touch",
2214                 "/dev/single-touch-test:12345:54321",
2215                 "--gpu",
2216                 "width=1024,height=768",
2217                 "/dev/null",
2218             ],
2219         )
2220         .unwrap()
2221         .try_into()
2222         .unwrap();
2223 
2224         let single_touch = config
2225             .virtio_input
2226             .iter()
2227             .find(|input| matches!(input, InputDeviceOption::SingleTouch { .. }))
2228             .unwrap();
2229 
2230         assert_eq!(
2231             *single_touch,
2232             InputDeviceOption::SingleTouch {
2233                 path: PathBuf::from("/dev/single-touch-test"),
2234                 width: Some(12345),
2235                 height: Some(54321),
2236                 name: None
2237             }
2238         );
2239 
2240         assert_eq!(config.display_input_width, Some(1024));
2241         assert_eq!(config.display_input_height, Some(768));
2242     }
2243 
2244     #[test]
virtio_switches()2245     fn virtio_switches() {
2246         let config: Config = crate::crosvm::cmdline::RunCommand::from_args(
2247             &[],
2248             &["--switches", "/dev/switches-test", "/dev/null"],
2249         )
2250         .unwrap()
2251         .try_into()
2252         .unwrap();
2253 
2254         let switches = config
2255             .virtio_input
2256             .iter()
2257             .find(|input| matches!(input, InputDeviceOption::Switches { .. }))
2258             .unwrap();
2259 
2260         assert_eq!(
2261             *switches,
2262             InputDeviceOption::Switches {
2263                 path: PathBuf::from("/dev/switches-test")
2264             }
2265         );
2266     }
2267 
2268     #[test]
virtio_rotary()2269     fn virtio_rotary() {
2270         let config: Config = crate::crosvm::cmdline::RunCommand::from_args(
2271             &[],
2272             &["--rotary", "/dev/rotary-test", "/dev/null"],
2273         )
2274         .unwrap()
2275         .try_into()
2276         .unwrap();
2277 
2278         let rotary = config
2279             .virtio_input
2280             .iter()
2281             .find(|input| matches!(input, InputDeviceOption::Rotary { .. }))
2282             .unwrap();
2283 
2284         assert_eq!(
2285             *rotary,
2286             InputDeviceOption::Rotary {
2287                 path: PathBuf::from("/dev/rotary-test")
2288             }
2289         );
2290     }
2291 }
2292