1 /*++
2 
3 Copyright (c) 2013-2017, ARM Ltd. All rights reserved.<BR>
4 
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6 
7 --*/
8 
9 #include "ArmGicDxe.h"
10 
11 VOID
12 EFIAPI
13 IrqInterruptHandler (
14   IN EFI_EXCEPTION_TYPE           InterruptType,
15   IN EFI_SYSTEM_CONTEXT           SystemContext
16   );
17 
18 VOID
19 EFIAPI
20 ExitBootServicesEvent (
21   IN EFI_EVENT  Event,
22   IN VOID       *Context
23   );
24 
25 // Making this global saves a few bytes in image size
26 EFI_HANDLE  gHardwareInterruptHandle = NULL;
27 
28 // Notifications
29 EFI_EVENT EfiExitBootServicesEvent      = (EFI_EVENT)NULL;
30 
31 // Maximum Number of Interrupts
32 UINTN mGicNumInterrupts                 = 0;
33 
34 HARDWARE_INTERRUPT_HANDLER  *gRegisteredInterruptHandlers = NULL;
35 
36 
37 /**
38   Calculate GICD_ICFGRn base address and corresponding bit
39   field Int_config[1] of the GIC distributor register.
40 
41   @param Source       Hardware source of the interrupt.
42   @param RegAddress   Corresponding GICD_ICFGRn base address.
43   @param Config1Bit   Bit number of F Int_config[1] bit in the register.
44 
45   @retval EFI_SUCCESS       Source interrupt supported.
46   @retval EFI_UNSUPPORTED   Source interrupt is not supported.
47 **/
48 EFI_STATUS
GicGetDistributorIcfgBaseAndBit(IN HARDWARE_INTERRUPT_SOURCE Source,OUT UINTN * RegAddress,OUT UINTN * Config1Bit)49 GicGetDistributorIcfgBaseAndBit (
50   IN HARDWARE_INTERRUPT_SOURCE             Source,
51   OUT UINTN                               *RegAddress,
52   OUT UINTN                               *Config1Bit
53   )
54 {
55   UINTN                  RegIndex;
56   UINTN                  Field;
57 
58   if (Source >= mGicNumInterrupts) {
59     ASSERT(Source < mGicNumInterrupts);
60     return EFI_UNSUPPORTED;
61   }
62 
63   RegIndex = Source / ARM_GIC_ICDICFR_F_STRIDE;  // NOTE: truncation is significant
64   Field = Source % ARM_GIC_ICDICFR_F_STRIDE;
65   *RegAddress = PcdGet64 (PcdGicDistributorBase)
66                 + ARM_GIC_ICDICFR
67                 + (ARM_GIC_ICDICFR_BYTES * RegIndex);
68   *Config1Bit = ((Field * ARM_GIC_ICDICFR_F_WIDTH)
69                  + ARM_GIC_ICDICFR_F_CONFIG1_BIT);
70 
71   return EFI_SUCCESS;
72 }
73 
74 
75 
76 /**
77   Register Handler for the specified interrupt source.
78 
79   @param This     Instance pointer for this protocol
80   @param Source   Hardware source of the interrupt
81   @param Handler  Callback for interrupt. NULL to unregister
82 
83   @retval EFI_SUCCESS Source was updated to support Handler.
84   @retval EFI_DEVICE_ERROR  Hardware could not be programmed.
85 
86 **/
87 EFI_STATUS
88 EFIAPI
RegisterInterruptSource(IN EFI_HARDWARE_INTERRUPT_PROTOCOL * This,IN HARDWARE_INTERRUPT_SOURCE Source,IN HARDWARE_INTERRUPT_HANDLER Handler)89 RegisterInterruptSource (
90   IN EFI_HARDWARE_INTERRUPT_PROTOCOL    *This,
91   IN HARDWARE_INTERRUPT_SOURCE          Source,
92   IN HARDWARE_INTERRUPT_HANDLER         Handler
93   )
94 {
95   if (Source >= mGicNumInterrupts) {
96     ASSERT(FALSE);
97     return EFI_UNSUPPORTED;
98   }
99 
100   if ((Handler == NULL) && (gRegisteredInterruptHandlers[Source] == NULL)) {
101     return EFI_INVALID_PARAMETER;
102   }
103 
104   if ((Handler != NULL) && (gRegisteredInterruptHandlers[Source] != NULL)) {
105     return EFI_ALREADY_STARTED;
106   }
107 
108   gRegisteredInterruptHandlers[Source] = Handler;
109 
110   // If the interrupt handler is unregistered then disable the interrupt
111   if (NULL == Handler){
112     return This->DisableInterruptSource (This, Source);
113   } else {
114     return This->EnableInterruptSource (This, Source);
115   }
116 }
117 
118 STATIC VOID *mCpuArchProtocolNotifyEventRegistration;
119 
120 STATIC
121 VOID
122 EFIAPI
CpuArchEventProtocolNotify(IN EFI_EVENT Event,IN VOID * Context)123 CpuArchEventProtocolNotify (
124   IN  EFI_EVENT       Event,
125   IN  VOID            *Context
126   )
127 {
128   EFI_CPU_ARCH_PROTOCOL   *Cpu;
129   EFI_STATUS              Status;
130 
131   // Get the CPU protocol that this driver requires.
132   Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **)&Cpu);
133   if (EFI_ERROR (Status)) {
134     return;
135   }
136 
137   // Unregister the default exception handler.
138   Status = Cpu->RegisterInterruptHandler (Cpu, ARM_ARCH_EXCEPTION_IRQ, NULL);
139   if (EFI_ERROR (Status)) {
140     DEBUG ((DEBUG_ERROR, "%a: Cpu->RegisterInterruptHandler() - %r\n",
141       __FUNCTION__, Status));
142     return;
143   }
144 
145   // Register to receive interrupts
146   Status = Cpu->RegisterInterruptHandler (Cpu, ARM_ARCH_EXCEPTION_IRQ,
147                   Context);
148   if (EFI_ERROR (Status)) {
149     DEBUG ((DEBUG_ERROR, "%a: Cpu->RegisterInterruptHandler() - %r\n",
150       __FUNCTION__, Status));
151   }
152 
153   gBS->CloseEvent (Event);
154 }
155 
156 EFI_STATUS
InstallAndRegisterInterruptService(IN EFI_HARDWARE_INTERRUPT_PROTOCOL * InterruptProtocol,IN EFI_HARDWARE_INTERRUPT2_PROTOCOL * Interrupt2Protocol,IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler,IN EFI_EVENT_NOTIFY ExitBootServicesEvent)157 InstallAndRegisterInterruptService (
158   IN EFI_HARDWARE_INTERRUPT_PROTOCOL   *InterruptProtocol,
159   IN EFI_HARDWARE_INTERRUPT2_PROTOCOL  *Interrupt2Protocol,
160   IN EFI_CPU_INTERRUPT_HANDLER          InterruptHandler,
161   IN EFI_EVENT_NOTIFY                   ExitBootServicesEvent
162   )
163 {
164   EFI_STATUS               Status;
165   CONST UINTN              RihArraySize =
166     (sizeof(HARDWARE_INTERRUPT_HANDLER) * mGicNumInterrupts);
167 
168   // Initialize the array for the Interrupt Handlers
169   gRegisteredInterruptHandlers = AllocateZeroPool (RihArraySize);
170   if (gRegisteredInterruptHandlers == NULL) {
171     return EFI_OUT_OF_RESOURCES;
172   }
173 
174   Status = gBS->InstallMultipleProtocolInterfaces (
175                   &gHardwareInterruptHandle,
176                   &gHardwareInterruptProtocolGuid,
177                   InterruptProtocol,
178                   &gHardwareInterrupt2ProtocolGuid,
179                   Interrupt2Protocol,
180                   NULL
181                   );
182   if (EFI_ERROR (Status)) {
183     return Status;
184   }
185 
186   //
187   // Install the interrupt handler as soon as the CPU arch protocol appears.
188   //
189   EfiCreateProtocolNotifyEvent (
190     &gEfiCpuArchProtocolGuid,
191     TPL_CALLBACK,
192     CpuArchEventProtocolNotify,
193     InterruptHandler,
194     &mCpuArchProtocolNotifyEventRegistration);
195 
196   // Register for an ExitBootServicesEvent
197   Status = gBS->CreateEvent (
198                   EVT_SIGNAL_EXIT_BOOT_SERVICES,
199                   TPL_NOTIFY,
200                   ExitBootServicesEvent,
201                   NULL,
202                   &EfiExitBootServicesEvent
203                   );
204 
205   return Status;
206 }
207