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