1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright (C) 2022 Linaro Limited
4  */
5 
6 #include <assert.h>
7 #include <config.h>
8 #include <crypto/crypto.h>
9 #include <kernel/panic.h>
10 #include <kernel/thread_arch.h>
11 #include <rng_support.h>
12 #include <sm/std_smc.h>
13 #include <stdbool.h>
14 #include <string.h>
15 #include <util.h>
16 #include <tee_api_types.h>
17 #include <tee/tee_cryp_utl.h>
18 #include <trace.h>
19 
20 /*
21  * Arm SMCCC TRNG firmware interface specification:
22  * https://developer.arm.com/documentation/den0098/
23  */
24 #define ARM_SMCCC_TRNG_VERSION		0x84000050
25 #define ARM_SMCCC_TRNG_FEATURES		0x84000051
26 #define ARM_SMCCC_TRNG_GET_UUID		0x84000052
27 #define ARM_SMCCC_TRNG_RND_32		0x84000053
28 #define ARM_SMCCC_TRNG_RND_64		0xc4000053
29 
30 #define ARM_SMCCC_RET_TRNG_SUCCESS		U(0)
31 #define ARM_SMCCC_RET_TRNG_NOT_SUPPORTED	((unsigned long)-1)
32 #define ARM_SMCCC_RET_TRNG_INVALID_PARAMETER	((unsigned long)-2)
33 #define ARM_SMCCC_RET_TRNG_NO_ENTROPY		((unsigned long)-3)
34 
35 #define TRNG_MAJOR_MASK		GENMASK_32(30, 16)
36 #define TRNG_MAJOR_SHIFT	16
37 #define TRNG_MINOR_MASK		GENMASK_32(15, 0)
38 #define TRNG_MINOR_SHIFT	0
39 #define TRNG_MAKE_VERSION(major, minor)	\
40 	((SHIFT_U32(major, TRNG_MAJOR_SHIFT) & TRNG_MAJOR_MASK) | \
41 	 (SHIFT_U32(minor, TRNG_MINOR_SHIFT) & TRNG_MINOR_MASK))
42 
43 #define TRNG_VERSION_1_0	TRNG_MAKE_VERSION(1, 0)
44 
45 #define TRNG_MAX_RND_64		(192 / 8)
46 #define TRNG_MAX_RND_32		(96 / 8)
47 
48 /* Function ID discovered for getting random bytes or 0 if not supported */
49 static uint32_t trng_rnd_fid;
50 
smccc_trng_is_supported(void)51 static bool smccc_trng_is_supported(void)
52 {
53 	struct thread_smc_args args = { };
54 	static bool inited;
55 
56 	if (inited)
57 		return trng_rnd_fid != 0;
58 
59 	inited = true;
60 
61 	/*
62 	 * TRNG ABI requires caller to check that Arm SMCCC version is
63 	 * larger or equal to v1.1
64 	 */
65 	args.a0 = ARM_SMCCC_VERSION;
66 	thread_smccc(&args);
67 	if (args.a0 & BIT32(31) || args.a0 < SMCCC_V_1_1)
68 		return false;
69 
70 	/*
71 	 * Check TRNG version, if successful we're guaranteed to have at least
72 	 * the ARM_SMCCC_TRNG_FEATURES fid.
73 	 */
74 	args.a0 = ARM_SMCCC_TRNG_VERSION;
75 	thread_smccc(&args);
76 	if (args.a0 & BIT32(31) || args.a0 < TRNG_VERSION_1_0)
77 		return false;
78 
79 #ifdef ARM64
80 	args.a0 = ARM_SMCCC_TRNG_FEATURES;
81 	args.a1 = ARM_SMCCC_TRNG_RND_64;
82 	thread_smccc(&args);
83 	if (args.a0 == ARM_SMCCC_RET_SUCCESS) {
84 		trng_rnd_fid = ARM_SMCCC_TRNG_RND_64;
85 		return true;
86 	}
87 #endif
88 
89 	args.a0 = ARM_SMCCC_TRNG_FEATURES;
90 	args.a1 = ARM_SMCCC_TRNG_RND_32;
91 	thread_smccc(&args);
92 	if (args.a0 == ARM_SMCCC_RET_TRNG_SUCCESS) {
93 		trng_rnd_fid = ARM_SMCCC_TRNG_RND_32;
94 		return true;
95 	}
96 
97 	return false;
98 }
99 
read_bytes(unsigned long val,size_t byte_count,uint8_t ** buf,size_t * rem)100 static void read_bytes(unsigned long val, size_t byte_count, uint8_t **buf,
101 		       size_t *rem)
102 {
103 	size_t count = MIN(byte_count, *rem);
104 	size_t n = 0;
105 
106 	for (n = 0; n < count; n++)
107 		(*buf)[n] = val >> (n * 8);
108 
109 	*buf += count;
110 	*rem -= count;
111 }
112 
read_samples(struct thread_smc_args * args,uint8_t * buf,size_t len)113 static void read_samples(struct thread_smc_args *args, uint8_t *buf, size_t len)
114 {
115 	uint8_t *ptr = buf;
116 	size_t rem = len;
117 	size_t byte_count = 4;
118 
119 #ifdef ARM64
120 	if (trng_rnd_fid == ARM_SMCCC_TRNG_RND_64)
121 		byte_count = 8;
122 #endif
123 
124 	read_bytes(args->a3, byte_count, &ptr, &rem);
125 	read_bytes(args->a2, byte_count, &ptr, &rem);
126 	read_bytes(args->a1, byte_count, &ptr, &rem);
127 }
128 
smccc_trng_read(void * buf,size_t len)129 static TEE_Result __maybe_unused smccc_trng_read(void *buf, size_t len)
130 {
131 	struct thread_smc_args args = { };
132 	uint8_t *ptr = buf;
133 	size_t rem = len;
134 	size_t max_burst = 0;
135 
136 	if (!smccc_trng_is_supported())
137 		return TEE_ERROR_NOT_SUPPORTED;
138 
139 	if (trng_rnd_fid == ARM_SMCCC_TRNG_RND_64)
140 		max_burst = TRNG_MAX_RND_64;
141 	else
142 		max_burst = TRNG_MAX_RND_32;
143 
144 	while (rem) {
145 		size_t burst = MIN(rem, max_burst);
146 
147 		args.a0 = trng_rnd_fid;
148 		args.a1 = burst * 8;
149 
150 		thread_smccc(&args);
151 
152 		switch (args.a0) {
153 		case ARM_SMCCC_RET_TRNG_SUCCESS:
154 			read_samples(&args, ptr, burst);
155 			rem -= burst;
156 			ptr += burst;
157 			break;
158 		case ARM_SMCCC_RET_TRNG_NO_ENTROPY:
159 			break;
160 		default:
161 			return TEE_ERROR_GENERIC;
162 		}
163 	}
164 
165 	return TEE_SUCCESS;
166 }
167 
smccc_trng_print_info(void)168 static void __maybe_unused smccc_trng_print_info(void)
169 {
170 	struct thread_smc_args args = { };
171 	unsigned int __maybe_unused major = 0;
172 	unsigned int __maybe_unused minor = 0;
173 
174 	if (!IS_ENABLED(CFG_TEE_CORE_DEBUG))
175 		return;
176 
177 	args.a0 = ARM_SMCCC_TRNG_VERSION;
178 	thread_smccc(&args);
179 	assert((args.a0 & BIT32(31)) == 0);
180 	major = (args.a0 & TRNG_MAJOR_MASK) >> TRNG_MAJOR_SHIFT;
181 	minor = (args.a0 & TRNG_MINOR_MASK) >> TRNG_MINOR_SHIFT;
182 
183 	args.a0 = ARM_SMCCC_TRNG_GET_UUID;
184 	thread_smccc(&args);
185 	assert(args.a0 != ARM_SMCCC_RET_TRNG_NOT_SUPPORTED);
186 
187 	DMSG("SMCCC TRNG v%u.%u, UUID %08lx-%04lx-%04lx-%04lx-%04lx%08lx\n",
188 	     major, minor, (unsigned long)args.a0, (unsigned long)args.a1 >> 16,
189 	     (unsigned long)args.a1 & GENMASK_32(16, 0),
190 	     (unsigned long)args.a2 >> 16,
191 	     (unsigned long)args.a2 & GENMASK_32(16, 0),
192 	     (unsigned long)args.a3);
193 }
194 
plat_rng_init(void)195 void plat_rng_init(void)
196 {
197 	if (!smccc_trng_is_supported())
198 		panic("SMCCC TRNG not supported");
199 
200 	smccc_trng_print_info();
201 
202 	if (IS_ENABLED(CFG_WITH_SOFTWARE_PRNG)) {
203 		/* If CFG_WITH_SOFTWARE_PRNG is enabled, seed PRNG with TRNG */
204 		uint8_t seed[32] = { 0 };
205 
206 		if (smccc_trng_read(seed, sizeof(seed)))
207 			panic("SMCCC TRNG not supported");
208 
209 		if (crypto_rng_init(seed, sizeof(seed)))
210 			panic();
211 	}
212 }
213 
214 /* If CFG_WITH_SOFTWARE_PRNG is disabled, TRNG is our HW RNG */
215 #ifndef CFG_WITH_SOFTWARE_PRNG
hw_get_random_bytes(void * buf,size_t len)216 TEE_Result hw_get_random_bytes(void *buf, size_t len)
217 {
218 	return smccc_trng_read(buf, len);
219 }
220 #endif
221