--- /dev/null
+/*
+ * 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);
+
+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<Request>(
+ 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<Request>(
+ 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;
+
+ 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;
+
+ 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;
+
+ 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)
+{
+ const Addr base = its.pageAddress(Gicv3Its::DEVICE_TABLE);
+ const Addr address = base + device_id;
+
+ uint64_t 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)
+{
+ const Addr address = itt_base + event_id;
+
+ uint64_t 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)
+{
+ const Addr base = its.pageAddress(Gicv3Its::COLLECTION_TABLE);
+ const Addr address = base + collection_id;
+
+ uint64_t 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++;
+}
+
+ItsTranslation::~ItsTranslation()
+{
+ assert(its.pendingTranslations >= 1);
+ its.pendingTranslations--;
+}
+
+void
+ItsTranslation::main(Yield &yield)
+{
+ PacketPtr pkt = yield.get();
+
+ const uint32_t device_id = pkt->req->streamId();
+ const uint32_t event_id = pkt->getLE<uint32_t>();
+
+ 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<uint32_t, Gicv3Redistributor *>
+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;
+}
+
+ItsCommand::~ItsCommand()
+{
+ its.pendingCommands = false;
+}
+
+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(0x1),
+ gitsTyper(params->gits_typer),
+ gitsCbaser(0), gitsCreadr(0),
+ gitsCwriter(0), gitsIidr(0),
+ masterId(params->system->getMasterId(this)),
+ gic(nullptr),
+ commandEvent([this] { checkCommandQueue(); }, name()),
+ pendingCommands(false),
+ pendingTranslations(0)
+{
+ for (auto idx = 0; idx < NUM_BASER_REGS; idx++) {
+ BASER gits_baser = 0;
+ gits_baser.type = idx;
+ gits_baser.entrySize = sizeof(uint64_t) - 1;
+ tableBases.push_back(gits_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_CBASER:
+ value = gitsCbaser;
+ break;
+
+ case GITS_CWRITER:
+ value = gitsCwriter;
+ break;
+
+ case GITS_CREADR:
+ value = gitsCreadr;
+ 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<uint32_t>();
+ break;
+
+ case GITS_IIDR:
+ panic("GITS_IIDR is Read Only\n");
+
+ case GITS_TYPER:
+ panic("GITS_TYPER is Read Only\n");
+
+ case GITS_CBASER:
+ assert(pkt->getSize() == sizeof(uint64_t));
+ gitsCbaser = pkt->getLE<uint64_t>();
+ gitsCreadr = 0; // Cleared when CBASER gets written
+
+ checkCommandQueue();
+ break;
+
+ case GITS_CWRITER:
+ assert(pkt->getSize() == sizeof(uint64_t));
+ gitsCwriter = pkt->getLE<uint64_t>();
+
+ 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);
+
+ BASER val = pkt->getLE<uint64_t>();
+
+ panic_if(val.indirect,
+ "We currently don't support two level ITS tables");
+
+ tableBases[baser_index] = 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
+ auto queue_end = (4096 * (gitsCbaser.size + 1));
+
+ if (gitsCreadr.offset == queue_end) {
+ gitsCreadr.offset = 0;
+ }
+}
+
+void
+Gicv3Its::checkCommandQueue()
+{
+ if (!gitsControl.enabled || !gitsCbaser.valid)
+ 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<ItsProcess *>(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)
+{
+ const BASER base = tableBases[table];
+ // 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->updateAndInformCPUInterface();
+}
+
+Gicv3Its *
+Gicv3ItsParams::create()
+{
+ return new Gicv3Its(this);
+}
--- /dev/null
+/*
+ * 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
+ */
+
+#ifndef __DEV_ARM_GICV3_ITS_H__
+#define __DEV_ARM_GICV3_ITS_H__
+
+#include <queue>
+
+#include "base/coroutine.hh"
+#include "dev/dma_device.hh"
+#include "params/Gicv3Its.hh"
+
+class Gicv3;
+class Gicv3Redistributor;
+class ItsProcess;
+class ItsTranslation;
+class ItsCommand;
+
+enum class ItsActionType
+{
+ INITIAL_NOP,
+ SEND_REQ,
+ TERMINATE,
+};
+
+struct ItsAction
+{
+ ItsActionType type;
+ PacketPtr pkt;
+ Tick delay;
+};
+
+/**
+ * GICv3 ITS module. This class is just modelling a pio device with its
+ * memory mapped registers. Most of the ITS functionalities are
+ * implemented as processes (ItsProcess) objects, like ItsTranslation or
+ * ItsCommand.
+ * Main job of Gicv3Its is to spawn those processes upon receival of packets.
+ */
+class Gicv3Its : public BasicPioDevice
+{
+ friend class ::ItsProcess;
+ friend class ::ItsTranslation;
+ friend class ::ItsCommand;
+ public:
+ class DataPort : public MasterPort
+ {
+ protected:
+ Gicv3Its &its;
+
+ public:
+ DataPort(const std::string &_name, Gicv3Its &_its) :
+ MasterPort(_name, &_its),
+ its(_its)
+ {}
+
+ virtual ~DataPort() {}
+
+ bool recvTimingResp(PacketPtr pkt) { return its.recvTimingResp(pkt); }
+ void recvReqRetry() { return its.recvReqRetry(); }
+ };
+
+ DataPort dmaPort;
+
+ Port & getPort(const std::string &if_name, PortID idx) override;
+ bool recvTimingResp(PacketPtr pkt);
+ void recvReqRetry();
+
+ Gicv3Its(const Gicv3ItsParams *params);
+
+ void setGIC(Gicv3 *_gic);
+
+ static const uint32_t itsControl = 0x0;
+ static const uint32_t itsTranslate = 0x10000;
+
+ // Address range part of Control frame
+ static const AddrRange GITS_BASER;
+
+ static const uint32_t NUM_BASER_REGS = 8;
+
+ enum : Addr
+ {
+ // Control frame
+ GITS_CTLR = itsControl + 0x0000,
+ GITS_IIDR = itsControl + 0x0004,
+ GITS_TYPER = itsControl + 0x0008,
+ GITS_CBASER = itsControl + 0x0080,
+ GITS_CWRITER = itsControl + 0x0088,
+ GITS_CREADR = itsControl + 0x0090,
+
+ // Translation frame
+ GITS_TRANSLATER = itsTranslate + 0x0040
+ };
+
+ AddrRangeList getAddrRanges() const override;
+
+ Tick read(PacketPtr pkt) override;
+ Tick write(PacketPtr pkt) override;
+
+ DrainState drain() override;
+ void serialize(CheckpointOut & cp) const override;
+ void unserialize(CheckpointIn & cp) override;
+
+ void translate(PacketPtr pkt);
+
+ BitUnion32(CTLR)
+ Bitfield<31> quiescent;
+ Bitfield<7, 4> itsNumber;
+ Bitfield<1> imDe;
+ Bitfield<0> enabled;
+ EndBitUnion(CTLR)
+
+ // Command read/write, (CREADR, CWRITER)
+ BitUnion64(CRDWR)
+ Bitfield<19, 5> offset;
+ Bitfield<0> retry;
+ Bitfield<0> stalled;
+ EndBitUnion(CRDWR)
+
+ BitUnion64(CBASER)
+ Bitfield<63> valid;
+ Bitfield<61, 59> innerCache;
+ Bitfield<55, 53> outerCache;
+ Bitfield<51, 12> physAddr;
+ Bitfield<11, 10> shareability;
+ Bitfield<7, 0> size;
+ EndBitUnion(CBASER)
+
+ BitUnion64(BASER)
+ Bitfield<63> valid;
+ Bitfield<62> indirect;
+ Bitfield<61, 59> innerCache;
+ Bitfield<58, 56> type;
+ Bitfield<55, 53> outerCache;
+ Bitfield<52, 48> entrySize;
+ Bitfield<47, 12> physAddr;
+ Bitfield<11, 10> shareability;
+ Bitfield<9, 8> pageSize;
+ Bitfield<7, 0> size;
+ EndBitUnion(BASER)
+
+ BitUnion64(TYPER)
+ Bitfield<37> vmovp;
+ Bitfield<36> cil;
+ Bitfield<35, 32> cidBits;
+ Bitfield<31, 24> hcc;
+ Bitfield<19> pta;
+ Bitfield<18> seis;
+ Bitfield<17, 13> devBits;
+ Bitfield<12, 8> idBits;
+ Bitfield<7, 4> ittEntrySize;
+ Bitfield<2> cct;
+ Bitfield<1> _virtual;
+ Bitfield<0> physical;
+ EndBitUnion(TYPER)
+
+ CTLR gitsControl;
+ TYPER gitsTyper;
+ CBASER gitsCbaser;
+ CRDWR gitsCreadr;
+ CRDWR gitsCwriter;
+ uint32_t gitsIidr;
+ uint32_t gitsTranslater;
+
+ std::vector<BASER> tableBases;
+
+ /**
+ * Returns TRUE if the eventID supplied has bits above the implemented
+ * size or above the itt_range
+ */
+ bool idOutOfRange(uint32_t event_id, uint8_t itt_range) const;
+
+ /**
+ * Returns TRUE if the value supplied has bits above the implemented range
+ * or if the value supplied exceeds the maximum configured size in the
+ * appropriate GITS_BASER<n>
+ */
+ bool deviceOutOfRange(uint32_t device_id) const;
+
+ /**
+ * Returns TRUE if the value (size) supplied exceeds the maximum
+ * allowed by GITS_TYPER.ID_bits. Size is the parameter which is
+ * passed to the ITS via the MAPD command and is stored in the
+ * DTE.ittRange field.
+ */
+ bool sizeOutOfRange(uint32_t size) const;
+
+ /**
+ * Returns TRUE if the value supplied has bits above the implemented range
+ * or if the value exceeds the total number of collections supported in
+ * hardware and external memory
+ */
+ bool collectionOutOfRange(uint32_t collection_id) const;
+
+ /**
+ * Returns TRUE if the value supplied is larger than that permitted by
+ * GICD_TYPER.IDbits or not in the LPI range and is not 1023
+ */
+ bool lpiOutOfRange(uint32_t intid) const;
+
+ private: // Command
+ void checkCommandQueue();
+ void incrementReadPointer();
+
+ public: // TableWalk
+ BitUnion64(DTE)
+ Bitfield<57, 53> ittRange;
+ Bitfield<52, 1> ittAddress;
+ Bitfield<0> valid;
+ EndBitUnion(DTE)
+
+ BitUnion64(ITTE)
+ Bitfield<59, 46> vpeid;
+ Bitfield<45, 30> icid;
+ Bitfield<29, 16> intNumHyp;
+ Bitfield<15, 2> intNum;
+ Bitfield<1> intType;
+ Bitfield<0> valid;
+ EndBitUnion(ITTE)
+
+ BitUnion64(CTE)
+ Bitfield<40, 1> rdBase;
+ Bitfield<0> valid;
+ EndBitUnion(CTE)
+
+ enum InterruptType
+ {
+ VIRTUAL_INTERRUPT = 0,
+ PHYSICAL_INTERRUPT = 1
+ };
+
+ private:
+ Gicv3Redistributor* getRedistributor(uint64_t rd_base);
+ Gicv3Redistributor* getRedistributor(CTE cte)
+ {
+ return getRedistributor(cte.rdBase);
+ }
+
+ ItsAction runProcess(ItsProcess *proc, PacketPtr pkt);
+ ItsAction runProcessTiming(ItsProcess *proc, PacketPtr pkt);
+ ItsAction runProcessAtomic(ItsProcess *proc, PacketPtr pkt);
+
+ enum ItsTables
+ {
+ DEVICE_TABLE = 1,
+ VPE_TABLE = 2,
+ TRANSLATION_TABLE = 3,
+ COLLECTION_TABLE = 4
+ };
+
+ enum PageSize
+ {
+ SIZE_4K,
+ SIZE_16K,
+ SIZE_64K
+ };
+
+ Addr pageAddress(enum ItsTables table);
+
+ void moveAllPendingState(
+ Gicv3Redistributor *rd1, Gicv3Redistributor *rd2);
+
+ private:
+ std::queue<ItsAction> packetsToRetry;
+ uint32_t masterId;
+ Gicv3 *gic;
+ EventFunctionWrapper commandEvent;
+
+ bool pendingCommands;
+ uint32_t pendingTranslations;
+};
+
+/**
+ * ItsProcess is a base coroutine wrapper which is spawned by
+ * the Gicv3Its module when the latter needs to perform different
+ * actions, like translating a peripheral's MSI into an LPI
+ * (See derived ItsTranslation) or processing a Command from the
+ * ITS queue (ItsCommand).
+ * The action to take is implemented by the method:
+ *
+ * virtual void main(Yield &yield) = 0;
+ * It's inheriting from Packet::SenderState since the generic process
+ * will be stopped (we are using coroutines) and sent with the packet
+ * to memory when doing table walks.
+ * When Gicv3Its receives a response, it will resume the coroutine from
+ * the point it stopped when yielding.
+ */
+class ItsProcess : public Packet::SenderState
+{
+ public:
+ using DTE = Gicv3Its::DTE;
+ using ITTE = Gicv3Its::ITTE;
+ using CTE = Gicv3Its::CTE;
+ using Coroutine = m5::Coroutine<PacketPtr, ItsAction>;
+ using Yield = Coroutine::CallerType;
+
+ ItsProcess(Gicv3Its &_its);
+ virtual ~ItsProcess();
+
+ /** Returns the Gicv3Its name. Mainly used for DPRINTS */
+ const std::string name() const;
+
+ ItsAction run(PacketPtr pkt);
+
+ protected:
+ void reinit();
+ virtual void main(Yield &yield) = 0;
+
+ void writeDeviceTable(Yield &yield, uint32_t device_id, DTE dte);
+
+ void writeIrqTranslationTable(
+ Yield &yield, const Addr itt_base, uint32_t event_id, ITTE itte);
+
+ void writeIrqCollectionTable(
+ Yield &yield, uint32_t collection_id, CTE cte);
+
+ uint64_t readDeviceTable(
+ Yield &yield, uint32_t device_id);
+
+ uint64_t readIrqTranslationTable(
+ Yield &yield, const Addr itt_base, uint32_t event_id);
+
+ uint64_t readIrqCollectionTable(Yield &yield, uint32_t collection_id);
+
+ void doRead(Yield &yield, Addr addr, void *ptr, size_t size);
+ void doWrite(Yield &yield, Addr addr, void *ptr, size_t size);
+ void terminate(Yield &yield);
+
+ protected:
+ Gicv3Its &its;
+
+ private:
+ std::unique_ptr<Coroutine> coroutine;
+};
+
+/**
+ * An ItsTranslation is created whenever a peripheral writes a message in
+ * GITS_TRANSLATER (MSI). In this case main will simply do the table walks
+ * until it gets a redistributor and an INTID. It will then raise the
+ * LPI interrupt to the target redistributor.
+ */
+class ItsTranslation : public ItsProcess
+{
+ public:
+ ItsTranslation(Gicv3Its &_its);
+ ~ItsTranslation();
+
+ protected:
+ void main(Yield &yield) override;
+
+ std::pair<uint32_t, Gicv3Redistributor *>
+ translateLPI(Yield &yield, uint32_t device_id, uint32_t event_id);
+};
+
+/**
+ * An ItsCommand is created whenever there is a new command in the command
+ * queue. Only one command can be executed per time.
+ * main will firstly read the command from memory and then it will process
+ * it.
+ */
+class ItsCommand : public ItsProcess
+{
+ public:
+ union CommandEntry
+ {
+ struct
+ {
+ uint32_t type;
+ uint32_t deviceId;
+ uint32_t eventId;
+ uint32_t pintId;
+
+ uint32_t data[4];
+ };
+ uint64_t raw[4];
+ };
+
+ enum CommandType : uint32_t
+ {
+ CLEAR = 0x04,
+ DISCARD = 0x0F,
+ INT = 0x03,
+ INV = 0x0C,
+ INVALL = 0x0D,
+ MAPC = 0x09,
+ MAPD = 0x08,
+ MAPI = 0x0B,
+ MAPTI = 0x0A,
+ MOVALL = 0x0E,
+ MOVI = 0x01,
+ SYNC = 0x05,
+ VINVALL = 0x2D,
+ VMAPI = 0x2B,
+ VMAPP = 0x29,
+ VMAPTI = 0x2A,
+ VMOVI = 0x21,
+ VMOVP = 0x22,
+ VSYNC = 0x25
+ };
+
+ ItsCommand(Gicv3Its &_its);
+ ~ItsCommand();
+
+ protected:
+ /**
+ * Dispatch entry is a metadata struct which contains information about
+ * the command (like the name) and the function object implementing
+ * the command.
+ */
+ struct DispatchEntry
+ {
+ using ExecFn = std::function<void(ItsCommand*, Yield&, CommandEntry&)>;
+
+ DispatchEntry(std::string _name, ExecFn _exec)
+ : name(_name), exec(_exec)
+ {}
+
+ std::string name;
+ ExecFn exec;
+ };
+
+ using DispatchTable = std::unordered_map<
+ std::underlying_type<enum CommandType>::type, DispatchEntry>;
+
+ static DispatchTable cmdDispatcher;
+
+ static std::string commandName(uint32_t cmd);
+
+ void main(Yield &yield) override;
+
+ void readCommand(Yield &yield, CommandEntry &command);
+ void processCommand(Yield &yield, CommandEntry &command);
+
+ // Commands
+ void clear(Yield &yield, CommandEntry &command);
+ void discard(Yield &yield, CommandEntry &command);
+ void mapc(Yield &yield, CommandEntry &command);
+ void mapd(Yield &yield, CommandEntry &command);
+ void mapi(Yield &yield, CommandEntry &command);
+ void mapti(Yield &yield, CommandEntry &command);
+ void movall(Yield &yield, CommandEntry &command);
+ void movi(Yield &yield, CommandEntry &command);
+ void sync(Yield &yield, CommandEntry &command);
+ void doInt(Yield &yield, CommandEntry &command);
+ void inv(Yield &yield, CommandEntry &command);
+ void invall(Yield &yield, CommandEntry &command);
+ void vinvall(Yield &yield, CommandEntry &command);
+ void vmapi(Yield &yield, CommandEntry &command);
+ void vmapp(Yield &yield, CommandEntry &command);
+ void vmapti(Yield &yield, CommandEntry &command);
+ void vmovi(Yield &yield, CommandEntry &command);
+ void vmovp(Yield &yield, CommandEntry &command);
+ void vsync(Yield &yield, CommandEntry &command);
+
+ protected: // Helpers
+ bool idOutOfRange(CommandEntry &command, DTE dte) const
+ {
+ return its.idOutOfRange(command.eventId, dte.ittRange);
+ }
+
+ bool deviceOutOfRange(CommandEntry &command) const
+ {
+ return its.deviceOutOfRange(command.deviceId);
+ }
+
+ bool sizeOutOfRange(CommandEntry &command) const
+ {
+ const auto size = bits(command.raw[1], 4, 0);
+ const auto valid = bits(command.raw[2], 63);
+ if (valid)
+ return its.sizeOutOfRange(size);
+ else
+ return false;
+ }
+
+ bool collectionOutOfRange(CommandEntry &command) const
+ {
+ return its.collectionOutOfRange(bits(command.raw[2], 15, 0));
+ }
+};
+
+#endif