]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/boot/efi/loader/main.c
MFC Loader Fixes 2017q1: r311458,r312237,r312314,r312374,r312947,r313042,
[FreeBSD/FreeBSD.git] / sys / boot / efi / loader / main.c
1 /*-
2  * Copyright (c) 2008-2010 Rui Paulo
3  * Copyright (c) 2006 Marcel Moolenaar
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
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 AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 #include <sys/param.h>
32 #include <sys/reboot.h>
33 #include <sys/boot.h>
34 #include <inttypes.h>
35 #include <stand.h>
36 #include <string.h>
37 #include <setjmp.h>
38 #include <disk.h>
39
40 #include <efi.h>
41 #include <efilib.h>
42
43 #include <uuid.h>
44
45 #include <bootstrap.h>
46 #include <smbios.h>
47
48 #ifdef EFI_ZFS_BOOT
49 #include <libzfs.h>
50 #endif
51
52 #include "loader_efi.h"
53
54 extern char bootprog_info[];
55
56 struct arch_switch archsw;      /* MI/MD interface boundary */
57
58 EFI_GUID acpi = ACPI_TABLE_GUID;
59 EFI_GUID acpi20 = ACPI_20_TABLE_GUID;
60 EFI_GUID devid = DEVICE_PATH_PROTOCOL;
61 EFI_GUID imgid = LOADED_IMAGE_PROTOCOL;
62 EFI_GUID mps = MPS_TABLE_GUID;
63 EFI_GUID netid = EFI_SIMPLE_NETWORK_PROTOCOL;
64 EFI_GUID smbios = SMBIOS_TABLE_GUID;
65 EFI_GUID dxe = DXE_SERVICES_TABLE_GUID;
66 EFI_GUID hoblist = HOB_LIST_TABLE_GUID;
67 EFI_GUID memtype = MEMORY_TYPE_INFORMATION_TABLE_GUID;
68 EFI_GUID debugimg = DEBUG_IMAGE_INFO_TABLE_GUID;
69 EFI_GUID fdtdtb = FDT_TABLE_GUID;
70 EFI_GUID inputid = SIMPLE_TEXT_INPUT_PROTOCOL;
71
72 #ifdef EFI_ZFS_BOOT
73 static void efi_zfs_probe(void);
74 static uint64_t pool_guid;
75 #endif
76
77 static int
78 has_keyboard(void)
79 {
80         EFI_STATUS status;
81         EFI_DEVICE_PATH *path;
82         EFI_HANDLE *hin, *hin_end, *walker;
83         UINTN sz;
84         int retval = 0;
85         
86         /*
87          * Find all the handles that support the SIMPLE_TEXT_INPUT_PROTOCOL and
88          * do the typical dance to get the right sized buffer.
89          */
90         sz = 0;
91         hin = NULL;
92         status = BS->LocateHandle(ByProtocol, &inputid, 0, &sz, 0);
93         if (status == EFI_BUFFER_TOO_SMALL) {
94                 hin = (EFI_HANDLE *)malloc(sz);
95                 status = BS->LocateHandle(ByProtocol, &inputid, 0, &sz,
96                     hin);
97                 if (EFI_ERROR(status))
98                         free(hin);
99         }
100         if (EFI_ERROR(status))
101                 return retval;
102
103         /*
104          * Look at each of the handles. If it supports the device path protocol,
105          * use it to get the device path for this handle. Then see if that
106          * device path matches either the USB device path for keyboards or the
107          * legacy device path for keyboards.
108          */
109         hin_end = &hin[sz / sizeof(*hin)];
110         for (walker = hin; walker < hin_end; walker++) {
111                 status = BS->HandleProtocol(*walker, &devid, (VOID **)&path);
112                 if (EFI_ERROR(status))
113                         continue;
114
115                 while (!IsDevicePathEnd(path)) {
116                         /*
117                          * Check for the ACPI keyboard node. All PNP3xx nodes
118                          * are keyboards of different flavors. Note: It is
119                          * unclear of there's always a keyboard node when
120                          * there's a keyboard controller, or if there's only one
121                          * when a keyboard is detected at boot.
122                          */
123                         if (DevicePathType(path) == ACPI_DEVICE_PATH &&
124                             (DevicePathSubType(path) == ACPI_DP ||
125                                 DevicePathSubType(path) == ACPI_EXTENDED_DP)) {
126                                 ACPI_HID_DEVICE_PATH  *acpi;
127
128                                 acpi = (ACPI_HID_DEVICE_PATH *)(void *)path;
129                                 if ((EISA_ID_TO_NUM(acpi->HID) & 0xff00) == 0x300 &&
130                                     (acpi->HID & 0xffff) == PNP_EISA_ID_CONST) {
131                                         retval = 1;
132                                         goto out;
133                                 }
134                         /*
135                          * Check for USB keyboard node, if present. Unlike a
136                          * PS/2 keyboard, these definitely only appear when
137                          * connected to the system.
138                          */
139                         } else if (DevicePathType(path) == MESSAGING_DEVICE_PATH &&
140                             DevicePathSubType(path) == MSG_USB_CLASS_DP) {
141                                 USB_CLASS_DEVICE_PATH *usb;
142                                
143                                 usb = (USB_CLASS_DEVICE_PATH *)(void *)path;
144                                 if (usb->DeviceClass == 3 && /* HID */
145                                     usb->DeviceSubClass == 1 && /* Boot devices */
146                                     usb->DeviceProtocol == 1) { /* Boot keyboards */
147                                         retval = 1;
148                                         goto out;
149                                 }
150                         }
151                         path = NextDevicePathNode(path);
152                 }
153         }
154 out:
155         free(hin);
156         return retval;
157 }
158
159 static void
160 set_devdesc_currdev(struct devsw *dev, int unit)
161 {
162         struct devdesc currdev;
163         char *devname;
164
165         currdev.d_dev = dev;
166         currdev.d_type = currdev.d_dev->dv_type;
167         currdev.d_unit = unit;
168         currdev.d_opendata = NULL;
169         devname = efi_fmtdev(&currdev);
170
171         env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev,
172             env_nounset);
173         env_setenv("loaddev", EV_VOLATILE, devname, env_noset, env_nounset);
174 }
175
176 static int
177 find_currdev(EFI_LOADED_IMAGE *img)
178 {
179         pdinfo_list_t *pdi_list;
180         pdinfo_t *dp, *pp;
181         EFI_DEVICE_PATH *devpath, *copy;
182         EFI_HANDLE h;
183         char *devname;
184         struct devsw *dev;
185         int unit;
186         uint64_t extra;
187
188 #ifdef EFI_ZFS_BOOT
189         /* Did efi_zfs_probe() detect the boot pool? */
190         if (pool_guid != 0) {
191                 struct zfs_devdesc currdev;
192
193                 currdev.d_dev = &zfs_dev;
194                 currdev.d_unit = 0;
195                 currdev.d_type = currdev.d_dev->dv_type;
196                 currdev.d_opendata = NULL;
197                 currdev.pool_guid = pool_guid;
198                 currdev.root_guid = 0;
199                 devname = efi_fmtdev(&currdev);
200
201                 env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev,
202                     env_nounset);
203                 env_setenv("loaddev", EV_VOLATILE, devname, env_noset,
204                     env_nounset);
205                 init_zfs_bootenv(devname);
206                 return (0);
207         }
208 #endif /* EFI_ZFS_BOOT */
209
210         /* We have device lists for hd, cd, fd, walk them all. */
211         pdi_list = efiblk_get_pdinfo_list(&efipart_hddev);
212         STAILQ_FOREACH(dp, pdi_list, pd_link) {
213                 struct disk_devdesc currdev;
214
215                 currdev.d_dev = &efipart_hddev;
216                 currdev.d_type = currdev.d_dev->dv_type;
217                 currdev.d_unit = dp->pd_unit;
218                 currdev.d_opendata = NULL;
219                 currdev.d_slice = -1;
220                 currdev.d_partition = -1;
221
222                 if (dp->pd_handle == img->DeviceHandle) {
223                         devname = efi_fmtdev(&currdev);
224
225                         env_setenv("currdev", EV_VOLATILE, devname,
226                             efi_setcurrdev, env_nounset);
227                         env_setenv("loaddev", EV_VOLATILE, devname,
228                             env_noset, env_nounset);
229                         return (0);
230                 }
231                 /* Assuming GPT partitioning. */
232                 STAILQ_FOREACH(pp, &dp->pd_part, pd_link) {
233                         if (pp->pd_handle == img->DeviceHandle) {
234                                 currdev.d_slice = pp->pd_unit;
235                                 currdev.d_partition = 255;
236                                 devname = efi_fmtdev(&currdev);
237
238                                 env_setenv("currdev", EV_VOLATILE, devname,
239                                     efi_setcurrdev, env_nounset);
240                                 env_setenv("loaddev", EV_VOLATILE, devname,
241                                     env_noset, env_nounset);
242                                 return (0);
243                         }
244                 }
245         }
246
247         pdi_list = efiblk_get_pdinfo_list(&efipart_cddev);
248         STAILQ_FOREACH(dp, pdi_list, pd_link) {
249                 if (dp->pd_handle == img->DeviceHandle ||
250                     dp->pd_alias == img->DeviceHandle) {
251                         set_devdesc_currdev(&efipart_cddev, dp->pd_unit);
252                         return (0);
253                 }
254         }
255
256         pdi_list = efiblk_get_pdinfo_list(&efipart_fddev);
257         STAILQ_FOREACH(dp, pdi_list, pd_link) {
258                 if (dp->pd_handle == img->DeviceHandle) {
259                         set_devdesc_currdev(&efipart_fddev, dp->pd_unit);
260                         return (0);
261                 }
262         }
263
264         /*
265          * Try the device handle from our loaded image first.  If that
266          * fails, use the device path from the loaded image and see if
267          * any of the nodes in that path match one of the enumerated
268          * handles.
269          */
270         if (efi_handle_lookup(img->DeviceHandle, &dev, &unit, &extra) == 0) {
271                 set_devdesc_currdev(dev, unit);
272                 return (0);
273         }
274
275         copy = NULL;
276         devpath = efi_lookup_image_devpath(IH);
277         while (devpath != NULL) {
278                 h = efi_devpath_handle(devpath);
279                 if (h == NULL)
280                         break;
281
282                 free(copy);
283                 copy = NULL;
284
285                 if (efi_handle_lookup(h, &dev, &unit, &extra) == 0) {
286                         set_devdesc_currdev(dev, unit);
287                         return (0);
288                 }
289
290                 devpath = efi_lookup_devpath(h);
291                 if (devpath != NULL) {
292                         copy = efi_devpath_trim(devpath);
293                         devpath = copy;
294                 }
295         }
296         free(copy);
297
298         return (ENOENT);
299 }
300
301 EFI_STATUS
302 main(int argc, CHAR16 *argv[])
303 {
304         char var[128];
305         EFI_LOADED_IMAGE *img;
306         EFI_GUID *guid;
307         int i, j, vargood, howto;
308         UINTN k;
309         int has_kbd;
310         char buf[40];
311
312         archsw.arch_autoload = efi_autoload;
313         archsw.arch_getdev = efi_getdev;
314         archsw.arch_copyin = efi_copyin;
315         archsw.arch_copyout = efi_copyout;
316         archsw.arch_readin = efi_readin;
317 #ifdef EFI_ZFS_BOOT
318         /* Note this needs to be set before ZFS init. */
319         archsw.arch_zfs_probe = efi_zfs_probe;
320 #endif
321
322         /* Init the time source */
323         efi_time_init();
324
325         has_kbd = has_keyboard();
326
327         /*
328          * XXX Chicken-and-egg problem; we want to have console output
329          * early, but some console attributes may depend on reading from
330          * eg. the boot device, which we can't do yet.  We can use
331          * printf() etc. once this is done.
332          */
333         cons_probe();
334
335         /*
336          * Initialise the block cache. Set the upper limit.
337          */
338         bcache_init(32768, 512);
339
340         /*
341          * Parse the args to set the console settings, etc
342          * boot1.efi passes these in, if it can read /boot.config or /boot/config
343          * or iPXE may be setup to pass these in.
344          *
345          * Loop through the args, and for each one that contains an '=' that is
346          * not the first character, add it to the environment.  This allows
347          * loader and kernel env vars to be passed on the command line.  Convert
348          * args from UCS-2 to ASCII (16 to 8 bit) as they are copied.
349          */
350         howto = 0;
351         for (i = 1; i < argc; i++) {
352                 if (argv[i][0] == '-') {
353                         for (j = 1; argv[i][j] != 0; j++) {
354                                 int ch;
355
356                                 ch = argv[i][j];
357                                 switch (ch) {
358                                 case 'a':
359                                         howto |= RB_ASKNAME;
360                                         break;
361                                 case 'd':
362                                         howto |= RB_KDB;
363                                         break;
364                                 case 'D':
365                                         howto |= RB_MULTIPLE;
366                                         break;
367                                 case 'h':
368                                         howto |= RB_SERIAL;
369                                         break;
370                                 case 'm':
371                                         howto |= RB_MUTE;
372                                         break;
373                                 case 'p':
374                                         howto |= RB_PAUSE;
375                                         break;
376                                 case 'P':
377                                         if (!has_kbd)
378                                                 howto |= RB_SERIAL | RB_MULTIPLE;
379                                         break;
380                                 case 'r':
381                                         howto |= RB_DFLTROOT;
382                                         break;
383                                 case 's':
384                                         howto |= RB_SINGLE;
385                                         break;
386                                 case 'S':
387                                         if (argv[i][j + 1] == 0) {
388                                                 if (i + 1 == argc) {
389                                                         setenv("comconsole_speed", "115200", 1);
390                                                 } else {
391                                                         cpy16to8(&argv[i + 1][0], var,
392                                                             sizeof(var));
393                                                         setenv("comconsole_speed", var, 1);
394                                                 }
395                                                 i++;
396                                                 break;
397                                         } else {
398                                                 cpy16to8(&argv[i][j + 1], var,
399                                                     sizeof(var));
400                                                 setenv("comconsole_speed", var, 1);
401                                                 break;
402                                         }
403                                 case 'v':
404                                         howto |= RB_VERBOSE;
405                                         break;
406                                 }
407                         }
408                 } else {
409                         vargood = 0;
410                         for (j = 0; argv[i][j] != 0; j++) {
411                                 if (j == sizeof(var)) {
412                                         vargood = 0;
413                                         break;
414                                 }
415                                 if (j > 0 && argv[i][j] == '=')
416                                         vargood = 1;
417                                 var[j] = (char)argv[i][j];
418                         }
419                         if (vargood) {
420                                 var[j] = 0;
421                                 putenv(var);
422                         }
423                 }
424         }
425         for (i = 0; howto_names[i].ev != NULL; i++)
426                 if (howto & howto_names[i].mask)
427                         setenv(howto_names[i].ev, "YES", 1);
428         if (howto & RB_MULTIPLE) {
429                 if (howto & RB_SERIAL)
430                         setenv("console", "comconsole efi" , 1);
431                 else
432                         setenv("console", "efi comconsole" , 1);
433         } else if (howto & RB_SERIAL) {
434                 setenv("console", "comconsole" , 1);
435         }
436
437         if (efi_copy_init()) {
438                 printf("failed to allocate staging area\n");
439                 return (EFI_BUFFER_TOO_SMALL);
440         }
441
442         /*
443          * March through the device switch probing for things.
444          */
445         for (i = 0; devsw[i] != NULL; i++)
446                 if (devsw[i]->dv_init != NULL)
447                         (devsw[i]->dv_init)();
448
449         /* Get our loaded image protocol interface structure. */
450         BS->HandleProtocol(IH, &imgid, (VOID**)&img);
451
452         printf("Command line arguments:");
453         for (i = 0; i < argc; i++)
454                 printf(" %S", argv[i]);
455         printf("\n");
456
457         printf("Image base: 0x%lx\n", (u_long)img->ImageBase);
458         printf("EFI version: %d.%02d\n", ST->Hdr.Revision >> 16,
459             ST->Hdr.Revision & 0xffff);
460         printf("EFI Firmware: %S (rev %d.%02d)\n", ST->FirmwareVendor,
461             ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
462
463         printf("\n%s", bootprog_info);
464
465         /*
466          * Disable the watchdog timer. By default the boot manager sets
467          * the timer to 5 minutes before invoking a boot option. If we
468          * want to return to the boot manager, we have to disable the
469          * watchdog timer and since we're an interactive program, we don't
470          * want to wait until the user types "quit". The timer may have
471          * fired by then. We don't care if this fails. It does not prevent
472          * normal functioning in any way...
473          */
474         BS->SetWatchdogTimer(0, 0, 0, NULL);
475
476         if (find_currdev(img) != 0)
477                 return (EFI_NOT_FOUND);
478
479         efi_init_environment();
480         setenv("LINES", "24", 1);       /* optional */
481
482         for (k = 0; k < ST->NumberOfTableEntries; k++) {
483                 guid = &ST->ConfigurationTable[k].VendorGuid;
484                 if (!memcmp(guid, &smbios, sizeof(EFI_GUID))) {
485                         snprintf(buf, sizeof(buf), "%p",
486                             ST->ConfigurationTable[k].VendorTable);
487                         setenv("hint.smbios.0.mem", buf, 1);
488                         smbios_detect(ST->ConfigurationTable[k].VendorTable);
489                         break;
490                 }
491         }
492
493         interact(NULL);                 /* doesn't return */
494
495         return (EFI_SUCCESS);           /* keep compiler happy */
496 }
497
498 COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot);
499
500 static int
501 command_reboot(int argc, char *argv[])
502 {
503         int i;
504
505         for (i = 0; devsw[i] != NULL; ++i)
506                 if (devsw[i]->dv_cleanup != NULL)
507                         (devsw[i]->dv_cleanup)();
508
509         RS->ResetSystem(EfiResetCold, EFI_SUCCESS, 23,
510             (CHAR16 *)"Reboot from the loader");
511
512         /* NOTREACHED */
513         return (CMD_ERROR);
514 }
515
516 COMMAND_SET(quit, "quit", "exit the loader", command_quit);
517
518 static int
519 command_quit(int argc, char *argv[])
520 {
521         exit(0);
522         return (CMD_OK);
523 }
524
525 COMMAND_SET(memmap, "memmap", "print memory map", command_memmap);
526
527 static int
528 command_memmap(int argc, char *argv[])
529 {
530         UINTN sz;
531         EFI_MEMORY_DESCRIPTOR *map, *p;
532         UINTN key, dsz;
533         UINT32 dver;
534         EFI_STATUS status;
535         int i, ndesc;
536         char line[80];
537         static char *types[] = {
538             "Reserved",
539             "LoaderCode",
540             "LoaderData",
541             "BootServicesCode",
542             "BootServicesData",
543             "RuntimeServicesCode",
544             "RuntimeServicesData",
545             "ConventionalMemory",
546             "UnusableMemory",
547             "ACPIReclaimMemory",
548             "ACPIMemoryNVS",
549             "MemoryMappedIO",
550             "MemoryMappedIOPortSpace",
551             "PalCode"
552         };
553
554         sz = 0;
555         status = BS->GetMemoryMap(&sz, 0, &key, &dsz, &dver);
556         if (status != EFI_BUFFER_TOO_SMALL) {
557                 printf("Can't determine memory map size\n");
558                 return (CMD_ERROR);
559         }
560         map = malloc(sz);
561         status = BS->GetMemoryMap(&sz, map, &key, &dsz, &dver);
562         if (EFI_ERROR(status)) {
563                 printf("Can't read memory map\n");
564                 return (CMD_ERROR);
565         }
566
567         ndesc = sz / dsz;
568         snprintf(line, sizeof(line), "%23s %12s %12s %8s %4s\n",
569             "Type", "Physical", "Virtual", "#Pages", "Attr");
570         pager_open();
571         if (pager_output(line)) {
572                 pager_close();
573                 return (CMD_OK);
574         }
575
576         for (i = 0, p = map; i < ndesc;
577              i++, p = NextMemoryDescriptor(p, dsz)) {
578                 printf("%23s %012jx %012jx %08jx ", types[p->Type],
579                     (uintmax_t)p->PhysicalStart, (uintmax_t)p->VirtualStart,
580                     (uintmax_t)p->NumberOfPages);
581                 if (p->Attribute & EFI_MEMORY_UC)
582                         printf("UC ");
583                 if (p->Attribute & EFI_MEMORY_WC)
584                         printf("WC ");
585                 if (p->Attribute & EFI_MEMORY_WT)
586                         printf("WT ");
587                 if (p->Attribute & EFI_MEMORY_WB)
588                         printf("WB ");
589                 if (p->Attribute & EFI_MEMORY_UCE)
590                         printf("UCE ");
591                 if (p->Attribute & EFI_MEMORY_WP)
592                         printf("WP ");
593                 if (p->Attribute & EFI_MEMORY_RP)
594                         printf("RP ");
595                 if (p->Attribute & EFI_MEMORY_XP)
596                         printf("XP ");
597                 if (pager_output("\n"))
598                         break;
599         }
600
601         pager_close();
602         return (CMD_OK);
603 }
604
605 COMMAND_SET(configuration, "configuration", "print configuration tables",
606     command_configuration);
607
608 static const char *
609 guid_to_string(EFI_GUID *guid)
610 {
611         static char buf[40];
612
613         sprintf(buf, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
614             guid->Data1, guid->Data2, guid->Data3, guid->Data4[0],
615             guid->Data4[1], guid->Data4[2], guid->Data4[3], guid->Data4[4],
616             guid->Data4[5], guid->Data4[6], guid->Data4[7]);
617         return (buf);
618 }
619
620 static int
621 command_configuration(int argc, char *argv[])
622 {
623         char line[80];
624         UINTN i;
625
626         snprintf(line, sizeof(line), "NumberOfTableEntries=%lu\n",
627                 (unsigned long)ST->NumberOfTableEntries);
628         pager_open();
629         if (pager_output(line)) {
630                 pager_close();
631                 return (CMD_OK);
632         }
633
634         for (i = 0; i < ST->NumberOfTableEntries; i++) {
635                 EFI_GUID *guid;
636
637                 printf("  ");
638                 guid = &ST->ConfigurationTable[i].VendorGuid;
639                 if (!memcmp(guid, &mps, sizeof(EFI_GUID)))
640                         printf("MPS Table");
641                 else if (!memcmp(guid, &acpi, sizeof(EFI_GUID)))
642                         printf("ACPI Table");
643                 else if (!memcmp(guid, &acpi20, sizeof(EFI_GUID)))
644                         printf("ACPI 2.0 Table");
645                 else if (!memcmp(guid, &smbios, sizeof(EFI_GUID)))
646                         printf("SMBIOS Table %p",
647                             ST->ConfigurationTable[i].VendorTable);
648                 else if (!memcmp(guid, &dxe, sizeof(EFI_GUID)))
649                         printf("DXE Table");
650                 else if (!memcmp(guid, &hoblist, sizeof(EFI_GUID)))
651                         printf("HOB List Table");
652                 else if (!memcmp(guid, &memtype, sizeof(EFI_GUID)))
653                         printf("Memory Type Information Table");
654                 else if (!memcmp(guid, &debugimg, sizeof(EFI_GUID)))
655                         printf("Debug Image Info Table");
656                 else if (!memcmp(guid, &fdtdtb, sizeof(EFI_GUID)))
657                         printf("FDT Table");
658                 else
659                         printf("Unknown Table (%s)", guid_to_string(guid));
660                 snprintf(line, sizeof(line), " at %p\n",
661                     ST->ConfigurationTable[i].VendorTable);
662                 if (pager_output(line))
663                         break;
664         }
665
666         pager_close();
667         return (CMD_OK);
668 }
669
670
671 COMMAND_SET(mode, "mode", "change or display EFI text modes", command_mode);
672
673 static int
674 command_mode(int argc, char *argv[])
675 {
676         UINTN cols, rows;
677         unsigned int mode;
678         int i;
679         char *cp;
680         char rowenv[8];
681         EFI_STATUS status;
682         SIMPLE_TEXT_OUTPUT_INTERFACE *conout;
683         extern void HO(void);
684
685         conout = ST->ConOut;
686
687         if (argc > 1) {
688                 mode = strtol(argv[1], &cp, 0);
689                 if (cp[0] != '\0') {
690                         printf("Invalid mode\n");
691                         return (CMD_ERROR);
692                 }
693                 status = conout->QueryMode(conout, mode, &cols, &rows);
694                 if (EFI_ERROR(status)) {
695                         printf("invalid mode %d\n", mode);
696                         return (CMD_ERROR);
697                 }
698                 status = conout->SetMode(conout, mode);
699                 if (EFI_ERROR(status)) {
700                         printf("couldn't set mode %d\n", mode);
701                         return (CMD_ERROR);
702                 }
703                 sprintf(rowenv, "%u", (unsigned)rows);
704                 setenv("LINES", rowenv, 1);
705                 HO();           /* set cursor */
706                 return (CMD_OK);
707         }
708
709         printf("Current mode: %d\n", conout->Mode->Mode);
710         for (i = 0; i <= conout->Mode->MaxMode; i++) {
711                 status = conout->QueryMode(conout, i, &cols, &rows);
712                 if (EFI_ERROR(status))
713                         continue;
714                 printf("Mode %d: %u columns, %u rows\n", i, (unsigned)cols,
715                     (unsigned)rows);
716         }
717
718         if (i != 0)
719                 printf("Select a mode with the command \"mode <number>\"\n");
720
721         return (CMD_OK);
722 }
723
724 #ifdef EFI_ZFS_BOOT
725 COMMAND_SET(lszfs, "lszfs", "list child datasets of a zfs dataset",
726     command_lszfs);
727
728 static int
729 command_lszfs(int argc, char *argv[])
730 {
731         int err;
732
733         if (argc != 2) {
734                 command_errmsg = "wrong number of arguments";
735                 return (CMD_ERROR);
736         }
737
738         err = zfs_list(argv[1]);
739         if (err != 0) {
740                 command_errmsg = strerror(err);
741                 return (CMD_ERROR);
742         }
743         return (CMD_OK);
744 }
745
746 COMMAND_SET(reloadbe, "reloadbe", "refresh the list of ZFS Boot Environments",
747             command_reloadbe);
748
749 static int
750 command_reloadbe(int argc, char *argv[])
751 {
752         int err;
753         char *root;
754
755         if (argc > 2) {
756                 command_errmsg = "wrong number of arguments";
757                 return (CMD_ERROR);
758         }
759
760         if (argc == 2) {
761                 err = zfs_bootenv(argv[1]);
762         } else {
763                 root = getenv("zfs_be_root");
764                 if (root == NULL) {
765                         return (CMD_OK);
766                 }
767                 err = zfs_bootenv(root);
768         }
769
770         if (err != 0) {
771                 command_errmsg = strerror(err);
772                 return (CMD_ERROR);
773         }
774
775         return (CMD_OK);
776 }
777 #endif
778
779 #ifdef LOADER_FDT_SUPPORT
780 extern int command_fdt_internal(int argc, char *argv[]);
781
782 /*
783  * Since proper fdt command handling function is defined in fdt_loader_cmd.c,
784  * and declaring it as extern is in contradiction with COMMAND_SET() macro
785  * (which uses static pointer), we're defining wrapper function, which
786  * calls the proper fdt handling routine.
787  */
788 static int
789 command_fdt(int argc, char *argv[])
790 {
791
792         return (command_fdt_internal(argc, argv));
793 }
794
795 COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt);
796 #endif
797
798 #ifdef EFI_ZFS_BOOT
799 static void
800 efi_zfs_probe(void)
801 {
802         pdinfo_list_t *hdi;
803         pdinfo_t *hd, *pd = NULL;
804         EFI_GUID imgid = LOADED_IMAGE_PROTOCOL;
805         EFI_LOADED_IMAGE *img;
806         EFI_HANDLE boot_disk = NULL;
807         char devname[SPECNAMELEN + 1];
808         uint64_t *guidp = NULL;
809
810         BS->HandleProtocol(IH, &imgid, (VOID**)&img);
811
812         /* Find the handle for the boot disk. */
813         hdi = efiblk_get_pdinfo_list(&efipart_hddev);
814         STAILQ_FOREACH(hd, hdi, pd_link) {
815                 STAILQ_FOREACH(pd, &hd->pd_part, pd_link) {
816                         if (pd->pd_handle == img->DeviceHandle)
817                                 boot_disk = hd->pd_handle;
818                 }
819         }
820
821         /*
822          * We provide non-NULL guid pointer if the disk was used for boot,
823          * and reset after the first found pool.
824          * Technically this solution is not very correct, we assume the boot
825          * pool is the first pool on this disk.
826          */
827
828         STAILQ_FOREACH(hd, hdi, pd_link) {
829                 if (hd->pd_handle == boot_disk)
830                         guidp = &pool_guid;
831
832                 STAILQ_FOREACH(pd, &hd->pd_part, pd_link) {
833                         snprintf(devname, sizeof(devname), "%s%dp%d:",
834                             efipart_hddev.dv_name, hd->pd_unit, pd->pd_unit);
835                         (void) zfs_probe_dev(devname, guidp);
836                         if (guidp != NULL && pool_guid != 0)
837                                 guidp = NULL;
838                 }
839         }
840 }
841 #endif