162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
262306a36Sopenharmony_ci#ifndef _ASM_ARCHRANDOM_H
362306a36Sopenharmony_ci#define _ASM_ARCHRANDOM_H
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#include <linux/arm-smccc.h>
662306a36Sopenharmony_ci#include <linux/bug.h>
762306a36Sopenharmony_ci#include <linux/kernel.h>
862306a36Sopenharmony_ci#include <linux/irqflags.h>
962306a36Sopenharmony_ci#include <asm/cpufeature.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#define ARM_SMCCC_TRNG_MIN_VERSION	0x10000UL
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ciextern bool smccc_trng_available;
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_cistatic inline bool __init smccc_probe_trng(void)
1662306a36Sopenharmony_ci{
1762306a36Sopenharmony_ci	struct arm_smccc_res res;
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci	arm_smccc_1_1_invoke(ARM_SMCCC_TRNG_VERSION, &res);
2062306a36Sopenharmony_ci	if ((s32)res.a0 < 0)
2162306a36Sopenharmony_ci		return false;
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci	return res.a0 >= ARM_SMCCC_TRNG_MIN_VERSION;
2462306a36Sopenharmony_ci}
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic inline bool __arm64_rndr(unsigned long *v)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	bool ok;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	/*
3162306a36Sopenharmony_ci	 * Reads of RNDR set PSTATE.NZCV to 0b0000 on success,
3262306a36Sopenharmony_ci	 * and set PSTATE.NZCV to 0b0100 otherwise.
3362306a36Sopenharmony_ci	 */
3462306a36Sopenharmony_ci	asm volatile(
3562306a36Sopenharmony_ci		__mrs_s("%0", SYS_RNDR_EL0) "\n"
3662306a36Sopenharmony_ci	"	cset %w1, ne\n"
3762306a36Sopenharmony_ci	: "=r" (*v), "=r" (ok)
3862306a36Sopenharmony_ci	:
3962306a36Sopenharmony_ci	: "cc");
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	return ok;
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic inline bool __arm64_rndrrs(unsigned long *v)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	bool ok;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	/*
4962306a36Sopenharmony_ci	 * Reads of RNDRRS set PSTATE.NZCV to 0b0000 on success,
5062306a36Sopenharmony_ci	 * and set PSTATE.NZCV to 0b0100 otherwise.
5162306a36Sopenharmony_ci	 */
5262306a36Sopenharmony_ci	asm volatile(
5362306a36Sopenharmony_ci		__mrs_s("%0", SYS_RNDRRS_EL0) "\n"
5462306a36Sopenharmony_ci	"	cset %w1, ne\n"
5562306a36Sopenharmony_ci	: "=r" (*v), "=r" (ok)
5662306a36Sopenharmony_ci	:
5762306a36Sopenharmony_ci	: "cc");
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	return ok;
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic __always_inline bool __cpu_has_rng(void)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	if (unlikely(!system_capabilities_finalized() && !preemptible()))
6562306a36Sopenharmony_ci		return this_cpu_has_cap(ARM64_HAS_RNG);
6662306a36Sopenharmony_ci	return cpus_have_const_cap(ARM64_HAS_RNG);
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic inline size_t __must_check arch_get_random_longs(unsigned long *v, size_t max_longs)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	/*
7262306a36Sopenharmony_ci	 * Only support the generic interface after we have detected
7362306a36Sopenharmony_ci	 * the system wide capability, avoiding complexity with the
7462306a36Sopenharmony_ci	 * cpufeature code and with potential scheduling between CPUs
7562306a36Sopenharmony_ci	 * with and without the feature.
7662306a36Sopenharmony_ci	 */
7762306a36Sopenharmony_ci	if (max_longs && __cpu_has_rng() && __arm64_rndr(v))
7862306a36Sopenharmony_ci		return 1;
7962306a36Sopenharmony_ci	return 0;
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic inline size_t __must_check arch_get_random_seed_longs(unsigned long *v, size_t max_longs)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	if (!max_longs)
8562306a36Sopenharmony_ci		return 0;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	/*
8862306a36Sopenharmony_ci	 * We prefer the SMCCC call, since its semantics (return actual
8962306a36Sopenharmony_ci	 * hardware backed entropy) is closer to the idea behind this
9062306a36Sopenharmony_ci	 * function here than what even the RNDRSS register provides
9162306a36Sopenharmony_ci	 * (the output of a pseudo RNG freshly seeded by a TRNG).
9262306a36Sopenharmony_ci	 */
9362306a36Sopenharmony_ci	if (smccc_trng_available) {
9462306a36Sopenharmony_ci		struct arm_smccc_res res;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci		max_longs = min_t(size_t, 3, max_longs);
9762306a36Sopenharmony_ci		arm_smccc_1_1_invoke(ARM_SMCCC_TRNG_RND64, max_longs * 64, &res);
9862306a36Sopenharmony_ci		if ((int)res.a0 >= 0) {
9962306a36Sopenharmony_ci			switch (max_longs) {
10062306a36Sopenharmony_ci			case 3:
10162306a36Sopenharmony_ci				*v++ = res.a1;
10262306a36Sopenharmony_ci				fallthrough;
10362306a36Sopenharmony_ci			case 2:
10462306a36Sopenharmony_ci				*v++ = res.a2;
10562306a36Sopenharmony_ci				fallthrough;
10662306a36Sopenharmony_ci			case 1:
10762306a36Sopenharmony_ci				*v++ = res.a3;
10862306a36Sopenharmony_ci				break;
10962306a36Sopenharmony_ci			}
11062306a36Sopenharmony_ci			return max_longs;
11162306a36Sopenharmony_ci		}
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	/*
11562306a36Sopenharmony_ci	 * RNDRRS is not backed by an entropy source but by a DRBG that is
11662306a36Sopenharmony_ci	 * reseeded after each invocation. This is not a 100% fit but good
11762306a36Sopenharmony_ci	 * enough to implement this API if no other entropy source exists.
11862306a36Sopenharmony_ci	 */
11962306a36Sopenharmony_ci	if (__cpu_has_rng() && __arm64_rndrrs(v))
12062306a36Sopenharmony_ci		return 1;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	return 0;
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic inline bool __init __early_cpu_has_rndr(void)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	/* Open code as we run prior to the first call to cpufeature. */
12862306a36Sopenharmony_ci	unsigned long ftr = read_sysreg_s(SYS_ID_AA64ISAR0_EL1);
12962306a36Sopenharmony_ci	return (ftr >> ID_AA64ISAR0_EL1_RNDR_SHIFT) & 0xf;
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ciu64 kaslr_early_init(void *fdt);
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci#endif /* _ASM_ARCHRANDOM_H */
135