1 // Copyright © 2024 Institute of Software, CAS. All rights reserved. 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 5 pub mod aia; 6 7 use kvm_bindings::{ 8 kvm_mp_state, kvm_one_reg, kvm_riscv_core, KVM_REG_RISCV_CORE, KVM_REG_RISCV_TYPE_MASK, 9 KVM_REG_SIZE_MASK, KVM_REG_SIZE_U64, 10 }; 11 pub use kvm_ioctls::{Cap, Kvm}; 12 use serde::{Deserialize, Serialize}; 13 14 use crate::kvm::{KvmError, KvmResult}; 15 16 // This macro gets the offset of a structure (i.e `str`) member (i.e `field`) without having 17 // an instance of that structure. 18 #[macro_export] 19 macro_rules! _offset_of { 20 ($str:ty, $field:ident) => {{ 21 let tmp: std::mem::MaybeUninit<$str> = std::mem::MaybeUninit::uninit(); 22 let base = tmp.as_ptr(); 23 24 // Avoid warnings when nesting `unsafe` blocks. 25 #[allow(unused_unsafe)] 26 // SAFETY: The pointer is valid and aligned, just not initialised. Using `addr_of` ensures 27 // that we don't actually read from `base` (which would be UB) nor create an intermediate 28 // reference. 29 let member = unsafe { core::ptr::addr_of!((*base).$field) } as *const u8; 30 31 // Avoid warnings when nesting `unsafe` blocks. 32 #[allow(unused_unsafe)] 33 // SAFETY: The two pointers are within the same allocated object `tmp`. All requirements 34 // from offset_from are upheld. 35 unsafe { 36 member.offset_from(base as *const u8) as usize 37 } 38 }}; 39 } 40 41 #[macro_export] 42 macro_rules! offset_of { 43 ($reg_struct:ty, $field:ident) => { 44 $crate::_offset_of!($reg_struct, $field) 45 }; 46 ($outer_reg_struct:ty, $outer_field:ident, $($inner_reg_struct:ty, $inner_field:ident), +) => { 47 $crate::_offset_of!($outer_reg_struct, $outer_field) + offset_of!($($inner_reg_struct, $inner_field), +) 48 }; 49 } 50 51 // Following are macros that help with getting the ID of a riscv64 register, including config registers, core registers and timer registers. 52 // The register of core registers are wrapped in the `user_regs_struct` structure. See: 53 // https://elixir.bootlin.com/linux/v6.10/source/arch/riscv/include/uapi/asm/kvm.h#L62 54 55 // Get the ID of a register 56 #[macro_export] 57 macro_rules! riscv64_reg_id { 58 ($reg_type: tt, $offset: tt) => { 59 // The core registers of an riscv64 machine are represented 60 // in kernel by the `kvm_riscv_core` structure: 61 // 62 // struct kvm_riscv_core { 63 // struct user_regs_struct regs; 64 // unsigned long mode; 65 // }; 66 // 67 // struct user_regs_struct { 68 // unsigned long pc; 69 // unsigned long ra; 70 // unsigned long sp; 71 // unsigned long gp; 72 // unsigned long tp; 73 // unsigned long t0; 74 // unsigned long t1; 75 // unsigned long t2; 76 // unsigned long s0; 77 // unsigned long s1; 78 // unsigned long a0; 79 // unsigned long a1; 80 // unsigned long a2; 81 // unsigned long a3; 82 // unsigned long a4; 83 // unsigned long a5; 84 // unsigned long a6; 85 // unsigned long a7; 86 // unsigned long s2; 87 // unsigned long s3; 88 // unsigned long s4; 89 // unsigned long s5; 90 // unsigned long s6; 91 // unsigned long s7; 92 // unsigned long s8; 93 // unsigned long s9; 94 // unsigned long s10; 95 // unsigned long s11; 96 // unsigned long t3; 97 // unsigned long t4; 98 // unsigned long t5; 99 // unsigned long t6; 100 // }; 101 // The id of a core register can be obtained like this: offset = id & 102 // ~(KVM_REG_ARCH_MASK | KVM_REG_SIZE_MASK | KVM_REG_RISCV_CORE). Thus, 103 // id = KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_CORE | offset 104 // 105 // To generalize, the id of a register can be obtained by: 106 // id = KVM_REG_RISCV | KVM_REG_SIZE_U64 | 107 // KVM_REG_RISCV_CORE/KVM_REG_RISCV_CONFIG/KVM_REG_RISCV_TIMER | 108 // offset 109 kvm_bindings::KVM_REG_RISCV as u64 110 | u64::from($reg_type) 111 | u64::from(kvm_bindings::KVM_REG_SIZE_U64) 112 | (($offset / std::mem::size_of::<u64>()) as u64) 113 }; 114 } 115 116 /// Specifies whether a particular register is a core register or not. 117 /// 118 /// # Arguments 119 /// 120 /// * `regid` - The index of the register we are checking. 121 pub fn is_non_core_register(regid: u64) -> bool { 122 if (regid & KVM_REG_RISCV_TYPE_MASK as u64) == KVM_REG_RISCV_CORE as u64 { 123 return false; 124 } 125 126 let size = regid & KVM_REG_SIZE_MASK; 127 128 assert!( 129 size == KVM_REG_SIZE_U64, 130 "Unexpected register size for system register {size}" 131 ); 132 133 true 134 } 135 136 pub fn check_required_kvm_extensions(kvm: &Kvm) -> KvmResult<()> { 137 macro_rules! check_extension { 138 ($cap:expr) => { 139 if !kvm.check_extension($cap) { 140 return Err(KvmError::CapabilityMissing($cap)); 141 } 142 }; 143 } 144 145 // SetGuestDebug is required but some kernels have it implemented without the capability flag. 146 check_extension!(Cap::ImmediateExit); 147 check_extension!(Cap::Ioeventfd); 148 check_extension!(Cap::Irqchip); 149 check_extension!(Cap::Irqfd); 150 check_extension!(Cap::IrqRouting); 151 check_extension!(Cap::MpState); 152 check_extension!(Cap::OneReg); 153 check_extension!(Cap::UserMemory); 154 Ok(()) 155 } 156 157 #[derive(Clone, Default, Serialize, Deserialize)] 158 pub struct VcpuKvmState { 159 pub mp_state: kvm_mp_state, 160 pub core_regs: kvm_riscv_core, 161 pub non_core_regs: Vec<kvm_one_reg>, 162 } 163