* Copyright (c) 2016, Linaro Limited
*/
#include <initcall.h>
#include <io.h>
#include <keep.h>
#include <kernel/interrupt.h>
#include <kernel/misc.h>
#include <kernel/spinlock.h>
#include <mm/core_memprot.h>
#include <mm/core_mmu.h>
#include <platform_config.h>
#include <rng_support.h>
#define RNG_OUTPUT_L 0x0000
#define RNG_OUTPUT_H 0x0004
#define RNG_STATUS 0x0008
# define RNG_READY BIT(0)
# define SHUTDOWN_OFLO BIT(1)
#define RNG_INTMASK 0x000C
#define RNG_INTACK 0x0010
#define RNG_CONTROL 0x0014
# define ENABLE_TRNG BIT(10)
#define RNG_CONFIG 0x0018
#define RNG_ALARMCNT 0x001C
#define RNG_FROENABLE 0x0020
#define RNG_FRODETUNE 0x0024
#define RNG_ALARMMASK 0x0028
#define RNG_ALARMSTOP 0x002C
#define RNG_LFSR_L 0x0030
#define RNG_LFSR_M 0x0034
#define RNG_LFSR_H 0x0038
#define RNG_COUNT 0x003C
#define RNG_OPTIONS 0x0078
#define RNG_EIP_REV 0x007C
#define RNG_MMR_STATUS_EN 0x1FD8
#define RNG_REV 0x1FE0
#define RNG_SYS_CONFIG_REG 0x1FE4
# define RNG_AUTOIDLE BIT(0)
#define RNG_MMR_STATUS_SET 0x1FEC
#define RNG_SOFT_RESET_REG 0x1FF0
# define RNG_SOFT_RESET BIT(0)
#define RNG_IRQ_EOI_REG 0x1FF4
#define RNG_IRQSTATUS 0x1FF8
#define RNG_CONTROL_STARTUP_CYCLES_SHIFT 16
#define RNG_CONTROL_STARTUP_CYCLES_MASK GENMASK_32(31, 16)
#define RNG_CONFIG_MAX_REFIL_CYCLES_SHIFT 16
#define RNG_CONFIG_MAX_REFIL_CYCLES_MASK GENMASK_32(31, 16)
#define RNG_CONFIG_MIN_REFIL_CYCLES_SHIFT 0
#define RNG_CONFIG_MIN_REFIL_CYCLES_MASK GENMASK_32(7, 0)
#define RNG_ALARMCNT_ALARM_TH_SHIFT 0
#define RNG_ALARMCNT_ALARM_TH_MASK GENMASK_32(7, 0)
#define RNG_ALARMCNT_SHUTDOWN_TH_SHIFT 16
#define RNG_ALARMCNT_SHUTDOWN_TH_MASK GENMASK_32(20, 16)
#define RNG_CONTROL_STARTUP_CYCLES 0xff
#define RNG_CONFIG_MIN_REFIL_CYCLES 0x21
#define RNG_CONFIG_MAX_REFIL_CYCLES 0x22
#define RNG_ALARM_THRESHOLD 0xff
#define RNG_SHUTDOWN_THRESHOLD 0x4
#define RNG_FRO_MASK GENMASK_32(23, 0)
#define RNG_REG_SIZE 0x2000
register_phys_mem_pgdir(MEM_AREA_IO_SEC, RNG_BASE, RNG_REG_SIZE);
static unsigned int rng_lock = SPINLOCK_UNLOCK;
static vaddr_t rng;
static void dra7_rng_read64(uint32_t *low_word, uint32_t *high_word)
{
while (!(io_read32(rng + RNG_STATUS) & RNG_READY)) {
if (io_read32(rng + RNG_STATUS) & SHUTDOWN_OFLO) {
uint32_t alarm = io_read32(rng + RNG_ALARMSTOP);
uint32_t tune = io_read32(rng + RNG_FRODETUNE);
io_write32(rng + RNG_ALARMMASK, 0x0);
io_write32(rng + RNG_ALARMSTOP, 0x0);
io_write32(rng + RNG_FRODETUNE, tune ^ alarm);
io_write32(rng + RNG_FROENABLE, RNG_FRO_MASK);
io_write32(rng + RNG_INTACK, SHUTDOWN_OFLO);
DMSG("Fixed FRO shutdown\n");
}
}
*low_word = io_read32(rng + RNG_OUTPUT_L);
*high_word = io_read32(rng + RNG_OUTPUT_H);
io_write32(rng + RNG_INTACK, RNG_READY);
}
TEE_Result hw_get_random_bytes(void *buf, size_t len)
{
static union {
uint32_t val[2];
uint8_t byte[8];
} fifo;
static size_t fifo_pos;
uint8_t *buffer = buf;
size_t buffer_pos = 0;
while (buffer_pos < len) {
uint32_t exceptions = cpu_spin_lock_xsave(&rng_lock);
if (fifo_pos == 0)
dra7_rng_read64(&fifo.val[0], &fifo.val[1]);
buffer[buffer_pos++] = fifo.byte[fifo_pos++];
fifo_pos %= 8;
cpu_spin_unlock_xrestore(&rng_lock, exceptions);
}
return TEE_SUCCESS;
}
static TEE_Result dra7_rng_init(void)
{
uint32_t val;
rng = (vaddr_t)phys_to_virt(RNG_BASE, MEM_AREA_IO_SEC, RNG_REG_SIZE);
io_write32(rng + RNG_SOFT_RESET_REG, RNG_SOFT_RESET);
while (io_read32(rng + RNG_SOFT_RESET_REG) & RNG_SOFT_RESET)
;
io_write32(rng + RNG_SYS_CONFIG_REG, RNG_AUTOIDLE);
* Select the number of clock input cycles to the
* FROs between two samples
*/
val = 0;
val |= RNG_CONFIG_MIN_REFIL_CYCLES <<
RNG_CONFIG_MIN_REFIL_CYCLES_SHIFT;
val |= RNG_CONFIG_MAX_REFIL_CYCLES <<
RNG_CONFIG_MAX_REFIL_CYCLES_SHIFT;
io_write32(rng + RNG_CONFIG, val);
io_write32(rng + RNG_FRODETUNE, 0x0);
io_write32(rng + RNG_FROENABLE, 0xffffff);
* Select the maximum number of samples after
* which if a repeating pattern is still detected, an
* alarm event is generated
*/
val = RNG_ALARM_THRESHOLD << RNG_ALARMCNT_ALARM_TH_SHIFT;
* Set the shutdown threshold to the number of FROs
* allowed to be shut downed
*/
val |= RNG_SHUTDOWN_THRESHOLD << RNG_ALARMCNT_SHUTDOWN_TH_SHIFT;
io_write32(rng + RNG_ALARMCNT, val);
val = RNG_CONTROL_STARTUP_CYCLES << RNG_CONTROL_STARTUP_CYCLES_SHIFT;
val |= ENABLE_TRNG;
io_write32(rng + RNG_CONTROL, val);
IMSG("DRA7x TRNG initialized");
return TEE_SUCCESS;
}
driver_init(dra7_rng_init);