device_tree.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  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 0x10000
  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. }
  132. while ((de = readdir(d)) != NULL) {
  133. char *tmpnam;
  134. if (!g_strcmp0(de->d_name, ".")
  135. || !g_strcmp0(de->d_name, "..")) {
  136. continue;
  137. }
  138. tmpnam = g_strdup_printf("%s/%s", dirname, de->d_name);
  139. if (lstat(tmpnam, &st) < 0) {
  140. error_setg(&error_fatal, "%s cannot lstat %s", __func__, tmpnam);
  141. }
  142. if (S_ISREG(st.st_mode)) {
  143. gchar *val;
  144. gsize len;
  145. if (!g_file_get_contents(tmpnam, &val, &len, NULL)) {
  146. error_setg(&error_fatal, "%s not able to extract info from %s",
  147. __func__, tmpnam);
  148. }
  149. if (strlen(parent_node) > 0) {
  150. qemu_fdt_setprop(fdt, parent_node,
  151. de->d_name, val, len);
  152. } else {
  153. qemu_fdt_setprop(fdt, "/", de->d_name, val, len);
  154. }
  155. g_free(val);
  156. } else if (S_ISDIR(st.st_mode)) {
  157. char *node_name;
  158. node_name = g_strdup_printf("%s/%s",
  159. parent_node, de->d_name);
  160. qemu_fdt_add_subnode(fdt, node_name);
  161. g_free(node_name);
  162. read_fstree(fdt, tmpnam);
  163. }
  164. g_free(tmpnam);
  165. }
  166. closedir(d);
  167. }
  168. /* load_device_tree_from_sysfs: extract the dt blob from host sysfs */
  169. void *load_device_tree_from_sysfs(void)
  170. {
  171. void *host_fdt;
  172. int host_fdt_size;
  173. host_fdt = create_device_tree(&host_fdt_size);
  174. read_fstree(host_fdt, SYSFS_DT_BASEDIR);
  175. if (fdt_check_header(host_fdt)) {
  176. error_setg(&error_fatal,
  177. "%s host device tree extracted into memory is invalid",
  178. __func__);
  179. }
  180. return host_fdt;
  181. }
  182. #endif /* CONFIG_LINUX */
  183. static int findnode_nofail(void *fdt, const char *node_path)
  184. {
  185. int offset;
  186. offset = fdt_path_offset(fdt, node_path);
  187. if (offset < 0) {
  188. error_report("%s Couldn't find node %s: %s", __func__, node_path,
  189. fdt_strerror(offset));
  190. exit(1);
  191. }
  192. return offset;
  193. }
  194. char **qemu_fdt_node_path(void *fdt, const char *name, char *compat,
  195. Error **errp)
  196. {
  197. int offset, len, ret;
  198. const char *iter_name;
  199. unsigned int path_len = 16, n = 0;
  200. GSList *path_list = NULL, *iter;
  201. char **path_array;
  202. offset = fdt_node_offset_by_compatible(fdt, -1, compat);
  203. while (offset >= 0) {
  204. iter_name = fdt_get_name(fdt, offset, &len);
  205. if (!iter_name) {
  206. offset = len;
  207. break;
  208. }
  209. if (!strcmp(iter_name, name)) {
  210. char *path;
  211. path = g_malloc(path_len);
  212. while ((ret = fdt_get_path(fdt, offset, path, path_len))
  213. == -FDT_ERR_NOSPACE) {
  214. path_len += 16;
  215. path = g_realloc(path, path_len);
  216. }
  217. path_list = g_slist_prepend(path_list, path);
  218. n++;
  219. }
  220. offset = fdt_node_offset_by_compatible(fdt, offset, compat);
  221. }
  222. if (offset < 0 && offset != -FDT_ERR_NOTFOUND) {
  223. error_setg(errp, "%s: abort parsing dt for %s/%s: %s",
  224. __func__, name, compat, fdt_strerror(offset));
  225. for (iter = path_list; iter; iter = iter->next) {
  226. g_free(iter->data);
  227. }
  228. g_slist_free(path_list);
  229. return NULL;
  230. }
  231. path_array = g_new(char *, n + 1);
  232. path_array[n--] = NULL;
  233. for (iter = path_list; iter; iter = iter->next) {
  234. path_array[n--] = iter->data;
  235. }
  236. g_slist_free(path_list);
  237. return path_array;
  238. }
  239. int qemu_fdt_setprop(void *fdt, const char *node_path,
  240. const char *property, const void *val, int size)
  241. {
  242. int r;
  243. r = fdt_setprop(fdt, findnode_nofail(fdt, node_path), property, val, size);
  244. if (r < 0) {
  245. error_report("%s: Couldn't set %s/%s: %s", __func__, node_path,
  246. property, fdt_strerror(r));
  247. exit(1);
  248. }
  249. return r;
  250. }
  251. int qemu_fdt_setprop_cell(void *fdt, const char *node_path,
  252. const char *property, uint32_t val)
  253. {
  254. int r;
  255. r = fdt_setprop_cell(fdt, findnode_nofail(fdt, node_path), property, val);
  256. if (r < 0) {
  257. error_report("%s: Couldn't set %s/%s = %#08x: %s", __func__,
  258. node_path, property, val, fdt_strerror(r));
  259. exit(1);
  260. }
  261. return r;
  262. }
  263. int qemu_fdt_setprop_u64(void *fdt, const char *node_path,
  264. const char *property, uint64_t val)
  265. {
  266. val = cpu_to_be64(val);
  267. return qemu_fdt_setprop(fdt, node_path, property, &val, sizeof(val));
  268. }
  269. int qemu_fdt_setprop_string(void *fdt, const char *node_path,
  270. const char *property, const char *string)
  271. {
  272. int r;
  273. r = fdt_setprop_string(fdt, findnode_nofail(fdt, node_path), property, string);
  274. if (r < 0) {
  275. error_report("%s: Couldn't set %s/%s = %s: %s", __func__,
  276. node_path, property, string, fdt_strerror(r));
  277. exit(1);
  278. }
  279. return r;
  280. }
  281. const void *qemu_fdt_getprop(void *fdt, const char *node_path,
  282. const char *property, int *lenp, Error **errp)
  283. {
  284. int len;
  285. const void *r;
  286. if (!lenp) {
  287. lenp = &len;
  288. }
  289. r = fdt_getprop(fdt, findnode_nofail(fdt, node_path), property, lenp);
  290. if (!r) {
  291. error_setg(errp, "%s: Couldn't get %s/%s: %s", __func__,
  292. node_path, property, fdt_strerror(*lenp));
  293. }
  294. return r;
  295. }
  296. uint32_t qemu_fdt_getprop_cell(void *fdt, const char *node_path,
  297. const char *property, int *lenp, Error **errp)
  298. {
  299. int len;
  300. const uint32_t *p;
  301. if (!lenp) {
  302. lenp = &len;
  303. }
  304. p = qemu_fdt_getprop(fdt, node_path, property, lenp, errp);
  305. if (!p) {
  306. return 0;
  307. } else if (*lenp != 4) {
  308. error_setg(errp, "%s: %s/%s not 4 bytes long (not a cell?)",
  309. __func__, node_path, property);
  310. *lenp = -EINVAL;
  311. return 0;
  312. }
  313. return be32_to_cpu(*p);
  314. }
  315. uint32_t qemu_fdt_get_phandle(void *fdt, const char *path)
  316. {
  317. uint32_t r;
  318. r = fdt_get_phandle(fdt, findnode_nofail(fdt, path));
  319. if (r == 0) {
  320. error_report("%s: Couldn't get phandle for %s: %s", __func__,
  321. path, fdt_strerror(r));
  322. exit(1);
  323. }
  324. return r;
  325. }
  326. int qemu_fdt_setprop_phandle(void *fdt, const char *node_path,
  327. const char *property,
  328. const char *target_node_path)
  329. {
  330. uint32_t phandle = qemu_fdt_get_phandle(fdt, target_node_path);
  331. return qemu_fdt_setprop_cell(fdt, node_path, property, phandle);
  332. }
  333. uint32_t qemu_fdt_alloc_phandle(void *fdt)
  334. {
  335. static int phandle = 0x0;
  336. /*
  337. * We need to find out if the user gave us special instruction at
  338. * which phandle id to start allocating phandles.
  339. */
  340. if (!phandle) {
  341. phandle = machine_phandle_start(current_machine);
  342. }
  343. if (!phandle) {
  344. /*
  345. * None or invalid phandle given on the command line, so fall back to
  346. * default starting point.
  347. */
  348. phandle = 0x8000;
  349. }
  350. return phandle++;
  351. }
  352. int qemu_fdt_nop_node(void *fdt, const char *node_path)
  353. {
  354. int r;
  355. r = fdt_nop_node(fdt, findnode_nofail(fdt, node_path));
  356. if (r < 0) {
  357. error_report("%s: Couldn't nop node %s: %s", __func__, node_path,
  358. fdt_strerror(r));
  359. exit(1);
  360. }
  361. return r;
  362. }
  363. int qemu_fdt_add_subnode(void *fdt, const char *name)
  364. {
  365. char *dupname = g_strdup(name);
  366. char *basename = strrchr(dupname, '/');
  367. int retval;
  368. int parent = 0;
  369. if (!basename) {
  370. g_free(dupname);
  371. return -1;
  372. }
  373. basename[0] = '\0';
  374. basename++;
  375. if (dupname[0]) {
  376. parent = findnode_nofail(fdt, dupname);
  377. }
  378. retval = fdt_add_subnode(fdt, parent, basename);
  379. if (retval < 0) {
  380. error_report("FDT: Failed to create subnode %s: %s", name,
  381. fdt_strerror(retval));
  382. exit(1);
  383. }
  384. g_free(dupname);
  385. return retval;
  386. }
  387. void qemu_fdt_dumpdtb(void *fdt, int size)
  388. {
  389. const char *dumpdtb = qemu_opt_get(qemu_get_machine_opts(), "dumpdtb");
  390. if (dumpdtb) {
  391. /* Dump the dtb to a file and quit */
  392. exit(g_file_set_contents(dumpdtb, fdt, size, NULL) ? 0 : 1);
  393. }
  394. }
  395. int qemu_fdt_setprop_sized_cells_from_array(void *fdt,
  396. const char *node_path,
  397. const char *property,
  398. int numvalues,
  399. uint64_t *values)
  400. {
  401. uint32_t *propcells;
  402. uint64_t value;
  403. int cellnum, vnum, ncells;
  404. uint32_t hival;
  405. int ret;
  406. propcells = g_new0(uint32_t, numvalues * 2);
  407. cellnum = 0;
  408. for (vnum = 0; vnum < numvalues; vnum++) {
  409. ncells = values[vnum * 2];
  410. if (ncells != 1 && ncells != 2) {
  411. ret = -1;
  412. goto out;
  413. }
  414. value = values[vnum * 2 + 1];
  415. hival = cpu_to_be32(value >> 32);
  416. if (ncells > 1) {
  417. propcells[cellnum++] = hival;
  418. } else if (hival != 0) {
  419. ret = -1;
  420. goto out;
  421. }
  422. propcells[cellnum++] = cpu_to_be32(value);
  423. }
  424. ret = qemu_fdt_setprop(fdt, node_path, property, propcells,
  425. cellnum * sizeof(uint32_t));
  426. out:
  427. g_free(propcells);
  428. return ret;
  429. }