xref: /cloud-hypervisor/hypervisor/src/kvm/riscv64/mod.rs (revision ee0b0d43d8fb4b851be8fdef07dd803c156b3a31)
12df8d2faSRuoqing He // Copyright © 2024 Institute of Software, CAS. All rights reserved.
22df8d2faSRuoqing He //
32df8d2faSRuoqing He // SPDX-License-Identifier: Apache-2.0
42df8d2faSRuoqing He 
5*c5774685SRuoqing He pub mod aia;
6*c5774685SRuoqing He 
72df8d2faSRuoqing He use kvm_bindings::{
82df8d2faSRuoqing He     kvm_mp_state, kvm_one_reg, kvm_riscv_core, KVM_REG_RISCV_CORE, KVM_REG_RISCV_TYPE_MASK,
92df8d2faSRuoqing He     KVM_REG_SIZE_MASK, KVM_REG_SIZE_U64,
102df8d2faSRuoqing He };
112df8d2faSRuoqing He pub use kvm_ioctls::{Cap, Kvm};
122df8d2faSRuoqing He use serde::{Deserialize, Serialize};
132df8d2faSRuoqing He 
142df8d2faSRuoqing He use crate::kvm::{KvmError, KvmResult};
152df8d2faSRuoqing He 
162df8d2faSRuoqing He // This macro gets the offset of a structure (i.e `str`) member (i.e `field`) without having
172df8d2faSRuoqing He // an instance of that structure.
182df8d2faSRuoqing He #[macro_export]
192df8d2faSRuoqing He macro_rules! _offset_of {
202df8d2faSRuoqing He     ($str:ty, $field:ident) => {{
212df8d2faSRuoqing He         let tmp: std::mem::MaybeUninit<$str> = std::mem::MaybeUninit::uninit();
222df8d2faSRuoqing He         let base = tmp.as_ptr();
232df8d2faSRuoqing He 
242df8d2faSRuoqing He         // Avoid warnings when nesting `unsafe` blocks.
252df8d2faSRuoqing He         #[allow(unused_unsafe)]
262df8d2faSRuoqing He         // SAFETY: The pointer is valid and aligned, just not initialised. Using `addr_of` ensures
272df8d2faSRuoqing He         // that we don't actually read from `base` (which would be UB) nor create an intermediate
282df8d2faSRuoqing He         // reference.
292df8d2faSRuoqing He         let member = unsafe { core::ptr::addr_of!((*base).$field) } as *const u8;
302df8d2faSRuoqing He 
312df8d2faSRuoqing He         // Avoid warnings when nesting `unsafe` blocks.
322df8d2faSRuoqing He         #[allow(unused_unsafe)]
332df8d2faSRuoqing He         // SAFETY: The two pointers are within the same allocated object `tmp`. All requirements
342df8d2faSRuoqing He         // from offset_from are upheld.
352df8d2faSRuoqing He         unsafe {
362df8d2faSRuoqing He             member.offset_from(base as *const u8) as usize
372df8d2faSRuoqing He         }
382df8d2faSRuoqing He     }};
392df8d2faSRuoqing He }
402df8d2faSRuoqing He 
412df8d2faSRuoqing He #[macro_export]
422df8d2faSRuoqing He macro_rules! offset_of {
432df8d2faSRuoqing He     ($reg_struct:ty, $field:ident) => {
442df8d2faSRuoqing He         $crate::_offset_of!($reg_struct, $field)
452df8d2faSRuoqing He     };
462df8d2faSRuoqing He     ($outer_reg_struct:ty, $outer_field:ident, $($inner_reg_struct:ty, $inner_field:ident), +) => {
472df8d2faSRuoqing He         $crate::_offset_of!($outer_reg_struct, $outer_field) + offset_of!($($inner_reg_struct, $inner_field), +)
482df8d2faSRuoqing He     };
492df8d2faSRuoqing He }
502df8d2faSRuoqing He 
512df8d2faSRuoqing He // Following are macros that help with getting the ID of a riscv64 register, including config registers, core registers and timer registers.
522df8d2faSRuoqing He // The register of core registers are wrapped in the `user_regs_struct` structure. See:
532df8d2faSRuoqing He // https://elixir.bootlin.com/linux/v6.10/source/arch/riscv/include/uapi/asm/kvm.h#L62
542df8d2faSRuoqing He 
552df8d2faSRuoqing He // Get the ID of a register
562df8d2faSRuoqing He #[macro_export]
572df8d2faSRuoqing He macro_rules! riscv64_reg_id {
582df8d2faSRuoqing He     ($reg_type: tt, $offset: tt) => {
592df8d2faSRuoqing He         // The core registers of an riscv64 machine are represented
602df8d2faSRuoqing He         // in kernel by the `kvm_riscv_core` structure:
612df8d2faSRuoqing He         //
622df8d2faSRuoqing He         // struct kvm_riscv_core {
632df8d2faSRuoqing He         //     struct user_regs_struct regs;
642df8d2faSRuoqing He         //     unsigned long mode;
652df8d2faSRuoqing He         // };
662df8d2faSRuoqing He         //
672df8d2faSRuoqing He         // struct user_regs_struct {
682df8d2faSRuoqing He         //     unsigned long pc;
692df8d2faSRuoqing He         //     unsigned long ra;
702df8d2faSRuoqing He         //     unsigned long sp;
712df8d2faSRuoqing He         //     unsigned long gp;
722df8d2faSRuoqing He         //     unsigned long tp;
732df8d2faSRuoqing He         //     unsigned long t0;
742df8d2faSRuoqing He         //     unsigned long t1;
752df8d2faSRuoqing He         //     unsigned long t2;
762df8d2faSRuoqing He         //     unsigned long s0;
772df8d2faSRuoqing He         //     unsigned long s1;
782df8d2faSRuoqing He         //     unsigned long a0;
792df8d2faSRuoqing He         //     unsigned long a1;
802df8d2faSRuoqing He         //     unsigned long a2;
812df8d2faSRuoqing He         //     unsigned long a3;
822df8d2faSRuoqing He         //     unsigned long a4;
832df8d2faSRuoqing He         //     unsigned long a5;
842df8d2faSRuoqing He         //     unsigned long a6;
852df8d2faSRuoqing He         //     unsigned long a7;
862df8d2faSRuoqing He         //     unsigned long s2;
872df8d2faSRuoqing He         //     unsigned long s3;
882df8d2faSRuoqing He         //     unsigned long s4;
892df8d2faSRuoqing He         //     unsigned long s5;
902df8d2faSRuoqing He         //     unsigned long s6;
912df8d2faSRuoqing He         //     unsigned long s7;
922df8d2faSRuoqing He         //     unsigned long s8;
932df8d2faSRuoqing He         //     unsigned long s9;
942df8d2faSRuoqing He         //     unsigned long s10;
952df8d2faSRuoqing He         //     unsigned long s11;
962df8d2faSRuoqing He         //     unsigned long t3;
972df8d2faSRuoqing He         //     unsigned long t4;
982df8d2faSRuoqing He         //     unsigned long t5;
992df8d2faSRuoqing He         //     unsigned long t6;
1002df8d2faSRuoqing He         // };
1012df8d2faSRuoqing He         // The id of a core register can be obtained like this: offset = id &
1022df8d2faSRuoqing He         // ~(KVM_REG_ARCH_MASK | KVM_REG_SIZE_MASK | KVM_REG_RISCV_CORE). Thus,
1032df8d2faSRuoqing He         // id = KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_CORE | offset
1042df8d2faSRuoqing He         //
1052df8d2faSRuoqing He         // To generalize, the id of a register can be obtained by:
1062df8d2faSRuoqing He         // id = KVM_REG_RISCV | KVM_REG_SIZE_U64 |
1072df8d2faSRuoqing He         //      KVM_REG_RISCV_CORE/KVM_REG_RISCV_CONFIG/KVM_REG_RISCV_TIMER |
1082df8d2faSRuoqing He         //      offset
1092df8d2faSRuoqing He         kvm_bindings::KVM_REG_RISCV as u64
1102df8d2faSRuoqing He             | u64::from($reg_type)
1112df8d2faSRuoqing He             | u64::from(kvm_bindings::KVM_REG_SIZE_U64)
1122df8d2faSRuoqing He             | (($offset / std::mem::size_of::<u64>()) as u64)
1132df8d2faSRuoqing He     };
1142df8d2faSRuoqing He }
1152df8d2faSRuoqing He 
1162df8d2faSRuoqing He /// Specifies whether a particular register is a core register or not.
1172df8d2faSRuoqing He ///
1182df8d2faSRuoqing He /// # Arguments
1192df8d2faSRuoqing He ///
1202df8d2faSRuoqing He /// * `regid` - The index of the register we are checking.
is_non_core_register(regid: u64) -> bool1212df8d2faSRuoqing He pub fn is_non_core_register(regid: u64) -> bool {
1222df8d2faSRuoqing He     if (regid & KVM_REG_RISCV_TYPE_MASK as u64) == KVM_REG_RISCV_CORE as u64 {
1232df8d2faSRuoqing He         return false;
1242df8d2faSRuoqing He     }
1252df8d2faSRuoqing He 
1262df8d2faSRuoqing He     let size = regid & KVM_REG_SIZE_MASK;
1272df8d2faSRuoqing He 
1282df8d2faSRuoqing He     assert!(
1292df8d2faSRuoqing He         size == KVM_REG_SIZE_U64,
1302df8d2faSRuoqing He         "Unexpected register size for system register {size}"
1312df8d2faSRuoqing He     );
1322df8d2faSRuoqing He 
1332df8d2faSRuoqing He     true
1342df8d2faSRuoqing He }
1352df8d2faSRuoqing He 
check_required_kvm_extensions(kvm: &Kvm) -> KvmResult<()>1362df8d2faSRuoqing He pub fn check_required_kvm_extensions(kvm: &Kvm) -> KvmResult<()> {
1372df8d2faSRuoqing He     macro_rules! check_extension {
1382df8d2faSRuoqing He         ($cap:expr) => {
1392df8d2faSRuoqing He             if !kvm.check_extension($cap) {
1402df8d2faSRuoqing He                 return Err(KvmError::CapabilityMissing($cap));
1412df8d2faSRuoqing He             }
1422df8d2faSRuoqing He         };
1432df8d2faSRuoqing He     }
1442df8d2faSRuoqing He 
1452df8d2faSRuoqing He     // SetGuestDebug is required but some kernels have it implemented without the capability flag.
1462df8d2faSRuoqing He     check_extension!(Cap::ImmediateExit);
1472df8d2faSRuoqing He     check_extension!(Cap::Ioeventfd);
1482df8d2faSRuoqing He     check_extension!(Cap::Irqchip);
1492df8d2faSRuoqing He     check_extension!(Cap::Irqfd);
1502df8d2faSRuoqing He     check_extension!(Cap::IrqRouting);
1512df8d2faSRuoqing He     check_extension!(Cap::MpState);
1522df8d2faSRuoqing He     check_extension!(Cap::OneReg);
1532df8d2faSRuoqing He     check_extension!(Cap::UserMemory);
1542df8d2faSRuoqing He     Ok(())
1552df8d2faSRuoqing He }
1562df8d2faSRuoqing He 
1572df8d2faSRuoqing He #[derive(Clone, Default, Serialize, Deserialize)]
1582df8d2faSRuoqing He pub struct VcpuKvmState {
1592df8d2faSRuoqing He     pub mp_state: kvm_mp_state,
1602df8d2faSRuoqing He     pub core_regs: kvm_riscv_core,
1612df8d2faSRuoqing He     pub non_core_regs: Vec<kvm_one_reg>,
1622df8d2faSRuoqing He }
163