xref: /linux/arch/arm64/include/asm/archrandom.h (revision 35bde68bba5413592d88864eced79f8a0482bb4f)
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