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