#------------------------------------------------------------------------------ #* #* Copyright (c) 2012 - 2013, 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. #* #* ExceptionHandlerAsm.S #* #* Abstract: #* #* IA32 CPU Exception Handler # #------------------------------------------------------------------------------ #.MMX #.XMM ASM_GLOBAL ASM_PFX(CommonExceptionHandler) ASM_GLOBAL ASM_PFX(CommonInterruptEntry) ASM_GLOBAL ASM_PFX(HookAfterStubEnd) #EXTRN ASM_PFX(mErrorCodeFlag):DWORD # Error code flags for exceptions #EXTRN ASM_PFX(mDoFarReturnFlag):DWORD # Do far return flag .text # # exception handler stub table # Exception0Handle: .byte 0x6a # push #VectorNum .byte 0 pushl %eax .byte 0xB8 .long ASM_PFX(CommonInterruptEntry) jmp *%eax Exception1Handle: .byte 0x6a # push #VectorNum .byte 1 pushl %eax .byte 0xB8 .long ASM_PFX(CommonInterruptEntry) jmp *%eax Exception2Handle: .byte 0x6a # push #VectorNum .byte 2 pushl %eax .byte 0xB8 .long ASM_PFX(CommonInterruptEntry) jmp *%eax Exception3Handle: .byte 0x6a # push #VectorNum .byte 3 pushl %eax .byte 0xB8 .long ASM_PFX(CommonInterruptEntry) jmp *%eax Exception4Handle: .byte 0x6a # push #VectorNum .byte 4 pushl %eax .byte 0xB8 .long ASM_PFX(CommonInterruptEntry) jmp *%eax Exception5Handle: .byte 0x6a # push #VectorNum .byte 5 pushl %eax .byte 0xB8 .long ASM_PFX(CommonInterruptEntry) jmp *%eax Exception6Handle: .byte 0x6a # push #VectorNum .byte 6 pushl %eax .byte 0xB8 .long ASM_PFX(CommonInterruptEntry) jmp *%eax Exception7Handle: .byte 0x6a # push #VectorNum .byte 7 pushl %eax .byte 0xB8 .long ASM_PFX(CommonInterruptEntry) jmp *%eax Exception8Handle: .byte 0x6a # push #VectorNum .byte 8 pushl %eax .byte 0xB8 .long ASM_PFX(CommonInterruptEntry) jmp *%eax Exception9Handle: .byte 0x6a # push #VectorNum .byte 9 pushl %eax .byte 0xB8 .long ASM_PFX(CommonInterruptEntry) jmp *%eax Exception10Handle: .byte 0x6a # push #VectorNum .byte 10 pushl %eax .byte 0xB8 .long ASM_PFX(CommonInterruptEntry) jmp *%eax Exception11Handle: .byte 0x6a # push #VectorNum .byte 11 pushl %eax .byte 0xB8 .long ASM_PFX(CommonInterruptEntry) jmp *%eax Exception12Handle: .byte 0x6a # push #VectorNum .byte 12 pushl %eax .byte 0xB8 .long ASM_PFX(CommonInterruptEntry) jmp *%eax Exception13Handle: .byte 0x6a # push #VectorNum .byte 13 pushl %eax .byte 0xB8 .long ASM_PFX(CommonInterruptEntry) jmp *%eax Exception14Handle: .byte 0x6a # push #VectorNum .byte 14 pushl %eax .byte 0xB8 .long ASM_PFX(CommonInterruptEntry) jmp *%eax Exception15Handle: .byte 0x6a # push #VectorNum .byte 15 pushl %eax .byte 0xB8 .long ASM_PFX(CommonInterruptEntry) jmp *%eax Exception16Handle: .byte 0x6a # push #VectorNum .byte 16 pushl %eax .byte 0xB8 .long ASM_PFX(CommonInterruptEntry) jmp *%eax Exception17Handle: .byte 0x6a # push #VectorNum .byte 17 pushl %eax .byte 0xB8 .long ASM_PFX(CommonInterruptEntry) jmp *%eax Exception18Handle: .byte 0x6a # push #VectorNum .byte 18 pushl %eax .byte 0xB8 .long ASM_PFX(CommonInterruptEntry) jmp *%eax Exception19Handle: .byte 0x6a # push #VectorNum .byte 19 pushl %eax .byte 0xB8 .long ASM_PFX(CommonInterruptEntry) jmp *%eax Exception20Handle: .byte 0x6a # push #VectorNum .byte 20 pushl %eax .byte 0xB8 .long ASM_PFX(CommonInterruptEntry) jmp *%eax Exception21Handle: .byte 0x6a # push #VectorNum .byte 21 pushl %eax .byte 0xB8 .long ASM_PFX(CommonInterruptEntry) jmp *%eax Exception22Handle: .byte 0x6a # push #VectorNum .byte 22 pushl %eax .byte 0xB8 .long ASM_PFX(CommonInterruptEntry) jmp *%eax Exception23Handle: .byte 0x6a # push #VectorNum .byte 23 pushl %eax .byte 0xB8 .long ASM_PFX(CommonInterruptEntry) jmp *%eax Exception24Handle: .byte 0x6a # push #VectorNum .byte 24 pushl %eax .byte 0xB8 .long ASM_PFX(CommonInterruptEntry) jmp *%eax Exception25Handle: .byte 0x6a # push #VectorNum .byte 25 pushl %eax .byte 0xB8 .long ASM_PFX(CommonInterruptEntry) jmp *%eax Exception26Handle: .byte 0x6a # push #VectorNum .byte 26 pushl %eax .byte 0xB8 .long ASM_PFX(CommonInterruptEntry) jmp *%eax Exception27Handle: .byte 0x6a # push #VectorNum .byte 27 pushl %eax .byte 0xB8 .long ASM_PFX(CommonInterruptEntry) jmp *%eax Exception28Handle: .byte 0x6a # push #VectorNum .byte 28 pushl %eax .byte 0xB8 .long ASM_PFX(CommonInterruptEntry) jmp *%eax Exception29Handle: .byte 0x6a # push #VectorNum .byte 29 pushl %eax .byte 0xB8 .long ASM_PFX(CommonInterruptEntry) jmp *%eax Exception30Handle: .byte 0x6a # push #VectorNum .byte 30 pushl %eax .byte 0xB8 .long ASM_PFX(CommonInterruptEntry) jmp *%eax Exception31Handle: .byte 0x6a # push #VectorNum .byte 31 pushl %eax .byte 0xB8 .long ASM_PFX(CommonInterruptEntry) jmp *%eax HookAfterStubBegin: .byte 0x6a # push VectorNum: .byte 0 # 0 will be fixed pushl %eax .byte 0xB8 # movl ASM_PFX(HookAfterStubHeaderEnd), %eax .long ASM_PFX(HookAfterStubHeaderEnd) jmp *%eax ASM_GLOBAL ASM_PFX(HookAfterStubHeaderEnd) HookAfterStubHeaderEnd: popl %eax subl $8, %esp # reserve room for filling exception data later pushl 8(%esp) xchgl (%esp), %ecx # get vector number bt %ecx, ASM_PFX(mErrorCodeFlag) jnc NoErrorData pushl (%esp) # addition push if exception data needed NoErrorData: xchg (%esp), %ecx # restore ecx pushl %eax #---------------------------------------; # CommonInterruptEntry ; #---------------------------------------; # The follow algorithm is used for the common interrupt routine. ASM_GLOBAL ASM_PFX(CommonInterruptEntry) ASM_PFX(CommonInterruptEntry): cli popl %eax # # All interrupt handlers are invoked through interrupt gates, so # IF flag automatically cleared at the entry point # # # Get vector number from top of stack # xchgl (%esp), %ecx andl $0x0FF, %ecx # Vector number should be less than 256 cmpl $32, %ecx # Intel reserved vector for exceptions? jae NoErrorCode bt %ecx, ASM_PFX(mErrorCodeFlag) jc HasErrorCode NoErrorCode: # # Stack: # +---------------------+ # + EFlags + # +---------------------+ # + CS + # +---------------------+ # + EIP + # +---------------------+ # + ECX + # +---------------------+ <-- ESP # # Registers: # ECX - Vector Number # # # Put Vector Number on stack # pushl %ecx # # Put 0 (dummy) error code on stack, and restore ECX # xorl %ecx, %ecx # ECX = 0 xchgl 4(%esp), %ecx jmp ErrorCodeAndVectorOnStack HasErrorCode: # # Stack: # +---------------------+ # + EFlags + # +---------------------+ # + CS + # +---------------------+ # + EIP + # +---------------------+ # + Error Code + # +---------------------+ # + ECX + # +---------------------+ <-- ESP # # Registers: # ECX - Vector Number # # # Put Vector Number on stack and restore ECX # xchgl (%esp), %ecx ErrorCodeAndVectorOnStack: pushl %ebp movl %esp, %ebp # # Stack: # +---------------------+ # + EFlags + # +---------------------+ # + CS + # +---------------------+ # + EIP + # +---------------------+ # + Error Code + # +---------------------+ # + Vector Number + # +---------------------+ # + EBP + # +---------------------+ <-- EBP # # # Align stack to make sure that EFI_FX_SAVE_STATE_IA32 of EFI_SYSTEM_CONTEXT_IA32 # is 16-byte aligned # andl $0x0fffffff0, %esp subl $12, %esp subl $8, %esp pushl $0 # check EXCEPTION_HANDLER_CONTEXT.OldIdtHandler pushl $0 # check EXCEPTION_HANDLER_CONTEXT.ExceptionDataFlag #; UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax; pushl %eax pushl %ecx pushl %edx pushl %ebx leal 24(%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 16(%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 12(%ebp), %eax pushl %eax #; UINT32 Gdtr[2], Idtr[2]; subl $8, %esp sidt (%esp) movl 2(%esp), %eax xchgl (%esp), %eax andl $0x0FFFF, %eax movl %eax, 4(%esp) subl $8, %esp sgdt (%esp) movl 2(%esp), %eax xchgl (%esp), %eax andl $0x0FFFF, %eax movl %eax, 4(%esp) #; UINT32 Ldtr, Tr; xorl %eax, %eax str %ax pushl %eax sldt %ax pushl %eax #; UINT32 EFlags; movl 20(%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, 0x0ae, 0x07 #fxsave [edi] #; UEFI calling convention for IA32 requires that Direction flag in EFLAGs is clear cld #; UINT32 ExceptionData; pushl 8(%ebp) #; Prepare parameter and call movl %esp, %edx pushl %edx movl 4(%ebp), %edx pushl %edx # # Call External Exception Handler # call ASM_PFX(CommonExceptionHandler) addl $8, %esp cli #; UINT32 ExceptionData; addl $4, %esp #; FX_SAVE_STATE_IA32 FxSaveState; movl %esp, %esi .byte 0x0f, 0x0ae, 0x0e # fxrstor [esi] addl $512, %esp #; UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; #; Skip restoration of DRx registers to support in-circuit emualators #; or debuggers set breakpoint in interrupt/exception context addl $24, %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 20(%ebp) #; UINT32 Ldtr, Tr; #; UINT32 Gdtr[2], Idtr[2]; #; Best not let anyone mess with these particular registers... addl $24, %esp #; UINT32 Eip; popl 12(%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 16(%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 popl -8(%ebp) popl -4(%ebp) movl %ebp, %esp popl %ebp addl $8, %esp cmpl $0, -16(%esp) # check EXCEPTION_HANDLER_CONTEXT.OldIdtHandler jz DoReturn cmpl $1, -20(%esp) # check EXCEPTION_HANDLER_CONTEXT.ExceptionDataFlag jz ErrorCode jmp *-16(%esp) ErrorCode: subl $4, %esp jmp *-12(%esp) DoReturn: cmpl $0, ASM_PFX(mDoFarReturnFlag) jz DoIret pushl 8(%esp) # save EFLAGS addl $16, %esp pushl -8(%esp) # save CS in new location pushl -8(%esp) # save EIP in new location pushl -8(%esp) # save EFLAGS in new location popfl # restore EFLAGS retf # far return DoIret: iretl #---------------------------------------; # _AsmGetTemplateAddressMap ; #---------------------------------------; # # Protocol prototype # AsmGetTemplateAddressMap ( # EXCEPTION_HANDLER_TEMPLATE_MAP *AddressMap # ); # # Routine Description: # # Return address map of interrupt handler template so that C code can generate # interrupt table. # # Arguments: # # # Returns: # # Nothing # # # Input: [ebp][0] = Original ebp # [ebp][4] = Return address # # Output: Nothing # # Destroys: Nothing #-----------------------------------------------------------------------------; #------------------------------------------------------------------------------------- # AsmGetAddressMap (&AddressMap); #------------------------------------------------------------------------------------- ASM_GLOBAL ASM_PFX(AsmGetTemplateAddressMap) ASM_PFX(AsmGetTemplateAddressMap): pushl %ebp movl %esp,%ebp pushal movl 0x8(%ebp), %ebx movl $Exception0Handle, (%ebx) movl $(Exception1Handle - Exception0Handle), 0x4(%ebx) movl $(HookAfterStubBegin), 0x8(%ebx) popal popl %ebp ret #------------------------------------------------------------------------------------- # AsmVectorNumFixup (*VectorBase, VectorNum); #------------------------------------------------------------------------------------- ASM_GLOBAL ASM_PFX(AsmVectorNumFixup) ASM_PFX(AsmVectorNumFixup): movl 8(%esp), %eax movl 4(%esp), %ecx movb %al, (VectorNum - HookAfterStubBegin)(%ecx) ret