boot.c revision 1.40 1 /* $NetBSD: boot.c,v 1.40 2021/10/17 14:12:54 jmcneill 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 void command_boot(char *);
97 void command_dev(char *);
98 void command_initrd(char *);
99 void command_rndseed(char *);
100 #ifdef EFIBOOT_FDT
101 void command_dtb(char *);
102 void command_dtoverlay(char *);
103 void command_dtoverlays(char *);
104 #endif
105 void command_modules(char *);
106 void command_load(char *);
107 void command_unload(char *);
108 void command_ls(char *);
109 void command_gop(char *);
110 void command_mem(char *);
111 void command_menu(char *);
112 void command_reset(char *);
113 void command_setup(char *);
114 void command_version(char *);
115 void command_quit(char *);
116
117 const struct boot_command commands[] = {
118 { "boot", command_boot, "boot [dev:][filename] [args]\n (ex. \"hd0a:\\netbsd.old -s\"" },
119 { "dev", command_dev, "dev" },
120 #ifdef EFIBOOT_FDT
121 { "dtb", command_dtb, "dtb [dev:][filename]" },
122 { "dtoverlay", command_dtoverlay, "dtoverlay [dev:][filename]" },
123 { "dtoverlays", command_dtoverlays, "dtoverlays [{on|off|reset}]" },
124 #endif
125 { "initrd", command_initrd, "initrd [dev:][filename]" },
126 { "fs", command_initrd, NULL },
127 { "rndseed", command_rndseed, "rndseed [dev:][filename]" },
128 { "modules", command_modules, "modules [{on|off|reset}]" },
129 { "load", command_load, "load <module_name>" },
130 { "unload", command_unload, "unload <module_name>" },
131 { "ls", command_ls, "ls [hdNn:/path]" },
132 { "gop", command_gop, "gop [mode]" },
133 { "mem", command_mem, "mem" },
134 { "menu", command_menu, "menu" },
135 { "reboot", command_reset, "reboot|reset" },
136 { "reset", command_reset, NULL },
137 { "setup", command_setup, "setup" },
138 { "version", command_version, "version" },
139 { "ver", command_version, NULL },
140 { "help", command_help, "help|?" },
141 { "?", command_help, NULL },
142 { "quit", command_quit, "quit" },
143 { NULL, NULL },
144 };
145
146 static int
147 bootcfg_path(char *pathbuf, size_t pathbuflen)
148 {
149
150 /*
151 * Fallback to default_device
152 * - for ISO9660 (efi_file_path() succeeds but does not work correctly)
153 * - or whenever efi_file_path() fails (due to broken firmware)
154 */
155 if (default_fstype == FS_ISO9660 || efi_bootdp == NULL ||
156 efi_file_path(efi_bootdp, BOOTCFG_FILENAME, pathbuf, pathbuflen))
157 snprintf(pathbuf, pathbuflen, "%s:%s", default_device,
158 BOOTCFG_FILENAME);
159
160 return 0;
161 }
162
163 void
164 command_help(char *arg)
165 {
166 int n;
167
168 printf("commands are:\n");
169 for (n = 0; commands[n].c_name; n++) {
170 if (commands[n].c_help)
171 printf("%s\n", commands[n].c_help);
172 }
173 }
174
175 void
176 command_boot(char *arg)
177 {
178 char *fname = arg;
179 const char *kernel = *fname ? fname : bootfile;
180 char *bootargs = gettrailer(arg);
181
182 if (!kernel || !*kernel)
183 kernel = DEFFILENAME;
184
185 if (!*bootargs)
186 bootargs = netbsd_args;
187
188 efi_block_set_readahead(true);
189 exec_netbsd(kernel, bootargs);
190 efi_block_set_readahead(false);
191 }
192
193 void
194 command_dev(char *arg)
195 {
196 if (arg && *arg) {
197 set_default_device(arg);
198 } else {
199 efi_block_show();
200 efi_net_show();
201 }
202
203 if (strlen(default_device) > 0) {
204 printf("\n");
205 printf("default: %s\n", default_device);
206 }
207 }
208
209 void
210 command_initrd(char *arg)
211 {
212 set_initrd_path(arg);
213 }
214
215 void
216 command_rndseed(char *arg)
217 {
218 set_rndseed_path(arg);
219 }
220
221 #ifdef EFIBOOT_FDT
222 void
223 command_dtb(char *arg)
224 {
225 set_dtb_path(arg);
226 }
227
228 void
229 command_dtoverlays(char *arg)
230 {
231 if (arg && *arg) {
232 if (strcmp(arg, "on") == 0)
233 dtoverlay_enable(1);
234 else if (strcmp(arg, "off") == 0)
235 dtoverlay_enable(0);
236 else if (strcmp(arg, "reset") == 0)
237 dtoverlay_remove_all();
238 else {
239 command_help("");
240 return;
241 }
242 } else {
243 printf("Device Tree overlays are %sabled\n",
244 dtoverlay_enabled ? "en" : "dis");
245 }
246 }
247
248 void
249 command_dtoverlay(char *arg)
250 {
251 if (!arg || !*arg) {
252 command_help("");
253 return;
254 }
255
256 dtoverlay_add(arg);
257 }
258 #endif
259
260 void
261 command_modules(char *arg)
262 {
263 if (arg && *arg) {
264 if (strcmp(arg, "on") == 0)
265 module_enable(1);
266 else if (strcmp(arg, "off") == 0)
267 module_enable(0);
268 else if (strcmp(arg, "reset") == 0)
269 module_remove_all();
270 else {
271 command_help("");
272 return;
273 }
274 } else {
275 printf("modules are %sabled\n", module_enabled ? "en" : "dis");
276 }
277 }
278
279 void
280 command_load(char *arg)
281 {
282 if (!arg || !*arg) {
283 command_help("");
284 return;
285 }
286
287 module_add(arg);
288 }
289
290 void
291 command_unload(char *arg)
292 {
293 if (!arg || !*arg) {
294 command_help("");
295 return;
296 }
297
298 module_remove(arg);
299 }
300
301 void
302 command_ls(char *arg)
303 {
304 ls(arg);
305 }
306
307 void
308 command_gop(char *arg)
309 {
310 UINT32 mode;
311
312 if (!arg || !*arg) {
313 efi_gop_dump();
314 return;
315 }
316
317 mode = atoi(arg);
318 efi_gop_setmode(mode);
319 }
320
321 void
322 command_mem(char *arg)
323 {
324 EFI_MEMORY_DESCRIPTOR *md, *memmap;
325 UINTN nentries, mapkey, descsize;
326 UINT32 descver;
327 int n;
328
329 printf("Type Start End Attributes\n");
330 printf("---------------------- ---------------- ---------------- ----------------\n");
331 memmap = LibMemoryMap(&nentries, &mapkey, &descsize, &descver);
332 for (n = 0, md = memmap; n < nentries; n++, md = NextMemoryDescriptor(md, descsize)) {
333 const char *mem_type = "<unknown>";
334 if (md->Type < __arraycount(efi_memory_type))
335 mem_type = efi_memory_type[md->Type];
336
337 printf("%-22s %016" PRIx64 " %016" PRIx64 " %016" PRIx64 "\n",
338 mem_type, md->PhysicalStart, md->PhysicalStart + (md->NumberOfPages * EFI_PAGE_SIZE) - 1,
339 md->Attribute);
340 }
341 }
342
343 void
344 command_menu(char *arg)
345 {
346 if (bootcfg_info.nummenu == 0) {
347 printf("No menu defined in boot.cfg\n");
348 return;
349 }
350
351 doboottypemenu(); /* Does not return */
352 }
353
354 void
355 command_version(char *arg)
356 {
357 char pathbuf[80];
358 char *ufirmware;
359 const UINT64 *osindsup;
360 int rv;
361
362 printf("Version: %s (%s)\n", bootprog_rev, bootprog_kernrev);
363 printf("EFI: %d.%02d\n",
364 ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
365 ufirmware = NULL;
366 rv = ucs2_to_utf8(ST->FirmwareVendor, &ufirmware);
367 if (rv == 0) {
368 printf("Firmware: %s (rev 0x%x)\n", ufirmware,
369 ST->FirmwareRevision);
370 FreePool(ufirmware);
371 }
372 if (bootcfg_path(pathbuf, sizeof(pathbuf)) == 0) {
373 printf("Config path: %s\n", pathbuf);
374 }
375
376 osindsup = LibGetVariable(L"OsIndicationsSupported", &EfiGlobalVariable);
377 if (osindsup != NULL) {
378 printf("UEFI OS indications supported: 0x%" PRIx64 "\n", *osindsup);
379 }
380
381 #ifdef EFIBOOT_FDT
382 efi_fdt_show();
383 #endif
384 #ifdef EFIBOOT_ACPI
385 efi_acpi_show();
386 #endif
387 efi_rng_show();
388 efi_md_show();
389 efi_gop_show();
390 }
391
392 void
393 command_quit(char *arg)
394 {
395 efi_exit();
396 }
397
398 void
399 command_reset(char *arg)
400 {
401 efi_reboot();
402 }
403
404 void
405 command_setup(char *arg)
406 {
407 EFI_STATUS status;
408 const UINT64 *osindsup;
409 UINT64 osind;
410
411 osindsup = LibGetVariable(L"OsIndicationsSupported", &EfiGlobalVariable);
412 if (osindsup == NULL || (*osindsup & EFI_OS_INDICATIONS_BOOT_TO_FW_UI) == 0) {
413 printf("Not supported by firmware\n");
414 return;
415 }
416
417 osind = EFI_OS_INDICATIONS_BOOT_TO_FW_UI;
418 status = LibSetNVVariable(L"OsIndications", &EfiGlobalVariable, sizeof(osind), &osind);
419 if (EFI_ERROR(status)) {
420 printf("Failed to set OsIndications variable: %lu\n", (u_long)status);
421 return;
422 }
423
424 efi_reboot();
425 }
426
427 int
428 set_default_device(const char *arg)
429 {
430 if (strlen(arg) + 1 > sizeof(default_device))
431 return ERANGE;
432 strcpy(default_device, arg);
433 return 0;
434 }
435
436 char *
437 get_default_device(void)
438 {
439 return default_device;
440 }
441
442 void
443 set_default_fstype(int fstype)
444 {
445 default_fstype = fstype;
446 }
447
448 int
449 get_default_fstype(void)
450 {
451 return default_fstype;
452 }
453
454 int
455 set_initrd_path(const char *arg)
456 {
457 if (strlen(arg) + 1 > sizeof(initrd_path))
458 return ERANGE;
459 strcpy(initrd_path, arg);
460 return 0;
461 }
462
463 char *
464 get_initrd_path(void)
465 {
466 return initrd_path;
467 }
468
469 int
470 set_dtb_path(const char *arg)
471 {
472 if (strlen(arg) + 1 > sizeof(dtb_path))
473 return ERANGE;
474 strcpy(dtb_path, arg);
475 return 0;
476 }
477
478 char *
479 get_dtb_path(void)
480 {
481 return dtb_path;
482 }
483
484 int
485 set_rndseed_path(const char *arg)
486 {
487 if (strlen(arg) + 1 > sizeof(rndseed_path))
488 return ERANGE;
489 strcpy(rndseed_path, arg);
490 return 0;
491 }
492
493 char *
494 get_rndseed_path(void)
495 {
496 return rndseed_path;
497 }
498
499 int
500 set_bootfile(const char *arg)
501 {
502 if (strlen(arg) + 1 > sizeof(netbsd_path))
503 return ERANGE;
504 strcpy(netbsd_path, arg);
505 return 0;
506 }
507
508 int
509 set_bootargs(const char *arg)
510 {
511 if (strlen(arg) + 1 > sizeof(netbsd_args))
512 return ERANGE;
513 strcpy(netbsd_args, arg);
514 return 0;
515 }
516
517 void
518 boot(void)
519 {
520 char pathbuf[80];
521 int currname, c;
522
523 if (bootcfg_path(pathbuf, sizeof(pathbuf)) == 0) {
524 twiddle_toggle = 1;
525 parsebootconf(pathbuf);
526 }
527
528 if (bootcfg_info.clear)
529 uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
530
531 print_bootcfg_banner(bootprog_name, bootprog_rev);
532
533 /* Display menu if configured */
534 if (bootcfg_info.nummenu > 0) {
535 doboottypemenu(); /* No return */
536 }
537
538 printf("Press return to boot now, any other key for boot prompt\n");
539
540 if (netbsd_path[0] != '\0')
541 currname = -1;
542 else
543 currname = 0;
544
545 for (; currname < (int)NUMNAMES; currname++) {
546 if (currname >= 0)
547 set_bootfile(names[currname]);
548 printf("booting %s%s%s - starting in ", netbsd_path,
549 netbsd_args[0] != '\0' ? " " : "", netbsd_args);
550
551 c = awaitkey(bootcfg_info.timeout, 1);
552 if (c != '\r' && c != '\n' && c != '\0')
553 bootprompt(); /* does not return */
554
555 efi_block_set_readahead(true);
556 exec_netbsd(netbsd_path, netbsd_args);
557 efi_block_set_readahead(false);
558 }
559
560 bootprompt(); /* does not return */
561 }
562