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