1264391Snwhitehorn/*-
2264391Snwhitehorn * Copyright (c) 1998 Robert Nordier
3264391Snwhitehorn * All rights reserved.
4264391Snwhitehorn * Copyright (c) 2001 Robert Drehmel
5264391Snwhitehorn * All rights reserved.
6264404Snwhitehorn * Copyright (c) 2014 Nathan Whitehorn
7264404Snwhitehorn * All rights reserved.
8294060Ssmh * Copyright (c) 2015 Eric McCorkle
9294060Ssmh * All rights reserved.
10264391Snwhitehorn *
11264391Snwhitehorn * Redistribution and use in source and binary forms are freely
12264391Snwhitehorn * permitted provided that the above copyright notice and this
13264391Snwhitehorn * paragraph and the following disclaimer are duplicated in all
14264391Snwhitehorn * such forms.
15264391Snwhitehorn *
16264391Snwhitehorn * This software is provided "AS IS" and without any express or
17264391Snwhitehorn * implied warranties, including, without limitation, the implied
18264391Snwhitehorn * warranties of merchantability and fitness for a particular
19264391Snwhitehorn * purpose.
20264391Snwhitehorn */
21264391Snwhitehorn
22264391Snwhitehorn#include <sys/cdefs.h>
23264391Snwhitehorn__FBSDID("$FreeBSD: stable/11/stand/efi/boot1/boot1.c 332156 2018-04-06 21:50:09Z kevans $");
24264391Snwhitehorn
25264391Snwhitehorn#include <sys/param.h>
26264391Snwhitehorn#include <machine/elf.h>
27264391Snwhitehorn#include <machine/stdarg.h>
28293460Ssmh#include <stand.h>
29264391Snwhitehorn
30264391Snwhitehorn#include <efi.h>
31271762Semaste#include <eficonsctl.h>
32329114Skevans#include <efichar.h>
33264391Snwhitehorn
34329140Skevans#include "boot_module.h"
35294765Simp#include "paths.h"
36294060Ssmh
37329114Skevansstatic void efi_panic(EFI_STATUS s, const char *fmt, ...) __dead2 __printflike(2, 3);
38329114Skevans
39329140Skevansstatic const boot_module_t *boot_modules[] =
40329140Skevans{
41294068Ssmh#ifdef EFI_ZFS_BOOT
42329140Skevans	&zfs_module,
43294068Ssmh#endif
44294060Ssmh#ifdef EFI_UFS_BOOT
45329140Skevans	&ufs_module
46294060Ssmh#endif
47294060Ssmh};
48264391Snwhitehorn
49329140Skevans#define	NUM_BOOT_MODULES	nitems(boot_modules)
50294060Ssmh/* The initial number of handles used to query EFI for partitions. */
51294060Ssmh#define NUM_HANDLES_INIT	24
52294060Ssmh
53329140Skevansstatic EFI_GUID BlockIoProtocolGUID = BLOCK_IO_PROTOCOL;
54264391Snwhitehornstatic EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL;
55264391Snwhitehornstatic EFI_GUID LoadedImageGUID = LOADED_IMAGE_PROTOCOL;
56329140Skevansstatic EFI_GUID ConsoleControlGUID = EFI_CONSOLE_CONTROL_PROTOCOL_GUID;
57264391Snwhitehorn
58329140Skevans/*
59329140Skevans * Provide Malloc / Free backed by EFIs AllocatePool / FreePool which ensures
60329140Skevans * memory is correctly aligned avoiding EFI_INVALID_PARAMETER returns from
61329140Skevans * EFI methods.
62329140Skevans */
63329140Skevansvoid *
64329140SkevansMalloc(size_t len, const char *file __unused, int line __unused)
65294060Ssmh{
66329140Skevans	void *out;
67264391Snwhitehorn
68329140Skevans	if (BS->AllocatePool(EfiLoaderData, len, &out) == EFI_SUCCESS)
69329140Skevans		return (out);
70294060Ssmh
71329140Skevans	return (NULL);
72329140Skevans}
73294060Ssmh
74329140Skevansvoid
75329140SkevansFree(void *buf, const char *file __unused, int line __unused)
76329140Skevans{
77329140Skevans	if (buf != NULL)
78329140Skevans		(void)BS->FreePool(buf);
79294060Ssmh}
80294060Ssmh
81329140Skevans/*
82329140Skevans * nodes_match returns TRUE if the imgpath isn't NULL and the nodes match,
83329140Skevans * FALSE otherwise.
84329140Skevans */
85329140Skevansstatic BOOLEAN
86329140Skevansnodes_match(EFI_DEVICE_PATH *imgpath, EFI_DEVICE_PATH *devpath)
87294060Ssmh{
88329140Skevans	size_t len;
89295320Ssmh
90329140Skevans	if (imgpath == NULL || imgpath->Type != devpath->Type ||
91329140Skevans	    imgpath->SubType != devpath->SubType)
92329140Skevans		return (FALSE);
93295320Ssmh
94329140Skevans	len = DevicePathNodeLength(imgpath);
95329140Skevans	if (len != DevicePathNodeLength(devpath))
96329140Skevans		return (FALSE);
97295320Ssmh
98329140Skevans	return (memcmp(imgpath, devpath, (size_t)len) == 0);
99295320Ssmh}
100295320Ssmh
101329140Skevans/*
102329140Skevans * device_paths_match returns TRUE if the imgpath isn't NULL and all nodes
103329140Skevans * in imgpath and devpath match up to their respective occurrences of a
104329140Skevans * media node, FALSE otherwise.
105329140Skevans */
106329140Skevansstatic BOOLEAN
107329140Skevansdevice_paths_match(EFI_DEVICE_PATH *imgpath, EFI_DEVICE_PATH *devpath)
108295320Ssmh{
109295320Ssmh
110329140Skevans	if (imgpath == NULL)
111329140Skevans		return (FALSE);
112295320Ssmh
113329140Skevans	while (!IsDevicePathEnd(imgpath) && !IsDevicePathEnd(devpath)) {
114329140Skevans		if (IsDevicePathType(imgpath, MEDIA_DEVICE_PATH) &&
115329140Skevans		    IsDevicePathType(devpath, MEDIA_DEVICE_PATH))
116329140Skevans			return (TRUE);
117295320Ssmh
118329140Skevans		if (!nodes_match(imgpath, devpath))
119329140Skevans			return (FALSE);
120295320Ssmh
121329140Skevans		imgpath = NextDevicePathNode(imgpath);
122329140Skevans		devpath = NextDevicePathNode(devpath);
123329140Skevans	}
124295320Ssmh
125329140Skevans	return (FALSE);
126295320Ssmh}
127295320Ssmh
128329140Skevans/*
129329140Skevans * devpath_last returns the last non-path end node in devpath.
130329140Skevans */
131329140Skevansstatic EFI_DEVICE_PATH *
132329140Skevansdevpath_last(EFI_DEVICE_PATH *devpath)
133295320Ssmh{
134295320Ssmh
135329140Skevans	while (!IsDevicePathEnd(NextDevicePathNode(devpath)))
136329140Skevans		devpath = NextDevicePathNode(devpath);
137295320Ssmh
138329140Skevans	return (devpath);
139295320Ssmh}
140295320Ssmh
141329140Skevans/*
142329140Skevans * load_loader attempts to load the loader image data.
143329140Skevans *
144329140Skevans * It tries each module and its respective devices, identified by mod->probe,
145329140Skevans * in order until a successful load occurs at which point it returns EFI_SUCCESS
146329140Skevans * and EFI_NOT_FOUND otherwise.
147329140Skevans *
148329140Skevans * Only devices which have preferred matching the preferred parameter are tried.
149329140Skevans */
150329140Skevansstatic EFI_STATUS
151329140Skevansload_loader(const boot_module_t **modp, dev_info_t **devinfop, void **bufp,
152329140Skevans    size_t *bufsize, BOOLEAN preferred)
153295320Ssmh{
154329140Skevans	UINTN i;
155329140Skevans	dev_info_t *dev;
156329140Skevans	const boot_module_t *mod;
157295320Ssmh
158329140Skevans	for (i = 0; i < NUM_BOOT_MODULES; i++) {
159329140Skevans		mod = boot_modules[i];
160329140Skevans		for (dev = mod->devices(); dev != NULL; dev = dev->next) {
161329140Skevans			if (dev->preferred != preferred)
162329140Skevans				continue;
163295320Ssmh
164329140Skevans			if (mod->load(PATH_LOADER_EFI, dev, bufp, bufsize) ==
165329140Skevans			    EFI_SUCCESS) {
166329140Skevans				*devinfop = dev;
167329140Skevans				*modp = mod;
168329140Skevans				return (EFI_SUCCESS);
169295320Ssmh			}
170295320Ssmh		}
171295320Ssmh	}
172295320Ssmh
173295320Ssmh	return (EFI_NOT_FOUND);
174295320Ssmh}
175295320Ssmh
176295320Ssmh/*
177295320Ssmh * try_boot only returns if it fails to load the loader. If it succeeds
178295320Ssmh * it simply boots, otherwise it returns the status of last EFI call.
179295320Ssmh */
180295320Ssmhstatic EFI_STATUS
181323484Srlibbytry_boot(void)
182295320Ssmh{
183295320Ssmh	size_t bufsize, loadersize, cmdsize;
184295320Ssmh	void *buf, *loaderbuf;
185294768Simp	char *cmd;
186329140Skevans	dev_info_t *dev;
187329140Skevans	const boot_module_t *mod;
188294060Ssmh	EFI_HANDLE loaderhandle;
189294060Ssmh	EFI_LOADED_IMAGE *loaded_image;
190264391Snwhitehorn	EFI_STATUS status;
191294060Ssmh
192329140Skevans	status = load_loader(&mod, &dev, &loaderbuf, &loadersize, TRUE);
193329140Skevans	if (status != EFI_SUCCESS) {
194329140Skevans		status = load_loader(&mod, &dev, &loaderbuf, &loadersize,
195329140Skevans		    FALSE);
196295320Ssmh		if (status != EFI_SUCCESS) {
197329140Skevans			printf("Failed to load '%s'\n", PATH_LOADER_EFI);
198329140Skevans			return (status);
199329140Skevans		}
200295320Ssmh	}
201295320Ssmh
202294768Simp	/*
203294768Simp	 * Read in and parse the command line from /boot.config or /boot/config,
204294768Simp	 * if present. We'll pass it the next stage via a simple ASCII
205294768Simp	 * string. loader.efi has a hack for ASCII strings, so we'll use that to
206294768Simp	 * keep the size down here. We only try to read the alternate file if
207294768Simp	 * we get EFI_NOT_FOUND because all other errors mean that the boot_module
208294768Simp	 * had troubles with the filesystem. We could return early, but we'll let
209294768Simp	 * loading the actual kernel sort all that out. Since these files are
210294768Simp	 * optional, we don't report errors in trying to read them.
211294768Simp	 */
212294768Simp	cmd = NULL;
213294768Simp	cmdsize = 0;
214329140Skevans	status = mod->load(PATH_DOTCONFIG, dev, &buf, &bufsize);
215294768Simp	if (status == EFI_NOT_FOUND)
216329140Skevans		status = mod->load(PATH_CONFIG, dev, &buf, &bufsize);
217294768Simp	if (status == EFI_SUCCESS) {
218294768Simp		cmdsize = bufsize + 1;
219294768Simp		cmd = malloc(cmdsize);
220295320Ssmh		if (cmd == NULL)
221295320Ssmh			goto errout;
222294768Simp		memcpy(cmd, buf, bufsize);
223294768Simp		cmd[bufsize] = '\0';
224294768Simp		free(buf);
225295320Ssmh		buf = NULL;
226294768Simp	}
227294768Simp
228329140Skevans	if ((status = BS->LoadImage(TRUE, IH, devpath_last(dev->devpath),
229295320Ssmh	    loaderbuf, loadersize, &loaderhandle)) != EFI_SUCCESS) {
230329140Skevans		printf("Failed to load image provided by %s, size: %zu, (%lu)\n",
231329140Skevans		     mod->name, loadersize, EFI_ERROR_CODE(status));
232295320Ssmh		goto errout;
233294060Ssmh	}
234294060Ssmh
235329140Skevans	if ((status = BS->HandleProtocol(loaderhandle, &LoadedImageGUID,
236329140Skevans	    (VOID**)&loaded_image)) != EFI_SUCCESS) {
237329140Skevans		printf("Failed to query LoadedImage provided by %s (%lu)\n",
238329140Skevans		    mod->name, EFI_ERROR_CODE(status));
239295320Ssmh		goto errout;
240294060Ssmh	}
241294060Ssmh
242295320Ssmh	if (cmd != NULL)
243295320Ssmh		printf("    command args: %s\n", cmd);
244295320Ssmh
245329140Skevans	loaded_image->DeviceHandle = dev->devhandle;
246294768Simp	loaded_image->LoadOptionsSize = cmdsize;
247294768Simp	loaded_image->LoadOptions = cmd;
248294060Ssmh
249295320Ssmh	DPRINTF("Starting '%s' in 5 seconds...", PATH_LOADER_EFI);
250295320Ssmh	DSTALL(1000000);
251295320Ssmh	DPRINTF(".");
252295320Ssmh	DSTALL(1000000);
253295320Ssmh	DPRINTF(".");
254295320Ssmh	DSTALL(1000000);
255295320Ssmh	DPRINTF(".");
256295320Ssmh	DSTALL(1000000);
257295320Ssmh	DPRINTF(".");
258295320Ssmh	DSTALL(1000000);
259295320Ssmh	DPRINTF(".\n");
260295320Ssmh
261329114Skevans	if ((status = BS->StartImage(loaderhandle, NULL, NULL)) !=
262294060Ssmh	    EFI_SUCCESS) {
263329140Skevans		printf("Failed to start image provided by %s (%lu)\n",
264329140Skevans		    mod->name, EFI_ERROR_CODE(status));
265294768Simp		loaded_image->LoadOptionsSize = 0;
266294768Simp		loaded_image->LoadOptions = NULL;
267294060Ssmh	}
268295320Ssmh
269295320Ssmherrout:
270295320Ssmh	if (cmd != NULL)
271295320Ssmh		free(cmd);
272295320Ssmh	if (buf != NULL)
273295320Ssmh		free(buf);
274295320Ssmh	if (loaderbuf != NULL)
275295320Ssmh		free(loaderbuf);
276295320Ssmh
277295320Ssmh	return (status);
278294060Ssmh}
279294060Ssmh
280329140Skevans/*
281329140Skevans * probe_handle determines if the passed handle represents a logical partition
282329140Skevans * if it does it uses each module in order to probe it and if successful it
283329140Skevans * returns EFI_SUCCESS.
284329140Skevans */
285329140Skevansstatic EFI_STATUS
286329140Skevansprobe_handle(EFI_HANDLE h, EFI_DEVICE_PATH *imgpath, BOOLEAN *preferred)
287295320Ssmh{
288329140Skevans	dev_info_t *devinfo;
289329140Skevans	EFI_BLOCK_IO *blkio;
290329140Skevans	EFI_DEVICE_PATH *devpath;
291329140Skevans	EFI_STATUS status;
292329140Skevans	UINTN i;
293295320Ssmh
294329140Skevans	/* Figure out if we're dealing with an actual partition. */
295329140Skevans	status = BS->HandleProtocol(h, &DevicePathGUID, (void **)&devpath);
296329140Skevans	if (status == EFI_UNSUPPORTED)
297329140Skevans		return (status);
298295320Ssmh
299329140Skevans	if (status != EFI_SUCCESS) {
300329140Skevans		DPRINTF("\nFailed to query DevicePath (%lu)\n",
301329140Skevans		    EFI_ERROR_CODE(status));
302329140Skevans		return (status);
303329140Skevans	}
304329140Skevans#ifdef EFI_DEBUG
305329140Skevans	{
306329140Skevans		CHAR16 *text = efi_devpath_name(devpath);
307329140Skevans		DPRINTF("probing: %S\n", text);
308329140Skevans		efi_free_devpath_name(text);
309329140Skevans	}
310329114Skevans#endif
311329140Skevans	status = BS->HandleProtocol(h, &BlockIoProtocolGUID, (void **)&blkio);
312329140Skevans	if (status == EFI_UNSUPPORTED)
313329140Skevans		return (status);
314295320Ssmh
315329140Skevans	if (status != EFI_SUCCESS) {
316329140Skevans		DPRINTF("\nFailed to query BlockIoProtocol (%lu)\n",
317329140Skevans		    EFI_ERROR_CODE(status));
318329140Skevans		return (status);
319329140Skevans	}
320295320Ssmh
321329140Skevans	if (!blkio->Media->LogicalPartition)
322329140Skevans		return (EFI_UNSUPPORTED);
323329140Skevans
324329140Skevans	*preferred = device_paths_match(imgpath, devpath);
325329140Skevans
326329140Skevans	/* Run through each module, see if it can load this partition */
327329140Skevans	for (i = 0; i < NUM_BOOT_MODULES; i++) {
328329140Skevans		devinfo = malloc(sizeof(*devinfo));
329329140Skevans		if (devinfo == NULL) {
330329140Skevans			DPRINTF("\nFailed to allocate devinfo\n");
331329140Skevans			continue;
332329140Skevans		}
333329140Skevans		devinfo->dev = blkio;
334329140Skevans		devinfo->devpath = devpath;
335329140Skevans		devinfo->devhandle = h;
336329140Skevans		devinfo->devdata = NULL;
337329140Skevans		devinfo->preferred = *preferred;
338329140Skevans		devinfo->next = NULL;
339329140Skevans
340329140Skevans		status = boot_modules[i]->probe(devinfo);
341329140Skevans		if (status == EFI_SUCCESS)
342329140Skevans			return (EFI_SUCCESS);
343329140Skevans		free(devinfo);
344329140Skevans	}
345329140Skevans
346329140Skevans	return (EFI_UNSUPPORTED);
347329140Skevans}
348329140Skevans
349329140Skevans/*
350329140Skevans * probe_handle_status calls probe_handle and outputs the returned status
351329140Skevans * of the call.
352329140Skevans */
353329140Skevansstatic void
354329140Skevansprobe_handle_status(EFI_HANDLE h, EFI_DEVICE_PATH *imgpath)
355329140Skevans{
356329140Skevans	EFI_STATUS status;
357329140Skevans	BOOLEAN preferred;
358329140Skevans
359329140Skevans	preferred = FALSE;
360329140Skevans	status = probe_handle(h, imgpath, &preferred);
361329140Skevans
362329140Skevans	DPRINTF("probe: ");
363329140Skevans	switch (status) {
364329140Skevans	case EFI_UNSUPPORTED:
365329140Skevans		printf(".");
366329140Skevans		DPRINTF(" not supported\n");
367329140Skevans		break;
368329140Skevans	case EFI_SUCCESS:
369329140Skevans		if (preferred) {
370329140Skevans			printf("%c", '*');
371329140Skevans			DPRINTF(" supported (preferred)\n");
372329140Skevans		} else {
373329140Skevans			printf("%c", '+');
374329140Skevans			DPRINTF(" supported\n");
375329140Skevans		}
376329140Skevans		break;
377329140Skevans	default:
378329140Skevans		printf("x");
379329140Skevans		DPRINTF(" error (%lu)\n", EFI_ERROR_CODE(status));
380329140Skevans		break;
381329140Skevans	}
382329140Skevans	DSTALL(500000);
383329140Skevans}
384329140Skevans
385329140SkevansEFI_STATUS
386329140Skevansefi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE *Xsystab)
387329140Skevans{
388329140Skevans	EFI_HANDLE *handles;
389329140Skevans	EFI_LOADED_IMAGE *img;
390329140Skevans	EFI_DEVICE_PATH *imgpath;
391329140Skevans	EFI_STATUS status;
392329140Skevans	EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL;
393329140Skevans	SIMPLE_TEXT_OUTPUT_INTERFACE *conout = NULL;
394329140Skevans	UINTN i, max_dim, best_mode, cols, rows, hsize, nhandles;
395329140Skevans	CHAR16 *text;
396329183Skevans	UINT16 boot_current;
397329183Skevans	size_t sz;
398329183Skevans	UINT16 boot_order[100];
399329140Skevans
400329140Skevans	/* Basic initialization*/
401329140Skevans	ST = Xsystab;
402329140Skevans	IH = Ximage;
403329140Skevans	BS = ST->BootServices;
404329140Skevans	RS = ST->RuntimeServices;
405329140Skevans
406329140Skevans	/* Set up the console, so printf works. */
407329140Skevans	status = BS->LocateProtocol(&ConsoleControlGUID, NULL,
408329140Skevans	    (VOID **)&ConsoleControl);
409329140Skevans	if (status == EFI_SUCCESS)
410329140Skevans		(void)ConsoleControl->SetMode(ConsoleControl,
411329140Skevans		    EfiConsoleControlScreenText);
412281059Srpaulo	/*
413281059Srpaulo	 * Reset the console and find the best text mode.
414281059Srpaulo	 */
415329114Skevans	conout = ST->ConOut;
416281059Srpaulo	conout->Reset(conout, TRUE);
417281059Srpaulo	max_dim = best_mode = 0;
418329140Skevans	for (i = 0; ; i++) {
419293274Ssmh		status = conout->QueryMode(conout, i, &cols, &rows);
420281059Srpaulo		if (EFI_ERROR(status))
421281059Srpaulo			break;
422281059Srpaulo		if (cols * rows > max_dim) {
423281059Srpaulo			max_dim = cols * rows;
424281059Srpaulo			best_mode = i;
425281059Srpaulo		}
426281059Srpaulo	}
427329140Skevans	if (max_dim > 0)
428281059Srpaulo		conout->SetMode(conout, best_mode);
429281059Srpaulo	conout->EnableCursor(conout, TRUE);
430281059Srpaulo	conout->ClearScreen(conout);
431271762Semaste
432294060Ssmh	printf("\n>> FreeBSD EFI boot block\n");
433294765Simp	printf("   Loader path: %s\n\n", PATH_LOADER_EFI);
434329140Skevans	printf("   Initializing modules:");
435329140Skevans	for (i = 0; i < NUM_BOOT_MODULES; i++) {
436329140Skevans		printf(" %s", boot_modules[i]->name);
437329140Skevans		if (boot_modules[i]->init != NULL)
438329140Skevans			boot_modules[i]->init();
439294060Ssmh	}
440329140Skevans	putchar('\n');
441264391Snwhitehorn
442329114Skevans	/* Determine the devpath of our image so we can prefer it. */
443329140Skevans	status = BS->HandleProtocol(IH, &LoadedImageGUID, (VOID**)&img);
444329114Skevans	imgpath = NULL;
445329114Skevans	if (status == EFI_SUCCESS) {
446329140Skevans		text = efi_devpath_name(img->FilePath);
447329140Skevans		if (text != NULL) {
448329140Skevans			printf("   Load Path: %S\n", text);
449329140Skevans			efi_setenv_freebsd_wcs("Boot1Path", text);
450329140Skevans			efi_free_devpath_name(text);
451329140Skevans		}
452329114Skevans
453329140Skevans		status = BS->HandleProtocol(img->DeviceHandle, &DevicePathGUID,
454329114Skevans		    (void **)&imgpath);
455329114Skevans		if (status != EFI_SUCCESS) {
456329114Skevans			DPRINTF("Failed to get image DevicePath (%lu)\n",
457329114Skevans			    EFI_ERROR_CODE(status));
458329114Skevans		} else {
459329114Skevans			text = efi_devpath_name(imgpath);
460329140Skevans			if (text != NULL) {
461329140Skevans				printf("   Load Device: %S\n", text);
462329140Skevans				efi_setenv_freebsd_wcs("Boot1Dev", text);
463329140Skevans				efi_free_devpath_name(text);
464329140Skevans			}
465329114Skevans		}
466329140Skevans	}
467329114Skevans
468329183Skevans	boot_current = 0;
469329183Skevans	sz = sizeof(boot_current);
470332156Skevans	if (efi_global_getenv("BootCurrent", &boot_current, &sz) == EFI_SUCCESS) {
471332156Skevans		printf("   BootCurrent: %04x\n", boot_current);
472329183Skevans
473332156Skevans		sz = sizeof(boot_order);
474332156Skevans		if (efi_global_getenv("BootOrder", &boot_order, &sz) == EFI_SUCCESS) {
475332156Skevans			printf("   BootOrder:");
476332156Skevans			for (i = 0; i < sz / sizeof(boot_order[0]); i++)
477332156Skevans				printf(" %04x%s", boot_order[i],
478332156Skevans				    boot_order[i] == boot_current ? "[*]" : "");
479332156Skevans			printf("\n");
480332156Skevans		}
481332156Skevans	}
482329183Skevans
483329183Skevans#ifdef TEST_FAILURE
484329183Skevans	/*
485329183Skevans	 * For testing failover scenarios, it's nice to be able to fail fast.
486329183Skevans	 * Define TEST_FAILURE to create a boot1.efi that always fails after
487329183Skevans	 * reporting the boot manager protocol details.
488329183Skevans	 */
489329183Skevans	BS->Exit(IH, EFI_OUT_OF_RESOURCES, 0, NULL);
490329183Skevans#endif
491329183Skevans
492329140Skevans	/* Get all the device handles */
493329140Skevans	hsize = (UINTN)NUM_HANDLES_INIT * sizeof(EFI_HANDLE);
494329140Skevans	handles = malloc(hsize);
495329183Skevans	if (handles == NULL)
496329140Skevans		printf("Failed to allocate %d handles\n", NUM_HANDLES_INIT);
497329114Skevans
498329140Skevans	status = BS->LocateHandle(ByProtocol, &BlockIoProtocolGUID, NULL,
499329140Skevans	    &hsize, handles);
500329140Skevans	switch (status) {
501329140Skevans	case EFI_SUCCESS:
502329140Skevans		break;
503329140Skevans	case EFI_BUFFER_TOO_SMALL:
504329140Skevans		free(handles);
505329140Skevans		handles = malloc(hsize);
506329140Skevans		if (handles == NULL)
507329140Skevans			efi_panic(EFI_OUT_OF_RESOURCES, "Failed to allocate %d handles\n",
508329140Skevans			    NUM_HANDLES_INIT);
509329140Skevans		status = BS->LocateHandle(ByProtocol, &BlockIoProtocolGUID,
510329140Skevans		    NULL, &hsize, handles);
511329140Skevans		if (status != EFI_SUCCESS)
512329140Skevans			efi_panic(status, "Failed to get device handles\n");
513329140Skevans		break;
514329140Skevans	default:
515329140Skevans		efi_panic(status, "Failed to get device handles\n");
516329140Skevans		break;
517329140Skevans	}
518264391Snwhitehorn
519329140Skevans	/* Scan all partitions, probing with all modules. */
520329140Skevans	nhandles = hsize / sizeof(*handles);
521329140Skevans	printf("   Probing %zu block devices...", nhandles);
522329140Skevans	DPRINTF("\n");
523329135Skevans
524329140Skevans	for (i = 0; i < nhandles; i++)
525329140Skevans		probe_handle_status(handles[i], imgpath);
526329140Skevans	printf(" done\n");
527329135Skevans
528329140Skevans	/* Status summary. */
529329140Skevans	for (i = 0; i < NUM_BOOT_MODULES; i++) {
530329140Skevans		printf("    ");
531329140Skevans		boot_modules[i]->status();
532294060Ssmh	}
533264391Snwhitehorn
534295320Ssmh	try_boot();
535281058Srpaulo
536294060Ssmh	/* If we get here, we're out of luck... */
537329114Skevans	efi_panic(EFI_LOAD_ERROR, "No bootable partitions found!");
538264391Snwhitehorn}
539264391Snwhitehorn
540295320Ssmh/*
541329140Skevans * add_device adds a device to the passed devinfo list.
542329140Skevans */
543329140Skevansvoid
544329140Skevansadd_device(dev_info_t **devinfop, dev_info_t *devinfo)
545329140Skevans{
546329140Skevans	dev_info_t *dev;
547329140Skevans
548329140Skevans	if (*devinfop == NULL) {
549329140Skevans		*devinfop = devinfo;
550329140Skevans		return;
551329140Skevans	}
552329140Skevans
553329140Skevans	for (dev = *devinfop; dev->next != NULL; dev = dev->next)
554329140Skevans		;
555329140Skevans
556329140Skevans	dev->next = devinfo;
557329140Skevans}
558329140Skevans
559329140Skevans/*
560329114Skevans * OK. We totally give up. Exit back to EFI with a sensible status so
561329114Skevans * it can try the next option on the list.
562329114Skevans */
563329114Skevansstatic void
564329114Skevansefi_panic(EFI_STATUS s, const char *fmt, ...)
565264391Snwhitehorn{
566329140Skevans	va_list ap;
567264391Snwhitehorn
568329140Skevans	printf("panic: ");
569329140Skevans	va_start(ap, fmt);
570329140Skevans	vprintf(fmt, ap);
571329140Skevans	va_end(ap);
572329140Skevans	printf("\n");
573264391Snwhitehorn
574329140Skevans	BS->Exit(IH, s, 0, NULL);
575264391Snwhitehorn}
576329140Skevans
577329140Skevansvoid
578329140Skevansputchar(int c)
579329140Skevans{
580329140Skevans	CHAR16 buf[2];
581329140Skevans
582329140Skevans	if (c == '\n') {
583329140Skevans		buf[0] = '\r';
584329140Skevans		buf[1] = 0;
585329140Skevans		ST->ConOut->OutputString(ST->ConOut, buf);
586329140Skevans	}
587329140Skevans	buf[0] = c;
588329140Skevans	buf[1] = 0;
589329140Skevans	ST->ConOut->OutputString(ST->ConOut, buf);
590329140Skevans}
591