hypervisor: Add support for GenieZone

Enable GenieZone hypervisor as one of the backend options.

Bug: 284377484
Test: Booted Microdroid VM from GenieZone hypervisor

Signed-off-by: Jerry Wang <ze-yu.wang@mediatek.com>
Signed-off-by: Yi-De Wu <yi-de.wu@mediatek.com>

Change-Id: I01f0b8f9fecf46e53790a03588eef1d49091b385
diff --git a/libs/hyp/src/error.rs b/libs/hyp/src/error.rs
index 408150e..b8498ca 100644
--- a/libs/hyp/src/error.rs
+++ b/libs/hyp/src/error.rs
@@ -14,6 +14,7 @@
 
 //! Error and Result types for hypervisor.
 
+use crate::GeniezoneError;
 use crate::KvmError;
 use core::{fmt, result};
 use uuid::Uuid;
@@ -28,7 +29,9 @@
     MmioGuardNotsupported,
     /// Failed to invoke a certain KVM HVC function.
     KvmError(KvmError, u32),
-    /// Unsupported Hypervisor.
+    /// 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),
@@ -41,6 +44,12 @@
             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/hyp/src/hypervisor/geniezone.rs b/libs/hyp/src/hypervisor/geniezone.rs
new file mode 100644
index 0000000..73daff7
--- /dev/null
+++ b/libs/hyp/src/hypervisor/geniezone.rs
@@ -0,0 +1,159 @@
+// 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 super::common::{Hypervisor, HypervisorCap, MMIO_GUARD_GRANULE_SIZE};

+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_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");

+    const CAPABILITIES: HypervisorCap = HypervisorCap::DYNAMIC_MEM_SHARE;

+}

+

+/// 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 mmio_guard_init(&self) -> Result<()> {

+        mmio_guard_enroll()?;

+        let mmio_granule = mmio_guard_granule()?;

+        if mmio_granule != MMIO_GUARD_GRANULE_SIZE {

+            return Err(Error::UnsupportedMmioGuardGranule(mmio_granule));

+        }

+        Ok(())

+    }

+

+    fn mmio_guard_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 mmio_guard_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 mem_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 mem_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 memory_protection_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 has_cap(&self, cap: HypervisorCap) -> bool {

+        Self::CAPABILITIES.contains(cap)

+    }

+}

+

+fn mmio_guard_granule() -> Result<usize> {

+    let args = [0u64; 17];

+

+    let granule = checked_hvc64(VENDOR_HYP_GZVM_MMIO_GUARD_INFO_FUNC_ID, args)?;

+    Ok(granule.try_into().unwrap())

+}

+

+fn mmio_guard_enroll() -> 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(Error::MmioGuardNotsupported),

+        Err(GeniezoneError::NotRequired) => Err(Error::MmioGuardNotsupported),

+        Err(e) => Err(Error::GeniezoneError(e, VENDOR_HYP_GZVM_MMIO_GUARD_ENROLL_FUNC_ID)),

+    }

+}

+

+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/hyp/src/hypervisor/mod.rs b/libs/hyp/src/hypervisor/mod.rs
index 923a21d..93d53fe 100644
--- a/libs/hyp/src/hypervisor/mod.rs
+++ b/libs/hyp/src/hypervisor/mod.rs
@@ -17,6 +17,7 @@
 extern crate alloc;
 
 mod common;
+mod geniezone;
 mod gunyah;
 mod kvm;
 
@@ -25,6 +26,8 @@
 pub use common::Hypervisor;
 pub use common::HypervisorCap;
 pub use common::MMIO_GUARD_GRANULE_SIZE;
+pub use geniezone::GeniezoneError;
+use geniezone::GeniezoneHypervisor;
 use gunyah::GunyahHypervisor;
 pub use kvm::KvmError;
 use kvm::KvmHypervisor;
@@ -35,6 +38,7 @@
 enum HypervisorBackend {
     Kvm,
     Gunyah,
+    Geniezone,
 }
 
 impl HypervisorBackend {
@@ -42,6 +46,7 @@
         match self {
             Self::Kvm => &KvmHypervisor,
             Self::Gunyah => &GunyahHypervisor,
+            Self::Geniezone => &GeniezoneHypervisor,
         }
     }
 }
@@ -51,6 +56,7 @@
 
     fn try_from(uuid: Uuid) -> Result<HypervisorBackend> {
         match uuid {
+            GeniezoneHypervisor::UUID => Ok(HypervisorBackend::Geniezone),
             GunyahHypervisor::UUID => Ok(HypervisorBackend::Gunyah),
             KvmHypervisor::UUID => Ok(HypervisorBackend::Kvm),
             u => Err(Error::UnsupportedHypervisorUuid(u)),
diff --git a/libs/hyp/src/lib.rs b/libs/hyp/src/lib.rs
index 2c2d1d6..32a59d1 100644
--- a/libs/hyp/src/lib.rs
+++ b/libs/hyp/src/lib.rs
@@ -22,3 +22,5 @@
 
 pub use error::{Error, Result};
 pub use hypervisor::{get_hypervisor, Hypervisor, HypervisorCap, KvmError, MMIO_GUARD_GRANULE_SIZE};
+
+use hypervisor::GeniezoneError;