xref: /freebsd/stand/efi/boot1/proto.c (revision 1f474190)
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 
38 static EFI_GUID BlockIoProtocolGUID = BLOCK_IO_PROTOCOL;
39 static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL;
40 
41 #ifndef EFI_DEBUG
42 static 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  */
55 static int
56 probe_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  */
130 static EFI_STATUS
131 load_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 
156 void
157 choice_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