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