/** @file | |
PE/Coff Extra Action library instances. | |
Copyright (c) 2010, Intel Corporation. All rights reserved.<BR> | |
This program and the accompanying materials | |
are licensed and made available under the terms and conditions of the BSD License | |
which accompanies this distribution. The full text of the license may be found at | |
http://opensource.org/licenses/bsd-license.php. | |
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
**/ | |
#include <Base.h> | |
#include <Library/PeCoffExtraActionLib.h> | |
#include <Library/DebugLib.h> | |
#include <Library/BaseLib.h> | |
#include <Library/IoLib.h> | |
#include <Library/PcdLib.h> | |
#include <ImageDebugSupport.h> | |
#define DEBUG_LOAD_IMAGE_METHOD_IO_HW_BREAKPOINT 1 | |
#define DEBUG_LOAD_IMAGE_METHOD_SOFT_INT3 2 | |
/** | |
Check if the hardware breakpoint in Drx is enabled by checking the Lx and Gx bit in Dr7. | |
It assumes that DebugAgent will set both Lx and Gx bit when setting up the hardware breakpoint. | |
@param RegisterIndex Index of Dr register. The value range is from 0 to 3. | |
@param Dr7 Value of Dr7 register. | |
@return TRUE The hardware breakpoint specified in the Drx is enabled. | |
@return FALSE The hardware breakpoint specified in the Drx is disabled. | |
**/ | |
BOOLEAN | |
IsDrxEnabled ( | |
IN UINT8 RegisterIndex, | |
IN UINTN Dr7 | |
) | |
{ | |
return (BOOLEAN) (((Dr7 >> (RegisterIndex * 2)) & (BIT0 | BIT1)) == (BIT0 | BIT1)); | |
} | |
/** | |
Performs additional actions after a PE/COFF image has been loaded and relocated. | |
If ImageContext is NULL, then ASSERT(). | |
@param ImageContext Pointer to the image context structure that describes the | |
PE/COFF image that has already been loaded and relocated. | |
**/ | |
VOID | |
EFIAPI | |
PeCoffLoaderRelocateImageExtraAction ( | |
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext | |
) | |
{ | |
BOOLEAN InterruptState; | |
UINTN Dr0; | |
UINTN Dr1; | |
UINTN Dr2; | |
UINTN Dr3; | |
UINTN Dr7; | |
UINTN Cr4; | |
UINTN NewDr7; | |
UINT8 LoadImageMethod; | |
UINT8 DebugAgentStatus; | |
ASSERT (ImageContext != NULL); | |
if (ImageContext->PdbPointer != NULL) { | |
DEBUG((EFI_D_ERROR, " PDB = %a\n", ImageContext->PdbPointer)); | |
} | |
// | |
// Disable interrupts and save the current interrupt state | |
// | |
InterruptState = SaveAndDisableInterrupts (); | |
// | |
// Save Debug Register State | |
// | |
Dr0 = AsmReadDr0 (); | |
Dr1 = AsmReadDr1 (); | |
Dr2 = AsmReadDr2 (); | |
Dr3 = AsmReadDr3 (); | |
Dr7 = AsmReadDr7 (); | |
Cr4 = AsmReadCr4 (); | |
// | |
// DR0 = IMAGE_LOAD_SIGNATURE | |
// DR1 = The address of the Null-terminated ASCII string for the PE/COFF image's PDB file name | |
// DR2 = The pointer to the ImageContext structure | |
// DR3 = IO_PORT_BREAKPOINT_ADDRESS | |
// DR7 = Disables all HW breakpoints except for DR3 I/O port access of length 1 byte | |
// CR4 = Make sure DE(BIT3) is set | |
// | |
AsmWriteDr7 (0); | |
AsmWriteDr0 (IMAGE_LOAD_SIGNATURE); | |
AsmWriteDr1 ((UINTN)ImageContext->PdbPointer); | |
AsmWriteDr2 ((UINTN)ImageContext); | |
AsmWriteDr3 (IO_PORT_BREAKPOINT_ADDRESS); | |
LoadImageMethod = PcdGet8 (PcdDebugLoadImageMethod); | |
if (LoadImageMethod == DEBUG_LOAD_IMAGE_METHOD_IO_HW_BREAKPOINT) { | |
AsmWriteDr7 (0x20000480); | |
AsmWriteCr4 (Cr4 | BIT3); | |
// | |
// Do an IN from IO_PORT_BREAKPOINT_ADDRESS to generate a HW breakpoint until the port | |
// returns a read value other than DEBUG_AGENT_IMAGE_WAIT | |
// | |
do { | |
DebugAgentStatus = IoRead8 (IO_PORT_BREAKPOINT_ADDRESS); | |
} while (DebugAgentStatus == DEBUG_AGENT_IMAGE_WAIT); | |
} else if (LoadImageMethod == DEBUG_LOAD_IMAGE_METHOD_SOFT_INT3) { | |
// | |
// Generate a software break point. | |
// | |
CpuBreakpoint (); | |
} | |
// | |
// Restore Debug Register State only when Host didn't change it inside exception handler. | |
// E.g.: User halts the target and sets the HW breakpoint while target is | |
// in the above exception handler | |
// | |
NewDr7 = AsmReadDr7 (); | |
if (!IsDrxEnabled (0, NewDr7)) { | |
AsmWriteDr0 (Dr0); | |
} | |
if (!IsDrxEnabled (1, NewDr7)) { | |
AsmWriteDr1 (Dr1); | |
} | |
if (!IsDrxEnabled (2, NewDr7)) { | |
AsmWriteDr2 (Dr2); | |
} | |
if (!IsDrxEnabled (3, NewDr7)) { | |
AsmWriteDr3 (Dr3); | |
} | |
if (AsmReadCr4 () == (Cr4 | BIT3)) { | |
AsmWriteCr4 (Cr4); | |
} | |
if (NewDr7 == 0x20000480) { | |
AsmWriteDr7 (Dr7); | |
} | |
// | |
// Restore the interrupt state | |
// | |
SetInterruptState (InterruptState); | |
} | |
/** | |
Performs additional actions just before a PE/COFF image is unloaded. Any resources | |
that were allocated by PeCoffLoaderRelocateImageExtraAction() must be freed. | |
If ImageContext is NULL, then ASSERT(). | |
@param ImageContext Pointer to the image context structure that describes the | |
PE/COFF image that is being unloaded. | |
**/ | |
VOID | |
EFIAPI | |
PeCoffLoaderUnloadImageExtraAction ( | |
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext | |
) | |
{ | |
BOOLEAN InterruptState; | |
UINTN Dr0; | |
UINTN Dr1; | |
UINTN Dr2; | |
UINTN Dr3; | |
UINTN Dr7; | |
UINTN Cr4; | |
UINTN NewDr7; | |
UINT8 LoadImageMethod; | |
UINT8 DebugAgentStatus; | |
ASSERT (ImageContext != NULL); | |
if (ImageContext->PdbPointer != NULL) { | |
DEBUG((EFI_D_ERROR, " PDB = %a\n", ImageContext->PdbPointer)); | |
} | |
// | |
// Disable interrupts and save the current interrupt state | |
// | |
InterruptState = SaveAndDisableInterrupts (); | |
// | |
// Save Debug Register State | |
// | |
Dr0 = AsmReadDr0 (); | |
Dr1 = AsmReadDr1 (); | |
Dr2 = AsmReadDr2 (); | |
Dr3 = AsmReadDr3 (); | |
Dr7 = AsmReadDr7 (); | |
Cr4 = AsmReadCr4 (); | |
// | |
// DR0 = IMAGE_UNLOAD_SIGNATURE | |
// DR1 = The address of the Null-terminated ASCII string for the PE/COFF image's PDB file name | |
// DR2 = The pointer to the ImageContext structure | |
// DR3 = IO_PORT_BREAKPOINT_ADDRESS | |
// DR7 = Disables all HW breakpoints except for DR3 I/O port access of length 1 byte | |
// CR4 = Make sure DE(BIT3) is set | |
// | |
AsmWriteDr7 (0); | |
AsmWriteDr0 (IMAGE_UNLOAD_SIGNATURE); | |
AsmWriteDr1 ((UINTN)ImageContext->PdbPointer); | |
AsmWriteDr2 ((UINTN)ImageContext); | |
AsmWriteDr3 (IO_PORT_BREAKPOINT_ADDRESS); | |
LoadImageMethod = PcdGet8 (PcdDebugLoadImageMethod); | |
if (LoadImageMethod == DEBUG_LOAD_IMAGE_METHOD_IO_HW_BREAKPOINT) { | |
AsmWriteDr7 (0x20000480); | |
AsmWriteCr4 (Cr4 | BIT3); | |
// | |
// Do an IN from IO_PORT_BREAKPOINT_ADDRESS to generate a HW breakpoint until the port | |
// returns a read value other than DEBUG_AGENT_IMAGE_WAIT | |
// | |
do { | |
DebugAgentStatus = IoRead8 (IO_PORT_BREAKPOINT_ADDRESS); | |
} while (DebugAgentStatus == DEBUG_AGENT_IMAGE_WAIT); | |
} else if (LoadImageMethod == DEBUG_LOAD_IMAGE_METHOD_SOFT_INT3) { | |
// | |
// Generate a software break point. | |
// | |
CpuBreakpoint (); | |
} | |
// | |
// Restore Debug Register State only when Host didn't change it inside exception handler. | |
// E.g.: User halts the target and sets the HW breakpoint while target is | |
// in the above exception handler | |
// | |
NewDr7 = AsmReadDr7 (); | |
if (!IsDrxEnabled (0, NewDr7)) { | |
AsmWriteDr0 (Dr0); | |
} | |
if (!IsDrxEnabled (1, NewDr7)) { | |
AsmWriteDr1 (Dr1); | |
} | |
if (!IsDrxEnabled (2, NewDr7)) { | |
AsmWriteDr2 (Dr2); | |
} | |
if (!IsDrxEnabled (3, NewDr7)) { | |
AsmWriteDr3 (Dr3); | |
} | |
if (AsmReadCr4 () == (Cr4 | BIT3)) { | |
AsmWriteCr4 (Cr4); | |
} | |
if (NewDr7 == 0x20000480) { | |
AsmWriteDr7 (Dr7); | |
} | |
// | |
// Restore the interrupt state | |
// | |
SetInterruptState (InterruptState); | |
} |