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