boot1.c revision 295539
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: stable/10/sys/boot/efi/boot1/boot1.c 295539 2016-02-11 17:57:42Z smh $"); 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 33#include "boot_module.h" 34#include "paths.h" 35 36static const boot_module_t *boot_modules[] = 37{ 38#ifdef EFI_ZFS_BOOT 39 &zfs_module, 40#endif 41#ifdef EFI_UFS_BOOT 42 &ufs_module 43#endif 44}; 45 46#define NUM_BOOT_MODULES (sizeof(boot_modules) / sizeof(boot_module_t*)) 47/* The initial number of handles used to query EFI for partitions. */ 48#define NUM_HANDLES_INIT 24 49 50void putchar(int c); 51EFI_STATUS efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE* Xsystab); 52 53static void try_load(const boot_module_t* mod); 54static EFI_STATUS probe_handle(EFI_HANDLE h); 55 56EFI_SYSTEM_TABLE *systab; 57EFI_BOOT_SERVICES *bs; 58static EFI_HANDLE *image; 59 60static EFI_GUID BlockIoProtocolGUID = BLOCK_IO_PROTOCOL; 61static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL; 62static EFI_GUID LoadedImageGUID = LOADED_IMAGE_PROTOCOL; 63static EFI_GUID ConsoleControlGUID = EFI_CONSOLE_CONTROL_PROTOCOL_GUID; 64 65/* 66 * Provide Malloc / Free backed by EFIs AllocatePool / FreePool which ensures 67 * memory is correctly aligned avoiding EFI_INVALID_PARAMETER returns from 68 * EFI methods. 69 */ 70void * 71Malloc(size_t len, const char *file __unused, int line __unused) 72{ 73 void *out; 74 75 if (bs->AllocatePool(EfiLoaderData, len, &out) == EFI_SUCCESS) 76 return (out); 77 78 return (NULL); 79} 80 81void 82Free(void *buf, const char *file __unused, int line __unused) 83{ 84 (void)bs->FreePool(buf); 85} 86 87/* 88 * This function only returns if it fails to load the kernel. If it 89 * succeeds, it simply boots the kernel. 90 */ 91void 92try_load(const boot_module_t *mod) 93{ 94 size_t bufsize, cmdsize; 95 void *buf; 96 char *cmd; 97 dev_info_t *dev; 98 EFI_HANDLE loaderhandle; 99 EFI_LOADED_IMAGE *loaded_image; 100 EFI_STATUS status; 101 102 /* 103 * Read in and parse the command line from /boot.config or /boot/config, 104 * if present. We'll pass it the next stage via a simple ASCII 105 * string. loader.efi has a hack for ASCII strings, so we'll use that to 106 * keep the size down here. We only try to read the alternate file if 107 * we get EFI_NOT_FOUND because all other errors mean that the boot_module 108 * had troubles with the filesystem. We could return early, but we'll let 109 * loading the actual kernel sort all that out. Since these files are 110 * optional, we don't report errors in trying to read them. 111 */ 112 cmd = NULL; 113 cmdsize = 0; 114 status = mod->load(PATH_DOTCONFIG, &dev, &buf, &bufsize); 115 if (status == EFI_NOT_FOUND) 116 status = mod->load(PATH_CONFIG, &dev, &buf, &bufsize); 117 if (status == EFI_SUCCESS) { 118 cmdsize = bufsize + 1; 119 cmd = malloc(cmdsize); 120 if (cmd == NULL) { 121 free(buf); 122 return; 123 } 124 memcpy(cmd, buf, bufsize); 125 cmd[bufsize] = '\0'; 126 free(buf); 127 } 128 129 status = mod->load(PATH_LOADER_EFI, &dev, &buf, &bufsize); 130 if (status == EFI_NOT_FOUND) 131 return; 132 133 if (status != EFI_SUCCESS) { 134 printf("%s failed to load %s (%lu)\n", mod->name, 135 PATH_LOADER_EFI, EFI_ERROR_CODE(status)); 136 return; 137 } 138 139 if ((status = bs->LoadImage(TRUE, image, dev->devpath, buf, bufsize, 140 &loaderhandle)) != EFI_SUCCESS) { 141 printf("Failed to load image provided by %s, size: %zu, (%lu)\n", 142 mod->name, bufsize, EFI_ERROR_CODE(status)); 143 return; 144 } 145 146 if (cmd != NULL) 147 printf(" command args: %s\n", cmd); 148 149 if ((status = bs->HandleProtocol(loaderhandle, &LoadedImageGUID, 150 (VOID**)&loaded_image)) != EFI_SUCCESS) { 151 printf("Failed to query LoadedImage provided by %s (%lu)\n", 152 mod->name, EFI_ERROR_CODE(status)); 153 return; 154 } 155 156 loaded_image->DeviceHandle = dev->devhandle; 157 loaded_image->LoadOptionsSize = cmdsize; 158 loaded_image->LoadOptions = cmd; 159 160 if ((status = bs->StartImage(loaderhandle, NULL, NULL)) != 161 EFI_SUCCESS) { 162 printf("Failed to start image provided by %s (%lu)\n", 163 mod->name, EFI_ERROR_CODE(status)); 164 free(cmd); 165 loaded_image->LoadOptionsSize = 0; 166 loaded_image->LoadOptions = NULL; 167 return; 168 } 169} 170 171EFI_STATUS 172efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE *Xsystab) 173{ 174 EFI_HANDLE *handles; 175 EFI_STATUS status; 176 EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL; 177 SIMPLE_TEXT_OUTPUT_INTERFACE *conout = NULL; 178 UINTN i, max_dim, best_mode, cols, rows, hsize, nhandles; 179 180 /* Basic initialization*/ 181 systab = Xsystab; 182 image = Ximage; 183 bs = Xsystab->BootServices; 184 185 /* Set up the console, so printf works. */ 186 status = bs->LocateProtocol(&ConsoleControlGUID, NULL, 187 (VOID **)&ConsoleControl); 188 if (status == EFI_SUCCESS) 189 (void)ConsoleControl->SetMode(ConsoleControl, 190 EfiConsoleControlScreenText); 191 /* 192 * Reset the console and find the best text mode. 193 */ 194 conout = systab->ConOut; 195 conout->Reset(conout, TRUE); 196 max_dim = best_mode = 0; 197 for (i = 0; ; i++) { 198 status = conout->QueryMode(conout, i, &cols, &rows); 199 if (EFI_ERROR(status)) 200 break; 201 if (cols * rows > max_dim) { 202 max_dim = cols * rows; 203 best_mode = i; 204 } 205 } 206 if (max_dim > 0) 207 conout->SetMode(conout, best_mode); 208 conout->EnableCursor(conout, TRUE); 209 conout->ClearScreen(conout); 210 211 printf("\n>> FreeBSD EFI boot block\n"); 212 printf(" Loader path: %s\n\n", PATH_LOADER_EFI); 213 printf(" Initializing modules:"); 214 for (i = 0; i < NUM_BOOT_MODULES; i++) { 215 if (boot_modules[i] == NULL) 216 continue; 217 218 printf(" %s", boot_modules[i]->name); 219 if (boot_modules[i]->init != NULL) 220 boot_modules[i]->init(); 221 } 222 putchar('\n'); 223 224 /* Get all the device handles */ 225 hsize = (UINTN)NUM_HANDLES_INIT * sizeof(EFI_HANDLE); 226 if ((status = bs->AllocatePool(EfiLoaderData, hsize, (void **)&handles)) 227 != EFI_SUCCESS) 228 panic("Failed to allocate %d handles (%lu)", NUM_HANDLES_INIT, 229 EFI_ERROR_CODE(status)); 230 231 status = bs->LocateHandle(ByProtocol, &BlockIoProtocolGUID, NULL, 232 &hsize, handles); 233 switch (status) { 234 case EFI_SUCCESS: 235 break; 236 case EFI_BUFFER_TOO_SMALL: 237 (void)bs->FreePool(handles); 238 if ((status = bs->AllocatePool(EfiLoaderData, hsize, 239 (void **)&handles) != EFI_SUCCESS)) { 240 panic("Failed to allocate %zu handles (%lu)", hsize / 241 sizeof(*handles), EFI_ERROR_CODE(status)); 242 } 243 status = bs->LocateHandle(ByProtocol, &BlockIoProtocolGUID, 244 NULL, &hsize, handles); 245 if (status != EFI_SUCCESS) 246 panic("Failed to get device handles (%lu)\n", 247 EFI_ERROR_CODE(status)); 248 break; 249 default: 250 panic("Failed to get device handles (%lu)", 251 EFI_ERROR_CODE(status)); 252 } 253 254 /* Scan all partitions, probing with all modules. */ 255 nhandles = hsize / sizeof(*handles); 256 printf(" Probing %zu block devices...", nhandles); 257 for (i = 0; i < nhandles; i++) { 258 status = probe_handle(handles[i]); 259 switch (status) { 260 case EFI_UNSUPPORTED: 261 printf("."); 262 break; 263 case EFI_SUCCESS: 264 printf("+"); 265 break; 266 default: 267 printf("x"); 268 break; 269 } 270 } 271 printf(" done\n"); 272 273 /* Status summary. */ 274 for (i = 0; i < NUM_BOOT_MODULES; i++) { 275 if (boot_modules[i] != NULL) { 276 printf(" "); 277 boot_modules[i]->status(); 278 } 279 } 280 281 /* Select a partition to boot by trying each module in order. */ 282 for (i = 0; i < NUM_BOOT_MODULES; i++) 283 if (boot_modules[i] != NULL) 284 try_load(boot_modules[i]); 285 286 /* If we get here, we're out of luck... */ 287 panic("No bootable partitions found!"); 288} 289 290static EFI_STATUS 291probe_handle(EFI_HANDLE h) 292{ 293 dev_info_t *devinfo; 294 EFI_BLOCK_IO *blkio; 295 EFI_DEVICE_PATH *devpath; 296 EFI_STATUS status; 297 UINTN i; 298 299 /* Figure out if we're dealing with an actual partition. */ 300 status = bs->HandleProtocol(h, &DevicePathGUID, (void **)&devpath); 301 if (status == EFI_UNSUPPORTED) 302 return (status); 303 304 if (status != EFI_SUCCESS) { 305 DPRINTF("\nFailed to query DevicePath (%lu)\n", 306 EFI_ERROR_CODE(status)); 307 return (status); 308 } 309 310 while (!IsDevicePathEnd(NextDevicePathNode(devpath))) 311 devpath = NextDevicePathNode(devpath); 312 313 status = bs->HandleProtocol(h, &BlockIoProtocolGUID, (void **)&blkio); 314 if (status == EFI_UNSUPPORTED) 315 return (status); 316 317 if (status != EFI_SUCCESS) { 318 DPRINTF("\nFailed to query BlockIoProtocol (%lu)\n", 319 EFI_ERROR_CODE(status)); 320 return (status); 321 } 322 323 if (!blkio->Media->LogicalPartition) 324 return (EFI_UNSUPPORTED); 325 326 /* Run through each module, see if it can load this partition */ 327 for (i = 0; i < NUM_BOOT_MODULES; i++) { 328 if (boot_modules[i] == NULL) 329 continue; 330 331 if ((status = bs->AllocatePool(EfiLoaderData, 332 sizeof(*devinfo), (void **)&devinfo)) != 333 EFI_SUCCESS) { 334 DPRINTF("\nFailed to allocate devinfo (%lu)\n", 335 EFI_ERROR_CODE(status)); 336 continue; 337 } 338 devinfo->dev = blkio; 339 devinfo->devpath = devpath; 340 devinfo->devhandle = h; 341 devinfo->devdata = NULL; 342 devinfo->next = NULL; 343 344 status = boot_modules[i]->probe(devinfo); 345 if (status == EFI_SUCCESS) 346 return (EFI_SUCCESS); 347 (void)bs->FreePool(devinfo); 348 } 349 350 return (EFI_UNSUPPORTED); 351} 352 353void 354add_device(dev_info_t **devinfop, dev_info_t *devinfo) 355{ 356 dev_info_t *dev; 357 358 if (*devinfop == NULL) { 359 *devinfop = devinfo; 360 return; 361 } 362 363 for (dev = *devinfop; dev->next != NULL; dev = dev->next) 364 ; 365 366 dev->next = devinfo; 367} 368 369void 370panic(const char *fmt, ...) 371{ 372 va_list ap; 373 374 printf("panic: "); 375 va_start(ap, fmt); 376 vprintf(fmt, ap); 377 va_end(ap); 378 printf("\n"); 379 380 while (1) {} 381} 382 383void 384putchar(int c) 385{ 386 CHAR16 buf[2]; 387 388 if (c == '\n') { 389 buf[0] = '\r'; 390 buf[1] = 0; 391 systab->ConOut->OutputString(systab->ConOut, buf); 392 } 393 buf[0] = c; 394 buf[1] = 0; 395 systab->ConOut->OutputString(systab->ConOut, buf); 396} 397