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>
34 #include "boot_module.h"
38 static void efi_panic(EFI_STATUS s, const char *fmt, ...) __dead2 __printflike(2, 3);
40 const boot_module_t *boot_modules[] =
49 const UINTN num_boot_modules = nitems(boot_modules);
51 static EFI_GUID BlockIoProtocolGUID = BLOCK_IO_PROTOCOL;
52 static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL;
53 static EFI_GUID LoadedImageGUID = LOADED_IMAGE_PROTOCOL;
54 static EFI_GUID ConsoleControlGUID = EFI_CONSOLE_CONTROL_PROTOCOL_GUID;
57 * Provide Malloc / Free / Calloc backed by EFIs AllocatePool / FreePool which ensures
58 * memory is correctly aligned avoiding EFI_INVALID_PARAMETER returns from
63 Malloc(size_t len, const char *file __unused, int line __unused)
67 if (BS->AllocatePool(EfiLoaderData, len, &out) == EFI_SUCCESS)
74 Free(void *buf, const char *file __unused, int line __unused)
77 (void)BS->FreePool(buf);
81 Calloc(size_t n1, size_t n2, const char *file, int line)
87 if ((res = Malloc(bytes, file, line)) != NULL)
94 * try_boot only returns if it fails to load the loader. If it succeeds
95 * it simply boots, otherwise it returns the status of last EFI call.
98 try_boot(const boot_module_t *mod, dev_info_t *dev, void *loaderbuf, size_t loadersize)
100 size_t bufsize, cmdsize;
103 EFI_HANDLE loaderhandle;
104 EFI_LOADED_IMAGE *loaded_image;
108 * Read in and parse the command line from /boot.config or /boot/config,
109 * if present. We'll pass it the next stage via a simple ASCII
110 * string. loader.efi has a hack for ASCII strings, so we'll use that to
111 * keep the size down here. We only try to read the alternate file if
112 * we get EFI_NOT_FOUND because all other errors mean that the boot_module
113 * had troubles with the filesystem. We could return early, but we'll let
114 * loading the actual kernel sort all that out. Since these files are
115 * optional, we don't report errors in trying to read them.
119 status = mod->load(PATH_DOTCONFIG, dev, &buf, &bufsize);
120 if (status == EFI_NOT_FOUND)
121 status = mod->load(PATH_CONFIG, dev, &buf, &bufsize);
122 if (status == EFI_SUCCESS) {
123 cmdsize = bufsize + 1;
124 cmd = malloc(cmdsize);
127 memcpy(cmd, buf, bufsize);
133 if ((status = BS->LoadImage(TRUE, IH, efi_devpath_last_node(dev->devpath),
134 loaderbuf, loadersize, &loaderhandle)) != EFI_SUCCESS) {
135 printf("Failed to load image provided by %s, size: %zu, (%lu)\n",
136 mod->name, loadersize, EFI_ERROR_CODE(status));
140 status = OpenProtocolByHandle(loaderhandle, &LoadedImageGUID,
141 (void **)&loaded_image);
142 if (status != EFI_SUCCESS) {
143 printf("Failed to query LoadedImage provided by %s (%lu)\n",
144 mod->name, EFI_ERROR_CODE(status));
149 printf(" command args: %s\n", cmd);
151 loaded_image->DeviceHandle = dev->devhandle;
152 loaded_image->LoadOptionsSize = cmdsize;
153 loaded_image->LoadOptions = cmd;
155 DPRINTF("Starting '%s' in 5 seconds...", PATH_LOADER_EFI);
167 if ((status = BS->StartImage(loaderhandle, NULL, NULL)) !=
169 printf("Failed to start image provided by %s (%lu)\n",
170 mod->name, EFI_ERROR_CODE(status));
171 loaded_image->LoadOptionsSize = 0;
172 loaded_image->LoadOptions = NULL;
180 if (loaderbuf != NULL)
187 efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE *Xsystab)
190 EFI_LOADED_IMAGE *img;
191 EFI_DEVICE_PATH *imgpath;
193 EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL;
194 SIMPLE_TEXT_OUTPUT_INTERFACE *conout = NULL;
195 UINTN i, hsize, nhandles;
198 /* Basic initialization*/
201 BS = ST->BootServices;
202 RS = ST->RuntimeServices;
204 /* Set up the console, so printf works. */
205 status = BS->LocateProtocol(&ConsoleControlGUID, NULL,
206 (VOID **)&ConsoleControl);
207 if (status == EFI_SUCCESS)
208 (void)ConsoleControl->SetMode(ConsoleControl,
209 EfiConsoleControlScreenText);
211 * Reset the console enable the cursor. Later we'll choose a better
212 * console size through GOP/UGA.
215 conout->Reset(conout, TRUE);
216 /* Explicitly set conout to mode 0, 80x25 */
217 conout->SetMode(conout, 0);
218 conout->EnableCursor(conout, TRUE);
219 conout->ClearScreen(conout);
221 printf("\n>> FreeBSD EFI boot block\n");
222 printf(" Loader path: %s\n\n", PATH_LOADER_EFI);
223 printf(" Initializing modules:");
224 for (i = 0; i < num_boot_modules; i++) {
225 printf(" %s", boot_modules[i]->name);
226 if (boot_modules[i]->init != NULL)
227 boot_modules[i]->init();
231 /* Fetch all the block I/O handles, we have to search through them later */
233 BS->LocateHandle(ByProtocol, &BlockIoProtocolGUID, NULL,
235 handles = malloc(hsize);
237 efi_panic(EFI_OUT_OF_RESOURCES, "Failed to allocate %d handles\n",
239 status = BS->LocateHandle(ByProtocol, &BlockIoProtocolGUID,
240 NULL, &hsize, handles);
241 if (status != EFI_SUCCESS)
242 efi_panic(status, "Failed to get device handles\n");
243 nhandles = hsize / sizeof(*handles);
245 /* Determine the devpath of our image so we can prefer it. */
246 status = OpenProtocolByHandle(IH, &LoadedImageGUID, (void **)&img);
248 if (status == EFI_SUCCESS) {
249 text = efi_devpath_name(img->FilePath);
251 printf(" Load Path: %S\n", text);
252 efi_setenv_freebsd_wcs("Boot1Path", text);
253 efi_free_devpath_name(text);
256 status = OpenProtocolByHandle(img->DeviceHandle,
257 &DevicePathGUID, (void **)&imgpath);
258 if (status != EFI_SUCCESS) {
259 DPRINTF("Failed to get image DevicePath (%lu)\n",
260 EFI_ERROR_CODE(status));
262 text = efi_devpath_name(imgpath);
264 printf(" Load Device: %S\n", text);
265 efi_setenv_freebsd_wcs("Boot1Dev", text);
266 efi_free_devpath_name(text);
271 choice_protocol(handles, nhandles, imgpath);
273 /* If we get here, we're out of luck... */
274 efi_panic(EFI_LOAD_ERROR, "No bootable partitions found!");
278 * add_device adds a device to the passed devinfo list.
281 add_device(dev_info_t **devinfop, dev_info_t *devinfo)
285 if (*devinfop == NULL) {
290 for (dev = *devinfop; dev->next != NULL; dev = dev->next)
297 * OK. We totally give up. Exit back to EFI with a sensible status so
298 * it can try the next option on the list.
301 efi_panic(EFI_STATUS s, const char *fmt, ...)
311 BS->Exit(IH, s, 0, NULL);
322 ST->ConOut->OutputString(ST->ConOut, buf);
326 ST->ConOut->OutputString(ST->ConOut, buf);