]> git.proxmox.com Git - mirror_edk2.git/blobdiff - ArmPkg/Drivers/ArmGic/GicV3/ArmGicV3Dxe.c
ArmPkg: Fix writes to GICv3 GICD_IROUTER<n> reg
[mirror_edk2.git] / ArmPkg / Drivers / ArmGic / GicV3 / ArmGicV3Dxe.c
index 50fa56262eaf3393884665554c7196635240f367..67c74f79654586f8b6e47795d3c7400b88172d6e 100644 (file)
@@ -1,6 +1,6 @@
 /** @file\r
 *\r
-*  Copyright (c) 2011-2015, ARM Limited. All rights reserved.\r
+*  Copyright (c) 2011-2018, ARM Limited. All rights reserved.\r
 *\r
 *  This program and the accompanying materials\r
 *  are licensed and made available under the terms and conditions of the BSD License\r
@@ -19,6 +19,7 @@
 #define ARM_GIC_DEFAULT_PRIORITY  0x80\r
 \r
 extern EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptV3Protocol;\r
+extern EFI_HARDWARE_INTERRUPT2_PROTOCOL gHardwareInterrupt2V3Protocol;\r
 \r
 STATIC UINTN mGicDistributorBase;\r
 STATIC UINTN mGicRedistributorsBase;\r
@@ -33,6 +34,7 @@ STATIC UINTN mGicRedistributorsBase;
   @retval EFI_DEVICE_ERROR  Hardware could not be programmed.\r
 \r
 **/\r
+STATIC\r
 EFI_STATUS\r
 EFIAPI\r
 GicV3EnableInterruptSource (\r
@@ -60,6 +62,7 @@ GicV3EnableInterruptSource (
   @retval EFI_DEVICE_ERROR  Hardware could not be programmed.\r
 \r
 **/\r
+STATIC\r
 EFI_STATUS\r
 EFIAPI\r
 GicV3DisableInterruptSource (\r
@@ -88,6 +91,7 @@ GicV3DisableInterruptSource (
   @retval EFI_DEVICE_ERROR  InterruptState is not valid\r
 \r
 **/\r
+STATIC\r
 EFI_STATUS\r
 EFIAPI\r
 GicV3GetInterruptSourceState (\r
@@ -101,7 +105,11 @@ GicV3GetInterruptSourceState (
     return EFI_UNSUPPORTED;\r
   }\r
 \r
-  *InterruptState = ArmGicIsInterruptEnabled (mGicDistributorBase, mGicRedistributorsBase, Source);\r
+  *InterruptState = ArmGicIsInterruptEnabled (\r
+                      mGicDistributorBase,\r
+                      mGicRedistributorsBase,\r
+                      Source\r
+                      );\r
 \r
   return EFI_SUCCESS;\r
 }\r
@@ -117,6 +125,7 @@ GicV3GetInterruptSourceState (
   @retval EFI_DEVICE_ERROR  Hardware could not be programmed.\r
 \r
 **/\r
+STATIC\r
 EFI_STATUS\r
 EFIAPI\r
 GicV3EndOfInterrupt (\r
@@ -137,13 +146,15 @@ GicV3EndOfInterrupt (
   EFI_CPU_INTERRUPT_HANDLER that is called when a processor interrupt occurs.\r
 \r
   @param  InterruptType    Defines the type of interrupt or exception that\r
-                           occurred on the processor.This parameter is processor architecture specific.\r
+                           occurred on the processor. This parameter is\r
+                           processor architecture specific.\r
   @param  SystemContext    A pointer to the processor context when\r
                            the interrupt occurred on the processor.\r
 \r
   @return None\r
 \r
 **/\r
+STATIC\r
 VOID\r
 EFIAPI\r
 GicV3IrqInterruptHandler (\r
@@ -168,15 +179,12 @@ GicV3IrqInterruptHandler (
     // Call the registered interrupt handler.\r
     InterruptHandler (GicInterrupt, SystemContext);\r
   } else {\r
-    DEBUG ((EFI_D_ERROR, "Spurious GIC interrupt: 0x%x\n", GicInterrupt));\r
+    DEBUG ((DEBUG_ERROR, "Spurious GIC interrupt: 0x%x\n", GicInterrupt));\r
+    GicV3EndOfInterrupt (&gHardwareInterruptV3Protocol, GicInterrupt);\r
   }\r
-\r
-  GicV3EndOfInterrupt (&gHardwareInterruptV3Protocol, GicInterrupt);\r
 }\r
 \r
-//\r
 // The protocol instance produced by this driver\r
-//\r
 EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptV3Protocol = {\r
   RegisterInterruptSource,\r
   GicV3EnableInterruptSource,\r
@@ -185,6 +193,140 @@ EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptV3Protocol = {
   GicV3EndOfInterrupt\r
 };\r
 \r
+/**\r
+  Get interrupt trigger type of an interrupt\r
+\r
+  @param This          Instance pointer for this protocol\r
+  @param Source        Hardware source of the interrupt.\r
+  @param TriggerType   Returns interrupt trigger type.\r
+\r
+  @retval EFI_SUCCESS       Source interrupt supported.\r
+  @retval EFI_UNSUPPORTED   Source interrupt is not supported.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+GicV3GetTriggerType (\r
+  IN  EFI_HARDWARE_INTERRUPT2_PROTOCOL      *This,\r
+  IN  HARDWARE_INTERRUPT_SOURCE             Source,\r
+  OUT EFI_HARDWARE_INTERRUPT2_TRIGGER_TYPE  *TriggerType\r
+  )\r
+{\r
+  UINTN                   RegAddress;\r
+  UINTN                   Config1Bit;\r
+  EFI_STATUS              Status;\r
+\r
+  Status = GicGetDistributorIcfgBaseAndBit (\r
+             Source,\r
+             &RegAddress,\r
+             &Config1Bit\r
+             );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  if ((MmioRead32 (RegAddress) & (1 << Config1Bit)) == 0) {\r
+     *TriggerType = EFI_HARDWARE_INTERRUPT2_TRIGGER_LEVEL_HIGH;\r
+  } else {\r
+     *TriggerType = EFI_HARDWARE_INTERRUPT2_TRIGGER_EDGE_RISING;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Set interrupt trigger type of an interrupt\r
+\r
+  @param This          Instance pointer for this protocol\r
+  @param Source        Hardware source of the interrupt.\r
+  @param TriggerType   Interrupt trigger type.\r
+\r
+  @retval EFI_SUCCESS       Source interrupt supported.\r
+  @retval EFI_UNSUPPORTED   Source interrupt is not supported.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+GicV3SetTriggerType (\r
+  IN  EFI_HARDWARE_INTERRUPT2_PROTOCOL      *This,\r
+  IN  HARDWARE_INTERRUPT_SOURCE             Source,\r
+  IN  EFI_HARDWARE_INTERRUPT2_TRIGGER_TYPE  TriggerType\r
+  )\r
+{\r
+  UINTN                   RegAddress;\r
+  UINTN                   Config1Bit;\r
+  UINT32                  Value;\r
+  EFI_STATUS              Status;\r
+  BOOLEAN                 SourceEnabled;\r
+\r
+  if (   (TriggerType != EFI_HARDWARE_INTERRUPT2_TRIGGER_EDGE_RISING)\r
+      && (TriggerType != EFI_HARDWARE_INTERRUPT2_TRIGGER_LEVEL_HIGH)) {\r
+          DEBUG ((DEBUG_ERROR, "Invalid interrupt trigger type: %d\n", \\r
+                 TriggerType));\r
+          ASSERT (FALSE);\r
+          return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  Status = GicGetDistributorIcfgBaseAndBit (\r
+             Source,\r
+             &RegAddress,\r
+             &Config1Bit\r
+             );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Status = GicV3GetInterruptSourceState (\r
+             (EFI_HARDWARE_INTERRUPT_PROTOCOL*)This,\r
+             Source,\r
+             &SourceEnabled\r
+             );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Value = (TriggerType == EFI_HARDWARE_INTERRUPT2_TRIGGER_EDGE_RISING)\r
+          ?  ARM_GIC_ICDICFR_EDGE_TRIGGERED\r
+          :  ARM_GIC_ICDICFR_LEVEL_TRIGGERED;\r
+\r
+  // Before changing the value, we must disable the interrupt,\r
+  // otherwise GIC behavior is UNPREDICTABLE.\r
+  if (SourceEnabled) {\r
+    GicV3DisableInterruptSource (\r
+      (EFI_HARDWARE_INTERRUPT_PROTOCOL*)This,\r
+      Source\r
+      );\r
+  }\r
+\r
+  MmioAndThenOr32 (\r
+    RegAddress,\r
+    ~(0x1 << Config1Bit),\r
+    Value << Config1Bit\r
+    );\r
+  // Restore interrupt state\r
+  if (SourceEnabled) {\r
+    GicV3EnableInterruptSource (\r
+      (EFI_HARDWARE_INTERRUPT_PROTOCOL*)This,\r
+      Source\r
+      );\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+EFI_HARDWARE_INTERRUPT2_PROTOCOL gHardwareInterrupt2V3Protocol = {\r
+  (HARDWARE_INTERRUPT2_REGISTER)RegisterInterruptSource,\r
+  (HARDWARE_INTERRUPT2_ENABLE)GicV3EnableInterruptSource,\r
+  (HARDWARE_INTERRUPT2_DISABLE)GicV3DisableInterruptSource,\r
+  (HARDWARE_INTERRUPT2_INTERRUPT_STATE)GicV3GetInterruptSourceState,\r
+  (HARDWARE_INTERRUPT2_END_OF_INTERRUPT)GicV3EndOfInterrupt,\r
+  GicV3GetTriggerType,\r
+  GicV3SetTriggerType\r
+};\r
+\r
 /**\r
   Shutdown our hardware\r
 \r
@@ -243,17 +385,16 @@ GicV3DxeInitialize (
   UINT64                  CpuTarget;\r
   UINT64                  MpId;\r
 \r
-  // Make sure the Interrupt Controller Protocol is not already installed in the system.\r
+  // Make sure the Interrupt Controller Protocol is not already installed in\r
+  // the system.\r
   ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gHardwareInterruptProtocolGuid);\r
 \r
-  mGicDistributorBase    = PcdGet32 (PcdGicDistributorBase);\r
-  mGicRedistributorsBase = PcdGet32 (PcdGicRedistributorsBase);\r
+  mGicDistributorBase    = PcdGet64 (PcdGicDistributorBase);\r
+  mGicRedistributorsBase = PcdGet64 (PcdGicRedistributorsBase);\r
   mGicNumInterrupts      = ArmGicGetMaxNumInterrupts (mGicDistributorBase);\r
 \r
-  //\r
   // We will be driving this GIC in native v3 mode, i.e., with Affinity\r
   // Routing enabled. So ensure that the ARE bit is set.\r
-  //\r
   if (!FeaturePcdGet (PcdArmGicV3WithV2Legacy)) {\r
     MmioOr32 (mGicDistributorBase + ARM_GIC_ICDDCR, ARM_GIC_ICDDCR_ARE);\r
   }\r
@@ -271,35 +412,65 @@ GicV3DxeInitialize (
       );\r
   }\r
 \r
-  //\r
   // Targets the interrupts to the Primary Cpu\r
-  //\r
 \r
   if (FeaturePcdGet (PcdArmGicV3WithV2Legacy)) {\r
-    // Only Primary CPU will run this code. We can identify our GIC CPU ID by reading\r
-    // the GIC Distributor Target register. The 8 first GICD_ITARGETSRn are banked to each\r
-    // connected CPU. These 8 registers hold the CPU targets fields for interrupts 0-31.\r
-    // More Info in the GIC Specification about "Interrupt Processor Targets Registers"\r
-    //\r
-    // Read the first Interrupt Processor Targets Register (that corresponds to the 4\r
-    // first SGIs)\r
+    // Only Primary CPU will run this code. We can identify our GIC CPU ID by\r
+    // reading the GIC Distributor Target register. The 8 first\r
+    // GICD_ITARGETSRn are banked to each connected CPU. These 8 registers\r
+    // hold the CPU targets fields for interrupts 0-31. More Info in the GIC\r
+    // Specification about "Interrupt Processor Targets Registers"\r
+\r
+    // Read the first Interrupt Processor Targets Register (that corresponds\r
+    // to the 4 first SGIs)\r
     CpuTarget = MmioRead32 (mGicDistributorBase + ARM_GIC_ICDIPTR);\r
 \r
-    // The CPU target is a bit field mapping each CPU to a GIC CPU Interface. This value\r
-    // is 0 when we run on a uniprocessor platform.\r
+    // The CPU target is a bit field mapping each CPU to a GIC CPU Interface.\r
+    // This value is 0 when we run on a uniprocessor platform.\r
     if (CpuTarget != 0) {\r
       // The 8 first Interrupt Processor Targets Registers are read-only\r
       for (Index = 8; Index < (mGicNumInterrupts / 4); Index++) {\r
-        MmioWrite32 (mGicDistributorBase + ARM_GIC_ICDIPTR + (Index * 4), CpuTarget);\r
+        MmioWrite32 (\r
+          mGicDistributorBase + ARM_GIC_ICDIPTR + (Index * 4),\r
+          CpuTarget\r
+          );\r
       }\r
     }\r
   } else {\r
     MpId = ArmReadMpidr ();\r
-    CpuTarget = MpId & (ARM_CORE_AFF0 | ARM_CORE_AFF1 | ARM_CORE_AFF2 | ARM_CORE_AFF3);\r
+    CpuTarget = MpId &\r
+      (ARM_CORE_AFF0 | ARM_CORE_AFF1 | ARM_CORE_AFF2 | ARM_CORE_AFF3);\r
+\r
+    if ((MmioRead32 (\r
+           mGicDistributorBase + ARM_GIC_ICDDCR\r
+         ) & ARM_GIC_ICDDCR_DS) != 0) {\r
+\r
+      // If the Disable Security (DS) control bit is set, we are dealing with a\r
+      // GIC that has only one security state. In this case, let's assume we are\r
+      // executing in non-secure state (which is appropriate for DXE modules)\r
+      // and that no other firmware has performed any configuration on the GIC.\r
+      // This means we need to reconfigure all interrupts to non-secure Group 1\r
+      // first.\r
+\r
+      MmioWrite32 (\r
+        mGicRedistributorsBase + ARM_GICR_CTLR_FRAME_SIZE + ARM_GIC_ICDISR,\r
+        0xffffffff\r
+        );\r
+\r
+      for (Index = 32; Index < mGicNumInterrupts; Index += 32) {\r
+        MmioWrite32 (\r
+          mGicDistributorBase + ARM_GIC_ICDISR + Index / 8,\r
+          0xffffffff\r
+          );\r
+      }\r
+    }\r
 \r
     // Route the SPIs to the primary CPU. SPIs start at the INTID 32\r
     for (Index = 0; Index < (mGicNumInterrupts - 32); Index++) {\r
-      MmioWrite32 (mGicDistributorBase + ARM_GICD_IROUTER + (Index * 8), CpuTarget | ARM_GICD_IROUTER_IRM);\r
+      MmioWrite64 (\r
+        mGicDistributorBase + ARM_GICD_IROUTER + (Index * 8),\r
+        CpuTarget\r
+        );\r
     }\r
   }\r
 \r
@@ -316,7 +487,11 @@ GicV3DxeInitialize (
   ArmGicEnableDistributor (mGicDistributorBase);\r
 \r
   Status = InstallAndRegisterInterruptService (\r
-          &gHardwareInterruptV3Protocol, GicV3IrqInterruptHandler, GicV3ExitBootServicesEvent);\r
+             &gHardwareInterruptV3Protocol,\r
+             &gHardwareInterrupt2V3Protocol,\r
+             GicV3IrqInterruptHandler,\r
+             GicV3ExitBootServicesEvent\r
+             );\r
 \r
   return Status;\r
 }\r