sinic.cc revision 11202
1/*
2 * Copyright (c) 2004-2005 The Regents of The University of Michigan
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met: redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer;
9 * redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution;
12 * neither the name of the copyright holders nor the names of its
13 * contributors may be used to endorse or promote products derived from
14 * this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * Authors: Nathan Binkert
29 */
30
31#include <deque>
32#include <limits>
33#include <string>
34
35#ifdef SINIC_VTOPHYS
36#include "arch/vtophys.hh"
37#endif
38#include "base/compiler.hh"
39#include "base/debug.hh"
40#include "base/inet.hh"
41#include "base/types.hh"
42#include "config/the_isa.hh"
43#include "debug/EthernetAll.hh"
44#include "dev/etherlink.hh"
45#include "dev/sinic.hh"
46#include "mem/packet.hh"
47#include "mem/packet_access.hh"
48#include "sim/eventq.hh"
49#include "sim/stats.hh"
50
51using namespace std;
52using namespace Net;
53using namespace TheISA;
54
55namespace Sinic {
56
57const char *RxStateStrings[] =
58{
59    "rxIdle",
60    "rxFifoBlock",
61    "rxBeginCopy",
62    "rxCopy",
63    "rxCopyDone"
64};
65
66const char *TxStateStrings[] =
67{
68    "txIdle",
69    "txFifoBlock",
70    "txBeginCopy",
71    "txCopy",
72    "txCopyDone"
73};
74
75
76///////////////////////////////////////////////////////////////////////
77//
78// Sinic PCI Device
79//
80Base::Base(const Params *p)
81    : EtherDevBase(p), rxEnable(false), txEnable(false),
82      intrDelay(p->intr_delay), intrTick(0), cpuIntrEnable(false),
83      cpuPendingIntr(false), intrEvent(0), interface(NULL)
84{
85}
86
87Device::Device(const Params *p)
88    : Base(p), rxUnique(0), txUnique(0),
89      virtualRegs(p->virtual_count < 1 ? 1 : p->virtual_count),
90      rxFifo(p->rx_fifo_size), txFifo(p->tx_fifo_size),
91      rxKickTick(0), txKickTick(0),
92      txEvent(this), rxDmaEvent(this), txDmaEvent(this),
93      dmaReadDelay(p->dma_read_delay), dmaReadFactor(p->dma_read_factor),
94      dmaWriteDelay(p->dma_write_delay), dmaWriteFactor(p->dma_write_factor)
95{
96    interface = new Interface(name() + ".int0", this);
97    reset();
98
99}
100
101Device::~Device()
102{}
103
104void
105Device::regStats()
106{
107    Base::regStats();
108
109    _maxVnicDistance = 0;
110
111    maxVnicDistance
112        .name(name() + ".maxVnicDistance")
113        .desc("maximum vnic distance")
114        ;
115
116    totalVnicDistance
117        .name(name() + ".totalVnicDistance")
118        .desc("total vnic distance")
119        ;
120    numVnicDistance
121        .name(name() + ".numVnicDistance")
122        .desc("number of vnic distance measurements")
123        ;
124
125    avgVnicDistance
126        .name(name() + ".avgVnicDistance")
127        .desc("average vnic distance")
128        ;
129
130    avgVnicDistance = totalVnicDistance / numVnicDistance;
131}
132
133void
134Device::resetStats()
135{
136    Base::resetStats();
137
138    _maxVnicDistance = 0;
139}
140
141EtherInt*
142Device::getEthPort(const std::string &if_name, int idx)
143{
144    if (if_name == "interface") {
145        if (interface->getPeer())
146            panic("interface already connected to\n");
147
148        return interface;
149    }
150    return NULL;
151}
152
153
154void
155Device::prepareIO(ContextID cpu, int index)
156{
157    int size = virtualRegs.size();
158    if (index > size)
159        panic("Trying to access a vnic that doesn't exist %d > %d\n",
160              index, size);
161}
162
163//add stats for head of line blocking
164//add stats for average fifo length
165//add stats for average number of vnics busy
166
167void
168Device::prepareRead(ContextID cpu, int index)
169{
170    using namespace Regs;
171    prepareIO(cpu, index);
172
173    VirtualReg &vnic = virtualRegs[index];
174
175    // update rx registers
176    uint64_t rxdone = vnic.RxDone;
177    rxdone = set_RxDone_Packets(rxdone, rxFifo.countPacketsAfter(rxFifoPtr));
178    rxdone = set_RxDone_Empty(rxdone, rxFifo.empty());
179    rxdone = set_RxDone_High(rxdone, rxFifo.size() > regs.RxFifoHigh);
180    rxdone = set_RxDone_NotHigh(rxdone, rxLow);
181    regs.RxData = vnic.RxData;
182    regs.RxDone = rxdone;
183    regs.RxWait = rxdone;
184
185    // update tx regsiters
186    uint64_t txdone = vnic.TxDone;
187    txdone = set_TxDone_Packets(txdone, txFifo.packets());
188    txdone = set_TxDone_Full(txdone, txFifo.avail() < regs.TxMaxCopy);
189    txdone = set_TxDone_Low(txdone, txFifo.size() < regs.TxFifoLow);
190    regs.TxData = vnic.TxData;
191    regs.TxDone = txdone;
192    regs.TxWait = txdone;
193
194    int head = 0xffff;
195
196    if (!rxFifo.empty()) {
197        int vnic = rxFifo.begin()->priv;
198        if (vnic != -1 && virtualRegs[vnic].rxPacketOffset > 0)
199            head = vnic;
200    }
201
202    regs.RxStatus = set_RxStatus_Head(regs.RxStatus, head);
203    regs.RxStatus = set_RxStatus_Busy(regs.RxStatus, rxBusyCount);
204    regs.RxStatus = set_RxStatus_Mapped(regs.RxStatus, rxMappedCount);
205    regs.RxStatus = set_RxStatus_Dirty(regs.RxStatus, rxDirtyCount);
206}
207
208void
209Device::prepareWrite(ContextID cpu, int index)
210{
211    prepareIO(cpu, index);
212}
213
214/**
215 * I/O read of device register
216 */
217Tick
218Device::read(PacketPtr pkt)
219{
220    assert(config.command & PCI_CMD_MSE);
221    assert(pkt->getAddr() >= BARAddrs[0] && pkt->getSize() < BARSize[0]);
222
223    ContextID cpu = pkt->req->contextId();
224    Addr daddr = pkt->getAddr() - BARAddrs[0];
225    Addr index = daddr >> Regs::VirtualShift;
226    Addr raddr = daddr & Regs::VirtualMask;
227
228    if (!regValid(raddr))
229        panic("invalid register: cpu=%d vnic=%d da=%#x pa=%#x size=%d",
230              cpu, index, daddr, pkt->getAddr(), pkt->getSize());
231
232    const Regs::Info &info = regInfo(raddr);
233    if (!info.read)
234        panic("read %s (write only): "
235              "cpu=%d vnic=%d da=%#x pa=%#x size=%d",
236              info.name, cpu, index, daddr, pkt->getAddr(), pkt->getSize());
237
238        panic("read %s (invalid size): "
239              "cpu=%d vnic=%d da=%#x pa=%#x size=%d",
240              info.name, cpu, index, daddr, pkt->getAddr(), pkt->getSize());
241
242    prepareRead(cpu, index);
243
244    uint64_t value M5_VAR_USED = 0;
245    if (pkt->getSize() == 4) {
246        uint32_t reg = regData32(raddr);
247        pkt->set(reg);
248        value = reg;
249    }
250
251    if (pkt->getSize() == 8) {
252        uint64_t reg = regData64(raddr);
253        pkt->set(reg);
254        value = reg;
255    }
256
257    DPRINTF(EthernetPIO,
258            "read %s: cpu=%d vnic=%d da=%#x pa=%#x size=%d val=%#x\n",
259            info.name, cpu, index, daddr, pkt->getAddr(), pkt->getSize(), value);
260
261    // reading the interrupt status register has the side effect of
262    // clearing it
263    if (raddr == Regs::IntrStatus)
264        devIntrClear();
265
266    return pioDelay;
267}
268
269/**
270 * IPR read of device register
271
272    Fault
273Device::iprRead(Addr daddr, ContextID cpu, uint64_t &result)
274{
275    if (!regValid(daddr))
276        panic("invalid address: da=%#x", daddr);
277
278    const Regs::Info &info = regInfo(daddr);
279    if (!info.read)
280        panic("reading %s (write only): cpu=%d da=%#x", info.name, cpu, daddr);
281
282    DPRINTF(EthernetPIO, "IPR read %s: cpu=%d da=%#x\n",
283            info.name, cpu, daddr);
284
285    prepareRead(cpu, 0);
286
287    if (info.size == 4)
288        result = regData32(daddr);
289
290    if (info.size == 8)
291        result = regData64(daddr);
292
293    DPRINTF(EthernetPIO, "IPR read %s: cpu=%s da=%#x val=%#x\n",
294            info.name, cpu, result);
295
296    return NoFault;
297}
298*/
299/**
300 * I/O write of device register
301 */
302Tick
303Device::write(PacketPtr pkt)
304{
305    assert(config.command & PCI_CMD_MSE);
306    assert(pkt->getAddr() >= BARAddrs[0] && pkt->getSize() < BARSize[0]);
307
308    ContextID cpu = pkt->req->contextId();
309    Addr daddr = pkt->getAddr() - BARAddrs[0];
310    Addr index = daddr >> Regs::VirtualShift;
311    Addr raddr = daddr & Regs::VirtualMask;
312
313    if (!regValid(raddr))
314        panic("invalid register: cpu=%d, da=%#x pa=%#x size=%d",
315                cpu, daddr, pkt->getAddr(), pkt->getSize());
316
317    const Regs::Info &info = regInfo(raddr);
318    if (!info.write)
319        panic("write %s (read only): "
320              "cpu=%d vnic=%d da=%#x pa=%#x size=%d",
321              info.name, cpu, index, daddr, pkt->getAddr(), pkt->getSize());
322
323    if (pkt->getSize() != info.size)
324        panic("write %s (invalid size): "
325              "cpu=%d vnic=%d da=%#x pa=%#x size=%d",
326              info.name, cpu, index, daddr, pkt->getAddr(), pkt->getSize());
327
328    VirtualReg &vnic = virtualRegs[index];
329
330    DPRINTF(EthernetPIO,
331            "write %s vnic %d: cpu=%d val=%#x da=%#x pa=%#x size=%d\n",
332            info.name, index, cpu, info.size == 4 ? pkt->get<uint32_t>() :
333            pkt->get<uint64_t>(), daddr, pkt->getAddr(), pkt->getSize());
334
335    prepareWrite(cpu, index);
336
337    switch (raddr) {
338      case Regs::Config:
339        changeConfig(pkt->get<uint32_t>());
340        break;
341
342      case Regs::Command:
343        command(pkt->get<uint32_t>());
344        break;
345
346      case Regs::IntrStatus:
347        devIntrClear(regs.IntrStatus & pkt->get<uint32_t>());
348        break;
349
350      case Regs::IntrMask:
351        devIntrChangeMask(pkt->get<uint32_t>());
352        break;
353
354      case Regs::RxData:
355        if (Regs::get_RxDone_Busy(vnic.RxDone))
356            panic("receive machine busy with another request! rxState=%s",
357                  RxStateStrings[rxState]);
358
359        vnic.rxUnique = rxUnique++;
360        vnic.RxDone = Regs::RxDone_Busy;
361        vnic.RxData = pkt->get<uint64_t>();
362        rxBusyCount++;
363
364        if (Regs::get_RxData_Vaddr(pkt->get<uint64_t>())) {
365            panic("vtophys not implemented in newmem");
366#ifdef SINIC_VTOPHYS
367            Addr vaddr = Regs::get_RxData_Addr(reg64);
368            Addr paddr = vtophys(req->xc, vaddr);
369            DPRINTF(EthernetPIO, "write RxData vnic %d (rxunique %d): "
370                    "vaddr=%#x, paddr=%#x\n",
371                    index, vnic.rxUnique, vaddr, paddr);
372
373            vnic.RxData = Regs::set_RxData_Addr(vnic.RxData, paddr);
374#endif
375        } else {
376            DPRINTF(EthernetPIO, "write RxData vnic %d (rxunique %d)\n",
377                    index, vnic.rxUnique);
378        }
379
380        if (vnic.rxIndex == rxFifo.end()) {
381            DPRINTF(EthernetPIO, "request new packet...appending to rxList\n");
382            rxList.push_back(index);
383        } else {
384            DPRINTF(EthernetPIO, "packet exists...appending to rxBusy\n");
385            rxBusy.push_back(index);
386        }
387
388        if (rxEnable && (rxState == rxIdle || rxState == rxFifoBlock)) {
389            rxState = rxFifoBlock;
390            rxKick();
391        }
392        break;
393
394      case Regs::TxData:
395        if (Regs::get_TxDone_Busy(vnic.TxDone))
396            panic("transmit machine busy with another request! txState=%s",
397                  TxStateStrings[txState]);
398
399        vnic.txUnique = txUnique++;
400        vnic.TxDone = Regs::TxDone_Busy;
401
402        if (Regs::get_TxData_Vaddr(pkt->get<uint64_t>())) {
403            panic("vtophys won't work here in newmem.\n");
404#ifdef SINIC_VTOPHYS
405            Addr vaddr = Regs::get_TxData_Addr(reg64);
406            Addr paddr = vtophys(req->xc, vaddr);
407            DPRINTF(EthernetPIO, "write TxData vnic %d (txunique %d): "
408                    "vaddr=%#x, paddr=%#x\n",
409                    index, vnic.txUnique, vaddr, paddr);
410
411            vnic.TxData = Regs::set_TxData_Addr(vnic.TxData, paddr);
412#endif
413        } else {
414            DPRINTF(EthernetPIO, "write TxData vnic %d (txunique %d)\n",
415                    index, vnic.txUnique);
416        }
417
418        if (txList.empty() || txList.front() != index)
419            txList.push_back(index);
420        if (txEnable && txState == txIdle && txList.front() == index) {
421            txState = txFifoBlock;
422            txKick();
423        }
424        break;
425    }
426
427    return pioDelay;
428}
429
430void
431Device::devIntrPost(uint32_t interrupts)
432{
433    if ((interrupts & Regs::Intr_Res))
434        panic("Cannot set a reserved interrupt");
435
436    regs.IntrStatus |= interrupts;
437
438    DPRINTF(EthernetIntr,
439            "interrupt written to intStatus: intr=%#x status=%#x mask=%#x\n",
440            interrupts, regs.IntrStatus, regs.IntrMask);
441
442    interrupts = regs.IntrStatus & regs.IntrMask;
443
444    // Intr_RxHigh is special, we only signal it if we've emptied the fifo
445    // and then filled it above the high watermark
446    if (rxEmpty)
447        rxEmpty = false;
448    else
449        interrupts &= ~Regs::Intr_RxHigh;
450
451    // Intr_TxLow is special, we only signal it if we've filled up the fifo
452    // and then dropped below the low watermark
453    if (txFull)
454        txFull = false;
455    else
456        interrupts &= ~Regs::Intr_TxLow;
457
458    if (interrupts) {
459        Tick when = curTick();
460        if ((interrupts & Regs::Intr_NoDelay) == 0)
461            when += intrDelay;
462        cpuIntrPost(when);
463    }
464}
465
466void
467Device::devIntrClear(uint32_t interrupts)
468{
469    if ((interrupts & Regs::Intr_Res))
470        panic("Cannot clear a reserved interrupt");
471
472    regs.IntrStatus &= ~interrupts;
473
474    DPRINTF(EthernetIntr,
475            "interrupt cleared from intStatus: intr=%x status=%x mask=%x\n",
476            interrupts, regs.IntrStatus, regs.IntrMask);
477
478    if (!(regs.IntrStatus & regs.IntrMask))
479        cpuIntrClear();
480}
481
482void
483Device::devIntrChangeMask(uint32_t newmask)
484{
485    if (regs.IntrMask == newmask)
486        return;
487
488    regs.IntrMask = newmask;
489
490    DPRINTF(EthernetIntr,
491            "interrupt mask changed: intStatus=%x intMask=%x masked=%x\n",
492            regs.IntrStatus, regs.IntrMask, regs.IntrStatus & regs.IntrMask);
493
494    if (regs.IntrStatus & regs.IntrMask)
495        cpuIntrPost(curTick());
496    else
497        cpuIntrClear();
498}
499
500void
501Base::cpuIntrPost(Tick when)
502{
503    // If the interrupt you want to post is later than an interrupt
504    // already scheduled, just let it post in the coming one and don't
505    // schedule another.
506    // HOWEVER, must be sure that the scheduled intrTick is in the
507    // future (this was formerly the source of a bug)
508    /**
509     * @todo this warning should be removed and the intrTick code should
510     * be fixed.
511     */
512    assert(when >= curTick());
513    assert(intrTick >= curTick() || intrTick == 0);
514    if (!cpuIntrEnable) {
515        DPRINTF(EthernetIntr, "interrupts not enabled.\n",
516                intrTick);
517        return;
518    }
519
520    if (when > intrTick && intrTick != 0) {
521        DPRINTF(EthernetIntr, "don't need to schedule event...intrTick=%d\n",
522                intrTick);
523        return;
524    }
525
526    intrTick = when;
527    if (intrTick < curTick()) {
528        Debug::breakpoint();
529        intrTick = curTick();
530    }
531
532    DPRINTF(EthernetIntr, "going to schedule an interrupt for intrTick=%d\n",
533            intrTick);
534
535    if (intrEvent)
536        intrEvent->squash();
537    intrEvent = new IntrEvent(this, true);
538    schedule(intrEvent, intrTick);
539}
540
541void
542Base::cpuInterrupt()
543{
544    assert(intrTick == curTick());
545
546    // Whether or not there's a pending interrupt, we don't care about
547    // it anymore
548    intrEvent = 0;
549    intrTick = 0;
550
551    // Don't send an interrupt if there's already one
552    if (cpuPendingIntr) {
553        DPRINTF(EthernetIntr,
554                "would send an interrupt now, but there's already pending\n");
555    } else {
556        // Send interrupt
557        cpuPendingIntr = true;
558
559        DPRINTF(EthernetIntr, "posting interrupt\n");
560        intrPost();
561    }
562}
563
564void
565Base::cpuIntrClear()
566{
567    if (!cpuPendingIntr)
568        return;
569
570    if (intrEvent) {
571        intrEvent->squash();
572        intrEvent = 0;
573    }
574
575    intrTick = 0;
576
577    cpuPendingIntr = false;
578
579    DPRINTF(EthernetIntr, "clearing cchip interrupt\n");
580    intrClear();
581}
582
583bool
584Base::cpuIntrPending() const
585{ return cpuPendingIntr; }
586
587void
588Device::changeConfig(uint32_t newconf)
589{
590    uint32_t changed = regs.Config ^ newconf;
591    if (!changed)
592        return;
593
594    regs.Config = newconf;
595
596    if ((changed & Regs::Config_IntEn)) {
597        cpuIntrEnable = regs.Config & Regs::Config_IntEn;
598        if (cpuIntrEnable) {
599            if (regs.IntrStatus & regs.IntrMask)
600                cpuIntrPost(curTick());
601        } else {
602            cpuIntrClear();
603        }
604    }
605
606    if ((changed & Regs::Config_TxEn)) {
607        txEnable = regs.Config & Regs::Config_TxEn;
608        if (txEnable)
609            txKick();
610    }
611
612    if ((changed & Regs::Config_RxEn)) {
613        rxEnable = regs.Config & Regs::Config_RxEn;
614        if (rxEnable)
615            rxKick();
616    }
617}
618
619void
620Device::command(uint32_t command)
621{
622    if (command & Regs::Command_Intr)
623        devIntrPost(Regs::Intr_Soft);
624
625    if (command & Regs::Command_Reset)
626        reset();
627}
628
629void
630Device::reset()
631{
632    using namespace Regs;
633
634    memset(&regs, 0, sizeof(regs));
635
636    regs.Config = 0;
637    if (params()->rx_thread)
638        regs.Config |= Config_RxThread;
639    if (params()->tx_thread)
640        regs.Config |= Config_TxThread;
641    if (params()->rss)
642        regs.Config |= Config_RSS;
643    if (params()->zero_copy)
644        regs.Config |= Config_ZeroCopy;
645    if (params()->delay_copy)
646        regs.Config |= Config_DelayCopy;
647    if (params()->virtual_addr)
648        regs.Config |= Config_Vaddr;
649
650    if (params()->delay_copy && params()->zero_copy)
651        panic("Can't delay copy and zero copy");
652
653    regs.IntrMask = Intr_Soft | Intr_RxHigh | Intr_RxPacket | Intr_TxLow;
654    regs.RxMaxCopy = params()->rx_max_copy;
655    regs.TxMaxCopy = params()->tx_max_copy;
656    regs.ZeroCopySize = params()->zero_copy_size;
657    regs.ZeroCopyMark = params()->zero_copy_threshold;
658    regs.VirtualCount = params()->virtual_count;
659    regs.RxMaxIntr = params()->rx_max_intr;
660    regs.RxFifoSize = params()->rx_fifo_size;
661    regs.TxFifoSize = params()->tx_fifo_size;
662    regs.RxFifoLow = params()->rx_fifo_low_mark;
663    regs.TxFifoLow = params()->tx_fifo_threshold;
664    regs.RxFifoHigh = params()->rx_fifo_threshold;
665    regs.TxFifoHigh = params()->tx_fifo_high_mark;
666    regs.HwAddr = params()->hardware_address;
667
668    if (regs.RxMaxCopy < regs.ZeroCopyMark)
669        panic("Must be able to copy at least as many bytes as the threshold");
670
671    if (regs.ZeroCopySize >= regs.ZeroCopyMark)
672        panic("The number of bytes to copy must be less than the threshold");
673
674    rxList.clear();
675    rxBusy.clear();
676    rxActive = -1;
677    txList.clear();
678    rxBusyCount = 0;
679    rxDirtyCount = 0;
680    rxMappedCount = 0;
681
682    rxState = rxIdle;
683    txState = txIdle;
684
685    rxFifo.clear();
686    rxFifoPtr = rxFifo.end();
687    txFifo.clear();
688    rxEmpty = false;
689    rxLow = true;
690    txFull = false;
691
692    int size = virtualRegs.size();
693    virtualRegs.clear();
694    virtualRegs.resize(size);
695    for (int i = 0; i < size; ++i)
696        virtualRegs[i].rxIndex = rxFifo.end();
697}
698
699void
700Device::rxDmaDone()
701{
702    assert(rxState == rxCopy);
703    rxState = rxCopyDone;
704    DPRINTF(EthernetDMA, "end rx dma write paddr=%#x len=%d\n",
705            rxDmaAddr, rxDmaLen);
706    DDUMP(EthernetData, rxDmaData, rxDmaLen);
707
708    // If the transmit state machine  has a pending DMA, let it go first
709    if (txState == txBeginCopy)
710        txKick();
711
712    rxKick();
713}
714
715void
716Device::rxKick()
717{
718    VirtualReg *vnic = NULL;
719
720    DPRINTF(EthernetSM, "rxKick: rxState=%s (rxFifo.size=%d)\n",
721            RxStateStrings[rxState], rxFifo.size());
722
723    if (rxKickTick > curTick()) {
724        DPRINTF(EthernetSM, "rxKick: exiting, can't run till %d\n",
725                rxKickTick);
726        return;
727    }
728
729  next:
730    rxFifo.check();
731    if (rxState == rxIdle)
732        goto exit;
733
734    if (rxActive == -1) {
735        if (rxState != rxFifoBlock)
736            panic("no active vnic while in state %s", RxStateStrings[rxState]);
737
738        DPRINTF(EthernetSM, "processing rxState=%s\n",
739                RxStateStrings[rxState]);
740    } else {
741        vnic = &virtualRegs[rxActive];
742        DPRINTF(EthernetSM,
743                "processing rxState=%s for vnic %d (rxunique %d)\n",
744                RxStateStrings[rxState], rxActive, vnic->rxUnique);
745    }
746
747    switch (rxState) {
748      case rxFifoBlock:
749        if (DTRACE(EthernetSM)) {
750            PacketFifo::iterator end = rxFifo.end();
751            int size = virtualRegs.size();
752            for (int i = 0; i < size; ++i) {
753                VirtualReg *vn = &virtualRegs[i];
754                bool busy = Regs::get_RxDone_Busy(vn->RxDone);
755                if (vn->rxIndex != end) {
756#ifndef NDEBUG
757                    bool dirty = vn->rxPacketOffset > 0;
758                    const char *status;
759
760                    if (busy && dirty)
761                        status = "busy,dirty";
762                    else if (busy)
763                        status = "busy";
764                    else if (dirty)
765                        status = "dirty";
766                    else
767                        status = "mapped";
768
769                    DPRINTF(EthernetSM,
770                            "vnic %d %s (rxunique %d), packet %d, slack %d\n",
771                            i, status, vn->rxUnique,
772                            rxFifo.countPacketsBefore(vn->rxIndex),
773                            vn->rxIndex->slack);
774#endif
775                } else if (busy) {
776                    DPRINTF(EthernetSM, "vnic %d unmapped (rxunique %d)\n",
777                            i, vn->rxUnique);
778                }
779            }
780        }
781
782        if (!rxBusy.empty()) {
783            rxActive = rxBusy.front();
784            rxBusy.pop_front();
785            vnic = &virtualRegs[rxActive];
786
787            if (vnic->rxIndex == rxFifo.end())
788                panic("continuing vnic without packet\n");
789
790            DPRINTF(EthernetSM,
791                    "continue processing for vnic %d (rxunique %d)\n",
792                    rxActive, vnic->rxUnique);
793
794            rxState = rxBeginCopy;
795
796            int vnic_distance = rxFifo.countPacketsBefore(vnic->rxIndex);
797            totalVnicDistance += vnic_distance;
798            numVnicDistance += 1;
799            if (vnic_distance > _maxVnicDistance) {
800                maxVnicDistance = vnic_distance;
801                _maxVnicDistance = vnic_distance;
802            }
803
804            break;
805        }
806
807        if (rxFifoPtr == rxFifo.end()) {
808            DPRINTF(EthernetSM, "receive waiting for data.  Nothing to do.\n");
809            goto exit;
810        }
811
812        if (rxList.empty())
813            panic("Not idle, but nothing to do!");
814
815        assert(!rxFifo.empty());
816
817        rxActive = rxList.front();
818        rxList.pop_front();
819        vnic = &virtualRegs[rxActive];
820
821        DPRINTF(EthernetSM,
822                "processing new packet for vnic %d (rxunique %d)\n",
823                rxActive, vnic->rxUnique);
824
825        // Grab a new packet from the fifo.
826        vnic->rxIndex = rxFifoPtr++;
827        vnic->rxIndex->priv = rxActive;
828        vnic->rxPacketOffset = 0;
829        vnic->rxPacketBytes = vnic->rxIndex->packet->length;
830        assert(vnic->rxPacketBytes);
831        rxMappedCount++;
832
833        vnic->rxDoneData = 0;
834        /* scope for variables */ {
835            IpPtr ip(vnic->rxIndex->packet);
836            if (ip) {
837                DPRINTF(Ethernet, "ID is %d\n", ip->id());
838                vnic->rxDoneData |= Regs::RxDone_IpPacket;
839                rxIpChecksums++;
840                if (cksum(ip) != 0) {
841                    DPRINTF(EthernetCksum, "Rx IP Checksum Error\n");
842                    vnic->rxDoneData |= Regs::RxDone_IpError;
843                }
844                TcpPtr tcp(ip);
845                UdpPtr udp(ip);
846                if (tcp) {
847                    DPRINTF(Ethernet,
848                            "Src Port=%d, Dest Port=%d, Seq=%d, Ack=%d\n",
849                            tcp->sport(), tcp->dport(), tcp->seq(),
850                            tcp->ack());
851                    vnic->rxDoneData |= Regs::RxDone_TcpPacket;
852                    rxTcpChecksums++;
853                    if (cksum(tcp) != 0) {
854                        DPRINTF(EthernetCksum, "Rx TCP Checksum Error\n");
855                        vnic->rxDoneData |= Regs::RxDone_TcpError;
856                    }
857                } else if (udp) {
858                    vnic->rxDoneData |= Regs::RxDone_UdpPacket;
859                    rxUdpChecksums++;
860                    if (cksum(udp) != 0) {
861                        DPRINTF(EthernetCksum, "Rx UDP Checksum Error\n");
862                        vnic->rxDoneData |= Regs::RxDone_UdpError;
863                    }
864                }
865            }
866        }
867        rxState = rxBeginCopy;
868        break;
869
870      case rxBeginCopy:
871        if (dmaPending() || drainState() != DrainState::Running)
872            goto exit;
873
874        rxDmaAddr = pciToDma(Regs::get_RxData_Addr(vnic->RxData));
875        rxDmaLen = min<unsigned>(Regs::get_RxData_Len(vnic->RxData),
876                                 vnic->rxPacketBytes);
877
878        /*
879         * if we're doing zero/delay copy and we're below the fifo
880         * threshold, see if we should try to do the zero/defer copy
881         */
882        if ((Regs::get_Config_ZeroCopy(regs.Config) ||
883             Regs::get_Config_DelayCopy(regs.Config)) &&
884            !Regs::get_RxData_NoDelay(vnic->RxData) && rxLow) {
885            if (rxDmaLen > regs.ZeroCopyMark)
886                rxDmaLen = regs.ZeroCopySize;
887        }
888        rxDmaData = vnic->rxIndex->packet->data + vnic->rxPacketOffset;
889        rxState = rxCopy;
890        if (rxDmaAddr == 1LL) {
891            rxState = rxCopyDone;
892            break;
893        }
894
895        dmaWrite(rxDmaAddr, rxDmaLen, &rxDmaEvent, rxDmaData);
896        break;
897
898      case rxCopy:
899        DPRINTF(EthernetSM, "receive machine still copying\n");
900        goto exit;
901
902      case rxCopyDone:
903        vnic->RxDone = vnic->rxDoneData;
904        vnic->RxDone |= Regs::RxDone_Complete;
905        rxBusyCount--;
906
907        if (vnic->rxPacketBytes == rxDmaLen) {
908            if (vnic->rxPacketOffset)
909                rxDirtyCount--;
910
911            // Packet is complete.  Indicate how many bytes were copied
912            vnic->RxDone = Regs::set_RxDone_CopyLen(vnic->RxDone, rxDmaLen);
913
914            DPRINTF(EthernetSM,
915                    "rxKick: packet complete on vnic %d (rxunique %d)\n",
916                    rxActive, vnic->rxUnique);
917            rxFifo.remove(vnic->rxIndex);
918            vnic->rxIndex = rxFifo.end();
919            rxMappedCount--;
920        } else {
921            if (!vnic->rxPacketOffset)
922                rxDirtyCount++;
923
924            vnic->rxPacketBytes -= rxDmaLen;
925            vnic->rxPacketOffset += rxDmaLen;
926            vnic->RxDone |= Regs::RxDone_More;
927            vnic->RxDone = Regs::set_RxDone_CopyLen(vnic->RxDone,
928                                                    vnic->rxPacketBytes);
929            DPRINTF(EthernetSM,
930                    "rxKick: packet not complete on vnic %d (rxunique %d): "
931                    "%d bytes left\n",
932                    rxActive, vnic->rxUnique, vnic->rxPacketBytes);
933        }
934
935        rxActive = -1;
936        rxState = rxBusy.empty() && rxList.empty() ? rxIdle : rxFifoBlock;
937
938        if (rxFifo.empty()) {
939            devIntrPost(Regs::Intr_RxEmpty);
940            rxEmpty = true;
941        }
942
943        if (rxFifo.size() < regs.RxFifoLow)
944            rxLow = true;
945
946        if (rxFifo.size() > regs.RxFifoHigh)
947            rxLow = false;
948
949        devIntrPost(Regs::Intr_RxDMA);
950        break;
951
952      default:
953        panic("Invalid rxState!");
954    }
955
956    DPRINTF(EthernetSM, "entering next rxState=%s\n",
957            RxStateStrings[rxState]);
958
959    goto next;
960
961  exit:
962    /**
963     * @todo do we want to schedule a future kick?
964     */
965    DPRINTF(EthernetSM, "rx state machine exited rxState=%s\n",
966            RxStateStrings[rxState]);
967}
968
969void
970Device::txDmaDone()
971{
972    assert(txState == txCopy);
973    txState = txCopyDone;
974    DPRINTF(EthernetDMA, "tx dma read paddr=%#x len=%d\n",
975            txDmaAddr, txDmaLen);
976    DDUMP(EthernetData, txDmaData, txDmaLen);
977
978    // If the receive state machine  has a pending DMA, let it go first
979    if (rxState == rxBeginCopy)
980        rxKick();
981
982    txKick();
983}
984
985void
986Device::transmit()
987{
988    if (txFifo.empty()) {
989        DPRINTF(Ethernet, "nothing to transmit\n");
990        return;
991    }
992
993    uint32_t interrupts;
994    EthPacketPtr packet = txFifo.front();
995    if (!interface->sendPacket(packet)) {
996        DPRINTF(Ethernet, "Packet Transmit: failed txFifo available %d\n",
997                txFifo.avail());
998        return;
999    }
1000
1001    txFifo.pop();
1002#if TRACING_ON
1003    if (DTRACE(Ethernet)) {
1004        IpPtr ip(packet);
1005        if (ip) {
1006            DPRINTF(Ethernet, "ID is %d\n", ip->id());
1007            TcpPtr tcp(ip);
1008            if (tcp) {
1009                DPRINTF(Ethernet,
1010                        "Src Port=%d, Dest Port=%d, Seq=%d, Ack=%d\n",
1011                        tcp->sport(), tcp->dport(), tcp->seq(),
1012                        tcp->ack());
1013            }
1014        }
1015    }
1016#endif
1017
1018    DDUMP(EthernetData, packet->data, packet->length);
1019    txBytes += packet->length;
1020    txPackets++;
1021
1022    DPRINTF(Ethernet, "Packet Transmit: successful txFifo Available %d\n",
1023            txFifo.avail());
1024
1025    interrupts = Regs::Intr_TxPacket;
1026    if (txFifo.size() < regs.TxFifoLow)
1027        interrupts |= Regs::Intr_TxLow;
1028    devIntrPost(interrupts);
1029}
1030
1031void
1032Device::txKick()
1033{
1034    VirtualReg *vnic;
1035    DPRINTF(EthernetSM, "txKick: txState=%s (txFifo.size=%d)\n",
1036            TxStateStrings[txState], txFifo.size());
1037
1038    if (txKickTick > curTick()) {
1039        DPRINTF(EthernetSM, "txKick: exiting, can't run till %d\n",
1040                txKickTick);
1041        return;
1042    }
1043
1044  next:
1045    if (txState == txIdle)
1046        goto exit;
1047
1048    assert(!txList.empty());
1049    vnic = &virtualRegs[txList.front()];
1050
1051    switch (txState) {
1052      case txFifoBlock:
1053        assert(Regs::get_TxDone_Busy(vnic->TxDone));
1054        if (!txPacket) {
1055            // Grab a new packet from the fifo.
1056            txPacket = make_shared<EthPacketData>(16384);
1057            txPacketOffset = 0;
1058        }
1059
1060        if (txFifo.avail() - txPacket->length <
1061            Regs::get_TxData_Len(vnic->TxData)) {
1062            DPRINTF(EthernetSM, "transmit fifo full.  Nothing to do.\n");
1063            goto exit;
1064        }
1065
1066        txState = txBeginCopy;
1067        break;
1068
1069      case txBeginCopy:
1070        if (dmaPending() || drainState() != DrainState::Running)
1071            goto exit;
1072
1073        txDmaAddr = pciToDma(Regs::get_TxData_Addr(vnic->TxData));
1074        txDmaLen = Regs::get_TxData_Len(vnic->TxData);
1075        txDmaData = txPacket->data + txPacketOffset;
1076        txState = txCopy;
1077
1078        dmaRead(txDmaAddr, txDmaLen, &txDmaEvent, txDmaData);
1079        break;
1080
1081      case txCopy:
1082        DPRINTF(EthernetSM, "transmit machine still copying\n");
1083        goto exit;
1084
1085      case txCopyDone:
1086        vnic->TxDone = txDmaLen | Regs::TxDone_Complete;
1087        txPacket->length += txDmaLen;
1088        if ((vnic->TxData & Regs::TxData_More)) {
1089            txPacketOffset += txDmaLen;
1090            txState = txIdle;
1091            devIntrPost(Regs::Intr_TxDMA);
1092            break;
1093        }
1094
1095        assert(txPacket->length <= txFifo.avail());
1096        if ((vnic->TxData & Regs::TxData_Checksum)) {
1097            IpPtr ip(txPacket);
1098            if (ip) {
1099                TcpPtr tcp(ip);
1100                if (tcp) {
1101                    tcp->sum(0);
1102                    tcp->sum(cksum(tcp));
1103                    txTcpChecksums++;
1104                }
1105
1106                UdpPtr udp(ip);
1107                if (udp) {
1108                    udp->sum(0);
1109                    udp->sum(cksum(udp));
1110                    txUdpChecksums++;
1111                }
1112
1113                ip->sum(0);
1114                ip->sum(cksum(ip));
1115                txIpChecksums++;
1116            }
1117        }
1118
1119        txFifo.push(txPacket);
1120        if (txFifo.avail() < regs.TxMaxCopy) {
1121            devIntrPost(Regs::Intr_TxFull);
1122            txFull = true;
1123        }
1124        txPacket = 0;
1125        transmit();
1126        txList.pop_front();
1127        txState = txList.empty() ? txIdle : txFifoBlock;
1128        devIntrPost(Regs::Intr_TxDMA);
1129        break;
1130
1131      default:
1132        panic("Invalid txState!");
1133    }
1134
1135    DPRINTF(EthernetSM, "entering next txState=%s\n",
1136            TxStateStrings[txState]);
1137
1138    goto next;
1139
1140  exit:
1141    /**
1142     * @todo do we want to schedule a future kick?
1143     */
1144    DPRINTF(EthernetSM, "tx state machine exited txState=%s\n",
1145            TxStateStrings[txState]);
1146}
1147
1148void
1149Device::transferDone()
1150{
1151    if (txFifo.empty()) {
1152        DPRINTF(Ethernet, "transfer complete: txFifo empty...nothing to do\n");
1153        return;
1154    }
1155
1156    DPRINTF(Ethernet, "transfer complete: data in txFifo...schedule xmit\n");
1157
1158    reschedule(txEvent, clockEdge(Cycles(1)), true);
1159}
1160
1161bool
1162Device::rxFilter(const EthPacketPtr &packet)
1163{
1164    if (!Regs::get_Config_Filter(regs.Config))
1165        return false;
1166
1167    panic("receive filter not implemented\n");
1168    bool drop = true;
1169
1170#if 0
1171    string type;
1172
1173    EthHdr *eth = packet->eth();
1174    if (eth->unicast()) {
1175        // If we're accepting all unicast addresses
1176        if (acceptUnicast)
1177            drop = false;
1178
1179        // If we make a perfect match
1180        if (acceptPerfect && params->eaddr == eth.dst())
1181            drop = false;
1182
1183        if (acceptArp && eth->type() == ETH_TYPE_ARP)
1184            drop = false;
1185
1186    } else if (eth->broadcast()) {
1187        // if we're accepting broadcasts
1188        if (acceptBroadcast)
1189            drop = false;
1190
1191    } else if (eth->multicast()) {
1192        // if we're accepting all multicasts
1193        if (acceptMulticast)
1194            drop = false;
1195
1196    }
1197
1198    if (drop) {
1199        DPRINTF(Ethernet, "rxFilter drop\n");
1200        DDUMP(EthernetData, packet->data, packet->length);
1201    }
1202#endif
1203    return drop;
1204}
1205
1206bool
1207Device::recvPacket(EthPacketPtr packet)
1208{
1209    rxBytes += packet->length;
1210    rxPackets++;
1211
1212    DPRINTF(Ethernet, "Receiving packet from wire, rxFifo Available is %d\n",
1213            rxFifo.avail());
1214
1215    if (!rxEnable) {
1216        DPRINTF(Ethernet, "receive disabled...packet dropped\n");
1217        return true;
1218    }
1219
1220    if (rxFilter(packet)) {
1221        DPRINTF(Ethernet, "packet filtered...dropped\n");
1222        return true;
1223    }
1224
1225    if (rxFifo.size() >= regs.RxFifoHigh)
1226        devIntrPost(Regs::Intr_RxHigh);
1227
1228    if (!rxFifo.push(packet)) {
1229        DPRINTF(Ethernet,
1230                "packet will not fit in receive buffer...packet dropped\n");
1231        return false;
1232    }
1233
1234    // If we were at the last element, back up one ot go to the new
1235    // last element of the list.
1236    if (rxFifoPtr == rxFifo.end())
1237        --rxFifoPtr;
1238
1239    devIntrPost(Regs::Intr_RxPacket);
1240    rxKick();
1241    return true;
1242}
1243
1244void
1245Device::drainResume()
1246{
1247    Drainable::drainResume();
1248
1249    // During drain we could have left the state machines in a waiting state and
1250    // they wouldn't get out until some other event occured to kick them.
1251    // This way they'll get out immediately
1252    txKick();
1253    rxKick();
1254}
1255
1256//=====================================================================
1257//
1258//
1259void
1260Base::serialize(CheckpointOut &cp) const
1261{
1262    // Serialize the PciDevice base class
1263    PciDevice::serialize(cp);
1264
1265    SERIALIZE_SCALAR(rxEnable);
1266    SERIALIZE_SCALAR(txEnable);
1267    SERIALIZE_SCALAR(cpuIntrEnable);
1268
1269    /*
1270     * Keep track of pending interrupt status.
1271     */
1272    SERIALIZE_SCALAR(intrTick);
1273    SERIALIZE_SCALAR(cpuPendingIntr);
1274    Tick intrEventTick = 0;
1275    if (intrEvent)
1276        intrEventTick = intrEvent->when();
1277    SERIALIZE_SCALAR(intrEventTick);
1278}
1279
1280void
1281Base::unserialize(CheckpointIn &cp)
1282{
1283    // Unserialize the PciDevice base class
1284    PciDevice::unserialize(cp);
1285
1286    UNSERIALIZE_SCALAR(rxEnable);
1287    UNSERIALIZE_SCALAR(txEnable);
1288    UNSERIALIZE_SCALAR(cpuIntrEnable);
1289
1290    /*
1291     * Keep track of pending interrupt status.
1292     */
1293    UNSERIALIZE_SCALAR(intrTick);
1294    UNSERIALIZE_SCALAR(cpuPendingIntr);
1295    Tick intrEventTick;
1296    UNSERIALIZE_SCALAR(intrEventTick);
1297    if (intrEventTick) {
1298        intrEvent = new IntrEvent(this, true);
1299        schedule(intrEvent, intrEventTick);
1300    }
1301}
1302
1303void
1304Device::serialize(CheckpointOut &cp) const
1305{
1306    int count;
1307
1308    // Serialize the PciDevice base class
1309    Base::serialize(cp);
1310
1311    if (rxState == rxCopy)
1312        panic("can't serialize with an in flight dma request rxState=%s",
1313              RxStateStrings[rxState]);
1314
1315    if (txState == txCopy)
1316        panic("can't serialize with an in flight dma request txState=%s",
1317              TxStateStrings[txState]);
1318
1319    /*
1320     * Serialize the device registers that could be modified by the OS.
1321     */
1322    SERIALIZE_SCALAR(regs.Config);
1323    SERIALIZE_SCALAR(regs.IntrStatus);
1324    SERIALIZE_SCALAR(regs.IntrMask);
1325    SERIALIZE_SCALAR(regs.RxData);
1326    SERIALIZE_SCALAR(regs.TxData);
1327
1328    /*
1329     * Serialize the virtual nic state
1330     */
1331    int virtualRegsSize = virtualRegs.size();
1332    SERIALIZE_SCALAR(virtualRegsSize);
1333    for (int i = 0; i < virtualRegsSize; ++i) {
1334        const VirtualReg *vnic = &virtualRegs[i];
1335
1336        std::string reg = csprintf("vnic%d", i);
1337        paramOut(cp, reg + ".RxData", vnic->RxData);
1338        paramOut(cp, reg + ".RxDone", vnic->RxDone);
1339        paramOut(cp, reg + ".TxData", vnic->TxData);
1340        paramOut(cp, reg + ".TxDone", vnic->TxDone);
1341
1342        bool rxPacketExists = vnic->rxIndex != rxFifo.end();
1343        paramOut(cp, reg + ".rxPacketExists", rxPacketExists);
1344        if (rxPacketExists) {
1345            int rxPacket = 0;
1346            auto i = rxFifo.begin();
1347            while (i != vnic->rxIndex) {
1348                assert(i != rxFifo.end());
1349                ++i;
1350                ++rxPacket;
1351            }
1352
1353            paramOut(cp, reg + ".rxPacket", rxPacket);
1354            paramOut(cp, reg + ".rxPacketOffset", vnic->rxPacketOffset);
1355            paramOut(cp, reg + ".rxPacketBytes", vnic->rxPacketBytes);
1356        }
1357        paramOut(cp, reg + ".rxDoneData", vnic->rxDoneData);
1358    }
1359
1360    int rxFifoPtr = -1;
1361    if (this->rxFifoPtr != rxFifo.end())
1362        rxFifoPtr = rxFifo.countPacketsBefore(this->rxFifoPtr);
1363    SERIALIZE_SCALAR(rxFifoPtr);
1364
1365    SERIALIZE_SCALAR(rxActive);
1366    SERIALIZE_SCALAR(rxBusyCount);
1367    SERIALIZE_SCALAR(rxDirtyCount);
1368    SERIALIZE_SCALAR(rxMappedCount);
1369
1370    VirtualList::const_iterator i, end;
1371    for (count = 0, i = rxList.begin(), end = rxList.end(); i != end; ++i)
1372        paramOut(cp, csprintf("rxList%d", count++), *i);
1373    int rxListSize = count;
1374    SERIALIZE_SCALAR(rxListSize);
1375
1376    for (count = 0, i = rxBusy.begin(), end = rxBusy.end(); i != end; ++i)
1377        paramOut(cp, csprintf("rxBusy%d", count++), *i);
1378    int rxBusySize = count;
1379    SERIALIZE_SCALAR(rxBusySize);
1380
1381    for (count = 0, i = txList.begin(), end = txList.end(); i != end; ++i)
1382        paramOut(cp, csprintf("txList%d", count++), *i);
1383    int txListSize = count;
1384    SERIALIZE_SCALAR(txListSize);
1385
1386    /*
1387     * Serialize rx state machine
1388     */
1389    int rxState = this->rxState;
1390    SERIALIZE_SCALAR(rxState);
1391    SERIALIZE_SCALAR(rxEmpty);
1392    SERIALIZE_SCALAR(rxLow);
1393    rxFifo.serialize("rxFifo", cp);
1394
1395    /*
1396     * Serialize tx state machine
1397     */
1398    int txState = this->txState;
1399    SERIALIZE_SCALAR(txState);
1400    SERIALIZE_SCALAR(txFull);
1401    txFifo.serialize("txFifo", cp);
1402    bool txPacketExists = txPacket != nullptr;
1403    SERIALIZE_SCALAR(txPacketExists);
1404    if (txPacketExists) {
1405        txPacket->serialize("txPacket", cp);
1406        SERIALIZE_SCALAR(txPacketOffset);
1407        SERIALIZE_SCALAR(txPacketBytes);
1408    }
1409
1410    /*
1411     * If there's a pending transmit, store the time so we can
1412     * reschedule it later
1413     */
1414    Tick transmitTick = txEvent.scheduled() ? txEvent.when() - curTick() : 0;
1415    SERIALIZE_SCALAR(transmitTick);
1416}
1417
1418void
1419Device::unserialize(CheckpointIn &cp)
1420{
1421    // Unserialize the PciDevice base class
1422    Base::unserialize(cp);
1423
1424    /*
1425     * Unserialize the device registers that may have been written by the OS.
1426     */
1427    UNSERIALIZE_SCALAR(regs.Config);
1428    UNSERIALIZE_SCALAR(regs.IntrStatus);
1429    UNSERIALIZE_SCALAR(regs.IntrMask);
1430    UNSERIALIZE_SCALAR(regs.RxData);
1431    UNSERIALIZE_SCALAR(regs.TxData);
1432
1433    UNSERIALIZE_SCALAR(rxActive);
1434    UNSERIALIZE_SCALAR(rxBusyCount);
1435    UNSERIALIZE_SCALAR(rxDirtyCount);
1436    UNSERIALIZE_SCALAR(rxMappedCount);
1437
1438    int rxListSize;
1439    UNSERIALIZE_SCALAR(rxListSize);
1440    rxList.clear();
1441    for (int i = 0; i < rxListSize; ++i) {
1442        int value;
1443        paramIn(cp, csprintf("rxList%d", i), value);
1444        rxList.push_back(value);
1445    }
1446
1447    int rxBusySize;
1448    UNSERIALIZE_SCALAR(rxBusySize);
1449    rxBusy.clear();
1450    for (int i = 0; i < rxBusySize; ++i) {
1451        int value;
1452        paramIn(cp, csprintf("rxBusy%d", i), value);
1453        rxBusy.push_back(value);
1454    }
1455
1456    int txListSize;
1457    UNSERIALIZE_SCALAR(txListSize);
1458    txList.clear();
1459    for (int i = 0; i < txListSize; ++i) {
1460        int value;
1461        paramIn(cp, csprintf("txList%d", i), value);
1462        txList.push_back(value);
1463    }
1464
1465    /*
1466     * Unserialize rx state machine
1467     */
1468    int rxState;
1469    UNSERIALIZE_SCALAR(rxState);
1470    UNSERIALIZE_SCALAR(rxEmpty);
1471    UNSERIALIZE_SCALAR(rxLow);
1472    this->rxState = (RxState) rxState;
1473    rxFifo.unserialize("rxFifo", cp);
1474
1475    int rxFifoPtr;
1476    UNSERIALIZE_SCALAR(rxFifoPtr);
1477    if (rxFifoPtr >= 0) {
1478        this->rxFifoPtr = rxFifo.begin();
1479        for (int i = 0; i < rxFifoPtr; ++i)
1480            ++this->rxFifoPtr;
1481    } else {
1482        this->rxFifoPtr = rxFifo.end();
1483    }
1484
1485    /*
1486     * Unserialize tx state machine
1487     */
1488    int txState;
1489    UNSERIALIZE_SCALAR(txState);
1490    UNSERIALIZE_SCALAR(txFull);
1491    this->txState = (TxState) txState;
1492    txFifo.unserialize("txFifo", cp);
1493    bool txPacketExists;
1494    UNSERIALIZE_SCALAR(txPacketExists);
1495    txPacket = 0;
1496    if (txPacketExists) {
1497        txPacket = make_shared<EthPacketData>(16384);
1498        txPacket->unserialize("txPacket", cp);
1499        UNSERIALIZE_SCALAR(txPacketOffset);
1500        UNSERIALIZE_SCALAR(txPacketBytes);
1501    }
1502
1503    /*
1504     * unserialize the virtual nic registers/state
1505     *
1506     * this must be done after the unserialization of the rxFifo
1507     * because the packet iterators depend on the fifo being populated
1508     */
1509    int virtualRegsSize;
1510    UNSERIALIZE_SCALAR(virtualRegsSize);
1511    virtualRegs.clear();
1512    virtualRegs.resize(virtualRegsSize);
1513    for (int i = 0; i < virtualRegsSize; ++i) {
1514        VirtualReg *vnic = &virtualRegs[i];
1515        std::string reg = csprintf("vnic%d", i);
1516
1517        paramIn(cp, reg + ".RxData", vnic->RxData);
1518        paramIn(cp, reg + ".RxDone", vnic->RxDone);
1519        paramIn(cp, reg + ".TxData", vnic->TxData);
1520        paramIn(cp, reg + ".TxDone", vnic->TxDone);
1521
1522        vnic->rxUnique = rxUnique++;
1523        vnic->txUnique = txUnique++;
1524
1525        bool rxPacketExists;
1526        paramIn(cp, reg + ".rxPacketExists", rxPacketExists);
1527        if (rxPacketExists) {
1528            int rxPacket;
1529            paramIn(cp, reg + ".rxPacket", rxPacket);
1530            vnic->rxIndex = rxFifo.begin();
1531            while (rxPacket--)
1532                ++vnic->rxIndex;
1533
1534            paramIn(cp, reg + ".rxPacketOffset",
1535                    vnic->rxPacketOffset);
1536            paramIn(cp, reg + ".rxPacketBytes", vnic->rxPacketBytes);
1537        } else {
1538            vnic->rxIndex = rxFifo.end();
1539        }
1540        paramIn(cp, reg + ".rxDoneData", vnic->rxDoneData);
1541    }
1542
1543    /*
1544     * If there's a pending transmit, reschedule it now
1545     */
1546    Tick transmitTick;
1547    UNSERIALIZE_SCALAR(transmitTick);
1548    if (transmitTick)
1549        schedule(txEvent, curTick() + transmitTick);
1550
1551    pioPort.sendRangeChange();
1552
1553}
1554
1555} // namespace Sinic
1556
1557Sinic::Device *
1558SinicParams::create()
1559{
1560    return new Sinic::Device(this);
1561}
1562