boot1.c revision 295453
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 295453 2016-02-09 22:32:24Z emaste $"); 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; 95 void *buf; 96 dev_info_t *dev; 97 EFI_HANDLE loaderhandle; 98 EFI_LOADED_IMAGE *loaded_image; 99 EFI_STATUS status; 100 101 status = mod->load(PATH_LOADER_EFI, &dev, &buf, &bufsize); 102 if (status == EFI_NOT_FOUND) 103 return; 104 105 if (status != EFI_SUCCESS) { 106 printf("%s failed to load %s (%lu)\n", mod->name, 107 PATH_LOADER_EFI, EFI_ERROR_CODE(status)); 108 return; 109 } 110 111 if ((status = bs->LoadImage(TRUE, image, dev->devpath, buf, bufsize, 112 &loaderhandle)) != EFI_SUCCESS) { 113 printf("Failed to load image provided by %s, size: %zu, (%lu)\n", 114 mod->name, bufsize, EFI_ERROR_CODE(status)); 115 return; 116 } 117 118 if ((status = bs->HandleProtocol(loaderhandle, &LoadedImageGUID, 119 (VOID**)&loaded_image)) != EFI_SUCCESS) { 120 printf("Failed to query LoadedImage provided by %s (%lu)\n", 121 mod->name, EFI_ERROR_CODE(status)); 122 return; 123 } 124 125 loaded_image->DeviceHandle = dev->devhandle; 126 127 if ((status = bs->StartImage(loaderhandle, NULL, NULL)) != 128 EFI_SUCCESS) { 129 printf("Failed to start image provided by %s (%lu)\n", 130 mod->name, EFI_ERROR_CODE(status)); 131 return; 132 } 133} 134 135EFI_STATUS 136efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE *Xsystab) 137{ 138 EFI_HANDLE *handles; 139 EFI_STATUS status; 140 EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL; 141 SIMPLE_TEXT_OUTPUT_INTERFACE *conout = NULL; 142 UINTN i, max_dim, best_mode, cols, rows, hsize, nhandles; 143 144 /* Basic initialization*/ 145 systab = Xsystab; 146 image = Ximage; 147 bs = Xsystab->BootServices; 148 149 /* Set up the console, so printf works. */ 150 status = bs->LocateProtocol(&ConsoleControlGUID, NULL, 151 (VOID **)&ConsoleControl); 152 if (status == EFI_SUCCESS) 153 (void)ConsoleControl->SetMode(ConsoleControl, 154 EfiConsoleControlScreenText); 155 /* 156 * Reset the console and find the best text mode. 157 */ 158 conout = systab->ConOut; 159 conout->Reset(conout, TRUE); 160 max_dim = best_mode = 0; 161 for (i = 0; ; i++) { 162 status = conout->QueryMode(conout, i, &cols, &rows); 163 if (EFI_ERROR(status)) 164 break; 165 if (cols * rows > max_dim) { 166 max_dim = cols * rows; 167 best_mode = i; 168 } 169 } 170 if (max_dim > 0) 171 conout->SetMode(conout, best_mode); 172 conout->EnableCursor(conout, TRUE); 173 conout->ClearScreen(conout); 174 175 printf("\n>> FreeBSD EFI boot block\n"); 176 printf(" Loader path: %s\n\n", PATH_LOADER_EFI); 177 printf(" Initializing modules:"); 178 for (i = 0; i < NUM_BOOT_MODULES; i++) { 179 if (boot_modules[i] == NULL) 180 continue; 181 182 printf(" %s", boot_modules[i]->name); 183 if (boot_modules[i]->init != NULL) 184 boot_modules[i]->init(); 185 } 186 putchar('\n'); 187 188 /* Get all the device handles */ 189 hsize = (UINTN)NUM_HANDLES_INIT * sizeof(EFI_HANDLE); 190 if ((status = bs->AllocatePool(EfiLoaderData, hsize, (void **)&handles)) 191 != EFI_SUCCESS) 192 panic("Failed to allocate %d handles (%lu)", NUM_HANDLES_INIT, 193 EFI_ERROR_CODE(status)); 194 195 status = bs->LocateHandle(ByProtocol, &BlockIoProtocolGUID, NULL, 196 &hsize, handles); 197 switch (status) { 198 case EFI_SUCCESS: 199 break; 200 case EFI_BUFFER_TOO_SMALL: 201 (void)bs->FreePool(handles); 202 if ((status = bs->AllocatePool(EfiLoaderData, hsize, 203 (void **)&handles) != EFI_SUCCESS)) { 204 panic("Failed to allocate %zu handles (%lu)", hsize / 205 sizeof(*handles), EFI_ERROR_CODE(status)); 206 } 207 status = bs->LocateHandle(ByProtocol, &BlockIoProtocolGUID, 208 NULL, &hsize, handles); 209 if (status != EFI_SUCCESS) 210 panic("Failed to get device handles (%lu)\n", 211 EFI_ERROR_CODE(status)); 212 break; 213 default: 214 panic("Failed to get device handles (%lu)", 215 EFI_ERROR_CODE(status)); 216 } 217 218 /* Scan all partitions, probing with all modules. */ 219 nhandles = hsize / sizeof(*handles); 220 printf(" Probing %zu block devices...", nhandles); 221 for (i = 0; i < nhandles; i++) { 222 status = probe_handle(handles[i]); 223 switch (status) { 224 case EFI_UNSUPPORTED: 225 printf("."); 226 break; 227 case EFI_SUCCESS: 228 printf("+"); 229 break; 230 default: 231 printf("x"); 232 break; 233 } 234 } 235 printf(" done\n"); 236 237 /* Status summary. */ 238 for (i = 0; i < NUM_BOOT_MODULES; i++) { 239 if (boot_modules[i] != NULL) { 240 printf(" "); 241 boot_modules[i]->status(); 242 } 243 } 244 245 /* Select a partition to boot by trying each module in order. */ 246 for (i = 0; i < NUM_BOOT_MODULES; i++) 247 if (boot_modules[i] != NULL) 248 try_load(boot_modules[i]); 249 250 /* If we get here, we're out of luck... */ 251 panic("No bootable partitions found!"); 252} 253 254static EFI_STATUS 255probe_handle(EFI_HANDLE h) 256{ 257 dev_info_t *devinfo; 258 EFI_BLOCK_IO *blkio; 259 EFI_DEVICE_PATH *devpath; 260 EFI_STATUS status; 261 UINTN i; 262 263 /* Figure out if we're dealing with an actual partition. */ 264 status = bs->HandleProtocol(h, &DevicePathGUID, (void **)&devpath); 265 if (status == EFI_UNSUPPORTED) 266 return (status); 267 268 if (status != EFI_SUCCESS) { 269 DPRINTF("\nFailed to query DevicePath (%lu)\n", 270 EFI_ERROR_CODE(status)); 271 return (status); 272 } 273 274 while (!IsDevicePathEnd(NextDevicePathNode(devpath))) 275 devpath = NextDevicePathNode(devpath); 276 277 status = bs->HandleProtocol(h, &BlockIoProtocolGUID, (void **)&blkio); 278 if (status == EFI_UNSUPPORTED) 279 return (status); 280 281 if (status != EFI_SUCCESS) { 282 DPRINTF("\nFailed to query BlockIoProtocol (%lu)\n", 283 EFI_ERROR_CODE(status)); 284 return (status); 285 } 286 287 if (!blkio->Media->LogicalPartition) 288 return (EFI_UNSUPPORTED); 289 290 /* Run through each module, see if it can load this partition */ 291 for (i = 0; i < NUM_BOOT_MODULES; i++) { 292 if (boot_modules[i] == NULL) 293 continue; 294 295 if ((status = bs->AllocatePool(EfiLoaderData, 296 sizeof(*devinfo), (void **)&devinfo)) != 297 EFI_SUCCESS) { 298 DPRINTF("\nFailed to allocate devinfo (%lu)\n", 299 EFI_ERROR_CODE(status)); 300 continue; 301 } 302 devinfo->dev = blkio; 303 devinfo->devpath = devpath; 304 devinfo->devhandle = h; 305 devinfo->devdata = NULL; 306 devinfo->next = NULL; 307 308 status = boot_modules[i]->probe(devinfo); 309 if (status == EFI_SUCCESS) 310 return (EFI_SUCCESS); 311 (void)bs->FreePool(devinfo); 312 } 313 314 return (EFI_UNSUPPORTED); 315} 316 317void 318add_device(dev_info_t **devinfop, dev_info_t *devinfo) 319{ 320 dev_info_t *dev; 321 322 if (*devinfop == NULL) { 323 *devinfop = devinfo; 324 return; 325 } 326 327 for (dev = *devinfop; dev->next != NULL; dev = dev->next) 328 ; 329 330 dev->next = devinfo; 331} 332 333void 334panic(const char *fmt, ...) 335{ 336 va_list ap; 337 338 printf("panic: "); 339 va_start(ap, fmt); 340 vprintf(fmt, ap); 341 va_end(ap); 342 printf("\n"); 343 344 while (1) {} 345} 346 347void 348putchar(int c) 349{ 350 CHAR16 buf[2]; 351 352 if (c == '\n') { 353 buf[0] = '\r'; 354 buf[1] = 0; 355 systab->ConOut->OutputString(systab->ConOut, buf); 356 } 357 buf[0] = c; 358 buf[1] = 0; 359 systab->ConOut->OutputString(systab->ConOut, buf); 360} 361