xref: /optee_os/core/kernel/dt.c (revision fe5af822179a7b4b7a9c8109aaa09e9441e9f5bb)
1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright (c) 2016, Linaro Limited
4  */
5 
6 #include <assert.h>
7 #include <config.h>
8 #include <initcall.h>
9 #include <kernel/dt.h>
10 #include <kernel/dt_driver.h>
11 #include <kernel/interrupt.h>
12 #include <libfdt.h>
13 #include <mm/core_memprot.h>
14 #include <mm/core_mmu.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <trace.h>
18 
19 static struct dt_descriptor external_dt __nex_bss;
20 
21 #if defined(CFG_CORE_FFA)
22 static void *manifest_dt __nex_bss;
23 #endif
24 
25 const struct dt_driver *dt_find_compatible_driver(const void *fdt, int offs)
26 {
27 	const struct dt_device_match *dm;
28 	const struct dt_driver *drv;
29 
30 	for_each_dt_driver(drv) {
31 		for (dm = drv->match_table; dm; dm++) {
32 			if (!dm->compatible) {
33 				break;
34 			}
35 			if (!fdt_node_check_compatible(fdt, offs,
36 						       dm->compatible)) {
37 				return drv;
38 			}
39 		}
40 	}
41 
42 	return NULL;
43 }
44 
45 bool dt_have_prop(const void *fdt, int offs, const char *propname)
46 {
47 	const void *prop;
48 
49 	prop = fdt_getprop(fdt, offs, propname, NULL);
50 
51 	return prop;
52 }
53 
54 int dt_disable_status(void *fdt, int node)
55 {
56 	const char *prop = NULL;
57 	int len = 0;
58 
59 	prop = fdt_getprop(fdt, node, "status", &len);
60 	if (!prop) {
61 		if (fdt_setprop_string(fdt, node, "status", "disabled"))
62 			return -1;
63 	} else {
64 		/*
65 		 * Status is there, modify it.
66 		 * Ask to set "disabled" value to the property. The value
67 		 * will be automatically truncated with "len" size by the
68 		 * fdt_setprop_inplace function.
69 		 * Setting a value different from "ok" or "okay" will disable
70 		 * the property.
71 		 * Setting a truncated value of "disabled" with the original
72 		 * property "len" is preferred to not increase the DT size and
73 		 * losing time in recalculating the overall DT offsets.
74 		 * If original length of the status property is larger than
75 		 * "disabled", the property will start with "disabled" and be
76 		 * completed with the rest of the original property.
77 		 */
78 		if (fdt_setprop_inplace(fdt, node, "status", "disabled", len))
79 			return -1;
80 	}
81 
82 	return 0;
83 }
84 
85 int dt_enable_secure_status(void *fdt, int node)
86 {
87 	if (dt_disable_status(fdt, node)) {
88 		EMSG("Unable to disable Normal Status");
89 		return -1;
90 	}
91 
92 	if (fdt_setprop_string(fdt, node, "secure-status", "okay"))
93 		return -1;
94 
95 	return 0;
96 }
97 
98 int dt_map_dev(const void *fdt, int offs, vaddr_t *base, size_t *size,
99 	       enum dt_map_dev_directive mapping)
100 {
101 	enum teecore_memtypes mtype;
102 	paddr_t pbase;
103 	vaddr_t vbase;
104 	size_t sz;
105 	int st;
106 
107 	assert(cpu_mmu_enabled());
108 
109 	st = fdt_get_status(fdt, offs);
110 	if (st == DT_STATUS_DISABLED)
111 		return -1;
112 
113 	pbase = fdt_reg_base_address(fdt, offs);
114 	if (pbase == DT_INFO_INVALID_REG)
115 		return -1;
116 	sz = fdt_reg_size(fdt, offs);
117 	if (sz == DT_INFO_INVALID_REG_SIZE)
118 		return -1;
119 
120 	switch (mapping) {
121 	case DT_MAP_AUTO:
122 		if ((st & DT_STATUS_OK_SEC) && !(st & DT_STATUS_OK_NSEC))
123 			mtype = MEM_AREA_IO_SEC;
124 		else
125 			mtype = MEM_AREA_IO_NSEC;
126 		break;
127 	case DT_MAP_SECURE:
128 		mtype = MEM_AREA_IO_SEC;
129 		break;
130 	case DT_MAP_NON_SECURE:
131 		mtype = MEM_AREA_IO_NSEC;
132 		break;
133 	default:
134 		panic("Invalid mapping specified");
135 		break;
136 	}
137 
138 	/* Check if we have a mapping, create one if needed */
139 	vbase = (vaddr_t)core_mmu_add_mapping(mtype, pbase, sz);
140 	if (!vbase) {
141 		EMSG("Failed to map %zu bytes at PA 0x%"PRIxPA,
142 		     (size_t)sz, pbase);
143 		return -1;
144 	}
145 
146 	*base = vbase;
147 	*size = sz;
148 	return 0;
149 }
150 
151 /* Read a physical address (n=1 or 2 cells) */
152 static paddr_t fdt_read_paddr(const uint32_t *cell, int n)
153 {
154 	paddr_t addr;
155 
156 	if (n < 1 || n > 2)
157 		goto bad;
158 
159 	addr = fdt32_to_cpu(*cell);
160 	cell++;
161 	if (n == 2) {
162 #ifdef ARM32
163 		if (addr) {
164 			/* High order 32 bits can't be nonzero */
165 			goto bad;
166 		}
167 		addr = fdt32_to_cpu(*cell);
168 #else
169 		addr = (addr << 32) | fdt32_to_cpu(*cell);
170 #endif
171 	}
172 
173 	return addr;
174 bad:
175 	return DT_INFO_INVALID_REG;
176 
177 }
178 
179 paddr_t fdt_reg_base_address(const void *fdt, int offs)
180 {
181 	const void *reg;
182 	int ncells;
183 	int len;
184 	int parent;
185 
186 	parent = fdt_parent_offset(fdt, offs);
187 	if (parent < 0)
188 		return DT_INFO_INVALID_REG;
189 
190 	reg = fdt_getprop(fdt, offs, "reg", &len);
191 	if (!reg)
192 		return DT_INFO_INVALID_REG;
193 
194 	ncells = fdt_address_cells(fdt, parent);
195 	if (ncells < 0)
196 		return DT_INFO_INVALID_REG;
197 
198 	return fdt_read_paddr(reg, ncells);
199 }
200 
201 static size_t fdt_read_size(const uint32_t *cell, int n)
202 {
203 	uint32_t sz = 0;
204 
205 	sz = fdt32_to_cpu(*cell);
206 	if (n == 2) {
207 		if (sz)
208 			return DT_INFO_INVALID_REG_SIZE;
209 
210 		cell++;
211 		sz = fdt32_to_cpu(*cell);
212 	}
213 
214 	return sz;
215 }
216 
217 size_t fdt_reg_size(const void *fdt, int offs)
218 {
219 	const uint32_t *reg;
220 	int n;
221 	int len;
222 	int parent;
223 
224 	parent = fdt_parent_offset(fdt, offs);
225 	if (parent < 0)
226 		return DT_INFO_INVALID_REG_SIZE;
227 
228 	reg = (const uint32_t *)fdt_getprop(fdt, offs, "reg", &len);
229 	if (!reg)
230 		return DT_INFO_INVALID_REG_SIZE;
231 
232 	n = fdt_address_cells(fdt, parent);
233 	if (n < 1 || n > 2)
234 		return DT_INFO_INVALID_REG_SIZE;
235 
236 	reg += n;
237 
238 	n = fdt_size_cells(fdt, parent);
239 	if (n < 1 || n > 2)
240 		return DT_INFO_INVALID_REG_SIZE;
241 
242 	return fdt_read_size(reg, n);
243 }
244 
245 static bool is_okay(const char *st, int len)
246 {
247 	return !strncmp(st, "ok", len) || !strncmp(st, "okay", len);
248 }
249 
250 int fdt_get_status(const void *fdt, int offs)
251 {
252 	const char *prop;
253 	int st = 0;
254 	int len;
255 
256 	prop = fdt_getprop(fdt, offs, "status", &len);
257 	if (!prop || is_okay(prop, len)) {
258 		/* If status is not specified, it defaults to "okay" */
259 		st |= DT_STATUS_OK_NSEC;
260 	}
261 
262 	prop = fdt_getprop(fdt, offs, "secure-status", &len);
263 	if (!prop) {
264 		/*
265 		 * When secure-status is not specified it defaults to the same
266 		 * value as status
267 		 */
268 		if (st & DT_STATUS_OK_NSEC)
269 			st |= DT_STATUS_OK_SEC;
270 	} else {
271 		if (is_okay(prop, len))
272 			st |= DT_STATUS_OK_SEC;
273 	}
274 
275 	return st;
276 }
277 
278 void fdt_fill_device_info(const void *fdt, struct dt_node_info *info, int offs)
279 {
280 	struct dt_node_info dinfo = {
281 		.reg = DT_INFO_INVALID_REG,
282 		.reg_size = DT_INFO_INVALID_REG_SIZE,
283 		.clock = DT_INFO_INVALID_CLOCK,
284 		.reset = DT_INFO_INVALID_RESET,
285 		.interrupt = DT_INFO_INVALID_INTERRUPT,
286 	};
287 	const fdt32_t *cuint;
288 
289 	dinfo.reg = fdt_reg_base_address(fdt, offs);
290 	dinfo.reg_size = fdt_reg_size(fdt, offs);
291 
292 	cuint = fdt_getprop(fdt, offs, "clocks", NULL);
293 	if (cuint) {
294 		cuint++;
295 		dinfo.clock = (int)fdt32_to_cpu(*cuint);
296 	}
297 
298 	cuint = fdt_getprop(fdt, offs, "resets", NULL);
299 	if (cuint) {
300 		cuint++;
301 		dinfo.reset = (int)fdt32_to_cpu(*cuint);
302 	}
303 
304 	dinfo.interrupt = dt_get_irq_type_prio(fdt, offs, &dinfo.type,
305 					       &dinfo.prio);
306 
307 	dinfo.status = fdt_get_status(fdt, offs);
308 
309 	*info = dinfo;
310 }
311 
312 int fdt_read_uint32_array(const void *fdt, int node, const char *prop_name,
313 			  uint32_t *array, size_t count)
314 {
315 	const fdt32_t *cuint = NULL;
316 	int len = 0;
317 	uint32_t i = 0;
318 
319 	cuint = fdt_getprop(fdt, node, prop_name, &len);
320 	if (!cuint)
321 		return len;
322 
323 	if ((uint32_t)len != (count * sizeof(uint32_t)))
324 		return -FDT_ERR_BADLAYOUT;
325 
326 	for (i = 0; i < ((uint32_t)len / sizeof(uint32_t)); i++) {
327 		*array = fdt32_to_cpu(*cuint);
328 		array++;
329 		cuint++;
330 	}
331 
332 	return 0;
333 }
334 
335 int fdt_read_uint32_index(const void *fdt, int node, const char *prop_name,
336 			  int index, uint32_t *value)
337 {
338 	const fdt32_t *cuint = NULL;
339 	int len = 0;
340 
341 	cuint = fdt_getprop(fdt, node, prop_name, &len);
342 	if (!cuint)
343 		return len;
344 
345 	if ((uint32_t)len < (sizeof(uint32_t) * (index + 1)))
346 		return -FDT_ERR_BADLAYOUT;
347 
348 	*value = fdt32_to_cpu(cuint[index]);
349 
350 	return 0;
351 }
352 
353 int fdt_read_uint32(const void *fdt, int node, const char *prop_name,
354 		    uint32_t *value)
355 {
356 	return fdt_read_uint32_array(fdt, node, prop_name, value, 1);
357 }
358 
359 uint32_t fdt_read_uint32_default(const void *fdt, int node,
360 				 const char *prop_name, uint32_t dflt_value)
361 {
362 	uint32_t ret = dflt_value;
363 
364 	fdt_read_uint32_index(fdt, node, prop_name, 0, &ret);
365 
366 	return ret;
367 }
368 
369 int fdt_get_reg_props_by_index(const void *fdt, int node, int index,
370 			       paddr_t *base, size_t *size)
371 {
372 	const fdt32_t *prop = NULL;
373 	int parent = 0;
374 	int len = 0;
375 	int address_cells = 0;
376 	int size_cells = 0;
377 	int cell = 0;
378 
379 	parent = fdt_parent_offset(fdt, node);
380 	if (parent < 0)
381 		return parent;
382 
383 	address_cells = fdt_address_cells(fdt, parent);
384 	if (address_cells < 0)
385 		return address_cells;
386 
387 	size_cells = fdt_size_cells(fdt, parent);
388 	if (size_cells < 0)
389 		return size_cells;
390 
391 	cell = index * (address_cells + size_cells);
392 
393 	prop = fdt_getprop(fdt, node, "reg", &len);
394 	if (!prop)
395 		return len;
396 
397 	if (((cell + address_cells + size_cells) * (int)sizeof(uint32_t)) > len)
398 		return -FDT_ERR_BADVALUE;
399 
400 	if (base) {
401 		*base = fdt_read_paddr(&prop[cell], address_cells);
402 		if (*base == DT_INFO_INVALID_REG)
403 			return -FDT_ERR_BADVALUE;
404 	}
405 
406 	if (size) {
407 		*size = fdt_read_size(&prop[cell + address_cells], size_cells);
408 		if (*size == DT_INFO_INVALID_REG_SIZE)
409 			return -FDT_ERR_BADVALUE;
410 	}
411 
412 	return 0;
413 }
414 
415 int fdt_get_reg_props_by_name(const void *fdt, int node, const char *name,
416 			      paddr_t *base, size_t *size)
417 {
418 	int index = 0;
419 
420 	index = fdt_stringlist_search(fdt, node, "reg-names", name);
421 	if (index < 0)
422 		return index;
423 
424 	return fdt_get_reg_props_by_index(fdt, node, index, base, size);
425 }
426 
427 int dt_getprop_as_number(const void *fdt, int nodeoffset, const char *name,
428 			 uint64_t *num)
429 {
430 	const void *prop = NULL;
431 	int len = 0;
432 
433 	prop = fdt_getprop(fdt, nodeoffset, name, &len);
434 	if (!prop)
435 		return len;
436 
437 	switch (len) {
438 	case sizeof(uint32_t):
439 		*num = fdt32_ld(prop);
440 		return 0;
441 	case sizeof(uint64_t):
442 		*num = fdt64_ld(prop);
443 		return 0;
444 	default:
445 		return -FDT_ERR_BADVALUE;
446 	}
447 }
448 
449 void *get_dt(void)
450 {
451 	void *fdt = get_embedded_dt();
452 
453 	if (!fdt)
454 		fdt = get_external_dt();
455 
456 	if (!fdt)
457 		fdt = get_manifest_dt();
458 
459 	return fdt;
460 }
461 
462 void *get_secure_dt(void)
463 {
464 	void *fdt = get_embedded_dt();
465 
466 	if (!fdt && IS_ENABLED(CFG_MAP_EXT_DT_SECURE))
467 		fdt = get_external_dt();
468 
469 	if (!fdt)
470 		fdt = get_manifest_dt();
471 
472 	return fdt;
473 }
474 
475 #if defined(CFG_EMBED_DTB)
476 void *get_embedded_dt(void)
477 {
478 	static bool checked;
479 
480 	assert(cpu_mmu_enabled());
481 
482 	if (!checked) {
483 		IMSG("Embedded DTB found");
484 
485 		if (fdt_check_header(embedded_secure_dtb))
486 			panic("Invalid embedded DTB");
487 
488 		checked = true;
489 	}
490 
491 	return embedded_secure_dtb;
492 }
493 #else
494 void *get_embedded_dt(void)
495 {
496 	return NULL;
497 }
498 #endif /*CFG_EMBED_DTB*/
499 
500 #ifdef _CFG_USE_DTB_OVERLAY
501 static int add_dt_overlay_fragment(struct dt_descriptor *dt, int ioffs)
502 {
503 	char frag[32] = { };
504 	int offs = 0;
505 	int ret = 0;
506 
507 	ret = snprintf(frag, sizeof(frag), "fragment@%d", dt->frag_id);
508 	if (ret < 0 || (size_t)ret >= sizeof(frag))
509 		return -1;
510 
511 	offs = fdt_add_subnode(dt->blob, ioffs, frag);
512 	if (offs < 0)
513 		return offs;
514 
515 	dt->frag_id += 1;
516 
517 	ret = fdt_setprop_string(dt->blob, offs, "target-path", "/");
518 	if (ret < 0)
519 		return ret;
520 
521 	return fdt_add_subnode(dt->blob, offs, "__overlay__");
522 }
523 
524 static int init_dt_overlay(struct dt_descriptor *dt, int __maybe_unused dt_size)
525 {
526 	int fragment = 0;
527 
528 	if (IS_ENABLED(CFG_EXTERNAL_DTB_OVERLAY)) {
529 		if (!fdt_check_header(dt->blob)) {
530 			fdt_for_each_subnode(fragment, dt->blob, 0)
531 				dt->frag_id += 1;
532 			return 0;
533 		}
534 	}
535 
536 	return fdt_create_empty_tree(dt->blob, dt_size);
537 }
538 #else
539 static int add_dt_overlay_fragment(struct dt_descriptor *dt __unused, int offs)
540 {
541 	return offs;
542 }
543 
544 static int init_dt_overlay(struct dt_descriptor *dt __unused,
545 			   int dt_size __unused)
546 {
547 	return 0;
548 }
549 #endif /* _CFG_USE_DTB_OVERLAY */
550 
551 struct dt_descriptor *get_external_dt_desc(void)
552 {
553 	if (!IS_ENABLED(CFG_EXTERNAL_DT))
554 		return NULL;
555 
556 	return &external_dt;
557 }
558 
559 void init_external_dt(unsigned long phys_dt, size_t dt_sz)
560 {
561 	struct dt_descriptor *dt = &external_dt;
562 	int ret = 0;
563 	enum teecore_memtypes mtype = MEM_AREA_MAXTYPE;
564 
565 	if (!IS_ENABLED(CFG_EXTERNAL_DT))
566 		return;
567 
568 	if (!phys_dt || !dt_sz) {
569 		/*
570 		 * No need to panic as we're not using the DT in OP-TEE
571 		 * yet, we're only adding some nodes for normal world use.
572 		 * This makes the switch to using DT easier as we can boot
573 		 * a newer OP-TEE with older boot loaders. Once we start to
574 		 * initialize devices based on DT we'll likely panic
575 		 * instead of returning here.
576 		 */
577 		IMSG("No non-secure external DT");
578 		return;
579 	}
580 
581 	mtype = core_mmu_get_type_by_pa(phys_dt);
582 	if (mtype == MEM_AREA_MAXTYPE) {
583 		/* Map the DTB if it is not yet mapped */
584 		dt->blob = core_mmu_add_mapping(MEM_AREA_EXT_DT, phys_dt,
585 						dt_sz);
586 		if (!dt->blob)
587 			panic("Failed to map external DTB");
588 	} else {
589 		/* Get the DTB address if already mapped in a memory area */
590 		dt->blob = phys_to_virt(phys_dt, mtype, dt_sz);
591 		if (!dt->blob) {
592 			EMSG("Failed to get a mapped external DTB for PA %#lx",
593 			     phys_dt);
594 			panic();
595 		}
596 	}
597 
598 	ret = init_dt_overlay(dt, dt_sz);
599 	if (ret < 0) {
600 		EMSG("Device Tree Overlay init fail @ %#lx: error %d", phys_dt,
601 		     ret);
602 		panic();
603 	}
604 
605 	ret = fdt_open_into(dt->blob, dt->blob, dt_sz);
606 	if (ret < 0) {
607 		EMSG("Invalid Device Tree at %#lx: error %d", phys_dt, ret);
608 		panic();
609 	}
610 
611 	IMSG("Non-secure external DT found");
612 }
613 
614 void *get_external_dt(void)
615 {
616 	if (!IS_ENABLED(CFG_EXTERNAL_DT))
617 		return NULL;
618 
619 	assert(cpu_mmu_enabled());
620 	return external_dt.blob;
621 }
622 
623 static TEE_Result release_external_dt(void)
624 {
625 	int ret = 0;
626 	paddr_t pa_dt = 0;
627 
628 	if (!IS_ENABLED(CFG_EXTERNAL_DT))
629 		return TEE_SUCCESS;
630 
631 	if (!external_dt.blob)
632 		return TEE_SUCCESS;
633 
634 	pa_dt = virt_to_phys(external_dt.blob);
635 	/*
636 	 * Skip packing and un-mapping operations if the external DTB is mapped
637 	 * in a different memory area
638 	 */
639 	if (core_mmu_get_type_by_pa(pa_dt) != MEM_AREA_EXT_DT)
640 		return TEE_SUCCESS;
641 
642 	ret = fdt_pack(external_dt.blob);
643 	if (ret < 0) {
644 		EMSG("Failed to pack Device Tree at 0x%" PRIxPA ": error %d",
645 		     virt_to_phys(external_dt.blob), ret);
646 		panic();
647 	}
648 
649 	if (core_mmu_remove_mapping(MEM_AREA_EXT_DT, external_dt.blob,
650 				    CFG_DTB_MAX_SIZE))
651 		panic("Failed to remove temporary Device Tree mapping");
652 
653 	/* External DTB no more reached, reset pointer to invalid */
654 	external_dt.blob = NULL;
655 
656 	return TEE_SUCCESS;
657 }
658 
659 boot_final(release_external_dt);
660 
661 int add_dt_path_subnode(struct dt_descriptor *dt, const char *path,
662 			const char *subnode)
663 {
664 	int offs = 0;
665 
666 	offs = fdt_path_offset(dt->blob, path);
667 	if (offs < 0)
668 		return offs;
669 	offs = add_dt_overlay_fragment(dt, offs);
670 	if (offs < 0)
671 		return offs;
672 	return fdt_add_subnode(dt->blob, offs, subnode);
673 }
674 
675 static void set_dt_val(void *data, uint32_t cell_size, uint64_t val)
676 {
677 	if (cell_size == 1) {
678 		fdt32_t v = cpu_to_fdt32((uint32_t)val);
679 
680 		memcpy(data, &v, sizeof(v));
681 	} else {
682 		fdt64_t v = cpu_to_fdt64(val);
683 
684 		memcpy(data, &v, sizeof(v));
685 	}
686 }
687 
688 int add_res_mem_dt_node(struct dt_descriptor *dt, const char *name,
689 			paddr_t pa, size_t size)
690 {
691 	int offs = 0;
692 	int ret = 0;
693 	int addr_size = -1;
694 	int len_size = -1;
695 	bool found = true;
696 	char subnode_name[80] = { };
697 
698 	offs = fdt_path_offset(dt->blob, "/reserved-memory");
699 
700 	if (offs < 0) {
701 		found = false;
702 		offs = 0;
703 	}
704 
705 	if (IS_ENABLED2(_CFG_USE_DTB_OVERLAY)) {
706 		len_size = sizeof(paddr_t) / sizeof(uint32_t);
707 		addr_size = sizeof(paddr_t) / sizeof(uint32_t);
708 	} else {
709 		len_size = fdt_size_cells(dt->blob, offs);
710 		if (len_size < 0)
711 			return len_size;
712 		addr_size = fdt_address_cells(dt->blob, offs);
713 		if (addr_size < 0)
714 			return addr_size;
715 	}
716 
717 	if (!found) {
718 		offs = add_dt_path_subnode(dt, "/", "reserved-memory");
719 		if (offs < 0)
720 			return offs;
721 		ret = fdt_setprop_cell(dt->blob, offs, "#address-cells",
722 				       addr_size);
723 		if (ret < 0)
724 			return ret;
725 		ret = fdt_setprop_cell(dt->blob, offs, "#size-cells", len_size);
726 		if (ret < 0)
727 			return ret;
728 		ret = fdt_setprop(dt->blob, offs, "ranges", NULL, 0);
729 		if (ret < 0)
730 			return ret;
731 	}
732 
733 	ret = snprintf(subnode_name, sizeof(subnode_name),
734 		       "%s@%" PRIxPA, name, pa);
735 	if (ret < 0 || ret >= (int)sizeof(subnode_name))
736 		DMSG("truncated node \"%s@%" PRIxPA"\"", name, pa);
737 	offs = fdt_add_subnode(dt->blob, offs, subnode_name);
738 	if (offs >= 0) {
739 		uint32_t data[FDT_MAX_NCELLS * 2] = { };
740 
741 		set_dt_val(data, addr_size, pa);
742 		set_dt_val(data + addr_size, len_size, size);
743 		ret = fdt_setprop(dt->blob, offs, "reg", data,
744 				  sizeof(uint32_t) * (addr_size + len_size));
745 		if (ret < 0)
746 			return ret;
747 		ret = fdt_setprop(dt->blob, offs, "no-map", NULL, 0);
748 		if (ret < 0)
749 			return ret;
750 	} else {
751 		return offs;
752 	}
753 	return 0;
754 }
755 
756 #if defined(CFG_CORE_FFA)
757 void init_manifest_dt(void *fdt)
758 {
759 	manifest_dt = fdt;
760 }
761 
762 void reinit_manifest_dt(void)
763 {
764 	paddr_t pa = (unsigned long)manifest_dt;
765 	void *fdt = NULL;
766 	int ret = 0;
767 
768 	if (!pa) {
769 		EMSG("No manifest DT found");
770 		return;
771 	}
772 
773 	fdt = core_mmu_add_mapping(MEM_AREA_MANIFEST_DT, pa, CFG_DTB_MAX_SIZE);
774 	if (!fdt)
775 		panic("Failed to map manifest DT");
776 
777 	manifest_dt = fdt;
778 
779 	ret = fdt_check_full(fdt, CFG_DTB_MAX_SIZE);
780 	if (ret < 0) {
781 		EMSG("Invalid manifest Device Tree at %#lx: error %d", pa, ret);
782 		panic();
783 	}
784 
785 	IMSG("manifest DT found");
786 }
787 
788 void *get_manifest_dt(void)
789 {
790 	return manifest_dt;
791 }
792 
793 static TEE_Result release_manifest_dt(void)
794 {
795 	if (!manifest_dt)
796 		return TEE_SUCCESS;
797 
798 	if (core_mmu_remove_mapping(MEM_AREA_MANIFEST_DT, manifest_dt,
799 				    CFG_DTB_MAX_SIZE))
800 		panic("Failed to remove temporary manifest DT mapping");
801 	manifest_dt = NULL;
802 
803 	return TEE_SUCCESS;
804 }
805 
806 boot_final(release_manifest_dt);
807 #else
808 void init_manifest_dt(void *fdt __unused)
809 {
810 }
811 
812 void reinit_manifest_dt(void)
813 {
814 }
815 
816 void *get_manifest_dt(void)
817 {
818 	return NULL;
819 }
820 #endif /*CFG_CORE_FFA*/
821