added NE2000 emulation


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@269 c046a42c-6fe2-441c-8c8c-71466251a162
diff --git a/vl.c b/vl.c
index 35d0081..b97f74f 100644
--- a/vl.c
+++ b/vl.c
@@ -21,11 +21,19 @@
 #include <termios.h>
 #include <sys/poll.h>
 #include <errno.h>
+#include <sys/wait.h>
+
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <linux/if.h>
+#include <linux/if_tun.h>
 
 #include "cpu-i386.h"
 #include "disas.h"
 
 #define DEBUG_LOGFILE "/tmp/vl.log"
+#define DEFAULT_NETWORK_SCRIPT "/etc/vl-ifup"
+
 //#define DEBUG_UNUSED_IOPORT
 
 #define PHYS_RAM_BASE 0xa8000000
@@ -225,6 +233,24 @@
     return 0;
 }
 
+int register_ioport_readw(int start, int length, IOPortReadFunc *func)
+{
+    int i;
+
+    for(i = start; i < start + length; i += 2)
+        ioport_readw_table[i] = func;
+    return 0;
+}
+
+int register_ioport_writew(int start, int length, IOPortWriteFunc *func)
+{
+    int i;
+
+    for(i = start; i < start + length; i += 2)
+        ioport_writew_table[i] = func;
+    return 0;
+}
+
 void pstrcpy(char *buf, int buf_size, const char *str)
 {
     int c;
@@ -1149,6 +1175,474 @@
     term_init();
 }
 
+/***********************************************************/
+/* ne2000 emulation */
+
+//#define DEBUG_NE2000
+
+#define NE2000_IOPORT   0x300
+#define NE2000_IRQ      9
+
+#define MAX_ETH_FRAME_SIZE 1514
+
+#define E8390_CMD	0x00  /* The command register (for all pages) */
+/* Page 0 register offsets. */
+#define EN0_CLDALO	0x01	/* Low byte of current local dma addr  RD */
+#define EN0_STARTPG	0x01	/* Starting page of ring bfr WR */
+#define EN0_CLDAHI	0x02	/* High byte of current local dma addr  RD */
+#define EN0_STOPPG	0x02	/* Ending page +1 of ring bfr WR */
+#define EN0_BOUNDARY	0x03	/* Boundary page of ring bfr RD WR */
+#define EN0_TSR		0x04	/* Transmit status reg RD */
+#define EN0_TPSR	0x04	/* Transmit starting page WR */
+#define EN0_NCR		0x05	/* Number of collision reg RD */
+#define EN0_TCNTLO	0x05	/* Low  byte of tx byte count WR */
+#define EN0_FIFO	0x06	/* FIFO RD */
+#define EN0_TCNTHI	0x06	/* High byte of tx byte count WR */
+#define EN0_ISR		0x07	/* Interrupt status reg RD WR */
+#define EN0_CRDALO	0x08	/* low byte of current remote dma address RD */
+#define EN0_RSARLO	0x08	/* Remote start address reg 0 */
+#define EN0_CRDAHI	0x09	/* high byte, current remote dma address RD */
+#define EN0_RSARHI	0x09	/* Remote start address reg 1 */
+#define EN0_RCNTLO	0x0a	/* Remote byte count reg WR */
+#define EN0_RCNTHI	0x0b	/* Remote byte count reg WR */
+#define EN0_RSR		0x0c	/* rx status reg RD */
+#define EN0_RXCR	0x0c	/* RX configuration reg WR */
+#define EN0_TXCR	0x0d	/* TX configuration reg WR */
+#define EN0_COUNTER0	0x0d	/* Rcv alignment error counter RD */
+#define EN0_DCFG	0x0e	/* Data configuration reg WR */
+#define EN0_COUNTER1	0x0e	/* Rcv CRC error counter RD */
+#define EN0_IMR		0x0f	/* Interrupt mask reg WR */
+#define EN0_COUNTER2	0x0f	/* Rcv missed frame error counter RD */
+
+#define EN1_PHYS        0x11
+#define EN1_CURPAG      0x17
+#define EN1_MULT        0x18
+
+/*  Register accessed at EN_CMD, the 8390 base addr.  */
+#define E8390_STOP	0x01	/* Stop and reset the chip */
+#define E8390_START	0x02	/* Start the chip, clear reset */
+#define E8390_TRANS	0x04	/* Transmit a frame */
+#define E8390_RREAD	0x08	/* Remote read */
+#define E8390_RWRITE	0x10	/* Remote write  */
+#define E8390_NODMA	0x20	/* Remote DMA */
+#define E8390_PAGE0	0x00	/* Select page chip registers */
+#define E8390_PAGE1	0x40	/* using the two high-order bits */
+#define E8390_PAGE2	0x80	/* Page 3 is invalid. */
+
+/* Bits in EN0_ISR - Interrupt status register */
+#define ENISR_RX	0x01	/* Receiver, no error */
+#define ENISR_TX	0x02	/* Transmitter, no error */
+#define ENISR_RX_ERR	0x04	/* Receiver, with error */
+#define ENISR_TX_ERR	0x08	/* Transmitter, with error */
+#define ENISR_OVER	0x10	/* Receiver overwrote the ring */
+#define ENISR_COUNTERS	0x20	/* Counters need emptying */
+#define ENISR_RDC	0x40	/* remote dma complete */
+#define ENISR_RESET	0x80	/* Reset completed */
+#define ENISR_ALL	0x3f	/* Interrupts we will enable */
+
+/* Bits in received packet status byte and EN0_RSR*/
+#define ENRSR_RXOK	0x01	/* Received a good packet */
+#define ENRSR_CRC	0x02	/* CRC error */
+#define ENRSR_FAE	0x04	/* frame alignment error */
+#define ENRSR_FO	0x08	/* FIFO overrun */
+#define ENRSR_MPA	0x10	/* missed pkt */
+#define ENRSR_PHY	0x20	/* physical/multicast address */
+#define ENRSR_DIS	0x40	/* receiver disable. set in monitor mode */
+#define ENRSR_DEF	0x80	/* deferring */
+
+/* Transmitted packet status, EN0_TSR. */
+#define ENTSR_PTX 0x01	/* Packet transmitted without error */
+#define ENTSR_ND  0x02	/* The transmit wasn't deferred. */
+#define ENTSR_COL 0x04	/* The transmit collided at least once. */
+#define ENTSR_ABT 0x08  /* The transmit collided 16 times, and was deferred. */
+#define ENTSR_CRS 0x10	/* The carrier sense was lost. */
+#define ENTSR_FU  0x20  /* A "FIFO underrun" occurred during transmit. */
+#define ENTSR_CDH 0x40	/* The collision detect "heartbeat" signal was lost. */
+#define ENTSR_OWC 0x80  /* There was an out-of-window collision. */
+
+#define NE2000_MEM_SIZE 32768
+
+typedef struct NE2000State {
+    uint8_t cmd;
+    uint32_t start;
+    uint32_t stop;
+    uint8_t boundary;
+    uint8_t tsr;
+    uint8_t tpsr;
+    uint16_t tcnt;
+    uint16_t rcnt;
+    uint32_t rsar;
+    uint8_t isr;
+    uint8_t dcfg;
+    uint8_t imr;
+    uint8_t phys[6]; /* mac address */
+    uint8_t curpag;
+    uint8_t mult[8]; /* multicast mask array */
+    uint8_t mem[NE2000_MEM_SIZE];
+} NE2000State;
+
+NE2000State ne2000_state;
+int net_fd = -1;
+char network_script[1024];
+
+void ne2000_reset(void)
+{
+    NE2000State *s = &ne2000_state;
+    int i;
+
+    s->isr = ENISR_RESET;
+    s->mem[0] = 0x52;
+    s->mem[1] = 0x54;
+    s->mem[2] = 0x00;
+    s->mem[3] = 0x12;
+    s->mem[4] = 0x34;
+    s->mem[5] = 0x56;
+    s->mem[14] = 0x57;
+    s->mem[15] = 0x57;
+
+    /* duplicate prom data */
+    for(i = 15;i >= 0; i--) {
+        s->mem[2 * i] = s->mem[i];
+        s->mem[2 * i + 1] = s->mem[i];
+    }
+}
+
+void ne2000_update_irq(NE2000State *s)
+{
+    int isr;
+    isr = s->isr & s->imr;
+    if (isr)
+        pic_set_irq(NE2000_IRQ, 1);
+    else
+        pic_set_irq(NE2000_IRQ, 0);
+}
+
+int net_init(void)
+{
+    struct ifreq ifr;
+    int fd, ret, pid, status;
+    
+    fd = open("/dev/net/tun", O_RDWR);
+    if (fd < 0) {
+        fprintf(stderr, "warning: could not open /dev/net/tun: no virtual network emulation\n");
+        return -1;
+    }
+    memset(&ifr, 0, sizeof(ifr));
+    ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+    pstrcpy(ifr.ifr_name, IFNAMSIZ, "tun%d");
+    ret = ioctl(fd, TUNSETIFF, (void *) &ifr);
+    if (ret != 0) {
+        fprintf(stderr, "warning: could not configure /dev/net/tun: no virtual network emulation\n");
+        close(fd);
+        return -1;
+    }
+    printf("connected to host network interface: %s\n", ifr.ifr_name);
+    fcntl(fd, F_SETFL, O_NONBLOCK);
+    net_fd = fd;
+
+    /* try to launch network init script */
+    pid = fork();
+    if (pid >= 0) {
+        if (pid == 0) {
+            execl(network_script, network_script, ifr.ifr_name, NULL);
+            exit(1);
+        }
+        while (waitpid(pid, &status, 0) != pid);
+        if (!WIFEXITED(status) ||
+            WEXITSTATUS(status) != 0) {
+            fprintf(stderr, "%s: could not launch network script for '%s'\n",
+                    network_script, ifr.ifr_name);
+        }
+    }
+    return 0;
+}
+
+void net_send_packet(NE2000State *s, const uint8_t *buf, int size)
+{
+#ifdef DEBUG_NE2000
+    printf("NE2000: sending packet size=%d\n", size);
+#endif
+    write(net_fd, buf, size);
+}
+
+/* return true if the NE2000 can receive more data */
+int ne2000_can_receive(NE2000State *s)
+{
+    int avail, index, boundary;
+    
+    if (s->cmd & E8390_STOP)
+        return 0;
+    index = s->curpag << 8;
+    boundary = s->boundary << 8;
+    if (index < boundary)
+        avail = boundary - index;
+    else
+        avail = (s->stop - s->start) - (index - boundary);
+    if (avail < (MAX_ETH_FRAME_SIZE + 4))
+        return 0;
+    return 1;
+}
+
+void ne2000_receive(NE2000State *s, uint8_t *buf, int size)
+{
+    uint8_t *p;
+    int total_len, next, avail, len, index;
+
+#if defined(DEBUG_NE2000)
+    printf("NE2000: received len=%d\n", size);
+#endif
+
+    index = s->curpag << 8;
+    /* 4 bytes for header */
+    total_len = size + 4;
+    /* address for next packet (4 bytes for CRC) */
+    next = index + ((total_len + 4 + 255) & ~0xff);
+    if (next >= s->stop)
+        next -= (s->stop - s->start);
+    /* prepare packet header */
+    p = s->mem + index;
+    p[0] = ENRSR_RXOK; /* receive status */
+    p[1] = next >> 8;
+    p[2] = total_len;
+    p[3] = total_len >> 8;
+    index += 4;
+
+    /* write packet data */
+    while (size > 0) {
+        avail = s->stop - index;
+        len = size;
+        if (len > avail)
+            len = avail;
+        memcpy(s->mem + index, buf, len);
+        buf += len;
+        index += len;
+        if (index == s->stop)
+            index = s->start;
+        size -= len;
+    }
+    s->curpag = next >> 8;
+    
+    /* now we can signal we have receive something */
+    s->isr |= ENISR_RX;
+    ne2000_update_irq(s);
+}
+
+void ne2000_ioport_write(CPUX86State *env, uint32_t addr, uint32_t val)
+{
+    NE2000State *s = &ne2000_state;
+    int offset, page;
+
+    addr &= 0xf;
+#ifdef DEBUG_NE2000
+    printf("NE2000: write addr=0x%x val=0x%02x\n", addr, val);
+#endif
+    if (addr == E8390_CMD) {
+        /* control register */
+        s->cmd = val;
+        if (val & E8390_START) {
+            /* test specific case: zero length transfert */
+            if ((val & (E8390_RREAD | E8390_RWRITE)) &&
+                s->rcnt == 0) {
+                s->isr |= ENISR_RDC;
+                ne2000_update_irq(s);
+            }
+            if (val & E8390_TRANS) {
+                net_send_packet(s, s->mem + (s->tpsr << 8), s->tcnt);
+                /* signal end of transfert */
+                s->tsr = ENTSR_PTX;
+                s->isr |= ENISR_TX;
+                ne2000_update_irq(s);
+            }
+        }
+    } else {
+        page = s->cmd >> 6;
+        offset = addr | (page << 4);
+        switch(offset) {
+        case EN0_STARTPG:
+            s->start = val << 8;
+            break;
+        case EN0_STOPPG:
+            s->stop = val << 8;
+            break;
+        case EN0_BOUNDARY:
+            s->boundary = val;
+            break;
+        case EN0_IMR:
+            s->imr = val;
+            ne2000_update_irq(s);
+            break;
+        case EN0_TPSR:
+            s->tpsr = val;
+            break;
+        case EN0_TCNTLO:
+            s->tcnt = (s->tcnt & 0xff00) | val;
+            break;
+        case EN0_TCNTHI:
+            s->tcnt = (s->tcnt & 0x00ff) | (val << 8);
+            break;
+        case EN0_RSARLO:
+            s->rsar = (s->rsar & 0xff00) | val;
+            break;
+        case EN0_RSARHI:
+            s->rsar = (s->rsar & 0x00ff) | (val << 8);
+            break;
+        case EN0_RCNTLO:
+            s->rcnt = (s->rcnt & 0xff00) | val;
+            break;
+        case EN0_RCNTHI:
+            s->rcnt = (s->rcnt & 0x00ff) | (val << 8);
+            break;
+        case EN0_DCFG:
+            s->dcfg = val;
+            break;
+        case EN0_ISR:
+            s->isr &= ~val;
+            ne2000_update_irq(s);
+            break;
+        case EN1_PHYS ... EN1_PHYS + 5:
+            s->phys[offset - EN1_PHYS] = val;
+            break;
+        case EN1_CURPAG:
+            s->curpag = val;
+            break;
+        case EN1_MULT ... EN1_MULT + 7:
+            s->mult[offset - EN1_MULT] = val;
+            break;
+        }
+    }
+}
+
+uint32_t ne2000_ioport_read(CPUX86State *env, uint32_t addr)
+{
+    NE2000State *s = &ne2000_state;
+    int offset, page, ret;
+
+    addr &= 0xf;
+    if (addr == E8390_CMD) {
+        ret = s->cmd;
+    } else {
+        page = s->cmd >> 6;
+        offset = addr | (page << 4);
+        switch(offset) {
+        case EN0_TSR:
+            ret = s->tsr;
+            break;
+        case EN0_BOUNDARY:
+            ret = s->boundary;
+            break;
+        case EN0_ISR:
+            ret = s->isr;
+            break;
+        case EN1_PHYS ... EN1_PHYS + 5:
+            ret = s->phys[offset - EN1_PHYS];
+            break;
+        case EN1_CURPAG:
+            ret = s->curpag;
+            break;
+        case EN1_MULT ... EN1_MULT + 7:
+            ret = s->mult[offset - EN1_MULT];
+            break;
+        default:
+            ret = 0x00;
+            break;
+        }
+    }
+#ifdef DEBUG_NE2000
+    printf("NE2000: read addr=0x%x val=%02x\n", addr, ret);
+#endif
+    return ret;
+}
+
+void ne2000_asic_ioport_write(CPUX86State *env, uint32_t addr, uint32_t val)
+{
+    NE2000State *s = &ne2000_state;
+    uint8_t *p;
+
+#ifdef DEBUG_NE2000
+    printf("NE2000: asic write val=0x%04x\n", val);
+#endif
+    p = s->mem + s->rsar;
+    if (s->dcfg & 0x01) {
+        /* 16 bit access */
+        p[0] = val;
+        p[1] = val >> 8;
+        s->rsar += 2;
+        s->rcnt -= 2;
+    } else {
+        /* 8 bit access */
+        p[0] = val;
+        s->rsar++;
+        s->rcnt--;
+    }
+    /* wrap */
+    if (s->rsar == s->stop)
+        s->rsar = s->start;
+    if (s->rcnt == 0) {
+        /* signal end of transfert */
+        s->isr |= ENISR_RDC;
+        ne2000_update_irq(s);
+    }
+}
+
+uint32_t ne2000_asic_ioport_read(CPUX86State *env, uint32_t addr)
+{
+    NE2000State *s = &ne2000_state;
+    uint8_t *p;
+    int ret;
+
+    p = s->mem + s->rsar;
+    if (s->dcfg & 0x01) {
+        /* 16 bit access */
+        ret = p[0] | (p[1] << 8);
+        s->rsar += 2;
+        s->rcnt -= 2;
+    } else {
+        /* 8 bit access */
+        ret = p[0];
+        s->rsar++;
+        s->rcnt--;
+    }
+    /* wrap */
+    if (s->rsar == s->stop)
+        s->rsar = s->start;
+    if (s->rcnt == 0) {
+        /* signal end of transfert */
+        s->isr |= ENISR_RDC;
+        ne2000_update_irq(s);
+    }
+#ifdef DEBUG_NE2000
+    printf("NE2000: asic read val=0x%04x\n", ret);
+#endif
+    return ret;
+}
+
+void ne2000_reset_ioport_write(CPUX86State *env, uint32_t addr, uint32_t val)
+{
+    /* nothing to do (end of reset pulse) */
+}
+
+uint32_t ne2000_reset_ioport_read(CPUX86State *env, uint32_t addr)
+{
+    ne2000_reset();
+    return 0;
+}
+
+void ne2000_init(void)
+{
+    register_ioport_writeb(NE2000_IOPORT, 16, ne2000_ioport_write);
+    register_ioport_readb(NE2000_IOPORT, 16, ne2000_ioport_read);
+
+    register_ioport_writeb(NE2000_IOPORT + 0x10, 1, ne2000_asic_ioport_write);
+    register_ioport_readb(NE2000_IOPORT + 0x10, 1, ne2000_asic_ioport_read);
+    register_ioport_writew(NE2000_IOPORT + 0x10, 2, ne2000_asic_ioport_write);
+    register_ioport_readw(NE2000_IOPORT + 0x10, 2, ne2000_asic_ioport_read);
+
+    register_ioport_writeb(NE2000_IOPORT + 0x1f, 1, ne2000_reset_ioport_write);
+    register_ioport_readb(NE2000_IOPORT + 0x1f, 1, ne2000_reset_ioport_read);
+    ne2000_reset();
+}
+
+/***********************************************************/
 /* cpu signal handler */
 static void host_segv_handler(int host_signum, siginfo_t *info, 
                               void *puc)
@@ -1178,10 +1672,11 @@
            "to 0x90000000 in asm/page.h and arch/i386/vmlinux.lds)\n"
            "'initrd' is an initrd image\n"
            "-m megs   set virtual RAM size to megs MB\n"
+           "-n script set network init script [default=%s]\n"
            "-d        output log in /tmp/vl.log\n"
            "\n"
-           "During emulation, use C-a h to get terminal commands:\n"
-           );
+           "During emulation, use C-a h to get terminal commands:\n",
+           DEFAULT_NETWORK_SCRIPT);
     term_print_help();
     exit(1);
 }
@@ -1198,8 +1693,9 @@
     mallopt(M_MMAP_THRESHOLD, 4096 * 1024);
     
     phys_ram_size = 32 * 1024 * 1024;
+    pstrcpy(network_script, sizeof(network_script), DEFAULT_NETWORK_SCRIPT);
     for(;;) {
-        c = getopt(argc, argv, "hm:d");
+        c = getopt(argc, argv, "hm:dn:");
         if (c == -1)
             break;
         switch(c) {
@@ -1214,6 +1710,9 @@
         case 'd':
             loglevel = 1;
             break;
+        case 'n':
+            pstrcpy(network_script, sizeof(network_script), optarg);
+            break;
         }
     }
     if (optind + 1 >= argc)
@@ -1229,6 +1728,9 @@
         setvbuf(logfile, NULL, _IOLBF, 0);
     }
 
+    /* init network tun interface */
+    net_init();
+
     /* init the memory */
     strcpy(phys_ram_file, "/tmp/vlXXXXXX");
     if (mkstemp(phys_ram_file) < 0) {
@@ -1294,6 +1796,7 @@
     pic_init();
     pit_init();
     serial_init();
+    ne2000_init();
 
     /* setup cpu signal handlers for MMU / self modifying code handling */
     sigfillset(&act.sa_mask);
@@ -1341,7 +1844,7 @@
     setitimer(ITIMER_REAL, &itv, NULL);
 
     for(;;) {
-        struct pollfd ufds[1], *pf;
+        struct pollfd ufds[2], *pf, *serial_ufd, *net_ufd;
         int ret, n, timeout;
         uint8_t ch;
 
@@ -1353,20 +1856,41 @@
         else
             timeout = 0;
         /* poll any events */
+        serial_ufd = NULL;
+        net_ufd = NULL;
         pf = ufds;
         if (!(serial_ports[0].lsr & UART_LSR_DR)) {
+            serial_ufd = pf;
             pf->fd = 0;
             pf->events = POLLIN;
             pf++;
         }
+        if (net_fd > 0 && ne2000_can_receive(&ne2000_state)) {
+            net_ufd = pf;
+            pf->fd = net_fd;
+            pf->events = POLLIN;
+            pf++;
+        }
         ret = poll(ufds, pf - ufds, timeout);
         if (ret > 0) {
-            if (ufds[0].revents & POLLIN) {
+            if (serial_ufd && (serial_ufd->revents & POLLIN)) {
                 n = read(0, &ch, 1);
                 if (n == 1) {
                     serial_received_byte(&serial_ports[0], ch);
                 }
             }
+            if (net_ufd && (net_ufd->revents & POLLIN)) {
+                uint8_t buf[MAX_ETH_FRAME_SIZE];
+
+                n = read(net_fd, buf, MAX_ETH_FRAME_SIZE);
+                if (n > 0) {
+                    if (n < 60) {
+                        memset(buf + n, 0, 60 - n);
+                        n = 60;
+                    }
+                    ne2000_receive(&ne2000_state, buf, n);
+                }
+            }
         }
 
         /* just for testing */