1 /* $NetBSD: boot.c,v 1.5 2017/05/01 13:03:01 nonaka Exp $ */ 2 3 /*- 4 * Copyright (c) 2016 Kimihiro Nonaka <nonaka@netbsd.org> 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 REGENTS 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 REGENTS 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 "efiboot.h" 30 31 #include <sys/bootblock.h> 32 #include <sys/boot_flag.h> 33 #include <machine/limits.h> 34 35 #include "bootcfg.h" 36 #include "bootmod.h" 37 #include "bootmenu.h" 38 #include "devopen.h" 39 40 int errno; 41 int boot_biosdev; 42 daddr_t boot_biossector; 43 44 extern const char bootprog_name[], bootprog_rev[], bootprog_kernrev[]; 45 46 extern struct x86_boot_params boot_params; 47 extern char twiddle_toggle; 48 49 static const char * const names[][2] = { 50 { "netbsd", "netbsd.gz" }, 51 { "onetbsd", "onetbsd.gz" }, 52 { "netbsd.old", "netbsd.old.gz" }, 53 }; 54 55 #define NUMNAMES __arraycount(names) 56 #define DEFFILENAME names[0][0] 57 58 #define MAXDEVNAME 16 59 60 void command_help(char *); 61 void command_quit(char *); 62 void command_boot(char *); 63 void command_consdev(char *); 64 void command_dev(char *); 65 void command_devpath(char *); 66 void command_efivar(char *); 67 void command_gop(char *); 68 #if LIBSA_ENABLE_LS_OP 69 void command_ls(char *); 70 #endif 71 void command_memmap(char *); 72 #ifndef SMALL 73 void command_menu(char *); 74 #endif 75 void command_modules(char *); 76 void command_multiboot(char *); 77 void command_text(char *); 78 void command_version(char *); 79 80 const struct bootblk_command commands[] = { 81 { "help", command_help }, 82 { "?", command_help }, 83 { "quit", command_quit }, 84 { "boot", command_boot }, 85 { "consdev", command_consdev }, 86 { "dev", command_dev }, 87 { "devpath", command_devpath }, 88 { "efivar", command_efivar }, 89 { "fs", fs_add }, 90 { "gop", command_gop }, 91 { "load", module_add }, 92 #if LIBSA_ENABLE_LS_OP 93 { "ls", command_ls }, 94 #endif 95 { "memmap", command_memmap }, 96 #ifndef SMALL 97 { "menu", command_menu }, 98 #endif 99 { "modules", command_modules }, 100 { "multiboot", command_multiboot }, 101 { "rndseed", rnd_add }, 102 { "splash", splash_add }, 103 { "text", command_text }, 104 { "userconf", userconf_add }, 105 { "version", command_version }, 106 { NULL, NULL }, 107 }; 108 109 static char *default_devname; 110 static int default_unit, default_partition; 111 static const char *default_filename; 112 113 static char *sprint_bootsel(const char *); 114 static void bootit(const char *, int); 115 116 int 117 parsebootfile(const char *fname, char **fsname, char **devname, int *unit, 118 int *partition, const char **file) 119 { 120 const char *col; 121 122 *fsname = "ufs"; 123 *devname = default_devname; 124 *unit = default_unit; 125 *partition = default_partition; 126 *file = default_filename; 127 128 if (fname == NULL) 129 return 0; 130 131 if ((col = strchr(fname, ':')) != NULL) { /* device given */ 132 static char savedevname[MAXDEVNAME+1]; 133 int devlen; 134 int u = 0, p = 0; 135 int i = 0; 136 137 devlen = col - fname; 138 if (devlen > MAXDEVNAME) 139 return EINVAL; 140 141 #define isvalidname(c) ((c) >= 'a' && (c) <= 'z') 142 if (!isvalidname(fname[i])) 143 return EINVAL; 144 do { 145 savedevname[i] = fname[i]; 146 i++; 147 } while (isvalidname(fname[i])); 148 savedevname[i] = '\0'; 149 150 #define isnum(c) ((c) >= '0' && (c) <= '9') 151 if (i < devlen) { 152 if (!isnum(fname[i])) 153 return EUNIT; 154 do { 155 u *= 10; 156 u += fname[i++] - '0'; 157 } while (isnum(fname[i])); 158 } 159 160 #define isvalidpart(c) ((c) >= 'a' && (c) <= 'z') 161 if (i < devlen) { 162 if (!isvalidpart(fname[i])) 163 return EPART; 164 p = fname[i++] - 'a'; 165 } 166 167 if (i != devlen) 168 return ENXIO; 169 170 *devname = savedevname; 171 *unit = u; 172 *partition = p; 173 fname = col + 1; 174 } 175 176 if (*fname) 177 *file = fname; 178 179 return 0; 180 } 181 182 static char * 183 sprint_bootsel(const char *filename) 184 { 185 char *fsname, *devname; 186 int unit, partition; 187 const char *file; 188 static char buf[80]; 189 190 if (parsebootfile(filename, &fsname, &devname, &unit, 191 &partition, &file) == 0) { 192 snprintf(buf, sizeof(buf), "%s%d%c:%s", devname, unit, 193 'a' + partition, file); 194 return buf; 195 } 196 return "(invalid)"; 197 } 198 199 void 200 clearit(void) 201 { 202 203 if (bootcfg_info.clear) 204 clear_pc_screen(); 205 } 206 207 static void 208 bootit(const char *filename, int howto) 209 { 210 EFI_STATUS status; 211 EFI_PHYSICAL_ADDRESS bouncebuf; 212 UINTN npages; 213 u_long allocsz; 214 215 if (howto & AB_VERBOSE) 216 printf("booting %s (howto 0x%x)\n", sprint_bootsel(filename), 217 howto); 218 219 if (count_netbsd(filename, &allocsz) < 0) { 220 printf("boot: %s: %s\n", sprint_bootsel(filename), 221 strerror(errno)); 222 return; 223 } 224 225 bouncebuf = EFI_ALLOCATE_MAX_ADDRESS; 226 npages = EFI_SIZE_TO_PAGES(allocsz); 227 status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress, 228 EfiLoaderData, npages, &bouncebuf); 229 if (EFI_ERROR(status)) { 230 printf("boot: %s: %s\n", sprint_bootsel(filename), 231 strerror(ENOMEM)); 232 return; 233 } 234 235 efi_loadaddr = bouncebuf; 236 if (exec_netbsd(filename, bouncebuf, howto, 0, efi_cleanup) < 0) 237 printf("boot: %s: %s\n", sprint_bootsel(filename), 238 strerror(errno)); 239 else 240 printf("boot returned\n"); 241 242 (void) uefi_call_wrapper(BS->FreePages, 2, bouncebuf, npages); 243 efi_loadaddr = 0; 244 } 245 246 void 247 print_banner(void) 248 { 249 int n; 250 251 clearit(); 252 if (bootcfg_info.banner[0]) { 253 for (n = 0; n < BOOTCFG_MAXBANNER && bootcfg_info.banner[n]; 254 n++) 255 printf("%s\n", bootcfg_info.banner[n]); 256 } else 257 command_version("short"); 258 } 259 260 void 261 boot(void) 262 { 263 int currname; 264 int c; 265 266 boot_modules_enabled = !(boot_params.bp_flags & X86_BP_FLAGS_NOMODULES); 267 268 /* try to set default device to what BIOS tells us */ 269 bios2dev(boot_biosdev, boot_biossector, &default_devname, &default_unit, 270 &default_partition); 271 272 /* if the user types "boot" without filename */ 273 default_filename = DEFFILENAME; 274 275 if (!(boot_params.bp_flags & X86_BP_FLAGS_NOBOOTCONF)) { 276 parsebootconf(BOOTCFG_FILENAME); 277 } else { 278 bootcfg_info.timeout = boot_params.bp_timeout; 279 } 280 281 /* 282 * If console set in boot.cfg, switch to it. 283 * This will print the banner, so we don't need to explicitly do it 284 */ 285 if (bootcfg_info.consdev) 286 command_consdev(bootcfg_info.consdev); 287 else 288 print_banner(); 289 290 /* Display the menu, if applicable */ 291 twiddle_toggle = 0; 292 if (bootcfg_info.nummenu > 0) { 293 /* Does not return */ 294 doboottypemenu(); 295 } 296 297 printf("Press return to boot now, any other key for boot menu\n"); 298 for (currname = 0; currname < NUMNAMES; currname++) { 299 printf("booting %s - starting in ", 300 sprint_bootsel(names[currname][0])); 301 302 c = awaitkey((bootcfg_info.timeout < 0) ? 0 303 : bootcfg_info.timeout, 1); 304 if ((c != '\r') && (c != '\n') && (c != '\0')) { 305 if ((boot_params.bp_flags & X86_BP_FLAGS_PASSWORD) == 0) { 306 /* do NOT ask for password */ 307 bootmenu(); /* does not return */ 308 } else { 309 /* DO ask for password */ 310 if (check_password((char *)boot_params.bp_password)) { 311 /* password ok */ 312 printf("type \"?\" or \"help\" for help.\n"); 313 bootmenu(); /* does not return */ 314 } else { 315 /* bad password */ 316 printf("Wrong password.\n"); 317 currname = 0; 318 continue; 319 } 320 } 321 } 322 323 /* 324 * try pairs of names[] entries, foo and foo.gz 325 */ 326 /* don't print "booting..." again */ 327 bootit(names[currname][0], 0); 328 /* since it failed, try compressed bootfile. */ 329 bootit(names[currname][1], AB_VERBOSE); 330 } 331 332 bootmenu(); /* does not return */ 333 } 334 335 /* ARGSUSED */ 336 void 337 command_help(char *arg) 338 { 339 340 printf("commands are:\n" 341 "boot [xdNx:][filename] [-12acdqsvxz]\n" 342 " (ex. \"hd0a:netbsd.old -s\"\n" 343 "dev [xd[N[x]]:]\n" 344 "consdev {pc|com[0123][,{speed}]|com,{ioport}[,{speed}]}\n" 345 "devpath\n" 346 "efivar\n" 347 "gop [{modenum|list}]\n" 348 "load {path_to_module}\n" 349 #if LIBSA_ENABLE_LS_OP 350 "ls [path]\n" 351 #endif 352 "memmap [{sorted|unsorted}]\n" 353 #ifndef SMALL 354 "menu (reenters boot menu, if defined in boot.cfg)\n" 355 #endif 356 "modules {on|off|enabled|disabled}\n" 357 "multiboot [xdNx:][filename] [<args>]\n" 358 "rndseed {path_to_rndseed_file}\n" 359 "splash {path_to_image_file}\n" 360 "text [{modenum|list}]\n" 361 "userconf {command}\n" 362 "version\n" 363 "help|?\n" 364 "quit\n"); 365 } 366 367 #if LIBSA_ENABLE_LS_OP 368 void 369 command_ls(char *arg) 370 { 371 const char *save = default_filename; 372 373 default_filename = "/"; 374 ls(arg); 375 default_filename = save; 376 } 377 #endif 378 379 /* ARGSUSED */ 380 void 381 command_quit(char *arg) 382 { 383 384 printf("Exiting...\n"); 385 delay(1 * 1000 * 1000); 386 reboot(); 387 /* Note: we shouldn't get to this point! */ 388 panic("Could not reboot!"); 389 } 390 391 void 392 command_boot(char *arg) 393 { 394 char *filename; 395 int howto; 396 397 if (!parseboot(arg, &filename, &howto)) 398 return; 399 400 if (filename != NULL) { 401 bootit(filename, howto); 402 } else { 403 int i; 404 405 if (howto == 0) 406 bootdefault(); 407 for (i = 0; i < NUMNAMES; i++) { 408 bootit(names[i][0], howto); 409 bootit(names[i][1], howto); 410 } 411 } 412 } 413 414 void 415 command_dev(char *arg) 416 { 417 static char savedevname[MAXDEVNAME + 1]; 418 char *fsname, *devname; 419 const char *file; /* dummy */ 420 421 if (*arg == '\0') { 422 biosdisk_probe(); 423 printf("default %s%d%c\n", default_devname, default_unit, 424 'a' + default_partition); 425 return; 426 } 427 428 if (strchr(arg, ':') == NULL || 429 parsebootfile(arg, &fsname, &devname, &default_unit, 430 &default_partition, &file)) { 431 command_help(NULL); 432 return; 433 } 434 435 /* put to own static storage */ 436 strncpy(savedevname, devname, MAXDEVNAME + 1); 437 default_devname = savedevname; 438 } 439 440 static const struct cons_devs { 441 const char *name; 442 u_int tag; 443 int ioport; 444 } cons_devs[] = { 445 { "pc", CONSDEV_PC, 0 }, 446 { "com0", CONSDEV_COM0, 0 }, 447 { "com1", CONSDEV_COM1, 0 }, 448 { "com2", CONSDEV_COM2, 0 }, 449 { "com3", CONSDEV_COM3, 0 }, 450 { "com0kbd", CONSDEV_COM0KBD, 0 }, 451 { "com1kbd", CONSDEV_COM1KBD, 0 }, 452 { "com2kbd", CONSDEV_COM2KBD, 0 }, 453 { "com3kbd", CONSDEV_COM3KBD, 0 }, 454 { "com", CONSDEV_COM0, -1 }, 455 { "auto", CONSDEV_AUTO, 0 }, 456 { NULL, 0 } 457 }; 458 459 void 460 command_consdev(char *arg) 461 { 462 const struct cons_devs *cdp; 463 char *sep, *sep2 = NULL; 464 int ioport, speed = 0; 465 466 sep = strchr(arg, ','); 467 if (sep != NULL) { 468 *sep++ = '\0'; 469 sep2 = strchr(sep, ','); 470 if (sep != NULL) 471 *sep2++ = '\0'; 472 } 473 474 for (cdp = cons_devs; cdp->name; cdp++) { 475 if (strcmp(arg, cdp->name) == 0) { 476 ioport = cdp->ioport; 477 if (cdp->tag == CONSDEV_PC || cdp->tag == CONSDEV_AUTO) { 478 if (sep != NULL || sep2 != NULL) 479 goto error; 480 } else { 481 /* com? */ 482 if (ioport == -1) { 483 if (sep != NULL) { 484 u_long t = strtoul(sep, NULL, 0); 485 if (t > INT_MAX) 486 goto error; 487 ioport = (int)t; 488 } 489 if (sep2 != NULL) { 490 speed = atoi(sep2); 491 if (speed < 0) 492 goto error; 493 } 494 } else { 495 if (sep != NULL) { 496 speed = atoi(sep); 497 if (speed < 0) 498 goto error; 499 } 500 if (sep2 != NULL) 501 goto error; 502 } 503 } 504 consinit(cdp->tag, ioport, speed); 505 print_banner(); 506 return; 507 } 508 } 509 error: 510 printf("invalid console device.\n"); 511 } 512 513 #ifndef SMALL 514 /* ARGSUSED */ 515 void 516 command_menu(char *arg) 517 { 518 519 if (bootcfg_info.nummenu > 0) { 520 /* Does not return */ 521 doboottypemenu(); 522 } else 523 printf("No menu defined in boot.cfg\n"); 524 } 525 #endif /* !SMALL */ 526 527 void 528 command_modules(char *arg) 529 { 530 531 if (strcmp(arg, "enabled") == 0 || 532 strcmp(arg, "on") == 0) 533 boot_modules_enabled = true; 534 else if (strcmp(arg, "disabled") == 0 || 535 strcmp(arg, "off") == 0) 536 boot_modules_enabled = false; 537 else 538 printf("invalid flag, must be 'enabled' or 'disabled'.\n"); 539 } 540 541 void 542 command_multiboot(char *arg) 543 { 544 char *filename; 545 546 filename = arg; 547 if (exec_multiboot(filename, gettrailer(arg)) < 0) 548 printf("multiboot: %s: %s\n", sprint_bootsel(filename), 549 strerror(errno)); 550 else 551 printf("boot returned\n"); 552 } 553 554 void 555 command_version(char *arg) 556 { 557 558 if (strcmp(arg, "full") == 0) { 559 printf("ImageBase: 0x%" PRIxPTR "\n", 560 (uintptr_t)efi_li->ImageBase); 561 printf("Stack: 0x%" PRIxPTR "\n", efi_main_sp); 562 printf("EFI version: %d.%02d\n", 563 ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff); 564 Print(L"EFI Firmware: %s (rev %d.%02d)\n", ST->FirmwareVendor, 565 ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff); 566 } 567 568 printf("\n" 569 ">> %s, Revision %s (from NetBSD %s)\n" 570 ">> Memory: %d/%d k\n", 571 bootprog_name, bootprog_rev, bootprog_kernrev, 572 getbasemem(), getextmem()); 573 } 574 575 void 576 command_memmap(char *arg) 577 { 578 bool sorted = true; 579 580 if (*arg == '\0' || strcmp(arg, "sorted") == 0) 581 /* Already sorted is true. */; 582 else if (strcmp(arg, "unsorted") == 0) 583 sorted = false; 584 else { 585 printf("invalid flag, " 586 "must be 'sorted' or 'unsorted'.\n"); 587 return; 588 } 589 590 efi_memory_show_map(sorted); 591 } 592 593 void 594 command_devpath(char *arg) 595 { 596 EFI_STATUS status; 597 UINTN i, nhandles; 598 EFI_HANDLE *handles; 599 EFI_DEVICE_PATH *dp0, *dp; 600 CHAR16 *path; 601 UINTN cols, rows, row = 0; 602 603 status = uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut, 604 ST->ConOut->Mode->Mode, &cols, &rows); 605 if (EFI_ERROR(status) || rows <= 2) 606 rows = 0; 607 else 608 rows -= 2; 609 610 /* 611 * all devices. 612 */ 613 status = LibLocateHandle(ByProtocol, &DevicePathProtocol, NULL, 614 &nhandles, &handles); 615 if (EFI_ERROR(status)) 616 return; 617 618 for (i = 0; i < nhandles; i++) { 619 status = uefi_call_wrapper(BS->HandleProtocol, 3, handles[i], 620 &DevicePathProtocol, (void **)&dp0); 621 if (EFI_ERROR(status)) 622 break; 623 624 Print(L"DevicePathType %d\n", DevicePathType(dp0)); 625 for (dp = dp0; 626 !IsDevicePathEnd(dp); 627 dp = NextDevicePathNode(dp)) { 628 path = DevicePathToStr(dp); 629 Print(L"%d:%d:%s\n", DevicePathType(dp), DevicePathSubType(dp), path); 630 FreePool(path); 631 632 if (++row >= rows) { 633 row = 0; 634 Print(L"Press Any Key to continue :"); 635 (void) awaitkey(-1, 0); 636 Print(L"\n"); 637 } 638 } 639 } 640 } 641 642 void 643 command_efivar(char *arg) 644 { 645 static const CHAR16 header[] = 646 L"GUID Variable Name Value\n" 647 L"=================================== ==================== ========\n"; 648 EFI_STATUS status; 649 UINTN sz = 64, osz; 650 CHAR16 *name = NULL, *tmp, *val; 651 EFI_GUID vendor; 652 UINTN cols, rows, row = 0; 653 654 status = uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut, 655 ST->ConOut->Mode->Mode, &cols, &rows); 656 if (EFI_ERROR(status) || rows <= 2) 657 rows = 0; 658 else 659 rows -= 2; 660 661 name = AllocatePool(sz); 662 if (name == NULL) { 663 Print(L"memory allocation failed: %ld bytes\n", 664 (UINT64)sz); 665 return; 666 } 667 668 SetMem(name, sz, 0); 669 vendor = NullGuid; 670 671 Print(L"%s", header); 672 for (;;) { 673 osz = sz; 674 status = uefi_call_wrapper(RT->GetNextVariableName, 3, 675 &sz, name, &vendor); 676 if (EFI_ERROR(status)) { 677 if (status == EFI_NOT_FOUND) 678 break; 679 if (status != EFI_BUFFER_TOO_SMALL) { 680 Print(L"GetNextVariableName failed: %r\n", 681 status); 682 break; 683 } 684 685 tmp = AllocatePool(sz); 686 if (tmp == NULL) { 687 Print(L"memory allocation failed: %ld bytes\n", 688 (UINT64)sz); 689 break; 690 } 691 SetMem(tmp, sz, 0); 692 CopyMem(tmp, name, osz); 693 FreePool(name); 694 name = tmp; 695 continue; 696 } 697 698 val = LibGetVariable(name, &vendor); 699 Print(L"%.-35g %.-20s %s\n", &vendor, name, 700 val ? val : L"(null)"); 701 FreePool(val); 702 703 if (++row >= rows) { 704 row = 0; 705 Print(L"Press Any Key to continue :"); 706 (void) awaitkey(-1, 0); 707 Print(L"\n"); 708 } 709 } 710 711 FreePool(name); 712 } 713