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
GicV3EnableInterruptSource(IN EFI_HARDWARE_INTERRUPT_PROTOCOL * This,IN HARDWARE_INTERRUPT_SOURCE Source)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
GicV3DisableInterruptSource(IN EFI_HARDWARE_INTERRUPT_PROTOCOL * This,IN HARDWARE_INTERRUPT_SOURCE Source)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
GicV3GetInterruptSourceState(IN EFI_HARDWARE_INTERRUPT_PROTOCOL * This,IN HARDWARE_INTERRUPT_SOURCE Source,IN BOOLEAN * InterruptState)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
GicV3EndOfInterrupt(IN EFI_HARDWARE_INTERRUPT_PROTOCOL * This,IN HARDWARE_INTERRUPT_SOURCE Source)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
GicV3IrqInterruptHandler(IN EFI_EXCEPTION_TYPE InterruptType,IN EFI_SYSTEM_CONTEXT SystemContext)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
GicV3ExitBootServicesEvent(IN EFI_EVENT Event,IN VOID * Context)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
GicV3DxeInitialize(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)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