]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - common/cmd_fdt.c
fdt: Allow fdt command to check and update control FDT
[karo-tx-uboot.git] / common / cmd_fdt.c
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
27 #include <common.h>
28 #include <command.h>
29 #include <linux/ctype.h>
30 #include <linux/types.h>
31 #include <asm/global_data.h>
32 #include <libfdt.h>
33 #include <fdt_support.h>
34
35 #define MAX_LEVEL       32              /* how deeply nested we will go */
36 #define SCRATCHPAD      1024            /* bytes of scratchpad memory */
37 #ifndef CONFIG_CMD_FDT_MAX_DUMP
38 #define CONFIG_CMD_FDT_MAX_DUMP 64
39 #endif
40
41 /*
42  * Global data (for the gd->bd)
43  */
44 DECLARE_GLOBAL_DATA_PTR;
45
46 static int fdt_valid(struct fdt_header **blobp);
47 static int fdt_parse_prop(char *const*newval, int count, char *data, int *len);
48 static int fdt_print(const char *pathp, char *prop, int depth);
49 static int is_printable_string(const void *data, int len);
50
51 /*
52  * The working_fdt points to our working flattened device tree.
53  */
54 struct fdt_header *working_fdt;
55
56 void set_working_fdt_addr(void *addr)
57 {
58         working_fdt = addr;
59         setenv_addr("fdtaddr", addr);
60 }
61
62 /*
63  * Get a value from the fdt and format it to be set in the environment
64  */
65 static int fdt_value_setenv(const void *nodep, int len, const char *var)
66 {
67         if (is_printable_string(nodep, len))
68                 setenv(var, (void *)nodep);
69         else if (len == 4) {
70                 char buf[11];
71
72                 sprintf(buf, "0x%08X", *(uint32_t *)nodep);
73                 setenv(var, buf);
74         } else if (len%4 == 0 && len <= 20) {
75                 /* Needed to print things like sha1 hashes. */
76                 char buf[41];
77                 int i;
78
79                 for (i = 0; i < len; i += sizeof(unsigned int))
80                         sprintf(buf + (i * 2), "%08x",
81                                 *(unsigned int *)(nodep + i));
82                 setenv(var, buf);
83         } else {
84                 printf("error: unprintable value\n");
85                 return 1;
86         }
87         return 0;
88 }
89
90 /*
91  * Flattened Device Tree command, see the help for parameter definitions.
92  */
93 static int do_fdt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
94 {
95         if (argc < 2)
96                 return CMD_RET_USAGE;
97
98         /*
99          * Set the address of the fdt
100          */
101         if (argv[1][0] == 'a') {
102                 unsigned long addr;
103                 int control = 0;
104                 struct fdt_header *blob;
105                 /*
106                  * Set the address [and length] of the fdt.
107                  */
108                 argc -= 2;
109                 argv += 2;
110 /* Temporary #ifdef - some archs don't have fdt_blob yet */
111 #ifdef CONFIG_OF_CONTROL
112                 if (argc && !strcmp(*argv, "-c")) {
113                         control = 1;
114                         argc--;
115                         argv++;
116                 }
117 #endif
118                 if (argc == 0) {
119                         if (control)
120                                 blob = (struct fdt_header *)gd->fdt_blob;
121                         else
122                                 blob = working_fdt;
123                         if (!blob || !fdt_valid(&blob))
124                                 return 1;
125                         printf("The address of the fdt is %#08lx\n",
126                                control ? (ulong)blob :
127                                         getenv_hex("fdtaddr", 0));
128                         return 0;
129                 }
130
131                 addr = simple_strtoul(argv[0], NULL, 16);
132                 blob = (struct fdt_header *)addr;
133                 if (!fdt_valid(&blob))
134                         return 1;
135                 if (control)
136                         gd->fdt_blob = blob;
137                 else
138                         set_working_fdt_addr((void *)addr);
139
140                 if (argc >= 2) {
141                         int  len;
142                         int  err;
143                         /*
144                          * Optional new length
145                          */
146                         len = simple_strtoul(argv[1], NULL, 16);
147                         if (len < fdt_totalsize(blob)) {
148                                 printf ("New length %d < existing length %d, "
149                                         "ignoring.\n",
150                                         len, fdt_totalsize(blob));
151                         } else {
152                                 /*
153                                  * Open in place with a new length.
154                                  */
155                                 err = fdt_open_into(blob, blob, len);
156                                 if (err != 0) {
157                                         printf ("libfdt fdt_open_into(): %s\n",
158                                                 fdt_strerror(err));
159                                 }
160                         }
161                 }
162
163                 return CMD_RET_SUCCESS;
164         }
165
166         if (!working_fdt) {
167                 puts(
168                         "No FDT memory address configured. Please configure\n"
169                         "the FDT address via \"fdt addr <address>\" command.\n"
170                         "Aborting!\n");
171                 return CMD_RET_FAILURE;
172         }
173
174         /*
175          * Move the working_fdt
176          */
177         if (strncmp(argv[1], "mo", 2) == 0) {
178                 struct fdt_header *newaddr;
179                 int  len;
180                 int  err;
181
182                 if (argc < 4)
183                         return CMD_RET_USAGE;
184
185                 /*
186                  * Set the address and length of the fdt.
187                  */
188                 working_fdt = (struct fdt_header *)simple_strtoul(argv[2], NULL, 16);
189                 if (!fdt_valid(&working_fdt))
190                         return 1;
191
192                 newaddr = (struct fdt_header *)simple_strtoul(argv[3],NULL,16);
193
194                 /*
195                  * If the user specifies a length, use that.  Otherwise use the
196                  * current length.
197                  */
198                 if (argc <= 4) {
199                         len = fdt_totalsize(working_fdt);
200                 } else {
201                         len = simple_strtoul(argv[4], NULL, 16);
202                         if (len < fdt_totalsize(working_fdt)) {
203                                 printf ("New length 0x%X < existing length "
204                                         "0x%X, aborting.\n",
205                                         len, fdt_totalsize(working_fdt));
206                                 return 1;
207                         }
208                 }
209
210                 /*
211                  * Copy to the new location.
212                  */
213                 err = fdt_open_into(working_fdt, newaddr, len);
214                 if (err != 0) {
215                         printf ("libfdt fdt_open_into(): %s\n",
216                                 fdt_strerror(err));
217                         return 1;
218                 }
219                 working_fdt = newaddr;
220
221         /*
222          * Make a new node
223          */
224         } else if (strncmp(argv[1], "mk", 2) == 0) {
225                 char *pathp;            /* path */
226                 char *nodep;            /* new node to add */
227                 int  nodeoffset;        /* node offset from libfdt */
228                 int  err;
229
230                 /*
231                  * Parameters: Node path, new node to be appended to the path.
232                  */
233                 if (argc < 4)
234                         return CMD_RET_USAGE;
235
236                 pathp = argv[2];
237                 nodep = argv[3];
238
239                 nodeoffset = fdt_path_offset (working_fdt, pathp);
240                 if (nodeoffset < 0) {
241                         /*
242                          * Not found or something else bad happened.
243                          */
244                         printf ("libfdt fdt_path_offset() returned %s\n",
245                                 fdt_strerror(nodeoffset));
246                         return 1;
247                 }
248                 err = fdt_add_subnode(working_fdt, nodeoffset, nodep);
249                 if (err < 0) {
250                         printf ("libfdt fdt_add_subnode(): %s\n",
251                                 fdt_strerror(err));
252                         return 1;
253                 }
254
255         /*
256          * Set the value of a property in the working_fdt.
257          */
258         } else if (argv[1][0] == 's') {
259                 char *pathp;            /* path */
260                 char *prop;             /* property */
261                 int  nodeoffset;        /* node offset from libfdt */
262                 static char data[SCRATCHPAD];   /* storage for the property */
263                 int  len;               /* new length of the property */
264                 int  ret;               /* return value */
265
266                 /*
267                  * Parameters: Node path, property, optional value.
268                  */
269                 if (argc < 4)
270                         return CMD_RET_USAGE;
271
272                 pathp  = argv[2];
273                 prop   = argv[3];
274                 if (argc == 4) {
275                         len = 0;
276                 } else {
277                         ret = fdt_parse_prop(&argv[4], argc - 4, data, &len);
278                         if (ret != 0)
279                                 return ret;
280                 }
281
282                 nodeoffset = fdt_path_offset (working_fdt, pathp);
283                 if (nodeoffset < 0) {
284                         /*
285                          * Not found or something else bad happened.
286                          */
287                         printf ("libfdt fdt_path_offset() returned %s\n",
288                                 fdt_strerror(nodeoffset));
289                         return 1;
290                 }
291
292                 ret = fdt_setprop(working_fdt, nodeoffset, prop, data, len);
293                 if (ret < 0) {
294                         printf ("libfdt fdt_setprop(): %s\n", fdt_strerror(ret));
295                         return 1;
296                 }
297
298         /********************************************************************
299          * Get the value of a property in the working_fdt.
300          ********************************************************************/
301         } else if (argv[1][0] == 'g') {
302                 char *subcmd;           /* sub-command */
303                 char *pathp;            /* path */
304                 char *prop;             /* property */
305                 char *var;              /* variable to store result */
306                 int  nodeoffset;        /* node offset from libfdt */
307                 const void *nodep;      /* property node pointer */
308                 int  len = 0;           /* new length of the property */
309
310                 /*
311                  * Parameters: Node path, property, optional value.
312                  */
313                 if (argc < 5)
314                         return CMD_RET_USAGE;
315
316                 subcmd = argv[2];
317
318                 if (argc < 6 && subcmd[0] != 's')
319                         return CMD_RET_USAGE;
320
321                 var    = argv[3];
322                 pathp  = argv[4];
323                 prop   = argv[5];
324
325                 nodeoffset = fdt_path_offset(working_fdt, pathp);
326                 if (nodeoffset < 0) {
327                         /*
328                          * Not found or something else bad happened.
329                          */
330                         printf("libfdt fdt_path_offset() returned %s\n",
331                                 fdt_strerror(nodeoffset));
332                         return 1;
333                 }
334
335                 if (subcmd[0] == 'n' || (subcmd[0] == 's' && argc == 5)) {
336                         int reqIndex = -1;
337                         int startDepth = fdt_node_depth(
338                                 working_fdt, nodeoffset);
339                         int curDepth = startDepth;
340                         int curIndex = -1;
341                         int nextNodeOffset = fdt_next_node(
342                                 working_fdt, nodeoffset, &curDepth);
343
344                         if (subcmd[0] == 'n')
345                                 reqIndex = simple_strtoul(argv[5], NULL, 16);
346
347                         while (curDepth > startDepth) {
348                                 if (curDepth == startDepth + 1)
349                                         curIndex++;
350                                 if (subcmd[0] == 'n' && curIndex == reqIndex) {
351                                         const char *nodeName = fdt_get_name(
352                                             working_fdt, nextNodeOffset, NULL);
353
354                                         setenv(var, (char *)nodeName);
355                                         return 0;
356                                 }
357                                 nextNodeOffset = fdt_next_node(
358                                         working_fdt, nextNodeOffset, &curDepth);
359                                 if (nextNodeOffset < 0)
360                                         break;
361                         }
362                         if (subcmd[0] == 's') {
363                                 /* get the num nodes at this level */
364                                 setenv_ulong(var, curIndex + 1);
365                         } else {
366                                 /* node index not found */
367                                 printf("libfdt node not found\n");
368                                 return 1;
369                         }
370                 } else {
371                         nodep = fdt_getprop(
372                                 working_fdt, nodeoffset, prop, &len);
373                         if (len == 0) {
374                                 /* no property value */
375                                 setenv(var, "");
376                                 return 0;
377                         } else if (len > 0) {
378                                 if (subcmd[0] == 'v') {
379                                         int ret;
380
381                                         ret = fdt_value_setenv(nodep, len, var);
382                                         if (ret != 0)
383                                                 return ret;
384                                 } else if (subcmd[0] == 'a') {
385                                         /* Get address */
386                                         char buf[11];
387
388                                         sprintf(buf, "0x%p", nodep);
389                                         setenv(var, buf);
390                                 } else if (subcmd[0] == 's') {
391                                         /* Get size */
392                                         char buf[11];
393
394                                         sprintf(buf, "0x%08X", len);
395                                         setenv(var, buf);
396                                 } else
397                                         return CMD_RET_USAGE;
398                                 return 0;
399                         } else {
400                                 printf("libfdt fdt_getprop(): %s\n",
401                                         fdt_strerror(len));
402                                 return 1;
403                         }
404                 }
405
406         /*
407          * Print (recursive) / List (single level)
408          */
409         } else if ((argv[1][0] == 'p') || (argv[1][0] == 'l')) {
410                 int depth = MAX_LEVEL;  /* how deep to print */
411                 char *pathp;            /* path */
412                 char *prop;             /* property */
413                 int  ret;               /* return value */
414                 static char root[2] = "/";
415
416                 /*
417                  * list is an alias for print, but limited to 1 level
418                  */
419                 if (argv[1][0] == 'l') {
420                         depth = 1;
421                 }
422
423                 /*
424                  * Get the starting path.  The root node is an oddball,
425                  * the offset is zero and has no name.
426                  */
427                 if (argc == 2)
428                         pathp = root;
429                 else
430                         pathp = argv[2];
431                 if (argc > 3)
432                         prop = argv[3];
433                 else
434                         prop = NULL;
435
436                 ret = fdt_print(pathp, prop, depth);
437                 if (ret != 0)
438                         return ret;
439
440         /*
441          * Remove a property/node
442          */
443         } else if (strncmp(argv[1], "rm", 2) == 0) {
444                 int  nodeoffset;        /* node offset from libfdt */
445                 int  err;
446
447                 /*
448                  * Get the path.  The root node is an oddball, the offset
449                  * is zero and has no name.
450                  */
451                 nodeoffset = fdt_path_offset (working_fdt, argv[2]);
452                 if (nodeoffset < 0) {
453                         /*
454                          * Not found or something else bad happened.
455                          */
456                         printf ("libfdt fdt_path_offset() returned %s\n",
457                                 fdt_strerror(nodeoffset));
458                         return 1;
459                 }
460                 /*
461                  * Do the delete.  A fourth parameter means delete a property,
462                  * otherwise delete the node.
463                  */
464                 if (argc > 3) {
465                         err = fdt_delprop(working_fdt, nodeoffset, argv[3]);
466                         if (err < 0) {
467                                 printf("libfdt fdt_delprop():  %s\n",
468                                         fdt_strerror(err));
469                                 return err;
470                         }
471                 } else {
472                         err = fdt_del_node(working_fdt, nodeoffset);
473                         if (err < 0) {
474                                 printf("libfdt fdt_del_node():  %s\n",
475                                         fdt_strerror(err));
476                                 return err;
477                         }
478                 }
479
480         /*
481          * Display header info
482          */
483         } else if (argv[1][0] == 'h') {
484                 u32 version = fdt_version(working_fdt);
485                 printf("magic:\t\t\t0x%x\n", fdt_magic(working_fdt));
486                 printf("totalsize:\t\t0x%x (%d)\n", fdt_totalsize(working_fdt),
487                        fdt_totalsize(working_fdt));
488                 printf("off_dt_struct:\t\t0x%x\n",
489                        fdt_off_dt_struct(working_fdt));
490                 printf("off_dt_strings:\t\t0x%x\n",
491                        fdt_off_dt_strings(working_fdt));
492                 printf("off_mem_rsvmap:\t\t0x%x\n",
493                        fdt_off_mem_rsvmap(working_fdt));
494                 printf("version:\t\t%d\n", version);
495                 printf("last_comp_version:\t%d\n",
496                        fdt_last_comp_version(working_fdt));
497                 if (version >= 2)
498                         printf("boot_cpuid_phys:\t0x%x\n",
499                                 fdt_boot_cpuid_phys(working_fdt));
500                 if (version >= 3)
501                         printf("size_dt_strings:\t0x%x\n",
502                                 fdt_size_dt_strings(working_fdt));
503                 if (version >= 17)
504                         printf("size_dt_struct:\t\t0x%x\n",
505                                 fdt_size_dt_struct(working_fdt));
506                 printf("number mem_rsv:\t\t0x%x\n",
507                        fdt_num_mem_rsv(working_fdt));
508                 printf("\n");
509
510         /*
511          * Set boot cpu id
512          */
513         } else if (strncmp(argv[1], "boo", 3) == 0) {
514                 unsigned long tmp = simple_strtoul(argv[2], NULL, 16);
515                 fdt_set_boot_cpuid_phys(working_fdt, tmp);
516
517         /*
518          * memory command
519          */
520         } else if (strncmp(argv[1], "me", 2) == 0) {
521                 uint64_t addr, size;
522                 int err;
523                 addr = simple_strtoull(argv[2], NULL, 16);
524                 size = simple_strtoull(argv[3], NULL, 16);
525                 err = fdt_fixup_memory(working_fdt, addr, size);
526                 if (err < 0)
527                         return err;
528
529         /*
530          * mem reserve commands
531          */
532         } else if (strncmp(argv[1], "rs", 2) == 0) {
533                 if (argv[2][0] == 'p') {
534                         uint64_t addr, size;
535                         int total = fdt_num_mem_rsv(working_fdt);
536                         int j, err;
537                         printf("index\t\t   start\t\t    size\n");
538                         printf("-------------------------------"
539                                 "-----------------\n");
540                         for (j = 0; j < total; j++) {
541                                 err = fdt_get_mem_rsv(working_fdt, j, &addr, &size);
542                                 if (err < 0) {
543                                         printf("libfdt fdt_get_mem_rsv():  %s\n",
544                                                         fdt_strerror(err));
545                                         return err;
546                                 }
547                                 printf("    %x\t%08x%08x\t%08x%08x\n", j,
548                                         (u32)(addr >> 32),
549                                         (u32)(addr & 0xffffffff),
550                                         (u32)(size >> 32),
551                                         (u32)(size & 0xffffffff));
552                         }
553                 } else if (argv[2][0] == 'a') {
554                         uint64_t addr, size;
555                         int err;
556                         addr = simple_strtoull(argv[3], NULL, 16);
557                         size = simple_strtoull(argv[4], NULL, 16);
558                         err = fdt_add_mem_rsv(working_fdt, addr, size);
559
560                         if (err < 0) {
561                                 printf("libfdt fdt_add_mem_rsv():  %s\n",
562                                         fdt_strerror(err));
563                                 return err;
564                         }
565                 } else if (argv[2][0] == 'd') {
566                         unsigned long idx = simple_strtoul(argv[3], NULL, 16);
567                         int err = fdt_del_mem_rsv(working_fdt, idx);
568
569                         if (err < 0) {
570                                 printf("libfdt fdt_del_mem_rsv():  %s\n",
571                                         fdt_strerror(err));
572                                 return err;
573                         }
574                 } else {
575                         /* Unrecognized command */
576                         return CMD_RET_USAGE;
577                 }
578         }
579 #ifdef CONFIG_OF_BOARD_SETUP
580         /* Call the board-specific fixup routine */
581         else if (strncmp(argv[1], "boa", 3) == 0)
582                 ft_board_setup(working_fdt, gd->bd);
583 #endif
584         /* Create a chosen node */
585         else if (argv[1][0] == 'c') {
586                 unsigned long initrd_start = 0, initrd_end = 0;
587
588                 if ((argc != 2) && (argc != 4))
589                         return CMD_RET_USAGE;
590
591                 if (argc == 4) {
592                         initrd_start = simple_strtoul(argv[2], NULL, 16);
593                         initrd_end = simple_strtoul(argv[3], NULL, 16);
594                 }
595
596                 fdt_chosen(working_fdt, 1);
597                 fdt_initrd(working_fdt, initrd_start, initrd_end, 1);
598         }
599         /* resize the fdt */
600         else if (strncmp(argv[1], "re", 2) == 0) {
601                 fdt_resize(working_fdt);
602         }
603         else {
604                 /* Unrecognized command */
605                 return CMD_RET_USAGE;
606         }
607
608         return 0;
609 }
610
611 /****************************************************************************/
612
613 /**
614  * fdt_valid() - Check if an FDT is valid. If not, change it to NULL
615  *
616  * @blobp: Pointer to FDT pointer
617  * @return 1 if OK, 0 if bad (in which case *blobp is set to NULL)
618  */
619 static int fdt_valid(struct fdt_header **blobp)
620 {
621         const void *blob = *blobp;
622         int err;
623
624         if (blob == NULL) {
625                 printf ("The address of the fdt is invalid (NULL).\n");
626                 return 0;
627         }
628
629         err = fdt_check_header(blob);
630         if (err == 0)
631                 return 1;       /* valid */
632
633         if (err < 0) {
634                 printf("libfdt fdt_check_header(): %s", fdt_strerror(err));
635                 /*
636                  * Be more informative on bad version.
637                  */
638                 if (err == -FDT_ERR_BADVERSION) {
639                         if (fdt_version(blob) <
640                             FDT_FIRST_SUPPORTED_VERSION) {
641                                 printf (" - too old, fdt %d < %d",
642                                         fdt_version(blob),
643                                         FDT_FIRST_SUPPORTED_VERSION);
644                         }
645                         if (fdt_last_comp_version(blob) >
646                             FDT_LAST_SUPPORTED_VERSION) {
647                                 printf (" - too new, fdt %d > %d",
648                                         fdt_version(blob),
649                                         FDT_LAST_SUPPORTED_VERSION);
650                         }
651                 }
652                 printf("\n");
653                 *blobp = NULL;
654                 return 0;
655         }
656         return 1;
657 }
658
659 /****************************************************************************/
660
661 /*
662  * Parse the user's input, partially heuristic.  Valid formats:
663  * <0x00112233 4 05>    - an array of cells.  Numbers follow standard
664  *                      C conventions.
665  * [00 11 22 .. nn] - byte stream
666  * "string"     - If the the value doesn't start with "<" or "[", it is
667  *                      treated as a string.  Note that the quotes are
668  *                      stripped by the parser before we get the string.
669  * newval: An array of strings containing the new property as specified
670  *      on the command line
671  * count: The number of strings in the array
672  * data: A bytestream to be placed in the property
673  * len: The length of the resulting bytestream
674  */
675 static int fdt_parse_prop(char * const *newval, int count, char *data, int *len)
676 {
677         char *cp;               /* temporary char pointer */
678         char *newp;             /* temporary newval char pointer */
679         unsigned long tmp;      /* holds converted values */
680         int stridx = 0;
681
682         *len = 0;
683         newp = newval[0];
684
685         /* An array of cells */
686         if (*newp == '<') {
687                 newp++;
688                 while ((*newp != '>') && (stridx < count)) {
689                         /*
690                          * Keep searching until we find that last ">"
691                          * That way users don't have to escape the spaces
692                          */
693                         if (*newp == '\0') {
694                                 newp = newval[++stridx];
695                                 continue;
696                         }
697
698                         cp = newp;
699                         tmp = simple_strtoul(cp, &newp, 0);
700                         *(__be32 *)data = __cpu_to_be32(tmp);
701                         data  += 4;
702                         *len += 4;
703
704                         /* If the ptr didn't advance, something went wrong */
705                         if ((newp - cp) <= 0) {
706                                 printf("Sorry, I could not convert \"%s\"\n",
707                                         cp);
708                                 return 1;
709                         }
710
711                         while (*newp == ' ')
712                                 newp++;
713                 }
714
715                 if (*newp != '>') {
716                         printf("Unexpected character '%c'\n", *newp);
717                         return 1;
718                 }
719         } else if (*newp == '[') {
720                 /*
721                  * Byte stream.  Convert the values.
722                  */
723                 newp++;
724                 while ((stridx < count) && (*newp != ']')) {
725                         while (*newp == ' ')
726                                 newp++;
727                         if (*newp == '\0') {
728                                 newp = newval[++stridx];
729                                 continue;
730                         }
731                         if (!isxdigit(*newp))
732                                 break;
733                         tmp = simple_strtoul(newp, &newp, 16);
734                         *data++ = tmp & 0xFF;
735                         *len    = *len + 1;
736                 }
737                 if (*newp != ']') {
738                         printf("Unexpected character '%c'\n", *newp);
739                         return 1;
740                 }
741         } else {
742                 /*
743                  * Assume it is one or more strings.  Copy it into our
744                  * data area for convenience (including the
745                  * terminating '\0's).
746                  */
747                 while (stridx < count) {
748                         size_t length = strlen(newp) + 1;
749                         strcpy(data, newp);
750                         data += length;
751                         *len += length;
752                         newp = newval[++stridx];
753                 }
754         }
755         return 0;
756 }
757
758 /****************************************************************************/
759
760 /*
761  * Heuristic to guess if this is a string or concatenated strings.
762  */
763
764 static int is_printable_string(const void *data, int len)
765 {
766         const char *s = data;
767
768         /* zero length is not */
769         if (len == 0)
770                 return 0;
771
772         /* must terminate with zero or '\n' */
773         if (s[len - 1] != '\0' && s[len - 1] != '\n')
774                 return 0;
775
776         /* printable or a null byte (concatenated strings) */
777         while (((*s == '\0') || isprint(*s) || isspace(*s)) && (len > 0)) {
778                 /*
779                  * If we see a null, there are three possibilities:
780                  * 1) If len == 1, it is the end of the string, printable
781                  * 2) Next character also a null, not printable.
782                  * 3) Next character not a null, continue to check.
783                  */
784                 if (s[0] == '\0') {
785                         if (len == 1)
786                                 return 1;
787                         if (s[1] == '\0')
788                                 return 0;
789                 }
790                 s++;
791                 len--;
792         }
793
794         /* Not the null termination, or not done yet: not printable */
795         if (*s != '\0' || (len != 0))
796                 return 0;
797
798         return 1;
799 }
800
801
802 /*
803  * Print the property in the best format, a heuristic guess.  Print as
804  * a string, concatenated strings, a byte, word, double word, or (if all
805  * else fails) it is printed as a stream of bytes.
806  */
807 static void print_data(const void *data, int len)
808 {
809         int j;
810
811         /* no data, don't print */
812         if (len == 0)
813                 return;
814
815         /*
816          * It is a string, but it may have multiple strings (embedded '\0's).
817          */
818         if (is_printable_string(data, len)) {
819                 puts("\"");
820                 j = 0;
821                 while (j < len) {
822                         if (j > 0)
823                                 puts("\", \"");
824                         puts(data);
825                         j    += strlen(data) + 1;
826                         data += strlen(data) + 1;
827                 }
828                 puts("\"");
829                 return;
830         }
831
832         if ((len %4) == 0) {
833                 if (len > CONFIG_CMD_FDT_MAX_DUMP)
834                         printf("* 0x%p [0x%08x]", data, len);
835                 else {
836                         const __be32 *p;
837
838                         printf("<");
839                         for (j = 0, p = data; j < len/4; j++)
840                                 printf("0x%08x%s", fdt32_to_cpu(p[j]),
841                                         j < (len/4 - 1) ? " " : "");
842                         printf(">");
843                 }
844         } else { /* anything else... hexdump */
845                 if (len > CONFIG_CMD_FDT_MAX_DUMP)
846                         printf("* 0x%p [0x%08x]", data, len);
847                 else {
848                         const u8 *s;
849
850                         printf("[");
851                         for (j = 0, s = data; j < len; j++)
852                                 printf("%02x%s", s[j], j < len - 1 ? " " : "");
853                         printf("]");
854                 }
855         }
856 }
857
858 /****************************************************************************/
859
860 /*
861  * Recursively print (a portion of) the working_fdt.  The depth parameter
862  * determines how deeply nested the fdt is printed.
863  */
864 static int fdt_print(const char *pathp, char *prop, int depth)
865 {
866         static char tabs[MAX_LEVEL+1] =
867                 "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"
868                 "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
869         const void *nodep;      /* property node pointer */
870         int  nodeoffset;        /* node offset from libfdt */
871         int  nextoffset;        /* next node offset from libfdt */
872         uint32_t tag;           /* tag */
873         int  len;               /* length of the property */
874         int  level = 0;         /* keep track of nesting level */
875         const struct fdt_property *fdt_prop;
876
877         nodeoffset = fdt_path_offset (working_fdt, pathp);
878         if (nodeoffset < 0) {
879                 /*
880                  * Not found or something else bad happened.
881                  */
882                 printf ("libfdt fdt_path_offset() returned %s\n",
883                         fdt_strerror(nodeoffset));
884                 return 1;
885         }
886         /*
887          * The user passed in a property as well as node path.
888          * Print only the given property and then return.
889          */
890         if (prop) {
891                 nodep = fdt_getprop (working_fdt, nodeoffset, prop, &len);
892                 if (len == 0) {
893                         /* no property value */
894                         printf("%s %s\n", pathp, prop);
895                         return 0;
896                 } else if (len > 0) {
897                         printf("%s = ", prop);
898                         print_data (nodep, len);
899                         printf("\n");
900                         return 0;
901                 } else {
902                         printf ("libfdt fdt_getprop(): %s\n",
903                                 fdt_strerror(len));
904                         return 1;
905                 }
906         }
907
908         /*
909          * The user passed in a node path and no property,
910          * print the node and all subnodes.
911          */
912         while(level >= 0) {
913                 tag = fdt_next_tag(working_fdt, nodeoffset, &nextoffset);
914                 switch(tag) {
915                 case FDT_BEGIN_NODE:
916                         pathp = fdt_get_name(working_fdt, nodeoffset, NULL);
917                         if (level <= depth) {
918                                 if (pathp == NULL)
919                                         pathp = "/* NULL pointer error */";
920                                 if (*pathp == '\0')
921                                         pathp = "/";    /* root is nameless */
922                                 printf("%s%s {\n",
923                                         &tabs[MAX_LEVEL - level], pathp);
924                         }
925                         level++;
926                         if (level >= MAX_LEVEL) {
927                                 printf("Nested too deep, aborting.\n");
928                                 return 1;
929                         }
930                         break;
931                 case FDT_END_NODE:
932                         level--;
933                         if (level <= depth)
934                                 printf("%s};\n", &tabs[MAX_LEVEL - level]);
935                         if (level == 0) {
936                                 level = -1;             /* exit the loop */
937                         }
938                         break;
939                 case FDT_PROP:
940                         fdt_prop = fdt_offset_ptr(working_fdt, nodeoffset,
941                                         sizeof(*fdt_prop));
942                         pathp    = fdt_string(working_fdt,
943                                         fdt32_to_cpu(fdt_prop->nameoff));
944                         len      = fdt32_to_cpu(fdt_prop->len);
945                         nodep    = fdt_prop->data;
946                         if (len < 0) {
947                                 printf ("libfdt fdt_getprop(): %s\n",
948                                         fdt_strerror(len));
949                                 return 1;
950                         } else if (len == 0) {
951                                 /* the property has no value */
952                                 if (level <= depth)
953                                         printf("%s%s;\n",
954                                                 &tabs[MAX_LEVEL - level],
955                                                 pathp);
956                         } else {
957                                 if (level <= depth) {
958                                         printf("%s%s = ",
959                                                 &tabs[MAX_LEVEL - level],
960                                                 pathp);
961                                         print_data (nodep, len);
962                                         printf(";\n");
963                                 }
964                         }
965                         break;
966                 case FDT_NOP:
967                         printf("%s/* NOP */\n", &tabs[MAX_LEVEL - level]);
968                         break;
969                 case FDT_END:
970                         return 1;
971                 default:
972                         if (level <= depth)
973                                 printf("Unknown tag 0x%08X\n", tag);
974                         return 1;
975                 }
976                 nodeoffset = nextoffset;
977         }
978         return 0;
979 }
980
981 /********************************************************************/
982 #ifdef CONFIG_SYS_LONGHELP
983 static char fdt_help_text[] =
984         "addr [-c]  <addr> [<length>]   - Set the [control] fdt location to <addr>\n"
985 #ifdef CONFIG_OF_BOARD_SETUP
986         "fdt boardsetup                      - Do board-specific set up\n"
987 #endif
988         "fdt move   <fdt> <newaddr> <length> - Copy the fdt to <addr> and make it active\n"
989         "fdt resize                          - Resize fdt to size + padding to 4k addr\n"
990         "fdt print  <path> [<prop>]          - Recursive print starting at <path>\n"
991         "fdt list   <path> [<prop>]          - Print one level starting at <path>\n"
992         "fdt get value <var> <path> <prop>   - Get <property> and store in <var>\n"
993         "fdt get name <var> <path> <index>   - Get name of node <index> and store in <var>\n"
994         "fdt get addr <var> <path> <prop>    - Get start address of <property> and store in <var>\n"
995         "fdt get size <var> <path> [<prop>]  - Get size of [<property>] or num nodes and store in <var>\n"
996         "fdt set    <path> <prop> [<val>]    - Set <property> [to <val>]\n"
997         "fdt mknode <path> <node>            - Create a new node after <path>\n"
998         "fdt rm     <path> [<prop>]          - Delete the node or <property>\n"
999         "fdt header                          - Display header info\n"
1000         "fdt bootcpu <id>                    - Set boot cpuid\n"
1001         "fdt memory <addr> <size>            - Add/Update memory node\n"
1002         "fdt rsvmem print                    - Show current mem reserves\n"
1003         "fdt rsvmem add <addr> <size>        - Add a mem reserve\n"
1004         "fdt rsvmem delete <index>           - Delete a mem reserves\n"
1005         "fdt chosen [<start> <end>]          - Add/update the /chosen branch in the tree\n"
1006         "                                        <start>/<end> - initrd start/end addr\n"
1007         "NOTE: Dereference aliases by omiting the leading '/', "
1008                 "e.g. fdt print ethernet0.";
1009 #endif
1010
1011 U_BOOT_CMD(
1012         fdt,    255,    0,      do_fdt,
1013         "flattened device tree utility commands", fdt_help_text
1014 );