diff options
Diffstat (limited to 'sys/dev/beri/virtio/network/if_vtbe.c')
-rw-r--r-- | sys/dev/beri/virtio/network/if_vtbe.c | 648 |
1 files changed, 0 insertions, 648 deletions
diff --git a/sys/dev/beri/virtio/network/if_vtbe.c b/sys/dev/beri/virtio/network/if_vtbe.c deleted file mode 100644 index de1a8ebaac97..000000000000 --- a/sys/dev/beri/virtio/network/if_vtbe.c +++ /dev/null @@ -1,648 +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 Networking Frontend - */ - -#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/endian.h> -#include <sys/lock.h> -#include <sys/mbuf.h> -#include <sys/mutex.h> -#include <sys/socket.h> -#include <sys/sockio.h> -#include <sys/sysctl.h> -#include <sys/mdioctl.h> -#include <sys/conf.h> -#include <sys/stat.h> -#include <sys/uio.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 <net/bpf.h> -#include <net/if.h> -#include <net/ethernet.h> -#include <net/if_dl.h> -#include <net/if_media.h> -#include <net/if_types.h> -#include <net/if_var.h> -#include <net/if_vlan_var.h> - -#include <netinet/in.h> -#include <netinet/udp.h> -#include <netinet/tcp.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/beri/virtio/virtio_mmio_platform.h> - -#include <dev/altera/pio/pio.h> - -#include <dev/virtio/mmio/virtio_mmio.h> -#include <dev/virtio/network/virtio_net.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, args...) printf(fmt, ##args) - -#define READ4(_sc, _reg) \ - bus_read_4((_sc)->res[0], _reg) -#define WRITE4(_sc, _reg, _val) \ - bus_write_4((_sc)->res[0], _reg, _val) - -#define VTBE_LOCK(sc) mtx_lock(&(sc)->mtx) -#define VTBE_UNLOCK(sc) mtx_unlock(&(sc)->mtx) -#define VTBE_ASSERT_LOCKED(sc) mtx_assert(&(sc)->mtx, MA_OWNED); -#define VTBE_ASSERT_UNLOCKED(sc) mtx_assert(&(sc)->mtx, MA_NOTOWNED); - -/* - * Driver data and defines. - */ -#define DESC_COUNT 256 - -struct vtbe_softc { - struct resource *res[2]; - bus_space_tag_t bst; - bus_space_handle_t bsh; - device_t dev; - if_t ifp; - int if_flags; - struct mtx mtx; - boolean_t is_attached; - - int beri_mem_offset; - device_t pio_send; - device_t pio_recv; - int opened; - - struct vqueue_info vs_queues[2]; - int vs_curq; - int hdrsize; -}; - -static struct resource_spec vtbe_spec[] = { - { SYS_RES_MEMORY, 0, RF_ACTIVE }, - { -1, 0 } -}; - -static void vtbe_txfinish_locked(struct vtbe_softc *sc); -static void vtbe_rxfinish_locked(struct vtbe_softc *sc); -static void vtbe_stop_locked(struct vtbe_softc *sc); -static int pio_enable_irq(struct vtbe_softc *sc, int enable); - -static void -vtbe_txstart_locked(struct vtbe_softc *sc) -{ - struct iovec iov[DESC_COUNT]; - struct virtio_net_hdr *vnh; - struct vqueue_info *vq; - struct iovec *tiov; - if_t ifp; - struct mbuf *m; - struct uio uio; - int enqueued; - int iolen; - int error; - int reg; - int len; - int n; - - VTBE_ASSERT_LOCKED(sc); - - /* RX queue */ - vq = &sc->vs_queues[0]; - if (!vq_has_descs(vq)) { - return; - } - - ifp = sc->ifp; - if (if_getdrvflags(ifp) & IFF_DRV_OACTIVE) { - return; - } - - enqueued = 0; - - if (!vq_ring_ready(vq)) - return; - - vq->vq_save_used = be16toh(vq->vq_used->idx); - - for (;;) { - if (!vq_has_descs(vq)) { - if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0); - break; - } - - m = if_dequeue(ifp); - if (m == NULL) { - break; - } - - n = vq_getchain(sc->beri_mem_offset, vq, iov, - DESC_COUNT, NULL); - KASSERT(n == 2, - ("Unexpected amount of descriptors (%d)", n)); - - tiov = getcopy(iov, n); - vnh = iov[0].iov_base; - memset(vnh, 0, sc->hdrsize); - - len = iov[1].iov_len; - uio.uio_resid = len; - uio.uio_iov = &tiov[1]; - uio.uio_segflg = UIO_SYSSPACE; - uio.uio_iovcnt = 1; - uio.uio_offset = 0; - uio.uio_rw = UIO_READ; - - error = m_mbuftouio(&uio, m, 0); - if (error) - panic("m_mbuftouio failed\n"); - - iolen = (len - uio.uio_resid + sc->hdrsize); - - free(tiov, M_DEVBUF); - vq_relchain(vq, iov, n, iolen); - - if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); - - BPF_MTAP(ifp, m); - m_freem(m); - - ++enqueued; - } - - if (enqueued != 0) { - reg = htobe32(VIRTIO_MMIO_INT_VRING); - WRITE4(sc, VIRTIO_MMIO_INTERRUPT_STATUS, reg); - - PIO_SET(sc->pio_send, Q_INTR, 1); - } -} - -static void -vtbe_txstart(if_t ifp) -{ - struct vtbe_softc *sc = if_getsoftc(ifp); - - VTBE_LOCK(sc); - vtbe_txstart_locked(sc); - VTBE_UNLOCK(sc); -} - -static void -vtbe_stop_locked(struct vtbe_softc *sc) -{ - if_t ifp; - - VTBE_ASSERT_LOCKED(sc); - - ifp = sc->ifp; - if_setdrvflagbits(ifp, 0, (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)); -} - -static void -vtbe_init_locked(struct vtbe_softc *sc) -{ - if_t ifp = sc->ifp; - - VTBE_ASSERT_LOCKED(sc); - - if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) - return; - - if_setdrvflagbits(ifp, IFF_DRV_RUNNING, 0); -} - -static void -vtbe_init(void *if_softc) -{ - struct vtbe_softc *sc = if_softc; - - VTBE_LOCK(sc); - vtbe_init_locked(sc); - VTBE_UNLOCK(sc); -} - -static int -vtbe_ioctl(if_t ifp, u_long cmd, caddr_t data) -{ - struct ifmediareq *ifmr; - struct vtbe_softc *sc; - struct ifreq *ifr; - int mask, error; - - sc = if_getsoftc(ifp); - ifr = (struct ifreq *)data; - - error = 0; - switch (cmd) { - case SIOCSIFFLAGS: - VTBE_LOCK(sc); - if (if_getflags(ifp) & IFF_UP) { - pio_enable_irq(sc, 1); - - if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0) { - vtbe_init_locked(sc); - } - } else { - pio_enable_irq(sc, 0); - - if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) { - vtbe_stop_locked(sc); - } - } - sc->if_flags = if_getflags(ifp); - VTBE_UNLOCK(sc); - break; - case SIOCADDMULTI: - case SIOCDELMULTI: - break; - case SIOCSIFMEDIA: - case SIOCGIFMEDIA: - ifmr = (struct ifmediareq *)data; - ifmr->ifm_count = 1; - ifmr->ifm_status = (IFM_AVALID | IFM_ACTIVE); - ifmr->ifm_active = (IFM_ETHER | IFM_10G_T | IFM_FDX); - ifmr->ifm_current = ifmr->ifm_active; - break; - case SIOCSIFCAP: - mask = if_getcapenable(ifp) ^ ifr->ifr_reqcap; - if (mask & IFCAP_VLAN_MTU) { - if_togglecapenable(ifp, IFCAP_VLAN_MTU); - } - break; - - case SIOCSIFADDR: - pio_enable_irq(sc, 1); - default: - error = ether_ioctl(ifp, cmd, data); - break; - } - - return (error); -} - -static void -vtbe_txfinish_locked(struct vtbe_softc *sc) -{ - if_t ifp; - - VTBE_ASSERT_LOCKED(sc); - - ifp = sc->ifp; -} - -static int -vq_init(struct vtbe_softc *sc) -{ - struct vqueue_info *vq; - uint8_t *base; - int size; - int reg; - int pfn; - - vq = &sc->vs_queues[sc->vs_curq]; - vq->vq_qsize = DESC_COUNT; - - 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 -vtbe_proc_rx(struct vtbe_softc *sc, struct vqueue_info *vq) -{ - struct iovec iov[DESC_COUNT]; - struct iovec *tiov; - if_t ifp; - struct uio uio; - struct mbuf *m; - int iolen; - int i; - int n; - - ifp = sc->ifp; - - n = vq_getchain(sc->beri_mem_offset, vq, iov, - DESC_COUNT, NULL); - - KASSERT(n >= 1 && n <= DESC_COUNT, - ("wrong n %d", n)); - - tiov = getcopy(iov, n); - - iolen = 0; - for (i = 1; i < n; i++) { - iolen += iov[i].iov_len; - } - - uio.uio_resid = iolen; - uio.uio_iov = &tiov[1]; - uio.uio_segflg = UIO_SYSSPACE; - uio.uio_iovcnt = (n - 1); - uio.uio_rw = UIO_WRITE; - - if ((m = m_uiotombuf(&uio, M_NOWAIT, 0, ETHER_ALIGN, - M_PKTHDR)) == NULL) { - if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); - goto done; - } - - m->m_pkthdr.rcvif = ifp; - - if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); - - CURVNET_SET(if_getvnet(ifp)); - VTBE_UNLOCK(sc); - if_input(ifp, m); - VTBE_LOCK(sc); - CURVNET_RESTORE(); - -done: - free(tiov, M_DEVBUF); - vq_relchain(vq, iov, n, iolen + sc->hdrsize); -} - -static void -vtbe_rxfinish_locked(struct vtbe_softc *sc) -{ - struct vqueue_info *vq; - int reg; - - /* TX queue */ - vq = &sc->vs_queues[1]; - if (!vq_ring_ready(vq)) - return; - - /* Process new descriptors */ - vq->vq_save_used = be16toh(vq->vq_used->idx); - - while (vq_has_descs(vq)) { - vtbe_proc_rx(sc, vq); - } - - /* Interrupt the other side */ - reg = htobe32(VIRTIO_MMIO_INT_VRING); - WRITE4(sc, VIRTIO_MMIO_INTERRUPT_STATUS, reg); - - PIO_SET(sc->pio_send, Q_INTR, 1); -} - -static void -vtbe_intr(void *arg) -{ - struct vtbe_softc *sc; - int pending; - uint32_t reg; - - sc = arg; - - VTBE_LOCK(sc); - - reg = PIO_READ(sc->pio_recv); - - /* Ack */ - PIO_SET(sc->pio_recv, reg, 0); - - pending = htobe32(reg); - if (pending & Q_SEL) { - reg = READ4(sc, VIRTIO_MMIO_QUEUE_SEL); - sc->vs_curq = be32toh(reg); - } - - if (pending & Q_PFN) { - vq_init(sc); - } - - if (pending & Q_NOTIFY) { - /* beri rx / arm tx notify */ - vtbe_txfinish_locked(sc); - } - - if (pending & Q_NOTIFY1) { - vtbe_rxfinish_locked(sc); - } - - VTBE_UNLOCK(sc); -} - -static int -vtbe_get_hwaddr(struct vtbe_softc *sc, uint8_t *hwaddr) -{ - int rnd; - - /* - * Generate MAC address, use 'bsd' + random 24 low-order bits. - */ - - rnd = arc4random() & 0x00ffffff; - - hwaddr[0] = 'b'; - hwaddr[1] = 's'; - hwaddr[2] = 'd'; - hwaddr[3] = rnd >> 16; - hwaddr[4] = rnd >> 8; - hwaddr[5] = rnd >> 0; - - return (0); -} - -static int -pio_enable_irq(struct vtbe_softc *sc, int enable) -{ - - /* - * IRQ lines should be disabled while reprogram FPGA core. - */ - - if (enable) { - if (sc->opened == 0) { - sc->opened = 1; - PIO_SETUP_IRQ(sc->pio_recv, vtbe_intr, sc); - } - } else { - if (sc->opened == 1) { - PIO_TEARDOWN_IRQ(sc->pio_recv); - sc->opened = 0; - } - } - - return (0); -} - -static int -vtbe_probe(device_t dev) -{ - - if (!ofw_bus_status_okay(dev)) - return (ENXIO); - - if (!ofw_bus_is_compatible(dev, "sri-cambridge,beri-vtnet")) - return (ENXIO); - - device_set_desc(dev, "Virtio BERI Ethernet Controller"); - return (BUS_PROBE_DEFAULT); -} - -static int -vtbe_attach(device_t dev) -{ - uint8_t macaddr[ETHER_ADDR_LEN]; - struct vtbe_softc *sc; - if_t ifp; - int reg; - - sc = device_get_softc(dev); - sc->dev = dev; - - sc->hdrsize = sizeof(struct virtio_net_hdr); - - if (bus_alloc_resources(dev, vtbe_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]); - - mtx_init(&sc->mtx, device_get_nameunit(sc->dev), - MTX_NETWORK_LOCK, MTX_DEF); - - 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); - - /* Setup MMIO */ - - /* Specify that we provide network device */ - reg = htobe32(VIRTIO_ID_NETWORK); - WRITE4(sc, VIRTIO_MMIO_DEVICE_ID, reg); - - /* The number of desc we support */ - reg = htobe32(DESC_COUNT); - WRITE4(sc, VIRTIO_MMIO_QUEUE_NUM_MAX, reg); - - /* Our features */ - reg = htobe32(VIRTIO_NET_F_MAC | - VIRTIO_F_NOTIFY_ON_EMPTY); - WRITE4(sc, VIRTIO_MMIO_HOST_FEATURES, reg); - - /* Get MAC */ - if (vtbe_get_hwaddr(sc, macaddr)) { - device_printf(sc->dev, "can't get mac\n"); - return (ENXIO); - } - - /* Set up the ethernet interface. */ - sc->ifp = ifp = if_alloc(IFT_ETHER); - if_setbaudrate(ifp, IF_Gbps(10)); - if_setsoftc(ifp, sc); - if_initname(ifp, device_get_name(dev), device_get_unit(dev)); - if_setflags(ifp, IFF_BROADCAST | IFF_SIMPLEX | - IFF_MULTICAST | IFF_PROMISC); - if_setcapabilities(ifp, IFCAP_VLAN_MTU); - if_setcapenable(ifp, if_getcapabilities(ifp)); - if_setstartfn(ifp, vtbe_txstart); - if_setioctlfn(ifp, vtbe_ioctl); - if_setinitfn(ifp, vtbe_init); - if_setsendqlen(ifp, DESC_COUNT - 1); - if_setsendqready(ifp); - if_setifheaderlen(ifp, sizeof(struct ether_vlan_header)); - - /* All ready to run, attach the ethernet interface. */ - ether_ifattach(ifp, macaddr); - - sc->is_attached = true; - - return (0); -} - -static device_method_t vtbe_methods[] = { - DEVMETHOD(device_probe, vtbe_probe), - DEVMETHOD(device_attach, vtbe_attach), - { 0, 0 } -}; - -static driver_t vtbe_driver = { - "vtbe", - vtbe_methods, - sizeof(struct vtbe_softc), -}; - -DRIVER_MODULE(vtbe, simplebus, vtbe_driver, 0, 0); -MODULE_DEPEND(vtbe, ether, 1, 1, 1); |