[hypervisor] Detect hypervisor

Detect underlying hypervisor as a first step for being able to do
hypervisor-specific operations.

Test: m pvmfw_img
Bug: 271493784
Change-Id: I82cc31f3b852bd295eedd492a20e4ead5d128bf0
diff --git a/libs/hyp/Android.bp b/libs/hyp/Android.bp
index e4353c8..0f125fc 100644
--- a/libs/hyp/Android.bp
+++ b/libs/hyp/Android.bp
@@ -8,7 +8,9 @@
     srcs: ["src/lib.rs"],
     prefer_rlib: true,
     rustlibs: [
+        "libonce_cell_nostd",
         "libpsci",
+        "libuuid_nostd",
     ],
     no_stdlibs: true,
     stdlibs: [
diff --git a/libs/hyp/src/error.rs b/libs/hyp/src/error.rs
index 4e25e7f..408150e 100644
--- a/libs/hyp/src/error.rs
+++ b/libs/hyp/src/error.rs
@@ -16,6 +16,7 @@
 
 use crate::KvmError;
 use core::{fmt, result};
+use uuid::Uuid;
 
 /// Result type with hypervisor error.
 pub type Result<T> = result::Result<T, Error>;
@@ -27,6 +28,8 @@
     MmioGuardNotsupported,
     /// Failed to invoke a certain KVM HVC function.
     KvmError(KvmError, u32),
+    /// Unsupported Hypervisor.
+    UnsupportedHypervisorUuid(Uuid),
     /// The MMIO_GUARD granule used by the hypervisor is not supported.
     UnsupportedMmioGuardGranule(usize),
 }
@@ -38,6 +41,9 @@
             Self::KvmError(e, function_id) => {
                 write!(f, "Failed to invoke the 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/libs/hyp/src/hypervisor/kvm.rs b/libs/hyp/src/hypervisor/kvm.rs
index c0c1ac9..00efde4 100644
--- a/libs/hyp/src/hypervisor/kvm.rs
+++ b/libs/hyp/src/hypervisor/kvm.rs
@@ -22,6 +22,7 @@
     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)]
@@ -71,6 +72,12 @@
 
 pub(super) struct KvmHypervisor;
 
+impl KvmHypervisor {
+    // 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 KvmHypervisor {
     fn mmio_guard_init(&self) -> Result<()> {
         mmio_guard_enroll()?;
diff --git a/libs/hyp/src/hypervisor/mod.rs b/libs/hyp/src/hypervisor/mod.rs
index a694029..dc9e7c3 100644
--- a/libs/hyp/src/hypervisor/mod.rs
+++ b/libs/hyp/src/hypervisor/mod.rs
@@ -14,14 +14,19 @@
 
 //! Wrappers around hypervisor back-ends.
 
+extern crate alloc;
+
 mod common;
 mod kvm;
 
+use crate::error::{Error, Result};
+use alloc::boxed::Box;
 pub use common::Hypervisor;
 pub use kvm::KvmError;
 use kvm::KvmHypervisor;
-
-static HYPERVISOR: HypervisorBackend = HypervisorBackend::Kvm;
+use once_cell::race::OnceBox;
+use psci::smccc::hvc64;
+use uuid::Uuid;
 
 enum HypervisorBackend {
     Kvm,
@@ -35,7 +40,50 @@
     }
 }
 
+impl TryFrom<Uuid> for HypervisorBackend {
+    type Error = Error;
+
+    fn try_from(uuid: Uuid) -> Result<HypervisorBackend> {
+        match uuid {
+            KvmHypervisor::UUID => Ok(HypervisorBackend::Kvm),
+            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("Unknown hypervisor")
+}
+
 /// Gets the hypervisor singleton.
 pub fn get_hypervisor() -> &'static dyn Hypervisor {
-    HYPERVISOR.get_hypervisor()
+    static HYPERVISOR: OnceBox<HypervisorBackend> = OnceBox::new();
+
+    HYPERVISOR.get_or_init(|| Box::new(detect_hypervisor())).get_hypervisor()
 }