| /** @file | |
| PE/Coff Extra Action library instances. | |
| Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include <PeCoffExtraActionLib.h> | |
| /** | |
| 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)); | |
| } | |
| /** | |
| Common routine to report the PE/COFF image loading/relocating or unloading event. | |
| If ImageContext is NULL, then ASSERT(). | |
| @param ImageContext Pointer to the image context structure that describes the | |
| PE/COFF image. | |
| @param Signature IMAGE_LOAD_SIGNATURE or IMAGE_UNLOAD_SIGNATURE. | |
| **/ | |
| VOID | |
| PeCoffLoaderExtraActionCommon ( | |
| IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext, | |
| IN UINTN Signature | |
| ) | |
| { | |
| BOOLEAN InterruptState; | |
| UINTN Dr0; | |
| UINTN Dr1; | |
| UINTN Dr2; | |
| UINTN Dr3; | |
| UINTN Dr7; | |
| UINTN Cr4; | |
| UINTN NewDr7; | |
| UINT8 LoadImageMethod; | |
| UINT8 DebugAgentStatus; | |
| IA32_DESCRIPTOR IdtDescriptor; | |
| IA32_IDT_GATE_DESCRIPTOR OriginalIdtEntry; | |
| BOOLEAN IdtEntryHooked; | |
| UINT32 RegEdx; | |
| ASSERT (ImageContext != NULL); | |
| if (ImageContext->PdbPointer != NULL) { | |
| DEBUG ((DEBUG_ERROR, " PDB = %a\n", ImageContext->PdbPointer)); | |
| } | |
| // | |
| // Disable interrupts and save the current interrupt state | |
| // | |
| InterruptState = SaveAndDisableInterrupts (); | |
| IdtEntryHooked = FALSE; | |
| LoadImageMethod = PcdGet8 (PcdDebugLoadImageMethod); | |
| if (LoadImageMethod == DEBUG_LOAD_IMAGE_METHOD_IO_HW_BREAKPOINT) { | |
| // | |
| // If the CPU does not support Debug Extensions(CPUID:01 EDX:BIT2) | |
| // then force use of DEBUG_LOAD_IMAGE_METHOD_SOFT_INT3 | |
| // | |
| AsmCpuid (1, NULL, NULL, NULL, &RegEdx); | |
| if ((RegEdx & BIT2) == 0) { | |
| LoadImageMethod = DEBUG_LOAD_IMAGE_METHOD_SOFT_INT3; | |
| } | |
| } | |
| AsmReadIdtr (&IdtDescriptor); | |
| if (LoadImageMethod == DEBUG_LOAD_IMAGE_METHOD_SOFT_INT3) { | |
| if (!CheckDebugAgentHandler (&IdtDescriptor, SOFT_INT_VECTOR_NUM)) { | |
| // | |
| // Do not trigger INT3 if Debug Agent did not setup IDT entries. | |
| // | |
| return; | |
| } | |
| } else { | |
| if (!CheckDebugAgentHandler (&IdtDescriptor, IO_HW_BREAKPOINT_VECTOR_NUM)) { | |
| // | |
| // Save and update IDT entry for INT1 | |
| // | |
| SaveAndUpdateIdtEntry1 (&IdtDescriptor, &OriginalIdtEntry); | |
| IdtEntryHooked = TRUE; | |
| } | |
| } | |
| // | |
| // Save Debug Register State | |
| // | |
| Dr0 = AsmReadDr0 (); | |
| Dr1 = AsmReadDr1 (); | |
| Dr2 = AsmReadDr2 (); | |
| Dr3 = AsmReadDr3 (); | |
| Dr7 = AsmReadDr7 () | BIT10; // H/w sets bit 10, some simulators don't | |
| Cr4 = AsmReadCr4 (); | |
| // | |
| // DR0 = 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 (BIT10); | |
| AsmWriteDr0 (Signature); | |
| AsmWriteDr1 ((UINTN)ImageContext->PdbPointer); | |
| AsmWriteDr2 ((UINTN)ImageContext); | |
| AsmWriteDr3 (IO_PORT_BREAKPOINT_ADDRESS); | |
| 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 () | BIT10; // H/w sets bit 10, some simulators don't | |
| if (!IsDrxEnabled (0, NewDr7) && ((AsmReadDr0 () == 0) || (AsmReadDr0 () == Signature))) { | |
| // | |
| // If user changed Dr3 (by setting HW bp in the above exception handler, | |
| // we will not set Dr0 to 0 in GO/STEP handler because the break cause is not IMAGE_LOAD/_UNLOAD. | |
| // | |
| AsmWriteDr0 (Dr0); | |
| } | |
| if (!IsDrxEnabled (1, NewDr7) && (AsmReadDr1 () == (UINTN)ImageContext->PdbPointer)) { | |
| AsmWriteDr1 (Dr1); | |
| } | |
| if (!IsDrxEnabled (2, NewDr7) && (AsmReadDr2 () == (UINTN)ImageContext)) { | |
| AsmWriteDr2 (Dr2); | |
| } | |
| if (!IsDrxEnabled (3, NewDr7) && (AsmReadDr3 () == IO_PORT_BREAKPOINT_ADDRESS)) { | |
| AsmWriteDr3 (Dr3); | |
| } | |
| if (LoadImageMethod == DEBUG_LOAD_IMAGE_METHOD_IO_HW_BREAKPOINT) { | |
| if (AsmReadCr4 () == (Cr4 | BIT3)) { | |
| AsmWriteCr4 (Cr4); | |
| } | |
| if (NewDr7 == 0x20000480) { | |
| AsmWriteDr7 (Dr7); | |
| } | |
| } else if (LoadImageMethod == DEBUG_LOAD_IMAGE_METHOD_SOFT_INT3) { | |
| if (NewDr7 == BIT10) { | |
| AsmWriteDr7 (Dr7); | |
| } | |
| } | |
| // | |
| // Restore original IDT entry for INT1 if it was hooked. | |
| // | |
| if (IdtEntryHooked) { | |
| RestoreIdtEntry1 (&IdtDescriptor, &OriginalIdtEntry); | |
| } | |
| // | |
| // Restore the interrupt state | |
| // | |
| SetInterruptState (InterruptState); | |
| } | |
| /** | |
| Performs additional actions after a PE/COFF image has been loaded and relocated. | |
| @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 | |
| ) | |
| { | |
| PeCoffLoaderExtraActionCommon (ImageContext, IMAGE_LOAD_SIGNATURE); | |
| } | |
| /** | |
| Performs additional actions just before a PE/COFF image is unloaded. Any resources | |
| that were allocated by PeCoffLoaderRelocateImageExtraAction() must be freed. | |
| @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 | |
| ) | |
| { | |
| PeCoffLoaderExtraActionCommon (ImageContext, IMAGE_UNLOAD_SIGNATURE); | |
| } |