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 <machine/elf.h> 28 #include <machine/stdarg.h> 29 #include <stand.h> 30 31 #include <efi.h> 32 #include <eficonsctl.h> 33 #include <efichar.h> 34 35 #include "boot_module.h" 36 #include "paths.h" 37 #include "proto.h" 38 39 #include "gpt.h" 40 #include <sys/gpt.h> 41 static const uuid_t freebsd_ufs_uuid = GPT_ENT_TYPE_FREEBSD_UFS; 42 static char secbuf[4096] __aligned(4096); 43 static struct dsk dsk; 44 static dev_info_t *devices = NULL; 45 static dev_info_t *raw_device = NULL; 46 47 static EFI_GUID BlockIoProtocolGUID = BLOCK_IO_PROTOCOL; 48 static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL; 49 50 /* 51 * Shim routine for the gpt code to read in the gpt table. The 52 * devinfo is always going to be for the raw device. 53 */ 54 int 55 drvread(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk) 56 { 57 int size; 58 EFI_STATUS status; 59 dev_info_t *devinfo = (dev_info_t *)dskp->devinfo; 60 EFI_BLOCK_IO *dev = devinfo->dev; 61 62 lba = lba / (dev->Media->BlockSize / DEV_BSIZE); 63 size = nblk * DEV_BSIZE; 64 65 status = dev->ReadBlocks(dev, dev->Media->MediaId, lba, size, buf); 66 if (status != EFI_SUCCESS) { 67 DPRINTF("dskread: failed dev: %p, id: %u, lba: %ju, size: %d, " 68 "status: %lu\n", devinfo->dev, 69 dev->Media->MediaId, (uintmax_t)lba, size, 70 EFI_ERROR_CODE(status)); 71 return (-1); 72 } 73 74 return (0); 75 } 76 77 /* 78 * Shim routine for the gpt code to write in the gpt table. The 79 * devinfo is always going to be for the raw device. 80 */ 81 int 82 drvwrite(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk) 83 { 84 int size; 85 EFI_STATUS status; 86 dev_info_t *devinfo = (dev_info_t *)dskp->devinfo; 87 EFI_BLOCK_IO *dev = devinfo->dev; 88 89 if (dev->Media->ReadOnly) 90 return -1; 91 92 lba = lba / (dev->Media->BlockSize / DEV_BSIZE); 93 size = nblk * DEV_BSIZE; 94 95 status = dev->WriteBlocks(dev, dev->Media->MediaId, lba, size, buf); 96 if (status != EFI_SUCCESS) { 97 DPRINTF("dskread: failed dev: %p, id: %u, lba: %ju, size: %d, " 98 "status: %lu\n", devinfo->dev, 99 dev->Media->MediaId, (uintmax_t)lba, size, 100 EFI_ERROR_CODE(status)); 101 return (-1); 102 } 103 104 return (0); 105 } 106 107 /* 108 * Return the number of LBAs the drive has. 109 */ 110 uint64_t 111 drvsize(struct dsk *dskp) 112 { 113 dev_info_t *devinfo = (dev_info_t *)dskp->devinfo; 114 EFI_BLOCK_IO *dev = devinfo->dev; 115 116 return (dev->Media->LastBlock + 1); 117 } 118 119 static int 120 partition_number(EFI_DEVICE_PATH *devpath) 121 { 122 EFI_DEVICE_PATH *md; 123 HARDDRIVE_DEVICE_PATH *hd; 124 125 md = efi_devpath_last_node(devpath); 126 if (md == NULL) 127 return (-1); 128 if (DevicePathSubType(md) != MEDIA_HARDDRIVE_DP) 129 return (-1); 130 hd = (HARDDRIVE_DEVICE_PATH *)md; 131 return (hd->PartitionNumber); 132 } 133 134 /* 135 * Find the raw partition for the imgpath and save it 136 */ 137 static void 138 probe_handle(EFI_HANDLE h, EFI_DEVICE_PATH *imgpath) 139 { 140 dev_info_t *devinfo; 141 EFI_BLOCK_IO *blkio; 142 EFI_DEVICE_PATH *devpath, *trimmed = NULL; 143 EFI_STATUS status; 144 145 /* Figure out if we're dealing with an actual partition. */ 146 status = OpenProtocolByHandle(h, &DevicePathGUID, (void **)&devpath); 147 if (status != EFI_SUCCESS) 148 return; 149 #ifdef EFI_DEBUG 150 { 151 CHAR16 *text = efi_devpath_name(devpath); 152 DPRINTF("probing: %S ", text); 153 efi_free_devpath_name(text); 154 } 155 #endif 156 /* 157 * The RAW device is the same as the imgpath with the last 158 * MEDIA_DEVICE bit trimmed off. imgpath will end with the 159 * MEDIA_DEVICE for the ESP we booted off of. 160 */ 161 if (!efi_devpath_same_disk(imgpath, devpath)) { 162 trimmed = efi_devpath_trim(imgpath); 163 if (!efi_devpath_match(trimmed, devpath)) { 164 free(trimmed); 165 DPRINTF("Not the same disk\n"); 166 return; 167 } 168 } 169 status = OpenProtocolByHandle(h, &BlockIoProtocolGUID, (void **)&blkio); 170 if (status != EFI_SUCCESS) { 171 DPRINTF("Can't get the block I/O protocol block\n"); 172 return; 173 } 174 devinfo = malloc(sizeof(*devinfo)); 175 if (devinfo == NULL) { 176 DPRINTF("Failed to allocate devinfo\n"); 177 return; 178 } 179 devinfo->dev = blkio; 180 devinfo->devpath = devpath; 181 devinfo->devhandle = h; 182 devinfo->preferred = 1; 183 devinfo->next = NULL; 184 devinfo->devdata = NULL; 185 if (trimmed == NULL) { 186 DPRINTF("Found partition %d\n", partition_number(devpath)); 187 add_device(&devices, devinfo); 188 } else { 189 free(trimmed); 190 DPRINTF("Found raw device\n"); 191 if (raw_device) { 192 printf(BOOTPROG": Found two raw devices, inconceivable?\n"); 193 return; 194 } 195 raw_device = devinfo; 196 } 197 } 198 199 static void 200 probe_handles(EFI_HANDLE *handles, UINTN nhandles, EFI_DEVICE_PATH *imgpath) 201 { 202 UINTN i; 203 204 for (i = 0; i < nhandles; i++) 205 probe_handle(handles[i], imgpath); 206 } 207 208 static dev_info_t * 209 find_partition(int part) 210 { 211 dev_info_t *dev; 212 213 if (part == 0) 214 return (NULL); 215 for (dev = devices; dev != NULL; dev = dev->next) 216 if (partition_number(dev->devpath) == part) 217 break; 218 return (dev); 219 } 220 221 void 222 choice_protocol(EFI_HANDLE *handles, UINTN nhandles, EFI_DEVICE_PATH *imgpath) 223 { 224 const boot_module_t *mod = &ufs_module; 225 dev_info_t *bootdev; 226 void *loaderbuf; 227 size_t loadersize; 228 int parts; 229 const char *fn = PATH_LOADER_EFI; 230 231 /* 232 * Probe the provided handles to find the partitions that 233 * are on the same drive. 234 */ 235 probe_handles(handles, nhandles, imgpath); 236 dsk.devinfo = raw_device; 237 if (dsk.devinfo == NULL) { 238 printf(BOOTPROG": unable to find raw disk to read gpt\n"); 239 return; 240 } 241 242 /* 243 * Read in the GPT table, and then find the right partition. 244 * gptread, gptfind and gptfaileboot are shared with the 245 * BIOS version of the gptboot program. 246 */ 247 if (gptread(&dsk, secbuf) == -1) { 248 printf(BOOTPROG ": unable to load GPT\n"); 249 return; 250 } 251 // XXX: 252 // real gptboot can parse a command line before trying this loop. 253 // But since we don't parse anything at all, hard wire the partition 254 // to be -1 (meaning look for the next one). 255 parts = 0; 256 while (gptfind(&freebsd_ufs_uuid, &dsk, -1) != -1) { 257 parts++; 258 bootdev = find_partition(dsk.part); 259 if (bootdev == NULL) { 260 printf(BOOTPROG": Can't find partition %d\n", 261 dsk.part); 262 goto next; 263 } 264 if (mod->load(fn, bootdev, &loaderbuf, &loadersize) != 265 EFI_SUCCESS) { 266 printf(BOOTPROG": Can't load %s from partition %d\n", 267 fn, dsk.part); 268 goto next; 269 } 270 try_boot(mod, bootdev, loaderbuf, loadersize); 271 next: 272 gptbootfailed(&dsk); 273 } 274 if (parts == 0) 275 printf("%s: no UFS partition was found\n", BOOTPROG); 276 } 277