1 /** @file
2 CPU Exception Library provides DXE/SMM CPU common exception handler.
3
4 Copyright (c) 2012 - 2013, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials are licensed and made available under
6 the terms and conditions of the BSD License that accompanies this distribution.
7 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 "CpuExceptionCommon.h"
16 #include <Library/DebugLib.h>
17
18 //
19 // Spinlock for CPU information display
20 //
21 SPIN_LOCK mDisplayMessageSpinLock;
22
23 //
24 // Image align size for DXE/SMM
25 //
26 CONST UINTN mImageAlignSize = SIZE_4KB;
27
28 RESERVED_VECTORS_DATA mReservedVectorsData[CPU_EXCEPTION_NUM];
29 EFI_CPU_INTERRUPT_HANDLER mExternalInterruptHandlerTable[CPU_EXCEPTION_NUM];
30 EFI_CPU_INTERRUPT_HANDLER *mExternalInterruptHandler = NULL;
31 UINTN mEnabledInterruptNum = 0;
32
33 /**
34 Common exception handler.
35
36 @param ExceptionType Exception type.
37 @param SystemContext Pointer to EFI_SYSTEM_CONTEXT.
38 **/
39 VOID
40 EFIAPI
CommonExceptionHandler(IN EFI_EXCEPTION_TYPE ExceptionType,IN EFI_SYSTEM_CONTEXT SystemContext)41 CommonExceptionHandler (
42 IN EFI_EXCEPTION_TYPE ExceptionType,
43 IN EFI_SYSTEM_CONTEXT SystemContext
44 )
45 {
46 EXCEPTION_HANDLER_CONTEXT *ExceptionHandlerContext;
47
48 ExceptionHandlerContext = (EXCEPTION_HANDLER_CONTEXT *) (UINTN) (SystemContext.SystemContextIa32);
49
50 switch (mReservedVectors[ExceptionType].Attribute) {
51 case EFI_VECTOR_HANDOFF_HOOK_BEFORE:
52 //
53 // Need to jmp to old IDT handler after this exception handler
54 //
55 ExceptionHandlerContext->ExceptionDataFlag = (mErrorCodeFlag & (1 << ExceptionType)) ? TRUE : FALSE;
56 ExceptionHandlerContext->OldIdtHandler = mReservedVectors[ExceptionType].ExceptonHandler;
57 break;
58 case EFI_VECTOR_HANDOFF_HOOK_AFTER:
59 while (TRUE) {
60 //
61 // If if anyone has gotten SPIN_LOCK for owner running hook after
62 //
63 if (AcquireSpinLockOrFail (&mReservedVectors[ExceptionType].SpinLock)) {
64 //
65 // Need to execute old IDT handler before running this exception handler
66 //
67 mReservedVectors[ExceptionType].ApicId = GetApicId ();
68 ArchSaveExceptionContext (ExceptionType, SystemContext);
69 ExceptionHandlerContext->ExceptionDataFlag = (mErrorCodeFlag & (1 << ExceptionType)) ? TRUE : FALSE;
70 ExceptionHandlerContext->OldIdtHandler = mReservedVectors[ExceptionType].ExceptonHandler;
71 return;
72 }
73 //
74 // If failed to acquire SPIN_LOCK, check if it was locked by processor itself
75 //
76 if (mReservedVectors[ExceptionType].ApicId == GetApicId ()) {
77 //
78 // Old IDT handler has been executed, then retore CPU exception content to
79 // run new exception handler.
80 //
81 ArchRestoreExceptionContext (ExceptionType, SystemContext);
82 //
83 // Rlease spin lock for ApicId
84 //
85 ReleaseSpinLock (&mReservedVectors[ExceptionType].SpinLock);
86 break;
87 }
88 CpuPause ();
89 }
90 break;
91 case 0xffffffff:
92 break;
93 default:
94 //
95 // It should never reach here
96 //
97 CpuDeadLoop ();
98 break;
99 }
100
101 if (mExternalInterruptHandler[ExceptionType] != NULL) {
102 (mExternalInterruptHandler[ExceptionType]) (ExceptionType, SystemContext);
103 } else if (ExceptionType < CPU_EXCEPTION_NUM) {
104 //
105 // Get Spinlock to display CPU information
106 //
107 while (!AcquireSpinLockOrFail (&mDisplayMessageSpinLock)) {
108 CpuPause ();
109 }
110 //
111 // Display ExceptionType, CPU information and Image information
112 //
113 DumpCpuContent (ExceptionType, SystemContext);
114 //
115 // Release Spinlock of output message
116 //
117 ReleaseSpinLock (&mDisplayMessageSpinLock);
118 //
119 // Enter a dead loop if needn't to execute old IDT handler further
120 //
121 if (mReservedVectors[ExceptionType].Attribute != EFI_VECTOR_HANDOFF_HOOK_BEFORE) {
122 CpuDeadLoop ();
123 }
124 }
125 }
126
127 /**
128 Internal worker function to update IDT entries accordling to vector attributes.
129
130 @param[in] IdtTable Pointer to IDT table.
131 @param[in] TemplateMap Pointer to a buffer where the address map is returned.
132 @param[in] IdtEntryCount IDT entries number to be updated.
133
134 **/
135 VOID
UpdateIdtTable(IN IA32_IDT_GATE_DESCRIPTOR * IdtTable,IN EXCEPTION_HANDLER_TEMPLATE_MAP * TemplateMap,IN UINTN IdtEntryCount)136 UpdateIdtTable (
137 IN IA32_IDT_GATE_DESCRIPTOR *IdtTable,
138 IN EXCEPTION_HANDLER_TEMPLATE_MAP *TemplateMap,
139 IN UINTN IdtEntryCount
140 )
141 {
142 UINT16 CodeSegment;
143 UINTN Index;
144 UINTN InterruptHandler;
145
146 //
147 // Use current CS as the segment selector of interrupt gate in IDT
148 //
149 CodeSegment = AsmReadCs ();
150
151 for (Index = 0; Index < IdtEntryCount; Index ++) {
152 IdtTable[Index].Bits.Selector = CodeSegment;
153 //
154 // Check reserved vectors attributes
155 //
156 switch (mReservedVectors[Index].Attribute) {
157 case EFI_VECTOR_HANDOFF_DO_NOT_HOOK:
158 //
159 // Keep original IDT entry
160 //
161 continue;
162 case EFI_VECTOR_HANDOFF_HOOK_AFTER:
163 InitializeSpinLock (&mReservedVectors[Index].SpinLock);
164 CopyMem (
165 (VOID *) mReservedVectors[Index].HookAfterStubHeaderCode,
166 (VOID *) TemplateMap->HookAfterStubHeaderStart,
167 TemplateMap->ExceptionStubHeaderSize
168 );
169 AsmVectorNumFixup (
170 (VOID *) mReservedVectors[Index].HookAfterStubHeaderCode,
171 (UINT8) Index,
172 (VOID *) TemplateMap->HookAfterStubHeaderStart
173 );
174 //
175 // Go on the following code
176 //
177 case EFI_VECTOR_HANDOFF_HOOK_BEFORE:
178 //
179 // Save original IDT handler address
180 //
181 mReservedVectors[Index].ExceptonHandler = ArchGetIdtHandler (&IdtTable[Index]);
182 //
183 // Go on the following code
184 //
185 default:
186 //
187 // Update new IDT entry
188 //
189 InterruptHandler = TemplateMap->ExceptionStart + Index * TemplateMap->ExceptionStubHeaderSize;
190 ArchUpdateIdtEntry (&IdtTable[Index], InterruptHandler);
191 break;
192 }
193 }
194
195 //
196 // Save Interrupt number to global variable used for RegisterCpuInterruptHandler ()
197 //
198 mEnabledInterruptNum = IdtEntryCount;
199 }
200
201 /**
202 Internal worker function to initialize exception handler.
203
204 @param[in] VectorInfo Pointer to reserved vector list.
205
206 @retval EFI_SUCCESS CPU Exception Entries have been successfully initialized
207 with default exception handlers.
208 @retval EFI_INVALID_PARAMETER VectorInfo includes the invalid content if VectorInfo is not NULL.
209 @retval EFI_UNSUPPORTED This function is not supported.
210
211 **/
212 EFI_STATUS
InitializeCpuExceptionHandlersWorker(IN EFI_VECTOR_HANDOFF_INFO * VectorInfo OPTIONAL)213 InitializeCpuExceptionHandlersWorker (
214 IN EFI_VECTOR_HANDOFF_INFO *VectorInfo OPTIONAL
215 )
216 {
217 EFI_STATUS Status;
218 IA32_DESCRIPTOR IdtDescriptor;
219 UINTN IdtEntryCount;
220 EXCEPTION_HANDLER_TEMPLATE_MAP TemplateMap;
221 IA32_IDT_GATE_DESCRIPTOR *IdtTable;
222
223 mReservedVectors = mReservedVectorsData;
224 SetMem ((VOID *) mReservedVectors, sizeof (RESERVED_VECTORS_DATA) * CPU_EXCEPTION_NUM, 0xff);
225 if (VectorInfo != NULL) {
226 Status = ReadAndVerifyVectorInfo (VectorInfo, mReservedVectors, CPU_EXCEPTION_NUM);
227 if (EFI_ERROR (Status)) {
228 return EFI_INVALID_PARAMETER;
229 }
230 }
231 InitializeSpinLock (&mDisplayMessageSpinLock);
232
233 mExternalInterruptHandler = mExternalInterruptHandlerTable;
234 //
235 // Read IDT descriptor and calculate IDT size
236 //
237 AsmReadIdtr (&IdtDescriptor);
238 IdtEntryCount = (IdtDescriptor.Limit + 1) / sizeof (IA32_IDT_GATE_DESCRIPTOR);
239 if (IdtEntryCount > CPU_EXCEPTION_NUM) {
240 //
241 // CPU exeption library only setup CPU_EXCEPTION_NUM exception handler at most
242 //
243 IdtEntryCount = CPU_EXCEPTION_NUM;
244 }
245
246 IdtTable = (IA32_IDT_GATE_DESCRIPTOR *) IdtDescriptor.Base;
247 AsmGetTemplateAddressMap (&TemplateMap);
248 ASSERT (TemplateMap.ExceptionStubHeaderSize <= HOOKAFTER_STUB_SIZE);
249 UpdateIdtTable (IdtTable, &TemplateMap, IdtEntryCount);
250 mEnabledInterruptNum = IdtEntryCount;
251 return EFI_SUCCESS;
252 }
253
254 /**
255 Registers a function to be called from the processor interrupt handler.
256
257 @param[in] InterruptType Defines which interrupt or exception to hook.
258 @param[in] InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
259 when a processor interrupt occurs. If this parameter is NULL, then the handler
260 will be uninstalled.
261
262 @retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled.
263 @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InterruptType was
264 previously installed.
265 @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not
266 previously installed.
267 @retval EFI_UNSUPPORTED The interrupt specified by InterruptType is not supported,
268 or this function is not supported.
269 **/
270 EFI_STATUS
RegisterCpuInterruptHandlerWorker(IN EFI_EXCEPTION_TYPE InterruptType,IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler)271 RegisterCpuInterruptHandlerWorker (
272 IN EFI_EXCEPTION_TYPE InterruptType,
273 IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler
274 )
275 {
276 if (InterruptType < 0 || InterruptType >= (EFI_EXCEPTION_TYPE)mEnabledInterruptNum ||
277 mReservedVectors[InterruptType].Attribute == EFI_VECTOR_HANDOFF_DO_NOT_HOOK) {
278 return EFI_UNSUPPORTED;
279 }
280
281 if (InterruptHandler == NULL && mExternalInterruptHandler[InterruptType] == NULL) {
282 return EFI_INVALID_PARAMETER;
283 }
284
285 if (InterruptHandler != NULL && mExternalInterruptHandler[InterruptType] != NULL) {
286 return EFI_ALREADY_STARTED;
287 }
288
289 mExternalInterruptHandler[InterruptType] = InterruptHandler;
290 return EFI_SUCCESS;
291 }
292
293