plat/arm: juno: Refactor juno_getentropy()

Currently we use the Juno's TRNG hardware entropy source to initialise
the stack canary. The current function allows to fill a buffer of any
size, but we will actually only ever request 16 bytes, as this is what
the hardware implements. Out of this, we only need at most 64 bits for
the canary.

In preparation for the introduction of the SMCCC TRNG interface, we
can simplify this Juno specific interface by making it compatible with
the generic one: We just deliver 64 bits of entropy on each call.
This reduces the complexity of the code. As the raw entropy register
readouts seem to be biased, it makes sense to do some conditioning
inside the juno_getentropy() function already.
Also initialise the TRNG hardware, if not already done.

Change-Id: I11b977ddc5417d52ac38709a9a7b61499eee481f
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
diff --git a/plat/arm/board/juno/juno_decl.h b/plat/arm/board/juno/juno_decl.h
index cd87c3b..21e56c0 100644
--- a/plat/arm/board/juno/juno_decl.h
+++ b/plat/arm/board/juno/juno_decl.h
@@ -7,6 +7,6 @@
 #ifndef JUNO_DECL_H
 #define JUNO_DECL_H
 
-int juno_getentropy(void *buf, size_t len);
+bool juno_getentropy(uint64_t *buf);
 
 #endif /* JUNO_DECL_H */
diff --git a/plat/arm/board/juno/juno_stack_protector.c b/plat/arm/board/juno/juno_stack_protector.c
index 236eb5b..8c51f57 100644
--- a/plat/arm/board/juno/juno_stack_protector.c
+++ b/plat/arm/board/juno/juno_stack_protector.c
@@ -13,20 +13,16 @@
 
 u_register_t plat_get_stack_protector_canary(void)
 {
-	u_register_t c[TRNG_NBYTES / sizeof(u_register_t)];
-	u_register_t ret = 0;
-	size_t i;
+	uint64_t entropy;
 
-	if (juno_getentropy(c, sizeof(c)) != 0) {
+	if (!juno_getentropy(&entropy)) {
 		ERROR("Not enough entropy to initialize canary value\n");
 		panic();
 	}
 
-	/*
-	 * On Juno we get 128-bits of entropy in one round.
-	 * Fuse the values together to form the canary.
-	 */
-	for (i = 0; i < ARRAY_SIZE(c); i++)
-		ret ^= c[i];
-	return ret;
+	if (sizeof(entropy) == sizeof(u_register_t)) {
+		return entropy;
+	}
+
+	return (entropy & 0xffffffffULL) ^ (entropy >> 32);
 }
diff --git a/plat/arm/board/juno/juno_trng.c b/plat/arm/board/juno/juno_trng.c
index 7869d3e..b38e49f 100644
--- a/plat/arm/board/juno/juno_trng.c
+++ b/plat/arm/board/juno/juno_trng.c
@@ -5,6 +5,8 @@
  */
 
 #include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
 #include <string.h>
 
 #include <lib/mmio.h>
@@ -16,7 +18,10 @@
 #define NSAMPLE_CLOCKS	1 /* min 1 cycle, max 231 cycles */
 #define NRETRIES	5
 
-static inline int output_valid(void)
+/* initialised to false */
+static bool juno_trng_initialized;
+
+static bool output_valid(void)
 {
 	int i;
 
@@ -25,59 +30,58 @@
 
 		val = mmio_read_32(TRNG_BASE + TRNG_STATUS);
 		if (val & 1U)
-			break;
+			return true;
 	}
-	if (i >= NRETRIES)
-		return 0; /* No output data available. */
-	return 1;
+	return false; /* No output data available. */
 }
 
 /*
- * This function fills `buf` with `len` bytes of entropy.
+ * This function fills `buf` with 8 bytes of entropy.
  * It uses the Trusted Entropy Source peripheral on Juno.
- * Returns 0 when the buffer has been filled with entropy
- * successfully and -1 otherwise.
+ * Returns 'true' when the buffer has been filled with entropy
+ * successfully, or 'false' otherwise.
  */
-int juno_getentropy(void *buf, size_t len)
+bool juno_getentropy(uint64_t *buf)
 {
-	uint8_t *bp = buf;
+	uint64_t ret;
 
 	assert(buf);
-	assert(len);
-	assert(!check_uptr_overflow((uintptr_t)bp, len));
+	assert(!check_uptr_overflow((uintptr_t)buf, sizeof(*buf)));
 
-	/* Disable interrupt mode. */
-	mmio_write_32(TRNG_BASE + TRNG_INTMASK, 0);
-	/* Program TRNG to sample for `NSAMPLE_CLOCKS`. */
-	mmio_write_32(TRNG_BASE + TRNG_CONFIG, NSAMPLE_CLOCKS);
+	if (!juno_trng_initialized) {
+		/* Disable interrupt mode. */
+		mmio_write_32(TRNG_BASE + TRNG_INTMASK, 0);
+		/* Program TRNG to sample for `NSAMPLE_CLOCKS`. */
+		mmio_write_32(TRNG_BASE + TRNG_CONFIG, NSAMPLE_CLOCKS);
+		/* Abort any potentially pending sampling. */
+		mmio_write_32(TRNG_BASE + TRNG_CONTROL, 2);
+		/* Reset TRNG outputs. */
+		mmio_write_32(TRNG_BASE + TRNG_STATUS, 1);
 
-	while (len > 0) {
-		int i;
+		juno_trng_initialized = true;
+	}
 
+	if (!output_valid()) {
 		/* Start TRNG. */
 		mmio_write_32(TRNG_BASE + TRNG_CONTROL, 1);
 
-		/* Check if output is valid. */
 		if (!output_valid())
-			return -1;
+			return false;
+	}
 
-		/* Fill entropy buffer. */
-		for (i = 0; i < TRNG_NOUTPUTS; i++) {
-			size_t n;
-			uint32_t val;
+	/* XOR each two 32-bit registers together, combine the pairs */
+	ret = mmio_read_32(TRNG_BASE + 0);
+	ret ^= mmio_read_32(TRNG_BASE + 4);
+	ret <<= 32;
 
-			val = mmio_read_32(TRNG_BASE + i * sizeof(uint32_t));
-			n = MIN(len, sizeof(uint32_t));
-			memcpy(bp, &val, n);
-			bp += n;
-			len -= n;
-			if (len == 0)
-				break;
-		}
+	ret |= mmio_read_32(TRNG_BASE + 8);
+	ret ^= mmio_read_32(TRNG_BASE + 12);
+	*buf = ret;
 
-		/* Reset TRNG outputs. */
-		mmio_write_32(TRNG_BASE + TRNG_STATUS, 1);
-	}
+	/* Acknowledge current cycle, clear output registers. */
+	mmio_write_32(TRNG_BASE + TRNG_STATUS, 1);
+	/* Trigger next TRNG cycle. */
+	mmio_write_32(TRNG_BASE + TRNG_CONTROL, 1);
 
-	return 0;
+	return true;
 }