[hypervisor] Add hypervisor backend trait to facilitate hyp support
This cl adds a hypervisor backend trait to make it easier to support
different hypervisors in the future for both Rialto and pvmfw
(e.g. aosp/2479400).
Test: m pvmfw_img
Bug: 272226230
Change-Id: I0a1b568f37b819c047ae3b09048a791cb40f3372
diff --git a/libs/hyp/src/hypervisor/common.rs b/libs/hyp/src/hypervisor/common.rs
new file mode 100644
index 0000000..d1439db
--- /dev/null
+++ b/libs/hyp/src/hypervisor/common.rs
@@ -0,0 +1,47 @@
+// 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.
+
+//! This module regroups some common traits shared by all the hypervisors.
+
+use smccc::Result;
+
+/// Trait for the hypervisor.
+pub trait Hypervisor {
+ /// Returns MMIO guard granule size in bytes.
+ fn mmio_guard_granule(&self) -> Result<usize>;
+
+ /// Registers to use MMIO guard APIs.
+ /// By enrolling, all MMIO will be blocked unless allow-listed with `mmio_guard_map`.
+ /// Protected VMs are auto-enrolled.
+ fn mmio_guard_enroll(&self) -> Result<()>;
+
+ /// Maps a memory address to the hypervisor MMIO guard.
+ fn mmio_guard_map(&self, ipa: u64) -> Result<()>;
+
+ /// Unmaps a memory address from the hypervisor MMIO guard.
+ fn mmio_guard_unmap(&self, ipa: u64) -> Result<()>;
+
+ /// Shares a region of memory with host, granting it read, write and execute permissions.
+ /// The size of the region is equal to the memory protection granule returned by
+ /// [`hyp_meminfo`].
+ fn mem_share(&self, base_ipa: u64) -> Result<()>;
+
+ /// Revokes access permission from host to a memory region previously shared with
+ /// [`mem_share`]. The size of the region is equal to the memory protection granule returned by
+ /// [`hyp_meminfo`].
+ fn mem_unshare(&self, base_ipa: u64) -> Result<()>;
+
+ /// Returns the memory protection granule size in bytes.
+ fn memory_protection_granule(&self) -> Result<usize>;
+}
diff --git a/libs/hyp/src/hypervisor/kvm.rs b/libs/hyp/src/hypervisor/kvm.rs
index a34acc8..cac9ba2 100644
--- a/libs/hyp/src/hypervisor/kvm.rs
+++ b/libs/hyp/src/hypervisor/kvm.rs
@@ -14,6 +14,7 @@
//! Wrappers around calls to the KVM hypervisor.
+use super::common::Hypervisor;
use smccc::{checked_hvc64, checked_hvc64_expect_zero, Error, Result};
const ARM_SMCCC_KVM_FUNC_HYP_MEMINFO: u32 = 0xc6000002;
@@ -25,71 +26,69 @@
const VENDOR_HYP_KVM_MMIO_GUARD_MAP_FUNC_ID: u32 = 0xc6000007;
const VENDOR_HYP_KVM_MMIO_GUARD_UNMAP_FUNC_ID: u32 = 0xc6000008;
-/// Queries the memory protection parameters for a protected virtual machine.
-///
-/// Returns the memory protection granule size in bytes.
-pub(super) fn hyp_meminfo() -> Result<u64> {
- let args = [0u64; 17];
- checked_hvc64(ARM_SMCCC_KVM_FUNC_HYP_MEMINFO, args)
-}
+pub(super) struct KvmHypervisor;
-/// Shares a region of memory with the KVM host, granting it read, write and execute permissions.
-/// The size of the region is equal to the memory protection granule returned by [`hyp_meminfo`].
-pub(super) fn mem_share(base_ipa: u64) -> Result<()> {
- let mut args = [0u64; 17];
- args[0] = base_ipa;
+impl Hypervisor for KvmHypervisor {
+ fn mmio_guard_granule(&self) -> Result<usize> {
+ let args = [0u64; 17];
- checked_hvc64_expect_zero(ARM_SMCCC_KVM_FUNC_MEM_SHARE, args)
-}
-
-/// Revokes access permission from the KVM host to a memory region previously shared with
-/// [`mem_share`]. The size of the region is equal to the memory protection granule returned by
-/// [`hyp_meminfo`].
-pub(super) fn mem_unshare(base_ipa: u64) -> Result<()> {
- let mut args = [0u64; 17];
- args[0] = base_ipa;
-
- checked_hvc64_expect_zero(ARM_SMCCC_KVM_FUNC_MEM_UNSHARE, args)
-}
-
-pub(super) fn mmio_guard_info() -> Result<u64> {
- let args = [0u64; 17];
-
- checked_hvc64(VENDOR_HYP_KVM_MMIO_GUARD_INFO_FUNC_ID, args)
-}
-
-pub(super) fn mmio_guard_enroll() -> Result<()> {
- let args = [0u64; 17];
-
- checked_hvc64_expect_zero(VENDOR_HYP_KVM_MMIO_GUARD_ENROLL_FUNC_ID, args)
-}
-
-pub(super) fn mmio_guard_map(ipa: u64) -> Result<()> {
- let mut args = [0u64; 17];
- args[0] = ipa;
-
- // TODO(b/277859415): pKVM returns a i32 instead of a i64 in T.
- // Drop this hack once T reaches EoL.
- let is_i32_error_code = |n| u32::try_from(n).ok().filter(|v| (*v as i32) < 0).is_some();
- match checked_hvc64_expect_zero(VENDOR_HYP_KVM_MMIO_GUARD_MAP_FUNC_ID, args) {
- Err(Error::Unexpected(e)) if is_i32_error_code(e) => match e as u32 as i32 {
- -1 => Err(Error::NotSupported),
- -2 => Err(Error::NotRequired),
- -3 => Err(Error::InvalidParameter),
- ret => Err(Error::Unknown(ret as i64)),
- },
- res => res,
+ let granule = checked_hvc64(VENDOR_HYP_KVM_MMIO_GUARD_INFO_FUNC_ID, args)?;
+ Ok(granule.try_into().unwrap())
}
-}
-pub(super) fn mmio_guard_unmap(ipa: u64) -> Result<()> {
- let mut args = [0u64; 17];
- args[0] = ipa;
+ fn mmio_guard_enroll(&self) -> Result<()> {
+ let args = [0u64; 17];
- // TODO(b/277860860): pKVM returns NOT_SUPPORTED for SUCCESS in T.
- // Drop this hack once T reaches EoL.
- match checked_hvc64_expect_zero(VENDOR_HYP_KVM_MMIO_GUARD_UNMAP_FUNC_ID, args) {
- Err(Error::NotSupported) | Ok(_) => Ok(()),
- x => x,
+ checked_hvc64_expect_zero(VENDOR_HYP_KVM_MMIO_GUARD_ENROLL_FUNC_ID, args)
+ }
+
+ fn mmio_guard_map(&self, ipa: u64) -> Result<()> {
+ let mut args = [0u64; 17];
+ args[0] = ipa;
+
+ // TODO(b/277859415): pKVM returns a i32 instead of a i64 in T.
+ // Drop this hack once T reaches EoL.
+ let is_i32_error_code = |n| u32::try_from(n).ok().filter(|v| (*v as i32) < 0).is_some();
+ match checked_hvc64_expect_zero(VENDOR_HYP_KVM_MMIO_GUARD_MAP_FUNC_ID, args) {
+ Err(Error::Unexpected(e)) if is_i32_error_code(e) => match e as u32 as i32 {
+ -1 => Err(Error::NotSupported),
+ -2 => Err(Error::NotRequired),
+ -3 => Err(Error::InvalidParameter),
+ ret => Err(Error::Unknown(ret as i64)),
+ },
+ res => res,
+ }
+ }
+
+ fn mmio_guard_unmap(&self, ipa: u64) -> Result<()> {
+ let mut args = [0u64; 17];
+ args[0] = ipa;
+
+ // TODO(b/277860860): pKVM returns NOT_SUPPORTED for SUCCESS in T.
+ // Drop this hack once T reaches EoL.
+ match checked_hvc64_expect_zero(VENDOR_HYP_KVM_MMIO_GUARD_UNMAP_FUNC_ID, args) {
+ Err(Error::NotSupported) | Ok(_) => Ok(()),
+ x => x,
+ }
+ }
+
+ fn mem_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 mem_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 memory_protection_granule(&self) -> Result<usize> {
+ let args = [0u64; 17];
+ let granule = checked_hvc64(ARM_SMCCC_KVM_FUNC_HYP_MEMINFO, args)?;
+ Ok(granule.try_into().unwrap())
}
}
diff --git a/libs/hyp/src/hypervisor/mod.rs b/libs/hyp/src/hypervisor/mod.rs
index 5807698..87925d2 100644
--- a/libs/hyp/src/hypervisor/mod.rs
+++ b/libs/hyp/src/hypervisor/mod.rs
@@ -14,40 +14,27 @@
//! Wrappers around hypervisor back-ends.
+mod common;
mod kvm;
-/// Queries the memory protection parameters for a protected virtual machine.
-///
-/// Returns the memory protection granule size in bytes.
-pub fn hyp_meminfo() -> smccc::Result<u64> {
- kvm::hyp_meminfo()
+pub use common::Hypervisor;
+use kvm::KvmHypervisor;
+
+static HYPERVISOR: HypervisorBackend = HypervisorBackend::Kvm;
+
+enum HypervisorBackend {
+ Kvm,
}
-/// Shares a region of memory with the host, granting it read, write and execute permissions.
-/// The size of the region is equal to the memory protection granule returned by [`hyp_meminfo`].
-pub fn mem_share(base_ipa: u64) -> smccc::Result<()> {
- kvm::mem_share(base_ipa)
+impl HypervisorBackend {
+ fn get_hypervisor(&self) -> &'static dyn Hypervisor {
+ match self {
+ Self::Kvm => &KvmHypervisor,
+ }
+ }
}
-/// Revokes access permission from the host to a memory region previously shared with
-/// [`mem_share`]. The size of the region is equal to the memory protection granule returned by
-/// [`hyp_meminfo`].
-pub fn mem_unshare(base_ipa: u64) -> smccc::Result<()> {
- kvm::mem_unshare(base_ipa)
-}
-
-pub(crate) fn mmio_guard_info() -> smccc::Result<u64> {
- kvm::mmio_guard_info()
-}
-
-pub(crate) fn mmio_guard_enroll() -> smccc::Result<()> {
- kvm::mmio_guard_enroll()
-}
-
-pub(crate) fn mmio_guard_map(ipa: u64) -> smccc::Result<()> {
- kvm::mmio_guard_map(ipa)
-}
-
-pub(crate) fn mmio_guard_unmap(ipa: u64) -> smccc::Result<()> {
- kvm::mmio_guard_unmap(ipa)
+/// Gets the hypervisor singleton.
+pub fn get_hypervisor() -> &'static dyn Hypervisor {
+ HYPERVISOR.get_hypervisor()
}
diff --git a/libs/hyp/src/lib.rs b/libs/hyp/src/lib.rs
index f0f0631..676af1d 100644
--- a/libs/hyp/src/lib.rs
+++ b/libs/hyp/src/lib.rs
@@ -19,5 +19,5 @@
mod hypervisor;
mod util;
-pub use hypervisor::{hyp_meminfo, mem_share, mem_unshare};
+pub use hypervisor::{get_hypervisor, Hypervisor};
pub mod mmio_guard;
diff --git a/libs/hyp/src/mmio_guard.rs b/libs/hyp/src/mmio_guard.rs
index 512eb88..84dac64 100644
--- a/libs/hyp/src/mmio_guard.rs
+++ b/libs/hyp/src/mmio_guard.rs
@@ -14,7 +14,7 @@
//! Safe MMIO_GUARD support.
-use crate::hypervisor::{mmio_guard_enroll, mmio_guard_info, mmio_guard_map, mmio_guard_unmap};
+use crate::hypervisor::get_hypervisor;
use crate::util::{page_address, SIZE_4KB};
use core::{fmt, result};
@@ -24,7 +24,7 @@
/// Failed the necessary MMIO_GUARD_ENROLL call.
EnrollFailed(smccc::Error),
/// Failed to obtain the MMIO_GUARD granule size.
- InfoFailed(smccc::Error),
+ GranuleQueryFailed(smccc::Error),
/// Failed to MMIO_GUARD_MAP a page.
MapFailed(smccc::Error),
/// Failed to MMIO_GUARD_UNMAP a page.
@@ -37,7 +37,7 @@
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::EnrollFailed(e) => write!(f, "Failed to enroll into MMIO_GUARD: {e}"),
- Self::InfoFailed(e) => write!(f, "Failed to get the MMIO_GUARD granule: {e}"),
+ Self::GranuleQueryFailed(e) => write!(f, "Failed to get the MMIO_GUARD granule: {e}"),
Self::MapFailed(e) => write!(f, "Failed to MMIO_GUARD map: {e}"),
Self::UnmapFailed(e) => write!(f, "Failed to MMIO_GUARD unmap: {e}"),
Self::UnsupportedGranule(g) => write!(f, "Unsupported MMIO_GUARD granule: {g}"),
@@ -50,8 +50,9 @@
/// Initializes the hypervisor by enrolling a MMIO guard and checking the memory granule size.
pub fn init() -> Result<()> {
- mmio_guard_enroll().map_err(Error::EnrollFailed)?;
- let mmio_granule = mmio_guard_info().map_err(Error::InfoFailed)? as usize;
+ let hyp = get_hypervisor();
+ hyp.mmio_guard_enroll().map_err(Error::EnrollFailed)?;
+ let mmio_granule = hyp.mmio_guard_granule().map_err(Error::GranuleQueryFailed)?;
if mmio_granule != SIZE_4KB {
return Err(Error::UnsupportedGranule(mmio_granule));
}
@@ -60,10 +61,10 @@
/// Maps a memory address to the hypervisor MMIO guard.
pub fn map(addr: usize) -> Result<()> {
- mmio_guard_map(page_address(addr)).map_err(Error::MapFailed)
+ get_hypervisor().mmio_guard_map(page_address(addr)).map_err(Error::MapFailed)
}
/// Unmaps a memory address from the hypervisor MMIO guard.
pub fn unmap(addr: usize) -> Result<()> {
- mmio_guard_unmap(page_address(addr)).map_err(Error::UnmapFailed)
+ get_hypervisor().mmio_guard_unmap(page_address(addr)).map_err(Error::UnmapFailed)
}
diff --git a/pvmfw/src/memory.rs b/pvmfw/src/memory.rs
index fde3f9b..3f44f8a 100644
--- a/pvmfw/src/memory.rs
+++ b/pvmfw/src/memory.rs
@@ -29,7 +29,7 @@
use core::ops::Range;
use core::ptr::NonNull;
use core::result;
-use hyp::{hyp_meminfo, mem_share, mem_unshare, mmio_guard};
+use hyp::{get_hypervisor, mmio_guard};
use log::error;
use tinyvec::ArrayVec;
@@ -283,7 +283,7 @@
.expect("Memory protection granule was not a power of two")..range.end)
.step_by(granule)
{
- mem_share(base as u64)?;
+ get_hypervisor().mem_share(base as u64)?;
}
Ok(())
}
@@ -296,7 +296,7 @@
.expect("Memory protection granule was not a power of two")..range.end)
.step_by(granule)
{
- mem_unshare(base as u64)?;
+ get_hypervisor().mem_unshare(base as u64)?;
}
Ok(())
}
@@ -354,7 +354,7 @@
/// Panics if `size` is 0.
fn shared_buffer_layout(size: usize) -> smccc::Result<Layout> {
assert_ne!(size, 0);
- let granule = hyp_meminfo()? as usize;
+ let granule = get_hypervisor().memory_protection_granule()?;
let allocated_size =
align_up(size, granule).expect("Memory protection granule was not a power of two");
Ok(Layout::from_size_align(allocated_size, granule).unwrap())