| ;------------------------------------------------------------------------------ ; | |
| ; Copyright (c) 2009 - 2019, Intel Corporation. All rights reserved.<BR> | |
| ; SPDX-License-Identifier: BSD-2-Clause-Patent | |
| ; | |
| ; Module Name: | |
| ; | |
| ; SmiException.nasm | |
| ; | |
| ; Abstract: | |
| ; | |
| ; Exception handlers used in SM mode | |
| ; | |
| ;------------------------------------------------------------------------------- | |
| extern ASM_PFX(SmiPFHandler) | |
| extern ASM_PFX(mSetupDebugTrap) | |
| global ASM_PFX(gcSmiIdtr) | |
| global ASM_PFX(gcSmiGdtr) | |
| global ASM_PFX(gTaskGateDescriptor) | |
| global ASM_PFX(gcPsd) | |
| SECTION .data | |
| NullSeg: DQ 0 ; reserved by architecture | |
| CodeSeg32: | |
| DW -1 ; LimitLow | |
| DW 0 ; BaseLow | |
| DB 0 ; BaseMid | |
| DB 0x9b | |
| DB 0xcf ; LimitHigh | |
| DB 0 ; BaseHigh | |
| ProtModeCodeSeg32: | |
| DW -1 ; LimitLow | |
| DW 0 ; BaseLow | |
| DB 0 ; BaseMid | |
| DB 0x9b | |
| DB 0xcf ; LimitHigh | |
| DB 0 ; BaseHigh | |
| ProtModeSsSeg32: | |
| DW -1 ; LimitLow | |
| DW 0 ; BaseLow | |
| DB 0 ; BaseMid | |
| DB 0x93 | |
| DB 0xcf ; LimitHigh | |
| DB 0 ; BaseHigh | |
| DataSeg32: | |
| DW -1 ; LimitLow | |
| DW 0 ; BaseLow | |
| DB 0 ; BaseMid | |
| DB 0x93 | |
| DB 0xcf ; LimitHigh | |
| DB 0 ; BaseHigh | |
| CodeSeg16: | |
| DW -1 | |
| DW 0 | |
| DB 0 | |
| DB 0x9b | |
| DB 0x8f | |
| DB 0 | |
| DataSeg16: | |
| DW -1 | |
| DW 0 | |
| DB 0 | |
| DB 0x93 | |
| DB 0x8f | |
| DB 0 | |
| CodeSeg64: | |
| DW -1 ; LimitLow | |
| DW 0 ; BaseLow | |
| DB 0 ; BaseMid | |
| DB 0x9b | |
| DB 0xaf ; LimitHigh | |
| DB 0 ; BaseHigh | |
| GDT_SIZE equ $ - NullSeg | |
| TssSeg: | |
| DW TSS_DESC_SIZE ; LimitLow | |
| DW 0 ; BaseLow | |
| DB 0 ; BaseMid | |
| DB 0x89 | |
| DB 0x80 ; LimitHigh | |
| DB 0 ; BaseHigh | |
| ExceptionTssSeg: | |
| DW EXCEPTION_TSS_DESC_SIZE ; LimitLow | |
| DW 0 ; BaseLow | |
| DB 0 ; BaseMid | |
| DB 0x89 | |
| DB 0x80 ; LimitHigh | |
| DB 0 ; BaseHigh | |
| CODE_SEL equ CodeSeg32 - NullSeg | |
| DATA_SEL equ DataSeg32 - NullSeg | |
| TSS_SEL equ TssSeg - NullSeg | |
| EXCEPTION_TSS_SEL equ ExceptionTssSeg - NullSeg | |
| 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 | |
| ; Create 2 TSS segments just after GDT | |
| TssDescriptor: | |
| 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 equ $ - TssDescriptor | |
| ExceptionTssDescriptor: | |
| 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 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 | |
| DD 0 ; SSP | |
| EXCEPTION_TSS_DESC_SIZE equ $ - ExceptionTssDescriptor | |
| ASM_PFX(gcPsd): | |
| DB 'PSDSIG ' | |
| DW PSD_SIZE | |
| DW 2 | |
| DW 1 << 2 | |
| DW CODE_SEL | |
| DW DATA_SEL | |
| DW DATA_SEL | |
| DW DATA_SEL | |
| DW 0 | |
| DQ 0 | |
| DQ 0 | |
| DQ 0 | |
| DD 0 | |
| DD NullSeg | |
| DD GDT_SIZE | |
| DD 0 | |
| times 24 DB 0 | |
| DD 0 | |
| DD 0 | |
| PSD_SIZE equ $ - ASM_PFX(gcPsd) | |
| ASM_PFX(gcSmiGdtr): | |
| DW GDT_SIZE - 1 | |
| DD NullSeg | |
| ASM_PFX(gcSmiIdtr): | |
| DW 0 | |
| DD 0 | |
| ASM_PFX(gTaskGateDescriptor): | |
| DW 0 ; Reserved | |
| DW EXCEPTION_TSS_SEL ; TSS Segment selector | |
| DB 0 ; Reserved | |
| DB 0x85 ; Task Gate, present, DPL = 0 | |
| DW 0 ; Reserved | |
| SECTION .text | |
| ;------------------------------------------------------------------------------ | |
| ; PageFaultIdtHandlerSmmProfile is the entry point page fault only | |
| ; | |
| ; | |
| ; Stack: | |
| ; +---------------------+ | |
| ; + EFlags + | |
| ; +---------------------+ | |
| ; + CS + | |
| ; +---------------------+ | |
| ; + EIP + | |
| ; +---------------------+ | |
| ; + Error Code + | |
| ; +---------------------+ | |
| ; + Vector Number + | |
| ; +---------------------+ | |
| ; + EBP + | |
| ; +---------------------+ <-- EBP | |
| ; | |
| ; | |
| ;------------------------------------------------------------------------------ | |
| global ASM_PFX(PageFaultIdtHandlerSmmProfile) | |
| ASM_PFX(PageFaultIdtHandlerSmmProfile): | |
| push 0xe ; 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, 0xfffffff0 | |
| 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 [ebp] ; EBP | |
| push esi | |
| push edi | |
| ;; UINT32 Gs, Fs, Es, Ds, Cs, Ss; | |
| mov eax, ss | |
| push eax | |
| movzx eax, word [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, 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; | |
| 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, 0x208 | |
| 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 | |
| fxsave [edi] | |
| ; UEFI calling convention for IA32 requires that Direction flag in EFLAGs is clear | |
| cld | |
| ;; UINT32 ExceptionData; | |
| push dword [ebp + 2 * 4] | |
| ;; call into exception handler | |
| ;; Prepare parameter and call | |
| mov edx, esp | |
| push edx | |
| mov edx, dword [ebp + 1 * 4] | |
| push edx | |
| ; | |
| ; Call External Exception Handler | |
| ; | |
| mov eax, ASM_PFX(SmiPFHandler) | |
| call eax | |
| add esp, 8 | |
| ;; UINT32 ExceptionData; | |
| add esp, 4 | |
| ;; FX_SAVE_STATE_IA32 FxSaveState; | |
| mov esi, esp | |
| 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 [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 [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 [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 [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, 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, TSS_SEL | |
| 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 | |
| or eax, 0x208 | |
| 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 | |
| fxsave [edi] | |
| ; UEFI calling convention for IA32 requires that Direction flag in EFLAGs is clear | |
| cld | |
| ;; UINT32 ExceptionData; | |
| push dword [ebp] | |
| ;; call into exception handler | |
| mov ebx, ecx | |
| mov eax, ASM_PFX(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 | |
| 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 dword [ecx + IA32_TSS._CR3], eax | |
| pop eax | |
| mov cr4, eax | |
| ;; UINT32 EFlags; | |
| pop dword [ecx + IA32_TSS.EFLAGS] | |
| ;; 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] | |
| mov esp, ebp | |
| ; Set single step DB# if SMM profile is enabled and page fault exception happens | |
| cmp byte [dword ASM_PFX(mSetupDebugTrap)], 0 | |
| jz @Done2 | |
| ; 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 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(PageFaultStubFunction) | |
| mov dword [ecx + IA32_TSS.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 | |
| global ASM_PFX(PageFaultStubFunction) | |
| ASM_PFX(PageFaultStubFunction): | |
| ; | |
| ; we need clean TS bit in CR0 to execute | |
| ; x87 FPU/MMX/SSE/SSE2/SSE3/SSSE3/SSE4 instructions. | |
| ; | |
| clts | |
| iretd | |