1 /*
2  * Copyright (c) 2018-2023, 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 /*
128  * Write the Event Log address and its size in the DTB.
129  *
130  * Returns:
131  *	0 = success
132  *    < 0 = error
133  */
arm_set_event_log_info(uintptr_t config_base,uintptr_t sm_log_addr,uintptr_t log_addr,size_t log_size)134 static int arm_set_event_log_info(uintptr_t config_base,
135 #ifdef SPD_opteed
136 				  uintptr_t sm_log_addr,
137 #endif
138 				  uintptr_t log_addr, size_t log_size)
139 {
140 	/* As libfdt uses void *, we can't avoid this cast */
141 	void *dtb = (void *)config_base;
142 	const char *compatible = "arm,tpm_event_log";
143 	int err, node;
144 
145 	/*
146 	 * Verify that the DTB is valid, before attempting to write to it,
147 	 * and get the DTB root node.
148 	 */
149 
150 	/* Check if the pointer to DT is correct */
151 	err = fdt_check_header(dtb);
152 	if (err < 0) {
153 		WARN("Invalid DTB file passed\n");
154 		return err;
155 	}
156 
157 	/* Assert the node offset point to compatible property */
158 	node = fdt_node_offset_by_compatible(dtb, -1, compatible);
159 	if (node < 0) {
160 		WARN("The compatible property '%s' not%s", compatible,
161 			" found in the config\n");
162 		return node;
163 	}
164 
165 	VERBOSE("Dyn cfg: '%s'%s", compatible, " found in the config\n");
166 
167 #ifdef SPD_opteed
168 	if (sm_log_addr != 0UL) {
169 		err = fdtw_write_inplace_cells(dtb, node,
170 			DTB_PROP_HW_SM_LOG_ADDR, 2, &sm_log_addr);
171 		if (err < 0) {
172 			ERROR("%sDTB property '%s'\n",
173 				"Unable to write ", DTB_PROP_HW_SM_LOG_ADDR);
174 			return err;
175 		}
176 	}
177 #endif
178 	err = fdtw_write_inplace_cells(dtb, node,
179 		DTB_PROP_HW_LOG_ADDR, 2, &log_addr);
180 	if (err < 0) {
181 		ERROR("%sDTB property '%s'\n",
182 			"Unable to write ", DTB_PROP_HW_LOG_ADDR);
183 		return err;
184 	}
185 
186 	assert(event_log_max_size != 0U);
187 	err = fdtw_write_inplace_cells(dtb, node,
188 				       DTB_PROP_HW_LOG_MAX_SIZE, 1,
189 				       &event_log_max_size);
190 	if (err < 0) {
191 		ERROR("%sDTB property '%s'\n",
192 		      "Unable to write ", DTB_PROP_HW_LOG_MAX_SIZE);
193 		return err;
194 	}
195 
196 	err = fdtw_write_inplace_cells(dtb, node,
197 		DTB_PROP_HW_LOG_SIZE, 1, &log_size);
198 	if (err < 0) {
199 		ERROR("%sDTB property '%s'\n",
200 			"Unable to write ", DTB_PROP_HW_LOG_SIZE);
201 	} else {
202 		/*
203 		 * Ensure that the info written to the DTB is visible
204 		 * to other images.
205 		 */
206 		flush_dcache_range(config_base, fdt_totalsize(dtb));
207 	}
208 
209 	return err;
210 }
211 
212 /*
213  * This function writes the Event Log address and its size
214  * in the TOS_FW_CONFIG DTB.
215  *
216  * This function is supposed to be called only by BL2.
217  *
218  * Returns:
219  *	0 = success
220  *    < 0 = error
221  */
arm_set_tos_fw_info(uintptr_t log_addr,size_t log_size)222 int arm_set_tos_fw_info(uintptr_t log_addr, size_t log_size)
223 {
224 	uintptr_t config_base;
225 	const bl_mem_params_node_t *cfg_mem_params;
226 	int err;
227 
228 	assert(log_addr != 0UL);
229 
230 	/* Get the config load address and size of TOS_FW_CONFIG */
231 	cfg_mem_params = get_bl_mem_params_node(TOS_FW_CONFIG_ID);
232 	assert(cfg_mem_params != NULL);
233 
234 	config_base = cfg_mem_params->image_info.image_base;
235 
236 	/* Write the Event Log address and its size in the DTB */
237 	err = arm_set_event_log_info(config_base,
238 #ifdef SPD_opteed
239 					0UL,
240 #endif
241 					log_addr, log_size);
242 	if (err < 0) {
243 		ERROR("%sEvent Log data to TOS_FW_CONFIG\n",
244 					"Unable to write ");
245 	}
246 
247 	return err;
248 }
249 
250 /*
251  * This function writes the Event Log address and its size
252  * in the NT_FW_CONFIG DTB.
253  *
254  * This function is supposed to be called only by BL2.
255  *
256  * Returns:
257  *	0 = success
258  *    < 0 = error
259  */
arm_set_nt_fw_info(uintptr_t log_addr,size_t log_size,uintptr_t * ns_log_addr)260 int arm_set_nt_fw_info(
261 #ifdef SPD_opteed
262 			uintptr_t log_addr,
263 #endif
264 			size_t log_size, uintptr_t *ns_log_addr)
265 {
266 	uintptr_t config_base;
267 	uintptr_t ns_addr;
268 	const bl_mem_params_node_t *cfg_mem_params;
269 	int err;
270 
271 	assert(ns_log_addr != NULL);
272 
273 	/* Get the config load address and size from NT_FW_CONFIG */
274 	cfg_mem_params = get_bl_mem_params_node(NT_FW_CONFIG_ID);
275 	assert(cfg_mem_params != NULL);
276 
277 	config_base = cfg_mem_params->image_info.image_base;
278 
279 	/* Calculate Event Log address in Non-secure memory */
280 	ns_addr = cfg_mem_params->image_info.image_base +
281 			cfg_mem_params->image_info.image_max_size;
282 
283 	/* Check for memory space */
284 	if ((uint64_t)(ns_addr + log_size) > ARM_NS_DRAM1_END) {
285 		return -1;
286 	}
287 
288 	/* Write the Event Log address and its size in the DTB */
289 	err = arm_set_event_log_info(config_base,
290 #ifdef SPD_opteed
291 					log_addr,
292 #endif
293 					ns_addr, log_size);
294 
295 	/* Return Event Log address in Non-secure memory */
296 	*ns_log_addr = (err < 0) ? 0UL : ns_addr;
297 	return err;
298 }
299 
300 /*
301  * This function writes the Event Log address and its size
302  * in the TB_FW_CONFIG DTB.
303  *
304  * This function is supposed to be called only by BL1.
305  *
306  * Returns:
307  *     0 = success
308  *   < 0 = error
309  */
arm_set_tb_fw_info(uintptr_t log_addr,size_t log_size,size_t log_max_size)310 int arm_set_tb_fw_info(uintptr_t log_addr, size_t log_size, size_t log_max_size)
311 {
312 	/*
313 	 * Read tb_fw_config device tree for Event Log properties
314 	 * and write the Event Log address and its size in the DTB
315 	 */
316 	const struct dyn_cfg_dtb_info_t *tb_fw_config_info;
317 	uintptr_t tb_fw_cfg_dtb;
318 	int err;
319 
320 	tb_fw_config_info = FCONF_GET_PROPERTY(dyn_cfg, dtb, TB_FW_CONFIG_ID);
321 	assert(tb_fw_config_info != NULL);
322 
323 	tb_fw_cfg_dtb = tb_fw_config_info->config_addr;
324 
325 	event_log_max_size = log_max_size;
326 
327 	err = arm_set_event_log_info(tb_fw_cfg_dtb,
328 #ifdef SPD_opteed
329 				     0UL,
330 #endif
331 				     log_addr, log_size);
332 	return err;
333 }
334 
335 /*
336  * This function reads the Event Log address and its size
337  * properties present in TB_FW_CONFIG DTB.
338  *
339  * This function is supposed to be called only by BL2.
340  *
341  * Returns:
342  *     0 = success
343  *   < 0 = error
344  * Alongside returns Event Log address and its size.
345  */
346 
arm_get_tb_fw_info(uint64_t * log_addr,size_t * log_size,size_t * log_max_size)347 int arm_get_tb_fw_info(uint64_t *log_addr, size_t *log_size,
348 		       size_t *log_max_size)
349 {
350 	/* As libfdt uses void *, we can't avoid this cast */
351 	const struct dyn_cfg_dtb_info_t *tb_fw_config_info;
352 	int node, rc;
353 
354 	tb_fw_config_info = FCONF_GET_PROPERTY(dyn_cfg, dtb, TB_FW_CONFIG_ID);
355 	assert(tb_fw_config_info != NULL);
356 
357 	void *dtb = (void *)tb_fw_config_info->config_addr;
358 	const char *compatible = "arm,tpm_event_log";
359 
360 	/* Assert the node offset point to compatible property */
361 	node = fdt_node_offset_by_compatible(dtb, -1, compatible);
362 	if (node < 0) {
363 		WARN("The compatible property '%s'%s", compatible,
364 		     " not specified in TB_FW config.\n");
365 		return node;
366 	}
367 
368 	VERBOSE("Dyn cfg: '%s'%s", compatible, " found in the config\n");
369 
370 	rc = fdt_read_uint64(dtb, node, DTB_PROP_HW_LOG_ADDR, log_addr);
371 	if (rc != 0) {
372 		ERROR("%s%s", DTB_PROP_HW_LOG_ADDR,
373 		      " not specified in TB_FW config.\n");
374 		return rc;
375 	}
376 
377 	rc = fdt_read_uint32(dtb, node, DTB_PROP_HW_LOG_SIZE, (uint32_t *)log_size);
378 	if (rc != 0) {
379 		ERROR("%s%s", DTB_PROP_HW_LOG_SIZE,
380 		      " not specified in TB_FW config.\n");
381 		return rc;
382 	}
383 
384 	rc = fdt_read_uint32(dtb, node, DTB_PROP_HW_LOG_MAX_SIZE,
385 			     (uint32_t *)log_max_size);
386 	if (rc != 0) {
387 		ERROR("%s%s", DTB_PROP_HW_LOG_MAX_SIZE,
388 		      " not specified in TB_FW config.\n");
389 		return rc;
390 	} else {
391 		event_log_max_size = *log_max_size;
392 	}
393 
394 	return rc;
395 }
396 #endif /* MEASURED_BOOT */
397