@@ -48,6 +48,17 @@
* 2011-Mar-22 Benjamin Poirier: Implemented VLAN offloading
*/
+/*
+ * Testcases and successful regression tests:
+ * 1.) DOS RSET8139.EXE: EEPROM Test successful
+ * 2.) DOS RSET8139.EXE: Local loopback Test (Run Diagnostics On Board)
+ * 3.) DOS RSET8139.EXE: Remote loopback Test as Initiator (Run Diagnostics On Network)
+ * 4.) DOS RSET8139.EXE: Remote loopback Test as Responder (Run Diagnostics On Network)
+ * 5.) DOS driver: Loads and works
+ * 6.) Linux tests
+ * 7.) Windows tests
+ */
+
/* For crc32 */
#include <zlib.h>
@@ -61,7 +72,7 @@
#include "iov.h"
/* debug RTL8139 card */
-//#define DEBUG_RTL8139 1
+#define DEBUG_RTL8139 1
#define PCI_FREQUENCY 33000000L
@@ -130,6 +141,7 @@ enum RTL8139_registers {
NWayExpansion = 0x6A,
/* Undocumented registers, but required for proper operation. */
FIFOTMS = 0x70, /* FIFO Control and test. */
+ RX_ER = 0x72, /* RX_ER Counter */
CSCR = 0x74, /* Chip Status and Configuration Register. */
PARA78 = 0x78,
PARA7c = 0x7c, /* Magic transceiver parameter register. */
@@ -467,6 +479,8 @@ typedef struct RTL8139State {
uint16_t NWayLPAR;
uint16_t NWayExpansion;
+ uint16_t Fifo_TMS;
+
uint16_t CpCmd;
uint8_t TxThresh;
@@ -774,6 +788,12 @@ static void rtl8139_write_buffer(RTL8139State *s, const void *buf, int size)
if (size > wrapped)
{
+ DPRINTF(">>> rx packet pci dma write "
+ "RxBuf=0x%x, RxBufAddr=0x%x, RxBuf+RxBufAddr=0x%x, "
+ "buf=%p, size=%i, wrapped=%i, size-wrapped=%i\n",
+ s->RxBuf, s->RxBufAddr, s->RxBuf + s->RxBufAddr,
+ buf, size, wrapped, size-wrapped
+ );
pci_dma_write(&s->dev, s->RxBuf + s->RxBufAddr,
buf, size-wrapped);
}
@@ -781,6 +801,12 @@ static void rtl8139_write_buffer(RTL8139State *s, const void *buf, int size)
/* reset buffer pointer */
s->RxBufAddr = 0;
+ DPRINTF(">>> rx packet pci dma write "
+ "RxBuf=0x%x, RxBufAddr=0x%x, RxBuf+RxBufAddr=0x%x, "
+ "buf=%p, size=%i, wrapped=%i, size-wrapped=%i\n",
+ s->RxBuf, s->RxBufAddr, s->RxBuf + s->RxBufAddr,
+ buf, size, wrapped, size-wrapped
+ );
pci_dma_write(&s->dev, s->RxBuf + s->RxBufAddr,
buf + (size-wrapped), wrapped);
@@ -791,6 +817,13 @@ static void rtl8139_write_buffer(RTL8139State *s, const void *buf, int size)
}
/* non-wrapping path or overwrapping enabled */
+ DPRINTF(">>> rx packet pci dma write "
+ "RxBuf=0x%x, RxBufAddr=0x%x, RxBuf+RxBufAddr=0x%x, "
+ "buf=%p, size=%i\n",
+ s->RxBuf, s->RxBufAddr, s->RxBuf + s->RxBufAddr,
+ buf, size
+ );
+
pci_dma_write(&s->dev, s->RxBuf + s->RxBufAddr, buf, size);
s->RxBufAddr += size;
@@ -1209,15 +1242,18 @@ static ssize_t rtl8139_receive(VLANClientState *nc, const uint8_t *buf, size_t s
static void rtl8139_reset_rxring(RTL8139State *s, uint32_t bufferSize)
{
s->RxBufferSize = bufferSize;
+ /* Why not 0xFFF0? */
s->RxBufPtr = 0;
s->RxBufAddr = 0;
}
-static void rtl8139_reset(DeviceState *d)
+static void rtl8139_reset_delegate(DeviceState *d, int hard_reset)
{
RTL8139State *s = container_of(d, RTL8139State, dev.qdev);
int i;
+ DPRINTF("rtl8139_reset_delegate, hard_reset=%i\n", hard_reset);
+
/* restore MAC address */
memcpy(s->phys, s->conf.macaddr.a, 6);
@@ -1240,7 +1276,18 @@ static void rtl8139_reset(DeviceState *d)
s->RxRingAddrLO = 0;
s->RxRingAddrHI = 0;
- s->RxBuf = 0;
+ /*
+ * DOS driver sets the RxBuf and then resets again.
+ * Afterwards RxBuf is not set anymore. Looks like real hardware
+ * also doesn't reset RxBuf on reset.
+ * When set to 0 DOS OS crashed because of adress 0 is overwritten ...
+ *
+ * On the other hand this must be done on a hardware triggered
+ * reset (a DMA enabled receiver might overwrite some areas!).
+ */
+
+ if (hard_reset)
+ s->RxBuf = 0;
rtl8139_reset_rxring(s, 8192);
@@ -1271,7 +1318,8 @@ static void rtl8139_reset(DeviceState *d)
// s->BasicModeCtrl = 0x3100; // 100Mbps, full duplex, autonegotiation
// s->BasicModeCtrl = 0x2100; // 100Mbps, full duplex
- s->BasicModeCtrl = 0x1000; // autonegotiation
+// s->BasicModeCtrl = 0x1000; // autonegotiation
+ s->BasicModeCtrl = 0x3100; // 100Mbps, full duplex, autonegotiation
s->BasicModeStatus = 0x7809;
//s->BasicModeStatus |= 0x0040; /* UTP medium */
@@ -1282,6 +1330,8 @@ static void rtl8139_reset(DeviceState *d)
s->NWayLPAR = 0x05e1; /* all modes, full duplex */
s->NWayExpansion = 0x0001; /* autonegotiation supported */
+ s->Fifo_TMS = 0x0000; /* Phy N-Way Test Register */
+
/* also reset timer and disable timer interrupt */
s->TCTR = 0;
s->TimerInt = 0;
@@ -1291,6 +1341,11 @@ static void rtl8139_reset(DeviceState *d)
RTL8139TallyCounters_clear(&s->tally_counters);
}
+static void rtl8139_reset(DeviceState *d)
+{
+ rtl8139_reset_delegate(d, 1);
+}
+
static void RTL8139TallyCounters_clear(RTL8139TallyCounters* counters)
{
counters->TxOk = 0;
@@ -1388,7 +1443,7 @@ static void rtl8139_ChipCmd_write(RTL8139State *s, uint32_t val)
if (val & CmdReset)
{
DPRINTF("ChipCmd reset\n");
- rtl8139_reset(&s->dev.qdev);
+ rtl8139_reset_delegate(&s->dev.qdev, 0);
}
if (val & CmdRxEnb)
{
@@ -1561,7 +1616,7 @@ static void rtl8139_Cfg9346_write(RTL8139State *s, uint32_t val)
} else if (opmode == 0x40) {
/* Reset. */
val = 0;
- rtl8139_reset(&s->dev.qdev);
+ rtl8139_reset_delegate(&s->dev.qdev, 0);
}
s->Cfg9346 = val;
@@ -1726,6 +1781,8 @@ static void rtl8139_TxConfig_write(RTL8139State *s, uint32_t val)
val = SET_MASKED(val, TxVersionMask | 0x8070f80f, s->TxConfig);
s->TxConfig = val;
+
+ s->currTxDesc = 0;
}
static void rtl8139_TxConfig_writeb(RTL8139State *s, uint32_t val)
@@ -1804,7 +1861,8 @@ static void rtl8139_transfer_frame(RTL8139State *s, uint8_t *buf, int size,
}
DPRINTF("+++ transmit loopback mode\n");
- rtl8139_do_receive(&s->nic->nc, buf, size, do_interrupt);
+ /* Interrupt must be triggered here! */
+ rtl8139_do_receive(&s->nic->nc, buf, size, 1);
if (iov) {
g_free(buf2);
@@ -1846,12 +1904,14 @@ static int rtl8139_transmit_one(RTL8139State *s, int descriptor)
pci_dma_read(&s->dev, s->TxAddr[descriptor], txbuffer, txsize);
- /* Mark descriptor as transferred */
+ /* Send frame */
+ rtl8139_transfer_frame(s, txbuffer, txsize, 0, NULL);
+
+ /* Mark descriptor as transferred but after sending */
+ /* (now correct and avoids race conditions!) */
s->TxStatus[descriptor] |= TxHostOwns;
s->TxStatus[descriptor] |= TxStatOK;
- rtl8139_transfer_frame(s, txbuffer, txsize, 0, NULL);
-
DPRINTF("+++ transmitted %d bytes from descriptor %d\n", txsize,
descriptor);
@@ -2488,12 +2548,16 @@ static void rtl8139_TxStatus_write(RTL8139State *s, uint32_t txRegOffset, uint32
DPRINTF("TxStatus write offset=0x%x val=0x%08x descriptor=%d\n",
txRegOffset, val, descriptor);
+ DPRINTF("TxStatus write old value=0x%08x descriptor=%d\n",
+ s->TxStatus[descriptor], descriptor);
/* mask only reserved bits */
val &= ~0xff00c000; /* these bits are reset on write */
val = SET_MASKED(val, 0x00c00000, s->TxStatus[descriptor]);
s->TxStatus[descriptor] = val;
+ DPRINTF("TxStatus write new value=0x%08x descriptor=%d\n",
+ s->TxStatus[descriptor], descriptor);
/* attempt to start transmission */
rtl8139_transmit(s);
@@ -2641,6 +2705,16 @@ static void rtl8139_IntrStatus_write(RTL8139State *s, uint32_t val)
{
DPRINTF("IntrStatus write(w) val=0x%04x\n", val);
+ /*
+ * According to the specification writing to ISR must
+ * have no effect: "Writing to the ISR has no effect."
+ * http://www.cs.usfca.edu/~cruse/cs326/RTL8139D_DataSheet.pdf
+ *
+ * According to the newer specification writing to ISR clears
+ * ones bits!!
+ * http://realtek.info/pdf/rtl8139cp.pdf
+ * See also: http://www.lowlevel.eu/wiki/RTL8139
+ */
#if 0
/* writing to ISR has no effect */
@@ -2678,6 +2752,16 @@ static uint32_t rtl8139_IntrStatus_read(RTL8139State *s)
DPRINTF("IntrStatus read(w) val=0x%04x\n", ret);
+ /*
+ * According to the specification interrupts have to be cleared.
+ * "Reading the ISR clears all interrupts"
+ * http://www.cs.usfca.edu/~cruse/cs326/RTL8139D_DataSheet.pdf
+ *
+ * But according to newer specifications all interrupt bits
+ * are not cleared!!!
+ * http://realtek.info/pdf/rtl8139cp.pdf
+ * See also: http://www.lowlevel.eu/wiki/RTL8139
+ */
#if 0
/* reading ISR clears all interrupts */
@@ -2727,6 +2811,12 @@ static void rtl8139_io_writeb(void *opaque, uint8_t addr, uint32_t val)
case ChipCmd:
rtl8139_ChipCmd_write(s, val);
break;
+ case IntrMask:
+ s->IntrMask = (s->IntrMask & 0xFF00) | (val & 0xFF);
+ break;
+ case IntrStatus:
+ rtl8139_IntrStatus_write(s, val & 0xFF);
+ break;
case Cfg9346:
rtl8139_Cfg9346_write(s, val);
break;
@@ -2748,6 +2838,29 @@ static void rtl8139_io_writeb(void *opaque, uint8_t addr, uint32_t val)
case Config5:
rtl8139_Config5_write(s, val);
break;
+ case RX_ER:
+ s->tally_counters.RxERR = (s->tally_counters.RxERR & 0xFF00) | (val & 0xFF);
+ break;
+ case RX_ER+1:
+ s->tally_counters.RxERR = ((val & 0xFF)<<8) | (s->tally_counters.RxERR & 0xFF);
+ break;
+ case FIFOTMS:
+ s->Fifo_TMS = (s->Fifo_TMS & 0xFF00) | (val & 0xFF);
+ break;
+ case FIFOTMS+1:
+ s->Fifo_TMS = ((val & 0xFF)<<8) | (s->Fifo_TMS & 0xFF);
+ break;
+ case PARA78:
+ case PARA78+1:
+ case PARA78+2:
+ case PARA78+3:
+ case PARA7c:
+ case PARA7c+1:
+ case PARA7c+2:
+ case PARA7c+3:
+ DPRINTF("not implemented write(b) to PARA%02x val=0x%02x\n",
+ addr, val);
+ break;
case MediaStatus:
/* ignore */
DPRINTF("not implemented write(b) to MediaStatus val=0x%02x\n",
@@ -2834,6 +2947,10 @@ static void rtl8139_io_writew(void *opaque, uint8_t addr, uint32_t val)
s->NWayExpansion = val;
break;
+ case FIFOTMS:
+ DPRINTF("Fifo_TMS write(w) val=0x%04x\n", val);
+ s->Fifo_TMS = val;
+ break;
case CpCmd:
rtl8139_CpCmd_write(s, val);
break;
@@ -2974,9 +3091,29 @@ static uint32_t rtl8139_io_readb(void *opaque, uint8_t addr)
case MAR0 ... MAR0+7:
ret = s->mult[addr - MAR0];
break;
+ case TxAddr0 ... TxAddr0 + 4*4-1:
+ {
+ int offset = (addr-TxAddr0)/4;
+ int shift = ((addr-TxAddr0)%4) << 3;
+ ret = (s->TxAddr[offset] >> shift ) & 0xFF;
+ }
+ break;
+
case ChipCmd:
ret = rtl8139_ChipCmd_read(s);
break;
+ case RxBufPtr:
+ ret = s->RxBufPtr & 0xFF;
+ break;
+ case RxBufAddr:
+ ret = s->RxBufAddr & 0xFF;
+ break;
+ case IntrMask:
+ ret = s->IntrMask & 0xFF;
+ break;
+ case IntrStatus:
+ ret = s->IntrStatus & 0xFF;
+ break;
case Cfg9346:
ret = rtl8139_Cfg9346_read(s);
break;
@@ -2984,7 +3121,10 @@ static uint32_t rtl8139_io_readb(void *opaque, uint8_t addr)
ret = rtl8139_Config0_read(s);
break;
case Config1:
- ret = rtl8139_Config1_read(s);
+ ret = rtl8139_Config1_read(s) & 0xFF;
+ break;
+ case Config1+1:
+ ret = (rtl8139_Config1_read(s) >> 8) & 0xFF;
break;
case Config3:
ret = rtl8139_Config3_read(s);
@@ -2995,7 +3135,30 @@ static uint32_t rtl8139_io_readb(void *opaque, uint8_t addr)
case Config5:
ret = rtl8139_Config5_read(s);
break;
-
+ case RX_ER:
+ ret = s->tally_counters.RxERR & 0xFF;
+ break;
+ case RX_ER+1:
+ ret = (s->tally_counters.RxERR >> 8) & 0xFF;
+ break;
+ case RxConfig:
+ ret = s->RxConfig & 0xFF;
+ break;
+ case RxConfig+1:
+ ret = (s->RxConfig >> 8) & 0xFF;
+ break;
+ case FIFOTMS:
+ ret = s->Fifo_TMS & 0xFF;
+ break;
+ case FIFOTMS+1:
+ ret = (s->Fifo_TMS >> 8) & 0xFF;
+ break;
+ case BasicModeCtrl:
+ ret = s->BasicModeCtrl & 0xFF;
+ break;
+ case BasicModeCtrl+1:
+ ret = (s->BasicModeCtrl >> 8) & 0xFF;
+ break;
case MediaStatus:
ret = 0xd0;
DPRINTF("MediaStatus read 0x%x\n", ret);
@@ -3075,6 +3238,10 @@ static uint32_t rtl8139_io_readw(void *opaque, uint8_t addr)
ret = s->NWayExpansion;
DPRINTF("NWayExpansion read(w) val=0x%04x\n", ret);
break;
+ case FIFOTMS:
+ ret = s->Fifo_TMS;
+ DPRINTF("Fifo_TMS read(w) val=0x%04x\n", ret);
+ break;
case CpCmd:
ret = rtl8139_CpCmd_read(s);
@@ -3322,6 +3489,7 @@ static const VMStateDescription vmstate_rtl8139 = {
VMSTATE_UINT16(NWayAdvert, RTL8139State),
VMSTATE_UINT16(NWayLPAR, RTL8139State),
VMSTATE_UINT16(NWayExpansion, RTL8139State),
+ VMSTATE_UINT16(Fifo_TMS, RTL8139State),
VMSTATE_UINT16(CpCmd, RTL8139State),
VMSTATE_UINT8(TxThresh, RTL8139State),
@@ -3452,6 +3620,8 @@ static int pci_rtl8139_init(PCIDevice *dev)
{
RTL8139State * s = DO_UPCAST(RTL8139State, dev, dev);
uint8_t *pci_conf;
+ int i = 0;
+ uint16_t checksum = 0;
pci_conf = s->dev.config;
pci_conf[PCI_INTERRUPT_PIN] = 1; /* interrupt pin A */
@@ -3473,10 +3643,47 @@ static int pci_rtl8139_init(PCIDevice *dev)
s->eeprom.contents[1] = PCI_VENDOR_ID_REALTEK;
s->eeprom.contents[2] = PCI_DEVICE_ID_REALTEK_8139;
#endif
+
+ /* PCI subsystem vendor and device ID should be mirrored here */
+ s->eeprom.contents[3] = PCI_VENDOR_ID_REALTEK;
+ s->eeprom.contents[4] = PCI_DEVICE_ID_REALTEK_8139;
+ s->eeprom.contents[5] = 0x4020;
+ s->eeprom.contents[6] = 0xE110;
s->eeprom.contents[7] = s->conf.macaddr.a[0] | s->conf.macaddr.a[1] << 8;
s->eeprom.contents[8] = s->conf.macaddr.a[2] | s->conf.macaddr.a[3] << 8;
s->eeprom.contents[9] = s->conf.macaddr.a[4] | s->conf.macaddr.a[5] << 8;
+ s->eeprom.contents[10] = 0x4D10;
+ s->eeprom.contents[11] = 0xF7C2;
+ s->eeprom.contents[12] = 0x8001;
+ s->eeprom.contents[13] = 0xB388;
+ s->eeprom.contents[14] = 0x58FA;
+ s->eeprom.contents[15] = 0x0708;
+ s->eeprom.contents[16] = 0xD843;
+ s->eeprom.contents[17] = 0xA438;
+ s->eeprom.contents[18] = 0xD843;
+ s->eeprom.contents[19] = 0xA438;
+ s->eeprom.contents[20] = 0xD843;
+ s->eeprom.contents[21] = 0xA438;
+ s->eeprom.contents[22] = 0xD843;
+ s->eeprom.contents[23] = 0xA438;
+
+ s->eeprom.contents[31] = 0x2000;
+
+ for (i = 0; i < 24; i++) checksum += s->eeprom.contents[i];
+ checksum = (~checksum + 1) & 0xFFFF;
+ DPRINTF("EEPROM checksum=0x%04X\n", checksum);
+ s->eeprom.contents[25] = checksum; /* Checksum */
+
+ DPRINTF("EEPROM contents\n");
+ for (i = 0; i < 64; i++) {
+#if 0
+ DPRINTF("0x%04X,%s", s->eeprom.contents[i], ((i+1)%8) == 0 ? "\n" : " ");
+#else
+ printf("0x%04X,%s", s->eeprom.contents[i], ((i+1)%8) == 0 ? "\n" : " ");
+#endif
+ }
+
s->nic = qemu_new_nic(&net_rtl8139_info, &s->conf,
object_get_typename(OBJECT(dev)), dev->qdev.id, s);
qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
@@ -3509,6 +3716,13 @@ static void rtl8139_class_init(ObjectClass *klass, void *data)
k->romfile = "pxe-rtl8139.rom";
k->vendor_id = PCI_VENDOR_ID_REALTEK;
k->device_id = PCI_DEVICE_ID_REALTEK_8139;
+#ifndef ALTERNATE_EEPROM_CONTENTS
+ k->subsystem_vendor_id = PCI_VENDOR_ID_REALTEK;
+ k->subsystem_id = PCI_DEVICE_ID_REALTEK_8139;
+#else
+ k->subsystem_vendor_id = 0x10BD;
+ k->subsystem_id = 0x0320;
+#endif
k->revision = RTL8139_PCI_REVID; /* >=0x20 is for 8139C+ */
k->class_id = PCI_CLASS_NETWORK_ETHERNET;
dc->reset = rtl8139_reset;