1 // SPDX-License-Identifier: BSD-2-Clause 2 /* 3 * Copyright (c) 2016, Linaro Limited 4 */ 5 6 #include <assert.h> 7 #include <kernel/dt.h> 8 #include <kernel/linker.h> 9 #include <libfdt.h> 10 #include <mm/core_memprot.h> 11 #include <mm/core_mmu.h> 12 #include <string.h> 13 #include <trace.h> 14 15 const struct dt_driver *dt_find_compatible_driver(const void *fdt, int offs) 16 { 17 const struct dt_device_match *dm; 18 const struct dt_driver *drv; 19 20 for_each_dt_driver(drv) { 21 for (dm = drv->match_table; dm; dm++) { 22 if (!dm->compatible) { 23 break; 24 } 25 if (!fdt_node_check_compatible(fdt, offs, 26 dm->compatible)) { 27 return drv; 28 } 29 } 30 } 31 32 return NULL; 33 } 34 35 const struct dt_driver *__dt_driver_start(void) 36 { 37 return &__rodata_dtdrv_start; 38 } 39 40 const struct dt_driver *__dt_driver_end(void) 41 { 42 return &__rodata_dtdrv_end; 43 } 44 45 bool dt_have_prop(const void *fdt, int offs, const char *propname) 46 { 47 const void *prop; 48 49 prop = fdt_getprop(fdt, offs, propname, NULL); 50 51 return prop; 52 } 53 54 int dt_disable_status(void *fdt, int node) 55 { 56 const char *prop = NULL; 57 int len = 0; 58 59 prop = fdt_getprop(fdt, node, "status", &len); 60 if (!prop) { 61 if (fdt_setprop_string(fdt, node, "status", "disabled")) 62 return -1; 63 } else { 64 /* 65 * Status is there, modify it. 66 * Ask to set "disabled" value to the property. The value 67 * will be automatically truncated with "len" size by the 68 * fdt_setprop_inplace function. 69 * Setting a value different from "ok" or "okay" will disable 70 * the property. 71 * Setting a truncated value of "disabled" with the original 72 * property "len" is preferred to not increase the DT size and 73 * losing time in recalculating the overall DT offsets. 74 * If original length of the status property is larger than 75 * "disabled", the property will start with "disabled" and be 76 * completed with the rest of the original property. 77 */ 78 if (fdt_setprop_inplace(fdt, node, "status", "disabled", len)) 79 return -1; 80 } 81 82 return 0; 83 } 84 85 int dt_enable_secure_status(void *fdt, int node) 86 { 87 if (dt_disable_status(fdt, node)) { 88 EMSG("Unable to disable Normal Status"); 89 return -1; 90 } 91 92 if (fdt_setprop_string(fdt, node, "secure-status", "okay")) 93 return -1; 94 95 return 0; 96 } 97 98 int dt_map_dev(const void *fdt, int offs, vaddr_t *base, size_t *size) 99 { 100 enum teecore_memtypes mtype; 101 paddr_t pbase; 102 vaddr_t vbase; 103 ssize_t sz; 104 int st; 105 106 assert(cpu_mmu_enabled()); 107 108 st = _fdt_get_status(fdt, offs); 109 if (st == DT_STATUS_DISABLED) 110 return -1; 111 112 pbase = _fdt_reg_base_address(fdt, offs); 113 if (pbase == DT_INFO_INVALID_REG) 114 return -1; 115 sz = _fdt_reg_size(fdt, offs); 116 if (sz < 0) 117 return -1; 118 119 if ((st & DT_STATUS_OK_SEC) && !(st & DT_STATUS_OK_NSEC)) 120 mtype = MEM_AREA_IO_SEC; 121 else 122 mtype = MEM_AREA_IO_NSEC; 123 124 /* Check if we have a mapping, create one if needed */ 125 if (!core_mmu_add_mapping(mtype, pbase, sz)) { 126 EMSG("Failed to map %zu bytes at PA 0x%"PRIxPA, 127 (size_t)sz, pbase); 128 return -1; 129 } 130 vbase = (vaddr_t)phys_to_virt(pbase, mtype); 131 if (!vbase) { 132 EMSG("Failed to get VA for PA 0x%"PRIxPA, pbase); 133 return -1; 134 } 135 136 *base = vbase; 137 *size = sz; 138 return 0; 139 } 140 141 /* Read a physical address (n=1 or 2 cells) */ 142 static paddr_t _fdt_read_paddr(const uint32_t *cell, int n) 143 { 144 paddr_t addr; 145 146 if (n < 1 || n > 2) 147 goto bad; 148 149 addr = fdt32_to_cpu(*cell); 150 cell++; 151 if (n == 2) { 152 #ifdef ARM32 153 if (addr) { 154 /* High order 32 bits can't be nonzero */ 155 goto bad; 156 } 157 addr = fdt32_to_cpu(*cell); 158 #else 159 addr = (addr << 32) | fdt32_to_cpu(*cell); 160 #endif 161 } 162 163 if (!addr) 164 goto bad; 165 166 return addr; 167 bad: 168 return DT_INFO_INVALID_REG; 169 170 } 171 172 paddr_t _fdt_reg_base_address(const void *fdt, int offs) 173 { 174 const void *reg; 175 int ncells; 176 int len; 177 int parent; 178 179 parent = fdt_parent_offset(fdt, offs); 180 if (parent < 0) 181 return DT_INFO_INVALID_REG; 182 183 reg = fdt_getprop(fdt, offs, "reg", &len); 184 if (!reg) 185 return DT_INFO_INVALID_REG; 186 187 ncells = fdt_address_cells(fdt, parent); 188 if (ncells < 0) 189 return DT_INFO_INVALID_REG; 190 191 return _fdt_read_paddr(reg, ncells); 192 } 193 194 ssize_t _fdt_reg_size(const void *fdt, int offs) 195 { 196 const uint32_t *reg; 197 uint32_t sz; 198 int n; 199 int len; 200 int parent; 201 202 parent = fdt_parent_offset(fdt, offs); 203 if (parent < 0) 204 return DT_INFO_INVALID_REG_SIZE; 205 206 reg = (const uint32_t *)fdt_getprop(fdt, offs, "reg", &len); 207 if (!reg) 208 return -1; 209 210 n = fdt_address_cells(fdt, parent); 211 if (n < 1 || n > 2) 212 return -1; 213 214 reg += n; 215 216 n = fdt_size_cells(fdt, parent); 217 if (n < 1 || n > 2) 218 return -1; 219 220 sz = fdt32_to_cpu(*reg); 221 if (n == 2) { 222 if (sz) 223 return -1; 224 reg++; 225 sz = fdt32_to_cpu(*reg); 226 } 227 228 return sz; 229 } 230 231 static bool is_okay(const char *st, int len) 232 { 233 return !strncmp(st, "ok", len) || !strncmp(st, "okay", len); 234 } 235 236 int _fdt_get_status(const void *fdt, int offs) 237 { 238 const char *prop; 239 int st = 0; 240 int len; 241 242 prop = fdt_getprop(fdt, offs, "status", &len); 243 if (!prop || is_okay(prop, len)) { 244 /* If status is not specified, it defaults to "okay" */ 245 st |= DT_STATUS_OK_NSEC; 246 } 247 248 prop = fdt_getprop(fdt, offs, "secure-status", &len); 249 if (!prop) { 250 /* 251 * When secure-status is not specified it defaults to the same 252 * value as status 253 */ 254 if (st & DT_STATUS_OK_NSEC) 255 st |= DT_STATUS_OK_SEC; 256 } else { 257 if (is_okay(prop, len)) 258 st |= DT_STATUS_OK_SEC; 259 } 260 261 return st; 262 } 263 264 void _fdt_fill_device_info(void *fdt, struct dt_node_info *info, int offs) 265 { 266 struct dt_node_info dinfo = { 267 .reg = DT_INFO_INVALID_REG, 268 .clock = DT_INFO_INVALID_CLOCK, 269 .reset = DT_INFO_INVALID_RESET, 270 }; 271 const fdt32_t *cuint; 272 273 dinfo.reg = _fdt_reg_base_address(fdt, offs); 274 275 cuint = fdt_getprop(fdt, offs, "clocks", NULL); 276 if (cuint) { 277 cuint++; 278 dinfo.clock = (int)fdt32_to_cpu(*cuint); 279 } 280 281 cuint = fdt_getprop(fdt, offs, "resets", NULL); 282 if (cuint) { 283 cuint++; 284 dinfo.reset = (int)fdt32_to_cpu(*cuint); 285 } 286 287 dinfo.status = _fdt_get_status(fdt, offs); 288 289 *info = dinfo; 290 } 291