Move to a model with a unified request object.
[gem5.git] / dev / sinic.cc
index e3e1ffba083d0df51d6baee455af3a73ed824446..363994919c8c95da50393ec05402ca90505c9b7c 100644 (file)
 #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 {
 
@@ -93,31 +94,25 @@ Device::Device(Params *p)
 {
     reset();
 
-    if (p->io_bus) {
-        pioInterface = newPioInterface(p->name + ".pio", p->hier, p->io_bus,
+    if (p->pio_bus) {
+        pioInterface = newPioInterface(p->name + ".pio", p->hier, p->pio_bus,
                                        this, &Device::cacheAccess);
+        pioLatency = p->pio_latency * p->pio_bus->clockRate;
+    }
 
-        pioLatency = p->pio_latency * p->io_bus->clockRate;
-
+    if (p->header_bus) {
         if (p->payload_bus)
-            dmaInterface = new DMAInterface<Bus>(p->name + ".dma", p->io_bus,
+            dmaInterface = new DMAInterface<Bus>(p->name + ".dma",
+                                                 p->header_bus,
                                                  p->payload_bus, 1,
                                                  p->dma_no_allocate);
         else
-            dmaInterface = new DMAInterface<Bus>(p->name + ".dma", p->io_bus,
-                                                 p->io_bus, 1,
+            dmaInterface = new DMAInterface<Bus>(p->name + ".dma",
+                                                 p->header_bus,
+                                                 p->header_bus, 1,
                                                  p->dma_no_allocate);
-    } else if (p->payload_bus) {
-        pioInterface = newPioInterface(p->name + ".pio", p->hier,
-                                       p->payload_bus, this,
-                                       &Device::cacheAccess);
-
-        pioLatency = p->pio_latency * p->payload_bus->clockRate;
-
-        dmaInterface = new DMAInterface<Bus>(p->name + ".dma", p->payload_bus,
-                                             p->payload_bus, 1,
-                                             p->dma_no_allocate);
-    }
+    } else if (p->payload_bus)
+        panic("must define a header bus if defining a payload bus");
 }
 
 Device::~Device()
@@ -316,130 +311,206 @@ Device::writeConfig(int offset, int size, const uint8_t *data)
     }
 }
 
+void
+Device::prepareIO(int cpu, int index)
+{
+    int size = virtualRegs.size();
+    if (index < size)
+        return;
+
+    virtualRegs.resize(index + 1);
+    for (int i = size; i <= index; ++i)
+        virtualRegs[i].rxPacket = rxFifo.end();
+}
+
+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.packets());
+    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)
 {
     assert(config.command & PCI_CMD_MSE);
+    Fault fault = readBar(req, data);
 
-    //The mask is to give you only the offset into the device register file
-    Addr daddr = req->paddr & 0xfff;
+    if (fault->isMachineCheckFault()) {
+        panic("address does not map to a BAR pa=%#x va=%#x size=%d",
+              req->paddr, req->vaddr, req->size);
 
-    if (Regs::regSize(daddr) == 0)
-        panic("invalid address: da=%#x pa=%#x va=%#x size=%d",
-              daddr, req->paddr, req->vaddr, req->size);
+        return genMachineCheckFault();
+    }
 
-    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);
+    return fault;
+}
 
-    DPRINTF(EthernetPIO, "read reg=%s da=%#x pa=%#x va=%#x size=%d\n",
-            Regs::regName(daddr), daddr, req->paddr, req->vaddr, req->size);
+Fault
+Device::readBar0(MemReqPtr &req, Addr daddr, uint8_t *data)
+{
+    int cpu = (req->xc->readMiscReg(TheISA::IPR_PALtemp16) >> 8) & 0xff;
+    Addr index = daddr >> Regs::VirtualShift;
+    Addr raddr = daddr & Regs::VirtualMask;
+
+    if (!regValid(raddr))
+        panic("invalid register: cpu=%d, da=%#x pa=%#x va=%#x size=%d",
+              cpu, daddr, req->paddr, req->vaddr, req->size);
+
+    const Regs::Info &info = regInfo(raddr);
+    if (!info.read)
+        panic("reading %s (write only): cpu=%d da=%#x pa=%#x va=%#x size=%d",
+              info.name, cpu, daddr, req->paddr, req->vaddr, req->size);
+
+    if (req->size != info.size)
+        panic("invalid size for reg %s: cpu=%d da=%#x pa=%#x va=%#x size=%d",
+              info.name, cpu, daddr, req->paddr, req->vaddr, req->size);
+
+    prepareRead(cpu, index);
+
+    uint64_t value = 0;
+    if (req->size == 4) {
+        uint32_t &reg = *(uint32_t *)data;
+        reg = regData32(raddr);
+        value = reg;
+    }
 
-    uint32_t &reg32 = *(uint32_t *)data;
-    uint64_t &reg64 = *(uint64_t *)data;
+    if (req->size == 8) {
+        uint64_t &reg = *(uint64_t *)data;
+        reg = regData64(raddr);
+        value = reg;
+    }
 
-    switch (daddr) {
-      case Regs::Config:
-        reg32 = regs.Config;
-        break;
+    DPRINTF(EthernetPIO,
+            "read %s cpu=%d da=%#x pa=%#x va=%#x size=%d val=%#x\n",
+            info.name, cpu, daddr, req->paddr, req->vaddr, req->size, value);
 
-      case Regs::RxMaxCopy:
-        reg32 = regs.RxMaxCopy;
-        break;
+    // reading the interrupt status register has the side effect of
+    // clearing it
+    if (raddr == Regs::IntrStatus)
+        devIntrClear();
 
-      case Regs::TxMaxCopy:
-        reg32 = regs.TxMaxCopy;
-        break;
+    return NoFault;
+}
 
-      case Regs::RxThreshold:
-        reg32 = regs.RxThreshold;
-        break;
+/**
+ * IPR read of device register
+ */
+Fault
+Device::iprRead(Addr daddr, int cpu, uint64_t &result)
+{
+    if (!regValid(daddr))
+        panic("invalid address: da=%#x", daddr);
 
-      case Regs::TxThreshold:
-        reg32 = regs.TxThreshold;
-        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::IntrStatus:
-        reg32 = regs.IntrStatus;
-        devIntrClear();
-        break;
+    DPRINTF(EthernetPIO, "IPR read %s: cpu=%d da=%#x\n",
+            info.name, cpu, daddr);
 
-      case Regs::IntrMask:
-        reg32 = regs.IntrMask;
-        break;
+    prepareRead(cpu, 0);
 
-      case Regs::RxData:
-        reg64 = regs.RxData;
-        break;
+    if (info.size == 4)
+        result = regData32(daddr);
 
-      case Regs::RxDone:
-      case Regs::RxWait:
-        reg64 = Regs::set_RxDone_FifoLen(regs.RxDone,
-                                         min(rxFifo.packets(), 255));
-        break;
+    if (info.size == 8)
+        result = regData64(daddr);
 
-      case Regs::TxData:
-        reg64 = regs.TxData;
-        break;
+    DPRINTF(EthernetPIO, "IPR read %s: cpu=%s da=%#x val=%#x\n",
+            info.name, cpu, result);
 
-      case Regs::TxDone:
-      case Regs::TxWait:
-        reg64 = Regs::set_TxDone_FifoLen(regs.TxDone,
-                                         min(txFifo.packets(), 255));
-        break;
+    return NoFault;
+}
 
-      case Regs::HwAddr:
-        reg64 = params()->eaddr;
-        break;
+/**
+ * I/O write of device register
+ */
+Fault
+Device::write(MemReqPtr &req, const uint8_t *data)
+{
+    assert(config.command & PCI_CMD_MSE);
+    Fault fault = writeBar(req, data);
 
-      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);
-    }
+    if (fault->isMachineCheckFault()) {
+        panic("address does not map to a BAR pa=%#x va=%#x size=%d",
+              req->paddr, req->vaddr, req->size);
 
-    DPRINTF(EthernetPIO, "read reg=%s done val=%#x\n", Regs::regName(daddr),
-            Regs::regSize(daddr) == 4 ? reg32 : reg64);
+        return genMachineCheckFault();
+    }
 
-    return No_Fault;
+    return fault;
 }
 
 Fault
-Device::write(MemReqPtr &req, const uint8_t *data)
+Device::writeBar0(MemReqPtr &req, Addr daddr, const uint8_t *data)
 {
-    assert(config.command & PCI_CMD_MSE);
-    Addr daddr = req->paddr & 0xfff;
+    int cpu = (req->xc->readMiscReg(TheISA::IPR_PALtemp16) >> 8) & 0xff;
+    Addr index = daddr >> Regs::VirtualShift;
+    Addr raddr = daddr & Regs::VirtualMask;
+
+    if (!regValid(raddr))
+        panic("invalid address: cpu=%d da=%#x pa=%#x va=%#x size=%d",
+              cpu, daddr, req->paddr, req->vaddr, req->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("writing %s (read only): cpu=%d da=%#x",
+              info.name, cpu, daddr);
 
-    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 (req->size != info.size)
+        panic("invalid size for %s: cpu=%d da=%#x pa=%#x va=%#x size=%d",
+              info.name, cpu, daddr, req->paddr, req->vaddr, req->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,
+    DPRINTF(EthernetPIO,
+            "write %s: cpu=%d val=%#x da=%#x pa=%#x va=%#x size=%d\n",
+            info.name, cpu, info.size == 4 ? reg32 : reg64,
             daddr, req->paddr, req->vaddr, req->size);
 
+    prepareWrite(cpu, index);
 
-    switch (daddr) {
+    switch (raddr) {
       case Regs::Config:
         changeConfig(reg32);
         break;
 
-      case Regs::RxThreshold:
-        regs.RxThreshold = reg32;
+      case Regs::Command:
+        command(reg32);
         break;
 
-      case Regs::TxThreshold:
-        regs.TxThreshold = reg32;
+      case Regs::IntrStatus:
+        devIntrClear(regs.IntrStatus & reg32);
         break;
 
       case Regs::IntrMask:
@@ -447,35 +518,36 @@ Device::write(MemReqPtr &req, const uint8_t *data)
         break;
 
       case Regs::RxData:
-        if (rxState != rxIdle)
-            panic("receive machine busy with another request!");
-
-        regs.RxDone = 0;
-        regs.RxData = reg64;
-        if (rxEnable) {
+        if (Regs::get_RxDone_Busy(vnic.RxDone))
+            panic("receive machine busy with another request! rxState=%s",
+                  RxStateStrings[rxState]);
+
+        vnic.RxDone = Regs::RxDone_Busy;
+        vnic.RxData = reg64;
+        rxList.push_back(index);
+        if (rxEnable && rxState == rxIdle) {
             rxState = rxFifoBlock;
             rxKick();
         }
         break;
 
       case Regs::TxData:
-        if (txState != txIdle)
-            panic("transmit machine busy with another request!");
-
-        regs.TxDone = 0;
-        regs.TxData = reg64;
-        if (txEnable) {
+        if (Regs::get_TxDone_Busy(vnic.TxDone))
+            panic("transmit machine busy with another request! txState=%s",
+                  TxStateStrings[txState]);
+
+        vnic.TxDone = Regs::TxDone_Busy;
+        vnic.TxData = reg64;
+        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 NoFault;
 }
 
 void
@@ -490,9 +562,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);
     }
@@ -628,12 +716,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) {
@@ -657,20 +739,57 @@ 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;
+    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.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();
+    txList.clear();
 
     rxState = rxIdle;
     txState = txIdle;
 
     rxFifo.clear();
+    rxFifoPtr = rxFifo.end();
     txFifo.clear();
+    rxEmpty = false;
+    txFull = false;
+
+    int size = virtualRegs.size();
+    virtualRegs.clear();
+    virtualRegs.resize(size);
+    for (int i = 0; i < size; ++i)
+        virtualRegs[i].rxPacket = rxFifo.end();
 }
 
 void
@@ -681,19 +800,26 @@ Device::rxDmaCopy()
     physmem->dma_write(rxDmaAddr, (uint8_t *)rxDmaData, rxDmaLen);
     DPRINTF(EthernetDMA, "rx dma write paddr=%#x len=%d\n",
             rxDmaAddr, rxDmaLen);
-    DDUMP(EthernetDMA, rxDmaData, rxDmaLen);
+    DDUMP(EthernetData, rxDmaData, rxDmaLen);
 }
 
 void
 Device::rxDmaDone()
 {
     rxDmaCopy();
+
+    // If the transmit state machine  has a pending DMA, let it go first
+    if (txState == txBeginCopy)
+        txKick();
+
     rxKick();
 }
 
 void
 Device::rxKick()
 {
+    VirtualReg *vnic;
+
     DPRINTF(EthernetSM, "receive kick rxState=%s (rxFifo.size=%d)\n",
             RxStateStrings[rxState], rxFifo.size());
 
@@ -704,56 +830,60 @@ Device::rxKick()
     }
 
   next:
-    switch (rxState) {
-      case rxIdle:
-        if (rxPioRequest) {
-            pioInterface->respond(rxPioRequest, curTick);
-            rxPioRequest = 0;
-        }
+    if (rxState == rxIdle)
         goto exit;
 
+    assert(!rxList.empty());
+    vnic = &virtualRegs[rxList.front()];
+
+    DPRINTF(EthernetSM, "processing rxState=%s for virtual nic %d\n",
+            RxStateStrings[rxState], rxList.front());
+
+    switch (rxState) {
       case rxFifoBlock:
-        if (rxPacket) {
+        if (vnic->rxPacket != rxFifo.end()) {
             rxState = rxBeginCopy;
             break;
         }
 
-        if (rxFifo.empty()) {
+        if (rxFifoPtr == rxFifo.end()) {
             DPRINTF(EthernetSM, "receive waiting for data.  Nothing to do.\n");
             goto exit;
         }
 
+        assert(!rxFifo.empty());
+
         // 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;
+                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;
+                    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;
                     }
                 }
             }
@@ -762,20 +892,21 @@ 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 && dmaInterface->busy())
+            goto exit;
+
+        rxDmaAddr = plat->pciToDma(Regs::get_RxData_Addr(vnic->RxData));
+        rxDmaLen = min<int>(Regs::get_RxData_Len(vnic->RxData),
+                            vnic->rxPacketBytes);
+        rxDmaData = (*vnic->rxPacket)->data + vnic->rxPacketOffset;
+        rxState = rxCopy;
 
         if (dmaInterface) {
-            if (!dmaInterface->busy()) {
-                dmaInterface->doDMA(WriteInvalidate, rxDmaAddr, rxDmaLen,
-                                    curTick, &rxDmaEvent, true);
-                rxState = rxCopy;
-            }
+            dmaInterface->doDMA(WriteInvalidate, rxDmaAddr, rxDmaLen,
+                                curTick, &rxDmaEvent, true);
             goto exit;
         }
 
-        rxState = rxCopy;
         if (dmaWriteDelay != 0 || dmaWriteFactor != 0) {
             Tick factor = ((rxDmaLen + ULL(63)) >> ULL(6)) * dmaWriteFactor;
             Tick start = curTick + dmaWriteDelay + factor;
@@ -791,20 +922,32 @@ Device::rxKick()
         goto exit;
 
       case rxCopyDone:
-        regs.RxDone = rxDoneData | rxDmaLen;
-
-        if (rxPktBytes == rxDmaLen) {
-            rxPacket = NULL;
-            rxFifo.pop();
+        vnic->RxDone = vnic->rxDoneData | rxDmaLen;
+        vnic->RxDone |= Regs::RxDone_Complete;
+
+        if (vnic->rxPacketBytes == rxDmaLen) {
+            DPRINTF(EthernetSM, "rxKick: packet complete on vnic %d\n",
+                    rxList.front());
+            rxFifo.remove(vnic->rxPacket);
+            vnic->rxPacket = rxFifo.end();
         } else {
-            regs.RxDone |= Regs::RxDone_More;
-            rxPktBytes -= rxDmaLen;
-            rxPacketBufPtr += rxDmaLen;
+            vnic->RxDone |= Regs::RxDone_More;
+            vnic->rxPacketBytes -= rxDmaLen;
+            vnic->rxPacketOffset += rxDmaLen;
+            DPRINTF(EthernetSM,
+                    "rxKick: packet not complete on vnic %d: %d bytes left\n",
+                    rxList.front(), vnic->rxPacketBytes);
         }
 
-        regs.RxDone |= Regs::RxDone_Complete;
-        devIntrPost(Regs::Intr_RxData);
-        rxState = rxIdle;
+        rxList.pop_front();
+        rxState = rxList.empty() ? rxIdle : rxFifoBlock;
+
+        if (rxFifo.empty()) {
+            devIntrPost(Regs::Intr_RxEmpty);
+            rxEmpty = true;
+        }
+
+        devIntrPost(Regs::Intr_RxDMA);
         break;
 
       default:
@@ -832,13 +975,18 @@ Device::txDmaCopy()
     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);
 }
 
 void
 Device::txDmaDone()
 {
     txDmaCopy();
+
+    // If the receive state machine  has a pending DMA, let it go first
+    if (rxState == rxBeginCopy)
+        rxKick();
+
     txKick();
 }
 
@@ -850,6 +998,7 @@ Device::transmit()
         return;
     }
 
+    uint32_t interrupts;
     PacketPtr packet = txFifo.front();
     if (!interface->sendPacket(packet)) {
         DPRINTF(Ethernet, "Packet Transmit: failed txFifo available %d\n",
@@ -858,7 +1007,6 @@ Device::transmit()
     }
 
     txFifo.pop();
-
 #if TRACING_ON
     if (DTRACE(Ethernet)) {
         IpPtr ip(packet);
@@ -873,17 +1021,17 @@ Device::transmit()
     }
 #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()) {
@@ -895,6 +1043,7 @@ Device::transmit()
 void
 Device::txKick()
 {
+    VirtualReg *vnic;
     DPRINTF(EthernetSM, "transmit kick txState=%s (txFifo.size=%d)\n",
             TxStateStrings[txState], txFifo.size());
 
@@ -905,23 +1054,23 @@ Device::txKick()
     }
 
   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->TxData));
         if (!txPacket) {
             // Grab a new packet from the fifo.
             txPacket = new PacketData(16384);
-            txPacketBufPtr = txPacket->data;
+            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;
         }
@@ -930,21 +1079,20 @@ 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 && dmaInterface->busy())
+            goto exit;
 
-        if (dmaInterface) {
-            if (!dmaInterface->busy()) {
-                dmaInterface->doDMA(Read, txDmaAddr, txDmaLen,
-                                    curTick, &txDmaEvent, true);
-                txState = txCopy;
-            }
+        txDmaAddr = plat->pciToDma(Regs::get_TxData_Addr(vnic->TxData));
+        txDmaLen = Regs::get_TxData_Len(vnic->TxData);
+        txDmaData = txPacket->data + txPacketOffset;
+        txState = txCopy;
 
+        if (dmaInterface) {
+            dmaInterface->doDMA(Read, txDmaAddr, txDmaLen,
+                                curTick, &txDmaEvent, true);
             goto exit;
         }
 
-        txState = txCopy;
         if (dmaReadDelay != 0 || dmaReadFactor != 0) {
             Tick factor = ((txDmaLen + ULL(63)) >> ULL(6)) * dmaReadFactor;
             Tick start = curTick + dmaReadDelay + factor;
@@ -960,41 +1108,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:
@@ -1094,8 +1250,8 @@ Device::recvPacket(PacketPtr packet)
         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,
@@ -1103,7 +1259,12 @@ Device::recvPacket(PacketPtr packet)
         return false;
     }
 
-    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;
 }
@@ -1161,56 +1322,96 @@ Device::serialize(ostream &os)
     // 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.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];
+
+        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);
+
+        PacketFifo::iterator rxFifoPtr;
+
+        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);
+    }
+
+    VirtualList::iterator i, end;
+    int count;
+
+    int rxListSize = rxList.size();
+    SERIALIZE_SCALAR(rxListSize);
+    for (count = 0, i = rxList.begin(), end = rxList.end(); i != end; ++i)
+        paramOut(os, csprintf("rxList%d", count++), *i);
+
+    int txListSize = txList.size();
+    SERIALIZE_SCALAR(txListSize);
+    for (count = 0, i = txList.begin(), end = txList.end(); i != end; ++i)
+        paramOut(os, csprintf("txList%d", count++), *i);
+
     /*
      * Serialize rx state machine
      */
     int rxState = this->rxState;
     SERIALIZE_SCALAR(rxState);
+    SERIALIZE_SCALAR(rxEmpty);
     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);
     }
 
     /*
@@ -1231,42 +1432,49 @@ 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.RxData);
     UNSERIALIZE_SCALAR(regs.RxDone);
     UNSERIALIZE_SCALAR(regs.TxData);
     UNSERIALIZE_SCALAR(regs.TxDone);
 
+    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 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);
     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);
 
     /*
      * Unserialize tx state machine
      */
     int txState;
     UNSERIALIZE_SCALAR(txState);
+    UNSERIALIZE_SCALAR(txFull);
     this->txState = (TxState) txState;
     txFifo.unserialize("txFifo", cp, section);
     bool txPacketExists;
@@ -1275,10 +1483,45 @@ Device::unserialize(Checkpoint *cp, const std::string &section)
     if (txPacketExists) {
         txPacket = new PacketData(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];
+        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);
+
+        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);
     }
 
     /*
@@ -1292,38 +1535,25 @@ Device::unserialize(Checkpoint *cp, const std::string &section)
     /*
      * re-add addrRanges to bus bridges
      */
-    if (pioInterface)
+    if (pioInterface) {
         pioInterface->addAddrRange(RangeSize(BARAddrs[0], BARSize[0]));
+        pioInterface->addAddrRange(RangeSize(BARAddrs[1], BARSize[1]));
+    }
 }
 
 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;
+    Addr daddr;
+    int bar;
+    if (!getBAR(req->paddr, daddr, bar))
+        panic("address does not map to a BAR pa=%#x va=%#x size=%d",
+              req->paddr, req->vaddr, req->size);
 
-      case Regs::TxDone:
-        if (txState != txIdle) {
-            txPioRequest = req;
-            when = 0;
-        }
-        break;
-    }
+    DPRINTF(EthernetPIO, "timing %s to paddr=%#x bar=%d daddr=%#x\n",
+            req->cmd.toString(), req->paddr, bar, daddr);
 
-    return when;
+    return curTick + pioLatency;
 }
 
 BEGIN_DECLARE_SIM_OBJECT_PARAMS(Interface)
@@ -1371,7 +1601,8 @@ BEGIN_DECLARE_SIM_OBJECT_PARAMS(Device)
     Param<uint32_t> pci_func;
 
     SimObjectParam<HierParams *> hier;
-    SimObjectParam<Bus*> io_bus;
+    SimObjectParam<Bus*> pio_bus;
+    SimObjectParam<Bus*> dma_bus;
     SimObjectParam<Bus*> payload_bus;
     Param<Tick> dma_read_delay;
     Param<Tick> dma_read_factor;
@@ -1385,6 +1616,7 @@ BEGIN_DECLARE_SIM_OBJECT_PARAMS(Device)
     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;
@@ -1392,6 +1624,9 @@ BEGIN_DECLARE_SIM_OBJECT_PARAMS(Device)
 
     Param<bool> rx_filter;
     Param<string> hardware_address;
+    Param<bool> rx_thread;
+    Param<bool> tx_thread;
+    Param<bool> rss;
 
 END_DECLARE_SIM_OBJECT_PARAMS(Device)
 
@@ -1410,7 +1645,8 @@ BEGIN_INIT_SIM_OBJECT_PARAMS(Device)
     INIT_PARAM(pci_func, "PCI function code"),
 
     INIT_PARAM(hier, "Hierarchy global variables"),
-    INIT_PARAM(io_bus, "The IO Bus to attach to for headers"),
+    INIT_PARAM(pio_bus, ""),
+    INIT_PARAM(dma_bus, ""),
     INIT_PARAM(payload_bus, "The IO Bus to attach to for payload"),
     INIT_PARAM(dma_read_delay, "fixed delay for dma reads"),
     INIT_PARAM(dma_read_factor, "multiplier for dma reads"),
@@ -1424,13 +1660,17 @@ BEGIN_INIT_SIM_OBJECT_PARAMS(Device)
     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(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(hardware_address, "Ethernet Hardware Address"),
+    INIT_PARAM(rx_thread, ""),
+    INIT_PARAM(tx_thread, ""),
+    INIT_PARAM(rss, "")
 
 END_INIT_SIM_OBJECT_PARAMS(Device)
 
@@ -1442,6 +1682,7 @@ CREATE_SIM_OBJECT(Device)
     params->name = getInstanceName();
 
     params->clock = clock;
+
     params->mmu = mmu;
     params->physmem = physmem;
     params->configSpace = configspace;
@@ -1452,7 +1693,8 @@ CREATE_SIM_OBJECT(Device)
     params->functionNum = pci_func;
 
     params->hier = hier;
-    params->io_bus = io_bus;
+    params->pio_bus = pio_bus;
+    params->header_bus = dma_bus;
     params->payload_bus = payload_bus;
     params->dma_read_delay = dma_read_delay;
     params->dma_read_factor = dma_read_factor;
@@ -1466,6 +1708,7 @@ CREATE_SIM_OBJECT(Device)
     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;
@@ -1473,6 +1716,9 @@ CREATE_SIM_OBJECT(Device)
 
     params->rx_filter = rx_filter;
     params->eaddr = hardware_address;
+    params->rx_thread = rx_thread;
+    params->tx_thread = tx_thread;
+    params->rss = rss;
 
     return new Device(params);
 }