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