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
41static dev_info_t *devices;
42
43static int
44vdev_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
66static EFI_STATUS
67probe(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
93static EFI_STATUS
94load(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
156static void
157status()
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
174static void
175init()
176{
177
178	zfs_init();
179}
180
181static dev_info_t *
182_devices()
183{
184
185	return (devices);
186}
187
188const 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