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/param.h> 23 #include <machine/elf.h> 24 #include <machine/stdarg.h> 25 #include <stand.h> 26 27 #include <efi.h> 28 #include <eficonsctl.h> 29 #include <efichar.h> 30 31 #include "boot_module.h" 32 #include "paths.h" 33 #include "proto.h" 34 35 static EFI_GUID BlockIoProtocolGUID = BLOCK_IO_PROTOCOL; 36 static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL; 37 38 #ifndef EFI_DEBUG 39 static const char *prio_str[] = { 40 "error", 41 "not supported", 42 "good", 43 "better" 44 }; 45 #endif 46 47 /* 48 * probe_handle determines if the passed handle represents a logical partition 49 * if it does it uses each module in order to probe it and if successful it 50 * returns EFI_SUCCESS. 51 */ 52 static int 53 probe_handle(EFI_HANDLE h, EFI_DEVICE_PATH *imgpath) 54 { 55 dev_info_t *devinfo; 56 EFI_BLOCK_IO *blkio; 57 EFI_DEVICE_PATH *devpath; 58 EFI_STATUS status; 59 UINTN i; 60 int preferred; 61 62 /* Figure out if we're dealing with an actual partition. */ 63 status = OpenProtocolByHandle(h, &DevicePathGUID, (void **)&devpath); 64 if (status == EFI_UNSUPPORTED) 65 return (0); 66 67 if (status != EFI_SUCCESS) { 68 DPRINTF("\nFailed to query DevicePath (%lu)\n", 69 EFI_ERROR_CODE(status)); 70 return (-1); 71 } 72 #ifdef EFI_DEBUG 73 { 74 CHAR16 *text = efi_devpath_name(devpath); 75 DPRINTF("probing: %S ", text); 76 efi_free_devpath_name(text); 77 } 78 #endif 79 status = OpenProtocolByHandle(h, &BlockIoProtocolGUID, (void **)&blkio); 80 if (status == EFI_UNSUPPORTED) 81 return (0); 82 83 if (status != EFI_SUCCESS) { 84 DPRINTF("\nFailed to query BlockIoProtocol (%lu)\n", 85 EFI_ERROR_CODE(status)); 86 return (-1); 87 } 88 89 if (!blkio->Media->LogicalPartition) 90 return (0); 91 92 preferred = efi_devpath_same_disk(imgpath, devpath); 93 94 /* Run through each module, see if it can load this partition */ 95 devinfo = malloc(sizeof(*devinfo)); 96 if (devinfo == NULL) { 97 DPRINTF("\nFailed to allocate devinfo\n"); 98 return (-1); 99 } 100 devinfo->dev = blkio; 101 devinfo->devpath = devpath; 102 devinfo->devhandle = h; 103 devinfo->preferred = preferred; 104 devinfo->next = NULL; 105 106 for (i = 0; i < num_boot_modules; i++) { 107 devinfo->devdata = NULL; 108 109 status = boot_modules[i]->probe(devinfo); 110 if (status == EFI_SUCCESS) 111 return (preferred + 1); 112 } 113 free(devinfo); 114 115 return (0); 116 } 117 118 /* 119 * load_loader attempts to load the loader image data. 120 * 121 * It tries each module and its respective devices, identified by mod->probe, 122 * in order until a successful load occurs at which point it returns EFI_SUCCESS 123 * and EFI_NOT_FOUND otherwise. 124 * 125 * Only devices which have preferred matching the preferred parameter are tried. 126 */ 127 static EFI_STATUS 128 load_loader(const boot_module_t **modp, dev_info_t **devinfop, void **bufp, 129 size_t *bufsize, int preferred) 130 { 131 UINTN i; 132 dev_info_t *dev; 133 const boot_module_t *mod; 134 135 for (i = 0; i < num_boot_modules; i++) { 136 mod = boot_modules[i]; 137 for (dev = mod->devices(); dev != NULL; dev = dev->next) { 138 if (dev->preferred != preferred) 139 continue; 140 141 if (mod->load(PATH_LOADER_EFI, dev, bufp, bufsize) == 142 EFI_SUCCESS) { 143 *devinfop = dev; 144 *modp = mod; 145 return (EFI_SUCCESS); 146 } 147 } 148 } 149 150 return (EFI_NOT_FOUND); 151 } 152 153 void 154 choice_protocol(EFI_HANDLE *handles, UINTN nhandles, EFI_DEVICE_PATH *imgpath) 155 { 156 UINT16 boot_current; 157 size_t sz; 158 UINT16 boot_order[100]; 159 unsigned i; 160 int rv; 161 EFI_STATUS status; 162 const boot_module_t *mod; 163 dev_info_t *dev; 164 void *loaderbuf; 165 size_t loadersize; 166 167 /* Report UEFI Boot Manager Protocol details */ 168 boot_current = 0; 169 sz = sizeof(boot_current); 170 if (efi_global_getenv("BootCurrent", &boot_current, &sz) == EFI_SUCCESS) { 171 printf(" BootCurrent: %04x\n", boot_current); 172 173 sz = sizeof(boot_order); 174 if (efi_global_getenv("BootOrder", &boot_order, &sz) == EFI_SUCCESS) { 175 printf(" BootOrder:"); 176 for (i = 0; i < sz / sizeof(boot_order[0]); i++) 177 printf(" %04x%s", boot_order[i], 178 boot_order[i] == boot_current ? "[*]" : ""); 179 printf("\n"); 180 } 181 } 182 183 #ifdef TEST_FAILURE 184 /* 185 * For testing failover scenarios, it's nice to be able to fail fast. 186 * Define TEST_FAILURE to create a boot1.efi that always fails after 187 * reporting the boot manager protocol details. 188 */ 189 BS->Exit(IH, EFI_OUT_OF_RESOURCES, 0, NULL); 190 #endif 191 192 /* Scan all partitions, probing with all modules. */ 193 printf(" Probing %zu block devices...", nhandles); 194 DPRINTF("\n"); 195 for (i = 0; i < nhandles; i++) { 196 rv = probe_handle(handles[i], imgpath); 197 #ifdef EFI_DEBUG 198 printf("%c", "x.+*"[rv + 1]); 199 #else 200 printf("%s\n", prio_str[rv + 1]); 201 #endif 202 } 203 printf(" done\n"); 204 205 206 /* Status summary. */ 207 for (i = 0; i < num_boot_modules; i++) { 208 printf(" "); 209 boot_modules[i]->status(); 210 } 211 212 status = load_loader(&mod, &dev, &loaderbuf, &loadersize, 1); 213 if (status != EFI_SUCCESS) { 214 status = load_loader(&mod, &dev, &loaderbuf, &loadersize, 0); 215 if (status != EFI_SUCCESS) { 216 printf("Failed to load '%s'\n", PATH_LOADER_EFI); 217 return; 218 } 219 } 220 221 try_boot(mod, dev, loaderbuf, loadersize); 222 } 223