1139749Simp/*-
265942Sgibbs * Copyright (C) 2002 Benno Rice <benno@FreeBSD.org>
365942Sgibbs * All rights reserved.
471717Sgibbs *
595378Sgibbs * Redistribution and use in source and binary forms, with or without
665942Sgibbs * modification, are permitted provided that the following conditions
765942Sgibbs * are met:
865942Sgibbs * 1. Redistributions of source code must retain the above copyright
965942Sgibbs *    notice, this list of conditions and the following disclaimer.
1065942Sgibbs * 2. Redistributions in binary form must reproduce the above copyright
1165942Sgibbs *    notice, this list of conditions and the following disclaimer in the
1265942Sgibbs *    documentation and/or other materials provided with the distribution.
1365942Sgibbs *
1495378Sgibbs * THIS SOFTWARE IS PROVIDED BY Benno Rice ``AS IS'' AND ANY EXPRESS OR
1595378Sgibbs * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1695378Sgibbs * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1795378Sgibbs * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
1895378Sgibbs * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
1995378Sgibbs * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
2095378Sgibbs * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
2195378Sgibbs * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
2265942Sgibbs * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
2365942Sgibbs * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2495378Sgibbs */
2595378Sgibbs
2665942Sgibbs#include <sys/cdefs.h>
2795378Sgibbs__FBSDID("$FreeBSD$");
2895378Sgibbs
2995378Sgibbs#include <sys/param.h>
3095378Sgibbs#include <sys/systm.h>
3195378Sgibbs#include <sys/bio.h>
3295378Sgibbs#include <sys/kernel.h>
3365942Sgibbs#include <sys/kthread.h>
3465942Sgibbs#include <sys/linker.h>
3595378Sgibbs#include <sys/lock.h>
3695378Sgibbs#include <sys/malloc.h>
3795378Sgibbs#include <sys/mutex.h>
3895378Sgibbs#include <sys/proc.h>
3965942Sgibbs
40123579Sgibbs#include <geom/geom.h>
4165942Sgibbs
4265942Sgibbs#include <dev/ofw/openfirm.h>
4365942Sgibbs
4465942Sgibbs#define	OFWD_BLOCKSIZE	512
4565942Sgibbs
4665942Sgibbsstruct ofwd_softc
4765942Sgibbs{
4865942Sgibbs	struct bio_queue_head ofwd_bio_queue;
4979874Sgibbs	struct mtx	ofwd_queue_mtx;
5074094Sgibbs	ihandle_t	ofwd_instance;
5174094Sgibbs	off_t		ofwd_mediasize;
5274094Sgibbs	unsigned	ofwd_sectorsize;
5365942Sgibbs	unsigned	ofwd_fwheads;
5465942Sgibbs	unsigned	ofwd_fwsectors;
5565942Sgibbs	struct proc	*ofwd_procp;
5665942Sgibbs	struct g_geom	*ofwd_gp;
5765942Sgibbs	struct g_provider *ofwd_pp;
5865942Sgibbs} ofwd_softc;
5965942Sgibbs
6065942Sgibbsstatic g_init_t g_ofwd_init;
6165942Sgibbsstatic g_start_t g_ofwd_start;
6265942Sgibbsstatic g_access_t g_ofwd_access;
6365942Sgibbs
6465942Sgibbsstruct g_class g_ofwd_class = {
6565942Sgibbs	.name = "OFWD",
6665942Sgibbs	.version = G_VERSION,
6765942Sgibbs	.init = g_ofwd_init,
6865942Sgibbs	.start = g_ofwd_start,
6965942Sgibbs	.access = g_ofwd_access,
7065942Sgibbs};
7165942Sgibbs
7265942SgibbsDECLARE_GEOM_CLASS(g_ofwd_class, g_ofwd);
7365942Sgibbs
7474094Sgibbsstatic int ofwd_enable = 0;
7565942SgibbsTUNABLE_INT("kern.ofw.disk", &ofwd_enable);
7665942Sgibbs
7765942Sgibbsstatic int
7865942Sgibbsofwd_startio(struct ofwd_softc *sc, struct bio *bp)
7965942Sgibbs{
8065942Sgibbs	u_int r;
8165942Sgibbs
8265942Sgibbs	r = OF_seek(sc->ofwd_instance, bp->bio_offset);
8365942Sgibbs
8465942Sgibbs	switch (bp->bio_cmd) {
8565942Sgibbs	case BIO_READ:
8665942Sgibbs		r = OF_read(sc->ofwd_instance, (void *)bp->bio_data,
8774094Sgibbs		   bp->bio_length);
8865942Sgibbs		break;
8965942Sgibbs	case BIO_WRITE:
9065942Sgibbs		r = OF_write(sc->ofwd_instance, (void *)bp->bio_data,
9165942Sgibbs		   bp->bio_length);
9265942Sgibbs		break;
9365942Sgibbs	}
9465942Sgibbs	if (r != bp->bio_length)
9574094Sgibbs		panic("ofwd: incorrect i/o count");
9665942Sgibbs
9765942Sgibbs	bp->bio_resid = 0;
9865942Sgibbs	return (0);
9965942Sgibbs}
10065942Sgibbs
10165942Sgibbsstatic void
10265942Sgibbsofwd_kthread(void *arg)
10365942Sgibbs{
10465942Sgibbs	struct ofwd_softc *sc;
10565942Sgibbs	struct bio *bp;
10665942Sgibbs	int error;
10765942Sgibbs
10865942Sgibbs	sc = arg;
10965942Sgibbs	curthread->td_base_pri = PRIBIO;
11065942Sgibbs
11165942Sgibbs	for (;;) {
11274094Sgibbs		mtx_lock(&sc->ofwd_queue_mtx);
11365942Sgibbs		bp = bioq_takefirst(&sc->ofwd_bio_queue);
11465942Sgibbs		if (!bp) {
11565942Sgibbs			msleep(sc, &sc->ofwd_queue_mtx, PRIBIO | PDROP,
11665942Sgibbs			    "ofwdwait", 0);
11765942Sgibbs			continue;
11865942Sgibbs		}
11965942Sgibbs		mtx_unlock(&sc->ofwd_queue_mtx);
12065942Sgibbs		if (bp->bio_cmd == BIO_GETATTR) {
12165942Sgibbs			error = EOPNOTSUPP;
12265942Sgibbs		} else
12365942Sgibbs			error = ofwd_startio(sc, bp);
12465942Sgibbs
12565942Sgibbs		if (error != -1) {
12665942Sgibbs			bp->bio_completed = bp->bio_length;
12765942Sgibbs			g_io_deliver(bp, error);
12865942Sgibbs		}
12971390Sgibbs	}
13065942Sgibbs}
13165942Sgibbs
13265942Sgibbsstatic void
13365942Sgibbsg_ofwd_init(struct g_class *mp __unused)
13465942Sgibbs{
13565942Sgibbs	char path[128];
13665942Sgibbs	char fname[32];
13765942Sgibbs	phandle_t ofd;
13865942Sgibbs	struct ofwd_softc *sc;
13965942Sgibbs	struct g_geom *gp;
14065942Sgibbs	struct g_provider *pp;
14165942Sgibbs	ihandle_t ifd;
14271390Sgibbs	int error;
14365942Sgibbs
14465942Sgibbs	if (ofwd_enable == 0)
14565942Sgibbs		return;
14665942Sgibbs
14765942Sgibbs	ofd = OF_finddevice("ofwdisk");
14865942Sgibbs	if (ofd == -1)
14965942Sgibbs		return;
15065942Sgibbs
15165942Sgibbs	bzero(path, 128);
15265942Sgibbs	OF_package_to_path(ofd, path, 128);
15365942Sgibbs	OF_getprop(ofd, "file", fname, sizeof(fname));
15465942Sgibbs	printf("ofw_disk located at %s, file %s\n", path, fname);
15565942Sgibbs	ifd = OF_open(path);
15665942Sgibbs	if (ifd == -1) {
15765942Sgibbs		printf("ofw_disk: could not create instance\n");
15879874Sgibbs		return;
15979874Sgibbs	}
16079874Sgibbs
16179874Sgibbs	sc = (struct ofwd_softc *)malloc(sizeof *sc, M_DEVBUF,
16279874Sgibbs	    M_WAITOK | M_ZERO);
16379874Sgibbs	bioq_init(&sc->ofwd_bio_queue);
16479874Sgibbs	mtx_init(&sc->ofwd_queue_mtx, "ofwd bio queue", NULL, MTX_DEF);
16565942Sgibbs	sc->ofwd_instance = ifd;
16665942Sgibbs	sc->ofwd_mediasize = (off_t)2 * 33554432;
16765942Sgibbs	sc->ofwd_sectorsize = OFWD_BLOCKSIZE;
16865942Sgibbs	sc->ofwd_fwsectors = 0;
16965942Sgibbs	sc->ofwd_fwheads = 0;
17065942Sgibbs	error = kproc_create(ofwd_kthread, sc, &sc->ofwd_procp, 0, 0,
17165942Sgibbs	    "ofwd0");
17265942Sgibbs	if (error != 0) {
17365942Sgibbs		free(sc, M_DEVBUF);
17465942Sgibbs		return;
17565942Sgibbs	}
17665942Sgibbs
17765942Sgibbs	gp = g_new_geomf(&g_ofwd_class, "ofwd0");
17865942Sgibbs	gp->softc = sc;
17965942Sgibbs	pp = g_new_providerf(gp, "ofwd0");
18065942Sgibbs	pp->mediasize = sc->ofwd_mediasize;
18165942Sgibbs	pp->sectorsize = sc->ofwd_sectorsize;
18265942Sgibbs	sc->ofwd_gp = gp;
18365942Sgibbs	sc->ofwd_pp = pp;
18465942Sgibbs	g_error_provider(pp, 0);
18565942Sgibbs}
18665942Sgibbs
18765942Sgibbsstatic void
18865942Sgibbsg_ofwd_start(struct bio *bp)
18965942Sgibbs{
19065942Sgibbs	struct ofwd_softc *sc;
19165942Sgibbs
19265942Sgibbs	sc = bp->bio_to->geom->softc;
19365942Sgibbs	mtx_lock(&sc->ofwd_queue_mtx);
19465942Sgibbs	bioq_disksort(&sc->ofwd_bio_queue, bp);
19565942Sgibbs	mtx_unlock(&sc->ofwd_queue_mtx);
19679874Sgibbs	wakeup(sc);
19779874Sgibbs}
19879874Sgibbs
199123579Sgibbsstatic int
20079874Sgibbsg_ofwd_access(struct g_provider *pp, int r, int w, int e)
20179874Sgibbs{
20279874Sgibbs
20379874Sgibbs	if (pp->geom->softc == NULL)
20479874Sgibbs		return (ENXIO);
20579874Sgibbs	return (0);
20679874Sgibbs}
20779874Sgibbs