2 CPU Exception Library provides PEI/DXE/SMM CPU common exception handler.
4 Copyright (c) 2012 - 2018, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
9 #include <Library/DebugLib.h>
10 #include <Library/VmgExitLib.h>
11 #include "CpuExceptionCommon.h"
14 Internal worker function for common exception handler.
16 @param ExceptionType Exception type.
17 @param SystemContext Pointer to EFI_SYSTEM_CONTEXT.
18 @param ExceptionHandlerData Pointer to exception handler data.
21 CommonExceptionHandlerWorker (
22 IN EFI_EXCEPTION_TYPE ExceptionType
,
23 IN EFI_SYSTEM_CONTEXT SystemContext
,
24 IN EXCEPTION_HANDLER_DATA
*ExceptionHandlerData
27 EXCEPTION_HANDLER_CONTEXT
*ExceptionHandlerContext
;
28 RESERVED_VECTORS_DATA
*ReservedVectors
;
29 EFI_CPU_INTERRUPT_HANDLER
*ExternalInterruptHandler
;
31 if (ExceptionType
== VC_EXCEPTION
) {
34 // #VC needs to be handled immediately upon enabling exception handling
35 // and therefore can't use the RegisterCpuInterruptHandler() interface.
38 // On EFI_SUCCESS - Exception has been handled, return
39 // On other - ExceptionType contains (possibly new) exception
42 Status
= VmgExitHandleVc (&ExceptionType
, SystemContext
);
43 if (!EFI_ERROR (Status
)) {
48 ExceptionHandlerContext
= (EXCEPTION_HANDLER_CONTEXT
*) (UINTN
) (SystemContext
.SystemContextIa32
);
49 ReservedVectors
= ExceptionHandlerData
->ReservedVectors
;
50 ExternalInterruptHandler
= ExceptionHandlerData
->ExternalInterruptHandler
;
52 switch (ReservedVectors
[ExceptionType
].Attribute
) {
53 case EFI_VECTOR_HANDOFF_HOOK_BEFORE
:
55 // The new exception handler registered by RegisterCpuInterruptHandler() is executed BEFORE original handler.
56 // Save the original handler to stack so the assembly code can jump to it instead of returning from handler.
58 ExceptionHandlerContext
->ExceptionDataFlag
= (mErrorCodeFlag
& (1 << ExceptionType
)) ? TRUE
: FALSE
;
59 ExceptionHandlerContext
->OldIdtHandler
= ReservedVectors
[ExceptionType
].ExceptonHandler
;
61 case EFI_VECTOR_HANDOFF_HOOK_AFTER
:
64 // If spin-lock can be acquired, it's the first time entering here.
66 if (AcquireSpinLockOrFail (&ReservedVectors
[ExceptionType
].SpinLock
)) {
68 // The new exception handler registered by RegisterCpuInterruptHandler() is executed AFTER original handler.
69 // Save the original handler to stack but skip running the new handler so the original handler is executed
72 ReservedVectors
[ExceptionType
].ApicId
= GetApicId ();
73 ArchSaveExceptionContext (ExceptionType
, SystemContext
, ExceptionHandlerData
);
74 ExceptionHandlerContext
->ExceptionDataFlag
= (mErrorCodeFlag
& (1 << ExceptionType
)) ? TRUE
: FALSE
;
75 ExceptionHandlerContext
->OldIdtHandler
= ReservedVectors
[ExceptionType
].ExceptonHandler
;
79 // If spin-lock cannot be acquired, it's the second time entering here.
80 // 'break' instead of 'return' is used so the new exception handler can be executed.
82 if (ReservedVectors
[ExceptionType
].ApicId
== GetApicId ()) {
84 // Old IDT handler has been executed, then restore CPU exception content to
85 // run new exception handler.
87 ArchRestoreExceptionContext (ExceptionType
, SystemContext
, ExceptionHandlerData
);
89 // Release spin lock for ApicId
91 ReleaseSpinLock (&ReservedVectors
[ExceptionType
].SpinLock
);
101 // It should never reach here
107 if (ExternalInterruptHandler
!= NULL
&&
108 ExternalInterruptHandler
[ExceptionType
] != NULL
) {
109 (ExternalInterruptHandler
[ExceptionType
]) (ExceptionType
, SystemContext
);
110 } else if (ExceptionType
< CPU_EXCEPTION_NUM
) {
112 // Get Spinlock to display CPU information
114 while (!AcquireSpinLockOrFail (&ExceptionHandlerData
->DisplayMessageSpinLock
)) {
118 // Initialize the serial port before dumping.
120 SerialPortInitialize ();
122 // Display ExceptionType, CPU information and Image information
124 DumpImageAndCpuContent (ExceptionType
, SystemContext
);
126 // Release Spinlock of output message
128 ReleaseSpinLock (&ExceptionHandlerData
->DisplayMessageSpinLock
);
130 // Enter a dead loop if needn't to execute old IDT handler further
132 if (ReservedVectors
[ExceptionType
].Attribute
!= EFI_VECTOR_HANDOFF_HOOK_BEFORE
) {
139 Internal worker function to update IDT entries accordling to vector attributes.
141 @param[in] IdtTable Pointer to IDT table.
142 @param[in] TemplateMap Pointer to a buffer where the address map is
144 @param[in] ExceptionHandlerData Pointer to exception handler data.
149 IN IA32_IDT_GATE_DESCRIPTOR
*IdtTable
,
150 IN EXCEPTION_HANDLER_TEMPLATE_MAP
*TemplateMap
,
151 IN EXCEPTION_HANDLER_DATA
*ExceptionHandlerData
156 UINTN InterruptHandler
;
157 RESERVED_VECTORS_DATA
*ReservedVectors
;
159 ReservedVectors
= ExceptionHandlerData
->ReservedVectors
;
161 // Use current CS as the segment selector of interrupt gate in IDT
163 CodeSegment
= AsmReadCs ();
165 for (Index
= 0; Index
< ExceptionHandlerData
->IdtEntryCount
; Index
++) {
166 IdtTable
[Index
].Bits
.Selector
= CodeSegment
;
168 // Check reserved vectors attributes
170 switch (ReservedVectors
[Index
].Attribute
) {
171 case EFI_VECTOR_HANDOFF_DO_NOT_HOOK
:
173 // Keep original IDT entry
176 case EFI_VECTOR_HANDOFF_HOOK_AFTER
:
177 InitializeSpinLock (&ReservedVectors
[Index
].SpinLock
);
179 (VOID
*) ReservedVectors
[Index
].HookAfterStubHeaderCode
,
180 (VOID
*) TemplateMap
->HookAfterStubHeaderStart
,
181 TemplateMap
->ExceptionStubHeaderSize
184 (VOID
*) ReservedVectors
[Index
].HookAfterStubHeaderCode
,
186 (VOID
*) TemplateMap
->HookAfterStubHeaderStart
189 // Go on the following code
191 case EFI_VECTOR_HANDOFF_HOOK_BEFORE
:
193 // Save original IDT handler address
195 ReservedVectors
[Index
].ExceptonHandler
= ArchGetIdtHandler (&IdtTable
[Index
]);
197 // Go on the following code
201 // Update new IDT entry
203 InterruptHandler
= TemplateMap
->ExceptionStart
+ Index
* TemplateMap
->ExceptionStubHeaderSize
;
204 ArchUpdateIdtEntry (&IdtTable
[Index
], InterruptHandler
);
211 Internal worker function to initialize exception handler.
213 @param[in] VectorInfo Pointer to reserved vector list.
214 @param[in, out] ExceptionHandlerData Pointer to exception handler data.
216 @retval EFI_SUCCESS CPU Exception Entries have been successfully initialized
217 with default exception handlers.
218 @retval EFI_INVALID_PARAMETER VectorInfo includes the invalid content if VectorInfo is not NULL.
219 @retval EFI_UNSUPPORTED This function is not supported.
223 InitializeCpuExceptionHandlersWorker (
224 IN EFI_VECTOR_HANDOFF_INFO
*VectorInfo OPTIONAL
,
225 IN OUT EXCEPTION_HANDLER_DATA
*ExceptionHandlerData
229 IA32_DESCRIPTOR IdtDescriptor
;
231 EXCEPTION_HANDLER_TEMPLATE_MAP TemplateMap
;
232 IA32_IDT_GATE_DESCRIPTOR
*IdtTable
;
233 RESERVED_VECTORS_DATA
*ReservedVectors
;
235 ReservedVectors
= ExceptionHandlerData
->ReservedVectors
;
236 SetMem ((VOID
*) ReservedVectors
, sizeof (RESERVED_VECTORS_DATA
) * CPU_EXCEPTION_NUM
, 0xff);
237 if (VectorInfo
!= NULL
) {
238 Status
= ReadAndVerifyVectorInfo (VectorInfo
, ReservedVectors
, CPU_EXCEPTION_NUM
);
239 if (EFI_ERROR (Status
)) {
240 return EFI_INVALID_PARAMETER
;
245 // Read IDT descriptor and calculate IDT size
247 AsmReadIdtr (&IdtDescriptor
);
248 IdtEntryCount
= (IdtDescriptor
.Limit
+ 1) / sizeof (IA32_IDT_GATE_DESCRIPTOR
);
249 if (IdtEntryCount
> CPU_EXCEPTION_NUM
) {
251 // CPU exception library only setup CPU_EXCEPTION_NUM exception handler at most
253 IdtEntryCount
= CPU_EXCEPTION_NUM
;
256 IdtTable
= (IA32_IDT_GATE_DESCRIPTOR
*) IdtDescriptor
.Base
;
257 AsmGetTemplateAddressMap (&TemplateMap
);
258 ASSERT (TemplateMap
.ExceptionStubHeaderSize
<= HOOKAFTER_STUB_SIZE
);
260 ExceptionHandlerData
->IdtEntryCount
= IdtEntryCount
;
261 UpdateIdtTable (IdtTable
, &TemplateMap
, ExceptionHandlerData
);
267 Registers a function to be called from the processor interrupt handler.
269 @param[in] InterruptType Defines which interrupt or exception to hook.
270 @param[in] InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
271 when a processor interrupt occurs. If this parameter is NULL, then the handler
273 @param[in] ExceptionHandlerData Pointer to exception handler data.
275 @retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled.
276 @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InterruptType was
277 previously installed.
278 @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not
279 previously installed.
280 @retval EFI_UNSUPPORTED The interrupt specified by InterruptType is not supported,
281 or this function is not supported.
284 RegisterCpuInterruptHandlerWorker (
285 IN EFI_EXCEPTION_TYPE InterruptType
,
286 IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler
,
287 IN EXCEPTION_HANDLER_DATA
*ExceptionHandlerData
290 UINTN EnabledInterruptNum
;
291 RESERVED_VECTORS_DATA
*ReservedVectors
;
292 EFI_CPU_INTERRUPT_HANDLER
*ExternalInterruptHandler
;
294 EnabledInterruptNum
= ExceptionHandlerData
->IdtEntryCount
;
295 ReservedVectors
= ExceptionHandlerData
->ReservedVectors
;
296 ExternalInterruptHandler
= ExceptionHandlerData
->ExternalInterruptHandler
;
298 if (InterruptType
< 0 || InterruptType
>= (EFI_EXCEPTION_TYPE
)EnabledInterruptNum
||
299 ReservedVectors
[InterruptType
].Attribute
== EFI_VECTOR_HANDOFF_DO_NOT_HOOK
) {
300 return EFI_UNSUPPORTED
;
303 if (InterruptHandler
== NULL
&& ExternalInterruptHandler
[InterruptType
] == NULL
) {
304 return EFI_INVALID_PARAMETER
;
307 if (InterruptHandler
!= NULL
&& ExternalInterruptHandler
[InterruptType
] != NULL
) {
308 return EFI_ALREADY_STARTED
;
311 ExternalInterruptHandler
[InterruptType
] = InterruptHandler
;