xref: /freebsd/stand/efi/boot1/boot1.c (revision 6683132d54bd6d589889e43dabdc53d35e38a028)
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 __FBSDID("$FreeBSD$");
24 
25 #include <sys/param.h>
26 #include <machine/elf.h>
27 #include <machine/stdarg.h>
28 #include <stand.h>
29 
30 #include <efi.h>
31 #include <eficonsctl.h>
32 #include <efichar.h>
33 
34 #include "boot_module.h"
35 #include "paths.h"
36 #include "proto.h"
37 
38 static void efi_panic(EFI_STATUS s, const char *fmt, ...) __dead2 __printflike(2, 3);
39 
40 const boot_module_t *boot_modules[] =
41 {
42 #ifdef EFI_ZFS_BOOT
43 	&zfs_module,
44 #endif
45 #ifdef EFI_UFS_BOOT
46 	&ufs_module
47 #endif
48 };
49 const UINTN num_boot_modules = nitems(boot_modules);
50 
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;
55 
56 /*
57  * Provide Malloc / Free / Calloc backed by EFIs AllocatePool / FreePool which ensures
58  * memory is correctly aligned avoiding EFI_INVALID_PARAMETER returns from
59  * EFI methods.
60  */
61 
62 void *
63 Malloc(size_t len, const char *file __unused, int line __unused)
64 {
65 	void *out;
66 
67 	if (BS->AllocatePool(EfiLoaderData, len, &out) == EFI_SUCCESS)
68 		return (out);
69 
70 	return (NULL);
71 }
72 
73 void
74 Free(void *buf, const char *file __unused, int line __unused)
75 {
76 	if (buf != NULL)
77 		(void)BS->FreePool(buf);
78 }
79 
80 void *
81 Calloc(size_t n1, size_t n2, const char *file, int line)
82 {
83 	size_t bytes;
84 	void *res;
85 
86 	bytes = n1 * n2;
87 	if ((res = Malloc(bytes, file, line)) != NULL)
88 		bzero(res, bytes);
89 
90 	return (res);
91 }
92 
93 /*
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.
96  */
97 EFI_STATUS
98 try_boot(const boot_module_t *mod, dev_info_t *dev, void *loaderbuf, size_t loadersize)
99 {
100 	size_t bufsize, cmdsize;
101 	void *buf;
102 	char *cmd;
103 	EFI_HANDLE loaderhandle;
104 	EFI_LOADED_IMAGE *loaded_image;
105 	EFI_STATUS status;
106 
107 	/*
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.
116 	 */
117 	cmd = NULL;
118 	cmdsize = 0;
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);
125 		if (cmd == NULL)
126 			goto errout;
127 		memcpy(cmd, buf, bufsize);
128 		cmd[bufsize] = '\0';
129 		free(buf);
130 		buf = NULL;
131 	}
132 
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));
137 		goto errout;
138 	}
139 
140 	if ((status = BS->HandleProtocol(loaderhandle, &LoadedImageGUID,
141 	    (VOID**)&loaded_image)) != EFI_SUCCESS) {
142 		printf("Failed to query LoadedImage provided by %s (%lu)\n",
143 		    mod->name, EFI_ERROR_CODE(status));
144 		goto errout;
145 	}
146 
147 	if (cmd != NULL)
148 		printf("    command args: %s\n", cmd);
149 
150 	loaded_image->DeviceHandle = dev->devhandle;
151 	loaded_image->LoadOptionsSize = cmdsize;
152 	loaded_image->LoadOptions = cmd;
153 
154 	DPRINTF("Starting '%s' in 5 seconds...", PATH_LOADER_EFI);
155 	DSTALL(1000000);
156 	DPRINTF(".");
157 	DSTALL(1000000);
158 	DPRINTF(".");
159 	DSTALL(1000000);
160 	DPRINTF(".");
161 	DSTALL(1000000);
162 	DPRINTF(".");
163 	DSTALL(1000000);
164 	DPRINTF(".\n");
165 
166 	if ((status = BS->StartImage(loaderhandle, NULL, NULL)) !=
167 	    EFI_SUCCESS) {
168 		printf("Failed to start image provided by %s (%lu)\n",
169 		    mod->name, EFI_ERROR_CODE(status));
170 		loaded_image->LoadOptionsSize = 0;
171 		loaded_image->LoadOptions = NULL;
172 	}
173 
174 errout:
175 	if (cmd != NULL)
176 		free(cmd);
177 	if (buf != NULL)
178 		free(buf);
179 	if (loaderbuf != NULL)
180 		free(loaderbuf);
181 
182 	return (status);
183 }
184 
185 EFI_STATUS
186 efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE *Xsystab)
187 {
188 	EFI_HANDLE *handles;
189 	EFI_LOADED_IMAGE *img;
190 	EFI_DEVICE_PATH *imgpath;
191 	EFI_STATUS status;
192 	EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL;
193 	SIMPLE_TEXT_OUTPUT_INTERFACE *conout = NULL;
194 	UINTN i, hsize, nhandles;
195 	CHAR16 *text;
196 
197 	/* Basic initialization*/
198 	ST = Xsystab;
199 	IH = Ximage;
200 	BS = ST->BootServices;
201 	RS = ST->RuntimeServices;
202 
203 	/* Set up the console, so printf works. */
204 	status = BS->LocateProtocol(&ConsoleControlGUID, NULL,
205 	    (VOID **)&ConsoleControl);
206 	if (status == EFI_SUCCESS)
207 		(void)ConsoleControl->SetMode(ConsoleControl,
208 		    EfiConsoleControlScreenText);
209 	/*
210 	 * Reset the console enable the cursor. Later we'll choose a better
211 	 * console size through GOP/UGA.
212 	 */
213 	conout = ST->ConOut;
214 	conout->Reset(conout, TRUE);
215 	/* Explicitly set conout to mode 0, 80x25 */
216 	conout->SetMode(conout, 0);
217 	conout->EnableCursor(conout, TRUE);
218 	conout->ClearScreen(conout);
219 
220 	printf("\n>> FreeBSD EFI boot block\n");
221 	printf("   Loader path: %s\n\n", PATH_LOADER_EFI);
222 	printf("   Initializing modules:");
223 	for (i = 0; i < num_boot_modules; i++) {
224 		printf(" %s", boot_modules[i]->name);
225 		if (boot_modules[i]->init != NULL)
226 			boot_modules[i]->init();
227 	}
228 	putchar('\n');
229 
230 	/* Fetch all the block I/O handles, we have to search through them later */
231 	hsize = 0;
232 	BS->LocateHandle(ByProtocol, &BlockIoProtocolGUID, NULL,
233 	    &hsize, NULL);
234 	handles = malloc(hsize);
235 	if (handles == NULL)
236 		efi_panic(EFI_OUT_OF_RESOURCES, "Failed to allocate %d handles\n",
237 		    hsize);
238 	status = BS->LocateHandle(ByProtocol, &BlockIoProtocolGUID,
239 	    NULL, &hsize, handles);
240 	if (status != EFI_SUCCESS)
241 		efi_panic(status, "Failed to get device handles\n");
242 	nhandles = hsize / sizeof(*handles);
243 
244 	/* Determine the devpath of our image so we can prefer it. */
245 	status = BS->HandleProtocol(IH, &LoadedImageGUID, (VOID**)&img);
246 	imgpath = NULL;
247 	if (status == EFI_SUCCESS) {
248 		text = efi_devpath_name(img->FilePath);
249 		if (text != NULL) {
250 			printf("   Load Path: %S\n", text);
251 			efi_setenv_freebsd_wcs("Boot1Path", text);
252 			efi_free_devpath_name(text);
253 		}
254 
255 		status = BS->HandleProtocol(img->DeviceHandle, &DevicePathGUID,
256 		    (void **)&imgpath);
257 		if (status != EFI_SUCCESS) {
258 			DPRINTF("Failed to get image DevicePath (%lu)\n",
259 			    EFI_ERROR_CODE(status));
260 		} else {
261 			text = efi_devpath_name(imgpath);
262 			if (text != NULL) {
263 				printf("   Load Device: %S\n", text);
264 				efi_setenv_freebsd_wcs("Boot1Dev", text);
265 				efi_free_devpath_name(text);
266 			}
267 		}
268 	}
269 
270 	choice_protocol(handles, nhandles, imgpath);
271 
272 	/* If we get here, we're out of luck... */
273 	efi_panic(EFI_LOAD_ERROR, "No bootable partitions found!");
274 }
275 
276 /*
277  * add_device adds a device to the passed devinfo list.
278  */
279 void
280 add_device(dev_info_t **devinfop, dev_info_t *devinfo)
281 {
282 	dev_info_t *dev;
283 
284 	if (*devinfop == NULL) {
285 		*devinfop = devinfo;
286 		return;
287 	}
288 
289 	for (dev = *devinfop; dev->next != NULL; dev = dev->next)
290 		;
291 
292 	dev->next = devinfo;
293 }
294 
295 /*
296  * OK. We totally give up. Exit back to EFI with a sensible status so
297  * it can try the next option on the list.
298  */
299 static void
300 efi_panic(EFI_STATUS s, const char *fmt, ...)
301 {
302 	va_list ap;
303 
304 	printf("panic: ");
305 	va_start(ap, fmt);
306 	vprintf(fmt, ap);
307 	va_end(ap);
308 	printf("\n");
309 
310 	BS->Exit(IH, s, 0, NULL);
311 }
312 
313 void
314 putchar(int c)
315 {
316 	CHAR16 buf[2];
317 
318 	if (c == '\n') {
319 		buf[0] = '\r';
320 		buf[1] = 0;
321 		ST->ConOut->OutputString(ST->ConOut, buf);
322 	}
323 	buf[0] = c;
324 	buf[1] = 0;
325 	ST->ConOut->OutputString(ST->ConOut, buf);
326 }
327