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