boot1.c revision 294768
1118611Snjl/*-
2118611Snjl * Copyright (c) 1998 Robert Nordier
3118611Snjl * All rights reserved.
4118611Snjl * Copyright (c) 2001 Robert Drehmel
5118611Snjl * All rights reserved.
6118611Snjl * Copyright (c) 2014 Nathan Whitehorn
7217365Sjkim * All rights reserved.
8229989Sjkim * Copyright (c) 2015 Eric McCorkle
9118611Snjl * All rights reserved.
10118611Snjl *
11217365Sjkim * Redistribution and use in source and binary forms are freely
12217365Sjkim * permitted provided that the above copyright notice and this
13217365Sjkim * paragraph and the following disclaimer are duplicated in all
14217365Sjkim * such forms.
15217365Sjkim *
16217365Sjkim * This software is provided "AS IS" and without any express or
17217365Sjkim * implied warranties, including, without limitation, the implied
18217365Sjkim * warranties of merchantability and fitness for a particular
19217365Sjkim * purpose.
20217365Sjkim */
21217365Sjkim
22217365Sjkim#include <sys/cdefs.h>
23217365Sjkim__FBSDID("$FreeBSD: head/sys/boot/efi/boot1/boot1.c 294768 2016-01-26 06:26:55Z imp $");
24217365Sjkim
25118611Snjl#include <sys/param.h>
26217365Sjkim#include <machine/elf.h>
27217365Sjkim#include <machine/stdarg.h>
28217365Sjkim#include <stand.h>
29118611Snjl
30217365Sjkim#include <efi.h>
31217365Sjkim#include <eficonsctl.h>
32217365Sjkim
33217365Sjkim#include "boot_module.h"
34217365Sjkim#include "paths.h"
35217365Sjkim
36217365Sjkimstatic const boot_module_t *boot_modules[] =
37217365Sjkim{
38217365Sjkim#ifdef EFI_ZFS_BOOT
39217365Sjkim	&zfs_module,
40217365Sjkim#endif
41217365Sjkim#ifdef EFI_UFS_BOOT
42217365Sjkim	&ufs_module
43118611Snjl#endif
44118611Snjl};
45118611Snjl
46118611Snjl#define NUM_BOOT_MODULES (sizeof(boot_modules) / sizeof(boot_module_t*))
47151937Sjkim/* The initial number of handles used to query EFI for partitions. */
48193529Sjkim#define NUM_HANDLES_INIT	24
49210976Sjkim
50240716Sjkimvoid putchar(int c);
51118611SnjlEFI_STATUS efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE* Xsystab);
52118611Snjl
53118611Snjlstatic void try_load(const boot_module_t* mod);
54118611Snjlstatic EFI_STATUS probe_handle(EFI_HANDLE h);
55118611Snjl
56118611SnjlEFI_SYSTEM_TABLE *systab;
57118611SnjlEFI_BOOT_SERVICES *bs;
58118611Snjlstatic EFI_HANDLE *image;
59151937Sjkim
60118611Snjlstatic EFI_GUID BlockIoProtocolGUID = BLOCK_IO_PROTOCOL;
61151937Sjkimstatic EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL;
62151937Sjkimstatic EFI_GUID LoadedImageGUID = LOADED_IMAGE_PROTOCOL;
63151937Sjkimstatic EFI_GUID ConsoleControlGUID = EFI_CONSOLE_CONTROL_PROTOCOL_GUID;
64151937Sjkim
65151937Sjkim/*
66237412Sjkim * Provide Malloc / Free backed by EFIs AllocatePool / FreePool which ensures
67151937Sjkim * memory is correctly aligned avoiding EFI_INVALID_PARAMETER returns from
68151937Sjkim * EFI methods.
69151937Sjkim */
70151937Sjkimvoid *
71151937SjkimMalloc(size_t len, const char *file __unused, int line __unused)
72151937Sjkim{
73240716Sjkim	void *out;
74240716Sjkim
75240716Sjkim	if (bs->AllocatePool(EfiLoaderData, len, &out) == EFI_SUCCESS)
76240716Sjkim		return (out);
77151937Sjkim
78151937Sjkim	return (NULL);
79151937Sjkim}
80151937Sjkim
81193529Sjkimvoid
82151937SjkimFree(void *buf, const char *file __unused, int line __unused)
83151937Sjkim{
84151937Sjkim	(void)bs->FreePool(buf);
85151937Sjkim}
86197104Sjkim
87197104Sjkim/*
88197104Sjkim * This function only returns if it fails to load the kernel. If it
89197104Sjkim * succeeds, it simply boots the kernel.
90197104Sjkim */
91151937Sjkimvoid
92197104Sjkimtry_load(const boot_module_t *mod)
93197104Sjkim{
94197104Sjkim	size_t bufsize, cmdsize;
95197104Sjkim	void *buf;
96197104Sjkim	char *cmd;
97197104Sjkim	dev_info_t *dev;
98197104Sjkim	EFI_HANDLE loaderhandle;
99197104Sjkim	EFI_LOADED_IMAGE *loaded_image;
100197104Sjkim	EFI_STATUS status;
101197104Sjkim
102197104Sjkim	/*
103240716Sjkim	 * Read in and parse the command line from /boot.config or /boot/config,
104197104Sjkim	 * if present. We'll pass it the next stage via a simple ASCII
105197104Sjkim	 * string. loader.efi has a hack for ASCII strings, so we'll use that to
106118611Snjl	 * keep the size down here. We only try to read the alternate file if
107118611Snjl	 * we get EFI_NOT_FOUND because all other errors mean that the boot_module
108118611Snjl	 * had troubles with the filesystem. We could return early, but we'll let
109118611Snjl	 * loading the actual kernel sort all that out. Since these files are
110118611Snjl	 * optional, we don't report errors in trying to read them.
111118611Snjl	 */
112118611Snjl	cmd = NULL;
113118611Snjl	cmdsize = 0;
114118611Snjl	status = mod->load(PATH_DOTCONFIG, &dev, &buf, &bufsize);
115118611Snjl	if (status == EFI_NOT_FOUND)
116118611Snjl		status = mod->load(PATH_CONFIG, &dev, &buf, &bufsize);
117118611Snjl	if (status == EFI_SUCCESS) {
118151937Sjkim		cmdsize = bufsize + 1;
119118611Snjl		cmd = malloc(cmdsize);
120118611Snjl		if (cmd == NULL) {
121118611Snjl			free(buf);
122118611Snjl			return;
123228110Sjkim		}
124228110Sjkim		memcpy(cmd, buf, bufsize);
125228110Sjkim		cmd[bufsize] = '\0';
126241973Sjkim		free(buf);
127238381Sjkim	}
128197104Sjkim
129233250Sjkim	status = mod->load(PATH_LOADER_EFI, &dev, &buf, &bufsize);
130233250Sjkim	if (status == EFI_NOT_FOUND)
131233250Sjkim		return;
132233250Sjkim
133234623Sjkim	if (status != EFI_SUCCESS) {
134233250Sjkim		printf("%s failed to load %s (%lu)\n", mod->name,
135197104Sjkim		    PATH_LOADER_EFI, EFI_ERROR_CODE(status));
136228110Sjkim		return;
137228110Sjkim	}
138228110Sjkim
139228110Sjkim	if ((status = bs->LoadImage(TRUE, image, dev->devpath, buf, bufsize,
140228110Sjkim	    &loaderhandle)) != EFI_SUCCESS) {
141228110Sjkim		printf("Failed to load image provided by %s, size: %zu, (%lu)\n",
142233250Sjkim		     mod->name, bufsize, EFI_ERROR_CODE(status));
143234623Sjkim		return;
144118611Snjl	}
145241973Sjkim
146241973Sjkim	if (cmd != NULL)
147233250Sjkim		printf("    command args: %s\n", cmd);
148241973Sjkim
149118611Snjl	if ((status = bs->HandleProtocol(loaderhandle, &LoadedImageGUID,
150138287Smarks	    (VOID**)&loaded_image)) != EFI_SUCCESS) {
151228110Sjkim		printf("Failed to query LoadedImage provided by %s (%lu)\n",
152228110Sjkim		    mod->name, EFI_ERROR_CODE(status));
153228110Sjkim		return;
154228110Sjkim	}
155228110Sjkim
156228110Sjkim	loaded_image->DeviceHandle = dev->devhandle;
157118611Snjl	loaded_image->LoadOptionsSize = cmdsize;
158212761Sjkim	loaded_image->LoadOptions = cmd;
159228110Sjkim
160228110Sjkim	if ((status = bs->StartImage(loaderhandle, NULL, NULL)) !=
161228110Sjkim	    EFI_SUCCESS) {
162118611Snjl		printf("Failed to start image provided by %s (%lu)\n",
163209746Sjkim		    mod->name, EFI_ERROR_CODE(status));
164228110Sjkim		free(cmd);
165228110Sjkim		loaded_image->LoadOptionsSize = 0;
166209746Sjkim		loaded_image->LoadOptions = NULL;
167118611Snjl		return;
168228110Sjkim	}
169228110Sjkim}
170240716Sjkim
171228110SjkimEFI_STATUS
172228110Sjkimefi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE *Xsystab)
173228110Sjkim{
174228110Sjkim	EFI_HANDLE *handles;
175228110Sjkim	EFI_STATUS status;
176241973Sjkim	EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL;
177118611Snjl	SIMPLE_TEXT_OUTPUT_INTERFACE *conout = NULL;
178118611Snjl	UINTN i, max_dim, best_mode, cols, rows, hsize, nhandles;
179237412Sjkim
180228110Sjkim	/* Basic initialization*/
181237412Sjkim	systab = Xsystab;
182228110Sjkim	image = Ximage;
183228110Sjkim	bs = Xsystab->BootServices;
184237412Sjkim
185237412Sjkim	/* Set up the console, so printf works. */
186237412Sjkim	status = bs->LocateProtocol(&ConsoleControlGUID, NULL,
187237412Sjkim	    (VOID **)&ConsoleControl);
188237412Sjkim	if (status == EFI_SUCCESS)
189237412Sjkim		(void)ConsoleControl->SetMode(ConsoleControl,
190237412Sjkim		    EfiConsoleControlScreenText);
191237412Sjkim	/*
192118611Snjl	 * Reset the console and find the best text mode.
193118611Snjl	 */
194118611Snjl	conout = systab->ConOut;
195118611Snjl	conout->Reset(conout, TRUE);
196118611Snjl	max_dim = best_mode = 0;
197237412Sjkim	for (i = 0; ; i++) {
198118611Snjl		status = conout->QueryMode(conout, i, &cols, &rows);
199118611Snjl		if (EFI_ERROR(status))
200118611Snjl			break;
201118611Snjl		if (cols * rows > max_dim) {
202118611Snjl			max_dim = cols * rows;
203237412Sjkim			best_mode = i;
204118611Snjl		}
205118611Snjl	}
206118611Snjl	if (max_dim > 0)
207151937Sjkim		conout->SetMode(conout, best_mode);
208237412Sjkim	conout->EnableCursor(conout, TRUE);
209118611Snjl	conout->ClearScreen(conout);
210118611Snjl
211118611Snjl	printf("\n>> FreeBSD EFI boot block\n");
212228110Sjkim	printf("   Loader path: %s\n\n", PATH_LOADER_EFI);
213118611Snjl	printf("   Initializing modules:");
214241973Sjkim	for (i = 0; i < NUM_BOOT_MODULES; i++) {
215118611Snjl		if (boot_modules[i] == NULL)
216118611Snjl			continue;
217118611Snjl
218118611Snjl		printf(" %s", boot_modules[i]->name);
219118611Snjl		if (boot_modules[i]->init != NULL)
220118611Snjl			boot_modules[i]->init();
221118611Snjl	}
222118611Snjl	putchar('\n');
223118611Snjl
224118611Snjl	/* Get all the device handles */
225118611Snjl	hsize = (UINTN)NUM_HANDLES_INIT * sizeof(EFI_HANDLE);
226118611Snjl	if ((status = bs->AllocatePool(EfiLoaderData, hsize, (void **)&handles))
227118611Snjl	    != EFI_SUCCESS)
228118611Snjl		panic("Failed to allocate %d handles (%lu)", NUM_HANDLES_INIT,
229118611Snjl		    EFI_ERROR_CODE(status));
230118611Snjl
231118611Snjl	status = bs->LocateHandle(ByProtocol, &BlockIoProtocolGUID, NULL,
232118611Snjl	    &hsize, handles);
233118611Snjl	switch (status) {
234118611Snjl	case EFI_SUCCESS:
235151937Sjkim		break;
236118611Snjl	case EFI_BUFFER_TOO_SMALL:
237118611Snjl		(void)bs->FreePool(handles);
238118611Snjl		if ((status = bs->AllocatePool(EfiLoaderData, hsize,
239118611Snjl		    (void **)&handles) != EFI_SUCCESS)) {
240228110Sjkim			panic("Failed to allocate %zu handles (%lu)", hsize /
241228110Sjkim			    sizeof(*handles), EFI_ERROR_CODE(status));
242118611Snjl		}
243118611Snjl		status = bs->LocateHandle(ByProtocol, &BlockIoProtocolGUID,
244118611Snjl		    NULL, &hsize, handles);
245118611Snjl		if (status != EFI_SUCCESS)
246240716Sjkim			panic("Failed to get device handles (%lu)\n",
247240716Sjkim			    EFI_ERROR_CODE(status));
248240716Sjkim		break;
249240716Sjkim	default:
250240716Sjkim		panic("Failed to get device handles (%lu)",
251240716Sjkim		    EFI_ERROR_CODE(status));
252240716Sjkim	}
253240716Sjkim
254240716Sjkim	/* Scan all partitions, probing with all modules. */
255240716Sjkim	nhandles = hsize / sizeof(*handles);
256240716Sjkim	printf("   Probing %zu block devices...", nhandles);
257240716Sjkim	for (i = 0; i < nhandles; i++) {
258240716Sjkim		status = probe_handle(handles[i]);
259240716Sjkim		switch (status) {
260240716Sjkim		case EFI_UNSUPPORTED:
261240716Sjkim			printf(".");
262240716Sjkim			break;
263240716Sjkim		case EFI_SUCCESS:
264240716Sjkim			printf("+");
265240716Sjkim			break;
266240716Sjkim		default:
267240716Sjkim			printf("x");
268240716Sjkim			break;
269240716Sjkim		}
270240716Sjkim	}
271240716Sjkim	printf(" done\n");
272240716Sjkim
273240716Sjkim	/* Status summary. */
274240716Sjkim	for (i = 0; i < NUM_BOOT_MODULES; i++) {
275240716Sjkim		if (boot_modules[i] != NULL) {
276240716Sjkim			printf("    ");
277240716Sjkim			boot_modules[i]->status();
278240716Sjkim		}
279240716Sjkim	}
280240716Sjkim
281240716Sjkim	/* Select a partition to boot by trying each module in order. */
282240716Sjkim	for (i = 0; i < NUM_BOOT_MODULES; i++)
283240716Sjkim		if (boot_modules[i] != NULL)
284240716Sjkim			try_load(boot_modules[i]);
285240716Sjkim
286240716Sjkim	/* If we get here, we're out of luck... */
287240716Sjkim	panic("No bootable partitions found!");
288240716Sjkim}
289118611Snjl
290118611Snjlstatic EFI_STATUS
291118611Snjlprobe_handle(EFI_HANDLE h)
292118611Snjl{
293118611Snjl	dev_info_t *devinfo;
294118611Snjl	EFI_BLOCK_IO *blkio;
295118611Snjl	EFI_DEVICE_PATH *devpath;
296118611Snjl	EFI_STATUS status;
297118611Snjl	UINTN i;
298118611Snjl
299118611Snjl	/* Figure out if we're dealing with an actual partition. */
300118611Snjl	status = bs->HandleProtocol(h, &DevicePathGUID, (void **)&devpath);
301151937Sjkim	if (status == EFI_UNSUPPORTED)
302151937Sjkim		return (status);
303151937Sjkim
304118611Snjl	if (status != EFI_SUCCESS) {
305118611Snjl		DPRINTF("\nFailed to query DevicePath (%lu)\n",
306118611Snjl		    EFI_ERROR_CODE(status));
307118611Snjl		return (status);
308118611Snjl	}
309118611Snjl
310118611Snjl	while (!IsDevicePathEnd(NextDevicePathNode(devpath)))
311118611Snjl		devpath = NextDevicePathNode(devpath);
312118611Snjl
313118611Snjl	status = bs->HandleProtocol(h, &BlockIoProtocolGUID, (void **)&blkio);
314118611Snjl	if (status == EFI_UNSUPPORTED)
315118611Snjl		return (status);
316118611Snjl
317118611Snjl	if (status != EFI_SUCCESS) {
318118611Snjl		DPRINTF("\nFailed to query BlockIoProtocol (%lu)\n",
319118611Snjl		    EFI_ERROR_CODE(status));
320118611Snjl		return (status);
321118611Snjl	}
322118611Snjl
323118611Snjl	if (!blkio->Media->LogicalPartition)
324240716Sjkim		return (EFI_UNSUPPORTED);
325240716Sjkim
326240716Sjkim	/* Run through each module, see if it can load this partition */
327240716Sjkim	for (i = 0; i < NUM_BOOT_MODULES; i++) {
328240716Sjkim		if (boot_modules[i] == NULL)
329118611Snjl			continue;
330118611Snjl
331118611Snjl		if ((status = bs->AllocatePool(EfiLoaderData,
332118611Snjl		    sizeof(*devinfo), (void **)&devinfo)) !=
333118611Snjl		    EFI_SUCCESS) {
334197104Sjkim			DPRINTF("\nFailed to allocate devinfo (%lu)\n",
335118611Snjl			    EFI_ERROR_CODE(status));
336197104Sjkim			continue;
337197104Sjkim		}
338118611Snjl		devinfo->dev = blkio;
339118611Snjl		devinfo->devpath = devpath;
340118611Snjl		devinfo->devhandle = h;
341197104Sjkim		devinfo->devdata = NULL;
342118611Snjl		devinfo->next = NULL;
343118611Snjl
344118611Snjl		status = boot_modules[i]->probe(devinfo);
345197104Sjkim		if (status == EFI_SUCCESS)
346197104Sjkim			return (EFI_SUCCESS);
347197104Sjkim		(void)bs->FreePool(devinfo);
348197104Sjkim	}
349197104Sjkim
350197104Sjkim	return (EFI_UNSUPPORTED);
351197104Sjkim}
352197104Sjkim
353197104Sjkimvoid
354197104Sjkimadd_device(dev_info_t **devinfop, dev_info_t *devinfo)
355197104Sjkim{
356197104Sjkim	dev_info_t *dev;
357197104Sjkim
358197104Sjkim	if (*devinfop == NULL) {
359197104Sjkim		*devinfop = devinfo;
360197104Sjkim		return;
361197104Sjkim	}
362197104Sjkim
363197104Sjkim	for (dev = *devinfop; dev->next != NULL; dev = dev->next)
364197104Sjkim		;
365197104Sjkim
366197104Sjkim	dev->next = devinfo;
367197104Sjkim}
368197104Sjkim
369197104Sjkimvoid
370197104Sjkimpanic(const char *fmt, ...)
371197104Sjkim{
372197104Sjkim	va_list ap;
373197104Sjkim
374197104Sjkim	printf("panic: ");
375197104Sjkim	va_start(ap, fmt);
376193529Sjkim	vprintf(fmt, ap);
377197104Sjkim	va_end(ap);
378197104Sjkim	printf("\n");
379118611Snjl
380197104Sjkim	while (1) {}
381197104Sjkim}
382197104Sjkim
383197104Sjkimvoid
384197104Sjkimputchar(int c)
385118611Snjl{
386118611Snjl	CHAR16 buf[2];
387197104Sjkim
388197104Sjkim	if (c == '\n') {
389197104Sjkim		buf[0] = '\r';
390197104Sjkim		buf[1] = 0;
391197104Sjkim		systab->ConOut->OutputString(systab->ConOut, buf);
392241973Sjkim	}
393197104Sjkim	buf[0] = c;
394118611Snjl	buf[1] = 0;
395197104Sjkim	systab->ConOut->OutputString(systab->ConOut, buf);
396197104Sjkim}
397197104Sjkim