11a50ec0bSRichard Henderson /* SPDX-License-Identifier: GPL-2.0 */ 21a50ec0bSRichard Henderson #ifndef _ASM_ARCHRANDOM_H 31a50ec0bSRichard Henderson #define _ASM_ARCHRANDOM_H 41a50ec0bSRichard Henderson 51a50ec0bSRichard Henderson #ifdef CONFIG_ARCH_RANDOM 61a50ec0bSRichard Henderson 738db9873SAndre Przywara #include <linux/arm-smccc.h> 8ead5084cSMark Rutland #include <linux/bug.h> 9ead5084cSMark Rutland #include <linux/kernel.h> 101a50ec0bSRichard Henderson #include <asm/cpufeature.h> 111a50ec0bSRichard Henderson 1238db9873SAndre Przywara #define ARM_SMCCC_TRNG_MIN_VERSION 0x10000UL 1338db9873SAndre Przywara 1438db9873SAndre Przywara extern bool smccc_trng_available; 1538db9873SAndre Przywara 16a37e31fcSAndre Przywara static inline bool __init smccc_probe_trng(void) 17a37e31fcSAndre Przywara { 1838db9873SAndre Przywara struct arm_smccc_res res; 1938db9873SAndre Przywara 2038db9873SAndre Przywara arm_smccc_1_1_invoke(ARM_SMCCC_TRNG_VERSION, &res); 2138db9873SAndre Przywara if ((s32)res.a0 < 0) 22a37e31fcSAndre Przywara return false; 2338db9873SAndre Przywara 2438db9873SAndre Przywara return res.a0 >= ARM_SMCCC_TRNG_MIN_VERSION; 25a37e31fcSAndre Przywara } 26a37e31fcSAndre Przywara 271a50ec0bSRichard Henderson static inline bool __arm64_rndr(unsigned long *v) 281a50ec0bSRichard Henderson { 291a50ec0bSRichard Henderson bool ok; 301a50ec0bSRichard Henderson 311a50ec0bSRichard Henderson /* 321a50ec0bSRichard Henderson * Reads of RNDR set PSTATE.NZCV to 0b0000 on success, 331a50ec0bSRichard Henderson * and set PSTATE.NZCV to 0b0100 otherwise. 341a50ec0bSRichard Henderson */ 351a50ec0bSRichard Henderson asm volatile( 361a50ec0bSRichard Henderson __mrs_s("%0", SYS_RNDR_EL0) "\n" 371a50ec0bSRichard Henderson " cset %w1, ne\n" 381a50ec0bSRichard Henderson : "=r" (*v), "=r" (ok) 391a50ec0bSRichard Henderson : 401a50ec0bSRichard Henderson : "cc"); 411a50ec0bSRichard Henderson 421a50ec0bSRichard Henderson return ok; 431a50ec0bSRichard Henderson } 441a50ec0bSRichard Henderson 45*35bde68bSArd Biesheuvel static inline bool __arm64_rndrrs(unsigned long *v) 46*35bde68bSArd Biesheuvel { 47*35bde68bSArd Biesheuvel bool ok; 48*35bde68bSArd Biesheuvel 49*35bde68bSArd Biesheuvel /* 50*35bde68bSArd Biesheuvel * Reads of RNDRRS set PSTATE.NZCV to 0b0000 on success, 51*35bde68bSArd Biesheuvel * and set PSTATE.NZCV to 0b0100 otherwise. 52*35bde68bSArd Biesheuvel */ 53*35bde68bSArd Biesheuvel asm volatile( 54*35bde68bSArd Biesheuvel __mrs_s("%0", SYS_RNDRRS_EL0) "\n" 55*35bde68bSArd Biesheuvel " cset %w1, ne\n" 56*35bde68bSArd Biesheuvel : "=r" (*v), "=r" (ok) 57*35bde68bSArd Biesheuvel : 58*35bde68bSArd Biesheuvel : "cc"); 59*35bde68bSArd Biesheuvel 60*35bde68bSArd Biesheuvel return ok; 61*35bde68bSArd Biesheuvel } 62*35bde68bSArd Biesheuvel 631a50ec0bSRichard Henderson static inline bool __must_check arch_get_random_long(unsigned long *v) 641a50ec0bSRichard Henderson { 65*35bde68bSArd Biesheuvel /* 66*35bde68bSArd Biesheuvel * Only support the generic interface after we have detected 67*35bde68bSArd Biesheuvel * the system wide capability, avoiding complexity with the 68*35bde68bSArd Biesheuvel * cpufeature code and with potential scheduling between CPUs 69*35bde68bSArd Biesheuvel * with and without the feature. 70*35bde68bSArd Biesheuvel */ 71*35bde68bSArd Biesheuvel if (cpus_have_const_cap(ARM64_HAS_RNG) && __arm64_rndr(v)) 72*35bde68bSArd Biesheuvel return true; 731a50ec0bSRichard Henderson return false; 741a50ec0bSRichard Henderson } 751a50ec0bSRichard Henderson 761a50ec0bSRichard Henderson static inline bool __must_check arch_get_random_int(unsigned int *v) 771a50ec0bSRichard Henderson { 78*35bde68bSArd Biesheuvel if (cpus_have_const_cap(ARM64_HAS_RNG)) { 79*35bde68bSArd Biesheuvel unsigned long val; 80*35bde68bSArd Biesheuvel 81*35bde68bSArd Biesheuvel if (__arm64_rndr(&val)) { 82*35bde68bSArd Biesheuvel *v = val; 83*35bde68bSArd Biesheuvel return true; 84*35bde68bSArd Biesheuvel } 85*35bde68bSArd Biesheuvel } 861a50ec0bSRichard Henderson return false; 871a50ec0bSRichard Henderson } 881a50ec0bSRichard Henderson 891a50ec0bSRichard Henderson static inline bool __must_check arch_get_random_seed_long(unsigned long *v) 901a50ec0bSRichard Henderson { 9138db9873SAndre Przywara struct arm_smccc_res res; 9238db9873SAndre Przywara 9338db9873SAndre Przywara /* 9438db9873SAndre Przywara * We prefer the SMCCC call, since its semantics (return actual 9538db9873SAndre Przywara * hardware backed entropy) is closer to the idea behind this 9638db9873SAndre Przywara * function here than what even the RNDRSS register provides 9738db9873SAndre Przywara * (the output of a pseudo RNG freshly seeded by a TRNG). 9838db9873SAndre Przywara */ 9938db9873SAndre Przywara if (smccc_trng_available) { 10038db9873SAndre Przywara arm_smccc_1_1_invoke(ARM_SMCCC_TRNG_RND64, 64, &res); 10138db9873SAndre Przywara if ((int)res.a0 >= 0) { 10238db9873SAndre Przywara *v = res.a3; 10338db9873SAndre Przywara return true; 10438db9873SAndre Przywara } 10538db9873SAndre Przywara } 10638db9873SAndre Przywara 1071a50ec0bSRichard Henderson /* 108*35bde68bSArd Biesheuvel * RNDRRS is not backed by an entropy source but by a DRBG that is 109*35bde68bSArd Biesheuvel * reseeded after each invocation. This is not a 100% fit but good 110*35bde68bSArd Biesheuvel * enough to implement this API if no other entropy source exists. 1111a50ec0bSRichard Henderson */ 112*35bde68bSArd Biesheuvel if (cpus_have_const_cap(ARM64_HAS_RNG) && __arm64_rndrrs(v)) 11338db9873SAndre Przywara return true; 11438db9873SAndre Przywara 1151a50ec0bSRichard Henderson return false; 1161a50ec0bSRichard Henderson } 1171a50ec0bSRichard Henderson 1181a50ec0bSRichard Henderson static inline bool __must_check arch_get_random_seed_int(unsigned int *v) 1191a50ec0bSRichard Henderson { 12038db9873SAndre Przywara struct arm_smccc_res res; 1211a50ec0bSRichard Henderson unsigned long val; 1221a50ec0bSRichard Henderson 12338db9873SAndre Przywara if (smccc_trng_available) { 12438db9873SAndre Przywara arm_smccc_1_1_invoke(ARM_SMCCC_TRNG_RND64, 32, &res); 12538db9873SAndre Przywara if ((int)res.a0 >= 0) { 12638db9873SAndre Przywara *v = res.a3 & GENMASK(31, 0); 12738db9873SAndre Przywara return true; 12838db9873SAndre Przywara } 12938db9873SAndre Przywara } 13038db9873SAndre Przywara 13138db9873SAndre Przywara if (cpus_have_const_cap(ARM64_HAS_RNG)) { 132*35bde68bSArd Biesheuvel if (__arm64_rndrrs(&val)) { 1331a50ec0bSRichard Henderson *v = val; 13438db9873SAndre Przywara return true; 13538db9873SAndre Przywara } 13638db9873SAndre Przywara } 13738db9873SAndre Przywara 13838db9873SAndre Przywara return false; 1391a50ec0bSRichard Henderson } 1401a50ec0bSRichard Henderson 1412e8e1ea8SMark Brown static inline bool __init __early_cpu_has_rndr(void) 1422e8e1ea8SMark Brown { 1432e8e1ea8SMark Brown /* Open code as we run prior to the first call to cpufeature. */ 1442e8e1ea8SMark Brown unsigned long ftr = read_sysreg_s(SYS_ID_AA64ISAR0_EL1); 1452e8e1ea8SMark Brown return (ftr >> ID_AA64ISAR0_RNDR_SHIFT) & 0xf; 1462e8e1ea8SMark Brown } 1472e8e1ea8SMark Brown 148ead5084cSMark Rutland static inline bool __init __must_check 149ead5084cSMark Rutland arch_get_random_seed_long_early(unsigned long *v) 150ead5084cSMark Rutland { 151ead5084cSMark Rutland WARN_ON(system_state != SYSTEM_BOOTING); 152ead5084cSMark Rutland 15338db9873SAndre Przywara if (smccc_trng_available) { 15438db9873SAndre Przywara struct arm_smccc_res res; 155ead5084cSMark Rutland 15638db9873SAndre Przywara arm_smccc_1_1_invoke(ARM_SMCCC_TRNG_RND64, 64, &res); 15738db9873SAndre Przywara if ((int)res.a0 >= 0) { 15838db9873SAndre Przywara *v = res.a3; 15938db9873SAndre Przywara return true; 16038db9873SAndre Przywara } 16138db9873SAndre Przywara } 16238db9873SAndre Przywara 16338db9873SAndre Przywara if (__early_cpu_has_rndr() && __arm64_rndr(v)) 16438db9873SAndre Przywara return true; 16538db9873SAndre Przywara 16638db9873SAndre Przywara return false; 167ead5084cSMark Rutland } 168ead5084cSMark Rutland #define arch_get_random_seed_long_early arch_get_random_seed_long_early 169ead5084cSMark Rutland 170a37e31fcSAndre Przywara #else /* !CONFIG_ARCH_RANDOM */ 171a37e31fcSAndre Przywara 172a37e31fcSAndre Przywara static inline bool __init smccc_probe_trng(void) 173a37e31fcSAndre Przywara { 174a37e31fcSAndre Przywara return false; 175a37e31fcSAndre Przywara } 176a37e31fcSAndre Przywara 1771a50ec0bSRichard Henderson #endif /* CONFIG_ARCH_RANDOM */ 1781a50ec0bSRichard Henderson #endif /* _ASM_ARCHRANDOM_H */ 179