]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/fdt/fdt_loader_cmd.c
stand: TARGET_ARCH is spelled MACHINE_ARCH in Makefiles
[FreeBSD/FreeBSD.git] / stand / fdt / fdt_loader_cmd.c
1 /*-
2  * Copyright (c) 2009-2010 The FreeBSD Foundation
3  * All rights reserved.
4  *
5  * This software was developed by Semihalf under sponsorship from
6  * the FreeBSD Foundation.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 #include <stand.h>
34 #include <libfdt.h>
35 #include <fdt.h>
36 #include <sys/param.h>
37 #include <sys/linker.h>
38 #include <machine/elf.h>
39
40 #include "bootstrap.h"
41 #include "fdt_platform.h"
42
43 #ifdef DEBUG
44 #define debugf(fmt, args...) do { printf("%s(): ", __func__);   \
45     printf(fmt,##args); } while (0)
46 #else
47 #define debugf(fmt, args...)
48 #endif
49
50 #define FDT_CWD_LEN     256
51 #define FDT_MAX_DEPTH   12
52
53 #define FDT_PROP_SEP    " = "
54
55 #define COPYOUT(s,d,l)  archsw.arch_copyout(s, d, l)
56 #define COPYIN(s,d,l)   archsw.arch_copyin(s, d, l)
57
58 #define FDT_STATIC_DTB_SYMBOL   "fdt_static_dtb"
59
60 #define CMD_REQUIRES_BLOB       0x01
61
62 /* Location of FDT yet to be loaded. */
63 /* This may be in read-only memory, so can't be manipulated directly. */
64 static struct fdt_header *fdt_to_load = NULL;
65 /* Location of FDT on heap. */
66 /* This is the copy we actually manipulate. */
67 static struct fdt_header *fdtp = NULL;
68 /* Size of FDT blob */
69 static size_t fdtp_size = 0;
70
71 static int fdt_load_dtb(vm_offset_t va);
72 static void fdt_print_overlay_load_error(int err, const char *filename);
73 static int fdt_check_overlay_compatible(void *base_fdt, void *overlay_fdt);
74
75 static int fdt_cmd_nyi(int argc, char *argv[]);
76 static int fdt_load_dtb_overlays_string(const char * filenames);
77
78 static int fdt_cmd_addr(int argc, char *argv[]);
79 static int fdt_cmd_mkprop(int argc, char *argv[]);
80 static int fdt_cmd_cd(int argc, char *argv[]);
81 static int fdt_cmd_hdr(int argc, char *argv[]);
82 static int fdt_cmd_ls(int argc, char *argv[]);
83 static int fdt_cmd_prop(int argc, char *argv[]);
84 static int fdt_cmd_pwd(int argc, char *argv[]);
85 static int fdt_cmd_rm(int argc, char *argv[]);
86 static int fdt_cmd_mknode(int argc, char *argv[]);
87 static int fdt_cmd_mres(int argc, char *argv[]);
88
89 typedef int cmdf_t(int, char *[]);
90
91 struct cmdtab {
92         const char      *name;
93         cmdf_t          *handler;
94         int             flags;
95 };
96
97 static const struct cmdtab commands[] = {
98         { "addr", &fdt_cmd_addr,        0 },
99         { "alias", &fdt_cmd_nyi,        0 },
100         { "cd", &fdt_cmd_cd,            CMD_REQUIRES_BLOB },
101         { "header", &fdt_cmd_hdr,       CMD_REQUIRES_BLOB },
102         { "ls", &fdt_cmd_ls,            CMD_REQUIRES_BLOB },
103         { "mknode", &fdt_cmd_mknode,    CMD_REQUIRES_BLOB },
104         { "mkprop", &fdt_cmd_mkprop,    CMD_REQUIRES_BLOB },
105         { "mres", &fdt_cmd_mres,        CMD_REQUIRES_BLOB },
106         { "prop", &fdt_cmd_prop,        CMD_REQUIRES_BLOB },
107         { "pwd", &fdt_cmd_pwd,          CMD_REQUIRES_BLOB },
108         { "rm", &fdt_cmd_rm,            CMD_REQUIRES_BLOB },
109         { NULL, NULL }
110 };
111
112 static char cwd[FDT_CWD_LEN] = "/";
113
114 static vm_offset_t
115 fdt_find_static_dtb()
116 {
117         Elf_Ehdr *ehdr;
118         Elf_Shdr *shdr;
119         Elf_Sym sym;
120         vm_offset_t strtab, symtab, fdt_start;
121         uint64_t offs;
122         struct preloaded_file *kfp;
123         struct file_metadata *md;
124         char *strp;
125         int i, sym_count;
126
127         debugf("fdt_find_static_dtb()\n");
128
129         sym_count = symtab = strtab = 0;
130         strp = NULL;
131
132         offs = __elfN(relocation_offset);
133
134         kfp = file_findfile(NULL, NULL);
135         if (kfp == NULL)
136                 return (0);
137
138         /* Locate the dynamic symbols and strtab. */
139         md = file_findmetadata(kfp, MODINFOMD_ELFHDR);
140         if (md == NULL)
141                 return (0);
142         ehdr = (Elf_Ehdr *)md->md_data;
143
144         md = file_findmetadata(kfp, MODINFOMD_SHDR);
145         if (md == NULL)
146                 return (0);
147         shdr = (Elf_Shdr *)md->md_data;
148
149         for (i = 0; i < ehdr->e_shnum; ++i) {
150                 if (shdr[i].sh_type == SHT_DYNSYM && symtab == 0) {
151                         symtab = shdr[i].sh_addr + offs;
152                         sym_count = shdr[i].sh_size / sizeof(Elf_Sym);
153                 } else if (shdr[i].sh_type == SHT_STRTAB && strtab == 0) {
154                         strtab = shdr[i].sh_addr + offs;
155                 }
156         }
157
158         /*
159          * The most efficient way to find a symbol would be to calculate a
160          * hash, find proper bucket and chain, and thus find a symbol.
161          * However, that would involve code duplication (e.g. for hash
162          * function). So we're using simpler and a bit slower way: we're
163          * iterating through symbols, searching for the one which name is
164          * 'equal' to 'fdt_static_dtb'. To speed up the process a little bit,
165          * we are eliminating symbols type of which is not STT_NOTYPE, or(and)
166          * those which binding attribute is not STB_GLOBAL.
167          */
168         fdt_start = 0;
169         while (sym_count > 0 && fdt_start == 0) {
170                 COPYOUT(symtab, &sym, sizeof(sym));
171                 symtab += sizeof(sym);
172                 --sym_count;
173                 if (ELF_ST_BIND(sym.st_info) != STB_GLOBAL ||
174                     ELF_ST_TYPE(sym.st_info) != STT_NOTYPE)
175                         continue;
176                 strp = strdupout(strtab + sym.st_name);
177                 if (strcmp(strp, FDT_STATIC_DTB_SYMBOL) == 0)
178                         fdt_start = (vm_offset_t)sym.st_value + offs;
179                 free(strp);
180         }
181         return (fdt_start);
182 }
183
184 static int
185 fdt_load_dtb(vm_offset_t va)
186 {
187         struct fdt_header header;
188         int err;
189
190         debugf("fdt_load_dtb(0x%08jx)\n", (uintmax_t)va);
191
192         COPYOUT(va, &header, sizeof(header));
193         err = fdt_check_header(&header);
194         if (err < 0) {
195                 if (err == -FDT_ERR_BADVERSION) {
196                         snprintf(command_errbuf, sizeof(command_errbuf),
197                             "incompatible blob version: %d, should be: %d",
198                             fdt_version(fdtp), FDT_LAST_SUPPORTED_VERSION);
199                 } else {
200                         snprintf(command_errbuf, sizeof(command_errbuf),
201                             "error validating blob: %s", fdt_strerror(err));
202                 }
203                 return (1);
204         }
205
206         /*
207          * Release previous blob
208          */
209         if (fdtp)
210                 free(fdtp);
211
212         fdtp_size = fdt_totalsize(&header);
213         fdtp = malloc(fdtp_size);
214
215         if (fdtp == NULL) {
216                 command_errmsg = "can't allocate memory for device tree copy";
217                 return (1);
218         }
219
220         COPYOUT(va, fdtp, fdtp_size);
221         debugf("DTB blob found at 0x%jx, size: 0x%jx\n", (uintmax_t)va, (uintmax_t)fdtp_size);
222
223         return (0);
224 }
225
226 int
227 fdt_load_dtb_addr(struct fdt_header *header)
228 {
229         int err;
230
231         debugf("fdt_load_dtb_addr(%p)\n", header);
232
233         fdtp_size = fdt_totalsize(header);
234         err = fdt_check_header(header);
235         if (err < 0) {
236                 snprintf(command_errbuf, sizeof(command_errbuf),
237                     "error validating blob: %s", fdt_strerror(err));
238                 return (err);
239         }
240         free(fdtp);
241         if ((fdtp = malloc(fdtp_size)) == NULL) {
242                 command_errmsg = "can't allocate memory for device tree copy";
243                 return (1);
244         }
245
246         bcopy(header, fdtp, fdtp_size);
247         return (0);
248 }
249
250 int
251 fdt_load_dtb_file(const char * filename)
252 {
253         struct preloaded_file *bfp, *oldbfp;
254         int err;
255
256         debugf("fdt_load_dtb_file(%s)\n", filename);
257
258         oldbfp = file_findfile(NULL, "dtb");
259
260         /* Attempt to load and validate a new dtb from a file. */
261         if ((bfp = file_loadraw(filename, "dtb", 1)) == NULL) {
262                 snprintf(command_errbuf, sizeof(command_errbuf),
263                     "failed to load file '%s'", filename);
264                 return (1);
265         }
266         if ((err = fdt_load_dtb(bfp->f_addr)) != 0) {
267                 file_discard(bfp);
268                 return (err);
269         }
270
271         /* A new dtb was validated, discard any previous file. */
272         if (oldbfp)
273                 file_discard(oldbfp);
274         return (0);
275 }
276
277 static int
278 fdt_load_dtb_overlay(const char * filename)
279 {
280         struct preloaded_file *bfp;
281         struct fdt_header header;
282         int err;
283
284         debugf("fdt_load_dtb_overlay(%s)\n", filename);
285
286         /* Attempt to load and validate a new dtb from a file. FDT_ERR_NOTFOUND
287          * is normally a libfdt error code, but libfdt would actually return
288          * -FDT_ERR_NOTFOUND. We re-purpose the error code here to convey a
289          * similar meaning: the file itself was not found, which can still be
290          * considered an error dealing with FDT pieces.
291          */
292         if ((bfp = file_loadraw(filename, "dtbo", 1)) == NULL)
293                 return (FDT_ERR_NOTFOUND);
294
295         COPYOUT(bfp->f_addr, &header, sizeof(header));
296         err = fdt_check_header(&header);
297
298         if (err < 0) {
299                 file_discard(bfp);
300                 return (err);
301         }
302
303         return (0);
304 }
305
306 static void
307 fdt_print_overlay_load_error(int err, const char *filename)
308 {
309
310         switch (err) {
311                 case FDT_ERR_NOTFOUND:
312                         printf("%s: failed to load file\n", filename);
313                         break;
314                 case -FDT_ERR_BADVERSION:
315                         printf("%s: incompatible blob version: %d, should be: %d\n",
316                             filename, fdt_version(fdtp),
317                             FDT_LAST_SUPPORTED_VERSION);
318                         break;
319                 default:
320                         /* libfdt errs are negative */
321                         if (err < 0)
322                                 printf("%s: error validating blob: %s\n",
323                                     filename, fdt_strerror(err));
324                         else
325                                 printf("%s: unknown load error\n", filename);
326                         break;
327         }
328 }
329
330 static int
331 fdt_load_dtb_overlays_string(const char * filenames)
332 {
333         char *names;
334         char *name, *name_ext;
335         char *comaptr;
336         int err, namesz;
337
338         debugf("fdt_load_dtb_overlays_string(%s)\n", filenames);
339
340         names = strdup(filenames);
341         if (names == NULL)
342                 return (1);
343         name = names;
344         do {
345                 comaptr = strchr(name, ',');
346                 if (comaptr)
347                         *comaptr = '\0';
348                 err = fdt_load_dtb_overlay(name);
349                 if (err == FDT_ERR_NOTFOUND) {
350                         /* Allocate enough to append ".dtbo" */
351                         namesz = strlen(name) + 6;
352                         name_ext = malloc(namesz);
353                         if (name_ext == NULL) {
354                                 fdt_print_overlay_load_error(err, name);
355                                 name = comaptr + 1;
356                                 continue;
357                         }
358                         snprintf(name_ext, namesz, "%s.dtbo", name);
359                         err = fdt_load_dtb_overlay(name_ext);
360                         free(name_ext);
361                 }
362                 /* Catch error with either initial load or fallback load */
363                 if (err != 0)
364                         fdt_print_overlay_load_error(err, name);
365                 name = comaptr + 1;
366         } while(comaptr);
367
368         free(names);
369         return (0);
370 }
371
372 /*
373  * fdt_check_overlay_compatible - check that the overlay_fdt is compatible with
374  * base_fdt before we attempt to apply it. It will need to re-calculate offsets
375  * in the base every time, rather than trying to cache them earlier in the
376  * process, because the overlay application process can/will invalidate a lot of
377  * offsets.
378  */
379 static int
380 fdt_check_overlay_compatible(void *base_fdt, void *overlay_fdt)
381 {
382         const char *compat;
383         int compat_len, ocompat_len;
384         int oroot_offset, root_offset;
385         int slidx, sllen;
386
387         oroot_offset = fdt_path_offset(overlay_fdt, "/");
388         if (oroot_offset < 0)
389                 return (oroot_offset);
390         /*
391          * If /compatible in the overlay does not exist or if it is empty, then
392          * we're automatically compatible. We do this for the sake of rapid
393          * overlay development for overlays that aren't intended to be deployed.
394          * The user assumes the risk of using an overlay without /compatible.
395          */
396         if (fdt_get_property(overlay_fdt, oroot_offset, "compatible",
397             &ocompat_len) == NULL || ocompat_len == 0)
398                 return (0);
399         root_offset = fdt_path_offset(base_fdt, "/");
400         if (root_offset < 0)
401                 return (root_offset);
402         /*
403          * However, an empty or missing /compatible on the base is an error,
404          * because allowing this offers no advantages.
405          */
406         if (fdt_get_property(base_fdt, root_offset, "compatible",
407             &compat_len) == NULL)
408                 return (compat_len);
409         else if(compat_len == 0)
410                 return (1);
411
412         slidx = 0;
413         compat = fdt_stringlist_get(overlay_fdt, oroot_offset, "compatible",
414             slidx, &sllen);
415         while (compat != NULL) {
416                 if (fdt_stringlist_search(base_fdt, root_offset, "compatible",
417                     compat) >= 0)
418                         return (0);
419                 ++slidx;
420                 compat = fdt_stringlist_get(overlay_fdt, oroot_offset,
421                     "compatible", slidx, &sllen);
422         };
423
424         /* We've exhausted the overlay's /compatible property... no match */
425         return (1);
426 }
427
428 void
429 fdt_apply_overlays()
430 {
431         struct preloaded_file *fp;
432         size_t max_overlay_size, next_fdtp_size;
433         size_t current_fdtp_size;
434         void *current_fdtp;
435         void *next_fdtp;
436         void *overlay;
437         int rv;
438
439         if ((fdtp == NULL) || (fdtp_size == 0))
440                 return;
441
442         max_overlay_size = 0;
443         for (fp = file_findfile(NULL, "dtbo"); fp != NULL; fp = fp->f_next) {
444                 if (max_overlay_size < fp->f_size)
445                         max_overlay_size = fp->f_size;
446         }
447
448         /* Nothing to apply */
449         if (max_overlay_size == 0)
450                 return;
451
452         overlay = malloc(max_overlay_size);
453         if (overlay == NULL) {
454                 printf("failed to allocate memory for DTB blob with overlays\n");
455                 return;
456         }
457         current_fdtp = fdtp;
458         current_fdtp_size = fdtp_size;
459         for (fp = file_findfile(NULL, "dtbo"); fp != NULL; fp = fp->f_next) {
460                 COPYOUT(fp->f_addr, overlay, fp->f_size);
461                 /* Check compatible first to avoid unnecessary allocation */
462                 rv = fdt_check_overlay_compatible(current_fdtp, overlay);
463                 if (rv != 0) {
464                         printf("DTB overlay '%s' not compatible\n", fp->f_name);
465                         continue;
466                 }
467                 printf("applying DTB overlay '%s'\n", fp->f_name);
468                 next_fdtp_size = current_fdtp_size + fp->f_size;
469                 next_fdtp = malloc(next_fdtp_size);
470                 if (next_fdtp == NULL) {
471                         /*
472                          * Output warning, then move on to applying other
473                          * overlays in case this one is simply too large.
474                          */
475                         printf("failed to allocate memory for overlay base\n");
476                         continue;
477                 }
478                 rv = fdt_open_into(current_fdtp, next_fdtp, next_fdtp_size);
479                 if (rv != 0) {
480                         free(next_fdtp);
481                         printf("failed to open base dtb into overlay base\n");
482                         continue;
483                 }
484                 /* Both overlay and next_fdtp may be modified in place */
485                 rv = fdt_overlay_apply(next_fdtp, overlay);
486                 if (rv == 0) {
487                         /* Rotate next -> current */
488                         if (current_fdtp != fdtp)
489                                 free(current_fdtp);
490                         current_fdtp = next_fdtp;
491                         current_fdtp_size = next_fdtp_size;
492                 } else {
493                         /*
494                          * Assume here that the base we tried to apply on is
495                          * either trashed or in an inconsistent state. Trying to
496                          * load it might work, but it's better to discard it and
497                          * play it safe. */
498                         free(next_fdtp);
499                         printf("failed to apply overlay: %s\n",
500                             fdt_strerror(rv));
501                 }
502         }
503         /* We could have failed to apply all overlays; then we do nothing */
504         if (current_fdtp != fdtp) {
505                 free(fdtp);
506                 fdtp = current_fdtp;
507                 fdtp_size = current_fdtp_size;
508         }
509         free(overlay);
510 }
511
512 int
513 fdt_is_setup(void)
514 {
515
516         if (fdtp != NULL)
517                 return (1);
518
519         return (0);
520 }
521
522 int
523 fdt_setup_fdtp()
524 {
525         struct preloaded_file *bfp;
526         vm_offset_t va;
527         
528         debugf("fdt_setup_fdtp()\n");
529
530         /* If we already loaded a file, use it. */
531         if ((bfp = file_findfile(NULL, "dtb")) != NULL) {
532                 if (fdt_load_dtb(bfp->f_addr) == 0) {
533                         printf("Using DTB from loaded file '%s'.\n", 
534                             bfp->f_name);
535                         fdt_platform_load_overlays();
536                         return (0);
537                 }
538         }
539
540         /* If we were given the address of a valid blob in memory, use it. */
541         if (fdt_to_load != NULL) {
542                 if (fdt_load_dtb_addr(fdt_to_load) == 0) {
543                         printf("Using DTB from memory address %p.\n",
544                             fdt_to_load);
545                         fdt_platform_load_overlays();
546                         return (0);
547                 }
548         }
549
550         if (fdt_platform_load_dtb() == 0) {
551                 fdt_platform_load_overlays();
552                 return (0);
553         }
554
555         /* If there is a dtb compiled into the kernel, use it. */
556         if ((va = fdt_find_static_dtb()) != 0) {
557                 if (fdt_load_dtb(va) == 0) {
558                         printf("Using DTB compiled into kernel.\n");
559                         return (0);
560                 }
561         }
562         
563         command_errmsg = "No device tree blob found!\n";
564         return (1);
565 }
566
567 #define fdt_strtovect(str, cellbuf, lim, cellsize) _fdt_strtovect((str), \
568     (cellbuf), (lim), (cellsize), 0);
569
570 /* Force using base 16 */
571 #define fdt_strtovectx(str, cellbuf, lim, cellsize) _fdt_strtovect((str), \
572     (cellbuf), (lim), (cellsize), 16);
573
574 static int
575 _fdt_strtovect(const char *str, void *cellbuf, int lim, unsigned char cellsize,
576     uint8_t base)
577 {
578         const char *buf = str;
579         const char *end = str + strlen(str) - 2;
580         uint32_t *u32buf = NULL;
581         uint8_t *u8buf = NULL;
582         int cnt = 0;
583
584         if (cellsize == sizeof(uint32_t))
585                 u32buf = (uint32_t *)cellbuf;
586         else
587                 u8buf = (uint8_t *)cellbuf;
588
589         if (lim == 0)
590                 return (0);
591
592         while (buf < end) {
593
594                 /* Skip white whitespace(s)/separators */
595                 while (!isxdigit(*buf) && buf < end)
596                         buf++;
597
598                 if (u32buf != NULL)
599                         u32buf[cnt] =
600                             cpu_to_fdt32((uint32_t)strtol(buf, NULL, base));
601
602                 else
603                         u8buf[cnt] = (uint8_t)strtol(buf, NULL, base);
604
605                 if (cnt + 1 <= lim - 1)
606                         cnt++;
607                 else
608                         break;
609                 buf++;
610                 /* Find another number */
611                 while ((isxdigit(*buf) || *buf == 'x') && buf < end)
612                         buf++;
613         }
614         return (cnt);
615 }
616
617 void
618 fdt_fixup_ethernet(const char *str, char *ethstr, int len)
619 {
620         uint8_t tmp_addr[6];
621
622         /* Convert macaddr string into a vector of uints */
623         fdt_strtovectx(str, &tmp_addr, 6, sizeof(uint8_t));
624         /* Set actual property to a value from vect */
625         fdt_setprop(fdtp, fdt_path_offset(fdtp, ethstr),
626             "local-mac-address", &tmp_addr, 6 * sizeof(uint8_t));
627 }
628
629 void
630 fdt_fixup_cpubusfreqs(unsigned long cpufreq, unsigned long busfreq)
631 {
632         int lo, o = 0, o2, maxo = 0, depth;
633         const uint32_t zero = 0;
634
635         /* We want to modify every subnode of /cpus */
636         o = fdt_path_offset(fdtp, "/cpus");
637         if (o < 0)
638                 return;
639
640         /* maxo should contain offset of node next to /cpus */
641         depth = 0;
642         maxo = o;
643         while (depth != -1)
644                 maxo = fdt_next_node(fdtp, maxo, &depth);
645
646         /* Find CPU frequency properties */
647         o = fdt_node_offset_by_prop_value(fdtp, o, "clock-frequency",
648             &zero, sizeof(uint32_t));
649
650         o2 = fdt_node_offset_by_prop_value(fdtp, o, "bus-frequency", &zero,
651             sizeof(uint32_t));
652
653         lo = MIN(o, o2);
654
655         while (o != -FDT_ERR_NOTFOUND && o2 != -FDT_ERR_NOTFOUND) {
656
657                 o = fdt_node_offset_by_prop_value(fdtp, lo,
658                     "clock-frequency", &zero, sizeof(uint32_t));
659
660                 o2 = fdt_node_offset_by_prop_value(fdtp, lo, "bus-frequency",
661                     &zero, sizeof(uint32_t));
662
663                 /* We're only interested in /cpus subnode(s) */
664                 if (lo > maxo)
665                         break;
666
667                 fdt_setprop_inplace_cell(fdtp, lo, "clock-frequency",
668                     (uint32_t)cpufreq);
669
670                 fdt_setprop_inplace_cell(fdtp, lo, "bus-frequency",
671                     (uint32_t)busfreq);
672
673                 lo = MIN(o, o2);
674         }
675 }
676
677 #ifdef notyet
678 static int
679 fdt_reg_valid(uint32_t *reg, int len, int addr_cells, int size_cells)
680 {
681         int cells_in_tuple, i, tuples, tuple_size;
682         uint32_t cur_start, cur_size;
683
684         cells_in_tuple = (addr_cells + size_cells);
685         tuple_size = cells_in_tuple * sizeof(uint32_t);
686         tuples = len / tuple_size;
687         if (tuples == 0)
688                 return (EINVAL);
689
690         for (i = 0; i < tuples; i++) {
691                 if (addr_cells == 2)
692                         cur_start = fdt64_to_cpu(reg[i * cells_in_tuple]);
693                 else
694                         cur_start = fdt32_to_cpu(reg[i * cells_in_tuple]);
695
696                 if (size_cells == 2)
697                         cur_size = fdt64_to_cpu(reg[i * cells_in_tuple + 2]);
698                 else
699                         cur_size = fdt32_to_cpu(reg[i * cells_in_tuple + 1]);
700
701                 if (cur_size == 0)
702                         return (EINVAL);
703
704                 debugf(" reg#%d (start: 0x%0x size: 0x%0x) valid!\n",
705                     i, cur_start, cur_size);
706         }
707         return (0);
708 }
709 #endif
710
711 void
712 fdt_fixup_memory(struct fdt_mem_region *region, size_t num)
713 {
714         struct fdt_mem_region *curmr;
715         uint32_t addr_cells, size_cells;
716         uint32_t *addr_cellsp, *size_cellsp;
717         int err, i, len, memory, root;
718         size_t realmrno;
719         uint8_t *buf, *sb;
720         uint64_t rstart, rsize;
721         int reserved;
722
723         root = fdt_path_offset(fdtp, "/");
724         if (root < 0) {
725                 sprintf(command_errbuf, "Could not find root node !");
726                 return;
727         }
728
729         memory = fdt_path_offset(fdtp, "/memory");
730         if (memory <= 0) {
731                 /* Create proper '/memory' node. */
732                 memory = fdt_add_subnode(fdtp, root, "memory");
733                 if (memory <= 0) {
734                         snprintf(command_errbuf, sizeof(command_errbuf),
735                             "Could not fixup '/memory' "
736                             "node, error code : %d!\n", memory);
737                         return;
738                 }
739
740                 err = fdt_setprop(fdtp, memory, "device_type", "memory",
741                     sizeof("memory"));
742
743                 if (err < 0)
744                         return;
745         }
746
747         addr_cellsp = (uint32_t *)fdt_getprop(fdtp, root, "#address-cells",
748             NULL);
749         size_cellsp = (uint32_t *)fdt_getprop(fdtp, root, "#size-cells", NULL);
750
751         if (addr_cellsp == NULL || size_cellsp == NULL) {
752                 snprintf(command_errbuf, sizeof(command_errbuf),
753                     "Could not fixup '/memory' node : "
754                     "%s %s property not found in root node!\n",
755                     (!addr_cellsp) ? "#address-cells" : "",
756                     (!size_cellsp) ? "#size-cells" : "");
757                 return;
758         }
759
760         addr_cells = fdt32_to_cpu(*addr_cellsp);
761         size_cells = fdt32_to_cpu(*size_cellsp);
762
763         /*
764          * Convert memreserve data to memreserve property
765          * Check if property already exists
766          */
767         reserved = fdt_num_mem_rsv(fdtp);
768         if (reserved &&
769             (fdt_getprop(fdtp, root, "memreserve", NULL) == NULL)) {
770                 len = (addr_cells + size_cells) * reserved * sizeof(uint32_t);
771                 sb = buf = (uint8_t *)malloc(len);
772                 if (!buf)
773                         return;
774
775                 bzero(buf, len);
776
777                 for (i = 0; i < reserved; i++) {
778                         if (fdt_get_mem_rsv(fdtp, i, &rstart, &rsize))
779                                 break;
780                         if (rsize) {
781                                 /* Ensure endianness, and put cells into a buffer */
782                                 if (addr_cells == 2)
783                                         *(uint64_t *)buf =
784                                             cpu_to_fdt64(rstart);
785                                 else
786                                         *(uint32_t *)buf =
787                                             cpu_to_fdt32(rstart);
788
789                                 buf += sizeof(uint32_t) * addr_cells;
790                                 if (size_cells == 2)
791                                         *(uint64_t *)buf =
792                                             cpu_to_fdt64(rsize);
793                                 else
794                                         *(uint32_t *)buf =
795                                             cpu_to_fdt32(rsize);
796
797                                 buf += sizeof(uint32_t) * size_cells;
798                         }
799                 }
800
801                 /* Set property */
802                 if ((err = fdt_setprop(fdtp, root, "memreserve", sb, len)) < 0)
803                         printf("Could not fixup 'memreserve' property.\n");
804
805                 free(sb);
806         } 
807
808         /* Count valid memory regions entries in sysinfo. */
809         realmrno = num;
810         for (i = 0; i < num; i++)
811                 if (region[i].start == 0 && region[i].size == 0)
812                         realmrno--;
813
814         if (realmrno == 0) {
815                 sprintf(command_errbuf, "Could not fixup '/memory' node : "
816                     "sysinfo doesn't contain valid memory regions info!\n");
817                 return;
818         }
819
820         len = (addr_cells + size_cells) * realmrno * sizeof(uint32_t);
821         sb = buf = (uint8_t *)malloc(len);
822         if (!buf)
823                 return;
824
825         bzero(buf, len);
826
827         for (i = 0; i < num; i++) {
828                 curmr = &region[i];
829                 if (curmr->size != 0) {
830                         /* Ensure endianness, and put cells into a buffer */
831                         if (addr_cells == 2)
832                                 *(uint64_t *)buf =
833                                     cpu_to_fdt64(curmr->start);
834                         else
835                                 *(uint32_t *)buf =
836                                     cpu_to_fdt32(curmr->start);
837
838                         buf += sizeof(uint32_t) * addr_cells;
839                         if (size_cells == 2)
840                                 *(uint64_t *)buf =
841                                     cpu_to_fdt64(curmr->size);
842                         else
843                                 *(uint32_t *)buf =
844                                     cpu_to_fdt32(curmr->size);
845
846                         buf += sizeof(uint32_t) * size_cells;
847                 }
848         }
849
850         /* Set property */
851         if ((err = fdt_setprop(fdtp, memory, "reg", sb, len)) < 0)
852                 sprintf(command_errbuf, "Could not fixup '/memory' node.\n");
853
854         free(sb);
855 }
856
857 void
858 fdt_fixup_stdout(const char *str)
859 {
860         char *ptr;
861         int len, no, sero;
862         const struct fdt_property *prop;
863         char *tmp[10];
864
865         ptr = (char *)str + strlen(str) - 1;
866         while (ptr > str && isdigit(*(str - 1)))
867                 str--;
868
869         if (ptr == str)
870                 return;
871
872         no = fdt_path_offset(fdtp, "/chosen");
873         if (no < 0)
874                 return;
875
876         prop = fdt_get_property(fdtp, no, "stdout", &len);
877
878         /* If /chosen/stdout does not extist, create it */
879         if (prop == NULL || (prop != NULL && len == 0)) {
880
881                 bzero(tmp, 10 * sizeof(char));
882                 strcpy((char *)&tmp, "serial");
883                 if (strlen(ptr) > 3)
884                         /* Serial number too long */
885                         return;
886
887                 strncpy((char *)tmp + 6, ptr, 3);
888                 sero = fdt_path_offset(fdtp, (const char *)tmp);
889                 if (sero < 0)
890                         /*
891                          * If serial device we're trying to assign
892                          * stdout to doesn't exist in DT -- return.
893                          */
894                         return;
895
896                 fdt_setprop(fdtp, no, "stdout", &tmp,
897                     strlen((char *)&tmp) + 1);
898                 fdt_setprop(fdtp, no, "stdin", &tmp,
899                     strlen((char *)&tmp) + 1);
900         }
901 }
902
903 void
904 fdt_load_dtb_overlays(const char *extras)
905 {
906         const char *s;
907
908         /* Any extra overlays supplied by pre-loader environment */
909         if (extras != NULL && *extras != '\0') {
910                 printf("Loading DTB overlays: '%s'\n", extras);
911                 fdt_load_dtb_overlays_string(extras);
912         }
913
914         /* Any overlays supplied by loader environment */
915         s = getenv("fdt_overlays");
916         if (s != NULL && *s != '\0') {
917                 printf("Loading DTB overlays: '%s'\n", s);
918                 fdt_load_dtb_overlays_string(s);
919         }
920 }
921
922 /*
923  * Locate the blob, fix it up and return its location.
924  */
925 static int
926 fdt_fixup(void)
927 {
928         int chosen;
929
930         debugf("fdt_fixup()\n");
931
932         if (fdtp == NULL && fdt_setup_fdtp() != 0)
933                 return (0);
934
935         /* Create /chosen node (if not exists) */
936         if ((chosen = fdt_subnode_offset(fdtp, 0, "chosen")) ==
937             -FDT_ERR_NOTFOUND)
938                 chosen = fdt_add_subnode(fdtp, 0, "chosen");
939
940         /* Value assigned to fixup-applied does not matter. */
941         if (fdt_getprop(fdtp, chosen, "fixup-applied", NULL))
942                 return (1);
943
944         fdt_platform_fixups();
945
946         /*
947          * Re-fetch the /chosen subnode; our fixups may apply overlays or add
948          * nodes/properties that invalidate the offset we grabbed or created
949          * above, so we can no longer trust it.
950          */
951         chosen = fdt_subnode_offset(fdtp, 0, "chosen");
952         fdt_setprop(fdtp, chosen, "fixup-applied", NULL, 0);
953         return (1);
954 }
955
956 /*
957  * Copy DTB blob to specified location and return size
958  */
959 int
960 fdt_copy(vm_offset_t va)
961 {
962         int err;
963         debugf("fdt_copy va 0x%08x\n", va);
964         if (fdtp == NULL) {
965                 err = fdt_setup_fdtp();
966                 if (err) {
967                         printf("No valid device tree blob found!\n");
968                         return (0);
969                 }
970         }
971
972         if (fdt_fixup() == 0)
973                 return (0);
974
975         COPYIN(fdtp, va, fdtp_size);
976         return (fdtp_size);
977 }
978
979
980
981 int
982 command_fdt_internal(int argc, char *argv[])
983 {
984         cmdf_t *cmdh;
985         int flags;
986         int i, err;
987
988         if (argc < 2) {
989                 command_errmsg = "usage is 'fdt <command> [<args>]";
990                 return (CMD_ERROR);
991         }
992
993         /*
994          * Validate fdt <command>.
995          */
996         i = 0;
997         cmdh = NULL;
998         while (!(commands[i].name == NULL)) {
999                 if (strcmp(argv[1], commands[i].name) == 0) {
1000                         /* found it */
1001                         cmdh = commands[i].handler;
1002                         flags = commands[i].flags;
1003                         break;
1004                 }
1005                 i++;
1006         }
1007         if (cmdh == NULL) {
1008                 command_errmsg = "unknown command";
1009                 return (CMD_ERROR);
1010         }
1011
1012         if (flags & CMD_REQUIRES_BLOB) {
1013                 /*
1014                  * Check if uboot env vars were parsed already. If not, do it now.
1015                  */
1016                 if (fdt_fixup() == 0)
1017                         return (CMD_ERROR);
1018         }
1019
1020         /*
1021          * Call command handler.
1022          */
1023         err = (*cmdh)(argc, argv);
1024
1025         return (err);
1026 }
1027
1028 static int
1029 fdt_cmd_addr(int argc, char *argv[])
1030 {
1031         struct preloaded_file *fp;
1032         struct fdt_header *hdr;
1033         const char *addr;
1034         char *cp;
1035
1036         fdt_to_load = NULL;
1037
1038         if (argc > 2)
1039                 addr = argv[2];
1040         else {
1041                 sprintf(command_errbuf, "no address specified");
1042                 return (CMD_ERROR);
1043         }
1044
1045         hdr = (struct fdt_header *)strtoul(addr, &cp, 16);
1046         if (cp == addr) {
1047                 snprintf(command_errbuf, sizeof(command_errbuf),
1048                     "Invalid address: %s", addr);
1049                 return (CMD_ERROR);
1050         }
1051
1052         while ((fp = file_findfile(NULL, "dtb")) != NULL) {
1053                 file_discard(fp);
1054         }
1055
1056         fdt_to_load = hdr;
1057         return (CMD_OK);
1058 }
1059
1060 static int
1061 fdt_cmd_cd(int argc, char *argv[])
1062 {
1063         char *path;
1064         char tmp[FDT_CWD_LEN];
1065         int len, o;
1066
1067         path = (argc > 2) ? argv[2] : "/";
1068
1069         if (path[0] == '/') {
1070                 len = strlen(path);
1071                 if (len >= FDT_CWD_LEN)
1072                         goto fail;
1073         } else {
1074                 /* Handle path specification relative to cwd */
1075                 len = strlen(cwd) + strlen(path) + 1;
1076                 if (len >= FDT_CWD_LEN)
1077                         goto fail;
1078
1079                 strcpy(tmp, cwd);
1080                 strcat(tmp, "/");
1081                 strcat(tmp, path);
1082                 path = tmp;
1083         }
1084
1085         o = fdt_path_offset(fdtp, path);
1086         if (o < 0) {
1087                 snprintf(command_errbuf, sizeof(command_errbuf),
1088                     "could not find node: '%s'", path);
1089                 return (CMD_ERROR);
1090         }
1091
1092         strcpy(cwd, path);
1093         return (CMD_OK);
1094
1095 fail:
1096         snprintf(command_errbuf, sizeof(command_errbuf),
1097             "path too long: %d, max allowed: %d", len, FDT_CWD_LEN - 1);
1098         return (CMD_ERROR);
1099 }
1100
1101 static int
1102 fdt_cmd_hdr(int argc __unused, char *argv[] __unused)
1103 {
1104         char line[80];
1105         int ver;
1106
1107         if (fdtp == NULL) {
1108                 command_errmsg = "no device tree blob pointer?!";
1109                 return (CMD_ERROR);
1110         }
1111
1112         ver = fdt_version(fdtp);
1113         pager_open();
1114         sprintf(line, "\nFlattened device tree header (%p):\n", fdtp);
1115         if (pager_output(line))
1116                 goto out;
1117         sprintf(line, " magic                   = 0x%08x\n", fdt_magic(fdtp));
1118         if (pager_output(line))
1119                 goto out;
1120         sprintf(line, " size                    = %d\n", fdt_totalsize(fdtp));
1121         if (pager_output(line))
1122                 goto out;
1123         sprintf(line, " off_dt_struct           = 0x%08x\n",
1124             fdt_off_dt_struct(fdtp));
1125         if (pager_output(line))
1126                 goto out;
1127         sprintf(line, " off_dt_strings          = 0x%08x\n",
1128             fdt_off_dt_strings(fdtp));
1129         if (pager_output(line))
1130                 goto out;
1131         sprintf(line, " off_mem_rsvmap          = 0x%08x\n",
1132             fdt_off_mem_rsvmap(fdtp));
1133         if (pager_output(line))
1134                 goto out;
1135         sprintf(line, " version                 = %d\n", ver); 
1136         if (pager_output(line))
1137                 goto out;
1138         sprintf(line, " last compatible version = %d\n",
1139             fdt_last_comp_version(fdtp));
1140         if (pager_output(line))
1141                 goto out;
1142         if (ver >= 2) {
1143                 sprintf(line, " boot_cpuid              = %d\n",
1144                     fdt_boot_cpuid_phys(fdtp));
1145                 if (pager_output(line))
1146                         goto out;
1147         }
1148         if (ver >= 3) {
1149                 sprintf(line, " size_dt_strings         = %d\n",
1150                     fdt_size_dt_strings(fdtp));
1151                 if (pager_output(line))
1152                         goto out;
1153         }
1154         if (ver >= 17) {
1155                 sprintf(line, " size_dt_struct          = %d\n",
1156                     fdt_size_dt_struct(fdtp));
1157                 if (pager_output(line))
1158                         goto out;
1159         }
1160 out:
1161         pager_close();
1162
1163         return (CMD_OK);
1164 }
1165
1166 static int
1167 fdt_cmd_ls(int argc, char *argv[])
1168 {
1169         const char *prevname[FDT_MAX_DEPTH] = { NULL };
1170         const char *name;
1171         char *path;
1172         int i, o, depth;
1173
1174         path = (argc > 2) ? argv[2] : NULL;
1175         if (path == NULL)
1176                 path = cwd;
1177
1178         o = fdt_path_offset(fdtp, path);
1179         if (o < 0) {
1180                 snprintf(command_errbuf, sizeof(command_errbuf),
1181                     "could not find node: '%s'", path);
1182                 return (CMD_ERROR);
1183         }
1184
1185         for (depth = 0;
1186             (o >= 0) && (depth >= 0);
1187             o = fdt_next_node(fdtp, o, &depth)) {
1188
1189                 name = fdt_get_name(fdtp, o, NULL);
1190
1191                 if (depth > FDT_MAX_DEPTH) {
1192                         printf("max depth exceeded: %d\n", depth);
1193                         continue;
1194                 }
1195
1196                 prevname[depth] = name;
1197
1198                 /* Skip root (i = 1) when printing devices */
1199                 for (i = 1; i <= depth; i++) {
1200                         if (prevname[i] == NULL)
1201                                 break;
1202
1203                         if (strcmp(cwd, "/") == 0)
1204                                 printf("/");
1205                         printf("%s", prevname[i]);
1206                 }
1207                 printf("\n");
1208         }
1209
1210         return (CMD_OK);
1211 }
1212
1213 static __inline int
1214 isprint(int c)
1215 {
1216
1217         return (c >= ' ' && c <= 0x7e);
1218 }
1219
1220 static int
1221 fdt_isprint(const void *data, int len, int *count)
1222 {
1223         const char *d;
1224         char ch;
1225         int yesno, i;
1226
1227         if (len == 0)
1228                 return (0);
1229
1230         d = (const char *)data;
1231         if (d[len - 1] != '\0')
1232                 return (0);
1233
1234         *count = 0;
1235         yesno = 1;
1236         for (i = 0; i < len; i++) {
1237                 ch = *(d + i);
1238                 if (isprint(ch) || (ch == '\0' && i > 0)) {
1239                         /* Count strings */
1240                         if (ch == '\0')
1241                                 (*count)++;
1242                         continue;
1243                 }
1244
1245                 yesno = 0;
1246                 break;
1247         }
1248
1249         return (yesno);
1250 }
1251
1252 static int
1253 fdt_data_str(const void *data, int len, int count, char **buf)
1254 {
1255         char *b, *tmp;
1256         const char *d;
1257         int buf_len, i, l;
1258
1259         /*
1260          * Calculate the length for the string and allocate memory.
1261          *
1262          * Note that 'len' already includes at least one terminator.
1263          */
1264         buf_len = len;
1265         if (count > 1) {
1266                 /*
1267                  * Each token had already a terminator buried in 'len', but we
1268                  * only need one eventually, don't count space for these.
1269                  */
1270                 buf_len -= count - 1;
1271
1272                 /* Each consecutive token requires a ", " separator. */
1273                 buf_len += count * 2;
1274         }
1275
1276         /* Add some space for surrounding double quotes. */
1277         buf_len += count * 2;
1278
1279         /* Note that string being put in 'tmp' may be as big as 'buf_len'. */
1280         b = (char *)malloc(buf_len);
1281         tmp = (char *)malloc(buf_len);
1282         if (b == NULL)
1283                 goto error;
1284
1285         if (tmp == NULL) {
1286                 free(b);
1287                 goto error;
1288         }
1289
1290         b[0] = '\0';
1291
1292         /*
1293          * Now that we have space, format the string.
1294          */
1295         i = 0;
1296         do {
1297                 d = (const char *)data + i;
1298                 l = strlen(d) + 1;
1299
1300                 sprintf(tmp, "\"%s\"%s", d,
1301                     (i + l) < len ?  ", " : "");
1302                 strcat(b, tmp);
1303
1304                 i += l;
1305
1306         } while (i < len);
1307         *buf = b;
1308
1309         free(tmp);
1310
1311         return (0);
1312 error:
1313         return (1);
1314 }
1315
1316 static int
1317 fdt_data_cell(const void *data, int len, char **buf)
1318 {
1319         char *b, *tmp;
1320         const uint32_t *c;
1321         int count, i, l;
1322
1323         /* Number of cells */
1324         count = len / 4;
1325
1326         /*
1327          * Calculate the length for the string and allocate memory.
1328          */
1329
1330         /* Each byte translates to 2 output characters */
1331         l = len * 2;
1332         if (count > 1) {
1333                 /* Each consecutive cell requires a " " separator. */
1334                 l += (count - 1) * 1;
1335         }
1336         /* Each cell will have a "0x" prefix */
1337         l += count * 2;
1338         /* Space for surrounding <> and terminator */
1339         l += 3;
1340
1341         b = (char *)malloc(l);
1342         tmp = (char *)malloc(l);
1343         if (b == NULL)
1344                 goto error;
1345
1346         if (tmp == NULL) {
1347                 free(b);
1348                 goto error;
1349         }
1350
1351         b[0] = '\0';
1352         strcat(b, "<");
1353
1354         for (i = 0; i < len; i += 4) {
1355                 c = (const uint32_t *)((const uint8_t *)data + i);
1356                 sprintf(tmp, "0x%08x%s", fdt32_to_cpu(*c),
1357                     i < (len - 4) ? " " : "");
1358                 strcat(b, tmp);
1359         }
1360         strcat(b, ">");
1361         *buf = b;
1362
1363         free(tmp);
1364
1365         return (0);
1366 error:
1367         return (1);
1368 }
1369
1370 static int
1371 fdt_data_bytes(const void *data, int len, char **buf)
1372 {
1373         char *b, *tmp;
1374         const char *d;
1375         int i, l;
1376
1377         /*
1378          * Calculate the length for the string and allocate memory.
1379          */
1380
1381         /* Each byte translates to 2 output characters */
1382         l = len * 2;
1383         if (len > 1)
1384                 /* Each consecutive byte requires a " " separator. */
1385                 l += (len - 1) * 1;
1386         /* Each byte will have a "0x" prefix */
1387         l += len * 2;
1388         /* Space for surrounding [] and terminator. */
1389         l += 3;
1390
1391         b = (char *)malloc(l);
1392         tmp = (char *)malloc(l);
1393         if (b == NULL)
1394                 goto error;
1395
1396         if (tmp == NULL) {
1397                 free(b);
1398                 goto error;
1399         }
1400
1401         b[0] = '\0';
1402         strcat(b, "[");
1403
1404         for (i = 0, d = data; i < len; i++) {
1405                 sprintf(tmp, "0x%02x%s", d[i], i < len - 1 ? " " : "");
1406                 strcat(b, tmp);
1407         }
1408         strcat(b, "]");
1409         *buf = b;
1410
1411         free(tmp);
1412
1413         return (0);
1414 error:
1415         return (1);
1416 }
1417
1418 static int
1419 fdt_data_fmt(const void *data, int len, char **buf)
1420 {
1421         int count;
1422
1423         if (len == 0) {
1424                 *buf = NULL;
1425                 return (1);
1426         }
1427
1428         if (fdt_isprint(data, len, &count))
1429                 return (fdt_data_str(data, len, count, buf));
1430
1431         else if ((len % 4) == 0)
1432                 return (fdt_data_cell(data, len, buf));
1433
1434         else
1435                 return (fdt_data_bytes(data, len, buf));
1436 }
1437
1438 static int
1439 fdt_prop(int offset)
1440 {
1441         char *line, *buf;
1442         const struct fdt_property *prop;
1443         const char *name;
1444         const void *data;
1445         int len, rv;
1446
1447         line = NULL;
1448         prop = fdt_offset_ptr(fdtp, offset, sizeof(*prop));
1449         if (prop == NULL)
1450                 return (1);
1451
1452         name = fdt_string(fdtp, fdt32_to_cpu(prop->nameoff));
1453         len = fdt32_to_cpu(prop->len);
1454
1455         rv = 0;
1456         buf = NULL;
1457         if (len == 0) {
1458                 /* Property without value */
1459                 line = (char *)malloc(strlen(name) + 2);
1460                 if (line == NULL) {
1461                         rv = 2;
1462                         goto out2;
1463                 }
1464                 sprintf(line, "%s\n", name);
1465                 goto out1;
1466         }
1467
1468         /*
1469          * Process property with value
1470          */
1471         data = prop->data;
1472
1473         if (fdt_data_fmt(data, len, &buf) != 0) {
1474                 rv = 3;
1475                 goto out2;
1476         }
1477
1478         line = (char *)malloc(strlen(name) + strlen(FDT_PROP_SEP) +
1479             strlen(buf) + 2);
1480         if (line == NULL) {
1481                 sprintf(command_errbuf, "could not allocate space for string");
1482                 rv = 4;
1483                 goto out2;
1484         }
1485
1486         sprintf(line, "%s" FDT_PROP_SEP "%s\n", name, buf);
1487
1488 out1:
1489         pager_open();
1490         pager_output(line);
1491         pager_close();
1492
1493 out2:
1494         if (buf)
1495                 free(buf);
1496
1497         if (line)
1498                 free(line);
1499
1500         return (rv);
1501 }
1502
1503 static int
1504 fdt_modprop(int nodeoff, char *propname, void *value, char mode)
1505 {
1506         uint32_t cells[100];
1507         const char *buf;
1508         int len, rv;
1509         const struct fdt_property *p;
1510
1511         p = fdt_get_property(fdtp, nodeoff, propname, NULL);
1512
1513         if (p != NULL) {
1514                 if (mode == 1) {
1515                          /* Adding inexistant value in mode 1 is forbidden */
1516                         sprintf(command_errbuf, "property already exists!");
1517                         return (CMD_ERROR);
1518                 }
1519         } else if (mode == 0) {
1520                 sprintf(command_errbuf, "property does not exist!");
1521                 return (CMD_ERROR);
1522         }
1523         rv = 0;
1524         buf = value;
1525
1526         switch (*buf) {
1527         case '&':
1528                 /* phandles */
1529                 break;
1530         case '<':
1531                 /* Data cells */
1532                 len = fdt_strtovect(buf, (void *)&cells, 100,
1533                     sizeof(uint32_t));
1534
1535                 rv = fdt_setprop(fdtp, nodeoff, propname, &cells,
1536                     len * sizeof(uint32_t));
1537                 break;
1538         case '[':
1539                 /* Data bytes */
1540                 len = fdt_strtovect(buf, (void *)&cells, 100,
1541                     sizeof(uint8_t));
1542
1543                 rv = fdt_setprop(fdtp, nodeoff, propname, &cells,
1544                     len * sizeof(uint8_t));
1545                 break;
1546         case '"':
1547         default:
1548                 /* Default -- string */
1549                 rv = fdt_setprop_string(fdtp, nodeoff, propname, value);
1550                 break;
1551         }
1552
1553         if (rv != 0) {
1554                 if (rv == -FDT_ERR_NOSPACE)
1555                         sprintf(command_errbuf,
1556                             "Device tree blob is too small!\n");
1557                 else
1558                         sprintf(command_errbuf,
1559                             "Could not add/modify property!\n");
1560         }
1561         return (rv);
1562 }
1563
1564 /* Merge strings from argv into a single string */
1565 static int
1566 fdt_merge_strings(int argc, char *argv[], int start, char **buffer)
1567 {
1568         char *buf;
1569         int i, idx, sz;
1570
1571         *buffer = NULL;
1572         sz = 0;
1573
1574         for (i = start; i < argc; i++)
1575                 sz += strlen(argv[i]);
1576
1577         /* Additional bytes for whitespaces between args */
1578         sz += argc - start;
1579
1580         buf = (char *)malloc(sizeof(char) * sz);
1581         if (buf == NULL) {
1582                 sprintf(command_errbuf, "could not allocate space "
1583                     "for string");
1584                 return (1);
1585         }
1586         bzero(buf, sizeof(char) * sz);
1587
1588         idx = 0;
1589         for (i = start, idx = 0; i < argc; i++) {
1590                 strcpy(buf + idx, argv[i]);
1591                 idx += strlen(argv[i]);
1592                 buf[idx] = ' ';
1593                 idx++;
1594         }
1595         buf[sz - 1] = '\0';
1596         *buffer = buf;
1597         return (0);
1598 }
1599
1600 /* Extract offset and name of node/property from a given path */
1601 static int
1602 fdt_extract_nameloc(char **pathp, char **namep, int *nodeoff)
1603 {
1604         int o;
1605         char *path = *pathp, *name = NULL, *subpath = NULL;
1606
1607         subpath = strrchr(path, '/');
1608         if (subpath == NULL) {
1609                 o = fdt_path_offset(fdtp, cwd);
1610                 name = path;
1611                 path = (char *)&cwd;
1612         } else {
1613                 *subpath = '\0';
1614                 if (strlen(path) == 0)
1615                         path = cwd;
1616
1617                 name = subpath + 1;
1618                 o = fdt_path_offset(fdtp, path);
1619         }
1620
1621         if (strlen(name) == 0) {
1622                 sprintf(command_errbuf, "name not specified");
1623                 return (1);
1624         }
1625         if (o < 0) {
1626                 snprintf(command_errbuf, sizeof(command_errbuf),
1627                     "could not find node: '%s'", path);
1628                 return (1);
1629         }
1630         *namep = name;
1631         *nodeoff = o;
1632         *pathp = path;
1633         return (0);
1634 }
1635
1636 static int
1637 fdt_cmd_prop(int argc, char *argv[])
1638 {
1639         char *path, *propname, *value;
1640         int o, next, depth, rv;
1641         uint32_t tag;
1642
1643         path = (argc > 2) ? argv[2] : NULL;
1644
1645         value = NULL;
1646
1647         if (argc > 3) {
1648                 /* Merge property value strings into one */
1649                 if (fdt_merge_strings(argc, argv, 3, &value) != 0)
1650                         return (CMD_ERROR);
1651         } else
1652                 value = NULL;
1653
1654         if (path == NULL)
1655                 path = cwd;
1656
1657         rv = CMD_OK;
1658
1659         if (value) {
1660                 /* If value is specified -- try to modify prop. */
1661                 if (fdt_extract_nameloc(&path, &propname, &o) != 0)
1662                         return (CMD_ERROR);
1663
1664                 rv = fdt_modprop(o, propname, value, 0);
1665                 if (rv)
1666                         return (CMD_ERROR);
1667                 return (CMD_OK);
1668
1669         }
1670         /* User wants to display properties */
1671         o = fdt_path_offset(fdtp, path);
1672
1673         if (o < 0) {
1674                 snprintf(command_errbuf, sizeof(command_errbuf),
1675                     "could not find node: '%s'", path);
1676                 rv = CMD_ERROR;
1677                 goto out;
1678         }
1679
1680         depth = 0;
1681         while (depth >= 0) {
1682                 tag = fdt_next_tag(fdtp, o, &next);
1683                 switch (tag) {
1684                 case FDT_NOP:
1685                         break;
1686                 case FDT_PROP:
1687                         if (depth > 1)
1688                                 /* Don't process properties of nested nodes */
1689                                 break;
1690
1691                         if (fdt_prop(o) != 0) {
1692                                 sprintf(command_errbuf, "could not process "
1693                                     "property");
1694                                 rv = CMD_ERROR;
1695                                 goto out;
1696                         }
1697                         break;
1698                 case FDT_BEGIN_NODE:
1699                         depth++;
1700                         if (depth > FDT_MAX_DEPTH) {
1701                                 printf("warning: nesting too deep: %d\n",
1702                                     depth);
1703                                 goto out;
1704                         }
1705                         break;
1706                 case FDT_END_NODE:
1707                         depth--;
1708                         if (depth == 0)
1709                                 /*
1710                                  * This is the end of our starting node, force
1711                                  * the loop finish.
1712                                  */
1713                                 depth--;
1714                         break;
1715                 }
1716                 o = next;
1717         }
1718 out:
1719         return (rv);
1720 }
1721
1722 static int
1723 fdt_cmd_mkprop(int argc, char *argv[])
1724 {
1725         int o;
1726         char *path, *propname, *value;
1727
1728         path = (argc > 2) ? argv[2] : NULL;
1729
1730         value = NULL;
1731
1732         if (argc > 3) {
1733                 /* Merge property value strings into one */
1734                 if (fdt_merge_strings(argc, argv, 3, &value) != 0)
1735                         return (CMD_ERROR);
1736         } else
1737                 value = NULL;
1738
1739         if (fdt_extract_nameloc(&path, &propname, &o) != 0)
1740                 return (CMD_ERROR);
1741
1742         if (fdt_modprop(o, propname, value, 1))
1743                 return (CMD_ERROR);
1744
1745         return (CMD_OK);
1746 }
1747
1748 static int
1749 fdt_cmd_rm(int argc, char *argv[])
1750 {
1751         int o, rv;
1752         char *path = NULL, *propname;
1753
1754         if (argc > 2)
1755                 path = argv[2];
1756         else {
1757                 sprintf(command_errbuf, "no node/property name specified");
1758                 return (CMD_ERROR);
1759         }
1760
1761         o = fdt_path_offset(fdtp, path);
1762         if (o < 0) {
1763                 /* If node not found -- try to find & delete property */
1764                 if (fdt_extract_nameloc(&path, &propname, &o) != 0)
1765                         return (CMD_ERROR);
1766
1767                 if ((rv = fdt_delprop(fdtp, o, propname)) != 0) {
1768                         snprintf(command_errbuf, sizeof(command_errbuf),
1769                             "could not delete %s\n",
1770                             (rv == -FDT_ERR_NOTFOUND) ?
1771                             "(property/node does not exist)" : "");
1772                         return (CMD_ERROR);
1773
1774                 } else
1775                         return (CMD_OK);
1776         }
1777         /* If node exists -- remove node */
1778         rv = fdt_del_node(fdtp, o);
1779         if (rv) {
1780                 sprintf(command_errbuf, "could not delete node");
1781                 return (CMD_ERROR);
1782         }
1783         return (CMD_OK);
1784 }
1785
1786 static int
1787 fdt_cmd_mknode(int argc, char *argv[])
1788 {
1789         int o, rv;
1790         char *path = NULL, *nodename = NULL;
1791
1792         if (argc > 2)
1793                 path = argv[2];
1794         else {
1795                 sprintf(command_errbuf, "no node name specified");
1796                 return (CMD_ERROR);
1797         }
1798
1799         if (fdt_extract_nameloc(&path, &nodename, &o) != 0)
1800                 return (CMD_ERROR);
1801
1802         rv = fdt_add_subnode(fdtp, o, nodename);
1803
1804         if (rv < 0) {
1805                 if (rv == -FDT_ERR_NOSPACE)
1806                         sprintf(command_errbuf,
1807                             "Device tree blob is too small!\n");
1808                 else
1809                         sprintf(command_errbuf,
1810                             "Could not add node!\n");
1811                 return (CMD_ERROR);
1812         }
1813         return (CMD_OK);
1814 }
1815
1816 static int
1817 fdt_cmd_pwd(int argc, char *argv[])
1818 {
1819         char line[FDT_CWD_LEN];
1820
1821         pager_open();
1822         sprintf(line, "%s\n", cwd);
1823         pager_output(line);
1824         pager_close();
1825         return (CMD_OK);
1826 }
1827
1828 static int
1829 fdt_cmd_mres(int argc, char *argv[])
1830 {
1831         uint64_t start, size;
1832         int i, total;
1833         char line[80];
1834
1835         pager_open();
1836         total = fdt_num_mem_rsv(fdtp);
1837         if (total > 0) {
1838                 if (pager_output("Reserved memory regions:\n"))
1839                         goto out;
1840                 for (i = 0; i < total; i++) {
1841                         fdt_get_mem_rsv(fdtp, i, &start, &size);
1842                         sprintf(line, "reg#%d: (start: 0x%jx, size: 0x%jx)\n", 
1843                             i, start, size);
1844                         if (pager_output(line))
1845                                 goto out;
1846                 }
1847         } else
1848                 pager_output("No reserved memory regions\n");
1849 out:
1850         pager_close();
1851
1852         return (CMD_OK);
1853 }
1854
1855 static int
1856 fdt_cmd_nyi(int argc, char *argv[])
1857 {
1858
1859         printf("command not yet implemented\n");
1860         return (CMD_ERROR);
1861 }