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 /* 57 * Provide Malloc / Free / Calloc backed by EFIs AllocatePool / FreePool which ensures 58 * memory is correctly aligned avoiding EFI_INVALID_PARAMETER returns from 59 * EFI methods. 60 */ 61 62 void * 63 Malloc(size_t len, const char *file __unused, int line __unused) 64 { 65 void *out; 66 67 if (BS->AllocatePool(EfiLoaderData, len, &out) == EFI_SUCCESS) 68 return (out); 69 70 return (NULL); 71 } 72 73 void 74 Free(void *buf, const char *file __unused, int line __unused) 75 { 76 if (buf != NULL) 77 (void)BS->FreePool(buf); 78 } 79 80 void * 81 Calloc(size_t n1, size_t n2, const char *file, int line) 82 { 83 size_t bytes; 84 void *res; 85 86 bytes = n1 * n2; 87 if ((res = Malloc(bytes, file, line)) != NULL) 88 bzero(res, bytes); 89 90 return (res); 91 } 92 93 /* 94 * try_boot only returns if it fails to load the loader. If it succeeds 95 * it simply boots, otherwise it returns the status of last EFI call. 96 */ 97 EFI_STATUS 98 try_boot(const boot_module_t *mod, dev_info_t *dev, void *loaderbuf, size_t loadersize) 99 { 100 size_t bufsize, cmdsize; 101 void *buf; 102 char *cmd; 103 EFI_HANDLE loaderhandle; 104 EFI_LOADED_IMAGE *loaded_image; 105 EFI_STATUS status; 106 107 /* 108 * Read in and parse the command line from /boot.config or /boot/config, 109 * if present. We'll pass it the next stage via a simple ASCII 110 * string. loader.efi has a hack for ASCII strings, so we'll use that to 111 * keep the size down here. We only try to read the alternate file if 112 * we get EFI_NOT_FOUND because all other errors mean that the boot_module 113 * had troubles with the filesystem. We could return early, but we'll let 114 * loading the actual kernel sort all that out. Since these files are 115 * optional, we don't report errors in trying to read them. 116 */ 117 cmd = NULL; 118 cmdsize = 0; 119 status = mod->load(PATH_DOTCONFIG, dev, &buf, &bufsize); 120 if (status == EFI_NOT_FOUND) 121 status = mod->load(PATH_CONFIG, dev, &buf, &bufsize); 122 if (status == EFI_SUCCESS) { 123 cmdsize = bufsize + 1; 124 cmd = malloc(cmdsize); 125 if (cmd == NULL) 126 goto errout; 127 memcpy(cmd, buf, bufsize); 128 cmd[bufsize] = '\0'; 129 free(buf); 130 buf = NULL; 131 } 132 133 if ((status = BS->LoadImage(TRUE, IH, efi_devpath_last_node(dev->devpath), 134 loaderbuf, loadersize, &loaderhandle)) != EFI_SUCCESS) { 135 printf("Failed to load image provided by %s, size: %zu, (%lu)\n", 136 mod->name, loadersize, EFI_ERROR_CODE(status)); 137 goto errout; 138 } 139 140 status = OpenProtocolByHandle(loaderhandle, &LoadedImageGUID, 141 (void **)&loaded_image); 142 if (status != EFI_SUCCESS) { 143 printf("Failed to query LoadedImage provided by %s (%lu)\n", 144 mod->name, EFI_ERROR_CODE(status)); 145 goto errout; 146 } 147 148 if (cmd != NULL) 149 printf(" command args: %s\n", cmd); 150 151 loaded_image->DeviceHandle = dev->devhandle; 152 loaded_image->LoadOptionsSize = cmdsize; 153 loaded_image->LoadOptions = cmd; 154 155 DPRINTF("Starting '%s' in 5 seconds...", PATH_LOADER_EFI); 156 DSTALL(1000000); 157 DPRINTF("."); 158 DSTALL(1000000); 159 DPRINTF("."); 160 DSTALL(1000000); 161 DPRINTF("."); 162 DSTALL(1000000); 163 DPRINTF("."); 164 DSTALL(1000000); 165 DPRINTF(".\n"); 166 167 if ((status = BS->StartImage(loaderhandle, NULL, NULL)) != 168 EFI_SUCCESS) { 169 printf("Failed to start image provided by %s (%lu)\n", 170 mod->name, EFI_ERROR_CODE(status)); 171 loaded_image->LoadOptionsSize = 0; 172 loaded_image->LoadOptions = NULL; 173 } 174 175 errout: 176 if (cmd != NULL) 177 free(cmd); 178 if (buf != NULL) 179 free(buf); 180 if (loaderbuf != NULL) 181 free(loaderbuf); 182 183 return (status); 184 } 185 186 EFI_STATUS 187 efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE *Xsystab) 188 { 189 EFI_HANDLE *handles; 190 EFI_LOADED_IMAGE *img; 191 EFI_DEVICE_PATH *imgpath; 192 EFI_STATUS status; 193 EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL; 194 SIMPLE_TEXT_OUTPUT_INTERFACE *conout = NULL; 195 UINTN i, hsize, nhandles; 196 CHAR16 *text; 197 198 /* Basic initialization*/ 199 ST = Xsystab; 200 IH = Ximage; 201 BS = ST->BootServices; 202 RS = ST->RuntimeServices; 203 204 /* Set up the console, so printf works. */ 205 status = BS->LocateProtocol(&ConsoleControlGUID, NULL, 206 (VOID **)&ConsoleControl); 207 if (status == EFI_SUCCESS) 208 (void)ConsoleControl->SetMode(ConsoleControl, 209 EfiConsoleControlScreenText); 210 /* 211 * Reset the console enable the cursor. Later we'll choose a better 212 * console size through GOP/UGA. 213 */ 214 conout = ST->ConOut; 215 conout->Reset(conout, TRUE); 216 /* Explicitly set conout to mode 0, 80x25 */ 217 conout->SetMode(conout, 0); 218 conout->EnableCursor(conout, TRUE); 219 conout->ClearScreen(conout); 220 221 printf("\n>> FreeBSD EFI boot block\n"); 222 printf(" Loader path: %s\n\n", PATH_LOADER_EFI); 223 printf(" Initializing modules:"); 224 for (i = 0; i < num_boot_modules; i++) { 225 printf(" %s", boot_modules[i]->name); 226 if (boot_modules[i]->init != NULL) 227 boot_modules[i]->init(); 228 } 229 putchar('\n'); 230 231 /* Fetch all the block I/O handles, we have to search through them later */ 232 hsize = 0; 233 BS->LocateHandle(ByProtocol, &BlockIoProtocolGUID, NULL, 234 &hsize, NULL); 235 handles = malloc(hsize); 236 if (handles == NULL) 237 efi_panic(EFI_OUT_OF_RESOURCES, "Failed to allocate %d handles\n", 238 hsize); 239 status = BS->LocateHandle(ByProtocol, &BlockIoProtocolGUID, 240 NULL, &hsize, handles); 241 if (status != EFI_SUCCESS) 242 efi_panic(status, "Failed to get device handles\n"); 243 nhandles = hsize / sizeof(*handles); 244 245 /* Determine the devpath of our image so we can prefer it. */ 246 status = OpenProtocolByHandle(IH, &LoadedImageGUID, (void **)&img); 247 imgpath = NULL; 248 if (status == EFI_SUCCESS) { 249 text = efi_devpath_name(img->FilePath); 250 if (text != NULL) { 251 printf(" Load Path: %S\n", text); 252 efi_setenv_freebsd_wcs("Boot1Path", text); 253 efi_free_devpath_name(text); 254 } 255 256 status = OpenProtocolByHandle(img->DeviceHandle, 257 &DevicePathGUID, (void **)&imgpath); 258 if (status != EFI_SUCCESS) { 259 DPRINTF("Failed to get image DevicePath (%lu)\n", 260 EFI_ERROR_CODE(status)); 261 } else { 262 text = efi_devpath_name(imgpath); 263 if (text != NULL) { 264 printf(" Load Device: %S\n", text); 265 efi_setenv_freebsd_wcs("Boot1Dev", text); 266 efi_free_devpath_name(text); 267 } 268 } 269 } 270 271 choice_protocol(handles, nhandles, imgpath); 272 273 /* If we get here, we're out of luck... */ 274 efi_panic(EFI_LOAD_ERROR, "No bootable partitions found!"); 275 } 276 277 /* 278 * add_device adds a device to the passed devinfo list. 279 */ 280 void 281 add_device(dev_info_t **devinfop, dev_info_t *devinfo) 282 { 283 dev_info_t *dev; 284 285 if (*devinfop == NULL) { 286 *devinfop = devinfo; 287 return; 288 } 289 290 for (dev = *devinfop; dev->next != NULL; dev = dev->next) 291 ; 292 293 dev->next = devinfo; 294 } 295 296 void 297 efi_exit(EFI_STATUS s) 298 { 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