]> git.proxmox.com Git - mirror_edk2.git/blob - UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
UefiCpuPkg/ExceptionLib: Add CET support.
[mirror_edk2.git] / UefiCpuPkg / Library / CpuExceptionHandlerLib / X64 / ArchExceptionHandler.c
1 /** @file
2 x64 CPU Exception Handler.
3
4 Copyright (c) 2012 - 2019, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. 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
17 /**
18 Return address map of exception handler template so that C code can generate
19 exception tables.
20
21 @param IdtEntry Pointer to IDT entry to be updated.
22 @param InterruptHandler IDT handler value.
23 **/
24 VOID
25 ArchUpdateIdtEntry (
26 IN IA32_IDT_GATE_DESCRIPTOR *IdtEntry,
27 IN UINTN InterruptHandler
28 )
29 {
30 IdtEntry->Bits.OffsetLow = (UINT16)(UINTN)InterruptHandler;
31 IdtEntry->Bits.OffsetHigh = (UINT16)((UINTN)InterruptHandler >> 16);
32 IdtEntry->Bits.OffsetUpper = (UINT32)((UINTN)InterruptHandler >> 32);
33 IdtEntry->Bits.GateType = IA32_IDT_GATE_TYPE_INTERRUPT_32;
34 }
35
36 /**
37 Read IDT handler value from IDT entry.
38
39 @param IdtEntry Pointer to IDT entry to be read.
40
41 **/
42 UINTN
43 ArchGetIdtHandler (
44 IN IA32_IDT_GATE_DESCRIPTOR *IdtEntry
45 )
46 {
47 return IdtEntry->Bits.OffsetLow + (((UINTN) IdtEntry->Bits.OffsetHigh) << 16) +
48 (((UINTN) IdtEntry->Bits.OffsetUpper) << 32);
49 }
50
51 /**
52 Save CPU exception context when handling EFI_VECTOR_HANDOFF_HOOK_AFTER case.
53
54 @param[in] ExceptionType Exception type.
55 @param[in] SystemContext Pointer to EFI_SYSTEM_CONTEXT.
56 @param[in] ExceptionHandlerData Pointer to exception handler data.
57 **/
58 VOID
59 ArchSaveExceptionContext (
60 IN UINTN ExceptionType,
61 IN EFI_SYSTEM_CONTEXT SystemContext,
62 IN EXCEPTION_HANDLER_DATA *ExceptionHandlerData
63 )
64 {
65 IA32_EFLAGS32 Eflags;
66 RESERVED_VECTORS_DATA *ReservedVectors;
67
68 ReservedVectors = ExceptionHandlerData->ReservedVectors;
69 //
70 // Save Exception context in global variable in first entry of the exception handler.
71 // So when original exception handler returns to the new exception handler (second entry),
72 // the Eflags/Cs/Eip/ExceptionData can be used.
73 //
74 ReservedVectors[ExceptionType].OldSs = SystemContext.SystemContextX64->Ss;
75 ReservedVectors[ExceptionType].OldSp = SystemContext.SystemContextX64->Rsp;
76 ReservedVectors[ExceptionType].OldFlags = SystemContext.SystemContextX64->Rflags;
77 ReservedVectors[ExceptionType].OldCs = SystemContext.SystemContextX64->Cs;
78 ReservedVectors[ExceptionType].OldIp = SystemContext.SystemContextX64->Rip;
79 ReservedVectors[ExceptionType].ExceptionData = SystemContext.SystemContextX64->ExceptionData;
80 //
81 // Clear IF flag to avoid old IDT handler enable interrupt by IRET
82 //
83 Eflags.UintN = SystemContext.SystemContextX64->Rflags;
84 Eflags.Bits.IF = 0;
85 SystemContext.SystemContextX64->Rflags = Eflags.UintN;
86 //
87 // Modify the EIP in stack, then old IDT handler will return to HookAfterStubBegin.
88 //
89 SystemContext.SystemContextX64->Rip = (UINTN) ReservedVectors[ExceptionType].HookAfterStubHeaderCode;
90 }
91
92 /**
93 Restore CPU exception context when handling EFI_VECTOR_HANDOFF_HOOK_AFTER case.
94
95 @param[in] ExceptionType Exception type.
96 @param[in] SystemContext Pointer to EFI_SYSTEM_CONTEXT.
97 @param[in] ExceptionHandlerData Pointer to exception handler data.
98 **/
99 VOID
100 ArchRestoreExceptionContext (
101 IN UINTN ExceptionType,
102 IN EFI_SYSTEM_CONTEXT SystemContext,
103 IN EXCEPTION_HANDLER_DATA *ExceptionHandlerData
104 )
105 {
106 RESERVED_VECTORS_DATA *ReservedVectors;
107
108 ReservedVectors = ExceptionHandlerData->ReservedVectors;
109 SystemContext.SystemContextX64->Ss = ReservedVectors[ExceptionType].OldSs;
110 SystemContext.SystemContextX64->Rsp = ReservedVectors[ExceptionType].OldSp;
111 SystemContext.SystemContextX64->Rflags = ReservedVectors[ExceptionType].OldFlags;
112 SystemContext.SystemContextX64->Cs = ReservedVectors[ExceptionType].OldCs;
113 SystemContext.SystemContextX64->Rip = ReservedVectors[ExceptionType].OldIp;
114 SystemContext.SystemContextX64->ExceptionData = ReservedVectors[ExceptionType].ExceptionData;
115 }
116
117 /**
118 Setup separate stack for given exceptions.
119
120 @param[in] StackSwitchData Pointer to data required for setuping up
121 stack switch.
122
123 @retval EFI_SUCCESS The exceptions have been successfully
124 initialized with new stack.
125 @retval EFI_INVALID_PARAMETER StackSwitchData contains invalid content.
126
127 **/
128 EFI_STATUS
129 ArchSetupExceptionStack (
130 IN CPU_EXCEPTION_INIT_DATA *StackSwitchData
131 )
132 {
133 IA32_DESCRIPTOR Gdtr;
134 IA32_DESCRIPTOR Idtr;
135 IA32_IDT_GATE_DESCRIPTOR *IdtTable;
136 IA32_TSS_DESCRIPTOR *TssDesc;
137 IA32_TASK_STATE_SEGMENT *Tss;
138 UINTN StackTop;
139 UINTN Index;
140 UINTN Vector;
141 UINTN TssBase;
142 UINTN GdtSize;
143
144 if (StackSwitchData == NULL ||
145 StackSwitchData->Ia32.Revision != CPU_EXCEPTION_INIT_DATA_REV ||
146 StackSwitchData->X64.KnownGoodStackTop == 0 ||
147 StackSwitchData->X64.KnownGoodStackSize == 0 ||
148 StackSwitchData->X64.StackSwitchExceptions == NULL ||
149 StackSwitchData->X64.StackSwitchExceptionNumber == 0 ||
150 StackSwitchData->X64.StackSwitchExceptionNumber > CPU_EXCEPTION_NUM ||
151 StackSwitchData->X64.GdtTable == NULL ||
152 StackSwitchData->X64.IdtTable == NULL ||
153 StackSwitchData->X64.ExceptionTssDesc == NULL ||
154 StackSwitchData->X64.ExceptionTss == NULL) {
155 return EFI_INVALID_PARAMETER;
156 }
157
158 //
159 // The caller is responsible for that the GDT table, no matter the existing
160 // one or newly allocated, has enough space to hold descriptors for exception
161 // task-state segments.
162 //
163 if (((UINTN)StackSwitchData->X64.GdtTable & (IA32_GDT_ALIGNMENT - 1)) != 0) {
164 return EFI_INVALID_PARAMETER;
165 }
166
167 if ((UINTN)StackSwitchData->X64.ExceptionTssDesc < (UINTN)(StackSwitchData->X64.GdtTable)) {
168 return EFI_INVALID_PARAMETER;
169 }
170
171 if (((UINTN)StackSwitchData->X64.ExceptionTssDesc + StackSwitchData->X64.ExceptionTssDescSize) >
172 ((UINTN)(StackSwitchData->X64.GdtTable) + StackSwitchData->X64.GdtTableSize)) {
173 return EFI_INVALID_PARAMETER;
174 }
175
176 //
177 // One task gate descriptor and one task-state segment are needed.
178 //
179 if (StackSwitchData->X64.ExceptionTssDescSize < sizeof (IA32_TSS_DESCRIPTOR)) {
180 return EFI_INVALID_PARAMETER;
181 }
182 if (StackSwitchData->X64.ExceptionTssSize < sizeof (IA32_TASK_STATE_SEGMENT)) {
183 return EFI_INVALID_PARAMETER;
184 }
185
186 //
187 // Interrupt stack table supports only 7 vectors.
188 //
189 TssDesc = StackSwitchData->X64.ExceptionTssDesc;
190 Tss = StackSwitchData->X64.ExceptionTss;
191 if (StackSwitchData->X64.StackSwitchExceptionNumber > ARRAY_SIZE (Tss->IST)) {
192 return EFI_INVALID_PARAMETER;
193 }
194
195 //
196 // Initialize new GDT table and/or IDT table, if any
197 //
198 AsmReadIdtr (&Idtr);
199 AsmReadGdtr (&Gdtr);
200
201 GdtSize = (UINTN)TssDesc + sizeof (IA32_TSS_DESCRIPTOR) -
202 (UINTN)(StackSwitchData->X64.GdtTable);
203 if ((UINTN)StackSwitchData->X64.GdtTable != Gdtr.Base) {
204 CopyMem (StackSwitchData->X64.GdtTable, (VOID *)Gdtr.Base, Gdtr.Limit + 1);
205 Gdtr.Base = (UINTN)StackSwitchData->X64.GdtTable;
206 Gdtr.Limit = (UINT16)GdtSize - 1;
207 }
208
209 if ((UINTN)StackSwitchData->X64.IdtTable != Idtr.Base) {
210 Idtr.Base = (UINTN)StackSwitchData->X64.IdtTable;
211 }
212 if (StackSwitchData->X64.IdtTableSize > 0) {
213 Idtr.Limit = (UINT16)(StackSwitchData->X64.IdtTableSize - 1);
214 }
215
216 //
217 // Fixup current task descriptor. Task-state segment for current task will
218 // be filled by processor during task switching.
219 //
220 TssBase = (UINTN)Tss;
221
222 TssDesc->Uint128.Uint64 = 0;
223 TssDesc->Uint128.Uint64_1= 0;
224 TssDesc->Bits.LimitLow = sizeof(IA32_TASK_STATE_SEGMENT) - 1;
225 TssDesc->Bits.BaseLow = (UINT16)TssBase;
226 TssDesc->Bits.BaseMidl = (UINT8)(TssBase >> 16);
227 TssDesc->Bits.Type = IA32_GDT_TYPE_TSS;
228 TssDesc->Bits.P = 1;
229 TssDesc->Bits.LimitHigh = 0;
230 TssDesc->Bits.BaseMidh = (UINT8)(TssBase >> 24);
231 TssDesc->Bits.BaseHigh = (UINT32)(TssBase >> 32);
232
233 //
234 // Fixup exception task descriptor and task-state segment
235 //
236 ZeroMem (Tss, sizeof (*Tss));
237 StackTop = StackSwitchData->X64.KnownGoodStackTop - CPU_STACK_ALIGNMENT;
238 StackTop = (UINTN)ALIGN_POINTER (StackTop, CPU_STACK_ALIGNMENT);
239 IdtTable = StackSwitchData->X64.IdtTable;
240 for (Index = 0; Index < StackSwitchData->X64.StackSwitchExceptionNumber; ++Index) {
241 //
242 // Fixup IST
243 //
244 Tss->IST[Index] = StackTop;
245 StackTop -= StackSwitchData->X64.KnownGoodStackSize;
246
247 //
248 // Set the IST field to enable corresponding IST
249 //
250 Vector = StackSwitchData->X64.StackSwitchExceptions[Index];
251 if (Vector >= CPU_EXCEPTION_NUM ||
252 Vector >= (Idtr.Limit + 1) / sizeof (IA32_IDT_GATE_DESCRIPTOR)) {
253 continue;
254 }
255 IdtTable[Vector].Bits.Reserved_0 = (UINT8)(Index + 1);
256 }
257
258 //
259 // Publish GDT
260 //
261 AsmWriteGdtr (&Gdtr);
262
263 //
264 // Load current task
265 //
266 AsmWriteTr ((UINT16)((UINTN)StackSwitchData->X64.ExceptionTssDesc - Gdtr.Base));
267
268 //
269 // Publish IDT
270 //
271 AsmWriteIdtr (&Idtr);
272
273 return EFI_SUCCESS;
274 }
275
276 /**
277 Display CPU information.
278
279 @param ExceptionType Exception type.
280 @param SystemContext Pointer to EFI_SYSTEM_CONTEXT.
281 **/
282 VOID
283 EFIAPI
284 DumpCpuContext (
285 IN EFI_EXCEPTION_TYPE ExceptionType,
286 IN EFI_SYSTEM_CONTEXT SystemContext
287 )
288 {
289 InternalPrintMessage (
290 "!!!! X64 Exception Type - %02x(%a) CPU Apic ID - %08x !!!!\n",
291 ExceptionType,
292 GetExceptionNameStr (ExceptionType),
293 GetApicId ()
294 );
295 if ((mErrorCodeFlag & (1 << ExceptionType)) != 0) {
296 InternalPrintMessage (
297 "ExceptionData - %016lx",
298 SystemContext.SystemContextX64->ExceptionData
299 );
300 if (ExceptionType == EXCEPT_IA32_PAGE_FAULT) {
301 InternalPrintMessage (
302 " I:%x R:%x U:%x W:%x P:%x PK:%x SS:%x SGX:%x",
303 (SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_ID) != 0,
304 (SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_RSVD) != 0,
305 (SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_US) != 0,
306 (SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_WR) != 0,
307 (SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_P) != 0,
308 (SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_PK) != 0,
309 (SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_SS) != 0,
310 (SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_SGX) != 0
311 );
312 }
313 InternalPrintMessage ("\n");
314 }
315 InternalPrintMessage (
316 "RIP - %016lx, CS - %016lx, RFLAGS - %016lx\n",
317 SystemContext.SystemContextX64->Rip,
318 SystemContext.SystemContextX64->Cs,
319 SystemContext.SystemContextX64->Rflags
320 );
321 InternalPrintMessage (
322 "RAX - %016lx, RCX - %016lx, RDX - %016lx\n",
323 SystemContext.SystemContextX64->Rax,
324 SystemContext.SystemContextX64->Rcx,
325 SystemContext.SystemContextX64->Rdx
326 );
327 InternalPrintMessage (
328 "RBX - %016lx, RSP - %016lx, RBP - %016lx\n",
329 SystemContext.SystemContextX64->Rbx,
330 SystemContext.SystemContextX64->Rsp,
331 SystemContext.SystemContextX64->Rbp
332 );
333 InternalPrintMessage (
334 "RSI - %016lx, RDI - %016lx\n",
335 SystemContext.SystemContextX64->Rsi,
336 SystemContext.SystemContextX64->Rdi
337 );
338 InternalPrintMessage (
339 "R8 - %016lx, R9 - %016lx, R10 - %016lx\n",
340 SystemContext.SystemContextX64->R8,
341 SystemContext.SystemContextX64->R9,
342 SystemContext.SystemContextX64->R10
343 );
344 InternalPrintMessage (
345 "R11 - %016lx, R12 - %016lx, R13 - %016lx\n",
346 SystemContext.SystemContextX64->R11,
347 SystemContext.SystemContextX64->R12,
348 SystemContext.SystemContextX64->R13
349 );
350 InternalPrintMessage (
351 "R14 - %016lx, R15 - %016lx\n",
352 SystemContext.SystemContextX64->R14,
353 SystemContext.SystemContextX64->R15
354 );
355 InternalPrintMessage (
356 "DS - %016lx, ES - %016lx, FS - %016lx\n",
357 SystemContext.SystemContextX64->Ds,
358 SystemContext.SystemContextX64->Es,
359 SystemContext.SystemContextX64->Fs
360 );
361 InternalPrintMessage (
362 "GS - %016lx, SS - %016lx\n",
363 SystemContext.SystemContextX64->Gs,
364 SystemContext.SystemContextX64->Ss
365 );
366 InternalPrintMessage (
367 "CR0 - %016lx, CR2 - %016lx, CR3 - %016lx\n",
368 SystemContext.SystemContextX64->Cr0,
369 SystemContext.SystemContextX64->Cr2,
370 SystemContext.SystemContextX64->Cr3
371 );
372 InternalPrintMessage (
373 "CR4 - %016lx, CR8 - %016lx\n",
374 SystemContext.SystemContextX64->Cr4,
375 SystemContext.SystemContextX64->Cr8
376 );
377 InternalPrintMessage (
378 "DR0 - %016lx, DR1 - %016lx, DR2 - %016lx\n",
379 SystemContext.SystemContextX64->Dr0,
380 SystemContext.SystemContextX64->Dr1,
381 SystemContext.SystemContextX64->Dr2
382 );
383 InternalPrintMessage (
384 "DR3 - %016lx, DR6 - %016lx, DR7 - %016lx\n",
385 SystemContext.SystemContextX64->Dr3,
386 SystemContext.SystemContextX64->Dr6,
387 SystemContext.SystemContextX64->Dr7
388 );
389 InternalPrintMessage (
390 "GDTR - %016lx %016lx, LDTR - %016lx\n",
391 SystemContext.SystemContextX64->Gdtr[0],
392 SystemContext.SystemContextX64->Gdtr[1],
393 SystemContext.SystemContextX64->Ldtr
394 );
395 InternalPrintMessage (
396 "IDTR - %016lx %016lx, TR - %016lx\n",
397 SystemContext.SystemContextX64->Idtr[0],
398 SystemContext.SystemContextX64->Idtr[1],
399 SystemContext.SystemContextX64->Tr
400 );
401 InternalPrintMessage (
402 "FXSAVE_STATE - %016lx\n",
403 &SystemContext.SystemContextX64->FxSaveState
404 );
405 }
406
407 /**
408 Display CPU information.
409
410 @param ExceptionType Exception type.
411 @param SystemContext Pointer to EFI_SYSTEM_CONTEXT.
412 **/
413 VOID
414 DumpImageAndCpuContent (
415 IN EFI_EXCEPTION_TYPE ExceptionType,
416 IN EFI_SYSTEM_CONTEXT SystemContext
417 )
418 {
419 DumpCpuContext (ExceptionType, SystemContext);
420 //
421 // Dump module image base and module entry point by RIP
422 //
423 if ((ExceptionType == EXCEPT_IA32_PAGE_FAULT) &&
424 ((SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_ID) != 0)) {
425 //
426 // The RIP in SystemContext could not be used
427 // if it is page fault with I/D set.
428 //
429 DumpModuleImageInfo ((*(UINTN *)(UINTN)SystemContext.SystemContextX64->Rsp));
430 } else {
431 DumpModuleImageInfo (SystemContext.SystemContextX64->Rip);
432 }
433 }