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
try_boot(const boot_module_t * mod,dev_info_t * dev,void * loaderbuf,size_t loadersize)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
efi_main(EFI_HANDLE Ximage,EFI_SYSTEM_TABLE * Xsystab)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
add_device(dev_info_t ** devinfop,dev_info_t * devinfo)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
efi_exit(EFI_STATUS s)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
exit(int error __unused)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
efi_panic(EFI_STATUS s,const char * fmt,...)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
getchar(void)306 int getchar(void)
307 {
308 return (-1);
309 }
310
311 void
putchar(int c)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 }
325