diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/dev/arm/Gic.py | 18 | ||||
-rw-r--r-- | src/dev/arm/RealView.py | 5 | ||||
-rw-r--r-- | src/dev/arm/SConscript | 2 | ||||
-rw-r--r-- | src/dev/arm/gic_v3.cc | 3 | ||||
-rw-r--r-- | src/dev/arm/gic_v3.hh | 2 | ||||
-rw-r--r-- | src/dev/arm/gic_v3_its.cc | 1213 | ||||
-rw-r--r-- | src/dev/arm/gic_v3_its.hh | 518 | ||||
-rw-r--r-- | src/dev/arm/gic_v3_redistributor.hh | 2 |
8 files changed, 1759 insertions, 4 deletions
diff --git a/src/dev/arm/Gic.py b/src/dev/arm/Gic.py index 011e238cc..d31a582d5 100644 --- a/src/dev/arm/Gic.py +++ b/src/dev/arm/Gic.py @@ -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") diff --git a/src/dev/arm/RealView.py b/src/dev/arm/RealView.py index 186d6df41..b34ab006c 100644 --- a/src/dev/arm/RealView.py +++ b/src/dev/arm/RealView.py @@ -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): diff --git a/src/dev/arm/SConscript b/src/dev/arm/SConscript index c4aa52180..7d14abe67 100644 --- a/src/dev/arm/SConscript +++ b/src/dev/arm/SConscript @@ -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') diff --git a/src/dev/arm/gic_v3.cc b/src/dev/arm/gic_v3.cc index 9004f656f..6f4312b03 100644 --- a/src/dev/arm/gic_v3.cc +++ b/src/dev/arm/gic_v3.cc @@ -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(); } diff --git a/src/dev/arm/gic_v3.hh b/src/dev/arm/gic_v3.hh index 5a13a7479..7dab5a2fb 100644 --- a/src/dev/arm/gic_v3.hh +++ b/src/dev/arm/gic_v3.hh @@ -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 index 000000000..f3fa0a50e --- /dev/null +++ b/src/dev/arm/gic_v3_its.cc @@ -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 index 000000000..aa0b8c805 --- /dev/null +++ b/src/dev/arm/gic_v3_its.hh @@ -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 diff --git a/src/dev/arm/gic_v3_redistributor.hh b/src/dev/arm/gic_v3_redistributor.hh index 8d7de3d7a..29ff8672d 100644 --- a/src/dev/arm/gic_v3_redistributor.hh +++ b/src/dev/arm/gic_v3_redistributor.hh @@ -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: |