blob: dab15ecf3579f1029355b3dae4768ece9f10aa9c [file] [log] [blame]
// Copyright 2023, The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Wrappers around hypervisor back-ends.
mod common;
mod geniezone;
mod gunyah;
mod kvm;
use super::{Error, Result};
use alloc::boxed::Box;
use common::Hypervisor;
pub use common::{
DeviceAssigningHypervisor, MemSharingHypervisor, MmioGuardedHypervisor, MMIO_GUARD_GRANULE_SIZE,
};
pub use geniezone::GeniezoneError;
use geniezone::GeniezoneHypervisor;
use gunyah::GunyahHypervisor;
pub use kvm::KvmError;
use kvm::{ProtectedKvmHypervisor, RegularKvmHypervisor};
use once_cell::race::OnceBox;
use smccc::hvc64;
use uuid::Uuid;
enum HypervisorBackend {
RegularKvm,
Gunyah,
Geniezone,
ProtectedKvm,
}
impl HypervisorBackend {
fn get_hypervisor(&self) -> &'static dyn Hypervisor {
match self {
Self::RegularKvm => &RegularKvmHypervisor,
Self::Gunyah => &GunyahHypervisor,
Self::Geniezone => &GeniezoneHypervisor,
Self::ProtectedKvm => &ProtectedKvmHypervisor,
}
}
}
impl TryFrom<Uuid> for HypervisorBackend {
type Error = Error;
fn try_from(uuid: Uuid) -> Result<HypervisorBackend> {
match uuid {
GeniezoneHypervisor::UUID => Ok(HypervisorBackend::Geniezone),
GunyahHypervisor::UUID => Ok(HypervisorBackend::Gunyah),
RegularKvmHypervisor::UUID => {
// Protected KVM has the same UUID as "regular" KVM so issue an HVC that is assumed
// to only be supported by pKVM: if it returns SUCCESS, deduce that this is pKVM
// and if it returns NOT_SUPPORTED assume that it is "regular" KVM.
match ProtectedKvmHypervisor.as_mmio_guard().unwrap().granule() {
Ok(_) => Ok(HypervisorBackend::ProtectedKvm),
Err(Error::KvmError(KvmError::NotSupported, _)) => {
Ok(HypervisorBackend::RegularKvm)
}
Err(e) => Err(e),
}
}
u => Err(Error::UnsupportedHypervisorUuid(u)),
}
}
}
const ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID: u32 = 0x8600ff01;
fn query_vendor_hyp_call_uid() -> Uuid {
let args = [0u64; 17];
let res = hvc64(ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID, args);
// KVM's UUID of "28b46fb6-2ec5-11e9-a9ca-4b564d003a74" is generated by
// Uuid::from_u128() from an input value of
// 0x28b46fb6_2ec511e9_a9ca4b56_4d003a74. ARM's SMC calling convention
// (Document number ARM DEN 0028E) describes the UUID register mapping such
// that W0 contains bytes 0..3 of UUID, with byte 0 in lower order bits. In
// the KVM example, byte 0 of KVM's UUID (0x28) will be returned in the low
// 8-bits of W0, while byte 15 (0x74) will be returned in bits 31-24 of W3.
//
// `uuid` value derived below thus need to be byte-reversed before
// being used in Uuid::from_u128(). Alternately use Uuid::from_u128_le()
// to achieve the same.
let uuid = ((res[3] as u32 as u128) << 96)
| ((res[2] as u32 as u128) << 64)
| ((res[1] as u32 as u128) << 32)
| (res[0] as u32 as u128);
Uuid::from_u128_le(uuid)
}
fn detect_hypervisor() -> HypervisorBackend {
query_vendor_hyp_call_uid().try_into().expect("Failed to detect hypervisor")
}
/// Gets the hypervisor singleton.
fn get_hypervisor() -> &'static dyn Hypervisor {
static HYPERVISOR: OnceBox<HypervisorBackend> = OnceBox::new();
HYPERVISOR.get_or_init(|| Box::new(detect_hypervisor())).get_hypervisor()
}
/// Gets the MMIO_GUARD hypervisor singleton, if any.
pub fn get_mmio_guard() -> Option<&'static dyn MmioGuardedHypervisor> {
get_hypervisor().as_mmio_guard()
}
/// Gets the dynamic memory sharing hypervisor singleton, if any.
pub fn get_mem_sharer() -> Option<&'static dyn MemSharingHypervisor> {
get_hypervisor().as_mem_sharer()
}
/// Gets the device assigning hypervisor singleton, if any.
pub fn get_device_assigner() -> Option<&'static dyn DeviceAssigningHypervisor> {
get_hypervisor().as_device_assigner()
}