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