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> 7*e6027f48SAlvin Chang #include <config.h> 8*e6027f48SAlvin Chang #include <initcall.h> 9a4f139d7SJerome Forissier #include <kernel/dt.h> 109e3c57c8SEtienne Carriere #include <kernel/dt_driver.h> 117acb3a47SLudovic Barre #include <kernel/interrupt.h> 12bce4951cSJens Wiklander #include <kernel/linker.h> 139fe4c797SJerome Forissier #include <libfdt.h> 147ba16abbSJerome Forissier #include <mm/core_memprot.h> 157ba16abbSJerome Forissier #include <mm/core_mmu.h> 16*e6027f48SAlvin Chang #include <stdio.h> 17a4f139d7SJerome Forissier #include <string.h> 187ba16abbSJerome Forissier #include <trace.h> 19a4f139d7SJerome Forissier 20*e6027f48SAlvin Chang static struct dt_descriptor external_dt __nex_bss; 21*e6027f48SAlvin Chang 22a4f139d7SJerome Forissier const struct dt_driver *dt_find_compatible_driver(const void *fdt, int offs) 23a4f139d7SJerome Forissier { 24a4f139d7SJerome Forissier const struct dt_device_match *dm; 25a4f139d7SJerome Forissier const struct dt_driver *drv; 26a4f139d7SJerome Forissier 27db783ff8SEtienne Carriere for_each_dt_driver(drv) { 28db783ff8SEtienne Carriere for (dm = drv->match_table; dm; dm++) { 29db783ff8SEtienne Carriere if (!dm->compatible) { 30db783ff8SEtienne Carriere break; 31db783ff8SEtienne Carriere } 32a4f139d7SJerome Forissier if (!fdt_node_check_compatible(fdt, offs, 33db783ff8SEtienne Carriere dm->compatible)) { 34a4f139d7SJerome Forissier return drv; 35db783ff8SEtienne Carriere } 36db783ff8SEtienne Carriere } 37db783ff8SEtienne Carriere } 38a4f139d7SJerome Forissier 39a4f139d7SJerome Forissier return NULL; 40a4f139d7SJerome Forissier } 41a4f139d7SJerome Forissier 4250f3b323SPeng Fan bool dt_have_prop(const void *fdt, int offs, const char *propname) 4350f3b323SPeng Fan { 4450f3b323SPeng Fan const void *prop; 4550f3b323SPeng Fan 4650f3b323SPeng Fan prop = fdt_getprop(fdt, offs, propname, NULL); 4750f3b323SPeng Fan 4850f3b323SPeng Fan return prop; 4950f3b323SPeng Fan } 5050f3b323SPeng Fan 5195cdc5e0SCedric Neveux int dt_disable_status(void *fdt, int node) 5295cdc5e0SCedric Neveux { 5395cdc5e0SCedric Neveux const char *prop = NULL; 5495cdc5e0SCedric Neveux int len = 0; 5595cdc5e0SCedric Neveux 5695cdc5e0SCedric Neveux prop = fdt_getprop(fdt, node, "status", &len); 5795cdc5e0SCedric Neveux if (!prop) { 5895cdc5e0SCedric Neveux if (fdt_setprop_string(fdt, node, "status", "disabled")) 5995cdc5e0SCedric Neveux return -1; 6095cdc5e0SCedric Neveux } else { 6195cdc5e0SCedric Neveux /* 6295cdc5e0SCedric Neveux * Status is there, modify it. 6395cdc5e0SCedric Neveux * Ask to set "disabled" value to the property. The value 6495cdc5e0SCedric Neveux * will be automatically truncated with "len" size by the 6595cdc5e0SCedric Neveux * fdt_setprop_inplace function. 6695cdc5e0SCedric Neveux * Setting a value different from "ok" or "okay" will disable 6795cdc5e0SCedric Neveux * the property. 6895cdc5e0SCedric Neveux * Setting a truncated value of "disabled" with the original 6995cdc5e0SCedric Neveux * property "len" is preferred to not increase the DT size and 7095cdc5e0SCedric Neveux * losing time in recalculating the overall DT offsets. 7195cdc5e0SCedric Neveux * If original length of the status property is larger than 7295cdc5e0SCedric Neveux * "disabled", the property will start with "disabled" and be 7395cdc5e0SCedric Neveux * completed with the rest of the original property. 7495cdc5e0SCedric Neveux */ 7595cdc5e0SCedric Neveux if (fdt_setprop_inplace(fdt, node, "status", "disabled", len)) 7695cdc5e0SCedric Neveux return -1; 7795cdc5e0SCedric Neveux } 7895cdc5e0SCedric Neveux 7995cdc5e0SCedric Neveux return 0; 8095cdc5e0SCedric Neveux } 8195cdc5e0SCedric Neveux 8295cdc5e0SCedric Neveux int dt_enable_secure_status(void *fdt, int node) 8395cdc5e0SCedric Neveux { 8495cdc5e0SCedric Neveux if (dt_disable_status(fdt, node)) { 8595cdc5e0SCedric Neveux EMSG("Unable to disable Normal Status"); 8695cdc5e0SCedric Neveux return -1; 8795cdc5e0SCedric Neveux } 8895cdc5e0SCedric Neveux 8995cdc5e0SCedric Neveux if (fdt_setprop_string(fdt, node, "secure-status", "okay")) 9095cdc5e0SCedric Neveux return -1; 9195cdc5e0SCedric Neveux 9295cdc5e0SCedric Neveux return 0; 9395cdc5e0SCedric Neveux } 9495cdc5e0SCedric Neveux 95a5d5bbc8SVesa Jääskeläinen int dt_map_dev(const void *fdt, int offs, vaddr_t *base, size_t *size, 96a5d5bbc8SVesa Jääskeläinen enum dt_map_dev_directive mapping) 977ba16abbSJerome Forissier { 987ba16abbSJerome Forissier enum teecore_memtypes mtype; 997ba16abbSJerome Forissier paddr_t pbase; 1007ba16abbSJerome Forissier vaddr_t vbase; 101df7cecc0SLionel Debieve size_t sz; 1027ba16abbSJerome Forissier int st; 1037ba16abbSJerome Forissier 1047ba16abbSJerome Forissier assert(cpu_mmu_enabled()); 1057ba16abbSJerome Forissier 106f354a5d8SGatien Chevallier st = fdt_get_status(fdt, offs); 1077ba16abbSJerome Forissier if (st == DT_STATUS_DISABLED) 1087ba16abbSJerome Forissier return -1; 1097ba16abbSJerome Forissier 110f354a5d8SGatien Chevallier pbase = fdt_reg_base_address(fdt, offs); 111c0cfb36cSEtienne Carriere if (pbase == DT_INFO_INVALID_REG) 1127ba16abbSJerome Forissier return -1; 113f354a5d8SGatien Chevallier sz = fdt_reg_size(fdt, offs); 114df7cecc0SLionel Debieve if (sz == DT_INFO_INVALID_REG_SIZE) 1157ba16abbSJerome Forissier return -1; 1167ba16abbSJerome Forissier 117a5d5bbc8SVesa Jääskeläinen switch (mapping) { 118a5d5bbc8SVesa Jääskeläinen case DT_MAP_AUTO: 1197ba16abbSJerome Forissier if ((st & DT_STATUS_OK_SEC) && !(st & DT_STATUS_OK_NSEC)) 1207ba16abbSJerome Forissier mtype = MEM_AREA_IO_SEC; 1217ba16abbSJerome Forissier else 1227ba16abbSJerome Forissier mtype = MEM_AREA_IO_NSEC; 123a5d5bbc8SVesa Jääskeläinen break; 124a5d5bbc8SVesa Jääskeläinen case DT_MAP_SECURE: 125a5d5bbc8SVesa Jääskeläinen mtype = MEM_AREA_IO_SEC; 126a5d5bbc8SVesa Jääskeläinen break; 127a5d5bbc8SVesa Jääskeläinen case DT_MAP_NON_SECURE: 128a5d5bbc8SVesa Jääskeläinen mtype = MEM_AREA_IO_NSEC; 129a5d5bbc8SVesa Jääskeläinen break; 130a5d5bbc8SVesa Jääskeläinen default: 131a5d5bbc8SVesa Jääskeläinen panic("Invalid mapping specified"); 132a5d5bbc8SVesa Jääskeläinen break; 133a5d5bbc8SVesa Jääskeläinen } 1347ba16abbSJerome Forissier 1357ba16abbSJerome Forissier /* Check if we have a mapping, create one if needed */ 136bc9618c0SAnton Rybakov vbase = (vaddr_t)core_mmu_add_mapping(mtype, pbase, sz); 137bc9618c0SAnton Rybakov if (!vbase) { 1387ba16abbSJerome Forissier EMSG("Failed to map %zu bytes at PA 0x%"PRIxPA, 13923b1daf4SPeng Fan (size_t)sz, pbase); 1407ba16abbSJerome Forissier return -1; 1417ba16abbSJerome Forissier } 1427ba16abbSJerome Forissier 1437ba16abbSJerome Forissier *base = vbase; 1447ba16abbSJerome Forissier *size = sz; 1457ba16abbSJerome Forissier return 0; 1467ba16abbSJerome Forissier } 1477ba16abbSJerome Forissier 1489fe4c797SJerome Forissier /* Read a physical address (n=1 or 2 cells) */ 149f354a5d8SGatien Chevallier static paddr_t fdt_read_paddr(const uint32_t *cell, int n) 1509fe4c797SJerome Forissier { 1519fe4c797SJerome Forissier paddr_t addr; 1529fe4c797SJerome Forissier 1539fe4c797SJerome Forissier if (n < 1 || n > 2) 1549fe4c797SJerome Forissier goto bad; 1559fe4c797SJerome Forissier 1569fe4c797SJerome Forissier addr = fdt32_to_cpu(*cell); 1579fe4c797SJerome Forissier cell++; 1589fe4c797SJerome Forissier if (n == 2) { 1599fe4c797SJerome Forissier #ifdef ARM32 1609fe4c797SJerome Forissier if (addr) { 1619fe4c797SJerome Forissier /* High order 32 bits can't be nonzero */ 1629fe4c797SJerome Forissier goto bad; 1639fe4c797SJerome Forissier } 1649fe4c797SJerome Forissier addr = fdt32_to_cpu(*cell); 1659fe4c797SJerome Forissier #else 1669fe4c797SJerome Forissier addr = (addr << 32) | fdt32_to_cpu(*cell); 1679fe4c797SJerome Forissier #endif 1689fe4c797SJerome Forissier } 1699fe4c797SJerome Forissier 1709fe4c797SJerome Forissier return addr; 1719fe4c797SJerome Forissier bad: 172c0cfb36cSEtienne Carriere return DT_INFO_INVALID_REG; 1739fe4c797SJerome Forissier 1749fe4c797SJerome Forissier } 1759fe4c797SJerome Forissier 176f354a5d8SGatien Chevallier paddr_t fdt_reg_base_address(const void *fdt, int offs) 1779fe4c797SJerome Forissier { 1789fe4c797SJerome Forissier const void *reg; 1799fe4c797SJerome Forissier int ncells; 1809fe4c797SJerome Forissier int len; 18134deb103SPeng Fan int parent; 18234deb103SPeng Fan 18334deb103SPeng Fan parent = fdt_parent_offset(fdt, offs); 18434deb103SPeng Fan if (parent < 0) 185c0cfb36cSEtienne Carriere return DT_INFO_INVALID_REG; 1869fe4c797SJerome Forissier 1879fe4c797SJerome Forissier reg = fdt_getprop(fdt, offs, "reg", &len); 1889fe4c797SJerome Forissier if (!reg) 189c0cfb36cSEtienne Carriere return DT_INFO_INVALID_REG; 1909fe4c797SJerome Forissier 19134deb103SPeng Fan ncells = fdt_address_cells(fdt, parent); 1929fe4c797SJerome Forissier if (ncells < 0) 193c0cfb36cSEtienne Carriere return DT_INFO_INVALID_REG; 1949fe4c797SJerome Forissier 195f354a5d8SGatien Chevallier return fdt_read_paddr(reg, ncells); 1969fe4c797SJerome Forissier } 1979fe4c797SJerome Forissier 198a2e8c036SGatien Chevallier static size_t fdt_read_size(const uint32_t *cell, int n) 199a2e8c036SGatien Chevallier { 200a2e8c036SGatien Chevallier uint32_t sz = 0; 201a2e8c036SGatien Chevallier 202a2e8c036SGatien Chevallier sz = fdt32_to_cpu(*cell); 203a2e8c036SGatien Chevallier if (n == 2) { 204a2e8c036SGatien Chevallier if (sz) 205a2e8c036SGatien Chevallier return DT_INFO_INVALID_REG_SIZE; 206a2e8c036SGatien Chevallier 207a2e8c036SGatien Chevallier cell++; 208a2e8c036SGatien Chevallier sz = fdt32_to_cpu(*cell); 209a2e8c036SGatien Chevallier } 210a2e8c036SGatien Chevallier 211a2e8c036SGatien Chevallier return sz; 212a2e8c036SGatien Chevallier } 213a2e8c036SGatien Chevallier 214f354a5d8SGatien Chevallier size_t fdt_reg_size(const void *fdt, int offs) 2159fe4c797SJerome Forissier { 2169fe4c797SJerome Forissier const uint32_t *reg; 2179fe4c797SJerome Forissier int n; 2189fe4c797SJerome Forissier int len; 21934deb103SPeng Fan int parent; 22034deb103SPeng Fan 22134deb103SPeng Fan parent = fdt_parent_offset(fdt, offs); 22234deb103SPeng Fan if (parent < 0) 2231527e616SMarek Vasut return DT_INFO_INVALID_REG_SIZE; 2249fe4c797SJerome Forissier 2259fe4c797SJerome Forissier reg = (const uint32_t *)fdt_getprop(fdt, offs, "reg", &len); 2269fe4c797SJerome Forissier if (!reg) 227df7cecc0SLionel Debieve return DT_INFO_INVALID_REG_SIZE; 2289fe4c797SJerome Forissier 22934deb103SPeng Fan n = fdt_address_cells(fdt, parent); 2309fe4c797SJerome Forissier if (n < 1 || n > 2) 231df7cecc0SLionel Debieve return DT_INFO_INVALID_REG_SIZE; 2329fe4c797SJerome Forissier 2339fe4c797SJerome Forissier reg += n; 2349fe4c797SJerome Forissier 23534deb103SPeng Fan n = fdt_size_cells(fdt, parent); 2369fe4c797SJerome Forissier if (n < 1 || n > 2) 237df7cecc0SLionel Debieve return DT_INFO_INVALID_REG_SIZE; 2389fe4c797SJerome Forissier 239a2e8c036SGatien Chevallier return fdt_read_size(reg, n); 2409fe4c797SJerome Forissier } 2419fe4c797SJerome Forissier 2429fe4c797SJerome Forissier static bool is_okay(const char *st, int len) 2439fe4c797SJerome Forissier { 2449fe4c797SJerome Forissier return !strncmp(st, "ok", len) || !strncmp(st, "okay", len); 2459fe4c797SJerome Forissier } 2469fe4c797SJerome Forissier 247f354a5d8SGatien Chevallier int fdt_get_status(const void *fdt, int offs) 2489fe4c797SJerome Forissier { 2499fe4c797SJerome Forissier const char *prop; 2509fe4c797SJerome Forissier int st = 0; 2519fe4c797SJerome Forissier int len; 2529fe4c797SJerome Forissier 2539fe4c797SJerome Forissier prop = fdt_getprop(fdt, offs, "status", &len); 2549fe4c797SJerome Forissier if (!prop || is_okay(prop, len)) { 2559fe4c797SJerome Forissier /* If status is not specified, it defaults to "okay" */ 2569fe4c797SJerome Forissier st |= DT_STATUS_OK_NSEC; 2579fe4c797SJerome Forissier } 2589fe4c797SJerome Forissier 2599fe4c797SJerome Forissier prop = fdt_getprop(fdt, offs, "secure-status", &len); 2609fe4c797SJerome Forissier if (!prop) { 2619fe4c797SJerome Forissier /* 2629fe4c797SJerome Forissier * When secure-status is not specified it defaults to the same 2639fe4c797SJerome Forissier * value as status 2649fe4c797SJerome Forissier */ 2659fe4c797SJerome Forissier if (st & DT_STATUS_OK_NSEC) 2669fe4c797SJerome Forissier st |= DT_STATUS_OK_SEC; 2679fe4c797SJerome Forissier } else { 2689fe4c797SJerome Forissier if (is_okay(prop, len)) 2699fe4c797SJerome Forissier st |= DT_STATUS_OK_SEC; 2709fe4c797SJerome Forissier } 2719fe4c797SJerome Forissier 2729fe4c797SJerome Forissier return st; 2739fe4c797SJerome Forissier } 274c0cfb36cSEtienne Carriere 275f354a5d8SGatien Chevallier void fdt_fill_device_info(const void *fdt, struct dt_node_info *info, int offs) 276c0cfb36cSEtienne Carriere { 277c0cfb36cSEtienne Carriere struct dt_node_info dinfo = { 278c0cfb36cSEtienne Carriere .reg = DT_INFO_INVALID_REG, 27906fd21ddSLionel Debieve .reg_size = DT_INFO_INVALID_REG_SIZE, 280c0cfb36cSEtienne Carriere .clock = DT_INFO_INVALID_CLOCK, 281c0cfb36cSEtienne Carriere .reset = DT_INFO_INVALID_RESET, 2827acb3a47SLudovic Barre .interrupt = DT_INFO_INVALID_INTERRUPT, 283c0cfb36cSEtienne Carriere }; 284c0cfb36cSEtienne Carriere const fdt32_t *cuint; 285c0cfb36cSEtienne Carriere 286f354a5d8SGatien Chevallier dinfo.reg = fdt_reg_base_address(fdt, offs); 287f354a5d8SGatien Chevallier dinfo.reg_size = fdt_reg_size(fdt, offs); 288c0cfb36cSEtienne Carriere 289c0cfb36cSEtienne Carriere cuint = fdt_getprop(fdt, offs, "clocks", NULL); 290c0cfb36cSEtienne Carriere if (cuint) { 291c0cfb36cSEtienne Carriere cuint++; 292c0cfb36cSEtienne Carriere dinfo.clock = (int)fdt32_to_cpu(*cuint); 293c0cfb36cSEtienne Carriere } 294c0cfb36cSEtienne Carriere 295c0cfb36cSEtienne Carriere cuint = fdt_getprop(fdt, offs, "resets", NULL); 296c0cfb36cSEtienne Carriere if (cuint) { 297c0cfb36cSEtienne Carriere cuint++; 298c0cfb36cSEtienne Carriere dinfo.reset = (int)fdt32_to_cpu(*cuint); 299c0cfb36cSEtienne Carriere } 300c0cfb36cSEtienne Carriere 301702fe5a7SClément Léger dinfo.interrupt = dt_get_irq_type_prio(fdt, offs, &dinfo.type, 302702fe5a7SClément Léger &dinfo.prio); 3037acb3a47SLudovic Barre 304f354a5d8SGatien Chevallier dinfo.status = fdt_get_status(fdt, offs); 305c0cfb36cSEtienne Carriere 306c0cfb36cSEtienne Carriere *info = dinfo; 307c0cfb36cSEtienne Carriere } 308876826f3SGabriel Fernandez 309f354a5d8SGatien Chevallier int fdt_read_uint32_array(const void *fdt, int node, const char *prop_name, 310876826f3SGabriel Fernandez uint32_t *array, size_t count) 311876826f3SGabriel Fernandez { 312876826f3SGabriel Fernandez const fdt32_t *cuint = NULL; 313876826f3SGabriel Fernandez int len = 0; 314876826f3SGabriel Fernandez uint32_t i = 0; 315876826f3SGabriel Fernandez 316876826f3SGabriel Fernandez cuint = fdt_getprop(fdt, node, prop_name, &len); 317876826f3SGabriel Fernandez if (!cuint) 31873e27bfaSGatien Chevallier return len; 319876826f3SGabriel Fernandez 320876826f3SGabriel Fernandez if ((uint32_t)len != (count * sizeof(uint32_t))) 321876826f3SGabriel Fernandez return -FDT_ERR_BADLAYOUT; 322876826f3SGabriel Fernandez 323876826f3SGabriel Fernandez for (i = 0; i < ((uint32_t)len / sizeof(uint32_t)); i++) { 324876826f3SGabriel Fernandez *array = fdt32_to_cpu(*cuint); 325876826f3SGabriel Fernandez array++; 326876826f3SGabriel Fernandez cuint++; 327876826f3SGabriel Fernandez } 328876826f3SGabriel Fernandez 329876826f3SGabriel Fernandez return 0; 330876826f3SGabriel Fernandez } 331876826f3SGabriel Fernandez 3327c3a6b7bSGatien Chevallier int fdt_read_uint32_index(const void *fdt, int node, const char *prop_name, 3337c3a6b7bSGatien Chevallier int index, uint32_t *value) 3347c3a6b7bSGatien Chevallier { 3357c3a6b7bSGatien Chevallier const fdt32_t *cuint = NULL; 3367c3a6b7bSGatien Chevallier int len = 0; 3377c3a6b7bSGatien Chevallier 3387c3a6b7bSGatien Chevallier cuint = fdt_getprop(fdt, node, prop_name, &len); 3397c3a6b7bSGatien Chevallier if (!cuint) 3407c3a6b7bSGatien Chevallier return len; 3417c3a6b7bSGatien Chevallier 3427c3a6b7bSGatien Chevallier if ((uint32_t)len < (sizeof(uint32_t) * (index + 1))) 3437c3a6b7bSGatien Chevallier return -FDT_ERR_BADLAYOUT; 3447c3a6b7bSGatien Chevallier 3457c3a6b7bSGatien Chevallier *value = fdt32_to_cpu(cuint[index]); 3467c3a6b7bSGatien Chevallier 3477c3a6b7bSGatien Chevallier return 0; 3487c3a6b7bSGatien Chevallier } 3497c3a6b7bSGatien Chevallier 350f354a5d8SGatien Chevallier int fdt_read_uint32(const void *fdt, int node, const char *prop_name, 351876826f3SGabriel Fernandez uint32_t *value) 352876826f3SGabriel Fernandez { 353f354a5d8SGatien Chevallier return fdt_read_uint32_array(fdt, node, prop_name, value, 1); 354876826f3SGabriel Fernandez } 355876826f3SGabriel Fernandez 356f354a5d8SGatien Chevallier uint32_t fdt_read_uint32_default(const void *fdt, int node, 357876826f3SGabriel Fernandez const char *prop_name, uint32_t dflt_value) 358876826f3SGabriel Fernandez { 3597c3a6b7bSGatien Chevallier uint32_t ret = dflt_value; 360876826f3SGabriel Fernandez 3617c3a6b7bSGatien Chevallier fdt_read_uint32_index(fdt, node, prop_name, 0, &ret); 362876826f3SGabriel Fernandez 3637c3a6b7bSGatien Chevallier return ret; 364876826f3SGabriel Fernandez } 36507ced948SGatien Chevallier 36607ced948SGatien Chevallier int fdt_get_reg_props_by_index(const void *fdt, int node, int index, 36707ced948SGatien Chevallier paddr_t *base, size_t *size) 36807ced948SGatien Chevallier { 36907ced948SGatien Chevallier const fdt32_t *prop = NULL; 37007ced948SGatien Chevallier int parent = 0; 37107ced948SGatien Chevallier int len = 0; 37207ced948SGatien Chevallier int address_cells = 0; 37307ced948SGatien Chevallier int size_cells = 0; 37407ced948SGatien Chevallier int cell = 0; 37507ced948SGatien Chevallier 37607ced948SGatien Chevallier parent = fdt_parent_offset(fdt, node); 37707ced948SGatien Chevallier if (parent < 0) 37807ced948SGatien Chevallier return parent; 37907ced948SGatien Chevallier 38007ced948SGatien Chevallier address_cells = fdt_address_cells(fdt, parent); 38107ced948SGatien Chevallier if (address_cells < 0) 38207ced948SGatien Chevallier return address_cells; 38307ced948SGatien Chevallier 38407ced948SGatien Chevallier size_cells = fdt_size_cells(fdt, parent); 38507ced948SGatien Chevallier if (size_cells < 0) 38607ced948SGatien Chevallier return size_cells; 38707ced948SGatien Chevallier 38807ced948SGatien Chevallier cell = index * (address_cells + size_cells); 38907ced948SGatien Chevallier 39007ced948SGatien Chevallier prop = fdt_getprop(fdt, node, "reg", &len); 39107ced948SGatien Chevallier if (!prop) 39207ced948SGatien Chevallier return len; 39307ced948SGatien Chevallier 39407ced948SGatien Chevallier if (((cell + address_cells + size_cells) * (int)sizeof(uint32_t)) > len) 39507ced948SGatien Chevallier return -FDT_ERR_BADVALUE; 39607ced948SGatien Chevallier 39707ced948SGatien Chevallier if (base) { 39807ced948SGatien Chevallier *base = fdt_read_paddr(&prop[cell], address_cells); 39907ced948SGatien Chevallier if (*base == DT_INFO_INVALID_REG) 40007ced948SGatien Chevallier return -FDT_ERR_BADVALUE; 40107ced948SGatien Chevallier } 40207ced948SGatien Chevallier 40307ced948SGatien Chevallier if (size) { 40407ced948SGatien Chevallier *size = fdt_read_size(&prop[cell + address_cells], size_cells); 40507ced948SGatien Chevallier if (*size == DT_INFO_INVALID_REG_SIZE) 40607ced948SGatien Chevallier return -FDT_ERR_BADVALUE; 40707ced948SGatien Chevallier } 40807ced948SGatien Chevallier 40907ced948SGatien Chevallier return 0; 41007ced948SGatien Chevallier } 41107ced948SGatien Chevallier 41207ced948SGatien Chevallier int fdt_get_reg_props_by_name(const void *fdt, int node, const char *name, 41307ced948SGatien Chevallier paddr_t *base, size_t *size) 41407ced948SGatien Chevallier { 41507ced948SGatien Chevallier int index = 0; 41607ced948SGatien Chevallier 41707ced948SGatien Chevallier index = fdt_stringlist_search(fdt, node, "reg-names", name); 41807ced948SGatien Chevallier if (index < 0) 41907ced948SGatien Chevallier return index; 42007ced948SGatien Chevallier 42107ced948SGatien Chevallier return fdt_get_reg_props_by_index(fdt, node, index, base, size); 42207ced948SGatien Chevallier } 4234e45454aSJens Wiklander 4244e45454aSJens Wiklander int dt_getprop_as_number(const void *fdt, int nodeoffset, const char *name, 4254e45454aSJens Wiklander uint64_t *num) 4264e45454aSJens Wiklander { 4274e45454aSJens Wiklander const void *prop = NULL; 4284e45454aSJens Wiklander int len = 0; 4294e45454aSJens Wiklander 4304e45454aSJens Wiklander prop = fdt_getprop(fdt, nodeoffset, name, &len); 4314e45454aSJens Wiklander if (!prop) 4324e45454aSJens Wiklander return len; 4334e45454aSJens Wiklander 4344e45454aSJens Wiklander switch (len) { 4354e45454aSJens Wiklander case sizeof(uint32_t): 4364e45454aSJens Wiklander *num = fdt32_ld(prop); 4374e45454aSJens Wiklander return 0; 4384e45454aSJens Wiklander case sizeof(uint64_t): 4394e45454aSJens Wiklander *num = fdt64_ld(prop); 4404e45454aSJens Wiklander return 0; 4414e45454aSJens Wiklander default: 4424e45454aSJens Wiklander return -FDT_ERR_BADVALUE; 4434e45454aSJens Wiklander } 4444e45454aSJens Wiklander } 445*e6027f48SAlvin Chang 446*e6027f48SAlvin Chang void *get_dt(void) 447*e6027f48SAlvin Chang { 448*e6027f48SAlvin Chang void *fdt = get_embedded_dt(); 449*e6027f48SAlvin Chang 450*e6027f48SAlvin Chang if (!fdt) 451*e6027f48SAlvin Chang fdt = get_external_dt(); 452*e6027f48SAlvin Chang 453*e6027f48SAlvin Chang return fdt; 454*e6027f48SAlvin Chang } 455*e6027f48SAlvin Chang 456*e6027f48SAlvin Chang void *get_secure_dt(void) 457*e6027f48SAlvin Chang { 458*e6027f48SAlvin Chang void *fdt = get_embedded_dt(); 459*e6027f48SAlvin Chang 460*e6027f48SAlvin Chang if (!fdt && IS_ENABLED(CFG_MAP_EXT_DT_SECURE)) 461*e6027f48SAlvin Chang fdt = get_external_dt(); 462*e6027f48SAlvin Chang 463*e6027f48SAlvin Chang return fdt; 464*e6027f48SAlvin Chang } 465*e6027f48SAlvin Chang 466*e6027f48SAlvin Chang #if defined(CFG_EMBED_DTB) 467*e6027f48SAlvin Chang void *get_embedded_dt(void) 468*e6027f48SAlvin Chang { 469*e6027f48SAlvin Chang static bool checked; 470*e6027f48SAlvin Chang 471*e6027f48SAlvin Chang assert(cpu_mmu_enabled()); 472*e6027f48SAlvin Chang 473*e6027f48SAlvin Chang if (!checked) { 474*e6027f48SAlvin Chang IMSG("Embedded DTB found"); 475*e6027f48SAlvin Chang 476*e6027f48SAlvin Chang if (fdt_check_header(embedded_secure_dtb)) 477*e6027f48SAlvin Chang panic("Invalid embedded DTB"); 478*e6027f48SAlvin Chang 479*e6027f48SAlvin Chang checked = true; 480*e6027f48SAlvin Chang } 481*e6027f48SAlvin Chang 482*e6027f48SAlvin Chang return embedded_secure_dtb; 483*e6027f48SAlvin Chang } 484*e6027f48SAlvin Chang #else 485*e6027f48SAlvin Chang void *get_embedded_dt(void) 486*e6027f48SAlvin Chang { 487*e6027f48SAlvin Chang return NULL; 488*e6027f48SAlvin Chang } 489*e6027f48SAlvin Chang #endif /*CFG_EMBED_DTB*/ 490*e6027f48SAlvin Chang 491*e6027f48SAlvin Chang #ifdef _CFG_USE_DTB_OVERLAY 492*e6027f48SAlvin Chang static int add_dt_overlay_fragment(struct dt_descriptor *dt, int ioffs) 493*e6027f48SAlvin Chang { 494*e6027f48SAlvin Chang char frag[32]; 495*e6027f48SAlvin Chang int offs; 496*e6027f48SAlvin Chang int ret; 497*e6027f48SAlvin Chang 498*e6027f48SAlvin Chang snprintf(frag, sizeof(frag), "fragment@%d", dt->frag_id); 499*e6027f48SAlvin Chang offs = fdt_add_subnode(dt->blob, ioffs, frag); 500*e6027f48SAlvin Chang if (offs < 0) 501*e6027f48SAlvin Chang return offs; 502*e6027f48SAlvin Chang 503*e6027f48SAlvin Chang dt->frag_id += 1; 504*e6027f48SAlvin Chang 505*e6027f48SAlvin Chang ret = fdt_setprop_string(dt->blob, offs, "target-path", "/"); 506*e6027f48SAlvin Chang if (ret < 0) 507*e6027f48SAlvin Chang return -1; 508*e6027f48SAlvin Chang 509*e6027f48SAlvin Chang return fdt_add_subnode(dt->blob, offs, "__overlay__"); 510*e6027f48SAlvin Chang } 511*e6027f48SAlvin Chang 512*e6027f48SAlvin Chang static int init_dt_overlay(struct dt_descriptor *dt, int __maybe_unused dt_size) 513*e6027f48SAlvin Chang { 514*e6027f48SAlvin Chang int fragment; 515*e6027f48SAlvin Chang 516*e6027f48SAlvin Chang if (IS_ENABLED(CFG_EXTERNAL_DTB_OVERLAY)) { 517*e6027f48SAlvin Chang if (!fdt_check_header(dt->blob)) { 518*e6027f48SAlvin Chang fdt_for_each_subnode(fragment, dt->blob, 0) 519*e6027f48SAlvin Chang dt->frag_id += 1; 520*e6027f48SAlvin Chang return 0; 521*e6027f48SAlvin Chang } 522*e6027f48SAlvin Chang } 523*e6027f48SAlvin Chang 524*e6027f48SAlvin Chang return fdt_create_empty_tree(dt->blob, dt_size); 525*e6027f48SAlvin Chang } 526*e6027f48SAlvin Chang #else 527*e6027f48SAlvin Chang static int add_dt_overlay_fragment(struct dt_descriptor *dt __unused, int offs) 528*e6027f48SAlvin Chang { 529*e6027f48SAlvin Chang return offs; 530*e6027f48SAlvin Chang } 531*e6027f48SAlvin Chang 532*e6027f48SAlvin Chang static int init_dt_overlay(struct dt_descriptor *dt __unused, 533*e6027f48SAlvin Chang int dt_size __unused) 534*e6027f48SAlvin Chang { 535*e6027f48SAlvin Chang return 0; 536*e6027f48SAlvin Chang } 537*e6027f48SAlvin Chang #endif /* _CFG_USE_DTB_OVERLAY */ 538*e6027f48SAlvin Chang 539*e6027f48SAlvin Chang struct dt_descriptor *get_external_dt_desc(void) 540*e6027f48SAlvin Chang { 541*e6027f48SAlvin Chang if (!IS_ENABLED(CFG_EXTERNAL_DT)) 542*e6027f48SAlvin Chang return NULL; 543*e6027f48SAlvin Chang 544*e6027f48SAlvin Chang return &external_dt; 545*e6027f48SAlvin Chang } 546*e6027f48SAlvin Chang 547*e6027f48SAlvin Chang void init_external_dt(unsigned long phys_dt) 548*e6027f48SAlvin Chang { 549*e6027f48SAlvin Chang struct dt_descriptor *dt = &external_dt; 550*e6027f48SAlvin Chang void *fdt; 551*e6027f48SAlvin Chang int ret; 552*e6027f48SAlvin Chang 553*e6027f48SAlvin Chang if (!IS_ENABLED(CFG_EXTERNAL_DT)) 554*e6027f48SAlvin Chang return; 555*e6027f48SAlvin Chang 556*e6027f48SAlvin Chang if (!phys_dt) { 557*e6027f48SAlvin Chang /* 558*e6027f48SAlvin Chang * No need to panic as we're not using the DT in OP-TEE 559*e6027f48SAlvin Chang * yet, we're only adding some nodes for normal world use. 560*e6027f48SAlvin Chang * This makes the switch to using DT easier as we can boot 561*e6027f48SAlvin Chang * a newer OP-TEE with older boot loaders. Once we start to 562*e6027f48SAlvin Chang * initialize devices based on DT we'll likely panic 563*e6027f48SAlvin Chang * instead of returning here. 564*e6027f48SAlvin Chang */ 565*e6027f48SAlvin Chang IMSG("No non-secure external DT"); 566*e6027f48SAlvin Chang return; 567*e6027f48SAlvin Chang } 568*e6027f48SAlvin Chang 569*e6027f48SAlvin Chang fdt = core_mmu_add_mapping(MEM_AREA_EXT_DT, phys_dt, CFG_DTB_MAX_SIZE); 570*e6027f48SAlvin Chang if (!fdt) 571*e6027f48SAlvin Chang panic("Failed to map external DTB"); 572*e6027f48SAlvin Chang 573*e6027f48SAlvin Chang dt->blob = fdt; 574*e6027f48SAlvin Chang 575*e6027f48SAlvin Chang ret = init_dt_overlay(dt, CFG_DTB_MAX_SIZE); 576*e6027f48SAlvin Chang if (ret < 0) { 577*e6027f48SAlvin Chang EMSG("Device Tree Overlay init fail @ %#lx: error %d", phys_dt, 578*e6027f48SAlvin Chang ret); 579*e6027f48SAlvin Chang panic(); 580*e6027f48SAlvin Chang } 581*e6027f48SAlvin Chang 582*e6027f48SAlvin Chang ret = fdt_open_into(fdt, fdt, CFG_DTB_MAX_SIZE); 583*e6027f48SAlvin Chang if (ret < 0) { 584*e6027f48SAlvin Chang EMSG("Invalid Device Tree at %#lx: error %d", phys_dt, ret); 585*e6027f48SAlvin Chang panic(); 586*e6027f48SAlvin Chang } 587*e6027f48SAlvin Chang 588*e6027f48SAlvin Chang IMSG("Non-secure external DT found"); 589*e6027f48SAlvin Chang } 590*e6027f48SAlvin Chang 591*e6027f48SAlvin Chang void *get_external_dt(void) 592*e6027f48SAlvin Chang { 593*e6027f48SAlvin Chang if (!IS_ENABLED(CFG_EXTERNAL_DT)) 594*e6027f48SAlvin Chang return NULL; 595*e6027f48SAlvin Chang 596*e6027f48SAlvin Chang assert(cpu_mmu_enabled()); 597*e6027f48SAlvin Chang return external_dt.blob; 598*e6027f48SAlvin Chang } 599*e6027f48SAlvin Chang 600*e6027f48SAlvin Chang static TEE_Result release_external_dt(void) 601*e6027f48SAlvin Chang { 602*e6027f48SAlvin Chang int ret = 0; 603*e6027f48SAlvin Chang 604*e6027f48SAlvin Chang if (!IS_ENABLED(CFG_EXTERNAL_DT)) 605*e6027f48SAlvin Chang return TEE_SUCCESS; 606*e6027f48SAlvin Chang 607*e6027f48SAlvin Chang if (!external_dt.blob) 608*e6027f48SAlvin Chang return TEE_SUCCESS; 609*e6027f48SAlvin Chang 610*e6027f48SAlvin Chang ret = fdt_pack(external_dt.blob); 611*e6027f48SAlvin Chang if (ret < 0) { 612*e6027f48SAlvin Chang EMSG("Failed to pack Device Tree at 0x%" PRIxPA ": error %d", 613*e6027f48SAlvin Chang virt_to_phys(external_dt.blob), ret); 614*e6027f48SAlvin Chang panic(); 615*e6027f48SAlvin Chang } 616*e6027f48SAlvin Chang 617*e6027f48SAlvin Chang if (core_mmu_remove_mapping(MEM_AREA_EXT_DT, external_dt.blob, 618*e6027f48SAlvin Chang CFG_DTB_MAX_SIZE)) 619*e6027f48SAlvin Chang panic("Failed to remove temporary Device Tree mapping"); 620*e6027f48SAlvin Chang 621*e6027f48SAlvin Chang /* External DTB no more reached, reset pointer to invalid */ 622*e6027f48SAlvin Chang external_dt.blob = NULL; 623*e6027f48SAlvin Chang 624*e6027f48SAlvin Chang return TEE_SUCCESS; 625*e6027f48SAlvin Chang } 626*e6027f48SAlvin Chang 627*e6027f48SAlvin Chang boot_final(release_external_dt); 628*e6027f48SAlvin Chang 629*e6027f48SAlvin Chang int add_dt_path_subnode(struct dt_descriptor *dt, const char *path, 630*e6027f48SAlvin Chang const char *subnode) 631*e6027f48SAlvin Chang { 632*e6027f48SAlvin Chang int offs; 633*e6027f48SAlvin Chang 634*e6027f48SAlvin Chang offs = fdt_path_offset(dt->blob, path); 635*e6027f48SAlvin Chang if (offs < 0) 636*e6027f48SAlvin Chang return -1; 637*e6027f48SAlvin Chang offs = add_dt_overlay_fragment(dt, offs); 638*e6027f48SAlvin Chang if (offs < 0) 639*e6027f48SAlvin Chang return -1; 640*e6027f48SAlvin Chang offs = fdt_add_subnode(dt->blob, offs, subnode); 641*e6027f48SAlvin Chang if (offs < 0) 642*e6027f48SAlvin Chang return -1; 643*e6027f48SAlvin Chang return offs; 644*e6027f48SAlvin Chang } 645*e6027f48SAlvin Chang 646*e6027f48SAlvin Chang static void set_dt_val(void *data, uint32_t cell_size, uint64_t val) 647*e6027f48SAlvin Chang { 648*e6027f48SAlvin Chang if (cell_size == 1) { 649*e6027f48SAlvin Chang fdt32_t v = cpu_to_fdt32((uint32_t)val); 650*e6027f48SAlvin Chang 651*e6027f48SAlvin Chang memcpy(data, &v, sizeof(v)); 652*e6027f48SAlvin Chang } else { 653*e6027f48SAlvin Chang fdt64_t v = cpu_to_fdt64(val); 654*e6027f48SAlvin Chang 655*e6027f48SAlvin Chang memcpy(data, &v, sizeof(v)); 656*e6027f48SAlvin Chang } 657*e6027f48SAlvin Chang } 658*e6027f48SAlvin Chang 659*e6027f48SAlvin Chang int add_res_mem_dt_node(struct dt_descriptor *dt, const char *name, 660*e6027f48SAlvin Chang paddr_t pa, size_t size) 661*e6027f48SAlvin Chang { 662*e6027f48SAlvin Chang int offs = 0; 663*e6027f48SAlvin Chang int ret = 0; 664*e6027f48SAlvin Chang int addr_size = -1; 665*e6027f48SAlvin Chang int len_size = -1; 666*e6027f48SAlvin Chang bool found = true; 667*e6027f48SAlvin Chang char subnode_name[80] = { 0 }; 668*e6027f48SAlvin Chang 669*e6027f48SAlvin Chang offs = fdt_path_offset(dt->blob, "/reserved-memory"); 670*e6027f48SAlvin Chang 671*e6027f48SAlvin Chang if (offs < 0) { 672*e6027f48SAlvin Chang found = false; 673*e6027f48SAlvin Chang offs = 0; 674*e6027f48SAlvin Chang } 675*e6027f48SAlvin Chang 676*e6027f48SAlvin Chang if (IS_ENABLED2(_CFG_USE_DTB_OVERLAY)) { 677*e6027f48SAlvin Chang len_size = sizeof(paddr_t) / sizeof(uint32_t); 678*e6027f48SAlvin Chang addr_size = sizeof(paddr_t) / sizeof(uint32_t); 679*e6027f48SAlvin Chang } else { 680*e6027f48SAlvin Chang len_size = fdt_size_cells(dt->blob, offs); 681*e6027f48SAlvin Chang if (len_size < 0) 682*e6027f48SAlvin Chang return -1; 683*e6027f48SAlvin Chang addr_size = fdt_address_cells(dt->blob, offs); 684*e6027f48SAlvin Chang if (addr_size < 0) 685*e6027f48SAlvin Chang return -1; 686*e6027f48SAlvin Chang } 687*e6027f48SAlvin Chang 688*e6027f48SAlvin Chang if (!found) { 689*e6027f48SAlvin Chang offs = add_dt_path_subnode(dt, "/", "reserved-memory"); 690*e6027f48SAlvin Chang if (offs < 0) 691*e6027f48SAlvin Chang return -1; 692*e6027f48SAlvin Chang ret = fdt_setprop_cell(dt->blob, offs, "#address-cells", 693*e6027f48SAlvin Chang addr_size); 694*e6027f48SAlvin Chang if (ret < 0) 695*e6027f48SAlvin Chang return -1; 696*e6027f48SAlvin Chang ret = fdt_setprop_cell(dt->blob, offs, "#size-cells", len_size); 697*e6027f48SAlvin Chang if (ret < 0) 698*e6027f48SAlvin Chang return -1; 699*e6027f48SAlvin Chang ret = fdt_setprop(dt->blob, offs, "ranges", NULL, 0); 700*e6027f48SAlvin Chang if (ret < 0) 701*e6027f48SAlvin Chang return -1; 702*e6027f48SAlvin Chang } 703*e6027f48SAlvin Chang 704*e6027f48SAlvin Chang ret = snprintf(subnode_name, sizeof(subnode_name), 705*e6027f48SAlvin Chang "%s@%" PRIxPA, name, pa); 706*e6027f48SAlvin Chang if (ret < 0 || ret >= (int)sizeof(subnode_name)) 707*e6027f48SAlvin Chang DMSG("truncated node \"%s@%" PRIxPA"\"", name, pa); 708*e6027f48SAlvin Chang offs = fdt_add_subnode(dt->blob, offs, subnode_name); 709*e6027f48SAlvin Chang if (offs >= 0) { 710*e6027f48SAlvin Chang uint32_t data[FDT_MAX_NCELLS * 2]; 711*e6027f48SAlvin Chang 712*e6027f48SAlvin Chang set_dt_val(data, addr_size, pa); 713*e6027f48SAlvin Chang set_dt_val(data + addr_size, len_size, size); 714*e6027f48SAlvin Chang ret = fdt_setprop(dt->blob, offs, "reg", data, 715*e6027f48SAlvin Chang sizeof(uint32_t) * (addr_size + len_size)); 716*e6027f48SAlvin Chang if (ret < 0) 717*e6027f48SAlvin Chang return -1; 718*e6027f48SAlvin Chang ret = fdt_setprop(dt->blob, offs, "no-map", NULL, 0); 719*e6027f48SAlvin Chang if (ret < 0) 720*e6027f48SAlvin Chang return -1; 721*e6027f48SAlvin Chang } else { 722*e6027f48SAlvin Chang return -1; 723*e6027f48SAlvin Chang } 724*e6027f48SAlvin Chang return 0; 725*e6027f48SAlvin Chang } 726