blob: c0d5485cbf8225de783145e607d080e1ec795242 [file] [log] [blame]
/** @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;
}