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