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