1 // SPDX-License-Identifier: BSD-2-Clause
2 /**
3 * Copyright 2017-2021, 2024 NXP
4 *
5 * Brief CAAM Random Number Generator manager.
6 * Implementation of RNG functions.
7 */
8 #include <atomic.h>
9 #include <caam_common.h>
10 #include <caam_hal_rng.h>
11 #include <caam_jr.h>
12 #include <caam_rng.h>
13 #include <caam_utils_mem.h>
14 #include <caam_utils_status.h>
15 #include <crypto/crypto.h>
16 #include <kernel/panic.h>
17 #include <mm/core_memprot.h>
18 #include <rng_support.h>
19 #include <tee/cache.h>
20 #include <tee/tee_cryp_utl.h>
21 #include <string.h>
22
23 /*
24 * Define the number of descriptor entry to generate random data
25 */
26 #define RNG_GEN_DESC_ENTRIES 5
27
28 /*
29 * RNG module private data
30 */
31 struct rng_privdata {
32 vaddr_t baseaddr; /* RNG base address */
33 bool instantiated; /* RNG instantiated */
34 bool pr_enabled; /* RNG prediction resistance */
35 };
36
37 static struct rng_privdata *rng_privdata;
38
39 /* Allocate and initialize module private data */
do_allocate(void)40 static enum caam_status do_allocate(void)
41 {
42 /* Allocate the Module resources */
43 rng_privdata = caam_calloc(sizeof(*rng_privdata));
44 if (!rng_privdata) {
45 RNG_TRACE("Private Data allocation error");
46 return CAAM_OUT_MEMORY;
47 }
48
49 rng_privdata->instantiated = false;
50
51 return CAAM_NO_ERROR;
52 }
53
54 /* Free module private data */
do_free(void)55 static void do_free(void)
56 {
57 caam_free(rng_privdata);
58 rng_privdata = NULL;
59 }
60
61 #ifdef CFG_NXP_CAAM_RNG_DRV
62 /*
63 * Return the requested random data
64 *
65 * @buf [out] data buffer
66 * @len number of bytes to returns
67 */
do_rng_read(uint8_t * buf,size_t len)68 static TEE_Result do_rng_read(uint8_t *buf, size_t len)
69 {
70 TEE_Result ret = TEE_ERROR_GENERIC;
71 uint32_t *desc = NULL;
72 uint32_t op = RNG_GEN_DATA;
73 void *rng_data = NULL;
74 paddr_t paddr = 0;
75 struct caam_jobctx jobctx = { };
76
77 if (!rng_privdata) {
78 RNG_TRACE("RNG Driver not initialized");
79 return TEE_ERROR_BAD_STATE;
80 }
81
82 if (!rng_privdata->instantiated) {
83 RNG_TRACE("RNG Driver not initialized");
84 return TEE_ERROR_BAD_STATE;
85 }
86
87 rng_data = caam_calloc_align(len);
88 if (!rng_data) {
89 RNG_TRACE("RNG buffer allocation failed");
90 return TEE_ERROR_OUT_OF_MEMORY;
91 }
92
93 /* Ensure that data buffer is visible from the HW */
94 cache_operation(TEE_CACHEFLUSH, rng_data, len);
95
96 /* Convert the buffer virtual address to physical address */
97 paddr = virt_to_phys(rng_data);
98 if (!paddr) {
99 RNG_TRACE("Virtual/Physical conversion failed");
100 goto exit;
101 }
102
103 desc = caam_calloc_desc(RNG_GEN_DESC_ENTRIES);
104 if (!desc) {
105 RNG_TRACE("Descriptor allocation failed");
106 ret = TEE_ERROR_OUT_OF_MEMORY;
107 goto exit;
108 }
109
110 if (IS_ENABLED(CFG_CAAM_RNG_RUNTIME_PR) && rng_privdata->pr_enabled)
111 op |= ALGO_RNG_PR;
112
113 caam_desc_init(desc);
114 caam_desc_add_word(desc, DESC_HEADER(0));
115 caam_desc_add_word(desc, op);
116 caam_desc_add_word(desc, FIFO_ST(CLASS_NO, RNG_TO_MEM, len));
117 caam_desc_add_ptr(desc, paddr);
118
119 jobctx.desc = desc;
120 RNG_DUMPDESC(desc);
121
122 if (!caam_jr_enqueue(&jobctx, NULL)) {
123 cache_operation(TEE_CACHEINVALIDATE, rng_data, len);
124 memcpy(buf, rng_data, len);
125 ret = TEE_SUCCESS;
126 } else {
127 RNG_TRACE("CAAM Status 0x%08" PRIx32, jobctx.status);
128 ret = job_status_to_tee_result(jobctx.status);
129 }
130
131 exit:
132 caam_free(rng_data);
133 caam_free_desc(&desc);
134 return ret;
135 }
136 #endif /* CFG_NXP_CAAM_RNG_DRV */
137
138 /*
139 * Prepares the instantiation descriptor
140 *
141 * @nb_sh Number of the State Handle
142 * @sh_status State Handles status
143 * @desc Reference to the descriptor
144 * @desc [out] Descriptor filled
145 */
prepare_inst_desc(uint32_t nb_sh,uint32_t sh_status,uint32_t * desc)146 static void prepare_inst_desc(uint32_t nb_sh, uint32_t sh_status,
147 uint32_t *desc)
148 {
149 bool key_loaded = false;
150 unsigned int sh_idx = 0;
151 unsigned int nb_max_sh = nb_sh;
152
153 /* Read the SH and secure key status */
154 key_loaded = caam_hal_rng_key_loaded(rng_privdata->baseaddr);
155 RNG_TRACE("RNG SH Status 0x%08" PRIx32 " - Key Status %" PRId8,
156 sh_status, key_loaded);
157
158 while (sh_status & BIT(sh_idx))
159 sh_idx++;
160
161 RNG_TRACE("Instantiation start at SH%" PRIu32 " (%" PRIu32 ")", sh_idx,
162 nb_max_sh);
163
164 /* Don't set the descriptor header now */
165 caam_desc_init(desc);
166 caam_desc_add_word(desc, DESC_HEADER(0));
167 /* First State Handle to instantiate */
168 caam_desc_add_word(desc, RNG_SH_INST(sh_idx));
169
170 /* Next State Handles */
171 for (sh_idx++; sh_idx < nb_max_sh; sh_idx++) {
172 if (!(sh_status & BIT(sh_idx))) {
173 /*
174 * If there is more SH to instantiate, add a wait loop
175 * followed by a reset of the done status to execute
176 * next command
177 */
178 caam_desc_add_word(desc,
179 JUMP_C1_LOCAL(ALL_COND_TRUE,
180 JMP_COND(NONE), 1));
181 caam_desc_add_word(desc,
182 LD_NOCLASS_IMM(REG_CLEAR_WRITTEN,
183 sizeof(uint32_t)));
184 caam_desc_add_word(desc, 0x1);
185 caam_desc_add_word(desc, RNG_SH_INST(sh_idx));
186 }
187 }
188
189 /* Load the Key if needed */
190 if (!key_loaded) {
191 /*
192 * Add a wait loop while previous operation not completed,
193 * followed by a register clear before executing next command
194 */
195 caam_desc_add_word(desc, JUMP_C1_LOCAL(ALL_COND_TRUE,
196 JMP_COND(NONE), 1));
197 caam_desc_add_word(desc, LD_NOCLASS_IMM(REG_CLEAR_WRITTEN,
198 sizeof(uint32_t)));
199 caam_desc_add_word(desc, 0x1);
200 caam_desc_add_word(desc, RNG_GEN_SECKEYS);
201 }
202
203 RNG_DUMPDESC(desc);
204 }
205
caam_rng_instantiation(void)206 enum caam_status caam_rng_instantiation(void)
207 {
208 enum caam_status retstatus = CAAM_FAILURE;
209 struct caam_jobctx jobctx = {};
210 uint32_t *desc = NULL;
211 uint32_t sh_status = 0;
212 uint32_t nb_sh = 0;
213 uint32_t sh_mask = 0;
214 uint32_t inc_delay = 0;
215
216 RNG_TRACE("RNG Instantation");
217
218 /* Check if RNG is already instantiated */
219 retstatus = caam_hal_rng_instantiated(rng_privdata->baseaddr);
220
221 /* RNG is already instantiated or an error occurred */
222 if (retstatus != CAAM_NOT_INIT)
223 goto end_inst;
224
225 /*
226 * RNG needs to be instantiated. Allocate and prepare the
227 * Job Descriptor
228 */
229
230 /* Calculate the State Handles bit mask */
231 nb_sh = caam_hal_rng_get_nb_sh(rng_privdata->baseaddr);
232 sh_mask = GENMASK_32(nb_sh - 1, 0);
233
234 /*
235 * The maximum size of the descriptor is:
236 * |----------------------|
237 * | Header | = 1
238 * |----------------------|
239 * | First instantation | = 1
240 * |----------------------|-------------------------
241 * | wait complete | = 1
242 * |----------------------|
243 * | Clear done status | Repeat (nb_sh - 1)
244 * | | = 2
245 * |----------------------|
246 * | next SH instantation | = 1
247 * |----------------------|-------------------------
248 * | wait complete | = 1
249 * |----------------------|
250 * | Clear done status | = 2
251 * | |
252 * |----------------------|
253 * | Generate Secure Keys | = 1
254 * |----------------------|
255 * | Pad with a 0 | = 1
256 */
257 desc = caam_calloc_desc(2 + (nb_sh - 1) * 4 + 4 + 1);
258 if (!desc) {
259 RNG_TRACE("Descriptor Allocation error");
260 retstatus = CAAM_OUT_MEMORY;
261 goto end_inst;
262 }
263
264 jobctx.desc = desc;
265
266 do {
267 /* Check if all State Handles are instantiated */
268 sh_status = caam_hal_rng_get_sh_status(rng_privdata->baseaddr);
269 if ((sh_status & sh_mask) == sh_mask) {
270 RNG_TRACE("RNG All SH are instantiated (0x%08" PRIx32
271 ")",
272 sh_status);
273 retstatus = CAAM_NO_ERROR;
274 goto end_inst;
275 }
276
277 if (sh_status == 0) {
278 retstatus = caam_hal_rng_kick(rng_privdata->baseaddr,
279 inc_delay);
280 RNG_TRACE("RNG Kick (inc=%" PRIu32 ") ret 0x%08x",
281 inc_delay, retstatus);
282 if (retstatus != CAAM_NO_ERROR) {
283 retstatus = CAAM_FAILURE;
284 goto end_inst;
285 }
286 inc_delay += 200;
287 }
288
289 prepare_inst_desc(nb_sh, sh_status, desc);
290
291 retstatus = caam_jr_enqueue(&jobctx, NULL);
292 RNG_TRACE("RNG Job returned 0x%08x", retstatus);
293
294 if (retstatus != CAAM_NO_ERROR &&
295 retstatus != CAAM_JOB_STATUS)
296 goto end_inst;
297
298 if (retstatus == CAAM_JOB_STATUS) {
299 RNG_TRACE("RNG Job status 0x%08" PRIx32, jobctx.status);
300 if ((JRSTA_SRC_GET(jobctx.status) != JRSTA_SRC(CCB)) ||
301 (JRSTA_CCB_GET_ERR(jobctx.status) !=
302 (JRSTA_CCB_CHAID_RNG | JRSTA_CCB_ERRID_HW)))
303 retstatus = CAAM_FAILURE;
304 else
305 retstatus = CAAM_NO_ERROR;
306 }
307 } while (retstatus == CAAM_NO_ERROR);
308
309 end_inst:
310 if (retstatus == CAAM_NO_ERROR) {
311 rng_privdata->instantiated = true;
312 rng_privdata->pr_enabled =
313 caam_hal_rng_pr_enabled(rng_privdata->baseaddr) &
314 IS_ENABLED(CFG_CAAM_RNG_RUNTIME_PR);
315
316 RNG_TRACE("RNG prediction resistance is %sabled",
317 rng_privdata->pr_enabled ? "en" : "dis");
318 }
319
320 caam_free_desc(&desc);
321
322 RNG_TRACE("RNG Instantiation return 0x%08x", retstatus);
323
324 return retstatus;
325 }
326
caam_rng_init(vaddr_t ctrl_addr)327 enum caam_status caam_rng_init(vaddr_t ctrl_addr)
328 {
329 enum caam_status retstatus = CAAM_FAILURE;
330
331 RNG_TRACE("Initialization");
332 retstatus = do_allocate();
333 if (retstatus == CAAM_NO_ERROR) {
334 rng_privdata->baseaddr = ctrl_addr;
335 retstatus = caam_rng_instantiation();
336 }
337
338 if (retstatus != CAAM_NO_ERROR)
339 do_free();
340
341 return retstatus;
342 }
343
344 #ifdef CFG_NXP_CAAM_RNG_DRV
345 #ifdef CFG_WITH_SOFTWARE_PRNG
plat_rng_init(void)346 void plat_rng_init(void)
347 {
348 TEE_Result res = TEE_SUCCESS;
349 uint8_t buf[64] = { };
350
351 res = do_rng_read(buf, sizeof(buf));
352 if (res) {
353 EMSG("Failed to read RNG: %#" PRIx32, res);
354 panic();
355 }
356
357 res = crypto_rng_init(buf, sizeof(buf));
358 if (res) {
359 EMSG("Failed to initialize RNG: %#" PRIx32, res);
360 panic();
361 }
362
363 RNG_TRACE("PRNG seeded from CAAM");
364 }
365 #else /* !CFG_WITH_SOFTWARE_PRNG */
hw_get_random_bytes(void * buf,size_t blen)366 TEE_Result hw_get_random_bytes(void *buf, size_t blen)
367 {
368 if (!buf)
369 return TEE_ERROR_BAD_PARAMETERS;
370
371 return do_rng_read(buf, blen);
372 }
373
plat_rng_init(void)374 void plat_rng_init(void)
375 {
376 }
377 #endif /* CFG_WITH_SOFTWARE_PRNG */
378 #endif /* CFG_NXP_CAAM_RNG_DRV */
379