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 } 325