| #/**@file | |
| # Low leve IA32 specific debug support functions. | |
| # | |
| # Copyright (c) 2006 - 2011, Intel Corporation. <BR> | |
| # 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. | |
| # | |
| #**/ | |
| ASM_GLOBAL ASM_PFX(OrigVector) | |
| ASM_GLOBAL ASM_PFX(InterruptEntryStub) | |
| ASM_GLOBAL ASM_PFX(StubSize) | |
| ASM_GLOBAL ASM_PFX(CommonIdtEntry) | |
| ASM_GLOBAL ASM_PFX(FxStorSupport) | |
| ASM_PFX(StubSize): .long ASM_PFX(InterruptEntryStubEnd) - ASM_PFX(InterruptEntryStub) | |
| ASM_PFX(AppEsp): .long 0x11111111 # ? | |
| ASM_PFX(DebugEsp): .long 0x22222222 # ? | |
| ASM_PFX(ExtraPush): .long 0x33333333 # ? | |
| ASM_PFX(ExceptData): .long 0x44444444 # ? | |
| ASM_PFX(Eflags): .long 0x55555555 # ? | |
| ASM_PFX(OrigVector): .long 0x66666666 # ? | |
| #------------------------------------------------------------------------------ | |
| # BOOLEAN | |
| # FxStorSupport ( | |
| # void | |
| # ) | |
| # | |
| # Abstract: Returns TRUE if FxStor instructions are supported | |
| # | |
| ASM_GLOBAL ASM_PFX(FxStorSupport) | |
| ASM_PFX(FxStorSupport): | |
| # | |
| # cpuid corrupts ebx which must be preserved per the C calling convention | |
| # | |
| push %ebx | |
| mov $0x1,%eax | |
| cpuid | |
| mov %edx,%eax | |
| and $0x1000000,%eax | |
| shr $0x18,%eax | |
| pop %ebx | |
| ret | |
| #------------------------------------------------------------------------------ | |
| # void | |
| # Vect2Desc ( | |
| # DESCRIPTOR * DestDesc, | |
| # void (*Vector) (void) | |
| # ) | |
| # | |
| # Abstract: Encodes an IDT descriptor with the given physical address | |
| # | |
| ASM_GLOBAL ASM_PFX(Vect2Desc) | |
| ASM_PFX(Vect2Desc): | |
| push %ebp | |
| mov %esp,%ebp | |
| mov 0xc(%ebp),%eax | |
| mov 0x8(%ebp),%ecx | |
| mov %ax,(%ecx) | |
| movw $0x20,0x2(%ecx) | |
| movw $0x8e00,0x4(%ecx) | |
| shr $0x10,%eax | |
| mov %ax,0x6(%ecx) | |
| leave | |
| ret | |
| ASM_GLOBAL ASM_PFX(InterruptEntryStub) | |
| ASM_PFX(InterruptEntryStub): | |
| mov %esp,0x0 # save stack top | |
| mov $0x0,%esp # switch to debugger stack | |
| push $0x0 # push vector number - will be modified before installed | |
| jmp ASM_PFX(CommonIdtEntry) # jump CommonIdtEntry | |
| ASM_GLOBAL ASM_PFX(InterruptEntryStubEnd) | |
| ASM_PFX(InterruptEntryStubEnd): | |
| #------------------------------------------------------------------------------ | |
| # CommonIdtEntry | |
| # | |
| # Abstract: This code is not a function, but is the common part for all IDT | |
| # vectors. | |
| # | |
| ASM_GLOBAL ASM_PFX(CommonIdtEntry) | |
| ASM_PFX(CommonIdtEntry): | |
| ## | |
| ## At this point, the stub has saved the current application stack esp into AppEsp | |
| ## and switched stacks to the debug stack, where it pushed the vector number | |
| ## | |
| ## The application stack looks like this: | |
| ## | |
| ## ... | |
| ## (last application stack entry) | |
| ## eflags from interrupted task | |
| ## CS from interrupted task | |
| ## EIP from interrupted task | |
| ## Error code <-------------------- Only present for some exeption types | |
| ## | |
| ## | |
| ## The stub switched us to the debug stack and pushed the interrupt number. | |
| ## | |
| ## Next, construct the context record. It will be build on the debug stack by | |
| ## pushing the registers in the correct order so as to create the context structure | |
| ## on the debug stack. The context record must be built from the end back to the | |
| ## beginning because the stack grows down... | |
| # | |
| ## For reference, the context record looks like this: | |
| ## | |
| ## typedef | |
| ## struct { | |
| ## UINT32 ExceptionData; | |
| ## FX_SAVE_STATE_IA32 FxSaveState; | |
| ## UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; | |
| ## UINT32 Cr0, Cr2, Cr3, Cr4; | |
| ## UINT32 EFlags; | |
| ## UINT32 Ldtr, Tr; | |
| ## UINT32 Gdtr[2], Idtr[2]; | |
| ## UINT32 Eip; | |
| ## UINT32 Gs, Fs, Es, Ds, Cs, Ss; | |
| ## UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax; | |
| ## } SYSTEM_CONTEXT_IA32; // 32 bit system context record | |
| ## UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax; | |
| pusha | |
| ## Save interrupt state eflags register... | |
| pushf | |
| pop %eax | |
| ## We need to determine if any extra data was pushed by the exception, and if so, save it | |
| ## To do this, we check the exception number pushed by the stub, and cache the | |
| ## result in a variable since we'll need this again. | |
| mov %eax,0x0 | |
| cmpl $0x8,0x0 | |
| jne ASM_PFX(CommonIdtEntry+0x20) | |
| movl $0x1,0x0 | |
| jmp ASM_PFX(CommonIdtEntry+0xa8) | |
| cmpl $0xa,0x0 | |
| jne ASM_PFX(CommonIdtEntry+0x35) | |
| movl $0x1,0x0 | |
| jmp ASM_PFX(CommonIdtEntry+0xa8) | |
| cmpl $0xb,0x0 | |
| jne ASM_PFX(CommonIdtEntry+0x4a) | |
| movl $0x1,0x0 | |
| jmp ASM_PFX(CommonIdtEntry+0xa8) | |
| cmpl $0xc,0x0 | |
| jne ASM_PFX(CommonIdtEntry+0x5f) | |
| movl $0x1,0x0 | |
| jmp ASM_PFX(CommonIdtEntry+0xa8) | |
| cmpl $0xd,0x0 | |
| jne ASM_PFX(CommonIdtEntry+0x74) | |
| movl $0x1,0x0 | |
| jmp ASM_PFX(CommonIdtEntry+0xa8) | |
| cmpl $0xe,0x0 | |
| jne ASM_PFX(CommonIdtEntry+0x89) | |
| movl $0x1,0x0 | |
| jmp ASM_PFX(CommonIdtEntry+0xa8) | |
| cmpl $0x11,0x0 | |
| jne ASM_PFX(CommonIdtEntry+0x9e) | |
| movl $0x1,0x0 | |
| jmp ASM_PFX(CommonIdtEntry+0xa8) | |
| movl $0x0,0x0 | |
| ## If there's some extra data, save it also, and modify the saved AppEsp to effectively | |
| ## pop this value off the application's stack. | |
| cmpl $0x1,0x0 | |
| jne ASM_PFX(CommonIdtEntry+0xc8) | |
| mov 0x0,%eax | |
| mov (%eax),%ebx | |
| mov %ebx,0x0 | |
| add $0x4,%eax | |
| mov %eax,0x0 | |
| jmp ASM_PFX(CommonIdtEntry+0xd2) | |
| movl $0x0,0x0 | |
| ## The "pushad" above pushed the debug stack esp. Since what we're actually doing | |
| ## is building the context record on the debug stack, we need to save the pushed | |
| ## debug ESP, and replace it with the application's last stack entry... | |
| mov 0xc(%esp),%eax | |
| mov %eax,0x0 | |
| mov 0x0,%eax | |
| add $0xc,%eax | |
| # application stack has eflags, cs, & eip, so | |
| # last actual application stack entry is | |
| # 12 bytes into the application stack. | |
| mov %eax,0xc(%esp) | |
| ## continue building context record | |
| ## UINT32 Gs, Fs, Es, Ds, Cs, Ss; insure high 16 bits of each is zero | |
| mov %ss,%eax | |
| push %eax | |
| # CS from application is one entry back in application stack | |
| mov 0x0,%eax | |
| movzwl 0x4(%eax),%eax | |
| push %eax | |
| mov %ds,%eax | |
| push %eax | |
| mov %es,%eax | |
| push %eax | |
| mov %fs,%eax | |
| push %eax | |
| mov %gs,%eax | |
| push %eax | |
| ## UINT32 Eip; | |
| # Eip from application is on top of application stack | |
| mov 0x0,%eax | |
| pushl (%eax) | |
| ## UINT32 Gdtr[2], Idtr[2]; | |
| push $0x0 | |
| push $0x0 | |
| sidtl (%esp) | |
| push $0x0 | |
| push $0x0 | |
| sgdtl (%esp) | |
| ## UINT32 Ldtr, Tr; | |
| xor %eax,%eax | |
| str %eax | |
| push %eax | |
| sldt %eax | |
| push %eax | |
| ## UINT32 EFlags; | |
| ## Eflags from application is two entries back in application stack | |
| mov 0x0,%eax | |
| pushl 0x8(%eax) | |
| ## UINT32 Cr0, Cr1, Cr2, Cr3, Cr4; | |
| ## insure FXSAVE/FXRSTOR is enabled in CR4... | |
| ## ... while we're at it, make sure DE is also enabled... | |
| mov %cr4,%eax | |
| or $0x208,%eax | |
| mov %eax,%cr4 | |
| push %eax | |
| mov %cr3,%eax | |
| push %eax | |
| mov %cr2,%eax | |
| push %eax | |
| push $0x0 | |
| mov %cr0,%eax | |
| push %eax | |
| ## UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; | |
| mov %db7,%eax | |
| push %eax | |
| ## clear Dr7 while executing debugger itself | |
| xor %eax,%eax | |
| mov %eax,%db7 | |
| mov %db6,%eax | |
| push %eax | |
| ## insure all status bits in dr6 are clear... | |
| xor %eax,%eax | |
| mov %eax,%db6 | |
| mov %db3,%eax | |
| push %eax | |
| mov %db2,%eax | |
| push %eax | |
| mov %db1,%eax | |
| push %eax | |
| mov %db0,%eax | |
| push %eax | |
| ## FX_SAVE_STATE_IA32 FxSaveState; | |
| sub $0x200,%esp | |
| mov %esp,%edi | |
| # IMPORTANT!! The debug stack has been carefully constructed to | |
| # insure that esp and edi are 16 byte aligned when we get here. | |
| # They MUST be. If they are not, a GP fault will occur. | |
| fxsave (%edi) | |
| ## UEFI calling convention for IA32 requires that Direction flag in EFLAGs is clear | |
| cld | |
| ## UINT32 ExceptionData; | |
| mov 0x0,%eax | |
| push %eax | |
| # call to C code which will in turn call registered handler | |
| # pass in the vector number | |
| mov %esp,%eax | |
| push %eax | |
| mov 0x0,%eax | |
| push %eax | |
| call ASM_PFX(CommonIdtEntry+0x184) | |
| add $0x8,%esp | |
| # restore context... | |
| ## UINT32 ExceptionData; | |
| add $0x4,%esp | |
| ## FX_SAVE_STATE_IA32 FxSaveState; | |
| mov %esp,%esi | |
| fxrstor (%esi) | |
| add $0x200,%esp | |
| ## UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; | |
| pop %eax | |
| mov %eax,%db0 | |
| pop %eax | |
| mov %eax,%db1 | |
| pop %eax | |
| mov %eax,%db2 | |
| pop %eax | |
| mov %eax,%db3 | |
| ## skip restore of dr6. We cleared dr6 during the context save. | |
| add $0x4,%esp | |
| pop %eax | |
| mov %eax,%db7 | |
| ## UINT32 Cr0, Cr1, Cr2, Cr3, Cr4; | |
| pop %eax | |
| mov %eax,%cr0 | |
| add $0x4,%esp | |
| pop %eax | |
| mov %eax,%cr2 | |
| pop %eax | |
| mov %eax,%cr3 | |
| pop %eax | |
| mov %eax,%cr4 | |
| ## UINT32 EFlags; | |
| mov 0x0,%eax | |
| popl 0x8(%eax) | |
| ## UINT32 Ldtr, Tr; | |
| ## UINT32 Gdtr[2], Idtr[2]; | |
| ## Best not let anyone mess with these particular registers... | |
| add $0x18,%esp | |
| ## UINT32 Eip; | |
| popl (%eax) | |
| ## UINT32 SegGs, SegFs, SegEs, SegDs, SegCs, SegSs; | |
| ## 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 | |
| popl 0x4(%eax) | |
| pop %ss | |
| mov 0xc(%esp),%ebx | |
| ## The next stuff to restore is the general purpose registers that were pushed | |
| ## using the "pushad" instruction. | |
| ## | |
| ## The value of ESP as stored in the context record is the application ESP | |
| ## including the 3 entries on the application stack caused by the exception | |
| ## itself. It may have been modified by the debug agent, so we need to | |
| ## determine if we need to relocate the application stack. | |
| mov 0x0,%eax # move the potentially modified AppEsp into ebx | |
| add $0xc,%eax | |
| cmp %eax,%ebx | |
| je ASM_PFX(CommonIdtEntry+0x202) | |
| mov 0x0,%eax | |
| mov (%eax),%ecx # EIP | |
| mov %ecx,(%ebx) | |
| mov 0x4(%eax),%ecx # CS | |
| mov %ecx,0x4(%ebx) | |
| mov 0x8(%eax),%ecx # EFLAGS | |
| mov %ecx,0x8(%ebx) | |
| mov %ebx,%eax # modify the saved AppEsp to the new AppEsp | |
| mov %eax,0x0 | |
| mov 0x0,%eax # restore the DebugEsp on the debug stack | |
| # so our "popad" will not cause a stack switch | |
| mov %eax,0xc(%esp) | |
| cmpl $0x68,0x0 | |
| jne PhonyIretd+0xd | |
| ## Restore eflags so when we chain, the flags will be exactly as if we were never here. | |
| ## We gin up the stack to do an iretd so we can get ALL the flags. | |
| mov 0x0,%eax | |
| mov 0x8(%eax),%ebx | |
| and $0xfffffcff,%ebx # special handling for IF and TF | |
| push %ebx | |
| push %cs | |
| push $0x0 | |
| iret | |
| PhonyIretd: | |
| ## UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax; | |
| popa | |
| ## Switch back to application stack | |
| mov 0x0,%esp | |
| jmp *0x0 | |
| ## Jump to original handler | |
| ## UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax; | |
| popa | |
| ## Switch back to application stack | |
| mov 0x0,%esp | |
| ## We're outa here... | |
| iret |