1 /*-
   2  * Copyright (c) 2015 Eric McCorkle
   3  * All rights reserved.
   4  *
   5  * Redistribution and use in source and binary forms, with or without
   6  * modification, are permitted provided that the following conditions
   7  * are met:
   8  * 1. Redistributions of source code must retain the above copyright
   9  *    notice, this list of conditions and the following disclaimer.
  10  * 2. Redistributions in binary form must reproduce the above copyright
  11  *    notice, this list of conditions and the following disclaimer in the
  12  *    documentation and/or other materials provided with the distribution.
  13  *
  14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  24  * SUCH DAMAGE.
  25  */
  26 #include <stddef.h>
  27 #include <stdarg.h>
  28 #include <stdbool.h>
  29 #include <sys/cdefs.h>
  30 #include <sys/param.h>
  31 #include <sys/queue.h>
  32 #include <efi.h>
  33 
  34 #include "boot_module.h"
  35 
  36 #include "libzfs.h"
  37 #include "zfsimpl.c"
  38 
  39 static dev_info_t *devices;
  40 
  41 uint64_t
  42 ldi_get_size(void *priv)
  43 {
  44         dev_info_t *devinfo = priv;
  45 
  46         return (devinfo->dev->Media->BlockSize *
  47             (devinfo->dev->Media->LastBlock + 1));
  48 }
  49 
  50 static int
  51 vdev_read(vdev_t *vdev, void *priv, off_t off, void *buf, size_t bytes)
  52 {
  53         dev_info_t *devinfo;
  54         uint64_t lba;
  55         size_t size, remainder, rb_size, blksz;
  56         char *bouncebuf = NULL, *rb_buf;
  57         EFI_STATUS status;
  58 
  59         devinfo = (dev_info_t *)priv;
  60         lba = off / devinfo->dev->Media->BlockSize;
  61         remainder = off % devinfo->dev->Media->BlockSize;
  62 
  63         rb_buf = buf;
  64         rb_size = bytes;
  65 
  66         /*
  67          * If we have remainder from off, we need to add remainder part.
  68          * Since buffer must be multiple of the BlockSize, round it all up.
  69          */
  70         size = roundup2(bytes + remainder, devinfo->dev->Media->BlockSize);
  71         blksz = size;
  72         if (remainder != 0 || size != bytes) {
  73                 rb_size = devinfo->dev->Media->BlockSize;
  74                 bouncebuf = malloc(rb_size);
  75                 if (bouncebuf == NULL) {
  76                         printf("vdev_read: out of memory\n");
  77                         return (-1);
  78                 }
  79                 rb_buf = bouncebuf;
  80                 blksz = rb_size - remainder;
  81         }
  82 
  83         while (bytes > 0) {
  84                 status = devinfo->dev->ReadBlocks(devinfo->dev,
  85                     devinfo->dev->Media->MediaId, lba, rb_size, rb_buf);
  86                 if (status != EFI_SUCCESS)
  87                         goto error;
  88                 if (bytes < blksz)
  89                         blksz = bytes;
  90                 if (bouncebuf != NULL)
  91                         memcpy(buf, rb_buf + remainder, blksz);
  92                 buf = (void *)((uintptr_t)buf + blksz);
  93                 bytes -= blksz;
  94                 lba++;
  95                 remainder = 0;
  96                 blksz = rb_size;
  97         }
  98 
  99         free(bouncebuf);
 100         return (0);
 101 
 102 error:
 103         free(bouncebuf);
 104         DPRINTF("vdev_read: failed dev: %p, id: %u, lba: %ju, size: %zu,"
 105             " rb_size: %zu, status: %lu\n", devinfo->dev,
 106             devinfo->dev->Media->MediaId, (uintmax_t)lba, bytes, rb_size,
 107             EFI_ERROR_CODE(status));
 108         return (-1);
 109 }
 110 
 111 static EFI_STATUS
 112 probe(dev_info_t *dev)
 113 {
 114         spa_t *spa;
 115         dev_info_t *tdev;
 116         EFI_STATUS status;
 117 
 118         /* ZFS consumes the dev on success so we need a copy. */
 119         if ((status = BS->AllocatePool(EfiLoaderData, sizeof(*dev),
 120             (void**)&tdev)) != EFI_SUCCESS) {
 121                 DPRINTF("Failed to allocate tdev (%lu)\n",
 122                     EFI_ERROR_CODE(status));
 123                 return (status);
 124         }
 125         memcpy(tdev, dev, sizeof(*dev));
 126 
 127         if (vdev_probe(vdev_read, tdev, &spa) != 0) {
 128                 (void)BS->FreePool(tdev);
 129                 return (EFI_UNSUPPORTED);
 130         }
 131 
 132         dev->devdata = spa;
 133         add_device(&devices, dev);
 134 
 135         return (EFI_SUCCESS);
 136 }
 137 
 138 static EFI_STATUS
 139 load(const char *filepath, dev_info_t *devinfo, void **bufp, size_t *bufsize)
 140 {
 141         spa_t *spa;
 142         struct zfsmount zfsmount;
 143         dnode_phys_t dn;
 144         struct stat st;
 145         int err;
 146         void *buf;
 147         EFI_STATUS status;
 148 
 149         spa = devinfo->devdata;
 150 
 151         DPRINTF("load: '%s' spa: '%s', devpath: %s\n", filepath, spa->spa_name,
 152             devpath_str(devinfo->devpath));
 153 
 154         if ((err = zfs_spa_init(spa)) != 0) {
 155                 DPRINTF("Failed to load pool '%s' (%d)\n", spa->spa_name, err);
 156                 return (EFI_NOT_FOUND);
 157         }
 158 
 159         if ((err = zfs_mount(spa, 0, &zfsmount)) != 0) {
 160                 DPRINTF("Failed to mount pool '%s' (%d)\n", spa->spa_name, err);
 161                 return (EFI_NOT_FOUND);
 162         }
 163 
 164         if ((err = zfs_lookup(&zfsmount, filepath, &dn)) != 0) {
 165                 if (err == ENOENT) {
 166                         DPRINTF("Failed to find '%s' on pool '%s' (%d)\n",
 167                             filepath, spa->spa_name, err);
 168                         return (EFI_NOT_FOUND);
 169                 }
 170                 printf("Failed to lookup '%s' on pool '%s' (%d)\n", filepath,
 171                     spa->spa_name, err);
 172                 return (EFI_INVALID_PARAMETER);
 173         }
 174 
 175         if ((err = zfs_dnode_stat(spa, &dn, &st)) != 0) {
 176                 printf("Failed to stat '%s' on pool '%s' (%d)\n", filepath,
 177                     spa->spa_name, err);
 178                 return (EFI_INVALID_PARAMETER);
 179         }
 180 
 181         if ((status = BS->AllocatePool(EfiLoaderData, (UINTN)st.st_size, &buf))
 182             != EFI_SUCCESS) {
 183                 printf("Failed to allocate load buffer %zd for pool '%s' for '%s' "
 184                     "(%lu)\n", st.st_size, spa->spa_name, filepath, EFI_ERROR_CODE(status));
 185                 return (EFI_INVALID_PARAMETER);
 186         }
 187 
 188         if ((err = dnode_read(spa, &dn, 0, buf, st.st_size)) != 0) {
 189                 printf("Failed to read node from %s (%d)\n", spa->spa_name,
 190                     err);
 191                 (void)BS->FreePool(buf);
 192                 return (EFI_INVALID_PARAMETER);
 193         }
 194 
 195         *bufsize = st.st_size;
 196         *bufp = buf;
 197 
 198         return (EFI_SUCCESS);
 199 }
 200 
 201 static void
 202 status(void)
 203 {
 204         spa_t *spa;
 205 
 206         spa = STAILQ_FIRST(&zfs_pools);
 207         if (spa == NULL) {
 208                 printf("%s found no pools\n", zfs_module.name);
 209                 return;
 210         }
 211 
 212         printf("%s found the following pools:", zfs_module.name);
 213         STAILQ_FOREACH(spa, &zfs_pools, spa_link)
 214                 printf(" %s", spa->spa_name);
 215 
 216         printf("\n");
 217 }
 218 
 219 static void
 220 init(void)
 221 {
 222 
 223         zfs_init();
 224 }
 225 
 226 static dev_info_t *
 227 _devices(void)
 228 {
 229 
 230         return (devices);
 231 }
 232 
 233 const boot_module_t zfs_module =
 234 {
 235         .name = "ZFS",
 236         .init = init,
 237         .probe = probe,
 238         .load = load,
 239         .status = status,
 240         .devices = _devices
 241 };