Turn hypervisor backends module into a library

Break out the code in the hyp module of libvmbase
into its own library such that it can be used from
other contexts such as the Trusty kernel.

Update uses in guest/{pvmfw,rialto}.

Add Trusty build rules for the new library.

Test: atest
Test: build.py generic-x86_64-test
Change-Id: I68670549c8c8e1b3c8539c2c297e2ca1ba24edfb
diff --git a/libs/libhypervisor_backends/Android.bp b/libs/libhypervisor_backends/Android.bp
new file mode 100644
index 0000000..b001b8f
--- /dev/null
+++ b/libs/libhypervisor_backends/Android.bp
@@ -0,0 +1,35 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_library_rlib {
+    name: "libhypervisor_backends",
+    crate_name: "hypervisor_backends",
+    defaults: ["avf_build_flags_rust"],
+    edition: "2021",
+    prefer_rlib: true,
+    host_supported: false,
+    no_stdlibs: true,
+    srcs: ["src/lib.rs"],
+    rustlibs: [
+        "libonce_cell_nostd",
+        "libsmccc",
+        "libuuid_nostd",
+    ],
+    enabled: false,
+    target: {
+        android_arm64: {
+            enabled: true,
+            stdlibs: [
+                "libcompiler_builtins.rust_sysroot",
+                "libcore.rust_sysroot",
+            ],
+        },
+    },
+}
+
+dirgroup {
+    name: "trusty_dirgroup_packages_modules_virtualization_libs_libhypervisor_backends",
+    visibility: ["//trusty/vendor/google/aosp/scripts"],
+    dirs: ["."],
+}
diff --git a/libs/libhypervisor_backends/rules.mk b/libs/libhypervisor_backends/rules.mk
new file mode 100644
index 0000000..6fc9dea
--- /dev/null
+++ b/libs/libhypervisor_backends/rules.mk
@@ -0,0 +1,13 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+MODULE := $(LOCAL_DIR)
+MODULE_CRATE_NAME := hypervisor_backends
+MODULE_SRCS := \
+	$(LOCAL_DIR)/src/lib.rs \
+
+MODULE_LIBRARY_DEPS := \
+	trusty/user/base/lib/liballoc-rust \
+	$(call FIND_CRATE,once_cell) \
+	$(call FIND_CRATE,smccc) \
+	$(call FIND_CRATE,uuid) \
+
+include make/library.mk
\ No newline at end of file
diff --git a/libs/libhypervisor_backends/src/error.rs b/libs/libhypervisor_backends/src/error.rs
new file mode 100644
index 0000000..e9c37e1
--- /dev/null
+++ b/libs/libhypervisor_backends/src/error.rs
@@ -0,0 +1,56 @@
+// 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),
+}
+
+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}")
+            }
+        }
+    }
+}
diff --git a/libs/libhypervisor_backends/src/hypervisor.rs b/libs/libhypervisor_backends/src/hypervisor.rs
new file mode 100644
index 0000000..1b45f38
--- /dev/null
+++ b/libs/libhypervisor_backends/src/hypervisor.rs
@@ -0,0 +1,127 @@
+// 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};
+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/libs/libhypervisor_backends/src/hypervisor/common.rs b/libs/libhypervisor_backends/src/hypervisor/common.rs
new file mode 100644
index 0000000..bfe638f
--- /dev/null
+++ b/libs/libhypervisor_backends/src/hypervisor/common.rs
@@ -0,0 +1,76 @@
+// 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::Result;
+
+/// 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>;
+}
+
+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) -> 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/libs/libhypervisor_backends/src/hypervisor/geniezone.rs b/libs/libhypervisor_backends/src/hypervisor/geniezone.rs
new file mode 100644
index 0000000..fe56528
--- /dev/null
+++ b/libs/libhypervisor_backends/src/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::{mem::page_4kb_of, 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_4kb_of(addr).try_into().unwrap();
+
+        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_4kb_of(addr).try_into().unwrap();
+
+        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/libs/libhypervisor_backends/src/hypervisor/gunyah.rs b/libs/libhypervisor_backends/src/hypervisor/gunyah.rs
new file mode 100644
index 0000000..45c01bf
--- /dev/null
+++ b/libs/libhypervisor_backends/src/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/libs/libhypervisor_backends/src/hypervisor/kvm.rs b/libs/libhypervisor_backends/src/hypervisor/kvm.rs
new file mode 100644
index 0000000..e18c1f4
--- /dev/null
+++ b/libs/libhypervisor_backends/src/hypervisor/kvm.rs
@@ -0,0 +1,203 @@
+// 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::{mem::page_4kb_of, 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 = 0xc600001b;
+
+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_4kb_of(addr).try_into().unwrap();
+
+        if cfg!(feature = "compat_android_13") {
+            let res = hvc64(VENDOR_HYP_KVM_MMIO_GUARD_MAP_FUNC_ID, args)[0];
+            // pKVM returns i32 instead of the intended i64 in Android 13.
+            return success_or_error_32(res as u32)
+                .map_err(|e| Error::KvmError(e, VENDOR_HYP_KVM_MMIO_GUARD_MAP_FUNC_ID));
+        }
+
+        checked_hvc64_expect_zero(VENDOR_HYP_KVM_MMIO_GUARD_MAP_FUNC_ID, args)
+    }
+
+    fn unmap(&self, addr: usize) -> Result<()> {
+        let mut args = [0u64; 17];
+        args[0] = page_4kb_of(addr).try_into().unwrap();
+
+        if cfg!(feature = "compat_android_13") {
+            let res = hvc64(VENDOR_HYP_KVM_MMIO_GUARD_UNMAP_FUNC_ID, args)[0];
+            // pKVM returns NOT_SUPPORTED for SUCCESS in Android 13.
+            return match success_or_error_64(res) {
+                Err(KvmError::NotSupported) | Ok(_) => Ok(()),
+                Err(e) => Err(Error::KvmError(e, VENDOR_HYP_KVM_MMIO_GUARD_UNMAP_FUNC_ID)),
+            };
+        }
+
+        checked_hvc64_expect_zero(VENDOR_HYP_KVM_MMIO_GUARD_UNMAP_FUNC_ID, args)
+    }
+
+    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) -> Result<u64> {
+        let mut args = [0u64; 17];
+        args[0] = base_ipa;
+
+        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/libs/libhypervisor_backends/src/lib.rs b/libs/libhypervisor_backends/src/lib.rs
new file mode 100644
index 0000000..33dc5ad
--- /dev/null
+++ b/libs/libhypervisor_backends/src/lib.rs
@@ -0,0 +1,28 @@
+// 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.
+
+#![no_std]
+
+extern crate alloc;
+
+mod error;
+mod hypervisor;
+mod mem;
+
+pub use error::{Error, Result};
+pub use hypervisor::{
+    get_device_assigner, get_mem_sharer, get_mmio_guard, DeviceAssigningHypervisor, KvmError,
+};
diff --git a/libs/libhypervisor_backends/src/mem.rs b/libs/libhypervisor_backends/src/mem.rs
new file mode 100644
index 0000000..ff65c49
--- /dev/null
+++ b/libs/libhypervisor_backends/src/mem.rs
@@ -0,0 +1,28 @@
+// Copyright 2024, 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.
+
+/// The size of a 4KB memory in bytes.
+pub const SIZE_4KB: usize = 4 << 10;
+
+/// Computes the largest multiple of the provided alignment smaller or equal to the address.
+///
+/// Note: the result is undefined if alignment isn't a power of two.
+pub const fn unchecked_align_down(addr: usize, alignment: usize) -> usize {
+    addr & !(alignment - 1)
+}
+
+/// Computes the address of the 4KiB page containing a given address.
+pub const fn page_4kb_of(addr: usize) -> usize {
+    unchecked_align_down(addr, SIZE_4KB)
+}