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