copy_engine.cc revision 13784
19983Sstever@gmail.com/*
29983Sstever@gmail.com * Copyright (c) 2012 ARM Limited
39983Sstever@gmail.com * All rights reserved
49983Sstever@gmail.com *
59983Sstever@gmail.com * The license below extends only to copyright in the software and shall
69983Sstever@gmail.com * not be construed as granting a license to any other intellectual
79983Sstever@gmail.com * property including but not limited to intellectual property relating
89983Sstever@gmail.com * to a hardware implementation of the functionality of the software
99983Sstever@gmail.com * licensed hereunder.  You may use the software subject to the license
109983Sstever@gmail.com * terms below provided that you ensure that this notice is replicated
119983Sstever@gmail.com * unmodified and in its entirety in all distributions of the software,
129983Sstever@gmail.com * modified or unmodified, in source code or in binary form.
139983Sstever@gmail.com *
149983Sstever@gmail.com * Copyright (c) 2008 The Regents of The University of Michigan
159983Sstever@gmail.com * All rights reserved.
169983Sstever@gmail.com *
179983Sstever@gmail.com * Redistribution and use in source and binary forms, with or without
189983Sstever@gmail.com * modification, are permitted provided that the following conditions are
199983Sstever@gmail.com * met: redistributions of source code must retain the above copyright
209983Sstever@gmail.com * notice, this list of conditions and the following disclaimer;
219983Sstever@gmail.com * redistributions in binary form must reproduce the above copyright
229983Sstever@gmail.com * notice, this list of conditions and the following disclaimer in the
239983Sstever@gmail.com * documentation and/or other materials provided with the distribution;
249983Sstever@gmail.com * neither the name of the copyright holders nor the names of its
259983Sstever@gmail.com * contributors may be used to endorse or promote products derived from
269983Sstever@gmail.com * this software without specific prior written permission.
279983Sstever@gmail.com *
289983Sstever@gmail.com * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
299983Sstever@gmail.com * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
309983Sstever@gmail.com * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
319983Sstever@gmail.com * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
329983Sstever@gmail.com * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
339983Sstever@gmail.com * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
349983Sstever@gmail.com * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
359983Sstever@gmail.com * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
369983Sstever@gmail.com * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
379983Sstever@gmail.com * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
389983Sstever@gmail.com * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
399983Sstever@gmail.com *
409983Sstever@gmail.com * Authors: Ali Saidi
419983Sstever@gmail.com */
429983Sstever@gmail.com
439983Sstever@gmail.com/* @file
449983Sstever@gmail.com * Device model for Intel's I/O AT DMA copy engine.
459983Sstever@gmail.com */
469983Sstever@gmail.com
479983Sstever@gmail.com#include "dev/pci/copy_engine.hh"
489983Sstever@gmail.com
499983Sstever@gmail.com#include <algorithm>
509983Sstever@gmail.com
519983Sstever@gmail.com#include "base/cp_annotate.hh"
529983Sstever@gmail.com#include "base/trace.hh"
539983Sstever@gmail.com#include "debug/DMACopyEngine.hh"
549983Sstever@gmail.com#include "debug/Drain.hh"
559983Sstever@gmail.com#include "mem/packet.hh"
569983Sstever@gmail.com#include "mem/packet_access.hh"
579983Sstever@gmail.com#include "params/CopyEngine.hh"
589983Sstever@gmail.com#include "sim/stats.hh"
599983Sstever@gmail.com#include "sim/system.hh"
609983Sstever@gmail.com
619983Sstever@gmail.comusing namespace CopyEngineReg;
629983Sstever@gmail.com
639983Sstever@gmail.comCopyEngine::CopyEngine(const Params *p)
649983Sstever@gmail.com    : PciDevice(p)
659983Sstever@gmail.com{
669983Sstever@gmail.com    // All Reg regs are initialized to 0 by default
679983Sstever@gmail.com    regs.chanCount = p->ChanCnt;
689983Sstever@gmail.com    regs.xferCap = findMsbSet(p->XferCap);
699983Sstever@gmail.com    regs.attnStatus = 0;
709983Sstever@gmail.com
719983Sstever@gmail.com    if (regs.chanCount > 64)
729983Sstever@gmail.com        fatal("CopyEngine interface doesn't support more than 64 DMA engines\n");
739983Sstever@gmail.com
749983Sstever@gmail.com    for (int x = 0; x < regs.chanCount; x++) {
759983Sstever@gmail.com        CopyEngineChannel *ch = new CopyEngineChannel(this, x);
769983Sstever@gmail.com        chan.push_back(ch);
779983Sstever@gmail.com    }
789983Sstever@gmail.com}
799983Sstever@gmail.com
809983Sstever@gmail.com
819983Sstever@gmail.comCopyEngine::CopyEngineChannel::CopyEngineChannel(CopyEngine *_ce, int cid)
829983Sstever@gmail.com    : cePort(_ce, _ce->sys),
839983Sstever@gmail.com      ce(_ce), channelId(cid), busy(false), underReset(false),
849983Sstever@gmail.com      refreshNext(false), latBeforeBegin(ce->params()->latBeforeBegin),
859983Sstever@gmail.com      latAfterCompletion(ce->params()->latAfterCompletion),
869983Sstever@gmail.com      completionDataReg(0), nextState(Idle),
879983Sstever@gmail.com      fetchCompleteEvent([this]{ fetchDescComplete(); }, name()),
889983Sstever@gmail.com      addrCompleteEvent([this]{ fetchAddrComplete(); }, name()),
899983Sstever@gmail.com      readCompleteEvent([this]{ readCopyBytesComplete(); }, name()),
909983Sstever@gmail.com      writeCompleteEvent([this]{ writeCopyBytesComplete(); }, name()),
919983Sstever@gmail.com      statusCompleteEvent([this]{ writeStatusComplete(); }, name())
929983Sstever@gmail.com
939983Sstever@gmail.com{
9410153Sandreas@sandberg.pp.se        cr.status.dma_transfer_status(3);
9510153Sandreas@sandberg.pp.se        cr.descChainAddr = 0;
9610153Sandreas@sandberg.pp.se        cr.completionAddr = 0;
9710153Sandreas@sandberg.pp.se
9810153Sandreas@sandberg.pp.se        curDmaDesc = new DmaDesc;
9910153Sandreas@sandberg.pp.se        memset(curDmaDesc, 0, sizeof(DmaDesc));
10010153Sandreas@sandberg.pp.se        copyBuffer = new uint8_t[ce->params()->XferCap];
10110153Sandreas@sandberg.pp.se}
10210153Sandreas@sandberg.pp.se
10310361SAndreas.Sandberg@ARM.comCopyEngine::~CopyEngine()
1049983Sstever@gmail.com{
1059983Sstever@gmail.com    for (int x = 0; x < chan.size(); x++) {
1069983Sstever@gmail.com        delete chan[x];
1079983Sstever@gmail.com    }
1089983Sstever@gmail.com}
1099983Sstever@gmail.com
1109983Sstever@gmail.comCopyEngine::CopyEngineChannel::~CopyEngineChannel()
1119983Sstever@gmail.com{
11210361SAndreas.Sandberg@ARM.com    delete curDmaDesc;
1139983Sstever@gmail.com    delete [] copyBuffer;
1149983Sstever@gmail.com}
1159983Sstever@gmail.com
1169983Sstever@gmail.comPort &
1179983Sstever@gmail.comCopyEngine::getPort(const std::string &if_name, PortID idx)
1189983Sstever@gmail.com{
1199983Sstever@gmail.com    if (if_name != "dma") {
1209983Sstever@gmail.com        // pass it along to our super class
1219983Sstever@gmail.com        return PciDevice::getPort(if_name, idx);
1229983Sstever@gmail.com    } else {
1239983Sstever@gmail.com        if (idx >= static_cast<int>(chan.size())) {
1249983Sstever@gmail.com            panic("CopyEngine::getPort: unknown index %d\n", idx);
1259983Sstever@gmail.com        }
1269983Sstever@gmail.com
1279983Sstever@gmail.com        return chan[idx]->getPort();
1289983Sstever@gmail.com    }
1299983Sstever@gmail.com}
1309983Sstever@gmail.com
1319983Sstever@gmail.com
1329983Sstever@gmail.comPort &
1339983Sstever@gmail.comCopyEngine::CopyEngineChannel::getPort()
1349983Sstever@gmail.com{
1359983Sstever@gmail.com    return cePort;
1369983Sstever@gmail.com}
1379983Sstever@gmail.com
1389983Sstever@gmail.comvoid
1399983Sstever@gmail.comCopyEngine::CopyEngineChannel::recvCommand()
1409983Sstever@gmail.com{
1419983Sstever@gmail.com    if (cr.command.start_dma()) {
1429983Sstever@gmail.com        assert(!busy);
1439983Sstever@gmail.com        cr.status.dma_transfer_status(0);
1449983Sstever@gmail.com        nextState = DescriptorFetch;
1459983Sstever@gmail.com        fetchAddress = cr.descChainAddr;
1469983Sstever@gmail.com        if (ce->drainState() == DrainState::Running)
1479983Sstever@gmail.com            fetchDescriptor(cr.descChainAddr);
1489983Sstever@gmail.com    } else if (cr.command.append_dma()) {
1499983Sstever@gmail.com        if (!busy) {
1509983Sstever@gmail.com            nextState = AddressFetch;
1519983Sstever@gmail.com            if (ce->drainState() == DrainState::Running)
1529983Sstever@gmail.com                fetchNextAddr(lastDescriptorAddr);
1539983Sstever@gmail.com        } else
1549983Sstever@gmail.com            refreshNext = true;
1559983Sstever@gmail.com    } else if (cr.command.reset_dma()) {
1569983Sstever@gmail.com        if (busy)
1579983Sstever@gmail.com            underReset = true;
1589983Sstever@gmail.com        else {
1599983Sstever@gmail.com            cr.status.dma_transfer_status(3);
1609983Sstever@gmail.com            nextState = Idle;
1619983Sstever@gmail.com        }
1629983Sstever@gmail.com    } else if (cr.command.resume_dma() || cr.command.abort_dma() ||
1639983Sstever@gmail.com            cr.command.suspend_dma())
1649983Sstever@gmail.com        panic("Resume, Abort, and Suspend are not supported\n");
1659983Sstever@gmail.com    cr.command(0);
1669983Sstever@gmail.com}
1679983Sstever@gmail.com
1689983Sstever@gmail.comTick
1699983Sstever@gmail.comCopyEngine::read(PacketPtr pkt)
1709983Sstever@gmail.com{
1719983Sstever@gmail.com    int bar;
1729983Sstever@gmail.com    Addr daddr;
1739983Sstever@gmail.com
1749983Sstever@gmail.com    if (!getBAR(pkt->getAddr(), bar, daddr))
1759983Sstever@gmail.com        panic("Invalid PCI memory access to unmapped memory.\n");
1769983Sstever@gmail.com
1779983Sstever@gmail.com    // Only Memory register BAR is allowed
1789983Sstever@gmail.com    assert(bar == 0);
1799983Sstever@gmail.com
1809983Sstever@gmail.com    int size = pkt->getSize();
1819983Sstever@gmail.com    if (size != sizeof(uint64_t) && size != sizeof(uint32_t) &&
1829983Sstever@gmail.com        size != sizeof(uint16_t) && size != sizeof(uint8_t)) {
1839983Sstever@gmail.com        panic("Unknown size for MMIO access: %d\n", pkt->getSize());
1849983Sstever@gmail.com    }
1859983Sstever@gmail.com
1869983Sstever@gmail.com    DPRINTF(DMACopyEngine, "Read device register %#X size: %d\n", daddr, size);
1879983Sstever@gmail.com
1889983Sstever@gmail.com    ///
1899983Sstever@gmail.com    /// Handle read of register here
1909983Sstever@gmail.com    ///
1919983Sstever@gmail.com
1929983Sstever@gmail.com    if (daddr < 0x80) {
1939983Sstever@gmail.com        switch (daddr) {
1949983Sstever@gmail.com          case GEN_CHANCOUNT:
1959983Sstever@gmail.com            assert(size == sizeof(regs.chanCount));
1969983Sstever@gmail.com            pkt->setLE<uint8_t>(regs.chanCount);
1979983Sstever@gmail.com            break;
1989983Sstever@gmail.com          case GEN_XFERCAP:
1999983Sstever@gmail.com            assert(size == sizeof(regs.xferCap));
2009983Sstever@gmail.com            pkt->setLE<uint8_t>(regs.xferCap);
2019983Sstever@gmail.com            break;
2029983Sstever@gmail.com          case GEN_INTRCTRL:
2039983Sstever@gmail.com            assert(size == sizeof(uint8_t));
2049983Sstever@gmail.com            pkt->setLE<uint8_t>(regs.intrctrl());
2059983Sstever@gmail.com            regs.intrctrl.master_int_enable(0);
2069983Sstever@gmail.com            break;
2079983Sstever@gmail.com          case GEN_ATTNSTATUS:
2089983Sstever@gmail.com            assert(size == sizeof(regs.attnStatus));
2099983Sstever@gmail.com            pkt->setLE<uint32_t>(regs.attnStatus);
2109983Sstever@gmail.com            regs.attnStatus = 0;
2119983Sstever@gmail.com            break;
2129983Sstever@gmail.com          default:
2139983Sstever@gmail.com            panic("Read request to unknown register number: %#x\n", daddr);
2149983Sstever@gmail.com        }
2159983Sstever@gmail.com        pkt->makeAtomicResponse();
2169983Sstever@gmail.com        return pioDelay;
2179983Sstever@gmail.com    }
2189983Sstever@gmail.com
2199983Sstever@gmail.com
2209983Sstever@gmail.com    // Find which channel we're accessing
2219983Sstever@gmail.com    int chanid = 0;
22211290Sgabor.dozsa@arm.com    daddr -= 0x80;
2239983Sstever@gmail.com    while (daddr >= 0x80) {
2249983Sstever@gmail.com        chanid++;
2259983Sstever@gmail.com        daddr -= 0x80;
2269983Sstever@gmail.com    }
2279983Sstever@gmail.com
2289983Sstever@gmail.com    if (chanid >= regs.chanCount)
2299983Sstever@gmail.com        panic("Access to channel %d (device only configured for %d channels)",
2309983Sstever@gmail.com                chanid, regs.chanCount);
2319983Sstever@gmail.com
2329983Sstever@gmail.com    ///
2339983Sstever@gmail.com    /// Channel registers are handled here
2349983Sstever@gmail.com    ///
2359983Sstever@gmail.com    chan[chanid]->channelRead(pkt, daddr, size);
2369983Sstever@gmail.com
2379983Sstever@gmail.com    pkt->makeAtomicResponse();
2389983Sstever@gmail.com    return pioDelay;
2399983Sstever@gmail.com}
240
241void
242CopyEngine::CopyEngineChannel::channelRead(Packet *pkt, Addr daddr, int size)
243{
244    switch (daddr) {
245      case CHAN_CONTROL:
246        assert(size == sizeof(uint16_t));
247        pkt->setLE<uint16_t>(cr.ctrl());
248        cr.ctrl.in_use(1);
249        break;
250      case CHAN_STATUS:
251        assert(size == sizeof(uint64_t));
252        pkt->setLE<uint64_t>(cr.status() | (busy ? 0 : 1));
253        break;
254      case CHAN_CHAINADDR:
255        assert(size == sizeof(uint64_t) || size == sizeof(uint32_t));
256        if (size == sizeof(uint64_t))
257            pkt->setLE<uint64_t>(cr.descChainAddr);
258        else
259            pkt->setLE<uint32_t>(bits(cr.descChainAddr,0,31));
260        break;
261      case CHAN_CHAINADDR_HIGH:
262        assert(size == sizeof(uint32_t));
263        pkt->setLE<uint32_t>(bits(cr.descChainAddr,32,63));
264        break;
265      case CHAN_COMMAND:
266        assert(size == sizeof(uint8_t));
267        pkt->setLE<uint32_t>(cr.command());
268        break;
269      case CHAN_CMPLNADDR:
270        assert(size == sizeof(uint64_t) || size == sizeof(uint32_t));
271        if (size == sizeof(uint64_t))
272            pkt->setLE<uint64_t>(cr.completionAddr);
273        else
274            pkt->setLE<uint32_t>(bits(cr.completionAddr,0,31));
275        break;
276      case CHAN_CMPLNADDR_HIGH:
277        assert(size == sizeof(uint32_t));
278        pkt->setLE<uint32_t>(bits(cr.completionAddr,32,63));
279        break;
280      case CHAN_ERROR:
281        assert(size == sizeof(uint32_t));
282        pkt->setLE<uint32_t>(cr.error());
283        break;
284      default:
285        panic("Read request to unknown channel register number: (%d)%#x\n",
286                channelId, daddr);
287    }
288}
289
290
291Tick
292CopyEngine::write(PacketPtr pkt)
293{
294    int bar;
295    Addr daddr;
296
297
298    if (!getBAR(pkt->getAddr(), bar, daddr))
299        panic("Invalid PCI memory access to unmapped memory.\n");
300
301    // Only Memory register BAR is allowed
302    assert(bar == 0);
303
304    int size = pkt->getSize();
305
306    ///
307    /// Handle write of register here
308    ///
309
310    if (size == sizeof(uint64_t)) {
311        uint64_t val M5_VAR_USED = pkt->getLE<uint64_t>();
312        DPRINTF(DMACopyEngine, "Wrote device register %#X value %#X\n",
313                daddr, val);
314    } else if (size == sizeof(uint32_t)) {
315        uint32_t val M5_VAR_USED = pkt->getLE<uint32_t>();
316        DPRINTF(DMACopyEngine, "Wrote device register %#X value %#X\n",
317                daddr, val);
318    } else if (size == sizeof(uint16_t)) {
319        uint16_t val M5_VAR_USED = pkt->getLE<uint16_t>();
320        DPRINTF(DMACopyEngine, "Wrote device register %#X value %#X\n",
321                daddr, val);
322    } else if (size == sizeof(uint8_t)) {
323        uint8_t val M5_VAR_USED = pkt->getLE<uint8_t>();
324        DPRINTF(DMACopyEngine, "Wrote device register %#X value %#X\n",
325                daddr, val);
326    } else {
327        panic("Unknown size for MMIO access: %d\n", size);
328    }
329
330    if (daddr < 0x80) {
331        switch (daddr) {
332          case GEN_CHANCOUNT:
333          case GEN_XFERCAP:
334          case GEN_ATTNSTATUS:
335            DPRINTF(DMACopyEngine, "Warning, ignorning write to register %x\n",
336                    daddr);
337            break;
338          case GEN_INTRCTRL:
339            regs.intrctrl.master_int_enable(bits(pkt->getLE<uint8_t>(), 0, 1));
340            break;
341          default:
342            panic("Read request to unknown register number: %#x\n", daddr);
343        }
344        pkt->makeAtomicResponse();
345        return pioDelay;
346    }
347
348    // Find which channel we're accessing
349    int chanid = 0;
350    daddr -= 0x80;
351    while (daddr >= 0x80) {
352        chanid++;
353        daddr -= 0x80;
354    }
355
356    if (chanid >= regs.chanCount)
357        panic("Access to channel %d (device only configured for %d channels)",
358                chanid, regs.chanCount);
359
360    ///
361    /// Channel registers are handled here
362    ///
363    chan[chanid]->channelWrite(pkt, daddr, size);
364
365    pkt->makeAtomicResponse();
366    return pioDelay;
367}
368
369void
370CopyEngine::CopyEngineChannel::channelWrite(Packet *pkt, Addr daddr, int size)
371{
372    switch (daddr) {
373      case CHAN_CONTROL:
374        assert(size == sizeof(uint16_t));
375        int old_int_disable;
376        old_int_disable = cr.ctrl.interrupt_disable();
377        cr.ctrl(pkt->getLE<uint16_t>());
378        if (cr.ctrl.interrupt_disable())
379            cr.ctrl.interrupt_disable(0);
380        else
381            cr.ctrl.interrupt_disable(old_int_disable);
382        break;
383      case CHAN_STATUS:
384        assert(size == sizeof(uint64_t));
385        DPRINTF(DMACopyEngine, "Warning, ignorning write to register %x\n",
386                    daddr);
387        break;
388      case CHAN_CHAINADDR:
389        assert(size == sizeof(uint64_t) || size == sizeof(uint32_t));
390        if (size == sizeof(uint64_t))
391            cr.descChainAddr = pkt->getLE<uint64_t>();
392        else
393            cr.descChainAddr =  (uint64_t)pkt->getLE<uint32_t>() |
394                (cr.descChainAddr & ~mask(32));
395        DPRINTF(DMACopyEngine, "Chain Address %x\n", cr.descChainAddr);
396        break;
397      case CHAN_CHAINADDR_HIGH:
398        assert(size == sizeof(uint32_t));
399        cr.descChainAddr =  ((uint64_t)pkt->getLE<uint32_t>() << 32) |
400            (cr.descChainAddr & mask(32));
401        DPRINTF(DMACopyEngine, "Chain Address %x\n", cr.descChainAddr);
402        break;
403      case CHAN_COMMAND:
404        assert(size == sizeof(uint8_t));
405        cr.command(pkt->getLE<uint8_t>());
406        recvCommand();
407        break;
408      case CHAN_CMPLNADDR:
409        assert(size == sizeof(uint64_t) || size == sizeof(uint32_t));
410        if (size == sizeof(uint64_t))
411            cr.completionAddr = pkt->getLE<uint64_t>();
412        else
413            cr.completionAddr =  pkt->getLE<uint32_t>() |
414                (cr.completionAddr & ~mask(32));
415        break;
416      case CHAN_CMPLNADDR_HIGH:
417        assert(size == sizeof(uint32_t));
418        cr.completionAddr =  ((uint64_t)pkt->getLE<uint32_t>() <<32) |
419            (cr.completionAddr & mask(32));
420        break;
421      case CHAN_ERROR:
422        assert(size == sizeof(uint32_t));
423        cr.error(~pkt->getLE<uint32_t>() & cr.error());
424        break;
425      default:
426        panic("Read request to unknown channel register number: (%d)%#x\n",
427                channelId, daddr);
428    }
429}
430
431void
432CopyEngine::regStats()
433{
434    PciDevice::regStats();
435
436    using namespace Stats;
437    bytesCopied
438        .init(regs.chanCount)
439        .name(name() + ".bytes_copied")
440        .desc("Number of bytes copied by each engine")
441        .flags(total)
442        ;
443    copiesProcessed
444        .init(regs.chanCount)
445        .name(name() + ".copies_processed")
446        .desc("Number of copies processed by each engine")
447        .flags(total)
448        ;
449}
450
451void
452CopyEngine::CopyEngineChannel::fetchDescriptor(Addr address)
453{
454    anDq();
455    anBegin("FetchDescriptor");
456    DPRINTF(DMACopyEngine, "Reading descriptor from at memory location %#x(%#x)\n",
457           address, ce->pciToDma(address));
458    assert(address);
459    busy = true;
460
461    DPRINTF(DMACopyEngine, "dmaAction: %#x, %d bytes, to addr %#x\n",
462            ce->pciToDma(address), sizeof(DmaDesc), curDmaDesc);
463
464    cePort.dmaAction(MemCmd::ReadReq, ce->pciToDma(address),
465                     sizeof(DmaDesc), &fetchCompleteEvent,
466                     (uint8_t*)curDmaDesc, latBeforeBegin);
467    lastDescriptorAddr = address;
468}
469
470void
471CopyEngine::CopyEngineChannel::fetchDescComplete()
472{
473    DPRINTF(DMACopyEngine, "Read of descriptor complete\n");
474
475    if ((curDmaDesc->command & DESC_CTRL_NULL)) {
476        DPRINTF(DMACopyEngine, "Got NULL descriptor, skipping\n");
477        assert(!(curDmaDesc->command & DESC_CTRL_CP_STS));
478        if (curDmaDesc->command & DESC_CTRL_CP_STS) {
479            panic("Shouldn't be able to get here\n");
480            nextState = CompletionWrite;
481            if (inDrain()) return;
482            writeCompletionStatus();
483        } else {
484            anBegin("Idle");
485            anWait();
486            busy = false;
487            nextState = Idle;
488            inDrain();
489        }
490        return;
491    }
492
493    if (curDmaDesc->command & ~DESC_CTRL_CP_STS)
494        panic("Descriptor has flag other that completion status set\n");
495
496    nextState = DMARead;
497    if (inDrain()) return;
498    readCopyBytes();
499}
500
501void
502CopyEngine::CopyEngineChannel::readCopyBytes()
503{
504    anBegin("ReadCopyBytes");
505    DPRINTF(DMACopyEngine, "Reading %d bytes from buffer to memory location %#x(%#x)\n",
506           curDmaDesc->len, curDmaDesc->dest,
507           ce->pciToDma(curDmaDesc->src));
508    cePort.dmaAction(MemCmd::ReadReq, ce->pciToDma(curDmaDesc->src),
509                     curDmaDesc->len, &readCompleteEvent, copyBuffer, 0);
510}
511
512void
513CopyEngine::CopyEngineChannel::readCopyBytesComplete()
514{
515    DPRINTF(DMACopyEngine, "Read of bytes to copy complete\n");
516
517    nextState = DMAWrite;
518    if (inDrain()) return;
519    writeCopyBytes();
520}
521
522void
523CopyEngine::CopyEngineChannel::writeCopyBytes()
524{
525    anBegin("WriteCopyBytes");
526    DPRINTF(DMACopyEngine, "Writing %d bytes from buffer to memory location %#x(%#x)\n",
527           curDmaDesc->len, curDmaDesc->dest,
528           ce->pciToDma(curDmaDesc->dest));
529
530    cePort.dmaAction(MemCmd::WriteReq, ce->pciToDma(curDmaDesc->dest),
531                     curDmaDesc->len, &writeCompleteEvent, copyBuffer, 0);
532
533    ce->bytesCopied[channelId] += curDmaDesc->len;
534    ce->copiesProcessed[channelId]++;
535}
536
537void
538CopyEngine::CopyEngineChannel::writeCopyBytesComplete()
539{
540    DPRINTF(DMACopyEngine, "Write of bytes to copy complete user1: %#x\n",
541            curDmaDesc->user1);
542
543    cr.status.compl_desc_addr(lastDescriptorAddr >> 6);
544    completionDataReg = cr.status() | 1;
545
546    anQ("DMAUsedDescQ", channelId, 1);
547    anQ("AppRecvQ", curDmaDesc->user1, curDmaDesc->len);
548    if (curDmaDesc->command & DESC_CTRL_CP_STS) {
549        nextState = CompletionWrite;
550        if (inDrain()) return;
551        writeCompletionStatus();
552        return;
553    }
554
555    continueProcessing();
556}
557
558void
559CopyEngine::CopyEngineChannel::continueProcessing()
560{
561    busy = false;
562
563    if (underReset) {
564        anBegin("Reset");
565        anWait();
566        underReset = false;
567        refreshNext = false;
568        busy = false;
569        nextState = Idle;
570        return;
571    }
572
573    if (curDmaDesc->next) {
574        nextState = DescriptorFetch;
575        fetchAddress = curDmaDesc->next;
576        if (inDrain()) return;
577        fetchDescriptor(curDmaDesc->next);
578    } else if (refreshNext) {
579        nextState = AddressFetch;
580        refreshNext = false;
581        if (inDrain()) return;
582        fetchNextAddr(lastDescriptorAddr);
583    } else {
584        inDrain();
585        nextState = Idle;
586        anWait();
587        anBegin("Idle");
588    }
589}
590
591void
592CopyEngine::CopyEngineChannel::writeCompletionStatus()
593{
594    anBegin("WriteCompletionStatus");
595    DPRINTF(DMACopyEngine, "Writing completion status %#x to address %#x(%#x)\n",
596            completionDataReg, cr.completionAddr,
597            ce->pciToDma(cr.completionAddr));
598
599    cePort.dmaAction(MemCmd::WriteReq,
600                     ce->pciToDma(cr.completionAddr),
601                     sizeof(completionDataReg), &statusCompleteEvent,
602                     (uint8_t*)&completionDataReg, latAfterCompletion);
603}
604
605void
606CopyEngine::CopyEngineChannel::writeStatusComplete()
607{
608    DPRINTF(DMACopyEngine, "Writing completion status complete\n");
609    continueProcessing();
610}
611
612void
613CopyEngine::CopyEngineChannel::fetchNextAddr(Addr address)
614{
615    anBegin("FetchNextAddr");
616    DPRINTF(DMACopyEngine, "Fetching next address...\n");
617    busy = true;
618    cePort.dmaAction(MemCmd::ReadReq,
619                     ce->pciToDma(address + offsetof(DmaDesc, next)),
620                     sizeof(Addr), &addrCompleteEvent,
621                     (uint8_t*)curDmaDesc + offsetof(DmaDesc, next), 0);
622}
623
624void
625CopyEngine::CopyEngineChannel::fetchAddrComplete()
626{
627    DPRINTF(DMACopyEngine, "Fetching next address complete: %#x\n",
628            curDmaDesc->next);
629    if (!curDmaDesc->next) {
630        DPRINTF(DMACopyEngine, "Got NULL descriptor, nothing more to do\n");
631        busy = false;
632        nextState = Idle;
633        anWait();
634        anBegin("Idle");
635        inDrain();
636        return;
637    }
638    nextState = DescriptorFetch;
639    fetchAddress = curDmaDesc->next;
640    if (inDrain()) return;
641    fetchDescriptor(curDmaDesc->next);
642}
643
644bool
645CopyEngine::CopyEngineChannel::inDrain()
646{
647    if (drainState() == DrainState::Draining) {
648        DPRINTF(Drain, "CopyEngine done draining, processing drain event\n");
649        signalDrainDone();
650    }
651
652    return ce->drainState() != DrainState::Running;
653}
654
655DrainState
656CopyEngine::CopyEngineChannel::drain()
657{
658    if (nextState == Idle || ce->drainState() != DrainState::Running) {
659        return DrainState::Drained;
660    } else {
661        DPRINTF(Drain, "CopyEngineChannel not drained\n");
662        return DrainState::Draining;
663    }
664}
665
666void
667CopyEngine::serialize(CheckpointOut &cp) const
668{
669    PciDevice::serialize(cp);
670    regs.serialize(cp);
671    for (int x =0; x < chan.size(); x++)
672        chan[x]->serializeSection(cp, csprintf("channel%d", x));
673}
674
675void
676CopyEngine::unserialize(CheckpointIn &cp)
677{
678    PciDevice::unserialize(cp);
679    regs.unserialize(cp);
680    for (int x = 0; x < chan.size(); x++)
681        chan[x]->unserializeSection(cp, csprintf("channel%d", x));
682}
683
684void
685CopyEngine::CopyEngineChannel::serialize(CheckpointOut &cp) const
686{
687    SERIALIZE_SCALAR(channelId);
688    SERIALIZE_SCALAR(busy);
689    SERIALIZE_SCALAR(underReset);
690    SERIALIZE_SCALAR(refreshNext);
691    SERIALIZE_SCALAR(lastDescriptorAddr);
692    SERIALIZE_SCALAR(completionDataReg);
693    SERIALIZE_SCALAR(fetchAddress);
694    int nextState = this->nextState;
695    SERIALIZE_SCALAR(nextState);
696    arrayParamOut(cp, "curDmaDesc", (uint8_t*)curDmaDesc, sizeof(DmaDesc));
697    SERIALIZE_ARRAY(copyBuffer, ce->params()->XferCap);
698    cr.serialize(cp);
699
700}
701void
702CopyEngine::CopyEngineChannel::unserialize(CheckpointIn &cp)
703{
704    UNSERIALIZE_SCALAR(channelId);
705    UNSERIALIZE_SCALAR(busy);
706    UNSERIALIZE_SCALAR(underReset);
707    UNSERIALIZE_SCALAR(refreshNext);
708    UNSERIALIZE_SCALAR(lastDescriptorAddr);
709    UNSERIALIZE_SCALAR(completionDataReg);
710    UNSERIALIZE_SCALAR(fetchAddress);
711    int nextState;
712    UNSERIALIZE_SCALAR(nextState);
713    this->nextState = (ChannelState)nextState;
714    arrayParamIn(cp, "curDmaDesc", (uint8_t*)curDmaDesc, sizeof(DmaDesc));
715    UNSERIALIZE_ARRAY(copyBuffer, ce->params()->XferCap);
716    cr.unserialize(cp);
717
718}
719
720void
721CopyEngine::CopyEngineChannel::restartStateMachine()
722{
723    switch(nextState) {
724      case AddressFetch:
725        fetchNextAddr(lastDescriptorAddr);
726        break;
727      case DescriptorFetch:
728        fetchDescriptor(fetchAddress);
729        break;
730      case DMARead:
731        readCopyBytes();
732        break;
733      case DMAWrite:
734        writeCopyBytes();
735        break;
736      case CompletionWrite:
737        writeCompletionStatus();
738        break;
739      case Idle:
740        break;
741      default:
742        panic("Unknown state for CopyEngineChannel\n");
743    }
744}
745
746void
747CopyEngine::CopyEngineChannel::drainResume()
748{
749    DPRINTF(DMACopyEngine, "Restarting state machine at state %d\n", nextState);
750    restartStateMachine();
751}
752
753CopyEngine *
754CopyEngineParams::create()
755{
756    return new CopyEngine(this);
757}
758