]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - sys/boot/fdt/fdt_loader_cmd.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / sys / boot / 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 <fdt.h>
35 #include <libfdt.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   6
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 /* Location of FDT in kernel or module. */
71 /* This won't be set if FDT is loaded from disk or memory. */
72 /* If it is set, we'll update it when fdt_copy() gets called. */
73 static vm_offset_t fdtp_va = 0;
74
75 static int fdt_load_dtb(vm_offset_t va);
76
77 static int fdt_cmd_nyi(int argc, char *argv[]);
78
79 static int fdt_cmd_addr(int argc, char *argv[]);
80 static int fdt_cmd_mkprop(int argc, char *argv[]);
81 static int fdt_cmd_cd(int argc, char *argv[]);
82 static int fdt_cmd_hdr(int argc, char *argv[]);
83 static int fdt_cmd_ls(int argc, char *argv[]);
84 static int fdt_cmd_prop(int argc, char *argv[]);
85 static int fdt_cmd_pwd(int argc, char *argv[]);
86 static int fdt_cmd_rm(int argc, char *argv[]);
87 static int fdt_cmd_mknode(int argc, char *argv[]);
88 static int fdt_cmd_mres(int argc, char *argv[]);
89
90 typedef int cmdf_t(int, char *[]);
91
92 struct cmdtab {
93         const char      *name;
94         cmdf_t          *handler;
95         int             flags;
96 };
97
98 static const struct cmdtab commands[] = {
99         { "addr", &fdt_cmd_addr,        0 },
100         { "alias", &fdt_cmd_nyi,        0 },
101         { "cd", &fdt_cmd_cd,            CMD_REQUIRES_BLOB },
102         { "header", &fdt_cmd_hdr,       CMD_REQUIRES_BLOB },
103         { "ls", &fdt_cmd_ls,            CMD_REQUIRES_BLOB },
104         { "mknode", &fdt_cmd_mknode,    CMD_REQUIRES_BLOB },
105         { "mkprop", &fdt_cmd_mkprop,    CMD_REQUIRES_BLOB },
106         { "mres", &fdt_cmd_mres,        CMD_REQUIRES_BLOB },
107         { "prop", &fdt_cmd_prop,        CMD_REQUIRES_BLOB },
108         { "pwd", &fdt_cmd_pwd,          CMD_REQUIRES_BLOB },
109         { "rm", &fdt_cmd_rm,            CMD_REQUIRES_BLOB },
110         { NULL, NULL }
111 };
112
113 static char cwd[FDT_CWD_LEN] = "/";
114
115 static vm_offset_t
116 fdt_find_static_dtb()
117 {
118         Elf_Ehdr *ehdr;
119         Elf_Shdr *shdr;
120         Elf_Sym sym;
121         vm_offset_t strtab, symtab, fdt_start;
122         uint64_t offs;
123         struct preloaded_file *kfp;
124         struct file_metadata *md;
125         char *strp;
126         int i, sym_count;
127
128         debugf("fdt_find_static_dtb()\n");
129
130         sym_count = symtab = strtab = 0;
131         strp = NULL;
132
133         offs = __elfN(relocation_offset);
134
135         kfp = file_findfile(NULL, NULL);
136         if (kfp == NULL)
137                 return (0);
138
139         /* Locate the dynamic symbols and strtab. */
140         md = file_findmetadata(kfp, MODINFOMD_ELFHDR);
141         if (md == NULL)
142                 return (0);
143         ehdr = (Elf_Ehdr *)md->md_data;
144
145         md = file_findmetadata(kfp, MODINFOMD_SHDR);
146         if (md == NULL)
147                 return (0);
148         shdr = (Elf_Shdr *)md->md_data;
149
150         for (i = 0; i < ehdr->e_shnum; ++i) {
151                 if (shdr[i].sh_type == SHT_DYNSYM && symtab == 0) {
152                         symtab = shdr[i].sh_addr + offs;
153                         sym_count = shdr[i].sh_size / sizeof(Elf_Sym);
154                 } else if (shdr[i].sh_type == SHT_STRTAB && strtab == 0) {
155                         strtab = shdr[i].sh_addr + offs;
156                 }
157         }
158
159         /*
160          * The most efficent way to find a symbol would be to calculate a
161          * hash, find proper bucket and chain, and thus find a symbol.
162          * However, that would involve code duplication (e.g. for hash
163          * function). So we're using simpler and a bit slower way: we're
164          * iterating through symbols, searching for the one which name is
165          * 'equal' to 'fdt_static_dtb'. To speed up the process a little bit,
166          * we are eliminating symbols type of which is not STT_NOTYPE, or(and)
167          * those which binding attribute is not STB_GLOBAL.
168          */
169         fdt_start = 0;
170         while (sym_count > 0 && fdt_start == 0) {
171                 COPYOUT(symtab, &sym, sizeof(sym));
172                 symtab += sizeof(sym);
173                 --sym_count;
174                 if (ELF_ST_BIND(sym.st_info) != STB_GLOBAL ||
175                     ELF_ST_TYPE(sym.st_info) != STT_NOTYPE)
176                         continue;
177                 strp = strdupout(strtab + sym.st_name);
178                 if (strcmp(strp, FDT_STATIC_DTB_SYMBOL) == 0)
179                         fdt_start = (vm_offset_t)sym.st_value + offs;
180                 free(strp);
181         }
182         return (fdt_start);
183 }
184
185 static int
186 fdt_load_dtb(vm_offset_t va)
187 {
188         struct fdt_header header;
189         int err;
190
191         debugf("fdt_load_dtb(0x%08jx)\n", (uintmax_t)va);
192
193         COPYOUT(va, &header, sizeof(header));
194         err = fdt_check_header(&header);
195         if (err < 0) {
196                 if (err == -FDT_ERR_BADVERSION)
197                         sprintf(command_errbuf,
198                             "incompatible blob version: %d, should be: %d",
199                             fdt_version(fdtp), FDT_LAST_SUPPORTED_VERSION);
200
201                 else
202                         sprintf(command_errbuf, "error validating blob: %s",
203                             fdt_strerror(err));
204                 return (1);
205         }
206
207         /*
208          * Release previous blob
209          */
210         if (fdtp)
211                 free(fdtp);
212
213         fdtp_size = fdt_totalsize(&header);
214         fdtp = malloc(fdtp_size);
215
216         if (fdtp == NULL) {
217                 command_errmsg = "can't allocate memory for device tree copy";
218                 return (1);
219         }
220
221         fdtp_va = va;
222         COPYOUT(va, fdtp, fdtp_size);
223         debugf("DTB blob found at 0x%jx, size: 0x%jx\n", (uintmax_t)va, (uintmax_t)fdtp_size);
224
225         return (0);
226 }
227
228 int
229 fdt_load_dtb_addr(struct fdt_header *header)
230 {
231         int err;
232
233         debugf("fdt_load_dtb_addr(0x%p)\n", header);
234
235         fdtp_size = fdt_totalsize(header);
236         err = fdt_check_header(header);
237         if (err < 0) {
238                 sprintf(command_errbuf, "error validating blob: %s",
239                     fdt_strerror(err));
240                 return (err);
241         }
242         free(fdtp);
243         if ((fdtp = malloc(fdtp_size)) == NULL) {
244                 command_errmsg = "can't allocate memory for device tree copy";
245                 return (1);
246         }
247
248         fdtp_va = 0; // Don't write this back into module or kernel.
249         bcopy(header, fdtp, fdtp_size);
250         return (0);
251 }
252
253 int
254 fdt_load_dtb_file(const char * filename)
255 {
256         struct preloaded_file *bfp, *oldbfp;
257         int err;
258
259         debugf("fdt_load_dtb_file(%s)\n", filename);
260
261         oldbfp = file_findfile(NULL, "dtb");
262
263         /* Attempt to load and validate a new dtb from a file. */
264         if ((bfp = file_loadraw(filename, "dtb")) == NULL) {
265                 sprintf(command_errbuf, "failed to load file '%s'", filename);
266                 return (1);
267         }
268         if ((err = fdt_load_dtb(bfp->f_addr)) != 0) {
269                 file_discard(bfp);
270                 return (err);
271         }
272
273         /* A new dtb was validated, discard any previous file. */
274         if (oldbfp)
275                 file_discard(oldbfp);
276         return (0);
277 }
278
279 int
280 fdt_setup_fdtp()
281 {
282         struct preloaded_file *bfp;
283         vm_offset_t va;
284         
285         debugf("fdt_setup_fdtp()\n");
286
287         /* If we already loaded a file, use it. */
288         if ((bfp = file_findfile(NULL, "dtb")) != NULL) {
289                 if (fdt_load_dtb(bfp->f_addr) == 0) {
290                         printf("Using DTB from loaded file '%s'.\n", 
291                             bfp->f_name);
292                         return (0);
293                 }
294         }
295
296         /* If we were given the address of a valid blob in memory, use it. */
297         if (fdt_to_load != NULL) {
298                 if (fdt_load_dtb_addr(fdt_to_load) == 0) {
299                         printf("Using DTB from memory address 0x%08X.\n",
300                             (unsigned int)fdt_to_load);
301                         return (0);
302                 }
303         }
304
305         if (fdt_platform_load_dtb() == 0)
306                 return (0);
307
308         /* If there is a dtb compiled into the kernel, use it. */
309         if ((va = fdt_find_static_dtb()) != 0) {
310                 if (fdt_load_dtb(va) == 0) {
311                         printf("Using DTB compiled into kernel.\n");
312                         return (0);
313                 }
314         }
315         
316         command_errmsg = "No device tree blob found!\n";
317         return (1);
318 }
319
320 #define fdt_strtovect(str, cellbuf, lim, cellsize) _fdt_strtovect((str), \
321     (cellbuf), (lim), (cellsize), 0);
322
323 /* Force using base 16 */
324 #define fdt_strtovectx(str, cellbuf, lim, cellsize) _fdt_strtovect((str), \
325     (cellbuf), (lim), (cellsize), 16);
326
327 static int
328 _fdt_strtovect(const char *str, void *cellbuf, int lim, unsigned char cellsize,
329     uint8_t base)
330 {
331         const char *buf = str;
332         const char *end = str + strlen(str) - 2;
333         uint32_t *u32buf = NULL;
334         uint8_t *u8buf = NULL;
335         int cnt = 0;
336
337         if (cellsize == sizeof(uint32_t))
338                 u32buf = (uint32_t *)cellbuf;
339         else
340                 u8buf = (uint8_t *)cellbuf;
341
342         if (lim == 0)
343                 return (0);
344
345         while (buf < end) {
346
347                 /* Skip white whitespace(s)/separators */
348                 while (!isxdigit(*buf) && buf < end)
349                         buf++;
350
351                 if (u32buf != NULL)
352                         u32buf[cnt] =
353                             cpu_to_fdt32((uint32_t)strtol(buf, NULL, base));
354
355                 else
356                         u8buf[cnt] = (uint8_t)strtol(buf, NULL, base);
357
358                 if (cnt + 1 <= lim - 1)
359                         cnt++;
360                 else
361                         break;
362                 buf++;
363                 /* Find another number */
364                 while ((isxdigit(*buf) || *buf == 'x') && buf < end)
365                         buf++;
366         }
367         return (cnt);
368 }
369
370 void
371 fdt_fixup_ethernet(const char *str, char *ethstr, int len)
372 {
373         uint8_t tmp_addr[6];
374
375         /* Convert macaddr string into a vector of uints */
376         fdt_strtovectx(str, &tmp_addr, 6, sizeof(uint8_t));
377         /* Set actual property to a value from vect */
378         fdt_setprop(fdtp, fdt_path_offset(fdtp, ethstr),
379             "local-mac-address", &tmp_addr, 6 * sizeof(uint8_t));
380 }
381
382 void
383 fdt_fixup_cpubusfreqs(unsigned long cpufreq, unsigned long busfreq)
384 {
385         int lo, o = 0, o2, maxo = 0, depth;
386         const uint32_t zero = 0;
387
388         /* We want to modify every subnode of /cpus */
389         o = fdt_path_offset(fdtp, "/cpus");
390         if (o < 0)
391                 return;
392
393         /* maxo should contain offset of node next to /cpus */
394         depth = 0;
395         maxo = o;
396         while (depth != -1)
397                 maxo = fdt_next_node(fdtp, maxo, &depth);
398
399         /* Find CPU frequency properties */
400         o = fdt_node_offset_by_prop_value(fdtp, o, "clock-frequency",
401             &zero, sizeof(uint32_t));
402
403         o2 = fdt_node_offset_by_prop_value(fdtp, o, "bus-frequency", &zero,
404             sizeof(uint32_t));
405
406         lo = MIN(o, o2);
407
408         while (o != -FDT_ERR_NOTFOUND && o2 != -FDT_ERR_NOTFOUND) {
409
410                 o = fdt_node_offset_by_prop_value(fdtp, lo,
411                     "clock-frequency", &zero, sizeof(uint32_t));
412
413                 o2 = fdt_node_offset_by_prop_value(fdtp, lo, "bus-frequency",
414                     &zero, sizeof(uint32_t));
415
416                 /* We're only interested in /cpus subnode(s) */
417                 if (lo > maxo)
418                         break;
419
420                 fdt_setprop_inplace_cell(fdtp, lo, "clock-frequency",
421                     (uint32_t)cpufreq);
422
423                 fdt_setprop_inplace_cell(fdtp, lo, "bus-frequency",
424                     (uint32_t)busfreq);
425
426                 lo = MIN(o, o2);
427         }
428 }
429
430 static int
431 fdt_reg_valid(uint32_t *reg, int len, int addr_cells, int size_cells)
432 {
433         int cells_in_tuple, i, tuples, tuple_size;
434         uint32_t cur_start, cur_size;
435
436         cells_in_tuple = (addr_cells + size_cells);
437         tuple_size = cells_in_tuple * sizeof(uint32_t);
438         tuples = len / tuple_size;
439         if (tuples == 0)
440                 return (EINVAL);
441
442         for (i = 0; i < tuples; i++) {
443                 if (addr_cells == 2)
444                         cur_start = fdt64_to_cpu(reg[i * cells_in_tuple]);
445                 else
446                         cur_start = fdt32_to_cpu(reg[i * cells_in_tuple]);
447
448                 if (size_cells == 2)
449                         cur_size = fdt64_to_cpu(reg[i * cells_in_tuple + 2]);
450                 else
451                         cur_size = fdt32_to_cpu(reg[i * cells_in_tuple + 1]);
452
453                 if (cur_size == 0)
454                         return (EINVAL);
455
456                 debugf(" reg#%d (start: 0x%0x size: 0x%0x) valid!\n",
457                     i, cur_start, cur_size);
458         }
459         return (0);
460 }
461
462 void
463 fdt_fixup_memory(struct fdt_mem_region *region, size_t num)
464 {
465         struct fdt_mem_region *curmr;
466         uint32_t addr_cells, size_cells;
467         uint32_t *addr_cellsp, *reg,  *size_cellsp;
468         int err, i, len, memory, root;
469         size_t realmrno;
470         uint8_t *buf, *sb;
471         uint64_t rstart, rsize;
472         int reserved;
473
474         root = fdt_path_offset(fdtp, "/");
475         if (root < 0) {
476                 sprintf(command_errbuf, "Could not find root node !");
477                 return;
478         }
479
480         memory = fdt_path_offset(fdtp, "/memory");
481         if (memory <= 0) {
482                 /* Create proper '/memory' node. */
483                 memory = fdt_add_subnode(fdtp, root, "memory");
484                 if (memory <= 0) {
485                         sprintf(command_errbuf, "Could not fixup '/memory' "
486                             "node, error code : %d!\n", memory);
487                         return;
488                 }
489
490                 err = fdt_setprop(fdtp, memory, "device_type", "memory",
491                     sizeof("memory"));
492
493                 if (err < 0)
494                         return;
495         }
496
497         addr_cellsp = (uint32_t *)fdt_getprop(fdtp, root, "#address-cells",
498             NULL);
499         size_cellsp = (uint32_t *)fdt_getprop(fdtp, root, "#size-cells", NULL);
500
501         if (addr_cellsp == NULL || size_cellsp == NULL) {
502                 sprintf(command_errbuf, "Could not fixup '/memory' node : "
503                     "%s %s property not found in root node!\n",
504                     (!addr_cellsp) ? "#address-cells" : "",
505                     (!size_cellsp) ? "#size-cells" : "");
506                 return;
507         }
508
509         addr_cells = fdt32_to_cpu(*addr_cellsp);
510         size_cells = fdt32_to_cpu(*size_cellsp);
511
512         /*
513          * Convert memreserve data to memreserve property
514          * Check if property already exists
515          */
516         reserved = fdt_num_mem_rsv(fdtp);
517         if (reserved &&
518             (fdt_getprop(fdtp, root, "memreserve", NULL) == NULL)) {
519                 len = (addr_cells + size_cells) * reserved * sizeof(uint32_t);
520                 sb = buf = (uint8_t *)malloc(len);
521                 if (!buf)
522                         return;
523
524                 bzero(buf, len);
525
526                 for (i = 0; i < reserved; i++) {
527                         if (fdt_get_mem_rsv(fdtp, i, &rstart, &rsize))
528                                 break;
529                         if (rsize) {
530                                 /* Ensure endianess, and put cells into a buffer */
531                                 if (addr_cells == 2)
532                                         *(uint64_t *)buf =
533                                             cpu_to_fdt64(rstart);
534                                 else
535                                         *(uint32_t *)buf =
536                                             cpu_to_fdt32(rstart);
537
538                                 buf += sizeof(uint32_t) * addr_cells;
539                                 if (size_cells == 2)
540                                         *(uint64_t *)buf =
541                                             cpu_to_fdt64(rsize);
542                                 else
543                                         *(uint32_t *)buf =
544                                             cpu_to_fdt32(rsize);
545
546                                 buf += sizeof(uint32_t) * size_cells;
547                         }
548                 }
549
550                 /* Set property */
551                 if ((err = fdt_setprop(fdtp, root, "memreserve", sb, len)) < 0)
552                         printf("Could not fixup 'memreserve' property.\n");
553
554                 free(sb);
555         } 
556
557         /* Count valid memory regions entries in sysinfo. */
558         realmrno = num;
559         for (i = 0; i < num; i++)
560                 if (region[i].start == 0 && region[i].size == 0)
561                         realmrno--;
562
563         if (realmrno == 0) {
564                 sprintf(command_errbuf, "Could not fixup '/memory' node : "
565                     "sysinfo doesn't contain valid memory regions info!\n");
566                 return;
567         }
568
569         len = (addr_cells + size_cells) * realmrno * sizeof(uint32_t);
570         sb = buf = (uint8_t *)malloc(len);
571         if (!buf)
572                 return;
573
574         bzero(buf, len);
575
576         for (i = 0; i < num; i++) {
577                 curmr = &region[i];
578                 if (curmr->size != 0) {
579                         /* Ensure endianess, and put cells into a buffer */
580                         if (addr_cells == 2)
581                                 *(uint64_t *)buf =
582                                     cpu_to_fdt64(curmr->start);
583                         else
584                                 *(uint32_t *)buf =
585                                     cpu_to_fdt32(curmr->start);
586
587                         buf += sizeof(uint32_t) * addr_cells;
588                         if (size_cells == 2)
589                                 *(uint64_t *)buf =
590                                     cpu_to_fdt64(curmr->size);
591                         else
592                                 *(uint32_t *)buf =
593                                     cpu_to_fdt32(curmr->size);
594
595                         buf += sizeof(uint32_t) * size_cells;
596                 }
597         }
598
599         /* Set property */
600         if ((err = fdt_setprop(fdtp, memory, "reg", sb, len)) < 0)
601                 sprintf(command_errbuf, "Could not fixup '/memory' node.\n");
602
603         free(sb);
604 }
605
606 void
607 fdt_fixup_stdout(const char *str)
608 {
609         char *ptr;
610         int serialno;
611         int len, no, sero;
612         const struct fdt_property *prop;
613         char *tmp[10];
614
615         ptr = (char *)str + strlen(str) - 1;
616         while (ptr > str && isdigit(*(str - 1)))
617                 str--;
618
619         if (ptr == str)
620                 return;
621
622         serialno = (int)strtol(ptr, NULL, 0);
623         no = fdt_path_offset(fdtp, "/chosen");
624         if (no < 0)
625                 return;
626
627         prop = fdt_get_property(fdtp, no, "stdout", &len);
628
629         /* If /chosen/stdout does not extist, create it */
630         if (prop == NULL || (prop != NULL && len == 0)) {
631
632                 bzero(tmp, 10 * sizeof(char));
633                 strcpy((char *)&tmp, "serial");
634                 if (strlen(ptr) > 3)
635                         /* Serial number too long */
636                         return;
637
638                 strncpy((char *)tmp + 6, ptr, 3);
639                 sero = fdt_path_offset(fdtp, (const char *)tmp);
640                 if (sero < 0)
641                         /*
642                          * If serial device we're trying to assign
643                          * stdout to doesn't exist in DT -- return.
644                          */
645                         return;
646
647                 fdt_setprop(fdtp, no, "stdout", &tmp,
648                     strlen((char *)&tmp) + 1);
649                 fdt_setprop(fdtp, no, "stdin", &tmp,
650                     strlen((char *)&tmp) + 1);
651         }
652 }
653
654 /*
655  * Locate the blob, fix it up and return its location.
656  */
657 static int
658 fdt_fixup(void)
659 {
660         int chosen, len;
661
662         len = 0;
663
664         debugf("fdt_fixup()\n");
665
666         if (fdtp == NULL && fdt_setup_fdtp() != 0)
667                 return (0);
668
669         /* Create /chosen node (if not exists) */
670         if ((chosen = fdt_subnode_offset(fdtp, 0, "chosen")) ==
671             -FDT_ERR_NOTFOUND)
672                 chosen = fdt_add_subnode(fdtp, 0, "chosen");
673
674         /* Value assigned to fixup-applied does not matter. */
675         if (fdt_getprop(fdtp, chosen, "fixup-applied", NULL))
676                 return (1);
677
678         fdt_platform_fixups();
679
680         fdt_setprop(fdtp, chosen, "fixup-applied", NULL, 0);
681         return (1);
682 }
683
684 /*
685  * Copy DTB blob to specified location and return size
686  */
687 int
688 fdt_copy(vm_offset_t va)
689 {
690         int err;
691         debugf("fdt_copy va 0x%08x\n", va);
692         if (fdtp == NULL) {
693                 err = fdt_setup_fdtp();
694                 if (err) {
695                         printf("No valid device tree blob found!\n");
696                         return (0);
697                 }
698         }
699
700         if (fdt_fixup() == 0)
701                 return (0);
702
703         if (fdtp_va != 0) {
704                 /* Overwrite the FDT with the fixed version. */
705                 /* XXX Is this really appropriate? */
706                 COPYIN(fdtp, fdtp_va, fdtp_size);
707         }
708         COPYIN(fdtp, va, fdtp_size);
709         return (fdtp_size);
710 }
711
712
713
714 int
715 command_fdt_internal(int argc, char *argv[])
716 {
717         cmdf_t *cmdh;
718         int flags;
719         char *cmd;
720         int i, err;
721
722         if (argc < 2) {
723                 command_errmsg = "usage is 'fdt <command> [<args>]";
724                 return (CMD_ERROR);
725         }
726
727         /*
728          * Validate fdt <command>.
729          */
730         cmd = strdup(argv[1]);
731         i = 0;
732         cmdh = NULL;
733         while (!(commands[i].name == NULL)) {
734                 if (strcmp(cmd, commands[i].name) == 0) {
735                         /* found it */
736                         cmdh = commands[i].handler;
737                         flags = commands[i].flags;
738                         break;
739                 }
740                 i++;
741         }
742         if (cmdh == NULL) {
743                 command_errmsg = "unknown command";
744                 return (CMD_ERROR);
745         }
746
747         if (flags & CMD_REQUIRES_BLOB) {
748                 /*
749                  * Check if uboot env vars were parsed already. If not, do it now.
750                  */
751                 if (fdt_fixup() == 0)
752                         return (CMD_ERROR);
753         }
754
755         /*
756          * Call command handler.
757          */
758         err = (*cmdh)(argc, argv);
759
760         return (err);
761 }
762
763 static int
764 fdt_cmd_addr(int argc, char *argv[])
765 {
766         struct preloaded_file *fp;
767         struct fdt_header *hdr;
768         const char *addr;
769         char *cp;
770
771         fdt_to_load = NULL;
772
773         if (argc > 2)
774                 addr = argv[2];
775         else {
776                 sprintf(command_errbuf, "no address specified");
777                 return (CMD_ERROR);
778         }
779
780         hdr = (struct fdt_header *)strtoul(addr, &cp, 16);
781         if (cp == addr) {
782                 sprintf(command_errbuf, "Invalid address: %s", addr);
783                 return (CMD_ERROR);
784         }
785
786         while ((fp = file_findfile(NULL, "dtb")) != NULL) {
787                 file_discard(fp);
788         }
789
790         fdt_to_load = hdr;
791         return (CMD_OK);
792 }
793
794 static int
795 fdt_cmd_cd(int argc, char *argv[])
796 {
797         char *path;
798         char tmp[FDT_CWD_LEN];
799         int len, o;
800
801         path = (argc > 2) ? argv[2] : "/";
802
803         if (path[0] == '/') {
804                 len = strlen(path);
805                 if (len >= FDT_CWD_LEN)
806                         goto fail;
807         } else {
808                 /* Handle path specification relative to cwd */
809                 len = strlen(cwd) + strlen(path) + 1;
810                 if (len >= FDT_CWD_LEN)
811                         goto fail;
812
813                 strcpy(tmp, cwd);
814                 strcat(tmp, "/");
815                 strcat(tmp, path);
816                 path = tmp;
817         }
818
819         o = fdt_path_offset(fdtp, path);
820         if (o < 0) {
821                 sprintf(command_errbuf, "could not find node: '%s'", path);
822                 return (CMD_ERROR);
823         }
824
825         strcpy(cwd, path);
826         return (CMD_OK);
827
828 fail:
829         sprintf(command_errbuf, "path too long: %d, max allowed: %d",
830             len, FDT_CWD_LEN - 1);
831         return (CMD_ERROR);
832 }
833
834 static int
835 fdt_cmd_hdr(int argc __unused, char *argv[] __unused)
836 {
837         char line[80];
838         int ver;
839
840         if (fdtp == NULL) {
841                 command_errmsg = "no device tree blob pointer?!";
842                 return (CMD_ERROR);
843         }
844
845         ver = fdt_version(fdtp);
846         pager_open();
847         sprintf(line, "\nFlattened device tree header (%p):\n", fdtp);
848         pager_output(line);
849         sprintf(line, " magic                   = 0x%08x\n", fdt_magic(fdtp));
850         pager_output(line);
851         sprintf(line, " size                    = %d\n", fdt_totalsize(fdtp));
852         pager_output(line);
853         sprintf(line, " off_dt_struct           = 0x%08x\n",
854             fdt_off_dt_struct(fdtp));
855         pager_output(line);
856         sprintf(line, " off_dt_strings          = 0x%08x\n",
857             fdt_off_dt_strings(fdtp));
858         pager_output(line);
859         sprintf(line, " off_mem_rsvmap          = 0x%08x\n",
860             fdt_off_mem_rsvmap(fdtp));
861         pager_output(line);
862         sprintf(line, " version                 = %d\n", ver); 
863         pager_output(line);
864         sprintf(line, " last compatible version = %d\n",
865             fdt_last_comp_version(fdtp));
866         pager_output(line);
867         if (ver >= 2) {
868                 sprintf(line, " boot_cpuid              = %d\n",
869                     fdt_boot_cpuid_phys(fdtp));
870                 pager_output(line);
871         }
872         if (ver >= 3) {
873                 sprintf(line, " size_dt_strings         = %d\n",
874                     fdt_size_dt_strings(fdtp));
875                 pager_output(line);
876         }
877         if (ver >= 17) {
878                 sprintf(line, " size_dt_struct          = %d\n",
879                     fdt_size_dt_struct(fdtp));
880                 pager_output(line);
881         }
882         pager_close();
883
884         return (CMD_OK);
885 }
886
887 static int
888 fdt_cmd_ls(int argc, char *argv[])
889 {
890         const char *prevname[FDT_MAX_DEPTH] = { NULL };
891         const char *name;
892         char *path;
893         int i, o, depth, len;
894
895         path = (argc > 2) ? argv[2] : NULL;
896         if (path == NULL)
897                 path = cwd;
898
899         o = fdt_path_offset(fdtp, path);
900         if (o < 0) {
901                 sprintf(command_errbuf, "could not find node: '%s'", path);
902                 return (CMD_ERROR);
903         }
904
905         for (depth = 0;
906             (o >= 0) && (depth >= 0);
907             o = fdt_next_node(fdtp, o, &depth)) {
908
909                 name = fdt_get_name(fdtp, o, &len);
910
911                 if (depth > FDT_MAX_DEPTH) {
912                         printf("max depth exceeded: %d\n", depth);
913                         continue;
914                 }
915
916                 prevname[depth] = name;
917
918                 /* Skip root (i = 1) when printing devices */
919                 for (i = 1; i <= depth; i++) {
920                         if (prevname[i] == NULL)
921                                 break;
922
923                         if (strcmp(cwd, "/") == 0)
924                                 printf("/");
925                         printf("%s", prevname[i]);
926                 }
927                 printf("\n");
928         }
929
930         return (CMD_OK);
931 }
932
933 static __inline int
934 isprint(int c)
935 {
936
937         return (c >= ' ' && c <= 0x7e);
938 }
939
940 static int
941 fdt_isprint(const void *data, int len, int *count)
942 {
943         const char *d;
944         char ch;
945         int yesno, i;
946
947         if (len == 0)
948                 return (0);
949
950         d = (const char *)data;
951         if (d[len - 1] != '\0')
952                 return (0);
953
954         *count = 0;
955         yesno = 1;
956         for (i = 0; i < len; i++) {
957                 ch = *(d + i);
958                 if (isprint(ch) || (ch == '\0' && i > 0)) {
959                         /* Count strings */
960                         if (ch == '\0')
961                                 (*count)++;
962                         continue;
963                 }
964
965                 yesno = 0;
966                 break;
967         }
968
969         return (yesno);
970 }
971
972 static int
973 fdt_data_str(const void *data, int len, int count, char **buf)
974 {
975         char *b, *tmp;
976         const char *d;
977         int buf_len, i, l;
978
979         /*
980          * Calculate the length for the string and allocate memory.
981          *
982          * Note that 'len' already includes at least one terminator.
983          */
984         buf_len = len;
985         if (count > 1) {
986                 /*
987                  * Each token had already a terminator buried in 'len', but we
988                  * only need one eventually, don't count space for these.
989                  */
990                 buf_len -= count - 1;
991
992                 /* Each consecutive token requires a ", " separator. */
993                 buf_len += count * 2;
994         }
995
996         /* Add some space for surrounding double quotes. */
997         buf_len += count * 2;
998
999         /* Note that string being put in 'tmp' may be as big as 'buf_len'. */
1000         b = (char *)malloc(buf_len);
1001         tmp = (char *)malloc(buf_len);
1002         if (b == NULL)
1003                 goto error;
1004
1005         if (tmp == NULL) {
1006                 free(b);
1007                 goto error;
1008         }
1009
1010         b[0] = '\0';
1011
1012         /*
1013          * Now that we have space, format the string.
1014          */
1015         i = 0;
1016         do {
1017                 d = (const char *)data + i;
1018                 l = strlen(d) + 1;
1019
1020                 sprintf(tmp, "\"%s\"%s", d,
1021                     (i + l) < len ?  ", " : "");
1022                 strcat(b, tmp);
1023
1024                 i += l;
1025
1026         } while (i < len);
1027         *buf = b;
1028
1029         free(tmp);
1030
1031         return (0);
1032 error:
1033         return (1);
1034 }
1035
1036 static int
1037 fdt_data_cell(const void *data, int len, char **buf)
1038 {
1039         char *b, *tmp;
1040         const uint32_t *c;
1041         int count, i, l;
1042
1043         /* Number of cells */
1044         count = len / 4;
1045
1046         /*
1047          * Calculate the length for the string and allocate memory.
1048          */
1049
1050         /* Each byte translates to 2 output characters */
1051         l = len * 2;
1052         if (count > 1) {
1053                 /* Each consecutive cell requires a " " separator. */
1054                 l += (count - 1) * 1;
1055         }
1056         /* Each cell will have a "0x" prefix */
1057         l += count * 2;
1058         /* Space for surrounding <> and terminator */
1059         l += 3;
1060
1061         b = (char *)malloc(l);
1062         tmp = (char *)malloc(l);
1063         if (b == NULL)
1064                 goto error;
1065
1066         if (tmp == NULL) {
1067                 free(b);
1068                 goto error;
1069         }
1070
1071         b[0] = '\0';
1072         strcat(b, "<");
1073
1074         for (i = 0; i < len; i += 4) {
1075                 c = (const uint32_t *)((const uint8_t *)data + i);
1076                 sprintf(tmp, "0x%08x%s", fdt32_to_cpu(*c),
1077                     i < (len - 4) ? " " : "");
1078                 strcat(b, tmp);
1079         }
1080         strcat(b, ">");
1081         *buf = b;
1082
1083         free(tmp);
1084
1085         return (0);
1086 error:
1087         return (1);
1088 }
1089
1090 static int
1091 fdt_data_bytes(const void *data, int len, char **buf)
1092 {
1093         char *b, *tmp;
1094         const char *d;
1095         int i, l;
1096
1097         /*
1098          * Calculate the length for the string and allocate memory.
1099          */
1100
1101         /* Each byte translates to 2 output characters */
1102         l = len * 2;
1103         if (len > 1)
1104                 /* Each consecutive byte requires a " " separator. */
1105                 l += (len - 1) * 1;
1106         /* Each byte will have a "0x" prefix */
1107         l += len * 2;
1108         /* Space for surrounding [] and terminator. */
1109         l += 3;
1110
1111         b = (char *)malloc(l);
1112         tmp = (char *)malloc(l);
1113         if (b == NULL)
1114                 goto error;
1115
1116         if (tmp == NULL) {
1117                 free(b);
1118                 goto error;
1119         }
1120
1121         b[0] = '\0';
1122         strcat(b, "[");
1123
1124         for (i = 0, d = data; i < len; i++) {
1125                 sprintf(tmp, "0x%02x%s", d[i], i < len - 1 ? " " : "");
1126                 strcat(b, tmp);
1127         }
1128         strcat(b, "]");
1129         *buf = b;
1130
1131         free(tmp);
1132
1133         return (0);
1134 error:
1135         return (1);
1136 }
1137
1138 static int
1139 fdt_data_fmt(const void *data, int len, char **buf)
1140 {
1141         int count;
1142
1143         if (len == 0) {
1144                 *buf = NULL;
1145                 return (1);
1146         }
1147
1148         if (fdt_isprint(data, len, &count))
1149                 return (fdt_data_str(data, len, count, buf));
1150
1151         else if ((len % 4) == 0)
1152                 return (fdt_data_cell(data, len, buf));
1153
1154         else
1155                 return (fdt_data_bytes(data, len, buf));
1156 }
1157
1158 static int
1159 fdt_prop(int offset)
1160 {
1161         char *line, *buf;
1162         const struct fdt_property *prop;
1163         const char *name;
1164         const void *data;
1165         int len, rv;
1166
1167         line = NULL;
1168         prop = fdt_offset_ptr(fdtp, offset, sizeof(*prop));
1169         if (prop == NULL)
1170                 return (1);
1171
1172         name = fdt_string(fdtp, fdt32_to_cpu(prop->nameoff));
1173         len = fdt32_to_cpu(prop->len);
1174
1175         rv = 0;
1176         buf = NULL;
1177         if (len == 0) {
1178                 /* Property without value */
1179                 line = (char *)malloc(strlen(name) + 2);
1180                 if (line == NULL) {
1181                         rv = 2;
1182                         goto out2;
1183                 }
1184                 sprintf(line, "%s\n", name);
1185                 goto out1;
1186         }
1187
1188         /*
1189          * Process property with value
1190          */
1191         data = prop->data;
1192
1193         if (fdt_data_fmt(data, len, &buf) != 0) {
1194                 rv = 3;
1195                 goto out2;
1196         }
1197
1198         line = (char *)malloc(strlen(name) + strlen(FDT_PROP_SEP) +
1199             strlen(buf) + 2);
1200         if (line == NULL) {
1201                 sprintf(command_errbuf, "could not allocate space for string");
1202                 rv = 4;
1203                 goto out2;
1204         }
1205
1206         sprintf(line, "%s" FDT_PROP_SEP "%s\n", name, buf);
1207
1208 out1:
1209         pager_open();
1210         pager_output(line);
1211         pager_close();
1212
1213 out2:
1214         if (buf)
1215                 free(buf);
1216
1217         if (line)
1218                 free(line);
1219
1220         return (rv);
1221 }
1222
1223 static int
1224 fdt_modprop(int nodeoff, char *propname, void *value, char mode)
1225 {
1226         uint32_t cells[100];
1227         const char *buf;
1228         int len, rv;
1229         const struct fdt_property *p;
1230
1231         p = fdt_get_property(fdtp, nodeoff, propname, NULL);
1232
1233         if (p != NULL) {
1234                 if (mode == 1) {
1235                          /* Adding inexistant value in mode 1 is forbidden */
1236                         sprintf(command_errbuf, "property already exists!");
1237                         return (CMD_ERROR);
1238                 }
1239         } else if (mode == 0) {
1240                 sprintf(command_errbuf, "property does not exist!");
1241                 return (CMD_ERROR);
1242         }
1243         len = strlen(value);
1244         rv = 0;
1245         buf = value;
1246
1247         switch (*buf) {
1248         case '&':
1249                 /* phandles */
1250                 break;
1251         case '<':
1252                 /* Data cells */
1253                 len = fdt_strtovect(buf, (void *)&cells, 100,
1254                     sizeof(uint32_t));
1255
1256                 rv = fdt_setprop(fdtp, nodeoff, propname, &cells,
1257                     len * sizeof(uint32_t));
1258                 break;
1259         case '[':
1260                 /* Data bytes */
1261                 len = fdt_strtovect(buf, (void *)&cells, 100,
1262                     sizeof(uint8_t));
1263
1264                 rv = fdt_setprop(fdtp, nodeoff, propname, &cells,
1265                     len * sizeof(uint8_t));
1266                 break;
1267         case '"':
1268         default:
1269                 /* Default -- string */
1270                 rv = fdt_setprop_string(fdtp, nodeoff, propname, value);
1271                 break;
1272         }
1273
1274         if (rv != 0) {
1275                 if (rv == -FDT_ERR_NOSPACE)
1276                         sprintf(command_errbuf,
1277                             "Device tree blob is too small!\n");
1278                 else
1279                         sprintf(command_errbuf,
1280                             "Could not add/modify property!\n");
1281         }
1282         return (rv);
1283 }
1284
1285 /* Merge strings from argv into a single string */
1286 static int
1287 fdt_merge_strings(int argc, char *argv[], int start, char **buffer)
1288 {
1289         char *buf;
1290         int i, idx, sz;
1291
1292         *buffer = NULL;
1293         sz = 0;
1294
1295         for (i = start; i < argc; i++)
1296                 sz += strlen(argv[i]);
1297
1298         /* Additional bytes for whitespaces between args */
1299         sz += argc - start;
1300
1301         buf = (char *)malloc(sizeof(char) * sz);
1302         if (buf == NULL) {
1303                 sprintf(command_errbuf, "could not allocate space "
1304                     "for string");
1305                 return (1);
1306         }
1307         bzero(buf, sizeof(char) * sz);
1308
1309         idx = 0;
1310         for (i = start, idx = 0; i < argc; i++) {
1311                 strcpy(buf + idx, argv[i]);
1312                 idx += strlen(argv[i]);
1313                 buf[idx] = ' ';
1314                 idx++;
1315         }
1316         buf[sz - 1] = '\0';
1317         *buffer = buf;
1318         return (0);
1319 }
1320
1321 /* Extract offset and name of node/property from a given path */
1322 static int
1323 fdt_extract_nameloc(char **pathp, char **namep, int *nodeoff)
1324 {
1325         int o;
1326         char *path = *pathp, *name = NULL, *subpath = NULL;
1327
1328         subpath = strrchr(path, '/');
1329         if (subpath == NULL) {
1330                 o = fdt_path_offset(fdtp, cwd);
1331                 name = path;
1332                 path = (char *)&cwd;
1333         } else {
1334                 *subpath = '\0';
1335                 if (strlen(path) == 0)
1336                         path = cwd;
1337
1338                 name = subpath + 1;
1339                 o = fdt_path_offset(fdtp, path);
1340         }
1341
1342         if (strlen(name) == 0) {
1343                 sprintf(command_errbuf, "name not specified");
1344                 return (1);
1345         }
1346         if (o < 0) {
1347                 sprintf(command_errbuf, "could not find node: '%s'", path);
1348                 return (1);
1349         }
1350         *namep = name;
1351         *nodeoff = o;
1352         *pathp = path;
1353         return (0);
1354 }
1355
1356 static int
1357 fdt_cmd_prop(int argc, char *argv[])
1358 {
1359         char *path, *propname, *value;
1360         int o, next, depth, rv;
1361         uint32_t tag;
1362
1363         path = (argc > 2) ? argv[2] : NULL;
1364
1365         value = NULL;
1366
1367         if (argc > 3) {
1368                 /* Merge property value strings into one */
1369                 if (fdt_merge_strings(argc, argv, 3, &value) != 0)
1370                         return (CMD_ERROR);
1371         } else
1372                 value = NULL;
1373
1374         if (path == NULL)
1375                 path = cwd;
1376
1377         rv = CMD_OK;
1378
1379         if (value) {
1380                 /* If value is specified -- try to modify prop. */
1381                 if (fdt_extract_nameloc(&path, &propname, &o) != 0)
1382                         return (CMD_ERROR);
1383
1384                 rv = fdt_modprop(o, propname, value, 0);
1385                 if (rv)
1386                         return (CMD_ERROR);
1387                 return (CMD_OK);
1388
1389         }
1390         /* User wants to display properties */
1391         o = fdt_path_offset(fdtp, path);
1392
1393         if (o < 0) {
1394                 sprintf(command_errbuf, "could not find node: '%s'", path);
1395                 rv = CMD_ERROR;
1396                 goto out;
1397         }
1398
1399         depth = 0;
1400         while (depth >= 0) {
1401                 tag = fdt_next_tag(fdtp, o, &next);
1402                 switch (tag) {
1403                 case FDT_NOP:
1404                         break;
1405                 case FDT_PROP:
1406                         if (depth > 1)
1407                                 /* Don't process properties of nested nodes */
1408                                 break;
1409
1410                         if (fdt_prop(o) != 0) {
1411                                 sprintf(command_errbuf, "could not process "
1412                                     "property");
1413                                 rv = CMD_ERROR;
1414                                 goto out;
1415                         }
1416                         break;
1417                 case FDT_BEGIN_NODE:
1418                         depth++;
1419                         if (depth > FDT_MAX_DEPTH) {
1420                                 printf("warning: nesting too deep: %d\n",
1421                                     depth);
1422                                 goto out;
1423                         }
1424                         break;
1425                 case FDT_END_NODE:
1426                         depth--;
1427                         if (depth == 0)
1428                                 /*
1429                                  * This is the end of our starting node, force
1430                                  * the loop finish.
1431                                  */
1432                                 depth--;
1433                         break;
1434                 }
1435                 o = next;
1436         }
1437 out:
1438         return (rv);
1439 }
1440
1441 static int
1442 fdt_cmd_mkprop(int argc, char *argv[])
1443 {
1444         int o;
1445         char *path, *propname, *value;
1446
1447         path = (argc > 2) ? argv[2] : NULL;
1448
1449         value = NULL;
1450
1451         if (argc > 3) {
1452                 /* Merge property value strings into one */
1453                 if (fdt_merge_strings(argc, argv, 3, &value) != 0)
1454                         return (CMD_ERROR);
1455         } else
1456                 value = NULL;
1457
1458         if (fdt_extract_nameloc(&path, &propname, &o) != 0)
1459                 return (CMD_ERROR);
1460
1461         if (fdt_modprop(o, propname, value, 1))
1462                 return (CMD_ERROR);
1463
1464         return (CMD_OK);
1465 }
1466
1467 static int
1468 fdt_cmd_rm(int argc, char *argv[])
1469 {
1470         int o, rv;
1471         char *path = NULL, *propname;
1472
1473         if (argc > 2)
1474                 path = argv[2];
1475         else {
1476                 sprintf(command_errbuf, "no node/property name specified");
1477                 return (CMD_ERROR);
1478         }
1479
1480         o = fdt_path_offset(fdtp, path);
1481         if (o < 0) {
1482                 /* If node not found -- try to find & delete property */
1483                 if (fdt_extract_nameloc(&path, &propname, &o) != 0)
1484                         return (CMD_ERROR);
1485
1486                 if ((rv = fdt_delprop(fdtp, o, propname)) != 0) {
1487                         sprintf(command_errbuf, "could not delete"
1488                             "%s\n", (rv == -FDT_ERR_NOTFOUND) ?
1489                             "(property/node does not exist)" : "");
1490                         return (CMD_ERROR);
1491
1492                 } else
1493                         return (CMD_OK);
1494         }
1495         /* If node exists -- remove node */
1496         rv = fdt_del_node(fdtp, o);
1497         if (rv) {
1498                 sprintf(command_errbuf, "could not delete node");
1499                 return (CMD_ERROR);
1500         }
1501         return (CMD_OK);
1502 }
1503
1504 static int
1505 fdt_cmd_mknode(int argc, char *argv[])
1506 {
1507         int o, rv;
1508         char *path = NULL, *nodename = NULL;
1509
1510         if (argc > 2)
1511                 path = argv[2];
1512         else {
1513                 sprintf(command_errbuf, "no node name specified");
1514                 return (CMD_ERROR);
1515         }
1516
1517         if (fdt_extract_nameloc(&path, &nodename, &o) != 0)
1518                 return (CMD_ERROR);
1519
1520         rv = fdt_add_subnode(fdtp, o, nodename);
1521
1522         if (rv < 0) {
1523                 if (rv == -FDT_ERR_NOSPACE)
1524                         sprintf(command_errbuf,
1525                             "Device tree blob is too small!\n");
1526                 else
1527                         sprintf(command_errbuf,
1528                             "Could not add node!\n");
1529                 return (CMD_ERROR);
1530         }
1531         return (CMD_OK);
1532 }
1533
1534 static int
1535 fdt_cmd_pwd(int argc, char *argv[])
1536 {
1537         char line[FDT_CWD_LEN];
1538
1539         pager_open();
1540         sprintf(line, "%s\n", cwd);
1541         pager_output(line);
1542         pager_close();
1543         return (CMD_OK);
1544 }
1545
1546 static int
1547 fdt_cmd_mres(int argc, char *argv[])
1548 {
1549         uint64_t start, size;
1550         int i, total;
1551         char line[80];
1552
1553         pager_open();
1554         total = fdt_num_mem_rsv(fdtp);
1555         if (total > 0) {
1556                 pager_output("Reserved memory regions:\n");
1557                 for (i = 0; i < total; i++) {
1558                         fdt_get_mem_rsv(fdtp, i, &start, &size);
1559                         sprintf(line, "reg#%d: (start: 0x%jx, size: 0x%jx)\n", 
1560                             i, start, size);
1561                         pager_output(line);
1562                 }
1563         } else
1564                 pager_output("No reserved memory regions\n");
1565         pager_close();
1566
1567         return (CMD_OK);
1568 }
1569
1570 static int
1571 fdt_cmd_nyi(int argc, char *argv[])
1572 {
1573
1574         printf("command not yet implemented\n");
1575         return (CMD_ERROR);
1576 }