boot1.c revision 332156
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: stable/11/stand/efi/boot1/boot1.c 332156 2018-04-06 21:50:09Z kevans $");
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
37static void efi_panic(EFI_STATUS s, const char *fmt, ...) __dead2 __printflike(2, 3);
38
39static const boot_module_t *boot_modules[] =
40{
41#ifdef EFI_ZFS_BOOT
42	&zfs_module,
43#endif
44#ifdef EFI_UFS_BOOT
45	&ufs_module
46#endif
47};
48
49#define	NUM_BOOT_MODULES	nitems(boot_modules)
50/* The initial number of handles used to query EFI for partitions. */
51#define NUM_HANDLES_INIT	24
52
53static EFI_GUID BlockIoProtocolGUID = BLOCK_IO_PROTOCOL;
54static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL;
55static EFI_GUID LoadedImageGUID = LOADED_IMAGE_PROTOCOL;
56static EFI_GUID ConsoleControlGUID = EFI_CONSOLE_CONTROL_PROTOCOL_GUID;
57
58/*
59 * Provide Malloc / Free backed by EFIs AllocatePool / FreePool which ensures
60 * memory is correctly aligned avoiding EFI_INVALID_PARAMETER returns from
61 * EFI methods.
62 */
63void *
64Malloc(size_t len, const char *file __unused, int line __unused)
65{
66	void *out;
67
68	if (BS->AllocatePool(EfiLoaderData, len, &out) == EFI_SUCCESS)
69		return (out);
70
71	return (NULL);
72}
73
74void
75Free(void *buf, const char *file __unused, int line __unused)
76{
77	if (buf != NULL)
78		(void)BS->FreePool(buf);
79}
80
81/*
82 * nodes_match returns TRUE if the imgpath isn't NULL and the nodes match,
83 * FALSE otherwise.
84 */
85static BOOLEAN
86nodes_match(EFI_DEVICE_PATH *imgpath, EFI_DEVICE_PATH *devpath)
87{
88	size_t len;
89
90	if (imgpath == NULL || imgpath->Type != devpath->Type ||
91	    imgpath->SubType != devpath->SubType)
92		return (FALSE);
93
94	len = DevicePathNodeLength(imgpath);
95	if (len != DevicePathNodeLength(devpath))
96		return (FALSE);
97
98	return (memcmp(imgpath, devpath, (size_t)len) == 0);
99}
100
101/*
102 * device_paths_match returns TRUE if the imgpath isn't NULL and all nodes
103 * in imgpath and devpath match up to their respective occurrences of a
104 * media node, FALSE otherwise.
105 */
106static BOOLEAN
107device_paths_match(EFI_DEVICE_PATH *imgpath, EFI_DEVICE_PATH *devpath)
108{
109
110	if (imgpath == NULL)
111		return (FALSE);
112
113	while (!IsDevicePathEnd(imgpath) && !IsDevicePathEnd(devpath)) {
114		if (IsDevicePathType(imgpath, MEDIA_DEVICE_PATH) &&
115		    IsDevicePathType(devpath, MEDIA_DEVICE_PATH))
116			return (TRUE);
117
118		if (!nodes_match(imgpath, devpath))
119			return (FALSE);
120
121		imgpath = NextDevicePathNode(imgpath);
122		devpath = NextDevicePathNode(devpath);
123	}
124
125	return (FALSE);
126}
127
128/*
129 * devpath_last returns the last non-path end node in devpath.
130 */
131static EFI_DEVICE_PATH *
132devpath_last(EFI_DEVICE_PATH *devpath)
133{
134
135	while (!IsDevicePathEnd(NextDevicePathNode(devpath)))
136		devpath = NextDevicePathNode(devpath);
137
138	return (devpath);
139}
140
141/*
142 * load_loader attempts to load the loader image data.
143 *
144 * It tries each module and its respective devices, identified by mod->probe,
145 * in order until a successful load occurs at which point it returns EFI_SUCCESS
146 * and EFI_NOT_FOUND otherwise.
147 *
148 * Only devices which have preferred matching the preferred parameter are tried.
149 */
150static EFI_STATUS
151load_loader(const boot_module_t **modp, dev_info_t **devinfop, void **bufp,
152    size_t *bufsize, BOOLEAN preferred)
153{
154	UINTN i;
155	dev_info_t *dev;
156	const boot_module_t *mod;
157
158	for (i = 0; i < NUM_BOOT_MODULES; i++) {
159		mod = boot_modules[i];
160		for (dev = mod->devices(); dev != NULL; dev = dev->next) {
161			if (dev->preferred != preferred)
162				continue;
163
164			if (mod->load(PATH_LOADER_EFI, dev, bufp, bufsize) ==
165			    EFI_SUCCESS) {
166				*devinfop = dev;
167				*modp = mod;
168				return (EFI_SUCCESS);
169			}
170		}
171	}
172
173	return (EFI_NOT_FOUND);
174}
175
176/*
177 * try_boot only returns if it fails to load the loader. If it succeeds
178 * it simply boots, otherwise it returns the status of last EFI call.
179 */
180static EFI_STATUS
181try_boot(void)
182{
183	size_t bufsize, loadersize, cmdsize;
184	void *buf, *loaderbuf;
185	char *cmd;
186	dev_info_t *dev;
187	const boot_module_t *mod;
188	EFI_HANDLE loaderhandle;
189	EFI_LOADED_IMAGE *loaded_image;
190	EFI_STATUS status;
191
192	status = load_loader(&mod, &dev, &loaderbuf, &loadersize, TRUE);
193	if (status != EFI_SUCCESS) {
194		status = load_loader(&mod, &dev, &loaderbuf, &loadersize,
195		    FALSE);
196		if (status != EFI_SUCCESS) {
197			printf("Failed to load '%s'\n", PATH_LOADER_EFI);
198			return (status);
199		}
200	}
201
202	/*
203	 * Read in and parse the command line from /boot.config or /boot/config,
204	 * if present. We'll pass it the next stage via a simple ASCII
205	 * string. loader.efi has a hack for ASCII strings, so we'll use that to
206	 * keep the size down here. We only try to read the alternate file if
207	 * we get EFI_NOT_FOUND because all other errors mean that the boot_module
208	 * had troubles with the filesystem. We could return early, but we'll let
209	 * loading the actual kernel sort all that out. Since these files are
210	 * optional, we don't report errors in trying to read them.
211	 */
212	cmd = NULL;
213	cmdsize = 0;
214	status = mod->load(PATH_DOTCONFIG, dev, &buf, &bufsize);
215	if (status == EFI_NOT_FOUND)
216		status = mod->load(PATH_CONFIG, dev, &buf, &bufsize);
217	if (status == EFI_SUCCESS) {
218		cmdsize = bufsize + 1;
219		cmd = malloc(cmdsize);
220		if (cmd == NULL)
221			goto errout;
222		memcpy(cmd, buf, bufsize);
223		cmd[bufsize] = '\0';
224		free(buf);
225		buf = NULL;
226	}
227
228	if ((status = BS->LoadImage(TRUE, IH, devpath_last(dev->devpath),
229	    loaderbuf, loadersize, &loaderhandle)) != EFI_SUCCESS) {
230		printf("Failed to load image provided by %s, size: %zu, (%lu)\n",
231		     mod->name, loadersize, EFI_ERROR_CODE(status));
232		goto errout;
233	}
234
235	if ((status = BS->HandleProtocol(loaderhandle, &LoadedImageGUID,
236	    (VOID**)&loaded_image)) != EFI_SUCCESS) {
237		printf("Failed to query LoadedImage provided by %s (%lu)\n",
238		    mod->name, EFI_ERROR_CODE(status));
239		goto errout;
240	}
241
242	if (cmd != NULL)
243		printf("    command args: %s\n", cmd);
244
245	loaded_image->DeviceHandle = dev->devhandle;
246	loaded_image->LoadOptionsSize = cmdsize;
247	loaded_image->LoadOptions = cmd;
248
249	DPRINTF("Starting '%s' in 5 seconds...", PATH_LOADER_EFI);
250	DSTALL(1000000);
251	DPRINTF(".");
252	DSTALL(1000000);
253	DPRINTF(".");
254	DSTALL(1000000);
255	DPRINTF(".");
256	DSTALL(1000000);
257	DPRINTF(".");
258	DSTALL(1000000);
259	DPRINTF(".\n");
260
261	if ((status = BS->StartImage(loaderhandle, NULL, NULL)) !=
262	    EFI_SUCCESS) {
263		printf("Failed to start image provided by %s (%lu)\n",
264		    mod->name, EFI_ERROR_CODE(status));
265		loaded_image->LoadOptionsSize = 0;
266		loaded_image->LoadOptions = NULL;
267	}
268
269errout:
270	if (cmd != NULL)
271		free(cmd);
272	if (buf != NULL)
273		free(buf);
274	if (loaderbuf != NULL)
275		free(loaderbuf);
276
277	return (status);
278}
279
280/*
281 * probe_handle determines if the passed handle represents a logical partition
282 * if it does it uses each module in order to probe it and if successful it
283 * returns EFI_SUCCESS.
284 */
285static EFI_STATUS
286probe_handle(EFI_HANDLE h, EFI_DEVICE_PATH *imgpath, BOOLEAN *preferred)
287{
288	dev_info_t *devinfo;
289	EFI_BLOCK_IO *blkio;
290	EFI_DEVICE_PATH *devpath;
291	EFI_STATUS status;
292	UINTN i;
293
294	/* Figure out if we're dealing with an actual partition. */
295	status = BS->HandleProtocol(h, &DevicePathGUID, (void **)&devpath);
296	if (status == EFI_UNSUPPORTED)
297		return (status);
298
299	if (status != EFI_SUCCESS) {
300		DPRINTF("\nFailed to query DevicePath (%lu)\n",
301		    EFI_ERROR_CODE(status));
302		return (status);
303	}
304#ifdef EFI_DEBUG
305	{
306		CHAR16 *text = efi_devpath_name(devpath);
307		DPRINTF("probing: %S\n", text);
308		efi_free_devpath_name(text);
309	}
310#endif
311	status = BS->HandleProtocol(h, &BlockIoProtocolGUID, (void **)&blkio);
312	if (status == EFI_UNSUPPORTED)
313		return (status);
314
315	if (status != EFI_SUCCESS) {
316		DPRINTF("\nFailed to query BlockIoProtocol (%lu)\n",
317		    EFI_ERROR_CODE(status));
318		return (status);
319	}
320
321	if (!blkio->Media->LogicalPartition)
322		return (EFI_UNSUPPORTED);
323
324	*preferred = device_paths_match(imgpath, devpath);
325
326	/* Run through each module, see if it can load this partition */
327	for (i = 0; i < NUM_BOOT_MODULES; i++) {
328		devinfo = malloc(sizeof(*devinfo));
329		if (devinfo == NULL) {
330			DPRINTF("\nFailed to allocate devinfo\n");
331			continue;
332		}
333		devinfo->dev = blkio;
334		devinfo->devpath = devpath;
335		devinfo->devhandle = h;
336		devinfo->devdata = NULL;
337		devinfo->preferred = *preferred;
338		devinfo->next = NULL;
339
340		status = boot_modules[i]->probe(devinfo);
341		if (status == EFI_SUCCESS)
342			return (EFI_SUCCESS);
343		free(devinfo);
344	}
345
346	return (EFI_UNSUPPORTED);
347}
348
349/*
350 * probe_handle_status calls probe_handle and outputs the returned status
351 * of the call.
352 */
353static void
354probe_handle_status(EFI_HANDLE h, EFI_DEVICE_PATH *imgpath)
355{
356	EFI_STATUS status;
357	BOOLEAN preferred;
358
359	preferred = FALSE;
360	status = probe_handle(h, imgpath, &preferred);
361
362	DPRINTF("probe: ");
363	switch (status) {
364	case EFI_UNSUPPORTED:
365		printf(".");
366		DPRINTF(" not supported\n");
367		break;
368	case EFI_SUCCESS:
369		if (preferred) {
370			printf("%c", '*');
371			DPRINTF(" supported (preferred)\n");
372		} else {
373			printf("%c", '+');
374			DPRINTF(" supported\n");
375		}
376		break;
377	default:
378		printf("x");
379		DPRINTF(" error (%lu)\n", EFI_ERROR_CODE(status));
380		break;
381	}
382	DSTALL(500000);
383}
384
385EFI_STATUS
386efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE *Xsystab)
387{
388	EFI_HANDLE *handles;
389	EFI_LOADED_IMAGE *img;
390	EFI_DEVICE_PATH *imgpath;
391	EFI_STATUS status;
392	EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL;
393	SIMPLE_TEXT_OUTPUT_INTERFACE *conout = NULL;
394	UINTN i, max_dim, best_mode, cols, rows, hsize, nhandles;
395	CHAR16 *text;
396	UINT16 boot_current;
397	size_t sz;
398	UINT16 boot_order[100];
399
400	/* Basic initialization*/
401	ST = Xsystab;
402	IH = Ximage;
403	BS = ST->BootServices;
404	RS = ST->RuntimeServices;
405
406	/* Set up the console, so printf works. */
407	status = BS->LocateProtocol(&ConsoleControlGUID, NULL,
408	    (VOID **)&ConsoleControl);
409	if (status == EFI_SUCCESS)
410		(void)ConsoleControl->SetMode(ConsoleControl,
411		    EfiConsoleControlScreenText);
412	/*
413	 * Reset the console and find the best text mode.
414	 */
415	conout = ST->ConOut;
416	conout->Reset(conout, TRUE);
417	max_dim = best_mode = 0;
418	for (i = 0; ; i++) {
419		status = conout->QueryMode(conout, i, &cols, &rows);
420		if (EFI_ERROR(status))
421			break;
422		if (cols * rows > max_dim) {
423			max_dim = cols * rows;
424			best_mode = i;
425		}
426	}
427	if (max_dim > 0)
428		conout->SetMode(conout, best_mode);
429	conout->EnableCursor(conout, TRUE);
430	conout->ClearScreen(conout);
431
432	printf("\n>> FreeBSD EFI boot block\n");
433	printf("   Loader path: %s\n\n", PATH_LOADER_EFI);
434	printf("   Initializing modules:");
435	for (i = 0; i < NUM_BOOT_MODULES; i++) {
436		printf(" %s", boot_modules[i]->name);
437		if (boot_modules[i]->init != NULL)
438			boot_modules[i]->init();
439	}
440	putchar('\n');
441
442	/* Determine the devpath of our image so we can prefer it. */
443	status = BS->HandleProtocol(IH, &LoadedImageGUID, (VOID**)&img);
444	imgpath = NULL;
445	if (status == EFI_SUCCESS) {
446		text = efi_devpath_name(img->FilePath);
447		if (text != NULL) {
448			printf("   Load Path: %S\n", text);
449			efi_setenv_freebsd_wcs("Boot1Path", text);
450			efi_free_devpath_name(text);
451		}
452
453		status = BS->HandleProtocol(img->DeviceHandle, &DevicePathGUID,
454		    (void **)&imgpath);
455		if (status != EFI_SUCCESS) {
456			DPRINTF("Failed to get image DevicePath (%lu)\n",
457			    EFI_ERROR_CODE(status));
458		} else {
459			text = efi_devpath_name(imgpath);
460			if (text != NULL) {
461				printf("   Load Device: %S\n", text);
462				efi_setenv_freebsd_wcs("Boot1Dev", text);
463				efi_free_devpath_name(text);
464			}
465		}
466	}
467
468	boot_current = 0;
469	sz = sizeof(boot_current);
470	if (efi_global_getenv("BootCurrent", &boot_current, &sz) == EFI_SUCCESS) {
471		printf("   BootCurrent: %04x\n", boot_current);
472
473		sz = sizeof(boot_order);
474		if (efi_global_getenv("BootOrder", &boot_order, &sz) == EFI_SUCCESS) {
475			printf("   BootOrder:");
476			for (i = 0; i < sz / sizeof(boot_order[0]); i++)
477				printf(" %04x%s", boot_order[i],
478				    boot_order[i] == boot_current ? "[*]" : "");
479			printf("\n");
480		}
481	}
482
483#ifdef TEST_FAILURE
484	/*
485	 * For testing failover scenarios, it's nice to be able to fail fast.
486	 * Define TEST_FAILURE to create a boot1.efi that always fails after
487	 * reporting the boot manager protocol details.
488	 */
489	BS->Exit(IH, EFI_OUT_OF_RESOURCES, 0, NULL);
490#endif
491
492	/* Get all the device handles */
493	hsize = (UINTN)NUM_HANDLES_INIT * sizeof(EFI_HANDLE);
494	handles = malloc(hsize);
495	if (handles == NULL)
496		printf("Failed to allocate %d handles\n", NUM_HANDLES_INIT);
497
498	status = BS->LocateHandle(ByProtocol, &BlockIoProtocolGUID, NULL,
499	    &hsize, handles);
500	switch (status) {
501	case EFI_SUCCESS:
502		break;
503	case EFI_BUFFER_TOO_SMALL:
504		free(handles);
505		handles = malloc(hsize);
506		if (handles == NULL)
507			efi_panic(EFI_OUT_OF_RESOURCES, "Failed to allocate %d handles\n",
508			    NUM_HANDLES_INIT);
509		status = BS->LocateHandle(ByProtocol, &BlockIoProtocolGUID,
510		    NULL, &hsize, handles);
511		if (status != EFI_SUCCESS)
512			efi_panic(status, "Failed to get device handles\n");
513		break;
514	default:
515		efi_panic(status, "Failed to get device handles\n");
516		break;
517	}
518
519	/* Scan all partitions, probing with all modules. */
520	nhandles = hsize / sizeof(*handles);
521	printf("   Probing %zu block devices...", nhandles);
522	DPRINTF("\n");
523
524	for (i = 0; i < nhandles; i++)
525		probe_handle_status(handles[i], imgpath);
526	printf(" done\n");
527
528	/* Status summary. */
529	for (i = 0; i < NUM_BOOT_MODULES; i++) {
530		printf("    ");
531		boot_modules[i]->status();
532	}
533
534	try_boot();
535
536	/* If we get here, we're out of luck... */
537	efi_panic(EFI_LOAD_ERROR, "No bootable partitions found!");
538}
539
540/*
541 * add_device adds a device to the passed devinfo list.
542 */
543void
544add_device(dev_info_t **devinfop, dev_info_t *devinfo)
545{
546	dev_info_t *dev;
547
548	if (*devinfop == NULL) {
549		*devinfop = devinfo;
550		return;
551	}
552
553	for (dev = *devinfop; dev->next != NULL; dev = dev->next)
554		;
555
556	dev->next = devinfo;
557}
558
559/*
560 * OK. We totally give up. Exit back to EFI with a sensible status so
561 * it can try the next option on the list.
562 */
563static void
564efi_panic(EFI_STATUS s, const char *fmt, ...)
565{
566	va_list ap;
567
568	printf("panic: ");
569	va_start(ap, fmt);
570	vprintf(fmt, ap);
571	va_end(ap);
572	printf("\n");
573
574	BS->Exit(IH, s, 0, NULL);
575}
576
577void
578putchar(int c)
579{
580	CHAR16 buf[2];
581
582	if (c == '\n') {
583		buf[0] = '\r';
584		buf[1] = 0;
585		ST->ConOut->OutputString(ST->ConOut, buf);
586	}
587	buf[0] = c;
588	buf[1] = 0;
589	ST->ConOut->OutputString(ST->ConOut, buf);
590}
591