1 /*- 2 * Copyright (c) 2019 Netflix, Inc 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26 #include <sys/param.h> 27 #include <sys/stdarg.h> 28 29 #include <machine/elf.h> 30 31 #include <stand.h> 32 33 #include <efi.h> 34 #include <eficonsctl.h> 35 #include <efichar.h> 36 37 #include "boot_module.h" 38 #include "paths.h" 39 #include "proto.h" 40 41 #include "gpt.h" 42 #include <sys/gpt.h> 43 static const uuid_t freebsd_ufs_uuid = GPT_ENT_TYPE_FREEBSD_UFS; 44 static char secbuf[4096] __aligned(4096); 45 static struct dsk dsk; 46 static dev_info_t *devices = NULL; 47 static dev_info_t *raw_device = NULL; 48 49 static EFI_GUID BlockIoProtocolGUID = BLOCK_IO_PROTOCOL; 50 static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL; 51 52 /* 53 * Shim routine for the gpt code to read in the gpt table. The 54 * devinfo is always going to be for the raw device. 55 */ 56 int 57 drvread(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk) 58 { 59 int size; 60 EFI_STATUS status; 61 dev_info_t *devinfo = (dev_info_t *)dskp->devinfo; 62 EFI_BLOCK_IO *dev = devinfo->dev; 63 64 lba = lba / (dev->Media->BlockSize / DEV_BSIZE); 65 size = nblk * DEV_BSIZE; 66 67 status = dev->ReadBlocks(dev, dev->Media->MediaId, lba, size, buf); 68 if (status != EFI_SUCCESS) { 69 DPRINTF("dskread: failed dev: %p, id: %u, lba: %ju, size: %d, " 70 "status: %lu\n", devinfo->dev, 71 dev->Media->MediaId, (uintmax_t)lba, size, 72 EFI_ERROR_CODE(status)); 73 return (-1); 74 } 75 76 return (0); 77 } 78 79 /* 80 * Shim routine for the gpt code to write in the gpt table. The 81 * devinfo is always going to be for the raw device. 82 */ 83 int 84 drvwrite(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk) 85 { 86 int size; 87 EFI_STATUS status; 88 dev_info_t *devinfo = (dev_info_t *)dskp->devinfo; 89 EFI_BLOCK_IO *dev = devinfo->dev; 90 91 if (dev->Media->ReadOnly) 92 return -1; 93 94 lba = lba / (dev->Media->BlockSize / DEV_BSIZE); 95 size = nblk * DEV_BSIZE; 96 97 status = dev->WriteBlocks(dev, dev->Media->MediaId, lba, size, buf); 98 if (status != EFI_SUCCESS) { 99 DPRINTF("dskread: failed dev: %p, id: %u, lba: %ju, size: %d, " 100 "status: %lu\n", devinfo->dev, 101 dev->Media->MediaId, (uintmax_t)lba, size, 102 EFI_ERROR_CODE(status)); 103 return (-1); 104 } 105 106 return (0); 107 } 108 109 /* 110 * Return the number of LBAs the drive has. 111 */ 112 uint64_t 113 drvsize(struct dsk *dskp) 114 { 115 dev_info_t *devinfo = (dev_info_t *)dskp->devinfo; 116 EFI_BLOCK_IO *dev = devinfo->dev; 117 118 return (dev->Media->LastBlock + 1); 119 } 120 121 static int 122 partition_number(EFI_DEVICE_PATH *devpath) 123 { 124 EFI_DEVICE_PATH *md; 125 HARDDRIVE_DEVICE_PATH *hd; 126 127 md = efi_devpath_last_node(devpath); 128 if (md == NULL) 129 return (-1); 130 if (DevicePathSubType(md) != MEDIA_HARDDRIVE_DP) 131 return (-1); 132 hd = (HARDDRIVE_DEVICE_PATH *)md; 133 return (hd->PartitionNumber); 134 } 135 136 /* 137 * Find the raw partition for the imgpath and save it 138 */ 139 static void 140 probe_handle(EFI_HANDLE h, EFI_DEVICE_PATH *imgpath) 141 { 142 dev_info_t *devinfo; 143 EFI_BLOCK_IO *blkio; 144 EFI_DEVICE_PATH *devpath, *trimmed = NULL; 145 EFI_STATUS status; 146 147 /* Figure out if we're dealing with an actual partition. */ 148 status = OpenProtocolByHandle(h, &DevicePathGUID, (void **)&devpath); 149 if (status != EFI_SUCCESS) 150 return; 151 #ifdef EFI_DEBUG 152 { 153 CHAR16 *text = efi_devpath_name(devpath); 154 DPRINTF("probing: %S ", text); 155 efi_free_devpath_name(text); 156 } 157 #endif 158 /* 159 * The RAW device is the same as the imgpath with the last 160 * MEDIA_DEVICE bit trimmed off. imgpath will end with the 161 * MEDIA_DEVICE for the ESP we booted off of. 162 */ 163 if (!efi_devpath_same_disk(imgpath, devpath)) { 164 trimmed = efi_devpath_trim(imgpath); 165 if (!efi_devpath_match(trimmed, devpath)) { 166 free(trimmed); 167 DPRINTF("Not the same disk\n"); 168 return; 169 } 170 } 171 status = OpenProtocolByHandle(h, &BlockIoProtocolGUID, (void **)&blkio); 172 if (status != EFI_SUCCESS) { 173 DPRINTF("Can't get the block I/O protocol block\n"); 174 return; 175 } 176 devinfo = malloc(sizeof(*devinfo)); 177 if (devinfo == NULL) { 178 DPRINTF("Failed to allocate devinfo\n"); 179 return; 180 } 181 devinfo->dev = blkio; 182 devinfo->devpath = devpath; 183 devinfo->devhandle = h; 184 devinfo->preferred = 1; 185 devinfo->next = NULL; 186 devinfo->devdata = NULL; 187 if (trimmed == NULL) { 188 DPRINTF("Found partition %d\n", partition_number(devpath)); 189 add_device(&devices, devinfo); 190 } else { 191 free(trimmed); 192 DPRINTF("Found raw device\n"); 193 if (raw_device) { 194 printf(BOOTPROG": Found two raw devices, inconceivable?\n"); 195 return; 196 } 197 raw_device = devinfo; 198 } 199 } 200 201 static void 202 probe_handles(EFI_HANDLE *handles, UINTN nhandles, EFI_DEVICE_PATH *imgpath) 203 { 204 UINTN i; 205 206 for (i = 0; i < nhandles; i++) 207 probe_handle(handles[i], imgpath); 208 } 209 210 static dev_info_t * 211 find_partition(int part) 212 { 213 dev_info_t *dev; 214 215 if (part == 0) 216 return (NULL); 217 for (dev = devices; dev != NULL; dev = dev->next) 218 if (partition_number(dev->devpath) == part) 219 break; 220 return (dev); 221 } 222 223 void 224 choice_protocol(EFI_HANDLE *handles, UINTN nhandles, EFI_DEVICE_PATH *imgpath) 225 { 226 const boot_module_t *mod = &ufs_module; 227 dev_info_t *bootdev; 228 void *loaderbuf; 229 size_t loadersize; 230 int parts; 231 const char *fn = PATH_LOADER_EFI; 232 233 /* 234 * Probe the provided handles to find the partitions that 235 * are on the same drive. 236 */ 237 probe_handles(handles, nhandles, imgpath); 238 dsk.devinfo = raw_device; 239 if (dsk.devinfo == NULL) { 240 printf(BOOTPROG": unable to find raw disk to read gpt\n"); 241 return; 242 } 243 244 /* 245 * Read in the GPT table, and then find the right partition. 246 * gptread, gptfind and gptfaileboot are shared with the 247 * BIOS version of the gptboot program. 248 */ 249 if (gptread(&dsk, secbuf) == -1) { 250 printf(BOOTPROG ": unable to load GPT\n"); 251 return; 252 } 253 // XXX: 254 // real gptboot can parse a command line before trying this loop. 255 // But since we don't parse anything at all, hard wire the partition 256 // to be -1 (meaning look for the next one). 257 parts = 0; 258 while (gptfind(&freebsd_ufs_uuid, &dsk, -1) != -1) { 259 parts++; 260 bootdev = find_partition(dsk.part); 261 if (bootdev == NULL) { 262 printf(BOOTPROG": Can't find partition %d\n", 263 dsk.part); 264 goto next; 265 } 266 if (mod->load(fn, bootdev, &loaderbuf, &loadersize) != 267 EFI_SUCCESS) { 268 printf(BOOTPROG": Can't load %s from partition %d\n", 269 fn, dsk.part); 270 goto next; 271 } 272 try_boot(mod, bootdev, loaderbuf, loadersize); 273 next: 274 gptbootfailed(&dsk); 275 } 276 if (parts == 0) 277 printf("%s: no UFS partition was found\n", BOOTPROG); 278 } 279