1324709Simp/*-
2324709Simp * Copyright (c) 2015 Eric McCorkle
3324709Simp * All rights reserved.
4324709Simp *
5324709Simp * Redistribution and use in source and binary forms, with or without
6324709Simp * modification, are permitted provided that the following conditions
7324709Simp * are met:
8324709Simp * 1. Redistributions of source code must retain the above copyright
9324709Simp *    notice, this list of conditions and the following disclaimer.
10324709Simp * 2. Redistributions in binary form must reproduce the above copyright
11324709Simp *    notice, this list of conditions and the following disclaimer in the
12324709Simp *    documentation and/or other materials provided with the distribution.
13324709Simp *
14324709Simp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15324709Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16324709Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17324709Simp * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18324709Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19324709Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20324709Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21324709Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22324709Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23324709Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24324709Simp * SUCH DAMAGE.
25324709Simp *
26324709Simp * $FreeBSD: stable/11/stand/efi/boot1/zfs_module.c 329140 2018-02-11 20:58:00Z kevans $
27324709Simp */
28324709Simp#include <stddef.h>
29324709Simp#include <stdarg.h>
30324709Simp#include <stdbool.h>
31324709Simp#include <sys/cdefs.h>
32324709Simp#include <sys/param.h>
33324709Simp#include <sys/queue.h>
34324709Simp#include <efi.h>
35324709Simp
36324709Simp#include "boot_module.h"
37324709Simp
38324709Simp#include "libzfs.h"
39324709Simp#include "zfsimpl.c"
40324709Simp
41324709Simpstatic dev_info_t *devices;
42324709Simp
43324709Simpuint64_t
44324709Simpldi_get_size(void *priv)
45324709Simp{
46324709Simp	dev_info_t *devinfo = priv;
47324709Simp
48324709Simp	return (devinfo->dev->Media->BlockSize *
49324709Simp	    (devinfo->dev->Media->LastBlock + 1));
50324709Simp}
51324709Simp
52324709Simpstatic int
53324709Simpvdev_read(vdev_t *vdev, void *priv, off_t off, void *buf, size_t bytes)
54324709Simp{
55324709Simp	dev_info_t *devinfo;
56324709Simp	uint64_t lba;
57324709Simp	size_t size, remainder, rb_size, blksz;
58324709Simp	char *bouncebuf = NULL, *rb_buf;
59324709Simp	EFI_STATUS status;
60324709Simp
61324709Simp	devinfo = (dev_info_t *)priv;
62324709Simp	lba = off / devinfo->dev->Media->BlockSize;
63324709Simp	remainder = off % devinfo->dev->Media->BlockSize;
64324709Simp
65324709Simp	rb_buf = buf;
66324709Simp	rb_size = bytes;
67324709Simp
68324709Simp	/*
69324709Simp	 * If we have remainder from off, we need to add remainder part.
70324709Simp	 * Since buffer must be multiple of the BlockSize, round it all up.
71324709Simp	 */
72324709Simp	size = roundup2(bytes + remainder, devinfo->dev->Media->BlockSize);
73324709Simp	blksz = size;
74324709Simp	if (remainder != 0 || size != bytes) {
75324709Simp		rb_size = devinfo->dev->Media->BlockSize;
76324709Simp		bouncebuf = malloc(rb_size);
77324709Simp		if (bouncebuf == NULL) {
78324709Simp			printf("vdev_read: out of memory\n");
79324709Simp			return (-1);
80324709Simp		}
81324709Simp		rb_buf = bouncebuf;
82324709Simp		blksz = rb_size - remainder;
83324709Simp	}
84324709Simp
85324709Simp	while (bytes > 0) {
86324709Simp		status = devinfo->dev->ReadBlocks(devinfo->dev,
87324709Simp		    devinfo->dev->Media->MediaId, lba, rb_size, rb_buf);
88324709Simp		if (EFI_ERROR(status))
89324709Simp				goto error;
90324709Simp		if (bytes < blksz)
91324709Simp			blksz = bytes;
92324709Simp		if (bouncebuf != NULL)
93324709Simp			memcpy(buf, rb_buf + remainder, blksz);
94324709Simp		buf = (void *)((uintptr_t)buf + blksz);
95324709Simp		bytes -= blksz;
96324709Simp		lba++;
97324709Simp		remainder = 0;
98324709Simp		blksz = rb_size;
99324709Simp	}
100324709Simp
101324709Simp	free(bouncebuf);
102324709Simp	return (0);
103324709Simp
104324709Simperror:
105324709Simp	free(bouncebuf);
106324709Simp	DPRINTF("vdev_read: failed dev: %p, id: %u, lba: %ju, size: %zu,"
107324709Simp	    " rb_size: %zu, status: %lu\n", devinfo->dev,
108324709Simp	    devinfo->dev->Media->MediaId, (uintmax_t)lba, bytes, rb_size,
109324709Simp	    EFI_ERROR_CODE(status));
110324709Simp	return (-1);
111324709Simp}
112324709Simp
113324709Simpstatic EFI_STATUS
114324709Simpprobe(dev_info_t *dev)
115324709Simp{
116324709Simp	spa_t *spa;
117324709Simp	dev_info_t *tdev;
118324709Simp	EFI_STATUS status;
119324709Simp
120324709Simp	/* ZFS consumes the dev on success so we need a copy. */
121324709Simp	if ((status = BS->AllocatePool(EfiLoaderData, sizeof(*dev),
122324709Simp	    (void**)&tdev)) != EFI_SUCCESS) {
123324709Simp		DPRINTF("Failed to allocate tdev (%lu)\n",
124324709Simp		    EFI_ERROR_CODE(status));
125324709Simp		return (status);
126324709Simp	}
127324709Simp	memcpy(tdev, dev, sizeof(*dev));
128324709Simp
129324709Simp	if (vdev_probe(vdev_read, tdev, &spa) != 0) {
130324709Simp		(void)BS->FreePool(tdev);
131324709Simp		return (EFI_UNSUPPORTED);
132324709Simp	}
133324709Simp
134324709Simp	dev->devdata = spa;
135324709Simp	add_device(&devices, dev);
136324709Simp
137324709Simp	return (EFI_SUCCESS);
138324709Simp}
139324709Simp
140324709Simpstatic EFI_STATUS
141324709Simpload(const char *filepath, dev_info_t *devinfo, void **bufp, size_t *bufsize)
142324709Simp{
143324709Simp	spa_t *spa;
144324709Simp	struct zfsmount zfsmount;
145324709Simp	dnode_phys_t dn;
146324709Simp	struct stat st;
147324709Simp	int err;
148324709Simp	void *buf;
149324709Simp	EFI_STATUS status;
150324709Simp
151324709Simp	spa = devinfo->devdata;
152324709Simp
153324709Simp#ifdef EFI_DEBUG
154324709Simp	{
155324709Simp		CHAR16 *text = efi_devpath_name(devinfo->devpath);
156324709Simp		DPRINTF("load: '%s' spa: '%s', devpath: %S\n", filepath,
157324709Simp		    spa->spa_name, text);
158324709Simp		efi_free_devpath_name(text);
159324709Simp	}
160324709Simp#endif
161324709Simp	if ((err = zfs_spa_init(spa)) != 0) {
162324709Simp		DPRINTF("Failed to load pool '%s' (%d)\n", spa->spa_name, err);
163324709Simp		return (EFI_NOT_FOUND);
164324709Simp	}
165324709Simp
166324709Simp	if ((err = zfs_mount(spa, 0, &zfsmount)) != 0) {
167324709Simp		DPRINTF("Failed to mount pool '%s' (%d)\n", spa->spa_name, err);
168324709Simp		return (EFI_NOT_FOUND);
169324709Simp	}
170324709Simp
171324709Simp	if ((err = zfs_lookup(&zfsmount, filepath, &dn)) != 0) {
172324709Simp		if (err == ENOENT) {
173324709Simp			DPRINTF("Failed to find '%s' on pool '%s' (%d)\n",
174324709Simp			    filepath, spa->spa_name, err);
175324709Simp			return (EFI_NOT_FOUND);
176324709Simp		}
177324709Simp		printf("Failed to lookup '%s' on pool '%s' (%d)\n", filepath,
178324709Simp		    spa->spa_name, err);
179324709Simp		return (EFI_INVALID_PARAMETER);
180324709Simp	}
181324709Simp
182324709Simp	if ((err = zfs_dnode_stat(spa, &dn, &st)) != 0) {
183324709Simp		printf("Failed to stat '%s' on pool '%s' (%d)\n", filepath,
184324709Simp		    spa->spa_name, err);
185324709Simp		return (EFI_INVALID_PARAMETER);
186324709Simp	}
187324709Simp
188324709Simp	if ((status = BS->AllocatePool(EfiLoaderData, (UINTN)st.st_size, &buf))
189324709Simp	    != EFI_SUCCESS) {
190324709Simp		printf("Failed to allocate load buffer %jd for pool '%s' for '%s' "
191324709Simp		    "(%lu)\n", (intmax_t)st.st_size, spa->spa_name, filepath, EFI_ERROR_CODE(status));
192324709Simp		return (EFI_INVALID_PARAMETER);
193324709Simp	}
194324709Simp
195324709Simp	if ((err = dnode_read(spa, &dn, 0, buf, st.st_size)) != 0) {
196324709Simp		printf("Failed to read node from %s (%d)\n", spa->spa_name,
197324709Simp		    err);
198324709Simp		(void)BS->FreePool(buf);
199324709Simp		return (EFI_INVALID_PARAMETER);
200324709Simp	}
201324709Simp
202324709Simp	*bufsize = st.st_size;
203324709Simp	*bufp = buf;
204324709Simp
205324709Simp	return (EFI_SUCCESS);
206324709Simp}
207324709Simp
208324709Simpstatic void
209324709Simpstatus(void)
210324709Simp{
211324709Simp	spa_t *spa;
212324709Simp
213324709Simp	spa = STAILQ_FIRST(&zfs_pools);
214324709Simp	if (spa == NULL) {
215324709Simp		printf("%s found no pools\n", zfs_module.name);
216324709Simp		return;
217324709Simp	}
218324709Simp
219324709Simp	printf("%s found the following pools:", zfs_module.name);
220324709Simp	STAILQ_FOREACH(spa, &zfs_pools, spa_link)
221324709Simp		printf(" %s", spa->spa_name);
222324709Simp
223324709Simp	printf("\n");
224324709Simp}
225324709Simp
226324709Simpstatic void
227324709Simpinit(void)
228324709Simp{
229324709Simp
230324709Simp	zfs_init();
231324709Simp}
232324709Simp
233324709Simpstatic dev_info_t *
234324709Simp_devices(void)
235324709Simp{
236324709Simp
237324709Simp	return (devices);
238324709Simp}
239324709Simp
240324709Simpconst boot_module_t zfs_module =
241324709Simp{
242324709Simp	.name = "ZFS",
243324709Simp	.init = init,
244324709Simp	.probe = probe,
245324709Simp	.load = load,
246324709Simp	.status = status,
247324709Simp	.devices = _devices
248324709Simp};
249