diff options
Diffstat (limited to 'sys/dev/beri/virtio/virtio_block.c')
-rw-r--r-- | sys/dev/beri/virtio/virtio_block.c | 553 |
1 files changed, 0 insertions, 553 deletions
diff --git a/sys/dev/beri/virtio/virtio_block.c b/sys/dev/beri/virtio/virtio_block.c deleted file mode 100644 index 0d4a37ea9772..000000000000 --- a/sys/dev/beri/virtio/virtio_block.c +++ /dev/null @@ -1,553 +0,0 @@ -/*- - * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com> - * 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/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_ids.h> -#include <dev/virtio/virtio_config.h> -#include <dev/virtio/virtio_ring.h> - -#include "pio_if.h" - -#define DPRINTF(fmt, ...) - -/* We use indirect descriptors */ -#define NUM_DESCS 1 -#define NUM_QUEUES 1 - -#define VTBLK_BLK_ID_BYTES 20 -#define VTBLK_MAXSEGS 256 - -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); - } 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); - 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; - struct iovec *tiov; - 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)); - - tiov = getcopy(iov, 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, tiov + 1, i - 1, - offset, type, iolen); - break; - case VIRTIO_BLK_T_GET_ID: - /* Assume a single buffer */ - strncpy(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; - - free(tiov, M_DEVBUF); - 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); - 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); - error = vn_open(&nd, &flags, 0, NULL); - if (error != 0) - return (error); - NDFREE_PNBUF(&nd); - - 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 (VN_IS_DOOMED(nd.ni_vp)) { - return (1); - } - } - nd.ni_vp->v_vflag |= VV_MD; - VOP_UNLOCK(nd.ni_vp); - - 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 the other side */ - if ((be16toh(vq->vq_avail->flags) & VRING_AVAIL_F_NO_INTERRUPT) == 0) { - reg = htobe32(VIRTIO_MMIO_INT_VRING); - WRITE4(sc, VIRTIO_MMIO_INTERRUPT_STATUS, reg); - 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_DESCS; - - 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 -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); - - /* Queue size */ - reg = htobe32(NUM_DESCS); - WRITE4(sc, VIRTIO_MMIO_QUEUE_NUM_MAX, 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; - } - - strncpy(sc->ident, "Virtio block backend", sizeof(sc->ident)); - - 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 == NULL) { - /* 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(dev, &sc->beri_mem_offset) != 0) - return (ENXIO); - if (setup_pio(dev, "pio-send", &sc->pio_send) != 0) - return (ENXIO); - if (setup_pio(dev, "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), -}; - -DRIVER_MODULE(beri_vtblk, simplebus, beri_vtblk_driver, 0, 0); |