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