2 CPU Exception Library provides PEI/DXE/SMM CPU common exception handler.
4 Copyright (c) 2012 - 2022, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
9 #include <Library/DebugLib.h>
10 #include <Library/CcExitLib.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
28 EXCEPTION_HANDLER_CONTEXT
*ExceptionHandlerContext
;
29 RESERVED_VECTORS_DATA
*ReservedVectors
;
30 EFI_CPU_INTERRUPT_HANDLER
*ExternalInterruptHandler
;
32 switch (ExceptionType
) {
35 // #VC needs to be handled immediately upon enabling exception handling
36 // and therefore can't use the RegisterCpuInterruptHandler() interface.
39 // On EFI_SUCCESS - Exception has been handled, return
40 // On other - ExceptionType contains (possibly new) exception
43 Status
= CcExitHandleVc (&ExceptionType
, SystemContext
);
44 if (!EFI_ERROR (Status
)) {
52 // #VE needs to be handled immediately upon enabling exception handling
53 // and therefore can't use the RegisterCpuInterruptHandler() interface.
56 // On EFI_SUCCESS - Exception has been handled, return
57 // On other - ExceptionType contains (possibly new) exception
60 Status
= CcExitHandleVe (&ExceptionType
, SystemContext
);
61 if (!EFI_ERROR (Status
)) {
71 ExceptionHandlerContext
= (EXCEPTION_HANDLER_CONTEXT
*)(UINTN
)(SystemContext
.SystemContextIa32
);
72 ReservedVectors
= ExceptionHandlerData
->ReservedVectors
;
73 ExternalInterruptHandler
= ExceptionHandlerData
->ExternalInterruptHandler
;
75 switch (ReservedVectors
[ExceptionType
].Attribute
) {
76 case EFI_VECTOR_HANDOFF_HOOK_BEFORE
:
78 // The new exception handler registered by RegisterCpuInterruptHandler() is executed BEFORE original handler.
79 // Save the original handler to stack so the assembly code can jump to it instead of returning from handler.
81 ExceptionHandlerContext
->ExceptionDataFlag
= (mErrorCodeFlag
& (1 << ExceptionType
)) ? TRUE
: FALSE
;
82 ExceptionHandlerContext
->OldIdtHandler
= ReservedVectors
[ExceptionType
].ExceptonHandler
;
84 case EFI_VECTOR_HANDOFF_HOOK_AFTER
:
87 // If spin-lock can be acquired, it's the first time entering here.
89 if (AcquireSpinLockOrFail (&ReservedVectors
[ExceptionType
].SpinLock
)) {
91 // The new exception handler registered by RegisterCpuInterruptHandler() is executed AFTER original handler.
92 // Save the original handler to stack but skip running the new handler so the original handler is executed
95 ReservedVectors
[ExceptionType
].ApicId
= GetApicId ();
96 ArchSaveExceptionContext (ExceptionType
, SystemContext
, ExceptionHandlerData
);
97 ExceptionHandlerContext
->ExceptionDataFlag
= (mErrorCodeFlag
& (1 << ExceptionType
)) ? TRUE
: FALSE
;
98 ExceptionHandlerContext
->OldIdtHandler
= ReservedVectors
[ExceptionType
].ExceptonHandler
;
103 // If spin-lock cannot be acquired, it's the second time entering here.
104 // 'break' instead of 'return' is used so the new exception handler can be executed.
106 if (ReservedVectors
[ExceptionType
].ApicId
== GetApicId ()) {
108 // Old IDT handler has been executed, then restore CPU exception content to
109 // run new exception handler.
111 ArchRestoreExceptionContext (ExceptionType
, SystemContext
, ExceptionHandlerData
);
113 // Release spin lock for ApicId
115 ReleaseSpinLock (&ReservedVectors
[ExceptionType
].SpinLock
);
127 // It should never reach here
133 if ((ExternalInterruptHandler
!= NULL
) &&
134 (ExternalInterruptHandler
[ExceptionType
] != NULL
))
136 (ExternalInterruptHandler
[ExceptionType
])(ExceptionType
, SystemContext
);
137 } else if (ExceptionType
< CPU_EXCEPTION_NUM
) {
139 // Get Spinlock to display CPU information
141 while (!AcquireSpinLockOrFail (&ExceptionHandlerData
->DisplayMessageSpinLock
)) {
146 // Initialize the serial port before dumping.
148 SerialPortInitialize ();
150 // Display ExceptionType, CPU information and Image information
152 DumpImageAndCpuContent (ExceptionType
, SystemContext
);
154 // Release Spinlock of output message
156 ReleaseSpinLock (&ExceptionHandlerData
->DisplayMessageSpinLock
);
158 // Enter a dead loop if needn't to execute old IDT handler further
160 if (ReservedVectors
[ExceptionType
].Attribute
!= EFI_VECTOR_HANDOFF_HOOK_BEFORE
) {
167 Internal worker function to update IDT entries accordling to vector attributes.
169 @param[in] IdtTable Pointer to IDT table.
170 @param[in] TemplateMap Pointer to a buffer where the address map is
172 @param[in] ExceptionHandlerData Pointer to exception handler data.
177 IN IA32_IDT_GATE_DESCRIPTOR
*IdtTable
,
178 IN EXCEPTION_HANDLER_TEMPLATE_MAP
*TemplateMap
,
179 IN EXCEPTION_HANDLER_DATA
*ExceptionHandlerData
184 UINTN InterruptHandler
;
185 RESERVED_VECTORS_DATA
*ReservedVectors
;
187 ReservedVectors
= ExceptionHandlerData
->ReservedVectors
;
189 // Use current CS as the segment selector of interrupt gate in IDT
191 CodeSegment
= AsmReadCs ();
193 for (Index
= 0; Index
< ExceptionHandlerData
->IdtEntryCount
; Index
++) {
194 IdtTable
[Index
].Bits
.Selector
= CodeSegment
;
196 // Check reserved vectors attributes
198 switch (ReservedVectors
[Index
].Attribute
) {
199 case EFI_VECTOR_HANDOFF_DO_NOT_HOOK
:
201 // Keep original IDT entry
204 case EFI_VECTOR_HANDOFF_HOOK_AFTER
:
205 InitializeSpinLock (&ReservedVectors
[Index
].SpinLock
);
207 (VOID
*)ReservedVectors
[Index
].HookAfterStubHeaderCode
,
208 (VOID
*)TemplateMap
->HookAfterStubHeaderStart
,
209 TemplateMap
->ExceptionStubHeaderSize
212 (VOID
*)ReservedVectors
[Index
].HookAfterStubHeaderCode
,
214 (VOID
*)TemplateMap
->HookAfterStubHeaderStart
217 // Go on the following code
219 case EFI_VECTOR_HANDOFF_HOOK_BEFORE
:
221 // Save original IDT handler address
223 ReservedVectors
[Index
].ExceptonHandler
= ArchGetIdtHandler (&IdtTable
[Index
]);
225 // Go on the following code
229 // Update new IDT entry
231 InterruptHandler
= TemplateMap
->ExceptionStart
+ Index
* TemplateMap
->ExceptionStubHeaderSize
;
232 ArchUpdateIdtEntry (&IdtTable
[Index
], InterruptHandler
);
239 Internal worker function to initialize exception handler.
241 @param[in] VectorInfo Pointer to reserved vector list.
242 @param[in, out] ExceptionHandlerData Pointer to exception handler data.
244 @retval EFI_SUCCESS CPU Exception Entries have been successfully initialized
245 with default exception handlers.
246 @retval EFI_INVALID_PARAMETER VectorInfo includes the invalid content if VectorInfo is not NULL.
247 @retval EFI_UNSUPPORTED This function is not supported.
251 InitializeCpuExceptionHandlersWorker (
252 IN EFI_VECTOR_HANDOFF_INFO
*VectorInfo OPTIONAL
,
253 IN OUT EXCEPTION_HANDLER_DATA
*ExceptionHandlerData
257 IA32_DESCRIPTOR IdtDescriptor
;
259 EXCEPTION_HANDLER_TEMPLATE_MAP TemplateMap
;
260 IA32_IDT_GATE_DESCRIPTOR
*IdtTable
;
261 RESERVED_VECTORS_DATA
*ReservedVectors
;
263 ReservedVectors
= ExceptionHandlerData
->ReservedVectors
;
264 SetMem ((VOID
*)ReservedVectors
, sizeof (RESERVED_VECTORS_DATA
) * ExceptionHandlerData
->IdtEntryCount
, 0xff);
265 if (VectorInfo
!= NULL
) {
266 Status
= ReadAndVerifyVectorInfo (VectorInfo
, ReservedVectors
, ExceptionHandlerData
->IdtEntryCount
);
267 if (EFI_ERROR (Status
)) {
268 return EFI_INVALID_PARAMETER
;
273 // Setup the exception handlers according to IDT size, but no more than
274 // ExceptionHandlerData->IdtEntryCount (32 in PEI and SMM, 256 in DXE) handlers.
276 AsmReadIdtr (&IdtDescriptor
);
277 IdtEntryCount
= (IdtDescriptor
.Limit
+ 1) / sizeof (IA32_IDT_GATE_DESCRIPTOR
);
278 ExceptionHandlerData
->IdtEntryCount
= MIN (IdtEntryCount
, ExceptionHandlerData
->IdtEntryCount
);
280 IdtTable
= (IA32_IDT_GATE_DESCRIPTOR
*)IdtDescriptor
.Base
;
281 AsmGetTemplateAddressMap (&TemplateMap
);
282 ASSERT (TemplateMap
.ExceptionStubHeaderSize
<= HOOKAFTER_STUB_SIZE
);
284 UpdateIdtTable (IdtTable
, &TemplateMap
, ExceptionHandlerData
);
290 Registers a function to be called from the processor interrupt handler.
292 @param[in] InterruptType Defines which interrupt or exception to hook.
293 @param[in] InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
294 when a processor interrupt occurs. If this parameter is NULL, then the handler
296 @param[in] ExceptionHandlerData Pointer to exception handler data.
298 @retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled.
299 @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InterruptType was
300 previously installed.
301 @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not
302 previously installed.
303 @retval EFI_UNSUPPORTED The interrupt specified by InterruptType is not supported,
304 or this function is not supported.
307 RegisterCpuInterruptHandlerWorker (
308 IN EFI_EXCEPTION_TYPE InterruptType
,
309 IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler
,
310 IN EXCEPTION_HANDLER_DATA
*ExceptionHandlerData
313 UINTN EnabledInterruptNum
;
314 RESERVED_VECTORS_DATA
*ReservedVectors
;
315 EFI_CPU_INTERRUPT_HANDLER
*ExternalInterruptHandler
;
317 EnabledInterruptNum
= ExceptionHandlerData
->IdtEntryCount
;
318 ReservedVectors
= ExceptionHandlerData
->ReservedVectors
;
319 ExternalInterruptHandler
= ExceptionHandlerData
->ExternalInterruptHandler
;
321 if ((InterruptType
< 0) || (InterruptType
>= (EFI_EXCEPTION_TYPE
)EnabledInterruptNum
) ||
322 (ReservedVectors
[InterruptType
].Attribute
== EFI_VECTOR_HANDOFF_DO_NOT_HOOK
))
324 return EFI_UNSUPPORTED
;
327 if ((InterruptHandler
== NULL
) && (ExternalInterruptHandler
[InterruptType
] == NULL
)) {
328 return EFI_INVALID_PARAMETER
;
331 if ((InterruptHandler
!= NULL
) && (ExternalInterruptHandler
[InterruptType
] != NULL
)) {
332 return EFI_ALREADY_STARTED
;
335 ExternalInterruptHandler
[InterruptType
] = InterruptHandler
;