device_tree.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  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. #include "qemu/osdep.h"
  14. #ifdef CONFIG_LINUX
  15. #include <dirent.h>
  16. #endif
  17. #include "qapi/error.h"
  18. #include "qemu-common.h"
  19. #include "qemu/error-report.h"
  20. #include "qemu/bswap.h"
  21. #include "sysemu/device_tree.h"
  22. #include "sysemu/sysemu.h"
  23. #include "hw/loader.h"
  24. #include "hw/boards.h"
  25. #include "qemu/config-file.h"
  26. #include <libfdt.h>
  27. #define FDT_MAX_SIZE 0x100000
  28. void *create_device_tree(int *sizep)
  29. {
  30. void *fdt;
  31. int ret;
  32. *sizep = FDT_MAX_SIZE;
  33. fdt = g_malloc0(FDT_MAX_SIZE);
  34. ret = fdt_create(fdt, FDT_MAX_SIZE);
  35. if (ret < 0) {
  36. goto fail;
  37. }
  38. ret = fdt_finish_reservemap(fdt);
  39. if (ret < 0) {
  40. goto fail;
  41. }
  42. ret = fdt_begin_node(fdt, "");
  43. if (ret < 0) {
  44. goto fail;
  45. }
  46. ret = fdt_end_node(fdt);
  47. if (ret < 0) {
  48. goto fail;
  49. }
  50. ret = fdt_finish(fdt);
  51. if (ret < 0) {
  52. goto fail;
  53. }
  54. ret = fdt_open_into(fdt, fdt, *sizep);
  55. if (ret) {
  56. error_report("Unable to copy device tree in memory");
  57. exit(1);
  58. }
  59. return fdt;
  60. fail:
  61. error_report("%s Couldn't create dt: %s", __func__, fdt_strerror(ret));
  62. exit(1);
  63. }
  64. void *load_device_tree(const char *filename_path, int *sizep)
  65. {
  66. int dt_size;
  67. int dt_file_load_size;
  68. int ret;
  69. void *fdt = NULL;
  70. *sizep = 0;
  71. dt_size = get_image_size(filename_path);
  72. if (dt_size < 0) {
  73. error_report("Unable to get size of device tree file '%s'",
  74. filename_path);
  75. goto fail;
  76. }
  77. /* Expand to 2x size to give enough room for manipulation. */
  78. dt_size += 10000;
  79. dt_size *= 2;
  80. /* First allocate space in qemu for device tree */
  81. fdt = g_malloc0(dt_size);
  82. dt_file_load_size = load_image(filename_path, fdt);
  83. if (dt_file_load_size < 0) {
  84. error_report("Unable to open device tree file '%s'",
  85. filename_path);
  86. goto fail;
  87. }
  88. ret = fdt_open_into(fdt, fdt, dt_size);
  89. if (ret) {
  90. error_report("Unable to copy device tree in memory");
  91. goto fail;
  92. }
  93. /* Check sanity of device tree */
  94. if (fdt_check_header(fdt)) {
  95. error_report("Device tree file loaded into memory is invalid: %s",
  96. filename_path);
  97. goto fail;
  98. }
  99. *sizep = dt_size;
  100. return fdt;
  101. fail:
  102. g_free(fdt);
  103. return NULL;
  104. }
  105. #ifdef CONFIG_LINUX
  106. #define SYSFS_DT_BASEDIR "/proc/device-tree"
  107. /**
  108. * read_fstree: this function is inspired from dtc read_fstree
  109. * @fdt: preallocated fdt blob buffer, to be populated
  110. * @dirname: directory to scan under SYSFS_DT_BASEDIR
  111. * the search is recursive and the tree is searched down to the
  112. * leaves (property files).
  113. *
  114. * the function asserts in case of error
  115. */
  116. static void read_fstree(void *fdt, const char *dirname)
  117. {
  118. DIR *d;
  119. struct dirent *de;
  120. struct stat st;
  121. const char *root_dir = SYSFS_DT_BASEDIR;
  122. const char *parent_node;
  123. if (strstr(dirname, root_dir) != dirname) {
  124. error_setg(&error_fatal, "%s: %s must be searched within %s",
  125. __func__, dirname, root_dir);
  126. }
  127. parent_node = &dirname[strlen(SYSFS_DT_BASEDIR)];
  128. d = opendir(dirname);
  129. if (!d) {
  130. error_setg(&error_fatal, "%s cannot open %s", __func__, dirname);
  131. return;
  132. }
  133. while ((de = readdir(d)) != NULL) {
  134. char *tmpnam;
  135. if (!g_strcmp0(de->d_name, ".")
  136. || !g_strcmp0(de->d_name, "..")) {
  137. continue;
  138. }
  139. tmpnam = g_strdup_printf("%s/%s", dirname, de->d_name);
  140. if (lstat(tmpnam, &st) < 0) {
  141. error_setg(&error_fatal, "%s cannot lstat %s", __func__, tmpnam);
  142. }
  143. if (S_ISREG(st.st_mode)) {
  144. gchar *val;
  145. gsize len;
  146. if (!g_file_get_contents(tmpnam, &val, &len, NULL)) {
  147. error_setg(&error_fatal, "%s not able to extract info from %s",
  148. __func__, tmpnam);
  149. }
  150. if (strlen(parent_node) > 0) {
  151. qemu_fdt_setprop(fdt, parent_node,
  152. de->d_name, val, len);
  153. } else {
  154. qemu_fdt_setprop(fdt, "/", de->d_name, val, len);
  155. }
  156. g_free(val);
  157. } else if (S_ISDIR(st.st_mode)) {
  158. char *node_name;
  159. node_name = g_strdup_printf("%s/%s",
  160. parent_node, de->d_name);
  161. qemu_fdt_add_subnode(fdt, node_name);
  162. g_free(node_name);
  163. read_fstree(fdt, tmpnam);
  164. }
  165. g_free(tmpnam);
  166. }
  167. closedir(d);
  168. }
  169. /* load_device_tree_from_sysfs: extract the dt blob from host sysfs */
  170. void *load_device_tree_from_sysfs(void)
  171. {
  172. void *host_fdt;
  173. int host_fdt_size;
  174. host_fdt = create_device_tree(&host_fdt_size);
  175. read_fstree(host_fdt, SYSFS_DT_BASEDIR);
  176. if (fdt_check_header(host_fdt)) {
  177. error_setg(&error_fatal,
  178. "%s host device tree extracted into memory is invalid",
  179. __func__);
  180. }
  181. return host_fdt;
  182. }
  183. #endif /* CONFIG_LINUX */
  184. static int findnode_nofail(void *fdt, const char *node_path)
  185. {
  186. int offset;
  187. offset = fdt_path_offset(fdt, node_path);
  188. if (offset < 0) {
  189. error_report("%s Couldn't find node %s: %s", __func__, node_path,
  190. fdt_strerror(offset));
  191. exit(1);
  192. }
  193. return offset;
  194. }
  195. char **qemu_fdt_node_path(void *fdt, const char *name, char *compat,
  196. Error **errp)
  197. {
  198. int offset, len, ret;
  199. const char *iter_name;
  200. unsigned int path_len = 16, n = 0;
  201. GSList *path_list = NULL, *iter;
  202. char **path_array;
  203. offset = fdt_node_offset_by_compatible(fdt, -1, compat);
  204. while (offset >= 0) {
  205. iter_name = fdt_get_name(fdt, offset, &len);
  206. if (!iter_name) {
  207. offset = len;
  208. break;
  209. }
  210. if (!strcmp(iter_name, name)) {
  211. char *path;
  212. path = g_malloc(path_len);
  213. while ((ret = fdt_get_path(fdt, offset, path, path_len))
  214. == -FDT_ERR_NOSPACE) {
  215. path_len += 16;
  216. path = g_realloc(path, path_len);
  217. }
  218. path_list = g_slist_prepend(path_list, path);
  219. n++;
  220. }
  221. offset = fdt_node_offset_by_compatible(fdt, offset, compat);
  222. }
  223. if (offset < 0 && offset != -FDT_ERR_NOTFOUND) {
  224. error_setg(errp, "%s: abort parsing dt for %s/%s: %s",
  225. __func__, name, compat, fdt_strerror(offset));
  226. for (iter = path_list; iter; iter = iter->next) {
  227. g_free(iter->data);
  228. }
  229. g_slist_free(path_list);
  230. return NULL;
  231. }
  232. path_array = g_new(char *, n + 1);
  233. path_array[n--] = NULL;
  234. for (iter = path_list; iter; iter = iter->next) {
  235. path_array[n--] = iter->data;
  236. }
  237. g_slist_free(path_list);
  238. return path_array;
  239. }
  240. int qemu_fdt_setprop(void *fdt, const char *node_path,
  241. const char *property, const void *val, int size)
  242. {
  243. int r;
  244. r = fdt_setprop(fdt, findnode_nofail(fdt, node_path), property, val, size);
  245. if (r < 0) {
  246. error_report("%s: Couldn't set %s/%s: %s", __func__, node_path,
  247. property, fdt_strerror(r));
  248. exit(1);
  249. }
  250. return r;
  251. }
  252. int qemu_fdt_setprop_cell(void *fdt, const char *node_path,
  253. const char *property, uint32_t val)
  254. {
  255. int r;
  256. r = fdt_setprop_cell(fdt, findnode_nofail(fdt, node_path), property, val);
  257. if (r < 0) {
  258. error_report("%s: Couldn't set %s/%s = %#08x: %s", __func__,
  259. node_path, property, val, fdt_strerror(r));
  260. exit(1);
  261. }
  262. return r;
  263. }
  264. int qemu_fdt_setprop_u64(void *fdt, const char *node_path,
  265. const char *property, uint64_t val)
  266. {
  267. val = cpu_to_be64(val);
  268. return qemu_fdt_setprop(fdt, node_path, property, &val, sizeof(val));
  269. }
  270. int qemu_fdt_setprop_string(void *fdt, const char *node_path,
  271. const char *property, const char *string)
  272. {
  273. int r;
  274. r = fdt_setprop_string(fdt, findnode_nofail(fdt, node_path), property, string);
  275. if (r < 0) {
  276. error_report("%s: Couldn't set %s/%s = %s: %s", __func__,
  277. node_path, property, string, fdt_strerror(r));
  278. exit(1);
  279. }
  280. return r;
  281. }
  282. const void *qemu_fdt_getprop(void *fdt, const char *node_path,
  283. const char *property, int *lenp, Error **errp)
  284. {
  285. int len;
  286. const void *r;
  287. if (!lenp) {
  288. lenp = &len;
  289. }
  290. r = fdt_getprop(fdt, findnode_nofail(fdt, node_path), property, lenp);
  291. if (!r) {
  292. error_setg(errp, "%s: Couldn't get %s/%s: %s", __func__,
  293. node_path, property, fdt_strerror(*lenp));
  294. }
  295. return r;
  296. }
  297. uint32_t qemu_fdt_getprop_cell(void *fdt, const char *node_path,
  298. const char *property, int *lenp, Error **errp)
  299. {
  300. int len;
  301. const uint32_t *p;
  302. if (!lenp) {
  303. lenp = &len;
  304. }
  305. p = qemu_fdt_getprop(fdt, node_path, property, lenp, errp);
  306. if (!p) {
  307. return 0;
  308. } else if (*lenp != 4) {
  309. error_setg(errp, "%s: %s/%s not 4 bytes long (not a cell?)",
  310. __func__, node_path, property);
  311. *lenp = -EINVAL;
  312. return 0;
  313. }
  314. return be32_to_cpu(*p);
  315. }
  316. uint32_t qemu_fdt_get_phandle(void *fdt, const char *path)
  317. {
  318. uint32_t r;
  319. r = fdt_get_phandle(fdt, findnode_nofail(fdt, path));
  320. if (r == 0) {
  321. error_report("%s: Couldn't get phandle for %s: %s", __func__,
  322. path, fdt_strerror(r));
  323. exit(1);
  324. }
  325. return r;
  326. }
  327. int qemu_fdt_setprop_phandle(void *fdt, const char *node_path,
  328. const char *property,
  329. const char *target_node_path)
  330. {
  331. uint32_t phandle = qemu_fdt_get_phandle(fdt, target_node_path);
  332. return qemu_fdt_setprop_cell(fdt, node_path, property, phandle);
  333. }
  334. uint32_t qemu_fdt_alloc_phandle(void *fdt)
  335. {
  336. static int phandle = 0x0;
  337. /*
  338. * We need to find out if the user gave us special instruction at
  339. * which phandle id to start allocating phandles.
  340. */
  341. if (!phandle) {
  342. phandle = machine_phandle_start(current_machine);
  343. }
  344. if (!phandle) {
  345. /*
  346. * None or invalid phandle given on the command line, so fall back to
  347. * default starting point.
  348. */
  349. phandle = 0x8000;
  350. }
  351. return phandle++;
  352. }
  353. int qemu_fdt_nop_node(void *fdt, const char *node_path)
  354. {
  355. int r;
  356. r = fdt_nop_node(fdt, findnode_nofail(fdt, node_path));
  357. if (r < 0) {
  358. error_report("%s: Couldn't nop node %s: %s", __func__, node_path,
  359. fdt_strerror(r));
  360. exit(1);
  361. }
  362. return r;
  363. }
  364. int qemu_fdt_add_subnode(void *fdt, const char *name)
  365. {
  366. char *dupname = g_strdup(name);
  367. char *basename = strrchr(dupname, '/');
  368. int retval;
  369. int parent = 0;
  370. if (!basename) {
  371. g_free(dupname);
  372. return -1;
  373. }
  374. basename[0] = '\0';
  375. basename++;
  376. if (dupname[0]) {
  377. parent = findnode_nofail(fdt, dupname);
  378. }
  379. retval = fdt_add_subnode(fdt, parent, basename);
  380. if (retval < 0) {
  381. error_report("FDT: Failed to create subnode %s: %s", name,
  382. fdt_strerror(retval));
  383. exit(1);
  384. }
  385. g_free(dupname);
  386. return retval;
  387. }
  388. void qemu_fdt_dumpdtb(void *fdt, int size)
  389. {
  390. const char *dumpdtb = qemu_opt_get(qemu_get_machine_opts(), "dumpdtb");
  391. if (dumpdtb) {
  392. /* Dump the dtb to a file and quit */
  393. exit(g_file_set_contents(dumpdtb, fdt, size, NULL) ? 0 : 1);
  394. }
  395. }
  396. int qemu_fdt_setprop_sized_cells_from_array(void *fdt,
  397. const char *node_path,
  398. const char *property,
  399. int numvalues,
  400. uint64_t *values)
  401. {
  402. uint32_t *propcells;
  403. uint64_t value;
  404. int cellnum, vnum, ncells;
  405. uint32_t hival;
  406. int ret;
  407. propcells = g_new0(uint32_t, numvalues * 2);
  408. cellnum = 0;
  409. for (vnum = 0; vnum < numvalues; vnum++) {
  410. ncells = values[vnum * 2];
  411. if (ncells != 1 && ncells != 2) {
  412. ret = -1;
  413. goto out;
  414. }
  415. value = values[vnum * 2 + 1];
  416. hival = cpu_to_be32(value >> 32);
  417. if (ncells > 1) {
  418. propcells[cellnum++] = hival;
  419. } else if (hival != 0) {
  420. ret = -1;
  421. goto out;
  422. }
  423. propcells[cellnum++] = cpu_to_be32(value);
  424. }
  425. ret = qemu_fdt_setprop(fdt, node_path, property, propcells,
  426. cellnum * sizeof(uint32_t));
  427. out:
  428. g_free(propcells);
  429. return ret;
  430. }