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