Many files:
[gem5.git] / dev / sinic.cc
index 9535a58ca4f6b0fa0ab5a4d77a92a7982036c81b..7f4a7f8f03cee93b5629c7afa56a6724cadfb65c 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
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include <cstdio>
 #include <deque>
+#include <limits>
 #include <string>
 
 #include "base/inet.hh"
 #include "cpu/exec_context.hh"
 #include "cpu/intr_control.hh"
-#include "dev/dma.hh"
 #include "dev/etherlink.hh"
 #include "dev/sinic.hh"
 #include "dev/pciconfigall.hh"
-#include "mem/bus/bus.hh"
-#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/packet.hh"
 #include "sim/builder.hh"
 #include "sim/debug.hh"
 #include "sim/eventq.hh"
 #include "sim/host.hh"
 #include "sim/stats.hh"
-#include "targetarch/vtophys.hh"
+#include "arch/vtophys.hh"
 
 using namespace Net;
+using namespace TheISA;
 
 namespace Sinic {
 
@@ -78,15 +73,15 @@ const char *TxStateStrings[] =
 // Sinic PCI Device
 //
 Base::Base(Params *p)
-    : PciDev(p), rxEnable(false), txEnable(false), cycleTime(p->cycle_time),
+    : PciDev(p), rxEnable(false), txEnable(false), clock(p->clock),
       intrDelay(p->intr_delay), intrTick(0), cpuIntrEnable(false),
       cpuPendingIntr(false), intrEvent(0), interface(NULL)
 {
 }
 
 Device::Device(Params *p)
-    : Base(p), plat(p->plat), physmem(p->physmem),
-      rxFifo(p->rx_fifo_size), txFifo(p->tx_fifo_size),
+    : Base(p), plat(p->plat), physmem(p->physmem), rxUnique(0), txUnique(0),
+      virtualRegs(p->virtual_count < 1 ? 1 : p->virtual_count),
       rxKickTick(0), txKickTick(0),
       txEvent(this), rxDmaEvent(this), txDmaEvent(this),
       dmaReadDelay(p->dma_read_delay), dmaReadFactor(p->dma_read_factor),
@@ -94,29 +89,6 @@ Device::Device(Params *p)
 {
     reset();
 
-    if (p->header_bus) {
-        pioInterface = newPioInterface(p->name, p->hier, p->header_bus, this,
-                                       &Device::cacheAccess);
-
-        pioLatency = p->pio_latency * p->header_bus->clockRatio;
-
-        if (p->payload_bus)
-            dmaInterface = new DMAInterface<Bus>(p->name + ".dma",
-                                                 p->header_bus, p->payload_bus,
-                                                 1);
-        else
-            dmaInterface = new DMAInterface<Bus>(p->name + ".dma",
-                                                 p->header_bus, p->header_bus,
-                                                 1);
-    } else if (p->payload_bus) {
-        pioInterface = newPioInterface(p->name, p->hier, p->payload_bus, this,
-                                       &Device::cacheAccess);
-
-        pioLatency = p->pio_latency * p->payload_bus->clockRatio;
-
-        dmaInterface = new DMAInterface<Bus>(p->name + ".dma", p->payload_bus,
-                                             p->payload_bus, 1);
-    }
 }
 
 Device::~Device()
@@ -292,189 +264,262 @@ Device::regStats()
     rxPacketRate = rxPackets / simSeconds;
 }
 
-/**
- * This is to write to the PCI general configuration registers
- */
 void
-Device::WriteConfig(int offset, int size, uint32_t data)
+Device::prepareIO(int cpu, int index)
 {
-    switch (offset) {
-      case PCI0_BASE_ADDR0:
-        // Need to catch writes to BARs to update the PIO interface
-        PciDev::WriteConfig(offset, size, data);
-        if (BARAddrs[0] != 0) {
-            if (pioInterface)
-                pioInterface->addAddrRange(RangeSize(BARAddrs[0], BARSize[0]));
-
-            BARAddrs[0] &= EV5::PAddrUncachedMask;
-        }
-        break;
+    int size = virtualRegs.size();
+    if (index > size)
+        panic("Trying to access a vnic that doesn't exist %d > %d\n",
+              index, size);
+}
 
-      default:
-        PciDev::WriteConfig(offset, size, data);
-    }
+void
+Device::prepareRead(int cpu, int index)
+{
+    using namespace Regs;
+    prepareIO(cpu, index);
+
+    VirtualReg &vnic = virtualRegs[index];
+
+    // update rx registers
+    uint64_t rxdone = vnic.RxDone;
+    rxdone = set_RxDone_Packets(rxdone, rxFifo.countPacketsAfter(rxFifoPtr));
+    rxdone = set_RxDone_Empty(rxdone, rxFifo.empty());
+    rxdone = set_RxDone_High(rxdone, rxFifo.size() > regs.RxFifoMark);
+    rxdone = set_RxDone_NotHigh(rxdone, rxLow);
+    regs.RxData = vnic.RxData;
+    regs.RxDone = rxdone;
+    regs.RxWait = rxdone;
+
+    // update tx regsiters
+    uint64_t txdone = vnic.TxDone;
+    txdone = set_TxDone_Packets(txdone, txFifo.packets());
+    txdone = set_TxDone_Full(txdone, txFifo.avail() < regs.TxMaxCopy);
+    txdone = set_TxDone_Low(txdone, txFifo.size() < regs.TxFifoMark);
+    regs.TxData = vnic.TxData;
+    regs.TxDone = txdone;
+    regs.TxWait = txdone;
+}
+
+void
+Device::prepareWrite(int cpu, int index)
+{
+    prepareIO(cpu, index);
 }
 
 /**
- * This reads the device registers, which are detailed in the NS83820
- * spec sheet
+ * I/O read of device register
  */
-Fault
-Device::read(MemReqPtr &req, uint8_t *data)
+Tick
+Device::read(Packet &pkt)
 {
-    assert(config.hdr.command & PCI_CMD_MSE);
-
-    //The mask is to give you only the offset into the device register file
-    Addr daddr = req->paddr & 0xfff;
-
-    if (Regs::regSize(daddr) == 0)
-        panic("invalid address: da=%#x pa=%#x va=%#x size=%d",
-              daddr, req->paddr, req->vaddr, req->size);
-
-    if (req->size != Regs::regSize(daddr))
-        panic("invalid size for reg %s: da=%#x pa=%#x va=%#x size=%d",
-              Regs::regName(daddr), daddr, req->paddr, req->vaddr, req->size);
-
-    DPRINTF(EthernetPIO, "read reg=%s da=%#x pa=%#x va=%#x size=%d\n",
-            Regs::regName(daddr), daddr, req->paddr, req->vaddr, req->size);
-
-    uint32_t &reg32 = *(uint32_t *)data;
-    uint64_t &reg64 = *(uint64_t *)data;
+    assert(config.command & PCI_CMD_MSE);
+    assert(pkt.addr >= BARAddrs[0] && pkt.size < BARSize[0]);
+
+    int cpu = pkt.req->getCpuNum();
+    Addr daddr = pkt.addr - BARAddrs[0];
+    Addr index = daddr >> Regs::VirtualShift;
+    Addr raddr = daddr & Regs::VirtualMask;
+
+    pkt.time += pioDelay;
+    pkt.allocate();
+
+    if (!regValid(raddr))
+        panic("invalid register: cpu=%d vnic=%d da=%#x pa=%#x size=%d",
+              cpu, index, daddr, pkt.addr, pkt.size);
+
+    const Regs::Info &info = regInfo(raddr);
+    if (!info.read)
+        panic("read %s (write only): "
+              "cpu=%d vnic=%d da=%#x pa=%#x size=%d",
+              info.name, cpu, index, daddr, pkt.addr, pkt.size);
+
+        panic("read %s (invalid size): "
+              "cpu=%d vnic=%d da=%#x pa=%#x size=%d",
+              info.name, cpu, index, daddr, pkt.addr, pkt.size);
+
+    prepareRead(cpu, index);
+
+    uint64_t value = 0;
+    if (pkt.size == 4) {
+        uint32_t reg = regData32(raddr);
+        pkt.set(reg);
+        value = reg;
+    }
 
-    switch (daddr) {
-      case Regs::Config:
-        reg32 = regs.Config;
-        break;
+    if (pkt.size == 8) {
+        uint64_t reg = regData64(raddr);
+        pkt.set(reg);
+        value = reg;
+    }
 
-      case Regs::RxMaxCopy:
-        reg32 = regs.RxMaxCopy;
-        break;
+    DPRINTF(EthernetPIO,
+            "read %s: cpu=%d vnic=%d da=%#x pa=%#x size=%d val=%#x\n",
+            info.name, cpu, index, daddr, pkt.addr, pkt.size, value);
 
-      case Regs::TxMaxCopy:
-        reg32 = regs.TxMaxCopy;
-        break;
+    // reading the interrupt status register has the side effect of
+    // clearing it
+    if (raddr == Regs::IntrStatus)
+        devIntrClear();
 
-      case Regs::RxThreshold:
-        reg32 = regs.RxThreshold;
-        break;
+    return pioDelay;
+}
 
-      case Regs::TxThreshold:
-        reg32 = regs.TxThreshold;
-        break;
+/**
+ * IPR read of device register
 
-      case Regs::IntrStatus:
-        reg32 = regs.IntrStatus;
-        devIntrClear();
-        break;
+    Fault
+Device::iprRead(Addr daddr, int cpu, uint64_t &result)
+{
+    if (!regValid(daddr))
+        panic("invalid address: da=%#x", daddr);
 
-      case Regs::IntrMask:
-        reg32 = regs.IntrMask;
-        break;
+    const Regs::Info &info = regInfo(daddr);
+    if (!info.read)
+        panic("reading %s (write only): cpu=%d da=%#x", info.name, cpu, daddr);
 
-      case Regs::RxData:
-        reg64 = regs.RxData;
-        break;
+    DPRINTF(EthernetPIO, "IPR read %s: cpu=%d da=%#x\n",
+            info.name, cpu, daddr);
 
-      case Regs::RxDone:
-      case Regs::RxWait:
-        reg64 = Regs::set_RxDone_FifoLen(regs.RxDone,
-                                         min(rxFifo.packets(), 255));
-        break;
+    prepareRead(cpu, 0);
 
-      case Regs::TxData:
-        reg64 = regs.TxData;
-        break;
+    if (info.size == 4)
+        result = regData32(daddr);
 
-      case Regs::TxDone:
-      case Regs::TxWait:
-        reg64 = Regs::set_TxDone_FifoLen(regs.TxDone,
-                                         min(txFifo.packets(), 255));
-        break;
+    if (info.size == 8)
+        result = regData64(daddr);
 
-      case Regs::HwAddr:
-        reg64 = params()->eaddr;
-        break;
+    DPRINTF(EthernetPIO, "IPR read %s: cpu=%s da=%#x val=%#x\n",
+            info.name, cpu, result);
 
-      default:
-        panic("reading write only register %s: da=%#x pa=%#x va=%#x size=%d",
-              Regs::regName(daddr), daddr, req->paddr, req->vaddr, req->size);
-    }
+    return NoFault;
+}
+*/
+/**
+ * I/O write of device register
+ */
+Tick
+Device::write(Packet &pkt)
+{
+    assert(config.command & PCI_CMD_MSE);
+    assert(pkt.addr >= BARAddrs[0] && pkt.size < BARSize[0]);
 
-    DPRINTF(EthernetPIO, "read reg=%s done val=%#x\n", Regs::regName(daddr),
-            Regs::regSize(daddr) == 4 ? reg32 : reg64);
+    int cpu = pkt.req->getCpuNum();
+    Addr daddr = pkt.addr - BARAddrs[0];
+    Addr index = daddr >> Regs::VirtualShift;
+    Addr raddr = daddr & Regs::VirtualMask;
 
-    return No_Fault;
-}
+    pkt.time += pioDelay;
 
-Fault
-Device::write(MemReqPtr &req, const uint8_t *data)
-{
-    assert(config.hdr.command & PCI_CMD_MSE);
-    Addr daddr = req->paddr & 0xfff;
+    if (!regValid(raddr))
+        panic("invalid register: cpu=%d, da=%#x pa=%#x size=%d",
+                cpu, daddr, pkt.addr, pkt.size);
 
-    if (Regs::regSize(daddr) == 0)
-        panic("invalid address: da=%#x pa=%#x va=%#x size=%d",
-              daddr, req->paddr, req->vaddr, req->size);
+    const Regs::Info &info = regInfo(raddr);
+    if (!info.write)
+        panic("write %s (read only): "
+              "cpu=%d vnic=%d da=%#x pa=%#x size=%d",
+              info.name, cpu, index, daddr, pkt.addr, pkt.size);
 
-    if (req->size != Regs::regSize(daddr))
-        panic("invalid size: reg=%s da=%#x pa=%#x va=%#x size=%d",
-              Regs::regName(daddr), daddr, req->paddr, req->vaddr, req->size);
+    if (pkt.size != info.size)
+        panic("write %s (invalid size): "
+              "cpu=%d vnic=%d da=%#x pa=%#x size=%d",
+              info.name, cpu, index, daddr, pkt.addr, pkt.size);
 
-    uint32_t reg32 = *(uint32_t *)data;
-    uint64_t reg64 = *(uint64_t *)data;
+    VirtualReg &vnic = virtualRegs[index];
 
-    DPRINTF(EthernetPIO, "write reg=%s val=%#x da=%#x pa=%#x va=%#x size=%d\n",
-            Regs::regName(daddr), Regs::regSize(daddr) == 4 ? reg32 : reg64,
-            daddr, req->paddr, req->vaddr, req->size);
+    DPRINTF(EthernetPIO,
+            "write %s vnic %d: cpu=%d val=%#x da=%#x pa=%#x size=%d\n",
+            info.name, index, cpu, info.size == 4 ? pkt.get<uint32_t>() :
+            pkt.get<uint64_t>(), daddr, pkt.addr, pkt.size);
 
+    prepareWrite(cpu, index);
 
-    switch (daddr) {
+    switch (raddr) {
       case Regs::Config:
-        changeConfig(reg32);
+        changeConfig(pkt.get<uint32_t>());
         break;
 
-      case Regs::RxThreshold:
-        regs.RxThreshold = reg32;
+      case Regs::Command:
+        command(pkt.get<uint32_t>());
         break;
 
-      case Regs::TxThreshold:
-        regs.TxThreshold = reg32;
+      case Regs::IntrStatus:
+        devIntrClear(regs.IntrStatus & pkt.get<uint32_t>());
         break;
 
       case Regs::IntrMask:
-        devIntrChangeMask(reg32);
+        devIntrChangeMask(pkt.get<uint32_t>());
         break;
 
       case Regs::RxData:
-        if (rxState != rxIdle)
-            panic("receive machine busy with another request!");
+        if (Regs::get_RxDone_Busy(vnic.RxDone))
+            panic("receive machine busy with another request! rxState=%s",
+                  RxStateStrings[rxState]);
+
+        vnic.rxUnique = rxUnique++;
+        vnic.RxDone = Regs::RxDone_Busy;
+        vnic.RxData = pkt.get<uint64_t>();
+
+        if (Regs::get_RxData_Vaddr(reg64)) {
+            Addr vaddr = Regs::get_RxData_Addr(reg64);
+            Addr paddr = vtophys(req->xc, vaddr);
+            DPRINTF(EthernetPIO, "write RxData vnic %d (rxunique %d): "
+                    "vaddr=%#x, paddr=%#x\n",
+                    index, vnic.rxUnique, vaddr, paddr);
+
+            vnic.RxData = Regs::set_RxData_Addr(vnic.RxData, paddr);
+        } else {
+            DPRINTF(EthernetPIO, "write RxData vnic %d (rxunique %d)\n",
+                    index, vnic.rxUnique);
+        }
+
+        if (vnic.rxPacket == rxFifo.end()) {
+            DPRINTF(EthernetPIO, "request new packet...appending to rxList\n");
+            rxList.push_back(index);
+        } else {
+            DPRINTF(EthernetPIO, "packet exists...appending to rxBusy\n");
+            rxBusy.push_back(index);
+        }
 
-        regs.RxDone = 0;
-        regs.RxData = reg64;
-        if (rxEnable) {
+        if (rxEnable && (rxState == rxIdle || rxState == rxFifoBlock)) {
             rxState = rxFifoBlock;
             rxKick();
         }
         break;
 
       case Regs::TxData:
-        if (txState != txIdle)
-            panic("transmit machine busy with another request!");
+        if (Regs::get_TxDone_Busy(vnic.TxDone))
+            panic("transmit machine busy with another request! txState=%s",
+                  TxStateStrings[txState]);
+
+        vnic.txUnique = txUnique++;
+        vnic.TxDone = Regs::TxDone_Busy;
+
+        if (Regs::get_TxData_Vaddr(pkt.get<uint64_t>())) {
+            panic("vtophys won't work here in newmem.\n");
+            /*Addr vaddr = Regs::get_TxData_Addr(reg64);
+            Addr paddr = vtophys(req->xc, vaddr);
+            DPRINTF(EthernetPIO, "write TxData vnic %d (rxunique %d): "
+                    "vaddr=%#x, paddr=%#x\n",
+                    index, vnic.txUnique, vaddr, paddr);
+
+            vnic.TxData = Regs::set_TxData_Addr(vnic.TxData, paddr);*/
+        } else {
+            DPRINTF(EthernetPIO, "write TxData vnic %d (rxunique %d)\n",
+                    index, vnic.txUnique);
+        }
 
-        regs.TxDone = 0;
-        regs.TxData = reg64;
-        if (txEnable) {
+        if (txList.empty() || txList.front() != index)
+            txList.push_back(index);
+        if (txEnable && txState == txIdle && txList.front() == index) {
             txState = txFifoBlock;
             txKick();
         }
         break;
-
-      default:
-        panic("writing read only register %s: da=%#x pa=%#x va=%#x size=%d",
-              Regs::regName(daddr), daddr, req->paddr, req->vaddr, req->size);
     }
 
-    return No_Fault;
+    return pioDelay;
 }
 
 void
@@ -489,9 +534,25 @@ Device::devIntrPost(uint32_t interrupts)
             "interrupt written to intStatus: intr=%#x status=%#x mask=%#x\n",
             interrupts, regs.IntrStatus, regs.IntrMask);
 
-    if ((regs.IntrStatus & regs.IntrMask)) {
+    interrupts = regs.IntrStatus & regs.IntrMask;
+
+    // Intr_RxHigh is special, we only signal it if we've emptied the fifo
+    // and then filled it above the high watermark
+    if (rxEmpty)
+        rxEmpty = false;
+    else
+        interrupts &= ~Regs::Intr_RxHigh;
+
+    // Intr_TxLow is special, we only signal it if we've filled up the fifo
+    // and then dropped below the low watermark
+    if (txFull)
+        txFull = false;
+    else
+        interrupts &= ~Regs::Intr_TxLow;
+
+    if (interrupts) {
         Tick when = curTick;
-        if ((regs.IntrStatus & regs.IntrMask & Regs::Intr_NoDelay) == 0)
+        if ((interrupts & Regs::Intr_NoDelay) == 0)
             when += intrDelay;
         cpuIntrPost(when);
     }
@@ -627,12 +688,6 @@ Device::changeConfig(uint32_t newconf)
 
     regs.Config = newconf;
 
-    if ((changed & Regs::Config_Reset)) {
-        assert(regs.Config & Regs::Config_Reset);
-        reset();
-        regs.Config &= ~Regs::Config_Reset;
-    }
-
     if ((changed & Regs::Config_IntEn)) {
         cpuIntrEnable = regs.Config & Regs::Config_IntEn;
         if (cpuIntrEnable) {
@@ -656,103 +711,208 @@ Device::changeConfig(uint32_t newconf)
     }
 }
 
+void
+Device::command(uint32_t command)
+{
+    if (command & Regs::Command_Intr)
+        devIntrPost(Regs::Intr_Soft);
+
+    if (command & Regs::Command_Reset)
+        reset();
+}
+
 void
 Device::reset()
 {
     using namespace Regs;
+
     memset(&regs, 0, sizeof(regs));
+
+    regs.Config = 0;
+    if (params()->rx_thread)
+        regs.Config |= Config_RxThread;
+    if (params()->tx_thread)
+        regs.Config |= Config_TxThread;
+    if (params()->rss)
+        regs.Config |= Config_RSS;
+    if (params()->zero_copy)
+        regs.Config |= Config_ZeroCopy;
+    if (params()->delay_copy)
+        regs.Config |= Config_DelayCopy;
+    if (params()->virtual_addr)
+        regs.Config |= Config_Vaddr;
+
+    if (params()->delay_copy && params()->zero_copy)
+        panic("Can't delay copy and zero copy");
+
+    regs.IntrMask = Intr_Soft | Intr_RxHigh | Intr_RxPacket | Intr_TxLow;
     regs.RxMaxCopy = params()->rx_max_copy;
     regs.TxMaxCopy = params()->tx_max_copy;
-    regs.IntrMask = Intr_TxFifo | Intr_RxFifo | Intr_RxData;
+    regs.RxMaxIntr = params()->rx_max_intr;
+    regs.VirtualCount = params()->virtual_count;
+    regs.RxFifoSize = params()->rx_fifo_size;
+    regs.TxFifoSize = params()->tx_fifo_size;
+    regs.RxFifoMark = params()->rx_fifo_threshold;
+    regs.TxFifoMark = params()->tx_fifo_threshold;
+    regs.HwAddr = params()->eaddr;
+
+    rxList.clear();
+    rxBusy.clear();
+    rxActive = -1;
+    txList.clear();
 
     rxState = rxIdle;
     txState = txIdle;
 
     rxFifo.clear();
+    rxFifoPtr = rxFifo.end();
     txFifo.clear();
+    rxEmpty = false;
+    rxLow = true;
+    txFull = false;
+
+    int size = virtualRegs.size();
+    virtualRegs.clear();
+    virtualRegs.resize(size);
+    for (int i = 0; i < size; ++i)
+        virtualRegs[i].rxPacket = rxFifo.end();
 }
 
 void
-Device::rxDmaCopy()
+Device::rxDmaDone()
 {
     assert(rxState == rxCopy);
     rxState = rxCopyDone;
-    physmem->dma_write(rxDmaAddr, (uint8_t *)rxDmaData, rxDmaLen);
-    DPRINTF(EthernetDMA, "rx dma write paddr=%#x len=%d\n",
+    DPRINTF(EthernetDMA, "end rx dma write paddr=%#x len=%d\n",
             rxDmaAddr, rxDmaLen);
-    DDUMP(EthernetDMA, rxDmaData, rxDmaLen);
-}
+    DDUMP(EthernetData, rxDmaData, rxDmaLen);
+
+    // If the transmit state machine  has a pending DMA, let it go first
+    if (txState == txBeginCopy)
+        txKick();
 
-void
-Device::rxDmaDone()
-{
-    rxDmaCopy();
     rxKick();
 }
 
 void
 Device::rxKick()
 {
-    DPRINTF(EthernetSM, "receive kick rxState=%s (rxFifo.size=%d)\n",
+    VirtualReg *vnic = NULL;
+
+    DPRINTF(EthernetSM, "rxKick: rxState=%s (rxFifo.size=%d)\n",
             RxStateStrings[rxState], rxFifo.size());
 
     if (rxKickTick > curTick) {
-        DPRINTF(EthernetSM, "receive kick exiting, can't run till %d\n",
+        DPRINTF(EthernetSM, "rxKick: exiting, can't run till %d\n",
                 rxKickTick);
         return;
     }
 
   next:
-    switch (rxState) {
-      case rxIdle:
-        if (rxPioRequest) {
-            pioInterface->respond(rxPioRequest, curTick);
-            rxPioRequest = 0;
-        }
+    if (rxState == rxIdle)
         goto exit;
 
+    if (rxActive == -1) {
+        if (rxState != rxFifoBlock)
+            panic("no active vnic while in state %s", RxStateStrings[rxState]);
+
+        DPRINTF(EthernetSM, "processing rxState=%s\n",
+                RxStateStrings[rxState]);
+    } else {
+        vnic = &virtualRegs[rxActive];
+        DPRINTF(EthernetSM,
+                "processing rxState=%s for vnic %d (rxunique %d)\n",
+                RxStateStrings[rxState], rxActive, vnic->rxUnique);
+    }
+
+    switch (rxState) {
       case rxFifoBlock:
-        if (rxPacket) {
+        if (DTRACE(EthernetSM)) {
+            PacketFifo::iterator end = rxFifo.end();
+            int size = virtualRegs.size();
+            for (int i = 0; i < size; ++i) {
+                VirtualReg *vn = &virtualRegs[i];
+                if (vn->rxPacket != end &&
+                    !Regs::get_RxDone_Busy(vn->RxDone)) {
+                    DPRINTF(EthernetSM,
+                            "vnic %d (rxunique %d), has outstanding packet %d\n",
+                            i, vn->rxUnique,
+                            rxFifo.countPacketsBefore(vn->rxPacket));
+                }
+            }
+        }
+
+        if (!rxBusy.empty()) {
+            rxActive = rxBusy.front();
+            rxBusy.pop_front();
+            vnic = &virtualRegs[rxActive];
+
+            if (vnic->rxPacket == rxFifo.end())
+                panic("continuing vnic without packet\n");
+
+            DPRINTF(EthernetSM,
+                    "continue processing for vnic %d (rxunique %d)\n",
+                    rxActive, vnic->rxUnique);
+
             rxState = rxBeginCopy;
+
             break;
         }
 
-        if (rxFifo.empty()) {
+        if (rxFifoPtr == rxFifo.end()) {
             DPRINTF(EthernetSM, "receive waiting for data.  Nothing to do.\n");
             goto exit;
         }
 
+        if (rxList.empty())
+            panic("Not idle, but nothing to do!");
+
+        assert(!rxFifo.empty());
+
+        rxActive = rxList.front();
+        rxList.pop_front();
+        vnic = &virtualRegs[rxActive];
+
+        DPRINTF(EthernetSM,
+                "processing new packet for vnic %d (rxunique %d)\n",
+                rxActive, vnic->rxUnique);
+
         // Grab a new packet from the fifo.
-        rxPacket = rxFifo.front();
-        rxPacketBufPtr = rxPacket->data;
-        rxPktBytes = rxPacket->length;
-        assert(rxPktBytes);
+        vnic->rxPacket = rxFifoPtr++;
+        vnic->rxPacketOffset = 0;
+        vnic->rxPacketBytes = (*vnic->rxPacket)->length;
+        assert(vnic->rxPacketBytes);
 
-        rxDoneData = 0;
+        vnic->rxDoneData = 0;
         /* scope for variables */ {
-            IpPtr ip(rxPacket);
+            IpPtr ip(*vnic->rxPacket);
             if (ip) {
-                rxDoneData |= Regs::RxDone_IpPacket;
+                DPRINTF(Ethernet, "ID is %d\n", ip->id());
+                vnic->rxDoneData |= Regs::RxDone_IpPacket;
                 rxIpChecksums++;
                 if (cksum(ip) != 0) {
                     DPRINTF(EthernetCksum, "Rx IP Checksum Error\n");
-                    rxDoneData |= Regs::RxDone_IpError;
+                    vnic->rxDoneData |= Regs::RxDone_IpError;
                 }
                 TcpPtr tcp(ip);
                 UdpPtr udp(ip);
                 if (tcp) {
-                    rxDoneData |= Regs::RxDone_TcpPacket;
+                    DPRINTF(Ethernet,
+                            "Src Port=%d, Dest Port=%d, Seq=%d, Ack=%d\n",
+                            tcp->sport(), tcp->dport(), tcp->seq(),
+                            tcp->ack());
+                    vnic->rxDoneData |= Regs::RxDone_TcpPacket;
                     rxTcpChecksums++;
                     if (cksum(tcp) != 0) {
                         DPRINTF(EthernetCksum, "Rx TCP Checksum Error\n");
-                        rxDoneData |= Regs::RxDone_TcpError;
+                        vnic->rxDoneData |= Regs::RxDone_TcpError;
                     }
                 } else if (udp) {
-                    rxDoneData |= Regs::RxDone_UdpPacket;
+                    vnic->rxDoneData |= Regs::RxDone_UdpPacket;
                     rxUdpChecksums++;
                     if (cksum(udp) != 0) {
                         DPRINTF(EthernetCksum, "Rx UDP Checksum Error\n");
-                        rxDoneData |= Regs::RxDone_UdpError;
+                        vnic->rxDoneData |= Regs::RxDone_UdpError;
                     }
                 }
             }
@@ -761,28 +921,22 @@ Device::rxKick()
         break;
 
       case rxBeginCopy:
-        rxDmaAddr = plat->pciToDma(Regs::get_RxData_Addr(regs.RxData));
-        rxDmaLen = min<int>(Regs::get_RxData_Len(regs.RxData), rxPktBytes);
-        rxDmaData = rxPacketBufPtr;
-
-        if (dmaInterface) {
-            if (!dmaInterface->busy()) {
-                dmaInterface->doDMA(WriteInvalidate, rxDmaAddr, rxDmaLen,
-                                    curTick, &rxDmaEvent, true);
-                rxState = rxCopy;
-            }
+        if (dmaPending())
             goto exit;
-        }
 
+        rxDmaAddr = params()->platform->pciToDma(
+                Regs::get_RxData_Addr(vnic->RxData));
+        rxDmaLen = std::min<int>(Regs::get_RxData_Len(vnic->RxData),
+                            vnic->rxPacketBytes);
+        rxDmaData = (*vnic->rxPacket)->data + vnic->rxPacketOffset;
         rxState = rxCopy;
-        if (dmaWriteDelay != 0 || dmaWriteFactor != 0) {
-            Tick factor = ((rxDmaLen + ULL(63)) >> ULL(6)) * dmaWriteFactor;
-            Tick start = curTick + dmaWriteDelay + factor;
-            rxDmaEvent.schedule(start);
-            goto exit;
+        if (rxDmaAddr == 1LL) {
+            rxState = rxCopyDone;
+            break;
         }
 
-        rxDmaCopy();
+
+        dmaWrite(rxDmaAddr, rxDmaLen, &rxDmaEvent, rxDmaData);
         break;
 
       case rxCopy:
@@ -790,20 +944,45 @@ Device::rxKick()
         goto exit;
 
       case rxCopyDone:
-        regs.RxDone = rxDoneData | rxDmaLen;
-
-        if (rxPktBytes == rxDmaLen) {
-            rxPacket = NULL;
-            rxFifo.pop();
+        vnic->RxDone = vnic->rxDoneData;
+        vnic->RxDone |= Regs::RxDone_Complete;
+
+        if (vnic->rxPacketBytes == rxDmaLen) {
+            // Packet is complete.  Indicate how many bytes were copied
+            vnic->RxDone = Regs::set_RxDone_CopyLen(vnic->RxDone, rxDmaLen);
+
+            DPRINTF(EthernetSM,
+                    "rxKick: packet complete on vnic %d (rxunique %d)\n",
+                    rxActive, vnic->rxUnique);
+            rxFifo.remove(vnic->rxPacket);
+            vnic->rxPacket = rxFifo.end();
         } else {
-            regs.RxDone |= Regs::RxDone_More;
-            rxPktBytes -= rxDmaLen;
-            rxPacketBufPtr += rxDmaLen;
+            vnic->rxPacketBytes -= rxDmaLen;
+            vnic->rxPacketOffset += rxDmaLen;
+            vnic->RxDone |= Regs::RxDone_More;
+            vnic->RxDone = Regs::set_RxDone_CopyLen(vnic->RxDone,
+                                                    vnic->rxPacketBytes);
+            DPRINTF(EthernetSM,
+                    "rxKick: packet not complete on vnic %d (rxunique %d): "
+                    "%d bytes left\n",
+                    rxActive, vnic->rxUnique, vnic->rxPacketBytes);
+        }
+
+        rxActive = -1;
+        rxState = rxBusy.empty() && rxList.empty() ? rxIdle : rxFifoBlock;
+
+        if (rxFifo.empty()) {
+            devIntrPost(Regs::Intr_RxEmpty);
+            rxEmpty = true;
         }
 
-        regs.RxDone |= Regs::RxDone_Complete;
-        devIntrPost(Regs::Intr_RxData);
-        rxState = rxIdle;
+        if (rxFifo.size() < params()->rx_fifo_low_mark)
+            rxLow = true;
+
+        if (rxFifo.size() > params()->rx_fifo_threshold)
+            rxLow = false;
+
+        devIntrPost(Regs::Intr_RxDMA);
         break;
 
       default:
@@ -824,20 +1003,18 @@ Device::rxKick()
 }
 
 void
-Device::txDmaCopy()
+Device::txDmaDone()
 {
     assert(txState == txCopy);
     txState = txCopyDone;
-    physmem->dma_read((uint8_t *)txDmaData, txDmaAddr, txDmaLen);
     DPRINTF(EthernetDMA, "tx dma read paddr=%#x len=%d\n",
             txDmaAddr, txDmaLen);
-    DDUMP(EthernetDMA, txDmaData, txDmaLen);
-}
+    DDUMP(EthernetData, txDmaData, txDmaLen);
+
+    // If the receive state machine  has a pending DMA, let it go first
+    if (rxState == rxBeginCopy)
+        rxKick();
 
-void
-Device::txDmaDone()
-{
-    txDmaCopy();
     txKick();
 }
 
@@ -849,7 +1026,8 @@ Device::transmit()
         return;
     }
 
-    PacketPtr packet = txFifo.front();
+    uint32_t interrupts;
+    EthPacketPtr packet = txFifo.front();
     if (!interface->sendPacket(packet)) {
         DPRINTF(Ethernet, "Packet Transmit: failed txFifo available %d\n",
                 txFifo.avail());
@@ -857,7 +1035,6 @@ Device::transmit()
     }
 
     txFifo.pop();
-
 #if TRACING_ON
     if (DTRACE(Ethernet)) {
         IpPtr ip(packet);
@@ -865,24 +1042,26 @@ Device::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, packet->data, packet->length);
+    DDUMP(EthernetData, packet->data, packet->length);
     txBytes += packet->length;
     txPackets++;
 
     DPRINTF(Ethernet, "Packet Transmit: successful txFifo Available %d\n",
             txFifo.avail());
 
-    if (txFifo.size() <= params()->tx_fifo_threshold)
-        devIntrPost(Regs::Intr_TxFifo);
-
-    devIntrPost(Regs::Intr_TxDone);
+    interrupts = Regs::Intr_TxPacket;
+    if (txFifo.size() < regs.TxFifoMark)
+        interrupts |= Regs::Intr_TxLow;
+    devIntrPost(interrupts);
 
   reschedule:
    if (!txFifo.empty() && !txEvent.scheduled()) {
@@ -894,33 +1073,34 @@ Device::transmit()
 void
 Device::txKick()
 {
-    DPRINTF(EthernetSM, "transmit kick txState=%s (txFifo.size=%d)\n",
+    VirtualReg *vnic;
+    DPRINTF(EthernetSM, "txKick: txState=%s (txFifo.size=%d)\n",
             TxStateStrings[txState], txFifo.size());
 
     if (txKickTick > curTick) {
-        DPRINTF(EthernetSM, "transmit kick exiting, can't run till %d\n",
+        DPRINTF(EthernetSM, "txKick: exiting, can't run till %d\n",
                 txKickTick);
         return;
     }
 
   next:
-    switch (txState) {
-      case txIdle:
-        if (txPioRequest) {
-            pioInterface->respond(txPioRequest, curTick + pioLatency);
-            txPioRequest = 0;
-        }
+    if (txState == txIdle)
         goto exit;
 
+    assert(!txList.empty());
+    vnic = &virtualRegs[txList.front()];
+
+    switch (txState) {
       case txFifoBlock:
+        assert(Regs::get_TxDone_Busy(vnic->TxDone));
         if (!txPacket) {
             // Grab a new packet from the fifo.
-            txPacket = new PacketData(16384);
-            txPacketBufPtr = txPacket->data;
+            txPacket = new EthPacketData(16384);
+            txPacketOffset = 0;
         }
 
         if (txFifo.avail() - txPacket->length <
-            Regs::get_TxData_Len(regs.TxData)) {
+            Regs::get_TxData_Len(vnic->TxData)) {
             DPRINTF(EthernetSM, "transmit fifo full.  Nothing to do.\n");
             goto exit;
         }
@@ -929,29 +1109,16 @@ Device::txKick()
         break;
 
       case txBeginCopy:
-        txDmaAddr = plat->pciToDma(Regs::get_TxData_Addr(regs.TxData));
-        txDmaLen = Regs::get_TxData_Len(regs.TxData);
-        txDmaData = txPacketBufPtr;
-
-        if (dmaInterface) {
-            if (!dmaInterface->busy()) {
-                dmaInterface->doDMA(Read, txDmaAddr, txDmaLen,
-                                    curTick, &txDmaEvent, true);
-                txState = txCopy;
-            }
-
+        if (dmaPending())
             goto exit;
-        }
 
+        txDmaAddr = params()->platform->pciToDma(
+                Regs::get_TxData_Addr(vnic->TxData));
+        txDmaLen = Regs::get_TxData_Len(vnic->TxData);
+        txDmaData = txPacket->data + txPacketOffset;
         txState = txCopy;
-        if (dmaReadDelay != 0 || dmaReadFactor != 0) {
-            Tick factor = ((txDmaLen + ULL(63)) >> ULL(6)) * dmaReadFactor;
-            Tick start = curTick + dmaReadDelay + factor;
-            txDmaEvent.schedule(start);
-            goto exit;
-        }
 
-        txDmaCopy();
+        dmaRead(txDmaAddr, txDmaLen, &txDmaEvent, txDmaData);
         break;
 
       case txCopy:
@@ -959,41 +1126,49 @@ Device::txKick()
         goto exit;
 
       case txCopyDone:
+        vnic->TxDone = txDmaLen | Regs::TxDone_Complete;
         txPacket->length += txDmaLen;
-        if ((regs.TxData & Regs::TxData_More)) {
-            txPacketBufPtr += txDmaLen;
-        } else {
-            assert(txPacket->length <= txFifo.avail());
-            if ((regs.TxData & Regs::TxData_Checksum)) {
-                IpPtr ip(txPacket);
-                if (ip) {
-                    TcpPtr tcp(ip);
-                    if (tcp) {
-                        tcp->sum(0);
-                        tcp->sum(cksum(tcp));
-                        txTcpChecksums++;
-                    }
+        if ((vnic->TxData & Regs::TxData_More)) {
+            txPacketOffset += txDmaLen;
+            txState = txIdle;
+            devIntrPost(Regs::Intr_TxDMA);
+            break;
+        }
 
-                    UdpPtr udp(ip);
-                    if (udp) {
-                        udp->sum(0);
-                        udp->sum(cksum(udp));
-                        txUdpChecksums++;
-                    }
+        assert(txPacket->length <= txFifo.avail());
+        if ((vnic->TxData & Regs::TxData_Checksum)) {
+            IpPtr ip(txPacket);
+            if (ip) {
+                TcpPtr tcp(ip);
+                if (tcp) {
+                    tcp->sum(0);
+                    tcp->sum(cksum(tcp));
+                    txTcpChecksums++;
+                }
 
-                    ip->sum(0);
-                    ip->sum(cksum(ip));
-                    txIpChecksums++;
+                UdpPtr udp(ip);
+                if (udp) {
+                    udp->sum(0);
+                    udp->sum(cksum(udp));
+                    txUdpChecksums++;
                 }
+
+                ip->sum(0);
+                ip->sum(cksum(ip));
+                txIpChecksums++;
             }
-            txFifo.push(txPacket);
-            txPacket = 0;
-            transmit();
         }
 
-        regs.TxDone = txDmaLen | Regs::TxDone_Complete;
-        devIntrPost(Regs::Intr_TxData);
-        txState = txIdle;
+        txFifo.push(txPacket);
+        if (txFifo.avail() < regs.TxMaxCopy) {
+            devIntrPost(Regs::Intr_TxFull);
+            txFull = true;
+        }
+        txPacket = 0;
+        transmit();
+        txList.pop_front();
+        txState = txList.empty() ? txIdle : txFifoBlock;
+        devIntrPost(Regs::Intr_TxDMA);
         break;
 
       default:
@@ -1030,7 +1205,7 @@ Device::transferDone()
 }
 
 bool
-Device::rxFilter(const PacketPtr &packet)
+Device::rxFilter(const EthPacketPtr &packet)
 {
     if (!Regs::get_Config_Filter(regs.Config))
         return false;
@@ -1075,7 +1250,7 @@ Device::rxFilter(const PacketPtr &packet)
 }
 
 bool
-Device::recvPacket(PacketPtr packet)
+Device::recvPacket(EthPacketPtr packet)
 {
     rxBytes += packet->length;
     rxPackets++;
@@ -1085,18 +1260,16 @@ Device::recvPacket(PacketPtr packet)
 
     if (!rxEnable) {
         DPRINTF(Ethernet, "receive disabled...packet dropped\n");
-        interface->recvDone();
         return true;
     }
 
     if (rxFilter(packet)) {
         DPRINTF(Ethernet, "packet filtered...dropped\n");
-        interface->recvDone();
         return true;
     }
 
-    if (rxFifo.size() >= params()->rx_fifo_threshold)
-        devIntrPost(Regs::Intr_RxFifo);
+    if (rxFifo.size() >= regs.RxFifoMark)
+        devIntrPost(Regs::Intr_RxHigh);
 
     if (!rxFifo.push(packet)) {
         DPRINTF(Ethernet,
@@ -1104,8 +1277,12 @@ Device::recvPacket(PacketPtr packet)
         return false;
     }
 
-    interface->recvDone();
-    devIntrPost(Regs::Intr_RxDone);
+    // If we were at the last element, back up one ot go to the new
+    // last element of the list.
+    if (rxFifoPtr == rxFifo.end())
+        --rxFifoPtr;
+
+    devIntrPost(Regs::Intr_RxPacket);
     rxKick();
     return true;
 }
@@ -1114,7 +1291,7 @@ Device::recvPacket(PacketPtr packet)
 //
 //
 void
-Base::serialize(ostream &os)
+Base::serialize(std::ostream &os)
 {
     // Serialize the PciDev base class
     PciDev::serialize(os);
@@ -1158,61 +1335,111 @@ Base::unserialize(Checkpoint *cp, const std::string &section)
 }
 
 void
-Device::serialize(ostream &os)
+Device::serialize(std::ostream &os)
 {
+    int count;
+
     // Serialize the PciDev base class
     Base::serialize(os);
 
-    if (rxDmaEvent.scheduled())
-        rxDmaCopy();
+    if (rxState == rxCopy)
+        panic("can't serialize with an in flight dma request rxState=%s",
+              RxStateStrings[rxState]);
 
-    if (txDmaEvent.scheduled())
-        txDmaCopy();
+    if (txState == txCopy)
+        panic("can't serialize with an in flight dma request txState=%s",
+              TxStateStrings[txState]);
 
     /*
      * Serialize the device registers
      */
     SERIALIZE_SCALAR(regs.Config);
-    SERIALIZE_SCALAR(regs.RxMaxCopy);
-    SERIALIZE_SCALAR(regs.TxMaxCopy);
-    SERIALIZE_SCALAR(regs.RxThreshold);
-    SERIALIZE_SCALAR(regs.TxThreshold);
     SERIALIZE_SCALAR(regs.IntrStatus);
     SERIALIZE_SCALAR(regs.IntrMask);
+    SERIALIZE_SCALAR(regs.RxMaxCopy);
+    SERIALIZE_SCALAR(regs.TxMaxCopy);
+    SERIALIZE_SCALAR(regs.RxMaxIntr);
+    SERIALIZE_SCALAR(regs.VirtualCount);
     SERIALIZE_SCALAR(regs.RxData);
     SERIALIZE_SCALAR(regs.RxDone);
     SERIALIZE_SCALAR(regs.TxData);
     SERIALIZE_SCALAR(regs.TxDone);
 
+    /*
+     * Serialize the virtual nic state
+     */
+    int virtualRegsSize = virtualRegs.size();
+    SERIALIZE_SCALAR(virtualRegsSize);
+    for (int i = 0; i < virtualRegsSize; ++i) {
+        VirtualReg *vnic = &virtualRegs[i];
+
+        std::string reg = csprintf("vnic%d", i);
+        paramOut(os, reg + ".RxData", vnic->RxData);
+        paramOut(os, reg + ".RxDone", vnic->RxDone);
+        paramOut(os, reg + ".TxData", vnic->TxData);
+        paramOut(os, reg + ".TxDone", vnic->TxDone);
+
+        bool rxPacketExists = vnic->rxPacket != rxFifo.end();
+        paramOut(os, reg + ".rxPacketExists", rxPacketExists);
+        if (rxPacketExists) {
+            int rxPacket = 0;
+            PacketFifo::iterator i = rxFifo.begin();
+            while (i != vnic->rxPacket) {
+                assert(i != rxFifo.end());
+                ++i;
+                ++rxPacket;
+            }
+
+            paramOut(os, reg + ".rxPacket", rxPacket);
+            paramOut(os, reg + ".rxPacketOffset", vnic->rxPacketOffset);
+            paramOut(os, reg + ".rxPacketBytes", vnic->rxPacketBytes);
+        }
+        paramOut(os, reg + ".rxDoneData", vnic->rxDoneData);
+    }
+
+    int rxFifoPtr = rxFifo.countPacketsBefore(this->rxFifoPtr);
+    SERIALIZE_SCALAR(rxFifoPtr);
+
+    SERIALIZE_SCALAR(rxActive);
+
+    VirtualList::iterator i, end;
+    for (count = 0, i = rxList.begin(), end = rxList.end(); i != end; ++i)
+        paramOut(os, csprintf("rxList%d", count++), *i);
+    int rxListSize = count;
+    SERIALIZE_SCALAR(rxListSize);
+
+    for (count = 0, i = rxBusy.begin(), end = rxBusy.end(); i != end; ++i)
+        paramOut(os, csprintf("rxBusy%d", count++), *i);
+    int rxBusySize = count;
+    SERIALIZE_SCALAR(rxBusySize);
+
+    for (count = 0, i = txList.begin(), end = txList.end(); i != end; ++i)
+        paramOut(os, csprintf("txList%d", count++), *i);
+    int txListSize = count;
+    SERIALIZE_SCALAR(txListSize);
+
     /*
      * Serialize rx state machine
      */
     int rxState = this->rxState;
     SERIALIZE_SCALAR(rxState);
+    SERIALIZE_SCALAR(rxEmpty);
+    SERIALIZE_SCALAR(rxLow);
     rxFifo.serialize("rxFifo", os);
-    bool rxPacketExists = rxPacket;
-    SERIALIZE_SCALAR(rxPacketExists);
-    if (rxPacketExists) {
-        rxPacket->serialize("rxPacket", os);
-        uint32_t rxPktBufPtr = (uint32_t) (rxPacketBufPtr - rxPacket->data);
-        SERIALIZE_SCALAR(rxPktBufPtr);
-        SERIALIZE_SCALAR(rxPktBytes);
-    }
-    SERIALIZE_SCALAR(rxDoneData);
 
     /*
      * Serialize tx state machine
      */
     int txState = this->txState;
     SERIALIZE_SCALAR(txState);
+    SERIALIZE_SCALAR(txFull);
     txFifo.serialize("txFifo", os);
     bool txPacketExists = txPacket;
     SERIALIZE_SCALAR(txPacketExists);
     if (txPacketExists) {
         txPacket->serialize("txPacket", os);
-        uint32_t txPktBufPtr = (uint32_t) (txPacketBufPtr - txPacket->data);
-        SERIALIZE_SCALAR(txPktBufPtr);
-        SERIALIZE_SCALAR(txPktBytes);
+        SERIALIZE_SCALAR(txPacketOffset);
+        SERIALIZE_SCALAR(txPacketBytes);
     }
 
     /*
@@ -1233,54 +1460,118 @@ Device::unserialize(Checkpoint *cp, const std::string &section)
      * Unserialize the device registers
      */
     UNSERIALIZE_SCALAR(regs.Config);
-    UNSERIALIZE_SCALAR(regs.RxMaxCopy);
-    UNSERIALIZE_SCALAR(regs.TxMaxCopy);
-    UNSERIALIZE_SCALAR(regs.RxThreshold);
-    UNSERIALIZE_SCALAR(regs.TxThreshold);
     UNSERIALIZE_SCALAR(regs.IntrStatus);
     UNSERIALIZE_SCALAR(regs.IntrMask);
+    UNSERIALIZE_SCALAR(regs.RxMaxCopy);
+    UNSERIALIZE_SCALAR(regs.TxMaxCopy);
+    UNSERIALIZE_SCALAR(regs.RxMaxIntr);
+    UNSERIALIZE_SCALAR(regs.VirtualCount);
     UNSERIALIZE_SCALAR(regs.RxData);
     UNSERIALIZE_SCALAR(regs.RxDone);
     UNSERIALIZE_SCALAR(regs.TxData);
     UNSERIALIZE_SCALAR(regs.TxDone);
 
+    UNSERIALIZE_SCALAR(rxActive);
+
+    int rxListSize;
+    UNSERIALIZE_SCALAR(rxListSize);
+    rxList.clear();
+    for (int i = 0; i < rxListSize; ++i) {
+        int value;
+        paramIn(cp, section, csprintf("rxList%d", i), value);
+        rxList.push_back(value);
+    }
+
+    int rxBusySize;
+    UNSERIALIZE_SCALAR(rxBusySize);
+    rxBusy.clear();
+    for (int i = 0; i < rxBusySize; ++i) {
+        int value;
+        paramIn(cp, section, csprintf("rxBusy%d", i), value);
+        rxBusy.push_back(value);
+    }
+
+    int txListSize;
+    UNSERIALIZE_SCALAR(txListSize);
+    txList.clear();
+    for (int i = 0; i < txListSize; ++i) {
+        int value;
+        paramIn(cp, section, csprintf("txList%d", i), value);
+        txList.push_back(value);
+    }
+
     /*
      * Unserialize rx state machine
      */
     int rxState;
     UNSERIALIZE_SCALAR(rxState);
+    UNSERIALIZE_SCALAR(rxEmpty);
+    UNSERIALIZE_SCALAR(rxLow);
     this->rxState = (RxState) rxState;
     rxFifo.unserialize("rxFifo", cp, section);
-    bool rxPacketExists;
-    UNSERIALIZE_SCALAR(rxPacketExists);
-    rxPacket = 0;
-    if (rxPacketExists) {
-        rxPacket = new PacketData(16384);
-        rxPacket->unserialize("rxPacket", cp, section);
-        uint32_t rxPktBufPtr;
-        UNSERIALIZE_SCALAR(rxPktBufPtr);
-        this->rxPacketBufPtr = (uint8_t *) rxPacket->data + rxPktBufPtr;
-        UNSERIALIZE_SCALAR(rxPktBytes);
-    }
-    UNSERIALIZE_SCALAR(rxDoneData);
+
+    int rxFifoPtr;
+    UNSERIALIZE_SCALAR(rxFifoPtr);
+    this->rxFifoPtr = rxFifo.begin();
+    for (int i = 0; i < rxFifoPtr; ++i)
+        ++this->rxFifoPtr;
 
     /*
      * Unserialize tx state machine
      */
     int txState;
     UNSERIALIZE_SCALAR(txState);
+    UNSERIALIZE_SCALAR(txFull);
     this->txState = (TxState) txState;
     txFifo.unserialize("txFifo", cp, section);
     bool txPacketExists;
     UNSERIALIZE_SCALAR(txPacketExists);
     txPacket = 0;
     if (txPacketExists) {
-        txPacket = new PacketData(16384);
+        txPacket = new EthPacketData(16384);
         txPacket->unserialize("txPacket", cp, section);
-        uint32_t txPktBufPtr;
-        UNSERIALIZE_SCALAR(txPktBufPtr);
-        this->txPacketBufPtr = (uint8_t *) txPacket->data + txPktBufPtr;
-        UNSERIALIZE_SCALAR(txPktBytes);
+        UNSERIALIZE_SCALAR(txPacketOffset);
+        UNSERIALIZE_SCALAR(txPacketBytes);
+    }
+
+    /*
+     * unserialize the virtual nic registers/state
+     *
+     * this must be done after the unserialization of the rxFifo
+     * because the packet iterators depend on the fifo being populated
+     */
+    int virtualRegsSize;
+    UNSERIALIZE_SCALAR(virtualRegsSize);
+    virtualRegs.clear();
+    virtualRegs.resize(virtualRegsSize);
+    for (int i = 0; i < virtualRegsSize; ++i) {
+        VirtualReg *vnic = &virtualRegs[i];
+        std::string reg = csprintf("vnic%d", i);
+
+        paramIn(cp, section, reg + ".RxData", vnic->RxData);
+        paramIn(cp, section, reg + ".RxDone", vnic->RxDone);
+        paramIn(cp, section, reg + ".TxData", vnic->TxData);
+        paramIn(cp, section, reg + ".TxDone", vnic->TxDone);
+
+        vnic->rxUnique = rxUnique++;
+        vnic->txUnique = txUnique++;
+
+        bool rxPacketExists;
+        paramIn(cp, section, reg + ".rxPacketExists", rxPacketExists);
+        if (rxPacketExists) {
+            int rxPacket;
+            paramIn(cp, section, reg + ".rxPacket", rxPacket);
+            vnic->rxPacket = rxFifo.begin();
+            while (rxPacket--)
+                ++vnic->rxPacket;
+
+            paramIn(cp, section, reg + ".rxPacketOffset",
+                    vnic->rxPacketOffset);
+            paramIn(cp, section, reg + ".rxPacketBytes", vnic->rxPacketBytes);
+        } else {
+            vnic->rxPacket = rxFifo.end();
+        }
+        paramIn(cp, section, reg + ".rxDoneData", vnic->rxDoneData);
     }
 
     /*
@@ -1291,43 +1582,11 @@ Device::unserialize(Checkpoint *cp, const std::string &section)
     if (transmitTick)
         txEvent.schedule(curTick + transmitTick);
 
-    /*
-     * re-add addrRanges to bus bridges
-     */
-    if (pioInterface)
-        pioInterface->addAddrRange(RangeSize(BARAddrs[0], BARSize[0]));
-}
-
-Tick
-Device::cacheAccess(MemReqPtr &req)
-{
-    //The mask is to give you only the offset into the device register file
-    Addr daddr = req->paddr - addr;
-
-    DPRINTF(EthernetPIO, "timing access to paddr=%#x (daddr=%#x)\n",
-            req->paddr, daddr);
-
-    Tick when = curTick + pioLatency;
-
-    switch (daddr) {
-      case Regs::RxDone:
-        if (rxState != rxIdle) {
-            rxPioRequest = req;
-            when = 0;
-        }
-        break;
-
-      case Regs::TxDone:
-        if (txState != txIdle) {
-            txPioRequest = req;
-            when = 0;
-        }
-        break;
-    }
+    pioPort->sendStatusChange(Port::RangeChange);
 
-    return when;
 }
 
+
 BEGIN_DECLARE_SIM_OBJECT_PARAMS(Interface)
 
     SimObjectParam<EtherInt *> peer;
@@ -1360,68 +1619,87 @@ REGISTER_SIM_OBJECT("SinicInt", Interface)
 
 BEGIN_DECLARE_SIM_OBJECT_PARAMS(Device)
 
-    Param<Tick> cycle_time;
-    Param<Tick> tx_delay;
-    Param<Tick> rx_delay;
-    Param<Tick> intr_delay;
-    SimObjectParam<MemoryController *> mmu;
-    SimObjectParam<PhysicalMemory *> physmem;
-    Param<bool> rx_filter;
-    Param<string> hardware_address;
-    SimObjectParam<Bus*> header_bus;
-    SimObjectParam<Bus*> payload_bus;
-    SimObjectParam<HierParams *> hier;
-    Param<Tick> pio_latency;
+
+    SimObjectParam<System *> system;
+    SimObjectParam<Platform *> platform;
     SimObjectParam<PciConfigAll *> configspace;
     SimObjectParam<PciConfigData *> configdata;
-    SimObjectParam<Platform *> platform;
     Param<uint32_t> pci_bus;
     Param<uint32_t> pci_dev;
     Param<uint32_t> pci_func;
+    Param<Tick> pio_latency;
+    Param<Tick> intr_delay;
+
+    Param<Tick> clock;
+    Param<Tick> dma_read_delay;
+    Param<Tick> dma_read_factor;
+    Param<Tick> dma_write_delay;
+    Param<Tick> dma_write_factor;
+
+    Param<Tick> rx_delay;
+    Param<Tick> tx_delay;
     Param<uint32_t> rx_max_copy;
     Param<uint32_t> tx_max_copy;
+    Param<uint32_t> rx_max_intr;
     Param<uint32_t> rx_fifo_size;
     Param<uint32_t> tx_fifo_size;
     Param<uint32_t> rx_fifo_threshold;
+    Param<uint32_t> rx_fifo_low_mark;
+    Param<uint32_t> tx_fifo_high_mark;
     Param<uint32_t> tx_fifo_threshold;
-    Param<Tick> dma_read_delay;
-    Param<Tick> dma_read_factor;
-    Param<Tick> dma_write_delay;
-    Param<Tick> dma_write_factor;
+
+    Param<bool> rx_filter;
+    Param<std::string> hardware_address;
+    Param<bool> rx_thread;
+    Param<bool> tx_thread;
+    Param<bool> rss;
+    Param<uint32_t> virtual_count;
+    Param<bool> zero_copy;
+    Param<bool> delay_copy;
+    Param<bool> virtual_addr;
 
 END_DECLARE_SIM_OBJECT_PARAMS(Device)
 
 BEGIN_INIT_SIM_OBJECT_PARAMS(Device)
 
-    INIT_PARAM(cycle_time, "State machine cycle time"),
-    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(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_DFLT(header_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),
-    INIT_PARAM_DFLT(pio_latency, "Programmed IO latency in bus cycles", 1),
+
+    INIT_PARAM(system, "System pointer"),
+    INIT_PARAM(platform, "Platform pointer"),
     INIT_PARAM(configspace, "PCI Configspace"),
     INIT_PARAM(configdata, "PCI Config data"),
-    INIT_PARAM(platform, "Platform"),
-    INIT_PARAM(pci_bus, "PCI bus"),
+    INIT_PARAM(pci_bus, "PCI bus ID"),
     INIT_PARAM(pci_dev, "PCI device number"),
     INIT_PARAM(pci_func, "PCI function code"),
-    INIT_PARAM_DFLT(rx_max_copy, "rx max copy", 16*1024),
-    INIT_PARAM_DFLT(tx_max_copy, "rx max copy", 16*1024),
-    INIT_PARAM_DFLT(rx_fifo_size, "max size in bytes of rxFifo", 64*1024),
-    INIT_PARAM_DFLT(tx_fifo_size, "max size in bytes of txFifo", 64*1024),
-    INIT_PARAM_DFLT(rx_fifo_threshold, "max size in bytes of rxFifo", 48*1024),
-    INIT_PARAM_DFLT(tx_fifo_threshold, "max size in bytes of txFifo", 16*1024),
-    INIT_PARAM_DFLT(dma_read_delay, "fixed delay for dma reads", 0),
-    INIT_PARAM_DFLT(dma_read_factor, "multiplier for dma reads", 0),
-    INIT_PARAM_DFLT(dma_write_delay, "fixed delay for dma writes", 0),
-    INIT_PARAM_DFLT(dma_write_factor, "multiplier for dma writes", 0)
+    INIT_PARAM_DFLT(pio_latency, "Programmed IO latency in bus cycles", 1),
+    INIT_PARAM(intr_delay, "Interrupt Delay"),
+    INIT_PARAM(clock, "State machine cycle time"),
+
+    INIT_PARAM(dma_read_delay, "fixed delay for dma reads"),
+    INIT_PARAM(dma_read_factor, "multiplier for dma reads"),
+    INIT_PARAM(dma_write_delay, "fixed delay for dma writes"),
+    INIT_PARAM(dma_write_factor, "multiplier for dma writes"),
+
+    INIT_PARAM(rx_delay, "Receive Delay"),
+    INIT_PARAM(tx_delay, "Transmit Delay"),
+    INIT_PARAM(rx_max_copy, "rx max copy"),
+    INIT_PARAM(tx_max_copy, "rx max copy"),
+    INIT_PARAM(rx_max_intr, "rx max intr"),
+    INIT_PARAM(rx_fifo_size, "max size in bytes of rxFifo"),
+    INIT_PARAM(tx_fifo_size, "max size in bytes of txFifo"),
+    INIT_PARAM(rx_fifo_threshold, "max size in bytes of rxFifo"),
+    INIT_PARAM(rx_fifo_low_mark, "max size in bytes of rxFifo"),
+    INIT_PARAM(tx_fifo_high_mark, "max size in bytes of txFifo"),
+    INIT_PARAM(tx_fifo_threshold, "max size in bytes of txFifo"),
+
+    INIT_PARAM(rx_filter, "Enable Receive Filter"),
+    INIT_PARAM(hardware_address, "Ethernet Hardware Address"),
+    INIT_PARAM(rx_thread, ""),
+    INIT_PARAM(tx_thread, ""),
+    INIT_PARAM(rss, ""),
+    INIT_PARAM(virtual_count, ""),
+    INIT_PARAM(zero_copy, ""),
+    INIT_PARAM(delay_copy, ""),
+    INIT_PARAM(virtual_addr, "")
 
 END_INIT_SIM_OBJECT_PARAMS(Device)
 
@@ -1430,34 +1708,44 @@ CREATE_SIM_OBJECT(Device)
 {
     Device::Params *params = new Device::Params;
     params->name = getInstanceName();
-    params->intr_delay = intr_delay;
-    params->physmem = physmem;
-    params->cycle_time = cycle_time;
-    params->tx_delay = tx_delay;
-    params->rx_delay = rx_delay;
-    params->mmu = mmu;
-    params->hier = hier;
-    params->header_bus = header_bus;
-    params->payload_bus = payload_bus;
-    params->pio_latency = pio_latency;
+    params->platform = platform;
+    params->system = system;
     params->configSpace = configspace;
     params->configData = configdata;
-    params->plat = platform;
     params->busNum = pci_bus;
     params->deviceNum = pci_dev;
     params->functionNum = pci_func;
-    params->rx_filter = rx_filter;
-    params->eaddr = hardware_address;
+    params->pio_delay = pio_latency;
+    params->intr_delay = intr_delay;
+    params->clock = clock;
+
+    params->dma_read_delay = dma_read_delay;
+    params->dma_read_factor = dma_read_factor;
+    params->dma_write_delay = dma_write_delay;
+    params->dma_write_factor = dma_write_factor;
+
+    params->tx_delay = tx_delay;
+    params->rx_delay = rx_delay;
     params->rx_max_copy = rx_max_copy;
     params->tx_max_copy = tx_max_copy;
+    params->rx_max_intr = rx_max_intr;
     params->rx_fifo_size = rx_fifo_size;
     params->tx_fifo_size = tx_fifo_size;
     params->rx_fifo_threshold = rx_fifo_threshold;
+    params->rx_fifo_low_mark = rx_fifo_low_mark;
+    params->tx_fifo_high_mark = tx_fifo_high_mark;
     params->tx_fifo_threshold = tx_fifo_threshold;
-    params->dma_read_delay = dma_read_delay;
-    params->dma_read_factor = dma_read_factor;
-    params->dma_write_delay = dma_write_delay;
-    params->dma_write_factor = dma_write_factor;
+
+    params->rx_filter = rx_filter;
+    params->eaddr = hardware_address;
+    params->rx_thread = rx_thread;
+    params->tx_thread = tx_thread;
+    params->rss = rss;
+    params->virtual_count = virtual_count;
+    params->zero_copy = zero_copy;
+    params->delay_copy = delay_copy;
+    params->virtual_addr = virtual_addr;
+
     return new Device(params);
 }