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 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 static char zfs_bootonce[VDEV_PAD_SIZE]; 42 43 uint64_t 44 ldi_get_size(void *priv) 45 { 46 dev_info_t *devinfo = priv; 47 48 return (devinfo->dev->Media->BlockSize * 49 (devinfo->dev->Media->LastBlock + 1)); 50 } 51 52 static int 53 vdev_read(vdev_t *vdev, void *priv, off_t off, void *buf, size_t bytes) 54 { 55 dev_info_t *devinfo; 56 uint64_t lba; 57 size_t size, remainder, rb_size, blksz; 58 char *bouncebuf = NULL, *rb_buf; 59 EFI_STATUS status; 60 61 devinfo = (dev_info_t *)priv; 62 lba = off / devinfo->dev->Media->BlockSize; 63 remainder = off % devinfo->dev->Media->BlockSize; 64 65 rb_buf = buf; 66 rb_size = bytes; 67 68 /* 69 * If we have remainder from off, we need to add remainder part. 70 * Since buffer must be multiple of the BlockSize, round it all up. 71 */ 72 size = roundup2(bytes + remainder, devinfo->dev->Media->BlockSize); 73 blksz = size; 74 if (remainder != 0 || size != bytes) { 75 rb_size = devinfo->dev->Media->BlockSize; 76 bouncebuf = malloc(rb_size); 77 if (bouncebuf == NULL) { 78 printf("vdev_read: out of memory\n"); 79 return (-1); 80 } 81 rb_buf = bouncebuf; 82 blksz = rb_size - remainder; 83 } 84 85 while (bytes > 0) { 86 status = devinfo->dev->ReadBlocks(devinfo->dev, 87 devinfo->dev->Media->MediaId, lba, rb_size, rb_buf); 88 if (EFI_ERROR(status)) 89 goto error; 90 if (bytes < blksz) 91 blksz = bytes; 92 if (bouncebuf != NULL) 93 memcpy(buf, rb_buf + remainder, blksz); 94 buf = (void *)((uintptr_t)buf + blksz); 95 bytes -= blksz; 96 lba++; 97 remainder = 0; 98 blksz = rb_size; 99 } 100 101 free(bouncebuf); 102 return (0); 103 104 error: 105 free(bouncebuf); 106 DPRINTF("vdev_read: failed dev: %p, id: %u, lba: %ju, size: %zu," 107 " rb_size: %zu, status: %lu\n", devinfo->dev, 108 devinfo->dev->Media->MediaId, (uintmax_t)lba, bytes, rb_size, 109 EFI_ERROR_CODE(status)); 110 return (-1); 111 } 112 113 static EFI_STATUS 114 probe(dev_info_t *dev) 115 { 116 spa_t *spa; 117 dev_info_t *tdev; 118 119 /* ZFS consumes the dev on success so we need a copy. */ 120 tdev = malloc(sizeof(*dev)); 121 if (tdev == NULL) { 122 DPRINTF("Failed to allocate tdev\n"); 123 return (EFI_OUT_OF_RESOURCES); 124 } 125 memcpy(tdev, dev, sizeof(*dev)); 126 127 if (vdev_probe(vdev_read, NULL, tdev, &spa) != 0) { 128 free(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 zmount; 143 dnode_phys_t dn; 144 struct stat st; 145 uint64_t rootobj; 146 int err; 147 void *buf; 148 149 spa = devinfo->devdata; 150 151 #ifdef EFI_DEBUG 152 { 153 CHAR16 *text = efi_devpath_name(devinfo->devpath); 154 DPRINTF("load: '%s' spa: '%s', devpath: %S\n", filepath, 155 spa->spa_name, text); 156 efi_free_devpath_name(text); 157 } 158 #endif 159 if ((err = zfs_spa_init(spa)) != 0) { 160 DPRINTF("Failed to load pool '%s' (%d)\n", spa->spa_name, err); 161 return (EFI_NOT_FOUND); 162 } 163 164 if (zfs_get_bootonce_spa(spa, OS_BOOTONCE, zfs_bootonce, 165 sizeof(zfs_bootonce)) == 0) { 166 /* 167 * If bootonce attribute is present, use it as root dataset. 168 * Any attempt to use it should clear the 'once' flag. Prior 169 * to now, we'd not be able to clear it anyway. We don't care 170 * if we can't find the files to boot, or if there's a problem 171 * with it: we've tried to use it once we're able to mount the 172 * ZFS dataset. 173 * 174 * Note: the attribute is prefixed with "zfs:" and suffixed 175 * with ":". 176 */ 177 char *dname, *end; 178 179 if (zfs_bootonce[0] != 'z' || zfs_bootonce[1] != 'f' || 180 zfs_bootonce[2] != 's' || zfs_bootonce[3] != ':' || 181 (dname = strchr(&zfs_bootonce[4], '/')) == NULL || 182 (end = strrchr(&zfs_bootonce[4], ':')) == NULL) { 183 printf("INVALID zfs bootonce: %s\n", zfs_bootonce); 184 *zfs_bootonce = '\0'; 185 rootobj = 0; 186 } else { 187 dname += 1; 188 *end = '\0'; 189 if (zfs_lookup_dataset(spa, dname, &rootobj) != 0) { 190 printf("zfs bootonce dataset %s NOT FOUND\n", 191 dname); 192 *zfs_bootonce = '\0'; 193 rootobj = 0; 194 } else 195 printf("zfs bootonce: %s\n", zfs_bootonce); 196 *end = ':'; 197 } 198 } else { 199 *zfs_bootonce = '\0'; 200 rootobj = 0; 201 } 202 203 if ((err = zfs_mount_impl(spa, rootobj, &zmount)) != 0) { 204 printf("Failed to mount pool '%s' (%d)\n", spa->spa_name, err); 205 return (EFI_NOT_FOUND); 206 } 207 208 if ((err = zfs_lookup(&zmount, filepath, &dn)) != 0) { 209 if (err == ENOENT) { 210 DPRINTF("Failed to find '%s' on pool '%s' (%d)\n", 211 filepath, spa->spa_name, err); 212 return (EFI_NOT_FOUND); 213 } 214 printf("Failed to lookup '%s' on pool '%s' (%d)\n", filepath, 215 spa->spa_name, err); 216 return (EFI_INVALID_PARAMETER); 217 } 218 219 if ((err = zfs_dnode_stat(spa, &dn, &st)) != 0) { 220 printf("Failed to stat '%s' on pool '%s' (%d)\n", filepath, 221 spa->spa_name, err); 222 return (EFI_INVALID_PARAMETER); 223 } 224 225 buf = malloc(st.st_size); 226 if (buf == NULL) { 227 printf("Failed to allocate load buffer %jd for pool '%s' for '%s' ", 228 (intmax_t)st.st_size, spa->spa_name, filepath); 229 return (EFI_INVALID_PARAMETER); 230 } 231 232 if ((err = dnode_read(spa, &dn, 0, buf, st.st_size)) != 0) { 233 printf("Failed to read node from %s (%d)\n", spa->spa_name, 234 err); 235 free(buf); 236 return (EFI_INVALID_PARAMETER); 237 } 238 239 *bufsize = st.st_size; 240 *bufp = buf; 241 242 return (EFI_SUCCESS); 243 } 244 245 static void 246 status(void) 247 { 248 spa_t *spa; 249 250 spa = STAILQ_FIRST(&zfs_pools); 251 if (spa == NULL) { 252 printf("%s found no pools\n", zfs_module.name); 253 return; 254 } 255 256 printf("%s found the following pools:", zfs_module.name); 257 STAILQ_FOREACH(spa, &zfs_pools, spa_link) 258 printf(" %s", spa->spa_name); 259 260 printf("\n"); 261 } 262 263 static const char * 264 extra_env(void) 265 { 266 char *rv = NULL; /* So we return NULL if asprintf fails */ 267 268 if (*zfs_bootonce == '\0') 269 return NULL; 270 asprintf(&rv, "zfs-bootonce=%s", zfs_bootonce); 271 return (rv); 272 } 273 274 275 static void 276 init(void) 277 { 278 279 zfs_init(); 280 } 281 282 static dev_info_t * 283 _devices(void) 284 { 285 286 return (devices); 287 } 288 289 const boot_module_t zfs_module = 290 { 291 .name = "ZFS", 292 .init = init, 293 .probe = probe, 294 .load = load, 295 .status = status, 296 .devices = _devices, 297 .extra_env = extra_env, 298 }; 299