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