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