1 /*
2 * Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 #include <assert.h>
8
9 #include <common/debug.h>
10 #if MEASURED_BOOT
11 #include <common/desc_image_load.h>
12 #endif
13 #include <common/fdt_wrappers.h>
14
15 #include <lib/fconf/fconf.h>
16 #include <lib/fconf/fconf_dyn_cfg_getter.h>
17 #include <libfdt.h>
18
19 #include <plat/arm/common/arm_dyn_cfg_helpers.h>
20 #include <plat/arm/common/plat_arm.h>
21
22 #define DTB_PROP_MBEDTLS_HEAP_ADDR "mbedtls_heap_addr"
23 #define DTB_PROP_MBEDTLS_HEAP_SIZE "mbedtls_heap_size"
24
25 #if MEASURED_BOOT
26 #ifdef SPD_opteed
27 /*
28 * Currently OP-TEE does not support reading DTBs from Secure memory
29 * and this property should be removed when this feature is supported.
30 */
31 #define DTB_PROP_HW_SM_LOG_ADDR "tpm_event_log_sm_addr"
32 #endif /* SPD_opteed */
33 #define DTB_PROP_HW_LOG_ADDR "tpm_event_log_addr"
34 #define DTB_PROP_HW_LOG_SIZE "tpm_event_log_size"
35 #define DTB_PROP_HW_LOG_MAX_SIZE "tpm_event_log_max_size"
36 #endif /* MEASURED_BOOT */
37
38 static size_t event_log_max_size __unused;
39
40 /*******************************************************************************
41 * Validate the tb_fw_config is a valid DTB file and returns the node offset
42 * to "arm,tb_fw" property.
43 * Arguments:
44 * void *dtb - pointer to the TB_FW_CONFIG in memory
45 * int *node - Returns the node offset to "arm,tb_fw" property if found.
46 *
47 * Returns 0 on success and -1 on error.
48 ******************************************************************************/
arm_dyn_tb_fw_cfg_init(void * dtb,int * node)49 int arm_dyn_tb_fw_cfg_init(void *dtb, int *node)
50 {
51 assert(dtb != NULL);
52 assert(node != NULL);
53
54 /* Check if the pointer to DT is correct */
55 if (fdt_check_header(dtb) != 0) {
56 WARN("Invalid DTB file passed as%s\n", " TB_FW_CONFIG");
57 return -1;
58 }
59
60 /* Assert the node offset point to "arm,tb_fw" compatible property */
61 *node = fdt_node_offset_by_compatible(dtb, -1, "arm,tb_fw");
62 if (*node < 0) {
63 WARN("The compatible property '%s' not%s", "arm,tb_fw",
64 " found in the config\n");
65 return -1;
66 }
67
68 VERBOSE("Dyn cfg: '%s'%s", "arm,tb_fw", " found in the config\n");
69 return 0;
70 }
71
72 /*
73 * This function writes the Mbed TLS heap address and size in the DTB. When it
74 * is called, it is guaranteed that a DTB is available. However it is not
75 * guaranteed that the shared Mbed TLS heap implementation is used. Thus we
76 * return error code from here and it's the responsibility of the caller to
77 * determine the action upon error.
78 *
79 * This function is supposed to be called only by BL1.
80 *
81 * Returns:
82 * 0 = success
83 * -1 = error
84 */
arm_set_dtb_mbedtls_heap_info(void * dtb,void * heap_addr,size_t heap_size)85 int arm_set_dtb_mbedtls_heap_info(void *dtb, void *heap_addr, size_t heap_size)
86 {
87 int dtb_root;
88
89 /*
90 * Verify that the DTB is valid, before attempting to write to it,
91 * and get the DTB root node.
92 */
93 int err = arm_dyn_tb_fw_cfg_init(dtb, &dtb_root);
94 if (err < 0) {
95 ERROR("Invalid%s loaded. Unable to get root node\n",
96 " TB_FW_CONFIG");
97 return -1;
98 }
99
100 /*
101 * Write the heap address and size in the DTB.
102 *
103 * NOTE: The variables heap_addr and heap_size are corrupted
104 * by the "fdtw_write_inplace_cells" function. After the
105 * function calls they must NOT be reused.
106 */
107 err = fdtw_write_inplace_cells(dtb, dtb_root,
108 DTB_PROP_MBEDTLS_HEAP_ADDR, 2, &heap_addr);
109 if (err < 0) {
110 ERROR("%sDTB property '%s'\n",
111 "Unable to write ", DTB_PROP_MBEDTLS_HEAP_ADDR);
112 return -1;
113 }
114
115 err = fdtw_write_inplace_cells(dtb, dtb_root,
116 DTB_PROP_MBEDTLS_HEAP_SIZE, 1, &heap_size);
117 if (err < 0) {
118 ERROR("%sDTB property '%s'\n",
119 "Unable to write ", DTB_PROP_MBEDTLS_HEAP_SIZE);
120 return -1;
121 }
122
123 return 0;
124 }
125
126 #if MEASURED_BOOT
127 #if DICE_PROTECTION_ENVIRONMENT
128
129 #include <common/desc_image_load.h>
130
131 #define DTB_PROP_DPE_CTX_HANDLE "dpe_ctx_handle"
132
arm_set_dpe_context_handle(uintptr_t config_base,int * ctx_handle)133 static int arm_set_dpe_context_handle(uintptr_t config_base,
134 int *ctx_handle)
135 {
136 /* As libfdt uses void *, we can't avoid this cast */
137 void *dtb = (void *)config_base;
138 const char *compatible = "arm,dpe_ctx_handle";
139 int err, node;
140
141 /*
142 * Verify that the DTB is valid, before attempting to write to it,
143 * and get the DTB root node.
144 */
145
146 /* Check if the pointer to DT is correct */
147 err = fdt_check_header(dtb);
148 if (err < 0) {
149 WARN("Invalid DTB file passed\n");
150 return err;
151 }
152
153 /* Assert the node offset point to compatible property */
154 node = fdt_node_offset_by_compatible(dtb, -1, compatible);
155 if (node < 0) {
156 WARN("The compatible property '%s' not%s", compatible,
157 " found in the config\n");
158 return node;
159 }
160
161 VERBOSE("Dyn cfg: '%s'%s", compatible, " found in the config\n");
162
163 err = fdtw_write_inplace_cells(dtb, node,
164 DTB_PROP_DPE_CTX_HANDLE, 1, ctx_handle);
165 if (err < 0) {
166 ERROR("%sDTB property '%s'\n",
167 "Unable to write ", DTB_PROP_DPE_CTX_HANDLE);
168 } else {
169 /*
170 * Ensure that the info written to the DTB is visible
171 * to other images.
172 */
173 flush_dcache_range(config_base, fdt_totalsize(dtb));
174 }
175
176 return err;
177 }
178
179 /*
180 * This function writes the DPE context handle value to the NT_FW_CONFIG DTB.
181 *
182 * This function is supposed to be called only by BL2.
183 *
184 * Returns:
185 * 0 = success
186 * < 0 = error
187 */
arm_set_nt_fw_info(int * ctx_handle)188 int arm_set_nt_fw_info(int *ctx_handle)
189 {
190 uintptr_t config_base;
191 const bl_mem_params_node_t *cfg_mem_params;
192
193 /* Get the config load address and size from NT_FW_CONFIG */
194 cfg_mem_params = get_bl_mem_params_node(NT_FW_CONFIG_ID);
195 assert(cfg_mem_params != NULL);
196
197 config_base = cfg_mem_params->image_info.image_base;
198
199 /* Write the context handle value in the DTB */
200 return arm_set_dpe_context_handle(config_base, ctx_handle);
201 }
202
203 /*
204 * This function writes the DPE context handle value to the TB_FW_CONFIG DTB.
205 *
206 * This function is supposed to be called only by BL1.
207 *
208 * Returns:
209 * 0 = success
210 * < 0 = error
211 */
arm_set_tb_fw_info(int * ctx_handle)212 int arm_set_tb_fw_info(int *ctx_handle)
213 {
214 /*
215 * Read tb_fw_config device tree for Event Log properties
216 * and write the Event Log address and its size in the DTB
217 */
218 const struct dyn_cfg_dtb_info_t *tb_fw_config_info;
219 uintptr_t tb_fw_cfg_dtb;
220
221 tb_fw_config_info = FCONF_GET_PROPERTY(dyn_cfg, dtb, TB_FW_CONFIG_ID);
222 assert(tb_fw_config_info != NULL);
223
224 tb_fw_cfg_dtb = tb_fw_config_info->config_addr;
225
226 /* Write the context handle value in the DTB */
227 return arm_set_dpe_context_handle(tb_fw_cfg_dtb, ctx_handle);
228 }
229
230 /*
231 * This function reads the initial DPE context handle from TB_FW_CONFIG DTB.
232 *
233 * This function is supposed to be called only by BL2.
234 *
235 * Returns:
236 * 0 = success
237 * < 0 = error
238 */
239
arm_get_tb_fw_info(int * ctx_handle)240 int arm_get_tb_fw_info(int *ctx_handle)
241 {
242 /* As libfdt uses void *, we can't avoid this cast */
243 const struct dyn_cfg_dtb_info_t *tb_fw_config_info;
244 int node, rc;
245
246 tb_fw_config_info = FCONF_GET_PROPERTY(dyn_cfg, dtb, TB_FW_CONFIG_ID);
247 assert(tb_fw_config_info != NULL);
248
249 void *dtb = (void *)tb_fw_config_info->config_addr;
250 const char *compatible = "arm,dpe_ctx_handle";
251
252 /* Assert the node offset point to compatible property */
253 node = fdt_node_offset_by_compatible(dtb, -1, compatible);
254 if (node < 0) {
255 WARN("The compatible property '%s'%s", compatible,
256 " not specified in TB_FW config.\n");
257 return node;
258 }
259
260 VERBOSE("Dyn cfg: '%s'%s", compatible, " found in the config\n");
261
262 rc = fdt_read_uint32(dtb, node, DTB_PROP_DPE_CTX_HANDLE, (uint32_t *)ctx_handle);
263 if (rc != 0) {
264 ERROR("%s%s", DTB_PROP_DPE_CTX_HANDLE,
265 " not specified in TB_FW config.\n");
266 }
267
268 return rc;
269 }
270 #else
271 /*
272 * Write the Event Log address and its size in the DTB.
273 *
274 * Returns:
275 * 0 = success
276 * < 0 = error
277 */
arm_set_event_log_info(uintptr_t config_base,uintptr_t sm_log_addr,uintptr_t log_addr,size_t log_size)278 static int arm_set_event_log_info(uintptr_t config_base,
279 #ifdef SPD_opteed
280 uintptr_t sm_log_addr,
281 #endif
282 uintptr_t log_addr, size_t log_size)
283 {
284 /* As libfdt uses void *, we can't avoid this cast */
285 void *dtb = (void *)config_base;
286 const char *compatible = "arm,tpm_event_log";
287 int err, node;
288
289 /*
290 * Verify that the DTB is valid, before attempting to write to it,
291 * and get the DTB root node.
292 */
293
294 /* Check if the pointer to DT is correct */
295 err = fdt_check_header(dtb);
296 if (err < 0) {
297 WARN("Invalid DTB file passed\n");
298 return err;
299 }
300
301 /* Assert the node offset point to compatible property */
302 node = fdt_node_offset_by_compatible(dtb, -1, compatible);
303 if (node < 0) {
304 WARN("The compatible property '%s' not%s", compatible,
305 " found in the config\n");
306 return node;
307 }
308
309 VERBOSE("Dyn cfg: '%s'%s", compatible, " found in the config\n");
310
311 #ifdef SPD_opteed
312 if (sm_log_addr != 0UL) {
313 err = fdtw_write_inplace_cells(dtb, node,
314 DTB_PROP_HW_SM_LOG_ADDR, 2, &sm_log_addr);
315 if (err < 0) {
316 ERROR("%sDTB property '%s'\n",
317 "Unable to write ", DTB_PROP_HW_SM_LOG_ADDR);
318 return err;
319 }
320 }
321 #endif
322 err = fdtw_write_inplace_cells(dtb, node,
323 DTB_PROP_HW_LOG_ADDR, 2, &log_addr);
324 if (err < 0) {
325 ERROR("%sDTB property '%s'\n",
326 "Unable to write ", DTB_PROP_HW_LOG_ADDR);
327 return err;
328 }
329
330 assert(event_log_max_size != 0U);
331 err = fdtw_write_inplace_cells(dtb, node,
332 DTB_PROP_HW_LOG_MAX_SIZE, 1,
333 &event_log_max_size);
334 if (err < 0) {
335 ERROR("%sDTB property '%s'\n",
336 "Unable to write ", DTB_PROP_HW_LOG_MAX_SIZE);
337 return err;
338 }
339
340 err = fdtw_write_inplace_cells(dtb, node,
341 DTB_PROP_HW_LOG_SIZE, 1, &log_size);
342 if (err < 0) {
343 ERROR("%sDTB property '%s'\n",
344 "Unable to write ", DTB_PROP_HW_LOG_SIZE);
345 } else {
346 /*
347 * Ensure that the info written to the DTB is visible
348 * to other images.
349 */
350 flush_dcache_range(config_base, fdt_totalsize(dtb));
351 }
352
353 return err;
354 }
355
356 /*
357 * This function writes the Event Log address and its size
358 * in the TOS_FW_CONFIG DTB.
359 *
360 * This function is supposed to be called only by BL2.
361 *
362 * Returns:
363 * 0 = success
364 * < 0 = error
365 */
arm_set_tos_fw_info(uintptr_t log_addr,size_t log_size)366 int arm_set_tos_fw_info(uintptr_t log_addr, size_t log_size)
367 {
368 uintptr_t config_base;
369 const bl_mem_params_node_t *cfg_mem_params;
370 int err;
371
372 assert(log_addr != 0UL);
373
374 /* Get the config load address and size of TOS_FW_CONFIG */
375 cfg_mem_params = get_bl_mem_params_node(TOS_FW_CONFIG_ID);
376 assert(cfg_mem_params != NULL);
377
378 config_base = cfg_mem_params->image_info.image_base;
379
380 /* Write the Event Log address and its size in the DTB */
381 err = arm_set_event_log_info(config_base,
382 #ifdef SPD_opteed
383 0UL,
384 #endif
385 log_addr, log_size);
386 if (err < 0) {
387 ERROR("%sEvent Log data to TOS_FW_CONFIG\n",
388 "Unable to write ");
389 }
390
391 return err;
392 }
393
394 /*
395 * This function writes the Event Log address and its size
396 * in the NT_FW_CONFIG DTB.
397 *
398 * This function is supposed to be called only by BL2.
399 *
400 * Returns:
401 * 0 = success
402 * < 0 = error
403 */
arm_set_nt_fw_info(uintptr_t log_addr,size_t log_size,uintptr_t * ns_log_addr)404 int arm_set_nt_fw_info(
405 #ifdef SPD_opteed
406 uintptr_t log_addr,
407 #endif
408 size_t log_size, uintptr_t *ns_log_addr)
409 {
410 uintptr_t config_base;
411 uintptr_t ns_addr;
412 const bl_mem_params_node_t *cfg_mem_params;
413 int err;
414
415 assert(ns_log_addr != NULL);
416
417 /* Get the config load address and size from NT_FW_CONFIG */
418 cfg_mem_params = get_bl_mem_params_node(NT_FW_CONFIG_ID);
419 assert(cfg_mem_params != NULL);
420
421 config_base = cfg_mem_params->image_info.image_base;
422
423 /* Calculate Event Log address in Non-secure memory */
424 ns_addr = cfg_mem_params->image_info.image_base +
425 cfg_mem_params->image_info.image_max_size;
426
427 /* Check for memory space */
428 if ((uint64_t)(ns_addr + log_size) > ARM_NS_DRAM1_END) {
429 return -1;
430 }
431
432 /* Write the Event Log address and its size in the DTB */
433 err = arm_set_event_log_info(config_base,
434 #ifdef SPD_opteed
435 log_addr,
436 #endif
437 ns_addr, log_size);
438
439 /* Return Event Log address in Non-secure memory */
440 *ns_log_addr = (err < 0) ? 0UL : ns_addr;
441 return err;
442 }
443
444 /*
445 * This function writes the Event Log address and its size
446 * in the TB_FW_CONFIG DTB.
447 *
448 * This function is supposed to be called only by BL1.
449 *
450 * Returns:
451 * 0 = success
452 * < 0 = error
453 */
arm_set_tb_fw_info(uintptr_t log_addr,size_t log_size,size_t log_max_size)454 int arm_set_tb_fw_info(uintptr_t log_addr, size_t log_size, size_t log_max_size)
455 {
456 /*
457 * Read tb_fw_config device tree for Event Log properties
458 * and write the Event Log address and its size in the DTB
459 */
460 const struct dyn_cfg_dtb_info_t *tb_fw_config_info;
461 uintptr_t tb_fw_cfg_dtb;
462 int err;
463
464 tb_fw_config_info = FCONF_GET_PROPERTY(dyn_cfg, dtb, TB_FW_CONFIG_ID);
465 assert(tb_fw_config_info != NULL);
466
467 tb_fw_cfg_dtb = tb_fw_config_info->config_addr;
468
469 event_log_max_size = log_max_size;
470
471 err = arm_set_event_log_info(tb_fw_cfg_dtb,
472 #ifdef SPD_opteed
473 0UL,
474 #endif
475 log_addr, log_size);
476 return err;
477 }
478
479 /*
480 * This function reads the Event Log address and its size
481 * properties present in TB_FW_CONFIG DTB.
482 *
483 * This function is supposed to be called only by BL2.
484 *
485 * Returns:
486 * 0 = success
487 * < 0 = error
488 * Alongside returns Event Log address and its size.
489 */
490
arm_get_tb_fw_info(uint64_t * log_addr,size_t * log_size,size_t * log_max_size)491 int arm_get_tb_fw_info(uint64_t *log_addr, size_t *log_size,
492 size_t *log_max_size)
493 {
494 /* As libfdt uses void *, we can't avoid this cast */
495 const struct dyn_cfg_dtb_info_t *tb_fw_config_info;
496 int node, rc;
497
498 tb_fw_config_info = FCONF_GET_PROPERTY(dyn_cfg, dtb, TB_FW_CONFIG_ID);
499 assert(tb_fw_config_info != NULL);
500
501 void *dtb = (void *)tb_fw_config_info->config_addr;
502 const char *compatible = "arm,tpm_event_log";
503
504 /* Assert the node offset point to compatible property */
505 node = fdt_node_offset_by_compatible(dtb, -1, compatible);
506 if (node < 0) {
507 WARN("The compatible property '%s'%s", compatible,
508 " not specified in TB_FW config.\n");
509 return node;
510 }
511
512 VERBOSE("Dyn cfg: '%s'%s", compatible, " found in the config\n");
513
514 rc = fdt_read_uint64(dtb, node, DTB_PROP_HW_LOG_ADDR, log_addr);
515 if (rc != 0) {
516 ERROR("%s%s", DTB_PROP_HW_LOG_ADDR,
517 " not specified in TB_FW config.\n");
518 return rc;
519 }
520
521 rc = fdt_read_uint32(dtb, node, DTB_PROP_HW_LOG_SIZE, (uint32_t *)log_size);
522 if (rc != 0) {
523 ERROR("%s%s", DTB_PROP_HW_LOG_SIZE,
524 " not specified in TB_FW config.\n");
525 return rc;
526 }
527
528 rc = fdt_read_uint32(dtb, node, DTB_PROP_HW_LOG_MAX_SIZE,
529 (uint32_t *)log_max_size);
530 if (rc != 0) {
531 ERROR("%s%s", DTB_PROP_HW_LOG_MAX_SIZE,
532 " not specified in TB_FW config.\n");
533 return rc;
534 } else {
535 event_log_max_size = *log_max_size;
536 }
537
538 return rc;
539 }
540 #endif /* DICE_PROTECTION_ENVIRONMENT */
541 #endif /* MEASURED_BOOT */
542