| /* | |
| * Copyright (c) 2004-2005 The Regents of The University of Michigan | |
| * All rights reserved. | |
| * | |
| * Redistribution and use in source and binary forms, with or without | |
| * modification, are permitted provided that the following conditions are | |
| * met: redistributions of source code must retain the above copyright | |
| * notice, this list of conditions and the following disclaimer; | |
| * redistributions in binary form must reproduce the above copyright | |
| * notice, this list of conditions and the following disclaimer in the | |
| * documentation and/or other materials provided with the distribution; | |
| * neither the name of the copyright holders nor the names of its | |
| * contributors may be used to endorse or promote products derived from | |
| * this software without specific prior written permission. | |
| * | |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| * | |
| * Authors: Ali Saidi | |
| * Andrew Schultz | |
| */ | |
| /** @file | |
| * Tsunami PChip (pci) | |
| */ | |
| #include "dev/alpha/tsunami_pchip.hh" | |
| #include <deque> | |
| #include <string> | |
| #include <vector> | |
| #include "base/trace.hh" | |
| #include "config/the_isa.hh" | |
| #include "debug/Tsunami.hh" | |
| #include "dev/alpha/tsunami.hh" | |
| #include "dev/alpha/tsunami_cchip.hh" | |
| #include "dev/alpha/tsunamireg.h" | |
| #include "dev/pci/device.hh" | |
| #include "mem/packet.hh" | |
| #include "mem/packet_access.hh" | |
| #include "sim/system.hh" | |
| using namespace std; | |
| //Should this be AlphaISA? | |
| using namespace TheISA; | |
| TsunamiPChip::TsunamiPChip(const Params *p) | |
| : GenericPciHost(p), | |
| pioRange(RangeSize(p->pio_addr, 0x1000)), | |
| pioDelay(p->pio_latency) | |
| { | |
| for (int i = 0; i < 4; i++) { | |
| wsba[i] = 0; | |
| wsm[i] = 0; | |
| tba[i] = 0; | |
| } | |
| // initialize pchip control register | |
| pctl = (ULL(0x1) << 20) | (ULL(0x1) << 32) | (ULL(0x2) << 36); | |
| //Set back pointer in tsunami | |
| p->tsunami->pchip = this; | |
| } | |
| Tick | |
| TsunamiPChip::read(PacketPtr pkt) | |
| { | |
| // We only need to handle our own configuration registers, pass | |
| // unknown addresses to the generic code. | |
| if (!pioRange.contains(pkt->getAddr())) | |
| return GenericPciHost::read(pkt); | |
| Addr daddr = (pkt->getAddr() - pioRange.start()) >> 6;; | |
| assert(pkt->getSize() == sizeof(uint64_t)); | |
| DPRINTF(Tsunami, "read va=%#x size=%d\n", pkt->getAddr(), pkt->getSize()); | |
| switch(daddr) { | |
| case TSDEV_PC_WSBA0: | |
| pkt->setLE(wsba[0]); | |
| break; | |
| case TSDEV_PC_WSBA1: | |
| pkt->setLE(wsba[1]); | |
| break; | |
| case TSDEV_PC_WSBA2: | |
| pkt->setLE(wsba[2]); | |
| break; | |
| case TSDEV_PC_WSBA3: | |
| pkt->setLE(wsba[3]); | |
| break; | |
| case TSDEV_PC_WSM0: | |
| pkt->setLE(wsm[0]); | |
| break; | |
| case TSDEV_PC_WSM1: | |
| pkt->setLE(wsm[1]); | |
| break; | |
| case TSDEV_PC_WSM2: | |
| pkt->setLE(wsm[2]); | |
| break; | |
| case TSDEV_PC_WSM3: | |
| pkt->setLE(wsm[3]); | |
| break; | |
| case TSDEV_PC_TBA0: | |
| pkt->setLE(tba[0]); | |
| break; | |
| case TSDEV_PC_TBA1: | |
| pkt->setLE(tba[1]); | |
| break; | |
| case TSDEV_PC_TBA2: | |
| pkt->setLE(tba[2]); | |
| break; | |
| case TSDEV_PC_TBA3: | |
| pkt->setLE(tba[3]); | |
| break; | |
| case TSDEV_PC_PCTL: | |
| pkt->setLE(pctl); | |
| break; | |
| case TSDEV_PC_PLAT: | |
| panic("PC_PLAT not implemented\n"); | |
| case TSDEV_PC_RES: | |
| panic("PC_RES not implemented\n"); | |
| case TSDEV_PC_PERROR: | |
| pkt->setLE((uint64_t)0x00); | |
| break; | |
| case TSDEV_PC_PERRMASK: | |
| pkt->setLE((uint64_t)0x00); | |
| break; | |
| case TSDEV_PC_PERRSET: | |
| panic("PC_PERRSET not implemented\n"); | |
| case TSDEV_PC_TLBIV: | |
| panic("PC_TLBIV not implemented\n"); | |
| case TSDEV_PC_TLBIA: | |
| pkt->setLE((uint64_t)0x00); // shouldn't be readable, but linux | |
| break; | |
| case TSDEV_PC_PMONCTL: | |
| panic("PC_PMONCTL not implemented\n"); | |
| case TSDEV_PC_PMONCNT: | |
| panic("PC_PMONCTN not implemented\n"); | |
| default: | |
| panic("Default in PChip Read reached reading 0x%x\n", daddr); | |
| } | |
| pkt->makeAtomicResponse(); | |
| return pioDelay; | |
| } | |
| Tick | |
| TsunamiPChip::write(PacketPtr pkt) | |
| { | |
| // We only need to handle our own configuration registers, pass | |
| // unknown addresses to the generic code. | |
| if (!pioRange.contains(pkt->getAddr())) | |
| return GenericPciHost::write(pkt); | |
| Addr daddr = (pkt->getAddr() - pioRange.start()) >> 6; | |
| assert(pkt->getSize() == sizeof(uint64_t)); | |
| DPRINTF(Tsunami, "write - va=%#x size=%d \n", pkt->getAddr(), pkt->getSize()); | |
| switch(daddr) { | |
| case TSDEV_PC_WSBA0: | |
| wsba[0] = pkt->getLE<uint64_t>(); | |
| break; | |
| case TSDEV_PC_WSBA1: | |
| wsba[1] = pkt->getLE<uint64_t>(); | |
| break; | |
| case TSDEV_PC_WSBA2: | |
| wsba[2] = pkt->getLE<uint64_t>(); | |
| break; | |
| case TSDEV_PC_WSBA3: | |
| wsba[3] = pkt->getLE<uint64_t>(); | |
| break; | |
| case TSDEV_PC_WSM0: | |
| wsm[0] = pkt->getLE<uint64_t>(); | |
| break; | |
| case TSDEV_PC_WSM1: | |
| wsm[1] = pkt->getLE<uint64_t>(); | |
| break; | |
| case TSDEV_PC_WSM2: | |
| wsm[2] = pkt->getLE<uint64_t>(); | |
| break; | |
| case TSDEV_PC_WSM3: | |
| wsm[3] = pkt->getLE<uint64_t>(); | |
| break; | |
| case TSDEV_PC_TBA0: | |
| tba[0] = pkt->getLE<uint64_t>(); | |
| break; | |
| case TSDEV_PC_TBA1: | |
| tba[1] = pkt->getLE<uint64_t>(); | |
| break; | |
| case TSDEV_PC_TBA2: | |
| tba[2] = pkt->getLE<uint64_t>(); | |
| break; | |
| case TSDEV_PC_TBA3: | |
| tba[3] = pkt->getLE<uint64_t>(); | |
| break; | |
| case TSDEV_PC_PCTL: | |
| pctl = pkt->getLE<uint64_t>(); | |
| break; | |
| case TSDEV_PC_PLAT: | |
| panic("PC_PLAT not implemented\n"); | |
| case TSDEV_PC_RES: | |
| panic("PC_RES not implemented\n"); | |
| case TSDEV_PC_PERROR: | |
| break; | |
| case TSDEV_PC_PERRMASK: | |
| panic("PC_PERRMASK not implemented\n"); | |
| case TSDEV_PC_PERRSET: | |
| panic("PC_PERRSET not implemented\n"); | |
| case TSDEV_PC_TLBIV: | |
| panic("PC_TLBIV not implemented\n"); | |
| case TSDEV_PC_TLBIA: | |
| break; // value ignored, supposted to invalidate SG TLB | |
| case TSDEV_PC_PMONCTL: | |
| panic("PC_PMONCTL not implemented\n"); | |
| case TSDEV_PC_PMONCNT: | |
| panic("PC_PMONCTN not implemented\n"); | |
| default: | |
| panic("Default in PChip write reached reading 0x%x\n", daddr); | |
| } // uint64_t | |
| pkt->makeAtomicResponse(); | |
| return pioDelay; | |
| } | |
| AddrRangeList | |
| TsunamiPChip::getAddrRanges() const | |
| { | |
| return AddrRangeList({ | |
| RangeSize(confBase, confSize), | |
| pioRange | |
| }); | |
| } | |
| #define DMA_ADDR_MASK ULL(0x3ffffffff) | |
| Addr | |
| TsunamiPChip::dmaAddr(const PciBusAddr &dev, Addr busAddr) const | |
| { | |
| // compare the address to the window base registers | |
| uint64_t tbaMask = 0; | |
| uint64_t baMask = 0; | |
| uint64_t windowMask = 0; | |
| uint64_t windowBase = 0; | |
| uint64_t pteEntry = 0; | |
| Addr pteAddr; | |
| Addr dmaAddr; | |
| for (int i = 0; i < 4; i++) { | |
| windowBase = wsba[i]; | |
| windowMask = ~wsm[i] & (ULL(0xfff) << 20); | |
| if ((busAddr & windowMask) == (windowBase & windowMask)) { | |
| if (wsba[i] & 0x1) { // see if enabled | |
| if (wsba[i] & 0x2) { // see if SG bit is set | |
| /** @todo | |
| This currently is faked by just doing a direct | |
| read from memory, however, to be realistic, this | |
| needs to actually do a bus transaction. The process | |
| is explained in the tsunami documentation on page | |
| 10-12 and basically munges the address to look up a | |
| PTE from a table in memory and then uses that mapping | |
| to create an address for the SG page | |
| */ | |
| tbaMask = ~(((wsm[i] & (ULL(0xfff) << 20)) >> 10) | | |
| ULL(0x3ff)); | |
| baMask = (wsm[i] & (ULL(0xfff) << 20)) | (ULL(0x7f) << 13); | |
| pteAddr = (tba[i] & tbaMask) | ((busAddr & baMask) >> 10); | |
| sys->physProxy.readBlob(pteAddr, &pteEntry, | |
| sizeof(uint64_t)); | |
| dmaAddr = ((pteEntry & ~ULL(0x1)) << 12) | | |
| (busAddr & ULL(0x1fff)); | |
| } else { | |
| baMask = (wsm[i] & (ULL(0xfff) << 20)) | ULL(0xfffff); | |
| tbaMask = ~baMask; | |
| dmaAddr = (tba[i] & tbaMask) | (busAddr & baMask); | |
| } | |
| return (dmaAddr & DMA_ADDR_MASK); | |
| } | |
| } | |
| } | |
| // if no match was found, then return the original address | |
| return busAddr; | |
| } | |
| void | |
| TsunamiPChip::serialize(CheckpointOut &cp) const | |
| { | |
| SERIALIZE_SCALAR(pctl); | |
| SERIALIZE_ARRAY(wsba, 4); | |
| SERIALIZE_ARRAY(wsm, 4); | |
| SERIALIZE_ARRAY(tba, 4); | |
| } | |
| void | |
| TsunamiPChip::unserialize(CheckpointIn &cp) | |
| { | |
| UNSERIALIZE_SCALAR(pctl); | |
| UNSERIALIZE_ARRAY(wsba, 4); | |
| UNSERIALIZE_ARRAY(wsm, 4); | |
| UNSERIALIZE_ARRAY(tba, 4); | |
| } | |
| TsunamiPChip * | |
| TsunamiPChipParams::create() | |
| { | |
| return new TsunamiPChip(this); | |
| } |