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