mirror of
https://github.com/obhq/obliteration.git
synced 2024-10-07 00:13:24 +00:00
Fixes incomplete ID_AA64MMFR0_EL1 on Linux (#999)
This commit is contained in:
parent
98825ea886
commit
80dde69731
@ -4,15 +4,26 @@ pub const KVM_GET_API_VERSION: c_ulong = _IO(KVMIO, 0x00);
|
||||
pub const KVM_CREATE_VM: c_ulong = _IO(KVMIO, 0x01);
|
||||
pub const KVM_CHECK_EXTENSION: c_ulong = _IO(KVMIO, 0x03);
|
||||
pub const KVM_GET_VCPU_MMAP_SIZE: c_ulong = _IO(KVMIO, 0x04);
|
||||
pub const KVM_CREATE_VCPU: c_ulong = _IO(KVMIO, 0x41);
|
||||
pub const KVM_GET_ONE_REG: c_ulong = _IOW::<KvmOneReg<()>>(KVMIO, 0xab);
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
pub const KVM_ARM_VCPU_INIT: c_ulong = _IOW::<KvmVcpuInit>(KVMIO, 0xae);
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
pub const KVM_ARM_PREFERRED_TARGET: c_ulong = _IOR::<KvmVcpuInit>(KVMIO, 0xaf);
|
||||
|
||||
pub const KVM_API_VERSION: c_int = 12;
|
||||
|
||||
pub const KVM_CAP_MAX_VCPUS: c_int = 66;
|
||||
pub const KVM_CAP_ONE_REG: c_int = 70;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
pub const KVM_CAP_ARM_VM_IPA_SIZE: c_int = 165;
|
||||
|
||||
const KVMIO: c_ulong = 0xAE;
|
||||
|
||||
const _IOC_NONE: c_ulong = 0;
|
||||
const _IOC_WRITE: c_ulong = 1;
|
||||
const _IOC_READ: c_ulong = 2;
|
||||
|
||||
const _IOC_NRSHIFT: c_ulong = 0;
|
||||
const _IOC_NRBITS: c_ulong = 8;
|
||||
const _IOC_TYPEBITS: c_ulong = 8;
|
||||
@ -27,17 +38,53 @@ pub fn KVM_VM_TYPE_ARM_IPA_SIZE(v: c_int) -> c_int {
|
||||
v & 0xff
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[allow(non_snake_case)]
|
||||
pub fn ARM64_SYS_REG(op0: u64, op1: u64, crn: u64, crm: u64, op2: u64) -> u64 {
|
||||
(0x6000000000000000
|
||||
| 0x0013 << 16
|
||||
| (op0 << 14) & 0x000000000000c000
|
||||
| (op1 << 11) & 0x0000000000003800
|
||||
| (crn << 7) & 0x0000000000000780
|
||||
| (crm << 3) & 0x0000000000000078
|
||||
| op2 & 0x0000000000000007)
|
||||
| 0x0030000000000000
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
const fn _IO(ty: c_ulong, nr: c_ulong) -> c_ulong {
|
||||
_IOC(_IOC_NONE, ty, nr, 0)
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
const fn _IOR<T>(ty: c_ulong, nr: c_ulong) -> c_ulong {
|
||||
_IOC(_IOC_READ, ty, nr, size_of::<T>() as _)
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
const fn _IOW<T>(ty: c_ulong, nr: c_ulong) -> c_ulong {
|
||||
_IOC(_IOC_WRITE, ty, nr, size_of::<T>() as _)
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
const fn _IOC(dir: c_ulong, ty: c_ulong, nr: c_ulong, size: c_ulong) -> c_ulong {
|
||||
((dir) << _IOC_DIRSHIFT)
|
||||
| ((ty) << _IOC_TYPESHIFT)
|
||||
| ((nr) << _IOC_NRSHIFT)
|
||||
| ((size) << _IOC_SIZESHIFT)
|
||||
(dir << _IOC_DIRSHIFT)
|
||||
| (ty << _IOC_TYPESHIFT)
|
||||
| (nr << _IOC_NRSHIFT)
|
||||
| (size << _IOC_SIZESHIFT)
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct KvmOneReg<'a, T> {
|
||||
pub id: u64,
|
||||
pub addr: &'a mut T,
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[repr(C)]
|
||||
pub struct KvmVcpuInit {
|
||||
pub target: u32,
|
||||
pub features: [u32; 7],
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
@ -48,7 +95,5 @@ extern "C" {
|
||||
len: u64,
|
||||
mem: *mut c_void,
|
||||
) -> c_int;
|
||||
pub fn kvm_create_vcpu(vm: c_int, id: u32, fd: *mut c_int) -> c_int;
|
||||
|
||||
pub fn kvm_run(vcpu: c_int) -> c_int;
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
use self::cpu::KvmCpu;
|
||||
use self::ffi::{
|
||||
kvm_create_vcpu, kvm_set_user_memory_region, KVM_API_VERSION, KVM_CAP_MAX_VCPUS,
|
||||
KVM_CAP_ONE_REG, KVM_CHECK_EXTENSION, KVM_CREATE_VM, KVM_GET_API_VERSION,
|
||||
kvm_set_user_memory_region, KvmOneReg, KVM_API_VERSION, KVM_CAP_MAX_VCPUS, KVM_CAP_ONE_REG,
|
||||
KVM_CHECK_EXTENSION, KVM_CREATE_VCPU, KVM_CREATE_VM, KVM_GET_API_VERSION, KVM_GET_ONE_REG,
|
||||
KVM_GET_VCPU_MMAP_SIZE,
|
||||
};
|
||||
use super::{CpuFeats, Hypervisor};
|
||||
@ -63,6 +63,18 @@ pub fn new(cpu: usize, ram: Ram) -> Result<impl Hypervisor, VmmError> {
|
||||
|
||||
// Create a VM.
|
||||
let vm = create_vm(kvm.as_fd())?;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
let preferred_target = unsafe {
|
||||
let mut v: self::ffi::KvmVcpuInit = std::mem::zeroed();
|
||||
|
||||
if ioctl(vm.as_raw_fd(), self::ffi::KVM_ARM_PREFERRED_TARGET, &mut v) < 0 {
|
||||
return Err(VmmError::GetPreferredTargetFailed(Error::last_os_error()));
|
||||
}
|
||||
|
||||
v
|
||||
};
|
||||
|
||||
// Set RAM.
|
||||
let slot = 0;
|
||||
let len = ram.len().try_into().unwrap();
|
||||
let mem = ram.host_addr().cast_mut().cast();
|
||||
@ -74,6 +86,8 @@ pub fn new(cpu: usize, ram: Ram) -> Result<impl Hypervisor, VmmError> {
|
||||
|
||||
Ok(Kvm {
|
||||
vcpu_mmap_size: vcpu_mmap_size.try_into().unwrap(),
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
preferred_target,
|
||||
vm,
|
||||
ram,
|
||||
kvm,
|
||||
@ -127,34 +141,70 @@ fn create_vm(kvm: BorrowedFd) -> Result<OwnedFd, VmmError> {
|
||||
/// Fields in this struct need to drop in a correct order (e.g. vm must be dropped before ram).
|
||||
struct Kvm {
|
||||
vcpu_mmap_size: usize,
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
preferred_target: self::ffi::KvmVcpuInit,
|
||||
vm: OwnedFd,
|
||||
ram: Ram,
|
||||
#[allow(dead_code)] // kvm are needed by vm.
|
||||
kvm: OwnedFd,
|
||||
}
|
||||
|
||||
impl Kvm {
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn create_cpu(&self, id: usize) -> Result<OwnedFd, KvmCpuError> {
|
||||
use self::ffi::KVM_ARM_VCPU_INIT;
|
||||
|
||||
// Create CPU.
|
||||
let cpu = unsafe { ioctl(self.vm.as_raw_fd(), KVM_CREATE_VCPU, id) };
|
||||
|
||||
if cpu < 0 {
|
||||
return Err(KvmCpuError::CreateCpuFailed(Error::last_os_error()));
|
||||
}
|
||||
|
||||
// Init CPU.
|
||||
let cpu = unsafe { OwnedFd::from_raw_fd(cpu) };
|
||||
|
||||
if unsafe { ioctl(cpu.as_raw_fd(), KVM_ARM_VCPU_INIT, &self.preferred_target) < 0 } {
|
||||
return Err(KvmCpuError::InitCpuFailed(Error::last_os_error()));
|
||||
}
|
||||
|
||||
Ok(cpu)
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn create_cpu(&self, id: usize) -> Result<OwnedFd, KvmCpuError> {
|
||||
let cpu = unsafe { ioctl(self.vm.as_raw_fd(), KVM_CREATE_VCPU, id) };
|
||||
|
||||
if cpu < 0 {
|
||||
Err(KvmCpuError::CreateCpuFailed(Error::last_os_error()))
|
||||
} else {
|
||||
Ok(unsafe { OwnedFd::from_raw_fd(cpu) })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Hypervisor for Kvm {
|
||||
type Cpu<'a> = KvmCpu<'a>;
|
||||
type CpuErr = KvmCpuError;
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn cpu_features(&mut self) -> Result<CpuFeats, Self::CpuErr> {
|
||||
// See https://www.kernel.org/doc/html/latest/arch/arm64/cpu-feature-registers.html for the
|
||||
// reason why we can access *_EL1 registers from a user space.
|
||||
use self::ffi::ARM64_SYS_REG;
|
||||
use crate::vmm::hv::{Mmfr0, Mmfr1, Mmfr2};
|
||||
use std::arch::asm;
|
||||
|
||||
// ID_AA64MMFR0_EL1.
|
||||
let mut mmfr0;
|
||||
|
||||
unsafe {
|
||||
asm!(
|
||||
"mrs {v}, ID_AA64MMFR0_EL1",
|
||||
v = out(reg) mmfr0,
|
||||
options(pure, nomem, preserves_flags, nostack)
|
||||
)
|
||||
let cpu = self.create_cpu(0)?;
|
||||
let mut mmfr0 = Mmfr0::default();
|
||||
let mut req = KvmOneReg {
|
||||
id: ARM64_SYS_REG(0b11, 0b000, 0b0000, 0b0111, 0b000),
|
||||
addr: &mut mmfr0,
|
||||
};
|
||||
|
||||
if unsafe { ioctl(cpu.as_raw_fd(), KVM_GET_ONE_REG, &mut req) < 0 } {
|
||||
return Err(KvmCpuError::ReadMmfr0Failed(Error::last_os_error()));
|
||||
}
|
||||
|
||||
// ID_AA64MMFR1_EL1.
|
||||
let mut mmfr1;
|
||||
|
||||
@ -178,7 +228,7 @@ impl Hypervisor for Kvm {
|
||||
};
|
||||
|
||||
Ok(CpuFeats {
|
||||
mmfr0: Mmfr0::from_bits(mmfr0),
|
||||
mmfr0,
|
||||
mmfr1: Mmfr1::from_bits(mmfr1),
|
||||
mmfr2: Mmfr2::from_bits(mmfr2),
|
||||
})
|
||||
@ -198,17 +248,7 @@ impl Hypervisor for Kvm {
|
||||
}
|
||||
|
||||
fn create_cpu(&self, id: usize) -> Result<Self::Cpu<'_>, Self::CpuErr> {
|
||||
use std::io::Error;
|
||||
|
||||
// Create vCPU.
|
||||
let id = id.try_into().unwrap();
|
||||
let mut vcpu = -1;
|
||||
let vcpu = match unsafe { kvm_create_vcpu(self.vm.as_raw_fd(), id, &mut vcpu) } {
|
||||
0 => unsafe { OwnedFd::from_raw_fd(vcpu) },
|
||||
v => return Err(KvmCpuError::CreateVcpuFailed(Error::from_raw_os_error(v))),
|
||||
};
|
||||
|
||||
// Get kvm_run.
|
||||
let vcpu = self.create_cpu(id)?;
|
||||
let cx = unsafe {
|
||||
mmap(
|
||||
null_mut(),
|
||||
@ -231,9 +271,17 @@ impl Hypervisor for Kvm {
|
||||
/// Implementation of [`Hypervisor::CpuErr`].
|
||||
#[derive(Debug, Error)]
|
||||
pub enum KvmCpuError {
|
||||
#[error("failed to create vcpu")]
|
||||
CreateVcpuFailed(#[source] std::io::Error),
|
||||
#[error("couldn't create vCPU")]
|
||||
CreateCpuFailed(#[source] std::io::Error),
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[error("couldn't initialize vCPU")]
|
||||
InitCpuFailed(#[source] std::io::Error),
|
||||
|
||||
#[error("couldn't get a pointer to kvm_run")]
|
||||
GetKvmRunFailed(#[source] std::io::Error),
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[error("couldn't read ID_AA64MMFR0_EL1")]
|
||||
ReadMmfr0Failed(#[source] std::io::Error),
|
||||
}
|
||||
|
@ -722,6 +722,10 @@ enum VmmError {
|
||||
#[error("couldn't get the size of vCPU mmap")]
|
||||
GetMmapSizeFailed(#[source] std::io::Error),
|
||||
|
||||
#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
|
||||
#[error("couldn't get preferred CPU target")]
|
||||
GetPreferredTargetFailed(#[source] std::io::Error),
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
#[error("couldn't create Vulkan device")]
|
||||
CreateVulkanDeviceFailed(#[source] ash::vk::Result),
|
||||
|
13
src/kvm.cpp
13
src/kvm.cpp
@ -32,19 +32,6 @@ extern "C" int kvm_set_user_memory_region(
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" int kvm_create_vcpu(int vm, uint32_t id, int *fd)
|
||||
{
|
||||
auto vcpu = ioctl(vm, KVM_CREATE_VCPU, id);
|
||||
|
||||
if (vcpu < 0) {
|
||||
return errno;
|
||||
}
|
||||
|
||||
*fd = vcpu;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" int kvm_run(int vcpu)
|
||||
{
|
||||
return ioctl(vcpu, KVM_RUN, 0);
|
||||
|
Loading…
Reference in New Issue
Block a user