cmd_fdt.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635
  1. /*
  2. * (C) Copyright 2007
  3. * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com
  4. * Based on code written by:
  5. * Pantelis Antoniou <pantelis.antoniou@gmail.com> and
  6. * Matthew McClintock <msm@freescale.com>
  7. *
  8. * See file CREDITS for list of people who contributed to this
  9. * project.
  10. *
  11. * This program is free software; you can redistribute it and/or
  12. * modify it under the terms of the GNU General Public License as
  13. * published by the Free Software Foundation; either version 2 of
  14. * the License, or (at your option) any later version.
  15. *
  16. * This program is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. * GNU General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU General Public License
  22. * along with this program; if not, write to the Free Software
  23. * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  24. * MA 02111-1307 USA
  25. */
  26. #include <common.h>
  27. #include <command.h>
  28. #include <linux/ctype.h>
  29. #include <linux/types.h>
  30. #ifdef CONFIG_OF_LIBFDT
  31. #include <asm/global_data.h>
  32. #include <fdt.h>
  33. #include <libfdt.h>
  34. #include <fdt_support.h>
  35. #define MAX_LEVEL 32 /* how deeply nested we will go */
  36. #define SCRATCHPAD 1024 /* bytes of scratchpad memory */
  37. /*
  38. * Global data (for the gd->bd)
  39. */
  40. DECLARE_GLOBAL_DATA_PTR;
  41. /*
  42. * Scratchpad memory.
  43. */
  44. static char data[SCRATCHPAD];
  45. /*
  46. * Function prototypes/declarations.
  47. */
  48. static int fdt_valid(void);
  49. static void print_data(const void *data, int len);
  50. /*
  51. * Flattened Device Tree command, see the help for parameter definitions.
  52. */
  53. int do_fdt (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
  54. {
  55. char op;
  56. if (argc < 2) {
  57. printf ("Usage:\n%s\n", cmdtp->usage);
  58. return 1;
  59. }
  60. /*
  61. * Figure out which subcommand was given
  62. */
  63. op = argv[1][0];
  64. /********************************************************************
  65. * Set the address of the fdt
  66. ********************************************************************/
  67. if (op == 'a') {
  68. /*
  69. * Set the address [and length] of the fdt.
  70. */
  71. fdt = (struct fdt_header *)simple_strtoul(argv[2], NULL, 16);
  72. if (!fdt_valid()) {
  73. return 1;
  74. }
  75. if (argc >= 4) {
  76. int len;
  77. int err;
  78. /*
  79. * Optional new length
  80. */
  81. len = simple_strtoul(argv[3], NULL, 16);
  82. if (len < fdt_totalsize(fdt)) {
  83. printf ("New length %d < existing length %d, ignoring.\n",
  84. len, fdt_totalsize(fdt));
  85. } else {
  86. /*
  87. * Open in place with a new length.
  88. */
  89. err = fdt_open_into(fdt, fdt, len);
  90. if (err != 0) {
  91. printf ("libfdt: %s\n", fdt_strerror(err));
  92. }
  93. }
  94. }
  95. /********************************************************************
  96. * Move the fdt
  97. ********************************************************************/
  98. } else if (op == 'm') {
  99. struct fdt_header *newaddr;
  100. int len;
  101. int err;
  102. if (argc != 5) {
  103. printf ("Usage:\n%s\n", cmdtp->usage);
  104. return 1;
  105. }
  106. /*
  107. * Set the address and length of the fdt.
  108. */
  109. fdt = (struct fdt_header *)simple_strtoul(argv[2], NULL, 16);
  110. if (!fdt_valid()) {
  111. return 1;
  112. }
  113. newaddr = (struct fdt_header *)simple_strtoul(argv[3], NULL, 16);
  114. len = simple_strtoul(argv[4], NULL, 16);
  115. if (len < fdt_totalsize(fdt)) {
  116. printf ("New length %d < existing length %d, aborting.\n",
  117. len, fdt_totalsize(fdt));
  118. return 1;
  119. }
  120. /*
  121. * Copy to the new location.
  122. */
  123. err = fdt_open_into(fdt, newaddr, len);
  124. if (err != 0) {
  125. printf ("libfdt: %s\n", fdt_strerror(err));
  126. return 1;
  127. }
  128. fdt = newaddr;
  129. /********************************************************************
  130. * Set the value of a node in the fdt.
  131. ********************************************************************/
  132. } else if (op == 's') {
  133. char *pathp; /* path */
  134. char *prop; /* property */
  135. struct fdt_property *nodep; /* node struct pointer */
  136. char *newval; /* value from the user (as a string) */
  137. char *vp; /* temporary value pointer */
  138. char *cp; /* temporary char pointer */
  139. int nodeoffset; /* node offset from libfdt */
  140. int len; /* new length of the property */
  141. int oldlen; /* original length of the property */
  142. unsigned long tmp; /* holds converted values */
  143. int ret; /* return value */
  144. /*
  145. * Parameters: Node path, property, value.
  146. */
  147. if (argc < 5) {
  148. printf ("Usage:\n%s\n", cmdtp->usage);
  149. return 1;
  150. }
  151. pathp = argv[2];
  152. prop = argv[3];
  153. newval = argv[4];
  154. if (strcmp(pathp, "/") == 0) {
  155. nodeoffset = 0;
  156. } else {
  157. nodeoffset = fdt_path_offset (fdt, pathp);
  158. if (nodeoffset < 0) {
  159. /*
  160. * Not found or something else bad happened.
  161. */
  162. printf ("libfdt: %s\n", fdt_strerror(nodeoffset));
  163. return 1;
  164. }
  165. }
  166. nodep = fdt_getprop (fdt, nodeoffset, prop, &oldlen);
  167. if (oldlen < 0) {
  168. printf ("libfdt %s\n", fdt_strerror(oldlen));
  169. return 1;
  170. } else if (oldlen == 0) {
  171. /*
  172. * The specified property has no value
  173. */
  174. printf("%s has no value, cannot set one (yet).\n", prop);
  175. return 1;
  176. } else {
  177. /*
  178. * Convert the new property
  179. */
  180. vp = data;
  181. if (*newval == '<') {
  182. /*
  183. * Bigger values than bytes.
  184. */
  185. len = 0;
  186. newval++;
  187. while ((*newval != '>') && (*newval != '\0')) {
  188. cp = newval;
  189. tmp = simple_strtoul(cp, &newval, 16);
  190. if ((newval - cp) <= 2) {
  191. *vp = tmp & 0xFF;
  192. vp += 1;
  193. len += 1;
  194. } else if ((newval - cp) <= 4) {
  195. *(uint16_t *)vp = __cpu_to_be16(tmp);
  196. vp += 2;
  197. len += 2;
  198. } else if ((newval - cp) <= 8) {
  199. *(uint32_t *)vp = __cpu_to_be32(tmp);
  200. vp += 4;
  201. len += 4;
  202. } else {
  203. printf("Sorry, I could not convert \"%s\"\n", cp);
  204. return 1;
  205. }
  206. while (*newval == ' ')
  207. newval++;
  208. }
  209. if (*newval != '>') {
  210. printf("Unexpected character '%c'\n", *newval);
  211. return 1;
  212. }
  213. } else if (*newval == '[') {
  214. /*
  215. * Byte stream. Convert the values.
  216. */
  217. len = 0;
  218. newval++;
  219. while ((*newval != ']') && (*newval != '\0')) {
  220. tmp = simple_strtoul(newval, &newval, 16);
  221. *vp++ = tmp & 0xFF;
  222. len++;
  223. while (*newval == ' ')
  224. newval++;
  225. }
  226. if (*newval != ']') {
  227. printf("Unexpected character '%c'\n", *newval);
  228. return 1;
  229. }
  230. } else {
  231. /*
  232. * Assume it is a string. Copy it into our data area for
  233. * convenience (including the terminating '\0').
  234. */
  235. len = strlen(newval) + 1;
  236. strcpy(data, newval);
  237. }
  238. ret = fdt_setprop(fdt, nodeoffset, prop, data, len);
  239. if (ret < 0) {
  240. printf ("libfdt %s\n", fdt_strerror(ret));
  241. return 1;
  242. }
  243. }
  244. /********************************************************************
  245. * Print (recursive) / List (single level)
  246. ********************************************************************/
  247. } else if ((op == 'p') || (op == 'l')) {
  248. /*
  249. * Recursively print (a portion of) the fdt.
  250. */
  251. static int offstack[MAX_LEVEL];
  252. static char tabs[MAX_LEVEL+1] = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
  253. int depth = MAX_LEVEL; /* how deep to print */
  254. char *pathp; /* path */
  255. char *prop; /* property */
  256. void *nodep; /* property node pointer */
  257. int nodeoffset; /* node offset from libfdt */
  258. int nextoffset; /* next node offset from libfdt */
  259. uint32_t tag; /* tag */
  260. int len; /* length of the property */
  261. int level = 0; /* keep track of nesting level */
  262. /*
  263. * list is an alias for print, but limited to 1 level
  264. */
  265. if (op == 'l') {
  266. depth = 1;
  267. }
  268. /*
  269. * Get the starting path. The root node is an oddball,
  270. * the offset is zero and has no name.
  271. */
  272. pathp = argv[2];
  273. if (argc > 3)
  274. prop = argv[3];
  275. else
  276. prop = NULL;
  277. if (strcmp(pathp, "/") == 0) {
  278. nodeoffset = 0;
  279. printf("/");
  280. } else {
  281. nodeoffset = fdt_path_offset (fdt, pathp);
  282. if (nodeoffset < 0) {
  283. /*
  284. * Not found or something else bad happened.
  285. */
  286. printf ("libfdt %s\n", fdt_strerror(nodeoffset));
  287. return 1;
  288. }
  289. }
  290. /*
  291. * The user passed in a property as well as node path. Print only
  292. * the given property and then return.
  293. */
  294. if (prop) {
  295. nodep = fdt_getprop (fdt, nodeoffset, prop, &len);
  296. if (len == 0) {
  297. printf("%s %s\n", pathp, prop); /* no property value */
  298. return 0;
  299. } else if (len > 0) {
  300. printf("%s=", prop);
  301. print_data (nodep, len);
  302. printf("\n");
  303. return 0;
  304. } else {
  305. printf ("libfdt %s\n", fdt_strerror(len));
  306. return 1;
  307. }
  308. }
  309. /*
  310. * The user passed in a node path and no property, print the node
  311. * and all subnodes.
  312. */
  313. offstack[0] = nodeoffset;
  314. while(level >= 0) {
  315. tag = fdt_next_tag(fdt, nodeoffset, &nextoffset, &pathp);
  316. switch(tag) {
  317. case FDT_BEGIN_NODE:
  318. if(level <= depth)
  319. printf("%s%s {\n", &tabs[MAX_LEVEL - level], pathp);
  320. level++;
  321. offstack[level] = nodeoffset;
  322. if (level >= MAX_LEVEL) {
  323. printf("Aaaiii <splat> nested too deep.\n");
  324. return 1;
  325. }
  326. break;
  327. case FDT_END_NODE:
  328. level--;
  329. if(level <= depth)
  330. printf("%s};\n", &tabs[MAX_LEVEL - level]);
  331. if (level == 0) {
  332. level = -1; /* exit the loop */
  333. }
  334. break;
  335. case FDT_PROP:
  336. nodep = fdt_getprop (fdt, offstack[level], pathp, &len);
  337. if (len < 0) {
  338. printf ("libfdt %s\n", fdt_strerror(len));
  339. return 1;
  340. } else if (len == 0) {
  341. /* the property has no value */
  342. if(level <= depth)
  343. printf("%s%s;\n", &tabs[MAX_LEVEL - level], pathp);
  344. } else {
  345. if(level <= depth) {
  346. printf("%s%s=", &tabs[MAX_LEVEL - level], pathp);
  347. print_data (nodep, len);
  348. printf(";\n");
  349. }
  350. }
  351. break;
  352. case FDT_NOP:
  353. break;
  354. case FDT_END:
  355. return 1;
  356. default:
  357. if(level <= depth)
  358. printf("Unknown tag 0x%08X\n", tag);
  359. return 1;
  360. }
  361. nodeoffset = nextoffset;
  362. }
  363. /********************************************************************
  364. * Remove a property/node
  365. ********************************************************************/
  366. } else if (op == 'r') {
  367. int nodeoffset; /* node offset from libfdt */
  368. int err;
  369. /*
  370. * Get the path. The root node is an oddball, the offset
  371. * is zero and has no name.
  372. */
  373. if (strcmp(argv[2], "/") == 0) {
  374. nodeoffset = 0;
  375. } else {
  376. nodeoffset = fdt_path_offset (fdt, argv[2]);
  377. if (nodeoffset < 0) {
  378. /*
  379. * Not found or something else bad happened.
  380. */
  381. printf ("libfdt %s\n", fdt_strerror(nodeoffset));
  382. return 1;
  383. }
  384. }
  385. /*
  386. * Do the delete. A fourth parameter means delete a property,
  387. * otherwise delete the node.
  388. */
  389. if (argc > 3) {
  390. err = fdt_delprop(fdt, nodeoffset, argv[3]);
  391. if (err < 0) {
  392. printf("fdt_delprop libfdt: %s\n", fdt_strerror(err));
  393. return err;
  394. }
  395. } else {
  396. err = fdt_del_node(fdt, nodeoffset);
  397. if (err < 0) {
  398. printf("fdt_del_node libfdt: %s\n", fdt_strerror(err));
  399. return err;
  400. }
  401. }
  402. /********************************************************************
  403. * Create a chosen node
  404. ********************************************************************/
  405. } else if (op == 'c') {
  406. fdt_chosen(fdt, 0, 0, 1);
  407. /********************************************************************
  408. * Create a u-boot-env node
  409. ********************************************************************/
  410. } else if (op == 'e') {
  411. fdt_env(fdt);
  412. /********************************************************************
  413. * Create a bd_t node
  414. ********************************************************************/
  415. } else if (op == 'b') {
  416. fdt_bd_t(fdt);
  417. /********************************************************************
  418. * Unrecognized command
  419. ********************************************************************/
  420. } else {
  421. printf ("Usage:\n%s\n", cmdtp->usage);
  422. return 1;
  423. }
  424. return 0;
  425. }
  426. /********************************************************************/
  427. static int fdt_valid(void)
  428. {
  429. int err;
  430. if (fdt == NULL) {
  431. printf ("The address of the fdt is invalid (NULL).\n");
  432. return 0;
  433. }
  434. err = fdt_check_header(fdt);
  435. if (err == 0)
  436. return 1; /* valid */
  437. if (err < 0) {
  438. printf("libfdt: %s", fdt_strerror(err));
  439. /*
  440. * Be more informative on bad version.
  441. */
  442. if (err == -FDT_ERR_BADVERSION) {
  443. if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) {
  444. printf (" - too old, fdt $d < %d",
  445. fdt_version(fdt), FDT_FIRST_SUPPORTED_VERSION);
  446. fdt = NULL;
  447. }
  448. if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION) {
  449. printf (" - too new, fdt $d > %d",
  450. fdt_version(fdt), FDT_LAST_SUPPORTED_VERSION);
  451. fdt = NULL;
  452. }
  453. return 0;
  454. }
  455. printf("\n");
  456. return 0;
  457. }
  458. return 1;
  459. }
  460. /********************************************************************/
  461. /*
  462. * OF flat tree handling
  463. * Written by: Pantelis Antoniou <pantelis.antoniou@gmail.com>
  464. * Updated by: Matthew McClintock <msm@freescale.com>
  465. * Converted to libfdt by: Gerald Van Baren <vanbaren@cideas.com>
  466. */
  467. static int is_printable_string(const void *data, int len)
  468. {
  469. const char *s = data;
  470. /* zero length is not */
  471. if (len == 0)
  472. return 0;
  473. /* must terminate with zero */
  474. if (s[len - 1] != '\0')
  475. return 0;
  476. /* printable or a null byte (concatenated strings) */
  477. while (((*s == '\0') || isprint(*s)) && (len > 0)) {
  478. /*
  479. * If we see a null, there are three possibilities:
  480. * 1) If len == 1, it is the end of the string, printable
  481. * 2) Next character also a null, not printable.
  482. * 3) Next character not a null, continue to check.
  483. */
  484. if (s[0] == '\0') {
  485. if (len == 1)
  486. return 1;
  487. if (s[1] == '\0')
  488. return 0;
  489. }
  490. s++;
  491. len--;
  492. }
  493. /* Not the null termination, or not done yet: not printable */
  494. if (*s != '\0' || (len != 0))
  495. return 0;
  496. return 1;
  497. }
  498. static void print_data(const void *data, int len)
  499. {
  500. int j;
  501. const u8 *s;
  502. /* no data, don't print */
  503. if (len == 0)
  504. return;
  505. /*
  506. * It is a string, but it may have multiple strings (embedded '\0's).
  507. */
  508. if (is_printable_string(data, len)) {
  509. puts("\"");
  510. j = 0;
  511. while (j < len) {
  512. if (j > 0)
  513. puts("\", \"");
  514. puts(data);
  515. j += strlen(data) + 1;
  516. data += strlen(data) + 1;
  517. }
  518. puts("\"");
  519. return;
  520. }
  521. switch (len) {
  522. case 1: /* byte */
  523. printf("<%02x>", (*(u8 *) data) & 0xff);
  524. break;
  525. case 2: /* half-word */
  526. printf("<%04x>", be16_to_cpu(*(u16 *) data) & 0xffff);
  527. break;
  528. case 4: /* word */
  529. printf("<%08x>", be32_to_cpu(*(u32 *) data) & 0xffffffffU);
  530. break;
  531. case 8: /* double-word */
  532. #if __WORDSIZE == 64
  533. printf("<%016llx>", be64_to_cpu(*(uint64_t *) data));
  534. #else
  535. printf("<%08x ", be32_to_cpu(*(u32 *) data) & 0xffffffffU);
  536. data += 4;
  537. printf("%08x>", be32_to_cpu(*(u32 *) data) & 0xffffffffU);
  538. #endif
  539. break;
  540. default: /* anything else... hexdump */
  541. printf("[");
  542. for (j = 0, s = data; j < len; j++)
  543. printf("%02x%s", s[j], j < len - 1 ? " " : "");
  544. printf("]");
  545. break;
  546. }
  547. }
  548. /********************************************************************/
  549. U_BOOT_CMD(
  550. fdt, 5, 0, do_fdt,
  551. "fdt - flattened device tree utility commands\n",
  552. "addr <addr> [<length>] - Set the fdt location to <addr>\n"
  553. "fdt move <fdt> <newaddr> <length> - Copy the fdt to <addr>\n"
  554. "fdt print <path> [<prop>] - Recursive print starting at <path>\n"
  555. "fdt list <path> [<prop>] - Print one level starting at <path>\n"
  556. "fdt set <path> <prop> [<val>] - Set <property> [to <val>]\n"
  557. "fdt mknode <path> <node> - Create a new node after <path>\n"
  558. "fdt rm <path> [<prop>] - Delete the node or <property>\n"
  559. "fdt chosen - Add/update the \"/chosen\" branch in the tree\n"
  560. #ifdef CONFIG_OF_HAS_UBOOT_ENV
  561. "fdt env - Add/replace the \"/u-boot-env\" branch in the tree\n"
  562. #endif
  563. #ifdef CONFIG_OF_HAS_BD_T
  564. "fdt bd_t - Add/replace the \"/bd_t\" branch in the tree\n"
  565. #endif
  566. "Hints:\n"
  567. " * Set a larger length with the fdt addr command to add to the blob.\n"
  568. " * If the property you are setting/printing has a '#' character,\n"
  569. " you MUST escape it with a \\ character or quote it with \" or\n"
  570. " it will be ignored as a comment.\n"
  571. " * If the value has spaces in it, you MUST escape the spaces with\n"
  572. " \\ characters or quote it with \"\"\n"
  573. "Examples: fdt print / # print the whole tree\n"
  574. " fdt print /cpus \"#address-cells\"\n"
  575. " fdt set /cpus \"#address-cells\" \"[00 00 00 01]\"\n"
  576. );
  577. #endif /* CONFIG_OF_LIBFDT */