cmd_fdt.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644
  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 < 4) {
  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. /*
  115. * If the user specifies a length, use that. Otherwise use the
  116. * current length.
  117. */
  118. if (argc <= 4) {
  119. len = fdt_totalsize(fdt);
  120. } else {
  121. len = simple_strtoul(argv[4], NULL, 16);
  122. if (len < fdt_totalsize(fdt)) {
  123. printf ("New length 0x%X < existing length 0x%X, aborting.\n",
  124. len, fdt_totalsize(fdt));
  125. return 1;
  126. }
  127. }
  128. /*
  129. * Copy to the new location.
  130. */
  131. err = fdt_open_into(fdt, newaddr, len);
  132. if (err != 0) {
  133. printf ("libfdt: %s\n", fdt_strerror(err));
  134. return 1;
  135. }
  136. fdt = newaddr;
  137. /********************************************************************
  138. * Set the value of a node in the fdt.
  139. ********************************************************************/
  140. } else if (op == 's') {
  141. char *pathp; /* path */
  142. char *prop; /* property */
  143. struct fdt_property *nodep; /* node struct pointer */
  144. char *newval; /* value from the user (as a string) */
  145. char *vp; /* temporary value pointer */
  146. char *cp; /* temporary char pointer */
  147. int nodeoffset; /* node offset from libfdt */
  148. int len; /* new length of the property */
  149. int oldlen; /* original length of the property */
  150. unsigned long tmp; /* holds converted values */
  151. int ret; /* return value */
  152. /*
  153. * Parameters: Node path, property, value.
  154. */
  155. if (argc < 5) {
  156. printf ("Usage:\n%s\n", cmdtp->usage);
  157. return 1;
  158. }
  159. pathp = argv[2];
  160. prop = argv[3];
  161. newval = argv[4];
  162. if (strcmp(pathp, "/") == 0) {
  163. nodeoffset = 0;
  164. } else {
  165. nodeoffset = fdt_path_offset (fdt, pathp);
  166. if (nodeoffset < 0) {
  167. /*
  168. * Not found or something else bad happened.
  169. */
  170. printf ("libfdt: %s\n", fdt_strerror(nodeoffset));
  171. return 1;
  172. }
  173. }
  174. nodep = fdt_getprop (fdt, nodeoffset, prop, &oldlen);
  175. if (oldlen < 0) {
  176. printf ("libfdt %s\n", fdt_strerror(oldlen));
  177. return 1;
  178. } else if (oldlen == 0) {
  179. /*
  180. * The specified property has no value
  181. */
  182. printf("%s has no value, cannot set one (yet).\n", prop);
  183. return 1;
  184. } else {
  185. /*
  186. * Convert the new property
  187. */
  188. vp = data;
  189. if (*newval == '<') {
  190. /*
  191. * Bigger values than bytes.
  192. */
  193. len = 0;
  194. newval++;
  195. while ((*newval != '>') && (*newval != '\0')) {
  196. cp = newval;
  197. tmp = simple_strtoul(cp, &newval, 16);
  198. if ((newval - cp) <= 2) {
  199. *vp = tmp & 0xFF;
  200. vp += 1;
  201. len += 1;
  202. } else if ((newval - cp) <= 4) {
  203. *(uint16_t *)vp = __cpu_to_be16(tmp);
  204. vp += 2;
  205. len += 2;
  206. } else if ((newval - cp) <= 8) {
  207. *(uint32_t *)vp = __cpu_to_be32(tmp);
  208. vp += 4;
  209. len += 4;
  210. } else {
  211. printf("Sorry, I could not convert \"%s\"\n", cp);
  212. return 1;
  213. }
  214. while (*newval == ' ')
  215. newval++;
  216. }
  217. if (*newval != '>') {
  218. printf("Unexpected character '%c'\n", *newval);
  219. return 1;
  220. }
  221. } else if (*newval == '[') {
  222. /*
  223. * Byte stream. Convert the values.
  224. */
  225. len = 0;
  226. newval++;
  227. while ((*newval != ']') && (*newval != '\0')) {
  228. tmp = simple_strtoul(newval, &newval, 16);
  229. *vp++ = tmp & 0xFF;
  230. len++;
  231. while (*newval == ' ')
  232. newval++;
  233. }
  234. if (*newval != ']') {
  235. printf("Unexpected character '%c'\n", *newval);
  236. return 1;
  237. }
  238. } else {
  239. /*
  240. * Assume it is a string. Copy it into our data area for
  241. * convenience (including the terminating '\0').
  242. */
  243. len = strlen(newval) + 1;
  244. strcpy(data, newval);
  245. }
  246. ret = fdt_setprop(fdt, nodeoffset, prop, data, len);
  247. if (ret < 0) {
  248. printf ("libfdt %s\n", fdt_strerror(ret));
  249. return 1;
  250. }
  251. }
  252. /********************************************************************
  253. * Print (recursive) / List (single level)
  254. ********************************************************************/
  255. } else if ((op == 'p') || (op == 'l')) {
  256. /*
  257. * Recursively print (a portion of) the fdt.
  258. */
  259. static int offstack[MAX_LEVEL];
  260. 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";
  261. int depth = MAX_LEVEL; /* how deep to print */
  262. char *pathp; /* path */
  263. char *prop; /* property */
  264. void *nodep; /* property node pointer */
  265. int nodeoffset; /* node offset from libfdt */
  266. int nextoffset; /* next node offset from libfdt */
  267. uint32_t tag; /* tag */
  268. int len; /* length of the property */
  269. int level = 0; /* keep track of nesting level */
  270. /*
  271. * list is an alias for print, but limited to 1 level
  272. */
  273. if (op == 'l') {
  274. depth = 1;
  275. }
  276. /*
  277. * Get the starting path. The root node is an oddball,
  278. * the offset is zero and has no name.
  279. */
  280. pathp = argv[2];
  281. if (argc > 3)
  282. prop = argv[3];
  283. else
  284. prop = NULL;
  285. if (strcmp(pathp, "/") == 0) {
  286. nodeoffset = 0;
  287. printf("/");
  288. } else {
  289. nodeoffset = fdt_path_offset (fdt, pathp);
  290. if (nodeoffset < 0) {
  291. /*
  292. * Not found or something else bad happened.
  293. */
  294. printf ("libfdt %s\n", fdt_strerror(nodeoffset));
  295. return 1;
  296. }
  297. }
  298. /*
  299. * The user passed in a property as well as node path. Print only
  300. * the given property and then return.
  301. */
  302. if (prop) {
  303. nodep = fdt_getprop (fdt, nodeoffset, prop, &len);
  304. if (len == 0) {
  305. printf("%s %s\n", pathp, prop); /* no property value */
  306. return 0;
  307. } else if (len > 0) {
  308. printf("%s=", prop);
  309. print_data (nodep, len);
  310. printf("\n");
  311. return 0;
  312. } else {
  313. printf ("libfdt %s\n", fdt_strerror(len));
  314. return 1;
  315. }
  316. }
  317. /*
  318. * The user passed in a node path and no property, print the node
  319. * and all subnodes.
  320. */
  321. offstack[0] = nodeoffset;
  322. while(level >= 0) {
  323. tag = fdt_next_tag(fdt, nodeoffset, &nextoffset, &pathp);
  324. switch(tag) {
  325. case FDT_BEGIN_NODE:
  326. if(level <= depth)
  327. printf("%s%s {\n", &tabs[MAX_LEVEL - level], pathp);
  328. level++;
  329. offstack[level] = nodeoffset;
  330. if (level >= MAX_LEVEL) {
  331. printf("Aaaiii <splat> nested too deep.\n");
  332. return 1;
  333. }
  334. break;
  335. case FDT_END_NODE:
  336. level--;
  337. if(level <= depth)
  338. printf("%s};\n", &tabs[MAX_LEVEL - level]);
  339. if (level == 0) {
  340. level = -1; /* exit the loop */
  341. }
  342. break;
  343. case FDT_PROP:
  344. nodep = fdt_getprop (fdt, offstack[level], pathp, &len);
  345. if (len < 0) {
  346. printf ("libfdt %s\n", fdt_strerror(len));
  347. return 1;
  348. } else if (len == 0) {
  349. /* the property has no value */
  350. if(level <= depth)
  351. printf("%s%s;\n", &tabs[MAX_LEVEL - level], pathp);
  352. } else {
  353. if(level <= depth) {
  354. printf("%s%s=", &tabs[MAX_LEVEL - level], pathp);
  355. print_data (nodep, len);
  356. printf(";\n");
  357. }
  358. }
  359. break;
  360. case FDT_NOP:
  361. break;
  362. case FDT_END:
  363. return 1;
  364. default:
  365. if(level <= depth)
  366. printf("Unknown tag 0x%08X\n", tag);
  367. return 1;
  368. }
  369. nodeoffset = nextoffset;
  370. }
  371. /********************************************************************
  372. * Remove a property/node
  373. ********************************************************************/
  374. } else if (op == 'r') {
  375. int nodeoffset; /* node offset from libfdt */
  376. int err;
  377. /*
  378. * Get the path. The root node is an oddball, the offset
  379. * is zero and has no name.
  380. */
  381. if (strcmp(argv[2], "/") == 0) {
  382. nodeoffset = 0;
  383. } else {
  384. nodeoffset = fdt_path_offset (fdt, argv[2]);
  385. if (nodeoffset < 0) {
  386. /*
  387. * Not found or something else bad happened.
  388. */
  389. printf ("libfdt %s\n", fdt_strerror(nodeoffset));
  390. return 1;
  391. }
  392. }
  393. /*
  394. * Do the delete. A fourth parameter means delete a property,
  395. * otherwise delete the node.
  396. */
  397. if (argc > 3) {
  398. err = fdt_delprop(fdt, nodeoffset, argv[3]);
  399. if (err < 0) {
  400. printf("fdt_delprop libfdt: %s\n", fdt_strerror(err));
  401. return err;
  402. }
  403. } else {
  404. err = fdt_del_node(fdt, nodeoffset);
  405. if (err < 0) {
  406. printf("fdt_del_node libfdt: %s\n", fdt_strerror(err));
  407. return err;
  408. }
  409. }
  410. /********************************************************************
  411. * Create a chosen node
  412. ********************************************************************/
  413. } else if (op == 'c') {
  414. fdt_chosen(fdt, 0, 0, 1);
  415. /********************************************************************
  416. * Create a u-boot-env node
  417. ********************************************************************/
  418. } else if (op == 'e') {
  419. fdt_env(fdt);
  420. /********************************************************************
  421. * Create a bd_t node
  422. ********************************************************************/
  423. } else if (op == 'b') {
  424. fdt_bd_t(fdt);
  425. /********************************************************************
  426. * Unrecognized command
  427. ********************************************************************/
  428. } else {
  429. printf ("Usage:\n%s\n", cmdtp->usage);
  430. return 1;
  431. }
  432. return 0;
  433. }
  434. /********************************************************************/
  435. static int fdt_valid(void)
  436. {
  437. int err;
  438. if (fdt == NULL) {
  439. printf ("The address of the fdt is invalid (NULL).\n");
  440. return 0;
  441. }
  442. err = fdt_check_header(fdt);
  443. if (err == 0)
  444. return 1; /* valid */
  445. if (err < 0) {
  446. printf("libfdt: %s", fdt_strerror(err));
  447. /*
  448. * Be more informative on bad version.
  449. */
  450. if (err == -FDT_ERR_BADVERSION) {
  451. if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) {
  452. printf (" - too old, fdt $d < %d",
  453. fdt_version(fdt), FDT_FIRST_SUPPORTED_VERSION);
  454. fdt = NULL;
  455. }
  456. if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION) {
  457. printf (" - too new, fdt $d > %d",
  458. fdt_version(fdt), FDT_LAST_SUPPORTED_VERSION);
  459. fdt = NULL;
  460. }
  461. return 0;
  462. }
  463. printf("\n");
  464. return 0;
  465. }
  466. return 1;
  467. }
  468. /********************************************************************/
  469. /*
  470. * OF flat tree handling
  471. * Written by: Pantelis Antoniou <pantelis.antoniou@gmail.com>
  472. * Updated by: Matthew McClintock <msm@freescale.com>
  473. * Converted to libfdt by: Gerald Van Baren <vanbaren@cideas.com>
  474. */
  475. static int is_printable_string(const void *data, int len)
  476. {
  477. const char *s = data;
  478. /* zero length is not */
  479. if (len == 0)
  480. return 0;
  481. /* must terminate with zero */
  482. if (s[len - 1] != '\0')
  483. return 0;
  484. /* printable or a null byte (concatenated strings) */
  485. while (((*s == '\0') || isprint(*s)) && (len > 0)) {
  486. /*
  487. * If we see a null, there are three possibilities:
  488. * 1) If len == 1, it is the end of the string, printable
  489. * 2) Next character also a null, not printable.
  490. * 3) Next character not a null, continue to check.
  491. */
  492. if (s[0] == '\0') {
  493. if (len == 1)
  494. return 1;
  495. if (s[1] == '\0')
  496. return 0;
  497. }
  498. s++;
  499. len--;
  500. }
  501. /* Not the null termination, or not done yet: not printable */
  502. if (*s != '\0' || (len != 0))
  503. return 0;
  504. return 1;
  505. }
  506. static void print_data(const void *data, int len)
  507. {
  508. int j;
  509. const u8 *s;
  510. /* no data, don't print */
  511. if (len == 0)
  512. return;
  513. /*
  514. * It is a string, but it may have multiple strings (embedded '\0's).
  515. */
  516. if (is_printable_string(data, len)) {
  517. puts("\"");
  518. j = 0;
  519. while (j < len) {
  520. if (j > 0)
  521. puts("\", \"");
  522. puts(data);
  523. j += strlen(data) + 1;
  524. data += strlen(data) + 1;
  525. }
  526. puts("\"");
  527. return;
  528. }
  529. switch (len) {
  530. case 1: /* byte */
  531. printf("<%02x>", (*(u8 *) data) & 0xff);
  532. break;
  533. case 2: /* half-word */
  534. printf("<%04x>", be16_to_cpu(*(u16 *) data) & 0xffff);
  535. break;
  536. case 4: /* word */
  537. printf("<%08x>", be32_to_cpu(*(u32 *) data) & 0xffffffffU);
  538. break;
  539. case 8: /* double-word */
  540. #if __WORDSIZE == 64
  541. printf("<%016llx>", be64_to_cpu(*(uint64_t *) data));
  542. #else
  543. printf("<%08x ", be32_to_cpu(*(u32 *) data) & 0xffffffffU);
  544. data += 4;
  545. printf("%08x>", be32_to_cpu(*(u32 *) data) & 0xffffffffU);
  546. #endif
  547. break;
  548. default: /* anything else... hexdump */
  549. printf("[");
  550. for (j = 0, s = data; j < len; j++)
  551. printf("%02x%s", s[j], j < len - 1 ? " " : "");
  552. printf("]");
  553. break;
  554. }
  555. }
  556. /********************************************************************/
  557. U_BOOT_CMD(
  558. fdt, 5, 0, do_fdt,
  559. "fdt - flattened device tree utility commands\n",
  560. "addr <addr> [<length>] - Set the fdt location to <addr>\n"
  561. "fdt move <fdt> <newaddr> <length> - Copy the fdt to <addr>\n"
  562. "fdt print <path> [<prop>] - Recursive print starting at <path>\n"
  563. "fdt list <path> [<prop>] - Print one level starting at <path>\n"
  564. "fdt set <path> <prop> [<val>] - Set <property> [to <val>]\n"
  565. "fdt mknode <path> <node> - Create a new node after <path>\n"
  566. "fdt rm <path> [<prop>] - Delete the node or <property>\n"
  567. "fdt chosen - Add/update the \"/chosen\" branch in the tree\n"
  568. #ifdef CONFIG_OF_HAS_UBOOT_ENV
  569. "fdt env - Add/replace the \"/u-boot-env\" branch in the tree\n"
  570. #endif
  571. #ifdef CONFIG_OF_HAS_BD_T
  572. "fdt bd_t - Add/replace the \"/bd_t\" branch in the tree\n"
  573. #endif
  574. "Hints:\n"
  575. " * Set a larger length with the fdt addr command to add to the blob.\n"
  576. " * If the property you are setting/printing has a '#' character,\n"
  577. " you MUST escape it with a \\ character or quote it with \" or\n"
  578. " it will be ignored as a comment.\n"
  579. " * If the value has spaces in it, you MUST escape the spaces with\n"
  580. " \\ characters or quote it with \"\"\n"
  581. "Examples: fdt print / # print the whole tree\n"
  582. " fdt print /cpus \"#address-cells\"\n"
  583. " fdt set /cpus \"#address-cells\" \"[00 00 00 01]\"\n"
  584. );
  585. #endif /* CONFIG_OF_LIBFDT */