xref: /netbsd-src/sys/stand/efiboot/boot.c (revision dea74b4df0a1278747c8ae642ad83e60b510b95f)
1 /*	$NetBSD: boot.c,v 1.45 2023/06/14 00:42:21 rin Exp $	*/
2 
3 /*-
4  * Copyright (c) 2016 Kimihiro Nonaka <nonaka@netbsd.org>
5  * Copyright (c) 2018 Jared McNeill <jmcneill@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
bootcfg_path(char * pathbuf,size_t pathbuflen)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
command_help(char * arg)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
command_acpi(char * arg)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
command_boot(char * arg)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
command_dev(char * arg)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
command_initrd(char * arg)238 command_initrd(char *arg)
239 {
240 	set_initrd_path(arg);
241 }
242 
243 void
command_rndseed(char * arg)244 command_rndseed(char *arg)
245 {
246 	set_rndseed_path(arg);
247 }
248 
249 #ifdef EFIBOOT_FDT
250 void
command_dtb(char * arg)251 command_dtb(char *arg)
252 {
253 	set_dtb_path(arg);
254 }
255 
256 void
command_dtoverlays(char * arg)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
command_dtoverlay(char * arg)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
command_modules(char * arg)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
command_load(char * arg)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
command_unload(char * arg)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
command_ls(char * arg)330 command_ls(char *arg)
331 {
332 	ls(arg);
333 }
334 
335 void
command_gop(char * arg)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
command_mem(char * arg)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
command_menu(char * arg)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
command_printtab(const char * key,const char * fmt,...)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
command_version(char * arg)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
command_quit(char * arg)436 command_quit(char *arg)
437 {
438 	efi_exit();
439 }
440 
441 void
command_reset(char * arg)442 command_reset(char *arg)
443 {
444 	efi_reboot();
445 }
446 
447 void
command_setup(char * arg)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
command_userconf(char * arg)471 command_userconf(char *arg)
472 {
473 	userconf_add(arg);
474 }
475 
476 int
set_default_device(const char * arg)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 *
get_default_device(void)486 get_default_device(void)
487 {
488 	return default_device;
489 }
490 
491 void
set_default_fstype(int fstype)492 set_default_fstype(int fstype)
493 {
494 	default_fstype = fstype;
495 }
496 
497 int
get_default_fstype(void)498 get_default_fstype(void)
499 {
500 	return default_fstype;
501 }
502 
503 int
set_initrd_path(const char * arg)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 *
get_initrd_path(void)513 get_initrd_path(void)
514 {
515 	return initrd_path;
516 }
517 
518 int
set_dtb_path(const char * arg)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 *
get_dtb_path(void)528 get_dtb_path(void)
529 {
530 	return dtb_path;
531 }
532 
533 int
set_rndseed_path(const char * arg)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 *
get_rndseed_path(void)543 get_rndseed_path(void)
544 {
545 	return rndseed_path;
546 }
547 
548 int
set_bootfile(const char * arg)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
set_bootargs(const char * arg)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
boot(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