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