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