dev-arm: Add a GICv3 model

Change-Id: Ib0067fc743f84ff7be9f12d2fc33ddf63736bdd1
Reviewed-on: https://gem5-review.googlesource.com/c/13436
Reviewed-by: Giacomo Travaglini <giacomo.travaglini@arm.com>
Maintainer: Giacomo Travaglini <giacomo.travaglini@arm.com>
diff --git a/src/arch/arm/isa.cc b/src/arch/arm/isa.cc
index 647ecf7..d7d51b8 100644
--- a/src/arch/arm/isa.cc
+++ b/src/arch/arm/isa.cc
@@ -48,6 +48,8 @@
 #include "debug/Arm.hh"
 #include "debug/MiscRegs.hh"
 #include "dev/arm/generic_timer.hh"
+#include "dev/arm/gic_v3.hh"
+#include "dev/arm/gic_v3_cpu_interface.hh"
 #include "params/ArmISA.hh"
 #include "sim/faults.hh"
 #include "sim/stat_control.hh"
@@ -94,6 +96,13 @@
         physAddrRange = 32;  // dummy value
     }
 
+    // GICv3 CPU interface system registers are supported
+    haveGICv3CPUInterface = false;
+
+    if (system && dynamic_cast<Gicv3 *>(system->getGIC())) {
+        haveGICv3CPUInterface = true;
+    }
+
     initializeMiscRegMetadata();
     preUnflattenMiscReg();
 
@@ -372,6 +381,13 @@
 {
     pmu->setThreadContext(tc);
 
+    if (system) {
+        Gicv3 *gicv3 = dynamic_cast<Gicv3 *>(system->getGIC());
+        if (gicv3) {
+            gicv3CpuInterface.reset(gicv3->getCPUInterface(tc->contextId()));
+            gicv3CpuInterface->setISA(this);
+        }
+    }
 }
 
 
@@ -672,10 +688,11 @@
                  | (haveTimer          ? 0x00010000 : 0x0);
         }
       case MISCREG_ID_AA64PFR0_EL1:
-        return 0x0000000000000002   // AArch{64,32} supported at EL0
-             | 0x0000000000000020                             // EL1
-             | (haveVirtualization ? 0x0000000000000200 : 0)  // EL2
-             | (haveSecurity       ? 0x0000000000002000 : 0); // EL3
+        return 0x0000000000000002 | // AArch{64,32} supported at EL0
+               0x0000000000000020                               | // EL1
+               (haveVirtualization    ? 0x0000000000000200 : 0) | // EL2
+               (haveSecurity          ? 0x0000000000002000 : 0) | // EL3
+               (haveGICv3CPUInterface ? 0x0000000001000000 : 0);
       case MISCREG_ID_AA64PFR1_EL1:
         return 0; // bits [63:0] RES0 (reserved for future use)
 
@@ -689,6 +706,10 @@
       case MISCREG_CNTVOFF_EL2 ... MISCREG_CNTPS_CVAL_EL1:
         return getGenericTimer(tc).readMiscReg(misc_reg);
 
+      case MISCREG_ICC_PMR_EL1 ... MISCREG_ICC_IGRPEN1_EL3:
+      case MISCREG_ICH_AP0R0_EL2 ... MISCREG_ICH_LR15_EL2:
+        return getGICv3CPUInterface(tc).readMiscReg(misc_reg);
+
       default:
         break;
 
@@ -1965,6 +1986,11 @@
           case MISCREG_CNTVOFF_EL2 ... MISCREG_CNTPS_CVAL_EL1:
             getGenericTimer(tc).setMiscReg(misc_reg, newVal);
             break;
+
+          case MISCREG_ICC_PMR_EL1 ... MISCREG_ICC_IGRPEN1_EL3:
+          case MISCREG_ICH_AP0R0_EL2 ... MISCREG_ICH_LR15_EL2:
+            getGICv3CPUInterface(tc).setMiscReg(misc_reg, newVal);
+            return;
         }
     }
     setMiscRegNoEffect(misc_reg, newVal);
@@ -1991,6 +2017,13 @@
     return *timer.get();
 }
 
+BaseISADevice &
+ISA::getGICv3CPUInterface(ThreadContext *tc)
+{
+    panic_if(!gicv3CpuInterface, "GICV3 cpu interface is not registered!");
+    return *gicv3CpuInterface.get();
+}
+
 }
 
 ArmISA::ISA *
diff --git a/src/arch/arm/isa.hh b/src/arch/arm/isa.hh
index 65d2251..1ec0f46 100644
--- a/src/arch/arm/isa.hh
+++ b/src/arch/arm/isa.hh
@@ -82,6 +82,9 @@
         // Generic timer interface belonging to this ISA
         std::unique_ptr<BaseISADevice> timer;
 
+        // GICv3 CPU interface belonging to this ISA
+        std::unique_ptr<BaseISADevice> gicv3CpuInterface;
+
         // Cached copies of system-level properties
         bool highestELIs64;
         bool haveSecurity;
@@ -89,6 +92,7 @@
         bool haveVirtualization;
         bool haveCrypto;
         bool haveLargeAsid64;
+        bool haveGICv3CPUInterface;
         uint8_t physAddrRange;
 
         /**
@@ -400,6 +404,7 @@
         }
 
         BaseISADevice &getGenericTimer(ThreadContext *tc);
+        BaseISADevice &getGICv3CPUInterface(ThreadContext *tc);
 
 
       private:
diff --git a/src/arch/arm/miscregs.cc b/src/arch/arm/miscregs.cc
index 7c1a693..1d4002a 100644
--- a/src/arch/arm/miscregs.cc
+++ b/src/arch/arm/miscregs.cc
@@ -287,6 +287,11 @@
             return MISCREG_DACR;
         }
         break;
+      case 4:
+        if (opc1 == 0 && crm == 6 && opc2 == 0) {
+            return MISCREG_ICC_PMR;
+        }
+        break;
       case 5:
         if (opc1 == 0) {
             if (crm == 0) {
@@ -668,10 +673,193 @@
                 if (opc2 == 0) {
                     return MISCREG_ISR;
                 }
+            } else if (crm == 8) {
+                switch (opc2) {
+                    case 0:
+                        return MISCREG_ICC_IAR0;
+                    case 1:
+                        return MISCREG_ICC_EOIR0;
+                    case 2:
+                        return MISCREG_ICC_HPPIR0;
+                    case 3:
+                        return MISCREG_ICC_BPR0;
+                    case 4:
+                        return MISCREG_ICC_AP0R0;
+                    case 5:
+                        return MISCREG_ICC_AP0R1;
+                    case 6:
+                        return MISCREG_ICC_AP0R2;
+                    case 7:
+                        return MISCREG_ICC_AP0R3;
+                }
+            } else if (crm == 9) {
+                switch (opc2) {
+                    case 0:
+                        return MISCREG_ICC_AP1R0;
+                    case 1:
+                        return MISCREG_ICC_AP1R1;
+                    case 2:
+                        return MISCREG_ICC_AP1R2;
+                    case 3:
+                        return MISCREG_ICC_AP1R3;
+                }
+            } else if (crm == 11) {
+                switch (opc2) {
+                    case 1:
+                        return MISCREG_ICC_DIR;
+                    case 3:
+                        return MISCREG_ICC_RPR;
+                }
+            } else if (crm == 12) {
+                switch (opc2) {
+                    case 0:
+                        return MISCREG_ICC_IAR1;
+                    case 1:
+                        return MISCREG_ICC_EOIR1;
+                    case 2:
+                        return MISCREG_ICC_HPPIR1;
+                    case 3:
+                        return MISCREG_ICC_BPR1;
+                    case 4:
+                        return MISCREG_ICC_CTLR;
+                    case 5:
+                        return MISCREG_ICC_SRE;
+                    case 6:
+                        return MISCREG_ICC_IGRPEN0;
+                    case 7:
+                        return MISCREG_ICC_IGRPEN1;
+                }
             }
         } else if (opc1 == 4) {
-            if (crm == 0 && opc2 == 0)
+            if (crm == 0 && opc2 == 0) {
                 return MISCREG_HVBAR;
+            } else if (crm == 8) {
+                switch (opc2) {
+                    case 0:
+                        return MISCREG_ICH_AP0R0;
+                    case 1:
+                        return MISCREG_ICH_AP0R1;
+                    case 2:
+                        return MISCREG_ICH_AP0R2;
+                    case 3:
+                        return MISCREG_ICH_AP0R3;
+                }
+            } else if (crm == 9) {
+                switch (opc2) {
+                    case 0:
+                        return MISCREG_ICH_AP1R0;
+                    case 1:
+                        return MISCREG_ICH_AP1R1;
+                    case 2:
+                        return MISCREG_ICH_AP1R2;
+                    case 3:
+                        return MISCREG_ICH_AP1R3;
+                    case 5:
+                        return MISCREG_ICC_HSRE;
+                }
+            } else if (crm == 11) {
+                switch (opc2) {
+                    case 0:
+                        return MISCREG_ICH_HCR;
+                    case 1:
+                        return MISCREG_ICH_VTR;
+                    case 2:
+                        return MISCREG_ICH_MISR;
+                    case 3:
+                        return MISCREG_ICH_EISR;
+                    case 5:
+                        return MISCREG_ICH_ELRSR;
+                    case 7:
+                        return MISCREG_ICH_VMCR;
+                }
+            } else if (crm == 12) {
+                switch (opc2) {
+                    case 0:
+                        return MISCREG_ICH_LR0;
+                    case 1:
+                        return MISCREG_ICH_LR1;
+                    case 2:
+                        return MISCREG_ICH_LR2;
+                    case 3:
+                        return MISCREG_ICH_LR3;
+                    case 4:
+                        return MISCREG_ICH_LR4;
+                    case 5:
+                        return MISCREG_ICH_LR5;
+                    case 6:
+                        return MISCREG_ICH_LR6;
+                    case 7:
+                        return MISCREG_ICH_LR7;
+                }
+            } else if (crm == 13) {
+                switch (opc2) {
+                    case 0:
+                        return MISCREG_ICH_LR8;
+                    case 1:
+                        return MISCREG_ICH_LR9;
+                    case 2:
+                        return MISCREG_ICH_LR10;
+                    case 3:
+                        return MISCREG_ICH_LR11;
+                    case 4:
+                        return MISCREG_ICH_LR12;
+                    case 5:
+                        return MISCREG_ICH_LR13;
+                    case 6:
+                        return MISCREG_ICH_LR14;
+                    case 7:
+                        return MISCREG_ICH_LR15;
+                }
+            } else if (crm == 14) {
+                switch (opc2) {
+                    case 0:
+                        return MISCREG_ICH_LRC0;
+                    case 1:
+                        return MISCREG_ICH_LRC1;
+                    case 2:
+                        return MISCREG_ICH_LRC2;
+                    case 3:
+                        return MISCREG_ICH_LRC3;
+                    case 4:
+                        return MISCREG_ICH_LRC4;
+                    case 5:
+                        return MISCREG_ICH_LRC5;
+                    case 6:
+                        return MISCREG_ICH_LRC6;
+                    case 7:
+                        return MISCREG_ICH_LRC7;
+                }
+            } else if (crm == 15) {
+                switch (opc2) {
+                    case 0:
+                        return MISCREG_ICH_LRC8;
+                    case 1:
+                        return MISCREG_ICH_LRC9;
+                    case 2:
+                        return MISCREG_ICH_LRC10;
+                    case 3:
+                        return MISCREG_ICH_LRC11;
+                    case 4:
+                        return MISCREG_ICH_LRC12;
+                    case 5:
+                        return MISCREG_ICH_LRC13;
+                    case 6:
+                        return MISCREG_ICH_LRC14;
+                    case 7:
+                        return MISCREG_ICH_LRC15;
+                }
+            }
+        } else if (opc1 == 6) {
+            if (crm == 12) {
+                switch (opc2) {
+                    case 4:
+                        return MISCREG_ICC_MCTLR;
+                    case 5:
+                        return MISCREG_ICC_MSRE;
+                    case 7:
+                        return MISCREG_ICC_MGRPEN1;
+                }
+            }
         }
         break;
       case 13:
@@ -1766,6 +1954,12 @@
                         return MISCREG_CURRENTEL;
                     }
                     break;
+                  case 6:
+                    switch (op2) {
+                      case 0:
+                        return MISCREG_ICC_PMR_EL1;
+                    }
+                    break;
                 }
                 break;
               case 3:
@@ -2145,6 +2339,72 @@
                         return MISCREG_DISR_EL1;
                     }
                     break;
+                  case 8:
+                    switch (op2) {
+                      case 0:
+                        return MISCREG_ICC_IAR0_EL1;
+                      case 1:
+                        return MISCREG_ICC_EOIR0_EL1;
+                      case 2:
+                        return MISCREG_ICC_HPPIR0_EL1;
+                      case 3:
+                        return MISCREG_ICC_BPR0_EL1;
+                      case 4:
+                        return MISCREG_ICC_AP0R0_EL1;
+                      case 5:
+                        return MISCREG_ICC_AP0R1_EL1;
+                      case 6:
+                        return MISCREG_ICC_AP0R2_EL1;
+                      case 7:
+                        return MISCREG_ICC_AP0R3_EL1;
+                    }
+                    break;
+                  case 9:
+                    switch (op2) {
+                      case 0:
+                        return MISCREG_ICC_AP1R0_EL1;
+                      case 1:
+                        return MISCREG_ICC_AP1R1_EL1;
+                      case 2:
+                        return MISCREG_ICC_AP1R2_EL1;
+                      case 3:
+                        return MISCREG_ICC_AP1R3_EL1;
+                    }
+                    break;
+                  case 11:
+                    switch (op2) {
+                      case 1:
+                        return MISCREG_ICC_DIR_EL1;
+                      case 3:
+                        return MISCREG_ICC_RPR_EL1;
+                      case 5:
+                        return MISCREG_ICC_SGI1R_EL1;
+                      case 6:
+                        return MISCREG_ICC_ASGI1R_EL1;
+                      case 7:
+                        return MISCREG_ICC_SGI0R_EL1;
+                    }
+                    break;
+                  case 12:
+                    switch (op2) {
+                      case 0:
+                        return MISCREG_ICC_IAR1_EL1;
+                      case 1:
+                        return MISCREG_ICC_EOIR1_EL1;
+                      case 2:
+                        return MISCREG_ICC_HPPIR1_EL1;
+                      case 3:
+                        return MISCREG_ICC_BPR1_EL1;
+                      case 4:
+                        return MISCREG_ICC_CTLR_EL1;
+                      case 5:
+                        return MISCREG_ICC_SRE_EL1;
+                      case 6:
+                        return MISCREG_ICC_IGRPEN0_EL1;
+                      case 7:
+                        return MISCREG_ICC_IGRPEN1_EL1;
+                    }
+                    break;
                 }
                 break;
               case 4:
@@ -2163,6 +2423,88 @@
                         return MISCREG_VDISR_EL2;
                     }
                     break;
+                  case 8:
+                    switch (op2) {
+                      case 0:
+                        return MISCREG_ICH_AP0R0_EL2;
+                      case 1:
+                        return MISCREG_ICH_AP0R1_EL2;
+                      case 2:
+                        return MISCREG_ICH_AP0R2_EL2;
+                      case 3:
+                        return MISCREG_ICH_AP0R3_EL2;
+                    }
+                    break;
+                  case 9:
+                    switch (op2) {
+                      case 0:
+                        return MISCREG_ICH_AP1R0_EL2;
+                      case 1:
+                        return MISCREG_ICH_AP1R1_EL2;
+                      case 2:
+                        return MISCREG_ICH_AP1R2_EL2;
+                      case 3:
+                        return MISCREG_ICH_AP1R3_EL2;
+                      case 5:
+                        return MISCREG_ICC_SRE_EL2;
+                    }
+                    break;
+                  case 11:
+                    switch (op2) {
+                      case 0:
+                        return MISCREG_ICH_HCR_EL2;
+                      case 1:
+                        return MISCREG_ICH_VTR_EL2;
+                      case 2:
+                        return MISCREG_ICH_MISR_EL2;
+                      case 3:
+                        return MISCREG_ICH_EISR_EL2;
+                      case 5:
+                        return MISCREG_ICH_ELRSR_EL2;
+                      case 7:
+                        return MISCREG_ICH_VMCR_EL2;
+                    }
+                    break;
+                  case 12:
+                    switch (op2) {
+                      case 0:
+                        return MISCREG_ICH_LR0_EL2;
+                      case 1:
+                        return MISCREG_ICH_LR1_EL2;
+                      case 2:
+                        return MISCREG_ICH_LR2_EL2;
+                      case 3:
+                        return MISCREG_ICH_LR3_EL2;
+                      case 4:
+                        return MISCREG_ICH_LR4_EL2;
+                      case 5:
+                        return MISCREG_ICH_LR5_EL2;
+                      case 6:
+                        return MISCREG_ICH_LR6_EL2;
+                      case 7:
+                        return MISCREG_ICH_LR7_EL2;
+                    }
+                    break;
+                  case 13:
+                    switch (op2) {
+                      case 0:
+                        return MISCREG_ICH_LR8_EL2;
+                      case 1:
+                        return MISCREG_ICH_LR9_EL2;
+                      case 2:
+                        return MISCREG_ICH_LR10_EL2;
+                      case 3:
+                        return MISCREG_ICH_LR11_EL2;
+                      case 4:
+                        return MISCREG_ICH_LR12_EL2;
+                      case 5:
+                        return MISCREG_ICH_LR13_EL2;
+                      case 6:
+                        return MISCREG_ICH_LR14_EL2;
+                      case 7:
+                        return MISCREG_ICH_LR15_EL2;
+                    }
+                    break;
                 }
                 break;
               case 6:
@@ -2177,6 +2519,16 @@
                         return MISCREG_RMR_EL3;
                     }
                     break;
+                  case 12:
+                    switch (op2) {
+                      case 4:
+                        return MISCREG_ICC_CTLR_EL3;
+                      case 5:
+                        return MISCREG_ICC_SRE_EL3;
+                      case 7:
+                        return MISCREG_ICC_IGRPEN1_EL3;
+                    }
+                    break;
                 }
                 break;
             }
@@ -4094,6 +4446,476 @@
       .allPrivileges().exceptUserMode().writes(0);
     InitReg(MISCREG_CONTEXTIDR_EL2)
       .mon().hyp();
+
+    // GICv3 AArch64
+    InitReg(MISCREG_ICC_PMR_EL1)
+        .res0(0xffffff00) // [31:8]
+        .allPrivileges().exceptUserMode()
+        .mapsTo(MISCREG_ICC_PMR);
+    InitReg(MISCREG_ICC_IAR0_EL1)
+        .allPrivileges().exceptUserMode().writes(0)
+        .mapsTo(MISCREG_ICC_IAR0);
+    InitReg(MISCREG_ICC_EOIR0_EL1)
+        .allPrivileges().exceptUserMode().reads(0)
+        .mapsTo(MISCREG_ICC_EOIR0);
+    InitReg(MISCREG_ICC_HPPIR0_EL1)
+        .allPrivileges().exceptUserMode().writes(0)
+        .mapsTo(MISCREG_ICC_HPPIR0);
+    InitReg(MISCREG_ICC_BPR0_EL1)
+        .res0(0xfffffff8) // [31:3]
+        .allPrivileges().exceptUserMode()
+        .mapsTo(MISCREG_ICC_BPR0);
+    InitReg(MISCREG_ICC_AP0R0_EL1)
+        .allPrivileges().exceptUserMode()
+        .mapsTo(MISCREG_ICC_AP0R0);
+    InitReg(MISCREG_ICC_AP0R1_EL1)
+        .allPrivileges().exceptUserMode()
+        .mapsTo(MISCREG_ICC_AP0R1);
+    InitReg(MISCREG_ICC_AP0R2_EL1)
+        .allPrivileges().exceptUserMode()
+        .mapsTo(MISCREG_ICC_AP0R2);
+    InitReg(MISCREG_ICC_AP0R3_EL1)
+        .allPrivileges().exceptUserMode()
+        .mapsTo(MISCREG_ICC_AP0R3);
+    InitReg(MISCREG_ICC_AP1R0_EL1)
+        .banked()
+        .mapsTo(MISCREG_ICC_AP1R0);
+    InitReg(MISCREG_ICC_AP1R0_EL1_NS)
+        .bankedChild()
+        .allPrivileges().exceptUserMode()
+        .mapsTo(MISCREG_ICC_AP1R0_NS);
+    InitReg(MISCREG_ICC_AP1R0_EL1_S)
+        .bankedChild()
+        .allPrivileges().exceptUserMode()
+        .mapsTo(MISCREG_ICC_AP1R0_S);
+    InitReg(MISCREG_ICC_AP1R1_EL1)
+        .banked()
+        .mapsTo(MISCREG_ICC_AP1R1);
+    InitReg(MISCREG_ICC_AP1R1_EL1_NS)
+        .bankedChild()
+        .allPrivileges().exceptUserMode()
+        .mapsTo(MISCREG_ICC_AP1R1_NS);
+    InitReg(MISCREG_ICC_AP1R1_EL1_S)
+        .bankedChild()
+        .allPrivileges().exceptUserMode()
+        .mapsTo(MISCREG_ICC_AP1R1_S);
+    InitReg(MISCREG_ICC_AP1R2_EL1)
+        .banked()
+        .mapsTo(MISCREG_ICC_AP1R2);
+    InitReg(MISCREG_ICC_AP1R2_EL1_NS)
+        .bankedChild()
+        .allPrivileges().exceptUserMode()
+        .mapsTo(MISCREG_ICC_AP1R2_NS);
+    InitReg(MISCREG_ICC_AP1R2_EL1_S)
+        .bankedChild()
+        .allPrivileges().exceptUserMode()
+        .mapsTo(MISCREG_ICC_AP1R2_S);
+    InitReg(MISCREG_ICC_AP1R3_EL1)
+        .banked()
+        .mapsTo(MISCREG_ICC_AP1R3);
+    InitReg(MISCREG_ICC_AP1R3_EL1_NS)
+        .bankedChild()
+        .allPrivileges().exceptUserMode()
+        .mapsTo(MISCREG_ICC_AP1R3_NS);
+    InitReg(MISCREG_ICC_AP1R3_EL1_S)
+        .bankedChild()
+        .allPrivileges().exceptUserMode()
+        .mapsTo(MISCREG_ICC_AP1R3_S);
+    InitReg(MISCREG_ICC_DIR_EL1)
+        .res0(0xFF000000) // [31:24]
+        .allPrivileges().exceptUserMode().reads(0)
+        .mapsTo(MISCREG_ICC_DIR);
+    InitReg(MISCREG_ICC_RPR_EL1)
+        .allPrivileges().exceptUserMode().writes(0)
+        .mapsTo(MISCREG_ICC_RPR);
+    InitReg(MISCREG_ICC_SGI1R_EL1)
+        .allPrivileges().exceptUserMode().reads(0)
+        .mapsTo(MISCREG_ICC_SGI1R);
+    InitReg(MISCREG_ICC_ASGI1R_EL1)
+        .allPrivileges().exceptUserMode().reads(0)
+        .mapsTo(MISCREG_ICC_ASGI1R);
+    InitReg(MISCREG_ICC_SGI0R_EL1)
+        .allPrivileges().exceptUserMode().reads(0)
+        .mapsTo(MISCREG_ICC_SGI0R);
+    InitReg(MISCREG_ICC_IAR1_EL1)
+        .allPrivileges().exceptUserMode().writes(0)
+        .mapsTo(MISCREG_ICC_IAR1);
+    InitReg(MISCREG_ICC_EOIR1_EL1)
+        .res0(0xFF000000) // [31:24]
+        .allPrivileges().exceptUserMode().reads(0)
+        .mapsTo(MISCREG_ICC_EOIR1);
+    InitReg(MISCREG_ICC_HPPIR1_EL1)
+        .allPrivileges().exceptUserMode().writes(0)
+        .mapsTo(MISCREG_ICC_HPPIR1);
+    InitReg(MISCREG_ICC_BPR1_EL1)
+        .banked()
+        .mapsTo(MISCREG_ICC_BPR1);
+    InitReg(MISCREG_ICC_BPR1_EL1_NS)
+        .bankedChild()
+        .res0(0xfffffff8) // [31:3]
+        .allPrivileges().exceptUserMode()
+        .mapsTo(MISCREG_ICC_BPR1_NS);
+    InitReg(MISCREG_ICC_BPR1_EL1_S)
+        .bankedChild()
+        .res0(0xfffffff8) // [31:3]
+        .secure().exceptUserMode()
+        .mapsTo(MISCREG_ICC_BPR1_S);
+    InitReg(MISCREG_ICC_CTLR_EL1)
+        .banked()
+        .mapsTo(MISCREG_ICC_CTLR);
+    InitReg(MISCREG_ICC_CTLR_EL1_NS)
+        .bankedChild()
+        .res0(0xFFFB00BC) // [31:19, 17:16, 7, 5:2]
+        .allPrivileges().exceptUserMode()
+        .mapsTo(MISCREG_ICC_CTLR_NS);
+    InitReg(MISCREG_ICC_CTLR_EL1_S)
+        .bankedChild()
+        .res0(0xFFFB00BC) // [31:19, 17:16, 7, 5:2]
+        .secure().exceptUserMode()
+        .mapsTo(MISCREG_ICC_CTLR_S);
+    InitReg(MISCREG_ICC_SRE_EL1)
+        .banked()
+        .mapsTo(MISCREG_ICC_SRE);
+    InitReg(MISCREG_ICC_SRE_EL1_NS)
+        .bankedChild()
+        .res0(0xFFFFFFF8) // [31:3]
+        .allPrivileges().exceptUserMode()
+        .mapsTo(MISCREG_ICC_SRE_NS);
+    InitReg(MISCREG_ICC_SRE_EL1_S)
+        .bankedChild()
+        .res0(0xFFFFFFF8) // [31:3]
+        .secure().exceptUserMode()
+        .mapsTo(MISCREG_ICC_SRE_S);
+    InitReg(MISCREG_ICC_IGRPEN0_EL1)
+        .res0(0xFFFFFFFE) // [31:1]
+        .allPrivileges().exceptUserMode()
+        .mapsTo(MISCREG_ICC_IGRPEN0);
+    InitReg(MISCREG_ICC_IGRPEN1_EL1)
+        .banked()
+        .mapsTo(MISCREG_ICC_IGRPEN1);
+    InitReg(MISCREG_ICC_IGRPEN1_EL1_NS)
+        .bankedChild()
+        .res0(0xFFFFFFFE) // [31:1]
+        .allPrivileges().exceptUserMode()
+        .mapsTo(MISCREG_ICC_IGRPEN1_NS);
+    InitReg(MISCREG_ICC_IGRPEN1_EL1_S)
+        .bankedChild()
+        .res0(0xFFFFFFFE) // [31:1]
+        .secure().exceptUserMode()
+        .mapsTo(MISCREG_ICC_IGRPEN1_S);
+    InitReg(MISCREG_ICC_SRE_EL2)
+        .hyp().mon()
+        .mapsTo(MISCREG_ICC_HSRE);
+    InitReg(MISCREG_ICC_CTLR_EL3)
+        .allPrivileges().exceptUserMode()
+        .mapsTo(MISCREG_ICC_MCTLR);
+    InitReg(MISCREG_ICC_SRE_EL3)
+        .allPrivileges().exceptUserMode()
+        .mapsTo(MISCREG_ICC_MSRE);
+    InitReg(MISCREG_ICC_IGRPEN1_EL3)
+        .allPrivileges().exceptUserMode()
+        .mapsTo(MISCREG_ICC_MGRPEN1);
+
+    InitReg(MISCREG_ICH_AP0R0_EL2)
+        .hyp().mon()
+        .mapsTo(MISCREG_ICH_AP0R0);
+    InitReg(MISCREG_ICH_AP0R1_EL2)
+        .hyp().mon()
+        .unimplemented()
+        .mapsTo(MISCREG_ICH_AP0R1);
+    InitReg(MISCREG_ICH_AP0R2_EL2)
+        .hyp().mon()
+        .unimplemented()
+        .mapsTo(MISCREG_ICH_AP0R2);
+    InitReg(MISCREG_ICH_AP0R3_EL2)
+        .hyp().mon()
+        .unimplemented()
+        .mapsTo(MISCREG_ICH_AP0R3);
+    InitReg(MISCREG_ICH_AP1R0_EL2)
+        .hyp().mon()
+        .mapsTo(MISCREG_ICH_AP1R0);
+    InitReg(MISCREG_ICH_AP1R1_EL2)
+        .hyp().mon()
+        .unimplemented()
+        .mapsTo(MISCREG_ICH_AP1R1);
+    InitReg(MISCREG_ICH_AP1R2_EL2)
+        .hyp().mon()
+        .unimplemented()
+        .mapsTo(MISCREG_ICH_AP1R2);
+    InitReg(MISCREG_ICH_AP1R3_EL2)
+        .hyp().mon()
+        .unimplemented()
+        .mapsTo(MISCREG_ICH_AP1R3);
+    InitReg(MISCREG_ICH_HCR_EL2)
+        .hyp().mon()
+        .mapsTo(MISCREG_ICH_HCR);
+    InitReg(MISCREG_ICH_VTR_EL2)
+        .hyp().mon().writes(0)
+        .mapsTo(MISCREG_ICH_VTR);
+    InitReg(MISCREG_ICH_MISR_EL2)
+        .hyp().mon().writes(0)
+        .mapsTo(MISCREG_ICH_MISR);
+    InitReg(MISCREG_ICH_EISR_EL2)
+        .hyp().mon().writes(0)
+        .mapsTo(MISCREG_ICH_EISR);
+    InitReg(MISCREG_ICH_ELRSR_EL2)
+        .hyp().mon().writes(0)
+        .mapsTo(MISCREG_ICH_ELRSR);
+    InitReg(MISCREG_ICH_VMCR_EL2)
+        .hyp().mon()
+        .mapsTo(MISCREG_ICH_VMCR);
+    InitReg(MISCREG_ICH_LR0_EL2)
+        .hyp().mon()
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICH_LR1_EL2)
+        .hyp().mon()
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICH_LR2_EL2)
+        .hyp().mon()
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICH_LR3_EL2)
+        .hyp().mon()
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICH_LR4_EL2)
+        .hyp().mon()
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICH_LR5_EL2)
+        .hyp().mon()
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICH_LR6_EL2)
+        .hyp().mon()
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICH_LR7_EL2)
+        .hyp().mon()
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICH_LR8_EL2)
+        .hyp().mon()
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICH_LR9_EL2)
+        .hyp().mon()
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICH_LR10_EL2)
+        .hyp().mon()
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICH_LR11_EL2)
+        .hyp().mon()
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICH_LR12_EL2)
+        .hyp().mon()
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICH_LR13_EL2)
+        .hyp().mon()
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICH_LR14_EL2)
+        .hyp().mon()
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICH_LR15_EL2)
+        .hyp().mon()
+        .allPrivileges().exceptUserMode();
+
+    // GICv3 AArch32
+    InitReg(MISCREG_ICC_AP0R0)
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICC_AP0R1)
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICC_AP0R2)
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICC_AP0R3)
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICC_AP1R0)
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICC_AP1R0_NS)
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICC_AP1R0_S)
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICC_AP1R1)
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICC_AP1R1_NS)
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICC_AP1R1_S)
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICC_AP1R2)
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICC_AP1R2_NS)
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICC_AP1R2_S)
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICC_AP1R3)
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICC_AP1R3_NS)
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICC_AP1R3_S)
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICC_ASGI1R)
+        .allPrivileges().exceptUserMode().reads(0);
+    InitReg(MISCREG_ICC_BPR0)
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICC_BPR1)
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICC_BPR1_NS)
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICC_BPR1_S)
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICC_CTLR)
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICC_CTLR_NS)
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICC_CTLR_S)
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICC_DIR)
+        .allPrivileges().exceptUserMode().reads(0);
+    InitReg(MISCREG_ICC_EOIR0)
+        .allPrivileges().exceptUserMode().reads(0);
+    InitReg(MISCREG_ICC_EOIR1)
+        .allPrivileges().exceptUserMode().reads(0);
+    InitReg(MISCREG_ICC_HPPIR0)
+        .allPrivileges().exceptUserMode().writes(0);
+    InitReg(MISCREG_ICC_HPPIR1)
+        .allPrivileges().exceptUserMode().writes(0);
+    InitReg(MISCREG_ICC_HSRE)
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICC_IAR0)
+        .allPrivileges().exceptUserMode().writes(0);
+    InitReg(MISCREG_ICC_IAR1)
+        .allPrivileges().exceptUserMode().writes(0);
+    InitReg(MISCREG_ICC_IGRPEN0)
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICC_IGRPEN1)
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICC_IGRPEN1_NS)
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICC_IGRPEN1_S)
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICC_MCTLR)
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICC_MGRPEN1)
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICC_MSRE)
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICC_PMR)
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICC_RPR)
+        .allPrivileges().exceptUserMode().writes(0);
+    InitReg(MISCREG_ICC_SGI0R)
+        .allPrivileges().exceptUserMode().reads(0);
+    InitReg(MISCREG_ICC_SGI1R)
+        .allPrivileges().exceptUserMode().reads(0);
+    InitReg(MISCREG_ICC_SRE)
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICC_SRE_NS)
+        .allPrivileges().exceptUserMode();
+    InitReg(MISCREG_ICC_SRE_S)
+        .allPrivileges().exceptUserMode();
+
+    InitReg(MISCREG_ICH_AP0R0)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_AP0R1)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_AP0R2)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_AP0R3)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_AP1R0)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_AP1R1)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_AP1R2)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_AP1R3)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_HCR)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_VTR)
+        .hyp().mon().writes(0);
+    InitReg(MISCREG_ICH_MISR)
+        .hyp().mon().writes(0);
+    InitReg(MISCREG_ICH_EISR)
+        .hyp().mon().writes(0);
+    InitReg(MISCREG_ICH_ELRSR)
+        .hyp().mon().writes(0);
+    InitReg(MISCREG_ICH_VMCR)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_LR0)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_LR1)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_LR2)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_LR3)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_LR4)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_LR5)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_LR6)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_LR7)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_LR8)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_LR9)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_LR10)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_LR11)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_LR12)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_LR13)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_LR14)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_LR15)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_LRC0)
+        .mapsTo(MISCREG_ICH_LR0)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_LRC1)
+        .mapsTo(MISCREG_ICH_LR1)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_LRC2)
+        .mapsTo(MISCREG_ICH_LR2)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_LRC3)
+        .mapsTo(MISCREG_ICH_LR3)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_LRC4)
+        .mapsTo(MISCREG_ICH_LR4)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_LRC5)
+        .mapsTo(MISCREG_ICH_LR5)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_LRC6)
+        .mapsTo(MISCREG_ICH_LR6)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_LRC7)
+        .mapsTo(MISCREG_ICH_LR7)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_LRC8)
+        .mapsTo(MISCREG_ICH_LR8)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_LRC9)
+        .mapsTo(MISCREG_ICH_LR9)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_LRC10)
+        .mapsTo(MISCREG_ICH_LR10)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_LRC11)
+        .mapsTo(MISCREG_ICH_LR11)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_LRC12)
+        .mapsTo(MISCREG_ICH_LR12)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_LRC13)
+        .mapsTo(MISCREG_ICH_LR13)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_LRC14)
+        .mapsTo(MISCREG_ICH_LR14)
+        .hyp().mon();
+    InitReg(MISCREG_ICH_LRC15)
+        .mapsTo(MISCREG_ICH_LR15)
+        .hyp().mon();
+
     InitReg(MISCREG_CNTHV_CTL_EL2)
       .mon().hyp();
     InitReg(MISCREG_CNTHV_CVAL_EL2)
diff --git a/src/arch/arm/miscregs.hh b/src/arch/arm/miscregs.hh
index dcaaa7f..0d40b27 100644
--- a/src/arch/arm/miscregs.hh
+++ b/src/arch/arm/miscregs.hh
@@ -673,6 +673,224 @@
         MISCREG_CNTHV_TVAL_EL2,
 
         MISCREG_ID_AA64MMFR2_EL1,
+
+        // GICv3, CPU interface
+        MISCREG_ICC_PMR_EL1,
+        MISCREG_ICC_IAR0_EL1,
+        MISCREG_ICC_EOIR0_EL1,
+        MISCREG_ICC_HPPIR0_EL1,
+        MISCREG_ICC_BPR0_EL1,
+        MISCREG_ICC_AP0R0_EL1,
+        MISCREG_ICC_AP0R1_EL1,
+        MISCREG_ICC_AP0R2_EL1,
+        MISCREG_ICC_AP0R3_EL1,
+        MISCREG_ICC_AP1R0_EL1,
+        MISCREG_ICC_AP1R0_EL1_NS,
+        MISCREG_ICC_AP1R0_EL1_S,
+        MISCREG_ICC_AP1R1_EL1,
+        MISCREG_ICC_AP1R1_EL1_NS,
+        MISCREG_ICC_AP1R1_EL1_S,
+        MISCREG_ICC_AP1R2_EL1,
+        MISCREG_ICC_AP1R2_EL1_NS,
+        MISCREG_ICC_AP1R2_EL1_S,
+        MISCREG_ICC_AP1R3_EL1,
+        MISCREG_ICC_AP1R3_EL1_NS,
+        MISCREG_ICC_AP1R3_EL1_S,
+        MISCREG_ICC_DIR_EL1,
+        MISCREG_ICC_RPR_EL1,
+        MISCREG_ICC_SGI1R_EL1,
+        MISCREG_ICC_ASGI1R_EL1,
+        MISCREG_ICC_SGI0R_EL1,
+        MISCREG_ICC_IAR1_EL1,
+        MISCREG_ICC_EOIR1_EL1,
+        MISCREG_ICC_HPPIR1_EL1,
+        MISCREG_ICC_BPR1_EL1,
+        MISCREG_ICC_BPR1_EL1_NS,
+        MISCREG_ICC_BPR1_EL1_S,
+        MISCREG_ICC_CTLR_EL1,
+        MISCREG_ICC_CTLR_EL1_NS,
+        MISCREG_ICC_CTLR_EL1_S,
+        MISCREG_ICC_SRE_EL1,
+        MISCREG_ICC_SRE_EL1_NS,
+        MISCREG_ICC_SRE_EL1_S,
+        MISCREG_ICC_IGRPEN0_EL1,
+        MISCREG_ICC_IGRPEN1_EL1,
+        MISCREG_ICC_IGRPEN1_EL1_NS,
+        MISCREG_ICC_IGRPEN1_EL1_S,
+        MISCREG_ICC_SRE_EL2,
+        MISCREG_ICC_CTLR_EL3,
+        MISCREG_ICC_SRE_EL3,
+        MISCREG_ICC_IGRPEN1_EL3,
+
+        // GICv3, CPU interface, virtualization
+        MISCREG_ICH_AP0R0_EL2,
+        MISCREG_ICH_AP0R1_EL2,
+        MISCREG_ICH_AP0R2_EL2,
+        MISCREG_ICH_AP0R3_EL2,
+        MISCREG_ICH_AP1R0_EL2,
+        MISCREG_ICH_AP1R1_EL2,
+        MISCREG_ICH_AP1R2_EL2,
+        MISCREG_ICH_AP1R3_EL2,
+        MISCREG_ICH_HCR_EL2,
+        MISCREG_ICH_VTR_EL2,
+        MISCREG_ICH_MISR_EL2,
+        MISCREG_ICH_EISR_EL2,
+        MISCREG_ICH_ELRSR_EL2,
+        MISCREG_ICH_VMCR_EL2,
+        MISCREG_ICH_LR0_EL2,
+        MISCREG_ICH_LR1_EL2,
+        MISCREG_ICH_LR2_EL2,
+        MISCREG_ICH_LR3_EL2,
+        MISCREG_ICH_LR4_EL2,
+        MISCREG_ICH_LR5_EL2,
+        MISCREG_ICH_LR6_EL2,
+        MISCREG_ICH_LR7_EL2,
+        MISCREG_ICH_LR8_EL2,
+        MISCREG_ICH_LR9_EL2,
+        MISCREG_ICH_LR10_EL2,
+        MISCREG_ICH_LR11_EL2,
+        MISCREG_ICH_LR12_EL2,
+        MISCREG_ICH_LR13_EL2,
+        MISCREG_ICH_LR14_EL2,
+        MISCREG_ICH_LR15_EL2,
+
+        MISCREG_ICV_PMR_EL1,
+        MISCREG_ICV_IAR0_EL1,
+        MISCREG_ICV_EOIR0_EL1,
+        MISCREG_ICV_HPPIR0_EL1,
+        MISCREG_ICV_BPR0_EL1,
+        MISCREG_ICV_AP0R0_EL1,
+        MISCREG_ICV_AP0R1_EL1,
+        MISCREG_ICV_AP0R2_EL1,
+        MISCREG_ICV_AP0R3_EL1,
+        MISCREG_ICV_AP1R0_EL1,
+        MISCREG_ICV_AP1R0_EL1_NS,
+        MISCREG_ICV_AP1R0_EL1_S,
+        MISCREG_ICV_AP1R1_EL1,
+        MISCREG_ICV_AP1R1_EL1_NS,
+        MISCREG_ICV_AP1R1_EL1_S,
+        MISCREG_ICV_AP1R2_EL1,
+        MISCREG_ICV_AP1R2_EL1_NS,
+        MISCREG_ICV_AP1R2_EL1_S,
+        MISCREG_ICV_AP1R3_EL1,
+        MISCREG_ICV_AP1R3_EL1_NS,
+        MISCREG_ICV_AP1R3_EL1_S,
+        MISCREG_ICV_DIR_EL1,
+        MISCREG_ICV_RPR_EL1,
+        MISCREG_ICV_SGI1R_EL1,
+        MISCREG_ICV_ASGI1R_EL1,
+        MISCREG_ICV_SGI0R_EL1,
+        MISCREG_ICV_IAR1_EL1,
+        MISCREG_ICV_EOIR1_EL1,
+        MISCREG_ICV_HPPIR1_EL1,
+        MISCREG_ICV_BPR1_EL1,
+        MISCREG_ICV_BPR1_EL1_NS,
+        MISCREG_ICV_BPR1_EL1_S,
+        MISCREG_ICV_CTLR_EL1,
+        MISCREG_ICV_CTLR_EL1_NS,
+        MISCREG_ICV_CTLR_EL1_S,
+        MISCREG_ICV_SRE_EL1,
+        MISCREG_ICV_SRE_EL1_NS,
+        MISCREG_ICV_SRE_EL1_S,
+        MISCREG_ICV_IGRPEN0_EL1,
+        MISCREG_ICV_IGRPEN1_EL1,
+        MISCREG_ICV_IGRPEN1_EL1_NS,
+        MISCREG_ICV_IGRPEN1_EL1_S,
+
+        MISCREG_ICC_AP0R0,
+        MISCREG_ICC_AP0R1,
+        MISCREG_ICC_AP0R2,
+        MISCREG_ICC_AP0R3,
+        MISCREG_ICC_AP1R0,
+        MISCREG_ICC_AP1R0_NS,
+        MISCREG_ICC_AP1R0_S,
+        MISCREG_ICC_AP1R1,
+        MISCREG_ICC_AP1R1_NS,
+        MISCREG_ICC_AP1R1_S,
+        MISCREG_ICC_AP1R2,
+        MISCREG_ICC_AP1R2_NS,
+        MISCREG_ICC_AP1R2_S,
+        MISCREG_ICC_AP1R3,
+        MISCREG_ICC_AP1R3_NS,
+        MISCREG_ICC_AP1R3_S,
+        MISCREG_ICC_ASGI1R,
+        MISCREG_ICC_BPR0,
+        MISCREG_ICC_BPR1,
+        MISCREG_ICC_BPR1_NS,
+        MISCREG_ICC_BPR1_S,
+        MISCREG_ICC_CTLR,
+        MISCREG_ICC_CTLR_NS,
+        MISCREG_ICC_CTLR_S,
+        MISCREG_ICC_DIR,
+        MISCREG_ICC_EOIR0,
+        MISCREG_ICC_EOIR1,
+        MISCREG_ICC_HPPIR0,
+        MISCREG_ICC_HPPIR1,
+        MISCREG_ICC_HSRE,
+        MISCREG_ICC_IAR0,
+        MISCREG_ICC_IAR1,
+        MISCREG_ICC_IGRPEN0,
+        MISCREG_ICC_IGRPEN1,
+        MISCREG_ICC_IGRPEN1_NS,
+        MISCREG_ICC_IGRPEN1_S,
+        MISCREG_ICC_MCTLR,
+        MISCREG_ICC_MGRPEN1,
+        MISCREG_ICC_MSRE,
+        MISCREG_ICC_PMR,
+        MISCREG_ICC_RPR,
+        MISCREG_ICC_SGI0R,
+        MISCREG_ICC_SGI1R,
+        MISCREG_ICC_SRE,
+        MISCREG_ICC_SRE_NS,
+        MISCREG_ICC_SRE_S,
+
+        MISCREG_ICH_AP0R0,
+        MISCREG_ICH_AP0R1,
+        MISCREG_ICH_AP0R2,
+        MISCREG_ICH_AP0R3,
+        MISCREG_ICH_AP1R0,
+        MISCREG_ICH_AP1R1,
+        MISCREG_ICH_AP1R2,
+        MISCREG_ICH_AP1R3,
+        MISCREG_ICH_HCR,
+        MISCREG_ICH_VTR,
+        MISCREG_ICH_MISR,
+        MISCREG_ICH_EISR,
+        MISCREG_ICH_ELRSR,
+        MISCREG_ICH_VMCR,
+        MISCREG_ICH_LR0,
+        MISCREG_ICH_LR1,
+        MISCREG_ICH_LR2,
+        MISCREG_ICH_LR3,
+        MISCREG_ICH_LR4,
+        MISCREG_ICH_LR5,
+        MISCREG_ICH_LR6,
+        MISCREG_ICH_LR7,
+        MISCREG_ICH_LR8,
+        MISCREG_ICH_LR9,
+        MISCREG_ICH_LR10,
+        MISCREG_ICH_LR11,
+        MISCREG_ICH_LR12,
+        MISCREG_ICH_LR13,
+        MISCREG_ICH_LR14,
+        MISCREG_ICH_LR15,
+        MISCREG_ICH_LRC0,
+        MISCREG_ICH_LRC1,
+        MISCREG_ICH_LRC2,
+        MISCREG_ICH_LRC3,
+        MISCREG_ICH_LRC4,
+        MISCREG_ICH_LRC5,
+        MISCREG_ICH_LRC6,
+        MISCREG_ICH_LRC7,
+        MISCREG_ICH_LRC8,
+        MISCREG_ICH_LRC9,
+        MISCREG_ICH_LRC10,
+        MISCREG_ICH_LRC11,
+        MISCREG_ICH_LRC12,
+        MISCREG_ICH_LRC13,
+        MISCREG_ICH_LRC14,
+        MISCREG_ICH_LRC15,
+
         // These MISCREG_FREESLOT are available Misc Register
         // slots for future registers to be implemented.
         MISCREG_FREESLOT_1,
@@ -1389,6 +1607,224 @@
         "cnthv_cval_el2",
         "cnthv_tval_el2",
         "id_aa64mmfr2_el1",
+
+        // GICv3, CPU interface
+        "icc_pmr_el1",
+        "icc_iar0_el1",
+        "icc_eoir0_el1",
+        "icc_hppir0_el1",
+        "icc_bpr0_el1",
+        "icc_ap0r0_el1",
+        "icc_ap0r1_el1",
+        "icc_ap0r2_el1",
+        "icc_ap0r3_el1",
+        "icc_ap1r0_el1",
+        "icc_ap1r0_el1_ns",
+        "icc_ap1r0_el1_s",
+        "icc_ap1r1_el1",
+        "icc_ap1r1_el1_ns",
+        "icc_ap1r1_el1_s",
+        "icc_ap1r2_el1",
+        "icc_ap1r2_el1_ns",
+        "icc_ap1r2_el1_s",
+        "icc_ap1r3_el1",
+        "icc_ap1r3_el1_ns",
+        "icc_ap1r3_el1_s",
+        "icc_dir_el1",
+        "icc_rpr_el1",
+        "icc_sgi1r_el1",
+        "icc_asgi1r_el1",
+        "icc_sgi0r_el1",
+        "icc_iar1_el1",
+        "icc_eoir1_el1",
+        "icc_hppir1_el1",
+        "icc_bpr1_el1",
+        "icc_bpr1_el1_ns",
+        "icc_bpr1_el1_s",
+        "icc_ctlr_el1",
+        "icc_ctlr_el1_ns",
+        "icc_ctlr_el1_s",
+        "icc_sre_el1",
+        "icc_sre_el1_ns",
+        "icc_sre_el1_s",
+        "icc_igrpen0_el1",
+        "icc_igrpen1_el1",
+        "icc_igrpen1_el1_ns",
+        "icc_igrpen1_el1_s",
+        "icc_sre_el2",
+        "icc_ctlr_el3",
+        "icc_sre_el3",
+        "icc_igrpen1_el3",
+
+        // GICv3, CPU interface, virtualization
+        "ich_ap0r0_el2",
+        "ich_ap0r1_el2",
+        "ich_ap0r2_el2",
+        "ich_ap0r3_el2",
+        "ich_ap1r0_el2",
+        "ich_ap1r1_el2",
+        "ich_ap1r2_el2",
+        "ich_ap1r3_el2",
+        "ich_hcr_el2",
+        "ich_vtr_el2",
+        "ich_misr_el2",
+        "ich_eisr_el2",
+        "ich_elrsr_el2",
+        "ich_vmcr_el2",
+        "ich_lr0_el2",
+        "ich_lr1_el2",
+        "ich_lr2_el2",
+        "ich_lr3_el2",
+        "ich_lr4_el2",
+        "ich_lr5_el2",
+        "ich_lr6_el2",
+        "ich_lr7_el2",
+        "ich_lr8_el2",
+        "ich_lr9_el2",
+        "ich_lr10_el2",
+        "ich_lr11_el2",
+        "ich_lr12_el2",
+        "ich_lr13_el2",
+        "ich_lr14_el2",
+        "ich_lr15_el2",
+
+        "icv_pmr_el1",
+        "icv_iar0_el1",
+        "icv_eoir0_el1",
+        "icv_hppir0_el1",
+        "icv_bpr0_el1",
+        "icv_ap0r0_el1",
+        "icv_ap0r1_el1",
+        "icv_ap0r2_el1",
+        "icv_ap0r3_el1",
+        "icv_ap1r0_el1",
+        "icv_ap1r0_el1_ns",
+        "icv_ap1r0_el1_s",
+        "icv_ap1r1_el1",
+        "icv_ap1r1_el1_ns",
+        "icv_ap1r1_el1_s",
+        "icv_ap1r2_el1",
+        "icv_ap1r2_el1_ns",
+        "icv_ap1r2_el1_s",
+        "icv_ap1r3_el1",
+        "icv_ap1r3_el1_ns",
+        "icv_ap1r3_el1_s",
+        "icv_dir_el1",
+        "icv_rpr_el1",
+        "icv_sgi1r_el1",
+        "icv_asgi1r_el1",
+        "icv_sgi0r_el1",
+        "icv_iar1_el1",
+        "icv_eoir1_el1",
+        "icv_hppir1_el1",
+        "icv_bpr1_el1",
+        "icv_bpr1_el1_ns",
+        "icv_bpr1_el1_s",
+        "icv_ctlr_el1",
+        "icv_ctlr_el1_ns",
+        "icv_ctlr_el1_s",
+        "icv_sre_el1",
+        "icv_sre_el1_ns",
+        "icv_sre_el1_s",
+        "icv_igrpen0_el1",
+        "icv_igrpen1_el1",
+        "icv_igrpen1_el1_ns",
+        "icv_igrpen1_el1_s",
+
+        "icc_ap0r0",
+        "icc_ap0r1",
+        "icc_ap0r2",
+        "icc_ap0r3",
+        "icc_ap1r0",
+        "icc_ap1r0_ns",
+        "icc_ap1r0_s",
+        "icc_ap1r1",
+        "icc_ap1r1_ns",
+        "icc_ap1r1_s",
+        "icc_ap1r2",
+        "icc_ap1r2_ns",
+        "icc_ap1r2_s",
+        "icc_ap1r3",
+        "icc_ap1r3_ns",
+        "icc_ap1r3_s",
+        "icc_asgi1r",
+        "icc_bpr0",
+        "icc_bpr1",
+        "icc_bpr1_ns",
+        "icc_bpr1_s",
+        "icc_ctlr",
+        "icc_ctlr_ns",
+        "icc_ctlr_s",
+        "icc_dir",
+        "icc_eoir0",
+        "icc_eoir1",
+        "icc_hppir0",
+        "icc_hppir1",
+        "icc_hsre",
+        "icc_iar0",
+        "icc_iar1",
+        "icc_igrpen0",
+        "icc_igrpen1",
+        "icc_igrpen1_ns",
+        "icc_igrpen1_s",
+        "icc_mctlr",
+        "icc_mgrpen1",
+        "icc_msre",
+        "icc_pmr",
+        "icc_rpr",
+        "icc_sgi0r",
+        "icc_sgi1r",
+        "icc_sre",
+        "icc_sre_ns",
+        "icc_sre_s",
+
+        "ich_ap0r0",
+        "ich_ap0r1",
+        "ich_ap0r2",
+        "ich_ap0r3",
+        "ich_ap1r0",
+        "ich_ap1r1",
+        "ich_ap1r2",
+        "ich_ap1r3",
+        "ich_hcr",
+        "ich_vtr",
+        "ich_misr",
+        "ich_eisr",
+        "ich_elrsr",
+        "ich_vmcr",
+        "ich_lr0",
+        "ich_lr1",
+        "ich_lr2",
+        "ich_lr3",
+        "ich_lr4",
+        "ich_lr5",
+        "ich_lr6",
+        "ich_lr7",
+        "ich_lr8",
+        "ich_lr9",
+        "ich_lr10",
+        "ich_lr11",
+        "ich_lr12",
+        "ich_lr13",
+        "ich_lr14",
+        "ich_lr15",
+        "ich_lrc0",
+        "ich_lrc1",
+        "ich_lrc2",
+        "ich_lrc3",
+        "ich_lrc4",
+        "ich_lrc5",
+        "ich_lrc6",
+        "ich_lrc7",
+        "ich_lrc8",
+        "ich_lrc9",
+        "ich_lrc10",
+        "ich_lrc11",
+        "ich_lrc12",
+        "ich_lrc13",
+        "ich_lrc14",
+        "ich_lrc15",
+
         "freeslot2",
 
         "num_phys_regs",
diff --git a/src/arch/arm/system.cc b/src/arch/arm/system.cc
index 164c965..70622c3 100644
--- a/src/arch/arm/system.cc
+++ b/src/arch/arm/system.cc
@@ -48,6 +48,7 @@
 #include "base/loader/object_file.hh"
 #include "base/loader/symtab.hh"
 #include "cpu/thread_context.hh"
+#include "dev/arm/gic_v3.hh"
 #include "mem/fs_translating_port_proxy.hh"
 #include "mem/physical.hh"
 #include "sim/full_system.hh"
@@ -63,6 +64,7 @@
       _haveVirtualization(p->have_virtualization),
       _haveCrypto(p->have_crypto),
       _genericTimer(nullptr),
+      _gic(nullptr),
       _resetAddr(p->auto_reset_addr ?
                  (kernelEntry & loadAddrMask) + loadAddrOffset :
                  p->reset_addr),
@@ -136,6 +138,7 @@
     const Params* p = params();
 
     if (bootldr) {
+        bool isGICv3System = dynamic_cast<Gicv3 *>(getGIC()) != nullptr;
         bootldr->loadSections(physProxy);
 
         inform("Using bootloader at address %#x\n", bootldr->entryPoint());
@@ -143,15 +146,18 @@
         // Put the address of the boot loader into r7 so we know
         // where to branch to after the reset fault
         // All other values needed by the boot loader to know what to do
-        if (!p->gic_cpu_addr || !p->flags_addr)
-            fatal("gic_cpu_addr && flags_addr must be set with bootloader\n");
+        if (!p->flags_addr)
+           fatal("flags_addr must be set with bootloader\n");
+
+        if (!p->gic_cpu_addr && !isGICv3System)
+            fatal("gic_cpu_addr must be set with bootloader\n");
 
         for (int i = 0; i < threadContexts.size(); i++) {
             if (!_highestELIs64)
                 threadContexts[i]->setIntReg(3, (kernelEntry & loadAddrMask) +
                         loadAddrOffset);
-
-            threadContexts[i]->setIntReg(4, params()->gic_cpu_addr);
+            if (!isGICv3System)
+                threadContexts[i]->setIntReg(4, params()->gic_cpu_addr);
             threadContexts[i]->setIntReg(5, params()->flags_addr);
         }
         inform("Using kernel entry physical address at %#x\n",
diff --git a/src/arch/arm/system.hh b/src/arch/arm/system.hh
index c697460..1a6a64f 100644
--- a/src/arch/arm/system.hh
+++ b/src/arch/arm/system.hh
@@ -54,6 +54,7 @@
 #include "sim/system.hh"
 
 class GenericTimer;
+class BaseGic;
 class ThreadContext;
 
 class ArmSystem : public System
@@ -97,6 +98,7 @@
      * Pointer to the Generic Timer wrapper.
      */
     GenericTimer *_genericTimer;
+    BaseGic *_gic;
 
     /**
      * Reset address (ARMv8)
@@ -192,9 +194,18 @@
         _genericTimer = generic_timer;
     }
 
+    /** Sets the pointer to the GIC. */
+    void setGIC(BaseGic *gic)
+    {
+        _gic = gic;
+    }
+
     /** Get a pointer to the system's generic timer model */
     GenericTimer *getGenericTimer() const { return _genericTimer; }
 
+    /** Get a pointer to the system's GIC */
+    BaseGic *getGIC() const { return _gic; }
+
     /** Returns true if the register width of the highest implemented exception
      * level is 64 bits (ARMv8) */
     bool highestELIs64() const { return _highestELIs64; }
diff --git a/src/base/bitfield.hh b/src/base/bitfield.hh
index 23c8b4b..d696715 100644
--- a/src/base/bitfield.hh
+++ b/src/base/bitfield.hh
@@ -282,4 +282,15 @@
     return val;
 };
 
+/**
+ * Count trailing zeros in a 32-bit value.
+ *
+ * Returns 32 if the value is zero. Note that the GCC builtin is
+ * undefined if the value is zero.
+ */
+inline int ctz32(uint32_t value)
+{
+    return value ? __builtin_ctz(value) : 32;
+}
+
 #endif // __BASE_BITFIELD_HH__
diff --git a/src/dev/arm/Gic.py b/src/dev/arm/Gic.py
index dddb7df..947036a 100644
--- a/src/dev/arm/Gic.py
+++ b/src/dev/arm/Gic.py
@@ -160,3 +160,15 @@
         node.appendPhandle(gic)
 
         yield node
+
+class Gicv3(BaseGic):
+    type = 'Gicv3'
+    cxx_header = "dev/arm/gic_v3.hh"
+
+    dist_addr = Param.Addr(0x2c000000, "Address for distributor")
+    dist_pio_delay = Param.Latency('10ns', "Delay for PIO r/w to distributor")
+    redist_addr = Param.Addr(0x2c010000, "Address for redistributors")
+    redist_pio_delay = Param.Latency('10ns',
+            "Delay for PIO r/w to redistributors")
+    it_lines = Param.UInt32(1020,
+            "Number of interrupt lines supported (max = 1020)")
diff --git a/src/dev/arm/SConscript b/src/dev/arm/SConscript
index b687ee8..c4aa521 100644
--- a/src/dev/arm/SConscript
+++ b/src/dev/arm/SConscript
@@ -57,6 +57,10 @@
     Source('generic_timer.cc')
     Source('gic_v2.cc')
     Source('gic_v2m.cc')
+    Source('gic_v3.cc')
+    Source('gic_v3_cpu_interface.cc')
+    Source('gic_v3_distributor.cc')
+    Source('gic_v3_redistributor.cc')
     Source('pl011.cc')
     Source('pl111.cc')
     Source('hdlcd.cc')
diff --git a/src/dev/arm/base_gic.cc b/src/dev/arm/base_gic.cc
index 864bad3..a23c123 100644
--- a/src/dev/arm/base_gic.cc
+++ b/src/dev/arm/base_gic.cc
@@ -64,6 +64,13 @@
 {
 }
 
+void
+BaseGic::init()
+{
+    PioDevice::init();
+    getSystem()->setGIC(this);
+}
+
 const BaseGic::Params *
 BaseGic::params() const
 {
diff --git a/src/dev/arm/base_gic.hh b/src/dev/arm/base_gic.hh
index f18539f..7c0cc0e 100644
--- a/src/dev/arm/base_gic.hh
+++ b/src/dev/arm/base_gic.hh
@@ -46,6 +46,7 @@
 
 #include <unordered_map>
 
+#include "arch/arm/system.hh"
 #include "dev/io_device.hh"
 
 class Platform;
@@ -67,6 +68,7 @@
 
     BaseGic(const Params *p);
     virtual ~BaseGic();
+    void init() override;
 
     const Params * params() const;
 
@@ -99,6 +101,12 @@
      */
     virtual void clearInt(uint32_t num) = 0;
 
+    ArmSystem *
+    getSystem() const
+    {
+        return (ArmSystem *) sys;
+    }
+
   protected:
     /** Platform this GIC belongs to. */
     Platform *platform;
diff --git a/src/dev/arm/gic_v3.cc b/src/dev/arm/gic_v3.cc
new file mode 100644
index 0000000..738eb11
--- /dev/null
+++ b/src/dev/arm/gic_v3.cc
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 2018 Metempsy Technology Consulting
+ * All rights reserved.
+ *
+ * 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: Jairo Balart
+ */
+
+#include "dev/arm/gic_v3.hh"
+
+#include "cpu/intr_control.hh"
+#include "debug/GIC.hh"
+#include "debug/Interrupt.hh"
+#include "dev/arm/gic_v3_cpu_interface.hh"
+#include "dev/arm/gic_v3_distributor.hh"
+#include "dev/arm/gic_v3_redistributor.hh"
+#include "dev/platform.hh"
+#include "mem/packet.hh"
+#include "mem/packet_access.hh"
+
+Gicv3::Gicv3(const Params * p)
+    : BaseGic(p)
+{
+}
+
+Gicv3::~Gicv3()
+{
+}
+
+void
+Gicv3::init()
+{
+    distRange = RangeSize(params()->dist_addr,
+                          Gicv3Distributor::ADDR_RANGE_SIZE - 1);
+    redistRange = RangeSize(params()->redist_addr,
+        Gicv3Redistributor::ADDR_RANGE_SIZE * sys->numContexts() - 1);
+    addrRanges = {distRange, redistRange};
+    BaseGic::init();
+    distributor = new Gicv3Distributor(this, params()->it_lines);
+    redistributors.resize(sys->numContexts(), nullptr);
+    cpuInterfaces.resize(sys->numContexts(), nullptr);
+
+    for (int i = 0; i < sys->numContexts(); i++) {
+        redistributors[i] = new Gicv3Redistributor(this, i);
+        cpuInterfaces[i] = new Gicv3CPUInterface(this, i);
+    }
+
+    distributor->init();
+
+    for (int i = 0; i < sys->numContexts(); i++) {
+        redistributors[i]->init();
+        cpuInterfaces[i]->init();
+    }
+}
+
+void
+Gicv3::initState()
+{
+    distributor->initState();
+
+    for (int i = 0; i < sys->numContexts(); i++) {
+        redistributors[i]->initState();
+        cpuInterfaces[i]->initState();
+    }
+}
+
+Tick
+Gicv3::read(PacketPtr pkt)
+{
+    const Addr addr = pkt->getAddr();
+    const size_t size = pkt->getSize();
+    const ContextID context_id = pkt->req->contextId();
+    bool is_secure_access = pkt->isSecure();
+    uint64_t resp = 0;
+    Tick delay = 0;
+
+    if (distRange.contains(addr)) {
+        const Addr daddr = addr - distRange.start();
+        panic_if(!distributor, "Distributor is null!");
+        resp = distributor->read(daddr, size, is_secure_access);
+        delay = params()->dist_pio_delay;
+        DPRINTF(GIC, "Gicv3::read(): (distributor) context_id %d register %#x "
+                "size %d is_secure_access %d (value %#x)\n",
+                context_id, daddr, size, is_secure_access, resp);
+    } else if (redistRange.contains(addr)) {
+        Addr daddr = addr - redistRange.start();
+        uint32_t redistributor_id =
+            daddr / Gicv3Redistributor::ADDR_RANGE_SIZE;
+        daddr = daddr % Gicv3Redistributor::ADDR_RANGE_SIZE;
+        panic_if(redistributor_id >= redistributors.size(),
+                 "Invalid redistributor_id!");
+        panic_if(!redistributors[redistributor_id], "Redistributor is null!");
+        resp = redistributors[redistributor_id]->read(daddr, size,
+                is_secure_access);
+        delay = params()->redist_pio_delay;
+        DPRINTF(GIC, "Gicv3::read(): (redistributor %d) context_id %d "
+                "register %#x size %d is_secure_access %d (value %#x)\n",
+                redistributor_id, context_id, daddr, size, is_secure_access,
+                resp);
+    } else {
+        panic("Gicv3::read(): unknown address %#x\n", addr);
+    }
+
+    pkt->setUintX(resp, LittleEndianByteOrder);
+    pkt->makeAtomicResponse();
+    return delay;
+}
+
+Tick
+Gicv3::write(PacketPtr pkt)
+{
+    const size_t size = pkt->getSize();
+    uint64_t data = pkt->getUintX(LittleEndianByteOrder);
+    const Addr addr = pkt->getAddr();
+    const ContextID context_id = pkt->req->contextId();
+    bool is_secure_access = pkt->isSecure();
+    Tick delay = 0;
+
+    if (distRange.contains(addr)) {
+        const Addr daddr = addr - distRange.start();
+        panic_if(!distributor, "Distributor is null!");
+        DPRINTF(GIC, "Gicv3::write(): (distributor) context_id %d "
+                "register %#x size %d is_secure_access %d value %#x\n",
+                context_id, daddr, size, is_secure_access, data);
+        distributor->write(daddr, data, size, is_secure_access);
+        delay = params()->dist_pio_delay;
+    } else if (redistRange.contains(addr)) {
+        Addr daddr = addr - redistRange.start();
+        uint32_t redistributor_id =
+            daddr / Gicv3Redistributor::ADDR_RANGE_SIZE;
+        daddr = daddr % Gicv3Redistributor::ADDR_RANGE_SIZE;
+        panic_if(redistributor_id >= redistributors.size(),
+                 "Invalid redistributor_id!");
+        panic_if(!redistributors[redistributor_id], "Redistributor is null!");
+        DPRINTF(GIC, "Gicv3::write(): (redistributor %d) context_id %d "
+                "register %#x size %d is_secure_access %d value %#x\n",
+                redistributor_id, context_id, daddr, size, is_secure_access,
+                data);
+        redistributors[redistributor_id]->write(daddr, data, size,
+                                                is_secure_access);
+        delay = params()->redist_pio_delay;
+    } else {
+        panic("Gicv3::write(): unknown address %#x\n", addr);
+    }
+
+    pkt->makeAtomicResponse();
+    return delay;
+}
+
+void
+Gicv3::sendInt(uint32_t int_id)
+{
+    panic_if(int_id < Gicv3::SGI_MAX + Gicv3::PPI_MAX, "Invalid SPI!");
+    panic_if(int_id >= Gicv3::INTID_SECURE, "Invalid SPI!");
+    DPRINTF(Interrupt, "Gicv3::sendInt(): received SPI %d\n", int_id);
+    distributor->sendInt(int_id);
+}
+
+void
+Gicv3::clearInt(uint32_t number)
+{
+    distributor->intDeasserted(number);
+}
+
+void
+Gicv3::sendPPInt(uint32_t int_id, uint32_t cpu)
+{
+    panic_if(cpu >= redistributors.size(), "Invalid cpuID sending PPI!");
+    panic_if(int_id < Gicv3::SGI_MAX, "Invalid PPI!");
+    panic_if(int_id >= Gicv3::SGI_MAX + Gicv3::PPI_MAX, "Invalid PPI!");
+    DPRINTF(Interrupt, "Gicv3::sendPPInt(): received PPI %d cpuTarget %#x\n",
+            int_id, cpu);
+    redistributors[cpu]->sendPPInt(int_id);
+}
+
+void
+Gicv3::clearPPInt(uint32_t num, uint32_t cpu)
+{
+}
+
+void
+Gicv3::postInt(uint32_t cpu, ArmISA::InterruptTypes int_type)
+{
+    postDelayedInt(cpu, int_type);
+}
+
+void
+Gicv3::deassertInt(uint32_t cpu, ArmISA::InterruptTypes int_type)
+{
+    platform->intrctrl->clear(cpu, int_type, 0);
+}
+
+void
+Gicv3::postDelayedInt(uint32_t cpu, ArmISA::InterruptTypes int_type)
+{
+    platform->intrctrl->post(cpu, int_type, 0);
+}
+
+Gicv3Redistributor *
+Gicv3::getRedistributorByAffinity(uint32_t affinity)
+{
+    for (auto & redistributor : redistributors) {
+        if (redistributor->getAffinity() == affinity) {
+            return redistributor;
+        }
+    }
+
+    return nullptr;
+}
+
+void
+Gicv3::serialize(CheckpointOut & cp) const
+{
+    distributor->serializeSection(cp, "distributor");
+
+    for (uint32_t redistributor_id = 0;
+         redistributor_id < redistributors.size();
+         redistributor_id++)
+        redistributors[redistributor_id]->serializeSection(cp,
+            csprintf("redistributors.%i", redistributor_id));
+
+    for (uint32_t cpu_interface_id = 0;
+         cpu_interface_id < cpuInterfaces.size();
+         cpu_interface_id++)
+        cpuInterfaces[cpu_interface_id]->serializeSection(cp,
+            csprintf("cpuInterface.%i", cpu_interface_id));
+}
+
+void
+Gicv3::unserialize(CheckpointIn & cp)
+{
+    getSystem()->setGIC(this);
+
+    distributor->unserializeSection(cp, "distributor");
+
+    for (uint32_t redistributor_id = 0;
+         redistributor_id < redistributors.size();
+         redistributor_id++)
+        redistributors[redistributor_id]->unserializeSection(cp,
+            csprintf("redistributors.%i", redistributor_id));
+
+    for (uint32_t cpu_interface_id = 0;
+         cpu_interface_id < cpuInterfaces.size();
+         cpu_interface_id++)
+        cpuInterfaces[cpu_interface_id]->unserializeSection(cp,
+            csprintf("cpuInterface.%i", cpu_interface_id));
+}
+
+Gicv3 *
+Gicv3Params::create()
+{
+    return new Gicv3(this);
+}
diff --git a/src/dev/arm/gic_v3.hh b/src/dev/arm/gic_v3.hh
new file mode 100644
index 0000000..a242639
--- /dev/null
+++ b/src/dev/arm/gic_v3.hh
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2018 Metempsy Technology Consulting
+ * All rights reserved.
+ *
+ * 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: Jairo Balart
+ */
+
+#ifndef __DEV_ARM_GICV3_H__
+#define __DEV_ARM_GICV3_H__
+
+#include "dev/arm/base_gic.hh"
+#include "params/Gicv3.hh"
+
+class Gicv3Distributor;
+class Gicv3Redistributor;
+class Gicv3CPUInterface;
+
+class Gicv3 : public BaseGic
+{
+  protected:
+
+    Gicv3Distributor * distributor;
+    std::vector<Gicv3Redistributor *> redistributors;
+    std::vector<Gicv3CPUInterface *> cpuInterfaces;
+    AddrRange distRange;
+    AddrRange redistRange;
+    AddrRangeList addrRanges;
+
+  public:
+
+    // Special interrupt IDs
+    static const int INTID_SECURE = 1020;
+    static const int INTID_NONSECURE = 1021;
+    static const int INTID_SPURIOUS = 1023;
+
+    // Number of Software Generated Interrupts
+    static const int SGI_MAX = 16;
+    // Number of Private Peripheral Interrupts
+    static const int PPI_MAX = 16;
+
+    typedef enum {
+        INT_INACTIVE,
+        INT_PENDING,
+        INT_ACTIVE,
+        INT_ACTIVE_PENDING,
+    } IntStatus;
+
+    typedef enum {
+        G0S,
+        G1S,
+        G1NS,
+    } GroupId;
+
+    typedef enum {
+        INT_LEVEL_SENSITIVE,
+        INT_EDGE_TRIGGERED,
+    } IntTriggerType;
+
+    typedef Gicv3Params Params;
+
+    const Params *
+    params() const
+    {
+        return dynamic_cast<const Params *>(_params);
+    }
+
+    Gicv3(const Params * p);
+    ~Gicv3();
+    void init() override;
+    void initState() override;
+
+    AddrRangeList
+    getAddrRanges() const override
+    {
+        return addrRanges;
+    }
+
+    Tick read(PacketPtr pkt) override;
+    Tick write(PacketPtr pkt) override;
+    void sendInt(uint32_t int_id) override;
+    void clearInt(uint32_t int_id) override;
+    void sendPPInt(uint32_t int_id, uint32_t cpu) override;
+    void clearPPInt(uint32_t int_id, uint32_t cpu) override;
+
+    void serialize(CheckpointOut & cp) const override;
+    void unserialize(CheckpointIn & cp) override;
+
+    Gicv3Distributor *
+    getDistributor() const
+    {
+        return distributor;
+    }
+
+    Gicv3CPUInterface *
+    getCPUInterface(int cpu_id) const
+    {
+        assert(cpu_id < cpuInterfaces.size() and cpuInterfaces[cpu_id]);
+        return cpuInterfaces[cpu_id];
+    }
+
+    Gicv3Redistributor *
+    getRedistributor(ContextID context_id) const
+    {
+        assert(context_id < redistributors.size() and
+               redistributors[context_id]);
+        return redistributors[context_id];
+    }
+
+    Gicv3Redistributor * getRedistributorByAffinity(uint32_t affinity);
+    void postInt(uint32_t cpu, ArmISA::InterruptTypes int_type);
+    void postDelayedInt(uint32_t cpu, ArmISA::InterruptTypes int_type);
+    void deassertInt(uint32_t cpu, ArmISA::InterruptTypes int_type);
+
+  protected:
+
+    void reset();
+};
+
+#endif //__DEV_ARM_GICV3_H__
diff --git a/src/dev/arm/gic_v3_cpu_interface.cc b/src/dev/arm/gic_v3_cpu_interface.cc
new file mode 100644
index 0000000..7245cc6
--- /dev/null
+++ b/src/dev/arm/gic_v3_cpu_interface.cc
@@ -0,0 +1,2362 @@
+/*
+ * Copyright (c) 2018 Metempsy Technology Consulting
+ * All rights reserved.
+ *
+ * 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: Jairo Balart
+ */
+
+#include "dev/arm/gic_v3_cpu_interface.hh"
+
+#include "arch/arm/isa.hh"
+#include "debug/GIC.hh"
+#include "dev/arm/gic_v3.hh"
+#include "dev/arm/gic_v3_distributor.hh"
+#include "dev/arm/gic_v3_redistributor.hh"
+
+Gicv3CPUInterface::Gicv3CPUInterface(Gicv3 * gic, uint32_t cpu_id)
+    : BaseISADevice(),
+      gic(gic),
+      redistributor(nullptr),
+      distributor(nullptr),
+      cpuId(cpu_id)
+{
+}
+
+Gicv3CPUInterface::~Gicv3CPUInterface()
+{
+}
+
+void
+Gicv3CPUInterface::init()
+{
+    redistributor = gic->getRedistributor(cpuId);
+    distributor = gic->getDistributor();
+}
+
+void
+Gicv3CPUInterface::initState()
+{
+    reset();
+}
+
+void
+Gicv3CPUInterface::reset()
+{
+    hppi.prio = 0xff;
+}
+
+bool
+Gicv3CPUInterface::getHCREL2FMO()
+{
+    HCR hcr = isa->readMiscRegNoEffect(MISCREG_HCR_EL2);
+
+    if (hcr.tge && hcr.e2h) {
+        return false;
+    } else if (hcr.tge) {
+        return true;
+    } else {
+        return hcr.fmo;
+    }
+}
+
+bool
+Gicv3CPUInterface::getHCREL2IMO()
+{
+    HCR hcr = isa->readMiscRegNoEffect(MISCREG_HCR_EL2);
+
+    if (hcr.tge && hcr.e2h) {
+        return false;
+    } else if (hcr.tge) {
+        return true;
+    } else {
+        return hcr.imo;
+    }
+}
+
+ArmISA::MiscReg
+Gicv3CPUInterface::readMiscReg(int misc_reg)
+{
+    ArmISA::MiscReg value = isa->readMiscRegNoEffect(misc_reg);
+    bool hcr_fmo = getHCREL2FMO();
+    bool hcr_imo = getHCREL2IMO();
+
+    switch (misc_reg) {
+      case MISCREG_ICC_AP1R0:
+      case MISCREG_ICC_AP1R0_EL1: {
+          if ((currEL() == EL1) && !inSecureState() && hcr_imo) {
+              return isa->readMiscRegNoEffect(MISCREG_ICV_AP1R0_EL1);
+          }
+
+          break;
+      }
+
+      case MISCREG_ICC_AP1R1:
+      case MISCREG_ICC_AP1R1_EL1:
+
+        // only implemented if supporting 6 or more bits of priority
+      case MISCREG_ICC_AP1R2:
+      case MISCREG_ICC_AP1R2_EL1:
+
+        // only implemented if supporting 7 or more bits of priority
+      case MISCREG_ICC_AP1R3:
+      case MISCREG_ICC_AP1R3_EL1:
+        // only implemented if supporting 7 or more bits of priority
+        return 0;
+
+      case MISCREG_ICC_AP0R0:
+      case MISCREG_ICC_AP0R0_EL1: {
+          if ((currEL() == EL1) && !inSecureState() && hcr_fmo) {
+              return isa->readMiscRegNoEffect(MISCREG_ICV_AP0R0_EL1);
+          }
+
+          break;
+      }
+
+      case MISCREG_ICC_AP0R1:
+      case MISCREG_ICC_AP0R1_EL1:
+
+        // only implemented if supporting 6 or more bits of priority
+      case MISCREG_ICC_AP0R2:
+      case MISCREG_ICC_AP0R2_EL1:
+
+        // only implemented if supporting 7 or more bits of priority
+      case MISCREG_ICC_AP0R3:
+      case MISCREG_ICC_AP0R3_EL1:
+        // only implemented if supporting 7 or more bits of priority
+        return 0;
+
+      case MISCREG_ICC_IGRPEN0:
+      case MISCREG_ICC_IGRPEN0_EL1: {
+          if ((currEL() == EL1) && !inSecureState() && hcr_fmo) {
+              return isa->readMiscRegNoEffect(MISCREG_ICV_IGRPEN0_EL1);
+          }
+
+          break;
+      }
+
+      case MISCREG_ICC_IGRPEN1:
+      case MISCREG_ICC_IGRPEN1_EL1: {
+          if ((currEL() == EL1) && !inSecureState() && hcr_imo) {
+              return isa->readMiscRegNoEffect(MISCREG_ICV_IGRPEN1_EL1);
+          }
+
+          break;
+      }
+
+      case MISCREG_ICC_MGRPEN1:
+      case MISCREG_ICC_IGRPEN1_EL3: {
+          // EnableGrp1S and EnableGrp1NS are aliased with
+          // ICC_IGRPEN1_EL1_S.Enable and ICC_IGRPEN1_EL1_NS.Enable
+          bool enable_grp_1s =
+              isa->readMiscRegNoEffect(MISCREG_ICC_IGRPEN1_EL1_S) &
+              ICC_IGRPEN1_EL1_ENABLE;
+          bool enable_grp_1ns =
+              isa->readMiscRegNoEffect(MISCREG_ICC_IGRPEN1_EL1_NS) &
+              ICC_IGRPEN1_EL1_ENABLE;
+          value = 0;
+
+          if (enable_grp_1s) {
+              value |= ICC_IGRPEN1_EL3_ENABLEGRP1S;
+          }
+
+          if (enable_grp_1ns) {
+              value |= ICC_IGRPEN1_EL3_ENABLEGRP1NS;
+          }
+
+          break;
+      }
+
+      case MISCREG_ICC_RPR:
+      case MISCREG_ICC_RPR_EL1: {
+          if ((currEL() == EL1) && !inSecureState() &&
+                  (hcr_imo || hcr_fmo)) {
+              return readMiscReg(MISCREG_ICV_RPR_EL1);
+          }
+
+          uint8_t rprio = highestActivePriority();
+
+          if (haveEL(EL3) && !inSecureState() &&
+                  (isa->readMiscRegNoEffect(MISCREG_SCR_EL3) & (1U << 2))) {
+              /* NS GIC access and Group 0 is inaccessible to NS */
+              if ((rprio & 0x80) == 0) {
+                  /* NS should not see priorities in the Secure half of the
+                   * range */
+                  rprio = 0;
+              } else if (rprio != 0xff) {
+                  /* Non-idle priority: show the Non-secure view of it */
+                  rprio = (rprio << 1) & 0xff;
+              }
+          }
+
+          value = rprio;
+          break;
+      }
+
+      case MISCREG_ICV_RPR_EL1: {
+          value = virtualHighestActivePriority();
+          break;
+      }
+
+      case MISCREG_ICC_HPPIR0:
+      case MISCREG_ICC_HPPIR0_EL1: {
+          if ((currEL() == EL1) && !inSecureState() && hcr_fmo) {
+              return readMiscReg(MISCREG_ICV_HPPIR0_EL1);
+          }
+
+          value = getHPPIR0();
+          break;
+      }
+
+      case MISCREG_ICV_HPPIR0_EL1: {
+          value = Gicv3::INTID_SPURIOUS;
+          int lr_idx = getHPPVILR();
+
+          if (lr_idx >= 0) {
+              ArmISA::MiscReg lr =
+                  isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx);
+              Gicv3::GroupId group =
+                  lr & ICH_LR_EL2_GROUP ? Gicv3::G1NS : Gicv3::G0S;
+
+              if (group == Gicv3::G0S) {
+                  value = bits(lr, 31, 0);
+              }
+          }
+
+          break;
+      }
+
+      case MISCREG_ICC_HPPIR1:
+      case MISCREG_ICC_HPPIR1_EL1: {
+          if ((currEL() == EL1) && !inSecureState() && hcr_imo) {
+              return readMiscReg(MISCREG_ICV_HPPIR1_EL1);
+          }
+
+          value = getHPPIR1();
+          break;
+      }
+
+      case MISCREG_ICV_HPPIR1_EL1: {
+          value = Gicv3::INTID_SPURIOUS;
+          int lr_idx = getHPPVILR();
+
+          if (lr_idx >= 0) {
+              ArmISA::MiscReg lr =
+                  isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx);
+              Gicv3::GroupId group =
+                  lr & ICH_LR_EL2_GROUP ? Gicv3::G1NS : Gicv3::G0S;
+
+              if (group == Gicv3::G1NS) {
+                  value = bits(lr, 31, 0);
+              }
+          }
+
+          break;
+      }
+
+      case MISCREG_ICC_BPR0:
+      case MISCREG_ICC_BPR0_EL1:
+        if ((currEL() == EL1) && !inSecureState() && hcr_fmo) {
+            return readMiscReg(MISCREG_ICV_BPR0_EL1);
+        }
+
+        M5_FALLTHROUGH;
+
+      case MISCREG_ICC_BPR1:
+      case MISCREG_ICC_BPR1_EL1:
+        if ((currEL() == EL1) && !inSecureState() && hcr_imo) {
+            return readMiscReg(MISCREG_ICV_BPR1_EL1);
+        }
+
+        {
+            Gicv3::GroupId group =
+                misc_reg == MISCREG_ICC_BPR0_EL1 ? Gicv3::G0S : Gicv3::G1S;
+
+            if (group == Gicv3::G1S && !inSecureState()) {
+                group = Gicv3::G1NS;
+            }
+
+            if ((group == Gicv3::G1S) &&
+                    !isEL3OrMon() &&
+                    (isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL1_S)
+                     & ICC_CTLR_EL1_CBPR)) {
+                group = Gicv3::G0S;
+            }
+
+            bool sat_inc = false;
+
+            if ((group == Gicv3::G1NS) &&
+                    (currEL() < EL3) &&
+                    (isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL1_NS)
+                     & ICC_CTLR_EL1_CBPR)) {
+                // Reads return BPR0 + 1 saturated to 7, WI
+                group = Gicv3::G0S;
+                sat_inc = true;
+            }
+
+            uint8_t bpr;
+
+            if (group == Gicv3::G0S) {
+                bpr = isa->readMiscRegNoEffect(MISCREG_ICC_BPR0_EL1);
+            } else {
+                bpr = isa->readMiscRegNoEffect(MISCREG_ICC_BPR1_EL1);
+            }
+
+            if (sat_inc) {
+                bpr++;
+
+                if (bpr > 7) {
+                    bpr = 7;
+                }
+            }
+
+            value = bpr;
+            break;
+        }
+
+      case MISCREG_ICV_BPR0_EL1:
+      case MISCREG_ICV_BPR1_EL1: {
+          Gicv3::GroupId group =
+              misc_reg == MISCREG_ICV_BPR0_EL1 ? Gicv3::G0S : Gicv3::G1NS;
+          ArmISA::MiscReg ich_vmcr_el2 =
+              isa->readMiscRegNoEffect(MISCREG_ICH_VMCR_EL2);
+          bool sat_inc = false;
+
+          if (group == Gicv3::G1NS && (ich_vmcr_el2 & ICH_VMCR_EL2_VCBPR)) {
+              // reads return bpr0 + 1 saturated to 7, writes ignored
+              group = Gicv3::G0S;
+              sat_inc = true;
+          }
+
+          uint8_t vbpr;
+
+          if (group == Gicv3::G0S) {
+              vbpr = bits(ich_vmcr_el2, 23, 21);
+          } else {
+              vbpr = bits(ich_vmcr_el2, 20, 18);
+          }
+
+          if (sat_inc) {
+              vbpr++;
+
+              if (vbpr > 7) {
+                  vbpr = 7;
+              }
+          }
+
+          value = vbpr;
+          break;
+      }
+
+      case MISCREG_ICC_PMR:
+      case MISCREG_ICC_PMR_EL1: // Priority Mask Register
+        if ((currEL() == EL1) && !inSecureState() &&
+                (hcr_imo || hcr_fmo)) {
+            return isa->readMiscRegNoEffect(MISCREG_ICV_PMR_EL1);
+        }
+
+        if (haveEL(EL3) && !inSecureState() &&
+                (isa->readMiscRegNoEffect(MISCREG_SCR_EL3) & (1U << 2))) {
+            /* NS GIC access and Group 0 is inaccessible to NS */
+            if ((value & 0x80) == 0) {
+                /* NS should not see priorities in the Secure half of the
+                 * range */
+                value = 0;
+            } else if (value != 0xff) {
+                /* Non-idle priority: show the Non-secure view of it */
+                value = (value << 1) & 0xff;
+            }
+        }
+
+        break;
+
+      case MISCREG_ICC_IAR0:
+      case MISCREG_ICC_IAR0_EL1: { // Interrupt Acknowledge Register 0
+          if ((currEL() == EL1) && !inSecureState() && hcr_fmo) {
+              return readMiscReg(MISCREG_ICV_IAR0_EL1);
+          }
+
+          uint32_t int_id;
+
+          if (hppiCanPreempt()) {
+              int_id = getHPPIR0();
+
+              // avoid activation for special interrupts
+              if (int_id < Gicv3::INTID_SECURE) {
+                  activateIRQ(int_id, hppi.group);
+              }
+          } else {
+              int_id = Gicv3::INTID_SPURIOUS;
+          }
+
+          value = int_id;
+          break;
+      }
+
+      case MISCREG_ICV_IAR0_EL1: {
+          int lr_idx = getHPPVILR();
+          uint32_t int_id = Gicv3::INTID_SPURIOUS;
+
+          if (lr_idx >= 0) {
+              ArmISA::MiscReg lr =
+                  isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx);
+
+              if (!(lr & ICH_LR_EL2_GROUP) && hppviCanPreempt(lr_idx)) {
+                  int_id = value = bits(lr, 31, 0);
+
+                  if (int_id < Gicv3::INTID_SECURE ||
+                          int_id > Gicv3::INTID_SPURIOUS) {
+                      virtualActivateIRQ(lr_idx);
+                  } else {
+                      // Bogus... Pseudocode says:
+                      // - Move from pending to invalid...
+                      // - Return de bogus id...
+                      lr &= ~ICH_LR_EL2_STATE_PENDING_BIT;
+                      isa->setMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx,
+                              lr);
+                  }
+              }
+          }
+
+          value = int_id;
+          virtualUpdate();
+          break;
+      }
+
+      case MISCREG_ICC_IAR1:
+      case MISCREG_ICC_IAR1_EL1: { // Interrupt Acknowledge Register 1
+          if ((currEL() == EL1) && !inSecureState() && hcr_imo) {
+              return readMiscReg(MISCREG_ICV_IAR1_EL1);
+          }
+
+          uint32_t int_id;
+
+          if (hppiCanPreempt()) {
+              int_id = getHPPIR1();
+
+              // avoid activation for special interrupts
+              if (int_id < Gicv3::INTID_SECURE) {
+                  activateIRQ(int_id, hppi.group);
+              }
+          } else {
+              int_id = Gicv3::INTID_SPURIOUS;
+          }
+
+          value = int_id;
+          break;
+      }
+
+      case MISCREG_ICV_IAR1_EL1: {
+          int lr_idx = getHPPVILR();
+          uint32_t int_id = Gicv3::INTID_SPURIOUS;
+
+          if (lr_idx >= 0) {
+              ArmISA::MiscReg lr =
+                  isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx);
+
+              if (lr & ICH_LR_EL2_GROUP && hppviCanPreempt(lr_idx)) {
+                  int_id = value = bits(lr, 31, 0);
+
+                  if (int_id < Gicv3::INTID_SECURE ||
+                          int_id > Gicv3::INTID_SPURIOUS) {
+                      virtualActivateIRQ(lr_idx);
+                  } else {
+                      // Bogus... Pseudocode says:
+                      // - Move from pending to invalid...
+                      // - Return de bogus id...
+                      lr &= ~ICH_LR_EL2_STATE_PENDING_BIT;
+                      isa->setMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx,
+                              lr);
+                  }
+              }
+          }
+
+          value = int_id;
+          virtualUpdate();
+          break;
+      }
+
+      case MISCREG_ICC_SRE:
+      case MISCREG_ICC_SRE_EL1: { // System Register Enable Register
+          bool dfb;
+          bool dib;
+
+          if (haveEL(EL3) && !distributor->DS) {
+              // DIB is RO alias of ICC_SRE_EL3.DIB
+              // DFB is RO alias of ICC_SRE_EL3.DFB
+              ArmISA::MiscReg icc_sre_el3 =
+                  isa->readMiscRegNoEffect(MISCREG_ICC_SRE_EL3);
+              dfb = icc_sre_el3 & ICC_SRE_EL3_DFB;
+              dib = icc_sre_el3 & ICC_SRE_EL3_DIB;
+          } else if (haveEL(EL3) && distributor->DS) {
+              // DIB is RW alias of ICC_SRE_EL3.DIB
+              // DFB is RW alias of ICC_SRE_EL3.DFB
+              ArmISA::MiscReg icc_sre_el3 =
+                  isa->readMiscRegNoEffect(MISCREG_ICC_SRE_EL3);
+              dfb = icc_sre_el3 & ICC_SRE_EL3_DFB;
+              dib = icc_sre_el3 & ICC_SRE_EL3_DIB;
+          } else if ((!haveEL(EL3) || distributor->DS) and haveEL(EL2)) {
+              // DIB is RO alias of ICC_SRE_EL2.DIB
+              // DFB is RO alias of ICC_SRE_EL2.DFB
+              ArmISA::MiscReg icc_sre_el2 =
+                  isa->readMiscRegNoEffect(MISCREG_ICC_SRE_EL2);
+              dfb = icc_sre_el2 & ICC_SRE_EL2_DFB;
+              dib = icc_sre_el2 & ICC_SRE_EL2_DIB;
+          } else {
+              dfb = value & ICC_SRE_EL1_DFB;
+              dib = value & ICC_SRE_EL1_DIB;
+          }
+
+          value = ICC_SRE_EL1_SRE;
+
+          if (dfb) {
+              value |= ICC_SRE_EL1_DFB;
+          }
+
+          if (dib) {
+              value |= ICC_SRE_EL1_DIB;
+          }
+
+          break;
+      }
+
+      case MISCREG_ICC_HSRE:
+      case MISCREG_ICC_SRE_EL2: // System Register Enable Register
+        /*
+         * Enable [3] == 1
+         * (Secure EL1 accesses to Secure ICC_SRE_EL1 do not trap to EL2,
+         * RAO/WI)
+         * DIB [2] == 1 (IRQ bypass not supported, RAO/WI)
+         * DFB [1] == 1 (FIQ bypass not supported, RAO/WI)
+         * SRE [0] == 1 (Only system register interface supported, RAO/WI)
+         */
+        value = ICC_SRE_EL2_ENABLE | ICC_SRE_EL2_DIB | ICC_SRE_EL2_DFB |
+            ICC_SRE_EL2_SRE;
+        break;
+
+      case MISCREG_ICC_MSRE:
+      case MISCREG_ICC_SRE_EL3: // System Register Enable Register
+        /*
+         * Enable [3] == 1
+         * (Secure EL1 accesses to Secure ICC_SRE_EL1 do not trap to EL3,
+         * RAO/WI)
+         * DIB [2] == 1 (IRQ bypass not supported, RAO/WI)
+         * DFB [1] == 1 (FIQ bypass not supported, RAO/WI)
+         * SRE [0] == 1 (Only system register interface supported, RAO/WI)
+         */
+        value = ICC_SRE_EL3_ENABLE | ICC_SRE_EL3_DIB | ICC_SRE_EL3_DFB |
+            ICC_SRE_EL3_SRE;
+        break;
+
+      case MISCREG_ICC_CTLR:
+      case MISCREG_ICC_CTLR_EL1: { // Control Register
+          if ((currEL() == EL1) && !inSecureState() &&
+                  (hcr_imo || hcr_fmo)) {
+              return readMiscReg(MISCREG_ICV_CTLR_EL1);
+          }
+
+          // Add value for RO bits
+          // IDbits [13:11], 001 = 24 bits | 000 = 16 bits
+          // PRIbits [10:8], number of priority bits implemented, minus one
+          value |= ICC_CTLR_EL1_RSS | ICC_CTLR_EL1_A3V |
+              (1 << 11) | ((PRIORITY_BITS - 1) << 8);
+          break;
+      }
+
+      case MISCREG_ICV_CTLR_EL1: {
+          value = ICC_CTLR_EL1_A3V | (1 << ICC_CTLR_EL1_IDBITS_SHIFT) |
+              (7 << ICC_CTLR_EL1_PRIBITS_SHIFT);
+          ArmISA::MiscReg ich_vmcr_el2 =
+              isa->readMiscRegNoEffect(MISCREG_ICH_VMCR_EL2);
+
+          if (ich_vmcr_el2 & ICH_VMCR_EL2_VEOIM) {
+              value |= ICC_CTLR_EL1_EOIMODE;
+          }
+
+          if (ich_vmcr_el2 & ICH_VMCR_EL2_VCBPR) {
+              value |= ICC_CTLR_EL1_CBPR;
+          }
+
+          break;
+      }
+
+      case MISCREG_ICC_MCTLR:
+      case MISCREG_ICC_CTLR_EL3: {
+          // Add value for RO bits
+          // RSS [18]
+          // A3V [15]
+          // IDbits [13:11], 001 = 24 bits | 000 = 16 bits
+          // PRIbits [10:8], number of priority bits implemented, minus one
+          value |= ICC_CTLR_EL3_RSS | ICC_CTLR_EL3_A3V | (0 << 11) |
+              ((PRIORITY_BITS - 1) << 8);
+          // Aliased bits...
+          ArmISA::MiscReg icc_ctlr_el1_ns =
+              isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL1_NS);
+          ArmISA::MiscReg icc_ctlr_el1_s =
+              isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL1_S);
+
+          if (icc_ctlr_el1_ns & ICC_CTLR_EL1_EOIMODE) {
+              value |= ICC_CTLR_EL3_EOIMODE_EL1NS;
+          }
+
+          if (icc_ctlr_el1_ns & ICC_CTLR_EL1_CBPR) {
+              value |= ICC_CTLR_EL3_CBPR_EL1NS;
+          }
+
+          if (icc_ctlr_el1_s & ICC_CTLR_EL1_EOIMODE) {
+              value |= ICC_CTLR_EL3_EOIMODE_EL1S;
+          }
+
+          if (icc_ctlr_el1_s & ICC_CTLR_EL1_CBPR) {
+              value |= ICC_CTLR_EL3_CBPR_EL1S;
+          }
+
+          break;
+      }
+
+      case MISCREG_ICH_HCR:
+      case MISCREG_ICH_HCR_EL2:
+        break;
+
+      case MISCREG_ICH_AP0R0:
+      case MISCREG_ICH_AP0R0_EL2:
+        break;
+
+      case MISCREG_ICH_AP1R0:
+      case MISCREG_ICH_AP1R0_EL2:
+        break;
+
+      case MISCREG_ICH_MISR:
+      case MISCREG_ICH_MISR_EL2: {
+          value = 0;
+          // Scan list registers and fill in the U, NP and EOI bits
+          eoiMaintenanceInterruptStatus((uint32_t *) &value);
+          ArmISA::MiscReg ich_hcr_el2 =
+              isa->readMiscRegNoEffect(MISCREG_ICH_HCR_EL2);
+          ArmISA::MiscReg ich_vmcr_el2 =
+              isa->readMiscRegNoEffect(MISCREG_ICH_VMCR_EL2);
+
+          if (ich_hcr_el2 &
+                  (ICH_HCR_EL2_LRENPIE | ICH_HCR_EL2_EOICOUNT_MASK)) {
+              value |= ICH_MISR_EL2_LRENP;
+          }
+
+          if ((ich_hcr_el2 & ICH_HCR_EL2_VGRP0EIE) &&
+                  (ich_vmcr_el2 & ICH_VMCR_EL2_VENG0)) {
+              value |= ICH_MISR_EL2_VGRP0E;
+          }
+
+          if ((ich_hcr_el2 & ICH_HCR_EL2_VGRP0DIE) &&
+                  !(ich_vmcr_el2 & ICH_VMCR_EL2_VENG1)) {
+              value |= ICH_MISR_EL2_VGRP0D;
+          }
+
+          if ((ich_hcr_el2 & ICH_HCR_EL2_VGRP1EIE) &&
+                  (ich_vmcr_el2 & ICH_VMCR_EL2_VENG1)) {
+              value |= ICH_MISR_EL2_VGRP1E;
+          }
+
+          if ((ich_hcr_el2 & ICH_HCR_EL2_VGRP1DIE) &&
+                  !(ich_vmcr_el2 & ICH_VMCR_EL2_VENG1)) {
+              value |= ICH_MISR_EL2_VGRP1D;
+          }
+
+          break;
+      }
+
+      case MISCREG_ICH_VTR:
+      case MISCREG_ICH_VTR_EL2:
+        /*
+         * PRIbits [31:29]
+         * PREbits [28:26]
+         * IDbits [25:23]
+         * SEIS [22] == 0 (SEI Support)
+         * A3V [21] == 1
+         * (Non-zero values supported for Affinity 3 in SGI genearion)
+         * nV4 [20] == 0
+         * (Support for direct injection of virtual interrupts)
+         * TDS [19] == 0 (Implementation supports ICH_HCR_EL2.TDIR)
+         * ListRegs [4:0]
+         */
+        value = (16 - 1) << 0 |
+            (5 - 1) << 26 |
+            (5 - 1) << 29;
+        value =
+            ((VIRTUAL_NUM_LIST_REGS - 1) << ICH_VTR_EL2_LISTREGS_SHIFT) |
+            // ICH_VTR_EL2_TDS |
+            // ICH_VTR_EL2_NV4 |
+            ICH_VTR_EL2_A3V |
+            (1 << ICH_VTR_EL2_IDBITS_SHIFT) |
+            ((VIRTUAL_PREEMPTION_BITS - 1) << ICH_VTR_EL2_PREBITS_SHIFT) |
+            ((VIRTUAL_PRIORITY_BITS - 1) << ICH_VTR_EL2_PRIBITS_SHIFT);
+        break;
+
+      case MISCREG_ICH_EISR:
+      case MISCREG_ICH_EISR_EL2:
+        value = eoiMaintenanceInterruptStatus(nullptr);
+        break;
+
+      case MISCREG_ICH_ELRSR:
+      case MISCREG_ICH_ELRSR_EL2:
+        value = 0;
+
+        for (int lr_idx = 0; lr_idx < VIRTUAL_NUM_LIST_REGS; lr_idx++) {
+            ArmISA::MiscReg lr =
+                isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx);
+
+            if ((lr & ICH_LR_EL2_STATE_MASK) == 0 &&
+                    ((lr & ICH_LR_EL2_HW) != 0 ||
+                     (lr & ICH_LR_EL2_EOI) == 0)) {
+                value |= (1 << lr_idx);
+            }
+        }
+
+        break;
+
+      case MISCREG_ICH_LRC0 ... MISCREG_ICH_LRC15:
+        // AArch32 (maps to AArch64 MISCREG_ICH_LR<n>_EL2 high half part)
+        value = value >> 32;
+        break;
+
+      case MISCREG_ICH_LR0 ... MISCREG_ICH_LR15:
+        // AArch32 (maps to AArch64 MISCREG_ICH_LR<n>_EL2 low half part)
+        value = value & 0xffffffff;
+        break;
+
+      case MISCREG_ICH_LR0_EL2 ... MISCREG_ICH_LR15_EL2:
+        break;
+
+      case MISCREG_ICH_VMCR:
+      case MISCREG_ICH_VMCR_EL2:
+        break;
+
+      default:
+        panic("Gicv3CPUInterface::readMiscReg(): "
+                "unknown register %d (%s)",
+                misc_reg, miscRegName[misc_reg]);
+    }
+
+    DPRINTF(GIC, "Gicv3CPUInterface::readMiscReg(): "
+            "register %s value %#x\n", miscRegName[misc_reg], value);
+    return value;
+}
+
+void
+Gicv3CPUInterface::setMiscReg(int misc_reg, ArmISA::MiscReg val)
+{
+    bool do_virtual_update = false;
+    DPRINTF(GIC, "Gicv3CPUInterface::setMiscReg(): "
+            "register %s value %#x\n", miscRegName[misc_reg], val);
+    bool hcr_fmo = getHCREL2FMO();
+    bool hcr_imo = getHCREL2IMO();
+
+    switch (misc_reg) {
+      case MISCREG_ICC_AP1R0:
+      case MISCREG_ICC_AP1R0_EL1:
+        if ((currEL() == EL1) && !inSecureState() && hcr_imo) {
+            return isa->setMiscRegNoEffect(MISCREG_ICV_AP1R0_EL1, val);
+        }
+
+        break;
+
+      case MISCREG_ICC_AP1R1:
+      case MISCREG_ICC_AP1R1_EL1:
+
+        // only implemented if supporting 6 or more bits of priority
+      case MISCREG_ICC_AP1R2:
+      case MISCREG_ICC_AP1R2_EL1:
+
+        // only implemented if supporting 7 or more bits of priority
+      case MISCREG_ICC_AP1R3:
+      case MISCREG_ICC_AP1R3_EL1:
+        // only implemented if supporting 7 or more bits of priority
+        break;
+
+      case MISCREG_ICC_AP0R0:
+      case MISCREG_ICC_AP0R0_EL1:
+        if ((currEL() == EL1) && !inSecureState() && hcr_fmo) {
+            return isa->setMiscRegNoEffect(MISCREG_ICV_AP0R0_EL1, val);
+        }
+
+        break;
+
+      case MISCREG_ICC_AP0R1:
+      case MISCREG_ICC_AP0R1_EL1:
+
+        // only implemented if supporting 6 or more bits of priority
+      case MISCREG_ICC_AP0R2:
+      case MISCREG_ICC_AP0R2_EL1:
+
+        // only implemented if supporting 7 or more bits of priority
+      case MISCREG_ICC_AP0R3:
+      case MISCREG_ICC_AP0R3_EL1:
+        // only implemented if supporting 7 or more bits of priority
+        break;
+
+      case MISCREG_ICC_EOIR0:
+      case MISCREG_ICC_EOIR0_EL1: { // End Of Interrupt Register 0
+          if ((currEL() == EL1) && !inSecureState() && hcr_fmo) {
+              return setMiscReg(MISCREG_ICV_EOIR0_EL1, val);
+          }
+
+          int int_id = val & 0xffffff;
+
+          // avoid activation for special interrupts
+          if (int_id >= Gicv3::INTID_SECURE) {
+              return;
+          }
+
+          Gicv3::GroupId group = Gicv3::G0S;
+
+          if (highestActiveGroup() != group) {
+              return;
+          }
+
+          dropPriority(group);
+
+          if (!isEOISplitMode()) {
+              deactivateIRQ(int_id, group);
+          }
+
+          break;
+      }
+
+      case MISCREG_ICV_EOIR0_EL1: {
+          int int_id = val & 0xffffff;
+
+          // avoid deactivation for special interrupts
+          if (int_id >= Gicv3::INTID_SECURE &&
+                  int_id <= Gicv3::INTID_SPURIOUS) {
+              return;
+          }
+
+          uint8_t drop_prio = virtualDropPriority();
+
+          if (drop_prio == 0xff) {
+              return;
+          }
+
+          int lr_idx = virtualFindActive(int_id);
+
+          if (lr_idx < 0) {
+              // No LR found matching
+              virtualIncrementEOICount();
+          } else {
+              ArmISA::MiscReg lr =
+                  isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx);
+              Gicv3::GroupId lr_group =
+                  lr & ICH_LR_EL2_GROUP ? Gicv3::G1NS : Gicv3::G0S;
+              uint8_t lr_group_prio = bits(lr, 55, 48) & 0xf8;
+
+              if (lr_group == Gicv3::G0S && lr_group_prio == drop_prio) {
+                  //JAIRO if (!virtualIsEOISplitMode())
+                  {
+                      virtualDeactivateIRQ(lr_idx);
+                  }
+              }
+          }
+
+          virtualUpdate();
+          break;
+      }
+
+      case MISCREG_ICC_EOIR1:
+      case MISCREG_ICC_EOIR1_EL1: { // End Of Interrupt Register 1
+          if ((currEL() == EL1) && !inSecureState() && hcr_imo) {
+              return setMiscReg(MISCREG_ICV_EOIR1_EL1, val);
+          }
+
+          int int_id = val & 0xffffff;
+
+          // avoid deactivation for special interrupts
+          if (int_id >= Gicv3::INTID_SECURE) {
+              return;
+          }
+
+          Gicv3::GroupId group =
+              inSecureState() ? Gicv3::G1S : Gicv3::G1NS;
+
+          if (highestActiveGroup() == Gicv3::G0S) {
+              return;
+          }
+
+          if (distributor->DS == 0) {
+              if (highestActiveGroup() == Gicv3::G1S && !inSecureState()) {
+                  return;
+              } else if (highestActiveGroup() == Gicv3::G1NS &&
+                      !(!inSecureState() or (currEL() == EL3))) {
+                  return;
+              }
+          }
+
+          dropPriority(group);
+
+          if (!isEOISplitMode()) {
+              deactivateIRQ(int_id, group);
+          }
+
+          break;
+      }
+
+      case MISCREG_ICV_EOIR1_EL1: {
+          int int_id = val & 0xffffff;
+
+          // avoid deactivation for special interrupts
+          if (int_id >= Gicv3::INTID_SECURE &&
+                  int_id <= Gicv3::INTID_SPURIOUS) {
+              return;
+          }
+
+          uint8_t drop_prio = virtualDropPriority();
+
+          if (drop_prio == 0xff) {
+              return;
+          }
+
+          int lr_idx = virtualFindActive(int_id);
+
+          if (lr_idx < 0) {
+              // No LR found matching
+              virtualIncrementEOICount();
+          } else {
+              ArmISA::MiscReg lr =
+                  isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx);
+              Gicv3::GroupId lr_group =
+                  lr & ICH_LR_EL2_GROUP ? Gicv3::G1NS : Gicv3::G0S;
+              uint8_t lr_group_prio = bits(lr, 55, 48) & 0xf8;
+
+              if (lr_group == Gicv3::G1NS && lr_group_prio == drop_prio) {
+                  if (!virtualIsEOISplitMode()) {
+                      virtualDeactivateIRQ(lr_idx);
+                  }
+              }
+          }
+
+          virtualUpdate();
+          break;
+      }
+
+      case MISCREG_ICC_DIR:
+      case MISCREG_ICC_DIR_EL1: { // Deactivate Interrupt Register
+          if ((currEL() == EL1) && !inSecureState() &&
+                  (hcr_imo || hcr_fmo)) {
+              return setMiscReg(MISCREG_ICV_DIR_EL1, val);
+          }
+
+          int int_id = val & 0xffffff;
+
+          // avoid deactivation for special interrupts
+          if (int_id >= Gicv3::INTID_SECURE) {
+              return;
+          }
+
+          if (!isEOISplitMode()) {
+              return;
+          }
+
+          /*
+           * Check whether we're allowed to deactivate.
+           * These checks are correspond to the spec's pseudocode.
+           */
+          Gicv3::GroupId group =
+              int_id >= 32 ? distributor->getIntGroup(int_id) :
+              redistributor->getIntGroup(int_id);
+          bool irq_is_grp0 = group == Gicv3::G0S;
+          bool single_sec_state = distributor->DS;
+          bool irq_is_secure = !single_sec_state && (group != Gicv3::G1NS);
+          SCR scr_el3 = isa->readMiscRegNoEffect(MISCREG_SCR_EL3);
+          bool route_fiq_to_el3 = scr_el3.fiq;
+          bool route_irq_to_el3 = scr_el3.irq;
+          bool route_fiq_to_el2 = hcr_fmo;
+          bool route_irq_to_el2 = hcr_imo;
+
+          switch (currEL()) {
+            case EL3:
+              break;
+
+            case EL2:
+              if (single_sec_state && irq_is_grp0 && !route_fiq_to_el3) {
+                  break;
+              }
+
+              if (!irq_is_secure && !irq_is_grp0 && !route_irq_to_el3) {
+                  break;
+              }
+
+              return;
+
+            case EL1:
+              if (!isSecureBelowEL3()) {
+                  if (single_sec_state && irq_is_grp0 &&
+                          !route_fiq_to_el3 && !route_fiq_to_el2) {
+                      break;
+                  }
+
+                  if (!irq_is_secure && !irq_is_grp0 &&
+                          !route_irq_to_el3 && !route_irq_to_el2) {
+                      break;
+                  }
+              } else {
+                  if (irq_is_grp0 && !route_fiq_to_el3) {
+                      break;
+                  }
+
+                  if (!irq_is_grp0 &&
+                          (!irq_is_secure || !single_sec_state) &&
+                          !route_irq_to_el3) {
+                      break;
+                  }
+              }
+
+              return;
+
+            default:
+              break;
+          }
+
+          deactivateIRQ(int_id, group);
+          break;
+      }
+
+      case MISCREG_ICV_DIR_EL1: {
+          int int_id = val & 0xffffff;
+
+          // avoid deactivation for special interrupts
+          if (int_id >= Gicv3::INTID_SECURE &&
+                  int_id <= Gicv3::INTID_SPURIOUS) {
+              return;
+          }
+
+          if (!virtualIsEOISplitMode()) {
+              return;
+          }
+
+          int lr_idx = virtualFindActive(int_id);
+
+          if (lr_idx < 0) {
+              // No LR found matching
+              virtualIncrementEOICount();
+          } else {
+              virtualDeactivateIRQ(lr_idx);
+          }
+
+          virtualUpdate();
+          break;
+      }
+
+      case MISCREG_ICC_BPR0:
+      case MISCREG_ICC_BPR0_EL1: // Binary Point Register 0
+      case MISCREG_ICC_BPR1:
+      case MISCREG_ICC_BPR1_EL1: { // Binary Point Register 1
+          if ((currEL() == EL1) && !inSecureState()) {
+              if (misc_reg == MISCREG_ICC_BPR0_EL1 && hcr_fmo) {
+                  return setMiscReg(MISCREG_ICV_BPR0_EL1, val);
+              } else if (misc_reg == MISCREG_ICC_BPR1_EL1 && hcr_imo) {
+                  return setMiscReg(MISCREG_ICV_BPR1_EL1, val);
+              }
+          }
+
+          Gicv3::GroupId group =
+              misc_reg == MISCREG_ICC_BPR0_EL1 ? Gicv3::G0S : Gicv3::G1S;
+
+          if (group == Gicv3::G1S && !inSecureState()) {
+              group = Gicv3::G1NS;
+          }
+
+          if ((group == Gicv3::G1S) &&
+                  !isEL3OrMon() &&
+                  (isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL1_S) &
+                   ICC_CTLR_EL1_CBPR)) {
+              group = Gicv3::G0S;
+          }
+
+          if ((group == Gicv3::G1NS) &&
+                  (currEL() < EL3) &&
+                  (isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL1_NS) &
+                   ICC_CTLR_EL1_CBPR)) {
+              // Reads return BPR0 + 1 saturated to 7, WI
+              return;
+          }
+
+          uint8_t min_val = (group == Gicv3::G1NS) ?
+              GIC_MIN_BPR_NS : GIC_MIN_BPR;
+          val &= 0x7;
+
+          if (val < min_val) {
+              val = min_val;
+          }
+
+          break;
+      }
+
+      case MISCREG_ICV_BPR0_EL1:
+      case MISCREG_ICV_BPR1_EL1: {
+          Gicv3::GroupId group =
+              misc_reg == MISCREG_ICV_BPR0_EL1 ? Gicv3::G0S : Gicv3::G1NS;
+          ArmISA::MiscReg ich_vmcr_el2 =
+              isa->readMiscRegNoEffect(MISCREG_ICH_VMCR_EL2);
+
+          if (group == Gicv3::G1NS && (ich_vmcr_el2 & ICH_VMCR_EL2_VCBPR)) {
+              // reads return bpr0 + 1 saturated to 7, writes ignored
+              return;
+          }
+
+          uint8_t min_VPBR = 7 - VIRTUAL_PREEMPTION_BITS;
+
+          if (group != Gicv3::G0S) {
+              min_VPBR++;
+          }
+
+          if (val < min_VPBR) {
+              val = min_VPBR;
+          }
+
+          if (group == Gicv3::G0S) {
+              ich_vmcr_el2 = insertBits(ich_vmcr_el2,
+                      ICH_VMCR_EL2_VBPR0_SHIFT + 2, ICH_VMCR_EL2_VBPR0_SHIFT,
+                      val);
+          } else {
+              ich_vmcr_el2 = insertBits(ich_vmcr_el2,
+                      ICH_VMCR_EL2_VBPR1_SHIFT + 2, ICH_VMCR_EL2_VBPR1_SHIFT,
+                      val);
+          }
+
+          isa->setMiscRegNoEffect(MISCREG_ICH_VMCR_EL2, ich_vmcr_el2);
+          do_virtual_update = true;
+          break;
+      }
+
+      case MISCREG_ICC_CTLR:
+      case MISCREG_ICC_CTLR_EL1: { // Control Register
+          if ((currEL() == EL1) && !inSecureState() &&
+                  (hcr_imo || hcr_fmo)) {
+              return setMiscReg(MISCREG_ICV_CTLR_EL1, val);
+          }
+
+          /*
+           * RSS is RO.
+           * A3V is RO.
+           * SEIS is RO.
+           * IDbits is RO.
+           * PRIbits is RO.
+           * If EL3 is implemented and GICD_CTLR.DS == 0, then PMHE is RO.
+           * So, only CBPR[0] and EOIMODE[1] are RW.
+           * If EL3 is implemented and GICD_CTLR.DS == 0, then CBPR is RO.
+           */
+          uint64_t mask;
+
+          if (haveEL(EL3) and distributor->DS == 0) {
+              mask = ICC_CTLR_EL1_EOIMODE;
+          } else if (haveEL(EL3) and distributor->DS == 1) {
+              mask = ICC_CTLR_EL1_PMHE | ICC_CTLR_EL1_CBPR |
+                  ICC_CTLR_EL1_EOIMODE;
+          } else {
+              mask = ICC_CTLR_EL1_CBPR | ICC_CTLR_EL1_EOIMODE;
+          }
+
+          ArmISA::MiscReg old_val =
+              isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL1);
+          old_val &= ~mask;
+          val = old_val | (val & mask);
+          break;
+      }
+
+      case MISCREG_ICV_CTLR_EL1: {
+          ArmISA::MiscReg ich_vmcr_el2 =
+              isa->readMiscRegNoEffect(MISCREG_ICH_VMCR_EL2);
+          ich_vmcr_el2 = insertBits(ich_vmcr_el2, ICH_VMCR_EL2_VCBPR_SHIFT,
+                  val & ICC_CTLR_EL1_CBPR ? 1 : 0);
+          ich_vmcr_el2 = insertBits(ich_vmcr_el2, ICH_VMCR_EL2_VEOIM_SHIFT,
+                  val & ICC_CTLR_EL1_EOIMODE ? 1 : 0);
+          isa->setMiscRegNoEffect(MISCREG_ICH_VMCR_EL2, ich_vmcr_el2);
+          do_virtual_update = true;
+          break;
+      }
+
+      case MISCREG_ICC_MCTLR:
+      case MISCREG_ICC_CTLR_EL3: {
+          ArmISA::MiscReg icc_ctlr_el1_s =
+              isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL1_S);
+          ArmISA::MiscReg icc_ctlr_el1_ns =
+              isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL1_NS);
+
+          // ICC_CTLR_EL1(NS).EOImode is an alias of
+          // ICC_CTLR_EL3.EOImode_EL1NS
+          if (val & ICC_CTLR_EL3_EOIMODE_EL1NS) {
+              icc_ctlr_el1_ns |= ICC_CTLR_EL1_EOIMODE;
+          } else {
+              icc_ctlr_el1_ns &= ~ICC_CTLR_EL1_EOIMODE;
+          }
+
+          // ICC_CTLR_EL1(NS).CBPR is an alias of ICC_CTLR_EL3.CBPR_EL1NS
+          if (val & ICC_CTLR_EL3_CBPR_EL1NS) {
+              icc_ctlr_el1_ns |= ICC_CTLR_EL1_CBPR;
+          } else {
+              icc_ctlr_el1_ns &= ~ICC_CTLR_EL1_CBPR;
+          }
+
+          // ICC_CTLR_EL1(S).EOImode is an alias of ICC_CTLR_EL3.EOImode_EL1S
+          if (val & ICC_CTLR_EL3_EOIMODE_EL1S) {
+              icc_ctlr_el1_s |= ICC_CTLR_EL1_EOIMODE;
+          } else {
+              icc_ctlr_el1_s &= ~ICC_CTLR_EL1_EOIMODE;
+          }
+
+          // ICC_CTLR_EL1(S).CBPR is an alias of ICC_CTLR_EL3.CBPR_EL1S
+          if (val & ICC_CTLR_EL3_CBPR_EL1S) {
+              icc_ctlr_el1_s |= ICC_CTLR_EL1_CBPR;
+          } else {
+              icc_ctlr_el1_s &= ~ICC_CTLR_EL1_CBPR;
+          }
+
+          isa->setMiscRegNoEffect(MISCREG_ICC_CTLR_EL1_S, icc_ctlr_el1_s);
+          isa->setMiscRegNoEffect(MISCREG_ICC_CTLR_EL1_NS, icc_ctlr_el1_ns);
+          // Only ICC_CTLR_EL3_EOIMODE_EL3 is writable
+          ArmISA::MiscReg old_icc_ctlr_el3 =
+              isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL3);
+          old_icc_ctlr_el3 &= ~(ICC_CTLR_EL3_EOIMODE_EL3 | ICC_CTLR_EL3_RM);
+          val = old_icc_ctlr_el3 |
+              (val & (ICC_CTLR_EL3_EOIMODE_EL3 | ICC_CTLR_EL3_RM));
+          break;
+      }
+
+      case MISCREG_ICC_PMR:
+      case MISCREG_ICC_PMR_EL1: { // Priority Mask Register
+          if ((currEL() == EL1) && !inSecureState() &&
+                  (hcr_imo || hcr_fmo)) {
+              return isa->setMiscRegNoEffect(MISCREG_ICV_PMR_EL1, val);
+          }
+
+          val &= 0xff;
+          SCR scr_el3 = isa->readMiscRegNoEffect(MISCREG_SCR_EL3);
+
+          if (haveEL(EL3) && !inSecureState() && (scr_el3.fiq)) {
+              /*
+               * NS access and Group 0 is inaccessible to NS: return the
+               * NS view of the current priority
+               */
+              ArmISA::MiscReg old_icc_pmr_el1 =
+                  isa->readMiscRegNoEffect(MISCREG_ICC_PMR_EL1);
+
+              if (!(old_icc_pmr_el1 & 0x80)) {
+                  /* Current PMR in the secure range, don't allow NS to
+                   * change it */
+                  return;
+              }
+
+              val = (val >> 1) | 0x80;
+          }
+
+          val &= ~0U << (8 - PRIORITY_BITS);
+          break;
+      }
+
+      case MISCREG_ICC_IGRPEN0:
+      case MISCREG_ICC_IGRPEN0_EL1: { // Interrupt Group 0 Enable Register
+          if ((currEL() == EL1) && !inSecureState() && hcr_fmo) {
+              return setMiscReg(MISCREG_ICV_IGRPEN0_EL1, val);
+          }
+
+          break;
+      }
+
+      case MISCREG_ICV_IGRPEN0_EL1: {
+          bool enable = val & 0x1;
+          ArmISA::MiscReg ich_vmcr_el2 =
+              isa->readMiscRegNoEffect(MISCREG_ICH_VMCR_EL2);
+          ich_vmcr_el2 = insertBits(ich_vmcr_el2,
+                  ICH_VMCR_EL2_VENG0_SHIFT, enable);
+          isa->setMiscRegNoEffect(MISCREG_ICH_VMCR_EL2, ich_vmcr_el2);
+          virtualUpdate();
+          return;
+      }
+
+      case MISCREG_ICC_IGRPEN1:
+      case MISCREG_ICC_IGRPEN1_EL1: { // Interrupt Group 1 Enable Register
+          if ((currEL() == EL1) && !inSecureState() && hcr_imo) {
+              return setMiscReg(MISCREG_ICV_IGRPEN1_EL1, val);
+          }
+
+          break;
+      }
+
+      case MISCREG_ICV_IGRPEN1_EL1: {
+          bool enable = val & 0x1;
+          ArmISA::MiscReg ich_vmcr_el2 =
+              isa->readMiscRegNoEffect(MISCREG_ICH_VMCR_EL2);
+          ich_vmcr_el2 = insertBits(ich_vmcr_el2,
+                  ICH_VMCR_EL2_VENG1_SHIFT, enable);
+          isa->setMiscRegNoEffect(MISCREG_ICH_VMCR_EL2, ich_vmcr_el2);
+          virtualUpdate();
+          return;
+      }
+
+      case MISCREG_ICC_MGRPEN1:
+      case MISCREG_ICC_IGRPEN1_EL3: {
+          // EnableGrp1S and EnableGrp1NS are aliased with
+          // ICC_IGRPEN1_EL1_S.Enable and ICC_IGRPEN1_EL1_NS.Enable
+          bool enable_grp_1s = val & ICC_IGRPEN1_EL3_ENABLEGRP1S;
+          bool enable_grp_1ns = val & ICC_IGRPEN1_EL3_ENABLEGRP1NS;
+          isa->setMiscRegNoEffect(MISCREG_ICC_IGRPEN1_EL1_S, enable_grp_1s);
+          isa->setMiscRegNoEffect(MISCREG_ICC_IGRPEN1_EL1_NS, enable_grp_1ns);
+          return;
+      }
+
+        // Software Generated Interrupt Group 0 Register
+      case MISCREG_ICC_SGI0R:
+      case MISCREG_ICC_SGI0R_EL1:
+
+        // Software Generated Interrupt Group 1 Register
+      case MISCREG_ICC_SGI1R:
+      case MISCREG_ICC_SGI1R_EL1:
+
+        // Alias Software Generated Interrupt Group 1 Register
+      case MISCREG_ICC_ASGI1R:
+      case MISCREG_ICC_ASGI1R_EL1: {
+          bool ns = !inSecureState();
+          Gicv3::GroupId group;
+
+          if (misc_reg == MISCREG_ICC_SGI1R_EL1) {
+              group = ns ? Gicv3::G1NS : Gicv3::G1S;
+          } else if (misc_reg == MISCREG_ICC_ASGI1R_EL1) {
+              group = ns ? Gicv3::G1S : Gicv3::G1NS;
+          } else {
+              group = Gicv3::G0S;
+          }
+
+          if (distributor->DS && group == Gicv3::G1S) {
+              group = Gicv3::G0S;
+          }
+
+          uint8_t aff3 = bits(val, 55, 48);
+          uint8_t aff2 = bits(val, 39, 32);
+          uint8_t aff1 = bits(val, 23, 16);;
+          uint16_t target_list = bits(val, 15, 0);
+          uint32_t int_id = bits(val, 27, 24);
+          bool irm = bits(val, 40, 40);
+          uint8_t rs = bits(val, 47, 44);
+
+          for (int i = 0; i < gic->getSystem()->numContexts(); i++) {
+              Gicv3Redistributor * redistributor_i =
+                  gic->getRedistributor(i);
+              uint32_t affinity_i = redistributor_i->getAffinity();
+
+              if (irm) {
+                  // Interrupts routed to all PEs in the system,
+                  // excluding "self"
+                  if (affinity_i == redistributor->getAffinity()) {
+                      continue;
+                  }
+              } else {
+                  // Interrupts routed to the PEs specified by
+                  // Aff3.Aff2.Aff1.<target list>
+                  if ((affinity_i >> 8) !=
+                          ((aff3 << 16) | (aff2 << 8) | (aff1 << 0))) {
+                      continue;
+                  }
+
+                  uint8_t aff0_i = bits(affinity_i, 7, 0);
+
+                  if (!(aff0_i >= rs * 16 && aff0_i < (rs + 1) * 16 &&
+                              ((0x1 << (aff0_i - rs * 16)) & target_list))) {
+                      continue;
+                  }
+              }
+
+              redistributor_i->sendSGI(int_id, group, ns);
+          }
+
+          break;
+      }
+
+      case MISCREG_ICC_SRE:
+      case MISCREG_ICC_SRE_EL1: { // System Register Enable Register EL1
+          if (!(val & ICC_SRE_EL1_SRE)) {
+              warn("Gicv3CPUInterface::setMiscReg(): "
+                      "ICC_SRE_EL*.SRE is RAO/WI, legacy not supported!\n");
+          }
+
+          bool dfb = val & ICC_SRE_EL1_DFB;
+          bool dib = val & ICC_SRE_EL1_DIB;
+
+          if (haveEL(EL3) && !distributor->DS) {
+              // DIB is RO alias of ICC_SRE_EL3.DIB
+              // DFB is RO alias of ICC_SRE_EL3.DFB
+          } else if (haveEL(EL3) && distributor->DS) {
+              // DIB is RW alias of ICC_SRE_EL3.DIB
+              // DFB is RW alias of ICC_SRE_EL3.DFB
+              ArmISA::MiscReg icc_sre_el3 =
+                  isa->readMiscRegNoEffect(MISCREG_ICC_SRE_EL3);
+              icc_sre_el3 = insertBits(icc_sre_el3, ICC_SRE_EL3_DFB, dfb);
+              icc_sre_el3 = insertBits(icc_sre_el3, ICC_SRE_EL3_DIB, dib);
+              isa->setMiscRegNoEffect(MISCREG_ICC_SRE_EL3, icc_sre_el3);
+          } else if ((!haveEL(EL3) || distributor->DS) and haveEL(EL2)) {
+              // DIB is RO alias of ICC_SRE_EL2.DIB
+              // DFB is RO alias of ICC_SRE_EL2.DFB
+          } else {
+              isa->setMiscRegNoEffect(misc_reg, val);
+          }
+
+          return;
+      }
+
+      case MISCREG_ICC_HSRE:
+      case MISCREG_ICC_SRE_EL2: // System Register Enable Register EL2
+      case MISCREG_ICC_MSRE:
+      case MISCREG_ICC_SRE_EL3: // System Register Enable Register EL3
+        if (!(val & (1 << 0))) {
+            warn("Gicv3CPUInterface::setMiscReg(): "
+                    "ICC_SRE_EL*.SRE is RAO/WI, legacy not supported!\n");
+        }
+
+        // All bits are RAO/WI
+        break;
+
+      case MISCREG_ICH_HCR:
+      case MISCREG_ICH_HCR_EL2:
+        val &= ICH_HCR_EL2_EN | ICH_HCR_EL2_UIE | ICH_HCR_EL2_LRENPIE |
+            ICH_HCR_EL2_NPIE | ICH_HCR_EL2_VGRP0EIE |
+            ICH_HCR_EL2_VGRP0DIE | ICH_HCR_EL2_VGRP1EIE |
+            ICH_HCR_EL2_VGRP1DIE | ICH_HCR_EL2_TC | ICH_HCR_EL2_TALL0 |
+            ICH_HCR_EL2_TALL1 | ICH_HCR_EL2_TDIR |
+            ICH_HCR_EL2_EOICOUNT_MASK;
+        do_virtual_update = true;
+        break;
+
+      case MISCREG_ICH_LRC0 ... MISCREG_ICH_LRC15:
+        // AArch32 (maps to AArch64 MISCREG_ICH_LR<n>_EL2 high half part)
+        {
+            // Enforce RES0 bits in priority field, 5 of 8 bits used
+            val = insertBits(val, ICH_LRC_PRIORITY_SHIFT + 2,
+                    ICH_LRC_PRIORITY_SHIFT, 0);
+            ArmISA::MiscReg old_val = isa->readMiscRegNoEffect(misc_reg);
+            val = (old_val & 0xffffffff) | (val << 32);
+            do_virtual_update = true;
+            break;
+        }
+
+      case MISCREG_ICH_LR0 ... MISCREG_ICH_LR15: {
+          // AArch32 (maps to AArch64 MISCREG_ICH_LR<n>_EL2 low half part)
+          ArmISA::MiscReg old_val = isa->readMiscRegNoEffect(misc_reg);
+          val = (old_val & 0xffffffff00000000) | (val & 0xffffffff);
+          do_virtual_update = true;
+          break;
+      }
+
+      case MISCREG_ICH_LR0_EL2 ... MISCREG_ICH_LR15_EL2: { // AArch64
+          // Enforce RES0 bits in priority field, 5 of 8 bits used
+          val = insertBits(val, ICH_LR_EL2_PRIORITY_SHIFT + 2,
+                  ICH_LR_EL2_PRIORITY_SHIFT, 0);
+          do_virtual_update = true;
+          break;
+      }
+
+      case MISCREG_ICH_VMCR:
+      case MISCREG_ICH_VMCR_EL2: {
+          val &= ICH_VMCR_EL2_VENG0 | ICH_VMCR_EL2_VENG1 |
+              ICH_VMCR_EL2_VCBPR | ICH_VMCR_EL2_VEOIM |
+              ICH_VMCR_EL2_VBPR1_MASK | ICH_VMCR_EL2_VBPR0_MASK |
+              ICH_VMCR_EL2_VPMR_MASK;
+          val |= ICH_VMCR_EL2_VFIQEN; // RES1
+          // Check VBPRs against minimun allowed value
+          uint8_t vbpr0 = bits(val, 23, 21);
+          uint8_t vbpr1 = bits(val, 20, 18);
+          uint8_t min_vpr0 = 7 - VIRTUAL_PREEMPTION_BITS;
+          uint8_t min_vpr1 = min_vpr0 + 1;
+          vbpr0 = vbpr0 < min_vpr0 ? min_vpr0 : vbpr0;
+          vbpr1 = vbpr1 < min_vpr1 ? min_vpr1 : vbpr1;
+          val = insertBits(val, ICH_VMCR_EL2_VBPR0_SHIFT + 2,
+                  ICH_VMCR_EL2_VBPR0_SHIFT, vbpr0);
+          val = insertBits(val, ICH_VMCR_EL2_VBPR1_SHIFT + 2,
+                  ICH_VMCR_EL2_VBPR1_SHIFT, vbpr1);
+          break;
+      }
+
+      case MISCREG_ICH_AP0R0 ... MISCREG_ICH_AP0R3:
+      case MISCREG_ICH_AP0R0_EL2 ... MISCREG_ICH_AP0R3_EL2:
+      case MISCREG_ICH_AP1R0 ... MISCREG_ICH_AP1R3:
+      case MISCREG_ICH_AP1R0_EL2 ... MISCREG_ICH_AP1R3_EL2:
+        break;
+
+      default:
+        panic("Gicv3CPUInterface::setMiscReg(): "
+                "unknown register %d (%s)",
+                misc_reg, miscRegName[misc_reg]);
+    }
+
+    isa->setMiscRegNoEffect(misc_reg, val);
+
+    if (do_virtual_update) {
+        virtualUpdate();
+    }
+}
+
+int
+Gicv3CPUInterface::virtualFindActive(uint32_t int_id)
+{
+    for (uint32_t lr_idx = 0; lr_idx < VIRTUAL_NUM_LIST_REGS; lr_idx++) {
+        ArmISA::MiscReg lr =
+            isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx);
+        uint32_t lr_intid = bits(lr, 31, 0);
+
+        if ((lr & ICH_LR_EL2_STATE_ACTIVE_BIT) && lr_intid == int_id) {
+            return lr_idx;
+        }
+    }
+
+    return -1;
+}
+
+uint32_t
+Gicv3CPUInterface::getHPPIR0()
+{
+    if (hppi.prio == 0xff) {
+        return Gicv3::INTID_SPURIOUS;
+    }
+
+    bool irq_is_secure = !distributor->DS && hppi.group != Gicv3::G1NS;
+
+    if ((hppi.group != Gicv3::G0S) && isEL3OrMon()) {
+        /* Indicate to EL3 that there's a Group 1 interrupt for the
+         * other state pending.
+         */
+        return irq_is_secure ? Gicv3::INTID_SECURE : Gicv3::INTID_NONSECURE;
+    }
+
+    if ((hppi.group != Gicv3::G0S)) { // && !isEL3OrMon())
+        return Gicv3::INTID_SPURIOUS;
+    }
+
+    if (irq_is_secure && !inSecureState()) {
+        // Secure interrupts not visible in Non-secure
+        return Gicv3::INTID_SPURIOUS;
+    }
+
+    return hppi.intid;
+}
+
+uint32_t
+Gicv3CPUInterface::getHPPIR1()
+{
+    if (hppi.prio == 0xff) {
+        return Gicv3::INTID_SPURIOUS;
+    }
+
+    //if ((currEL() == EL3) && ICC_CTLR_EL3_RM)
+    if ((currEL() == EL3) &&
+            isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL3) & ICC_CTLR_EL3_RM) {
+        if (hppi.group == Gicv3::G0S) {
+            return Gicv3::INTID_SECURE;
+        } else if (hppi.group == Gicv3::G1NS) {
+            return Gicv3::INTID_NONSECURE;
+        }
+    }
+
+    if (hppi.group == Gicv3::G0S) {
+        return Gicv3::INTID_SPURIOUS;
+    }
+
+    bool irq_is_secure = (distributor->DS == 0) && (hppi.group != Gicv3::G1NS);
+
+    if (irq_is_secure) {
+        if (!inSecureState()) {
+            // Secure interrupts not visible in Non-secure
+            return Gicv3::INTID_SPURIOUS;
+        }
+    } else if (!isEL3OrMon() && inSecureState()) {
+        // Group 1 non-secure interrupts not visible in Secure EL1
+        return Gicv3::INTID_SPURIOUS;
+    }
+
+    return hppi.intid;
+}
+
+void
+Gicv3CPUInterface::dropPriority(Gicv3::GroupId group)
+{
+    int apr_misc_reg;
+    ArmISA::MiscReg apr;
+    apr_misc_reg = group == Gicv3::G0S ?
+                   MISCREG_ICC_AP0R0_EL1 : MISCREG_ICC_AP1R0_EL1;
+    apr = isa->readMiscRegNoEffect(apr_misc_reg);
+
+    if (apr) {
+        /* Clear the lowest set bit */
+        apr &= apr - 1;
+        isa->setMiscRegNoEffect(apr_misc_reg, apr);
+    }
+
+    update();
+}
+
+uint8_t
+Gicv3CPUInterface::virtualDropPriority()
+{
+    /* Drop the priority of the currently active virtual interrupt
+     * (favouring group 0 if there is a set active bit at
+     * the same priority for both group 0 and group 1).
+     * Return the priority value for the bit we just cleared,
+     * or 0xff if no bits were set in the AP registers at all.
+     * Note that though the ich_apr[] are uint64_t only the low
+     * 32 bits are actually relevant.
+     */
+    int apr_max = 1 << (VIRTUAL_PREEMPTION_BITS - 5);
+
+    for (int i = 0; i < apr_max; i++) {
+        ArmISA::MiscReg vapr0 =
+            isa->readMiscRegNoEffect(MISCREG_ICH_AP0R0_EL2 + i);
+        ArmISA::MiscReg vapr1 =
+            isa->readMiscRegNoEffect(MISCREG_ICH_AP1R0_EL2 + i);
+
+        if (!vapr0 && !vapr1) {
+            continue;
+        }
+
+        int vapr0_count = ctz32(vapr0);
+        int vapr1_count = ctz32(vapr1);
+
+        if (vapr0_count <= vapr1_count) {
+            /* Clear the lowest set bit */
+            vapr0 &= vapr0 - 1;
+            isa->setMiscRegNoEffect(MISCREG_ICH_AP0R0_EL2 + i, vapr0);
+            return (vapr0_count + i * 32) << (GIC_MIN_VBPR + 1);
+        } else {
+            /* Clear the lowest set bit */
+            vapr1 &= vapr1 - 1;
+            isa->setMiscRegNoEffect(MISCREG_ICH_AP1R0_EL2 + i, vapr1);
+            return (vapr1_count + i * 32) << (GIC_MIN_VBPR + 1);
+        }
+    }
+
+    return 0xff;
+}
+
+void
+Gicv3CPUInterface::activateIRQ(uint32_t int_id, Gicv3::GroupId group)
+{
+    // Update active priority registers.
+    uint32_t prio = hppi.prio & 0xf8;
+    int apr_bit = prio >> (8 - PRIORITY_BITS);
+    int reg_bit = apr_bit % 32;
+    int apr_idx = group == Gicv3::G0S ?
+                 MISCREG_ICC_AP0R0_EL1 : MISCREG_ICC_AP1R0_EL1;
+    ArmISA::MiscReg apr = isa->readMiscRegNoEffect(apr_idx);
+    apr |= (1 << reg_bit);
+    isa->setMiscRegNoEffect(apr_idx, apr);
+
+    // Move interrupt state from pending to active.
+    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();
+    }
+}
+
+void
+Gicv3CPUInterface::virtualActivateIRQ(uint32_t lr_idx)
+{
+    // Update active priority registers.
+    ArmISA::MiscReg lr = isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 +
+            lr_idx);
+    Gicv3::GroupId group = lr & ICH_LR_EL2_GROUP ? Gicv3::G1NS : Gicv3::G0S;
+    uint8_t prio = bits(lr, 55, 48) & 0xf8;
+    int apr_bit = prio >> (8 - VIRTUAL_PREEMPTION_BITS);
+    int reg_no = apr_bit / 32;
+    int reg_bit = apr_bit % 32;
+    int apr_idx = group == Gicv3::G0S ?
+        MISCREG_ICH_AP0R0_EL2 + reg_no : MISCREG_ICH_AP1R0_EL2 + reg_no;
+    ArmISA::MiscReg apr = isa->readMiscRegNoEffect(apr_idx);
+    apr |= (1 << reg_bit);
+    isa->setMiscRegNoEffect(apr_idx, apr);
+    // Move interrupt state from pending to active.
+    lr &= ~ICH_LR_EL2_STATE_PENDING_BIT;
+    lr |= ICH_LR_EL2_STATE_ACTIVE_BIT;
+    isa->setMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx, lr);
+}
+
+void
+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 {
+        return;
+    }
+}
+
+void
+Gicv3CPUInterface::virtualDeactivateIRQ(int lr_idx)
+{
+    ArmISA::MiscReg lr = isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 +
+            lr_idx);
+
+    if (lr & ICH_LR_EL2_HW) {
+        // Deactivate the associated physical interrupt
+        int pintid = bits(lr, 41, 32);
+
+        if (pintid < Gicv3::INTID_SECURE) {
+            Gicv3::GroupId group =
+                pintid >= 32 ? distributor->getIntGroup(pintid) :
+                redistributor->getIntGroup(pintid);
+            deactivateIRQ(pintid, group);
+        }
+    }
+
+    //  Remove the active bit
+    lr &= ~ICH_LR_EL2_STATE_ACTIVE_BIT;
+    isa->setMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx, lr);
+}
+
+/*
+ * Return a mask word which clears the subpriority bits from
+ * a priority value for an interrupt in the specified group.
+ * This depends on the BPR value. For CBPR0 (S or NS):
+ *  a BPR of 0 means the group priority bits are [7:1];
+ *  a BPR of 1 means they are [7:2], and so on down to
+ *  ...
+ *  a BPR of 7 meaning no group priority bits at all.
+ * For CBPR1 NS:
+ *  a BPR of 0 is impossible (the minimum value is 1)
+ *  a BPR of 1 means the group priority bits are [7:1];
+ *  a BPR of 2 means they are [7:2], and so on down to
+ *  ...
+ *  a BPR of 7 meaning the group priority is [7].
+ *
+ * Which BPR to use depends on the group of the interrupt and
+ * the current ICC_CTLR.CBPR settings.
+ *
+ * This corresponds to the GroupBits() pseudocode from 4.8.2.
+ */
+uint32_t
+Gicv3CPUInterface::groupPriorityMask(Gicv3::GroupId group)
+{
+    if ((group == Gicv3::G1S &&
+            isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL1_S)
+            & ICC_CTLR_EL1_CBPR) ||
+            (group == Gicv3::G1NS &&
+             isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL1_NS)
+             & ICC_CTLR_EL1_CBPR)) {
+        group = Gicv3::G0S;
+    }
+
+    int bpr;
+
+    if (group == Gicv3::G0S) {
+        bpr = isa->readMiscRegNoEffect(MISCREG_ICC_BPR0_EL1) & 0x7;
+    } else {
+        bpr = isa->readMiscRegNoEffect(MISCREG_ICC_BPR1_EL1) & 0x7;
+    }
+
+    if (group == Gicv3::G1NS) {
+        assert(bpr > 0);
+        bpr--;
+    }
+
+    return ~0U << (bpr + 1);
+}
+
+uint32_t
+Gicv3CPUInterface::virtualGroupPriorityMask(Gicv3::GroupId group)
+{
+    ArmISA::MiscReg ich_vmcr_el2 =
+        isa->readMiscRegNoEffect(MISCREG_ICH_VMCR_EL2);
+
+    if (group == Gicv3::G1NS && (ich_vmcr_el2 & ICH_VMCR_EL2_VCBPR)) {
+        group = Gicv3::G0S;
+    }
+
+    int bpr;
+
+    if (group == Gicv3::G0S) {
+        bpr = bits(ich_vmcr_el2, 23, 21);
+    } else {
+        bpr = bits(ich_vmcr_el2, 20, 18);
+    }
+
+    if (group == Gicv3::G1NS) {
+        assert(bpr > 0);
+        bpr--;
+    }
+
+    return ~0U << (bpr + 1);
+}
+
+bool
+Gicv3CPUInterface::isEOISplitMode()
+{
+    if (isEL3OrMon()) {
+        return isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL3) &
+               ICC_CTLR_EL3_EOIMODE_EL3;
+    } else {
+        return isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL1) &
+               ICC_CTLR_EL1_EOIMODE;
+    }
+}
+
+bool
+Gicv3CPUInterface::virtualIsEOISplitMode()
+{
+    ArmISA::MiscReg ich_vmcr_el2 =
+        isa->readMiscRegNoEffect(MISCREG_ICH_VMCR_EL2);
+    return ich_vmcr_el2 & ICH_VMCR_EL2_VEOIM;
+}
+
+int
+Gicv3CPUInterface::highestActiveGroup()
+{
+    int g0_ctz = ctz32(isa->readMiscRegNoEffect(MISCREG_ICC_AP0R0_EL1));
+    int gq_ctz = ctz32(isa->readMiscRegNoEffect(MISCREG_ICC_AP1R0_EL1_S));
+    int g1nz_ctz = ctz32(isa->readMiscRegNoEffect(MISCREG_ICC_AP1R0_EL1_NS));
+
+    if (g1nz_ctz < g0_ctz && g1nz_ctz < gq_ctz) {
+        return Gicv3::G1NS;
+    }
+
+    if (gq_ctz < g0_ctz) {
+        return Gicv3::G1S;
+    }
+
+    if (g0_ctz < 32) {
+        return Gicv3::G0S;
+    }
+
+    return -1;
+}
+
+void
+Gicv3CPUInterface::update()
+{
+    bool signal_IRQ = false;
+    bool signal_FIQ = false;
+
+    if (hppi.group == Gicv3::G1S && !haveEL(EL3)) {
+        /*
+         * Secure enabled GIC sending a G1S IRQ to a secure disabled
+         * CPU -> send G0 IRQ
+         */
+        hppi.group = Gicv3::G0S;
+    }
+
+    if (hppiCanPreempt()) {
+        ArmISA::InterruptTypes int_type = intSignalType(hppi.group);
+        DPRINTF(GIC, "Gicv3CPUInterface::update(): "
+                "posting int as %d!\n", int_type);
+        int_type == ArmISA::INT_IRQ ? signal_IRQ = true : signal_FIQ = true;
+    }
+
+    if (signal_IRQ) {
+        gic->postInt(cpuId, ArmISA::INT_IRQ);
+    } else {
+        gic->deassertInt(cpuId, ArmISA::INT_IRQ);
+    }
+
+    if (signal_FIQ) {
+        gic->postInt(cpuId, ArmISA::INT_FIQ);
+    } else {
+        gic->deassertInt(cpuId, ArmISA::INT_FIQ);
+    }
+}
+
+void
+Gicv3CPUInterface::virtualUpdate()
+{
+    bool signal_IRQ = false;
+    bool signal_FIQ = false;
+    int lr_idx = getHPPVILR();
+
+    if (lr_idx >= 0) {
+        ArmISA::MiscReg ich_lr_el2 =
+            isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx);
+
+        if (hppviCanPreempt(lr_idx)) {
+            if (ich_lr_el2 & ICH_LR_EL2_GROUP) {
+                signal_IRQ = true;
+            } else {
+                signal_FIQ = true;
+            }
+        }
+    }
+
+    ArmISA::MiscReg ich_hcr_el2 =
+        isa->readMiscRegNoEffect(MISCREG_ICH_HCR_EL2);
+
+    if (ich_hcr_el2 & ICH_HCR_EL2_EN) {
+        if (maintenanceInterruptStatus()) {
+            redistributor->sendPPInt(25);
+        }
+    }
+
+    if (signal_IRQ) {
+        DPRINTF(GIC, "Gicv3CPUInterface::virtualUpdate(): "
+                "posting int as %d!\n", ArmISA::INT_VIRT_IRQ);
+        gic->postInt(cpuId, ArmISA::INT_VIRT_IRQ);
+    } else {
+        gic->deassertInt(cpuId, ArmISA::INT_VIRT_IRQ);
+    }
+
+    if (signal_FIQ) {
+        DPRINTF(GIC, "Gicv3CPUInterface::virtualUpdate(): "
+                "posting int as %d!\n", ArmISA::INT_VIRT_FIQ);
+        gic->postInt(cpuId, ArmISA::INT_VIRT_FIQ);
+    } else {
+        gic->deassertInt(cpuId, ArmISA::INT_VIRT_FIQ);
+    }
+}
+
+// Returns the intex of the LR with the HPPI
+int
+Gicv3CPUInterface::getHPPVILR()
+{
+    int idx = -1;
+    ArmISA::MiscReg ich_vmcr_el2 =
+        isa->readMiscRegNoEffect(MISCREG_ICH_VMCR_EL2);
+
+    if (!(ich_vmcr_el2 & (ICH_VMCR_EL2_VENG0 | ICH_VMCR_EL2_VENG1))) {
+        // VG0 and VG1 disabled...
+        return idx;
+    }
+
+    uint8_t highest_prio = 0xff;
+
+    for (int i = 0; i < 16; i++) {
+        ArmISA::MiscReg ich_lri_el2 =
+            isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + i);
+        uint8_t state = bits(ich_lri_el2, 63, 62);
+
+        if (state != Gicv3::INT_PENDING) {
+            continue;
+        }
+
+        if (ich_lri_el2 & ICH_LR_EL2_GROUP) {
+            // VG1
+            if (!(ich_vmcr_el2 & ICH_VMCR_EL2_VENG1)) {
+                continue;
+            }
+        } else {
+            // VG0
+            if (!(ich_vmcr_el2 & ICH_VMCR_EL2_VENG0)) {
+                continue;
+            }
+        }
+
+        uint8_t prio = bits(ich_lri_el2, 55, 48);
+
+        if (prio < highest_prio) {
+            highest_prio = prio;
+            idx = i;
+        }
+    }
+
+    return idx;
+}
+
+bool
+Gicv3CPUInterface::hppviCanPreempt(int lr_idx)
+{
+    ArmISA::MiscReg lr = isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 +
+            lr_idx);
+
+    if (!(isa->readMiscRegNoEffect(MISCREG_ICH_HCR_EL2) & ICH_HCR_EL2_EN)) {
+        // virtual interface is disabled
+        return false;
+    }
+
+    uint8_t prio = bits(lr, 55, 48);
+    uint8_t vpmr =
+        bits(isa->readMiscRegNoEffect(MISCREG_ICH_VMCR_EL2), 31, 24);
+
+    if (prio >= vpmr) {
+        // prioriry masked
+        return false;
+    }
+
+    uint8_t rprio = virtualHighestActivePriority();
+
+    if (rprio == 0xff) {
+        return true;
+    }
+
+    Gicv3::GroupId group = lr & ICH_LR_EL2_GROUP ? Gicv3::G1NS : Gicv3::G0S;
+    uint32_t prio_mask = virtualGroupPriorityMask(group);
+
+    if ((prio & prio_mask) < (rprio & prio_mask)) {
+        return true;
+    }
+
+    return false;
+}
+
+uint8_t
+Gicv3CPUInterface::virtualHighestActivePriority()
+{
+    uint8_t num_aprs = 1 << (VIRTUAL_PRIORITY_BITS - 5);
+
+    for (int i = 0; i < num_aprs; i++) {
+        ArmISA::MiscReg vapr =
+            isa->readMiscRegNoEffect(MISCREG_ICH_AP0R0_EL2 + i) |
+            isa->readMiscRegNoEffect(MISCREG_ICH_AP1R0_EL2 + i);
+
+        if (!vapr) {
+            continue;
+        }
+
+        return (i * 32 + ctz32(vapr)) << (GIC_MIN_VBPR + 1);
+    }
+
+    // no active interrups, return idle priority
+    return 0xff;
+}
+
+void
+Gicv3CPUInterface::virtualIncrementEOICount()
+{
+    // Increment the EOICOUNT field in ICH_HCR_EL2
+    ArmISA::MiscReg ich_hcr_el2 =
+        isa->readMiscRegNoEffect(MISCREG_ICH_HCR_EL2);
+    uint32_t EOI_cout = bits(ich_hcr_el2, 31, 27);
+    EOI_cout++;
+    ich_hcr_el2 = insertBits(ich_hcr_el2, 31, 27, EOI_cout);
+    isa->setMiscRegNoEffect(MISCREG_ICH_HCR_EL2, ich_hcr_el2);
+}
+
+/*
+ * Should we signal the interrupt as IRQ or FIQ?
+ * see spec section 4.6.2
+ */
+ArmISA::InterruptTypes
+Gicv3CPUInterface::intSignalType(Gicv3::GroupId group)
+{
+    bool is_fiq = false;
+
+    switch (group) {
+      case Gicv3::G0S:
+        is_fiq = true;
+        break;
+
+      case Gicv3::G1S:
+        is_fiq = (distributor->DS == 0) &&
+            (!inSecureState() || ((currEL() == EL3) && isAA64()));
+        break;
+
+      case Gicv3::G1NS:
+        is_fiq = (distributor->DS == 0) && inSecureState();
+        break;
+
+      default:
+        panic("Gicv3CPUInterface::intSignalType(): invalid group!");
+    }
+
+    if (is_fiq) {
+        return ArmISA::INT_FIQ;
+    } else {
+        return ArmISA::INT_IRQ;
+    }
+}
+
+bool
+Gicv3CPUInterface::hppiCanPreempt()
+{
+    if (hppi.prio == 0xff) {
+        // there is no pending interrupt
+        return false;
+    }
+
+    if (!groupEnabled(hppi.group)) {
+        // group disabled at CPU interface
+        return false;
+    }
+
+    if (hppi.prio >= isa->readMiscRegNoEffect(MISCREG_ICC_PMR_EL1)) {
+        // priority masked
+        return false;
+    }
+
+    uint8_t rprio = highestActivePriority();
+
+    if (rprio == 0xff) {
+        return true;
+    }
+
+    uint32_t prio_mask = groupPriorityMask(hppi.group);
+
+    if ((hppi.prio & prio_mask) < (rprio & prio_mask)) {
+        return true;
+    }
+
+    return false;
+}
+
+uint8_t
+Gicv3CPUInterface::highestActivePriority()
+{
+    uint32_t apr = isa->readMiscRegNoEffect(MISCREG_ICC_AP0R0_EL1) |
+                   isa->readMiscRegNoEffect(MISCREG_ICC_AP1R0_EL1_NS) |
+                   isa->readMiscRegNoEffect(MISCREG_ICC_AP1R0_EL1_S);
+
+    if (apr) {
+        return ctz32(apr) << (GIC_MIN_BPR + 1);
+    }
+
+    // no active interrups, return idle priority
+    return 0xff;
+}
+
+bool
+Gicv3CPUInterface::groupEnabled(Gicv3::GroupId group)
+{
+    switch (group) {
+      case Gicv3::G0S:
+        return isa->readMiscRegNoEffect(MISCREG_ICC_IGRPEN0_EL1) &
+            ICC_IGRPEN0_EL1_ENABLE;
+
+      case Gicv3::G1S:
+        //if (distributor->DS)
+        //{
+        //    return isa->readMiscRegNoEffect(MISCREG_ICC_IGRPEN1_EL1_NS) &
+        //           ICC_IGRPEN1_EL1_ENABLE;
+        //}
+        //else
+        //{
+        return isa->readMiscRegNoEffect(MISCREG_ICC_IGRPEN1_EL1_S) &
+            ICC_IGRPEN1_EL1_ENABLE;
+
+        //}
+
+      case Gicv3::G1NS:
+        return isa->readMiscRegNoEffect(MISCREG_ICC_IGRPEN1_EL1_NS) &
+            ICC_IGRPEN1_EL1_ENABLE;
+
+      default:
+        panic("Gicv3CPUInterface::groupEnable(): invalid group!\n");
+    }
+}
+
+bool
+Gicv3CPUInterface::inSecureState()
+{
+    if (!gic->getSystem()->haveSecurity()) {
+        return false;
+    }
+
+    CPSR cpsr = isa->readMiscRegNoEffect(MISCREG_CPSR);
+    SCR scr = isa->readMiscRegNoEffect(MISCREG_SCR);
+    return ArmISA::inSecureState(scr, cpsr);
+}
+
+int
+Gicv3CPUInterface::currEL()
+{
+    CPSR cpsr = isa->readMiscRegNoEffect(MISCREG_CPSR);
+    bool is_64 = opModeIs64((OperatingMode)(uint8_t) cpsr.mode);
+
+    if (is_64) {
+        return (ExceptionLevel)(uint8_t) cpsr.el;
+    } else {
+        switch (cpsr.mode) {
+          case MODE_USER:
+            return 0;
+
+          case MODE_HYP:
+            return 2;
+
+          case MODE_MON:
+            return 3;
+
+          default:
+            return 1;
+        }
+    }
+}
+
+bool
+Gicv3CPUInterface::haveEL(ExceptionLevel el)
+{
+    switch (el) {
+      case EL0:
+      case EL1:
+        return true;
+
+      case EL2:
+        return gic->getSystem()->haveVirtualization();
+
+      case EL3:
+        return gic->getSystem()->haveSecurity();
+
+      default:
+        warn("Unimplemented Exception Level\n");
+        return false;
+    }
+}
+
+bool
+Gicv3CPUInterface::isSecureBelowEL3()
+{
+    SCR scr = isa->readMiscRegNoEffect(MISCREG_SCR_EL3);
+    return haveEL(EL3) && scr.ns == 0;
+}
+
+bool
+Gicv3CPUInterface::isAA64()
+{
+    CPSR cpsr = isa->readMiscRegNoEffect(MISCREG_CPSR);
+    return opModeIs64((OperatingMode)(uint8_t) cpsr.mode);
+}
+
+bool
+Gicv3CPUInterface::isEL3OrMon()
+{
+    if (haveEL(EL3)) {
+        CPSR cpsr = isa->readMiscRegNoEffect(MISCREG_CPSR);
+        bool is_64 = opModeIs64((OperatingMode)(uint8_t) cpsr.mode);
+
+        if (is_64 && (cpsr.el == EL3)) {
+            return true;
+        } else if (!is_64 && (cpsr.mode == MODE_MON)) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+uint32_t
+Gicv3CPUInterface::eoiMaintenanceInterruptStatus(uint32_t * misr)
+{
+    /* Return a set of bits indicating the EOI maintenance interrupt status
+     * for each list register. The EOI maintenance interrupt status is
+     * 1 if LR.State == 0 && LR.HW == 0 && LR.EOI == 1
+     * (see the GICv3 spec for the ICH_EISR_EL2 register).
+     * If misr is not NULL then we should also collect the information
+     * about the MISR.EOI, MISR.NP and MISR.U bits.
+     */
+    uint32_t value = 0;
+    int valid_count = 0;
+    bool seen_pending = false;
+
+    for (int lr_idx = 0; lr_idx < VIRTUAL_NUM_LIST_REGS; lr_idx++) {
+        ArmISA::MiscReg lr =
+            isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx);
+
+        if ((lr & (ICH_LR_EL2_STATE_MASK | ICH_LR_EL2_HW | ICH_LR_EL2_EOI)) ==
+                ICH_LR_EL2_EOI) {
+            value |= (1 << lr_idx);
+        }
+
+        if ((lr & ICH_LR_EL2_STATE_MASK)) {
+            valid_count++;
+        }
+
+        if (bits(lr, ICH_LR_EL2_STATE_SHIFT + ICH_LR_EL2_STATE_LENGTH,
+                 ICH_LR_EL2_STATE_SHIFT) == ICH_LR_EL2_STATE_PENDING) {
+            seen_pending = true;
+        }
+    }
+
+    if (misr) {
+        ArmISA::MiscReg ich_hcr_el2 =
+            isa->readMiscRegNoEffect(MISCREG_ICH_HCR_EL2);
+
+        if (valid_count < 2 && (ich_hcr_el2 & ICH_HCR_EL2_UIE)) {
+            *misr |= ICH_MISR_EL2_U;
+        }
+
+        if (!seen_pending && (ich_hcr_el2 & ICH_HCR_EL2_NPIE)) {
+            *misr |= ICH_MISR_EL2_NP;
+        }
+
+        if (value) {
+            *misr |= ICH_MISR_EL2_EOI;
+        }
+    }
+
+    return value;
+}
+
+uint32_t
+Gicv3CPUInterface::maintenanceInterruptStatus()
+{
+    /* Return a set of bits indicating the maintenance interrupt status
+     * (as seen in the ICH_MISR_EL2 register).
+     */
+    uint32_t value = 0;
+    /* Scan list registers and fill in the U, NP and EOI bits */
+    eoiMaintenanceInterruptStatus(&value);
+    ArmISA::MiscReg ich_hcr_el2 =
+        isa->readMiscRegNoEffect(MISCREG_ICH_HCR_EL2);
+    ArmISA::MiscReg ich_vmcr_el2 =
+        isa->readMiscRegNoEffect(MISCREG_ICH_VMCR_EL2);
+
+    if (ich_hcr_el2 & (ICH_HCR_EL2_LRENPIE | ICH_HCR_EL2_EOICOUNT_MASK)) {
+        value |= ICH_MISR_EL2_LRENP;
+    }
+
+    if ((ich_hcr_el2 & ICH_HCR_EL2_VGRP0EIE) &&
+            (ich_vmcr_el2 & ICH_VMCR_EL2_VENG0)) {
+        value |= ICH_MISR_EL2_VGRP0E;
+    }
+
+    if ((ich_hcr_el2 & ICH_HCR_EL2_VGRP0DIE) &&
+            !(ich_vmcr_el2 & ICH_VMCR_EL2_VENG1)) {
+        value |= ICH_MISR_EL2_VGRP0D;
+    }
+
+    if ((ich_hcr_el2 & ICH_HCR_EL2_VGRP1EIE) &&
+            (ich_vmcr_el2 & ICH_VMCR_EL2_VENG1)) {
+        value |= ICH_MISR_EL2_VGRP1E;
+    }
+
+    if ((ich_hcr_el2 & ICH_HCR_EL2_VGRP1DIE) &&
+            !(ich_vmcr_el2 & ICH_VMCR_EL2_VENG1)) {
+        value |= ICH_MISR_EL2_VGRP1D;
+    }
+
+    return value;
+}
+
+void
+Gicv3CPUInterface::serialize(CheckpointOut & cp) const
+{
+    SERIALIZE_SCALAR(hppi.intid);
+    SERIALIZE_SCALAR(hppi.prio);
+    SERIALIZE_ENUM(hppi.group);
+}
+
+void
+Gicv3CPUInterface::unserialize(CheckpointIn & cp)
+{
+    UNSERIALIZE_SCALAR(hppi.intid);
+    UNSERIALIZE_SCALAR(hppi.prio);
+    UNSERIALIZE_ENUM(hppi.group);
+}
diff --git a/src/dev/arm/gic_v3_cpu_interface.hh b/src/dev/arm/gic_v3_cpu_interface.hh
new file mode 100644
index 0000000..63d3b53
--- /dev/null
+++ b/src/dev/arm/gic_v3_cpu_interface.hh
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 2018 Metempsy Technology Consulting
+ * All rights reserved.
+ *
+ * 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: Jairo Balart
+ */
+
+#ifndef __DEV_ARM_GICV3_CPU_INTERFACE_H__
+#define __DEV_ARM_GICV3_CPU_INTERFACE_H__
+
+#include "arch/arm/isa_device.hh"
+#include "dev/arm/gic_v3.hh"
+
+class Gicv3Redistributor;
+class Gicv3Distributor;
+
+class Gicv3CPUInterface : public ArmISA::BaseISADevice, public Serializable
+{
+  private:
+
+    friend class Gicv3Redistributor;
+    friend class Gicv3Distributor;
+
+  protected:
+
+    Gicv3 * gic;
+    Gicv3Redistributor * redistributor;
+    Gicv3Distributor * distributor;
+    uint32_t cpuId;
+
+    static const uint32_t ICC_SRE_EL1_SRE = 1 << 0;
+    static const uint32_t ICC_SRE_EL1_DFB = 1 << 1;
+    static const uint32_t ICC_SRE_EL1_DIB = 1 << 2;
+
+    static const uint32_t ICC_SRE_EL2_SRE = 1 << 0;
+    static const uint32_t ICC_SRE_EL2_DFB = 1 << 1;
+    static const uint32_t ICC_SRE_EL2_DIB = 1 << 2;
+    static const uint32_t ICC_SRE_EL2_ENABLE = 1 << 3;
+
+    static const uint32_t ICC_SRE_EL3_SRE = 1 << 0;
+    static const uint32_t ICC_SRE_EL3_DFB = 1 << 1;
+    static const uint32_t ICC_SRE_EL3_DIB = 1 << 2;
+    static const uint32_t ICC_SRE_EL3_ENABLE = 1 << 3;
+
+    static const uint32_t ICC_CTLR_EL3_CBPR_EL1S = 1 << 0;
+    static const uint32_t ICC_CTLR_EL3_CBPR_EL1NS = 1 << 1;
+    static const uint32_t ICC_CTLR_EL3_EOIMODE_EL3 = 1 << 2;
+    static const uint32_t ICC_CTLR_EL3_EOIMODE_EL1S = 1 << 3;
+    static const uint32_t ICC_CTLR_EL3_EOIMODE_EL1NS = 1 << 4;
+    static const uint32_t ICC_CTLR_EL3_RM = 1 << 5;
+    static const uint32_t ICC_CTLR_EL3_PMHE = 1 << 6;
+    static const uint32_t ICC_CTLR_EL3_PRIBITS_SHIFT = 8;
+    static const uint32_t ICC_CTLR_EL3_IDBITS_SHIFT = 11;
+    static const uint32_t ICC_CTLR_EL3_SEIS = 1 << 14;
+    static const uint32_t ICC_CTLR_EL3_A3V = 1 << 15;
+    static const uint32_t ICC_CTLR_EL3_nDS = 1 << 17;
+    static const uint32_t ICC_CTLR_EL3_RSS = 1 << 18;
+
+    static const uint32_t ICC_CTLR_EL1_CBPR = 1 << 0;
+    static const uint32_t ICC_CTLR_EL1_EOIMODE = 1 << 1;
+    static const uint32_t ICC_CTLR_EL1_PMHE = 1 << 6;
+    static const uint32_t ICC_CTLR_EL1_SEIS = 1 << 14;
+    static const uint32_t ICC_CTLR_EL1_A3V = 1 << 15;
+    static const uint32_t ICC_CTLR_EL1_RSS = 1 << 18;
+    static const uint32_t ICC_CTLR_EL1_PRIBITS_SHIFT = 8;
+    static const uint32_t ICC_CTLR_EL1_PRIBITS_MASK =
+        7U << ICC_CTLR_EL1_PRIBITS_SHIFT;
+    static const uint32_t ICC_CTLR_EL1_IDBITS_SHIFT = 11;
+
+    static const uint32_t ICC_IGRPEN0_EL1_ENABLE = 1 << 0;
+    static const uint32_t ICC_IGRPEN1_EL1_ENABLE = 1 << 0;
+
+    static const uint32_t ICC_IGRPEN1_EL3_ENABLEGRP1NS = 1 << 0;
+    static const uint32_t ICC_IGRPEN1_EL3_ENABLEGRP1S = 1 << 1;
+
+    static const uint8_t PRIORITY_BITS = 5;
+
+    /* Minimum BPR for Secure, or when security not enabled */
+    static const uint8_t GIC_MIN_BPR = 2;
+    /* Minimum BPR for Nonsecure when security is enabled */
+    static const uint8_t GIC_MIN_BPR_NS = GIC_MIN_BPR + 1;
+
+    static const uint8_t VIRTUAL_PRIORITY_BITS = 5;
+    static const uint8_t VIRTUAL_PREEMPTION_BITS = 5;
+    static const uint8_t VIRTUAL_NUM_LIST_REGS = 16;
+
+    static const uint8_t GIC_MIN_VBPR = 7 - VIRTUAL_PREEMPTION_BITS;
+
+    typedef struct {
+        uint32_t intid;
+        uint8_t prio;
+        Gicv3::GroupId group;
+    } hppi_t;
+
+    hppi_t hppi;
+
+    // GIC CPU interface memory mapped control registers (legacy)
+    enum {
+        GICC_CTLR = 0x0000,
+        GICC_PMR = 0x0004,
+        GICC_BPR = 0x0008,
+        GICC_IAR = 0x000C,
+        GICC_EOIR = 0x0010,
+        GICC_RPR = 0x0014,
+        GICC_HPPI = 0x0018,
+        GICC_ABPR = 0x001C,
+        GICC_AIAR = 0x0020,
+        GICC_AEOIR = 0x0024,
+        GICC_AHPPIR = 0x0028,
+        GICC_STATUSR = 0x002C,
+        GICC_IIDR = 0x00FC,
+    };
+
+    static const AddrRange GICC_APR;
+    static const AddrRange GICC_NSAPR;
+
+    // GIC CPU virtual interface memory mapped control registers (legacy)
+    enum {
+        GICH_HCR = 0x0000,
+        GICH_VTR = 0x0004,
+        GICH_VMCR = 0x0008,
+        GICH_MISR = 0x0010,
+        GICH_EISR = 0x0020,
+        GICH_ELRSR = 0x0030,
+    };
+
+    static const AddrRange GICH_APR;
+    static const AddrRange GICH_LR;
+
+    static const uint32_t ICH_HCR_EL2_EN = 1 << 0;
+    static const uint32_t ICH_HCR_EL2_UIE = 1 << 1;
+    static const uint32_t ICH_HCR_EL2_LRENPIE = 1 << 2;
+    static const uint32_t ICH_HCR_EL2_NPIE = 1 << 3;
+    static const uint32_t ICH_HCR_EL2_VGRP0EIE = 1 << 4;
+    static const uint32_t ICH_HCR_EL2_VGRP0DIE = 1 << 5;
+    static const uint32_t ICH_HCR_EL2_VGRP1EIE = 1 << 6;
+    static const uint32_t ICH_HCR_EL2_VGRP1DIE = 1 << 7;
+    static const uint32_t ICH_HCR_EL2_TC = 1 << 10;
+    static const uint32_t ICH_HCR_EL2_TALL0 = 1 << 11;
+    static const uint32_t ICH_HCR_EL2_TALL1 = 1 << 12;
+    static const uint32_t ICH_HCR_EL2_TSEI = 1 << 13;
+    static const uint32_t ICH_HCR_EL2_TDIR = 1 << 14;
+    static const uint32_t ICH_HCR_EL2_EOICOUNT_MASK = 0x1fU << 27;
+
+    static const uint64_t ICH_LR_EL2_VINTID_SHIFT = 0;
+    static const uint64_t ICH_LR_EL2_VINTID_LENGTH = 32;
+    static const uint64_t ICH_LR_EL2_VINTID_MASK =
+        (0xffffffffULL << ICH_LR_EL2_VINTID_SHIFT);
+    static const uint64_t ICH_LR_EL2_PINTID_SHIFT = 32;
+    static const uint64_t ICH_LR_EL2_PINTID_LENGTH = 10;
+    static const uint64_t ICH_LR_EL2_PINTID_MASK =
+        (0x3ffULL << ICH_LR_EL2_PINTID_SHIFT);
+    /* Note that EOI shares with the top bit of the pINTID field */
+    static const uint64_t ICH_LR_EL2_EOI = (1ULL << 41);
+    static const uint64_t ICH_LR_EL2_PRIORITY_SHIFT = 48;
+    static const uint64_t ICH_LR_EL2_PRIORITY_LENGTH = 8;
+    static const uint64_t ICH_LR_EL2_PRIORITY_MASK =
+        (0xffULL << ICH_LR_EL2_PRIORITY_SHIFT);
+    static const uint64_t ICH_LR_EL2_GROUP = (1ULL << 60);
+    static const uint64_t ICH_LR_EL2_HW = (1ULL << 61);
+    static const uint64_t ICH_LR_EL2_STATE_SHIFT = 62;
+    static const uint64_t ICH_LR_EL2_STATE_LENGTH = 2;
+    static const uint64_t ICH_LR_EL2_STATE_MASK =
+        (3ULL << ICH_LR_EL2_STATE_SHIFT);
+    /* values for the state field: */
+    static const uint64_t ICH_LR_EL2_STATE_INVALID = 0;
+    static const uint64_t ICH_LR_EL2_STATE_PENDING = 1;
+    static const uint64_t ICH_LR_EL2_STATE_ACTIVE = 2;
+    static const uint64_t ICH_LR_EL2_STATE_ACTIVE_PENDING = 3;
+    static const uint64_t ICH_LR_EL2_STATE_PENDING_BIT =
+        (1ULL << ICH_LR_EL2_STATE_SHIFT);
+    static const uint64_t ICH_LR_EL2_STATE_ACTIVE_BIT =
+        (2ULL << ICH_LR_EL2_STATE_SHIFT);
+
+    static const uint64_t ICH_LRC_PRIORITY_SHIFT =
+        ICH_LR_EL2_PRIORITY_SHIFT - 32;
+    static const uint64_t ICH_LRC_PRIORITY_LENGTH =
+        ICH_LR_EL2_PRIORITY_LENGTH;
+
+    static const uint32_t ICH_MISR_EL2_EOI = (1 << 0);
+    static const uint32_t ICH_MISR_EL2_U = (1 << 1);
+    static const uint32_t ICH_MISR_EL2_LRENP = (1 << 2);
+    static const uint32_t ICH_MISR_EL2_NP = (1 << 3);
+    static const uint32_t ICH_MISR_EL2_VGRP0E = (1 << 4);
+    static const uint32_t ICH_MISR_EL2_VGRP0D = (1 << 5);
+    static const uint32_t ICH_MISR_EL2_VGRP1E = (1 << 6);
+    static const uint32_t ICH_MISR_EL2_VGRP1D = (1 << 7);
+
+    static const uint32_t ICH_VMCR_EL2_VENG0_SHIFT = 0;
+    static const uint32_t ICH_VMCR_EL2_VENG0 =
+        (1 << ICH_VMCR_EL2_VENG0_SHIFT);
+    static const uint32_t ICH_VMCR_EL2_VENG1_SHIFT = 1;
+    static const uint32_t ICH_VMCR_EL2_VENG1 =
+        (1 << ICH_VMCR_EL2_VENG1_SHIFT);
+    static const uint32_t ICH_VMCR_EL2_VACKCTL = (1 << 2);
+    static const uint32_t ICH_VMCR_EL2_VFIQEN = (1 << 3);
+    static const uint32_t ICH_VMCR_EL2_VCBPR_SHIFT = 4;
+    static const uint32_t ICH_VMCR_EL2_VCBPR =
+        (1 << ICH_VMCR_EL2_VCBPR_SHIFT);
+    static const uint32_t ICH_VMCR_EL2_VEOIM_SHIFT = 9;
+    static const uint32_t ICH_VMCR_EL2_VEOIM =
+        (1 << ICH_VMCR_EL2_VEOIM_SHIFT);
+    static const uint32_t ICH_VMCR_EL2_VBPR1_SHIFT = 18;
+    static const uint32_t ICH_VMCR_EL2_VBPR1_LENGTH = 3;
+    static const uint32_t ICH_VMCR_EL2_VBPR1_MASK =
+        (0x7U << ICH_VMCR_EL2_VBPR1_SHIFT);
+    static const uint32_t ICH_VMCR_EL2_VBPR0_SHIFT = 21;
+    static const uint32_t ICH_VMCR_EL2_VBPR0_LENGTH = 3;
+    static const uint32_t ICH_VMCR_EL2_VBPR0_MASK =
+        (0x7U << ICH_VMCR_EL2_VBPR0_SHIFT);
+    static const uint32_t ICH_VMCR_EL2_VPMR_SHIFT = 24;
+    static const uint32_t ICH_VMCR_EL2_VPMR_LENGTH = 8;
+    static const uint32_t ICH_VMCR_EL2_VPMR_MASK =
+        (0xffU << ICH_VMCR_EL2_VPMR_SHIFT);
+
+    static const uint32_t ICH_VTR_EL2_LISTREGS_SHIFT = 0;
+    static const uint32_t ICH_VTR_EL2_TDS = 1 << 19;
+    static const uint32_t ICH_VTR_EL2_NV4 = 1 << 20;
+    static const uint32_t ICH_VTR_EL2_A3V = 1 << 21;
+    static const uint32_t ICH_VTR_EL2_SEIS = 1 << 22;
+    static const uint32_t ICH_VTR_EL2_IDBITS_SHIFT = 23;
+    static const uint32_t ICH_VTR_EL2_PREBITS_SHIFT = 26;
+    static const uint32_t ICH_VTR_EL2_PRIBITS_SHIFT = 29;
+
+  public:
+
+    Gicv3CPUInterface(Gicv3 * gic, uint32_t cpu_id);
+    ~Gicv3CPUInterface();
+    void init();
+    void initState();
+
+    ArmISA::MiscReg readMiscReg(int misc_reg) override;
+    void setMiscReg(int misc_reg, ArmISA::MiscReg val) override;
+    void update();
+    void virtualUpdate();
+
+    void serialize(CheckpointOut & cp) const override;
+    void unserialize(CheckpointIn & cp) override;
+
+  protected:
+
+    void reset();
+    bool hppiCanPreempt();
+    bool hppviCanPreempt(int lrIdx);
+    bool groupEnabled(Gicv3::GroupId group);
+    uint8_t highestActivePriority();
+    uint8_t virtualHighestActivePriority();
+    bool inSecureState();
+    int currEL();
+    bool haveEL(ArmISA::ExceptionLevel el);
+    void activateIRQ(uint32_t intid, Gicv3::GroupId group);
+    void virtualActivateIRQ(uint32_t lrIdx);
+    void deactivateIRQ(uint32_t intid, Gicv3::GroupId group);
+    void virtualDeactivateIRQ(int lrIdx);
+    uint32_t groupPriorityMask(Gicv3::GroupId group);
+    uint32_t virtualGroupPriorityMask(Gicv3::GroupId group);
+    void dropPriority(Gicv3::GroupId group);
+    uint8_t virtualDropPriority();
+    ArmISA::InterruptTypes intSignalType(Gicv3::GroupId group);
+    bool isEOISplitMode();
+    bool virtualIsEOISplitMode();
+    bool isSecureBelowEL3();
+    bool inSecureState2();
+    uint32_t eoiMaintenanceInterruptStatus(uint32_t * misr);
+    uint32_t maintenanceInterruptStatus();
+    int highestActiveGroup();
+    bool getHCREL2FMO();
+    bool getHCREL2IMO();
+    uint32_t getHPPIR1();
+    uint32_t getHPPIR0();
+    int getHPPVILR();
+    int virtualFindActive(uint32_t intid);
+    void virtualIncrementEOICount();
+    bool isEL3OrMon();
+    bool isAA64();
+};
+
+#endif //__DEV_ARM_GICV3_CPU_INTERFACE_H__
diff --git a/src/dev/arm/gic_v3_distributor.cc b/src/dev/arm/gic_v3_distributor.cc
new file mode 100644
index 0000000..00f29a7
--- /dev/null
+++ b/src/dev/arm/gic_v3_distributor.cc
@@ -0,0 +1,1132 @@
+/*
+ * Copyright (c) 2018 Metempsy Technology Consulting
+ * All rights reserved.
+ *
+ * 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: Jairo Balart
+ */
+#include "dev/arm/gic_v3_distributor.hh"
+
+#include <algorithm>
+
+#include "debug/GIC.hh"
+#include "dev/arm/gic_v3.hh"
+#include "dev/arm/gic_v3_cpu_interface.hh"
+#include "dev/arm/gic_v3_redistributor.hh"
+
+const AddrRange Gicv3Distributor::GICD_IGROUPR(0x0080, 0x00ff);
+const AddrRange Gicv3Distributor::GICD_ISENABLER(0x0100, 0x017f);
+const AddrRange Gicv3Distributor::GICD_ICENABLER(0x0180, 0x01ff);
+const AddrRange Gicv3Distributor::GICD_ISPENDR(0x0200, 0x027f);
+const AddrRange Gicv3Distributor::GICD_ICPENDR(0x0280, 0x02ff);
+const AddrRange Gicv3Distributor::GICD_ISACTIVER(0x0300, 0x037f);
+const AddrRange Gicv3Distributor::GICD_ICACTIVER(0x0380, 0x03ff);
+const AddrRange Gicv3Distributor::GICD_IPRIORITYR(0x0400, 0x07ff);
+const AddrRange Gicv3Distributor::GICD_ITARGETSR(0x0800, 0x08ff);
+const AddrRange Gicv3Distributor::GICD_ICFGR(0x0c00, 0x0cff);
+const AddrRange Gicv3Distributor::GICD_IGRPMODR(0x0d00, 0x0d7f);
+const AddrRange Gicv3Distributor::GICD_NSACR(0x0e00, 0x0eff);
+const AddrRange Gicv3Distributor::GICD_CPENDSGIR(0x0f10, 0x0f1f);
+const AddrRange Gicv3Distributor::GICD_SPENDSGIR(0x0f20, 0x0f2f);
+const AddrRange Gicv3Distributor::GICD_IROUTER(0x6000, 0x7fe0);
+
+Gicv3Distributor::Gicv3Distributor(Gicv3 * gic, uint32_t it_lines)
+    : gic(gic),
+      itLines(it_lines),
+      irqGroup(it_lines),
+      irqEnabled(it_lines),
+      irqPending(it_lines),
+      irqActive(it_lines),
+      irqPriority(it_lines),
+      irqConfig(it_lines),
+      irqGrpmod(it_lines),
+      irqNsacr(it_lines),
+      irqAffinityRouting(it_lines)
+{
+    panic_if(it_lines > Gicv3::INTID_SECURE, "Invalid value for it_lines!");
+}
+
+Gicv3Distributor::~Gicv3Distributor()
+{
+}
+
+void
+Gicv3Distributor::init()
+{
+}
+
+void
+Gicv3Distributor::initState()
+{
+    reset();
+}
+
+void
+Gicv3Distributor::reset()
+{
+    std::fill(irqGroup.begin(), irqGroup.end(), 0);
+    // Imp. defined reset value
+    std::fill(irqEnabled.begin(), irqEnabled.end(), false);
+    std::fill(irqPending.begin(), irqPending.end(), false);
+    std::fill(irqActive.begin(), irqActive.end(), false);
+    // Imp. defined reset value
+    std::fill(irqPriority.begin(), irqPriority.end(), 0xAAAAAAAA);
+    std::fill(irqConfig.begin(), irqConfig.end(),
+              Gicv3::INT_LEVEL_SENSITIVE); // Imp. defined reset value
+    std::fill(irqGrpmod.begin(), irqGrpmod.end(), 0);
+    std::fill(irqNsacr.begin(), irqNsacr.end(), 0);
+    /*
+     * For our implementation affinity routing is always enabled,
+     * no GICv2 legacy
+     */
+    ARE = true;
+
+    if (gic->getSystem()->haveSecurity()) {
+        DS = false;
+    } else {
+        DS = true;
+    }
+
+    EnableGrp0 = 0;
+    EnableGrp1NS = 0;
+    EnableGrp1S = 0;
+}
+
+uint64_t
+Gicv3Distributor::read(Addr addr, size_t size, bool is_secure_access)
+{
+    if (GICD_IGROUPR.contains(addr)) { // Interrupt Group Registers
+        uint64_t val = 0x0;
+
+        if (!DS && !is_secure_access) {
+            // RAZ/WI for non-secure accesses
+            return 0;
+        }
+
+        int first_intid = (addr - GICD_IGROUPR.start()) * 8;
+
+        if (isNotSPI(first_intid)) {
+            return 0;
+        }
+
+        for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
+                i++, int_id++) {
+            val |= irqGroup[int_id] << i;
+        }
+
+        return val;
+        // Interrupt Set-Enable Registers
+    } else if (GICD_ISENABLER.contains(addr)) {
+        uint64_t val = 0x0;
+        int first_intid = (addr - GICD_ISENABLER.start()) * 8;
+
+        if (isNotSPI(first_intid)) {
+            return 0;
+        }
+
+        for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
+                i++, int_id++) {
+            if (nsAccessToSecInt(int_id, is_secure_access))
+            {
+                continue;
+            }
+
+            val |= irqEnabled[int_id] << i;
+        }
+
+        return val;
+    } else if (GICD_ICENABLER.contains(addr)) {
+        // Interrupt Clear-Enable Registers
+        uint64_t val = 0x0;
+        int first_intid = (addr - GICD_ICENABLER.start()) * 8;
+
+        if (isNotSPI(first_intid)) {
+            return 0;
+        }
+
+        for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
+                i++, int_id++) {
+            if (nsAccessToSecInt(int_id, is_secure_access))
+            {
+                continue;
+            }
+
+            val |= (irqEnabled[int_id] << i);
+        }
+
+        return val;
+    } else if (GICD_ISPENDR.contains(addr)) {
+        uint64_t val = 0x0;
+        int first_intid = (addr - GICD_ISPENDR.start()) * 8;
+
+        if (isNotSPI(first_intid)) {
+            return 0;
+        }
+
+        for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
+                i++, int_id++) {
+            if (nsAccessToSecInt(int_id, is_secure_access))
+            {
+                if (irqNsacr[int_id] == 0) {
+                    // Group 0 or Secure Group 1 interrupts are RAZ/WI
+                    continue;
+                }
+            }
+
+            val |= (irqPending[int_id] << i);
+        }
+
+        return val;
+    } else if (GICD_ICPENDR.contains(addr)) {
+        uint64_t val = 0x0;
+        int first_intid = (addr - GICD_ICPENDR.start()) * 8;
+
+        if (isNotSPI(first_intid)) {
+            return 0;
+        }
+
+        for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
+                i++, int_id++) {
+            if (nsAccessToSecInt(int_id, is_secure_access))
+            {
+                if (irqNsacr[int_id] < 2) {
+                    // Group 0 or Secure Group 1 interrupts are RAZ/WI
+                    continue;
+                }
+            }
+
+            val |= (irqPending[int_id] << i);
+        }
+
+        return val;
+        // Interrupt Set-Active Registers
+    } else if (GICD_ISACTIVER.contains(addr)) {
+        int first_intid = (addr - GICD_ISACTIVER.start()) * 8;
+
+        if (isNotSPI(first_intid)) {
+            return 0;
+        }
+
+        uint64_t val = 0x0;
+
+        for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
+                i++, int_id++) {
+            if (nsAccessToSecInt(int_id, is_secure_access))
+            {
+                // Group 0 or Secure Group 1 interrupts are RAZ/WI
+                if (irqNsacr[int_id] < 2) {
+                    continue;
+                }
+            }
+
+            val |= (irqActive[int_id] << i);
+        }
+
+        return val;
+        // Interrupt Clear-Active Registers
+    } else if (GICD_ICACTIVER.contains(addr)) {
+        int first_intid = (addr - GICD_ICACTIVER.start()) * 8;
+
+        if (isNotSPI(first_intid)) {
+            return 0;
+        }
+
+        uint64_t val = 0x0;
+
+        for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
+                i++, int_id++) {
+            if (nsAccessToSecInt(int_id, is_secure_access))
+            {
+                if (irqNsacr[int_id] < 2) {
+                    continue;
+                }
+            }
+
+            val |= (irqActive[int_id] << i);
+        }
+
+        return val;
+        // Interrupt Priority Registers
+    } else if (GICD_IPRIORITYR.contains(addr)) {
+        uint64_t val = 0x0;
+        int first_intid = addr - GICD_IPRIORITYR.start();
+
+        if (isNotSPI(first_intid)) {
+            return 0;
+        }
+
+        for (int i = 0, int_id = first_intid; i < size && int_id < itLines;
+                i++, int_id++) {
+            uint8_t prio = irqPriority[int_id];
+
+            if (!DS && !is_secure_access) {
+                if (getIntGroup(int_id) != Gicv3::G1NS) {
+                    // RAZ/WI for non-secure accesses for secure interrupts
+                    continue;
+                } else {
+                    // NS view
+                    prio = (prio << 1) & 0xff;
+                }
+            }
+
+            val |= prio << (i * 8);
+        }
+
+        return val;
+    } else if (GICD_ITARGETSR.contains(addr)) {
+        // Interrupt Processor Targets Registers
+        // ARE always on, RAZ/WI
+        warn("Gicv3Distributor::read(): "
+             "GICD_ITARGETSR is RAZ/WI, legacy not supported!\n");
+        return 0;
+        // Interrupt Configuration Registers
+    } else if (GICD_ICFGR.contains(addr)) {
+        int first_intid = (addr - GICD_ICFGR.start()) * 4;
+
+        if (isNotSPI(first_intid)) {
+            return 0;
+        }
+
+        uint64_t val = 0x0;
+
+        for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
+                i = i + 2, int_id++) {
+            if (nsAccessToSecInt(int_id, is_secure_access))
+            {
+                continue;
+            }
+
+            if (irqConfig[int_id] == Gicv3::INT_EDGE_TRIGGERED) {
+                val |= (0x2 << i);
+            }
+        }
+
+        return val;
+    } else if (GICD_IGRPMODR.contains(addr)) {
+        // Interrupt Group Modifier Registers
+        if (DS) {
+            // RAZ/WI if security disabled
+            return 0;
+        } else {
+            if (!is_secure_access) {
+                // RAZ/WI for non-secure accesses
+                return 0;
+            } else {
+                int first_intid = (addr - GICD_IGRPMODR.start()) * 8;
+
+                if (isNotSPI(first_intid)) {
+                    return 0;
+                }
+
+                uint64_t val = 0x0;
+
+                for (int i = 0, int_id = first_intid;
+                        i < 8 * size && int_id < itLines; i++, int_id++) {
+                    val |= irqGrpmod[int_id] << i;
+                }
+
+                return val;
+            }
+        }
+
+        // Non-secure Access Control Registers
+    } else if (GICD_NSACR.contains(addr)) {
+        // 2 bits per interrupt
+        int first_intid = (addr - GICD_NSACR.start()) * 4;
+
+        if (isNotSPI(first_intid)) {
+            return 0;
+        }
+
+        if (DS || (!DS && !is_secure_access)) {
+            return 0;
+        }
+
+        uint64_t val = 0x0;
+
+        for (int i = 0, int_id = first_intid;
+                i < 8 * size && int_id < itLines; i = i + 2, int_id++) {
+            val |= irqNsacr[int_id] << i;
+        }
+
+        return val;
+    } else if (GICD_CPENDSGIR.contains(addr)) { // SGI Clear-Pending Registers
+        // ARE always on, RAZ/WI
+        warn("Gicv3Distributor::read(): "
+             "GICD_CPENDSGIR is RAZ/WI, legacy not supported!\n");
+        return 0x0;
+    } else if (GICD_SPENDSGIR.contains(addr)) { // SGI Set-Pending Registers
+        // ARE always on, RAZ/WI
+        warn("Gicv3Distributor::read(): "
+             "GICD_SPENDSGIR is RAZ/WI, legacy not supported!\n");
+        return 0x0;
+    } else if (GICD_IROUTER.contains(addr)) { // Interrupt Routing Registers
+        // 64 bit registers. 2 or 1 access.
+        int int_id = (addr - GICD_IROUTER.start()) / 8;
+
+        if (isNotSPI(int_id)) {
+            return 0;
+        }
+
+        if (nsAccessToSecInt(int_id, is_secure_access))
+        {
+            if (irqNsacr[int_id] < 3) {
+                return 0;
+            }
+        }
+
+        if (size == 4) {
+            if (addr & 7) { // high half of 64 bit register
+                return irqAffinityRouting[int_id] >> 32;
+            } else { // high low of 64 bit register
+                return irqAffinityRouting[int_id] & 0xFFFFFFFF;
+            }
+        } else {
+            return irqAffinityRouting[int_id];
+        }
+    }
+
+    switch (addr) {
+      case GICD_CTLR: // Control Register
+        if (!DS) {
+            if (is_secure_access) {
+                // E1NWF [7] RAZ/WI
+                // DS [6] - Disable Security
+                // ARE_NS [5] RAO/WI
+                // ARE_S [4] RAO/WI
+                // EnableGrp1S [2]
+                // EnableGrp1NS [1]
+                // EnableGrp0 [0]
+                return (EnableGrp0 << 0) |
+                    (EnableGrp1NS << 1) |
+                    (EnableGrp1S << 2) |
+                    (1 << 4) |
+                    (1 << 5) |
+                    (DS << 6);
+            } else {
+                // ARE_NS [4] RAO/WI;
+                // EnableGrp1A [1] is a read-write alias of the Secure
+                // GICD_CTLR.EnableGrp1NS
+                // EnableGrp1 [0] RES0
+                return (1 << 4) | (EnableGrp1NS << 1);
+            }
+        } else {
+            return (DS << 6) | (ARE << 4) |
+                (EnableGrp1NS << 1) | (EnableGrp0 << 0);
+        }
+
+      case GICD_TYPER: // Interrupt Controller Type Register
+        /*
+         * RSS           [26]    == 1
+         * (The implementation does supports targeted SGIs with affinity
+         * level 0 values of 0 - 255)
+         * No1N          [25]    == 1
+         * (1 of N SPI interrupts are not supported)
+         * A3V           [24]    == 1
+         * (Supports nonzero values of Affinity level 3)
+         * IDbits        [23:19] == 0xf
+         * (The number of interrupt identifier bits supported, minus one)
+         * DVIS          [18]    == 0
+         * (The implementation does not support Direct Virtual LPI
+         * injection)
+         * LPIS          [17]    == 0
+         * (The implementation does not support LPIs)
+         * MBIS          [16]    == 0
+         * (The implementation does not support message-based interrupts
+         * by writing to Distributor registers)
+         * SecurityExtn  [10]    == X
+         * (The GIC implementation supports two Security states)
+         * CPUNumber     [7:5]   == 0
+         * (since for us ARE is always 1 [(ARE = 0) == Gicv2 legacy])
+         * ITLinesNumber [4:0]   == N
+         * (MaxSPIIntId = 32 (N + 1) - 1)
+         */
+        {
+            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) |
+                (it_lines_number << 0);
+        }
+
+      case GICD_IIDR: // Implementer Identification Register
+        //return 0x43b; // ARM JEP106 code (r0p0 GIC-500)
+        return 0;
+
+      case GICD_STATUSR: // Error Reporting Status Register
+        // Optional register, RAZ/WI
+        return 0x0;
+
+      case GICD_PIDR0: { // Peripheral ID0 Register
+          uint8_t part_0 = 0x92; // Part number, bits[7:0]
+          return part_0;
+      }
+
+      case GICD_PIDR1: { // Peripheral ID1 Register
+          uint8_t des_0 = 0xB; // JEP106 identification code, bits[3:0]
+          uint8_t part_1 = 0x4; // Part number, bits[11:8]
+          return (des_0 << 4) | (part_1 << 0);
+      }
+
+      case GICD_PIDR2: { // Peripheral ID2 Register
+          uint8_t arch_rev = 0x3; // 0x3 GICv3
+          uint8_t jdec = 0x1; // JEP code
+          uint8_t des_1 = 0x3; // JEP106 identification code, bits[6:4]
+          return (arch_rev << 4) | (jdec << 3) | (des_1 << 0);
+      }
+
+      case GICD_PIDR3: // Peripheral ID3 Register
+        return 0x0; // Implementation defined
+
+      case GICD_PIDR4: { // Peripheral ID4 Register
+          uint8_t size = 0x4; // 64 KB software visible page
+          uint8_t des_2 = 0x4; // ARM implementation
+          return (size << 4) | (des_2 << 0);
+      }
+
+      case GICD_PIDR5: // Peripheral ID5 Register
+      case GICD_PIDR6: // Peripheral ID6 Register
+      case GICD_PIDR7: // Peripheral ID7 Register
+        return 0; // RES0
+
+      default:
+        panic("Gicv3Distributor::read(): invalid offset %#x\n", addr);
+        break;
+    }
+}
+
+void
+Gicv3Distributor::write(Addr addr, uint64_t data, size_t size,
+                        bool is_secure_access)
+{
+    if (GICD_IGROUPR.contains(addr)) { // Interrupt Group Registers
+        if (!DS && !is_secure_access) {
+            // RAZ/WI for non-secure accesses
+            return;
+        }
+
+        int first_intid = (addr - GICD_IGROUPR.start()) * 8;
+
+        if (isNotSPI(first_intid)) {
+            return;
+        }
+
+        for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
+                i++, int_id++) {
+            irqGroup[int_id] = data & (1 << i) ? 1 : 0;
+            DPRINTF(GIC, "Gicv3Distributor::write(): int_id %d group %d\n",
+                    int_id, irqGroup[int_id]);
+        }
+
+        return;
+        // Interrupt Set-Enable Registers
+    } else if (GICD_ISENABLER.contains(addr)) {
+        int first_intid = (addr - GICD_ISENABLER.start()) * 8;
+
+        if (isNotSPI(first_intid)) {
+            return;
+        }
+
+        for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
+                i++, int_id++) {
+            if (nsAccessToSecInt(int_id, is_secure_access))
+            {
+                continue;
+            }
+
+            bool enable = data & (1 << i) ? 1 : 0;
+
+            if (enable) {
+                if (!irqEnabled[int_id]) {
+                    DPRINTF(GIC, "Gicv3Distributor::write(): "
+                            "int_id %d enabled\n", int_id);
+                }
+
+                irqEnabled[int_id] = true;
+            }
+        }
+
+        return;
+    } else if (GICD_ICENABLER.contains(addr)) {
+        // Interrupt Clear-Enable Registers
+        int first_intid = (addr - GICD_ICENABLER.start()) * 8;
+
+        for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
+                i++, int_id++) {
+            if (nsAccessToSecInt(int_id, is_secure_access))
+            {
+                continue;
+            }
+
+            bool disable = data & (1 << i) ? 1 : 0;
+
+            if (disable) {
+                if (irqEnabled[int_id]) {
+                    DPRINTF(GIC, "Gicv3Distributor::write(): "
+                            "int_id %d disabled\n", int_id);
+                }
+
+                irqEnabled[int_id] = false;
+            }
+        }
+
+        return;
+    } else if (GICD_ISPENDR.contains(addr)) {
+        int first_intid = (addr - GICD_ISPENDR.start()) * 8;
+
+        if (isNotSPI(first_intid)) {
+            return;
+        }
+
+        for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
+                i++, int_id++) {
+            if (nsAccessToSecInt(int_id, is_secure_access))
+            {
+                if (irqNsacr[int_id] == 0) {
+                    // Group 0 or Secure Group 1 interrupts are RAZ/WI
+                    continue;
+                }
+            }
+
+            bool pending = data & (1 << i) ? 1 : 0;
+
+            if (pending) {
+                DPRINTF(GIC, "Gicv3Distributor::write() (GICD_ISPENDR): "
+                        "int_id %d (SPI) pending bit set\n", int_id);
+                irqPending[int_id] = true;
+            }
+        }
+
+        updateAndInformCPUInterfaces();
+        return;
+    } else if (GICD_ICPENDR.contains(addr)) {
+        int first_intid = (addr - GICD_ICPENDR.start()) * 8;
+
+        if (isNotSPI(first_intid)) {
+            return;
+        }
+
+        for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
+                i++, int_id++) {
+            if (nsAccessToSecInt(int_id, is_secure_access))
+            {
+                if (irqNsacr[int_id] < 2) {
+                    // Group 0 or Secure Group 1 interrupts are RAZ/WI
+                    continue;
+                }
+            }
+
+            bool clear = data & (1 << i) ? 1 : 0;
+
+            if (clear) {
+                irqPending[int_id] = false;
+            }
+        }
+
+        updateAndInformCPUInterfaces();
+        return;
+        // Interrupt Set-Active Registers
+    } else if (GICD_ISACTIVER.contains(addr)) {
+        int first_intid = (addr - GICD_ISACTIVER.start()) * 8;
+
+        if (isNotSPI(first_intid)) {
+            return;
+        }
+
+        for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
+                i++, int_id++) {
+            if (nsAccessToSecInt(int_id, is_secure_access))
+            {
+                continue;
+            }
+
+            bool active = data & (1 << i) ? 1 : 0;
+
+            if (active) {
+                irqActive[int_id] = 1;
+            }
+        }
+
+        return;
+    } else if (GICD_ICACTIVER.contains(addr)) {
+        // Interrupt Clear-Active Registers
+        int first_intid = (addr - GICD_ICACTIVER.start()) * 8;
+
+        if (isNotSPI(first_intid)) {
+            return;
+        }
+
+        for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
+                i++, int_id++) {
+            if (nsAccessToSecInt(int_id, is_secure_access))
+            {
+                continue;
+            }
+
+            bool clear = data & (1 << i) ? 1 : 0;
+
+            if (clear) {
+                if (irqActive[int_id]) {
+                    DPRINTF(GIC, "Gicv3Distributor::write(): "
+                            "int_id %d active cleared\n", int_id);
+                }
+
+                irqActive[int_id] = false;
+            }
+        }
+
+        return;
+        // Interrupt Priority Registers
+    } else if (GICD_IPRIORITYR.contains(addr)) {
+        int first_intid = addr - GICD_IPRIORITYR.start();
+
+        if (isNotSPI(first_intid)) {
+            return;
+        }
+
+        for (int i = 0, int_id = first_intid; i < size && int_id < itLines;
+                i++, int_id++) {
+            uint8_t prio = bits(data, (i + 1) * 8 - 1, (i * 8));
+
+            if (!DS && !is_secure_access) {
+                if (getIntGroup(int_id) != Gicv3::G1NS) {
+                    // RAZ/WI for non-secure accesses to secure interrupts
+                    continue;
+                } else {
+                    prio = 0x80 | (prio >> 1);
+                }
+            }
+
+            irqPriority[int_id] = prio;
+            DPRINTF(GIC, "Gicv3Distributor::write(): int_id %d priority %d\n",
+                    int_id, irqPriority[int_id]);
+        }
+
+        return;
+    } else if (GICD_ITARGETSR.contains(addr)) {
+        // Interrupt Processor Targets Registers
+        // ARE always on, RAZ/WI
+        warn("Gicv3Distributor::write(): "
+             "GICD_ITARGETSR is RAZ/WI, legacy not supported!\n");
+        return;
+        // Interrupt Configuration Registers
+    } else if (GICD_ICFGR.contains(addr)) {
+        /* Here only the odd bits are used; even bits are RES0 */
+        int first_intid = (addr - GICD_ICFGR.start()) * 4;
+
+        if (isNotSPI(first_intid)) {
+            return;
+        }
+
+        for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
+                i = i + 2, int_id++) {
+            irqConfig[int_id] = data & (0x2 << i) ?
+                                Gicv3::INT_EDGE_TRIGGERED :
+                                Gicv3::INT_LEVEL_SENSITIVE;
+            DPRINTF(GIC, "Gicv3Distributor::write(): int_id %d config %d\n",
+                    int_id, irqConfig[int_id]);
+        }
+
+        return;
+    } else if (GICD_IGRPMODR.contains(addr)) {
+        // Interrupt Group Modifier Registers
+        if (DS) {
+            return;
+        } else {
+            if (!is_secure_access) {
+                // RAZ/WI for non-secure accesses
+                return;
+            } else {
+                int first_intid = (addr - GICD_IGRPMODR.start()) * 8;
+
+                if (isNotSPI(first_intid)) {
+                    return;
+                }
+
+                for (int i = 0, int_id = first_intid;
+                        i < 8 * size && int_id < itLines; i++, int_id++) {
+                    irqGrpmod[int_id] = data & (0x1 << i);
+                }
+
+                return ;
+            }
+        }
+
+        // Non-secure Access Control Registers
+    } else if (GICD_NSACR.contains(addr)) {
+        // 2 bits per interrupt
+        int first_intid = (addr - GICD_NSACR.start()) * 4;
+
+        if (isNotSPI(first_intid)) {
+            return;
+        }
+
+        if (DS || (!DS && !is_secure_access)) {
+            return;
+        }
+
+        for (int i = 0, int_id = first_intid;
+                i < 8 * size && int_id < itLines; i = i + 2, int_id++) {
+            irqNsacr[int_id] = (data >> (2 * int_id)) & 0x3;
+        }
+
+        return;
+    } else if (GICD_IROUTER.contains(addr)) { // Interrupt Routing Registers
+        // 64 bit registers. 2 accesses.
+        int int_id = (addr - GICD_IROUTER.start()) / 8;
+
+        if (isNotSPI(int_id)) {
+            return;
+        }
+
+        if (nsAccessToSecInt(int_id, is_secure_access))
+        {
+            if (irqNsacr[int_id] < 3) {
+                // Group 0 or Secure Group 1 interrupts are RAZ/WI
+                return;
+            }
+        }
+
+        if (size == 4) {
+            if (addr & 7) { // high half of 64 bit register
+                irqAffinityRouting[int_id] =
+                    (irqAffinityRouting[int_id] & 0xffffffff) | (data << 32);
+            } else { // low half of 64 bit register
+                irqAffinityRouting[int_id] =
+                    (irqAffinityRouting[int_id] & 0xffffffff00000000) |
+                    (data & 0xffffffff);
+            }
+        } else {
+            irqAffinityRouting[int_id] = data;
+        }
+
+        DPRINTF(GIC, "Gicv3Distributor::write(): "
+                "int_id %d GICD_IROUTER %#llx\n",
+                int_id, irqAffinityRouting[int_id]);
+        return;
+    }
+
+    switch (addr) {
+      case GICD_CTLR: // Control Register
+        if (DS) {
+            /*
+             * E1NWF [7]
+             * 1 of N wakeup functionality not supported, RAZ/WI
+             * DS [6] - RAO/WI
+             * ARE [4]
+             * affinity routing always on, no GICv2 legacy, RAO/WI
+             * EnableGrp1 [1]
+             * EnableGrp0 [0]
+             */
+            if ((data & (1 << 4)) == 0) {
+                warn("Gicv3Distributor::write(): "
+                        "setting ARE to 0 is not supported!\n");
+            }
+
+            EnableGrp1NS = data & GICD_CTLR_ENABLEGRP1NS;
+            EnableGrp0 = data & GICD_CTLR_ENABLEGRP0;
+            DPRINTF(GIC, "Gicv3Distributor::write(): (DS 1)"
+                    "EnableGrp1NS %d EnableGrp0 %d\n",
+                    EnableGrp1NS, EnableGrp0);
+        } else {
+            if (is_secure_access) {
+                /*
+                 * E1NWF [7]
+                 * 1 of N wakeup functionality not supported, RAZ/WI
+                 * DS [6]
+                 * ARE_NS [5]
+                 * affinity routing always on, no GICv2 legacy, RAO/WI
+                 * ARE_S [4]
+                 * affinity routing always on, no GICv2 legacy, RAO/WI
+                 * EnableGrp1S [2]
+                 * EnableGrp1NS [1]
+                 * EnableGrp0 [0]
+                 */
+                if ((data & (1 << 5)) == 0) {
+                    warn("Gicv3Distributor::write(): "
+                            "setting ARE_NS to 0 is not supported!\n");
+                }
+
+                if ((data & (1 << 4)) == 0) {
+                    warn("Gicv3Distributor::write(): "
+                            "setting ARE_S to 0 is not supported!\n");
+                }
+
+                DS = data & GICD_CTLR_DS;
+                EnableGrp1S = data & GICD_CTLR_ENABLEGRP1S;
+                EnableGrp1NS = data & GICD_CTLR_ENABLEGRP1NS;
+                EnableGrp0 = data & GICD_CTLR_ENABLEGRP0;
+                DPRINTF(GIC, "Gicv3Distributor::write(): (DS 0 secure)"
+                        "DS %d "
+                        "EnableGrp1S %d EnableGrp1NS %d EnableGrp0 %d\n",
+                        DS, EnableGrp1S, EnableGrp1NS, EnableGrp0);
+
+                if (data & GICD_CTLR_DS) {
+                    EnableGrp1S = 0;
+                }
+            } else {
+                /*
+                 * ARE_NS [4] RAO/WI;
+                 * EnableGrp1A [1] is a read-write alias of the Secure
+                 * GICD_CTLR.EnableGrp1NS
+                 * EnableGrp1 [0] RES0
+                 */
+                if ((data & (1 << 4)) == 0) {
+                    warn("Gicv3Distributor::write(): "
+                            "setting ARE_NS to 0 is not supported!\n");
+                }
+
+                EnableGrp1NS = data & GICD_CTLR_ENABLEGRP1A;
+                DPRINTF(GIC, "Gicv3Distributor::write(): (DS 0 non-secure)"
+                        "EnableGrp1NS %d\n", EnableGrp1NS);
+            }
+        }
+
+        break;
+
+      default:
+        panic("Gicv3Distributor::write(): invalid offset %#x\n", addr);
+        break;
+    }
+}
+
+void
+Gicv3Distributor::sendInt(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] = true;
+    DPRINTF(GIC, "Gicv3Distributor::sendInt(): "
+            "int_id %d (SPI) pending bit set\n", int_id);
+    updateAndInformCPUInterfaces();
+}
+
+void
+Gicv3Distributor::intDeasserted(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();
+}
+
+void
+Gicv3Distributor::updateAndInformCPUInterfaces()
+{
+    update();
+
+    for (int i = 0; i < gic->getSystem()->numContexts(); i++) {
+        gic->getCPUInterface(i)->update();
+    }
+}
+
+void
+Gicv3Distributor::fullUpdate()
+{
+    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();
+    }
+}
+
+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++) {
+        Gicv3::GroupId int_group = getIntGroup(int_id);
+        bool group_enabled = groupEnabled(int_group);
+
+        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;
+            }
+
+            Gicv3CPUInterface * target_cpu_interface =
+                target_redistributor->getCPUInterface();
+            uint32_t target_cpu = target_redistributor->cpuId;
+
+            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;
+            }
+        }
+    }
+
+    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();
+        }
+    }
+}
+
+Gicv3::IntStatus
+Gicv3Distributor::intStatus(uint32_t int_id)
+{
+    panic_if(int_id < Gicv3::SGI_MAX + Gicv3::PPI_MAX, "Invalid SPI!");
+    panic_if(int_id > itLines, "Invalid SPI!");
+
+    if (irqPending[int_id]) {
+        if (irqActive[int_id]) {
+            return Gicv3::INT_ACTIVE_PENDING;
+        }
+
+        return Gicv3::INT_PENDING;
+    } else if (irqActive[int_id]) {
+        return Gicv3::INT_ACTIVE;
+    } else {
+        return Gicv3::INT_INACTIVE;
+    }
+}
+
+Gicv3::GroupId
+Gicv3Distributor::getIntGroup(int int_id)
+{
+    panic_if(int_id < Gicv3::SGI_MAX + Gicv3::PPI_MAX, "Invalid SPI!");
+    panic_if(int_id > itLines, "Invalid SPI!");
+
+    if (DS) {
+        if (irqGroup[int_id] == 1) {
+            return Gicv3::G1NS;
+        } else {
+            return Gicv3::G0S;
+        }
+    } else {
+        if (irqGrpmod[int_id] == 0 && irqGroup[int_id] == 0) {
+            return Gicv3::G0S;
+        } else if (irqGrpmod[int_id] == 0 && irqGroup[int_id] == 1) {
+            return Gicv3::G1NS;
+        } else if (irqGrpmod[int_id] == 1 && irqGroup[int_id] == 0) {
+            return Gicv3::G1S;
+        } else if (irqGrpmod[int_id] == 1 && irqGroup[int_id] == 1) {
+            return Gicv3::G1NS;
+        }
+    }
+
+    M5_UNREACHABLE;
+}
+
+void
+Gicv3Distributor::activateIRQ(uint32_t int_id)
+{
+    irqPending[int_id] = false;
+    irqActive[int_id] = true;
+}
+
+void
+Gicv3Distributor::deactivateIRQ(uint32_t int_id)
+{
+    irqActive[int_id] = false;
+}
+
+void
+Gicv3Distributor::serialize(CheckpointOut & cp) const
+{
+    SERIALIZE_SCALAR(ARE);
+    SERIALIZE_SCALAR(DS);
+    SERIALIZE_SCALAR(EnableGrp1S);
+    SERIALIZE_SCALAR(EnableGrp1NS);
+    SERIALIZE_SCALAR(EnableGrp0);
+    SERIALIZE_CONTAINER(irqGroup);
+    SERIALIZE_CONTAINER(irqEnabled);
+    SERIALIZE_CONTAINER(irqPending);
+    SERIALIZE_CONTAINER(irqActive);
+    SERIALIZE_CONTAINER(irqPriority);
+    SERIALIZE_CONTAINER(irqConfig);
+    SERIALIZE_CONTAINER(irqGrpmod);
+    SERIALIZE_CONTAINER(irqNsacr);
+    SERIALIZE_CONTAINER(irqAffinityRouting);
+}
+
+void
+Gicv3Distributor::unserialize(CheckpointIn & cp)
+{
+    UNSERIALIZE_SCALAR(ARE);
+    UNSERIALIZE_SCALAR(DS);
+    UNSERIALIZE_SCALAR(EnableGrp1S);
+    UNSERIALIZE_SCALAR(EnableGrp1NS);
+    UNSERIALIZE_SCALAR(EnableGrp0);
+    UNSERIALIZE_CONTAINER(irqGroup);
+    UNSERIALIZE_CONTAINER(irqEnabled);
+    UNSERIALIZE_CONTAINER(irqPending);
+    UNSERIALIZE_CONTAINER(irqActive);
+    UNSERIALIZE_CONTAINER(irqPriority);
+    UNSERIALIZE_CONTAINER(irqConfig);
+    UNSERIALIZE_CONTAINER(irqGrpmod);
+    UNSERIALIZE_CONTAINER(irqNsacr);
+    UNSERIALIZE_CONTAINER(irqAffinityRouting);
+}
diff --git a/src/dev/arm/gic_v3_distributor.hh b/src/dev/arm/gic_v3_distributor.hh
new file mode 100644
index 0000000..334ecee
--- /dev/null
+++ b/src/dev/arm/gic_v3_distributor.hh
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2018 Metempsy Technology Consulting
+ * All rights reserved.
+ *
+ * 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: Jairo Balart
+ */
+
+#ifndef __DEV_ARM_GICV3_DISTRIBUTOR_H__
+#define __DEV_ARM_GICV3_DISTRIBUTOR_H__
+
+#include "base/addr_range.hh"
+#include "dev/arm/gic_v3.hh"
+#include "sim/serialize.hh"
+
+class Gicv3Distributor : public Serializable
+{
+  private:
+
+    friend class Gicv3Redistributor;
+    friend class Gicv3CPUInterface;
+
+  protected:
+
+    Gicv3 * gic;
+    const uint32_t itLines;
+
+    enum {
+        // Control Register
+        GICD_CTLR  = 0x0000,
+        // Interrupt Controller Type Register
+        GICD_TYPER = 0x0004,
+        // Implementer Identification Register
+        GICD_IIDR = 0x0008,
+        // Error Reporting Status Register
+        GICD_STATUSR = 0x0010,
+        // Peripheral ID0 Register
+        GICD_PIDR0 = 0xffe0,
+        // Peripheral ID1 Register
+        GICD_PIDR1 = 0xffe4,
+        // Peripheral ID2 Register
+        GICD_PIDR2 = 0xffe8,
+        // Peripheral ID3 Register
+        GICD_PIDR3 = 0xffec,
+        // Peripheral ID4 Register
+        GICD_PIDR4 = 0xffd0,
+        // Peripheral ID5 Register
+        GICD_PIDR5 = 0xffd4,
+        // Peripheral ID6 Register
+        GICD_PIDR6 = 0xffd8,
+        // Peripheral ID7 Register
+        GICD_PIDR7 = 0xffdc,
+    };
+
+    // Interrupt Group Registers
+    static const AddrRange GICD_IGROUPR;
+    // Interrupt Set-Enable Registers
+    static const AddrRange GICD_ISENABLER;
+    // Interrupt Clear-Enable Registers
+    static const AddrRange GICD_ICENABLER;
+    // Interrupt Set-Pending Registers
+    static const AddrRange GICD_ISPENDR;
+    // Interrupt Clear-Pending Registers
+    static const AddrRange GICD_ICPENDR;
+    // Interrupt Set-Active Registers
+    static const AddrRange GICD_ISACTIVER;
+    // Interrupt Clear-Active Registers
+    static const AddrRange GICD_ICACTIVER;
+    // Interrupt Priority Registers
+    static const AddrRange GICD_IPRIORITYR;
+    // Interrupt Processor Targets Registers
+    static const AddrRange GICD_ITARGETSR; // GICv2 legacy
+    // Interrupt Configuration Registers
+    static const AddrRange GICD_ICFGR;
+    // Interrupt Group Modifier Registers
+    static const AddrRange GICD_IGRPMODR;
+    // Non-secure Access Control Registers
+    static const AddrRange GICD_NSACR;
+    // SGI Clear-Pending Registers
+    static const AddrRange GICD_CPENDSGIR; // GICv2 legacy
+    // SGI Set-Pending Registers
+    static const AddrRange GICD_SPENDSGIR; // GICv2 legacy
+    // Interrupt Routing Registers
+    static const AddrRange GICD_IROUTER;
+
+    BitUnion64(IROUTER)
+    Bitfield<63, 40> res0_1;
+    Bitfield<39, 32> Aff3;
+    Bitfield<31> IRM;
+    Bitfield<30, 24> res0_2;
+    Bitfield<23, 16> Aff2;
+    Bitfield<15, 8> Aff1;
+    Bitfield<7, 0> Aff0;
+    EndBitUnion(IROUTER)
+
+    static const uint32_t GICD_CTLR_ENABLEGRP0 = 1 << 0;
+    static const uint32_t GICD_CTLR_ENABLEGRP1NS = 1 << 1;
+    static const uint32_t GICD_CTLR_ENABLEGRP1S = 1 << 2;
+    static const uint32_t GICD_CTLR_ENABLEGRP1 = 1 << 0;
+    static const uint32_t GICD_CTLR_ENABLEGRP1A = 1 << 1;
+    static const uint32_t GICD_CTLR_DS = 1 << 6;
+
+    bool ARE;
+    bool DS;
+    bool EnableGrp1S;
+    bool EnableGrp1NS;
+    bool EnableGrp0;
+    std::vector <uint8_t> irqGroup;
+    std::vector <bool> irqEnabled;
+    std::vector <bool> irqPending;
+    std::vector <bool> irqActive;
+    std::vector <uint8_t> irqPriority;
+    std::vector <Gicv3::IntTriggerType> irqConfig;
+    std::vector <uint8_t> irqGrpmod;
+    std::vector <uint8_t> irqNsacr;
+    std::vector <IROUTER> irqAffinityRouting;
+
+  public:
+
+    static const uint32_t ADDR_RANGE_SIZE = 0x10000;
+
+    Gicv3Distributor(Gicv3 * gic, uint32_t it_lines);
+    ~Gicv3Distributor();
+    void init();
+    void initState();
+
+    uint64_t read(Addr addr, size_t size, bool is_secure_access);
+    void write(Addr addr, uint64_t data, size_t size,
+               bool is_secure_access);
+    void serialize(CheckpointOut & cp) const override;
+    void unserialize(CheckpointIn & cp) override;
+
+    bool
+    groupEnabled(Gicv3::GroupId group)
+    {
+        if (DS == 0) {
+            switch (group) {
+              case Gicv3::G0S:
+                return EnableGrp0;
+
+              case Gicv3::G1S:
+                return EnableGrp1S;
+
+              case Gicv3::G1NS:
+                return EnableGrp1NS;
+
+              default:
+                panic("Gicv3Distributor::groupEnabled(): "
+                        "invalid group!\n");
+            }
+        } else {
+            switch (group) {
+              case Gicv3::G0S:
+                return EnableGrp0;
+
+              case Gicv3::G1S:
+              case Gicv3::G1NS:
+                return EnableGrp1NS;
+
+              default:
+                panic("Gicv3Distributor::groupEnabled(): "
+                        "invalid group!\n");
+            }
+        }
+    }
+
+    void sendInt(uint32_t int_id);
+    void intDeasserted(uint32_t int_id);
+    Gicv3::IntStatus intStatus(uint32_t int_id);
+    void updateAndInformCPUInterfaces();
+    void update();
+    void fullUpdate();
+    void activateIRQ(uint32_t int_id);
+    void deactivateIRQ(uint32_t int_id);
+
+    inline bool isNotSPI(uint8_t int_id)
+    {
+        if (int_id < (Gicv3::SGI_MAX + Gicv3::PPI_MAX) || int_id >= itLines) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    inline bool nsAccessToSecInt(uint8_t int_id, bool is_secure_access)
+    {
+        return !DS && !is_secure_access && getIntGroup(int_id) != Gicv3::G1NS;
+    }
+
+  protected:
+
+    void reset();
+    Gicv3::GroupId getIntGroup(int int_id);
+};
+
+#endif //__DEV_ARM_GICV3_DISTRIBUTOR_H__
diff --git a/src/dev/arm/gic_v3_redistributor.cc b/src/dev/arm/gic_v3_redistributor.cc
new file mode 100644
index 0000000..ec1e388
--- /dev/null
+++ b/src/dev/arm/gic_v3_redistributor.cc
@@ -0,0 +1,834 @@
+/*
+ * Copyright (c) 2018 Metempsy Technology Consulting
+ * All rights reserved.
+ *
+ * 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: Jairo Balart
+ */
+
+#include "dev/arm/gic_v3_redistributor.hh"
+
+#include "arch/arm/utility.hh"
+#include "debug/GIC.hh"
+#include "dev/arm/gic_v3_cpu_interface.hh"
+#include "dev/arm/gic_v3_distributor.hh"
+
+const AddrRange Gicv3Redistributor::GICR_IPRIORITYR(SGI_base + 0x0400,
+        SGI_base + 0x041f);
+
+Gicv3Redistributor::Gicv3Redistributor(Gicv3 * gic, uint32_t cpu_id)
+    : gic(gic),
+      distributor(nullptr),
+      cpuInterface(nullptr),
+      cpuId(cpu_id),
+      irqGroup(Gicv3::SGI_MAX + Gicv3::PPI_MAX),
+      irqEnabled(Gicv3::SGI_MAX + Gicv3::PPI_MAX),
+      irqPending(Gicv3::SGI_MAX + Gicv3::PPI_MAX),
+      irqActive(Gicv3::SGI_MAX + Gicv3::PPI_MAX),
+      irqPriority(Gicv3::SGI_MAX + Gicv3::PPI_MAX),
+      irqConfig(Gicv3::SGI_MAX + Gicv3::PPI_MAX),
+      irqGrpmod(Gicv3::SGI_MAX + Gicv3::PPI_MAX),
+      irqNsacr(Gicv3::SGI_MAX + Gicv3::PPI_MAX)
+{
+}
+
+Gicv3Redistributor::~Gicv3Redistributor()
+{
+}
+
+void
+Gicv3Redistributor::init()
+{
+    distributor = gic->getDistributor();
+    cpuInterface = gic->getCPUInterface(cpuId);
+}
+
+void
+Gicv3Redistributor::initState()
+{
+    reset();
+}
+
+void
+Gicv3Redistributor::reset()
+{
+    peInLowPowerState = true;
+    std::fill(irqGroup.begin(), irqGroup.end(), 0);
+    std::fill(irqEnabled.begin(), irqEnabled.end(), false);
+    std::fill(irqPending.begin(), irqPending.end(), false);
+    std::fill(irqActive.begin(), irqActive.end(), false);
+    std::fill(irqPriority.begin(), irqPriority.end(), 0);
+
+    // SGIs have edge-triggered behavior
+    for (uint32_t int_id = 0; int_id < Gicv3::SGI_MAX; int_id++) {
+        irqConfig[int_id] = Gicv3::INT_EDGE_TRIGGERED;
+    }
+
+    std::fill(irqGrpmod.begin(), irqGrpmod.end(), 0);
+    std::fill(irqNsacr.begin(), irqNsacr.end(), 0);
+    DPG1S = false;
+    DPG1NS = false;
+    DPG0 = false;
+}
+
+uint64_t
+Gicv3Redistributor::read(Addr addr, size_t size, bool is_secure_access)
+{
+    if (GICR_IPRIORITYR.contains(addr)) { // Interrupt Priority Registers
+        uint64_t value = 0;
+        int first_intid = addr - GICR_IPRIORITYR.start();
+
+        for (int i = 0, int_id = first_intid; i < size; i++, int_id++) {
+            uint8_t prio = irqPriority[int_id];
+
+            if (!distributor->DS && !is_secure_access) {
+                if (getIntGroup(int_id) != Gicv3::G1NS) {
+                    // RAZ/WI for non-secure accesses for secure interrupts
+                    continue;
+                } else {
+                    // NS view
+                    prio = (prio << 1) & 0xff;
+                }
+            }
+
+            value |= prio << (i * 8);
+        }
+
+        return value;
+    }
+
+    switch (addr) {
+      case GICR_CTLR: { // Control Register
+          uint64_t value = 0;
+
+          if (DPG1S) {
+              value |= GICR_CTLR_DPG1S;
+          }
+
+          if (DPG1NS) {
+              value |= GICR_CTLR_DPG1NS;
+          }
+
+          if (DPG0) {
+              value |= GICR_CTLR_DPG0;
+          }
+
+          return value;
+      }
+
+      case GICR_IIDR: // Implementer Identification Register
+        //return 0x43b; // r0p0 GIC-500
+        return 0;
+
+      case GICR_TYPER: { // Type Register
+          /*
+           * Affinity_Value   [63:32] == X
+           * (The identity of the PE associated with this Redistributor)
+           * CommonLPIAff     [25:24] == 01
+           * (All Redistributors with the same Aff3 value must share an
+           * LPI Configuration table)
+           * Processor_Number [23:8]  == X
+           * (A unique identifier for the PE)
+           * DPGS             [5]     == 1
+           * (GICR_CTLR.DPG* bits are supported)
+           * 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)
+           * VLPIS            [1]     == 0
+           * (virtual LPIs not supported)
+           * PLPIS            [0]     == 0
+           * (physical LPIs not supported)
+           */
+          uint64_t affinity = getAffinity();
+          int last = cpuId == (gic->getSystem()->numContexts() - 1);
+          return (affinity << 32) | (1 << 24) | (cpuId << 8) |
+              (1 << 5) | (last << 4);
+      }
+
+      case GICR_WAKER: // Wake Register
+        if (!distributor->DS && !is_secure_access) {
+            // RAZ/WI for non-secure accesses
+            return 0;
+        }
+
+        if (peInLowPowerState) {
+            return GICR_WAKER_ChildrenAsleep | GICR_WAKER_ProcessorSleep;
+        } else {
+            return 0;
+        }
+
+      case GICR_PIDR0: { // Peripheral ID0 Register
+          uint8_t part_0 = 0x92; // Part number, bits[7:0]
+          return part_0;
+      }
+
+      case GICR_PIDR1: { // Peripheral ID1 Register
+          uint8_t des_0 = 0xB; // JEP106 identification code, bits[3:0]
+          uint8_t part_1 = 0x4; // Part number, bits[11:8]
+          return (des_0 << 4) | (part_1 << 0);
+      }
+
+      case GICR_PIDR2: { // Peripheral ID2 Register
+          uint8_t arch_rev = 0x3; // 0x3 GICv3
+          uint8_t jedec = 0x1; // JEP code
+          uint8_t des_1 = 0x3; // JEP106 identification code, bits[6:4]
+          return (arch_rev << 4) | (jedec << 3) | (des_1 << 0);
+      }
+
+      case GICR_PIDR3: // Peripheral ID3 Register
+        return 0x0; // Implementation defined
+
+      case GICR_PIDR4: { // Peripheral ID4 Register
+          uint8_t size = 0x4; // 64 KB software visible page
+          uint8_t des_2 = 0x4; // ARM implementation
+          return (size << 4) | (des_2 << 0);
+      }
+
+      case GICR_PIDR5: // Peripheral ID5 Register
+      case GICR_PIDR6: // Peripheral ID6 Register
+      case GICR_PIDR7: // Peripheral ID7 Register
+        return 0; // RES0
+
+      case GICR_IGROUPR0: { // Interrupt Group Register 0
+          uint64_t value = 0;
+
+          if (!distributor->DS && !is_secure_access) {
+              // RAZ/WI for non-secure accesses
+              return 0;
+          }
+
+          for (int int_id = 0; int_id < 8 * size; int_id++) {
+              value |= (irqGroup[int_id] << int_id);
+          }
+
+          return value;
+      }
+
+      case GICR_ISENABLER0: // Interrupt Set-Enable Register 0
+      case GICR_ICENABLER0: { // Interrupt Clear-Enable Register 0
+          uint64_t value = 0;
+
+          for (int int_id = 0; int_id < 8 * size; int_id++) {
+              if (!distributor->DS && !is_secure_access) {
+                  // RAZ/WI for non-secure accesses for secure interrupts
+                  if (getIntGroup(int_id) != Gicv3::G1NS) {
+                      continue;
+                  }
+              }
+
+              if (irqEnabled[int_id]) {
+                  value |= (1 << int_id);
+              }
+          }
+
+          return value;
+      }
+
+      case GICR_ISPENDR0: // Interrupt Set-Pending Register 0
+      case GICR_ICPENDR0: { // Interrupt Clear-Pending Register 0
+          uint64_t value = 0;
+
+          for (int int_id = 0; int_id < 8 * size; int_id++) {
+              if (!distributor->DS && !is_secure_access) {
+                  // RAZ/WI for non-secure accesses for secure interrupts
+                  if (getIntGroup(int_id) != Gicv3::G1NS) {
+                      continue;
+                  }
+              }
+
+              value |= (irqPending[int_id] << int_id);
+          }
+
+          return value;
+      }
+
+      case GICR_ISACTIVER0: // Interrupt Set-Active Register 0
+      case GICR_ICACTIVER0: { // Interrupt Clear-Active Register 0
+          uint64_t value = 0;
+
+          for (int int_id = 0; int_id < 8 * size; int_id++) {
+              if (!distributor->DS && !is_secure_access) {
+                  // RAZ/WI for non-secure accesses for secure interrupts
+                  if (getIntGroup(int_id) != Gicv3::G1NS) {
+                      continue;
+                  }
+              }
+
+              value |=  irqActive[int_id] << int_id;
+          }
+
+          return value;
+      }
+
+      case GICR_ICFGR0: // SGI Configuration Register
+      case GICR_ICFGR1: { // PPI Configuration Register
+          uint64_t value = 0;
+          uint32_t first_int_id = addr == GICR_ICFGR0 ? 0 : Gicv3::SGI_MAX;
+
+          for (int i = 0, int_id = first_int_id; i < 32;
+                  i = i + 2, int_id++) {
+              if (!distributor->DS && !is_secure_access) {
+                  // RAZ/WI for non-secure accesses for secure interrupts
+                  if (getIntGroup(int_id) != Gicv3::G1NS) {
+                      continue;
+                  }
+              }
+
+              if (irqConfig[int_id] == Gicv3::INT_EDGE_TRIGGERED) {
+                  value |= (0x2) << i;
+              }
+          }
+
+          return value;
+      }
+
+      case GICR_IGRPMODR0: { // Interrupt Group Modifier Register 0
+          uint64_t value = 0;
+
+          if (distributor->DS) {
+              value = 0;
+          } else {
+              if (!is_secure_access) {
+                  // RAZ/WI for non-secure accesses
+                  value = 0;
+              } else {
+                  for (int int_id = 0; int_id < 8 * size; int_id++) {
+                      value |= irqGrpmod[int_id] << int_id;
+                  }
+              }
+          }
+
+          return value;
+      }
+
+      case GICR_NSACR: { // Non-secure Access Control Register
+          uint64_t value = 0;
+
+          if (distributor->DS) {
+              // RAZ/WI
+              value = 0;
+          } else {
+              if (!is_secure_access) {
+                  // RAZ/WI
+                  value = 0;
+              } else {
+                  for (int i = 0, int_id = 0; i < 8 * size;
+                          i = i + 2, int_id++) {
+                      value |= irqNsacr[int_id] << i;
+                  }
+              }
+          }
+
+          return value;
+      }
+
+      default:
+        panic("Gicv3Redistributor::read(): invalid offset %#x\n", addr);
+        break;
+    }
+}
+
+void
+Gicv3Redistributor::write(Addr addr, uint64_t data, size_t size,
+                          bool is_secure_access)
+{
+    if (GICR_IPRIORITYR.contains(addr)) { // Interrupt Priority Registers
+        int first_intid = addr - GICR_IPRIORITYR.start();
+
+        for (int i = 0, int_id = first_intid; i < size; i++, int_id++) {
+            uint8_t prio = bits(data, (i + 1) * 8 - 1, (i * 8));
+
+            if (!distributor->DS && !is_secure_access) {
+                if (getIntGroup(int_id) != Gicv3::G1NS) {
+                    // RAZ/WI for non-secure accesses for secure interrupts
+                    continue;
+                } else {
+                    // NS view
+                    prio = 0x80 | (prio >> 1);
+                }
+            }
+
+            irqPriority[int_id] = prio;
+            DPRINTF(GIC, "Gicv3Redistributor::write(): "
+                    "int_id %d priority %d\n", int_id, irqPriority[int_id]);
+        }
+
+        return;
+    }
+
+    switch (addr) {
+      case GICR_CTLR: {
+          // GICR_TYPER.LPIS is 0 so Enable_LPIs is RES0
+          DPG1S = data & GICR_CTLR_DPG1S;
+          DPG1NS = data & GICR_CTLR_DPG1NS;
+          DPG0 = data & GICR_CTLR_DPG0;
+          break;
+      }
+
+      case GICR_WAKER: // Wake Register
+        if (!distributor->DS && !is_secure_access) {
+            // RAZ/WI for non-secure accesses
+            return;
+        }
+
+        if (not peInLowPowerState and
+                (data & GICR_WAKER_ProcessorSleep)) {
+            DPRINTF(GIC, "Gicv3Redistributor::write(): "
+                    "PE entering in low power state\n");
+        } else if (peInLowPowerState and
+                not(data & GICR_WAKER_ProcessorSleep)) {
+            DPRINTF(GIC, "Gicv3Redistributor::write(): powering up PE\n");
+        }
+
+        peInLowPowerState = data & GICR_WAKER_ProcessorSleep;
+        break;
+
+      case GICR_IGROUPR0: // Interrupt Group Register 0
+        if (!distributor->DS && !is_secure_access) {
+            // RAZ/WI for non-secure accesses
+            return;
+        }
+
+        for (int int_id = 0; int_id < 8 * size; int_id++) {
+            irqGroup[int_id] = data & (1 << int_id) ? 1 : 0;
+            DPRINTF(GIC, "Gicv3Redistributor::write(): "
+                    "int_id %d group %d\n", int_id, irqGroup[int_id]);
+        }
+
+        break;
+
+      case GICR_ISENABLER0: // Interrupt Set-Enable Register 0
+        for (int int_id = 0; int_id < 8 * size; int_id++) {
+            if (!distributor->DS && !is_secure_access) {
+                // RAZ/WI for non-secure accesses for secure interrupts
+                if (getIntGroup(int_id) != Gicv3::G1NS) {
+                    continue;
+                }
+            }
+
+            bool enable = data & (1 << int_id) ? 1 : 0;
+
+            if (enable) {
+                irqEnabled[int_id] = true;
+            }
+
+            DPRINTF(GIC, "Gicv3Redistributor::write(): "
+                    "int_id %d enable %i\n", int_id, irqEnabled[int_id]);
+        }
+
+        break;
+
+      case GICR_ICENABLER0: // Interrupt Clear-Enable Register 0
+        for (int int_id = 0; int_id < 8 * size; int_id++) {
+            if (!distributor->DS && !is_secure_access) {
+                // RAZ/WI for non-secure accesses for secure interrupts
+                if (getIntGroup(int_id) != Gicv3::G1NS) {
+                    continue;
+                }
+            }
+
+            bool disable = data & (1 << int_id) ? 1 : 0;
+
+            if (disable) {
+                irqEnabled[int_id] = false;
+            }
+
+            DPRINTF(GIC, "Gicv3Redistributor::write(): "
+                    "int_id %d enable %i\n", int_id, irqEnabled[int_id]);
+        }
+
+        break;
+
+      case GICR_ISPENDR0: // Interrupt Set-Pending Register 0
+        for (int int_id = 0; int_id < 8 * size; int_id++) {
+            if (!distributor->DS && !is_secure_access) {
+                // RAZ/WI for non-secure accesses for secure interrupts
+                if (getIntGroup(int_id) != Gicv3::G1NS) {
+                    continue;
+                }
+            }
+
+            bool pending = data & (1 << int_id) ? 1 : 0;
+
+            if (pending) {
+                DPRINTF(GIC, "Gicv3Redistributor::write() "
+                        "(GICR_ISPENDR0): int_id %d (PPI) "
+                        "pending bit set\n", int_id);
+                irqPending[int_id] = true;
+            }
+        }
+
+        updateAndInformCPUInterface();
+        break;
+
+      case GICR_ICPENDR0:// Interrupt Clear-Pending Register 0
+        for (int int_id = 0; int_id < 8 * size; int_id++) {
+            if (!distributor->DS && !is_secure_access) {
+                // RAZ/WI for non-secure accesses for secure interrupts
+                if (getIntGroup(int_id) != Gicv3::G1NS) {
+                    continue;
+                }
+            }
+
+            bool clear = data & (1 << int_id) ? 1 : 0;
+
+            if (clear) {
+                irqPending[int_id] = false;
+            }
+        }
+
+        break;
+
+      case GICR_ISACTIVER0: // Interrupt Set-Active Register 0
+        for (int int_id = 0; int_id < 8 * size; int_id++) {
+            if (!distributor->DS && !is_secure_access) {
+                // RAZ/WI for non-secure accesses for secure interrupts
+                if (getIntGroup(int_id) != Gicv3::G1NS) {
+                    continue;
+                }
+            }
+
+            bool activate = data & (1 << int_id) ? 1 : 0;
+
+            if (activate) {
+                if (!irqActive[int_id]) {
+                    DPRINTF(GIC, "Gicv3Redistributor::write(): "
+                            "int_id %d active set\n", int_id);
+                }
+
+                irqActive[int_id] = true;
+            }
+        }
+
+        break;
+
+      case GICR_ICACTIVER0: // Interrupt Clear-Active Register 0
+        for (int int_id = 0; int_id < 8 * size; int_id++) {
+            if (!distributor->DS && !is_secure_access) {
+                // RAZ/WI for non-secure accesses for secure interrupts
+                if (getIntGroup(int_id) != Gicv3::G1NS) {
+                    continue;
+                }
+            }
+
+            bool clear = data & (1 << int_id) ? 1 : 0;
+
+            if (clear) {
+                if (irqActive[int_id]) {
+                    DPRINTF(GIC, "Gicv3Redistributor::write(): "
+                            "int_id %d active cleared\n", int_id);
+                }
+
+                irqActive[int_id] = false;
+            }
+        }
+
+        break;
+
+      case GICR_ICFGR1: { // PPI Configuration Register
+          int first_intid = Gicv3::SGI_MAX;
+
+          for (int i = 0, int_id = first_intid; i < 8 * size;
+                  i = i + 2, int_id++) {
+              if (!distributor->DS && !is_secure_access) {
+                  // RAZ/WI for non-secure accesses for secure interrupts
+                  if (getIntGroup(int_id) != Gicv3::G1NS) {
+                      continue;
+                  }
+              }
+
+              irqConfig[int_id] = data & (0x2 << i)
+                  ? Gicv3::INT_EDGE_TRIGGERED :
+                  Gicv3::INT_LEVEL_SENSITIVE;
+              DPRINTF(GIC, "Gicv3Redistributor::write(): "
+                      "int_id %d (PPI) config %d\n",
+                      int_id, irqConfig[int_id]);
+          }
+
+          break;
+      }
+
+      case GICR_IGRPMODR0: { // Interrupt Group Modifier Register 0
+          if (distributor->DS) {
+              // RAZ/WI if secutiry disabled
+          } else {
+              for (int int_id = 0; int_id < 8 * size; int_id++) {
+                  if (!is_secure_access) {
+                      // RAZ/WI for non-secure accesses
+                      continue;
+                  }
+
+                  irqGrpmod[int_id] = data & (1 << int_id);
+              }
+          }
+
+          break;
+      }
+
+      case GICR_NSACR: { // Non-secure Access Control Register
+          if (distributor->DS) {
+              // RAZ/WI
+          } else {
+              if (!is_secure_access) {
+                  // RAZ/WI
+              } else {
+                  for (int i = 0, int_id = 0; i < 8 * size;
+                          i = i + 2, int_id++) {
+                      irqNsacr[int_id] = (data >> i) & 0x3;
+                  }
+              }
+          }
+
+          break;
+      }
+
+      default:
+        panic("Gicv3Redistributor::write(): invalid offset %#x\n", addr);
+        break;
+    }
+}
+
+void
+Gicv3Redistributor::sendPPInt(uint32_t int_id)
+{
+    assert((int_id >= Gicv3::SGI_MAX) &&
+           (int_id < Gicv3::SGI_MAX + Gicv3::PPI_MAX));
+    irqPending[int_id] = true;
+    DPRINTF(GIC, "Gicv3Redistributor::sendPPInt(): "
+            "int_id %d (PPI) pending bit set\n", int_id);
+    updateAndInformCPUInterface();
+}
+
+void
+Gicv3Redistributor::sendSGI(uint32_t int_id, Gicv3::GroupId group, bool ns)
+{
+    assert(int_id < Gicv3::SGI_MAX);
+    Gicv3::GroupId int_group = getIntGroup(int_id);
+
+    // asked for secure group 1
+    // configured as group 0
+    // send group 0
+    if (int_group == Gicv3::G0S && group == Gicv3::G1S) {
+        group = Gicv3::G0S;
+    }
+
+    if (group == Gicv3::G0S and int_group != Gicv3::G0S) {
+        return;
+    }
+
+    if (ns && distributor->DS == 0) {
+        int nsaccess = irqNsacr[int_id];
+
+        if ((int_group == Gicv3::G0S && nsaccess < 1) ||
+                (int_group == Gicv3::G1S && nsaccess < 2)) {
+            return;
+        }
+    }
+
+    irqPending[int_id] = true;
+    DPRINTF(GIC, "Gicv3ReDistributor::sendSGI(): "
+            "int_id %d (SGI) pending bit set\n", int_id);
+    updateAndInformCPUInterface();
+}
+
+Gicv3::IntStatus
+Gicv3Redistributor::intStatus(uint32_t int_id)
+{
+    assert(int_id < Gicv3::SGI_MAX + Gicv3::PPI_MAX);
+
+    if (irqPending[int_id]) {
+        if (irqActive[int_id]) {
+            return Gicv3::INT_ACTIVE_PENDING;
+        }
+
+        return Gicv3::INT_PENDING;
+    } else if (irqActive[int_id]) {
+        return Gicv3::INT_ACTIVE;
+    } else {
+        return Gicv3::INT_INACTIVE;
+    }
+}
+
+/*
+ * Recalculate the highest priority pending interrupt after a
+ * change to redistributor state.
+ */
+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);
+
+        if (irqPending[int_id] && irqEnabled[int_id] &&
+                !irqActive[int_id] && group_enabled) {
+            if ((irqPriority[int_id] < cpuInterface->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] == cpuInterface->hppi.prio &&
+                     int_id < cpuInterface->hppi.intid)) {
+                cpuInterface->hppi.intid = int_id;
+                cpuInterface->hppi.prio = irqPriority[int_id];
+                cpuInterface->hppi.group = int_group;
+                new_hppi = true;
+            }
+        }
+    }
+
+    if (!new_hppi && cpuInterface->hppi.prio != 0xff &&
+            cpuInterface->hppi.intid < Gicv3::SGI_MAX + Gicv3::PPI_MAX) {
+        distributor->fullUpdate();
+    }
+}
+
+void
+Gicv3Redistributor::updateAndInformCPUInterface()
+{
+    update();
+    cpuInterface->update();
+}
+
+Gicv3::GroupId
+Gicv3Redistributor::getIntGroup(int int_id)
+{
+    assert(int_id < (Gicv3::SGI_MAX + Gicv3::PPI_MAX));
+
+    if (distributor->DS) {
+        if (irqGroup[int_id] == 0) {
+            return Gicv3::G0S;
+        } else {
+            return Gicv3::G1NS;
+        }
+    } else {
+        if (irqGrpmod[int_id] == 0 && irqGroup[int_id] == 0) {
+            return Gicv3::G0S;
+        } else if (irqGrpmod[int_id] == 0 && irqGroup[int_id] == 1) {
+            return Gicv3::G1NS;
+        } else if (irqGrpmod[int_id] == 1 && irqGroup[int_id] == 0) {
+            return Gicv3::G1S;
+        } else if (irqGrpmod[int_id] == 1 && irqGroup[int_id] == 1) {
+            return Gicv3::G1NS;
+        }
+    }
+
+    M5_UNREACHABLE;
+}
+
+void
+Gicv3Redistributor::activateIRQ(uint32_t int_id)
+{
+    irqPending[int_id] = false;
+    irqActive[int_id] = true;
+}
+
+void
+Gicv3Redistributor::deactivateIRQ(uint32_t int_id)
+{
+    irqActive[int_id] = false;
+}
+
+uint32_t
+Gicv3Redistributor::getAffinity()
+{
+    ThreadContext * tc = gic->getSystem()->getThreadContext(cpuId);
+    uint64_t mpidr = getMPIDR(gic->getSystem(), tc);
+    /*
+     * Aff3 = MPIDR[39:32]
+     * (Note getMPIDR() returns uint32_t so Aff3 is always 0...)
+     * Aff2 = MPIDR[23:16]
+     * Aff1 = MPIDR[15:8]
+     * Aff0 = MPIDR[7:0]
+     * affinity = Aff3.Aff2.Aff1.Aff0
+     */
+    uint64_t affinity = ((mpidr & 0xff00000000) >> 8) | (mpidr & (0xffffff));
+    return affinity;
+}
+
+bool
+Gicv3Redistributor::canBeSelectedFor1toNInterrupt(Gicv3::GroupId group)
+{
+    if (peInLowPowerState) {
+        return false;
+    }
+
+    if (!distributor->groupEnabled(group)) {
+        return false;
+    }
+
+    if ((group == Gicv3::G1S) && DPG1S) {
+        return false;
+    }
+
+    if ((group == Gicv3::G1NS) && DPG1NS) {
+        return false;
+    }
+
+    if ((group == Gicv3::G0S) && DPG0) {
+        return false;
+    }
+
+    return true;
+}
+
+void
+Gicv3Redistributor::serialize(CheckpointOut & cp) const
+{
+    SERIALIZE_SCALAR(peInLowPowerState);
+    SERIALIZE_CONTAINER(irqGroup);
+    SERIALIZE_CONTAINER(irqEnabled);
+    SERIALIZE_CONTAINER(irqPending);
+    SERIALIZE_CONTAINER(irqActive);
+    SERIALIZE_CONTAINER(irqPriority);
+    SERIALIZE_CONTAINER(irqConfig);
+    SERIALIZE_CONTAINER(irqGrpmod);
+    SERIALIZE_CONTAINER(irqNsacr);
+    SERIALIZE_SCALAR(DPG1S);
+    SERIALIZE_SCALAR(DPG1NS);
+    SERIALIZE_SCALAR(DPG0);
+}
+
+void
+Gicv3Redistributor::unserialize(CheckpointIn & cp)
+{
+    UNSERIALIZE_SCALAR(peInLowPowerState);
+    UNSERIALIZE_CONTAINER(irqGroup);
+    UNSERIALIZE_CONTAINER(irqEnabled);
+    UNSERIALIZE_CONTAINER(irqPending);
+    UNSERIALIZE_CONTAINER(irqActive);
+    UNSERIALIZE_CONTAINER(irqPriority);
+    UNSERIALIZE_CONTAINER(irqConfig);
+    UNSERIALIZE_CONTAINER(irqGrpmod);
+    UNSERIALIZE_CONTAINER(irqNsacr);
+    UNSERIALIZE_SCALAR(DPG1S);
+    UNSERIALIZE_SCALAR(DPG1NS);
+    UNSERIALIZE_SCALAR(DPG0);
+}
diff --git a/src/dev/arm/gic_v3_redistributor.hh b/src/dev/arm/gic_v3_redistributor.hh
new file mode 100644
index 0000000..1155397
--- /dev/null
+++ b/src/dev/arm/gic_v3_redistributor.hh
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2018 Metempsy Technology Consulting
+ * All rights reserved.
+ *
+ * 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: Jairo Balart
+ */
+
+#ifndef __DEV_ARM_GICV3_REDISTRIBUTOR_H__
+#define __DEV_ARM_GICV3_REDISTRIBUTOR_H__
+
+#include "base/addr_range.hh"
+#include "dev/arm/gic_v3.hh"
+#include "sim/serialize.hh"
+
+class Gicv3Distributor;
+class Gicv3CPUInterface;
+
+class Gicv3Redistributor : public Serializable
+{
+  private:
+
+    friend class Gicv3CPUInterface;
+    friend class Gicv3Distributor;
+
+  protected:
+
+    Gicv3 * gic;
+    Gicv3Distributor * distributor;
+    Gicv3CPUInterface * cpuInterface;
+    uint32_t cpuId;
+
+    /*
+     * GICv3 defines 2 contiguous 64KB frames for each redistributor.
+     * Order of frames must be RD_base, SGI_base.
+     */
+    static const uint32_t RD_base = 0x0;
+    static const uint32_t SGI_base = 0x10000;
+
+    enum {
+        // Control Register
+        GICR_CTLR  = RD_base + 0x0000,
+        // Implementer Identification Register
+        GICR_IIDR = RD_base + 0x0004,
+        // Type Register
+        GICR_TYPER = RD_base + 0x0008,
+        // Wake Register
+        GICR_WAKER = RD_base + 0x0014,
+        // Peripheral ID0 Register
+        GICR_PIDR0 = RD_base + 0xffe0,
+        // Peripheral ID1 Register
+        GICR_PIDR1 = RD_base + 0xffe4,
+        // Peripheral ID2 Register
+        GICR_PIDR2 = RD_base + 0xffe8,
+        // Peripheral ID3 Register
+        GICR_PIDR3 = RD_base + 0xffec,
+        // Peripheral ID4 Register
+        GICR_PIDR4 = RD_base + 0xffd0,
+        // Peripheral ID5 Register
+        GICR_PIDR5 = RD_base + 0xffd4,
+        // Peripheral ID6 Register
+        GICR_PIDR6 = RD_base + 0xffd8,
+        // Peripheral ID7 Register
+        GICR_PIDR7 = RD_base + 0xffdc,
+    };
+
+    static const uint32_t GICR_WAKER_ProcessorSleep = 1 << 1;
+    static const uint32_t GICR_WAKER_ChildrenAsleep = 1 << 2;
+
+    bool peInLowPowerState;
+
+    enum {
+        // Interrupt Group Register 0
+        GICR_IGROUPR0   = SGI_base + 0x0080,
+        // Interrupt Set-Enable Register 0
+        GICR_ISENABLER0 = SGI_base + 0x0100,
+        // Interrupt Clear-Enable Register 0
+        GICR_ICENABLER0 = SGI_base + 0x0180,
+        // Interrupt Set-Pending Register 0
+        GICR_ISPENDR0 = SGI_base + 0x0200,
+        // Interrupt Clear-Pending Register 0
+        GICR_ICPENDR0 = SGI_base + 0x0280,
+        // Interrupt Set-Active Register 0
+        GICR_ISACTIVER0 = SGI_base + 0x0300,
+        // Interrupt Clear-Active Register 0
+        GICR_ICACTIVER0 = SGI_base + 0x0380,
+        // SGI Configuration Register
+        GICR_ICFGR0 = SGI_base + 0x0c00,
+        // PPI Configuration Register
+        GICR_ICFGR1 = SGI_base + 0x0c04,
+        // Interrupt Group Modifier Register 0
+        GICR_IGRPMODR0 = SGI_base + 0x0d00,
+        // Non-secure Access Control Register
+        GICR_NSACR = SGI_base + 0x0e00,
+    };
+
+    // Interrupt Priority Registers
+    static const AddrRange GICR_IPRIORITYR;
+
+    std::vector <uint8_t> irqGroup;
+    std::vector <bool> irqEnabled;
+    std::vector <bool> irqPending;
+    std::vector <bool> irqActive;
+    std::vector <uint8_t> irqPriority;
+    std::vector <Gicv3::IntTriggerType> irqConfig;
+    std::vector <uint8_t> irqGrpmod;
+    std::vector <uint8_t> irqNsacr;
+
+    bool DPG1S;
+    bool DPG1NS;
+    bool DPG0;
+
+    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;
+
+  public:
+
+    /*
+     * GICv3 defines only 2 64K consecutive frames for the redistributor
+     * (RD_base and SGI_base) but we are using 2 extra 64K stride frames
+     * to match GICv4 that defines 4 64K consecutive frames for them.
+     * Note this must match with DTB/DTS GIC node definition and boot
+     * loader code.
+     */
+    static const uint32_t ADDR_RANGE_SIZE = 0x40000;
+
+    Gicv3Redistributor(Gicv3 * gic, uint32_t cpu_id);
+    ~Gicv3Redistributor();
+    void init();
+    void initState();
+
+    uint64_t read(Addr addr, size_t size, bool is_secure_access);
+    void write(Addr addr, uint64_t data, size_t size,
+               bool is_secure_access);
+    void sendPPInt(uint32_t int_id);
+    void sendSGI(uint32_t int_id, Gicv3::GroupId group, bool ns);
+    void serialize(CheckpointOut & cp) const override;
+    void unserialize(CheckpointIn & cp) override;
+    uint32_t getAffinity();
+
+    Gicv3CPUInterface *
+    getCPUInterface() const
+    {
+        return cpuInterface;
+    }
+
+    bool canBeSelectedFor1toNInterrupt(Gicv3::GroupId group);
+
+  protected:
+
+    void reset();
+    void update();
+    void updateAndInformCPUInterface();
+    Gicv3::IntStatus intStatus(uint32_t int_id);
+    Gicv3::GroupId getIntGroup(int int_id);
+    void activateIRQ(uint32_t int_id);
+    void deactivateIRQ(uint32_t int_id);
+};
+
+#endif //__DEV_ARM_GICV3_REDISTRIBUTOR_H__