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