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/interrupt.h> 9 #include <kernel/linker.h> 10 #include <libfdt.h> 11 #include <mm/core_memprot.h> 12 #include <mm/core_mmu.h> 13 #include <string.h> 14 #include <trace.h> 15 16 const struct dt_driver *dt_find_compatible_driver(const void *fdt, int offs) 17 { 18 const struct dt_device_match *dm; 19 const struct dt_driver *drv; 20 21 for_each_dt_driver(drv) { 22 for (dm = drv->match_table; dm; dm++) { 23 if (!dm->compatible) { 24 break; 25 } 26 if (!fdt_node_check_compatible(fdt, offs, 27 dm->compatible)) { 28 return drv; 29 } 30 } 31 } 32 33 return NULL; 34 } 35 36 bool dt_have_prop(const void *fdt, int offs, const char *propname) 37 { 38 const void *prop; 39 40 prop = fdt_getprop(fdt, offs, propname, NULL); 41 42 return prop; 43 } 44 45 int dt_disable_status(void *fdt, int node) 46 { 47 const char *prop = NULL; 48 int len = 0; 49 50 prop = fdt_getprop(fdt, node, "status", &len); 51 if (!prop) { 52 if (fdt_setprop_string(fdt, node, "status", "disabled")) 53 return -1; 54 } else { 55 /* 56 * Status is there, modify it. 57 * Ask to set "disabled" value to the property. The value 58 * will be automatically truncated with "len" size by the 59 * fdt_setprop_inplace function. 60 * Setting a value different from "ok" or "okay" will disable 61 * the property. 62 * Setting a truncated value of "disabled" with the original 63 * property "len" is preferred to not increase the DT size and 64 * losing time in recalculating the overall DT offsets. 65 * If original length of the status property is larger than 66 * "disabled", the property will start with "disabled" and be 67 * completed with the rest of the original property. 68 */ 69 if (fdt_setprop_inplace(fdt, node, "status", "disabled", len)) 70 return -1; 71 } 72 73 return 0; 74 } 75 76 int dt_enable_secure_status(void *fdt, int node) 77 { 78 if (dt_disable_status(fdt, node)) { 79 EMSG("Unable to disable Normal Status"); 80 return -1; 81 } 82 83 if (fdt_setprop_string(fdt, node, "secure-status", "okay")) 84 return -1; 85 86 return 0; 87 } 88 89 int dt_map_dev(const void *fdt, int offs, vaddr_t *base, size_t *size, 90 enum dt_map_dev_directive mapping) 91 { 92 enum teecore_memtypes mtype; 93 paddr_t pbase; 94 vaddr_t vbase; 95 size_t sz; 96 int st; 97 98 assert(cpu_mmu_enabled()); 99 100 st = fdt_get_status(fdt, offs); 101 if (st == DT_STATUS_DISABLED) 102 return -1; 103 104 pbase = fdt_reg_base_address(fdt, offs); 105 if (pbase == DT_INFO_INVALID_REG) 106 return -1; 107 sz = fdt_reg_size(fdt, offs); 108 if (sz == DT_INFO_INVALID_REG_SIZE) 109 return -1; 110 111 switch (mapping) { 112 case DT_MAP_AUTO: 113 if ((st & DT_STATUS_OK_SEC) && !(st & DT_STATUS_OK_NSEC)) 114 mtype = MEM_AREA_IO_SEC; 115 else 116 mtype = MEM_AREA_IO_NSEC; 117 break; 118 case DT_MAP_SECURE: 119 mtype = MEM_AREA_IO_SEC; 120 break; 121 case DT_MAP_NON_SECURE: 122 mtype = MEM_AREA_IO_NSEC; 123 break; 124 default: 125 panic("Invalid mapping specified"); 126 break; 127 } 128 129 /* Check if we have a mapping, create one if needed */ 130 vbase = (vaddr_t)core_mmu_add_mapping(mtype, pbase, sz); 131 if (!vbase) { 132 EMSG("Failed to map %zu bytes at PA 0x%"PRIxPA, 133 (size_t)sz, pbase); 134 return -1; 135 } 136 137 *base = vbase; 138 *size = sz; 139 return 0; 140 } 141 142 /* Read a physical address (n=1 or 2 cells) */ 143 static paddr_t fdt_read_paddr(const uint32_t *cell, int n) 144 { 145 paddr_t addr; 146 147 if (n < 1 || n > 2) 148 goto bad; 149 150 addr = fdt32_to_cpu(*cell); 151 cell++; 152 if (n == 2) { 153 #ifdef ARM32 154 if (addr) { 155 /* High order 32 bits can't be nonzero */ 156 goto bad; 157 } 158 addr = fdt32_to_cpu(*cell); 159 #else 160 addr = (addr << 32) | fdt32_to_cpu(*cell); 161 #endif 162 } 163 164 return addr; 165 bad: 166 return DT_INFO_INVALID_REG; 167 168 } 169 170 paddr_t fdt_reg_base_address(const void *fdt, int offs) 171 { 172 const void *reg; 173 int ncells; 174 int len; 175 int parent; 176 177 parent = fdt_parent_offset(fdt, offs); 178 if (parent < 0) 179 return DT_INFO_INVALID_REG; 180 181 reg = fdt_getprop(fdt, offs, "reg", &len); 182 if (!reg) 183 return DT_INFO_INVALID_REG; 184 185 ncells = fdt_address_cells(fdt, parent); 186 if (ncells < 0) 187 return DT_INFO_INVALID_REG; 188 189 return fdt_read_paddr(reg, ncells); 190 } 191 192 static size_t fdt_read_size(const uint32_t *cell, int n) 193 { 194 uint32_t sz = 0; 195 196 sz = fdt32_to_cpu(*cell); 197 if (n == 2) { 198 if (sz) 199 return DT_INFO_INVALID_REG_SIZE; 200 201 cell++; 202 sz = fdt32_to_cpu(*cell); 203 } 204 205 return sz; 206 } 207 208 size_t fdt_reg_size(const void *fdt, int offs) 209 { 210 const uint32_t *reg; 211 int n; 212 int len; 213 int parent; 214 215 parent = fdt_parent_offset(fdt, offs); 216 if (parent < 0) 217 return DT_INFO_INVALID_REG_SIZE; 218 219 reg = (const uint32_t *)fdt_getprop(fdt, offs, "reg", &len); 220 if (!reg) 221 return DT_INFO_INVALID_REG_SIZE; 222 223 n = fdt_address_cells(fdt, parent); 224 if (n < 1 || n > 2) 225 return DT_INFO_INVALID_REG_SIZE; 226 227 reg += n; 228 229 n = fdt_size_cells(fdt, parent); 230 if (n < 1 || n > 2) 231 return DT_INFO_INVALID_REG_SIZE; 232 233 return fdt_read_size(reg, n); 234 } 235 236 static bool is_okay(const char *st, int len) 237 { 238 return !strncmp(st, "ok", len) || !strncmp(st, "okay", len); 239 } 240 241 int fdt_get_status(const void *fdt, int offs) 242 { 243 const char *prop; 244 int st = 0; 245 int len; 246 247 prop = fdt_getprop(fdt, offs, "status", &len); 248 if (!prop || is_okay(prop, len)) { 249 /* If status is not specified, it defaults to "okay" */ 250 st |= DT_STATUS_OK_NSEC; 251 } 252 253 prop = fdt_getprop(fdt, offs, "secure-status", &len); 254 if (!prop) { 255 /* 256 * When secure-status is not specified it defaults to the same 257 * value as status 258 */ 259 if (st & DT_STATUS_OK_NSEC) 260 st |= DT_STATUS_OK_SEC; 261 } else { 262 if (is_okay(prop, len)) 263 st |= DT_STATUS_OK_SEC; 264 } 265 266 return st; 267 } 268 269 void fdt_fill_device_info(const void *fdt, struct dt_node_info *info, int offs) 270 { 271 struct dt_node_info dinfo = { 272 .reg = DT_INFO_INVALID_REG, 273 .reg_size = DT_INFO_INVALID_REG_SIZE, 274 .clock = DT_INFO_INVALID_CLOCK, 275 .reset = DT_INFO_INVALID_RESET, 276 .interrupt = DT_INFO_INVALID_INTERRUPT, 277 }; 278 const fdt32_t *cuint; 279 280 dinfo.reg = fdt_reg_base_address(fdt, offs); 281 dinfo.reg_size = fdt_reg_size(fdt, offs); 282 283 cuint = fdt_getprop(fdt, offs, "clocks", NULL); 284 if (cuint) { 285 cuint++; 286 dinfo.clock = (int)fdt32_to_cpu(*cuint); 287 } 288 289 cuint = fdt_getprop(fdt, offs, "resets", NULL); 290 if (cuint) { 291 cuint++; 292 dinfo.reset = (int)fdt32_to_cpu(*cuint); 293 } 294 295 dinfo.interrupt = dt_get_irq_type_prio(fdt, offs, &dinfo.type, 296 &dinfo.prio); 297 298 dinfo.status = fdt_get_status(fdt, offs); 299 300 *info = dinfo; 301 } 302 303 int fdt_read_uint32_array(const void *fdt, int node, const char *prop_name, 304 uint32_t *array, size_t count) 305 { 306 const fdt32_t *cuint = NULL; 307 int len = 0; 308 uint32_t i = 0; 309 310 cuint = fdt_getprop(fdt, node, prop_name, &len); 311 if (!cuint) 312 return len; 313 314 if ((uint32_t)len != (count * sizeof(uint32_t))) 315 return -FDT_ERR_BADLAYOUT; 316 317 for (i = 0; i < ((uint32_t)len / sizeof(uint32_t)); i++) { 318 *array = fdt32_to_cpu(*cuint); 319 array++; 320 cuint++; 321 } 322 323 return 0; 324 } 325 326 int fdt_read_uint32_index(const void *fdt, int node, const char *prop_name, 327 int index, uint32_t *value) 328 { 329 const fdt32_t *cuint = NULL; 330 int len = 0; 331 332 cuint = fdt_getprop(fdt, node, prop_name, &len); 333 if (!cuint) 334 return len; 335 336 if ((uint32_t)len < (sizeof(uint32_t) * (index + 1))) 337 return -FDT_ERR_BADLAYOUT; 338 339 *value = fdt32_to_cpu(cuint[index]); 340 341 return 0; 342 } 343 344 int fdt_read_uint32(const void *fdt, int node, const char *prop_name, 345 uint32_t *value) 346 { 347 return fdt_read_uint32_array(fdt, node, prop_name, value, 1); 348 } 349 350 uint32_t fdt_read_uint32_default(const void *fdt, int node, 351 const char *prop_name, uint32_t dflt_value) 352 { 353 uint32_t ret = dflt_value; 354 355 fdt_read_uint32_index(fdt, node, prop_name, 0, &ret); 356 357 return ret; 358 } 359 360 int fdt_get_reg_props_by_index(const void *fdt, int node, int index, 361 paddr_t *base, size_t *size) 362 { 363 const fdt32_t *prop = NULL; 364 int parent = 0; 365 int len = 0; 366 int address_cells = 0; 367 int size_cells = 0; 368 int cell = 0; 369 370 parent = fdt_parent_offset(fdt, node); 371 if (parent < 0) 372 return parent; 373 374 address_cells = fdt_address_cells(fdt, parent); 375 if (address_cells < 0) 376 return address_cells; 377 378 size_cells = fdt_size_cells(fdt, parent); 379 if (size_cells < 0) 380 return size_cells; 381 382 cell = index * (address_cells + size_cells); 383 384 prop = fdt_getprop(fdt, node, "reg", &len); 385 if (!prop) 386 return len; 387 388 if (((cell + address_cells + size_cells) * (int)sizeof(uint32_t)) > len) 389 return -FDT_ERR_BADVALUE; 390 391 if (base) { 392 *base = fdt_read_paddr(&prop[cell], address_cells); 393 if (*base == DT_INFO_INVALID_REG) 394 return -FDT_ERR_BADVALUE; 395 } 396 397 if (size) { 398 *size = fdt_read_size(&prop[cell + address_cells], size_cells); 399 if (*size == DT_INFO_INVALID_REG_SIZE) 400 return -FDT_ERR_BADVALUE; 401 } 402 403 return 0; 404 } 405 406 int fdt_get_reg_props_by_name(const void *fdt, int node, const char *name, 407 paddr_t *base, size_t *size) 408 { 409 int index = 0; 410 411 index = fdt_stringlist_search(fdt, node, "reg-names", name); 412 if (index < 0) 413 return index; 414 415 return fdt_get_reg_props_by_index(fdt, node, index, base, size); 416 } 417