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