Hi Thanks for the submission. My comments are going to be very general.
+ Can you document which version of the FreeBSD source files this is based up? Perhaps an svn revision/branch and a list of the exact files in their source tree? This has been a historical problem when trying to track future changes. + What were the nature of the rtems specific changes? Are they cleanly ifdef'ed so a diff against the original FreeBSD source shows ifdef additions and blocks of RTEMS specific code added? + I didn't see any instructions on how to use this. What type of setup is needed? I am pretty sure I actually reviewed a conference paper on this work and am excited to see it submitted. Let's just make it top notch on usability and maintainability. --joel On Tue, Mar 29, 2016 at 8:47 AM, Jinhyun <jinh...@konkuk.ac.kr> wrote: > diff --git a/c/src/lib/libbsp/i386/pc386/Makefile.am > b/c/src/lib/libbsp/i386/pc386/Makefile.am > index d9af7dd..da50c67 100644 > --- a/c/src/lib/libbsp/i386/pc386/Makefile.am > +++ b/c/src/lib/libbsp/i386/pc386/Makefile.am > @@ -232,6 +232,19 @@ noinst_PROGRAMS += 3c509.rel > 3c509_rel_LDFLAGS += -Wl,--undefined=ep_board > 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_vtnet.h > +vtnet_rel_SOURCES += virtio/virtio_pci.c > +vtnet_rel_SOURCES += virtio/virtio_pci.h > +vtnet_rel_SOURCES += virtio/virtio.c > +vtnet_rel_SOURCES += virtio/virtio.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 > @@ -246,6 +259,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/if_vtnet.c > b/c/src/lib/libbsp/i386/pc386/virtio/if_vtnet.c > new file mode 100644 > index 0000000..4fcd92c > --- /dev/null > +++ b/c/src/lib/libbsp/i386/pc386/virtio/if_vtnet.c > @@ -0,0 +1,1032 @@ > +/** > + * @file if_vtnet.c > + * @brief Driver for virtio network devices > + */ > + > +/* > + * Copyright (c) 2016 Jin-Hyun Kim <jinh...@konkuk.ac.kr> > + * and Hyun-Wook Jin <j...@konkuk.ac.kr> > + * Ported from FreeBSD to RTEMS March 16, http://sslab.konkuk.ac.kr > + * > + * 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. > + */ > + > +/*- > + * Copyright (c) 2011, Bryan Venteicher <bry...@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. > + * > + */ > + > +#include <rtems.h> > +#include <rtems/rtems_bsdnet.h> > + > +#include <bsp.h> > +#include <bsp/irq.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 <pcibios.h> > + > +#include "virtio.h" > +#include "virtio_pci.h" > +#include "if_vtnet.h" > + > +static struct vtnet_softc vtnet_softc; > +static rtems_interval vtnet_ticksPerSecond; > + > +int rtems_vtnet_driver_attach( > + struct rtems_bsdnet_ifconfig *config, > + int attaching > +) > +{ > + struct vtnet_softc *sc; > + int ret; > + > + printk( "rtems_vtnet_driver_attach start\n" ); > + > + sc = &vtnet_softc; > + memset( sc, 0, sizeof( struct vtnet_softc ) ); > + > + /* Set up timing values */ > + vtnet_ticksPerSecond = rtems_clock_get_ticks_per_second(); > + > + printk( "\tAttaching virtio pci...\n" ); > + ret = vtpci_attach( config, &sc->vtpci_softc ); > + > + if ( ret ) { > + printk( "vtpci_attach fail...\n" ); > + } > + > + /* Alloc virtqueues */ > + printk( "\tAllocating virtqueues...\n" ); > + ret = vtnet_alloc_virtqueues( sc ); > + > + if ( ret ) { > + printk( "vtnet_alloc_virtqueues fail...\n" ); > + } > + > + /* Setup interrupt */ > + printk( "\tSetup interrupt...\n" ); > + ret = vtnet_setup_intr( &sc->vtpci_softc ); > + > + if ( ret ) { > + printk( "vtpci_setup_intr fail...\n" ); > + } > + > + /* Setup interface */ > + printk( "\tSetup interface...\n" ); > + ret = vtnet_setup_interface( sc, config ); > + > + if ( ret ) { > + printk( "vtnet_setup_interface fail...\n" ); > + } > + > + printk( "rtems_vtnet_driver_attach end\n" ); > + > + return 0; > +} > + > +static int vtnet_alloc_virtqueues( struct vtnet_softc *sc ) > +{ > + uint16_t val16; > + int size, error; > + > + /* Init virtio_net_hdr */ > + memset( &sc->vtnet_net_hdr, 0, sizeof( struct virtio_net_hdr ) ); > + > + /* Select virtqueue 0 */ > + vtpci_io_write_2( &sc->vtpci_softc, > + VIRTIO_PCI_QUEUE_SEL, > + VIRTIO_PCI_QUEUE_SEL_RX ); > + val16 = vtpci_io_read_2( &sc->vtpci_softc, VIRTIO_PCI_QUEUE_NUM ); > + > + /* Alloc memory & continuous memory for vring */ > + sc->vtnet_rxvq_size = val16; > + size = sizeof( struct virtqueue ) + val16 * sizeof( struct vq_desc_extra > ); > + sc->vtnet_rxvq = malloc( size, M_DEVBUF, M_NOWAIT ); > + memset( sc->vtnet_rxvq, 0, size ); > + > + error = > + vtnet_alloc_virtqueue( sc->vtnet_rxvq, VIRTIO_PCI_QUEUE_SEL_RX, val16 > ); > + > + if ( error ) { > + return error; > + } > + > + /* Select virtqueue 1 */ > + vtpci_io_write_2( &sc->vtpci_softc, > + VIRTIO_PCI_QUEUE_SEL, > + VIRTIO_PCI_QUEUE_SEL_TX ); > + val16 = vtpci_io_read_2( &sc->vtpci_softc, VIRTIO_PCI_QUEUE_NUM ); > + > + /* Alloc memory & continuous memory for vring */ > + sc->vtnet_txvq_size = val16; > + size = sizeof( struct virtqueue ) + val16 * sizeof( struct vq_desc_extra > ); > + sc->vtnet_txvq = malloc( size, M_DEVBUF, M_NOWAIT ); > + memset( sc->vtnet_txvq, 0, size ); > + > + error = > + vtnet_alloc_virtqueue( sc->vtnet_txvq, VIRTIO_PCI_QUEUE_SEL_TX, val16 > ); > + > + if ( error ) { > + return error; > + } > + > + return 0; > +} > + > +static int vtnet_alloc_virtqueue( > + struct virtqueue *vq, > + uint16_t queue_num, > + uint16_t queue_size > +) > +{ > + unsigned long align; > + void *mem; > + int size; > + > + vq->vq_queue_index = queue_num; > + vq->vq_alignment = VIRTIO_PCI_VRING_ALIGN; > + vq->vq_nentries = queue_size; > + vq->vq_free_cnt = queue_size; > + vq->vq_max_indirect_size = VTNET_RX_MIN_SEGS; > + > + align = vq->vq_alignment; > + size = queue_size * sizeof( struct vring_desc ); > + size += sizeof( struct vring_avail ) + ( queue_size * sizeof( uint16_t ) > ) + > + sizeof( uint16_t ); > + > + size = ( size + align - 1 ) & ~( vq->vq_alignment - 1 ); > + size += sizeof( struct vring_used ) + > + ( queue_size * sizeof( struct vring_used_elem ) ) + > + sizeof( uint16_t ); > + > + mem = (void *) malloc( size + align, M_DEVBUF, M_NOWAIT ); > + > + if ( ( (unsigned long) mem % align ) > 0 ) { > + mem = > + (void *) ( (unsigned long) mem + > + ( align - ( (unsigned long) mem % align ) ) ); > + } > + > + vq->vq_ring_size = size; > + vq->vq_ring_mem = mem; > + memset( vq->vq_ring_mem, 0, size ); > + > + virtqueue_disable_intr( vq ); > + > + return 0; > +} > + > +int vtnet_setup_intr( struct vtpci_softc *sc ) > +{ > + uint8_t val8; > + int ret; > + > + /* Get intrrupt level */ > + pcib_conf_read8( sc->pci_signature, PCI_INTERRUPT_LINE, &val8 ); > + ret = rtems_interrupt_handler_install( > + val8, > + NULL, > + RTEMS_INTERRUPT_SHARED, > + (rtems_interrupt_handler) vtnet_intr, > + NULL ); > + > + return ret; > +} > + > +static int vtnet_setup_interface( > + struct vtnet_softc *sc, > + struct rtems_bsdnet_ifconfig *config > +) > +{ > + struct ifnet *ifp; > + int mtu; > + > + ifp = &sc->arpcom.ac_if; > + ifp->if_softc = (void *) &vtnet_softc; > + > + if ( config->mtu ) { > + mtu = config->mtu; > + } else { > + mtu = ETHERMTU; > + } > + > + 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; > + > + ifp->if_softc = sc; > + ifp->if_unit = sc->vtpci_softc.unit_number; > + ifp->if_name = sc->vtpci_softc.unit_name; > + ifp->if_mtu = mtu; > + ifp->if_init = vtnet_init; > + ifp->if_ioctl = vtnet_ioctl; > + ifp->if_start = vtnet_start; > + ifp->if_output = ether_output; > + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX; > + ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; > + > + if_attach( ifp ); > + ether_ifattach( ifp ); > + > + return 0; > +} > + > +static void vtnet_init( void *xsc ) > +{ > + rtems_interrupt_level level; > + struct vtnet_softc *sc; > + struct ifnet *ifp; > + struct mbuf *m; > + uint32_t val32; > + uint16_t val16; > + uint8_t val8; > + int last; > + > + sc = xsc; > + ifp = &sc->arpcom.ac_if; > + > + rtems_interrupt_disable( level ); > + > + if ( ifp->if_flags & IFF_RUNNING ) { > + return; > + } > + > + printk( "vtnet_init start\n" ); > + > + /* vtnet_stop */ > + ifp->if_flags &= ~IFF_RUNNING; > + sc->vtnet_link_active = 0; > + vtnet_callout_stop( sc ); > + > + /* vtnet_disable_interrupts */ > + virtqueue_disable_intr( sc->vtnet_rxvq ); > + virtqueue_disable_intr( sc->vtnet_txvq ); > + > + /* vtpci_stop */ > + vtpci_io_write_1( &sc->vtpci_softc, > + VIRTIO_PCI_STATUS, > + VIRTIO_CONFIG_STATUS_RESET ); > + > + /* vtnet_drain_rxtx_queues */ > + last = 0; > + > + while ( ( m = virtqueue_drain( sc->vtnet_rxvq, &last ) ) != NULL ) { > + m_freem( m ); > + } > + > + last = 0; > + > + while ( ( m = virtqueue_drain( sc->vtnet_txvq, &last ) ) != NULL ) { > + m_freem( m ); > + } > + > + /* vtpci_reinit */ > + val8 = vtpci_io_read_1( &sc->vtpci_softc, VIRTIO_PCI_STATUS ); > + > + if ( val8 != VIRTIO_CONFIG_STATUS_RESET ) { > + /* vtpci_stop */ > + vtpci_io_write_1( &sc->vtpci_softc, > + VIRTIO_PCI_STATUS, > + VIRTIO_CONFIG_STATUS_RESET ); > + } > + > + /* Set status bit as acknowlege & driver */ > + val8 = vtpci_io_read_1( &sc->vtpci_softc, VIRTIO_PCI_STATUS ); > + val8 |= VIRTIO_CONFIG_STATUS_ACK; > + vtpci_io_write_1( &sc->vtpci_softc, VIRTIO_PCI_STATUS, val8 ); > + val8 = vtpci_io_read_1( &sc->vtpci_softc, VIRTIO_PCI_STATUS ); > + val8 |= VIRTIO_CONFIG_STATUS_DRIVER; > + vtpci_io_write_1( &sc->vtpci_softc, VIRTIO_PCI_STATUS, val8 ); > + > + vtnet_negotiate_features( sc ); > + > + /* vtpci_init_virtqueue */ > + vtnet_init_virtqueue( sc, sc->vtnet_rxvq ); > + vtnet_init_virtqueue( sc, sc->vtnet_txvq ); > + > + vtpci_io_write_2( &sc->vtpci_softc, > + VIRTIO_PCI_QUEUE_SEL, > + VIRTIO_PCI_QUEUE_SEL_RX ); > + val16 = vtpci_io_read_2( &sc->vtpci_softc, VIRTIO_PCI_QUEUE_NUM ); > + > + if ( !val16 || vtpci_io_read_4( &sc->vtpci_softc, VIRTIO_PCI_QUEUE_PFN ) > ) { > + printk( "\tRXQ already exists!\n" ); > + } > + > + vtpci_io_write_4( &sc->vtpci_softc, VIRTIO_PCI_QUEUE_PFN, > + ( (uint32_t) sc->vtnet_rxvq->vq_ring_mem >> > + VIRTIO_PCI_QUEUE_ADDR_SHIFT ) ); > + > + vtpci_io_write_2( &sc->vtpci_softc, > + VIRTIO_PCI_QUEUE_SEL, > + VIRTIO_PCI_QUEUE_SEL_TX ); > + val16 = vtpci_io_read_2( &sc->vtpci_softc, VIRTIO_PCI_QUEUE_NUM ); > + > + if ( !val16 || vtpci_io_read_4( &sc->vtpci_softc, VIRTIO_PCI_QUEUE_PFN ) > ) { > + printk( "\tTXQ already exists!\n" ); > + } > + > + vtpci_io_write_4( &sc->vtpci_softc, VIRTIO_PCI_QUEUE_PFN, > + ( (uint32_t) sc->vtnet_txvq->vq_ring_mem >> > + VIRTIO_PCI_QUEUE_ADDR_SHIFT ) ); > + > + /* vtnet_init_rx_queues */ > + sc->vtnet_rx_clsize = MCLBYTES; > + vtnet_rxq_populate( sc ); > + > + /* vtnet_init_tx_queues */ > + sc->vtnet_tx_watchdog = 0; > + > + vq_ring_enable_interrupt( sc->vtnet_rxvq, 0 ); > + vq_ring_enable_interrupt( sc->vtnet_txvq, 0 ); > + ifp->if_flags |= IFF_RUNNING; > + > + /* virtio_reinit_complete */ > + val8 = vtpci_io_read_1( &sc->vtpci_softc, VIRTIO_PCI_STATUS ); > + val8 |= VIRTIO_CONFIG_STATUS_DRIVER_OK; > + vtpci_io_write_1( &sc->vtpci_softc, VIRTIO_PCI_STATUS, val8 ); > + > + /* vtnet_update_link_status; */ > + sc->vtnet_link_active = 1; > + > + /* start vtnet_daemon */ > + if ( sc->daemonTid == 0 ) { > + sc->daemonTid = rtems_bsdnet_newproc( "VTNd", > + VTNET_DAEMON_STKSIZE, > + vtnet_daemon, > + sc ); > + } > + > + /* start vtnet_tick */ > + sc->stat_ch = vtnet_timeout_running; > + /* TODO: timeout routine is not working */ > + /* timeout(vtnet_tick, sc, hz); */ > + > + printk( "vtnet_init end\n" ); > + > + rtems_interrupt_enable( level ); > +} > + > +static int vtnet_rxq_eof( struct vtnet_softc *sc ) > +{ > + struct virtqueue *vq; > + struct ifnet *ifp; > + struct mbuf *m; > + struct ether_header *eh; > + uint32_t len; > + int deq, count; > + > + vq = sc->vtnet_rxvq; > + ifp = &sc->arpcom.ac_if; > + len = 0; > + deq = 0; > + count = VTNET_RX_PROCESS_LIMIT; > + > + while ( count-- > 0 ) { > + m = virtqueue_dequeue( vq, &len ); > + > + if ( m == NULL ) { > + break; > + } > + > + deq++; > + > + if ( len < sc->vtnet_hdr_size ) { > + vtnet_rxq_enqueue( sc, m ); > + continue; > + } > + > + if ( vtnet_rxq_replace( sc, m, len ) != 0 ) { > + vtnet_rxq_enqueue( sc, m ); > + continue; > + } > + > + m->m_pkthdr.len = len; > + m->m_pkthdr.rcvif = ifp; > + m_adj( m, sizeof( struct virtio_net_hdr ) ); > + > + /* vtnet_rxq_input */ > + eh = mtod( m, struct ether_header * ); > + m_adj( m, sizeof( struct ether_header ) ); > + m->m_pkthdr.len = m->m_len; > + > + ether_input( ifp, eh, m ); > + > + if ( ( ifp->if_flags & IFF_RUNNING ) == 0 ) { > + break; > + } > + } > + > + if ( deq > 0 ) { > + vtpci_notify( &sc->vtpci_softc, vq ); > + } > + > + return ( count > 0 ? 0 : EAGAIN ); > +} > + > +static int vtnet_rxq_enqueue( > + struct vtnet_softc *sc, > + struct mbuf *m_head > +) > +{ > + struct virtqueue *vq; > + struct vring_desc *desc, *dp; > + struct vq_desc_extra *dxp; > + struct mbuf *m; > + uint16_t head_idx, idx; > + uint8_t segs; > + > + vq = sc->vtnet_rxvq; > + desc = vq->vq_ring.desc; > + > + if ( vq->vq_free_cnt == 0 ) { > + return ENOSPC; > + } > + > + segs = 0; > + > + for ( m = m_head; m != NULL; m = m->m_next ) { > + segs++; > + } > + > + if ( segs == 0 ) { > + return EINVAL; > + } > + > + if ( vq->vq_free_cnt < segs ) { > + return EMSGSIZE; > + } > + > + head_idx = vq->vq_desc_head_idx; > + dxp = &vq->vq_descx[ head_idx ]; > + dxp->cookie = m_head; > + dxp->ndescs = segs; > + > + for ( m = m_head, idx = head_idx; m != NULL; > + m = m->m_next, idx = dp->next ) { > + dp = &desc[ idx ]; > + dp->addr = (uint32_t) m->m_data; > + dp->len = m->m_len; > + dp->flags = VRING_DESC_F_NEXT | VRING_DESC_F_WRITE; > + } > + > + dp->flags &= ~( VRING_DESC_F_NEXT ); > + > + vq->vq_desc_head_idx = idx; > + vq->vq_free_cnt -= segs; > + > + vq_ring_update_avail( vq, head_idx ); > + > + return 0; > +} > + > +static int vtnet_rxq_replace( > + struct vtnet_softc *sc, > + struct mbuf *m, > + int len > +) > +{ > + struct mbuf *m_new; > + int error; > + > + if ( m->m_next == NULL ) { > + if ( m->m_len < len ) { > + return EINVAL; > + } > + > + m_new = vtnet_rxq_alloc_mbuf( sc, 1, NULL ); > + > + if ( m_new == NULL ) { > + return ENOBUFS; > + } > + > + error = vtnet_rxq_enqueue( sc, m_new ); > + > + if ( error ) { > + m_freem( m_new ); > + } else { > + m->m_len = len; > + } > + } else { > + error = vtnet_rxq_replace_nomgr( sc, m, len ); > + } > + > + return error; > +} > + > +static struct mbuf *vtnet_rxq_alloc_mbuf( > + struct vtnet_softc *sc, > + int nbufs, > + struct mbuf **m_tailp > +) > +{ > + struct mbuf *m_head, *m_tail, *m; > + int i; > + > + MGETHDR( m_head, M_DONTWAIT, MT_DATA ); > + > + if ( m_head == NULL ) { > + m_freem( m_head ); > + > + return NULL; > + } > + > + MCLGET( m_head, M_DONTWAIT ); > + > + if ( ( m_head->m_flags & M_EXT ) == 0 ) { > + m_freem( m_head ); > + > + return NULL; > + } > + > + m_head->m_len = sc->vtnet_rx_clsize; > + m_tail = m_head; > + > + for ( i = 1; i < nbufs; i++ ) { > + MGETHDR( m, M_DONTWAIT, MT_DATA ); > + > + if ( m == NULL ) { > + m_freem( m_head ); > + > + return NULL; > + } > + > + MCLGET( m, M_DONTWAIT ); > + > + if ( ( m->m_flags & M_EXT ) == 0 ) { > + m_freem( m_head ); > + m_freem( m ); > + > + return NULL; > + } > + > + m->m_len = sc->vtnet_rx_clsize; > + m_tail->m_next = m; > + m_tail = m; > + } > + > + if ( m_tailp != NULL ) { > + *m_tailp = m_tail; > + } > + > + return m_head; > +} > + > +static int vtnet_rxq_replace_nomgr( > + struct vtnet_softc *sc, > + struct mbuf *m0, > + int len0 > +) > +{ > + struct mbuf *m, *m_prev, *m_new, *m_tail; > + int len, clsize, nreplace, error; > + > + clsize = sc->vtnet_rx_clsize; > + m_prev = NULL; > + m_tail = NULL; > + nreplace = 0; > + m = m0; > + len = len0; > + > + while ( len > 0 ) { > + if ( m == NULL ) { > + return EMSGSIZE; > + } > + > + m->m_len = ( m->m_len > len ) ? len : m->m_len; > + len -= m->m_len; > + > + m_prev = m; > + m = m->m_next; > + nreplace++; > + } > + > + m_new = vtnet_rxq_alloc_mbuf( sc, nreplace, &m_tail ); > + > + if ( m_new == NULL ) { > + m_prev->m_len = clsize; > + > + return ENOBUFS; > + } > + > + if ( m_prev->m_next != NULL ) { > + m_tail->m_next = m_prev->m_next; > + m_prev->m_next = NULL; > + } > + > + error = vtnet_rxq_enqueue( sc, m_new ); > + > + if ( error ) { > + 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 void vtnet_start( struct ifnet *ifp ) > +{ > + rtems_interrupt_level level; > + struct vtnet_softc *sc; > + struct virtqueue *vq; > + struct mbuf *m; > + int enq, error; > + > + sc = &vtnet_softc; > + vq = sc->vtnet_txvq; > + enq = 0; > + > + rtems_interrupt_disable( level ); > + > + if ( ( ifp->if_flags & IFF_RUNNING ) == 0 || sc->vtnet_link_active == 0 > ) > { > + rtems_interrupt_enable( level ); > + > + return; > + } > + > + vtnet_txq_eof( sc ); > + > + while ( ifp->if_snd.ifq_head != NULL ) { > + IF_DEQUEUE( &ifp->if_snd, m ); > + > + if ( m == NULL ) { > + break; > + } > + > + if ( ( error = vtnet_txq_enqueue( sc, m ) ) != 0 ) { > + if ( m != NULL ) { > + IF_PREPEND( &ifp->if_snd, m ); > + } > + > + break; > + } > + > + enq++; > + } > + > + if ( enq > 0 ) { > + vtpci_notify( &sc->vtpci_softc, vq ); > + sc->vtnet_tx_watchdog = VTNET_TX_TIMEOUT; > + } > + > + rtems_interrupt_enable( level ); > +} > + > +static void vtnet_txq_eof( struct vtnet_softc *sc ) > +{ > + struct virtqueue *vq; > + struct mbuf *m; > + > + vq = sc->vtnet_txvq; > + > + while ( ( m = virtqueue_dequeue( vq, NULL ) ) != NULL ) { > + m_freem( m ); > + } > + > + if ( vq->vq_nentries == vq->vq_free_cnt ) { > + sc->vtnet_tx_watchdog = 0; > + } > +} > + > +static int vtnet_txq_enqueue( > + struct vtnet_softc *sc, > + struct mbuf *m_head > +) > +{ > + struct virtqueue *vq; > + struct vring_desc *desc, *dp; > + struct vq_desc_extra *dxp; > + struct mbuf *m; > + uint16_t head_idx; > + uint8_t segs; > + > + vq = sc->vtnet_txvq; > + desc = vq->vq_ring.desc; > + > + if ( vq->vq_free_cnt == 0 ) { > + return ENOSPC; > + } > + > + segs = 1; > + > + for ( m = m_head; m != NULL; m = m->m_next ) { > + segs++; > + } > + > + if ( segs == 1 ) { > + return EINVAL; > + } > + > + if ( vq->vq_free_cnt < segs ) { > + return EMSGSIZE; > + } > + > + head_idx = vq->vq_desc_head_idx; > + dxp = &vq->vq_descx[ head_idx ]; > + dxp->cookie = m_head; > + dxp->ndescs = segs; > + > + /* First desc of chain must be vtnet tx hdr */ > + dp = &desc[ head_idx ]; > + dp->addr = (uint32_t) &sc->vtnet_net_hdr; > + dp->len = sizeof( struct virtio_net_hdr ); > + dp->flags |= VRING_DESC_F_NEXT; > + > + /* Link rest mbuf to chain */ > + for ( m = m_head; m != NULL; m = m->m_next ) { > + dp = &desc[ dp->next ]; > + dp->addr = mtod( m, uint32_t ); > + dp->len = m->m_len; > + dp->flags |= VRING_DESC_F_NEXT; > + } > + > + dp->flags &= ~( VRING_DESC_F_NEXT ); > + > + vq->vq_desc_head_idx = dp->next; > + vq->vq_free_cnt -= segs; > + > + vq_ring_update_avail( vq, head_idx ); > + > + return 0; > +} > + > +static void vtnet_negotiate_features( struct vtnet_softc *sc ) > +{ > + uint32_t host_features; > + uint32_t guest_features; > + > + host_features = > + vtpci_io_read_4( &sc->vtpci_softc, VIRTIO_PCI_HOST_FEATURES ); > + guest_features = host_features & 0xfffff; > + guest_features &= VTNET_FEATURES; > + > + vtpci_io_write_4( &sc->vtpci_softc, VIRTIO_PCI_GUEST_FEATURES, > + guest_features ); > + guest_features = > + vtpci_io_read_4( &sc->vtpci_softc, VIRTIO_PCI_GUEST_FEATURES ); > + > + printk( "\tHost_features:\t0x%08x\n", host_features ); > + printk( "\tGuest_features:\t0x%08x\n", guest_features ); > + > + sc->vtnet_hdr_size = sizeof( struct virtio_net_hdr ) + ETHER_HDR_LEN; > +} > + > +static void vtnet_callout_stop( struct vtnet_softc *sc ) > +{ > + 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(); > + } > + } > +} > + > +static void vtnet_callout_reset( struct vtnet_softc *sc ) > +{ > + 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; > + } > +} > + > +static int vtnet_init_virtqueue( > + struct vtnet_softc *sc, > + struct virtqueue *vq > +) > +{ > + struct vq_desc_extra *dxp; > + int i; > + > + vq->vq_desc_head_idx = 0; > + vq->vq_used_cons_idx = 0; > + vq->vq_queued_cnt = 0; > + vq->vq_free_cnt = vq->vq_nentries; > + > + memset( vq->vq_ring_mem, 0, vq->vq_ring_size ); > + > + for ( i = 0; i < vq->vq_nentries; i++ ) { > + dxp = &vq->vq_descx[ i ]; > + dxp->cookie = NULL; > + dxp->ndescs = 0; > + dxp->indirect = NULL; > + } > + > + vq_ring_init( vq ); > + virtqueue_disable_intr( vq ); > + > + return 0; > +} > + > +static int vtnet_rxq_populate( struct vtnet_softc *sc ) > +{ > + struct virtqueue *rxvq; > + struct mbuf *m; > + int nbufs, error; > + > + rxvq = sc->vtnet_rxvq; > + > + for ( nbufs = 0; rxvq->vq_free_cnt; nbufs++ ) { > + m = vtnet_rxq_alloc_mbuf( sc, 1, NULL ); > + > + if ( m == NULL ) { > + return ENOBUFS; > + } > + > + error = vtnet_rxq_enqueue( sc, m ); > + > + if ( error ) { > + m_freem( m ); > + } > + } > + > + if ( nbufs > 0 ) { > + vtpci_notify( &sc->vtpci_softc, rxvq ); > + > + if ( error == EMSGSIZE ) { > + error = 0; > + } > + } > + > + return error; > +} > + > +static int vtnet_ioctl( > + struct ifnet *ifp, > + ioctl_command_t cmd, > + caddr_t data > +) > +{ > + int error; > + > + error = 0; > + > + switch ( cmd ) { > + case SIOCSIFMTU: > + /* TODO: vtnet_change_mtu */ > + break; > + case SIOCSIFFLAGS: > + vtnet_init( ifp->if_softc ); > + break; > + case SIOCADDMULTI: > + case SIOCDELMULTI: > + /* TODO: vtnet_rx_filter_mac */ > + break; > + default: > + error = ether_ioctl( ifp, cmd, data ); > + break; > + } > + > + return error; > +} > + > +static void vtnet_tick( void *xsc ) > +{ > + struct vtnet_softc *sc; > + struct ifnet *ifp; > + int timedout; > + > + sc = &vtnet_softc; > + ifp = &sc->arpcom.ac_if; > + > + timedout = vtnet_watchdog( sc ); > + > + if ( timedout != 0 ) { > + ifp->if_flags &= ~IFF_RUNNING; > + vtnet_init( sc ); > + } else { > + vtnet_callout_reset( sc ); > + } > +} > + > +static int vtnet_watchdog( struct vtnet_softc *sc ) > +{ > + if ( sc->vtnet_tx_watchdog == 0 || --sc->vtnet_tx_watchdog ) { > + return 0; > + } > + > + return 1; > +} > + > +static rtems_isr vtnet_intr( rtems_vector_number v ) > +{ > + struct vtnet_softc *sc; > + struct ifnet *ifp; > + uint8_t isr; > + > + sc = &vtnet_softc; > + ifp = &sc->arpcom.ac_if; > + > + isr = vtpci_io_read_1( &sc->vtpci_softc, VIRTIO_PCI_ISR ); > + > + if ( isr & VIRTIO_PCI_ISR_INTR ) { > + if ( ( ifp->if_flags & IFF_RUNNING ) == 0 ) { > + return; > + } > + > + rtems_bsdnet_event_send( sc->daemonTid, RTEMS_EVENT_1 ); > + } else if ( isr & VIRTIO_PCI_ISR_CONFIG ) { > + /* > + * TODO: Handling of config interrupt > + */ > + } > +} > + > +static void vtnet_daemon( void *xsc ) > +{ > + struct vtnet_softc *sc; > + struct ifnet *ifp; > + struct virtqueue *rxvq; > + struct virtqueue *txvq; > + int more, tries; > + rtems_event_set events; > + > + sc = &vtnet_softc; > + ifp = &sc->arpcom.ac_if; > + rxvq = sc->vtnet_rxvq; > + txvq = sc->vtnet_txvq; > + > + while ( 1 ) { > + rtems_bsdnet_event_receive( RTEMS_EVENT_1, > + RTEMS_WAIT | RTEMS_EVENT_ANY, > + RTEMS_NO_TIMEOUT, > + &events ); > + > + /* tx intr */ > + tries = 0; > +againtx: > + vtnet_txq_eof( sc ); > + > + if ( ifp->if_snd.ifq_head != NULL ) { > + vtnet_start( ifp ); > + } > + > + if ( virtqueue_postpone_intr( txvq, VQ_POSTPONE_LONG ) != 0 ) { > + virtqueue_disable_intr( txvq ); > + > + if ( tries++ < VTNET_INTR_DISABLE_RETRIES ) { > + goto againtx; > + } > + } > + > + /* rx intr */ > + tries = 0; > +againrx: > + more = vtnet_rxq_eof( sc ); > + > + if ( more || vq_ring_enable_interrupt( rxvq, 0 ) != 0 ) { > + if ( !more ) { > + virtqueue_disable_intr( rxvq ); > + } > + > + if ( tries++ < VTNET_INTR_DISABLE_RETRIES ) { > + goto againrx; > + } > + } > + } > +} > diff --git a/c/src/lib/libbsp/i386/pc386/virtio/if_vtnet.h > b/c/src/lib/libbsp/i386/pc386/virtio/if_vtnet.h > new file mode 100644 > index 0000000..7f81bad > --- /dev/null > +++ b/c/src/lib/libbsp/i386/pc386/virtio/if_vtnet.h > @@ -0,0 +1,181 @@ > +/** > + * @file if_vtnet.h > + * @brief Header for if_vtnet.c > + */ > + > +/* > + * Copyright (c) 2016 Jin-Hyun Kim <jinh...@konkuk.ac.kr> > + * and Hyun-Wook Jin <j...@konkuk.ac.kr> > + * Ported from FreeBSD to RTEMS March 16, http://sslab.konkuk.ac.kr > + * > + * 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. > + */ > + > +/*- > + * 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 _IF_VTNET_H_ > +#define _IF_VTNET_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 VTNET_FEATURES \ > + ( VIRTIO_NET_F_CSUM | \ > + VIRTIO_NET_F_MAC | \ > + VIRTIO_NET_F_GSO | \ > + VIRTIO_NET_F_HOST_TSO4 | \ > + VIRTIO_NET_F_HOST_TSO6 | \ > + VIRTIO_NET_F_HOST_ECN | \ > + VIRTIO_NET_F_HOST_UFO ) > + > +#define VTNET_RX_MIN_SEGS 2 > +#define VTNET_RX_PROCESS_LIMIT 512 > +#define VTNET_TX_TIMEOUT 5 > +#define VTNET_INTR_DISABLE_RETRIES 4 > + > +#define VTNET_DAEMON_STKSIZE 4096 > + > +struct virtio_net_hdr { > + uint8_t flags; > + uint8_t gso_type; > + uint16_t hdr_len; > + uint16_t gso_size; > + uint16_t csum_start; > + uint16_t csum_offset; > +}; > + > +struct vtnet_tx_header { > + struct virtio_net_hdr hdr; > + struct mbuf *vth_mbuf; > +}; > + > +struct vtnet_softc { > + struct arpcom arpcom; > + > + struct vtpci_softc vtpci_softc; > + struct virtqueue *vtnet_rxvq; > + struct virtqueue *vtnet_txvq; > + > + rtems_id daemonTid; > + > + struct virtio_net_hdr vtnet_net_hdr; > + int vtnet_rxvq_size; > + int vtnet_txvq_size; > + > + int vtnet_link_active; > + int vtnet_hdr_size; > + int vtnet_rx_clsize; > + int vtnet_tx_watchdog; > + > + enum { vtnet_timeout_stopped, vtnet_timeout_running, > vtnet_timeout_stop_rq } > + stat_ch; > +}; > + > +static int vtnet_alloc_virtqueues( struct vtnet_softc *sc ); > +static int vtnet_alloc_virtqueue( > + struct virtqueue *vq, > + uint16_t num, > + uint16_t queue_size > +); > + > +static int vtnet_setup_interface( > + struct vtnet_softc *sc, > + struct rtems_bsdnet_ifconfig *config > +); > +static void vtnet_init( void *xsc ); > +static void vtnet_negotiate_features( struct vtnet_softc *sc ); > +static int vtnet_init_virtqueue( > + struct vtnet_softc *sc, > + struct virtqueue *vq > +); > +static void vtnet_start( struct ifnet *ifp ); > +static int vtnet_ioctl( > + struct ifnet *ifp, > + ioctl_command_t cmd, > + caddr_t data > +); > + > +static int vtnet_setup_intr( struct vtpci_softc *sc ); > +static rtems_isr vtnet_intr( rtems_vector_number v ); > +static void vtnet_daemon( void *xsc ); > + > +static void vtnet_tick( void *xsc ); > +static int vtnet_watchdog( struct vtnet_softc *sc ); > +static void vtnet_callout_stop( struct vtnet_softc *sc ); > +static void vtnet_callout_reset( struct vtnet_softc *sc ); > + > +static int vtnet_rxq_populate( struct vtnet_softc *sc ); > +static int vtnet_rxq_eof( struct vtnet_softc *sc ); > +static int vtnet_rxq_enqueue( > + struct vtnet_softc *sc, > + struct mbuf *m_head > +); > +static int vtnet_rxq_replace( > + struct vtnet_softc *sc, > + struct mbuf *m, > + int len > +); > +static struct mbuf *vtnet_rxq_alloc_mbuf( > + struct vtnet_softc *sc, > + int nbufs, > + struct mbuf **m_tailp > +); > +static int vtnet_rxq_replace_nomgr( > + struct vtnet_softc *sc, > + struct mbuf *m0, > + int len0 > +); > + > +static void vtnet_txq_eof( struct vtnet_softc *sc ); > +static int vtnet_txq_enqueue( > + struct vtnet_softc *sc, > + struct mbuf *m_head > +); > + > +#endif /* _IF_VTNET_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..85251fa > --- /dev/null > +++ b/c/src/lib/libbsp/i386/pc386/virtio/virtio.c > @@ -0,0 +1,220 @@ > +/** > + * @file virtio.c > + * @brief Functions for virtqueue, vring > + */ > + > +/* > + * Copyright (c) 2016 Jin-Hyun Kim <jinh...@konkuk.ac.kr> > + * and Hyun-Wook Jin <j...@konkuk.ac.kr> > + * Ported from FreeBSD to RTEMS March 16, http://sslab.konkuk.ac.kr > + * > + * 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. > + */ > + > +/*- > + * Copyright (c) 2011, Bryan Venteicher <bry...@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. > + */ > + > +#include <rtems.h> > + > +#include "virtio.h" > + > +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(); > + > + 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; > + vq->vq_descx[ desc_idx ].cookie = NULL; > + > + return cookie; > +} > + > +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; > + vq_ring_free_chain( vq, idx ); > + } > + > + idx++; > + } > + > + *last = idx; > + > + return cookie; > +} > + > +void virtqueue_disable_intr( struct virtqueue *vq ) > +{ > + vq->vq_ring.avail->flags |= VRING_AVAIL_F_NO_INTERRUPT; > +} > + > +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 ); > +} > + > +void vq_ring_init( struct virtqueue *vq ) > +{ > + struct vring *vr; > + uint8_t *ring_mem; > + int i, size; > + > + ring_mem = vq->vq_ring_mem; > + size = vq->vq_nentries; > + vr = &vq->vq_ring; > + > + vr->num = size; > + vr->desc = (struct vring_desc *) ring_mem; > + vr->avail = > + (struct vring_avail *) ( ring_mem + size * sizeof( struct vring_desc ) > ); > + vr->used = (void *) > + ( ( (unsigned long) &vr->avail->ring[ size ] + > vq->vq_alignment - > + 1 ) & ~( vq->vq_alignment - 1 ) ); > + > + for ( i = 0; i < size - 1; i++ ) { > + vr->desc[ i ].next = i + 1; > + } > + > + vr->desc[ i ].next = VQ_RING_DESC_CHAIN_END; > +} > + > +void vq_ring_free_chain( > + struct virtqueue *vq, > + uint16_t desc_idx > +) > +{ > + struct vring_desc *dp; > + struct vq_desc_extra *dxp; > + > + dp = &vq->vq_ring.desc[ desc_idx ]; > + dxp = &vq->vq_descx[ desc_idx ]; > + > + vq->vq_free_cnt += dxp->ndescs; > + dxp->ndescs--; > + > + while ( dp->flags & VRING_DESC_F_NEXT ) { > + dp = &vq->vq_ring.desc[ dp->next ]; > + dxp->ndescs--; > + } > + > + dp->next = vq->vq_desc_head_idx; > + vq->vq_desc_head_idx = desc_idx; > +} > + > +void vq_ring_update_avail( > + struct virtqueue *vq, > + uint16_t desc_idx > +) > +{ > + uint16_t avail_idx; > + > + 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++; > + vq->vq_queued_cnt++; > +} > + > +int vq_ring_must_notify_host( struct virtqueue *vq ) > +{ > + return ( ( vq->vq_ring.used->flags & VRING_USED_F_NO_NOTIFY ) == 0 ); > +} > + > +int vq_ring_enable_interrupt( > + struct virtqueue *vq, > + uint16_t ndesc > +) > +{ > + uint16_t used_idx, nused; > + > + vq->vq_ring.avail->flags &= ~VRING_AVAIL_F_NO_INTERRUPT; > + > + mb(); > + > + used_idx = vq->vq_ring.used->idx; > + nused = (uint16_t) ( used_idx - vq->vq_used_cons_idx ); > + > + if ( nused > ndesc ) { > + return 1; > + } > + > + return 0; > +} > 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..47a61e8 > --- /dev/null > +++ b/c/src/lib/libbsp/i386/pc386/virtio/virtio.h > @@ -0,0 +1,167 @@ > +/** > + * @file virtio.h > + * @brief Header for virtio.c > + */ > + > +/* > + * Copyright (c) 2016 Jin-Hyun Kim <jinh...@konkuk.ac.kr> > + * and Hyun-Wook Jin <j...@konkuk.ac.kr> > + * Ported from FreeBSD to RTEMS March 16, http://sslab.konkuk.ac.kr > + * > + * 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. > + */ > + > +/*- > + * 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_ > + > +#define mb() asm volatile ( "lock; addl $0,0(%%esp) " ::: "memory" ); > +#define rmb() mb() > +#define wmb() asm volatile ( "lock; addl $0, (%%esp)" ::: "memory", "cc" ) > + > +#define vring_used_event( vr ) ( ( vr )->avail->ring[ ( vr )->num ] ) > +#define vring_avail_event( vr ) ( *(uint16_t *) &( vr )->used->ring[ ( vr > )-> \ > + num ] > ) > +#define vring_need_event( event_idx, new_idx, old ) \ > + ( (uint16_t) ( new_idx - event_idx - 1 ) < (uint16_t) ( new_idx - old ) > ) > + > +/* VirtIO PCI vendor/device ID. */ > +#define VIRTIO_VENDOR_ID 0x1AF4 > +#define VIRTIO_DEVICE_ID_MIN 0x1000 > +#define VIRTIO_DEVICE_ID_MAX 0x1040 > + > +#define VRING_DESC_F_NEXT 1 > +#define VRING_DESC_F_WRITE 2 > +#define VRING_DESC_F_INDIRECT 4 > +#define VRING_USED_F_NO_NOTIFY 1 > +#define VRING_AVAIL_F_NO_INTERRUPT 1 > + > +#define VQ_RING_DESC_CHAIN_END 32768 > + > +typedef enum { > + VQ_POSTPONE_SHORT, > + VQ_POSTPONE_LONG, > + VQ_POSTPONE_EMPTIED > +} vq_postpone_t; > + > +struct vring_desc { > + uint64_t addr; > + uint32_t len; > + uint16_t flags; > + uint16_t next; > +}; > + > +struct vring_avail { > + uint16_t flags; > + uint16_t idx; > + uint16_t ring[]; > +}; > + > +struct vring_used_elem { > + uint32_t id; > + uint32_t len; > +}; > + > +struct vring_used { > + uint16_t flags; > + uint16_t idx; > + struct vring_used_elem ring[]; > +}; > + > +struct vring { > + unsigned int num; > + struct vring_desc *desc; > + struct vring_avail *avail; > + struct vring_used *used; > +}; > + > +struct vq_desc_extra { > + void *cookie; > + struct vring_desc *indirect; > + uint32_t indirect_paddr; > + uint16_t ndescs; > +}; > + > +struct virtqueue { > + uint16_t vq_queue_index; > + uint16_t vq_nentries; > + uint32_t vq_flags; > + > + uint16_t vq_free_cnt; > + uint16_t vq_queued_cnt; > + > + int vq_alignment; > + int vq_ring_size; > + > + struct vring vq_ring; > + void *vq_ring_mem; > + int vq_max_indirect_size; > + int vq_indirect_mem_size; > + > + uint16_t vq_desc_head_idx; > + uint16_t vq_used_cons_idx; > + > + struct vq_desc_extra vq_descx[ 0 ]; > +}; > + > +void *virtqueue_dequeue( > + struct virtqueue *vq, > + uint32_t *len > +); > +void *virtqueue_drain( > + struct virtqueue *vq, > + int *last > +); > +void virtqueue_disable_intr( struct virtqueue *vq ); > +int virtqueue_postpone_intr( > + struct virtqueue *vq, > + vq_postpone_t hint > +); > + > +void vq_ring_init( struct virtqueue *vq ); > +void vq_ring_free_chain( > + struct virtqueue *vq, > + uint16_t desc_idx > +); > +void vq_ring_update_avail( > + struct virtqueue *vq, > + uint16_t desc_idx > +); > +int vq_ring_must_notify_host( struct virtqueue *vq ); > +int vq_ring_enable_interrupt( > + struct virtqueue *vq, > + uint16_t ndesc > +); > + > +#endif /* _VIRTIO_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..8ba9365 > --- /dev/null > +++ b/c/src/lib/libbsp/i386/pc386/virtio/virtio_pci.c > @@ -0,0 +1,176 @@ > +/** > + * @file virtio_pci.c > + * @brief Driver for the virtio PCI interface > + */ > + > +/* > + * Copyright (c) 2016 Jin-Hyun Kim <jinh...@konkuk.ac.kr> > + * and Hyun-Wook Jin <j...@konkuk.ac.kr> > + * Ported from FreeBSD to RTEMS March 16, http://sslab.konkuk.ac.kr > + * > + * 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. > + */ > + > +/*- > + * Copyright (c) 2011, Bryan Venteicher <bry...@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. > + */ > + > +/* Driver for the VirtIO PCI interface. */ > + > +#ifdef __i386__ > + > +#include <rtems.h> > +#include <rtems/rtems_bsdnet.h> > +#include <bsp.h> > + > +#include <pcibios.h> > + > +#include "virtio.h" > +#include "virtio_pci.h" > + > +__inline uint8_t vtpci_io_read_1( > + struct vtpci_softc *sc, > + int reg > +) > +{ > + uint8_t val; > + > + inport_byte( sc->pci_io_base + reg, val ); > + > + return val; > +} > +__inline uint16_t vtpci_io_read_2( > + struct vtpci_softc *sc, > + int reg > +) > +{ > + uint16_t val; > + > + inport_word( sc->pci_io_base + reg, val ); > + > + return val; > +} > +__inline uint32_t vtpci_io_read_4( > + struct vtpci_softc *sc, > + int reg > +) > +{ > + uint32_t val; > + > + inport_long( sc->pci_io_base + reg, val ); > + > + return val; > +} > +__inline void vtpci_io_write_1( > + struct vtpci_softc *sc, > + int reg, > + uint8_t val > +) > +{ > + outport_byte( sc->pci_io_base + reg, val ); > +} > +__inline void vtpci_io_write_2( > + struct vtpci_softc *sc, > + int reg, > + uint16_t val > +) > +{ > + outport_word( sc->pci_io_base + reg, val ); > +} > +__inline void vtpci_io_write_4( > + struct vtpci_softc *sc, > + int reg, > + uint32_t val > +) > +{ > + outport_long( sc->pci_io_base + reg, val ); > +} > + > +int vtpci_attach( > + struct rtems_bsdnet_ifconfig *config, > + struct vtpci_softc *sc > +) > +{ > + int i, ret; > + uint8_t val8; > + uint16_t val16; > + uint32_t val32; > + > + /* 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_DEVICE_ID_MIN; i < VIRTIO_DEVICE_ID_MAX; i++ ) { > + ret = pci_find_device( VIRTIO_VENDOR_ID, i, sc->unit_number, > + &pbus, &pdev, &pfun ); > + > + if ( ret == PCIB_ERR_SUCCESS ) { > + sc->pci_signature = PCIB_DEVSIG_MAKE( pbus, pdev, pfun ); > + break; > + } > + } > + } > + > + /* Enable bus matering */ > + pcib_conf_read16( sc->pci_signature, PCI_COMMAND, &val16 ); > + val16 |= PCI_COMMAND_MASTER; > + pcib_conf_write16( sc->pci_signature, PCI_COMMAND, val16 ); > + > + /* Set latency timer */ > + pcib_conf_read8( sc->pci_signature, PCI_LATENCY_TIMER, &val8 ); > + val8 |= 0x00; > + pcib_conf_write8( sc->pci_signature, PCI_LATENCY_TIMER, val8 ); > + > + /* 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; > + > + return 0; > +} > + > +void vtpci_notify( > + struct vtpci_softc *sc, > + struct virtqueue *vq > +) > +{ > + mb(); > + > + if ( vq_ring_must_notify_host( vq ) ) { > + vtpci_io_write_2( sc, VIRTIO_PCI_QUEUE_NOTIFY, vq->vq_queue_index ); > + } > + > + vq->vq_queued_cnt = 0; > +} > + > +#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..7d0db9e > --- /dev/null > +++ b/c/src/lib/libbsp/i386/pc386/virtio/virtio_pci.h > @@ -0,0 +1,131 @@ > +/** > + * @file virtio_pci.h > + * @brief Header for virtio_pci.c > + */ > + > +/* > + * Copyright (c) 2016 Jin-Hyun Kim <jinh...@konkuk.ac.kr> > + * and Hyun-Wook Jin <j...@konkuk.ac.kr> > + * Ported from FreeBSD to RTEMS March 16, http://sslab.konkuk.ac.kr > + * > + * 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. > + */ > + > +/*- > + * Copyright IBM Corp. 2007 > + * > + * Authors: > + * Anthony Liguori <aligu...@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_ > + > +/* 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 > */ > +#define VIRTIO_MSI_CONFIG_VECTOR 20 > +#define VIRTIO_MSI_QUEUE_VECTOR 22 > + > +#define VIRTIO_PCI_QUEUE_SEL_RX 0 > +#define VIRTIO_PCI_QUEUE_SEL_TX 1 > + > +#define VIRTIO_PCI_ISR_INTR 0x1 > +#define VIRTIO_PCI_ISR_CONFIG 0x2 > + > +#define VIRTIO_MSI_NO_VECTOR 0xFFFF > + > +/* How many bits to shift physical queue address written to QUEUE_PFN */ > +#define VIRTIO_PCI_QUEUE_ADDR_SHIFT 12 > + > +/* The alignment to use between consumer and producer parts of vring */ > +#define VIRTIO_PCI_VRING_ALIGN 4096 > + > +/* 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 > + > +struct vtpci_softc { > + int unit_number; > + char *unit_name; > + > + int pci_signature; > + uint32_t pci_io_base; > +}; > + > +__inline uint8_t vtpci_io_read_1( > + struct vtpci_softc *sc, > + int reg > +); > +__inline uint16_t vtpci_io_read_2( > + struct vtpci_softc *sc, > + int reg > +); > +__inline uint32_t vtpci_io_read_4( > + struct vtpci_softc *sc, > + int reg > +); > +__inline void vtpci_io_write_1( > + struct vtpci_softc *sc, > + int reg, > + uint8_t val > +); > +__inline void vtpci_io_write_2( > + struct vtpci_softc *sc, > + int reg, > + uint16_t val > +); > +__inline void vtpci_io_write_4( > + struct vtpci_softc *sc, > + int reg, > + uint32_t val > +); > +int vtpci_attach( > + struct rtems_bsdnet_ifconfig *config, > + struct vtpci_softc *sc > +); > +void vtpci_notify( > + struct vtpci_softc *sc, > + struct virtqueue *vq > +); > + > +#endif /* _VIRTIO_PCI_H_ */ > > > > _______________________________________________ > devel mailing list > devel@rtems.org > http://lists.rtems.org/mailman/listinfo/devel >
_______________________________________________ devel mailing list devel@rtems.org http://lists.rtems.org/mailman/listinfo/devel