boot1.c revision 295539
1/*-
2 * Copyright (c) 1998 Robert Nordier
3 * All rights reserved.
4 * Copyright (c) 2001 Robert Drehmel
5 * All rights reserved.
6 * Copyright (c) 2014 Nathan Whitehorn
7 * All rights reserved.
8 * Copyright (c) 2015 Eric McCorkle
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms are freely
12 * permitted provided that the above copyright notice and this
13 * paragraph and the following disclaimer are duplicated in all
14 * such forms.
15 *
16 * This software is provided "AS IS" and without any express or
17 * implied warranties, including, without limitation, the implied
18 * warranties of merchantability and fitness for a particular
19 * purpose.
20 */
21
22#include <sys/cdefs.h>
23__FBSDID("$FreeBSD: stable/10/sys/boot/efi/boot1/boot1.c 295539 2016-02-11 17:57:42Z smh $");
24
25#include <sys/param.h>
26#include <machine/elf.h>
27#include <machine/stdarg.h>
28#include <stand.h>
29
30#include <efi.h>
31#include <eficonsctl.h>
32
33#include "boot_module.h"
34#include "paths.h"
35
36static const boot_module_t *boot_modules[] =
37{
38#ifdef EFI_ZFS_BOOT
39	&zfs_module,
40#endif
41#ifdef EFI_UFS_BOOT
42	&ufs_module
43#endif
44};
45
46#define NUM_BOOT_MODULES (sizeof(boot_modules) / sizeof(boot_module_t*))
47/* The initial number of handles used to query EFI for partitions. */
48#define NUM_HANDLES_INIT	24
49
50void putchar(int c);
51EFI_STATUS efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE* Xsystab);
52
53static void try_load(const boot_module_t* mod);
54static EFI_STATUS probe_handle(EFI_HANDLE h);
55
56EFI_SYSTEM_TABLE *systab;
57EFI_BOOT_SERVICES *bs;
58static EFI_HANDLE *image;
59
60static EFI_GUID BlockIoProtocolGUID = BLOCK_IO_PROTOCOL;
61static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL;
62static EFI_GUID LoadedImageGUID = LOADED_IMAGE_PROTOCOL;
63static EFI_GUID ConsoleControlGUID = EFI_CONSOLE_CONTROL_PROTOCOL_GUID;
64
65/*
66 * Provide Malloc / Free backed by EFIs AllocatePool / FreePool which ensures
67 * memory is correctly aligned avoiding EFI_INVALID_PARAMETER returns from
68 * EFI methods.
69 */
70void *
71Malloc(size_t len, const char *file __unused, int line __unused)
72{
73	void *out;
74
75	if (bs->AllocatePool(EfiLoaderData, len, &out) == EFI_SUCCESS)
76		return (out);
77
78	return (NULL);
79}
80
81void
82Free(void *buf, const char *file __unused, int line __unused)
83{
84	(void)bs->FreePool(buf);
85}
86
87/*
88 * This function only returns if it fails to load the kernel. If it
89 * succeeds, it simply boots the kernel.
90 */
91void
92try_load(const boot_module_t *mod)
93{
94	size_t bufsize, cmdsize;
95	void *buf;
96	char *cmd;
97	dev_info_t *dev;
98	EFI_HANDLE loaderhandle;
99	EFI_LOADED_IMAGE *loaded_image;
100	EFI_STATUS status;
101
102	/*
103	 * Read in and parse the command line from /boot.config or /boot/config,
104	 * if present. We'll pass it the next stage via a simple ASCII
105	 * string. loader.efi has a hack for ASCII strings, so we'll use that to
106	 * keep the size down here. We only try to read the alternate file if
107	 * we get EFI_NOT_FOUND because all other errors mean that the boot_module
108	 * had troubles with the filesystem. We could return early, but we'll let
109	 * loading the actual kernel sort all that out. Since these files are
110	 * optional, we don't report errors in trying to read them.
111	 */
112	cmd = NULL;
113	cmdsize = 0;
114	status = mod->load(PATH_DOTCONFIG, &dev, &buf, &bufsize);
115	if (status == EFI_NOT_FOUND)
116		status = mod->load(PATH_CONFIG, &dev, &buf, &bufsize);
117	if (status == EFI_SUCCESS) {
118		cmdsize = bufsize + 1;
119		cmd = malloc(cmdsize);
120		if (cmd == NULL) {
121			free(buf);
122			return;
123		}
124		memcpy(cmd, buf, bufsize);
125		cmd[bufsize] = '\0';
126		free(buf);
127	}
128
129	status = mod->load(PATH_LOADER_EFI, &dev, &buf, &bufsize);
130	if (status == EFI_NOT_FOUND)
131		return;
132
133	if (status != EFI_SUCCESS) {
134		printf("%s failed to load %s (%lu)\n", mod->name,
135		    PATH_LOADER_EFI, EFI_ERROR_CODE(status));
136		return;
137	}
138
139	if ((status = bs->LoadImage(TRUE, image, dev->devpath, buf, bufsize,
140	    &loaderhandle)) != EFI_SUCCESS) {
141		printf("Failed to load image provided by %s, size: %zu, (%lu)\n",
142		     mod->name, bufsize, EFI_ERROR_CODE(status));
143		return;
144	}
145
146	if (cmd != NULL)
147		printf("    command args: %s\n", cmd);
148
149	if ((status = bs->HandleProtocol(loaderhandle, &LoadedImageGUID,
150	    (VOID**)&loaded_image)) != EFI_SUCCESS) {
151		printf("Failed to query LoadedImage provided by %s (%lu)\n",
152		    mod->name, EFI_ERROR_CODE(status));
153		return;
154	}
155
156	loaded_image->DeviceHandle = dev->devhandle;
157	loaded_image->LoadOptionsSize = cmdsize;
158	loaded_image->LoadOptions = cmd;
159
160	if ((status = bs->StartImage(loaderhandle, NULL, NULL)) !=
161	    EFI_SUCCESS) {
162		printf("Failed to start image provided by %s (%lu)\n",
163		    mod->name, EFI_ERROR_CODE(status));
164		free(cmd);
165		loaded_image->LoadOptionsSize = 0;
166		loaded_image->LoadOptions = NULL;
167		return;
168	}
169}
170
171EFI_STATUS
172efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE *Xsystab)
173{
174	EFI_HANDLE *handles;
175	EFI_STATUS status;
176	EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL;
177	SIMPLE_TEXT_OUTPUT_INTERFACE *conout = NULL;
178	UINTN i, max_dim, best_mode, cols, rows, hsize, nhandles;
179
180	/* Basic initialization*/
181	systab = Xsystab;
182	image = Ximage;
183	bs = Xsystab->BootServices;
184
185	/* Set up the console, so printf works. */
186	status = bs->LocateProtocol(&ConsoleControlGUID, NULL,
187	    (VOID **)&ConsoleControl);
188	if (status == EFI_SUCCESS)
189		(void)ConsoleControl->SetMode(ConsoleControl,
190		    EfiConsoleControlScreenText);
191	/*
192	 * Reset the console and find the best text mode.
193	 */
194	conout = systab->ConOut;
195	conout->Reset(conout, TRUE);
196	max_dim = best_mode = 0;
197	for (i = 0; ; i++) {
198		status = conout->QueryMode(conout, i, &cols, &rows);
199		if (EFI_ERROR(status))
200			break;
201		if (cols * rows > max_dim) {
202			max_dim = cols * rows;
203			best_mode = i;
204		}
205	}
206	if (max_dim > 0)
207		conout->SetMode(conout, best_mode);
208	conout->EnableCursor(conout, TRUE);
209	conout->ClearScreen(conout);
210
211	printf("\n>> FreeBSD EFI boot block\n");
212	printf("   Loader path: %s\n\n", PATH_LOADER_EFI);
213	printf("   Initializing modules:");
214	for (i = 0; i < NUM_BOOT_MODULES; i++) {
215		if (boot_modules[i] == NULL)
216			continue;
217
218		printf(" %s", boot_modules[i]->name);
219		if (boot_modules[i]->init != NULL)
220			boot_modules[i]->init();
221	}
222	putchar('\n');
223
224	/* Get all the device handles */
225	hsize = (UINTN)NUM_HANDLES_INIT * sizeof(EFI_HANDLE);
226	if ((status = bs->AllocatePool(EfiLoaderData, hsize, (void **)&handles))
227	    != EFI_SUCCESS)
228		panic("Failed to allocate %d handles (%lu)", NUM_HANDLES_INIT,
229		    EFI_ERROR_CODE(status));
230
231	status = bs->LocateHandle(ByProtocol, &BlockIoProtocolGUID, NULL,
232	    &hsize, handles);
233	switch (status) {
234	case EFI_SUCCESS:
235		break;
236	case EFI_BUFFER_TOO_SMALL:
237		(void)bs->FreePool(handles);
238		if ((status = bs->AllocatePool(EfiLoaderData, hsize,
239		    (void **)&handles) != EFI_SUCCESS)) {
240			panic("Failed to allocate %zu handles (%lu)", hsize /
241			    sizeof(*handles), EFI_ERROR_CODE(status));
242		}
243		status = bs->LocateHandle(ByProtocol, &BlockIoProtocolGUID,
244		    NULL, &hsize, handles);
245		if (status != EFI_SUCCESS)
246			panic("Failed to get device handles (%lu)\n",
247			    EFI_ERROR_CODE(status));
248		break;
249	default:
250		panic("Failed to get device handles (%lu)",
251		    EFI_ERROR_CODE(status));
252	}
253
254	/* Scan all partitions, probing with all modules. */
255	nhandles = hsize / sizeof(*handles);
256	printf("   Probing %zu block devices...", nhandles);
257	for (i = 0; i < nhandles; i++) {
258		status = probe_handle(handles[i]);
259		switch (status) {
260		case EFI_UNSUPPORTED:
261			printf(".");
262			break;
263		case EFI_SUCCESS:
264			printf("+");
265			break;
266		default:
267			printf("x");
268			break;
269		}
270	}
271	printf(" done\n");
272
273	/* Status summary. */
274	for (i = 0; i < NUM_BOOT_MODULES; i++) {
275		if (boot_modules[i] != NULL) {
276			printf("    ");
277			boot_modules[i]->status();
278		}
279	}
280
281	/* Select a partition to boot by trying each module in order. */
282	for (i = 0; i < NUM_BOOT_MODULES; i++)
283		if (boot_modules[i] != NULL)
284			try_load(boot_modules[i]);
285
286	/* If we get here, we're out of luck... */
287	panic("No bootable partitions found!");
288}
289
290static EFI_STATUS
291probe_handle(EFI_HANDLE h)
292{
293	dev_info_t *devinfo;
294	EFI_BLOCK_IO *blkio;
295	EFI_DEVICE_PATH *devpath;
296	EFI_STATUS status;
297	UINTN i;
298
299	/* Figure out if we're dealing with an actual partition. */
300	status = bs->HandleProtocol(h, &DevicePathGUID, (void **)&devpath);
301	if (status == EFI_UNSUPPORTED)
302		return (status);
303
304	if (status != EFI_SUCCESS) {
305		DPRINTF("\nFailed to query DevicePath (%lu)\n",
306		    EFI_ERROR_CODE(status));
307		return (status);
308	}
309
310	while (!IsDevicePathEnd(NextDevicePathNode(devpath)))
311		devpath = NextDevicePathNode(devpath);
312
313	status = bs->HandleProtocol(h, &BlockIoProtocolGUID, (void **)&blkio);
314	if (status == EFI_UNSUPPORTED)
315		return (status);
316
317	if (status != EFI_SUCCESS) {
318		DPRINTF("\nFailed to query BlockIoProtocol (%lu)\n",
319		    EFI_ERROR_CODE(status));
320		return (status);
321	}
322
323	if (!blkio->Media->LogicalPartition)
324		return (EFI_UNSUPPORTED);
325
326	/* Run through each module, see if it can load this partition */
327	for (i = 0; i < NUM_BOOT_MODULES; i++) {
328		if (boot_modules[i] == NULL)
329			continue;
330
331		if ((status = bs->AllocatePool(EfiLoaderData,
332		    sizeof(*devinfo), (void **)&devinfo)) !=
333		    EFI_SUCCESS) {
334			DPRINTF("\nFailed to allocate devinfo (%lu)\n",
335			    EFI_ERROR_CODE(status));
336			continue;
337		}
338		devinfo->dev = blkio;
339		devinfo->devpath = devpath;
340		devinfo->devhandle = h;
341		devinfo->devdata = NULL;
342		devinfo->next = NULL;
343
344		status = boot_modules[i]->probe(devinfo);
345		if (status == EFI_SUCCESS)
346			return (EFI_SUCCESS);
347		(void)bs->FreePool(devinfo);
348	}
349
350	return (EFI_UNSUPPORTED);
351}
352
353void
354add_device(dev_info_t **devinfop, dev_info_t *devinfo)
355{
356	dev_info_t *dev;
357
358	if (*devinfop == NULL) {
359		*devinfop = devinfo;
360		return;
361	}
362
363	for (dev = *devinfop; dev->next != NULL; dev = dev->next)
364		;
365
366	dev->next = devinfo;
367}
368
369void
370panic(const char *fmt, ...)
371{
372	va_list ap;
373
374	printf("panic: ");
375	va_start(ap, fmt);
376	vprintf(fmt, ap);
377	va_end(ap);
378	printf("\n");
379
380	while (1) {}
381}
382
383void
384putchar(int c)
385{
386	CHAR16 buf[2];
387
388	if (c == '\n') {
389		buf[0] = '\r';
390		buf[1] = 0;
391		systab->ConOut->OutputString(systab->ConOut, buf);
392	}
393	buf[0] = c;
394	buf[1] = 0;
395	systab->ConOut->OutputString(systab->ConOut, buf);
396}
397