diff options
Diffstat (limited to 'drivers/rng/arm_rndr.c')
-rw-r--r-- | drivers/rng/arm_rndr.c | 82 |
1 files changed, 82 insertions, 0 deletions
diff --git a/drivers/rng/arm_rndr.c b/drivers/rng/arm_rndr.c new file mode 100644 index 00000000000..55989743eae --- /dev/null +++ b/drivers/rng/arm_rndr.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2023, Arm Ltd. + * + * Use the (optional) ARMv8.5 RNDR register to provide random numbers to + * U-Boot's UCLASS_RNG users. + * Detection is done at runtime using the CPU ID registers. + */ + +#define LOG_CATEGORY UCLASS_RNG + +#include <common.h> +#include <dm.h> +#include <linux/kernel.h> +#include <rng.h> +#include <asm/system.h> + +#define DRIVER_NAME "arm-rndr" + +static bool cpu_has_rndr(void) +{ + uint64_t reg; + + __asm__ volatile("mrs %0, ID_AA64ISAR0_EL1\n" : "=r" (reg)); + return !!(reg & ID_AA64ISAR0_EL1_RNDR); +} + +/* + * The system register name is RNDR, but this isn't widely known among older + * toolchains, and also triggers errors because of it being an architecture + * extension. Since we check the availability of the register before, it's + * fine to use here, though. + */ +#define RNDR "S3_3_C2_C4_0" + +static uint64_t read_rndr(void) +{ + uint64_t reg; + + __asm__ volatile("mrs %0, " RNDR "\n" : "=r" (reg)); + + return reg; +} + +static int arm_rndr_read(struct udevice *dev, void *data, size_t len) +{ + uint64_t random; + + while (len) { + int tocopy = min(sizeof(uint64_t), len); + + random = read_rndr(); + memcpy(data, &random, tocopy); + len -= tocopy; + data += tocopy; + } + + return 0; +} + +static const struct dm_rng_ops arm_rndr_ops = { + .read = arm_rndr_read, +}; + +static int arm_rndr_probe(struct udevice *dev) +{ + if (!cpu_has_rndr()) + return -ENODEV; + + return 0; +} + +U_BOOT_DRIVER(arm_rndr) = { + .name = DRIVER_NAME, + .id = UCLASS_RNG, + .ops = &arm_rndr_ops, + .probe = arm_rndr_probe, +}; + +U_BOOT_DRVINFO(cpu_arm_rndr) = { + .name = DRIVER_NAME, +}; |