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