dev-arm: LPI support for GICv3. This doesn't include an ITS model.

Change-Id: Ia2c02cca4f95672d6361fba16201a56e2047ddb7
Reviewed-on: https://gem5-review.googlesource.com/c/16142
Reviewed-by: Giacomo Travaglini <giacomo.travaglini@arm.com>
Maintainer: Andreas Sandberg <andreas.sandberg@arm.com>
diff --git a/src/dev/arm/gic_v3_cpu_interface.cc b/src/dev/arm/gic_v3_cpu_interface.cc
index 9bc8cbd..1beceac 100644
--- a/src/dev/arm/gic_v3_cpu_interface.cc
+++ b/src/dev/arm/gic_v3_cpu_interface.cc
@@ -460,6 +460,14 @@
               if (int_id < Gicv3::INTID_SECURE) {
                   activateIRQ(int_id, hppi.group);
               }
+
+              // LPIs are not activated and when acked their pending
+              // bit is cleared
+              if (int_id >= Gicv3Redistributor::SMALLEST_LPI_ID)
+              {
+                  redistributor->setClrLPI(int_id, false);
+              }
+
           } else {
               int_id = Gicv3::INTID_SPURIOUS;
           }
diff --git a/src/dev/arm/gic_v3_distributor.cc b/src/dev/arm/gic_v3_distributor.cc
index 00f29a7..5eee07d 100644
--- a/src/dev/arm/gic_v3_distributor.cc
+++ b/src/dev/arm/gic_v3_distributor.cc
@@ -451,7 +451,7 @@
          * DVIS          [18]    == 0
          * (The implementation does not support Direct Virtual LPI
          * injection)
-         * LPIS          [17]    == 0
+         * LPIS          [17]    == 1
          * (The implementation does not support LPIs)
          * MBIS          [16]    == 0
          * (The implementation does not support message-based interrupts
@@ -467,7 +467,7 @@
             int max_spi_int_id = itLines - 1;
             int it_lines_number = ceil((max_spi_int_id + 1) / 32.0) - 1;
             return (1 << 26) | (1 << 25) | (1 << 24) | (0xf << 19) |
-                (gic->getSystem()->haveSecurity() << 10) |
+                (1 << 17) | (gic->getSystem()->haveSecurity() << 10) |
                 (it_lines_number << 0);
         }
 
diff --git a/src/dev/arm/gic_v3_redistributor.cc b/src/dev/arm/gic_v3_redistributor.cc
index ec1e388..6256507 100644
--- a/src/dev/arm/gic_v3_redistributor.cc
+++ b/src/dev/arm/gic_v3_redistributor.cc
@@ -34,6 +34,7 @@
 #include "debug/GIC.hh"
 #include "dev/arm/gic_v3_cpu_interface.hh"
 #include "dev/arm/gic_v3_distributor.hh"
+#include "mem/fs_translating_port_proxy.hh"
 
 const AddrRange Gicv3Redistributor::GICR_IPRIORITYR(SGI_base + 0x0400,
         SGI_base + 0x041f);
@@ -91,6 +92,10 @@
     DPG1S = false;
     DPG1NS = false;
     DPG0 = false;
+    EnableLPIs = false;
+    lpiConfigurationTablePtr = 0;
+    lpiIDBits = 0;
+    lpiPendingTablePtr = 0;
 }
 
 uint64_t
@@ -135,6 +140,10 @@
               value |= GICR_CTLR_DPG0;
           }
 
+          if (EnableLPIs) {
+              value |= GICR_CTLR_ENABLE_LPIS;
+          }
+
           return value;
       }
 
@@ -156,17 +165,17 @@
            * Last             [4]     == X
            * (This Redistributor is the highest-numbered Redistributor in
            * a series of contiguous Redistributor pages)
-           * DirectLPI        [3]     == 0
-           * (direct injection of LPIs not supported)
+           * DirectLPI        [3]     == 1
+           * (direct injection of LPIs supported)
            * VLPIS            [1]     == 0
            * (virtual LPIs not supported)
-           * PLPIS            [0]     == 0
-           * (physical LPIs not supported)
+           * PLPIS            [0]     == 1
+           * (physical LPIs supported)
            */
           uint64_t affinity = getAffinity();
           int last = cpuId == (gic->getSystem()->numContexts() - 1);
           return (affinity << 32) | (1 << 24) | (cpuId << 8) |
-              (1 << 5) | (last << 4);
+              (1 << 5) | (last << 4) | (1 << 3) | (1 << 0);
       }
 
       case GICR_WAKER: // Wake Register
@@ -346,6 +355,39 @@
           return value;
       }
 
+      case GICR_PROPBASER: // Redistributor Properties Base Address Register
+        // OuterCache, bits [58:56]
+        //   000 Memory type defined in InnerCache field
+        // Physical_Address, bits [51:12]
+        //   Bits [51:12] of the physical address containing the LPI
+        //   Configuration table
+        // Shareability, bits [11:10]
+        //   00 Non-shareable
+        // InnerCache, bits [9:7]
+        //   000 Device-nGnRnE
+        // IDbits, bits [4:0]
+        //   limited by GICD_TYPER.IDbits
+        return lpiConfigurationTablePtr | lpiIDBits;
+
+      // Redistributor LPI Pending Table Base Address Register
+      case GICR_PENDBASER:
+        // PTZ, bit [62]
+        //   Pending Table Zero
+        // OuterCache, bits [58:56]
+        //   000 Memory type defined in InnerCache field
+        // Physical_Address, bits [51:16]
+        //   Bits [51:16] of the physical address containing the LPI Pending
+        //   table
+        // Shareability, bits [11:10]
+        //   00 Non-shareable
+        // InnerCache, bits [9:7]
+        //   000 Device-nGnRnE
+        return lpiPendingTablePtr;
+
+      // Redistributor Synchronize Register
+      case GICR_SYNCR:
+        return 0;
+
       default:
         panic("Gicv3Redistributor::read(): invalid offset %#x\n", addr);
         break;
@@ -382,7 +424,8 @@
 
     switch (addr) {
       case GICR_CTLR: {
-          // GICR_TYPER.LPIS is 0 so Enable_LPIs is RES0
+          // GICR_TYPER.LPIS is 0 so EnableLPIs is RES0
+          EnableLPIs = data & GICR_CTLR_ENABLE_LPIS;
           DPG1S = data & GICR_CTLR_DPG1S;
           DPG1NS = data & GICR_CTLR_DPG1NS;
           DPG0 = data & GICR_CTLR_DPG0;
@@ -606,6 +649,81 @@
           break;
       }
 
+      case GICR_SETLPIR: // Set LPI Pending Register
+        setClrLPI(data, true);
+        break;
+
+      case GICR_CLRLPIR: // Clear LPI Pending Register
+        setClrLPI(data, false);
+        break;
+
+      case GICR_PROPBASER: { // Redistributor Properties Base Address Register
+          // OuterCache, bits [58:56]
+          //   000 Memory type defined in InnerCache field
+          // Physical_Address, bits [51:12]
+          //   Bits [51:12] of the physical address containing the LPI
+          //   Configuration table
+          // Shareability, bits [11:10]
+          //   00 Non-shareable
+          // InnerCache, bits [9:7]
+          //   000 Device-nGnRnE
+          // IDbits, bits [4:0]
+          //   limited by GICD_TYPER.IDbits (= 0xf)
+          lpiConfigurationTablePtr = data & 0xFFFFFFFFFF000;
+          lpiIDBits = data & 0x1f;
+
+          // 0xf here matches the value of GICD_TYPER.IDbits.
+          // TODO - make GICD_TYPER.IDbits a parameter instead of a hardcoded
+          // value
+          if (lpiIDBits > 0xf) {
+              lpiIDBits = 0xf;
+          }
+
+          uint32_t largest_lpi_id = 2 ^ (lpiIDBits + 1);
+          uint32_t number_lpis = largest_lpi_id - SMALLEST_LPI_ID + 1;
+          lpiConfigurationTable.resize(number_lpis);
+          break;
+      }
+
+      // Redistributor LPI Pending Table Base Address Register
+      case GICR_PENDBASER:
+        // PTZ, bit [62]
+        //   Pending Table Zero
+        // OuterCache, bits [58:56]
+        //   000 Memory type defined in InnerCache field
+        // Physical_Address, bits [51:16]
+        //   Bits [51:16] of the physical address containing the LPI Pending
+        //   table
+        // Shareability, bits [11:10]
+        //   00 Non-shareable
+        // InnerCache, bits [9:7]
+        //   000 Device-nGnRnE
+        lpiPendingTablePtr = data & 0xFFFFFFFFF0000;
+        break;
+
+      case GICR_INVLPIR: { // Redistributor Invalidate LPI Register
+          uint32_t lpi_id = data & 0xffffffff;
+          uint32_t largest_lpi_id = 2 ^ (lpiIDBits + 1);
+
+          if (lpi_id > largest_lpi_id) {
+              return;
+          }
+
+          uint32_t lpi_table_entry_index = lpi_id - SMALLEST_LPI_ID;
+          invalLpiConfig(lpi_table_entry_index);
+          break;
+      }
+
+      case GICR_INVALLR: { // Redistributor Invalidate All Register
+          for (int lpi_table_entry_index = 0;
+               lpi_table_entry_index < lpiConfigurationTable.size();
+               lpi_table_entry_index++) {
+              invalLpiConfig(lpi_table_entry_index);
+          }
+
+          break;
+      }
+
       default:
         panic("Gicv3Redistributor::write(): invalid offset %#x\n", addr);
         break;
@@ -613,6 +731,17 @@
 }
 
 void
+Gicv3Redistributor::invalLpiConfig(uint32_t lpi_entry_index)
+{
+    Addr lpi_table_entry_ptr = lpiConfigurationTablePtr +
+        lpi_entry_index * sizeof(LPIConfigurationTableEntry);
+    ThreadContext * tc = gic->getSystem()->getThreadContext(cpuId);
+    tc->getVirtProxy().readBlob(lpi_table_entry_ptr,
+            (uint8_t*) &lpiConfigurationTable[lpi_entry_index],
+            sizeof(LPIConfigurationTableEntry));
+}
+
+void
 Gicv3Redistributor::sendPPInt(uint32_t int_id)
 {
     assert((int_id >= Gicv3::SGI_MAX) &&
@@ -704,6 +833,42 @@
         }
     }
 
+    // Check LPIs
+    uint32_t largest_lpi_id = 2 ^ (lpiIDBits + 1);
+    char lpi_pending_table[largest_lpi_id / 8];
+    ThreadContext * tc = gic->getSystem()->getThreadContext(cpuId);
+    tc->getVirtProxy().readBlob(lpiPendingTablePtr,
+                                (uint8_t *) lpi_pending_table,
+                                sizeof(lpi_pending_table));
+    for (int lpi_id = SMALLEST_LPI_ID; lpi_id < largest_lpi_id;
+         largest_lpi_id++) {
+        uint32_t lpi_pending_entry_byte = lpi_id / 8;
+        uint8_t lpi_pending_entry_bit_position = lpi_id % 8;
+        bool lpi_is_pending = lpi_pending_table[lpi_pending_entry_byte] &
+                              1 << lpi_pending_entry_bit_position;
+        uint32_t lpi_configuration_entry_index = lpi_id - SMALLEST_LPI_ID;
+        bool lpi_is_enable =
+            lpiConfigurationTable[lpi_configuration_entry_index].enable;
+        // LPIs are always Non-secure Group 1 interrupts,
+        // in a system where two Security states are enabled.
+        Gicv3::GroupId lpi_group = Gicv3::G1NS;
+        bool group_enabled = distributor->groupEnabled(lpi_group);
+
+        if (lpi_is_pending && lpi_is_enable && group_enabled) {
+            uint8_t lpi_priority =
+                lpiConfigurationTable[lpi_configuration_entry_index].priority;
+
+            if ((lpi_priority < cpuInterface->hppi.prio) ||
+                (lpi_priority == cpuInterface->hppi.prio &&
+                 lpi_id < cpuInterface->hppi.intid)) {
+                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) {
         distributor->fullUpdate();
@@ -711,6 +876,57 @@
 }
 
 void
+Gicv3Redistributor::setClrLPI(uint64_t data, bool set)
+{
+    if (!EnableLPIs) {
+        // Writes to GICR_SETLPIR or GICR_CLRLPIR have not effect if
+        // GICR_CTLR.EnableLPIs == 0.
+        return;
+    }
+
+    uint32_t lpi_id = data & 0xffffffff;
+    uint32_t largest_lpi_id = 2 ^ (lpiIDBits + 1);
+
+    if (lpi_id > largest_lpi_id) {
+        // Writes to GICR_SETLPIR or GICR_CLRLPIR have not effect if
+        // pINTID value specifies an unimplemented LPI.
+        return;
+    }
+
+    Addr lpi_pending_entry_ptr = lpiPendingTablePtr + (lpi_id / 8);
+    uint8_t lpi_pending_entry;
+    ThreadContext * tc = gic->getSystem()->getThreadContext(cpuId);
+    tc->getVirtProxy().readBlob(lpi_pending_entry_ptr,
+            (uint8_t*) &lpi_pending_entry,
+            sizeof(lpi_pending_entry));
+    uint8_t lpi_pending_entry_bit_position = lpi_id % 8;
+    bool is_set = lpi_pending_entry & (1 << lpi_pending_entry_bit_position);
+
+    if (set) {
+        if (is_set) {
+            // Writes to GICR_SETLPIR have not effect if the pINTID field
+            // corresponds to an LPI that is already pending.
+            return;
+        }
+
+        lpi_pending_entry |= 1 << (lpi_pending_entry_bit_position);
+    } else {
+        if (!is_set) {
+            // Writes to GICR_SETLPIR have not effect if the pINTID field
+            // corresponds to an LPI that is not pending.
+            return;
+        }
+
+        lpi_pending_entry &= ~(1 << (lpi_pending_entry_bit_position));
+    }
+
+    tc->getVirtProxy().writeBlob(lpi_pending_entry_ptr,
+            (uint8_t*) &lpi_pending_entry,
+            sizeof(lpi_pending_entry));
+    updateAndInformCPUInterface();
+}
+
+void
 Gicv3Redistributor::updateAndInformCPUInterface()
 {
     update();
@@ -814,6 +1030,10 @@
     SERIALIZE_SCALAR(DPG1S);
     SERIALIZE_SCALAR(DPG1NS);
     SERIALIZE_SCALAR(DPG0);
+    SERIALIZE_SCALAR(EnableLPIs);
+    SERIALIZE_SCALAR(lpiConfigurationTablePtr);
+    SERIALIZE_SCALAR(lpiIDBits);
+    SERIALIZE_SCALAR(lpiPendingTablePtr);
 }
 
 void
@@ -831,4 +1051,8 @@
     UNSERIALIZE_SCALAR(DPG1S);
     UNSERIALIZE_SCALAR(DPG1NS);
     UNSERIALIZE_SCALAR(DPG0);
+    UNSERIALIZE_SCALAR(EnableLPIs);
+    UNSERIALIZE_SCALAR(lpiConfigurationTablePtr);
+    UNSERIALIZE_SCALAR(lpiIDBits);
+    UNSERIALIZE_SCALAR(lpiPendingTablePtr);
 }
diff --git a/src/dev/arm/gic_v3_redistributor.hh b/src/dev/arm/gic_v3_redistributor.hh
index 1155397..6641736 100644
--- a/src/dev/arm/gic_v3_redistributor.hh
+++ b/src/dev/arm/gic_v3_redistributor.hh
@@ -119,6 +119,24 @@
     // Interrupt Priority Registers
     static const AddrRange GICR_IPRIORITYR;
 
+    // GIC physical LPI Redistributor register
+    enum {
+        // Set LPI Pending Register
+        GICR_SETLPIR = RD_base + 0x0040,
+        // Clear LPI Pending Register
+        GICR_CLRLPIR = RD_base + 0x0048,
+        //Redistributor Properties Base Address Register
+        GICR_PROPBASER = RD_base + 0x0070,
+        // Redistributor LPI Pending Table Base Address Register
+        GICR_PENDBASER = RD_base + 0x0078,
+        // Redistributor Invalidate LPI Register
+        GICR_INVLPIR = RD_base + 0x00A0,
+        // Redistributor Invalidate All Register
+        GICR_INVALLR = RD_base + 0x00B0,
+        // Redistributor Synchronize Register
+        GICR_SYNCR = RD_base + 0x00C0,
+    };
+
     std::vector <uint8_t> irqGroup;
     std::vector <bool> irqEnabled;
     std::vector <bool> irqPending;
@@ -131,7 +149,21 @@
     bool DPG1S;
     bool DPG1NS;
     bool DPG0;
+    bool EnableLPIs;
 
+    Addr lpiConfigurationTablePtr;
+    uint8_t lpiIDBits;
+    Addr lpiPendingTablePtr;
+
+    BitUnion8(LPIConfigurationTableEntry)
+        Bitfield<7, 2> priority;
+        Bitfield<1> res1;
+        Bitfield<0> enable;
+    EndBitUnion(LPIConfigurationTableEntry)
+
+    std::vector<LPIConfigurationTableEntry> lpiConfigurationTable;
+
+    static const uint32_t GICR_CTLR_ENABLE_LPIS = 1 << 0;
     static const uint32_t GICR_CTLR_DPG0 = 1 << 24;
     static const uint32_t GICR_CTLR_DPG1NS = 1 << 25;
     static const uint32_t GICR_CTLR_DPG1S = 1 << 26;
@@ -147,6 +179,8 @@
      */
     static const uint32_t ADDR_RANGE_SIZE = 0x40000;
 
+    static const uint32_t SMALLEST_LPI_ID = 8192;
+
     Gicv3Redistributor(Gicv3 * gic, uint32_t cpu_id);
     ~Gicv3Redistributor();
     void init();
@@ -168,6 +202,7 @@
     }
 
     bool canBeSelectedFor1toNInterrupt(Gicv3::GroupId group);
+    void setClrLPI(uint64_t data, bool set);
 
   protected:
 
@@ -178,6 +213,7 @@
     Gicv3::GroupId getIntGroup(int int_id);
     void activateIRQ(uint32_t int_id);
     void deactivateIRQ(uint32_t int_id);
+    void invalLpiConfig(uint32_t lpi_entry_index);
 };
 
 #endif //__DEV_ARM_GICV3_REDISTRIBUTOR_H__