fdt.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. /*
  2. * Functions for working with the Flattened Device Tree data format
  3. *
  4. * Copyright 2009 Benjamin Herrenschmidt, IBM Corp
  5. * benh@kernel.crashing.org
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * version 2 as published by the Free Software Foundation.
  10. */
  11. #include <linux/kernel.h>
  12. #include <linux/lmb.h>
  13. #include <linux/initrd.h>
  14. #include <linux/of.h>
  15. #include <linux/of_fdt.h>
  16. struct boot_param_header *initial_boot_params;
  17. char *find_flat_dt_string(u32 offset)
  18. {
  19. return ((char *)initial_boot_params) +
  20. initial_boot_params->off_dt_strings + offset;
  21. }
  22. /**
  23. * of_scan_flat_dt - scan flattened tree blob and call callback on each.
  24. * @it: callback function
  25. * @data: context data pointer
  26. *
  27. * This function is used to scan the flattened device-tree, it is
  28. * used to extract the memory information at boot before we can
  29. * unflatten the tree
  30. */
  31. int __init of_scan_flat_dt(int (*it)(unsigned long node,
  32. const char *uname, int depth,
  33. void *data),
  34. void *data)
  35. {
  36. unsigned long p = ((unsigned long)initial_boot_params) +
  37. initial_boot_params->off_dt_struct;
  38. int rc = 0;
  39. int depth = -1;
  40. do {
  41. u32 tag = *((u32 *)p);
  42. char *pathp;
  43. p += 4;
  44. if (tag == OF_DT_END_NODE) {
  45. depth--;
  46. continue;
  47. }
  48. if (tag == OF_DT_NOP)
  49. continue;
  50. if (tag == OF_DT_END)
  51. break;
  52. if (tag == OF_DT_PROP) {
  53. u32 sz = *((u32 *)p);
  54. p += 8;
  55. if (initial_boot_params->version < 0x10)
  56. p = _ALIGN(p, sz >= 8 ? 8 : 4);
  57. p += sz;
  58. p = _ALIGN(p, 4);
  59. continue;
  60. }
  61. if (tag != OF_DT_BEGIN_NODE) {
  62. pr_err("Invalid tag %x in flat device tree!\n", tag);
  63. return -EINVAL;
  64. }
  65. depth++;
  66. pathp = (char *)p;
  67. p = _ALIGN(p + strlen(pathp) + 1, 4);
  68. if ((*pathp) == '/') {
  69. char *lp, *np;
  70. for (lp = NULL, np = pathp; *np; np++)
  71. if ((*np) == '/')
  72. lp = np+1;
  73. if (lp != NULL)
  74. pathp = lp;
  75. }
  76. rc = it(p, pathp, depth, data);
  77. if (rc != 0)
  78. break;
  79. } while (1);
  80. return rc;
  81. }
  82. /**
  83. * of_get_flat_dt_root - find the root node in the flat blob
  84. */
  85. unsigned long __init of_get_flat_dt_root(void)
  86. {
  87. unsigned long p = ((unsigned long)initial_boot_params) +
  88. initial_boot_params->off_dt_struct;
  89. while (*((u32 *)p) == OF_DT_NOP)
  90. p += 4;
  91. BUG_ON(*((u32 *)p) != OF_DT_BEGIN_NODE);
  92. p += 4;
  93. return _ALIGN(p + strlen((char *)p) + 1, 4);
  94. }
  95. /**
  96. * of_get_flat_dt_prop - Given a node in the flat blob, return the property ptr
  97. *
  98. * This function can be used within scan_flattened_dt callback to get
  99. * access to properties
  100. */
  101. void *__init of_get_flat_dt_prop(unsigned long node, const char *name,
  102. unsigned long *size)
  103. {
  104. unsigned long p = node;
  105. do {
  106. u32 tag = *((u32 *)p);
  107. u32 sz, noff;
  108. const char *nstr;
  109. p += 4;
  110. if (tag == OF_DT_NOP)
  111. continue;
  112. if (tag != OF_DT_PROP)
  113. return NULL;
  114. sz = *((u32 *)p);
  115. noff = *((u32 *)(p + 4));
  116. p += 8;
  117. if (initial_boot_params->version < 0x10)
  118. p = _ALIGN(p, sz >= 8 ? 8 : 4);
  119. nstr = find_flat_dt_string(noff);
  120. if (nstr == NULL) {
  121. pr_warning("Can't find property index name !\n");
  122. return NULL;
  123. }
  124. if (strcmp(name, nstr) == 0) {
  125. if (size)
  126. *size = sz;
  127. return (void *)p;
  128. }
  129. p += sz;
  130. p = _ALIGN(p, 4);
  131. } while (1);
  132. }
  133. /**
  134. * of_flat_dt_is_compatible - Return true if given node has compat in compatible list
  135. * @node: node to test
  136. * @compat: compatible string to compare with compatible list.
  137. */
  138. int __init of_flat_dt_is_compatible(unsigned long node, const char *compat)
  139. {
  140. const char *cp;
  141. unsigned long cplen, l;
  142. cp = of_get_flat_dt_prop(node, "compatible", &cplen);
  143. if (cp == NULL)
  144. return 0;
  145. while (cplen > 0) {
  146. if (strncasecmp(cp, compat, strlen(compat)) == 0)
  147. return 1;
  148. l = strlen(cp) + 1;
  149. cp += l;
  150. cplen -= l;
  151. }
  152. return 0;
  153. }
  154. static void *__init unflatten_dt_alloc(unsigned long *mem, unsigned long size,
  155. unsigned long align)
  156. {
  157. void *res;
  158. *mem = _ALIGN(*mem, align);
  159. res = (void *)*mem;
  160. *mem += size;
  161. return res;
  162. }
  163. /**
  164. * unflatten_dt_node - Alloc and populate a device_node from the flat tree
  165. * @p: pointer to node in flat tree
  166. * @dad: Parent struct device_node
  167. * @allnextpp: pointer to ->allnext from last allocated device_node
  168. * @fpsize: Size of the node path up at the current depth.
  169. */
  170. unsigned long __init unflatten_dt_node(unsigned long mem,
  171. unsigned long *p,
  172. struct device_node *dad,
  173. struct device_node ***allnextpp,
  174. unsigned long fpsize)
  175. {
  176. struct device_node *np;
  177. struct property *pp, **prev_pp = NULL;
  178. char *pathp;
  179. u32 tag;
  180. unsigned int l, allocl;
  181. int has_name = 0;
  182. int new_format = 0;
  183. tag = *((u32 *)(*p));
  184. if (tag != OF_DT_BEGIN_NODE) {
  185. pr_err("Weird tag at start of node: %x\n", tag);
  186. return mem;
  187. }
  188. *p += 4;
  189. pathp = (char *)*p;
  190. l = allocl = strlen(pathp) + 1;
  191. *p = _ALIGN(*p + l, 4);
  192. /* version 0x10 has a more compact unit name here instead of the full
  193. * path. we accumulate the full path size using "fpsize", we'll rebuild
  194. * it later. We detect this because the first character of the name is
  195. * not '/'.
  196. */
  197. if ((*pathp) != '/') {
  198. new_format = 1;
  199. if (fpsize == 0) {
  200. /* root node: special case. fpsize accounts for path
  201. * plus terminating zero. root node only has '/', so
  202. * fpsize should be 2, but we want to avoid the first
  203. * level nodes to have two '/' so we use fpsize 1 here
  204. */
  205. fpsize = 1;
  206. allocl = 2;
  207. } else {
  208. /* account for '/' and path size minus terminal 0
  209. * already in 'l'
  210. */
  211. fpsize += l;
  212. allocl = fpsize;
  213. }
  214. }
  215. np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + allocl,
  216. __alignof__(struct device_node));
  217. if (allnextpp) {
  218. memset(np, 0, sizeof(*np));
  219. np->full_name = ((char *)np) + sizeof(struct device_node);
  220. if (new_format) {
  221. char *fn = np->full_name;
  222. /* rebuild full path for new format */
  223. if (dad && dad->parent) {
  224. strcpy(fn, dad->full_name);
  225. #ifdef DEBUG
  226. if ((strlen(fn) + l + 1) != allocl) {
  227. pr_debug("%s: p: %d, l: %d, a: %d\n",
  228. pathp, (int)strlen(fn),
  229. l, allocl);
  230. }
  231. #endif
  232. fn += strlen(fn);
  233. }
  234. *(fn++) = '/';
  235. memcpy(fn, pathp, l);
  236. } else
  237. memcpy(np->full_name, pathp, l);
  238. prev_pp = &np->properties;
  239. **allnextpp = np;
  240. *allnextpp = &np->allnext;
  241. if (dad != NULL) {
  242. np->parent = dad;
  243. /* we temporarily use the next field as `last_child'*/
  244. if (dad->next == NULL)
  245. dad->child = np;
  246. else
  247. dad->next->sibling = np;
  248. dad->next = np;
  249. }
  250. kref_init(&np->kref);
  251. }
  252. while (1) {
  253. u32 sz, noff;
  254. char *pname;
  255. tag = *((u32 *)(*p));
  256. if (tag == OF_DT_NOP) {
  257. *p += 4;
  258. continue;
  259. }
  260. if (tag != OF_DT_PROP)
  261. break;
  262. *p += 4;
  263. sz = *((u32 *)(*p));
  264. noff = *((u32 *)((*p) + 4));
  265. *p += 8;
  266. if (initial_boot_params->version < 0x10)
  267. *p = _ALIGN(*p, sz >= 8 ? 8 : 4);
  268. pname = find_flat_dt_string(noff);
  269. if (pname == NULL) {
  270. pr_info("Can't find property name in list !\n");
  271. break;
  272. }
  273. if (strcmp(pname, "name") == 0)
  274. has_name = 1;
  275. l = strlen(pname) + 1;
  276. pp = unflatten_dt_alloc(&mem, sizeof(struct property),
  277. __alignof__(struct property));
  278. if (allnextpp) {
  279. if (strcmp(pname, "linux,phandle") == 0) {
  280. np->node = *((u32 *)*p);
  281. if (np->linux_phandle == 0)
  282. np->linux_phandle = np->node;
  283. }
  284. if (strcmp(pname, "ibm,phandle") == 0)
  285. np->linux_phandle = *((u32 *)*p);
  286. pp->name = pname;
  287. pp->length = sz;
  288. pp->value = (void *)*p;
  289. *prev_pp = pp;
  290. prev_pp = &pp->next;
  291. }
  292. *p = _ALIGN((*p) + sz, 4);
  293. }
  294. /* with version 0x10 we may not have the name property, recreate
  295. * it here from the unit name if absent
  296. */
  297. if (!has_name) {
  298. char *p1 = pathp, *ps = pathp, *pa = NULL;
  299. int sz;
  300. while (*p1) {
  301. if ((*p1) == '@')
  302. pa = p1;
  303. if ((*p1) == '/')
  304. ps = p1 + 1;
  305. p1++;
  306. }
  307. if (pa < ps)
  308. pa = p1;
  309. sz = (pa - ps) + 1;
  310. pp = unflatten_dt_alloc(&mem, sizeof(struct property) + sz,
  311. __alignof__(struct property));
  312. if (allnextpp) {
  313. pp->name = "name";
  314. pp->length = sz;
  315. pp->value = pp + 1;
  316. *prev_pp = pp;
  317. prev_pp = &pp->next;
  318. memcpy(pp->value, ps, sz - 1);
  319. ((char *)pp->value)[sz - 1] = 0;
  320. pr_debug("fixed up name for %s -> %s\n", pathp,
  321. (char *)pp->value);
  322. }
  323. }
  324. if (allnextpp) {
  325. *prev_pp = NULL;
  326. np->name = of_get_property(np, "name", NULL);
  327. np->type = of_get_property(np, "device_type", NULL);
  328. if (!np->name)
  329. np->name = "<NULL>";
  330. if (!np->type)
  331. np->type = "<NULL>";
  332. }
  333. while (tag == OF_DT_BEGIN_NODE) {
  334. mem = unflatten_dt_node(mem, p, np, allnextpp, fpsize);
  335. tag = *((u32 *)(*p));
  336. }
  337. if (tag != OF_DT_END_NODE) {
  338. pr_err("Weird tag at end of node: %x\n", tag);
  339. return mem;
  340. }
  341. *p += 4;
  342. return mem;
  343. }
  344. #ifdef CONFIG_BLK_DEV_INITRD
  345. /**
  346. * early_init_dt_check_for_initrd - Decode initrd location from flat tree
  347. * @node: reference to node containing initrd location ('chosen')
  348. */
  349. void __init early_init_dt_check_for_initrd(unsigned long node)
  350. {
  351. unsigned long len;
  352. u32 *prop;
  353. pr_debug("Looking for initrd properties... ");
  354. prop = of_get_flat_dt_prop(node, "linux,initrd-start", &len);
  355. if (prop) {
  356. initrd_start = (unsigned long)
  357. __va(of_read_ulong(prop, len/4));
  358. prop = of_get_flat_dt_prop(node, "linux,initrd-end", &len);
  359. if (prop) {
  360. initrd_end = (unsigned long)
  361. __va(of_read_ulong(prop, len/4));
  362. initrd_below_start_ok = 1;
  363. } else {
  364. initrd_start = 0;
  365. }
  366. }
  367. pr_debug("initrd_start=0x%lx initrd_end=0x%lx\n",
  368. initrd_start, initrd_end);
  369. }
  370. #else
  371. inline void early_init_dt_check_for_initrd(unsigned long node)
  372. {
  373. }
  374. #endif /* CONFIG_BLK_DEV_INITRD */
  375. /**
  376. * unflatten_device_tree - create tree of device_nodes from flat blob
  377. *
  378. * unflattens the device-tree passed by the firmware, creating the
  379. * tree of struct device_node. It also fills the "name" and "type"
  380. * pointers of the nodes so the normal device-tree walking functions
  381. * can be used.
  382. */
  383. void __init unflatten_device_tree(void)
  384. {
  385. unsigned long start, mem, size;
  386. struct device_node **allnextp = &allnodes;
  387. pr_debug(" -> unflatten_device_tree()\n");
  388. /* First pass, scan for size */
  389. start = ((unsigned long)initial_boot_params) +
  390. initial_boot_params->off_dt_struct;
  391. size = unflatten_dt_node(0, &start, NULL, NULL, 0);
  392. size = (size | 3) + 1;
  393. pr_debug(" size is %lx, allocating...\n", size);
  394. /* Allocate memory for the expanded device tree */
  395. mem = lmb_alloc(size + 4, __alignof__(struct device_node));
  396. mem = (unsigned long) __va(mem);
  397. ((u32 *)mem)[size / 4] = 0xdeadbeef;
  398. pr_debug(" unflattening %lx...\n", mem);
  399. /* Second pass, do actual unflattening */
  400. start = ((unsigned long)initial_boot_params) +
  401. initial_boot_params->off_dt_struct;
  402. unflatten_dt_node(mem, &start, NULL, &allnextp, 0);
  403. if (*((u32 *)start) != OF_DT_END)
  404. pr_warning("Weird tag at end of tree: %08x\n", *((u32 *)start));
  405. if (((u32 *)mem)[size / 4] != 0xdeadbeef)
  406. pr_warning("End of tree marker overwritten: %08x\n",
  407. ((u32 *)mem)[size / 4]);
  408. *allnextp = NULL;
  409. /* Get pointer to OF "/chosen" node for use everywhere */
  410. of_chosen = of_find_node_by_path("/chosen");
  411. if (of_chosen == NULL)
  412. of_chosen = of_find_node_by_path("/chosen@0");
  413. pr_debug(" <- unflatten_device_tree()\n");
  414. }