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