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