]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/efi/boot1/boot1.c
MFHead @348740
[FreeBSD/FreeBSD.git] / stand / efi / boot1 / boot1.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
37 static void efi_panic(EFI_STATUS s, const char *fmt, ...) __dead2 __printflike(2, 3);
38
39 static const boot_module_t *boot_modules[] =
40 {
41 #ifdef EFI_ZFS_BOOT
42         &zfs_module,
43 #endif
44 #ifdef EFI_UFS_BOOT
45         &ufs_module
46 #endif
47 };
48
49 #define NUM_BOOT_MODULES        nitems(boot_modules)
50
51 static EFI_GUID BlockIoProtocolGUID = BLOCK_IO_PROTOCOL;
52 static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL;
53 static EFI_GUID LoadedImageGUID = LOADED_IMAGE_PROTOCOL;
54 static EFI_GUID ConsoleControlGUID = EFI_CONSOLE_CONTROL_PROTOCOL_GUID;
55
56 /*
57  * Provide Malloc / Free / Calloc backed by EFIs AllocatePool / FreePool which ensures
58  * memory is correctly aligned avoiding EFI_INVALID_PARAMETER returns from
59  * EFI methods.
60  */
61
62 void *
63 Malloc(size_t len, const char *file __unused, int line __unused)
64 {
65         void *out;
66
67         if (BS->AllocatePool(EfiLoaderData, len, &out) == EFI_SUCCESS)
68                 return (out);
69
70         return (NULL);
71 }
72
73 void
74 Free(void *buf, const char *file __unused, int line __unused)
75 {
76         if (buf != NULL)
77                 (void)BS->FreePool(buf);
78 }
79
80 void *
81 Calloc(size_t n1, size_t n2, const char *file, int line)
82 {
83         size_t bytes;
84         void *res;
85
86         bytes = n1 * n2;
87         if ((res = Malloc(bytes, file, line)) != NULL)
88                 bzero(res, bytes);
89
90         return (res);
91 }
92
93 /*
94  * load_loader attempts to load the loader image data.
95  *
96  * It tries each module and its respective devices, identified by mod->probe,
97  * in order until a successful load occurs at which point it returns EFI_SUCCESS
98  * and EFI_NOT_FOUND otherwise.
99  *
100  * Only devices which have preferred matching the preferred parameter are tried.
101  */
102 static EFI_STATUS
103 load_loader(const boot_module_t **modp, dev_info_t **devinfop, void **bufp,
104     size_t *bufsize, BOOLEAN preferred)
105 {
106         UINTN i;
107         dev_info_t *dev;
108         const boot_module_t *mod;
109
110         for (i = 0; i < NUM_BOOT_MODULES; i++) {
111                 mod = boot_modules[i];
112                 for (dev = mod->devices(); dev != NULL; dev = dev->next) {
113                         if (dev->preferred != preferred)
114                                 continue;
115
116                         if (mod->load(PATH_LOADER_EFI, dev, bufp, bufsize) ==
117                             EFI_SUCCESS) {
118                                 *devinfop = dev;
119                                 *modp = mod;
120                                 return (EFI_SUCCESS);
121                         }
122                 }
123         }
124
125         return (EFI_NOT_FOUND);
126 }
127
128 /*
129  * try_boot only returns if it fails to load the loader. If it succeeds
130  * it simply boots, otherwise it returns the status of last EFI call.
131  */
132 static EFI_STATUS
133 try_boot(void)
134 {
135         size_t bufsize, loadersize, cmdsize;
136         void *buf, *loaderbuf;
137         char *cmd;
138         dev_info_t *dev;
139         const boot_module_t *mod;
140         EFI_HANDLE loaderhandle;
141         EFI_LOADED_IMAGE *loaded_image;
142         EFI_STATUS status;
143
144         status = load_loader(&mod, &dev, &loaderbuf, &loadersize, TRUE);
145         if (status != EFI_SUCCESS) {
146                 status = load_loader(&mod, &dev, &loaderbuf, &loadersize,
147                     FALSE);
148                 if (status != EFI_SUCCESS) {
149                         printf("Failed to load '%s'\n", PATH_LOADER_EFI);
150                         return (status);
151                 }
152         }
153
154         /*
155          * Read in and parse the command line from /boot.config or /boot/config,
156          * if present. We'll pass it the next stage via a simple ASCII
157          * string. loader.efi has a hack for ASCII strings, so we'll use that to
158          * keep the size down here. We only try to read the alternate file if
159          * we get EFI_NOT_FOUND because all other errors mean that the boot_module
160          * had troubles with the filesystem. We could return early, but we'll let
161          * loading the actual kernel sort all that out. Since these files are
162          * optional, we don't report errors in trying to read them.
163          */
164         cmd = NULL;
165         cmdsize = 0;
166         status = mod->load(PATH_DOTCONFIG, dev, &buf, &bufsize);
167         if (status == EFI_NOT_FOUND)
168                 status = mod->load(PATH_CONFIG, dev, &buf, &bufsize);
169         if (status == EFI_SUCCESS) {
170                 cmdsize = bufsize + 1;
171                 cmd = malloc(cmdsize);
172                 if (cmd == NULL)
173                         goto errout;
174                 memcpy(cmd, buf, bufsize);
175                 cmd[bufsize] = '\0';
176                 free(buf);
177                 buf = NULL;
178         }
179
180         if ((status = BS->LoadImage(TRUE, IH, efi_devpath_last_node(dev->devpath),
181             loaderbuf, loadersize, &loaderhandle)) != EFI_SUCCESS) {
182                 printf("Failed to load image provided by %s, size: %zu, (%lu)\n",
183                      mod->name, loadersize, EFI_ERROR_CODE(status));
184                 goto errout;
185         }
186
187         if ((status = BS->HandleProtocol(loaderhandle, &LoadedImageGUID,
188             (VOID**)&loaded_image)) != EFI_SUCCESS) {
189                 printf("Failed to query LoadedImage provided by %s (%lu)\n",
190                     mod->name, EFI_ERROR_CODE(status));
191                 goto errout;
192         }
193
194         if (cmd != NULL)
195                 printf("    command args: %s\n", cmd);
196
197         loaded_image->DeviceHandle = dev->devhandle;
198         loaded_image->LoadOptionsSize = cmdsize;
199         loaded_image->LoadOptions = cmd;
200
201         DPRINTF("Starting '%s' in 5 seconds...", PATH_LOADER_EFI);
202         DSTALL(1000000);
203         DPRINTF(".");
204         DSTALL(1000000);
205         DPRINTF(".");
206         DSTALL(1000000);
207         DPRINTF(".");
208         DSTALL(1000000);
209         DPRINTF(".");
210         DSTALL(1000000);
211         DPRINTF(".\n");
212
213         if ((status = BS->StartImage(loaderhandle, NULL, NULL)) !=
214             EFI_SUCCESS) {
215                 printf("Failed to start image provided by %s (%lu)\n",
216                     mod->name, EFI_ERROR_CODE(status));
217                 loaded_image->LoadOptionsSize = 0;
218                 loaded_image->LoadOptions = NULL;
219         }
220
221 errout:
222         if (cmd != NULL)
223                 free(cmd);
224         if (buf != NULL)
225                 free(buf);
226         if (loaderbuf != NULL)
227                 free(loaderbuf);
228
229         return (status);
230 }
231
232 /*
233  * probe_handle determines if the passed handle represents a logical partition
234  * if it does it uses each module in order to probe it and if successful it
235  * returns EFI_SUCCESS.
236  */
237 static EFI_STATUS
238 probe_handle(EFI_HANDLE h, EFI_DEVICE_PATH *imgpath, BOOLEAN *preferred)
239 {
240         dev_info_t *devinfo;
241         EFI_BLOCK_IO *blkio;
242         EFI_DEVICE_PATH *devpath;
243         EFI_STATUS status;
244         UINTN i;
245
246         /* Figure out if we're dealing with an actual partition. */
247         status = BS->HandleProtocol(h, &DevicePathGUID, (void **)&devpath);
248         if (status == EFI_UNSUPPORTED)
249                 return (status);
250
251         if (status != EFI_SUCCESS) {
252                 DPRINTF("\nFailed to query DevicePath (%lu)\n",
253                     EFI_ERROR_CODE(status));
254                 return (status);
255         }
256 #ifdef EFI_DEBUG
257         {
258                 CHAR16 *text = efi_devpath_name(devpath);
259                 DPRINTF("probing: %S\n", text);
260                 efi_free_devpath_name(text);
261         }
262 #endif
263         status = BS->HandleProtocol(h, &BlockIoProtocolGUID, (void **)&blkio);
264         if (status == EFI_UNSUPPORTED)
265                 return (status);
266
267         if (status != EFI_SUCCESS) {
268                 DPRINTF("\nFailed to query BlockIoProtocol (%lu)\n",
269                     EFI_ERROR_CODE(status));
270                 return (status);
271         }
272
273         if (!blkio->Media->LogicalPartition)
274                 return (EFI_UNSUPPORTED);
275
276         *preferred = efi_devpath_same_disk(imgpath, devpath);
277
278         /* Run through each module, see if it can load this partition */
279         devinfo = malloc(sizeof(*devinfo));
280         if (devinfo == NULL) {
281                 DPRINTF("\nFailed to allocate devinfo\n");
282                 return (EFI_UNSUPPORTED);
283         }
284         devinfo->dev = blkio;
285         devinfo->devpath = devpath;
286         devinfo->devhandle = h;
287         devinfo->preferred = *preferred;
288         devinfo->next = NULL;
289
290         for (i = 0; i < NUM_BOOT_MODULES; i++) {
291                 devinfo->devdata = NULL;
292                 status = boot_modules[i]->probe(devinfo);
293                 if (status == EFI_SUCCESS)
294                         return (EFI_SUCCESS);
295         }
296         free(devinfo);
297
298         return (EFI_UNSUPPORTED);
299 }
300
301 /*
302  * probe_handle_status calls probe_handle and outputs the returned status
303  * of the call.
304  */
305 static void
306 probe_handle_status(EFI_HANDLE h, EFI_DEVICE_PATH *imgpath)
307 {
308         EFI_STATUS status;
309         BOOLEAN preferred;
310
311         preferred = FALSE;
312         status = probe_handle(h, imgpath, &preferred);
313         
314         DPRINTF("probe: ");
315         switch (status) {
316         case EFI_UNSUPPORTED:
317                 printf(".");
318                 DPRINTF(" not supported\n");
319                 break;
320         case EFI_SUCCESS:
321                 if (preferred) {
322                         printf("%c", '*');
323                         DPRINTF(" supported (preferred)\n");
324                 } else {
325                         printf("%c", '+');
326                         DPRINTF(" supported\n");
327                 }
328                 break;
329         default:
330                 printf("x");
331                 DPRINTF(" error (%lu)\n", EFI_ERROR_CODE(status));
332                 break;
333         }
334         DSTALL(500000);
335 }
336
337 EFI_STATUS
338 efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE *Xsystab)
339 {
340         EFI_HANDLE *handles;
341         EFI_LOADED_IMAGE *img;
342         EFI_DEVICE_PATH *imgpath;
343         EFI_STATUS status;
344         EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL;
345         SIMPLE_TEXT_OUTPUT_INTERFACE *conout = NULL;
346         UINTN i, hsize, nhandles;
347         CHAR16 *text;
348         UINT16 boot_current;
349         size_t sz;
350         UINT16 boot_order[100];
351
352         /* Basic initialization*/
353         ST = Xsystab;
354         IH = Ximage;
355         BS = ST->BootServices;
356         RS = ST->RuntimeServices;
357
358         /* Set up the console, so printf works. */
359         status = BS->LocateProtocol(&ConsoleControlGUID, NULL,
360             (VOID **)&ConsoleControl);
361         if (status == EFI_SUCCESS)
362                 (void)ConsoleControl->SetMode(ConsoleControl,
363                     EfiConsoleControlScreenText);
364         /*
365          * Reset the console enable the cursor. Later we'll choose a better
366          * console size through GOP/UGA.
367          */
368         conout = ST->ConOut;
369         conout->Reset(conout, TRUE);
370         /* Explicitly set conout to mode 0, 80x25 */
371         conout->SetMode(conout, 0);
372         conout->EnableCursor(conout, TRUE);
373         conout->ClearScreen(conout);
374
375         printf("\n>> FreeBSD EFI boot block\n");
376         printf("   Loader path: %s\n\n", PATH_LOADER_EFI);
377         printf("   Initializing modules:");
378         for (i = 0; i < NUM_BOOT_MODULES; i++) {
379                 printf(" %s", boot_modules[i]->name);
380                 if (boot_modules[i]->init != NULL)
381                         boot_modules[i]->init();
382         }
383         putchar('\n');
384
385         /* Determine the devpath of our image so we can prefer it. */
386         status = BS->HandleProtocol(IH, &LoadedImageGUID, (VOID**)&img);
387         imgpath = NULL;
388         if (status == EFI_SUCCESS) {
389                 text = efi_devpath_name(img->FilePath);
390                 if (text != NULL) {
391                         printf("   Load Path: %S\n", text);
392                         efi_setenv_freebsd_wcs("Boot1Path", text);
393                         efi_free_devpath_name(text);
394                 }
395
396                 status = BS->HandleProtocol(img->DeviceHandle, &DevicePathGUID,
397                     (void **)&imgpath);
398                 if (status != EFI_SUCCESS) {
399                         DPRINTF("Failed to get image DevicePath (%lu)\n",
400                             EFI_ERROR_CODE(status));
401                 } else {
402                         text = efi_devpath_name(imgpath);
403                         if (text != NULL) {
404                                 printf("   Load Device: %S\n", text);
405                                 efi_setenv_freebsd_wcs("Boot1Dev", text);
406                                 efi_free_devpath_name(text);
407                         }
408                 }
409         }
410
411         boot_current = 0;
412         sz = sizeof(boot_current);
413         if (efi_global_getenv("BootCurrent", &boot_current, &sz) == EFI_SUCCESS) {
414                 printf("   BootCurrent: %04x\n", boot_current);
415
416                 sz = sizeof(boot_order);
417                 if (efi_global_getenv("BootOrder", &boot_order, &sz) == EFI_SUCCESS) {
418                         printf("   BootOrder:");
419                         for (i = 0; i < sz / sizeof(boot_order[0]); i++)
420                                 printf(" %04x%s", boot_order[i],
421                                     boot_order[i] == boot_current ? "[*]" : "");
422                         printf("\n");
423                 }
424         }
425
426 #ifdef TEST_FAILURE
427         /*
428          * For testing failover scenarios, it's nice to be able to fail fast.
429          * Define TEST_FAILURE to create a boot1.efi that always fails after
430          * reporting the boot manager protocol details.
431          */
432         BS->Exit(IH, EFI_OUT_OF_RESOURCES, 0, NULL);
433 #endif
434
435         hsize = 0;
436         BS->LocateHandle(ByProtocol, &BlockIoProtocolGUID, NULL,
437             &hsize, NULL);
438         handles = malloc(hsize);
439         if (handles == NULL)
440                 efi_panic(EFI_OUT_OF_RESOURCES, "Failed to allocate %d handles\n",
441                     hsize);
442         status = BS->LocateHandle(ByProtocol, &BlockIoProtocolGUID,
443             NULL, &hsize, handles);
444         if (status != EFI_SUCCESS)
445                 efi_panic(status, "Failed to get device handles\n");
446
447         /* Scan all partitions, probing with all modules. */
448         nhandles = hsize / sizeof(*handles);
449         printf("   Probing %zu block devices...", nhandles);
450         DPRINTF("\n");
451
452         for (i = 0; i < nhandles; i++)
453                 probe_handle_status(handles[i], imgpath);
454         printf(" done\n");
455
456         /* Status summary. */
457         for (i = 0; i < NUM_BOOT_MODULES; i++) {
458                 printf("    ");
459                 boot_modules[i]->status();
460         }
461
462         try_boot();
463
464         /* If we get here, we're out of luck... */
465         efi_panic(EFI_LOAD_ERROR, "No bootable partitions found!");
466 }
467
468 /*
469  * add_device adds a device to the passed devinfo list.
470  */
471 void
472 add_device(dev_info_t **devinfop, dev_info_t *devinfo)
473 {
474         dev_info_t *dev;
475
476         if (*devinfop == NULL) {
477                 *devinfop = devinfo;
478                 return;
479         }
480
481         for (dev = *devinfop; dev->next != NULL; dev = dev->next)
482                 ;
483
484         dev->next = devinfo;
485 }
486
487 /*
488  * OK. We totally give up. Exit back to EFI with a sensible status so
489  * it can try the next option on the list.
490  */
491 static void
492 efi_panic(EFI_STATUS s, const char *fmt, ...)
493 {
494         va_list ap;
495
496         printf("panic: ");
497         va_start(ap, fmt);
498         vprintf(fmt, ap);
499         va_end(ap);
500         printf("\n");
501
502         BS->Exit(IH, s, 0, NULL);
503 }
504
505 void
506 putchar(int c)
507 {
508         CHAR16 buf[2];
509
510         if (c == '\n') {
511                 buf[0] = '\r';
512                 buf[1] = 0;
513                 ST->ConOut->OutputString(ST->ConOut, buf);
514         }
515         buf[0] = c;
516         buf[1] = 0;
517         ST->ConOut->OutputString(ST->ConOut, buf);
518 }