1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (C) 2002 Benno Rice <benno@FreeBSD.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY Benno Rice ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include <sys/param.h>
29#include <sys/systm.h>
30#include <sys/bio.h>
31#include <sys/kernel.h>
32#include <sys/kthread.h>
33#include <sys/linker.h>
34#include <sys/lock.h>
35#include <sys/malloc.h>
36#include <sys/mutex.h>
37#include <sys/proc.h>
38
39#include <geom/geom.h>
40
41#include <dev/ofw/openfirm.h>
42
43#define	OFWD_BLOCKSIZE	512
44
45struct ofwd_softc
46{
47	struct bio_queue_head ofwd_bio_queue;
48	struct mtx	ofwd_queue_mtx;
49	ihandle_t	ofwd_instance;
50	off_t		ofwd_mediasize;
51	unsigned	ofwd_sectorsize;
52	unsigned	ofwd_fwheads;
53	unsigned	ofwd_fwsectors;
54	struct proc	*ofwd_procp;
55	struct g_geom	*ofwd_gp;
56	struct g_provider *ofwd_pp;
57} ofwd_softc;
58
59static g_init_t g_ofwd_init;
60static g_start_t g_ofwd_start;
61static g_access_t g_ofwd_access;
62
63struct g_class g_ofwd_class = {
64	.name = "OFWD",
65	.version = G_VERSION,
66	.init = g_ofwd_init,
67	.start = g_ofwd_start,
68	.access = g_ofwd_access,
69};
70
71DECLARE_GEOM_CLASS(g_ofwd_class, g_ofwd);
72
73static int ofwd_enable = 0;
74TUNABLE_INT("kern.ofw.disk", &ofwd_enable);
75
76static int
77ofwd_startio(struct ofwd_softc *sc, struct bio *bp)
78{
79	u_int r;
80
81	r = OF_seek(sc->ofwd_instance, bp->bio_offset);
82
83	switch (bp->bio_cmd) {
84	case BIO_READ:
85		r = OF_read(sc->ofwd_instance, (void *)bp->bio_data,
86		   bp->bio_length);
87		break;
88	case BIO_WRITE:
89		r = OF_write(sc->ofwd_instance, (void *)bp->bio_data,
90		   bp->bio_length);
91		break;
92	}
93	if (r != bp->bio_length)
94		panic("ofwd: incorrect i/o count");
95
96	bp->bio_resid = 0;
97	return (0);
98}
99
100static void
101ofwd_kthread(void *arg)
102{
103	struct ofwd_softc *sc;
104	struct bio *bp;
105	int error;
106
107	sc = arg;
108	curthread->td_base_pri = PRIBIO;
109
110	for (;;) {
111		mtx_lock(&sc->ofwd_queue_mtx);
112		bp = bioq_takefirst(&sc->ofwd_bio_queue);
113		if (!bp) {
114			msleep(sc, &sc->ofwd_queue_mtx, PRIBIO | PDROP,
115			    "ofwdwait", 0);
116			continue;
117		}
118		mtx_unlock(&sc->ofwd_queue_mtx);
119		if (bp->bio_cmd == BIO_GETATTR) {
120			error = EOPNOTSUPP;
121		} else
122			error = ofwd_startio(sc, bp);
123
124		if (error != -1) {
125			bp->bio_completed = bp->bio_length;
126			g_io_deliver(bp, error);
127		}
128	}
129}
130
131static void
132g_ofwd_init(struct g_class *mp __unused)
133{
134	char path[128];
135	char fname[32];
136	phandle_t ofd;
137	struct ofwd_softc *sc;
138	struct g_geom *gp;
139	struct g_provider *pp;
140	ihandle_t ifd;
141	int error;
142
143	if (ofwd_enable == 0)
144		return;
145
146	ofd = OF_finddevice("ofwdisk");
147	if (ofd == -1)
148		return;
149
150	bzero(path, 128);
151	OF_package_to_path(ofd, path, 128);
152	OF_getprop(ofd, "file", fname, sizeof(fname));
153	printf("ofw_disk located at %s, file %s\n", path, fname);
154	ifd = OF_open(path);
155	if (ifd == -1) {
156		printf("ofw_disk: could not create instance\n");
157		return;
158	}
159
160	sc = (struct ofwd_softc *)malloc(sizeof *sc, M_DEVBUF,
161	    M_WAITOK | M_ZERO);
162	bioq_init(&sc->ofwd_bio_queue);
163	mtx_init(&sc->ofwd_queue_mtx, "ofwd bio queue", NULL, MTX_DEF);
164	sc->ofwd_instance = ifd;
165	sc->ofwd_mediasize = (off_t)2 * 33554432;
166	sc->ofwd_sectorsize = OFWD_BLOCKSIZE;
167	sc->ofwd_fwsectors = 0;
168	sc->ofwd_fwheads = 0;
169	error = kproc_create(ofwd_kthread, sc, &sc->ofwd_procp, 0, 0,
170	    "ofwd0");
171	if (error != 0) {
172		free(sc, M_DEVBUF);
173		return;
174	}
175
176	gp = g_new_geomf(&g_ofwd_class, "ofwd0");
177	gp->softc = sc;
178	pp = g_new_providerf(gp, "ofwd0");
179	pp->mediasize = sc->ofwd_mediasize;
180	pp->sectorsize = sc->ofwd_sectorsize;
181	sc->ofwd_gp = gp;
182	sc->ofwd_pp = pp;
183	g_error_provider(pp, 0);
184}
185
186static void
187g_ofwd_start(struct bio *bp)
188{
189	struct ofwd_softc *sc;
190
191	sc = bp->bio_to->geom->softc;
192	mtx_lock(&sc->ofwd_queue_mtx);
193	bioq_disksort(&sc->ofwd_bio_queue, bp);
194	mtx_unlock(&sc->ofwd_queue_mtx);
195	wakeup(sc);
196}
197
198static int
199g_ofwd_access(struct g_provider *pp, int r, int w, int e)
200{
201
202	if (pp->geom->softc == NULL)
203		return (ENXIO);
204	return (0);
205}
206