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