/* * Copyright (c) 2019 ARM Limited * All rights reserved * * The license below extends only to copyright in the software and shall * not be construed as granting a license to any other intellectual * property including but not limited to intellectual property relating * to a hardware implementation of the functionality of the software * licensed hereunder. You may use the software subject to the license * terms below provided that you ensure that this notice is replicated * unmodified and in its entirety in all distributions of the software, * modified or unmodified, in source code or in binary form. * * 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: Giacomo Travaglini */ #include "dev/arm/gic_v3_its.hh" #include "debug/AddrRanges.hh" #include "debug/Drain.hh" #include "debug/GIC.hh" #include "debug/ITS.hh" #include "dev/arm/gic_v3.hh" #include "dev/arm/gic_v3_distributor.hh" #include "dev/arm/gic_v3_redistributor.hh" #include "mem/packet_access.hh" #define COMMAND(x, method) { x, DispatchEntry(#x, method) } const AddrRange Gicv3Its::GITS_BASER(0x0100, 0x0138); const uint32_t Gicv3Its::CTLR_QUIESCENT = 0x80000000; ItsProcess::ItsProcess(Gicv3Its &_its) : its(_its), coroutine(nullptr) { } ItsProcess::~ItsProcess() { } void ItsProcess::reinit() { coroutine.reset(new Coroutine( std::bind(&ItsProcess::main, this, std::placeholders::_1))); } const std::string ItsProcess::name() const { return its.name(); } ItsAction ItsProcess::run(PacketPtr pkt) { assert(coroutine != nullptr); assert(*coroutine); return (*coroutine)(pkt).get(); } void ItsProcess::doRead(Yield &yield, Addr addr, void *ptr, size_t size) { ItsAction a; a.type = ItsActionType::SEND_REQ; RequestPtr req = std::make_shared( addr, size, 0, its.masterId); req->taskId(ContextSwitchTaskId::DMA); a.pkt = new Packet(req, MemCmd::ReadReq); a.pkt->dataStatic(ptr); a.delay = 0; PacketPtr pkt = yield(a).get(); assert(pkt); assert(pkt->getSize() >= size); delete pkt; } void ItsProcess::doWrite(Yield &yield, Addr addr, void *ptr, size_t size) { ItsAction a; a.type = ItsActionType::SEND_REQ; RequestPtr req = std::make_shared( addr, size, 0, its.masterId); req->taskId(ContextSwitchTaskId::DMA); a.pkt = new Packet(req, MemCmd::WriteReq); a.pkt->dataStatic(ptr); a.delay = 0; PacketPtr pkt = yield(a).get(); assert(pkt); assert(pkt->getSize() >= size); delete pkt; } void ItsProcess::terminate(Yield &yield) { ItsAction a; a.type = ItsActionType::TERMINATE; a.pkt = NULL; a.delay = 0; yield(a); } void ItsProcess::writeDeviceTable(Yield &yield, uint32_t device_id, DTE dte) { const Addr base = its.pageAddress(Gicv3Its::DEVICE_TABLE); const Addr address = base + (device_id * sizeof(dte)); DPRINTF(ITS, "Writing DTE at address %#x: %#x\n", address, dte); doWrite(yield, address, &dte, sizeof(dte)); } void ItsProcess::writeIrqTranslationTable( Yield &yield, const Addr itt_base, uint32_t event_id, ITTE itte) { const Addr address = itt_base + (event_id * sizeof(itte)); doWrite(yield, address, &itte, sizeof(itte)); DPRINTF(ITS, "Writing ITTE at address %#x: %#x\n", address, itte); } void ItsProcess::writeIrqCollectionTable( Yield &yield, uint32_t collection_id, CTE cte) { const Addr base = its.pageAddress(Gicv3Its::COLLECTION_TABLE); const Addr address = base + (collection_id * sizeof(cte)); doWrite(yield, address, &cte, sizeof(cte)); DPRINTF(ITS, "Writing CTE at address %#x: %#x\n", address, cte); } uint64_t ItsProcess::readDeviceTable(Yield &yield, uint32_t device_id) { uint64_t dte; const Addr base = its.pageAddress(Gicv3Its::DEVICE_TABLE); const Addr address = base + (device_id * sizeof(dte)); doRead(yield, address, &dte, sizeof(dte)); DPRINTF(ITS, "Reading DTE at address %#x: %#x\n", address, dte); return dte; } uint64_t ItsProcess::readIrqTranslationTable( Yield &yield, const Addr itt_base, uint32_t event_id) { uint64_t itte; const Addr address = itt_base + (event_id * sizeof(itte)); doRead(yield, address, &itte, sizeof(itte)); DPRINTF(ITS, "Reading ITTE at address %#x: %#x\n", address, itte); return itte; } uint64_t ItsProcess::readIrqCollectionTable(Yield &yield, uint32_t collection_id) { uint64_t cte; const Addr base = its.pageAddress(Gicv3Its::COLLECTION_TABLE); const Addr address = base + (collection_id * sizeof(cte)); doRead(yield, address, &cte, sizeof(cte)); DPRINTF(ITS, "Reading CTE at address %#x: %#x\n", address, cte); return cte; } ItsTranslation::ItsTranslation(Gicv3Its &_its) : ItsProcess(_its) { reinit(); its.pendingTranslations++; its.gitsControl.quiescent = 0; } ItsTranslation::~ItsTranslation() { assert(its.pendingTranslations >= 1); its.pendingTranslations--; if (!its.pendingTranslations && !its.pendingCommands) its.gitsControl.quiescent = 1; } void ItsTranslation::main(Yield &yield) { PacketPtr pkt = yield.get(); const uint32_t device_id = pkt->req->streamId(); const uint32_t event_id = pkt->getLE(); auto result = translateLPI(yield, device_id, event_id); uint32_t intid = result.first; Gicv3Redistributor *redist = result.second; // Set the LPI in the redistributor redist->setClrLPI(intid, true); // Update the value in GITS_TRANSLATER only once we know // there was no error in the tranlation process (before // terminating the translation its.gitsTranslater = event_id; terminate(yield); } std::pair ItsTranslation::translateLPI(Yield &yield, uint32_t device_id, uint32_t event_id) { if (its.deviceOutOfRange(device_id)) { terminate(yield); } DTE dte = readDeviceTable(yield, device_id); if (!dte.valid || its.idOutOfRange(event_id, dte.ittRange)) { terminate(yield); } ITTE itte = readIrqTranslationTable(yield, dte.ittAddress, event_id); const auto collection_id = itte.icid; if (!itte.valid || its.collectionOutOfRange(collection_id)) { terminate(yield); } CTE cte = readIrqCollectionTable(yield, collection_id); if (!cte.valid) { terminate(yield); } // Returning the INTID and the target Redistributor return std::make_pair(itte.intNum, its.getRedistributor(cte)); } ItsCommand::DispatchTable ItsCommand::cmdDispatcher = { COMMAND(CLEAR, &ItsCommand::clear), COMMAND(DISCARD, &ItsCommand::discard), COMMAND(INT, &ItsCommand::doInt), COMMAND(INV, &ItsCommand::inv), COMMAND(INVALL, &ItsCommand::invall), COMMAND(MAPC, &ItsCommand::mapc), COMMAND(MAPD, &ItsCommand::mapd), COMMAND(MAPI, &ItsCommand::mapi), COMMAND(MAPTI, &ItsCommand::mapti), COMMAND(MOVALL, &ItsCommand::movall), COMMAND(MOVI, &ItsCommand::movi), COMMAND(SYNC, &ItsCommand::sync), COMMAND(VINVALL, &ItsCommand::vinvall), COMMAND(VMAPI, &ItsCommand::vmapi), COMMAND(VMAPP, &ItsCommand::vmapp), COMMAND(VMAPTI, &ItsCommand::vmapti), COMMAND(VMOVI, &ItsCommand::vmovi), COMMAND(VMOVP, &ItsCommand::vmovp), COMMAND(VSYNC, &ItsCommand::vsync), }; ItsCommand::ItsCommand(Gicv3Its &_its) : ItsProcess(_its) { reinit(); its.pendingCommands = true; its.gitsControl.quiescent = 0; } ItsCommand::~ItsCommand() { its.pendingCommands = false; if (!its.pendingTranslations) its.gitsControl.quiescent = 1; } std::string ItsCommand::commandName(uint32_t cmd) { const auto entry = cmdDispatcher.find(cmd); return entry != cmdDispatcher.end() ? entry->second.name : "INVALID"; } void ItsCommand::main(Yield &yield) { ItsAction a; a.type = ItsActionType::INITIAL_NOP; a.pkt = nullptr; a.delay = 0; yield(a); while (its.gitsCwriter.offset != its.gitsCreadr.offset) { CommandEntry command; // Reading the command from CMDQ readCommand(yield, command); processCommand(yield, command); its.incrementReadPointer(); } terminate(yield); } void ItsCommand::readCommand(Yield &yield, CommandEntry &command) { // read the command pointed by GITS_CREADR const Addr cmd_addr = (its.gitsCbaser.physAddr << 12) + (its.gitsCreadr.offset << 5); doRead(yield, cmd_addr, &command, sizeof(command)); DPRINTF(ITS, "Command %s read from queue at address: %#x\n", commandName(command.type), cmd_addr); DPRINTF(ITS, "dw0: %#x dw1: %#x dw2: %#x dw3: %#x\n", command.raw[0], command.raw[1], command.raw[2], command.raw[3]); } void ItsCommand::processCommand(Yield &yield, CommandEntry &command) { const auto entry = cmdDispatcher.find(command.type); if (entry != cmdDispatcher.end()) { // Execute the command entry->second.exec(this, yield, command); } else { panic("Unrecognized command type: %u", command.type); } } void ItsCommand::clear(Yield &yield, CommandEntry &command) { if (deviceOutOfRange(command)) { its.incrementReadPointer(); terminate(yield); } DTE dte = readDeviceTable(yield, command.deviceId); if (!dte.valid || idOutOfRange(command, dte)) { its.incrementReadPointer(); terminate(yield); } ITTE itte = readIrqTranslationTable( yield, dte.ittAddress, command.eventId); if (!itte.valid) { its.incrementReadPointer(); terminate(yield); } const auto collection_id = itte.icid; CTE cte = readIrqCollectionTable(yield, collection_id); if (!cte.valid) { its.incrementReadPointer(); terminate(yield); } // Clear the LPI in the redistributor its.getRedistributor(cte)->setClrLPI(itte.intNum, false); } void ItsCommand::discard(Yield &yield, CommandEntry &command) { if (deviceOutOfRange(command)) { its.incrementReadPointer(); terminate(yield); } DTE dte = readDeviceTable(yield, command.deviceId); if (!dte.valid || idOutOfRange(command, dte)) { its.incrementReadPointer(); terminate(yield); } ITTE itte = readIrqTranslationTable( yield, dte.ittAddress, command.eventId); if (!itte.valid) { its.incrementReadPointer(); terminate(yield); } const auto collection_id = itte.icid; Gicv3Its::CTE cte = readIrqCollectionTable(yield, collection_id); if (!cte.valid) { its.incrementReadPointer(); terminate(yield); } its.getRedistributor(cte)->setClrLPI(itte.intNum, false); // Then removes the mapping from the ITT (invalidating) itte.valid = 0; writeIrqTranslationTable( yield, dte.ittAddress, command.eventId, itte); } void ItsCommand::doInt(Yield &yield, CommandEntry &command) { if (deviceOutOfRange(command)) { its.incrementReadPointer(); terminate(yield); } DTE dte = readDeviceTable(yield, command.deviceId); if (!dte.valid || idOutOfRange(command, dte)) { its.incrementReadPointer(); terminate(yield); } ITTE itte = readIrqTranslationTable( yield, dte.ittAddress, command.eventId); if (!itte.valid) { its.incrementReadPointer(); terminate(yield); } const auto collection_id = itte.icid; CTE cte = readIrqCollectionTable(yield, collection_id); if (!cte.valid) { its.incrementReadPointer(); terminate(yield); } // Set the LPI in the redistributor its.getRedistributor(cte)->setClrLPI(itte.intNum, true); } void ItsCommand::inv(Yield &yield, CommandEntry &command) { if (deviceOutOfRange(command)) { its.incrementReadPointer(); terminate(yield); } DTE dte = readDeviceTable(yield, command.deviceId); if (!dte.valid || idOutOfRange(command, dte)) { its.incrementReadPointer(); terminate(yield); } ITTE itte = readIrqTranslationTable( yield, dte.ittAddress, command.eventId); if (!itte.valid) { its.incrementReadPointer(); terminate(yield); } const auto collection_id = itte.icid; CTE cte = readIrqCollectionTable(yield, collection_id); if (!cte.valid) { its.incrementReadPointer(); terminate(yield); } // Do nothing since caching is currently not supported in // Redistributor } void ItsCommand::invall(Yield &yield, CommandEntry &command) { if (collectionOutOfRange(command)) { its.incrementReadPointer(); terminate(yield); } const auto icid = bits(command.raw[2], 15, 0); CTE cte = readIrqCollectionTable(yield, icid); if (!cte.valid) { its.incrementReadPointer(); terminate(yield); } // Do nothing since caching is currently not supported in // Redistributor } void ItsCommand::mapc(Yield &yield, CommandEntry &command) { if (collectionOutOfRange(command)) { its.incrementReadPointer(); terminate(yield); } CTE cte = 0; cte.valid = bits(command.raw[2], 63); cte.rdBase = bits(command.raw[2], 50, 16); const auto icid = bits(command.raw[2], 15, 0); writeIrqCollectionTable(yield, icid, cte); } void ItsCommand::mapd(Yield &yield, CommandEntry &command) { if (deviceOutOfRange(command) || sizeOutOfRange(command)) { its.incrementReadPointer(); terminate(yield); } DTE dte = 0; dte.valid = bits(command.raw[2], 63); dte.ittAddress = mbits(command.raw[2], 51, 8); dte.ittRange = bits(command.raw[1], 4, 0); writeDeviceTable(yield, command.deviceId, dte); } void ItsCommand::mapi(Yield &yield, CommandEntry &command) { if (deviceOutOfRange(command)) { its.incrementReadPointer(); terminate(yield); } if (collectionOutOfRange(command)) { its.incrementReadPointer(); terminate(yield); } DTE dte = readDeviceTable(yield, command.deviceId); if (!dte.valid || idOutOfRange(command, dte) || its.lpiOutOfRange(command.eventId)) { its.incrementReadPointer(); terminate(yield); } Gicv3Its::ITTE itte = readIrqTranslationTable( yield, dte.ittAddress, command.eventId); itte.valid = 1; itte.intType = Gicv3Its::PHYSICAL_INTERRUPT; itte.intNum = command.eventId; itte.icid = bits(command.raw[2], 15, 0); writeIrqTranslationTable( yield, dte.ittAddress, command.eventId, itte); } void ItsCommand::mapti(Yield &yield, CommandEntry &command) { if (deviceOutOfRange(command)) { its.incrementReadPointer(); terminate(yield); } if (collectionOutOfRange(command)) { its.incrementReadPointer(); terminate(yield); } DTE dte = readDeviceTable(yield, command.deviceId); const auto pintid = bits(command.raw[1], 63, 32); if (!dte.valid || idOutOfRange(command, dte) || its.lpiOutOfRange(pintid)) { its.incrementReadPointer(); terminate(yield); } ITTE itte = readIrqTranslationTable( yield, dte.ittAddress, command.eventId); itte.valid = 1; itte.intType = Gicv3Its::PHYSICAL_INTERRUPT; itte.intNum = pintid; itte.icid = bits(command.raw[2], 15, 0); writeIrqTranslationTable( yield, dte.ittAddress, command.eventId, itte); } void ItsCommand::movall(Yield &yield, CommandEntry &command) { const uint64_t rd1 = bits(command.raw[2], 50, 16); const uint64_t rd2 = bits(command.raw[3], 50, 16); if (rd1 != rd2) { Gicv3Redistributor * redist1 = its.getRedistributor(rd1); Gicv3Redistributor * redist2 = its.getRedistributor(rd2); its.moveAllPendingState(redist1, redist2); } } void ItsCommand::movi(Yield &yield, CommandEntry &command) { if (deviceOutOfRange(command)) { its.incrementReadPointer(); terminate(yield); } if (collectionOutOfRange(command)) { its.incrementReadPointer(); terminate(yield); } DTE dte = readDeviceTable(yield, command.deviceId); if (!dte.valid || idOutOfRange(command, dte)) { its.incrementReadPointer(); terminate(yield); } ITTE itte = readIrqTranslationTable( yield, dte.ittAddress, command.eventId); if (!itte.valid || itte.intType == Gicv3Its::VIRTUAL_INTERRUPT) { its.incrementReadPointer(); terminate(yield); } const auto collection_id1 = itte.icid; CTE cte1 = readIrqCollectionTable(yield, collection_id1); if (!cte1.valid) { its.incrementReadPointer(); terminate(yield); } const auto collection_id2 = bits(command.raw[2], 15, 0); CTE cte2 = readIrqCollectionTable(yield, collection_id2); if (!cte2.valid) { its.incrementReadPointer(); terminate(yield); } Gicv3Redistributor *first_redist = its.getRedistributor(cte1); Gicv3Redistributor *second_redist = its.getRedistributor(cte2); if (second_redist != first_redist) { // move pending state of the interrupt from one redistributor // to the other. if (first_redist->isPendingLPI(itte.intNum)) { first_redist->setClrLPI(itte.intNum, false); second_redist->setClrLPI(itte.intNum, true); } } itte.icid = collection_id2; writeIrqTranslationTable( yield, dte.ittAddress, command.eventId, itte); } void ItsCommand::sync(Yield &yield, CommandEntry &command) { warn("ITS %s command unimplemented", __func__); } void ItsCommand::vinvall(Yield &yield, CommandEntry &command) { panic("ITS %s command unimplemented", __func__); } void ItsCommand::vmapi(Yield &yield, CommandEntry &command) { panic("ITS %s command unimplemented", __func__); } void ItsCommand::vmapp(Yield &yield, CommandEntry &command) { panic("ITS %s command unimplemented", __func__); } void ItsCommand::vmapti(Yield &yield, CommandEntry &command) { panic("ITS %s command unimplemented", __func__); } void ItsCommand::vmovi(Yield &yield, CommandEntry &command) { panic("ITS %s command unimplemented", __func__); } void ItsCommand::vmovp(Yield &yield, CommandEntry &command) { panic("ITS %s command unimplemented", __func__); } void ItsCommand::vsync(Yield &yield, CommandEntry &command) { panic("ITS %s command unimplemented", __func__); } Gicv3Its::Gicv3Its(const Gicv3ItsParams *params) : BasicPioDevice(params, params->pio_size), dmaPort(name() + ".dma", *this), gitsControl(CTLR_QUIESCENT), gitsTyper(params->gits_typer), gitsCbaser(0), gitsCreadr(0), gitsCwriter(0), gitsIidr(0), tableBases(NUM_BASER_REGS, 0), masterId(params->system->getMasterId(this)), gic(nullptr), commandEvent([this] { checkCommandQueue(); }, name()), pendingCommands(false), pendingTranslations(0) { BASER device_baser = 0; device_baser.type = DEVICE_TABLE; device_baser.entrySize = sizeof(uint64_t) - 1; tableBases[0] = device_baser; BASER icollect_baser = 0; icollect_baser.type = COLLECTION_TABLE; icollect_baser.entrySize = sizeof(uint64_t) - 1; tableBases[1] = icollect_baser; } void Gicv3Its::setGIC(Gicv3 *_gic) { assert(!gic); gic = _gic; } AddrRangeList Gicv3Its::getAddrRanges() const { assert(pioSize != 0); AddrRangeList ranges; DPRINTF(AddrRanges, "registering range: %#x-%#x\n", pioAddr, pioSize); ranges.push_back(RangeSize(pioAddr, pioSize)); return ranges; } Tick Gicv3Its::read(PacketPtr pkt) { const Addr addr = pkt->getAddr() - pioAddr; uint64_t value = 0; DPRINTF(GIC, "%s register at addr: %#x\n", __func__, addr); switch (addr) { case GITS_CTLR: value = gitsControl; break; case GITS_IIDR: value = gitsIidr; break; case GITS_TYPER: value = gitsTyper; break; case GITS_TYPER + 4: value = gitsTyper.high; break; case GITS_CBASER: value = gitsCbaser; break; case GITS_CBASER + 4: value = gitsCbaser.high; break; case GITS_CWRITER: value = gitsCwriter; break; case GITS_CWRITER + 4: value = gitsCwriter.high; break; case GITS_CREADR: value = gitsCreadr; break; case GITS_CREADR + 4: value = gitsCreadr.high; break; case GITS_PIDR2: value = gic->getDistributor()->gicdPidr2; break; case GITS_TRANSLATER: value = gitsTranslater; break; default: if (GITS_BASER.contains(addr)) { auto relative_addr = addr - GITS_BASER.start(); auto baser_index = relative_addr / sizeof(uint64_t); value = tableBases[baser_index]; break; } else { panic("Unrecognized register access\n"); } } pkt->setUintX(value, LittleEndianByteOrder); pkt->makeAtomicResponse(); return pioDelay; } Tick Gicv3Its::write(PacketPtr pkt) { Addr addr = pkt->getAddr() - pioAddr; DPRINTF(GIC, "%s register at addr: %#x\n", __func__, addr); switch (addr) { case GITS_CTLR: assert(pkt->getSize() == sizeof(uint32_t)); gitsControl = (pkt->getLE() & ~CTLR_QUIESCENT); // We should check here if the ITS has been disabled, and if // that's the case, flush GICv3 caches to external memory. // This is not happening now, since LPI caching is not // currently implemented in gem5. break; case GITS_IIDR: panic("GITS_IIDR is Read Only\n"); case GITS_TYPER: panic("GITS_TYPER is Read Only\n"); case GITS_CBASER: if (pkt->getSize() == sizeof(uint32_t)) { gitsCbaser.low = pkt->getLE(); } else { assert(pkt->getSize() == sizeof(uint64_t)); gitsCbaser = pkt->getLE(); } gitsCreadr = 0; // Cleared when CBASER gets written checkCommandQueue(); break; case GITS_CBASER + 4: assert(pkt->getSize() == sizeof(uint32_t)); gitsCbaser.high = pkt->getLE(); gitsCreadr = 0; // Cleared when CBASER gets written checkCommandQueue(); break; case GITS_CWRITER: if (pkt->getSize() == sizeof(uint32_t)) { gitsCwriter.low = pkt->getLE(); } else { assert(pkt->getSize() == sizeof(uint64_t)); gitsCwriter = pkt->getLE(); } checkCommandQueue(); break; case GITS_CWRITER + 4: assert(pkt->getSize() == sizeof(uint32_t)); gitsCwriter.high = pkt->getLE(); checkCommandQueue(); break; case GITS_CREADR: panic("GITS_READR is Read Only\n"); case GITS_TRANSLATER: if (gitsControl.enabled) { translate(pkt); } break; default: if (GITS_BASER.contains(addr)) { auto relative_addr = addr - GITS_BASER.start(); auto baser_index = relative_addr / sizeof(uint64_t); const uint64_t table_base = tableBases[baser_index]; const uint64_t w_mask = tableBases[baser_index].type ? BASER_WMASK : BASER_WMASK_UNIMPL; const uint64_t val = pkt->getLE() & w_mask; tableBases[baser_index] = table_base | val; break; } else { panic("Unrecognized register access\n"); } } pkt->makeAtomicResponse(); return pioDelay; } bool Gicv3Its::idOutOfRange(uint32_t event_id, uint8_t itt_range) const { const uint32_t id_bits = gitsTyper.idBits; return event_id >= (1ULL << (id_bits + 1)) || event_id >= ((1ULL << itt_range) + 1); } bool Gicv3Its::deviceOutOfRange(uint32_t device_id) const { return device_id >= (1ULL << (gitsTyper.devBits + 1)); } bool Gicv3Its::sizeOutOfRange(uint32_t size) const { return size > gitsTyper.idBits; } bool Gicv3Its::collectionOutOfRange(uint32_t collection_id) const { // If GITS_TYPER.CIL == 0, ITS supports 16-bit CollectionID // Otherwise, #bits is specified by GITS_TYPER.CIDbits const auto cid_bits = gitsTyper.cil == 0 ? 16 : gitsTyper.cidBits + 1; return collection_id >= (1ULL << cid_bits); } bool Gicv3Its::lpiOutOfRange(uint32_t intid) const { return intid >= (1ULL << (Gicv3Distributor::IDBITS + 1)) || (intid < Gicv3Redistributor::SMALLEST_LPI_ID && intid != Gicv3::INTID_SPURIOUS); } DrainState Gicv3Its::drain() { if (!pendingCommands && !pendingTranslations) { return DrainState::Drained; } else { DPRINTF(Drain, "GICv3 ITS not drained\n"); return DrainState::Draining; } } void Gicv3Its::serialize(CheckpointOut & cp) const { SERIALIZE_SCALAR(gitsControl); SERIALIZE_SCALAR(gitsTyper); SERIALIZE_SCALAR(gitsCbaser); SERIALIZE_SCALAR(gitsCreadr); SERIALIZE_SCALAR(gitsCwriter); SERIALIZE_SCALAR(gitsIidr); SERIALIZE_CONTAINER(tableBases); } void Gicv3Its::unserialize(CheckpointIn & cp) { UNSERIALIZE_SCALAR(gitsControl); UNSERIALIZE_SCALAR(gitsTyper); UNSERIALIZE_SCALAR(gitsCbaser); UNSERIALIZE_SCALAR(gitsCreadr); UNSERIALIZE_SCALAR(gitsCwriter); UNSERIALIZE_SCALAR(gitsIidr); UNSERIALIZE_CONTAINER(tableBases); } void Gicv3Its::incrementReadPointer() { // Make the reader point to the next element gitsCreadr.offset = gitsCreadr.offset + 1; // Check for wrapping if (gitsCreadr.offset == maxCommands()) { gitsCreadr.offset = 0; } } uint64_t Gicv3Its::maxCommands() const { return (4096 * (gitsCbaser.size + 1)) / sizeof(ItsCommand::CommandEntry); } void Gicv3Its::checkCommandQueue() { if (!gitsControl.enabled || !gitsCbaser.valid) return; // If GITS_CWRITER gets set by sw to a value bigger than the // allowed one, the command queue should stop processing commands // until the register gets reset to an allowed one if (gitsCwriter.offset >= maxCommands()) { return; } if (gitsCwriter.offset != gitsCreadr.offset) { // writer and reader pointing to different command // entries: queue not empty. DPRINTF(ITS, "Reading command from queue\n"); if (!pendingCommands) { auto *cmd_proc = new ItsCommand(*this); runProcess(cmd_proc, nullptr); } else { DPRINTF(ITS, "Waiting for pending command to finish\n"); } } } Port & Gicv3Its::getPort(const std::string &if_name, PortID idx) { if (if_name == "dma") { return dmaPort; } return BasicPioDevice::getPort(if_name, idx); } void Gicv3Its::recvReqRetry() { assert(!packetsToRetry.empty()); while (!packetsToRetry.empty()) { ItsAction a = packetsToRetry.front(); assert(a.type == ItsActionType::SEND_REQ); if (!dmaPort.sendTimingReq(a.pkt)) break; packetsToRetry.pop(); } } bool Gicv3Its::recvTimingResp(PacketPtr pkt) { // @todo: We need to pay for this and not just zero it out pkt->headerDelay = pkt->payloadDelay = 0; ItsProcess *proc = safe_cast(pkt->popSenderState()); runProcessTiming(proc, pkt); return true; } ItsAction Gicv3Its::runProcess(ItsProcess *proc, PacketPtr pkt) { if (sys->isAtomicMode()) { return runProcessAtomic(proc, pkt); } else if (sys->isTimingMode()) { return runProcessTiming(proc, pkt); } else { panic("Not in timing or atomic mode\n"); } } ItsAction Gicv3Its::runProcessTiming(ItsProcess *proc, PacketPtr pkt) { ItsAction action = proc->run(pkt); switch (action.type) { case ItsActionType::SEND_REQ: action.pkt->pushSenderState(proc); if (packetsToRetry.empty() && dmaPort.sendTimingReq(action.pkt)) { } else { packetsToRetry.push(action); } break; case ItsActionType::TERMINATE: delete proc; if (!pendingCommands && !commandEvent.scheduled()) { schedule(commandEvent, clockEdge()); } break; default: panic("Unknown action\n"); } return action; } ItsAction Gicv3Its::runProcessAtomic(ItsProcess *proc, PacketPtr pkt) { ItsAction action; Tick delay = 0; bool terminate = false; do { action = proc->run(pkt); switch (action.type) { case ItsActionType::SEND_REQ: delay += dmaPort.sendAtomic(action.pkt); pkt = action.pkt; break; case ItsActionType::TERMINATE: delete proc; terminate = true; break; default: panic("Unknown action\n"); } } while (!terminate); action.delay = delay; return action; } void Gicv3Its::translate(PacketPtr pkt) { DPRINTF(ITS, "Starting Translation Request\n"); auto *proc = new ItsTranslation(*this); runProcess(proc, pkt); } Gicv3Redistributor* Gicv3Its::getRedistributor(uint64_t rd_base) { if (gitsTyper.pta == 1) { // RDBase is a redistributor address return gic->getRedistributorByAddr(rd_base << 16); } else { // RDBase is a redistributor number return gic->getRedistributor(rd_base); } } Addr Gicv3Its::pageAddress(Gicv3Its::ItsTables table) { auto base_it = std::find_if( tableBases.begin(), tableBases.end(), [table] (const BASER &b) { return b.type == table; } ); panic_if(base_it == tableBases.end(), "ITS Table not recognised\n"); const BASER base = *base_it; // real address depends on page size switch (base.pageSize) { case SIZE_4K: case SIZE_16K: return mbits(base, 47, 12); case SIZE_64K: return mbits(base, 47, 16) | (bits(base, 15, 12) << 48); default: panic("Unsupported page size\n"); } } void Gicv3Its::moveAllPendingState( Gicv3Redistributor *rd1, Gicv3Redistributor *rd2) { const uint64_t largest_lpi_id = 1ULL << (rd1->lpiIDBits + 1); uint8_t lpi_pending_table[largest_lpi_id / 8]; // Copying the pending table from redistributor 1 to redistributor 2 rd1->memProxy->readBlob( rd1->lpiPendingTablePtr, (uint8_t *)lpi_pending_table, sizeof(lpi_pending_table)); rd2->memProxy->writeBlob( rd2->lpiPendingTablePtr, (uint8_t *)lpi_pending_table, sizeof(lpi_pending_table)); // Clearing pending table in redistributor 2 rd1->memProxy->memsetBlob( rd1->lpiPendingTablePtr, 0, sizeof(lpi_pending_table)); rd2->updateDistributor(); } Gicv3Its * Gicv3ItsParams::create() { return new Gicv3Its(this); }