[PATCH] pc386: Add virtio network driver
Jinhyun
jinhyun at konkuk.ac.kr
Mon May 30 07:35:13 UTC 2016
>From 1eebb8a11846e1b760f0362c77aac19d5a294bb8 Mon Sep 17 00:00:00 2001
From: Jin-Hyun <jinhyun at konkuk.ac.kr>
Date: Mon, 30 May 2016 15:40:43 +0900
Subject: [PATCH] virtio
---
c/src/lib/libbsp/i386/pc386/Makefile.am | 18 +
c/src/lib/libbsp/i386/pc386/virtio/HOWTOUSE | 62 +
c/src/lib/libbsp/i386/pc386/virtio/if_vtnet.c | 4151
++++++++++++++++++++++
c/src/lib/libbsp/i386/pc386/virtio/if_vtnetvar.h | 391 ++
c/src/lib/libbsp/i386/pc386/virtio/virtio.c | 291 ++
c/src/lib/libbsp/i386/pc386/virtio/virtio.h | 255 ++
c/src/lib/libbsp/i386/pc386/virtio/virtio_net.h | 218 ++
c/src/lib/libbsp/i386/pc386/virtio/virtio_pci.c | 1526 ++++++++
c/src/lib/libbsp/i386/pc386/virtio/virtio_pci.h | 166 +
c/src/lib/libbsp/i386/pc386/virtio/virtio_ring.h | 180 +
c/src/lib/libbsp/i386/pc386/virtio/virtqueue.c | 963 +++++
c/src/lib/libbsp/i386/pc386/virtio/virtqueue.h | 127 +
12 files changed, 8348 insertions(+)
create mode 100644 c/src/lib/libbsp/i386/pc386/virtio/HOWTOUSE
create mode 100644 c/src/lib/libbsp/i386/pc386/virtio/if_vtnet.c
create mode 100644 c/src/lib/libbsp/i386/pc386/virtio/if_vtnetvar.h
create mode 100644 c/src/lib/libbsp/i386/pc386/virtio/virtio.c
create mode 100644 c/src/lib/libbsp/i386/pc386/virtio/virtio.h
create mode 100644 c/src/lib/libbsp/i386/pc386/virtio/virtio_net.h
create mode 100644 c/src/lib/libbsp/i386/pc386/virtio/virtio_pci.c
create mode 100644 c/src/lib/libbsp/i386/pc386/virtio/virtio_pci.h
create mode 100644 c/src/lib/libbsp/i386/pc386/virtio/virtio_ring.h
create mode 100644 c/src/lib/libbsp/i386/pc386/virtio/virtqueue.c
create mode 100644 c/src/lib/libbsp/i386/pc386/virtio/virtqueue.h
diff --git a/c/src/lib/libbsp/i386/pc386/Makefile.am
b/c/src/lib/libbsp/i386/pc386/Makefile.am
index a8c9ec1..d4ef7a2 100644
--- a/c/src/lib/libbsp/i386/pc386/Makefile.am
+++ b/c/src/lib/libbsp/i386/pc386/Makefile.am
@@ -226,6 +226,23 @@ noinst_PROGRAMS += 3c509.rel
3c509_rel_LDFLAGS = $(RTEMS_RELLDFLAGS)
endif
+if HAS_NETWORKING
+vtnet_CPPFLAGS = -D__INSIDE_RTEMS_BSD_TCPIP_STACK__
+noinst_PROGRAMS += vtnet.rel
+vtnet_rel_SOURCES = virtio/if_vtnet.c
+vtnet_rel_SOURCES += virtio/if_vtnetvar.h
+vtnet_rel_SOURCES += virtio/virtio_net.h
+vtnet_rel_SOURCES += virtio/virtio_pci.c
+vtnet_rel_SOURCES += virtio/virtio_pci.h
+vtnet_rel_SOURCES += virtio/virtqueue.c
+vtnet_rel_SOURCES += virtio/virtqueue.h
+vtnet_rel_SOURCES += virtio/virtio.c
+vtnet_rel_SOURCES += virtio/virtio.h
+vtnet_rel_SOURCES += virtio/virtio_ring.h
+vtnet_rel_CPPFLAGS = $(AM_CPPFLAGS) $(vtnet_CPPFLAGS)
+vtnet_rel_LDFLAGS = $(RTEMS_RELLDFLAGS)
+endif
+
libbsp_a_LIBADD = ../../../libcpu/@RTEMS_CPU@/cache.rel
libbsp_a_LIBADD += ../../../libcpu/@RTEMS_CPU@/page.rel
libbsp_a_LIBADD += ../../../libcpu/@RTEMS_CPU@/score.rel
@@ -240,6 +257,7 @@ if HAS_NETWORKING
libbsp_a_LIBADD += ne2000.rel
libbsp_a_LIBADD += wd8003.rel
libbsp_a_LIBADD += 3c509.rel
+libbsp_a_LIBADD += vtnet.rel
endif
EXTRA_DIST += HOWTO
diff --git a/c/src/lib/libbsp/i386/pc386/virtio/HOWTOUSE
b/c/src/lib/libbsp/i386/pc386/virtio/HOWTOUSE
new file mode 100644
index 0000000..eb91f80
--- /dev/null
+++ b/c/src/lib/libbsp/i386/pc386/virtio/HOWTOUSE
@@ -0,0 +1,62 @@
+1. Introduction
+---------------
+
+ This document explains how to setup RTEMS so that RTEMS applications
can be
+built for and run with the virtio network driver. The virtio network driver
is
+ported from FreeBSD release 10.0.0, and RTEMS specific changes of the
driver are
+ ifdef'ed by RTEMS_VIRTIO_NET.
+
+
+2. Building RTEMS kernel with the virtio network driver
+---------------------------------------------------
+
+ When running configure, you need to use following values for the
listed
+options with an i386-rtems toolset. The original virtio network driver of
+FreeBSD works with various CPUs, but the virtio network driver for RTEMS is
only
+tested on i386-pc386, yet.
+
+ --target=i386-rtems
+ --enable-rtemsbsp=pc386
+
+
+3. Building RTEMS applications with virtio network driver
+--------------------------------------------------------
+
+ You can use virtio network driver in the same way with other network
drivers
+of RTEMS, but you have to set the mbuf cluster space so that it can be
larger
+than 512KB because the virtio network driver uses 256 mbuf clusters for Rx.
An
+example of network configuration is as follows.
+
+ extern int rtems_vtnet_driver_attach(struct rtems_bsdnet_ifconfig *,
int)
+ static struct rtems_bsdnet_ifconfig virtio_config[]={
+ {
+ "vio1", rtems_vtnet_driver_attach, NULL,
+ },
+ };
+ struct rtems_bsdnet_config rtems_bsdnet_config = {
+ virtio_config, /* link to next interface */
+ 0, /* Use BOOTP to get network
configuration */
+ 150, /* Network task priority */
+ 128*1024, /* MBUF space */
+ 512*1024, /* MBUF cluster space */
+ ...
+ }
+
+4. Running virtual machine with virtio network device
+-----------------------------------------------------
+
+ To use the virtio network driver, the hypervisor has to support
virtio
+network device. The current implementation can successfully run with KVM
and
+VirtualBox. Example commands for these hypervisors are as follows.
+
+ For KVM:
+
+ $ qemu-system-i386 -enable-kvm -no-shutdown -m 128 \
+ -boot a -fda rtems-boot.img \
+ -hda hdd.img -net nic,model=virtio -net
tap,ifname=tap0,script=no \
+ -monitor stdio
+
+ For VirtualBox:
+
+ $ VBoxManage modifyvm RTEMS-4.11 --nictype1 virtio
+ $ VBoxManage startvm RTEMS-4.11
diff --git a/c/src/lib/libbsp/i386/pc386/virtio/if_vtnet.c
b/c/src/lib/libbsp/i386/pc386/virtio/if_vtnet.c
new file mode 100644
index 0000000..1ee9199
--- /dev/null
+++ b/c/src/lib/libbsp/i386/pc386/virtio/if_vtnet.c
@@ -0,0 +1,4151 @@
+/**
+ * @file if_vtnet.c
+ * @brief Driver for virtio network devices
+ */
+
+/*
+ * Authors: Jin-Hyun Kim <jinhyun at konkuk.ac.kr>,
+ * and Hyun-Wook Jin <jinh at konkuk.ac.kr>, http://sslab.konkuk.ac.kr
+ * Ported from FreeBSD to RTEMS March 16
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.NET
+ */
+
+/*-
+ * Copyright (c) 2011, Bryan Venteicher <bryanv at FreeBSD.org>
+ * All rights reserved.
+ *
+ * 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 unmodified, 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 ``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 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: release/10.0.0/sys/dev/virtio/network/if_vtnet.c 256066
2013-10-05 18:07:24Z bryanv $
+ */
+
+#define VTNET_LEGACY_TX
+#define RTEMS_VIRTIO_NET
+
+#include <rtems.h>
+#include <rtems/rtems_bsdnet.h>
+
+#include <bsp.h>
+
+#include <sys/mbuf.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/systm.h>
+
+#include <net/if.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include "virtio.h"
+#include "virtqueue.h"
+#include "virtio_pci.h"
+#include "virtio_net.h"
+#include "if_vtnetvar.h"
+
+#ifdef RTEMS_VIRTIO_NET
+static struct vtnet_softc vtnet_softc;
+static rtems_interval vtnet_ticksPerSecond;
+static const char vtnet_nameunit[] = "vtnet";
+
+#define IFF_DRV_RUNNING IFF_RUNNING
+#define if_drv_flags if_flags
+
+#define device_get_softc(dev) &vtnet_softc
+#define device_get_nameunit(dev) vtnet_nameunit
+#define IF_LLADDR(ifp) (sc->arpcom.ac_enaddr)
+
+#define IFQ_SET_MAXLEN(ifq, len) ((ifq)->ifq_maxlen=(len))
+#define IFQ_DRV_IS_EMPTY(ifq) (0==(ifq)->ifq_head)
+#define IFQ_DRV_DEQUEUE(ifq,m) IF_DEQUEUE((ifq),(m))
+#define IFQ_DRV_PREPEND(ifq,m) IF_PREPEND((ifq),(m))
+
+int rtems_vtnet_driver_attach(struct rtems_bsdnet_ifconfig *config, int
attaching);
+static void vtnet_daemon( void *xsc );
+#endif
+
+#ifdef NOTUSED
+static int vtnet_modevent(module_t, int, void *);
+
+static int vtnet_probe(device_t);
+static int vtnet_detach(device_t);
+static int vtnet_suspend(device_t);
+static int vtnet_resume(device_t);
+static int vtnet_shutdown(device_t);
+static int vtnet_attach_completed(device_t);
+static int vtnet_config_change(device_t);
+
+static int vtnet_alloc_rx_filters(struct vtnet_softc *);
+static void vtnet_free_rx_filters(struct vtnet_softc *);
+static int vtnet_change_mtu(struct vtnet_softc *, int);
+
+static int vtnet_rxq_csum(struct vtnet_rxq *, struct mbuf *,
+ struct virtio_net_hdr *);
+static void vtnet_rxq_tq_intr(void *, int);
+
+static int vtnet_txq_offload_ctx(struct vtnet_txq *, struct mbuf *,
+ int *, int *, int *);
+static int vtnet_txq_offload_tso(struct vtnet_txq *, struct mbuf *,
int,
+ int, struct virtio_net_hdr *);
+static struct mbuf *
+ vtnet_txq_offload(struct vtnet_txq *, struct mbuf *,
+ struct virtio_net_hdr *);
+#ifdef VTNET_LEGACY_TX
+static void vtnet_start_locked(struct vtnet_txq *, struct ifnet *);
+#else
+static int vtnet_txq_mq_start_locked(struct vtnet_txq *, struct mbuf
*);
+static int vtnet_txq_mq_start(struct ifnet *, struct mbuf *);
+static void vtnet_txq_tq_deferred(void *, int);
+#endif
+static void vtnet_txq_tq_intr(void *, int);
+
+#ifndef VTNET_LEGACY_TX
+static void vtnet_qflush(struct ifnet *);
+#endif
+
+static void vtnet_rxq_accum_stats(struct vtnet_rxq *,
+ struct vtnet_rxq_stats *);
+static void vtnet_txq_accum_stats(struct vtnet_txq *,
+ struct vtnet_txq_stats *);
+static void vtnet_accumulate_stats(struct vtnet_softc *);
+
+static void vtnet_start_taskqueues(struct vtnet_softc *);
+static void vtnet_free_taskqueues(struct vtnet_softc *);
+static void vtnet_drain_taskqueues(struct vtnet_softc *);
+
+static void vtnet_init_rx_filters(struct vtnet_softc *);
+
+static void vtnet_free_ctrl_vq(struct vtnet_softc *);
+static void vtnet_exec_ctrl_cmd(struct vtnet_softc *, void *,
+ struct sglist *, int, int);
+static int vtnet_ctrl_mac_cmd(struct vtnet_softc *, uint8_t *);
+static int vtnet_ctrl_mq_cmd(struct vtnet_softc *, uint16_t);
+static int vtnet_ctrl_rx_cmd(struct vtnet_softc *, int, int);
+static int vtnet_set_promisc(struct vtnet_softc *, int);
+static int vtnet_set_allmulti(struct vtnet_softc *, int);
+static void vtnet_attach_disable_promisc(struct vtnet_softc *);
+static void vtnet_rx_filter(struct vtnet_softc *);
+static void vtnet_rx_filter_mac(struct vtnet_softc *);
+static int vtnet_exec_vlan_filter(struct vtnet_softc *, int, uint16_t);
+static void vtnet_rx_filter_vlan(struct vtnet_softc *);
+static void vtnet_update_vlan_filter(struct vtnet_softc *, int,
uint16_t);
+static void vtnet_register_vlan(void *, struct ifnet *, uint16_t);
+static void vtnet_unregister_vlan(void *, struct ifnet *, uint16_t);
+
+static int vtnet_ifmedia_upd(struct ifnet *);
+static void vtnet_ifmedia_sts(struct ifnet *, struct ifmediareq *);
+static void vtnet_vlan_tag_remove(struct mbuf *);
+
+static void vtnet_setup_rxq_sysctl(struct sysctl_ctx_list *,
+ struct sysctl_oid_list *, struct vtnet_rxq *);
+static void vtnet_setup_txq_sysctl(struct sysctl_ctx_list *,
+ struct sysctl_oid_list *, struct vtnet_txq *);
+static void vtnet_setup_queue_sysctl(struct vtnet_softc *);
+static void vtnet_setup_sysctl(struct vtnet_softc *);
+
+static int vtnet_tunable_int(struct vtnet_softc *, const char *, int);
+#endif
+
+static int vtnet_attach(device_t);
+
+static void vtnet_negotiate_features(struct vtnet_softc *);
+static void vtnet_setup_features(struct vtnet_softc *);
+static int vtnet_init_rxq(struct vtnet_softc *, int);
+static int vtnet_init_txq(struct vtnet_softc *, int);
+static int vtnet_alloc_rxtx_queues(struct vtnet_softc *);
+static void vtnet_free_rxtx_queues(struct vtnet_softc *);
+static int vtnet_alloc_virtqueues(struct vtnet_softc *);
+static int vtnet_setup_interface(struct vtnet_softc *);
+static int vtnet_ioctl(struct ifnet *, u_long, caddr_t);
+
+static int vtnet_rxq_populate(struct vtnet_rxq *);
+static void vtnet_rxq_free_mbufs(struct vtnet_rxq *);
+static struct mbuf *
+ vtnet_rx_alloc_buf(struct vtnet_softc *, int , struct mbuf
**);
+static int vtnet_rxq_replace_lro_nomgr_buf(struct vtnet_rxq *,
+ struct mbuf *, int);
+static int vtnet_rxq_replace_buf(struct vtnet_rxq *, struct mbuf *,
int);
+static int vtnet_rxq_enqueue_buf(struct vtnet_rxq *, struct mbuf *);
+static int vtnet_rxq_new_buf(struct vtnet_rxq *);
+static void vtnet_rxq_discard_merged_bufs(struct vtnet_rxq *, int);
+static void vtnet_rxq_discard_buf(struct vtnet_rxq *, struct mbuf *);
+static int vtnet_rxq_merged_eof(struct vtnet_rxq *, struct mbuf *,
int);
+static void vtnet_rxq_input(struct vtnet_rxq *, struct mbuf *,
+ struct virtio_net_hdr *);
+static int vtnet_rxq_eof(struct vtnet_rxq *);
+static void vtnet_rx_vq_intr(void *);
+static void vtnet_tx_start_all(struct vtnet_softc *);
+
+static void vtnet_txq_free_mbufs(struct vtnet_txq *);
+static int vtnet_txq_enqueue_buf(struct vtnet_txq *, struct mbuf **,
+ struct vtnet_tx_header *);
+static int vtnet_txq_encap(struct vtnet_txq *, struct mbuf **);
+static void vtnet_start_locked(struct vtnet_txq *, struct ifnet *);
+static void vtnet_start(struct ifnet *);
+static void vtnet_txq_eof(struct vtnet_txq *);
+static void vtnet_tx_vq_intr(void *);
+
+static int vtnet_watchdog(struct vtnet_txq *);
+static void vtnet_tick(void *);
+
+static void vtnet_drain_rxtx_queues(struct vtnet_softc *);
+static void vtnet_stop_rendezvous(struct vtnet_softc *);
+static void vtnet_stop(struct vtnet_softc *);
+static int vtnet_virtio_reinit(struct vtnet_softc *);
+static int vtnet_init_rx_queues(struct vtnet_softc *);
+static int vtnet_init_tx_queues(struct vtnet_softc *);
+static int vtnet_init_rxtx_queues(struct vtnet_softc *);
+static void vtnet_set_active_vq_pairs(struct vtnet_softc *);
+static int vtnet_reinit(struct vtnet_softc *);
+static void vtnet_init_locked(struct vtnet_softc *);
+static void vtnet_init(void *);
+
+static int vtnet_is_link_up(struct vtnet_softc *);
+static void vtnet_update_link_status(struct vtnet_softc *);
+static void vtnet_get_hwaddr(struct vtnet_softc *);
+static void vtnet_set_hwaddr(struct vtnet_softc *);
+
+static int vtnet_rxq_enable_intr(struct vtnet_rxq *);
+static void vtnet_rxq_disable_intr(struct vtnet_rxq *);
+static int vtnet_txq_enable_intr(struct vtnet_txq *);
+static void vtnet_txq_disable_intr(struct vtnet_txq *);
+static void vtnet_enable_rx_interrupts(struct vtnet_softc *);
+static void vtnet_enable_tx_interrupts(struct vtnet_softc *);
+static void vtnet_enable_interrupts(struct vtnet_softc *);
+static void vtnet_disable_rx_interrupts(struct vtnet_softc *);
+static void vtnet_disable_tx_interrupts(struct vtnet_softc *);
+static void vtnet_disable_interrupts(struct vtnet_softc *);
+
+#ifdef NOTUSED
+/* Tunables. */
+static int vtnet_csum_disable = 0;
+TUNABLE_INT("hw.vtnet.csum_disable", &vtnet_csum_disable);
+static int vtnet_tso_disable = 0;
+TUNABLE_INT("hw.vtnet.tso_disable", &vtnet_tso_disable);
+static int vtnet_lro_disable = 0;
+TUNABLE_INT("hw.vtnet.lro_disable", &vtnet_lro_disable);
+static int vtnet_mq_disable = 0;
+TUNABLE_INT("hw.vtnet.mq_disable", &vtnet_mq_disable);
+static int vtnet_mq_max_pairs = 0;
+TUNABLE_INT("hw.vtnet.mq_max_pairs", &vtnet_mq_max_pairs);
+static int vtnet_rx_process_limit = 512;
+TUNABLE_INT("hw.vtnet.rx_process_limit", &vtnet_rx_process_limit);
+
+/*
+ * Reducing the number of transmit completed interrupts can improve
+ * performance. To do so, the define below keeps the Tx vq interrupt
+ * disabled and adds calls to vtnet_txeof() in the start and watchdog
+ * paths. The price to pay for this is the m_free'ing of transmitted
+ * mbufs may be delayed until the watchdog fires.
+ *
+ * BMV: Reintroduce this later as a run-time option, if it makes
+ * sense after the EVENT_IDX feature is supported.
+ *
+ * #define VTNET_TX_INTR_MODERATION
+ */
+
+static uma_zone_t vtnet_tx_header_zone;
+
+static struct virtio_feature_desc vtnet_feature_desc[] = {
+ { VIRTIO_NET_F_CSUM, "TxChecksum" },
+ { VIRTIO_NET_F_GUEST_CSUM, "RxChecksum" },
+ { VIRTIO_NET_F_MAC, "MacAddress" },
+ { VIRTIO_NET_F_GSO, "TxAllGSO" },
+ { VIRTIO_NET_F_GUEST_TSO4, "RxTSOv4" },
+ { VIRTIO_NET_F_GUEST_TSO6, "RxTSOv6" },
+ { VIRTIO_NET_F_GUEST_ECN, "RxECN" },
+ { VIRTIO_NET_F_GUEST_UFO, "RxUFO" },
+ { VIRTIO_NET_F_HOST_TSO4, "TxTSOv4" },
+ { VIRTIO_NET_F_HOST_TSO6, "TxTSOv6" },
+ { VIRTIO_NET_F_HOST_ECN, "TxTSOECN" },
+ { VIRTIO_NET_F_HOST_UFO, "TxUFO" },
+ { VIRTIO_NET_F_MRG_RXBUF, "MrgRxBuf" },
+ { VIRTIO_NET_F_STATUS, "Status" },
+ { VIRTIO_NET_F_CTRL_VQ, "ControlVq" },
+ { VIRTIO_NET_F_CTRL_RX, "RxMode" },
+ { VIRTIO_NET_F_CTRL_VLAN, "VLanFilter" },
+ { VIRTIO_NET_F_CTRL_RX_EXTRA, "RxModeExtra" },
+ { VIRTIO_NET_F_GUEST_ANNOUNCE, "GuestAnnounce" },
+ { VIRTIO_NET_F_MQ, "Multiqueue" },
+ { VIRTIO_NET_F_CTRL_MAC_ADDR, "SetMacAddress" },
+
+ { 0, NULL }
+};
+
+static device_method_t vtnet_methods[] = {
+ /* Device methods. */
+ DEVMETHOD(device_probe, vtnet_probe),
+ DEVMETHOD(device_attach, vtnet_attach),
+ DEVMETHOD(device_detach, vtnet_detach),
+ DEVMETHOD(device_suspend, vtnet_suspend),
+ DEVMETHOD(device_resume, vtnet_resume),
+ DEVMETHOD(device_shutdown, vtnet_shutdown),
+
+ /* VirtIO methods. */
+ DEVMETHOD(virtio_attach_completed, vtnet_attach_completed),
+ DEVMETHOD(virtio_config_change, vtnet_config_change),
+
+ DEVMETHOD_END
+};
+
+static driver_t vtnet_driver = {
+ "vtnet",
+ vtnet_methods,
+ sizeof(struct vtnet_softc)
+};
+static devclass_t vtnet_devclass;
+
+DRIVER_MODULE(vtnet, virtio_pci, vtnet_driver, vtnet_devclass,
+ vtnet_modevent, 0);
+MODULE_VERSION(vtnet, 1);
+MODULE_DEPEND(vtnet, virtio, 1, 1, 1);
+
+static int
+vtnet_modevent(module_t mod, int type, void *unused)
+{
+ int error;
+
+ error = 0;
+
+ switch (type) {
+ case MOD_LOAD:
+ vtnet_tx_header_zone = uma_zcreate("vtnet_tx_hdr",
+ sizeof(struct vtnet_tx_header),
+ NULL, NULL, NULL, NULL, 0, 0);
+ break;
+ case MOD_QUIESCE:
+ case MOD_UNLOAD:
+ if (uma_zone_get_cur(vtnet_tx_header_zone) > 0)
+ error = EBUSY;
+ else if (type == MOD_UNLOAD) {
+ uma_zdestroy(vtnet_tx_header_zone);
+ vtnet_tx_header_zone = NULL;
+ }
+ break;
+ case MOD_SHUTDOWN:
+ break;
+ default:
+ error = EOPNOTSUPP;
+ break;
+ }
+
+ return (error);
+}
+
+static int
+vtnet_probe(device_t dev)
+{
+
+ if (virtio_get_device_type(dev) != VIRTIO_ID_NETWORK)
+ return (ENXIO);
+
+ device_set_desc(dev, "VirtIO Networking Adapter");
+
+ return (BUS_PROBE_DEFAULT);
+}
+#endif
+
+#ifdef RTEMS_VIRTIO_NET
+int
+rtems_vtnet_driver_attach(
+ struct rtems_bsdnet_ifconfig *config,
+ int attaching
+){
+ struct vtnet_softc *sc;
+ int error;
+
+ sc = &vtnet_softc;
+ sc->config = config;
+ memset(sc, 0, sizeof(struct vtnet_softc));
+ vtnet_ticksPerSecond = rtems_clock_get_ticks_per_second();
+
+ error = rtems_vtpci_attach( config, &sc->vtpci_softc );
+ if ( error ) {
+ device_printf(dev, "cannot attach pci device\n");
+ return error;
+ }
+
+ sc->vtnet_flags = 0;
+ sc->vtnet_hdr_size = 0;
+ sc->vtnet_max_vq_pairs = 1;
+ sc->vtnet_rx_process_limit = 512;
+
+ error = vtnet_attach(attaching);
+
+ return (error);
+}
+
+static void
+vtnet_daemon( void *xsc )
+{
+ struct vtpci_softc *sc;
+ struct vtpci_virtqueue *vqx;
+ rtems_event_set events;
+ int i;
+
+ sc = ((struct vtnet_softc *)xsc)->vtpci_softc;
+
+ while ( 1 ) {
+ rtems_bsdnet_event_receive( RTEMS_EVENT_1,
+ RTEMS_WAIT | RTEMS_EVENT_ANY, RTEMS_NO_TIMEOUT, &events );
+
+ vqx = &sc->vtpci_vqs[0];
+ for (i = 0; i < sc->vtpci_nvqs; i++, vqx++) {
+ if (vqx->vtv_no_intr == 0)
+ virtqueue_intr(vqx->vtv_vq);
+ }
+ }
+}
+#endif
+
+static int
+vtnet_attach(device_t dev)
+{
+ struct vtnet_softc *sc;
+ int error;
+
+ sc = device_get_softc(dev);
+ sc->vtnet_dev = dev;
+
+#ifdef NOTUSED
+ /* Register our feature descriptions. */
+ virtio_set_feature_desc(dev, vtnet_feature_desc);
+
+ VTNET_CORE_LOCK_INIT(sc);
+ callout_init_mtx(&sc->vtnet_tick_ch, VTNET_CORE_MTX(sc), 0);
+
+ vtnet_setup_sysctl(sc);
+#endif
+ vtnet_setup_features(sc);
+#ifdef NOTUSED
+
+ error = vtnet_alloc_rx_filters(sc);
+ if (error) {
+ device_printf(dev, "cannot allocate Rx filters\n");
+ goto fail;
+ }
+#endif
+
+ error = vtnet_alloc_rxtx_queues(sc);
+ if (error) {
+ device_printf(dev, "cannot allocate queues\n");
+ goto fail;
+ }
+
+ error = vtnet_alloc_virtqueues(sc);
+ if (error) {
+ device_printf(dev, "cannot allocate virtqueues\n");
+ goto fail;
+ }
+
+ error = vtnet_setup_interface(sc);
+ if (error) {
+ device_printf(dev, "cannot setup interface\n");
+ goto fail;
+ }
+
+ error = virtio_setup_intr(dev, INTR_TYPE_NET);
+ if (error) {
+ device_printf(dev, "cannot setup virtqueue interrupts\n");
+ /* BMV: This will crash if during boot! */
+#ifdef NOTUSED
+ ether_ifdetach(sc->vtnet_ifp);
+#endif
+ goto fail;
+ }
+
+#ifdef NOTUSED
+ vtnet_start_taskqueues(sc);
+#endif
+
+fail:
+#ifdef NOTUSED
+ if (error)
+ vtnet_detach(dev);
+#endif
+
+ return (error);
+}
+
+#ifdef NOTUSED
+static int
+vtnet_detach(device_t dev)
+{
+ struct vtnet_softc *sc;
+ struct ifnet *ifp;
+
+ sc = device_get_softc(dev);
+ ifp = sc->vtnet_ifp;
+
+ if (device_is_attached(dev)) {
+ VTNET_CORE_LOCK(sc);
+ vtnet_stop(sc);
+ VTNET_CORE_UNLOCK(sc);
+
+ callout_drain(&sc->vtnet_tick_ch);
+ vtnet_drain_taskqueues(sc);
+
+ ether_ifdetach(ifp);
+ }
+
+ vtnet_free_taskqueues(sc);
+
+ if (sc->vtnet_vlan_attach != NULL) {
+ EVENTHANDLER_DEREGISTER(vlan_config, sc->vtnet_vlan_attach);
+ sc->vtnet_vlan_attach = NULL;
+ }
+ if (sc->vtnet_vlan_detach != NULL) {
+ EVENTHANDLER_DEREGISTER(vlan_unconfg,
sc->vtnet_vlan_detach);
+ sc->vtnet_vlan_detach = NULL;
+ }
+
+ ifmedia_removeall(&sc->vtnet_media);
+
+ if (ifp != NULL) {
+ if_free(ifp);
+ sc->vtnet_ifp = NULL;
+ }
+
+ vtnet_free_rxtx_queues(sc);
+ vtnet_free_rx_filters(sc);
+
+ if (sc->vtnet_ctrl_vq != NULL)
+ vtnet_free_ctrl_vq(sc);
+
+ VTNET_CORE_LOCK_DESTROY(sc);
+
+ return (0);
+}
+
+static int
+vtnet_suspend(device_t dev)
+{
+ struct vtnet_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ VTNET_CORE_LOCK(sc);
+ vtnet_stop(sc);
+ sc->vtnet_flags |= VTNET_FLAG_SUSPENDED;
+ VTNET_CORE_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+vtnet_resume(device_t dev)
+{
+ struct vtnet_softc *sc;
+ struct ifnet *ifp;
+
+ sc = device_get_softc(dev);
+ ifp = sc->vtnet_ifp;
+
+ VTNET_CORE_LOCK(sc);
+ if (ifp->if_flags & IFF_UP)
+ vtnet_init_locked(sc);
+ sc->vtnet_flags &= ~VTNET_FLAG_SUSPENDED;
+ VTNET_CORE_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+vtnet_shutdown(device_t dev)
+{
+
+ /*
+ * Suspend already does all of what we need to
+ * do here; we just never expect to be resumed.
+ */
+ return (vtnet_suspend(dev));
+}
+
+static int
+vtnet_attach_completed(device_t dev)
+{
+
+ vtnet_attach_disable_promisc(device_get_softc(dev));
+
+ return (0);
+}
+#endif
+
+static int
+vtnet_config_change(device_t dev)
+{
+ struct vtnet_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ VTNET_CORE_LOCK(sc);
+ vtnet_update_link_status(sc);
+ if (sc->vtnet_link_active != 0)
+ vtnet_tx_start_all(sc);
+ VTNET_CORE_UNLOCK(sc);
+
+ return (0);
+}
+
+static void
+vtnet_negotiate_features(struct vtnet_softc *sc)
+{
+ device_t dev;
+ uint64_t mask, features;
+
+ dev = sc->vtnet_dev;
+ mask = 0;
+
+#ifdef NOTUSED
+ /*
+ * TSO and LRO are only available when their corresponding checksum
+ * offload feature is also negotiated.
+ */
+ if (vtnet_tunable_int(sc, "csum_disable", vtnet_csum_disable)) {
+ mask |= VIRTIO_NET_F_CSUM | VIRTIO_NET_F_GUEST_CSUM;
+ mask |= VTNET_TSO_FEATURES | VTNET_LRO_FEATURES;
+ }
+ if (vtnet_tunable_int(sc, "tso_disable", vtnet_tso_disable))
+ mask |= VTNET_TSO_FEATURES;
+ if (vtnet_tunable_int(sc, "lro_disable", vtnet_lro_disable))
+ mask |= VTNET_LRO_FEATURES;
+ if (vtnet_tunable_int(sc, "mq_disable", vtnet_mq_disable))
+ mask |= VIRTIO_NET_F_MQ;
+#endif
+#ifdef VTNET_LEGACY_TX
+ mask |= VIRTIO_NET_F_MQ;
+#endif
+
+ features = VTNET_FEATURES & ~mask;
+ sc->vtnet_features = virtio_negotiate_features(dev, features);
+
+ if (virtio_with_feature(dev, VTNET_LRO_FEATURES) == 0)
+ return;
+ if (virtio_with_feature(dev, VIRTIO_NET_F_MRG_RXBUF))
+ return;
+
+#ifdef NOTUSED
+ /*
+ * LRO without mergeable buffers requires special care. This is not
+ * ideal because every receive buffer must be large enough to hold
+ * the maximum TCP packet, the Ethernet header, and the header. This
+ * requires up to 34 descriptors with MCLBYTES clusters. If we do
+ * not have indirect descriptors, LRO is disabled since the
virtqueue
+ * will not contain very many receive buffers.
+ */
+ if (virtio_with_feature(dev, VIRTIO_RING_F_INDIRECT_DESC) == 0) {
+ device_printf(dev,
+ "LRO disabled due to both mergeable buffers and indirect
"
+ "descriptors not negotiated\n");
+
+ features &= ~VTNET_LRO_FEATURES;
+ sc->vtnet_features = virtio_negotiate_features(dev,
features);
+ } else
+ sc->vtnet_flags |= VTNET_FLAG_LRO_NOMRG;
+#endif
+}
+
+static void
+vtnet_setup_features(struct vtnet_softc *sc)
+{
+ device_t dev;
+ int max_pairs, max;
+
+ dev = sc->vtnet_dev;
+
+ vtnet_negotiate_features(sc);
+
+ if (virtio_with_feature(dev, VIRTIO_RING_F_EVENT_IDX))
+ sc->vtnet_flags |= VTNET_FLAG_EVENT_IDX;
+
+ if (virtio_with_feature(dev, VIRTIO_NET_F_MAC)) {
+ /* This feature should always be negotiated. */
+ sc->vtnet_flags |= VTNET_FLAG_MAC;
+ }
+
+ if (virtio_with_feature(dev, VIRTIO_NET_F_MRG_RXBUF)) {
+ sc->vtnet_flags |= VTNET_FLAG_MRG_RXBUFS;
+ sc->vtnet_hdr_size = sizeof(struct
virtio_net_hdr_mrg_rxbuf);
+ } else
+ sc->vtnet_hdr_size = sizeof(struct virtio_net_hdr);
+
+ if (virtio_with_feature(dev, VIRTIO_NET_F_CTRL_VQ)) {
+ sc->vtnet_flags |= VTNET_FLAG_CTRL_VQ;
+
+ if (virtio_with_feature(dev, VIRTIO_NET_F_CTRL_RX))
+ sc->vtnet_flags |= VTNET_FLAG_CTRL_RX;
+ if (virtio_with_feature(dev, VIRTIO_NET_F_CTRL_VLAN))
+ sc->vtnet_flags |= VTNET_FLAG_VLAN_FILTER;
+ if (virtio_with_feature(dev, VIRTIO_NET_F_CTRL_MAC_ADDR))
+ sc->vtnet_flags |= VTNET_FLAG_CTRL_MAC;
+ }
+
+ if (virtio_with_feature(dev, VIRTIO_NET_F_MQ) &&
+ sc->vtnet_flags & VTNET_FLAG_CTRL_VQ) {
+ max_pairs = virtio_read_dev_config_2(dev,
+ offsetof(struct virtio_net_config,
max_virtqueue_pairs));
+ if (max_pairs < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN ||
+ max_pairs > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX)
+ max_pairs = 1;
+ } else
+ max_pairs = 1;
+
+#ifdef NOTUSED
+ if (max_pairs > 1) {
+ /*
+ * Limit the maximum number of queue pairs to the number of
+ * CPUs or the configured maximum. The actual number of
+ * queues that get used may be less.
+ */
+ max = vtnet_tunable_int(sc, "mq_max_pairs",
vtnet_mq_max_pairs);
+ if (max > 0 && max_pairs > max)
+ max_pairs = max;
+ if (max_pairs > mp_ncpus)
+ max_pairs = mp_ncpus;
+ if (max_pairs > VTNET_MAX_QUEUE_PAIRS)
+ max_pairs = VTNET_MAX_QUEUE_PAIRS;
+ if (max_pairs > 1)
+ sc->vtnet_flags |= VTNET_FLAG_MULTIQ;
+ }
+#endif
+
+ sc->vtnet_max_vq_pairs = max_pairs;
+}
+
+static int
+vtnet_init_rxq(struct vtnet_softc *sc, int id)
+{
+ struct vtnet_rxq *rxq;
+
+ rxq = &sc->vtnet_rxqs[id];
+
+ snprintf(rxq->vtnrx_name, sizeof(rxq->vtnrx_name), "%s-rx%d",
+ device_get_nameunit(sc->vtnet_dev), id);
+ mtx_init(&rxq->vtnrx_mtx, rxq->vtnrx_name, NULL, MTX_DEF);
+
+ rxq->vtnrx_sc = sc;
+ rxq->vtnrx_id = id;
+
+#ifdef NOTUSED
+ TASK_INIT(&rxq->vtnrx_intrtask, 0, vtnet_rxq_tq_intr, rxq);
+ rxq->vtnrx_tq = taskqueue_create(rxq->vtnrx_name, M_NOWAIT,
+ taskqueue_thread_enqueue, &rxq->vtnrx_tq);
+
+ return (rxq->vtnrx_tq == NULL ? ENOMEM : 0);
+#endif
+
+ return (0);
+}
+
+static int
+vtnet_init_txq(struct vtnet_softc *sc, int id)
+{
+ struct vtnet_txq *txq;
+
+ txq = &sc->vtnet_txqs[id];
+
+ snprintf(txq->vtntx_name, sizeof(txq->vtntx_name), "%s-tx%d",
+ device_get_nameunit(sc->vtnet_dev), id);
+ mtx_init(&txq->vtntx_mtx, txq->vtntx_name, NULL, MTX_DEF);
+
+ txq->vtntx_sc = sc;
+ txq->vtntx_id = id;
+
+#ifndef VTNET_LEGACY_TX
+ txq->vtntx_br = buf_ring_alloc(VTNET_DEFAULT_BUFRING_SIZE, M_DEVBUF,
+ M_NOWAIT, &txq->vtntx_mtx);
+ if (txq->vtntx_br == NULL)
+ return (ENOMEM);
+
+ TASK_INIT(&txq->vtntx_defrtask, 0, vtnet_txq_tq_deferred, txq);
+#endif
+#ifdef NOTUSED
+ TASK_INIT(&txq->vtntx_intrtask, 0, vtnet_txq_tq_intr, txq);
+ txq->vtntx_tq = taskqueue_create(txq->vtntx_name, M_NOWAIT,
+ taskqueue_thread_enqueue, &txq->vtntx_tq);
+ if (txq->vtntx_tq == NULL)
+ return (ENOMEM);
+#endif
+
+ return (0);
+}
+
+static int
+vtnet_alloc_rxtx_queues(struct vtnet_softc *sc)
+{
+ int i, npairs, error;
+
+ npairs = sc->vtnet_max_vq_pairs;
+
+ sc->vtnet_rxqs = malloc(sizeof(struct vtnet_rxq) * npairs, M_DEVBUF,
+ M_NOWAIT | M_ZERO);
+ sc->vtnet_txqs = malloc(sizeof(struct vtnet_txq) * npairs, M_DEVBUF,
+ M_NOWAIT | M_ZERO);
+ if (sc->vtnet_rxqs == NULL || sc->vtnet_txqs == NULL)
+ return (ENOMEM);
+
+ for (i = 0; i < npairs; i++) {
+ error = vtnet_init_rxq(sc, i);
+ if (error)
+ return (error);
+ error = vtnet_init_txq(sc, i);
+ if (error)
+ return (error);
+ }
+
+#ifdef NOTUSED
+ vtnet_setup_queue_sysctl(sc);
+#endif
+
+ return (0);
+}
+
+static void
+vtnet_destroy_rxq(struct vtnet_rxq *rxq)
+{
+
+ rxq->vtnrx_sc = NULL;
+ rxq->vtnrx_id = -1;
+
+ if (mtx_initialized(&rxq->vtnrx_mtx) != 0)
+ mtx_destroy(&rxq->vtnrx_mtx);
+}
+
+static void
+vtnet_destroy_txq(struct vtnet_txq *txq)
+{
+
+ txq->vtntx_sc = NULL;
+ txq->vtntx_id = -1;
+
+#ifndef VTNET_LEGACY_TX
+ if (txq->vtntx_br != NULL) {
+ buf_ring_free(txq->vtntx_br, M_DEVBUF);
+ txq->vtntx_br = NULL;
+ }
+#endif
+
+ if (mtx_initialized(&txq->vtntx_mtx) != 0)
+ mtx_destroy(&txq->vtntx_mtx);
+}
+
+static void
+vtnet_free_rxtx_queues(struct vtnet_softc *sc)
+{
+ int i;
+
+ if (sc->vtnet_rxqs != NULL) {
+ for (i = 0; i < sc->vtnet_max_vq_pairs; i++)
+ vtnet_destroy_rxq(&sc->vtnet_rxqs[i]);
+ free(sc->vtnet_rxqs, M_DEVBUF);
+ sc->vtnet_rxqs = NULL;
+ }
+
+ if (sc->vtnet_txqs != NULL) {
+ for (i = 0; i < sc->vtnet_max_vq_pairs; i++)
+ vtnet_destroy_txq(&sc->vtnet_txqs[i]);
+ free(sc->vtnet_txqs, M_DEVBUF);
+ sc->vtnet_txqs = NULL;
+ }
+}
+
+#ifdef NOTUSED
+static int
+vtnet_alloc_rx_filters(struct vtnet_softc *sc)
+{
+
+ if (sc->vtnet_flags & VTNET_FLAG_CTRL_RX) {
+ sc->vtnet_mac_filter = malloc(sizeof(struct
vtnet_mac_filter),
+ M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (sc->vtnet_mac_filter == NULL)
+ return (ENOMEM);
+ }
+
+ if (sc->vtnet_flags & VTNET_FLAG_VLAN_FILTER) {
+ sc->vtnet_vlan_filter = malloc(sizeof(uint32_t) *
+ VTNET_VLAN_FILTER_NWORDS, M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (sc->vtnet_vlan_filter == NULL)
+ return (ENOMEM);
+ }
+
+ return (0);
+}
+
+static void
+vtnet_free_rx_filters(struct vtnet_softc *sc)
+{
+
+ if (sc->vtnet_mac_filter != NULL) {
+ free(sc->vtnet_mac_filter, M_DEVBUF);
+ sc->vtnet_mac_filter = NULL;
+ }
+
+ if (sc->vtnet_vlan_filter != NULL) {
+ free(sc->vtnet_vlan_filter, M_DEVBUF);
+ sc->vtnet_vlan_filter = NULL;
+ }
+}
+#endif
+
+static int
+vtnet_alloc_virtqueues(struct vtnet_softc *sc)
+{
+ device_t dev;
+ struct vq_alloc_info *info;
+ struct vtnet_rxq *rxq;
+ struct vtnet_txq *txq;
+ int i, idx, flags, nvqs, rxsegs, error;
+
+ dev = sc->vtnet_dev;
+ flags = 0;
+
+ /*
+ * Indirect descriptors are not needed for the Rx virtqueue when
+ * mergeable buffers are negotiated. The header is placed inline
+ * with the data, not in a separate descriptor, and mbuf clusters
+ * are always physically contiguous.
+ */
+ if (sc->vtnet_flags & VTNET_FLAG_MRG_RXBUFS)
+ rxsegs = 0;
+ else if (sc->vtnet_flags & VTNET_FLAG_LRO_NOMRG)
+ rxsegs = VTNET_MAX_RX_SEGS;
+ else
+ rxsegs = VTNET_MIN_RX_SEGS;
+
+ nvqs = sc->vtnet_max_vq_pairs * 2;
+ if (sc->vtnet_flags & VTNET_FLAG_CTRL_VQ)
+ nvqs++;
+
+ info = malloc(sizeof(struct vq_alloc_info) * nvqs , M_TEMP,
M_NOWAIT);
+ if (info == NULL)
+ return (ENOMEM);
+
+ for (i = 0, idx = 0; i < sc->vtnet_max_vq_pairs; i++, idx+=2) {
+ rxq = &sc->vtnet_rxqs[i];
+ VQ_ALLOC_INFO_INIT(&info[idx], rxsegs,
+ vtnet_rx_vq_intr, rxq, &rxq->vtnrx_vq,
+ "%s-%d rx", device_get_nameunit(dev), rxq->vtnrx_id);
+
+ txq = &sc->vtnet_txqs[i];
+ VQ_ALLOC_INFO_INIT(&info[idx+1], VTNET_MAX_TX_SEGS,
+ vtnet_tx_vq_intr, txq, &txq->vtntx_vq,
+ "%s-%d tx", device_get_nameunit(dev), txq->vtntx_id);
+ }
+
+ if (sc->vtnet_flags & VTNET_FLAG_CTRL_VQ) {
+ VQ_ALLOC_INFO_INIT(&info[idx], 0, NULL, NULL,
+ &sc->vtnet_ctrl_vq, "%s ctrl",
device_get_nameunit(dev));
+ }
+
+ /*
+ * Enable interrupt binding if this is multiqueue. This only matters
+ * when per-vq MSIX is available.
+ */
+ if (sc->vtnet_flags & VTNET_FLAG_MULTIQ)
+ flags |= 0;
+
+ error = virtio_alloc_virtqueues(dev, flags, nvqs, info);
+ free(info, M_TEMP);
+
+ return (error);
+}
+
+static int
+vtnet_setup_interface(struct vtnet_softc *sc)
+{
+ device_t dev;
+ struct ifnet *ifp;
+ int limit;
+
+#ifdef NOTUSED
+ dev = sc->vtnet_dev;
+
+ ifp = sc->vtnet_ifp = if_alloc(IFT_ETHER);
+ if (ifp == NULL) {
+ device_printf(dev, "cannot allocate ifnet structure\n");
+ return (ENOSPC);
+ }
+
+ if_initname(ifp, device_get_name(dev), device_get_unit(dev));
+ if_initbaudrate(ifp, IF_Gbps(10)); /* Approx. */
+#endif
+#ifdef RTEMS_VIRTIO_NET
+ ifp = &sc->arpcom.ac_if;
+ sc->vtnet_ifp = ifp;
+#endif
+ ifp->if_softc = sc;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+ ifp->if_init = vtnet_init;
+ ifp->if_ioctl = vtnet_ioctl;
+
+#ifdef NOTUSED
+#ifndef VTNET_LEGACY_TX
+ ifp->if_transmit = vtnet_txq_mq_start;
+ ifp->if_qflush = vtnet_qflush;
+#else
+ struct virtqueue *vq = sc->vtnet_txqs[0].vtntx_vq;
+ ifp->if_start = vtnet_start;
+ IFQ_SET_MAXLEN(&ifp->if_snd, virtqueue_size(vq) - 1);
+ ifp->if_snd.ifq_drv_maxlen = virtqueue_size(vq) - 1;
+ IFQ_SET_READY(&ifp->if_snd);
+#endif
+
+ ifmedia_init(&sc->vtnet_media, IFM_IMASK, vtnet_ifmedia_upd,
+ vtnet_ifmedia_sts);
+ ifmedia_add(&sc->vtnet_media, VTNET_MEDIATYPE, 0, NULL);
+ ifmedia_set(&sc->vtnet_media, VTNET_MEDIATYPE);
+#endif
+
+ /* Read (or generate) the MAC address for the adapter. */
+ vtnet_get_hwaddr(sc);
+
+#ifdef RTEMS_VIRTIO_NET
+ struct virtqueue *vq = sc->vtnet_txqs[0].vtntx_vq;
+
+ ifp->if_start = vtnet_start;
+ IFQ_SET_MAXLEN(&ifp->if_snd, virtqueue_size(vq) - 1);
+ ifp->if_unit = sc->vtpci_softc->unit_number;
+ ifp->if_name = sc->vtpci_softc->unit_name;
+ ifp->if_mtu = sc->config->mtu ? sc->config->mtu : ETHERMTU;
+ ifp->if_output = ether_output;
+ if_attach(ifp);
+#endif
+ ether_ifattach(ifp);
+
+#ifdef NOTUSED
+ if (virtio_with_feature(dev, VIRTIO_NET_F_STATUS))
+ ifp->if_capabilities |= IFCAP_LINKSTATE;
+
+ /* Tell the upper layer(s) we support long frames. */
+ ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header);
+ ifp->if_capabilities |= IFCAP_JUMBO_MTU | IFCAP_VLAN_MTU;
+
+ if (virtio_with_feature(dev, VIRTIO_NET_F_CSUM)) {
+ ifp->if_capabilities |= IFCAP_TXCSUM | IFCAP_TXCSUM_IPV6;
+
+ if (virtio_with_feature(dev, VIRTIO_NET_F_GSO)) {
+ ifp->if_capabilities |= IFCAP_TSO4 | IFCAP_TSO6;
+ sc->vtnet_flags |= VTNET_FLAG_TSO_ECN;
+ } else {
+ if (virtio_with_feature(dev,
VIRTIO_NET_F_HOST_TSO4))
+ ifp->if_capabilities |= IFCAP_TSO4;
+ if (virtio_with_feature(dev,
VIRTIO_NET_F_HOST_TSO6))
+ ifp->if_capabilities |= IFCAP_TSO6;
+ if (virtio_with_feature(dev, VIRTIO_NET_F_HOST_ECN))
+ sc->vtnet_flags |= VTNET_FLAG_TSO_ECN;
+ }
+
+ if (ifp->if_capabilities & IFCAP_TSO)
+ ifp->if_capabilities |= IFCAP_VLAN_HWTSO;
+ }
+
+ if (virtio_with_feature(dev, VIRTIO_NET_F_GUEST_CSUM))
+ ifp->if_capabilities |= IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6;
+
+ if (ifp->if_capabilities & IFCAP_HWCSUM) {
+ /*
+ * VirtIO does not support VLAN tagging, but we can fake
+ * it by inserting and removing the 802.1Q header during
+ * transmit and receive. We are then able to do checksum
+ * offloading of VLAN frames.
+ */
+ ifp->if_capabilities |=
+ IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_HWCSUM;
+ }
+
+ ifp->if_capenable = ifp->if_capabilities;
+
+ /*
+ * Capabilities after here are not enabled by default.
+ */
+
+ if (ifp->if_capabilities & IFCAP_RXCSUM) {
+ if (virtio_with_feature(dev, VIRTIO_NET_F_GUEST_TSO4) ||
+ virtio_with_feature(dev, VIRTIO_NET_F_GUEST_TSO6))
+ ifp->if_capabilities |= IFCAP_LRO;
+ }
+
+ if (sc->vtnet_flags & VTNET_FLAG_VLAN_FILTER) {
+ ifp->if_capabilities |= IFCAP_VLAN_HWFILTER;
+
+ sc->vtnet_vlan_attach = EVENTHANDLER_REGISTER(vlan_config,
+ vtnet_register_vlan, sc, EVENTHANDLER_PRI_FIRST);
+ sc->vtnet_vlan_detach = EVENTHANDLER_REGISTER(vlan_unconfig,
+ vtnet_unregister_vlan, sc, EVENTHANDLER_PRI_FIRST);
+ }
+
+ limit = vtnet_tunable_int(sc, "rx_process_limit",
+ vtnet_rx_process_limit);
+ if (limit < 0)
+ limit = INT_MAX;
+
+ sc->vtnet_rx_process_limit = limit;
+#endif
+
+ return (0);
+}
+
+#ifdef NOTUSED
+static int
+vtnet_change_mtu(struct vtnet_softc *sc, int new_mtu)
+{
+ struct ifnet *ifp;
+ int frame_size, clsize;
+
+ ifp = sc->vtnet_ifp;
+
+ if (new_mtu < ETHERMIN || new_mtu > VTNET_MAX_MTU)
+ return (EINVAL);
+
+ frame_size = sc->vtnet_hdr_size + sizeof(struct ether_vlan_header) +
+ new_mtu;
+
+ /*
+ * Based on the new MTU (and hence frame size) determine which
+ * cluster size is most appropriate for the receive queues.
+ */
+ if (frame_size <= MCLBYTES) {
+ clsize = MCLBYTES;
+ } else if ((sc->vtnet_flags & VTNET_FLAG_MRG_RXBUFS) == 0) {
+ /* Avoid going past 9K jumbos. */
+ if (frame_size > MJUM9BYTES)
+ return (EINVAL);
+ clsize = MJUM9BYTES;
+ } else
+ clsize = MJUMPAGESIZE;
+
+ ifp->if_mtu = new_mtu;
+ sc->vtnet_rx_new_clsize = clsize;
+
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+ vtnet_init_locked(sc);
+ }
+
+ return (0);
+}
+#endif
+
+static int
+vtnet_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct vtnet_softc *sc;
+ struct ifreq *ifr;
+ int reinit, mask, error;
+
+ sc = ifp->if_softc;
+ ifr = (struct ifreq *) data;
+ error = 0;
+
+ switch (cmd) {
+#ifdef NOTUSED
+ case SIOCSIFMTU:
+ if (ifp->if_mtu != ifr->ifr_mtu) {
+ VTNET_CORE_LOCK(sc);
+ error = vtnet_change_mtu(sc, ifr->ifr_mtu);
+ VTNET_CORE_UNLOCK(sc);
+ }
+ break;
+#endif
+ case SIOCSIFFLAGS:
+ VTNET_CORE_LOCK(sc);
+ if ((ifp->if_flags & IFF_UP) == 0) {
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING){
+ vtnet_stop(sc);
+ }
+#ifdef NOTUSED
+ } else if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ if ((ifp->if_flags ^ sc->vtnet_if_flags) &
+ (IFF_PROMISC | IFF_ALLMULTI)) {
+ if (sc->vtnet_flags & VTNET_FLAG_CTRL_RX)
+ vtnet_rx_filter(sc);
+ else
+ error = ENOTSUP;
+ }
+#endif
+ } else{
+ vtnet_init_locked(sc);
+ }
+
+ if (error == 0)
+ sc->vtnet_if_flags = ifp->if_flags;
+ VTNET_CORE_UNLOCK(sc);
+ break;
+#ifdef NOTUSED
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ if ((sc->vtnet_flags & VTNET_FLAG_CTRL_RX) == 0)
+ break;
+ VTNET_CORE_LOCK(sc);
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ vtnet_rx_filter_mac(sc);
+ VTNET_CORE_UNLOCK(sc);
+ break;
+ case SIOCSIFMEDIA:
+ case SIOCGIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &sc->vtnet_media, cmd);
+ break;
+ case SIOCSIFCAP:
+ VTNET_CORE_LOCK(sc);
+ mask = ifr->ifr_reqcap ^ ifp->if_capenable;
+
+ if (mask & IFCAP_TXCSUM)
+ ifp->if_capenable ^= IFCAP_TXCSUM;
+ if (mask & IFCAP_TXCSUM_IPV6)
+ ifp->if_capenable ^= IFCAP_TXCSUM_IPV6;
+ if (mask & IFCAP_TSO4)
+ ifp->if_capenable ^= IFCAP_TSO4;
+ if (mask & IFCAP_TSO6)
+ ifp->if_capenable ^= IFCAP_TSO6;
+
+ if (mask & (IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6 | IFCAP_LRO |
+ IFCAP_VLAN_HWFILTER)) {
+ /* These Rx features require us to renegotiate. */
+ reinit = 1;
+
+ if (mask & IFCAP_RXCSUM)
+ ifp->if_capenable ^= IFCAP_RXCSUM;
+ if (mask & IFCAP_RXCSUM_IPV6)
+ ifp->if_capenable ^= IFCAP_RXCSUM_IPV6;
+ if (mask & IFCAP_LRO)
+ ifp->if_capenable ^= IFCAP_LRO;
+ if (mask & IFCAP_VLAN_HWFILTER)
+ ifp->if_capenable ^= IFCAP_VLAN_HWFILTER;
+ } else
+ reinit = 0;
+
+ if (mask & IFCAP_VLAN_HWTSO)
+ ifp->if_capenable ^= IFCAP_VLAN_HWTSO;
+ if (mask & IFCAP_VLAN_HWTAGGING)
+ ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING;
+
+ if (reinit && (ifp->if_drv_flags & IFF_DRV_RUNNING)) {
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+ vtnet_init_locked(sc);
+ }
+
+ VTNET_CORE_UNLOCK(sc);
+ VLAN_CAPABILITIES(ifp);
+
+ break;
+#endif
+ default:
+ error = ether_ioctl(ifp, cmd, data);
+ break;
+ }
+
+ VTNET_CORE_LOCK_ASSERT_NOTOWNED(sc);
+
+ return (error);
+}
+
+static int
+vtnet_rxq_populate(struct vtnet_rxq *rxq)
+{
+ struct virtqueue *vq;
+ int nbufs, error;
+
+ vq = rxq->vtnrx_vq;
+ error = ENOSPC;
+
+ for (nbufs = 0; !virtqueue_full(vq); nbufs++) {
+ error = vtnet_rxq_new_buf(rxq);
+ if (error)
+ break;
+ }
+
+ if (nbufs > 0) {
+ virtqueue_notify(vq);
+ /*
+ * EMSGSIZE signifies the virtqueue did not have enough
+ * entries available to hold the last mbuf. This is not
+ * an error.
+ */
+ if (error == EMSGSIZE)
+ error = 0;
+ }
+
+ return (error);
+}
+
+static void
+vtnet_rxq_free_mbufs(struct vtnet_rxq *rxq)
+{
+ struct virtqueue *vq;
+ struct mbuf *m;
+ int last;
+
+ vq = rxq->vtnrx_vq;
+ last = 0;
+
+ while ((m = virtqueue_drain(vq, &last)) != NULL)
+ m_freem(m);
+
+ KASSERT(virtqueue_empty(vq),
+ ("%s: mbufs remaining in rx queue %p", __func__, rxq));
+}
+
+static struct mbuf *
+vtnet_rx_alloc_buf(struct vtnet_softc *sc, int nbufs, struct mbuf
**m_tailp)
+{
+ struct mbuf *m_head, *m_tail, *m;
+ int i, clsize;
+
+ clsize = sc->vtnet_rx_clsize;
+
+ KASSERT(nbufs == 1 || sc->vtnet_flags & VTNET_FLAG_LRO_NOMRG,
+ ("%s: chained mbuf %d request without LRO_NOMRG", __func__,
nbufs));
+
+#ifdef NOTUSED
+ m_head = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, clsize);
+#endif
+#ifdef RTEMS_VIRTIO_NET
+ m_head = m_gethdr(M_NOWAIT, MT_DATA);
+ if (m_head == NULL)
+ goto fail;
+ MCLGET(m_head, M_NOWAIT);
+#endif
+
+ m_head->m_len = clsize;
+ m_tail = m_head;
+
+ /* Allocate the rest of the chain. */
+ for (i = 1; i < nbufs; i++) {
+#ifdef NOTUSED
+ m = m_getjcl(M_NOWAIT, MT_DATA, 0, clsize);
+#endif
+#ifdef RTEMS_VIRTIO_NET
+ m = m_gethdr(M_NOWAIT, MT_DATA);
+ if (m == NULL)
+ goto fail;
+ MCLGET(m_head, M_NOWAIT);
+#endif
+
+ m->m_len = clsize;
+ m_tail->m_next = m;
+ m_tail = m;
+ }
+
+ if (m_tailp != NULL)
+ *m_tailp = m_tail;
+
+ return (m_head);
+
+fail:
+ sc->vtnet_stats.mbuf_alloc_failed++;
+ m_freem(m_head);
+
+ return (NULL);
+}
+
+/*
+ * Slow path for when LRO without mergeable buffers is negotiated.
+ */
+static int
+vtnet_rxq_replace_lro_nomgr_buf(struct vtnet_rxq *rxq, struct mbuf *m0,
+ int len0)
+{
+ struct vtnet_softc *sc;
+ struct mbuf *m, *m_prev;
+ struct mbuf *m_new, *m_tail;
+ int len, clsize, nreplace, error;
+
+ sc = rxq->vtnrx_sc;
+ clsize = sc->vtnet_rx_clsize;
+
+ m_prev = NULL;
+ m_tail = NULL;
+ nreplace = 0;
+
+ m = m0;
+ len = len0;
+
+ /*
+ * Since these mbuf chains are so large, we avoid allocating an
+ * entire replacement chain if possible. When the received frame
+ * did not consume the entire chain, the unused mbufs are moved
+ * to the replacement chain.
+ */
+ while (len > 0) {
+ /*
+ * Something is seriously wrong if we received a frame
+ * larger than the chain. Drop it.
+ */
+ if (m == NULL) {
+ sc->vtnet_stats.rx_frame_too_large++;
+ return (EMSGSIZE);
+ }
+
+ /* We always allocate the same cluster size. */
+ KASSERT(m->m_len == clsize,
+ ("%s: mbuf size %d is not the cluster size %d",
+ __func__, m->m_len, clsize));
+
+ m->m_len = MIN(m->m_len, len);
+ len -= m->m_len;
+
+ m_prev = m;
+ m = m->m_next;
+ nreplace++;
+ }
+
+ KASSERT(nreplace <= sc->vtnet_rx_nmbufs,
+ ("%s: too many replacement mbufs %d max %d", __func__, nreplace,
+ sc->vtnet_rx_nmbufs));
+
+ m_new = vtnet_rx_alloc_buf(sc, nreplace, &m_tail);
+ if (m_new == NULL) {
+ m_prev->m_len = clsize;
+ return (ENOBUFS);
+ }
+
+ /*
+ * Move any unused mbufs from the received chain onto the end
+ * of the new chain.
+ */
+ if (m_prev->m_next != NULL) {
+ m_tail->m_next = m_prev->m_next;
+ m_prev->m_next = NULL;
+ }
+
+ error = vtnet_rxq_enqueue_buf(rxq, m_new);
+ if (error) {
+ /*
+ * BAD! We could not enqueue the replacement mbuf chain. We
+ * must restore the m0 chain to the original state if it was
+ * modified so we can subsequently discard it.
+ *
+ * NOTE: The replacement is suppose to be an identical copy
+ * to the one just dequeued so this is an unexpected error.
+ */
+ sc->vtnet_stats.rx_enq_replacement_failed++;
+
+ if (m_tail->m_next != NULL) {
+ m_prev->m_next = m_tail->m_next;
+ m_tail->m_next = NULL;
+ }
+
+ m_prev->m_len = clsize;
+ m_freem(m_new);
+ }
+
+ return (error);
+}
+
+static int
+vtnet_rxq_replace_buf(struct vtnet_rxq *rxq, struct mbuf *m, int len)
+{
+ struct vtnet_softc *sc;
+ struct mbuf *m_new;
+ int error;
+
+ sc = rxq->vtnrx_sc;
+
+ KASSERT(sc->vtnet_flags & VTNET_FLAG_LRO_NOMRG || m->m_next == NULL,
+ ("%s: chained mbuf without LRO_NOMRG", __func__));
+
+ if (m->m_next == NULL) {
+ /* Fast-path for the common case of just one mbuf. */
+ if (m->m_len < len)
+ return (EINVAL);
+
+ m_new = vtnet_rx_alloc_buf(sc, 1, NULL);
+ if (m_new == NULL)
+ return (ENOBUFS);
+
+ error = vtnet_rxq_enqueue_buf(rxq, m_new);
+ if (error) {
+ /*
+ * The new mbuf is suppose to be an identical
+
+ * copy of the one just dequeued so this is an
+ * unexpected error.
+ */
+ m_freem(m_new);
+ sc->vtnet_stats.rx_enq_replacement_failed++;
+ } else
+ m->m_len = len;
+ } else
+ error = vtnet_rxq_replace_lro_nomgr_buf(rxq, m, len);
+
+ return (error);
+}
+
+static int
+vtnet_rxq_enqueue_buf(struct vtnet_rxq *rxq, struct mbuf *m)
+{
+#ifdef NOTUSED
+ struct sglist sg;
+ struct sglist_seg segs[VTNET_MAX_RX_SEGS];
+ struct vtnet_softc *sc;
+ struct vtnet_rx_header *rxhdr;
+ uint8_t *mdata;
+ int offset, error, nsegs;
+
+ sc = rxq->vtnrx_sc;
+ mdata = mtod(m, uint8_t *);
+
+ VTNET_RXQ_LOCK_ASSERT(rxq);
+ KASSERT(sc->vtnet_flags & VTNET_FLAG_LRO_NOMRG || m->m_next == NULL,
+ ("%s: chained mbuf without LRO_NOMRG", __func__));
+ KASSERT(m->m_len == sc->vtnet_rx_clsize,
+ ("%s: unexpected cluster size %d/%d", __func__, m->m_len,
+ sc->vtnet_rx_clsize));
+
+ sglist_init(&sg, VTNET_MAX_RX_SEGS, segs);
+ if ((sc->vtnet_flags & VTNET_FLAG_MRG_RXBUFS) == 0) {
+ MPASS(sc->vtnet_hdr_size == sizeof(struct virtio_net_hdr));
+ rxhdr = (struct vtnet_rx_header *) mdata;
+ sglist_append(&sg, &rxhdr->vrh_hdr, sc->vtnet_hdr_size);
+ offset = sizeof(struct vtnet_rx_header);
+ } else
+ offset = 0;
+
+ sglist_append(&sg, mdata + offset, m->m_len - offset);
+ if (m->m_next != NULL) {
+ error = sglist_append_mbuf(&sg, m->m_next);
+ MPASS(error == 0);
+ }
+
+ error = virtqueue_enqueue(rxq->vtnrx_vq, m, &sg, 0, sg.sg_nsegs);
+#endif
+#ifdef RTEMS_VIRTIO_NET
+ struct mbuf *m_rxhdr;
+ struct vtnet_softc *sc;
+ int error, nsegs;
+
+ sc = rxq->vtnrx_sc;
+ nsegs = 0;
+
+ if ((sc->vtnet_flags & VTNET_FLAG_MRG_RXBUFS) == 0) {
+ MGET(m_rxhdr, M_DONTWAIT, MT_DATA);
+ if( m_rxhdr == NULL ){
+ m_freem(m);
+ m_freem(m_rxhdr);
+ return ENOBUFS;
+ }
+ memcpy(m_rxhdr->m_data, mtod(m, uint8_t *),
sc->vtnet_hdr_size);
+ m_adj(m, sc->vtnet_hdr_size);
+ m_rxhdr->m_len = sc->vtnet_hdr_size;
+ m_rxhdr->m_next = m;
+ }
+ else{
+ m_rxhdr = m;
+ }
+
+ for( m=m_rxhdr ; m!=NULL ; m=m->m_next )
+ nsegs++;
+
+ error = rtems_virtqueue_enqueue(rxq->vtnrx_vq, m_rxhdr, 0, nsegs);
+#endif
+
+ return (error);
+}
+
+static int
+vtnet_rxq_new_buf(struct vtnet_rxq *rxq)
+{
+ struct vtnet_softc *sc;
+ struct mbuf *m;
+ int error;
+
+ sc = rxq->vtnrx_sc;
+
+ m = vtnet_rx_alloc_buf(sc, sc->vtnet_rx_nmbufs, NULL);
+ if (m == NULL)
+ return (ENOBUFS);
+
+ error = vtnet_rxq_enqueue_buf(rxq, m);
+ if (error)
+ m_freem(m);
+
+ return (error);
+}
+
+#ifdef NOTUSED
+/*
+ * Use the checksum offset in the VirtIO header to set the
+ * correct CSUM_* flags.
+ */
+static int
+vtnet_rxq_csum_by_offset(struct vtnet_rxq *rxq, struct mbuf *m,
+ uint16_t eth_type, int ip_start, struct virtio_net_hdr *hdr)
+{
+ struct vtnet_softc *sc;
+#if defined(INET) || defined(INET6)
+ int offset = hdr->csum_start + hdr->csum_offset;
+#endif
+
+ sc = rxq->vtnrx_sc;
+
+ /* Only do a basic sanity check on the offset. */
+ switch (eth_type) {
+#if defined(INET)
+ case ETHERTYPE_IP:
+ if (__predict_false(offset < ip_start + sizeof(struct ip)))
+ return (1);
+ break;
+#endif
+#if defined(INET6)
+ case ETHERTYPE_IPV6:
+ if (__predict_false(offset < ip_start + sizeof(struct
ip6_hdr)))
+ return (1);
+ break;
+#endif
+ default:
+ sc->vtnet_stats.rx_csum_bad_ethtype++;
+ return (1);
+ }
+
+ /*
+ * Use the offset to determine the appropriate CSUM_* flags. This is
+ * a bit dirty, but we can get by with it since the checksum offsets
+ * happen to be different. We assume the host host does not do IPv4
+ * header checksum offloading.
+ */
+ switch (hdr->csum_offset) {
+ case offsetof(struct udphdr, uh_sum):
+ case offsetof(struct tcphdr, th_sum):
+ m->m_pkthdr.csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
+ m->m_pkthdr.csum_data = 0xFFFF;
+ break;
+ case offsetof(struct sctphdr, checksum):
+ m->m_pkthdr.csum_flags |= CSUM_SCTP_VALID;
+ break;
+ default:
+ sc->vtnet_stats.rx_csum_bad_offset++;
+ return (1);
+ }
+
+ return (0);
+}
+
+static int
+vtnet_rxq_csum_by_parse(struct vtnet_rxq *rxq, struct mbuf *m,
+ uint16_t eth_type, int ip_start, struct virtio_net_hdr *hdr)
+{
+ struct vtnet_softc *sc;
+ int offset, proto;
+
+ sc = rxq->vtnrx_sc;
+
+ switch (eth_type) {
+#if defined(INET)
+ case ETHERTYPE_IP: {
+ struct ip *ip;
+ if (__predict_false(m->m_len < ip_start + sizeof(struct
ip)))
+ return (1);
+ ip = (struct ip *)(m->m_data + ip_start);
+ proto = ip->ip_p;
+ offset = ip_start + (ip->ip_hl << 2);
+ break;
+ }
+#endif
+#if defined(INET6)
+ case ETHERTYPE_IPV6:
+ if (__predict_false(m->m_len < ip_start +
+ sizeof(struct ip6_hdr)))
+ return (1);
+ offset = ip6_lasthdr(m, ip_start, IPPROTO_IPV6, &proto);
+ if (__predict_false(offset < 0))
+ return (1);
+ break;
+#endif
+ default:
+ sc->vtnet_stats.rx_csum_bad_ethtype++;
+ return (1);
+ }
+
+ switch (proto) {
+ case IPPROTO_TCP:
+ if (__predict_false(m->m_len < offset + sizeof(struct
tcphdr)))
+ return (1);
+ m->m_pkthdr.csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
+ m->m_pkthdr.csum_data = 0xFFFF;
+ break;
+ case IPPROTO_UDP:
+ if (__predict_false(m->m_len < offset + sizeof(struct
udphdr)))
+ return (1);
+ m->m_pkthdr.csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
+ m->m_pkthdr.csum_data = 0xFFFF;
+ break;
+ case IPPROTO_SCTP:
+ if (__predict_false(m->m_len < offset + sizeof(struct
sctphdr)))
+ return (1);
+ m->m_pkthdr.csum_flags |= CSUM_SCTP_VALID;
+ break;
+ default:
+ /*
+ * For the remaining protocols, FreeBSD does not support
+ * checksum offloading, so the checksum will be recomputed.
+ */
+#if 0
+ if_printf(sc->vtnet_ifp, "cksum offload of unsupported "
+ "protocol eth_type=%#x proto=%d csum_start=%d "
+ "csum_offset=%d\n", __func__, eth_type, proto,
+ hdr->csum_start, hdr->csum_offset);
+#endif
+ break;
+ }
+
+ return (0);
+}
+
+/*
+ * Set the appropriate CSUM_* flags. Unfortunately, the information
+ * provided is not directly useful to us. The VirtIO header gives the
+ * offset of the checksum, which is all Linux needs, but this is not
+ * how FreeBSD does things. We are forced to peek inside the packet
+ * a bit.
+ *
+ * It would be nice if VirtIO gave us the L4 protocol or if FreeBSD
+ * could accept the offsets and let the stack figure it out.
+ */
+static int
+vtnet_rxq_csum(struct vtnet_rxq *rxq, struct mbuf *m,
+ struct virtio_net_hdr *hdr)
+{
+ struct ether_header *eh;
+ struct ether_vlan_header *evh;
+ uint16_t eth_type;
+ int offset, error;
+
+ eh = mtod(m, struct ether_header *);
+ eth_type = ntohs(eh->ether_type);
+ if (eth_type == ETHERTYPE_VLAN) {
+ /* BMV: We should handle nested VLAN tags too. */
+ evh = mtod(m, struct ether_vlan_header *);
+ eth_type = ntohs(evh->evl_proto);
+ offset = sizeof(struct ether_vlan_header);
+ } else
+ offset = sizeof(struct ether_header);
+
+ if (hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM)
+ error = vtnet_rxq_csum_by_offset(rxq, m, eth_type, offset,
hdr);
+ else
+ error = vtnet_rxq_csum_by_parse(rxq, m, eth_type, offset,
hdr);
+
+ return (error);
+}
+#endif
+
+static void
+vtnet_rxq_discard_merged_bufs(struct vtnet_rxq *rxq, int nbufs)
+{
+ struct mbuf *m;
+
+ while (--nbufs > 0) {
+ m = virtqueue_dequeue(rxq->vtnrx_vq, NULL);
+ if (m == NULL)
+ break;
+ vtnet_rxq_discard_buf(rxq, m);
+ }
+}
+
+static void
+vtnet_rxq_discard_buf(struct vtnet_rxq *rxq, struct mbuf *m)
+{
+ int error;
+
+ /*
+ * Requeue the discarded mbuf. This should always be successful
+ * since it was just dequeued.
+ */
+ error = vtnet_rxq_enqueue_buf(rxq, m);
+ KASSERT(error == 0,
+ ("%s: cannot requeue discarded mbuf %d", __func__, error));
+}
+
+static int
+vtnet_rxq_merged_eof(struct vtnet_rxq *rxq, struct mbuf *m_head, int nbufs)
+{
+ struct vtnet_softc *sc;
+ struct ifnet *ifp;
+ struct virtqueue *vq;
+ struct mbuf *m, *m_tail;
+#ifdef NOTUSED
+ int len;
+#endif
+#ifdef RTEMS_VIRTIO_NET
+ uint32_t len;
+#endif
+
+ sc = rxq->vtnrx_sc;
+ vq = rxq->vtnrx_vq;
+ ifp = sc->vtnet_ifp;
+ m_tail = m_head;
+
+ while (--nbufs > 0) {
+ m = virtqueue_dequeue(vq, &len);
+ if (m == NULL) {
+ rxq->vtnrx_stats.vrxs_ierrors++;
+ goto fail;
+ }
+
+ if (vtnet_rxq_new_buf(rxq) != 0) {
+ rxq->vtnrx_stats.vrxs_iqdrops++;
+ vtnet_rxq_discard_buf(rxq, m);
+ if (nbufs > 1)
+ vtnet_rxq_discard_merged_bufs(rxq, nbufs);
+ goto fail;
+ }
+
+ if (m->m_len < len)
+ len = m->m_len;
+
+ m->m_len = len;
+ m->m_flags &= ~M_PKTHDR;
+
+ m_head->m_pkthdr.len += len;
+ m_tail->m_next = m;
+ m_tail = m;
+ }
+
+ return (0);
+
+fail:
+ sc->vtnet_stats.rx_mergeable_failed++;
+ m_freem(m_head);
+
+ return (1);
+}
+
+static void
+vtnet_rxq_input(struct vtnet_rxq *rxq, struct mbuf *m,
+ struct virtio_net_hdr *hdr)
+{
+ struct vtnet_softc *sc;
+ struct ifnet *ifp;
+ struct ether_header *eh;
+
+ sc = rxq->vtnrx_sc;
+ ifp = sc->vtnet_ifp;
+
+#ifdef NOTUSED
+ if (ifp->if_capenable & IFCAP_VLAN_HWTAGGING) {
+ eh = mtod(m, struct ether_header *);
+ if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
+ vtnet_vlan_tag_remove(m);
+ /*
+ * With the 802.1Q header removed, update the
+ * checksum starting location accordingly.
+ */
+ if (hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM)
+ hdr->csum_start -= ETHER_VLAN_ENCAP_LEN;
+ }
+ }
+
+ m->m_pkthdr.flowid = rxq->vtnrx_id;
+ m->m_flags |= M_FLOWID;
+
+ /*
+ * BMV: FreeBSD does not have the UNNECESSARY and PARTIAL checksum
+ * distinction that Linux does. Need to reevaluate if performing
+ * offloading for the NEEDS_CSUM case is really appropriate.
+ */
+ if (hdr->flags & (VIRTIO_NET_HDR_F_NEEDS_CSUM |
+ VIRTIO_NET_HDR_F_DATA_VALID)) {
+ if (vtnet_rxq_csum(rxq, m, hdr) == 0)
+ rxq->vtnrx_stats.vrxs_csum++;
+ else
+ rxq->vtnrx_stats.vrxs_csum_failed++;
+ }
+#endif
+
+ rxq->vtnrx_stats.vrxs_ipackets++;
+ rxq->vtnrx_stats.vrxs_ibytes += m->m_pkthdr.len;
+
+#ifdef NOTUSED
+ VTNET_RXQ_UNLOCK(rxq);
+ (*ifp->if_input)(ifp, m);
+ VTNET_RXQ_LOCK(rxq);
+#endif
+#ifdef RTEMS_VIRTIO_NET
+ eh = mtod(m, struct ether_header *);
+ m_adj( m, sizeof( struct ether_header ) );
+ ether_input(ifp, eh, m);
+#endif
+}
+
+static int
+vtnet_rxq_eof(struct vtnet_rxq *rxq)
+{
+ struct virtio_net_hdr lhdr, *hdr;
+ struct vtnet_softc *sc;
+ struct ifnet *ifp;
+ struct virtqueue *vq;
+ struct mbuf *m;
+ struct virtio_net_hdr_mrg_rxbuf *mhdr;
+#ifdef NOTUSED
+ int len, deq, nbufs, adjsz, count;
+#endif
+#ifdef RTEMS_VIRTIO_NET
+ uint32_t len;
+ int deq, nbufs, adjsz, count;
+#endif
+
+ sc = rxq->vtnrx_sc;
+ vq = rxq->vtnrx_vq;
+ ifp = sc->vtnet_ifp;
+ hdr = &lhdr;
+ deq = 0;
+ count = sc->vtnet_rx_process_limit;
+
+ VTNET_RXQ_LOCK_ASSERT(rxq);
+
+ while (count-- > 0) {
+ m = virtqueue_dequeue(vq, &len);
+ if (m == NULL)
+ break;
+ deq++;
+
+ if (len < sc->vtnet_hdr_size + ETHER_HDR_LEN) {
+ rxq->vtnrx_stats.vrxs_ierrors++;
+ vtnet_rxq_discard_buf(rxq, m);
+ continue;
+ }
+
+ if ((sc->vtnet_flags & VTNET_FLAG_MRG_RXBUFS) == 0) {
+ nbufs = 1;
+ adjsz = sizeof(struct vtnet_rx_header);
+ /*
+ * Account for our pad inserted between the header
+ * and the actual start of the frame.
+ */
+ len += VTNET_RX_HEADER_PAD;
+ } else {
+ mhdr = mtod(m, struct virtio_net_hdr_mrg_rxbuf *);
+ nbufs = mhdr->num_buffers;
+ adjsz = sizeof(struct virtio_net_hdr_mrg_rxbuf);
+ }
+
+ if (vtnet_rxq_replace_buf(rxq, m, len) != 0) {
+ rxq->vtnrx_stats.vrxs_iqdrops++;
+ vtnet_rxq_discard_buf(rxq, m);
+ if (nbufs > 1)
+ vtnet_rxq_discard_merged_bufs(rxq, nbufs);
+ continue;
+ }
+
+ m->m_pkthdr.len = len;
+ m->m_pkthdr.rcvif = ifp;
+#ifdef NOTUSED
+ m->m_pkthdr.csum_flags = 0;
+#endif
+
+ if (nbufs > 1) {
+ /* Dequeue the rest of chain. */
+ if (vtnet_rxq_merged_eof(rxq, m, nbufs) != 0)
+ continue;
+ }
+
+ /*
+ * Save copy of header before we strip it. For both
mergeable
+ * and non-mergeable, the header is at the beginning of the
+ * mbuf data. We no longer need num_buffers, so always use a
+ * regular header.
+ *
+ * BMV: Is this memcpy() expensive? We know the mbuf data is
+ * still valid even after the m_adj().
+ */
+ memcpy(hdr, mtod(m, void *), sizeof(struct virtio_net_hdr));
+ m_adj(m, adjsz);
+
+ vtnet_rxq_input(rxq, m, hdr);
+
+ /* Must recheck after dropping the Rx lock. */
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ break;
+ }
+
+ if (deq > 0)
+ virtqueue_notify(vq);
+
+ return (count > 0 ? 0 : EAGAIN);
+}
+
+static void
+vtnet_rx_vq_intr(void *xrxq)
+{
+ struct vtnet_softc *sc;
+ struct vtnet_rxq *rxq;
+ struct ifnet *ifp;
+ int tries, more;
+
+ rxq = xrxq;
+ sc = rxq->vtnrx_sc;
+ ifp = sc->vtnet_ifp;
+ tries = 0;
+
+ if (__predict_false(rxq->vtnrx_id >= sc->vtnet_act_vq_pairs)) {
+ /*
+ * Ignore this interrupt. Either this is a spurious
interrupt
+ * or multiqueue without per-VQ MSIX so every queue needs to
+ * be polled (a brain dead configuration we could try harder
+ * to avoid).
+ */
+ vtnet_rxq_disable_intr(rxq);
+ return;
+ }
+
+again:
+ VTNET_RXQ_LOCK(rxq);
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
+ VTNET_RXQ_UNLOCK(rxq);
+ return;
+ }
+
+ more = vtnet_rxq_eof(rxq);
+ if (more || vtnet_rxq_enable_intr(rxq) != 0) {
+ if (!more)
+ vtnet_rxq_disable_intr(rxq);
+ /*
+ * This is an occasional condition or race (when !more),
+ * so retry a few times before scheduling the taskqueue.
+ */
+ rxq->vtnrx_stats.vrxs_rescheduled++;
+ VTNET_RXQ_UNLOCK(rxq);
+ if (tries++ < VTNET_INTR_DISABLE_RETRIES)
+ goto again;
+#ifdef NOTUSED
+ taskqueue_enqueue(rxq->vtnrx_tq, &rxq->vtnrx_intrtask);
+#endif
+ } else
+ VTNET_RXQ_UNLOCK(rxq);
+}
+
+#ifdef NOTUSED
+static void
+vtnet_rxq_tq_intr(void *xrxq, int pending)
+{
+ struct vtnet_softc *sc;
+ struct vtnet_rxq *rxq;
+ struct ifnet *ifp;
+ int more;
+
+ rxq = xrxq;
+ sc = rxq->vtnrx_sc;
+ ifp = sc->vtnet_ifp;
+
+ VTNET_RXQ_LOCK(rxq);
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
+ VTNET_RXQ_UNLOCK(rxq);
+ return;
+ }
+
+ more = vtnet_rxq_eof(rxq);
+ if (more || vtnet_rxq_enable_intr(rxq) != 0) {
+ if (!more)
+ vtnet_rxq_disable_intr(rxq);
+ rxq->vtnrx_stats.vrxs_rescheduled++;
+ taskqueue_enqueue(rxq->vtnrx_tq, &rxq->vtnrx_intrtask);
+ }
+
+ VTNET_RXQ_UNLOCK(rxq);
+}
+#endif
+
+static void
+vtnet_txq_free_mbufs(struct vtnet_txq *txq)
+{
+ struct virtqueue *vq;
+ struct mbuf *m_txhdr;
+ int last;
+
+ vq = txq->vtntx_vq;
+ last = 0;
+
+#ifdef NOTUSED
+ while ((txhdr = virtqueue_drain(vq, &last)) != NULL) {
+ m_freem(txhdr->vth_mbuf);
+ uma_zfree(vtnet_tx_header_zone, txhdr);
+ }
+#endif
+#ifdef RTEMS_VIRTIO_NET
+ while ((m_txhdr = virtqueue_drain(vq, &last)) != NULL) {
+ m_freem(m_txhdr);
+ }
+#endif
+
+ KASSERT(virtqueue_empty(vq),
+ ("%s: mbufs remaining in tx queue %p", __func__, txq));
+}
+
+#ifdef NOTUSED
+/*
+ * BMV: Much of this can go away once we finally have offsets in
+ * the mbuf packet header. Bug andre at .
+ */
+static int
+vtnet_txq_offload_ctx(struct vtnet_txq *txq, struct mbuf *m,
+ int *etype, int *proto, int *start)
+{
+ struct vtnet_softc *sc;
+ struct ether_vlan_header *evh;
+ int offset;
+
+ sc = txq->vtntx_sc;
+
+ evh = mtod(m, struct ether_vlan_header *);
+ if (evh->evl_encap_proto == htons(ETHERTYPE_VLAN)) {
+ /* BMV: We should handle nested VLAN tags too. */
+ *etype = ntohs(evh->evl_proto);
+ offset = sizeof(struct ether_vlan_header);
+ } else {
+ *etype = ntohs(evh->evl_encap_proto);
+ offset = sizeof(struct ether_header);
+ }
+
+ switch (*etype) {
+#if defined(INET)
+ case ETHERTYPE_IP: {
+ struct ip *ip, iphdr;
+ if (__predict_false(m->m_len < offset + sizeof(struct ip)))
{
+ m_copydata(m, offset, sizeof(struct ip),
+ (caddr_t) &iphdr);
+ ip = &iphdr;
+ } else
+ ip = (struct ip *)(m->m_data + offset);
+ *proto = ip->ip_p;
+ *start = offset + (ip->ip_hl << 2);
+ break;
+ }
+#endif
+#if defined(INET6)
+ case ETHERTYPE_IPV6:
+ *proto = -1;
+ *start = ip6_lasthdr(m, offset, IPPROTO_IPV6, proto);
+ /* Assert the network stack sent us a valid packet. */
+ KASSERT(*start > offset,
+ ("%s: mbuf %p start %d offset %d proto %d", __func__, m,
+ *start, offset, *proto));
+ break;
+#endif
+ default:
+ sc->vtnet_stats.tx_csum_bad_ethtype++;
+ return (EINVAL);
+ }
+
+ return (0);
+}
+
+static int
+vtnet_txq_offload_tso(struct vtnet_txq *txq, struct mbuf *m, int eth_type,
+ int offset, struct virtio_net_hdr *hdr)
+{
+ static struct timeval lastecn;
+ static int curecn;
+ struct vtnet_softc *sc;
+ struct tcphdr *tcp, tcphdr;
+
+ sc = txq->vtntx_sc;
+
+ if (__predict_false(m->m_len < offset + sizeof(struct tcphdr))) {
+ m_copydata(m, offset, sizeof(struct tcphdr), (caddr_t)
&tcphdr);
+ tcp = &tcphdr;
+ } else
+ tcp = (struct tcphdr *)(m->m_data + offset);
+
+ hdr->hdr_len = offset + (tcp->th_off << 2);
+ hdr->gso_size = m->m_pkthdr.tso_segsz;
+ hdr->gso_type = eth_type == ETHERTYPE_IP ? VIRTIO_NET_HDR_GSO_TCPV4
:
+ VIRTIO_NET_HDR_GSO_TCPV6;
+
+ if (tcp->th_flags & TH_CWR) {
+ /*
+ * Drop if VIRTIO_NET_F_HOST_ECN was not negotiated. In
FreeBSD,
+ * ECN support is not on a per-interface basis, but globally
via
+ * the net.inet.tcp.ecn.enable sysctl knob. The default is
off.
+ */
+ if ((sc->vtnet_flags & VTNET_FLAG_TSO_ECN) == 0) {
+ if (ppsratecheck(&lastecn, &curecn, 1))
+ if_printf(sc->vtnet_ifp,
+ "TSO with ECN not negotiated with
host\n");
+ return (ENOTSUP);
+ }
+ hdr->gso_type |= VIRTIO_NET_HDR_GSO_ECN;
+ }
+
+ txq->vtntx_stats.vtxs_tso++;
+
+ return (0);
+}
+
+static struct mbuf *
+vtnet_txq_offload(struct vtnet_txq *txq, struct mbuf *m,
+ struct virtio_net_hdr *hdr)
+{
+ struct vtnet_softc *sc;
+ int flags, etype, csum_start, proto, error;
+
+ sc = txq->vtntx_sc;
+ flags = m->m_pkthdr.csum_flags;
+
+ error = vtnet_txq_offload_ctx(txq, m, &etype, &proto, &csum_start);
+ if (error)
+ goto drop;
+
+ if ((etype == ETHERTYPE_IP && flags & VTNET_CSUM_OFFLOAD) ||
+ (etype == ETHERTYPE_IPV6 && flags & VTNET_CSUM_OFFLOAD_IPV6)) {
+ /*
+ * We could compare the IP protocol vs the CSUM_ flag too,
+ * but that really should not be necessary.
+ */
+ hdr->flags |= VIRTIO_NET_HDR_F_NEEDS_CSUM;
+ hdr->csum_start = csum_start;
+ hdr->csum_offset = m->m_pkthdr.csum_data;
+ txq->vtntx_stats.vtxs_csum++;
+ }
+
+ if (flags & CSUM_TSO) {
+ if (__predict_false(proto != IPPROTO_TCP)) {
+ /* Likely failed to correctly parse the mbuf. */
+ sc->vtnet_stats.tx_tso_not_tcp++;
+ goto drop;
+ }
+
+ KASSERT(hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM,
+ ("%s: mbuf %p TSO without checksum offload", __func__,
m));
+
+ error = vtnet_txq_offload_tso(txq, m, etype, csum_start,
hdr);
+ if (error)
+ goto drop;
+ }
+
+ return (m);
+
+drop:
+ m_freem(m);
+ return (NULL);
+}
+#endif
+
+static int
+vtnet_txq_enqueue_buf(struct vtnet_txq *txq, struct mbuf **m_head,
+ struct vtnet_tx_header *txhdr)
+{
+#ifdef NOTUSED
+ struct sglist sg;
+ struct sglist_seg segs[VTNET_MAX_TX_SEGS];
+ struct vtnet_softc *sc;
+ struct virtqueue *vq;
+ struct mbuf *m;
+ int collapsed, error;
+
+ vq = txq->vtntx_vq;
+ sc = txq->vtntx_sc;
+ m = *m_head;
+ collapsed = 0;
+
+ sglist_init(&sg, VTNET_MAX_TX_SEGS, segs);
+ error = sglist_append(&sg, &txhdr->vth_uhdr, sc->vtnet_hdr_size);
+ KASSERT(error == 0 && sg.sg_nseg == 1,
+ ("%s: error %d adding header to sglist", __func__, error));
+
+again:
+ error = sglist_append_mbuf(&sg, m);
+ if (error) {
+ if (collapsed)
+ goto fail;
+
+ m = m_collapse(m, M_NOWAIT, VTNET_MAX_TX_SEGS - 1);
+ if (m == NULL)
+ goto fail;
+
+ *m_head = m;
+ collapsed = 1;
+ txq->vtntx_stats.vtxs_collapsed++;
+ goto again;
+ }
+
+ txhdr->vth_mbuf = m;
+ error = virtqueue_enqueue(vq, txhdr, &sg, sg.sg_nseg, 0);
+#endif
+#ifdef RTEMS_VIRTIO_NET
+ struct mbuf *m;
+ struct virtqueue *vq;
+ int nsegs, error;
+
+ vq = txq->vtntx_vq;
+ nsegs = 0;
+
+ txhdr->vth_mbuf = (*m_head)->m_next;
+ for( m=*m_head ; m!=NULL ; m=m->m_next )
+ nsegs++;
+
+ error = rtems_virtqueue_enqueue(vq, *m_head, nsegs, 0);
+#endif
+
+ return (error);
+#ifdef NOTUSED
+fail:
+ m_freem(*m_head);
+ *m_head = NULL;
+
+ return (ENOBUFS);
+#endif
+}
+
+static int
+vtnet_txq_encap(struct vtnet_txq *txq, struct mbuf **m_head)
+{
+ struct vtnet_softc *sc;
+ struct vtnet_tx_header *txhdr;
+ struct virtio_net_hdr *hdr;
+ struct mbuf *m;
+ int error;
+
+ sc = txq->vtntx_sc;
+ m = *m_head;
+#ifdef NOTUSED
+ M_ASSERTPKTHDR(m);
+
+ txhdr = uma_zalloc(vtnet_tx_header_zone, M_NOWAIT | M_ZERO);
+ if( txhdr == NULL ){
+ m_freem(m);
+ *m_head = NULL;
+ return (ENOMEM);
+ }
+
+ /*
+ * Always use the non-mergeable header, regardless if the feature
+ * was negotiated. For transmit, num_buffers is always zero. The
+ * vtnet_hdr_size is used to enqueue the correct header size.
+ */
+ hdr = &txhdr->vth_uhdr.hdr;
+
+ if (m->m_flags & M_VLANTAG) {
+ m = ether_vlanencap(m, m->m_pkthdr.ether_vtag);
+ if ((*m_head = m) == NULL) {
+ error = ENOBUFS;
+ goto fail;
+ }
+ m->m_flags &= ~M_VLANTAG;
+ }
+
+ if (m->m_pkthdr.csum_flags & VTNET_CSUM_ALL_OFFLOAD) {
+ m = vtnet_txq_offload(txq, m, hdr);
+ if ((*m_head = m) == NULL) {
+ error = ENOBUFS;
+ goto fail;
+ }
+ }
+
+ error = vtnet_txq_enqueue_buf(txq, m_head, txhdr);
+#endif
+#ifdef RTEMS_VIRTIO_NET
+ struct mbuf *m_txhdr;
+
+ MGET(m_txhdr, M_DONTWAIT, MT_DATA);
+ if( m_txhdr == NULL ){
+ m_freem(m);
+ *m_head = NULL;
+ return (ENOMEM);
+ }
+ memset(m_txhdr->m_data, 0, sc->vtnet_hdr_size);
+ m_txhdr->m_len = sc->vtnet_hdr_size;
+ m_txhdr->m_next = *m_head;
+ txhdr = mtod(m_txhdr, struct vtnet_tx_header *);
+
+ error = vtnet_txq_enqueue_buf(txq, &m_txhdr, txhdr);
+#endif
+ if (error == 0)
+ return (0);
+
+#ifdef NOTUSED
+fail:
+ uma_zfree(vtnet_tx_header_zone, txhdr);
+#endif
+#ifdef RTEMS_VIRTIO_NET
+ m_free(m_txhdr);
+#endif
+
+ return (error);
+}
+
+#ifdef VTNET_LEGACY_TX
+static void
+vtnet_start_locked(struct vtnet_txq *txq, struct ifnet *ifp)
+{
+ struct vtnet_softc *sc;
+ struct virtqueue *vq;
+ struct mbuf *m0;
+ int enq;
+
+ sc = txq->vtntx_sc;
+ vq = txq->vtntx_vq;
+ enq = 0;
+
+ VTNET_TXQ_LOCK_ASSERT(txq);
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 ||
+ sc->vtnet_link_active == 0)
+ return;
+
+ vtnet_txq_eof(txq);
+
+ while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) {
+ if (virtqueue_full(vq))
+ break;
+
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m0);
+ if (m0 == NULL)
+ break;
+
+ if (vtnet_txq_encap(txq, &m0) != 0) {
+ if (m0 != NULL)
+ IFQ_DRV_PREPEND(&ifp->if_snd, m0);
+ break;
+ }
+
+ enq++;
+#ifdef NOTUSED
+ ETHER_BPF_MTAP(ifp, m0);
+#endif
+ }
+
+ if (enq > 0) {
+ virtqueue_notify(vq);
+ txq->vtntx_watchdog = VTNET_TX_TIMEOUT;
+ }
+}
+
+static void
+vtnet_start(struct ifnet *ifp)
+{
+ struct vtnet_softc *sc;
+ struct vtnet_txq *txq;
+
+ sc = ifp->if_softc;
+ txq = &sc->vtnet_txqs[0];
+
+ VTNET_TXQ_LOCK(txq);
+ vtnet_start_locked(txq, ifp);
+ VTNET_TXQ_UNLOCK(txq);
+}
+
+#else /* !VTNET_LEGACY_TX */
+
+static int
+vtnet_txq_mq_start_locked(struct vtnet_txq *txq, struct mbuf *m)
+{
+ struct vtnet_softc *sc;
+ struct virtqueue *vq;
+ struct buf_ring *br;
+ struct ifnet *ifp;
+ int enq, error;
+
+ sc = txq->vtntx_sc;
+ vq = txq->vtntx_vq;
+ br = txq->vtntx_br;
+ ifp = sc->vtnet_ifp;
+ enq = 0;
+ error = 0;
+
+ VTNET_TXQ_LOCK_ASSERT(txq);
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 ||
+ sc->vtnet_link_active == 0) {
+ if (m != NULL)
+ error = drbr_enqueue(ifp, br, m);
+ return (error);
+ }
+
+ if (m != NULL) {
+ error = drbr_enqueue(ifp, br, m);
+ if (error)
+ return (error);
+ }
+
+ vtnet_txq_eof(txq);
+
+ while ((m = drbr_peek(ifp, br)) != NULL) {
+ error = vtnet_txq_encap(txq, &m);
+ if (error) {
+ if (m != NULL)
+ drbr_putback(ifp, br, m);
+ else
+ drbr_advance(ifp, br);
+ break;
+ }
+ drbr_advance(ifp, br);
+
+ enq++;
+ ETHER_BPF_MTAP(ifp, m);
+ }
+
+ if (enq > 0) {
+ virtqueue_notify(vq);
+ txq->vtntx_watchdog = VTNET_TX_TIMEOUT;
+ }
+
+ return (error);
+}
+
+static int
+vtnet_txq_mq_start(struct ifnet *ifp, struct mbuf *m)
+{
+ struct vtnet_softc *sc;
+ struct vtnet_txq *txq;
+ int i, npairs, error;
+
+ sc = ifp->if_softc;
+ npairs = sc->vtnet_act_vq_pairs;
+
+ if (m->m_flags & M_FLOWID)
+ i = m->m_pkthdr.flowid % npairs;
+ else
+ i = curcpu % npairs;
+
+ txq = &sc->vtnet_txqs[i];
+
+ if (VTNET_TXQ_TRYLOCK(txq) != 0) {
+ error = vtnet_txq_mq_start_locked(txq, m);
+ VTNET_TXQ_UNLOCK(txq);
+ } else {
+ error = drbr_enqueue(ifp, txq->vtntx_br, m);
+ taskqueue_enqueue(txq->vtntx_tq, &txq->vtntx_defrtask);
+ }
+
+ return (error);
+}
+
+static void
+vtnet_txq_tq_deferred(void *xtxq, int pending)
+{
+ struct vtnet_softc *sc;
+ struct vtnet_txq *txq;
+
+ txq = xtxq;
+ sc = txq->vtntx_sc;
+
+ VTNET_TXQ_LOCK(txq);
+ if (!drbr_empty(sc->vtnet_ifp, txq->vtntx_br))
+ vtnet_txq_mq_start_locked(txq, NULL);
+ VTNET_TXQ_UNLOCK(txq);
+}
+
+#endif /* VTNET_LEGACY_TX */
+
+#ifdef NOTUSED
+static void
+vtnet_txq_tq_intr(void *xtxq, int pending)
+{
+ struct vtnet_softc *sc;
+ struct vtnet_txq *txq;
+ struct ifnet *ifp;
+
+ txq = xtxq;
+ sc = txq->vtntx_sc;
+ ifp = sc->vtnet_ifp;
+
+ VTNET_TXQ_LOCK(txq);
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
+ VTNET_TXQ_UNLOCK(txq);
+ return;
+ }
+
+ vtnet_txq_eof(txq);
+
+#ifdef VTNET_LEGACY_TX
+ if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+ vtnet_start_locked(txq, ifp);
+#else
+ if (!drbr_empty(ifp, txq->vtntx_br))
+ vtnet_txq_mq_start_locked(txq, NULL);
+#endif
+
+ if (vtnet_txq_enable_intr(txq) != 0) {
+ vtnet_txq_disable_intr(txq);
+ txq->vtntx_stats.vtxs_rescheduled++;
+ taskqueue_enqueue(txq->vtntx_tq, &txq->vtntx_intrtask);
+ }
+
+ VTNET_TXQ_UNLOCK(txq);
+}al
+#endif
+
+static void
+vtnet_txq_eof(struct vtnet_txq *txq)
+{
+ struct virtqueue *vq;
+ struct vtnet_tx_header *txhdr;
+ struct mbuf *m;
+
+ vq = txq->vtntx_vq;
+ VTNET_TXQ_LOCK_ASSERT(txq);
+
+#ifdef NOTUSED
+ while ((txhdr = virtqueue_dequeue(vq, NULL)) != NULL) {
+ m = txhdr->vth_mbuf;
+
+ txq->vtntx_stats.vtxs_opackets++;
+ txq->vtntx_stats.vtxs_obytes += m->m_pkthdr.len;
+ if (m->m_flags & M_MCAST)
+ txq->vtntx_stats.vtxs_omcasts++;
+
+ m_freem(m);
+ uma_zfree(vtnet_tx_header_zone, txhdr);
+ }
+#endif
+#ifdef RTEMS_VIRTIO_NET
+ struct mbuf *m_txhdr;
+
+ while((m_txhdr = virtqueue_dequeue(vq, NULL)) != NULL) {
+ txq->vtntx_stats.vtxs_opackets++;
+ txhdr = mtod(m_txhdr, struct vtnet_tx_header *);
+ txq->vtntx_stats.vtxs_obytes +=
txhdr->vth_mbuf->m_pkthdr.len;
+ if (m->m_flags & M_MCAST)
+ txq->vtntx_stats.vtxs_omcasts++;
+
+ m_freem(m_txhdr);
+ }
+#endif
+
+ if (virtqueue_empty(vq))
+ txq->vtntx_watchdog = 0;
+}
+
+static void
+vtnet_tx_vq_intr(void *xtxq)
+{
+ struct vtnet_softc *sc;
+ struct vtnet_txq *txq;
+ struct ifnet *ifp;
+ int tries;
+
+ txq = xtxq;
+ sc = txq->vtntx_sc;
+ ifp = sc->vtnet_ifp;
+ tries = 0;
+
+ if (__predict_false(txq->vtntx_id >= sc->vtnet_act_vq_pairs)) {
+ /*
+ * Ignore this interrupt. Either this is a spurious
interrupt
+ * or multiqueue without per-VQ MSIX so every queue needs to
+ * be polled (a brain dead configuration we could try harder
+ * to avoid).
+ */
+ vtnet_txq_disable_intr(txq);
+ return;
+ }
+
+again:
+ VTNET_TXQ_LOCK(txq);
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
+ VTNET_TXQ_UNLOCK(txq);
+ return;
+ }
+
+ vtnet_txq_eof(txq);
+
+#ifdef VTNET_LEGACY_TX
+ if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+ vtnet_start_locked(txq, ifp);
+#else
+ if (!drbr_empty(ifp, txq->vtntx_br))
+ vtnet_txq_mq_start_locked(txq, NULL);
+#endif
+
+ if (vtnet_txq_enable_intr(txq) != 0) {
+ vtnet_txq_disable_intr(txq);
+ /*
+ * This is an occasional race, so retry a few times
+ * before scheduling the taskqueue.
+ */
+ VTNET_TXQ_UNLOCK(txq);
+ if (tries++ < VTNET_INTR_DISABLE_RETRIES)
+ goto again;
+ txq->vtntx_stats.vtxs_rescheduled++;
+#ifdef NOTUSED
+ taskqueue_enqueue(txq->vtntx_tq, &txq->vtntx_intrtask);
+#endif
+ } else
+ VTNET_TXQ_UNLOCK(txq);
+}
+
+static void
+vtnet_tx_start_all(struct vtnet_softc *sc)
+{
+ struct ifnet *ifp;
+ struct vtnet_txq *txq;
+ int i;
+
+ ifp = sc->vtnet_ifp;
+ VTNET_CORE_LOCK_ASSERT(sc);
+
+ for (i = 0; i < sc->vtnet_act_vq_pairs; i++) {
+ txq = &sc->vtnet_txqs[i];
+
+ VTNET_TXQ_LOCK(txq);
+#ifdef VTNET_LEGACY_TX
+ if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+ vtnet_start_locked(txq, ifp);
+#else
+ if (!drbr_empty(ifp, txq->vtntx_br))
+ vtnet_txq_mq_start_locked(txq, NULL);
+#endif
+ VTNET_TXQ_UNLOCK(txq);
+ }
+}
+
+#ifndef VTNET_LEGACY_TX
+static void
+vtnet_qflush(struct ifnet *ifp)
+{
+ struct vtnet_softc *sc;
+ struct vtnet_txq *txq;
+ struct mbuf *m;
+ int i;
+
+ sc = ifp->if_softc;
+
+ for (i = 0; i < sc->vtnet_act_vq_pairs; i++) {
+ txq = &sc->vtnet_txqs[i];
+
+ VTNET_TXQ_LOCK(txq);
+ while ((m = buf_ring_dequeue_sc(txq->vtntx_br)) != NULL)
+ m_freem(m);
+ VTNET_TXQ_UNLOCK(txq);
+ }
+
+ if_qflush(ifp);
+}
+#endif
+
+static int
+vtnet_watchdog(struct vtnet_txq *txq)
+{
+ struct vtnet_softc *sc;
+
+ sc = txq->vtntx_sc;
+
+ VTNET_TXQ_LOCK(txq);
+ if (sc->vtnet_flags & VTNET_FLAG_EVENT_IDX)
+ vtnet_txq_eof(txq);
+ if (txq->vtntx_watchdog == 0 || --txq->vtntx_watchdog) {
+ VTNET_TXQ_UNLOCK(txq);
+ return (0);
+ }
+ VTNET_TXQ_UNLOCK(txq);
+
+#ifdef NOTUSED
+ if_printf(sc->vtnet_ifp, "watchdog timeout on queue %d\n",
+ txq->vtntx_id);
+#endif
+
+ return (1);
+}
+
+#ifdef NOTUSED
+static void
+vtnet_rxq_accum_stats(struct vtnet_rxq *rxq, struct vtnet_rxq_stats *accum)
+{
+ struct vtnet_rxq_stats *st;
+
+ st = &rxq->vtnrx_stats;
+
+ accum->vrxs_ipackets += st->vrxs_ipackets;
+ accum->vrxs_ibytes += st->vrxs_ibytes;
+ accum->vrxs_iqdrops += st->vrxs_iqdrops;
+ accum->vrxs_csum += st->vrxs_csum;
+ accum->vrxs_csum_failed += st->vrxs_csum_failed;
+ accum->vrxs_rescheduled += st->vrxs_rescheduled;
+}
+
+static void
+vtnet_txq_accum_stats(struct vtnet_txq *txq, struct vtnet_txq_stats *accum)
+{
+ struct vtnet_txq_stats *st;
+
+ st = &txq->vtntx_stats;
+
+ accum->vtxs_opackets += st->vtxs_opackets;
+ accum->vtxs_obytes += st->vtxs_obytes;
+ accum->vtxs_csum += st->vtxs_csum;
+ accum->vtxs_tso += st->vtxs_tso;
+ accum->vtxs_collapsed += st->vtxs_collapsed;
+ accum->vtxs_rescheduled += st->vtxs_rescheduled;
+}
+
+static void
+vtnet_accumulate_stats(struct vtnet_softc *sc)
+{
+ struct ifnet *ifp;
+ struct vtnet_statistics *st;
+ struct vtnet_rxq_stats rxaccum;
+ struct vtnet_txq_stats txaccum;
+ int i;
+
+ ifp = sc->vtnet_ifp;
+ st = &sc->vtnet_stats;
+ bzero(&rxaccum, sizeof(struct vtnet_rxq_stats));
+ bzero(&txaccum, sizeof(struct vtnet_txq_stats));
+
+ for (i = 0; i < sc->vtnet_max_vq_pairs; i++) {
+ vtnet_rxq_accum_stats(&sc->vtnet_rxqs[i], &rxaccum);
+ vtnet_txq_accum_stats(&sc->vtnet_txqs[i], &txaccum);
+ }
+
+ st->rx_csum_offloaded = rxaccum.vrxs_csum;
+ st->rx_csum_failed = rxaccum.vrxs_csum_failed;
+ st->rx_task_rescheduled = rxaccum.vrxs_rescheduled;
+ st->tx_csum_offloaded = txaccum.vtxs_csum;
+ st->tx_tso_offloaded = txaccum.vtxs_tso;
+ st->tx_task_rescheduled = txaccum.vtxs_rescheduled;
+
+ /*
+ * With the exception of if_ierrors, these ifnet statistics are
+ * only updated in the driver, so just set them to our accumulated
+ * values. if_ierrors is updated in ether_input() for malformed
+ * frames that we should have already discarded.
+ */
+ ifp->if_ipackets = rxaccum.vrxs_ipackets;
+ ifp->if_iqdrops = rxaccum.vrxs_iqdrops;
+ ifp->if_ierrors = rxaccum.vrxs_ierrors;
+ ifp->if_opackets = txaccum.vtxs_opackets;
+#ifndef VTNET_LEGACY_TX
+ ifp->if_obytes = txaccum.vtxs_obytes;
+ ifp->if_omcasts = txaccum.vtxs_omcasts;
+#endif
+}
+#endif
+
+static void
+vtnet_tick(void *xsc)
+{
+ struct vtnet_softc *sc;
+ struct ifnet *ifp;
+ int i, timedout;
+
+ sc = xsc;
+ ifp = sc->vtnet_ifp;
+ timedout = 0;
+
+ VTNET_CORE_LOCK_ASSERT(sc);
+#ifdef NOTUSED
+ vtnet_accumulate_stats(sc);
+#endif
+
+ for (i = 0; i < sc->vtnet_act_vq_pairs; i++)
+ timedout |= vtnet_watchdog(&sc->vtnet_txqs[i]);
+
+ if (timedout != 0) {
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+ vtnet_init_locked(sc);
+ } else
+#ifdef NOTUSED
+ callout_schedule(&sc->vtnet_tick_ch, hz);
+#endif
+#ifdef RTEMS_VIRTIO_NET
+ {
+ if( sc->stat_ch == vtnet_timeout_running ) {
+ timeout(vtnet_tick, sc, hz);
+ } else if ( sc->stat_ch == vtnet_timeout_stop_rq ) {
+ sc->stat_ch = vtnet_timeout_stopped;
+ }
+ }
+#endif
+}
+
+#ifdef NOTUSED
+static void
+vtnet_start_taskqueues(struct vtnet_softc *sc)
+{
+ device_t dev;
+ struct vtnet_rxq *rxq;
+ struct vtnet_txq *txq;
+ int i, error;
+
+ dev = sc->vtnet_dev;
+
+ /*
+ * Errors here are very difficult to recover from - we cannot
+ * easily fail because, if this is during boot, we will hang
+ * when freeing any successfully started taskqueues because
+ * the scheduler isn't up yet.
+ *
+ * Most drivers just ignore the return value - it only
+ * with ENOMEM so an error is not likely.
+ */
+ for (i = 0; i < sc->vtnet_max_vq_pairs; i++) {
+ rxq = &sc->vtnet_rxqs[i];
+ error = taskqueue_start_threads(&rxq->vtnrx_tq, 1, PI_NET,
+ "%s rxq %d", device_get_nameunit(dev), rxq->vtnrx_id);
+ if (error) {
+ device_printf(dev, "failed to start rx taskq %d\n",
+ rxq->vtnrx_id);
+ }
+
+ txq = &sc->vtnet_txqs[i];
+ error = taskqueue_start_threads(&txq->vtntx_tq, 1, PI_NET,
+ "%s txq %d", device_get_nameunit(dev), txq->vtntx_id);
+ if (error) {
+ device_printf(dev, "failed to start tx taskq %d\n",
+ txq->vtntx_id);
+ }
+ }
+}
+
+static void
+vtnet_free_taskqueues(struct vtnet_softc *sc)
+{
+ struct vtnet_rxq *rxq;
+ struct vtnet_txq *txq;
+ int i;
+
+ for (i = 0; i < sc->vtnet_max_vq_pairs; i++) {
+ rxq = &sc->vtnet_rxqs[i];
+ if (rxq->vtnrx_tq != NULL) {
+ taskqueue_free(rxq->vtnrx_tq);
+ rxq->vtnrx_vq = NULL;
+ }
+
+ txq = &sc->vtnet_txqs[i];
+ if (txq->vtntx_tq != NULL) {
+ taskqueue_free(txq->vtntx_tq);
+ txq->vtntx_tq = NULL;
+ }
+ }
+}
+
+static void
+vtnet_drain_taskqueues(struct vtnet_softc *sc)
+{
+ struct vtnet_rxq *rxq;
+ struct vtnet_txq *txq;
+ int i;
+
+ for (i = 0; i < sc->vtnet_max_vq_pairs; i++) {
+ rxq = &sc->vtnet_rxqs[i];
+ if (rxq->vtnrx_tq != NULL)
+ taskqueue_drain(rxq->vtnrx_tq,
&rxq->vtnrx_intrtask);
+
+ txq = &sc->vtnet_txqs[i];
+ if (txq->vtntx_tq != NULL) {
+ taskqueue_drain(txq->vtntx_tq,
&txq->vtntx_intrtask);
+#ifndef VTNET_LEGACY_TX
+ taskqueue_drain(txq->vtntx_tq,
&txq->vtntx_defrtask);
+#endif
+ }
+ }
+}
+#endif
+
+static void
+vtnet_drain_rxtx_queues(struct vtnet_softc *sc)
+{
+ struct vtnet_rxq *rxq;
+ struct vtnet_txq *txq;
+ int i;
+
+ for (i = 0; i < sc->vtnet_act_vq_pairs; i++) {
+ rxq = &sc->vtnet_rxqs[i];
+ vtnet_rxq_free_mbufs(rxq);
+
+ txq = &sc->vtnet_txqs[i];
+ vtnet_txq_free_mbufs(txq);
+ }
+}
+
+static void
+vtnet_stop_rendezvous(struct vtnet_softc *sc)
+{
+ struct vtnet_rxq *rxq;
+ struct vtnet_txq *txq;
+ int i;
+
+ /*
+ * Lock and unlock the per-queue mutex so we known the stop
+ * state is visible. Doing only the active queues should be
+ * sufficient, but it does not cost much extra to do all the
+ * queues. Note we hold the core mutex here too.
+ */
+ for (i = 0; i < sc->vtnet_max_vq_pairs; i++) {
+ rxq = &sc->vtnet_rxqs[i];
+ VTNET_RXQ_LOCK(rxq);
+ VTNET_RXQ_UNLOCK(rxq);
+
+ txq = &sc->vtnet_txqs[i];
+ VTNET_TXQ_LOCK(txq);
+ VTNET_TXQ_UNLOCK(txq);
+ }
+}
+
+static void
+vtnet_stop(struct vtnet_softc *sc)
+{
+ device_t dev;
+ struct ifnet *ifp;
+
+ dev = sc->vtnet_dev;
+ ifp = sc->vtnet_ifp;
+
+ VTNET_CORE_LOCK_ASSERT(sc);
+
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+ sc->vtnet_link_active = 0;
+#ifdef NOTUSED
+ callout_stop(&sc->vtnet_tick_ch);
+#endif
+#ifdef RTEMS_VIRTIO_NET
+ if( sc->stat_ch == vtnet_timeout_running ){
+ sc->stat_ch = vtnet_timeout_stop_rq;
+ while( sc->stat_ch != vtnet_timeout_stopped ){
+ rtems_bsdnet_semaphore_release();
+ rtems_task_wake_after( vtnet_ticksPerSecond );
+ rtems_bsdnet_semaphore_obtain();
+ }
+ }
+#endif
+
+ /* Only advisory. */
+ vtnet_disable_interrupts(sc);
+
+ /*
+ * Stop the host adapter. This resets it to the pre-initialized
+ * state. It will not generate any interrupts until after it is
+ * reinitialized.
+ */
+ virtio_stop(dev);
+ vtnet_stop_rendezvous(sc);
+
+ /* Free any mbufs left in the virtqueues. */
+ vtnet_drain_rxtx_queues(sc);
+}
+
+static int
+vtnet_virtio_reinit(struct vtnet_softc *sc)
+{
+ device_t dev;
+ struct ifnet *ifp;
+ uint64_t features;
+ int mask, error;
+
+ dev = sc->vtnet_dev;
+ ifp = sc->vtnet_ifp;
+ features = sc->vtnet_features;
+
+ mask = 0;
+#ifdef NOTUSED
+#if defined(INET)
+ mask |= IFCAP_RXCSUM;
+#endif
+#if defined (INET6)
+ mask |= IFCAP_RXCSUM_IPV6;
+#endif
+
+ /*
+ * Re-negotiate with the host, removing any disabled receive
+ * features. Transmit features are disabled only on our side
+ * via if_capenable and if_hwassist.
+ */
+
+ if (ifp->if_capabilities & mask) {
+ /*
+ * We require both IPv4 and IPv6 offloading to be enabled
+ * in order to negotiated it: VirtIO does not distinguish
+ * between the two.
+ */
+ if ((ifp->if_capenable & mask) != mask)
+ features &= ~VIRTIO_NET_F_GUEST_CSUM;
+ }
+
+ if (ifp->if_capabilities & IFCAP_LRO) {
+ if ((ifp->if_capenable & IFCAP_LRO) == 0)
+ features &= ~VTNET_LRO_FEATURES;
+ }
+
+ if (ifp->if_capabilities & IFCAP_VLAN_HWFILTER) {
+ if ((ifp->if_capenable & IFCAP_VLAN_HWFILTER) == 0)
+ features &= ~VIRTIO_NET_F_CTRL_VLAN;
+ }
+#endif
+
+ error = virtio_reinit(dev, features);
+ if (error)
+ device_printf(dev, "virtio reinit error %d\n", error);
+
+ return (error);
+}
+
+#ifdef NOTUSED
+static void
+vtnet_init_rx_filters(struct vtnet_softc *sc)
+{
+ struct ifnet *ifp;
+
+ ifp = sc->vtnet_ifp;
+
+ if (sc->vtnet_flags & VTNET_FLAG_CTRL_RX) {
+ /* Restore promiscuous and all-multicast modes. */
+ vtnet_rx_filter(sc);
+ /* Restore filtered MAC addresses. */
+ vtnet_rx_filter_mac(sc);
+ }
+
+ if (ifp->if_capenable & IFCAP_VLAN_HWFILTER)
+ vtnet_rx_filter_vlan(sc);
+}
+#endif
+
+static int
+vtnet_init_rx_queues(struct vtnet_softc *sc)
+{
+ device_t dev;
+ struct vtnet_rxq *rxq;
+ int i, clsize, error;
+
+ dev = sc->vtnet_dev;
+
+ /*
+ * Use the new cluster size if one has been set (via a MTU
+ * change). Otherwise, use the standard 2K clusters.
+ *
+ * BMV: It might make sense to use page sized clusters as
+ * the default (depending on the features negotiated).
+ */
+ if (sc->vtnet_rx_new_clsize != 0) {
+ clsize = sc->vtnet_rx_new_clsize;
+ sc->vtnet_rx_new_clsize = 0;
+ } else
+ clsize = MCLBYTES;
+
+ sc->vtnet_rx_clsize = clsize;
+ sc->vtnet_rx_nmbufs = VTNET_NEEDED_RX_MBUFS(sc, clsize);
+
+ /* The first segment is reserved for the header. */
+ KASSERT(sc->vtnet_rx_nmbufs < VTNET_MAX_RX_SEGS,
+ ("%s: too many rx mbufs %d", __func__, sc->vtnet_rx_nmbufs));
+
+ for (i = 0; i < sc->vtnet_act_vq_pairs; i++) {
+ rxq = &sc->vtnet_rxqs[i];
+
+ /* Hold the lock to satisfy asserts. */
+ VTNET_RXQ_LOCK(rxq);
+ error = vtnet_rxq_populate(rxq);
+ VTNET_RXQ_UNLOCK(rxq);
+
+ if (error) {
+ device_printf(dev,
+ "cannot allocate mbufs for Rx queue %d\n", i);
+ return (error);
+ }
+ }
+
+ return (0);
+}
+
+static int
+vtnet_init_tx_queues(struct vtnet_softc *sc)
+{
+ struct vtnet_txq *txq;
+ int i;
+
+ for (i = 0; i < sc->vtnet_act_vq_pairs; i++) {
+ txq = &sc->vtnet_txqs[i];
+ txq->vtntx_watchdog = 0;
+ }
+
+ return (0);
+}
+
+static int
+vtnet_init_rxtx_queues(struct vtnet_softc *sc)
+{
+ int error;
+
+ error = vtnet_init_rx_queues(sc);
+ if (error)
+ return (error);
+
+ error = vtnet_init_tx_queues(sc);
+ if (error)
+ return (error);
+
+ return (0);
+}
+
+static void
+vtnet_set_active_vq_pairs(struct vtnet_softc *sc)
+{
+ device_t dev;
+ int npairs;
+
+ dev = sc->vtnet_dev;
+
+ if ((sc->vtnet_flags & VTNET_FLAG_MULTIQ) == 0) {
+ MPASS(sc->vtnet_max_vq_pairs == 1);
+ sc->vtnet_act_vq_pairs = 1;
+ return;
+ }
+
+#ifdef NOTUSED
+ /* BMV: Just use the maximum configured for now. */
+ npairs = sc->vtnet_max_vq_pairs;
+
+ if (vtnet_ctrl_mq_cmd(sc, npairs) != 0) {
+ device_printf(dev,
+ "cannot set active queue pairs to %d\n", npairs);
+ npairs = 1;
+ }
+
+ sc->vtnet_act_vq_pairs = npairs;
+#endif
+
+ return;
+}
+
+static int
+vtnet_reinit(struct vtnet_softc *sc)
+{
+ device_t dev;
+ struct ifnet *ifp;
+ int error;
+
+ dev = sc->vtnet_dev;
+ ifp = sc->vtnet_ifp;
+
+ /* Use the current MAC address. */
+ bcopy(IF_LLADDR(ifp), sc->vtnet_hwaddr, ETHER_ADDR_LEN);
+ vtnet_set_hwaddr(sc);
+
+ vtnet_set_active_vq_pairs(sc);
+
+#ifdef NOTUSED
+ ifp->if_hwassist = 0;
+ if (ifp->if_capenable & IFCAP_TXCSUM)
+ ifp->if_hwassist |= VTNET_CSUM_OFFLOAD;
+ if (ifp->if_capenable & IFCAP_TXCSUM_IPV6)
+ ifp->if_hwassist |= VTNET_CSUM_OFFLOAD_IPV6;
+ if (ifp->if_capenable & IFCAP_TSO4)
+ ifp->if_hwassist |= CSUM_TSO;
+ if (ifp->if_capenable & IFCAP_TSO6)
+ ifp->if_hwassist |= CSUM_TSO; /* No CSUM_TSO_IPV6. */
+
+ if (sc->vtnet_flags & VTNET_FLAG_CTRL_VQ)
+ vtnet_init_rx_filters(sc);
+#endif
+
+ error = vtnet_init_rxtx_queues(sc);
+ if (error)
+ return (error);
+
+ vtnet_enable_interrupts(sc);
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ return (0);
+}
+
+static void
+vtnet_init_locked(struct vtnet_softc *sc)
+{
+ device_t dev;
+ struct ifnet *ifp;
+
+ dev = sc->vtnet_dev;
+ ifp = sc->vtnet_ifp;
+
+ VTNET_CORE_LOCK_ASSERT(sc);
+
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ return;
+
+ vtnet_stop(sc);
+
+ /* Reinitialize with the host. */
+ if (vtnet_virtio_reinit(sc) != 0)
+ goto fail;
+
+ if (vtnet_reinit(sc) != 0)
+ goto fail;
+
+ virtio_reinit_complete(dev);
+
+ vtnet_update_link_status(sc);
+#ifdef NOTUSED
+ callout_reset(&sc->vtnet_tick_ch, hz, vtnet_tick, sc);
+#endif
+#ifdef RTEMS_VIRTIO_NET
+ /* start vtnet_daemon */
+ if ( sc->vtpci_softc->daemonTid == 0 ) {
+ sc->vtpci_softc->daemonTid = rtems_bsdnet_newproc( "VTNd",
4096, vtnet_daemon, sc );
+ }
+
+ sc->stat_ch = vtnet_timeout_running;
+ timeout(vtnet_tick, sc, hz);
+#endif
+ return;
+
+fail:
+ vtnet_stop(sc);
+}
+
+static void
+vtnet_init(void *xsc)
+{
+ struct vtnet_softc *sc;
+
+ sc = xsc;
+
+ VTNET_CORE_LOCK(sc);
+ vtnet_init_locked(sc);
+ VTNET_CORE_UNLOCK(sc);
+}
+
+#ifdef NOTUSED
+static void
+vtnet_free_ctrl_vq(struct vtnet_softc *sc)
+{
+ struct virtqueue *vq;
+
+ vq = sc->vtnet_ctrl_vq;
+
+ /*
+ * The control virtqueue is only polled and therefore it should
+ * already be empty.
+ */
+ KASSERT(virtqueue_empty(vq),
+ ("%s: ctrl vq %p not empty", __func__, vq));
+}
+
+static void
+vtnet_exec_ctrl_cmd(struct vtnet_softc *sc, void *cookie,
+ struct sglist *sg, int readable, int writable)
+{
+ struct virtqueue *vq;
+
+ vq = sc->vtnet_ctrl_vq;
+
+ VTNET_CORE_LOCK_ASSERT(sc);
+ KASSERT(sc->vtnet_flags & VTNET_FLAG_CTRL_VQ,
+ ("%s: CTRL_VQ feature not negotiated", __func__));
+
+ if (!virtqueue_empty(vq))
+ return;
+ if (virtqueue_enqueue(vq, cookie, sg, readable, writable) != 0)
+ return;
+
+ /*
+ * Poll for the response, but the command is likely already
+ * done when we return from the notify.
+ */
+ virtqueue_notify(vq);
+ virtqueue_poll(vq, NULL);
+}
+
+static int
+vtnet_ctrl_mac_cmd(struct vtnet_softc *sc, uint8_t *hwaddr)
+{
+ struct virtio_net_ctrl_hdr hdr;
+ struct sglist_seg segs[3];
+ struct sglist sg;
+ uint8_t ack;
+ int error;
+
+ hdr.class = VIRTIO_NET_CTRL_MAC;
+ hdr.cmd = VIRTIO_NET_CTRL_MAC_ADDR_SET;
+ ack = VIRTIO_NET_ERR;
+
+ sglist_init(&sg, 3, segs);
+ error = 0;
+ error |= sglist_append(&sg, &hdr, sizeof(struct
virtio_net_ctrl_hdr));
+ error |= sglist_append(&sg, hwaddr, ETHER_ADDR_LEN);
+ error |= sglist_append(&sg, &ack, sizeof(uint8_t));
+ KASSERT(error == 0 && sg.sg_nseg == 3,
+ ("%s: error %d adding set MAC msg to sglist", __func__, error));
+
+ vtnet_exec_ctrl_cmd(sc, &ack, &sg, sg.sg_nseg - 1, 1);
+
+ return (ack == VIRTIO_NET_OK ? 0 : EIO);
+}
+
+static int
+vtnet_ctrl_mq_cmd(struct vtnet_softc *sc, uint16_t npairs)
+{
+ struct sglist_seg segs[3];
+ struct sglist sg;
+ struct {
+ struct virtio_net_ctrl_hdr hdr;
+ uint8_t pad1;
+ struct virtio_net_ctrl_mq mq;
+ uint8_t pad2;
+ uint8_t ack;
+ } s;
+ int error;
+
+ s.hdr.class = VIRTIO_NET_CTRL_MQ;
+ s.hdr.cmd = VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET;
+ s.mq.virtqueue_pairs = npairs;
+ s.ack = VIRTIO_NET_ERR;
+
+ sglist_init(&sg, 3, segs);
+ error = 0;
+ error |= sglist_append(&sg, &s.hdr, sizeof(struct
virtio_net_ctrl_hdr));
+ error |= sglist_append(&sg, &s.mq, sizeof(struct
virtio_net_ctrl_mq));
+ error |= sglist_append(&sg, &s.ack, sizeof(uint8_t));
+ KASSERT(error == 0 && sg.sg_nseg == 3,
+ ("%s: error %d adding MQ message to sglist", __func__, error));
+
+ vtnet_exec_ctrl_cmd(sc, &s.ack, &sg, sg.sg_nseg - 1, 1);
+
+ return (s.ack == VIRTIO_NET_OK ? 0 : EIO);
+}
+
+static int
+vtnet_ctrl_rx_cmd(struct vtnet_softc *sc, int cmd, int on)
+{
+ struct sglist_seg segs[3];
+ struct sglist sg;
+ struct {
+ struct virtio_net_ctrl_hdr hdr;
+ uint8_t pad1;
+ uint8_t onoff;
+ uint8_t pad2;
+ uint8_t ack;
+ } s;
+ int error;
+
+ KASSERT(sc->vtnet_flags & VTNET_FLAG_CTRL_RX,
+ ("%s: CTRL_RX feature not negotiated", __func__));
+
+ s.hdr.class = VIRTIO_NET_CTRL_RX;
+ s.hdr.cmd = cmd;
+ s.onoff = !!on;
+ s.ack = VIRTIO_NET_ERR;
+
+ sglist_init(&sg, 3, segs);
+ error = 0;
+ error |= sglist_append(&sg, &s.hdr, sizeof(struct
virtio_net_ctrl_hdr));
+ error |= sglist_append(&sg, &s.onoff, sizeof(uint8_t));
+ error |= sglist_append(&sg, &s.ack, sizeof(uint8_t));
+ KASSERT(error == 0 && sg.sg_nseg == 3,
+ ("%s: error %d adding Rx message to sglist", __func__, error));
+
+ vtnet_exec_ctrl_cmd(sc, &s.ack, &sg, sg.sg_nseg - 1, 1);
+
+ return (s.ack == VIRTIO_NET_OK ? 0 : EIO);
+}
+
+static int
+vtnet_set_promisc(struct vtnet_softc *sc, int on)
+{
+
+ return (vtnet_ctrl_rx_cmd(sc, VIRTIO_NET_CTRL_RX_PROMISC, on));
+}
+
+static int
+vtnet_set_allmulti(struct vtnet_softc *sc, int on)
+{
+
+ return (vtnet_ctrl_rx_cmd(sc, VIRTIO_NET_CTRL_RX_ALLMULTI, on));
+}
+
+/*
+ * The device defaults to promiscuous mode for backwards compatibility.
+ * Turn it off at attach time if possible.
+ */
+static void
+vtnet_attach_disable_promisc(struct vtnet_softc *sc)
+{
+ struct ifnet *ifp;
+
+ ifp = sc->vtnet_ifp;
+
+ VTNET_CORE_LOCK(sc);
+ if ((sc->vtnet_flags & VTNET_FLAG_CTRL_RX) == 0) {
+ ifp->if_flags |= IFF_PROMISC;
+ } else if (vtnet_set_promisc(sc, 0) != 0) {
+ ifp->if_flags |= IFF_PROMISC;
+ device_printf(sc->vtnet_dev,
+ "cannot disable default promiscuous mode\n");
+ }
+ VTNET_CORE_UNLOCK(sc);
+}
+
+static void
+vtnet_rx_filter(struct vtnet_softc *sc)
+{
+ device_t dev;
+ struct ifnet *ifp;
+
+ dev = sc->vtnet_dev;
+ ifp = sc->vtnet_ifp;
+
+ VTNET_CORE_LOCK_ASSERT(sc);
+
+ if (vtnet_set_promisc(sc, ifp->if_flags & IFF_PROMISC) != 0)
+ device_printf(dev, "cannot %s promiscuous mode\n",
+ ifp->if_flags & IFF_PROMISC ? "enable" : "disable");
+
+ if (vtnet_set_allmulti(sc, ifp->if_flags & IFF_ALLMULTI) != 0)
+ device_printf(dev, "cannot %s all-multicast mode\n",
+ ifp->if_flags & IFF_ALLMULTI ? "enable" : "disable");
+}
+
+static void
+vtnet_rx_filter_mac(struct vtnet_softc *sc)
+{
+ struct virtio_net_ctrl_hdr hdr;
+ struct vtnet_mac_filter *filter;
+ struct sglist_seg segs[4];
+ struct sglist sg;
+ struct ifnet *ifp;
+ struct ifaddr *ifa;
+ struct ifmultiaddr *ifma;
+ int ucnt, mcnt, promisc, allmulti, error;
+ uint8_t ack;
+
+ ifp = sc->vtnet_ifp;
+ filter = sc->vtnet_mac_filter;
+ ucnt = 0;
+ mcnt = 0;
+ promisc = 0;
+ allmulti = 0;
+
+ VTNET_CORE_LOCK_ASSERT(sc);
+ KASSERT(sc->vtnet_flags & VTNET_FLAG_CTRL_RX,
+ ("%s: CTRL_RX feature not negotiated", __func__));
+
+ /* Unicast MAC addresses: */
+ if_addr_rlock(ifp);
+ TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
+ if (ifa->ifa_addr->sa_family != AF_LINK)
+ continue;
+ else if (memcmp(LLADDR((struct sockaddr_dl *)ifa->ifa_addr),
+ sc->vtnet_hwaddr, ETHER_ADDR_LEN) == 0)
+ continue;
+ else if (ucnt == VTNET_MAX_MAC_ENTRIES) {
+ promisc = 1;
+ break;
+ }
+
+ bcopy(LLADDR((struct sockaddr_dl *)ifa->ifa_addr),
+ &filter->vmf_unicast.macs[ucnt], ETHER_ADDR_LEN);
+ ucnt++;
+ }
+ if_addr_runlock(ifp);
+
+ if (promisc != 0) {
+ filter->vmf_unicast.nentries = 0;
+ if_printf(ifp, "more than %d MAC addresses assigned, "
+ "falling back to promiscuous mode\n",
+ VTNET_MAX_MAC_ENTRIES);
+ } else
+ filter->vmf_unicast.nentries = ucnt;
+
+ /* Multicast MAC addresses: */
+ if_maddr_rlock(ifp);
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ else if (mcnt == VTNET_MAX_MAC_ENTRIES) {
+ allmulti = 1;
+ break;
+ }
+
+ bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
+ &filter->vmf_multicast.macs[mcnt], ETHER_ADDR_LEN);
+ mcnt++;
+ }
+ if_maddr_runlock(ifp);
+
+ if (allmulti != 0) {
+ filter->vmf_multicast.nentries = 0;
+ if_printf(ifp, "more than %d multicast MAC addresses "
+ "assigned, falling back to all-multicast mode\n",
+ VTNET_MAX_MAC_ENTRIES);
+ } else
+ filter->vmf_multicast.nentries = mcnt;
+
+ if (promisc != 0 && allmulti != 0)
+ goto out;
+
+ hdr.class = VIRTIO_NET_CTRL_MAC;
+ hdr.cmd = VIRTIO_NET_CTRL_MAC_TABLE_SET;
+ ack = VIRTIO_NET_ERR;
+
+ sglist_init(&sg, 4, segs);
+ error = 0;
+ error |= sglist_append(&sg, &hdr, sizeof(struct
virtio_net_ctrl_hdr));
+ error |= sglist_append(&sg, &filter->vmf_unicast,
+ sizeof(uint32_t) + filter->vmf_unicast.nentries *
ETHER_ADDR_LEN);
+ error |= sglist_append(&sg, &filter->vmf_multicast,
+ sizeof(uint32_t) + filter->vmf_multicast.nentries *
ETHER_ADDR_LEN);
+ error |= sglist_append(&sg, &ack, sizeof(uint8_t));
+ KASSERT(error == 0 && sg.sg_nseg == 4,
+ ("%s: error %d adding MAC filter msg to sglist", __func__,
error));
+
+ vtnet_exec_ctrl_cmd(sc, &ack, &sg, sg.sg_nseg - 1, 1);
+
+ if (ack != VIRTIO_NET_OK)
+ if_printf(ifp, "error setting host MAC filter table\n");
+
+out:
+ if (promisc != 0 && vtnet_set_promisc(sc, 1) != 0)
+ if_printf(ifp, "cannot enable promiscuous mode\n");
+ if (allmulti != 0 && vtnet_set_allmulti(sc, 1) != 0)
+ if_printf(ifp, "cannot enable all-multicast mode\n");
+}
+
+static int
+vtnet_exec_vlan_filter(struct vtnet_softc *sc, int add, uint16_t tag)
+{
+ struct sglist_seg segs[3];
+ struct sglist sg;
+ struct {
+ struct virtio_net_ctrl_hdr hdr;
+ uint8_t pad1;
+ uint16_t tag;
+ uint8_t pad2;
+ uint8_t ack;
+ } s;
+ int error;
+
+ s.hdr.class = VIRTIO_NET_CTRL_VLAN;
+ s.hdr.cmd = add ? VIRTIO_NET_CTRL_VLAN_ADD :
VIRTIO_NET_CTRL_VLAN_DEL;
+ s.tag = tag;
+ s.ack = VIRTIO_NET_ERR;
+
+ sglist_init(&sg, 3, segs);
+ error = 0;
+ error |= sglist_append(&sg, &s.hdr, sizeof(struct
virtio_net_ctrl_hdr));
+ error |= sglist_append(&sg, &s.tag, sizeof(uint16_t));
+ error |= sglist_append(&sg, &s.ack, sizeof(uint8_t));
+ KASSERT(error == 0 && sg.sg_nseg == 3,
+ ("%s: error %d adding VLAN message to sglist", __func__,
error));
+
+ vtnet_exec_ctrl_cmd(sc, &s.ack, &sg, sg.sg_nseg - 1, 1);
+
+ return (s.ack == VIRTIO_NET_OK ? 0 : EIO);
+}
+
+static void
+vtnet_rx_filter_vlan(struct vtnet_softc *sc)
+{
+ uint32_t w;
+ uint16_t tag;
+ int i, bit;
+
+ VTNET_CORE_LOCK_ASSERT(sc);
+ KASSERT(sc->vtnet_flags & VTNET_FLAG_VLAN_FILTER,
+ ("%s: VLAN_FILTER feature not negotiated", __func__));
+
+ /* Enable the filter for each configured VLAN. */
+ for (i = 0; i < VTNET_VLAN_FILTER_NWORDS; i++) {
+ w = sc->vtnet_vlan_filter[i];
+
+ while ((bit = ffs(w) - 1) != -1) {
+ w &= ~(1 << bit);
+ tag = sizeof(w) * CHAR_BIT * i + bit;
+
+ if (vtnet_exec_vlan_filter(sc, 1, tag) != 0) {
+ device_printf(sc->vtnet_dev,
+ "cannot enable VLAN %d filter\n", tag);
+ }
+ }
+ }
+}
+
+static void
+vtnet_update_vlan_filter(struct vtnet_softc *sc, int add, uint16_t tag)
+{
+ struct ifnet *ifp;
+ int idx, bit;
+
+ ifp = sc->vtnet_ifp;
+ idx = (tag >> 5) & 0x7F;
+ bit = tag & 0x1F;
+
+ if (tag == 0 || tag > 4095)
+ return;
+
+ VTNET_CORE_LOCK(sc);
+
+ if (add)
+ sc->vtnet_vlan_filter[idx] |= (1 << bit);
+ else
+ sc->vtnet_vlan_filter[idx] &= ~(1 << bit);
+
+ if (ifp->if_capenable & IFCAP_VLAN_HWFILTER &&
+ vtnet_exec_vlan_filter(sc, add, tag) != 0) {
+ device_printf(sc->vtnet_dev,
+ "cannot %s VLAN %d %s the host filter table\n",
+ add ? "add" : "remove", tag, add ? "to" : "from");
+ }
+
+ VTNET_CORE_UNLOCK(sc);
+}
+
+static void
+vtnet_register_vlan(void *arg, struct ifnet *ifp, uint16_t tag)
+{
+
+ if (ifp->if_softc != arg)
+ return;
+
+ vtnet_update_vlan_filter(arg, 1, tag);
+}
+
+static void
+vtnet_unregister_vlan(void *arg, struct ifnet *ifp, uint16_t tag)
+{
+
+ if (ifp->if_softc != arg)
+ return;
+
+ vtnet_update_vlan_filter(arg, 0, tag);
+}
+#endif
+
+static int
+vtnet_is_link_up(struct vtnet_softc *sc)
+{
+ device_t dev;
+ struct ifnet *ifp;
+ uint16_t status;
+
+ dev = sc->vtnet_dev;
+ ifp = sc->vtnet_ifp;
+
+#ifdef NOTUSED
+ if ((ifp->if_capabilities & IFCAP_LINKSTATE) == 0)
+ status = VIRTIO_NET_S_LINK_UP;
+ else
+ status = virtio_read_dev_config_2(dev,
+ offsetof(struct virtio_net_config, status));
+
+ return ((status & VIRTIO_NET_S_LINK_UP) != 0);
+#endif
+#ifdef RTEMS_VIRTIO_NET
+ return (1);
+#endif
+}
+
+static void
+vtnet_update_link_status(struct vtnet_softc *sc)
+{
+ struct ifnet *ifp;
+ int link;
+
+ ifp = sc->vtnet_ifp;
+
+ VTNET_CORE_LOCK_ASSERT(sc);
+ link = vtnet_is_link_up(sc);
+
+ /* Notify if the link status has changed. */
+ if (link != 0 && sc->vtnet_link_active == 0) {
+ sc->vtnet_link_active = 1;
+#ifdef NOTUSED
+ if_link_state_change(ifp, LINK_STATE_UP);
+#endif
+ } else if (link == 0 && sc->vtnet_link_active != 0) {
+ sc->vtnet_link_active = 0;
+#ifdef NOTUSED
+ if_link_state_change(ifp, LINK_STATE_DOWN);
+#endif
+ }
+}
+
+#ifdef NOTUSED
+static int
+vtnet_ifmedia_upd(struct ifnet *ifp)
+{
+ struct vtnet_softc *sc;
+ struct ifmedia *ifm;
+
+ sc = ifp->if_softc;
+ ifm = &sc->vtnet_media;
+
+ if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER)
+ return (EINVAL);
+
+ return (0);
+}
+
+static void
+vtnet_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+ struct vtnet_softc *sc;
+
+ sc = ifp->if_softc;
+
+ ifmr->ifm_status = IFM_AVALID;
+ ifmr->ifm_active = IFM_ETHER;
+
+ VTNET_CORE_LOCK(sc);
+ if (vtnet_is_link_up(sc) != 0) {
+ ifmr->ifm_status |= IFM_ACTIVE;
+ ifmr->ifm_active |= VTNET_MEDIATYPE;
+ } else
+ ifmr->ifm_active |= IFM_NONE;
+ VTNET_CORE_UNLOCK(sc);
+}
+#endif
+
+static void
+vtnet_set_hwaddr(struct vtnet_softc *sc)
+{
+ device_t dev;
+
+ dev = sc->vtnet_dev;
+
+ if (sc->vtnet_flags & VTNET_FLAG_CTRL_MAC) {
+#ifdef NOTUSED
+ if (vtnet_ctrl_mac_cmd(sc, sc->vtnet_hwaddr) != 0)
+ device_printf(dev, "unable to set MAC address\n");
+#endif
+ } else if (sc->vtnet_flags & VTNET_FLAG_MAC) {
+ virtio_write_device_config(dev,
+ offsetof(struct virtio_net_config, mac),
+ sc->vtnet_hwaddr, ETHER_ADDR_LEN);
+ }
+}
+
+static void
+vtnet_get_hwaddr(struct vtnet_softc *sc)
+{
+ device_t dev;
+
+ dev = sc->vtnet_dev;
+
+ if ((sc->vtnet_flags & VTNET_FLAG_MAC) == 0) {
+ /*
+ * Generate a random locally administered unicast address.
+ *
+ * It would be nice to generate the same MAC address across
+ * reboots, but it seems all the hosts currently available
+ * support the MAC feature, so this isn't too important.
+ */
+
+#ifdef NOTUSED
+ sc->vtnet_hwaddr[0] = 0xB2;
+ arc4rand(&sc->vtnet_hwaddr[1], ETHER_ADDR_LEN - 1, 0);
+#endif
+#ifdef RTEMS_VIRTIO_NET
+ sc->arpcom.ac_enaddr[ 0 ] = 0x08;
+ sc->arpcom.ac_enaddr[ 1 ] = 0x00;
+ sc->arpcom.ac_enaddr[ 2 ] = 0x27;
+ sc->arpcom.ac_enaddr[ 3 ] = 0x98;
+ sc->arpcom.ac_enaddr[ 4 ] = 0xe7;
+ sc->arpcom.ac_enaddr[ 5 ] = 0x0f;
+ bcopy(sc->arpcom.ac_enaddr, sc->vtnet_hwaddr,
ETHER_ADDR_LEN);
+#endif
+ vtnet_set_hwaddr(sc);
+ return;
+ }
+
+ virtio_read_device_config(dev, offsetof(struct virtio_net_config,
mac),
+ sc->vtnet_hwaddr, ETHER_ADDR_LEN);
+}
+
+#ifdef NOTUSED
+static void
+vtnet_vlan_tag_remove(struct mbuf *m)
+{
+ struct ether_vlan_header *evh;
+
+ evh = mtod(m, struct ether_vlan_header *);
+ m->m_pkthdr.ether_vtag = ntohs(evh->evl_tag);
+ m->m_flags |= M_VLANTAG;
+
+ /* Strip the 802.1Q header. */
+ bcopy((char *) evh, (char *) evh + ETHER_VLAN_ENCAP_LEN,
+ ETHER_HDR_LEN - ETHER_TYPE_LEN);
+ m_adj(m, ETHER_VLAN_ENCAP_LEN);
+}
+
+static void
+vtnet_setup_rxq_sysctl(struct sysctl_ctx_list *ctx,
+ struct sysctl_oid_list *child, struct vtnet_rxq *rxq)
+{
+ struct sysctl_oid *node;
+ struct sysctl_oid_list *list;
+ struct vtnet_rxq_stats *stats;
+ char namebuf[16];
+
+ snprintf(namebuf, sizeof(namebuf), "rxq%d", rxq->vtnrx_id);
+ node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, namebuf,
+ CTLFLAG_RD, NULL, "Receive Queue");
+ list = SYSCTL_CHILDREN(node);
+
+ stats = &rxq->vtnrx_stats;
+
+ SYSCTL_ADD_UQUAD(ctx, list, OID_AUTO, "ipackets", CTLFLAG_RD,
+ &stats->vrxs_ipackets, "Receive packets");
+ SYSCTL_ADD_UQUAD(ctx, list, OID_AUTO, "ibytes", CTLFLAG_RD,
+ &stats->vrxs_ibytes, "Receive bytes");
+ SYSCTL_ADD_UQUAD(ctx, list, OID_AUTO, "iqdrops", CTLFLAG_RD,
+ &stats->vrxs_iqdrops, "Receive drops");
+ SYSCTL_ADD_UQUAD(ctx, list, OID_AUTO, "ierrors", CTLFLAG_RD,
+ &stats->vrxs_ierrors, "Receive errors");
+ SYSCTL_ADD_UQUAD(ctx, list, OID_AUTO, "csum", CTLFLAG_RD,
+ &stats->vrxs_csum, "Receive checksum offloaded");
+ SYSCTL_ADD_UQUAD(ctx, list, OID_AUTO, "csum_failed", CTLFLAG_RD,
+ &stats->vrxs_csum_failed, "Receive checksum offload failed");
+ SYSCTL_ADD_UQUAD(ctx, list, OID_AUTO, "rescheduled", CTLFLAG_RD,
+ &stats->vrxs_rescheduled,
+ "Receive interrupt handler rescheduled");
+}
+
+static void
+vtnet_setup_txq_sysctl(struct sysctl_ctx_list *ctx,
+ struct sysctl_oid_list *child, struct vtnet_txq *txq)
+{
+ struct sysctl_oid *node;
+ struct sysctl_oid_list *list;
+ struct vtnet_txq_stats *stats;
+ char namebuf[16];
+
+ snprintf(namebuf, sizeof(namebuf), "txq%d", txq->vtntx_id);
+ node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, namebuf,
+ CTLFLAG_RD, NULL, "Transmit Queue");
+ list = SYSCTL_CHILDREN(node);
+
+ stats = &txq->vtntx_stats;
+
+ SYSCTL_ADD_UQUAD(ctx, list, OID_AUTO, "opackets", CTLFLAG_RD,
+ &stats->vtxs_opackets, "Transmit packets");
+ SYSCTL_ADD_UQUAD(ctx, list, OID_AUTO, "obytes", CTLFLAG_RD,
+ &stats->vtxs_obytes, "Transmit bytes");
+ SYSCTL_ADD_UQUAD(ctx, list, OID_AUTO, "omcasts", CTLFLAG_RD,
+ &stats->vtxs_omcasts, "Transmit multicasts");
+ SYSCTL_ADD_UQUAD(ctx, list, OID_AUTO, "csum", CTLFLAG_RD,
+ &stats->vtxs_csum, "Transmit checksum offloaded");
+ SYSCTL_ADD_UQUAD(ctx, list, OID_AUTO, "tso", CTLFLAG_RD,
+ &stats->vtxs_tso, "Transmit segmentation offloaded");
+ SYSCTL_ADD_UQUAD(ctx, list, OID_AUTO, "collapsed", CTLFLAG_RD,
+ &stats->vtxs_collapsed, "Transmit mbufs collapsed");
+ SYSCTL_ADD_UQUAD(ctx, list, OID_AUTO, "rescheduled", CTLFLAG_RD,
+ &stats->vtxs_rescheduled,
+ "Transmit interrupt handler rescheduled");
+}
+
+static void
+vtnet_setup_queue_sysctl(struct vtnet_softc *sc)
+{
+ device_t dev;
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid *tree;
+ struct sysctl_oid_list *child;
+ int i;
+
+ dev = sc->vtnet_dev;
+ ctx = device_get_sysctl_ctx(dev);
+ tree = device_get_sysctl_tree(dev);
+ child = SYSCTL_CHILDREN(tree);
+
+ for (i = 0; i < sc->vtnet_max_vq_pairs; i++) {
+ vtnet_setup_rxq_sysctl(ctx, child, &sc->vtnet_rxqs[i]);
+ vtnet_setup_txq_sysctl(ctx, child, &sc->vtnet_txqs[i]);
+ }
+}
+
+static void
+vtnet_setup_stat_sysctl(struct sysctl_ctx_list *ctx,
+ struct sysctl_oid_list *child, struct vtnet_softc *sc)
+{
+ struct vtnet_statistics *stats;
+
+ stats = &sc->vtnet_stats;
+
+ SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "mbuf_alloc_failed",
+ CTLFLAG_RD, &stats->mbuf_alloc_failed,
+ "Mbuf cluster allocation failures");
+
+ SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "rx_frame_too_large",
+ CTLFLAG_RD, &stats->rx_frame_too_large,
+ "Received frame larger than the mbuf chain");
+ SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "rx_enq_replacement_failed",
+ CTLFLAG_RD, &stats->rx_enq_replacement_failed,
+ "Enqueuing the replacement receive mbuf failed");
+ SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "rx_mergeable_failed",
+ CTLFLAG_RD, &stats->rx_mergeable_failed,
+ "Mergeable buffers receive failures");
+ SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "rx_csum_bad_ethtype",
+ CTLFLAG_RD, &stats->rx_csum_bad_ethtype,
+ "Received checksum offloaded buffer with unsupported "
+ "Ethernet type");
+ SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "rx_csum_bad_ipproto",
+ CTLFLAG_RD, &stats->rx_csum_bad_ipproto,
+ "Received checksum offloaded buffer with incorrect IP
protocol");
+ SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "rx_csum_bad_offset",
+ CTLFLAG_RD, &stats->rx_csum_bad_offset,
+ "Received checksum offloaded buffer with incorrect offset");
+ SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "rx_csum_bad_proto",
+ CTLFLAG_RD, &stats->rx_csum_bad_proto,
+ "Received checksum offloaded buffer with incorrect protocol");
+ SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "rx_csum_failed",
+ CTLFLAG_RD, &stats->rx_csum_failed,
+ "Received buffer checksum offload failed");
+ SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "rx_csum_offloaded",
+ CTLFLAG_RD, &stats->rx_csum_offloaded,
+ "Received buffer checksum offload succeeded");
+ SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "rx_task_rescheduled",
+ CTLFLAG_RD, &stats->rx_task_rescheduled,
+ "Times the receive interrupt task rescheduled itself");
+
+ SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "tx_csum_bad_ethtype",
+ CTLFLAG_RD, &stats->tx_csum_bad_ethtype,
+ "Aborted transmit of checksum offloaded buffer with unknown "
+ "Ethernet type");
+ SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "tx_tso_bad_ethtype",
+ CTLFLAG_RD, &stats->tx_tso_bad_ethtype,
+ "Aborted transmit of TSO buffer with unknown Ethernet type");
+ SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "tx_tso_not_tcp",
+ CTLFLAG_RD, &stats->tx_tso_not_tcp,
+ "Aborted transmit of TSO buffer with non TCP protocol");
+ SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "tx_csum_offloaded",
+ CTLFLAG_RD, &stats->tx_csum_offloaded,
+ "Offloaded checksum of transmitted buffer");
+ SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "tx_tso_offloaded",
+ CTLFLAG_RD, &stats->tx_tso_offloaded,
+ "Segmentation offload of transmitted buffer");
+ SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "tx_task_rescheduled",
+ CTLFLAG_RD, &stats->tx_task_rescheduled,
+ "Times the transmit interrupt task rescheduled itself");
+}
+
+static void
+vtnet_setup_sysctl(struct vtnet_softc *sc)
+{
+ device_t dev;
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid *tree;
+ struct sysctl_oid_list *child;
+
+ dev = sc->vtnet_dev;
+ ctx = device_get_sysctl_ctx(dev);
+ tree = device_get_sysctl_tree(dev);
+ child = SYSCTL_CHILDREN(tree);
+
+ SYSCTL_ADD_INT(ctx, child, OID_AUTO, "max_vq_pairs",
+ CTLFLAG_RD, &sc->vtnet_max_vq_pairs, 0,
+ "Maximum number of supported virtqueue pairs");
+ SYSCTL_ADD_INT(ctx, child, OID_AUTO, "act_vq_pairs",
+ CTLFLAG_RD, &sc->vtnet_act_vq_pairs, 0,
+ "Number of active virtqueue pairs");
+
+ vtnet_setup_stat_sysctl(ctx, child, sc);
+}
+#endif
+
+static int
+vtnet_rxq_enable_intr(struct vtnet_rxq *rxq)
+{
+
+ return (virtqueue_enable_intr(rxq->vtnrx_vq));
+}
+
+static void
+vtnet_rxq_disable_intr(struct vtnet_rxq *rxq)
+{
+
+ virtqueue_disable_intr(rxq->vtnrx_vq);
+}
+
+static int
+vtnet_txq_enable_intr(struct vtnet_txq *txq)
+{
+
+ return (virtqueue_postpone_intr(txq->vtntx_vq, VQ_POSTPONE_LONG));
+}
+
+static void
+vtnet_txq_disable_intr(struct vtnet_txq *txq)
+{
+
+ virtqueue_disable_intr(txq->vtntx_vq);
+}
+
+static void
+vtnet_enable_rx_interrupts(struct vtnet_softc *sc)
+{
+ int i;
+
+ for (i = 0; i < sc->vtnet_act_vq_pairs; i++)
+ vtnet_rxq_enable_intr(&sc->vtnet_rxqs[i]);
+}
+
+static void
+vtnet_enable_tx_interrupts(struct vtnet_softc *sc)
+{
+ int i;
+
+ for (i = 0; i < sc->vtnet_act_vq_pairs; i++)
+ vtnet_txq_enable_intr(&sc->vtnet_txqs[i]);
+}
+
+static void
+vtnet_enable_interrupts(struct vtnet_softc *sc)
+{
+
+ vtnet_enable_rx_interrupts(sc);
+ vtnet_enable_tx_interrupts(sc);
+}
+
+static void
+vtnet_disable_rx_interrupts(struct vtnet_softc *sc)
+{
+ int i;
+
+ for (i = 0; i < sc->vtnet_act_vq_pairs; i++)
+ vtnet_rxq_disable_intr(&sc->vtnet_rxqs[i]);
+}
+
+static void
+vtnet_disable_tx_interrupts(struct vtnet_softc *sc)
+{
+ int i;
+
+ for (i = 0; i < sc->vtnet_act_vq_pairs; i++)
+ vtnet_txq_disable_intr(&sc->vtnet_txqs[i]);
+}
+
+static void
+vtnet_disable_interrupts(struct vtnet_softc *sc)
+{
+
+ vtnet_disable_rx_interrupts(sc);
+ vtnet_disable_tx_interrupts(sc);
+}
+
+#ifdef NOTUSED
+static int
+vtnet_tunable_int(struct vtnet_softc *sc, const char *knob, int def)
+{
+ char path[64];
+
+ snprintf(path, sizeof(path),
+ "hw.vtnet.%d.%s", device_get_unit(sc->vtnet_dev), knob);
+ TUNABLE_INT_FETCH(path, &def);
+
+ return (def);
+}
+#endif
diff --git a/c/src/lib/libbsp/i386/pc386/virtio/if_vtnetvar.h
b/c/src/lib/libbsp/i386/pc386/virtio/if_vtnetvar.h
new file mode 100644
index 0000000..5d2bd78
--- /dev/null
+++ b/c/src/lib/libbsp/i386/pc386/virtio/if_vtnetvar.h
@@ -0,0 +1,391 @@
+/**
+ * @file if_vtnetvar.h
+ * @brief Header for if_vtnet.c
+ */
+
+/*
+ * Authors: Jin-Hyun Kim <jinhyun at konkuk.ac.kr>,
+ * and Hyun-Wook Jin <jinh at konkuk.ac.kr>, http://sslab.konkuk.ac.kr
+ * Ported from FreeBSD to RTEMS March 16
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.NET
+ */
+
+/*-
+ * Copyright (c) 2011, Bryan Venteicher <bryanv at FreeBSD.org>
+ * All rights reserved.
+ *
+ * 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 unmodified, 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 ``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 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: release/10.0.0/sys/dev/virtio/network/if_vtnetvar.h 255167
2013-09-03 02:28:31Z bryanv $
+ */
+
+#ifndef _IF_VTNETVAR_H
+#define _IF_VTNETVAR_H
+
+struct vtnet_softc;
+struct vtpci_softc;
+
+struct vtnet_statistics {
+ uint64_t mbuf_alloc_failed;
+
+ uint64_t rx_frame_too_large;
+ uint64_t rx_enq_replacement_failed;
+ uint64_t rx_mergeable_failed;
+ uint64_t rx_csum_bad_ethtype;
+ uint64_t rx_csum_bad_ipproto;
+ uint64_t rx_csum_bad_offset;
+ uint64_t rx_csum_bad_proto;
+ uint64_t tx_csum_bad_ethtype;
+ uint64_t tx_tso_bad_ethtype;
+ uint64_t tx_tso_not_tcp;
+
+ /*
+ * These are accumulated from each Rx/Tx queue.
+ */
+ uint64_t rx_csum_failed;
+ uint64_t rx_csum_offloaded;
+ uint64_t rx_task_rescheduled;
+ uint64_t tx_csum_offloaded;
+ uint64_t tx_tso_offloaded;
+ uint64_t tx_task_rescheduled;
+};
+
+struct vtnet_rxq_stats {
+ uint64_t vrxs_ipackets; /* if_ipackets */
+ uint64_t vrxs_ibytes; /* if_ibytes */
+ uint64_t vrxs_iqdrops; /* if_iqdrops */
+ uint64_t vrxs_ierrors; /* if_ierrors */
+ uint64_t vrxs_csum;
+ uint64_t vrxs_csum_failed;
+ uint64_t vrxs_rescheduled;
+};
+
+struct vtnet_rxq {
+ struct mtx vtnrx_mtx;
+ struct vtnet_softc *vtnrx_sc;
+ struct virtqueue *vtnrx_vq;
+ int vtnrx_id;
+ int vtnrx_process_limit;
+ struct vtnet_rxq_stats vtnrx_stats;
+#ifdef NOTUSED
+ struct taskqueue *vtnrx_tq;
+ struct task vtnrx_intrtask;
+#endif
+ char vtnrx_name[16];
+} __aligned(CACHE_LINE_SIZE);
+
+#define VTNET_RXQ_LOCK(_rxq) mtx_lock(&(_rxq)->vtnrx_mtx)
+#define VTNET_RXQ_UNLOCK(_rxq) mtx_unlock(&(_rxq)->vtnrx_mtx)
+#define VTNET_RXQ_LOCK_ASSERT(_rxq) \
+ mtx_assert(&(_rxq)->vtnrx_mtx, MA_OWNED)
+#define VTNET_RXQ_LOCK_ASSERT_NOTOWNED(_rxq) \
+ mtx_assert(&(_rxq)->vtnrx_mtx, MA_NOTOWNED)
+
+struct vtnet_txq_stats {
+ uint64_t vtxs_opackets; /* if_opackets */
+ uint64_t vtxs_obytes; /* if_obytes */
+ uint64_t vtxs_omcasts; /* if_omcasts */
+ uint64_t vtxs_csum;
+ uint64_t vtxs_tso;
+ uint64_t vtxs_collapsed;
+ uint64_t vtxs_rescheduled;
+};
+
+struct vtnet_txq {
+ struct mtx vtntx_mtx;
+ struct vtnet_softc *vtntx_sc;
+ struct virtqueue *vtntx_vq;
+#ifndef VTNET_LEGACY_TX
+ struct buf_ring *vtntx_br;
+#endif
+ int vtntx_id;
+ int vtntx_watchdog;
+ struct vtnet_txq_stats vtntx_stats;
+#ifdef NOTUSED
+ struct taskqueue *vtntx_tq;
+ struct task vtntx_intrtask;
+#endif
+#ifndef VTNET_LEGACY_TX
+ struct task vtntx_defrtask;
+#endif
+ char vtntx_name[16];
+} __aligned(CACHE_LINE_SIZE);
+
+#define VTNET_TXQ_LOCK(_txq) mtx_lock(&(_txq)->vtntx_mtx)
+#define VTNET_TXQ_TRYLOCK(_txq) mtx_trylock(&(_txq)->vtntx_mtx)
+#define VTNET_TXQ_UNLOCK(_txq) mtx_unlock(&(_txq)->vtntx_mtx)
+#define VTNET_TXQ_LOCK_ASSERT(_txq) \
+ mtx_assert(&(_txq)->vtntx_mtx, MA_OWNED)
+#define VTNET_TXQ_LOCK_ASSERT_NOTOWNED(_txq) \
+ mtx_assert(&(_txq)->vtntx_mtx, MA_NOTOWNED)
+
+#define VTNET_FLAG_SUSPENDED 0x0001
+#define VTNET_FLAG_MAC 0x0002
+#define VTNET_FLAG_CTRL_VQ 0x0004
+#define VTNET_FLAG_CTRL_RX 0x0008
+#define VTNET_FLAG_CTRL_MAC 0x0010
+#define VTNET_FLAG_VLAN_FILTER 0x0020
+#define VTNET_FLAG_TSO_ECN 0x0040
+#define VTNET_FLAG_MRG_RXBUFS 0x0080
+#define VTNET_FLAG_LRO_NOMRG 0x0100
+#define VTNET_FLAG_MULTIQ 0x0200
+#define VTNET_FLAG_EVENT_IDX 0x0400
+
+struct vtnet_softc {
+#ifdef RTEMS_VIRTIO_NET
+ struct arpcom arpcom;
+ struct rtems_bsdnet_ifconfig *config;
+ struct vtpci_softc *vtpci_softc;
+ enum { vtnet_timeout_stopped, vtnet_timeout_running,
vtnet_timeout_stop_rq }
+ stat_ch;
+#endif
+ device_t vtnet_dev;
+ struct ifnet *vtnet_ifp;
+ struct vtnet_rxq *vtnet_rxqs;
+ struct vtnet_txq *vtnet_txqs;
+
+ uint32_t vtnet_flags;
+
+ int vtnet_link_active;
+ int vtnet_hdr_size;
+ int vtnet_rx_process_limit;
+ int vtnet_rx_nmbufs;
+ int vtnet_rx_clsize;
+ int vtnet_rx_new_clsize;
+ int vtnet_if_flags;
+ int vtnet_act_vq_pairs;
+ int vtnet_max_vq_pairs;
+
+ struct virtqueue *vtnet_ctrl_vq;
+#ifdef NOTUSED
+ struct vtnet_mac_filter *vtnet_mac_filter;
+ uint32_t *vtnet_vlan_filter;
+#endif
+
+ uint64_t vtnet_features;
+ struct vtnet_statistics vtnet_stats;
+#ifdef NOTUSED
+ struct callout vtnet_tick_ch;
+ struct ifmedia vtnet_media;
+ eventhandler_tag vtnet_vlan_attach;
+ eventhandler_tag vtnet_vlan_detach;
+#endif
+
+ struct mtx vtnet_mtx;
+ char vtnet_mtx_name[16];
+ char vtnet_hwaddr[ETHER_ADDR_LEN];
+};
+
+/*
+ * Maximum number of queue pairs we will autoconfigure to.
+ */
+#define VTNET_MAX_QUEUE_PAIRS 8
+
+/*
+ * Additional completed entries can appear in a virtqueue before we can
+ * reenable interrupts. Number of times to retry before scheduling the
+ * taskqueue to process the completed entries.
+ */
+#define VTNET_INTR_DISABLE_RETRIES 4
+
+#ifdef NOTUSED
+/*
+ * Fake the media type. The host does not provide us with any real media
+ * information.
+ */
+#define VTNET_MEDIATYPE (IFM_ETHER | IFM_10G_T | IFM_FDX)
+#endif
+
+/*
+ * Number of words to allocate for the VLAN shadow table. There is one
+ * bit for each VLAN.
+ */
+#define VTNET_VLAN_FILTER_NWORDS (4096 / 32)
+
+/*
+ * When mergeable buffers are not negotiated, the vtnet_rx_header structure
+ * below is placed at the beginning of the mbuf data. Use 4 bytes of pad to
+ * both keep the VirtIO header and the data non-contiguous and to keep the
+ * frame's payload 4 byte aligned.
+ *
+ * When mergeable buffers are negotiated, the host puts the VirtIO header
in
+ * the beginning of the first mbuf's data.
+ */
+#define VTNET_RX_HEADER_PAD 4
+struct vtnet_rx_header {
+ struct virtio_net_hdr vrh_hdr;
+ char vrh_pad[VTNET_RX_HEADER_PAD];
+} __packed;
+
+/*
+ * For each outgoing frame, the vtnet_tx_header below is allocated from
+ * the vtnet_tx_header_zone.
+ */
+struct vtnet_tx_header {
+ union {
+ struct virtio_net_hdr hdr;
+ struct virtio_net_hdr_mrg_rxbuf mhdr;
+ } vth_uhdr;
+
+ struct mbuf *vth_mbuf;
+};
+
+/*
+ * The VirtIO specification does not place a limit on the number of MAC
+ * addresses the guest driver may request to be filtered. In practice,
+ * the host is constrained by available resources. To simplify this driver,
+ * impose a reasonably high limit of MAC addresses we will filter before
+ * falling back to promiscuous or all-multicast modes.
+ */
+#define VTNET_MAX_MAC_ENTRIES 128
+#
+struct vtnet_mac_table {
+ uint32_t nentries;
+ uint8_t macs[VTNET_MAX_MAC_ENTRIES][ETHER_ADDR_LEN];
+} __packed;
+
+struct vtnet_mac_filter {
+ struct vtnet_mac_table vmf_unicast;
+ uint32_t vmf_pad; /* Make tables non-contiguous. */
+ struct vtnet_mac_table vmf_multicast;
+};
+
+#ifdef NOTUSED
+/*
+ * The MAC filter table is malloc(9)'d when needed. Ensure it will
+ * always fit in one segment.
+ */
+CTASSERT(sizeof(struct vtnet_mac_filter) <= PAGE_SIZE);
+#endif
+
+#define VTNET_TX_TIMEOUT 5
+#define VTNET_CSUM_OFFLOAD (CSUM_TCP | CSUM_UDP | CSUM_SCTP)
+#define VTNET_CSUM_OFFLOAD_IPV6 (CSUM_TCP_IPV6 | CSUM_UDP_IPV6 |
CSUM_SCTP_IPV6)
+
+#define VTNET_CSUM_ALL_OFFLOAD \
+ (VTNET_CSUM_OFFLOAD | VTNET_CSUM_OFFLOAD_IPV6 | CSUM_TSO)
+
+/* Features desired/implemented by this driver. */
+#ifdef NOTUSED
+#define VTNET_FEATURES \
+ (VIRTIO_NET_F_MAC | \
+ VIRTIO_NET_F_STATUS | \
+ VIRTIO_NET_F_CTRL_VQ | \
+ VIRTIO_NET_F_CTRL_RX | \
+ VIRTIO_NET_F_CTRL_MAC_ADDR | \
+ VIRTIO_NET_F_CTRL_VLAN | \
+ VIRTIO_NET_F_CSUM | \
+ VIRTIO_NET_F_GSO | \
+ VIRTIO_NET_F_HOST_TSO4 | \
+ VIRTIO_NET_F_HOST_TSO6 | \
+ VIRTIO_NET_F_HOST_ECN | \
+ VIRTIO_NET_F_GUEST_CSUM | \
+ VIRTIO_NET_F_GUEST_TSO4 | \
+ VIRTIO_NET_F_GUEST_TSO6 | \
+ VIRTIO_NET_F_GUEST_ECN | \
+ VIRTIO_NET_F_MRG_RXBUF | \
+ VIRTIO_NET_F_MQ | \
+ VIRTIO_RING_F_EVENT_IDX | \
+ VIRTIO_RING_F_INDIRECT_DESC)
+#endif
+#ifdef RTEMS_VIRTIO_NET
+#define VTNET_FEATURES \
+ ( VIRTIO_NET_F_CSUM | \
+ VIRTIO_NET_F_GSO | \
+ VIRTIO_NET_F_HOST_TSO4 | \
+ VIRTIO_NET_F_HOST_TSO6 | \
+ VIRTIO_NET_F_HOST_ECN | \
+ VIRTIO_NET_F_HOST_UFO | \
+ VIRTIO_NET_F_MRG_RXBUF )
+#endif
+
+/*
+ * The VIRTIO_NET_F_HOST_TSO[46] features permit us to send the host
+ * frames larger than 1514 bytes.
+ */
+#define VTNET_TSO_FEATURES (VIRTIO_NET_F_GSO | VIRTIO_NET_F_HOST_TSO4 | \
+ VIRTIO_NET_F_HOST_TSO6 | VIRTIO_NET_F_HOST_ECN)
+
+/*
+ * The VIRTIO_NET_F_GUEST_TSO[46] features permit the host to send us
+ * frames larger than 1514 bytes. We do not yet support software LRO
+ * via tcp_lro_rx().
+ */
+#define VTNET_LRO_FEATURES (VIRTIO_NET_F_GUEST_TSO4 | \
+ VIRTIO_NET_F_GUEST_TSO6 | VIRTIO_NET_F_GUEST_ECN)
+
+#define VTNET_MAX_MTU 65536
+#define VTNET_MAX_RX_SIZE 65550
+
+/*
+ * Used to preallocate the Vq indirect descriptors. The first segment
+ * is reserved for the header.
+ */
+#define VTNET_MIN_RX_SEGS 2
+#define VTNET_MAX_RX_SEGS 34
+#define VTNET_MAX_TX_SEGS 34
+
+#ifdef NOTUSED
+/*
+ * Assert we can receive and transmit the maximum with regular
+ * size clusters.
+ */
+CTASSERT(((VTNET_MAX_RX_SEGS - 1) * MCLBYTES) >= VTNET_MAX_RX_SIZE);
+CTASSERT(((VTNET_MAX_TX_SEGS - 1) * MCLBYTES) >= VTNET_MAX_MTU);
+#endif
+
+/*
+ * Number of slots in the Tx bufrings. This value matches most other
+ * multiqueue drivers.
+ */
+#define VTNET_DEFAULT_BUFRING_SIZE 4096
+
+/*
+ * Determine how many mbufs are in each receive buffer. For LRO without
+ * mergeable descriptors, we must allocate an mbuf chain large enough to
+ * hold both the vtnet_rx_header and the maximum receivable data.
+ */
+#define VTNET_NEEDED_RX_MBUFS(_sc, _clsize) \
+ ((_sc)->vtnet_flags & VTNET_FLAG_LRO_NOMRG) == 0 ? 1 : \
+ howmany(sizeof(struct vtnet_rx_header) + VTNET_MAX_RX_SIZE, \
+ (_clsize))
+
+#define VTNET_CORE_MTX(_sc) &(_sc)->vtnet_mtx
+#define VTNET_CORE_LOCK(_sc) mtx_lock(VTNET_CORE_MTX((_sc)))
+#define VTNET_CORE_UNLOCK(_sc) mtx_unlock(VTNET_CORE_MTX((_sc)))
+#define VTNET_CORE_LOCK_DESTROY(_sc) mtx_destroy(VTNET_CORE_MTX((_sc)))
+#define VTNET_CORE_LOCK_ASSERT(_sc) \
+ mtx_assert(VTNET_CORE_MTX((_sc)), MA_OWNED)
+#define VTNET_CORE_LOCK_ASSERT_NOTOWNED(_sc) \
+ mtx_assert(VTNET_CORE_MTX((_sc)), MA_NOTOWNED)
+
+#define VTNET_CORE_LOCK_INIT(_sc) do { \
+ snprintf((_sc)->vtnet_mtx_name, sizeof((_sc)->vtnet_mtx_name), \
+ "%s", device_get_nameunit((_sc)->vtnet_dev)); \
+ mtx_init(VTNET_CORE_MTX((_sc)), (_sc)->vtnet_mtx_name, \
+ "VTNET Core Lock", MTX_DEF); \
+} while (0)
+
+#endif /* _IF_VTNETVAR_H */
diff --git a/c/src/lib/libbsp/i386/pc386/virtio/virtio.c
b/c/src/lib/libbsp/i386/pc386/virtio/virtio.c
new file mode 100644
index 0000000..5a99122
--- /dev/null
+++ b/c/src/lib/libbsp/i386/pc386/virtio/virtio.c
@@ -0,0 +1,291 @@
+/**
+ * @file virtio.c
+ * @brief
+ */
+
+/*
+ * Authors: Jin-Hyun Kim <jinhyun at konkuk.ac.kr>,
+ * and Hyun-Wook Jin <jinh at konkuk.ac.kr>, http://sslab.konkuk.ac.kr
+ * Ported from FreeBSD to RTEMS March 16
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.NET
+ */
+
+/*-
+ * Copyright (c) 2011, Bryan Venteicher <bryanv at FreeBSD.org>
+ * All rights reserved.
+ *
+ * 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 unmodified, 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 ``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 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: release/10.0.0/sys/dev/virtio/virtio.c 252707 2013-07-04
17:57:26Z bryanv $
+ */
+
+#define VTNET_LEGACY_TX
+#define RTEMS_VIRTIO_NET
+
+#include <rtems.h>
+#include <rtems/rtems_bsdnet.h>
+
+#include "virtio.h"
+#include "virtqueue.h"
+#include "virtio_pci.h"
+
+#ifdef NOTUSED
+static int virtio_modevent(module_t, int, void *);
+static const char *virtio_feature_name(uint64_t, struct virtio_feature_desc
*);
+
+static struct virtio_ident {
+ uint16_t devid;
+ const char *name;
+} virtio_ident_table[] = {
+ { VIRTIO_ID_NETWORK, "Network" }
+ { VIRTIO_ID_BLOCK, "Block" },
+ { VIRTIO_ID_CONSOLE, "Console" },
+ { VIRTIO_ID_ENTROPY, "Entropy" },
+ { VIRTIO_ID_BALLOON, "Balloon" },
+ { VIRTIO_ID_IOMEMORY, "IOMemory" },
+ { VIRTIO_ID_SCSI, "SCSI" },
+ { VIRTIO_ID_9P, "9P Transport" },
+
+ { 0, NULL }
+};
+
+/* Device independent features. */
+static struct virtio_feature_desc virtio_common_feature_desc[] = {
+ { VIRTIO_F_NOTIFY_ON_EMPTY, "NotifyOnEmpty" },
+ { VIRTIO_RING_F_INDIRECT_DESC, "RingIndirect" },
+ { VIRTIO_RING_F_EVENT_IDX, "EventIdx" },
+ { VIRTIO_F_BAD_FEATURE, "BadFeature" },
+
+ { 0, NULL }
+};
+
+const char *
+virtio_device_name(uint16_t devid)
+{
+ struct virtio_ident *ident;
+
+ for (ident = virtio_ident_table; ident->name != NULL; ident++) {
+ if (ident->devid == devid)
+ return (ident->name);
+ }
+
+ return (NULL);
+}
+
+static const char *
+virtio_feature_name(uint64_t val, struct virtio_feature_desc *desc)
+{
+ int i, j;
+ struct virtio_feature_desc *descs[2] = { desc,
+ virtio_common_feature_desc };
+
+ for (i = 0; i < 2; i++) {
+ if (descs[i] == NULL)
+ continue;
+
+ for (j = 0; descs[i][j].vfd_val != 0; j++) {
+ if (val == descs[i][j].vfd_val)
+ return (descs[i][j].vfd_str);
+ }
+ }
+
+ return (NULL);
+}
+#endif
+
+void
+virtio_describe(device_t dev, const char *msg,
+ uint64_t features, struct virtio_feature_desc *desc)
+{
+#ifdef NOTUSED
+ struct sbuf sb;
+ uint64_t val;
+ char *buf;
+ const char *name;
+ int n;
+
+ if ((buf = malloc(512, M_TEMP, M_NOWAIT)) == NULL) {
+ device_printf(dev, "%s features: %#jx\n", msg, (uintmax_t)
features);
+ return;
+ }
+
+ sbuf_new(&sb, buf, 512, SBUF_FIXEDLEN);
+ sbuf_printf(&sb, "%s features: %#jx", msg, (uintmax_t) features);
+
+ for (n = 0, val = 1ULL << 63; val != 0; val >>= 1) {
+ /*
+ * BAD_FEATURE is used to detect broken Linux clients
+ * and therefore is not applicable to FreeBSD.
+ */
+ if (((features & val) == 0) || val == VIRTIO_F_BAD_FEATURE)
+ continue;
+
+ if (n++ == 0)
+ sbuf_cat(&sb, " <");
+ else
+ sbuf_cat(&sb, ",");
+
+ name = virtio_feature_name(val, desc);
+ if (name == NULL)
+ sbuf_printf(&sb, "%#jx", (uintmax_t) val);
+ else
+ sbuf_cat(&sb, name);
+ }
+
+ if (n > 0)
+ sbuf_cat(&sb, ">");
+
+#if __FreeBSD_version < 900020
+ sbuf_finish(&sb);
+ if (sbuf_overflowed(&sb) == 0)
+#else
+ if (sbuf_finish(&sb) == 0)
+#endif
+ device_printf(dev, "%s\n", sbuf_data(&sb));
+
+ sbuf_delete(&sb);
+ free(buf, M_TEMP);
+#endif
+}
+
+/*
+ * VirtIO bus method wrappers.
+ */
+
+#ifdef NOTUSED
+void
+virtio_read_ivar(device_t dev, int ivar, uintptr_t *val)
+{
+
+ *val = -1;
+ BUS_READ_IVAR(device_get_parent(dev), dev, ivar, val);
+}
+
+void
+virtio_write_ivar(device_t dev, int ivar, uintptr_t val)
+{
+
+ BUS_WRITE_IVAR(device_get_parent(dev), dev, ivar, val);
+}
+#endif
+
+uint64_t
+virtio_negotiate_features(device_t dev, uint64_t child_features)
+{
+
+ return (VIRTIO_BUS_NEGOTIATE_FEATURES(device_get_parent(dev),
+ child_features));
+}
+
+int
+virtio_alloc_virtqueues(device_t dev, int flags, int nvqs,
+ struct vq_alloc_info *info)
+{
+
+ return (VIRTIO_BUS_ALLOC_VIRTQUEUES(device_get_parent(dev), flags,
+ nvqs, info));
+}
+
+int
+virtio_setup_intr(device_t dev, enum intr_type type)
+{
+
+ return (VIRTIO_BUS_SETUP_INTR(device_get_parent(dev), type));
+}
+
+int
+virtio_with_feature(device_t dev, uint64_t feature)
+{
+
+ return (VIRTIO_BUS_WITH_FEATURE(device_get_parent(dev), feature));
+}
+
+void
+virtio_stop(device_t dev)
+{
+
+ VIRTIO_BUS_STOP(device_get_parent(dev));
+}
+
+int
+virtio_reinit(device_t dev, uint64_t features)
+{
+
+ return (VIRTIO_BUS_REINIT(device_get_parent(dev), features));
+}
+
+void
+virtio_reinit_complete(device_t dev)
+{
+
+ VIRTIO_BUS_REINIT_COMPLETE(device_get_parent(dev));
+}
+
+void
+virtio_read_device_config(device_t dev, bus_size_t offset, void *dst, int
len)
+{
+
+ VIRTIO_BUS_READ_DEVICE_CONFIG(device_get_parent(dev),
+ offset, dst, len);
+}
+
+void
+virtio_write_device_config(device_t dev, bus_size_t offset, void *dst, int
len)
+{
+
+ VIRTIO_BUS_WRITE_DEVICE_CONFIG(device_get_parent(dev),
+ offset, dst, len);
+}
+
+#ifdef NOTUSED
+static int
+virtio_modevent(module_t mod, int type, void *unused)
+{
+ int error;
+
+ switch (type) {
+ case MOD_LOAD:
+ case MOD_QUIESCE:
+ case MOD_UNLOAD:
+ case MOD_SHUTDOWN:
+ error = 0;
+ break;
+ default:
+ error = EOPNOTSUPP;
+ break;
+ }
+
+ return (error);
+}
+
+static moduledata_t virtio_mod = {
+ "virtio",
+ virtio_modevent,
+ 0
+};
+
+DECLARE_MODULE(virtio, virtio_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
+MODULE_VERSION(virtio, 1);
+#endif
diff --git a/c/src/lib/libbsp/i386/pc386/virtio/virtio.h
b/c/src/lib/libbsp/i386/pc386/virtio/virtio.h
new file mode 100644
index 0000000..294cac4
--- /dev/null
+++ b/c/src/lib/libbsp/i386/pc386/virtio/virtio.h
@@ -0,0 +1,255 @@
+/**
+ * @file virtio.h
+ * @brief Header for virtio.c
+ */
+
+/*
+ * Authors: Jin-Hyun Kim <jinhyun at konkuk.ac.kr>,
+ * and Hyun-Wook Jin <jinh at konkuk.ac.kr>, http://sslab.konkuk.ac.kr
+ * Ported from FreeBSD to RTEMS March 16
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.NET
+ */
+
+/*-
+ * This header is BSD licensed so anyone can use the definitions to
implement
+ * compatible drivers/servers.
+ *
+ * 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.
+ * 3. Neither the name of IBM nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 IBM 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: release/10.0.0/sys/dev/virtio/virtio.h 252708 2013-07-04
17:59:09Z bryanv $
+ */
+
+#ifndef _VIRTIO_H_
+#define _VIRTIO_H_
+
+#ifdef RTEMS_VIRTIO_NET
+/* Some adaptation replacements for RTEMS */
+
+#define mtx_init(a,b,c,d)
+#define mtx_initialized(a) (1)
+#define mtx_destroy(a) do {} while(0)
+#define mtx_lock(a) do {} while(0)
+#define mtx_unlock(a) do {} while(0)
+#define mtx_assert(a,b) do {} while(0)
+#define howmany(x,y) (((x)+((y)-1))/(y))
+
+typedef uint32_t u_long;
+typedef int device_t;
+typedef uint32_t bus_size_t;
+typedef uint32_t vm_paddr_t;
+
+struct mtx { int dummy; };
+
+enum intr_type {
+ INTR_TYPE_NET = 4,
+ INTR_MPSAFE = 512
+};
+
+#define CACHE_LINE_SHIFT 6
+#define CACHE_LINE_SIZE (1<<CACHE_LINE_SHIFT)
+#define M_ZERO 0
+#define MTX_DEF 0
+#define MIN(a,b) ((a)>(b)?(b):(a))
+
+#define device_get_parent(dev) (dev)
+#define device_printf(dev,format,args...) printk(format,## args)
+#define vtophys(m) (m)
+#define snprintf(buf, size, str,args...) sprintf(buf, str,## args)
+
+#define mb() asm volatile ( "lock; addl $0,0(%%esp) " ::: "memory" )
+#define rmb() mb()
+#define wmb() asm volatile ( "lock; addl $0, (%%esp)" ::: "memory", "cc" )
+
+#define VIRTIO_BUS_NEGOTIATE_FEATURES(dev, feature)
vtpci_negotiate_features(dev, feature)
+#define VIRTIO_BUS_ALLOC_VIRTQUEUES(dev, flags, nvqs, info)
vtpci_alloc_virtqueues(dev, flags, nvqs, info)
+#define VIRTIO_BUS_SETUP_INTR(dev, type) vtpci_setup_intr(dev, type)
+#define VIRTIO_BUS_WITH_FEATURE(dev, feature) vtpci_with_feature(dev,
feature)
+#define VIRTIO_BUS_STOP(dev) vtpci_stop(dev)
+#define VIRTIO_BUS_REINIT(dev, features) vtpci_reinit(dev, features)
+#define VIRTIO_BUS_REINIT_COMPLETE(dev) vtpci_reinit_complete(dev)
+#define VIRTIO_BUS_READ_DEVICE_CONFIG(dev,offset,dst,len)
vtpci_read_dev_config(dev,offset,dst,len)
+#define VIRTIO_BUS_WRITE_DEVICE_CONFIG(dev,offset,dst,len)
vtpci_write_dev_config(dev,offset,dst,len)
+#define VIRTIO_BUS_NOTIFY_VQ(dev, index) vtpci_notify_virtqueue(dev, index)
+
+#define KASSERT(exp,msg) do {\
+ if(!(exp)){\
+ printk msg;\
+ printk("\n");\
+ }\
+} while(0)
+#define MPASS(exp) \
+ KASSERT((exp), ("Assertion %s failed at %s:%d", #exp, __FILE__,
__LINE__))
+#endif /* RTEMS_VIRTIO_NET */
+
+struct vq_alloc_info;
+
+/* VirtIO device IDs. */
+#define VIRTIO_ID_NETWORK 0x01
+#define VIRTIO_ID_BLOCK 0x02
+#define VIRTIO_ID_CONSOLE 0x03
+#define VIRTIO_ID_ENTROPY 0x04
+#define VIRTIO_ID_BALLOON 0x05
+#define VIRTIO_ID_IOMEMORY 0x06
+#define VIRTIO_ID_SCSI 0x08
+#define VIRTIO_ID_9P 0x09
+
+/* Status byte for guest to report progress. */
+#define VIRTIO_CONFIG_STATUS_RESET 0x00
+#define VIRTIO_CONFIG_STATUS_ACK 0x01
+#define VIRTIO_CONFIG_STATUS_DRIVER 0x02
+#define VIRTIO_CONFIG_STATUS_DRIVER_OK 0x04
+#define VIRTIO_CONFIG_STATUS_FAILED 0x80
+
+/*
+ * Generate interrupt when the virtqueue ring is
+ * completely used, even if we've suppressed them.
+ */
+#define VIRTIO_F_NOTIFY_ON_EMPTY (1 << 24)
+
+/*
+ * The guest should never negotiate this feature; it
+ * is used to detect faulty drivers.
+ */
+#define VIRTIO_F_BAD_FEATURE (1 << 30)
+
+/*
+ * Some VirtIO feature bits (currently bits 28 through 31) are
+ * reserved for the transport being used (eg. virtio_ring), the
+ * rest are per-device feature bits.
+ */
+#define VIRTIO_TRANSPORT_F_START 28
+#define VIRTIO_TRANSPORT_F_END 32
+
+/*
+ * Each virtqueue indirect descriptor list must be physically contiguous.
+ * To allow us to malloc(9) each list individually, limit the number
+ * supported to what will fit in one page. With 4KB pages, this is a limit
+ * of 256 descriptors. If there is ever a need for more, we can switch to
+ * contigmalloc(9) for the larger allocations, similar to what
+ * bus_dmamem_alloc(9) does.
+ *
+ * Note the sizeof(struct vring_desc) is 16 bytes.
+ */
+#define VIRTIO_MAX_INDIRECT ((int) (PAGE_SIZE / 16))
+
+/*
+ * VirtIO instance variables indices.
+ */
+#define VIRTIO_IVAR_DEVTYPE 1
+#define VIRTIO_IVAR_FEATURE_DESC 2
+#define VIRTIO_IVAR_VENDOR 3
+#define VIRTIO_IVAR_DEVICE 4
+#define VIRTIO_IVAR_SUBVENDOR 5
+#define VIRTIO_IVAR_SUBDEVICE 6
+
+struct virtio_feature_desc {
+ uint64_t vfd_val;
+ const char *vfd_str;
+};
+
+const char *virtio_device_name(uint16_t devid);
+void virtio_describe(device_t dev, const char *msg,
+ uint64_t features, struct virtio_feature_desc *feature_desc);
+
+/*
+ * VirtIO Bus Methods.
+ */
+void virtio_read_ivar(device_t dev, int ivar, uintptr_t *val);
+void virtio_write_ivar(device_t dev, int ivar, uintptr_t val);
+uint64_t virtio_negotiate_features(device_t dev, uint64_t child_features);
+int virtio_alloc_virtqueues(device_t dev, int flags, int nvqs,
+ struct vq_alloc_info *info);
+int virtio_setup_intr(device_t dev, enum intr_type type);
+int virtio_with_feature(device_t dev, uint64_t feature);
+void virtio_stop(device_t dev);
+int virtio_reinit(device_t dev, uint64_t features);
+void virtio_reinit_complete(device_t dev);
+
+/*
+ * Read/write a variable amount from the device specific (ie, network)
+ * configuration region. This region is encoded in the same endian as
+ * the guest.
+ */
+void virtio_read_device_config(device_t dev, bus_size_t offset,
+ void *dst, int length);
+void virtio_write_device_config(device_t dev, bus_size_t offset,
+ void *src, int length);
+
+
+/* Inlined device specific read/write functions for common lengths. */
+#define VIRTIO_RDWR_DEVICE_CONFIG(size, type) \
+static inline type \
+__CONCAT(virtio_read_dev_config_,size)(device_t dev, \
+ bus_size_t offset) \
+{ \
+ type val; \
+ virtio_read_device_config(dev, offset, &val, sizeof(type)); \
+ return (val); \
+} \
+ \
+static inline void \
+__CONCAT(virtio_write_dev_config_,size)(device_t dev, \
+ bus_size_t offset, type val) \
+{ \
+ virtio_write_device_config(dev, offset, &val, sizeof(type)); \
+}
+
+VIRTIO_RDWR_DEVICE_CONFIG(1, uint8_t);
+VIRTIO_RDWR_DEVICE_CONFIG(2, uint16_t);
+VIRTIO_RDWR_DEVICE_CONFIG(4, uint32_t);
+
+#undef VIRTIO_RDWR_DEVICE_CONFIG
+
+#define VIRTIO_READ_IVAR(name, ivar) \
+static inline int \
+__CONCAT(virtio_get_,name)(device_t dev) \
+{ \
+ uintptr_t val; \
+ virtio_read_ivar(dev, ivar, &val); \
+ return ((int) val); \
+}
+
+VIRTIO_READ_IVAR(device_type, VIRTIO_IVAR_DEVTYPE);
+VIRTIO_READ_IVAR(vendor, VIRTIO_IVAR_VENDOR);
+VIRTIO_READ_IVAR(device, VIRTIO_IVAR_DEVICE);
+VIRTIO_READ_IVAR(subvendor, VIRTIO_IVAR_SUBVENDOR);
+VIRTIO_READ_IVAR(subdevice, VIRTIO_IVAR_SUBDEVICE);
+
+#undef VIRTIO_READ_IVAR
+
+#define VIRTIO_WRITE_IVAR(name, ivar) \
+static inline void \
+__CONCAT(virtio_set_,name)(device_t dev, void *val) \
+{ \
+ virtio_write_ivar(dev, ivar, (uintptr_t) val); \
+}
+
+VIRTIO_WRITE_IVAR(feature_desc, VIRTIO_IVAR_FEATURE_DESC);
+
+#undef VIRTIO_WRITE_IVAR
+
+#endif /* _VIRTIO_H_ */
diff --git a/c/src/lib/libbsp/i386/pc386/virtio/virtio_net.h
b/c/src/lib/libbsp/i386/pc386/virtio/virtio_net.h
new file mode 100644
index 0000000..f6604cd
--- /dev/null
+++ b/c/src/lib/libbsp/i386/pc386/virtio/virtio_net.h
@@ -0,0 +1,218 @@
+/**
+ * @file virtio_net.h
+ * @brief Header for if_vtnet.c
+ */
+
+/*
+ * Authors: Jin-Hyun Kim <jinhyun at konkuk.ac.kr>,
+ * and Hyun-Wook Jin <jinh at konkuk.ac.kr>, http://sslab.konkuk.ac.kr
+ * Ported from FreeBSD to RTEMS March 16
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.NET
+ */
+
+/*-
+ * This header is BSD licensed so anyone can use the definitions to
implement
+ * compatible drivers/servers.
+ *
+ * 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.
+ * 3. Neither the name of IBM nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 IBM 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: release/10.0.0/sys/dev/virtio/network/virtio_net.h 255111
2013-09-01 04:23:54Z bryanv $
+ */
+
+#ifndef _VIRTIO_NET_H
+#define _VIRTIO_NET_H
+
+/* The feature bitmap for virtio net */
+#define VIRTIO_NET_F_CSUM 0x00001 /* Host handles pkts w/ partial csum
*/
+#define VIRTIO_NET_F_GUEST_CSUM 0x00002 /* Guest handles pkts w/ partial
csum*/
+#define VIRTIO_NET_F_MAC 0x00020 /* Host has given MAC address. */
+#define VIRTIO_NET_F_GSO 0x00040 /* Host handles pkts w/ any GSO type
*/
+#define VIRTIO_NET_F_GUEST_TSO4 0x00080 /* Guest can handle TSOv4
in. */
+#define VIRTIO_NET_F_GUEST_TSO6 0x00100 /* Guest can handle TSOv6
in. */
+#define VIRTIO_NET_F_GUEST_ECN 0x00200 /* Guest can handle TSO[6] w/ ECN
in.*/
+#define VIRTIO_NET_F_GUEST_UFO 0x00400 /* Guest can handle UFO in. */
+#define VIRTIO_NET_F_HOST_TSO4 0x00800 /* Host can handle TSOv4 in. */
+#define VIRTIO_NET_F_HOST_TSO6 0x01000 /* Host can handle TSOv6 in. */
+#define VIRTIO_NET_F_HOST_ECN 0x02000 /* Host can handle TSO[6] w/ ECN in.
*/
+#define VIRTIO_NET_F_HOST_UFO 0x04000 /* Host can handle UFO in. */
+#define VIRTIO_NET_F_MRG_RXBUF 0x08000 /* Host can merge receive buffers.
*/
+#define VIRTIO_NET_F_STATUS 0x10000 /* virtio_net_config.status
available*/
+#define VIRTIO_NET_F_CTRL_VQ 0x20000 /* Control channel available */
+#define VIRTIO_NET_F_CTRL_RX 0x40000 /* Control channel RX mode support
*/
+#define VIRTIO_NET_F_CTRL_VLAN 0x80000 /* Control channel VLAN filtering */
+#define VIRTIO_NET_F_CTRL_RX_EXTRA 0x100000 /* Extra RX mode control
support */
+#define VIRTIO_NET_F_GUEST_ANNOUNCE 0x200000 /* Announce device on network
*/
+#define VIRTIO_NET_F_MQ 0x400000 /* Device supports RFS */
+#define VIRTIO_NET_F_CTRL_MAC_ADDR 0x800000 /* Set MAC address */
+
+#define VIRTIO_NET_S_LINK_UP 1 /* Link is up */
+
+struct virtio_net_config {
+ /* The config defining mac address (if VIRTIO_NET_F_MAC) */
+ uint8_t mac[ETHER_ADDR_LEN];
+ /* See VIRTIO_NET_F_STATUS and VIRTIO_NET_S_* above */
+ uint16_t status;
+ /* Maximum number of each of transmit and receive queues;
+ * see VIRTIO_NET_F_MQ and VIRTIO_NET_CTRL_MQ.
+ * Legal values are between 1 and 0x8000.
+ */
+ uint16_t max_virtqueue_pairs;
+} __packed;
+
+/*
+ * This is the first element of the scatter-gather list. If you don't
+ * specify GSO or CSUM features, you can simply ignore the header.
+ */
+#define VIRTIO_NET_HDR_F_NEEDS_CSUM 1 /* Use
csum_start,csum_offset*/
+#define VIRTIO_NET_HDR_F_DATA_VALID 2 /* Csum is valid */
+#define VIRTIO_NET_HDR_GSO_NONE 0 /* Not a GSO frame
*/
+#define VIRTIO_NET_HDR_GSO_TCPV4 1 /* GSO frame, IPv4 TCP (TSO)
*/
+#define VIRTIO_NET_HDR_GSO_UDP 3 /* GSO frame, IPv4 UDP (UFO)
*/
+#define VIRTIO_NET_HDR_GSO_TCPV6 4 /* GSO frame, IPv6 TCP */
+#define VIRTIO_NET_HDR_GSO_ECN 0x80 /* TCP has ECN set */q
+
+struct virtio_net_hdr {
+ uint8_t flags;
+ uint8_t gso_type;
+ uint16_t hdr_len; /* Ethernet + IP + tcp/udp hdrs */
+ uint16_t gso_size; /* Bytes to append to hdr_len per frame */
+ uint16_t csum_start; /* Position to start checksumming from */
+ uint16_t csum_offset; /* Offset after that to place checksum */
+};
+
+/*
+ * This is the version of the header to use when the MRG_RXBUF
+ * feature has been negotiated.
+ */
+struct virtio_net_hdr_mrg_rxbuf {
+ struct virtio_net_hdr hdr;
+ uint16_t num_buffers; /* Number of merged rx buffers */
+};
+
+/*
+ * Control virtqueue data structures
+ *
+ * The control virtqueue expects a header in the first sg entry
+ * and an ack/status response in the last entry. Data for the
+ * command goes in between.
+ */
+struct virtio_net_ctrl_hdr {
+ uint8_t class;
+ uint8_t cmd;
+} __packed;
+
+#define VIRTIO_NET_OK 0
+#define VIRTIO_NET_ERR 1
+
+/*
+ * Control the RX mode, ie. promiscuous, allmulti, etc...
+ * All commands require an "out" sg entry containing a 1 byte
+ * state value, zero = disable, non-zero = enable. Commands
+ * 0 and 1 are supported with the VIRTIO_NET_F_CTRL_RX feature.
+ * Commands 2-5 are added with VIRTIO_NET_F_CTRL_RX_EXTRA.
+ */
+#define VIRTIO_NET_CTRL_RX 0
+#define VIRTIO_NET_CTRL_RX_PROMISC 0
+#define VIRTIO_NET_CTRL_RX_ALLMULTI 1
+#define VIRTIO_NET_CTRL_RX_ALLUNI 2
+#define VIRTIO_NET_CTRL_RX_NOMULTI 3
+#define VIRTIO_NET_CTRL_RX_NOUNI 4
+#define VIRTIO_NET_CTRL_RX_NOBCAST 5
+
+/*
+ * Control the MAC filter table.
+ *
+ * The MAC filter table is managed by the hypervisor, the guest should
+ * assume the size is infinite. Filtering should be considered
+ * non-perfect, ie. based on hypervisor resources, the guest may
+ * received packets from sources not specified in the filter list.
+ *
+ * In addition to the class/cmd header, the TABLE_SET command requires
+ * two out scatterlists. Each contains a 4 byte count of entries followed
+ * by a concatenated byte stream of the ETH_ALEN MAC addresses. The
+ * first sg list contains unicast addresses, the second is for multicast.
+ * This functionality is present if the VIRTIO_NET_F_CTRL_RX feature
+ * is available.
+ *
+ * The ADDR_SET command requests one out scatterlist, it contains a
+ * 6 bytes MAC address. This functionality is present if the
+ * VIRTIO_NET_F_CTRL_MAC_ADDR feature is available.
+ */
+struct virtio_net_ctrl_mac {
+ uint32_t entries;
+ uint8_t macs[][ETHER_ADDR_LEN];
+} __packed;
+
+#define VIRTIO_NET_CTRL_MAC 1
+#define VIRTIO_NET_CTRL_MAC_TABLE_SET 0
+#define VIRTIO_NET_CTRL_MAC_ADDR_SET 1
+
+/*
+ * Control VLAN filtering
+ *
+ * The VLAN filter table is controlled via a simple ADD/DEL interface.
+ * VLAN IDs not added may be filtered by the hypervisor. Del is the
+ * opposite of add. Both commands expect an out entry containing a 2
+ * byte VLAN ID. VLAN filtering is available with the
+ * VIRTIO_NET_F_CTRL_VLAN feature bit.
+ */
+#define VIRTIO_NET_CTRL_VLAN 2
+#define VIRTIO_NET_CTRL_VLAN_ADD 0
+#define VIRTIO_NET_CTRL_VLAN_DEL 1
+
+/*
+ * Control link announce acknowledgement
+ *
+ * The command VIRTIO_NET_CTRL_ANNOUNCE_ACK is used to indicate that
+ * driver has recevied the notification; device would clear the
+ * VIRTIO_NET_S_ANNOUNCE bit in the status field after it receives
+ * this command.
+ */
+#define VIRTIO_NET_CTRL_ANNOUNCE 3
+#define VIRTIO_NET_CTRL_ANNOUNCE_ACK 0
+
+/*
+ * Control Receive Flow Steering
+ *
+ * The command VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET enables Receive Flow
+ * Steering, specifying the number of the transmit and receive queues
+ * that will be used. After the command is consumed and acked by the
+ * device, the device will not steer new packets on receive virtqueues
+ * other than specified nor read from transmit virtqueues other than
+ * specified. Accordingly, driver should not transmit new packets on
+ * virtqueues other than specified.
+ */
+struct virtio_net_ctrl_mq {
+ uint16_t virtqueue_pairs;
+} __packed;
+
+#define VIRTIO_NET_CTRL_MQ 4
+#define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET 0
+#define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN 1
+#define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX 0x8000
+
+#endif /* _VIRTIO_NET_H */
diff --git a/c/src/lib/libbsp/i386/pc386/virtio/virtio_pci.c
b/c/src/lib/libbsp/i386/pc386/virtio/virtio_pci.c
new file mode 100644
index 0000000..c9b7709
--- /dev/null
+++ b/c/src/lib/libbsp/i386/pc386/virtio/virtio_pci.c
@@ -0,0 +1,1526 @@
+/**
+ * @file virtio_pci.c
+ * @brief Driver for the virtio PCI interface
+ */
+
+/*
+ * Authors: Jin-Hyun Kim <jinhyun at konkuk.ac.kr>,
+ * and Hyun-Wook Jin <jinh at konkuk.ac.kr>, http://sslab.konkuk.ac.kr
+ * Ported from FreeBSD to RTEMS March 16
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.NET
+ */
+
+/*-
+ * Copyright (c) 2011, Bryan Venteicher <bryanv at FreeBSD.org>
+ * All rights reserved.
+ *
+ * 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 unmodified, 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 ``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 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: release/10.0.0/sys/dev/virtio/pci/virtio_pci.c 255110
2013-09-01 04:20:23Z bryanv $
+ */
+
+#define VTNET_LEGACY_TX
+#define RTEMS_VIRTIO_NET
+
+/* Driver for the VirtIO PCI interface. */
+
+#ifdef __i386__
+
+#include <rtems.h>
+#include <rtems/rtems_bsdnet.h>
+
+#include <bsp.h>
+#include <bsp/irq.h>
+
+#include <sys/mbuf.h>
+#include <sys/param.h>
+
+#include <pcibios.h>
+
+#include "virtio.h"
+#include "virtqueue.h"
+#include "virtio_pci.h"
+
+#ifdef RTEMS_VIRTIO_NET
+/* Some adaptation replacements for RTEMS */
+
+static struct vtpci_softc vtpci_softc;
+#define device_get_softc(dev) &vtpci_softc
+
+static uint8_t vtpci_read_config_1(struct vtpci_softc *sc, int offset){
+ uint8_t val;
+ inport_byte(sc->pci_io_base+offset, val);
+ return val;
+}
+static uint16_t vtpci_read_config_2(struct vtpci_softc *sc, int offset){
+ uint16_t val;
+ inport_word(sc->pci_io_base+offset, val);
+ return val;
+}
+static uint32_t vtpci_read_config_4(struct vtpci_softc *sc, int offset){
+ uint32_t val;
+ inport_long(sc->pci_io_base+offset, val);
+ return val;
+}
+
+static void vtpci_write_config_1(struct vtpci_softc *sc, int offset,
uint8_t val){
+ outport_byte(sc->pci_io_base+offset, val);
+}
+static void vtpci_write_config_2(struct vtpci_softc *sc, int offset,
uint16_t val){
+ outport_word(sc->pci_io_base+offset, val);
+}
+static void vtpci_write_config_4(struct vtpci_softc *sc, int offset,
uint32_t val){
+ outport_long(sc->pci_io_base+offset, val);
+}
+#endif
+
+#ifdef NOTUSED
+struct vtpci_interrupt {
+ struct resource *vti_irq;
+ int vti_rid;
+ uint32_t isr_number;
+ void *vti_handler;
+};
+
+struct vtpci_virtqueue {
+ struct virtqueue *vtv_vq;
+ int vtv_no_intr;
+};
+#endif
+
+#define VTPCI_FLAG_NO_MSI 0x0001
+#define VTPCI_FLAG_NO_MSIX 0x0002
+#define VTPCI_FLAG_LEGACY 0x1000
+#define VTPCI_FLAG_MSI 0x2000
+#define VTPCI_FLAG_MSIX 0x4000
+#define VTPCI_FLAG_SHARED_MSIX 0x8000
+#define VTPCI_FLAG_ITYPE_MASK 0xF000
+
+#ifdef NOTUSED
+struct vtpci_softc {
+ device_t vtpci_dev;
+ struct resource *vtpci_res;
+ struct resource *vtpci_msix_res;
+ uint64_t vtpci_features;
+ uint32_t vtpci_flags;
+
+ /* This "bus" will only ever have one child. */
+ device_t vtpci_child_dev;
+ struct virtio_feature_desc *vtpci_child_feat_desc;
+
+ int vtpci_nvqs;
+ struct vtpci_virtqueue *vtpci_vqs;
+
+ /*
+ * Ideally, each virtqueue that the driver provides a callback for
will
+ * receive its own MSIX vector. If there are not sufficient vectors
+ * available, then attempt to have all the VQs share one vector. For
+ * MSIX, the configuration changed notifications must be on their
own
+ * vector.
+ *
+ * If MSIX is not available, we will attempt to have the whole
device
+ * share one MSI vector, and then, finally, one legacy interrupt.
+ */
+ struct vtpci_interrupt vtpci_device_interrupt;
+ struct vtpci_interrupt *vtpci_msix_vq_interrupts;
+ int vtpci_nmsix_resources;
+};
+#endif
+
+#ifdef NOTUSED
+static int vtpci_probe(device_t);
+static int vtpci_detach(device_t);
+static int vtpci_suspend(device_t);
+static int vtpci_resume(device_t);
+static int vtpci_shutdown(device_t);
+static void vtpci_driver_added(device_t, driver_t *);
+static void vtpci_child_detached(device_t, device_t);
+static int vtpci_read_config_ivar(device_t, device_t, int, uintptr_t
*);
+static int vtpci_write_ivar(device_t, device_t, int, uintptr_t);
+
+static uint64_t vtpci_negotiate_features(device_t, uint64_t);
+static int vtpci_with_feature(device_t, uint64_t);
+static int vtpci_alloc_virtqueues(device_t, int, int,
+ struct vq_alloc_info *);
+static int vtpci_setup_intr(device_t, enum intr_type);
+static void vtpci_stop(device_t);
+static int vtpci_reinit(device_t, uint64_t);
+static void vtpci_reinit_complete(device_t);
+static void vtpci_notify_virtqueue(device_t, uint16_t);
+static void vtpci_read_dev_config(device_t, bus_size_t, void *, int);
+static void vtpci_write_dev_config(device_t, bus_size_t, void *, int);
+
+static int vtpci_alloc_msix(struct vtpci_softc *, int);
+static int vtpci_alloc_msi(struct vtpci_softc *);
+static int vtpci_alloc_intr_msix_pervq(struct vtpci_softc *);
+static int vtpci_alloc_intr_msix_shared(struct vtpci_softc *);
+static int vtpci_alloc_intr_msi(struct vtpci_softc *);
+
+static int vtpci_setup_pervq_msix_interrupts(struct vtpci_softc *,
+ enum intr_type);
+static int vtpci_setup_msix_interrupts(struct vtpci_softc *,
+ enum intr_type);
+
+static int vtpci_register_msix_vector(struct vtpci_softc *, int,
+ struct vtpci_interrupt *);
+static int vtpci_set_host_msix_vectors(struct vtpci_softc *);
+
+static void vtpci_release_child_resources(struct vtpci_softc *);
+
+static int vtpci_vq_shared_intr_filter(void *);
+static void vtpci_vq_shared_intr(void *);
+static int vtpci_vq_intr_filter(void *);
+static void vtpci_vq_intr(void *);
+
+#define vtpci_setup_msi_interrupt vtpci_setup_legacy_interrupt
+
+/*
+ * I/O port read/write wrappers.
+ */
+#define vtpci_read_config_1(sc, o) bus_read_config_1((sc)->vtpci_res,
(o))
+#define vtpci_read_config_2(sc, o) bus_read_config_2((sc)->vtpci_res,
(o))
+#define vtpci_read_config_4(sc, o) bus_read_config_4((sc)->vtpci_res,
(o))
+#define vtpci_write_config_1(sc, o, v) bus_write_1((sc)->vtpci_res, (o),
(v))
+#define vtpci_write_config_2(sc, o, v) bus_write_2((sc)->vtpci_res, (o),
(v))
+#define vtpci_write_config_4(sc, o, v) bus_write_4((sc)->vtpci_res, (o),
(v))
+#endif
+
+static int vtpci_attach(device_t);
+
+static uint8_t vtpci_get_status(device_t);
+static void vtpci_set_status(device_t, uint8_t);
+
+static void vtpci_describe_features(struct vtpci_softc *, const char *,
+ uint64_t);
+static void vtpci_probe_and_attach_child(struct vtpci_softc *);
+
+static int vtpci_alloc_intr_legacy(struct vtpci_softc *);
+static int vtpci_alloc_interrupt(struct vtpci_softc *, int, int,
+ struct vtpci_interrupt *);
+static int vtpci_alloc_intr_resources(struct vtpci_softc *);
+
+static int vtpci_setup_legacy_interrupt(struct vtpci_softc *,
+ enum intr_type);
+static int vtpci_setup_interrupts(struct vtpci_softc *, enum
intr_type);
+
+static int vtpci_reinit_virtqueue(struct vtpci_softc *, int);
+
+static void vtpci_free_interrupt(struct vtpci_softc *,
+ struct vtpci_interrupt *);
+static void vtpci_free_interrupts(struct vtpci_softc *);
+static void vtpci_free_virtqueues(struct vtpci_softc *);
+static void vtpci_cleanup_setup_intr_attempt(struct vtpci_softc *);
+static void vtpci_reset(struct vtpci_softc *);
+
+static void vtpci_select_virtqueue(struct vtpci_softc *, int);
+
+static void vtpci_legacy_intr(void *);
+static void vtpci_config_intr(void *);
+
+#ifdef NOTUSED
+/* Tunables. */
+static int vtpci_disable_msix = 0;
+TUNABLE_INT("hw.virtio.pci.disable_msix", &vtpci_disable_msix);
+
+static device_method_t vtpci_methods[] = {
+ /* Device interface. */
+ DEVMETHOD(device_probe, vtpci_probe),
+ DEVMETHOD(device_attach, vtpci_attach),
+ DEVMETHOD(device_detach, vtpci_detach),
+ DEVMETHOD(device_suspend, vtpci_suspend),
+ DEVMETHOD(device_resume, vtpci_resume),
+ DEVMETHOD(device_shutdown, vtpci_shutdown),
+
+ /* Bus interface. */
+ DEVMETHOD(bus_driver_added, vtpci_driver_added),
+ DEVMETHOD(bus_child_detached, vtpci_child_detached),
+ DEVMETHOD(bus_read_config_ivar, vtpci_read_config_ivar),
+ DEVMETHOD(bus_write_ivar, vtpci_write_ivar),
+
+ /* VirtIO bus interface. */
+ DEVMETHOD(virtio_bus_negotiate_features, vtpci_negotiate_features),
+ DEVMETHOD(virtio_bus_with_feature, vtpci_with_feature),
+ DEVMETHOD(virtio_bus_alloc_virtqueues, vtpci_alloc_virtqueues),
+ DEVMETHOD(virtio_bus_setup_intr, vtpci_setup_intr),
+ DEVMETHOD(virtio_bus_stop, vtpci_stop),
+ DEVMETHOD(virtio_bus_reinit, vtpci_reinit),
+ DEVMETHOD(virtio_bus_reinit_complete, vtpci_reinit_complete),
+ DEVMETHOD(virtio_bus_notify_vq, vtpci_notify_virtqueue),
+ DEVMETHOD(virtio_bus_read_device_config, vtpci_read_dev_config),
+ DEVMETHOD(virtio_bus_write_device_config, vtpci_write_dev_config),
+
+ DEVMETHOD_END
+};
+
+static driver_t vtpci_driver = {
+ "virtio_pci",
+ vtpci_methods,
+ sizeof(struct vtpci_softc)
+};
+
+devclass_t vtpci_devclass;
+
+DRIVER_MODULE(virtio_pci, pci, vtpci_driver, vtpci_devclass, 0, 0);
+MODULE_VERSION(virtio_pci, 1);
+MODULE_DEPEND(virtio_pci, pci, 1, 1, 1);
+MODULE_DEPEND(virtio_pci, virtio, 1, 1, 1);
+
+static int
+vtpci_probe(device_t dev)
+{
+ char desc[36];
+ const char *name;
+
+ if (pci_get_vendor(dev) != VIRTIO_PCI_VENDORID)
+ return (ENXIO);
+
+ if (pci_get_device(dev) < VIRTIO_PCI_DEVICEID_MIN ||
+ pci_get_device(dev) > VIRTIO_PCI_DEVICEID_MAX)
+ return (ENXIO);
+
+ if (pci_get_revid(dev) != VIRTIO_PCI_ABI_VERSION)
+ return (ENXIO);
+
+ name = virtio_device_name(pci_get_subdevice(dev));
+ if (name == NULL)
+ name = "Unknown";
+
+ snprintf(desc, sizeof(desc), "VirtIO PCI %s adapter", name);
+ device_set_desc_copy(dev, desc);
+
+ return (BUS_PROBE_DEFAULT);
+}
+#endif
+
+#ifdef RTEMS_VIRTIO_NET
+int rtems_vtpci_attach(
+ struct rtems_bsdnet_ifconfig *config,
+ struct vtpci_softc **xsc
+)
+{
+ struct vtpci_softc *sc;
+ int error, i, ret;
+ uint32_t val32;
+
+ *xsc = &vtpci_softc;
+ sc = &vtpci_softc;
+
+ /* Parse NIC_NAME & Init structures */
+ if ( ( sc->unit_number =
+ rtems_bsdnet_parse_driver_name( config,
&sc->unit_name ) ) < 0 ) {
+ return 0;
+ }
+
+ /* Find device on pci bus */
+ {
+ int pbus, pdev, pfun;
+
+ for ( i = VIRTIO_PCI_DEVICEID_MIN; i < VIRTIO_PCI_DEVICEID_MAX; i++
) {
+ ret = pci_find_device( VIRTIO_PCI_VENDORID, i,
sc->unit_number,
+ &pbus, &pdev, &pfun );
+
+ if ( ret == PCIB_ERR_SUCCESS ) {
+ sc->pci_signature = PCIB_DEVSIG_MAKE( pbus, pdev,
pfun );
+ break;
+ }
+ }
+ }
+
+ /* Get IO Address */
+ pcib_conf_read32( sc->pci_signature, PCI_BASE_ADDRESS_0, &val32 );
+ val32 &= PCI_BASE_ADDRESS_IO_MASK;
+ sc->pci_io_base = val32;
+
+ error = vtpci_attach(1);
+
+ return (error);
+}
+#endif
+
+static int
+vtpci_attach(device_t dev)
+{
+ struct vtpci_softc *sc;
+ device_t child;
+ int rid;
+
+ sc = device_get_softc(dev);
+ sc->vtpci_dev = dev;
+#ifdef NOTUSED
+
+ pci_enable_busmaster(dev);
+
+ rid = PCIR_BAR(0);
+ sc->vtpci_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid,
+ RF_ACTIVE);
+ if (sc->vtpci_res == NULL) {
+ device_printf(dev, "cannot map I/O space\n");
+ return (ENXIO);
+ }
+
+ if (pci_find_cap(dev, PCIY_MSI, NULL) != 0)
+ sc->vtpci_flags |= VTPCI_FLAG_NO_MSI;
+
+ if (pci_find_cap(dev, PCIY_MSIX, NULL) == 0) {
+ rid = PCIR_BAR(1);
+ sc->vtpci_msix_res = bus_alloc_resource_any(dev,
+ SYS_RES_MEMORY, &rid, RF_ACTIVE);
+ }
+
+ if (sc->vtpci_msix_res == NULL)
+ sc->vtpci_flags |= VTPCI_FLAG_NO_MSIX;
+#endif
+#ifdef RTEMS_VIRTIO_NET
+ uint16_t val16;
+
+ pcib_conf_read16( sc->pci_signature, PCI_COMMAND, &val16 );
+ val16 |= PCI_COMMAND_MASTER;
+ pcib_conf_write16( sc->pci_signature, PCI_COMMAND, val16 );
+#endif
+
+ vtpci_reset(sc);
+
+ /* Tell the host we've noticed this device. */
+ vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK);
+
+#ifdef NOTUSED
+ if ((child = device_add_child(dev, NULL, -1)) == NULL) {
+ device_printf(dev, "cannot create child device\n");
+ vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_FAILED);
+ vtpci_detach(dev);
+ return (ENOMEM);
+ }
+
+ sc->vtpci_child_dev = child;
+#endif
+ vtpci_probe_and_attach_child(sc);
+
+ return (0);
+}
+
+#ifdef NOTUSED
+static int
+vtpci_detach(device_t dev)
+{
+ struct vtpci_softc *sc;
+ device_t child;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ if ((child = sc->vtpci_child_dev) != NULL) {
+ error = device_delete_child(dev, child);
+ if (error)
+ return (error);
+ sc->vtpci_child_dev = NULL;
+ }
+
+ vtpci_reset(sc);
+
+ if (sc->vtpci_msix_res != NULL) {
+ bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BAR(1),
+ sc->vtpci_msix_res);
+ sc->vtpci_msix_res = NULL;
+ }
+
+ if (sc->vtpci_res != NULL) {
+ bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(0),
+ sc->vtpci_res);
+ sc->vtpci_res = NULL;
+ }
+
+ return (0);
+}
+
+static int
+vtpci_suspend(device_t dev)
+{
+
+ return (bus_generic_suspend(dev));
+}
+
+static int
+vtpci_resume(device_t dev)
+{
+
+ return (bus_generic_resume(dev));
+}
+
+int
+vtpci_shutdown(device_t dev)
+{
+
+ (void) bus_generic_shutdown(dev);
+ /* Forcibly stop the host device. */
+ vtpci_stop(dev);
+
+ return (0);
+}
+
+static void
+vtpci_driver_added(device_t dev, driver_t *driver)
+{
+ struct vtpci_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ vtpci_probe_and_attach_child(sc);
+}
+
+static void
+vtpci_child_detached(device_t dev, device_t child)
+{
+ struct vtpci_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ vtpci_reset(sc);
+ vtpci_release_child_resources(sc);
+}
+
+static int
+vtpci_read_config_ivar(device_t dev, device_t child, int index, uintptr_t
*result)
+{
+ struct vtpci_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (sc->vtpci_child_dev != child)
+ return (ENOENT);
+
+ switch (index) {
+ case VIRTIO_IVAR_DEVTYPE:
+ case VIRTIO_IVAR_SUBDEVICE:
+ *result = pci_get_subdevice(dev);
+ break;
+ case VIRTIO_IVAR_VENDOR:
+ *result = pci_get_vendor(dev);
+ break;
+ case VIRTIO_IVAR_DEVICE:
+ *result = pci_get_device(dev);
+ break;
+ case VIRTIO_IVAR_SUBVENDOR:
+ *result = pci_get_subdevice(dev);
+ break;
+ default:
+ return (ENOENT);
+ }
+
+ return (0);
+}
+
+static int
+vtpci_write_ivar(device_t dev, device_t child, int index, uintptr_t value)
+{
+ struct vtpci_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (sc->vtpci_child_dev != child)
+ return (ENOENT);
+
+ switch (index) {
+ case VIRTIO_IVAR_FEATURE_DESC:
+ sc->vtpci_child_feat_desc = (void *) value;
+ break;
+ default:
+ return (ENOENT);
+ }
+
+ return (0);
+}
+#endif
+
+uint64_t
+vtpci_negotiate_features(device_t dev, uint64_t child_features)
+{
+ struct vtpci_softc *sc;
+ uint64_t host_features, features;
+
+ sc = device_get_softc(dev);
+
+ host_features = vtpci_read_config_4(sc, VIRTIO_PCI_HOST_FEATURES);
+ vtpci_describe_features(sc, "host", host_features);
+
+ /*
+ * Limit negotiated features to what the driver, virtqueue, and
+ * host all support.
+ */
+ features = host_features & child_features;
+ features = virtqueue_filter_features(features);
+ sc->vtpci_features = features;
+
+ vtpci_describe_features(sc, "negotiated", features);
+ vtpci_write_config_4(sc, VIRTIO_PCI_GUEST_FEATURES, features);
+
+ return (features);
+}
+
+int
+vtpci_with_feature(device_t dev, uint64_t feature)
+{
+ struct vtpci_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ return ((sc->vtpci_features & feature) != 0);
+}
+
+int
+vtpci_alloc_virtqueues(device_t dev, int flags, int nvqs,
+ struct vq_alloc_info *vq_info)
+{
+ struct vtpci_softc *sc;
+ struct virtqueue *vq;
+ struct vtpci_virtqueue *vqx;
+ struct vq_alloc_info *info;
+ int idx, error;
+ uint16_t size;
+
+ sc = device_get_softc(dev);
+
+ if (sc->vtpci_nvqs != 0)
+#ifdef NOTUSED
+ return (EALREADY);
+#endif
+#ifdef RTEMS_VIRTIO_NET
+ return (EINVAL);
+#endif
+ if (nvqs <= 0)
+ return (EINVAL);
+
+ sc->vtpci_vqs = malloc(nvqs * sizeof(struct vtpci_virtqueue),
+ M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (sc->vtpci_vqs == NULL)
+ return (ENOMEM);
+
+ for (idx = 0; idx < nvqs; idx++) {
+ vqx = &sc->vtpci_vqs[idx];
+ info = &vq_info[idx];
+
+ vtpci_select_virtqueue(sc, idx);
+ size = vtpci_read_config_2(sc, VIRTIO_PCI_QUEUE_NUM);
+
+ error = virtqueue_alloc(dev, idx, size,
VIRTIO_PCI_VRING_ALIGN,
+ 0xFFFFFFFFUL, info, &vq);
+ if (error) {
+ device_printf(dev,
+ "cannot allocate virtqueue %d: %d\n", idx,
error);
+ break;
+ }
+
+ vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN,
+ virtqueue_paddr(vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT);
+
+ vqx->vtv_vq = *info->vqai_vq = vq;
+ vqx->vtv_no_intr = info->vqai_intr == NULL;
+
+ sc->vtpci_nvqs++;
+ }
+
+ if (error)
+ vtpci_free_virtqueues(sc);
+
+ return (error);
+}
+
+int
+vtpci_setup_intr(device_t dev, enum intr_type type)
+{
+ struct vtpci_softc *sc;
+ int attempt, error;
+
+ sc = device_get_softc(dev);
+
+ for (attempt = 0; attempt < 5; attempt++) {
+ /*
+ * Start with the most desirable interrupt configuration and
+ * fallback towards less desirable ones.
+ */
+ switch (attempt) {
+#ifdef NOTUSED
+ case 0:
+ error = vtpci_alloc_intr_msix_pervq(sc);
+ break;
+ case 1:
+ error = vtpci_alloc_intr_msix_shared(sc);
+ break;
+ case 2:
+ error = vtpci_alloc_intr_msi(sc);
+ break;
+ case 3:
+ error = vtpci_alloc_intr_legacy(sc);
+ break;
+#endif
+#ifdef RTEMS_VIRTIO_NET
+ case 0:
+ error = vtpci_alloc_intr_legacy(sc);
+ break;
+#endif
+ default:
+ device_printf(dev,
+ "exhausted all interrupt allocation
attempts\n");
+ return (ENXIO);
+ }
+
+ if (error == 0 && vtpci_setup_interrupts(sc, type) == 0)
+ break;
+
+ vtpci_cleanup_setup_intr_attempt(sc);
+ }
+
+#ifdef NOTUSED
+ if (bootverbose) {
+ if (sc->vtpci_flags & VTPCI_FLAG_LEGACY)
+ device_printf(dev, "using legacy interrupt\n");
+ else if (sc->vtpci_flags & VTPCI_FLAG_MSI)
+ device_printf(dev, "using MSI interrupt\n");
+ else if (sc->vtpci_flags & VTPCI_FLAG_SHARED_MSIX)
+ device_printf(dev, "using shared MSIX
interrupts\n");
+ else
+ device_printf(dev, "using per VQ MSIX
interrupts\n");
+ }
+#endif
+
+ return (0);
+}
+
+void
+vtpci_stop(device_t dev)
+{
+
+ vtpci_reset(device_get_softc(dev));
+}
+
+int
+vtpci_reinit(device_t dev, uint64_t features)
+{
+ struct vtpci_softc *sc;
+ int idx, error;
+
+ sc = device_get_softc(dev);
+
+ /*
+ * Redrive the device initialization. This is a bit of an abuse of
+ * the specification, but VirtualBox, QEMU/KVM, and BHyVe seem to
+ * play nice.
+ *
+ * We do not allow the host device to change from what was
originally
+ * negotiated beyond what the guest driver changed. MSIX state
should
+ * not change, number of virtqueues and their size remain the same,
etc.
+ * This will need to be rethought when we want to support migration.
+ */
+
+ if (vtpci_get_status(dev) != VIRTIO_CONFIG_STATUS_RESET)
+ vtpci_stop(dev);
+
+ /*
+ * Quickly drive the status through ACK and DRIVER. The device
+ * does not become usable again until vtpci_reinit_complete().
+ */
+ vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK);
+ vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER);
+
+ vtpci_negotiate_features(dev, features);
+
+ for (idx = 0; idx < sc->vtpci_nvqs; idx++) {
+ error = vtpci_reinit_virtqueue(sc, idx);
+ if (error)
+ return (error);
+ }
+
+#ifdef NOTUSED
+ if (sc->vtpci_flags & VTPCI_FLAG_MSIX) {
+ error = vtpci_set_host_msix_vectors(sc);
+ if (error)
+ return (error);
+ }
+#endif
+
+ return (0);
+}
+
+void
+vtpci_reinit_complete(device_t dev)
+{
+
+ vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER_OK);
+}
+
+void
+vtpci_notify_virtqueue(device_t dev, uint16_t queue)
+{
+ struct vtpci_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_NOTIFY, queue);
+}
+
+static uint8_t
+vtpci_get_status(device_t dev)
+{
+ struct vtpci_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ return (vtpci_read_config_1(sc, VIRTIO_PCI_STATUS));
+}
+
+static void
+vtpci_set_status(device_t dev, uint8_t status)
+{
+ struct vtpci_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (status != VIRTIO_CONFIG_STATUS_RESET)
+ status |= vtpci_get_status(dev);
+
+ vtpci_write_config_1(sc, VIRTIO_PCI_STATUS, status);
+}
+
+void
+vtpci_read_dev_config(device_t dev, bus_size_t offset,
+ void *dst, int length)
+{
+ struct vtpci_softc *sc;
+ bus_size_t off;
+ uint8_t *d;
+ int size;
+
+ sc = device_get_softc(dev);
+ off = VIRTIO_PCI_CONFIG(sc) + offset;
+
+ for (d = dst; length > 0; d += size, off += size, length -= size) {
+ if (length >= 4) {
+ size = 4;
+ *(uint32_t *)d = vtpci_read_config_4(sc, off);
+ } else if (length >= 2) {
+ size = 2;
+ *(uint16_t *)d = vtpci_read_config_2(sc, off);
+ } else {
+ size = 1;
+ *d = vtpci_read_config_1(sc, off);
+ }
+ }
+}
+
+void
+vtpci_write_dev_config(device_t dev, bus_size_t offset,
+ void *src, int length)
+{
+ struct vtpci_softc *sc;
+ bus_size_t off;
+ uint8_t *s;
+ int size;
+
+ sc = device_get_softc(dev);
+ off = VIRTIO_PCI_CONFIG(sc) + offset;
+
+ for (s = src; length > 0; s += size, off += size, length -= size) {
+ if (length >= 4) {
+ size = 4;
+ vtpci_write_config_4(sc, off, *(uint32_t *)s);
+ } else if (length >= 2) {
+ size = 2;
+ vtpci_write_config_2(sc, off, *(uint16_t *)s);
+ } else {
+ size = 1;
+ vtpci_write_config_1(sc, off, *s);
+ }
+ }
+}
+
+static void
+vtpci_describe_features(struct vtpci_softc *sc, const char *msg,
+ uint64_t features)
+{
+ device_t dev, child;
+
+ dev = sc->vtpci_dev;
+#ifdef NOTUSED
+ child = sc->vtpci_child_dev;
+
+ if (device_is_attached(child) && bootverbose == 0)
+ return;
+#endif
+
+ virtio_describe(dev, msg, features, sc->vtpci_child_feat_desc);
+}
+
+static void
+vtpci_probe_and_attach_child(struct vtpci_softc *sc)
+{
+ device_t dev, child;
+
+ dev = sc->vtpci_dev;
+#ifdef NOTUSED
+ child = sc->vtpci_child_dev;
+
+ if (child == NULL)
+ return;
+
+ if (device_get_state(child) != DS_NOTPRESENT)
+ return;
+
+ if (device_probe(child) != 0)
+ return;
+#endif
+ vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER);
+#ifdef RTEMS_VIRTIO_NET
+ vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER_OK);
+#endif
+#ifdef NOTUSED
+ if (device_attach(child) != 0) {
+ vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_FAILED);
+ vtpci_reset(sc);
+ vtpci_release_child_resources(sc);
+ /* Reset status for future attempt. */
+ vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK);
+ } else {
+ vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER_OK);
+ VIRTIO_ATTACH_COMPLETED(child);
+ }
+#endif
+}
+
+#ifdef NOTUSED
+static int
+vtpci_alloc_msix(struct vtpci_softc *sc, int nvectors)
+{
+ device_t dev;
+ int nmsix, cnt, required;
+
+ dev = sc->vtpci_dev;
+
+ /* Allocate an additional vector for the config changes. */
+ required = nvectors + 1;
+
+ nmsix = pci_msix_count(dev);
+ if (nmsix < required)
+ return (1);
+
+ cnt = required;
+ if (pci_alloc_msix(dev, &cnt) == 0 && cnt >= required) {
+ sc->vtpci_nmsix_resources = required;
+ return (0);
+ }
+
+ pci_release_msi(dev);
+
+ return (1);
+}
+
+static int
+vtpci_alloc_msi(struct vtpci_softc *sc)
+{
+ device_t dev;
+ int nmsi, cnt, required;
+
+ dev = sc->vtpci_dev;
+ required = 1;
+
+ nmsi = pci_msi_count(dev);
+ if (nmsi < required)
+ return (1);
+
+ cnt = required;
+ if (pci_alloc_msi(dev, &cnt) == 0 && cnt >= required)
+ return (0);
+
+ pci_release_msi(dev);
+
+ return (1);
+}
+
+static int
+vtpci_alloc_intr_msix_pervq(struct vtpci_softc *sc)
+{
+ int i, nvectors, error;
+
+ if (vtpci_disable_msix != 0 ||
+ sc->vtpci_flags & VTPCI_FLAG_NO_MSIX)
+ return (ENOTSUP);
+
+ for (nvectors = 0, i = 0; i < sc->vtpci_nvqs; i++) {
+ if (sc->vtpci_vqs[i].vtv_no_intr == 0)
+ nvectors++;
+ }
+
+ error = vtpci_alloc_msix(sc, nvectors);
+ if (error)
+ return (error);
+
+ sc->vtpci_flags |= VTPCI_FLAG_MSIX;
+
+ return (0);
+}
+
+static int
+vtpci_alloc_intr_msix_shared(struct vtpci_softc *sc)
+{
+ int error;
+
+ if (vtpci_disable_msix != 0 ||
+ sc->vtpci_flags & VTPCI_FLAG_NO_MSIX)
+ return (ENOTSUP);
+
+ error = vtpci_alloc_msix(sc, 1);
+ if (error)
+ return (error);
+
+ sc->vtpci_flags |= VTPCI_FLAG_MSIX | VTPCI_FLAG_SHARED_MSIX;
+
+ return (0);
+}
+
+static int
+vtpci_alloc_intr_msi(struct vtpci_softc *sc)
+{
+ int error;
+
+ /* Only BHyVe supports MSI. */
+ if (sc->vtpci_flags & VTPCI_FLAG_NO_MSI)
+ return (ENOTSUP);
+
+ error = vtpci_alloc_msi(sc);
+ if (error)
+ return (error);
+
+ sc->vtpci_flags |= VTPCI_FLAG_MSI;
+
+ return (0);
+}
+#endif
+
+static int
+vtpci_alloc_intr_legacy(struct vtpci_softc *sc)
+{
+
+ sc->vtpci_flags |= VTPCI_FLAG_LEGACY;
+
+ return (0);
+}
+
+static int
+vtpci_alloc_interrupt(struct vtpci_softc *sc, int rid, int flags,
+ struct vtpci_interrupt *intr)
+{
+#ifdef NOTUSED
+ struct resource *irq;
+
+ irq = bus_alloc_resource_any(sc->vtpci_dev, SYS_RES_IRQ, &rid,
flags);
+ if (irq == NULL)
+ return (ENXIO);
+
+ intr->vti_irq = irq;
+ intr->vti_rid = rid;
+#endif
+#ifdef RTEMS_VIRTIO_NET
+ uint8_t val8;
+ pcib_conf_read8( sc->pci_signature, PCI_INTERRUPT_LINE, &val8 );
+ intr->isr_number = val8;
+#endif
+
+ return (0);
+}
+
+static int
+vtpci_alloc_intr_resources(struct vtpci_softc *sc)
+{
+ struct vtpci_interrupt *intr;
+ int i, rid, flags, nvq_intrs, error;
+
+ rid = 0;
+#ifdef NOTUSED
+ flags = RF_ACTIVE;
+
+ if (sc->vtpci_flags & VTPCI_FLAG_LEGACY)
+ flags |= RF_SHAREABLE;
+ else
+ rid = 1;
+#endif
+
+ /*
+ * For legacy and MSI interrupts, this single resource handles all
+ * interrupts. For MSIX, this resource is used for the configuration
+ * changed interrupt.
+ */
+ intr = &sc->vtpci_device_interrupt;
+ error = vtpci_alloc_interrupt(sc, rid, flags, intr);
+ if (error || sc->vtpci_flags & (VTPCI_FLAG_LEGACY | VTPCI_FLAG_MSI))
+ return (error);
+
+#ifdef NOTUSED
+ /* Subtract one for the configuration changed interrupt. */
+ nvq_intrs = sc->vtpci_nmsix_resources - 1;
+
+ intr = sc->vtpci_msix_vq_interrupts = malloc(nvq_intrs *
+ sizeof(struct vtpci_interrupt), M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (sc->vtpci_msix_vq_interrupts == NULL)
+ return (ENOMEM);
+
+ for (i = 0, rid++; i < nvq_intrs; i++, rid++, intr++) {
+ error = vtpci_alloc_interrupt(sc, rid, flags, intr);
+ if (error)
+ return (error);
+ }
+#endif
+
+ return (0);
+}
+
+static int
+vtpci_setup_legacy_interrupt(struct vtpci_softc *sc, enum intr_type type)
+{
+ struct vtpci_interrupt *intr;
+ int error;
+
+ intr = &sc->vtpci_device_interrupt;
+#ifdef NOTUSED
+ error = bus_setup_intr(sc->vtpci_dev, intr->vti_irq, type, NULL,
+ vtpci_legacy_intr, sc, &intr->vti_handler);
+#endif
+#ifdef RTEMS_VIRTIO_NET
+ error = rtems_interrupt_handler_install(
+ intr->isr_number,
+ NULL,
+ RTEMS_INTERRUPT_SHARED,
+ (rtems_interrupt_handler) vtpci_legacy_intr,
+ sc);
+ intr->vti_handler = vtpci_legacy_intr;
+#endif
+
+ return (error);
+}
+
+#ifdef NOTUSED
+static int
+vtpci_setup_pervq_msix_interrupts(struct vtpci_softc *sc, enum intr_type
type)
+{
+ struct vtpci_virtqueue *vqx;
+ struct vtpci_interrupt *intr;
+ int i, error;
+
+ intr = sc->vtpci_msix_vq_interrupts;
+
+ for (i = 0; i < sc->vtpci_nvqs; i++) {
+ vqx = &sc->vtpci_vqs[i];
+
+ if (vqx->vtv_no_intr)
+ continue;
+
+ error = bus_setup_intr(sc->vtpci_dev, intr->vti_irq, type,
+ vtpci_vq_intr_filter, vtpci_vq_intr, vqx->vtv_vq,
+ &intr->vti_handler);
+ if (error)
+ return (error);
+
+ intr++;
+ }
+
+ return (0);
+}
+
+static int
+vtpci_setup_msix_interrupts(struct vtpci_softc *sc, enum intr_type type)
+{
+ device_t dev;
+ struct vtpci_interrupt *intr;
+ int error;
+
+ dev = sc->vtpci_dev;
+ intr = &sc->vtpci_device_interrupt;
+
+ error = bus_setup_intr(dev, intr->vti_irq, type, NULL,
+ vtpci_config_intr, sc, &intr->vti_handler);
+ if (error)
+ return (error);
+
+ if (sc->vtpci_flags & VTPCI_FLAG_SHARED_MSIX) {
+ intr = sc->vtpci_msix_vq_interrupts;
+ error = bus_setup_intr(dev, intr->vti_irq, type,
+ vtpci_vq_shared_intr_filter, vtpci_vq_shared_intr, sc,
+ &intr->vti_handler);
+ } else
+ error = vtpci_setup_pervq_msix_interrupts(sc, type);
+
+ return (error ? error : vtpci_set_host_msix_vectors(sc));
+}
+#endif
+
+static int
+vtpci_setup_interrupts(struct vtpci_softc *sc, enum intr_type type)
+{
+ int error;
+
+ type |= INTR_MPSAFE;
+ KASSERT(sc->vtpci_flags & VTPCI_FLAG_ITYPE_MASK,
+ ("%s: no interrupt type selected %#x", __func__,
sc->vtpci_flags));
+
+ error = vtpci_alloc_intr_resources(sc);
+ if (error)
+ return (error);
+
+ if (sc->vtpci_flags & VTPCI_FLAG_LEGACY)
+ error = vtpci_setup_legacy_interrupt(sc, type);
+#ifdef NOTUSED
+ else if (sc->vtpci_flags & VTPCI_FLAG_MSI)
+ error = vtpci_setup_msi_interrupt(sc, type);
+ else
+ error = vtpci_setup_msix_interrupts(sc, type);
+#endif
+
+ return (error);
+}
+
+#ifdef NOTUSED
+static int
+vtpci_register_msix_vector(struct vtpci_softc *sc, int offset,
+ struct vtpci_interrupt *intr)
+{
+ device_t dev;
+ uint16_t vector;
+
+ dev = sc->vtpci_dev;
+
+ if (intr != NULL) {
+ /* Map from guest rid to host vector. */
+ vector = intr->vti_rid - 1;
+ } else
+ vector = VIRTIO_MSI_NO_VECTOR;
+
+ vtpci_write_config_2(sc, offset, vector);
+
+ /* Read vector to determine if the host had sufficient resources. */
+ if (vtpci_read_config_2(sc, offset) != vector) {
+ device_printf(dev,
+ "insufficient host resources for MSIX interrupts\n");
+ return (ENODEV);
+ }
+
+ return (0);
+}
+
+static int
+vtpci_set_host_msix_vectors(struct vtpci_softc *sc)
+{
+ struct vtpci_interrupt *intr, *tintr;
+ int idx, offset, error;
+
+ intr = &sc->vtpci_device_interrupt;
+ offset = VIRTIO_MSI_CONFIG_VECTOR;
+
+ error = vtpci_register_msix_vector(sc, offset, intr);
+ if (error)
+ return (error);
+
+ intr = sc->vtpci_msix_vq_interrupts;
+ offset = VIRTIO_MSI_QUEUE_VECTOR;
+
+ for (idx = 0; idx < sc->vtpci_nvqs; idx++) {
+ vtpci_select_virtqueue(sc, idx);
+
+ if (sc->vtpci_vqs[idx].vtv_no_intr)
+ tintr = NULL;
+ else
+ tintr = intr;
+
+ error = vtpci_register_msix_vector(sc, offset, tintr);
+ if (error)
+ break;
+
+ /*
+ * For shared MSIX, all the virtqueues share the first
+ * interrupt.
+ */
+ if ((sc->vtpci_flags & VTPCI_FLAG_SHARED_MSIX) == 0)
+ intr++;
+ }
+
+ return (error);
+}
+#endif
+
+static int
+vtpci_reinit_virtqueue(struct vtpci_softc *sc, int idx)
+{
+ struct vtpci_virtqueue *vqx;
+ struct virtqueue *vq;
+ int error;
+ uint16_t size;
+
+ vqx = &sc->vtpci_vqs[idx];
+ vq = vqx->vtv_vq;
+
+ KASSERT(vq != NULL, ("%s: vq %d not allocated", __func__, idx));
+
+ vtpci_select_virtqueue(sc, idx);
+ size = vtpci_read_config_2(sc, VIRTIO_PCI_QUEUE_NUM);
+
+ error = virtqueue_reinit(vq, size);
+ if (error)
+ return (error);
+
+ vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN,
+ virtqueue_paddr(vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT);
+
+ return (0);
+}
+
+static void
+vtpci_free_interrupt(struct vtpci_softc *sc, struct vtpci_interrupt *intr)
+{
+ device_t dev;
+
+ dev = sc->vtpci_dev;
+
+#ifdef NOTUSED
+ if (intr->vti_handler != NULL) {
+ bus_teardown_intr(dev, intr->vti_irq, intr->vti_handler);
+ intr->vti_handler = NULL;
+ }
+
+ if (intr->vti_irq != NULL) {
+ bus_release_resource(dev, SYS_RES_IRQ, intr->vti_rid,
+ intr->vti_irq);
+ intr->vti_irq = NULL;
+ intr->vti_rid = -1;
+ }
+#endif
+#ifdef RTEMS_VIRTIO_NET
+ if( intr->isr_number != 0 ){
+ rtems_interrupt_handler_remove(
+ intr->isr_number,
+ (rtems_interrupt_handler) vtpci_legacy_intr,
+ NULL);
+ intr->vti_handler = NULL;
+ }
+#endif
+}
+
+static void
+vtpci_free_interrupts(struct vtpci_softc *sc)
+{
+ struct vtpci_interrupt *intr;
+ int i, nvq_intrs;
+
+ vtpci_free_interrupt(sc, &sc->vtpci_device_interrupt);
+
+ if (sc->vtpci_nmsix_resources != 0) {
+ nvq_intrs = sc->vtpci_nmsix_resources - 1;
+ sc->vtpci_nmsix_resources = 0;
+
+ intr = sc->vtpci_msix_vq_interrupts;
+ if (intr != NULL) {
+ for (i = 0; i < nvq_intrs; i++, intr++)
+ vtpci_free_interrupt(sc, intr);
+
+ free(sc->vtpci_msix_vq_interrupts, M_DEVBUF);
+ sc->vtpci_msix_vq_interrupts = NULL;
+ }
+ }
+
+#ifdef NOTUSED
+ if (sc->vtpci_flags & (VTPCI_FLAG_MSI | VTPCI_FLAG_MSIX))
+ pci_release_msi(sc->vtpci_dev);
+#endif
+
+ sc->vtpci_flags &= ~VTPCI_FLAG_ITYPE_MASK;
+}
+
+static void
+vtpci_free_virtqueues(struct vtpci_softc *sc)
+{
+ struct vtpci_virtqueue *vqx;
+ int idx;
+
+ for (idx = 0; idx < sc->vtpci_nvqs; idx++) {
+ vqx = &sc->vtpci_vqs[idx];
+
+ vtpci_select_virtqueue(sc, idx);
+ vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN, 0);
+
+ virtqueue_free(vqx->vtv_vq);
+ vqx->vtv_vq = NULL;
+ }
+
+ free(sc->vtpci_vqs, M_DEVBUF);
+ sc->vtpci_vqs = NULL;
+ sc->vtpci_nvqs = 0;
+}
+
+#ifdef NOTUSED
+static void
+vtpci_release_child_resources(struct vtpci_softc *sc)
+{
+
+ vtpci_free_interrupts(sc);
+ vtpci_free_virtqueues(sc);
+}
+#endif
+
+static void
+vtpci_cleanup_setup_intr_attempt(struct vtpci_softc *sc)
+{
+ int idx;
+
+ if (sc->vtpci_flags & VTPCI_FLAG_MSIX) {
+ vtpci_write_config_2(sc, VIRTIO_MSI_CONFIG_VECTOR,
+ VIRTIO_MSI_NO_VECTOR);
+
+ for (idx = 0; idx < sc->vtpci_nvqs; idx++) {
+ vtpci_select_virtqueue(sc, idx);
+ vtpci_write_config_2(sc, VIRTIO_MSI_QUEUE_VECTOR,
+ VIRTIO_MSI_NO_VECTOR);
+ }
+ }
+
+ vtpci_free_interrupts(sc);
+}
+
+static void
+vtpci_reset(struct vtpci_softc *sc)
+{
+
+ /*
+ * Setting the status to RESET sets the host device to
+ * the original, uninitialized state.
+ */
+ vtpci_set_status(sc->vtpci_dev, VIRTIO_CONFIG_STATUS_RESET);
+}
+
+static void
+vtpci_select_virtqueue(struct vtpci_softc *sc, int idx)
+{
+
+ vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_SEL, idx);
+}
+
+static void
+vtpci_legacy_intr(void *xsc)
+{
+ struct vtpci_softc *sc;
+ struct vtpci_virtqueue *vqx;
+ int i;
+ uint8_t isr;
+
+ sc = xsc;
+ vqx = &sc->vtpci_vqs[0];
+
+ /* Reading the ISR also clears it. */
+ isr = vtpci_read_config_1(sc, VIRTIO_PCI_ISR);
+
+ if (isr & VIRTIO_PCI_ISR_CONFIG)
+ vtpci_config_intr(sc);
+
+ if (isr & VIRTIO_PCI_ISR_INTR) {
+#ifdef NOTUSED
+ for (i = 0; i < sc->vtpci_nvqs; i++, vqx++) {
+ if (vqx->vtv_no_intr == 0)
+ virtqueue_intr(vqx->vtv_vq);
+ }
+#endif
+#ifdef RTEMS_VIRTIO_NET
+ rtems_bsdnet_event_send( sc->daemonTid, RTEMS_EVENT_1 );
+#endif
+ }
+}
+
+#ifdef NOTUSED
+static int
+vtpci_vq_shared_intr_filter(void *xsc)
+{
+ struct vtpci_softc *sc;
+ struct vtpci_virtqueue *vqx;
+ int i, rc;
+
+ rc = 0;
+ sc = xsc;
+ vqx = &sc->vtpci_vqs[0];
+
+ for (i = 0; i < sc->vtpci_nvqs; i++, vqx++) {
+ if (vqx->vtv_no_intr == 0)
+ rc |= virtqueue_intr_filter(vqx->vtv_vq);
+ }
+
+ return (rc ? FILTER_SCHEDULE_THREAD : FILTER_STRAY);
+}
+
+static void
+vtpci_vq_shared_intr(void *xsc)
+{
+ struct vtpci_softc *sc;
+ struct vtpci_virtqueue *vqx;
+ int i;
+
+ sc = xsc;
+ vqx = &sc->vtpci_vqs[0];
+
+ for (i = 0; i < sc->vtpci_nvqs; i++, vqx++) {
+ if (vqx->vtv_no_intr == 0)
+ virtqueue_intr(vqx->vtv_vq);
+ }
+}
+
+static int
+vtpci_vq_intr_filter(void *xvq)
+{
+ struct virtqueue *vq;
+ int rc;
+
+ vq = xvq;
+ rc = virtqueue_intr_filter(vq);
+
+ return (rc ? FILTER_SCHEDULE_THREAD : FILTER_STRAY);
+}
+static void
+vtpci_vq_intr(void *xvq)
+{
+ struct virtqueue *vq;
+
+ vq = xvq;
+ virtqueue_intr(vq);
+}
+#endif
+
+
+static void
+vtpci_config_intr(void *xsc)
+{
+ struct vtpci_softc *sc;
+ device_t child;
+
+ sc = xsc;
+ child = sc->vtpci_child_dev;
+
+#ifdef NOTUSED
+ if (child != NULL)
+ VIRTIO_CONFIG_CHANGE(child);
+#endif
+}
+
+#endif /* __i386__ */
diff --git a/c/src/lib/libbsp/i386/pc386/virtio/virtio_pci.h
b/c/src/lib/libbsp/i386/pc386/virtio/virtio_pci.h
new file mode 100644
index 0000000..c34708e
--- /dev/null
+++ b/c/src/lib/libbsp/i386/pc386/virtio/virtio_pci.h
@@ -0,0 +1,166 @@
+/**
+ * @file virtio_pci.h
+ * @brief Header for virtio_pci.c
+ */
+
+/*
+ * Authors: Jin-Hyun Kim <jinhyun at konkuk.ac.kr>,
+ * and Hyun-Wook Jin <jinh at konkuk.ac.kr>, http://sslab.konkuk.ac.kr
+ * Ported from FreeBSD to RTEMS March 16
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.NET
+ */
+
+/*-
+ * Copyright IBM Corp. 2007
+ *
+ * Authors:
+ * Anthony Liguori <aliguori at us.ibm.com>
+ *
+ * This header is BSD licensed so anyone can use the definitions to
implement
+ * compatible drivers/servers.
+ *
+ * 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.
+ * 3. Neither the name of IBM nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 IBM 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 `
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: release/10.0.0/sys/dev/virtio/pci/virtio_pci.h 238360
2012-07-11 02:57:19Z grehan $
+ */
+
+#ifndef _VIRTIO_PCI_H
+#define _VIRTIO_PCI_H
+
+#ifdef RTEMS_VIRTIO_NET
+struct vtpci_interrupt {
+ uint32_t isr_number;
+ void *vti_handler;
+};
+
+struct vtpci_virtqueue {
+ struct virtqueue *vtv_vq;
+ int vtv_no_intr;
+};
+
+struct vtpci_softc {
+ device_t vtpci_dev;
+#ifdef NOTUSED
+ struct resource *vtpci_res;
+ struct resource *vtpci_msix_res;
+#endif
+#ifdef RTEMS_VIRTIO_NET
+ int unit_number;
+ char *unit_name;
+
+ int pci_signature;
+ uint32_t pci_io_base;
+ rtems_id daemonTid;
+#endif
+ uint64_t vtpci_features;
+ uint32_t vtpci_flags;
+
+ /* This "bus" will only ever have one child. */
+ device_t vtpci_child_dev;
+ struct virtio_feature_desc *vtpci_child_feat_desc;
+
+ int vtpci_nvqs;
+ struct vtpci_virtqueue *vtpci_vqs;
+
+ /*
+ * Ideally, each virtqueue that the driver provides a callback for
will
+ * receive its own MSIX vector. If there are not sufficient vectors
+ * available, then attempt to have all the VQs share one vector. For
+ * MSIX, the configuration changed notifications must be on their
own
+ * vector.
+ *
+ * If MSIX is not available, we will attempt to have the whole
device
+ * share one MSI vector, and then, finally, one legacy interrupt.
+ */
+ struct vtpci_interrupt vtpci_device_interrupt;
+ struct vtpci_interrupt *vtpci_msix_vq_interrupts;
+ int vtpci_nmsix_resources;
+};
+
+int rtems_vtpci_attach(struct rtems_bsdnet_ifconfig *config, struct
vtpci_softc **xsc);
+uint64_t vtpci_negotiate_features(device_t, uint64_t);
+int vtpci_with_feature(device_t, uint64_t);
+int vtpci_alloc_virtqueues(device_t, int, int,
+ struct vq_alloc_info *);
+int vtpci_setup_intr(device_t, enum intr_type);
+void vtpci_stop(device_t);
+int vtpci_reinit(device_t, uint64_t);
+void vtpci_reinit_complete(device_t);
+void vtpci_notify_virtqueue(device_t, uint16_t);
+void vtpci_read_dev_config(device_t, bus_size_t, void *, int);
+void vtpci_write_dev_config(device_t, bus_size_t, void *, int);
+#endif /* RTEMS_VIRTIO_NET */
+
+/* VirtIO PCI vendor/device ID. */
+#define VIRTIO_PCI_VENDORID 0x1AF4
+#define VIRTIO_PCI_DEVICEID_MIN 0x1000
+#define VIRTIO_PCI_DEVICEID_MAX 0x103F
+
+/* VirtIO ABI version, this must match exactly. */
+#define VIRTIO_PCI_ABI_VERSION 0
+
+/*
+ * VirtIO Header, located in BAR 0.
+ */
+#define VIRTIO_PCI_HOST_FEATURES 0 /* host's supported features (32bit,
RO)*/
+#define VIRTIO_PCI_GUEST_FEATURES 4 /* guest's supported features (32, RW)
*/
+#define VIRTIO_PCI_QUEUE_PFN 8 /* physical address of VQ (32, RW) */
+#define VIRTIO_PCI_QUEUE_NUM 12 /* number of ring entries (16, RO) */
+#define VIRTIO_PCI_QUEUE_SEL 14 /* current VQ selection (16, RW) */
+#define VIRTIO_PCI_QUEUE_NOTIFY 16 /* notify host regarding VQ
(16, RW) */
+#define VIRTIO_PCI_STATUS 18 /* device status register (8, RW) */
+#define VIRTIO_PCI_ISR 19 /* interrupt status register, reading
+ * also clears the register (8, RO) */
+/* Only if MSIX is enabled: */
+#define VIRTIO_MSI_CONFIG_VECTOR 20 /* configuration change vector (16,
RW) */
+#define VIRTIO_MSI_QUEUE_VECTOR 22 /* vector for selected VQ
notifications
+ (16, RW) */
+
+/* The bit of the ISR which indicates a device has an interrupt. */
+#define VIRTIO_PCI_ISR_INTR 0x1
+/* The bit of the ISR which indicates a device configuration change. */
+#define VIRTIO_PCI_ISR_CONFIG 0x2
+/* Vector value used to disable MSI for queue. */
+#define VIRTIO_MSI_NO_VECTOR 0xFFFF
+
+/*
+ * The remaining space is defined by each driver as the per-driver
+ * configuration space.
+ */
+#define VIRTIO_PCI_CONFIG(sc) \
+ (((sc)->vtpci_flags & VTPCI_FLAG_MSIX) ? 24 : 20)
+
+/*
+ * How many bits to shift physical queue address written to QUEUE_PFN.
+ * 12 is historical, and due to x86 page size.
+ */
+#define VIRTIO_PCI_QUEUE_ADDR_SHIFT 12
+
+/* The alignment to use between consumer and producer parts of vring. */
+#define VIRTIO_PCI_VRING_ALIGN 4096
+
+#endif /* _VIRTIO_PCI_H */
diff --git a/c/src/lib/libbsp/i386/pc386/virtio/virtio_ring.h
b/c/src/lib/libbsp/i386/pc386/virtio/virtio_ring.h
new file mode 100644
index 0000000..3e780c5
--- /dev/null
+++ b/c/src/lib/libbsp/i386/pc386/virtio/virtio_ring.h
@@ -0,0 +1,180 @@
+/**
+ * @file virtio_ring.h
+ * @brief Header for virtio.c
+ */
+
+/*
+ * Authors: Jin-Hyun Kim <jinhyun at konkuk.ac.kr>,
+ * and Hyun-Wook Jin <jinh at konkuk.ac.kr>, http://sslab.konkuk.ac.kr
+ * Ported from FreeBSD to RTEMS March 16
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.NET
+ */
+
+/*-
+ * Copyright Rusty Russell IBM Corporation 2007.
+ *
+ * This header is BSD licensed so anyone can use the definitions to
implement
+ * compatible drivers/servers.
+ *
+ * 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.
+ * 3. Neither the name of IBM nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 IBM 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: release/10.0.0/sys/dev/virtio/virtio_ring.h 238360 2012-07-11
02:57:19Z grehan $
+ */
+
+#ifndef VIRTIO_RING_H
+#define VIRTIO_RING_H
+
+/* This marks a buffer as continuing via the next field. */
+#define VRING_DESC_F_NEXT 1
+/* This marks a buffer as write-only (otherwise read-only). */
+#define VRING_DESC_F_WRITE 2
+/* This means the buffer contains a list of buffer descriptors. */
+#define VRING_DESC_F_INDIRECT 4
+
+/* The Host uses this in used->flags to advise the Guest: don't kick me
+ * when you add a buffer. It's unreliable, so it's simply an
+ * optimization. Guest will still kick if it's out of buffers. */
+#define VRING_USED_F_NO_NOTIFY 1
+/* The Guest uses this in avail->flags to advise the Host: don't
+ * interrupt me when you consume a buffer. It's unreliable, so it's
+ * simply an optimization. */
+#define VRING_AVAIL_F_NO_INTERRUPT 1
+
+/* VirtIO ring descriptors: 16 bytes.
+ * These can chain together via "next". */
+struct vring_desc {
+ /* Address (guest-physical). */
+ uint64_t addr;
+ /* Length. */
+ uint32_t len;
+ /* The flags as indicated above. */
+ uint16_t flags;
+ /* We chain unused descriptors via this, too. */
+ uint16_t next;
+};
+
+struct vring_avail {
+ uint16_t flags;
+ uint16_t idx;
+ uint16_t ring[0];
+};
+
+/* uint32_t is used here for ids for padding reasons. */
+struct vring_used_elem {
+ /* Index of start of used descriptor chain. */
+ uint32_t id;
+ /* Total length of the descriptor chain which was written to. */
+ uint32_t len;
+};
+
+struct vring_used {
+ uint16_t flags;
+ uint16_t idx;
+ struct vring_used_elem ring[0];
+};
+
+struct vring {
+ unsigned int num;
+
+ struct vring_desc *desc;
+ struct vring_avail *avail;
+ struct vring_used *used;
+};
+
+/* The standard layout for the ring is a continuous chunk of memory which
+ * looks like this. We assume num is a power of 2.
+ *
+ * struct vring {
+ * // The actual descriptors (16 bytes each)
+ * struct vring_desc desc[num];
+ *
+ * // A ring of available descriptor heads with free-running index.
+ * __u16 avail_flags;
+ * __u16 avail_idx;
+ * __u16 available[num];
+ * __u16 used_event_idx;
+ *
+ * // Padding to the next align boundary.
+ * char pad[];
+ *
+ * // A ring of used descriptor heads with free-running index.
+ * __u16 used_flags;
+ * __u16 used_idx;
+ * struct vring_used_elem used[num];
+ * __u16 avail_event_idx;
+ * };
+ *
+ * NOTE: for VirtIO PCI, align is 4096.
+ */
+
+/*
+ * We publish the used event index at the end of the available ring, and
vice
+ * versa. They are at the end for backwards compatibility.
+ */
+#define vring_used_event(vr) ((vr)->avail->ring[(vr)->num])
+#define vring_avail_event(vr) (*(uint16_t *)&(vr)->used->ring[(vr)->num])
+
+static inline int
+vring_size(unsigned int num, unsigned long align)
+{
+ int size;
+
+ size = num * sizeof(struct vring_desc);
+ size += sizeof(struct vring_avail) + (num * sizeof(uint16_t)) +
+ sizeof(uint16_t);
+ size = (size + align - 1) & ~(align - 1);
+ size += sizeof(struct vring_used) +
+ (num * sizeof(struct vring_used_elem)) + sizeof(uint16_t);
+ return (size);
+}
+
+static inline void
+vring_init(struct vring *vr, unsigned int num, uint8_t *p,
+ unsigned long align)
+{
+ vr->num = num;
+ vr->desc = (struct vring_desc *) p;
+ vr->avail = (struct vring_avail *) (p +
+ num * sizeof(struct vring_desc));
+ vr->used = (void *)
+ (((unsigned long) &vr->avail->ring[num] + align-1) &
~(align-1));
+}
+
+/*
+ * The following is used with VIRTIO_RING_F_EVENT_IDX.
+ *
+ * Assuming a given event_idx value from the other size, if we have
+ * just incremented index from old to new_idx, should we trigger an
+ * event?
+ */
+static inline int
+vring_need_event(uint16_t event_idx, uint16_t new_idx, uint16_t old)
+{
+
+ return (uint16_t)(new_idx - event_idx - 1) < (uint16_t)(new_idx -
old);
+}
+#endif /* VIRTIO_RING_H */
diff --git a/c/src/lib/libbsp/i386/pc386/virtio/virtqueue.c
b/c/src/lib/libbsp/i386/pc386/virtio/virtqueue.c
new file mode 100644
index 0000000..1d07b76
--- /dev/null
+++ b/c/src/lib/libbsp/i386/pc386/virtio/virtqueue.c
@@ -0,0 +1,963 @@
+/**
+ * @file virqueue.c
+ * @brief Implements the virtqueue interface as basically described
+ * in the original VirtIO paper.
+ */
+
+/*
+ * Authors: Jin-Hyun Kim <jinhyun at konkuk.ac.kr>,
+ * and Hyun-Wook Jin <jinh at konkuk.ac.kr>, http://sslab.konkuk.ac.kr
+ * Ported from FreeBSD to RTEMS March 16
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.NET
+ */
+
+/*-
+ * Copyright (c) 2011, Bryan Venteicher <bryanv at FreeBSD.org>
+ * All rights reserved.
+ *
+ * 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 unmodified, 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 ``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 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: release/10.0.0/sys/dev/virtio/virtqueue.c 255166 2013-09-03
02:26:57Z bryanv $
+ */
+
+#define VTNET_LEGACY_TX
+#define RTEMS_VIRTIO_NET
+
+#include <rtems.h>
+#include <rtems/rtems_bsdnet.h>
+
+#include <bsp.h>
+
+#include <sys/mbuf.h>
+#include <sys/param.h>
+
+#include "virtio.h"
+#include "virtqueue.h"
+#include "virtio_ring.h"
+#include "virtio_pci.h"
+
+#define RTEMS_VIRTIO_NET
+#ifdef RTEMS_VIRTIO_NET
+static uint16_t rtems_vq_ring_enqueue_segments(struct virtqueue *vq,
+ struct vring_desc *desc, uint16_t head_idx, struct mbuf *m_head,
+ int readable, int writable);
+#endif
+
+#define VIRTQUEUE_FLAG_INDIRECT 0x0001
+#define VIRTQUEUE_FLAG_EVENT_IDX 0x0002
+
+struct vq_desc_extra {
+ void *cookie;
+ struct vring_desc *indirect;
+ vm_paddr_t indirect_paddr;
+ uint16_t ndescs;
+};
+
+
+struct virtqueue {
+ device_t vq_dev;
+ char vq_name[VIRTQUEUE_MAX_NAME_SZ];
+ uint16_t vq_queue_index;
+ uint16_t vq_nentries;
+ uint32_t vq_flags;
+
+ int vq_alignment;
+ int vq_ring_size;
+ void *vq_ring_mem;
+ void *vq_ring_mem_orig;
+ int vq_max_indirect_size;
+ int vq_indirect_mem_size;
+ virtqueue_intr_t *vq_intrhand;
+ void *vq_intrhand_arg;
+
+ struct vring vq_ring;
+ uint16_t vq_free_cnt;
+ uint16_t vq_queued_cnt;
+ /*
+ * Head of the free chain in the descriptor table. If
+ * there are no free descriptors, this will be set to
+ * VQ_RING_DESC_CHAIN_END.
+ */
+ uint16_t vq_desc_head_idx;
+ /*
+ * Last consumed descriptor in the used table,
+ * trails vq_ring.used->idx.
+ */
+ uint16_t vq_used_cons_idx;
+
+ struct vq_desc_extra vq_descx[10];
+};
+
+/*
+ * The maximum virtqueue size is 2^15. Use that value as the end of
+ * descriptor chain terminator since it will never be a valid index
+ * in the descriptor table. This is used to verify we are correctly
+ * handling vq_free_cnt.
+ */
+#define VQ_RING_DESC_CHAIN_END 32768
+
+#define VQASSERT(_vq, _exp, _msg, ...) \
+ KASSERT((_exp),("%s: %s - "_msg, __func__, (_vq)->vq_name, \
+ ##__VA_ARGS__))
+
+#define VQ_RING_ASSERT_VALID_IDX(_vq, _idx) \
+ VQASSERT((_vq), (_idx) < (_vq)->vq_nentries, \
+ "invalid ring index: %d, max: %d", (_idx), \
+ (_vq)->vq_nentries)
+
+#define VQ_RING_ASSERT_CHAIN_TERM(_vq) \
+ VQASSERT((_vq), (_vq)->vq_desc_head_idx == \
+ VQ_RING_DESC_CHAIN_END, "full ring terminated " \
+ "incorrectly: head idx: %d", (_vq)->vq_desc_head_idx)
+
+#ifdef NOTUSED
+static int virtqueue_init_indirect(struct virtqueue *vq, int);
+static void virtqueue_free_indirect(struct virtqueue *vq);
+static void virtqueue_init_indirect_list(struct virtqueue *,
+ struct vring_desc *);
+
+static uint16_t vq_ring_enqueue_segments(struct virtqueue *,
+ struct vring_desc *, uint16_t, struct sglist *, int,
int);
+static int vq_ring_use_indirect(struct virtqueue *, int);
+static void vq_ring_enqueue_indirect(struct virtqueue *, void *,
+#endif
+
+static void vq_ring_init(struct virtqueue *);
+static void vq_ring_update_avail(struct virtqueue *, uint16_t);
+static int vq_ring_enable_interrupt(struct virtqueue *, uint16_t);
+static int vq_ring_must_notify_host(struct virtqueue *);
+static void vq_ring_notify_host(struct virtqueue *);
+static void vq_ring_free_chain(struct virtqueue *, uint16_t);
+
+uint64_t
+virtqueue_filter_features(uint64_t features)
+{
+ uint64_t mask;
+
+ mask = (1 << VIRTIO_TRANSPORT_F_START) - 1;
+ mask |= VIRTIO_RING_F_INDIRECT_DESC;
+ mask |= VIRTIO_RING_F_EVENT_IDX;
+
+ return (features & mask);
+}
+
+int
+virtqueue_alloc(device_t dev, uint16_t queue, uint16_t size, int align,
+ vm_paddr_t highaddr, struct vq_alloc_info *info, struct virtqueue
**vqp)
+{
+ struct virtqueue *vq;
+ int error;
+
+ *vqp = NULL;
+ error = 0;
+
+ if (size == 0) {
+ device_printf(dev,
+ "virtqueue %d (%s) does not exist (size is zero)\n",
+ queue, info->vqai_name);
+ return (ENODEV);
+ } else if (!powerof2(size)) {
+ device_printf(dev,
+ "virtqueue %d (%s) size is not a power of 2: %d\n",
+ queue, info->vqai_name, size);
+ return (ENXIO);
+ } else if (info->vqai_maxindirsz > VIRTIO_MAX_INDIRECT) {
+ device_printf(dev, "virtqueue %d (%s) requested too many "
+ "indirect descriptors: %d, max %d\n",
+ queue, info->vqai_name, info->vqai_maxindirsz,
+ VIRTIO_MAX_INDIRECT);
+ return (EINVAL);
+ }
+
+ vq = malloc(sizeof(struct virtqueue) +
+ size * sizeof(struct vq_desc_extra), M_DEVBUF, M_NOWAIT |
M_ZERO);
+ if (vq == NULL) {
+ device_printf(dev, "cannot allocate virtqueue\n");
+ return (ENOMEM);
+ }
+
+ vq->vq_dev = dev;
+ strlcpy(vq->vq_name, info->vqai_name, sizeof(vq->vq_name));
+ vq->vq_queue_index = queue;
+ vq->vq_alignment = align;
+ vq->vq_nentries = size;
+ vq->vq_free_cnt = size;
+ vq->vq_intrhand = info->vqai_intr;
+ vq->vq_intrhand_arg = info->vqai_intr_arg;
+
+ if (VIRTIO_BUS_WITH_FEATURE(dev, VIRTIO_RING_F_EVENT_IDX) != 0)
+ vq->vq_flags |= VIRTQUEUE_FLAG_EVENT_IDX;
+
+#ifdef NOTUSED
+ if (info->vqai_maxindirsz > 1) {
+ error = virtqueue_init_indirect(vq, info->vqai_maxindirsz);
+ if (error)
+ goto fail;
+ }
+
+ vq->vq_ring_size = round_page(vring_size(size, align));
+ vq->vq_ring_mem = contigmalloc(vq->vq_ring_size, M_DEVBUF,
+ M_NOWAIT | M_ZERO, 0, highaddr, PAGE_SIZE, 0);
+#endif
+#ifdef RTEMS_VIRTIO_NET
+ vq->vq_ring_size = vring_size(size, align);
+ vq->vq_ring_mem = malloc(vq->vq_ring_size+align, M_DEVBUF,
M_NOWAIT);
+ vq->vq_ring_mem_orig = vq->vq_ring_mem;
+ if ( ( (unsigned long) vq->vq_ring_mem % align ) > 0 ) {
+ vq->vq_ring_mem =
+ (void *) ( (unsigned long) vq->vq_ring_mem +
+ ( align - ( (unsigned long) vq->vq_ring_mem % align ) ) );
+ }
+#endif
+ if (vq->vq_ring_mem == NULL) {
+ device_printf(dev,
+ "cannot allocate memory for virtqueue ring\n");
+ error = ENOMEM;
+ goto fail;
+ }
+
+ vq_ring_init(vq);
+ virtqueue_disable_intr(vq);
+
+ *vqp = vq;
+
+fail:
+ if (error)
+ virtqueue_free(vq);
+
+ return (error);
+}
+
+#ifdef NOTUSED
+static int
+virtqueue_init_indirect(struct virtqueue *vq, int indirect_size)
+{
+ device_t dev;
+ struct vq_desc_extra *dxp;
+ int i, size;
+
+ dev = vq->vq_dev;
+
+ if (VIRTIO_BUS_WITH_FEATURE(dev, VIRTIO_RING_F_INDIRECT_DESC) == 0)
{
+ /*
+ * Indirect descriptors requested by the driver but not
+ * negotiated. Return zero to keep the initialization
+ * going: we'll run fine without.
+ */
+ if (bootverbose)
+ device_printf(dev, "virtqueue %d (%s) requested "
+ "indirect descriptors but not negotiated\n",
+ vq->vq_queue_index, vq->vq_name);
+ return (0);
+ }
+
+ size = indirect_size * sizeof(struct vring_desc);
+ vq->vq_max_indirect_size = indirect_size;
+ vq->vq_indirect_mem_size = size;
+ vq->vq_flags |= VIRTQUEUE_FLAG_INDIRECT;
+
+ for (i = 0; i < vq->vq_nentries; i++) {
+ dxp = &vq->vq_descx[i];
+
+ dxp->indirect = malloc(size, M_DEVBUF, M_NOWAIT);
+ if (dxp->indirect == NULL) {
+ device_printf(dev, "cannot allocate indirect
list\n");
+ return (ENOMEM);
+ }
+
+ dxp->indirect_paddr = vtophys(dxp->indirect);
+ virtqueue_init_indirect_list(vq, dxp->indirect);
+ }
+
+ return (0);
+}
+
+static void
+virtqueue_free_indirect(struct virtqueue *vq)
+{
+ struct vq_desc_extra *dxp;
+ int i;
+
+ for (i = 0; i < vq->vq_nentries; i++) {
+ dxp = &vq->vq_descx[i];
+
+ if (dxp->indirect == NULL)
+ break;
+
+ free(dxp->indirect, M_DEVBUF);
+ dxp->indirect = NULL;
+ dxp->indirect_paddr = 0;
+ }
+
+ vq->vq_flags &= ~VIRTQUEUE_FLAG_INDIRECT;
+ vq->vq_indirect_mem_size = 0;
+}
+
+static void
+virtqueue_init_indirect_list(struct virtqueue *vq,
+ struct vring_desc *indirect)
+{
+ int i;
+
+ bzero(indirect, vq->vq_indirect_mem_size);
+
+ for (i = 0; i < vq->vq_max_indirect_size - 1; i++)
+ indirect[i].next = i + 1;
+ indirect[i].next = VQ_RING_DESC_CHAIN_END;
+}
+#endif
+
+int
+virtqueue_reinit(struct virtqueue *vq, uint16_t size)
+{
+ struct vq_desc_extra *dxp;
+ int i;
+
+ if (vq->vq_nentries != size) {
+ device_printf(vq->vq_dev,
+ "%s: '%s' changed size; old=%hu, new=%hu\n",
+ __func__, vq->vq_name, vq->vq_nentries, size);
+ return (EINVAL);
+ }
+
+ /* Warn if the virtqueue was not properly cleaned up. */
+ if (vq->vq_free_cnt != vq->vq_nentries) {
+ device_printf(vq->vq_dev,
+ "%s: warning '%s' virtqueue not empty, "
+ "leaking %d entries\n", __func__, vq->vq_name,
+ vq->vq_nentries - vq->vq_free_cnt);
+ }
+
+ vq->vq_desc_head_idx = 0;
+ vq->vq_used_cons_idx = 0;
+ vq->vq_queued_cnt = 0;
+ vq->vq_free_cnt = vq->vq_nentries;
+
+ /* To be safe, reset all our allocated memory. */
+ bzero(vq->vq_ring_mem, vq->vq_ring_size);
+ for (i = 0; i < vq->vq_nentries; i++) {
+ dxp = &vq->vq_descx[i];
+ dxp->cookie = NULL;
+ dxp->ndescs = 0;
+#ifdef NOTUSED
+ if (vq->vq_flags & VIRTQUEUE_FLAG_INDIRECT)
+ virtqueue_init_indirect_list(vq, dxp->indirect);
+#endif
+ }
+
+ vq_ring_init(vq);
+ virtqueue_disable_intr(vq);
+
+ return (0);
+}
+
+void
+virtqueue_free(struct virtqueue *vq)
+{
+
+ if (vq->vq_free_cnt != vq->vq_nentries) {
+ device_printf(vq->vq_dev, "%s: freeing non-empty virtqueue,
"
+ "leaking %d entries\n", vq->vq_name,
+ vq->vq_nentries - vq->vq_free_cnt);
+ }
+
+#ifdef NOTUSED
+ if (vq->vq_flags & VIRTQUEUE_FLAG_INDIRECT)
+ virtqueue_free_indirect(vq);
+#endif
+
+ if (vq->vq_ring_mem != NULL) {
+#ifdef NOTUSED
+ contigfree(vq->vq_ring_mem, vq->vq_ring_size, M_DEVBUF);
+#endif
+#ifdef RTEMS_VIRTIO_NET
+ free(vq->vq_ring_mem_orig, M_DEVBUF);
+#endif
+ vq->vq_ring_size = 0;
+ vq->vq_ring_mem = NULL;
+ }
+
+ free(vq, M_DEVBUF);
+}
+
+vm_paddr_t
+virtqueue_paddr(struct virtqueue *vq)
+{
+
+ return (vtophys(vq->vq_ring_mem));
+}
+
+int
+virtqueue_size(struct virtqueue *vq)
+{
+
+ return (vq->vq_nentries);
+}
+
+int
+virtqueue_empty(struct virtqueue *vq)
+{
+
+ return (vq->vq_nentries == vq->vq_free_cnt);
+}
+
+int
+virtqueue_full(struct virtqueue *vq)
+{
+
+ return (vq->vq_free_cnt == 0);
+}
+
+void
+virtqueue_notify(struct virtqueue *vq)
+{
+
+ /* Ensure updated avail->idx is visible to host. */
+ mb();
+
+ if (vq_ring_must_notify_host(vq))
+ vq_ring_notify_host(vq);
+ vq->vq_queued_cnt = 0;
+}
+
+int
+virtqueue_nused(struct virtqueue *vq)
+{
+ uint16_t used_idx, nused;
+
+ used_idx = vq->vq_ring.used->idx;
+
+ nused = (uint16_t)(used_idx - vq->vq_used_cons_idx);
+ VQASSERT(vq, nused <= vq->vq_nentries, "used more than available");
+
+ return (nused);
+}
+
+int
+virtqueue_intr_filter(struct virtqueue *vq)
+{
+
+ if (vq->vq_used_cons_idx == vq->vq_ring.used->idx)
+ return (0);
+
+ virtqueue_disable_intr(vq);
+
+ return (1);
+}
+
+void
+virtqueue_intr(struct virtqueue *vq)
+{
+
+ vq->vq_intrhand(vq->vq_intrhand_arg);
+}
+
+int
+virtqueue_enable_intr(struct virtqueue *vq)
+{
+
+ return (vq_ring_enable_interrupt(vq, 0));
+}
+
+int
+virtqueue_postpone_intr(struct virtqueue *vq, vq_postpone_t hint)
+{
+ uint16_t ndesc, avail_idx;
+
+ avail_idx = vq->vq_ring.avail->idx;
+ ndesc = (uint16_t)(avail_idx - vq->vq_used_cons_idx);
+
+ switch (hint) {
+ case VQ_POSTPONE_SHORT:
+ ndesc = ndesc / 4;
+ break;
+ case VQ_POSTPONE_LONG:
+ ndesc = (ndesc * 3) / 4;
+ break;
+ case VQ_POSTPONE_EMPTIED:
+ break;
+ }
+
+ return (vq_ring_enable_interrupt(vq, ndesc));
+}
+
+/*
+ * Note this is only considered a hint to the host.
+ */
+void
+virtqueue_disable_intr(struct virtqueue *vq)
+{
+
+ if (vq->vq_flags & VIRTQUEUE_FLAG_EVENT_IDX) {
+ vring_used_event(&vq->vq_ring) = vq->vq_used_cons_idx -
+ vq->vq_nentries - 1;
+ } else
+ vq->vq_ring.avail->flags |= VRING_AVAIL_F_NO_INTERRUPT;
+}
+
+#ifdef NOTUSED
+int
+virtqueue_enqueue(struct virtqueue *vq, void *cookie, struct sglist *sg,
+ int readable, int writable)
+{
+ struct vq_desc_extra *dxp;
+ int needed;
+ uint16_t head_idx, idx;
+
+ needed = readable + writable;
+
+ VQASSERT(vq, cookie != NULL, "enqueuing with no cookie");
+ VQASSERT(vq, needed == sg->sg_nseg,
+ "segment count mismatch, %d, %d", needed, sg->sg_nseg);
+ VQASSERT(vq,
+ needed <= vq->vq_nentries || needed <= vq->vq_max_indirect_size,
+ "too many segments to enqueue: %d, %d/%d", needed,
+ vq->vq_nentries, vq->vq_max_indirect_size);
+
+ if (needed < 1)
+ return (EINVAL);
+ if (vq->vq_free_cnt == 0)
+ return (ENOSPC);
+
+ if (vq_ring_use_indirect(vq, needed)) {
+ vq_ring_enqueue_indirect(vq, cookie, sg, readable,
writable);
+ return (0);
+ } else if (vq->vq_free_cnt < needed)
+ return (EMSGSIZE);
+
+ head_idx = vq->vq_desc_head_idx;
+ VQ_RING_ASSERT_VALID_IDX(vq, head_idx);
+ dxp = &vq->vq_descx[head_idx];
+
+ VQASSERT(vq, dxp->cookie == NULL,
+ "cookie already exists for index %d", head_idx);
+ dxp->cookie = cookie;
+ dxp->ndescs = needed;
+
+ idx = vq_ring_enqueue_segments(vq, vq->vq_ring.desc, head_idx,
+ sg, readable, writable);
+
+ vq->vq_desc_head_idx = idx;
+ vq->vq_free_cnt -= needed;
+ if (vq->vq_free_cnt == 0)
+ VQ_RING_ASSERT_CHAIN_TERM(vq);
+ else
+ VQ_RING_ASSERT_VALID_IDX(vq, idx);
+
+ vq_ring_update_avail(vq, head_idx);
+
+ return (0);
+}
+#endif
+#ifdef RTEMS_VIRTIO_NET
+int
+rtems_virtqueue_enqueue(struct virtqueue *vq, void *cookie,
+ int readable, int writable)
+{
+ struct vq_desc_extra *dxp;
+ int needed;
+ uint16_t head_idx, idx;
+
+ needed = readable + writable;
+
+ VQASSERT(vq, cookie != NULL, "enqueuing with no cookie");
+ VQASSERT(vq,
+ needed <= vq->vq_nentries || needed <= vq->vq_max_indirect_size,
+ "too many segments to enqueue: %d, %d/%d", needed,
+ vq->vq_nentries, vq->vq_max_indirect_size);
+
+ if (needed < 1)
+ return (EINVAL);
+ if (vq->vq_free_cnt == 0)
+ return (ENOSPC);
+
+#ifdef NOTUSED
+ if (vq_ring_use_indirect(vq, needed)) {
+ vq_ring_enqueue_indirect(vq, cookie, sg, readable,
writable);
+ return (0);
+ } else if (vq->vq_free_cnt < needed)
+ return (EMSGSIZE);
+#endif
+#ifdef RTEMS_VIRTIO_NET
+ if (vq->vq_free_cnt < needed)
+ return (EMSGSIZE);
+#endif
+
+ head_idx = vq->vq_desc_head_idx;
+ VQ_RING_ASSERT_VALID_IDX(vq, head_idx);
+ dxp = &vq->vq_descx[head_idx];
+
+ VQASSERT(vq, dxp->cookie == NULL,
+ "cookie already exists for index %d", head_idx);
+ dxp->cookie = cookie;
+ dxp->ndescs = needed;
+
+ idx = rtems_vq_ring_enqueue_segments(vq, vq->vq_ring.desc, head_idx,
+ cookie, readable, writable);
+
+ vq->vq_desc_head_idx = idx;
+ vq->vq_free_cnt -= needed;
+ if (vq->vq_free_cnt == 0)
+ VQ_RING_ASSERT_CHAIN_TERM(vq);
+ else
+ VQ_RING_ASSERT_VALID_IDX(vq, idx);
+
+ vq_ring_update_avail(vq, head_idx);
+
+ return (0);
+}
+#endif
+
+void *
+virtqueue_dequeue(struct virtqueue *vq, uint32_t *len)
+{
+ struct vring_used_elem *uep;
+ void *cookie;
+ uint16_t used_idx, desc_idx;
+
+ if (vq->vq_used_cons_idx == vq->vq_ring.used->idx)
+ return (NULL);
+
+ used_idx = vq->vq_used_cons_idx++ & (vq->vq_nentries - 1);
+ uep = &vq->vq_ring.used->ring[used_idx];
+
+ rmb();
+ // __asm __volatile("lock; addl $0,(%%esp)" : : : "memory", "cc")
+ desc_idx = (uint16_t) uep->id;
+ if (len != NULL)
+ *len = uep->len;
+
+ vq_ring_free_chain(vq, desc_idx);
+
+ cookie = vq->vq_descx[desc_idx].cookie;
+ VQASSERT(vq, cookie != NULL, "no cookie for index %d", desc_idx);
+ vq->vq_descx[desc_idx].cookie = NULL;
+
+ return (cookie);
+}
+
+#ifdef NOTUSED
+void *
+virtqueue_poll(struct virtqueue *vq, uint32_t *len)
+{
+ void *cookie;
+
+ while ((cookie = virtqueue_dequeue(vq, len)) == NULL)
+ cpu_spinwait();
+
+ return (cookie);
+}
+#endif
+
+void *
+virtqueue_drain(struct virtqueue *vq, int *last)
+{
+ void *cookie;
+ int idx;
+
+ cookie = NULL;
+ idx = *last;
+
+ while (idx < vq->vq_nentries && cookie == NULL) {
+ if ((cookie = vq->vq_descx[idx].cookie) != NULL) {
+ vq->vq_descx[idx].cookie = NULL;
+ /* Free chain to keep free count consistent. */
+ vq_ring_free_chain(vq, idx);
+ }
+ idx++;
+ }
+
+ *last = idx;
+
+ return (cookie);
+}
+
+#ifdef NOTUSED
+void
+virtqueue_dump(struct virtqueue *vq)
+{
+
+ if (vq == NULL)
+ return;
+
+ printf("VQ: %s - size=%d; free=%d; used=%d; queued=%d; "
+ "desc_head_idx=%d; avail.idx=%d; used_cons_idx=%d; "
+ "used.idx=%d; avail.flags=0x%x; used.flags=0x%x\n",
+ vq->vq_name, vq->vq_nentries, vq->vq_free_cnt,
+ virtqueue_nused(vq), vq->vq_queued_cnt, vq->vq_desc_head_idx,
+ vq->vq_ring.avail->idx, vq->vq_used_cons_idx,
+ vq->vq_ring.used->idx, vq->vq_ring.avail->flags,
+ vq->vq_ring.used->flags);
+}
+#endif
+
+static void
+vq_ring_init(struct virtqueue *vq)
+{
+ struct vring *vr;
+ char *ring_mem;
+ int i, size;
+
+ ring_mem = vq->vq_ring_mem;
+ size = vq->vq_nentries;
+ vr = &vq->vq_ring;
+
+ vring_init(vr, size, ring_mem, vq->vq_alignment);
+
+ for (i = 0; i < size - 1; i++)
+ vr->desc[i].next = i + 1;
+ vr->desc[i].next = VQ_RING_DESC_CHAIN_END;
+}
+
+static void
+vq_ring_update_avail(struct virtqueue *vq, uint16_t desc_idx)
+{
+ uint16_t avail_idx;
+
+ /*
+ * Place the head of the descriptor chain into the next slot and
make
+ * it usable to the host. The chain is made available now rather
than
+ * deferring to virtqueue_notify() in the hopes that if the host is
+ * currently running on another CPU, we can keep it processing the
new
+ * descriptor.
+ */
+ avail_idx = vq->vq_ring.avail->idx & (vq->vq_nentries - 1);
+ vq->vq_ring.avail->ring[avail_idx] = desc_idx;
+
+ wmb();
+ vq->vq_ring.avail->idx++;
+
+ /* Keep pending count until virtqueue_notify(). */
+ vq->vq_queued_cnt++;
+}
+
+#ifdef NOTUSED
+static uint16_t
+vq_ring_enqueue_segments(struct virtqueue *vq, struct vring_desc *desc,
+ uint16_t head_idx, struct sglist *sg, int readable, int writable)
+{
+ struct sglist_seg *seg;
+ struct vring_desc *dp;
+ int i, needed;
+ uint16_t idx;
+
+ needed = readable + writable;
+
+ for (i = 0, idx = head_idx, seg = sg->sg_segs;
+ i < needed;
+ i++, idx = dp->next, seg++) {
+ VQASSERT(vq, idx != VQ_RING_DESC_CHAIN_END,
+ "premature end of free desc chain");
+
+ dp = &desc[idx];
+ dp->addr = seg->ss_paddr;
+ dp->len = seg->ss_len;
+ dp->flags = 0;
+
+ if (i < needed - 1)
+ dp->flags |= VRING_DESC_F_NEXT;
+ if (i >= readable)
+ dp->flags |= VRING_DESC_F_WRITE;
+ }
+
+ return (idx);
+}
+#endif
+#ifdef RTEMS_VIRTIO_NET
+static uint16_t
+rtems_vq_ring_enqueue_segments(struct virtqueue *vq, struct vring_desc
*desc,
+ uint16_t head_idx, struct mbuf *m_head, int readable, int writable)
+{
+ struct mbuf *m;
+ struct vring_desc *dp;
+ int i, needed;
+ uint16_t idx;
+
+ needed = readable + writable;
+
+ for (i = 0, idx = head_idx, m = m_head; i < needed;
+ i++, idx = dp->next, m=m->m_next) {
+ VQASSERT(vq, idx != VQ_RING_DESC_CHAIN_END,
+ "premature end of free desc chain");
+
+ dp = &desc[idx];
+ dp->addr = (uint64_t) m->m_data;
+ dp->len = m->m_len;
+ dp->flags = 0;
+
+ if (i < needed - 1)
+ dp->flags |= VRING_DESC_F_NEXT;
+ if (i >= readable)
+ dp->flags |= VRING_DESC_F_WRITE;
+ }
+
+ return (idx);
+}
+#endif
+
+#ifdef NOTUSED
+static int
+vq_ring_use_indirect(struct virtqueue *vq, int needed)
+{
+
+ if ((vq->vq_flags & VIRTQUEUE_FLAG_INDIRECT) == 0)
+ return (0);
+
+ if (vq->vq_max_indirect_size < needed)
+ return (0);
+
+ if (needed < 2)
+ return (0);
+
+ return (1);
+}
+
+static void
+vq_ring_enqueue_indirect(struct virtqueue *vq, void *cookie,
+ struct sglist *sg, int readable, int writable)
+{
+ struct vring_desc *dp;
+ struct vq_desc_extra *dxp;
+ int needed;
+ uint16_t head_idx;
+
+ needed = readable + writable;
+ VQASSERT(vq, needed <= vq->vq_max_indirect_size,
+ "enqueuing too many indirect descriptors");
+
+ head_idx = vq->vq_desc_head_idx;
+ VQ_RING_ASSERT_VALID_IDX(vq, head_idx);
+ dp = &vq->vq_ring.desc[head_idx];
+ dxp = &vq->vq_descx[head_idx];
+
+ VQASSERT(vq, dxp->cookie == NULL,
+ "cookie already exists for index %d", head_idx);
+ dxp->cookie = cookie;
+ dxp->ndescs = 1;
+
+ dp->addr = dxp->indirect_paddr;
+ dp->len = needed * sizeof(struct vring_desc);
+ dp->flags = VRING_DESC_F_INDIRECT;
+
+ vq_ring_enqueue_segments(vq, dxp->indirect, 0,
+ sg, readable, writable);
+
+ vq->vq_desc_head_idx = dp->next;
+ vq->vq_free_cnt--;
+ if (vq->vq_free_cnt == 0)
+ VQ_RING_ASSERT_CHAIN_TERM(vq);
+ else
+ VQ_RING_ASSERT_VALID_IDX(vq, vq->vq_desc_head_idx);
+
+ vq_ring_update_avail(vq, head_idx);
+}
+#endif
+
+static int
+vq_ring_enable_interrupt(struct virtqueue *vq, uint16_t ndesc)
+{
+
+ /*
+ * Enable interrupts, making sure we get the latest index of
+ * what's already been consumed.
+ */
+ if (vq->vq_flags & VIRTQUEUE_FLAG_EVENT_IDX)
+ vring_used_event(&vq->vq_ring) = vq->vq_used_cons_idx +
ndesc;
+ else
+ vq->vq_ring.avail->flags &= ~VRING_AVAIL_F_NO_INTERRUPT;
+
+ mb();
+
+ /*
+ * Enough items may have already been consumed to meet our threshold
+ * since we last checked. Let our caller know so it processes the
new
+ * entries.
+ */
+ if (virtqueue_nused(vq) > ndesc)
+ return (1);
+
+ return (0);
+}
+
+static int
+vq_ring_must_notify_host(struct virtqueue *vq)
+{
+ uint16_t new_idx, prev_idx, event_idx;
+
+#ifdef NOTUSED
+ if (vq->vq_flags & VIRTQUEUE_FLAG_EVENT_IDX) {
+ new_idx = vq->vq_ring.avail->idx;
+ prev_idx = new_idx - vq->vq_queued_cnt;
+ event_idx = vring_avail_event(&vq->vq_ring);
+
+ return (vring_need_event(event_idx, new_idx, prev_idx) !=
0);
+ }
+#endif
+
+ return ((vq->vq_ring.used->flags & VRING_USED_F_NO_NOTIFY) == 0);
+}
+
+static void
+vq_ring_notify_host(struct virtqueue *vq)
+{
+
+ VIRTIO_BUS_NOTIFY_VQ(vq->vq_dev, vq->vq_queue_index);
+}
+
+static void
+vq_ring_free_chain(struct virtqueue *vq, uint16_t desc_idx)
+{
+ struct vring_desc *dp;
+ struct vq_desc_extra *dxp;
+
+ VQ_RING_ASSERT_VALID_IDX(vq, desc_idx);
+ dp = &vq->vq_ring.desc[desc_idx];
+ dxp = &vq->vq_descx[desc_idx];
+
+ if (vq->vq_free_cnt == 0)
+ VQ_RING_ASSERT_CHAIN_TERM(vq);
+
+ vq->vq_free_cnt += dxp->ndescs;
+ dxp->ndescs--;
+
+ if ((dp->flags & VRING_DESC_F_INDIRECT) == 0) {
+ while (dp->flags & VRING_DESC_F_NEXT) {
+ VQ_RING_ASSERT_VALID_IDX(vq, dp->next);
+ dp = &vq->vq_ring.desc[dp->next];
+ dxp->ndescs--;
+ }
+ }
+
+ VQASSERT(vq, dxp->ndescs == 0,
+ "failed to free entire desc chain, remaining: %d", dxp->ndescs);
+
+ /*
+ * We must append the existing free chain, if any, to the end of
+ * newly freed chain. If the virtqueue was completely used, then
+ * head would be VQ_RING_DESC_CHAIN_END (ASSERTed above).
+ */
+ dp->next = vq->vq_desc_head_idx;
+ vq->vq_desc_head_idx = desc_idx;
+}
diff --git a/c/src/lib/libbsp/i386/pc386/virtio/virtqueue.h
b/c/src/lib/libbsp/i386/pc386/virtio/virtqueue.h
new file mode 100644
index 0000000..9016f7d
--- /dev/null
+++ b/c/src/lib/libbsp/i386/pc386/virtio/virtqueue.h
@@ -0,0 +1,127 @@
+/**
+ * @file virqueue.h
+ * @brief
+ */
+
+/*
+ * Authors: Jin-Hyun Kim <jinhyun at konkuk.ac.kr>,
+ * and Hyun-Wook Jin <jinh at konkuk.ac.kr>, http://sslab.konkuk.ac.kr
+ * Ported from FreeBSD to RTEMS March 16
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.NET
+ */
+
+/*-
+ * Copyright (c) 2011, Bryan Venteicher <bryanv at FreeBSD.org>
+ * All rights reserved.
+ *
+ * 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 unmodified, 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 ``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 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: release/10.0.0/sys/dev/virtio/virtqueue.h 255109 2013-09-01
04:16:43Z bryanv $
+ */
+
+#ifndef _VIRTIO_VIRTQUEUE_H
+#define _VIRTIO_VIRTQUEUE_H
+
+struct virtqueue;
+#ifdef NOTUSED
+struct sglist;
+#endif
+
+/* Support for indirect buffer descriptors. */
+#define VIRTIO_RING_F_INDIRECT_DESC (1 << 28)
+
+/* Support to suppress interrupt until specific index is reached. */
+#define VIRTIO_RING_F_EVENT_IDX (1 << 29)
+
+/* Device callback for a virtqueue interrupt. */
+typedef void virtqueue_intr_t(void *);
+
+/*
+ * Hint on how long the next interrupt should be postponed. This is
+ * only used when the EVENT_IDX feature is negotiated.
+ */
+typedef enum {
+ VQ_POSTPONE_SHORT,
+ VQ_POSTPONE_LONG,
+ VQ_POSTPONE_EMPTIED /* Until all available desc are used. */
+} vq_postpone_t;
+
+#define VIRTQUEUE_MAX_NAME_SZ 32
+
+/* One for each virtqueue the device wishes to allocate. */
+struct vq_alloc_info {
+ char vqai_name[VIRTQUEUE_MAX_NAME_SZ];
+ int vqai_maxindirsz;
+ virtqueue_intr_t *vqai_intr;
+ void *vqai_intr_arg;
+ struct virtqueue **vqai_vq;
+};
+
+#define VQ_ALLOC_INFO_INIT(_i,_nsegs,_intr,_arg,_vqp,_str,...) do { \
+ snprintf((_i)->vqai_name, VIRTQUEUE_MAX_NAME_SZ, _str, \
+ ##__VA_ARGS__); \
+ (_i)->vqai_maxindirsz = (_nsegs); \
+ (_i)->vqai_intr = (_intr); \
+ (_i)->vqai_intr_arg = (_arg); \
+ (_i)->vqai_vq = (_vqp); \
+} while (0)
+
+uint64_t virtqueue_filter_features(uint64_t features);
+
+int virtqueue_alloc(device_t dev, uint16_t queue, uint16_t size,
+ int align, vm_paddr_t highaddr, struct vq_alloc_info *info,
+ struct virtqueue **vqp);
+void *virtqueue_drain(struct virtqueue *vq, int *last);
+void virtqueue_free(struct virtqueue *vq);
+int virtqueue_reinit(struct virtqueue *vq, uint16_t size);
+
+int virtqueue_intr_filter(struct virtqueue *vq);
+void virtqueue_intr(struct virtqueue *vq);
+int virtqueue_enable_intr(struct virtqueue *vq);
+int virtqueue_postpone_intr(struct virtqueue *vq, vq_postpone_t hint);
+void virtqueue_disable_intr(struct virtqueue *vq);
+
+/* Get physical address of the virtqueue ring. */
+vm_paddr_t virtqueue_paddr(struct virtqueue *vq);
+
+int virtqueue_full(struct virtqueue *vq);
+int virtqueue_empty(struct virtqueue *vq);
+int virtqueue_size(struct virtqueue *vq);
+int virtqueue_nused(struct virtqueue *vq);
+void virtqueue_notify(struct virtqueue *vq);
+void virtqueue_dump(struct virtqueue *vq);
+
+#ifdef NOTUSED
+int virtqueue_enqueue(struct virtqueue *vq, void *cookie,
+ struct sglist *sg, int readable, int writable);
+#endif
+#ifdef RTEMS_VIRTIO_NET
+int rtems_virtqueue_enqueue(struct virtqueue *vq, void *cookie,
+ int readable, int writable);
+#endif
+void *virtqueue_dequeue(struct virtqueue *vq, uint32_t *len);
+void *virtqueue_poll(struct virtqueue *vq, uint32_t *len);
+
+#endif /* _VIRTIO_VIRTQUEUE_H */
--
1.9.1
More information about the devel
mailing list