• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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