2 * Copyright (c) 1998 Robert Nordier
4 * Copyright (c) 2001 Robert Drehmel
6 * Copyright (c) 2014 Nathan Whitehorn
8 * Copyright (c) 2015 Eric McCorkle
11 * Redistribution and use in source and binary forms are freely
12 * permitted provided that the above copyright notice and this
13 * paragraph and the following disclaimer are duplicated in all
16 * This software is provided "AS IS" and without any express or
17 * implied warranties, including, without limitation, the implied
18 * warranties of merchantability and fitness for a particular
22 #include <sys/cdefs.h>
23 __FBSDID("$FreeBSD$");
25 #include <sys/param.h>
26 #include <machine/elf.h>
27 #include <machine/stdarg.h>
31 #include <eficonsctl.h>
32 typedef CHAR16 efi_char;
35 #include "boot_module.h"
38 static void efi_panic(EFI_STATUS s, const char *fmt, ...) __dead2 __printflike(2, 3);
40 static const boot_module_t *boot_modules[] =
50 #define NUM_BOOT_MODULES nitems(boot_modules)
51 /* The initial number of handles used to query EFI for partitions. */
52 #define NUM_HANDLES_INIT 24
54 static EFI_GUID BlockIoProtocolGUID = BLOCK_IO_PROTOCOL;
55 static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL;
56 static EFI_GUID LoadedImageGUID = LOADED_IMAGE_PROTOCOL;
57 static EFI_GUID ConsoleControlGUID = EFI_CONSOLE_CONTROL_PROTOCOL_GUID;
58 static EFI_GUID FreeBSDBootVarGUID = FREEBSD_BOOT_VAR_GUID;
61 * Provide Malloc / Free backed by EFIs AllocatePool / FreePool which ensures
62 * memory is correctly aligned avoiding EFI_INVALID_PARAMETER returns from
66 Malloc(size_t len, const char *file __unused, int line __unused)
70 if (BS->AllocatePool(EfiLoaderData, len, &out) == EFI_SUCCESS)
77 Free(void *buf, const char *file __unused, int line __unused)
80 (void)BS->FreePool(buf);
84 efi_setenv_freebsd_wcs(const char *varname, CHAR16 *valstr)
90 utf8_to_ucs2(varname, &var, &len);
92 return (EFI_OUT_OF_RESOURCES);
93 rv = RS->SetVariable(var, &FreeBSDBootVarGUID,
94 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
95 (ucs2len(valstr) + 1) * sizeof(efi_char), valstr);
101 * nodes_match returns TRUE if the imgpath isn't NULL and the nodes match,
105 nodes_match(EFI_DEVICE_PATH *imgpath, EFI_DEVICE_PATH *devpath)
109 if (imgpath == NULL || imgpath->Type != devpath->Type ||
110 imgpath->SubType != devpath->SubType)
113 len = DevicePathNodeLength(imgpath);
114 if (len != DevicePathNodeLength(devpath))
117 return (memcmp(imgpath, devpath, (size_t)len) == 0);
121 * device_paths_match returns TRUE if the imgpath isn't NULL and all nodes
122 * in imgpath and devpath match up to their respective occurrences of a
123 * media node, FALSE otherwise.
126 device_paths_match(EFI_DEVICE_PATH *imgpath, EFI_DEVICE_PATH *devpath)
132 while (!IsDevicePathEnd(imgpath) && !IsDevicePathEnd(devpath)) {
133 if (IsDevicePathType(imgpath, MEDIA_DEVICE_PATH) &&
134 IsDevicePathType(devpath, MEDIA_DEVICE_PATH))
137 if (!nodes_match(imgpath, devpath))
140 imgpath = NextDevicePathNode(imgpath);
141 devpath = NextDevicePathNode(devpath);
148 * devpath_last returns the last non-path end node in devpath.
150 static EFI_DEVICE_PATH *
151 devpath_last(EFI_DEVICE_PATH *devpath)
154 while (!IsDevicePathEnd(NextDevicePathNode(devpath)))
155 devpath = NextDevicePathNode(devpath);
161 * load_loader attempts to load the loader image data.
163 * It tries each module and its respective devices, identified by mod->probe,
164 * in order until a successful load occurs at which point it returns EFI_SUCCESS
165 * and EFI_NOT_FOUND otherwise.
167 * Only devices which have preferred matching the preferred parameter are tried.
170 load_loader(const boot_module_t **modp, dev_info_t **devinfop, void **bufp,
171 size_t *bufsize, BOOLEAN preferred)
175 const boot_module_t *mod;
177 for (i = 0; i < NUM_BOOT_MODULES; i++) {
178 mod = boot_modules[i];
179 for (dev = mod->devices(); dev != NULL; dev = dev->next) {
180 if (dev->preferred != preferred)
183 if (mod->load(PATH_LOADER_EFI, dev, bufp, bufsize) ==
187 return (EFI_SUCCESS);
192 return (EFI_NOT_FOUND);
196 * try_boot only returns if it fails to load the loader. If it succeeds
197 * it simply boots, otherwise it returns the status of last EFI call.
202 size_t bufsize, loadersize, cmdsize;
203 void *buf, *loaderbuf;
206 const boot_module_t *mod;
207 EFI_HANDLE loaderhandle;
208 EFI_LOADED_IMAGE *loaded_image;
211 status = load_loader(&mod, &dev, &loaderbuf, &loadersize, TRUE);
212 if (status != EFI_SUCCESS) {
213 status = load_loader(&mod, &dev, &loaderbuf, &loadersize,
215 if (status != EFI_SUCCESS) {
216 printf("Failed to load '%s'\n", PATH_LOADER_EFI);
222 * Read in and parse the command line from /boot.config or /boot/config,
223 * if present. We'll pass it the next stage via a simple ASCII
224 * string. loader.efi has a hack for ASCII strings, so we'll use that to
225 * keep the size down here. We only try to read the alternate file if
226 * we get EFI_NOT_FOUND because all other errors mean that the boot_module
227 * had troubles with the filesystem. We could return early, but we'll let
228 * loading the actual kernel sort all that out. Since these files are
229 * optional, we don't report errors in trying to read them.
233 status = mod->load(PATH_DOTCONFIG, dev, &buf, &bufsize);
234 if (status == EFI_NOT_FOUND)
235 status = mod->load(PATH_CONFIG, dev, &buf, &bufsize);
236 if (status == EFI_SUCCESS) {
237 cmdsize = bufsize + 1;
238 cmd = malloc(cmdsize);
241 memcpy(cmd, buf, bufsize);
247 if ((status = BS->LoadImage(TRUE, IH, devpath_last(dev->devpath),
248 loaderbuf, loadersize, &loaderhandle)) != EFI_SUCCESS) {
249 printf("Failed to load image provided by %s, size: %zu, (%lu)\n",
250 mod->name, loadersize, EFI_ERROR_CODE(status));
254 if ((status = BS->HandleProtocol(loaderhandle, &LoadedImageGUID,
255 (VOID**)&loaded_image)) != EFI_SUCCESS) {
256 printf("Failed to query LoadedImage provided by %s (%lu)\n",
257 mod->name, EFI_ERROR_CODE(status));
262 printf(" command args: %s\n", cmd);
264 loaded_image->DeviceHandle = dev->devhandle;
265 loaded_image->LoadOptionsSize = cmdsize;
266 loaded_image->LoadOptions = cmd;
268 DPRINTF("Starting '%s' in 5 seconds...", PATH_LOADER_EFI);
280 if ((status = BS->StartImage(loaderhandle, NULL, NULL)) !=
282 printf("Failed to start image provided by %s (%lu)\n",
283 mod->name, EFI_ERROR_CODE(status));
284 loaded_image->LoadOptionsSize = 0;
285 loaded_image->LoadOptions = NULL;
293 if (loaderbuf != NULL)
300 * probe_handle determines if the passed handle represents a logical partition
301 * if it does it uses each module in order to probe it and if successful it
302 * returns EFI_SUCCESS.
305 probe_handle(EFI_HANDLE h, EFI_DEVICE_PATH *imgpath, BOOLEAN *preferred)
309 EFI_DEVICE_PATH *devpath;
313 /* Figure out if we're dealing with an actual partition. */
314 status = BS->HandleProtocol(h, &DevicePathGUID, (void **)&devpath);
315 if (status == EFI_UNSUPPORTED)
318 if (status != EFI_SUCCESS) {
319 DPRINTF("\nFailed to query DevicePath (%lu)\n",
320 EFI_ERROR_CODE(status));
325 CHAR16 *text = efi_devpath_name(devpath);
326 DPRINTF("probing: %S\n", text);
327 efi_free_devpath_name(text);
330 status = BS->HandleProtocol(h, &BlockIoProtocolGUID, (void **)&blkio);
331 if (status == EFI_UNSUPPORTED)
334 if (status != EFI_SUCCESS) {
335 DPRINTF("\nFailed to query BlockIoProtocol (%lu)\n",
336 EFI_ERROR_CODE(status));
340 if (!blkio->Media->LogicalPartition)
341 return (EFI_UNSUPPORTED);
343 *preferred = device_paths_match(imgpath, devpath);
345 /* Run through each module, see if it can load this partition */
346 for (i = 0; i < NUM_BOOT_MODULES; i++) {
347 devinfo = malloc(sizeof(*devinfo));
348 if (devinfo == NULL) {
349 DPRINTF("\nFailed to allocate devinfo\n");
352 devinfo->dev = blkio;
353 devinfo->devpath = devpath;
354 devinfo->devhandle = h;
355 devinfo->devdata = NULL;
356 devinfo->preferred = *preferred;
357 devinfo->next = NULL;
359 status = boot_modules[i]->probe(devinfo);
360 if (status == EFI_SUCCESS)
361 return (EFI_SUCCESS);
365 return (EFI_UNSUPPORTED);
369 * probe_handle_status calls probe_handle and outputs the returned status
373 probe_handle_status(EFI_HANDLE h, EFI_DEVICE_PATH *imgpath)
379 status = probe_handle(h, imgpath, &preferred);
383 case EFI_UNSUPPORTED:
385 DPRINTF(" not supported\n");
390 DPRINTF(" supported (preferred)\n");
393 DPRINTF(" supported\n");
398 DPRINTF(" error (%lu)\n", EFI_ERROR_CODE(status));
405 efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE *Xsystab)
408 EFI_LOADED_IMAGE *img;
409 EFI_DEVICE_PATH *imgpath;
411 EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL;
412 SIMPLE_TEXT_OUTPUT_INTERFACE *conout = NULL;
413 UINTN i, max_dim, best_mode, cols, rows, hsize, nhandles;
416 /* Basic initialization*/
419 BS = ST->BootServices;
420 RS = ST->RuntimeServices;
422 /* Set up the console, so printf works. */
423 status = BS->LocateProtocol(&ConsoleControlGUID, NULL,
424 (VOID **)&ConsoleControl);
425 if (status == EFI_SUCCESS)
426 (void)ConsoleControl->SetMode(ConsoleControl,
427 EfiConsoleControlScreenText);
429 * Reset the console and find the best text mode.
432 conout->Reset(conout, TRUE);
433 max_dim = best_mode = 0;
435 status = conout->QueryMode(conout, i, &cols, &rows);
436 if (EFI_ERROR(status))
438 if (cols * rows > max_dim) {
439 max_dim = cols * rows;
444 conout->SetMode(conout, best_mode);
445 conout->EnableCursor(conout, TRUE);
446 conout->ClearScreen(conout);
448 printf("\n>> FreeBSD EFI boot block\n");
449 printf(" Loader path: %s\n\n", PATH_LOADER_EFI);
450 printf(" Initializing modules:");
451 for (i = 0; i < NUM_BOOT_MODULES; i++) {
452 printf(" %s", boot_modules[i]->name);
453 if (boot_modules[i]->init != NULL)
454 boot_modules[i]->init();
458 /* Determine the devpath of our image so we can prefer it. */
459 status = BS->HandleProtocol(IH, &LoadedImageGUID, (VOID**)&img);
461 if (status == EFI_SUCCESS) {
462 text = efi_devpath_name(img->FilePath);
464 printf(" Load Path: %S\n", text);
465 efi_setenv_freebsd_wcs("Boot1Path", text);
466 efi_free_devpath_name(text);
469 status = BS->HandleProtocol(img->DeviceHandle, &DevicePathGUID,
471 if (status != EFI_SUCCESS) {
472 DPRINTF("Failed to get image DevicePath (%lu)\n",
473 EFI_ERROR_CODE(status));
475 text = efi_devpath_name(imgpath);
477 printf(" Load Device: %S\n", text);
478 efi_setenv_freebsd_wcs("Boot1Dev", text);
479 efi_free_devpath_name(text);
484 /* Get all the device handles */
485 hsize = (UINTN)NUM_HANDLES_INIT * sizeof(EFI_HANDLE);
486 handles = malloc(hsize);
487 if (handles == NULL) {
488 printf("Failed to allocate %d handles\n", NUM_HANDLES_INIT);
491 status = BS->LocateHandle(ByProtocol, &BlockIoProtocolGUID, NULL,
496 case EFI_BUFFER_TOO_SMALL:
498 handles = malloc(hsize);
500 efi_panic(EFI_OUT_OF_RESOURCES, "Failed to allocate %d handles\n",
502 status = BS->LocateHandle(ByProtocol, &BlockIoProtocolGUID,
503 NULL, &hsize, handles);
504 if (status != EFI_SUCCESS)
505 efi_panic(status, "Failed to get device handles\n");
508 efi_panic(status, "Failed to get device handles\n");
512 /* Scan all partitions, probing with all modules. */
513 nhandles = hsize / sizeof(*handles);
514 printf(" Probing %zu block devices...", nhandles);
517 for (i = 0; i < nhandles; i++)
518 probe_handle_status(handles[i], imgpath);
521 /* Status summary. */
522 for (i = 0; i < NUM_BOOT_MODULES; i++) {
524 boot_modules[i]->status();
529 /* If we get here, we're out of luck... */
530 efi_panic(EFI_LOAD_ERROR, "No bootable partitions found!");
534 * add_device adds a device to the passed devinfo list.
537 add_device(dev_info_t **devinfop, dev_info_t *devinfo)
541 if (*devinfop == NULL) {
546 for (dev = *devinfop; dev->next != NULL; dev = dev->next)
553 * OK. We totally give up. Exit back to EFI with a sensible status so
554 * it can try the next option on the list.
557 efi_panic(EFI_STATUS s, const char *fmt, ...)
567 BS->Exit(IH, s, 0, NULL);
578 ST->ConOut->OutputString(ST->ConOut, buf);
582 ST->ConOut->OutputString(ST->ConOut, buf);