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