fdt.c 12 KB

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