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 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 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 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 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 302 exit(int error) 303 { 304 efi_exit(errno_to_efi_status(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 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 325 int getchar(void) 326 { 327 return (-1); 328 } 329 330 void 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