device_tree.c (15821B)
1/* 2 * Functions to help device tree manipulation using libfdt. 3 * It also provides functions to read entries from device tree proc 4 * interface. 5 * 6 * Copyright 2008 IBM Corporation. 7 * Authors: Jerone Young <jyoung5@us.ibm.com> 8 * Hollis Blanchard <hollisb@us.ibm.com> 9 * 10 * This work is licensed under the GNU GPL license version 2 or later. 11 * 12 */ 13 14#include "qemu/osdep.h" 15 16#ifdef CONFIG_LINUX 17#include <dirent.h> 18#endif 19 20#include "qapi/error.h" 21#include "qemu/error-report.h" 22#include "qemu/option.h" 23#include "qemu/bswap.h" 24#include "qemu/cutils.h" 25#include "sysemu/device_tree.h" 26#include "hw/loader.h" 27#include "hw/boards.h" 28#include "qemu/config-file.h" 29 30#include <libfdt.h> 31 32#define FDT_MAX_SIZE 0x100000 33 34void *create_device_tree(int *sizep) 35{ 36 void *fdt; 37 int ret; 38 39 *sizep = FDT_MAX_SIZE; 40 fdt = g_malloc0(FDT_MAX_SIZE); 41 ret = fdt_create(fdt, FDT_MAX_SIZE); 42 if (ret < 0) { 43 goto fail; 44 } 45 ret = fdt_finish_reservemap(fdt); 46 if (ret < 0) { 47 goto fail; 48 } 49 ret = fdt_begin_node(fdt, ""); 50 if (ret < 0) { 51 goto fail; 52 } 53 ret = fdt_end_node(fdt); 54 if (ret < 0) { 55 goto fail; 56 } 57 ret = fdt_finish(fdt); 58 if (ret < 0) { 59 goto fail; 60 } 61 ret = fdt_open_into(fdt, fdt, *sizep); 62 if (ret) { 63 error_report("Unable to copy device tree in memory"); 64 exit(1); 65 } 66 67 return fdt; 68fail: 69 error_report("%s Couldn't create dt: %s", __func__, fdt_strerror(ret)); 70 exit(1); 71} 72 73void *load_device_tree(const char *filename_path, int *sizep) 74{ 75 int dt_size; 76 int dt_file_load_size; 77 int ret; 78 void *fdt = NULL; 79 80 *sizep = 0; 81 dt_size = get_image_size(filename_path); 82 if (dt_size < 0) { 83 error_report("Unable to get size of device tree file '%s'", 84 filename_path); 85 goto fail; 86 } 87 if (dt_size > INT_MAX / 2 - 10000) { 88 error_report("Device tree file '%s' is too large", filename_path); 89 goto fail; 90 } 91 92 /* Expand to 2x size to give enough room for manipulation. */ 93 dt_size += 10000; 94 dt_size *= 2; 95 /* First allocate space in qemu for device tree */ 96 fdt = g_malloc0(dt_size); 97 98 dt_file_load_size = load_image_size(filename_path, fdt, dt_size); 99 if (dt_file_load_size < 0) { 100 error_report("Unable to open device tree file '%s'", 101 filename_path); 102 goto fail; 103 } 104 105 ret = fdt_open_into(fdt, fdt, dt_size); 106 if (ret) { 107 error_report("Unable to copy device tree in memory"); 108 goto fail; 109 } 110 111 /* Check sanity of device tree */ 112 if (fdt_check_header(fdt)) { 113 error_report("Device tree file loaded into memory is invalid: %s", 114 filename_path); 115 goto fail; 116 } 117 *sizep = dt_size; 118 return fdt; 119 120fail: 121 g_free(fdt); 122 return NULL; 123} 124 125#ifdef CONFIG_LINUX 126 127#define SYSFS_DT_BASEDIR "/proc/device-tree" 128 129/** 130 * read_fstree: this function is inspired from dtc read_fstree 131 * @fdt: preallocated fdt blob buffer, to be populated 132 * @dirname: directory to scan under SYSFS_DT_BASEDIR 133 * the search is recursive and the tree is searched down to the 134 * leaves (property files). 135 * 136 * the function asserts in case of error 137 */ 138static void read_fstree(void *fdt, const char *dirname) 139{ 140 DIR *d; 141 struct dirent *de; 142 struct stat st; 143 const char *root_dir = SYSFS_DT_BASEDIR; 144 const char *parent_node; 145 146 if (strstr(dirname, root_dir) != dirname) { 147 error_report("%s: %s must be searched within %s", 148 __func__, dirname, root_dir); 149 exit(1); 150 } 151 parent_node = &dirname[strlen(SYSFS_DT_BASEDIR)]; 152 153 d = opendir(dirname); 154 if (!d) { 155 error_report("%s cannot open %s", __func__, dirname); 156 exit(1); 157 } 158 159 while ((de = readdir(d)) != NULL) { 160 char *tmpnam; 161 162 if (!g_strcmp0(de->d_name, ".") 163 || !g_strcmp0(de->d_name, "..")) { 164 continue; 165 } 166 167 tmpnam = g_strdup_printf("%s/%s", dirname, de->d_name); 168 169 if (lstat(tmpnam, &st) < 0) { 170 error_report("%s cannot lstat %s", __func__, tmpnam); 171 exit(1); 172 } 173 174 if (S_ISREG(st.st_mode)) { 175 gchar *val; 176 gsize len; 177 178 if (!g_file_get_contents(tmpnam, &val, &len, NULL)) { 179 error_report("%s not able to extract info from %s", 180 __func__, tmpnam); 181 exit(1); 182 } 183 184 if (strlen(parent_node) > 0) { 185 qemu_fdt_setprop(fdt, parent_node, 186 de->d_name, val, len); 187 } else { 188 qemu_fdt_setprop(fdt, "/", de->d_name, val, len); 189 } 190 g_free(val); 191 } else if (S_ISDIR(st.st_mode)) { 192 char *node_name; 193 194 node_name = g_strdup_printf("%s/%s", 195 parent_node, de->d_name); 196 qemu_fdt_add_subnode(fdt, node_name); 197 g_free(node_name); 198 read_fstree(fdt, tmpnam); 199 } 200 201 g_free(tmpnam); 202 } 203 204 closedir(d); 205} 206 207/* load_device_tree_from_sysfs: extract the dt blob from host sysfs */ 208void *load_device_tree_from_sysfs(void) 209{ 210 void *host_fdt; 211 int host_fdt_size; 212 213 host_fdt = create_device_tree(&host_fdt_size); 214 read_fstree(host_fdt, SYSFS_DT_BASEDIR); 215 if (fdt_check_header(host_fdt)) { 216 error_report("%s host device tree extracted into memory is invalid", 217 __func__); 218 exit(1); 219 } 220 return host_fdt; 221} 222 223#endif /* CONFIG_LINUX */ 224 225static int findnode_nofail(void *fdt, const char *node_path) 226{ 227 int offset; 228 229 offset = fdt_path_offset(fdt, node_path); 230 if (offset < 0) { 231 error_report("%s Couldn't find node %s: %s", __func__, node_path, 232 fdt_strerror(offset)); 233 exit(1); 234 } 235 236 return offset; 237} 238 239char **qemu_fdt_node_unit_path(void *fdt, const char *name, Error **errp) 240{ 241 char *prefix = g_strdup_printf("%s@", name); 242 unsigned int path_len = 16, n = 0; 243 GSList *path_list = NULL, *iter; 244 const char *iter_name; 245 int offset, len, ret; 246 char **path_array; 247 248 offset = fdt_next_node(fdt, -1, NULL); 249 250 while (offset >= 0) { 251 iter_name = fdt_get_name(fdt, offset, &len); 252 if (!iter_name) { 253 offset = len; 254 break; 255 } 256 if (!strcmp(iter_name, name) || g_str_has_prefix(iter_name, prefix)) { 257 char *path; 258 259 path = g_malloc(path_len); 260 while ((ret = fdt_get_path(fdt, offset, path, path_len)) 261 == -FDT_ERR_NOSPACE) { 262 path_len += 16; 263 path = g_realloc(path, path_len); 264 } 265 path_list = g_slist_prepend(path_list, path); 266 n++; 267 } 268 offset = fdt_next_node(fdt, offset, NULL); 269 } 270 g_free(prefix); 271 272 if (offset < 0 && offset != -FDT_ERR_NOTFOUND) { 273 error_setg(errp, "%s: abort parsing dt for %s node units: %s", 274 __func__, name, fdt_strerror(offset)); 275 for (iter = path_list; iter; iter = iter->next) { 276 g_free(iter->data); 277 } 278 g_slist_free(path_list); 279 return NULL; 280 } 281 282 path_array = g_new(char *, n + 1); 283 path_array[n--] = NULL; 284 285 for (iter = path_list; iter; iter = iter->next) { 286 path_array[n--] = iter->data; 287 } 288 289 g_slist_free(path_list); 290 291 return path_array; 292} 293 294char **qemu_fdt_node_path(void *fdt, const char *name, const char *compat, 295 Error **errp) 296{ 297 int offset, len, ret; 298 const char *iter_name; 299 unsigned int path_len = 16, n = 0; 300 GSList *path_list = NULL, *iter; 301 char **path_array; 302 303 offset = fdt_node_offset_by_compatible(fdt, -1, compat); 304 305 while (offset >= 0) { 306 iter_name = fdt_get_name(fdt, offset, &len); 307 if (!iter_name) { 308 offset = len; 309 break; 310 } 311 if (!name || !strcmp(iter_name, name)) { 312 char *path; 313 314 path = g_malloc(path_len); 315 while ((ret = fdt_get_path(fdt, offset, path, path_len)) 316 == -FDT_ERR_NOSPACE) { 317 path_len += 16; 318 path = g_realloc(path, path_len); 319 } 320 path_list = g_slist_prepend(path_list, path); 321 n++; 322 } 323 offset = fdt_node_offset_by_compatible(fdt, offset, compat); 324 } 325 326 if (offset < 0 && offset != -FDT_ERR_NOTFOUND) { 327 error_setg(errp, "%s: abort parsing dt for %s/%s: %s", 328 __func__, name, compat, fdt_strerror(offset)); 329 for (iter = path_list; iter; iter = iter->next) { 330 g_free(iter->data); 331 } 332 g_slist_free(path_list); 333 return NULL; 334 } 335 336 path_array = g_new(char *, n + 1); 337 path_array[n--] = NULL; 338 339 for (iter = path_list; iter; iter = iter->next) { 340 path_array[n--] = iter->data; 341 } 342 343 g_slist_free(path_list); 344 345 return path_array; 346} 347 348int qemu_fdt_setprop(void *fdt, const char *node_path, 349 const char *property, const void *val, int size) 350{ 351 int r; 352 353 r = fdt_setprop(fdt, findnode_nofail(fdt, node_path), property, val, size); 354 if (r < 0) { 355 error_report("%s: Couldn't set %s/%s: %s", __func__, node_path, 356 property, fdt_strerror(r)); 357 exit(1); 358 } 359 360 return r; 361} 362 363int qemu_fdt_setprop_cell(void *fdt, const char *node_path, 364 const char *property, uint32_t val) 365{ 366 int r; 367 368 r = fdt_setprop_cell(fdt, findnode_nofail(fdt, node_path), property, val); 369 if (r < 0) { 370 error_report("%s: Couldn't set %s/%s = %#08x: %s", __func__, 371 node_path, property, val, fdt_strerror(r)); 372 exit(1); 373 } 374 375 return r; 376} 377 378int qemu_fdt_setprop_u64(void *fdt, const char *node_path, 379 const char *property, uint64_t val) 380{ 381 val = cpu_to_be64(val); 382 return qemu_fdt_setprop(fdt, node_path, property, &val, sizeof(val)); 383} 384 385int qemu_fdt_setprop_string(void *fdt, const char *node_path, 386 const char *property, const char *string) 387{ 388 int r; 389 390 r = fdt_setprop_string(fdt, findnode_nofail(fdt, node_path), property, string); 391 if (r < 0) { 392 error_report("%s: Couldn't set %s/%s = %s: %s", __func__, 393 node_path, property, string, fdt_strerror(r)); 394 exit(1); 395 } 396 397 return r; 398} 399 400/* 401 * libfdt doesn't allow us to add string arrays directly but they are 402 * test a series of null terminated strings with a length. We build 403 * the string up here so we can calculate the final length. 404 */ 405int qemu_fdt_setprop_string_array(void *fdt, const char *node_path, 406 const char *prop, char **array, int len) 407{ 408 int ret, i, total_len = 0; 409 char *str, *p; 410 for (i = 0; i < len; i++) { 411 total_len += strlen(array[i]) + 1; 412 } 413 p = str = g_malloc0(total_len); 414 for (i = 0; i < len; i++) { 415 int len = strlen(array[i]) + 1; 416 pstrcpy(p, len, array[i]); 417 p += len; 418 } 419 420 ret = qemu_fdt_setprop(fdt, node_path, prop, str, total_len); 421 g_free(str); 422 return ret; 423} 424 425const void *qemu_fdt_getprop(void *fdt, const char *node_path, 426 const char *property, int *lenp, Error **errp) 427{ 428 int len; 429 const void *r; 430 431 if (!lenp) { 432 lenp = &len; 433 } 434 r = fdt_getprop(fdt, findnode_nofail(fdt, node_path), property, lenp); 435 if (!r) { 436 error_setg(errp, "%s: Couldn't get %s/%s: %s", __func__, 437 node_path, property, fdt_strerror(*lenp)); 438 } 439 return r; 440} 441 442uint32_t qemu_fdt_getprop_cell(void *fdt, const char *node_path, 443 const char *property, int *lenp, Error **errp) 444{ 445 int len; 446 const uint32_t *p; 447 448 if (!lenp) { 449 lenp = &len; 450 } 451 p = qemu_fdt_getprop(fdt, node_path, property, lenp, errp); 452 if (!p) { 453 return 0; 454 } else if (*lenp != 4) { 455 error_setg(errp, "%s: %s/%s not 4 bytes long (not a cell?)", 456 __func__, node_path, property); 457 *lenp = -EINVAL; 458 return 0; 459 } 460 return be32_to_cpu(*p); 461} 462 463uint32_t qemu_fdt_get_phandle(void *fdt, const char *path) 464{ 465 uint32_t r; 466 467 r = fdt_get_phandle(fdt, findnode_nofail(fdt, path)); 468 if (r == 0) { 469 error_report("%s: Couldn't get phandle for %s: %s", __func__, 470 path, fdt_strerror(r)); 471 exit(1); 472 } 473 474 return r; 475} 476 477int qemu_fdt_setprop_phandle(void *fdt, const char *node_path, 478 const char *property, 479 const char *target_node_path) 480{ 481 uint32_t phandle = qemu_fdt_get_phandle(fdt, target_node_path); 482 return qemu_fdt_setprop_cell(fdt, node_path, property, phandle); 483} 484 485uint32_t qemu_fdt_alloc_phandle(void *fdt) 486{ 487 static int phandle = 0x0; 488 489 /* 490 * We need to find out if the user gave us special instruction at 491 * which phandle id to start allocating phandles. 492 */ 493 if (!phandle) { 494 phandle = machine_phandle_start(current_machine); 495 } 496 497 if (!phandle) { 498 /* 499 * None or invalid phandle given on the command line, so fall back to 500 * default starting point. 501 */ 502 phandle = 0x8000; 503 } 504 505 return phandle++; 506} 507 508int qemu_fdt_nop_node(void *fdt, const char *node_path) 509{ 510 int r; 511 512 r = fdt_nop_node(fdt, findnode_nofail(fdt, node_path)); 513 if (r < 0) { 514 error_report("%s: Couldn't nop node %s: %s", __func__, node_path, 515 fdt_strerror(r)); 516 exit(1); 517 } 518 519 return r; 520} 521 522int qemu_fdt_add_subnode(void *fdt, const char *name) 523{ 524 char *dupname = g_strdup(name); 525 char *basename = strrchr(dupname, '/'); 526 int retval; 527 int parent = 0; 528 529 if (!basename) { 530 g_free(dupname); 531 return -1; 532 } 533 534 basename[0] = '\0'; 535 basename++; 536 537 if (dupname[0]) { 538 parent = findnode_nofail(fdt, dupname); 539 } 540 541 retval = fdt_add_subnode(fdt, parent, basename); 542 if (retval < 0) { 543 error_report("FDT: Failed to create subnode %s: %s", name, 544 fdt_strerror(retval)); 545 exit(1); 546 } 547 548 g_free(dupname); 549 return retval; 550} 551 552void qemu_fdt_dumpdtb(void *fdt, int size) 553{ 554 const char *dumpdtb = current_machine->dumpdtb; 555 556 if (dumpdtb) { 557 /* Dump the dtb to a file and quit */ 558 if (g_file_set_contents(dumpdtb, fdt, size, NULL)) { 559 info_report("dtb dumped to %s. Exiting.", dumpdtb); 560 exit(0); 561 } 562 error_report("%s: Failed dumping dtb to %s", __func__, dumpdtb); 563 exit(1); 564 } 565} 566 567int qemu_fdt_setprop_sized_cells_from_array(void *fdt, 568 const char *node_path, 569 const char *property, 570 int numvalues, 571 uint64_t *values) 572{ 573 uint32_t *propcells; 574 uint64_t value; 575 int cellnum, vnum, ncells; 576 uint32_t hival; 577 int ret; 578 579 propcells = g_new0(uint32_t, numvalues * 2); 580 581 cellnum = 0; 582 for (vnum = 0; vnum < numvalues; vnum++) { 583 ncells = values[vnum * 2]; 584 if (ncells != 1 && ncells != 2) { 585 ret = -1; 586 goto out; 587 } 588 value = values[vnum * 2 + 1]; 589 hival = cpu_to_be32(value >> 32); 590 if (ncells > 1) { 591 propcells[cellnum++] = hival; 592 } else if (hival != 0) { 593 ret = -1; 594 goto out; 595 } 596 propcells[cellnum++] = cpu_to_be32(value); 597 } 598 599 ret = qemu_fdt_setprop(fdt, node_path, property, propcells, 600 cellnum * sizeof(uint32_t)); 601out: 602 g_free(propcells); 603 return ret; 604}