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