xref: /freebsd/stand/efi/boot1/boot1.c (revision 7c43148a)
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