// SPDX-License-Identifier: BSD-2-Clause /* * Copyright (c) 2022, Vaisala Oyj */ #include #include #include #include #include #include #include #include #include #include #include #include #define CONTROL_REG 0x00000000 #define STATUS_REG 0x00000004 #define RAND_REG 0x00000000 #define HOST_TO_TRNG_RESET 0x00000001 #define HOST_TO_TRNG_RELEASE_RESET 0x00000002 #define HOST_TO_TRNG_ENABLE 0x80000000 #define HOST_TO_TRNG_ZEROIZE 0x80000004 #define HOST_TO_TRNG_ACK_ZEROIZE 0x80000008 #define HOST_TO_TRNG_READ 0x8000000F /* trng statuses */ #define TRNG_ACK_RESET 0x000000AC #define TRNG_SUCCESSFUL_STARTUP 0x00000057 #define TRNG_FAILED_STARTUP 0x000000FA #define TRNG_NEW_RAND_AVAILABLE 0x000000ED static unsigned int trng_lock = SPINLOCK_UNLOCK; static vaddr_t xiphera_trng_base; static bool xiphera_trng_random_available(void) { uint32_t status = 0; status = io_read32(xiphera_trng_base + STATUS_REG); return status == TRNG_NEW_RAND_AVAILABLE; } static uint32_t xiphera_trng_read32(void) { uint32_t value = 0; value = io_read32(xiphera_trng_base + RAND_REG); /* * Ack that RNG value has been consumed and trigger new one to be * generated */ io_write32(xiphera_trng_base + CONTROL_REG, HOST_TO_TRNG_READ); io_write32(xiphera_trng_base + CONTROL_REG, HOST_TO_TRNG_ENABLE); return value; } /* This is a true RNG, no need for seeding */ void plat_rng_init(void) { } TEE_Result hw_get_random_bytes(void *buf, size_t len) { uint8_t *rngbuf = buf; uint32_t val = 0; size_t len_to_copy = 0; assert(buf); assert(xiphera_trng_base); while (len) { uint32_t exceptions = cpu_spin_lock_xsave(&trng_lock); if (xiphera_trng_random_available()) { val = xiphera_trng_read32(); len_to_copy = MIN(len, sizeof(uint32_t)); memcpy(rngbuf, &val, len_to_copy); rngbuf += len_to_copy; len -= len_to_copy; } cpu_spin_unlock_xrestore(&trng_lock, exceptions); } return TEE_SUCCESS; } static TEE_Result xiphera_trng_probe(const void *fdt, int node, const void *compat_data __unused) { int dt_status = _fdt_get_status(fdt, node); uint32_t status = 0; size_t size = 0; /* Skip non-secure instances */ if (dt_status != DT_STATUS_OK_SEC) return TEE_ERROR_NODE_DISABLED; if (xiphera_trng_base) { EMSG("Only one secure instance is supported"); return TEE_ERROR_GENERIC; } if (dt_map_dev(fdt, node, &xiphera_trng_base, &size, DT_MAP_AUTO) < 0) return TEE_ERROR_GENERIC; /* * The TRNG needs to be first reset in order to provide stable * operation. * * Reset of the chip should complete within 200 us but in some cases it * could take up to 400 us. If it is not ready within 400 us assume * there is problem. */ io_write32(xiphera_trng_base + CONTROL_REG, HOST_TO_TRNG_RESET); udelay(200); status = io_read32(xiphera_trng_base + STATUS_REG); if (status != TRNG_ACK_RESET) { /* * Give it additional 200 us to allow it to reset. * * If still not done -> error out. */ udelay(200); status = io_read32(xiphera_trng_base + STATUS_REG); if (status != TRNG_ACK_RESET) { EMSG("Failed to reset TRNG\n"); return TEE_ERROR_GENERIC; } } /* * Now TRNG should be internally stable. * * Clear internal random number generation engine to start in stable * state and give it 20 ms to enable good random number entropy and * then check that random number engine is ready. */ io_write32(xiphera_trng_base + CONTROL_REG, HOST_TO_TRNG_RELEASE_RESET); io_write32(xiphera_trng_base + CONTROL_REG, HOST_TO_TRNG_ENABLE); io_write32(xiphera_trng_base + CONTROL_REG, HOST_TO_TRNG_ZEROIZE); mdelay(20); status = io_read32(xiphera_trng_base + STATUS_REG); if (status != TRNG_SUCCESSFUL_STARTUP) { /* * Check specifically if there were startup test errors to aid * in debugging TRNG implementation in FPGA */ if (status == TRNG_FAILED_STARTUP) { EMSG("Startup tests have failed\n"); return TEE_ERROR_GENERIC; } EMSG("Startup tests yielded no response -> TRNG stuck\n"); return TEE_ERROR_GENERIC; } io_write32(xiphera_trng_base + CONTROL_REG, HOST_TO_TRNG_ACK_ZEROIZE); DMSG("TRNG initialized\n"); return TEE_SUCCESS; } static const struct dt_device_match xiphera_trng_match_table[] = { { .compatible = "xiphera,xip8001b-trng" }, { } }; DEFINE_DT_DRIVER(xiphera_trng_dt_driver) = { .name = "xiphera_trng", .type = DT_DRIVER_NOTYPE, .match_table = xiphera_trng_match_table, .probe = xiphera_trng_probe, };