xref: /freebsd/sys/dev/beri/virtio/virtio_block.c (revision fdafd315)
113e19fb3SRuslan Bukin /*-
213e19fb3SRuslan Bukin  * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
313e19fb3SRuslan Bukin  * All rights reserved.
413e19fb3SRuslan Bukin  *
513e19fb3SRuslan Bukin  * This software was developed by SRI International and the University of
613e19fb3SRuslan Bukin  * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
713e19fb3SRuslan Bukin  * ("CTSRD"), as part of the DARPA CRASH research programme.
813e19fb3SRuslan Bukin  *
913e19fb3SRuslan Bukin  * Redistribution and use in source and binary forms, with or without
1013e19fb3SRuslan Bukin  * modification, are permitted provided that the following conditions
1113e19fb3SRuslan Bukin  * are met:
1213e19fb3SRuslan Bukin  * 1. Redistributions of source code must retain the above copyright
1313e19fb3SRuslan Bukin  *    notice, this list of conditions and the following disclaimer.
1413e19fb3SRuslan Bukin  * 2. Redistributions in binary form must reproduce the above copyright
1513e19fb3SRuslan Bukin  *    notice, this list of conditions and the following disclaimer in the
1613e19fb3SRuslan Bukin  *    documentation and/or other materials provided with the distribution.
1713e19fb3SRuslan Bukin  *
1813e19fb3SRuslan Bukin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1913e19fb3SRuslan Bukin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2013e19fb3SRuslan Bukin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2113e19fb3SRuslan Bukin  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2213e19fb3SRuslan Bukin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2313e19fb3SRuslan Bukin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2413e19fb3SRuslan Bukin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2513e19fb3SRuslan Bukin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2613e19fb3SRuslan Bukin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2713e19fb3SRuslan Bukin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2813e19fb3SRuslan Bukin  * SUCH DAMAGE.
2913e19fb3SRuslan Bukin  */
3013e19fb3SRuslan Bukin 
3113e19fb3SRuslan Bukin /*
3213e19fb3SRuslan Bukin  * BERI virtio block backend driver
3313e19fb3SRuslan Bukin  */
3413e19fb3SRuslan Bukin 
3513e19fb3SRuslan Bukin #include <sys/param.h>
3613e19fb3SRuslan Bukin #include <sys/systm.h>
3713e19fb3SRuslan Bukin #include <sys/bus.h>
3813e19fb3SRuslan Bukin #include <sys/kernel.h>
3913e19fb3SRuslan Bukin #include <sys/module.h>
4013e19fb3SRuslan Bukin #include <sys/rman.h>
4113e19fb3SRuslan Bukin #include <sys/conf.h>
4213e19fb3SRuslan Bukin #include <sys/stat.h>
4313e19fb3SRuslan Bukin #include <sys/endian.h>
4413e19fb3SRuslan Bukin #include <sys/disk.h>
4513e19fb3SRuslan Bukin #include <sys/vnode.h>
4613e19fb3SRuslan Bukin #include <sys/fcntl.h>
4713e19fb3SRuslan Bukin #include <sys/kthread.h>
4813e19fb3SRuslan Bukin #include <sys/buf.h>
4913e19fb3SRuslan Bukin #include <sys/mdioctl.h>
5013e19fb3SRuslan Bukin #include <sys/namei.h>
5113e19fb3SRuslan Bukin 
5213e19fb3SRuslan Bukin #include <machine/bus.h>
5313e19fb3SRuslan Bukin #include <machine/fdt.h>
5413e19fb3SRuslan Bukin #include <machine/cpu.h>
5513e19fb3SRuslan Bukin #include <machine/intr.h>
5613e19fb3SRuslan Bukin 
5713e19fb3SRuslan Bukin #include <dev/fdt/fdt_common.h>
5813e19fb3SRuslan Bukin #include <dev/ofw/openfirm.h>
5913e19fb3SRuslan Bukin #include <dev/ofw/ofw_bus.h>
6013e19fb3SRuslan Bukin #include <dev/ofw/ofw_bus_subr.h>
6113e19fb3SRuslan Bukin 
6213e19fb3SRuslan Bukin #include <dev/beri/virtio/virtio.h>
6313e19fb3SRuslan Bukin #include <dev/beri/virtio/virtio_mmio_platform.h>
6413e19fb3SRuslan Bukin #include <dev/altera/pio/pio.h>
6513e19fb3SRuslan Bukin #include <dev/virtio/mmio/virtio_mmio.h>
6613e19fb3SRuslan Bukin #include <dev/virtio/block/virtio_blk.h>
67f8357716SBryan Venteicher #include <dev/virtio/virtio_ids.h>
68f8357716SBryan Venteicher #include <dev/virtio/virtio_config.h>
6913e19fb3SRuslan Bukin #include <dev/virtio/virtio_ring.h>
7013e19fb3SRuslan Bukin 
7113e19fb3SRuslan Bukin #include "pio_if.h"
7213e19fb3SRuslan Bukin 
7313e19fb3SRuslan Bukin #define DPRINTF(fmt, ...)
7413e19fb3SRuslan Bukin 
75a8098016SRuslan Bukin /* We use indirect descriptors */
76a8098016SRuslan Bukin #define	NUM_DESCS	1
77a8098016SRuslan Bukin #define	NUM_QUEUES	1
78a8098016SRuslan Bukin 
79a8098016SRuslan Bukin #define	VTBLK_BLK_ID_BYTES	20
80a8098016SRuslan Bukin #define	VTBLK_MAXSEGS		256
81a8098016SRuslan Bukin 
8213e19fb3SRuslan Bukin struct beri_vtblk_softc {
8313e19fb3SRuslan Bukin 	struct resource		*res[1];
8413e19fb3SRuslan Bukin 	bus_space_tag_t		bst;
8513e19fb3SRuslan Bukin 	bus_space_handle_t	bsh;
8613e19fb3SRuslan Bukin 	struct cdev		*cdev;
8713e19fb3SRuslan Bukin 	device_t		dev;
8813e19fb3SRuslan Bukin 	int			opened;
8913e19fb3SRuslan Bukin 	device_t		pio_recv;
9013e19fb3SRuslan Bukin 	device_t		pio_send;
9113e19fb3SRuslan Bukin 	struct vqueue_info	vs_queues[NUM_QUEUES];
9213e19fb3SRuslan Bukin 	char			ident[VTBLK_BLK_ID_BYTES];
9313e19fb3SRuslan Bukin 	struct ucred		*cred;
9413e19fb3SRuslan Bukin 	struct vnode		*vnode;
9513e19fb3SRuslan Bukin 	struct thread		*vtblk_ktd;
9613e19fb3SRuslan Bukin 	struct sx		sc_mtx;
9713e19fb3SRuslan Bukin 	int			beri_mem_offset;
9813e19fb3SRuslan Bukin 	struct md_ioctl		*mdio;
9913e19fb3SRuslan Bukin 	struct virtio_blk_config *cfg;
10013e19fb3SRuslan Bukin };
10113e19fb3SRuslan Bukin 
10213e19fb3SRuslan Bukin static struct resource_spec beri_spec[] = {
10313e19fb3SRuslan Bukin 	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
10413e19fb3SRuslan Bukin 	{ -1, 0 }
10513e19fb3SRuslan Bukin };
10613e19fb3SRuslan Bukin 
10713e19fb3SRuslan Bukin static int
vtblk_rdwr(struct beri_vtblk_softc * sc,struct iovec * iov,int cnt,int offset,int operation,int iolen)10813e19fb3SRuslan Bukin vtblk_rdwr(struct beri_vtblk_softc *sc, struct iovec *iov,
10913e19fb3SRuslan Bukin 	int cnt, int offset, int operation, int iolen)
11013e19fb3SRuslan Bukin {
11113e19fb3SRuslan Bukin 	struct vnode *vp;
11213e19fb3SRuslan Bukin 	struct mount *mp;
11313e19fb3SRuslan Bukin 	struct uio auio;
11413e19fb3SRuslan Bukin 	int error;
11513e19fb3SRuslan Bukin 
11613e19fb3SRuslan Bukin 	bzero(&auio, sizeof(auio));
11713e19fb3SRuslan Bukin 
11813e19fb3SRuslan Bukin 	vp = sc->vnode;
11913e19fb3SRuslan Bukin 
12013e19fb3SRuslan Bukin 	KASSERT(vp != NULL, ("file not opened"));
12113e19fb3SRuslan Bukin 
12213e19fb3SRuslan Bukin 	auio.uio_iov = iov;
12313e19fb3SRuslan Bukin 	auio.uio_iovcnt = cnt;
12413e19fb3SRuslan Bukin 	auio.uio_offset = offset;
12513e19fb3SRuslan Bukin 	auio.uio_segflg = UIO_SYSSPACE;
12613e19fb3SRuslan Bukin 	auio.uio_rw = operation;
12713e19fb3SRuslan Bukin 	auio.uio_resid = iolen;
12813e19fb3SRuslan Bukin 	auio.uio_td = curthread;
12913e19fb3SRuslan Bukin 
13013e19fb3SRuslan Bukin 	if (operation == 0) {
13113e19fb3SRuslan Bukin 		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
13213e19fb3SRuslan Bukin 		error = VOP_READ(vp, &auio, IO_DIRECT, sc->cred);
133b249ce48SMateusz Guzik 		VOP_UNLOCK(vp);
13413e19fb3SRuslan Bukin 	} else {
13513e19fb3SRuslan Bukin 		(void) vn_start_write(vp, &mp, V_WAIT);
13613e19fb3SRuslan Bukin 		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
13713e19fb3SRuslan Bukin 		error = VOP_WRITE(vp, &auio, IO_SYNC, sc->cred);
138b249ce48SMateusz Guzik 		VOP_UNLOCK(vp);
13913e19fb3SRuslan Bukin 		vn_finished_write(mp);
14013e19fb3SRuslan Bukin 	}
14113e19fb3SRuslan Bukin 
14213e19fb3SRuslan Bukin 	return (error);
14313e19fb3SRuslan Bukin }
14413e19fb3SRuslan Bukin 
14513e19fb3SRuslan Bukin static void
vtblk_proc(struct beri_vtblk_softc * sc,struct vqueue_info * vq)14613e19fb3SRuslan Bukin vtblk_proc(struct beri_vtblk_softc *sc, struct vqueue_info *vq)
14713e19fb3SRuslan Bukin {
14813e19fb3SRuslan Bukin 	struct iovec iov[VTBLK_MAXSEGS + 2];
14913e19fb3SRuslan Bukin 	uint16_t flags[VTBLK_MAXSEGS + 2];
15013e19fb3SRuslan Bukin 	struct virtio_blk_outhdr *vbh;
1511aac28e7SRuslan Bukin 	struct iovec *tiov;
15213e19fb3SRuslan Bukin 	uint8_t *status;
15313e19fb3SRuslan Bukin 	off_t offset;
15413e19fb3SRuslan Bukin 	int iolen;
15513e19fb3SRuslan Bukin 	int type;
15613e19fb3SRuslan Bukin 	int i, n;
15713e19fb3SRuslan Bukin 	int err;
15813e19fb3SRuslan Bukin 
15913e19fb3SRuslan Bukin 	n = vq_getchain(sc->beri_mem_offset, vq, iov,
16013e19fb3SRuslan Bukin 		VTBLK_MAXSEGS + 2, flags);
16113e19fb3SRuslan Bukin 	KASSERT(n >= 2 && n <= VTBLK_MAXSEGS + 2,
16213e19fb3SRuslan Bukin 		("wrong n value %d", n));
16313e19fb3SRuslan Bukin 
1641aac28e7SRuslan Bukin 	tiov = getcopy(iov, n);
16513e19fb3SRuslan Bukin 	vbh = iov[0].iov_base;
16613e19fb3SRuslan Bukin 
16713e19fb3SRuslan Bukin 	status = iov[n-1].iov_base;
16813e19fb3SRuslan Bukin 	KASSERT(iov[n-1].iov_len == 1,
16913e19fb3SRuslan Bukin 		("iov_len == %d", iov[n-1].iov_len));
17013e19fb3SRuslan Bukin 
17113e19fb3SRuslan Bukin 	type = be32toh(vbh->type) & ~VIRTIO_BLK_T_BARRIER;
17213e19fb3SRuslan Bukin 	offset = be64toh(vbh->sector) * DEV_BSIZE;
17313e19fb3SRuslan Bukin 
17413e19fb3SRuslan Bukin 	iolen = 0;
17513e19fb3SRuslan Bukin 	for (i = 1; i < (n-1); i++) {
17613e19fb3SRuslan Bukin 		iolen += iov[i].iov_len;
17713e19fb3SRuslan Bukin 	}
17813e19fb3SRuslan Bukin 
17913e19fb3SRuslan Bukin 	switch (type) {
18013e19fb3SRuslan Bukin 	case VIRTIO_BLK_T_OUT:
18113e19fb3SRuslan Bukin 	case VIRTIO_BLK_T_IN:
1821aac28e7SRuslan Bukin 		err = vtblk_rdwr(sc, tiov + 1, i - 1,
18313e19fb3SRuslan Bukin 			offset, type, iolen);
18413e19fb3SRuslan Bukin 		break;
18513e19fb3SRuslan Bukin 	case VIRTIO_BLK_T_GET_ID:
18613e19fb3SRuslan Bukin 		/* Assume a single buffer */
1873af08701SOleksandr Tymoshenko 		strncpy(iov[1].iov_base, sc->ident,
18813e19fb3SRuslan Bukin 		    MIN(iov[1].iov_len, sizeof(sc->ident)));
18913e19fb3SRuslan Bukin 		err = 0;
19013e19fb3SRuslan Bukin 		break;
19113e19fb3SRuslan Bukin 	case VIRTIO_BLK_T_FLUSH:
19213e19fb3SRuslan Bukin 		/* Possible? */
19313e19fb3SRuslan Bukin 	default:
19413e19fb3SRuslan Bukin 		err = -ENOSYS;
19513e19fb3SRuslan Bukin 		break;
19613e19fb3SRuslan Bukin 	}
19713e19fb3SRuslan Bukin 
19813e19fb3SRuslan Bukin 	if (err < 0) {
19913e19fb3SRuslan Bukin 		if (err == -ENOSYS) {
20013e19fb3SRuslan Bukin 			*status = VIRTIO_BLK_S_UNSUPP;
20113e19fb3SRuslan Bukin 		} else
20213e19fb3SRuslan Bukin 			*status = VIRTIO_BLK_S_IOERR;
20313e19fb3SRuslan Bukin 	} else
20413e19fb3SRuslan Bukin 		*status = VIRTIO_BLK_S_OK;
20513e19fb3SRuslan Bukin 
2061aac28e7SRuslan Bukin 	free(tiov, M_DEVBUF);
20713e19fb3SRuslan Bukin 	vq_relchain(vq, iov, n, 1);
20813e19fb3SRuslan Bukin }
20913e19fb3SRuslan Bukin 
21013e19fb3SRuslan Bukin static int
close_file(struct beri_vtblk_softc * sc,struct thread * td)21113e19fb3SRuslan Bukin close_file(struct beri_vtblk_softc *sc, struct thread *td)
21213e19fb3SRuslan Bukin {
21313e19fb3SRuslan Bukin 	int error;
21413e19fb3SRuslan Bukin 
21513e19fb3SRuslan Bukin 	if (sc->vnode != NULL) {
21613e19fb3SRuslan Bukin 		vn_lock(sc->vnode, LK_EXCLUSIVE | LK_RETRY);
21713e19fb3SRuslan Bukin 		sc->vnode->v_vflag &= ~VV_MD;
218b249ce48SMateusz Guzik 		VOP_UNLOCK(sc->vnode);
21913e19fb3SRuslan Bukin 		error = vn_close(sc->vnode, (FREAD|FWRITE),
22013e19fb3SRuslan Bukin 				sc->cred, td);
22113e19fb3SRuslan Bukin 		if (error != 0)
22213e19fb3SRuslan Bukin 			return (error);
22313e19fb3SRuslan Bukin 		sc->vnode = NULL;
22413e19fb3SRuslan Bukin 	}
22513e19fb3SRuslan Bukin 
22613e19fb3SRuslan Bukin 	if (sc->cred != NULL)
22713e19fb3SRuslan Bukin 		crfree(sc->cred);
22813e19fb3SRuslan Bukin 
22913e19fb3SRuslan Bukin 	return (0);
23013e19fb3SRuslan Bukin }
23113e19fb3SRuslan Bukin 
23213e19fb3SRuslan Bukin static int
open_file(struct beri_vtblk_softc * sc,struct thread * td)23313e19fb3SRuslan Bukin open_file(struct beri_vtblk_softc *sc, struct thread *td)
23413e19fb3SRuslan Bukin {
23513e19fb3SRuslan Bukin 	struct nameidata nd;
23613e19fb3SRuslan Bukin 	struct vattr vattr;
23713e19fb3SRuslan Bukin 	int error;
23813e19fb3SRuslan Bukin 	int flags;
23913e19fb3SRuslan Bukin 
24013e19fb3SRuslan Bukin 	flags = (FREAD | FWRITE);
2417e1d3eefSMateusz Guzik 	NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, sc->mdio->md_file);
24213e19fb3SRuslan Bukin 	error = vn_open(&nd, &flags, 0, NULL);
24313e19fb3SRuslan Bukin 	if (error != 0)
24413e19fb3SRuslan Bukin 		return (error);
245bb92cd7bSMateusz Guzik 	NDFREE_PNBUF(&nd);
24613e19fb3SRuslan Bukin 
24713e19fb3SRuslan Bukin 	if (nd.ni_vp->v_type != VREG) {
24813e19fb3SRuslan Bukin 		return (EINVAL);
24913e19fb3SRuslan Bukin 	}
25013e19fb3SRuslan Bukin 
25113e19fb3SRuslan Bukin 	error = VOP_GETATTR(nd.ni_vp, &vattr, td->td_ucred);
25213e19fb3SRuslan Bukin 	if (error != 0)
25313e19fb3SRuslan Bukin 		return (error);
25413e19fb3SRuslan Bukin 
25513e19fb3SRuslan Bukin 	if (VOP_ISLOCKED(nd.ni_vp) != LK_EXCLUSIVE) {
25613e19fb3SRuslan Bukin 		vn_lock(nd.ni_vp, LK_UPGRADE | LK_RETRY);
257abd80ddbSMateusz Guzik 		if (VN_IS_DOOMED(nd.ni_vp)) {
25813e19fb3SRuslan Bukin 			return (1);
25913e19fb3SRuslan Bukin 		}
26013e19fb3SRuslan Bukin 	}
26113e19fb3SRuslan Bukin 	nd.ni_vp->v_vflag |= VV_MD;
262b249ce48SMateusz Guzik 	VOP_UNLOCK(nd.ni_vp);
26313e19fb3SRuslan Bukin 
26413e19fb3SRuslan Bukin 	sc->vnode = nd.ni_vp;
26513e19fb3SRuslan Bukin 	sc->cred = crhold(td->td_ucred);
26613e19fb3SRuslan Bukin 
26713e19fb3SRuslan Bukin 	return (0);
26813e19fb3SRuslan Bukin }
26913e19fb3SRuslan Bukin 
27013e19fb3SRuslan Bukin static int
vtblk_notify(struct beri_vtblk_softc * sc)27113e19fb3SRuslan Bukin vtblk_notify(struct beri_vtblk_softc *sc)
27213e19fb3SRuslan Bukin {
27313e19fb3SRuslan Bukin 	struct vqueue_info *vq;
27413e19fb3SRuslan Bukin 	int queue;
27513e19fb3SRuslan Bukin 	int reg;
27613e19fb3SRuslan Bukin 
27713e19fb3SRuslan Bukin 	vq = &sc->vs_queues[0];
27813e19fb3SRuslan Bukin 	if (!vq_ring_ready(vq))
27913e19fb3SRuslan Bukin 		return (0);
28013e19fb3SRuslan Bukin 
28113e19fb3SRuslan Bukin 	if (!sc->opened)
28213e19fb3SRuslan Bukin 		return (0);
28313e19fb3SRuslan Bukin 
28413e19fb3SRuslan Bukin 	reg = READ2(sc, VIRTIO_MMIO_QUEUE_NOTIFY);
28513e19fb3SRuslan Bukin 	queue = be16toh(reg);
28613e19fb3SRuslan Bukin 
28713e19fb3SRuslan Bukin 	KASSERT(queue == 0, ("we support single queue only"));
28813e19fb3SRuslan Bukin 
28913e19fb3SRuslan Bukin 	/* Process new descriptors */
29013e19fb3SRuslan Bukin 	vq = &sc->vs_queues[queue];
29113e19fb3SRuslan Bukin 	vq->vq_save_used = be16toh(vq->vq_used->idx);
29213e19fb3SRuslan Bukin 	while (vq_has_descs(vq))
29313e19fb3SRuslan Bukin 		vtblk_proc(sc, vq);
29413e19fb3SRuslan Bukin 
295a8098016SRuslan Bukin 	/* Interrupt the other side */
296a8098016SRuslan Bukin 	if ((be16toh(vq->vq_avail->flags) & VRING_AVAIL_F_NO_INTERRUPT) == 0) {
297a8098016SRuslan Bukin 		reg = htobe32(VIRTIO_MMIO_INT_VRING);
298a8098016SRuslan Bukin 		WRITE4(sc, VIRTIO_MMIO_INTERRUPT_STATUS, reg);
29913e19fb3SRuslan Bukin 		PIO_SET(sc->pio_send, Q_INTR, 1);
300a8098016SRuslan Bukin 	}
30113e19fb3SRuslan Bukin 
30213e19fb3SRuslan Bukin 	return (0);
30313e19fb3SRuslan Bukin }
30413e19fb3SRuslan Bukin 
30513e19fb3SRuslan Bukin static int
vq_init(struct beri_vtblk_softc * sc)30613e19fb3SRuslan Bukin vq_init(struct beri_vtblk_softc *sc)
30713e19fb3SRuslan Bukin {
30813e19fb3SRuslan Bukin 	struct vqueue_info *vq;
30913e19fb3SRuslan Bukin 	uint8_t *base;
31013e19fb3SRuslan Bukin 	int size;
31113e19fb3SRuslan Bukin 	int reg;
31213e19fb3SRuslan Bukin 	int pfn;
31313e19fb3SRuslan Bukin 
31413e19fb3SRuslan Bukin 	vq = &sc->vs_queues[0];
315a8098016SRuslan Bukin 	vq->vq_qsize = NUM_DESCS;
31613e19fb3SRuslan Bukin 
31713e19fb3SRuslan Bukin 	reg = READ4(sc, VIRTIO_MMIO_QUEUE_PFN);
31813e19fb3SRuslan Bukin 	pfn = be32toh(reg);
31913e19fb3SRuslan Bukin 	vq->vq_pfn = pfn;
32013e19fb3SRuslan Bukin 
32113e19fb3SRuslan Bukin 	size = vring_size(vq->vq_qsize, VRING_ALIGN);
32213e19fb3SRuslan Bukin 	base = paddr_map(sc->beri_mem_offset,
32313e19fb3SRuslan Bukin 		(pfn << PAGE_SHIFT), size);
32413e19fb3SRuslan Bukin 
32513e19fb3SRuslan Bukin 	/* First pages are descriptors */
32613e19fb3SRuslan Bukin 	vq->vq_desc = (struct vring_desc *)base;
32713e19fb3SRuslan Bukin 	base += vq->vq_qsize * sizeof(struct vring_desc);
32813e19fb3SRuslan Bukin 
32913e19fb3SRuslan Bukin 	/* Then avail ring */
33013e19fb3SRuslan Bukin 	vq->vq_avail = (struct vring_avail *)base;
33113e19fb3SRuslan Bukin 	base += (2 + vq->vq_qsize + 1) * sizeof(uint16_t);
33213e19fb3SRuslan Bukin 
33313e19fb3SRuslan Bukin 	/* Then it's rounded up to the next page */
33413e19fb3SRuslan Bukin 	base = (uint8_t *)roundup2((uintptr_t)base, VRING_ALIGN);
33513e19fb3SRuslan Bukin 
33613e19fb3SRuslan Bukin 	/* And the last pages are the used ring */
33713e19fb3SRuslan Bukin 	vq->vq_used = (struct vring_used *)base;
33813e19fb3SRuslan Bukin 
33913e19fb3SRuslan Bukin 	/* Mark queue as allocated, and start at 0 when we use it. */
34013e19fb3SRuslan Bukin 	vq->vq_flags = VQ_ALLOC;
34113e19fb3SRuslan Bukin 	vq->vq_last_avail = 0;
34213e19fb3SRuslan Bukin 
34313e19fb3SRuslan Bukin 	return (0);
34413e19fb3SRuslan Bukin }
34513e19fb3SRuslan Bukin 
34613e19fb3SRuslan Bukin static void
vtblk_thread(void * arg)34713e19fb3SRuslan Bukin vtblk_thread(void *arg)
34813e19fb3SRuslan Bukin {
34913e19fb3SRuslan Bukin 	struct beri_vtblk_softc *sc;
35013e19fb3SRuslan Bukin 	int err;
35113e19fb3SRuslan Bukin 
35213e19fb3SRuslan Bukin 	sc = arg;
35313e19fb3SRuslan Bukin 
35413e19fb3SRuslan Bukin 	sx_xlock(&sc->sc_mtx);
35513e19fb3SRuslan Bukin 	for (;;) {
35613e19fb3SRuslan Bukin 		err = msleep(sc, &sc->sc_mtx, PCATCH | PZERO, "prd", hz);
35713e19fb3SRuslan Bukin 		vtblk_notify(sc);
35813e19fb3SRuslan Bukin 	}
35913e19fb3SRuslan Bukin 	sx_xunlock(&sc->sc_mtx);
36013e19fb3SRuslan Bukin 
36113e19fb3SRuslan Bukin 	kthread_exit();
36213e19fb3SRuslan Bukin }
36313e19fb3SRuslan Bukin 
36413e19fb3SRuslan Bukin static int
backend_info(struct beri_vtblk_softc * sc)36513e19fb3SRuslan Bukin backend_info(struct beri_vtblk_softc *sc)
36613e19fb3SRuslan Bukin {
36713e19fb3SRuslan Bukin 	struct virtio_blk_config *cfg;
36813e19fb3SRuslan Bukin 	uint32_t *s;
36913e19fb3SRuslan Bukin 	int reg;
37013e19fb3SRuslan Bukin 	int i;
37113e19fb3SRuslan Bukin 
37213e19fb3SRuslan Bukin 	/* Specify that we provide block device */
37313e19fb3SRuslan Bukin 	reg = htobe32(VIRTIO_ID_BLOCK);
37413e19fb3SRuslan Bukin 	WRITE4(sc, VIRTIO_MMIO_DEVICE_ID, reg);
37513e19fb3SRuslan Bukin 
376a8098016SRuslan Bukin 	/* Queue size */
377a8098016SRuslan Bukin 	reg = htobe32(NUM_DESCS);
378a8098016SRuslan Bukin 	WRITE4(sc, VIRTIO_MMIO_QUEUE_NUM_MAX, reg);
37913e19fb3SRuslan Bukin 
38013e19fb3SRuslan Bukin 	/* Our features */
38113e19fb3SRuslan Bukin 	reg = htobe32(VIRTIO_RING_F_INDIRECT_DESC
38213e19fb3SRuslan Bukin 	    | VIRTIO_BLK_F_BLK_SIZE
38313e19fb3SRuslan Bukin 	    | VIRTIO_BLK_F_SEG_MAX);
38413e19fb3SRuslan Bukin 	WRITE4(sc, VIRTIO_MMIO_HOST_FEATURES, reg);
38513e19fb3SRuslan Bukin 
38613e19fb3SRuslan Bukin 	cfg = sc->cfg;
38713e19fb3SRuslan Bukin 	cfg->capacity = htobe64(sc->mdio->md_mediasize / DEV_BSIZE);
38813e19fb3SRuslan Bukin 	cfg->size_max = 0; /* not negotiated */
38913e19fb3SRuslan Bukin 	cfg->seg_max = htobe32(VTBLK_MAXSEGS);
39013e19fb3SRuslan Bukin 	cfg->blk_size = htobe32(DEV_BSIZE);
39113e19fb3SRuslan Bukin 
39213e19fb3SRuslan Bukin 	s = (uint32_t *)cfg;
39313e19fb3SRuslan Bukin 
39413e19fb3SRuslan Bukin 	for (i = 0; i < sizeof(struct virtio_blk_config); i+=4) {
39513e19fb3SRuslan Bukin 		WRITE4(sc, VIRTIO_MMIO_CONFIG + i, *s);
39613e19fb3SRuslan Bukin 		s+=1;
39713e19fb3SRuslan Bukin 	}
39813e19fb3SRuslan Bukin 
3993af08701SOleksandr Tymoshenko 	strncpy(sc->ident, "Virtio block backend", sizeof(sc->ident));
40013e19fb3SRuslan Bukin 
40113e19fb3SRuslan Bukin 	return (0);
40213e19fb3SRuslan Bukin }
40313e19fb3SRuslan Bukin 
40413e19fb3SRuslan Bukin static void
vtblk_intr(void * arg)40513e19fb3SRuslan Bukin vtblk_intr(void *arg)
40613e19fb3SRuslan Bukin {
40713e19fb3SRuslan Bukin 	struct beri_vtblk_softc *sc;
40813e19fb3SRuslan Bukin 	int pending;
40913e19fb3SRuslan Bukin 	int reg;
41013e19fb3SRuslan Bukin 
41113e19fb3SRuslan Bukin 	sc = arg;
41213e19fb3SRuslan Bukin 
41313e19fb3SRuslan Bukin 	reg = PIO_READ(sc->pio_recv);
41413e19fb3SRuslan Bukin 
41513e19fb3SRuslan Bukin 	/* Ack */
41613e19fb3SRuslan Bukin 	PIO_SET(sc->pio_recv, reg, 0);
41713e19fb3SRuslan Bukin 
41813e19fb3SRuslan Bukin 	pending = htobe32(reg);
41913e19fb3SRuslan Bukin 
42013e19fb3SRuslan Bukin 	if (pending & Q_PFN) {
42113e19fb3SRuslan Bukin 		vq_init(sc);
42213e19fb3SRuslan Bukin 	}
42313e19fb3SRuslan Bukin 
42413e19fb3SRuslan Bukin 	if (pending & Q_NOTIFY) {
42513e19fb3SRuslan Bukin 		wakeup(sc);
42613e19fb3SRuslan Bukin 	}
42713e19fb3SRuslan Bukin }
42813e19fb3SRuslan Bukin 
42913e19fb3SRuslan Bukin static int
beri_ioctl(struct cdev * dev,u_long cmd,caddr_t addr,int flags,struct thread * td)43013e19fb3SRuslan Bukin beri_ioctl(struct cdev *dev, u_long cmd, caddr_t addr,
43113e19fb3SRuslan Bukin 		int flags, struct thread *td)
43213e19fb3SRuslan Bukin {
43313e19fb3SRuslan Bukin 	struct beri_vtblk_softc *sc;
43413e19fb3SRuslan Bukin 	int err;
43513e19fb3SRuslan Bukin 
43613e19fb3SRuslan Bukin 	sc = dev->si_drv1;
43713e19fb3SRuslan Bukin 
43813e19fb3SRuslan Bukin 	switch (cmd) {
43913e19fb3SRuslan Bukin 	case MDIOCATTACH:
44013e19fb3SRuslan Bukin 		/* take file as argument */
44113e19fb3SRuslan Bukin 		if (sc->vnode != NULL) {
44213e19fb3SRuslan Bukin 			/* Already opened */
44313e19fb3SRuslan Bukin 			return (1);
44413e19fb3SRuslan Bukin 		}
44513e19fb3SRuslan Bukin 		sc->mdio = (struct md_ioctl *)addr;
44613e19fb3SRuslan Bukin 		backend_info(sc);
44713e19fb3SRuslan Bukin 		DPRINTF("opening file, td 0x%08x\n", (int)td);
44813e19fb3SRuslan Bukin 		err = open_file(sc, td);
44913e19fb3SRuslan Bukin 		if (err)
45013e19fb3SRuslan Bukin 			return (err);
45113e19fb3SRuslan Bukin 		PIO_SETUP_IRQ(sc->pio_recv, vtblk_intr, sc);
45213e19fb3SRuslan Bukin 		sc->opened = 1;
45313e19fb3SRuslan Bukin 		break;
45413e19fb3SRuslan Bukin 	case MDIOCDETACH:
4554d24901aSPedro F. Giffuni 		if (sc->vnode == NULL) {
45613e19fb3SRuslan Bukin 			/* File not opened */
45713e19fb3SRuslan Bukin 			return (1);
45813e19fb3SRuslan Bukin 		}
45913e19fb3SRuslan Bukin 		sc->opened = 0;
46013e19fb3SRuslan Bukin 		DPRINTF("closing file, td 0x%08x\n", (int)td);
46113e19fb3SRuslan Bukin 		err = close_file(sc, td);
46213e19fb3SRuslan Bukin 		if (err)
46313e19fb3SRuslan Bukin 			return (err);
46413e19fb3SRuslan Bukin 		PIO_TEARDOWN_IRQ(sc->pio_recv);
46513e19fb3SRuslan Bukin 		break;
46613e19fb3SRuslan Bukin 	default:
46713e19fb3SRuslan Bukin 		break;
46813e19fb3SRuslan Bukin 	}
46913e19fb3SRuslan Bukin 
47013e19fb3SRuslan Bukin 	return (0);
47113e19fb3SRuslan Bukin }
47213e19fb3SRuslan Bukin 
47313e19fb3SRuslan Bukin static struct cdevsw beri_cdevsw = {
47413e19fb3SRuslan Bukin 	.d_version =	D_VERSION,
47513e19fb3SRuslan Bukin 	.d_ioctl =	beri_ioctl,
47613e19fb3SRuslan Bukin 	.d_name =	"virtio block backend",
47713e19fb3SRuslan Bukin };
47813e19fb3SRuslan Bukin 
47913e19fb3SRuslan Bukin static int
beri_vtblk_probe(device_t dev)48013e19fb3SRuslan Bukin beri_vtblk_probe(device_t dev)
48113e19fb3SRuslan Bukin {
48213e19fb3SRuslan Bukin 
48313e19fb3SRuslan Bukin 	if (!ofw_bus_status_okay(dev))
48413e19fb3SRuslan Bukin 		return (ENXIO);
48513e19fb3SRuslan Bukin 
48613e19fb3SRuslan Bukin 	if (!ofw_bus_is_compatible(dev, "sri-cambridge,beri-vtblk"))
48713e19fb3SRuslan Bukin 		return (ENXIO);
48813e19fb3SRuslan Bukin 
48913e19fb3SRuslan Bukin 	device_set_desc(dev, "SRI-Cambridge BERI block");
49013e19fb3SRuslan Bukin 	return (BUS_PROBE_DEFAULT);
49113e19fb3SRuslan Bukin }
49213e19fb3SRuslan Bukin 
49313e19fb3SRuslan Bukin static int
beri_vtblk_attach(device_t dev)49413e19fb3SRuslan Bukin beri_vtblk_attach(device_t dev)
49513e19fb3SRuslan Bukin {
49613e19fb3SRuslan Bukin 	struct beri_vtblk_softc *sc;
49713e19fb3SRuslan Bukin 	int error;
49813e19fb3SRuslan Bukin 
49913e19fb3SRuslan Bukin 	sc = device_get_softc(dev);
50013e19fb3SRuslan Bukin 	sc->dev = dev;
50113e19fb3SRuslan Bukin 
50213e19fb3SRuslan Bukin 	if (bus_alloc_resources(dev, beri_spec, sc->res)) {
50313e19fb3SRuslan Bukin 		device_printf(dev, "could not allocate resources\n");
50413e19fb3SRuslan Bukin 		return (ENXIO);
50513e19fb3SRuslan Bukin 	}
50613e19fb3SRuslan Bukin 
50713e19fb3SRuslan Bukin 	/* Memory interface */
50813e19fb3SRuslan Bukin 	sc->bst = rman_get_bustag(sc->res[0]);
50913e19fb3SRuslan Bukin 	sc->bsh = rman_get_bushandle(sc->res[0]);
51013e19fb3SRuslan Bukin 
51113e19fb3SRuslan Bukin 	sc->cfg = malloc(sizeof(struct virtio_blk_config),
51213e19fb3SRuslan Bukin 		M_DEVBUF, M_NOWAIT|M_ZERO);
51313e19fb3SRuslan Bukin 
51413e19fb3SRuslan Bukin 	sx_init(&sc->sc_mtx, device_get_nameunit(sc->dev));
51513e19fb3SRuslan Bukin 
51613e19fb3SRuslan Bukin 	error = kthread_add(vtblk_thread, sc, NULL, &sc->vtblk_ktd,
51713e19fb3SRuslan Bukin 		0, 0, "beri_virtio_block");
51813e19fb3SRuslan Bukin 	if (error) {
51913e19fb3SRuslan Bukin 		device_printf(dev, "cannot create kthread\n");
52013e19fb3SRuslan Bukin 		return (ENXIO);
52113e19fb3SRuslan Bukin 	}
52213e19fb3SRuslan Bukin 
523a8098016SRuslan Bukin 	if (setup_offset(dev, &sc->beri_mem_offset) != 0)
52413e19fb3SRuslan Bukin 		return (ENXIO);
525a8098016SRuslan Bukin 	if (setup_pio(dev, "pio-send", &sc->pio_send) != 0)
52613e19fb3SRuslan Bukin 		return (ENXIO);
527a8098016SRuslan Bukin 	if (setup_pio(dev, "pio-recv", &sc->pio_recv) != 0)
52813e19fb3SRuslan Bukin 		return (ENXIO);
52913e19fb3SRuslan Bukin 
53013e19fb3SRuslan Bukin 	sc->cdev = make_dev(&beri_cdevsw, 0, UID_ROOT, GID_WHEEL,
53113e19fb3SRuslan Bukin 	    S_IRWXU, "beri_vtblk");
53213e19fb3SRuslan Bukin 	if (sc->cdev == NULL) {
53313e19fb3SRuslan Bukin 		device_printf(dev, "Failed to create character device.\n");
53413e19fb3SRuslan Bukin 		return (ENXIO);
53513e19fb3SRuslan Bukin 	}
53613e19fb3SRuslan Bukin 
53713e19fb3SRuslan Bukin 	sc->cdev->si_drv1 = sc;
53813e19fb3SRuslan Bukin 	return (0);
53913e19fb3SRuslan Bukin }
54013e19fb3SRuslan Bukin 
54113e19fb3SRuslan Bukin static device_method_t beri_vtblk_methods[] = {
54213e19fb3SRuslan Bukin 	DEVMETHOD(device_probe,		beri_vtblk_probe),
54313e19fb3SRuslan Bukin 	DEVMETHOD(device_attach,	beri_vtblk_attach),
54413e19fb3SRuslan Bukin 	{ 0, 0 }
54513e19fb3SRuslan Bukin };
54613e19fb3SRuslan Bukin 
54713e19fb3SRuslan Bukin static driver_t beri_vtblk_driver = {
54813e19fb3SRuslan Bukin 	"beri_vtblk",
54913e19fb3SRuslan Bukin 	beri_vtblk_methods,
55013e19fb3SRuslan Bukin 	sizeof(struct beri_vtblk_softc),
55113e19fb3SRuslan Bukin };
55213e19fb3SRuslan Bukin 
5532ded2b9aSJohn Baldwin DRIVER_MODULE(beri_vtblk, simplebus, beri_vtblk_driver, 0, 0);
554