1f3c22059SEtienne Carriere // SPDX-License-Identifier: BSD-3-Clause 2f3c22059SEtienne Carriere /* 3f3c22059SEtienne Carriere * Copyright (c) 2018-2019, STMicroelectronics 4f3c22059SEtienne Carriere */ 5f3c22059SEtienne Carriere 6f3c22059SEtienne Carriere #include <assert.h> 7d7a1a7d2SEtienne Carriere #include <drivers/clk.h> 8d7a1a7d2SEtienne Carriere #include <drivers/clk_dt.h> 9f3c22059SEtienne Carriere #include <drivers/stm32_rng.h> 10f3c22059SEtienne Carriere #include <io.h> 11f3c22059SEtienne Carriere #include <kernel/delay.h> 12a2fc83d1SJerome Forissier #include <kernel/dt.h> 1365401337SJens Wiklander #include <kernel/boot.h> 14f3c22059SEtienne Carriere #include <kernel/panic.h> 1565b5ada4SMarouene Boubakri #include <kernel/thread.h> 16a2fc83d1SJerome Forissier #include <libfdt.h> 17f3c22059SEtienne Carriere #include <mm/core_memprot.h> 18*097f329aSEtienne Carriere #include <rng_support.h> 19f3c22059SEtienne Carriere #include <stdbool.h> 20f3c22059SEtienne Carriere #include <stm32_util.h> 21f3c22059SEtienne Carriere #include <string.h> 22f3c22059SEtienne Carriere 23f3c22059SEtienne Carriere #define DT_RNG_COMPAT "st,stm32-rng" 24f3c22059SEtienne Carriere #define RNG_CR 0x00U 25f3c22059SEtienne Carriere #define RNG_SR 0x04U 26f3c22059SEtienne Carriere #define RNG_DR 0x08U 27f3c22059SEtienne Carriere 28f3c22059SEtienne Carriere #define RNG_CR_RNGEN BIT(2) 29f3c22059SEtienne Carriere #define RNG_CR_IE BIT(3) 30f3c22059SEtienne Carriere #define RNG_CR_CED BIT(5) 31f3c22059SEtienne Carriere 32f3c22059SEtienne Carriere #define RNG_SR_DRDY BIT(0) 33f3c22059SEtienne Carriere #define RNG_SR_CECS BIT(1) 34f3c22059SEtienne Carriere #define RNG_SR_SECS BIT(2) 35f3c22059SEtienne Carriere #define RNG_SR_CEIS BIT(5) 36f3c22059SEtienne Carriere #define RNG_SR_SEIS BIT(6) 37f3c22059SEtienne Carriere 38c99311c8SEtienne Carriere #define RNG_TIMEOUT_US U(100000) 39f3c22059SEtienne Carriere 40f3c22059SEtienne Carriere struct stm32_rng_instance { 41f3c22059SEtienne Carriere struct io_pa_va base; 42d7a1a7d2SEtienne Carriere struct clk *clock; 43f3c22059SEtienne Carriere unsigned int lock; 44f3c22059SEtienne Carriere unsigned int refcount; 45d8682c4cSEtienne Carriere bool release_post_boot; 46f3c22059SEtienne Carriere }; 47f3c22059SEtienne Carriere 48f3c22059SEtienne Carriere static struct stm32_rng_instance *stm32_rng; 49f3c22059SEtienne Carriere 50f3c22059SEtienne Carriere /* 51f3c22059SEtienne Carriere * Extracts from the STM32 RNG specification: 52f3c22059SEtienne Carriere * 53f3c22059SEtienne Carriere * When a noise source (or seed) error occurs, the RNG stops generating 54f3c22059SEtienne Carriere * random numbers and sets to “1” both SEIS and SECS bits to indicate 55f3c22059SEtienne Carriere * that a seed error occurred. (...) 56f3c22059SEtienne Carriere 57f3c22059SEtienne Carriere * The following sequence shall be used to fully recover from a seed 58f3c22059SEtienne Carriere * error after the RNG initialization: 59f3c22059SEtienne Carriere * 1. Clear the SEIS bit by writing it to “0”. 60f3c22059SEtienne Carriere * 2. Read out 12 words from the RNG_DR register, and discard each of 61f3c22059SEtienne Carriere * them in order to clean the pipeline. 62f3c22059SEtienne Carriere * 3. Confirm that SEIS is still cleared. Random number generation is 63f3c22059SEtienne Carriere * back to normal. 64f3c22059SEtienne Carriere */ 65f3c22059SEtienne Carriere static void conceal_seed_error(vaddr_t rng_base) 66f3c22059SEtienne Carriere { 67f3c22059SEtienne Carriere if (io_read32(rng_base + RNG_SR) & (RNG_SR_SECS | RNG_SR_SEIS)) { 68f3c22059SEtienne Carriere size_t i = 0; 69f3c22059SEtienne Carriere 70f3c22059SEtienne Carriere io_mask32(rng_base + RNG_SR, 0, RNG_SR_SEIS); 71f3c22059SEtienne Carriere 72f3c22059SEtienne Carriere for (i = 12; i != 0; i--) 73f3c22059SEtienne Carriere (void)io_read32(rng_base + RNG_DR); 74f3c22059SEtienne Carriere 75f3c22059SEtienne Carriere if (io_read32(rng_base + RNG_SR) & RNG_SR_SEIS) 76f3c22059SEtienne Carriere panic("RNG noise"); 77f3c22059SEtienne Carriere } 78f3c22059SEtienne Carriere } 79f3c22059SEtienne Carriere 80f3c22059SEtienne Carriere #define RNG_FIFO_BYTE_DEPTH 16u 81f3c22059SEtienne Carriere 82c99311c8SEtienne Carriere static TEE_Result read_available(vaddr_t rng_base, uint8_t *out, size_t *size) 83f3c22059SEtienne Carriere { 84c99311c8SEtienne Carriere uint8_t *buf = NULL; 85c99311c8SEtienne Carriere size_t req_size = 0; 86c99311c8SEtienne Carriere size_t len = 0; 87f3c22059SEtienne Carriere 88f3c22059SEtienne Carriere conceal_seed_error(rng_base); 89f3c22059SEtienne Carriere 9023123473SEtienne Carriere if (!(io_read32(rng_base + RNG_SR) & RNG_SR_DRDY)) { 9123123473SEtienne Carriere FMSG("RNG not ready"); 92c99311c8SEtienne Carriere return TEE_ERROR_NO_DATA; 9323123473SEtienne Carriere } 94f3c22059SEtienne Carriere 9523123473SEtienne Carriere if (io_read32(rng_base + RNG_SR) & RNG_SR_SEIS) { 9623123473SEtienne Carriere FMSG("RNG noise error"); 97c99311c8SEtienne Carriere return TEE_ERROR_NO_DATA; 9823123473SEtienne Carriere } 99c99311c8SEtienne Carriere 100c99311c8SEtienne Carriere buf = out; 101c99311c8SEtienne Carriere req_size = MIN(RNG_FIFO_BYTE_DEPTH, *size); 102c99311c8SEtienne Carriere len = req_size; 103f3c22059SEtienne Carriere 104f3c22059SEtienne Carriere /* RNG is ready: read up to 4 32bit words */ 105f3c22059SEtienne Carriere while (len) { 106f3c22059SEtienne Carriere uint32_t data32 = io_read32(rng_base + RNG_DR); 107f3c22059SEtienne Carriere size_t sz = MIN(len, sizeof(uint32_t)); 108f3c22059SEtienne Carriere 109f3c22059SEtienne Carriere memcpy(buf, &data32, sz); 110f3c22059SEtienne Carriere buf += sz; 111f3c22059SEtienne Carriere len -= sz; 112f3c22059SEtienne Carriere } 113c99311c8SEtienne Carriere 114f3c22059SEtienne Carriere *size = req_size; 115f3c22059SEtienne Carriere 116c99311c8SEtienne Carriere return TEE_SUCCESS; 117f3c22059SEtienne Carriere } 118f3c22059SEtienne Carriere 119f3c22059SEtienne Carriere static void gate_rng(bool enable, struct stm32_rng_instance *dev) 120f3c22059SEtienne Carriere { 121c2e4eb43SAnton Rybakov vaddr_t rng_cr = io_pa_or_va(&dev->base, 1) + RNG_CR; 122f3c22059SEtienne Carriere uint32_t exceptions = may_spin_lock(&dev->lock); 123f3c22059SEtienne Carriere 124f3c22059SEtienne Carriere if (enable) { 125f3c22059SEtienne Carriere /* incr_refcnt return non zero if resource shall be enabled */ 126f3c22059SEtienne Carriere if (incr_refcnt(&dev->refcount)) { 12723123473SEtienne Carriere FMSG("enable RNG"); 128d7a1a7d2SEtienne Carriere clk_enable(dev->clock); 129f3c22059SEtienne Carriere io_write32(rng_cr, 0); 130f3c22059SEtienne Carriere io_write32(rng_cr, RNG_CR_RNGEN | RNG_CR_CED); 131f3c22059SEtienne Carriere } 132f3c22059SEtienne Carriere } else { 133f3c22059SEtienne Carriere /* decr_refcnt return non zero if resource shall be disabled */ 134f3c22059SEtienne Carriere if (decr_refcnt(&dev->refcount)) { 13523123473SEtienne Carriere FMSG("disable RNG"); 136f3c22059SEtienne Carriere io_write32(rng_cr, 0); 137d7a1a7d2SEtienne Carriere clk_disable(dev->clock); 138f3c22059SEtienne Carriere } 139f3c22059SEtienne Carriere } 140f3c22059SEtienne Carriere 141f3c22059SEtienne Carriere may_spin_unlock(&dev->lock, exceptions); 142f3c22059SEtienne Carriere } 143f3c22059SEtienne Carriere 144f3c22059SEtienne Carriere TEE_Result stm32_rng_read(uint8_t *out, size_t size) 145f3c22059SEtienne Carriere { 146c99311c8SEtienne Carriere TEE_Result rc = TEE_ERROR_GENERIC; 147c99311c8SEtienne Carriere bool burst_timeout = false; 148c99311c8SEtienne Carriere uint64_t timeout_ref = 0; 149f3c22059SEtienne Carriere uint32_t exceptions = 0; 150f3c22059SEtienne Carriere uint8_t *out_ptr = out; 151c99311c8SEtienne Carriere vaddr_t rng_base = 0; 152f3c22059SEtienne Carriere size_t out_size = 0; 153f3c22059SEtienne Carriere 154f3c22059SEtienne Carriere if (!stm32_rng) { 155f3c22059SEtienne Carriere DMSG("No RNG"); 156f3c22059SEtienne Carriere return TEE_ERROR_NOT_SUPPORTED; 157f3c22059SEtienne Carriere } 158f3c22059SEtienne Carriere 159f3c22059SEtienne Carriere gate_rng(true, stm32_rng); 160c99311c8SEtienne Carriere rng_base = io_pa_or_va(&stm32_rng->base, 1); 161c99311c8SEtienne Carriere 162c99311c8SEtienne Carriere /* Arm timeout */ 163c99311c8SEtienne Carriere timeout_ref = timeout_init_us(RNG_TIMEOUT_US); 164c99311c8SEtienne Carriere burst_timeout = false; 165f3c22059SEtienne Carriere 166f3c22059SEtienne Carriere while (out_size < size) { 167f3c22059SEtienne Carriere /* Read by chunks of the size the RNG FIFO depth */ 168f3c22059SEtienne Carriere size_t sz = size - out_size; 169f3c22059SEtienne Carriere 170f3c22059SEtienne Carriere exceptions = may_spin_lock(&stm32_rng->lock); 171f3c22059SEtienne Carriere 172c99311c8SEtienne Carriere rc = read_available(rng_base, out_ptr, &sz); 173c99311c8SEtienne Carriere 174c99311c8SEtienne Carriere /* Raise timeout only if we failed to get some samples */ 175c99311c8SEtienne Carriere assert(!rc || rc == TEE_ERROR_NO_DATA); 176c99311c8SEtienne Carriere if (rc) 177c99311c8SEtienne Carriere burst_timeout = timeout_elapsed(timeout_ref); 178f3c22059SEtienne Carriere 179f3c22059SEtienne Carriere may_spin_unlock(&stm32_rng->lock, exceptions); 180f3c22059SEtienne Carriere 181c99311c8SEtienne Carriere if (burst_timeout) { 182c99311c8SEtienne Carriere rc = TEE_ERROR_GENERIC; 183c99311c8SEtienne Carriere goto out; 184f3c22059SEtienne Carriere } 185f3c22059SEtienne Carriere 186c99311c8SEtienne Carriere if (!rc) { 187c99311c8SEtienne Carriere out_size += sz; 188c99311c8SEtienne Carriere out_ptr += sz; 189c99311c8SEtienne Carriere /* Re-arm timeout */ 190c99311c8SEtienne Carriere timeout_ref = timeout_init_us(RNG_TIMEOUT_US); 191c99311c8SEtienne Carriere burst_timeout = false; 192c99311c8SEtienne Carriere } 193c99311c8SEtienne Carriere } 194c99311c8SEtienne Carriere 195c99311c8SEtienne Carriere out: 196c99311c8SEtienne Carriere assert(!rc || rc == TEE_ERROR_GENERIC); 197f3c22059SEtienne Carriere gate_rng(false, stm32_rng); 198f3c22059SEtienne Carriere 199f3c22059SEtienne Carriere return rc; 200f3c22059SEtienne Carriere } 201f3c22059SEtienne Carriere 202*097f329aSEtienne Carriere #ifndef CFG_WITH_SOFTWARE_PRNG 203*097f329aSEtienne Carriere TEE_Result crypto_rng_read(void *out, size_t size) 204*097f329aSEtienne Carriere { 205*097f329aSEtienne Carriere return stm32_rng_read(out, size); 206*097f329aSEtienne Carriere } 207*097f329aSEtienne Carriere 208*097f329aSEtienne Carriere uint8_t hw_get_random_byte(void) 209*097f329aSEtienne Carriere { 210*097f329aSEtienne Carriere uint8_t byte = 0; 211*097f329aSEtienne Carriere 212*097f329aSEtienne Carriere if (stm32_rng_read(&byte, sizeof(byte))) 213*097f329aSEtienne Carriere panic(); 214*097f329aSEtienne Carriere 215*097f329aSEtienne Carriere return byte; 216*097f329aSEtienne Carriere } 217*097f329aSEtienne Carriere #endif 218*097f329aSEtienne Carriere 219f3c22059SEtienne Carriere #ifdef CFG_EMBED_DTB 220f3c22059SEtienne Carriere static TEE_Result stm32_rng_init(void) 221f3c22059SEtienne Carriere { 222f3c22059SEtienne Carriere void *fdt = NULL; 223f3c22059SEtienne Carriere int node = -1; 224f3c22059SEtienne Carriere struct dt_node_info dt_info; 225d7a1a7d2SEtienne Carriere TEE_Result res = TEE_ERROR_GENERIC; 226f3c22059SEtienne Carriere 227f3c22059SEtienne Carriere memset(&dt_info, 0, sizeof(dt_info)); 228f3c22059SEtienne Carriere 229f3c22059SEtienne Carriere fdt = get_embedded_dt(); 230f3c22059SEtienne Carriere if (!fdt) 231f3c22059SEtienne Carriere panic(); 232f3c22059SEtienne Carriere 233f3c22059SEtienne Carriere while (true) { 234f3c22059SEtienne Carriere node = fdt_node_offset_by_compatible(fdt, node, DT_RNG_COMPAT); 235f3c22059SEtienne Carriere if (node < 0) 236f3c22059SEtienne Carriere break; 237f3c22059SEtienne Carriere 238f3c22059SEtienne Carriere _fdt_fill_device_info(fdt, &dt_info, node); 239f3c22059SEtienne Carriere 240f3c22059SEtienne Carriere if (!(dt_info.status & DT_STATUS_OK_SEC)) 241f3c22059SEtienne Carriere continue; 242f3c22059SEtienne Carriere 243f3c22059SEtienne Carriere if (stm32_rng) 244f3c22059SEtienne Carriere panic(); 245f3c22059SEtienne Carriere 246f3c22059SEtienne Carriere stm32_rng = calloc(1, sizeof(*stm32_rng)); 247f3c22059SEtienne Carriere if (!stm32_rng) 248f3c22059SEtienne Carriere panic(); 249f3c22059SEtienne Carriere 250f3c22059SEtienne Carriere assert(dt_info.clock != DT_INFO_INVALID_CLOCK && 251aabd492eSLionel Debieve dt_info.reg != DT_INFO_INVALID_REG && 252aabd492eSLionel Debieve dt_info.reg_size != DT_INFO_INVALID_REG_SIZE); 253f3c22059SEtienne Carriere 25428f25d8dSEtienne Carriere if (dt_info.status & DT_STATUS_OK_NSEC) { 25528f25d8dSEtienne Carriere stm32mp_register_non_secure_periph_iomem(dt_info.reg); 256d8682c4cSEtienne Carriere stm32_rng->release_post_boot = true; 25728f25d8dSEtienne Carriere } else { 25828f25d8dSEtienne Carriere stm32mp_register_secure_periph_iomem(dt_info.reg); 25928f25d8dSEtienne Carriere } 26028f25d8dSEtienne Carriere 261f3c22059SEtienne Carriere stm32_rng->base.pa = dt_info.reg; 262d8682c4cSEtienne Carriere if (!io_pa_or_va_secure(&stm32_rng->base, dt_info.reg_size)) 263d8682c4cSEtienne Carriere panic(); 26468c4a16bSEtienne Carriere 265d7a1a7d2SEtienne Carriere res = clk_dt_get_by_index(fdt, node, 0, &stm32_rng->clock); 266d7a1a7d2SEtienne Carriere if (res) 267d7a1a7d2SEtienne Carriere return res; 268d7a1a7d2SEtienne Carriere 269d7a1a7d2SEtienne Carriere assert(stm32_rng->clock); 270f3c22059SEtienne Carriere 271f3c22059SEtienne Carriere DMSG("RNG init"); 272f3c22059SEtienne Carriere } 273f3c22059SEtienne Carriere 274f3c22059SEtienne Carriere return TEE_SUCCESS; 275f3c22059SEtienne Carriere } 276f3c22059SEtienne Carriere 277d8682c4cSEtienne Carriere early_init_late(stm32_rng_init); 278d8682c4cSEtienne Carriere 279d8682c4cSEtienne Carriere static TEE_Result stm32_rng_release(void) 280d8682c4cSEtienne Carriere { 281d8682c4cSEtienne Carriere if (stm32_rng && stm32_rng->release_post_boot) { 282d8682c4cSEtienne Carriere DMSG("Release RNG driver"); 283d8682c4cSEtienne Carriere assert(!stm32_rng->refcount); 284d8682c4cSEtienne Carriere free(stm32_rng); 285d8682c4cSEtienne Carriere stm32_rng = NULL; 286d8682c4cSEtienne Carriere } 287d8682c4cSEtienne Carriere 288d8682c4cSEtienne Carriere return TEE_SUCCESS; 289d8682c4cSEtienne Carriere } 290d8682c4cSEtienne Carriere 291d8682c4cSEtienne Carriere release_init_resource(stm32_rng_release); 292f3c22059SEtienne Carriere #endif /*CFG_EMBED_DTB*/ 293