• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #[cfg(target_arch = "aarch64")]
19 mod geniezone;
20 #[cfg(target_arch = "aarch64")]
21 mod gunyah;
22 #[cfg(target_arch = "aarch64")]
23 #[path = "hypervisor/kvm_aarch64.rs"]
24 mod kvm;
25 
26 #[cfg(target_arch = "x86_64")]
27 #[path = "hypervisor/kvm_x86.rs"]
28 mod kvm;
29 
30 #[cfg(target_arch = "aarch64")]
31 use {
32     super::{Error, Result},
33     geniezone::GeniezoneHypervisor,
34     gunyah::GunyahHypervisor,
35     smccc::hvc64,
36     uuid::Uuid,
37 };
38 
39 #[cfg(target_arch = "aarch64")]
40 pub use geniezone::GeniezoneError;
41 
42 #[cfg(target_arch = "aarch64")]
43 pub use gunyah::GunyahError;
44 
45 use alloc::boxed::Box;
46 use common::Hypervisor;
47 pub use common::{DeviceAssigningHypervisor, MemSharingHypervisor, MmioGuardedHypervisor};
48 pub use kvm::KvmError;
49 use kvm::{ProtectedKvmHypervisor, RegularKvmHypervisor};
50 use once_cell::race::OnceBox;
51 
52 enum HypervisorBackend {
53     RegularKvm,
54     #[cfg(target_arch = "aarch64")]
55     Gunyah,
56     #[cfg(target_arch = "aarch64")]
57     Geniezone,
58     ProtectedKvm,
59 }
60 
61 impl HypervisorBackend {
get_hypervisor(&self) -> &'static dyn Hypervisor62     fn get_hypervisor(&self) -> &'static dyn Hypervisor {
63         match self {
64             Self::RegularKvm => &RegularKvmHypervisor,
65             #[cfg(target_arch = "aarch64")]
66             Self::Gunyah => &GunyahHypervisor,
67             #[cfg(target_arch = "aarch64")]
68             Self::Geniezone => &GeniezoneHypervisor,
69             Self::ProtectedKvm => &ProtectedKvmHypervisor,
70         }
71     }
72 }
73 
74 #[cfg(target_arch = "aarch64")]
75 impl TryFrom<Uuid> for HypervisorBackend {
76     type Error = Error;
77 
try_from(uuid: Uuid) -> Result<HypervisorBackend>78     fn try_from(uuid: Uuid) -> Result<HypervisorBackend> {
79         match uuid {
80             GeniezoneHypervisor::UUID => Ok(HypervisorBackend::Geniezone),
81             GunyahHypervisor::UUID => Ok(HypervisorBackend::Gunyah),
82             RegularKvmHypervisor::UUID => {
83                 // Protected KVM has the same UUID as "regular" KVM so issue an HVC that is assumed
84                 // to only be supported by pKVM: if it returns SUCCESS, deduce that this is pKVM
85                 // and if it returns NOT_SUPPORTED assume that it is "regular" KVM.
86                 match ProtectedKvmHypervisor.as_mmio_guard().unwrap().granule() {
87                     Ok(_) => Ok(HypervisorBackend::ProtectedKvm),
88                     Err(Error::KvmError(KvmError::NotSupported, _)) => {
89                         Ok(HypervisorBackend::RegularKvm)
90                     }
91                     Err(e) => Err(e),
92                 }
93             }
94             u => Err(Error::UnsupportedHypervisorUuid(u)),
95         }
96     }
97 }
98 
99 #[cfg(target_arch = "aarch64")]
100 const ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID: u32 = 0x8600ff01;
101 
102 #[cfg(target_arch = "aarch64")]
query_vendor_hyp_call_uid() -> Uuid103 fn query_vendor_hyp_call_uid() -> Uuid {
104     let args = [0u64; 17];
105     let res = hvc64(ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID, args);
106 
107     // KVM's UUID of "28b46fb6-2ec5-11e9-a9ca-4b564d003a74" is generated by
108     // Uuid::from_u128() from an input value of
109     // 0x28b46fb6_2ec511e9_a9ca4b56_4d003a74. ARM's SMC calling convention
110     // (Document number ARM DEN 0028E) describes the UUID register mapping such
111     // that W0 contains bytes 0..3 of UUID, with byte 0 in lower order bits. In
112     // the KVM example, byte 0 of KVM's UUID (0x28) will be returned in the low
113     // 8-bits of W0, while byte 15 (0x74) will be returned in bits 31-24 of W3.
114     //
115     // `uuid` value derived below thus need to be byte-reversed before
116     // being used in Uuid::from_u128(). Alternately use Uuid::from_u128_le()
117     // to achieve the same.
118 
119     let uuid = ((res[3] as u32 as u128) << 96)
120         | ((res[2] as u32 as u128) << 64)
121         | ((res[1] as u32 as u128) << 32)
122         | (res[0] as u32 as u128);
123 
124     Uuid::from_u128_le(uuid)
125 }
126 
detect_hypervisor() -> HypervisorBackend127 fn detect_hypervisor() -> HypervisorBackend {
128     #[cfg(target_arch = "aarch64")]
129     {
130         query_vendor_hyp_call_uid().try_into().expect("Failed to detect hypervisor")
131     }
132 
133     #[cfg(target_arch = "x86_64")]
134     kvm::determine_hyp_type().expect("Failed to detect hypervisor")
135 }
136 
137 /// Gets the hypervisor singleton.
get_hypervisor() -> &'static dyn Hypervisor138 fn get_hypervisor() -> &'static dyn Hypervisor {
139     static HYPERVISOR: OnceBox<HypervisorBackend> = OnceBox::new();
140 
141     HYPERVISOR.get_or_init(|| Box::new(detect_hypervisor())).get_hypervisor()
142 }
143 
144 /// Gets the MMIO_GUARD hypervisor singleton, if any.
get_mmio_guard() -> Option<&'static dyn MmioGuardedHypervisor>145 pub fn get_mmio_guard() -> Option<&'static dyn MmioGuardedHypervisor> {
146     get_hypervisor().as_mmio_guard()
147 }
148 
149 /// Gets the dynamic memory sharing hypervisor singleton, if any.
get_mem_sharer() -> Option<&'static dyn MemSharingHypervisor>150 pub fn get_mem_sharer() -> Option<&'static dyn MemSharingHypervisor> {
151     get_hypervisor().as_mem_sharer()
152 }
153 
154 /// Gets the device assigning hypervisor singleton, if any.
get_device_assigner() -> Option<&'static dyn DeviceAssigningHypervisor>155 pub fn get_device_assigner() -> Option<&'static dyn DeviceAssigningHypervisor> {
156     get_hypervisor().as_device_assigner()
157 }
158 
159 /// Gets the unique hypervisor granule size, if any.
get_granule_size() -> Option<usize>160 pub fn get_granule_size() -> Option<usize> {
161     get_hypervisor().get_granule_size()
162 }
163