;------------------------------------------------------------------------------ ; | |
; 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(FeaturePcdGet (PcdCpuSmmProfileEnable)) | |
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 | |