vmbase: Integrate libhyp as module
The only client of libhyp should ever be libvmbase so integrate the
former into the latter, which will allow code de-dup, easier tracking of
logic (including global static variables) and more straightforward code
structure.
Bug: 279539763
Test: m libpvmfw libvmbase_example librialto
Change-Id: Icbeda4cc936ead9c6c6df0533089ddb6b88a0833
diff --git a/vmbase/Android.bp b/vmbase/Android.bp
index 07e1b4c..236a895 100644
--- a/vmbase/Android.bp
+++ b/vmbase/Android.bp
@@ -79,13 +79,13 @@
"libbuddy_system_allocator",
"libcstr",
"libfdtpci",
- "libhyp",
"liblibfdt",
"liblog_rust_nostd",
"libonce_cell_nostd",
"libsmccc",
"libspin_nostd",
"libtinyvec_nostd",
+ "libuuid_nostd",
"libvirtio_drivers",
"libzerocopy_nostd",
"libzeroize_nostd",
diff --git a/vmbase/src/entry.rs b/vmbase/src/entry.rs
index b19efce..8b1d7c6 100644
--- a/vmbase/src/entry.rs
+++ b/vmbase/src/entry.rs
@@ -15,17 +15,16 @@
//! Rust entry point.
use crate::{
- bionic, console, heap, logger,
+ bionic, console, heap, hyp, logger,
power::{reboot, shutdown},
rand,
};
use core::mem::size_of;
-use hyp::{self, get_mmio_guard};
fn try_console_init() -> Result<(), hyp::Error> {
console::init();
- if let Some(mmio_guard) = get_mmio_guard() {
+ if let Some(mmio_guard) = hyp::get_mmio_guard() {
mmio_guard.enroll()?;
mmio_guard.validate_granule()?;
mmio_guard.map(console::BASE_ADDRESS)?;
diff --git a/vmbase/src/hyp.rs b/vmbase/src/hyp.rs
new file mode 100644
index 0000000..103d28f
--- /dev/null
+++ b/vmbase/src/hyp.rs
@@ -0,0 +1,25 @@
+// 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 library provides wrappers around various hypervisor backends.
+
+mod error;
+mod hypervisor;
+mod util;
+
+pub use error::{Error, Result};
+pub use hypervisor::{
+ get_device_assigner, get_mem_sharer, get_mmio_guard, DeviceAssigningHypervisor, KvmError,
+ MMIO_GUARD_GRANULE_SIZE,
+};
diff --git a/vmbase/src/hyp/error.rs b/vmbase/src/hyp/error.rs
new file mode 100644
index 0000000..bbf706e
--- /dev/null
+++ b/vmbase/src/hyp/error.rs
@@ -0,0 +1,61 @@
+// 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.
+
+//! Error and Result types for hypervisor.
+
+use core::{fmt, result};
+
+use super::hypervisor::{GeniezoneError, KvmError};
+use uuid::Uuid;
+
+/// Result type with hypervisor error.
+pub type Result<T> = result::Result<T, Error>;
+
+/// Hypervisor error.
+#[derive(Debug, Clone)]
+pub enum Error {
+ /// MMIO guard is not supported.
+ MmioGuardNotSupported,
+ /// Failed to invoke a certain KVM HVC function.
+ KvmError(KvmError, u32),
+ /// Failed to invoke GenieZone HVC function.
+ GeniezoneError(GeniezoneError, u32),
+ /// Unsupported Hypervisor
+ UnsupportedHypervisorUuid(Uuid),
+ /// The MMIO_GUARD granule used by the hypervisor is not supported.
+ UnsupportedMmioGuardGranule(usize),
+}
+
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Self::MmioGuardNotSupported => write!(f, "MMIO guard is not supported"),
+ Self::KvmError(e, function_id) => {
+ write!(f, "Failed to invoke the HVC function with function ID {function_id}: {e}")
+ }
+ Self::GeniezoneError(e, function_id) => {
+ write!(
+ f,
+ "Failed to invoke GenieZone HVC function with function ID {function_id}: {e}"
+ )
+ }
+ Self::UnsupportedHypervisorUuid(u) => {
+ write!(f, "Unsupported Hypervisor UUID {u}")
+ }
+ Self::UnsupportedMmioGuardGranule(g) => {
+ write!(f, "Unsupported MMIO guard granule: {g}")
+ }
+ }
+ }
+}
diff --git a/vmbase/src/hyp/hypervisor.rs b/vmbase/src/hyp/hypervisor.rs
new file mode 100644
index 0000000..dab15ec
--- /dev/null
+++ b/vmbase/src/hyp/hypervisor.rs
@@ -0,0 +1,129 @@
+// 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()
+}
diff --git a/vmbase/src/hyp/hypervisor/common.rs b/vmbase/src/hyp/hypervisor/common.rs
new file mode 100644
index 0000000..de564a8
--- /dev/null
+++ b/vmbase/src/hyp/hypervisor/common.rs
@@ -0,0 +1,88 @@
+// 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 crate::hyp::{util::SIZE_4KB, Error, Result};
+
+/// Expected MMIO guard granule size, validated during MMIO guard initialization.
+pub const MMIO_GUARD_GRANULE_SIZE: usize = SIZE_4KB;
+
+/// Trait for the hypervisor.
+pub trait Hypervisor {
+ /// Returns the hypervisor's MMIO_GUARD implementation, if any.
+ fn as_mmio_guard(&self) -> Option<&dyn MmioGuardedHypervisor> {
+ None
+ }
+
+ /// Returns the hypervisor's dynamic memory sharing implementation, if any.
+ fn as_mem_sharer(&self) -> Option<&dyn MemSharingHypervisor> {
+ None
+ }
+
+ /// Returns the hypervisor's device assigning implementation, if any.
+ fn as_device_assigner(&self) -> Option<&dyn DeviceAssigningHypervisor> {
+ None
+ }
+}
+
+pub trait MmioGuardedHypervisor {
+ /// Enrolls with the MMIO guard so that all MMIO will be blocked unless allow-listed with
+ /// `MmioGuardedHypervisor::map`.
+ fn enroll(&self) -> Result<()>;
+
+ /// Maps a page containing the given memory address to the hypervisor MMIO guard.
+ /// The page size corresponds to the MMIO guard granule size.
+ fn map(&self, addr: usize) -> Result<()>;
+
+ /// Unmaps a page containing the given memory address from the hypervisor MMIO guard.
+ /// The page size corresponds to the MMIO guard granule size.
+ fn unmap(&self, addr: usize) -> Result<()>;
+
+ /// Returns the MMIO guard granule size in bytes.
+ fn granule(&self) -> Result<usize>;
+
+ // TODO(ptosi): Fully move granule validation to client code.
+ /// Validates the MMIO guard granule size.
+ fn validate_granule(&self) -> Result<()> {
+ match self.granule()? {
+ MMIO_GUARD_GRANULE_SIZE => Ok(()),
+ granule => Err(Error::UnsupportedMmioGuardGranule(granule)),
+ }
+ }
+}
+
+pub trait MemSharingHypervisor {
+ /// 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 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 unshare(&self, base_ipa: u64) -> Result<()>;
+
+ /// Returns the memory protection granule size in bytes.
+ fn granule(&self) -> Result<usize>;
+}
+
+/// Device assigning hypervisor
+pub trait DeviceAssigningHypervisor {
+ /// Returns MMIO token.
+ fn get_phys_mmio_token(&self, base_ipa: u64, size: u64) -> Result<u64>;
+
+ /// Returns DMA token as a tuple of (phys_iommu_id, phys_sid).
+ fn get_phys_iommu_token(&self, pviommu_id: u64, vsid: u64) -> Result<(u64, u64)>;
+}
diff --git a/vmbase/src/hyp/hypervisor/geniezone.rs b/vmbase/src/hyp/hypervisor/geniezone.rs
new file mode 100644
index 0000000..69ecf41
--- /dev/null
+++ b/vmbase/src/hyp/hypervisor/geniezone.rs
@@ -0,0 +1,158 @@
+// 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 calls to the GenieZone hypervisor.
+
+use core::fmt::{self, Display, Formatter};
+
+use super::{Hypervisor, MemSharingHypervisor, MmioGuardedHypervisor};
+use crate::hyp::{util::page_address, Error, Result};
+
+use smccc::{
+ error::{positive_or_error_64, success_or_error_64},
+ hvc64,
+};
+use uuid::{uuid, Uuid};
+
+pub(super) struct GeniezoneHypervisor;
+
+const ARM_SMCCC_GZVM_FUNC_HYP_MEMINFO: u32 = 0xc6000002;
+const ARM_SMCCC_GZVM_FUNC_MEM_SHARE: u32 = 0xc6000003;
+const ARM_SMCCC_GZVM_FUNC_MEM_UNSHARE: u32 = 0xc6000004;
+
+const VENDOR_HYP_GZVM_MMIO_GUARD_INFO_FUNC_ID: u32 = 0xc6000005;
+const VENDOR_HYP_GZVM_MMIO_GUARD_ENROLL_FUNC_ID: u32 = 0xc6000006;
+const VENDOR_HYP_GZVM_MMIO_GUARD_MAP_FUNC_ID: u32 = 0xc6000007;
+const VENDOR_HYP_GZVM_MMIO_GUARD_UNMAP_FUNC_ID: u32 = 0xc6000008;
+
+impl GeniezoneHypervisor {
+ // We generate this uuid by ourselves to identify GenieZone hypervisor
+ // and share the same identification along with guest VMs.
+ // The previous uuid was removed due to duplication elsewhere.
+ pub const UUID: Uuid = uuid!("7e134ed0-3b82-488d-8cee-69c19211dbe7");
+}
+
+/// Error from a GenieZone HVC call.
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub enum GeniezoneError {
+ /// The call is not supported by the implementation.
+ NotSupported,
+ /// The call is not required to implement.
+ NotRequired,
+ /// One of the call parameters has a invalid value.
+ InvalidParameter,
+ /// There was an unexpected return value.
+ Unknown(i64),
+}
+
+impl From<i64> for GeniezoneError {
+ fn from(value: i64) -> Self {
+ match value {
+ -1 => GeniezoneError::NotSupported,
+ -2 => GeniezoneError::NotRequired,
+ -3 => GeniezoneError::InvalidParameter,
+ _ => GeniezoneError::Unknown(value),
+ }
+ }
+}
+
+impl From<i32> for GeniezoneError {
+ fn from(value: i32) -> Self {
+ i64::from(value).into()
+ }
+}
+
+impl Display for GeniezoneError {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ match self {
+ Self::NotSupported => write!(f, "GenieZone call not supported"),
+ Self::NotRequired => write!(f, "GenieZone call not required"),
+ Self::InvalidParameter => write!(f, "GenieZone call received invalid value"),
+ Self::Unknown(e) => write!(f, "Unknown return value from GenieZone {} ({0:#x})", e),
+ }
+ }
+}
+
+impl Hypervisor for GeniezoneHypervisor {
+ fn as_mmio_guard(&self) -> Option<&dyn MmioGuardedHypervisor> {
+ Some(self)
+ }
+
+ fn as_mem_sharer(&self) -> Option<&dyn MemSharingHypervisor> {
+ Some(self)
+ }
+}
+
+impl MmioGuardedHypervisor for GeniezoneHypervisor {
+ fn enroll(&self) -> Result<()> {
+ let args = [0u64; 17];
+ match success_or_error_64(hvc64(VENDOR_HYP_GZVM_MMIO_GUARD_ENROLL_FUNC_ID, args)[0]) {
+ Ok(()) => Ok(()),
+ Err(GeniezoneError::NotSupported) | Err(GeniezoneError::NotRequired) => {
+ Err(Error::MmioGuardNotSupported)
+ }
+ Err(e) => Err(Error::GeniezoneError(e, VENDOR_HYP_GZVM_MMIO_GUARD_ENROLL_FUNC_ID)),
+ }
+ }
+
+ fn map(&self, addr: usize) -> Result<()> {
+ let mut args = [0u64; 17];
+ args[0] = page_address(addr);
+
+ checked_hvc64_expect_zero(VENDOR_HYP_GZVM_MMIO_GUARD_MAP_FUNC_ID, args)
+ }
+
+ fn unmap(&self, addr: usize) -> Result<()> {
+ let mut args = [0u64; 17];
+ args[0] = page_address(addr);
+
+ checked_hvc64_expect_zero(VENDOR_HYP_GZVM_MMIO_GUARD_UNMAP_FUNC_ID, args)
+ }
+
+ fn granule(&self) -> Result<usize> {
+ let args = [0u64; 17];
+ let granule = checked_hvc64(VENDOR_HYP_GZVM_MMIO_GUARD_INFO_FUNC_ID, args)?;
+ Ok(granule.try_into().unwrap())
+ }
+}
+
+impl MemSharingHypervisor for GeniezoneHypervisor {
+ fn share(&self, base_ipa: u64) -> Result<()> {
+ let mut args = [0u64; 17];
+ args[0] = base_ipa;
+
+ checked_hvc64_expect_zero(ARM_SMCCC_GZVM_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_GZVM_FUNC_MEM_UNSHARE, args)
+ }
+
+ fn granule(&self) -> Result<usize> {
+ let args = [0u64; 17];
+ let granule = checked_hvc64(ARM_SMCCC_GZVM_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::GeniezoneError(e, function))
+}
+
+fn checked_hvc64(function: u32, args: [u64; 17]) -> Result<u64> {
+ positive_or_error_64(hvc64(function, args)[0]).map_err(|e| Error::GeniezoneError(e, function))
+}
diff --git a/vmbase/src/hyp/hypervisor/gunyah.rs b/vmbase/src/hyp/hypervisor/gunyah.rs
new file mode 100644
index 0000000..45c01bf
--- /dev/null
+++ b/vmbase/src/hyp/hypervisor/gunyah.rs
@@ -0,0 +1,10 @@
+use super::common::Hypervisor;
+use uuid::{uuid, Uuid};
+
+pub(super) struct GunyahHypervisor;
+
+impl GunyahHypervisor {
+ pub const UUID: Uuid = uuid!("c1d58fcd-a453-5fdb-9265-ce36673d5f14");
+}
+
+impl Hypervisor for GunyahHypervisor {}
diff --git a/vmbase/src/hyp/hypervisor/kvm.rs b/vmbase/src/hyp/hypervisor/kvm.rs
new file mode 100644
index 0000000..8b59234
--- /dev/null
+++ b/vmbase/src/hyp/hypervisor/kvm.rs
@@ -0,0 +1,196 @@
+// 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 core::fmt::{self, Display, Formatter};
+
+use super::{DeviceAssigningHypervisor, Hypervisor, MemSharingHypervisor, MmioGuardedHypervisor};
+use crate::hyp::{util::page_address, Error, Result};
+
+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;
+
+const VENDOR_HYP_KVM_DEV_REQ_MMIO_FUNC_ID: u32 = 0xc6000012;
+const VENDOR_HYP_KVM_DEV_REQ_DMA_FUNC_ID: u32 = 0xc6000013;
+
+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)
+ }
+
+ fn as_device_assigner(&self) -> Option<&dyn DeviceAssigningHypervisor> {
+ 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())
+ }
+}
+
+impl DeviceAssigningHypervisor for ProtectedKvmHypervisor {
+ fn get_phys_mmio_token(&self, base_ipa: u64, size: u64) -> Result<u64> {
+ let mut args = [0u64; 17];
+ args[0] = base_ipa;
+ args[1] = size;
+
+ let ret = checked_hvc64_expect_results(VENDOR_HYP_KVM_DEV_REQ_MMIO_FUNC_ID, args)?;
+ Ok(ret[0])
+ }
+
+ fn get_phys_iommu_token(&self, pviommu_id: u64, vsid: u64) -> Result<(u64, u64)> {
+ let mut args = [0u64; 17];
+ args[0] = pviommu_id;
+ args[1] = vsid;
+
+ let ret = checked_hvc64_expect_results(VENDOR_HYP_KVM_DEV_REQ_DMA_FUNC_ID, args)?;
+ Ok((ret[0], ret[1]))
+ }
+}
+
+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))
+}
+
+fn checked_hvc64_expect_results(function: u32, args: [u64; 17]) -> Result<[u64; 17]> {
+ let [ret, results @ ..] = hvc64(function, args);
+ success_or_error_64(ret).map_err(|e| Error::KvmError(e, function))?;
+ Ok(results)
+}
diff --git a/vmbase/src/hyp/util.rs b/vmbase/src/hyp/util.rs
new file mode 100644
index 0000000..56f94fd
--- /dev/null
+++ b/vmbase/src/hyp/util.rs
@@ -0,0 +1,22 @@
+// 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.
+
+//! Utility functions.
+
+pub(crate) const SIZE_4KB: usize = 4 << 10;
+
+/// Computes the low memory page address of the 4KiB page containing a given address.
+pub(crate) fn page_address(addr: usize) -> u64 {
+ (addr & !(SIZE_4KB - 1)).try_into().unwrap()
+}
diff --git a/vmbase/src/lib.rs b/vmbase/src/lib.rs
index 431e899..630834b 100644
--- a/vmbase/src/lib.rs
+++ b/vmbase/src/lib.rs
@@ -26,6 +26,7 @@
pub mod fdt;
pub mod heap;
mod hvc;
+pub mod hyp;
pub mod layout;
pub mod linker;
pub mod logger;
diff --git a/vmbase/src/memory/error.rs b/vmbase/src/memory/error.rs
index 273db56..1af8f8c 100644
--- a/vmbase/src/memory/error.rs
+++ b/vmbase/src/memory/error.rs
@@ -16,6 +16,8 @@
use core::fmt;
+use crate::hyp;
+
/// Errors for MemoryTracker operations.
#[derive(Debug, Clone)]
pub enum MemoryTrackerError {
diff --git a/vmbase/src/memory/shared.rs b/vmbase/src/memory/shared.rs
index dd433d4..8fda7a6 100644
--- a/vmbase/src/memory/shared.rs
+++ b/vmbase/src/memory/shared.rs
@@ -20,6 +20,7 @@
use super::util::{page_4kb_of, virt_to_phys};
use crate::dsb;
use crate::exceptions::HandleExceptionError;
+use crate::hyp::{self, get_mem_sharer, get_mmio_guard, MMIO_GUARD_GRANULE_SIZE};
use crate::util::RangeExt as _;
use aarch64_paging::paging::{
Attributes, Descriptor, MemoryRegion as VaRange, VirtualAddress, BITS_PER_LEVEL, PAGE_SIZE,
@@ -35,7 +36,6 @@
use core::ops::Range;
use core::ptr::NonNull;
use core::result;
-use hyp::{get_mem_sharer, get_mmio_guard, MMIO_GUARD_GRANULE_SIZE};
use log::{debug, error, trace};
use once_cell::race::OnceBox;
use spin::mutex::SpinMutex;