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