1*f3c22059SEtienne Carriere // SPDX-License-Identifier: BSD-3-Clause 2*f3c22059SEtienne Carriere /* 3*f3c22059SEtienne Carriere * Copyright (c) 2018-2019, STMicroelectronics 4*f3c22059SEtienne Carriere */ 5*f3c22059SEtienne Carriere 6*f3c22059SEtienne Carriere #include <assert.h> 7*f3c22059SEtienne Carriere #include <drivers/stm32_rng.h> 8*f3c22059SEtienne Carriere #include <io.h> 9*f3c22059SEtienne Carriere #include <kernel/dt.h> 10*f3c22059SEtienne Carriere #include <kernel/delay.h> 11*f3c22059SEtienne Carriere #include <kernel/generic_boot.h> 12*f3c22059SEtienne Carriere #include <kernel/panic.h> 13*f3c22059SEtienne Carriere #include <mm/core_memprot.h> 14*f3c22059SEtienne Carriere #include <stdbool.h> 15*f3c22059SEtienne Carriere #include <stm32_util.h> 16*f3c22059SEtienne Carriere #include <string.h> 17*f3c22059SEtienne Carriere 18*f3c22059SEtienne Carriere #ifdef CFG_DT 19*f3c22059SEtienne Carriere #include <libfdt.h> 20*f3c22059SEtienne Carriere #endif 21*f3c22059SEtienne Carriere 22*f3c22059SEtienne Carriere #define DT_RNG_COMPAT "st,stm32-rng" 23*f3c22059SEtienne Carriere #define RNG_CR 0x00U 24*f3c22059SEtienne Carriere #define RNG_SR 0x04U 25*f3c22059SEtienne Carriere #define RNG_DR 0x08U 26*f3c22059SEtienne Carriere 27*f3c22059SEtienne Carriere #define RNG_CR_RNGEN BIT(2) 28*f3c22059SEtienne Carriere #define RNG_CR_IE BIT(3) 29*f3c22059SEtienne Carriere #define RNG_CR_CED BIT(5) 30*f3c22059SEtienne Carriere 31*f3c22059SEtienne Carriere #define RNG_SR_DRDY BIT(0) 32*f3c22059SEtienne Carriere #define RNG_SR_CECS BIT(1) 33*f3c22059SEtienne Carriere #define RNG_SR_SECS BIT(2) 34*f3c22059SEtienne Carriere #define RNG_SR_CEIS BIT(5) 35*f3c22059SEtienne Carriere #define RNG_SR_SEIS BIT(6) 36*f3c22059SEtienne Carriere 37*f3c22059SEtienne Carriere #define RNG_TIMEOUT_US 1000 38*f3c22059SEtienne Carriere 39*f3c22059SEtienne Carriere struct stm32_rng_instance { 40*f3c22059SEtienne Carriere struct io_pa_va base; 41*f3c22059SEtienne Carriere unsigned long clock; 42*f3c22059SEtienne Carriere unsigned int lock; 43*f3c22059SEtienne Carriere unsigned int refcount; 44*f3c22059SEtienne Carriere }; 45*f3c22059SEtienne Carriere 46*f3c22059SEtienne Carriere static struct stm32_rng_instance *stm32_rng; 47*f3c22059SEtienne Carriere 48*f3c22059SEtienne Carriere /* 49*f3c22059SEtienne Carriere * Extracts from the STM32 RNG specification: 50*f3c22059SEtienne Carriere * 51*f3c22059SEtienne Carriere * When a noise source (or seed) error occurs, the RNG stops generating 52*f3c22059SEtienne Carriere * random numbers and sets to “1” both SEIS and SECS bits to indicate 53*f3c22059SEtienne Carriere * that a seed error occurred. (...) 54*f3c22059SEtienne Carriere 55*f3c22059SEtienne Carriere * The following sequence shall be used to fully recover from a seed 56*f3c22059SEtienne Carriere * error after the RNG initialization: 57*f3c22059SEtienne Carriere * 1. Clear the SEIS bit by writing it to “0”. 58*f3c22059SEtienne Carriere * 2. Read out 12 words from the RNG_DR register, and discard each of 59*f3c22059SEtienne Carriere * them in order to clean the pipeline. 60*f3c22059SEtienne Carriere * 3. Confirm that SEIS is still cleared. Random number generation is 61*f3c22059SEtienne Carriere * back to normal. 62*f3c22059SEtienne Carriere */ 63*f3c22059SEtienne Carriere static void conceal_seed_error(vaddr_t rng_base) 64*f3c22059SEtienne Carriere { 65*f3c22059SEtienne Carriere if (io_read32(rng_base + RNG_SR) & (RNG_SR_SECS | RNG_SR_SEIS)) { 66*f3c22059SEtienne Carriere size_t i = 0; 67*f3c22059SEtienne Carriere 68*f3c22059SEtienne Carriere io_mask32(rng_base + RNG_SR, 0, RNG_SR_SEIS); 69*f3c22059SEtienne Carriere 70*f3c22059SEtienne Carriere for (i = 12; i != 0; i--) 71*f3c22059SEtienne Carriere (void)io_read32(rng_base + RNG_DR); 72*f3c22059SEtienne Carriere 73*f3c22059SEtienne Carriere if (io_read32(rng_base + RNG_SR) & RNG_SR_SEIS) 74*f3c22059SEtienne Carriere panic("RNG noise"); 75*f3c22059SEtienne Carriere } 76*f3c22059SEtienne Carriere } 77*f3c22059SEtienne Carriere 78*f3c22059SEtienne Carriere #define RNG_FIFO_BYTE_DEPTH 16u 79*f3c22059SEtienne Carriere 80*f3c22059SEtienne Carriere TEE_Result stm32_rng_read_raw(vaddr_t rng_base, uint8_t *out, size_t *size) 81*f3c22059SEtienne Carriere { 82*f3c22059SEtienne Carriere bool enabled = false; 83*f3c22059SEtienne Carriere TEE_Result rc = TEE_ERROR_SECURITY; 84*f3c22059SEtienne Carriere uint32_t exceptions = thread_mask_exceptions(THREAD_EXCP_ALL); 85*f3c22059SEtienne Carriere uint64_t timeout_ref = timeout_init_us(RNG_TIMEOUT_US); 86*f3c22059SEtienne Carriere 87*f3c22059SEtienne Carriere if (!(io_read32(rng_base + RNG_CR) & RNG_CR_RNGEN)) { 88*f3c22059SEtienne Carriere /* Enable RNG if not, clock error is disabled */ 89*f3c22059SEtienne Carriere io_write32(rng_base + RNG_CR, RNG_CR_RNGEN | RNG_CR_CED); 90*f3c22059SEtienne Carriere enabled = true; 91*f3c22059SEtienne Carriere } 92*f3c22059SEtienne Carriere 93*f3c22059SEtienne Carriere /* Wait RNG has produced well seeded random samples */ 94*f3c22059SEtienne Carriere while (!timeout_elapsed(timeout_ref)) { 95*f3c22059SEtienne Carriere conceal_seed_error(rng_base); 96*f3c22059SEtienne Carriere 97*f3c22059SEtienne Carriere if (io_read32(rng_base + RNG_SR) & RNG_SR_DRDY) 98*f3c22059SEtienne Carriere break; 99*f3c22059SEtienne Carriere } 100*f3c22059SEtienne Carriere 101*f3c22059SEtienne Carriere if (io_read32(rng_base + RNG_SR) & RNG_SR_DRDY) { 102*f3c22059SEtienne Carriere uint8_t *buf = out; 103*f3c22059SEtienne Carriere size_t req_size = MIN(RNG_FIFO_BYTE_DEPTH, *size); 104*f3c22059SEtienne Carriere size_t len = req_size; 105*f3c22059SEtienne Carriere 106*f3c22059SEtienne Carriere /* RNG is ready: read up to 4 32bit words */ 107*f3c22059SEtienne Carriere while (len) { 108*f3c22059SEtienne Carriere uint32_t data32 = io_read32(rng_base + RNG_DR); 109*f3c22059SEtienne Carriere size_t sz = MIN(len, sizeof(uint32_t)); 110*f3c22059SEtienne Carriere 111*f3c22059SEtienne Carriere memcpy(buf, &data32, sz); 112*f3c22059SEtienne Carriere buf += sz; 113*f3c22059SEtienne Carriere len -= sz; 114*f3c22059SEtienne Carriere } 115*f3c22059SEtienne Carriere rc = TEE_SUCCESS; 116*f3c22059SEtienne Carriere *size = req_size; 117*f3c22059SEtienne Carriere } 118*f3c22059SEtienne Carriere 119*f3c22059SEtienne Carriere if (enabled) 120*f3c22059SEtienne Carriere io_write32(rng_base + RNG_CR, 0); 121*f3c22059SEtienne Carriere 122*f3c22059SEtienne Carriere thread_unmask_exceptions(exceptions); 123*f3c22059SEtienne Carriere 124*f3c22059SEtienne Carriere return rc; 125*f3c22059SEtienne Carriere } 126*f3c22059SEtienne Carriere 127*f3c22059SEtienne Carriere static void gate_rng(bool enable, struct stm32_rng_instance *dev) 128*f3c22059SEtienne Carriere { 129*f3c22059SEtienne Carriere vaddr_t rng_cr = io_pa_or_va(&dev->base) + RNG_CR; 130*f3c22059SEtienne Carriere uint32_t exceptions = may_spin_lock(&dev->lock); 131*f3c22059SEtienne Carriere 132*f3c22059SEtienne Carriere if (enable) { 133*f3c22059SEtienne Carriere /* incr_refcnt return non zero if resource shall be enabled */ 134*f3c22059SEtienne Carriere if (incr_refcnt(&dev->refcount)) { 135*f3c22059SEtienne Carriere stm32_clock_enable(dev->clock); 136*f3c22059SEtienne Carriere io_write32(rng_cr, 0); 137*f3c22059SEtienne Carriere io_write32(rng_cr, RNG_CR_RNGEN | RNG_CR_CED); 138*f3c22059SEtienne Carriere } 139*f3c22059SEtienne Carriere } else { 140*f3c22059SEtienne Carriere /* decr_refcnt return non zero if resource shall be disabled */ 141*f3c22059SEtienne Carriere if (decr_refcnt(&dev->refcount)) { 142*f3c22059SEtienne Carriere io_write32(rng_cr, 0); 143*f3c22059SEtienne Carriere stm32_clock_disable(dev->clock); 144*f3c22059SEtienne Carriere } 145*f3c22059SEtienne Carriere } 146*f3c22059SEtienne Carriere 147*f3c22059SEtienne Carriere may_spin_unlock(&dev->lock, exceptions); 148*f3c22059SEtienne Carriere } 149*f3c22059SEtienne Carriere 150*f3c22059SEtienne Carriere TEE_Result stm32_rng_read(uint8_t *out, size_t size) 151*f3c22059SEtienne Carriere { 152*f3c22059SEtienne Carriere TEE_Result rc = 0; 153*f3c22059SEtienne Carriere uint32_t exceptions = 0; 154*f3c22059SEtienne Carriere vaddr_t rng_base = io_pa_or_va(&stm32_rng->base); 155*f3c22059SEtienne Carriere uint8_t *out_ptr = out; 156*f3c22059SEtienne Carriere size_t out_size = 0; 157*f3c22059SEtienne Carriere 158*f3c22059SEtienne Carriere if (!stm32_rng) { 159*f3c22059SEtienne Carriere DMSG("No RNG"); 160*f3c22059SEtienne Carriere return TEE_ERROR_NOT_SUPPORTED; 161*f3c22059SEtienne Carriere } 162*f3c22059SEtienne Carriere 163*f3c22059SEtienne Carriere gate_rng(true, stm32_rng); 164*f3c22059SEtienne Carriere 165*f3c22059SEtienne Carriere while (out_size < size) { 166*f3c22059SEtienne Carriere /* Read by chunks of the size the RNG FIFO depth */ 167*f3c22059SEtienne Carriere size_t sz = size - out_size; 168*f3c22059SEtienne Carriere 169*f3c22059SEtienne Carriere exceptions = may_spin_lock(&stm32_rng->lock); 170*f3c22059SEtienne Carriere 171*f3c22059SEtienne Carriere rc = stm32_rng_read_raw(rng_base, out_ptr, &sz); 172*f3c22059SEtienne Carriere 173*f3c22059SEtienne Carriere may_spin_unlock(&stm32_rng->lock, exceptions); 174*f3c22059SEtienne Carriere 175*f3c22059SEtienne Carriere if (rc) 176*f3c22059SEtienne Carriere goto bail; 177*f3c22059SEtienne Carriere 178*f3c22059SEtienne Carriere out_size += sz; 179*f3c22059SEtienne Carriere out_ptr += sz; 180*f3c22059SEtienne Carriere } 181*f3c22059SEtienne Carriere 182*f3c22059SEtienne Carriere bail: 183*f3c22059SEtienne Carriere gate_rng(false, stm32_rng); 184*f3c22059SEtienne Carriere if (rc) 185*f3c22059SEtienne Carriere memset(out, 0, size); 186*f3c22059SEtienne Carriere 187*f3c22059SEtienne Carriere return rc; 188*f3c22059SEtienne Carriere } 189*f3c22059SEtienne Carriere 190*f3c22059SEtienne Carriere #ifdef CFG_EMBED_DTB 191*f3c22059SEtienne Carriere static TEE_Result stm32_rng_init(void) 192*f3c22059SEtienne Carriere { 193*f3c22059SEtienne Carriere void *fdt = NULL; 194*f3c22059SEtienne Carriere int node = -1; 195*f3c22059SEtienne Carriere struct dt_node_info dt_info; 196*f3c22059SEtienne Carriere 197*f3c22059SEtienne Carriere memset(&dt_info, 0, sizeof(dt_info)); 198*f3c22059SEtienne Carriere 199*f3c22059SEtienne Carriere fdt = get_embedded_dt(); 200*f3c22059SEtienne Carriere if (!fdt) 201*f3c22059SEtienne Carriere panic(); 202*f3c22059SEtienne Carriere 203*f3c22059SEtienne Carriere while (true) { 204*f3c22059SEtienne Carriere node = fdt_node_offset_by_compatible(fdt, node, DT_RNG_COMPAT); 205*f3c22059SEtienne Carriere if (node < 0) 206*f3c22059SEtienne Carriere break; 207*f3c22059SEtienne Carriere 208*f3c22059SEtienne Carriere _fdt_fill_device_info(fdt, &dt_info, node); 209*f3c22059SEtienne Carriere 210*f3c22059SEtienne Carriere if (!(dt_info.status & DT_STATUS_OK_SEC)) 211*f3c22059SEtienne Carriere continue; 212*f3c22059SEtienne Carriere 213*f3c22059SEtienne Carriere if (stm32_rng) 214*f3c22059SEtienne Carriere panic(); 215*f3c22059SEtienne Carriere 216*f3c22059SEtienne Carriere stm32_rng = calloc(1, sizeof(*stm32_rng)); 217*f3c22059SEtienne Carriere if (!stm32_rng) 218*f3c22059SEtienne Carriere panic(); 219*f3c22059SEtienne Carriere 220*f3c22059SEtienne Carriere assert(dt_info.clock != DT_INFO_INVALID_CLOCK && 221*f3c22059SEtienne Carriere dt_info.reg != DT_INFO_INVALID_REG); 222*f3c22059SEtienne Carriere 223*f3c22059SEtienne Carriere stm32_rng->base.pa = dt_info.reg; 224*f3c22059SEtienne Carriere stm32_rng->clock = (unsigned long)dt_info.clock; 225*f3c22059SEtienne Carriere 226*f3c22059SEtienne Carriere DMSG("RNG init"); 227*f3c22059SEtienne Carriere } 228*f3c22059SEtienne Carriere 229*f3c22059SEtienne Carriere return TEE_SUCCESS; 230*f3c22059SEtienne Carriere } 231*f3c22059SEtienne Carriere 232*f3c22059SEtienne Carriere driver_init(stm32_rng_init); 233*f3c22059SEtienne Carriere #endif /*CFG_EMBED_DTB*/ 234