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