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