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