dev-arm: Rewrite GICv3 update
authorGiacomo Travaglini <giacomo.travaglini@arm.com>
Tue, 27 Aug 2019 10:38:55 +0000 (11:38 +0100)
committerGiacomo Travaglini <giacomo.travaglini@arm.com>
Fri, 6 Sep 2019 11:53:49 +0000 (11:53 +0000)
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>
src/dev/arm/gic_v3_cpu_interface.cc
src/dev/arm/gic_v3_cpu_interface.hh
src/dev/arm/gic_v3_distributor.cc
src/dev/arm/gic_v3_distributor.hh
src/dev/arm/gic_v3_its.cc
src/dev/arm/gic_v3_redistributor.cc
src/dev/arm/gic_v3_redistributor.hh

index 97d914568b529424446ee3fbc4ec82b77a56f50f..b4c271d4a29f87c7d33241d157c54a0781de506b 100644 (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()
 {
index 5c66fd75f5e26ae381fd9445d7ee229ed38df876..ed432bce5c7bbc3371bd7fc88a1e473536cf791a 100644 (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();
index f85474c9c97b505d584b51d3c7e271ae28881b8c..0211bec561f9f05aab4245e632e2041cb7af10bd 100644 (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);
-
-                    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;
-            }
+            // Find the cpu interface where to route the interrupt
+            Gicv3CPUInterface *target_cpu_interface = route(int_id);
 
-            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();
     }
 }
 
index 0cf8d378c8f81760e8ab12b675eddf96fda2aca3..76ab6dd02e3d1ba33c709ebd49fd7c47156e30f9 100644 (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);
index f822042ce44d103ec0dd27e3a35c69d0ece478d3..4108cc744f5f8f7fe95fd4215554ed25029f1c70 100644 (file)
@@ -1271,7 +1271,7 @@ Gicv3Its::moveAllPendingState(
         rd1->lpiPendingTablePtr,
         0, sizeof(lpi_pending_table));
 
-    rd2->updateAndInformCPUInterface();
+    rd2->updateDistributor();
 }
 
 Gicv3Its *
index 71e74bfb8e12eba5aef595f8afcac737b22643f5..e22ea7080d97b4a6609a88b2eb481fbede4efa6c 100644 (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
index 29ff8672d5e6f677cf9e4b55a445c53455105977..c59c3411f2114204ba452c47e7d3f5e63db85628 100644 (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: