Commit 543f0d8b authored by Andre Przywara's avatar Andre Przywara
Browse files

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: default avatarAndre Przywara <andre.przywara@arm.com>
parent 0e14948e
...@@ -7,6 +7,6 @@ ...@@ -7,6 +7,6 @@
#ifndef JUNO_DECL_H #ifndef JUNO_DECL_H
#define 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 */ #endif /* JUNO_DECL_H */
...@@ -13,20 +13,16 @@ ...@@ -13,20 +13,16 @@
u_register_t plat_get_stack_protector_canary(void) u_register_t plat_get_stack_protector_canary(void)
{ {
u_register_t c[TRNG_NBYTES / sizeof(u_register_t)]; uint64_t entropy;
u_register_t ret = 0;
size_t i;
if (juno_getentropy(c, sizeof(c)) != 0) { if (!juno_getentropy(&entropy)) {
ERROR("Not enough entropy to initialize canary value\n"); ERROR("Not enough entropy to initialize canary value\n");
panic(); panic();
} }
/* if (sizeof(entropy) == sizeof(u_register_t)) {
* On Juno we get 128-bits of entropy in one round. return entropy;
* Fuse the values together to form the canary. }
*/
for (i = 0; i < ARRAY_SIZE(c); i++) return (entropy & 0xffffffffULL) ^ (entropy >> 32);
ret ^= c[i];
return ret;
} }
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
*/ */
#include <assert.h> #include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h> #include <string.h>
#include <lib/mmio.h> #include <lib/mmio.h>
...@@ -16,7 +18,10 @@ ...@@ -16,7 +18,10 @@
#define NSAMPLE_CLOCKS 1 /* min 1 cycle, max 231 cycles */ #define NSAMPLE_CLOCKS 1 /* min 1 cycle, max 231 cycles */
#define NRETRIES 5 #define NRETRIES 5
static inline int output_valid(void) /* initialised to false */
static bool juno_trng_initialized;
static bool output_valid(void)
{ {
int i; int i;
...@@ -25,59 +30,58 @@ static inline int output_valid(void) ...@@ -25,59 +30,58 @@ static inline int output_valid(void)
val = mmio_read_32(TRNG_BASE + TRNG_STATUS); val = mmio_read_32(TRNG_BASE + TRNG_STATUS);
if (val & 1U) if (val & 1U)
break; return true;
} }
if (i >= NRETRIES) return false; /* No output data available. */
return 0; /* No output data available. */
return 1;
} }
/* /*
* 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. * It uses the Trusted Entropy Source peripheral on Juno.
* Returns 0 when the buffer has been filled with entropy * Returns 'true' when the buffer has been filled with entropy
* successfully and -1 otherwise. * 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(buf);
assert(len); assert(!check_uptr_overflow((uintptr_t)buf, sizeof(*buf)));
assert(!check_uptr_overflow((uintptr_t)bp, len));
if (!juno_trng_initialized) {
/* Disable interrupt mode. */ /* Disable interrupt mode. */
mmio_write_32(TRNG_BASE + TRNG_INTMASK, 0); mmio_write_32(TRNG_BASE + TRNG_INTMASK, 0);
/* Program TRNG to sample for `NSAMPLE_CLOCKS`. */ /* Program TRNG to sample for `NSAMPLE_CLOCKS`. */
mmio_write_32(TRNG_BASE + TRNG_CONFIG, 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) { juno_trng_initialized = true;
int i; }
if (!output_valid()) {
/* Start TRNG. */ /* Start TRNG. */
mmio_write_32(TRNG_BASE + TRNG_CONTROL, 1); mmio_write_32(TRNG_BASE + TRNG_CONTROL, 1);
/* Check if output is valid. */
if (!output_valid()) if (!output_valid())
return -1; return false;
}
/* Fill entropy buffer. */ /* XOR each two 32-bit registers together, combine the pairs */
for (i = 0; i < TRNG_NOUTPUTS; i++) { ret = mmio_read_32(TRNG_BASE + 0);
size_t n; ret ^= mmio_read_32(TRNG_BASE + 4);
uint32_t val; ret <<= 32;
val = mmio_read_32(TRNG_BASE + i * sizeof(uint32_t)); ret |= mmio_read_32(TRNG_BASE + 8);
n = MIN(len, sizeof(uint32_t)); ret ^= mmio_read_32(TRNG_BASE + 12);
memcpy(bp, &val, n); *buf = ret;
bp += n;
len -= n;
if (len == 0)
break;
}
/* Reset TRNG outputs. */ /* Acknowledge current cycle, clear output registers. */
mmio_write_32(TRNG_BASE + TRNG_STATUS, 1); mmio_write_32(TRNG_BASE + TRNG_STATUS, 1);
} /* Trigger next TRNG cycle. */
mmio_write_32(TRNG_BASE + TRNG_CONTROL, 1);
return 0; return true;
} }
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment