added NE2000 emulation

git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@269 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
bellard 2003-06-25 00:07:40 +00:00
parent 357a94326c
commit f1510b2cc3
1 changed files with 529 additions and 5 deletions

534
vl.c
View File

@ -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 @@ int register_ioport_writeb(int start, int length, IOPortWriteFunc *func)
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 @@ void serial_init(void)
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 @@ void help(void)
"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 @@ int main(int argc, char **argv)
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 @@ int main(int argc, char **argv)
case 'd':
loglevel = 1;
break;
case 'n':
pstrcpy(network_script, sizeof(network_script), optarg);
break;
}
}
if (optind + 1 >= argc)
@ -1229,6 +1728,9 @@ int main(int argc, char **argv)
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 @@ int main(int argc, char **argv)
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 @@ int main(int argc, char **argv)
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 @@ int main(int argc, char **argv)
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 */