1 // SPDX-License-Identifier: BSD-2-Clause 2 /* 3 * Copyright (c) 2016, Linaro Limited 4 */ 5 6 #include <assert.h> 7 #include <config.h> 8 #include <initcall.h> 9 #include <kernel/dt.h> 10 #include <kernel/dt_driver.h> 11 #include <kernel/interrupt.h> 12 #include <kernel/linker.h> 13 #include <libfdt.h> 14 #include <mm/core_memprot.h> 15 #include <mm/core_mmu.h> 16 #include <stdio.h> 17 #include <string.h> 18 #include <trace.h> 19 20 static struct dt_descriptor external_dt __nex_bss; 21 22 const struct dt_driver *dt_find_compatible_driver(const void *fdt, int offs) 23 { 24 const struct dt_device_match *dm; 25 const struct dt_driver *drv; 26 27 for_each_dt_driver(drv) { 28 for (dm = drv->match_table; dm; dm++) { 29 if (!dm->compatible) { 30 break; 31 } 32 if (!fdt_node_check_compatible(fdt, offs, 33 dm->compatible)) { 34 return drv; 35 } 36 } 37 } 38 39 return NULL; 40 } 41 42 bool dt_have_prop(const void *fdt, int offs, const char *propname) 43 { 44 const void *prop; 45 46 prop = fdt_getprop(fdt, offs, propname, NULL); 47 48 return prop; 49 } 50 51 int dt_disable_status(void *fdt, int node) 52 { 53 const char *prop = NULL; 54 int len = 0; 55 56 prop = fdt_getprop(fdt, node, "status", &len); 57 if (!prop) { 58 if (fdt_setprop_string(fdt, node, "status", "disabled")) 59 return -1; 60 } else { 61 /* 62 * Status is there, modify it. 63 * Ask to set "disabled" value to the property. The value 64 * will be automatically truncated with "len" size by the 65 * fdt_setprop_inplace function. 66 * Setting a value different from "ok" or "okay" will disable 67 * the property. 68 * Setting a truncated value of "disabled" with the original 69 * property "len" is preferred to not increase the DT size and 70 * losing time in recalculating the overall DT offsets. 71 * If original length of the status property is larger than 72 * "disabled", the property will start with "disabled" and be 73 * completed with the rest of the original property. 74 */ 75 if (fdt_setprop_inplace(fdt, node, "status", "disabled", len)) 76 return -1; 77 } 78 79 return 0; 80 } 81 82 int dt_enable_secure_status(void *fdt, int node) 83 { 84 if (dt_disable_status(fdt, node)) { 85 EMSG("Unable to disable Normal Status"); 86 return -1; 87 } 88 89 if (fdt_setprop_string(fdt, node, "secure-status", "okay")) 90 return -1; 91 92 return 0; 93 } 94 95 int dt_map_dev(const void *fdt, int offs, vaddr_t *base, size_t *size, 96 enum dt_map_dev_directive mapping) 97 { 98 enum teecore_memtypes mtype; 99 paddr_t pbase; 100 vaddr_t vbase; 101 size_t sz; 102 int st; 103 104 assert(cpu_mmu_enabled()); 105 106 st = fdt_get_status(fdt, offs); 107 if (st == DT_STATUS_DISABLED) 108 return -1; 109 110 pbase = fdt_reg_base_address(fdt, offs); 111 if (pbase == DT_INFO_INVALID_REG) 112 return -1; 113 sz = fdt_reg_size(fdt, offs); 114 if (sz == DT_INFO_INVALID_REG_SIZE) 115 return -1; 116 117 switch (mapping) { 118 case DT_MAP_AUTO: 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 break; 124 case DT_MAP_SECURE: 125 mtype = MEM_AREA_IO_SEC; 126 break; 127 case DT_MAP_NON_SECURE: 128 mtype = MEM_AREA_IO_NSEC; 129 break; 130 default: 131 panic("Invalid mapping specified"); 132 break; 133 } 134 135 /* Check if we have a mapping, create one if needed */ 136 vbase = (vaddr_t)core_mmu_add_mapping(mtype, pbase, sz); 137 if (!vbase) { 138 EMSG("Failed to map %zu bytes at PA 0x%"PRIxPA, 139 (size_t)sz, pbase); 140 return -1; 141 } 142 143 *base = vbase; 144 *size = sz; 145 return 0; 146 } 147 148 /* Read a physical address (n=1 or 2 cells) */ 149 static paddr_t fdt_read_paddr(const uint32_t *cell, int n) 150 { 151 paddr_t addr; 152 153 if (n < 1 || n > 2) 154 goto bad; 155 156 addr = fdt32_to_cpu(*cell); 157 cell++; 158 if (n == 2) { 159 #ifdef ARM32 160 if (addr) { 161 /* High order 32 bits can't be nonzero */ 162 goto bad; 163 } 164 addr = fdt32_to_cpu(*cell); 165 #else 166 addr = (addr << 32) | fdt32_to_cpu(*cell); 167 #endif 168 } 169 170 return addr; 171 bad: 172 return DT_INFO_INVALID_REG; 173 174 } 175 176 paddr_t fdt_reg_base_address(const void *fdt, int offs) 177 { 178 const void *reg; 179 int ncells; 180 int len; 181 int parent; 182 183 parent = fdt_parent_offset(fdt, offs); 184 if (parent < 0) 185 return DT_INFO_INVALID_REG; 186 187 reg = fdt_getprop(fdt, offs, "reg", &len); 188 if (!reg) 189 return DT_INFO_INVALID_REG; 190 191 ncells = fdt_address_cells(fdt, parent); 192 if (ncells < 0) 193 return DT_INFO_INVALID_REG; 194 195 return fdt_read_paddr(reg, ncells); 196 } 197 198 static size_t fdt_read_size(const uint32_t *cell, int n) 199 { 200 uint32_t sz = 0; 201 202 sz = fdt32_to_cpu(*cell); 203 if (n == 2) { 204 if (sz) 205 return DT_INFO_INVALID_REG_SIZE; 206 207 cell++; 208 sz = fdt32_to_cpu(*cell); 209 } 210 211 return sz; 212 } 213 214 size_t fdt_reg_size(const void *fdt, int offs) 215 { 216 const uint32_t *reg; 217 int n; 218 int len; 219 int parent; 220 221 parent = fdt_parent_offset(fdt, offs); 222 if (parent < 0) 223 return DT_INFO_INVALID_REG_SIZE; 224 225 reg = (const uint32_t *)fdt_getprop(fdt, offs, "reg", &len); 226 if (!reg) 227 return DT_INFO_INVALID_REG_SIZE; 228 229 n = fdt_address_cells(fdt, parent); 230 if (n < 1 || n > 2) 231 return DT_INFO_INVALID_REG_SIZE; 232 233 reg += n; 234 235 n = fdt_size_cells(fdt, parent); 236 if (n < 1 || n > 2) 237 return DT_INFO_INVALID_REG_SIZE; 238 239 return fdt_read_size(reg, n); 240 } 241 242 static bool is_okay(const char *st, int len) 243 { 244 return !strncmp(st, "ok", len) || !strncmp(st, "okay", len); 245 } 246 247 int fdt_get_status(const void *fdt, int offs) 248 { 249 const char *prop; 250 int st = 0; 251 int len; 252 253 prop = fdt_getprop(fdt, offs, "status", &len); 254 if (!prop || is_okay(prop, len)) { 255 /* If status is not specified, it defaults to "okay" */ 256 st |= DT_STATUS_OK_NSEC; 257 } 258 259 prop = fdt_getprop(fdt, offs, "secure-status", &len); 260 if (!prop) { 261 /* 262 * When secure-status is not specified it defaults to the same 263 * value as status 264 */ 265 if (st & DT_STATUS_OK_NSEC) 266 st |= DT_STATUS_OK_SEC; 267 } else { 268 if (is_okay(prop, len)) 269 st |= DT_STATUS_OK_SEC; 270 } 271 272 return st; 273 } 274 275 void fdt_fill_device_info(const void *fdt, struct dt_node_info *info, int offs) 276 { 277 struct dt_node_info dinfo = { 278 .reg = DT_INFO_INVALID_REG, 279 .reg_size = DT_INFO_INVALID_REG_SIZE, 280 .clock = DT_INFO_INVALID_CLOCK, 281 .reset = DT_INFO_INVALID_RESET, 282 .interrupt = DT_INFO_INVALID_INTERRUPT, 283 }; 284 const fdt32_t *cuint; 285 286 dinfo.reg = fdt_reg_base_address(fdt, offs); 287 dinfo.reg_size = fdt_reg_size(fdt, offs); 288 289 cuint = fdt_getprop(fdt, offs, "clocks", NULL); 290 if (cuint) { 291 cuint++; 292 dinfo.clock = (int)fdt32_to_cpu(*cuint); 293 } 294 295 cuint = fdt_getprop(fdt, offs, "resets", NULL); 296 if (cuint) { 297 cuint++; 298 dinfo.reset = (int)fdt32_to_cpu(*cuint); 299 } 300 301 dinfo.interrupt = dt_get_irq_type_prio(fdt, offs, &dinfo.type, 302 &dinfo.prio); 303 304 dinfo.status = fdt_get_status(fdt, offs); 305 306 *info = dinfo; 307 } 308 309 int fdt_read_uint32_array(const void *fdt, int node, const char *prop_name, 310 uint32_t *array, size_t count) 311 { 312 const fdt32_t *cuint = NULL; 313 int len = 0; 314 uint32_t i = 0; 315 316 cuint = fdt_getprop(fdt, node, prop_name, &len); 317 if (!cuint) 318 return len; 319 320 if ((uint32_t)len != (count * sizeof(uint32_t))) 321 return -FDT_ERR_BADLAYOUT; 322 323 for (i = 0; i < ((uint32_t)len / sizeof(uint32_t)); i++) { 324 *array = fdt32_to_cpu(*cuint); 325 array++; 326 cuint++; 327 } 328 329 return 0; 330 } 331 332 int fdt_read_uint32_index(const void *fdt, int node, const char *prop_name, 333 int index, uint32_t *value) 334 { 335 const fdt32_t *cuint = NULL; 336 int len = 0; 337 338 cuint = fdt_getprop(fdt, node, prop_name, &len); 339 if (!cuint) 340 return len; 341 342 if ((uint32_t)len < (sizeof(uint32_t) * (index + 1))) 343 return -FDT_ERR_BADLAYOUT; 344 345 *value = fdt32_to_cpu(cuint[index]); 346 347 return 0; 348 } 349 350 int fdt_read_uint32(const void *fdt, int node, const char *prop_name, 351 uint32_t *value) 352 { 353 return fdt_read_uint32_array(fdt, node, prop_name, value, 1); 354 } 355 356 uint32_t fdt_read_uint32_default(const void *fdt, int node, 357 const char *prop_name, uint32_t dflt_value) 358 { 359 uint32_t ret = dflt_value; 360 361 fdt_read_uint32_index(fdt, node, prop_name, 0, &ret); 362 363 return ret; 364 } 365 366 int fdt_get_reg_props_by_index(const void *fdt, int node, int index, 367 paddr_t *base, size_t *size) 368 { 369 const fdt32_t *prop = NULL; 370 int parent = 0; 371 int len = 0; 372 int address_cells = 0; 373 int size_cells = 0; 374 int cell = 0; 375 376 parent = fdt_parent_offset(fdt, node); 377 if (parent < 0) 378 return parent; 379 380 address_cells = fdt_address_cells(fdt, parent); 381 if (address_cells < 0) 382 return address_cells; 383 384 size_cells = fdt_size_cells(fdt, parent); 385 if (size_cells < 0) 386 return size_cells; 387 388 cell = index * (address_cells + size_cells); 389 390 prop = fdt_getprop(fdt, node, "reg", &len); 391 if (!prop) 392 return len; 393 394 if (((cell + address_cells + size_cells) * (int)sizeof(uint32_t)) > len) 395 return -FDT_ERR_BADVALUE; 396 397 if (base) { 398 *base = fdt_read_paddr(&prop[cell], address_cells); 399 if (*base == DT_INFO_INVALID_REG) 400 return -FDT_ERR_BADVALUE; 401 } 402 403 if (size) { 404 *size = fdt_read_size(&prop[cell + address_cells], size_cells); 405 if (*size == DT_INFO_INVALID_REG_SIZE) 406 return -FDT_ERR_BADVALUE; 407 } 408 409 return 0; 410 } 411 412 int fdt_get_reg_props_by_name(const void *fdt, int node, const char *name, 413 paddr_t *base, size_t *size) 414 { 415 int index = 0; 416 417 index = fdt_stringlist_search(fdt, node, "reg-names", name); 418 if (index < 0) 419 return index; 420 421 return fdt_get_reg_props_by_index(fdt, node, index, base, size); 422 } 423 424 int dt_getprop_as_number(const void *fdt, int nodeoffset, const char *name, 425 uint64_t *num) 426 { 427 const void *prop = NULL; 428 int len = 0; 429 430 prop = fdt_getprop(fdt, nodeoffset, name, &len); 431 if (!prop) 432 return len; 433 434 switch (len) { 435 case sizeof(uint32_t): 436 *num = fdt32_ld(prop); 437 return 0; 438 case sizeof(uint64_t): 439 *num = fdt64_ld(prop); 440 return 0; 441 default: 442 return -FDT_ERR_BADVALUE; 443 } 444 } 445 446 void *get_dt(void) 447 { 448 void *fdt = get_embedded_dt(); 449 450 if (!fdt) 451 fdt = get_external_dt(); 452 453 return fdt; 454 } 455 456 void *get_secure_dt(void) 457 { 458 void *fdt = get_embedded_dt(); 459 460 if (!fdt && IS_ENABLED(CFG_MAP_EXT_DT_SECURE)) 461 fdt = get_external_dt(); 462 463 return fdt; 464 } 465 466 #if defined(CFG_EMBED_DTB) 467 void *get_embedded_dt(void) 468 { 469 static bool checked; 470 471 assert(cpu_mmu_enabled()); 472 473 if (!checked) { 474 IMSG("Embedded DTB found"); 475 476 if (fdt_check_header(embedded_secure_dtb)) 477 panic("Invalid embedded DTB"); 478 479 checked = true; 480 } 481 482 return embedded_secure_dtb; 483 } 484 #else 485 void *get_embedded_dt(void) 486 { 487 return NULL; 488 } 489 #endif /*CFG_EMBED_DTB*/ 490 491 #ifdef _CFG_USE_DTB_OVERLAY 492 static int add_dt_overlay_fragment(struct dt_descriptor *dt, int ioffs) 493 { 494 char frag[32] = { }; 495 int offs = 0; 496 int ret = 0; 497 498 snprintf(frag, sizeof(frag), "fragment@%d", dt->frag_id); 499 offs = fdt_add_subnode(dt->blob, ioffs, frag); 500 if (offs < 0) 501 return offs; 502 503 dt->frag_id += 1; 504 505 ret = fdt_setprop_string(dt->blob, offs, "target-path", "/"); 506 if (ret < 0) 507 return ret; 508 509 return fdt_add_subnode(dt->blob, offs, "__overlay__"); 510 } 511 512 static int init_dt_overlay(struct dt_descriptor *dt, int __maybe_unused dt_size) 513 { 514 int fragment = 0; 515 516 if (IS_ENABLED(CFG_EXTERNAL_DTB_OVERLAY)) { 517 if (!fdt_check_header(dt->blob)) { 518 fdt_for_each_subnode(fragment, dt->blob, 0) 519 dt->frag_id += 1; 520 return 0; 521 } 522 } 523 524 return fdt_create_empty_tree(dt->blob, dt_size); 525 } 526 #else 527 static int add_dt_overlay_fragment(struct dt_descriptor *dt __unused, int offs) 528 { 529 return offs; 530 } 531 532 static int init_dt_overlay(struct dt_descriptor *dt __unused, 533 int dt_size __unused) 534 { 535 return 0; 536 } 537 #endif /* _CFG_USE_DTB_OVERLAY */ 538 539 struct dt_descriptor *get_external_dt_desc(void) 540 { 541 if (!IS_ENABLED(CFG_EXTERNAL_DT)) 542 return NULL; 543 544 return &external_dt; 545 } 546 547 void init_external_dt(unsigned long phys_dt) 548 { 549 struct dt_descriptor *dt = &external_dt; 550 void *fdt = NULL; 551 int ret = 0; 552 553 if (!IS_ENABLED(CFG_EXTERNAL_DT)) 554 return; 555 556 if (!phys_dt) { 557 /* 558 * No need to panic as we're not using the DT in OP-TEE 559 * yet, we're only adding some nodes for normal world use. 560 * This makes the switch to using DT easier as we can boot 561 * a newer OP-TEE with older boot loaders. Once we start to 562 * initialize devices based on DT we'll likely panic 563 * instead of returning here. 564 */ 565 IMSG("No non-secure external DT"); 566 return; 567 } 568 569 fdt = core_mmu_add_mapping(MEM_AREA_EXT_DT, phys_dt, CFG_DTB_MAX_SIZE); 570 if (!fdt) 571 panic("Failed to map external DTB"); 572 573 dt->blob = fdt; 574 575 ret = init_dt_overlay(dt, CFG_DTB_MAX_SIZE); 576 if (ret < 0) { 577 EMSG("Device Tree Overlay init fail @ %#lx: error %d", phys_dt, 578 ret); 579 panic(); 580 } 581 582 ret = fdt_open_into(fdt, fdt, CFG_DTB_MAX_SIZE); 583 if (ret < 0) { 584 EMSG("Invalid Device Tree at %#lx: error %d", phys_dt, ret); 585 panic(); 586 } 587 588 IMSG("Non-secure external DT found"); 589 } 590 591 void *get_external_dt(void) 592 { 593 if (!IS_ENABLED(CFG_EXTERNAL_DT)) 594 return NULL; 595 596 assert(cpu_mmu_enabled()); 597 return external_dt.blob; 598 } 599 600 static TEE_Result release_external_dt(void) 601 { 602 int ret = 0; 603 604 if (!IS_ENABLED(CFG_EXTERNAL_DT)) 605 return TEE_SUCCESS; 606 607 if (!external_dt.blob) 608 return TEE_SUCCESS; 609 610 ret = fdt_pack(external_dt.blob); 611 if (ret < 0) { 612 EMSG("Failed to pack Device Tree at 0x%" PRIxPA ": error %d", 613 virt_to_phys(external_dt.blob), ret); 614 panic(); 615 } 616 617 if (core_mmu_remove_mapping(MEM_AREA_EXT_DT, external_dt.blob, 618 CFG_DTB_MAX_SIZE)) 619 panic("Failed to remove temporary Device Tree mapping"); 620 621 /* External DTB no more reached, reset pointer to invalid */ 622 external_dt.blob = NULL; 623 624 return TEE_SUCCESS; 625 } 626 627 boot_final(release_external_dt); 628 629 int add_dt_path_subnode(struct dt_descriptor *dt, const char *path, 630 const char *subnode) 631 { 632 int offs = 0; 633 634 offs = fdt_path_offset(dt->blob, path); 635 if (offs < 0) 636 return offs; 637 offs = add_dt_overlay_fragment(dt, offs); 638 if (offs < 0) 639 return offs; 640 return fdt_add_subnode(dt->blob, offs, subnode); 641 } 642 643 static void set_dt_val(void *data, uint32_t cell_size, uint64_t val) 644 { 645 if (cell_size == 1) { 646 fdt32_t v = cpu_to_fdt32((uint32_t)val); 647 648 memcpy(data, &v, sizeof(v)); 649 } else { 650 fdt64_t v = cpu_to_fdt64(val); 651 652 memcpy(data, &v, sizeof(v)); 653 } 654 } 655 656 int add_res_mem_dt_node(struct dt_descriptor *dt, const char *name, 657 paddr_t pa, size_t size) 658 { 659 int offs = 0; 660 int ret = 0; 661 int addr_size = -1; 662 int len_size = -1; 663 bool found = true; 664 char subnode_name[80] = { }; 665 666 offs = fdt_path_offset(dt->blob, "/reserved-memory"); 667 668 if (offs < 0) { 669 found = false; 670 offs = 0; 671 } 672 673 if (IS_ENABLED2(_CFG_USE_DTB_OVERLAY)) { 674 len_size = sizeof(paddr_t) / sizeof(uint32_t); 675 addr_size = sizeof(paddr_t) / sizeof(uint32_t); 676 } else { 677 len_size = fdt_size_cells(dt->blob, offs); 678 if (len_size < 0) 679 return len_size; 680 addr_size = fdt_address_cells(dt->blob, offs); 681 if (addr_size < 0) 682 return addr_size; 683 } 684 685 if (!found) { 686 offs = add_dt_path_subnode(dt, "/", "reserved-memory"); 687 if (offs < 0) 688 return offs; 689 ret = fdt_setprop_cell(dt->blob, offs, "#address-cells", 690 addr_size); 691 if (ret < 0) 692 return ret; 693 ret = fdt_setprop_cell(dt->blob, offs, "#size-cells", len_size); 694 if (ret < 0) 695 return ret; 696 ret = fdt_setprop(dt->blob, offs, "ranges", NULL, 0); 697 if (ret < 0) 698 return ret; 699 } 700 701 ret = snprintf(subnode_name, sizeof(subnode_name), 702 "%s@%" PRIxPA, name, pa); 703 if (ret < 0 || ret >= (int)sizeof(subnode_name)) 704 DMSG("truncated node \"%s@%" PRIxPA"\"", name, pa); 705 offs = fdt_add_subnode(dt->blob, offs, subnode_name); 706 if (offs >= 0) { 707 uint32_t data[FDT_MAX_NCELLS * 2] = { }; 708 709 set_dt_val(data, addr_size, pa); 710 set_dt_val(data + addr_size, len_size, size); 711 ret = fdt_setprop(dt->blob, offs, "reg", data, 712 sizeof(uint32_t) * (addr_size + len_size)); 713 if (ret < 0) 714 return ret; 715 ret = fdt_setprop(dt->blob, offs, "no-map", NULL, 0); 716 if (ret < 0) 717 return ret; 718 } else { 719 return offs; 720 } 721 return 0; 722 } 723