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 */