diff options
Diffstat (limited to '')
-rw-r--r-- | plat/arm/board/juno/juno_trng.c | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/plat/arm/board/juno/juno_trng.c b/plat/arm/board/juno/juno_trng.c new file mode 100644 index 0000000..09552a6 --- /dev/null +++ b/plat/arm/board/juno/juno_trng.c @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <arm_acle.h> +#include <assert.h> +#include <stdbool.h> +#include <stdint.h> +#include <string.h> + +#include <lib/mmio.h> +#include <lib/utils_def.h> +#include <platform_def.h> + +#include <lib/smccc.h> +#include <services/trng_svc.h> +#include <smccc_helpers.h> + +#include <plat/common/platform.h> + +#define NSAMPLE_CLOCKS 1 /* min 1 cycle, max 231 cycles */ +#define NRETRIES 5 + +/* initialised to false */ +static bool juno_trng_initialized; + +static bool output_valid(void) +{ + int i; + + for (i = 0; i < NRETRIES; i++) { + uint32_t val; + + val = mmio_read_32(TRNG_BASE + TRNG_STATUS); + if (val & 1U) + return true; + } + return false; /* No output data available. */ +} + +DEFINE_SVC_UUID2(_plat_trng_uuid, + 0x23523c58, 0x7448, 0x4083, 0x9d, 0x16, + 0xe3, 0xfa, 0xb9, 0xf1, 0x73, 0xbc +); +uuid_t plat_trng_uuid; + +static uint32_t crc_value = ~0U; + +/* + * Uses the Trusted Entropy Source peripheral on Juno to return 8 bytes of + * entropy. Returns 'true' when done successfully, 'false' otherwise. + */ +bool plat_get_entropy(uint64_t *out) +{ + uint64_t ret; + + assert(out); + assert(!check_uptr_overflow((uintptr_t)out, sizeof(*out))); + + 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); + + juno_trng_initialized = true; + } + + if (!output_valid()) { + /* Start TRNG. */ + mmio_write_32(TRNG_BASE + TRNG_CONTROL, 1); + + if (!output_valid()) + return false; + } + + /* CRC each two 32-bit registers together, combine the pairs */ + crc_value = __crc32w(crc_value, mmio_read_32(TRNG_BASE + 0)); + crc_value = __crc32w(crc_value, mmio_read_32(TRNG_BASE + 4)); + ret = (uint64_t)crc_value << 32; + + crc_value = __crc32w(crc_value, mmio_read_32(TRNG_BASE + 8)); + crc_value = __crc32w(crc_value, mmio_read_32(TRNG_BASE + 12)); + *out = ret | crc_value; + + /* 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; +} + +void plat_entropy_setup(void) +{ + uint64_t dummy; + + plat_trng_uuid = _plat_trng_uuid; + + /* Initialise the entropy source and trigger RNG generation */ + plat_get_entropy(&dummy); +} |