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