]> git.proxmox.com Git - mirror_edk2.git/blame - UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ExceptionTssEntryAsm.nasm
UefiCpuPkg/CpuExceptionHandlerLib: Add stack switch support
[mirror_edk2.git] / UefiCpuPkg / Library / CpuExceptionHandlerLib / Ia32 / ExceptionTssEntryAsm.nasm
CommitLineData
0ff5aa9c
JW
1;------------------------------------------------------------------------------ ;\r
2; Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>\r
3; This program and the accompanying materials\r
4; are licensed and made available under the terms and conditions of the BSD License\r
5; which accompanies this distribution. The full text of the license may be found at\r
6; http://opensource.org/licenses/bsd-license.php.\r
7;\r
8; THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
9; WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
10;\r
11; Module Name:\r
12;\r
13; ExceptionTssEntryAsm.Asm\r
14;\r
15; Abstract:\r
16;\r
17; IA32 CPU Exception Handler with Separate Stack\r
18;\r
19; Notes:\r
20;\r
21;------------------------------------------------------------------------------\r
22\r
23;\r
24; IA32 TSS Memory Layout Description\r
25;\r
26struc IA32_TSS\r
27 resw 1\r
28 resw 1\r
29 .ESP0: resd 1\r
30 .SS0: resw 1\r
31 resw 1\r
32 .ESP1: resd 1\r
33 .SS1: resw 1\r
34 resw 1\r
35 .ESP2: resd 1\r
36 .SS2: resw 1\r
37 resw 1\r
38 ._CR3: resd 1\r
39 .EIP: resd 1\r
40 .EFLAGS: resd 1\r
41 ._EAX: resd 1\r
42 ._ECX: resd 1\r
43 ._EDX: resd 1\r
44 ._EBX: resd 1\r
45 ._ESP: resd 1\r
46 ._EBP: resd 1\r
47 ._ESI: resd 1\r
48 ._EDI: resd 1\r
49 ._ES: resw 1\r
50 resw 1\r
51 ._CS: resw 1\r
52 resw 1\r
53 ._SS: resw 1\r
54 resw 1\r
55 ._DS: resw 1\r
56 resw 1\r
57 ._FS: resw 1\r
58 resw 1\r
59 ._GS: resw 1\r
60 resw 1\r
61 .LDT: resw 1\r
62 resw 1\r
63 resw 1\r
64 resw 1\r
65endstruc\r
66\r
67;\r
68; CommonExceptionHandler()\r
69;\r
70extern ASM_PFX(CommonExceptionHandler)\r
71\r
72SECTION .data\r
73\r
74SECTION .text\r
75\r
76ALIGN 8\r
77\r
78;\r
79; Exception handler stub table\r
80;\r
81AsmExceptionEntryBegin:\r
82%assign Vector 0\r
83%rep 32\r
84\r
85DoIret%[Vector]:\r
86 iretd\r
87ASM_PFX(ExceptionTaskSwtichEntry%[Vector]):\r
88 db 0x6a ; push #VectorNum\r
89 db %[Vector]\r
90 mov eax, ASM_PFX(CommonTaskSwtichEntryPoint)\r
91 call eax\r
92 mov esp, eax ; Restore stack top\r
93 jmp DoIret%[Vector]\r
94\r
95%assign Vector Vector+1\r
96%endrep\r
97AsmExceptionEntryEnd:\r
98\r
99;\r
100; Common part of exception handler\r
101;\r
102global ASM_PFX(CommonTaskSwtichEntryPoint)\r
103ASM_PFX(CommonTaskSwtichEntryPoint):\r
104 ;\r
105 ; Stack:\r
106 ; +---------------------+ <-- EBP - 8\r
107 ; + TSS Base +\r
108 ; +---------------------+ <-- EBP - 4\r
109 ; + CPUID.EDX +\r
110 ; +---------------------+ <-- EBP\r
111 ; + EIP +\r
112 ; +---------------------+ <-- EBP + 4\r
113 ; + Vector Number +\r
114 ; +---------------------+ <-- EBP + 8\r
115 ; + Error Code +\r
116 ; +---------------------+\r
117 ;\r
118\r
119 mov ebp, esp ; Stack frame\r
120\r
121; Use CPUID to determine if FXSAVE/FXRESTOR and DE are supported\r
122 mov eax, 1\r
123 cpuid\r
124 push edx\r
125\r
126; Get TSS base of interrupted task through PreviousTaskLink field in\r
127; current TSS base\r
128 sub esp, 8\r
129 sgdt [esp + 2]\r
130 mov eax, [esp + 4] ; GDT base\r
131 add esp, 8\r
132\r
133 xor ebx, ebx\r
134 str bx ; Current TR\r
135\r
136 mov ecx, [eax + ebx + 2]\r
137 shl ecx, 8\r
138 mov cl, [eax + ebx + 7]\r
139 ror ecx, 8 ; ecx = Current TSS base\r
140 push ecx ; keep it in stack for later use\r
141\r
142 movzx ebx, word [ecx] ; Previous Task Link\r
143 mov ecx, [eax + ebx + 2]\r
144 shl ecx, 8\r
145 mov cl, [eax + ebx + 7]\r
146 ror ecx, 8 ; ecx = Previous TSS base\r
147\r
148;\r
149; Align stack to make sure that EFI_FX_SAVE_STATE_IA32 of EFI_SYSTEM_CONTEXT_IA32\r
150; is 16-byte aligned\r
151;\r
152 and esp, 0xfffffff0\r
153 sub esp, 12\r
154\r
155;; UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax;\r
156 push dword [ecx + IA32_TSS._EAX]\r
157 push dword [ecx + IA32_TSS._ECX]\r
158 push dword [ecx + IA32_TSS._EDX]\r
159 push dword [ecx + IA32_TSS._EBX]\r
160 push dword [ecx + IA32_TSS._ESP]\r
161 push dword [ecx + IA32_TSS._EBP]\r
162 push dword [ecx + IA32_TSS._ESI]\r
163 push dword [ecx + IA32_TSS._EDI]\r
164\r
165;; UINT32 Gs, Fs, Es, Ds, Cs, Ss;\r
166 movzx eax, word [ecx + IA32_TSS._SS]\r
167 push eax\r
168 movzx eax, word [ecx + IA32_TSS._CS]\r
169 push eax\r
170 movzx eax, word [ecx + IA32_TSS._DS]\r
171 push eax\r
172 movzx eax, word [ecx + IA32_TSS._ES]\r
173 push eax\r
174 movzx eax, word [ecx + IA32_TSS._FS]\r
175 push eax\r
176 movzx eax, word [ecx + IA32_TSS._GS]\r
177 push eax\r
178\r
179;; UINT32 Eip;\r
180 push dword [ecx + IA32_TSS.EIP]\r
181\r
182;; UINT32 Gdtr[2], Idtr[2];\r
183 sub esp, 8\r
184 sidt [esp]\r
185 mov eax, [esp + 2]\r
186 xchg eax, [esp]\r
187 and eax, 0xFFFF\r
188 mov [esp+4], eax\r
189\r
190 sub esp, 8\r
191 sgdt [esp]\r
192 mov eax, [esp + 2]\r
193 xchg eax, [esp]\r
194 and eax, 0xFFFF\r
195 mov [esp+4], eax\r
196\r
197;; UINT32 Ldtr, Tr;\r
198 mov eax, ebx ; ebx still keeps selector of interrupted task\r
199 push eax\r
200 movzx eax, word [ecx + IA32_TSS.LDT]\r
201 push eax\r
202\r
203;; UINT32 EFlags;\r
204 push dword [ecx + IA32_TSS.EFLAGS]\r
205\r
206;; UINT32 Cr0, Cr1, Cr2, Cr3, Cr4;\r
207 mov eax, cr4\r
208 push eax ; push cr4 firstly\r
209\r
210 mov edx, [ebp - 4] ; cpuid.edx\r
211 test edx, BIT24 ; Test for FXSAVE/FXRESTOR support\r
212 jz .1\r
213 or eax, BIT9 ; Set CR4.OSFXSR\r
214.1:\r
215 test edx, BIT2 ; Test for Debugging Extensions support\r
216 jz .2\r
217 or eax, BIT3 ; Set CR4.DE\r
218.2:\r
219 mov cr4, eax\r
220\r
221 mov eax, cr3\r
222 push eax\r
223 mov eax, cr2\r
224 push eax\r
225 xor eax, eax\r
226 push eax\r
227 mov eax, cr0\r
228 push eax\r
229\r
230;; UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7;\r
231 mov eax, dr7\r
232 push eax\r
233 mov eax, dr6\r
234 push eax\r
235 mov eax, dr3\r
236 push eax\r
237 mov eax, dr2\r
238 push eax\r
239 mov eax, dr1\r
240 push eax\r
241 mov eax, dr0\r
242 push eax\r
243\r
244;; FX_SAVE_STATE_IA32 FxSaveState;\r
245;; Clear TS bit in CR0 to avoid Device Not Available Exception (#NM)\r
246;; when executing fxsave/fxrstor instruction\r
247 test edx, BIT24 ; Test for FXSAVE/FXRESTOR support.\r
248 ; edx still contains result from CPUID above\r
249 jz .3\r
250 clts\r
251 sub esp, 512\r
252 mov edi, esp\r
253 db 0xf, 0xae, 0x7 ;fxsave [edi]\r
254.3:\r
255\r
256;; UINT32 ExceptionData;\r
257 push dword [ebp + 8]\r
258\r
259;; UEFI calling convention for IA32 requires that Direction flag in EFLAGs is clear\r
260 cld\r
261\r
262;; call into exception handler\r
263 mov esi, ecx ; Keep TSS base to avoid overwrite\r
264 mov eax, ASM_PFX(CommonExceptionHandler)\r
265\r
266;; Prepare parameter and call\r
267 mov edx, esp\r
268 push edx ; EFI_SYSTEM_CONTEXT\r
269 push dword [ebp + 4] ; EFI_EXCEPTION_TYPE (vector number)\r
270\r
271 ;\r
272 ; Call External Exception Handler\r
273 ;\r
274 call eax\r
275 add esp, 8 ; Restore stack before calling\r
276 mov ecx, esi ; Restore TSS base\r
277\r
278;; UINT32 ExceptionData;\r
279 add esp, 4\r
280\r
281;; FX_SAVE_STATE_IA32 FxSaveState;\r
282 mov edx, [ebp - 4] ; cpuid.edx\r
283 test edx, BIT24 ; Test for FXSAVE/FXRESTOR support\r
284 jz .4\r
285 mov esi, esp\r
286 db 0xf, 0xae, 0xe ; fxrstor [esi]\r
287.4:\r
288 add esp, 512\r
289\r
290;; UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7;\r
291;; Skip restoration of DRx registers to support debuggers\r
292;; that set breakpoints in interrupt/exception context\r
293 add esp, 4 * 6\r
294\r
295;; UINT32 Cr0, Cr1, Cr2, Cr3, Cr4;\r
296 pop eax\r
297 mov cr0, eax\r
298 add esp, 4 ; not for Cr1\r
299 pop eax\r
300 mov cr2, eax\r
301 pop eax\r
302 mov dword [ecx + IA32_TSS._CR3], eax\r
303 pop eax\r
304 mov cr4, eax\r
305\r
306;; UINT32 EFlags;\r
307 pop dword [ecx + IA32_TSS.EFLAGS]\r
308 mov ebx, dword [ecx + IA32_TSS.EFLAGS]\r
309 btr ebx, 9 ; Do 'cli'\r
310 mov dword [ecx + IA32_TSS.EFLAGS], ebx\r
311\r
312;; UINT32 Ldtr, Tr;\r
313;; UINT32 Gdtr[2], Idtr[2];\r
314;; Best not let anyone mess with these particular registers...\r
315 add esp, 24\r
316\r
317;; UINT32 Eip;\r
318 pop dword [ecx + IA32_TSS.EIP]\r
319\r
320;; UINT32 Gs, Fs, Es, Ds, Cs, Ss;\r
321;; NOTE - modified segment registers could hang the debugger... We\r
322;; could attempt to insulate ourselves against this possibility,\r
323;; but that poses risks as well.\r
324;;\r
325 pop eax\r
326o16 mov [ecx + IA32_TSS._GS], ax\r
327 pop eax\r
328o16 mov [ecx + IA32_TSS._FS], ax\r
329 pop eax\r
330o16 mov [ecx + IA32_TSS._ES], ax\r
331 pop eax\r
332o16 mov [ecx + IA32_TSS._DS], ax\r
333 pop eax\r
334o16 mov [ecx + IA32_TSS._CS], ax\r
335 pop eax\r
336o16 mov [ecx + IA32_TSS._SS], ax\r
337\r
338;; UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax;\r
339 pop dword [ecx + IA32_TSS._EDI]\r
340 pop dword [ecx + IA32_TSS._ESI]\r
341 add esp, 4 ; not for ebp\r
342 add esp, 4 ; not for esp\r
343 pop dword [ecx + IA32_TSS._EBX]\r
344 pop dword [ecx + IA32_TSS._EDX]\r
345 pop dword [ecx + IA32_TSS._ECX]\r
346 pop dword [ecx + IA32_TSS._EAX]\r
347\r
348; Set single step DB# to allow debugger to able to go back to the EIP\r
349; where the exception is triggered.\r
350\r
351;; Create return context for iretd in stub function\r
352 mov eax, dword [ecx + IA32_TSS._ESP] ; Get old stack pointer\r
353 mov ebx, dword [ecx + IA32_TSS.EIP]\r
354 mov [eax - 0xc], ebx ; create EIP in old stack\r
355 movzx ebx, word [ecx + IA32_TSS._CS]\r
356 mov [eax - 0x8], ebx ; create CS in old stack\r
357 mov ebx, dword [ecx + IA32_TSS.EFLAGS]\r
358 bts ebx, 8\r
359 mov [eax - 0x4], ebx ; create eflags in old stack\r
360 mov dword [ecx + IA32_TSS.EFLAGS], ebx ; update eflags in old TSS\r
361 mov eax, dword [ecx + IA32_TSS._ESP] ; Get old stack pointer\r
362 sub eax, 0xc ; minus 12 byte\r
363 mov dword [ecx + IA32_TSS._ESP], eax ; Set new stack pointer\r
364\r
365;; Replace the EIP of interrupted task with stub function\r
366 mov eax, ASM_PFX(SingleStepStubFunction)\r
367 mov dword [ecx + IA32_TSS.EIP], eax\r
368\r
369 mov ecx, [ebp - 8] ; Get current TSS base\r
370 mov eax, dword [ecx + IA32_TSS._ESP] ; Return current stack top\r
371 mov esp, ebp\r
372\r
373 ret\r
374\r
375global ASM_PFX(SingleStepStubFunction)\r
376ASM_PFX(SingleStepStubFunction):\r
377;\r
378; we need clean TS bit in CR0 to execute\r
379; x87 FPU/MMX/SSE/SSE2/SSE3/SSSE3/SSE4 instructions.\r
380;\r
381 clts\r
382 iretd\r
383\r
384global ASM_PFX(AsmGetTssTemplateMap)\r
385ASM_PFX(AsmGetTssTemplateMap):\r
386 push ebp ; C prolog\r
387 mov ebp, esp\r
388 pushad\r
389\r
390 mov ebx, dword [ebp + 0x8]\r
391 mov dword [ebx], ASM_PFX(ExceptionTaskSwtichEntry0)\r
392 mov dword [ebx + 0x4], (AsmExceptionEntryEnd - AsmExceptionEntryBegin) / 32\r
393 mov dword [ebx + 0x8], 0\r
394\r
395 popad\r
396 pop ebp\r
397 ret\r
398\r