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