]> git.proxmox.com Git - mirror_edk2.git/blob - UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiDxeSmmCpuException.c
UefiCpuPkg/CpuExceptionHandler: Add base support for the #VC exception
[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 - 2018, 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/VmgExitLib.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 EXCEPTION_HANDLER_CONTEXT *ExceptionHandlerContext;
28 RESERVED_VECTORS_DATA *ReservedVectors;
29 EFI_CPU_INTERRUPT_HANDLER *ExternalInterruptHandler;
30
31 if (ExceptionType == VC_EXCEPTION) {
32 EFI_STATUS Status;
33 //
34 // #VC needs to be handled immediately upon enabling exception handling
35 // and therefore can't use the RegisterCpuInterruptHandler() interface.
36 //
37 // Handle the #VC:
38 // On EFI_SUCCESS - Exception has been handled, return
39 // On other - ExceptionType contains (possibly new) exception
40 // value
41 //
42 Status = VmgExitHandleVc (&ExceptionType, SystemContext);
43 if (!EFI_ERROR (Status)) {
44 return;
45 }
46 }
47
48 ExceptionHandlerContext = (EXCEPTION_HANDLER_CONTEXT *) (UINTN) (SystemContext.SystemContextIa32);
49 ReservedVectors = ExceptionHandlerData->ReservedVectors;
50 ExternalInterruptHandler = ExceptionHandlerData->ExternalInterruptHandler;
51
52 switch (ReservedVectors[ExceptionType].Attribute) {
53 case EFI_VECTOR_HANDOFF_HOOK_BEFORE:
54 //
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.
57 //
58 ExceptionHandlerContext->ExceptionDataFlag = (mErrorCodeFlag & (1 << ExceptionType)) ? TRUE : FALSE;
59 ExceptionHandlerContext->OldIdtHandler = ReservedVectors[ExceptionType].ExceptonHandler;
60 break;
61 case EFI_VECTOR_HANDOFF_HOOK_AFTER:
62 while (TRUE) {
63 //
64 // If spin-lock can be acquired, it's the first time entering here.
65 //
66 if (AcquireSpinLockOrFail (&ReservedVectors[ExceptionType].SpinLock)) {
67 //
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
70 // firstly.
71 //
72 ReservedVectors[ExceptionType].ApicId = GetApicId ();
73 ArchSaveExceptionContext (ExceptionType, SystemContext, ExceptionHandlerData);
74 ExceptionHandlerContext->ExceptionDataFlag = (mErrorCodeFlag & (1 << ExceptionType)) ? TRUE : FALSE;
75 ExceptionHandlerContext->OldIdtHandler = ReservedVectors[ExceptionType].ExceptonHandler;
76 return;
77 }
78 //
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.
81 //
82 if (ReservedVectors[ExceptionType].ApicId == GetApicId ()) {
83 //
84 // Old IDT handler has been executed, then restore CPU exception content to
85 // run new exception handler.
86 //
87 ArchRestoreExceptionContext (ExceptionType, SystemContext, ExceptionHandlerData);
88 //
89 // Release spin lock for ApicId
90 //
91 ReleaseSpinLock (&ReservedVectors[ExceptionType].SpinLock);
92 break;
93 }
94 CpuPause ();
95 }
96 break;
97 case 0xffffffff:
98 break;
99 default:
100 //
101 // It should never reach here
102 //
103 CpuDeadLoop ();
104 break;
105 }
106
107 if (ExternalInterruptHandler != NULL &&
108 ExternalInterruptHandler[ExceptionType] != NULL) {
109 (ExternalInterruptHandler[ExceptionType]) (ExceptionType, SystemContext);
110 } else if (ExceptionType < CPU_EXCEPTION_NUM) {
111 //
112 // Get Spinlock to display CPU information
113 //
114 while (!AcquireSpinLockOrFail (&ExceptionHandlerData->DisplayMessageSpinLock)) {
115 CpuPause ();
116 }
117 //
118 // Initialize the serial port before dumping.
119 //
120 SerialPortInitialize ();
121 //
122 // Display ExceptionType, CPU information and Image information
123 //
124 DumpImageAndCpuContent (ExceptionType, SystemContext);
125 //
126 // Release Spinlock of output message
127 //
128 ReleaseSpinLock (&ExceptionHandlerData->DisplayMessageSpinLock);
129 //
130 // Enter a dead loop if needn't to execute old IDT handler further
131 //
132 if (ReservedVectors[ExceptionType].Attribute != EFI_VECTOR_HANDOFF_HOOK_BEFORE) {
133 CpuDeadLoop ();
134 }
135 }
136 }
137
138 /**
139 Internal worker function to update IDT entries accordling to vector attributes.
140
141 @param[in] IdtTable Pointer to IDT table.
142 @param[in] TemplateMap Pointer to a buffer where the address map is
143 returned.
144 @param[in] ExceptionHandlerData Pointer to exception handler data.
145
146 **/
147 VOID
148 UpdateIdtTable (
149 IN IA32_IDT_GATE_DESCRIPTOR *IdtTable,
150 IN EXCEPTION_HANDLER_TEMPLATE_MAP *TemplateMap,
151 IN EXCEPTION_HANDLER_DATA *ExceptionHandlerData
152 )
153 {
154 UINT16 CodeSegment;
155 UINTN Index;
156 UINTN InterruptHandler;
157 RESERVED_VECTORS_DATA *ReservedVectors;
158
159 ReservedVectors = ExceptionHandlerData->ReservedVectors;
160 //
161 // Use current CS as the segment selector of interrupt gate in IDT
162 //
163 CodeSegment = AsmReadCs ();
164
165 for (Index = 0; Index < ExceptionHandlerData->IdtEntryCount; Index ++) {
166 IdtTable[Index].Bits.Selector = CodeSegment;
167 //
168 // Check reserved vectors attributes
169 //
170 switch (ReservedVectors[Index].Attribute) {
171 case EFI_VECTOR_HANDOFF_DO_NOT_HOOK:
172 //
173 // Keep original IDT entry
174 //
175 continue;
176 case EFI_VECTOR_HANDOFF_HOOK_AFTER:
177 InitializeSpinLock (&ReservedVectors[Index].SpinLock);
178 CopyMem (
179 (VOID *) ReservedVectors[Index].HookAfterStubHeaderCode,
180 (VOID *) TemplateMap->HookAfterStubHeaderStart,
181 TemplateMap->ExceptionStubHeaderSize
182 );
183 AsmVectorNumFixup (
184 (VOID *) ReservedVectors[Index].HookAfterStubHeaderCode,
185 (UINT8) Index,
186 (VOID *) TemplateMap->HookAfterStubHeaderStart
187 );
188 //
189 // Go on the following code
190 //
191 case EFI_VECTOR_HANDOFF_HOOK_BEFORE:
192 //
193 // Save original IDT handler address
194 //
195 ReservedVectors[Index].ExceptonHandler = ArchGetIdtHandler (&IdtTable[Index]);
196 //
197 // Go on the following code
198 //
199 default:
200 //
201 // Update new IDT entry
202 //
203 InterruptHandler = TemplateMap->ExceptionStart + Index * TemplateMap->ExceptionStubHeaderSize;
204 ArchUpdateIdtEntry (&IdtTable[Index], InterruptHandler);
205 break;
206 }
207 }
208 }
209
210 /**
211 Internal worker function to initialize exception handler.
212
213 @param[in] VectorInfo Pointer to reserved vector list.
214 @param[in, out] ExceptionHandlerData Pointer to exception handler data.
215
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.
220
221 **/
222 EFI_STATUS
223 InitializeCpuExceptionHandlersWorker (
224 IN EFI_VECTOR_HANDOFF_INFO *VectorInfo OPTIONAL,
225 IN OUT EXCEPTION_HANDLER_DATA *ExceptionHandlerData
226 )
227 {
228 EFI_STATUS Status;
229 IA32_DESCRIPTOR IdtDescriptor;
230 UINTN IdtEntryCount;
231 EXCEPTION_HANDLER_TEMPLATE_MAP TemplateMap;
232 IA32_IDT_GATE_DESCRIPTOR *IdtTable;
233 RESERVED_VECTORS_DATA *ReservedVectors;
234
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;
241 }
242 }
243
244 //
245 // Read IDT descriptor and calculate IDT size
246 //
247 AsmReadIdtr (&IdtDescriptor);
248 IdtEntryCount = (IdtDescriptor.Limit + 1) / sizeof (IA32_IDT_GATE_DESCRIPTOR);
249 if (IdtEntryCount > CPU_EXCEPTION_NUM) {
250 //
251 // CPU exception library only setup CPU_EXCEPTION_NUM exception handler at most
252 //
253 IdtEntryCount = CPU_EXCEPTION_NUM;
254 }
255
256 IdtTable = (IA32_IDT_GATE_DESCRIPTOR *) IdtDescriptor.Base;
257 AsmGetTemplateAddressMap (&TemplateMap);
258 ASSERT (TemplateMap.ExceptionStubHeaderSize <= HOOKAFTER_STUB_SIZE);
259
260 ExceptionHandlerData->IdtEntryCount = IdtEntryCount;
261 UpdateIdtTable (IdtTable, &TemplateMap, ExceptionHandlerData);
262
263 return EFI_SUCCESS;
264 }
265
266 /**
267 Registers a function to be called from the processor interrupt handler.
268
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
272 will be uninstalled
273 @param[in] ExceptionHandlerData Pointer to exception handler data.
274
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.
282 **/
283 EFI_STATUS
284 RegisterCpuInterruptHandlerWorker (
285 IN EFI_EXCEPTION_TYPE InterruptType,
286 IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler,
287 IN EXCEPTION_HANDLER_DATA *ExceptionHandlerData
288 )
289 {
290 UINTN EnabledInterruptNum;
291 RESERVED_VECTORS_DATA *ReservedVectors;
292 EFI_CPU_INTERRUPT_HANDLER *ExternalInterruptHandler;
293
294 EnabledInterruptNum = ExceptionHandlerData->IdtEntryCount;
295 ReservedVectors = ExceptionHandlerData->ReservedVectors;
296 ExternalInterruptHandler = ExceptionHandlerData->ExternalInterruptHandler;
297
298 if (InterruptType < 0 || InterruptType >= (EFI_EXCEPTION_TYPE)EnabledInterruptNum ||
299 ReservedVectors[InterruptType].Attribute == EFI_VECTOR_HANDOFF_DO_NOT_HOOK) {
300 return EFI_UNSUPPORTED;
301 }
302
303 if (InterruptHandler == NULL && ExternalInterruptHandler[InterruptType] == NULL) {
304 return EFI_INVALID_PARAMETER;
305 }
306
307 if (InterruptHandler != NULL && ExternalInterruptHandler[InterruptType] != NULL) {
308 return EFI_ALREADY_STARTED;
309 }
310
311 ExternalInterruptHandler[InterruptType] = InterruptHandler;
312 return EFI_SUCCESS;
313 }
314