blob: 3ef635fd9175078c0235aadfda820b363c9a8c26 [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>
Toomas Soomeeee59042016-11-12 00:06:58 +020044
Toomas Soome199767f2015-10-25 00:06:51 +030045#include <bootstrap.h>
46#include <smbios.h>
47
48#ifdef EFI_ZFS_BOOT
49#include <libzfs.h>
50#endif
51
52#include "loader_efi.h"
53
Toomas Soomef6c94442017-01-03 11:24:09 +020054extern char bootprog_info[];
Toomas Soome199767f2015-10-25 00:06:51 +030055
56struct arch_switch archsw; /* MI/MD interface boundary */
57
Toomas Soome199767f2015-10-25 00:06:51 +030058EFI_GUID devid = DEVICE_PATH_PROTOCOL;
59EFI_GUID imgid = LOADED_IMAGE_PROTOCOL;
Toomas Soome199767f2015-10-25 00:06:51 +030060EFI_GUID smbios = SMBIOS_TABLE_GUID;
61EFI_GUID smbios3 = SMBIOS3_TABLE_GUID;
Toomas Soome199767f2015-10-25 00:06:51 +030062EFI_GUID inputid = SIMPLE_TEXT_INPUT_PROTOCOL;
Toomas Soome199767f2015-10-25 00:06:51 +030063
Toomas Soomedcba96f2016-10-09 17:12:23 +030064extern void acpi_detect(void);
Toomas Soomef9feecc2016-10-09 17:30:28 +030065extern void efi_getsmap(void);
Toomas Soome199767f2015-10-25 00:06:51 +030066#ifdef EFI_ZFS_BOOT
67static void efi_zfs_probe(void);
68#endif
69
Toomas Soome199767f2015-10-25 00:06:51 +030070static int
71has_keyboard(void)
72{
73 EFI_STATUS status;
74 EFI_DEVICE_PATH *path;
75 EFI_HANDLE *hin, *hin_end, *walker;
76 UINTN sz;
77 int retval = 0;
78
79 /*
80 * Find all the handles that support the SIMPLE_TEXT_INPUT_PROTOCOL and
81 * do the typical dance to get the right sized buffer.
82 */
83 sz = 0;
84 hin = NULL;
85 status = BS->LocateHandle(ByProtocol, &inputid, 0, &sz, 0);
86 if (status == EFI_BUFFER_TOO_SMALL) {
87 hin = (EFI_HANDLE *)malloc(sz);
88 status = BS->LocateHandle(ByProtocol, &inputid, 0, &sz,
89 hin);
90 if (EFI_ERROR(status))
91 free(hin);
92 }
93 if (EFI_ERROR(status))
94 return retval;
95
96 /*
97 * Look at each of the handles. If it supports the device path protocol,
98 * use it to get the device path for this handle. Then see if that
99 * device path matches either the USB device path for keyboards or the
100 * legacy device path for keyboards.
101 */
102 hin_end = &hin[sz / sizeof(*hin)];
103 for (walker = hin; walker < hin_end; walker++) {
104 status = BS->HandleProtocol(*walker, &devid, (VOID **)&path);
105 if (EFI_ERROR(status))
106 continue;
107
108 while (!IsDevicePathEnd(path)) {
109 /*
110 * Check for the ACPI keyboard node. All PNP3xx nodes
111 * are keyboards of different flavors. Note: It is
112 * unclear of there's always a keyboard node when
113 * there's a keyboard controller, or if there's only one
114 * when a keyboard is detected at boot.
115 */
116 if (DevicePathType(path) == ACPI_DEVICE_PATH &&
117 (DevicePathSubType(path) == ACPI_DP ||
118 DevicePathSubType(path) == ACPI_EXTENDED_DP)) {
119 ACPI_HID_DEVICE_PATH *acpi;
120
121 acpi = (ACPI_HID_DEVICE_PATH *)(void *)path;
122 if ((EISA_ID_TO_NUM(acpi->HID) & 0xff00) == 0x300 &&
123 (acpi->HID & 0xffff) == PNP_EISA_ID_CONST) {
124 retval = 1;
125 goto out;
126 }
127 /*
128 * Check for USB keyboard node, if present. Unlike a
129 * PS/2 keyboard, these definitely only appear when
130 * connected to the system.
131 */
132 } else if (DevicePathType(path) == MESSAGING_DEVICE_PATH &&
133 DevicePathSubType(path) == MSG_USB_CLASS_DP) {
134 USB_CLASS_DEVICE_PATH *usb;
135
136 usb = (USB_CLASS_DEVICE_PATH *)(void *)path;
137 if (usb->DeviceClass == 3 && /* HID */
138 usb->DeviceSubClass == 1 && /* Boot devices */
139 usb->DeviceProtocol == 1) { /* Boot keyboards */
140 retval = 1;
141 goto out;
142 }
143 }
144 path = NextDevicePathNode(path);
145 }
146 }
147out:
148 free(hin);
149 return retval;
150}
151
Toomas Soomeab8c1d42016-11-12 13:46:26 +0200152static int
153find_currdev(EFI_LOADED_IMAGE *img, struct devsw **dev, int *unit,
154 uint64_t *extra)
155{
156 EFI_DEVICE_PATH *devpath, *copy;
157 EFI_HANDLE h;
158
159 /*
160 * Try the device handle from our loaded image first. If that
161 * fails, use the device path from the loaded image and see if
162 * any of the nodes in that path match one of the enumerated
163 * handles.
164 */
165 if (efi_handle_lookup(img->DeviceHandle, dev, unit, extra) == 0)
166 return (0);
167
168 copy = NULL;
169 devpath = efi_lookup_image_devpath(IH);
170 while (devpath != NULL) {
171 h = efi_devpath_handle(devpath);
172 if (h == NULL)
173 break;
174
175 free(copy);
176 copy = NULL;
177
178 if (efi_handle_lookup(h, dev, unit, extra) == 0)
179 return (0);
180
181 devpath = efi_lookup_devpath(h);
182 if (devpath != NULL) {
183 copy = efi_devpath_trim(devpath);
184 devpath = copy;
185 }
186 }
187 free(copy);
188
189 return (ENOENT);
190}
191
Toomas Soome199767f2015-10-25 00:06:51 +0300192EFI_STATUS
193main(int argc, CHAR16 *argv[])
194{
195 char var[128];
196 EFI_LOADED_IMAGE *img;
197 EFI_GUID *guid;
198 int i, j, vargood, unit, howto;
199 struct devsw *dev;
200 uint64_t pool_guid;
Toomas Soomedcba96f2016-10-09 17:12:23 +0300201 void *ptr;
Toomas Soome199767f2015-10-25 00:06:51 +0300202 UINTN k;
203 int has_kbd;
204
205 archsw.arch_autoload = efi_autoload;
206 archsw.arch_getdev = efi_getdev;
207 archsw.arch_copyin = efi_copyin;
208 archsw.arch_copyout = efi_copyout;
209 archsw.arch_readin = efi_readin;
Toomas Soomef9feecc2016-10-09 17:30:28 +0300210 archsw.arch_loadaddr = efi_loadaddr;
211 archsw.arch_free_loadaddr = efi_free_loadaddr;
Toomas Soome199767f2015-10-25 00:06:51 +0300212#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();
Toomas Soomef9feecc2016-10-09 17:30:28 +0300229 efi_getsmap();
Toomas Soome199767f2015-10-25 00:06:51 +0300230
231 /*
232 * Initialise the block cache. Set the upper limit.
233 */
234 bcache_init(32768, 512);
235
236 /*
237 * Parse the args to set the console settings, etc
238 * boot1.efi passes these in, if it can read /boot.config or /boot/config
239 * or iPXE may be setup to pass these in.
240 *
241 * Loop through the args, and for each one that contains an '=' that is
242 * not the first character, add it to the environment. This allows
243 * loader and kernel env vars to be passed on the command line. Convert
244 * args from UCS-2 to ASCII (16 to 8 bit) as they are copied.
245 */
246 howto = 0;
247 for (i = 1; i < argc; i++) {
248 if (argv[i][0] == '-') {
249 for (j = 1; argv[i][j] != 0; j++) {
250 int ch;
251
252 ch = argv[i][j];
253 switch (ch) {
254 case 'a':
255 howto |= RB_ASKNAME;
256 break;
257 case 'd':
258 howto |= RB_KDB;
259 break;
260 case 'D':
261 howto |= RB_MULTIPLE;
262 break;
263 case 'h':
264 howto |= RB_SERIAL;
265 break;
266 case 'm':
267 howto |= RB_MUTE;
268 break;
269 case 'p':
270 howto |= RB_PAUSE;
271 break;
272 case 'P':
273 if (!has_kbd)
274 howto |= RB_SERIAL | RB_MULTIPLE;
275 break;
276 case 'r':
277 howto |= RB_DFLTROOT;
278 break;
279 case 's':
280 howto |= RB_SINGLE;
281 break;
282 case 'S':
283 if (argv[i][j + 1] == 0) {
284 if (i + 1 == argc) {
285 setenv("comconsole_speed", "115200", 1);
286 } else {
Toomas Soomeeee59042016-11-12 00:06:58 +0200287 cpy16to8(&argv[i + 1][0], var,
Toomas Soome199767f2015-10-25 00:06:51 +0300288 sizeof(var));
289 setenv("comconsole_speedspeed", var, 1);
290 }
291 i++;
292 break;
293 } else {
Toomas Soomeeee59042016-11-12 00:06:58 +0200294 cpy16to8(&argv[i][j + 1], var,
Toomas Soome199767f2015-10-25 00:06:51 +0300295 sizeof(var));
296 setenv("comconsole_speed", var, 1);
297 break;
298 }
299 case 'v':
300 howto |= RB_VERBOSE;
301 break;
302 }
303 }
304 } else {
305 vargood = 0;
306 for (j = 0; argv[i][j] != 0; j++) {
307 if (j == sizeof(var)) {
308 vargood = 0;
309 break;
310 }
311 if (j > 0 && argv[i][j] == '=')
312 vargood = 1;
313 var[j] = (char)argv[i][j];
314 }
315 if (vargood) {
316 var[j] = 0;
317 putenv(var);
318 }
319 }
320 }
321 for (i = 0; howto_names[i].ev != NULL; i++)
322 if (howto & howto_names[i].mask)
323 setenv(howto_names[i].ev, "YES", 1);
324 if (howto & RB_MULTIPLE) {
325 if (howto & RB_SERIAL)
326 setenv("console", "ttya text" , 1);
327 else
328 setenv("console", "text ttya" , 1);
329 } else if (howto & RB_SERIAL) {
330 setenv("console", "ttya" , 1);
331 }
332
Toomas Soome199767f2015-10-25 00:06:51 +0300333 /*
334 * March through the device switch probing for things.
335 */
336 for (i = 0; devsw[i] != NULL; i++)
337 if (devsw[i]->dv_init != NULL)
338 (devsw[i]->dv_init)();
339
340 /* Get our loaded image protocol interface structure. */
341 BS->HandleProtocol(IH, &imgid, (VOID**)&img);
342
343 printf("Command line arguments:");
344 for (i = 0; i < argc; i++) {
Toomas Soomeeee59042016-11-12 00:06:58 +0200345 printf(" %S", argv[i]);
Toomas Soome199767f2015-10-25 00:06:51 +0300346 }
347 printf("\n");
348
349 printf("Image base: 0x%lx\n", (u_long)img->ImageBase);
350 printf("EFI version: %d.%02d\n", ST->Hdr.Revision >> 16,
351 ST->Hdr.Revision & 0xffff);
Toomas Soomeeee59042016-11-12 00:06:58 +0200352 printf("EFI Firmware: %S (rev %d.%02d)\n", ST->FirmwareVendor,
353 ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
Toomas Soome199767f2015-10-25 00:06:51 +0300354
Toomas Soomef6c94442017-01-03 11:24:09 +0200355 printf("\n%s", bootprog_info);
Toomas Soome199767f2015-10-25 00:06:51 +0300356
357 /*
358 * Disable the watchdog timer. By default the boot manager sets
359 * the timer to 5 minutes before invoking a boot option. If we
360 * want to return to the boot manager, we have to disable the
361 * watchdog timer and since we're an interactive program, we don't
362 * want to wait until the user types "quit". The timer may have
363 * fired by then. We don't care if this fails. It does not prevent
364 * normal functioning in any way...
365 */
366 BS->SetWatchdogTimer(0, 0, 0, NULL);
367
Toomas Soomeab8c1d42016-11-12 13:46:26 +0200368 if (find_currdev(img, &dev, &unit, &pool_guid) != 0)
Toomas Soome199767f2015-10-25 00:06:51 +0300369 return (EFI_NOT_FOUND);
370
371 switch (dev->dv_type) {
372#ifdef EFI_ZFS_BOOT
373 case DEVT_ZFS: {
374 struct zfs_devdesc currdev;
375
376 currdev.d_dev = dev;
377 currdev.d_unit = unit;
378 currdev.d_type = currdev.d_dev->dv_type;
379 currdev.d_opendata = NULL;
380 currdev.pool_guid = pool_guid;
381 currdev.root_guid = 0;
382 env_setenv("currdev", EV_VOLATILE, efi_fmtdev(&currdev),
383 efi_setcurrdev, env_nounset);
384 env_setenv("loaddev", EV_VOLATILE, efi_fmtdev(&currdev), env_noset,
385 env_nounset);
386#ifdef __FreeBSD__
387 init_zfs_bootenv(zfs_fmtdev(&currdev));
388#endif
389 break;
390 }
391#endif
392 default: {
393 struct devdesc currdev;
394
395 currdev.d_dev = dev;
396 currdev.d_unit = unit;
397 currdev.d_opendata = NULL;
398 currdev.d_type = currdev.d_dev->dv_type;
399 env_setenv("currdev", EV_VOLATILE, efi_fmtdev(&currdev),
400 efi_setcurrdev, env_nounset);
401 env_setenv("loaddev", EV_VOLATILE, efi_fmtdev(&currdev), env_noset,
402 env_nounset);
403 break;
404 }
405 }
406
Toomas Soomeeee59042016-11-12 00:06:58 +0200407 efi_init_environment();
Toomas Soome199767f2015-10-25 00:06:51 +0300408 setenv("ISADIR", "amd64", 1); /* we only build 64bit */
Toomas Soomedcba96f2016-10-09 17:12:23 +0300409 acpi_detect();
Toomas Soome199767f2015-10-25 00:06:51 +0300410
Toomas Soomedcba96f2016-10-09 17:12:23 +0300411 if ((ptr = efi_get_table(&smbios3)) == NULL)
412 ptr = efi_get_table(&smbios);
413 smbios_detect(ptr);
Toomas Soome199767f2015-10-25 00:06:51 +0300414
Toomas Soome199767f2015-10-25 00:06:51 +0300415 interact(NULL); /* doesn't return */
416
417 return (EFI_SUCCESS); /* keep compiler happy */
418}
419
420COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot);
421
422static int
Toomas Soome30c75cb2016-03-13 20:45:57 +0200423command_reboot(int argc __unused, char *argv[] __unused)
Toomas Soome199767f2015-10-25 00:06:51 +0300424{
425 int i;
Toomas Soome199767f2015-10-25 00:06:51 +0300426
427 for (i = 0; devsw[i] != NULL; ++i)
428 if (devsw[i]->dv_cleanup != NULL)
429 (devsw[i]->dv_cleanup)();
430
Toomas Soome48d84432017-04-27 19:19:29 +0300431 RS->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL);
Toomas Soome199767f2015-10-25 00:06:51 +0300432
433 /* NOTREACHED */
434 return (CMD_ERROR);
435}
436
437COMMAND_SET(memmap, "memmap", "print memory map", command_memmap);
438
439static int
Toomas Soome30c75cb2016-03-13 20:45:57 +0200440command_memmap(int argc __unused, char *argv[] __unused)
Toomas Soome199767f2015-10-25 00:06:51 +0300441{
442 UINTN sz;
443 EFI_MEMORY_DESCRIPTOR *map, *p;
444 UINTN key, dsz;
445 UINT32 dver;
446 EFI_STATUS status;
447 int i, ndesc;
448 int rv = 0;
449 char line[80];
Toomas Soome199767f2015-10-25 00:06:51 +0300450
451 sz = 0;
452 status = BS->GetMemoryMap(&sz, 0, &key, &dsz, &dver);
453 if (status != EFI_BUFFER_TOO_SMALL) {
454 printf("Can't determine memory map size\n");
455 return (CMD_ERROR);
456 }
457 map = malloc(sz);
458 status = BS->GetMemoryMap(&sz, map, &key, &dsz, &dver);
459 if (EFI_ERROR(status)) {
460 printf("Can't read memory map\n");
461 return (CMD_ERROR);
462 }
463
464 ndesc = sz / dsz;
465 snprintf(line, 80, "%23s %12s %12s %8s %4s\n",
466 "Type", "Physical", "Virtual", "#Pages", "Attr");
467 pager_open();
468 rv = pager_output(line);
469 if (rv) {
470 pager_close();
471 return (CMD_OK);
472 }
473
474 for (i = 0, p = map; i < ndesc;
475 i++, p = NextMemoryDescriptor(p, dsz)) {
476 snprintf(line, 80, "%23s %012lx %012lx %08lx ",
Toomas Soomeeee59042016-11-12 00:06:58 +0200477 efi_memory_type(p->Type),
Toomas Soome199767f2015-10-25 00:06:51 +0300478 p->PhysicalStart,
479 p->VirtualStart,
480 p->NumberOfPages);
481 rv = pager_output(line);
482 if (rv)
483 break;
484
485 if (p->Attribute & EFI_MEMORY_UC)
486 printf("UC ");
487 if (p->Attribute & EFI_MEMORY_WC)
488 printf("WC ");
489 if (p->Attribute & EFI_MEMORY_WT)
490 printf("WT ");
491 if (p->Attribute & EFI_MEMORY_WB)
492 printf("WB ");
493 if (p->Attribute & EFI_MEMORY_UCE)
494 printf("UCE ");
495 if (p->Attribute & EFI_MEMORY_WP)
496 printf("WP ");
497 if (p->Attribute & EFI_MEMORY_RP)
498 printf("RP ");
499 if (p->Attribute & EFI_MEMORY_XP)
500 printf("XP ");
Toomas Soomeeee59042016-11-12 00:06:58 +0200501 if (p->Attribute & EFI_MEMORY_NV)
502 printf("NV ");
503 if (p->Attribute & EFI_MEMORY_MORE_RELIABLE)
504 printf("MR ");
505 if (p->Attribute & EFI_MEMORY_RO)
506 printf("RO ");
Toomas Soome199767f2015-10-25 00:06:51 +0300507 rv = pager_output("\n");
508 if (rv)
509 break;
510 }
511
512 pager_close();
513 return (CMD_OK);
514}
515
516COMMAND_SET(configuration, "configuration", "print configuration tables",
517 command_configuration);
518
Toomas Soome199767f2015-10-25 00:06:51 +0300519static int
Toomas Soome30c75cb2016-03-13 20:45:57 +0200520command_configuration(int argc __unused, char *argv[] __unused)
Toomas Soome199767f2015-10-25 00:06:51 +0300521{
522 UINTN i;
Toomas Soomeeee59042016-11-12 00:06:58 +0200523 char *name;
Toomas Soome199767f2015-10-25 00:06:51 +0300524
525 printf("NumberOfTableEntries=%lu\n",
526 (unsigned long)ST->NumberOfTableEntries);
527 for (i = 0; i < ST->NumberOfTableEntries; i++) {
528 EFI_GUID *guid;
529
530 printf(" ");
531 guid = &ST->ConfigurationTable[i].VendorGuid;
Toomas Soomeeee59042016-11-12 00:06:58 +0200532
533 if (efi_guid_to_name(guid, &name) == true) {
534 printf(name);
535 free(name);
536 } else {
537 printf("Error while translating UUID to name");
538 }
Toomas Soome199767f2015-10-25 00:06:51 +0300539 printf(" at %p\n", ST->ConfigurationTable[i].VendorTable);
540 }
541
542 return (CMD_OK);
543}
544
545
546COMMAND_SET(mode, "mode", "change or display EFI text modes", command_mode);
547
548static int
549command_mode(int argc, char *argv[])
550{
551 UINTN cols, rows;
552 unsigned int mode;
553 int i;
554 char *cp;
555 char rowenv[8];
556 EFI_STATUS status;
557 SIMPLE_TEXT_OUTPUT_INTERFACE *conout;
558 extern void HO(void);
559
560 conout = ST->ConOut;
561
562 if (argc > 1) {
563 mode = strtol(argv[1], &cp, 0);
564 if (cp[0] != '\0') {
565 printf("Invalid mode\n");
566 return (CMD_ERROR);
567 }
568 status = conout->QueryMode(conout, mode, &cols, &rows);
569 if (EFI_ERROR(status)) {
570 printf("invalid mode %d\n", mode);
571 return (CMD_ERROR);
572 }
573 status = conout->SetMode(conout, mode);
574 if (EFI_ERROR(status)) {
575 printf("couldn't set mode %d\n", mode);
576 return (CMD_ERROR);
577 }
578 sprintf(rowenv, "%u", (unsigned)rows);
579 setenv("LINES", rowenv, 1);
580 sprintf(rowenv, "%u", (unsigned)cols);
581 setenv("COLUMNS", rowenv, 1);
582 HO(); /* set cursor */
583 return (CMD_OK);
584 }
585
586 printf("Current mode: %d\n", conout->Mode->Mode);
587 for (i = 0; i <= conout->Mode->MaxMode; i++) {
588 status = conout->QueryMode(conout, i, &cols, &rows);
589 if (EFI_ERROR(status))
590 continue;
591 printf("Mode %d: %u columns, %u rows\n", i, (unsigned)cols,
592 (unsigned)rows);
593 }
594
595 if (i != 0)
596 printf("Select a mode with the command \"mode <number>\"\n");
597
598 return (CMD_OK);
599}
600
Toomas Soome199767f2015-10-25 00:06:51 +0300601COMMAND_SET(lsefi, "lsefi", "list EFI handles", command_lsefi);
602
603static int
Toomas Soome30c75cb2016-03-13 20:45:57 +0200604command_lsefi(int argc __unused, char *argv[] __unused)
Toomas Soome199767f2015-10-25 00:06:51 +0300605{
Toomas Soomeeee59042016-11-12 00:06:58 +0200606 char *name;
Toomas Soome199767f2015-10-25 00:06:51 +0300607 EFI_HANDLE *buffer = NULL;
608 EFI_HANDLE handle;
609 UINTN bufsz = 0, i, j;
610 EFI_STATUS status;
Toomas Soomeeee59042016-11-12 00:06:58 +0200611 int ret;
Toomas Soome199767f2015-10-25 00:06:51 +0300612
613 status = BS->LocateHandle(AllHandles, NULL, NULL, &bufsz, buffer);
614 if (status != EFI_BUFFER_TOO_SMALL) {
615 snprintf(command_errbuf, sizeof (command_errbuf),
616 "unexpected error: %lld", (long long)status);
617 return (CMD_ERROR);
618 }
619 if ((buffer = malloc(bufsz)) == NULL) {
620 sprintf(command_errbuf, "out of memory");
621 return (CMD_ERROR);
622 }
623
624 status = BS->LocateHandle(AllHandles, NULL, NULL, &bufsz, buffer);
625 if (EFI_ERROR(status)) {
626 free(buffer);
627 snprintf(command_errbuf, sizeof (command_errbuf),
628 "LocateHandle() error: %lld", (long long)status);
629 return (CMD_ERROR);
630 }
631
632 pager_open();
633 for (i = 0; i < (bufsz / sizeof (EFI_HANDLE)); i++) {
634 UINTN nproto = 0;
635 EFI_GUID **protocols = NULL;
636
637 handle = buffer[i];
638 printf("Handle %p", handle);
639 if (pager_output("\n"))
640 break;
641 /* device path */
642
643 status = BS->ProtocolsPerHandle(handle, &protocols, &nproto);
644 if (EFI_ERROR(status)) {
645 snprintf(command_errbuf, sizeof (command_errbuf),
646 "ProtocolsPerHandle() error: %lld",
647 (long long)status);
648 continue;
649 }
Toomas Soomeeee59042016-11-12 00:06:58 +0200650
Toomas Soome199767f2015-10-25 00:06:51 +0300651 for (j = 0; j < nproto; j++) {
Toomas Soomeeee59042016-11-12 00:06:58 +0200652 if (efi_guid_to_name(protocols[j], &name) == true) {
653 printf(" %s", name);
654 free(name);
655 } else {
656 printf("Error while translating UUID to name");
657 }
658 if ((ret = pager_output("\n")) != 0)
Toomas Soome199767f2015-10-25 00:06:51 +0300659 break;
660 }
661 BS->FreePool(protocols);
Toomas Soomeeee59042016-11-12 00:06:58 +0200662 if (ret != 0)
Toomas Soome199767f2015-10-25 00:06:51 +0300663 break;
664 }
665 pager_close();
666 free(buffer);
667 return (CMD_OK);
668}
669
Toomas Soome199767f2015-10-25 00:06:51 +0300670COMMAND_SET(lszfs, "lszfs", "list child datasets of a zfs dataset",
671 command_lszfs);
672
673static int
674command_lszfs(int argc, char *argv[])
675{
676 int err;
677
678 if (argc != 2) {
679 command_errmsg = "wrong number of arguments";
680 return (CMD_ERROR);
681 }
682
683 err = zfs_list(argv[1]);
684 if (err != 0) {
685 command_errmsg = strerror(err);
686 return (CMD_ERROR);
687 }
688 return (CMD_OK);
689}
690
691#ifdef __FreeBSD__
692COMMAND_SET(reloadbe, "reloadbe", "refresh the list of ZFS Boot Environments",
693 command_reloadbe);
694
695static int
696command_reloadbe(int argc, char *argv[])
697{
698 int err;
699 char *root;
700
701 if (argc > 2) {
702 command_errmsg = "wrong number of arguments";
703 return (CMD_ERROR);
704 }
705
706 if (argc == 2) {
707 err = zfs_bootenv(argv[1]);
708 } else {
709 root = getenv("zfs_be_root");
710 if (root == NULL) {
711 return (CMD_OK);
712 }
713 err = zfs_bootenv(root);
714 }
715
716 if (err != 0) {
717 command_errmsg = strerror(err);
718 return (CMD_ERROR);
719 }
720
721 return (CMD_OK);
722}
723#endif /* __FreeBSD__ */
Toomas Soome199767f2015-10-25 00:06:51 +0300724
Toomas Soome199767f2015-10-25 00:06:51 +0300725#ifdef LOADER_FDT_SUPPORT
726extern int command_fdt_internal(int argc, char *argv[]);
727
728/*
729 * Since proper fdt command handling function is defined in fdt_loader_cmd.c,
730 * and declaring it as extern is in contradiction with COMMAND_SET() macro
731 * (which uses static pointer), we're defining wrapper function, which
732 * calls the proper fdt handling routine.
733 */
734static int
735command_fdt(int argc, char *argv[])
736{
737 return (command_fdt_internal(argc, argv));
738}
739
740COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt);
741#endif
742
Toomas Soomef9feecc2016-10-09 17:30:28 +0300743/*
744 * Chain load another efi loader.
745 */
746static int
747command_chain(int argc, char *argv[])
748{
749 EFI_GUID LoadedImageGUID = LOADED_IMAGE_PROTOCOL;
750 EFI_HANDLE loaderhandle;
751 EFI_LOADED_IMAGE *loaded_image;
752 EFI_STATUS status;
753 struct stat st;
754 struct devdesc *dev;
755 char *name, *path;
756 void *buf;
757 int fd;
758
759 if (argc < 2) {
760 command_errmsg = "wrong number of arguments";
761 return (CMD_ERROR);
762 }
763
764 name = argv[1];
765
766 if ((fd = open(name, O_RDONLY)) < 0) {
767 command_errmsg = "no such file";
768 return (CMD_ERROR);
769 }
770
771 if (fstat(fd, &st) < -1) {
772 command_errmsg = "stat failed";
773 close(fd);
774 return (CMD_ERROR);
775 }
776
777 status = BS->AllocatePool(EfiLoaderCode, (UINTN)st.st_size, &buf);
778 if (status != EFI_SUCCESS) {
779 command_errmsg = "failed to allocate buffer";
780 close(fd);
781 return (CMD_ERROR);
782 }
783 if (read(fd, buf, st.st_size) != st.st_size) {
784 command_errmsg = "error while reading the file";
785 (void)BS->FreePool(buf);
786 close(fd);
787 return (CMD_ERROR);
788 }
789 close(fd);
790 status = BS->LoadImage(FALSE, IH, NULL, buf, st.st_size, &loaderhandle);
791 (void)BS->FreePool(buf);
792 if (status != EFI_SUCCESS) {
793 command_errmsg = "LoadImage failed";
794 return (CMD_ERROR);
795 }
796 status = BS->HandleProtocol(loaderhandle, &LoadedImageGUID,
797 (void **)&loaded_image);
798
799 if (argc > 2) {
800 int i, len = 0;
801 CHAR16 *argp;
802
803 for (i = 2; i < argc; i++)
804 len += strlen(argv[i]) + 1;
805
806 len *= sizeof (*argp);
807 loaded_image->LoadOptions = argp = malloc (len);
808 if (loaded_image->LoadOptions == NULL) {
809 (void) BS->UnloadImage(loaded_image);
810 return (CMD_ERROR);
811 }
812 loaded_image->LoadOptionsSize = len;
813 for (i = 2; i < argc; i++) {
814 char *ptr = argv[i];
815 while (*ptr)
816 *(argp++) = *(ptr++);
817 *(argp++) = ' ';
818 }
819 *(--argv) = 0;
820 }
821
822 if (efi_getdev((void **)&dev, name, (const char **)&path) == 0)
823 loaded_image->DeviceHandle =
824 efi_find_handle(dev->d_dev, dev->d_unit);
825
826 dev_cleanup();
827 status = BS->StartImage(loaderhandle, NULL, NULL);
828 if (status != EFI_SUCCESS) {
829 command_errmsg = "StartImage failed";
830 free(loaded_image->LoadOptions);
831 loaded_image->LoadOptions = NULL;
832 status = BS->UnloadImage(loaded_image);
833 return (CMD_ERROR);
834 }
835
836 return (CMD_ERROR); /* not reached */
837}
838
839COMMAND_SET(chain, "chain", "chain load file", command_chain);
840
Toomas Soome199767f2015-10-25 00:06:51 +0300841#ifdef EFI_ZFS_BOOT
842static void
843efi_zfs_probe(void)
844{
845 EFI_HANDLE h;
846 u_int unit;
847 int i;
848 char dname[SPECNAMELEN + 1];
849 uint64_t guid;
850
851 unit = 0;
852 h = efi_find_handle(&efipart_dev, 0);
853 for (i = 0; h != NULL; h = efi_find_handle(&efipart_dev, ++i)) {
854 snprintf(dname, sizeof(dname), "%s%d:", efipart_dev.dv_name, i);
855 if (zfs_probe_dev(dname, &guid) == 0)
856 (void)efi_handle_update_dev(h, &zfs_dev, unit++, guid);
857 }
858}
Toomas Soomeedb35042016-11-07 15:37:12 +0200859
860uint64_t
861ldi_get_size(void *priv)
862{
863 int fd = (uintptr_t) priv;
864 uint64_t size;
865
866 ioctl(fd, DIOCGMEDIASIZE, &size);
867 return (size);
868}
Toomas Soome199767f2015-10-25 00:06:51 +0300869#endif