]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - common/cmd_fdt.c
For fdt_find_node_by_path(), handle the root path properly.
[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
32 #ifdef CONFIG_OF_LIBFDT
33
34 #include <asm/global_data.h>
35 #include <fdt.h>
36 #include <libfdt.h>
37 #include <fdt_support.h>
38
39 #define MAX_LEVEL       32              /* how deeply nested we will go */
40 #define SCRATCHPAD      1024    /* bytes of scratchpad memory */
41
42 /*
43  * Global data (for the gd->bd)
44  */
45 DECLARE_GLOBAL_DATA_PTR;
46
47 /*
48  * Function prototypes/declarations.
49  */
50 static int fdt_valid(void);
51 static int fdt_parse_prop(char *pathp, char *prop, char *newval,
52         char *data, int *len);
53 static int fdt_print(char *pathp, char *prop, int depth);
54
55 /*
56  * Flattened Device Tree command, see the help for parameter definitions.
57  */
58 int do_fdt (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
59 {
60         if (argc < 2) {
61                 printf ("Usage:\n%s\n", cmdtp->usage);
62                 return 1;
63         }
64
65         /********************************************************************
66          * Set the address of the fdt
67          ********************************************************************/
68         if (argv[1][0] == 'a') {
69                 /*
70                  * Set the address [and length] of the fdt.
71                  */
72                 fdt = (struct fdt_header *)simple_strtoul(argv[2], NULL, 16);
73
74                 if (!fdt_valid()) {
75                         return 1;
76                 }
77
78                 if (argc >= 4) {
79                         int  len;
80                         int  err;
81                         /*
82                          * Optional new length
83                          */
84                         len =  simple_strtoul(argv[3], NULL, 16);
85                         if (len < fdt_totalsize(fdt)) {
86                                 printf ("New length %d < existing length %d, "
87                                         "ignoring.\n",
88                                         len, fdt_totalsize(fdt));
89                         } else {
90                                 /*
91                                  * Open in place with a new length.
92                                  */
93                                 err = fdt_open_into(fdt, fdt, len);
94                                 if (err != 0) {
95                                         printf ("libfdt fdt_open_into(): %s\n",
96                                                 fdt_strerror(err));
97                                 }
98                         }
99                 }
100
101         /********************************************************************
102          * Move the fdt
103          ********************************************************************/
104         } else if ((argv[1][0] == 'm') && (argv[1][1] == 'o')) {
105                 struct fdt_header *newaddr;
106                 int  len;
107                 int  err;
108
109                 if (argc < 4) {
110                         printf ("Usage:\n%s\n", cmdtp->usage);
111                         return 1;
112                 }
113
114                 /*
115                  * Set the address and length of the fdt.
116                  */
117                 fdt = (struct fdt_header *)simple_strtoul(argv[2], NULL, 16);
118                 if (!fdt_valid()) {
119                         return 1;
120                 }
121
122                 newaddr = (struct fdt_header *)simple_strtoul(argv[3],NULL,16);
123
124                 /*
125                  * If the user specifies a length, use that.  Otherwise use the
126                  * current length.
127                  */
128                 if (argc <= 4) {
129                         len = fdt_totalsize(fdt);
130                 } else {
131                         len = simple_strtoul(argv[4], NULL, 16);
132                         if (len < fdt_totalsize(fdt)) {
133                                 printf ("New length 0x%X < existing length "
134                                         "0x%X, aborting.\n",
135                                         len, fdt_totalsize(fdt));
136                                 return 1;
137                         }
138                 }
139
140                 /*
141                  * Copy to the new location.
142                  */
143                 err = fdt_open_into(fdt, newaddr, len);
144                 if (err != 0) {
145                         printf ("libfdt fdt_open_into(): %s\n",
146                                 fdt_strerror(err));
147                         return 1;
148                 }
149                 fdt = newaddr;
150
151         /********************************************************************
152          * Make a new node
153          ********************************************************************/
154         } else if ((argv[1][0] == 'm') && (argv[1][1] == 'k')) {
155                 char *pathp;            /* path */
156                 char *nodep;            /* new node to add */
157                 int  nodeoffset;        /* node offset from libfdt */
158                 int  err;
159
160                 /*
161                  * Parameters: Node path, new node to be appended to the path.
162                  */
163                 if (argc < 4) {
164                         printf ("Usage:\n%s\n", cmdtp->usage);
165                         return 1;
166                 }
167
168                 pathp = argv[2];
169                 nodep = argv[3];
170
171                 nodeoffset = fdt_find_node_by_path (fdt, pathp);
172                 if (nodeoffset < 0) {
173                         /*
174                          * Not found or something else bad happened.
175                          */
176                         printf ("libfdt fdt_find_node_by_path() returned %s\n",
177                                 fdt_strerror(nodeoffset));
178                         return 1;
179                 }
180                 err = fdt_add_subnode(fdt, nodeoffset, nodep);
181                 if (err < 0) {
182                         printf ("libfdt fdt_add_subnode(): %s\n",
183                                 fdt_strerror(err));
184                         return 1;
185                 }
186
187         /********************************************************************
188          * Set the value of a property in the fdt.
189          ********************************************************************/
190         } else if (argv[1][0] == 's') {
191                 char *pathp;            /* path */
192                 char *prop;             /* property */
193                 char *newval;           /* value from the user (as a string) */
194                 int  nodeoffset;        /* node offset from libfdt */
195                 static char data[SCRATCHPAD];   /* storage for the property */
196                 int  len;               /* new length of the property */
197                 int  ret;               /* return value */
198
199                 /*
200                  * Parameters: Node path, property, value.
201                  */
202                 if (argc < 5) {
203                         printf ("Usage:\n%s\n", cmdtp->usage);
204                         return 1;
205                 }
206
207                 pathp  = argv[2];
208                 prop   = argv[3];
209                 newval = argv[4];
210
211                 nodeoffset = fdt_find_node_by_path (fdt, pathp);
212                 if (nodeoffset < 0) {
213                         /*
214                          * Not found or something else bad happened.
215                          */
216                         printf ("libfdt fdt_find_node_by_path() returned %s\n",
217                                 fdt_strerror(nodeoffset));
218                         return 1;
219                 }
220                 ret = fdt_parse_prop(pathp, prop, newval, data, &len);
221                 if (ret != 0)
222                         return ret;
223
224                 ret = fdt_setprop(fdt, nodeoffset, prop, data, len);
225                 if (ret < 0) {
226                         printf ("libfdt fdt_setprop(): %s\n", fdt_strerror(ret));
227                         return 1;
228                 }
229
230         /********************************************************************
231          * Print (recursive) / List (single level)
232          ********************************************************************/
233         } else if ((argv[1][0] == 'p') || (argv[1][0] == 'l')) {
234                 int depth = MAX_LEVEL;  /* how deep to print */
235                 char *pathp;            /* path */
236                 char *prop;             /* property */
237                 int  ret;               /* return value */
238
239                 /*
240                  * list is an alias for print, but limited to 1 level
241                  */
242                 if (argv[1][0] == 'l') {
243                         depth = 1;
244                 }
245
246                 /*
247                  * Get the starting path.  The root node is an oddball,
248                  * the offset is zero and has no name.
249                  */
250                 pathp = argv[2];
251                 if (argc > 3)
252                         prop = argv[3];
253                 else
254                         prop = NULL;
255
256                 ret = fdt_print(pathp, prop, depth);
257                 if (ret != 0)
258                         return ret;
259
260         /********************************************************************
261          * Remove a property/node
262          ********************************************************************/
263         } else if (argv[1][0] == 'r') {
264                 int  nodeoffset;        /* node offset from libfdt */
265                 int  err;
266
267                 /*
268                  * Get the path.  The root node is an oddball, the offset
269                  * is zero and has no name.
270                  */
271                 nodeoffset = fdt_find_node_by_path (fdt, argv[2]);
272                 if (nodeoffset < 0) {
273                         /*
274                          * Not found or something else bad happened.
275                          */
276                         printf ("libfdt fdt_find_node_by_path() returned %s\n",
277                                 fdt_strerror(nodeoffset));
278                         return 1;
279                 }
280                 /*
281                  * Do the delete.  A fourth parameter means delete a property,
282                  * otherwise delete the node.
283                  */
284                 if (argc > 3) {
285                         err = fdt_delprop(fdt, nodeoffset, argv[3]);
286                         if (err < 0) {
287                                 printf("libfdt fdt_delprop():  %s\n",
288                                         fdt_strerror(err));
289                                 return err;
290                         }
291                 } else {
292                         err = fdt_del_node(fdt, nodeoffset);
293                         if (err < 0) {
294                                 printf("libfdt fdt_del_node():  %s\n",
295                                         fdt_strerror(err));
296                                 return err;
297                         }
298                 }
299
300         /********************************************************************
301          * Create a chosen node
302          ********************************************************************/
303         } else if (argv[1][0] == 'c') {
304                 fdt_chosen(fdt, 0, 0, 1);
305
306         /********************************************************************
307          * Create a u-boot-env node
308          ********************************************************************/
309         } else if (argv[1][0] == 'e') {
310                 fdt_env(fdt);
311
312         /********************************************************************
313          * Create a bd_t node
314          ********************************************************************/
315         } else if (argv[1][0] == 'b') {
316                 fdt_bd_t(fdt);
317
318         /********************************************************************
319          * Unrecognized command
320          ********************************************************************/
321         } else {
322                 printf ("Usage:\n%s\n", cmdtp->usage);
323                 return 1;
324         }
325
326         return 0;
327 }
328
329 /****************************************************************************/
330
331 static int fdt_valid(void)
332 {
333         int  err;
334
335         if (fdt == NULL) {
336                 printf ("The address of the fdt is invalid (NULL).\n");
337                 return 0;
338         }
339
340         err = fdt_check_header(fdt);
341         if (err == 0)
342                 return 1;       /* valid */
343
344         if (err < 0) {
345                 printf("libfdt fdt_check_header(): %s", fdt_strerror(err));
346                 /*
347                  * Be more informative on bad version.
348                  */
349                 if (err == -FDT_ERR_BADVERSION) {
350                         if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) {
351                                 printf (" - too old, fdt $d < %d",
352                                         fdt_version(fdt),
353                                         FDT_FIRST_SUPPORTED_VERSION);
354                                 fdt = NULL;
355                         }
356                         if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION) {
357                                 printf (" - too new, fdt $d > %d",
358                                         fdt_version(fdt),
359                                         FDT_LAST_SUPPORTED_VERSION);
360                                 fdt = NULL;
361                         }
362                         return 0;
363                 }
364                 printf("\n");
365                 return 0;
366         }
367         return 1;
368 }
369
370 /****************************************************************************/
371
372 /*
373  * Parse the user's input, partially heuristic.  Valid formats:
374  * <00>         - hex byte
375  * <0011>       - hex half word (16 bits)
376  * <00112233>   - hex word (32 bits)
377  *              - hex double words (64 bits) are not supported, must use
378  *                      a byte stream instead.
379  * [00 11 22 .. nn] - byte stream
380  * "string"     - If the the value doesn't start with "<" or "[", it is
381  *                      treated as a string.  Note that the quotes are
382  *                      stripped by the parser before we get the string.
383  */
384 static int fdt_parse_prop(char *pathp, char *prop, char *newval,
385         char *data, int *len)
386 {
387         char *cp;               /* temporary char pointer */
388         unsigned long tmp;      /* holds converted values */
389
390         if (*newval == '<') {
391                 /*
392                  * Bigger values than bytes.
393                  */
394                 *len = 0;
395                 newval++;
396                 while ((*newval != '>') && (*newval != '\0')) {
397                         cp = newval;
398                         tmp = simple_strtoul(cp, &newval, 16);
399                         if ((newval - cp) <= 2) {
400                                 *data = tmp & 0xFF;
401                                 data  += 1;
402                                 *len += 1;
403                         } else if ((newval - cp) <= 4) {
404                                 *(uint16_t *)data = __cpu_to_be16(tmp);
405                                 data  += 2;
406                                 *len += 2;
407                         } else if ((newval - cp) <= 8) {
408                                 *(uint32_t *)data = __cpu_to_be32(tmp);
409                                 data  += 4;
410                                 *len += 4;
411                         } else {
412                                 printf("Sorry, I could not convert \"%s\"\n",
413                                         cp);
414                                 return 1;
415                         }
416                         while (*newval == ' ')
417                                 newval++;
418                 }
419                 if (*newval != '>') {
420                         printf("Unexpected character '%c'\n", *newval);
421                         return 1;
422                 }
423         } else if (*newval == '[') {
424                 /*
425                  * Byte stream.  Convert the values.
426                  */
427                 *len = 0;
428                 newval++;
429                 while ((*newval != ']') && (*newval != '\0')) {
430                         tmp = simple_strtoul(newval, &newval, 16);
431                         *data++ = tmp & 0xFF;
432                         *len++;
433                         while (*newval == ' ')
434                                 newval++;
435                 }
436                 if (*newval != ']') {
437                         printf("Unexpected character '%c'\n", *newval);
438                         return 1;
439                 }
440         } else {
441                 /*
442                  * Assume it is a string.  Copy it into our data area for
443                  * convenience (including the terminating '\0').
444                  */
445                 *len = strlen(newval) + 1;
446                 strcpy(data, newval);
447         }
448         return 0;
449 }
450
451 /****************************************************************************/
452
453 /*
454  * Heuristic to guess if this is a string or concatenated strings.
455  */
456
457 static int is_printable_string(const void *data, int len)
458 {
459         const char *s = data;
460
461         /* zero length is not */
462         if (len == 0)
463                 return 0;
464
465         /* must terminate with zero */
466         if (s[len - 1] != '\0')
467                 return 0;
468
469         /* printable or a null byte (concatenated strings) */
470         while (((*s == '\0') || isprint(*s)) && (len > 0)) {
471                 /*
472                  * If we see a null, there are three possibilities:
473                  * 1) If len == 1, it is the end of the string, printable
474                  * 2) Next character also a null, not printable.
475                  * 3) Next character not a null, continue to check.
476                  */
477                 if (s[0] == '\0') {
478                         if (len == 1)
479                                 return 1;
480                         if (s[1] == '\0')
481                                 return 0;
482                 }
483                 s++;
484                 len--;
485         }
486
487         /* Not the null termination, or not done yet: not printable */
488         if (*s != '\0' || (len != 0))
489                 return 0;
490
491         return 1;
492 }
493
494
495 /*
496  * Print the property in the best format, a heuristic guess.  Print as
497  * a string, concatenated strings, a byte, word, double word, or (if all
498  * else fails) it is printed as a stream of bytes.
499  */
500 static void print_data(const void *data, int len)
501 {
502         int j;
503         const u8 *s;
504
505         /* no data, don't print */
506         if (len == 0)
507                 return;
508
509         /*
510          * It is a string, but it may have multiple strings (embedded '\0's).
511          */
512         if (is_printable_string(data, len)) {
513                 puts("\"");
514                 j = 0;
515                 while (j < len) {
516                         if (j > 0)
517                                 puts("\", \"");
518                         puts(data);
519                         j    += strlen(data) + 1;
520                         data += strlen(data) + 1;
521                 }
522                 puts("\"");
523                 return;
524         }
525
526         switch (len) {
527         case 1:  /* byte */
528                 printf("<%02x>", (*(u8 *) data) & 0xff);
529                 break;
530         case 2:  /* half-word */
531                 printf("<%04x>", be16_to_cpu(*(u16 *) data) & 0xffff);
532                 break;
533         case 4:  /* word */
534                 printf("<%08x>", be32_to_cpu(*(u32 *) data) & 0xffffffffU);
535                 break;
536         case 8:  /* double-word */
537 #if __WORDSIZE == 64
538                 printf("<%016llx>", be64_to_cpu(*(uint64_t *) data));
539 #else
540                 printf("<%08x ", be32_to_cpu(*(u32 *) data) & 0xffffffffU);
541                 data += 4;
542                 printf("%08x>", be32_to_cpu(*(u32 *) data) & 0xffffffffU);
543 #endif
544                 break;
545         default:                /* anything else... hexdump */
546                 printf("[");
547                 for (j = 0, s = data; j < len; j++)
548                         printf("%02x%s", s[j], j < len - 1 ? " " : "");
549                 printf("]");
550
551                 break;
552         }
553 }
554
555 /****************************************************************************/
556
557 /*
558  * Recursively print (a portion of) the fdt.  The depth parameter
559  * determines how deeply nested the fdt is printed.
560  */
561 static int fdt_print(char *pathp, char *prop, int depth)
562 {
563         static int offstack[MAX_LEVEL];
564         static char tabs[MAX_LEVEL+1] =
565                 "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"
566                 "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
567         void *nodep;            /* property node pointer */
568         int  nodeoffset;        /* node offset from libfdt */
569         int  nextoffset;        /* next node offset from libfdt */
570         uint32_t tag;           /* tag */
571         int  len;               /* length of the property */
572         int  level = 0;         /* keep track of nesting level */
573
574         nodeoffset = fdt_find_node_by_path (fdt, pathp);
575         if (nodeoffset < 0) {
576                 /*
577                  * Not found or something else bad happened.
578                  */
579                 printf ("libfdt fdt_find_node_by_path() returned %s\n",
580                         fdt_strerror(nodeoffset));
581                 return 1;
582         }
583         /*
584          * The user passed in a property as well as node path.
585          * Print only the given property and then return.
586          */
587         if (prop) {
588                 nodep = fdt_getprop (fdt, nodeoffset, prop, &len);
589                 if (len == 0) {
590                         /* no property value */
591                         printf("%s %s\n", pathp, prop);
592                         return 0;
593                 } else if (len > 0) {
594                         printf("%s=", prop);
595                         print_data (nodep, len);
596                         printf("\n");
597                         return 0;
598                 } else {
599                         printf ("libfdt fdt_getprop(): %s\n",
600                                 fdt_strerror(len));
601                         return 1;
602                 }
603         }
604
605         /*
606          * The user passed in a node path and no property,
607          * print the node and all subnodes.
608          */
609         offstack[0] = nodeoffset;
610
611         while(level >= 0) {
612                 tag = fdt_next_tag(fdt, nodeoffset, &nextoffset, &pathp);
613                 switch(tag) {
614                 case FDT_BEGIN_NODE:
615                         if(level <= depth)
616                                 printf("%s%s {\n",
617                                         &tabs[MAX_LEVEL - level], pathp);
618                         level++;
619                         offstack[level] = nodeoffset;
620                         if (level >= MAX_LEVEL) {
621                                 printf("Aaaiii <splat> nested too deep. "
622                                         "Aborting.\n");
623                                 return 1;
624                         }
625                         break;
626                 case FDT_END_NODE:
627                         level--;
628                         if(level <= depth)
629                                 printf("%s};\n", &tabs[MAX_LEVEL - level]);
630                         if (level == 0) {
631                                 level = -1;             /* exit the loop */
632                         }
633                         break;
634                 case FDT_PROP:
635                         nodep = fdt_getprop (fdt, offstack[level], pathp, &len);
636                         if (len < 0) {
637                                 printf ("libfdt fdt_getprop(): %s\n",
638                                         fdt_strerror(len));
639                                 return 1;
640                         } else if (len == 0) {
641                                 /* the property has no value */
642                                 if(level <= depth)
643                                         printf("%s%s;\n",
644                                                 &tabs[MAX_LEVEL - level],
645                                                 pathp);
646                         } else {
647                                 if(level <= depth) {
648                                         printf("%s%s=",
649                                                 &tabs[MAX_LEVEL - level],
650                                                 pathp);
651                                         print_data (nodep, len);
652                                         printf(";\n");
653                                 }
654                         }
655                         break;
656                 case FDT_NOP:
657                         break;
658                 case FDT_END:
659                         return 1;
660                 default:
661                         if(level <= depth)
662                                 printf("Unknown tag 0x%08X\n", tag);
663                         return 1;
664                 }
665                 nodeoffset = nextoffset;
666         }
667         return 0;
668 }
669
670 /********************************************************************/
671
672 U_BOOT_CMD(
673         fdt,    5,      0,      do_fdt,
674         "fdt     - flattened device tree utility commands\n",
675             "addr   <addr> [<length>]        - Set the fdt location to <addr>\n"
676         "fdt move   <fdt> <newaddr> <length> - Copy the fdt to <addr>\n"
677         "fdt print  <path> [<prop>]          - Recursive print starting at <path>\n"
678         "fdt list   <path> [<prop>]          - Print one level starting at <path>\n"
679         "fdt set    <path> <prop> [<val>]    - Set <property> [to <val>]\n"
680         "fdt mknode <path> <node>            - Create a new node after <path>\n"
681         "fdt rm     <path> [<prop>]          - Delete the node or <property>\n"
682         "fdt chosen - Add/update the \"/chosen\" branch in the tree\n"
683 #ifdef CONFIG_OF_HAS_UBOOT_ENV
684         "fdt env    - Add/replace the \"/u-boot-env\" branch in the tree\n"
685 #endif
686 #ifdef CONFIG_OF_HAS_BD_T
687         "fdt bd_t   - Add/replace the \"/bd_t\" branch in the tree\n"
688 #endif
689         "Hints:\n"
690         " * If the property you are setting/printing has a '#' character,\n"
691         "     you MUST escape it with a \\ character or quote it with \" or\n"
692         "     it will be ignored as a comment.\n"
693         " * If the value has spaces in it, you MUST escape the spaces with\n"
694         "     \\ characters or quote it with \"\"\n"
695         "Examples: fdt print /               # print the whole tree\n"
696         "          fdt print /cpus \"#address-cells\"\n"
697         "          fdt set   /cpus \"#address-cells\" \"[00 00 00 01]\"\n"
698 );
699
700 #endif /* CONFIG_OF_LIBFDT */