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