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