Checks KVM_CAP_SET_GUEST_DEBUG (#1039)
Some checks are pending
Development Build / Build (push) Waiting to run
Development Build / Update PRs (push) Waiting to run

This commit is contained in:
Putta Khunchalee 2024-10-17 02:47:28 +07:00 committed by GitHub
parent 2372243b41
commit ae2bc802a3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 109 additions and 98 deletions

View File

@ -17,6 +17,7 @@ pub const KVM_ARM_PREFERRED_TARGET: c_ulong = _IOR::<KvmVcpuInit>(KVMIO, 0xaf);
pub const KVM_API_VERSION: c_int = 12;
pub const KVM_CAP_SET_GUEST_DEBUG: c_int = 23;
pub const KVM_CAP_MAX_VCPUS: c_int = 66;
#[cfg(target_arch = "aarch64")]
pub const KVM_CAP_ONE_REG: c_int = 70;

View File

@ -1,13 +1,12 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
use self::cpu::KvmCpu;
use self::ffi::{
KvmUserspaceMemoryRegion, KVM_API_VERSION, KVM_CAP_MAX_VCPUS, KVM_CHECK_EXTENSION,
KVM_CREATE_VCPU, KVM_CREATE_VM, KVM_GET_API_VERSION, KVM_GET_VCPU_MMAP_SIZE,
KVM_SET_USER_MEMORY_REGION,
KvmUserspaceMemoryRegion, KVM_API_VERSION, KVM_CAP_MAX_VCPUS, KVM_CAP_SET_GUEST_DEBUG,
KVM_CHECK_EXTENSION, KVM_CREATE_VCPU, KVM_CREATE_VM, KVM_GET_API_VERSION,
KVM_GET_VCPU_MMAP_SIZE, KVM_SET_USER_MEMORY_REGION,
};
use super::{CpuFeats, Hypervisor};
use crate::vmm::ram::Ram;
use crate::vmm::VmmError;
use libc::{ioctl, mmap, open, MAP_FAILED, MAP_PRIVATE, O_RDWR, PROT_READ, PROT_WRITE};
use std::io::Error;
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd};
@ -22,12 +21,12 @@ mod cpu;
mod ffi;
mod run;
pub fn new(cpu: usize, ram: Ram) -> Result<Kvm, VmmError> {
pub fn new(cpu: usize, ram: Ram, debug: bool) -> Result<Kvm, KvmError> {
// Open KVM device.
let kvm = unsafe { open(b"/dev/kvm\0".as_ptr().cast(), O_RDWR) };
if kvm < 0 {
return Err(VmmError::OpenKvmFailed(Error::last_os_error()));
return Err(KvmError::OpenKvmFailed(Error::last_os_error()));
}
// Check KVM version.
@ -35,18 +34,18 @@ pub fn new(cpu: usize, ram: Ram) -> Result<Kvm, VmmError> {
let version = unsafe { ioctl(kvm.as_raw_fd(), KVM_GET_API_VERSION) };
if version < 0 {
return Err(VmmError::GetKvmVersionFailed(Error::last_os_error()));
return Err(KvmError::GetKvmVersionFailed(Error::last_os_error()));
} else if version != KVM_API_VERSION {
return Err(VmmError::KvmVersionMismatched);
return Err(KvmError::KvmVersionMismatched);
}
// Check max CPU.
let max = unsafe { ioctl(kvm.as_raw_fd(), KVM_CHECK_EXTENSION, KVM_CAP_MAX_VCPUS) };
if max < 0 {
return Err(VmmError::GetMaxCpuFailed(Error::last_os_error()));
return Err(KvmError::GetMaxCpuFailed(Error::last_os_error()));
} else if TryInto::<usize>::try_into(max).unwrap() < cpu {
return Err(VmmError::MaxCpuTooLow);
return Err(KvmError::MaxCpuTooLow(cpu));
}
// On AArch64 we need KVM_SET_ONE_REG and KVM_GET_ONE_REG.
@ -58,14 +57,27 @@ pub fn new(cpu: usize, ram: Ram) -> Result<Kvm, VmmError> {
self::ffi::KVM_CAP_ONE_REG,
) <= 0
} {
return Err(VmmError::NoKvmOneReg);
return Err(KvmError::NoKvmOneReg);
}
// Check if debug supported.
if debug
&& unsafe {
ioctl(
kvm.as_raw_fd(),
KVM_CHECK_EXTENSION,
KVM_CAP_SET_GUEST_DEBUG,
) <= 0
}
{
return Err(KvmError::DebugNotSupported);
}
// Get size of CPU context.
let vcpu_mmap_size = unsafe { ioctl(kvm.as_raw_fd(), KVM_GET_VCPU_MMAP_SIZE, 0) };
if vcpu_mmap_size < 0 {
return Err(VmmError::GetMmapSizeFailed(Error::last_os_error()));
return Err(KvmError::GetMmapSizeFailed(Error::last_os_error()));
}
// Create a VM.
@ -75,7 +87,7 @@ pub fn new(cpu: usize, ram: Ram) -> Result<Kvm, VmmError> {
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()));
return Err(KvmError::GetPreferredTargetFailed(Error::last_os_error()));
}
v
@ -91,7 +103,7 @@ pub fn new(cpu: usize, ram: Ram) -> Result<Kvm, VmmError> {
};
if unsafe { ioctl(vm.as_raw_fd(), KVM_SET_USER_MEMORY_REGION, &mr) } < 0 {
return Err(VmmError::MapRamFailed(Error::last_os_error()));
return Err(KvmError::MapRamFailed(Error::last_os_error()));
}
// AArch64 require all CPU to be created before calling KVM_ARM_VCPU_INIT.
@ -101,7 +113,7 @@ pub fn new(cpu: usize, ram: Ram) -> Result<Kvm, VmmError> {
let cpu = unsafe { ioctl(vm.as_raw_fd(), KVM_CREATE_VCPU, id) };
if cpu < 0 {
return Err(VmmError::CreateCpuFailed(id, Error::last_os_error()));
return Err(KvmError::CreateCpuFailed(id, Error::last_os_error()));
}
cpus.push(Mutex::new(unsafe { OwnedFd::from_raw_fd(cpu) }));
@ -115,7 +127,7 @@ pub fn new(cpu: usize, ram: Ram) -> Result<Kvm, VmmError> {
let cpu = cpu.get_mut().unwrap();
if unsafe { ioctl(cpu.as_raw_fd(), KVM_ARM_VCPU_INIT, &preferred_target) < 0 } {
return Err(VmmError::InitCpuFailed(i, Error::last_os_error()));
return Err(KvmError::InitCpuFailed(i, Error::last_os_error()));
}
}
@ -130,7 +142,7 @@ pub fn new(cpu: usize, ram: Ram) -> Result<Kvm, VmmError> {
}
#[cfg(target_arch = "aarch64")]
fn create_vm(kvm: BorrowedFd) -> Result<OwnedFd, VmmError> {
fn create_vm(kvm: BorrowedFd) -> Result<OwnedFd, KvmError> {
use self::ffi::{KVM_CAP_ARM_VM_IPA_SIZE, KVM_VM_TYPE_ARM_IPA_SIZE};
// Check KVM_CAP_ARM_VM_IPA_SIZE. We cannot use default machine type on AArch64 otherwise
@ -145,34 +157,34 @@ fn create_vm(kvm: BorrowedFd) -> Result<OwnedFd, VmmError> {
};
if limit <= 0 {
return Err(VmmError::NoVmIpaSize);
return Err(KvmError::NoVmIpaSize);
} else if limit < 36 {
return Err(VmmError::PhysicalAddressTooSmall);
return Err(KvmError::PhysicalAddressTooSmall);
}
// Create a VM.
let vm = unsafe { ioctl(kvm.as_raw_fd(), KVM_CREATE_VM, KVM_VM_TYPE_ARM_IPA_SIZE(36)) };
if vm < 0 {
Err(VmmError::CreateVmFailed(Error::last_os_error()))
Err(KvmError::CreateVmFailed(Error::last_os_error()))
} else {
Ok(unsafe { OwnedFd::from_raw_fd(vm) })
}
}
#[cfg(target_arch = "x86_64")]
fn create_vm(kvm: BorrowedFd) -> Result<OwnedFd, VmmError> {
fn create_vm(kvm: BorrowedFd) -> Result<OwnedFd, KvmError> {
let vm = unsafe { ioctl(kvm.as_raw_fd(), KVM_CREATE_VM, 0) };
if vm < 0 {
Err(VmmError::CreateVmFailed(Error::last_os_error()))
Err(KvmError::CreateVmFailed(Error::last_os_error()))
} else {
Ok(unsafe { OwnedFd::from_raw_fd(vm) })
}
}
#[cfg(target_arch = "aarch64")]
fn load_feats(cpu: BorrowedFd) -> Result<CpuFeats, VmmError> {
fn load_feats(cpu: BorrowedFd) -> Result<CpuFeats, KvmError> {
use self::ffi::{KvmOneReg, ARM64_SYS_REG, KVM_GET_ONE_REG};
use crate::vmm::hv::{Mmfr0, Mmfr1, Mmfr2};
@ -184,7 +196,7 @@ fn load_feats(cpu: BorrowedFd) -> Result<CpuFeats, VmmError> {
};
if unsafe { ioctl(cpu.as_raw_fd(), KVM_GET_ONE_REG, &mut req) < 0 } {
return Err(VmmError::ReadMmfr0Failed(Error::last_os_error()));
return Err(KvmError::ReadMmfr0Failed(Error::last_os_error()));
}
// ID_AA64MMFR1_EL1.
@ -195,7 +207,7 @@ fn load_feats(cpu: BorrowedFd) -> Result<CpuFeats, VmmError> {
};
if unsafe { ioctl(cpu.as_raw_fd(), KVM_GET_ONE_REG, &mut req) < 0 } {
return Err(VmmError::ReadMmfr1Failed(Error::last_os_error()));
return Err(KvmError::ReadMmfr1Failed(Error::last_os_error()));
}
// ID_AA64MMFR2_EL1.
@ -206,7 +218,7 @@ fn load_feats(cpu: BorrowedFd) -> Result<CpuFeats, VmmError> {
};
if unsafe { ioctl(cpu.as_raw_fd(), KVM_GET_ONE_REG, &mut req) < 0 } {
return Err(VmmError::ReadMmfr2Failed(Error::last_os_error()));
return Err(KvmError::ReadMmfr2Failed(Error::last_os_error()));
}
Ok(CpuFeats {
@ -217,7 +229,7 @@ fn load_feats(cpu: BorrowedFd) -> Result<CpuFeats, VmmError> {
}
#[cfg(target_arch = "x86_64")]
fn load_feats(_: BorrowedFd) -> Result<CpuFeats, VmmError> {
fn load_feats(_: BorrowedFd) -> Result<CpuFeats, KvmError> {
Ok(CpuFeats {})
}
@ -276,6 +288,72 @@ impl Hypervisor for Kvm {
}
}
/// Represents an error when [`Kvm`] fails to initialize.
#[derive(Debug, Error)]
pub enum KvmError {
#[error("couldn't open /dev/kvm")]
OpenKvmFailed(#[source] Error),
#[error("couldn't get KVM version")]
GetKvmVersionFailed(#[source] Error),
#[error("unexpected KVM version")]
KvmVersionMismatched,
#[error("couldn't get maximum number of vCPU for a VM")]
GetMaxCpuFailed(#[source] Error),
#[error("your OS does not support {0} vCPU on a VM")]
MaxCpuTooLow(usize),
#[cfg(target_arch = "aarch64")]
#[error("your OS does not support KVM_CAP_ONE_REG")]
NoKvmOneReg,
#[error("your OS does not support KVM_CAP_SET_GUEST_DEBUG")]
DebugNotSupported,
#[error("couldn't get the size of vCPU mmap")]
GetMmapSizeFailed(#[source] Error),
#[cfg(target_arch = "aarch64")]
#[error("your OS does not support KVM_CAP_ARM_VM_IPA_SIZE")]
NoVmIpaSize,
#[cfg(target_arch = "aarch64")]
#[error("physical address supported by your CPU too small")]
PhysicalAddressTooSmall,
#[error("couldn't create a VM")]
CreateVmFailed(#[source] Error),
#[cfg(target_arch = "aarch64")]
#[error("couldn't get preferred CPU target")]
GetPreferredTargetFailed(#[source] Error),
#[error("couldn't map the RAM to the VM")]
MapRamFailed(#[source] Error),
#[error("couldn't create vCPU #{0}")]
CreateCpuFailed(usize, #[source] Error),
#[cfg(target_arch = "aarch64")]
#[error("couldn't initialize vCPU #{0}")]
InitCpuFailed(usize, #[source] Error),
#[cfg(all(target_arch = "aarch64"))]
#[error("couldn't read ID_AA64MMFR0_EL1")]
ReadMmfr0Failed(#[source] Error),
#[cfg(all(target_arch = "aarch64"))]
#[error("couldn't read ID_AA64MMFR1_EL1")]
ReadMmfr1Failed(#[source] Error),
#[cfg(target_arch = "aarch64")]
#[error("couldn't read ID_AA64MMFR2_EL1")]
ReadMmfr2Failed(#[source] Error),
}
/// Implementation of [`Hypervisor::CpuErr`].
#[derive(Debug, Error)]
pub enum KvmCpuError {

View File

@ -18,7 +18,7 @@ use thiserror::Error;
mod cpu;
pub fn new(_: usize, ram: Ram) -> Result<Hvf, VmmError> {
pub fn new(_: usize, ram: Ram, debug: bool) -> Result<Hvf, VmmError> {
// Create a VM.
let ret = unsafe { hv_vm_create(null_mut()) };
let mut hv = match NonZero::new(ret) {

View File

@ -10,7 +10,7 @@ use windows_sys::core::HRESULT;
mod cpu;
mod partition;
pub fn new(cpu: usize, ram: Ram) -> Result<Whp, VmmError> {
pub fn new(cpu: usize, ram: Ram, debug: bool) -> Result<Whp, VmmError> {
// Setup a partition.
let mut part = Partition::new().map_err(VmmError::CreatePartitionFailed)?;

View File

@ -342,7 +342,7 @@ pub unsafe extern "C" fn vmm_start(
let devices = Arc::new(setup_devices(ram.len().get(), block_size, event));
// Setup hypervisor.
let mut hv = match self::hv::new(8, ram) {
let mut hv = match self::hv::new(8, ram, debugger.is_some()) {
Ok(v) => v,
Err(e) => {
*err = RustError::with_source("couldn't setup a hypervisor", e).into_c();
@ -696,74 +696,6 @@ pub enum DebugResult {
/// Represents an error when [`vmm_new()`] fails.
#[derive(Debug, Error)]
enum VmmError {
#[cfg(target_os = "linux")]
#[error("couldn't get maximum number of CPU for a VM")]
GetMaxCpuFailed(#[source] std::io::Error),
#[cfg(not(target_os = "macos"))]
#[error("your OS does not support 8 vCPU on a VM")]
MaxCpuTooLow,
#[cfg(target_os = "linux")]
#[error("couldn't open /dev/kvm")]
OpenKvmFailed(#[source] std::io::Error),
#[cfg(target_os = "linux")]
#[error("couldn't get KVM version")]
GetKvmVersionFailed(#[source] std::io::Error),
#[cfg(target_os = "linux")]
#[error("unexpected KVM version")]
KvmVersionMismatched,
#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
#[error("your OS does not support KVM_CAP_ONE_REG")]
NoKvmOneReg,
#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
#[error("your OS does not support KVM_CAP_ARM_VM_IPA_SIZE")]
NoVmIpaSize,
#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
#[error("physical address supported by your CPU too small")]
PhysicalAddressTooSmall,
#[cfg(target_os = "linux")]
#[error("couldn't create a VM")]
CreateVmFailed(#[source] std::io::Error),
#[cfg(target_os = "linux")]
#[error("couldn't map the RAM to the VM")]
MapRamFailed(#[source] std::io::Error),
#[cfg(target_os = "linux")]
#[error("couldn't create vCPU #{0}")]
CreateCpuFailed(usize, #[source] std::io::Error),
#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
#[error("couldn't initialize vCPU #{0}")]
InitCpuFailed(usize, #[source] std::io::Error),
#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
#[error("couldn't read ID_AA64MMFR0_EL1")]
ReadMmfr0Failed(#[source] std::io::Error),
#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
#[error("couldn't read ID_AA64MMFR1_EL1")]
ReadMmfr1Failed(#[source] std::io::Error),
#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
#[error("couldn't read ID_AA64MMFR2_EL1")]
ReadMmfr2Failed(#[source] std::io::Error),
#[cfg(target_os = "linux")]
#[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),