pvmfw: Loop SMCCC_TRNG_RND on NO_ENTROPY
As the firmware is allowed by the specification to return NO_ENTROPY:
The call is non-blocking, when the available entropy is less than N,
the call must returns `NO_ENTROPY`.
There are system designs where FW may limit the rate at which
entropy is requested by a caller.
To enable this entropy request rate limitation, the call is allowed
to return `NO_ENTROPY` even if sufficient conditioned
Loop until we receive the requested entropy instead of throwing an error
(resulting in a pvmfw panic).
Note that this could cause a pVM to become unresponsive during boot if
the firmware never returns the requested entropy (a behaviour that seems
to be compliant with the spec).
Bug: 270841564
Test: atest MicrodroidHostTests
Change-Id: If2bb3fba0dcbded3772a41c9b7e6e7fb1180f225
diff --git a/pvmfw/src/rand.rs b/pvmfw/src/rand.rs
index a53cac6..bf0edd5 100644
--- a/pvmfw/src/rand.rs
+++ b/pvmfw/src/rand.rs
@@ -52,12 +52,11 @@
fn fill_with_entropy(s: &mut [u8]) -> Result<()> {
const MAX_BYTES_PER_CALL: usize = size_of::<hvc::TrngRng64Entropy>();
- let bits = usize::try_from(u8::BITS).unwrap();
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) = hvc::trng_rnd64((chunk.len() * bits).try_into().unwrap())?;
+ 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());
@@ -67,7 +66,7 @@
if !remainder.is_empty() {
let mut entropy = [0; MAX_BYTES_PER_CALL];
- let (r, s, t) = hvc::trng_rnd64((remainder.len() * bits).try_into().unwrap())?;
+ 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());
@@ -80,6 +79,17 @@
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,
+ }
+ }
+}
+
pub fn random_array<const N: usize>() -> Result<[u8; N]> {
let mut arr = [0; N];
fill_with_entropy(&mut arr)?;