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