vmbase: Move rand.rs out of pvmfw

The module isn't pvmfw-specific and could be re-used by other
vmbase-based code so move it. This will allow better future integration
with the TRNG already performed in vmbase/entry.S and with the C
interface exported by vmbase/src/bionic.rs.

Test: TH
Change-Id: I842425264c87cfe97618aceda371b410d7af6046
diff --git a/vmbase/src/hvc.rs b/vmbase/src/hvc.rs
new file mode 100644
index 0000000..9a5e716
--- /dev/null
+++ b/vmbase/src/hvc.rs
@@ -0,0 +1,51 @@
+// 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 hypervisor.
+
+pub mod trng;
+use self::trng::Error;
+use smccc::{
+    error::{positive_or_error_64, success_or_error_64},
+    hvc64,
+};
+
+const ARM_SMCCC_TRNG_VERSION: u32 = 0x8400_0050;
+#[allow(dead_code)]
+const ARM_SMCCC_TRNG_FEATURES: u32 = 0x8400_0051;
+#[allow(dead_code)]
+const ARM_SMCCC_TRNG_GET_UUID: u32 = 0x8400_0052;
+#[allow(dead_code)]
+const ARM_SMCCC_TRNG_RND32: u32 = 0x8400_0053;
+const ARM_SMCCC_TRNG_RND64: u32 = 0xc400_0053;
+
+/// Returns the (major, minor) version tuple, as defined by the SMCCC TRNG.
+pub fn trng_version() -> trng::Result<(u16, u16)> {
+    let args = [0u64; 17];
+
+    let version = positive_or_error_64::<Error>(hvc64(ARM_SMCCC_TRNG_VERSION, args)[0])?;
+    Ok(((version >> 16) as u16, version as u16))
+}
+
+pub type TrngRng64Entropy = (u64, u64, u64);
+
+pub fn trng_rnd64(nbits: u64) -> trng::Result<TrngRng64Entropy> {
+    let mut args = [0u64; 17];
+    args[0] = nbits;
+
+    let regs = hvc64(ARM_SMCCC_TRNG_RND64, args);
+    success_or_error_64::<Error>(regs[0])?;
+
+    Ok((regs[1], regs[2], regs[3]))
+}
diff --git a/vmbase/src/hvc/trng.rs b/vmbase/src/hvc/trng.rs
new file mode 100644
index 0000000..6331d66
--- /dev/null
+++ b/vmbase/src/hvc/trng.rs
@@ -0,0 +1,57 @@
+// 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.
+
+use core::fmt;
+use core::result;
+
+/// Standard SMCCC TRNG error values as described in DEN 0098 1.0 REL0.
+#[derive(Debug, Clone)]
+pub enum Error {
+    /// The call is not supported by the implementation.
+    NotSupported,
+    /// One of the call parameters has a non-supported value.
+    InvalidParameter,
+    /// Call returned without the requested entropy bits.
+    NoEntropy,
+    /// Negative values indicate error.
+    Unknown(i64),
+    /// The call returned a positive value when 0 was expected.
+    Unexpected(u64),
+}
+
+impl fmt::Display for Error {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            Self::NotSupported => write!(f, "SMCCC TRNG call not supported"),
+            Self::InvalidParameter => write!(f, "SMCCC TRNG call received non-supported value"),
+            Self::NoEntropy => write!(f, "SMCCC TRNG call returned no entropy"),
+            Self::Unexpected(v) => write!(f, "Unexpected SMCCC TRNG return value {} ({0:#x})", v),
+            Self::Unknown(e) => write!(f, "Unknown SMCCC TRNG return value {} ({0:#x})", e),
+        }
+    }
+}
+
+impl From<i64> for Error {
+    fn from(value: i64) -> Self {
+        match value {
+            -1 => Error::NotSupported,
+            -2 => Error::InvalidParameter,
+            -3 => Error::NoEntropy,
+            _ if value < 0 => Error::Unknown(value),
+            _ => Error::Unexpected(value as u64),
+        }
+    }
+}
+
+pub type Result<T> = result::Result<T, Error>;
diff --git a/vmbase/src/lib.rs b/vmbase/src/lib.rs
index 54f3384..7fc7b20 100644
--- a/vmbase/src/lib.rs
+++ b/vmbase/src/lib.rs
@@ -23,11 +23,13 @@
 pub mod console;
 mod entry;
 pub mod fdt;
+mod hvc;
 pub mod layout;
 mod linker;
 pub mod logger;
 pub mod memory;
 pub mod power;
+pub mod rand;
 pub mod uart;
 pub mod util;
 pub mod virtio;
diff --git a/vmbase/src/rand.rs b/vmbase/src/rand.rs
new file mode 100644
index 0000000..00567b8
--- /dev/null
+++ b/vmbase/src/rand.rs
@@ -0,0 +1,120 @@
+// 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.
+
+//! Functions and drivers for obtaining true entropy.
+
+use crate::hvc;
+use core::fmt;
+use core::mem::size_of;
+
+/// Error type for rand operations.
+pub enum Error {
+    /// Error during SMCCC TRNG call.
+    Trng(hvc::trng::Error),
+    /// Unsupported SMCCC TRNG version.
+    UnsupportedVersion((u16, u16)),
+}
+
+impl From<hvc::trng::Error> for Error {
+    fn from(e: hvc::trng::Error) -> Self {
+        Self::Trng(e)
+    }
+}
+
+/// Result type for rand operations.
+pub type Result<T> = core::result::Result<T, Error>;
+
+impl fmt::Display for Error {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            Self::Trng(e) => write!(f, "SMCCC TRNG error: {e}"),
+            Self::UnsupportedVersion((x, y)) => {
+                write!(f, "Unsupported SMCCC TRNG version v{x}.{y}")
+            }
+        }
+    }
+}
+
+impl fmt::Debug for Error {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{self}")
+    }
+}
+
+/// Configure the source of entropy.
+pub fn init() -> Result<()> {
+    match hvc::trng_version()? {
+        (1, _) => Ok(()),
+        version => Err(Error::UnsupportedVersion(version)),
+    }
+}
+
+fn fill_with_entropy(s: &mut [u8]) -> Result<()> {
+    const MAX_BYTES_PER_CALL: usize = size_of::<hvc::TrngRng64Entropy>();
+
+    let (aligned, remainder) = s.split_at_mut(s.len() - s.len() % MAX_BYTES_PER_CALL);
+
+    for chunk in aligned.chunks_exact_mut(MAX_BYTES_PER_CALL) {
+        let (r, s, t) = repeat_trng_rnd(chunk.len())?;
+
+        let mut words = chunk.chunks_exact_mut(size_of::<u64>());
+        words.next().unwrap().clone_from_slice(&t.to_ne_bytes());
+        words.next().unwrap().clone_from_slice(&s.to_ne_bytes());
+        words.next().unwrap().clone_from_slice(&r.to_ne_bytes());
+    }
+
+    if !remainder.is_empty() {
+        let mut entropy = [0; MAX_BYTES_PER_CALL];
+        let (r, s, t) = repeat_trng_rnd(remainder.len())?;
+
+        let mut words = entropy.chunks_exact_mut(size_of::<u64>());
+        words.next().unwrap().clone_from_slice(&t.to_ne_bytes());
+        words.next().unwrap().clone_from_slice(&s.to_ne_bytes());
+        words.next().unwrap().clone_from_slice(&r.to_ne_bytes());
+
+        remainder.clone_from_slice(&entropy[..remainder.len()]);
+    }
+
+    Ok(())
+}
+
+fn repeat_trng_rnd(n_bytes: usize) -> hvc::trng::Result<hvc::TrngRng64Entropy> {
+    let bits = usize::try_from(u8::BITS).unwrap();
+    let n_bits = (n_bytes * bits).try_into().unwrap();
+    loop {
+        match hvc::trng_rnd64(n_bits) {
+            Err(hvc::trng::Error::NoEntropy) => continue,
+            res => return res,
+        }
+    }
+}
+
+/// Generate an array of fixed-size initialized with true-random bytes.
+pub fn random_array<const N: usize>() -> Result<[u8; N]> {
+    let mut arr = [0; N];
+    fill_with_entropy(&mut arr)?;
+    Ok(arr)
+}
+
+#[no_mangle]
+extern "C" fn CRYPTO_sysrand_for_seed(out: *mut u8, req: usize) {
+    CRYPTO_sysrand(out, req)
+}
+
+#[no_mangle]
+extern "C" fn CRYPTO_sysrand(out: *mut u8, req: usize) {
+    // SAFETY - We need to assume that out points to valid memory of size req.
+    let s = unsafe { core::slice::from_raw_parts_mut(out, req) };
+    fill_with_entropy(s).unwrap()
+}