diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index 24955c2872..dc7cd4afa2 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -16,9 +16,11 @@ use std::cell::RefCell; use std::cmp::{min, Reverse}; use std::collections::{BTreeMap, BinaryHeap}; use std::convert::TryFrom; +use std::ffi::CString; use std::mem::{size_of, ManuallyDrop}; -use std::os::raw::{c_char, c_int, c_ulong, c_void}; -use std::os::unix::io::AsRawFd; +use std::os::raw::{c_int, c_ulong, c_void}; +use std::os::unix::{io::AsRawFd, prelude::OsStrExt}; +use std::path::{Path, PathBuf}; use std::ptr::copy_nonoverlapping; use std::sync::atomic::AtomicU64; use std::sync::Arc; @@ -94,11 +96,10 @@ pub struct Kvm { type KvmCap = kvm::Cap; impl Kvm { - /// Opens `/dev/kvm/` and returns a Kvm object on success. - pub fn new() -> Result { - // Open calls are safe because we give a constant nul-terminated string and verify the - // result. - let ret = unsafe { open("/dev/kvm\0".as_ptr() as *const c_char, O_RDWR | O_CLOEXEC) }; + pub fn new_with_path(device_path: &Path) -> Result { + // Open calls are safe because we give a nul-terminated string and verify the result. + let c_path = CString::new(device_path.as_os_str().as_bytes()).unwrap(); + let ret = unsafe { open(c_path.as_ptr(), O_RDWR | O_CLOEXEC) }; if ret < 0 { return errno_result(); } @@ -108,6 +109,11 @@ impl Kvm { }) } + /// Opens `/dev/kvm/` and returns a Kvm object on success. + pub fn new() -> Result { + Kvm::new_with_path(&PathBuf::from("/dev/kvm")) + } + /// Gets the size of the mmap required to use vcpu's `kvm_run` structure. pub fn get_vcpu_mmap_size(&self) -> Result { // Safe because we know that our file is a KVM fd and we verify the return result. diff --git a/kvm/src/lib.rs b/kvm/src/lib.rs index 357020af2f..5c7a6060b7 100644 --- a/kvm/src/lib.rs +++ b/kvm/src/lib.rs @@ -9,10 +9,13 @@ mod cap; use std::cell::RefCell; use std::cmp::{min, Ordering}; use std::collections::{BTreeMap, BinaryHeap}; +use std::ffi::CString; use std::fs::File; use std::mem::size_of; use std::ops::{Deref, DerefMut}; use std::os::raw::*; +use std::os::unix::prelude::OsStrExt; +use std::path::{Path, PathBuf}; use std::ptr::copy_nonoverlapping; use std::sync::Arc; use sync::Mutex; @@ -94,9 +97,14 @@ pub struct Kvm { impl Kvm { /// Opens `/dev/kvm/` and returns a Kvm object on success. pub fn new() -> Result { - // Open calls are safe because we give a constant nul-terminated string and verify the - // result. - let ret = unsafe { open("/dev/kvm\0".as_ptr() as *const c_char, O_RDWR | O_CLOEXEC) }; + Kvm::new_with_path(&PathBuf::from("/dev/kvm")) + } + + /// Opens a KVM device at `device_path` and returns a Kvm object on success. + pub fn new_with_path(device_path: &Path) -> Result { + // Open calls are safe because we give a nul-terminated string and verify the result. + let c_path = CString::new(device_path.as_os_str().as_bytes()).unwrap(); + let ret = unsafe { open(c_path.as_ptr(), O_RDWR | O_CLOEXEC) }; if ret < 0 { return errno_result(); } diff --git a/src/crosvm.rs b/src/crosvm.rs index 3ae82908a2..a998e23eea 100644 --- a/src/crosvm.rs +++ b/src/crosvm.rs @@ -29,6 +29,7 @@ use devices::ProtectionType; use libc::{getegid, geteuid}; use vm_control::BatteryType; +static KVM_PATH: &str = "/dev/kvm"; static SECCOMP_POLICY_DIR: &str = "/usr/share/policy/crosvm"; /// Indicates the location and kind of executable kernel for a VM. @@ -174,6 +175,7 @@ impl Default for SharedDir { /// Aggregate of all configurable options for a running VM. pub struct Config { + pub kvm_device_path: PathBuf, pub vcpu_count: Option, pub rt_cpus: Vec, pub vcpu_affinity: Option, @@ -234,6 +236,7 @@ pub struct Config { impl Default for Config { fn default() -> Config { Config { + kvm_device_path: PathBuf::from(KVM_PATH), vcpu_count: None, rt_cpus: Vec::new(), vcpu_affinity: None, diff --git a/src/linux.rs b/src/linux.rs index 52693d5543..2b373f21b5 100644 --- a/src/linux.rs +++ b/src/linux.rs @@ -2164,8 +2164,8 @@ fn file_to_i64>(path: P, nth: usize) -> io::Result { .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "empty file")) } -fn create_kvm(mem: GuestMemory) -> base::Result { - let kvm = Kvm::new()?; +fn create_kvm(device_path: &Path, mem: GuestMemory) -> base::Result { + let kvm = Kvm::new_with_path(device_path)?; let vm = KvmVm::new(&kvm, mem)?; Ok(vm) } @@ -2190,6 +2190,8 @@ fn create_kvm_split_irq_chip( } pub fn run_config(cfg: Config) -> Result<()> { + let kvm_device_path = cfg.kvm_device_path.clone(); + let create_kvm_with_path = |mem| create_kvm(&kvm_device_path, mem); if cfg.split_irqchip { #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] { @@ -2198,10 +2200,10 @@ pub fn run_config(cfg: Config) -> Result<()> { #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { - run_vm::<_, KvmVcpu, _, _, _>(cfg, create_kvm, create_kvm_split_irq_chip) + run_vm::<_, KvmVcpu, _, _, _>(cfg, create_kvm_with_path, create_kvm_split_irq_chip) } } else { - run_vm::<_, KvmVcpu, _, _, _>(cfg, create_kvm, create_kvm_kernel_irq_chip) + run_vm::<_, KvmVcpu, _, _, _>(cfg, create_kvm_with_path, create_kvm_kernel_irq_chip) } } diff --git a/src/main.rs b/src/main.rs index 45bc2f4bc9..de772582f7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -676,6 +676,17 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument:: } cfg.executable_path = Some(Executable::Kernel(kernel_path)); } + "kvm-device" => { + let kvm_device_path = PathBuf::from(value.unwrap()); + if !kvm_device_path.exists() { + return Err(argument::Error::InvalidValue { + value: value.unwrap().to_owned(), + expected: String::from("this kvm device path does not exist"), + }); + } + + cfg.kvm_device_path = kvm_device_path; + } "android-fstab" => { if cfg.android_fstab.is_some() && !cfg.android_fstab.as_ref().unwrap().as_os_str().is_empty() @@ -1595,6 +1606,7 @@ fn validate_arguments(cfg: &mut Config) -> std::result::Result<(), argument::Err fn run_vm(args: std::env::Args) -> std::result::Result<(), ()> { let arguments = &[Argument::positional("KERNEL", "bzImage of kernel to run"), + Argument::value("kvm-device", "PATH", "Path to the KVM device. (default /dev/kvm)"), Argument::value("android-fstab", "PATH", "Path to Android fstab"), Argument::short_value('i', "initrd", "PATH", "Initial ramdisk to load."), Argument::short_value('p', diff --git a/src/plugin/mod.rs b/src/plugin/mod.rs index f900bf5999..4d77c08f7b 100644 --- a/src/plugin/mod.rs +++ b/src/plugin/mod.rs @@ -691,7 +691,7 @@ pub fn run_config(cfg: Config) -> Result<()> { }; let vcpu_count = cfg.vcpu_count.unwrap_or(1) as u32; let mem = GuestMemory::new(&[]).unwrap(); - let kvm = Kvm::new().map_err(Error::CreateKvm)?; + let kvm = Kvm::new_with_path(&cfg.kvm_device_path).map_err(Error::CreateKvm)?; let mut vm = Vm::new(&kvm, mem).map_err(Error::CreateVm)?; vm.create_irq_chip().map_err(Error::CreateIrqChip)?; vm.create_pit().map_err(Error::CreatePIT)?;