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