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