1 /* $NetBSD: boot.c,v 1.45 2023/06/14 00:42:21 rin Exp $ */ 2 3 /*- 4 * Copyright (c) 2016 Kimihiro Nonaka <nonaka (at) netbsd.org> 5 * Copyright (c) 2018 Jared McNeill <jmcneill (at) invisible.ca> 6 * All rights reserved. 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 REGENTS 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 REGENTS 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 "efiboot.h" 31 #include "efiblock.h" 32 #include "efifile.h" 33 #include "efirng.h" 34 #include "module.h" 35 #include "bootmenu.h" 36 37 #ifdef EFIBOOT_FDT 38 #include "efifdt.h" 39 #include "overlay.h" 40 #endif 41 42 #ifdef EFIBOOT_ACPI 43 #include "efiacpi.h" 44 #endif 45 46 #include <sys/bootblock.h> 47 #include <sys/boot_flag.h> 48 #include <machine/limits.h> 49 50 #include <loadfile.h> 51 #include <bootcfg.h> 52 53 extern const char bootprog_name[], bootprog_rev[], bootprog_kernrev[]; 54 55 extern char twiddle_toggle; 56 57 static const char * const names[] = { 58 "netbsd", "netbsd.gz", 59 "onetbsd", "onetbsd.gz", 60 "netbsd.old", "netbsd.old.gz", 61 }; 62 63 #define NUMNAMES __arraycount(names) 64 65 static const char *efi_memory_type[] = { 66 [EfiReservedMemoryType] = "Reserved Memory Type", 67 [EfiLoaderCode] = "Loader Code", 68 [EfiLoaderData] = "Loader Data", 69 [EfiBootServicesCode] = "Boot Services Code", 70 [EfiBootServicesData] = "Boot Services Data", 71 [EfiRuntimeServicesCode] = "Runtime Services Code", 72 [EfiRuntimeServicesData] = "Runtime Services Data", 73 [EfiConventionalMemory] = "Conventional Memory", 74 [EfiUnusableMemory] = "Unusable Memory", 75 [EfiACPIReclaimMemory] = "ACPI Reclaim Memory", 76 [EfiACPIMemoryNVS] = "ACPI Memory NVS", 77 [EfiMemoryMappedIO] = "MMIO", 78 [EfiMemoryMappedIOPortSpace] = "MMIO (Port Space)", 79 [EfiPalCode] = "Pal Code", 80 [EfiPersistentMemory] = "Persistent Memory", 81 }; 82 83 static char default_device[32]; 84 static int default_fstype = FS_UNUSED; 85 static char initrd_path[255]; 86 static char dtb_path[255]; 87 static char netbsd_path[255]; 88 static char netbsd_args[255]; 89 static char rndseed_path[255]; 90 91 #define DEFFILENAME names[0] 92 93 int set_bootfile(const char *); 94 int set_bootargs(const char *); 95 96 #ifdef EFIBOOT_ACPI 97 void command_acpi(char *); 98 #endif 99 void command_boot(char *); 100 void command_dev(char *); 101 void command_initrd(char *); 102 void command_rndseed(char *); 103 #ifdef EFIBOOT_FDT 104 void command_dtb(char *); 105 void command_dtoverlay(char *); 106 void command_dtoverlays(char *); 107 #endif 108 void command_modules(char *); 109 void command_load(char *); 110 void command_unload(char *); 111 void command_ls(char *); 112 void command_gop(char *); 113 void command_mem(char *); 114 void command_menu(char *); 115 void command_reset(char *); 116 void command_setup(char *); 117 void command_userconf(char *); 118 void command_version(char *); 119 void command_quit(char *); 120 121 const struct boot_command commands[] = { 122 #ifdef EFIBOOT_ACPI 123 { "acpi", command_acpi, "acpi [{on|off}]" }, 124 #endif 125 { "boot", command_boot, "boot [dev:][filename] [args]\n (ex. \"hd0a:\\netbsd.old -s\"" }, 126 { "dev", command_dev, "dev" }, 127 #ifdef EFIBOOT_FDT 128 { "dtb", command_dtb, "dtb [dev:][filename]" }, 129 { "dtoverlay", command_dtoverlay, "dtoverlay [dev:][filename]" }, 130 { "dtoverlays", command_dtoverlays, "dtoverlays [{on|off|reset}]" }, 131 #endif 132 { "initrd", command_initrd, "initrd [dev:][filename]" }, 133 { "fs", command_initrd, NULL }, 134 { "rndseed", command_rndseed, "rndseed [dev:][filename]" }, 135 { "modules", command_modules, "modules [{on|off|reset}]" }, 136 { "load", command_load, "load <module_name>" }, 137 { "unload", command_unload, "unload <module_name>" }, 138 { "ls", command_ls, "ls [hdNn:/path]" }, 139 { "gop", command_gop, "gop [mode]" }, 140 { "mem", command_mem, "mem" }, 141 { "menu", command_menu, "menu" }, 142 { "reboot", command_reset, "reboot|reset" }, 143 { "reset", command_reset, NULL }, 144 { "setup", command_setup, "setup" }, 145 { "userconf", command_userconf, "userconf <command>" }, 146 { "version", command_version, "version" }, 147 { "ver", command_version, NULL }, 148 { "help", command_help, "help|?" }, 149 { "?", command_help, NULL }, 150 { "quit", command_quit, "quit" }, 151 { NULL, NULL, NULL }, 152 }; 153 154 static int 155 bootcfg_path(char *pathbuf, size_t pathbuflen) 156 { 157 158 /* 159 * Fallback to default_device 160 * - for ISO9660 (efi_file_path() succeeds but does not work correctly) 161 * - or whenever efi_file_path() fails (due to broken firmware) 162 */ 163 if (default_fstype == FS_ISO9660 || efi_bootdp == NULL || 164 efi_file_path(efi_bootdp, BOOTCFG_FILENAME, pathbuf, pathbuflen)) 165 snprintf(pathbuf, pathbuflen, "%s:%s", default_device, 166 BOOTCFG_FILENAME); 167 168 return 0; 169 } 170 171 void 172 command_help(char *arg) 173 { 174 int n; 175 176 printf("commands are:\n"); 177 for (n = 0; commands[n].c_name; n++) { 178 if (commands[n].c_help) 179 printf("%s\n", commands[n].c_help); 180 } 181 } 182 183 #ifdef EFIBOOT_ACPI 184 void 185 command_acpi(char *arg) 186 { 187 if (arg && *arg) { 188 if (strcmp(arg, "on") == 0) 189 efi_acpi_enable(1); 190 else if (strcmp(arg, "off") == 0) 191 efi_acpi_enable(0); 192 else { 193 command_help(""); 194 return; 195 } 196 } else { 197 printf("ACPI support is %sabled\n", 198 efi_acpi_enabled() ? "en" : "dis"); 199 } 200 } 201 #endif 202 203 void 204 command_boot(char *arg) 205 { 206 char *fname = arg; 207 const char *kernel = *fname ? fname : bootfile; 208 char *bootargs = gettrailer(arg); 209 210 if (!kernel || !*kernel) 211 kernel = DEFFILENAME; 212 213 if (!*bootargs) 214 bootargs = netbsd_args; 215 216 efi_block_set_readahead(true); 217 exec_netbsd(kernel, bootargs); 218 efi_block_set_readahead(false); 219 } 220 221 void 222 command_dev(char *arg) 223 { 224 if (arg && *arg) { 225 set_default_device(arg); 226 } else { 227 efi_block_show(); 228 efi_net_show(); 229 } 230 231 if (strlen(default_device) > 0) { 232 printf("\n"); 233 printf("default: %s\n", default_device); 234 } 235 } 236 237 void 238 command_initrd(char *arg) 239 { 240 set_initrd_path(arg); 241 } 242 243 void 244 command_rndseed(char *arg) 245 { 246 set_rndseed_path(arg); 247 } 248 249 #ifdef EFIBOOT_FDT 250 void 251 command_dtb(char *arg) 252 { 253 set_dtb_path(arg); 254 } 255 256 void 257 command_dtoverlays(char *arg) 258 { 259 if (arg && *arg) { 260 if (strcmp(arg, "on") == 0) 261 dtoverlay_enable(1); 262 else if (strcmp(arg, "off") == 0) 263 dtoverlay_enable(0); 264 else if (strcmp(arg, "reset") == 0) 265 dtoverlay_remove_all(); 266 else { 267 command_help(""); 268 return; 269 } 270 } else { 271 printf("Device Tree overlays are %sabled\n", 272 dtoverlay_enabled ? "en" : "dis"); 273 } 274 } 275 276 void 277 command_dtoverlay(char *arg) 278 { 279 if (!arg || !*arg) { 280 command_help(""); 281 return; 282 } 283 284 dtoverlay_add(arg); 285 } 286 #endif 287 288 void 289 command_modules(char *arg) 290 { 291 if (arg && *arg) { 292 if (strcmp(arg, "on") == 0) 293 module_enable(1); 294 else if (strcmp(arg, "off") == 0) 295 module_enable(0); 296 else if (strcmp(arg, "reset") == 0) 297 module_remove_all(); 298 else { 299 command_help(""); 300 return; 301 } 302 } else { 303 printf("modules are %sabled\n", module_enabled ? "en" : "dis"); 304 } 305 } 306 307 void 308 command_load(char *arg) 309 { 310 if (!arg || !*arg) { 311 command_help(""); 312 return; 313 } 314 315 module_add(arg); 316 } 317 318 void 319 command_unload(char *arg) 320 { 321 if (!arg || !*arg) { 322 command_help(""); 323 return; 324 } 325 326 module_remove(arg); 327 } 328 329 void 330 command_ls(char *arg) 331 { 332 ls(arg); 333 } 334 335 void 336 command_gop(char *arg) 337 { 338 UINT32 mode; 339 340 if (!arg || !*arg) { 341 efi_gop_dump(); 342 return; 343 } 344 345 mode = atoi(arg); 346 efi_gop_setmode(mode); 347 } 348 349 void 350 command_mem(char *arg) 351 { 352 EFI_MEMORY_DESCRIPTOR *md, *memmap; 353 UINTN nentries, mapkey, descsize; 354 UINT32 descver; 355 int n; 356 357 printf("Type Start End Attributes\n"); 358 printf("---------------------- ---------------- ---------------- ----------------\n"); 359 memmap = LibMemoryMap(&nentries, &mapkey, &descsize, &descver); 360 for (n = 0, md = memmap; n < nentries; n++, md = NextMemoryDescriptor(md, descsize)) { 361 const char *mem_type = "<unknown>"; 362 if (md->Type < __arraycount(efi_memory_type)) 363 mem_type = efi_memory_type[md->Type]; 364 365 printf("%-22s %016" PRIx64 " %016" PRIx64 " %016" PRIx64 "\n", 366 mem_type, md->PhysicalStart, md->PhysicalStart + (md->NumberOfPages * EFI_PAGE_SIZE) - 1, 367 md->Attribute); 368 } 369 } 370 371 void 372 command_menu(char *arg) 373 { 374 if (bootcfg_info.nummenu == 0) { 375 printf("No menu defined in boot.cfg\n"); 376 return; 377 } 378 379 doboottypemenu(); /* Does not return */ 380 } 381 382 void 383 command_printtab(const char *key, const char *fmt, ...) 384 { 385 va_list ap; 386 387 printf("%-16s: ", key); 388 389 va_start(ap, fmt); 390 vprintf(fmt, ap); 391 va_end(ap); 392 } 393 394 void 395 command_version(char *arg) 396 { 397 char pathbuf[80]; 398 char *ufirmware; 399 const UINT64 *osindsup; 400 int rv; 401 402 command_printtab("Version", "%s (%s)\n", 403 bootprog_rev, bootprog_kernrev); 404 command_printtab("EFI", "%d.%02d\n", 405 ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff); 406 407 ufirmware = NULL; 408 rv = ucs2_to_utf8(ST->FirmwareVendor, &ufirmware); 409 if (rv == 0) { 410 command_printtab("Firmware", "%s (rev 0x%x)\n", ufirmware, 411 ST->FirmwareRevision); 412 FreePool(ufirmware); 413 } 414 if (bootcfg_path(pathbuf, sizeof(pathbuf)) == 0) { 415 command_printtab("Config path", "%s\n", pathbuf); 416 } 417 418 osindsup = LibGetVariable(L"OsIndicationsSupported", &EfiGlobalVariable); 419 if (osindsup != NULL) { 420 command_printtab("OS Indications", "0x%" PRIx64 "\n", 421 *osindsup); 422 } 423 424 #ifdef EFIBOOT_FDT 425 efi_fdt_show(); 426 #endif 427 #ifdef EFIBOOT_ACPI 428 efi_acpi_show(); 429 #endif 430 efi_rng_show(); 431 efi_md_show(); 432 efi_gop_show(); 433 } 434 435 void 436 command_quit(char *arg) 437 { 438 efi_exit(); 439 } 440 441 void 442 command_reset(char *arg) 443 { 444 efi_reboot(); 445 } 446 447 void 448 command_setup(char *arg) 449 { 450 EFI_STATUS status; 451 const UINT64 *osindsup; 452 UINT64 osind; 453 454 osindsup = LibGetVariable(L"OsIndicationsSupported", &EfiGlobalVariable); 455 if (osindsup == NULL || (*osindsup & EFI_OS_INDICATIONS_BOOT_TO_FW_UI) == 0) { 456 printf("Not supported by firmware\n"); 457 return; 458 } 459 460 osind = EFI_OS_INDICATIONS_BOOT_TO_FW_UI; 461 status = LibSetNVVariable(L"OsIndications", &EfiGlobalVariable, sizeof(osind), &osind); 462 if (EFI_ERROR(status)) { 463 printf("Failed to set OsIndications variable: %lu\n", (u_long)status); 464 return; 465 } 466 467 efi_reboot(); 468 } 469 470 void 471 command_userconf(char *arg) 472 { 473 userconf_add(arg); 474 } 475 476 int 477 set_default_device(const char *arg) 478 { 479 if (strlen(arg) + 1 > sizeof(default_device)) 480 return ERANGE; 481 strcpy(default_device, arg); 482 return 0; 483 } 484 485 char * 486 get_default_device(void) 487 { 488 return default_device; 489 } 490 491 void 492 set_default_fstype(int fstype) 493 { 494 default_fstype = fstype; 495 } 496 497 int 498 get_default_fstype(void) 499 { 500 return default_fstype; 501 } 502 503 int 504 set_initrd_path(const char *arg) 505 { 506 if (strlen(arg) + 1 > sizeof(initrd_path)) 507 return ERANGE; 508 strcpy(initrd_path, arg); 509 return 0; 510 } 511 512 char * 513 get_initrd_path(void) 514 { 515 return initrd_path; 516 } 517 518 int 519 set_dtb_path(const char *arg) 520 { 521 if (strlen(arg) + 1 > sizeof(dtb_path)) 522 return ERANGE; 523 strcpy(dtb_path, arg); 524 return 0; 525 } 526 527 char * 528 get_dtb_path(void) 529 { 530 return dtb_path; 531 } 532 533 int 534 set_rndseed_path(const char *arg) 535 { 536 if (strlen(arg) + 1 > sizeof(rndseed_path)) 537 return ERANGE; 538 strcpy(rndseed_path, arg); 539 return 0; 540 } 541 542 char * 543 get_rndseed_path(void) 544 { 545 return rndseed_path; 546 } 547 548 int 549 set_bootfile(const char *arg) 550 { 551 if (strlen(arg) + 1 > sizeof(netbsd_path)) 552 return ERANGE; 553 strcpy(netbsd_path, arg); 554 return 0; 555 } 556 557 int 558 set_bootargs(const char *arg) 559 { 560 if (strlen(arg) + 1 > sizeof(netbsd_args)) 561 return ERANGE; 562 strcpy(netbsd_args, arg); 563 return 0; 564 } 565 566 void 567 boot(void) 568 { 569 char pathbuf[80]; 570 int currname, c; 571 572 if (bootcfg_path(pathbuf, sizeof(pathbuf)) == 0) { 573 twiddle_toggle = 1; 574 parsebootconf(pathbuf); 575 } 576 577 if (bootcfg_info.clear) 578 uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); 579 580 print_bootcfg_banner(bootprog_name, bootprog_rev); 581 582 /* Display menu if configured */ 583 if (bootcfg_info.nummenu > 0) { 584 doboottypemenu(); /* No return */ 585 } 586 587 printf("Press return to boot now, any other key for boot prompt\n"); 588 589 if (netbsd_path[0] != '\0') 590 currname = -1; 591 else 592 currname = 0; 593 594 for (; currname < (int)NUMNAMES; currname++) { 595 if (currname >= 0) 596 set_bootfile(names[currname]); 597 printf("booting %s%s%s - starting in ", netbsd_path, 598 netbsd_args[0] != '\0' ? " " : "", netbsd_args); 599 600 c = awaitkey(bootcfg_info.timeout, 1); 601 if (c != '\r' && c != '\n' && c != '\0') 602 bootprompt(); /* does not return */ 603 604 efi_block_set_readahead(true); 605 exec_netbsd(netbsd_path, netbsd_args); 606 efi_block_set_readahead(false); 607 } 608 609 bootprompt(); /* does not return */ 610 } 611