]> CyberLeo.Net >> Repos - FreeBSD/releng/9.0.git/blob - sys/boot/fdt/fdt_loader_cmd.c
Copy stable/9 to releng/9.0 as part of the FreeBSD 9.0-RELEASE release
[FreeBSD/releng/9.0.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
37 #include "bootstrap.h"
38 #include "glue.h"
39
40 #define DEBUG
41
42 #ifdef DEBUG
43 #define debugf(fmt, args...) do { printf("%s(): ", __func__);   \
44     printf(fmt,##args); } while (0)
45 #else
46 #define debugf(fmt, args...)
47 #endif
48
49 #define FDT_CWD_LEN     256
50 #define FDT_MAX_DEPTH   6
51
52 #define FDT_PROP_SEP    " = "
53
54 #define STR(number) #number
55 #define STRINGIFY(number) STR(number)
56
57 #define MIN(num1, num2) (((num1) < (num2)) ? (num1):(num2))
58
59 static struct fdt_header *fdtp = NULL;
60
61 static int fdt_cmd_nyi(int argc, char *argv[]);
62
63 static int fdt_cmd_mkprop(int argc, char *argv[]);
64 static int fdt_cmd_cd(int argc, char *argv[]);
65 static int fdt_cmd_hdr(int argc, char *argv[]);
66 static int fdt_cmd_ls(int argc, char *argv[]);
67 static int fdt_cmd_prop(int argc, char *argv[]);
68 static int fdt_cmd_pwd(int argc, char *argv[]);
69 static int fdt_cmd_rm(int argc, char *argv[]);
70 static int fdt_cmd_mknode(int argc, char *argv[]);
71
72 typedef int cmdf_t(int, char *[]);
73
74 struct cmdtab {
75         char    *name;
76         cmdf_t  *handler;
77 };
78
79 static const struct cmdtab commands[] = {
80         { "alias", &fdt_cmd_nyi },
81         { "cd", &fdt_cmd_cd },
82         { "header", &fdt_cmd_hdr },
83         { "ls", &fdt_cmd_ls },
84         { "mknode", &fdt_cmd_mknode },
85         { "mkprop", &fdt_cmd_mkprop },
86         { "mres", &fdt_cmd_nyi },
87         { "prop", &fdt_cmd_prop },
88         { "pwd", &fdt_cmd_pwd },
89         { "rm", &fdt_cmd_rm },
90         { NULL, NULL }
91 };
92
93 static char cwd[FDT_CWD_LEN] = "/";
94
95 static int
96 fdt_setup_fdtp()
97 {
98         struct preloaded_file *bfp;
99         int err;
100
101         /*
102          * Find the device tree blob.
103          */
104         bfp = file_findfile(NULL, "dtb");
105         if (bfp == NULL) {
106                 command_errmsg = "no device tree blob loaded";
107                 return (CMD_ERROR);
108         }
109         fdtp = (struct fdt_header *)bfp->f_addr;
110
111         /*
112          * Validate the blob.
113          */
114         err = fdt_check_header(fdtp);
115         if (err < 0) {
116                 if (err == -FDT_ERR_BADVERSION)
117                         sprintf(command_errbuf,
118                             "incompatible blob version: %d, should be: %d",
119                             fdt_version(fdtp), FDT_LAST_SUPPORTED_VERSION);
120
121                 else
122                         sprintf(command_errbuf, "error validating blob: %s",
123                             fdt_strerror(err));
124                 return (CMD_ERROR);
125         }
126         return (CMD_OK);
127 }
128
129 #define fdt_strtovect(str, cellbuf, lim, cellsize) _fdt_strtovect((str), \
130     (cellbuf), (lim), (cellsize), 0);
131
132 /* Force using base 16 */
133 #define fdt_strtovectx(str, cellbuf, lim, cellsize) _fdt_strtovect((str), \
134     (cellbuf), (lim), (cellsize), 16);
135
136 static int
137 _fdt_strtovect(char *str, void *cellbuf, int lim, unsigned char cellsize,
138     uint8_t base)
139 {
140         char *buf = str;
141         char *end = str + strlen(str) - 2;
142         uint32_t *u32buf = NULL;
143         uint8_t *u8buf = NULL;
144         int cnt = 0;
145
146         if (cellsize == sizeof(uint32_t))
147                 u32buf = (uint32_t *)cellbuf;
148         else
149                 u8buf = (uint8_t *)cellbuf;
150
151         if (lim == 0)
152                 return (0);
153
154         while (buf < end) {
155
156                 /* Skip white whitespace(s)/separators */
157                 while (!isxdigit(*buf) && buf < end)
158                         buf++;
159
160                 if (u32buf != NULL)
161                         u32buf[cnt] =
162                             cpu_to_fdt32((uint32_t)strtol(buf, NULL, base));
163
164                 else
165                         u8buf[cnt] = (uint8_t)strtol(buf, NULL, base);
166
167                 if (cnt + 1 <= lim - 1)
168                         cnt++;
169                 else
170                         break;
171                 buf++;
172                 /* Find another number */
173                 while ((isxdigit(*buf) || *buf == 'x') && buf < end)
174                         buf++;
175         }
176         return (cnt);
177 }
178
179 #define TMP_MAX_ETH     8
180
181 void
182 fixup_ethernet(const char *env, char *ethstr, int *eth_no, int len)
183 {
184         char *end, *str;
185         uint8_t tmp_addr[6];
186         int i, n;
187
188         /* Extract interface number */
189         i = strtol(env + 3, &end, 10);
190         if (end == (env + 3))
191                 /* 'ethaddr' means interface 0 address */
192                 n = 0;
193         else
194                 n = i;
195
196         if (n > TMP_MAX_ETH)
197                 return;
198
199         str = ub_env_get(env);
200
201         /* Convert macaddr string into a vector of uints */
202         fdt_strtovectx(str, &tmp_addr, 6, sizeof(uint8_t));
203         if (n != 0) {
204                 i = strlen(env) - 7;
205                 strncpy(ethstr + 8, env + 3, i);
206         }
207         /* Set actual property to a value from vect */
208         fdt_setprop(fdtp, fdt_path_offset(fdtp, ethstr),
209             "local-mac-address", &tmp_addr, 6 * sizeof(uint8_t));
210
211         /* Clear ethernet..XXXX.. string */
212         bzero(ethstr + 8, len - 8);
213
214         if (n + 1 > *eth_no)
215                 *eth_no = n + 1;
216 }
217
218 void
219 fixup_cpubusfreqs(unsigned long cpufreq, unsigned long busfreq)
220 {
221         int lo, o = 0, o2, maxo = 0, depth;
222         const uint32_t zero = 0;
223
224         /* We want to modify every subnode of /cpus */
225         o = fdt_path_offset(fdtp, "/cpus");
226
227         /* maxo should contain offset of node next to /cpus */
228         depth = 0;
229         maxo = o;
230         while (depth != -1)
231                 maxo = fdt_next_node(fdtp, maxo, &depth);
232
233         /* Find CPU frequency properties */
234         o = fdt_node_offset_by_prop_value(fdtp, o, "clock-frequency",
235             &zero, sizeof(uint32_t));
236
237         o2 = fdt_node_offset_by_prop_value(fdtp, o, "bus-frequency", &zero,
238             sizeof(uint32_t));
239
240         lo = MIN(o, o2);
241
242         while (o != -FDT_ERR_NOTFOUND && o2 != -FDT_ERR_NOTFOUND) {
243
244                 o = fdt_node_offset_by_prop_value(fdtp, lo,
245                     "clock-frequency", &zero, sizeof(uint32_t));
246
247                 o2 = fdt_node_offset_by_prop_value(fdtp, lo, "bus-frequency",
248                     &zero, sizeof(uint32_t));
249
250                 /* We're only interested in /cpus subnode(s) */
251                 if (lo > maxo)
252                         break;
253
254                 fdt_setprop_inplace_cell(fdtp, lo, "clock-frequency",
255                     (uint32_t)cpufreq);
256
257                 fdt_setprop_inplace_cell(fdtp, lo, "bus-frequency",
258                     (uint32_t)busfreq);
259
260                 lo = MIN(o, o2);
261         }
262 }
263
264 int
265 fdt_reg_valid(uint32_t *reg, int len, int addr_cells, int size_cells)
266 {
267         int cells_in_tuple, i, tuples, tuple_size;
268         uint32_t cur_start, cur_size;
269
270         cells_in_tuple = (addr_cells + size_cells);
271         tuple_size = cells_in_tuple * sizeof(uint32_t);
272         tuples = len / tuple_size;
273         if (tuples == 0)
274                 return (EINVAL);
275
276         for (i = 0; i < tuples; i++) {
277                 if (addr_cells == 2)
278                         cur_start = fdt64_to_cpu(reg[i * cells_in_tuple]);
279                 else
280                         cur_start = fdt32_to_cpu(reg[i * cells_in_tuple]);
281
282                 if (size_cells == 2)
283                         cur_size = fdt64_to_cpu(reg[i * cells_in_tuple + 2]);
284                 else
285                         cur_size = fdt32_to_cpu(reg[i * cells_in_tuple + 1]);
286
287                 if (cur_size == 0)
288                         return (EINVAL);
289
290                 debugf(" reg#%d (start: 0x%0x size: 0x%0x) valid!\n",
291                     i, cur_start, cur_size);
292         }
293         return (0);
294 }
295
296 void
297 fixup_memory(struct sys_info *si)
298 {
299         struct mem_region *curmr;
300         uint32_t addr_cells, size_cells;
301         uint32_t *addr_cellsp, *reg,  *size_cellsp;
302         int err, i, len, memory, realmrno, root;
303         uint8_t *buf, *sb;
304
305         root = fdt_path_offset(fdtp, "/");
306         if (root < 0) {
307                 sprintf(command_errbuf, "Could not find root node !");
308                 return;
309         }
310
311         memory = fdt_path_offset(fdtp, "/memory");
312         if (memory <= 0) {
313                 /* Create proper '/memory' node. */
314                 memory = fdt_add_subnode(fdtp, root, "memory");
315                 if (memory <= 0) {
316                         sprintf(command_errbuf, "Could not fixup '/memory' "
317                             "node, error code : %d!\n", memory);
318                         return;
319                 }
320
321                 err = fdt_setprop(fdtp, memory, "device_type", "memory",
322                     sizeof("memory"));
323
324                 if (err < 0)
325                         return;
326         }
327
328         addr_cellsp = (uint32_t *)fdt_getprop(fdtp, root, "#address-cells",
329             NULL);
330         size_cellsp = (uint32_t *)fdt_getprop(fdtp, root, "#size-cells", NULL);
331
332         if (addr_cellsp == NULL || size_cellsp == NULL) {
333                 sprintf(command_errbuf, "Could not fixup '/memory' node : "
334                     "%s %s property not found in root node!\n",
335                     (!addr_cellsp) ? "#address-cells" : "",
336                     (!size_cellsp) ? "#size-cells" : "");
337                 return;
338         }
339
340         addr_cells = fdt32_to_cpu(*addr_cellsp);
341         size_cells = fdt32_to_cpu(*size_cellsp);
342
343         /* Count valid memory regions entries in sysinfo. */
344         realmrno = si->mr_no;
345         for (i = 0; i < si->mr_no; i++)
346                 if (si->mr[i].start == 0 && si->mr[i].size == 0)
347                         realmrno--;
348
349         if (realmrno == 0) {
350                 sprintf(command_errbuf, "Could not fixup '/memory' node : "
351                     "sysinfo doesn't contain valid memory regions info!\n");
352                 return;
353         }
354
355         if ((reg = (uint32_t *)fdt_getprop(fdtp, memory, "reg",
356             &len)) != NULL) {
357
358                 if (fdt_reg_valid(reg, len, addr_cells, size_cells) == 0)
359                         /*
360                          * Do not apply fixup if existing 'reg' property
361                          * seems to be valid.
362                          */
363                         return;
364         }
365
366         len = (addr_cells + size_cells) * realmrno * sizeof(uint32_t);
367         sb = buf = (uint8_t *)malloc(len);
368         if (!buf)
369                 return;
370
371         bzero(buf, len);
372
373         for (i = 0; i < si->mr_no; i++) {
374                 curmr = &si->mr[i];
375                 if (curmr->size != 0) {
376                         /* Ensure endianess, and put cells into a buffer */
377                         if (addr_cells == 2)
378                                 *(uint64_t *)buf =
379                                     cpu_to_fdt64(curmr->start);
380                         else
381                                 *(uint32_t *)buf =
382                                     cpu_to_fdt32(curmr->start);
383
384                         buf += sizeof(uint32_t) * addr_cells;
385                         if (size_cells == 2)
386                                 *(uint64_t *)buf =
387                                     cpu_to_fdt64(curmr->size);
388                         else
389                                 *(uint32_t *)buf =
390                                     cpu_to_fdt32(curmr->size);
391
392                         buf += sizeof(uint32_t) * size_cells;
393                 }
394         }
395
396         /* Set property */
397         if ((err = fdt_setprop(fdtp, memory, "reg", sb, len)) < 0)
398                 sprintf(command_errbuf, "Could not fixup '/memory' node.\n");
399 }
400
401 void
402 fixup_stdout(const char *env)
403 {
404         const char *str;
405         char *ptr;
406         int serialno;
407         int len, no, sero;
408         const struct fdt_property *prop;
409         char *tmp[10];
410
411         str = ub_env_get(env);
412         ptr = (char *)str + strlen(str) - 1;
413         while (ptr > str && isdigit(*(str - 1)))
414                 str--;
415
416         if (ptr == str)
417                 return;
418
419         serialno = (int)strtol(ptr, NULL, 0);
420         no = fdt_path_offset(fdtp, "/chosen");
421         if (no < 0)
422                 return;
423
424         prop = fdt_get_property(fdtp, no, "stdout", &len);
425
426         /* If /chosen/stdout does not extist, create it */
427         if (prop == NULL || (prop != NULL && len == 0)) {
428
429                 bzero(tmp, 10 * sizeof(char));
430                 strcpy((char *)&tmp, "serial");
431                 if (strlen(ptr) > 3)
432                         /* Serial number too long */
433                         return;
434
435                 strncpy((char *)tmp + 6, ptr, 3);
436                 sero = fdt_path_offset(fdtp, (const char *)tmp);
437                 if (sero < 0)
438                         /*
439                          * If serial device we're trying to assign
440                          * stdout to doesn't exist in DT -- return.
441                          */
442                         return;
443
444                 fdt_setprop(fdtp, no, "stdout", &tmp,
445                     strlen((char *)&tmp) + 1);
446                 fdt_setprop(fdtp, no, "stdin", &tmp,
447                     strlen((char *)&tmp) + 1);
448         }
449 }
450
451 int
452 fdt_fixup(void)
453 {
454         const char *env;
455         char *ethstr;
456         int chosen, err, eth_no, len;
457         struct sys_info *si;
458
459         env = NULL;
460         eth_no = 0;
461         ethstr = NULL;
462         len = 0;
463
464         if (!fdtp) {
465                 err = fdt_setup_fdtp();
466                 if (err) {
467                         sprintf(command_errbuf, "Could not perform blob "
468                             "fixups. Error code: %d\n", err);
469                         return (err);
470                 }
471         }
472
473         /* Create /chosen node (if not exists) */
474         if ((chosen = fdt_subnode_offset(fdtp, 0, "chosen")) ==
475             -FDT_ERR_NOTFOUND)
476                 chosen = fdt_add_subnode(fdtp, 0, "chosen");
477
478         /* Value assigned to fixup-applied does not matter. */
479         if (fdt_getprop(fdtp, chosen, "fixup-applied", NULL))
480                 return (CMD_OK);
481
482         /* Acquire sys_info */
483         si = ub_get_sys_info();
484
485         while ((env = ub_env_enum(env)) != NULL) {
486                 if (strncmp(env, "eth", 3) == 0 &&
487                     strncmp(env + (strlen(env) - 4), "addr", 4) == 0) {
488                         /*
489                          * Handle Ethernet addrs: parse uboot env eth%daddr
490                          */
491
492                         if (!eth_no) {
493                                 /*
494                                  * Check how many chars we will need to store
495                                  * maximal eth iface number.
496                                  */
497                                 len = strlen(STRINGIFY(TMP_MAX_ETH)) +
498                                     strlen("ethernet");
499
500                                 /*
501                                  * Reserve mem for string "ethernet" and len
502                                  * chars for iface no.
503                                  */
504                                 ethstr = (char *)malloc(len * sizeof(char));
505                                 bzero(ethstr, len * sizeof(char));
506                                 strcpy(ethstr, "ethernet0");
507                         }
508
509                         /* Modify blob */
510                         fixup_ethernet(env, ethstr, &eth_no, len);
511
512                 } else if (strcmp(env, "consoledev") == 0)
513                         fixup_stdout(env);
514         }
515
516         /* Modify cpu(s) and bus clock frequenties in /cpus node [Hz] */
517         fixup_cpubusfreqs(si->clk_cpu, si->clk_bus);
518
519         /* Fixup memory regions */
520         fixup_memory(si);
521
522         fdt_setprop(fdtp, chosen, "fixup-applied", NULL, 0);
523
524         return (CMD_OK);
525 }
526
527 int
528 command_fdt_internal(int argc, char *argv[])
529 {
530         cmdf_t *cmdh;
531         char *cmd;
532         int i, err;
533
534         if (argc < 2) {
535                 command_errmsg = "usage is 'fdt <command> [<args>]";
536                 return (CMD_ERROR);
537         }
538
539         /*
540          * Check if uboot env vars were parsed already. If not, do it now.
541          */
542         fdt_fixup();
543
544         /*
545          * Validate fdt <command>.
546          */
547         cmd = strdup(argv[1]);
548         i = 0;
549         cmdh = NULL;
550         while (!(commands[i].name == NULL)) {
551                 if (strcmp(cmd, commands[i].name) == 0) {
552                         /* found it */
553                         cmdh = commands[i].handler;
554                         break;
555                 }
556                 i++;
557         }
558         if (cmdh == NULL) {
559                 command_errmsg = "unknown command";
560                 return (CMD_ERROR);
561         }
562
563         if (!fdtp)
564                 if (fdt_setup_fdtp())
565                         return (CMD_ERROR);
566
567         /*
568          * Call command handler.
569          */
570         err = (*cmdh)(argc, argv);
571
572         return (err);
573 }
574
575 static int
576 fdt_cmd_cd(int argc, char *argv[])
577 {
578         char *path;
579         char tmp[FDT_CWD_LEN];
580         int len, o;
581
582         path = (argc > 2) ? argv[2] : "/";
583
584         if (path[0] == '/') {
585                 len = strlen(path);
586                 if (len >= FDT_CWD_LEN)
587                         goto fail;
588         } else {
589                 /* Handle path specification relative to cwd */
590                 len = strlen(cwd) + strlen(path) + 1;
591                 if (len >= FDT_CWD_LEN)
592                         goto fail;
593
594                 strcpy(tmp, cwd);
595                 strcat(tmp, "/");
596                 strcat(tmp, path);
597                 path = tmp;
598         }
599
600         o = fdt_path_offset(fdtp, path);
601         if (o < 0) {
602                 sprintf(command_errbuf, "could not find node: '%s'", path);
603                 return (CMD_ERROR);
604         }
605
606         strcpy(cwd, path);
607         return (CMD_OK);
608
609 fail:
610         sprintf(command_errbuf, "path too long: %d, max allowed: %d",
611             len, FDT_CWD_LEN - 1);
612         return (CMD_ERROR);
613 }
614
615 static int
616 fdt_cmd_hdr(int argc __unused, char *argv[] __unused)
617 {
618         char line[80];
619         int ver;
620
621         if (fdtp == NULL) {
622                 command_errmsg = "no device tree blob pointer?!";
623                 return (CMD_ERROR);
624         }
625
626         ver = fdt_version(fdtp);
627         pager_open();
628         sprintf(line, "\nFlattened device tree header (%p):\n", fdtp);
629         pager_output(line);
630         sprintf(line, " magic                   = 0x%08x\n", fdt_magic(fdtp));
631         pager_output(line);
632         sprintf(line, " size                    = %d\n", fdt_totalsize(fdtp));
633         pager_output(line);
634         sprintf(line, " off_dt_struct           = 0x%08x\n",
635             fdt_off_dt_struct(fdtp));
636         pager_output(line);
637         sprintf(line, " off_dt_strings          = 0x%08x\n",
638             fdt_off_dt_strings(fdtp));
639         pager_output(line);
640         sprintf(line, " off_mem_rsvmap          = 0x%08x\n",
641             fdt_off_mem_rsvmap(fdtp));
642         pager_output(line);
643         sprintf(line, " version                 = %d\n", ver); 
644         pager_output(line);
645         sprintf(line, " last compatible version = %d\n",
646             fdt_last_comp_version(fdtp));
647         pager_output(line);
648         if (ver >= 2) {
649                 sprintf(line, " boot_cpuid              = %d\n",
650                     fdt_boot_cpuid_phys(fdtp));
651                 pager_output(line);
652         }
653         if (ver >= 3) {
654                 sprintf(line, " size_dt_strings         = %d\n",
655                     fdt_size_dt_strings(fdtp));
656                 pager_output(line);
657         }
658         if (ver >= 17) {
659                 sprintf(line, " size_dt_struct          = %d\n",
660                     fdt_size_dt_struct(fdtp));
661                 pager_output(line);
662         }
663         pager_close();
664
665         return (CMD_OK);
666 }
667
668 static int
669 fdt_cmd_ls(int argc, char *argv[])
670 {
671         const char *prevname[FDT_MAX_DEPTH] = { NULL };
672         const char *name;
673         char *path;
674         int i, o, depth, len;
675
676         path = (argc > 2) ? argv[2] : NULL;
677         if (path == NULL)
678                 path = cwd;
679
680         o = fdt_path_offset(fdtp, path);
681         if (o < 0) {
682                 sprintf(command_errbuf, "could not find node: '%s'", path);
683                 return (CMD_ERROR);
684         }
685
686         for (depth = 0;
687             (o >= 0) && (depth >= 0);
688             o = fdt_next_node(fdtp, o, &depth)) {
689
690                 name = fdt_get_name(fdtp, o, &len);
691
692                 if (depth > FDT_MAX_DEPTH) {
693                         printf("max depth exceeded: %d\n", depth);
694                         continue;
695                 }
696
697                 prevname[depth] = name;
698
699                 /* Skip root (i = 1) when printing devices */
700                 for (i = 1; i <= depth; i++) {
701                         if (prevname[i] == NULL)
702                                 break;
703
704                         if (strcmp(cwd, "/") == 0)
705                                 printf("/");
706                         printf("%s", prevname[i]);
707                 }
708                 printf("\n");
709         }
710
711         return (CMD_OK);
712 }
713
714 static __inline int
715 isprint(int c)
716 {
717
718         return (c >= ' ' && c <= 0x7e);
719 }
720
721 static int
722 fdt_isprint(const void *data, int len, int *count)
723 {
724         const char *d;
725         char ch;
726         int yesno, i;
727
728         if (len == 0)
729                 return (0);
730
731         d = (const char *)data;
732         if (d[len - 1] != '\0')
733                 return (0);
734
735         *count = 0;
736         yesno = 1;
737         for (i = 0; i < len; i++) {
738                 ch = *(d + i);
739                 if (isprint(ch) || (ch == '\0' && i > 0)) {
740                         /* Count strings */
741                         if (ch == '\0')
742                                 (*count)++;
743                         continue;
744                 }
745
746                 yesno = 0;
747                 break;
748         }
749
750         return (yesno);
751 }
752
753 static int
754 fdt_data_str(const void *data, int len, int count, char **buf)
755 {
756         char tmp[80], *b;
757         const char *d;
758         int i, l;
759
760         /*
761          * Calculate the length for the string and allocate memory.
762          *
763          * Note len already includes at least one terminator.
764          */
765         l = len;
766         if (count > 1) {
767                 /*
768                  * Each token had already a terminator buried in 'len', but we
769                  * only need one eventually, don't count space for these.
770                  */
771                 l -= count - 1;
772
773                 /* Each consecutive token requires a ", " separator. */
774                 l += count * 2;
775         }
776         /* Space for surrounding double quotes. */
777         l += count * 2;
778
779         b = (char *)malloc(l);
780         if (b == NULL)
781                 return (1);
782         b[0] = '\0';
783
784         /*
785          * Now that we have space, format the string.
786          */
787         i = 0;
788         do {
789                 d = (const char *)data + i;
790                 l = strlen(d) + 1;
791
792                 sprintf(tmp, "\"%s\"%s", d,
793                     (i + l) < len ?  ", " : "");
794                 strcat(b, tmp);
795
796                 i += l;
797
798         } while (i < len);
799         *buf = b;
800
801         return (0);
802 }
803
804 static int
805 fdt_data_cell(const void *data, int len, char **buf)
806 {
807         char tmp[80], *b;
808         const uint32_t *c;
809         int count, i, l;
810
811         /* Number of cells */
812         count = len / 4;
813
814         /*
815          * Calculate the length for the string and allocate memory.
816          */
817
818         /* Each byte translates to 2 output characters */
819         l = len * 2;
820         if (count > 1) {
821                 /* Each consecutive cell requires a " " separator. */
822                 l += (count - 1) * 1;
823         }
824         /* Each cell will have a "0x" prefix */
825         l += count * 2;
826         /* Space for surrounding <> and terminator */
827         l += 3;
828
829         b = (char *)malloc(l);
830         if (b == NULL)
831                 return (1);
832
833         b[0] = '\0';
834         strcat(b, "<");
835
836         for (i = 0; i < len; i += 4) {
837                 c = (const uint32_t *)((const uint8_t *)data + i);
838                 sprintf(tmp, "0x%08x%s", fdt32_to_cpu(*c),
839                     i < (len - 4) ? " " : "");
840                 strcat(b, tmp);
841         }
842         strcat(b, ">");
843         *buf = b;
844
845         return (0);
846 }
847
848 static int
849 fdt_data_bytes(const void *data, int len, char **buf)
850 {
851         char tmp[80], *b;
852         const char *d;
853         int i, l;
854
855         /*
856          * Calculate the length for the string and allocate memory.
857          */
858
859         /* Each byte translates to 2 output characters */
860         l = len * 2;
861         if (len > 1)
862                 /* Each consecutive byte requires a " " separator. */
863                 l += (len - 1) * 1;
864         /* Each byte will have a "0x" prefix */
865         l += len * 2;
866         /* Space for surrounding [] and terminator. */
867         l += 3;
868
869         b = (char *)malloc(l);
870         if (b == NULL)
871                 return (1);
872
873         b[0] = '\0';
874         strcat(b, "[");
875
876         for (i = 0, d = data; i < len; i++) {
877                 sprintf(tmp, "0x%02x%s", d[i], i < len - 1 ? " " : "");
878                 strcat(b, tmp);
879         }
880         strcat(b, "]");
881         *buf = b;
882
883         return (0);
884 }
885
886 static int
887 fdt_data_fmt(const void *data, int len, char **buf)
888 {
889         int count;
890
891         if (len == 0) {
892                 *buf = NULL;
893                 return (1);
894         }
895
896         if (fdt_isprint(data, len, &count))
897                 return (fdt_data_str(data, len, count, buf));
898
899         else if ((len % 4) == 0)
900                 return (fdt_data_cell(data, len, buf));
901
902         else
903                 return (fdt_data_bytes(data, len, buf));
904 }
905
906 static int
907 fdt_prop(int offset)
908 {
909         char *line, *buf;
910         const struct fdt_property *prop;
911         const char *name;
912         const void *data;
913         int len, rv;
914
915         line = NULL;
916         prop = fdt_offset_ptr(fdtp, offset, sizeof(*prop));
917         if (prop == NULL)
918                 return (1);
919
920         name = fdt_string(fdtp, fdt32_to_cpu(prop->nameoff));
921         len = fdt32_to_cpu(prop->len);
922
923         rv = 0;
924         buf = NULL;
925         if (len == 0) {
926                 /* Property without value */
927                 line = (char *)malloc(strlen(name) + 2);
928                 if (line == NULL) {
929                         rv = 2;
930                         goto out2;
931                 }
932                 sprintf(line, "%s\n", name);
933                 goto out1;
934         }
935
936         /*
937          * Process property with value
938          */
939         data = prop->data;
940
941         if (fdt_data_fmt(data, len, &buf) != 0) {
942                 rv = 3;
943                 goto out2;
944         }
945
946         line = (char *)malloc(strlen(name) + strlen(FDT_PROP_SEP) +
947             strlen(buf) + 2);
948         if (line == NULL) {
949                 sprintf(command_errbuf, "could not allocate space for string");
950                 rv = 4;
951                 goto out2;
952         }
953
954         sprintf(line, "%s" FDT_PROP_SEP "%s\n", name, buf);
955
956 out1:
957         pager_open();
958         pager_output(line);
959         pager_close();
960
961 out2:
962         if (buf)
963                 free(buf);
964
965         if (line)
966                 free(line);
967
968         return (rv);
969 }
970
971 static int
972 fdt_modprop(int nodeoff, char *propname, void *value, char mode)
973 {
974         uint32_t cells[100];
975         char *buf;
976         int len, rv;
977         const struct fdt_property *p;
978
979         p = fdt_get_property(fdtp, nodeoff, propname, NULL);
980
981         if (p != NULL) {
982                 if (mode == 1) {
983                          /* Adding inexistant value in mode 1 is forbidden */
984                         sprintf(command_errbuf, "property already exists!");
985                         return (CMD_ERROR);
986                 }
987         } else if (mode == 0) {
988                 sprintf(command_errbuf, "property does not exist!");
989                 return (CMD_ERROR);
990         }
991         len = strlen(value);
992         rv = 0;
993         buf = (char *)value;
994
995         switch (*buf) {
996         case '&':
997                 /* phandles */
998                 break;
999         case '<':
1000                 /* Data cells */
1001                 len = fdt_strtovect(buf, (void *)&cells, 100,
1002                     sizeof(uint32_t));
1003
1004                 rv = fdt_setprop(fdtp, nodeoff, propname, &cells,
1005                     len * sizeof(uint32_t));
1006                 break;
1007         case '[':
1008                 /* Data bytes */
1009                 len = fdt_strtovect(buf, (void *)&cells, 100,
1010                     sizeof(uint8_t));
1011
1012                 rv = fdt_setprop(fdtp, nodeoff, propname, &cells,
1013                     len * sizeof(uint8_t));
1014                 break;
1015         case '"':
1016         default:
1017                 /* Default -- string */
1018                 rv = fdt_setprop_string(fdtp, nodeoff, propname, value);
1019                 break;
1020         }
1021
1022         return (rv);
1023 }
1024
1025 /* Merge strings from argv into a single string */
1026 static int
1027 fdt_merge_strings(int argc, char *argv[], int start, char **buffer)
1028 {
1029         char *buf;
1030         int i, idx, sz;
1031
1032         *buffer = NULL;
1033         sz = 0;
1034
1035         for (i = start; i < argc; i++)
1036                 sz += strlen(argv[i]);
1037
1038         /* Additional bytes for whitespaces between args */
1039         sz += argc - start;
1040
1041         buf = (char *)malloc(sizeof(char) * sz);
1042         bzero(buf, sizeof(char) * sz);
1043
1044         if (buf == NULL) {
1045                 sprintf(command_errbuf, "could not allocate space "
1046                     "for string");
1047                 return (1);
1048         }
1049
1050         idx = 0;
1051         for (i = start, idx = 0; i < argc; i++) {
1052                 strcpy(buf + idx, argv[i]);
1053                 idx += strlen(argv[i]);
1054                 buf[idx] = ' ';
1055                 idx++;
1056         }
1057         buf[sz - 1] = '\0';
1058         *buffer = buf;
1059         return (0);
1060 }
1061
1062 /* Extract offset and name of node/property from a given path */
1063 static int
1064 fdt_extract_nameloc(char **pathp, char **namep, int *nodeoff)
1065 {
1066         int o;
1067         char *path = *pathp, *name = NULL, *subpath = NULL;
1068
1069         subpath = strrchr(path, '/');
1070         if (subpath == NULL) {
1071                 o = fdt_path_offset(fdtp, cwd);
1072                 name = path;
1073                 path = (char *)&cwd;
1074         } else {
1075                 *subpath = '\0';
1076                 if (strlen(path) == 0)
1077                         path = cwd;
1078
1079                 name = subpath + 1;
1080                 o = fdt_path_offset(fdtp, path);
1081         }
1082
1083         if (strlen(name) == 0) {
1084                 sprintf(command_errbuf, "name not specified");
1085                 return (1);
1086         }
1087         if (o < 0) {
1088                 sprintf(command_errbuf, "could not find node: '%s'", path);
1089                 return (1);
1090         }
1091         *namep = name;
1092         *nodeoff = o;
1093         *pathp = path;
1094         return (0);
1095 }
1096
1097 static int
1098 fdt_cmd_prop(int argc, char *argv[])
1099 {
1100         char *path, *propname, *value;
1101         int o, next, depth, rv;
1102         uint32_t tag;
1103
1104         path = (argc > 2) ? argv[2] : NULL;
1105
1106         value = NULL;
1107
1108         if (argc > 3) {
1109                 /* Merge property value strings into one */
1110                 if (fdt_merge_strings(argc, argv, 3, &value) != 0)
1111                         return (CMD_ERROR);
1112         } else
1113                 value = NULL;
1114
1115         if (path == NULL)
1116                 path = cwd;
1117
1118         rv = CMD_OK;
1119
1120         if (value) {
1121                 /* If value is specified -- try to modify prop. */
1122                 if (fdt_extract_nameloc(&path, &propname, &o) != 0)
1123                         return (CMD_ERROR);
1124
1125                 rv = fdt_modprop(o, propname, value, 0);
1126                 if (rv)
1127                         return (CMD_ERROR);
1128                 return (CMD_OK);
1129
1130         }
1131         /* User wants to display properties */
1132         o = fdt_path_offset(fdtp, path);
1133
1134         if (o < 0) {
1135                 sprintf(command_errbuf, "could not find node: '%s'", path);
1136                 rv = CMD_ERROR;
1137                 goto out;
1138         }
1139
1140         depth = 0;
1141         while (depth >= 0) {
1142                 tag = fdt_next_tag(fdtp, o, &next);
1143                 switch (tag) {
1144                 case FDT_NOP:
1145                         break;
1146                 case FDT_PROP:
1147                         if (depth > 1)
1148                                 /* Don't process properties of nested nodes */
1149                                 break;
1150
1151                         if (fdt_prop(o) != 0) {
1152                                 sprintf(command_errbuf, "could not process "
1153                                     "property");
1154                                 rv = CMD_ERROR;
1155                                 goto out;
1156                         }
1157                         break;
1158                 case FDT_BEGIN_NODE:
1159                         depth++;
1160                         if (depth > FDT_MAX_DEPTH) {
1161                                 printf("warning: nesting too deep: %d\n",
1162                                     depth);
1163                                 goto out;
1164                         }
1165                         break;
1166                 case FDT_END_NODE:
1167                         depth--;
1168                         if (depth == 0)
1169                                 /*
1170                                  * This is the end of our starting node, force
1171                                  * the loop finish.
1172                                  */
1173                                 depth--;
1174                         break;
1175                 }
1176                 o = next;
1177         }
1178 out:
1179         return (rv);
1180 }
1181
1182 static int
1183 fdt_cmd_mkprop(int argc, char *argv[])
1184 {
1185         int o;
1186         char *path, *propname, *value;
1187
1188         path = (argc > 2) ? argv[2] : NULL;
1189
1190         value = NULL;
1191
1192         if (argc > 3) {
1193                 /* Merge property value strings into one */
1194                 if (fdt_merge_strings(argc, argv, 3, &value) != 0)
1195                         return (CMD_ERROR);
1196         } else
1197                 value = NULL;
1198
1199         if (fdt_extract_nameloc(&path, &propname, &o) != 0)
1200                 return (CMD_ERROR);
1201
1202         if (fdt_modprop(o, propname, value, 1))
1203                 return (CMD_ERROR);
1204
1205         return (CMD_OK);
1206 }
1207
1208 static int
1209 fdt_cmd_rm(int argc, char *argv[])
1210 {
1211         int o, rv;
1212         char *path = NULL, *propname;
1213
1214         if (argc > 2)
1215                 path = argv[2];
1216         else {
1217                 sprintf(command_errbuf, "no node/property name specified");
1218                 return (CMD_ERROR);
1219         }
1220
1221         o = fdt_path_offset(fdtp, path);
1222         if (o < 0) {
1223                 /* If node not found -- try to find & delete property */
1224                 if (fdt_extract_nameloc(&path, &propname, &o) != 0)
1225                         return (CMD_ERROR);
1226
1227                 if ((rv = fdt_delprop(fdtp, o, propname)) != 0) {
1228                         sprintf(command_errbuf, "could not delete"
1229                             "%s\n", (rv == -FDT_ERR_NOTFOUND) ?
1230                             "(property/node does not exist)" : "");
1231                         return (CMD_ERROR);
1232
1233                 } else
1234                         return (CMD_OK);
1235         }
1236         /* If node exists -- remove node */
1237         rv = fdt_del_node(fdtp, o);
1238         if (rv) {
1239                 sprintf(command_errbuf, "could not delete node");
1240                 return (CMD_ERROR);
1241         }
1242         return (CMD_OK);
1243 }
1244
1245 static int
1246 fdt_cmd_mknode(int argc, char *argv[])
1247 {
1248         int o, rv;
1249         char *path = NULL, *nodename = NULL;
1250
1251         if (argc > 2)
1252                 path = argv[2];
1253         else {
1254                 sprintf(command_errbuf, "no node name specified");
1255                 return (CMD_ERROR);
1256         }
1257
1258         if (fdt_extract_nameloc(&path, &nodename, &o) != 0)
1259                 return (CMD_ERROR);
1260
1261         rv = fdt_add_subnode(fdtp, o, nodename);
1262
1263         if (rv < 0) {
1264                 sprintf(command_errbuf, "could not delete node %s\n",
1265                     (rv == -FDT_ERR_NOTFOUND) ?
1266                     "(node does not exist)" : "");
1267                 return (CMD_ERROR);
1268         }
1269         return (CMD_OK);
1270 }
1271
1272 static int
1273 fdt_cmd_pwd(int argc, char *argv[])
1274 {
1275         char line[80];
1276
1277         pager_open();
1278         sprintf(line, "%s\n", cwd);
1279         pager_output(line);
1280         pager_close();
1281         return (CMD_OK);
1282 }
1283
1284 static int
1285 fdt_cmd_nyi(int argc, char *argv[])
1286 {
1287
1288         printf("command not yet implemented\n");
1289         return (CMD_ERROR);
1290 }