blob: 22fd9e9b627be9ac7d38cf5e772aaf3bf08791f6 [file] [log] [blame]
Toomas Soome199767f2015-10-25 00:06:51 +03001/*-
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>
Toomas Soome199767f2015-10-25 00:06:51 +030029
Toomas Soomeedb35042016-11-07 15:37:12 +020030#include <sys/disk.h>
Toomas Soome199767f2015-10-25 00:06:51 +030031#include <sys/param.h>
32#include <sys/reboot.h>
33#include <sys/boot.h>
34#include <stand.h>
Toomas Soomeeee59042016-11-12 00:06:58 +020035#include <inttypes.h>
Toomas Soome199767f2015-10-25 00:06:51 +030036#include <string.h>
37#include <setjmp.h>
38
39#include <efi.h>
40#include <efilib.h>
41#include <efigpt.h>
42
Toomas Soomeeee59042016-11-12 00:06:58 +020043#include <uuid.h>
44#include <stdbool.h>
45
Toomas Soome199767f2015-10-25 00:06:51 +030046#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
Toomas Soomef6c94442017-01-03 11:24:09 +020055extern char bootprog_info[];
Toomas Soome199767f2015-10-25 00:06:51 +030056
57struct arch_switch archsw; /* MI/MD interface boundary */
58
Toomas Soome199767f2015-10-25 00:06:51 +030059EFI_GUID devid = DEVICE_PATH_PROTOCOL;
60EFI_GUID imgid = LOADED_IMAGE_PROTOCOL;
Toomas Soome199767f2015-10-25 00:06:51 +030061EFI_GUID smbios = SMBIOS_TABLE_GUID;
62EFI_GUID smbios3 = SMBIOS3_TABLE_GUID;
Toomas Soome199767f2015-10-25 00:06:51 +030063EFI_GUID inputid = SIMPLE_TEXT_INPUT_PROTOCOL;
64EFI_GUID serial_io = SERIAL_IO_PROTOCOL;
65
Toomas Soomedcba96f2016-10-09 17:12:23 +030066extern void acpi_detect(void);
Toomas Soome199767f2015-10-25 00:06:51 +030067void efi_serial_init(void);
68#ifdef EFI_ZFS_BOOT
69static void efi_zfs_probe(void);
70#endif
71
Toomas Soome199767f2015-10-25 00:06:51 +030072static int
73has_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 }
149out:
150 free(hin);
151 return retval;
152}
153
Toomas Soomeab8c1d42016-11-12 13:46:26 +0200154static int
155find_currdev(EFI_LOADED_IMAGE *img, struct devsw **dev, int *unit,
156 uint64_t *extra)
157{
158 EFI_DEVICE_PATH *devpath, *copy;
159 EFI_HANDLE h;
160
161 /*
162 * Try the device handle from our loaded image first. If that
163 * fails, use the device path from the loaded image and see if
164 * any of the nodes in that path match one of the enumerated
165 * handles.
166 */
167 if (efi_handle_lookup(img->DeviceHandle, dev, unit, extra) == 0)
168 return (0);
169
170 copy = NULL;
171 devpath = efi_lookup_image_devpath(IH);
172 while (devpath != NULL) {
173 h = efi_devpath_handle(devpath);
174 if (h == NULL)
175 break;
176
177 free(copy);
178 copy = NULL;
179
180 if (efi_handle_lookup(h, dev, unit, extra) == 0)
181 return (0);
182
183 devpath = efi_lookup_devpath(h);
184 if (devpath != NULL) {
185 copy = efi_devpath_trim(devpath);
186 devpath = copy;
187 }
188 }
189 free(copy);
190
191 return (ENOENT);
192}
193
Toomas Soome199767f2015-10-25 00:06:51 +0300194EFI_STATUS
195main(int argc, CHAR16 *argv[])
196{
197 char var[128];
198 EFI_LOADED_IMAGE *img;
199 EFI_GUID *guid;
200 int i, j, vargood, unit, howto;
201 struct devsw *dev;
202 uint64_t pool_guid;
Toomas Soomedcba96f2016-10-09 17:12:23 +0300203 void *ptr;
Toomas Soome199767f2015-10-25 00:06:51 +0300204 UINTN k;
205 int has_kbd;
206
207 archsw.arch_autoload = efi_autoload;
208 archsw.arch_getdev = efi_getdev;
209 archsw.arch_copyin = efi_copyin;
210 archsw.arch_copyout = efi_copyout;
211 archsw.arch_readin = efi_readin;
212#ifdef EFI_ZFS_BOOT
213 /* Note this needs to be set before ZFS init. */
214 archsw.arch_zfs_probe = efi_zfs_probe;
215#endif
216
Toomas Soome04f8e092016-11-10 21:05:15 +0200217 /* Init the time source */
218 efi_time_init();
219
Toomas Soome199767f2015-10-25 00:06:51 +0300220 has_kbd = has_keyboard();
221
222 /*
223 * XXX Chicken-and-egg problem; we want to have console output
224 * early, but some console attributes may depend on reading from
225 * eg. the boot device, which we can't do yet. We can use
226 * printf() etc. once this is done.
227 */
228 cons_probe();
229
230 /*
231 * Initialise the block cache. Set the upper limit.
232 */
233 bcache_init(32768, 512);
234
235 /*
236 * Parse the args to set the console settings, etc
237 * boot1.efi passes these in, if it can read /boot.config or /boot/config
238 * or iPXE may be setup to pass these in.
239 *
240 * Loop through the args, and for each one that contains an '=' that is
241 * not the first character, add it to the environment. This allows
242 * loader and kernel env vars to be passed on the command line. Convert
243 * args from UCS-2 to ASCII (16 to 8 bit) as they are copied.
244 */
245 howto = 0;
246 for (i = 1; i < argc; i++) {
247 if (argv[i][0] == '-') {
248 for (j = 1; argv[i][j] != 0; j++) {
249 int ch;
250
251 ch = argv[i][j];
252 switch (ch) {
253 case 'a':
254 howto |= RB_ASKNAME;
255 break;
256 case 'd':
257 howto |= RB_KDB;
258 break;
259 case 'D':
260 howto |= RB_MULTIPLE;
261 break;
262 case 'h':
263 howto |= RB_SERIAL;
264 break;
265 case 'm':
266 howto |= RB_MUTE;
267 break;
268 case 'p':
269 howto |= RB_PAUSE;
270 break;
271 case 'P':
272 if (!has_kbd)
273 howto |= RB_SERIAL | RB_MULTIPLE;
274 break;
275 case 'r':
276 howto |= RB_DFLTROOT;
277 break;
278 case 's':
279 howto |= RB_SINGLE;
280 break;
281 case 'S':
282 if (argv[i][j + 1] == 0) {
283 if (i + 1 == argc) {
284 setenv("comconsole_speed", "115200", 1);
285 } else {
Toomas Soomeeee59042016-11-12 00:06:58 +0200286 cpy16to8(&argv[i + 1][0], var,
Toomas Soome199767f2015-10-25 00:06:51 +0300287 sizeof(var));
288 setenv("comconsole_speedspeed", var, 1);
289 }
290 i++;
291 break;
292 } else {
Toomas Soomeeee59042016-11-12 00:06:58 +0200293 cpy16to8(&argv[i][j + 1], var,
Toomas Soome199767f2015-10-25 00:06:51 +0300294 sizeof(var));
295 setenv("comconsole_speed", var, 1);
296 break;
297 }
298 case 'v':
299 howto |= RB_VERBOSE;
300 break;
301 }
302 }
303 } else {
304 vargood = 0;
305 for (j = 0; argv[i][j] != 0; j++) {
306 if (j == sizeof(var)) {
307 vargood = 0;
308 break;
309 }
310 if (j > 0 && argv[i][j] == '=')
311 vargood = 1;
312 var[j] = (char)argv[i][j];
313 }
314 if (vargood) {
315 var[j] = 0;
316 putenv(var);
317 }
318 }
319 }
320 for (i = 0; howto_names[i].ev != NULL; i++)
321 if (howto & howto_names[i].mask)
322 setenv(howto_names[i].ev, "YES", 1);
323 if (howto & RB_MULTIPLE) {
324 if (howto & RB_SERIAL)
325 setenv("console", "ttya text" , 1);
326 else
327 setenv("console", "text ttya" , 1);
328 } else if (howto & RB_SERIAL) {
329 setenv("console", "ttya" , 1);
330 }
331
332 if (efi_copy_init()) {
333 printf("failed to allocate staging area\n");
334 return (EFI_BUFFER_TOO_SMALL);
335 }
336
337 /*
338 * March through the device switch probing for things.
339 */
340 for (i = 0; devsw[i] != NULL; i++)
341 if (devsw[i]->dv_init != NULL)
342 (devsw[i]->dv_init)();
343
344 /* Get our loaded image protocol interface structure. */
345 BS->HandleProtocol(IH, &imgid, (VOID**)&img);
346
347 printf("Command line arguments:");
348 for (i = 0; i < argc; i++) {
Toomas Soomeeee59042016-11-12 00:06:58 +0200349 printf(" %S", argv[i]);
Toomas Soome199767f2015-10-25 00:06:51 +0300350 }
351 printf("\n");
352
353 printf("Image base: 0x%lx\n", (u_long)img->ImageBase);
354 printf("EFI version: %d.%02d\n", ST->Hdr.Revision >> 16,
355 ST->Hdr.Revision & 0xffff);
Toomas Soomeeee59042016-11-12 00:06:58 +0200356 printf("EFI Firmware: %S (rev %d.%02d)\n", ST->FirmwareVendor,
357 ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
Toomas Soome199767f2015-10-25 00:06:51 +0300358
Toomas Soomef6c94442017-01-03 11:24:09 +0200359 printf("\n%s", bootprog_info);
Toomas Soome199767f2015-10-25 00:06:51 +0300360
361 /*
362 * Disable the watchdog timer. By default the boot manager sets
363 * the timer to 5 minutes before invoking a boot option. If we
364 * want to return to the boot manager, we have to disable the
365 * watchdog timer and since we're an interactive program, we don't
366 * want to wait until the user types "quit". The timer may have
367 * fired by then. We don't care if this fails. It does not prevent
368 * normal functioning in any way...
369 */
370 BS->SetWatchdogTimer(0, 0, 0, NULL);
371
Toomas Soomeab8c1d42016-11-12 13:46:26 +0200372 if (find_currdev(img, &dev, &unit, &pool_guid) != 0)
Toomas Soome199767f2015-10-25 00:06:51 +0300373 return (EFI_NOT_FOUND);
374
375 switch (dev->dv_type) {
376#ifdef EFI_ZFS_BOOT
377 case DEVT_ZFS: {
378 struct zfs_devdesc currdev;
379
380 currdev.d_dev = dev;
381 currdev.d_unit = unit;
382 currdev.d_type = currdev.d_dev->dv_type;
383 currdev.d_opendata = NULL;
384 currdev.pool_guid = pool_guid;
385 currdev.root_guid = 0;
386 env_setenv("currdev", EV_VOLATILE, efi_fmtdev(&currdev),
387 efi_setcurrdev, env_nounset);
388 env_setenv("loaddev", EV_VOLATILE, efi_fmtdev(&currdev), env_noset,
389 env_nounset);
390#ifdef __FreeBSD__
391 init_zfs_bootenv(zfs_fmtdev(&currdev));
392#endif
393 break;
394 }
395#endif
396 default: {
397 struct devdesc currdev;
398
399 currdev.d_dev = dev;
400 currdev.d_unit = unit;
401 currdev.d_opendata = NULL;
402 currdev.d_type = currdev.d_dev->dv_type;
403 env_setenv("currdev", EV_VOLATILE, efi_fmtdev(&currdev),
404 efi_setcurrdev, env_nounset);
405 env_setenv("loaddev", EV_VOLATILE, efi_fmtdev(&currdev), env_noset,
406 env_nounset);
407 break;
408 }
409 }
410
Toomas Soomeeee59042016-11-12 00:06:58 +0200411 efi_init_environment();
Toomas Soome199767f2015-10-25 00:06:51 +0300412 setenv("LINES", "24", 1); /* optional */
413 setenv("COLUMNS", "80", 1); /* optional */
414 setenv("ISADIR", "amd64", 1); /* we only build 64bit */
Toomas Soomedcba96f2016-10-09 17:12:23 +0300415 acpi_detect();
Toomas Soome199767f2015-10-25 00:06:51 +0300416
Toomas Soomedcba96f2016-10-09 17:12:23 +0300417 if ((ptr = efi_get_table(&smbios3)) == NULL)
418 ptr = efi_get_table(&smbios);
419 smbios_detect(ptr);
Toomas Soome199767f2015-10-25 00:06:51 +0300420
421 efi_serial_init(); /* detect and set up serial ports */
422 interact(NULL); /* doesn't return */
423
424 return (EFI_SUCCESS); /* keep compiler happy */
425}
426
427COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot);
428
429static int
430command_reboot(int argc __attribute((unused)),
431 char *argv[] __attribute((unused)))
432{
433 int i;
Toomas Soome199767f2015-10-25 00:06:51 +0300434
435 for (i = 0; devsw[i] != NULL; ++i)
436 if (devsw[i]->dv_cleanup != NULL)
437 (devsw[i]->dv_cleanup)();
438
Toomas Soome48d84432017-04-27 19:19:29 +0300439 RS->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL);
Toomas Soome199767f2015-10-25 00:06:51 +0300440
441 /* NOTREACHED */
442 return (CMD_ERROR);
443}
444
445COMMAND_SET(memmap, "memmap", "print memory map", command_memmap);
446
447static int
448command_memmap(int argc __attribute((unused)),
449 char *argv[] __attribute((unused)))
450{
451 UINTN sz;
452 EFI_MEMORY_DESCRIPTOR *map, *p;
453 UINTN key, dsz;
454 UINT32 dver;
455 EFI_STATUS status;
456 int i, ndesc;
457 int rv = 0;
458 char line[80];
Toomas Soome199767f2015-10-25 00:06:51 +0300459
460 sz = 0;
461 status = BS->GetMemoryMap(&sz, 0, &key, &dsz, &dver);
462 if (status != EFI_BUFFER_TOO_SMALL) {
463 printf("Can't determine memory map size\n");
464 return (CMD_ERROR);
465 }
466 map = malloc(sz);
467 status = BS->GetMemoryMap(&sz, map, &key, &dsz, &dver);
468 if (EFI_ERROR(status)) {
469 printf("Can't read memory map\n");
470 return (CMD_ERROR);
471 }
472
473 ndesc = sz / dsz;
474 snprintf(line, 80, "%23s %12s %12s %8s %4s\n",
475 "Type", "Physical", "Virtual", "#Pages", "Attr");
476 pager_open();
477 rv = pager_output(line);
478 if (rv) {
479 pager_close();
480 return (CMD_OK);
481 }
482
483 for (i = 0, p = map; i < ndesc;
484 i++, p = NextMemoryDescriptor(p, dsz)) {
485 snprintf(line, 80, "%23s %012lx %012lx %08lx ",
Toomas Soomeeee59042016-11-12 00:06:58 +0200486 efi_memory_type(p->Type),
Toomas Soome199767f2015-10-25 00:06:51 +0300487 p->PhysicalStart,
488 p->VirtualStart,
489 p->NumberOfPages);
490 rv = pager_output(line);
491 if (rv)
492 break;
493
494 if (p->Attribute & EFI_MEMORY_UC)
495 printf("UC ");
496 if (p->Attribute & EFI_MEMORY_WC)
497 printf("WC ");
498 if (p->Attribute & EFI_MEMORY_WT)
499 printf("WT ");
500 if (p->Attribute & EFI_MEMORY_WB)
501 printf("WB ");
502 if (p->Attribute & EFI_MEMORY_UCE)
503 printf("UCE ");
504 if (p->Attribute & EFI_MEMORY_WP)
505 printf("WP ");
506 if (p->Attribute & EFI_MEMORY_RP)
507 printf("RP ");
508 if (p->Attribute & EFI_MEMORY_XP)
509 printf("XP ");
Toomas Soomeeee59042016-11-12 00:06:58 +0200510 if (p->Attribute & EFI_MEMORY_NV)
511 printf("NV ");
512 if (p->Attribute & EFI_MEMORY_MORE_RELIABLE)
513 printf("MR ");
514 if (p->Attribute & EFI_MEMORY_RO)
515 printf("RO ");
Toomas Soome199767f2015-10-25 00:06:51 +0300516 rv = pager_output("\n");
517 if (rv)
518 break;
519 }
520
521 pager_close();
522 return (CMD_OK);
523}
524
525COMMAND_SET(configuration, "configuration", "print configuration tables",
526 command_configuration);
527
Toomas Soome199767f2015-10-25 00:06:51 +0300528static int
529command_configuration(int argc __attribute((unused)),
530 char *argv[] __attribute((unused)))
531{
532 UINTN i;
Toomas Soomeeee59042016-11-12 00:06:58 +0200533 char *name;
Toomas Soome199767f2015-10-25 00:06:51 +0300534
535 printf("NumberOfTableEntries=%lu\n",
536 (unsigned long)ST->NumberOfTableEntries);
537 for (i = 0; i < ST->NumberOfTableEntries; i++) {
538 EFI_GUID *guid;
539
540 printf(" ");
541 guid = &ST->ConfigurationTable[i].VendorGuid;
Toomas Soomeeee59042016-11-12 00:06:58 +0200542
543 if (efi_guid_to_name(guid, &name) == true) {
544 printf(name);
545 free(name);
546 } else {
547 printf("Error while translating UUID to name");
548 }
Toomas Soome199767f2015-10-25 00:06:51 +0300549 printf(" at %p\n", ST->ConfigurationTable[i].VendorTable);
550 }
551
552 return (CMD_OK);
553}
554
555
556COMMAND_SET(mode, "mode", "change or display EFI text modes", command_mode);
557
558static int
559command_mode(int argc, char *argv[])
560{
561 UINTN cols, rows;
562 unsigned int mode;
563 int i;
564 char *cp;
565 char rowenv[8];
566 EFI_STATUS status;
567 SIMPLE_TEXT_OUTPUT_INTERFACE *conout;
568 extern void HO(void);
569
570 conout = ST->ConOut;
571
572 if (argc > 1) {
573 mode = strtol(argv[1], &cp, 0);
574 if (cp[0] != '\0') {
575 printf("Invalid mode\n");
576 return (CMD_ERROR);
577 }
578 status = conout->QueryMode(conout, mode, &cols, &rows);
579 if (EFI_ERROR(status)) {
580 printf("invalid mode %d\n", mode);
581 return (CMD_ERROR);
582 }
583 status = conout->SetMode(conout, mode);
584 if (EFI_ERROR(status)) {
585 printf("couldn't set mode %d\n", mode);
586 return (CMD_ERROR);
587 }
588 sprintf(rowenv, "%u", (unsigned)rows);
589 setenv("LINES", rowenv, 1);
590 sprintf(rowenv, "%u", (unsigned)cols);
591 setenv("COLUMNS", rowenv, 1);
592 HO(); /* set cursor */
593 return (CMD_OK);
594 }
595
596 printf("Current mode: %d\n", conout->Mode->Mode);
597 for (i = 0; i <= conout->Mode->MaxMode; i++) {
598 status = conout->QueryMode(conout, i, &cols, &rows);
599 if (EFI_ERROR(status))
600 continue;
601 printf("Mode %d: %u columns, %u rows\n", i, (unsigned)cols,
602 (unsigned)rows);
603 }
604
605 if (i != 0)
606 printf("Select a mode with the command \"mode <number>\"\n");
607
608 return (CMD_OK);
609}
610
Toomas Soome199767f2015-10-25 00:06:51 +0300611COMMAND_SET(lsefi, "lsefi", "list EFI handles", command_lsefi);
612
613static int
614command_lsefi(int argc __attribute((unused)),
615 char *argv[] __attribute((unused)))
616{
Toomas Soomeeee59042016-11-12 00:06:58 +0200617 char *name;
Toomas Soome199767f2015-10-25 00:06:51 +0300618 EFI_HANDLE *buffer = NULL;
619 EFI_HANDLE handle;
620 UINTN bufsz = 0, i, j;
621 EFI_STATUS status;
Toomas Soomeeee59042016-11-12 00:06:58 +0200622 int ret;
Toomas Soome199767f2015-10-25 00:06:51 +0300623
624 status = BS->LocateHandle(AllHandles, NULL, NULL, &bufsz, buffer);
625 if (status != EFI_BUFFER_TOO_SMALL) {
626 snprintf(command_errbuf, sizeof (command_errbuf),
627 "unexpected error: %lld", (long long)status);
628 return (CMD_ERROR);
629 }
630 if ((buffer = malloc(bufsz)) == NULL) {
631 sprintf(command_errbuf, "out of memory");
632 return (CMD_ERROR);
633 }
634
635 status = BS->LocateHandle(AllHandles, NULL, NULL, &bufsz, buffer);
636 if (EFI_ERROR(status)) {
637 free(buffer);
638 snprintf(command_errbuf, sizeof (command_errbuf),
639 "LocateHandle() error: %lld", (long long)status);
640 return (CMD_ERROR);
641 }
642
643 pager_open();
644 for (i = 0; i < (bufsz / sizeof (EFI_HANDLE)); i++) {
645 UINTN nproto = 0;
646 EFI_GUID **protocols = NULL;
647
648 handle = buffer[i];
649 printf("Handle %p", handle);
650 if (pager_output("\n"))
651 break;
652 /* device path */
653
654 status = BS->ProtocolsPerHandle(handle, &protocols, &nproto);
655 if (EFI_ERROR(status)) {
656 snprintf(command_errbuf, sizeof (command_errbuf),
657 "ProtocolsPerHandle() error: %lld",
658 (long long)status);
659 continue;
660 }
Toomas Soomeeee59042016-11-12 00:06:58 +0200661
Toomas Soome199767f2015-10-25 00:06:51 +0300662 for (j = 0; j < nproto; j++) {
Toomas Soomeeee59042016-11-12 00:06:58 +0200663 if (efi_guid_to_name(protocols[j], &name) == true) {
664 printf(" %s", name);
665 free(name);
666 } else {
667 printf("Error while translating UUID to name");
668 }
669 if ((ret = pager_output("\n")) != 0)
Toomas Soome199767f2015-10-25 00:06:51 +0300670 break;
671 }
672 BS->FreePool(protocols);
Toomas Soomeeee59042016-11-12 00:06:58 +0200673 if (ret != 0)
Toomas Soome199767f2015-10-25 00:06:51 +0300674 break;
675 }
676 pager_close();
677 free(buffer);
678 return (CMD_OK);
679}
680
Toomas Soome199767f2015-10-25 00:06:51 +0300681COMMAND_SET(lszfs, "lszfs", "list child datasets of a zfs dataset",
682 command_lszfs);
683
684static int
685command_lszfs(int argc, char *argv[])
686{
687 int err;
688
689 if (argc != 2) {
690 command_errmsg = "wrong number of arguments";
691 return (CMD_ERROR);
692 }
693
694 err = zfs_list(argv[1]);
695 if (err != 0) {
696 command_errmsg = strerror(err);
697 return (CMD_ERROR);
698 }
699 return (CMD_OK);
700}
701
702#ifdef __FreeBSD__
703COMMAND_SET(reloadbe, "reloadbe", "refresh the list of ZFS Boot Environments",
704 command_reloadbe);
705
706static int
707command_reloadbe(int argc, char *argv[])
708{
709 int err;
710 char *root;
711
712 if (argc > 2) {
713 command_errmsg = "wrong number of arguments";
714 return (CMD_ERROR);
715 }
716
717 if (argc == 2) {
718 err = zfs_bootenv(argv[1]);
719 } else {
720 root = getenv("zfs_be_root");
721 if (root == NULL) {
722 return (CMD_OK);
723 }
724 err = zfs_bootenv(root);
725 }
726
727 if (err != 0) {
728 command_errmsg = strerror(err);
729 return (CMD_ERROR);
730 }
731
732 return (CMD_OK);
733}
734#endif /* __FreeBSD__ */
Toomas Soome199767f2015-10-25 00:06:51 +0300735
736void
737efi_serial_init(void)
738{
739 EFI_HANDLE *buffer = NULL;
740 UINTN bufsz = 0, i;
741 EFI_STATUS status;
742 int serial = 0;
743
744 /*
745 * get buffer size
746 */
747 status = BS->LocateHandle(ByProtocol, &serial_io, NULL, &bufsz, buffer);
748 if (status != EFI_BUFFER_TOO_SMALL) {
749 snprintf(command_errbuf, sizeof (command_errbuf),
750 "unexpected error: %lld", (long long)status);
751 return;
752 }
753 if ((buffer = malloc(bufsz)) == NULL) {
754 sprintf(command_errbuf, "out of memory");
755 return;
756 }
757
758 /*
759 * get handle array
760 */
761 status = BS->LocateHandle(ByProtocol, &serial_io, NULL, &bufsz, buffer);
762 if (EFI_ERROR(status)) {
763 free(buffer);
764 snprintf(command_errbuf, sizeof (command_errbuf),
765 "LocateHandle() error: %lld", (long long)status);
766 return;
767 }
768
769 for (i = 0; i < (bufsz / sizeof (EFI_HANDLE)); i++) {
770 SERIAL_IO_INTERFACE *sio;
771 status = BS->OpenProtocol(buffer[i], &serial_io, (void**)&sio,
772 IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
773 if (EFI_ERROR(status)) {
774 snprintf(command_errbuf, sizeof (command_errbuf),
775 "OpenProtocol() error: %lld", (long long)status);
776 }
777 printf("serial# %d\n", serial++);
778 }
779
780 free(buffer);
781}
782
783#ifdef LOADER_FDT_SUPPORT
784extern int command_fdt_internal(int argc, char *argv[]);
785
786/*
787 * Since proper fdt command handling function is defined in fdt_loader_cmd.c,
788 * and declaring it as extern is in contradiction with COMMAND_SET() macro
789 * (which uses static pointer), we're defining wrapper function, which
790 * calls the proper fdt handling routine.
791 */
792static int
793command_fdt(int argc, char *argv[])
794{
795 return (command_fdt_internal(argc, argv));
796}
797
798COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt);
799#endif
800
801#ifdef EFI_ZFS_BOOT
802static void
803efi_zfs_probe(void)
804{
805 EFI_HANDLE h;
806 u_int unit;
807 int i;
808 char dname[SPECNAMELEN + 1];
809 uint64_t guid;
810
811 unit = 0;
812 h = efi_find_handle(&efipart_dev, 0);
813 for (i = 0; h != NULL; h = efi_find_handle(&efipart_dev, ++i)) {
814 snprintf(dname, sizeof(dname), "%s%d:", efipart_dev.dv_name, i);
815 if (zfs_probe_dev(dname, &guid) == 0)
816 (void)efi_handle_update_dev(h, &zfs_dev, unit++, guid);
817 }
818}
Toomas Soomeedb35042016-11-07 15:37:12 +0200819
820uint64_t
821ldi_get_size(void *priv)
822{
823 int fd = (uintptr_t) priv;
824 uint64_t size;
825
826 ioctl(fd, DIOCGMEDIASIZE, &size);
827 return (size);
828}
Toomas Soome199767f2015-10-25 00:06:51 +0300829#endif