virtio_block.c revision 275048
1275048Sbr/*-
2275048Sbr * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
3275048Sbr * All rights reserved.
4275048Sbr *
5275048Sbr * This software was developed by SRI International and the University of
6275048Sbr * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
7275048Sbr * ("CTSRD"), as part of the DARPA CRASH research programme.
8275048Sbr *
9275048Sbr * Redistribution and use in source and binary forms, with or without
10275048Sbr * modification, are permitted provided that the following conditions
11275048Sbr * are met:
12275048Sbr * 1. Redistributions of source code must retain the above copyright
13275048Sbr *    notice, this list of conditions and the following disclaimer.
14275048Sbr * 2. Redistributions in binary form must reproduce the above copyright
15275048Sbr *    notice, this list of conditions and the following disclaimer in the
16275048Sbr *    documentation and/or other materials provided with the distribution.
17275048Sbr *
18275048Sbr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19275048Sbr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20275048Sbr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21275048Sbr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22275048Sbr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23275048Sbr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24275048Sbr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25275048Sbr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26275048Sbr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27275048Sbr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28275048Sbr * SUCH DAMAGE.
29275048Sbr */
30275048Sbr
31275048Sbr/*
32275048Sbr * BERI virtio block backend driver
33275048Sbr */
34275048Sbr
35275048Sbr#include <sys/cdefs.h>
36275048Sbr__FBSDID("$FreeBSD: head/sys/dev/beri/virtio/virtio_block.c 275048 2014-11-25 15:58:59Z br $");
37275048Sbr
38275048Sbr#include <sys/param.h>
39275048Sbr#include <sys/systm.h>
40275048Sbr#include <sys/bus.h>
41275048Sbr#include <sys/kernel.h>
42275048Sbr#include <sys/module.h>
43275048Sbr#include <sys/rman.h>
44275048Sbr#include <sys/conf.h>
45275048Sbr#include <sys/stat.h>
46275048Sbr#include <sys/endian.h>
47275048Sbr#include <sys/disk.h>
48275048Sbr#include <sys/vnode.h>
49275048Sbr#include <sys/fcntl.h>
50275048Sbr#include <sys/kthread.h>
51275048Sbr#include <sys/buf.h>
52275048Sbr#include <sys/mdioctl.h>
53275048Sbr#include <sys/namei.h>
54275048Sbr
55275048Sbr#include <machine/bus.h>
56275048Sbr#include <machine/fdt.h>
57275048Sbr#include <machine/cpu.h>
58275048Sbr#include <machine/intr.h>
59275048Sbr
60275048Sbr#include <dev/fdt/fdt_common.h>
61275048Sbr#include <dev/ofw/openfirm.h>
62275048Sbr#include <dev/ofw/ofw_bus.h>
63275048Sbr#include <dev/ofw/ofw_bus_subr.h>
64275048Sbr
65275048Sbr#include <dev/beri/virtio/virtio.h>
66275048Sbr#include <dev/beri/virtio/virtio_mmio_platform.h>
67275048Sbr#include <dev/altera/pio/pio.h>
68275048Sbr#include <dev/virtio/mmio/virtio_mmio.h>
69275048Sbr#include <dev/virtio/block/virtio_blk.h>
70275048Sbr#include <dev/virtio/virtio.h>
71275048Sbr#include <dev/virtio/virtio_ring.h>
72275048Sbr
73275048Sbr#include "pio_if.h"
74275048Sbr
75275048Sbr#define DPRINTF(fmt, ...)
76275048Sbr
77275048Sbrstruct beri_vtblk_softc {
78275048Sbr	struct resource		*res[1];
79275048Sbr	bus_space_tag_t		bst;
80275048Sbr	bus_space_handle_t	bsh;
81275048Sbr	struct cdev		*cdev;
82275048Sbr	device_t		dev;
83275048Sbr	int			opened;
84275048Sbr	device_t		pio_recv;
85275048Sbr	device_t		pio_send;
86275048Sbr	struct vqueue_info	vs_queues[NUM_QUEUES];
87275048Sbr	char			ident[VTBLK_BLK_ID_BYTES];
88275048Sbr	struct ucred		*cred;
89275048Sbr	struct vnode		*vnode;
90275048Sbr	struct thread		*vtblk_ktd;
91275048Sbr	struct sx		sc_mtx;
92275048Sbr	int			beri_mem_offset;
93275048Sbr	struct md_ioctl		*mdio;
94275048Sbr	struct virtio_blk_config *cfg;
95275048Sbr};
96275048Sbr
97275048Sbrstatic struct resource_spec beri_spec[] = {
98275048Sbr	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
99275048Sbr	{ -1, 0 }
100275048Sbr};
101275048Sbr
102275048Sbrstatic int
103275048Sbrvtblk_rdwr(struct beri_vtblk_softc *sc, struct iovec *iov,
104275048Sbr	int cnt, int offset, int operation, int iolen)
105275048Sbr{
106275048Sbr	struct vnode *vp;
107275048Sbr	struct mount *mp;
108275048Sbr	struct uio auio;
109275048Sbr	int error;
110275048Sbr
111275048Sbr	bzero(&auio, sizeof(auio));
112275048Sbr
113275048Sbr	vp = sc->vnode;
114275048Sbr
115275048Sbr	KASSERT(vp != NULL, ("file not opened"));
116275048Sbr
117275048Sbr	auio.uio_iov = iov;
118275048Sbr	auio.uio_iovcnt = cnt;
119275048Sbr	auio.uio_offset = offset;
120275048Sbr	auio.uio_segflg = UIO_SYSSPACE;
121275048Sbr	auio.uio_rw = operation;
122275048Sbr	auio.uio_resid = iolen;
123275048Sbr	auio.uio_td = curthread;
124275048Sbr
125275048Sbr	if (operation == 0) {
126275048Sbr		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
127275048Sbr		error = VOP_READ(vp, &auio, IO_DIRECT, sc->cred);
128275048Sbr		VOP_UNLOCK(vp, 0);
129275048Sbr	} else {
130275048Sbr		(void) vn_start_write(vp, &mp, V_WAIT);
131275048Sbr		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
132275048Sbr		error = VOP_WRITE(vp, &auio, IO_SYNC, sc->cred);
133275048Sbr		VOP_UNLOCK(vp, 0);
134275048Sbr		vn_finished_write(mp);
135275048Sbr	}
136275048Sbr
137275048Sbr	return (error);
138275048Sbr}
139275048Sbr
140275048Sbrstatic void
141275048Sbrvtblk_proc(struct beri_vtblk_softc *sc, struct vqueue_info *vq)
142275048Sbr{
143275048Sbr	struct iovec iov[VTBLK_MAXSEGS + 2];
144275048Sbr	uint16_t flags[VTBLK_MAXSEGS + 2];
145275048Sbr	struct virtio_blk_outhdr *vbh;
146275048Sbr	uint8_t *status;
147275048Sbr	off_t offset;
148275048Sbr	int iolen;
149275048Sbr	int type;
150275048Sbr	int i, n;
151275048Sbr	int err;
152275048Sbr
153275048Sbr	n = vq_getchain(sc->beri_mem_offset, vq, iov,
154275048Sbr		VTBLK_MAXSEGS + 2, flags);
155275048Sbr
156275048Sbr	KASSERT(n >= 2 && n <= VTBLK_MAXSEGS + 2,
157275048Sbr		("wrong n value %d", n));
158275048Sbr
159275048Sbr	vbh = iov[0].iov_base;
160275048Sbr
161275048Sbr	status = iov[n-1].iov_base;
162275048Sbr	KASSERT(iov[n-1].iov_len == 1,
163275048Sbr		("iov_len == %d", iov[n-1].iov_len));
164275048Sbr
165275048Sbr	type = be32toh(vbh->type) & ~VIRTIO_BLK_T_BARRIER;
166275048Sbr	offset = be64toh(vbh->sector) * DEV_BSIZE;
167275048Sbr
168275048Sbr	iolen = 0;
169275048Sbr	for (i = 1; i < (n-1); i++) {
170275048Sbr		iolen += iov[i].iov_len;
171275048Sbr	}
172275048Sbr
173275048Sbr	switch (type) {
174275048Sbr	case VIRTIO_BLK_T_OUT:
175275048Sbr	case VIRTIO_BLK_T_IN:
176275048Sbr		err = vtblk_rdwr(sc, iov + 1, i - 1,
177275048Sbr			offset, type, iolen);
178275048Sbr		break;
179275048Sbr	case VIRTIO_BLK_T_GET_ID:
180275048Sbr		/* Assume a single buffer */
181275048Sbr		strlcpy(iov[1].iov_base, sc->ident,
182275048Sbr		    MIN(iov[1].iov_len, sizeof(sc->ident)));
183275048Sbr		err = 0;
184275048Sbr		break;
185275048Sbr	case VIRTIO_BLK_T_FLUSH:
186275048Sbr		/* Possible? */
187275048Sbr	default:
188275048Sbr		err = -ENOSYS;
189275048Sbr		break;
190275048Sbr	}
191275048Sbr
192275048Sbr	if (err < 0) {
193275048Sbr		if (err == -ENOSYS) {
194275048Sbr			*status = VIRTIO_BLK_S_UNSUPP;
195275048Sbr		} else
196275048Sbr			*status = VIRTIO_BLK_S_IOERR;
197275048Sbr	} else
198275048Sbr		*status = VIRTIO_BLK_S_OK;
199275048Sbr
200275048Sbr	vq_relchain(vq, iov, n, 1);
201275048Sbr}
202275048Sbr
203275048Sbrstatic int
204275048Sbrclose_file(struct beri_vtblk_softc *sc, struct thread *td)
205275048Sbr{
206275048Sbr	int error;
207275048Sbr
208275048Sbr	if (sc->vnode != NULL) {
209275048Sbr		vn_lock(sc->vnode, LK_EXCLUSIVE | LK_RETRY);
210275048Sbr		sc->vnode->v_vflag &= ~VV_MD;
211275048Sbr		VOP_UNLOCK(sc->vnode, 0);
212275048Sbr		error = vn_close(sc->vnode, (FREAD|FWRITE),
213275048Sbr				sc->cred, td);
214275048Sbr		if (error != 0)
215275048Sbr			return (error);
216275048Sbr		sc->vnode = NULL;
217275048Sbr	}
218275048Sbr
219275048Sbr	if (sc->cred != NULL)
220275048Sbr		crfree(sc->cred);
221275048Sbr
222275048Sbr	return (0);
223275048Sbr}
224275048Sbr
225275048Sbrstatic int
226275048Sbropen_file(struct beri_vtblk_softc *sc, struct thread *td)
227275048Sbr{
228275048Sbr	struct nameidata nd;
229275048Sbr	struct vattr vattr;
230275048Sbr	int error;
231275048Sbr	int flags;
232275048Sbr
233275048Sbr	flags = (FREAD | FWRITE);
234275048Sbr	NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE,
235275048Sbr		sc->mdio->md_file, td);
236275048Sbr	error = vn_open(&nd, &flags, 0, NULL);
237275048Sbr	if (error != 0)
238275048Sbr		return (error);
239275048Sbr	NDFREE(&nd, NDF_ONLY_PNBUF);
240275048Sbr
241275048Sbr	if (nd.ni_vp->v_type != VREG) {
242275048Sbr		return (EINVAL);
243275048Sbr	}
244275048Sbr
245275048Sbr	error = VOP_GETATTR(nd.ni_vp, &vattr, td->td_ucred);
246275048Sbr	if (error != 0)
247275048Sbr		return (error);
248275048Sbr
249275048Sbr	if (VOP_ISLOCKED(nd.ni_vp) != LK_EXCLUSIVE) {
250275048Sbr		vn_lock(nd.ni_vp, LK_UPGRADE | LK_RETRY);
251275048Sbr		if (nd.ni_vp->v_iflag & VI_DOOMED) {
252275048Sbr			return (1);
253275048Sbr		}
254275048Sbr	}
255275048Sbr	nd.ni_vp->v_vflag |= VV_MD;
256275048Sbr	VOP_UNLOCK(nd.ni_vp, 0);
257275048Sbr
258275048Sbr	sc->vnode = nd.ni_vp;
259275048Sbr	sc->cred = crhold(td->td_ucred);
260275048Sbr
261275048Sbr	return (0);
262275048Sbr}
263275048Sbr
264275048Sbrstatic int
265275048Sbrvtblk_notify(struct beri_vtblk_softc *sc)
266275048Sbr{
267275048Sbr	struct vqueue_info *vq;
268275048Sbr	int queue;
269275048Sbr	int reg;
270275048Sbr
271275048Sbr	vq = &sc->vs_queues[0];
272275048Sbr	if (!vq_ring_ready(vq))
273275048Sbr		return (0);
274275048Sbr
275275048Sbr	if (!sc->opened)
276275048Sbr		return (0);
277275048Sbr
278275048Sbr	reg = READ2(sc, VIRTIO_MMIO_QUEUE_NOTIFY);
279275048Sbr	queue = be16toh(reg);
280275048Sbr
281275048Sbr	KASSERT(queue == 0, ("we support single queue only"));
282275048Sbr
283275048Sbr	/* Process new descriptors */
284275048Sbr	vq = &sc->vs_queues[queue];
285275048Sbr	vq->vq_save_used = be16toh(vq->vq_used->idx);
286275048Sbr	while (vq_has_descs(vq))
287275048Sbr		vtblk_proc(sc, vq);
288275048Sbr
289275048Sbr	/* Interrupt other side */
290275048Sbr	PIO_SET(sc->pio_send, Q_INTR, 1);
291275048Sbr
292275048Sbr	return (0);
293275048Sbr}
294275048Sbr
295275048Sbrstatic int
296275048Sbrvq_init(struct beri_vtblk_softc *sc)
297275048Sbr{
298275048Sbr	struct vqueue_info *vq;
299275048Sbr	uint8_t *base;
300275048Sbr	int size;
301275048Sbr	int reg;
302275048Sbr	int pfn;
303275048Sbr
304275048Sbr	vq = &sc->vs_queues[0];
305275048Sbr	vq->vq_qsize = NUM_QUEUES;
306275048Sbr
307275048Sbr	reg = READ4(sc, VIRTIO_MMIO_QUEUE_PFN);
308275048Sbr	pfn = be32toh(reg);
309275048Sbr	vq->vq_pfn = pfn;
310275048Sbr
311275048Sbr	size = vring_size(vq->vq_qsize, VRING_ALIGN);
312275048Sbr	base = paddr_map(sc->beri_mem_offset,
313275048Sbr		(pfn << PAGE_SHIFT), size);
314275048Sbr
315275048Sbr	/* First pages are descriptors */
316275048Sbr	vq->vq_desc = (struct vring_desc *)base;
317275048Sbr	base += vq->vq_qsize * sizeof(struct vring_desc);
318275048Sbr
319275048Sbr	/* Then avail ring */
320275048Sbr	vq->vq_avail = (struct vring_avail *)base;
321275048Sbr	base += (2 + vq->vq_qsize + 1) * sizeof(uint16_t);
322275048Sbr
323275048Sbr	/* Then it's rounded up to the next page */
324275048Sbr	base = (uint8_t *)roundup2((uintptr_t)base, VRING_ALIGN);
325275048Sbr
326275048Sbr	/* And the last pages are the used ring */
327275048Sbr	vq->vq_used = (struct vring_used *)base;
328275048Sbr
329275048Sbr	/* Mark queue as allocated, and start at 0 when we use it. */
330275048Sbr	vq->vq_flags = VQ_ALLOC;
331275048Sbr	vq->vq_last_avail = 0;
332275048Sbr
333275048Sbr	return (0);
334275048Sbr}
335275048Sbr
336275048Sbr
337275048Sbrstatic void
338275048Sbrvtblk_thread(void *arg)
339275048Sbr{
340275048Sbr	struct beri_vtblk_softc *sc;
341275048Sbr	int err;
342275048Sbr
343275048Sbr	sc = arg;
344275048Sbr
345275048Sbr	sx_xlock(&sc->sc_mtx);
346275048Sbr	for (;;) {
347275048Sbr		err = msleep(sc, &sc->sc_mtx, PCATCH | PZERO, "prd", hz);
348275048Sbr		vtblk_notify(sc);
349275048Sbr	}
350275048Sbr	sx_xunlock(&sc->sc_mtx);
351275048Sbr
352275048Sbr	kthread_exit();
353275048Sbr}
354275048Sbr
355275048Sbrstatic int
356275048Sbrsetup_pio(struct beri_vtblk_softc *sc, char *name, device_t *dev)
357275048Sbr{
358275048Sbr	phandle_t pio_node;
359275048Sbr	struct fdt_ic *ic;
360275048Sbr	phandle_t xref;
361275048Sbr	phandle_t node;
362275048Sbr
363275048Sbr	if ((node = ofw_bus_get_node(sc->dev)) == -1)
364275048Sbr		return (ENXIO);
365275048Sbr
366275048Sbr	if (OF_searchencprop(node, name, &xref,
367275048Sbr		sizeof(xref)) == -1) {
368275048Sbr		return (ENXIO);
369275048Sbr	}
370275048Sbr
371275048Sbr	pio_node = OF_node_from_xref(xref);
372275048Sbr	SLIST_FOREACH(ic, &fdt_ic_list_head, fdt_ics) {
373275048Sbr		if (ic->iph == pio_node) {
374275048Sbr			*dev = ic->dev;
375275048Sbr			PIO_CONFIGURE(*dev, PIO_OUT_ALL,
376275048Sbr					PIO_UNMASK_ALL);
377275048Sbr			return (0);
378275048Sbr		}
379275048Sbr	}
380275048Sbr
381275048Sbr	return (ENXIO);
382275048Sbr}
383275048Sbr
384275048Sbrstatic int
385275048Sbrsetup_offset(struct beri_vtblk_softc *sc)
386275048Sbr{
387275048Sbr	pcell_t dts_value[2];
388275048Sbr	phandle_t mem_node;
389275048Sbr	phandle_t xref;
390275048Sbr	phandle_t node;
391275048Sbr	int len;
392275048Sbr
393275048Sbr	if ((node = ofw_bus_get_node(sc->dev)) == -1)
394275048Sbr		return (ENXIO);
395275048Sbr
396275048Sbr	if (OF_searchencprop(node, "beri-mem", &xref,
397275048Sbr		sizeof(xref)) == -1) {
398275048Sbr		return (ENXIO);
399275048Sbr	}
400275048Sbr
401275048Sbr	mem_node = OF_node_from_xref(xref);
402275048Sbr	if ((len = OF_getproplen(mem_node, "reg")) <= 0)
403275048Sbr		return (ENXIO);
404275048Sbr	OF_getencprop(mem_node, "reg", dts_value, len);
405275048Sbr	sc->beri_mem_offset = dts_value[0];
406275048Sbr
407275048Sbr	return (0);
408275048Sbr}
409275048Sbr
410275048Sbrstatic int
411275048Sbrbackend_info(struct beri_vtblk_softc *sc)
412275048Sbr{
413275048Sbr	struct virtio_blk_config *cfg;
414275048Sbr	uint32_t *s;
415275048Sbr	int reg;
416275048Sbr	int i;
417275048Sbr
418275048Sbr	/* Specify that we provide block device */
419275048Sbr	reg = htobe32(VIRTIO_ID_BLOCK);
420275048Sbr	WRITE4(sc, VIRTIO_MMIO_DEVICE_ID, reg);
421275048Sbr
422275048Sbr	/* The number of queues we support */
423275048Sbr	reg = htobe16(NUM_QUEUES);
424275048Sbr	WRITE2(sc, VIRTIO_MMIO_QUEUE_NUM, reg);
425275048Sbr
426275048Sbr	/* Our features */
427275048Sbr	reg = htobe32(VIRTIO_RING_F_INDIRECT_DESC
428275048Sbr	    | VIRTIO_BLK_F_BLK_SIZE
429275048Sbr	    | VIRTIO_BLK_F_SEG_MAX);
430275048Sbr	WRITE4(sc, VIRTIO_MMIO_HOST_FEATURES, reg);
431275048Sbr
432275048Sbr	cfg = sc->cfg;
433275048Sbr	cfg->capacity = htobe64(sc->mdio->md_mediasize / DEV_BSIZE);
434275048Sbr	cfg->size_max = 0; /* not negotiated */
435275048Sbr	cfg->seg_max = htobe32(VTBLK_MAXSEGS);
436275048Sbr	cfg->blk_size = htobe32(DEV_BSIZE);
437275048Sbr
438275048Sbr	s = (uint32_t *)cfg;
439275048Sbr
440275048Sbr	for (i = 0; i < sizeof(struct virtio_blk_config); i+=4) {
441275048Sbr		WRITE4(sc, VIRTIO_MMIO_CONFIG + i, *s);
442275048Sbr		s+=1;
443275048Sbr	}
444275048Sbr
445275048Sbr	sprintf(sc->ident, "Virtio block backend");
446275048Sbr
447275048Sbr	return (0);
448275048Sbr}
449275048Sbr
450275048Sbrstatic void
451275048Sbrvtblk_intr(void *arg)
452275048Sbr{
453275048Sbr	struct beri_vtblk_softc *sc;
454275048Sbr	int pending;
455275048Sbr	int reg;
456275048Sbr
457275048Sbr	sc = arg;
458275048Sbr
459275048Sbr	reg = PIO_READ(sc->pio_recv);
460275048Sbr
461275048Sbr	/* Ack */
462275048Sbr	PIO_SET(sc->pio_recv, reg, 0);
463275048Sbr
464275048Sbr	pending = htobe32(reg);
465275048Sbr
466275048Sbr	if (pending & Q_PFN) {
467275048Sbr		vq_init(sc);
468275048Sbr	}
469275048Sbr
470275048Sbr	if (pending & Q_NOTIFY) {
471275048Sbr		wakeup(sc);
472275048Sbr	}
473275048Sbr}
474275048Sbr
475275048Sbrstatic int
476275048Sbrberi_ioctl(struct cdev *dev, u_long cmd, caddr_t addr,
477275048Sbr		int flags, struct thread *td)
478275048Sbr{
479275048Sbr	struct beri_vtblk_softc *sc;
480275048Sbr	int err;
481275048Sbr
482275048Sbr	sc = dev->si_drv1;
483275048Sbr
484275048Sbr	switch (cmd) {
485275048Sbr	case MDIOCATTACH:
486275048Sbr		/* take file as argument */
487275048Sbr		if (sc->vnode != NULL) {
488275048Sbr			/* Already opened */
489275048Sbr			return (1);
490275048Sbr		}
491275048Sbr		sc->mdio = (struct md_ioctl *)addr;
492275048Sbr		backend_info(sc);
493275048Sbr		DPRINTF("opening file, td 0x%08x\n", (int)td);
494275048Sbr		err = open_file(sc, td);
495275048Sbr		if (err)
496275048Sbr			return (err);
497275048Sbr		PIO_SETUP_IRQ(sc->pio_recv, vtblk_intr, sc);
498275048Sbr		sc->opened = 1;
499275048Sbr		break;
500275048Sbr	case MDIOCDETACH:
501275048Sbr		if (sc->vnode == 0) {
502275048Sbr			/* File not opened */
503275048Sbr			return (1);
504275048Sbr		}
505275048Sbr		sc->opened = 0;
506275048Sbr		DPRINTF("closing file, td 0x%08x\n", (int)td);
507275048Sbr		err = close_file(sc, td);
508275048Sbr		if (err)
509275048Sbr			return (err);
510275048Sbr		PIO_TEARDOWN_IRQ(sc->pio_recv);
511275048Sbr		break;
512275048Sbr	default:
513275048Sbr		break;
514275048Sbr	}
515275048Sbr
516275048Sbr	return (0);
517275048Sbr}
518275048Sbr
519275048Sbrstatic struct cdevsw beri_cdevsw = {
520275048Sbr	.d_version =	D_VERSION,
521275048Sbr	.d_ioctl =	beri_ioctl,
522275048Sbr	.d_name =	"virtio block backend",
523275048Sbr};
524275048Sbr
525275048Sbrstatic int
526275048Sbrberi_vtblk_probe(device_t dev)
527275048Sbr{
528275048Sbr
529275048Sbr	if (!ofw_bus_status_okay(dev))
530275048Sbr		return (ENXIO);
531275048Sbr
532275048Sbr	if (!ofw_bus_is_compatible(dev, "sri-cambridge,beri-vtblk"))
533275048Sbr		return (ENXIO);
534275048Sbr
535275048Sbr	device_set_desc(dev, "SRI-Cambridge BERI block");
536275048Sbr	return (BUS_PROBE_DEFAULT);
537275048Sbr}
538275048Sbr
539275048Sbrstatic int
540275048Sbrberi_vtblk_attach(device_t dev)
541275048Sbr{
542275048Sbr	struct beri_vtblk_softc *sc;
543275048Sbr	int error;
544275048Sbr
545275048Sbr	sc = device_get_softc(dev);
546275048Sbr	sc->dev = dev;
547275048Sbr
548275048Sbr	if (bus_alloc_resources(dev, beri_spec, sc->res)) {
549275048Sbr		device_printf(dev, "could not allocate resources\n");
550275048Sbr		return (ENXIO);
551275048Sbr	}
552275048Sbr
553275048Sbr	/* Memory interface */
554275048Sbr	sc->bst = rman_get_bustag(sc->res[0]);
555275048Sbr	sc->bsh = rman_get_bushandle(sc->res[0]);
556275048Sbr
557275048Sbr	sc->cfg = malloc(sizeof(struct virtio_blk_config),
558275048Sbr		M_DEVBUF, M_NOWAIT|M_ZERO);
559275048Sbr
560275048Sbr	sx_init(&sc->sc_mtx, device_get_nameunit(sc->dev));
561275048Sbr
562275048Sbr	error = kthread_add(vtblk_thread, sc, NULL, &sc->vtblk_ktd,
563275048Sbr		0, 0, "beri_virtio_block");
564275048Sbr	if (error) {
565275048Sbr		device_printf(dev, "cannot create kthread\n");
566275048Sbr		return (ENXIO);
567275048Sbr	}
568275048Sbr
569275048Sbr	if (setup_offset(sc) != 0)
570275048Sbr		return (ENXIO);
571275048Sbr	if (setup_pio(sc, "pio-send", &sc->pio_send) != 0)
572275048Sbr		return (ENXIO);
573275048Sbr	if (setup_pio(sc, "pio-recv", &sc->pio_recv) != 0)
574275048Sbr		return (ENXIO);
575275048Sbr
576275048Sbr	sc->cdev = make_dev(&beri_cdevsw, 0, UID_ROOT, GID_WHEEL,
577275048Sbr	    S_IRWXU, "beri_vtblk");
578275048Sbr	if (sc->cdev == NULL) {
579275048Sbr		device_printf(dev, "Failed to create character device.\n");
580275048Sbr		return (ENXIO);
581275048Sbr	}
582275048Sbr
583275048Sbr	sc->cdev->si_drv1 = sc;
584275048Sbr	return (0);
585275048Sbr}
586275048Sbr
587275048Sbrstatic device_method_t beri_vtblk_methods[] = {
588275048Sbr	DEVMETHOD(device_probe,		beri_vtblk_probe),
589275048Sbr	DEVMETHOD(device_attach,	beri_vtblk_attach),
590275048Sbr	{ 0, 0 }
591275048Sbr};
592275048Sbr
593275048Sbrstatic driver_t beri_vtblk_driver = {
594275048Sbr	"beri_vtblk",
595275048Sbr	beri_vtblk_methods,
596275048Sbr	sizeof(struct beri_vtblk_softc),
597275048Sbr};
598275048Sbr
599275048Sbrstatic devclass_t beri_vtblk_devclass;
600275048Sbr
601275048SbrDRIVER_MODULE(beri_vtblk, simplebus, beri_vtblk_driver,
602275048Sbr    beri_vtblk_devclass, 0, 0);
603