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 /* 100 * See if there's any env variables the module wants to set. If so, 101 * append it to any config present. 102 */ 103 if (mod->extra_env != NULL) { 104 const char *env = mod->extra_env(); 105 if (env != NULL) { 106 size_t newlen = cmdsize + strlen(env) + 1; 107 108 cmd = realloc(cmd, newlen); 109 if (cmd == NULL) 110 goto errout; 111 if (cmdsize > 0) 112 strlcat(cmd, " ", newlen); 113 strlcat(cmd, env, newlen); 114 cmdsize = strlen(cmd); 115 free(__DECONST(char *, env)); 116 } 117 } 118 119 if ((status = BS->LoadImage(TRUE, IH, efi_devpath_last_node(dev->devpath), 120 loaderbuf, loadersize, &loaderhandle)) != EFI_SUCCESS) { 121 printf("Failed to load image provided by %s, size: %zu, (%lu)\n", 122 mod->name, loadersize, EFI_ERROR_CODE(status)); 123 goto errout; 124 } 125 126 status = OpenProtocolByHandle(loaderhandle, &LoadedImageGUID, 127 (void **)&loaded_image); 128 if (status != EFI_SUCCESS) { 129 printf("Failed to query LoadedImage provided by %s (%lu)\n", 130 mod->name, EFI_ERROR_CODE(status)); 131 goto errout; 132 } 133 134 if (cmd != NULL) 135 printf(" command args: %s\n", cmd); 136 137 loaded_image->DeviceHandle = dev->devhandle; 138 loaded_image->LoadOptionsSize = cmdsize; 139 loaded_image->LoadOptions = cmd; 140 141 DPRINTF("Starting '%s' in 5 seconds...", PATH_LOADER_EFI); 142 DSTALL(1000000); 143 DPRINTF("."); 144 DSTALL(1000000); 145 DPRINTF("."); 146 DSTALL(1000000); 147 DPRINTF("."); 148 DSTALL(1000000); 149 DPRINTF("."); 150 DSTALL(1000000); 151 DPRINTF(".\n"); 152 153 if ((status = BS->StartImage(loaderhandle, NULL, NULL)) != 154 EFI_SUCCESS) { 155 printf("Failed to start image provided by %s (%lu)\n", 156 mod->name, EFI_ERROR_CODE(status)); 157 loaded_image->LoadOptionsSize = 0; 158 loaded_image->LoadOptions = NULL; 159 } 160 161 errout: 162 if (cmd != NULL) 163 free(cmd); 164 if (buf != NULL) 165 free(buf); 166 if (loaderbuf != NULL) 167 free(loaderbuf); 168 169 return (status); 170 } 171 172 EFI_STATUS 173 efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE *Xsystab) 174 { 175 EFI_HANDLE *handles; 176 EFI_LOADED_IMAGE *img; 177 EFI_DEVICE_PATH *imgpath; 178 EFI_STATUS status; 179 EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL; 180 SIMPLE_TEXT_OUTPUT_INTERFACE *conout = NULL; 181 UINTN i, hsize, nhandles; 182 CHAR16 *text; 183 184 /* Basic initialization*/ 185 ST = Xsystab; 186 IH = Ximage; 187 BS = ST->BootServices; 188 RS = ST->RuntimeServices; 189 190 heapsize = 64 * 1024 * 1024; 191 status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, 192 EFI_SIZE_TO_PAGES(heapsize), &heap); 193 if (status != EFI_SUCCESS) { 194 ST->ConOut->OutputString(ST->ConOut, 195 __DECONST(CHAR16 *, 196 L"Failed to allocate memory for heap.\r\n")); 197 BS->Exit(IH, status, 0, NULL); 198 } 199 200 setheap((void *)(uintptr_t)heap, (void *)(uintptr_t)(heap + heapsize)); 201 202 /* Set up the console, so printf works. */ 203 status = BS->LocateProtocol(&ConsoleControlGUID, NULL, 204 (VOID **)&ConsoleControl); 205 if (status == EFI_SUCCESS) 206 (void)ConsoleControl->SetMode(ConsoleControl, 207 EfiConsoleControlScreenText); 208 /* 209 * Reset the console enable the cursor. Later we'll choose a better 210 * console size through GOP/UGA. 211 */ 212 conout = ST->ConOut; 213 conout->Reset(conout, TRUE); 214 /* Explicitly set conout to mode 0, 80x25 */ 215 conout->SetMode(conout, 0); 216 conout->EnableCursor(conout, TRUE); 217 conout->ClearScreen(conout); 218 219 printf("\n>> FreeBSD EFI boot block\n"); 220 printf(" Loader path: %s\n\n", PATH_LOADER_EFI); 221 printf(" Initializing modules:"); 222 for (i = 0; i < num_boot_modules; i++) { 223 printf(" %s", boot_modules[i]->name); 224 if (boot_modules[i]->init != NULL) 225 boot_modules[i]->init(); 226 } 227 putchar('\n'); 228 229 /* Fetch all the block I/O handles, we have to search through them later */ 230 hsize = 0; 231 BS->LocateHandle(ByProtocol, &BlockIoProtocolGUID, NULL, 232 &hsize, NULL); 233 handles = malloc(hsize); 234 if (handles == NULL) 235 efi_panic(EFI_OUT_OF_RESOURCES, "Failed to allocate %d handles\n", 236 hsize); 237 status = BS->LocateHandle(ByProtocol, &BlockIoProtocolGUID, 238 NULL, &hsize, handles); 239 if (status != EFI_SUCCESS) 240 efi_panic(status, "Failed to get device handles\n"); 241 nhandles = hsize / sizeof(*handles); 242 243 /* Determine the devpath of our image so we can prefer it. */ 244 status = OpenProtocolByHandle(IH, &LoadedImageGUID, (void **)&img); 245 imgpath = NULL; 246 if (status == EFI_SUCCESS) { 247 text = efi_devpath_name(img->FilePath); 248 if (text != NULL) { 249 printf(" Load Path: %S\n", text); 250 efi_setenv_freebsd_wcs("Boot1Path", text); 251 efi_free_devpath_name(text); 252 } 253 254 status = OpenProtocolByHandle(img->DeviceHandle, 255 &DevicePathGUID, (void **)&imgpath); 256 if (status != EFI_SUCCESS) { 257 DPRINTF("Failed to get image DevicePath (%lu)\n", 258 EFI_ERROR_CODE(status)); 259 } else { 260 text = efi_devpath_name(imgpath); 261 if (text != NULL) { 262 printf(" Load Device: %S\n", text); 263 efi_setenv_freebsd_wcs("Boot1Dev", text); 264 efi_free_devpath_name(text); 265 } 266 } 267 } 268 269 choice_protocol(handles, nhandles, imgpath); 270 271 /* If we get here, we're out of luck... */ 272 efi_panic(EFI_LOAD_ERROR, "No bootable partitions found!"); 273 } 274 275 /* 276 * add_device adds a device to the passed devinfo list. 277 */ 278 void 279 add_device(dev_info_t **devinfop, dev_info_t *devinfo) 280 { 281 dev_info_t *dev; 282 283 if (*devinfop == NULL) { 284 *devinfop = devinfo; 285 return; 286 } 287 288 for (dev = *devinfop; dev->next != NULL; dev = dev->next) 289 ; 290 291 dev->next = devinfo; 292 } 293 294 void 295 efi_exit(EFI_STATUS s) 296 { 297 298 BS->FreePages(heap, EFI_SIZE_TO_PAGES(heapsize)); 299 BS->Exit(IH, s, 0, NULL); 300 } 301 302 void 303 exit(int error __unused) 304 { 305 efi_exit(EFI_LOAD_ERROR); 306 } 307 308 /* 309 * OK. We totally give up. Exit back to EFI with a sensible status so 310 * it can try the next option on the list. 311 */ 312 static void 313 efi_panic(EFI_STATUS s, const char *fmt, ...) 314 { 315 va_list ap; 316 317 printf("panic: "); 318 va_start(ap, fmt); 319 vprintf(fmt, ap); 320 va_end(ap); 321 printf("\n"); 322 323 efi_exit(s); 324 } 325 326 int getchar(void) 327 { 328 return (-1); 329 } 330 331 void 332 putchar(int c) 333 { 334 CHAR16 buf[2]; 335 336 if (c == '\n') { 337 buf[0] = '\r'; 338 buf[1] = 0; 339 ST->ConOut->OutputString(ST->ConOut, buf); 340 } 341 buf[0] = c; 342 buf[1] = 0; 343 ST->ConOut->OutputString(ST->ConOut, buf); 344 } 345