#------------------------------------------------------------------------------ | |
# | |
# Copyright (c) 2009 - 2016, 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: | |
# | |
# SmiException.S | |
# | |
# Abstract: | |
# | |
# Exception handlers used in SM mode | |
# | |
#------------------------------------------------------------------------------ | |
ASM_GLOBAL ASM_PFX(SmiPFHandler) | |
ASM_GLOBAL ASM_PFX(PageFaultStubFunction) | |
ASM_GLOBAL ASM_PFX(gSmiMtrrs) | |
ASM_GLOBAL ASM_PFX(gcSmiIdtr) | |
ASM_GLOBAL ASM_PFX(gcSmiGdtr) | |
ASM_GLOBAL ASM_PFX(gTaskGateDescriptor) | |
ASM_GLOBAL ASM_PFX(gcPsd) | |
ASM_GLOBAL ASM_PFX(FeaturePcdGet (PcdCpuSmmProfileEnable)) | |
.data | |
NullSeg: .quad 0 # reserved by architecture | |
CodeSeg32: | |
.word -1 # LimitLow | |
.word 0 # BaseLow | |
.byte 0 # BaseMid | |
.byte 0x9b | |
.byte 0xcf # LimitHigh | |
.byte 0 # BaseHigh | |
ProtModeCodeSeg32: | |
.word -1 # LimitLow | |
.word 0 # BaseLow | |
.byte 0 # BaseMid | |
.byte 0x9b | |
.byte 0xcf # LimitHigh | |
.byte 0 # BaseHigh | |
ProtModeSsSeg32: | |
.word -1 # LimitLow | |
.word 0 # BaseLow | |
.byte 0 # BaseMid | |
.byte 0x93 | |
.byte 0xcf # LimitHigh | |
.byte 0 # BaseHigh | |
DataSeg32: | |
.word -1 # LimitLow | |
.word 0 # BaseLow | |
.byte 0 # BaseMid | |
.byte 0x93 | |
.byte 0xcf # LimitHigh | |
.byte 0 # BaseHigh | |
CodeSeg16: | |
.word -1 | |
.word 0 | |
.byte 0 | |
.byte 0x9b | |
.byte 0x8f | |
.byte 0 | |
DataSeg16: | |
.word -1 | |
.word 0 | |
.byte 0 | |
.byte 0x93 | |
.byte 0x8f | |
.byte 0 | |
CodeSeg64: | |
.word -1 # LimitLow | |
.word 0 # BaseLow | |
.byte 0 # BaseMid | |
.byte 0x9b | |
.byte 0xaf # LimitHigh | |
.byte 0 # BaseHigh | |
.equ GDT_SIZE, .- NullSeg | |
TssSeg: | |
.word TSS_DESC_SIZE -1 # LimitLow | |
.word 0 # BaseLow | |
.byte 0 # BaseMid | |
.byte 0x89 | |
.byte 0x00 # LimitHigh | |
.byte 0 # BaseHigh | |
ExceptionTssSeg: | |
.word TSS_DESC_SIZE - 1 # LimitLow | |
.word 0 # BaseLow | |
.byte 0 # BaseMid | |
.byte 0x89 | |
.byte 0x00 # LimitHigh | |
.byte 0 # BaseHigh | |
.equ CODE_SEL, CodeSeg32 - NullSeg | |
.equ DATA_SEL, DataSeg32 - NullSeg | |
.equ TSS_SEL, TssSeg - NullSeg | |
.equ EXCEPTION_TSS_SEL, ExceptionTssSeg - NullSeg | |
# IA32 TSS fields | |
.equ TSS_ESP0, 4 | |
.equ TSS_SS0, 8 | |
.equ TSS_ESP1, 12 | |
.equ TSS_SS1, 16 | |
.equ TSS_ESP2, 20 | |
.equ TSS_SS2, 24 | |
.equ TSS_CR3, 28 | |
.equ TSS_EIP, 32 | |
.equ TSS_EFLAGS, 36 | |
.equ TSS_EAX, 40 | |
.equ TSS_ECX, 44 | |
.equ TSS_EDX, 48 | |
.equ TSS_EBX, 52 | |
.equ TSS_ESP, 56 | |
.equ TSS_EBP, 60 | |
.equ TSS_ESI, 64 | |
.equ TSS_EDI, 68 | |
.equ TSS_ES, 72 | |
.equ TSS_CS, 76 | |
.equ TSS_SS, 80 | |
.equ TSS_DS, 84 | |
.equ TSS_FS, 88 | |
.equ TSS_GS, 92 | |
.equ TSS_LDT, 96 | |
# Create 2 TSS segments just after GDT | |
TssDescriptor: | |
.word 0 # PreviousTaskLink | |
.word 0 # Reserved | |
.long 0 # ESP0 | |
.word 0 # SS0 | |
.word 0 # Reserved | |
.long 0 # ESP1 | |
.word 0 # SS1 | |
.word 0 # Reserved | |
.long 0 # ESP2 | |
.word 0 # SS2 | |
.word 0 # Reserved | |
.long 0 # CR3 | |
.long 0 # EIP | |
.long 0 # EFLAGS | |
.long 0 # EAX | |
.long 0 # ECX | |
.long 0 # EDX | |
.long 0 # EBX | |
.long 0 # ESP | |
.long 0 # EBP | |
.long 0 # ESI | |
.long 0 # EDI | |
.word 0 # ES | |
.word 0 # Reserved | |
.word 0 # CS | |
.word 0 # Reserved | |
.word 0 # SS | |
.word 0 # Reserved | |
.word 0 # DS | |
.word 0 # Reserved | |
.word 0 # FS | |
.word 0 # Reserved | |
.word 0 # GS | |
.word 0 # Reserved | |
.word 0 # LDT Selector | |
.word 0 # Reserved | |
.word 0 # T | |
.word 0 # I/O Map Base | |
.equ TSS_DESC_SIZE, . - TssDescriptor | |
ExceptionTssDescriptor: | |
.word 0 # PreviousTaskLink | |
.word 0 # Reserved | |
.long 0 # ESP0 | |
.word 0 # SS0 | |
.word 0 # Reserved | |
.long 0 # ESP1 | |
.word 0 # SS1 | |
.word 0 # Reserved | |
.long 0 # ESP2 | |
.word 0 # SS2 | |
.word 0 # Reserved | |
.long 0 # CR3 | |
.long PFHandlerEntry # EIP | |
.long 00000002 # EFLAGS | |
.long 0 # EAX | |
.long 0 # ECX | |
.long 0 # EDX | |
.long 0 # EBX | |
.long 0 # ESP | |
.long 0 # EBP | |
.long 0 # ESI | |
.long 0 # EDI | |
.word DATA_SEL # ES | |
.word 0 # Reserved | |
.word CODE_SEL # CS | |
.word 0 # Reserved | |
.word DATA_SEL # SS | |
.word 0 # Reserved | |
.word DATA_SEL # DS | |
.word 0 # Reserved | |
.word DATA_SEL # FS | |
.word 0 # Reserved | |
.word DATA_SEL # GS | |
.word 0 # Reserved | |
.word 0 # LDT Selector | |
.word 0 # Reserved | |
.word 0 # T | |
.word 0 # I/O Map Base | |
ASM_PFX(gcPsd): | |
.ascii "PSDSIG " | |
.word PSD_SIZE | |
.word 2 | |
.word 1 << 2 | |
.word CODE_SEL | |
.word DATA_SEL | |
.word DATA_SEL | |
.word DATA_SEL | |
.word 0 | |
.long 0 | |
.long 0 | |
.long 0 | |
.long 0 | |
.quad 0 | |
.long NullSeg | |
.long 0 | |
.long GDT_SIZE | |
.long 0 | |
.space 24, 0 | |
.long ASM_PFX(gSmiMtrrs) | |
.long 0 | |
.equ PSD_SIZE, . - ASM_PFX(gcPsd) | |
ASM_PFX(gcSmiGdtr): .word GDT_SIZE - 1 | |
.long NullSeg | |
ASM_PFX(gcSmiIdtr): .word 0 | |
.long 0 | |
ASM_PFX(gTaskGateDescriptor): | |
.word 0 # Reserved | |
.word EXCEPTION_TSS_SEL # TSS Segment selector | |
.byte 0 # Reserved | |
.byte 0x85 # Task Gate, present, DPL = 0 | |
.word 0 # Reserved | |
.text | |
#------------------------------------------------------------------------------ | |
# PageFaultIdtHandlerSmmProfile is the entry point for all exceptions | |
# | |
# Stack: | |
#+---------------------+ | |
#+ EFlags + | |
#+---------------------+ | |
#+ CS + | |
#+---------------------+ | |
#+ EIP + | |
#+---------------------+ | |
#+ Error Code + | |
#+---------------------+ | |
#+ Vector Number + | |
#+---------------------+ | |
#+ EBP + | |
#+---------------------+ <-- EBP | |
# | |
# RSP set to odd multiple of 8 means ErrCode PRESENT | |
#------------------------------------------------------------------------------ | |
ASM_GLOBAL ASM_PFX(PageFaultIdtHandlerSmmProfile) | |
ASM_PFX(PageFaultIdtHandlerSmmProfile): | |
pushl $0x0e # Page Fault | |
pushl %ebp | |
movl %esp, %ebp | |
# | |
# Align stack to make sure that EFI_FX_SAVE_STATE_IA32 of EFI_SYSTEM_CONTEXT_IA32 | |
# is 16-byte aligned | |
# | |
andl $0xfffffff0, %esp | |
subl $12, %esp | |
## UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax; | |
pushl %eax | |
pushl %ecx | |
pushl %edx | |
pushl %ebx | |
leal (6*4)(%ebp), %ecx | |
pushl %ecx # ESP | |
pushl (%ebp) # EBP | |
pushl %esi | |
pushl %edi | |
## UINT32 Gs, Fs, Es, Ds, Cs, Ss; | |
movl %ss, %eax | |
pushl %eax | |
movzwl (4*4)(%ebp), %eax | |
pushl %eax | |
movl %ds, %eax | |
pushl %eax | |
movl %es, %eax | |
pushl %eax | |
movl %fs, %eax | |
pushl %eax | |
movl %gs, %eax | |
pushl %eax | |
## UINT32 Eip; | |
movl (3*4)(%ebp), %eax | |
pushl %eax | |
## UINT32 Gdtr[2], Idtr[2]; | |
subl $8, %esp | |
sidt (%esp) | |
movl 2(%esp), %eax | |
xchgl (%esp), %eax | |
andl $0xffff, %eax | |
movl %eax, 4(%esp) | |
subl $8, %esp | |
sgdt (%esp) | |
movl 2(%esp), %eax | |
xchgl (%esp), %eax | |
andl $0xffff, %eax | |
movl %eax, 4(%esp) | |
## UINT32 Ldtr, Tr; | |
xorl %eax, %eax | |
strw %ax | |
pushl %eax | |
sldtw %ax | |
pushl %eax | |
## UINT32 EFlags; | |
movl (5*4)(%ebp), %eax | |
pushl %eax | |
## UINT32 Cr0, Cr1, Cr2, Cr3, Cr4; | |
movl %cr4, %eax | |
orl $0x208, %eax | |
movl %eax, %cr4 | |
pushl %eax | |
movl %cr3, %eax | |
pushl %eax | |
movl %cr2, %eax | |
pushl %eax | |
xorl %eax, %eax | |
pushl %eax | |
movl %cr0, %eax | |
pushl %eax | |
## UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; | |
movl %dr7, %eax | |
pushl %eax | |
movl %dr6, %eax | |
pushl %eax | |
movl %dr3, %eax | |
pushl %eax | |
movl %dr2, %eax | |
pushl %eax | |
movl %dr1, %eax | |
pushl %eax | |
movl %dr0, %eax | |
pushl %eax | |
## FX_SAVE_STATE_IA32 FxSaveState; | |
subl $512, %esp | |
movl %esp, %edi | |
.byte 0x0f, 0xae, 0x07 #fxsave [edi] | |
# UEFI calling convention for IA32 requires that Direction flag in EFLAGs is clear | |
cld | |
## UINT32 ExceptionData; | |
pushl (2*4)(%ebp) | |
## call into exception handler | |
## Prepare parameter and call | |
movl %esp, %edx | |
pushl %edx | |
movl (1*4)(%ebp), %edx | |
pushl %edx | |
# | |
# Call External Exception Handler | |
# | |
movl $ASM_PFX(SmiPFHandler), %eax | |
call *%eax | |
addl $8, %esp | |
jmp L4 | |
L4: | |
## UINT32 ExceptionData; | |
addl $4, %esp | |
## FX_SAVE_STATE_IA32 FxSaveState; | |
movl %esp, %esi | |
.byte 0xf, 0xae, 0xe # fxrstor [esi] | |
addl $512, %esp | |
## UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; | |
## Skip restoration of DRx registers to support debuggers | |
## that set breakpoints in interrupt/exception context | |
addl $4*6, %esp | |
## UINT32 Cr0, Cr1, Cr2, Cr3, Cr4; | |
popl %eax | |
movl %eax, %cr0 | |
addl $4, %esp # not for Cr1 | |
popl %eax | |
movl %eax, %cr2 | |
popl %eax | |
movl %eax, %cr3 | |
popl %eax | |
movl %eax, %cr4 | |
## UINT32 EFlags; | |
popl (5*4)(%ebp) | |
## UINT32 Ldtr, Tr; | |
## UINT32 Gdtr[2], Idtr[2]; | |
## Best not let anyone mess with these particular registers... | |
addl $24, %esp | |
## UINT32 Eip; | |
popl (3*4)(%ebp) | |
## 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. | |
## | |
popl %gs | |
popl %fs | |
popl %es | |
popl %ds | |
popl (4*4)(%ebp) | |
popl %ss | |
## UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax; | |
popl %edi | |
popl %esi | |
addl $4, %esp # not for ebp | |
addl $4, %esp # not for esp | |
popl %ebx | |
popl %edx | |
popl %ecx | |
popl %eax | |
movl %ebp, %esp | |
popl %ebp | |
# Enable TF bit after page fault handler runs | |
btsl $8, 16(%esp) # EFLAGS | |
addl $8, %esp # skip INT# & ErrCode | |
Return: | |
iret | |
# | |
# Page Fault Exception Handler entry when SMM Stack Guard is enabled | |
# Executiot starts here after a task switch | |
# | |
PFHandlerEntry: | |
# | |
# Get this processor's TSS | |
# | |
subl $8, %esp | |
sgdt 2(%esp) | |
movl 4(%esp), %eax # GDT base | |
addl $8, %esp | |
movl (TSS_SEL+2)(%eax), %ecx | |
shll $8, %ecx | |
movb (TSS_SEL+7)(%eax), %cl | |
rorl $8, %ecx # ecx = TSS base | |
movl %esp, %ebp | |
# | |
# Align stack to make sure that EFI_FX_SAVE_STATE_IA32 of EFI_SYSTEM_CONTEXT_IA32 | |
# is 16-byte aligned | |
# | |
andl $0xfffffff0, %esp | |
subl $12, %esp | |
## UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax; | |
pushl TSS_EAX(%ecx) | |
pushl TSS_ECX(%ecx) | |
pushl TSS_EDX(%ecx) | |
pushl TSS_EBX(%ecx) | |
pushl TSS_ESP(%ecx) | |
pushl TSS_EBP(%ecx) | |
pushl TSS_ESI(%ecx) | |
pushl TSS_EDI(%ecx) | |
## UINT32 Gs, Fs, Es, Ds, Cs, Ss; | |
movzwl TSS_SS(%ecx), %eax | |
pushl %eax | |
movzwl TSS_CS(%ecx), %eax | |
pushl %eax | |
movzwl TSS_DS(%ecx), %eax | |
pushl %eax | |
movzwl TSS_ES(%ecx), %eax | |
pushl %eax | |
movzwl TSS_FS(%ecx), %eax | |
pushl %eax | |
movzwl TSS_GS(%ecx), %eax | |
pushl %eax | |
## UINT32 Eip; | |
pushl TSS_EIP(%ecx) | |
## UINT32 Gdtr[2], Idtr[2]; | |
subl $8, %esp | |
sidt (%esp) | |
movl 2(%esp), %eax | |
xchgl (%esp), %eax | |
andl $0xFFFF, %eax | |
movl %eax, 4(%esp) | |
subl $8, %esp | |
sgdt (%esp) | |
movl 2(%esp), %eax | |
xchgl (%esp), %eax | |
andl $0xFFFF, %eax | |
movl %eax, 4(%esp) | |
## UINT32 Ldtr, Tr; | |
movl $TSS_SEL, %eax | |
pushl %eax | |
movzwl TSS_LDT(%ecx), %eax | |
pushl %eax | |
## UINT32 EFlags; | |
pushl TSS_EFLAGS(%ecx) | |
## UINT32 Cr0, Cr1, Cr2, Cr3, Cr4; | |
movl %cr4, %eax | |
orl $0x208, %eax | |
movl %eax, %cr4 | |
pushl %eax | |
movl %cr3, %eax | |
pushl %eax | |
movl %cr2, %eax | |
pushl %eax | |
xorl %eax, %eax | |
pushl %eax | |
movl %cr0, %eax | |
pushl %eax | |
## UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; | |
movl %dr7, %eax | |
pushl %eax | |
movl %dr6, %eax | |
pushl %eax | |
movl %dr3, %eax | |
pushl %eax | |
movl %dr2, %eax | |
pushl %eax | |
movl %dr1, %eax | |
pushl %eax | |
movl %dr0, %eax | |
pushl %eax | |
## FX_SAVE_STATE_IA32 FxSaveState; | |
## Clear TS bit in CR0 to avoid Device Not Available Exception (#NM) | |
## when executing fxsave/fxrstor instruction | |
clts | |
subl $512, %esp | |
movl %esp, %edi | |
.byte 0x0f, 0xae, 0x07 #fxsave [edi] | |
# UEFI calling convention for IA32 requires that Direction flag in EFLAGs is clear | |
cld | |
## UINT32 ExceptionData; | |
pushl (%ebp) | |
## call into exception handler | |
movl %ecx, %ebx | |
movl $ASM_PFX(SmiPFHandler), %eax | |
## Prepare parameter and call | |
movl %esp, %edx | |
pushl %edx | |
movl $14, %edx | |
pushl %edx | |
# | |
# Call External Exception Handler | |
# | |
call *%eax | |
addl $8, %esp | |
movl %ebx, %ecx | |
## UINT32 ExceptionData; | |
addl $4, %esp | |
## FX_SAVE_STATE_IA32 FxSaveState; | |
movl %esp, %esi | |
.byte 0xf, 0xae, 0xe # fxrstor [esi] | |
addl $512, %esp | |
## UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; | |
## Skip restoration of DRx registers to support debuggers | |
## that set breakpoints in interrupt/exception context | |
addl $4*6, %esp | |
## UINT32 Cr0, Cr1, Cr2, Cr3, Cr4; | |
popl %eax | |
movl %eax, %cr0 | |
addl $4, %esp # not for Cr1 | |
popl %eax | |
movl %eax, %cr2 | |
popl %eax | |
movl %eax, TSS_CR3(%ecx) | |
popl %eax | |
movl %eax, %cr4 | |
## UINT32 EFlags; | |
popl TSS_EFLAGS(%ecx) | |
## UINT32 Ldtr, Tr; | |
## UINT32 Gdtr[2], Idtr[2]; | |
## Best not let anyone mess with these particular registers... | |
addl $24, %esp | |
## UINT32 Eip; | |
popl TSS_EIP(%ecx) | |
## 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. | |
## | |
popl %eax | |
movw %ax, TSS_GS(%ecx) | |
popl %eax | |
movw %ax, TSS_FS(%ecx) | |
popl %eax | |
movw %ax, TSS_ES(%ecx) | |
popl %eax | |
movw %ax, TSS_DS(%ecx) | |
popl %eax | |
movw %ax, TSS_CS(%ecx) | |
popl %eax | |
movw %ax, TSS_SS(%ecx) | |
## UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax; | |
popl TSS_EDI(%ecx) | |
popl TSS_ESI(%ecx) | |
addl $4, %esp # not for ebp | |
addl $4, %esp # not for esp | |
popl TSS_EBX(%ecx) | |
popl TSS_EDX(%ecx) | |
popl TSS_ECX(%ecx) | |
popl TSS_EAX(%ecx) | |
movl %ebp, %esp | |
# Set single step DB# if SMM profile is enabled and page fault exception happens | |
cmpb $0, ASM_PFX(FeaturePcdGet (PcdCpuSmmProfileEnable)) | |
jz Done2 | |
# Create return context for iret in stub function | |
movl TSS_ESP(%ecx), %eax # Get old stack pointer | |
movl TSS_EIP(%ecx), %ebx | |
movl %ebx, -0xc(%eax) # create EIP in old stack | |
movzwl TSS_CS(%ecx), %ebx | |
movl %ebx, -0x8(%eax) # create CS in old stack | |
movl TSS_EFLAGS(%ecx), %ebx | |
btsl $8,%ebx | |
movl %ebx, -0x4(%eax) # create eflags in old stack | |
movl TSS_ESP(%ecx), %eax # Get old stack pointer | |
subl $12, %eax # minus 12 byte | |
movl %eax, TSS_ESP(%ecx) # Set new stack pointer | |
# Replace the EIP of interrupted task with stub function | |
movl $ASM_PFX(PageFaultStubFunction), %eax | |
movl %eax, TSS_EIP(%ecx) | |
# Jump to the iret so next page fault handler as a task will start again after iret. | |
Done2: | |
addl $4, %esp # skip ErrCode | |
jmp Return | |
ASM_PFX(PageFaultStubFunction): | |
# | |
# we need clean TS bit in CR0 to execute | |
# x87 FPU/MMX/SSE/SSE2/SSE3/SSSE3/SSE4 instructions. | |
# | |
clts | |
iret |