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