boot.c revision 1.34 1 /* $NetBSD: boot.c,v 1.34 2025/10/09 16:10:03 manu Exp $ */
2
3 /*-
4 * Copyright (c) 2016 Kimihiro Nonaka <nonaka (at) netbsd.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include "efiboot.h"
30
31 #include <sys/bootblock.h>
32 #include <sys/boot_flag.h>
33 #include <machine/limits.h>
34
35 #include "bootcfg.h"
36 #include "bootmod.h"
37 #include "bootmenu.h"
38 #include "biosdisk.h"
39 #include "devopen.h"
40
41 #ifdef _STANDALONE
42 #include <bootinfo.h>
43 #endif
44
45 int errno;
46 int boot_biosdev;
47 daddr_t boot_biossector;
48
49 extern const char bootprog_name[], bootprog_rev[], bootprog_kernrev[];
50
51 extern struct x86_boot_params boot_params;
52 extern char twiddle_toggle;
53
54 static const char * const names[][2] = {
55 { "netbsd", "netbsd.gz" },
56 { "onetbsd", "onetbsd.gz" },
57 { "netbsd.old", "netbsd.old.gz" },
58 { "netbsd/kernel", "netbsd/kernel.gz" },
59 { "onetbsd/kernel", "onetbsd/kernel.gz" },
60 { "netbsd.old/kernel", "netbsd.old/kernel.gz" },
61 };
62
63 #define NUMNAMES __arraycount(names)
64 #define DEFFILENAME names[0][0]
65
66 #ifndef EFIBOOTCFG_FILENAME
67 #define EFIBOOTCFG_FILENAME "esp:/EFI/NetBSD/boot.cfg"
68 #endif
69
70 void command_help(char *);
71 void command_quit(char *);
72 void command_boot(char *);
73 void command_pkboot(char *);
74 void command_consdev_subr(char *, bool);
75 void command_consdev(char *);
76 void command_kconsdev(char *);
77 void command_root(char *);
78 void command_dev(char *);
79 void command_devpath(char *);
80 void command_efivar(char *);
81 void command_gop(char *);
82 #if LIBSA_ENABLE_LS_OP
83 void command_ls(char *);
84 #endif
85 void command_memmap(char *);
86 #ifndef SMALL
87 void command_menu(char *);
88 #endif
89 void command_modules(char *);
90 void command_multiboot(char *);
91 void command_reloc(char *);
92 void command_text(char *);
93 void command_version(char *);
94
95 const struct bootblk_command commands[] = {
96 { "help", command_help },
97 { "?", command_help },
98 { "quit", command_quit },
99 { "boot", command_boot },
100 { "pkboot", command_pkboot },
101 { "consdev", command_consdev },
102 { "kconsdev", command_kconsdev },
103 { "root", command_root },
104 { "dev", command_dev },
105 { "devpath", command_devpath },
106 { "efivar", command_efivar },
107 { "fs", fs_add },
108 { "gop", command_gop },
109 { "load", module_add },
110 #if LIBSA_ENABLE_LS_OP
111 { "ls", command_ls },
112 #endif
113 { "memmap", command_memmap },
114 #ifndef SMALL
115 { "menu", command_menu },
116 #endif
117 { "modules", command_modules },
118 { "multiboot", command_multiboot },
119 { "reloc", command_reloc },
120 { "rndseed", rnd_add },
121 { "splash", splash_add },
122 { "text", command_text },
123 { "userconf", userconf_add },
124 { "version", command_version },
125 { NULL, NULL },
126 };
127
128 static char *default_fsname;
129 static char *default_devname;
130 static int default_unit, default_partition;
131 static const char *default_filename;
132 static const char *default_part_name;
133
134 static char *sprint_bootsel(const char *);
135 static void bootit(const char *, int);
136 static void bootit2(char *, size_t, int);
137
138 int
139 parsebootfile(const char *fname, char **fsname, char **devname, int *unit,
140 int *partition, const char **file)
141 {
142 const char *col;
143 static char savedevname[MAXDEVNAME+1];
144 #if defined(SUPPORT_NFS) || defined(SUPPORT_TFTP)
145 const struct netboot_fstab *nf;
146 #endif
147
148 *fsname = default_fsname;
149 if (default_part_name == NULL) {
150 *devname = default_devname;
151 } else {
152 snprintf(savedevname, sizeof(savedevname),
153 "NAME=%s", default_part_name);
154 *devname = savedevname;
155 }
156 *unit = default_unit;
157 *partition = default_partition;
158 *file = default_filename;
159
160 if (fname == NULL)
161 return 0;
162
163 if ((col = strchr(fname, ':')) != NULL) { /* device given */
164 int devlen;
165 int u = 0, p = 0;
166 int i = 0;
167
168 devlen = col - fname;
169 if (devlen > MAXDEVNAME)
170 return EINVAL;
171
172 if (strstr(fname, "NAME=") == fname) {
173 strlcpy(savedevname, fname, devlen + 1);
174 *fsname = "ufs";
175 *devname = savedevname;
176 *unit = -1;
177 *partition = -1;
178 fname = col + 1;
179 goto out;
180 }
181
182 #define isvalidname(c) ((c) >= 'a' && (c) <= 'z')
183 if (!isvalidname(fname[i]))
184 return EINVAL;
185 do {
186 savedevname[i] = fname[i];
187 i++;
188 } while (isvalidname(fname[i]));
189 savedevname[i] = '\0';
190
191 #define isnum(c) ((c) >= '0' && (c) <= '9')
192 if (i < devlen) {
193 if (!isnum(fname[i]))
194 return EUNIT;
195 do {
196 u *= 10;
197 u += fname[i++] - '0';
198 } while (isnum(fname[i]));
199 }
200
201 #define isvalidpart(c) ((c) >= 'a' && (c) <= 'z')
202 if (i < devlen) {
203 if (!isvalidpart(fname[i]))
204 return EPART;
205 p = fname[i++] - 'a';
206 }
207
208 if (i != devlen)
209 return ENXIO;
210
211 #if defined(SUPPORT_NFS) || defined(SUPPORT_TFTP)
212 nf = netboot_fstab_find(savedevname);
213 if (nf != NULL)
214 *fsname = (char *)nf->name;
215 else
216 #endif
217 *fsname = "ufs";
218 *devname = savedevname;
219 *unit = u;
220 *partition = p;
221 fname = col + 1;
222 }
223
224 out:
225 if (*fname)
226 *file = fname;
227
228 return 0;
229 }
230
231 static char *
232 snprint_bootdev(char *buf, size_t bufsize, const char *devname, int unit,
233 int partition)
234 {
235 static const char *no_partition_devs[] = { "esp", "net", "nfs", "tftp" };
236 int i;
237
238 for (i = 0; i < __arraycount(no_partition_devs); i++)
239 if (strcmp(devname, no_partition_devs[i]) == 0)
240 break;
241 if (strstr(devname, "NAME=") == devname)
242 strlcpy(buf, devname, bufsize);
243 else
244 snprintf(buf, bufsize, "%s%d%c", devname, unit,
245 i < __arraycount(no_partition_devs) ? '\0' : 'a' + partition);
246 return buf;
247 }
248
249 static char *
250 sprint_bootsel(const char *filename)
251 {
252 char *fsname, *devname;
253 int unit, partition;
254 const char *file;
255 static char buf[80];
256
257 if (parsebootfile(filename, &fsname, &devname, &unit,
258 &partition, &file) == 0) {
259 snprintf(buf, sizeof(buf), "%s:%s", snprint_bootdev(buf,
260 sizeof(buf), devname, unit, partition), file);
261 return buf;
262 }
263 return "(invalid)";
264 }
265
266 void
267 clearit(void)
268 {
269
270 if (bootcfg_info.clear)
271 clear_pc_screen();
272 }
273
274 static void
275 bootit(const char *filename, int howto)
276 {
277
278 if (howto & AB_VERBOSE)
279 printf("booting %s (howto 0x%x)\n", sprint_bootsel(filename),
280 howto);
281
282 if (exec_netbsd(filename, efi_loadaddr, howto, 0, efi_cleanup) < 0)
283 printf("boot: %s: %s\n", sprint_bootsel(filename),
284 strerror(errno));
285 else
286 printf("boot returned\n");
287 }
288
289 void
290 boot(void)
291 {
292 int currname;
293 int c;
294 #if defined(SUPPORT_NFS) || defined(SUPPORT_TFTP)
295 const struct netboot_fstab *nf;
296 #endif
297
298 boot_modules_enabled = !(boot_params.bp_flags & X86_BP_FLAGS_NOMODULES);
299
300 /* try to set default device to what BIOS tells us */
301 bios2dev(boot_biosdev, boot_biossector, &default_devname, &default_unit,
302 &default_partition, &default_part_name);
303
304 /* if the user types "boot" without filename */
305 default_filename = DEFFILENAME;
306
307 #if defined(SUPPORT_NFS) || defined(SUPPORT_TFTP)
308 nf = netboot_fstab_find(default_devname);
309 if (nf != NULL)
310 default_fsname = (char *)nf->name;
311 else
312 #endif
313 default_fsname = "ufs";
314
315 if (!(boot_params.bp_flags & X86_BP_FLAGS_NOBOOTCONF)) {
316 #ifdef EFIBOOTCFG_FILENAME
317 int rv = EINVAL;
318 if (efi_bootdp_type != BOOT_DEVICE_TYPE_NET)
319 rv = parsebootconf(EFIBOOTCFG_FILENAME);
320 if (rv)
321 #endif
322 parsebootconf(BOOTCFG_FILENAME);
323 } else {
324 bootcfg_info.timeout = boot_params.bp_timeout;
325 }
326
327 /*
328 * If console set in boot.cfg, switch to it.
329 * This will print the banner, so we don't need to explicitly do it
330 */
331 if (bootcfg_info.consdev) {
332 command_consdev(bootcfg_info.consdev);
333 } else {
334 clearit();
335 print_bootcfg_banner(bootprog_name, bootprog_rev);
336 }
337
338 /* Display the menu, if applicable */
339 twiddle_toggle = 0;
340 if (bootcfg_info.nummenu > 0) {
341 /* Does not return */
342 doboottypemenu();
343 }
344
345 printf("Press return to boot now, any other key for boot menu\n");
346 for (currname = 0; currname < NUMNAMES; currname++) {
347 printf("booting %s - starting in ",
348 sprint_bootsel(names[currname][0]));
349
350 c = awaitkey((bootcfg_info.timeout < 0) ? 0
351 : bootcfg_info.timeout, 1);
352 if ((c != '\r') && (c != '\n') && (c != '\0')) {
353 if ((boot_params.bp_flags & X86_BP_FLAGS_PASSWORD) == 0) {
354 /* do NOT ask for password */
355 bootmenu(); /* does not return */
356 } else {
357 /* DO ask for password */
358 if (check_password((char *)boot_params.bp_password)) {
359 /* password ok */
360 printf("type \"?\" or \"help\" for help.\n");
361 bootmenu(); /* does not return */
362 } else {
363 /* bad password */
364 printf("Wrong password.\n");
365 currname = 0;
366 continue;
367 }
368 }
369 }
370
371 /*
372 * try pairs of names[] entries, foo and foo.gz
373 */
374 /* don't print "booting..." again */
375 bootit(names[currname][0], 0);
376 /* since it failed, try compressed bootfile. */
377 bootit(names[currname][1], AB_VERBOSE);
378 }
379
380 bootmenu(); /* does not return */
381 }
382
383 /* ARGSUSED */
384 void
385 command_help(char *arg)
386 {
387
388 printf("commands are:\n"
389 "boot [dev:][filename] [-12acdqsvxz]\n"
390 #ifndef NO_RAIDFRAME
391 " dev syntax is (hd|fd|cd|raid)[N[x]]\n"
392 #else
393 " dev syntax is (hd|fd|cd)[N[x]]\n"
394 #endif
395 #ifndef NO_GPT
396 " or NAME=gpt_label\n"
397 #endif
398 " (ex. \"hd0a:netbsd.old -s\")\n"
399 "pkboot [dev:][filename] [-12acdqsvxz]\n"
400 "dev [dev:]\n"
401 "consdev {cons_spec}\n"
402 "kconsdev {cons_spec}\n"
403 " cons_spec can be\n"
404 " pc for keyboard and monitor\n"
405 " com[0123][,{speed}\n"
406 " com[,{addr}[,{speed}]] for EISA serial port\n"
407 " com{unit}[,{speed}]] for other serial port\n"
408 " ucom{unit}[,{speed}] for USB-to-serial adapter\n"
409 "root {root_spec}\n"
410 " root_spec can be disk, e.g. wd0, sd0\n"
411 " or string like wedge:name\n"
412 "devpath\n"
413 "efivar\n"
414 "gop [{modenum|list}]\n"
415 "load {path_to_module}\n"
416 #if LIBSA_ENABLE_LS_OP
417 "ls [dev:][path]\n"
418 #endif
419 "memmap [{sorted|unsorted|compact}]\n"
420 #ifndef SMALL
421 "menu (reenters boot menu, if defined in boot.cfg)\n"
422 #endif
423 "modules {on|off|enabled|disabled}\n"
424 "multiboot [dev:][filename] [<args>]\n"
425 "reloc {address|none|default}\n"
426 "rndseed {path_to_rndseed_file}\n"
427 "splash {path_to_image_file}\n"
428 "text [{modenum|list}]\n"
429 "userconf {command}\n"
430 "version\n"
431 "help|?\n"
432 "quit\n");
433 }
434
435 #if LIBSA_ENABLE_LS_OP
436 void
437 command_ls(char *arg)
438 {
439 const char *save = default_filename;
440
441 default_filename = "/";
442 ls(arg);
443 default_filename = save;
444 }
445 #endif
446
447 /* ARGSUSED */
448 void
449 command_quit(char *arg)
450 {
451
452 printf("Exiting...\n");
453 delay(1 * 1000 * 1000);
454 reboot();
455 /* Note: we shouldn't get to this point! */
456 panic("Could not reboot!");
457 }
458
459 static void
460 bootit2(char *path, size_t plen, int howto)
461 {
462 bootit(path, howto);
463 snprintf(path, plen, "%s.gz", path);
464 bootit(path, howto | AB_VERBOSE);
465 }
466
467 void
468 command_boot(char *arg)
469 {
470 char *filename;
471 char path[512];
472 int howto;
473
474 if (!parseboot(arg, &filename, &howto))
475 return;
476
477 if (filename != NULL && filename[0] != '\0') {
478 /* try old locations first to appease atf test beds */
479 snprintf(path, sizeof(path) - 4, "%s", filename);
480 bootit2(path, sizeof(path), howto);
481
482 /*
483 * now treat given filename as a directory unless there
484 * is already an embedded path-name separator '/' present
485 */
486 if (strchr(filename + 1, '/') == NULL) {
487 snprintf(path, sizeof(path) - 4, "%s/kernel",
488 filename);
489 bootit2(path, sizeof(path), howto);
490 }
491 } else {
492 int i;
493
494 for (i = 0; i < NUMNAMES; i++) {
495 bootit(names[i][0], howto);
496 bootit(names[i][1], howto);
497 }
498 }
499 }
500
501 void
502 command_pkboot(char *arg)
503 {
504 extern int has_prekern;
505 has_prekern = 1;
506 command_boot(arg);
507 has_prekern = 0;
508 }
509
510 void
511 command_dev(char *arg)
512 {
513 static char savedevname[MAXDEVNAME + 1];
514 char buf[80];
515 char *devname;
516 const char *file; /* dummy */
517
518 if (*arg == '\0') {
519 efi_disk_show();
520 efi_net_show();
521
522 if (default_part_name != NULL)
523 printf("default NAME=%s\n", default_part_name);
524 else
525 printf("default %s\n",
526 snprint_bootdev(buf, sizeof(buf),
527 default_devname, default_unit,
528 default_partition));
529 return;
530 }
531
532 if (strchr(arg, ':') == NULL ||
533 parsebootfile(arg, &default_fsname, &devname, &default_unit,
534 &default_partition, &file)) {
535 command_help(NULL);
536 return;
537 }
538
539 /* put to own static storage */
540 strncpy(savedevname, devname, MAXDEVNAME + 1);
541 default_devname = savedevname;
542
543 /* +5 to skip leading NAME= */
544 if (strstr(devname, "NAME=") == devname)
545 default_part_name = default_devname + 5;
546 }
547
548 static const struct cons_devs {
549 const char *name;
550 u_int tag;
551 int ioport;
552 bool parse_unit;
553 } cons_devs[] = {
554 { "pc", CONSDEV_PC, 0, false },
555 { "com0", CONSDEV_COM0, 0, false },
556 { "com1", CONSDEV_COM1, 0, false },
557 { "com2", CONSDEV_COM2, 0, false },
558 { "com3", CONSDEV_COM3, 0, false },
559 { "com0kbd", CONSDEV_COM0KBD, 0, false },
560 { "com1kbd", CONSDEV_COM1KBD, 0, false },
561 { "com2kbd", CONSDEV_COM2KBD, 0, false },
562 { "com3kbd", CONSDEV_COM3KBD, 0, false },
563 { "ucom", CONSDEV_UCOM, 0, true },
564 { "com", CONSDEV_COM0, -1, false }, /* backward compatible */
565 { "com", CONSDEV_COM, 0, true }, /* can match e.g.: com8 */
566 { "auto", CONSDEV_AUTO, 0, false },
567 { NULL, 0, 0, false }
568 };
569
570 void
571 command_consdev(char *arg)
572 {
573 return command_consdev_subr(arg, true);
574 }
575
576 void
577 command_kconsdev(char *arg)
578 {
579 return command_consdev_subr(arg, false);
580 }
581
582 void
583 command_consdev_subr(char *arg, bool switchcons)
584 {
585 const struct cons_devs *cdp;
586 char *sep, *sep2 = NULL;
587 int ioport, speed = 0;
588 int unit;
589 size_t xname_len;
590 size_t devname_len;
591 char *cp;
592
593 if (*arg == '\0') {
594 efi_cons_show();
595 return;
596 }
597
598 sep = strchr(arg, ',');
599 if (sep != NULL) {
600 *sep++ = '\0';
601 sep2 = strchr(sep, ',');
602 if (sep2 != NULL)
603 *sep2++ = '\0';
604 }
605
606
607 /*
608 * arg is for instance com5 or com5,0x3f8,115200
609 * we compute devname_len = strlen("com")
610 * unit = 5
611 */
612 xname_len = strlen(arg);
613 devname_len = xname_len;
614 unit = -1;
615 cp = strchr(arg, (int)',');
616 if (cp == NULL)
617 cp = arg + xname_len;
618 while (--cp >= arg) {
619 if (!isdigit((int)*cp)) {
620 if (cp - arg > 1) {
621 devname_len = cp + 1 - arg;
622 unit = (int)strtoul(cp + 1, NULL, 10);
623 break;
624 }
625 }
626 }
627
628 for (cdp = cons_devs; cdp->name; cdp++) {
629 bool match;
630
631 if (cdp->parse_unit)
632 match = (strncmp(arg, cdp->name, devname_len) == 0);
633 else
634 match = (strcmp(arg, cdp->name) == 0);
635 if (match) {
636 ioport = cdp->ioport;
637 if (cdp->tag == CONSDEV_PC || cdp->tag == CONSDEV_AUTO) {
638 if (sep != NULL || sep2 != NULL)
639 goto error;
640 } else {
641 /* com? */
642 if (ioport == -1) {
643 if (sep != NULL) {
644 u_long t = strtoul(sep, NULL, 0);
645 if (t > INT_MAX)
646 goto error;
647 ioport = (int)t;
648 }
649 if (sep2 != NULL) {
650 speed = atoi(sep2);
651 if (speed < 0)
652 goto error;
653 }
654 } else {
655 if (sep != NULL) {
656 speed = atoi(sep);
657 if (speed < 0)
658 goto error;
659 }
660 if (sep2 != NULL)
661 goto error;
662 }
663 }
664 efi_consinit(cdp->tag, ioport, unit, speed, switchcons);
665 if (switchcons) {
666 clearit();
667 print_bootcfg_banner(bootprog_name,
668 bootprog_rev);
669 }
670 return;
671 }
672 }
673 error:
674 printf("invalid console device.\n");
675 }
676
677 void
678 command_root(char *arg)
679 {
680 struct btinfo_rootdevice *biv = &bi_root;
681
682 strncpy(biv->devname, arg, sizeof(biv->devname));
683 if (biv->devname[sizeof(biv->devname)-1] != '\0') {
684 biv->devname[sizeof(biv->devname)-1] = '\0';
685 printf("truncated to %s\n",biv->devname);
686 }
687 }
688
689
690 #ifndef SMALL
691 /* ARGSUSED */
692 void
693 command_menu(char *arg)
694 {
695
696 if (bootcfg_info.nummenu > 0) {
697 /* Does not return */
698 doboottypemenu();
699 } else
700 printf("No menu defined in boot.cfg\n");
701 }
702 #endif /* !SMALL */
703
704 void
705 command_modules(char *arg)
706 {
707
708 if (strcmp(arg, "enabled") == 0 ||
709 strcmp(arg, "on") == 0)
710 boot_modules_enabled = true;
711 else if (strcmp(arg, "disabled") == 0 ||
712 strcmp(arg, "off") == 0)
713 boot_modules_enabled = false;
714 else
715 printf("invalid flag, must be 'enabled' or 'disabled'.\n");
716 }
717
718 void
719 command_multiboot(char *arg)
720 {
721 char *filename;
722
723 filename = arg;
724 if (exec_multiboot(filename, gettrailer(arg)) < 0)
725 printf("multiboot: %s: %s\n", sprint_bootsel(filename),
726 strerror(errno));
727 else
728 printf("boot returned\n");
729 }
730
731 void
732 command_reloc(char *arg)
733 {
734 char *ep;
735
736 if (*arg == '\0') {
737 switch (efi_reloc_type) {
738 case RELOC_NONE:
739 printf("reloc: none\n");
740 break;
741 case RELOC_ADDR:
742 printf("reloc: %p\n", (void *)efi_kernel_reloc);
743 break;
744 case RELOC_DEFAULT:
745 default:
746 printf("reloc: default\n");
747 break;
748 }
749 goto out;
750 }
751
752 if (strcmp(arg, "default") == 0) {
753 efi_reloc_type = RELOC_DEFAULT;
754 goto out;
755 }
756
757 if (strcmp(arg, "none") == 0) {
758 efi_reloc_type = RELOC_NONE;
759 goto out;
760 }
761
762 errno = 0;
763 efi_kernel_reloc = strtoul(arg, &ep, 0);
764 if (ep == arg || *ep != '\0' || errno)
765 printf("could not parse address \"%s\"\n", arg);
766 else
767 efi_reloc_type = RELOC_ADDR;
768 out:
769 return;
770
771 }
772
773 void
774 command_version(char *arg)
775 {
776 CHAR16 *path;
777 char *upath, *ufirmware;
778 int rv;
779
780 if (strcmp(arg, "full") == 0) {
781 printf("ImageBase: 0x%" PRIxPTR "\n",
782 (uintptr_t)efi_li->ImageBase);
783 printf("Stack: 0x%" PRIxPTR "\n", efi_main_sp);
784 printf("EFI version: %d.%02d\n",
785 ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
786 ufirmware = NULL;
787 rv = ucs2_to_utf8(ST->FirmwareVendor, &ufirmware);
788 if (rv == 0) {
789 printf("EFI Firmware: %s (rev %d.%02d)\n", ufirmware,
790 ST->FirmwareRevision >> 16,
791 ST->FirmwareRevision & 0xffff);
792 FreePool(ufirmware);
793 }
794 path = DevicePathToStr(efi_bootdp);
795 upath = NULL;
796 rv = ucs2_to_utf8(path, &upath);
797 FreePool(path);
798 if (rv == 0) {
799 printf("Boot DevicePath: %d:%d:%s\n",
800 DevicePathType(efi_bootdp),
801 DevicePathSubType(efi_bootdp), upath);
802 FreePool(upath);
803 }
804 }
805
806 printf("\n"
807 ">> %s, Revision %s (from NetBSD %s)\n"
808 ">> Memory: %d/%d k\n",
809 bootprog_name, bootprog_rev, bootprog_kernrev,
810 getbasemem(), getextmem());
811 }
812
813 void
814 command_memmap(char *arg)
815 {
816 bool sorted = true;
817 bool compact = false;
818
819 if (*arg == '\0' || strcmp(arg, "sorted") == 0)
820 /* Already sorted is true. */;
821 else if (strcmp(arg, "unsorted") == 0)
822 sorted = false;
823 else if (strcmp(arg, "compact") == 0)
824 compact = true;
825 else {
826 printf("invalid flag, "
827 "must be 'sorted', 'unsorted' or 'compact'.\n");
828 return;
829 }
830
831 efi_memory_show_map(sorted, compact);
832 }
833
834 void
835 command_devpath(char *arg)
836 {
837 EFI_STATUS status;
838 UINTN i, nhandles;
839 EFI_HANDLE *handles;
840 EFI_DEVICE_PATH *dp0, *dp;
841 CHAR16 *path;
842 char *upath;
843 UINTN cols, rows, row = 0;
844 int rv;
845
846 status = uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut,
847 ST->ConOut->Mode->Mode, &cols, &rows);
848 if (EFI_ERROR(status) || rows <= 2)
849 rows = 0;
850 else
851 rows -= 2;
852
853 /*
854 * all devices.
855 */
856 status = LibLocateHandle(ByProtocol, &DevicePathProtocol, NULL,
857 &nhandles, &handles);
858 if (EFI_ERROR(status))
859 return;
860
861 for (i = 0; i < nhandles; i++) {
862 status = uefi_call_wrapper(BS->HandleProtocol, 3, handles[i],
863 &DevicePathProtocol, (void **)&dp0);
864 if (EFI_ERROR(status))
865 break;
866
867 printf("DevicePathType %d\n", DevicePathType(dp0));
868 if (++row >= rows) {
869 row = 0;
870 printf("Press Any Key to continue :");
871 (void) awaitkey(-1, 0);
872 printf("\n");
873 }
874 for (dp = dp0;
875 !IsDevicePathEnd(dp);
876 dp = NextDevicePathNode(dp)) {
877
878 path = DevicePathToStr(dp);
879 upath = NULL;
880 rv = ucs2_to_utf8(path, &upath);
881 FreePool(path);
882 if (rv) {
883 printf("convert failed\n");
884 break;
885 }
886
887 printf("%d:%d:%s\n", DevicePathType(dp),
888 DevicePathSubType(dp), upath);
889 FreePool(upath);
890
891 if (++row >= rows) {
892 row = 0;
893 printf("Press Any Key to continue :");
894 (void) awaitkey(-1, 0);
895 printf("\n");
896 }
897 }
898 }
899 }
900
901
902 void
903 command_efivar(char *arg)
904 {
905 static const char header[] =
906 "GUID Variable Name Value\n"
907 "==================================== ==================== ========\n";
908 EFI_STATUS status;
909 UINTN sz = 64, osz;
910 CHAR16 *name = NULL, *tmp, *val, guid[128];
911 char *uname, *uval, *uguid;
912 EFI_GUID vendor;
913 UINTN cols, rows, row = 0;
914 int rv;
915
916 status = uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut,
917 ST->ConOut->Mode->Mode, &cols, &rows);
918 if (EFI_ERROR(status) || rows <= 2)
919 rows = 0;
920 else
921 rows -= 2;
922
923 name = AllocatePool(sz);
924 if (name == NULL) {
925 printf("memory allocation failed: %" PRIuMAX" bytes\n",
926 (uintmax_t)sz);
927 return;
928 }
929
930 SetMem(name, sz, 0);
931 vendor = NullGuid;
932
933 printf("%s", header);
934 for (;;) {
935 osz = sz;
936 status = uefi_call_wrapper(RT->GetNextVariableName, 3,
937 &sz, name, &vendor);
938 if (EFI_ERROR(status)) {
939 if (status == EFI_NOT_FOUND)
940 break;
941 if (status != EFI_BUFFER_TOO_SMALL) {
942 printf("GetNextVariableName failed: %" PRIxMAX "\n",
943 (uintmax_t)status);
944 break;
945 }
946
947 tmp = AllocatePool(sz);
948 if (tmp == NULL) {
949 printf("memory allocation failed: %" PRIuMAX
950 "bytes\n", (uintmax_t)sz);
951 break;
952 }
953 SetMem(tmp, sz, 0);
954 CopyMem(tmp, name, osz);
955 FreePool(name);
956 name = tmp;
957 continue;
958 }
959
960 val = LibGetVariable(name, &vendor);
961 if (val != NULL) {
962 uval = NULL;
963 rv = ucs2_to_utf8(val, &uval);
964 FreePool(val);
965 if (rv) {
966 printf("value convert failed\n");
967 break;
968 }
969 } else
970 uval = NULL;
971 uname = NULL;
972 rv = ucs2_to_utf8(name, &uname);
973 if (rv) {
974 printf("name convert failed\n");
975 FreePool(uval);
976 break;
977 }
978 GuidToString(guid, &vendor);
979 uguid = NULL;
980 rv = ucs2_to_utf8(guid, &uguid);
981 if (rv) {
982 printf("GUID convert failed\n");
983 FreePool(uval);
984 FreePool(uname);
985 break;
986 }
987 printf("%-35s %-20s %s\n", uguid, uname, uval ? uval : "(null)");
988 FreePool(uguid);
989 FreePool(uname);
990 if (uval != NULL)
991 FreePool(uval);
992
993 if (++row >= rows) {
994 row = 0;
995 printf("Press Any Key to continue :");
996 (void) awaitkey(-1, 0);
997 printf("\n");
998 }
999 }
1000
1001 FreePool(name);
1002 }
1003