Improve FreeBSD networking support.
[gem5.git] / dev / ns_gige.cc
index 613677875052007aa1ddd37c83329f365624746a..ae129c2493d8981d62c36f5e6ba3bd2e6870c36b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004 The Regents of The University of Michigan
+ * Copyright (c) 2004-2005 The Regents of The University of Michigan
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -26,7 +26,7 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-/* @file
+/** @file
  * Device module for modelling the National Semiconductor
  * DP83820 ethernet controller.  Does not support priority queueing
  */
@@ -36,7 +36,6 @@
 
 #include "base/inet.hh"
 #include "cpu/exec_context.hh"
-#include "dev/dma.hh"
 #include "dev/etherlink.hh"
 #include "dev/ns_gige.hh"
 #include "dev/pciconfigall.hh"
@@ -44,8 +43,8 @@
 #include "mem/bus/dma_interface.hh"
 #include "mem/bus/pio_interface.hh"
 #include "mem/bus/pio_interface_impl.hh"
-#include "mem/functional_mem/memory_control.hh"
-#include "mem/functional_mem/physical_memory.hh"
+#include "mem/functional/memory_control.hh"
+#include "mem/functional/physical.hh"
 #include "sim/builder.hh"
 #include "sim/debug.hh"
 #include "sim/host.hh"
@@ -94,19 +93,19 @@ NSGigE::NSGigE(Params *p)
     : PciDev(p), ioEnable(false),
       txFifo(p->tx_fifo_size), rxFifo(p->rx_fifo_size),
       txPacket(0), rxPacket(0), txPacketBufPtr(NULL), rxPacketBufPtr(NULL),
-      txXferLen(0), rxXferLen(0), txState(txIdle), txEnable(false),
-      CTDD(false),
+      txXferLen(0), rxXferLen(0), clock(p->clock),
+      txState(txIdle), txEnable(false), CTDD(false),
       txFragPtr(0), txDescCnt(0), txDmaState(dmaIdle), rxState(rxIdle),
       rxEnable(false), CRDD(false), rxPktBytes(0),
       rxFragPtr(0), rxDescCnt(0), rxDmaState(dmaIdle), extstsEnable(false),
-      rxDmaReadEvent(this), rxDmaWriteEvent(this),
+      eepromState(eepromStart), rxDmaReadEvent(this), rxDmaWriteEvent(this),
       txDmaReadEvent(this), txDmaWriteEvent(this),
       dmaDescFree(p->dma_desc_free), dmaDataFree(p->dma_data_free),
       txDelay(p->tx_delay), rxDelay(p->rx_delay),
-      rxKickTick(0), txKickTick(0),
+      rxKickTick(0), rxKickEvent(this), txKickTick(0), txKickEvent(this),
       txEvent(this), rxFilterEnable(p->rx_filter), acceptBroadcast(false),
       acceptMulticast(false), acceptUnicast(false),
-      acceptPerfect(false), acceptArp(false),
+      acceptPerfect(false), acceptArp(false), multicastHashEnable(false),
       physmem(p->pmem), intrTick(0), cpuPendingIntr(false),
       intrEvent(0), interface(0)
 {
@@ -115,30 +114,33 @@ NSGigE::NSGigE(Params *p)
                                        p->header_bus, this,
                                        &NSGigE::cacheAccess);
 
-        pioLatency = p->pio_latency * p->header_bus->clockRatio;
+        pioLatency = p->pio_latency * p->header_bus->clockRate;
 
         if (p->payload_bus)
             dmaInterface = new DMAInterface<Bus>(name() + ".dma",
                                                  p->header_bus,
-                                                 p->payload_bus, 1);
+                                                 p->payload_bus, 1,
+                                                 p->dma_no_allocate);
         else
             dmaInterface = new DMAInterface<Bus>(name() + ".dma",
                                                  p->header_bus,
-                                                 p->header_bus, 1);
+                                                 p->header_bus, 1,
+                                                 p->dma_no_allocate);
     } else if (p->payload_bus) {
         pioInterface = newPioInterface(name(), p->hier,
                                        p->payload_bus, this,
                                        &NSGigE::cacheAccess);
 
-        pioLatency = p->pio_latency * p->payload_bus->clockRatio;
+        pioLatency = p->pio_latency * p->payload_bus->clockRate;
 
         dmaInterface = new DMAInterface<Bus>(name() + ".dma",
                                              p->payload_bus,
-                                             p->payload_bus, 1);
+                                             p->payload_bus, 1,
+                                             p->dma_no_allocate);
     }
 
 
-    intrDelay = US2Ticks(p->intr_delay);
+    intrDelay = p->intr_delay;
     dmaReadDelay = p->dma_read_delay;
     dmaWriteDelay = p->dma_write_delay;
     dmaReadFactor = p->dma_read_factor;
@@ -471,8 +473,9 @@ NSGigE::regStats()
     coalescedTxDesc = totalTxDesc / postedInterrupts;
     coalescedRxOrn = totalRxOrn / postedInterrupts;
 
-    coalescedTotal = (totalSwi + totalRxIdle + totalRxOk + totalRxDesc + totalTxOk
-                      + totalTxIdle + totalTxDesc + totalRxOrn) / postedInterrupts;
+    coalescedTotal = (totalSwi + totalRxIdle + totalRxOk + totalRxDesc +
+                      totalTxOk + totalTxIdle + totalTxDesc +
+                      totalRxOrn) / postedInterrupts;
 
     txBandwidth = txBytes * Stats::constant(8) / simSeconds;
     rxBandwidth = rxBytes * Stats::constant(8) / simSeconds;
@@ -598,7 +601,7 @@ NSGigE::read(MemReqPtr &req, uint8_t *data)
                 reg &= ~(CR_RXD | CR_TXD | CR_TXR | CR_RXR);
                 break;
 
-              case CFG:
+              case CFGR:
                 reg = regs.config;
                 break;
 
@@ -635,7 +638,7 @@ NSGigE::read(MemReqPtr &req, uint8_t *data)
                 reg = regs.txdp_hi;
                 break;
 
-              case TXCFG:
+              case TX_CFG:
                 reg = regs.txcfg;
                 break;
 
@@ -651,7 +654,7 @@ NSGigE::read(MemReqPtr &req, uint8_t *data)
                 reg = regs.rxdp_hi;
                 break;
 
-              case RXCFG:
+              case RX_CFG:
                 reg = regs.rxcfg;
                 break;
 
@@ -677,7 +680,9 @@ NSGigE::read(MemReqPtr &req, uint8_t *data)
                 break;
 
               case RFDR:
-                switch (regs.rfcr & RFCR_RFADDR) {
+                uint16_t rfaddr = (uint16_t)(regs.rfcr & RFCR_RFADDR);
+                switch (rfaddr) {
+                  // Read from perfect match ROM octets
                   case 0x000:
                     reg = rom.perfectMatch[1];
                     reg = reg << 8;
@@ -692,9 +697,21 @@ NSGigE::read(MemReqPtr &req, uint8_t *data)
                     reg += rom.perfectMatch[4];
                     break;
                   default:
-                    panic("reading RFDR for something other than PMATCH!\n");
-                    // didn't implement other RFDR functionality b/c
-                    // driver didn't use it
+                    // Read filter hash table
+                    if (rfaddr >= FHASH_ADDR &&
+                        rfaddr < FHASH_ADDR + FHASH_SIZE) {
+
+                        // Only word-aligned reads supported
+                        if (rfaddr % 2)
+                            panic("unaligned read from filter hash table!");
+
+                        reg = rom.filterHash[rfaddr - FHASH_ADDR + 1] << 8;
+                        reg += rom.filterHash[rfaddr - FHASH_ADDR];
+                        break;
+                    }
+
+                    panic("reading RFDR for something other than pattern\
+                        matching or hashing! %#x\n", rfaddr);
                 }
                 break;
 
@@ -747,6 +764,10 @@ NSGigE::read(MemReqPtr &req, uint8_t *data)
                 reg = regs.tesr;
                 break;
 
+              case M5REG:
+                reg = params()->m5reg;
+                break;
+
               default:
                 panic("reading unimplemented register: addr=%#x", daddr);
             }
@@ -824,78 +845,95 @@ NSGigE::write(MemReqPtr &req, const uint8_t *data)
             }
             break;
 
-          case CFG:
-            if (reg & CFG_LNKSTS ||
-                reg & CFG_SPDSTS ||
-                reg & CFG_DUPSTS ||
-                reg & CFG_RESERVED ||
-                reg & CFG_T64ADDR ||
-                reg & CFG_PCI64_DET)
-                panic("writing to read-only or reserved CFG bits!\n");
-
-            regs.config |= reg & ~(CFG_LNKSTS | CFG_SPDSTS | CFG_DUPSTS |
-                                   CFG_RESERVED | CFG_T64ADDR | CFG_PCI64_DET);
+          case CFGR:
+            if (reg & CFGR_LNKSTS ||
+                reg & CFGR_SPDSTS ||
+                reg & CFGR_DUPSTS ||
+                reg & CFGR_RESERVED ||
+                reg & CFGR_T64ADDR ||
+                reg & CFGR_PCI64_DET)
+
+            // First clear all writable bits
+            regs.config &= CFGR_LNKSTS | CFGR_SPDSTS | CFGR_DUPSTS |
+                                   CFGR_RESERVED | CFGR_T64ADDR |
+                                   CFGR_PCI64_DET;
+            // Now set the appropriate writable bits
+            regs.config |= reg & ~(CFGR_LNKSTS | CFGR_SPDSTS | CFGR_DUPSTS |
+                                   CFGR_RESERVED | CFGR_T64ADDR |
+                                   CFGR_PCI64_DET);
 
 // all these #if 0's are because i don't THINK the kernel needs to
 // have these implemented. if there is a problem relating to one of
 // these, you may need to add functionality in.
 #if 0
-            if (reg & CFG_TBI_EN) ;
-            if (reg & CFG_MODE_1000) ;
+            if (reg & CFGR_TBI_EN) ;
+            if (reg & CFGR_MODE_1000) ;
 #endif
 
-            if (reg & CFG_AUTO_1000)
-                panic("CFG_AUTO_1000 not implemented!\n");
+            if (reg & CFGR_AUTO_1000)
+                panic("CFGR_AUTO_1000 not implemented!\n");
 
 #if 0
-            if (reg & CFG_PINT_DUPSTS ||
-                reg & CFG_PINT_LNKSTS ||
-                reg & CFG_PINT_SPDSTS)
+            if (reg & CFGR_PINT_DUPSTS ||
+                reg & CFGR_PINT_LNKSTS ||
+                reg & CFGR_PINT_SPDSTS)
                 ;
 
-            if (reg & CFG_TMRTEST) ;
-            if (reg & CFG_MRM_DIS) ;
-            if (reg & CFG_MWI_DIS) ;
+            if (reg & CFGR_TMRTEST) ;
+            if (reg & CFGR_MRM_DIS) ;
+            if (reg & CFGR_MWI_DIS) ;
 
-            if (reg & CFG_T64ADDR)
-                panic("CFG_T64ADDR is read only register!\n");
+            if (reg & CFGR_T64ADDR)
+                panic("CFGR_T64ADDR is read only register!\n");
 
-            if (reg & CFG_PCI64_DET)
-                panic("CFG_PCI64_DET is read only register!\n");
+            if (reg & CFGR_PCI64_DET)
+                panic("CFGR_PCI64_DET is read only register!\n");
 
-            if (reg & CFG_DATA64_EN) ;
-            if (reg & CFG_M64ADDR) ;
-            if (reg & CFG_PHY_RST) ;
-            if (reg & CFG_PHY_DIS) ;
+            if (reg & CFGR_DATA64_EN) ;
+            if (reg & CFGR_M64ADDR) ;
+            if (reg & CFGR_PHY_RST) ;
+            if (reg & CFGR_PHY_DIS) ;
 #endif
 
-            if (reg & CFG_EXTSTS_EN)
+            if (reg & CFGR_EXTSTS_EN)
                 extstsEnable = true;
             else
                 extstsEnable = false;
 
 #if 0
-              if (reg & CFG_REQALG) ;
-              if (reg & CFG_SB) ;
-              if (reg & CFG_POW) ;
-              if (reg & CFG_EXD) ;
-              if (reg & CFG_PESEL) ;
-              if (reg & CFG_BROM_DIS) ;
-              if (reg & CFG_EXT_125) ;
-              if (reg & CFG_BEM) ;
+              if (reg & CFGR_REQALG) ;
+              if (reg & CFGR_SB) ;
+              if (reg & CFGR_POW) ;
+              if (reg & CFGR_EXD) ;
+              if (reg & CFGR_PESEL) ;
+              if (reg & CFGR_BROM_DIS) ;
+              if (reg & CFGR_EXT_125) ;
+              if (reg & CFGR_BEM) ;
 #endif
             break;
 
           case MEAR:
-            regs.mear = reg;
+            // Clear writable bits
+            regs.mear &= MEAR_EEDO;
+            // Set appropriate writable bits
+            regs.mear |= reg & ~MEAR_EEDO;
+
+            // FreeBSD uses the EEPROM to read PMATCH (for the MAC address)
+            // even though it could get it through RFDR
+            if (reg & MEAR_EESEL) {
+                // Rising edge of clock
+                if (reg & MEAR_EECLK && !eepromClk)
+                    eepromKick();
+            }
+            else {
+                eepromState = eepromStart;
+                regs.mear &= ~MEAR_EEDI;
+            }
+
+            eepromClk = reg & MEAR_EECLK;
+
             // since phy is completely faked, MEAR_MD* don't matter
-            // and since the driver never uses MEAR_EE*, they don't
-            // matter
 #if 0
-            if (reg & MEAR_EEDI) ;
-            if (reg & MEAR_EEDO) ; // this one is read only
-            if (reg & MEAR_EECLK) ;
-            if (reg & MEAR_EESEL) ;
             if (reg & MEAR_MDIO) ;
             if (reg & MEAR_MDDIR) ;
             if (reg & MEAR_MDC) ;
@@ -941,14 +979,14 @@ NSGigE::write(MemReqPtr &req, const uint8_t *data)
             regs.txdp_hi = reg;
             break;
 
-          case TXCFG:
+          case TX_CFG:
             regs.txcfg = reg;
 #if 0
-            if (reg & TXCFG_CSI) ;
-            if (reg & TXCFG_HBI) ;
-            if (reg & TXCFG_MLB) ;
-            if (reg & TXCFG_ATP) ;
-            if (reg & TXCFG_ECRETRY) {
+            if (reg & TX_CFG_CSI) ;
+            if (reg & TX_CFG_HBI) ;
+            if (reg & TX_CFG_MLB) ;
+            if (reg & TX_CFG_ATP) ;
+            if (reg & TX_CFG_ECRETRY) {
                 /*
                  * this could easily be implemented, but considering
                  * the network is just a fake pipe, wouldn't make
@@ -956,12 +994,12 @@ NSGigE::write(MemReqPtr &req, const uint8_t *data)
                  */
             }
 
-            if (reg & TXCFG_BRST_DIS) ;
+            if (reg & TX_CFG_BRST_DIS) ;
 #endif
 
 #if 0
             /* we handle our own DMA, ignore the kernel's exhortations */
-            if (reg & TXCFG_MXDMA) ;
+            if (reg & TX_CFG_MXDMA) ;
 #endif
 
             // also, we currently don't care about fill/drain
@@ -972,7 +1010,11 @@ NSGigE::write(MemReqPtr &req, const uint8_t *data)
             break;
 
           case GPIOR:
-            regs.gpior = reg;
+            // Only write writable bits
+            regs.gpior &= GPIOR_UNUSED | GPIOR_GP5_IN | GPIOR_GP4_IN
+                        | GPIOR_GP3_IN | GPIOR_GP2_IN | GPIOR_GP1_IN;
+            regs.gpior |= reg & ~(GPIOR_UNUSED | GPIOR_GP5_IN | GPIOR_GP4_IN
+                                | GPIOR_GP3_IN | GPIOR_GP2_IN | GPIOR_GP1_IN);
             /* these just control general purpose i/o pins, don't matter */
             break;
 
@@ -985,23 +1027,23 @@ NSGigE::write(MemReqPtr &req, const uint8_t *data)
             regs.rxdp_hi = reg;
             break;
 
-          case RXCFG:
+          case RX_CFG:
             regs.rxcfg = reg;
 #if 0
-            if (reg & RXCFG_AEP) ;
-            if (reg & RXCFG_ARP) ;
-            if (reg & RXCFG_STRIPCRC) ;
-            if (reg & RXCFG_RX_RD) ;
-            if (reg & RXCFG_ALP) ;
-            if (reg & RXCFG_AIRL) ;
+            if (reg & RX_CFG_AEP) ;
+            if (reg & RX_CFG_ARP) ;
+            if (reg & RX_CFG_STRIPCRC) ;
+            if (reg & RX_CFG_RX_RD) ;
+            if (reg & RX_CFG_ALP) ;
+            if (reg & RX_CFG_AIRL) ;
 
             /* we handle our own DMA, ignore what kernel says about it */
-            if (reg & RXCFG_MXDMA) ;
+            if (reg & RX_CFG_MXDMA) ;
 
             //also, we currently don't care about fill/drain thresholds
             //though this may change in the future with more realistic
             //networks or a driver which changes it according to feedback
-            if (reg & (RXCFG_DRTH | RXCFG_DRTH0)) ;
+            if (reg & (RX_CFG_DRTH | RX_CFG_DRTH0)) ;
 #endif
             break;
 
@@ -1029,14 +1071,14 @@ NSGigE::write(MemReqPtr &req, const uint8_t *data)
             acceptUnicast = (reg & RFCR_AAU) ? true : false;
             acceptPerfect = (reg & RFCR_APM) ? true : false;
             acceptArp = (reg & RFCR_AARP) ? true : false;
+            multicastHashEnable = (reg & RFCR_MHEN) ? true : false;
 
 #if 0
             if (reg & RFCR_APAT)
                 panic("RFCR_APAT not implemented!\n");
 #endif
-
-            if (reg & RFCR_MHEN || reg & RFCR_UHEN)
-                panic("hash filtering not implemented!\n");
+            if (reg & RFCR_UHEN)
+                panic("Unicast hash filtering not used by drivers!\n");
 
             if (reg & RFCR_ULM)
                 panic("RFCR_ULM not implemented!\n");
@@ -1044,10 +1086,41 @@ NSGigE::write(MemReqPtr &req, const uint8_t *data)
             break;
 
           case RFDR:
-            panic("the driver never writes to RFDR, something is wrong!\n");
+            uint16_t rfaddr = (uint16_t)(regs.rfcr & RFCR_RFADDR);
+            switch (rfaddr) {
+              case 0x000:
+                rom.perfectMatch[0] = (uint8_t)reg;
+                rom.perfectMatch[1] = (uint8_t)(reg >> 8);
+                break;
+              case 0x002:
+                rom.perfectMatch[2] = (uint8_t)reg;
+                rom.perfectMatch[3] = (uint8_t)(reg >> 8);
+                break;
+              case 0x004:
+                rom.perfectMatch[4] = (uint8_t)reg;
+                rom.perfectMatch[5] = (uint8_t)(reg >> 8);
+                break;
+              default:
+
+                if (rfaddr >= FHASH_ADDR &&
+                    rfaddr < FHASH_ADDR + FHASH_SIZE) {
+
+                    // Only word-aligned writes supported
+                    if (rfaddr % 2)
+                        panic("unaligned write to filter hash table!");
+
+                    rom.filterHash[rfaddr - FHASH_ADDR] = (uint8_t)reg;
+                    rom.filterHash[rfaddr - FHASH_ADDR + 1]
+                        = (uint8_t)(reg >> 8);
+                    break;
+                }
+                panic("writing RFDR for something other than pattern matching\
+                    or hashing! %#x\n", rfaddr);
+            }
 
           case BRAR:
-            panic("the driver never uses BRAR, something is wrong!\n");
+            regs.brar = reg;
+            break;
 
           case BRDR:
             panic("the driver never uses BRDR, something is wrong!\n");
@@ -1068,7 +1141,6 @@ NSGigE::write(MemReqPtr &req, const uint8_t *data)
 
           case VDR:
             panic("the driver never uses VDR, something is wrong!\n");
-            break;
 
           case CCSR:
             /* not going to implement clockrun stuff */
@@ -1095,12 +1167,16 @@ NSGigE::write(MemReqPtr &req, const uint8_t *data)
             panic("TBISR is read only register!\n");
 
           case TANAR:
-            regs.tanar = reg;
-            if (reg & TANAR_PS2)
-                panic("this isn't used in driver, something wrong!\n");
+            // Only write the writable bits
+            regs.tanar &= TANAR_RF1 | TANAR_RF2 | TANAR_UNUSED;
+            regs.tanar |= reg & ~(TANAR_RF1 | TANAR_RF2 | TANAR_UNUSED);
+
+            // Pause capability unimplemented
+#if 0
+            if (reg & TANAR_PS2) ;
+            if (reg & TANAR_PS1) ;
+#endif
 
-            if (reg & TANAR_PS1)
-                panic("this isn't used in driver, something wrong!\n");
             break;
 
           case TANLPAR:
@@ -1352,8 +1428,8 @@ void
 NSGigE::regsReset()
 {
     memset(&regs, 0, sizeof(regs));
-    regs.config = CFG_LNKSTS;
-    regs.mear = MEAR_MDDIR | MEAR_EEDO;
+    regs.config = (CFGR_LNKSTS | CFGR_TBI_EN | CFGR_MODE_1000);
+    regs.mear = 0x12;
     regs.txcfg = 0x120; // set drain threshold to 1024 bytes and
                         // fill threshold to 32 bytes
     regs.rxcfg = 0x4;   // set drain threshold to 16 bytes
@@ -1361,6 +1437,7 @@ NSGigE::regsReset()
     regs.mibc = MIBC_FRZ;
     regs.vdr = 0x81;    // set the vlan tag type to 802.1q
     regs.tesr = 0xc000; // TBI capable of both full and half duplex
+    regs.brar = 0xffffffff;
 
     extstsEnable = false;
     acceptBroadcast = false;
@@ -1480,13 +1557,19 @@ NSGigE::rxKick()
     DPRINTF(EthernetSM, "receive kick rxState=%s (rxBuf.size=%d)\n",
             NsRxStateStrings[rxState], rxFifo.size());
 
-    if (rxKickTick > curTick) {
-        DPRINTF(EthernetSM, "receive kick exiting, can't run till %d\n",
-                rxKickTick);
-        return;
+  next:
+    if (clock) {
+        if (rxKickTick > curTick) {
+            DPRINTF(EthernetSM, "receive kick exiting, can't run till %d\n",
+                    rxKickTick);
+
+            goto exit;
+        }
+
+        // Go to the next state machine clock tick.
+        rxKickTick = curTick + cycles(1);
     }
 
-  next:
     switch(rxDmaState) {
       case dmaReadWaiting:
         if (doRxDmaRead())
@@ -1554,8 +1637,7 @@ NSGigE::rxKick()
         if (rxDmaState != dmaIdle)
             goto exit;
 
-        DPRINTF(EthernetDesc,
-                "rxDescCache: addr=%08x read descriptor\n",
+        DPRINTF(EthernetDesc, "rxDescCache: addr=%08x read descriptor\n",
                 regs.rxdp & 0x3fffffff);
         DPRINTF(EthernetDesc,
                 "rxDescCache: link=%08x bufptr=%08x cmdsts=%08x extsts=%08x\n",
@@ -1597,8 +1679,10 @@ NSGigE::rxKick()
                     DPRINTF(Ethernet, "ID is %d\n", ip->id());
                     TcpPtr tcp(ip);
                     if (tcp) {
-                        DPRINTF(Ethernet, "Src Port=%d, Dest Port=%d\n",
-                                tcp->sport(), tcp->dport());
+                        DPRINTF(Ethernet,
+                                "Src Port=%d, Dest Port=%d, Seq=%d, Ack=%d\n",
+                                tcp->sport(), tcp->dport(), tcp->seq(),
+                                tcp->ack());
                     }
                 }
             }
@@ -1774,7 +1858,6 @@ NSGigE::rxKick()
 
     DPRINTF(EthernetSM, "entering next rxState=%s\n",
             NsRxStateStrings[rxState]);
-
     goto next;
 
   exit:
@@ -1783,6 +1866,9 @@ NSGigE::rxKick()
      */
     DPRINTF(EthernetSM, "rx state machine exited rxState=%s\n",
             NsRxStateStrings[rxState]);
+
+    if (clock && !rxKickEvent.scheduled())
+        rxKickEvent.schedule(rxKickTick);
 }
 
 void
@@ -1803,14 +1889,15 @@ NSGigE::transmit()
                 DPRINTF(Ethernet, "ID is %d\n", ip->id());
                 TcpPtr tcp(ip);
                 if (tcp) {
-                    DPRINTF(Ethernet, "Src Port=%d, Dest Port=%d\n",
-                            tcp->sport(), tcp->dport());
+                    DPRINTF(Ethernet,
+                            "Src Port=%d, Dest Port=%d, Seq=%d, Ack=%d\n",
+                            tcp->sport(), tcp->dport(), tcp->seq(), tcp->ack());
                 }
             }
         }
 #endif
 
-        DDUMP(Ethernet, txFifo.front()->data, txFifo.front()->length);
+        DDUMP(EthernetData, txFifo.front()->data, txFifo.front()->length);
         txBytes += txFifo.front()->length;
         txPackets++;
 
@@ -1830,7 +1917,7 @@ NSGigE::transmit()
 
    if (!txFifo.empty() && !txEvent.scheduled()) {
        DPRINTF(Ethernet, "reschedule transmit\n");
-       txEvent.schedule(curTick + 1000);
+       txEvent.schedule(curTick + retryTime);
    }
 }
 
@@ -1944,14 +2031,18 @@ NSGigE::txKick()
     DPRINTF(EthernetSM, "transmit kick txState=%s\n",
             NsTxStateStrings[txState]);
 
-    if (txKickTick > curTick) {
-        DPRINTF(EthernetSM, "transmit kick exiting, can't run till %d\n",
-                txKickTick);
+  next:
+    if (clock) {
+        if (txKickTick > curTick) {
+            DPRINTF(EthernetSM, "transmit kick exiting, can't run till %d\n",
+                    txKickTick);
+            goto exit;
+        }
 
-        return;
+        // Go to the next state machine clock tick.
+        txKickTick = curTick + cycles(1);
     }
 
-  next:
     switch(txDmaState) {
       case dmaReadWaiting:
         if (doTxDmaRead())
@@ -2013,6 +2104,8 @@ NSGigE::txKick()
         if (txDmaState != dmaIdle)
             goto exit;
 
+        DPRINTF(EthernetDesc, "txDescCache: addr=%08x read descriptor\n",
+                regs.txdp & 0x3fffffff);
         DPRINTF(EthernetDesc,
                 "txDescCache: link=%08x bufptr=%08x cmdsts=%08x extsts=%08x\n",
                 txDescCache.link, txDescCache.bufptr, txDescCache.cmdsts,
@@ -2177,7 +2270,12 @@ NSGigE::txKick()
         if (txDescCache.cmdsts & CMDSTS_INTR)
             devIntrPost(ISR_TXDESC);
 
-        txState = txAdvance;
+        if (!txEnable) {
+            DPRINTF(EthernetSM, "halting TX state machine\n");
+            txState = txIdle;
+            goto exit;
+        } else
+            txState = txAdvance;
         break;
 
       case txAdvance:
@@ -2206,7 +2304,6 @@ NSGigE::txKick()
 
     DPRINTF(EthernetSM, "entering next txState=%s\n",
             NsTxStateStrings[txState]);
-
     goto next;
 
   exit:
@@ -2215,6 +2312,110 @@ NSGigE::txKick()
      */
     DPRINTF(EthernetSM, "tx state machine exited txState=%s\n",
             NsTxStateStrings[txState]);
+
+    if (clock && !txKickEvent.scheduled())
+        txKickEvent.schedule(txKickTick);
+}
+
+/**
+ * Advance the EEPROM state machine
+ * Called on rising edge of EEPROM clock bit in MEAR
+ */
+void
+NSGigE::eepromKick()
+{
+    switch (eepromState) {
+
+      case eepromStart:
+
+        // Wait for start bit
+        if (regs.mear & MEAR_EEDI) {
+            // Set up to get 2 opcode bits
+            eepromState = eepromGetOpcode;
+            eepromBitsToRx = 2;
+            eepromOpcode = 0;
+        }
+        break;
+
+      case eepromGetOpcode:
+        eepromOpcode <<= 1;
+        eepromOpcode += (regs.mear & MEAR_EEDI) ? 1 : 0;
+        --eepromBitsToRx;
+
+        // Done getting opcode
+        if (eepromBitsToRx == 0) {
+            if (eepromOpcode != EEPROM_READ)
+                panic("only EEPROM reads are implemented!");
+
+            // Set up to get address
+            eepromState = eepromGetAddress;
+            eepromBitsToRx = 6;
+            eepromAddress = 0;
+        }
+        break;
+
+      case eepromGetAddress:
+        eepromAddress <<= 1;
+        eepromAddress += (regs.mear & MEAR_EEDI) ? 1 : 0;
+        --eepromBitsToRx;
+
+        // Done getting address
+        if (eepromBitsToRx == 0) {
+
+            if (eepromAddress >= EEPROM_SIZE)
+                panic("EEPROM read access out of range!");
+
+            switch (eepromAddress) {
+
+              case EEPROM_PMATCH2_ADDR:
+                eepromData = rom.perfectMatch[5];
+                eepromData <<= 8;
+                eepromData += rom.perfectMatch[4];
+                break;
+
+              case EEPROM_PMATCH1_ADDR:
+                eepromData = rom.perfectMatch[3];
+                eepromData <<= 8;
+                eepromData += rom.perfectMatch[2];
+                break;
+
+              case EEPROM_PMATCH0_ADDR:
+                eepromData = rom.perfectMatch[1];
+                eepromData <<= 8;
+                eepromData += rom.perfectMatch[0];
+                break;
+
+              default:
+                panic("FreeBSD driver only uses EEPROM to read PMATCH!");
+            }
+            // Set up to read data
+            eepromState = eepromRead;
+            eepromBitsToRx = 16;
+
+            // Clear data in bit
+            regs.mear &= ~MEAR_EEDI;
+        }
+        break;
+
+      case eepromRead:
+        // Clear Data Out bit
+        regs.mear &= ~MEAR_EEDO;
+        // Set bit to value of current EEPROM bit
+        regs.mear |= (eepromData & 0x8000) ? MEAR_EEDO : 0x0;
+
+        eepromData <<= 1;
+        --eepromBitsToRx;
+
+        // All done
+        if (eepromBitsToRx == 0) {
+            eepromState = eepromStart;
+        }
+        break;
+
+      default:
+        panic("invalid EEPROM state");
+    }
+
 }
 
 void
@@ -2228,9 +2429,9 @@ NSGigE::transferDone()
     DPRINTF(Ethernet, "transfer complete: data in txFifo...schedule xmit\n");
 
     if (txEvent.scheduled())
-        txEvent.reschedule(curTick + 1);
+        txEvent.reschedule(curTick + cycles(1));
     else
-        txEvent.schedule(curTick + 1);
+        txEvent.schedule(curTick + cycles(1));
 }
 
 bool
@@ -2263,6 +2464,9 @@ NSGigE::rxFilter(const PacketPtr &packet)
         if (acceptMulticast)
             drop = false;
 
+        // Multicast hashing faked - all packets accepted
+        if (multicastHashEnable)
+            drop = false;
     }
 
     if (drop) {
@@ -2284,20 +2488,36 @@ NSGigE::recvPacket(PacketPtr packet)
 
     if (!rxEnable) {
         DPRINTF(Ethernet, "receive disabled...packet dropped\n");
-        debug_break();
         interface->recvDone();
         return true;
     }
 
-    if (rxFilterEnable && rxFilter(packet)) {
+    if (!rxFilterEnable) {
+        DPRINTF(Ethernet,
+            "receive packet filtering disabled . . . packet dropped\n");
+        interface->recvDone();
+        return true;
+    }
+
+    if (rxFilter(packet)) {
         DPRINTF(Ethernet, "packet filtered...dropped\n");
         interface->recvDone();
         return true;
     }
 
     if (rxFifo.avail() < packet->length) {
-        DPRINTF(Ethernet,
-                "packet will not fit in receive buffer...packet dropped\n");
+#if TRACING_ON
+        IpPtr ip(packet);
+        TcpPtr tcp(ip);
+        if (ip) {
+            DPRINTF(Ethernet,
+                    "packet won't fit in receive buffer...pkt ID %d dropped\n",
+                    ip->id());
+            if (tcp) {
+                DPRINTF(Ethernet, "Seq=%d\n", tcp->seq());
+            }
+        }
+#endif
         droppedPackets++;
         devIntrPost(ISR_RXORN);
         return false;
@@ -2354,6 +2574,8 @@ NSGigE::serialize(ostream &os)
     SERIALIZE_SCALAR(regs.pcr);
     SERIALIZE_SCALAR(regs.rfcr);
     SERIALIZE_SCALAR(regs.rfdr);
+    SERIALIZE_SCALAR(regs.brar);
+    SERIALIZE_SCALAR(regs.brdr);
     SERIALIZE_SCALAR(regs.srr);
     SERIALIZE_SCALAR(regs.mibc);
     SERIALIZE_SCALAR(regs.vrcr);
@@ -2368,6 +2590,7 @@ NSGigE::serialize(ostream &os)
     SERIALIZE_SCALAR(regs.tesr);
 
     SERIALIZE_ARRAY(rom.perfectMatch, ETH_ADDR_LEN);
+    SERIALIZE_ARRAY(rom.filterHash, FHASH_SIZE);
 
     SERIALIZE_SCALAR(ioEnable);
 
@@ -2383,6 +2606,7 @@ NSGigE::serialize(ostream &os)
     bool txPacketExists = txPacket;
     SERIALIZE_SCALAR(txPacketExists);
     if (txPacketExists) {
+        txPacket->length = txPacketBufPtr - txPacket->data;
         txPacket->serialize("txPacket", os);
         uint32_t txPktBufPtr = (uint32_t) (txPacketBufPtr - txPacket->data);
         SERIALIZE_SCALAR(txPktBufPtr);
@@ -2410,6 +2634,7 @@ NSGigE::serialize(ostream &os)
     SERIALIZE_SCALAR(rxDescCache.bufptr);
     SERIALIZE_SCALAR(rxDescCache.cmdsts);
     SERIALIZE_SCALAR(rxDescCache.extsts);
+    SERIALIZE_SCALAR(extstsEnable);
 
     /*
      * Serialize tx state machine
@@ -2422,6 +2647,7 @@ NSGigE::serialize(ostream &os)
     SERIALIZE_SCALAR(txDescCnt);
     int txDmaState = this->txDmaState;
     SERIALIZE_SCALAR(txDmaState);
+    SERIALIZE_SCALAR(txKickTick);
 
     /*
      * Serialize rx state machine
@@ -2435,8 +2661,18 @@ NSGigE::serialize(ostream &os)
     SERIALIZE_SCALAR(rxDescCnt);
     int rxDmaState = this->rxDmaState;
     SERIALIZE_SCALAR(rxDmaState);
+    SERIALIZE_SCALAR(rxKickTick);
 
-    SERIALIZE_SCALAR(extstsEnable);
+    /*
+     * Serialize EEPROM state machine
+     */
+    int eepromState = this->eepromState;
+    SERIALIZE_SCALAR(eepromState);
+    SERIALIZE_SCALAR(eepromClk);
+    SERIALIZE_SCALAR(eepromBitsToRx);
+    SERIALIZE_SCALAR(eepromOpcode);
+    SERIALIZE_SCALAR(eepromAddress);
+    SERIALIZE_SCALAR(eepromData);
 
     /*
      * If there's a pending transmit, store the time so we can
@@ -2454,6 +2690,7 @@ NSGigE::serialize(ostream &os)
     SERIALIZE_SCALAR(acceptUnicast);
     SERIALIZE_SCALAR(acceptPerfect);
     SERIALIZE_SCALAR(acceptArp);
+    SERIALIZE_SCALAR(multicastHashEnable);
 
     /*
      * Keep track of pending interrupt status.
@@ -2493,6 +2730,8 @@ NSGigE::unserialize(Checkpoint *cp, const std::string &section)
     UNSERIALIZE_SCALAR(regs.pcr);
     UNSERIALIZE_SCALAR(regs.rfcr);
     UNSERIALIZE_SCALAR(regs.rfdr);
+    UNSERIALIZE_SCALAR(regs.brar);
+    UNSERIALIZE_SCALAR(regs.brdr);
     UNSERIALIZE_SCALAR(regs.srr);
     UNSERIALIZE_SCALAR(regs.mibc);
     UNSERIALIZE_SCALAR(regs.vrcr);
@@ -2507,6 +2746,7 @@ NSGigE::unserialize(Checkpoint *cp, const std::string &section)
     UNSERIALIZE_SCALAR(regs.tesr);
 
     UNSERIALIZE_ARRAY(rom.perfectMatch, ETH_ADDR_LEN);
+    UNSERIALIZE_ARRAY(rom.filterHash, FHASH_SIZE);
 
     UNSERIALIZE_SCALAR(ioEnable);
 
@@ -2556,6 +2796,7 @@ NSGigE::unserialize(Checkpoint *cp, const std::string &section)
     UNSERIALIZE_SCALAR(rxDescCache.bufptr);
     UNSERIALIZE_SCALAR(rxDescCache.cmdsts);
     UNSERIALIZE_SCALAR(rxDescCache.extsts);
+    UNSERIALIZE_SCALAR(extstsEnable);
 
     /*
      * unserialize tx state machine
@@ -2570,6 +2811,9 @@ NSGigE::unserialize(Checkpoint *cp, const std::string &section)
     int txDmaState;
     UNSERIALIZE_SCALAR(txDmaState);
     this->txDmaState = (DmaState) txDmaState;
+    UNSERIALIZE_SCALAR(txKickTick);
+    if (txKickTick)
+        txKickEvent.schedule(txKickTick);
 
     /*
      * unserialize rx state machine
@@ -2585,10 +2829,23 @@ NSGigE::unserialize(Checkpoint *cp, const std::string &section)
     int rxDmaState;
     UNSERIALIZE_SCALAR(rxDmaState);
     this->rxDmaState = (DmaState) rxDmaState;
+    UNSERIALIZE_SCALAR(rxKickTick);
+    if (rxKickTick)
+        rxKickEvent.schedule(rxKickTick);
 
-    UNSERIALIZE_SCALAR(extstsEnable);
+    /*
+     * Unserialize EEPROM state machine
+     */
+    int eepromState;
+    UNSERIALIZE_SCALAR(eepromState);
+    this->eepromState = (EEPROMState) eepromState;
+    UNSERIALIZE_SCALAR(eepromClk);
+    UNSERIALIZE_SCALAR(eepromBitsToRx);
+    UNSERIALIZE_SCALAR(eepromOpcode);
+    UNSERIALIZE_SCALAR(eepromAddress);
+    UNSERIALIZE_SCALAR(eepromData);
 
-     /*
+    /*
      * If there's a pending transmit, reschedule it now
      */
     Tick transmitTick;
@@ -2605,6 +2862,7 @@ NSGigE::unserialize(Checkpoint *cp, const std::string &section)
     UNSERIALIZE_SCALAR(acceptUnicast);
     UNSERIALIZE_SCALAR(acceptPerfect);
     UNSERIALIZE_SCALAR(acceptArp);
+    UNSERIALIZE_SCALAR(multicastHashEnable);
 
     /*
      * Keep track of pending interrupt status.
@@ -2668,6 +2926,7 @@ REGISTER_SIM_OBJECT("NSGigEInt", NSGigEInt)
 BEGIN_DECLARE_SIM_OBJECT_PARAMS(NSGigE)
 
     Param<Addr> addr;
+    Param<Tick> clock;
     Param<Tick> tx_delay;
     Param<Tick> rx_delay;
     Param<Tick> intr_delay;
@@ -2693,20 +2952,22 @@ BEGIN_DECLARE_SIM_OBJECT_PARAMS(NSGigE)
     Param<uint32_t> pci_func;
     Param<uint32_t> tx_fifo_size;
     Param<uint32_t> rx_fifo_size;
+    Param<uint32_t> m5reg;
+    Param<bool> dma_no_allocate;
 
 END_DECLARE_SIM_OBJECT_PARAMS(NSGigE)
 
 BEGIN_INIT_SIM_OBJECT_PARAMS(NSGigE)
 
     INIT_PARAM(addr, "Device Address"),
-    INIT_PARAM_DFLT(tx_delay, "Transmit Delay", 1000),
-    INIT_PARAM_DFLT(rx_delay, "Receive Delay", 1000),
-    INIT_PARAM_DFLT(intr_delay, "Interrupt Delay in microseconds", 0),
+    INIT_PARAM(clock, "State machine processor frequency"),
+    INIT_PARAM(tx_delay, "Transmit Delay"),
+    INIT_PARAM(rx_delay, "Receive Delay"),
+    INIT_PARAM(intr_delay, "Interrupt Delay in microseconds"),
     INIT_PARAM(mmu, "Memory Controller"),
     INIT_PARAM(physmem, "Physical Memory"),
     INIT_PARAM_DFLT(rx_filter, "Enable Receive Filter", true),
-    INIT_PARAM_DFLT(hardware_address, "Ethernet Hardware Address",
-                    "00:99:00:00:00:01"),
+    INIT_PARAM(hardware_address, "Ethernet Hardware Address"),
     INIT_PARAM_DFLT(io_bus, "The IO Bus to attach to for headers", NULL),
     INIT_PARAM_DFLT(payload_bus, "The IO Bus to attach to for payload", NULL),
     INIT_PARAM_DFLT(hier, "Hierarchy global variables", &defaultHierParams),
@@ -2724,7 +2985,9 @@ BEGIN_INIT_SIM_OBJECT_PARAMS(NSGigE)
     INIT_PARAM(pci_dev, "PCI device number"),
     INIT_PARAM(pci_func, "PCI function code"),
     INIT_PARAM_DFLT(tx_fifo_size, "max size in bytes of txFifo", 131072),
-    INIT_PARAM_DFLT(rx_fifo_size, "max size in bytes of rxFifo", 131072)
+    INIT_PARAM_DFLT(rx_fifo_size, "max size in bytes of rxFifo", 131072),
+    INIT_PARAM(m5reg, "m5 register"),
+    INIT_PARAM_DFLT(dma_no_allocate, "Should DMA reads allocate cache lines", true)
 
 END_INIT_SIM_OBJECT_PARAMS(NSGigE)
 
@@ -2742,6 +3005,7 @@ CREATE_SIM_OBJECT(NSGigE)
     params->deviceNum = pci_dev;
     params->functionNum = pci_func;
 
+    params->clock = clock;
     params->intr_delay = intr_delay;
     params->pmem = physmem;
     params->tx_delay = tx_delay;
@@ -2760,6 +3024,8 @@ CREATE_SIM_OBJECT(NSGigE)
     params->eaddr = hardware_address;
     params->tx_fifo_size = tx_fifo_size;
     params->rx_fifo_size = rx_fifo_size;
+    params->m5reg = m5reg;
+    params->dma_no_allocate = dma_no_allocate;
     return new NSGigE(params);
 }