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