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 snprintf(frag, sizeof(frag), "fragment@%d", dt->frag_id); 498 offs = fdt_add_subnode(dt->blob, ioffs, frag); 499 if (offs < 0) 500 return offs; 501 502 dt->frag_id += 1; 503 504 ret = fdt_setprop_string(dt->blob, offs, "target-path", "/"); 505 if (ret < 0) 506 return ret; 507 508 return fdt_add_subnode(dt->blob, offs, "__overlay__"); 509 } 510 511 static int init_dt_overlay(struct dt_descriptor *dt, int __maybe_unused dt_size) 512 { 513 int fragment = 0; 514 515 if (IS_ENABLED(CFG_EXTERNAL_DTB_OVERLAY)) { 516 if (!fdt_check_header(dt->blob)) { 517 fdt_for_each_subnode(fragment, dt->blob, 0) 518 dt->frag_id += 1; 519 return 0; 520 } 521 } 522 523 return fdt_create_empty_tree(dt->blob, dt_size); 524 } 525 #else 526 static int add_dt_overlay_fragment(struct dt_descriptor *dt __unused, int offs) 527 { 528 return offs; 529 } 530 531 static int init_dt_overlay(struct dt_descriptor *dt __unused, 532 int dt_size __unused) 533 { 534 return 0; 535 } 536 #endif /* _CFG_USE_DTB_OVERLAY */ 537 538 struct dt_descriptor *get_external_dt_desc(void) 539 { 540 if (!IS_ENABLED(CFG_EXTERNAL_DT)) 541 return NULL; 542 543 return &external_dt; 544 } 545 546 void init_external_dt(unsigned long phys_dt, size_t dt_sz) 547 { 548 struct dt_descriptor *dt = &external_dt; 549 int ret = 0; 550 enum teecore_memtypes mtype = MEM_AREA_MAXTYPE; 551 552 if (!IS_ENABLED(CFG_EXTERNAL_DT)) 553 return; 554 555 if (!phys_dt || !dt_sz) { 556 /* 557 * No need to panic as we're not using the DT in OP-TEE 558 * yet, we're only adding some nodes for normal world use. 559 * This makes the switch to using DT easier as we can boot 560 * a newer OP-TEE with older boot loaders. Once we start to 561 * initialize devices based on DT we'll likely panic 562 * instead of returning here. 563 */ 564 IMSG("No non-secure external DT"); 565 return; 566 } 567 568 mtype = core_mmu_get_type_by_pa(phys_dt); 569 if (mtype == MEM_AREA_MAXTYPE) { 570 /* Map the DTB if it is not yet mapped */ 571 dt->blob = core_mmu_add_mapping(MEM_AREA_EXT_DT, phys_dt, 572 dt_sz); 573 if (!dt->blob) 574 panic("Failed to map external DTB"); 575 } else { 576 /* Get the DTB address if already mapped in a memory area */ 577 dt->blob = phys_to_virt(phys_dt, mtype, dt_sz); 578 if (!dt->blob) { 579 EMSG("Failed to get a mapped external DTB for PA %#lx", 580 phys_dt); 581 panic(); 582 } 583 } 584 585 ret = init_dt_overlay(dt, dt_sz); 586 if (ret < 0) { 587 EMSG("Device Tree Overlay init fail @ %#lx: error %d", phys_dt, 588 ret); 589 panic(); 590 } 591 592 ret = fdt_open_into(dt->blob, dt->blob, dt_sz); 593 if (ret < 0) { 594 EMSG("Invalid Device Tree at %#lx: error %d", phys_dt, ret); 595 panic(); 596 } 597 598 IMSG("Non-secure external DT found"); 599 } 600 601 void *get_external_dt(void) 602 { 603 if (!IS_ENABLED(CFG_EXTERNAL_DT)) 604 return NULL; 605 606 assert(cpu_mmu_enabled()); 607 return external_dt.blob; 608 } 609 610 static TEE_Result release_external_dt(void) 611 { 612 int ret = 0; 613 paddr_t pa_dt = 0; 614 615 if (!IS_ENABLED(CFG_EXTERNAL_DT)) 616 return TEE_SUCCESS; 617 618 if (!external_dt.blob) 619 return TEE_SUCCESS; 620 621 pa_dt = virt_to_phys(external_dt.blob); 622 /* 623 * Skip packing and un-mapping operations if the external DTB is mapped 624 * in a different memory area 625 */ 626 if (core_mmu_get_type_by_pa(pa_dt) != MEM_AREA_EXT_DT) 627 return TEE_SUCCESS; 628 629 ret = fdt_pack(external_dt.blob); 630 if (ret < 0) { 631 EMSG("Failed to pack Device Tree at 0x%" PRIxPA ": error %d", 632 virt_to_phys(external_dt.blob), ret); 633 panic(); 634 } 635 636 if (core_mmu_remove_mapping(MEM_AREA_EXT_DT, external_dt.blob, 637 CFG_DTB_MAX_SIZE)) 638 panic("Failed to remove temporary Device Tree mapping"); 639 640 /* External DTB no more reached, reset pointer to invalid */ 641 external_dt.blob = NULL; 642 643 return TEE_SUCCESS; 644 } 645 646 boot_final(release_external_dt); 647 648 int add_dt_path_subnode(struct dt_descriptor *dt, const char *path, 649 const char *subnode) 650 { 651 int offs = 0; 652 653 offs = fdt_path_offset(dt->blob, path); 654 if (offs < 0) 655 return offs; 656 offs = add_dt_overlay_fragment(dt, offs); 657 if (offs < 0) 658 return offs; 659 return fdt_add_subnode(dt->blob, offs, subnode); 660 } 661 662 static void set_dt_val(void *data, uint32_t cell_size, uint64_t val) 663 { 664 if (cell_size == 1) { 665 fdt32_t v = cpu_to_fdt32((uint32_t)val); 666 667 memcpy(data, &v, sizeof(v)); 668 } else { 669 fdt64_t v = cpu_to_fdt64(val); 670 671 memcpy(data, &v, sizeof(v)); 672 } 673 } 674 675 int add_res_mem_dt_node(struct dt_descriptor *dt, const char *name, 676 paddr_t pa, size_t size) 677 { 678 int offs = 0; 679 int ret = 0; 680 int addr_size = -1; 681 int len_size = -1; 682 bool found = true; 683 char subnode_name[80] = { }; 684 685 offs = fdt_path_offset(dt->blob, "/reserved-memory"); 686 687 if (offs < 0) { 688 found = false; 689 offs = 0; 690 } 691 692 if (IS_ENABLED2(_CFG_USE_DTB_OVERLAY)) { 693 len_size = sizeof(paddr_t) / sizeof(uint32_t); 694 addr_size = sizeof(paddr_t) / sizeof(uint32_t); 695 } else { 696 len_size = fdt_size_cells(dt->blob, offs); 697 if (len_size < 0) 698 return len_size; 699 addr_size = fdt_address_cells(dt->blob, offs); 700 if (addr_size < 0) 701 return addr_size; 702 } 703 704 if (!found) { 705 offs = add_dt_path_subnode(dt, "/", "reserved-memory"); 706 if (offs < 0) 707 return offs; 708 ret = fdt_setprop_cell(dt->blob, offs, "#address-cells", 709 addr_size); 710 if (ret < 0) 711 return ret; 712 ret = fdt_setprop_cell(dt->blob, offs, "#size-cells", len_size); 713 if (ret < 0) 714 return ret; 715 ret = fdt_setprop(dt->blob, offs, "ranges", NULL, 0); 716 if (ret < 0) 717 return ret; 718 } 719 720 ret = snprintf(subnode_name, sizeof(subnode_name), 721 "%s@%" PRIxPA, name, pa); 722 if (ret < 0 || ret >= (int)sizeof(subnode_name)) 723 DMSG("truncated node \"%s@%" PRIxPA"\"", name, pa); 724 offs = fdt_add_subnode(dt->blob, offs, subnode_name); 725 if (offs >= 0) { 726 uint32_t data[FDT_MAX_NCELLS * 2] = { }; 727 728 set_dt_val(data, addr_size, pa); 729 set_dt_val(data + addr_size, len_size, size); 730 ret = fdt_setprop(dt->blob, offs, "reg", data, 731 sizeof(uint32_t) * (addr_size + len_size)); 732 if (ret < 0) 733 return ret; 734 ret = fdt_setprop(dt->blob, offs, "no-map", NULL, 0); 735 if (ret < 0) 736 return ret; 737 } else { 738 return offs; 739 } 740 return 0; 741 } 742