1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (c) 2023, Arm Ltd. 4 * 5 * Use the (optional) ARMv8.5 RNDR register to provide random numbers to 6 * U-Boot's UCLASS_RNG users. 7 * Detection is done at runtime using the CPU ID registers. 8 */ 9 10#define LOG_CATEGORY UCLASS_RNG 11 12#include <dm.h> 13#include <rng.h> 14#include <asm/system.h> 15#include <linux/kernel.h> 16 17#define DRIVER_NAME "arm-rndr" 18 19static bool cpu_has_rndr(void) 20{ 21 uint64_t reg; 22 23 __asm__ volatile("mrs %0, ID_AA64ISAR0_EL1\n" : "=r" (reg)); 24 return !!(reg & ID_AA64ISAR0_EL1_RNDR); 25} 26 27/* 28 * The system register name is RNDR, but this isn't widely known among older 29 * toolchains, and also triggers errors because of it being an architecture 30 * extension. Since we check the availability of the register before, it's 31 * fine to use here, though. 32 */ 33#define RNDR "S3_3_C2_C4_0" 34 35static uint64_t read_rndr(void) 36{ 37 uint64_t reg; 38 39 __asm__ volatile("mrs %0, " RNDR "\n" : "=r" (reg)); 40 41 return reg; 42} 43 44static int arm_rndr_read(struct udevice *dev, void *data, size_t len) 45{ 46 uint64_t random; 47 48 while (len) { 49 int tocopy = min(sizeof(uint64_t), len); 50 51 random = read_rndr(); 52 memcpy(data, &random, tocopy); 53 len -= tocopy; 54 data += tocopy; 55 } 56 57 return 0; 58} 59 60static const struct dm_rng_ops arm_rndr_ops = { 61 .read = arm_rndr_read, 62}; 63 64static int arm_rndr_bind(struct udevice *dev) 65{ 66 if (!cpu_has_rndr()) 67 return -ENOENT; 68 69 return 0; 70} 71 72U_BOOT_DRIVER(arm_rndr) = { 73 .name = DRIVER_NAME, 74 .id = UCLASS_RNG, 75 .ops = &arm_rndr_ops, 76 .bind = arm_rndr_bind, 77}; 78 79U_BOOT_DRVINFO(cpu_arm_rndr) = { 80 .name = DRIVER_NAME, 81}; 82