Home | History | Annotate | Download | only in GicV3
      1 /** @file
      2 *
      3 *  Copyright (c) 2011-2016, ARM Limited. All rights reserved.
      4 *
      5 *  This program and the accompanying materials
      6 *  are licensed and made available under the terms and conditions of the BSD License
      7 *  which accompanies this distribution.  The full text of the license may be found at
      8 *  http://opensource.org/licenses/bsd-license.php
      9 *
     10 *  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     11 *  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     12 *
     13 **/
     14 
     15 #include <Library/ArmGicLib.h>
     16 
     17 #include "ArmGicDxe.h"
     18 
     19 #define ARM_GIC_DEFAULT_PRIORITY  0x80
     20 
     21 extern EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptV3Protocol;
     22 
     23 STATIC UINTN mGicDistributorBase;
     24 STATIC UINTN mGicRedistributorsBase;
     25 
     26 /**
     27   Enable interrupt source Source.
     28 
     29   @param This     Instance pointer for this protocol
     30   @param Source   Hardware source of the interrupt
     31 
     32   @retval EFI_SUCCESS       Source interrupt enabled.
     33   @retval EFI_DEVICE_ERROR  Hardware could not be programmed.
     34 
     35 **/
     36 EFI_STATUS
     37 EFIAPI
     38 GicV3EnableInterruptSource (
     39   IN EFI_HARDWARE_INTERRUPT_PROTOCOL    *This,
     40   IN HARDWARE_INTERRUPT_SOURCE          Source
     41   )
     42 {
     43   if (Source >= mGicNumInterrupts) {
     44     ASSERT(FALSE);
     45     return EFI_UNSUPPORTED;
     46   }
     47 
     48   ArmGicEnableInterrupt (mGicDistributorBase, mGicRedistributorsBase, Source);
     49 
     50   return EFI_SUCCESS;
     51 }
     52 
     53 /**
     54   Disable interrupt source Source.
     55 
     56   @param This     Instance pointer for this protocol
     57   @param Source   Hardware source of the interrupt
     58 
     59   @retval EFI_SUCCESS       Source interrupt disabled.
     60   @retval EFI_DEVICE_ERROR  Hardware could not be programmed.
     61 
     62 **/
     63 EFI_STATUS
     64 EFIAPI
     65 GicV3DisableInterruptSource (
     66   IN EFI_HARDWARE_INTERRUPT_PROTOCOL    *This,
     67   IN HARDWARE_INTERRUPT_SOURCE          Source
     68   )
     69 {
     70   if (Source >= mGicNumInterrupts) {
     71     ASSERT(FALSE);
     72     return EFI_UNSUPPORTED;
     73   }
     74 
     75   ArmGicDisableInterrupt (mGicDistributorBase, mGicRedistributorsBase, Source);
     76 
     77   return EFI_SUCCESS;
     78 }
     79 
     80 /**
     81   Return current state of interrupt source Source.
     82 
     83   @param This     Instance pointer for this protocol
     84   @param Source   Hardware source of the interrupt
     85   @param InterruptState  TRUE: source enabled, FALSE: source disabled.
     86 
     87   @retval EFI_SUCCESS       InterruptState is valid
     88   @retval EFI_DEVICE_ERROR  InterruptState is not valid
     89 
     90 **/
     91 EFI_STATUS
     92 EFIAPI
     93 GicV3GetInterruptSourceState (
     94   IN EFI_HARDWARE_INTERRUPT_PROTOCOL    *This,
     95   IN HARDWARE_INTERRUPT_SOURCE          Source,
     96   IN BOOLEAN                            *InterruptState
     97   )
     98 {
     99   if (Source >= mGicNumInterrupts) {
    100     ASSERT(FALSE);
    101     return EFI_UNSUPPORTED;
    102   }
    103 
    104   *InterruptState = ArmGicIsInterruptEnabled (mGicDistributorBase, mGicRedistributorsBase, Source);
    105 
    106   return EFI_SUCCESS;
    107 }
    108 
    109 /**
    110   Signal to the hardware that the End Of Interrupt state
    111   has been reached.
    112 
    113   @param This     Instance pointer for this protocol
    114   @param Source   Hardware source of the interrupt
    115 
    116   @retval EFI_SUCCESS       Source interrupt EOI'ed.
    117   @retval EFI_DEVICE_ERROR  Hardware could not be programmed.
    118 
    119 **/
    120 EFI_STATUS
    121 EFIAPI
    122 GicV3EndOfInterrupt (
    123   IN EFI_HARDWARE_INTERRUPT_PROTOCOL    *This,
    124   IN HARDWARE_INTERRUPT_SOURCE          Source
    125   )
    126 {
    127   if (Source >= mGicNumInterrupts) {
    128     ASSERT(FALSE);
    129     return EFI_UNSUPPORTED;
    130   }
    131 
    132   ArmGicV3EndOfInterrupt (Source);
    133   return EFI_SUCCESS;
    134 }
    135 
    136 /**
    137   EFI_CPU_INTERRUPT_HANDLER that is called when a processor interrupt occurs.
    138 
    139   @param  InterruptType    Defines the type of interrupt or exception that
    140                            occurred on the processor.This parameter is processor architecture specific.
    141   @param  SystemContext    A pointer to the processor context when
    142                            the interrupt occurred on the processor.
    143 
    144   @return None
    145 
    146 **/
    147 VOID
    148 EFIAPI
    149 GicV3IrqInterruptHandler (
    150   IN EFI_EXCEPTION_TYPE           InterruptType,
    151   IN EFI_SYSTEM_CONTEXT           SystemContext
    152   )
    153 {
    154   UINT32                      GicInterrupt;
    155   HARDWARE_INTERRUPT_HANDLER  InterruptHandler;
    156 
    157   GicInterrupt = ArmGicV3AcknowledgeInterrupt ();
    158 
    159   // Special Interrupts (ID1020-ID1023) have an Interrupt ID greater than the
    160   // number of interrupt (ie: Spurious interrupt).
    161   if ((GicInterrupt & ARM_GIC_ICCIAR_ACKINTID) >= mGicNumInterrupts) {
    162     // The special interrupt do not need to be acknowledge
    163     return;
    164   }
    165 
    166   InterruptHandler = gRegisteredInterruptHandlers[GicInterrupt];
    167   if (InterruptHandler != NULL) {
    168     // Call the registered interrupt handler.
    169     InterruptHandler (GicInterrupt, SystemContext);
    170   } else {
    171     DEBUG ((EFI_D_ERROR, "Spurious GIC interrupt: 0x%x\n", GicInterrupt));
    172     GicV3EndOfInterrupt (&gHardwareInterruptV3Protocol, GicInterrupt);
    173   }
    174 }
    175 
    176 //
    177 // The protocol instance produced by this driver
    178 //
    179 EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptV3Protocol = {
    180   RegisterInterruptSource,
    181   GicV3EnableInterruptSource,
    182   GicV3DisableInterruptSource,
    183   GicV3GetInterruptSourceState,
    184   GicV3EndOfInterrupt
    185 };
    186 
    187 /**
    188   Shutdown our hardware
    189 
    190   DXE Core will disable interrupts and turn off the timer and disable interrupts
    191   after all the event handlers have run.
    192 
    193   @param[in]  Event   The Event that is being processed
    194   @param[in]  Context Event Context
    195 **/
    196 VOID
    197 EFIAPI
    198 GicV3ExitBootServicesEvent (
    199   IN EFI_EVENT  Event,
    200   IN VOID       *Context
    201   )
    202 {
    203   UINTN    Index;
    204 
    205   // Acknowledge all pending interrupts
    206   for (Index = 0; Index < mGicNumInterrupts; Index++) {
    207     GicV3DisableInterruptSource (&gHardwareInterruptV3Protocol, Index);
    208   }
    209 
    210   for (Index = 0; Index < mGicNumInterrupts; Index++) {
    211     GicV3EndOfInterrupt (&gHardwareInterruptV3Protocol, Index);
    212   }
    213 
    214   // Disable Gic Interface
    215   ArmGicV3DisableInterruptInterface ();
    216 
    217   // Disable Gic Distributor
    218   ArmGicDisableDistributor (mGicDistributorBase);
    219 }
    220 
    221 /**
    222   Initialize the state information for the CPU Architectural Protocol
    223 
    224   @param  ImageHandle   of the loaded driver
    225   @param  SystemTable   Pointer to the System Table
    226 
    227   @retval EFI_SUCCESS           Protocol registered
    228   @retval EFI_OUT_OF_RESOURCES  Cannot allocate protocol data structure
    229   @retval EFI_DEVICE_ERROR      Hardware problems
    230 
    231 **/
    232 EFI_STATUS
    233 GicV3DxeInitialize (
    234   IN EFI_HANDLE         ImageHandle,
    235   IN EFI_SYSTEM_TABLE   *SystemTable
    236   )
    237 {
    238   EFI_STATUS              Status;
    239   UINTN                   Index;
    240   UINT32                  RegOffset;
    241   UINTN                   RegShift;
    242   UINT64                  CpuTarget;
    243   UINT64                  MpId;
    244 
    245   // Make sure the Interrupt Controller Protocol is not already installed in the system.
    246   ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gHardwareInterruptProtocolGuid);
    247 
    248   mGicDistributorBase    = PcdGet64 (PcdGicDistributorBase);
    249   mGicRedistributorsBase = PcdGet64 (PcdGicRedistributorsBase);
    250   mGicNumInterrupts      = ArmGicGetMaxNumInterrupts (mGicDistributorBase);
    251 
    252   //
    253   // We will be driving this GIC in native v3 mode, i.e., with Affinity
    254   // Routing enabled. So ensure that the ARE bit is set.
    255   //
    256   if (!FeaturePcdGet (PcdArmGicV3WithV2Legacy)) {
    257     MmioOr32 (mGicDistributorBase + ARM_GIC_ICDDCR, ARM_GIC_ICDDCR_ARE);
    258   }
    259 
    260   for (Index = 0; Index < mGicNumInterrupts; Index++) {
    261     GicV3DisableInterruptSource (&gHardwareInterruptV3Protocol, Index);
    262 
    263     // Set Priority
    264     RegOffset = Index / 4;
    265     RegShift = (Index % 4) * 8;
    266     MmioAndThenOr32 (
    267       mGicDistributorBase + ARM_GIC_ICDIPR + (4 * RegOffset),
    268       ~(0xff << RegShift),
    269       ARM_GIC_DEFAULT_PRIORITY << RegShift
    270       );
    271   }
    272 
    273   //
    274   // Targets the interrupts to the Primary Cpu
    275   //
    276 
    277   if (FeaturePcdGet (PcdArmGicV3WithV2Legacy)) {
    278     // Only Primary CPU will run this code. We can identify our GIC CPU ID by reading
    279     // the GIC Distributor Target register. The 8 first GICD_ITARGETSRn are banked to each
    280     // connected CPU. These 8 registers hold the CPU targets fields for interrupts 0-31.
    281     // More Info in the GIC Specification about "Interrupt Processor Targets Registers"
    282     //
    283     // Read the first Interrupt Processor Targets Register (that corresponds to the 4
    284     // first SGIs)
    285     CpuTarget = MmioRead32 (mGicDistributorBase + ARM_GIC_ICDIPTR);
    286 
    287     // The CPU target is a bit field mapping each CPU to a GIC CPU Interface. This value
    288     // is 0 when we run on a uniprocessor platform.
    289     if (CpuTarget != 0) {
    290       // The 8 first Interrupt Processor Targets Registers are read-only
    291       for (Index = 8; Index < (mGicNumInterrupts / 4); Index++) {
    292         MmioWrite32 (mGicDistributorBase + ARM_GIC_ICDIPTR + (Index * 4), CpuTarget);
    293       }
    294     }
    295   } else {
    296     MpId = ArmReadMpidr ();
    297     CpuTarget = MpId & (ARM_CORE_AFF0 | ARM_CORE_AFF1 | ARM_CORE_AFF2 | ARM_CORE_AFF3);
    298 
    299     if ((MmioRead32 (mGicDistributorBase + ARM_GIC_ICDDCR) & ARM_GIC_ICDDCR_DS) != 0) {
    300       //
    301       // If the Disable Security (DS) control bit is set, we are dealing with a
    302       // GIC that has only one security state. In this case, let's assume we are
    303       // executing in non-secure state (which is appropriate for DXE modules)
    304       // and that no other firmware has performed any configuration on the GIC.
    305       // This means we need to reconfigure all interrupts to non-secure Group 1
    306       // first.
    307       //
    308       MmioWrite32 (mGicRedistributorsBase + ARM_GICR_CTLR_FRAME_SIZE + ARM_GIC_ICDISR, 0xffffffff);
    309 
    310       for (Index = 32; Index < mGicNumInterrupts; Index += 32) {
    311         MmioWrite32 (mGicDistributorBase + ARM_GIC_ICDISR + Index / 8, 0xffffffff);
    312       }
    313     }
    314 
    315     // Route the SPIs to the primary CPU. SPIs start at the INTID 32
    316     for (Index = 0; Index < (mGicNumInterrupts - 32); Index++) {
    317       MmioWrite32 (mGicDistributorBase + ARM_GICD_IROUTER + (Index * 8), CpuTarget | ARM_GICD_IROUTER_IRM);
    318     }
    319   }
    320 
    321   // Set binary point reg to 0x7 (no preemption)
    322   ArmGicV3SetBinaryPointer (0x7);
    323 
    324   // Set priority mask reg to 0xff to allow all priorities through
    325   ArmGicV3SetPriorityMask (0xff);
    326 
    327   // Enable gic cpu interface
    328   ArmGicV3EnableInterruptInterface ();
    329 
    330   // Enable gic distributor
    331   ArmGicEnableDistributor (mGicDistributorBase);
    332 
    333   Status = InstallAndRegisterInterruptService (
    334           &gHardwareInterruptV3Protocol, GicV3IrqInterruptHandler, GicV3ExitBootServicesEvent);
    335 
    336   return Status;
    337 }
    338