Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

arm64: Add support for SMCCC TRNG entropy source

The ARM architected TRNG firmware interface, described in ARM spec
DEN0098, defines an ARM SMCCC based interface to a true random number
generator, provided by firmware.
This can be discovered via the SMCCC >=v1.1 interface, and provides
up to 192 bits of entropy per call.

Hook this SMC call into arm64's arch_get_random_*() implementation,
coming to the rescue when the CPU does not implement the ARM v8.5 RNG
system registers.

For the detection, we piggy back on the PSCI/SMCCC discovery (which gives
us the conduit to use (hvc/smc)), then try to call the
ARM_SMCCC_TRNG_VERSION function, which returns -1 if this interface is
not implemented.

Reviewed-by: Mark Brown <broonie@kernel.org>
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Signed-off-by: Will Deacon <will@kernel.org>

authored by

Andre Przywara and committed by
Will Deacon
38db9873 a37e31fc

+61 -11
+61 -11
arch/arm64/include/asm/archrandom.h
··· 4 4 5 5 #ifdef CONFIG_ARCH_RANDOM 6 6 7 + #include <linux/arm-smccc.h> 7 8 #include <linux/bug.h> 8 9 #include <linux/kernel.h> 9 10 #include <asm/cpufeature.h> 10 11 12 + #define ARM_SMCCC_TRNG_MIN_VERSION 0x10000UL 13 + 14 + extern bool smccc_trng_available; 15 + 11 16 static inline bool __init smccc_probe_trng(void) 12 17 { 13 - return false; 18 + struct arm_smccc_res res; 19 + 20 + arm_smccc_1_1_invoke(ARM_SMCCC_TRNG_VERSION, &res); 21 + if ((s32)res.a0 < 0) 22 + return false; 23 + 24 + return res.a0 >= ARM_SMCCC_TRNG_MIN_VERSION; 14 25 } 15 26 16 27 static inline bool __arm64_rndr(unsigned long *v) ··· 54 43 55 44 static inline bool __must_check arch_get_random_seed_long(unsigned long *v) 56 45 { 46 + struct arm_smccc_res res; 47 + 48 + /* 49 + * We prefer the SMCCC call, since its semantics (return actual 50 + * hardware backed entropy) is closer to the idea behind this 51 + * function here than what even the RNDRSS register provides 52 + * (the output of a pseudo RNG freshly seeded by a TRNG). 53 + */ 54 + if (smccc_trng_available) { 55 + arm_smccc_1_1_invoke(ARM_SMCCC_TRNG_RND64, 64, &res); 56 + if ((int)res.a0 >= 0) { 57 + *v = res.a3; 58 + return true; 59 + } 60 + } 61 + 57 62 /* 58 63 * Only support the generic interface after we have detected 59 64 * the system wide capability, avoiding complexity with the 60 65 * cpufeature code and with potential scheduling between CPUs 61 66 * with and without the feature. 62 67 */ 63 - if (!cpus_have_const_cap(ARM64_HAS_RNG)) 64 - return false; 68 + if (cpus_have_const_cap(ARM64_HAS_RNG) && __arm64_rndr(v)) 69 + return true; 65 70 66 - return __arm64_rndr(v); 71 + return false; 67 72 } 68 - 69 73 70 74 static inline bool __must_check arch_get_random_seed_int(unsigned int *v) 71 75 { 76 + struct arm_smccc_res res; 72 77 unsigned long val; 73 - bool ok = arch_get_random_seed_long(&val); 74 78 75 - *v = val; 76 - return ok; 79 + if (smccc_trng_available) { 80 + arm_smccc_1_1_invoke(ARM_SMCCC_TRNG_RND64, 32, &res); 81 + if ((int)res.a0 >= 0) { 82 + *v = res.a3 & GENMASK(31, 0); 83 + return true; 84 + } 85 + } 86 + 87 + if (cpus_have_const_cap(ARM64_HAS_RNG)) { 88 + if (__arm64_rndr(&val)) { 89 + *v = val; 90 + return true; 91 + } 92 + } 93 + 94 + return false; 77 95 } 78 96 79 97 static inline bool __init __early_cpu_has_rndr(void) ··· 117 77 { 118 78 WARN_ON(system_state != SYSTEM_BOOTING); 119 79 120 - if (!__early_cpu_has_rndr()) 121 - return false; 80 + if (smccc_trng_available) { 81 + struct arm_smccc_res res; 122 82 123 - return __arm64_rndr(v); 83 + arm_smccc_1_1_invoke(ARM_SMCCC_TRNG_RND64, 64, &res); 84 + if ((int)res.a0 >= 0) { 85 + *v = res.a3; 86 + return true; 87 + } 88 + } 89 + 90 + if (__early_cpu_has_rndr() && __arm64_rndr(v)) 91 + return true; 92 + 93 + return false; 124 94 } 125 95 #define arch_get_random_seed_long_early arch_get_random_seed_long_early 126 96