qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio
at master 579 lines 15 kB view raw
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 "sysemu/device_tree.h" 25#include "sysemu/sysemu.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 400const void *qemu_fdt_getprop(void *fdt, const char *node_path, 401 const char *property, int *lenp, Error **errp) 402{ 403 int len; 404 const void *r; 405 406 if (!lenp) { 407 lenp = &len; 408 } 409 r = fdt_getprop(fdt, findnode_nofail(fdt, node_path), property, lenp); 410 if (!r) { 411 error_setg(errp, "%s: Couldn't get %s/%s: %s", __func__, 412 node_path, property, fdt_strerror(*lenp)); 413 } 414 return r; 415} 416 417uint32_t qemu_fdt_getprop_cell(void *fdt, const char *node_path, 418 const char *property, int *lenp, Error **errp) 419{ 420 int len; 421 const uint32_t *p; 422 423 if (!lenp) { 424 lenp = &len; 425 } 426 p = qemu_fdt_getprop(fdt, node_path, property, lenp, errp); 427 if (!p) { 428 return 0; 429 } else if (*lenp != 4) { 430 error_setg(errp, "%s: %s/%s not 4 bytes long (not a cell?)", 431 __func__, node_path, property); 432 *lenp = -EINVAL; 433 return 0; 434 } 435 return be32_to_cpu(*p); 436} 437 438uint32_t qemu_fdt_get_phandle(void *fdt, const char *path) 439{ 440 uint32_t r; 441 442 r = fdt_get_phandle(fdt, findnode_nofail(fdt, path)); 443 if (r == 0) { 444 error_report("%s: Couldn't get phandle for %s: %s", __func__, 445 path, fdt_strerror(r)); 446 exit(1); 447 } 448 449 return r; 450} 451 452int qemu_fdt_setprop_phandle(void *fdt, const char *node_path, 453 const char *property, 454 const char *target_node_path) 455{ 456 uint32_t phandle = qemu_fdt_get_phandle(fdt, target_node_path); 457 return qemu_fdt_setprop_cell(fdt, node_path, property, phandle); 458} 459 460uint32_t qemu_fdt_alloc_phandle(void *fdt) 461{ 462 static int phandle = 0x0; 463 464 /* 465 * We need to find out if the user gave us special instruction at 466 * which phandle id to start allocating phandles. 467 */ 468 if (!phandle) { 469 phandle = machine_phandle_start(current_machine); 470 } 471 472 if (!phandle) { 473 /* 474 * None or invalid phandle given on the command line, so fall back to 475 * default starting point. 476 */ 477 phandle = 0x8000; 478 } 479 480 return phandle++; 481} 482 483int qemu_fdt_nop_node(void *fdt, const char *node_path) 484{ 485 int r; 486 487 r = fdt_nop_node(fdt, findnode_nofail(fdt, node_path)); 488 if (r < 0) { 489 error_report("%s: Couldn't nop node %s: %s", __func__, node_path, 490 fdt_strerror(r)); 491 exit(1); 492 } 493 494 return r; 495} 496 497int qemu_fdt_add_subnode(void *fdt, const char *name) 498{ 499 char *dupname = g_strdup(name); 500 char *basename = strrchr(dupname, '/'); 501 int retval; 502 int parent = 0; 503 504 if (!basename) { 505 g_free(dupname); 506 return -1; 507 } 508 509 basename[0] = '\0'; 510 basename++; 511 512 if (dupname[0]) { 513 parent = findnode_nofail(fdt, dupname); 514 } 515 516 retval = fdt_add_subnode(fdt, parent, basename); 517 if (retval < 0) { 518 error_report("FDT: Failed to create subnode %s: %s", name, 519 fdt_strerror(retval)); 520 exit(1); 521 } 522 523 g_free(dupname); 524 return retval; 525} 526 527void qemu_fdt_dumpdtb(void *fdt, int size) 528{ 529 const char *dumpdtb = qemu_opt_get(qemu_get_machine_opts(), "dumpdtb"); 530 531 if (dumpdtb) { 532 /* Dump the dtb to a file and quit */ 533 if (g_file_set_contents(dumpdtb, fdt, size, NULL)) { 534 info_report("dtb dumped to %s. Exiting.", dumpdtb); 535 exit(0); 536 } 537 error_report("%s: Failed dumping dtb to %s", __func__, dumpdtb); 538 exit(1); 539 } 540} 541 542int qemu_fdt_setprop_sized_cells_from_array(void *fdt, 543 const char *node_path, 544 const char *property, 545 int numvalues, 546 uint64_t *values) 547{ 548 uint32_t *propcells; 549 uint64_t value; 550 int cellnum, vnum, ncells; 551 uint32_t hival; 552 int ret; 553 554 propcells = g_new0(uint32_t, numvalues * 2); 555 556 cellnum = 0; 557 for (vnum = 0; vnum < numvalues; vnum++) { 558 ncells = values[vnum * 2]; 559 if (ncells != 1 && ncells != 2) { 560 ret = -1; 561 goto out; 562 } 563 value = values[vnum * 2 + 1]; 564 hival = cpu_to_be32(value >> 32); 565 if (ncells > 1) { 566 propcells[cellnum++] = hival; 567 } else if (hival != 0) { 568 ret = -1; 569 goto out; 570 } 571 propcells[cellnum++] = cpu_to_be32(value); 572 } 573 574 ret = qemu_fdt_setprop(fdt, node_path, property, propcells, 575 cellnum * sizeof(uint32_t)); 576out: 577 g_free(propcells); 578 return ret; 579}