// SPDX-License-Identifier: GPL-2.0-or-later /* * The RISC-V Zkr extension provides CSR seed which provides access to a * random number generator. */ #define LOG_CATEGORY UCLASS_RNG #include #include #include #include #define DRIVER_NAME "riscv_zkr" enum opst { /** @BIST: built in self test running */ BIST = 0b00, /** @WAIT: sufficient amount of entropy is not yet available */ WAIT = 0b01, /** @ES16: 16bits of entropy available */ ES16 = 0b10, /** @DEAD: unrecoverable self-test error */ DEAD = 0b11, }; static unsigned long read_seed(void) { unsigned long ret; __asm__ __volatile__("csrrw %0, seed, x0" : "=r" (ret) : : "memory"); return ret; } static int riscv_zkr_read(struct udevice *dev, void *data, size_t len) { u8 *ptr = data; while (len) { u32 val; val = read_seed(); switch (val >> 30) { case BIST: continue; case WAIT: continue; case ES16: *ptr++ = val & 0xff; if (--len) { *ptr++ = val >> 8; --len; } break; case DEAD: return -ENOENT; } } return 0; } /** * riscv_zkr_bind() - check if the seed register is available * * If the SBI software has not set mseccfg.sseed=1 or the Zkr extension is not * available, reading the seed register will result in an exception from which * this function safely resumes. * * @dev: RNG device * Return: 0 if successfully probed */ static int riscv_zkr_bind(struct udevice *dev) { struct resume_data resume; int ret; u32 val; /* Check if reading seed leads to interrupt */ set_resume(&resume); ret = setjmp(resume.jump); if (ret) log_debug("Exception %ld reading seed CSR\n", resume.code); else val = read_seed(); set_resume(NULL); if (ret) return -ENOENT; return 0; } /** * riscv_zkr_probe() - check if entropy is available * * The bind method already checked that the seed register can be read without * excpetiong. Here we wait for the self test to finish and entropy becoming * available. * * @dev: RNG device * Return: 0 if successfully probed */ static int riscv_zkr_probe(struct udevice *dev) { u32 val; do { val = read_seed(); val >>= 30; } while (val == BIST || val == WAIT); if (val == DEAD) return -ENOENT; return 0; } static const struct dm_rng_ops riscv_zkr_ops = { .read = riscv_zkr_read, }; U_BOOT_DRIVER(riscv_zkr) = { .name = DRIVER_NAME, .id = UCLASS_RNG, .ops = &riscv_zkr_ops, .bind = riscv_zkr_bind, .probe = riscv_zkr_probe, }; U_BOOT_DRVINFO(cpu_riscv_zkr) = { .name = DRIVER_NAME, };