;------------------------------------------------------------------------------ ; ; Copyright (c) 2009 - 2016, 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: ; ; SmiException.asm ; ; Abstract: ; ; Exception handlers used in SM mode ; ;------------------------------------------------------------------------------- .686p .model flat,C EXTERNDEF SmiPFHandler:PROC EXTERNDEF PageFaultStubFunction:PROC EXTERNDEF gSmiMtrrs:QWORD EXTERNDEF gcSmiIdtr:FWORD EXTERNDEF gcSmiGdtr:FWORD EXTERNDEF gTaskGateDescriptor:QWORD EXTERNDEF gcPsd:BYTE EXTERNDEF FeaturePcdGet (PcdCpuSmmProfileEnable):BYTE .data NullSeg DQ 0 ; reserved by architecture CodeSeg32 LABEL QWORD DW -1 ; LimitLow DW 0 ; BaseLow DB 0 ; BaseMid DB 9bh DB 0cfh ; LimitHigh DB 0 ; BaseHigh ProtModeCodeSeg32 LABEL QWORD DW -1 ; LimitLow DW 0 ; BaseLow DB 0 ; BaseMid DB 9bh DB 0cfh ; LimitHigh DB 0 ; BaseHigh ProtModeSsSeg32 LABEL QWORD DW -1 ; LimitLow DW 0 ; BaseLow DB 0 ; BaseMid DB 93h DB 0cfh ; LimitHigh DB 0 ; BaseHigh DataSeg32 LABEL QWORD DW -1 ; LimitLow DW 0 ; BaseLow DB 0 ; BaseMid DB 93h DB 0cfh ; LimitHigh DB 0 ; BaseHigh CodeSeg16 LABEL QWORD DW -1 DW 0 DB 0 DB 9bh DB 8fh DB 0 DataSeg16 LABEL QWORD DW -1 DW 0 DB 0 DB 93h DB 8fh DB 0 CodeSeg64 LABEL QWORD DW -1 ; LimitLow DW 0 ; BaseLow DB 0 ; BaseMid DB 9bh DB 0afh ; LimitHigh DB 0 ; BaseHigh GDT_SIZE = $ - offset NullSeg TssSeg LABEL QWORD DW TSS_DESC_SIZE - 1 ; LimitLow DW 0 ; BaseLow DB 0 ; BaseMid DB 89h DB 00h ; LimitHigh DB 0 ; BaseHigh ExceptionTssSeg LABEL QWORD DW TSS_DESC_SIZE - 1 ; LimitLow DW 0 ; BaseLow DB 0 ; BaseMid DB 89h DB 00h ; LimitHigh DB 0 ; BaseHigh CODE_SEL = offset CodeSeg32 - offset NullSeg DATA_SEL = offset DataSeg32 - offset NullSeg TSS_SEL = offset TssSeg - offset NullSeg EXCEPTION_TSS_SEL = offset ExceptionTssSeg - offset NullSeg IA32_TSS STRUC DW ? DW ? ESP0 DD ? SS0 DW ? DW ? ESP1 DD ? SS1 DW ? DW ? ESP2 DD ? SS2 DW ? DW ? _CR3 DD ? EIP DD ? EFLAGS DD ? _EAX DD ? _ECX DD ? _EDX DD ? _EBX DD ? _ESP DD ? _EBP DD ? _ESI DD ? _EDI DD ? _ES DW ? DW ? _CS DW ? DW ? _SS DW ? DW ? _DS DW ? DW ? _FS DW ? DW ? _GS DW ? DW ? LDT DW ? DW ? DW ? DW ? IA32_TSS ENDS ; Create 2 TSS segments just after GDT TssDescriptor LABEL BYTE DW 0 ; PreviousTaskLink DW 0 ; Reserved DD 0 ; ESP0 DW 0 ; SS0 DW 0 ; Reserved DD 0 ; ESP1 DW 0 ; SS1 DW 0 ; Reserved DD 0 ; ESP2 DW 0 ; SS2 DW 0 ; Reserved DD 0 ; CR3 DD 0 ; EIP DD 0 ; EFLAGS DD 0 ; EAX DD 0 ; ECX DD 0 ; EDX DD 0 ; EBX DD 0 ; ESP DD 0 ; EBP DD 0 ; ESI DD 0 ; EDI DW 0 ; ES DW 0 ; Reserved DW 0 ; CS DW 0 ; Reserved DW 0 ; SS DW 0 ; Reserved DW 0 ; DS DW 0 ; Reserved DW 0 ; FS DW 0 ; Reserved DW 0 ; GS DW 0 ; Reserved DW 0 ; LDT Selector DW 0 ; Reserved DW 0 ; T DW 0 ; I/O Map Base TSS_DESC_SIZE = $ - offset TssDescriptor ExceptionTssDescriptor LABEL BYTE DW 0 ; PreviousTaskLink DW 0 ; Reserved DD 0 ; ESP0 DW 0 ; SS0 DW 0 ; Reserved DD 0 ; ESP1 DW 0 ; SS1 DW 0 ; Reserved DD 0 ; ESP2 DW 0 ; SS2 DW 0 ; Reserved DD 0 ; CR3 DD offset PFHandlerEntry ; EIP DD 00000002 ; EFLAGS DD 0 ; EAX DD 0 ; ECX DD 0 ; EDX DD 0 ; EBX DD 0 ; ESP DD 0 ; EBP DD 0 ; ESI DD 0 ; EDI DW DATA_SEL ; ES DW 0 ; Reserved DW CODE_SEL ; CS DW 0 ; Reserved DW DATA_SEL ; SS DW 0 ; Reserved DW DATA_SEL ; DS DW 0 ; Reserved DW DATA_SEL ; FS DW 0 ; Reserved DW DATA_SEL ; GS DW 0 ; Reserved DW 0 ; LDT Selector DW 0 ; Reserved DW 0 ; T DW 0 ; I/O Map Base gcPsd LABEL BYTE DB 'PSDSIG ' DW PSD_SIZE DW 2 DW 1 SHL 2 DW CODE_SEL DW DATA_SEL DW DATA_SEL DW DATA_SEL DW 0 DQ 0 DQ 0 DQ 0 DQ offset NullSeg DD GDT_SIZE DD 0 DB 24 dup (0) DQ offset gSmiMtrrs PSD_SIZE = $ - offset gcPsd gcSmiGdtr LABEL FWORD DW GDT_SIZE - 1 DD offset NullSeg gcSmiIdtr LABEL FWORD DW 0 DD 0 gTaskGateDescriptor LABEL QWORD DW 0 ; Reserved DW EXCEPTION_TSS_SEL ; TSS Segment selector DB 0 ; Reserved DB 85h ; Task Gate, present, DPL = 0 DW 0 ; Reserved .code ;------------------------------------------------------------------------------ ; PageFaultIdtHandlerSmmProfile is the entry point page fault only ; ; ; Stack: ; +---------------------+ ; + EFlags + ; +---------------------+ ; + CS + ; +---------------------+ ; + EIP + ; +---------------------+ ; + Error Code + ; +---------------------+ ; + Vector Number + ; +---------------------+ ; + EBP + ; +---------------------+ <-- EBP ; ; ;------------------------------------------------------------------------------ PageFaultIdtHandlerSmmProfile PROC push 0eh ; Page Fault push ebp mov ebp, esp ; ; Align stack to make sure that EFI_FX_SAVE_STATE_IA32 of EFI_SYSTEM_CONTEXT_IA32 ; is 16-byte aligned ; and esp, 0fffffff0h sub esp, 12 ;; UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax; push eax push ecx push edx push ebx lea ecx, [ebp + 6 * 4] push ecx ; ESP push dword ptr [ebp] ; EBP push esi push edi ;; UINT32 Gs, Fs, Es, Ds, Cs, Ss; mov eax, ss push eax movzx eax, word ptr [ebp + 4 * 4] push eax mov eax, ds push eax mov eax, es push eax mov eax, fs push eax mov eax, gs push eax ;; UINT32 Eip; mov eax, [ebp + 3 * 4] push eax ;; UINT32 Gdtr[2], Idtr[2]; sub esp, 8 sidt [esp] mov eax, [esp + 2] xchg eax, [esp] and eax, 0FFFFh mov [esp+4], eax sub esp, 8 sgdt [esp] mov eax, [esp + 2] xchg eax, [esp] and eax, 0FFFFh mov [esp+4], eax ;; UINT32 Ldtr, Tr; xor eax, eax str ax push eax sldt ax push eax ;; UINT32 EFlags; mov eax, [ebp + 5 * 4] push eax ;; UINT32 Cr0, Cr1, Cr2, Cr3, Cr4; mov eax, cr4 or eax, 208h mov cr4, eax push 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; sub esp, 512 mov edi, esp db 0fh, 0aeh, 07h ;fxsave [edi] ; UEFI calling convention for IA32 requires that Direction flag in EFLAGs is clear cld ;; UINT32 ExceptionData; push dword ptr [ebp + 2 * 4] ;; call into exception handler ;; Prepare parameter and call mov edx, esp push edx mov edx, dword ptr [ebp + 1 * 4] push edx ; ; Call External Exception Handler ; mov eax, SmiPFHandler call eax add esp, 8 ;; UINT32 ExceptionData; add esp, 4 ;; FX_SAVE_STATE_IA32 FxSaveState; mov esi, esp db 0fh, 0aeh, 0eh ; fxrstor [esi] add esp, 512 ;; UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; ;; Skip restoration of DRx registers to support debuggers ;; that set breakpoint 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 cr3, eax pop eax mov cr4, eax ;; UINT32 EFlags; pop dword ptr [ebp + 5 * 4] ;; UINT32 Ldtr, Tr; ;; UINT32 Gdtr[2], Idtr[2]; ;; Best not let anyone mess with these particular registers... add esp, 24 ;; UINT32 Eip; pop dword ptr [ebp + 3 * 4] ;; 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 gs pop fs pop es pop ds pop dword ptr [ebp + 4 * 4] pop ss ;; UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax; pop edi pop esi add esp, 4 ; not for ebp add esp, 4 ; not for esp pop ebx pop edx pop ecx pop eax mov esp, ebp pop ebp ; Enable TF bit after page fault handler runs bts dword ptr [esp + 16], 8 ; EFLAGS add esp, 8 ; skip INT# & ErrCode Return: iretd ; ; Page Fault Exception Handler entry when SMM Stack Guard is enabled ; Executiot starts here after a task switch ; PFHandlerEntry:: ; ; Get this processor's TSS ; sub esp, 8 sgdt [esp + 2] mov eax, [esp + 4] ; GDT base add esp, 8 mov ecx, [eax + TSS_SEL + 2] shl ecx, 8 mov cl, [eax + TSS_SEL + 7] ror ecx, 8 ; ecx = TSS base mov ebp, esp ; ; Align stack to make sure that EFI_FX_SAVE_STATE_IA32 of EFI_SYSTEM_CONTEXT_IA32 ; is 16-byte aligned ; and esp, 0fffffff0h sub esp, 12 ;; UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax; push (IA32_TSS ptr [ecx])._EAX push (IA32_TSS ptr [ecx])._ECX push (IA32_TSS ptr [ecx])._EDX push (IA32_TSS ptr [ecx])._EBX push (IA32_TSS ptr [ecx])._ESP push (IA32_TSS ptr [ecx])._EBP push (IA32_TSS ptr [ecx])._ESI push (IA32_TSS ptr [ecx])._EDI ;; UINT32 Gs, Fs, Es, Ds, Cs, Ss; movzx eax, (IA32_TSS ptr [ecx])._SS push eax movzx eax, (IA32_TSS ptr [ecx])._CS push eax movzx eax, (IA32_TSS ptr [ecx])._DS push eax movzx eax, (IA32_TSS ptr [ecx])._ES push eax movzx eax, (IA32_TSS ptr [ecx])._FS push eax movzx eax, (IA32_TSS ptr [ecx])._GS push eax ;; UINT32 Eip; push (IA32_TSS ptr [ecx]).EIP ;; UINT32 Gdtr[2], Idtr[2]; sub esp, 8 sidt [esp] mov eax, [esp + 2] xchg eax, [esp] and eax, 0FFFFh mov [esp+4], eax sub esp, 8 sgdt [esp] mov eax, [esp + 2] xchg eax, [esp] and eax, 0FFFFh mov [esp+4], eax ;; UINT32 Ldtr, Tr; mov eax, TSS_SEL push eax movzx eax, (IA32_TSS ptr [ecx]).LDT push eax ;; UINT32 EFlags; push (IA32_TSS ptr [ecx]).EFLAGS ;; UINT32 Cr0, Cr1, Cr2, Cr3, Cr4; mov eax, cr4 or eax, 208h mov cr4, eax push 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 clts sub esp, 512 mov edi, esp db 0fh, 0aeh, 07h ;fxsave [edi] ; UEFI calling convention for IA32 requires that Direction flag in EFLAGs is clear cld ;; UINT32 ExceptionData; push dword ptr [ebp] ;; call into exception handler mov ebx, ecx mov eax, SmiPFHandler ;; Prepare parameter and call mov edx, esp push edx mov edx, 14 push edx ; ; Call External Exception Handler ; call eax add esp, 8 mov ecx, ebx ;; UINT32 ExceptionData; add esp, 4 ;; FX_SAVE_STATE_IA32 FxSaveState; mov esi, esp db 0fh, 0aeh, 0eh ; fxrstor [esi] 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 (IA32_TSS ptr [ecx])._CR3, eax pop eax mov cr4, eax ;; UINT32 EFlags; pop (IA32_TSS ptr [ecx]).EFLAGS ;; UINT32 Ldtr, Tr; ;; UINT32 Gdtr[2], Idtr[2]; ;; Best not let anyone mess with these particular registers... add esp, 24 ;; UINT32 Eip; pop (IA32_TSS ptr [ecx]).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 mov (IA32_TSS ptr [ecx])._GS, ax pop eax mov (IA32_TSS ptr [ecx])._FS, ax pop eax mov (IA32_TSS ptr [ecx])._ES, ax pop eax mov (IA32_TSS ptr [ecx])._DS, ax pop eax mov (IA32_TSS ptr [ecx])._CS, ax pop eax mov (IA32_TSS ptr [ecx])._SS, ax ;; UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax; pop (IA32_TSS ptr [ecx])._EDI pop (IA32_TSS ptr [ecx])._ESI add esp, 4 ; not for ebp add esp, 4 ; not for esp pop (IA32_TSS ptr [ecx])._EBX pop (IA32_TSS ptr [ecx])._EDX pop (IA32_TSS ptr [ecx])._ECX pop (IA32_TSS ptr [ecx])._EAX mov esp, ebp ; Set single step DB# if SMM profile is enabled and page fault exception happens cmp FeaturePcdGet (PcdCpuSmmProfileEnable), 0 jz @Done2 ; Create return context for iretd in stub function mov eax, (IA32_TSS ptr [ecx])._ESP ; Get old stack pointer mov ebx, (IA32_TSS ptr [ecx]).EIP mov [eax - 0ch], ebx ; create EIP in old stack movzx ebx, (IA32_TSS ptr [ecx])._CS mov [eax - 08h], ebx ; create CS in old stack mov ebx, (IA32_TSS ptr [ecx]).EFLAGS bts ebx, 8 mov [eax - 04h], ebx ; create eflags in old stack mov eax, (IA32_TSS ptr [ecx])._ESP ; Get old stack pointer sub eax, 0ch ; minus 12 byte mov (IA32_TSS ptr [ecx])._ESP, eax ; Set new stack pointer ; Replace the EIP of interrupted task with stub function mov eax, PageFaultStubFunction mov (IA32_TSS ptr [ecx]).EIP, eax ; Jump to the iretd so next page fault handler as a task will start again after iretd. @Done2: add esp, 4 ; skip ErrCode jmp Return PageFaultIdtHandlerSmmProfile ENDP PageFaultStubFunction PROC ; ; we need clean TS bit in CR0 to execute ; x87 FPU/MMX/SSE/SSE2/SSE3/SSSE3/SSE4 instructions. ; clts iretd PageFaultStubFunction ENDP END