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