/** @file | |
Copyright (c) 2007 - 2016, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include <Uefi.h> | |
#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 <Command> -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; | |
} |