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