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