Home | History | Annotate | Line # | Download | only in efiboot
      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