boot1.c revision 329183
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 329183 2018-02-12 20:51:28Z 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;
57329114Skevansstatic EFI_GUID FreeBSDBootVarGUID = FREEBSD_BOOT_VAR_GUID;
58329183Skevansstatic EFI_GUID GlobalBootVarGUID = UEFI_BOOT_VAR_GUID;
59264391Snwhitehorn
60329140Skevans/*
61329140Skevans * Provide Malloc / Free backed by EFIs AllocatePool / FreePool which ensures
62329140Skevans * memory is correctly aligned avoiding EFI_INVALID_PARAMETER returns from
63329140Skevans * EFI methods.
64329140Skevans */
65329140Skevansvoid *
66329140SkevansMalloc(size_t len, const char *file __unused, int line __unused)
67294060Ssmh{
68329140Skevans	void *out;
69264391Snwhitehorn
70329140Skevans	if (BS->AllocatePool(EfiLoaderData, len, &out) == EFI_SUCCESS)
71329140Skevans		return (out);
72294060Ssmh
73329140Skevans	return (NULL);
74329140Skevans}
75294060Ssmh
76329140Skevansvoid
77329140SkevansFree(void *buf, const char *file __unused, int line __unused)
78329140Skevans{
79329140Skevans	if (buf != NULL)
80329140Skevans		(void)BS->FreePool(buf);
81294060Ssmh}
82294060Ssmh
83329114Skevansstatic EFI_STATUS
84329183Skevansefi_getenv(EFI_GUID *g, const char *v, void *data, size_t *len)
85329183Skevans{
86329183Skevans	size_t ul;
87329183Skevans	CHAR16 *uv;
88329183Skevans	UINT32 attr;
89329183Skevans	UINTN dl;
90329183Skevans	EFI_STATUS rv;
91329183Skevans
92329183Skevans	uv = NULL;
93329183Skevans	if (utf8_to_ucs2(v, &uv, &ul) != 0)
94329183Skevans		return (EFI_OUT_OF_RESOURCES);
95329183Skevans	dl = *len;
96329183Skevans	rv = RS->GetVariable(uv, g, &attr, &dl, data);
97329183Skevans	if (rv == EFI_SUCCESS)
98329183Skevans		*len = dl;
99329183Skevans	free(uv);
100329183Skevans	return (rv);
101329183Skevans}
102329183Skevans
103329183Skevansstatic EFI_STATUS
104329114Skevansefi_setenv_freebsd_wcs(const char *varname, CHAR16 *valstr)
105329114Skevans{
106329114Skevans	CHAR16 *var = NULL;
107329114Skevans	size_t len;
108329114Skevans	EFI_STATUS rv;
109329114Skevans
110329183Skevans	if (utf8_to_ucs2(varname, &var, &len) != 0)
111329114Skevans		return (EFI_OUT_OF_RESOURCES);
112329114Skevans	rv = RS->SetVariable(var, &FreeBSDBootVarGUID,
113329114Skevans	    EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
114329114Skevans	    (ucs2len(valstr) + 1) * sizeof(efi_char), valstr);
115329114Skevans	free(var);
116329114Skevans	return (rv);
117329114Skevans}
118329114Skevans
119329140Skevans/*
120329140Skevans * nodes_match returns TRUE if the imgpath isn't NULL and the nodes match,
121329140Skevans * FALSE otherwise.
122329140Skevans */
123329140Skevansstatic BOOLEAN
124329140Skevansnodes_match(EFI_DEVICE_PATH *imgpath, EFI_DEVICE_PATH *devpath)
125294060Ssmh{
126329140Skevans	size_t len;
127295320Ssmh
128329140Skevans	if (imgpath == NULL || imgpath->Type != devpath->Type ||
129329140Skevans	    imgpath->SubType != devpath->SubType)
130329140Skevans		return (FALSE);
131295320Ssmh
132329140Skevans	len = DevicePathNodeLength(imgpath);
133329140Skevans	if (len != DevicePathNodeLength(devpath))
134329140Skevans		return (FALSE);
135295320Ssmh
136329140Skevans	return (memcmp(imgpath, devpath, (size_t)len) == 0);
137295320Ssmh}
138295320Ssmh
139329140Skevans/*
140329140Skevans * device_paths_match returns TRUE if the imgpath isn't NULL and all nodes
141329140Skevans * in imgpath and devpath match up to their respective occurrences of a
142329140Skevans * media node, FALSE otherwise.
143329140Skevans */
144329140Skevansstatic BOOLEAN
145329140Skevansdevice_paths_match(EFI_DEVICE_PATH *imgpath, EFI_DEVICE_PATH *devpath)
146295320Ssmh{
147295320Ssmh
148329140Skevans	if (imgpath == NULL)
149329140Skevans		return (FALSE);
150295320Ssmh
151329140Skevans	while (!IsDevicePathEnd(imgpath) && !IsDevicePathEnd(devpath)) {
152329140Skevans		if (IsDevicePathType(imgpath, MEDIA_DEVICE_PATH) &&
153329140Skevans		    IsDevicePathType(devpath, MEDIA_DEVICE_PATH))
154329140Skevans			return (TRUE);
155295320Ssmh
156329140Skevans		if (!nodes_match(imgpath, devpath))
157329140Skevans			return (FALSE);
158295320Ssmh
159329140Skevans		imgpath = NextDevicePathNode(imgpath);
160329140Skevans		devpath = NextDevicePathNode(devpath);
161329140Skevans	}
162295320Ssmh
163329140Skevans	return (FALSE);
164295320Ssmh}
165295320Ssmh
166329140Skevans/*
167329140Skevans * devpath_last returns the last non-path end node in devpath.
168329140Skevans */
169329140Skevansstatic EFI_DEVICE_PATH *
170329140Skevansdevpath_last(EFI_DEVICE_PATH *devpath)
171295320Ssmh{
172295320Ssmh
173329140Skevans	while (!IsDevicePathEnd(NextDevicePathNode(devpath)))
174329140Skevans		devpath = NextDevicePathNode(devpath);
175295320Ssmh
176329140Skevans	return (devpath);
177295320Ssmh}
178295320Ssmh
179329140Skevans/*
180329140Skevans * load_loader attempts to load the loader image data.
181329140Skevans *
182329140Skevans * It tries each module and its respective devices, identified by mod->probe,
183329140Skevans * in order until a successful load occurs at which point it returns EFI_SUCCESS
184329140Skevans * and EFI_NOT_FOUND otherwise.
185329140Skevans *
186329140Skevans * Only devices which have preferred matching the preferred parameter are tried.
187329140Skevans */
188329140Skevansstatic EFI_STATUS
189329140Skevansload_loader(const boot_module_t **modp, dev_info_t **devinfop, void **bufp,
190329140Skevans    size_t *bufsize, BOOLEAN preferred)
191295320Ssmh{
192329140Skevans	UINTN i;
193329140Skevans	dev_info_t *dev;
194329140Skevans	const boot_module_t *mod;
195295320Ssmh
196329140Skevans	for (i = 0; i < NUM_BOOT_MODULES; i++) {
197329140Skevans		mod = boot_modules[i];
198329140Skevans		for (dev = mod->devices(); dev != NULL; dev = dev->next) {
199329140Skevans			if (dev->preferred != preferred)
200329140Skevans				continue;
201295320Ssmh
202329140Skevans			if (mod->load(PATH_LOADER_EFI, dev, bufp, bufsize) ==
203329140Skevans			    EFI_SUCCESS) {
204329140Skevans				*devinfop = dev;
205329140Skevans				*modp = mod;
206329140Skevans				return (EFI_SUCCESS);
207295320Ssmh			}
208295320Ssmh		}
209295320Ssmh	}
210295320Ssmh
211295320Ssmh	return (EFI_NOT_FOUND);
212295320Ssmh}
213295320Ssmh
214295320Ssmh/*
215295320Ssmh * try_boot only returns if it fails to load the loader. If it succeeds
216295320Ssmh * it simply boots, otherwise it returns the status of last EFI call.
217295320Ssmh */
218295320Ssmhstatic EFI_STATUS
219323484Srlibbytry_boot(void)
220295320Ssmh{
221295320Ssmh	size_t bufsize, loadersize, cmdsize;
222295320Ssmh	void *buf, *loaderbuf;
223294768Simp	char *cmd;
224329140Skevans	dev_info_t *dev;
225329140Skevans	const boot_module_t *mod;
226294060Ssmh	EFI_HANDLE loaderhandle;
227294060Ssmh	EFI_LOADED_IMAGE *loaded_image;
228264391Snwhitehorn	EFI_STATUS status;
229294060Ssmh
230329140Skevans	status = load_loader(&mod, &dev, &loaderbuf, &loadersize, TRUE);
231329140Skevans	if (status != EFI_SUCCESS) {
232329140Skevans		status = load_loader(&mod, &dev, &loaderbuf, &loadersize,
233329140Skevans		    FALSE);
234295320Ssmh		if (status != EFI_SUCCESS) {
235329140Skevans			printf("Failed to load '%s'\n", PATH_LOADER_EFI);
236329140Skevans			return (status);
237329140Skevans		}
238295320Ssmh	}
239295320Ssmh
240294768Simp	/*
241294768Simp	 * Read in and parse the command line from /boot.config or /boot/config,
242294768Simp	 * if present. We'll pass it the next stage via a simple ASCII
243294768Simp	 * string. loader.efi has a hack for ASCII strings, so we'll use that to
244294768Simp	 * keep the size down here. We only try to read the alternate file if
245294768Simp	 * we get EFI_NOT_FOUND because all other errors mean that the boot_module
246294768Simp	 * had troubles with the filesystem. We could return early, but we'll let
247294768Simp	 * loading the actual kernel sort all that out. Since these files are
248294768Simp	 * optional, we don't report errors in trying to read them.
249294768Simp	 */
250294768Simp	cmd = NULL;
251294768Simp	cmdsize = 0;
252329140Skevans	status = mod->load(PATH_DOTCONFIG, dev, &buf, &bufsize);
253294768Simp	if (status == EFI_NOT_FOUND)
254329140Skevans		status = mod->load(PATH_CONFIG, dev, &buf, &bufsize);
255294768Simp	if (status == EFI_SUCCESS) {
256294768Simp		cmdsize = bufsize + 1;
257294768Simp		cmd = malloc(cmdsize);
258295320Ssmh		if (cmd == NULL)
259295320Ssmh			goto errout;
260294768Simp		memcpy(cmd, buf, bufsize);
261294768Simp		cmd[bufsize] = '\0';
262294768Simp		free(buf);
263295320Ssmh		buf = NULL;
264294768Simp	}
265294768Simp
266329140Skevans	if ((status = BS->LoadImage(TRUE, IH, devpath_last(dev->devpath),
267295320Ssmh	    loaderbuf, loadersize, &loaderhandle)) != EFI_SUCCESS) {
268329140Skevans		printf("Failed to load image provided by %s, size: %zu, (%lu)\n",
269329140Skevans		     mod->name, loadersize, EFI_ERROR_CODE(status));
270295320Ssmh		goto errout;
271294060Ssmh	}
272294060Ssmh
273329140Skevans	if ((status = BS->HandleProtocol(loaderhandle, &LoadedImageGUID,
274329140Skevans	    (VOID**)&loaded_image)) != EFI_SUCCESS) {
275329140Skevans		printf("Failed to query LoadedImage provided by %s (%lu)\n",
276329140Skevans		    mod->name, EFI_ERROR_CODE(status));
277295320Ssmh		goto errout;
278294060Ssmh	}
279294060Ssmh
280295320Ssmh	if (cmd != NULL)
281295320Ssmh		printf("    command args: %s\n", cmd);
282295320Ssmh
283329140Skevans	loaded_image->DeviceHandle = dev->devhandle;
284294768Simp	loaded_image->LoadOptionsSize = cmdsize;
285294768Simp	loaded_image->LoadOptions = cmd;
286294060Ssmh
287295320Ssmh	DPRINTF("Starting '%s' in 5 seconds...", PATH_LOADER_EFI);
288295320Ssmh	DSTALL(1000000);
289295320Ssmh	DPRINTF(".");
290295320Ssmh	DSTALL(1000000);
291295320Ssmh	DPRINTF(".");
292295320Ssmh	DSTALL(1000000);
293295320Ssmh	DPRINTF(".");
294295320Ssmh	DSTALL(1000000);
295295320Ssmh	DPRINTF(".");
296295320Ssmh	DSTALL(1000000);
297295320Ssmh	DPRINTF(".\n");
298295320Ssmh
299329114Skevans	if ((status = BS->StartImage(loaderhandle, NULL, NULL)) !=
300294060Ssmh	    EFI_SUCCESS) {
301329140Skevans		printf("Failed to start image provided by %s (%lu)\n",
302329140Skevans		    mod->name, EFI_ERROR_CODE(status));
303294768Simp		loaded_image->LoadOptionsSize = 0;
304294768Simp		loaded_image->LoadOptions = NULL;
305294060Ssmh	}
306295320Ssmh
307295320Ssmherrout:
308295320Ssmh	if (cmd != NULL)
309295320Ssmh		free(cmd);
310295320Ssmh	if (buf != NULL)
311295320Ssmh		free(buf);
312295320Ssmh	if (loaderbuf != NULL)
313295320Ssmh		free(loaderbuf);
314295320Ssmh
315295320Ssmh	return (status);
316294060Ssmh}
317294060Ssmh
318329140Skevans/*
319329140Skevans * probe_handle determines if the passed handle represents a logical partition
320329140Skevans * if it does it uses each module in order to probe it and if successful it
321329140Skevans * returns EFI_SUCCESS.
322329140Skevans */
323329140Skevansstatic EFI_STATUS
324329140Skevansprobe_handle(EFI_HANDLE h, EFI_DEVICE_PATH *imgpath, BOOLEAN *preferred)
325295320Ssmh{
326329140Skevans	dev_info_t *devinfo;
327329140Skevans	EFI_BLOCK_IO *blkio;
328329140Skevans	EFI_DEVICE_PATH *devpath;
329329140Skevans	EFI_STATUS status;
330329140Skevans	UINTN i;
331295320Ssmh
332329140Skevans	/* Figure out if we're dealing with an actual partition. */
333329140Skevans	status = BS->HandleProtocol(h, &DevicePathGUID, (void **)&devpath);
334329140Skevans	if (status == EFI_UNSUPPORTED)
335329140Skevans		return (status);
336295320Ssmh
337329140Skevans	if (status != EFI_SUCCESS) {
338329140Skevans		DPRINTF("\nFailed to query DevicePath (%lu)\n",
339329140Skevans		    EFI_ERROR_CODE(status));
340329140Skevans		return (status);
341329140Skevans	}
342329140Skevans#ifdef EFI_DEBUG
343329140Skevans	{
344329140Skevans		CHAR16 *text = efi_devpath_name(devpath);
345329140Skevans		DPRINTF("probing: %S\n", text);
346329140Skevans		efi_free_devpath_name(text);
347329140Skevans	}
348329114Skevans#endif
349329140Skevans	status = BS->HandleProtocol(h, &BlockIoProtocolGUID, (void **)&blkio);
350329140Skevans	if (status == EFI_UNSUPPORTED)
351329140Skevans		return (status);
352295320Ssmh
353329140Skevans	if (status != EFI_SUCCESS) {
354329140Skevans		DPRINTF("\nFailed to query BlockIoProtocol (%lu)\n",
355329140Skevans		    EFI_ERROR_CODE(status));
356329140Skevans		return (status);
357329140Skevans	}
358295320Ssmh
359329140Skevans	if (!blkio->Media->LogicalPartition)
360329140Skevans		return (EFI_UNSUPPORTED);
361329140Skevans
362329140Skevans	*preferred = device_paths_match(imgpath, devpath);
363329140Skevans
364329140Skevans	/* Run through each module, see if it can load this partition */
365329140Skevans	for (i = 0; i < NUM_BOOT_MODULES; i++) {
366329140Skevans		devinfo = malloc(sizeof(*devinfo));
367329140Skevans		if (devinfo == NULL) {
368329140Skevans			DPRINTF("\nFailed to allocate devinfo\n");
369329140Skevans			continue;
370329140Skevans		}
371329140Skevans		devinfo->dev = blkio;
372329140Skevans		devinfo->devpath = devpath;
373329140Skevans		devinfo->devhandle = h;
374329140Skevans		devinfo->devdata = NULL;
375329140Skevans		devinfo->preferred = *preferred;
376329140Skevans		devinfo->next = NULL;
377329140Skevans
378329140Skevans		status = boot_modules[i]->probe(devinfo);
379329140Skevans		if (status == EFI_SUCCESS)
380329140Skevans			return (EFI_SUCCESS);
381329140Skevans		free(devinfo);
382329140Skevans	}
383329140Skevans
384329140Skevans	return (EFI_UNSUPPORTED);
385329140Skevans}
386329140Skevans
387329140Skevans/*
388329140Skevans * probe_handle_status calls probe_handle and outputs the returned status
389329140Skevans * of the call.
390329140Skevans */
391329140Skevansstatic void
392329140Skevansprobe_handle_status(EFI_HANDLE h, EFI_DEVICE_PATH *imgpath)
393329140Skevans{
394329140Skevans	EFI_STATUS status;
395329140Skevans	BOOLEAN preferred;
396329140Skevans
397329140Skevans	preferred = FALSE;
398329140Skevans	status = probe_handle(h, imgpath, &preferred);
399329140Skevans
400329140Skevans	DPRINTF("probe: ");
401329140Skevans	switch (status) {
402329140Skevans	case EFI_UNSUPPORTED:
403329140Skevans		printf(".");
404329140Skevans		DPRINTF(" not supported\n");
405329140Skevans		break;
406329140Skevans	case EFI_SUCCESS:
407329140Skevans		if (preferred) {
408329140Skevans			printf("%c", '*');
409329140Skevans			DPRINTF(" supported (preferred)\n");
410329140Skevans		} else {
411329140Skevans			printf("%c", '+');
412329140Skevans			DPRINTF(" supported\n");
413329140Skevans		}
414329140Skevans		break;
415329140Skevans	default:
416329140Skevans		printf("x");
417329140Skevans		DPRINTF(" error (%lu)\n", EFI_ERROR_CODE(status));
418329140Skevans		break;
419329140Skevans	}
420329140Skevans	DSTALL(500000);
421329140Skevans}
422329140Skevans
423329140SkevansEFI_STATUS
424329140Skevansefi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE *Xsystab)
425329140Skevans{
426329140Skevans	EFI_HANDLE *handles;
427329140Skevans	EFI_LOADED_IMAGE *img;
428329140Skevans	EFI_DEVICE_PATH *imgpath;
429329140Skevans	EFI_STATUS status;
430329140Skevans	EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL;
431329140Skevans	SIMPLE_TEXT_OUTPUT_INTERFACE *conout = NULL;
432329140Skevans	UINTN i, max_dim, best_mode, cols, rows, hsize, nhandles;
433329140Skevans	CHAR16 *text;
434329183Skevans	UINT16 boot_current;
435329183Skevans	size_t sz;
436329183Skevans	UINT16 boot_order[100];
437329140Skevans
438329140Skevans	/* Basic initialization*/
439329140Skevans	ST = Xsystab;
440329140Skevans	IH = Ximage;
441329140Skevans	BS = ST->BootServices;
442329140Skevans	RS = ST->RuntimeServices;
443329140Skevans
444329140Skevans	/* Set up the console, so printf works. */
445329140Skevans	status = BS->LocateProtocol(&ConsoleControlGUID, NULL,
446329140Skevans	    (VOID **)&ConsoleControl);
447329140Skevans	if (status == EFI_SUCCESS)
448329140Skevans		(void)ConsoleControl->SetMode(ConsoleControl,
449329140Skevans		    EfiConsoleControlScreenText);
450281059Srpaulo	/*
451281059Srpaulo	 * Reset the console and find the best text mode.
452281059Srpaulo	 */
453329114Skevans	conout = ST->ConOut;
454281059Srpaulo	conout->Reset(conout, TRUE);
455281059Srpaulo	max_dim = best_mode = 0;
456329140Skevans	for (i = 0; ; i++) {
457293274Ssmh		status = conout->QueryMode(conout, i, &cols, &rows);
458281059Srpaulo		if (EFI_ERROR(status))
459281059Srpaulo			break;
460281059Srpaulo		if (cols * rows > max_dim) {
461281059Srpaulo			max_dim = cols * rows;
462281059Srpaulo			best_mode = i;
463281059Srpaulo		}
464281059Srpaulo	}
465329140Skevans	if (max_dim > 0)
466281059Srpaulo		conout->SetMode(conout, best_mode);
467281059Srpaulo	conout->EnableCursor(conout, TRUE);
468281059Srpaulo	conout->ClearScreen(conout);
469271762Semaste
470294060Ssmh	printf("\n>> FreeBSD EFI boot block\n");
471294765Simp	printf("   Loader path: %s\n\n", PATH_LOADER_EFI);
472329140Skevans	printf("   Initializing modules:");
473329140Skevans	for (i = 0; i < NUM_BOOT_MODULES; i++) {
474329140Skevans		printf(" %s", boot_modules[i]->name);
475329140Skevans		if (boot_modules[i]->init != NULL)
476329140Skevans			boot_modules[i]->init();
477294060Ssmh	}
478329140Skevans	putchar('\n');
479264391Snwhitehorn
480329114Skevans	/* Determine the devpath of our image so we can prefer it. */
481329140Skevans	status = BS->HandleProtocol(IH, &LoadedImageGUID, (VOID**)&img);
482329114Skevans	imgpath = NULL;
483329114Skevans	if (status == EFI_SUCCESS) {
484329140Skevans		text = efi_devpath_name(img->FilePath);
485329140Skevans		if (text != NULL) {
486329140Skevans			printf("   Load Path: %S\n", text);
487329140Skevans			efi_setenv_freebsd_wcs("Boot1Path", text);
488329140Skevans			efi_free_devpath_name(text);
489329140Skevans		}
490329114Skevans
491329140Skevans		status = BS->HandleProtocol(img->DeviceHandle, &DevicePathGUID,
492329114Skevans		    (void **)&imgpath);
493329114Skevans		if (status != EFI_SUCCESS) {
494329114Skevans			DPRINTF("Failed to get image DevicePath (%lu)\n",
495329114Skevans			    EFI_ERROR_CODE(status));
496329114Skevans		} else {
497329114Skevans			text = efi_devpath_name(imgpath);
498329140Skevans			if (text != NULL) {
499329140Skevans				printf("   Load Device: %S\n", text);
500329140Skevans				efi_setenv_freebsd_wcs("Boot1Dev", text);
501329140Skevans				efi_free_devpath_name(text);
502329140Skevans			}
503329114Skevans		}
504329140Skevans	}
505329114Skevans
506329183Skevans	boot_current = 0;
507329183Skevans	sz = sizeof(boot_current);
508329183Skevans	efi_getenv(&GlobalBootVarGUID, "BootCurrent", &boot_current, &sz);
509329183Skevans	printf("   BootCurrent: %04x\n", boot_current);
510329183Skevans
511329183Skevans	sz = sizeof(boot_order);
512329183Skevans	efi_getenv(&GlobalBootVarGUID, "BootOrder", &boot_order, &sz);
513329183Skevans	printf("   BootOrder:");
514329183Skevans	for (i = 0; i < sz / sizeof(boot_order[0]); i++)
515329183Skevans		printf(" %04x", boot_order[i]);
516329183Skevans	printf("\n");
517329183Skevans
518329183Skevans#ifdef TEST_FAILURE
519329183Skevans	/*
520329183Skevans	 * For testing failover scenarios, it's nice to be able to fail fast.
521329183Skevans	 * Define TEST_FAILURE to create a boot1.efi that always fails after
522329183Skevans	 * reporting the boot manager protocol details.
523329183Skevans	 */
524329183Skevans	BS->Exit(IH, EFI_OUT_OF_RESOURCES, 0, NULL);
525329183Skevans#endif
526329183Skevans
527329140Skevans	/* Get all the device handles */
528329140Skevans	hsize = (UINTN)NUM_HANDLES_INIT * sizeof(EFI_HANDLE);
529329140Skevans	handles = malloc(hsize);
530329183Skevans	if (handles == NULL)
531329140Skevans		printf("Failed to allocate %d handles\n", NUM_HANDLES_INIT);
532329114Skevans
533329140Skevans	status = BS->LocateHandle(ByProtocol, &BlockIoProtocolGUID, NULL,
534329140Skevans	    &hsize, handles);
535329140Skevans	switch (status) {
536329140Skevans	case EFI_SUCCESS:
537329140Skevans		break;
538329140Skevans	case EFI_BUFFER_TOO_SMALL:
539329140Skevans		free(handles);
540329140Skevans		handles = malloc(hsize);
541329140Skevans		if (handles == NULL)
542329140Skevans			efi_panic(EFI_OUT_OF_RESOURCES, "Failed to allocate %d handles\n",
543329140Skevans			    NUM_HANDLES_INIT);
544329140Skevans		status = BS->LocateHandle(ByProtocol, &BlockIoProtocolGUID,
545329140Skevans		    NULL, &hsize, handles);
546329140Skevans		if (status != EFI_SUCCESS)
547329140Skevans			efi_panic(status, "Failed to get device handles\n");
548329140Skevans		break;
549329140Skevans	default:
550329140Skevans		efi_panic(status, "Failed to get device handles\n");
551329140Skevans		break;
552329140Skevans	}
553264391Snwhitehorn
554329140Skevans	/* Scan all partitions, probing with all modules. */
555329140Skevans	nhandles = hsize / sizeof(*handles);
556329140Skevans	printf("   Probing %zu block devices...", nhandles);
557329140Skevans	DPRINTF("\n");
558329135Skevans
559329140Skevans	for (i = 0; i < nhandles; i++)
560329140Skevans		probe_handle_status(handles[i], imgpath);
561329140Skevans	printf(" done\n");
562329135Skevans
563329140Skevans	/* Status summary. */
564329140Skevans	for (i = 0; i < NUM_BOOT_MODULES; i++) {
565329140Skevans		printf("    ");
566329140Skevans		boot_modules[i]->status();
567294060Ssmh	}
568264391Snwhitehorn
569295320Ssmh	try_boot();
570281058Srpaulo
571294060Ssmh	/* If we get here, we're out of luck... */
572329114Skevans	efi_panic(EFI_LOAD_ERROR, "No bootable partitions found!");
573264391Snwhitehorn}
574264391Snwhitehorn
575295320Ssmh/*
576329140Skevans * add_device adds a device to the passed devinfo list.
577329140Skevans */
578329140Skevansvoid
579329140Skevansadd_device(dev_info_t **devinfop, dev_info_t *devinfo)
580329140Skevans{
581329140Skevans	dev_info_t *dev;
582329140Skevans
583329140Skevans	if (*devinfop == NULL) {
584329140Skevans		*devinfop = devinfo;
585329140Skevans		return;
586329140Skevans	}
587329140Skevans
588329140Skevans	for (dev = *devinfop; dev->next != NULL; dev = dev->next)
589329140Skevans		;
590329140Skevans
591329140Skevans	dev->next = devinfo;
592329140Skevans}
593329140Skevans
594329140Skevans/*
595329114Skevans * OK. We totally give up. Exit back to EFI with a sensible status so
596329114Skevans * it can try the next option on the list.
597329114Skevans */
598329114Skevansstatic void
599329114Skevansefi_panic(EFI_STATUS s, const char *fmt, ...)
600264391Snwhitehorn{
601329140Skevans	va_list ap;
602264391Snwhitehorn
603329140Skevans	printf("panic: ");
604329140Skevans	va_start(ap, fmt);
605329140Skevans	vprintf(fmt, ap);
606329140Skevans	va_end(ap);
607329140Skevans	printf("\n");
608264391Snwhitehorn
609329140Skevans	BS->Exit(IH, s, 0, NULL);
610264391Snwhitehorn}
611329140Skevans
612329140Skevansvoid
613329140Skevansputchar(int c)
614329140Skevans{
615329140Skevans	CHAR16 buf[2];
616329140Skevans
617329140Skevans	if (c == '\n') {
618329140Skevans		buf[0] = '\r';
619329140Skevans		buf[1] = 0;
620329140Skevans		ST->ConOut->OutputString(ST->ConOut, buf);
621329140Skevans	}
622329140Skevans	buf[0] = c;
623329140Skevans	buf[1] = 0;
624329140Skevans	ST->ConOut->OutputString(ST->ConOut, buf);
625329140Skevans}
626