1 /** @file
2 
3   Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>
4   Copyright (c) 2014, ARM Limited. All rights reserved.<BR>
5 
6   This program and the accompanying materials
7   are licensed and made available under the terms and conditions of the BSD License
8   which accompanies this distribution.  The full text of the license may be found at
9   http://opensource.org/licenses/bsd-license.php
10 
11   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13 
14 **/
15 
16 #include "CpuDxe.h"
17 
18 //FIXME: Will not compile on non-ARMv7 builds
19 #include <Chipset/ArmV7.h>
20 
21 VOID
22 ExceptionHandlersStart (
23   VOID
24   );
25 
26 VOID
27 ExceptionHandlersEnd (
28   VOID
29   );
30 
31 VOID
32 CommonExceptionEntry (
33   VOID
34   );
35 
36 VOID
37 AsmCommonExceptionEntry (
38   VOID
39   );
40 
41 
42 EFI_EXCEPTION_CALLBACK  gExceptionHandlers[MAX_ARM_EXCEPTION + 1];
43 EFI_EXCEPTION_CALLBACK  gDebuggerExceptionHandlers[MAX_ARM_EXCEPTION + 1];
44 
45 
46 
47 /**
48   This function registers and enables the handler specified by InterruptHandler for a processor
49   interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the
50   handler for the processor interrupt or exception type specified by InterruptType is uninstalled.
51   The installed handler is called once for each processor interrupt or exception.
52 
53   @param  InterruptType    A pointer to the processor's current interrupt state. Set to TRUE if interrupts
54                            are enabled and FALSE if interrupts are disabled.
55   @param  InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
56                            when a processor interrupt occurs. If this parameter is NULL, then the handler
57                            will be uninstalled.
58 
59   @retval EFI_SUCCESS           The handler for the processor interrupt was successfully installed or uninstalled.
60   @retval EFI_ALREADY_STARTED   InterruptHandler is not NULL, and a handler for InterruptType was
61                                 previously installed.
62   @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not
63                                 previously installed.
64   @retval EFI_UNSUPPORTED       The interrupt specified by InterruptType is not supported.
65 
66 **/
67 EFI_STATUS
RegisterInterruptHandler(IN EFI_EXCEPTION_TYPE InterruptType,IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler)68 RegisterInterruptHandler (
69   IN EFI_EXCEPTION_TYPE             InterruptType,
70   IN EFI_CPU_INTERRUPT_HANDLER      InterruptHandler
71   )
72 {
73   if (InterruptType > MAX_ARM_EXCEPTION) {
74     return EFI_UNSUPPORTED;
75   }
76 
77   if ((InterruptHandler != NULL) && (gExceptionHandlers[InterruptType] != NULL)) {
78     return EFI_ALREADY_STARTED;
79   }
80 
81   gExceptionHandlers[InterruptType] = InterruptHandler;
82 
83   return EFI_SUCCESS;
84 }
85 
86 
87 
88 
89 VOID
90 EFIAPI
CommonCExceptionHandler(IN EFI_EXCEPTION_TYPE ExceptionType,IN OUT EFI_SYSTEM_CONTEXT SystemContext)91 CommonCExceptionHandler (
92   IN     EFI_EXCEPTION_TYPE           ExceptionType,
93   IN OUT EFI_SYSTEM_CONTEXT           SystemContext
94   )
95 {
96   if (ExceptionType <= MAX_ARM_EXCEPTION) {
97     if (gExceptionHandlers[ExceptionType]) {
98       gExceptionHandlers[ExceptionType] (ExceptionType, SystemContext);
99       return;
100     }
101   } else {
102     DEBUG ((EFI_D_ERROR, "Unknown exception type %d from %08x\n", ExceptionType, SystemContext.SystemContextArm->PC));
103     ASSERT (FALSE);
104   }
105 
106   if (ExceptionType == EXCEPT_ARM_SOFTWARE_INTERRUPT) {
107     //
108     // ARM JTAG debuggers some times use this vector, so it is not an error to get one
109     //
110     return;
111   }
112 
113   DefaultExceptionHandler (ExceptionType, SystemContext);
114 }
115 
116 
117 
118 EFI_STATUS
InitializeExceptions(IN EFI_CPU_ARCH_PROTOCOL * Cpu)119 InitializeExceptions (
120   IN EFI_CPU_ARCH_PROTOCOL    *Cpu
121   )
122 {
123   EFI_STATUS           Status;
124   UINTN                Offset;
125   UINTN                Length;
126   UINTN                Index;
127   BOOLEAN              IrqEnabled;
128   BOOLEAN              FiqEnabled;
129   EFI_PHYSICAL_ADDRESS Base;
130   UINT32               *VectorBase;
131 
132   Status = EFI_SUCCESS;
133   ZeroMem (gExceptionHandlers,sizeof(*gExceptionHandlers));
134 
135   //
136   // Disable interrupts
137   //
138   Cpu->GetInterruptState (Cpu, &IrqEnabled);
139   Cpu->DisableInterrupt (Cpu);
140 
141   //
142   // EFI does not use the FIQ, but a debugger might so we must disable
143   // as we take over the exception vectors.
144   //
145   FiqEnabled = ArmGetFiqState ();
146   ArmDisableFiq ();
147 
148   if (FeaturePcdGet(PcdRelocateVectorTable) == TRUE) {
149     //
150     // Copy an implementation of the ARM exception vectors to PcdCpuVectorBaseAddress.
151     //
152     Length = (UINTN)ExceptionHandlersEnd - (UINTN)ExceptionHandlersStart;
153 
154     // Check if the exception vector is in the low address
155     if (PcdGet32 (PcdCpuVectorBaseAddress) == 0x0) {
156       // Set SCTLR.V to 0 to enable VBAR to be used
157       ArmSetLowVectors ();
158     } else {
159       ArmSetHighVectors ();
160     }
161 
162     //
163     // Reserve space for the exception handlers
164     //
165     Base = (EFI_PHYSICAL_ADDRESS)PcdGet32 (PcdCpuVectorBaseAddress);
166     VectorBase = (UINT32 *)(UINTN)Base;
167     Status = gBS->AllocatePages (AllocateAddress, EfiBootServicesCode, EFI_SIZE_TO_PAGES (Length), &Base);
168     // If the request was for memory that's not in the memory map (which is often the case for 0x00000000
169     // on embedded systems, for example, we don't want to hang up.  So we'll check here for a status of
170     // EFI_NOT_FOUND, and continue in that case.
171     if (EFI_ERROR(Status) && (Status != EFI_NOT_FOUND)) {
172       ASSERT_EFI_ERROR (Status);
173     }
174 
175     if (FeaturePcdGet(PcdDebuggerExceptionSupport) == TRUE) {
176       // Save existing vector table, in case debugger is already hooked in
177       CopyMem ((VOID *)gDebuggerExceptionHandlers, (VOID *)VectorBase, sizeof (gDebuggerExceptionHandlers));
178     }
179 
180     // Copy our assembly code into the page that contains the exception vectors.
181     CopyMem ((VOID *)VectorBase, (VOID *)ExceptionHandlersStart, Length);
182 
183     //
184     // Patch in the common Assembly exception handler
185     //
186     Offset = (UINTN)CommonExceptionEntry - (UINTN)ExceptionHandlersStart;
187     *(UINTN *) ((UINT8 *)(UINTN)PcdGet32 (PcdCpuVectorBaseAddress) + Offset) = (UINTN)AsmCommonExceptionEntry;
188 
189     //
190     // Initialize the C entry points for interrupts
191     //
192     for (Index = 0; Index <= MAX_ARM_EXCEPTION; Index++) {
193       if (!FeaturePcdGet(PcdDebuggerExceptionSupport) ||
194           (gDebuggerExceptionHandlers[Index] == 0) || (gDebuggerExceptionHandlers[Index] == (VOID *)(UINTN)0xEAFFFFFE)) {
195         // Exception handler contains branch to vector location (jmp $) so no handler
196         // NOTE: This code assumes vectors are ARM and not Thumb code
197         Status = RegisterInterruptHandler (Index, NULL);
198         ASSERT_EFI_ERROR (Status);
199       } else {
200         // If the debugger has already hooked put its vector back
201         VectorBase[Index] = (UINT32)(UINTN)gDebuggerExceptionHandlers[Index];
202       }
203     }
204 
205     // Flush Caches since we updated executable stuff
206     InvalidateInstructionCacheRange ((VOID *)PcdGet32(PcdCpuVectorBaseAddress), Length);
207 
208     //Note: On ARM processor with the Security Extension, the Vector Table can be located anywhere in the memory.
209     //      The Vector Base Address Register defines the location
210     ArmWriteVBar (PcdGet32(PcdCpuVectorBaseAddress));
211   } else {
212     // The Vector table must be 32-byte aligned
213     if (((UINT32)ExceptionHandlersStart & ARM_VECTOR_TABLE_ALIGNMENT) != 0) {
214       ASSERT (0);
215       return EFI_INVALID_PARAMETER;
216     }
217 
218     // We do not copy the Exception Table at PcdGet32(PcdCpuVectorBaseAddress). We just set Vector Base Address to point into CpuDxe code.
219     ArmWriteVBar ((UINT32)ExceptionHandlersStart);
220   }
221 
222   if (FiqEnabled) {
223     ArmEnableFiq ();
224   }
225 
226   if (IrqEnabled) {
227     //
228     // Restore interrupt state
229     //
230     Status = Cpu->EnableInterrupt (Cpu);
231   }
232 
233   return Status;
234 }
235