aarch64: Provide the maximum supported IPA size as the machine type

The value 0 passed to KVM_CREATE_VM has a special meaning for KVM/arm64.
It indicates that the VM is configured with a 40bit IPA space.
However, not all HW support such an IPA space, and the KVM_CREATE_VM
call fails on these systems.

In order to maximize compatibility, we can ask KVM for the maximum
supported IPA size, and use that as an input to KVM_CREATE_VM, at
which point the kernel will instantiate a VM with that IPA size.

This enables crosvm on exotic hardware such as the Apple-M1.

TEST=tools/run_tests

Change-Id: I7ea39ac6e5de6a1389c0c30cdfeb7c970c411589
Signed-off-by: Marc Zyngier <mzyngier@google.com>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/3124677
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Chirantan Ekbote <chirantan@chromium.org>
This commit is contained in:
Marc Zyngier 2021-08-26 17:53:35 +01:00 committed by Commit Bot
parent d80eabc803
commit 17b1e02fb6
4 changed files with 51 additions and 4 deletions

View file

@ -5,17 +5,36 @@
use libc::{EINVAL, ENOMEM, ENOSYS, ENXIO};
use base::{
errno_result, error, ioctl_with_mut_ref, ioctl_with_ref, Error, MemoryMappingBuilder, Result,
errno_result, error, ioctl_with_mut_ref, ioctl_with_ref, ioctl_with_val, Error,
MemoryMappingBuilder, Result,
};
use kvm_sys::*;
use std::os::raw::c_ulong;
use vm_memory::GuestAddress;
use super::{KvmCap, KvmVcpu, KvmVm};
use super::{Kvm, KvmCap, KvmVcpu, KvmVm};
use crate::{
ClockState, DeviceKind, Hypervisor, IrqSourceChip, PsciVersion, VcpuAArch64, VcpuFeature, Vm,
VmAArch64, VmCap,
};
impl Kvm {
// Compute the machine type, which should be the IPA range for the VM
// Ideally, this would take a description of the memory map and return
// the closest machine type for this VM. Here, we just return the maximum
// the kernel support.
pub fn get_vm_type(&self) -> c_ulong {
// Safe because we know self is a real kvm fd
match unsafe { ioctl_with_val(self, KVM_CHECK_EXTENSION(), KVM_CAP_ARM_VM_IPA_SIZE.into()) }
{
// Not supported? Use 0 as the machine type, which implies 40bit IPA
ret if ret < 0 => 0,
// Use the lower 8 bits representing the IPA space as the machine type
ipa => (ipa & 0xff) as c_ulong,
}
}
}
impl KvmVm {
/// Checks if a particular `VmCap` is available, or returns None if arch-independent
/// Vm.check_capability() should handle the check.

View file

@ -166,7 +166,7 @@ impl KvmVm {
pub fn new(kvm: &Kvm, guest_mem: GuestMemory) -> Result<KvmVm> {
// Safe because we know kvm is a real kvm fd as this module is the only one that can make
// Kvm objects.
let ret = unsafe { ioctl_with_val(kvm, KVM_CREATE_VM(), 0) };
let ret = unsafe { ioctl_with_val(kvm, KVM_CREATE_VM(), kvm.get_vm_type()) };
if ret < 0 {
return errno_result();
}

View file

@ -12,6 +12,7 @@ use base::{
};
use data_model::vec_with_array_field;
use kvm_sys::*;
use std::os::raw::c_ulong;
use vm_memory::GuestAddress;
use super::{Kvm, KvmVcpu, KvmVm};
@ -64,6 +65,11 @@ impl Kvm {
const KVM_MAX_ENTRIES: usize = 256;
get_cpuid_with_initial_capacity(self, kind, KVM_MAX_ENTRIES)
}
// The x86 machine type is always 0
pub fn get_vm_type(&self) -> c_ulong {
0
}
}
impl HypervisorX86_64 for Kvm {

View file

@ -198,6 +198,28 @@ impl Kvm {
Ok(indices.to_vec())
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
// The x86 machine type is always 0
pub fn get_vm_type(&self) -> c_ulong {
0
}
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
// Compute the machine type, which should be the IPA range for the VM
// Ideally, this would take a description of the memory map and return
// the closest machine type for this VM. Here, we just return the maximum
// the kernel support.
pub fn get_vm_type(&self) -> c_ulong {
// Safe because we know self is a real kvm fd
match unsafe { ioctl_with_val(self, KVM_CHECK_EXTENSION(), KVM_CAP_ARM_VM_IPA_SIZE.into()) }
{
// Not supported? Use 0 as the machine type, which implies 40bit IPA
ret if ret < 0 => 0,
// Use the lower 8 bits representing the IPA space as the machine type
ipa => (ipa & 0xff) as c_ulong,
}
}
}
impl AsRawDescriptor for Kvm {
@ -274,7 +296,7 @@ impl Vm {
pub fn new(kvm: &Kvm, guest_mem: GuestMemory) -> Result<Vm> {
// Safe because we know kvm is a real kvm fd as this module is the only one that can make
// Kvm objects.
let ret = unsafe { ioctl_with_val(kvm, KVM_CREATE_VM(), 0) };
let ret = unsafe { ioctl_with_val(kvm, KVM_CREATE_VM(), kvm.get_vm_type()) };
if ret >= 0 {
// Safe because we verify the value of ret and we are the owners of the fd.
let vm_file = unsafe { File::from_raw_descriptor(ret) };