1 /*- 2 * Copyright (c) 2000 Benno Rice <benno@jeamland.net> 3 * Copyright (c) 2000 Stephane Potvin <sepotvin@videotron.ca> 4 * Copyright (c) 2007-2008 Semihalf, Rafal Jaworowski <raj@semihalf.com> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/param.h> 30 31 #include <stand.h> 32 33 #include "api_public.h" 34 #include "bootstrap.h" 35 #include "glue.h" 36 #include "libuboot.h" 37 38 #ifndef nitems 39 #define nitems(x) (sizeof((x)) / sizeof((x)[0])) 40 #endif 41 42 #ifndef HEAP_SIZE 43 #define HEAP_SIZE (2 * 1024 * 1024) 44 #endif 45 46 struct uboot_devdesc currdev; 47 struct arch_switch archsw; /* MI/MD interface boundary */ 48 int devs_no; 49 50 uintptr_t uboot_heap_start; 51 uintptr_t uboot_heap_end; 52 53 struct device_type { 54 const char *name; 55 int type; 56 } device_types[] = { 57 { "disk", DEV_TYP_STOR }, 58 { "ide", DEV_TYP_STOR | DT_STOR_IDE }, 59 { "mmc", DEV_TYP_STOR | DT_STOR_MMC }, 60 { "sata", DEV_TYP_STOR | DT_STOR_SATA }, 61 { "scsi", DEV_TYP_STOR | DT_STOR_SCSI }, 62 { "usb", DEV_TYP_STOR | DT_STOR_USB }, 63 { "net", DEV_TYP_NET } 64 }; 65 66 extern char end[]; 67 68 extern unsigned char _etext[]; 69 extern unsigned char _edata[]; 70 extern unsigned char __bss_start[]; 71 extern unsigned char __sbss_start[]; 72 extern unsigned char __sbss_end[]; 73 extern unsigned char _end[]; 74 75 #ifdef LOADER_FDT_SUPPORT 76 extern int command_fdt_internal(int argc, char *argv[]); 77 #endif 78 79 static void 80 dump_sig(struct api_signature *sig) 81 { 82 #ifdef DEBUG 83 printf("signature:\n"); 84 printf(" version\t= %d\n", sig->version); 85 printf(" checksum\t= 0x%08x\n", sig->checksum); 86 printf(" sc entry\t= 0x%08x\n", sig->syscall); 87 #endif 88 } 89 90 static void 91 dump_addr_info(void) 92 { 93 #ifdef DEBUG 94 printf("\naddresses info:\n"); 95 printf(" _etext (sdata) = 0x%08x\n", (uint32_t)_etext); 96 printf(" _edata = 0x%08x\n", (uint32_t)_edata); 97 printf(" __sbss_start = 0x%08x\n", (uint32_t)__sbss_start); 98 printf(" __sbss_end = 0x%08x\n", (uint32_t)__sbss_end); 99 printf(" __sbss_start = 0x%08x\n", (uint32_t)__bss_start); 100 printf(" _end = 0x%08x\n", (uint32_t)_end); 101 printf(" syscall entry = 0x%08x\n", (uint32_t)syscall_ptr); 102 #endif 103 } 104 105 static uint64_t 106 memsize(struct sys_info *si, int flags) 107 { 108 uint64_t size; 109 int i; 110 111 size = 0; 112 for (i = 0; i < si->mr_no; i++) 113 if (si->mr[i].flags == flags && si->mr[i].size) 114 size += (si->mr[i].size); 115 116 return (size); 117 } 118 119 static void 120 meminfo(void) 121 { 122 uint64_t size; 123 struct sys_info *si; 124 int t[3] = { MR_ATTR_DRAM, MR_ATTR_FLASH, MR_ATTR_SRAM }; 125 int i; 126 127 if ((si = ub_get_sys_info()) == NULL) 128 panic("could not retrieve system info"); 129 130 for (i = 0; i < 3; i++) { 131 size = memsize(si, t[i]); 132 if (size > 0) 133 printf("%s: %juMB\n", ub_mem_type(t[i]), 134 (uintmax_t)(size / 1024 / 1024)); 135 } 136 } 137 138 static const char * 139 get_device_type(const char *devstr, int *devtype) 140 { 141 int i; 142 int namelen; 143 struct device_type *dt; 144 145 if (devstr) { 146 for (i = 0; i < nitems(device_types); i++) { 147 dt = &device_types[i]; 148 namelen = strlen(dt->name); 149 if (strncmp(dt->name, devstr, namelen) == 0) { 150 *devtype = dt->type; 151 return (devstr + namelen); 152 } 153 } 154 printf("Unknown device type '%s'\n", devstr); 155 } 156 157 *devtype = DEV_TYP_NONE; 158 return (NULL); 159 } 160 161 static const char * 162 device_typename(int type) 163 { 164 int i; 165 166 for (i = 0; i < nitems(device_types); i++) 167 if (device_types[i].type == type) 168 return (device_types[i].name); 169 170 return ("<unknown>"); 171 } 172 173 /* 174 * Parse a device string into type, unit, slice and partition numbers. A 175 * returned value of -1 for type indicates a search should be done for the 176 * first loadable device, otherwise a returned value of -1 for unit 177 * indicates a search should be done for the first loadable device of the 178 * given type. 179 * 180 * The returned values for slice and partition are interpreted by 181 * disk_open(). 182 * 183 * The device string can be a standard loader(8) disk specifier: 184 * 185 * disk<unit>s<slice> disk0s1 186 * disk<unit>s<slice><partition> disk1s2a 187 * disk<unit>p<partition> disk0p4 188 * 189 * or one of the following formats: 190 * 191 * Valid device strings: For device types: 192 * 193 * <type_name> DEV_TYP_STOR, DEV_TYP_NET 194 * <type_name><unit> DEV_TYP_STOR, DEV_TYP_NET 195 * <type_name><unit>: DEV_TYP_STOR, DEV_TYP_NET 196 * <type_name><unit>:<slice> DEV_TYP_STOR 197 * <type_name><unit>:<slice>. DEV_TYP_STOR 198 * <type_name><unit>:<slice>.<partition> DEV_TYP_STOR 199 * 200 * For valid type names, see the device_types array, above. 201 * 202 * Slice numbers are 1-based. 0 is a wildcard. 203 */ 204 static void 205 get_load_device(int *type, int *unit, int *slice, int *partition) 206 { 207 struct disk_devdesc *dev; 208 char *devstr; 209 const char *p; 210 char *endp; 211 212 *type = DEV_TYP_NONE; 213 *unit = -1; 214 *slice = D_SLICEWILD; 215 *partition = D_PARTWILD; 216 217 devstr = ub_env_get("loaderdev"); 218 if (devstr == NULL) { 219 printf("U-Boot env: loaderdev not set, will probe all devices.\n"); 220 return; 221 } 222 printf("U-Boot env: loaderdev='%s'\n", devstr); 223 224 p = get_device_type(devstr, type); 225 226 /* 227 * If type is DEV_TYP_STOR we have a disk-like device. If the remainder 228 * of the string contains spaces, dots, or a colon in any location other 229 * than the last char, it's legacy format. Otherwise it might be 230 * standard loader(8) format (e.g., disk0s2a or mmc1p12), so try to 231 * parse the remainder of the string as such, and if it works, return 232 * those results. Otherwise we'll fall through to the code that parses 233 * the legacy format. 234 * 235 * disk_parsedev now assumes that it points to the start of the device 236 * name, but since it doesn't know about uboot's usage, just subtract 4 237 * since it always adds 4. This is the least-bad solution since it makes 238 * all the other loader code easier (might be better to create a fake 239 * 'disk...' string, but that's more work than uboot is worth). 240 */ 241 if (*type & DEV_TYP_STOR) { 242 size_t len = strlen(p); 243 if (strcspn(p, " .") == len && strcspn(p, ":") >= len - 1 && 244 disk_parsedev((struct devdesc **)&dev, p - 4, NULL) == 0) { /* Hack */ 245 *unit = dev->dd.d_unit; 246 *slice = dev->d_slice; 247 *partition = dev->d_partition; 248 free(dev); 249 return; 250 } 251 } 252 253 /* Ignore optional spaces after the device name. */ 254 while (*p == ' ') 255 p++; 256 257 /* Unknown device name, or a known name without unit number. */ 258 if ((*type == DEV_TYP_NONE) || (*p == '\0')) { 259 return; 260 } 261 262 /* Malformed unit number. */ 263 if (!isdigit(*p)) { 264 *type = DEV_TYP_NONE; 265 return; 266 } 267 268 /* Guaranteed to extract a number from the string, as *p is a digit. */ 269 *unit = strtol(p, &endp, 10); 270 p = endp; 271 272 /* Known device name with unit number and nothing else. */ 273 if (*p == '\0') { 274 return; 275 } 276 277 /* Device string is malformed beyond unit number. */ 278 if (*p != ':') { 279 *type = DEV_TYP_NONE; 280 *unit = -1; 281 return; 282 } 283 284 p++; 285 286 /* No slice and partition specification. */ 287 if ('\0' == *p ) 288 return; 289 290 /* Only DEV_TYP_STOR devices can have a slice specification. */ 291 if (!(*type & DEV_TYP_STOR)) { 292 *type = DEV_TYP_NONE; 293 *unit = -1; 294 return; 295 } 296 297 *slice = strtoul(p, &endp, 10); 298 299 /* Malformed slice number. */ 300 if (p == endp) { 301 *type = DEV_TYP_NONE; 302 *unit = -1; 303 *slice = D_SLICEWILD; 304 return; 305 } 306 307 p = endp; 308 309 /* No partition specification. */ 310 if (*p == '\0') 311 return; 312 313 /* Device string is malformed beyond slice number. */ 314 if (*p != '.') { 315 *type = DEV_TYP_NONE; 316 *unit = -1; 317 *slice = D_SLICEWILD; 318 return; 319 } 320 321 p++; 322 323 /* No partition specification. */ 324 if (*p == '\0') 325 return; 326 327 *partition = strtol(p, &endp, 10); 328 p = endp; 329 330 /* Full, valid device string. */ 331 if (*endp == '\0') 332 return; 333 334 /* Junk beyond partition number. */ 335 *type = DEV_TYP_NONE; 336 *unit = -1; 337 *slice = D_SLICEWILD; 338 *partition = D_PARTWILD; 339 } 340 341 static void 342 print_disk_probe_info(void) 343 { 344 char slice[32]; 345 char partition[32]; 346 347 if (currdev.d_disk.d_slice == D_SLICENONE) 348 strlcpy(slice, "<none>", sizeof(slice)); 349 else if (currdev.d_disk.d_slice == D_SLICEWILD) 350 strlcpy(slice, "<auto>", sizeof(slice)); 351 else 352 snprintf(slice, sizeof(slice), "%d", currdev.d_disk.d_slice); 353 354 if (currdev.d_disk.d_partition == D_PARTNONE) 355 strlcpy(partition, "<none>", sizeof(partition)); 356 else if (currdev.d_disk.d_partition == D_PARTWILD) 357 strlcpy(partition, "<auto>", sizeof(partition)); 358 else 359 snprintf(partition, sizeof(partition), "%d", 360 currdev.d_disk.d_partition); 361 362 printf(" Checking unit=%d slice=%s partition=%s...", 363 currdev.dd.d_unit, slice, partition); 364 365 } 366 367 static int 368 probe_disks(int devidx, int load_type, int load_unit, int load_slice, 369 int load_partition) 370 { 371 int open_result, unit; 372 struct open_file f; 373 374 currdev.d_disk.d_slice = load_slice; 375 currdev.d_disk.d_partition = load_partition; 376 377 f.f_devdata = &currdev; 378 open_result = -1; 379 380 if (load_type == -1) { 381 printf(" Probing all disk devices...\n"); 382 /* Try each disk in succession until one works. */ 383 for (currdev.dd.d_unit = 0; currdev.dd.d_unit < UB_MAX_DEV; 384 currdev.dd.d_unit++) { 385 print_disk_probe_info(); 386 open_result = devsw[devidx]->dv_open(&f, &currdev); 387 if (open_result == 0) { 388 printf(" good.\n"); 389 return (0); 390 } 391 printf("\n"); 392 } 393 return (-1); 394 } 395 396 if (load_unit == -1) { 397 printf(" Probing all %s devices...\n", device_typename(load_type)); 398 /* Try each disk of given type in succession until one works. */ 399 for (unit = 0; unit < UB_MAX_DEV; unit++) { 400 currdev.dd.d_unit = uboot_diskgetunit(load_type, unit); 401 if (currdev.dd.d_unit == -1) 402 break; 403 print_disk_probe_info(); 404 open_result = devsw[devidx]->dv_open(&f, &currdev); 405 if (open_result == 0) { 406 printf(" good.\n"); 407 return (0); 408 } 409 printf("\n"); 410 } 411 return (-1); 412 } 413 414 if ((currdev.dd.d_unit = uboot_diskgetunit(load_type, load_unit)) != -1) { 415 print_disk_probe_info(); 416 open_result = devsw[devidx]->dv_open(&f,&currdev); 417 if (open_result == 0) { 418 printf(" good.\n"); 419 return (0); 420 } 421 printf("\n"); 422 } 423 424 printf(" Requested disk type/unit/slice/partition not found\n"); 425 return (-1); 426 } 427 428 int 429 main(int argc, char **argv) 430 { 431 struct api_signature *sig = NULL; 432 int load_type, load_unit, load_slice, load_partition; 433 int i; 434 const char *ldev; 435 436 /* 437 * We first check if a command line argument was passed to us containing 438 * API's signature address. If it wasn't then we try to search for the 439 * API signature via the usual hinted address. 440 * If we can't find the magic signature and related info, exit with a 441 * unique error code that U-Boot reports as "## Application terminated, 442 * rc = 0xnnbadab1". Hopefully 'badab1' looks enough like "bad api" to 443 * provide a clue. It's better than 0xffffffff anyway. 444 */ 445 if (!api_parse_cmdline_sig(argc, argv, &sig) && !api_search_sig(&sig)) 446 return (0x01badab1); 447 448 syscall_ptr = sig->syscall; 449 if (syscall_ptr == NULL) 450 return (0x02badab1); 451 452 if (sig->version > API_SIG_VERSION) 453 return (0x03badab1); 454 455 /* Clear BSS sections */ 456 bzero(__sbss_start, __sbss_end - __sbss_start); 457 bzero(__bss_start, _end - __bss_start); 458 459 /* 460 * Initialise the heap as early as possible. Once this is done, 461 * alloc() is usable. We are using the stack u-boot set up near the top 462 * of physical ram; hopefully there is sufficient space between the end 463 * of our bss and the bottom of the u-boot stack to avoid overlap. 464 */ 465 uboot_heap_start = round_page((uintptr_t)end); 466 uboot_heap_end = uboot_heap_start + HEAP_SIZE; 467 setheap((void *)uboot_heap_start, (void *)uboot_heap_end); 468 469 /* 470 * Set up console. 471 */ 472 cons_probe(); 473 printf("Compatible U-Boot API signature found @%p\n", sig); 474 475 printf("\n%s", bootprog_info); 476 printf("\n"); 477 478 dump_sig(sig); 479 dump_addr_info(); 480 481 meminfo(); 482 483 archsw.arch_loadaddr = uboot_loadaddr; 484 archsw.arch_getdev = uboot_getdev; 485 archsw.arch_copyin = uboot_copyin; 486 archsw.arch_copyout = uboot_copyout; 487 archsw.arch_readin = uboot_readin; 488 archsw.arch_autoload = uboot_autoload; 489 490 /* Set up currdev variable to have hooks in place. */ 491 env_setenv("currdev", EV_VOLATILE, "", uboot_setcurrdev, env_nounset); 492 493 /* 494 * Enumerate U-Boot devices 495 */ 496 if ((devs_no = ub_dev_enum()) == 0) { 497 printf("no U-Boot devices found"); 498 goto do_interact; 499 } 500 printf("Number of U-Boot devices: %d\n", devs_no); 501 502 get_load_device(&load_type, &load_unit, &load_slice, &load_partition); 503 504 /* 505 * March through the device switch probing for things. 506 */ 507 for (i = 0; devsw[i] != NULL; i++) { 508 509 if (devsw[i]->dv_init == NULL) 510 continue; 511 if ((devsw[i]->dv_init)() != 0) 512 continue; 513 514 printf("Found U-Boot device: %s\n", devsw[i]->dv_name); 515 516 currdev.dd.d_dev = devsw[i]; 517 currdev.dd.d_unit = 0; 518 519 if ((load_type == DEV_TYP_NONE || (load_type & DEV_TYP_STOR)) && 520 strcmp(devsw[i]->dv_name, "disk") == 0) { 521 if (probe_disks(i, load_type, load_unit, load_slice, 522 load_partition) == 0) 523 break; 524 } 525 526 if ((load_type == DEV_TYP_NONE || (load_type & DEV_TYP_NET)) && 527 strcmp(devsw[i]->dv_name, "net") == 0) 528 break; 529 } 530 531 /* 532 * If we couldn't find a boot device, return an error to u-boot. 533 * U-boot may be running a boot script that can try something different 534 * so returning an error is better than forcing a reboot. 535 */ 536 if (devsw[i] == NULL) { 537 printf("No boot device found!\n"); 538 return (0xbadef1ce); 539 } 540 541 ldev = devformat(&currdev.dd); 542 env_setenv("currdev", EV_VOLATILE, ldev, uboot_setcurrdev, env_nounset); 543 env_setenv("loaddev", EV_VOLATILE, ldev, env_noset, env_nounset); 544 printf("Booting from %s\n", ldev); 545 546 do_interact: 547 setenv("LINES", "24", 1); /* optional */ 548 setenv("prompt", "loader>", 1); 549 #ifdef __powerpc__ 550 setenv("usefdt", "1", 1); 551 #endif 552 553 interact(); /* doesn't return */ 554 555 return (0); 556 } 557 558 559 COMMAND_SET(heap, "heap", "show heap usage", command_heap); 560 static int 561 command_heap(int argc, char *argv[]) 562 { 563 564 printf("heap base at %p, top at %p, used %td\n", end, sbrk(0), 565 sbrk(0) - end); 566 567 return (CMD_OK); 568 } 569 570 COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot); 571 static int 572 command_reboot(int argc, char *argv[]) 573 { 574 575 printf("Resetting...\n"); 576 ub_reset(); 577 578 printf("Reset failed!\n"); 579 while (1); 580 __unreachable(); 581 } 582 583 COMMAND_SET(devinfo, "devinfo", "show U-Boot devices", command_devinfo); 584 static int 585 command_devinfo(int argc, char *argv[]) 586 { 587 int i; 588 589 if ((devs_no = ub_dev_enum()) == 0) { 590 command_errmsg = "no U-Boot devices found!?"; 591 return (CMD_ERROR); 592 } 593 594 printf("U-Boot devices:\n"); 595 for (i = 0; i < devs_no; i++) { 596 ub_dump_di(i); 597 printf("\n"); 598 } 599 return (CMD_OK); 600 } 601 602 COMMAND_SET(sysinfo, "sysinfo", "show U-Boot system info", command_sysinfo); 603 static int 604 command_sysinfo(int argc, char *argv[]) 605 { 606 struct sys_info *si; 607 608 if ((si = ub_get_sys_info()) == NULL) { 609 command_errmsg = "could not retrieve U-Boot sys info!?"; 610 return (CMD_ERROR); 611 } 612 613 printf("U-Boot system info:\n"); 614 ub_dump_si(si); 615 return (CMD_OK); 616 } 617 618 enum ubenv_action { 619 UBENV_UNKNOWN, 620 UBENV_SHOW, 621 UBENV_IMPORT 622 }; 623 624 static void 625 handle_uboot_env_var(enum ubenv_action action, const char * var) 626 { 627 char ldvar[128]; 628 const char *val; 629 char *wrk; 630 int len; 631 632 /* 633 * On an import with the variable name formatted as ldname=ubname, 634 * import the uboot variable ubname into the loader variable ldname, 635 * otherwise the historical behavior is to import to uboot.ubname. 636 */ 637 if (action == UBENV_IMPORT) { 638 len = strcspn(var, "="); 639 if (len == 0) { 640 printf("name cannot start with '=': '%s'\n", var); 641 return; 642 } 643 if (var[len] == 0) { 644 strcpy(ldvar, "uboot."); 645 strncat(ldvar, var, sizeof(ldvar) - 7); 646 } else { 647 len = MIN(len, sizeof(ldvar) - 1); 648 strncpy(ldvar, var, len); 649 ldvar[len] = 0; 650 var = &var[len + 1]; 651 } 652 } 653 654 /* 655 * If the user prepended "uboot." (which is how they usually see these 656 * names) strip it off as a convenience. 657 */ 658 if (strncmp(var, "uboot.", 6) == 0) { 659 var = &var[6]; 660 } 661 662 /* If there is no variable name left, punt. */ 663 if (var[0] == 0) { 664 printf("empty variable name\n"); 665 return; 666 } 667 668 val = ub_env_get(var); 669 if (action == UBENV_SHOW) { 670 if (val == NULL) 671 printf("uboot.%s is not set\n", var); 672 else 673 printf("uboot.%s=%s\n", var, val); 674 } else if (action == UBENV_IMPORT) { 675 if (val != NULL) { 676 setenv(ldvar, val, 1); 677 } 678 } 679 } 680 681 static int 682 command_ubenv(int argc, char *argv[]) 683 { 684 enum ubenv_action action; 685 const char *var; 686 int i; 687 688 action = UBENV_UNKNOWN; 689 if (argc > 1) { 690 if (strcasecmp(argv[1], "import") == 0) 691 action = UBENV_IMPORT; 692 else if (strcasecmp(argv[1], "show") == 0) 693 action = UBENV_SHOW; 694 } 695 if (action == UBENV_UNKNOWN) { 696 command_errmsg = "usage: 'ubenv <import|show> [var ...]"; 697 return (CMD_ERROR); 698 } 699 700 if (argc > 2) { 701 for (i = 2; i < argc; i++) 702 handle_uboot_env_var(action, argv[i]); 703 } else { 704 var = NULL; 705 for (;;) { 706 if ((var = ub_env_enum(var)) == NULL) 707 break; 708 handle_uboot_env_var(action, var); 709 } 710 } 711 712 return (CMD_OK); 713 } 714 COMMAND_SET(ubenv, "ubenv", "show or import U-Boot env vars", command_ubenv); 715 716 #ifdef LOADER_FDT_SUPPORT 717 /* 718 * Since proper fdt command handling function is defined in fdt_loader_cmd.c, 719 * and declaring it as extern is in contradiction with COMMAND_SET() macro 720 * (which uses static pointer), we're defining wrapper function, which 721 * calls the proper fdt handling routine. 722 */ 723 static int 724 command_fdt(int argc, char *argv[]) 725 { 726 727 return (command_fdt_internal(argc, argv)); 728 } 729 730 COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt); 731 #endif 732