/* $NetBSD: boot.c,v 1.45 2023/06/14 00:42:21 rin Exp $ */ /*- * Copyright (c) 2016 Kimihiro Nonaka * Copyright (c) 2018 Jared McNeill * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "efiboot.h" #include "efiblock.h" #include "efifile.h" #include "efirng.h" #include "module.h" #include "bootmenu.h" #ifdef EFIBOOT_FDT #include "efifdt.h" #include "overlay.h" #endif #ifdef EFIBOOT_ACPI #include "efiacpi.h" #endif #include #include #include #include #include extern const char bootprog_name[], bootprog_rev[], bootprog_kernrev[]; extern char twiddle_toggle; static const char * const names[] = { "netbsd", "netbsd.gz", "onetbsd", "onetbsd.gz", "netbsd.old", "netbsd.old.gz", }; #define NUMNAMES __arraycount(names) static const char *efi_memory_type[] = { [EfiReservedMemoryType] = "Reserved Memory Type", [EfiLoaderCode] = "Loader Code", [EfiLoaderData] = "Loader Data", [EfiBootServicesCode] = "Boot Services Code", [EfiBootServicesData] = "Boot Services Data", [EfiRuntimeServicesCode] = "Runtime Services Code", [EfiRuntimeServicesData] = "Runtime Services Data", [EfiConventionalMemory] = "Conventional Memory", [EfiUnusableMemory] = "Unusable Memory", [EfiACPIReclaimMemory] = "ACPI Reclaim Memory", [EfiACPIMemoryNVS] = "ACPI Memory NVS", [EfiMemoryMappedIO] = "MMIO", [EfiMemoryMappedIOPortSpace] = "MMIO (Port Space)", [EfiPalCode] = "Pal Code", [EfiPersistentMemory] = "Persistent Memory", }; static char default_device[32]; static int default_fstype = FS_UNUSED; static char initrd_path[255]; static char dtb_path[255]; static char netbsd_path[255]; static char netbsd_args[255]; static char rndseed_path[255]; #define DEFFILENAME names[0] int set_bootfile(const char *); int set_bootargs(const char *); #ifdef EFIBOOT_ACPI void command_acpi(char *); #endif void command_boot(char *); void command_dev(char *); void command_initrd(char *); void command_rndseed(char *); #ifdef EFIBOOT_FDT void command_dtb(char *); void command_dtoverlay(char *); void command_dtoverlays(char *); #endif void command_modules(char *); void command_load(char *); void command_unload(char *); void command_ls(char *); void command_gop(char *); void command_mem(char *); void command_menu(char *); void command_reset(char *); void command_setup(char *); void command_userconf(char *); void command_version(char *); void command_quit(char *); const struct boot_command commands[] = { #ifdef EFIBOOT_ACPI { "acpi", command_acpi, "acpi [{on|off}]" }, #endif { "boot", command_boot, "boot [dev:][filename] [args]\n (ex. \"hd0a:\\netbsd.old -s\"" }, { "dev", command_dev, "dev" }, #ifdef EFIBOOT_FDT { "dtb", command_dtb, "dtb [dev:][filename]" }, { "dtoverlay", command_dtoverlay, "dtoverlay [dev:][filename]" }, { "dtoverlays", command_dtoverlays, "dtoverlays [{on|off|reset}]" }, #endif { "initrd", command_initrd, "initrd [dev:][filename]" }, { "fs", command_initrd, NULL }, { "rndseed", command_rndseed, "rndseed [dev:][filename]" }, { "modules", command_modules, "modules [{on|off|reset}]" }, { "load", command_load, "load " }, { "unload", command_unload, "unload " }, { "ls", command_ls, "ls [hdNn:/path]" }, { "gop", command_gop, "gop [mode]" }, { "mem", command_mem, "mem" }, { "menu", command_menu, "menu" }, { "reboot", command_reset, "reboot|reset" }, { "reset", command_reset, NULL }, { "setup", command_setup, "setup" }, { "userconf", command_userconf, "userconf " }, { "version", command_version, "version" }, { "ver", command_version, NULL }, { "help", command_help, "help|?" }, { "?", command_help, NULL }, { "quit", command_quit, "quit" }, { NULL, NULL, NULL }, }; static int bootcfg_path(char *pathbuf, size_t pathbuflen) { /* * Fallback to default_device * - for ISO9660 (efi_file_path() succeeds but does not work correctly) * - or whenever efi_file_path() fails (due to broken firmware) */ if (default_fstype == FS_ISO9660 || efi_bootdp == NULL || efi_file_path(efi_bootdp, BOOTCFG_FILENAME, pathbuf, pathbuflen)) snprintf(pathbuf, pathbuflen, "%s:%s", default_device, BOOTCFG_FILENAME); return 0; } void command_help(char *arg) { int n; printf("commands are:\n"); for (n = 0; commands[n].c_name; n++) { if (commands[n].c_help) printf("%s\n", commands[n].c_help); } } #ifdef EFIBOOT_ACPI void command_acpi(char *arg) { if (arg && *arg) { if (strcmp(arg, "on") == 0) efi_acpi_enable(1); else if (strcmp(arg, "off") == 0) efi_acpi_enable(0); else { command_help(""); return; } } else { printf("ACPI support is %sabled\n", efi_acpi_enabled() ? "en" : "dis"); } } #endif void command_boot(char *arg) { char *fname = arg; const char *kernel = *fname ? fname : bootfile; char *bootargs = gettrailer(arg); if (!kernel || !*kernel) kernel = DEFFILENAME; if (!*bootargs) bootargs = netbsd_args; efi_block_set_readahead(true); exec_netbsd(kernel, bootargs); efi_block_set_readahead(false); } void command_dev(char *arg) { if (arg && *arg) { set_default_device(arg); } else { efi_block_show(); efi_net_show(); } if (strlen(default_device) > 0) { printf("\n"); printf("default: %s\n", default_device); } } void command_initrd(char *arg) { set_initrd_path(arg); } void command_rndseed(char *arg) { set_rndseed_path(arg); } #ifdef EFIBOOT_FDT void command_dtb(char *arg) { set_dtb_path(arg); } void command_dtoverlays(char *arg) { if (arg && *arg) { if (strcmp(arg, "on") == 0) dtoverlay_enable(1); else if (strcmp(arg, "off") == 0) dtoverlay_enable(0); else if (strcmp(arg, "reset") == 0) dtoverlay_remove_all(); else { command_help(""); return; } } else { printf("Device Tree overlays are %sabled\n", dtoverlay_enabled ? "en" : "dis"); } } void command_dtoverlay(char *arg) { if (!arg || !*arg) { command_help(""); return; } dtoverlay_add(arg); } #endif void command_modules(char *arg) { if (arg && *arg) { if (strcmp(arg, "on") == 0) module_enable(1); else if (strcmp(arg, "off") == 0) module_enable(0); else if (strcmp(arg, "reset") == 0) module_remove_all(); else { command_help(""); return; } } else { printf("modules are %sabled\n", module_enabled ? "en" : "dis"); } } void command_load(char *arg) { if (!arg || !*arg) { command_help(""); return; } module_add(arg); } void command_unload(char *arg) { if (!arg || !*arg) { command_help(""); return; } module_remove(arg); } void command_ls(char *arg) { ls(arg); } void command_gop(char *arg) { UINT32 mode; if (!arg || !*arg) { efi_gop_dump(); return; } mode = atoi(arg); efi_gop_setmode(mode); } void command_mem(char *arg) { EFI_MEMORY_DESCRIPTOR *md, *memmap; UINTN nentries, mapkey, descsize; UINT32 descver; int n; printf("Type Start End Attributes\n"); printf("---------------------- ---------------- ---------------- ----------------\n"); memmap = LibMemoryMap(&nentries, &mapkey, &descsize, &descver); for (n = 0, md = memmap; n < nentries; n++, md = NextMemoryDescriptor(md, descsize)) { const char *mem_type = ""; if (md->Type < __arraycount(efi_memory_type)) mem_type = efi_memory_type[md->Type]; printf("%-22s %016" PRIx64 " %016" PRIx64 " %016" PRIx64 "\n", mem_type, md->PhysicalStart, md->PhysicalStart + (md->NumberOfPages * EFI_PAGE_SIZE) - 1, md->Attribute); } } void command_menu(char *arg) { if (bootcfg_info.nummenu == 0) { printf("No menu defined in boot.cfg\n"); return; } doboottypemenu(); /* Does not return */ } void command_printtab(const char *key, const char *fmt, ...) { va_list ap; printf("%-16s: ", key); va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); } void command_version(char *arg) { char pathbuf[80]; char *ufirmware; const UINT64 *osindsup; int rv; command_printtab("Version", "%s (%s)\n", bootprog_rev, bootprog_kernrev); command_printtab("EFI", "%d.%02d\n", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff); ufirmware = NULL; rv = ucs2_to_utf8(ST->FirmwareVendor, &ufirmware); if (rv == 0) { command_printtab("Firmware", "%s (rev 0x%x)\n", ufirmware, ST->FirmwareRevision); FreePool(ufirmware); } if (bootcfg_path(pathbuf, sizeof(pathbuf)) == 0) { command_printtab("Config path", "%s\n", pathbuf); } osindsup = LibGetVariable(L"OsIndicationsSupported", &EfiGlobalVariable); if (osindsup != NULL) { command_printtab("OS Indications", "0x%" PRIx64 "\n", *osindsup); } #ifdef EFIBOOT_FDT efi_fdt_show(); #endif #ifdef EFIBOOT_ACPI efi_acpi_show(); #endif efi_rng_show(); efi_md_show(); efi_gop_show(); } void command_quit(char *arg) { efi_exit(); } void command_reset(char *arg) { efi_reboot(); } void command_setup(char *arg) { EFI_STATUS status; const UINT64 *osindsup; UINT64 osind; osindsup = LibGetVariable(L"OsIndicationsSupported", &EfiGlobalVariable); if (osindsup == NULL || (*osindsup & EFI_OS_INDICATIONS_BOOT_TO_FW_UI) == 0) { printf("Not supported by firmware\n"); return; } osind = EFI_OS_INDICATIONS_BOOT_TO_FW_UI; status = LibSetNVVariable(L"OsIndications", &EfiGlobalVariable, sizeof(osind), &osind); if (EFI_ERROR(status)) { printf("Failed to set OsIndications variable: %lu\n", (u_long)status); return; } efi_reboot(); } void command_userconf(char *arg) { userconf_add(arg); } int set_default_device(const char *arg) { if (strlen(arg) + 1 > sizeof(default_device)) return ERANGE; strcpy(default_device, arg); return 0; } char * get_default_device(void) { return default_device; } void set_default_fstype(int fstype) { default_fstype = fstype; } int get_default_fstype(void) { return default_fstype; } int set_initrd_path(const char *arg) { if (strlen(arg) + 1 > sizeof(initrd_path)) return ERANGE; strcpy(initrd_path, arg); return 0; } char * get_initrd_path(void) { return initrd_path; } int set_dtb_path(const char *arg) { if (strlen(arg) + 1 > sizeof(dtb_path)) return ERANGE; strcpy(dtb_path, arg); return 0; } char * get_dtb_path(void) { return dtb_path; } int set_rndseed_path(const char *arg) { if (strlen(arg) + 1 > sizeof(rndseed_path)) return ERANGE; strcpy(rndseed_path, arg); return 0; } char * get_rndseed_path(void) { return rndseed_path; } int set_bootfile(const char *arg) { if (strlen(arg) + 1 > sizeof(netbsd_path)) return ERANGE; strcpy(netbsd_path, arg); return 0; } int set_bootargs(const char *arg) { if (strlen(arg) + 1 > sizeof(netbsd_args)) return ERANGE; strcpy(netbsd_args, arg); return 0; } void boot(void) { char pathbuf[80]; int currname, c; if (bootcfg_path(pathbuf, sizeof(pathbuf)) == 0) { twiddle_toggle = 1; parsebootconf(pathbuf); } if (bootcfg_info.clear) uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); print_bootcfg_banner(bootprog_name, bootprog_rev); /* Display menu if configured */ if (bootcfg_info.nummenu > 0) { doboottypemenu(); /* No return */ } printf("Press return to boot now, any other key for boot prompt\n"); if (netbsd_path[0] != '\0') currname = -1; else currname = 0; for (; currname < (int)NUMNAMES; currname++) { if (currname >= 0) set_bootfile(names[currname]); printf("booting %s%s%s - starting in ", netbsd_path, netbsd_args[0] != '\0' ? " " : "", netbsd_args); c = awaitkey(bootcfg_info.timeout, 1); if (c != '\r' && c != '\n' && c != '\0') bootprompt(); /* does not return */ efi_block_set_readahead(true); exec_netbsd(netbsd_path, netbsd_args); efi_block_set_readahead(false); } bootprompt(); /* does not return */ }