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 <[email protected]>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <sys/conf.h>
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/event.h>
+#include <sys/selinfo.h>
+#include <sys/endian.h>
+#include <sys/rwlock.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <dev/beri/virtio/virtio.h>
+#include <dev/virtio/virtio.h>
+#include <dev/virtio/virtqueue.h>
+#include <dev/virtio/virtio_ring.h>
+
+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 <[email protected]>
+ * 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 <[email protected]>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/conf.h>
+#include <sys/stat.h>
+#include <sys/endian.h>
+#include <sys/disk.h>
+#include <sys/vnode.h>
+#include <sys/fcntl.h>
+#include <sys/kthread.h>
+#include <sys/buf.h>
+#include <sys/mdioctl.h>
+#include <sys/namei.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/beri/virtio/virtio.h>
+#include <dev/beri/virtio/virtio_mmio_platform.h>
+#include <dev/altera/pio/pio.h>
+#include <dev/virtio/mmio/virtio_mmio.h>
+#include <dev/virtio/block/virtio_blk.h>
+#include <dev/virtio/virtio.h>
+#include <dev/virtio/virtio_ring.h>
+
+#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);
_______________________________________________
[email protected] mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "[email protected]"

Reply via email to