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