;------------------------------------------------------------------------------ ; ; Copyright (c) 2017, Intel Corporation. All rights reserved.
; 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