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