]> git.proxmox.com Git - mirror_edk2.git/blob - UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiDxeSmmCpuException.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / UefiCpuPkg / Library / CpuExceptionHandlerLib / PeiDxeSmmCpuException.c
1 /** @file
2 CPU Exception Library provides PEI/DXE/SMM CPU common exception handler.
3
4 Copyright (c) 2012 - 2022, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6
7 **/
8
9 #include <Library/DebugLib.h>
10 #include <Library/CcExitLib.h>
11 #include "CpuExceptionCommon.h"
12
13 /**
14 Internal worker function for common exception handler.
15
16 @param ExceptionType Exception type.
17 @param SystemContext Pointer to EFI_SYSTEM_CONTEXT.
18 @param ExceptionHandlerData Pointer to exception handler data.
19 **/
20 VOID
21 CommonExceptionHandlerWorker (
22 IN EFI_EXCEPTION_TYPE ExceptionType,
23 IN EFI_SYSTEM_CONTEXT SystemContext,
24 IN EXCEPTION_HANDLER_DATA *ExceptionHandlerData
25 )
26 {
27 EFI_STATUS Status;
28 EXCEPTION_HANDLER_CONTEXT *ExceptionHandlerContext;
29 RESERVED_VECTORS_DATA *ReservedVectors;
30 EFI_CPU_INTERRUPT_HANDLER *ExternalInterruptHandler;
31
32 switch (ExceptionType) {
33 case VC_EXCEPTION:
34 //
35 // #VC needs to be handled immediately upon enabling exception handling
36 // and therefore can't use the RegisterCpuInterruptHandler() interface.
37 //
38 // Handle the #VC:
39 // On EFI_SUCCESS - Exception has been handled, return
40 // On other - ExceptionType contains (possibly new) exception
41 // value
42 //
43 Status = CcExitHandleVc (&ExceptionType, SystemContext);
44 if (!EFI_ERROR (Status)) {
45 return;
46 }
47
48 break;
49
50 case VE_EXCEPTION:
51 //
52 // #VE needs to be handled immediately upon enabling exception handling
53 // and therefore can't use the RegisterCpuInterruptHandler() interface.
54 //
55 // Handle the #VE:
56 // On EFI_SUCCESS - Exception has been handled, return
57 // On other - ExceptionType contains (possibly new) exception
58 // value
59 //
60 Status = CcExitHandleVe (&ExceptionType, SystemContext);
61 if (!EFI_ERROR (Status)) {
62 return;
63 }
64
65 break;
66
67 default:
68 break;
69 }
70
71 ExceptionHandlerContext = (EXCEPTION_HANDLER_CONTEXT *)(UINTN)(SystemContext.SystemContextIa32);
72 ReservedVectors = ExceptionHandlerData->ReservedVectors;
73 ExternalInterruptHandler = ExceptionHandlerData->ExternalInterruptHandler;
74
75 switch (ReservedVectors[ExceptionType].Attribute) {
76 case EFI_VECTOR_HANDOFF_HOOK_BEFORE:
77 //
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.
80 //
81 ExceptionHandlerContext->ExceptionDataFlag = (mErrorCodeFlag & (1 << ExceptionType)) ? TRUE : FALSE;
82 ExceptionHandlerContext->OldIdtHandler = ReservedVectors[ExceptionType].ExceptonHandler;
83 break;
84 case EFI_VECTOR_HANDOFF_HOOK_AFTER:
85 while (TRUE) {
86 //
87 // If spin-lock can be acquired, it's the first time entering here.
88 //
89 if (AcquireSpinLockOrFail (&ReservedVectors[ExceptionType].SpinLock)) {
90 //
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
93 // firstly.
94 //
95 ReservedVectors[ExceptionType].ApicId = GetApicId ();
96 ArchSaveExceptionContext (ExceptionType, SystemContext, ExceptionHandlerData);
97 ExceptionHandlerContext->ExceptionDataFlag = (mErrorCodeFlag & (1 << ExceptionType)) ? TRUE : FALSE;
98 ExceptionHandlerContext->OldIdtHandler = ReservedVectors[ExceptionType].ExceptonHandler;
99 return;
100 }
101
102 //
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.
105 //
106 if (ReservedVectors[ExceptionType].ApicId == GetApicId ()) {
107 //
108 // Old IDT handler has been executed, then restore CPU exception content to
109 // run new exception handler.
110 //
111 ArchRestoreExceptionContext (ExceptionType, SystemContext, ExceptionHandlerData);
112 //
113 // Release spin lock for ApicId
114 //
115 ReleaseSpinLock (&ReservedVectors[ExceptionType].SpinLock);
116 break;
117 }
118
119 CpuPause ();
120 }
121
122 break;
123 case 0xffffffff:
124 break;
125 default:
126 //
127 // It should never reach here
128 //
129 CpuDeadLoop ();
130 break;
131 }
132
133 if ((ExternalInterruptHandler != NULL) &&
134 (ExternalInterruptHandler[ExceptionType] != NULL))
135 {
136 (ExternalInterruptHandler[ExceptionType])(ExceptionType, SystemContext);
137 } else if (ExceptionType < CPU_EXCEPTION_NUM) {
138 //
139 // Get Spinlock to display CPU information
140 //
141 while (!AcquireSpinLockOrFail (&ExceptionHandlerData->DisplayMessageSpinLock)) {
142 CpuPause ();
143 }
144
145 //
146 // Initialize the serial port before dumping.
147 //
148 SerialPortInitialize ();
149 //
150 // Display ExceptionType, CPU information and Image information
151 //
152 DumpImageAndCpuContent (ExceptionType, SystemContext);
153 //
154 // Release Spinlock of output message
155 //
156 ReleaseSpinLock (&ExceptionHandlerData->DisplayMessageSpinLock);
157 //
158 // Enter a dead loop if needn't to execute old IDT handler further
159 //
160 if (ReservedVectors[ExceptionType].Attribute != EFI_VECTOR_HANDOFF_HOOK_BEFORE) {
161 CpuDeadLoop ();
162 }
163 }
164 }
165
166 /**
167 Internal worker function to update IDT entries accordling to vector attributes.
168
169 @param[in] IdtTable Pointer to IDT table.
170 @param[in] TemplateMap Pointer to a buffer where the address map is
171 returned.
172 @param[in] ExceptionHandlerData Pointer to exception handler data.
173
174 **/
175 VOID
176 UpdateIdtTable (
177 IN IA32_IDT_GATE_DESCRIPTOR *IdtTable,
178 IN EXCEPTION_HANDLER_TEMPLATE_MAP *TemplateMap,
179 IN EXCEPTION_HANDLER_DATA *ExceptionHandlerData
180 )
181 {
182 UINT16 CodeSegment;
183 UINTN Index;
184 UINTN InterruptHandler;
185 RESERVED_VECTORS_DATA *ReservedVectors;
186
187 ReservedVectors = ExceptionHandlerData->ReservedVectors;
188 //
189 // Use current CS as the segment selector of interrupt gate in IDT
190 //
191 CodeSegment = AsmReadCs ();
192
193 for (Index = 0; Index < ExceptionHandlerData->IdtEntryCount; Index++) {
194 IdtTable[Index].Bits.Selector = CodeSegment;
195 //
196 // Check reserved vectors attributes
197 //
198 switch (ReservedVectors[Index].Attribute) {
199 case EFI_VECTOR_HANDOFF_DO_NOT_HOOK:
200 //
201 // Keep original IDT entry
202 //
203 continue;
204 case EFI_VECTOR_HANDOFF_HOOK_AFTER:
205 InitializeSpinLock (&ReservedVectors[Index].SpinLock);
206 CopyMem (
207 (VOID *)ReservedVectors[Index].HookAfterStubHeaderCode,
208 (VOID *)TemplateMap->HookAfterStubHeaderStart,
209 TemplateMap->ExceptionStubHeaderSize
210 );
211 AsmVectorNumFixup (
212 (VOID *)ReservedVectors[Index].HookAfterStubHeaderCode,
213 (UINT8)Index,
214 (VOID *)TemplateMap->HookAfterStubHeaderStart
215 );
216 //
217 // Go on the following code
218 //
219 case EFI_VECTOR_HANDOFF_HOOK_BEFORE:
220 //
221 // Save original IDT handler address
222 //
223 ReservedVectors[Index].ExceptonHandler = ArchGetIdtHandler (&IdtTable[Index]);
224 //
225 // Go on the following code
226 //
227 default:
228 //
229 // Update new IDT entry
230 //
231 InterruptHandler = TemplateMap->ExceptionStart + Index * TemplateMap->ExceptionStubHeaderSize;
232 ArchUpdateIdtEntry (&IdtTable[Index], InterruptHandler);
233 break;
234 }
235 }
236 }
237
238 /**
239 Internal worker function to initialize exception handler.
240
241 @param[in] VectorInfo Pointer to reserved vector list.
242 @param[in, out] ExceptionHandlerData Pointer to exception handler data.
243
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.
248
249 **/
250 EFI_STATUS
251 InitializeCpuExceptionHandlersWorker (
252 IN EFI_VECTOR_HANDOFF_INFO *VectorInfo OPTIONAL,
253 IN OUT EXCEPTION_HANDLER_DATA *ExceptionHandlerData
254 )
255 {
256 EFI_STATUS Status;
257 IA32_DESCRIPTOR IdtDescriptor;
258 UINTN IdtEntryCount;
259 EXCEPTION_HANDLER_TEMPLATE_MAP TemplateMap;
260 IA32_IDT_GATE_DESCRIPTOR *IdtTable;
261 RESERVED_VECTORS_DATA *ReservedVectors;
262
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;
269 }
270 }
271
272 //
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.
275 //
276 AsmReadIdtr (&IdtDescriptor);
277 IdtEntryCount = (IdtDescriptor.Limit + 1) / sizeof (IA32_IDT_GATE_DESCRIPTOR);
278 ExceptionHandlerData->IdtEntryCount = MIN (IdtEntryCount, ExceptionHandlerData->IdtEntryCount);
279
280 IdtTable = (IA32_IDT_GATE_DESCRIPTOR *)IdtDescriptor.Base;
281 AsmGetTemplateAddressMap (&TemplateMap);
282 ASSERT (TemplateMap.ExceptionStubHeaderSize <= HOOKAFTER_STUB_SIZE);
283
284 UpdateIdtTable (IdtTable, &TemplateMap, ExceptionHandlerData);
285
286 return EFI_SUCCESS;
287 }
288
289 /**
290 Registers a function to be called from the processor interrupt handler.
291
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
295 will be uninstalled
296 @param[in] ExceptionHandlerData Pointer to exception handler data.
297
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.
305 **/
306 EFI_STATUS
307 RegisterCpuInterruptHandlerWorker (
308 IN EFI_EXCEPTION_TYPE InterruptType,
309 IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler,
310 IN EXCEPTION_HANDLER_DATA *ExceptionHandlerData
311 )
312 {
313 UINTN EnabledInterruptNum;
314 RESERVED_VECTORS_DATA *ReservedVectors;
315 EFI_CPU_INTERRUPT_HANDLER *ExternalInterruptHandler;
316
317 EnabledInterruptNum = ExceptionHandlerData->IdtEntryCount;
318 ReservedVectors = ExceptionHandlerData->ReservedVectors;
319 ExternalInterruptHandler = ExceptionHandlerData->ExternalInterruptHandler;
320
321 if ((InterruptType < 0) || (InterruptType >= (EFI_EXCEPTION_TYPE)EnabledInterruptNum) ||
322 (ReservedVectors[InterruptType].Attribute == EFI_VECTOR_HANDOFF_DO_NOT_HOOK))
323 {
324 return EFI_UNSUPPORTED;
325 }
326
327 if ((InterruptHandler == NULL) && (ExternalInterruptHandler[InterruptType] == NULL)) {
328 return EFI_INVALID_PARAMETER;
329 }
330
331 if ((InterruptHandler != NULL) && (ExternalInterruptHandler[InterruptType] != NULL)) {
332 return EFI_ALREADY_STARTED;
333 }
334
335 ExternalInterruptHandler[InterruptType] = InterruptHandler;
336 return EFI_SUCCESS;
337 }