]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/efi/boot1/boot1.c
loader.efi: boot1.c was left out of 350654
[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 #include "proto.h"
37
38 static void efi_panic(EFI_STATUS s, const char *fmt, ...) __dead2 __printflike(2, 3);
39
40 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 const UINTN 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  * try_boot only returns if it fails to load the loader. If it succeeds
95  * it simply boots, otherwise it returns the status of last EFI call.
96  */
97 EFI_STATUS
98 try_boot(const boot_module_t *mod, dev_info_t *dev, void *loaderbuf, size_t loadersize)
99 {
100         size_t bufsize, cmdsize;
101         void *buf;
102         char *cmd;
103         EFI_HANDLE loaderhandle;
104         EFI_LOADED_IMAGE *loaded_image;
105         EFI_STATUS status;
106
107         /*
108          * Read in and parse the command line from /boot.config or /boot/config,
109          * if present. We'll pass it the next stage via a simple ASCII
110          * string. loader.efi has a hack for ASCII strings, so we'll use that to
111          * keep the size down here. We only try to read the alternate file if
112          * we get EFI_NOT_FOUND because all other errors mean that the boot_module
113          * had troubles with the filesystem. We could return early, but we'll let
114          * loading the actual kernel sort all that out. Since these files are
115          * optional, we don't report errors in trying to read them.
116          */
117         cmd = NULL;
118         cmdsize = 0;
119         status = mod->load(PATH_DOTCONFIG, dev, &buf, &bufsize);
120         if (status == EFI_NOT_FOUND)
121                 status = mod->load(PATH_CONFIG, dev, &buf, &bufsize);
122         if (status == EFI_SUCCESS) {
123                 cmdsize = bufsize + 1;
124                 cmd = malloc(cmdsize);
125                 if (cmd == NULL)
126                         goto errout;
127                 memcpy(cmd, buf, bufsize);
128                 cmd[bufsize] = '\0';
129                 free(buf);
130                 buf = NULL;
131         }
132
133         if ((status = BS->LoadImage(TRUE, IH, efi_devpath_last_node(dev->devpath),
134             loaderbuf, loadersize, &loaderhandle)) != EFI_SUCCESS) {
135                 printf("Failed to load image provided by %s, size: %zu, (%lu)\n",
136                      mod->name, loadersize, EFI_ERROR_CODE(status));
137                 goto errout;
138         }
139
140         status = OpenProtocolByHandle(loaderhandle, &LoadedImageGUID,
141             (void **)&loaded_image);
142         if (status != EFI_SUCCESS) {
143                 printf("Failed to query LoadedImage provided by %s (%lu)\n",
144                     mod->name, EFI_ERROR_CODE(status));
145                 goto errout;
146         }
147
148         if (cmd != NULL)
149                 printf("    command args: %s\n", cmd);
150
151         loaded_image->DeviceHandle = dev->devhandle;
152         loaded_image->LoadOptionsSize = cmdsize;
153         loaded_image->LoadOptions = cmd;
154
155         DPRINTF("Starting '%s' in 5 seconds...", PATH_LOADER_EFI);
156         DSTALL(1000000);
157         DPRINTF(".");
158         DSTALL(1000000);
159         DPRINTF(".");
160         DSTALL(1000000);
161         DPRINTF(".");
162         DSTALL(1000000);
163         DPRINTF(".");
164         DSTALL(1000000);
165         DPRINTF(".\n");
166
167         if ((status = BS->StartImage(loaderhandle, NULL, NULL)) !=
168             EFI_SUCCESS) {
169                 printf("Failed to start image provided by %s (%lu)\n",
170                     mod->name, EFI_ERROR_CODE(status));
171                 loaded_image->LoadOptionsSize = 0;
172                 loaded_image->LoadOptions = NULL;
173         }
174
175 errout:
176         if (cmd != NULL)
177                 free(cmd);
178         if (buf != NULL)
179                 free(buf);
180         if (loaderbuf != NULL)
181                 free(loaderbuf);
182
183         return (status);
184 }
185
186 EFI_STATUS
187 efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE *Xsystab)
188 {
189         EFI_HANDLE *handles;
190         EFI_LOADED_IMAGE *img;
191         EFI_DEVICE_PATH *imgpath;
192         EFI_STATUS status;
193         EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL;
194         SIMPLE_TEXT_OUTPUT_INTERFACE *conout = NULL;
195         UINTN i, hsize, nhandles;
196         CHAR16 *text;
197
198         /* Basic initialization*/
199         ST = Xsystab;
200         IH = Ximage;
201         BS = ST->BootServices;
202         RS = ST->RuntimeServices;
203
204         /* Set up the console, so printf works. */
205         status = BS->LocateProtocol(&ConsoleControlGUID, NULL,
206             (VOID **)&ConsoleControl);
207         if (status == EFI_SUCCESS)
208                 (void)ConsoleControl->SetMode(ConsoleControl,
209                     EfiConsoleControlScreenText);
210         /*
211          * Reset the console enable the cursor. Later we'll choose a better
212          * console size through GOP/UGA.
213          */
214         conout = ST->ConOut;
215         conout->Reset(conout, TRUE);
216         /* Explicitly set conout to mode 0, 80x25 */
217         conout->SetMode(conout, 0);
218         conout->EnableCursor(conout, TRUE);
219         conout->ClearScreen(conout);
220
221         printf("\n>> FreeBSD EFI boot block\n");
222         printf("   Loader path: %s\n\n", PATH_LOADER_EFI);
223         printf("   Initializing modules:");
224         for (i = 0; i < num_boot_modules; i++) {
225                 printf(" %s", boot_modules[i]->name);
226                 if (boot_modules[i]->init != NULL)
227                         boot_modules[i]->init();
228         }
229         putchar('\n');
230
231         /* Fetch all the block I/O handles, we have to search through them later */
232         hsize = 0;
233         BS->LocateHandle(ByProtocol, &BlockIoProtocolGUID, NULL,
234             &hsize, NULL);
235         handles = malloc(hsize);
236         if (handles == NULL)
237                 efi_panic(EFI_OUT_OF_RESOURCES, "Failed to allocate %d handles\n",
238                     hsize);
239         status = BS->LocateHandle(ByProtocol, &BlockIoProtocolGUID,
240             NULL, &hsize, handles);
241         if (status != EFI_SUCCESS)
242                 efi_panic(status, "Failed to get device handles\n");
243         nhandles = hsize / sizeof(*handles);
244
245         /* Determine the devpath of our image so we can prefer it. */
246         status = OpenProtocolByHandle(IH, &LoadedImageGUID, (void **)&img);
247         imgpath = NULL;
248         if (status == EFI_SUCCESS) {
249                 text = efi_devpath_name(img->FilePath);
250                 if (text != NULL) {
251                         printf("   Load Path: %S\n", text);
252                         efi_setenv_freebsd_wcs("Boot1Path", text);
253                         efi_free_devpath_name(text);
254                 }
255
256                 status = OpenProtocolByHandle(img->DeviceHandle,
257                     &DevicePathGUID, (void **)&imgpath);
258                 if (status != EFI_SUCCESS) {
259                         DPRINTF("Failed to get image DevicePath (%lu)\n",
260                             EFI_ERROR_CODE(status));
261                 } else {
262                         text = efi_devpath_name(imgpath);
263                         if (text != NULL) {
264                                 printf("   Load Device: %S\n", text);
265                                 efi_setenv_freebsd_wcs("Boot1Dev", text);
266                                 efi_free_devpath_name(text);
267                         }
268                 }
269         }
270
271         choice_protocol(handles, nhandles, imgpath);
272
273         /* If we get here, we're out of luck... */
274         efi_panic(EFI_LOAD_ERROR, "No bootable partitions found!");
275 }
276
277 /*
278  * add_device adds a device to the passed devinfo list.
279  */
280 void
281 add_device(dev_info_t **devinfop, dev_info_t *devinfo)
282 {
283         dev_info_t *dev;
284
285         if (*devinfop == NULL) {
286                 *devinfop = devinfo;
287                 return;
288         }
289
290         for (dev = *devinfop; dev->next != NULL; dev = dev->next)
291                 ;
292
293         dev->next = devinfo;
294 }
295
296 /*
297  * OK. We totally give up. Exit back to EFI with a sensible status so
298  * it can try the next option on the list.
299  */
300 static void
301 efi_panic(EFI_STATUS s, const char *fmt, ...)
302 {
303         va_list ap;
304
305         printf("panic: ");
306         va_start(ap, fmt);
307         vprintf(fmt, ap);
308         va_end(ap);
309         printf("\n");
310
311         BS->Exit(IH, s, 0, NULL);
312 }
313
314 void
315 putchar(int c)
316 {
317         CHAR16 buf[2];
318
319         if (c == '\n') {
320                 buf[0] = '\r';
321                 buf[1] = 0;
322                 ST->ConOut->OutputString(ST->ConOut, buf);
323         }
324         buf[0] = c;
325         buf[1] = 0;
326         ST->ConOut->OutputString(ST->ConOut, buf);
327 }