From a1e516f2340560f2cb38ab95ec5f872a5c1ed7a8 Mon Sep 17 00:00:00 2001 From: Till Straumann Date: Fri, 11 Sep 2009 22:53:25 +0000 Subject: 2009-09-11 Till Straumann * Makefile.am, bsp.h, network/if_sim.c, network/README: added driver for NIC emulation. --- c/src/lib/libbsp/powerpc/psim/ChangeLog | 5 + c/src/lib/libbsp/powerpc/psim/Makefile.am | 2 + c/src/lib/libbsp/powerpc/psim/include/bsp.h | 8 + c/src/lib/libbsp/powerpc/psim/network/README | 141 +++++++ c/src/lib/libbsp/powerpc/psim/network/if_sim.c | 505 +++++++++++++++++++++++++ 5 files changed, 661 insertions(+) create mode 100644 c/src/lib/libbsp/powerpc/psim/network/README create mode 100644 c/src/lib/libbsp/powerpc/psim/network/if_sim.c (limited to 'c') diff --git a/c/src/lib/libbsp/powerpc/psim/ChangeLog b/c/src/lib/libbsp/powerpc/psim/ChangeLog index 863cbf180d..9f17670985 100644 --- a/c/src/lib/libbsp/powerpc/psim/ChangeLog +++ b/c/src/lib/libbsp/powerpc/psim/ChangeLog @@ -1,3 +1,8 @@ +2009-09-11 Till Straumann + + * Makefile.am, bsp.h, network/if_sim.c, network/README: + added driver for NIC emulation. + 2009-09-11 Till Straumann * Makefile.am, preinstall.am, irq/no_pic.c (REMOVED), diff --git a/c/src/lib/libbsp/powerpc/psim/Makefile.am b/c/src/lib/libbsp/powerpc/psim/Makefile.am index b2b97965d8..face282e53 100644 --- a/c/src/lib/libbsp/powerpc/psim/Makefile.am +++ b/c/src/lib/libbsp/powerpc/psim/Makefile.am @@ -69,6 +69,8 @@ libbsp_a_SOURCES += \ shmsupp/addrconv.c shmsupp/getcfg.c shmsupp/lock.c shmsupp/mpisr.c endif +libbsp_a_SOURCES += network/if_sim.c + libbsp_a_LIBADD = ../../../libcpu/@RTEMS_CPU@/shared/cpuIdent.rel \ ../../../libcpu/@RTEMS_CPU@/shared/stack.rel \ ../../../libcpu/@RTEMS_CPU@/@exceptions@/rtems-cpu.rel \ diff --git a/c/src/lib/libbsp/powerpc/psim/include/bsp.h b/c/src/lib/libbsp/powerpc/psim/include/bsp.h index 3960df6c9a..0add0ebd2c 100644 --- a/c/src/lib/libbsp/powerpc/psim/include/bsp.h +++ b/c/src/lib/libbsp/powerpc/psim/include/bsp.h @@ -67,6 +67,14 @@ extern uint32_t BSP_mem_size; #define Processor_Synchronize() \ asm(" eieio ") +struct rtems_bsdnet_ifconfig; + +int +rtems_ifsim_attach(struct rtems_bsdnet_ifconfig *ifcfg, int attaching); + +#define RTEMS_BSP_NETWORK_DRIVER_NAME "ifsim1" +#define RTEMS_BSP_NETWORK_DRIVER_ATTACH rtems_ifsim_attach + #endif /* ASM */ #define BSP_HAS_NO_VME diff --git a/c/src/lib/libbsp/powerpc/psim/network/README b/c/src/lib/libbsp/powerpc/psim/network/README new file mode 100644 index 0000000000..4ca287788c --- /dev/null +++ b/c/src/lib/libbsp/powerpc/psim/network/README @@ -0,0 +1,141 @@ +PSIM NETWORKING HOWTO +===================== + +IMPLEMENTATION INFORMATION + +1) NIC hardware emulation. + + In order to simulate networking the simulator (PSIM in our case) + has to emulate some networking hardware. + At the time of this writing (2009/09 - gdb-6.8) no such emulation + is available. + However, a patch has been created which adds this functionality + to PSIM (see diff in this directory). Unfortunately, implementing + a network chip (or some other sort of 'data-source') in PSIM is + not quite that simple since PSIM is at the end of the day a single- + threaded, monolithic application which has no built-in support for + external asynchronous events (e.g., NIC packet reception or a + character arriving at a UART [the PSIM BSP uses a polled console + driver]). + In order to add such asynchronous support, the 'async_io' module + was added to PSIM. 'async_io' uses OS-support in the form of + a signal (SIGIO) that the OS sends to the PSIM process when I/O + becomes possible. The signal handler then executes a callback + which e.g., may schedule a PSIM interrupt. + However, the use of SIGIO and the O_ASYNC fcntl-flag is not portable + (BSD and linux only). + The 'ethtap' NIC emulation uses another not quite portable OS + service -- the host OS' TUN/TAP device which is some sort of + pipe with a networking-interface on one end and a file-system + interface on the other. The 'ethtap' NIC reads/writes packets + to the file-system interface and they then become available + to the host OS' networking. + This ascii-art shows how a RTEMS application inside PSIM can + communicate with an application on the host with both using + sockets. (If the host sets up proper routing table entries + then the RTEMS APP can even communicate with the internet...) + + + RTEMS APP HOST APP + | | + ............. ............ + . . . . + .RTEMS BSD . . HOST OS . + .networking . .networking. + ............. ............ + + ....................... ..................... + .RTEMS BSD IF "ifsim1". . HOST OS IF: "tap0". + .e.g., 10.0.0.100 . . e.g., 10.0.0.1 . + ....................... ..................... + o + ............. o + .RTEMS ifsim. o + . driver . o + ............. o + || o + || o + --------------- o + ethtap o + hw emulation o + --------------- o + ^ o + | o + -----> /dev/net/tun (special file on host OS) ooo + + +2) Device-tree. Once PSIM supports the 'ethtap' device then it + must be added to the device tree. The following properties are + relevant (The register addresses must match with what the + BSP/if_sim expects): + + #### ETHTAP @ 0x0c100020 for 0x40 + # + + /ethtap@0x0c100020/reg 0x0c100020 0x40 + # route interrupt to open-pic + /ethtap@0x0c100020 > 0 irq0 /opic@0x0c130000 + # 'tun' device on host + /ethtap@0x0c100020/tun-device "/dev/net/tun" + # name of 'tap' device to use + /ethtap@0x0c100020/tap-ifname "tap0" + # ethernet address of simulated IF + /ethtap@0x0c100020/hw-address "00:00:00:22:11:00" + # generate CRC and append to received packet before + # handing over to the simulation. This is mostly for + # debugging the rtems device driver. If unsure, leave 'false'. + /ethtap@0x0c100020/enable-crc false + + The 'tun-device' and 'tap-ifname' properties allow you to + configure the name of the special-file and the 'tap' interface + on the host. + +3) RTEMS driver. The 'if_sim' driver implements a driver for + the 'ethtap' device. + +USAGE INFORMATION + +1) Configure application for networking; the + RTEMS_BSP_NETWORK_DRIVER_NAME is "ifsim1" + and + RTEMS_BSP_NETWORK_DRIVER_ATTACH is rtems_ifsim_attach + +2) Patch, configure (--target=powerpc-rtems) and build + gdb-6.8. As already mentioned, the NIC emulation only + is available if your host-os is linux. + +3) Create a 'device-tree' file. The BSP build process produces + a shell-script 'psim' residing in + + /powerpc-rtems/psim/tests/ + + which can be used for generating a device-tree file. + + Call 'psim -d -n '. The '-n' option adds the + emulated interface (the lines above) to the device tree. + The resulting file is saved as .device. + + The 'psim' script can also be used to launch an application + directly -- just omit the '-d' option. + +4) Linux host network configuration: + Create a 'permanent' 'tap' interface. This allows you + to use 'psim' w/o special privileges (the 'tunctl' command + still must be executed by the super-user). + + sudo tunctl -u + + You now can configure the 'tap0' interface: + + sudo ifconfig tap0 10.0.0.1 up + + and e.g., run a BOOTP server to provide RTEMS with its + network configuration: + + sudo dhcpd3 -d tap0 + + Assuming that BOOTP gives the RTEMS guest an IP address + e.g., '10.0.0.100' you can 'ping' the RTEMS guest + from the linux host: + + ping 10.0.0.100 diff --git a/c/src/lib/libbsp/powerpc/psim/network/if_sim.c b/c/src/lib/libbsp/powerpc/psim/network/if_sim.c new file mode 100644 index 0000000000..58f35396fc --- /dev/null +++ b/c/src/lib/libbsp/powerpc/psim/network/if_sim.c @@ -0,0 +1,505 @@ +/* $Id$ */ + +/* Trivial driver for PSIM's emulated ethernet device 'hw_ethtap'. + * + * NOTE: At the time of this writing (2009/1) 'hw_ethtap' requires + * a patched version of PSIM -- the vanilla version does not + * implement this device. + * Also, support for this device is currently only available + * on a linux host. + * + * Author/Copyright: Till Straumann + * + * LICENSE + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + */ + +#include +#include +#include +#include +#include +#include + + +#ifndef KERNEL +#define KERNEL +#endif +#ifndef _KERNEL +#define _KERNEL +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define IFSIM_SLOTS 1 + +#define DRVNAME "if_sim" + +#define IFSIM_TX_BUF_REG 1 +#define IFSIM_TX_STA_REG 2 + +#define IFSIM_RX_BUF_REG 4 +#define IFSIM_RX_STA_REG 5 + +#define IFSIM_RX_CNT_MSK 0xffff +#define IFSIM_RX_STA_DONE (1<<31) + +#define IFSIM_RX_ERR_NOSPC (1<<20) /* buffer too small */ +#define IFSIM_RX_ERR_DMA (1<<21) /* DMA error */ +#define IFSIM_RX_ERR_RD (1<<23) /* file read error */ + +#define IFSIM_TX_STA_LST (1<<16) +#define IFSIM_TX_STA_DONE (1<<31) + +#define IFSIM_IEN_REG 6 +#define IFSIM_IRQ_REG 7 + +#define IFSIM_RX_IRQ (1<<0) + +#define IFSIM_MACA0_REG 8 +#define IFSIM_MACA1_REG 9 + +#define IFSIM_CSR_REG 10 +#define IFSIM_CSR_PROM (1<<0) +/* Enable CRC generation/checking */ +#define IFSIM_CSR_CRCEN (1<<1) + + +#define RX_BUF_ALIGNMENT 1 +#define ETH_RX_OFFSET 0 + +/* + * Align 'p' up to a multiple of 'a' which must be + * a power of two. Result is cast to (uintptr_t) + */ +#define ALIGNTO(p,a) ((((uintptr_t)(p)) + (a) - 1) & ~((a)-1)) + + +typedef volatile unsigned reg_t; + +struct ifsim_private { + reg_t *base; + void *rbuf; + unsigned rx_cserrs; + unsigned rx_err_nospc; + unsigned rx_err_dma; + unsigned rx_err_rd; + unsigned rx_nobufs; + unsigned rx_irqs; +}; + +struct ifsim_softc { + struct arpcom arpcom; + struct ifsim_private pvt; +}; + +struct ifsim_softc theIfSims[IFSIM_SLOTS] = {{{{0}}} }; + +rtems_id ifsim_tid = 0; + +__inline__ uint32_t +ifsim_in(struct ifsim_softc *sc, unsigned regno) +{ + return in_be32( sc->pvt.base + regno ); +} + +__inline__ void +ifsim_out(struct ifsim_softc *sc, unsigned regno, uint32_t v) +{ + out_be32(sc->pvt.base + regno, v); +} + +static void * +alloc_mbuf_rx(int *psz, uintptr_t *paddr) +{ +struct mbuf *m; +unsigned long l,o; + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if ( !m ) + return 0; + MCLGET(m, M_DONTWAIT); + if ( ! (m->m_flags & M_EXT) ) { + m_freem(m); + return 0; + } + + o = mtod(m, unsigned long); + l = ALIGNTO(o, RX_BUF_ALIGNMENT) - o; + + /* align start of buffer */ + m->m_data += l; + + /* reduced length */ + l = MCLBYTES - l; + + m->m_len = m->m_pkthdr.len = l; + *psz = m->m_len; + *paddr = mtod(m, unsigned long); + + return (void*) m; +} + +static int +get_rxbuf(struct ifsim_softc *sc) +{ +int sz; +uintptr_t addr; +void *nbuf; + + nbuf = alloc_mbuf_rx(&sz, &addr); + + if ( nbuf ) { + sc->pvt.rbuf = nbuf; + ifsim_out(sc, IFSIM_RX_BUF_REG, addr); + ifsim_out(sc, IFSIM_RX_STA_REG, sz); + return 0; + } + return -1; +} + +/* set/clear promiscuous mode */ +static void +ifsim_upd_promisc(struct ifsim_softc *sc) +{ +uint32_t ncsr, csr; + + ncsr = csr = ifsim_in(sc, IFSIM_CSR_REG); + + if ( sc->arpcom.ac_if.if_flags & IFF_PROMISC ) + ncsr |= IFSIM_CSR_PROM; + else + ncsr &= ~IFSIM_CSR_PROM; + + if ( ncsr != csr ) + ifsim_out(sc, IFSIM_CSR_REG, ncsr); +} + +static void +ifsim_init(void *arg) +{ +struct ifsim_softc *sc = arg; +struct ifnet *ifp = &sc->arpcom.ac_if; + + if ( 0 == get_rxbuf(sc) ) { + ifsim_upd_promisc(sc); + ifp->if_flags |= IFF_RUNNING; + } +} + +static void +ifsim_start(struct ifnet *ifp) +{ +struct ifsim_softc *sc = ifp->if_softc; +struct mbuf *m, *mh, *m1; + + while ( ifp->if_snd.ifq_head ) { + IF_DEQUEUE( &ifp->if_snd, mh ); + for ( m=mh, m1 = m->m_next ; m1 ; m1 = m1->m_next ) { + ifsim_out(sc, IFSIM_TX_BUF_REG, mtod(m, uint32_t)); + ifsim_out(sc, IFSIM_TX_STA_REG, m->m_len); + /* dummy-busywait; the emulated hardware DMAs our + * data away 'immediately' i.e., this loop is + * never executed + */ + while ( ! (IFSIM_TX_STA_DONE & ifsim_in(sc, IFSIM_TX_STA_REG)) ) + /* Loop */; + m = m1; + } + ifsim_out(sc, IFSIM_TX_BUF_REG, mtod(m, uint32_t)); + ifsim_out(sc, IFSIM_TX_STA_REG, m->m_len | IFSIM_TX_STA_LST); + + /* dummy-busywait; the emulated hardware DMAs our + * data away 'immediately' i.e., this loop is + * never executed + */ + while ( ! (IFSIM_TX_STA_DONE & ifsim_in(sc, IFSIM_TX_STA_REG)) ) + /* Loop */; + + m_freem(mh); + } +} + +static int +ifsim_ioctl(struct ifnet *ifp, ioctl_command_t cmd, caddr_t data) +{ +struct ifsim_softc *sc = ifp->if_softc; +int rval = 0; +int f; + + switch (cmd) { + + case SIOCSIFFLAGS: + f = ifp->if_flags; + if ( f & IFF_UP ) { + if ( ! (f & IFF_RUNNING) ) { + ifsim_init(sc); + } else { + ifsim_upd_promisc(sc); + } + /* FIXME: handle other flags here */ + } else { + if ( f & IFF_RUNNING ) { + printk("WARNING: bringing "DRVNAME" down not really implemented\n"); + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + } + } + + break; + + case SIO_RTEMS_SHOW_STATS: + printf("RX bad chksum : %u\n", sc->pvt.rx_cserrs); + printf("RX no space : %u\n", sc->pvt.rx_err_nospc); + printf("RX bad DMA : %u\n", sc->pvt.rx_err_dma); + printf("RX read errors : %u\n", sc->pvt.rx_err_rd); + printf("rx_nobufs : %u\n", sc->pvt.rx_nobufs); + printf("rx_irqs : %u\n", sc->pvt.rx_irqs); + break; + + default: + rval = ether_ioctl(ifp, cmd, data); + break; + } + + return rval; +} + +static void ifsim_irq_on(const rtems_irq_connect_data *pd) +{ +struct ifsim_softc *sc = pd->handle; + ifsim_out(sc, IFSIM_IEN_REG, IFSIM_RX_IRQ); +} + +static void ifsim_irq_off(const rtems_irq_connect_data *pd) +{ +struct ifsim_softc *sc = pd->handle; + ifsim_out(sc, IFSIM_IEN_REG, 0); +} + +static int ifsim_irq_ison(const rtems_irq_connect_data *pd) +{ +struct ifsim_softc *sc = pd->handle; + return ifsim_in(sc, IFSIM_IEN_REG) & IFSIM_RX_IRQ ? 1 : 0; +} + +static void +ifsim_isr(void *arg) +{ +struct ifsim_softc *sc = arg; + + sc->pvt.rx_irqs++; +#ifdef IRQ_DEBUG + printk("ISR happened\n"); +#endif + + ifsim_out(sc, IFSIM_IEN_REG, 0); + rtems_event_send(ifsim_tid, (1<<(sc-theIfSims))); +} + +static void +ifsim_task(void *arg) +{ +struct ifsim_softc *sc; +uint32_t sta; +struct ifnet *ifp; +unsigned len; +rtems_event_set evs; + + while (1) { + + rtems_bsdnet_event_receive( + ((1<>=1, sc++ ) { + + if ( ! ( evs & 1 ) ) + continue; + + ifp = &sc->arpcom.ac_if; + + while ( ifsim_in(sc, IFSIM_IRQ_REG) & IFSIM_RX_IRQ ) { + struct mbuf *m = sc->pvt.rbuf; + + sta = ifsim_in(sc, IFSIM_RX_STA_REG); + + + if ( (sta & IFSIM_RX_STA_DONE) ) { + + if ( (ifp->if_flags & IFF_RUNNING) ) { + if ( 0 == get_rxbuf(sc) ) { + /* enqueue packet */ + struct ether_header *eh; + uint32_t crc_net, crc; + int crc_len; + + crc_len = (IFSIM_CSR_CRCEN & ifsim_in(sc, IFSIM_CSR_REG)) ? sizeof(crc_net) : 0; + + len = (sta & IFSIM_RX_CNT_MSK) - crc_len; + + eh = (struct ether_header*)(mtod(m,unsigned long) + ETH_RX_OFFSET); + + m->m_len = m->m_pkthdr.len = len - sizeof(struct ether_header) - ETH_RX_OFFSET; + m->m_data += sizeof(struct ether_header) + ETH_RX_OFFSET; + m->m_pkthdr.rcvif = ifp; + +#ifdef DEBUG_WO_BSDNET + { + int i; + for ( i=0; ipvt.rx_cserrs++; + } else { + + ifp->if_ipackets++; + ifp->if_ibytes += len; + +#ifdef DEBUG_WO_BSDNET + m_freem(m); +#else + ether_input(ifp, eh, m); +#endif + + m = 0; + } + + } else { + /* throw away and reuse buffer */ + sc->pvt.rx_nobufs++; + } + } + } else { + /* throw away and reuse buffer */ + if ( (sta & IFSIM_RX_ERR_NOSPC) ) + sc->pvt.rx_err_nospc++; + if ( (sta & IFSIM_RX_ERR_DMA) ) + sc->pvt.rx_err_dma++; + if ( (sta & IFSIM_RX_ERR_RD) ) + sc->pvt.rx_err_rd++; + } + + if ( m ) { + /* reuse */ + ifsim_out(sc, IFSIM_RX_STA_REG, m->m_pkthdr.len); + } + } + /* re-enable interrupt */ + ifsim_out(sc, IFSIM_IEN_REG, IFSIM_RX_IRQ); + } + } +} + +int +rtems_ifsim_attach(struct rtems_bsdnet_ifconfig *ifcfg, int attaching) +{ +char *unitName; +int unit; +struct ifsim_softc *sc; +struct ifnet *ifp; +uint32_t v; +rtems_irq_connect_data ihdl; + + if ( !attaching ) + return -1; + + unit = rtems_bsdnet_parse_driver_name(ifcfg, &unitName); + + if ( unit <= 0 || unit > IFSIM_SLOTS ) { + printk(DRVNAME": Bad unit number %i; must be 1..%i\n", unit, IFSIM_SLOTS); + return 1; + } + + sc = &theIfSims[unit-1]; + ifp = &sc->arpcom.ac_if; + + memset(&sc->pvt, 0, sizeof(sc->pvt)); + + sc->pvt.base = (reg_t*)ifcfg->port; + + if ( 0 == sc->pvt.base ) + sc->pvt.base = (reg_t*)PSIM.Ethtap; + + + sc->pvt.rbuf = 0; + + if ( !ifsim_tid ) { + ifsim_tid = rtems_bsdnet_newproc("IFSM", 10000, ifsim_task, 0); + } + + ihdl.name = ifcfg->irno; + ihdl.hdl = ifsim_isr; + ihdl.handle = sc; + ihdl.on = ifsim_irq_on; + ihdl.off = ifsim_irq_off; + ihdl.isOn = ifsim_irq_ison; + + if ( ! BSP_install_rtems_irq_handler(&ihdl) ) { + printk(DRVNAME"_attach(): installing ISR failed!\n"); + return -1; + } + + if ( ifcfg->hardware_address ) { + memcpy(sc->arpcom.ac_enaddr, ifcfg->hardware_address, ETHER_ADDR_LEN); + } else { + v = ifsim_in(sc, IFSIM_MACA0_REG); + memcpy(sc->arpcom.ac_enaddr, &v, 4); + v = ifsim_in(sc, IFSIM_MACA1_REG); + memcpy(sc->arpcom.ac_enaddr+4, &v, 2); + } + + ifp->if_softc = sc; + ifp->if_unit = unit; + ifp->if_name = unitName; + + ifp->if_mtu = ifcfg->mtu ? ifcfg->mtu : ETHERMTU; + + ifp->if_init = ifsim_init; + ifp->if_ioctl = ifsim_ioctl; + ifp->if_start = ifsim_start; + ifp->if_output = ether_output; + + ifp->if_watchdog = 0; + ifp->if_timer = 0; + + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX; + + ifp->if_snd.ifq_maxlen = ifqmaxlen; + + if_attach(ifp); + ether_ifattach(ifp); + + return 0; +} -- cgit v1.2.3