1 // Copyright 2019 The Chromium OS Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 //! The root level module that includes the config and aggregate of the submodules for running said 6 //! configs. 7 8 pub mod argument; 9 #[cfg(all(target_arch = "x86_64", feature = "gdb"))] 10 pub mod gdb; 11 #[path = "linux/mod.rs"] 12 pub mod platform; 13 #[cfg(feature = "plugin")] 14 pub mod plugin; 15 16 use std::collections::{BTreeMap, BTreeSet}; 17 use std::net; 18 use std::ops::RangeInclusive; 19 use std::os::unix::io::RawFd; 20 use std::path::{Path, PathBuf}; 21 use std::str::FromStr; 22 23 use arch::{Pstore, VcpuAffinity}; 24 use devices::serial_device::{SerialHardware, SerialParameters}; 25 use devices::virtio::block::block::DiskOption; 26 #[cfg(feature = "audio_cras")] 27 use devices::virtio::cras_backend::Parameters as CrasSndParameters; 28 use devices::virtio::fs::passthrough; 29 #[cfg(feature = "gpu")] 30 use devices::virtio::gpu::GpuParameters; 31 use devices::virtio::vhost::vsock::VhostVsockDeviceParameter; 32 #[cfg(any(feature = "video-decoder", feature = "video-encoder"))] 33 use devices::virtio::VideoBackendType; 34 #[cfg(feature = "audio")] 35 use devices::Ac97Parameters; 36 #[cfg(feature = "direct")] 37 use devices::BusRange; 38 use devices::{IommuDevType, PciAddress, StubPciParameters}; 39 use hypervisor::ProtectionType; 40 use libc::{getegid, geteuid}; 41 #[cfg(feature = "gpu")] 42 use platform::GpuRenderServerParameters; 43 use uuid::Uuid; 44 use vm_control::BatteryType; 45 46 static KVM_PATH: &str = "/dev/kvm"; 47 static VHOST_NET_PATH: &str = "/dev/vhost-net"; 48 static SECCOMP_POLICY_DIR: &str = "/usr/share/policy/crosvm"; 49 50 /// Indicates the location and kind of executable kernel for a VM. 51 #[derive(Debug)] 52 pub enum Executable { 53 /// An executable intended to be run as a BIOS directly. 54 Bios(PathBuf), 55 /// A elf linux kernel, loaded and executed by crosvm. 56 Kernel(PathBuf), 57 /// Path to a plugin executable that is forked by crosvm. 58 Plugin(PathBuf), 59 } 60 61 pub struct VhostUserOption { 62 pub socket: PathBuf, 63 } 64 65 pub struct VhostUserFsOption { 66 pub socket: PathBuf, 67 pub tag: String, 68 } 69 70 pub struct VhostUserWlOption { 71 pub socket: PathBuf, 72 pub vm_tube: PathBuf, 73 } 74 75 /// Options for virtio-vhost-user proxy device. 76 pub struct VvuOption { 77 pub socket: PathBuf, 78 pub addr: Option<PciAddress>, 79 pub uuid: Option<Uuid>, 80 } 81 82 /// A bind mount for directories in the plugin process. 83 pub struct BindMount { 84 pub src: PathBuf, 85 pub dst: PathBuf, 86 pub writable: bool, 87 } 88 89 /// A mapping of linux group IDs for the plugin process. 90 pub struct GidMap { 91 pub inner: libc::gid_t, 92 pub outer: libc::gid_t, 93 pub count: u32, 94 } 95 96 /// Direct IO forwarding options 97 #[cfg(feature = "direct")] 98 #[derive(Debug)] 99 pub struct DirectIoOption { 100 pub path: PathBuf, 101 pub ranges: Vec<BusRange>, 102 } 103 104 pub const DEFAULT_TOUCH_DEVICE_HEIGHT: u32 = 1024; 105 pub const DEFAULT_TOUCH_DEVICE_WIDTH: u32 = 1280; 106 107 pub struct TouchDeviceOption { 108 path: PathBuf, 109 width: Option<u32>, 110 height: Option<u32>, 111 default_width: u32, 112 default_height: u32, 113 } 114 115 impl TouchDeviceOption { new(path: PathBuf) -> TouchDeviceOption116 pub fn new(path: PathBuf) -> TouchDeviceOption { 117 TouchDeviceOption { 118 path, 119 width: None, 120 height: None, 121 default_width: DEFAULT_TOUCH_DEVICE_WIDTH, 122 default_height: DEFAULT_TOUCH_DEVICE_HEIGHT, 123 } 124 } 125 126 /// Getter for the path to the input event streams. get_path(&self) -> &Path127 pub fn get_path(&self) -> &Path { 128 self.path.as_path() 129 } 130 131 /// When a user specifies the parameters for a touch device, width and height are optional. 132 /// If the width and height are missing, default values are used. Default values can be set 133 /// dynamically, for example from the display sizes specified by the gpu argument. set_default_size(&mut self, width: u32, height: u32)134 pub fn set_default_size(&mut self, width: u32, height: u32) { 135 self.default_width = width; 136 self.default_height = height; 137 } 138 139 /// Setter for the width specified by the user. set_width(&mut self, width: u32)140 pub fn set_width(&mut self, width: u32) { 141 self.width.replace(width); 142 } 143 144 /// Setter for the height specified by the user. set_height(&mut self, height: u32)145 pub fn set_height(&mut self, height: u32) { 146 self.height.replace(height); 147 } 148 149 /// If the user specifies the size, use it. Otherwise, use the default values. get_size(&self) -> (u32, u32)150 pub fn get_size(&self) -> (u32, u32) { 151 ( 152 self.width.unwrap_or(self.default_width), 153 self.height.unwrap_or(self.default_height), 154 ) 155 } 156 } 157 158 #[derive(Eq, PartialEq)] 159 pub enum SharedDirKind { 160 FS, 161 P9, 162 } 163 164 impl FromStr for SharedDirKind { 165 type Err = &'static str; 166 from_str(s: &str) -> Result<Self, Self::Err>167 fn from_str(s: &str) -> Result<Self, Self::Err> { 168 use SharedDirKind::*; 169 match s { 170 "fs" | "FS" => Ok(FS), 171 "9p" | "9P" | "p9" | "P9" => Ok(P9), 172 _ => Err("invalid file system type"), 173 } 174 } 175 } 176 177 impl Default for SharedDirKind { default() -> SharedDirKind178 fn default() -> SharedDirKind { 179 SharedDirKind::P9 180 } 181 } 182 183 pub struct SharedDir { 184 pub src: PathBuf, 185 pub tag: String, 186 pub kind: SharedDirKind, 187 pub uid_map: String, 188 pub gid_map: String, 189 pub fs_cfg: passthrough::Config, 190 pub p9_cfg: p9::Config, 191 } 192 193 impl Default for SharedDir { default() -> SharedDir194 fn default() -> SharedDir { 195 SharedDir { 196 src: Default::default(), 197 tag: Default::default(), 198 kind: Default::default(), 199 uid_map: format!("0 {} 1", unsafe { geteuid() }), 200 gid_map: format!("0 {} 1", unsafe { getegid() }), 201 fs_cfg: Default::default(), 202 p9_cfg: Default::default(), 203 } 204 } 205 } 206 207 /// Vfio device type, recognized based on command line option. 208 #[derive(Eq, PartialEq, Clone, Copy)] 209 pub enum VfioType { 210 Pci, 211 Platform, 212 } 213 214 impl FromStr for VfioType { 215 type Err = &'static str; 216 from_str(s: &str) -> Result<Self, Self::Err>217 fn from_str(s: &str) -> Result<Self, Self::Err> { 218 use VfioType::*; 219 match s { 220 "vfio" => Ok(Pci), 221 "vfio-platform" => Ok(Platform), 222 _ => Err("invalid vfio device type, must be 'vfio|vfio-platform'"), 223 } 224 } 225 } 226 227 /// VFIO device structure for creating a new instance based on command line options. 228 pub struct VfioCommand { 229 vfio_path: PathBuf, 230 dev_type: VfioType, 231 params: BTreeMap<String, String>, 232 } 233 234 impl VfioCommand { new(dev_type: VfioType, path: &str) -> argument::Result<VfioCommand>235 pub fn new(dev_type: VfioType, path: &str) -> argument::Result<VfioCommand> { 236 let mut param = path.split(','); 237 let vfio_path = 238 PathBuf::from(param.next().ok_or_else(|| argument::Error::InvalidValue { 239 value: path.to_owned(), 240 expected: String::from("missing vfio path"), 241 })?); 242 243 if !vfio_path.exists() { 244 return Err(argument::Error::InvalidValue { 245 value: path.to_owned(), 246 expected: String::from("the vfio path does not exist"), 247 }); 248 } 249 if !vfio_path.is_dir() { 250 return Err(argument::Error::InvalidValue { 251 value: path.to_owned(), 252 expected: String::from("the vfio path should be directory"), 253 }); 254 } 255 256 let mut params = BTreeMap::new(); 257 for p in param { 258 let mut kv = p.splitn(2, '='); 259 if let (Some(kind), Some(value)) = (kv.next(), kv.next()) { 260 Self::validate_params(kind, value)?; 261 params.insert(kind.to_owned(), value.to_owned()); 262 }; 263 } 264 Ok(VfioCommand { 265 vfio_path, 266 params, 267 dev_type, 268 }) 269 } 270 validate_params(kind: &str, value: &str) -> Result<(), argument::Error>271 fn validate_params(kind: &str, value: &str) -> Result<(), argument::Error> { 272 match kind { 273 "guest-address" => { 274 if value.eq_ignore_ascii_case("auto") || PciAddress::from_str(value).is_ok() { 275 Ok(()) 276 } else { 277 Err(argument::Error::InvalidValue { 278 value: format!("{}={}", kind.to_owned(), value.to_owned()), 279 expected: String::from( 280 "option must be `guest-address=auto|<BUS:DEVICE.FUNCTION>`", 281 ), 282 }) 283 } 284 } 285 "iommu" => { 286 if IommuDevType::from_str(value).is_ok() { 287 Ok(()) 288 } else { 289 Err(argument::Error::InvalidValue { 290 value: format!("{}={}", kind.to_owned(), value.to_owned()), 291 expected: String::from("option must be `iommu=viommu|coiommu|off`"), 292 }) 293 } 294 } 295 _ => Err(argument::Error::InvalidValue { 296 value: format!("{}={}", kind.to_owned(), value.to_owned()), 297 expected: String::from("option must be `guest-address=<val>` and/or `iommu=<val>`"), 298 }), 299 } 300 } 301 get_type(&self) -> VfioType302 pub fn get_type(&self) -> VfioType { 303 self.dev_type 304 } 305 guest_address(&self) -> Option<PciAddress>306 pub fn guest_address(&self) -> Option<PciAddress> { 307 self.params 308 .get("guest-address") 309 .and_then(|addr| PciAddress::from_str(addr).ok()) 310 } 311 iommu_dev_type(&self) -> IommuDevType312 pub fn iommu_dev_type(&self) -> IommuDevType { 313 if let Some(iommu) = self.params.get("iommu") { 314 if let Ok(v) = IommuDevType::from_str(iommu) { 315 return v; 316 } 317 } 318 IommuDevType::NoIommu 319 } 320 } 321 322 #[derive(Debug)] 323 pub struct FileBackedMappingParameters { 324 pub address: u64, 325 pub size: u64, 326 pub path: PathBuf, 327 pub offset: u64, 328 pub writable: bool, 329 pub sync: bool, 330 } 331 332 #[derive(Clone)] 333 pub struct HostPcieRootPortParameters { 334 pub host_path: PathBuf, 335 pub hp_gpe: Option<u32>, 336 } 337 338 #[derive(Debug)] 339 pub struct JailConfig { 340 pub pivot_root: PathBuf, 341 pub seccomp_policy_dir: PathBuf, 342 pub seccomp_log_failures: bool, 343 } 344 345 impl Default for JailConfig { default() -> Self346 fn default() -> Self { 347 JailConfig { 348 pivot_root: PathBuf::from(option_env!("DEFAULT_PIVOT_ROOT").unwrap_or("/var/empty")), 349 seccomp_policy_dir: PathBuf::from(SECCOMP_POLICY_DIR), 350 seccomp_log_failures: false, 351 } 352 } 353 } 354 355 /// Aggregate of all configurable options for a running VM. 356 pub struct Config { 357 pub kvm_device_path: PathBuf, 358 pub vhost_vsock_device: Option<VhostVsockDeviceParameter>, 359 pub vhost_net_device_path: PathBuf, 360 pub vcpu_count: Option<usize>, 361 pub vcpu_cgroup_path: Option<PathBuf>, 362 pub rt_cpus: Vec<usize>, 363 pub vcpu_affinity: Option<VcpuAffinity>, 364 pub cpu_clusters: Vec<Vec<usize>>, 365 pub cpu_capacity: BTreeMap<usize, u32>, // CPU index -> capacity 366 pub per_vm_core_scheduling: bool, 367 #[cfg(feature = "audio_cras")] 368 pub cras_snds: Vec<CrasSndParameters>, 369 pub delay_rt: bool, 370 pub no_smt: bool, 371 pub memory: Option<u64>, 372 pub swiotlb: Option<u64>, 373 pub hugepages: bool, 374 pub memory_file: Option<PathBuf>, 375 pub executable_path: Option<Executable>, 376 pub android_fstab: Option<PathBuf>, 377 pub initrd_path: Option<PathBuf>, 378 pub jail_config: Option<JailConfig>, 379 pub jail_enabled: bool, 380 pub params: Vec<String>, 381 pub socket_path: Option<PathBuf>, 382 pub balloon_control: Option<PathBuf>, 383 pub plugin_root: Option<PathBuf>, 384 pub plugin_mounts: Vec<BindMount>, 385 pub plugin_gid_maps: Vec<GidMap>, 386 pub disks: Vec<DiskOption>, 387 pub pmem_devices: Vec<DiskOption>, 388 pub pstore: Option<Pstore>, 389 pub host_ip: Option<net::Ipv4Addr>, 390 pub netmask: Option<net::Ipv4Addr>, 391 pub mac_address: Option<net_util::MacAddress>, 392 pub net_vq_pairs: Option<u16>, 393 pub vhost_net: bool, 394 pub tap_fd: Vec<RawFd>, 395 pub tap_name: Vec<String>, 396 pub cid: Option<u64>, 397 pub wayland_socket_paths: BTreeMap<String, PathBuf>, 398 pub x_display: Option<String>, 399 pub shared_dirs: Vec<SharedDir>, 400 #[cfg(feature = "gpu")] 401 pub gpu_parameters: Option<GpuParameters>, 402 #[cfg(feature = "gpu")] 403 pub gpu_render_server_parameters: Option<GpuRenderServerParameters>, 404 pub software_tpm: bool, 405 pub display_window_keyboard: bool, 406 pub display_window_mouse: bool, 407 #[cfg(feature = "audio")] 408 pub ac97_parameters: Vec<Ac97Parameters>, 409 #[cfg(feature = "audio")] 410 pub sound: Option<PathBuf>, 411 pub serial_parameters: BTreeMap<(SerialHardware, u8), SerialParameters>, 412 pub syslog_tag: Option<String>, 413 pub usb: bool, 414 pub virtio_single_touch: Vec<TouchDeviceOption>, 415 pub virtio_multi_touch: Vec<TouchDeviceOption>, 416 pub virtio_trackpad: Vec<TouchDeviceOption>, 417 pub virtio_mice: Vec<PathBuf>, 418 pub virtio_keyboard: Vec<PathBuf>, 419 pub virtio_switches: Vec<PathBuf>, 420 pub virtio_input_evdevs: Vec<PathBuf>, 421 pub virtio_iommu: bool, 422 pub split_irqchip: bool, 423 pub vfio: Vec<VfioCommand>, 424 #[cfg(feature = "video-decoder")] 425 pub video_dec: Option<VideoBackendType>, 426 #[cfg(feature = "video-encoder")] 427 pub video_enc: Option<VideoBackendType>, 428 pub acpi_tables: Vec<PathBuf>, 429 pub protected_vm: ProtectionType, 430 pub battery_type: Option<BatteryType>, 431 #[cfg(all(target_arch = "x86_64", feature = "gdb"))] 432 pub gdb: Option<u32>, 433 pub balloon: bool, 434 pub balloon_bias: i64, 435 pub vhost_user_blk: Vec<VhostUserOption>, 436 pub vhost_user_console: Vec<VhostUserOption>, 437 pub vhost_user_fs: Vec<VhostUserFsOption>, 438 pub vhost_user_gpu: Vec<VhostUserOption>, 439 pub vhost_user_mac80211_hwsim: Option<VhostUserOption>, 440 pub vhost_user_net: Vec<VhostUserOption>, 441 #[cfg(feature = "audio")] 442 pub vhost_user_snd: Vec<VhostUserOption>, 443 pub vhost_user_vsock: Vec<VhostUserOption>, 444 pub vhost_user_wl: Vec<VhostUserWlOption>, 445 #[cfg(feature = "direct")] 446 pub direct_pmio: Option<DirectIoOption>, 447 #[cfg(feature = "direct")] 448 pub direct_mmio: Option<DirectIoOption>, 449 #[cfg(feature = "direct")] 450 pub direct_level_irq: Vec<u32>, 451 #[cfg(feature = "direct")] 452 pub direct_edge_irq: Vec<u32>, 453 #[cfg(feature = "direct")] 454 pub direct_wake_irq: Vec<u32>, 455 #[cfg(feature = "direct")] 456 pub direct_gpe: Vec<u32>, 457 pub dmi_path: Option<PathBuf>, 458 pub no_legacy: bool, 459 pub host_cpu_topology: bool, 460 pub privileged_vm: bool, 461 pub stub_pci_devices: Vec<StubPciParameters>, 462 pub vvu_proxy: Vec<VvuOption>, 463 pub coiommu_param: Option<devices::CoIommuParameters>, 464 pub file_backed_mappings: Vec<FileBackedMappingParameters>, 465 pub init_memory: Option<u64>, 466 #[cfg(feature = "direct")] 467 pub pcie_rp: Vec<HostPcieRootPortParameters>, 468 pub rng: bool, 469 pub force_s2idle: bool, 470 pub strict_balloon: bool, 471 pub mmio_address_ranges: Vec<RangeInclusive<u64>>, 472 pub userspace_msr: BTreeSet<u32>, 473 #[cfg(target_os = "android")] 474 pub task_profiles: Vec<String>, 475 } 476 477 impl Default for Config { default() -> Config478 fn default() -> Config { 479 Config { 480 kvm_device_path: PathBuf::from(KVM_PATH), 481 vhost_vsock_device: None, 482 vhost_net_device_path: PathBuf::from(VHOST_NET_PATH), 483 vcpu_count: None, 484 vcpu_cgroup_path: None, 485 rt_cpus: Vec::new(), 486 vcpu_affinity: None, 487 cpu_clusters: Vec::new(), 488 cpu_capacity: BTreeMap::new(), 489 per_vm_core_scheduling: false, 490 #[cfg(feature = "audio_cras")] 491 cras_snds: Vec::new(), 492 delay_rt: false, 493 no_smt: false, 494 memory: None, 495 swiotlb: None, 496 hugepages: false, 497 memory_file: None, 498 executable_path: None, 499 android_fstab: None, 500 initrd_path: None, 501 // We initialize the jail configuration with a default value so jail-related options can 502 // apply irrespective of whether jail is enabled or not. `jail_config` will then be 503 // assigned `None` if it turns out that `jail_enabled` is `false` after we parse all the 504 // arguments. 505 jail_config: Some(Default::default()), 506 jail_enabled: !cfg!(feature = "default-no-sandbox"), 507 params: Vec::new(), 508 socket_path: None, 509 balloon_control: None, 510 plugin_root: None, 511 plugin_mounts: Vec::new(), 512 plugin_gid_maps: Vec::new(), 513 disks: Vec::new(), 514 pmem_devices: Vec::new(), 515 pstore: None, 516 host_ip: None, 517 netmask: None, 518 mac_address: None, 519 net_vq_pairs: None, 520 vhost_net: false, 521 tap_fd: Vec::new(), 522 tap_name: Vec::new(), 523 cid: None, 524 #[cfg(feature = "gpu")] 525 gpu_parameters: None, 526 #[cfg(feature = "gpu")] 527 gpu_render_server_parameters: None, 528 software_tpm: false, 529 wayland_socket_paths: BTreeMap::new(), 530 x_display: None, 531 display_window_keyboard: false, 532 display_window_mouse: false, 533 shared_dirs: Vec::new(), 534 #[cfg(feature = "audio")] 535 ac97_parameters: Vec::new(), 536 #[cfg(feature = "audio")] 537 sound: None, 538 serial_parameters: BTreeMap::new(), 539 syslog_tag: None, 540 usb: true, 541 virtio_single_touch: Vec::new(), 542 virtio_multi_touch: Vec::new(), 543 virtio_trackpad: Vec::new(), 544 virtio_mice: Vec::new(), 545 virtio_keyboard: Vec::new(), 546 virtio_switches: Vec::new(), 547 virtio_input_evdevs: Vec::new(), 548 virtio_iommu: false, 549 split_irqchip: false, 550 vfio: Vec::new(), 551 #[cfg(feature = "video-decoder")] 552 video_dec: None, 553 #[cfg(feature = "video-encoder")] 554 video_enc: None, 555 acpi_tables: Vec::new(), 556 protected_vm: ProtectionType::Unprotected, 557 battery_type: None, 558 #[cfg(all(target_arch = "x86_64", feature = "gdb"))] 559 gdb: None, 560 balloon: true, 561 balloon_bias: 0, 562 vhost_user_blk: Vec::new(), 563 vhost_user_console: Vec::new(), 564 vhost_user_gpu: Vec::new(), 565 vhost_user_fs: Vec::new(), 566 vhost_user_mac80211_hwsim: None, 567 vhost_user_net: Vec::new(), 568 #[cfg(feature = "audio")] 569 vhost_user_snd: Vec::new(), 570 vhost_user_vsock: Vec::new(), 571 vhost_user_wl: Vec::new(), 572 vvu_proxy: Vec::new(), 573 #[cfg(feature = "direct")] 574 direct_pmio: None, 575 #[cfg(feature = "direct")] 576 direct_mmio: None, 577 #[cfg(feature = "direct")] 578 direct_level_irq: Vec::new(), 579 #[cfg(feature = "direct")] 580 direct_edge_irq: Vec::new(), 581 #[cfg(feature = "direct")] 582 direct_wake_irq: Vec::new(), 583 #[cfg(feature = "direct")] 584 direct_gpe: Vec::new(), 585 dmi_path: None, 586 no_legacy: false, 587 host_cpu_topology: false, 588 privileged_vm: false, 589 stub_pci_devices: Vec::new(), 590 coiommu_param: None, 591 file_backed_mappings: Vec::new(), 592 init_memory: None, 593 #[cfg(feature = "direct")] 594 pcie_rp: Vec::new(), 595 rng: true, 596 force_s2idle: false, 597 strict_balloon: false, 598 mmio_address_ranges: Vec::new(), 599 userspace_msr: BTreeSet::new(), 600 #[cfg(target_os = "android")] 601 task_profiles: Vec::new(), 602 } 603 } 604 } 605