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>
15414123aeSJens Wiklander #include <mm/phys_mem.h>
16e6027f48SAlvin Chang #include <stdio.h>
17a4f139d7SJerome Forissier #include <string.h>
187ba16abbSJerome Forissier #include <trace.h>
19a4f139d7SJerome Forissier
20e6027f48SAlvin Chang static struct dt_descriptor external_dt __nex_bss;
21e6027f48SAlvin Chang
224bc2c5f0SSungbae Yoo #if defined(CFG_CORE_FFA)
234bc2c5f0SSungbae Yoo static void *manifest_dt __nex_bss;
24414123aeSJens Wiklander static size_t manifest_max_size __nex_bss;
254bc2c5f0SSungbae Yoo #endif
264bc2c5f0SSungbae Yoo
dt_find_compatible_driver(const void * fdt,int offs)27a4f139d7SJerome Forissier const struct dt_driver *dt_find_compatible_driver(const void *fdt, int offs)
28a4f139d7SJerome Forissier {
29a4f139d7SJerome Forissier const struct dt_device_match *dm;
30a4f139d7SJerome Forissier const struct dt_driver *drv;
31a4f139d7SJerome Forissier
32db783ff8SEtienne Carriere for_each_dt_driver(drv) {
33db783ff8SEtienne Carriere for (dm = drv->match_table; dm; dm++) {
34db783ff8SEtienne Carriere if (!dm->compatible) {
35db783ff8SEtienne Carriere break;
36db783ff8SEtienne Carriere }
37a4f139d7SJerome Forissier if (!fdt_node_check_compatible(fdt, offs,
38db783ff8SEtienne Carriere dm->compatible)) {
39a4f139d7SJerome Forissier return drv;
40db783ff8SEtienne Carriere }
41db783ff8SEtienne Carriere }
42db783ff8SEtienne Carriere }
43a4f139d7SJerome Forissier
44a4f139d7SJerome Forissier return NULL;
45a4f139d7SJerome Forissier }
46a4f139d7SJerome Forissier
dt_have_prop(const void * fdt,int offs,const char * propname)4750f3b323SPeng Fan bool dt_have_prop(const void *fdt, int offs, const char *propname)
4850f3b323SPeng Fan {
4950f3b323SPeng Fan const void *prop;
5050f3b323SPeng Fan
5150f3b323SPeng Fan prop = fdt_getprop(fdt, offs, propname, NULL);
5250f3b323SPeng Fan
5350f3b323SPeng Fan return prop;
5450f3b323SPeng Fan }
5550f3b323SPeng Fan
dt_disable_status(void * fdt,int node)5695cdc5e0SCedric Neveux int dt_disable_status(void *fdt, int node)
5795cdc5e0SCedric Neveux {
5895cdc5e0SCedric Neveux const char *prop = NULL;
5995cdc5e0SCedric Neveux int len = 0;
6095cdc5e0SCedric Neveux
6195cdc5e0SCedric Neveux prop = fdt_getprop(fdt, node, "status", &len);
6295cdc5e0SCedric Neveux if (!prop) {
6395cdc5e0SCedric Neveux if (fdt_setprop_string(fdt, node, "status", "disabled"))
6495cdc5e0SCedric Neveux return -1;
6595cdc5e0SCedric Neveux } else {
6695cdc5e0SCedric Neveux /*
6795cdc5e0SCedric Neveux * Status is there, modify it.
6895cdc5e0SCedric Neveux * Ask to set "disabled" value to the property. The value
6995cdc5e0SCedric Neveux * will be automatically truncated with "len" size by the
7095cdc5e0SCedric Neveux * fdt_setprop_inplace function.
7195cdc5e0SCedric Neveux * Setting a value different from "ok" or "okay" will disable
7295cdc5e0SCedric Neveux * the property.
7395cdc5e0SCedric Neveux * Setting a truncated value of "disabled" with the original
7495cdc5e0SCedric Neveux * property "len" is preferred to not increase the DT size and
7595cdc5e0SCedric Neveux * losing time in recalculating the overall DT offsets.
7695cdc5e0SCedric Neveux * If original length of the status property is larger than
7795cdc5e0SCedric Neveux * "disabled", the property will start with "disabled" and be
7895cdc5e0SCedric Neveux * completed with the rest of the original property.
7995cdc5e0SCedric Neveux */
8095cdc5e0SCedric Neveux if (fdt_setprop_inplace(fdt, node, "status", "disabled", len))
8195cdc5e0SCedric Neveux return -1;
8295cdc5e0SCedric Neveux }
8395cdc5e0SCedric Neveux
8495cdc5e0SCedric Neveux return 0;
8595cdc5e0SCedric Neveux }
8695cdc5e0SCedric Neveux
dt_enable_secure_status(void * fdt,int node)8795cdc5e0SCedric Neveux int dt_enable_secure_status(void *fdt, int node)
8895cdc5e0SCedric Neveux {
8995cdc5e0SCedric Neveux if (dt_disable_status(fdt, node)) {
9095cdc5e0SCedric Neveux EMSG("Unable to disable Normal Status");
9195cdc5e0SCedric Neveux return -1;
9295cdc5e0SCedric Neveux }
9395cdc5e0SCedric Neveux
9495cdc5e0SCedric Neveux if (fdt_setprop_string(fdt, node, "secure-status", "okay"))
9595cdc5e0SCedric Neveux return -1;
9695cdc5e0SCedric Neveux
9795cdc5e0SCedric Neveux return 0;
9895cdc5e0SCedric Neveux }
9995cdc5e0SCedric Neveux
dt_map_dev(const void * fdt,int offs,vaddr_t * base,size_t * size,enum dt_map_dev_directive mapping)100a5d5bbc8SVesa Jääskeläinen int dt_map_dev(const void *fdt, int offs, vaddr_t *base, size_t *size,
101a5d5bbc8SVesa Jääskeläinen enum dt_map_dev_directive mapping)
1027ba16abbSJerome Forissier {
1037ba16abbSJerome Forissier enum teecore_memtypes mtype;
1047ba16abbSJerome Forissier paddr_t pbase;
1057ba16abbSJerome Forissier vaddr_t vbase;
106df7cecc0SLionel Debieve size_t sz;
1077ba16abbSJerome Forissier int st;
1087ba16abbSJerome Forissier
1097ba16abbSJerome Forissier assert(cpu_mmu_enabled());
1107ba16abbSJerome Forissier
111f354a5d8SGatien Chevallier st = fdt_get_status(fdt, offs);
1127ba16abbSJerome Forissier if (st == DT_STATUS_DISABLED)
1137ba16abbSJerome Forissier return -1;
1147ba16abbSJerome Forissier
115de56c16dSEtienne Carriere if (fdt_reg_info(fdt, offs, &pbase, &sz))
1167ba16abbSJerome Forissier return -1;
1177ba16abbSJerome Forissier
118a5d5bbc8SVesa Jääskeläinen switch (mapping) {
119a5d5bbc8SVesa Jääskeläinen case DT_MAP_AUTO:
1207ba16abbSJerome Forissier if ((st & DT_STATUS_OK_SEC) && !(st & DT_STATUS_OK_NSEC))
1217ba16abbSJerome Forissier mtype = MEM_AREA_IO_SEC;
1227ba16abbSJerome Forissier else
1237ba16abbSJerome Forissier mtype = MEM_AREA_IO_NSEC;
124a5d5bbc8SVesa Jääskeläinen break;
125a5d5bbc8SVesa Jääskeläinen case DT_MAP_SECURE:
126a5d5bbc8SVesa Jääskeläinen mtype = MEM_AREA_IO_SEC;
127a5d5bbc8SVesa Jääskeläinen break;
128a5d5bbc8SVesa Jääskeläinen case DT_MAP_NON_SECURE:
129a5d5bbc8SVesa Jääskeläinen mtype = MEM_AREA_IO_NSEC;
130a5d5bbc8SVesa Jääskeläinen break;
131a5d5bbc8SVesa Jääskeläinen default:
132a5d5bbc8SVesa Jääskeläinen panic("Invalid mapping specified");
133a5d5bbc8SVesa Jääskeläinen break;
134a5d5bbc8SVesa Jääskeläinen }
1357ba16abbSJerome Forissier
1367ba16abbSJerome Forissier /* Check if we have a mapping, create one if needed */
137bc9618c0SAnton Rybakov vbase = (vaddr_t)core_mmu_add_mapping(mtype, pbase, sz);
138bc9618c0SAnton Rybakov if (!vbase) {
1397ba16abbSJerome Forissier EMSG("Failed to map %zu bytes at PA 0x%"PRIxPA,
14023b1daf4SPeng Fan (size_t)sz, pbase);
1417ba16abbSJerome Forissier return -1;
1427ba16abbSJerome Forissier }
1437ba16abbSJerome Forissier
1447ba16abbSJerome Forissier *base = vbase;
1457ba16abbSJerome Forissier *size = sz;
1467ba16abbSJerome Forissier return 0;
1477ba16abbSJerome Forissier }
1487ba16abbSJerome Forissier
1499fe4c797SJerome Forissier /* Read a physical address (n=1 or 2 cells) */
fdt_read_paddr(const uint32_t * cell,int n)150f354a5d8SGatien Chevallier static paddr_t fdt_read_paddr(const uint32_t *cell, int n)
1519fe4c797SJerome Forissier {
1529fe4c797SJerome Forissier paddr_t addr;
1539fe4c797SJerome Forissier
1549fe4c797SJerome Forissier if (n < 1 || n > 2)
1559fe4c797SJerome Forissier goto bad;
1569fe4c797SJerome Forissier
1579fe4c797SJerome Forissier addr = fdt32_to_cpu(*cell);
1589fe4c797SJerome Forissier cell++;
1599fe4c797SJerome Forissier if (n == 2) {
1609fe4c797SJerome Forissier #ifdef ARM32
1619fe4c797SJerome Forissier if (addr) {
1629fe4c797SJerome Forissier /* High order 32 bits can't be nonzero */
1639fe4c797SJerome Forissier goto bad;
1649fe4c797SJerome Forissier }
1659fe4c797SJerome Forissier addr = fdt32_to_cpu(*cell);
1669fe4c797SJerome Forissier #else
1679fe4c797SJerome Forissier addr = (addr << 32) | fdt32_to_cpu(*cell);
1689fe4c797SJerome Forissier #endif
1699fe4c797SJerome Forissier }
1709fe4c797SJerome Forissier
1719fe4c797SJerome Forissier return addr;
1729fe4c797SJerome Forissier bad:
173c0cfb36cSEtienne Carriere return DT_INFO_INVALID_REG;
1749fe4c797SJerome Forissier
1759fe4c797SJerome Forissier }
1769fe4c797SJerome Forissier
fdt_read_size(const uint32_t * cell,int n)177a2e8c036SGatien Chevallier static size_t fdt_read_size(const uint32_t *cell, int n)
178a2e8c036SGatien Chevallier {
179a2e8c036SGatien Chevallier uint32_t sz = 0;
180a2e8c036SGatien Chevallier
181a2e8c036SGatien Chevallier sz = fdt32_to_cpu(*cell);
182a2e8c036SGatien Chevallier if (n == 2) {
183a2e8c036SGatien Chevallier if (sz)
184a2e8c036SGatien Chevallier return DT_INFO_INVALID_REG_SIZE;
185a2e8c036SGatien Chevallier
186a2e8c036SGatien Chevallier cell++;
187a2e8c036SGatien Chevallier sz = fdt32_to_cpu(*cell);
188a2e8c036SGatien Chevallier }
189a2e8c036SGatien Chevallier
190a2e8c036SGatien Chevallier return sz;
191a2e8c036SGatien Chevallier }
192a2e8c036SGatien Chevallier
fdt_get_reg_props_by_index(const void * fdt,int offs,int index,paddr_t * base,size_t * size)19396e33b7fSEtienne Carriere int fdt_get_reg_props_by_index(const void *fdt, int offs, int index,
19496e33b7fSEtienne Carriere paddr_t *base, size_t *size)
1959fe4c797SJerome Forissier {
196de56c16dSEtienne Carriere const fdt32_t *reg = NULL;
197de56c16dSEtienne Carriere int addr_ncells = 0;
198de56c16dSEtienne Carriere int size_ncells = 0;
19996e33b7fSEtienne Carriere int cell_offset = 0;
200578bc4feSEtienne Carriere int parent = 0;
201de56c16dSEtienne Carriere int len = 0;
2029fe4c797SJerome Forissier
20396e33b7fSEtienne Carriere if (index < 0)
20496e33b7fSEtienne Carriere return -FDT_ERR_BADOFFSET;
20596e33b7fSEtienne Carriere
2069fe4c797SJerome Forissier reg = (const uint32_t *)fdt_getprop(fdt, offs, "reg", &len);
2079fe4c797SJerome Forissier if (!reg)
208de56c16dSEtienne Carriere return -FDT_ERR_NOTFOUND;
2099fe4c797SJerome Forissier
210de56c16dSEtienne Carriere if (fdt_find_cached_parent_reg_cells(fdt, offs, &addr_ncells,
211de56c16dSEtienne Carriere &size_ncells) != 0) {
212578bc4feSEtienne Carriere parent = fdt_parent_offset(fdt, offs);
213578bc4feSEtienne Carriere if (parent < 0)
214de56c16dSEtienne Carriere return -FDT_ERR_NOTFOUND;
215578bc4feSEtienne Carriere
216de56c16dSEtienne Carriere addr_ncells = fdt_address_cells(fdt, parent);
217de56c16dSEtienne Carriere if (addr_ncells < 0)
218de56c16dSEtienne Carriere return -FDT_ERR_NOTFOUND;
2199fe4c797SJerome Forissier
220de56c16dSEtienne Carriere size_ncells = fdt_size_cells(fdt, parent);
221de56c16dSEtienne Carriere if (size_ncells < 0)
222de56c16dSEtienne Carriere return -FDT_ERR_NOTFOUND;
223578bc4feSEtienne Carriere }
224578bc4feSEtienne Carriere
22596e33b7fSEtienne Carriere cell_offset = index * (addr_ncells + size_ncells);
22696e33b7fSEtienne Carriere
22796e33b7fSEtienne Carriere if ((size_t)len < (cell_offset + addr_ncells) * sizeof(*reg))
228de56c16dSEtienne Carriere return -FDT_ERR_BADSTRUCTURE;
229de56c16dSEtienne Carriere
230de56c16dSEtienne Carriere if (base) {
23196e33b7fSEtienne Carriere *base = fdt_read_paddr(reg + cell_offset, addr_ncells);
232de56c16dSEtienne Carriere if (*base == DT_INFO_INVALID_REG)
233de56c16dSEtienne Carriere return -FDT_ERR_NOTFOUND;
234de56c16dSEtienne Carriere }
235de56c16dSEtienne Carriere
236de56c16dSEtienne Carriere if (size) {
23796e33b7fSEtienne Carriere if ((size_t)len <
23896e33b7fSEtienne Carriere (cell_offset + addr_ncells + size_ncells) * sizeof(*reg))
239de56c16dSEtienne Carriere return -FDT_ERR_BADSTRUCTURE;
240de56c16dSEtienne Carriere
24196e33b7fSEtienne Carriere *size = fdt_read_size(reg + cell_offset + addr_ncells,
24296e33b7fSEtienne Carriere size_ncells);
243de56c16dSEtienne Carriere if (*size == DT_INFO_INVALID_REG_SIZE)
244de56c16dSEtienne Carriere return -FDT_ERR_NOTFOUND;
245de56c16dSEtienne Carriere }
246de56c16dSEtienne Carriere
247de56c16dSEtienne Carriere return 0;
248de56c16dSEtienne Carriere }
249de56c16dSEtienne Carriere
fdt_reg_info(const void * fdt,int offs,paddr_t * base,size_t * size)25096e33b7fSEtienne Carriere int fdt_reg_info(const void *fdt, int offs, paddr_t *base, size_t *size)
25196e33b7fSEtienne Carriere {
25296e33b7fSEtienne Carriere return fdt_get_reg_props_by_index(fdt, offs, 0, base, size);
25396e33b7fSEtienne Carriere }
25496e33b7fSEtienne Carriere
fdt_reg_base_address(const void * fdt,int offs)255de56c16dSEtienne Carriere paddr_t fdt_reg_base_address(const void *fdt, int offs)
256de56c16dSEtienne Carriere {
257de56c16dSEtienne Carriere paddr_t base = 0;
258de56c16dSEtienne Carriere
259de56c16dSEtienne Carriere if (fdt_reg_info(fdt, offs, &base, NULL))
260de56c16dSEtienne Carriere return DT_INFO_INVALID_REG;
261de56c16dSEtienne Carriere
262de56c16dSEtienne Carriere return base;
263de56c16dSEtienne Carriere }
264de56c16dSEtienne Carriere
fdt_reg_size(const void * fdt,int offs)265de56c16dSEtienne Carriere size_t fdt_reg_size(const void *fdt, int offs)
266de56c16dSEtienne Carriere {
267de56c16dSEtienne Carriere size_t size = 0;
268de56c16dSEtienne Carriere
269de56c16dSEtienne Carriere if (fdt_reg_info(fdt, offs, NULL, &size))
270df7cecc0SLionel Debieve return DT_INFO_INVALID_REG_SIZE;
2719fe4c797SJerome Forissier
272de56c16dSEtienne Carriere return size;
2739fe4c797SJerome Forissier }
2749fe4c797SJerome Forissier
is_okay(const char * st,int len)2759fe4c797SJerome Forissier static bool is_okay(const char *st, int len)
2769fe4c797SJerome Forissier {
2779fe4c797SJerome Forissier return !strncmp(st, "ok", len) || !strncmp(st, "okay", len);
2789fe4c797SJerome Forissier }
2799fe4c797SJerome Forissier
fdt_get_status(const void * fdt,int offs)280f354a5d8SGatien Chevallier int fdt_get_status(const void *fdt, int offs)
2819fe4c797SJerome Forissier {
2829fe4c797SJerome Forissier const char *prop;
2839fe4c797SJerome Forissier int st = 0;
2849fe4c797SJerome Forissier int len;
2859fe4c797SJerome Forissier
2869fe4c797SJerome Forissier prop = fdt_getprop(fdt, offs, "status", &len);
2879fe4c797SJerome Forissier if (!prop || is_okay(prop, len)) {
2889fe4c797SJerome Forissier /* If status is not specified, it defaults to "okay" */
2899fe4c797SJerome Forissier st |= DT_STATUS_OK_NSEC;
2909fe4c797SJerome Forissier }
2919fe4c797SJerome Forissier
2929fe4c797SJerome Forissier prop = fdt_getprop(fdt, offs, "secure-status", &len);
2939fe4c797SJerome Forissier if (!prop) {
2949fe4c797SJerome Forissier /*
2959fe4c797SJerome Forissier * When secure-status is not specified it defaults to the same
2969fe4c797SJerome Forissier * value as status
2979fe4c797SJerome Forissier */
2989fe4c797SJerome Forissier if (st & DT_STATUS_OK_NSEC)
2999fe4c797SJerome Forissier st |= DT_STATUS_OK_SEC;
3009fe4c797SJerome Forissier } else {
3019fe4c797SJerome Forissier if (is_okay(prop, len))
3029fe4c797SJerome Forissier st |= DT_STATUS_OK_SEC;
3039fe4c797SJerome Forissier }
3049fe4c797SJerome Forissier
3059fe4c797SJerome Forissier return st;
3069fe4c797SJerome Forissier }
307c0cfb36cSEtienne Carriere
fdt_fill_device_info(const void * fdt,struct dt_node_info * info,int offs)308f354a5d8SGatien Chevallier void fdt_fill_device_info(const void *fdt, struct dt_node_info *info, int offs)
309c0cfb36cSEtienne Carriere {
310c0cfb36cSEtienne Carriere struct dt_node_info dinfo = {
311c0cfb36cSEtienne Carriere .reg = DT_INFO_INVALID_REG,
31206fd21ddSLionel Debieve .reg_size = DT_INFO_INVALID_REG_SIZE,
313c0cfb36cSEtienne Carriere .clock = DT_INFO_INVALID_CLOCK,
314c0cfb36cSEtienne Carriere .reset = DT_INFO_INVALID_RESET,
3157acb3a47SLudovic Barre .interrupt = DT_INFO_INVALID_INTERRUPT,
316c0cfb36cSEtienne Carriere };
317578bc4feSEtienne Carriere const fdt32_t *cuint = NULL;
318c0cfb36cSEtienne Carriere
319de56c16dSEtienne Carriere /* Intentionally discard fdt_reg_info() return value */
320de56c16dSEtienne Carriere fdt_reg_info(fdt, offs, &dinfo.reg, &dinfo.reg_size);
321c0cfb36cSEtienne Carriere
322c0cfb36cSEtienne Carriere cuint = fdt_getprop(fdt, offs, "clocks", NULL);
323c0cfb36cSEtienne Carriere if (cuint) {
324c0cfb36cSEtienne Carriere cuint++;
325c0cfb36cSEtienne Carriere dinfo.clock = (int)fdt32_to_cpu(*cuint);
326c0cfb36cSEtienne Carriere }
327c0cfb36cSEtienne Carriere
328c0cfb36cSEtienne Carriere cuint = fdt_getprop(fdt, offs, "resets", NULL);
329c0cfb36cSEtienne Carriere if (cuint) {
330c0cfb36cSEtienne Carriere cuint++;
331c0cfb36cSEtienne Carriere dinfo.reset = (int)fdt32_to_cpu(*cuint);
332c0cfb36cSEtienne Carriere }
333c0cfb36cSEtienne Carriere
334702fe5a7SClément Léger dinfo.interrupt = dt_get_irq_type_prio(fdt, offs, &dinfo.type,
335702fe5a7SClément Léger &dinfo.prio);
3367acb3a47SLudovic Barre
337f354a5d8SGatien Chevallier dinfo.status = fdt_get_status(fdt, offs);
338c0cfb36cSEtienne Carriere
339c0cfb36cSEtienne Carriere *info = dinfo;
340c0cfb36cSEtienne Carriere }
341876826f3SGabriel Fernandez
fdt_read_uint32_array(const void * fdt,int node,const char * prop_name,uint32_t * array,size_t count)342f354a5d8SGatien Chevallier int fdt_read_uint32_array(const void *fdt, int node, const char *prop_name,
343876826f3SGabriel Fernandez uint32_t *array, size_t count)
344876826f3SGabriel Fernandez {
345876826f3SGabriel Fernandez const fdt32_t *cuint = NULL;
346876826f3SGabriel Fernandez int len = 0;
347876826f3SGabriel Fernandez uint32_t i = 0;
348876826f3SGabriel Fernandez
349876826f3SGabriel Fernandez cuint = fdt_getprop(fdt, node, prop_name, &len);
350876826f3SGabriel Fernandez if (!cuint)
35173e27bfaSGatien Chevallier return len;
352876826f3SGabriel Fernandez
353876826f3SGabriel Fernandez if ((uint32_t)len != (count * sizeof(uint32_t)))
354876826f3SGabriel Fernandez return -FDT_ERR_BADLAYOUT;
355876826f3SGabriel Fernandez
356876826f3SGabriel Fernandez for (i = 0; i < ((uint32_t)len / sizeof(uint32_t)); i++) {
357876826f3SGabriel Fernandez *array = fdt32_to_cpu(*cuint);
358876826f3SGabriel Fernandez array++;
359876826f3SGabriel Fernandez cuint++;
360876826f3SGabriel Fernandez }
361876826f3SGabriel Fernandez
362876826f3SGabriel Fernandez return 0;
363876826f3SGabriel Fernandez }
364876826f3SGabriel Fernandez
fdt_read_uint32_index(const void * fdt,int node,const char * prop_name,int index,uint32_t * value)3657c3a6b7bSGatien Chevallier int fdt_read_uint32_index(const void *fdt, int node, const char *prop_name,
3667c3a6b7bSGatien Chevallier int index, uint32_t *value)
3677c3a6b7bSGatien Chevallier {
3687c3a6b7bSGatien Chevallier const fdt32_t *cuint = NULL;
3697c3a6b7bSGatien Chevallier int len = 0;
3707c3a6b7bSGatien Chevallier
3717c3a6b7bSGatien Chevallier cuint = fdt_getprop(fdt, node, prop_name, &len);
3727c3a6b7bSGatien Chevallier if (!cuint)
3737c3a6b7bSGatien Chevallier return len;
3747c3a6b7bSGatien Chevallier
3757c3a6b7bSGatien Chevallier if ((uint32_t)len < (sizeof(uint32_t) * (index + 1)))
3767c3a6b7bSGatien Chevallier return -FDT_ERR_BADLAYOUT;
3777c3a6b7bSGatien Chevallier
3787c3a6b7bSGatien Chevallier *value = fdt32_to_cpu(cuint[index]);
3797c3a6b7bSGatien Chevallier
3807c3a6b7bSGatien Chevallier return 0;
3817c3a6b7bSGatien Chevallier }
3827c3a6b7bSGatien Chevallier
fdt_read_uint32(const void * fdt,int node,const char * prop_name,uint32_t * value)383f354a5d8SGatien Chevallier int fdt_read_uint32(const void *fdt, int node, const char *prop_name,
384876826f3SGabriel Fernandez uint32_t *value)
385876826f3SGabriel Fernandez {
386f354a5d8SGatien Chevallier return fdt_read_uint32_array(fdt, node, prop_name, value, 1);
387876826f3SGabriel Fernandez }
388876826f3SGabriel Fernandez
fdt_read_uint32_default(const void * fdt,int node,const char * prop_name,uint32_t dflt_value)389f354a5d8SGatien Chevallier uint32_t fdt_read_uint32_default(const void *fdt, int node,
390876826f3SGabriel Fernandez const char *prop_name, uint32_t dflt_value)
391876826f3SGabriel Fernandez {
3927c3a6b7bSGatien Chevallier uint32_t ret = dflt_value;
393876826f3SGabriel Fernandez
3947c3a6b7bSGatien Chevallier fdt_read_uint32_index(fdt, node, prop_name, 0, &ret);
395876826f3SGabriel Fernandez
3967c3a6b7bSGatien Chevallier return ret;
397876826f3SGabriel Fernandez }
39807ced948SGatien Chevallier
fdt_get_reg_props_by_name(const void * fdt,int node,const char * name,paddr_t * base,size_t * size)39907ced948SGatien Chevallier int fdt_get_reg_props_by_name(const void *fdt, int node, const char *name,
40007ced948SGatien Chevallier paddr_t *base, size_t *size)
40107ced948SGatien Chevallier {
40207ced948SGatien Chevallier int index = 0;
40307ced948SGatien Chevallier
40407ced948SGatien Chevallier index = fdt_stringlist_search(fdt, node, "reg-names", name);
40507ced948SGatien Chevallier if (index < 0)
40607ced948SGatien Chevallier return index;
40707ced948SGatien Chevallier
40807ced948SGatien Chevallier return fdt_get_reg_props_by_index(fdt, node, index, base, size);
40907ced948SGatien Chevallier }
4104e45454aSJens Wiklander
dt_getprop_as_number(const void * fdt,int nodeoffset,const char * name,uint64_t * num)4114e45454aSJens Wiklander int dt_getprop_as_number(const void *fdt, int nodeoffset, const char *name,
4124e45454aSJens Wiklander uint64_t *num)
4134e45454aSJens Wiklander {
4144e45454aSJens Wiklander const void *prop = NULL;
4154e45454aSJens Wiklander int len = 0;
4164e45454aSJens Wiklander
4174e45454aSJens Wiklander prop = fdt_getprop(fdt, nodeoffset, name, &len);
4184e45454aSJens Wiklander if (!prop)
4194e45454aSJens Wiklander return len;
4204e45454aSJens Wiklander
4214e45454aSJens Wiklander switch (len) {
4224e45454aSJens Wiklander case sizeof(uint32_t):
4234e45454aSJens Wiklander *num = fdt32_ld(prop);
4244e45454aSJens Wiklander return 0;
4254e45454aSJens Wiklander case sizeof(uint64_t):
4264e45454aSJens Wiklander *num = fdt64_ld(prop);
4274e45454aSJens Wiklander return 0;
4284e45454aSJens Wiklander default:
4294e45454aSJens Wiklander return -FDT_ERR_BADVALUE;
4304e45454aSJens Wiklander }
4314e45454aSJens Wiklander }
432e6027f48SAlvin Chang
get_dt(void)433e6027f48SAlvin Chang void *get_dt(void)
434e6027f48SAlvin Chang {
435e6027f48SAlvin Chang void *fdt = get_embedded_dt();
436e6027f48SAlvin Chang
437e6027f48SAlvin Chang if (!fdt)
438e6027f48SAlvin Chang fdt = get_external_dt();
439e6027f48SAlvin Chang
440c5e3e79fSSungbae Yoo if (!fdt)
441c5e3e79fSSungbae Yoo fdt = get_manifest_dt();
442c5e3e79fSSungbae Yoo
443e6027f48SAlvin Chang return fdt;
444e6027f48SAlvin Chang }
445e6027f48SAlvin Chang
get_secure_dt(void)446e6027f48SAlvin Chang void *get_secure_dt(void)
447e6027f48SAlvin Chang {
448e6027f48SAlvin Chang void *fdt = get_embedded_dt();
449e6027f48SAlvin Chang
450e6027f48SAlvin Chang if (!fdt && IS_ENABLED(CFG_MAP_EXT_DT_SECURE))
451e6027f48SAlvin Chang fdt = get_external_dt();
452e6027f48SAlvin Chang
453c5e3e79fSSungbae Yoo if (!fdt)
454c5e3e79fSSungbae Yoo fdt = get_manifest_dt();
455c5e3e79fSSungbae Yoo
456e6027f48SAlvin Chang return fdt;
457e6027f48SAlvin Chang }
458e6027f48SAlvin Chang
459e6027f48SAlvin Chang #if defined(CFG_EMBED_DTB)
460578bc4feSEtienne Carriere #ifdef CFG_DT_CACHED_NODE_INFO
461578bc4feSEtienne Carriere /*
462578bc4feSEtienne Carriere * struct cached_node - Cached information of a DT node
463578bc4feSEtienne Carriere *
464578bc4feSEtienne Carriere * @node_offset: Offset of the node in @cached_node_info_fdt
465578bc4feSEtienne Carriere * @parent_offset: Offset of @node_offset parent node
466578bc4feSEtienne Carriere * @address_cells: #address-cells property value of the parent node or 0
467578bc4feSEtienne Carriere * @size_cells: #size-cells property value of the parent node or 0
468578bc4feSEtienne Carriere * @phandle: Phandle associated to the node or 0 if none
469578bc4feSEtienne Carriere */
470578bc4feSEtienne Carriere struct cached_node {
471578bc4feSEtienne Carriere int node_offset;
472578bc4feSEtienne Carriere int parent_offset;
473578bc4feSEtienne Carriere int8_t address_cells;
474578bc4feSEtienne Carriere int8_t size_cells;
475578bc4feSEtienne Carriere uint32_t phandle;
476578bc4feSEtienne Carriere };
477578bc4feSEtienne Carriere
478578bc4feSEtienne Carriere /*
479578bc4feSEtienne Carriere * struct dt_node_cache - Reference to cached information of DT nodes
480578bc4feSEtienne Carriere *
481578bc4feSEtienne Carriere * @array: Array of the cached node
482578bc4feSEtienne Carriere * @count: Number of initialized cells in @array
483578bc4feSEtienne Carriere * @alloced_count: Number of allocated cells in @array
484578bc4feSEtienne Carriere * @fdt: Reference to the FDT for which node information are cached
485578bc4feSEtienne Carriere */
486578bc4feSEtienne Carriere struct dt_node_cache {
487578bc4feSEtienne Carriere struct cached_node *array;
488578bc4feSEtienne Carriere size_t count;
489578bc4feSEtienne Carriere size_t alloced_count;
490578bc4feSEtienne Carriere const void *fdt;
491578bc4feSEtienne Carriere };
492578bc4feSEtienne Carriere
493578bc4feSEtienne Carriere static struct dt_node_cache *dt_node_cache;
494578bc4feSEtienne Carriere
fdt_node_info_are_cached(const void * fdt)495578bc4feSEtienne Carriere static bool fdt_node_info_are_cached(const void *fdt)
496578bc4feSEtienne Carriere {
497578bc4feSEtienne Carriere return dt_node_cache && dt_node_cache->fdt == fdt;
498578bc4feSEtienne Carriere }
499578bc4feSEtienne Carriere
find_cached_parent_node(const void * fdt,int node_offset)500578bc4feSEtienne Carriere static struct cached_node *find_cached_parent_node(const void *fdt,
501578bc4feSEtienne Carriere int node_offset)
502578bc4feSEtienne Carriere {
503578bc4feSEtienne Carriere struct cached_node *cell = NULL;
504578bc4feSEtienne Carriere size_t n = 0;
505578bc4feSEtienne Carriere
506578bc4feSEtienne Carriere if (!fdt_node_info_are_cached(fdt))
507578bc4feSEtienne Carriere return NULL;
508578bc4feSEtienne Carriere
509578bc4feSEtienne Carriere for (n = 0; n < dt_node_cache->count; n++)
510578bc4feSEtienne Carriere if (dt_node_cache->array[n].node_offset == node_offset)
511578bc4feSEtienne Carriere cell = dt_node_cache->array + n;
512578bc4feSEtienne Carriere
513578bc4feSEtienne Carriere return cell;
514578bc4feSEtienne Carriere }
515578bc4feSEtienne Carriere
fdt_find_cached_parent_node(const void * fdt,int node_offset,int * parent_offset)516578bc4feSEtienne Carriere int fdt_find_cached_parent_node(const void *fdt, int node_offset,
517578bc4feSEtienne Carriere int *parent_offset)
518578bc4feSEtienne Carriere {
519578bc4feSEtienne Carriere struct cached_node *cell = NULL;
520578bc4feSEtienne Carriere
521578bc4feSEtienne Carriere cell = find_cached_parent_node(fdt, node_offset);
522578bc4feSEtienne Carriere if (!cell)
523578bc4feSEtienne Carriere return -FDT_ERR_NOTFOUND;
524578bc4feSEtienne Carriere
525578bc4feSEtienne Carriere *parent_offset = cell->parent_offset;
526578bc4feSEtienne Carriere
527578bc4feSEtienne Carriere return 0;
528578bc4feSEtienne Carriere }
529578bc4feSEtienne Carriere
fdt_find_cached_parent_reg_cells(const void * fdt,int node_offset,int * address_cells,int * size_cells)530578bc4feSEtienne Carriere int fdt_find_cached_parent_reg_cells(const void *fdt, int node_offset,
531578bc4feSEtienne Carriere int *address_cells, int *size_cells)
532578bc4feSEtienne Carriere {
533578bc4feSEtienne Carriere struct cached_node *cell = NULL;
534578bc4feSEtienne Carriere int rc = 0;
535578bc4feSEtienne Carriere
536578bc4feSEtienne Carriere cell = find_cached_parent_node(fdt, node_offset);
537578bc4feSEtienne Carriere if (!cell)
538578bc4feSEtienne Carriere return -FDT_ERR_NOTFOUND;
539578bc4feSEtienne Carriere
540578bc4feSEtienne Carriere if (address_cells) {
541578bc4feSEtienne Carriere if (cell->address_cells >= 0)
542578bc4feSEtienne Carriere *address_cells = cell->address_cells;
543578bc4feSEtienne Carriere else
544578bc4feSEtienne Carriere rc = -FDT_ERR_NOTFOUND;
545578bc4feSEtienne Carriere }
546578bc4feSEtienne Carriere
547578bc4feSEtienne Carriere if (size_cells) {
548578bc4feSEtienne Carriere if (cell->size_cells >= 0)
549578bc4feSEtienne Carriere *size_cells = cell->size_cells;
550578bc4feSEtienne Carriere else
551578bc4feSEtienne Carriere rc = -FDT_ERR_NOTFOUND;
552578bc4feSEtienne Carriere }
553578bc4feSEtienne Carriere
554578bc4feSEtienne Carriere return rc;
555578bc4feSEtienne Carriere }
556578bc4feSEtienne Carriere
fdt_find_cached_node_phandle(const void * fdt,uint32_t phandle,int * node_offset)557578bc4feSEtienne Carriere int fdt_find_cached_node_phandle(const void *fdt, uint32_t phandle,
558578bc4feSEtienne Carriere int *node_offset)
559578bc4feSEtienne Carriere {
560578bc4feSEtienne Carriere struct cached_node *cell = NULL;
561578bc4feSEtienne Carriere size_t n = 0;
562578bc4feSEtienne Carriere
563578bc4feSEtienne Carriere if (!fdt_node_info_are_cached(fdt))
564578bc4feSEtienne Carriere return -FDT_ERR_NOTFOUND;
565578bc4feSEtienne Carriere
566578bc4feSEtienne Carriere for (n = 0; n < dt_node_cache->count; n++)
567578bc4feSEtienne Carriere if (dt_node_cache->array[n].phandle == phandle)
568578bc4feSEtienne Carriere cell = dt_node_cache->array + n;
569578bc4feSEtienne Carriere
570578bc4feSEtienne Carriere if (!cell)
571578bc4feSEtienne Carriere return -FDT_ERR_NOTFOUND;
572578bc4feSEtienne Carriere
573578bc4feSEtienne Carriere *node_offset = cell->node_offset;
574578bc4feSEtienne Carriere
575578bc4feSEtienne Carriere return 0;
576578bc4feSEtienne Carriere }
577578bc4feSEtienne Carriere
realloc_cached_node_array(void)578578bc4feSEtienne Carriere static TEE_Result realloc_cached_node_array(void)
579578bc4feSEtienne Carriere {
580578bc4feSEtienne Carriere assert(dt_node_cache);
581578bc4feSEtienne Carriere
582578bc4feSEtienne Carriere if (dt_node_cache->count + 1 > dt_node_cache->alloced_count) {
583578bc4feSEtienne Carriere size_t new_count = dt_node_cache->alloced_count * 2;
584578bc4feSEtienne Carriere struct cached_node *new = NULL;
585578bc4feSEtienne Carriere
586578bc4feSEtienne Carriere if (!new_count)
587578bc4feSEtienne Carriere new_count = 4;
588578bc4feSEtienne Carriere
589578bc4feSEtienne Carriere new = realloc(dt_node_cache->array,
590578bc4feSEtienne Carriere sizeof(*dt_node_cache->array) * new_count);
591578bc4feSEtienne Carriere if (!new)
592578bc4feSEtienne Carriere return TEE_ERROR_OUT_OF_MEMORY;
593578bc4feSEtienne Carriere
594578bc4feSEtienne Carriere dt_node_cache->array = new;
595578bc4feSEtienne Carriere dt_node_cache->alloced_count = new_count;
596578bc4feSEtienne Carriere }
597578bc4feSEtienne Carriere
598578bc4feSEtienne Carriere return TEE_SUCCESS;
599578bc4feSEtienne Carriere }
600578bc4feSEtienne Carriere
add_cached_node(int parent_offset,int node_offset,int address_cells,int size_cells)601578bc4feSEtienne Carriere static TEE_Result add_cached_node(int parent_offset,
602578bc4feSEtienne Carriere int node_offset, int address_cells,
603578bc4feSEtienne Carriere int size_cells)
604578bc4feSEtienne Carriere {
605578bc4feSEtienne Carriere TEE_Result res = TEE_ERROR_GENERIC;
606578bc4feSEtienne Carriere
607578bc4feSEtienne Carriere res = realloc_cached_node_array();
608578bc4feSEtienne Carriere if (res)
609578bc4feSEtienne Carriere return res;
610578bc4feSEtienne Carriere
611578bc4feSEtienne Carriere dt_node_cache->array[dt_node_cache->count] = (struct cached_node){
612578bc4feSEtienne Carriere .node_offset = node_offset,
613578bc4feSEtienne Carriere .parent_offset = parent_offset,
614578bc4feSEtienne Carriere .address_cells = address_cells,
615578bc4feSEtienne Carriere .size_cells = size_cells,
616578bc4feSEtienne Carriere .phandle = fdt_get_phandle(dt_node_cache->fdt, node_offset),
617578bc4feSEtienne Carriere };
618578bc4feSEtienne Carriere
619578bc4feSEtienne Carriere dt_node_cache->count++;
620578bc4feSEtienne Carriere
621578bc4feSEtienne Carriere return TEE_SUCCESS;
622578bc4feSEtienne Carriere }
623578bc4feSEtienne Carriere
add_cached_node_subtree(int node_offset)624578bc4feSEtienne Carriere static TEE_Result add_cached_node_subtree(int node_offset)
625578bc4feSEtienne Carriere {
626578bc4feSEtienne Carriere TEE_Result res = TEE_ERROR_GENERIC;
627578bc4feSEtienne Carriere const fdt32_t *cuint = NULL;
628578bc4feSEtienne Carriere int subnode_offset = 0;
629578bc4feSEtienne Carriere int8_t addr_cells = -1;
630578bc4feSEtienne Carriere int8_t size_cells = -1;
631578bc4feSEtienne Carriere
632578bc4feSEtienne Carriere cuint = fdt_getprop(dt_node_cache->fdt, node_offset, "#address-cells",
633578bc4feSEtienne Carriere NULL);
634578bc4feSEtienne Carriere if (cuint)
635578bc4feSEtienne Carriere addr_cells = (int)fdt32_to_cpu(*cuint);
636578bc4feSEtienne Carriere
637578bc4feSEtienne Carriere cuint = fdt_getprop(dt_node_cache->fdt, node_offset, "#size-cells",
638578bc4feSEtienne Carriere NULL);
639578bc4feSEtienne Carriere if (cuint)
640578bc4feSEtienne Carriere size_cells = (int)fdt32_to_cpu(*cuint);
641578bc4feSEtienne Carriere
642578bc4feSEtienne Carriere fdt_for_each_subnode(subnode_offset, dt_node_cache->fdt, node_offset) {
643578bc4feSEtienne Carriere res = add_cached_node(node_offset, subnode_offset, addr_cells,
644578bc4feSEtienne Carriere size_cells);
645578bc4feSEtienne Carriere if (res)
646578bc4feSEtienne Carriere return res;
647578bc4feSEtienne Carriere
648578bc4feSEtienne Carriere res = add_cached_node_subtree(subnode_offset);
649578bc4feSEtienne Carriere if (res)
650578bc4feSEtienne Carriere return res;
651578bc4feSEtienne Carriere }
652578bc4feSEtienne Carriere
653578bc4feSEtienne Carriere return TEE_SUCCESS;
654578bc4feSEtienne Carriere }
655578bc4feSEtienne Carriere
release_node_cache_info(void)656578bc4feSEtienne Carriere static TEE_Result release_node_cache_info(void)
657578bc4feSEtienne Carriere {
658578bc4feSEtienne Carriere if (dt_node_cache) {
659578bc4feSEtienne Carriere free(dt_node_cache->array);
660578bc4feSEtienne Carriere free(dt_node_cache);
661578bc4feSEtienne Carriere dt_node_cache = NULL;
662578bc4feSEtienne Carriere }
663578bc4feSEtienne Carriere
664578bc4feSEtienne Carriere return TEE_SUCCESS;
665578bc4feSEtienne Carriere }
666578bc4feSEtienne Carriere
667578bc4feSEtienne Carriere release_init_resource(release_node_cache_info);
668578bc4feSEtienne Carriere
init_node_cache_info(const void * fdt)669578bc4feSEtienne Carriere static void init_node_cache_info(const void *fdt)
670578bc4feSEtienne Carriere {
671578bc4feSEtienne Carriere TEE_Result res = TEE_ERROR_GENERIC;
672578bc4feSEtienne Carriere
673578bc4feSEtienne Carriere assert(!dt_node_cache);
674578bc4feSEtienne Carriere
675578bc4feSEtienne Carriere dt_node_cache = calloc(1, sizeof(*dt_node_cache));
676578bc4feSEtienne Carriere if (dt_node_cache) {
677578bc4feSEtienne Carriere dt_node_cache->fdt = fdt;
678578bc4feSEtienne Carriere res = add_cached_node_subtree(0);
679578bc4feSEtienne Carriere } else {
680578bc4feSEtienne Carriere res = TEE_ERROR_OUT_OF_MEMORY;
681578bc4feSEtienne Carriere }
682578bc4feSEtienne Carriere
683578bc4feSEtienne Carriere if (res) {
684578bc4feSEtienne Carriere EMSG("Error %#"PRIx32", disable DT cached info", res);
685578bc4feSEtienne Carriere release_node_cache_info();
686578bc4feSEtienne Carriere }
687578bc4feSEtienne Carriere }
688578bc4feSEtienne Carriere #else
init_node_cache_info(const void * fdt __unused)689578bc4feSEtienne Carriere static void init_node_cache_info(const void *fdt __unused)
690578bc4feSEtienne Carriere {
691578bc4feSEtienne Carriere }
692578bc4feSEtienne Carriere #endif /* CFG_DT_CACHED_NODE_INFO */
693578bc4feSEtienne Carriere
get_embedded_dt(void)694e6027f48SAlvin Chang void *get_embedded_dt(void)
695e6027f48SAlvin Chang {
696e6027f48SAlvin Chang static bool checked;
697e6027f48SAlvin Chang
698e6027f48SAlvin Chang assert(cpu_mmu_enabled());
699e6027f48SAlvin Chang
700e6027f48SAlvin Chang if (!checked) {
701e6027f48SAlvin Chang IMSG("Embedded DTB found");
702e6027f48SAlvin Chang
703e6027f48SAlvin Chang if (fdt_check_header(embedded_secure_dtb))
704e6027f48SAlvin Chang panic("Invalid embedded DTB");
705e6027f48SAlvin Chang
706e6027f48SAlvin Chang checked = true;
707578bc4feSEtienne Carriere
708578bc4feSEtienne Carriere init_node_cache_info(embedded_secure_dtb);
709e6027f48SAlvin Chang }
710e6027f48SAlvin Chang
711e6027f48SAlvin Chang return embedded_secure_dtb;
712e6027f48SAlvin Chang }
713e6027f48SAlvin Chang #else
get_embedded_dt(void)714e6027f48SAlvin Chang void *get_embedded_dt(void)
715e6027f48SAlvin Chang {
716e6027f48SAlvin Chang return NULL;
717e6027f48SAlvin Chang }
718e6027f48SAlvin Chang #endif /*CFG_EMBED_DTB*/
719e6027f48SAlvin Chang
720e6027f48SAlvin Chang #ifdef _CFG_USE_DTB_OVERLAY
add_dt_overlay_fragment(struct dt_descriptor * dt,int ioffs)721e6027f48SAlvin Chang static int add_dt_overlay_fragment(struct dt_descriptor *dt, int ioffs)
722e6027f48SAlvin Chang {
7230c49b6d6SAlvin Chang char frag[32] = { };
7240c49b6d6SAlvin Chang int offs = 0;
7250c49b6d6SAlvin Chang int ret = 0;
726e6027f48SAlvin Chang
727a039ffc6SClement Faure ret = snprintf(frag, sizeof(frag), "fragment@%d", dt->frag_id);
728a039ffc6SClement Faure if (ret < 0 || (size_t)ret >= sizeof(frag))
729a039ffc6SClement Faure return -1;
730a039ffc6SClement Faure
731e6027f48SAlvin Chang offs = fdt_add_subnode(dt->blob, ioffs, frag);
732e6027f48SAlvin Chang if (offs < 0)
733e6027f48SAlvin Chang return offs;
734e6027f48SAlvin Chang
735e6027f48SAlvin Chang dt->frag_id += 1;
736e6027f48SAlvin Chang
737e6027f48SAlvin Chang ret = fdt_setprop_string(dt->blob, offs, "target-path", "/");
738e6027f48SAlvin Chang if (ret < 0)
7390c49b6d6SAlvin Chang return ret;
740e6027f48SAlvin Chang
741e6027f48SAlvin Chang return fdt_add_subnode(dt->blob, offs, "__overlay__");
742e6027f48SAlvin Chang }
743e6027f48SAlvin Chang
init_dt_overlay(struct dt_descriptor * dt,int __maybe_unused dt_size)744e6027f48SAlvin Chang static int init_dt_overlay(struct dt_descriptor *dt, int __maybe_unused dt_size)
745e6027f48SAlvin Chang {
7460c49b6d6SAlvin Chang int fragment = 0;
747e6027f48SAlvin Chang
748e6027f48SAlvin Chang if (IS_ENABLED(CFG_EXTERNAL_DTB_OVERLAY)) {
749e6027f48SAlvin Chang if (!fdt_check_header(dt->blob)) {
750e6027f48SAlvin Chang fdt_for_each_subnode(fragment, dt->blob, 0)
751e6027f48SAlvin Chang dt->frag_id += 1;
752e6027f48SAlvin Chang return 0;
753e6027f48SAlvin Chang }
754e6027f48SAlvin Chang }
755e6027f48SAlvin Chang
756e6027f48SAlvin Chang return fdt_create_empty_tree(dt->blob, dt_size);
757e6027f48SAlvin Chang }
758e6027f48SAlvin Chang #else
add_dt_overlay_fragment(struct dt_descriptor * dt __unused,int offs)759e6027f48SAlvin Chang static int add_dt_overlay_fragment(struct dt_descriptor *dt __unused, int offs)
760e6027f48SAlvin Chang {
761e6027f48SAlvin Chang return offs;
762e6027f48SAlvin Chang }
763e6027f48SAlvin Chang
init_dt_overlay(struct dt_descriptor * dt __unused,int dt_size __unused)764e6027f48SAlvin Chang static int init_dt_overlay(struct dt_descriptor *dt __unused,
765e6027f48SAlvin Chang int dt_size __unused)
766e6027f48SAlvin Chang {
767e6027f48SAlvin Chang return 0;
768e6027f48SAlvin Chang }
769e6027f48SAlvin Chang #endif /* _CFG_USE_DTB_OVERLAY */
770e6027f48SAlvin Chang
get_external_dt_desc(void)771e6027f48SAlvin Chang struct dt_descriptor *get_external_dt_desc(void)
772e6027f48SAlvin Chang {
773e6027f48SAlvin Chang if (!IS_ENABLED(CFG_EXTERNAL_DT))
774e6027f48SAlvin Chang return NULL;
775e6027f48SAlvin Chang
776e6027f48SAlvin Chang return &external_dt;
777e6027f48SAlvin Chang }
778e6027f48SAlvin Chang
init_external_dt(unsigned long phys_dt,size_t dt_sz)779dcff802bSRaymond Mao void init_external_dt(unsigned long phys_dt, size_t dt_sz)
780e6027f48SAlvin Chang {
781e6027f48SAlvin Chang struct dt_descriptor *dt = &external_dt;
7820c49b6d6SAlvin Chang int ret = 0;
78366763721SRaymond Mao enum teecore_memtypes mtype = MEM_AREA_MAXTYPE;
784e6027f48SAlvin Chang
785e6027f48SAlvin Chang if (!IS_ENABLED(CFG_EXTERNAL_DT))
786e6027f48SAlvin Chang return;
787e6027f48SAlvin Chang
788dcff802bSRaymond Mao if (!phys_dt || !dt_sz) {
789e6027f48SAlvin Chang /*
790e6027f48SAlvin Chang * No need to panic as we're not using the DT in OP-TEE
791e6027f48SAlvin Chang * yet, we're only adding some nodes for normal world use.
792e6027f48SAlvin Chang * This makes the switch to using DT easier as we can boot
793e6027f48SAlvin Chang * a newer OP-TEE with older boot loaders. Once we start to
794e6027f48SAlvin Chang * initialize devices based on DT we'll likely panic
795e6027f48SAlvin Chang * instead of returning here.
796e6027f48SAlvin Chang */
797e6027f48SAlvin Chang IMSG("No non-secure external DT");
798e6027f48SAlvin Chang return;
799e6027f48SAlvin Chang }
800e6027f48SAlvin Chang
80166763721SRaymond Mao mtype = core_mmu_get_type_by_pa(phys_dt);
80266763721SRaymond Mao if (mtype == MEM_AREA_MAXTYPE) {
80366763721SRaymond Mao /* Map the DTB if it is not yet mapped */
80466763721SRaymond Mao dt->blob = core_mmu_add_mapping(MEM_AREA_EXT_DT, phys_dt,
805dcff802bSRaymond Mao dt_sz);
80666763721SRaymond Mao if (!dt->blob)
807e6027f48SAlvin Chang panic("Failed to map external DTB");
80866763721SRaymond Mao } else {
80966763721SRaymond Mao /* Get the DTB address if already mapped in a memory area */
810dcff802bSRaymond Mao dt->blob = phys_to_virt(phys_dt, mtype, dt_sz);
81166763721SRaymond Mao if (!dt->blob) {
81266763721SRaymond Mao EMSG("Failed to get a mapped external DTB for PA %#lx",
81366763721SRaymond Mao phys_dt);
81466763721SRaymond Mao panic();
81566763721SRaymond Mao }
81666763721SRaymond Mao }
817e6027f48SAlvin Chang
818dcff802bSRaymond Mao ret = init_dt_overlay(dt, dt_sz);
819e6027f48SAlvin Chang if (ret < 0) {
820e6027f48SAlvin Chang EMSG("Device Tree Overlay init fail @ %#lx: error %d", phys_dt,
821e6027f48SAlvin Chang ret);
822e6027f48SAlvin Chang panic();
823e6027f48SAlvin Chang }
824e6027f48SAlvin Chang
825dcff802bSRaymond Mao ret = fdt_open_into(dt->blob, dt->blob, dt_sz);
826e6027f48SAlvin Chang if (ret < 0) {
827e6027f48SAlvin Chang EMSG("Invalid Device Tree at %#lx: error %d", phys_dt, ret);
828e6027f48SAlvin Chang panic();
829e6027f48SAlvin Chang }
830e6027f48SAlvin Chang
831e6027f48SAlvin Chang IMSG("Non-secure external DT found");
832e6027f48SAlvin Chang }
833e6027f48SAlvin Chang
get_external_dt(void)834e6027f48SAlvin Chang void *get_external_dt(void)
835e6027f48SAlvin Chang {
836e6027f48SAlvin Chang if (!IS_ENABLED(CFG_EXTERNAL_DT))
837e6027f48SAlvin Chang return NULL;
838e6027f48SAlvin Chang
839e6027f48SAlvin Chang assert(cpu_mmu_enabled());
840e6027f48SAlvin Chang return external_dt.blob;
841e6027f48SAlvin Chang }
842e6027f48SAlvin Chang
release_external_dt(void)843e6027f48SAlvin Chang static TEE_Result release_external_dt(void)
844e6027f48SAlvin Chang {
845e6027f48SAlvin Chang int ret = 0;
84666763721SRaymond Mao paddr_t pa_dt = 0;
847e6027f48SAlvin Chang
848e6027f48SAlvin Chang if (!IS_ENABLED(CFG_EXTERNAL_DT))
849e6027f48SAlvin Chang return TEE_SUCCESS;
850e6027f48SAlvin Chang
851e6027f48SAlvin Chang if (!external_dt.blob)
852e6027f48SAlvin Chang return TEE_SUCCESS;
853e6027f48SAlvin Chang
85466763721SRaymond Mao pa_dt = virt_to_phys(external_dt.blob);
85566763721SRaymond Mao /*
85666763721SRaymond Mao * Skip packing and un-mapping operations if the external DTB is mapped
85766763721SRaymond Mao * in a different memory area
85866763721SRaymond Mao */
85966763721SRaymond Mao if (core_mmu_get_type_by_pa(pa_dt) != MEM_AREA_EXT_DT)
86066763721SRaymond Mao return TEE_SUCCESS;
86166763721SRaymond Mao
862e6027f48SAlvin Chang ret = fdt_pack(external_dt.blob);
863e6027f48SAlvin Chang if (ret < 0) {
864e6027f48SAlvin Chang EMSG("Failed to pack Device Tree at 0x%" PRIxPA ": error %d",
865e6027f48SAlvin Chang virt_to_phys(external_dt.blob), ret);
866e6027f48SAlvin Chang panic();
867e6027f48SAlvin Chang }
868e6027f48SAlvin Chang
869e6027f48SAlvin Chang if (core_mmu_remove_mapping(MEM_AREA_EXT_DT, external_dt.blob,
870e6027f48SAlvin Chang CFG_DTB_MAX_SIZE))
871e6027f48SAlvin Chang panic("Failed to remove temporary Device Tree mapping");
872e6027f48SAlvin Chang
873e6027f48SAlvin Chang /* External DTB no more reached, reset pointer to invalid */
874e6027f48SAlvin Chang external_dt.blob = NULL;
875e6027f48SAlvin Chang
876e6027f48SAlvin Chang return TEE_SUCCESS;
877e6027f48SAlvin Chang }
878e6027f48SAlvin Chang
879e6027f48SAlvin Chang boot_final(release_external_dt);
880e6027f48SAlvin Chang
add_dt_path_subnode(struct dt_descriptor * dt,const char * path,const char * subnode)881e6027f48SAlvin Chang int add_dt_path_subnode(struct dt_descriptor *dt, const char *path,
882e6027f48SAlvin Chang const char *subnode)
883e6027f48SAlvin Chang {
8840c49b6d6SAlvin Chang int offs = 0;
885e6027f48SAlvin Chang
886e6027f48SAlvin Chang offs = fdt_path_offset(dt->blob, path);
887e6027f48SAlvin Chang if (offs < 0)
8880c49b6d6SAlvin Chang return offs;
889e6027f48SAlvin Chang offs = add_dt_overlay_fragment(dt, offs);
890e6027f48SAlvin Chang if (offs < 0)
891e6027f48SAlvin Chang return offs;
8920c49b6d6SAlvin Chang return fdt_add_subnode(dt->blob, offs, subnode);
893e6027f48SAlvin Chang }
894e6027f48SAlvin Chang
set_dt_val(void * data,uint32_t cell_size,uint64_t val)895e6027f48SAlvin Chang static void set_dt_val(void *data, uint32_t cell_size, uint64_t val)
896e6027f48SAlvin Chang {
897e6027f48SAlvin Chang if (cell_size == 1) {
898e6027f48SAlvin Chang fdt32_t v = cpu_to_fdt32((uint32_t)val);
899e6027f48SAlvin Chang
900e6027f48SAlvin Chang memcpy(data, &v, sizeof(v));
901e6027f48SAlvin Chang } else {
902e6027f48SAlvin Chang fdt64_t v = cpu_to_fdt64(val);
903e6027f48SAlvin Chang
904e6027f48SAlvin Chang memcpy(data, &v, sizeof(v));
905e6027f48SAlvin Chang }
906e6027f48SAlvin Chang }
907e6027f48SAlvin Chang
add_res_mem_dt_node(struct dt_descriptor * dt,const char * name,paddr_t pa,size_t size)908e6027f48SAlvin Chang int add_res_mem_dt_node(struct dt_descriptor *dt, const char *name,
909e6027f48SAlvin Chang paddr_t pa, size_t size)
910e6027f48SAlvin Chang {
911e6027f48SAlvin Chang int offs = 0;
912e6027f48SAlvin Chang int ret = 0;
913e6027f48SAlvin Chang int addr_size = -1;
914e6027f48SAlvin Chang int len_size = -1;
915e6027f48SAlvin Chang bool found = true;
9160c49b6d6SAlvin Chang char subnode_name[80] = { };
917e6027f48SAlvin Chang
918e6027f48SAlvin Chang offs = fdt_path_offset(dt->blob, "/reserved-memory");
919e6027f48SAlvin Chang
920e6027f48SAlvin Chang if (offs < 0) {
921e6027f48SAlvin Chang found = false;
922e6027f48SAlvin Chang offs = 0;
923e6027f48SAlvin Chang }
924e6027f48SAlvin Chang
925e6027f48SAlvin Chang if (IS_ENABLED2(_CFG_USE_DTB_OVERLAY)) {
926e6027f48SAlvin Chang len_size = sizeof(paddr_t) / sizeof(uint32_t);
927e6027f48SAlvin Chang addr_size = sizeof(paddr_t) / sizeof(uint32_t);
928e6027f48SAlvin Chang } else {
929e6027f48SAlvin Chang len_size = fdt_size_cells(dt->blob, offs);
930e6027f48SAlvin Chang if (len_size < 0)
9310c49b6d6SAlvin Chang return len_size;
932e6027f48SAlvin Chang addr_size = fdt_address_cells(dt->blob, offs);
933e6027f48SAlvin Chang if (addr_size < 0)
9340c49b6d6SAlvin Chang return addr_size;
935e6027f48SAlvin Chang }
936e6027f48SAlvin Chang
937e6027f48SAlvin Chang if (!found) {
938e6027f48SAlvin Chang offs = add_dt_path_subnode(dt, "/", "reserved-memory");
939e6027f48SAlvin Chang if (offs < 0)
9400c49b6d6SAlvin Chang return offs;
941e6027f48SAlvin Chang ret = fdt_setprop_cell(dt->blob, offs, "#address-cells",
942e6027f48SAlvin Chang addr_size);
943e6027f48SAlvin Chang if (ret < 0)
9440c49b6d6SAlvin Chang return ret;
945e6027f48SAlvin Chang ret = fdt_setprop_cell(dt->blob, offs, "#size-cells", len_size);
946e6027f48SAlvin Chang if (ret < 0)
9470c49b6d6SAlvin Chang return ret;
948e6027f48SAlvin Chang ret = fdt_setprop(dt->blob, offs, "ranges", NULL, 0);
949e6027f48SAlvin Chang if (ret < 0)
9500c49b6d6SAlvin Chang return ret;
951e6027f48SAlvin Chang }
952e6027f48SAlvin Chang
953e6027f48SAlvin Chang ret = snprintf(subnode_name, sizeof(subnode_name),
954e6027f48SAlvin Chang "%s@%" PRIxPA, name, pa);
955e6027f48SAlvin Chang if (ret < 0 || ret >= (int)sizeof(subnode_name))
956e6027f48SAlvin Chang DMSG("truncated node \"%s@%" PRIxPA"\"", name, pa);
957e6027f48SAlvin Chang offs = fdt_add_subnode(dt->blob, offs, subnode_name);
958e6027f48SAlvin Chang if (offs >= 0) {
9590c49b6d6SAlvin Chang uint32_t data[FDT_MAX_NCELLS * 2] = { };
960e6027f48SAlvin Chang
961e6027f48SAlvin Chang set_dt_val(data, addr_size, pa);
962e6027f48SAlvin Chang set_dt_val(data + addr_size, len_size, size);
963e6027f48SAlvin Chang ret = fdt_setprop(dt->blob, offs, "reg", data,
964e6027f48SAlvin Chang sizeof(uint32_t) * (addr_size + len_size));
965e6027f48SAlvin Chang if (ret < 0)
9660c49b6d6SAlvin Chang return ret;
967e6027f48SAlvin Chang ret = fdt_setprop(dt->blob, offs, "no-map", NULL, 0);
968e6027f48SAlvin Chang if (ret < 0)
9690c49b6d6SAlvin Chang return ret;
970e6027f48SAlvin Chang } else {
9710c49b6d6SAlvin Chang return offs;
972e6027f48SAlvin Chang }
973e6027f48SAlvin Chang return 0;
974e6027f48SAlvin Chang }
9754bc2c5f0SSungbae Yoo
9764bc2c5f0SSungbae Yoo #if defined(CFG_CORE_FFA)
init_manifest_dt(void * fdt,size_t max_size)977414123aeSJens Wiklander void init_manifest_dt(void *fdt, size_t max_size)
9784bc2c5f0SSungbae Yoo {
9794bc2c5f0SSungbae Yoo manifest_dt = fdt;
980414123aeSJens Wiklander manifest_max_size = max_size;
9814bc2c5f0SSungbae Yoo }
9824bc2c5f0SSungbae Yoo
reinit_manifest_dt(void)9834bc2c5f0SSungbae Yoo void reinit_manifest_dt(void)
9844bc2c5f0SSungbae Yoo {
985414123aeSJens Wiklander paddr_t end_pa = 0;
9864bc2c5f0SSungbae Yoo void *fdt = NULL;
987414123aeSJens Wiklander paddr_t pa = 0;
9884bc2c5f0SSungbae Yoo int ret = 0;
9894bc2c5f0SSungbae Yoo
990414123aeSJens Wiklander if (!manifest_dt) {
9914bc2c5f0SSungbae Yoo EMSG("No manifest DT found");
9924bc2c5f0SSungbae Yoo return;
9934bc2c5f0SSungbae Yoo }
9944bc2c5f0SSungbae Yoo
995414123aeSJens Wiklander if (IS_ENABLED(CFG_CORE_SEL2_SPMC)) {
996414123aeSJens Wiklander pa = (unsigned long)manifest_dt;
997414123aeSJens Wiklander end_pa = pa + manifest_max_size;
998414123aeSJens Wiklander pa = ROUNDDOWN(pa, SMALL_PAGE_SIZE);
999414123aeSJens Wiklander end_pa = ROUNDUP(end_pa, SMALL_PAGE_SIZE);
1000*d40a2c00SJens Wiklander if (!nex_phys_mem_alloc2(pa, end_pa - pa)) {
1001414123aeSJens Wiklander EMSG("Failed to reserve manifest DT physical memory %#"PRIxPA"..%#"PRIxPA" len %#zx",
1002414123aeSJens Wiklander pa, end_pa - 1, end_pa - pa);
1003414123aeSJens Wiklander panic();
1004414123aeSJens Wiklander }
1005414123aeSJens Wiklander }
1006414123aeSJens Wiklander
1007414123aeSJens Wiklander pa = (unsigned long)manifest_dt;
1008414123aeSJens Wiklander fdt = core_mmu_add_mapping(MEM_AREA_MANIFEST_DT, pa, manifest_max_size);
10094bc2c5f0SSungbae Yoo if (!fdt)
10104bc2c5f0SSungbae Yoo panic("Failed to map manifest DT");
10114bc2c5f0SSungbae Yoo
10124bc2c5f0SSungbae Yoo manifest_dt = fdt;
10134bc2c5f0SSungbae Yoo
1014414123aeSJens Wiklander ret = fdt_check_full(fdt, manifest_max_size);
10154bc2c5f0SSungbae Yoo if (ret < 0) {
10164bc2c5f0SSungbae Yoo EMSG("Invalid manifest Device Tree at %#lx: error %d", pa, ret);
10174bc2c5f0SSungbae Yoo panic();
10184bc2c5f0SSungbae Yoo }
10194bc2c5f0SSungbae Yoo
10204bc2c5f0SSungbae Yoo IMSG("manifest DT found");
10214bc2c5f0SSungbae Yoo }
10224bc2c5f0SSungbae Yoo
get_manifest_dt(void)10234bc2c5f0SSungbae Yoo void *get_manifest_dt(void)
10244bc2c5f0SSungbae Yoo {
10254bc2c5f0SSungbae Yoo return manifest_dt;
10264bc2c5f0SSungbae Yoo }
10274bc2c5f0SSungbae Yoo
release_manifest_dt(void)10284bc2c5f0SSungbae Yoo static TEE_Result release_manifest_dt(void)
10294bc2c5f0SSungbae Yoo {
1030414123aeSJens Wiklander paddr_t pa = 0;
1031414123aeSJens Wiklander
10324bc2c5f0SSungbae Yoo if (!manifest_dt)
10334bc2c5f0SSungbae Yoo return TEE_SUCCESS;
10344bc2c5f0SSungbae Yoo
1035414123aeSJens Wiklander if (IS_ENABLED(CFG_CORE_SEL2_SPMC))
1036414123aeSJens Wiklander pa = virt_to_phys(manifest_dt);
1037414123aeSJens Wiklander
10384bc2c5f0SSungbae Yoo if (core_mmu_remove_mapping(MEM_AREA_MANIFEST_DT, manifest_dt,
1039414123aeSJens Wiklander manifest_max_size))
10404bc2c5f0SSungbae Yoo panic("Failed to remove temporary manifest DT mapping");
10414bc2c5f0SSungbae Yoo manifest_dt = NULL;
10424bc2c5f0SSungbae Yoo
1043414123aeSJens Wiklander if (IS_ENABLED(CFG_CORE_SEL2_SPMC))
1044*d40a2c00SJens Wiklander tee_mm_free(nex_phys_mem_mm_find(pa));
1045414123aeSJens Wiklander
10464bc2c5f0SSungbae Yoo return TEE_SUCCESS;
10474bc2c5f0SSungbae Yoo }
10484bc2c5f0SSungbae Yoo
10494bc2c5f0SSungbae Yoo boot_final(release_manifest_dt);
10504bc2c5f0SSungbae Yoo #else
init_manifest_dt(void * fdt __unused,size_t max_size __unused)1051414123aeSJens Wiklander void init_manifest_dt(void *fdt __unused, size_t max_size __unused)
10524bc2c5f0SSungbae Yoo {
10534bc2c5f0SSungbae Yoo }
10544bc2c5f0SSungbae Yoo
reinit_manifest_dt(void)10554bc2c5f0SSungbae Yoo void reinit_manifest_dt(void)
10564bc2c5f0SSungbae Yoo {
10574bc2c5f0SSungbae Yoo }
10584bc2c5f0SSungbae Yoo
get_manifest_dt(void)10594bc2c5f0SSungbae Yoo void *get_manifest_dt(void)
10604bc2c5f0SSungbae Yoo {
10614bc2c5f0SSungbae Yoo return NULL;
10624bc2c5f0SSungbae Yoo }
10634bc2c5f0SSungbae Yoo #endif /*CFG_CORE_FFA*/
1064