1ca987d46SWarner Losh /*-
2ca987d46SWarner Losh * Copyright (c) 1998 Robert Nordier
3ca987d46SWarner Losh * All rights reserved.
4ca987d46SWarner Losh * Copyright (c) 2001 Robert Drehmel
5ca987d46SWarner Losh * All rights reserved.
6ca987d46SWarner Losh * Copyright (c) 2014 Nathan Whitehorn
7ca987d46SWarner Losh * All rights reserved.
8ca987d46SWarner Losh * Copyright (c) 2015 Eric McCorkle
9ca987d46SWarner Losh * All rights reserved.
10ca987d46SWarner Losh *
11ca987d46SWarner Losh * Redistribution and use in source and binary forms are freely
12ca987d46SWarner Losh * permitted provided that the above copyright notice and this
13ca987d46SWarner Losh * paragraph and the following disclaimer are duplicated in all
14ca987d46SWarner Losh * such forms.
15ca987d46SWarner Losh *
16ca987d46SWarner Losh * This software is provided "AS IS" and without any express or
17ca987d46SWarner Losh * implied warranties, including, without limitation, the implied
18ca987d46SWarner Losh * warranties of merchantability and fitness for a particular
19ca987d46SWarner Losh * purpose.
20ca987d46SWarner Losh */
21ca987d46SWarner Losh
22ca987d46SWarner Losh #include <sys/param.h>
23ca987d46SWarner Losh #include <machine/elf.h>
24ca987d46SWarner Losh #include <machine/stdarg.h>
25ca987d46SWarner Losh #include <stand.h>
26ca987d46SWarner Losh
27ca987d46SWarner Losh #include <efi.h>
28ca987d46SWarner Losh #include <eficonsctl.h>
29ca987d46SWarner Losh #include <efichar.h>
30ca987d46SWarner Losh
31ca987d46SWarner Losh #include "boot_module.h"
32ca987d46SWarner Losh #include "paths.h"
33f46eb752SWarner Losh #include "proto.h"
34ca987d46SWarner Losh
35ca987d46SWarner Losh static void efi_panic(EFI_STATUS s, const char *fmt, ...) __dead2 __printflike(2, 3);
36ca987d46SWarner Losh
37f46eb752SWarner Losh const boot_module_t *boot_modules[] =
38ca987d46SWarner Losh {
39ca987d46SWarner Losh #ifdef EFI_ZFS_BOOT
40ca987d46SWarner Losh &zfs_module,
41ca987d46SWarner Losh #endif
42ca987d46SWarner Losh #ifdef EFI_UFS_BOOT
43ca987d46SWarner Losh &ufs_module
44ca987d46SWarner Losh #endif
45ca987d46SWarner Losh };
46f46eb752SWarner Losh const UINTN num_boot_modules = nitems(boot_modules);
47ca987d46SWarner Losh
48ca987d46SWarner Losh static EFI_GUID BlockIoProtocolGUID = BLOCK_IO_PROTOCOL;
49ca987d46SWarner Losh static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL;
50ca987d46SWarner Losh static EFI_GUID LoadedImageGUID = LOADED_IMAGE_PROTOCOL;
51ca987d46SWarner Losh static EFI_GUID ConsoleControlGUID = EFI_CONSOLE_CONTROL_PROTOCOL_GUID;
52ca987d46SWarner Losh
5312c470afSToomas Soome static EFI_PHYSICAL_ADDRESS heap;
5412c470afSToomas Soome static UINTN heapsize;
55f28f385bSToomas Soome
56ca987d46SWarner Losh /*
57ca987d46SWarner Losh * try_boot only returns if it fails to load the loader. If it succeeds
58ca987d46SWarner Losh * it simply boots, otherwise it returns the status of last EFI call.
59ca987d46SWarner Losh */
60f46eb752SWarner Losh EFI_STATUS
try_boot(const boot_module_t * mod,dev_info_t * dev,void * loaderbuf,size_t loadersize)61f46eb752SWarner Losh try_boot(const boot_module_t *mod, dev_info_t *dev, void *loaderbuf, size_t loadersize)
62ca987d46SWarner Losh {
63f46eb752SWarner Losh size_t bufsize, cmdsize;
64f46eb752SWarner Losh void *buf;
65ca987d46SWarner Losh char *cmd;
66ca987d46SWarner Losh EFI_HANDLE loaderhandle;
67ca987d46SWarner Losh EFI_LOADED_IMAGE *loaded_image;
68ca987d46SWarner Losh EFI_STATUS status;
69ca987d46SWarner Losh
70ca987d46SWarner Losh /*
71ca987d46SWarner Losh * Read in and parse the command line from /boot.config or /boot/config,
72ca987d46SWarner Losh * if present. We'll pass it the next stage via a simple ASCII
73ca987d46SWarner Losh * string. loader.efi has a hack for ASCII strings, so we'll use that to
74ca987d46SWarner Losh * keep the size down here. We only try to read the alternate file if
75ca987d46SWarner Losh * we get EFI_NOT_FOUND because all other errors mean that the boot_module
76ca987d46SWarner Losh * had troubles with the filesystem. We could return early, but we'll let
77ca987d46SWarner Losh * loading the actual kernel sort all that out. Since these files are
78ca987d46SWarner Losh * optional, we don't report errors in trying to read them.
79ca987d46SWarner Losh */
80ca987d46SWarner Losh cmd = NULL;
81ca987d46SWarner Losh cmdsize = 0;
82ca987d46SWarner Losh status = mod->load(PATH_DOTCONFIG, dev, &buf, &bufsize);
83ca987d46SWarner Losh if (status == EFI_NOT_FOUND)
84ca987d46SWarner Losh status = mod->load(PATH_CONFIG, dev, &buf, &bufsize);
85ca987d46SWarner Losh if (status == EFI_SUCCESS) {
86ca987d46SWarner Losh cmdsize = bufsize + 1;
87ca987d46SWarner Losh cmd = malloc(cmdsize);
88ca987d46SWarner Losh if (cmd == NULL)
89ca987d46SWarner Losh goto errout;
90ca987d46SWarner Losh memcpy(cmd, buf, bufsize);
91ca987d46SWarner Losh cmd[bufsize] = '\0';
92ca987d46SWarner Losh free(buf);
93ca987d46SWarner Losh buf = NULL;
94ca987d46SWarner Losh }
95ca987d46SWarner Losh
9691ac713bSWarner Losh /*
9791ac713bSWarner Losh * See if there's any env variables the module wants to set. If so,
9891ac713bSWarner Losh * append it to any config present.
9991ac713bSWarner Losh */
10091ac713bSWarner Losh if (mod->extra_env != NULL) {
10191ac713bSWarner Losh const char *env = mod->extra_env();
10291ac713bSWarner Losh if (env != NULL) {
10391ac713bSWarner Losh size_t newlen = cmdsize + strlen(env) + 1;
10491ac713bSWarner Losh
10591ac713bSWarner Losh cmd = realloc(cmd, newlen);
10691ac713bSWarner Losh if (cmd == NULL)
10791ac713bSWarner Losh goto errout;
10891ac713bSWarner Losh if (cmdsize > 0)
10991ac713bSWarner Losh strlcat(cmd, " ", newlen);
11091ac713bSWarner Losh strlcat(cmd, env, newlen);
11191ac713bSWarner Losh cmdsize = strlen(cmd);
11291ac713bSWarner Losh free(__DECONST(char *, env));
11391ac713bSWarner Losh }
11491ac713bSWarner Losh }
11591ac713bSWarner Losh
1164cf36aa1SWarner Losh if ((status = BS->LoadImage(TRUE, IH, efi_devpath_last_node(dev->devpath),
117ca987d46SWarner Losh loaderbuf, loadersize, &loaderhandle)) != EFI_SUCCESS) {
118ca987d46SWarner Losh printf("Failed to load image provided by %s, size: %zu, (%lu)\n",
119ca987d46SWarner Losh mod->name, loadersize, EFI_ERROR_CODE(status));
120ca987d46SWarner Losh goto errout;
121ca987d46SWarner Losh }
122ca987d46SWarner Losh
123cefffc0bSToomas Soome status = OpenProtocolByHandle(loaderhandle, &LoadedImageGUID,
124cefffc0bSToomas Soome (void **)&loaded_image);
125cefffc0bSToomas Soome if (status != EFI_SUCCESS) {
126ca987d46SWarner Losh printf("Failed to query LoadedImage provided by %s (%lu)\n",
127ca987d46SWarner Losh mod->name, EFI_ERROR_CODE(status));
128ca987d46SWarner Losh goto errout;
129ca987d46SWarner Losh }
130ca987d46SWarner Losh
131ca987d46SWarner Losh if (cmd != NULL)
132ca987d46SWarner Losh printf(" command args: %s\n", cmd);
133ca987d46SWarner Losh
134ca987d46SWarner Losh loaded_image->DeviceHandle = dev->devhandle;
135ca987d46SWarner Losh loaded_image->LoadOptionsSize = cmdsize;
136ca987d46SWarner Losh loaded_image->LoadOptions = cmd;
137ca987d46SWarner Losh
138ca987d46SWarner Losh DPRINTF("Starting '%s' in 5 seconds...", PATH_LOADER_EFI);
139ca987d46SWarner Losh DSTALL(1000000);
140ca987d46SWarner Losh DPRINTF(".");
141ca987d46SWarner Losh DSTALL(1000000);
142ca987d46SWarner Losh DPRINTF(".");
143ca987d46SWarner Losh DSTALL(1000000);
144ca987d46SWarner Losh DPRINTF(".");
145ca987d46SWarner Losh DSTALL(1000000);
146ca987d46SWarner Losh DPRINTF(".");
147ca987d46SWarner Losh DSTALL(1000000);
148ca987d46SWarner Losh DPRINTF(".\n");
149ca987d46SWarner Losh
150ca987d46SWarner Losh if ((status = BS->StartImage(loaderhandle, NULL, NULL)) !=
151ca987d46SWarner Losh EFI_SUCCESS) {
152ca987d46SWarner Losh printf("Failed to start image provided by %s (%lu)\n",
153ca987d46SWarner Losh mod->name, EFI_ERROR_CODE(status));
154ca987d46SWarner Losh loaded_image->LoadOptionsSize = 0;
155ca987d46SWarner Losh loaded_image->LoadOptions = NULL;
156ca987d46SWarner Losh }
157ca987d46SWarner Losh
158ca987d46SWarner Losh errout:
159ca987d46SWarner Losh if (cmd != NULL)
160ca987d46SWarner Losh free(cmd);
161ca987d46SWarner Losh if (buf != NULL)
162ca987d46SWarner Losh free(buf);
163ca987d46SWarner Losh if (loaderbuf != NULL)
164ca987d46SWarner Losh free(loaderbuf);
165ca987d46SWarner Losh
166ca987d46SWarner Losh return (status);
167ca987d46SWarner Losh }
168ca987d46SWarner Losh
169ca987d46SWarner Losh EFI_STATUS
efi_main(EFI_HANDLE Ximage,EFI_SYSTEM_TABLE * Xsystab)170ca987d46SWarner Losh efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE *Xsystab)
171ca987d46SWarner Losh {
172ca987d46SWarner Losh EFI_HANDLE *handles;
173ca987d46SWarner Losh EFI_LOADED_IMAGE *img;
174ca987d46SWarner Losh EFI_DEVICE_PATH *imgpath;
175ca987d46SWarner Losh EFI_STATUS status;
176ca987d46SWarner Losh EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL;
177ca987d46SWarner Losh SIMPLE_TEXT_OUTPUT_INTERFACE *conout = NULL;
1785f8cfbe1SKyle Evans UINTN i, hsize, nhandles;
179ca987d46SWarner Losh CHAR16 *text;
180ca987d46SWarner Losh
181ca987d46SWarner Losh /* Basic initialization*/
182ca987d46SWarner Losh ST = Xsystab;
183ca987d46SWarner Losh IH = Ximage;
184ca987d46SWarner Losh BS = ST->BootServices;
185ca987d46SWarner Losh RS = ST->RuntimeServices;
186ca987d46SWarner Losh
18712c470afSToomas Soome heapsize = 64 * 1024 * 1024;
18812c470afSToomas Soome status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
18912c470afSToomas Soome EFI_SIZE_TO_PAGES(heapsize), &heap);
19012c470afSToomas Soome if (status != EFI_SUCCESS) {
19112c470afSToomas Soome ST->ConOut->OutputString(ST->ConOut,
19212c470afSToomas Soome __DECONST(CHAR16 *,
19312c470afSToomas Soome L"Failed to allocate memory for heap.\r\n"));
19412c470afSToomas Soome BS->Exit(IH, status, 0, NULL);
19512c470afSToomas Soome }
19612c470afSToomas Soome
19712c470afSToomas Soome setheap((void *)(uintptr_t)heap, (void *)(uintptr_t)(heap + heapsize));
19812c470afSToomas Soome
199ca987d46SWarner Losh /* Set up the console, so printf works. */
200ca987d46SWarner Losh status = BS->LocateProtocol(&ConsoleControlGUID, NULL,
201ca987d46SWarner Losh (VOID **)&ConsoleControl);
202ca987d46SWarner Losh if (status == EFI_SUCCESS)
203ca987d46SWarner Losh (void)ConsoleControl->SetMode(ConsoleControl,
204ca987d46SWarner Losh EfiConsoleControlScreenText);
205ca987d46SWarner Losh /*
2065f8cfbe1SKyle Evans * Reset the console enable the cursor. Later we'll choose a better
2075f8cfbe1SKyle Evans * console size through GOP/UGA.
208ca987d46SWarner Losh */
209ca987d46SWarner Losh conout = ST->ConOut;
210ca987d46SWarner Losh conout->Reset(conout, TRUE);
211807dbf2bSKyle Evans /* Explicitly set conout to mode 0, 80x25 */
212807dbf2bSKyle Evans conout->SetMode(conout, 0);
213ca987d46SWarner Losh conout->EnableCursor(conout, TRUE);
214ca987d46SWarner Losh conout->ClearScreen(conout);
215ca987d46SWarner Losh
216ca987d46SWarner Losh printf("\n>> FreeBSD EFI boot block\n");
217ca987d46SWarner Losh printf(" Loader path: %s\n\n", PATH_LOADER_EFI);
218ca987d46SWarner Losh printf(" Initializing modules:");
219f46eb752SWarner Losh for (i = 0; i < num_boot_modules; i++) {
220ca987d46SWarner Losh printf(" %s", boot_modules[i]->name);
221ca987d46SWarner Losh if (boot_modules[i]->init != NULL)
222ca987d46SWarner Losh boot_modules[i]->init();
223ca987d46SWarner Losh }
224ca987d46SWarner Losh putchar('\n');
225ca987d46SWarner Losh
226f46eb752SWarner Losh /* Fetch all the block I/O handles, we have to search through them later */
227f46eb752SWarner Losh hsize = 0;
228f46eb752SWarner Losh BS->LocateHandle(ByProtocol, &BlockIoProtocolGUID, NULL,
229f46eb752SWarner Losh &hsize, NULL);
230f46eb752SWarner Losh handles = malloc(hsize);
231f46eb752SWarner Losh if (handles == NULL)
232f46eb752SWarner Losh efi_panic(EFI_OUT_OF_RESOURCES, "Failed to allocate %d handles\n",
233f46eb752SWarner Losh hsize);
234f46eb752SWarner Losh status = BS->LocateHandle(ByProtocol, &BlockIoProtocolGUID,
235f46eb752SWarner Losh NULL, &hsize, handles);
236f46eb752SWarner Losh if (status != EFI_SUCCESS)
237f46eb752SWarner Losh efi_panic(status, "Failed to get device handles\n");
238f46eb752SWarner Losh nhandles = hsize / sizeof(*handles);
239f46eb752SWarner Losh
240ca987d46SWarner Losh /* Determine the devpath of our image so we can prefer it. */
241cefffc0bSToomas Soome status = OpenProtocolByHandle(IH, &LoadedImageGUID, (void **)&img);
242ca987d46SWarner Losh imgpath = NULL;
243ca987d46SWarner Losh if (status == EFI_SUCCESS) {
244ca987d46SWarner Losh text = efi_devpath_name(img->FilePath);
245ca987d46SWarner Losh if (text != NULL) {
246ca987d46SWarner Losh printf(" Load Path: %S\n", text);
247ca987d46SWarner Losh efi_setenv_freebsd_wcs("Boot1Path", text);
248ca987d46SWarner Losh efi_free_devpath_name(text);
249ca987d46SWarner Losh }
250ca987d46SWarner Losh
251cefffc0bSToomas Soome status = OpenProtocolByHandle(img->DeviceHandle,
252cefffc0bSToomas Soome &DevicePathGUID, (void **)&imgpath);
253ca987d46SWarner Losh if (status != EFI_SUCCESS) {
254ca987d46SWarner Losh DPRINTF("Failed to get image DevicePath (%lu)\n",
255ca987d46SWarner Losh EFI_ERROR_CODE(status));
256ca987d46SWarner Losh } else {
257ca987d46SWarner Losh text = efi_devpath_name(imgpath);
258ca987d46SWarner Losh if (text != NULL) {
259ca987d46SWarner Losh printf(" Load Device: %S\n", text);
260ca987d46SWarner Losh efi_setenv_freebsd_wcs("Boot1Dev", text);
261ca987d46SWarner Losh efi_free_devpath_name(text);
262ca987d46SWarner Losh }
263ca987d46SWarner Losh }
264ca987d46SWarner Losh }
265ca987d46SWarner Losh
266f46eb752SWarner Losh choice_protocol(handles, nhandles, imgpath);
267ca987d46SWarner Losh
268ca987d46SWarner Losh /* If we get here, we're out of luck... */
269ca987d46SWarner Losh efi_panic(EFI_LOAD_ERROR, "No bootable partitions found!");
270ca987d46SWarner Losh }
271ca987d46SWarner Losh
272ca987d46SWarner Losh /*
273ca987d46SWarner Losh * add_device adds a device to the passed devinfo list.
274ca987d46SWarner Losh */
275ca987d46SWarner Losh void
add_device(dev_info_t ** devinfop,dev_info_t * devinfo)276ca987d46SWarner Losh add_device(dev_info_t **devinfop, dev_info_t *devinfo)
277ca987d46SWarner Losh {
278ca987d46SWarner Losh dev_info_t *dev;
279ca987d46SWarner Losh
280ca987d46SWarner Losh if (*devinfop == NULL) {
281ca987d46SWarner Losh *devinfop = devinfo;
282ca987d46SWarner Losh return;
283ca987d46SWarner Losh }
284ca987d46SWarner Losh
285ca987d46SWarner Losh for (dev = *devinfop; dev->next != NULL; dev = dev->next)
286ca987d46SWarner Losh ;
287ca987d46SWarner Losh
288ca987d46SWarner Losh dev->next = devinfo;
289ca987d46SWarner Losh }
290ca987d46SWarner Losh
291abc23d59SToomas Soome void
efi_exit(EFI_STATUS s)292abc23d59SToomas Soome efi_exit(EFI_STATUS s)
293abc23d59SToomas Soome {
29412c470afSToomas Soome
29512c470afSToomas Soome BS->FreePages(heap, EFI_SIZE_TO_PAGES(heapsize));
296abc23d59SToomas Soome BS->Exit(IH, s, 0, NULL);
297abc23d59SToomas Soome }
298abc23d59SToomas Soome
299abc23d59SToomas Soome void
exit(int error __unused)300abc23d59SToomas Soome exit(int error __unused)
301abc23d59SToomas Soome {
302abc23d59SToomas Soome efi_exit(EFI_LOAD_ERROR);
303abc23d59SToomas Soome }
304abc23d59SToomas Soome
305ca987d46SWarner Losh /*
306ca987d46SWarner Losh * OK. We totally give up. Exit back to EFI with a sensible status so
307ca987d46SWarner Losh * it can try the next option on the list.
308ca987d46SWarner Losh */
309ca987d46SWarner Losh static void
efi_panic(EFI_STATUS s,const char * fmt,...)310ca987d46SWarner Losh efi_panic(EFI_STATUS s, const char *fmt, ...)
311ca987d46SWarner Losh {
312ca987d46SWarner Losh va_list ap;
313ca987d46SWarner Losh
314ca987d46SWarner Losh printf("panic: ");
315ca987d46SWarner Losh va_start(ap, fmt);
316ca987d46SWarner Losh vprintf(fmt, ap);
317ca987d46SWarner Losh va_end(ap);
318ca987d46SWarner Losh printf("\n");
319ca987d46SWarner Losh
320abc23d59SToomas Soome efi_exit(s);
321abc23d59SToomas Soome }
322abc23d59SToomas Soome
getchar(void)323abc23d59SToomas Soome int getchar(void)
324abc23d59SToomas Soome {
325abc23d59SToomas Soome return (-1);
326ca987d46SWarner Losh }
327ca987d46SWarner Losh
328ca987d46SWarner Losh void
putchar(int c)329ca987d46SWarner Losh putchar(int c)
330ca987d46SWarner Losh {
331ca987d46SWarner Losh CHAR16 buf[2];
332ca987d46SWarner Losh
333ca987d46SWarner Losh if (c == '\n') {
334ca987d46SWarner Losh buf[0] = '\r';
335ca987d46SWarner Losh buf[1] = 0;
336ca987d46SWarner Losh ST->ConOut->OutputString(ST->ConOut, buf);
337ca987d46SWarner Losh }
338ca987d46SWarner Losh buf[0] = c;
339ca987d46SWarner Losh buf[1] = 0;
340ca987d46SWarner Losh ST->ConOut->OutputString(ST->ConOut, buf);
341ca987d46SWarner Losh }
342