1 /* 2 * Copyright (c) 2018-2021, ARM Limited and Contributors. All rights reserved. 3 * 4 * SPDX-License-Identifier: BSD-3-Clause 5 */ 6 7 /* Helper functions to offer easier navigation of Device Tree Blob */ 8 9 #include <assert.h> 10 #include <errno.h> 11 #include <string.h> 12 13 #include <libfdt.h> 14 15 #include <common/debug.h> 16 #include <common/fdt_wrappers.h> 17 #include <common/uuid.h> 18 19 /* 20 * Read cells from a given property of the given node. Any number of 32-bit 21 * cells of the property can be read. Returns 0 on success, or a negative 22 * FDT error value otherwise. 23 */ 24 int fdt_read_uint32_array(const void *dtb, int node, const char *prop_name, 25 unsigned int cells, uint32_t *value) 26 { 27 const fdt32_t *prop; 28 int value_len; 29 30 assert(dtb != NULL); 31 assert(prop_name != NULL); 32 assert(value != NULL); 33 assert(node >= 0); 34 35 /* Access property and obtain its length (in bytes) */ 36 prop = fdt_getprop(dtb, node, prop_name, &value_len); 37 if (prop == NULL) { 38 WARN("Couldn't find property %s in dtb\n", prop_name); 39 return -FDT_ERR_NOTFOUND; 40 } 41 42 /* Verify that property length can fill the entire array. */ 43 if (NCELLS((unsigned int)value_len) < cells) { 44 WARN("Property length mismatch\n"); 45 return -FDT_ERR_BADVALUE; 46 } 47 48 for (unsigned int i = 0U; i < cells; i++) { 49 value[i] = fdt32_to_cpu(prop[i]); 50 } 51 52 return 0; 53 } 54 55 int fdt_read_uint32(const void *dtb, int node, const char *prop_name, 56 uint32_t *value) 57 { 58 return fdt_read_uint32_array(dtb, node, prop_name, 1, value); 59 } 60 61 uint32_t fdt_read_uint32_default(const void *dtb, int node, 62 const char *prop_name, uint32_t dflt_value) 63 { 64 uint32_t ret = dflt_value; 65 int err = fdt_read_uint32(dtb, node, prop_name, &ret); 66 67 if (err < 0) { 68 return dflt_value; 69 } 70 71 return ret; 72 } 73 74 int fdt_read_uint64(const void *dtb, int node, const char *prop_name, 75 uint64_t *value) 76 { 77 uint32_t array[2] = {0, 0}; 78 int ret; 79 80 ret = fdt_read_uint32_array(dtb, node, prop_name, 2, array); 81 if (ret < 0) { 82 return ret; 83 } 84 85 *value = ((uint64_t)array[0] << 32) | array[1]; 86 return 0; 87 } 88 89 /* 90 * Read bytes from a given property of the given node. Any number of 91 * bytes of the property can be read. The fdt pointer is updated. 92 * Returns 0 on success, and -1 on error. 93 */ 94 int fdtw_read_bytes(const void *dtb, int node, const char *prop, 95 unsigned int length, void *value) 96 { 97 const void *ptr; 98 int value_len; 99 100 assert(dtb != NULL); 101 assert(prop != NULL); 102 assert(value != NULL); 103 assert(node >= 0); 104 105 /* Access property and obtain its length (in bytes) */ 106 ptr = fdt_getprop_namelen(dtb, node, prop, (int)strlen(prop), 107 &value_len); 108 if (ptr == NULL) { 109 WARN("Couldn't find property %s in dtb\n", prop); 110 return -1; 111 } 112 113 /* Verify that property length is not less than number of bytes */ 114 if ((unsigned int)value_len < length) { 115 WARN("Property length mismatch\n"); 116 return -1; 117 } 118 119 (void)memcpy(value, ptr, length); 120 121 return 0; 122 } 123 124 /* 125 * Read string from a given property of the given node. Up to 'size - 1' 126 * characters are read, and a NUL terminator is added. Returns 0 on success, 127 * and -1 upon error. 128 */ 129 int fdtw_read_string(const void *dtb, int node, const char *prop, 130 char *str, size_t size) 131 { 132 const char *ptr; 133 size_t len; 134 135 assert(dtb != NULL); 136 assert(node >= 0); 137 assert(prop != NULL); 138 assert(str != NULL); 139 assert(size > 0U); 140 141 ptr = fdt_getprop_namelen(dtb, node, prop, (int)strlen(prop), NULL); 142 if (ptr == NULL) { 143 WARN("Couldn't find property %s in dtb\n", prop); 144 return -1; 145 } 146 147 len = strlcpy(str, ptr, size); 148 if (len >= size) { 149 WARN("String of property %s in dtb has been truncated\n", prop); 150 return -1; 151 } 152 153 return 0; 154 } 155 156 /* 157 * Read UUID from a given property of the given node. Returns 0 on success, 158 * and a negative value upon error. 159 */ 160 int fdtw_read_uuid(const void *dtb, int node, const char *prop, 161 unsigned int length, uint8_t *uuid) 162 { 163 /* Buffer for UUID string (plus NUL terminator) */ 164 char uuid_string[UUID_STRING_LENGTH + 1U]; 165 int err; 166 167 assert(dtb != NULL); 168 assert(prop != NULL); 169 assert(uuid != NULL); 170 assert(node >= 0); 171 172 if (length < UUID_BYTES_LENGTH) { 173 return -EINVAL; 174 } 175 176 err = fdtw_read_string(dtb, node, prop, uuid_string, 177 UUID_STRING_LENGTH + 1U); 178 if (err != 0) { 179 return err; 180 } 181 182 if (read_uuid(uuid, uuid_string) != 0) { 183 return -FDT_ERR_BADVALUE; 184 } 185 186 return 0; 187 } 188 189 /* 190 * Write cells in place to a given property of the given node. At most 2 cells 191 * of the property are written. Returns 0 on success, and -1 upon error. 192 */ 193 int fdtw_write_inplace_cells(void *dtb, int node, const char *prop, 194 unsigned int cells, void *value) 195 { 196 int err, len; 197 198 assert(dtb != NULL); 199 assert(prop != NULL); 200 assert(value != NULL); 201 assert(node >= 0); 202 203 /* We expect either 1 or 2 cell property */ 204 assert(cells <= 2U); 205 206 if (cells == 2U) 207 *(uint64_t *)value = cpu_to_fdt64(*(uint64_t *)value); 208 else 209 *(uint32_t *)value = cpu_to_fdt32(*(uint32_t *)value); 210 211 len = (int)cells * 4; 212 213 /* Set property value in place */ 214 err = fdt_setprop_inplace(dtb, node, prop, value, len); 215 if (err != 0) { 216 WARN("Modify property %s failed with error %d\n", prop, err); 217 return -1; 218 } 219 220 return 0; 221 } 222 223 /* 224 * Write bytes in place to a given property of the given node. 225 * Any number of bytes of the property can be written. 226 * Returns 0 on success, and < 0 on error. 227 */ 228 int fdtw_write_inplace_bytes(void *dtb, int node, const char *prop, 229 unsigned int length, const void *data) 230 { 231 const void *ptr; 232 int namelen, value_len, err; 233 234 assert(dtb != NULL); 235 assert(prop != NULL); 236 assert(data != NULL); 237 assert(node >= 0); 238 239 namelen = (int)strlen(prop); 240 241 /* Access property and obtain its length in bytes */ 242 ptr = fdt_getprop_namelen(dtb, node, prop, namelen, &value_len); 243 if (ptr == NULL) { 244 WARN("Couldn't find property %s in dtb\n", prop); 245 return -1; 246 } 247 248 /* Verify that property length is not less than number of bytes */ 249 if ((unsigned int)value_len < length) { 250 WARN("Property length mismatch\n"); 251 return -1; 252 } 253 254 /* Set property value in place */ 255 err = fdt_setprop_inplace_namelen_partial(dtb, node, prop, 256 namelen, 0, 257 data, (int)length); 258 if (err != 0) { 259 WARN("Set property %s failed with error %d\n", prop, err); 260 } 261 262 return err; 263 } 264 265 static uint64_t fdt_read_prop_cells(const fdt32_t *prop, int nr_cells) 266 { 267 uint64_t reg = fdt32_to_cpu(prop[0]); 268 269 if (nr_cells > 1) { 270 reg = (reg << 32) | fdt32_to_cpu(prop[1]); 271 } 272 273 return reg; 274 } 275 276 int fdt_get_reg_props_by_index(const void *dtb, int node, int index, 277 uintptr_t *base, size_t *size) 278 { 279 const fdt32_t *prop; 280 int parent, len; 281 int ac, sc; 282 int cell; 283 284 parent = fdt_parent_offset(dtb, node); 285 if (parent < 0) { 286 return -FDT_ERR_BADOFFSET; 287 } 288 289 ac = fdt_address_cells(dtb, parent); 290 sc = fdt_size_cells(dtb, parent); 291 292 cell = index * (ac + sc); 293 294 prop = fdt_getprop(dtb, node, "reg", &len); 295 if (prop == NULL) { 296 WARN("Couldn't find \"reg\" property in dtb\n"); 297 return -FDT_ERR_NOTFOUND; 298 } 299 300 if (((cell + ac + sc) * (int)sizeof(uint32_t)) > len) { 301 return -FDT_ERR_BADVALUE; 302 } 303 304 if (base != NULL) { 305 *base = (uintptr_t)fdt_read_prop_cells(&prop[cell], ac); 306 } 307 308 if (size != NULL) { 309 *size = (size_t)fdt_read_prop_cells(&prop[cell + ac], sc); 310 } 311 312 return 0; 313 } 314 315 /******************************************************************************* 316 * This function fills reg node info (base & size) with an index found by 317 * checking the reg-names node. 318 * Returns 0 on success and a negative FDT error code on failure. 319 ******************************************************************************/ 320 int fdt_get_reg_props_by_name(const void *dtb, int node, const char *name, 321 uintptr_t *base, size_t *size) 322 { 323 int index; 324 325 index = fdt_stringlist_search(dtb, node, "reg-names", name); 326 if (index < 0) { 327 return index; 328 } 329 330 return fdt_get_reg_props_by_index(dtb, node, index, base, size); 331 } 332 333 /******************************************************************************* 334 * This function gets the stdout path node. 335 * It reads the value indicated inside the device tree. 336 * Returns node offset on success and a negative FDT error code on failure. 337 ******************************************************************************/ 338 int fdt_get_stdout_node_offset(const void *dtb) 339 { 340 int node; 341 const char *prop, *path; 342 int len; 343 344 /* The /secure-chosen node takes precedence over the standard one. */ 345 node = fdt_path_offset(dtb, "/secure-chosen"); 346 if (node < 0) { 347 node = fdt_path_offset(dtb, "/chosen"); 348 if (node < 0) { 349 return -FDT_ERR_NOTFOUND; 350 } 351 } 352 353 prop = fdt_getprop(dtb, node, "stdout-path", NULL); 354 if (prop == NULL) { 355 return -FDT_ERR_NOTFOUND; 356 } 357 358 /* Determine the actual path length, as a colon terminates the path. */ 359 path = strchr(prop, ':'); 360 if (path == NULL) { 361 len = strlen(prop); 362 } else { 363 len = path - prop; 364 } 365 366 /* Aliases cannot start with a '/', so it must be the actual path. */ 367 if (prop[0] == '/') { 368 return fdt_path_offset_namelen(dtb, prop, len); 369 } 370 371 /* Lookup the alias, as this contains the actual path. */ 372 path = fdt_get_alias_namelen(dtb, prop, len); 373 if (path == NULL) { 374 return -FDT_ERR_NOTFOUND; 375 } 376 377 return fdt_path_offset(dtb, path); 378 } 379 380 381 /******************************************************************************* 382 * Only devices which are direct children of root node use CPU address domain. 383 * All other devices use addresses that are local to the device node and cannot 384 * directly used by CPU. Device tree provides an address translation mechanism 385 * through "ranges" property which provides mappings from local address space to 386 * parent address space. Since a device could be a child of a child node to the 387 * root node, there can be more than one level of address translation needed to 388 * map the device local address space to CPU address space. 389 * fdtw_translate_address() API performs address translation of a local address 390 * to a global address with help of various helper functions. 391 ******************************************************************************/ 392 393 static bool fdtw_xlat_hit(const uint32_t *value, int child_addr_size, 394 int parent_addr_size, int range_size, uint64_t base_address, 395 uint64_t *translated_addr) 396 { 397 uint64_t local_address, parent_address, addr_range; 398 399 local_address = fdt_read_prop_cells(value, child_addr_size); 400 parent_address = fdt_read_prop_cells(value + child_addr_size, 401 parent_addr_size); 402 addr_range = fdt_read_prop_cells(value + child_addr_size + 403 parent_addr_size, 404 range_size); 405 VERBOSE("DT: Address %llx mapped to %llx with range %llx\n", 406 local_address, parent_address, addr_range); 407 408 /* Perform range check */ 409 if ((base_address < local_address) || 410 (base_address >= local_address + addr_range)) { 411 return false; 412 } 413 414 /* Found hit for the addr range that needs to be translated */ 415 *translated_addr = parent_address + (base_address - local_address); 416 VERBOSE("DT: child address %llx mapped to %llx in parent bus\n", 417 local_address, parent_address); 418 return true; 419 } 420 421 #define ILLEGAL_ADDR ULL(~0) 422 423 static uint64_t fdtw_search_all_xlat_entries(const void *dtb, 424 const struct fdt_property *ranges_prop, 425 int local_bus, uint64_t base_address) 426 { 427 uint64_t translated_addr; 428 const uint32_t *next_entry; 429 int parent_bus_node, nxlat_entries, length; 430 int self_addr_cells, parent_addr_cells, self_size_cells, ncells_xlat; 431 432 /* 433 * The number of cells in one translation entry in ranges is the sum of 434 * the following values: 435 * self#address-cells + parent#address-cells + self#size-cells 436 * Ex: the iofpga ranges property has one translation entry with 4 cells 437 * They represent iofpga#addr-cells + motherboard#addr-cells + iofpga#size-cells 438 * = 1 + 2 + 1 439 */ 440 441 parent_bus_node = fdt_parent_offset(dtb, local_bus); 442 self_addr_cells = fdt_address_cells(dtb, local_bus); 443 self_size_cells = fdt_size_cells(dtb, local_bus); 444 parent_addr_cells = fdt_address_cells(dtb, parent_bus_node); 445 446 /* Number of cells per translation entry i.e., mapping */ 447 ncells_xlat = self_addr_cells + parent_addr_cells + self_size_cells; 448 449 assert(ncells_xlat > 0); 450 451 /* 452 * Find the number of translations(mappings) specified in the current 453 * `ranges` property. Note that length represents number of bytes and 454 * is stored in big endian mode. 455 */ 456 length = fdt32_to_cpu(ranges_prop->len); 457 nxlat_entries = (length/sizeof(uint32_t))/ncells_xlat; 458 459 assert(nxlat_entries > 0); 460 461 next_entry = (const uint32_t *)ranges_prop->data; 462 463 /* Iterate over the entries in the "ranges" */ 464 for (int i = 0; i < nxlat_entries; i++) { 465 if (fdtw_xlat_hit(next_entry, self_addr_cells, 466 parent_addr_cells, self_size_cells, base_address, 467 &translated_addr)){ 468 return translated_addr; 469 } 470 next_entry = next_entry + ncells_xlat; 471 } 472 473 INFO("DT: No translation found for address %llx in node %s\n", 474 base_address, fdt_get_name(dtb, local_bus, NULL)); 475 return ILLEGAL_ADDR; 476 } 477 478 479 /******************************************************************************* 480 * address mapping needs to be done recursively starting from current node to 481 * root node through all intermediate parent nodes. 482 * Sample device tree is shown here: 483 484 smb@0,0 { 485 compatible = "simple-bus"; 486 487 #address-cells = <2>; 488 #size-cells = <1>; 489 ranges = <0 0 0 0x08000000 0x04000000>, 490 <1 0 0 0x14000000 0x04000000>, 491 <2 0 0 0x18000000 0x04000000>, 492 <3 0 0 0x1c000000 0x04000000>, 493 <4 0 0 0x0c000000 0x04000000>, 494 <5 0 0 0x10000000 0x04000000>; 495 496 motherboard { 497 arm,v2m-memory-map = "rs1"; 498 compatible = "arm,vexpress,v2m-p1", "simple-bus"; 499 #address-cells = <2>; 500 #size-cells = <1>; 501 ranges; 502 503 iofpga@3,00000000 { 504 compatible = "arm,amba-bus", "simple-bus"; 505 #address-cells = <1>; 506 #size-cells = <1>; 507 ranges = <0 3 0 0x200000>; 508 v2m_serial1: uart@a0000 { 509 compatible = "arm,pl011", "arm,primecell"; 510 reg = <0x0a0000 0x1000>; 511 interrupts = <0 6 4>; 512 clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>; 513 clock-names = "uartclk", "apb_pclk"; 514 }; 515 }; 516 }; 517 518 * As seen above, there are 3 levels of address translations needed. An empty 519 * `ranges` property denotes identity mapping (as seen in `motherboard` node). 520 * Each ranges property can map a set of child addresses to parent bus. Hence 521 * there can be more than 1 (translation) entry in the ranges property as seen 522 * in the `smb` node which has 6 translation entries. 523 ******************************************************************************/ 524 525 /* Recursive implementation */ 526 uint64_t fdtw_translate_address(const void *dtb, int node, 527 uint64_t base_address) 528 { 529 int length, local_bus_node; 530 const char *node_name; 531 uint64_t global_address; 532 533 local_bus_node = fdt_parent_offset(dtb, node); 534 node_name = fdt_get_name(dtb, local_bus_node, NULL); 535 536 /* 537 * In the example given above, starting from the leaf node: 538 * uart@a000 represents the current node 539 * iofpga@3,00000000 represents the local bus 540 * motherboard represents the parent bus 541 */ 542 543 /* Read the ranges property */ 544 const struct fdt_property *property = fdt_get_property(dtb, 545 local_bus_node, "ranges", &length); 546 547 if (property == NULL) { 548 if (local_bus_node == 0) { 549 /* 550 * root node doesn't have range property as addresses 551 * are in CPU address space. 552 */ 553 return base_address; 554 } 555 INFO("DT: Couldn't find ranges property in node %s\n", 556 node_name); 557 return ILLEGAL_ADDR; 558 } else if (length == 0) { 559 /* empty ranges indicates identity map to parent bus */ 560 return fdtw_translate_address(dtb, local_bus_node, base_address); 561 } 562 563 VERBOSE("DT: Translation lookup in node %s at offset %d\n", node_name, 564 local_bus_node); 565 global_address = fdtw_search_all_xlat_entries(dtb, property, 566 local_bus_node, base_address); 567 568 if (global_address == ILLEGAL_ADDR) { 569 return ILLEGAL_ADDR; 570 } 571 572 /* Translate the local device address recursively */ 573 return fdtw_translate_address(dtb, local_bus_node, global_address); 574 } 575