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