1ca987d46SWarner Losh /*- 2ca987d46SWarner Losh * Copyright (c) 1998 Robert Nordier 3ca987d46SWarner Losh * All rights reserved. 4ca987d46SWarner Losh * Copyright (c) 2001 Robert Drehmel 5ca987d46SWarner Losh * All rights reserved. 6ca987d46SWarner Losh * Copyright (c) 2014 Nathan Whitehorn 7ca987d46SWarner Losh * All rights reserved. 8ca987d46SWarner Losh * Copyright (c) 2015 Eric McCorkle 9ca987d46SWarner Losh * All rights reserved. 10ca987d46SWarner Losh * 11ca987d46SWarner Losh * Redistribution and use in source and binary forms are freely 12ca987d46SWarner Losh * permitted provided that the above copyright notice and this 13ca987d46SWarner Losh * paragraph and the following disclaimer are duplicated in all 14ca987d46SWarner Losh * such forms. 15ca987d46SWarner Losh * 16ca987d46SWarner Losh * This software is provided "AS IS" and without any express or 17ca987d46SWarner Losh * implied warranties, including, without limitation, the implied 18ca987d46SWarner Losh * warranties of merchantability and fitness for a particular 19ca987d46SWarner Losh * purpose. 20ca987d46SWarner Losh */ 21ca987d46SWarner Losh 22ca987d46SWarner Losh #include <sys/cdefs.h> 23ca987d46SWarner Losh __FBSDID("$FreeBSD$"); 24ca987d46SWarner Losh 25ca987d46SWarner Losh #include <sys/param.h> 26ca987d46SWarner Losh #include <machine/elf.h> 27ca987d46SWarner Losh #include <machine/stdarg.h> 28ca987d46SWarner Losh #include <stand.h> 29ca987d46SWarner Losh 30ca987d46SWarner Losh #include <efi.h> 31ca987d46SWarner Losh #include <eficonsctl.h> 32ca987d46SWarner Losh typedef CHAR16 efi_char; 33ca987d46SWarner Losh #include <efichar.h> 34ca987d46SWarner Losh 35ca987d46SWarner Losh #include "boot_module.h" 36ca987d46SWarner Losh #include "paths.h" 37ca987d46SWarner Losh 38ca987d46SWarner Losh static void efi_panic(EFI_STATUS s, const char *fmt, ...) __dead2 __printflike(2, 3); 39ca987d46SWarner Losh 40ca987d46SWarner Losh static const boot_module_t *boot_modules[] = 41ca987d46SWarner Losh { 42ca987d46SWarner Losh #ifdef EFI_ZFS_BOOT 43ca987d46SWarner Losh &zfs_module, 44ca987d46SWarner Losh #endif 45ca987d46SWarner Losh #ifdef EFI_UFS_BOOT 46ca987d46SWarner Losh &ufs_module 47ca987d46SWarner Losh #endif 48ca987d46SWarner Losh }; 49ca987d46SWarner Losh 50ca987d46SWarner Losh #define NUM_BOOT_MODULES nitems(boot_modules) 51ca987d46SWarner Losh /* The initial number of handles used to query EFI for partitions. */ 52ca987d46SWarner Losh #define NUM_HANDLES_INIT 24 53ca987d46SWarner Losh 54ca987d46SWarner Losh static EFI_GUID BlockIoProtocolGUID = BLOCK_IO_PROTOCOL; 55ca987d46SWarner Losh static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL; 56ca987d46SWarner Losh static EFI_GUID LoadedImageGUID = LOADED_IMAGE_PROTOCOL; 57ca987d46SWarner Losh static EFI_GUID ConsoleControlGUID = EFI_CONSOLE_CONTROL_PROTOCOL_GUID; 58ca987d46SWarner Losh static EFI_GUID FreeBSDBootVarGUID = FREEBSD_BOOT_VAR_GUID; 59ca987d46SWarner Losh 60ca987d46SWarner Losh /* 61ca987d46SWarner Losh * Provide Malloc / Free backed by EFIs AllocatePool / FreePool which ensures 62ca987d46SWarner Losh * memory is correctly aligned avoiding EFI_INVALID_PARAMETER returns from 63ca987d46SWarner Losh * EFI methods. 64ca987d46SWarner Losh */ 65ca987d46SWarner Losh void * 66ca987d46SWarner Losh Malloc(size_t len, const char *file __unused, int line __unused) 67ca987d46SWarner Losh { 68ca987d46SWarner Losh void *out; 69ca987d46SWarner Losh 70ca987d46SWarner Losh if (BS->AllocatePool(EfiLoaderData, len, &out) == EFI_SUCCESS) 71ca987d46SWarner Losh return (out); 72ca987d46SWarner Losh 73ca987d46SWarner Losh return (NULL); 74ca987d46SWarner Losh } 75ca987d46SWarner Losh 76ca987d46SWarner Losh void 77ca987d46SWarner Losh Free(void *buf, const char *file __unused, int line __unused) 78ca987d46SWarner Losh { 79ca987d46SWarner Losh if (buf != NULL) 80ca987d46SWarner Losh (void)BS->FreePool(buf); 81ca987d46SWarner Losh } 82ca987d46SWarner Losh 83ca987d46SWarner Losh static EFI_STATUS 84ca987d46SWarner Losh efi_setenv_freebsd_wcs(const char *varname, CHAR16 *valstr) 85ca987d46SWarner Losh { 86ca987d46SWarner Losh CHAR16 *var = NULL; 87ca987d46SWarner Losh size_t len; 88ca987d46SWarner Losh EFI_STATUS rv; 89ca987d46SWarner Losh 90ca987d46SWarner Losh utf8_to_ucs2(varname, &var, &len); 91ca987d46SWarner Losh if (var == NULL) 92ca987d46SWarner Losh return (EFI_OUT_OF_RESOURCES); 93ca987d46SWarner Losh rv = RS->SetVariable(var, &FreeBSDBootVarGUID, 94ca987d46SWarner Losh EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, 95ca987d46SWarner Losh (ucs2len(valstr) + 1) * sizeof(efi_char), valstr); 96ca987d46SWarner Losh free(var); 97ca987d46SWarner Losh return (rv); 98ca987d46SWarner Losh } 99ca987d46SWarner Losh 100ca987d46SWarner Losh /* 101ca987d46SWarner Losh * nodes_match returns TRUE if the imgpath isn't NULL and the nodes match, 102ca987d46SWarner Losh * FALSE otherwise. 103ca987d46SWarner Losh */ 104ca987d46SWarner Losh static BOOLEAN 105ca987d46SWarner Losh nodes_match(EFI_DEVICE_PATH *imgpath, EFI_DEVICE_PATH *devpath) 106ca987d46SWarner Losh { 107ca987d46SWarner Losh size_t len; 108ca987d46SWarner Losh 109ca987d46SWarner Losh if (imgpath == NULL || imgpath->Type != devpath->Type || 110ca987d46SWarner Losh imgpath->SubType != devpath->SubType) 111ca987d46SWarner Losh return (FALSE); 112ca987d46SWarner Losh 113ca987d46SWarner Losh len = DevicePathNodeLength(imgpath); 114ca987d46SWarner Losh if (len != DevicePathNodeLength(devpath)) 115ca987d46SWarner Losh return (FALSE); 116ca987d46SWarner Losh 117ca987d46SWarner Losh return (memcmp(imgpath, devpath, (size_t)len) == 0); 118ca987d46SWarner Losh } 119ca987d46SWarner Losh 120ca987d46SWarner Losh /* 121ca987d46SWarner Losh * device_paths_match returns TRUE if the imgpath isn't NULL and all nodes 122ca987d46SWarner Losh * in imgpath and devpath match up to their respective occurrences of a 123ca987d46SWarner Losh * media node, FALSE otherwise. 124ca987d46SWarner Losh */ 125ca987d46SWarner Losh static BOOLEAN 126ca987d46SWarner Losh device_paths_match(EFI_DEVICE_PATH *imgpath, EFI_DEVICE_PATH *devpath) 127ca987d46SWarner Losh { 128ca987d46SWarner Losh 129ca987d46SWarner Losh if (imgpath == NULL) 130ca987d46SWarner Losh return (FALSE); 131ca987d46SWarner Losh 132ca987d46SWarner Losh while (!IsDevicePathEnd(imgpath) && !IsDevicePathEnd(devpath)) { 133ca987d46SWarner Losh if (IsDevicePathType(imgpath, MEDIA_DEVICE_PATH) && 134ca987d46SWarner Losh IsDevicePathType(devpath, MEDIA_DEVICE_PATH)) 135ca987d46SWarner Losh return (TRUE); 136ca987d46SWarner Losh 137ca987d46SWarner Losh if (!nodes_match(imgpath, devpath)) 138ca987d46SWarner Losh return (FALSE); 139ca987d46SWarner Losh 140ca987d46SWarner Losh imgpath = NextDevicePathNode(imgpath); 141ca987d46SWarner Losh devpath = NextDevicePathNode(devpath); 142ca987d46SWarner Losh } 143ca987d46SWarner Losh 144ca987d46SWarner Losh return (FALSE); 145ca987d46SWarner Losh } 146ca987d46SWarner Losh 147ca987d46SWarner Losh /* 148ca987d46SWarner Losh * devpath_last returns the last non-path end node in devpath. 149ca987d46SWarner Losh */ 150ca987d46SWarner Losh static EFI_DEVICE_PATH * 151ca987d46SWarner Losh devpath_last(EFI_DEVICE_PATH *devpath) 152ca987d46SWarner Losh { 153ca987d46SWarner Losh 154ca987d46SWarner Losh while (!IsDevicePathEnd(NextDevicePathNode(devpath))) 155ca987d46SWarner Losh devpath = NextDevicePathNode(devpath); 156ca987d46SWarner Losh 157ca987d46SWarner Losh return (devpath); 158ca987d46SWarner Losh } 159ca987d46SWarner Losh 160ca987d46SWarner Losh /* 161ca987d46SWarner Losh * load_loader attempts to load the loader image data. 162ca987d46SWarner Losh * 163ca987d46SWarner Losh * It tries each module and its respective devices, identified by mod->probe, 164ca987d46SWarner Losh * in order until a successful load occurs at which point it returns EFI_SUCCESS 165ca987d46SWarner Losh * and EFI_NOT_FOUND otherwise. 166ca987d46SWarner Losh * 167ca987d46SWarner Losh * Only devices which have preferred matching the preferred parameter are tried. 168ca987d46SWarner Losh */ 169ca987d46SWarner Losh static EFI_STATUS 170ca987d46SWarner Losh load_loader(const boot_module_t **modp, dev_info_t **devinfop, void **bufp, 171ca987d46SWarner Losh size_t *bufsize, BOOLEAN preferred) 172ca987d46SWarner Losh { 173ca987d46SWarner Losh UINTN i; 174ca987d46SWarner Losh dev_info_t *dev; 175ca987d46SWarner Losh const boot_module_t *mod; 176ca987d46SWarner Losh 177ca987d46SWarner Losh for (i = 0; i < NUM_BOOT_MODULES; i++) { 178ca987d46SWarner Losh mod = boot_modules[i]; 179ca987d46SWarner Losh for (dev = mod->devices(); dev != NULL; dev = dev->next) { 180ca987d46SWarner Losh if (dev->preferred != preferred) 181ca987d46SWarner Losh continue; 182ca987d46SWarner Losh 183ca987d46SWarner Losh if (mod->load(PATH_LOADER_EFI, dev, bufp, bufsize) == 184ca987d46SWarner Losh EFI_SUCCESS) { 185ca987d46SWarner Losh *devinfop = dev; 186ca987d46SWarner Losh *modp = mod; 187ca987d46SWarner Losh return (EFI_SUCCESS); 188ca987d46SWarner Losh } 189ca987d46SWarner Losh } 190ca987d46SWarner Losh } 191ca987d46SWarner Losh 192ca987d46SWarner Losh return (EFI_NOT_FOUND); 193ca987d46SWarner Losh } 194ca987d46SWarner Losh 195ca987d46SWarner Losh /* 196ca987d46SWarner Losh * try_boot only returns if it fails to load the loader. If it succeeds 197ca987d46SWarner Losh * it simply boots, otherwise it returns the status of last EFI call. 198ca987d46SWarner Losh */ 199ca987d46SWarner Losh static EFI_STATUS 200ca987d46SWarner Losh try_boot(void) 201ca987d46SWarner Losh { 202ca987d46SWarner Losh size_t bufsize, loadersize, cmdsize; 203ca987d46SWarner Losh void *buf, *loaderbuf; 204ca987d46SWarner Losh char *cmd; 205ca987d46SWarner Losh dev_info_t *dev; 206ca987d46SWarner Losh const boot_module_t *mod; 207ca987d46SWarner Losh EFI_HANDLE loaderhandle; 208ca987d46SWarner Losh EFI_LOADED_IMAGE *loaded_image; 209ca987d46SWarner Losh EFI_STATUS status; 210ca987d46SWarner Losh 211ca987d46SWarner Losh status = load_loader(&mod, &dev, &loaderbuf, &loadersize, TRUE); 212ca987d46SWarner Losh if (status != EFI_SUCCESS) { 213ca987d46SWarner Losh status = load_loader(&mod, &dev, &loaderbuf, &loadersize, 214ca987d46SWarner Losh FALSE); 215ca987d46SWarner Losh if (status != EFI_SUCCESS) { 216ca987d46SWarner Losh printf("Failed to load '%s'\n", PATH_LOADER_EFI); 217ca987d46SWarner Losh return (status); 218ca987d46SWarner Losh } 219ca987d46SWarner Losh } 220ca987d46SWarner Losh 221ca987d46SWarner Losh /* 222ca987d46SWarner Losh * Read in and parse the command line from /boot.config or /boot/config, 223ca987d46SWarner Losh * if present. We'll pass it the next stage via a simple ASCII 224ca987d46SWarner Losh * string. loader.efi has a hack for ASCII strings, so we'll use that to 225ca987d46SWarner Losh * keep the size down here. We only try to read the alternate file if 226ca987d46SWarner Losh * we get EFI_NOT_FOUND because all other errors mean that the boot_module 227ca987d46SWarner Losh * had troubles with the filesystem. We could return early, but we'll let 228ca987d46SWarner Losh * loading the actual kernel sort all that out. Since these files are 229ca987d46SWarner Losh * optional, we don't report errors in trying to read them. 230ca987d46SWarner Losh */ 231ca987d46SWarner Losh cmd = NULL; 232ca987d46SWarner Losh cmdsize = 0; 233ca987d46SWarner Losh status = mod->load(PATH_DOTCONFIG, dev, &buf, &bufsize); 234ca987d46SWarner Losh if (status == EFI_NOT_FOUND) 235ca987d46SWarner Losh status = mod->load(PATH_CONFIG, dev, &buf, &bufsize); 236ca987d46SWarner Losh if (status == EFI_SUCCESS) { 237ca987d46SWarner Losh cmdsize = bufsize + 1; 238ca987d46SWarner Losh cmd = malloc(cmdsize); 239ca987d46SWarner Losh if (cmd == NULL) 240ca987d46SWarner Losh goto errout; 241ca987d46SWarner Losh memcpy(cmd, buf, bufsize); 242ca987d46SWarner Losh cmd[bufsize] = '\0'; 243ca987d46SWarner Losh free(buf); 244ca987d46SWarner Losh buf = NULL; 245ca987d46SWarner Losh } 246ca987d46SWarner Losh 247ca987d46SWarner Losh if ((status = BS->LoadImage(TRUE, IH, devpath_last(dev->devpath), 248ca987d46SWarner Losh loaderbuf, loadersize, &loaderhandle)) != EFI_SUCCESS) { 249ca987d46SWarner Losh printf("Failed to load image provided by %s, size: %zu, (%lu)\n", 250ca987d46SWarner Losh mod->name, loadersize, EFI_ERROR_CODE(status)); 251ca987d46SWarner Losh goto errout; 252ca987d46SWarner Losh } 253ca987d46SWarner Losh 254ca987d46SWarner Losh if ((status = BS->HandleProtocol(loaderhandle, &LoadedImageGUID, 255ca987d46SWarner Losh (VOID**)&loaded_image)) != EFI_SUCCESS) { 256ca987d46SWarner Losh printf("Failed to query LoadedImage provided by %s (%lu)\n", 257ca987d46SWarner Losh mod->name, EFI_ERROR_CODE(status)); 258ca987d46SWarner Losh goto errout; 259ca987d46SWarner Losh } 260ca987d46SWarner Losh 261ca987d46SWarner Losh if (cmd != NULL) 262ca987d46SWarner Losh printf(" command args: %s\n", cmd); 263ca987d46SWarner Losh 264ca987d46SWarner Losh loaded_image->DeviceHandle = dev->devhandle; 265ca987d46SWarner Losh loaded_image->LoadOptionsSize = cmdsize; 266ca987d46SWarner Losh loaded_image->LoadOptions = cmd; 267ca987d46SWarner Losh 268ca987d46SWarner Losh DPRINTF("Starting '%s' in 5 seconds...", PATH_LOADER_EFI); 269ca987d46SWarner Losh DSTALL(1000000); 270ca987d46SWarner Losh DPRINTF("."); 271ca987d46SWarner Losh DSTALL(1000000); 272ca987d46SWarner Losh DPRINTF("."); 273ca987d46SWarner Losh DSTALL(1000000); 274ca987d46SWarner Losh DPRINTF("."); 275ca987d46SWarner Losh DSTALL(1000000); 276ca987d46SWarner Losh DPRINTF("."); 277ca987d46SWarner Losh DSTALL(1000000); 278ca987d46SWarner Losh DPRINTF(".\n"); 279ca987d46SWarner Losh 280ca987d46SWarner Losh if ((status = BS->StartImage(loaderhandle, NULL, NULL)) != 281ca987d46SWarner Losh EFI_SUCCESS) { 282ca987d46SWarner Losh printf("Failed to start image provided by %s (%lu)\n", 283ca987d46SWarner Losh mod->name, EFI_ERROR_CODE(status)); 284ca987d46SWarner Losh loaded_image->LoadOptionsSize = 0; 285ca987d46SWarner Losh loaded_image->LoadOptions = NULL; 286ca987d46SWarner Losh } 287ca987d46SWarner Losh 288ca987d46SWarner Losh errout: 289ca987d46SWarner Losh if (cmd != NULL) 290ca987d46SWarner Losh free(cmd); 291ca987d46SWarner Losh if (buf != NULL) 292ca987d46SWarner Losh free(buf); 293ca987d46SWarner Losh if (loaderbuf != NULL) 294ca987d46SWarner Losh free(loaderbuf); 295ca987d46SWarner Losh 296ca987d46SWarner Losh return (status); 297ca987d46SWarner Losh } 298ca987d46SWarner Losh 299ca987d46SWarner Losh /* 300ca987d46SWarner Losh * probe_handle determines if the passed handle represents a logical partition 301ca987d46SWarner Losh * if it does it uses each module in order to probe it and if successful it 302ca987d46SWarner Losh * returns EFI_SUCCESS. 303ca987d46SWarner Losh */ 304ca987d46SWarner Losh static EFI_STATUS 305ca987d46SWarner Losh probe_handle(EFI_HANDLE h, EFI_DEVICE_PATH *imgpath, BOOLEAN *preferred) 306ca987d46SWarner Losh { 307ca987d46SWarner Losh dev_info_t *devinfo; 308ca987d46SWarner Losh EFI_BLOCK_IO *blkio; 309ca987d46SWarner Losh EFI_DEVICE_PATH *devpath; 310ca987d46SWarner Losh EFI_STATUS status; 311ca987d46SWarner Losh UINTN i; 312ca987d46SWarner Losh 313ca987d46SWarner Losh /* Figure out if we're dealing with an actual partition. */ 314ca987d46SWarner Losh status = BS->HandleProtocol(h, &DevicePathGUID, (void **)&devpath); 315ca987d46SWarner Losh if (status == EFI_UNSUPPORTED) 316ca987d46SWarner Losh return (status); 317ca987d46SWarner Losh 318ca987d46SWarner Losh if (status != EFI_SUCCESS) { 319ca987d46SWarner Losh DPRINTF("\nFailed to query DevicePath (%lu)\n", 320ca987d46SWarner Losh EFI_ERROR_CODE(status)); 321ca987d46SWarner Losh return (status); 322ca987d46SWarner Losh } 323ca987d46SWarner Losh #ifdef EFI_DEBUG 324ca987d46SWarner Losh { 325ca987d46SWarner Losh CHAR16 *text = efi_devpath_name(devpath); 326ca987d46SWarner Losh DPRINTF("probing: %S\n", text); 327ca987d46SWarner Losh efi_free_devpath_name(text); 328ca987d46SWarner Losh } 329ca987d46SWarner Losh #endif 330ca987d46SWarner Losh status = BS->HandleProtocol(h, &BlockIoProtocolGUID, (void **)&blkio); 331ca987d46SWarner Losh if (status == EFI_UNSUPPORTED) 332ca987d46SWarner Losh return (status); 333ca987d46SWarner Losh 334ca987d46SWarner Losh if (status != EFI_SUCCESS) { 335ca987d46SWarner Losh DPRINTF("\nFailed to query BlockIoProtocol (%lu)\n", 336ca987d46SWarner Losh EFI_ERROR_CODE(status)); 337ca987d46SWarner Losh return (status); 338ca987d46SWarner Losh } 339ca987d46SWarner Losh 340ca987d46SWarner Losh if (!blkio->Media->LogicalPartition) 341ca987d46SWarner Losh return (EFI_UNSUPPORTED); 342ca987d46SWarner Losh 343ca987d46SWarner Losh *preferred = device_paths_match(imgpath, devpath); 344ca987d46SWarner Losh 345ca987d46SWarner Losh /* Run through each module, see if it can load this partition */ 346ca987d46SWarner Losh for (i = 0; i < NUM_BOOT_MODULES; i++) { 347ca987d46SWarner Losh devinfo = malloc(sizeof(*devinfo)); 348ca987d46SWarner Losh if (devinfo == NULL) { 349ca987d46SWarner Losh DPRINTF("\nFailed to allocate devinfo\n"); 350ca987d46SWarner Losh continue; 351ca987d46SWarner Losh } 352ca987d46SWarner Losh devinfo->dev = blkio; 353ca987d46SWarner Losh devinfo->devpath = devpath; 354ca987d46SWarner Losh devinfo->devhandle = h; 355ca987d46SWarner Losh devinfo->devdata = NULL; 356ca987d46SWarner Losh devinfo->preferred = *preferred; 357ca987d46SWarner Losh devinfo->next = NULL; 358ca987d46SWarner Losh 359ca987d46SWarner Losh status = boot_modules[i]->probe(devinfo); 360ca987d46SWarner Losh if (status == EFI_SUCCESS) 361ca987d46SWarner Losh return (EFI_SUCCESS); 362ca987d46SWarner Losh free(devinfo); 363ca987d46SWarner Losh } 364ca987d46SWarner Losh 365ca987d46SWarner Losh return (EFI_UNSUPPORTED); 366ca987d46SWarner Losh } 367ca987d46SWarner Losh 368ca987d46SWarner Losh /* 369ca987d46SWarner Losh * probe_handle_status calls probe_handle and outputs the returned status 370ca987d46SWarner Losh * of the call. 371ca987d46SWarner Losh */ 372ca987d46SWarner Losh static void 373ca987d46SWarner Losh probe_handle_status(EFI_HANDLE h, EFI_DEVICE_PATH *imgpath) 374ca987d46SWarner Losh { 375ca987d46SWarner Losh EFI_STATUS status; 376ca987d46SWarner Losh BOOLEAN preferred; 377ca987d46SWarner Losh 378ca987d46SWarner Losh preferred = FALSE; 379ca987d46SWarner Losh status = probe_handle(h, imgpath, &preferred); 380ca987d46SWarner Losh 381ca987d46SWarner Losh DPRINTF("probe: "); 382ca987d46SWarner Losh switch (status) { 383ca987d46SWarner Losh case EFI_UNSUPPORTED: 384ca987d46SWarner Losh printf("."); 385ca987d46SWarner Losh DPRINTF(" not supported\n"); 386ca987d46SWarner Losh break; 387ca987d46SWarner Losh case EFI_SUCCESS: 388ca987d46SWarner Losh if (preferred) { 389ca987d46SWarner Losh printf("%c", '*'); 390ca987d46SWarner Losh DPRINTF(" supported (preferred)\n"); 391ca987d46SWarner Losh } else { 392ca987d46SWarner Losh printf("%c", '+'); 393ca987d46SWarner Losh DPRINTF(" supported\n"); 394ca987d46SWarner Losh } 395ca987d46SWarner Losh break; 396ca987d46SWarner Losh default: 397ca987d46SWarner Losh printf("x"); 398ca987d46SWarner Losh DPRINTF(" error (%lu)\n", EFI_ERROR_CODE(status)); 399ca987d46SWarner Losh break; 400ca987d46SWarner Losh } 401ca987d46SWarner Losh DSTALL(500000); 402ca987d46SWarner Losh } 403ca987d46SWarner Losh 404ca987d46SWarner Losh EFI_STATUS 405ca987d46SWarner Losh efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE *Xsystab) 406ca987d46SWarner Losh { 407ca987d46SWarner Losh EFI_HANDLE *handles; 408ca987d46SWarner Losh EFI_LOADED_IMAGE *img; 409ca987d46SWarner Losh EFI_DEVICE_PATH *imgpath; 410ca987d46SWarner Losh EFI_STATUS status; 411ca987d46SWarner Losh EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL; 412ca987d46SWarner Losh SIMPLE_TEXT_OUTPUT_INTERFACE *conout = NULL; 413ca987d46SWarner Losh UINTN i, max_dim, best_mode, cols, rows, hsize, nhandles; 414ca987d46SWarner Losh CHAR16 *text; 415ca987d46SWarner Losh 416ca987d46SWarner Losh /* Basic initialization*/ 417ca987d46SWarner Losh ST = Xsystab; 418ca987d46SWarner Losh IH = Ximage; 419ca987d46SWarner Losh BS = ST->BootServices; 420ca987d46SWarner Losh RS = ST->RuntimeServices; 421ca987d46SWarner Losh 422ca987d46SWarner Losh /* Set up the console, so printf works. */ 423ca987d46SWarner Losh status = BS->LocateProtocol(&ConsoleControlGUID, NULL, 424ca987d46SWarner Losh (VOID **)&ConsoleControl); 425ca987d46SWarner Losh if (status == EFI_SUCCESS) 426ca987d46SWarner Losh (void)ConsoleControl->SetMode(ConsoleControl, 427ca987d46SWarner Losh EfiConsoleControlScreenText); 428ca987d46SWarner Losh /* 429ca987d46SWarner Losh * Reset the console and find the best text mode. 430ca987d46SWarner Losh */ 431ca987d46SWarner Losh conout = ST->ConOut; 432ca987d46SWarner Losh conout->Reset(conout, TRUE); 433ca987d46SWarner Losh max_dim = best_mode = 0; 434ca987d46SWarner Losh for (i = 0; ; i++) { 435ca987d46SWarner Losh status = conout->QueryMode(conout, i, &cols, &rows); 436ca987d46SWarner Losh if (EFI_ERROR(status)) 437ca987d46SWarner Losh break; 438ca987d46SWarner Losh if (cols * rows > max_dim) { 439ca987d46SWarner Losh max_dim = cols * rows; 440ca987d46SWarner Losh best_mode = i; 441ca987d46SWarner Losh } 442ca987d46SWarner Losh } 443ca987d46SWarner Losh if (max_dim > 0) 444ca987d46SWarner Losh conout->SetMode(conout, best_mode); 445ca987d46SWarner Losh conout->EnableCursor(conout, TRUE); 446ca987d46SWarner Losh conout->ClearScreen(conout); 447ca987d46SWarner Losh 448ca987d46SWarner Losh printf("\n>> FreeBSD EFI boot block\n"); 449ca987d46SWarner Losh printf(" Loader path: %s\n\n", PATH_LOADER_EFI); 450ca987d46SWarner Losh printf(" Initializing modules:"); 451ca987d46SWarner Losh for (i = 0; i < NUM_BOOT_MODULES; i++) { 452ca987d46SWarner Losh printf(" %s", boot_modules[i]->name); 453ca987d46SWarner Losh if (boot_modules[i]->init != NULL) 454ca987d46SWarner Losh boot_modules[i]->init(); 455ca987d46SWarner Losh } 456ca987d46SWarner Losh putchar('\n'); 457ca987d46SWarner Losh 458ca987d46SWarner Losh /* Determine the devpath of our image so we can prefer it. */ 459ca987d46SWarner Losh status = BS->HandleProtocol(IH, &LoadedImageGUID, (VOID**)&img); 460ca987d46SWarner Losh imgpath = NULL; 461ca987d46SWarner Losh if (status == EFI_SUCCESS) { 462ca987d46SWarner Losh text = efi_devpath_name(img->FilePath); 463ca987d46SWarner Losh if (text != NULL) { 464ca987d46SWarner Losh printf(" Load Path: %S\n", text); 465ca987d46SWarner Losh efi_setenv_freebsd_wcs("Boot1Path", text); 466ca987d46SWarner Losh efi_free_devpath_name(text); 467ca987d46SWarner Losh } 468ca987d46SWarner Losh 469ca987d46SWarner Losh status = BS->HandleProtocol(img->DeviceHandle, &DevicePathGUID, 470ca987d46SWarner Losh (void **)&imgpath); 471ca987d46SWarner Losh if (status != EFI_SUCCESS) { 472ca987d46SWarner Losh DPRINTF("Failed to get image DevicePath (%lu)\n", 473ca987d46SWarner Losh EFI_ERROR_CODE(status)); 474ca987d46SWarner Losh } else { 475ca987d46SWarner Losh text = efi_devpath_name(imgpath); 476ca987d46SWarner Losh if (text != NULL) { 477ca987d46SWarner Losh printf(" Load Device: %S\n", text); 478ca987d46SWarner Losh efi_setenv_freebsd_wcs("Boot1Dev", text); 479ca987d46SWarner Losh efi_free_devpath_name(text); 480ca987d46SWarner Losh } 481ca987d46SWarner Losh } 482ca987d46SWarner Losh } 483ca987d46SWarner Losh 484ca987d46SWarner Losh /* Get all the device handles */ 485ca987d46SWarner Losh hsize = (UINTN)NUM_HANDLES_INIT * sizeof(EFI_HANDLE); 486ca987d46SWarner Losh handles = malloc(hsize); 487ca987d46SWarner Losh if (handles == NULL) { 488ca987d46SWarner Losh printf("Failed to allocate %d handles\n", NUM_HANDLES_INIT); 489ca987d46SWarner Losh } 490ca987d46SWarner Losh 491ca987d46SWarner Losh status = BS->LocateHandle(ByProtocol, &BlockIoProtocolGUID, NULL, 492ca987d46SWarner Losh &hsize, handles); 493ca987d46SWarner Losh switch (status) { 494ca987d46SWarner Losh case EFI_SUCCESS: 495ca987d46SWarner Losh break; 496ca987d46SWarner Losh case EFI_BUFFER_TOO_SMALL: 497ca987d46SWarner Losh free(handles); 498ca987d46SWarner Losh handles = malloc(hsize); 499ca987d46SWarner Losh if (handles == NULL) 500ca987d46SWarner Losh efi_panic(EFI_OUT_OF_RESOURCES, "Failed to allocate %d handles\n", 501ca987d46SWarner Losh NUM_HANDLES_INIT); 502ca987d46SWarner Losh status = BS->LocateHandle(ByProtocol, &BlockIoProtocolGUID, 503ca987d46SWarner Losh NULL, &hsize, handles); 504ca987d46SWarner Losh if (status != EFI_SUCCESS) 505ca987d46SWarner Losh efi_panic(status, "Failed to get device handles\n"); 506ca987d46SWarner Losh break; 507ca987d46SWarner Losh default: 508ca987d46SWarner Losh efi_panic(status, "Failed to get device handles\n"); 509ca987d46SWarner Losh break; 510ca987d46SWarner Losh } 511ca987d46SWarner Losh 512ca987d46SWarner Losh /* Scan all partitions, probing with all modules. */ 513ca987d46SWarner Losh nhandles = hsize / sizeof(*handles); 514ca987d46SWarner Losh printf(" Probing %zu block devices...", nhandles); 515ca987d46SWarner Losh DPRINTF("\n"); 516ca987d46SWarner Losh 517ca987d46SWarner Losh for (i = 0; i < nhandles; i++) 518ca987d46SWarner Losh probe_handle_status(handles[i], imgpath); 519ca987d46SWarner Losh printf(" done\n"); 520ca987d46SWarner Losh 521ca987d46SWarner Losh /* Status summary. */ 522ca987d46SWarner Losh for (i = 0; i < NUM_BOOT_MODULES; i++) { 523ca987d46SWarner Losh printf(" "); 524ca987d46SWarner Losh boot_modules[i]->status(); 525ca987d46SWarner Losh } 526ca987d46SWarner Losh 527ca987d46SWarner Losh try_boot(); 528ca987d46SWarner Losh 529ca987d46SWarner Losh /* If we get here, we're out of luck... */ 530ca987d46SWarner Losh efi_panic(EFI_LOAD_ERROR, "No bootable partitions found!"); 531ca987d46SWarner Losh } 532ca987d46SWarner Losh 533ca987d46SWarner Losh /* 534ca987d46SWarner Losh * add_device adds a device to the passed devinfo list. 535ca987d46SWarner Losh */ 536ca987d46SWarner Losh void 537ca987d46SWarner Losh add_device(dev_info_t **devinfop, dev_info_t *devinfo) 538ca987d46SWarner Losh { 539ca987d46SWarner Losh dev_info_t *dev; 540ca987d46SWarner Losh 541ca987d46SWarner Losh if (*devinfop == NULL) { 542ca987d46SWarner Losh *devinfop = devinfo; 543ca987d46SWarner Losh return; 544ca987d46SWarner Losh } 545ca987d46SWarner Losh 546ca987d46SWarner Losh for (dev = *devinfop; dev->next != NULL; dev = dev->next) 547ca987d46SWarner Losh ; 548ca987d46SWarner Losh 549ca987d46SWarner Losh dev->next = devinfo; 550ca987d46SWarner Losh } 551ca987d46SWarner Losh 552ca987d46SWarner Losh /* 553ca987d46SWarner Losh * OK. We totally give up. Exit back to EFI with a sensible status so 554ca987d46SWarner Losh * it can try the next option on the list. 555ca987d46SWarner Losh */ 556ca987d46SWarner Losh static void 557ca987d46SWarner Losh efi_panic(EFI_STATUS s, const char *fmt, ...) 558ca987d46SWarner Losh { 559ca987d46SWarner Losh va_list ap; 560ca987d46SWarner Losh 561ca987d46SWarner Losh printf("panic: "); 562ca987d46SWarner Losh va_start(ap, fmt); 563ca987d46SWarner Losh vprintf(fmt, ap); 564ca987d46SWarner Losh va_end(ap); 565ca987d46SWarner Losh printf("\n"); 566ca987d46SWarner Losh 567ca987d46SWarner Losh BS->Exit(IH, s, 0, NULL); 568ca987d46SWarner Losh } 569ca987d46SWarner Losh 570ca987d46SWarner Losh void 571ca987d46SWarner Losh putchar(int c) 572ca987d46SWarner Losh { 573ca987d46SWarner Losh CHAR16 buf[2]; 574ca987d46SWarner Losh 575ca987d46SWarner Losh if (c == '\n') { 576ca987d46SWarner Losh buf[0] = '\r'; 577ca987d46SWarner Losh buf[1] = 0; 578ca987d46SWarner Losh ST->ConOut->OutputString(ST->ConOut, buf); 579ca987d46SWarner Losh } 580ca987d46SWarner Losh buf[0] = c; 581ca987d46SWarner Losh buf[1] = 0; 582ca987d46SWarner Losh ST->ConOut->OutputString(ST->ConOut, buf); 583ca987d46SWarner Losh } 584