hypervisor: kvm: Introduce riscv64 register g/set

Implement macros to calculate register ID on riscv64, definition of
RISC-V `VcpuKvmState`.

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
This commit is contained in:
Ruoqing He 2024-10-09 20:39:39 +08:00 committed by Rob Bradford
parent dd00bd6ef2
commit 2df8d2fa1e
2 changed files with 164 additions and 0 deletions

View File

@ -65,6 +65,9 @@ use crate::{
// aarch64 dependencies
#[cfg(target_arch = "aarch64")]
pub mod aarch64;
// riscv64 dependencies
#[cfg(target_arch = "riscv64")]
pub mod riscv64;
#[cfg(target_arch = "aarch64")]
use std::mem;

View File

@ -0,0 +1,161 @@
// Copyright © 2024 Institute of Software, CAS. All rights reserved.
//
// SPDX-License-Identifier: Apache-2.0
use kvm_bindings::{
kvm_mp_state, kvm_one_reg, kvm_riscv_core, KVM_REG_RISCV_CORE, KVM_REG_RISCV_TYPE_MASK,
KVM_REG_SIZE_MASK, KVM_REG_SIZE_U64,
};
pub use kvm_bindings::{kvm_one_reg as Register, RegList};
pub use kvm_ioctls::{Cap, Kvm};
use serde::{Deserialize, Serialize};
use crate::kvm::{KvmError, KvmResult};
// This macro gets the offset of a structure (i.e `str`) member (i.e `field`) without having
// an instance of that structure.
#[macro_export]
macro_rules! _offset_of {
($str:ty, $field:ident) => {{
let tmp: std::mem::MaybeUninit<$str> = std::mem::MaybeUninit::uninit();
let base = tmp.as_ptr();
// Avoid warnings when nesting `unsafe` blocks.
#[allow(unused_unsafe)]
// SAFETY: The pointer is valid and aligned, just not initialised. Using `addr_of` ensures
// that we don't actually read from `base` (which would be UB) nor create an intermediate
// reference.
let member = unsafe { core::ptr::addr_of!((*base).$field) } as *const u8;
// Avoid warnings when nesting `unsafe` blocks.
#[allow(unused_unsafe)]
// SAFETY: The two pointers are within the same allocated object `tmp`. All requirements
// from offset_from are upheld.
unsafe {
member.offset_from(base as *const u8) as usize
}
}};
}
#[macro_export]
macro_rules! offset_of {
($reg_struct:ty, $field:ident) => {
$crate::_offset_of!($reg_struct, $field)
};
($outer_reg_struct:ty, $outer_field:ident, $($inner_reg_struct:ty, $inner_field:ident), +) => {
$crate::_offset_of!($outer_reg_struct, $outer_field) + offset_of!($($inner_reg_struct, $inner_field), +)
};
}
// Following are macros that help with getting the ID of a riscv64 register, including config registers, core registers and timer registers.
// The register of core registers are wrapped in the `user_regs_struct` structure. See:
// https://elixir.bootlin.com/linux/v6.10/source/arch/riscv/include/uapi/asm/kvm.h#L62
// Get the ID of a register
#[macro_export]
macro_rules! riscv64_reg_id {
($reg_type: tt, $offset: tt) => {
// The core registers of an riscv64 machine are represented
// in kernel by the `kvm_riscv_core` structure:
//
// struct kvm_riscv_core {
// struct user_regs_struct regs;
// unsigned long mode;
// };
//
// struct user_regs_struct {
// unsigned long pc;
// unsigned long ra;
// unsigned long sp;
// unsigned long gp;
// unsigned long tp;
// unsigned long t0;
// unsigned long t1;
// unsigned long t2;
// unsigned long s0;
// unsigned long s1;
// unsigned long a0;
// unsigned long a1;
// unsigned long a2;
// unsigned long a3;
// unsigned long a4;
// unsigned long a5;
// unsigned long a6;
// unsigned long a7;
// unsigned long s2;
// unsigned long s3;
// unsigned long s4;
// unsigned long s5;
// unsigned long s6;
// unsigned long s7;
// unsigned long s8;
// unsigned long s9;
// unsigned long s10;
// unsigned long s11;
// unsigned long t3;
// unsigned long t4;
// unsigned long t5;
// unsigned long t6;
// };
// The id of a core register can be obtained like this: offset = id &
// ~(KVM_REG_ARCH_MASK | KVM_REG_SIZE_MASK | KVM_REG_RISCV_CORE). Thus,
// id = KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_CORE | offset
//
// To generalize, the id of a register can be obtained by:
// id = KVM_REG_RISCV | KVM_REG_SIZE_U64 |
// KVM_REG_RISCV_CORE/KVM_REG_RISCV_CONFIG/KVM_REG_RISCV_TIMER |
// offset
kvm_bindings::KVM_REG_RISCV as u64
| u64::from($reg_type)
| u64::from(kvm_bindings::KVM_REG_SIZE_U64)
| (($offset / std::mem::size_of::<u64>()) as u64)
};
}
/// Specifies whether a particular register is a core register or not.
///
/// # Arguments
///
/// * `regid` - The index of the register we are checking.
pub fn is_non_core_register(regid: u64) -> bool {
if (regid & KVM_REG_RISCV_TYPE_MASK as u64) == KVM_REG_RISCV_CORE as u64 {
return false;
}
let size = regid & KVM_REG_SIZE_MASK;
assert!(
size == KVM_REG_SIZE_U64,
"Unexpected register size for system register {size}"
);
true
}
pub fn check_required_kvm_extensions(kvm: &Kvm) -> KvmResult<()> {
macro_rules! check_extension {
($cap:expr) => {
if !kvm.check_extension($cap) {
return Err(KvmError::CapabilityMissing($cap));
}
};
}
// SetGuestDebug is required but some kernels have it implemented without the capability flag.
check_extension!(Cap::ImmediateExit);
check_extension!(Cap::Ioeventfd);
check_extension!(Cap::Irqchip);
check_extension!(Cap::Irqfd);
check_extension!(Cap::IrqRouting);
check_extension!(Cap::MpState);
check_extension!(Cap::OneReg);
check_extension!(Cap::UserMemory);
Ok(())
}
#[derive(Clone, Default, Serialize, Deserialize)]
pub struct VcpuKvmState {
pub mp_state: kvm_mp_state,
pub core_regs: kvm_riscv_core,
pub non_core_regs: Vec<kvm_one_reg>,
}