]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/efi/boot1/proto.c
loader/ficl: Rename the ficl compile set to X4th_compile_set
[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/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 }