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/cdefs.h>
  30 #include <sys/param.h>
  31 
  32 #include <stand.h>
  33 
  34 #include "api_public.h"
  35 #include "bootstrap.h"
  36 #include "glue.h"
  37 #include "libuboot.h"
  38 
  39 #ifndef nitems
  40 #define nitems(x)       (sizeof((x)) / sizeof((x)[0]))
  41 #endif
  42 
  43 struct uboot_devdesc currdev;
  44 struct arch_switch archsw;              /* MI/MD interface boundary */
  45 int devs_no;
  46 
  47 uintptr_t uboot_heap_start;
  48 uintptr_t uboot_heap_end;
  49 
  50 struct device_type { 
  51         const char *name;
  52         int type;
  53 } device_types[] = {
  54         { "disk", DEV_TYP_STOR },
  55         { "ide",  DEV_TYP_STOR | DT_STOR_IDE },
  56         { "mmc",  DEV_TYP_STOR | DT_STOR_MMC },
  57         { "sata", DEV_TYP_STOR | DT_STOR_SATA },
  58         { "scsi", DEV_TYP_STOR | DT_STOR_SCSI },
  59         { "usb",  DEV_TYP_STOR | DT_STOR_USB },
  60         { "net",  DEV_TYP_NET }
  61 };
  62 
  63 extern char end[];
  64 extern char bootprog_info[];
  65 
  66 extern unsigned char _etext[];
  67 extern unsigned char _edata[];
  68 extern unsigned char __bss_start[];
  69 extern unsigned char __sbss_start[];
  70 extern unsigned char __sbss_end[];
  71 extern unsigned char _end[];
  72 
  73 #ifdef LOADER_FDT_SUPPORT
  74 extern int command_fdt_internal(int argc, char *argv[]);
  75 #endif
  76 
  77 static void
  78 dump_sig(struct api_signature *sig)
  79 {
  80 #ifdef DEBUG
  81         printf("signature:\n");
  82         printf("  version\t= %d\n", sig->version);
  83         printf("  checksum\t= 0x%08x\n", sig->checksum);
  84         printf("  sc entry\t= 0x%08x\n", sig->syscall);
  85 #endif
  86 }
  87 
  88 static void
  89 dump_addr_info(void)
  90 {
  91 #ifdef DEBUG
  92         printf("\naddresses info:\n");
  93         printf(" _etext (sdata) = 0x%08x\n", (uint32_t)_etext);
  94         printf(" _edata         = 0x%08x\n", (uint32_t)_edata);
  95         printf(" __sbss_start   = 0x%08x\n", (uint32_t)__sbss_start);
  96         printf(" __sbss_end     = 0x%08x\n", (uint32_t)__sbss_end);
  97         printf(" __sbss_start   = 0x%08x\n", (uint32_t)__bss_start);
  98         printf(" _end           = 0x%08x\n", (uint32_t)_end);
  99         printf(" syscall entry  = 0x%08x\n", (uint32_t)syscall_ptr);
 100 #endif
 101 }
 102 
 103 static uint64_t
 104 memsize(struct sys_info *si, int flags)
 105 {
 106         uint64_t size;
 107         int i;
 108 
 109         size = 0;
 110         for (i = 0; i < si->mr_no; i++)
 111                 if (si->mr[i].flags == flags && si->mr[i].size)
 112                         size += (si->mr[i].size);
 113 
 114         return (size);
 115 }
 116 
 117 static void
 118 meminfo(void)
 119 {
 120         uint64_t size;
 121         struct sys_info *si;
 122         int t[3] = { MR_ATTR_DRAM, MR_ATTR_FLASH, MR_ATTR_SRAM };
 123         int i;
 124 
 125         if ((si = ub_get_sys_info()) == NULL)
 126                 panic("could not retrieve system info");
 127 
 128         for (i = 0; i < 3; i++) {
 129                 size = memsize(si, t[i]);
 130                 if (size > 0)
 131                         printf("%s: %juMB\n", ub_mem_type(t[i]),
 132                             (uintmax_t)(size / 1024 / 1024));
 133         }
 134 }
 135 
 136 static const char *
 137 get_device_type(const char *devstr, int *devtype)
 138 {
 139         int i;
 140         int namelen;
 141         struct device_type *dt;
 142 
 143         if (devstr) {
 144                 for (i = 0; i < nitems(device_types); i++) {
 145                         dt = &device_types[i];
 146                         namelen = strlen(dt->name);
 147                         if (strncmp(dt->name, devstr, namelen) == 0) {
 148                                 *devtype = dt->type;
 149                                 return (devstr + namelen);
 150                         }
 151                 }
 152                 printf("Unknown device type '%s'\n", devstr);
 153         }
 154 
 155         *devtype = -1;
 156         return (NULL);
 157 }
 158 
 159 static const char *
 160 device_typename(int type)
 161 {
 162         int i;
 163 
 164         for (i = 0; i < nitems(device_types); i++)
 165                 if (device_types[i].type == type)
 166                         return (device_types[i].name);
 167 
 168         return ("<unknown>");
 169 }
 170 
 171 /*
 172  * Parse a device string into type, unit, slice and partition numbers. A
 173  * returned value of -1 for type indicates a search should be done for the
 174  * first loadable device, otherwise a returned value of -1 for unit
 175  * indicates a search should be done for the first loadable device of the
 176  * given type.
 177  *
 178  * The returned values for slice and partition are interpreted by
 179  * disk_open().
 180  *
 181  * Valid device strings:                     For device types:
 182  *
 183  * <type_name>                               DEV_TYP_STOR, DEV_TYP_NET
 184  * <type_name><unit>                         DEV_TYP_STOR, DEV_TYP_NET
 185  * <type_name><unit>:                        DEV_TYP_STOR, DEV_TYP_NET
 186  * <type_name><unit>:<slice>                 DEV_TYP_STOR
 187  * <type_name><unit>:<slice>.                DEV_TYP_STOR
 188  * <type_name><unit>:<slice>.<partition>     DEV_TYP_STOR
 189  *
 190  * For valid type names, see the device_types array, above.
 191  *
 192  * Slice numbers are 1-based.  0 is a wildcard.
 193  */
 194 static void
 195 get_load_device(int *type, int *unit, int *slice, int *partition)
 196 {
 197         char *devstr;
 198         const char *p;
 199         char *endp;
 200 
 201         *type = -1;
 202         *unit = -1;
 203         *slice = 0;
 204         *partition = -1;
 205 
 206         devstr = ub_env_get("loaderdev");
 207         if (devstr == NULL) {
 208                 printf("U-Boot env: loaderdev not set, will probe all devices.\n");
 209                 return;
 210         }
 211         printf("U-Boot env: loaderdev='%s'\n", devstr);
 212 
 213         p = get_device_type(devstr, type);
 214 
 215         /* Ignore optional spaces after the device name. */
 216         while (*p == ' ')
 217                 p++;
 218 
 219         /* Unknown device name, or a known name without unit number.  */
 220         if ((*type == -1) || (*p == '\0')) {
 221                 return;
 222         }
 223 
 224         /* Malformed unit number. */
 225         if (!isdigit(*p)) {
 226                 *type = -1;
 227                 return;
 228         }
 229 
 230         /* Guaranteed to extract a number from the string, as *p is a digit. */
 231         *unit = strtol(p, &endp, 10);
 232         p = endp;
 233 
 234         /* Known device name with unit number and nothing else. */
 235         if (*p == '\0') {
 236                 return;
 237         }
 238 
 239         /* Device string is malformed beyond unit number. */
 240         if (*p != ':') {
 241                 *type = -1;
 242                 *unit = -1;
 243                 return;
 244         }
 245 
 246         p++;
 247 
 248         /* No slice and partition specification. */
 249         if ('\0' == *p )
 250                 return;
 251 
 252         /* Only DEV_TYP_STOR devices can have a slice specification. */
 253         if (!(*type & DEV_TYP_STOR)) {
 254                 *type = -1;
 255                 *unit = -1;
 256                 return;
 257         }
 258 
 259         *slice = strtoul(p, &endp, 10);
 260 
 261         /* Malformed slice number. */
 262         if (p == endp) {
 263                 *type = -1;
 264                 *unit = -1;
 265                 *slice = 0;
 266                 return;
 267         }
 268 
 269         p = endp;
 270         
 271         /* No partition specification. */
 272         if (*p == '\0')
 273                 return;
 274 
 275         /* Device string is malformed beyond slice number. */
 276         if (*p != '.') {
 277                 *type = -1;
 278                 *unit = -1;
 279                 *slice = 0;
 280                 return;
 281         }
 282 
 283         p++;
 284 
 285         /* No partition specification. */
 286         if (*p == '\0')
 287                 return;
 288 
 289         *partition = strtol(p, &endp, 10);
 290         p = endp;
 291 
 292         /*  Full, valid device string. */
 293         if (*endp == '\0')
 294                 return;
 295 
 296         /* Junk beyond partition number. */
 297         *type = -1;
 298         *unit = -1;
 299         *slice = 0;
 300         *partition = -1;
 301 } 
 302 
 303 static void
 304 print_disk_probe_info()
 305 {
 306         char slice[32];
 307         char partition[32];
 308 
 309         if (currdev.d_disk.slice > 0)
 310                 sprintf(slice, "%d", currdev.d_disk.slice);
 311         else
 312                 strcpy(slice, "<auto>");
 313 
 314         if (currdev.d_disk.partition >= 0)
 315                 sprintf(partition, "%d", currdev.d_disk.partition);
 316         else
 317                 strcpy(partition, "<auto>");
 318 
 319         printf("  Checking unit=%d slice=%s partition=%s...",
 320             currdev.d_unit, slice, partition);
 321 
 322 }
 323 
 324 static int
 325 probe_disks(int devidx, int load_type, int load_unit, int load_slice, 
 326     int load_partition)
 327 {
 328         int open_result, unit;
 329         struct open_file f;
 330 
 331         currdev.d_disk.slice = load_slice;
 332         currdev.d_disk.partition = load_partition;
 333 
 334         f.f_devdata = &currdev;
 335         open_result = -1;
 336 
 337         if (load_type == -1) {
 338                 printf("  Probing all disk devices...\n");
 339                 /* Try each disk in succession until one works.  */
 340                 for (currdev.d_unit = 0; currdev.d_unit < UB_MAX_DEV;
 341                      currdev.d_unit++) {
 342                         print_disk_probe_info();
 343                         open_result = devsw[devidx]->dv_open(&f, &currdev);
 344                         if (open_result == 0) {
 345                                 printf(" good.\n");
 346                                 return (0);
 347                         }
 348                         printf("\n");
 349                 }
 350                 return (-1);
 351         }
 352 
 353         if (load_unit == -1) {
 354                 printf("  Probing all %s devices...\n", device_typename(load_type));
 355                 /* Try each disk of given type in succession until one works. */
 356                 for (unit = 0; unit < UB_MAX_DEV; unit++) {
 357                         currdev.d_unit = uboot_diskgetunit(load_type, unit);
 358                         if (currdev.d_unit == -1)
 359                                 break;
 360                         print_disk_probe_info();
 361                         open_result = devsw[devidx]->dv_open(&f, &currdev);
 362                         if (open_result == 0) {
 363                                 printf(" good.\n");
 364                                 return (0);
 365                         }
 366                         printf("\n");
 367                 }
 368                 return (-1);
 369         }
 370 
 371         if ((currdev.d_unit = uboot_diskgetunit(load_type, load_unit)) != -1) {
 372                 print_disk_probe_info();
 373                 open_result = devsw[devidx]->dv_open(&f,&currdev);
 374                 if (open_result == 0) {
 375                         printf(" good.\n");
 376                         return (0);
 377                 }
 378                 printf("\n");
 379         }
 380 
 381         printf("  Requested disk type/unit/slice/partition not found\n");
 382         return (-1);
 383 }
 384 
 385 int
 386 main(void)
 387 {
 388         struct api_signature *sig = NULL;
 389         int load_type, load_unit, load_slice, load_partition;
 390         int i;
 391         const char *ldev;
 392 
 393         /*
 394          * If we can't find the magic signature and related info, exit with a
 395          * unique error code that U-Boot reports as "## Application terminated,
 396          * rc = 0xnnbadab1". Hopefully 'badab1' looks enough like "bad api" to
 397          * provide a clue. It's better than 0xffffffff anyway.
 398          */
 399         if (!api_search_sig(&sig))
 400                 return (0x01badab1);
 401 
 402         syscall_ptr = sig->syscall;
 403         if (syscall_ptr == NULL)
 404                 return (0x02badab1);
 405 
 406         if (sig->version > API_SIG_VERSION)
 407                 return (0x03badab1);
 408 
 409         /* Clear BSS sections */
 410         bzero(__sbss_start, __sbss_end - __sbss_start);
 411         bzero(__bss_start, _end - __bss_start);
 412 
 413         /*
 414          * Initialise the heap as early as possible.  Once this is done,
 415          * alloc() is usable. The stack is buried inside us, so this is safe.
 416          */
 417         uboot_heap_start = round_page((uintptr_t)end);
 418         uboot_heap_end   = uboot_heap_start + 512 * 1024;
 419         setheap((void *)uboot_heap_start, (void *)uboot_heap_end);
 420 
 421         /*
 422          * Set up console.
 423          */
 424         cons_probe();
 425         printf("Compatible U-Boot API signature found @%p\n", sig);
 426 
 427         printf("\n%s", bootprog_info);
 428         printf("\n");
 429 
 430         dump_sig(sig);
 431         dump_addr_info();
 432 
 433         meminfo();
 434 
 435         /*
 436          * Enumerate U-Boot devices
 437          */
 438         if ((devs_no = ub_dev_enum()) == 0)
 439                 panic("no U-Boot devices found");
 440         printf("Number of U-Boot devices: %d\n", devs_no);
 441 
 442         get_load_device(&load_type, &load_unit, &load_slice, &load_partition);
 443 
 444         /*
 445          * March through the device switch probing for things.
 446          */
 447         for (i = 0; devsw[i] != NULL; i++) {
 448 
 449                 if (devsw[i]->dv_init == NULL)
 450                         continue;
 451                 if ((devsw[i]->dv_init)() != 0)
 452                         continue;
 453 
 454                 printf("Found U-Boot device: %s\n", devsw[i]->dv_name);
 455 
 456                 currdev.d_dev = devsw[i];
 457                 currdev.d_type = currdev.d_dev->dv_type;
 458                 currdev.d_unit = 0;
 459 
 460                 if ((load_type == -1 || (load_type & DEV_TYP_STOR)) &&
 461                     strcmp(devsw[i]->dv_name, "disk") == 0) {
 462                         if (probe_disks(i, load_type, load_unit, load_slice, 
 463                             load_partition) == 0)
 464                                 break;
 465                 }
 466 
 467                 if ((load_type == -1 || (load_type & DEV_TYP_NET)) &&
 468                     strcmp(devsw[i]->dv_name, "net") == 0)
 469                         break;
 470         }
 471 
 472         /*
 473          * If we couldn't find a boot device, return an error to u-boot.
 474          * U-boot may be running a boot script that can try something different
 475          * so returning an error is better than forcing a reboot.
 476          */
 477         if (devsw[i] == NULL) {
 478                 printf("No boot device found!\n");
 479                 return (0xbadef1ce);
 480         }
 481 
 482         ldev = uboot_fmtdev(&currdev);
 483         env_setenv("currdev", EV_VOLATILE, ldev, uboot_setcurrdev, env_nounset);
 484         env_setenv("loaddev", EV_VOLATILE, ldev, env_noset, env_nounset);
 485         printf("Booting from %s\n", ldev);
 486 
 487         setenv("LINES", "24", 1);               /* optional */
 488         setenv("prompt", "loader>", 1);
 489 
 490         archsw.arch_loadaddr = uboot_loadaddr;
 491         archsw.arch_getdev = uboot_getdev;
 492         archsw.arch_copyin = uboot_copyin;
 493         archsw.arch_copyout = uboot_copyout;
 494         archsw.arch_readin = uboot_readin;
 495         archsw.arch_autoload = uboot_autoload;
 496 
 497         interact(NULL);                         /* doesn't return */
 498 
 499         return (0);
 500 }
 501 
 502 
 503 COMMAND_SET(heap, "heap", "show heap usage", command_heap);
 504 static int
 505 command_heap(int argc, char *argv[])
 506 {
 507 
 508         printf("heap base at %p, top at %p, used %td\n", end, sbrk(0),
 509             sbrk(0) - end);
 510 
 511         return (CMD_OK);
 512 }
 513 
 514 COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot);
 515 static int
 516 command_reboot(int argc, char *argv[])
 517 {
 518 
 519         printf("Resetting...\n");
 520         ub_reset();
 521 
 522         printf("Reset failed!\n");
 523         while(1);
 524 }
 525 
 526 COMMAND_SET(devinfo, "devinfo", "show U-Boot devices", command_devinfo);
 527 static int
 528 command_devinfo(int argc, char *argv[])
 529 {
 530         int i;
 531 
 532         if ((devs_no = ub_dev_enum()) == 0) {
 533                 command_errmsg = "no U-Boot devices found!?";
 534                 return (CMD_ERROR);
 535         }
 536         
 537         printf("U-Boot devices:\n");
 538         for (i = 0; i < devs_no; i++) {
 539                 ub_dump_di(i);
 540                 printf("\n");
 541         }
 542         return (CMD_OK);
 543 }
 544 
 545 COMMAND_SET(sysinfo, "sysinfo", "show U-Boot system info", command_sysinfo);
 546 static int
 547 command_sysinfo(int argc, char *argv[])
 548 {
 549         struct sys_info *si;
 550 
 551         if ((si = ub_get_sys_info()) == NULL) {
 552                 command_errmsg = "could not retrieve U-Boot sys info!?";
 553                 return (CMD_ERROR);
 554         }
 555 
 556         printf("U-Boot system info:\n");
 557         ub_dump_si(si);
 558         return (CMD_OK);
 559 }
 560 
 561 enum ubenv_action {
 562         UBENV_UNKNOWN,
 563         UBENV_SHOW,
 564         UBENV_IMPORT
 565 };
 566 
 567 static void
 568 handle_uboot_env_var(enum ubenv_action action, const char * var)
 569 {
 570         char ldvar[128];
 571         const char *val;
 572         char *wrk;
 573         int len;
 574 
 575         /*
 576          * On an import with the variable name formatted as ldname=ubname,
 577          * import the uboot variable ubname into the loader variable ldname,
 578          * otherwise the historical behavior is to import to uboot.ubname.
 579          */
 580         if (action == UBENV_IMPORT) { 
 581                 len = strcspn(var, "=");
 582                 if (len == 0) {
 583                         printf("name cannot start with '=': '%s'\n", var);
 584                         return;
 585                 }
 586                 if (var[len] == 0) {
 587                         strcpy(ldvar, "uboot.");
 588                         strncat(ldvar, var, sizeof(ldvar) - 7);
 589                 } else {
 590                         len = MIN(len, sizeof(ldvar) - 1);
 591                         strncpy(ldvar, var, len);
 592                         ldvar[len] = 0;
 593                         var = &var[len + 1];
 594                 }
 595         }
 596 
 597         /*
 598          * If the user prepended "uboot." (which is how they usually see these
 599          * names) strip it off as a convenience.
 600          */
 601         if (strncmp(var, "uboot.", 6) == 0) {
 602                 var = &var[6];
 603         }
 604 
 605         /* If there is no variable name left, punt. */
 606         if (var[0] == 0) {
 607                 printf("empty variable name\n");
 608                 return;
 609         }
 610 
 611         val = ub_env_get(var);
 612         if (action == UBENV_SHOW) {
 613                 if (val == NULL)
 614                         printf("uboot.%s is not set\n", var);
 615                 else
 616                         printf("uboot.%s=%s\n", var, val);
 617         } else if (action == UBENV_IMPORT) {
 618                 if (val != NULL) {
 619                         setenv(ldvar, val, 1);
 620                 }
 621         }
 622 }
 623 
 624 static int
 625 command_ubenv(int argc, char *argv[])
 626 {
 627         enum ubenv_action action;
 628         const char *var;
 629         int i;
 630 
 631         action = UBENV_UNKNOWN;
 632         if (argc > 1) {
 633                 if (strcasecmp(argv[1], "import") == 0)
 634                         action = UBENV_IMPORT;
 635                 else if (strcasecmp(argv[1], "show") == 0)
 636                         action = UBENV_SHOW;
 637         }
 638         if (action == UBENV_UNKNOWN) {
 639                 command_errmsg = "usage: 'ubenv <import|show> [var ...]";
 640                 return (CMD_ERROR);
 641         }
 642 
 643         if (argc > 2) {
 644                 for (i = 2; i < argc; i++)
 645                         handle_uboot_env_var(action, argv[i]);
 646         } else {
 647                 var = NULL;
 648                 for (;;) {
 649                         if ((var = ub_env_enum(var)) == NULL)
 650                                 break;
 651                         handle_uboot_env_var(action, var);
 652                 }
 653         }
 654 
 655         return (CMD_OK);
 656 }
 657 COMMAND_SET(ubenv, "ubenv", "show or import U-Boot env vars", command_ubenv);
 658 
 659 #ifdef LOADER_FDT_SUPPORT
 660 /*
 661  * Since proper fdt command handling function is defined in fdt_loader_cmd.c,
 662  * and declaring it as extern is in contradiction with COMMAND_SET() macro
 663  * (which uses static pointer), we're defining wrapper function, which
 664  * calls the proper fdt handling routine.
 665  */
 666 static int
 667 command_fdt(int argc, char *argv[])
 668 {
 669 
 670         return (command_fdt_internal(argc, argv));
 671 }
 672 
 673 COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt);
 674 #endif