xref: /cloud-hypervisor/hypervisor/src/kvm/aarch64/mod.rs (revision 7d7bfb2034001d4cb15df2ddc56d2d350c8da30f)
1 // Copyright © 2019 Intel Corporation
2 //
3 // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
4 //
5 // Copyright © 2020, Microsoft Corporation
6 //
7 // Copyright 2018-2019 CrowdStrike, Inc.
8 //
9 //
10 
11 ///
12 /// Export generically-named wrappers of kvm-bindings for Unix-based platforms
13 ///
14 use crate::kvm::{KvmError, KvmResult};
15 use kvm_bindings::{
16     kvm_mp_state, kvm_one_reg, kvm_regs, KVM_REG_ARM64, KVM_REG_ARM64_SYSREG,
17     KVM_REG_ARM64_SYSREG_CRM_MASK, KVM_REG_ARM64_SYSREG_CRM_SHIFT, KVM_REG_ARM64_SYSREG_CRN_MASK,
18     KVM_REG_ARM64_SYSREG_CRN_SHIFT, KVM_REG_ARM64_SYSREG_OP0_MASK, KVM_REG_ARM64_SYSREG_OP0_SHIFT,
19     KVM_REG_ARM64_SYSREG_OP1_MASK, KVM_REG_ARM64_SYSREG_OP1_SHIFT, KVM_REG_ARM64_SYSREG_OP2_MASK,
20     KVM_REG_ARM64_SYSREG_OP2_SHIFT, KVM_REG_ARM_COPROC_MASK, KVM_REG_ARM_CORE, KVM_REG_SIZE_MASK,
21     KVM_REG_SIZE_U32, KVM_REG_SIZE_U64,
22 };
23 pub use kvm_bindings::{
24     kvm_one_reg as Register, kvm_regs as StandardRegisters, kvm_vcpu_init as VcpuInit, RegList,
25 };
26 use serde_derive::{Deserialize, Serialize};
27 pub use {kvm_ioctls::Cap, kvm_ioctls::Kvm};
28 
29 // This macro gets the offset of a structure (i.e `str`) member (i.e `field`) without having
30 // an instance of that structure.
31 #[macro_export]
32 macro_rules! offset__of {
33     ($str:ty, $($field:ident)+) => ({
34         let tmp: std::mem::MaybeUninit<$str> = std::mem::MaybeUninit::uninit();
35         let base = tmp.as_ptr();
36 
37         // Avoid warnings when nesting `unsafe` blocks.
38         #[allow(unused_unsafe)]
39         // SAFETY: The pointer is valid and aligned, just not initialised. Using `addr_of` ensures
40         // that we don't actually read from `base` (which would be UB) nor create an intermediate
41         // reference.
42         let member = unsafe { core::ptr::addr_of!((*base).$($field)*) } as *const u8;
43 
44         // Avoid warnings when nesting `unsafe` blocks.
45         #[allow(unused_unsafe)]
46         // SAFETY: The two pointers are within the same allocated object `tmp`. All requirements
47         // from offset_from are upheld.
48         unsafe { member.offset_from(base as *const u8) as usize }
49     });
50 }
51 
52 // Following are macros that help with getting the ID of a aarch64 core register.
53 // The core register are represented by the user_pt_regs structure. Look for it in
54 // arch/arm64/include/uapi/asm/ptrace.h.
55 
56 // Get the ID of a core register
57 #[macro_export]
58 macro_rules! arm64_core_reg_id {
59     ($size: tt, $offset: tt) => {
60         // The core registers of an arm64 machine are represented
61         // in kernel by the `kvm_regs` structure. This structure is a
62         // mix of 32, 64 and 128 bit fields:
63         // struct kvm_regs {
64         //     struct user_pt_regs      regs;
65         //
66         //     __u64                    sp_el1;
67         //     __u64                    elr_el1;
68         //
69         //     __u64                    spsr[KVM_NR_SPSR];
70         //
71         //     struct user_fpsimd_state fp_regs;
72         // };
73         // struct user_pt_regs {
74         //     __u64 regs[31];
75         //     __u64 sp;
76         //     __u64 pc;
77         //     __u64 pstate;
78         // };
79         // The id of a core register can be obtained like this:
80         // offset = id & ~(KVM_REG_ARCH_MASK | KVM_REG_SIZE_MASK | KVM_REG_ARM_CORE). Thus,
81         // id = KVM_REG_ARM64 | KVM_REG_SIZE_U64/KVM_REG_SIZE_U32/KVM_REG_SIZE_U128 | KVM_REG_ARM_CORE | offset
82         KVM_REG_ARM64 as u64
83             | u64::from(KVM_REG_ARM_CORE)
84             | $size
85             | (($offset / mem::size_of::<u32>()) as u64)
86     };
87 }
88 
89 // This macro computes the ID of a specific ARM64 system register similar to how
90 // the kernel C macro does.
91 // https://elixir.bootlin.com/linux/v4.20.17/source/arch/arm64/include/uapi/asm/kvm.h#L203
92 #[macro_export]
93 macro_rules! arm64_sys_reg {
94     ($name: tt, $op0: tt, $op1: tt, $crn: tt, $crm: tt, $op2: tt) => {
95         pub const $name: u64 = KVM_REG_ARM64 as u64
96             | KVM_REG_SIZE_U64 as u64
97             | KVM_REG_ARM64_SYSREG as u64
98             | ((($op0 as u64) << KVM_REG_ARM64_SYSREG_OP0_SHIFT)
99                 & KVM_REG_ARM64_SYSREG_OP0_MASK as u64)
100             | ((($op1 as u64) << KVM_REG_ARM64_SYSREG_OP1_SHIFT)
101                 & KVM_REG_ARM64_SYSREG_OP1_MASK as u64)
102             | ((($crn as u64) << KVM_REG_ARM64_SYSREG_CRN_SHIFT)
103                 & KVM_REG_ARM64_SYSREG_CRN_MASK as u64)
104             | ((($crm as u64) << KVM_REG_ARM64_SYSREG_CRM_SHIFT)
105                 & KVM_REG_ARM64_SYSREG_CRM_MASK as u64)
106             | ((($op2 as u64) << KVM_REG_ARM64_SYSREG_OP2_SHIFT)
107                 & KVM_REG_ARM64_SYSREG_OP2_MASK as u64);
108     };
109 }
110 
111 // Constant imported from the Linux kernel:
112 // https://elixir.bootlin.com/linux/v4.20.17/source/arch/arm64/include/asm/sysreg.h#L135
113 arm64_sys_reg!(MPIDR_EL1, 3, 0, 0, 0, 5);
114 
115 /// Specifies whether a particular register is a system register or not.
116 /// The kernel splits the registers on aarch64 in core registers and system registers.
117 /// So, below we get the system registers by checking that they are not core registers.
118 ///
119 /// # Arguments
120 ///
121 /// * `regid` - The index of the register we are checking.
122 pub fn is_system_register(regid: u64) -> bool {
123     if (regid & KVM_REG_ARM_COPROC_MASK as u64) == KVM_REG_ARM_CORE as u64 {
124         return false;
125     }
126 
127     let size = regid & KVM_REG_SIZE_MASK;
128 
129     assert!(
130         !(size != KVM_REG_SIZE_U32 && size != KVM_REG_SIZE_U64),
131         "Unexpected register size for system register {}",
132         size
133     );
134 
135     true
136 }
137 
138 pub fn check_required_kvm_extensions(kvm: &Kvm) -> KvmResult<()> {
139     if !kvm.check_extension(Cap::SignalMsi) {
140         return Err(KvmError::CapabilityMissing(Cap::SignalMsi));
141     }
142     if !kvm.check_extension(Cap::OneReg) {
143         return Err(KvmError::CapabilityMissing(Cap::OneReg));
144     }
145     Ok(())
146 }
147 
148 #[derive(Clone, Default, Serialize, Deserialize)]
149 pub struct VcpuKvmState {
150     pub mp_state: kvm_mp_state,
151     pub core_regs: kvm_regs,
152     pub sys_regs: Vec<kvm_one_reg>,
153     // We will be using the mpidr for passing it to the VmState.
154     // The VmState will give this away for saving restoring the icc and redistributor
155     // registers.
156     pub mpidr: u64,
157 }
158