boot1.c revision 295453
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 295453 2016-02-09 22:32:24Z emaste $");
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;
95	void *buf;
96	dev_info_t *dev;
97	EFI_HANDLE loaderhandle;
98	EFI_LOADED_IMAGE *loaded_image;
99	EFI_STATUS status;
100
101	status = mod->load(PATH_LOADER_EFI, &dev, &buf, &bufsize);
102	if (status == EFI_NOT_FOUND)
103		return;
104
105	if (status != EFI_SUCCESS) {
106		printf("%s failed to load %s (%lu)\n", mod->name,
107		    PATH_LOADER_EFI, EFI_ERROR_CODE(status));
108		return;
109	}
110
111	if ((status = bs->LoadImage(TRUE, image, dev->devpath, buf, bufsize,
112	    &loaderhandle)) != EFI_SUCCESS) {
113		printf("Failed to load image provided by %s, size: %zu, (%lu)\n",
114		     mod->name, bufsize, EFI_ERROR_CODE(status));
115		return;
116	}
117
118	if ((status = bs->HandleProtocol(loaderhandle, &LoadedImageGUID,
119	    (VOID**)&loaded_image)) != EFI_SUCCESS) {
120		printf("Failed to query LoadedImage provided by %s (%lu)\n",
121		    mod->name, EFI_ERROR_CODE(status));
122		return;
123	}
124
125	loaded_image->DeviceHandle = dev->devhandle;
126
127	if ((status = bs->StartImage(loaderhandle, NULL, NULL)) !=
128	    EFI_SUCCESS) {
129		printf("Failed to start image provided by %s (%lu)\n",
130		    mod->name, EFI_ERROR_CODE(status));
131		return;
132	}
133}
134
135EFI_STATUS
136efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE *Xsystab)
137{
138	EFI_HANDLE *handles;
139	EFI_STATUS status;
140	EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL;
141	SIMPLE_TEXT_OUTPUT_INTERFACE *conout = NULL;
142	UINTN i, max_dim, best_mode, cols, rows, hsize, nhandles;
143
144	/* Basic initialization*/
145	systab = Xsystab;
146	image = Ximage;
147	bs = Xsystab->BootServices;
148
149	/* Set up the console, so printf works. */
150	status = bs->LocateProtocol(&ConsoleControlGUID, NULL,
151	    (VOID **)&ConsoleControl);
152	if (status == EFI_SUCCESS)
153		(void)ConsoleControl->SetMode(ConsoleControl,
154		    EfiConsoleControlScreenText);
155	/*
156	 * Reset the console and find the best text mode.
157	 */
158	conout = systab->ConOut;
159	conout->Reset(conout, TRUE);
160	max_dim = best_mode = 0;
161	for (i = 0; ; i++) {
162		status = conout->QueryMode(conout, i, &cols, &rows);
163		if (EFI_ERROR(status))
164			break;
165		if (cols * rows > max_dim) {
166			max_dim = cols * rows;
167			best_mode = i;
168		}
169	}
170	if (max_dim > 0)
171		conout->SetMode(conout, best_mode);
172	conout->EnableCursor(conout, TRUE);
173	conout->ClearScreen(conout);
174
175	printf("\n>> FreeBSD EFI boot block\n");
176	printf("   Loader path: %s\n\n", PATH_LOADER_EFI);
177	printf("   Initializing modules:");
178	for (i = 0; i < NUM_BOOT_MODULES; i++) {
179		if (boot_modules[i] == NULL)
180			continue;
181
182		printf(" %s", boot_modules[i]->name);
183		if (boot_modules[i]->init != NULL)
184			boot_modules[i]->init();
185	}
186	putchar('\n');
187
188	/* Get all the device handles */
189	hsize = (UINTN)NUM_HANDLES_INIT * sizeof(EFI_HANDLE);
190	if ((status = bs->AllocatePool(EfiLoaderData, hsize, (void **)&handles))
191	    != EFI_SUCCESS)
192		panic("Failed to allocate %d handles (%lu)", NUM_HANDLES_INIT,
193		    EFI_ERROR_CODE(status));
194
195	status = bs->LocateHandle(ByProtocol, &BlockIoProtocolGUID, NULL,
196	    &hsize, handles);
197	switch (status) {
198	case EFI_SUCCESS:
199		break;
200	case EFI_BUFFER_TOO_SMALL:
201		(void)bs->FreePool(handles);
202		if ((status = bs->AllocatePool(EfiLoaderData, hsize,
203		    (void **)&handles) != EFI_SUCCESS)) {
204			panic("Failed to allocate %zu handles (%lu)", hsize /
205			    sizeof(*handles), EFI_ERROR_CODE(status));
206		}
207		status = bs->LocateHandle(ByProtocol, &BlockIoProtocolGUID,
208		    NULL, &hsize, handles);
209		if (status != EFI_SUCCESS)
210			panic("Failed to get device handles (%lu)\n",
211			    EFI_ERROR_CODE(status));
212		break;
213	default:
214		panic("Failed to get device handles (%lu)",
215		    EFI_ERROR_CODE(status));
216	}
217
218	/* Scan all partitions, probing with all modules. */
219	nhandles = hsize / sizeof(*handles);
220	printf("   Probing %zu block devices...", nhandles);
221	for (i = 0; i < nhandles; i++) {
222		status = probe_handle(handles[i]);
223		switch (status) {
224		case EFI_UNSUPPORTED:
225			printf(".");
226			break;
227		case EFI_SUCCESS:
228			printf("+");
229			break;
230		default:
231			printf("x");
232			break;
233		}
234	}
235	printf(" done\n");
236
237	/* Status summary. */
238	for (i = 0; i < NUM_BOOT_MODULES; i++) {
239		if (boot_modules[i] != NULL) {
240			printf("    ");
241			boot_modules[i]->status();
242		}
243	}
244
245	/* Select a partition to boot by trying each module in order. */
246	for (i = 0; i < NUM_BOOT_MODULES; i++)
247		if (boot_modules[i] != NULL)
248			try_load(boot_modules[i]);
249
250	/* If we get here, we're out of luck... */
251	panic("No bootable partitions found!");
252}
253
254static EFI_STATUS
255probe_handle(EFI_HANDLE h)
256{
257	dev_info_t *devinfo;
258	EFI_BLOCK_IO *blkio;
259	EFI_DEVICE_PATH *devpath;
260	EFI_STATUS status;
261	UINTN i;
262
263	/* Figure out if we're dealing with an actual partition. */
264	status = bs->HandleProtocol(h, &DevicePathGUID, (void **)&devpath);
265	if (status == EFI_UNSUPPORTED)
266		return (status);
267
268	if (status != EFI_SUCCESS) {
269		DPRINTF("\nFailed to query DevicePath (%lu)\n",
270		    EFI_ERROR_CODE(status));
271		return (status);
272	}
273
274	while (!IsDevicePathEnd(NextDevicePathNode(devpath)))
275		devpath = NextDevicePathNode(devpath);
276
277	status = bs->HandleProtocol(h, &BlockIoProtocolGUID, (void **)&blkio);
278	if (status == EFI_UNSUPPORTED)
279		return (status);
280
281	if (status != EFI_SUCCESS) {
282		DPRINTF("\nFailed to query BlockIoProtocol (%lu)\n",
283		    EFI_ERROR_CODE(status));
284		return (status);
285	}
286
287	if (!blkio->Media->LogicalPartition)
288		return (EFI_UNSUPPORTED);
289
290	/* Run through each module, see if it can load this partition */
291	for (i = 0; i < NUM_BOOT_MODULES; i++) {
292		if (boot_modules[i] == NULL)
293			continue;
294
295		if ((status = bs->AllocatePool(EfiLoaderData,
296		    sizeof(*devinfo), (void **)&devinfo)) !=
297		    EFI_SUCCESS) {
298			DPRINTF("\nFailed to allocate devinfo (%lu)\n",
299			    EFI_ERROR_CODE(status));
300			continue;
301		}
302		devinfo->dev = blkio;
303		devinfo->devpath = devpath;
304		devinfo->devhandle = h;
305		devinfo->devdata = NULL;
306		devinfo->next = NULL;
307
308		status = boot_modules[i]->probe(devinfo);
309		if (status == EFI_SUCCESS)
310			return (EFI_SUCCESS);
311		(void)bs->FreePool(devinfo);
312	}
313
314	return (EFI_UNSUPPORTED);
315}
316
317void
318add_device(dev_info_t **devinfop, dev_info_t *devinfo)
319{
320	dev_info_t *dev;
321
322	if (*devinfop == NULL) {
323		*devinfop = devinfo;
324		return;
325	}
326
327	for (dev = *devinfop; dev->next != NULL; dev = dev->next)
328		;
329
330	dev->next = devinfo;
331}
332
333void
334panic(const char *fmt, ...)
335{
336	va_list ap;
337
338	printf("panic: ");
339	va_start(ap, fmt);
340	vprintf(fmt, ap);
341	va_end(ap);
342	printf("\n");
343
344	while (1) {}
345}
346
347void
348putchar(int c)
349{
350	CHAR16 buf[2];
351
352	if (c == '\n') {
353		buf[0] = '\r';
354		buf[1] = 0;
355		systab->ConOut->OutputString(systab->ConOut, buf);
356	}
357	buf[0] = c;
358	buf[1] = 0;
359	systab->ConOut->OutputString(systab->ConOut, buf);
360}
361