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