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  * $FreeBSD: stable/10/sys/boot/efi/boot1/zfs_module.c 321660 2017-07-28 18:35:29Z dim $
27  */
28 #include <stddef.h>
29 #include <stdarg.h>
30 #include <stdbool.h>
31 #include <sys/cdefs.h>
32 #include <sys/param.h>
33 #include <sys/queue.h>
34 #include <efi.h>
35 
36 #include "boot_module.h"
37 
38 #include "libzfs.h"
39 #include "zfsimpl.c"
40 
41 static dev_info_t *devices;
42 
43 static int
vdev_read(vdev_t * vdev,void * priv,off_t off,void * buf,size_t bytes)44 vdev_read(vdev_t *vdev, void *priv, off_t off, void *buf, size_t bytes)
45 {
46 	dev_info_t *devinfo;
47 	off_t lba;
48 	EFI_STATUS status;
49 
50 	devinfo = (dev_info_t *)priv;
51 	lba = off / devinfo->dev->Media->BlockSize;
52 
53 	status = devinfo->dev->ReadBlocks(devinfo->dev,
54 	    devinfo->dev->Media->MediaId, lba, bytes, buf);
55 	if (status != EFI_SUCCESS) {
56 		DPRINTF("vdev_read: failed dev: %p, id: %u, lba: %zu, size: %zu,"
57                     " status: %lu\n", devinfo->dev,
58                     devinfo->dev->Media->MediaId, lba, bytes,
59                     EFI_ERROR_CODE(status));
60 		return (-1);
61 	}
62 
63 	return (0);
64 }
65 
66 static EFI_STATUS
probe(dev_info_t * dev)67 probe(dev_info_t *dev)
68 {
69 	spa_t *spa;
70 	dev_info_t *tdev;
71 	EFI_STATUS status;
72 
73 	/* ZFS consumes the dev on success so we need a copy. */
74 	if ((status = bs->AllocatePool(EfiLoaderData, sizeof(*dev),
75 	    (void**)&tdev)) != EFI_SUCCESS) {
76 		DPRINTF("Failed to allocate tdev (%lu)\n",
77 		    EFI_ERROR_CODE(status));
78 		return (status);
79 	}
80 	memcpy(tdev, dev, sizeof(*dev));
81 
82 	if (vdev_probe(vdev_read, tdev, &spa) != 0) {
83 		(void)bs->FreePool(tdev);
84 		return (EFI_UNSUPPORTED);
85 	}
86 
87 	dev->devdata = spa;
88 	add_device(&devices, dev);
89 
90 	return (EFI_SUCCESS);
91 }
92 
93 static EFI_STATUS
load(const char * filepath,dev_info_t * devinfo,void ** bufp,size_t * bufsize)94 load(const char *filepath, dev_info_t *devinfo, void **bufp, size_t *bufsize)
95 {
96 	spa_t *spa;
97 	struct zfsmount zfsmount;
98 	dnode_phys_t dn;
99 	struct stat st;
100 	int err;
101 	void *buf;
102 	EFI_STATUS status;
103 
104 	spa = devinfo->devdata;
105 
106 	DPRINTF("load: '%s' spa: '%s', devpath: %s\n", filepath, spa->spa_name,
107 	    devpath_str(devinfo->devpath));
108 
109 	if ((err = zfs_spa_init(spa)) != 0) {
110 		DPRINTF("Failed to load pool '%s' (%d)\n", spa->spa_name, err);
111 		return (EFI_NOT_FOUND);
112 	}
113 
114 	if ((err = zfs_mount(spa, 0, &zfsmount)) != 0) {
115 		DPRINTF("Failed to mount pool '%s' (%d)\n", spa->spa_name, err);
116 		return (EFI_NOT_FOUND);
117 	}
118 
119 	if ((err = zfs_lookup(&zfsmount, filepath, &dn)) != 0) {
120 		if (err == ENOENT) {
121 			DPRINTF("Failed to find '%s' on pool '%s' (%d)\n",
122 			    filepath, spa->spa_name, err);
123 			return (EFI_NOT_FOUND);
124 		}
125 		printf("Failed to lookup '%s' on pool '%s' (%d)\n", filepath,
126 		    spa->spa_name, err);
127 		return (EFI_INVALID_PARAMETER);
128 	}
129 
130 	if ((err = zfs_dnode_stat(spa, &dn, &st)) != 0) {
131 		printf("Failed to stat '%s' on pool '%s' (%d)\n", filepath,
132 		    spa->spa_name, err);
133 		return (EFI_INVALID_PARAMETER);
134 	}
135 
136 	if ((status = bs->AllocatePool(EfiLoaderData, (UINTN)st.st_size, &buf))
137 	    != EFI_SUCCESS) {
138 		printf("Failed to allocate load buffer %jd for pool '%s' for '%s' "
139 		    "(%lu)\n", (intmax_t)st.st_size, spa->spa_name, filepath, EFI_ERROR_CODE(status));
140 		return (EFI_INVALID_PARAMETER);
141 	}
142 
143 	if ((err = dnode_read(spa, &dn, 0, buf, st.st_size)) != 0) {
144 		printf("Failed to read node from %s (%d)\n", spa->spa_name,
145 		    err);
146 		(void)bs->FreePool(buf);
147 		return (EFI_INVALID_PARAMETER);
148 	}
149 
150 	*bufsize = st.st_size;
151 	*bufp = buf;
152 
153 	return (EFI_SUCCESS);
154 }
155 
156 static void
status()157 status()
158 {
159 	spa_t *spa;
160 
161 	spa = STAILQ_FIRST(&zfs_pools);
162 	if (spa == NULL) {
163 		printf("%s found no pools\n", zfs_module.name);
164 		return;
165 	}
166 
167 	printf("%s found the following pools:", zfs_module.name);
168 	STAILQ_FOREACH(spa, &zfs_pools, spa_link)
169 		printf(" %s", spa->spa_name);
170 
171 	printf("\n");
172 }
173 
174 static void
init()175 init()
176 {
177 
178 	zfs_init();
179 }
180 
181 static dev_info_t *
_devices()182 _devices()
183 {
184 
185 	return (devices);
186 }
187 
188 const boot_module_t zfs_module =
189 {
190 	.name = "ZFS",
191 	.init = init,
192 	.probe = probe,
193 	.load = load,
194 	.status = status,
195 	.devices = _devices
196 };
197