blob: 5835346990ede9bb1aeaa1f21881d53bae977832 [file] [log] [blame]
// Copyright 2022, 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 calls to the KVM hypervisor.
use super::common::{Hypervisor, MemSharingHypervisor, MmioGuardedHypervisor};
use crate::error::{Error, Result};
use crate::util::page_address;
use core::fmt::{self, Display, Formatter};
use smccc::{
error::{positive_or_error_64, success_or_error_32, success_or_error_64},
hvc64,
};
use uuid::{uuid, Uuid};
/// Error from a KVM HVC call.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum KvmError {
/// The call is not supported by the implementation.
NotSupported,
/// One of the call parameters has a non-supported value.
InvalidParameter,
/// There was an unexpected return value.
Unknown(i64),
}
impl From<i64> for KvmError {
fn from(value: i64) -> Self {
match value {
-1 => KvmError::NotSupported,
-3 => KvmError::InvalidParameter,
_ => KvmError::Unknown(value),
}
}
}
impl From<i32> for KvmError {
fn from(value: i32) -> Self {
i64::from(value).into()
}
}
impl Display for KvmError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::NotSupported => write!(f, "KVM call not supported"),
Self::InvalidParameter => write!(f, "KVM call received non-supported value"),
Self::Unknown(e) => write!(f, "Unknown return value from KVM {} ({0:#x})", e),
}
}
}
const ARM_SMCCC_KVM_FUNC_HYP_MEMINFO: u32 = 0xc6000002;
const ARM_SMCCC_KVM_FUNC_MEM_SHARE: u32 = 0xc6000003;
const ARM_SMCCC_KVM_FUNC_MEM_UNSHARE: u32 = 0xc6000004;
const VENDOR_HYP_KVM_MMIO_GUARD_INFO_FUNC_ID: u32 = 0xc6000005;
const VENDOR_HYP_KVM_MMIO_GUARD_ENROLL_FUNC_ID: u32 = 0xc6000006;
const VENDOR_HYP_KVM_MMIO_GUARD_MAP_FUNC_ID: u32 = 0xc6000007;
const VENDOR_HYP_KVM_MMIO_GUARD_UNMAP_FUNC_ID: u32 = 0xc6000008;
pub(super) struct RegularKvmHypervisor;
impl RegularKvmHypervisor {
// Based on ARM_SMCCC_VENDOR_HYP_UID_KVM_REG values listed in Linux kernel source:
// https://github.com/torvalds/linux/blob/master/include/linux/arm-smccc.h
pub(super) const UUID: Uuid = uuid!("28b46fb6-2ec5-11e9-a9ca-4b564d003a74");
}
impl Hypervisor for RegularKvmHypervisor {}
pub(super) struct ProtectedKvmHypervisor;
impl Hypervisor for ProtectedKvmHypervisor {
fn as_mmio_guard(&self) -> Option<&dyn MmioGuardedHypervisor> {
Some(self)
}
fn as_mem_sharer(&self) -> Option<&dyn MemSharingHypervisor> {
Some(self)
}
}
impl MmioGuardedHypervisor for ProtectedKvmHypervisor {
fn enroll(&self) -> Result<()> {
let args = [0u64; 17];
match success_or_error_64(hvc64(VENDOR_HYP_KVM_MMIO_GUARD_ENROLL_FUNC_ID, args)[0]) {
Ok(()) => Ok(()),
Err(KvmError::NotSupported) => Err(Error::MmioGuardNotSupported),
Err(e) => Err(Error::KvmError(e, VENDOR_HYP_KVM_MMIO_GUARD_ENROLL_FUNC_ID)),
}
}
fn map(&self, addr: usize) -> Result<()> {
let mut args = [0u64; 17];
args[0] = page_address(addr);
// TODO(b/277859415): pKVM returns a i32 instead of a i64 in T.
// Drop this hack once T reaches EoL.
success_or_error_32(hvc64(VENDOR_HYP_KVM_MMIO_GUARD_MAP_FUNC_ID, args)[0] as u32)
.map_err(|e| Error::KvmError(e, VENDOR_HYP_KVM_MMIO_GUARD_MAP_FUNC_ID))
}
fn unmap(&self, addr: usize) -> Result<()> {
let mut args = [0u64; 17];
args[0] = page_address(addr);
// TODO(b/277860860): pKVM returns NOT_SUPPORTED for SUCCESS in T.
// Drop this hack once T reaches EoL.
match success_or_error_64(hvc64(VENDOR_HYP_KVM_MMIO_GUARD_UNMAP_FUNC_ID, args)[0]) {
Err(KvmError::NotSupported) | Ok(_) => Ok(()),
Err(e) => Err(Error::KvmError(e, VENDOR_HYP_KVM_MMIO_GUARD_UNMAP_FUNC_ID)),
}
}
fn granule(&self) -> Result<usize> {
let args = [0u64; 17];
let granule = checked_hvc64(VENDOR_HYP_KVM_MMIO_GUARD_INFO_FUNC_ID, args)?;
Ok(granule.try_into().unwrap())
}
}
impl MemSharingHypervisor for ProtectedKvmHypervisor {
fn share(&self, base_ipa: u64) -> Result<()> {
let mut args = [0u64; 17];
args[0] = base_ipa;
checked_hvc64_expect_zero(ARM_SMCCC_KVM_FUNC_MEM_SHARE, args)
}
fn unshare(&self, base_ipa: u64) -> Result<()> {
let mut args = [0u64; 17];
args[0] = base_ipa;
checked_hvc64_expect_zero(ARM_SMCCC_KVM_FUNC_MEM_UNSHARE, args)
}
fn granule(&self) -> Result<usize> {
let args = [0u64; 17];
let granule = checked_hvc64(ARM_SMCCC_KVM_FUNC_HYP_MEMINFO, args)?;
Ok(granule.try_into().unwrap())
}
}
fn checked_hvc64_expect_zero(function: u32, args: [u64; 17]) -> Result<()> {
success_or_error_64(hvc64(function, args)[0]).map_err(|e| Error::KvmError(e, function))
}
fn checked_hvc64(function: u32, args: [u64; 17]) -> Result<u64> {
positive_or_error_64(hvc64(function, args)[0]).map_err(|e| Error::KvmError(e, function))
}