blob: a3bb6726da3bb28a8262fcd82c4912e9cc3d2e56 [file] [log] [blame]
/*
* 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.
*/
#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, 0x0140);
const uint32_t Gicv3Its::CTLR_QUIESCENT = 0x80000000;
ItsProcess::ItsProcess(Gicv3Its &_its)
: its(_its), coroutine(nullptr)
{
}
ItsProcess::~ItsProcess()
{
}
void
ItsProcess::reinit()
{
coroutine.reset(new Coroutine(
std::bind(&ItsProcess::main, this, std::placeholders::_1)));
}
const std::string
ItsProcess::name() const
{
return its.name();
}
ItsAction
ItsProcess::run(PacketPtr pkt)
{
assert(coroutine != nullptr);
assert(*coroutine);
return (*coroutine)(pkt).get();
}
void
ItsProcess::doRead(Yield &yield, Addr addr, void *ptr, size_t size)
{
ItsAction a;
a.type = ItsActionType::SEND_REQ;
RequestPtr req = std::make_shared<Request>(
addr, size, 0, its.requestorId);
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.requestorId);
req->taskId(ContextSwitchTaskId::DMA);
a.pkt = new Packet(req, MemCmd::WriteReq);
a.pkt->dataStatic(ptr);
a.delay = 0;
PacketPtr pkt = yield(a).get();
assert(pkt);
assert(pkt->getSize() >= size);
delete pkt;
}
void
ItsProcess::terminate(Yield &yield)
{
ItsAction a;
a.type = ItsActionType::TERMINATE;
a.pkt = NULL;
a.delay = 0;
yield(a);
}
void
ItsProcess::writeDeviceTable(Yield &yield, uint32_t device_id, DTE dte)
{
const Addr base = its.pageAddress(Gicv3Its::DEVICE_TABLE);
const Addr address = base + (device_id * sizeof(dte));
DPRINTF(ITS, "Writing DTE at address %#x: %#x\n", address, dte);
doWrite(yield, address, &dte, sizeof(dte));
}
void
ItsProcess::writeIrqTranslationTable(
Yield &yield, const Addr itt_base, uint32_t event_id, ITTE itte)
{
const Addr address = itt_base + (event_id * sizeof(itte));
doWrite(yield, address, &itte, sizeof(itte));
DPRINTF(ITS, "Writing ITTE at address %#x: %#x\n", address, itte);
}
void
ItsProcess::writeIrqCollectionTable(
Yield &yield, uint32_t collection_id, CTE cte)
{
const Addr base = its.pageAddress(Gicv3Its::COLLECTION_TABLE);
const Addr address = base + (collection_id * sizeof(cte));
doWrite(yield, address, &cte, sizeof(cte));
DPRINTF(ITS, "Writing CTE at address %#x: %#x\n", address, cte);
}
uint64_t
ItsProcess::readDeviceTable(Yield &yield, uint32_t device_id)
{
uint64_t dte;
const Addr base = its.pageAddress(Gicv3Its::DEVICE_TABLE);
const Addr address = base + (device_id * sizeof(dte));
doRead(yield, address, &dte, sizeof(dte));
DPRINTF(ITS, "Reading DTE at address %#x: %#x\n", address, dte);
return dte;
}
uint64_t
ItsProcess::readIrqTranslationTable(
Yield &yield, const Addr itt_base, uint32_t event_id)
{
uint64_t itte;
const Addr address = itt_base + (event_id * sizeof(itte));
doRead(yield, address, &itte, sizeof(itte));
DPRINTF(ITS, "Reading ITTE at address %#x: %#x\n", address, itte);
return itte;
}
uint64_t
ItsProcess::readIrqCollectionTable(Yield &yield, uint32_t collection_id)
{
uint64_t cte;
const Addr base = its.pageAddress(Gicv3Its::COLLECTION_TABLE);
const Addr address = base + (collection_id * sizeof(cte));
doRead(yield, address, &cte, sizeof(cte));
DPRINTF(ITS, "Reading CTE at address %#x: %#x\n", address, cte);
return cte;
}
ItsTranslation::ItsTranslation(Gicv3Its &_its)
: ItsProcess(_its)
{
reinit();
its.pendingTranslations++;
its.gitsControl.quiescent = 0;
}
ItsTranslation::~ItsTranslation()
{
assert(its.pendingTranslations >= 1);
its.pendingTranslations--;
if (!its.pendingTranslations && !its.pendingCommands)
its.gitsControl.quiescent = 1;
}
void
ItsTranslation::main(Yield &yield)
{
PacketPtr pkt = yield.get();
const uint32_t device_id = pkt->req->streamId();
const uint32_t event_id = pkt->getLE<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;
its.gitsControl.quiescent = 0;
}
ItsCommand::~ItsCommand()
{
its.pendingCommands = false;
if (!its.pendingTranslations)
its.gitsControl.quiescent = 1;
}
std::string
ItsCommand::commandName(uint32_t cmd)
{
const auto entry = cmdDispatcher.find(cmd);
return entry != cmdDispatcher.end() ? entry->second.name : "INVALID";
}
void
ItsCommand::main(Yield &yield)
{
ItsAction a;
a.type = ItsActionType::INITIAL_NOP;
a.pkt = nullptr;
a.delay = 0;
yield(a);
while (its.gitsCwriter.offset != its.gitsCreadr.offset) {
CommandEntry command;
// Reading the command from CMDQ
readCommand(yield, command);
processCommand(yield, command);
its.incrementReadPointer();
}
terminate(yield);
}
void
ItsCommand::readCommand(Yield &yield, CommandEntry &command)
{
// read the command pointed by GITS_CREADR
const Addr cmd_addr =
(its.gitsCbaser.physAddr << 12) + (its.gitsCreadr.offset << 5);
doRead(yield, cmd_addr, &command, sizeof(command));
DPRINTF(ITS, "Command %s read from queue at address: %#x\n",
commandName(command.type), cmd_addr);
DPRINTF(ITS, "dw0: %#x dw1: %#x dw2: %#x dw3: %#x\n",
command.raw[0], command.raw[1], command.raw[2], command.raw[3]);
}
void
ItsCommand::processCommand(Yield &yield, CommandEntry &command)
{
const auto entry = cmdDispatcher.find(command.type);
if (entry != cmdDispatcher.end()) {
// Execute the command
entry->second.exec(this, yield, command);
} else {
panic("Unrecognized command type: %u", command.type);
}
}
void
ItsCommand::clear(Yield &yield, CommandEntry &command)
{
if (deviceOutOfRange(command)) {
its.incrementReadPointer();
terminate(yield);
}
DTE dte = readDeviceTable(yield, command.deviceId);
if (!dte.valid || idOutOfRange(command, dte)) {
its.incrementReadPointer();
terminate(yield);
}
ITTE itte = readIrqTranslationTable(
yield, dte.ittAddress, command.eventId);
if (!itte.valid) {
its.incrementReadPointer();
terminate(yield);
}
const auto collection_id = itte.icid;
CTE cte = readIrqCollectionTable(yield, collection_id);
if (!cte.valid) {
its.incrementReadPointer();
terminate(yield);
}
// Clear the LPI in the redistributor
its.getRedistributor(cte)->setClrLPI(itte.intNum, false);
}
void
ItsCommand::discard(Yield &yield, CommandEntry &command)
{
if (deviceOutOfRange(command)) {
its.incrementReadPointer();
terminate(yield);
}
DTE dte = readDeviceTable(yield, command.deviceId);
if (!dte.valid || idOutOfRange(command, dte)) {
its.incrementReadPointer();
terminate(yield);
}
ITTE itte = readIrqTranslationTable(
yield, dte.ittAddress, command.eventId);
if (!itte.valid) {
its.incrementReadPointer();
terminate(yield);
}
const auto collection_id = itte.icid;
Gicv3Its::CTE cte = readIrqCollectionTable(yield, collection_id);
if (!cte.valid) {
its.incrementReadPointer();
terminate(yield);
}
its.getRedistributor(cte)->setClrLPI(itte.intNum, false);
// Then removes the mapping from the ITT (invalidating)
itte.valid = 0;
writeIrqTranslationTable(
yield, dte.ittAddress, command.eventId, itte);
}
void
ItsCommand::doInt(Yield &yield, CommandEntry &command)
{
if (deviceOutOfRange(command)) {
its.incrementReadPointer();
terminate(yield);
}
DTE dte = readDeviceTable(yield, command.deviceId);
if (!dte.valid || idOutOfRange(command, dte)) {
its.incrementReadPointer();
terminate(yield);
}
ITTE itte = readIrqTranslationTable(
yield, dte.ittAddress, command.eventId);
if (!itte.valid) {
its.incrementReadPointer();
terminate(yield);
}
const auto collection_id = itte.icid;
CTE cte = readIrqCollectionTable(yield, collection_id);
if (!cte.valid) {
its.incrementReadPointer();
terminate(yield);
}
// Set the LPI in the redistributor
its.getRedistributor(cte)->setClrLPI(itte.intNum, true);
}
void
ItsCommand::inv(Yield &yield, CommandEntry &command)
{
if (deviceOutOfRange(command)) {
its.incrementReadPointer();
terminate(yield);
}
DTE dte = readDeviceTable(yield, command.deviceId);
if (!dte.valid || idOutOfRange(command, dte)) {
its.incrementReadPointer();
terminate(yield);
}
ITTE itte = readIrqTranslationTable(
yield, dte.ittAddress, command.eventId);
if (!itte.valid) {
its.incrementReadPointer();
terminate(yield);
}
const auto collection_id = itte.icid;
CTE cte = readIrqCollectionTable(yield, collection_id);
if (!cte.valid) {
its.incrementReadPointer();
terminate(yield);
}
// Do nothing since caching is currently not supported in
// Redistributor
}
void
ItsCommand::invall(Yield &yield, CommandEntry &command)
{
if (collectionOutOfRange(command)) {
its.incrementReadPointer();
terminate(yield);
}
const auto icid = bits(command.raw[2], 15, 0);
CTE cte = readIrqCollectionTable(yield, icid);
if (!cte.valid) {
its.incrementReadPointer();
terminate(yield);
}
// Do nothing since caching is currently not supported in
// Redistributor
}
void
ItsCommand::mapc(Yield &yield, CommandEntry &command)
{
if (collectionOutOfRange(command)) {
its.incrementReadPointer();
terminate(yield);
}
CTE cte = 0;
cte.valid = bits(command.raw[2], 63);
cte.rdBase = bits(command.raw[2], 50, 16);
const auto icid = bits(command.raw[2], 15, 0);
writeIrqCollectionTable(yield, icid, cte);
}
void
ItsCommand::mapd(Yield &yield, CommandEntry &command)
{
if (deviceOutOfRange(command) || sizeOutOfRange(command)) {
its.incrementReadPointer();
terminate(yield);
}
DTE dte = 0;
dte.valid = bits(command.raw[2], 63);
dte.ittAddress = mbits(command.raw[2], 51, 8);
dte.ittRange = bits(command.raw[1], 4, 0);
writeDeviceTable(yield, command.deviceId, dte);
}
void
ItsCommand::mapi(Yield &yield, CommandEntry &command)
{
if (deviceOutOfRange(command)) {
its.incrementReadPointer();
terminate(yield);
}
if (collectionOutOfRange(command)) {
its.incrementReadPointer();
terminate(yield);
}
DTE dte = readDeviceTable(yield, command.deviceId);
if (!dte.valid || idOutOfRange(command, dte) ||
its.lpiOutOfRange(command.eventId)) {
its.incrementReadPointer();
terminate(yield);
}
Gicv3Its::ITTE itte = readIrqTranslationTable(
yield, dte.ittAddress, command.eventId);
itte.valid = 1;
itte.intType = Gicv3Its::PHYSICAL_INTERRUPT;
itte.intNum = command.eventId;
itte.icid = bits(command.raw[2], 15, 0);
writeIrqTranslationTable(
yield, dte.ittAddress, command.eventId, itte);
}
void
ItsCommand::mapti(Yield &yield, CommandEntry &command)
{
if (deviceOutOfRange(command)) {
its.incrementReadPointer();
terminate(yield);
}
if (collectionOutOfRange(command)) {
its.incrementReadPointer();
terminate(yield);
}
DTE dte = readDeviceTable(yield, command.deviceId);
const auto pintid = bits(command.raw[1], 63, 32);
if (!dte.valid || idOutOfRange(command, dte) ||
its.lpiOutOfRange(pintid)) {
its.incrementReadPointer();
terminate(yield);
}
ITTE itte = readIrqTranslationTable(
yield, dte.ittAddress, command.eventId);
itte.valid = 1;
itte.intType = Gicv3Its::PHYSICAL_INTERRUPT;
itte.intNum = pintid;
itte.icid = bits(command.raw[2], 15, 0);
writeIrqTranslationTable(
yield, dte.ittAddress, command.eventId, itte);
}
void
ItsCommand::movall(Yield &yield, CommandEntry &command)
{
const uint64_t rd1 = bits(command.raw[2], 50, 16);
const uint64_t rd2 = bits(command.raw[3], 50, 16);
if (rd1 != rd2) {
Gicv3Redistributor * redist1 = its.getRedistributor(rd1);
Gicv3Redistributor * redist2 = its.getRedistributor(rd2);
its.moveAllPendingState(redist1, redist2);
}
}
void
ItsCommand::movi(Yield &yield, CommandEntry &command)
{
if (deviceOutOfRange(command)) {
its.incrementReadPointer();
terminate(yield);
}
if (collectionOutOfRange(command)) {
its.incrementReadPointer();
terminate(yield);
}
DTE dte = readDeviceTable(yield, command.deviceId);
if (!dte.valid || idOutOfRange(command, dte)) {
its.incrementReadPointer();
terminate(yield);
}
ITTE itte = readIrqTranslationTable(
yield, dte.ittAddress, command.eventId);
if (!itte.valid || itte.intType == Gicv3Its::VIRTUAL_INTERRUPT) {
its.incrementReadPointer();
terminate(yield);
}
const auto collection_id1 = itte.icid;
CTE cte1 = readIrqCollectionTable(yield, collection_id1);
if (!cte1.valid) {
its.incrementReadPointer();
terminate(yield);
}
const auto collection_id2 = bits(command.raw[2], 15, 0);
CTE cte2 = readIrqCollectionTable(yield, collection_id2);
if (!cte2.valid) {
its.incrementReadPointer();
terminate(yield);
}
Gicv3Redistributor *first_redist = its.getRedistributor(cte1);
Gicv3Redistributor *second_redist = its.getRedistributor(cte2);
if (second_redist != first_redist) {
// move pending state of the interrupt from one redistributor
// to the other.
if (first_redist->isPendingLPI(itte.intNum)) {
first_redist->setClrLPI(itte.intNum, false);
second_redist->setClrLPI(itte.intNum, true);
}
}
itte.icid = collection_id2;
writeIrqTranslationTable(
yield, dte.ittAddress, command.eventId, itte);
}
void
ItsCommand::sync(Yield &yield, CommandEntry &command)
{
warn("ITS %s command unimplemented", __func__);
}
void
ItsCommand::vinvall(Yield &yield, CommandEntry &command)
{
panic("ITS %s command unimplemented", __func__);
}
void
ItsCommand::vmapi(Yield &yield, CommandEntry &command)
{
panic("ITS %s command unimplemented", __func__);
}
void
ItsCommand::vmapp(Yield &yield, CommandEntry &command)
{
panic("ITS %s command unimplemented", __func__);
}
void
ItsCommand::vmapti(Yield &yield, CommandEntry &command)
{
panic("ITS %s command unimplemented", __func__);
}
void
ItsCommand::vmovi(Yield &yield, CommandEntry &command)
{
panic("ITS %s command unimplemented", __func__);
}
void
ItsCommand::vmovp(Yield &yield, CommandEntry &command)
{
panic("ITS %s command unimplemented", __func__);
}
void
ItsCommand::vsync(Yield &yield, CommandEntry &command)
{
panic("ITS %s command unimplemented", __func__);
}
Gicv3Its::Gicv3Its(const Gicv3ItsParams *params)
: BasicPioDevice(params, params->pio_size),
dmaPort(name() + ".dma", *this),
gitsControl(CTLR_QUIESCENT),
gitsTyper(params->gits_typer),
gitsCbaser(0), gitsCreadr(0),
gitsCwriter(0), gitsIidr(0),
tableBases(NUM_BASER_REGS, 0),
requestorId(params->system->getRequestorId(this)),
gic(nullptr),
commandEvent([this] { checkCommandQueue(); }, name()),
pendingCommands(false),
pendingTranslations(0)
{
BASER device_baser = 0;
device_baser.type = DEVICE_TABLE;
device_baser.entrySize = sizeof(uint64_t) - 1;
tableBases[0] = device_baser;
BASER icollect_baser = 0;
icollect_baser.type = COLLECTION_TABLE;
icollect_baser.entrySize = sizeof(uint64_t) - 1;
tableBases[1] = icollect_baser;
}
void
Gicv3Its::setGIC(Gicv3 *_gic)
{
assert(!gic);
gic = _gic;
}
AddrRangeList
Gicv3Its::getAddrRanges() const
{
assert(pioSize != 0);
AddrRangeList ranges;
DPRINTF(AddrRanges, "registering range: %#x-%#x\n", pioAddr, pioSize);
ranges.push_back(RangeSize(pioAddr, pioSize));
return ranges;
}
Tick
Gicv3Its::read(PacketPtr pkt)
{
const Addr addr = pkt->getAddr() - pioAddr;
uint64_t value = 0;
DPRINTF(GIC, "%s register at addr: %#x\n", __func__, addr);
switch (addr) {
case GITS_CTLR:
value = gitsControl;
break;
case GITS_IIDR:
value = gitsIidr;
break;
case GITS_TYPER:
value = gitsTyper;
break;
case GITS_TYPER + 4:
value = gitsTyper.high;
break;
case GITS_CBASER:
value = gitsCbaser;
break;
case GITS_CBASER + 4:
value = gitsCbaser.high;
break;
case GITS_CWRITER:
value = gitsCwriter;
break;
case GITS_CWRITER + 4:
value = gitsCwriter.high;
break;
case GITS_CREADR:
value = gitsCreadr;
break;
case GITS_CREADR + 4:
value = gitsCreadr.high;
break;
case GITS_PIDR2:
value = gic->getDistributor()->gicdPidr2;
break;
case GITS_TRANSLATER:
value = gitsTranslater;
break;
default:
if (GITS_BASER.contains(addr)) {
auto relative_addr = addr - GITS_BASER.start();
auto baser_index = relative_addr / sizeof(uint64_t);
value = tableBases[baser_index];
break;
} else {
panic("Unrecognized register access\n");
}
}
pkt->setUintX(value, ByteOrder::little);
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>() & ~CTLR_QUIESCENT);
// We should check here if the ITS has been disabled, and if
// that's the case, flush GICv3 caches to external memory.
// This is not happening now, since LPI caching is not
// currently implemented in gem5.
break;
case GITS_IIDR:
panic("GITS_IIDR is Read Only\n");
case GITS_TYPER:
panic("GITS_TYPER is Read Only\n");
case GITS_CBASER:
if (pkt->getSize() == sizeof(uint32_t)) {
gitsCbaser.low = pkt->getLE<uint32_t>();
} else {
assert(pkt->getSize() == sizeof(uint64_t));
gitsCbaser = pkt->getLE<uint64_t>();
}
gitsCreadr = 0; // Cleared when CBASER gets written
checkCommandQueue();
break;
case GITS_CBASER + 4:
assert(pkt->getSize() == sizeof(uint32_t));
gitsCbaser.high = pkt->getLE<uint32_t>();
gitsCreadr = 0; // Cleared when CBASER gets written
checkCommandQueue();
break;
case GITS_CWRITER:
if (pkt->getSize() == sizeof(uint32_t)) {
gitsCwriter.low = pkt->getLE<uint32_t>();
} else {
assert(pkt->getSize() == sizeof(uint64_t));
gitsCwriter = pkt->getLE<uint64_t>();
}
checkCommandQueue();
break;
case GITS_CWRITER + 4:
assert(pkt->getSize() == sizeof(uint32_t));
gitsCwriter.high = pkt->getLE<uint32_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);
const uint64_t table_base = tableBases[baser_index];
const uint64_t w_mask = tableBases[baser_index].type ?
BASER_WMASK : BASER_WMASK_UNIMPL;
const uint64_t val = pkt->getLE<uint64_t>() & w_mask;
tableBases[baser_index] = table_base | val;
break;
} else {
panic("Unrecognized register access\n");
}
}
pkt->makeAtomicResponse();
return pioDelay;
}
bool
Gicv3Its::idOutOfRange(uint32_t event_id, uint8_t itt_range) const
{
const uint32_t id_bits = gitsTyper.idBits;
return event_id >= (1ULL << (id_bits + 1)) ||
event_id >= ((1ULL << itt_range) + 1);
}
bool
Gicv3Its::deviceOutOfRange(uint32_t device_id) const
{
return device_id >= (1ULL << (gitsTyper.devBits + 1));
}
bool
Gicv3Its::sizeOutOfRange(uint32_t size) const
{
return size > gitsTyper.idBits;
}
bool
Gicv3Its::collectionOutOfRange(uint32_t collection_id) const
{
// If GITS_TYPER.CIL == 0, ITS supports 16-bit CollectionID
// Otherwise, #bits is specified by GITS_TYPER.CIDbits
const auto cid_bits = gitsTyper.cil == 0 ?
16 : gitsTyper.cidBits + 1;
return collection_id >= (1ULL << cid_bits);
}
bool
Gicv3Its::lpiOutOfRange(uint32_t intid) const
{
return intid >= (1ULL << (Gicv3Distributor::IDBITS + 1)) ||
(intid < Gicv3Redistributor::SMALLEST_LPI_ID &&
intid != Gicv3::INTID_SPURIOUS);
}
DrainState
Gicv3Its::drain()
{
if (!pendingCommands && !pendingTranslations) {
return DrainState::Drained;
} else {
DPRINTF(Drain, "GICv3 ITS not drained\n");
return DrainState::Draining;
}
}
void
Gicv3Its::serialize(CheckpointOut & cp) const
{
SERIALIZE_SCALAR(gitsControl);
SERIALIZE_SCALAR(gitsTyper);
SERIALIZE_SCALAR(gitsCbaser);
SERIALIZE_SCALAR(gitsCreadr);
SERIALIZE_SCALAR(gitsCwriter);
SERIALIZE_SCALAR(gitsIidr);
SERIALIZE_CONTAINER(tableBases);
}
void
Gicv3Its::unserialize(CheckpointIn & cp)
{
UNSERIALIZE_SCALAR(gitsControl);
UNSERIALIZE_SCALAR(gitsTyper);
UNSERIALIZE_SCALAR(gitsCbaser);
UNSERIALIZE_SCALAR(gitsCreadr);
UNSERIALIZE_SCALAR(gitsCwriter);
UNSERIALIZE_SCALAR(gitsIidr);
UNSERIALIZE_CONTAINER(tableBases);
}
void
Gicv3Its::incrementReadPointer()
{
// Make the reader point to the next element
gitsCreadr.offset = gitsCreadr.offset + 1;
// Check for wrapping
if (gitsCreadr.offset == maxCommands()) {
gitsCreadr.offset = 0;
}
}
uint64_t
Gicv3Its::maxCommands() const
{
return (4096 * (gitsCbaser.size + 1)) / sizeof(ItsCommand::CommandEntry);
}
void
Gicv3Its::checkCommandQueue()
{
if (!gitsControl.enabled || !gitsCbaser.valid)
return;
// If GITS_CWRITER gets set by sw to a value bigger than the
// allowed one, the command queue should stop processing commands
// until the register gets reset to an allowed one
if (gitsCwriter.offset >= maxCommands()) {
return;
}
if (gitsCwriter.offset != gitsCreadr.offset) {
// writer and reader pointing to different command
// entries: queue not empty.
DPRINTF(ITS, "Reading command from queue\n");
if (!pendingCommands) {
auto *cmd_proc = new ItsCommand(*this);
runProcess(cmd_proc, nullptr);
} else {
DPRINTF(ITS, "Waiting for pending command to finish\n");
}
}
}
Port &
Gicv3Its::getPort(const std::string &if_name, PortID idx)
{
if (if_name == "dma") {
return dmaPort;
}
return BasicPioDevice::getPort(if_name, idx);
}
void
Gicv3Its::recvReqRetry()
{
assert(!packetsToRetry.empty());
while (!packetsToRetry.empty()) {
ItsAction a = packetsToRetry.front();
assert(a.type == ItsActionType::SEND_REQ);
if (!dmaPort.sendTimingReq(a.pkt))
break;
packetsToRetry.pop();
}
}
bool
Gicv3Its::recvTimingResp(PacketPtr pkt)
{
// @todo: We need to pay for this and not just zero it out
pkt->headerDelay = pkt->payloadDelay = 0;
ItsProcess *proc =
safe_cast<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)
{
auto base_it = std::find_if(
tableBases.begin(), tableBases.end(),
[table] (const BASER &b) { return b.type == table; }
);
panic_if(base_it == tableBases.end(),
"ITS Table not recognised\n");
const BASER base = *base_it;
// real address depends on page size
switch (base.pageSize) {
case SIZE_4K:
case SIZE_16K:
return mbits(base, 47, 12);
case SIZE_64K:
return mbits(base, 47, 16) | (bits(base, 15, 12) << 48);
default:
panic("Unsupported page size\n");
}
}
void
Gicv3Its::moveAllPendingState(
Gicv3Redistributor *rd1, Gicv3Redistributor *rd2)
{
const uint64_t largest_lpi_id = 1ULL << (rd1->lpiIDBits + 1);
uint8_t lpi_pending_table[largest_lpi_id / 8];
// Copying the pending table from redistributor 1 to redistributor 2
rd1->memProxy->readBlob(
rd1->lpiPendingTablePtr, (uint8_t *)lpi_pending_table,
sizeof(lpi_pending_table));
rd2->memProxy->writeBlob(
rd2->lpiPendingTablePtr, (uint8_t *)lpi_pending_table,
sizeof(lpi_pending_table));
// Clearing pending table in redistributor 2
rd1->memProxy->memsetBlob(
rd1->lpiPendingTablePtr,
0, sizeof(lpi_pending_table));
rd2->updateDistributor();
}
Gicv3Its *
Gicv3ItsParams::create()
{
return new Gicv3Its(this);
}