dev-arm: Rewrite GICv3 update

The GICv3 update methods are method which are invoked anytime the model
needs to evaluate a change in its state, which most of the time means
managing the state of an interrupt (forwarding it to a PE, deasserting
it, etc).
The way it is currently done is a little bit obscure and doesn't
handle correctly IRQ prioritization.
Example:
An IRQ which is handled by the redistributor (PPI or LPI) was not
competing with any pending interrupts coming from the distributor (SPIs)
once raised by a peripheral.

Also the way the pending state of an interrupt was removed at the
cpu interface level wasn't happening in place where this was actually
happening (E.g. when activating it), but happened with a weird
fullUpdate semantic, where if there was a pending interrupt in a
cpu interface, all cpu interfaces had their pending interrupt (if any)
been disabled.

With this patch, state update always starts at the distributor, and
it goes down until the cpu interface where a Gicv3CPUInterface::update
method selects the winning interrupt coming from distributor/redistributor
to be forwarded to the PE.

Change-Id: I1c517cbc4bf107cc2d7ae7beb2692e3cf5187a40
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/+/20614
Maintainer: Andreas Sandberg <andreas.sandberg@arm.com>
Tested-by: kokoro <noreply+kokoro@google.com>
This commit is contained in:
Giacomo Travaglini
2019-08-27 11:38:55 +01:00
parent c1458b5d58
commit 3093d65ad3
7 changed files with 80 additions and 98 deletions

View File

@@ -1819,15 +1819,19 @@ Gicv3CPUInterface::activateIRQ(uint32_t int_id, Gicv3::GroupId group)
if (int_id < Gicv3::SGI_MAX + Gicv3::PPI_MAX) {
// SGI or PPI, redistributor
redistributor->activateIRQ(int_id);
redistributor->updateAndInformCPUInterface();
} else if (int_id < Gicv3::INTID_SECURE) {
// SPI, distributor
distributor->activateIRQ(int_id);
distributor->updateAndInformCPUInterfaces();
} else if (int_id >= Gicv3Redistributor::SMALLEST_LPI_ID) {
// LPI, Redistributor
redistributor->setClrLPI(int_id, false);
}
// By setting the priority to 0xff we are effectively
// making the int_id not pending anymore at the cpu
// interface.
hppi.prio = 0xff;
updateDistributor();
}
void
@@ -1857,15 +1861,12 @@ Gicv3CPUInterface::deactivateIRQ(uint32_t int_id, Gicv3::GroupId group)
if (int_id < Gicv3::SGI_MAX + Gicv3::PPI_MAX) {
// SGI or PPI, redistributor
redistributor->deactivateIRQ(int_id);
redistributor->updateAndInformCPUInterface();
} else if (int_id < Gicv3::INTID_SECURE) {
// SPI, distributor
distributor->deactivateIRQ(int_id);
distributor->updateAndInformCPUInterfaces();
} else {
// LPI, redistributor, shouldn't deactivate
redistributor->updateAndInformCPUInterface();
}
updateDistributor();
}
void
@@ -1991,6 +1992,12 @@ Gicv3CPUInterface::highestActiveGroup() const
return -1;
}
void
Gicv3CPUInterface::updateDistributor()
{
distributor->update();
}
void
Gicv3CPUInterface::update()
{

View File

@@ -326,6 +326,7 @@ class Gicv3CPUInterface : public ArmISA::BaseISADevice, public Serializable
void serialize(CheckpointOut & cp) const override;
void unserialize(CheckpointIn & cp) override;
void update();
void updateDistributor();
void virtualActivateIRQ(uint32_t lrIdx);
void virtualDeactivateIRQ(int lrIdx);
uint8_t virtualDropPriority();

View File

@@ -638,7 +638,7 @@ Gicv3Distributor::write(Addr addr, uint64_t data, size_t size,
}
}
updateAndInformCPUInterfaces();
update();
return;
} else if (GICD_ICPENDR.contains(addr)) {
// Interrupt Clear-Pending Registers
@@ -663,10 +663,11 @@ Gicv3Distributor::write(Addr addr, uint64_t data, size_t size,
if (clear) {
irqPending[int_id] = false;
clearIrqCpuInterface(int_id);
}
}
updateAndInformCPUInterfaces();
update();
return;
} else if (GICD_ISACTIVER.contains(addr)) {
// Interrupt Set-Active Registers
@@ -947,7 +948,7 @@ Gicv3Distributor::sendInt(uint32_t int_id)
irqPending[int_id] = true;
DPRINTF(GIC, "Gicv3Distributor::sendInt(): "
"int_id %d (SPI) pending bit set\n", int_id);
updateAndInformCPUInterfaces();
update();
}
void
@@ -956,40 +957,59 @@ Gicv3Distributor::deassertSPI(uint32_t int_id)
panic_if(int_id < Gicv3::SGI_MAX + Gicv3::PPI_MAX, "Invalid SPI!");
panic_if(int_id > itLines, "Invalid SPI!");
irqPending[int_id] = false;
updateAndInformCPUInterfaces();
clearIrqCpuInterface(int_id);
update();
}
void
Gicv3Distributor::updateAndInformCPUInterfaces()
Gicv3CPUInterface*
Gicv3Distributor::route(uint32_t int_id)
{
update();
IROUTER affinity_routing = irqAffinityRouting[int_id];
Gicv3Redistributor * target_redistributor = nullptr;
for (int i = 0; i < gic->getSystem()->numContexts(); i++) {
gic->getCPUInterface(i)->update();
const Gicv3::GroupId int_group = getIntGroup(int_id);
if (affinity_routing.IRM) {
// Interrupts routed to any PE defined as a participating node
for (int i = 0; i < gic->getSystem()->numContexts(); i++) {
Gicv3Redistributor * redistributor_i =
gic->getRedistributor(i);
if (redistributor_i->
canBeSelectedFor1toNInterrupt(int_group)) {
target_redistributor = redistributor_i;
break;
}
}
} else {
uint32_t affinity = (affinity_routing.Aff3 << 24) |
(affinity_routing.Aff2 << 16) |
(affinity_routing.Aff1 << 8) |
(affinity_routing.Aff0 << 0);
target_redistributor =
gic->getRedistributorByAffinity(affinity);
}
if (!target_redistributor) {
// Interrrupts targeting not present cpus must remain pending
return nullptr;
} else {
return target_redistributor->getCPUInterface();
}
}
void
Gicv3Distributor::fullUpdate()
Gicv3Distributor::clearIrqCpuInterface(uint32_t int_id)
{
for (int i = 0; i < gic->getSystem()->numContexts(); i++) {
Gicv3CPUInterface * cpu_interface_i = gic->getCPUInterface(i);
cpu_interface_i->hppi.prio = 0xff;
}
update();
for (int i = 0; i < gic->getSystem()->numContexts(); i++) {
Gicv3Redistributor * redistributor_i = gic->getRedistributor(i);
redistributor_i->update();
}
auto cpu_interface = route(int_id);
if (cpu_interface)
cpu_interface->hppi.prio = 0xff;
}
void
Gicv3Distributor::update()
{
std::vector<bool> new_hppi(gic->getSystem()->numContexts(), false);
// Find the highest priority pending SPI
for (int int_id = Gicv3::SGI_MAX + Gicv3::PPI_MAX; int_id < itLines;
int_id++) {
@@ -998,65 +1018,27 @@ Gicv3Distributor::update()
if (irqPending[int_id] && irqEnabled[int_id] &&
!irqActive[int_id] && group_enabled) {
IROUTER affinity_routing = irqAffinityRouting[int_id];
Gicv3Redistributor * target_redistributor = nullptr;
if (affinity_routing.IRM) {
// Interrupts routed to any PE defined as a participating node
for (int i = 0; i < gic->getSystem()->numContexts(); i++) {
Gicv3Redistributor * redistributor_i =
gic->getRedistributor(i);
// Find the cpu interface where to route the interrupt
Gicv3CPUInterface *target_cpu_interface = route(int_id);
if (redistributor_i->
canBeSelectedFor1toNInterrupt(int_group)) {
target_redistributor = redistributor_i;
break;
}
}
} else {
uint32_t affinity = (affinity_routing.Aff3 << 24) |
(affinity_routing.Aff3 << 16) |
(affinity_routing.Aff1 << 8) |
(affinity_routing.Aff0 << 0);
target_redistributor =
gic->getRedistributorByAffinity(affinity);
}
if (!target_redistributor) {
// Interrrupts targeting not present cpus must remain pending
return;
}
Gicv3CPUInterface * target_cpu_interface =
target_redistributor->getCPUInterface();
uint32_t target_cpu = target_redistributor->cpuId;
// Invalid routing
if (!target_cpu_interface) continue;
if ((irqPriority[int_id] < target_cpu_interface->hppi.prio) ||
/*
* Multiple pending ints with same priority.
* Implementation choice which one to signal.
* Our implementation selects the one with the lower id.
*/
(irqPriority[int_id] == target_cpu_interface->hppi.prio &&
int_id < target_cpu_interface->hppi.intid)) {
target_cpu_interface->hppi.intid = int_id;
target_cpu_interface->hppi.prio = irqPriority[int_id];
target_cpu_interface->hppi.group = int_group;
new_hppi[target_cpu] = true;
}
}
}
// Update all redistributors
for (int i = 0; i < gic->getSystem()->numContexts(); i++) {
Gicv3Redistributor * redistributor_i = gic->getRedistributor(i);
Gicv3CPUInterface * cpu_interface_i =
redistributor_i->getCPUInterface();
if (!new_hppi[i] && cpu_interface_i->hppi.prio != 0xff &&
cpu_interface_i->hppi.intid >= (Gicv3::SGI_MAX + Gicv3::PPI_MAX) &&
cpu_interface_i->hppi.intid < Gicv3::INTID_SECURE) {
fullUpdate();
}
gic->getRedistributor(i)->update();
}
}

View File

@@ -222,13 +222,14 @@ class Gicv3Distributor : public Serializable
void serialize(CheckpointOut & cp) const override;
void unserialize(CheckpointIn & cp) override;
void update();
void updateAndInformCPUInterfaces();
Gicv3CPUInterface* route(uint32_t int_id);
public:
Gicv3Distributor(Gicv3 * gic, uint32_t it_lines);
void deassertSPI(uint32_t int_id);
void clearIrqCpuInterface(uint32_t int_id);
void init();
void initState();
uint64_t read(Addr addr, size_t size, bool is_secure_access);

View File

@@ -1271,7 +1271,7 @@ Gicv3Its::moveAllPendingState(
rd1->lpiPendingTablePtr,
0, sizeof(lpi_pending_table));
rd2->updateAndInformCPUInterface();
rd2->updateDistributor();
}
Gicv3Its *

View File

@@ -536,7 +536,7 @@ Gicv3Redistributor::write(Addr addr, uint64_t data, size_t size,
}
}
updateAndInformCPUInterface();
updateDistributor();
break;
case GICR_ICPENDR0:// Interrupt Clear-Pending Register 0
@@ -733,7 +733,7 @@ Gicv3Redistributor::sendPPInt(uint32_t int_id)
irqPending[int_id] = true;
DPRINTF(GIC, "Gicv3Redistributor::sendPPInt(): "
"int_id %d (PPI) pending bit set\n", int_id);
updateAndInformCPUInterface();
updateDistributor();
}
void
@@ -770,7 +770,7 @@ Gicv3Redistributor::sendSGI(uint32_t int_id, Gicv3::GroupId group, bool ns)
irqPending[int_id] = true;
DPRINTF(GIC, "Gicv3ReDistributor::sendSGI(): "
"int_id %d (SGI) pending bit set\n", int_id);
updateAndInformCPUInterface();
updateDistributor();
}
Gicv3::IntStatus
@@ -791,6 +791,12 @@ Gicv3Redistributor::intStatus(uint32_t int_id) const
}
}
void
Gicv3Redistributor::updateDistributor()
{
distributor->update();
}
/*
* Recalculate the highest priority pending interrupt after a
* change to redistributor state.
@@ -798,8 +804,6 @@ Gicv3Redistributor::intStatus(uint32_t int_id) const
void
Gicv3Redistributor::update()
{
bool new_hppi = false;
for (int int_id = 0; int_id < Gicv3::SGI_MAX + Gicv3::PPI_MAX; int_id++) {
Gicv3::GroupId int_group = getIntGroup(int_id);
bool group_enabled = distributor->groupEnabled(int_group);
@@ -817,7 +821,6 @@ Gicv3Redistributor::update()
cpuInterface->hppi.intid = int_id;
cpuInterface->hppi.prio = irqPriority[int_id];
cpuInterface->hppi.group = int_group;
new_hppi = true;
}
}
}
@@ -866,17 +869,12 @@ Gicv3Redistributor::update()
cpuInterface->hppi.intid = lpi_id;
cpuInterface->hppi.prio = lpi_priority;
cpuInterface->hppi.group = lpi_group;
new_hppi = true;
}
}
}
}
if (!new_hppi && cpuInterface->hppi.prio != 0xff &&
(cpuInterface->hppi.intid < Gicv3::SGI_MAX + Gicv3::PPI_MAX ||
cpuInterface->hppi.intid > SMALLEST_LPI_ID)) {
distributor->fullUpdate();
}
cpuInterface->update();
}
uint8_t
@@ -958,14 +956,7 @@ Gicv3Redistributor::setClrLPI(uint64_t data, bool set)
writeEntryLPI(lpi_id, lpi_pending_entry);
updateAndInformCPUInterface();
}
void
Gicv3Redistributor::updateAndInformCPUInterface()
{
update();
cpuInterface->update();
updateDistributor();
}
Gicv3::GroupId

View File

@@ -210,7 +210,7 @@ class Gicv3Redistributor : public Serializable
void serialize(CheckpointOut & cp) const override;
void unserialize(CheckpointIn & cp) override;
void update();
void updateAndInformCPUInterface();
void updateDistributor();
public: