1 // Copyright 2023, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 //! Wrappers around hypervisor back-ends.
16
17 mod common;
18 mod geniezone;
19 mod gunyah;
20 mod kvm;
21
22 use super::{Error, Result};
23 use alloc::boxed::Box;
24 use common::Hypervisor;
25 pub use common::{DeviceAssigningHypervisor, MemSharingHypervisor, MmioGuardedHypervisor};
26 pub use geniezone::GeniezoneError;
27 use geniezone::GeniezoneHypervisor;
28 use gunyah::GunyahHypervisor;
29 pub use kvm::KvmError;
30 use kvm::{ProtectedKvmHypervisor, RegularKvmHypervisor};
31 use once_cell::race::OnceBox;
32 use smccc::hvc64;
33 use uuid::Uuid;
34
35 enum HypervisorBackend {
36 RegularKvm,
37 Gunyah,
38 Geniezone,
39 ProtectedKvm,
40 }
41
42 impl HypervisorBackend {
get_hypervisor(&self) -> &'static dyn Hypervisor43 fn get_hypervisor(&self) -> &'static dyn Hypervisor {
44 match self {
45 Self::RegularKvm => &RegularKvmHypervisor,
46 Self::Gunyah => &GunyahHypervisor,
47 Self::Geniezone => &GeniezoneHypervisor,
48 Self::ProtectedKvm => &ProtectedKvmHypervisor,
49 }
50 }
51 }
52
53 impl TryFrom<Uuid> for HypervisorBackend {
54 type Error = Error;
55
try_from(uuid: Uuid) -> Result<HypervisorBackend>56 fn try_from(uuid: Uuid) -> Result<HypervisorBackend> {
57 match uuid {
58 GeniezoneHypervisor::UUID => Ok(HypervisorBackend::Geniezone),
59 GunyahHypervisor::UUID => Ok(HypervisorBackend::Gunyah),
60 RegularKvmHypervisor::UUID => {
61 // Protected KVM has the same UUID as "regular" KVM so issue an HVC that is assumed
62 // to only be supported by pKVM: if it returns SUCCESS, deduce that this is pKVM
63 // and if it returns NOT_SUPPORTED assume that it is "regular" KVM.
64 match ProtectedKvmHypervisor.as_mmio_guard().unwrap().granule() {
65 Ok(_) => Ok(HypervisorBackend::ProtectedKvm),
66 Err(Error::KvmError(KvmError::NotSupported, _)) => {
67 Ok(HypervisorBackend::RegularKvm)
68 }
69 Err(e) => Err(e),
70 }
71 }
72 u => Err(Error::UnsupportedHypervisorUuid(u)),
73 }
74 }
75 }
76
77 const ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID: u32 = 0x8600ff01;
78
query_vendor_hyp_call_uid() -> Uuid79 fn query_vendor_hyp_call_uid() -> Uuid {
80 let args = [0u64; 17];
81 let res = hvc64(ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID, args);
82
83 // KVM's UUID of "28b46fb6-2ec5-11e9-a9ca-4b564d003a74" is generated by
84 // Uuid::from_u128() from an input value of
85 // 0x28b46fb6_2ec511e9_a9ca4b56_4d003a74. ARM's SMC calling convention
86 // (Document number ARM DEN 0028E) describes the UUID register mapping such
87 // that W0 contains bytes 0..3 of UUID, with byte 0 in lower order bits. In
88 // the KVM example, byte 0 of KVM's UUID (0x28) will be returned in the low
89 // 8-bits of W0, while byte 15 (0x74) will be returned in bits 31-24 of W3.
90 //
91 // `uuid` value derived below thus need to be byte-reversed before
92 // being used in Uuid::from_u128(). Alternately use Uuid::from_u128_le()
93 // to achieve the same.
94
95 let uuid = ((res[3] as u32 as u128) << 96)
96 | ((res[2] as u32 as u128) << 64)
97 | ((res[1] as u32 as u128) << 32)
98 | (res[0] as u32 as u128);
99
100 Uuid::from_u128_le(uuid)
101 }
102
detect_hypervisor() -> HypervisorBackend103 fn detect_hypervisor() -> HypervisorBackend {
104 query_vendor_hyp_call_uid().try_into().expect("Failed to detect hypervisor")
105 }
106
107 /// Gets the hypervisor singleton.
get_hypervisor() -> &'static dyn Hypervisor108 fn get_hypervisor() -> &'static dyn Hypervisor {
109 static HYPERVISOR: OnceBox<HypervisorBackend> = OnceBox::new();
110
111 HYPERVISOR.get_or_init(|| Box::new(detect_hypervisor())).get_hypervisor()
112 }
113
114 /// Gets the MMIO_GUARD hypervisor singleton, if any.
get_mmio_guard() -> Option<&'static dyn MmioGuardedHypervisor>115 pub fn get_mmio_guard() -> Option<&'static dyn MmioGuardedHypervisor> {
116 get_hypervisor().as_mmio_guard()
117 }
118
119 /// Gets the dynamic memory sharing hypervisor singleton, if any.
get_mem_sharer() -> Option<&'static dyn MemSharingHypervisor>120 pub fn get_mem_sharer() -> Option<&'static dyn MemSharingHypervisor> {
121 get_hypervisor().as_mem_sharer()
122 }
123
124 /// Gets the device assigning hypervisor singleton, if any.
get_device_assigner() -> Option<&'static dyn DeviceAssigningHypervisor>125 pub fn get_device_assigner() -> Option<&'static dyn DeviceAssigningHypervisor> {
126 get_hypervisor().as_device_assigner()
127 }
128