xref: /cloud-hypervisor/hypervisor/src/kvm/riscv64/mod.rs (revision 30cf1eed5e63499f3101ed320fc384b59c60fc6b)
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