From owner-svn-src-all@FreeBSD.ORG Tue Nov 25 15:59:00 2014 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id 7563EA71; Tue, 25 Nov 2014 15:59:00 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 60D2DDC8; Tue, 25 Nov 2014 15:59:00 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.9/8.14.9) with ESMTP id sAPFx0Rp033877; Tue, 25 Nov 2014 15:59:00 GMT (envelope-from br@FreeBSD.org) Received: (from br@localhost) by svn.freebsd.org (8.14.9/8.14.9/Submit) id sAPFwxCr033868; Tue, 25 Nov 2014 15:58:59 GMT (envelope-from br@FreeBSD.org) Message-Id: <201411251558.sAPFwxCr033868@svn.freebsd.org> X-Authentication-Warning: svn.freebsd.org: br set sender to br@FreeBSD.org using -f From: Ruslan Bukin Date: Tue, 25 Nov 2014 15:58:59 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r275048 - head/sys/dev/beri/virtio X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 25 Nov 2014 15:59:00 -0000 Author: br Date: Tue Nov 25 15:58:59 2014 New Revision: 275048 URL: https://svnweb.freebsd.org/changeset/base/275048 Log: Add BERI-specific virtio block backend device driver. This part intended to operate on ARM side in heterogeneous (ARM/BERI) system on crystal. Added: head/sys/dev/beri/virtio/virtio.c (contents, props changed) head/sys/dev/beri/virtio/virtio.h (contents, props changed) head/sys/dev/beri/virtio/virtio_block.c (contents, props changed) Added: head/sys/dev/beri/virtio/virtio.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/dev/beri/virtio/virtio.c Tue Nov 25 15:58:59 2014 (r275048) @@ -0,0 +1,183 @@ +/*- + * Copyright (c) 2014 Ruslan Bukin + * All rights reserved. + * + * This software was developed by SRI International and the University of + * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) + * ("CTSRD"), as part of the DARPA CRASH research programme. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * BERI virtio mmio backend common methods + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +int +vq_ring_ready(struct vqueue_info *vq) +{ + + return (vq->vq_flags & VQ_ALLOC); +} + +int +vq_has_descs(struct vqueue_info *vq) +{ + + return (vq_ring_ready(vq) && vq->vq_last_avail != + be16toh(vq->vq_avail->idx)); +} + +void * +paddr_map(uint32_t offset, uint32_t phys, uint32_t size) +{ + bus_space_handle_t bsh; + + if (bus_space_map(fdtbus_bs_tag, (phys + offset), + size, 0, &bsh) != 0) { + panic("Couldn't map 0x%08x\n", (phys + offset)); + } + + return (void *)(bsh); +} + +void +paddr_unmap(void *phys, uint32_t size) +{ + + bus_space_unmap(fdtbus_bs_tag, (bus_space_handle_t)phys, size); +} + +static inline void +_vq_record(uint32_t offs, int i, volatile struct vring_desc *vd, + struct iovec *iov, int n_iov, uint16_t *flags) { + + if (i >= n_iov) + return; + + iov[i].iov_base = paddr_map(offs, be64toh(vd->addr), + be32toh(vd->len)); + iov[i].iov_len = be32toh(vd->len); + if (flags != NULL) + flags[i] = be16toh(vd->flags); +} + +int +vq_getchain(uint32_t offs, struct vqueue_info *vq, + struct iovec *iov, int n_iov, uint16_t *flags) +{ + volatile struct vring_desc *vdir, *vindir, *vp; + int idx, ndesc, n_indir; + int head, next; + int i; + + idx = vq->vq_last_avail; + ndesc = (be16toh(vq->vq_avail->idx) - idx); + if (ndesc == 0) + return (0); + + head = be16toh(vq->vq_avail->ring[idx & (vq->vq_qsize - 1)]); + next = head; + + for (i = 0; i < VQ_MAX_DESCRIPTORS; next = be16toh(vdir->next)) { + vdir = &vq->vq_desc[next]; + if ((be16toh(vdir->flags) & VRING_DESC_F_INDIRECT) == 0) { + _vq_record(offs, i, vdir, iov, n_iov, flags); + i++; + } else { + n_indir = be32toh(vdir->len) / 16; + vindir = paddr_map(offs, be64toh(vdir->addr), + be32toh(vdir->len)); + next = 0; + for (;;) { + vp = &vindir[next]; + _vq_record(offs, i, vp, iov, n_iov, flags); + i+=1; + if ((be16toh(vp->flags) & \ + VRING_DESC_F_NEXT) == 0) + break; + next = be16toh(vp->next); + } + paddr_unmap((void *)vindir, be32toh(vdir->len)); + } + + if ((be16toh(vdir->flags) & VRING_DESC_F_NEXT) == 0) + return (i); + } + + return (i); +} + +void +vq_relchain(struct vqueue_info *vq, struct iovec *iov, int n, uint32_t iolen) +{ + volatile struct vring_used_elem *vue; + volatile struct vring_used *vu; + uint16_t head, uidx, mask; + int i; + + mask = vq->vq_qsize - 1; + head = be16toh(vq->vq_avail->ring[vq->vq_last_avail++ & mask]); + + vu = vq->vq_used; + uidx = be16toh(vu->idx); + vue = &vu->ring[uidx++ & mask]; + vue->id = htobe16(head); + vue->len = htobe32(iolen); + vu->idx = htobe16(uidx); + + /* Clean up */ + for (i = 1; i < (n-1); i++) { + paddr_unmap((void *)iov[i].iov_base, iov[i].iov_len); + } +} Added: head/sys/dev/beri/virtio/virtio.h ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/dev/beri/virtio/virtio.h Tue Nov 25 15:58:59 2014 (r275048) @@ -0,0 +1,72 @@ +/*- + * Copyright (c) 2014 Ruslan Bukin + * All rights reserved. + * + * This software was developed by SRI International and the University of + * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) + * ("CTSRD"), as part of the DARPA CRASH research programme. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#define READ2(_sc, _reg) \ + bus_read_2((_sc)->res[0], _reg) +#define READ4(_sc, _reg) \ + bus_read_4((_sc)->res[0], _reg) +#define WRITE2(_sc, _reg, _val) \ + bus_write_2((_sc)->res[0], _reg, _val) +#define WRITE4(_sc, _reg, _val) \ + bus_write_4((_sc)->res[0], _reg, _val) + +#define PAGE_SHIFT 12 +#define VRING_ALIGN 4096 +#define NUM_QUEUES 1 + +#define VQ_ALLOC 0x01 /* set once we have a pfn */ +#define VQ_MAX_DESCRIPTORS 512 + +#define VTBLK_BLK_ID_BYTES 20 +#define VTBLK_MAXSEGS 256 + +struct vqueue_info { + uint16_t vq_qsize; /* size of this queue (a power of 2) */ + uint16_t vq_num; + uint16_t vq_flags; + uint16_t vq_last_avail; /* a recent value of vq_avail->va_idx */ + uint16_t vq_save_used; /* saved vq_used->vu_idx; see vq_endchains */ + uint32_t vq_pfn; /* PFN of virt queue (not shifted!) */ + + volatile struct vring_desc *vq_desc; /* descriptor array */ + volatile struct vring_avail *vq_avail; /* the "avail" ring */ + volatile struct vring_used *vq_used; /* the "used" ring */ +}; + +int vq_ring_ready(struct vqueue_info *vq); +int vq_has_descs(struct vqueue_info *vq); +void * paddr_map(uint32_t offset, uint32_t phys, uint32_t size); +void paddr_unmap(void *phys, uint32_t size); +int vq_getchain(uint32_t beri_mem_offset, struct vqueue_info *vq, + struct iovec *iov, int n_iov, uint16_t *flags); +void vq_relchain(struct vqueue_info *vq, struct iovec *iov, int n, uint32_t iolen); + Added: head/sys/dev/beri/virtio/virtio_block.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/dev/beri/virtio/virtio_block.c Tue Nov 25 15:58:59 2014 (r275048) @@ -0,0 +1,602 @@ +/*- + * Copyright (c) 2014 Ruslan Bukin + * All rights reserved. + * + * This software was developed by SRI International and the University of + * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) + * ("CTSRD"), as part of the DARPA CRASH research programme. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * BERI virtio block backend driver + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "pio_if.h" + +#define DPRINTF(fmt, ...) + +struct beri_vtblk_softc { + struct resource *res[1]; + bus_space_tag_t bst; + bus_space_handle_t bsh; + struct cdev *cdev; + device_t dev; + int opened; + device_t pio_recv; + device_t pio_send; + struct vqueue_info vs_queues[NUM_QUEUES]; + char ident[VTBLK_BLK_ID_BYTES]; + struct ucred *cred; + struct vnode *vnode; + struct thread *vtblk_ktd; + struct sx sc_mtx; + int beri_mem_offset; + struct md_ioctl *mdio; + struct virtio_blk_config *cfg; +}; + +static struct resource_spec beri_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { -1, 0 } +}; + +static int +vtblk_rdwr(struct beri_vtblk_softc *sc, struct iovec *iov, + int cnt, int offset, int operation, int iolen) +{ + struct vnode *vp; + struct mount *mp; + struct uio auio; + int error; + + bzero(&auio, sizeof(auio)); + + vp = sc->vnode; + + KASSERT(vp != NULL, ("file not opened")); + + auio.uio_iov = iov; + auio.uio_iovcnt = cnt; + auio.uio_offset = offset; + auio.uio_segflg = UIO_SYSSPACE; + auio.uio_rw = operation; + auio.uio_resid = iolen; + auio.uio_td = curthread; + + if (operation == 0) { + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); + error = VOP_READ(vp, &auio, IO_DIRECT, sc->cred); + VOP_UNLOCK(vp, 0); + } else { + (void) vn_start_write(vp, &mp, V_WAIT); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); + error = VOP_WRITE(vp, &auio, IO_SYNC, sc->cred); + VOP_UNLOCK(vp, 0); + vn_finished_write(mp); + } + + return (error); +} + +static void +vtblk_proc(struct beri_vtblk_softc *sc, struct vqueue_info *vq) +{ + struct iovec iov[VTBLK_MAXSEGS + 2]; + uint16_t flags[VTBLK_MAXSEGS + 2]; + struct virtio_blk_outhdr *vbh; + uint8_t *status; + off_t offset; + int iolen; + int type; + int i, n; + int err; + + n = vq_getchain(sc->beri_mem_offset, vq, iov, + VTBLK_MAXSEGS + 2, flags); + + KASSERT(n >= 2 && n <= VTBLK_MAXSEGS + 2, + ("wrong n value %d", n)); + + vbh = iov[0].iov_base; + + status = iov[n-1].iov_base; + KASSERT(iov[n-1].iov_len == 1, + ("iov_len == %d", iov[n-1].iov_len)); + + type = be32toh(vbh->type) & ~VIRTIO_BLK_T_BARRIER; + offset = be64toh(vbh->sector) * DEV_BSIZE; + + iolen = 0; + for (i = 1; i < (n-1); i++) { + iolen += iov[i].iov_len; + } + + switch (type) { + case VIRTIO_BLK_T_OUT: + case VIRTIO_BLK_T_IN: + err = vtblk_rdwr(sc, iov + 1, i - 1, + offset, type, iolen); + break; + case VIRTIO_BLK_T_GET_ID: + /* Assume a single buffer */ + strlcpy(iov[1].iov_base, sc->ident, + MIN(iov[1].iov_len, sizeof(sc->ident))); + err = 0; + break; + case VIRTIO_BLK_T_FLUSH: + /* Possible? */ + default: + err = -ENOSYS; + break; + } + + if (err < 0) { + if (err == -ENOSYS) { + *status = VIRTIO_BLK_S_UNSUPP; + } else + *status = VIRTIO_BLK_S_IOERR; + } else + *status = VIRTIO_BLK_S_OK; + + vq_relchain(vq, iov, n, 1); +} + +static int +close_file(struct beri_vtblk_softc *sc, struct thread *td) +{ + int error; + + if (sc->vnode != NULL) { + vn_lock(sc->vnode, LK_EXCLUSIVE | LK_RETRY); + sc->vnode->v_vflag &= ~VV_MD; + VOP_UNLOCK(sc->vnode, 0); + error = vn_close(sc->vnode, (FREAD|FWRITE), + sc->cred, td); + if (error != 0) + return (error); + sc->vnode = NULL; + } + + if (sc->cred != NULL) + crfree(sc->cred); + + return (0); +} + +static int +open_file(struct beri_vtblk_softc *sc, struct thread *td) +{ + struct nameidata nd; + struct vattr vattr; + int error; + int flags; + + flags = (FREAD | FWRITE); + NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, + sc->mdio->md_file, td); + error = vn_open(&nd, &flags, 0, NULL); + if (error != 0) + return (error); + NDFREE(&nd, NDF_ONLY_PNBUF); + + if (nd.ni_vp->v_type != VREG) { + return (EINVAL); + } + + error = VOP_GETATTR(nd.ni_vp, &vattr, td->td_ucred); + if (error != 0) + return (error); + + if (VOP_ISLOCKED(nd.ni_vp) != LK_EXCLUSIVE) { + vn_lock(nd.ni_vp, LK_UPGRADE | LK_RETRY); + if (nd.ni_vp->v_iflag & VI_DOOMED) { + return (1); + } + } + nd.ni_vp->v_vflag |= VV_MD; + VOP_UNLOCK(nd.ni_vp, 0); + + sc->vnode = nd.ni_vp; + sc->cred = crhold(td->td_ucred); + + return (0); +} + +static int +vtblk_notify(struct beri_vtblk_softc *sc) +{ + struct vqueue_info *vq; + int queue; + int reg; + + vq = &sc->vs_queues[0]; + if (!vq_ring_ready(vq)) + return (0); + + if (!sc->opened) + return (0); + + reg = READ2(sc, VIRTIO_MMIO_QUEUE_NOTIFY); + queue = be16toh(reg); + + KASSERT(queue == 0, ("we support single queue only")); + + /* Process new descriptors */ + vq = &sc->vs_queues[queue]; + vq->vq_save_used = be16toh(vq->vq_used->idx); + while (vq_has_descs(vq)) + vtblk_proc(sc, vq); + + /* Interrupt other side */ + PIO_SET(sc->pio_send, Q_INTR, 1); + + return (0); +} + +static int +vq_init(struct beri_vtblk_softc *sc) +{ + struct vqueue_info *vq; + uint8_t *base; + int size; + int reg; + int pfn; + + vq = &sc->vs_queues[0]; + vq->vq_qsize = NUM_QUEUES; + + reg = READ4(sc, VIRTIO_MMIO_QUEUE_PFN); + pfn = be32toh(reg); + vq->vq_pfn = pfn; + + size = vring_size(vq->vq_qsize, VRING_ALIGN); + base = paddr_map(sc->beri_mem_offset, + (pfn << PAGE_SHIFT), size); + + /* First pages are descriptors */ + vq->vq_desc = (struct vring_desc *)base; + base += vq->vq_qsize * sizeof(struct vring_desc); + + /* Then avail ring */ + vq->vq_avail = (struct vring_avail *)base; + base += (2 + vq->vq_qsize + 1) * sizeof(uint16_t); + + /* Then it's rounded up to the next page */ + base = (uint8_t *)roundup2((uintptr_t)base, VRING_ALIGN); + + /* And the last pages are the used ring */ + vq->vq_used = (struct vring_used *)base; + + /* Mark queue as allocated, and start at 0 when we use it. */ + vq->vq_flags = VQ_ALLOC; + vq->vq_last_avail = 0; + + return (0); +} + + +static void +vtblk_thread(void *arg) +{ + struct beri_vtblk_softc *sc; + int err; + + sc = arg; + + sx_xlock(&sc->sc_mtx); + for (;;) { + err = msleep(sc, &sc->sc_mtx, PCATCH | PZERO, "prd", hz); + vtblk_notify(sc); + } + sx_xunlock(&sc->sc_mtx); + + kthread_exit(); +} + +static int +setup_pio(struct beri_vtblk_softc *sc, char *name, device_t *dev) +{ + phandle_t pio_node; + struct fdt_ic *ic; + phandle_t xref; + phandle_t node; + + if ((node = ofw_bus_get_node(sc->dev)) == -1) + return (ENXIO); + + if (OF_searchencprop(node, name, &xref, + sizeof(xref)) == -1) { + return (ENXIO); + } + + pio_node = OF_node_from_xref(xref); + SLIST_FOREACH(ic, &fdt_ic_list_head, fdt_ics) { + if (ic->iph == pio_node) { + *dev = ic->dev; + PIO_CONFIGURE(*dev, PIO_OUT_ALL, + PIO_UNMASK_ALL); + return (0); + } + } + + return (ENXIO); +} + +static int +setup_offset(struct beri_vtblk_softc *sc) +{ + pcell_t dts_value[2]; + phandle_t mem_node; + phandle_t xref; + phandle_t node; + int len; + + if ((node = ofw_bus_get_node(sc->dev)) == -1) + return (ENXIO); + + if (OF_searchencprop(node, "beri-mem", &xref, + sizeof(xref)) == -1) { + return (ENXIO); + } + + mem_node = OF_node_from_xref(xref); + if ((len = OF_getproplen(mem_node, "reg")) <= 0) + return (ENXIO); + OF_getencprop(mem_node, "reg", dts_value, len); + sc->beri_mem_offset = dts_value[0]; + + return (0); +} + +static int +backend_info(struct beri_vtblk_softc *sc) +{ + struct virtio_blk_config *cfg; + uint32_t *s; + int reg; + int i; + + /* Specify that we provide block device */ + reg = htobe32(VIRTIO_ID_BLOCK); + WRITE4(sc, VIRTIO_MMIO_DEVICE_ID, reg); + + /* The number of queues we support */ + reg = htobe16(NUM_QUEUES); + WRITE2(sc, VIRTIO_MMIO_QUEUE_NUM, reg); + + /* Our features */ + reg = htobe32(VIRTIO_RING_F_INDIRECT_DESC + | VIRTIO_BLK_F_BLK_SIZE + | VIRTIO_BLK_F_SEG_MAX); + WRITE4(sc, VIRTIO_MMIO_HOST_FEATURES, reg); + + cfg = sc->cfg; + cfg->capacity = htobe64(sc->mdio->md_mediasize / DEV_BSIZE); + cfg->size_max = 0; /* not negotiated */ + cfg->seg_max = htobe32(VTBLK_MAXSEGS); + cfg->blk_size = htobe32(DEV_BSIZE); + + s = (uint32_t *)cfg; + + for (i = 0; i < sizeof(struct virtio_blk_config); i+=4) { + WRITE4(sc, VIRTIO_MMIO_CONFIG + i, *s); + s+=1; + } + + sprintf(sc->ident, "Virtio block backend"); + + return (0); +} + +static void +vtblk_intr(void *arg) +{ + struct beri_vtblk_softc *sc; + int pending; + int reg; + + sc = arg; + + reg = PIO_READ(sc->pio_recv); + + /* Ack */ + PIO_SET(sc->pio_recv, reg, 0); + + pending = htobe32(reg); + + if (pending & Q_PFN) { + vq_init(sc); + } + + if (pending & Q_NOTIFY) { + wakeup(sc); + } +} + +static int +beri_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, + int flags, struct thread *td) +{ + struct beri_vtblk_softc *sc; + int err; + + sc = dev->si_drv1; + + switch (cmd) { + case MDIOCATTACH: + /* take file as argument */ + if (sc->vnode != NULL) { + /* Already opened */ + return (1); + } + sc->mdio = (struct md_ioctl *)addr; + backend_info(sc); + DPRINTF("opening file, td 0x%08x\n", (int)td); + err = open_file(sc, td); + if (err) + return (err); + PIO_SETUP_IRQ(sc->pio_recv, vtblk_intr, sc); + sc->opened = 1; + break; + case MDIOCDETACH: + if (sc->vnode == 0) { + /* File not opened */ + return (1); + } + sc->opened = 0; + DPRINTF("closing file, td 0x%08x\n", (int)td); + err = close_file(sc, td); + if (err) + return (err); + PIO_TEARDOWN_IRQ(sc->pio_recv); + break; + default: + break; + } + + return (0); +} + +static struct cdevsw beri_cdevsw = { + .d_version = D_VERSION, + .d_ioctl = beri_ioctl, + .d_name = "virtio block backend", +}; + +static int +beri_vtblk_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "sri-cambridge,beri-vtblk")) + return (ENXIO); + + device_set_desc(dev, "SRI-Cambridge BERI block"); + return (BUS_PROBE_DEFAULT); +} + +static int +beri_vtblk_attach(device_t dev) +{ + struct beri_vtblk_softc *sc; + int error; + + sc = device_get_softc(dev); + sc->dev = dev; + + if (bus_alloc_resources(dev, beri_spec, sc->res)) { + device_printf(dev, "could not allocate resources\n"); + return (ENXIO); + } + + /* Memory interface */ + sc->bst = rman_get_bustag(sc->res[0]); + sc->bsh = rman_get_bushandle(sc->res[0]); + + sc->cfg = malloc(sizeof(struct virtio_blk_config), + M_DEVBUF, M_NOWAIT|M_ZERO); + + sx_init(&sc->sc_mtx, device_get_nameunit(sc->dev)); + + error = kthread_add(vtblk_thread, sc, NULL, &sc->vtblk_ktd, + 0, 0, "beri_virtio_block"); + if (error) { + device_printf(dev, "cannot create kthread\n"); + return (ENXIO); + } + + if (setup_offset(sc) != 0) + return (ENXIO); + if (setup_pio(sc, "pio-send", &sc->pio_send) != 0) + return (ENXIO); + if (setup_pio(sc, "pio-recv", &sc->pio_recv) != 0) + return (ENXIO); + + sc->cdev = make_dev(&beri_cdevsw, 0, UID_ROOT, GID_WHEEL, + S_IRWXU, "beri_vtblk"); + if (sc->cdev == NULL) { + device_printf(dev, "Failed to create character device.\n"); + return (ENXIO); + } + + sc->cdev->si_drv1 = sc; + return (0); +} + +static device_method_t beri_vtblk_methods[] = { + DEVMETHOD(device_probe, beri_vtblk_probe), + DEVMETHOD(device_attach, beri_vtblk_attach), + { 0, 0 } +}; + +static driver_t beri_vtblk_driver = { + "beri_vtblk", + beri_vtblk_methods, + sizeof(struct beri_vtblk_softc), +}; + +static devclass_t beri_vtblk_devclass; + +DRIVER_MODULE(beri_vtblk, simplebus, beri_vtblk_driver, + beri_vtblk_devclass, 0, 0);