1f3c22059SEtienne Carriere // SPDX-License-Identifier: BSD-3-Clause 2f3c22059SEtienne Carriere /* 3ea8ba295SGatien Chevallier * Copyright (c) 2018-2023, STMicroelectronics 4f3c22059SEtienne Carriere */ 5f3c22059SEtienne Carriere 6f3c22059SEtienne Carriere #include <assert.h> 7d7a1a7d2SEtienne Carriere #include <drivers/clk.h> 8d7a1a7d2SEtienne Carriere #include <drivers/clk_dt.h> 9ea8ba295SGatien Chevallier #include <drivers/rstctrl.h> 10f3c22059SEtienne Carriere #include <drivers/stm32_rng.h> 11f3c22059SEtienne Carriere #include <io.h> 12f3c22059SEtienne Carriere #include <kernel/delay.h> 13a2fc83d1SJerome Forissier #include <kernel/dt.h> 14ea8ba295SGatien Chevallier #include <kernel/dt_driver.h> 1565401337SJens Wiklander #include <kernel/boot.h> 16f3c22059SEtienne Carriere #include <kernel/panic.h> 1765b5ada4SMarouene Boubakri #include <kernel/thread.h> 18a2fc83d1SJerome Forissier #include <libfdt.h> 19f3c22059SEtienne Carriere #include <mm/core_memprot.h> 20097f329aSEtienne Carriere #include <rng_support.h> 21f3c22059SEtienne Carriere #include <stdbool.h> 22f3c22059SEtienne Carriere #include <stm32_util.h> 23f3c22059SEtienne Carriere #include <string.h> 24cd451498SEtienne Carriere #include <tee/tee_cryp_utl.h> 25f3c22059SEtienne Carriere 26*0817aa6fSGatien Chevallier #define RNG_CR U(0x00) 27*0817aa6fSGatien Chevallier #define RNG_SR U(0x04) 28*0817aa6fSGatien Chevallier #define RNG_DR U(0x08) 29f3c22059SEtienne Carriere 30f3c22059SEtienne Carriere #define RNG_CR_RNGEN BIT(2) 31f3c22059SEtienne Carriere #define RNG_CR_IE BIT(3) 32f3c22059SEtienne Carriere #define RNG_CR_CED BIT(5) 33f3c22059SEtienne Carriere 34f3c22059SEtienne Carriere #define RNG_SR_DRDY BIT(0) 35f3c22059SEtienne Carriere #define RNG_SR_CECS BIT(1) 36f3c22059SEtienne Carriere #define RNG_SR_SECS BIT(2) 37f3c22059SEtienne Carriere #define RNG_SR_CEIS BIT(5) 38f3c22059SEtienne Carriere #define RNG_SR_SEIS BIT(6) 39f3c22059SEtienne Carriere 40*0817aa6fSGatien Chevallier #if TRACE_LEVEL > TRACE_DEBUG 41*0817aa6fSGatien Chevallier #define RNG_READY_TIMEOUT_US U(100000) 42*0817aa6fSGatien Chevallier #else 43*0817aa6fSGatien Chevallier #define RNG_READY_TIMEOUT_US U(10000) 44*0817aa6fSGatien Chevallier #endif 45ea8ba295SGatien Chevallier #define RNG_RESET_TIMEOUT_US U(1000) 46f3c22059SEtienne Carriere 47*0817aa6fSGatien Chevallier #define RNG_FIFO_BYTE_DEPTH U(16) 48*0817aa6fSGatien Chevallier 49f3c22059SEtienne Carriere struct stm32_rng_instance { 50f3c22059SEtienne Carriere struct io_pa_va base; 51d7a1a7d2SEtienne Carriere struct clk *clock; 52ea8ba295SGatien Chevallier struct rstctrl *rstctrl; 53f3c22059SEtienne Carriere unsigned int lock; 54d8682c4cSEtienne Carriere bool release_post_boot; 55f3c22059SEtienne Carriere }; 56f3c22059SEtienne Carriere 57ea8ba295SGatien Chevallier /* Expect at most a single RNG instance */ 58f3c22059SEtienne Carriere static struct stm32_rng_instance *stm32_rng; 59f3c22059SEtienne Carriere 60f63f11bdSGatien Chevallier static vaddr_t get_base(void) 61f63f11bdSGatien Chevallier { 62f63f11bdSGatien Chevallier assert(stm32_rng); 63f63f11bdSGatien Chevallier 64f63f11bdSGatien Chevallier return io_pa_or_va(&stm32_rng->base, 1); 65f63f11bdSGatien Chevallier } 66f63f11bdSGatien Chevallier 67f3c22059SEtienne Carriere /* 68f3c22059SEtienne Carriere * Extracts from the STM32 RNG specification: 69f3c22059SEtienne Carriere * 70f3c22059SEtienne Carriere * When a noise source (or seed) error occurs, the RNG stops generating 71f3c22059SEtienne Carriere * random numbers and sets to “1” both SEIS and SECS bits to indicate 72f3c22059SEtienne Carriere * that a seed error occurred. (...) 73f3c22059SEtienne Carriere 74f3c22059SEtienne Carriere * The following sequence shall be used to fully recover from a seed 75f3c22059SEtienne Carriere * error after the RNG initialization: 76f3c22059SEtienne Carriere * 1. Clear the SEIS bit by writing it to “0”. 77f3c22059SEtienne Carriere * 2. Read out 12 words from the RNG_DR register, and discard each of 78f3c22059SEtienne Carriere * them in order to clean the pipeline. 79f3c22059SEtienne Carriere * 3. Confirm that SEIS is still cleared. Random number generation is 80f3c22059SEtienne Carriere * back to normal. 81f3c22059SEtienne Carriere */ 82f3c22059SEtienne Carriere static void conceal_seed_error(vaddr_t rng_base) 83f3c22059SEtienne Carriere { 84f3c22059SEtienne Carriere if (io_read32(rng_base + RNG_SR) & (RNG_SR_SECS | RNG_SR_SEIS)) { 85f3c22059SEtienne Carriere size_t i = 0; 86f3c22059SEtienne Carriere 87f3c22059SEtienne Carriere io_mask32(rng_base + RNG_SR, 0, RNG_SR_SEIS); 88f3c22059SEtienne Carriere 89f3c22059SEtienne Carriere for (i = 12; i != 0; i--) 90f3c22059SEtienne Carriere (void)io_read32(rng_base + RNG_DR); 91f3c22059SEtienne Carriere 92f3c22059SEtienne Carriere if (io_read32(rng_base + RNG_SR) & RNG_SR_SEIS) 93f3c22059SEtienne Carriere panic("RNG noise"); 94f3c22059SEtienne Carriere } 95f3c22059SEtienne Carriere } 96f3c22059SEtienne Carriere 97c99311c8SEtienne Carriere static TEE_Result read_available(vaddr_t rng_base, uint8_t *out, size_t *size) 98f3c22059SEtienne Carriere { 99c99311c8SEtienne Carriere uint8_t *buf = NULL; 100c99311c8SEtienne Carriere size_t req_size = 0; 101c99311c8SEtienne Carriere size_t len = 0; 102f3c22059SEtienne Carriere 103f3c22059SEtienne Carriere conceal_seed_error(rng_base); 104f3c22059SEtienne Carriere 10523123473SEtienne Carriere if (!(io_read32(rng_base + RNG_SR) & RNG_SR_DRDY)) { 10623123473SEtienne Carriere FMSG("RNG not ready"); 107c99311c8SEtienne Carriere return TEE_ERROR_NO_DATA; 10823123473SEtienne Carriere } 109f3c22059SEtienne Carriere 11023123473SEtienne Carriere if (io_read32(rng_base + RNG_SR) & RNG_SR_SEIS) { 11123123473SEtienne Carriere FMSG("RNG noise error"); 112c99311c8SEtienne Carriere return TEE_ERROR_NO_DATA; 11323123473SEtienne Carriere } 114c99311c8SEtienne Carriere 115c99311c8SEtienne Carriere buf = out; 116c99311c8SEtienne Carriere req_size = MIN(RNG_FIFO_BYTE_DEPTH, *size); 117c99311c8SEtienne Carriere len = req_size; 118f3c22059SEtienne Carriere 119f3c22059SEtienne Carriere /* RNG is ready: read up to 4 32bit words */ 120f3c22059SEtienne Carriere while (len) { 121f3c22059SEtienne Carriere uint32_t data32 = io_read32(rng_base + RNG_DR); 122f3c22059SEtienne Carriere size_t sz = MIN(len, sizeof(uint32_t)); 123f3c22059SEtienne Carriere 124f3c22059SEtienne Carriere memcpy(buf, &data32, sz); 125f3c22059SEtienne Carriere buf += sz; 126f3c22059SEtienne Carriere len -= sz; 127f3c22059SEtienne Carriere } 128c99311c8SEtienne Carriere 129f3c22059SEtienne Carriere *size = req_size; 130f3c22059SEtienne Carriere 131c99311c8SEtienne Carriere return TEE_SUCCESS; 132f3c22059SEtienne Carriere } 133f3c22059SEtienne Carriere 134f63f11bdSGatien Chevallier static TEE_Result init_rng(void) 135f3c22059SEtienne Carriere { 136f63f11bdSGatien Chevallier vaddr_t rng_base = get_base(); 137f63f11bdSGatien Chevallier uint64_t timeout_ref = 0; 138f3c22059SEtienne Carriere 139f63f11bdSGatien Chevallier /* Clean error indications */ 140f63f11bdSGatien Chevallier io_write32(rng_base + RNG_SR, 0); 141f3c22059SEtienne Carriere 142f63f11bdSGatien Chevallier io_setbits32(rng_base + RNG_CR, RNG_CR_RNGEN | RNG_CR_CED); 143f63f11bdSGatien Chevallier 144*0817aa6fSGatien Chevallier timeout_ref = timeout_init_us(RNG_READY_TIMEOUT_US); 145f63f11bdSGatien Chevallier while (!(io_read32(rng_base + RNG_SR) & RNG_SR_DRDY)) 146f63f11bdSGatien Chevallier if (timeout_elapsed(timeout_ref)) 147f63f11bdSGatien Chevallier break; 148f63f11bdSGatien Chevallier 149f63f11bdSGatien Chevallier if (!(io_read32(rng_base + RNG_SR) & RNG_SR_DRDY)) 150f63f11bdSGatien Chevallier return TEE_ERROR_GENERIC; 151f63f11bdSGatien Chevallier 152f63f11bdSGatien Chevallier return TEE_SUCCESS; 153f3c22059SEtienne Carriere } 154f3c22059SEtienne Carriere 155f3c22059SEtienne Carriere TEE_Result stm32_rng_read(uint8_t *out, size_t size) 156f3c22059SEtienne Carriere { 157c99311c8SEtienne Carriere TEE_Result rc = TEE_ERROR_GENERIC; 158c99311c8SEtienne Carriere bool burst_timeout = false; 159c99311c8SEtienne Carriere uint64_t timeout_ref = 0; 160f3c22059SEtienne Carriere uint32_t exceptions = 0; 161f3c22059SEtienne Carriere uint8_t *out_ptr = out; 162c99311c8SEtienne Carriere vaddr_t rng_base = 0; 163f3c22059SEtienne Carriere size_t out_size = 0; 164f3c22059SEtienne Carriere 165f3c22059SEtienne Carriere if (!stm32_rng) { 166f3c22059SEtienne Carriere DMSG("No RNG"); 167f3c22059SEtienne Carriere return TEE_ERROR_NOT_SUPPORTED; 168f3c22059SEtienne Carriere } 169f3c22059SEtienne Carriere 170f63f11bdSGatien Chevallier clk_enable(stm32_rng->clock); 171f63f11bdSGatien Chevallier rng_base = get_base(); 172c99311c8SEtienne Carriere 173c99311c8SEtienne Carriere /* Arm timeout */ 174*0817aa6fSGatien Chevallier timeout_ref = timeout_init_us(RNG_READY_TIMEOUT_US); 175c99311c8SEtienne Carriere burst_timeout = false; 176f3c22059SEtienne Carriere 177f3c22059SEtienne Carriere while (out_size < size) { 178f3c22059SEtienne Carriere /* Read by chunks of the size the RNG FIFO depth */ 179f3c22059SEtienne Carriere size_t sz = size - out_size; 180f3c22059SEtienne Carriere 181f3c22059SEtienne Carriere exceptions = may_spin_lock(&stm32_rng->lock); 182f3c22059SEtienne Carriere 183c99311c8SEtienne Carriere rc = read_available(rng_base, out_ptr, &sz); 184c99311c8SEtienne Carriere 185c99311c8SEtienne Carriere /* Raise timeout only if we failed to get some samples */ 186c99311c8SEtienne Carriere assert(!rc || rc == TEE_ERROR_NO_DATA); 187c99311c8SEtienne Carriere if (rc) 188c99311c8SEtienne Carriere burst_timeout = timeout_elapsed(timeout_ref); 189f3c22059SEtienne Carriere 190f3c22059SEtienne Carriere may_spin_unlock(&stm32_rng->lock, exceptions); 191f3c22059SEtienne Carriere 192c99311c8SEtienne Carriere if (burst_timeout) { 193c99311c8SEtienne Carriere rc = TEE_ERROR_GENERIC; 194c99311c8SEtienne Carriere goto out; 195f3c22059SEtienne Carriere } 196f3c22059SEtienne Carriere 197c99311c8SEtienne Carriere if (!rc) { 198c99311c8SEtienne Carriere out_size += sz; 199c99311c8SEtienne Carriere out_ptr += sz; 200c99311c8SEtienne Carriere /* Re-arm timeout */ 201*0817aa6fSGatien Chevallier timeout_ref = timeout_init_us(RNG_READY_TIMEOUT_US); 202c99311c8SEtienne Carriere burst_timeout = false; 203c99311c8SEtienne Carriere } 204c99311c8SEtienne Carriere } 205c99311c8SEtienne Carriere 206c99311c8SEtienne Carriere out: 207c99311c8SEtienne Carriere assert(!rc || rc == TEE_ERROR_GENERIC); 208f63f11bdSGatien Chevallier clk_disable(stm32_rng->clock); 209f3c22059SEtienne Carriere 210f3c22059SEtienne Carriere return rc; 211f3c22059SEtienne Carriere } 212f3c22059SEtienne Carriere 213cd451498SEtienne Carriere #ifdef CFG_WITH_SOFTWARE_PRNG 214cd451498SEtienne Carriere /* Override weak plat_rng_init with platform handler to seed PRNG */ 215cd451498SEtienne Carriere void plat_rng_init(void) 216cd451498SEtienne Carriere { 217cd451498SEtienne Carriere uint8_t seed[RNG_FIFO_BYTE_DEPTH] = { }; 218cd451498SEtienne Carriere 219cd451498SEtienne Carriere if (stm32_rng_read(seed, sizeof(seed))) 220cd451498SEtienne Carriere panic(); 221cd451498SEtienne Carriere 222cd451498SEtienne Carriere if (crypto_rng_init(seed, sizeof(seed))) 223cd451498SEtienne Carriere panic(); 224cd451498SEtienne Carriere 225cd451498SEtienne Carriere DMSG("PRNG seeded with RNG"); 226cd451498SEtienne Carriere } 227cd451498SEtienne Carriere #else 228cb2478efSAndrew Davis TEE_Result hw_get_random_bytes(void *out, size_t size) 229097f329aSEtienne Carriere { 230097f329aSEtienne Carriere return stm32_rng_read(out, size); 231097f329aSEtienne Carriere } 232097f329aSEtienne Carriere #endif 233097f329aSEtienne Carriere 234f3c22059SEtienne Carriere #ifdef CFG_EMBED_DTB 235ea8ba295SGatien Chevallier static TEE_Result stm32_rng_parse_fdt(const void *fdt, int node) 236f3c22059SEtienne Carriere { 237d7a1a7d2SEtienne Carriere TEE_Result res = TEE_ERROR_GENERIC; 238ea8ba295SGatien Chevallier struct dt_node_info dt_rng = { }; 239f3c22059SEtienne Carriere 240ea8ba295SGatien Chevallier _fdt_fill_device_info(fdt, &dt_rng, node); 241ea8ba295SGatien Chevallier if (dt_rng.reg == DT_INFO_INVALID_REG) 242ea8ba295SGatien Chevallier return TEE_ERROR_BAD_PARAMETERS; 243f3c22059SEtienne Carriere 244ea8ba295SGatien Chevallier stm32_rng->base.pa = dt_rng.reg; 245ea8ba295SGatien Chevallier stm32_rng->base.va = io_pa_or_va_secure(&stm32_rng->base, 246ea8ba295SGatien Chevallier dt_rng.reg_size); 247ea8ba295SGatien Chevallier assert(stm32_rng->base.va); 248f3c22059SEtienne Carriere 249ea8ba295SGatien Chevallier res = rstctrl_dt_get_by_index(fdt, node, 0, &stm32_rng->rstctrl); 250ea8ba295SGatien Chevallier if (res != TEE_SUCCESS && res != TEE_ERROR_ITEM_NOT_FOUND) 251ea8ba295SGatien Chevallier return res; 25268c4a16bSEtienne Carriere 253d7a1a7d2SEtienne Carriere res = clk_dt_get_by_index(fdt, node, 0, &stm32_rng->clock); 254d7a1a7d2SEtienne Carriere if (res) 255d7a1a7d2SEtienne Carriere return res; 256d7a1a7d2SEtienne Carriere 257ea8ba295SGatien Chevallier /* Release device if not used at runtime or for pm transitions */ 258ea8ba295SGatien Chevallier stm32_rng->release_post_boot = IS_ENABLED(CFG_WITH_SOFTWARE_PRNG) && 259ea8ba295SGatien Chevallier !IS_ENABLED(CFG_PM); 260f3c22059SEtienne Carriere 261f3c22059SEtienne Carriere return TEE_SUCCESS; 262f3c22059SEtienne Carriere } 263f3c22059SEtienne Carriere 264ea8ba295SGatien Chevallier static TEE_Result stm32_rng_probe(const void *fdt, int offs, 265ea8ba295SGatien Chevallier const void *compat_data __unused) 266ea8ba295SGatien Chevallier { 267ea8ba295SGatien Chevallier TEE_Result res = TEE_ERROR_GENERIC; 268ea8ba295SGatien Chevallier 269ea8ba295SGatien Chevallier /* Expect a single RNG instance */ 270ea8ba295SGatien Chevallier assert(!stm32_rng); 271ea8ba295SGatien Chevallier 272ea8ba295SGatien Chevallier stm32_rng = calloc(1, sizeof(*stm32_rng)); 273ea8ba295SGatien Chevallier if (!stm32_rng) 274ea8ba295SGatien Chevallier panic(); 275ea8ba295SGatien Chevallier 276ea8ba295SGatien Chevallier res = stm32_rng_parse_fdt(fdt, offs); 277ea8ba295SGatien Chevallier if (res) 278ea8ba295SGatien Chevallier goto err; 279ea8ba295SGatien Chevallier 280ea8ba295SGatien Chevallier res = clk_enable(stm32_rng->clock); 281ea8ba295SGatien Chevallier if (res) 282ea8ba295SGatien Chevallier goto err; 283ea8ba295SGatien Chevallier 284ea8ba295SGatien Chevallier if (stm32_rng->rstctrl && 285ea8ba295SGatien Chevallier rstctrl_assert_to(stm32_rng->rstctrl, RNG_RESET_TIMEOUT_US)) { 286ea8ba295SGatien Chevallier res = TEE_ERROR_GENERIC; 287ea8ba295SGatien Chevallier goto err_clk; 288ea8ba295SGatien Chevallier } 289ea8ba295SGatien Chevallier 290ea8ba295SGatien Chevallier if (stm32_rng->rstctrl && 291ea8ba295SGatien Chevallier rstctrl_deassert_to(stm32_rng->rstctrl, RNG_RESET_TIMEOUT_US)) { 292ea8ba295SGatien Chevallier res = TEE_ERROR_GENERIC; 293ea8ba295SGatien Chevallier goto err_clk; 294ea8ba295SGatien Chevallier } 295ea8ba295SGatien Chevallier 296f63f11bdSGatien Chevallier res = init_rng(); 297f63f11bdSGatien Chevallier if (res) 298f63f11bdSGatien Chevallier goto err_clk; 299f63f11bdSGatien Chevallier 300ea8ba295SGatien Chevallier clk_disable(stm32_rng->clock); 301ea8ba295SGatien Chevallier 302ea8ba295SGatien Chevallier if (stm32_rng->release_post_boot) 303ea8ba295SGatien Chevallier stm32mp_register_non_secure_periph_iomem(stm32_rng->base.pa); 304ea8ba295SGatien Chevallier else 305ea8ba295SGatien Chevallier stm32mp_register_secure_periph_iomem(stm32_rng->base.pa); 306ea8ba295SGatien Chevallier 307ea8ba295SGatien Chevallier return TEE_SUCCESS; 308ea8ba295SGatien Chevallier 309ea8ba295SGatien Chevallier err_clk: 310ea8ba295SGatien Chevallier clk_disable(stm32_rng->clock); 311ea8ba295SGatien Chevallier err: 312ea8ba295SGatien Chevallier free(stm32_rng); 313ea8ba295SGatien Chevallier stm32_rng = NULL; 314ea8ba295SGatien Chevallier 315ea8ba295SGatien Chevallier return res; 316ea8ba295SGatien Chevallier } 317ea8ba295SGatien Chevallier 318ea8ba295SGatien Chevallier static const struct dt_device_match rng_match_table[] = { 319ea8ba295SGatien Chevallier { .compatible = "st,stm32-rng" }, 320ea8ba295SGatien Chevallier { .compatible = "st,stm32mp13-rng" }, 321ea8ba295SGatien Chevallier { } 322ea8ba295SGatien Chevallier }; 323ea8ba295SGatien Chevallier 324ea8ba295SGatien Chevallier DEFINE_DT_DRIVER(stm32_rng_dt_driver) = { 325ea8ba295SGatien Chevallier .name = "stm32_rng", 326ea8ba295SGatien Chevallier .match_table = rng_match_table, 327ea8ba295SGatien Chevallier .probe = stm32_rng_probe, 328ea8ba295SGatien Chevallier }; 329d8682c4cSEtienne Carriere 330d8682c4cSEtienne Carriere static TEE_Result stm32_rng_release(void) 331d8682c4cSEtienne Carriere { 332d8682c4cSEtienne Carriere if (stm32_rng && stm32_rng->release_post_boot) { 333d8682c4cSEtienne Carriere DMSG("Release RNG driver"); 334d8682c4cSEtienne Carriere free(stm32_rng); 335d8682c4cSEtienne Carriere stm32_rng = NULL; 336d8682c4cSEtienne Carriere } 337d8682c4cSEtienne Carriere 338d8682c4cSEtienne Carriere return TEE_SUCCESS; 339d8682c4cSEtienne Carriere } 340d8682c4cSEtienne Carriere 341d8682c4cSEtienne Carriere release_init_resource(stm32_rng_release); 342f3c22059SEtienne Carriere #endif /*CFG_EMBED_DTB*/ 343