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