xref: /optee_os/core/kernel/dt.c (revision 96e33b7fdef95702f1a63448bc30b1e0980ee549)
11bb92983SJerome Forissier // SPDX-License-Identifier: BSD-2-Clause
2a4f139d7SJerome Forissier /*
3a4f139d7SJerome Forissier  * Copyright (c) 2016, Linaro Limited
4a4f139d7SJerome Forissier  */
5a4f139d7SJerome Forissier 
67ba16abbSJerome Forissier #include <assert.h>
7e6027f48SAlvin Chang #include <config.h>
8e6027f48SAlvin Chang #include <initcall.h>
9a4f139d7SJerome Forissier #include <kernel/dt.h>
109e3c57c8SEtienne Carriere #include <kernel/dt_driver.h>
117acb3a47SLudovic Barre #include <kernel/interrupt.h>
129fe4c797SJerome Forissier #include <libfdt.h>
137ba16abbSJerome Forissier #include <mm/core_memprot.h>
147ba16abbSJerome Forissier #include <mm/core_mmu.h>
15e6027f48SAlvin Chang #include <stdio.h>
16a4f139d7SJerome Forissier #include <string.h>
177ba16abbSJerome Forissier #include <trace.h>
18a4f139d7SJerome Forissier 
19e6027f48SAlvin Chang static struct dt_descriptor external_dt __nex_bss;
20e6027f48SAlvin Chang 
214bc2c5f0SSungbae Yoo #if defined(CFG_CORE_FFA)
224bc2c5f0SSungbae Yoo static void *manifest_dt __nex_bss;
234bc2c5f0SSungbae Yoo #endif
244bc2c5f0SSungbae Yoo 
25a4f139d7SJerome Forissier const struct dt_driver *dt_find_compatible_driver(const void *fdt, int offs)
26a4f139d7SJerome Forissier {
27a4f139d7SJerome Forissier 	const struct dt_device_match *dm;
28a4f139d7SJerome Forissier 	const struct dt_driver *drv;
29a4f139d7SJerome Forissier 
30db783ff8SEtienne Carriere 	for_each_dt_driver(drv) {
31db783ff8SEtienne Carriere 		for (dm = drv->match_table; dm; dm++) {
32db783ff8SEtienne Carriere 			if (!dm->compatible) {
33db783ff8SEtienne Carriere 				break;
34db783ff8SEtienne Carriere 			}
35a4f139d7SJerome Forissier 			if (!fdt_node_check_compatible(fdt, offs,
36db783ff8SEtienne Carriere 						       dm->compatible)) {
37a4f139d7SJerome Forissier 				return drv;
38db783ff8SEtienne Carriere 			}
39db783ff8SEtienne Carriere 		}
40db783ff8SEtienne Carriere 	}
41a4f139d7SJerome Forissier 
42a4f139d7SJerome Forissier 	return NULL;
43a4f139d7SJerome Forissier }
44a4f139d7SJerome Forissier 
4550f3b323SPeng Fan bool dt_have_prop(const void *fdt, int offs, const char *propname)
4650f3b323SPeng Fan {
4750f3b323SPeng Fan 	const void *prop;
4850f3b323SPeng Fan 
4950f3b323SPeng Fan 	prop = fdt_getprop(fdt, offs, propname, NULL);
5050f3b323SPeng Fan 
5150f3b323SPeng Fan 	return prop;
5250f3b323SPeng Fan }
5350f3b323SPeng Fan 
5495cdc5e0SCedric Neveux int dt_disable_status(void *fdt, int node)
5595cdc5e0SCedric Neveux {
5695cdc5e0SCedric Neveux 	const char *prop = NULL;
5795cdc5e0SCedric Neveux 	int len = 0;
5895cdc5e0SCedric Neveux 
5995cdc5e0SCedric Neveux 	prop = fdt_getprop(fdt, node, "status", &len);
6095cdc5e0SCedric Neveux 	if (!prop) {
6195cdc5e0SCedric Neveux 		if (fdt_setprop_string(fdt, node, "status", "disabled"))
6295cdc5e0SCedric Neveux 			return -1;
6395cdc5e0SCedric Neveux 	} else {
6495cdc5e0SCedric Neveux 		/*
6595cdc5e0SCedric Neveux 		 * Status is there, modify it.
6695cdc5e0SCedric Neveux 		 * Ask to set "disabled" value to the property. The value
6795cdc5e0SCedric Neveux 		 * will be automatically truncated with "len" size by the
6895cdc5e0SCedric Neveux 		 * fdt_setprop_inplace function.
6995cdc5e0SCedric Neveux 		 * Setting a value different from "ok" or "okay" will disable
7095cdc5e0SCedric Neveux 		 * the property.
7195cdc5e0SCedric Neveux 		 * Setting a truncated value of "disabled" with the original
7295cdc5e0SCedric Neveux 		 * property "len" is preferred to not increase the DT size and
7395cdc5e0SCedric Neveux 		 * losing time in recalculating the overall DT offsets.
7495cdc5e0SCedric Neveux 		 * If original length of the status property is larger than
7595cdc5e0SCedric Neveux 		 * "disabled", the property will start with "disabled" and be
7695cdc5e0SCedric Neveux 		 * completed with the rest of the original property.
7795cdc5e0SCedric Neveux 		 */
7895cdc5e0SCedric Neveux 		if (fdt_setprop_inplace(fdt, node, "status", "disabled", len))
7995cdc5e0SCedric Neveux 			return -1;
8095cdc5e0SCedric Neveux 	}
8195cdc5e0SCedric Neveux 
8295cdc5e0SCedric Neveux 	return 0;
8395cdc5e0SCedric Neveux }
8495cdc5e0SCedric Neveux 
8595cdc5e0SCedric Neveux int dt_enable_secure_status(void *fdt, int node)
8695cdc5e0SCedric Neveux {
8795cdc5e0SCedric Neveux 	if (dt_disable_status(fdt, node)) {
8895cdc5e0SCedric Neveux 		EMSG("Unable to disable Normal Status");
8995cdc5e0SCedric Neveux 		return -1;
9095cdc5e0SCedric Neveux 	}
9195cdc5e0SCedric Neveux 
9295cdc5e0SCedric Neveux 	if (fdt_setprop_string(fdt, node, "secure-status", "okay"))
9395cdc5e0SCedric Neveux 		return -1;
9495cdc5e0SCedric Neveux 
9595cdc5e0SCedric Neveux 	return 0;
9695cdc5e0SCedric Neveux }
9795cdc5e0SCedric Neveux 
98a5d5bbc8SVesa Jääskeläinen int dt_map_dev(const void *fdt, int offs, vaddr_t *base, size_t *size,
99a5d5bbc8SVesa Jääskeläinen 	       enum dt_map_dev_directive mapping)
1007ba16abbSJerome Forissier {
1017ba16abbSJerome Forissier 	enum teecore_memtypes mtype;
1027ba16abbSJerome Forissier 	paddr_t pbase;
1037ba16abbSJerome Forissier 	vaddr_t vbase;
104df7cecc0SLionel Debieve 	size_t sz;
1057ba16abbSJerome Forissier 	int st;
1067ba16abbSJerome Forissier 
1077ba16abbSJerome Forissier 	assert(cpu_mmu_enabled());
1087ba16abbSJerome Forissier 
109f354a5d8SGatien Chevallier 	st = fdt_get_status(fdt, offs);
1107ba16abbSJerome Forissier 	if (st == DT_STATUS_DISABLED)
1117ba16abbSJerome Forissier 		return -1;
1127ba16abbSJerome Forissier 
113de56c16dSEtienne Carriere 	if (fdt_reg_info(fdt, offs, &pbase, &sz))
1147ba16abbSJerome Forissier 		return -1;
1157ba16abbSJerome Forissier 
116a5d5bbc8SVesa Jääskeläinen 	switch (mapping) {
117a5d5bbc8SVesa Jääskeläinen 	case DT_MAP_AUTO:
1187ba16abbSJerome Forissier 		if ((st & DT_STATUS_OK_SEC) && !(st & DT_STATUS_OK_NSEC))
1197ba16abbSJerome Forissier 			mtype = MEM_AREA_IO_SEC;
1207ba16abbSJerome Forissier 		else
1217ba16abbSJerome Forissier 			mtype = MEM_AREA_IO_NSEC;
122a5d5bbc8SVesa Jääskeläinen 		break;
123a5d5bbc8SVesa Jääskeläinen 	case DT_MAP_SECURE:
124a5d5bbc8SVesa Jääskeläinen 		mtype = MEM_AREA_IO_SEC;
125a5d5bbc8SVesa Jääskeläinen 		break;
126a5d5bbc8SVesa Jääskeläinen 	case DT_MAP_NON_SECURE:
127a5d5bbc8SVesa Jääskeläinen 		mtype = MEM_AREA_IO_NSEC;
128a5d5bbc8SVesa Jääskeläinen 		break;
129a5d5bbc8SVesa Jääskeläinen 	default:
130a5d5bbc8SVesa Jääskeläinen 		panic("Invalid mapping specified");
131a5d5bbc8SVesa Jääskeläinen 		break;
132a5d5bbc8SVesa Jääskeläinen 	}
1337ba16abbSJerome Forissier 
1347ba16abbSJerome Forissier 	/* Check if we have a mapping, create one if needed */
135bc9618c0SAnton Rybakov 	vbase = (vaddr_t)core_mmu_add_mapping(mtype, pbase, sz);
136bc9618c0SAnton Rybakov 	if (!vbase) {
1377ba16abbSJerome Forissier 		EMSG("Failed to map %zu bytes at PA 0x%"PRIxPA,
13823b1daf4SPeng Fan 		     (size_t)sz, pbase);
1397ba16abbSJerome Forissier 		return -1;
1407ba16abbSJerome Forissier 	}
1417ba16abbSJerome Forissier 
1427ba16abbSJerome Forissier 	*base = vbase;
1437ba16abbSJerome Forissier 	*size = sz;
1447ba16abbSJerome Forissier 	return 0;
1457ba16abbSJerome Forissier }
1467ba16abbSJerome Forissier 
1479fe4c797SJerome Forissier /* Read a physical address (n=1 or 2 cells) */
148f354a5d8SGatien Chevallier static paddr_t fdt_read_paddr(const uint32_t *cell, int n)
1499fe4c797SJerome Forissier {
1509fe4c797SJerome Forissier 	paddr_t addr;
1519fe4c797SJerome Forissier 
1529fe4c797SJerome Forissier 	if (n < 1 || n > 2)
1539fe4c797SJerome Forissier 		goto bad;
1549fe4c797SJerome Forissier 
1559fe4c797SJerome Forissier 	addr = fdt32_to_cpu(*cell);
1569fe4c797SJerome Forissier 	cell++;
1579fe4c797SJerome Forissier 	if (n == 2) {
1589fe4c797SJerome Forissier #ifdef ARM32
1599fe4c797SJerome Forissier 		if (addr) {
1609fe4c797SJerome Forissier 			/* High order 32 bits can't be nonzero */
1619fe4c797SJerome Forissier 			goto bad;
1629fe4c797SJerome Forissier 		}
1639fe4c797SJerome Forissier 		addr = fdt32_to_cpu(*cell);
1649fe4c797SJerome Forissier #else
1659fe4c797SJerome Forissier 		addr = (addr << 32) | fdt32_to_cpu(*cell);
1669fe4c797SJerome Forissier #endif
1679fe4c797SJerome Forissier 	}
1689fe4c797SJerome Forissier 
1699fe4c797SJerome Forissier 	return addr;
1709fe4c797SJerome Forissier bad:
171c0cfb36cSEtienne Carriere 	return DT_INFO_INVALID_REG;
1729fe4c797SJerome Forissier 
1739fe4c797SJerome Forissier }
1749fe4c797SJerome Forissier 
175a2e8c036SGatien Chevallier static size_t fdt_read_size(const uint32_t *cell, int n)
176a2e8c036SGatien Chevallier {
177a2e8c036SGatien Chevallier 	uint32_t sz = 0;
178a2e8c036SGatien Chevallier 
179a2e8c036SGatien Chevallier 	sz = fdt32_to_cpu(*cell);
180a2e8c036SGatien Chevallier 	if (n == 2) {
181a2e8c036SGatien Chevallier 		if (sz)
182a2e8c036SGatien Chevallier 			return DT_INFO_INVALID_REG_SIZE;
183a2e8c036SGatien Chevallier 
184a2e8c036SGatien Chevallier 		cell++;
185a2e8c036SGatien Chevallier 		sz = fdt32_to_cpu(*cell);
186a2e8c036SGatien Chevallier 	}
187a2e8c036SGatien Chevallier 
188a2e8c036SGatien Chevallier 	return sz;
189a2e8c036SGatien Chevallier }
190a2e8c036SGatien Chevallier 
191*96e33b7fSEtienne Carriere int fdt_get_reg_props_by_index(const void *fdt, int offs, int index,
192*96e33b7fSEtienne Carriere 			       paddr_t *base, size_t *size)
1939fe4c797SJerome Forissier {
194de56c16dSEtienne Carriere 	const fdt32_t *reg = NULL;
195de56c16dSEtienne Carriere 	int addr_ncells = 0;
196de56c16dSEtienne Carriere 	int size_ncells = 0;
197*96e33b7fSEtienne Carriere 	int cell_offset = 0;
198578bc4feSEtienne Carriere 	int parent = 0;
199de56c16dSEtienne Carriere 	int len = 0;
2009fe4c797SJerome Forissier 
201*96e33b7fSEtienne Carriere 	if (index < 0)
202*96e33b7fSEtienne Carriere 		return -FDT_ERR_BADOFFSET;
203*96e33b7fSEtienne Carriere 
2049fe4c797SJerome Forissier 	reg = (const uint32_t *)fdt_getprop(fdt, offs, "reg", &len);
2059fe4c797SJerome Forissier 	if (!reg)
206de56c16dSEtienne Carriere 		return -FDT_ERR_NOTFOUND;
2079fe4c797SJerome Forissier 
208de56c16dSEtienne Carriere 	if (fdt_find_cached_parent_reg_cells(fdt, offs, &addr_ncells,
209de56c16dSEtienne Carriere 					     &size_ncells) != 0) {
210578bc4feSEtienne Carriere 		parent = fdt_parent_offset(fdt, offs);
211578bc4feSEtienne Carriere 		if (parent < 0)
212de56c16dSEtienne Carriere 			return -FDT_ERR_NOTFOUND;
213578bc4feSEtienne Carriere 
214de56c16dSEtienne Carriere 		addr_ncells = fdt_address_cells(fdt, parent);
215de56c16dSEtienne Carriere 		if (addr_ncells < 0)
216de56c16dSEtienne Carriere 			return -FDT_ERR_NOTFOUND;
2179fe4c797SJerome Forissier 
218de56c16dSEtienne Carriere 		size_ncells = fdt_size_cells(fdt, parent);
219de56c16dSEtienne Carriere 		if (size_ncells < 0)
220de56c16dSEtienne Carriere 			return -FDT_ERR_NOTFOUND;
221578bc4feSEtienne Carriere 	}
222578bc4feSEtienne Carriere 
223*96e33b7fSEtienne Carriere 	cell_offset = index * (addr_ncells + size_ncells);
224*96e33b7fSEtienne Carriere 
225*96e33b7fSEtienne Carriere 	if ((size_t)len < (cell_offset + addr_ncells) * sizeof(*reg))
226de56c16dSEtienne Carriere 		return -FDT_ERR_BADSTRUCTURE;
227de56c16dSEtienne Carriere 
228de56c16dSEtienne Carriere 	if (base) {
229*96e33b7fSEtienne Carriere 		*base = fdt_read_paddr(reg + cell_offset, addr_ncells);
230de56c16dSEtienne Carriere 		if (*base == DT_INFO_INVALID_REG)
231de56c16dSEtienne Carriere 			return -FDT_ERR_NOTFOUND;
232de56c16dSEtienne Carriere 	}
233de56c16dSEtienne Carriere 
234de56c16dSEtienne Carriere 	if (size) {
235*96e33b7fSEtienne Carriere 		if ((size_t)len <
236*96e33b7fSEtienne Carriere 		    (cell_offset + addr_ncells + size_ncells) * sizeof(*reg))
237de56c16dSEtienne Carriere 			return -FDT_ERR_BADSTRUCTURE;
238de56c16dSEtienne Carriere 
239*96e33b7fSEtienne Carriere 		*size = fdt_read_size(reg + cell_offset + addr_ncells,
240*96e33b7fSEtienne Carriere 				      size_ncells);
241de56c16dSEtienne Carriere 		if (*size == DT_INFO_INVALID_REG_SIZE)
242de56c16dSEtienne Carriere 			return -FDT_ERR_NOTFOUND;
243de56c16dSEtienne Carriere 	}
244de56c16dSEtienne Carriere 
245de56c16dSEtienne Carriere 	return 0;
246de56c16dSEtienne Carriere }
247de56c16dSEtienne Carriere 
248*96e33b7fSEtienne Carriere int fdt_reg_info(const void *fdt, int offs, paddr_t *base, size_t *size)
249*96e33b7fSEtienne Carriere {
250*96e33b7fSEtienne Carriere 	return fdt_get_reg_props_by_index(fdt, offs, 0, base, size);
251*96e33b7fSEtienne Carriere }
252*96e33b7fSEtienne Carriere 
253de56c16dSEtienne Carriere paddr_t fdt_reg_base_address(const void *fdt, int offs)
254de56c16dSEtienne Carriere {
255de56c16dSEtienne Carriere 	paddr_t base = 0;
256de56c16dSEtienne Carriere 
257de56c16dSEtienne Carriere 	if (fdt_reg_info(fdt, offs, &base, NULL))
258de56c16dSEtienne Carriere 		return DT_INFO_INVALID_REG;
259de56c16dSEtienne Carriere 
260de56c16dSEtienne Carriere 	return base;
261de56c16dSEtienne Carriere }
262de56c16dSEtienne Carriere 
263de56c16dSEtienne Carriere size_t fdt_reg_size(const void *fdt, int offs)
264de56c16dSEtienne Carriere {
265de56c16dSEtienne Carriere 	size_t size = 0;
266de56c16dSEtienne Carriere 
267de56c16dSEtienne Carriere 	if (fdt_reg_info(fdt, offs, NULL, &size))
268df7cecc0SLionel Debieve 		return DT_INFO_INVALID_REG_SIZE;
2699fe4c797SJerome Forissier 
270de56c16dSEtienne Carriere 	return size;
2719fe4c797SJerome Forissier }
2729fe4c797SJerome Forissier 
2739fe4c797SJerome Forissier static bool is_okay(const char *st, int len)
2749fe4c797SJerome Forissier {
2759fe4c797SJerome Forissier 	return !strncmp(st, "ok", len) || !strncmp(st, "okay", len);
2769fe4c797SJerome Forissier }
2779fe4c797SJerome Forissier 
278f354a5d8SGatien Chevallier int fdt_get_status(const void *fdt, int offs)
2799fe4c797SJerome Forissier {
2809fe4c797SJerome Forissier 	const char *prop;
2819fe4c797SJerome Forissier 	int st = 0;
2829fe4c797SJerome Forissier 	int len;
2839fe4c797SJerome Forissier 
2849fe4c797SJerome Forissier 	prop = fdt_getprop(fdt, offs, "status", &len);
2859fe4c797SJerome Forissier 	if (!prop || is_okay(prop, len)) {
2869fe4c797SJerome Forissier 		/* If status is not specified, it defaults to "okay" */
2879fe4c797SJerome Forissier 		st |= DT_STATUS_OK_NSEC;
2889fe4c797SJerome Forissier 	}
2899fe4c797SJerome Forissier 
2909fe4c797SJerome Forissier 	prop = fdt_getprop(fdt, offs, "secure-status", &len);
2919fe4c797SJerome Forissier 	if (!prop) {
2929fe4c797SJerome Forissier 		/*
2939fe4c797SJerome Forissier 		 * When secure-status is not specified it defaults to the same
2949fe4c797SJerome Forissier 		 * value as status
2959fe4c797SJerome Forissier 		 */
2969fe4c797SJerome Forissier 		if (st & DT_STATUS_OK_NSEC)
2979fe4c797SJerome Forissier 			st |= DT_STATUS_OK_SEC;
2989fe4c797SJerome Forissier 	} else {
2999fe4c797SJerome Forissier 		if (is_okay(prop, len))
3009fe4c797SJerome Forissier 			st |= DT_STATUS_OK_SEC;
3019fe4c797SJerome Forissier 	}
3029fe4c797SJerome Forissier 
3039fe4c797SJerome Forissier 	return st;
3049fe4c797SJerome Forissier }
305c0cfb36cSEtienne Carriere 
306f354a5d8SGatien Chevallier void fdt_fill_device_info(const void *fdt, struct dt_node_info *info, int offs)
307c0cfb36cSEtienne Carriere {
308c0cfb36cSEtienne Carriere 	struct dt_node_info dinfo = {
309c0cfb36cSEtienne Carriere 		.reg = DT_INFO_INVALID_REG,
31006fd21ddSLionel Debieve 		.reg_size = DT_INFO_INVALID_REG_SIZE,
311c0cfb36cSEtienne Carriere 		.clock = DT_INFO_INVALID_CLOCK,
312c0cfb36cSEtienne Carriere 		.reset = DT_INFO_INVALID_RESET,
3137acb3a47SLudovic Barre 		.interrupt = DT_INFO_INVALID_INTERRUPT,
314c0cfb36cSEtienne Carriere 	};
315578bc4feSEtienne Carriere 	const fdt32_t *cuint = NULL;
316c0cfb36cSEtienne Carriere 
317de56c16dSEtienne Carriere 	/* Intentionally discard fdt_reg_info() return value */
318de56c16dSEtienne Carriere 	fdt_reg_info(fdt, offs, &dinfo.reg, &dinfo.reg_size);
319c0cfb36cSEtienne Carriere 
320c0cfb36cSEtienne Carriere 	cuint = fdt_getprop(fdt, offs, "clocks", NULL);
321c0cfb36cSEtienne Carriere 	if (cuint) {
322c0cfb36cSEtienne Carriere 		cuint++;
323c0cfb36cSEtienne Carriere 		dinfo.clock = (int)fdt32_to_cpu(*cuint);
324c0cfb36cSEtienne Carriere 	}
325c0cfb36cSEtienne Carriere 
326c0cfb36cSEtienne Carriere 	cuint = fdt_getprop(fdt, offs, "resets", NULL);
327c0cfb36cSEtienne Carriere 	if (cuint) {
328c0cfb36cSEtienne Carriere 		cuint++;
329c0cfb36cSEtienne Carriere 		dinfo.reset = (int)fdt32_to_cpu(*cuint);
330c0cfb36cSEtienne Carriere 	}
331c0cfb36cSEtienne Carriere 
332702fe5a7SClément Léger 	dinfo.interrupt = dt_get_irq_type_prio(fdt, offs, &dinfo.type,
333702fe5a7SClément Léger 					       &dinfo.prio);
3347acb3a47SLudovic Barre 
335f354a5d8SGatien Chevallier 	dinfo.status = fdt_get_status(fdt, offs);
336c0cfb36cSEtienne Carriere 
337c0cfb36cSEtienne Carriere 	*info = dinfo;
338c0cfb36cSEtienne Carriere }
339876826f3SGabriel Fernandez 
340f354a5d8SGatien Chevallier int fdt_read_uint32_array(const void *fdt, int node, const char *prop_name,
341876826f3SGabriel Fernandez 			  uint32_t *array, size_t count)
342876826f3SGabriel Fernandez {
343876826f3SGabriel Fernandez 	const fdt32_t *cuint = NULL;
344876826f3SGabriel Fernandez 	int len = 0;
345876826f3SGabriel Fernandez 	uint32_t i = 0;
346876826f3SGabriel Fernandez 
347876826f3SGabriel Fernandez 	cuint = fdt_getprop(fdt, node, prop_name, &len);
348876826f3SGabriel Fernandez 	if (!cuint)
34973e27bfaSGatien Chevallier 		return len;
350876826f3SGabriel Fernandez 
351876826f3SGabriel Fernandez 	if ((uint32_t)len != (count * sizeof(uint32_t)))
352876826f3SGabriel Fernandez 		return -FDT_ERR_BADLAYOUT;
353876826f3SGabriel Fernandez 
354876826f3SGabriel Fernandez 	for (i = 0; i < ((uint32_t)len / sizeof(uint32_t)); i++) {
355876826f3SGabriel Fernandez 		*array = fdt32_to_cpu(*cuint);
356876826f3SGabriel Fernandez 		array++;
357876826f3SGabriel Fernandez 		cuint++;
358876826f3SGabriel Fernandez 	}
359876826f3SGabriel Fernandez 
360876826f3SGabriel Fernandez 	return 0;
361876826f3SGabriel Fernandez }
362876826f3SGabriel Fernandez 
3637c3a6b7bSGatien Chevallier int fdt_read_uint32_index(const void *fdt, int node, const char *prop_name,
3647c3a6b7bSGatien Chevallier 			  int index, uint32_t *value)
3657c3a6b7bSGatien Chevallier {
3667c3a6b7bSGatien Chevallier 	const fdt32_t *cuint = NULL;
3677c3a6b7bSGatien Chevallier 	int len = 0;
3687c3a6b7bSGatien Chevallier 
3697c3a6b7bSGatien Chevallier 	cuint = fdt_getprop(fdt, node, prop_name, &len);
3707c3a6b7bSGatien Chevallier 	if (!cuint)
3717c3a6b7bSGatien Chevallier 		return len;
3727c3a6b7bSGatien Chevallier 
3737c3a6b7bSGatien Chevallier 	if ((uint32_t)len < (sizeof(uint32_t) * (index + 1)))
3747c3a6b7bSGatien Chevallier 		return -FDT_ERR_BADLAYOUT;
3757c3a6b7bSGatien Chevallier 
3767c3a6b7bSGatien Chevallier 	*value = fdt32_to_cpu(cuint[index]);
3777c3a6b7bSGatien Chevallier 
3787c3a6b7bSGatien Chevallier 	return 0;
3797c3a6b7bSGatien Chevallier }
3807c3a6b7bSGatien Chevallier 
381f354a5d8SGatien Chevallier int fdt_read_uint32(const void *fdt, int node, const char *prop_name,
382876826f3SGabriel Fernandez 		    uint32_t *value)
383876826f3SGabriel Fernandez {
384f354a5d8SGatien Chevallier 	return fdt_read_uint32_array(fdt, node, prop_name, value, 1);
385876826f3SGabriel Fernandez }
386876826f3SGabriel Fernandez 
387f354a5d8SGatien Chevallier uint32_t fdt_read_uint32_default(const void *fdt, int node,
388876826f3SGabriel Fernandez 				 const char *prop_name, uint32_t dflt_value)
389876826f3SGabriel Fernandez {
3907c3a6b7bSGatien Chevallier 	uint32_t ret = dflt_value;
391876826f3SGabriel Fernandez 
3927c3a6b7bSGatien Chevallier 	fdt_read_uint32_index(fdt, node, prop_name, 0, &ret);
393876826f3SGabriel Fernandez 
3947c3a6b7bSGatien Chevallier 	return ret;
395876826f3SGabriel Fernandez }
39607ced948SGatien Chevallier 
39707ced948SGatien Chevallier int fdt_get_reg_props_by_name(const void *fdt, int node, const char *name,
39807ced948SGatien Chevallier 			      paddr_t *base, size_t *size)
39907ced948SGatien Chevallier {
40007ced948SGatien Chevallier 	int index = 0;
40107ced948SGatien Chevallier 
40207ced948SGatien Chevallier 	index = fdt_stringlist_search(fdt, node, "reg-names", name);
40307ced948SGatien Chevallier 	if (index < 0)
40407ced948SGatien Chevallier 		return index;
40507ced948SGatien Chevallier 
40607ced948SGatien Chevallier 	return fdt_get_reg_props_by_index(fdt, node, index, base, size);
40707ced948SGatien Chevallier }
4084e45454aSJens Wiklander 
4094e45454aSJens Wiklander int dt_getprop_as_number(const void *fdt, int nodeoffset, const char *name,
4104e45454aSJens Wiklander 			 uint64_t *num)
4114e45454aSJens Wiklander {
4124e45454aSJens Wiklander 	const void *prop = NULL;
4134e45454aSJens Wiklander 	int len = 0;
4144e45454aSJens Wiklander 
4154e45454aSJens Wiklander 	prop = fdt_getprop(fdt, nodeoffset, name, &len);
4164e45454aSJens Wiklander 	if (!prop)
4174e45454aSJens Wiklander 		return len;
4184e45454aSJens Wiklander 
4194e45454aSJens Wiklander 	switch (len) {
4204e45454aSJens Wiklander 	case sizeof(uint32_t):
4214e45454aSJens Wiklander 		*num = fdt32_ld(prop);
4224e45454aSJens Wiklander 		return 0;
4234e45454aSJens Wiklander 	case sizeof(uint64_t):
4244e45454aSJens Wiklander 		*num = fdt64_ld(prop);
4254e45454aSJens Wiklander 		return 0;
4264e45454aSJens Wiklander 	default:
4274e45454aSJens Wiklander 		return -FDT_ERR_BADVALUE;
4284e45454aSJens Wiklander 	}
4294e45454aSJens Wiklander }
430e6027f48SAlvin Chang 
431e6027f48SAlvin Chang void *get_dt(void)
432e6027f48SAlvin Chang {
433e6027f48SAlvin Chang 	void *fdt = get_embedded_dt();
434e6027f48SAlvin Chang 
435e6027f48SAlvin Chang 	if (!fdt)
436e6027f48SAlvin Chang 		fdt = get_external_dt();
437e6027f48SAlvin Chang 
438c5e3e79fSSungbae Yoo 	if (!fdt)
439c5e3e79fSSungbae Yoo 		fdt = get_manifest_dt();
440c5e3e79fSSungbae Yoo 
441e6027f48SAlvin Chang 	return fdt;
442e6027f48SAlvin Chang }
443e6027f48SAlvin Chang 
444e6027f48SAlvin Chang void *get_secure_dt(void)
445e6027f48SAlvin Chang {
446e6027f48SAlvin Chang 	void *fdt = get_embedded_dt();
447e6027f48SAlvin Chang 
448e6027f48SAlvin Chang 	if (!fdt && IS_ENABLED(CFG_MAP_EXT_DT_SECURE))
449e6027f48SAlvin Chang 		fdt = get_external_dt();
450e6027f48SAlvin Chang 
451c5e3e79fSSungbae Yoo 	if (!fdt)
452c5e3e79fSSungbae Yoo 		fdt = get_manifest_dt();
453c5e3e79fSSungbae Yoo 
454e6027f48SAlvin Chang 	return fdt;
455e6027f48SAlvin Chang }
456e6027f48SAlvin Chang 
457e6027f48SAlvin Chang #if defined(CFG_EMBED_DTB)
458578bc4feSEtienne Carriere #ifdef CFG_DT_CACHED_NODE_INFO
459578bc4feSEtienne Carriere /*
460578bc4feSEtienne Carriere  * struct cached_node - Cached information of a DT node
461578bc4feSEtienne Carriere  *
462578bc4feSEtienne Carriere  * @node_offset: Offset of the node in @cached_node_info_fdt
463578bc4feSEtienne Carriere  * @parent_offset: Offset of @node_offset parent node
464578bc4feSEtienne Carriere  * @address_cells: #address-cells property value of the parent node or 0
465578bc4feSEtienne Carriere  * @size_cells: #size-cells property value of the parent node or 0
466578bc4feSEtienne Carriere  * @phandle: Phandle associated to the node or 0 if none
467578bc4feSEtienne Carriere  */
468578bc4feSEtienne Carriere struct cached_node {
469578bc4feSEtienne Carriere 	int node_offset;
470578bc4feSEtienne Carriere 	int parent_offset;
471578bc4feSEtienne Carriere 	int8_t address_cells;
472578bc4feSEtienne Carriere 	int8_t size_cells;
473578bc4feSEtienne Carriere 	uint32_t phandle;
474578bc4feSEtienne Carriere };
475578bc4feSEtienne Carriere 
476578bc4feSEtienne Carriere /*
477578bc4feSEtienne Carriere  * struct dt_node_cache - Reference to cached information of DT nodes
478578bc4feSEtienne Carriere  *
479578bc4feSEtienne Carriere  * @array: Array of the cached node
480578bc4feSEtienne Carriere  * @count: Number of initialized cells in @array
481578bc4feSEtienne Carriere  * @alloced_count: Number of allocated cells in @array
482578bc4feSEtienne Carriere  * @fdt: Reference to the FDT for which node information are cached
483578bc4feSEtienne Carriere  */
484578bc4feSEtienne Carriere struct dt_node_cache {
485578bc4feSEtienne Carriere 	struct cached_node *array;
486578bc4feSEtienne Carriere 	size_t count;
487578bc4feSEtienne Carriere 	size_t alloced_count;
488578bc4feSEtienne Carriere 	const void *fdt;
489578bc4feSEtienne Carriere };
490578bc4feSEtienne Carriere 
491578bc4feSEtienne Carriere static struct dt_node_cache *dt_node_cache;
492578bc4feSEtienne Carriere 
493578bc4feSEtienne Carriere static bool fdt_node_info_are_cached(const void *fdt)
494578bc4feSEtienne Carriere {
495578bc4feSEtienne Carriere 	return dt_node_cache && dt_node_cache->fdt == fdt;
496578bc4feSEtienne Carriere }
497578bc4feSEtienne Carriere 
498578bc4feSEtienne Carriere static struct cached_node *find_cached_parent_node(const void *fdt,
499578bc4feSEtienne Carriere 						   int node_offset)
500578bc4feSEtienne Carriere {
501578bc4feSEtienne Carriere 	struct cached_node *cell = NULL;
502578bc4feSEtienne Carriere 	size_t n = 0;
503578bc4feSEtienne Carriere 
504578bc4feSEtienne Carriere 	if (!fdt_node_info_are_cached(fdt))
505578bc4feSEtienne Carriere 		return NULL;
506578bc4feSEtienne Carriere 
507578bc4feSEtienne Carriere 	for (n = 0; n < dt_node_cache->count; n++)
508578bc4feSEtienne Carriere 		if (dt_node_cache->array[n].node_offset == node_offset)
509578bc4feSEtienne Carriere 			cell = dt_node_cache->array + n;
510578bc4feSEtienne Carriere 
511578bc4feSEtienne Carriere 	return cell;
512578bc4feSEtienne Carriere }
513578bc4feSEtienne Carriere 
514578bc4feSEtienne Carriere int fdt_find_cached_parent_node(const void *fdt, int node_offset,
515578bc4feSEtienne Carriere 				int *parent_offset)
516578bc4feSEtienne Carriere {
517578bc4feSEtienne Carriere 	struct cached_node *cell = NULL;
518578bc4feSEtienne Carriere 
519578bc4feSEtienne Carriere 	cell = find_cached_parent_node(fdt, node_offset);
520578bc4feSEtienne Carriere 	if (!cell)
521578bc4feSEtienne Carriere 		return -FDT_ERR_NOTFOUND;
522578bc4feSEtienne Carriere 
523578bc4feSEtienne Carriere 	*parent_offset = cell->parent_offset;
524578bc4feSEtienne Carriere 
525578bc4feSEtienne Carriere 	return 0;
526578bc4feSEtienne Carriere }
527578bc4feSEtienne Carriere 
528578bc4feSEtienne Carriere int fdt_find_cached_parent_reg_cells(const void *fdt, int node_offset,
529578bc4feSEtienne Carriere 				     int *address_cells, int *size_cells)
530578bc4feSEtienne Carriere {
531578bc4feSEtienne Carriere 	struct cached_node *cell = NULL;
532578bc4feSEtienne Carriere 	int rc = 0;
533578bc4feSEtienne Carriere 
534578bc4feSEtienne Carriere 	cell = find_cached_parent_node(fdt, node_offset);
535578bc4feSEtienne Carriere 	if (!cell)
536578bc4feSEtienne Carriere 		return -FDT_ERR_NOTFOUND;
537578bc4feSEtienne Carriere 
538578bc4feSEtienne Carriere 	if (address_cells) {
539578bc4feSEtienne Carriere 		if (cell->address_cells >= 0)
540578bc4feSEtienne Carriere 			*address_cells = cell->address_cells;
541578bc4feSEtienne Carriere 		else
542578bc4feSEtienne Carriere 			rc = -FDT_ERR_NOTFOUND;
543578bc4feSEtienne Carriere 	}
544578bc4feSEtienne Carriere 
545578bc4feSEtienne Carriere 	if (size_cells) {
546578bc4feSEtienne Carriere 		if (cell->size_cells >= 0)
547578bc4feSEtienne Carriere 			*size_cells = cell->size_cells;
548578bc4feSEtienne Carriere 		else
549578bc4feSEtienne Carriere 			rc = -FDT_ERR_NOTFOUND;
550578bc4feSEtienne Carriere 	}
551578bc4feSEtienne Carriere 
552578bc4feSEtienne Carriere 	return rc;
553578bc4feSEtienne Carriere }
554578bc4feSEtienne Carriere 
555578bc4feSEtienne Carriere int fdt_find_cached_node_phandle(const void *fdt, uint32_t phandle,
556578bc4feSEtienne Carriere 				 int *node_offset)
557578bc4feSEtienne Carriere {
558578bc4feSEtienne Carriere 	struct cached_node *cell = NULL;
559578bc4feSEtienne Carriere 	size_t n = 0;
560578bc4feSEtienne Carriere 
561578bc4feSEtienne Carriere 	if (!fdt_node_info_are_cached(fdt))
562578bc4feSEtienne Carriere 		return -FDT_ERR_NOTFOUND;
563578bc4feSEtienne Carriere 
564578bc4feSEtienne Carriere 	for (n = 0; n < dt_node_cache->count; n++)
565578bc4feSEtienne Carriere 		if (dt_node_cache->array[n].phandle == phandle)
566578bc4feSEtienne Carriere 			cell = dt_node_cache->array + n;
567578bc4feSEtienne Carriere 
568578bc4feSEtienne Carriere 	if (!cell)
569578bc4feSEtienne Carriere 		return -FDT_ERR_NOTFOUND;
570578bc4feSEtienne Carriere 
571578bc4feSEtienne Carriere 	*node_offset = cell->node_offset;
572578bc4feSEtienne Carriere 
573578bc4feSEtienne Carriere 	return 0;
574578bc4feSEtienne Carriere }
575578bc4feSEtienne Carriere 
576578bc4feSEtienne Carriere static TEE_Result realloc_cached_node_array(void)
577578bc4feSEtienne Carriere {
578578bc4feSEtienne Carriere 	assert(dt_node_cache);
579578bc4feSEtienne Carriere 
580578bc4feSEtienne Carriere 	if (dt_node_cache->count + 1 > dt_node_cache->alloced_count) {
581578bc4feSEtienne Carriere 		size_t new_count = dt_node_cache->alloced_count * 2;
582578bc4feSEtienne Carriere 		struct cached_node *new = NULL;
583578bc4feSEtienne Carriere 
584578bc4feSEtienne Carriere 		if (!new_count)
585578bc4feSEtienne Carriere 			new_count = 4;
586578bc4feSEtienne Carriere 
587578bc4feSEtienne Carriere 		new = realloc(dt_node_cache->array,
588578bc4feSEtienne Carriere 			      sizeof(*dt_node_cache->array) * new_count);
589578bc4feSEtienne Carriere 		if (!new)
590578bc4feSEtienne Carriere 			return TEE_ERROR_OUT_OF_MEMORY;
591578bc4feSEtienne Carriere 
592578bc4feSEtienne Carriere 		dt_node_cache->array = new;
593578bc4feSEtienne Carriere 		dt_node_cache->alloced_count = new_count;
594578bc4feSEtienne Carriere 	}
595578bc4feSEtienne Carriere 
596578bc4feSEtienne Carriere 	return TEE_SUCCESS;
597578bc4feSEtienne Carriere }
598578bc4feSEtienne Carriere 
599578bc4feSEtienne Carriere static TEE_Result add_cached_node(int parent_offset,
600578bc4feSEtienne Carriere 				  int node_offset, int address_cells,
601578bc4feSEtienne Carriere 				  int size_cells)
602578bc4feSEtienne Carriere {
603578bc4feSEtienne Carriere 	TEE_Result res = TEE_ERROR_GENERIC;
604578bc4feSEtienne Carriere 
605578bc4feSEtienne Carriere 	res = realloc_cached_node_array();
606578bc4feSEtienne Carriere 	if (res)
607578bc4feSEtienne Carriere 		return res;
608578bc4feSEtienne Carriere 
609578bc4feSEtienne Carriere 	dt_node_cache->array[dt_node_cache->count] = (struct cached_node){
610578bc4feSEtienne Carriere 		.node_offset = node_offset,
611578bc4feSEtienne Carriere 		.parent_offset = parent_offset,
612578bc4feSEtienne Carriere 		.address_cells = address_cells,
613578bc4feSEtienne Carriere 		.size_cells = size_cells,
614578bc4feSEtienne Carriere 		.phandle = fdt_get_phandle(dt_node_cache->fdt, node_offset),
615578bc4feSEtienne Carriere 	};
616578bc4feSEtienne Carriere 
617578bc4feSEtienne Carriere 	dt_node_cache->count++;
618578bc4feSEtienne Carriere 
619578bc4feSEtienne Carriere 	return TEE_SUCCESS;
620578bc4feSEtienne Carriere }
621578bc4feSEtienne Carriere 
622578bc4feSEtienne Carriere static TEE_Result add_cached_node_subtree(int node_offset)
623578bc4feSEtienne Carriere {
624578bc4feSEtienne Carriere 	TEE_Result res = TEE_ERROR_GENERIC;
625578bc4feSEtienne Carriere 	const fdt32_t *cuint = NULL;
626578bc4feSEtienne Carriere 	int subnode_offset = 0;
627578bc4feSEtienne Carriere 	int8_t addr_cells = -1;
628578bc4feSEtienne Carriere 	int8_t size_cells = -1;
629578bc4feSEtienne Carriere 
630578bc4feSEtienne Carriere 	cuint = fdt_getprop(dt_node_cache->fdt, node_offset, "#address-cells",
631578bc4feSEtienne Carriere 			    NULL);
632578bc4feSEtienne Carriere 	if (cuint)
633578bc4feSEtienne Carriere 		addr_cells = (int)fdt32_to_cpu(*cuint);
634578bc4feSEtienne Carriere 
635578bc4feSEtienne Carriere 	cuint = fdt_getprop(dt_node_cache->fdt, node_offset, "#size-cells",
636578bc4feSEtienne Carriere 			    NULL);
637578bc4feSEtienne Carriere 	if (cuint)
638578bc4feSEtienne Carriere 		size_cells = (int)fdt32_to_cpu(*cuint);
639578bc4feSEtienne Carriere 
640578bc4feSEtienne Carriere 	fdt_for_each_subnode(subnode_offset, dt_node_cache->fdt, node_offset) {
641578bc4feSEtienne Carriere 		res = add_cached_node(node_offset, subnode_offset, addr_cells,
642578bc4feSEtienne Carriere 				      size_cells);
643578bc4feSEtienne Carriere 		if (res)
644578bc4feSEtienne Carriere 			return res;
645578bc4feSEtienne Carriere 
646578bc4feSEtienne Carriere 		res = add_cached_node_subtree(subnode_offset);
647578bc4feSEtienne Carriere 		if (res)
648578bc4feSEtienne Carriere 			return res;
649578bc4feSEtienne Carriere 	}
650578bc4feSEtienne Carriere 
651578bc4feSEtienne Carriere 	return TEE_SUCCESS;
652578bc4feSEtienne Carriere }
653578bc4feSEtienne Carriere 
654578bc4feSEtienne Carriere static TEE_Result release_node_cache_info(void)
655578bc4feSEtienne Carriere {
656578bc4feSEtienne Carriere 	if (dt_node_cache) {
657578bc4feSEtienne Carriere 		free(dt_node_cache->array);
658578bc4feSEtienne Carriere 		free(dt_node_cache);
659578bc4feSEtienne Carriere 		dt_node_cache = NULL;
660578bc4feSEtienne Carriere 	}
661578bc4feSEtienne Carriere 
662578bc4feSEtienne Carriere 	return TEE_SUCCESS;
663578bc4feSEtienne Carriere }
664578bc4feSEtienne Carriere 
665578bc4feSEtienne Carriere release_init_resource(release_node_cache_info);
666578bc4feSEtienne Carriere 
667578bc4feSEtienne Carriere static void init_node_cache_info(const void *fdt)
668578bc4feSEtienne Carriere {
669578bc4feSEtienne Carriere 	TEE_Result res = TEE_ERROR_GENERIC;
670578bc4feSEtienne Carriere 
671578bc4feSEtienne Carriere 	assert(!dt_node_cache);
672578bc4feSEtienne Carriere 
673578bc4feSEtienne Carriere 	dt_node_cache = calloc(1, sizeof(*dt_node_cache));
674578bc4feSEtienne Carriere 	if (dt_node_cache) {
675578bc4feSEtienne Carriere 		dt_node_cache->fdt = fdt;
676578bc4feSEtienne Carriere 		res = add_cached_node_subtree(0);
677578bc4feSEtienne Carriere 	} else {
678578bc4feSEtienne Carriere 		res = TEE_ERROR_OUT_OF_MEMORY;
679578bc4feSEtienne Carriere 	}
680578bc4feSEtienne Carriere 
681578bc4feSEtienne Carriere 	if (res) {
682578bc4feSEtienne Carriere 		EMSG("Error %#"PRIx32", disable DT cached info", res);
683578bc4feSEtienne Carriere 		release_node_cache_info();
684578bc4feSEtienne Carriere 	}
685578bc4feSEtienne Carriere }
686578bc4feSEtienne Carriere #else
687578bc4feSEtienne Carriere static void init_node_cache_info(const void *fdt __unused)
688578bc4feSEtienne Carriere {
689578bc4feSEtienne Carriere }
690578bc4feSEtienne Carriere #endif /* CFG_DT_CACHED_NODE_INFO */
691578bc4feSEtienne Carriere 
692e6027f48SAlvin Chang void *get_embedded_dt(void)
693e6027f48SAlvin Chang {
694e6027f48SAlvin Chang 	static bool checked;
695e6027f48SAlvin Chang 
696e6027f48SAlvin Chang 	assert(cpu_mmu_enabled());
697e6027f48SAlvin Chang 
698e6027f48SAlvin Chang 	if (!checked) {
699e6027f48SAlvin Chang 		IMSG("Embedded DTB found");
700e6027f48SAlvin Chang 
701e6027f48SAlvin Chang 		if (fdt_check_header(embedded_secure_dtb))
702e6027f48SAlvin Chang 			panic("Invalid embedded DTB");
703e6027f48SAlvin Chang 
704e6027f48SAlvin Chang 		checked = true;
705578bc4feSEtienne Carriere 
706578bc4feSEtienne Carriere 		init_node_cache_info(embedded_secure_dtb);
707e6027f48SAlvin Chang 	}
708e6027f48SAlvin Chang 
709e6027f48SAlvin Chang 	return embedded_secure_dtb;
710e6027f48SAlvin Chang }
711e6027f48SAlvin Chang #else
712e6027f48SAlvin Chang void *get_embedded_dt(void)
713e6027f48SAlvin Chang {
714e6027f48SAlvin Chang 	return NULL;
715e6027f48SAlvin Chang }
716e6027f48SAlvin Chang #endif /*CFG_EMBED_DTB*/
717e6027f48SAlvin Chang 
718e6027f48SAlvin Chang #ifdef _CFG_USE_DTB_OVERLAY
719e6027f48SAlvin Chang static int add_dt_overlay_fragment(struct dt_descriptor *dt, int ioffs)
720e6027f48SAlvin Chang {
7210c49b6d6SAlvin Chang 	char frag[32] = { };
7220c49b6d6SAlvin Chang 	int offs = 0;
7230c49b6d6SAlvin Chang 	int ret = 0;
724e6027f48SAlvin Chang 
725a039ffc6SClement Faure 	ret = snprintf(frag, sizeof(frag), "fragment@%d", dt->frag_id);
726a039ffc6SClement Faure 	if (ret < 0 || (size_t)ret >= sizeof(frag))
727a039ffc6SClement Faure 		return -1;
728a039ffc6SClement Faure 
729e6027f48SAlvin Chang 	offs = fdt_add_subnode(dt->blob, ioffs, frag);
730e6027f48SAlvin Chang 	if (offs < 0)
731e6027f48SAlvin Chang 		return offs;
732e6027f48SAlvin Chang 
733e6027f48SAlvin Chang 	dt->frag_id += 1;
734e6027f48SAlvin Chang 
735e6027f48SAlvin Chang 	ret = fdt_setprop_string(dt->blob, offs, "target-path", "/");
736e6027f48SAlvin Chang 	if (ret < 0)
7370c49b6d6SAlvin Chang 		return ret;
738e6027f48SAlvin Chang 
739e6027f48SAlvin Chang 	return fdt_add_subnode(dt->blob, offs, "__overlay__");
740e6027f48SAlvin Chang }
741e6027f48SAlvin Chang 
742e6027f48SAlvin Chang static int init_dt_overlay(struct dt_descriptor *dt, int __maybe_unused dt_size)
743e6027f48SAlvin Chang {
7440c49b6d6SAlvin Chang 	int fragment = 0;
745e6027f48SAlvin Chang 
746e6027f48SAlvin Chang 	if (IS_ENABLED(CFG_EXTERNAL_DTB_OVERLAY)) {
747e6027f48SAlvin Chang 		if (!fdt_check_header(dt->blob)) {
748e6027f48SAlvin Chang 			fdt_for_each_subnode(fragment, dt->blob, 0)
749e6027f48SAlvin Chang 				dt->frag_id += 1;
750e6027f48SAlvin Chang 			return 0;
751e6027f48SAlvin Chang 		}
752e6027f48SAlvin Chang 	}
753e6027f48SAlvin Chang 
754e6027f48SAlvin Chang 	return fdt_create_empty_tree(dt->blob, dt_size);
755e6027f48SAlvin Chang }
756e6027f48SAlvin Chang #else
757e6027f48SAlvin Chang static int add_dt_overlay_fragment(struct dt_descriptor *dt __unused, int offs)
758e6027f48SAlvin Chang {
759e6027f48SAlvin Chang 	return offs;
760e6027f48SAlvin Chang }
761e6027f48SAlvin Chang 
762e6027f48SAlvin Chang static int init_dt_overlay(struct dt_descriptor *dt __unused,
763e6027f48SAlvin Chang 			   int dt_size __unused)
764e6027f48SAlvin Chang {
765e6027f48SAlvin Chang 	return 0;
766e6027f48SAlvin Chang }
767e6027f48SAlvin Chang #endif /* _CFG_USE_DTB_OVERLAY */
768e6027f48SAlvin Chang 
769e6027f48SAlvin Chang struct dt_descriptor *get_external_dt_desc(void)
770e6027f48SAlvin Chang {
771e6027f48SAlvin Chang 	if (!IS_ENABLED(CFG_EXTERNAL_DT))
772e6027f48SAlvin Chang 		return NULL;
773e6027f48SAlvin Chang 
774e6027f48SAlvin Chang 	return &external_dt;
775e6027f48SAlvin Chang }
776e6027f48SAlvin Chang 
777dcff802bSRaymond Mao void init_external_dt(unsigned long phys_dt, size_t dt_sz)
778e6027f48SAlvin Chang {
779e6027f48SAlvin Chang 	struct dt_descriptor *dt = &external_dt;
7800c49b6d6SAlvin Chang 	int ret = 0;
78166763721SRaymond Mao 	enum teecore_memtypes mtype = MEM_AREA_MAXTYPE;
782e6027f48SAlvin Chang 
783e6027f48SAlvin Chang 	if (!IS_ENABLED(CFG_EXTERNAL_DT))
784e6027f48SAlvin Chang 		return;
785e6027f48SAlvin Chang 
786dcff802bSRaymond Mao 	if (!phys_dt || !dt_sz) {
787e6027f48SAlvin Chang 		/*
788e6027f48SAlvin Chang 		 * No need to panic as we're not using the DT in OP-TEE
789e6027f48SAlvin Chang 		 * yet, we're only adding some nodes for normal world use.
790e6027f48SAlvin Chang 		 * This makes the switch to using DT easier as we can boot
791e6027f48SAlvin Chang 		 * a newer OP-TEE with older boot loaders. Once we start to
792e6027f48SAlvin Chang 		 * initialize devices based on DT we'll likely panic
793e6027f48SAlvin Chang 		 * instead of returning here.
794e6027f48SAlvin Chang 		 */
795e6027f48SAlvin Chang 		IMSG("No non-secure external DT");
796e6027f48SAlvin Chang 		return;
797e6027f48SAlvin Chang 	}
798e6027f48SAlvin Chang 
79966763721SRaymond Mao 	mtype = core_mmu_get_type_by_pa(phys_dt);
80066763721SRaymond Mao 	if (mtype == MEM_AREA_MAXTYPE) {
80166763721SRaymond Mao 		/* Map the DTB if it is not yet mapped */
80266763721SRaymond Mao 		dt->blob = core_mmu_add_mapping(MEM_AREA_EXT_DT, phys_dt,
803dcff802bSRaymond Mao 						dt_sz);
80466763721SRaymond Mao 		if (!dt->blob)
805e6027f48SAlvin Chang 			panic("Failed to map external DTB");
80666763721SRaymond Mao 	} else {
80766763721SRaymond Mao 		/* Get the DTB address if already mapped in a memory area */
808dcff802bSRaymond Mao 		dt->blob = phys_to_virt(phys_dt, mtype, dt_sz);
80966763721SRaymond Mao 		if (!dt->blob) {
81066763721SRaymond Mao 			EMSG("Failed to get a mapped external DTB for PA %#lx",
81166763721SRaymond Mao 			     phys_dt);
81266763721SRaymond Mao 			panic();
81366763721SRaymond Mao 		}
81466763721SRaymond Mao 	}
815e6027f48SAlvin Chang 
816dcff802bSRaymond Mao 	ret = init_dt_overlay(dt, dt_sz);
817e6027f48SAlvin Chang 	if (ret < 0) {
818e6027f48SAlvin Chang 		EMSG("Device Tree Overlay init fail @ %#lx: error %d", phys_dt,
819e6027f48SAlvin Chang 		     ret);
820e6027f48SAlvin Chang 		panic();
821e6027f48SAlvin Chang 	}
822e6027f48SAlvin Chang 
823dcff802bSRaymond Mao 	ret = fdt_open_into(dt->blob, dt->blob, dt_sz);
824e6027f48SAlvin Chang 	if (ret < 0) {
825e6027f48SAlvin Chang 		EMSG("Invalid Device Tree at %#lx: error %d", phys_dt, ret);
826e6027f48SAlvin Chang 		panic();
827e6027f48SAlvin Chang 	}
828e6027f48SAlvin Chang 
829e6027f48SAlvin Chang 	IMSG("Non-secure external DT found");
830e6027f48SAlvin Chang }
831e6027f48SAlvin Chang 
832e6027f48SAlvin Chang void *get_external_dt(void)
833e6027f48SAlvin Chang {
834e6027f48SAlvin Chang 	if (!IS_ENABLED(CFG_EXTERNAL_DT))
835e6027f48SAlvin Chang 		return NULL;
836e6027f48SAlvin Chang 
837e6027f48SAlvin Chang 	assert(cpu_mmu_enabled());
838e6027f48SAlvin Chang 	return external_dt.blob;
839e6027f48SAlvin Chang }
840e6027f48SAlvin Chang 
841e6027f48SAlvin Chang static TEE_Result release_external_dt(void)
842e6027f48SAlvin Chang {
843e6027f48SAlvin Chang 	int ret = 0;
84466763721SRaymond Mao 	paddr_t pa_dt = 0;
845e6027f48SAlvin Chang 
846e6027f48SAlvin Chang 	if (!IS_ENABLED(CFG_EXTERNAL_DT))
847e6027f48SAlvin Chang 		return TEE_SUCCESS;
848e6027f48SAlvin Chang 
849e6027f48SAlvin Chang 	if (!external_dt.blob)
850e6027f48SAlvin Chang 		return TEE_SUCCESS;
851e6027f48SAlvin Chang 
85266763721SRaymond Mao 	pa_dt = virt_to_phys(external_dt.blob);
85366763721SRaymond Mao 	/*
85466763721SRaymond Mao 	 * Skip packing and un-mapping operations if the external DTB is mapped
85566763721SRaymond Mao 	 * in a different memory area
85666763721SRaymond Mao 	 */
85766763721SRaymond Mao 	if (core_mmu_get_type_by_pa(pa_dt) != MEM_AREA_EXT_DT)
85866763721SRaymond Mao 		return TEE_SUCCESS;
85966763721SRaymond Mao 
860e6027f48SAlvin Chang 	ret = fdt_pack(external_dt.blob);
861e6027f48SAlvin Chang 	if (ret < 0) {
862e6027f48SAlvin Chang 		EMSG("Failed to pack Device Tree at 0x%" PRIxPA ": error %d",
863e6027f48SAlvin Chang 		     virt_to_phys(external_dt.blob), ret);
864e6027f48SAlvin Chang 		panic();
865e6027f48SAlvin Chang 	}
866e6027f48SAlvin Chang 
867e6027f48SAlvin Chang 	if (core_mmu_remove_mapping(MEM_AREA_EXT_DT, external_dt.blob,
868e6027f48SAlvin Chang 				    CFG_DTB_MAX_SIZE))
869e6027f48SAlvin Chang 		panic("Failed to remove temporary Device Tree mapping");
870e6027f48SAlvin Chang 
871e6027f48SAlvin Chang 	/* External DTB no more reached, reset pointer to invalid */
872e6027f48SAlvin Chang 	external_dt.blob = NULL;
873e6027f48SAlvin Chang 
874e6027f48SAlvin Chang 	return TEE_SUCCESS;
875e6027f48SAlvin Chang }
876e6027f48SAlvin Chang 
877e6027f48SAlvin Chang boot_final(release_external_dt);
878e6027f48SAlvin Chang 
879e6027f48SAlvin Chang int add_dt_path_subnode(struct dt_descriptor *dt, const char *path,
880e6027f48SAlvin Chang 			const char *subnode)
881e6027f48SAlvin Chang {
8820c49b6d6SAlvin Chang 	int offs = 0;
883e6027f48SAlvin Chang 
884e6027f48SAlvin Chang 	offs = fdt_path_offset(dt->blob, path);
885e6027f48SAlvin Chang 	if (offs < 0)
8860c49b6d6SAlvin Chang 		return offs;
887e6027f48SAlvin Chang 	offs = add_dt_overlay_fragment(dt, offs);
888e6027f48SAlvin Chang 	if (offs < 0)
889e6027f48SAlvin Chang 		return offs;
8900c49b6d6SAlvin Chang 	return fdt_add_subnode(dt->blob, offs, subnode);
891e6027f48SAlvin Chang }
892e6027f48SAlvin Chang 
893e6027f48SAlvin Chang static void set_dt_val(void *data, uint32_t cell_size, uint64_t val)
894e6027f48SAlvin Chang {
895e6027f48SAlvin Chang 	if (cell_size == 1) {
896e6027f48SAlvin Chang 		fdt32_t v = cpu_to_fdt32((uint32_t)val);
897e6027f48SAlvin Chang 
898e6027f48SAlvin Chang 		memcpy(data, &v, sizeof(v));
899e6027f48SAlvin Chang 	} else {
900e6027f48SAlvin Chang 		fdt64_t v = cpu_to_fdt64(val);
901e6027f48SAlvin Chang 
902e6027f48SAlvin Chang 		memcpy(data, &v, sizeof(v));
903e6027f48SAlvin Chang 	}
904e6027f48SAlvin Chang }
905e6027f48SAlvin Chang 
906e6027f48SAlvin Chang int add_res_mem_dt_node(struct dt_descriptor *dt, const char *name,
907e6027f48SAlvin Chang 			paddr_t pa, size_t size)
908e6027f48SAlvin Chang {
909e6027f48SAlvin Chang 	int offs = 0;
910e6027f48SAlvin Chang 	int ret = 0;
911e6027f48SAlvin Chang 	int addr_size = -1;
912e6027f48SAlvin Chang 	int len_size = -1;
913e6027f48SAlvin Chang 	bool found = true;
9140c49b6d6SAlvin Chang 	char subnode_name[80] = { };
915e6027f48SAlvin Chang 
916e6027f48SAlvin Chang 	offs = fdt_path_offset(dt->blob, "/reserved-memory");
917e6027f48SAlvin Chang 
918e6027f48SAlvin Chang 	if (offs < 0) {
919e6027f48SAlvin Chang 		found = false;
920e6027f48SAlvin Chang 		offs = 0;
921e6027f48SAlvin Chang 	}
922e6027f48SAlvin Chang 
923e6027f48SAlvin Chang 	if (IS_ENABLED2(_CFG_USE_DTB_OVERLAY)) {
924e6027f48SAlvin Chang 		len_size = sizeof(paddr_t) / sizeof(uint32_t);
925e6027f48SAlvin Chang 		addr_size = sizeof(paddr_t) / sizeof(uint32_t);
926e6027f48SAlvin Chang 	} else {
927e6027f48SAlvin Chang 		len_size = fdt_size_cells(dt->blob, offs);
928e6027f48SAlvin Chang 		if (len_size < 0)
9290c49b6d6SAlvin Chang 			return len_size;
930e6027f48SAlvin Chang 		addr_size = fdt_address_cells(dt->blob, offs);
931e6027f48SAlvin Chang 		if (addr_size < 0)
9320c49b6d6SAlvin Chang 			return addr_size;
933e6027f48SAlvin Chang 	}
934e6027f48SAlvin Chang 
935e6027f48SAlvin Chang 	if (!found) {
936e6027f48SAlvin Chang 		offs = add_dt_path_subnode(dt, "/", "reserved-memory");
937e6027f48SAlvin Chang 		if (offs < 0)
9380c49b6d6SAlvin Chang 			return offs;
939e6027f48SAlvin Chang 		ret = fdt_setprop_cell(dt->blob, offs, "#address-cells",
940e6027f48SAlvin Chang 				       addr_size);
941e6027f48SAlvin Chang 		if (ret < 0)
9420c49b6d6SAlvin Chang 			return ret;
943e6027f48SAlvin Chang 		ret = fdt_setprop_cell(dt->blob, offs, "#size-cells", len_size);
944e6027f48SAlvin Chang 		if (ret < 0)
9450c49b6d6SAlvin Chang 			return ret;
946e6027f48SAlvin Chang 		ret = fdt_setprop(dt->blob, offs, "ranges", NULL, 0);
947e6027f48SAlvin Chang 		if (ret < 0)
9480c49b6d6SAlvin Chang 			return ret;
949e6027f48SAlvin Chang 	}
950e6027f48SAlvin Chang 
951e6027f48SAlvin Chang 	ret = snprintf(subnode_name, sizeof(subnode_name),
952e6027f48SAlvin Chang 		       "%s@%" PRIxPA, name, pa);
953e6027f48SAlvin Chang 	if (ret < 0 || ret >= (int)sizeof(subnode_name))
954e6027f48SAlvin Chang 		DMSG("truncated node \"%s@%" PRIxPA"\"", name, pa);
955e6027f48SAlvin Chang 	offs = fdt_add_subnode(dt->blob, offs, subnode_name);
956e6027f48SAlvin Chang 	if (offs >= 0) {
9570c49b6d6SAlvin Chang 		uint32_t data[FDT_MAX_NCELLS * 2] = { };
958e6027f48SAlvin Chang 
959e6027f48SAlvin Chang 		set_dt_val(data, addr_size, pa);
960e6027f48SAlvin Chang 		set_dt_val(data + addr_size, len_size, size);
961e6027f48SAlvin Chang 		ret = fdt_setprop(dt->blob, offs, "reg", data,
962e6027f48SAlvin Chang 				  sizeof(uint32_t) * (addr_size + len_size));
963e6027f48SAlvin Chang 		if (ret < 0)
9640c49b6d6SAlvin Chang 			return ret;
965e6027f48SAlvin Chang 		ret = fdt_setprop(dt->blob, offs, "no-map", NULL, 0);
966e6027f48SAlvin Chang 		if (ret < 0)
9670c49b6d6SAlvin Chang 			return ret;
968e6027f48SAlvin Chang 	} else {
9690c49b6d6SAlvin Chang 		return offs;
970e6027f48SAlvin Chang 	}
971e6027f48SAlvin Chang 	return 0;
972e6027f48SAlvin Chang }
9734bc2c5f0SSungbae Yoo 
9744bc2c5f0SSungbae Yoo #if defined(CFG_CORE_FFA)
9754bc2c5f0SSungbae Yoo void init_manifest_dt(void *fdt)
9764bc2c5f0SSungbae Yoo {
9774bc2c5f0SSungbae Yoo 	manifest_dt = fdt;
9784bc2c5f0SSungbae Yoo }
9794bc2c5f0SSungbae Yoo 
9804bc2c5f0SSungbae Yoo void reinit_manifest_dt(void)
9814bc2c5f0SSungbae Yoo {
9824bc2c5f0SSungbae Yoo 	paddr_t pa = (unsigned long)manifest_dt;
9834bc2c5f0SSungbae Yoo 	void *fdt = NULL;
9844bc2c5f0SSungbae Yoo 	int ret = 0;
9854bc2c5f0SSungbae Yoo 
9864bc2c5f0SSungbae Yoo 	if (!pa) {
9874bc2c5f0SSungbae Yoo 		EMSG("No manifest DT found");
9884bc2c5f0SSungbae Yoo 		return;
9894bc2c5f0SSungbae Yoo 	}
9904bc2c5f0SSungbae Yoo 
9914bc2c5f0SSungbae Yoo 	fdt = core_mmu_add_mapping(MEM_AREA_MANIFEST_DT, pa, CFG_DTB_MAX_SIZE);
9924bc2c5f0SSungbae Yoo 	if (!fdt)
9934bc2c5f0SSungbae Yoo 		panic("Failed to map manifest DT");
9944bc2c5f0SSungbae Yoo 
9954bc2c5f0SSungbae Yoo 	manifest_dt = fdt;
9964bc2c5f0SSungbae Yoo 
9974bc2c5f0SSungbae Yoo 	ret = fdt_check_full(fdt, CFG_DTB_MAX_SIZE);
9984bc2c5f0SSungbae Yoo 	if (ret < 0) {
9994bc2c5f0SSungbae Yoo 		EMSG("Invalid manifest Device Tree at %#lx: error %d", pa, ret);
10004bc2c5f0SSungbae Yoo 		panic();
10014bc2c5f0SSungbae Yoo 	}
10024bc2c5f0SSungbae Yoo 
10034bc2c5f0SSungbae Yoo 	IMSG("manifest DT found");
10044bc2c5f0SSungbae Yoo }
10054bc2c5f0SSungbae Yoo 
10064bc2c5f0SSungbae Yoo void *get_manifest_dt(void)
10074bc2c5f0SSungbae Yoo {
10084bc2c5f0SSungbae Yoo 	return manifest_dt;
10094bc2c5f0SSungbae Yoo }
10104bc2c5f0SSungbae Yoo 
10114bc2c5f0SSungbae Yoo static TEE_Result release_manifest_dt(void)
10124bc2c5f0SSungbae Yoo {
10134bc2c5f0SSungbae Yoo 	if (!manifest_dt)
10144bc2c5f0SSungbae Yoo 		return TEE_SUCCESS;
10154bc2c5f0SSungbae Yoo 
10164bc2c5f0SSungbae Yoo 	if (core_mmu_remove_mapping(MEM_AREA_MANIFEST_DT, manifest_dt,
10174bc2c5f0SSungbae Yoo 				    CFG_DTB_MAX_SIZE))
10184bc2c5f0SSungbae Yoo 		panic("Failed to remove temporary manifest DT mapping");
10194bc2c5f0SSungbae Yoo 	manifest_dt = NULL;
10204bc2c5f0SSungbae Yoo 
10214bc2c5f0SSungbae Yoo 	return TEE_SUCCESS;
10224bc2c5f0SSungbae Yoo }
10234bc2c5f0SSungbae Yoo 
10244bc2c5f0SSungbae Yoo boot_final(release_manifest_dt);
10254bc2c5f0SSungbae Yoo #else
10264bc2c5f0SSungbae Yoo void init_manifest_dt(void *fdt __unused)
10274bc2c5f0SSungbae Yoo {
10284bc2c5f0SSungbae Yoo }
10294bc2c5f0SSungbae Yoo 
10304bc2c5f0SSungbae Yoo void reinit_manifest_dt(void)
10314bc2c5f0SSungbae Yoo {
10324bc2c5f0SSungbae Yoo }
10334bc2c5f0SSungbae Yoo 
10344bc2c5f0SSungbae Yoo void *get_manifest_dt(void)
10354bc2c5f0SSungbae Yoo {
10364bc2c5f0SSungbae Yoo 	return NULL;
10374bc2c5f0SSungbae Yoo }
10384bc2c5f0SSungbae Yoo #endif /*CFG_CORE_FFA*/
1039