boot.c revision 1.19 1 /* $NetBSD: boot.c,v 1.19 2019/12/18 21:46:03 riastradh 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
36 #include <sys/bootblock.h>
37 #include <sys/boot_flag.h>
38 #include <machine/limits.h>
39
40 #include <loadfile.h>
41
42 extern const char bootprog_name[], bootprog_rev[], bootprog_kernrev[];
43
44 extern char twiddle_toggle;
45
46 static const char * const names[] = {
47 "netbsd", "netbsd.gz",
48 "onetbsd", "onetbsd.gz",
49 "netbsd.old", "netbsd.old.gz",
50 };
51
52 #define NUMNAMES __arraycount(names)
53
54 static const char *efi_memory_type[] = {
55 [EfiReservedMemoryType] = "Reserved Memory Type",
56 [EfiLoaderCode] = "Loader Code",
57 [EfiLoaderData] = "Loader Data",
58 [EfiBootServicesCode] = "Boot Services Code",
59 [EfiBootServicesData] = "Boot Services Data",
60 [EfiRuntimeServicesCode] = "Runtime Services Code",
61 [EfiRuntimeServicesData] = "Runtime Services Data",
62 [EfiConventionalMemory] = "Conventional Memory",
63 [EfiUnusableMemory] = "Unusable Memory",
64 [EfiACPIReclaimMemory] = "ACPI Reclaim Memory",
65 [EfiACPIMemoryNVS] = "ACPI Memory NVS",
66 [EfiMemoryMappedIO] = "MMIO",
67 [EfiMemoryMappedIOPortSpace] = "MMIO (Port Space)",
68 [EfiPalCode] = "Pal Code",
69 [EfiPersistentMemory] = "Persistent Memory",
70 };
71
72 static char default_device[32];
73 static char initrd_path[255];
74 static char dtb_path[255];
75 static char efibootplist_path[255];
76 static char netbsd_path[255];
77 static char netbsd_args[255];
78 static char rndseed_path[255];
79
80 #define DEFTIMEOUT 5
81 #define DEFFILENAME names[0]
82
83 int set_bootfile(const char *);
84 int set_bootargs(const char *);
85
86 void command_boot(char *);
87 void command_dev(char *);
88 void command_dtb(char *);
89 void command_plist(char *);
90 void command_initrd(char *);
91 void command_rndseed(char *);
92 void command_ls(char *);
93 void command_mem(char *);
94 void command_printenv(char *);
95 void command_setenv(char *);
96 void command_clearenv(char *);
97 void command_resetenv(char *);
98 void command_reset(char *);
99 void command_version(char *);
100 void command_quit(char *);
101
102 const struct boot_command commands[] = {
103 { "boot", command_boot, "boot [dev:][filename] [args]\n (ex. \"hd0a:\\netbsd.old -s\"" },
104 { "dev", command_dev, "dev" },
105 { "dtb", command_dtb, "dtb [dev:][filename]" },
106 { "plist", command_plist, "plist [dev:][filename]" },
107 { "initrd", command_initrd, "initrd [dev:][filename]" },
108 { "rndseed", command_rndseed, "rndseed [dev:][filename]" },
109 { "ls", command_ls, "ls [hdNn:/path]" },
110 { "mem", command_mem, "mem" },
111 { "printenv", command_printenv, "printenv [key]" },
112 { "setenv", command_setenv, "setenv <key> <value>" },
113 { "clearenv", command_clearenv, "clearenv <key>" },
114 { "resetenv", command_resetenv, "resetenv" },
115 { "reboot", command_reset, "reboot|reset" },
116 { "reset", command_reset, NULL },
117 { "version", command_version, "version" },
118 { "help", command_help, "help|?" },
119 { "?", command_help, NULL },
120 { "quit", command_quit, "quit" },
121 { NULL, NULL },
122 };
123
124 void
125 command_help(char *arg)
126 {
127 int n;
128
129 printf("commands are:\n");
130 for (n = 0; commands[n].c_name; n++) {
131 if (commands[n].c_help)
132 printf("%s\n", commands[n].c_help);
133 }
134 }
135
136 void
137 command_boot(char *arg)
138 {
139 char *fname = arg;
140 const char *kernel = *fname ? fname : bootfile;
141 char *bootargs = gettrailer(arg);
142
143 if (!kernel || !*kernel)
144 kernel = DEFFILENAME;
145
146 if (!*bootargs)
147 bootargs = netbsd_args;
148
149 exec_netbsd(kernel, bootargs);
150 }
151
152 void
153 command_dev(char *arg)
154 {
155 if (arg && *arg) {
156 set_default_device(arg);
157 } else {
158 efi_block_show();
159 efi_net_show();
160 }
161
162 if (strlen(default_device) > 0) {
163 printf("\n");
164 printf("default: %s\n", default_device);
165 }
166 }
167
168 void
169 command_dtb(char *arg)
170 {
171 set_dtb_path(arg);
172 }
173
174 void
175 command_plist(char *arg)
176 {
177 if (set_efibootplist_path(arg) == 0)
178 load_efibootplist(false);
179 }
180
181 void
182 command_initrd(char *arg)
183 {
184 set_initrd_path(arg);
185 }
186
187 void
188 command_rndseed(char *arg)
189 {
190 set_rndseed_path(arg);
191 }
192
193 void
194 command_ls(char *arg)
195 {
196 ls(arg);
197 }
198
199 void
200 command_mem(char *arg)
201 {
202 EFI_MEMORY_DESCRIPTOR *md, *memmap;
203 UINTN nentries, mapkey, descsize;
204 UINT32 descver;
205 int n;
206
207 printf("Type Start End Attributes\n");
208 printf("---------------------- ---------------- ---------------- ----------------\n");
209 memmap = LibMemoryMap(&nentries, &mapkey, &descsize, &descver);
210 for (n = 0, md = memmap; n < nentries; n++, md = NextMemoryDescriptor(md, descsize)) {
211 const char *mem_type = "<unknown>";
212 if (md->Type < __arraycount(efi_memory_type))
213 mem_type = efi_memory_type[md->Type];
214
215 printf("%-22s %016" PRIx64 " %016" PRIx64 " %016" PRIx64 "\n",
216 mem_type, md->PhysicalStart, md->PhysicalStart + (md->NumberOfPages * EFI_PAGE_SIZE) - 1,
217 md->Attribute);
218 }
219 }
220
221 void
222 command_printenv(char *arg)
223 {
224 char *val;
225
226 if (arg && *arg) {
227 val = efi_env_get(arg);
228 if (val) {
229 printf("\"%s\" = \"%s\"\n", arg, val);
230 FreePool(val);
231 }
232 } else {
233 efi_env_print();
234 }
235 }
236
237 void
238 command_setenv(char *arg)
239 {
240 char *spc;
241
242 spc = strchr(arg, ' ');
243 if (spc == NULL || spc[1] == '\0') {
244 command_help("");
245 return;
246 }
247
248 *spc = '\0';
249 efi_env_set(arg, spc + 1);
250 }
251
252 void
253 command_clearenv(char *arg)
254 {
255 if (*arg == '\0') {
256 command_help("");
257 return;
258 }
259 efi_env_clear(arg);
260 }
261
262 void
263 command_resetenv(char *arg)
264 {
265 efi_env_reset();
266 }
267
268 void
269 command_version(char *arg)
270 {
271 char *ufirmware;
272 int rv;
273
274 printf("EFI version: %d.%02d\n",
275 ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
276 ufirmware = NULL;
277 rv = ucs2_to_utf8(ST->FirmwareVendor, &ufirmware);
278 if (rv == 0) {
279 printf("EFI Firmware: %s (rev 0x%x)\n", ufirmware,
280 ST->FirmwareRevision);
281 FreePool(ufirmware);
282 }
283
284 efi_fdt_show();
285 efi_acpi_show();
286 }
287
288 void
289 command_quit(char *arg)
290 {
291 efi_exit();
292 }
293
294 void
295 command_reset(char *arg)
296 {
297 efi_reboot();
298 }
299
300 int
301 set_default_device(const char *arg)
302 {
303 if (strlen(arg) + 1 > sizeof(default_device))
304 return ERANGE;
305 strcpy(default_device, arg);
306 return 0;
307 }
308
309 char *
310 get_default_device(void)
311 {
312 return default_device;
313 }
314
315 int
316 set_initrd_path(const char *arg)
317 {
318 if (strlen(arg) + 1 > sizeof(initrd_path))
319 return ERANGE;
320 strcpy(initrd_path, arg);
321 return 0;
322 }
323
324 char *
325 get_initrd_path(void)
326 {
327 return initrd_path;
328 }
329
330 int
331 set_dtb_path(const char *arg)
332 {
333 if (strlen(arg) + 1 > sizeof(dtb_path))
334 return ERANGE;
335 strcpy(dtb_path, arg);
336 return 0;
337 }
338
339 char *
340 get_dtb_path(void)
341 {
342 return dtb_path;
343 }
344
345 int
346 set_efibootplist_path(const char *arg)
347 {
348 if (strlen(arg) + 1 > sizeof(efibootplist_path))
349 return ERANGE;
350 strcpy(efibootplist_path, arg);
351 return 0;
352 }
353
354 char *get_efibootplist_path(void)
355 {
356 return efibootplist_path;
357 }
358
359 int
360 set_rndseed_path(const char *arg)
361 {
362 if (strlen(arg) + 1 > sizeof(rndseed_path))
363 return ERANGE;
364 strcpy(rndseed_path, arg);
365 return 0;
366 }
367
368 char *
369 get_rndseed_path(void)
370 {
371 return rndseed_path;
372 }
373
374 int
375 set_bootfile(const char *arg)
376 {
377 if (strlen(arg) + 1 > sizeof(netbsd_path))
378 return ERANGE;
379 strcpy(netbsd_path, arg);
380 return 0;
381 }
382
383 int
384 set_bootargs(const char *arg)
385 {
386 if (strlen(arg) + 1 > sizeof(netbsd_args))
387 return ERANGE;
388 strcpy(netbsd_args, arg);
389 return 0;
390 }
391
392 void
393 print_banner(void)
394 {
395 printf("\n\n"
396 ">> %s, Revision %s (from NetBSD %s)\n",
397 bootprog_name, bootprog_rev, bootprog_kernrev);
398 }
399
400 static void
401 read_env(void)
402 {
403 char *s;
404
405 s = efi_env_get("efibootplist");
406 if (s) {
407 #ifdef EFIBOOT_DEBUG
408 printf(">> Setting efiboot.plist path to '%s' from environment\n", s);
409 #endif
410 set_efibootplist_path(s);
411 FreePool(s);
412 }
413
414 /*
415 * Read the efiboot.plist now as it may contain additional
416 * environment variables.
417 */
418 load_efibootplist(true);
419
420 s = efi_env_get("fdtfile");
421 if (s) {
422 #ifdef EFIBOOT_DEBUG
423 printf(">> Setting DTB path to '%s' from environment\n", s);
424 #endif
425 set_dtb_path(s);
426 FreePool(s);
427 }
428
429 s = efi_env_get("initrd");
430 if (s) {
431 #ifdef EFIBOOT_DEBUG
432 printf(">> Setting initrd path to '%s' from environment\n", s);
433 #endif
434 set_initrd_path(s);
435 FreePool(s);
436 }
437
438 s = efi_env_get("bootfile");
439 if (s) {
440 #ifdef EFIBOOT_DEBUG
441 printf(">> Setting bootfile path to '%s' from environment\n", s);
442 #endif
443 set_bootfile(s);
444 FreePool(s);
445 }
446
447 s = efi_env_get("rootdev");
448 if (s) {
449 #ifdef EFIBOOT_DEBUG
450 printf(">> Setting default device to '%s' from environment\n", s);
451 #endif
452 set_default_device(s);
453 FreePool(s);
454 }
455
456 s = efi_env_get("bootargs");
457 if (s) {
458 #ifdef EFIBOOT_DEBUG
459 printf(">> Setting default boot args to '%s' from environment\n", s);
460 #endif
461 set_bootargs(s);
462 FreePool(s);
463 }
464
465 s = efi_env_get("rndseed");
466 if (s) {
467 #ifdef EFIBOOT_DEBUG
468 printf(">> Setting rndseed path to '%s' from environment\n", s);
469 #endif
470 set_rndseed_path(s);
471 FreePool(s);
472 }
473 }
474
475 void
476 boot(void)
477 {
478 int currname, c;
479
480 read_env();
481 print_banner();
482 printf("Press return to boot now, any other key for boot prompt\n");
483
484 if (netbsd_path[0] != '\0')
485 currname = -1;
486 else
487 currname = 0;
488
489 for (; currname < (int)NUMNAMES; currname++) {
490 if (currname >= 0)
491 set_bootfile(names[currname]);
492 printf("booting %s%s%s - starting in ", netbsd_path,
493 netbsd_args[0] != '\0' ? " " : "", netbsd_args);
494
495 c = awaitkey(DEFTIMEOUT, 1);
496 if (c != '\r' && c != '\n' && c != '\0')
497 bootprompt(); /* does not return */
498
499 exec_netbsd(netbsd_path, netbsd_args);
500 }
501
502 bootprompt(); /* does not return */
503 }
504