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: releng/9.0/sys/boot/fdt/fdt_loader_cmd.c 208538 2010-05-25 15:21:39Z raj $");
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, ð_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 }
Cache object: 0eb644d1ef4f06cfd08b33607e5640df
|