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: stable/11/sys/dev/beri/virtio/virtio_block.c 315221 2017-03-14 02:06:03Z pfg $");
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>
70276354Sbryanv#include <dev/virtio/virtio_ids.h>
71276354Sbryanv#include <dev/virtio/virtio_config.h>
72275048Sbr#include <dev/virtio/virtio_ring.h>
73275048Sbr
74275048Sbr#include "pio_if.h"
75275048Sbr
76275048Sbr#define DPRINTF(fmt, ...)
77275048Sbr
78275647Sbr/* We use indirect descriptors */
79275647Sbr#define	NUM_DESCS	1
80275647Sbr#define	NUM_QUEUES	1
81275647Sbr
82275647Sbr#define	VTBLK_BLK_ID_BYTES	20
83275647Sbr#define	VTBLK_MAXSEGS		256
84275647Sbr
85275048Sbrstruct beri_vtblk_softc {
86275048Sbr	struct resource		*res[1];
87275048Sbr	bus_space_tag_t		bst;
88275048Sbr	bus_space_handle_t	bsh;
89275048Sbr	struct cdev		*cdev;
90275048Sbr	device_t		dev;
91275048Sbr	int			opened;
92275048Sbr	device_t		pio_recv;
93275048Sbr	device_t		pio_send;
94275048Sbr	struct vqueue_info	vs_queues[NUM_QUEUES];
95275048Sbr	char			ident[VTBLK_BLK_ID_BYTES];
96275048Sbr	struct ucred		*cred;
97275048Sbr	struct vnode		*vnode;
98275048Sbr	struct thread		*vtblk_ktd;
99275048Sbr	struct sx		sc_mtx;
100275048Sbr	int			beri_mem_offset;
101275048Sbr	struct md_ioctl		*mdio;
102275048Sbr	struct virtio_blk_config *cfg;
103275048Sbr};
104275048Sbr
105275048Sbrstatic struct resource_spec beri_spec[] = {
106275048Sbr	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
107275048Sbr	{ -1, 0 }
108275048Sbr};
109275048Sbr
110275048Sbrstatic int
111275048Sbrvtblk_rdwr(struct beri_vtblk_softc *sc, struct iovec *iov,
112275048Sbr	int cnt, int offset, int operation, int iolen)
113275048Sbr{
114275048Sbr	struct vnode *vp;
115275048Sbr	struct mount *mp;
116275048Sbr	struct uio auio;
117275048Sbr	int error;
118275048Sbr
119275048Sbr	bzero(&auio, sizeof(auio));
120275048Sbr
121275048Sbr	vp = sc->vnode;
122275048Sbr
123275048Sbr	KASSERT(vp != NULL, ("file not opened"));
124275048Sbr
125275048Sbr	auio.uio_iov = iov;
126275048Sbr	auio.uio_iovcnt = cnt;
127275048Sbr	auio.uio_offset = offset;
128275048Sbr	auio.uio_segflg = UIO_SYSSPACE;
129275048Sbr	auio.uio_rw = operation;
130275048Sbr	auio.uio_resid = iolen;
131275048Sbr	auio.uio_td = curthread;
132275048Sbr
133275048Sbr	if (operation == 0) {
134275048Sbr		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
135275048Sbr		error = VOP_READ(vp, &auio, IO_DIRECT, sc->cred);
136275048Sbr		VOP_UNLOCK(vp, 0);
137275048Sbr	} else {
138275048Sbr		(void) vn_start_write(vp, &mp, V_WAIT);
139275048Sbr		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
140275048Sbr		error = VOP_WRITE(vp, &auio, IO_SYNC, sc->cred);
141275048Sbr		VOP_UNLOCK(vp, 0);
142275048Sbr		vn_finished_write(mp);
143275048Sbr	}
144275048Sbr
145275048Sbr	return (error);
146275048Sbr}
147275048Sbr
148275048Sbrstatic void
149275048Sbrvtblk_proc(struct beri_vtblk_softc *sc, struct vqueue_info *vq)
150275048Sbr{
151275048Sbr	struct iovec iov[VTBLK_MAXSEGS + 2];
152275048Sbr	uint16_t flags[VTBLK_MAXSEGS + 2];
153275048Sbr	struct virtio_blk_outhdr *vbh;
154276710Sbr	struct iovec *tiov;
155275048Sbr	uint8_t *status;
156275048Sbr	off_t offset;
157275048Sbr	int iolen;
158275048Sbr	int type;
159275048Sbr	int i, n;
160275048Sbr	int err;
161275048Sbr
162275048Sbr	n = vq_getchain(sc->beri_mem_offset, vq, iov,
163275048Sbr		VTBLK_MAXSEGS + 2, flags);
164275048Sbr	KASSERT(n >= 2 && n <= VTBLK_MAXSEGS + 2,
165275048Sbr		("wrong n value %d", n));
166275048Sbr
167276710Sbr	tiov = getcopy(iov, n);
168275048Sbr	vbh = iov[0].iov_base;
169275048Sbr
170275048Sbr	status = iov[n-1].iov_base;
171275048Sbr	KASSERT(iov[n-1].iov_len == 1,
172275048Sbr		("iov_len == %d", iov[n-1].iov_len));
173275048Sbr
174275048Sbr	type = be32toh(vbh->type) & ~VIRTIO_BLK_T_BARRIER;
175275048Sbr	offset = be64toh(vbh->sector) * DEV_BSIZE;
176275048Sbr
177275048Sbr	iolen = 0;
178275048Sbr	for (i = 1; i < (n-1); i++) {
179275048Sbr		iolen += iov[i].iov_len;
180275048Sbr	}
181275048Sbr
182275048Sbr	switch (type) {
183275048Sbr	case VIRTIO_BLK_T_OUT:
184275048Sbr	case VIRTIO_BLK_T_IN:
185276710Sbr		err = vtblk_rdwr(sc, tiov + 1, i - 1,
186275048Sbr			offset, type, iolen);
187275048Sbr		break;
188275048Sbr	case VIRTIO_BLK_T_GET_ID:
189275048Sbr		/* Assume a single buffer */
190275048Sbr		strlcpy(iov[1].iov_base, sc->ident,
191275048Sbr		    MIN(iov[1].iov_len, sizeof(sc->ident)));
192275048Sbr		err = 0;
193275048Sbr		break;
194275048Sbr	case VIRTIO_BLK_T_FLUSH:
195275048Sbr		/* Possible? */
196275048Sbr	default:
197275048Sbr		err = -ENOSYS;
198275048Sbr		break;
199275048Sbr	}
200275048Sbr
201275048Sbr	if (err < 0) {
202275048Sbr		if (err == -ENOSYS) {
203275048Sbr			*status = VIRTIO_BLK_S_UNSUPP;
204275048Sbr		} else
205275048Sbr			*status = VIRTIO_BLK_S_IOERR;
206275048Sbr	} else
207275048Sbr		*status = VIRTIO_BLK_S_OK;
208275048Sbr
209276710Sbr	free(tiov, M_DEVBUF);
210275048Sbr	vq_relchain(vq, iov, n, 1);
211275048Sbr}
212275048Sbr
213275048Sbrstatic int
214275048Sbrclose_file(struct beri_vtblk_softc *sc, struct thread *td)
215275048Sbr{
216275048Sbr	int error;
217275048Sbr
218275048Sbr	if (sc->vnode != NULL) {
219275048Sbr		vn_lock(sc->vnode, LK_EXCLUSIVE | LK_RETRY);
220275048Sbr		sc->vnode->v_vflag &= ~VV_MD;
221275048Sbr		VOP_UNLOCK(sc->vnode, 0);
222275048Sbr		error = vn_close(sc->vnode, (FREAD|FWRITE),
223275048Sbr				sc->cred, td);
224275048Sbr		if (error != 0)
225275048Sbr			return (error);
226275048Sbr		sc->vnode = NULL;
227275048Sbr	}
228275048Sbr
229275048Sbr	if (sc->cred != NULL)
230275048Sbr		crfree(sc->cred);
231275048Sbr
232275048Sbr	return (0);
233275048Sbr}
234275048Sbr
235275048Sbrstatic int
236275048Sbropen_file(struct beri_vtblk_softc *sc, struct thread *td)
237275048Sbr{
238275048Sbr	struct nameidata nd;
239275048Sbr	struct vattr vattr;
240275048Sbr	int error;
241275048Sbr	int flags;
242275048Sbr
243275048Sbr	flags = (FREAD | FWRITE);
244275048Sbr	NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE,
245275048Sbr		sc->mdio->md_file, td);
246275048Sbr	error = vn_open(&nd, &flags, 0, NULL);
247275048Sbr	if (error != 0)
248275048Sbr		return (error);
249275048Sbr	NDFREE(&nd, NDF_ONLY_PNBUF);
250275048Sbr
251275048Sbr	if (nd.ni_vp->v_type != VREG) {
252275048Sbr		return (EINVAL);
253275048Sbr	}
254275048Sbr
255275048Sbr	error = VOP_GETATTR(nd.ni_vp, &vattr, td->td_ucred);
256275048Sbr	if (error != 0)
257275048Sbr		return (error);
258275048Sbr
259275048Sbr	if (VOP_ISLOCKED(nd.ni_vp) != LK_EXCLUSIVE) {
260275048Sbr		vn_lock(nd.ni_vp, LK_UPGRADE | LK_RETRY);
261275048Sbr		if (nd.ni_vp->v_iflag & VI_DOOMED) {
262275048Sbr			return (1);
263275048Sbr		}
264275048Sbr	}
265275048Sbr	nd.ni_vp->v_vflag |= VV_MD;
266275048Sbr	VOP_UNLOCK(nd.ni_vp, 0);
267275048Sbr
268275048Sbr	sc->vnode = nd.ni_vp;
269275048Sbr	sc->cred = crhold(td->td_ucred);
270275048Sbr
271275048Sbr	return (0);
272275048Sbr}
273275048Sbr
274275048Sbrstatic int
275275048Sbrvtblk_notify(struct beri_vtblk_softc *sc)
276275048Sbr{
277275048Sbr	struct vqueue_info *vq;
278275048Sbr	int queue;
279275048Sbr	int reg;
280275048Sbr
281275048Sbr	vq = &sc->vs_queues[0];
282275048Sbr	if (!vq_ring_ready(vq))
283275048Sbr		return (0);
284275048Sbr
285275048Sbr	if (!sc->opened)
286275048Sbr		return (0);
287275048Sbr
288275048Sbr	reg = READ2(sc, VIRTIO_MMIO_QUEUE_NOTIFY);
289275048Sbr	queue = be16toh(reg);
290275048Sbr
291275048Sbr	KASSERT(queue == 0, ("we support single queue only"));
292275048Sbr
293275048Sbr	/* Process new descriptors */
294275048Sbr	vq = &sc->vs_queues[queue];
295275048Sbr	vq->vq_save_used = be16toh(vq->vq_used->idx);
296275048Sbr	while (vq_has_descs(vq))
297275048Sbr		vtblk_proc(sc, vq);
298275048Sbr
299275647Sbr	/* Interrupt the other side */
300275647Sbr	if ((be16toh(vq->vq_avail->flags) & VRING_AVAIL_F_NO_INTERRUPT) == 0) {
301275647Sbr		reg = htobe32(VIRTIO_MMIO_INT_VRING);
302275647Sbr		WRITE4(sc, VIRTIO_MMIO_INTERRUPT_STATUS, reg);
303275647Sbr		PIO_SET(sc->pio_send, Q_INTR, 1);
304275647Sbr	}
305275048Sbr
306275048Sbr	return (0);
307275048Sbr}
308275048Sbr
309275048Sbrstatic int
310275048Sbrvq_init(struct beri_vtblk_softc *sc)
311275048Sbr{
312275048Sbr	struct vqueue_info *vq;
313275048Sbr	uint8_t *base;
314275048Sbr	int size;
315275048Sbr	int reg;
316275048Sbr	int pfn;
317275048Sbr
318275048Sbr	vq = &sc->vs_queues[0];
319275647Sbr	vq->vq_qsize = NUM_DESCS;
320275048Sbr
321275048Sbr	reg = READ4(sc, VIRTIO_MMIO_QUEUE_PFN);
322275048Sbr	pfn = be32toh(reg);
323275048Sbr	vq->vq_pfn = pfn;
324275048Sbr
325275048Sbr	size = vring_size(vq->vq_qsize, VRING_ALIGN);
326275048Sbr	base = paddr_map(sc->beri_mem_offset,
327275048Sbr		(pfn << PAGE_SHIFT), size);
328275048Sbr
329275048Sbr	/* First pages are descriptors */
330275048Sbr	vq->vq_desc = (struct vring_desc *)base;
331275048Sbr	base += vq->vq_qsize * sizeof(struct vring_desc);
332275048Sbr
333275048Sbr	/* Then avail ring */
334275048Sbr	vq->vq_avail = (struct vring_avail *)base;
335275048Sbr	base += (2 + vq->vq_qsize + 1) * sizeof(uint16_t);
336275048Sbr
337275048Sbr	/* Then it's rounded up to the next page */
338275048Sbr	base = (uint8_t *)roundup2((uintptr_t)base, VRING_ALIGN);
339275048Sbr
340275048Sbr	/* And the last pages are the used ring */
341275048Sbr	vq->vq_used = (struct vring_used *)base;
342275048Sbr
343275048Sbr	/* Mark queue as allocated, and start at 0 when we use it. */
344275048Sbr	vq->vq_flags = VQ_ALLOC;
345275048Sbr	vq->vq_last_avail = 0;
346275048Sbr
347275048Sbr	return (0);
348275048Sbr}
349275048Sbr
350275048Sbr
351275048Sbrstatic void
352275048Sbrvtblk_thread(void *arg)
353275048Sbr{
354275048Sbr	struct beri_vtblk_softc *sc;
355275048Sbr	int err;
356275048Sbr
357275048Sbr	sc = arg;
358275048Sbr
359275048Sbr	sx_xlock(&sc->sc_mtx);
360275048Sbr	for (;;) {
361275048Sbr		err = msleep(sc, &sc->sc_mtx, PCATCH | PZERO, "prd", hz);
362275048Sbr		vtblk_notify(sc);
363275048Sbr	}
364275048Sbr	sx_xunlock(&sc->sc_mtx);
365275048Sbr
366275048Sbr	kthread_exit();
367275048Sbr}
368275048Sbr
369275048Sbrstatic int
370275048Sbrbackend_info(struct beri_vtblk_softc *sc)
371275048Sbr{
372275048Sbr	struct virtio_blk_config *cfg;
373275048Sbr	uint32_t *s;
374275048Sbr	int reg;
375275048Sbr	int i;
376275048Sbr
377275048Sbr	/* Specify that we provide block device */
378275048Sbr	reg = htobe32(VIRTIO_ID_BLOCK);
379275048Sbr	WRITE4(sc, VIRTIO_MMIO_DEVICE_ID, reg);
380275048Sbr
381275647Sbr	/* Queue size */
382275647Sbr	reg = htobe32(NUM_DESCS);
383275647Sbr	WRITE4(sc, VIRTIO_MMIO_QUEUE_NUM_MAX, reg);
384275048Sbr
385275048Sbr	/* Our features */
386275048Sbr	reg = htobe32(VIRTIO_RING_F_INDIRECT_DESC
387275048Sbr	    | VIRTIO_BLK_F_BLK_SIZE
388275048Sbr	    | VIRTIO_BLK_F_SEG_MAX);
389275048Sbr	WRITE4(sc, VIRTIO_MMIO_HOST_FEATURES, reg);
390275048Sbr
391275048Sbr	cfg = sc->cfg;
392275048Sbr	cfg->capacity = htobe64(sc->mdio->md_mediasize / DEV_BSIZE);
393275048Sbr	cfg->size_max = 0; /* not negotiated */
394275048Sbr	cfg->seg_max = htobe32(VTBLK_MAXSEGS);
395275048Sbr	cfg->blk_size = htobe32(DEV_BSIZE);
396275048Sbr
397275048Sbr	s = (uint32_t *)cfg;
398275048Sbr
399275048Sbr	for (i = 0; i < sizeof(struct virtio_blk_config); i+=4) {
400275048Sbr		WRITE4(sc, VIRTIO_MMIO_CONFIG + i, *s);
401275048Sbr		s+=1;
402275048Sbr	}
403275048Sbr
404275048Sbr	sprintf(sc->ident, "Virtio block backend");
405275048Sbr
406275048Sbr	return (0);
407275048Sbr}
408275048Sbr
409275048Sbrstatic void
410275048Sbrvtblk_intr(void *arg)
411275048Sbr{
412275048Sbr	struct beri_vtblk_softc *sc;
413275048Sbr	int pending;
414275048Sbr	int reg;
415275048Sbr
416275048Sbr	sc = arg;
417275048Sbr
418275048Sbr	reg = PIO_READ(sc->pio_recv);
419275048Sbr
420275048Sbr	/* Ack */
421275048Sbr	PIO_SET(sc->pio_recv, reg, 0);
422275048Sbr
423275048Sbr	pending = htobe32(reg);
424275048Sbr
425275048Sbr	if (pending & Q_PFN) {
426275048Sbr		vq_init(sc);
427275048Sbr	}
428275048Sbr
429275048Sbr	if (pending & Q_NOTIFY) {
430275048Sbr		wakeup(sc);
431275048Sbr	}
432275048Sbr}
433275048Sbr
434275048Sbrstatic int
435275048Sbrberi_ioctl(struct cdev *dev, u_long cmd, caddr_t addr,
436275048Sbr		int flags, struct thread *td)
437275048Sbr{
438275048Sbr	struct beri_vtblk_softc *sc;
439275048Sbr	int err;
440275048Sbr
441275048Sbr	sc = dev->si_drv1;
442275048Sbr
443275048Sbr	switch (cmd) {
444275048Sbr	case MDIOCATTACH:
445275048Sbr		/* take file as argument */
446275048Sbr		if (sc->vnode != NULL) {
447275048Sbr			/* Already opened */
448275048Sbr			return (1);
449275048Sbr		}
450275048Sbr		sc->mdio = (struct md_ioctl *)addr;
451275048Sbr		backend_info(sc);
452275048Sbr		DPRINTF("opening file, td 0x%08x\n", (int)td);
453275048Sbr		err = open_file(sc, td);
454275048Sbr		if (err)
455275048Sbr			return (err);
456275048Sbr		PIO_SETUP_IRQ(sc->pio_recv, vtblk_intr, sc);
457275048Sbr		sc->opened = 1;
458275048Sbr		break;
459275048Sbr	case MDIOCDETACH:
460315221Spfg		if (sc->vnode == NULL) {
461275048Sbr			/* File not opened */
462275048Sbr			return (1);
463275048Sbr		}
464275048Sbr		sc->opened = 0;
465275048Sbr		DPRINTF("closing file, td 0x%08x\n", (int)td);
466275048Sbr		err = close_file(sc, td);
467275048Sbr		if (err)
468275048Sbr			return (err);
469275048Sbr		PIO_TEARDOWN_IRQ(sc->pio_recv);
470275048Sbr		break;
471275048Sbr	default:
472275048Sbr		break;
473275048Sbr	}
474275048Sbr
475275048Sbr	return (0);
476275048Sbr}
477275048Sbr
478275048Sbrstatic struct cdevsw beri_cdevsw = {
479275048Sbr	.d_version =	D_VERSION,
480275048Sbr	.d_ioctl =	beri_ioctl,
481275048Sbr	.d_name =	"virtio block backend",
482275048Sbr};
483275048Sbr
484275048Sbrstatic int
485275048Sbrberi_vtblk_probe(device_t dev)
486275048Sbr{
487275048Sbr
488275048Sbr	if (!ofw_bus_status_okay(dev))
489275048Sbr		return (ENXIO);
490275048Sbr
491275048Sbr	if (!ofw_bus_is_compatible(dev, "sri-cambridge,beri-vtblk"))
492275048Sbr		return (ENXIO);
493275048Sbr
494275048Sbr	device_set_desc(dev, "SRI-Cambridge BERI block");
495275048Sbr	return (BUS_PROBE_DEFAULT);
496275048Sbr}
497275048Sbr
498275048Sbrstatic int
499275048Sbrberi_vtblk_attach(device_t dev)
500275048Sbr{
501275048Sbr	struct beri_vtblk_softc *sc;
502275048Sbr	int error;
503275048Sbr
504275048Sbr	sc = device_get_softc(dev);
505275048Sbr	sc->dev = dev;
506275048Sbr
507275048Sbr	if (bus_alloc_resources(dev, beri_spec, sc->res)) {
508275048Sbr		device_printf(dev, "could not allocate resources\n");
509275048Sbr		return (ENXIO);
510275048Sbr	}
511275048Sbr
512275048Sbr	/* Memory interface */
513275048Sbr	sc->bst = rman_get_bustag(sc->res[0]);
514275048Sbr	sc->bsh = rman_get_bushandle(sc->res[0]);
515275048Sbr
516275048Sbr	sc->cfg = malloc(sizeof(struct virtio_blk_config),
517275048Sbr		M_DEVBUF, M_NOWAIT|M_ZERO);
518275048Sbr
519275048Sbr	sx_init(&sc->sc_mtx, device_get_nameunit(sc->dev));
520275048Sbr
521275048Sbr	error = kthread_add(vtblk_thread, sc, NULL, &sc->vtblk_ktd,
522275048Sbr		0, 0, "beri_virtio_block");
523275048Sbr	if (error) {
524275048Sbr		device_printf(dev, "cannot create kthread\n");
525275048Sbr		return (ENXIO);
526275048Sbr	}
527275048Sbr
528275647Sbr	if (setup_offset(dev, &sc->beri_mem_offset) != 0)
529275048Sbr		return (ENXIO);
530275647Sbr	if (setup_pio(dev, "pio-send", &sc->pio_send) != 0)
531275048Sbr		return (ENXIO);
532275647Sbr	if (setup_pio(dev, "pio-recv", &sc->pio_recv) != 0)
533275048Sbr		return (ENXIO);
534275048Sbr
535275048Sbr	sc->cdev = make_dev(&beri_cdevsw, 0, UID_ROOT, GID_WHEEL,
536275048Sbr	    S_IRWXU, "beri_vtblk");
537275048Sbr	if (sc->cdev == NULL) {
538275048Sbr		device_printf(dev, "Failed to create character device.\n");
539275048Sbr		return (ENXIO);
540275048Sbr	}
541275048Sbr
542275048Sbr	sc->cdev->si_drv1 = sc;
543275048Sbr	return (0);
544275048Sbr}
545275048Sbr
546275048Sbrstatic device_method_t beri_vtblk_methods[] = {
547275048Sbr	DEVMETHOD(device_probe,		beri_vtblk_probe),
548275048Sbr	DEVMETHOD(device_attach,	beri_vtblk_attach),
549275048Sbr	{ 0, 0 }
550275048Sbr};
551275048Sbr
552275048Sbrstatic driver_t beri_vtblk_driver = {
553275048Sbr	"beri_vtblk",
554275048Sbr	beri_vtblk_methods,
555275048Sbr	sizeof(struct beri_vtblk_softc),
556275048Sbr};
557275048Sbr
558275048Sbrstatic devclass_t beri_vtblk_devclass;
559275048Sbr
560275048SbrDRIVER_MODULE(beri_vtblk, simplebus, beri_vtblk_driver,
561275048Sbr    beri_vtblk_devclass, 0, 0);
562