| ;------------------------------------------------------------------------------ ; | |
| ; Copyright (c) 2017, Intel Corporation. All rights reserved.<BR> | |
| ; This program and the accompanying materials | |
| ; are licensed and made available under the terms and conditions of the BSD License | |
| ; which accompanies this distribution. The full text of the license may be found at | |
| ; http://opensource.org/licenses/bsd-license.php. | |
| ; | |
| ; THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| ; WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| ; | |
| ; Module Name: | |
| ; | |
| ; ExceptionTssEntryAsm.Asm | |
| ; | |
| ; Abstract: | |
| ; | |
| ; IA32 CPU Exception Handler with Separate Stack | |
| ; | |
| ; Notes: | |
| ; | |
| ;------------------------------------------------------------------------------ | |
| ; | |
| ; IA32 TSS Memory Layout Description | |
| ; | |
| struc IA32_TSS | |
| resw 1 | |
| resw 1 | |
| .ESP0: resd 1 | |
| .SS0: resw 1 | |
| resw 1 | |
| .ESP1: resd 1 | |
| .SS1: resw 1 | |
| resw 1 | |
| .ESP2: resd 1 | |
| .SS2: resw 1 | |
| resw 1 | |
| ._CR3: resd 1 | |
| .EIP: resd 1 | |
| .EFLAGS: resd 1 | |
| ._EAX: resd 1 | |
| ._ECX: resd 1 | |
| ._EDX: resd 1 | |
| ._EBX: resd 1 | |
| ._ESP: resd 1 | |
| ._EBP: resd 1 | |
| ._ESI: resd 1 | |
| ._EDI: resd 1 | |
| ._ES: resw 1 | |
| resw 1 | |
| ._CS: resw 1 | |
| resw 1 | |
| ._SS: resw 1 | |
| resw 1 | |
| ._DS: resw 1 | |
| resw 1 | |
| ._FS: resw 1 | |
| resw 1 | |
| ._GS: resw 1 | |
| resw 1 | |
| .LDT: resw 1 | |
| resw 1 | |
| resw 1 | |
| resw 1 | |
| endstruc | |
| ; | |
| ; CommonExceptionHandler() | |
| ; | |
| extern ASM_PFX(CommonExceptionHandler) | |
| SECTION .data | |
| SECTION .text | |
| ALIGN 8 | |
| ; | |
| ; Exception handler stub table | |
| ; | |
| AsmExceptionEntryBegin: | |
| %assign Vector 0 | |
| %rep 32 | |
| DoIret%[Vector]: | |
| iretd | |
| ASM_PFX(ExceptionTaskSwtichEntry%[Vector]): | |
| db 0x6a ; push #VectorNum | |
| db %[Vector] | |
| mov eax, ASM_PFX(CommonTaskSwtichEntryPoint) | |
| call eax | |
| mov esp, eax ; Restore stack top | |
| jmp DoIret%[Vector] | |
| %assign Vector Vector+1 | |
| %endrep | |
| AsmExceptionEntryEnd: | |
| ; | |
| ; Common part of exception handler | |
| ; | |
| global ASM_PFX(CommonTaskSwtichEntryPoint) | |
| ASM_PFX(CommonTaskSwtichEntryPoint): | |
| ; | |
| ; Stack: | |
| ; +---------------------+ <-- EBP - 8 | |
| ; + TSS Base + | |
| ; +---------------------+ <-- EBP - 4 | |
| ; + CPUID.EDX + | |
| ; +---------------------+ <-- EBP | |
| ; + EIP + | |
| ; +---------------------+ <-- EBP + 4 | |
| ; + Vector Number + | |
| ; +---------------------+ <-- EBP + 8 | |
| ; + Error Code + | |
| ; +---------------------+ | |
| ; | |
| mov ebp, esp ; Stack frame | |
| ; Use CPUID to determine if FXSAVE/FXRESTOR and DE are supported | |
| mov eax, 1 | |
| cpuid | |
| push edx | |
| ; Get TSS base of interrupted task through PreviousTaskLink field in | |
| ; current TSS base | |
| sub esp, 8 | |
| sgdt [esp + 2] | |
| mov eax, [esp + 4] ; GDT base | |
| add esp, 8 | |
| xor ebx, ebx | |
| str bx ; Current TR | |
| mov ecx, [eax + ebx + 2] | |
| shl ecx, 8 | |
| mov cl, [eax + ebx + 7] | |
| ror ecx, 8 ; ecx = Current TSS base | |
| push ecx ; keep it in stack for later use | |
| movzx ebx, word [ecx] ; Previous Task Link | |
| mov ecx, [eax + ebx + 2] | |
| shl ecx, 8 | |
| mov cl, [eax + ebx + 7] | |
| ror ecx, 8 ; ecx = Previous TSS base | |
| ; | |
| ; Align stack to make sure that EFI_FX_SAVE_STATE_IA32 of EFI_SYSTEM_CONTEXT_IA32 | |
| ; is 16-byte aligned | |
| ; | |
| and esp, 0xfffffff0 | |
| sub esp, 12 | |
| ;; UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax; | |
| push dword [ecx + IA32_TSS._EAX] | |
| push dword [ecx + IA32_TSS._ECX] | |
| push dword [ecx + IA32_TSS._EDX] | |
| push dword [ecx + IA32_TSS._EBX] | |
| push dword [ecx + IA32_TSS._ESP] | |
| push dword [ecx + IA32_TSS._EBP] | |
| push dword [ecx + IA32_TSS._ESI] | |
| push dword [ecx + IA32_TSS._EDI] | |
| ;; UINT32 Gs, Fs, Es, Ds, Cs, Ss; | |
| movzx eax, word [ecx + IA32_TSS._SS] | |
| push eax | |
| movzx eax, word [ecx + IA32_TSS._CS] | |
| push eax | |
| movzx eax, word [ecx + IA32_TSS._DS] | |
| push eax | |
| movzx eax, word [ecx + IA32_TSS._ES] | |
| push eax | |
| movzx eax, word [ecx + IA32_TSS._FS] | |
| push eax | |
| movzx eax, word [ecx + IA32_TSS._GS] | |
| push eax | |
| ;; UINT32 Eip; | |
| push dword [ecx + IA32_TSS.EIP] | |
| ;; UINT32 Gdtr[2], Idtr[2]; | |
| sub esp, 8 | |
| sidt [esp] | |
| mov eax, [esp + 2] | |
| xchg eax, [esp] | |
| and eax, 0xFFFF | |
| mov [esp+4], eax | |
| sub esp, 8 | |
| sgdt [esp] | |
| mov eax, [esp + 2] | |
| xchg eax, [esp] | |
| and eax, 0xFFFF | |
| mov [esp+4], eax | |
| ;; UINT32 Ldtr, Tr; | |
| mov eax, ebx ; ebx still keeps selector of interrupted task | |
| push eax | |
| movzx eax, word [ecx + IA32_TSS.LDT] | |
| push eax | |
| ;; UINT32 EFlags; | |
| push dword [ecx + IA32_TSS.EFLAGS] | |
| ;; UINT32 Cr0, Cr1, Cr2, Cr3, Cr4; | |
| mov eax, cr4 | |
| push eax ; push cr4 firstly | |
| mov edx, [ebp - 4] ; cpuid.edx | |
| test edx, BIT24 ; Test for FXSAVE/FXRESTOR support | |
| jz .1 | |
| or eax, BIT9 ; Set CR4.OSFXSR | |
| .1: | |
| test edx, BIT2 ; Test for Debugging Extensions support | |
| jz .2 | |
| or eax, BIT3 ; Set CR4.DE | |
| .2: | |
| mov cr4, eax | |
| mov eax, cr3 | |
| push eax | |
| mov eax, cr2 | |
| push eax | |
| xor eax, eax | |
| push eax | |
| mov eax, cr0 | |
| push eax | |
| ;; UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; | |
| mov eax, dr7 | |
| push eax | |
| mov eax, dr6 | |
| push eax | |
| mov eax, dr3 | |
| push eax | |
| mov eax, dr2 | |
| push eax | |
| mov eax, dr1 | |
| push eax | |
| mov eax, dr0 | |
| push eax | |
| ;; FX_SAVE_STATE_IA32 FxSaveState; | |
| ;; Clear TS bit in CR0 to avoid Device Not Available Exception (#NM) | |
| ;; when executing fxsave/fxrstor instruction | |
| test edx, BIT24 ; Test for FXSAVE/FXRESTOR support. | |
| ; edx still contains result from CPUID above | |
| jz .3 | |
| clts | |
| sub esp, 512 | |
| mov edi, esp | |
| db 0xf, 0xae, 0x7 ;fxsave [edi] | |
| .3: | |
| ;; UINT32 ExceptionData; | |
| push dword [ebp + 8] | |
| ;; UEFI calling convention for IA32 requires that Direction flag in EFLAGs is clear | |
| cld | |
| ;; call into exception handler | |
| mov esi, ecx ; Keep TSS base to avoid overwrite | |
| mov eax, ASM_PFX(CommonExceptionHandler) | |
| ;; Prepare parameter and call | |
| mov edx, esp | |
| push edx ; EFI_SYSTEM_CONTEXT | |
| push dword [ebp + 4] ; EFI_EXCEPTION_TYPE (vector number) | |
| ; | |
| ; Call External Exception Handler | |
| ; | |
| call eax | |
| add esp, 8 ; Restore stack before calling | |
| mov ecx, esi ; Restore TSS base | |
| ;; UINT32 ExceptionData; | |
| add esp, 4 | |
| ;; FX_SAVE_STATE_IA32 FxSaveState; | |
| mov edx, [ebp - 4] ; cpuid.edx | |
| test edx, BIT24 ; Test for FXSAVE/FXRESTOR support | |
| jz .4 | |
| mov esi, esp | |
| db 0xf, 0xae, 0xe ; fxrstor [esi] | |
| .4: | |
| add esp, 512 | |
| ;; UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; | |
| ;; Skip restoration of DRx registers to support debuggers | |
| ;; that set breakpoints in interrupt/exception context | |
| add esp, 4 * 6 | |
| ;; UINT32 Cr0, Cr1, Cr2, Cr3, Cr4; | |
| pop eax | |
| mov cr0, eax | |
| add esp, 4 ; not for Cr1 | |
| pop eax | |
| mov cr2, eax | |
| pop eax | |
| mov dword [ecx + IA32_TSS._CR3], eax | |
| pop eax | |
| mov cr4, eax | |
| ;; UINT32 EFlags; | |
| pop dword [ecx + IA32_TSS.EFLAGS] | |
| mov ebx, dword [ecx + IA32_TSS.EFLAGS] | |
| btr ebx, 9 ; Do 'cli' | |
| mov dword [ecx + IA32_TSS.EFLAGS], ebx | |
| ;; UINT32 Ldtr, Tr; | |
| ;; UINT32 Gdtr[2], Idtr[2]; | |
| ;; Best not let anyone mess with these particular registers... | |
| add esp, 24 | |
| ;; UINT32 Eip; | |
| pop dword [ecx + IA32_TSS.EIP] | |
| ;; UINT32 Gs, Fs, Es, Ds, Cs, Ss; | |
| ;; NOTE - modified segment registers could hang the debugger... We | |
| ;; could attempt to insulate ourselves against this possibility, | |
| ;; but that poses risks as well. | |
| ;; | |
| pop eax | |
| o16 mov [ecx + IA32_TSS._GS], ax | |
| pop eax | |
| o16 mov [ecx + IA32_TSS._FS], ax | |
| pop eax | |
| o16 mov [ecx + IA32_TSS._ES], ax | |
| pop eax | |
| o16 mov [ecx + IA32_TSS._DS], ax | |
| pop eax | |
| o16 mov [ecx + IA32_TSS._CS], ax | |
| pop eax | |
| o16 mov [ecx + IA32_TSS._SS], ax | |
| ;; UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax; | |
| pop dword [ecx + IA32_TSS._EDI] | |
| pop dword [ecx + IA32_TSS._ESI] | |
| add esp, 4 ; not for ebp | |
| add esp, 4 ; not for esp | |
| pop dword [ecx + IA32_TSS._EBX] | |
| pop dword [ecx + IA32_TSS._EDX] | |
| pop dword [ecx + IA32_TSS._ECX] | |
| pop dword [ecx + IA32_TSS._EAX] | |
| ; Set single step DB# to allow debugger to able to go back to the EIP | |
| ; where the exception is triggered. | |
| ;; Create return context for iretd in stub function | |
| mov eax, dword [ecx + IA32_TSS._ESP] ; Get old stack pointer | |
| mov ebx, dword [ecx + IA32_TSS.EIP] | |
| mov [eax - 0xc], ebx ; create EIP in old stack | |
| movzx ebx, word [ecx + IA32_TSS._CS] | |
| mov [eax - 0x8], ebx ; create CS in old stack | |
| mov ebx, dword [ecx + IA32_TSS.EFLAGS] | |
| bts ebx, 8 | |
| mov [eax - 0x4], ebx ; create eflags in old stack | |
| mov dword [ecx + IA32_TSS.EFLAGS], ebx ; update eflags in old TSS | |
| mov eax, dword [ecx + IA32_TSS._ESP] ; Get old stack pointer | |
| sub eax, 0xc ; minus 12 byte | |
| mov dword [ecx + IA32_TSS._ESP], eax ; Set new stack pointer | |
| ;; Replace the EIP of interrupted task with stub function | |
| mov eax, ASM_PFX(SingleStepStubFunction) | |
| mov dword [ecx + IA32_TSS.EIP], eax | |
| mov ecx, [ebp - 8] ; Get current TSS base | |
| mov eax, dword [ecx + IA32_TSS._ESP] ; Return current stack top | |
| mov esp, ebp | |
| ret | |
| global ASM_PFX(SingleStepStubFunction) | |
| ASM_PFX(SingleStepStubFunction): | |
| ; | |
| ; we need clean TS bit in CR0 to execute | |
| ; x87 FPU/MMX/SSE/SSE2/SSE3/SSSE3/SSE4 instructions. | |
| ; | |
| clts | |
| iretd | |
| global ASM_PFX(AsmGetTssTemplateMap) | |
| ASM_PFX(AsmGetTssTemplateMap): | |
| push ebp ; C prolog | |
| mov ebp, esp | |
| pushad | |
| mov ebx, dword [ebp + 0x8] | |
| mov dword [ebx], ASM_PFX(ExceptionTaskSwtichEntry0) | |
| mov dword [ebx + 0x4], (AsmExceptionEntryEnd - AsmExceptionEntryBegin) / 32 | |
| mov dword [ebx + 0x8], 0 | |
| popad | |
| pop ebp | |
| ret | |