From 7f05fa00f73038b425002566d3afe6c3ade2ccdb Mon Sep 17 00:00:00 2001 From: Guo Mang Date: Thu, 22 Dec 2016 15:55:38 +0800 Subject: MdeModulePkg: Move to new location Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Guo Mang --- .../Universal/DebugSupportDxe/DebugSupport.c | 133 ++ .../Universal/DebugSupportDxe/DebugSupportDxe.inf | 87 ++ .../Universal/DebugSupportDxe/DebugSupportDxe.uni | Bin 0 -> 2856 bytes .../DebugSupportDxe/DebugSupportDxeExtra.uni | Bin 0 -> 1352 bytes .../Universal/DebugSupportDxe/Ia32/AsmFuncs.S | 407 ++++++ .../Universal/DebugSupportDxe/Ia32/AsmFuncs.asm | 509 +++++++ .../Universal/DebugSupportDxe/Ia32/DebugSupport.h | 298 +++++ .../DebugSupportDxe/Ia32/PlDebugSupport.c | 373 ++++++ .../DebugSupportDxe/Ia32/PlDebugSupport.h | 22 + .../DebugSupportDxe/Ia32/PlDebugSupportIa32.c | 145 ++ .../Universal/DebugSupportDxe/Ipf/AsmFuncs.s | 1382 ++++++++++++++++++++ .../Universal/DebugSupportDxe/Ipf/Common.i | 29 + .../Universal/DebugSupportDxe/Ipf/Ds64Macros.i | 78 ++ .../Universal/DebugSupportDxe/Ipf/PlDebugSupport.c | 467 +++++++ .../Universal/DebugSupportDxe/Ipf/PlDebugSupport.h | 324 +++++ .../Universal/DebugSupportDxe/X64/AsmFuncs.S | 551 ++++++++ .../Universal/DebugSupportDxe/X64/AsmFuncs.asm | 596 +++++++++ .../Universal/DebugSupportDxe/X64/PlDebugSupport.h | 22 + .../DebugSupportDxe/X64/PlDebugSupportX64.c | 146 +++ 19 files changed, 5569 insertions(+) create mode 100644 Core/MdeModulePkg/Universal/DebugSupportDxe/DebugSupport.c create mode 100644 Core/MdeModulePkg/Universal/DebugSupportDxe/DebugSupportDxe.inf create mode 100644 Core/MdeModulePkg/Universal/DebugSupportDxe/DebugSupportDxe.uni create mode 100644 Core/MdeModulePkg/Universal/DebugSupportDxe/DebugSupportDxeExtra.uni create mode 100644 Core/MdeModulePkg/Universal/DebugSupportDxe/Ia32/AsmFuncs.S create mode 100644 Core/MdeModulePkg/Universal/DebugSupportDxe/Ia32/AsmFuncs.asm create mode 100644 Core/MdeModulePkg/Universal/DebugSupportDxe/Ia32/DebugSupport.h create mode 100644 Core/MdeModulePkg/Universal/DebugSupportDxe/Ia32/PlDebugSupport.c create mode 100644 Core/MdeModulePkg/Universal/DebugSupportDxe/Ia32/PlDebugSupport.h create mode 100644 Core/MdeModulePkg/Universal/DebugSupportDxe/Ia32/PlDebugSupportIa32.c create mode 100644 Core/MdeModulePkg/Universal/DebugSupportDxe/Ipf/AsmFuncs.s create mode 100644 Core/MdeModulePkg/Universal/DebugSupportDxe/Ipf/Common.i create mode 100644 Core/MdeModulePkg/Universal/DebugSupportDxe/Ipf/Ds64Macros.i create mode 100644 Core/MdeModulePkg/Universal/DebugSupportDxe/Ipf/PlDebugSupport.c create mode 100644 Core/MdeModulePkg/Universal/DebugSupportDxe/Ipf/PlDebugSupport.h create mode 100644 Core/MdeModulePkg/Universal/DebugSupportDxe/X64/AsmFuncs.S create mode 100644 Core/MdeModulePkg/Universal/DebugSupportDxe/X64/AsmFuncs.asm create mode 100644 Core/MdeModulePkg/Universal/DebugSupportDxe/X64/PlDebugSupport.h create mode 100644 Core/MdeModulePkg/Universal/DebugSupportDxe/X64/PlDebugSupportX64.c (limited to 'Core/MdeModulePkg/Universal/DebugSupportDxe') diff --git a/Core/MdeModulePkg/Universal/DebugSupportDxe/DebugSupport.c b/Core/MdeModulePkg/Universal/DebugSupportDxe/DebugSupport.c new file mode 100644 index 0000000000..9a8f86de10 --- /dev/null +++ b/Core/MdeModulePkg/Universal/DebugSupportDxe/DebugSupport.c @@ -0,0 +1,133 @@ +/** @file + Top level C file for debug support driver. Contains initialization function. + +Copyright (c) 2006 - 2009, 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. + +**/ + +#include "PlDebugSupport.h" + +EFI_DEBUG_SUPPORT_PROTOCOL mDebugSupportProtocolInterface = { + EFI_ISA, + GetMaximumProcessorIndex, + RegisterPeriodicCallback, + RegisterExceptionCallback, + InvalidateInstructionCache +}; + + +/** + Debug Support Driver entry point. + + Checks to see if there's not already a Debug Support protocol installed for + the selected processor before installing it. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval EFI_ALREADY_STARTED Debug Support protocol is installed already. + @retval other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +InitializeDebugSupportDriver ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_LOADED_IMAGE_PROTOCOL *LoadedImageProtocolPtr; + EFI_STATUS Status; + EFI_HANDLE Handle; + EFI_HANDLE *HandlePtr; + UINTN NumHandles; + EFI_DEBUG_SUPPORT_PROTOCOL *DebugSupportProtocolPtr; + + // + // First check to see that the debug support protocol for this processor + // type is not already installed + // + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiDebugSupportProtocolGuid, + NULL, + &NumHandles, + &HandlePtr + ); + + if (Status != EFI_NOT_FOUND) { + do { + NumHandles--; + Status = gBS->OpenProtocol ( + HandlePtr[NumHandles], + &gEfiDebugSupportProtocolGuid, + (VOID **) &DebugSupportProtocolPtr, + ImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if ((Status == EFI_SUCCESS) && (DebugSupportProtocolPtr->Isa == EFI_ISA)) { + // + // a Debug Support protocol has been installed for this processor + // + FreePool (HandlePtr); + Status = EFI_ALREADY_STARTED; + goto ErrExit; + } + } while (NumHandles > 0); + FreePool (HandlePtr); + } + + // + // Get our image information and install platform specific unload handler + // + Status = gBS->OpenProtocol ( + ImageHandle, + &gEfiLoadedImageProtocolGuid, + (VOID **) &LoadedImageProtocolPtr, + ImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + ASSERT (!EFI_ERROR (Status)); + if (Status != EFI_SUCCESS) { + goto ErrExit; + } + + LoadedImageProtocolPtr->Unload = PlUnloadDebugSupportDriver; + + // + // Call hook for processor specific initialization + // + Status = PlInitializeDebugSupportDriver (); + ASSERT (!EFI_ERROR (Status)); + if (Status != EFI_SUCCESS) { + goto ErrExit; + } + + // + // Install Debug Support protocol to new handle + // + Handle = NULL; + Status = gBS->InstallProtocolInterface ( + &Handle, + &gEfiDebugSupportProtocolGuid, + EFI_NATIVE_INTERFACE, + &mDebugSupportProtocolInterface + ); + ASSERT (!EFI_ERROR (Status)); + if (Status != EFI_SUCCESS) { + goto ErrExit; + } + +ErrExit: + return Status; +} diff --git a/Core/MdeModulePkg/Universal/DebugSupportDxe/DebugSupportDxe.inf b/Core/MdeModulePkg/Universal/DebugSupportDxe/DebugSupportDxe.inf new file mode 100644 index 0000000000..f1faa3d2c9 --- /dev/null +++ b/Core/MdeModulePkg/Universal/DebugSupportDxe/DebugSupportDxe.inf @@ -0,0 +1,87 @@ +## @file +# This driver installs Debug Support protocol for the selected processor. +# +# This driver provides the capabilities for debug-agent to gain control of the machine +# when certain types of events occur, i.e. breakpoint, processor execptions, etc. It also +# provides debug-agent to periodically gain control during operation of the machine to +# check for asynchronous commands form the host. +# +# Copyright (c) 2006 - 2014, 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. +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = DebugSupportDxe + MODULE_UNI_FILE = DebugSupportDxe.uni + FILE_GUID = 911D584C-35F7-4955-BEF9-B452769DDC3A + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = InitializeDebugSupportDriver + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF +# + +[Sources] + DebugSupport.c + +[Sources.Ia32] + Ia32/DebugSupport.h + Ia32/PlDebugSupport.c + Ia32/PlDebugSupport.h + Ia32/PlDebugSupportIa32.c + Ia32/AsmFuncs.S + Ia32/AsmFuncs.asm + +[Sources.X64] + Ia32/DebugSupport.h + Ia32/PlDebugSupport.c + X64/PlDebugSupport.h + X64/PlDebugSupportX64.c + X64/AsmFuncs.S + X64/AsmFuncs.asm + +[Sources.IPF] + Ipf/PlDebugSupport.h + Ipf/PlDebugSupport.c + Ipf/Ds64Macros.i + Ipf/Common.i + Ipf/AsmFuncs.s + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + + +[LibraryClasses] + UefiBootServicesTableLib + MemoryAllocationLib + BaseMemoryLib + UefiDriverEntryPoint + DebugLib + +[LibraryClasses.IA32, LibraryClasses.X64] + BaseLib + +[Protocols] + gEfiLoadedImageProtocolGuid ## CONSUMES + gEfiDebugSupportProtocolGuid ## PRODUCES + + +[Depex] + TRUE + +[UserExtensions.TianoCore."ExtraFiles"] + DebugSupportDxeExtra.uni diff --git a/Core/MdeModulePkg/Universal/DebugSupportDxe/DebugSupportDxe.uni b/Core/MdeModulePkg/Universal/DebugSupportDxe/DebugSupportDxe.uni new file mode 100644 index 0000000000..be3ca60bfb Binary files /dev/null and b/Core/MdeModulePkg/Universal/DebugSupportDxe/DebugSupportDxe.uni differ diff --git a/Core/MdeModulePkg/Universal/DebugSupportDxe/DebugSupportDxeExtra.uni b/Core/MdeModulePkg/Universal/DebugSupportDxe/DebugSupportDxeExtra.uni new file mode 100644 index 0000000000..5e49ee0140 Binary files /dev/null and b/Core/MdeModulePkg/Universal/DebugSupportDxe/DebugSupportDxeExtra.uni differ diff --git a/Core/MdeModulePkg/Universal/DebugSupportDxe/Ia32/AsmFuncs.S b/Core/MdeModulePkg/Universal/DebugSupportDxe/Ia32/AsmFuncs.S new file mode 100644 index 0000000000..fd1a96b76f --- /dev/null +++ b/Core/MdeModulePkg/Universal/DebugSupportDxe/Ia32/AsmFuncs.S @@ -0,0 +1,407 @@ +#/**@file +# Low leve IA32 specific debug support functions. +# +# Copyright (c) 2006 - 2011, 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. +# +#**/ + +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 diff --git a/Core/MdeModulePkg/Universal/DebugSupportDxe/Ia32/AsmFuncs.asm b/Core/MdeModulePkg/Universal/DebugSupportDxe/Ia32/AsmFuncs.asm new file mode 100644 index 0000000000..32cbc31654 --- /dev/null +++ b/Core/MdeModulePkg/Universal/DebugSupportDxe/Ia32/AsmFuncs.asm @@ -0,0 +1,509 @@ +;/** @file +; Low leve IA32 specific debug support functions. +; +; Copyright (c) 2006 - 2011, 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. +; +;**/ + +.586p +.MODEL FLAT, C + +EXCPT32_DIVIDE_ERROR EQU 0 +EXCPT32_DEBUG EQU 1 +EXCPT32_NMI EQU 2 +EXCPT32_BREAKPOINT EQU 3 +EXCPT32_OVERFLOW EQU 4 +EXCPT32_BOUND EQU 5 +EXCPT32_INVALID_OPCODE EQU 6 +EXCPT32_DOUBLE_FAULT EQU 8 +EXCPT32_INVALID_TSS EQU 10 +EXCPT32_SEG_NOT_PRESENT EQU 11 +EXCPT32_STACK_FAULT EQU 12 +EXCPT32_GP_FAULT EQU 13 +EXCPT32_PAGE_FAULT EQU 14 +EXCPT32_FP_ERROR EQU 16 +EXCPT32_ALIGNMENT_CHECK EQU 17 +EXCPT32_MACHINE_CHECK EQU 18 +EXCPT32_SIMD EQU 19 + +FXSTOR_FLAG EQU 01000000h ; bit cpuid 24 of feature flags + +;; The FXSTOR and FXRSTOR commands are used for saving and restoring the x87, +;; MMX, SSE, SSE2, etc registers. The initialization of the debugsupport driver +;; MUST check the CPUID feature flags to see that these instructions are available +;; and fail to init if they are not. + +;; fxstor [edi] +FXSTOR_EDI MACRO + db 0fh, 0aeh, 00000111y ; mod = 00, reg/op = 000, r/m = 111 = [edi] +ENDM + +;; fxrstor [esi] +FXRSTOR_ESI MACRO + db 0fh, 0aeh, 00001110y ; mod = 00, reg/op = 001, r/m = 110 = [esi] +ENDM +.DATA + +public OrigVector, InterruptEntryStub, StubSize, CommonIdtEntry, FxStorSupport + +StubSize dd InterruptEntryStubEnd - InterruptEntryStub +AppEsp dd 11111111h ; ? +DebugEsp dd 22222222h ; ? +ExtraPush dd 33333333h ; ? +ExceptData dd 44444444h ; ? +Eflags dd 55555555h ; ? +OrigVector dd 66666666h ; ? + +;; The declarations below define the memory region that will be used for the debug stack. +;; The context record will be built by pushing register values onto this stack. +;; It is imparitive that alignment be carefully managed, since the FXSTOR and +;; FXRSTOR instructions will GP fault if their memory operand is not 16 byte aligned. +;; +;; The stub will switch stacks from the application stack to the debuger stack +;; and pushes the exception number. +;; +;; Then we building the context record on the stack. Since the stack grows down, +;; we push the fields of the context record from the back to the front. There +;; are 132 bytes of stack used prior allocating the 512 bytes of stack to be +;; used as the memory buffer for the fxstor instruction. Therefore address of +;; the buffer used for the FXSTOR instruction is &Eax - 132 - 512, which +;; must be 16 byte aligned. +;; +;; We carefully locate the stack to make this happen. +;; +;; For reference, the context structure looks like this: +;; struct { +;; UINT32 ExceptionData; +;; FX_SAVE_STATE_IA32 FxSaveState; // 512 bytes, must be 16 byte aligned +;; UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; +;; UINT32 Cr0, Cr1, 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 + + +align 16 +DebugStackEnd db "DbgStkEnd >>>>>>" ;; 16 byte long string - must be 16 bytes to preserve alignment + dd 1ffdh dup (000000000h) ;; 32K should be enough stack + ;; This allocation is coocked to insure + ;; that the the buffer for the FXSTORE instruction + ;; will be 16 byte aligned also. + ;; +ExceptionNumber dd ? ;; first entry will be the vector number pushed by the stub + +DebugStackBegin db "<<<< DbgStkBegin" ;; initial debug ESP == DebugStackBegin, set in stub + +.CODE + +externdef InterruptDistrubutionHub:near + +;------------------------------------------------------------------------------ +; BOOLEAN +; FxStorSupport ( +; void +; ) +; +; Abstract: Returns TRUE if FxStor instructions are supported +; +FxStorSupport PROC C PUBLIC + +; +; cpuid corrupts ebx which must be preserved per the C calling convention +; + push ebx + mov eax, 1 + cpuid + mov eax, edx + and eax, FXSTOR_FLAG + shr eax, 24 + pop ebx + ret +FxStorSupport ENDP + + + +;------------------------------------------------------------------------------ +; void +; Vect2Desc ( +; DESCRIPTOR * DestDesc, +; void (*Vector) (void) +; ) +; +; Abstract: Encodes an IDT descriptor with the given physical address +; +Vect2Desc PROC C PUBLIC DestPtr:DWORD, Vector:DWORD + + mov eax, Vector + mov ecx, DestPtr + mov word ptr [ecx], ax ; write bits 15..0 of offset + mov dx, cs + mov word ptr [ecx+2], dx ; SYS_CODE_SEL from GDT + mov word ptr [ecx+4], 0e00h OR 8000h ; type = 386 interrupt gate, present + shr eax, 16 + mov word ptr [ecx+6], ax ; write bits 31..16 of offset + + ret + +Vect2Desc ENDP + + + +;------------------------------------------------------------------------------ +; InterruptEntryStub +; +; Abstract: This code is not a function, but is a small piece of code that is +; copied and fixed up once for each IDT entry that is hooked. +; +InterruptEntryStub:: + mov AppEsp, esp ; save stack top + mov esp, offset DebugStackBegin ; switch to debugger stack + push 0 ; push vector number - will be modified before installed + db 0e9h ; jump rel32 + dd 0 ; fixed up to relative address of CommonIdtEntry +InterruptEntryStubEnd: + + + +;------------------------------------------------------------------------------ +; CommonIdtEntry +; +; Abstract: This code is not a function, but is the common part for all IDT +; vectors. +; +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; + pushad + +;; Save interrupt state eflags register... + pushfd + pop eax + mov dword ptr Eflags, 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. + .IF ExceptionNumber == EXCPT32_DOUBLE_FAULT + mov ExtraPush, 1 + .ELSEIF ExceptionNumber == EXCPT32_INVALID_TSS + mov ExtraPush, 1 + .ELSEIF ExceptionNumber == EXCPT32_SEG_NOT_PRESENT + mov ExtraPush, 1 + .ELSEIF ExceptionNumber == EXCPT32_STACK_FAULT + mov ExtraPush, 1 + .ELSEIF ExceptionNumber == EXCPT32_GP_FAULT + mov ExtraPush, 1 + .ELSEIF ExceptionNumber == EXCPT32_PAGE_FAULT + mov ExtraPush, 1 + .ELSEIF ExceptionNumber == EXCPT32_ALIGNMENT_CHECK + mov ExtraPush, 1 + .ELSE + mov ExtraPush, 0 + .ENDIF + +;; If there's some extra data, save it also, and modify the saved AppEsp to effectively +;; pop this value off the application's stack. + .IF ExtraPush == 1 + mov eax, AppEsp + mov ebx, [eax] + mov ExceptData, ebx + add eax, 4 + mov AppEsp, eax + .ELSE + mov ExceptData, 0 + .ENDIF + +;; 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 eax, [esp + 12] + mov DebugEsp, eax + mov eax, AppEsp + add eax, 12 + ; application stack has eflags, cs, & eip, so + ; last actual application stack entry is + ; 12 bytes into the application stack. + mov [esp + 12], eax + +;; continue building context record +;; UINT32 Gs, Fs, Es, Ds, Cs, Ss; insure high 16 bits of each is zero + mov eax, ss + push eax + + ; CS from application is one entry back in application stack + mov eax, AppEsp + movzx eax, word ptr [eax + 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; + ; Eip from application is on top of application stack + mov eax, AppEsp + push dword ptr [eax] + +;; UINT32 Gdtr[2], Idtr[2]; + push 0 + push 0 + sidt fword ptr [esp] + push 0 + push 0 + sgdt fword ptr [esp] + +;; UINT32 Ldtr, Tr; + xor eax, eax + str ax + push eax + sldt ax + push eax + +;; UINT32 EFlags; +;; Eflags from application is two entries back in application stack + mov eax, AppEsp + push dword ptr [eax + 8] + +;; 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 eax, cr4 + or eax, 208h + mov cr4, eax + push eax + mov eax, cr3 + push eax + mov eax, cr2 + push eax + push 0 + mov eax, cr0 + push eax + +;; UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; + mov eax, dr7 + push eax +;; clear Dr7 while executing debugger itself + xor eax, eax + mov dr7, eax + + mov eax, dr6 + push eax +;; insure all status bits in dr6 are clear... + xor eax, eax + mov dr6, 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 + ; 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. + FXSTOR_EDI + +;; UEFI calling convention for IA32 requires that Direction flag in EFLAGs is clear + cld + +;; UINT32 ExceptionData; + mov eax, ExceptData + push eax + +; call to C code which will in turn call registered handler +; pass in the vector number + mov eax, esp + push eax + mov eax, ExceptionNumber + push eax + call InterruptDistrubutionHub + add esp, 8 + +; restore context... +;; 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; + pop eax + mov dr0, eax + pop eax + mov dr1, eax + pop eax + mov dr2, eax + pop eax + mov dr3, eax +;; skip restore of dr6. We cleared dr6 during the context save. + add esp, 4 + pop eax + mov dr7, eax + +;; UINT32 Cr0, Cr1, Cr2, Cr3, Cr4; + pop eax + mov cr0, eax + add esp, 4 + pop eax + mov cr2, eax + pop eax + mov cr3, eax + pop eax + mov cr4, eax + +;; UINT32 EFlags; + mov eax, AppEsp + pop dword ptr [eax + 8] + +;; UINT32 Ldtr, Tr; +;; UINT32 Gdtr[2], Idtr[2]; +;; Best not let anyone mess with these particular registers... + add esp, 24 + +;; UINT32 Eip; + pop dword ptr [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 + pop [eax + 4] + pop ss + +;; 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 ebx, [esp + 12] ; move the potentially modified AppEsp into ebx + mov eax, AppEsp + add eax, 12 + cmp ebx, eax + je NoAppStackMove + + mov eax, AppEsp + mov ecx, [eax] ; EIP + mov [ebx], ecx + + mov ecx, [eax + 4] ; CS + mov [ebx + 4], ecx + + mov ecx, [eax + 8] ; EFLAGS + mov [ebx + 8], ecx + + mov eax, ebx ; modify the saved AppEsp to the new AppEsp + mov AppEsp, eax +NoAppStackMove: + mov eax, DebugEsp ; restore the DebugEsp on the debug stack + ; so our "popad" will not cause a stack switch + mov [esp + 12], eax + + cmp ExceptionNumber, 068h + jne NoChain + +Chain: + +;; 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 eax, AppEsp + mov ebx, [eax + 8] + and ebx, NOT 300h ; special handling for IF and TF + push ebx + push cs + push PhonyIretd + iretd +PhonyIretd: + +;; UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax; + popad + +;; Switch back to application stack + mov esp, AppEsp + +;; Jump to original handler + jmp OrigVector + +NoChain: +;; UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax; + popad + +;; Switch back to application stack + mov esp, AppEsp + +;; We're outa here... + iretd +END + + + diff --git a/Core/MdeModulePkg/Universal/DebugSupportDxe/Ia32/DebugSupport.h b/Core/MdeModulePkg/Universal/DebugSupportDxe/Ia32/DebugSupport.h new file mode 100644 index 0000000000..7f7d8e5ba6 --- /dev/null +++ b/Core/MdeModulePkg/Universal/DebugSupportDxe/Ia32/DebugSupport.h @@ -0,0 +1,298 @@ +/** @file + Generic debug support macros, typedefs and prototypes for IA32/x64. + +Copyright (c) 2006 - 2010, 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. + +**/ + +#ifndef _DEBUG_SUPPORT_H_ +#define _DEBUG_SUPPORT_H_ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#define NUM_IDT_ENTRIES 0x78 +#define SYSTEM_TIMER_VECTOR 0x68 + +typedef +VOID +(*DEBUG_PROC) ( + VOID + ); + +typedef +VOID +(EFIAPI *CALLBACK_FUNC) ( + ); + +typedef struct { + IA32_IDT_GATE_DESCRIPTOR OrigDesc; + DEBUG_PROC OrigVector; + IA32_IDT_GATE_DESCRIPTOR NewDesc; + DEBUG_PROC StubEntry; + CALLBACK_FUNC RegisteredCallback; +} IDT_ENTRY; + +extern UINT8 InterruptEntryStub[]; +extern UINT32 StubSize; +extern VOID (*OrigVector) (VOID); +extern IDT_ENTRY *IdtEntryTable; +extern IA32_IDT_GATE_DESCRIPTOR NullDesc; + +/** + Generic IDT entry. + +**/ +VOID +CommonIdtEntry ( + VOID + ); + +/** + Check whether FXSTOR is supported + + @retval TRUE FXSTOR is supported. + @retval FALSE FXSTOR is not supported. + +**/ +BOOLEAN +FxStorSupport ( + VOID + ); + +/** + Encodes an IDT descriptor with the given physical address. + + @param DestDesc The IDT descriptor address. + @param Vecotr The interrupt vector entry. + +**/ +VOID +Vect2Desc ( + IA32_IDT_GATE_DESCRIPTOR * DestDesc, + VOID (*Vector) (VOID) + ); + +/** + Initializes driver's handler registration database. + + This code executes in boot services context + Must be public because it's referenced from DebugSupport.c + + @retval EFI_UNSUPPORTED If IA32 processor does not support FXSTOR/FXRSTOR instructions, + the context save will fail, so these processor's are not supported. + @retval EFI_OUT_OF_RESOURCES Fails to allocate memory. + @retval EFI_SUCCESS Initializes successfully. + +**/ +EFI_STATUS +PlInitializeDebugSupportDriver ( + VOID + ); + +/** + This is the callback that is written to the LoadedImage protocol instance + on the image handle. It uninstalls all registered handlers and frees all entry + stub memory. + + @param ImageHandle The firmware allocated handle for the EFI image. + + @retval EFI_SUCCESS Always. + +**/ +EFI_STATUS +EFIAPI +PlUnloadDebugSupportDriver ( + IN EFI_HANDLE ImageHandle + ); + +/** + Returns the maximum value that may be used for the ProcessorIndex parameter in + RegisterPeriodicCallback() and RegisterExceptionCallback(). + + Hard coded to support only 1 processor for now. + + @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance. + @param MaxProcessorIndex Pointer to a caller-allocated UINTN in which the maximum supported + processor index is returned. Always 0 returned. + + @retval EFI_SUCCESS Always returned with **MaxProcessorIndex set to 0. + +**/ +EFI_STATUS +EFIAPI +GetMaximumProcessorIndex ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + OUT UINTN *MaxProcessorIndex + ); + +/** + Registers a function to be called back periodically in interrupt context. + + @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance. + @param ProcessorIndex Specifies which processor the callback function applies to. + @param PeriodicCallback A pointer to a function of type PERIODIC_CALLBACK that is the main + periodic entry point of the debug agent. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ALREADY_STARTED Non-NULL PeriodicCallback parameter when a callback + function was previously registered. + @retval EFI_OUT_OF_RESOURCES System has insufficient memory resources to register new callback + function. +**/ +EFI_STATUS +EFIAPI +RegisterPeriodicCallback ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN EFI_PERIODIC_CALLBACK PeriodicCallback + ); + +/** + Registers a function to be called when a given processor exception occurs. + + This code executes in boot services context. + + @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance. + @param ProcessorIndex Specifies which processor the callback function applies to. + @param ExceptionCallback A pointer to a function of type EXCEPTION_CALLBACK that is called + when the processor exception specified by ExceptionType occurs. + @param ExceptionType Specifies which processor exception to hook. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ALREADY_STARTED Non-NULL PeriodicCallback parameter when a callback + function was previously registered. + @retval EFI_OUT_OF_RESOURCES System has insufficient memory resources to register new callback + function. +**/ +EFI_STATUS +EFIAPI +RegisterExceptionCallback ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN EFI_EXCEPTION_CALLBACK ExceptionCallback, + IN EFI_EXCEPTION_TYPE ExceptionType + ); + +/** + Invalidates processor instruction cache for a memory range. Subsequent execution in this range + causes a fresh memory fetch to retrieve code to be executed. + + @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance. + @param ProcessorIndex Specifies which processor's instruction cache is to be invalidated. + @param Start Specifies the physical base of the memory range to be invalidated. + @param Length Specifies the minimum number of bytes in the processor's instruction + cache to invalidate. + + @retval EFI_SUCCESS Always returned. + +**/ +EFI_STATUS +EFIAPI +InvalidateInstructionCache ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN VOID *Start, + IN UINT64 Length + ); + +/** + Allocate pool for a new IDT entry stub. + + Copy the generic stub into the new buffer and fixup the vector number + and jump target address. + + @param ExceptionType This is the exception type that the new stub will be created + for. + @param Stub On successful exit, *Stub contains the newly allocated entry stub. + +**/ +VOID +CreateEntryStub ( + IN EFI_EXCEPTION_TYPE ExceptionType, + OUT VOID **Stub + ); + +/** + Get Interrupt Handle from IDT Gate Descriptor. + + @param IdtGateDecriptor IDT Gate Descriptor. + + @return Interrupt Handle stored in IDT Gate Descriptor. + +**/ +UINTN +GetInterruptHandleFromIdt ( + IN IA32_IDT_GATE_DESCRIPTOR *IdtGateDecriptor + ); + +/** + This is the main worker function that manages the state of the interrupt + handlers. It both installs and uninstalls interrupt handlers based on the + value of NewCallback. If NewCallback is NULL, then uninstall is indicated. + If NewCallback is non-NULL, then install is indicated. + + @param NewCallback If non-NULL, NewCallback specifies the new handler to register. + If NULL, specifies that the previously registered handler should + be uninstalled. + @param ExceptionType Indicates which entry to manage. + + @retval EFI_SUCCESS Process is ok. + @retval EFI_INVALID_PARAMETER Requested uninstalling a handler from a vector that has + no handler registered for it + @retval EFI_ALREADY_STARTED Requested install to a vector that already has a handler registered. + @retval others Possible return values are passed through from UnHookEntry and HookEntry. + +**/ +EFI_STATUS +ManageIdtEntryTable ( + CALLBACK_FUNC NewCallback, + EFI_EXCEPTION_TYPE ExceptionType + ); + +/** + Creates a nes entry stub. Then saves the current IDT entry and replaces it + with an interrupt gate for the new entry point. The IdtEntryTable is updated + with the new registered function. + + This code executes in boot services context. The stub entry executes in interrupt + context. + + @param ExceptionType Specifies which vector to hook. + @param NewCallback A pointer to the new function to be registered. + +**/ +VOID +HookEntry ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN CALLBACK_FUNC NewCallback + ); + +/** + Undoes HookEntry. This code executes in boot services context. + + @param ExceptionType Specifies which entry to unhook + +**/ +VOID +UnhookEntry ( + IN EFI_EXCEPTION_TYPE ExceptionType + ); + +#endif diff --git a/Core/MdeModulePkg/Universal/DebugSupportDxe/Ia32/PlDebugSupport.c b/Core/MdeModulePkg/Universal/DebugSupportDxe/Ia32/PlDebugSupport.c new file mode 100644 index 0000000000..f0c529a41b --- /dev/null +++ b/Core/MdeModulePkg/Universal/DebugSupportDxe/Ia32/PlDebugSupport.c @@ -0,0 +1,373 @@ +/** @file + IA32/x64 generic functions to support Debug Support protocol. + +Copyright (c) 2006 - 2010, 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. + +**/ + +#include "DebugSupport.h" + +// +// This the global main table to keep track of the interrupts +// +IDT_ENTRY *IdtEntryTable = NULL; + +/** + Read IDT Gate Descriptor from IDT Table. + + @param Vector Specifies vector number. + @param IdtGateDescriptor Pointer to IDT Gate Descriptor read from IDT Table. + +**/ +VOID +ReadIdtGateDescriptor ( + IN EFI_EXCEPTION_TYPE Vector, + OUT IA32_IDT_GATE_DESCRIPTOR *IdtGateDescriptor + ) +{ + IA32_DESCRIPTOR IdtrValue; + IA32_IDT_GATE_DESCRIPTOR *IdtTable; + + AsmReadIdtr (&IdtrValue); + IdtTable = (IA32_IDT_GATE_DESCRIPTOR *) IdtrValue.Base; + + CopyMem ((VOID *) IdtGateDescriptor, (VOID *) &(IdtTable)[Vector], sizeof (IA32_IDT_GATE_DESCRIPTOR)); +} + +/** + Write IDT Gate Descriptor into IDT Table. + + @param Vector Specifies vector number. + @param IdtGateDescriptor Pointer to IDT Gate Descriptor written into IDT Table. + +**/ +VOID +WriteIdtGateDescriptor ( + EFI_EXCEPTION_TYPE Vector, + IA32_IDT_GATE_DESCRIPTOR *IdtGateDescriptor + ) +{ + IA32_DESCRIPTOR IdtrValue; + IA32_IDT_GATE_DESCRIPTOR *IdtTable; + + AsmReadIdtr (&IdtrValue); + IdtTable = (IA32_IDT_GATE_DESCRIPTOR *) IdtrValue.Base; + + CopyMem ((VOID *) &(IdtTable)[Vector], (VOID *) IdtGateDescriptor, sizeof (IA32_IDT_GATE_DESCRIPTOR)); +} + +/** + Creates a nes entry stub. Then saves the current IDT entry and replaces it + with an interrupt gate for the new entry point. The IdtEntryTable is updated + with the new registered function. + + This code executes in boot services context. The stub entry executes in interrupt + context. + + @param ExceptionType Specifies which vector to hook. + @param NewCallback A pointer to the new function to be registered. + +**/ +VOID +HookEntry ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN CALLBACK_FUNC NewCallback + ) +{ + BOOLEAN OldIntFlagState; + + CreateEntryStub (ExceptionType, (VOID **) &IdtEntryTable[ExceptionType].StubEntry); + + // + // Disables CPU interrupts and returns the previous interrupt state + // + OldIntFlagState = SaveAndDisableInterrupts (); + + // + // gets IDT Gate descriptor by index + // + ReadIdtGateDescriptor (ExceptionType, &(IdtEntryTable[ExceptionType].OrigDesc)); + // + // stores orignal interrupt handle + // + IdtEntryTable[ExceptionType].OrigVector = (DEBUG_PROC) GetInterruptHandleFromIdt (&(IdtEntryTable[ExceptionType].OrigDesc)); + + // + // encodes new IDT Gate descriptor by stub entry + // + Vect2Desc (&IdtEntryTable[ExceptionType].NewDesc, IdtEntryTable[ExceptionType].StubEntry); + // + // stores NewCallback + // + IdtEntryTable[ExceptionType].RegisteredCallback = NewCallback; + + // + // writes back new IDT Gate descriptor + // + WriteIdtGateDescriptor (ExceptionType, &(IdtEntryTable[ExceptionType].NewDesc)); + + // + // restore interrupt state + // + SetInterruptState (OldIntFlagState); + + return ; +} + +/** + Undoes HookEntry. This code executes in boot services context. + + @param ExceptionType Specifies which entry to unhook + +**/ +VOID +UnhookEntry ( + IN EFI_EXCEPTION_TYPE ExceptionType + ) +{ + BOOLEAN OldIntFlagState; + + // + // Disables CPU interrupts and returns the previous interrupt state + // + OldIntFlagState = SaveAndDisableInterrupts (); + + // + // restore the default IDT Date Descriptor + // + WriteIdtGateDescriptor (ExceptionType, &(IdtEntryTable[ExceptionType].OrigDesc)); + + // + // restore interrupt state + // + SetInterruptState (OldIntFlagState); + + return ; +} + +/** + Returns the maximum value that may be used for the ProcessorIndex parameter in + RegisterPeriodicCallback() and RegisterExceptionCallback(). + + Hard coded to support only 1 processor for now. + + @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance. + @param MaxProcessorIndex Pointer to a caller-allocated UINTN in which the maximum supported + processor index is returned. Always 0 returned. + + @retval EFI_SUCCESS Always returned with **MaxProcessorIndex set to 0. + +**/ +EFI_STATUS +EFIAPI +GetMaximumProcessorIndex ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + OUT UINTN *MaxProcessorIndex + ) +{ + *MaxProcessorIndex = 0; + return EFI_SUCCESS; +} + +/** + Registers a function to be called back periodically in interrupt context. + + @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance. + @param ProcessorIndex Specifies which processor the callback function applies to. + @param PeriodicCallback A pointer to a function of type PERIODIC_CALLBACK that is the main + periodic entry point of the debug agent. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ALREADY_STARTED Non-NULL PeriodicCallback parameter when a callback + function was previously registered. + @retval EFI_OUT_OF_RESOURCES System has insufficient memory resources to register new callback + function. +**/ +EFI_STATUS +EFIAPI +RegisterPeriodicCallback ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN EFI_PERIODIC_CALLBACK PeriodicCallback + ) +{ + return ManageIdtEntryTable (PeriodicCallback, SYSTEM_TIMER_VECTOR); +} + +/** + Registers a function to be called when a given processor exception occurs. + + This code executes in boot services context. + + @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance. + @param ProcessorIndex Specifies which processor the callback function applies to. + @param ExceptionCallback A pointer to a function of type EXCEPTION_CALLBACK that is called + when the processor exception specified by ExceptionType occurs. + @param ExceptionType Specifies which processor exception to hook. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ALREADY_STARTED Non-NULL PeriodicCallback parameter when a callback + function was previously registered. + @retval EFI_OUT_OF_RESOURCES System has insufficient memory resources to register new callback + function. +**/ +EFI_STATUS +EFIAPI +RegisterExceptionCallback ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN EFI_EXCEPTION_CALLBACK ExceptionCallback, + IN EFI_EXCEPTION_TYPE ExceptionType + ) +{ + return ManageIdtEntryTable (ExceptionCallback, ExceptionType); +} + + +/** + Invalidates processor instruction cache for a memory range. Subsequent execution in this range + causes a fresh memory fetch to retrieve code to be executed. + + @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance. + @param ProcessorIndex Specifies which processor's instruction cache is to be invalidated. + @param Start Specifies the physical base of the memory range to be invalidated. + @param Length Specifies the minimum number of bytes in the processor's instruction + cache to invalidate. + + @retval EFI_SUCCESS Always returned. + +**/ +EFI_STATUS +EFIAPI +InvalidateInstructionCache ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN VOID *Start, + IN UINT64 Length + ) +{ + AsmWbinvd (); + return EFI_SUCCESS; +} + +/** + Common piece of code that invokes the registered handlers. + + This code executes in exception context so no efi calls are allowed. + This code is called from assembly file. + + @param ExceptionType Exception type + @param ContextRecord System context + +**/ +VOID +InterruptDistrubutionHub ( + EFI_EXCEPTION_TYPE ExceptionType, + EFI_SYSTEM_CONTEXT_IA32 *ContextRecord + ) +{ + if (IdtEntryTable[ExceptionType].RegisteredCallback != NULL) { + if (ExceptionType != SYSTEM_TIMER_VECTOR) { + IdtEntryTable[ExceptionType].RegisteredCallback (ExceptionType, ContextRecord); + } else { + OrigVector = IdtEntryTable[ExceptionType].OrigVector; + IdtEntryTable[ExceptionType].RegisteredCallback (ContextRecord); + } + } +} + +/** + This is the callback that is written to the Loaded Image protocol instance + on the image handle. It uninstalls all registered handlers and frees all entry + stub memory. + + @param ImageHandle The firmware allocated handle for the EFI image. + + @retval EFI_SUCCESS Always. + +**/ +EFI_STATUS +EFIAPI +PlUnloadDebugSupportDriver ( + IN EFI_HANDLE ImageHandle + ) +{ + EFI_EXCEPTION_TYPE ExceptionType; + + for (ExceptionType = 0; ExceptionType < NUM_IDT_ENTRIES; ExceptionType++) { + ManageIdtEntryTable (NULL, ExceptionType); + // + // Free space for each Interrupt Stub precedure. + // + if (IdtEntryTable[ExceptionType].StubEntry != NULL) { + FreePool ((VOID *)(UINTN)IdtEntryTable[ExceptionType].StubEntry); + } + } + + FreePool (IdtEntryTable); + + return EFI_SUCCESS; +} + +/** + Initializes driver's handler registration database. + + This code executes in boot services context. + Must be public because it's referenced from DebugSupport.c + + @retval EFI_UNSUPPORTED If IA32/x64 processor does not support FXSTOR/FXRSTOR instructions, + the context save will fail, so these processors are not supported. + @retval EFI_OUT_OF_RESOURCES Fails to allocate memory. + @retval EFI_SUCCESS Initializes successfully. + +**/ +EFI_STATUS +PlInitializeDebugSupportDriver ( + VOID + ) +{ + EFI_EXCEPTION_TYPE ExceptionType; + + // + // Check whether FxStor instructions are supported. + // + if (!FxStorSupport ()) { + return EFI_UNSUPPORTED; + } + + IdtEntryTable = AllocateZeroPool (sizeof (IDT_ENTRY) * NUM_IDT_ENTRIES); + if (IdtEntryTable == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + for (ExceptionType = 0; ExceptionType < NUM_IDT_ENTRIES; ExceptionType ++) { + IdtEntryTable[ExceptionType].StubEntry = (DEBUG_PROC) (UINTN) AllocatePool (StubSize); + if (IdtEntryTable[ExceptionType].StubEntry == NULL) { + goto ErrorCleanup; + } + + // + // Copy Interrupt stub code. + // + CopyMem ((VOID *)(UINTN)IdtEntryTable[ExceptionType].StubEntry, InterruptEntryStub, StubSize); + } + return EFI_SUCCESS; + +ErrorCleanup: + + for (ExceptionType = 0; ExceptionType < NUM_IDT_ENTRIES; ExceptionType++) { + if (IdtEntryTable[ExceptionType].StubEntry != NULL) { + FreePool ((VOID *)(UINTN)IdtEntryTable[ExceptionType].StubEntry); + } + } + FreePool (IdtEntryTable); + + return EFI_OUT_OF_RESOURCES; +} diff --git a/Core/MdeModulePkg/Universal/DebugSupportDxe/Ia32/PlDebugSupport.h b/Core/MdeModulePkg/Universal/DebugSupportDxe/Ia32/PlDebugSupport.h new file mode 100644 index 0000000000..0a1577fab3 --- /dev/null +++ b/Core/MdeModulePkg/Universal/DebugSupportDxe/Ia32/PlDebugSupport.h @@ -0,0 +1,22 @@ +/** @file + IA32 specific debug support macros, typedefs and prototypes. + +Copyright (c) 2006 - 2008, 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. + +**/ + +#ifndef _PLDEBUG_SUPPORT_H_ +#define _PLDEBUG_SUPPORT_H_ + +#include "Ia32/DebugSupport.h" + +#define EFI_ISA IsaIa32 + +#endif diff --git a/Core/MdeModulePkg/Universal/DebugSupportDxe/Ia32/PlDebugSupportIa32.c b/Core/MdeModulePkg/Universal/DebugSupportDxe/Ia32/PlDebugSupportIa32.c new file mode 100644 index 0000000000..23222737f8 --- /dev/null +++ b/Core/MdeModulePkg/Universal/DebugSupportDxe/Ia32/PlDebugSupportIa32.c @@ -0,0 +1,145 @@ +/** @file + IA32 specific functions to support Debug Support protocol. + +Copyright (c) 2008 - 2010, 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. + +**/ + +#include "PlDebugSupport.h" + +IA32_IDT_GATE_DESCRIPTOR NullDesc = {{0}}; + +/** + Get Interrupt Handle from IDT Gate Descriptor. + + @param IdtGateDescriptor IDT Gate Descriptor. + + @return Interrupt Handle stored in IDT Gate Descriptor. + +**/ +UINTN +GetInterruptHandleFromIdt ( + IN IA32_IDT_GATE_DESCRIPTOR *IdtGateDescriptor + ) +{ + UINTN InterruptHandle; + + // + // InterruptHandle 0-15 : OffsetLow + // InterruptHandle 16-31 : OffsetHigh + // + ((UINT16 *) &InterruptHandle)[0] = (UINT16) IdtGateDescriptor->Bits.OffsetLow; + ((UINT16 *) &InterruptHandle)[1] = (UINT16) IdtGateDescriptor->Bits.OffsetHigh; + + return InterruptHandle; +} + +/** + Allocate pool for a new IDT entry stub. + + Copy the generic stub into the new buffer and fixup the vector number + and jump target address. + + @param ExceptionType This is the exception type that the new stub will be created + for. + @param Stub On successful exit, *Stub contains the newly allocated entry stub. + +**/ +VOID +CreateEntryStub ( + IN EFI_EXCEPTION_TYPE ExceptionType, + OUT VOID **Stub + ) +{ + UINT8 *StubCopy; + + StubCopy = *Stub; + + // + // Fixup the stub code for this vector + // + + // The stub code looks like this: + // + // 00000000 89 25 00000004 R mov AppEsp, esp ; save stack top + // 00000006 BC 00008014 R mov esp, offset DbgStkBot ; switch to debugger stack + // 0000000B 6A 00 push 0 ; push vector number - will be modified before installed + // 0000000D E9 db 0e9h ; jump rel32 + // 0000000E 00000000 dd 0 ; fixed up to relative address of CommonIdtEntry + // + + // + // poke in the exception type so the second push pushes the exception type + // + StubCopy[0x0c] = (UINT8) ExceptionType; + + // + // fixup the jump target to point to the common entry + // + *(UINT32 *) &StubCopy[0x0e] = (UINT32) CommonIdtEntry - (UINT32) &StubCopy[StubSize]; + + return ; +} + +/** + This is the main worker function that manages the state of the interrupt + handlers. It both installs and uninstalls interrupt handlers based on the + value of NewCallback. If NewCallback is NULL, then uninstall is indicated. + If NewCallback is non-NULL, then install is indicated. + + @param NewCallback If non-NULL, NewCallback specifies the new handler to register. + If NULL, specifies that the previously registered handler should + be uninstalled. + @param ExceptionType Indicates which entry to manage. + + @retval EFI_SUCCESS Installing or Uninstalling operation is ok. + @retval EFI_INVALID_PARAMETER Requested uninstalling a handler from a vector that has + no handler registered for it + @retval EFI_ALREADY_STARTED Requested install to a vector that already has a handler registered. + +**/ +EFI_STATUS +ManageIdtEntryTable ( + CALLBACK_FUNC NewCallback, + EFI_EXCEPTION_TYPE ExceptionType + ) +{ + EFI_STATUS Status; + + Status = EFI_SUCCESS; + + if (CompareMem (&IdtEntryTable[ExceptionType].NewDesc, &NullDesc, sizeof (IA32_IDT_GATE_DESCRIPTOR)) != 0) { + // + // we've already installed to this vector + // + if (NewCallback != NULL) { + // + // if the input handler is non-null, error + // + Status = EFI_ALREADY_STARTED; + } else { + UnhookEntry (ExceptionType); + } + } else { + // + // no user handler installed on this vector + // + if (NewCallback == NULL) { + // + // if the input handler is null, error + // + Status = EFI_INVALID_PARAMETER; + } else { + HookEntry (ExceptionType, NewCallback); + } + } + + return Status; +} diff --git a/Core/MdeModulePkg/Universal/DebugSupportDxe/Ipf/AsmFuncs.s b/Core/MdeModulePkg/Universal/DebugSupportDxe/Ipf/AsmFuncs.s new file mode 100644 index 0000000000..db75fc088e --- /dev/null +++ b/Core/MdeModulePkg/Universal/DebugSupportDxe/Ipf/AsmFuncs.s @@ -0,0 +1,1382 @@ +/// @file +/// Low level IPF routines used by the debug support driver +/// +/// Copyright (c) 2006 - 2008, 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. +/// +/// + + +#include "Common.i" +#include "Ds64Macros.i" + +ASM_GLOBAL PatchSaveBuffer +ASM_GLOBAL IpfContextBuf +ASM_GLOBAL CommonHandler +ASM_GLOBAL ExternalInterruptCount + + +///////////////////////////////////////////// +// +// Name: +// InstructionCacheFlush +// +// Description: +// Flushes instruction cache for specified number of bytes +// + ASM_GLOBAL InstructionCacheFlush + .proc InstructionCacheFlush + .align 32 +InstructionCacheFlush:: + { .mii + alloc r3=2, 0, 0, 0 + cmp4.leu p0,p6=32, r33;; + (p6) mov r33=32;; + } + { .mii + nop.m 0 + zxt4 r29=r33;; + dep.z r30=r29, 0, 5;; + } + { .mii + cmp4.eq p0,p7=r0, r30 + shr.u r28=r29, 5;; + (p7) adds r28=1, r28;; + } + { .mii + nop.m 0 + shl r27=r28, 5;; + zxt4 r26=r27;; + } + { .mfb + add r31=r26, r32 + nop.f 0 + nop.b 0 + } +LoopBack: // $L143: + { .mii + fc r32 + adds r32=32, r32;; + cmp.ltu p14,p15=r32, r31 + } + { .mfb + nop.m 0 + nop.f 0 + //(p14) br.cond.dptk.few $L143#;; + (p14) br.cond.dptk.few LoopBack;; + } + { .mmi + sync.i;; + srlz.i + nop.i 0;; + } + { .mfb + nop.m 0 + nop.f 0 + br.ret.sptk.few b0;; + } + .endp InstructionCacheFlush + + +///////////////////////////////////////////// +// +// Name: +// ChainHandler +// +// Description: +// Chains an interrupt handler +// +// The purpose of this function is to enable chaining of the external interrupt. +// Since there's no clean SAL abstraction for doing this, we must do it +// surreptitiously. +// +// The reserved IVT entry at offset 0x3400 is coopted for use by this handler. +// According to Itanium architecture, it is reserved. Strictly speaking, this is +// not safe, as we're cheating and violating the Itanium architecture. However, +// as long as we're the only ones cheating, we should be OK. Without hooks in +// the SAL to enable IVT management, there aren't many good options. +// +// The strategy is to replace the first bundle of the external interrupt handler +// with our own that will branch into a piece of code we've supplied and located +// in the reserved IVT entry. Only the first bundle of the external interrupt +// IVT entry is modified. +// +// The original bundle is moved and relocated to space +// allocated within the reserved IVT entry. The next bundle following is +// is generated to go a hard coded branch back to the second bundle of the +// external interrupt IVT entry just in case the first bundle had no branch. +// +// Our new code will execute our handler, and then fall through to the +// original bundle after restoring all context appropriately. +// +// The following is a representation of what the IVT memory map looks like with +// our chained handler installed: +// +// +// +// +// +// This IVT entry is Failsafe bundle +// reserved by the +// Itanium architecture Original bundle 0 +// and is used for +// for locating our +// handler and the +// original bundle Patch code... +// zero of the ext +// interrupt handler +// +// RSVD (3400) Unused +// +// +// +// +// +// +// +// +// +// +// +// +// EXT_INT (3000) Bundle 0 Bundle zero - This one is +// modified, all other bundles +// in the EXT_INT entry are +// untouched. +// +// +// Arguments: +// +// Returns: +// +// Notes: +// +// + ASM_GLOBAL ChainHandler + .proc ChainHandler +ChainHandler: + + NESTED_SETUP( 0,2+3,3,0 ) + + mov r8=1 // r8 = success + mov r2=cr.iva;; +// +// NOTE: There's a potential hazard here in that we're simply stealing a bunch of +// bundles (memory) from the IVT and assuming there's no catastrophic side effect. +// +// First, save IVT area we're taking over with the patch so we can restore it later +// + addl out0=PATCH_ENTRY_OFFSET, r2 // out0 = source buffer + movl out1=PatchSaveBuffer // out1 = destination buffer + mov out2=0x40;; // out2 = number of bundles to copy... save entire IDT entry + br.call.sptk.few b0 = CopyBundles + +// Next, copy the patch code into the IVT + movl out0=PatchCode // out0 = source buffer of patch code + addl out1=PATCH_OFFSET, r2 // out1 = destination buffer - in IVT + mov out2=PATCH_CODE_SIZE;; + shr out2=out2, 4;; // out2 = number of bundles to copy + br.call.sptk.few b0 = CopyBundles + + +// copy original bundle 0 from the external interrupt handler to the +// appropriate place in the reserved IVT interrupt slot + addl out0=EXT_INT_ENTRY_OFFSET, r2 // out0 = source buffer + addl out1=RELOCATED_EXT_INT, r2 // out1 = destination buffer - in reserved IVT + mov out2=1;; // out2 = copy 1 bundle + br.call.sptk.few b0 = CopyBundles + +// Now relocate it there because it very likely had a branch instruction that +// that must now be fixed up. + addl out0=RELOCATED_EXT_INT, r2 // out0 = new runtime address of bundle - in reserved IVT + addl out1=EXT_INT_ENTRY_OFFSET, r2;;// out1 = IP address of previous location + mov out2=out0;; // out2 = IP address of new location + br.call.sptk.few b0 = RelocateBundle + +// Now copy into the failsafe branch into the next bundle just in case +// the original ext int bundle 0 bundle did not contain a branch instruction + movl out0=FailsafeBranch // out0 = source buffer + addl out1=FAILSAFE_BRANCH_OFFSET, r2 // out1 = destination buffer - in reserved IVT + mov out2=1;; // out2 = copy 1 bundle + br.call.sptk.few b0 = CopyBundles + +// Last, copy in our replacement for the external interrupt IVT entry bundle 0 + movl out0=PatchCodeNewBun0 // out0 = source buffer - our replacement bundle 0 + addl out1=EXT_INT_ENTRY_OFFSET, r2 // out1 = destination buffer - bundle 0 of External interrupt entry + mov out2=1;; // out2 = copy 1 bundle + br.call.sptk.few b0 = CopyBundles + +ChainHandlerDone: + NESTED_RETURN + + .endp ChainHandler + + +///////////////////////////////////////////// +// +// Name: +// UnchainHandler +// +// Description: +// Unchains an interrupt handler +// +// Arguments: +// +// Returns: +// +// Notes: +// +// + ASM_GLOBAL UnchainHandler + .proc UnchainHandler + +UnchainHandler: + + NESTED_SETUP( 0,2+3,3,0 ) + + mov r8=1 // r8 = success + mov r2=cr.iva;; // r2 = interrupt vector address + +// First copy original Ext Int bundle 0 back to it's proper home... + addl out0=RELOCATED_EXT_INT, r2 // out0 = source - in reserved IVT + addl out1=EXT_INT_ENTRY_OFFSET, r2 // out1 = destination buffer - first bundle of Ext Int entry + mov out2=1;; // out2 = copy 1 bundle + br.call.sptk.few b0 = CopyBundles + +// Now, relocate it again... + addl out0=EXT_INT_ENTRY_OFFSET, r2 // out1 = New runtime address + addl out1=RELOCATED_EXT_INT, r2;; // out0 = IP address of previous location + mov out2=out0;; // out2 = IP address of new location + br.call.sptk.few b0 = RelocateBundle + +// Last, restore the patch area + movl out0=PatchSaveBuffer // out0 = source buffer + addl out1=PATCH_ENTRY_OFFSET, r2 // out1 = destination buffer + mov out2=0x40;; // out2 = number of bundles to copy... save entire IDT entry + br.call.sptk.few b0 = CopyBundles + +UnchainHandlerDone: + NESTED_RETURN + + .endp UnchainHandler + + +///////////////////////////////////////////// +// +// Name: +// CopyBundles +// +// Description: +// Copies instruction bundles - flushes icache as necessary +// +// Arguments: +// in0 - Bundle source +// in1 - Bundle destination +// in2 - Bundle count +// +// Returns: +// +// Notes: +// This procedure is a leaf routine +// + .proc CopyBundles + +CopyBundles: + + NESTED_SETUP(3,2+1,0,0) + + shl in2=in2, 1;; // in2 = count of 8 byte blocks to copy + +CopyBundlesLoop: + + cmp.eq p14, p15 = 0, in2;; // Check if done +(p14) br.sptk.few CopyBundlesDone;; + + ld8 loc2=[in0], 0x8;; // loc2 = source bytes + st8 [in1]=loc2;; // [in1] = destination bytes + fc in1;; // Flush instruction cache + sync.i;; // Ensure local and remote data/inst caches in sync + srlz.i;; // Ensure sync has been observed + add in1=0x8, in1;; // in1 = next destination + add in2=-1, in2;; // in2 = decrement 8 bytes blocks to copy + br.sptk.few CopyBundlesLoop;; + +CopyBundlesDone: + NESTED_RETURN + + .endp CopyBundles + + +///////////////////////////////////////////// +// +// Name: +// RelocateBundle +// +// Description: +// Relocates an instruction bundle by updating any ip-relative branch instructions. +// +// Arguments: +// in0 - Runtime address of bundle +// in1 - IP address of previous location of bundle +// in2 - IP address of new location of bundle +// +// Returns: +// in0 - 1 if successful or 0 if unsuccessful +// +// Notes: +// This routine examines all slots in the given bundle that are destined for the +// branch execution unit. If any of these slots contain an IP-relative branch +// namely instructions B1, B2, B3, or B6, the slot is fixed-up with a new relative +// address. Errors can occur if a branch cannot be reached. +// + .proc RelocateBundle + +RelocateBundle: + + NESTED_SETUP(3,2+4,3,0) + + mov loc2=SLOT0 // loc2 = slot index + mov loc5=in0;; // loc5 = runtime address of bundle + mov in0=1;; // in0 = success + +RelocateBundleNextSlot: + + cmp.ge p14, p15 = SLOT2, loc2;; // Check if maximum slot +(p15) br.sptk.few RelocateBundleDone + + mov out0=loc5;; // out0 = runtime address of bundle + br.call.sptk.few b0 = GetTemplate + mov loc3=out0;; // loc3 = instruction template + mov out0=loc5 // out0 = runtime address of bundle + mov out1=loc2;; // out1 = instruction slot number + br.call.sptk.few b0 = GetSlot + mov loc4=out0;; // loc4 = instruction encoding + mov out0=loc4 // out0 = instuction encoding + mov out1=loc2 // out1 = instruction slot number + mov out2=loc3;; // out2 = instruction template + br.call.sptk.few b0 = IsSlotBranch + cmp.eq p14, p15 = 1, out0;; // Check if branch slot +(p15) add loc2=1,loc2 // Increment slot +(p15) br.sptk.few RelocateBundleNextSlot + mov out0=loc4 // out0 = instuction encoding + mov out1=in1 // out1 = IP address of previous location + mov out2=in2;; // out2 = IP address of new location + br.call.sptk.few b0 = RelocateSlot + cmp.eq p14, p15 = 1, out1;; // Check if relocated slot +(p15) mov in0=0 // in0 = failure +(p15) br.sptk.few RelocateBundleDone + mov out2=out0;; // out2 = instruction encoding + mov out0=loc5 // out0 = runtime address of bundle + mov out1=loc2;; // out1 = instruction slot number + br.call.sptk.few b0 = SetSlot + add loc2=1,loc2;; // Increment slot + br.sptk.few RelocateBundleNextSlot + +RelocateBundleDone: + NESTED_RETURN + + .endp RelocateBundle + + +///////////////////////////////////////////// +// +// Name: +// RelocateSlot +// +// Description: +// Relocates an instruction bundle by updating any ip-relative branch instructions. +// +// Arguments: +// in0 - Instruction encoding (41-bits, right justified) +// in1 - IP address of previous location of bundle +// in2 - IP address of new location of bundle +// +// Returns: +// in0 - Instruction encoding (41-bits, right justified) +// in1 - 1 if successful otherwise 0 +// +// Notes: +// This procedure is a leaf routine +// + .proc RelocateSlot + +RelocateSlot: + NESTED_SETUP(3,2+5,0,0) + extr.u loc2=in0, 37, 4;; // loc2 = instruction opcode + cmp.eq p14, p15 = 4, loc2;; // IP-relative branch (B1) or + // IP-relative counted branch (B2) +(p15) cmp.eq p14, p15 = 5, loc2;; // IP-relative call (B3) +(p15) cmp.eq p14, p15 = 7, loc2;; // IP-relative predict (B6) +(p15) mov in1=1 // Instruction did not need to be reencoded +(p15) br.sptk.few RelocateSlotDone + tbit.nz p14, p15 = in0, 36;; // put relative offset sign bit in p14 + extr.u loc2=in0, 13, 20;; // loc2 = relative offset in instruction +(p14) movl loc3=0xfffffffffff00000;; // extend sign +(p14) or loc2=loc2, loc3;; + shl loc2=loc2,4;; // convert to byte offset instead of bundle offset + add loc3=loc2, in1;; // loc3 = physical address of branch target +(p14) sub loc2=r0,loc2;; // flip sign in loc2 if offset is negative + sub loc4=loc3,in2;; // loc4 = relative offset from new ip to branch target + cmp.lt p15, p14 = 0, loc4;; // get new sign bit +(p14) sub loc5=r0,loc4 // get absolute value of offset +(p15) mov loc5=loc4;; + movl loc6=0x0FFFFFF;; // maximum offset in bytes for ip-rel branch + cmp.gt p14, p15 = loc5, loc6;; // check to see we're not out of range for an ip-relative branch +(p14) br.sptk.few RelocateSlotError + cmp.lt p15, p14 = 0, loc4;; // store sign in p14 again +(p14) dep in0=-1,in0,36,1 // store sign bit in instruction +(p15) dep in0=0,in0,36,1 + shr loc4=loc4, 4;; // convert back to bundle offset + dep in0=loc4,in0,13,16;; // put first 16 bits of new offset into instruction + shr loc4=loc4,16;; + dep in0=loc4,in0,13+16,4 // put last 4 bits of new offset into instruction + mov in1=1;; // in1 = success + br.sptk.few RelocateSlotDone;; + +RelocateSlotError: + mov in1=0;; // in1 = failure + +RelocateSlotDone: + NESTED_RETURN + + .endp RelocateSlot + + +///////////////////////////////////////////// +// +// Name: +// IsSlotBranch +// +// Description: +// Determines if the given instruction is a branch instruction. +// +// Arguments: +// in0 - Instruction encoding (41-bits, right justified) +// in1 - Instruction slot number +// in2 - Bundle template +// +// Returns: +// in0 - 1 if branch or 0 if not branch +// +// Notes: +// This procedure is a leaf routine +// +// IsSlotBranch recognizes all branch instructions by looking at the provided template. +// The instruction encoding is only passed to this routine for future expansion. +// + .proc IsSlotBranch + +IsSlotBranch: + + NESTED_SETUP (3,2+0,0,0) + + mov in0=1;; // in0 = 1 which destroys the instruction + andcm in2=in2,in0;; // in2 = even template to reduce compares + mov in0=0;; // in0 = not a branch + cmp.eq p14, p15 = 0x16, in2;; // Template 0x16 is BBB +(p14) br.sptk.few IsSlotBranchTrue + cmp.eq p14, p15 = SLOT0, in1;; // Slot 0 has no other possiblities +(p14) br.sptk.few IsSlotBranchDone + cmp.eq p14, p15 = 0x12, in2;; // Template 0x12 is MBB +(p14) br.sptk.few IsSlotBranchTrue + cmp.eq p14, p15 = SLOT1, in1;; // Slot 1 has no other possiblities +(p14) br.sptk.few IsSlotBranchDone + cmp.eq p14, p15 = 0x10, in2;; // Template 0x10 is MIB +(p14) br.sptk.few IsSlotBranchTrue + cmp.eq p14, p15 = 0x18, in2;; // Template 0x18 is MMB +(p14) br.sptk.few IsSlotBranchTrue + cmp.eq p14, p15 = 0x1C, in2;; // Template 0x1C is MFB +(p14) br.sptk.few IsSlotBranchTrue + br.sptk.few IsSlotBranchDone + +IsSlotBranchTrue: + mov in0=1;; // in0 = branch + +IsSlotBranchDone: + NESTED_RETURN + + .endp IsSlotBranch + + +///////////////////////////////////////////// +// +// Name: +// GetTemplate +// +// Description: +// Retrieves the instruction template for an instruction bundle +// +// Arguments: +// in0 - Runtime address of bundle +// +// Returns: +// in0 - Instruction template (5-bits, right-justified) +// +// Notes: +// This procedure is a leaf routine +// + .proc GetTemplate + +GetTemplate: + + NESTED_SETUP (1,2+2,0,0) + + ld8 loc2=[in0], 0x8 // loc2 = first 8 bytes of branch bundle + movl loc3=MASK_0_4;; // loc3 = template mask + and loc2=loc2,loc3;; // loc2 = template, right justified + mov in0=loc2;; // in0 = template, right justified + + NESTED_RETURN + + .endp GetTemplate + + +///////////////////////////////////////////// +// +// Name: +// GetSlot +// +// Description: +// Gets the instruction encoding for an instruction slot and bundle +// +// Arguments: +// in0 - Runtime address of bundle +// in1 - Instruction slot (either 0, 1, or 2) +// +// Returns: +// in0 - Instruction encoding (41-bits, right justified) +// +// Notes: +// This procedure is a leaf routine +// +// Slot0 - [in0 + 0x8] Bits 45-5 +// Slot1 - [in0 + 0x8] Bits 63-46 and [in0] Bits 22-0 +// Slot2 - [in0] Bits 63-23 +// + .proc GetSlot + +GetSlot: + NESTED_SETUP (2,2+3,0,0) + + ld8 loc2=[in0], 0x8;; // loc2 = first 8 bytes of branch bundle + ld8 loc3=[in0];; // loc3 = second 8 bytes of branch bundle + cmp.eq p14, p15 = 2, in1;; // check if slot 2 specified + (p14) br.cond.sptk.few GetSlot2;; // get slot 2 + cmp.eq p14, p15 = 1, in1;; // check if slot 1 specified + (p14) br.cond.sptk.few GetSlot1;; // get slot 1 + +GetSlot0: + extr.u in0=loc2, 5, 45 // in0 = extracted slot 0 + br.sptk.few GetSlotDone;; + +GetSlot1: + extr.u in0=loc2, 46, 18 // in0 = bits 63-46 of loc2 right-justified + extr.u loc4=loc3, 0, 23;; // loc4 = bits 22-0 of loc3 right-justified + dep in0=loc4, in0, 18, 15;; + shr.u loc4=loc4,15;; + dep in0=loc4, in0, 33, 8;; // in0 = extracted slot 1 + br.sptk.few GetSlotDone;; + +GetSlot2: + extr.u in0=loc3, 23, 41;; // in0 = extracted slot 2 + +GetSlotDone: + NESTED_RETURN + + .endp GetSlot + + +///////////////////////////////////////////// +// +// Name: +// SetSlot +// +// Description: +// Sets the instruction encoding for an instruction slot and bundle +// +// Arguments: +// in0 - Runtime address of bundle +// in1 - Instruction slot (either 0, 1, or 2) +// in2 - Instruction encoding (41-bits, right justified) +// +// Returns: +// +// Notes: +// This procedure is a leaf routine +// + .proc SetSlot + +SetSlot: + NESTED_SETUP (3,2+3,0,0) + + ld8 loc2=[in0], 0x8;; // loc2 = first 8 bytes of bundle + ld8 loc3=[in0];; // loc3 = second 8 bytes of bundle + cmp.eq p14, p15 = 2, in1;; // check if slot 2 specified + (p14) br.cond.sptk.few SetSlot2;; // set slot 2 + cmp.eq p14, p15 = 1, in1;; // check if slot 1 specified + (p14) br.cond.sptk.few SetSlot1;; // set slot 1 + +SetSlot0: + dep loc2=0, loc2, 5, 41;; // remove old instruction from slot 0 + shl loc4=in2, 5;; // loc4 = new instruction ready to be inserted + or loc2=loc2, loc4;; // loc2 = updated first 8 bytes of bundle + add loc4=0x8,in0;; // loc4 = address to store first 8 bytes of bundle + st8 [loc4]=loc2 // [loc4] = updated bundle + br.sptk.few SetSlotDone;; + ;; + +SetSlot1: + dep loc2=0, loc2, 46, 18 // remove old instruction from slot 1 + dep loc3=0, loc3, 0, 23;; + shl loc4=in2, 46;; // loc4 = partial instruction ready to be inserted + or loc2=loc2, loc4;; // loc2 = updated first 8 bytes of bundle + add loc4=0x8,in0;; // loc4 = address to store first 8 bytes of bundle + st8 [loc4]=loc2;; // [loc4] = updated bundle + shr.u loc4=in2, 18;; // loc4 = partial instruction ready to be inserted + or loc3=loc3, loc4;; // loc3 = updated second 8 bytes of bundle + st8 [in0]=loc3;; // [in0] = updated bundle + br.sptk.few SetSlotDone;; + +SetSlot2: + dep loc3=0, loc3, 23, 41;; // remove old instruction from slot 2 + shl loc4=in2, 23;; // loc4 = instruction ready to be inserted + or loc3=loc3, loc4;; // loc3 = updated second 8 bytes of bundle + st8 [in0]=loc3;; // [in0] = updated bundle + +SetSlotDone: + + NESTED_RETURN + .endp SetSlot + + +///////////////////////////////////////////// +// +// Name: +// GetIva +// +// Description: +// C callable function to obtain the current value of IVA +// +// Returns: +// Current value if IVA + + ASM_GLOBAL GetIva + .proc GetIva +GetIva: + mov r8=cr2;; + br.ret.sptk.many b0 + + .endp GetIva + + +///////////////////////////////////////////// +// +// Name: +// ProgramInterruptFlags +// +// Description: +// C callable function to enable/disable interrupts +// +// Returns: +// Previous state of psr.ic +// + ASM_GLOBAL ProgramInterruptFlags + .proc ProgramInterruptFlags +ProgramInterruptFlags: + alloc loc0=1,2,0,0;; + mov loc0=psr + mov loc1=0x6000;; + and r8=loc0, loc1 // obtain current psr.ic and psr.i state + and in0=in0, loc1 // insure no extra bits set in input + andcm loc0=loc0,loc1;; // clear original psr.i and psr.ic + or loc0=loc0,in0;; // OR in new psr.ic value + mov psr.l=loc0;; // write new psr + srlz.d + br.ret.sptk.many b0 // return + + .endp ProgramInterruptFlags + + +///////////////////////////////////////////// +// +// Name: +// SpillContext +// +// Description: +// Saves system context to context record. +// +// Arguments: +// in0 = 512 byte aligned context record address +// in1 = original B0 +// in2 = original ar.bsp +// in3 = original ar.bspstore +// in4 = original ar.rnat +// in5 = original ar.pfs +// +// Notes: +// loc0 - scratch +// loc1 - scratch +// loc2 - temporary application unat storage +// loc3 - temporary exception handler unat storage + + .proc SpillContext + +SpillContext: + alloc loc0=6,4,0,0;; // alloc 6 input, 4 locals, 0 outs + mov loc2=ar.unat;; // save application context unat (spilled later) + mov ar.unat=r0;; // set UNAT=0 + st8.spill [in0]=r0,8;; + st8.spill [in0]=r1,8;; // save R1 - R31 + st8.spill [in0]=r2,8;; + st8.spill [in0]=r3,8;; + st8.spill [in0]=r4,8;; + st8.spill [in0]=r5,8;; + st8.spill [in0]=r6,8;; + st8.spill [in0]=r7,8;; + st8.spill [in0]=r8,8;; + st8.spill [in0]=r9,8;; + st8.spill [in0]=r10,8;; + st8.spill [in0]=r11,8;; + st8.spill [in0]=r12,8;; + st8.spill [in0]=r13,8;; + st8.spill [in0]=r14,8;; + st8.spill [in0]=r15,8;; + st8.spill [in0]=r16,8;; + st8.spill [in0]=r17,8;; + st8.spill [in0]=r18,8;; + st8.spill [in0]=r19,8;; + st8.spill [in0]=r20,8;; + st8.spill [in0]=r21,8;; + st8.spill [in0]=r22,8;; + st8.spill [in0]=r23,8;; + st8.spill [in0]=r24,8;; + st8.spill [in0]=r25,8;; + st8.spill [in0]=r26,8;; + st8.spill [in0]=r27,8;; + st8.spill [in0]=r28,8;; + st8.spill [in0]=r29,8;; + st8.spill [in0]=r30,8;; + st8.spill [in0]=r31,8;; + mov loc3=ar.unat;; // save debugger context unat (spilled later) + stf.spill [in0]=f2,16;; // save f2 - f31 + stf.spill [in0]=f3,16;; + stf.spill [in0]=f4,16;; + stf.spill [in0]=f5,16;; + stf.spill [in0]=f6,16;; + stf.spill [in0]=f7,16;; + stf.spill [in0]=f8,16;; + stf.spill [in0]=f9,16;; + stf.spill [in0]=f10,16;; + stf.spill [in0]=f11,16;; + stf.spill [in0]=f12,16;; + stf.spill [in0]=f13,16;; + stf.spill [in0]=f14,16;; + stf.spill [in0]=f15,16;; + stf.spill [in0]=f16,16;; + stf.spill [in0]=f17,16;; + stf.spill [in0]=f18,16;; + stf.spill [in0]=f19,16;; + stf.spill [in0]=f20,16;; + stf.spill [in0]=f21,16;; + stf.spill [in0]=f22,16;; + stf.spill [in0]=f23,16;; + stf.spill [in0]=f24,16;; + stf.spill [in0]=f25,16;; + stf.spill [in0]=f26,16;; + stf.spill [in0]=f27,16;; + stf.spill [in0]=f28,16;; + stf.spill [in0]=f29,16;; + stf.spill [in0]=f30,16;; + stf.spill [in0]=f31,16;; + mov loc0=pr;; // save predicates + st8.spill [in0]=loc0,8;; + st8.spill [in0]=in1,8;; // save b0 - b7... in1 already equals saved b0 + mov loc0=b1;; + st8.spill [in0]=loc0,8;; + mov loc0=b2;; + st8.spill [in0]=loc0,8;; + mov loc0=b3;; + st8.spill [in0]=loc0,8;; + mov loc0=b4;; + st8.spill [in0]=loc0,8;; + mov loc0=b5;; + st8.spill [in0]=loc0,8;; + mov loc0=b6;; + st8.spill [in0]=loc0,8;; + mov loc0=b7;; + st8.spill [in0]=loc0,8;; + mov loc0=ar.rsc;; // save ar.rsc + st8.spill [in0]=loc0,8;; + st8.spill [in0]=in2,8;; // save ar.bsp (in2) + st8.spill [in0]=in3,8;; // save ar.bspstore (in3) + st8.spill [in0]=in4,8;; // save ar.rnat (in4) + mov loc0=ar.fcr;; // save ar.fcr (ar21 - IA32 floating-point control register) + st8.spill [in0]=loc0,8;; + mov loc0=ar.eflag;; // save ar.eflag (ar24) + st8.spill [in0]=loc0,8;; + mov loc0=ar.csd;; // save ar.csd (ar25 - ia32 CS descriptor) + st8.spill [in0]=loc0,8;; + mov loc0=ar.ssd;; // save ar.ssd (ar26 - ia32 ss descriptor) + st8.spill [in0]=loc0,8;; + mov loc0=ar.cflg;; // save ar.cflg (ar27 - ia32 cr0 and cr4) + st8.spill [in0]=loc0,8;; + mov loc0=ar.fsr;; // save ar.fsr (ar28 - ia32 floating-point status register) + st8.spill [in0]=loc0,8;; + mov loc0=ar.fir;; // save ar.fir (ar29 - ia32 floating-point instruction register) + st8.spill [in0]=loc0,8;; + mov loc0=ar.fdr;; // save ar.fdr (ar30 - ia32 floating-point data register) + st8.spill [in0]=loc0,8;; + mov loc0=ar.ccv;; // save ar.ccv + st8.spill [in0]=loc0,8;; + st8.spill [in0]=loc2,8;; // save ar.unat (saved to loc2 earlier) + mov loc0=ar.fpsr;; // save floating point status register + st8.spill [in0]=loc0,8;; + st8.spill [in0]=in5,8;; // save ar.pfs + mov loc0=ar.lc;; // save ar.lc + st8.spill [in0]=loc0,8;; + mov loc0=ar.ec;; // save ar.ec + st8.spill [in0]=loc0,8;; + + // save control registers + mov loc0=cr.dcr;; // save dcr + st8.spill [in0]=loc0,8;; + mov loc0=cr.itm;; // save itm + st8.spill [in0]=loc0,8;; + mov loc0=cr.iva;; // save iva + st8.spill [in0]=loc0,8;; + mov loc0=cr.pta;; // save pta + st8.spill [in0]=loc0,8;; + mov loc0=cr.ipsr;; // save ipsr + st8.spill [in0]=loc0,8;; + mov loc0=cr.isr;; // save isr + st8.spill [in0]=loc0,8;; + mov loc0=cr.iip;; // save iip + st8.spill [in0]=loc0,8;; + mov loc0=cr.ifa;; // save ifa + st8.spill [in0]=loc0,8;; + mov loc0=cr.itir;; // save itir + st8.spill [in0]=loc0,8;; + mov loc0=cr.iipa;; // save iipa + st8.spill [in0]=loc0,8;; + mov loc0=cr.ifs;; // save ifs + st8.spill [in0]=loc0,8;; + mov loc0=cr.iim;; // save iim + st8.spill [in0]=loc0,8;; + mov loc0=cr.iha;; // save iha + st8.spill [in0]=loc0,8;; + + // save debug registers + mov loc0=dbr[r0];; // save dbr0 - dbr7 + st8.spill [in0]=loc0,8;; + movl loc1=1;; + mov loc0=dbr[loc1];; + st8.spill [in0]=loc0,8;; + movl loc1=2;; + mov loc0=dbr[loc1];; + st8.spill [in0]=loc0,8;; + movl loc1=3;; + mov loc0=dbr[loc1];; + st8.spill [in0]=loc0,8;; + movl loc1=4;; + mov loc0=dbr[loc1];; + st8.spill [in0]=loc0,8;; + movl loc1=5;; + mov loc0=dbr[loc1];; + st8.spill [in0]=loc0,8;; + movl loc1=6;; + mov loc0=dbr[loc1];; + st8.spill [in0]=loc0,8;; + movl loc1=7;; + mov loc0=dbr[loc1];; + st8.spill [in0]=loc0,8;; + mov loc0=ibr[r0];; // save ibr0 - ibr7 + st8.spill [in0]=loc0,8;; + movl loc1=1;; + mov loc0=ibr[loc1];; + st8.spill [in0]=loc0,8;; + movl loc1=2;; + mov loc0=ibr[loc1];; + st8.spill [in0]=loc0,8;; + movl loc1=3;; + mov loc0=ibr[loc1];; + st8.spill [in0]=loc0,8;; + movl loc1=4;; + mov loc0=ibr[loc1];; + st8.spill [in0]=loc0,8;; + movl loc1=5;; + mov loc0=ibr[loc1];; + st8.spill [in0]=loc0,8;; + movl loc1=6;; + mov loc0=ibr[loc1];; + st8.spill [in0]=loc0,8;; + movl loc1=7;; + mov loc0=ibr[loc1];; + st8.spill [in0]=loc0,8;; + st8.spill [in0]=loc3;; + + br.ret.sptk.few b0 + + .endp SpillContext + + +///////////////////////////////////////////// +// +// Name: +// FillContext +// +// Description: +// Restores register context from context record. +// +// Arguments: +// in0 = address of last element 512 byte aligned context record address +// in1 = modified B0 +// in2 = modified ar.bsp +// in3 = modified ar.bspstore +// in4 = modified ar.rnat +// in5 = modified ar.pfs +// +// Notes: +// loc0 - scratch +// loc1 - scratch +// loc2 - temporary application unat storage +// loc3 - temporary exception handler unat storage + + .proc FillContext +FillContext: + alloc loc0=6,4,0,0;; // alloc 6 inputs, 4 locals, 0 outs + ld8.fill loc3=[in0],-8;; // int_nat (nat bits for R1-31) + movl loc1=7;; // ibr7 + ld8.fill loc0=[in0],-8;; + mov ibr[loc1]=loc0;; + movl loc1=6;; // ibr6 + ld8.fill loc0=[in0],-8;; + mov ibr[loc1]=loc0;; + movl loc1=5;; // ibr5 + ld8.fill loc0=[in0],-8;; + mov ibr[loc1]=loc0;; + movl loc1=4;; // ibr4 + ld8.fill loc0=[in0],-8;; + mov ibr[loc1]=loc0;; + movl loc1=3;; // ibr3 + ld8.fill loc0=[in0],-8;; + mov ibr[loc1]=loc0;; + movl loc1=2;; // ibr2 + ld8.fill loc0=[in0],-8;; + mov ibr[loc1]=loc0;; + movl loc1=1;; // ibr1 + ld8.fill loc0=[in0],-8;; + mov ibr[loc1]=loc0;; + ld8.fill loc0=[in0],-8;; // ibr0 + mov ibr[r0]=loc0;; + movl loc1=7;; // dbr7 + ld8.fill loc0=[in0],-8;; + mov dbr[loc1]=loc0;; + movl loc1=6;; // dbr6 + ld8.fill loc0=[in0],-8;; + mov dbr[loc1]=loc0;; + movl loc1=5;; // dbr5 + ld8.fill loc0=[in0],-8;; + mov dbr[loc1]=loc0;; + movl loc1=4;; // dbr4 + ld8.fill loc0=[in0],-8;; + mov dbr[loc1]=loc0;; + movl loc1=3;; // dbr3 + ld8.fill loc0=[in0],-8;; + mov dbr[loc1]=loc0;; + movl loc1=2;; // dbr2 + ld8.fill loc0=[in0],-8;; + mov dbr[loc1]=loc0;; + movl loc1=1;; // dbr1 + ld8.fill loc0=[in0],-8;; + mov dbr[loc1]=loc0;; + ld8.fill loc0=[in0],-8;; // dbr0 + mov dbr[r0]=loc0;; + ld8.fill loc0=[in0],-8;; // iha + mov cr.iha=loc0;; + ld8.fill loc0=[in0],-8;; // iim + mov cr.iim=loc0;; + ld8.fill loc0=[in0],-8;; // ifs + mov cr.ifs=loc0;; + ld8.fill loc0=[in0],-8;; // iipa + mov cr.iipa=loc0;; + ld8.fill loc0=[in0],-8;; // itir + mov cr.itir=loc0;; + ld8.fill loc0=[in0],-8;; // ifa + mov cr.ifa=loc0;; + ld8.fill loc0=[in0],-8;; // iip + mov cr.iip=loc0;; + ld8.fill loc0=[in0],-8;; // isr + mov cr.isr=loc0;; + ld8.fill loc0=[in0],-8;; // ipsr + mov cr.ipsr=loc0;; + ld8.fill loc0=[in0],-8;; // pta + mov cr.pta=loc0;; + ld8.fill loc0=[in0],-8;; // iva + mov cr.iva=loc0;; + ld8.fill loc0=[in0],-8;; // itm + mov cr.itm=loc0;; + ld8.fill loc0=[in0],-8;; // dcr + mov cr.dcr=loc0;; + ld8.fill loc0=[in0],-8;; // ec + mov ar.ec=loc0;; + ld8.fill loc0=[in0],-8;; // lc + mov ar.lc=loc0;; + ld8.fill in5=[in0],-8;; // ar.pfs + ld8.fill loc0=[in0],-8;; // ar.fpsr + mov ar.fpsr=loc0;; + ld8.fill loc2=[in0],-8;; // ar.unat - restored later... + ld8.fill loc0=[in0],-8;; // ar.ccv + mov ar.ccv=loc0;; + ld8.fill loc0=[in0],-8;; // ar.fdr + mov ar.fdr=loc0;; + ld8.fill loc0=[in0],-8;; // ar.fir + mov ar.fir=loc0;; + ld8.fill loc0=[in0],-8;; // ar.fsr + mov ar.fsr=loc0;; + ld8.fill loc0=[in0],-8;; // ar.cflg + mov ar.cflg=loc0;; + ld8.fill loc0=[in0],-8;; // ar.ssd + mov ar.ssd=loc0;; + ld8.fill loc0=[in0],-8;; // ar.csd + mov ar.csd=loc0;; + ld8.fill loc0=[in0],-8;; // ar.eflag + mov ar.eflag=loc0;; + ld8.fill loc0=[in0],-8;; // ar.fcr + mov ar.fcr=loc0;; + ld8.fill in4=[in0],-8;; // ar.rnat + ld8.fill in3=[in0],-8;; // bspstore + ld8.fill in2=[in0],-8;; // bsp + ld8.fill loc0=[in0],-8;; // ar.rsc + mov ar.rsc=loc0;; + ld8.fill loc0=[in0],-8;; // B7 - B0 + mov b7=loc0;; + ld8.fill loc0=[in0],-8;; + mov b6=loc0;; + ld8.fill loc0=[in0],-8;; + mov b5=loc0;; + ld8.fill loc0=[in0],-8;; + mov b4=loc0;; + ld8.fill loc0=[in0],-8;; + mov b3=loc0;; + ld8.fill loc0=[in0],-8;; + mov b2=loc0;; + ld8.fill loc0=[in0],-8;; + mov b1=loc0;; + ld8.fill in1=[in0],-8;; // b0 is temporarily stored in in1 + ld8.fill loc0=[in0],-16;; // predicates + mov pr=loc0;; + ldf.fill f31=[in0],-16;; + ldf.fill f30=[in0],-16;; + ldf.fill f29=[in0],-16;; + ldf.fill f28=[in0],-16;; + ldf.fill f27=[in0],-16;; + ldf.fill f26=[in0],-16;; + ldf.fill f25=[in0],-16;; + ldf.fill f24=[in0],-16;; + ldf.fill f23=[in0],-16;; + ldf.fill f22=[in0],-16;; + ldf.fill f21=[in0],-16;; + ldf.fill f20=[in0],-16;; + ldf.fill f19=[in0],-16;; + ldf.fill f18=[in0],-16;; + ldf.fill f17=[in0],-16;; + ldf.fill f16=[in0],-16;; + ldf.fill f15=[in0],-16;; + ldf.fill f14=[in0],-16;; + ldf.fill f13=[in0],-16;; + ldf.fill f12=[in0],-16;; + ldf.fill f11=[in0],-16;; + ldf.fill f10=[in0],-16;; + ldf.fill f9=[in0],-16;; + ldf.fill f8=[in0],-16;; + ldf.fill f7=[in0],-16;; + ldf.fill f6=[in0],-16;; + ldf.fill f5=[in0],-16;; + ldf.fill f4=[in0],-16;; + ldf.fill f3=[in0],-16;; + ldf.fill f2=[in0],-8;; + mov ar.unat=loc3;; // restore unat (int_nat) before fill of general registers + ld8.fill r31=[in0],-8;; + ld8.fill r30=[in0],-8;; + ld8.fill r29=[in0],-8;; + ld8.fill r28=[in0],-8;; + ld8.fill r27=[in0],-8;; + ld8.fill r26=[in0],-8;; + ld8.fill r25=[in0],-8;; + ld8.fill r24=[in0],-8;; + ld8.fill r23=[in0],-8;; + ld8.fill r22=[in0],-8;; + ld8.fill r21=[in0],-8;; + ld8.fill r20=[in0],-8;; + ld8.fill r19=[in0],-8;; + ld8.fill r18=[in0],-8;; + ld8.fill r17=[in0],-8;; + ld8.fill r16=[in0],-8;; + ld8.fill r15=[in0],-8;; + ld8.fill r14=[in0],-8;; + ld8.fill r13=[in0],-8;; + ld8.fill r12=[in0],-8;; + ld8.fill r11=[in0],-8;; + ld8.fill r10=[in0],-8;; + ld8.fill r9=[in0],-8;; + ld8.fill r8=[in0],-8;; + ld8.fill r7=[in0],-8;; + ld8.fill r6=[in0],-8;; + ld8.fill r5=[in0],-8;; + ld8.fill r4=[in0],-8;; + ld8.fill r3=[in0],-8;; + ld8.fill r2=[in0],-8;; + ld8.fill r1=[in0],-8;; + mov ar.unat=loc2;; // restore application context unat + + br.ret.sptk.many b0 + + .endp FillContext + + +///////////////////////////////////////////// +// +// Name: +// HookHandler +// +// Description: +// Common branch target from hooked IVT entries. Runs in interrupt context. +// Responsible for saving and restoring context and calling common C +// handler. Banked registers running on bank 0 at entry. +// +// Arguments: +// All arguments are passed in banked registers: +// B0_REG = Original B0 +// SCRATCH_REG1 = IVT entry index +// +// Returns: +// Returns via rfi +// +// Notes: +// loc0 - scratch +// loc1 - scratch +// loc2 - vector number / mask +// loc3 - 16 byte aligned context record address +// loc4 - temporary storage of last address in context record + +HookHandler: + flushrs;; // Synch RSE with backing store + mov SCRATCH_REG2=ar.bsp // save interrupted context bsp + mov SCRATCH_REG3=ar.bspstore // save interrupted context bspstore + mov SCRATCH_REG4=ar.rnat // save interrupted context rnat + mov SCRATCH_REG6=cr.ifs;; // save IFS in case we need to chain... + cover;; // creates new frame, moves old + // CFM to IFS. + alloc SCRATCH_REG5=0,5,6,0 // alloc 5 locals, 6 outs + ;; + // save banked registers to locals + mov out1=B0_REG // out1 = Original B0 + mov out2=SCRATCH_REG2 // out2 = original ar.bsp + mov out3=SCRATCH_REG3 // out3 = original ar.bspstore + mov out4=SCRATCH_REG4 // out4 = original ar.rnat + mov out5=SCRATCH_REG5 // out5 = original ar.pfs + mov loc2=SCRATCH_REG1;; // loc2 = vector number + chain flag + bsw.1;; // switch banked registers to bank 1 + srlz.d // explicit serialize required + // now fill in context record structure + movl loc3=IpfContextBuf // Insure context record is aligned + add loc0=-0x200,r0;; // mask the lower 9 bits (align on 512 byte boundary) + and loc3=loc3,loc0;; + add loc3=0x200,loc3;; // move to next 512 byte boundary + // loc3 now contains the 512 byte aligned context record + // spill register context into context record + mov out0=loc3;; // Context record base in out0 + // original B0 in out1 already + // original ar.bsp in out2 already + // original ar.bspstore in out3 already + br.call.sptk.few b0=SpillContext;; // spill context + mov loc4=out0 // save modified address + + // At this point, the context has been saved to the context record and we're + // ready to call the C part of the handler... + + movl loc0=CommonHandler;; // obtain address of plabel + ld8 loc1=[loc0];; // get entry point of CommonHandler + mov b6=loc1;; // put it in a branch register + adds loc1= 8, loc0;; // index to GP in plabel + ld8 r1=[loc1];; // set up gp for C call + mov loc1=0xfffff;; // mask off so only vector bits are present + and out0=loc2,loc1;; // pass vector number (exception type) + mov out1=loc3;; // pass context record address + br.call.sptk.few b0=b6;; // call C handler + + // We've returned from the C call, so restore the context and either rfi + // back to interrupted thread, or chain into the SAL if this was an external interrupt + mov out0=loc4;; // pass address of last element in context record + br.call.sptk.few b0=FillContext;; // Fill context + mov b0=out1 // fill in b0 + mov ar.rnat=out4 + mov ar.pfs=out5 + + // Loadrs is necessary because the debugger may have changed some values in + // the backing store. The processor, however may not be aware that the + // stacked registers need to be reloaded from the backing store. Therefore, + // we explicitly cause the RSE to refresh the stacked register's contents + // from the backing store. + mov loc0=ar.rsc // get RSC value + mov loc1=ar.rsc // save it so we can restore it + movl loc3=0xffffffffc000ffff;; // create mask for clearing RSC.loadrs + and loc0=loc0,loc3;; // create value for RSC with RSC.loadrs==0 + mov ar.rsc=loc0;; // modify RSC + loadrs;; // invalidate register stack + mov ar.rsc=loc1;; // restore original RSC + + bsw.0;; // switch banked registers back to bank 0 + srlz.d;; // explicit serialize required + mov PR_REG=pr // save predicates - to be restored after chaining decision + mov B0_REG=b0 // save b0 - required by chain code + mov loc2=EXCPT_EXTERNAL_INTERRUPT;; + cmp.eq p7,p0=SCRATCH_REG1,loc2;; // check to see if this is the timer tick + (p7) br.cond.dpnt.few DO_CHAIN;; + +NO_CHAIN: + mov pr=PR_REG;; + rfi;; // we're outa here. + +DO_CHAIN: + mov pr=PR_REG + mov SCRATCH_REG1=cr.iva + mov SCRATCH_REG2=PATCH_RETURN_OFFSET;; + add SCRATCH_REG1=SCRATCH_REG1, SCRATCH_REG2;; + mov b0=SCRATCH_REG1;; + br.cond.sptk.few b0;; + +EndHookHandler: + + +///////////////////////////////////////////// +// +// Name: +// HookStub +// +// Description: +// HookStub will be copied from it's loaded location into the IVT when +// an IVT entry is hooked. The IVT entry does an indirect jump via B0 to +// HookHandler, which in turn calls into the default C handler, which calls +// the user-installed C handler. The calls return and HookHandler executes +// an rfi. +// +// Notes: +// Saves B0 to B0_REG +// Saves IVT index to SCRATCH_REG1 (immediate value is fixed up when code is copied +// to the IVT entry. + + ASM_GLOBAL HookStub + .proc HookStub +HookStub: + + mov B0_REG=b0 + movl SCRATCH_REG1=HookHandler;; + mov b0=SCRATCH_REG1;; + mov SCRATCH_REG1=0;;// immediate value is fixed up during install of handler to be the vector number + br.cond.sptk.few b0 + + .endp HookStub + + +///////////////////////////////////////////// +// The following code is moved into IVT entry 14 (offset 3400) which is reserved +// in the Itanium architecture. The patch code is located at the end of the +// IVT entry. + +PatchCode: + mov SCRATCH_REG0=psr + mov SCRATCH_REG6=cr.ipsr + mov PR_REG=pr + mov B0_REG=b0;; + + // turn off any virtual translations + movl SCRATCH_REG1 = ~( MASK(PSR_DT,1) | MASK(PSR_RT,1));; + and SCRATCH_REG1 = SCRATCH_REG0, SCRATCH_REG1;; + mov psr.l = SCRATCH_REG1;; + srlz.d + tbit.z p14, p15 = SCRATCH_REG6, PSR_IS;; // Check to see if we were + // interrupted from IA32 + // context. If so, bail out + // and chain to SAL immediately + (p15) br.cond.sptk.few Stub_IVT_Passthru;; + // we only want to take 1 out of 32 external interrupts to minimize the + // impact to system performance. Check our interrupt count and bail + // out if we're not up to 32 + movl SCRATCH_REG1=ExternalInterruptCount;; + ld8 SCRATCH_REG2=[SCRATCH_REG1];; // ExternalInterruptCount + tbit.z p14, p15 = SCRATCH_REG2, 5;; // bit 5 set? + (p14) add SCRATCH_REG2=1, SCRATCH_REG2;; // No? Then increment + // ExternalInterruptCount + // and Chain to SAL + // immediately + (p14) st8 [SCRATCH_REG1]=SCRATCH_REG2;; + (p14) br.cond.sptk.few Stub_IVT_Passthru;; + (p15) mov SCRATCH_REG2=0;; // Yes? Then reset + // ExternalInterruptCount + // and branch to + // HookHandler + (p15) st8 [SCRATCH_REG1]=SCRATCH_REG2;; + mov pr=PR_REG + movl SCRATCH_REG1=HookHandler;; // SCRATCH_REG1 = entrypoint of HookHandler + mov b0=SCRATCH_REG1;; // b0 = entrypoint of HookHandler + mov SCRATCH_REG1=EXCPT_EXTERNAL_INTERRUPT;; + br.sptk.few b0;; // branch to HookHandler + +PatchCodeRet: + // fake-up an rfi to get RSE back to being coherent and insure psr has + // original contents when interrupt occured, then exit to SAL + // at this point: + // cr.ifs has been modified by previous "cover" + // SCRATCH_REG6 has original cr.ifs + + mov SCRATCH_REG5=cr.ipsr + mov SCRATCH_REG4=cr.iip;; + mov cr.ipsr=SCRATCH_REG0 + mov SCRATCH_REG1=ip;; + add SCRATCH_REG1=0x30, SCRATCH_REG1;; + mov cr.iip=SCRATCH_REG1;; + rfi;; // rfi to next instruction + +Stub_RfiTarget: + mov cr.ifs=SCRATCH_REG6 + mov cr.ipsr=SCRATCH_REG5 + mov cr.iip=SCRATCH_REG4;; + +Stub_IVT_Passthru: + mov pr=PR_REG // pr = saved predicate registers + mov b0=B0_REG;; // b0 = saved b0 +EndPatchCode: + + +///////////////////////////////////////////// +// The following bundle is moved into IVT entry 14 (offset 0x3400) which is reserved +// in the Itanium architecture. This bundle will be the last bundle and will +// be located at offset 0x37F0 in the IVT. + +FailsafeBranch: +{ + .mib + nop.m 0 + nop.i 0 + br.sptk.few -(FAILSAFE_BRANCH_OFFSET - EXT_INT_ENTRY_OFFSET - 0x10) +} + + +///////////////////////////////////////////// +// The following bundle is moved into IVT entry 13 (offset 0x3000) which is the +// external interrupt. It branches to the patch code. + +PatchCodeNewBun0: +{ + .mib + nop.m 0 + nop.i 0 + br.cond.sptk.few PATCH_BRANCH +} diff --git a/Core/MdeModulePkg/Universal/DebugSupportDxe/Ipf/Common.i b/Core/MdeModulePkg/Universal/DebugSupportDxe/Ipf/Common.i new file mode 100644 index 0000000000..a11f780125 --- /dev/null +++ b/Core/MdeModulePkg/Universal/DebugSupportDxe/Ipf/Common.i @@ -0,0 +1,29 @@ +/// @file +/// This is set of useful macros. +/// +/// Copyright (c) 2006, 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. +/// +/// Module Name: Common.i +/// +/// + + +#define NESTED_SETUP(i,l,o,r) \ + alloc loc1=ar##.##pfs,i,l,o,r ; \ + mov loc0=b0 ;; + + +#define NESTED_RETURN \ + mov b0=loc0 ; \ + mov ar##.##pfs=loc1 ;; \ + br##.##ret##.##dpnt b0 ;; + +#define MASK(bp,value) (value << bp) + diff --git a/Core/MdeModulePkg/Universal/DebugSupportDxe/Ipf/Ds64Macros.i b/Core/MdeModulePkg/Universal/DebugSupportDxe/Ipf/Ds64Macros.i new file mode 100644 index 0000000000..8ce97f32c2 --- /dev/null +++ b/Core/MdeModulePkg/Universal/DebugSupportDxe/Ipf/Ds64Macros.i @@ -0,0 +1,78 @@ +/// @file +/// This is set of macros used in calculating offsets in the IVT. +/// +/// Copyright (c) 2006 - 2008, 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. +/// +/// + + +#define EXCPT_EXTERNAL_INTERRUPT 12 +#define MASK_0_4 0x000000000000001F // mask bits 0 through 4 +#define SLOT0 0 +#define SLOT1 1 +#define SLOT2 2 + +#define PSR_DT 17 +#define PSR_TB 26 +#define PSR_RT 27 +#define PSR_IS 34 +#define PSR_IT 36 +#define PSR_IC 13 +#define PSR_I 14 +#define PSR_SS 40 +#define PSR_BN 44 +#define PSR_RI_MASK 0x60000000000 + +#define EXCPT_EXTERNAL_INTERRUPT 12 + +#define SCRATCH_REG0 r23 +#define SCRATCH_REG1 r24 +#define SCRATCH_REG2 r25 +#define SCRATCH_REG3 r26 +#define SCRATCH_REG4 r27 +#define SCRATCH_REG5 r28 +#define SCRATCH_REG6 r29 +#define PR_REG r30 +#define B0_REG r31 + + +// EXT_INT_OFFSET is the offset of the external interrupt entry in the IVT +#define EXT_INT_ENTRY_OFFSET 0x03000 + +// PATCH_ENTRY_OFFSET is the offset into the IVT of the entry that is coopted (stolen) +// for use by the handler. The entire entry is restored when the handler is +// unloaded. +#define PATCH_ENTRY_OFFSET 0x03400 + +// PATCH_CODE_SIZE is the size of patch code +#define PATCH_CODE_SIZE (EndPatchCode - PatchCode) + +// A hard coded branch back into the external interrupt IVT entry's second bundle +// is put here, just in case the original bundle zero did not have a branch +// This is the last bundle in the reserved IVT entry +#define FAILSAFE_BRANCH_OFFSET (PATCH_ENTRY_OFFSET + 0x400 - 0x10) + +// the original external interrupt IVT entry bundle zero is copied and relocated +// here... also in the reserved IVT entry +// This is the second-to-last bundle in the reserved IVT entry +#define RELOCATED_EXT_INT (PATCH_ENTRY_OFFSET + 0x400 - 0x20) + +// The patch is actually stored at the end of IVT:PATCH_ENTRY. The PATCH_OFFSET +// is the offset into IVT where the patch is actually stored. It is carefully +// located so that when we run out of patch code, the next bundle is the +// relocated bundle 0 from the original external interrupt handler +#define PATCH_OFFSET (PATCH_ENTRY_OFFSET + 0x400 - ( EndPatchCode - PatchCode ) - 0x20) + +#define PATCH_RETURN_OFFSET (PATCH_ENTRY_OFFSET + 0x400 - ( EndPatchCode - PatchCodeRet ) - 0x20) + +// PATCH_BRANCH is used only in the new bundle that is placed at the beginning +// of the external interrupt IVT entry. +#define PATCH_BRANCH (PATCH_OFFSET - EXT_INT_ENTRY_OFFSET) + diff --git a/Core/MdeModulePkg/Universal/DebugSupportDxe/Ipf/PlDebugSupport.c b/Core/MdeModulePkg/Universal/DebugSupportDxe/Ipf/PlDebugSupport.c new file mode 100644 index 0000000000..44f59e8ec8 --- /dev/null +++ b/Core/MdeModulePkg/Universal/DebugSupportDxe/Ipf/PlDebugSupport.c @@ -0,0 +1,467 @@ +/** @file + IPF specific functions to support Debug Support protocol. + +Copyright (c) 2006 - 2010, 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. + +**/ + +#include "PlDebugSupport.h" + +BOOLEAN mInHandler = FALSE; + +// +// number of bundles to swap in ivt +// +#define NUM_BUNDLES_IN_STUB 5 +#define NUM_IVT_ENTRIES 64 + +typedef struct { + BUNDLE OrigBundles[NUM_BUNDLES_IN_STUB]; + CALLBACK_FUNC RegisteredCallback; +} IVT_ENTRY; + +IVT_ENTRY IvtEntryTable[NUM_IVT_ENTRIES]; + +// +// IPF context record is overallocated by 512 bytes to guarantee a 512 byte alignment exists +// within the buffer and still have a large enough buffer to hold a whole IPF context record. +// +UINT8 IpfContextBuf[sizeof (EFI_SYSTEM_CONTEXT_IPF) + 512]; + +// +// The PatchSaveBuffer is used to store the original bundles from the IVT where it is patched +// with the common handler. +// +UINT8 PatchSaveBuffer[0x400]; +UINTN ExternalInterruptCount; + + +/** + IPF specific DebugSupport driver initialization. + + Must be public because it's referenced from DebugSupport.c + + @retval EFI_SUCCESS Always. + +**/ +EFI_STATUS +PlInitializeDebugSupportDriver ( + VOID + ) +{ + ZeroMem (IvtEntryTable, sizeof (IvtEntryTable)); + ExternalInterruptCount = 0; + return EFI_SUCCESS; +} + +/** + Unload handler that is called during UnloadImage() - deallocates pool memory + used by the driver. + + Must be public because it's referenced from DebugSuport.c + + @param ImageHandle The firmware allocated handle for the EFI image. + + @retval EFI_SUCCESS Always. + +**/ +EFI_STATUS +EFIAPI +PlUnloadDebugSupportDriver ( + IN EFI_HANDLE ImageHandle + ) +{ + EFI_EXCEPTION_TYPE ExceptionType; + + for (ExceptionType = 0; ExceptionType < NUM_IVT_ENTRIES; ExceptionType++) { + ManageIvtEntryTable (ExceptionType, NULL, NULL); + } + + return EFI_SUCCESS; +} + +/** + C routine that is called for all registered exceptions. This is the main + exception dispatcher. + + Must be public because it's referenced from AsmFuncs.s. + + @param ExceptionType Specifies which processor exception. + @param Context System Context. +**/ +VOID +CommonHandler ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN EFI_SYSTEM_CONTEXT Context + ) +{ + DEBUG_CODE_BEGIN (); + if (mInHandler) { + DEBUG ((EFI_D_INFO, "ERROR: Re-entered debugger!\n" + " ExceptionType == %X\n" + " Context == %X\n" + " Context.SystemContextIpf->CrIip == %LX\n" + " Context.SystemContextIpf->CrIpsr == %LX\n" + " mInHandler == %X\n", + (INT32)ExceptionType, + Context, + Context.SystemContextIpf->CrIip, + Context.SystemContextIpf->CrIpsr, + mInHandler)); + } + DEBUG_CODE_END (); + + ASSERT (!mInHandler); + mInHandler = TRUE; + if (IvtEntryTable[ExceptionType].RegisteredCallback != NULL) { + if (ExceptionType != EXCEPT_IPF_EXTERNAL_INTERRUPT) { + IvtEntryTable[ExceptionType].RegisteredCallback (ExceptionType, Context.SystemContextIpf); + } else { + IvtEntryTable[ExceptionType].RegisteredCallback (Context.SystemContextIpf); + } + } else { + ASSERT (0); + } + + mInHandler = FALSE; +} + +/** + Given an integer number, return the physical address of the entry point in the IFT. + + @param HandlerIndex Index of the Handler + @param EntryPoint IFT Entrypoint + +**/ +VOID +GetHandlerEntryPoint ( + UINTN HandlerIndex, + VOID **EntryPoint + ) +{ + UINT8 *TempPtr; + + // + // get base address of IVT + // + TempPtr = GetIva (); + + if (HandlerIndex < 20) { + // + // first 20 provide 64 bundles per vector + // + TempPtr += 0x400 * HandlerIndex; + } else { + // + // the rest provide 16 bundles per vector + // + TempPtr += 0x5000 + 0x100 * (HandlerIndex - 20); + } + + *EntryPoint = (VOID *) TempPtr; +} + +/** + This is the worker function that uninstalls and removes all handlers. + + @param ExceptionType Specifies which processor exception. + @param NewBundles New Boundles. + @param NewCallback A pointer to the new function to be registered. + + @retval EFI_ALEADY_STARTED Ivt already hooked. + @retval EFI_SUCCESS Successfully uninstalled. + +**/ +EFI_STATUS +ManageIvtEntryTable ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN BUNDLE NewBundles[NUM_BUNDLES_IN_STUB], + IN CALLBACK_FUNC NewCallback + ) +{ + BUNDLE *B0Ptr; + UINT64 InterruptFlags; + EFI_TPL OldTpl; + + // + // Get address of bundle 0 + // + GetHandlerEntryPoint (ExceptionType, (VOID **) &B0Ptr); + + if (IvtEntryTable[ExceptionType].RegisteredCallback != NULL) { + // + // we've already installed to this vector + // + if (NewCallback != NULL) { + // + // if the input handler is non-null, error + // + return EFI_ALREADY_STARTED; + } else { + // + // else remove the previously installed handler + // + OldTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL); + InterruptFlags = ProgramInterruptFlags (DISABLE_INTERRUPTS); + if (ExceptionType == EXCEPT_IPF_EXTERNAL_INTERRUPT) { + UnchainExternalInterrupt (); + } else { + UnhookEntry (ExceptionType); + } + + ProgramInterruptFlags (InterruptFlags); + gBS->RestoreTPL (OldTpl); + // + // re-init IvtEntryTable + // + ZeroMem (&IvtEntryTable[ExceptionType], sizeof (IVT_ENTRY)); + } + } else { + // + // no user handler installed on this vector + // + if (NewCallback != NULL) { + OldTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL); + InterruptFlags = ProgramInterruptFlags (DISABLE_INTERRUPTS); + if (ExceptionType == EXCEPT_IPF_EXTERNAL_INTERRUPT) { + ChainExternalInterrupt (NewCallback); + } else { + HookEntry (ExceptionType, NewBundles, NewCallback); + } + + ProgramInterruptFlags (InterruptFlags); + gBS->RestoreTPL (OldTpl); + } + } + + return EFI_SUCCESS; +} + +/** + Saves original IVT contents and inserts a few new bundles which are fixed up + to store the ExceptionType and then call the common handler. + + @param ExceptionType Specifies which processor exception. + @param NewBundles New Boundles. + @param NewCallback A pointer to the new function to be hooked. + +**/ +VOID +HookEntry ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN BUNDLE NewBundles[4], + IN CALLBACK_FUNC NewCallback + ) +{ + BUNDLE *FixupBundle; + BUNDLE *B0Ptr; + + // + // Get address of bundle 0 + // + GetHandlerEntryPoint (ExceptionType, (VOID **) &B0Ptr); + + // + // copy original bundles from IVT to IvtEntryTable so we can restore them later + // + CopyMem ( + IvtEntryTable[ExceptionType].OrigBundles, + B0Ptr, + sizeof (BUNDLE) * NUM_BUNDLES_IN_STUB + ); + // + // insert new B0 + // + CopyMem (B0Ptr, NewBundles, sizeof (BUNDLE) * NUM_BUNDLES_IN_STUB); + + // + // fixup IVT entry so it stores its index and whether or not to chain... + // + FixupBundle = B0Ptr + 2; + FixupBundle->High |= ExceptionType << 36; + + InstructionCacheFlush (B0Ptr, 5); + IvtEntryTable[ExceptionType].RegisteredCallback = NewCallback; +} + +/** + Restores original IVT contents when unregistering a callback function. + + @param ExceptionType Specifies which processor exception. + +**/ +VOID +UnhookEntry ( + IN EFI_EXCEPTION_TYPE ExceptionType + ) +{ + BUNDLE *B0Ptr; + + // + // Get address of bundle 0 + // + GetHandlerEntryPoint (ExceptionType, (VOID **) &B0Ptr); + // + // restore original bundles in IVT + // + CopyMem ( + B0Ptr, + IvtEntryTable[ExceptionType].OrigBundles, + sizeof (BUNDLE) * NUM_BUNDLES_IN_STUB + ); + InstructionCacheFlush (B0Ptr, 5); +} + +/** + Sets up cache flush and calls assembly function to chain external interrupt. + + Records new callback in IvtEntryTable. + + @param NewCallback A pointer to the interrupt handle. + +**/ +VOID +ChainExternalInterrupt ( + IN CALLBACK_FUNC NewCallback + ) +{ + VOID *Start; + + Start = (VOID *) ((UINT8 *) GetIva () + 0x400 * EXCEPT_IPF_EXTERNAL_INTERRUPT + 0x400); + IvtEntryTable[EXCEPT_IPF_EXTERNAL_INTERRUPT].RegisteredCallback = NewCallback; + ChainHandler (); + InstructionCacheFlush (Start, 0x400); +} + +/** + Sets up cache flush and calls assembly function to restore external interrupt. + Removes registered callback from IvtEntryTable. + +**/ +VOID +UnchainExternalInterrupt ( + VOID + ) +{ + VOID *Start; + + Start = (VOID *) ((UINT8 *) GetIva () + 0x400 * EXCEPT_IPF_EXTERNAL_INTERRUPT + 0x400); + UnchainHandler (); + InstructionCacheFlush (Start, 0x400); + IvtEntryTable[EXCEPT_IPF_EXTERNAL_INTERRUPT].RegisteredCallback = NULL; +} + +/** + Returns the maximum value that may be used for the ProcessorIndex parameter in + RegisterPeriodicCallback() and RegisterExceptionCallback(). + + Hard coded to support only 1 processor for now. + + @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance. + @param MaxProcessorIndex Pointer to a caller-allocated UINTN in which the maximum supported + processor index is returned. Always 0 returned. + + @retval EFI_SUCCESS Always returned with **MaxProcessorIndex set to 0. + +**/ +EFI_STATUS +EFIAPI +GetMaximumProcessorIndex ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + OUT UINTN *MaxProcessorIndex + ) +{ + *MaxProcessorIndex = 0; + return (EFI_SUCCESS); +} + +/** + Registers a function to be called back periodically in interrupt context. + + @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance. + @param ProcessorIndex Specifies which processor the callback function applies to. + @param PeriodicCallback A pointer to a function of type PERIODIC_CALLBACK that is the main + periodic entry point of the debug agent. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ALREADY_STARTED Non-NULL PeriodicCallback parameter when a callback + function was previously registered. + @retval EFI_OUT_OF_RESOURCES System has insufficient memory resources to register new callback + function. +**/ +EFI_STATUS +EFIAPI +RegisterPeriodicCallback ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN EFI_PERIODIC_CALLBACK PeriodicCallback + ) +{ + return ManageIvtEntryTable (EXCEPT_IPF_EXTERNAL_INTERRUPT, NULL, PeriodicCallback); +} + +/** + Registers a function to be called when a given processor exception occurs. + + This code executes in boot services context. + + @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance. + @param ProcessorIndex Specifies which processor the callback function applies to. + @param ExceptionCallback A pointer to a function of type EXCEPTION_CALLBACK that is called + when the processor exception specified by ExceptionType occurs. + @param ExceptionType Specifies which processor exception to hook. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ALREADY_STARTED Non-NULL PeriodicCallback parameter when a callback + function was previously registered. + @retval EFI_OUT_OF_RESOURCES System has insufficient memory resources to register new callback + function. +**/ +EFI_STATUS +EFIAPI +RegisterExceptionCallback ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN EFI_EXCEPTION_CALLBACK ExceptionCallback, + IN EFI_EXCEPTION_TYPE ExceptionType + ) +{ + return ManageIvtEntryTable ( + ExceptionType, + (BUNDLE *) ((EFI_PLABEL *) HookStub)->EntryPoint, + ExceptionCallback + ); +} + +/** + Invalidates processor instruction cache for a memory range. Subsequent execution in this range + causes a fresh memory fetch to retrieve code to be executed. + + @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance. + @param ProcessorIndex Specifies which processor's instruction cache is to be invalidated. + @param Start Specifies the physical base of the memory range to be invalidated. + @param Length Specifies the minimum number of bytes in the processor's instruction + cache to invalidate. + + @retval EFI_SUCCESS Always returned. + +**/ +EFI_STATUS +EFIAPI +InvalidateInstructionCache ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN VOID *Start, + IN UINTN Length + ) +{ + InstructionCacheFlush (Start, Length); + return EFI_SUCCESS; +} diff --git a/Core/MdeModulePkg/Universal/DebugSupportDxe/Ipf/PlDebugSupport.h b/Core/MdeModulePkg/Universal/DebugSupportDxe/Ipf/PlDebugSupport.h new file mode 100644 index 0000000000..0cf29cadfb --- /dev/null +++ b/Core/MdeModulePkg/Universal/DebugSupportDxe/Ipf/PlDebugSupport.h @@ -0,0 +1,324 @@ +/** @file + IPF specific types, macros, and definitions for Debug Support Driver. + +Copyright (c) 2004 - 2010, 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. + +**/ + +#ifndef _PLDEBUG_SUPPORT_H_ +#define _PLDEBUG_SUPPORT_H_ + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#define DISABLE_INTERRUPTS 0UL + +#define EFI_ISA IsaIpf + +typedef struct { + UINT64 Low; + UINT64 High; +} BUNDLE; + +typedef +VOID +(*CALLBACK_FUNC) ( + ); + +/** + IPF specific DebugSupport driver initialization. + + Must be public because it's referenced from DebugSupport.c + + @retval EFI_SUCCESS Always. + +**/ +EFI_STATUS +PlInitializeDebugSupportDriver ( + VOID + ); + +/** + Unload handler that is called during UnloadImage() - deallocates pool memory + used by the driver. + + Must be public because it's referenced from DebugSuport.c + + @param ImageHandle The firmware allocated handle for the EFI image. + + @retval EFI_SUCCESS Always. + +**/ +EFI_STATUS +EFIAPI +PlUnloadDebugSupportDriver ( + IN EFI_HANDLE ImageHandle + ); + +/** + C callable function to obtain the current value of IVA. + + @return Current value of IVA. + +**/ +VOID * +GetIva ( + VOID + ); + +/** + C callable function that HookStub will be copied from it's loaded location into the IVT when + an IVT entry is hooked. + +**/ +VOID +HookStub ( + VOID + ); + +/** + C callable function to chain an interrupt handler. + +**/ +VOID +ChainHandler ( + VOID + ); + +/** + C callable function to unchain an interrupt handler. + +**/ +VOID +UnchainHandler ( + VOID + ); + +/** + C callable function to enable/disable interrupts. + + @param NewInterruptState New Interrupt State. + + @return Previous state of psr.ic. + +**/ +UINT64 +ProgramInterruptFlags ( + IN UINT64 NewInterruptState + ); + +/** + Flushes instruction cache for specified number of bytes. + + @param StartAddress Cache Start Address. + @param SizeInBytes Cache Size. + +**/ +VOID +InstructionCacheFlush ( + IN VOID *StartAddress, + IN UINTN SizeInBytes + ); + +/** + Returns the maximum value that may be used for the ProcessorIndex parameter in + RegisterPeriodicCallback() and RegisterExceptionCallback(). + + Hard coded to support only 1 processor for now. + + @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance. + @param MaxProcessorIndex Pointer to a caller-allocated UINTN in which the maximum supported + processor index is returned. Always 0 returned. + + @retval EFI_SUCCESS Always returned with **MaxProcessorIndex set to 0. + +**/ +EFI_STATUS +EFIAPI +GetMaximumProcessorIndex ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + OUT UINTN *MaxProcessorIndex + ); + +/** + Registers a function to be called back periodically in interrupt context. + + @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance. + @param ProcessorIndex Specifies which processor the callback function applies to. + @param PeriodicCallback A pointer to a function of type PERIODIC_CALLBACK that is the main + periodic entry point of the debug agent. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ALREADY_STARTED Non-NULL PeriodicCallback parameter when a callback + function was previously registered. + @retval EFI_OUT_OF_RESOURCES System has insufficient memory resources to register new callback + function. +**/ +EFI_STATUS +EFIAPI +RegisterPeriodicCallback ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN EFI_PERIODIC_CALLBACK PeriodicCallback + ); + +/** + Registers a function to be called when a given processor exception occurs. + + This code executes in boot services context. + + @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance. + @param ProcessorIndex Specifies which processor the callback function applies to. + @param ExceptionCallback A pointer to a function of type EXCEPTION_CALLBACK that is called + when the processor exception specified by ExceptionType occurs. + @param ExceptionType Specifies which processor exception to hook. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ALREADY_STARTED Non-NULL PeriodicCallback parameter when a callback + function was previously registered. + @retval EFI_OUT_OF_RESOURCES System has insufficient memory resources to register new callback + function. +**/ +EFI_STATUS +EFIAPI +RegisterExceptionCallback ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN EFI_EXCEPTION_CALLBACK ExceptionCallback, + IN EFI_EXCEPTION_TYPE ExceptionType + ); + +/** + Invalidates processor instruction cache for a memory range. Subsequent execution in this range + causes a fresh memory fetch to retrieve code to be executed. + + @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance. + @param ProcessorIndex Specifies which processor's instruction cache is to be invalidated. + @param Start Specifies the physical base of the memory range to be invalidated. + @param Length Specifies the minimum number of bytes in the processor's instruction + cache to invalidate. + + @retval EFI_SUCCESS Always returned. + +**/ +EFI_STATUS +EFIAPI +InvalidateInstructionCache ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN VOID *Start, + IN UINTN Length + ); + +/** + C routine that is called for all registered exceptions. This is the main + exception dispatcher. + + Must be public because it's referenced from AsmFuncs.s. + + @param ExceptionType Specifies which processor exception. + @param Context System Context. +**/ +VOID +CommonHandler ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN EFI_SYSTEM_CONTEXT Context + ); + +/** + This is the worker function that uninstalls and removes all handlers. + + @param ExceptionType Specifies which processor exception. + @param NewBundles New Boundles. + @param NewCallback A pointer to the new function to be registered. + + @retval EFI_ALEADY_STARTED Ivt already hooked. + @retval EFI_SUCCESS Successfully uninstalled. + +**/ +EFI_STATUS +ManageIvtEntryTable ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN BUNDLE NewBundles[4], + IN CALLBACK_FUNC NewCallback + ); + +/** + Saves original IVT contents and inserts a few new bundles which are fixed up + to store the ExceptionType and then call the common handler. + + @param ExceptionType Specifies which processor exception. + @param NewBundles New Boundles. + @param NewCallback A pointer to the new function to be hooked. + +**/ +VOID +HookEntry ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN BUNDLE NewBundles[4], + IN CALLBACK_FUNC NewCallback + ); + +/** + Restores original IVT contents when unregistering a callback function. + + @param ExceptionType Specifies which processor exception. + +**/ +VOID +UnhookEntry ( + IN EFI_EXCEPTION_TYPE ExceptionType + ); + +/** + Sets up cache flush and calls assembly function to chain external interrupt. + + Records new callback in IvtEntryTable. + + @param NewCallback A pointer to the interrupt handle. + +**/ +VOID +ChainExternalInterrupt ( + IN CALLBACK_FUNC NewCallback + ); + +/** + Sets up cache flush and calls assembly function to restore external interrupt. + Removes registered callback from IvtEntryTable. + +**/ +VOID +UnchainExternalInterrupt ( + VOID + ); + +/** + Given an integer number, return the physical address of the entry point in the IFT. + + @param HandlerIndex Index of the Handler + @param EntryPoint IFT Entrypoint + +**/ +VOID +GetHandlerEntryPoint ( + UINTN HandlerIndex, + VOID **EntryPoint + ); + +#endif diff --git a/Core/MdeModulePkg/Universal/DebugSupportDxe/X64/AsmFuncs.S b/Core/MdeModulePkg/Universal/DebugSupportDxe/X64/AsmFuncs.S new file mode 100644 index 0000000000..7f0919ee1b --- /dev/null +++ b/Core/MdeModulePkg/Universal/DebugSupportDxe/X64/AsmFuncs.S @@ -0,0 +1,551 @@ +///**@file +// Low leve x64 specific debug support functions. +// +// Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
+// Portions copyright (c) 2008 - 2009, Apple Inc. 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) + +.data + +ASM_PFX(StubSize): .long ASM_PFX(InterruptEntryStubEnd) - ASM_PFX(InterruptEntryStub) +ASM_PFX(AppRsp): .long 0x11111111 # ? + .long 0x11111111 # ? +ASM_PFX(DebugRsp): .long 0x22222222 # ? + .long 0x22222222 # ? +ASM_PFX(ExtraPush): .long 0x33333333 # ? + .long 0x33333333 # ? +ASM_PFX(ExceptData): .long 0x44444444 # ? + .long 0x44444444 # ? +ASM_PFX(Rflags): .long 0x55555555 # ? + .long 0x55555555 # ? +ASM_PFX(OrigVector): .long 0x66666666 # ? + .long 0x66666666 # ? + +// The declarations below define the memory region that will be used for the debug stack. +// The context record will be built by pushing register values onto this stack. +// It is imparitive that alignment be carefully managed, since the FXSTOR and +// FXRSTOR instructions will GP fault if their memory operand is not 16 byte aligned. +// +// The stub will switch stacks from the application stack to the debuger stack +// and pushes the exception number. +// +// Then we building the context record on the stack. Since the stack grows down, +// we push the fields of the context record from the back to the front. There +// are 336 bytes of stack used prior allocating the 512 bytes of stack to be +// used as the memory buffer for the fxstor instruction. Therefore address of +// the buffer used for the FXSTOR instruction is &Eax - 336 - 512, which +// must be 16 byte aligned. +// +// We carefully locate the stack to make this happen. +// +// For reference, the context structure looks like this: +// struct { +// UINT64 ExceptionData; +// FX_SAVE_STATE_X64 FxSaveState; // 512 bytes, must be 16 byte aligned +// UINT64 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; +// UINT64 Cr0, Cr1, Cr2, Cr3, Cr4, Cr8; +// UINT64 RFlags; +// UINT64 Ldtr, Tr; +// UINT64 Gdtr[2], Idtr[2]; +// UINT64 Rip; +// UINT64 Gs, Fs, Es, Ds, Cs, Ss; +// UINT64 Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax; +// UINT64 R8, R9, R10, R11, R12, R13, R14, R15; +// } SYSTEM_CONTEXT_X64; // 64 bit system context record + +.p2align 4 +DebugStackEnd : .ascii "DbgStkEnd >>>>>>" # 16 byte long string - must be 16 bytes to preserve alignment + .fill 0x1ffc, 4, 0x00000000 + # 32K should be enough stack + # This allocation is coocked to insure + # that the the buffer for the FXSTORE instruction + # will be 16 byte aligned also. + # +ASM_PFX(ExceptionNumber): .long 0x77777777 # first entry will be the vector number pushed by the stub + .long 0x77777777 # ? + +DebugStackBegin : .ascii "<<<< DbgStkBegin" # initial debug ESP == DebugStackBegin, set in stub + + +.text + +//------------------------------------------------------------------------------ +// BOOLEAN +// FxStorSupport ( +// void +// ) +// +// Abstract: Returns TRUE if FxStor instructions are supported +// +ASM_GLOBAL ASM_PFX(FxStorSupport) +ASM_PFX(FxStorSupport): +// +// cpuid corrupts rbx which must be preserved per the C calling convention +// + pushq %rbx + movq $1, %rax + cpuid + movl %edx, %eax + andq $0x01000000, %rax + shrq $24, %rax + popq %rbx + ret +//------------------------------------------------------------------------------ +// void +// Vect2Desc ( +// IA32_IDT_GATE_DESCRIPTOR * DestDesc, // rcx +// void (*Vector) (void) // rdx +// ) +// +// Abstract: Encodes an IDT descriptor with the given physical address +// +ASM_GLOBAL ASM_PFX(Vect2Desc) +ASM_PFX(Vect2Desc): + movq %rdx, %rax + movw %ax, (%rcx) # write bits 15..0 of offset + movw %cs, %dx + movw %dx, 2(%rcx) # SYS_CODE_SEL from GDT + movw $(0x0e00 | 0x8000), 4(%rcx) # type = 386 interrupt gate, present + shrq $16, %rax + movw %ax, 6(%rcx) # write bits 31..16 of offset + shrq $16, %rax + movl %eax, 8(%rcx) # write bits 63..32 of offset + + ret + +//------------------------------------------------------------------------------ +// InterruptEntryStub +// +// Abstract: This code is not a function, but is a small piece of code that is +// copied and fixed up once for each IDT entry that is hooked. +// +ASM_GLOBAL ASM_PFX(InterruptEntryStub) +ASM_PFX(InterruptEntryStub): + + pushq $0 # push vector number - will be modified before installed + jmp ASM_PFX(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) +// +// At this point, the stub has saved the current application stack esp into AppRsp +// and switched stacks to the debug stack, where it pushed the vector number +// +// The application stack looks like this: +// +// ... +// (last application stack entry) +// [16 bytes alignment, do not care it] +// SS from interrupted task +// RSP from interrupted task +// rflags from interrupted task +// CS from interrupted task +// RIP from interrupted task +// Error code <-------------------- Only present for some exeption types +// +// Vector Number <----------------- pushed in our IDT Entry +// + + +// 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 { +// UINT64 ExceptionData; +// FX_SAVE_STATE_X64 FxSaveState; +// UINT64 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; +// UINT64 Cr0, Cr2, Cr3, Cr4, Cr8; +// UINT64 RFlags; +// UINT64 Ldtr, Tr; +// UINT64 Gdtr[2], Idtr[2]; +// UINT64 Rip; +// UINT64 Gs, Fs, Es, Ds, Cs, Ss; +// UINT64 Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax; +// UINT64 R8, R9, R10, R11, R12, R13, R14, R15; +// } SYSTEM_CONTEXT_X64; // 64 +ASM_PFX(CommonIdtEntry): +// NOTE: we save rsp here to prevent compiler put rip reference cause error AppRsp + pushq %rax + movq (8)(%rsp), %rax # save vector number + movq %rax, ASM_PFX(ExceptionNumber)(%rip) # save vector number + popq %rax + addq $8, %rsp # pop vector number + movq %rsp, ASM_PFX(AppRsp)(%rip) # save stack top + movq DebugStackBegin(%rip), %rsp # switch to debugger stack + subq $8, %rsp # leave space for vector number +// UINT64 Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax; +// UINT64 R8, R9, R10, R11, R12, R13, R14, R15; + pushq %r15 + pushq %r14 + pushq %r13 + pushq %r12 + pushq %r11 + pushq %r10 + pushq %r9 + pushq %r8 + pushq %rax + pushq %rcx + pushq %rdx + pushq %rbx + pushq %rsp + pushq %rbp + pushq %rsi + pushq %rdi +// Save interrupt state rflags register... + pushfq + popq %rax + movq %rax, ASM_PFX(Rflags)(%rip) +// 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. + cmpl $0, ASM_PFX(ExceptionNumber)(%rip) + jz ExtraPushOne + cmpl $10, ASM_PFX(ExceptionNumber)(%rip) + jz ExtraPushOne + cmpl $11, ASM_PFX(ExceptionNumber)(%rip) + jz ExtraPushOne + cmpl $12, ASM_PFX(ExceptionNumber)(%rip) + jz ExtraPushOne + cmpl $13, ASM_PFX(ExceptionNumber)(%rip) + jz ExtraPushOne + cmpl $14, ASM_PFX(ExceptionNumber)(%rip) + jz ExtraPushOne + cmpl $17, ASM_PFX(ExceptionNumber)(%rip) + jz ExtraPushOne + movl $0, ASM_PFX(ExtraPush)(%rip) + movl $0, ASM_PFX(ExceptData)(%rip) + jmp ExtraPushDone +ExtraPushOne: + movl $1, ASM_PFX(ExtraPush)(%rip) + +// If there's some extra data, save it also, and modify the saved AppRsp to effectively +// pop this value off the application's stack. + movq ASM_PFX(AppRsp)(%rip), %rax + movq (%rax), %rbx + movq %rbx, ASM_PFX(ExceptData)(%rip) + addq $8, %rax + movq %rax, ASM_PFX(AppRsp)(%rip) + +ExtraPushDone: + +// The "push" above pushed the debug stack rsp. Since what we're actually doing +// is building the context record on the debug stack, we need to save the pushed +// debug RSP, and replace it with the application's last stack entry... + movq 24(%rsp), %rax + movq %rax, ASM_PFX(DebugRsp)(%rip) + movq ASM_PFX(AppRsp)(%rip), %rax + movq 24(%rax), %rax + # application stack has ss, rsp, rflags, cs, & rip, so + # last actual application stack entry is saved at offset + # 24 bytes from stack top. + movq %rax, 24(%rsp) + +// continue building context record +// UINT64 Gs, Fs, Es, Ds, Cs, Ss; insure high 16 bits of each is zero + mov %ss, %rax + pushq %rax + # CS from application is one entry back in application stack + movq ASM_PFX(AppRsp)(%rip), %rax + movzxw 8(%rax), %rax + pushq %rax + + mov %ds, %rax + pushq %rax + mov %es, %rax + pushq %rax + mov %fs, %rax + pushq %rax + mov %gs, %rax + pushq %rax +// UINT64 Rip; + # Rip from application is on top of application stack + movq ASM_PFX(AppRsp)(%rip), %rax + pushq (%rax) +// UINT64 Gdtr[2], Idtr[2]; + push $0 + push $0 + sidtq (%rsp) + push $0 + push $0 + sgdtq (%rsp) + +// UINT64 Ldtr, Tr; + xorq %rax, %rax + str %ax + pushq %rax + sldt %ax + pushq %rax + +// UINT64 RFlags; +// Rflags from application is two entries back in application stack + movq ASM_PFX(AppRsp)(%rip), %rax + pushq 16(%rax) +// UINT64 Cr0, Cr1, Cr2, Cr3, Cr4, Cr8; +// insure FXSAVE/FXRSTOR is enabled in CR4... +// ... while we're at it, make sure DE is also enabled... + movq %cr8, %rax + pushq %rax + movq %cr4, %rax + orq $0x208, %rax + movq %rax, %cr4 + pushq %rax + movq %cr3, %rax + pushq %rax + movq %cr2, %rax + pushq %rax + push $0 + movq %cr0, %rax + pushq %rax +// UINT64 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; + movq %dr7, %rax + pushq %rax +// clear Dr7 while executing debugger itself + xorq %rax, %rax + movq %rax, %dr7 + + movq %dr6, %rax + pushq %rax +// insure all status bits in dr6 are clear... + xorq %rax, %rax + movq %rax, %dr6 + + movq %dr3, %rax + pushq %rax + movq %dr2, %rax + pushq %rax + movq %dr1, %rax + pushq %rax + movq %dr0, %rax + pushq %rax + +// FX_SAVE_STATE_X64 FxSaveState; + subq $512, %rsp + movq %rsp, %rdi + # IMPORTANT!! The debug stack has been carefully constructed to + # insure that rsp and rdi are 16 byte aligned when we get here. + # They MUST be. If they are not, a GP fault will occur. + + # FXSTOR_RDI + fxsave (%rdi) + +// UEFI calling convention for x64 requires that Direction flag in EFLAGs is clear + cld + +// UINT64 ExceptionData; + movq ASM_PFX(ExceptData)(%rip), %rax + pushq %rax + +// call to C code which will in turn call registered handler +// pass in the vector number + movq %rsp, %rdx + movq ASM_PFX(ExceptionNumber)(%rip), %rcx + subq $40, %rsp + call ASM_PFX(InterruptDistrubutionHub) + addq $40, %rsp +// restore context... +// UINT64 ExceptionData; + addq $8, %rsp + +// FX_SAVE_STATE_X64 FxSaveState; + movq %rsp, %rsi + + # FXRSTOR_RSI + fxrstor (%rsi) + + addq $512, %rsp + +// UINT64 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; + popq %rax + movq %rax, %dr0 + popq %rax + movq %rax, %dr1 + popq %rax + movq %rax, %dr2 + popq %rax + movq %rax, %dr3 + +// skip restore of dr6. We cleared dr6 during the context save. + addq $8, %rsp + popq %rax + movq %rax, %dr7 + +// UINT64 Cr0, Cr1, Cr2, Cr3, Cr4, Cr8; + popq %rax + movq %rax, %cr0 + addq $8, %rsp + popq %rax + movq %rax, %cr2 + popq %rax + movq %rax, %cr3 + popq %rax + movq %rax, %cr4 + popq %rax + movq %rax, %cr8 +// UINT64 RFlags; + movq ASM_PFX(AppRsp)(%rip), %rax + popq 16(%rax) +// UINT64 Ldtr, Tr; +// UINT64 Gdtr[2], Idtr[2]; +// Best not let anyone mess with these particular registers... + addq $48, %rsp +// UINT64 Rip; + popq (%rax) + +// UINT64 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. +// + + popq %rax + # mov %rax, %gs + popq %rax + # mov %rax, %fs + popq %rax + mov %rax, %es + popq %rax + mov %rax, %ds + movq ASM_PFX(AppRsp)(%rip), %rax + popq 8(%rax) + popq %rax + mov %rax, %ss +## The next stuff to restore is the general purpose registers that were pushed +## using the "push" instruction. +## +## The value of RSP as stored in the context record is the application RSP +## including the 5 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. + + movq 24(%rsp), %rbx # move the potentially modified AppRsp into rbx + movq ASM_PFX(AppRsp)(%rip), %rax + movq 24(%rax), %rax + cmpq %rax, %rbx + je NoAppStackMove + + movq ASM_PFX(AppRsp)(%rip), %rax + movq (%rax), %rcx # RIP + movq %rcx, (%rbx) + + movq 8(%rax), %rcx # CS + movq %rcx, 8(%rbx) + + movq 16(%rax), %rcx # RFLAGS + movq %rcx, 16(%rbx) + + movq 24(%rax), %rcx # RSP + movq %rcx, 24(%rbx) + + movq 32(%rax), %rcx # SS + movq %rcx, 32(%rbx) + + movq %rbx, %rax # modify the saved AppRsp to the new AppRsp + movq %rax, ASM_PFX(AppRsp)(%rip) +NoAppStackMove: + movq ASM_PFX(DebugRsp)(%rip), %rax # restore the DebugRsp on the debug stack + # so our "pop" will not cause a stack switch + movq %rax, 24(%rsp) + + cmpl $0x068, ASM_PFX(ExceptionNumber)(%rip) + jne NoChain + +Chain: + +// Restore rflags so when we chain, the flags will be exactly as if we were never here. +// We gin up the stack to do an iretq so we can get ALL the flags. + movq ASM_PFX(AppRsp)(%rip), %rax + movq 40(%rax), %rbx + pushq %rbx + mov %ss, %rax + pushq %rax + movq %rsp, %rax + addq $16, %rax + pushq %rax + movq ASM_PFX(AppRsp)(%rip), %rax + movq 16(%rax), %rbx + andq $0xfffffffffffffcff, %rbx # special handling for IF and TF + pushq %rbx + mov %cs, %rax + pushq %rax + movq PhonyIretq(%rip), %rax + pushq %rax + iretq +PhonyIretq: + +// UINT64 Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax; +// UINT64 R8, R9, R10, R11, R12, R13, R14, R15; + popq %rdi + popq %rsi + popq %rbp + popq %rsp + popq %rbx + popq %rdx + popq %rcx + popq %rax + popq %r8 + popq %r9 + popq %r10 + popq %r11 + popq %r12 + popq %r13 + popq %r14 + popq %r15 + +// Switch back to application stack + movq ASM_PFX(AppRsp)(%rip), %rsp +// Jump to original handler + jmp ASM_PFX(OrigVector) +NoChain: +// UINT64 Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax; +// UINT64 R8, R9, R10, R11, R12, R13, R14, R15; + popq %rdi + popq %rsi + popq %rbp + popq %rsp + popq %rbx + popq %rdx + popq %rcx + popq %rax + popq %r8 + popq %r9 + popq %r10 + popq %r11 + popq %r12 + popq %r13 + popq %r14 + popq %r15 + +// Switch back to application stack + movq ASM_PFX(AppRsp)(%rip), %rsp + +// We're outa here... + iret diff --git a/Core/MdeModulePkg/Universal/DebugSupportDxe/X64/AsmFuncs.asm b/Core/MdeModulePkg/Universal/DebugSupportDxe/X64/AsmFuncs.asm new file mode 100644 index 0000000000..bce49ef762 --- /dev/null +++ b/Core/MdeModulePkg/Universal/DebugSupportDxe/X64/AsmFuncs.asm @@ -0,0 +1,596 @@ +;/** @file +; Low level x64 routines used by the debug support driver. +; +; Copyright (c) 2007 - 2011, 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. +; +;**/ + +EXCPT64_DIVIDE_ERROR EQU 0 +EXCPT64_DEBUG EQU 1 +EXCPT64_NMI EQU 2 +EXCPT64_BREAKPOINT EQU 3 +EXCPT64_OVERFLOW EQU 4 +EXCPT64_BOUND EQU 5 +EXCPT64_INVALID_OPCODE EQU 6 +EXCPT64_DOUBLE_FAULT EQU 8 +EXCPT64_INVALID_TSS EQU 10 +EXCPT64_SEG_NOT_PRESENT EQU 11 +EXCPT64_STACK_FAULT EQU 12 +EXCPT64_GP_FAULT EQU 13 +EXCPT64_PAGE_FAULT EQU 14 +EXCPT64_FP_ERROR EQU 16 +EXCPT64_ALIGNMENT_CHECK EQU 17 +EXCPT64_MACHINE_CHECK EQU 18 +EXCPT64_SIMD EQU 19 + +FXSTOR_FLAG EQU 01000000h ; bit cpuid 24 of feature flags + +;; The FXSTOR and FXRSTOR commands are used for saving and restoring the x87, +;; MMX, SSE, SSE2, etc registers. The initialization of the debugsupport driver +;; MUST check the CPUID feature flags to see that these instructions are available +;; and fail to init if they are not. + +;; fxstor [rdi] +FXSTOR_RDI MACRO + db 0fh, 0aeh, 00000111y ; mod = 00, reg/op = 000, r/m = 111 = [rdi] +ENDM + +;; fxrstor [rsi] +FXRSTOR_RSI MACRO + db 0fh, 0aeh, 00001110y ; mod = 00, reg/op = 001, r/m = 110 = [rsi] +ENDM + +data SEGMENT + +public OrigVector, InterruptEntryStub, StubSize, CommonIdtEntry, FxStorSupport + +StubSize dd InterruptEntryStubEnd - InterruptEntryStub +AppRsp dq 1111111111111111h ; ? +DebugRsp dq 2222222222222222h ; ? +ExtraPush dq 3333333333333333h ; ? +ExceptData dq 4444444444444444h ; ? +Rflags dq 5555555555555555h ; ? +OrigVector dq 6666666666666666h ; ? + +;; The declarations below define the memory region that will be used for the debug stack. +;; The context record will be built by pushing register values onto this stack. +;; It is imparitive that alignment be carefully managed, since the FXSTOR and +;; FXRSTOR instructions will GP fault if their memory operand is not 16 byte aligned. +;; +;; The stub will switch stacks from the application stack to the debuger stack +;; and pushes the exception number. +;; +;; Then we building the context record on the stack. Since the stack grows down, +;; we push the fields of the context record from the back to the front. There +;; are 336 bytes of stack used prior allocating the 512 bytes of stack to be +;; used as the memory buffer for the fxstor instruction. Therefore address of +;; the buffer used for the FXSTOR instruction is &Eax - 336 - 512, which +;; must be 16 byte aligned. +;; +;; We carefully locate the stack to make this happen. +;; +;; For reference, the context structure looks like this: +;; struct { +;; UINT64 ExceptionData; +;; FX_SAVE_STATE_X64 FxSaveState; // 512 bytes, must be 16 byte aligned +;; UINT64 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; +;; UINT64 Cr0, Cr1, Cr2, Cr3, Cr4, Cr8; +;; UINT64 RFlags; +;; UINT64 Ldtr, Tr; +;; UINT64 Gdtr[2], Idtr[2]; +;; UINT64 Rip; +;; UINT64 Gs, Fs, Es, Ds, Cs, Ss; +;; UINT64 Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax; +;; UINT64 R8, R9, R10, R11, R12, R13, R14, R15; +;; } SYSTEM_CONTEXT_X64; // 64 bit system context record + +align 16 +DebugStackEnd db "DbgStkEnd >>>>>>" ;; 16 byte long string - must be 16 bytes to preserve alignment + dd 1ffch dup (000000000h) ;; 32K should be enough stack + ;; This allocation is coocked to insure + ;; that the the buffer for the FXSTORE instruction + ;; will be 16 byte aligned also. + ;; +ExceptionNumber dq ? ;; first entry will be the vector number pushed by the stub + +DebugStackBegin db "<<<< DbgStkBegin" ;; initial debug ESP == DebugStackBegin, set in stub + +data ENDS + +text SEGMENT + +externdef InterruptDistrubutionHub:near + +;------------------------------------------------------------------------------ +; BOOLEAN +; FxStorSupport ( +; void +; ) +; +; Abstract: Returns TRUE if FxStor instructions are supported +; +FxStorSupport PROC PUBLIC + +; +; cpuid corrupts rbx which must be preserved per the C calling convention +; + push rbx + mov rax, 1 + cpuid + mov eax, edx + and rax, FXSTOR_FLAG + shr rax, 24 + pop rbx + ret +FxStorSupport ENDP + +;------------------------------------------------------------------------------ +; void +; Vect2Desc ( +; IA32_IDT_GATE_DESCRIPTOR * DestDesc, // rcx +; void (*Vector) (void) // rdx +; ) +; +; Abstract: Encodes an IDT descriptor with the given physical address +; +Vect2Desc PROC PUBLIC + + mov rax, rdx + mov word ptr [rcx], ax ; write bits 15..0 of offset + mov dx, cs + mov word ptr [rcx+2], dx ; SYS_CODE_SEL from GDT + mov word ptr [rcx+4], 0e00h OR 8000h ; type = 386 interrupt gate, present + shr rax, 16 + mov word ptr [rcx+6], ax ; write bits 31..16 of offset + shr rax, 16 + mov dword ptr [rcx+8], eax ; write bits 63..32 of offset + + ret + +Vect2Desc ENDP + + + +;------------------------------------------------------------------------------ +; InterruptEntryStub +; +; Abstract: This code is not a function, but is a small piece of code that is +; copied and fixed up once for each IDT entry that is hooked. +; +InterruptEntryStub:: + push 0 ; push vector number - will be modified before installed + db 0e9h ; jump rel32 + dd 0 ; fixed up to relative address of CommonIdtEntry +InterruptEntryStubEnd: + + + +;------------------------------------------------------------------------------ +; CommonIdtEntry +; +; Abstract: This code is not a function, but is the common part for all IDT +; vectors. +; +CommonIdtEntry:: +;; +;; At this point, the stub has saved the current application stack esp into AppRsp +;; and switched stacks to the debug stack, where it pushed the vector number +;; +;; The application stack looks like this: +;; +;; ... +;; (last application stack entry) +;; [16 bytes alignment, do not care it] +;; SS from interrupted task +;; RSP from interrupted task +;; rflags from interrupted task +;; CS from interrupted task +;; RIP from interrupted task +;; Error code <-------------------- Only present for some exeption types +;; +;; Vector Number <----------------- pushed in our IDT Entry +;; + + +;; 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 { +;; UINT64 ExceptionData; +;; FX_SAVE_STATE_X64 FxSaveState; +;; UINT64 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; +;; UINT64 Cr0, Cr2, Cr3, Cr4, Cr8; +;; UINT64 RFlags; +;; UINT64 Ldtr, Tr; +;; UINT64 Gdtr[2], Idtr[2]; +;; UINT64 Rip; +;; UINT64 Gs, Fs, Es, Ds, Cs, Ss; +;; UINT64 Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax; +;; UINT64 R8, R9, R10, R11, R12, R13, R14, R15; +;; } SYSTEM_CONTEXT_X64; // 64 bit system context record + +;; NOTE: we save rsp here to prevent compiler put rip reference cause error AppRsp + push rax + mov rax, qword ptr [rsp][8] ; save vector number + mov ExceptionNumber, rax ; save vector number + pop rax + add rsp, 8 ; pop vector number + mov AppRsp, rsp ; save stack top + mov rsp, offset DebugStackBegin ; switch to debugger stack + sub rsp, 8 ; leave space for vector number + +;; UINT64 Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax; +;; UINT64 R8, R9, R10, R11, R12, R13, R14, R15; + push r15 + push r14 + push r13 + push r12 + push r11 + push r10 + push r9 + push r8 + push rax + push rcx + push rdx + push rbx + push rsp + push rbp + push rsi + push rdi + +;; Save interrupt state rflags register... + pushfq + pop rax + mov qword ptr Rflags, rax + +;; 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. + cmp ExceptionNumber, EXCPT64_DOUBLE_FAULT + jz ExtraPushOne + cmp ExceptionNumber, EXCPT64_INVALID_TSS + jz ExtraPushOne + cmp ExceptionNumber, EXCPT64_SEG_NOT_PRESENT + jz ExtraPushOne + cmp ExceptionNumber, EXCPT64_STACK_FAULT + jz ExtraPushOne + cmp ExceptionNumber, EXCPT64_GP_FAULT + jz ExtraPushOne + cmp ExceptionNumber, EXCPT64_PAGE_FAULT + jz ExtraPushOne + cmp ExceptionNumber, EXCPT64_ALIGNMENT_CHECK + jz ExtraPushOne + mov ExtraPush, 0 + mov ExceptData, 0 + jmp ExtraPushDone +ExtraPushOne: + mov ExtraPush, 1 + +;; If there's some extra data, save it also, and modify the saved AppRsp to effectively +;; pop this value off the application's stack. + mov rax, AppRsp + mov rbx, [rax] + mov ExceptData, rbx + add rax, 8 + mov AppRsp, rax + +ExtraPushDone: + +;; The "push" above pushed the debug stack rsp. Since what we're actually doing +;; is building the context record on the debug stack, we need to save the pushed +;; debug RSP, and replace it with the application's last stack entry... + mov rax, [rsp + 24] + mov DebugRsp, rax + mov rax, AppRsp + mov rax, QWORD PTR [rax + 24] + ; application stack has ss, rsp, rflags, cs, & rip, so + ; last actual application stack entry is saved at offset + ; 24 bytes from stack top. + mov [rsp + 24], rax + +;; continue building context record +;; UINT64 Gs, Fs, Es, Ds, Cs, Ss; insure high 16 bits of each is zero + mov rax, ss + push rax + + ; CS from application is one entry back in application stack + mov rax, AppRsp + movzx rax, word ptr [rax + 8] + push rax + + mov rax, ds + push rax + mov rax, es + push rax + mov rax, fs + push rax + mov rax, gs + push rax + +;; UINT64 Rip; + ; Rip from application is on top of application stack + mov rax, AppRsp + push qword ptr [rax] + +;; UINT64 Gdtr[2], Idtr[2]; + push 0 + push 0 + sidt fword ptr [rsp] + push 0 + push 0 + sgdt fword ptr [rsp] + +;; UINT64 Ldtr, Tr; + xor rax, rax + str ax + push rax + sldt ax + push rax + +;; UINT64 RFlags; +;; Rflags from application is two entries back in application stack + mov rax, AppRsp + push qword ptr [rax + 16] + +;; UINT64 Cr0, Cr1, Cr2, Cr3, Cr4, Cr8; +;; insure FXSAVE/FXRSTOR is enabled in CR4... +;; ... while we're at it, make sure DE is also enabled... + mov rax, cr8 + push rax + mov rax, cr4 + or rax, 208h + mov cr4, rax + push rax + mov rax, cr3 + push rax + mov rax, cr2 + push rax + push 0 + mov rax, cr0 + push rax + +;; UINT64 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; + mov rax, dr7 + push rax +;; clear Dr7 while executing debugger itself + xor rax, rax + mov dr7, rax + + mov rax, dr6 + push rax +;; insure all status bits in dr6 are clear... + xor rax, rax + mov dr6, rax + + mov rax, dr3 + push rax + mov rax, dr2 + push rax + mov rax, dr1 + push rax + mov rax, dr0 + push rax + +;; FX_SAVE_STATE_X64 FxSaveState; + sub rsp, 512 + mov rdi, rsp + ; IMPORTANT!! The debug stack has been carefully constructed to + ; insure that rsp and rdi are 16 byte aligned when we get here. + ; They MUST be. If they are not, a GP fault will occur. + FXSTOR_RDI + +;; UEFI calling convention for x64 requires that Direction flag in EFLAGs is clear + cld + +;; UINT64 ExceptionData; + mov rax, ExceptData + push rax + +; call to C code which will in turn call registered handler +; pass in the vector number + mov rdx, rsp + mov rcx, ExceptionNumber + sub rsp, 40 + call InterruptDistrubutionHub + add rsp, 40 + +; restore context... +;; UINT64 ExceptionData; + add rsp, 8 + +;; FX_SAVE_STATE_X64 FxSaveState; + mov rsi, rsp + FXRSTOR_RSI + add rsp, 512 + +;; UINT64 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; + pop rax + mov dr0, rax + pop rax + mov dr1, rax + pop rax + mov dr2, rax + pop rax + mov dr3, rax +;; skip restore of dr6. We cleared dr6 during the context save. + add rsp, 8 + pop rax + mov dr7, rax + +;; UINT64 Cr0, Cr1, Cr2, Cr3, Cr4, Cr8; + pop rax + mov cr0, rax + add rsp, 8 + pop rax + mov cr2, rax + pop rax + mov cr3, rax + pop rax + mov cr4, rax + pop rax + mov cr8, rax + +;; UINT64 RFlags; + mov rax, AppRsp + pop qword ptr [rax + 16] + +;; UINT64 Ldtr, Tr; +;; UINT64 Gdtr[2], Idtr[2]; +;; Best not let anyone mess with these particular registers... + add rsp, 48 + +;; UINT64 Rip; + pop qword ptr [rax] + +;; UINT64 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 rax + ; mov gs, rax + pop rax + ; mov fs, rax + pop rax + mov es, rax + pop rax + mov ds, rax + mov rax, AppRsp + pop qword ptr [rax + 8] + pop rax + mov ss, rax + +;; The next stuff to restore is the general purpose registers that were pushed +;; using the "push" instruction. +;; +;; The value of RSP as stored in the context record is the application RSP +;; including the 5 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 rbx, [rsp + 24] ; move the potentially modified AppRsp into rbx + mov rax, AppRsp + mov rax, QWORD PTR [rax + 24] + cmp rbx, rax + je NoAppStackMove + + mov rax, AppRsp + mov rcx, [rax] ; RIP + mov [rbx], rcx + + mov rcx, [rax + 8] ; CS + mov [rbx + 8], rcx + + mov rcx, [rax + 16] ; RFLAGS + mov [rbx + 16], rcx + + mov rcx, [rax + 24] ; RSP + mov [rbx + 24], rcx + + mov rcx, [rax + 32] ; SS + mov [rbx + 32], rcx + + mov rax, rbx ; modify the saved AppRsp to the new AppRsp + mov AppRsp, rax +NoAppStackMove: + mov rax, DebugRsp ; restore the DebugRsp on the debug stack + ; so our "pop" will not cause a stack switch + mov [rsp + 24], rax + + cmp ExceptionNumber, 068h + jne NoChain + +Chain: + +;; Restore rflags so when we chain, the flags will be exactly as if we were never here. +;; We gin up the stack to do an iretq so we can get ALL the flags. + mov rax, AppRsp + mov rbx, [rax + 40] + push rbx + mov rax, ss + push rax + mov rax, rsp + add rax, 16 + push rax + mov rax, AppRsp + mov rbx, [rax + 16] + and rbx, NOT 300h ; special handling for IF and TF + push rbx + mov rax, cs + push rax + mov rax, offset PhonyIretq + push rax + iretq +PhonyIretq: + +;; UINT64 Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax; +;; UINT64 R8, R9, R10, R11, R12, R13, R14, R15; + pop rdi + pop rsi + pop rbp + pop rsp + pop rbx + pop rdx + pop rcx + pop rax + pop r8 + pop r9 + pop r10 + pop r11 + pop r12 + pop r13 + pop r14 + pop r15 + +;; Switch back to application stack + mov rsp, AppRsp + +;; Jump to original handler + jmp OrigVector + +NoChain: +;; UINT64 Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax; +;; UINT64 R8, R9, R10, R11, R12, R13, R14, R15; + pop rdi + pop rsi + pop rbp + pop rsp + pop rbx + pop rdx + pop rcx + pop rax + pop r8 + pop r9 + pop r10 + pop r11 + pop r12 + pop r13 + pop r14 + pop r15 + +;; Switch back to application stack + mov rsp, AppRsp + +;; We're outa here... + iretq +text ENDS + +END + + + diff --git a/Core/MdeModulePkg/Universal/DebugSupportDxe/X64/PlDebugSupport.h b/Core/MdeModulePkg/Universal/DebugSupportDxe/X64/PlDebugSupport.h new file mode 100644 index 0000000000..044e5d9d07 --- /dev/null +++ b/Core/MdeModulePkg/Universal/DebugSupportDxe/X64/PlDebugSupport.h @@ -0,0 +1,22 @@ +/** @file + X64 specific debug support macros. + +Copyright (c) 2006 - 2008, 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. + +**/ + +#ifndef _PLDEBUG_SUPPORT_H_ +#define _PLDEBUG_SUPPORT_H_ + +#include "Ia32/DebugSupport.h" + +#define EFI_ISA IsaX64 + +#endif diff --git a/Core/MdeModulePkg/Universal/DebugSupportDxe/X64/PlDebugSupportX64.c b/Core/MdeModulePkg/Universal/DebugSupportDxe/X64/PlDebugSupportX64.c new file mode 100644 index 0000000000..fa8869d287 --- /dev/null +++ b/Core/MdeModulePkg/Universal/DebugSupportDxe/X64/PlDebugSupportX64.c @@ -0,0 +1,146 @@ +/** @file + X64 specific functions to support Debug Support protocol. + +Copyright (c) 2008 - 2010, 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. + +**/ + +#include "PlDebugSupport.h" + +IA32_IDT_GATE_DESCRIPTOR NullDesc = {{0,0}}; + +/** + Get Interrupt Handle from IDT Gate Descriptor. + + @param IdtGateDecriptor IDT Gate Descriptor. + + @return Interrupt Handle stored in IDT Gate Descriptor. + +**/ +UINTN +GetInterruptHandleFromIdt ( + IN IA32_IDT_GATE_DESCRIPTOR *IdtGateDecriptor + ) +{ + UINTN InterruptHandle; + + // + // InterruptHandle 0-15 : OffsetLow + // InterruptHandle 16-31 : OffsetHigh + // InterruptHandle 32-63 : OffsetUpper + // + InterruptHandle = ((UINTN) IdtGateDecriptor->Bits.OffsetLow) | + (((UINTN) IdtGateDecriptor->Bits.OffsetHigh) << 16) | + (((UINTN) IdtGateDecriptor->Bits.OffsetUpper) << 32) ; + + return InterruptHandle; +} + +/** + Allocate pool for a new IDT entry stub. + + Copy the generic stub into the new buffer and fixup the vector number + and jump target address. + + @param ExceptionType This is the exception type that the new stub will be created + for. + @param Stub On successful exit, *Stub contains the newly allocated entry stub. + +**/ +VOID +CreateEntryStub ( + IN EFI_EXCEPTION_TYPE ExceptionType, + OUT VOID **Stub + ) +{ + UINT8 *StubCopy; + + StubCopy = *Stub; + + // + // Fixup the stub code for this vector + // + + // The stub code looks like this: + // + // 00000000 6A 00 push 0 ; push vector number - will be modified before installed + // 00000002 E9 db 0e9h ; jump rel32 + // 00000003 00000000 dd 0 ; fixed up to relative address of CommonIdtEntry + // + + // + // poke in the exception type so the second push pushes the exception type + // + StubCopy[0x1] = (UINT8) ExceptionType; + + // + // fixup the jump target to point to the common entry + // + *(UINT32 *) &StubCopy[0x3] = (UINT32)((UINTN) CommonIdtEntry - (UINTN) &StubCopy[StubSize]); + + return; +} + +/** + This is the main worker function that manages the state of the interrupt + handlers. It both installs and uninstalls interrupt handlers based on the + value of NewCallback. If NewCallback is NULL, then uninstall is indicated. + If NewCallback is non-NULL, then install is indicated. + + @param NewCallback If non-NULL, NewCallback specifies the new handler to register. + If NULL, specifies that the previously registered handler should + be uninstalled. + @param ExceptionType Indicates which entry to manage. + + @retval EFI_SUCCESS Process is ok. + @retval EFI_INVALID_PARAMETER Requested uninstalling a handler from a vector that has + no handler registered for it + @retval EFI_ALREADY_STARTED Requested install to a vector that already has a handler registered. + @retval others Possible return values are passed through from UnHookEntry and HookEntry. + +**/ +EFI_STATUS +ManageIdtEntryTable ( + CALLBACK_FUNC NewCallback, + EFI_EXCEPTION_TYPE ExceptionType + ) +{ + EFI_STATUS Status; + + Status = EFI_SUCCESS; + + if (CompareMem (&IdtEntryTable[ExceptionType].NewDesc, &NullDesc, sizeof (IA32_IDT_GATE_DESCRIPTOR)) != 0) { + // + // we've already installed to this vector + // + if (NewCallback != NULL) { + // + // if the input handler is non-null, error + // + Status = EFI_ALREADY_STARTED; + } else { + UnhookEntry (ExceptionType); + } + } else { + // + // no user handler installed on this vector + // + if (NewCallback == NULL) { + // + // if the input handler is null, error + // + Status = EFI_INVALID_PARAMETER; + } else { + HookEntry (ExceptionType, NewCallback); + } + } + + return Status; +} -- cgit v1.2.3