dev-arm: Provide a GICv3 ITS Implementation
authorGiacomo Travaglini <giacomo.travaglini@arm.com>
Tue, 16 Apr 2019 08:20:22 +0000 (09:20 +0100)
committerGiacomo Travaglini <giacomo.travaglini@arm.com>
Wed, 22 May 2019 15:17:15 +0000 (15:17 +0000)
This patch introduces the GICv3 ITS module, which is in charge of
translating MSIs into physical (GICv3) and virtual (GICv4) LPIs.  The
patch is only GICv3 compliant, which means that there is no direct
virtual LPI injection (this also means V* commands are unimplemented)
Other missing features are:

* No 2level ITS tables (only flat table supported)

* Command errors: when there is an error in the ITS, it is
IMPLEMENTATION DEFINED on how the ITS behaves.  There are three possible
scenarios (see GICv3 TRM) and this implementation only supports one of
these (which is, aborting the command and jumping to the next one).
Furter patches could make it possible to select different reactions

* Invalidation commands (INV, INVALL) are only doing the memory table
walks, assuming the current Gicv3Redistributor is not caching any
configuration table entry.

Change-Id: If4ae9267ac1de7b20a04986a2af3ca3109743211
Signed-off-by: Giacomo Travaglini <giacomo.travaglini@arm.com>
Reviewed-by: Andreas Sandberg <andreas.sandberg@arm.com>
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/18601
Maintainer: Andreas Sandberg <andreas.sandberg@arm.com>
Tested-by: kokoro <noreply+kokoro@google.com>
src/dev/arm/Gic.py
src/dev/arm/RealView.py
src/dev/arm/SConscript
src/dev/arm/gic_v3.cc
src/dev/arm/gic_v3.hh
src/dev/arm/gic_v3_its.cc [new file with mode: 0644]
src/dev/arm/gic_v3_its.hh [new file with mode: 0644]
src/dev/arm/gic_v3_redistributor.hh

index 011e238cc05f6f7d2a6479e8f46642aa0794b247..d31a582d54a983c47bb57c5c8be965dc27b59ade 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2012-2013, 2017-2018 ARM Limited
+# Copyright (c) 2012-2013, 2017-2019 ARM Limited
 # All rights reserved.
 #
 # The license below extends only to copyright in the software and shall
@@ -40,7 +40,7 @@ from m5.proxy import *
 from m5.util.fdthelper import *
 from m5.SimObject import SimObject
 
-from m5.objects.Device import PioDevice
+from m5.objects.Device import PioDevice, BasicPioDevice
 from m5.objects.Platform import Platform
 
 class BaseGic(PioDevice):
@@ -162,10 +162,24 @@ class VGic(PioDevice):
 
         yield node
 
+class Gicv3Its(BasicPioDevice):
+    type = 'Gicv3Its'
+    cxx_header = "dev/arm/gic_v3_its.hh"
+
+    dma = MasterPort("DMA port")
+    pio_size = Param.Unsigned(0x20000, "Gicv3Its pio size")
+
+    # CIL [36] = 0: ITS supports 16-bit CollectionID
+    # Devbits [17:13] = 0b100011: ITS supports 23 DeviceID bits
+    # ID_bits [12:8] = 0b11111: ITS supports 31 EventID bits
+    gits_typer = Param.UInt64(0x30023F01, "GITS_TYPER RO value")
+
 class Gicv3(BaseGic):
     type = 'Gicv3'
     cxx_header = "dev/arm/gic_v3.hh"
 
+    its = Param.Gicv3Its(Gicv3Its(), "GICv3 Interrupt Translation Service")
+
     dist_addr = Param.Addr("Address for distributor")
     dist_pio_delay = Param.Latency('10ns', "Delay for PIO r/w to distributor")
     redist_addr = Param.Addr("Address for redistributors")
index 186d6df415e72d123148ed650122865601e2bcc2..b34ab006cdad6d04af9cd46173cfa15afa5e4a0b 100644 (file)
@@ -1084,14 +1084,15 @@ class VExpress_GEM5_V1(VExpress_GEM5_V1_Base):
 
 class VExpress_GEM5_V2_Base(VExpress_GEM5_Base):
     gic = Gicv3(dist_addr=0x2c000000, redist_addr=0x2c010000,
-                maint_int=ArmPPI(num=25))
+                maint_int=ArmPPI(num=25),
+                its=Gicv3Its(pio_addr=0x2c120000))
 
     # Limiting to 128 since it will otherwise overlap with PCI space
     gic.cpu_max = 128
 
     def _on_chip_devices(self):
         return super(VExpress_GEM5_V2_Base,self)._on_chip_devices() + [
-                self.gic,
+                self.gic, self.gic.its
             ]
 
     def setupBootLoader(self, mem_bus, cur_sys, loc):
index c4aa5218014cd33591fdb92db2a4d000b0d64349..7d14abe67b27ff1f5a2b6982b5f6b3485f134e07 100644 (file)
@@ -61,6 +61,7 @@ if env['TARGET_ISA'] == 'arm':
     Source('gic_v3_cpu_interface.cc')
     Source('gic_v3_distributor.cc')
     Source('gic_v3_redistributor.cc')
+    Source('gic_v3_its.cc')
     Source('pl011.cc')
     Source('pl111.cc')
     Source('hdlcd.cc')
@@ -85,6 +86,7 @@ if env['TARGET_ISA'] == 'arm':
     DebugFlag('GICV2M')
     DebugFlag('Pl050')
     DebugFlag('GIC')
+    DebugFlag('ITS')
     DebugFlag('RVCTRL')
     DebugFlag('EnergyCtrl')
     DebugFlag('UFSHostDevice')
index 9004f656f0f5e8a58ad115cf7774b6cf541b98d7..6f4312b038dc524bf7266f27847efc54a7c37cf6 100644 (file)
@@ -35,6 +35,7 @@
 #include "debug/Interrupt.hh"
 #include "dev/arm/gic_v3_cpu_interface.hh"
 #include "dev/arm/gic_v3_distributor.hh"
+#include "dev/arm/gic_v3_its.hh"
 #include "dev/arm/gic_v3_redistributor.hh"
 #include "dev/platform.hh"
 #include "mem/packet.hh"
@@ -78,6 +79,8 @@ Gicv3::init()
         cpuInterfaces[i]->init();
     }
 
+    params()->its->setGIC(this);
+
     BaseGic::init();
 }
 
index 5a13a74793d40fc8d31cca584a3acc69e9ff627c..7dab5a2fbc16e75acdc16de192ec6ad06db3c088 100644 (file)
@@ -37,6 +37,7 @@
 class Gicv3CPUInterface;
 class Gicv3Distributor;
 class Gicv3Redistributor;
+class Gicv3Its;
 
 class Gicv3 : public BaseGic
 {
@@ -48,6 +49,7 @@ class Gicv3 : public BaseGic
     Gicv3Distributor * distributor;
     std::vector<Gicv3Redistributor *> redistributors;
     std::vector<Gicv3CPUInterface *> cpuInterfaces;
+    Gicv3Its * its;
     AddrRange distRange;
     AddrRange redistRange;
     AddrRangeList addrRanges;
diff --git a/src/dev/arm/gic_v3_its.cc b/src/dev/arm/gic_v3_its.cc
new file mode 100644 (file)
index 0000000..f3fa0a5
--- /dev/null
@@ -0,0 +1,1213 @@
+/*
+ * 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);
+}
diff --git a/src/dev/arm/gic_v3_its.hh b/src/dev/arm/gic_v3_its.hh
new file mode 100644 (file)
index 0000000..aa0b8c8
--- /dev/null
@@ -0,0 +1,518 @@
+/*
+ * 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
index 8d7de3d7a366ca9c78554aec6ae5ca4d6fa49e44..29ff8672d5e6f677cf9e4b55a445c53455105977 100644 (file)
@@ -37,6 +37,7 @@
 
 class Gicv3CPUInterface;
 class Gicv3Distributor;
+class Gicv3Its;
 
 class Gicv3Redistributor : public Serializable
 {
@@ -44,6 +45,7 @@ class Gicv3Redistributor : public Serializable
 
     friend class Gicv3CPUInterface;
     friend class Gicv3Distributor;
+    friend class Gicv3Its;
 
   protected: