vmbase: Move Trng internals to arch::aarch64
This commit moves trng implementation from common rand module to a
aarch64 arch module. This is at the moment purely cosmetic change,
however the purpose is move all aarch64 specific code away from common
modules to enable bringup of x86_64 support.
Bug: 362733888
Test: m pvmfw
Test: pVM boots with pvmfw
Change-Id: I5ac315ed38491de8b66f07a5df703cb50786c8bb
diff --git a/libs/libvmbase/src/arch/aarch64.rs b/libs/libvmbase/src/arch/aarch64.rs
index 05b5566..9c4d256 100644
--- a/libs/libvmbase/src/arch/aarch64.rs
+++ b/libs/libvmbase/src/arch/aarch64.rs
@@ -21,6 +21,7 @@
pub mod linker;
pub mod page_table;
pub mod platform;
+pub mod rand;
pub mod uart;
/// Reads a value from a system register.
diff --git a/libs/libvmbase/src/arch/aarch64/rand.rs b/libs/libvmbase/src/arch/aarch64/rand.rs
new file mode 100644
index 0000000..4fd1905
--- /dev/null
+++ b/libs/libvmbase/src/arch/aarch64/rand.rs
@@ -0,0 +1,131 @@
+// Copyright 2025, 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.
+
+//! Random number generator implementation for aarch64 platforms using TRNG
+
+use crate::arch::aarch64::hvc;
+use crate::rand::{Entropy, Error, Result};
+use core::fmt;
+use core::mem::size_of;
+use smccc::{self, Hvc};
+use zerocopy::IntoBytes as _;
+
+/// Error type for rand operations.
+pub enum PlatformError {
+ /// Error during architectural SMCCC call.
+ Smccc(smccc::arch::Error),
+ /// Error during SMCCC TRNG call.
+ Trng(hvc::trng::Error),
+ /// Unsupported SMCCC version.
+ UnsupportedSmcccVersion(smccc::arch::Version),
+ /// Unsupported SMCCC TRNG version.
+ UnsupportedTrngVersion(hvc::trng::Version),
+}
+
+impl From<smccc::arch::Error> for Error {
+ fn from(e: smccc::arch::Error) -> Self {
+ Self::Platform(PlatformError::Smccc(e))
+ }
+}
+
+impl From<hvc::trng::Error> for Error {
+ fn from(e: hvc::trng::Error) -> Self {
+ Self::Platform(PlatformError::Trng(e))
+ }
+}
+
+impl fmt::Display for PlatformError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Self::Smccc(e) => write!(f, "Architectural SMCCC error: {e}"),
+ Self::Trng(e) => write!(f, "SMCCC TRNG error: {e}"),
+ Self::UnsupportedSmcccVersion(v) => write!(f, "Unsupported SMCCC version {v}"),
+ Self::UnsupportedTrngVersion(v) => write!(f, "Unsupported SMCCC TRNG version {v}"),
+ }
+ }
+}
+
+impl fmt::Debug for PlatformError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{self}")
+ }
+}
+
+pub(crate) const MAX_BYTES_PER_CALL: usize = size_of::<u64>() * 3;
+
+/// Configure the source of entropy.
+pub(crate) fn init() -> Result<()> {
+ // SMCCC TRNG requires SMCCC v1.1.
+ match smccc::arch::version::<Hvc>()? {
+ smccc::arch::Version { major: 1, minor } if minor >= 1 => (),
+ version => return Err(PlatformError::UnsupportedSmcccVersion(version).into()),
+ }
+
+ // TRNG_RND requires SMCCC TRNG v1.0.
+ match hvc::trng_version()? {
+ hvc::trng::Version { major: 1, minor: _ } => (),
+ version => return Err(PlatformError::UnsupportedTrngVersion(version).into()),
+ }
+
+ // TRNG_RND64 doesn't define any special capabilities so ignore the successful result.
+ let _ = hvc::trng_features(hvc::ARM_SMCCC_TRNG_RND64).map_err(|e| {
+ if e == hvc::trng::Error::NotSupported {
+ // SMCCC TRNG is currently our only source of entropy.
+ Error::NoEntropySource
+ } else {
+ e.into()
+ }
+ })?;
+
+ Ok(())
+}
+
+/// Returns an array where the first `n_bytes` bytes hold entropy.
+///
+/// The rest of the array should be ignored.
+pub(crate) fn platform_entropy(n_bytes: usize) -> Result<Entropy> {
+ loop {
+ if let Some(entropy) = rnd64(n_bytes)? {
+ return Ok(entropy);
+ }
+ }
+}
+
+/// Returns an array where the first `n_bytes` bytes hold entropy, if available.
+///
+/// The rest of the array should be ignored.
+fn rnd64(n_bytes: usize) -> Result<Option<Entropy>> {
+ let bits = usize::try_from(u8::BITS).unwrap();
+ let result = hvc::trng_rnd64((n_bytes * bits).try_into().unwrap());
+ let entropy = if matches!(result, Err(hvc::trng::Error::NoEntropy)) {
+ None
+ } else {
+ let r = result?;
+ // From the SMCCC TRNG:
+ //
+ // A MAX_BITS-bits wide value (Entropy) is returned across X1 to X3.
+ // The requested conditioned entropy is returned in Entropy[N-1:0].
+ //
+ // X1 Entropy[191:128]
+ // X2 Entropy[127:64]
+ // X3 Entropy[63:0]
+ //
+ // The bits in Entropy[MAX_BITS-1:N] are 0.
+ let reordered = [r[2].to_le(), r[1].to_le(), r[0].to_le()];
+
+ Some(reordered.as_bytes().try_into().unwrap())
+ };
+
+ Ok(entropy)
+}