/** @file Copyright (c) 2007 - 2016, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include "Edb.h" EFI_DEBUGGER_PRIVATE_DATA mDebuggerPrivate = { EFI_DEBUGGER_SIGNATURE, // Signature IsaEbc, // Isa (EBC_DEBUGGER_MAJOR_VERSION << 16) | EBC_DEBUGGER_MINOR_VERSION, // EfiDebuggerRevision (VM_MAJOR_VERSION << 16) | VM_MINOR_VERSION, // EbcVmRevision { EFI_DEBUGGER_CONFIGURATION_VERSION, &mDebuggerPrivate, }, // DebuggerConfiguration NULL, // DebugImageInfoTableHeader NULL, // Vol NULL, // PciRootBridgeIo mDebuggerCommandSet, // DebuggerCommandSet { 0 }, // DebuggerSymbolContext 0, // DebuggerBreakpointCount { { 0 } }, // DebuggerBreakpointContext 0, // CallStackEntryCount { { 0 } }, // CallStackEntry 0, // TraceEntryCount { { 0 } }, // TraceEntry { 0 }, // StepContext { 0 }, // GoTilContext 0, // InstructionScope EFI_DEBUG_DEFAULT_INSTRUCTION_NUMBER, // InstructionNumber EFI_DEBUG_FLAG_EBC_BOE | EFI_DEBUG_FLAG_EBC_BOT, // FeatureFlags 0, // StatusFlags FALSE, // EnablePageBreak NULL // BreakEvent }; CHAR16 *mExceptionStr[] = { L"EXCEPT_EBC_UNDEFINED", L"EXCEPT_EBC_DIVIDE_ERROR", L"EXCEPT_EBC_DEBUG", L"EXCEPT_EBC_BREAKPOINT", L"EXCEPT_EBC_OVERFLOW", L"EXCEPT_EBC_INVALID_OPCODE", L"EXCEPT_EBC_STACK_FAULT", L"EXCEPT_EBC_ALIGNMENT_CHECK", L"EXCEPT_EBC_INSTRUCTION_ENCODING", L"EXCEPT_EBC_BAD_BREAK", L"EXCEPT_EBC_SINGLE_STEP", }; /** Clear all the breakpoint. @param DebuggerPrivate EBC Debugger private data structure @param NeedRemove Whether need to remove all the breakpoint **/ VOID EdbClearAllBreakpoint ( IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, IN BOOLEAN NeedRemove ) { UINTN Index; // // Patch all the breakpoint // for (Index = 0; (Index < DebuggerPrivate->DebuggerBreakpointCount) && (Index < EFI_DEBUGGER_BREAKPOINT_MAX); Index++) { if (DebuggerPrivate->DebuggerBreakpointContext[Index].State) { CopyMem ( (VOID *)(UINTN)DebuggerPrivate->DebuggerBreakpointContext[Index].BreakpointAddress, &DebuggerPrivate->DebuggerBreakpointContext[Index].OldInstruction, sizeof (UINT16) ); } } // // Zero Breakpoint context, if need to remove all breakpoint // if (NeedRemove) { DebuggerPrivate->DebuggerBreakpointCount = 0; ZeroMem (DebuggerPrivate->DebuggerBreakpointContext, sizeof (DebuggerPrivate->DebuggerBreakpointContext)); } // // Done // return; } /** Set all the breakpoint. @param DebuggerPrivate EBC Debugger private data structure **/ VOID EdbSetAllBreakpoint ( IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate ) { UINTN Index; UINT16 Data16; // // Set all the breakpoint (BREAK(3) : 0x0300) // Data16 = 0x0300; for (Index = 0; (Index < DebuggerPrivate->DebuggerBreakpointCount) && (Index < EFI_DEBUGGER_BREAKPOINT_MAX); Index++) { if (DebuggerPrivate->DebuggerBreakpointContext[Index].State) { CopyMem ( (VOID *)(UINTN)DebuggerPrivate->DebuggerBreakpointContext[Index].BreakpointAddress, &Data16, sizeof (UINT16) ); } } // // Check if current break is caused by breakpoint set. // If so, we need to patch memory back to let user see the real memory. // if (DebuggerPrivate->DebuggerBreakpointContext[EFI_DEBUGGER_BREAKPOINT_MAX].BreakpointAddress != 0) { CopyMem ( (VOID *)(UINTN)DebuggerPrivate->DebuggerBreakpointContext[EFI_DEBUGGER_BREAKPOINT_MAX].BreakpointAddress, &DebuggerPrivate->DebuggerBreakpointContext[EFI_DEBUGGER_BREAKPOINT_MAX].OldInstruction, sizeof (UINT16) ); DebuggerPrivate->StatusFlags &= ~EFI_DEBUG_FLAG_EBC_B_BP; } // // Done // return; } /** Check all the breakpoint, if match, then set status flag, and record current breakpoint. Then clear all breakpoint to let user see a clean memory @param DebuggerPrivate EBC Debugger private data structure @param SystemContext EBC system context. **/ VOID EdbCheckBreakpoint ( IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, IN EFI_SYSTEM_CONTEXT SystemContext ) { UINT64 Address; UINTN Index; BOOLEAN IsHitBreakpoint; // // Roll back IP for breakpoint instruction (BREAK(3) : 0x0300) // Address = SystemContext.SystemContextEbc->Ip - sizeof (UINT16); // // Check if the breakpoint is hit // IsHitBreakpoint = FALSE; for (Index = 0; (Index < DebuggerPrivate->DebuggerBreakpointCount) && (Index < EFI_DEBUGGER_BREAKPOINT_MAX); Index++) { if ((DebuggerPrivate->DebuggerBreakpointContext[Index].BreakpointAddress == Address) && (DebuggerPrivate->DebuggerBreakpointContext[Index].State)) { IsHitBreakpoint = TRUE; break; } } if (IsHitBreakpoint) { // // If hit, record current breakpoint // DebuggerPrivate->DebuggerBreakpointContext[EFI_DEBUGGER_BREAKPOINT_MAX] = DebuggerPrivate->DebuggerBreakpointContext[Index]; DebuggerPrivate->DebuggerBreakpointContext[EFI_DEBUGGER_BREAKPOINT_MAX].State = TRUE; // // Update: IP and Instruction (NOTE: Since we not allow set breakpoint to BREAK 3, this update is safe) // SystemContext.SystemContextEbc->Ip = Address; // // Set Flags // DebuggerPrivate->StatusFlags |= EFI_DEBUG_FLAG_EBC_BP; } else { // // If not hit, check whether current IP is in breakpoint list, // because STEP will be triggered before execute the instruction. // We should not patch it in de-init. // Address = SystemContext.SystemContextEbc->Ip; // // Check if the breakpoint is hit // IsHitBreakpoint = FALSE; for (Index = 0; (Index < DebuggerPrivate->DebuggerBreakpointCount) && (Index < EFI_DEBUGGER_BREAKPOINT_MAX); Index++) { if ((DebuggerPrivate->DebuggerBreakpointContext[Index].BreakpointAddress == Address) && (DebuggerPrivate->DebuggerBreakpointContext[Index].State)) { IsHitBreakpoint = TRUE; break; } } if (IsHitBreakpoint) { // // If hit, record current breakpoint // CopyMem ( &DebuggerPrivate->DebuggerBreakpointContext[EFI_DEBUGGER_BREAKPOINT_MAX], &DebuggerPrivate->DebuggerBreakpointContext[Index], sizeof (DebuggerPrivate->DebuggerBreakpointContext[EFI_DEBUGGER_BREAKPOINT_MAX]) ); DebuggerPrivate->DebuggerBreakpointContext[EFI_DEBUGGER_BREAKPOINT_MAX].State = TRUE; // // Do not set Breakpoint flag. We record the address here just let it not patch breakpoint address when de-init. // } else { // // Zero current breakpoint // ZeroMem ( &DebuggerPrivate->DebuggerBreakpointContext[EFI_DEBUGGER_BREAKPOINT_MAX], sizeof (DebuggerPrivate->DebuggerBreakpointContext[EFI_DEBUGGER_BREAKPOINT_MAX]) ); } } // // Done // return; } /** clear all the symbol. @param DebuggerPrivate EBC Debugger private data structure **/ VOID EdbClearSymbol ( IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate ) { EFI_DEBUGGER_SYMBOL_CONTEXT *DebuggerSymbolContext; EFI_DEBUGGER_SYMBOL_OBJECT *Object; UINTN ObjectIndex; UINTN Index; // // Go throuth each object // DebuggerSymbolContext = &DebuggerPrivate->DebuggerSymbolContext; for (ObjectIndex = 0; ObjectIndex < DebuggerSymbolContext->ObjectCount; ObjectIndex++) { Object = &DebuggerSymbolContext->Object[ObjectIndex]; // // Go throuth each entry // for (Index = 0; Index < Object->EntryCount; Index++) { ZeroMem (&Object->Entry[Index], sizeof (Object->Entry[Index])); } ZeroMem (Object->Name, sizeof (Object->Name)); Object->EntryCount = 0; Object->BaseAddress = 0; Object->StartEntrypointRVA = 0; Object->MainEntrypointRVA = 0; // // Free source buffer // for (Index = 0; Object->SourceBuffer[Index] != NULL; Index++) { gBS->FreePool (Object->SourceBuffer[Index]); Object->SourceBuffer[Index] = NULL; } } DebuggerSymbolContext->ObjectCount = 0; return; } /** Initialize Debugger private data structure @param DebuggerPrivate EBC Debugger private data structure @param ExceptionType Exception type. @param SystemContext EBC system context. @param Initialized Whether the DebuggerPrivate data is initialized. **/ EFI_STATUS InitDebuggerPrivateData ( IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, IN EFI_EXCEPTION_TYPE ExceptionType, IN EFI_SYSTEM_CONTEXT SystemContext, IN BOOLEAN Initialized ) { // // clear STEP flag in any condition. // if (SystemContext.SystemContextEbc->Flags & ((UINT64)VMFLAGS_STEP)) { SystemContext.SystemContextEbc->Flags &= ~((UINT64)VMFLAGS_STEP); } if (!Initialized) { // // Initialize everything // DebuggerPrivate->InstructionNumber = EFI_DEBUG_DEFAULT_INSTRUCTION_NUMBER; DebuggerPrivate->DebuggerBreakpointCount = 0; ZeroMem (DebuggerPrivate->DebuggerBreakpointContext, sizeof (DebuggerPrivate->DebuggerBreakpointContext)); // DebuggerPrivate->StatusFlags = 0; DebuggerPrivate->DebuggerSymbolContext.DisplaySymbol = TRUE; DebuggerPrivate->DebuggerSymbolContext.DisplayCodeOnly = FALSE; DebuggerPrivate->DebuggerSymbolContext.ObjectCount = 0; } else { // // Already initialized, just check Breakpoint here. // if (ExceptionType == EXCEPT_EBC_BREAKPOINT) { EdbCheckBreakpoint (DebuggerPrivate, SystemContext); } // // Clear all breakpoint // EdbClearAllBreakpoint (DebuggerPrivate, FALSE); } // // Set Scope to currentl IP. (Note: Check Breakpoint may change Ip) // DebuggerPrivate->InstructionScope = SystemContext.SystemContextEbc->Ip; // // Done // return EFI_SUCCESS; } /** De-initialize Debugger private data structure. @param DebuggerPrivate EBC Debugger private data structure @param ExceptionType Exception type. @param SystemContext EBC system context. @param Initialized Whether the DebuggerPrivate data is initialized. **/ EFI_STATUS DeinitDebuggerPrivateData ( IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, IN EFI_EXCEPTION_TYPE ExceptionType, IN EFI_SYSTEM_CONTEXT SystemContext, IN BOOLEAN Initialized ) { if (!Initialized) { // // If it does not want initialized state, de-init everything // DebuggerPrivate->FeatureFlags = EFI_DEBUG_FLAG_EBC_BOE | EFI_DEBUG_FLAG_EBC_BOT; DebuggerPrivate->CallStackEntryCount = 0; DebuggerPrivate->TraceEntryCount = 0; ZeroMem (DebuggerPrivate->CallStackEntry, sizeof (DebuggerPrivate->CallStackEntry)); ZeroMem (DebuggerPrivate->TraceEntry, sizeof (DebuggerPrivate->TraceEntry)); // // Clear all breakpoint // EdbClearAllBreakpoint (DebuggerPrivate, TRUE); // // Clear symbol // EdbClearSymbol (DebuggerPrivate); } else { // // If it wants to keep initialized state, just set breakpoint. // EdbSetAllBreakpoint (DebuggerPrivate); } // // Clear Step context // ZeroMem (&mDebuggerPrivate.StepContext, sizeof (mDebuggerPrivate.StepContext)); DebuggerPrivate->StatusFlags = 0; // // Done // return EFI_SUCCESS; } /** Print the reason of current break to EbcDebugger. @param DebuggerPrivate EBC Debugger private data structure @param ExceptionType Exception type. @param SystemContext EBC system context. @param Initialized Whether the DebuggerPrivate data is initialized. **/ VOID PrintExceptionReason ( IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, IN EFI_EXCEPTION_TYPE ExceptionType, IN EFI_SYSTEM_CONTEXT SystemContext, IN BOOLEAN Initialized ) { // // Print break status // if ((DebuggerPrivate->StatusFlags & EFI_DEBUG_FLAG_EBC_GT) == EFI_DEBUG_FLAG_EBC_GT) { EDBPrint (L"Break on GoTil\n"); } else if ((DebuggerPrivate->StatusFlags & EFI_DEBUG_FLAG_EBC_BOC) == EFI_DEBUG_FLAG_EBC_BOC) { EDBPrint (L"Break on CALL\n"); } else if ((DebuggerPrivate->StatusFlags & EFI_DEBUG_FLAG_EBC_BOCX) == EFI_DEBUG_FLAG_EBC_BOCX) { EDBPrint (L"Break on CALLEX\n"); } else if ((DebuggerPrivate->StatusFlags & EFI_DEBUG_FLAG_EBC_BOR) == EFI_DEBUG_FLAG_EBC_BOR) { EDBPrint (L"Break on RET\n"); } else if ((DebuggerPrivate->StatusFlags & EFI_DEBUG_FLAG_EBC_BOE) == EFI_DEBUG_FLAG_EBC_BOE) { EDBPrint (L"Break on Entrypoint\n"); } else if ((DebuggerPrivate->StatusFlags & EFI_DEBUG_FLAG_EBC_BOT) == EFI_DEBUG_FLAG_EBC_BOT) { EDBPrint (L"Break on Thunk\n"); } else if ((DebuggerPrivate->StatusFlags & EFI_DEBUG_FLAG_EBC_STEPOVER) == EFI_DEBUG_FLAG_EBC_STEPOVER) { EDBPrint (L"Break on StepOver\n"); } else if ((DebuggerPrivate->StatusFlags & EFI_DEBUG_FLAG_EBC_STEPOUT) == EFI_DEBUG_FLAG_EBC_STEPOUT) { EDBPrint (L"Break on StepOut\n"); } else if ((DebuggerPrivate->StatusFlags & EFI_DEBUG_FLAG_EBC_BP) == EFI_DEBUG_FLAG_EBC_BP) { EDBPrint (L"Break on Breakpoint\n"); } else if ((DebuggerPrivate->StatusFlags & EFI_DEBUG_FLAG_EBC_BOK) == EFI_DEBUG_FLAG_EBC_BOK) { EDBPrint (L"Break on Key\n"); } else { EDBPrint (L"Exception Type - %x", (UINTN)ExceptionType); if ((ExceptionType >= EXCEPT_EBC_UNDEFINED) && (ExceptionType <= EXCEPT_EBC_STEP)) { EDBPrint (L" (%s)\n", mExceptionStr[ExceptionType]); } else { EDBPrint (L"\n"); } } return; } /** The default Exception Callback for the VM interpreter. In this function, we report status code, and print debug information about EBC_CONTEXT, then dead loop. @param ExceptionType Exception type. @param SystemContext EBC system context. **/ VOID EFIAPI EdbExceptionHandler ( IN EFI_EXCEPTION_TYPE ExceptionType, IN OUT EFI_SYSTEM_CONTEXT SystemContext ) { CHAR16 InputBuffer[EFI_DEBUG_INPUS_BUFFER_SIZE]; CHAR16 *CommandArg; EFI_DEBUGGER_COMMAND DebuggerCommand; EFI_DEBUG_STATUS DebugStatus; STATIC BOOLEAN mInitialized; mInitialized = FALSE; DEBUG ((DEBUG_ERROR, "Hello EBC Debugger!\n")); if (!mInitialized) { // // Print version // EDBPrint ( L"EBC Interpreter Version - %d.%d\n", (UINTN)VM_MAJOR_VERSION, (UINTN)VM_MINOR_VERSION ); EDBPrint ( L"EBC Debugger Version - %d.%d\n", (UINTN)EBC_DEBUGGER_MAJOR_VERSION, (UINTN)EBC_DEBUGGER_MINOR_VERSION ); } // // Init Private Data // InitDebuggerPrivateData (&mDebuggerPrivate, ExceptionType, SystemContext, mInitialized); // // EDBPrint basic info // PrintExceptionReason (&mDebuggerPrivate, ExceptionType, SystemContext, mInitialized); EdbShowDisasm (&mDebuggerPrivate, SystemContext); // EFI_BREAKPOINT (); if (!mInitialized) { // // Interactive with user // EDBPrint (L"\nPlease enter command now, \'h\' for help.\n"); EDBPrint (L"(Using -b <...> to enable page break.)\n"); } mInitialized = TRUE; // // Dispatch each command // while (TRUE) { // // Get user input // Input (L"\n\r" EFI_DEBUG_PROMPT_STRING, InputBuffer, EFI_DEBUG_INPUS_BUFFER_SIZE); EDBPrint (L"\n"); // // Get command // DebuggerCommand = MatchDebuggerCommand (InputBuffer, &CommandArg); if (DebuggerCommand == NULL) { EDBPrint (L"ERROR: Command not found!\n"); continue; } // // Check PageBreak; // if (CommandArg != NULL) { if (StriCmp (CommandArg, L"-b") == 0) { CommandArg = StrGetNextTokenLine (L" "); mDebuggerPrivate.EnablePageBreak = TRUE; } } // // Dispatch command // DebugStatus = DebuggerCommand (CommandArg, &mDebuggerPrivate, ExceptionType, SystemContext); mDebuggerPrivate.EnablePageBreak = FALSE; // // Check command return status // if (DebugStatus == EFI_DEBUG_RETURN) { mInitialized = FALSE; break; } else if (DebugStatus == EFI_DEBUG_BREAK) { break; } else if (DebugStatus == EFI_DEBUG_CONTINUE) { continue; } else { ASSERT (FALSE); } } // // Deinit Private Data // DeinitDebuggerPrivateData (&mDebuggerPrivate, ExceptionType, SystemContext, mInitialized); DEBUG ((DEBUG_ERROR, "Goodbye EBC Debugger!\n")); return; }