]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/efi/boot1/proto.c
loader.efi: replace HandleProtocol() with OpenProtocol()
[FreeBSD/FreeBSD.git] / stand / efi / boot1 / proto.c
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 static const char *prio_str[] = {
42         "error",
43         "not supported",
44         "good",
45         "better"
46 };
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
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
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
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 }