/** @file Copyright (c) 2007 - 2016, 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 #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 // DebuggerPrivate->DebuggerBreakpointContext[EFI_DEBUGGER_BREAKPOINT_MAX] = DebuggerPrivate->DebuggerBreakpointContext[Index]; 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; }