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> 15*414123aeSJens 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; 24*414123aeSJens Wiklander static size_t manifest_max_size __nex_bss; 254bc2c5f0SSungbae Yoo #endif 264bc2c5f0SSungbae Yoo 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 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 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 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 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) */ 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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) 977*414123aeSJens Wiklander void init_manifest_dt(void *fdt, size_t max_size) 9784bc2c5f0SSungbae Yoo { 9794bc2c5f0SSungbae Yoo manifest_dt = fdt; 980*414123aeSJens Wiklander manifest_max_size = max_size; 9814bc2c5f0SSungbae Yoo } 9824bc2c5f0SSungbae Yoo 9834bc2c5f0SSungbae Yoo void reinit_manifest_dt(void) 9844bc2c5f0SSungbae Yoo { 985*414123aeSJens Wiklander paddr_t end_pa = 0; 9864bc2c5f0SSungbae Yoo void *fdt = NULL; 987*414123aeSJens Wiklander paddr_t pa = 0; 9884bc2c5f0SSungbae Yoo int ret = 0; 9894bc2c5f0SSungbae Yoo 990*414123aeSJens Wiklander if (!manifest_dt) { 9914bc2c5f0SSungbae Yoo EMSG("No manifest DT found"); 9924bc2c5f0SSungbae Yoo return; 9934bc2c5f0SSungbae Yoo } 9944bc2c5f0SSungbae Yoo 995*414123aeSJens Wiklander if (IS_ENABLED(CFG_CORE_SEL2_SPMC)) { 996*414123aeSJens Wiklander pa = (unsigned long)manifest_dt; 997*414123aeSJens Wiklander end_pa = pa + manifest_max_size; 998*414123aeSJens Wiklander pa = ROUNDDOWN(pa, SMALL_PAGE_SIZE); 999*414123aeSJens Wiklander end_pa = ROUNDUP(end_pa, SMALL_PAGE_SIZE); 1000*414123aeSJens Wiklander if (!phys_mem_alloc2(pa, end_pa - pa)) { 1001*414123aeSJens Wiklander EMSG("Failed to reserve manifest DT physical memory %#"PRIxPA"..%#"PRIxPA" len %#zx", 1002*414123aeSJens Wiklander pa, end_pa - 1, end_pa - pa); 1003*414123aeSJens Wiklander panic(); 1004*414123aeSJens Wiklander } 1005*414123aeSJens Wiklander } 1006*414123aeSJens Wiklander 1007*414123aeSJens Wiklander pa = (unsigned long)manifest_dt; 1008*414123aeSJens 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 1014*414123aeSJens 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 10234bc2c5f0SSungbae Yoo void *get_manifest_dt(void) 10244bc2c5f0SSungbae Yoo { 10254bc2c5f0SSungbae Yoo return manifest_dt; 10264bc2c5f0SSungbae Yoo } 10274bc2c5f0SSungbae Yoo 10284bc2c5f0SSungbae Yoo static TEE_Result release_manifest_dt(void) 10294bc2c5f0SSungbae Yoo { 1030*414123aeSJens Wiklander paddr_t pa = 0; 1031*414123aeSJens Wiklander 10324bc2c5f0SSungbae Yoo if (!manifest_dt) 10334bc2c5f0SSungbae Yoo return TEE_SUCCESS; 10344bc2c5f0SSungbae Yoo 1035*414123aeSJens Wiklander if (IS_ENABLED(CFG_CORE_SEL2_SPMC)) 1036*414123aeSJens Wiklander pa = virt_to_phys(manifest_dt); 1037*414123aeSJens Wiklander 10384bc2c5f0SSungbae Yoo if (core_mmu_remove_mapping(MEM_AREA_MANIFEST_DT, manifest_dt, 1039*414123aeSJens Wiklander manifest_max_size)) 10404bc2c5f0SSungbae Yoo panic("Failed to remove temporary manifest DT mapping"); 10414bc2c5f0SSungbae Yoo manifest_dt = NULL; 10424bc2c5f0SSungbae Yoo 1043*414123aeSJens Wiklander if (IS_ENABLED(CFG_CORE_SEL2_SPMC)) 1044*414123aeSJens Wiklander tee_mm_free(phys_mem_mm_find(pa)); 1045*414123aeSJens Wiklander 10464bc2c5f0SSungbae Yoo return TEE_SUCCESS; 10474bc2c5f0SSungbae Yoo } 10484bc2c5f0SSungbae Yoo 10494bc2c5f0SSungbae Yoo boot_final(release_manifest_dt); 10504bc2c5f0SSungbae Yoo #else 1051*414123aeSJens Wiklander void init_manifest_dt(void *fdt __unused, size_t max_size __unused) 10524bc2c5f0SSungbae Yoo { 10534bc2c5f0SSungbae Yoo } 10544bc2c5f0SSungbae Yoo 10554bc2c5f0SSungbae Yoo void reinit_manifest_dt(void) 10564bc2c5f0SSungbae Yoo { 10574bc2c5f0SSungbae Yoo } 10584bc2c5f0SSungbae Yoo 10594bc2c5f0SSungbae Yoo void *get_manifest_dt(void) 10604bc2c5f0SSungbae Yoo { 10614bc2c5f0SSungbae Yoo return NULL; 10624bc2c5f0SSungbae Yoo } 10634bc2c5f0SSungbae Yoo #endif /*CFG_CORE_FFA*/ 1064