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(¶ms)?;
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