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>
This commit is contained in:
Andre Przywara 2020-10-08 00:43:50 +01:00
parent 0e14948e2a
commit 543f0d8b08
3 changed files with 50 additions and 50 deletions

View File

@ -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 */

View File

@ -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);
}

View File

@ -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 @@ static inline int output_valid(void)
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;
/* Fill entropy buffer. */
for (i = 0; i < TRNG_NOUTPUTS; i++) {
size_t n;
uint32_t val;
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;
}
/* Reset TRNG outputs. */
mmio_write_32(TRNG_BASE + TRNG_STATUS, 1);
return false;
}
return 0;
/* 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;
ret |= mmio_read_32(TRNG_BASE + 8);
ret ^= mmio_read_32(TRNG_BASE + 12);
*buf = ret;
/* 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 true;
}