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$");
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#include <efichar.h>
33
34#include "boot_module.h"
35#include "paths.h"
36#include "proto.h"
37
38static EFI_GUID BlockIoProtocolGUID = BLOCK_IO_PROTOCOL;
39static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL;
40
41#ifndef EFI_DEBUG
42static const char *prio_str[] = {
43	"error",
44	"not supported",
45	"good",
46	"better"
47};
48#endif
49
50/*
51 * probe_handle determines if the passed handle represents a logical partition
52 * if it does it uses each module in order to probe it and if successful it
53 * returns EFI_SUCCESS.
54 */
55static int
56probe_handle(EFI_HANDLE h, EFI_DEVICE_PATH *imgpath)
57{
58	dev_info_t *devinfo;
59	EFI_BLOCK_IO *blkio;
60	EFI_DEVICE_PATH *devpath;
61	EFI_STATUS status;
62	UINTN i;
63	int preferred;
64
65	/* Figure out if we're dealing with an actual partition. */
66	status = OpenProtocolByHandle(h, &DevicePathGUID, (void **)&devpath);
67	if (status == EFI_UNSUPPORTED)
68		return (0);
69
70	if (status != EFI_SUCCESS) {
71		DPRINTF("\nFailed to query DevicePath (%lu)\n",
72		    EFI_ERROR_CODE(status));
73		return (-1);
74	}
75#ifdef EFI_DEBUG
76	{
77		CHAR16 *text = efi_devpath_name(devpath);
78		DPRINTF("probing: %S ", text);
79		efi_free_devpath_name(text);
80	}
81#endif
82	status = OpenProtocolByHandle(h, &BlockIoProtocolGUID, (void **)&blkio);
83	if (status == EFI_UNSUPPORTED)
84		return (0);
85
86	if (status != EFI_SUCCESS) {
87		DPRINTF("\nFailed to query BlockIoProtocol (%lu)\n",
88		    EFI_ERROR_CODE(status));
89		return (-1);
90	}
91
92	if (!blkio->Media->LogicalPartition)
93		return (0);
94
95	preferred = efi_devpath_same_disk(imgpath, devpath);
96
97	/* Run through each module, see if it can load this partition */
98	devinfo = malloc(sizeof(*devinfo));
99	if (devinfo == NULL) {
100		DPRINTF("\nFailed to allocate devinfo\n");
101		return (-1);
102	}
103	devinfo->dev = blkio;
104	devinfo->devpath = devpath;
105	devinfo->devhandle = h;
106	devinfo->preferred = preferred;
107	devinfo->next = NULL;
108
109	for (i = 0; i < num_boot_modules; i++) {
110		devinfo->devdata = NULL;
111
112		status = boot_modules[i]->probe(devinfo);
113		if (status == EFI_SUCCESS)
114			return (preferred + 1);
115	}
116	free(devinfo);
117
118	return (0);
119}
120
121/*
122 * load_loader attempts to load the loader image data.
123 *
124 * It tries each module and its respective devices, identified by mod->probe,
125 * in order until a successful load occurs at which point it returns EFI_SUCCESS
126 * and EFI_NOT_FOUND otherwise.
127 *
128 * Only devices which have preferred matching the preferred parameter are tried.
129 */
130static EFI_STATUS
131load_loader(const boot_module_t **modp, dev_info_t **devinfop, void **bufp,
132    size_t *bufsize, int preferred)
133{
134	UINTN i;
135	dev_info_t *dev;
136	const boot_module_t *mod;
137
138	for (i = 0; i < num_boot_modules; i++) {
139		mod = boot_modules[i];
140		for (dev = mod->devices(); dev != NULL; dev = dev->next) {
141			if (dev->preferred != preferred)
142				continue;
143
144			if (mod->load(PATH_LOADER_EFI, dev, bufp, bufsize) ==
145			    EFI_SUCCESS) {
146				*devinfop = dev;
147				*modp = mod;
148				return (EFI_SUCCESS);
149			}
150		}
151	}
152
153	return (EFI_NOT_FOUND);
154}
155
156void
157choice_protocol(EFI_HANDLE *handles, UINTN nhandles, EFI_DEVICE_PATH *imgpath)
158{
159	UINT16 boot_current;
160	size_t sz;
161	UINT16 boot_order[100];
162	unsigned i;
163	int rv;
164	EFI_STATUS status;
165	const boot_module_t *mod;
166	dev_info_t *dev;
167	void *loaderbuf;
168	size_t loadersize;
169
170	/* Report UEFI Boot Manager Protocol details */
171	boot_current = 0;
172	sz = sizeof(boot_current);
173	if (efi_global_getenv("BootCurrent", &boot_current, &sz) == EFI_SUCCESS) {
174		printf("   BootCurrent: %04x\n", boot_current);
175
176		sz = sizeof(boot_order);
177		if (efi_global_getenv("BootOrder", &boot_order, &sz) == EFI_SUCCESS) {
178			printf("   BootOrder:");
179			for (i = 0; i < sz / sizeof(boot_order[0]); i++)
180				printf(" %04x%s", boot_order[i],
181				    boot_order[i] == boot_current ? "[*]" : "");
182			printf("\n");
183		}
184	}
185
186#ifdef TEST_FAILURE
187	/*
188	 * For testing failover scenarios, it's nice to be able to fail fast.
189	 * Define TEST_FAILURE to create a boot1.efi that always fails after
190	 * reporting the boot manager protocol details.
191	 */
192	BS->Exit(IH, EFI_OUT_OF_RESOURCES, 0, NULL);
193#endif
194
195	/* Scan all partitions, probing with all modules. */
196	printf("   Probing %zu block devices...", nhandles);
197	DPRINTF("\n");
198	for (i = 0; i < nhandles; i++) {
199		rv = probe_handle(handles[i], imgpath);
200#ifdef EFI_DEBUG
201		printf("%c", "x.+*"[rv + 1]);
202#else
203		printf("%s\n", prio_str[rv + 1]);
204#endif
205	}
206	printf(" done\n");
207
208
209	/* Status summary. */
210	for (i = 0; i < num_boot_modules; i++) {
211		printf("    ");
212		boot_modules[i]->status();
213	}
214
215	status = load_loader(&mod, &dev, &loaderbuf, &loadersize, 1);
216	if (status != EFI_SUCCESS) {
217		status = load_loader(&mod, &dev, &loaderbuf, &loadersize, 0);
218		if (status != EFI_SUCCESS) {
219			printf("Failed to load '%s'\n", PATH_LOADER_EFI);
220			return;
221		}
222	}
223
224	try_boot(mod, dev, loaderbuf, loadersize);
225}
226