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