mirror of https://github.com/acidanthera/audk.git
2685 lines
86 KiB
C
2685 lines
86 KiB
C
/** @file
|
|
Commond Debug Agent library implementition. It mainly includes
|
|
the first C function called by exception/interrupt handlers,
|
|
read/write debug packet to communication with HOST based on transfer
|
|
protocol.
|
|
|
|
Copyright (c) 2010 - 2015, Intel Corporation. All rights reserved.<BR>
|
|
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 "DebugAgent.h"
|
|
#include "Ia32/DebugException.h"
|
|
|
|
GLOBAL_REMOVE_IF_UNREFERENCED CHAR8 mErrorMsgVersionAlert[] = "\rThe SourceLevelDebugPkg you are using requires a newer version of the Intel(R) UDK Debugger Tool.\r\n";
|
|
GLOBAL_REMOVE_IF_UNREFERENCED CHAR8 mErrorMsgSendInitPacket[] = "\rSend INIT break packet and try to connect the HOST (Intel(R) UDK Debugger Tool v1.5) ...\r\n";
|
|
GLOBAL_REMOVE_IF_UNREFERENCED CHAR8 mErrorMsgConnectOK[] = "HOST connection is successful!\r\n";
|
|
GLOBAL_REMOVE_IF_UNREFERENCED CHAR8 mErrorMsgConnectFail[] = "HOST connection is failed!\r\n";
|
|
GLOBAL_REMOVE_IF_UNREFERENCED CHAR8 mWarningMsgIngoreBreakpoint[] = "Ignore break point in SMM for SMI issued during DXE debugging!\r\n";
|
|
|
|
//
|
|
// Vector Handoff Info list used by Debug Agent for persist
|
|
//
|
|
GLOBAL_REMOVE_IF_UNREFERENCED EFI_VECTOR_HANDOFF_INFO mVectorHandoffInfoDebugAgent[] = {
|
|
{
|
|
DEBUG_EXCEPT_DIVIDE_ERROR, // Vector 0
|
|
EFI_VECTOR_HANDOFF_HOOK_BEFORE,
|
|
EFI_DEBUG_AGENT_GUID
|
|
},
|
|
{
|
|
DEBUG_EXCEPT_DEBUG, // Vector 1
|
|
EFI_VECTOR_HANDOFF_DO_NOT_HOOK,
|
|
EFI_DEBUG_AGENT_GUID
|
|
},
|
|
{
|
|
DEBUG_EXCEPT_NMI, // Vector 2
|
|
EFI_VECTOR_HANDOFF_HOOK_BEFORE,
|
|
EFI_DEBUG_AGENT_GUID
|
|
},
|
|
{
|
|
DEBUG_EXCEPT_BREAKPOINT, // Vector 3
|
|
EFI_VECTOR_HANDOFF_DO_NOT_HOOK,
|
|
EFI_DEBUG_AGENT_GUID
|
|
},
|
|
{
|
|
DEBUG_EXCEPT_OVERFLOW, // Vector 4
|
|
EFI_VECTOR_HANDOFF_HOOK_BEFORE,
|
|
EFI_DEBUG_AGENT_GUID
|
|
},
|
|
{
|
|
DEBUG_EXCEPT_BOUND, // Vector 5
|
|
EFI_VECTOR_HANDOFF_HOOK_BEFORE,
|
|
EFI_DEBUG_AGENT_GUID
|
|
},
|
|
{
|
|
DEBUG_EXCEPT_INVALID_OPCODE, // Vector 6
|
|
EFI_VECTOR_HANDOFF_HOOK_BEFORE,
|
|
EFI_DEBUG_AGENT_GUID
|
|
},
|
|
{
|
|
DEBUG_EXCEPT_DOUBLE_FAULT, // Vector 8
|
|
EFI_VECTOR_HANDOFF_HOOK_BEFORE,
|
|
EFI_DEBUG_AGENT_GUID
|
|
},
|
|
{
|
|
DEBUG_EXCEPT_INVALID_TSS, // Vector 10
|
|
EFI_VECTOR_HANDOFF_HOOK_BEFORE,
|
|
EFI_DEBUG_AGENT_GUID
|
|
},
|
|
{
|
|
DEBUG_EXCEPT_SEG_NOT_PRESENT, // Vector 11
|
|
EFI_VECTOR_HANDOFF_HOOK_BEFORE,
|
|
EFI_DEBUG_AGENT_GUID
|
|
},
|
|
{
|
|
DEBUG_EXCEPT_STACK_FAULT, // Vector 12
|
|
EFI_VECTOR_HANDOFF_HOOK_BEFORE,
|
|
EFI_DEBUG_AGENT_GUID
|
|
},
|
|
{
|
|
DEBUG_EXCEPT_GP_FAULT, // Vector 13
|
|
EFI_VECTOR_HANDOFF_HOOK_BEFORE,
|
|
EFI_DEBUG_AGENT_GUID
|
|
},
|
|
{
|
|
DEBUG_EXCEPT_PAGE_FAULT, // Vector 14
|
|
EFI_VECTOR_HANDOFF_HOOK_BEFORE,
|
|
EFI_DEBUG_AGENT_GUID
|
|
},
|
|
{
|
|
DEBUG_EXCEPT_FP_ERROR, // Vector 16
|
|
EFI_VECTOR_HANDOFF_HOOK_BEFORE,
|
|
EFI_DEBUG_AGENT_GUID
|
|
},
|
|
{
|
|
DEBUG_EXCEPT_ALIGNMENT_CHECK, // Vector 17
|
|
EFI_VECTOR_HANDOFF_HOOK_BEFORE,
|
|
EFI_DEBUG_AGENT_GUID
|
|
},
|
|
{
|
|
DEBUG_EXCEPT_MACHINE_CHECK, // Vector 18
|
|
EFI_VECTOR_HANDOFF_HOOK_BEFORE,
|
|
EFI_DEBUG_AGENT_GUID
|
|
},
|
|
{
|
|
DEBUG_EXCEPT_SIMD, // Vector 19
|
|
EFI_VECTOR_HANDOFF_HOOK_BEFORE,
|
|
EFI_DEBUG_AGENT_GUID
|
|
},
|
|
{
|
|
DEBUG_TIMER_VECTOR, // Vector 32
|
|
EFI_VECTOR_HANDOFF_DO_NOT_HOOK,
|
|
EFI_DEBUG_AGENT_GUID
|
|
},
|
|
{
|
|
DEBUG_MAILBOX_VECTOR, // Vector 33
|
|
EFI_VECTOR_HANDOFF_DO_NOT_HOOK,
|
|
EFI_DEBUG_AGENT_GUID
|
|
},
|
|
{
|
|
0,
|
|
EFI_VECTOR_HANDOFF_LAST_ENTRY,
|
|
{ 0 }
|
|
}
|
|
};
|
|
|
|
GLOBAL_REMOVE_IF_UNREFERENCED UINTN mVectorHandoffInfoCount = sizeof (mVectorHandoffInfoDebugAgent) / sizeof (EFI_VECTOR_HANDOFF_INFO);
|
|
|
|
/**
|
|
Calculate CRC16 for target data.
|
|
|
|
@param[in] Data The target data.
|
|
@param[in] DataSize The target data size.
|
|
@param[in] Crc Initial CRC.
|
|
|
|
@return UINT16 The CRC16 value.
|
|
|
|
**/
|
|
UINT16
|
|
CalculateCrc16 (
|
|
IN UINT8 *Data,
|
|
IN UINTN DataSize,
|
|
IN UINT16 Crc
|
|
)
|
|
{
|
|
UINTN Index;
|
|
UINTN BitIndex;
|
|
|
|
for (Index = 0; Index < DataSize; Index++) {
|
|
Crc ^= (UINT16)Data[Index];
|
|
for (BitIndex = 0; BitIndex < 8; BitIndex++) {
|
|
if ((Crc & 0x8000) != 0) {
|
|
Crc <<= 1;
|
|
Crc ^= 0x1021;
|
|
} else {
|
|
Crc <<= 1;
|
|
}
|
|
}
|
|
}
|
|
return Crc;
|
|
}
|
|
|
|
|
|
/**
|
|
Read IDT entry to check if IDT entries are setup by Debug Agent.
|
|
|
|
@retval TRUE IDT entries were setup by Debug Agent.
|
|
@retval FALSE IDT entries were not setup by Debug Agent.
|
|
|
|
**/
|
|
BOOLEAN
|
|
IsDebugAgentInitialzed (
|
|
VOID
|
|
)
|
|
{
|
|
UINTN InterruptHandler;
|
|
|
|
InterruptHandler = (UINTN) GetExceptionHandlerInIdtEntry (0);
|
|
if (InterruptHandler >= 4 && *(UINT32 *)(InterruptHandler - 4) == AGENT_HANDLER_SIGNATURE) {
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Find and report module image info to HOST.
|
|
|
|
@param[in] AlignSize Image aligned size.
|
|
|
|
**/
|
|
VOID
|
|
FindAndReportModuleImageInfo (
|
|
IN UINTN AlignSize
|
|
)
|
|
{
|
|
UINTN Pe32Data;
|
|
EFI_IMAGE_DOS_HEADER *DosHdr;
|
|
EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr;
|
|
PE_COFF_LOADER_IMAGE_CONTEXT ImageContext;
|
|
|
|
//
|
|
// Find Image Base
|
|
//
|
|
Pe32Data = ((UINTN)mErrorMsgVersionAlert) & ~(AlignSize - 1);
|
|
while (Pe32Data != 0) {
|
|
DosHdr = (EFI_IMAGE_DOS_HEADER *) Pe32Data;
|
|
if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
|
|
//
|
|
// DOS image header is present, so read the PE header after the DOS image header.
|
|
//
|
|
Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)(Pe32Data + (UINTN) ((DosHdr->e_lfanew) & 0x0ffff));
|
|
//
|
|
// Make sure PE header address does not overflow and is less than the initial address.
|
|
//
|
|
if (((UINTN)Hdr.Pe32 > Pe32Data) && ((UINTN)Hdr.Pe32 < (UINTN)mErrorMsgVersionAlert)) {
|
|
if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) {
|
|
//
|
|
// It's PE image.
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
//
|
|
// DOS image header is not present, TE header is at the image base.
|
|
//
|
|
Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)Pe32Data;
|
|
if ((Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) &&
|
|
((Hdr.Te->Machine == IMAGE_FILE_MACHINE_I386) || Hdr.Te->Machine == IMAGE_FILE_MACHINE_X64)) {
|
|
//
|
|
// It's TE image, it TE header and Machine type match
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Not found the image base, check the previous aligned address
|
|
//
|
|
Pe32Data -= AlignSize;
|
|
}
|
|
|
|
ImageContext.ImageAddress = Pe32Data;
|
|
ImageContext.PdbPointer = PeCoffLoaderGetPdbPointer ((VOID*) (UINTN) ImageContext.ImageAddress);
|
|
PeCoffLoaderRelocateImageExtraAction (&ImageContext);
|
|
}
|
|
|
|
/**
|
|
Trigger one software interrupt to debug agent to handle it.
|
|
|
|
@param[in] Signature Software interrupt signature.
|
|
|
|
**/
|
|
VOID
|
|
TriggerSoftInterrupt (
|
|
IN UINT32 Signature
|
|
)
|
|
{
|
|
UINTN Dr0;
|
|
UINTN Dr1;
|
|
|
|
//
|
|
// Save Debug Register State
|
|
//
|
|
Dr0 = AsmReadDr0 ();
|
|
Dr1 = AsmReadDr1 ();
|
|
|
|
//
|
|
// DR0 = Signature
|
|
//
|
|
AsmWriteDr0 (SOFT_INTERRUPT_SIGNATURE);
|
|
AsmWriteDr1 (Signature);
|
|
|
|
//
|
|
// Do INT3 to communicate with HOST side
|
|
//
|
|
CpuBreakpoint ();
|
|
|
|
//
|
|
// Restore Debug Register State only when Host didn't change it inside exception handler.
|
|
// Dr registers can only be changed by setting the HW breakpoint.
|
|
//
|
|
AsmWriteDr0 (Dr0);
|
|
AsmWriteDr1 (Dr1);
|
|
|
|
}
|
|
|
|
/**
|
|
Calculate Mailbox checksum and update the checksum field.
|
|
|
|
@param[in] Mailbox Debug Agent Mailbox pointer.
|
|
|
|
**/
|
|
VOID
|
|
UpdateMailboxChecksum (
|
|
IN DEBUG_AGENT_MAILBOX *Mailbox
|
|
)
|
|
{
|
|
Mailbox->CheckSum = CalculateCheckSum8 ((UINT8 *)Mailbox, sizeof (DEBUG_AGENT_MAILBOX) - 2);
|
|
}
|
|
|
|
/**
|
|
Verify Mailbox checksum.
|
|
|
|
If checksum error, print debug message and run init dead loop.
|
|
|
|
@param[in] Mailbox Debug Agent Mailbox pointer.
|
|
|
|
**/
|
|
VOID
|
|
VerifyMailboxChecksum (
|
|
IN DEBUG_AGENT_MAILBOX *Mailbox
|
|
)
|
|
{
|
|
UINT8 CheckSum;
|
|
|
|
CheckSum = CalculateCheckSum8 ((UINT8 *) Mailbox, sizeof (DEBUG_AGENT_MAILBOX) - 2);
|
|
//
|
|
// The checksum updating process may be disturbed by hardware SMI, we need to check CheckSum field
|
|
// and ToBeCheckSum field to validate the mail box.
|
|
//
|
|
if (CheckSum != Mailbox->CheckSum && CheckSum != Mailbox->ToBeCheckSum) {
|
|
DEBUG ((EFI_D_ERROR, "DebugAgent: Mailbox checksum error, stack or heap crashed!\n"));
|
|
DEBUG ((EFI_D_ERROR, "DebugAgent: CheckSum = %x, Mailbox->CheckSum = %x, Mailbox->ToBeCheckSum = %x\n", CheckSum, Mailbox->CheckSum, Mailbox->ToBeCheckSum));
|
|
CpuDeadLoop ();
|
|
}
|
|
}
|
|
|
|
/**
|
|
Update Mailbox content by index.
|
|
|
|
@param[in] Mailbox Debug Agent Mailbox pointer.
|
|
@param[in] Index Mailbox content index.
|
|
@param[in] Value Value to be set into Mailbox.
|
|
|
|
**/
|
|
VOID
|
|
UpdateMailboxContent (
|
|
IN DEBUG_AGENT_MAILBOX *Mailbox,
|
|
IN UINTN Index,
|
|
IN UINT64 Value
|
|
)
|
|
{
|
|
AcquireMpSpinLock (&mDebugMpContext.MailboxSpinLock);
|
|
switch (Index) {
|
|
case DEBUG_MAILBOX_DEBUG_FLAG_INDEX:
|
|
Mailbox->ToBeCheckSum = Mailbox->CheckSum + CalculateSum8 ((UINT8 *)&Mailbox->DebugFlag.Uint64, sizeof(UINT64))
|
|
- CalculateSum8 ((UINT8 *)&Value, sizeof(UINT64));
|
|
Mailbox->DebugFlag.Uint64 = Value;
|
|
break;
|
|
case DEBUG_MAILBOX_DEBUG_PORT_HANDLE_INDEX:
|
|
Mailbox->ToBeCheckSum = Mailbox->CheckSum + CalculateSum8 ((UINT8 *)&Mailbox->DebugPortHandle, sizeof(UINTN))
|
|
- CalculateSum8 ((UINT8 *)&Value, sizeof(UINTN));
|
|
Mailbox->DebugPortHandle = (UINTN) Value;
|
|
break;
|
|
case DEBUG_MAILBOX_EXCEPTION_BUFFER_POINTER_INDEX:
|
|
Mailbox->ToBeCheckSum = Mailbox->CheckSum + CalculateSum8 ((UINT8 *)&Mailbox->ExceptionBufferPointer, sizeof(UINTN))
|
|
- CalculateSum8 ((UINT8 *)&Value, sizeof(UINTN));
|
|
Mailbox->ExceptionBufferPointer = (UINTN) Value;
|
|
break;
|
|
case DEBUG_MAILBOX_LAST_ACK:
|
|
Mailbox->ToBeCheckSum = Mailbox->CheckSum + CalculateSum8 ((UINT8 *)&Mailbox->LastAck, sizeof(UINT8))
|
|
- CalculateSum8 ((UINT8 *)&Value, sizeof(UINT8));
|
|
Mailbox->LastAck = (UINT8) Value;
|
|
break;
|
|
case DEBUG_MAILBOX_SEQUENCE_NO_INDEX:
|
|
Mailbox->ToBeCheckSum = Mailbox->CheckSum + CalculateSum8 ((UINT8 *)&Mailbox->SequenceNo, sizeof(UINT8))
|
|
- CalculateSum8 ((UINT8 *)&Value, sizeof(UINT8));
|
|
Mailbox->SequenceNo = (UINT8) Value;
|
|
break;
|
|
case DEBUG_MAILBOX_HOST_SEQUENCE_NO_INDEX:
|
|
Mailbox->ToBeCheckSum = Mailbox->CheckSum + CalculateSum8 ((UINT8 *)&Mailbox->HostSequenceNo, sizeof(UINT8))
|
|
- CalculateSum8 ((UINT8 *)&Value, sizeof(UINT8));
|
|
Mailbox->HostSequenceNo = (UINT8) Value;
|
|
break;
|
|
case DEBUG_MAILBOX_DEBUG_TIMER_FREQUENCY:
|
|
Mailbox->ToBeCheckSum = Mailbox->CheckSum + CalculateSum8 ((UINT8 *)&Mailbox->DebugTimerFrequency, sizeof(UINT32))
|
|
- CalculateSum8 ((UINT8 *)&Value, sizeof(UINT32));
|
|
Mailbox->DebugTimerFrequency = (UINT32) Value;
|
|
break;
|
|
}
|
|
UpdateMailboxChecksum (Mailbox);
|
|
ReleaseMpSpinLock (&mDebugMpContext.MailboxSpinLock);
|
|
}
|
|
|
|
/**
|
|
Read data from debug device and save the data in buffer.
|
|
|
|
Reads NumberOfBytes data bytes from a debug device into the buffer
|
|
specified by Buffer. The number of bytes actually read is returned.
|
|
If the return value is less than NumberOfBytes, then the rest operation failed.
|
|
If NumberOfBytes is zero, then return 0.
|
|
|
|
@param Handle Debug port handle.
|
|
@param Buffer Pointer to the data buffer to store the data read from the debug device.
|
|
@param NumberOfBytes Number of bytes which will be read.
|
|
@param Timeout Timeout value for reading from debug device. It unit is Microsecond.
|
|
|
|
@retval 0 Read data failed, no data is to be read.
|
|
@retval >0 Actual number of bytes read from debug device.
|
|
|
|
**/
|
|
UINTN
|
|
DebugAgentReadBuffer (
|
|
IN DEBUG_PORT_HANDLE Handle,
|
|
IN UINT8 *Buffer,
|
|
IN UINTN NumberOfBytes,
|
|
IN UINTN Timeout
|
|
)
|
|
{
|
|
UINTN Index;
|
|
UINT32 Begin;
|
|
UINT32 TimeoutTicker;
|
|
UINT32 TimerRound;
|
|
UINT32 TimerFrequency;
|
|
UINT32 TimerCycle;
|
|
|
|
Begin = 0;
|
|
TimeoutTicker = 0;
|
|
TimerRound = 0;
|
|
TimerFrequency = GetMailboxPointer()->DebugTimerFrequency;
|
|
TimerCycle = GetApicTimerInitCount ();
|
|
|
|
if (Timeout != 0) {
|
|
Begin = GetApicTimerCurrentCount ();
|
|
TimeoutTicker = (UINT32) DivU64x32 (
|
|
MultU64x64 (
|
|
TimerFrequency,
|
|
Timeout
|
|
),
|
|
1000000u
|
|
);
|
|
TimerRound = (UINT32) DivU64x32Remainder (TimeoutTicker, TimerCycle / 2, &TimeoutTicker);
|
|
}
|
|
Index = 0;
|
|
while (Index < NumberOfBytes) {
|
|
if (DebugPortPollBuffer (Handle)) {
|
|
DebugPortReadBuffer (Handle, Buffer + Index, 1, 0);
|
|
Index ++;
|
|
continue;
|
|
}
|
|
if (Timeout != 0) {
|
|
if (TimerRound == 0) {
|
|
if (IsDebugTimerTimeout (TimerCycle, Begin, TimeoutTicker)) {
|
|
//
|
|
// If time out occurs.
|
|
//
|
|
return 0;
|
|
}
|
|
} else {
|
|
if (IsDebugTimerTimeout (TimerCycle, Begin, TimerCycle / 2)) {
|
|
TimerRound --;
|
|
Begin = GetApicTimerCurrentCount ();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return Index;
|
|
}
|
|
|
|
/**
|
|
Set debug flag in mailbox.
|
|
|
|
@param[in] FlagMask Debug flag mask value.
|
|
@param[in] FlagValue Debug flag value.
|
|
|
|
**/
|
|
VOID
|
|
SetDebugFlag (
|
|
IN UINT64 FlagMask,
|
|
IN UINT32 FlagValue
|
|
)
|
|
{
|
|
DEBUG_AGENT_MAILBOX *Mailbox;
|
|
UINT64 Data64;
|
|
|
|
Mailbox = GetMailboxPointer ();
|
|
Data64 = (Mailbox->DebugFlag.Uint64 & ~FlagMask) |
|
|
(LShiftU64 ((UINT64)FlagValue, LowBitSet64 (FlagMask)) & FlagMask);
|
|
UpdateMailboxContent (Mailbox, DEBUG_MAILBOX_DEBUG_FLAG_INDEX, Data64);
|
|
}
|
|
|
|
/**
|
|
Get debug flag in mailbox.
|
|
|
|
@param[in] FlagMask Debug flag mask value.
|
|
|
|
@return Debug flag value.
|
|
|
|
**/
|
|
UINT32
|
|
GetDebugFlag (
|
|
IN UINT64 FlagMask
|
|
)
|
|
{
|
|
DEBUG_AGENT_MAILBOX *Mailbox;
|
|
UINT32 DebugFlag;
|
|
|
|
Mailbox = GetMailboxPointer ();
|
|
DebugFlag = (UINT32) RShiftU64 (Mailbox->DebugFlag.Uint64 & FlagMask, LowBitSet64 (FlagMask));
|
|
|
|
return DebugFlag;
|
|
}
|
|
|
|
/**
|
|
Send a debug message packet to the debug port.
|
|
|
|
@param[in] Buffer The debug message.
|
|
@param[in] Length The length of debug message.
|
|
|
|
**/
|
|
VOID
|
|
SendDebugMsgPacket (
|
|
IN CHAR8 *Buffer,
|
|
IN UINTN Length
|
|
)
|
|
{
|
|
DEBUG_PACKET_HEADER DebugHeader;
|
|
DEBUG_PORT_HANDLE Handle;
|
|
|
|
Handle = GetDebugPortHandle();
|
|
|
|
DebugHeader.StartSymbol = DEBUG_STARTING_SYMBOL_NORMAL;
|
|
DebugHeader.Command = DEBUG_COMMAND_PRINT_MESSAGE;
|
|
DebugHeader.Length = sizeof (DEBUG_PACKET_HEADER) + (UINT8) Length;
|
|
DebugHeader.SequenceNo = 0xEE;
|
|
DebugHeader.Crc = 0;
|
|
DebugHeader.Crc = CalculateCrc16 (
|
|
(UINT8 *)Buffer, Length,
|
|
CalculateCrc16 ((UINT8 *)&DebugHeader, sizeof (DEBUG_PACKET_HEADER), 0)
|
|
);
|
|
|
|
DebugPortWriteBuffer (Handle, (UINT8 *)&DebugHeader, sizeof (DEBUG_PACKET_HEADER));
|
|
DebugPortWriteBuffer (Handle, (UINT8 *)Buffer, Length);
|
|
}
|
|
|
|
/**
|
|
Prints a debug message to the debug port if the specified error level is enabled.
|
|
|
|
If any bit in ErrorLevel is also set in Mainbox, then print the message specified
|
|
by Format and the associated variable argument list to the debug port.
|
|
|
|
@param[in] ErrorLevel The error level of the debug message.
|
|
@param[in] Format Format string for the debug message to print.
|
|
@param[in] ... Variable argument list whose contents are accessed
|
|
based on the format string specified by Format.
|
|
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
DebugAgentMsgPrint (
|
|
IN UINT8 ErrorLevel,
|
|
IN CHAR8 *Format,
|
|
...
|
|
)
|
|
{
|
|
CHAR8 Buffer[DEBUG_DATA_MAXIMUM_REAL_DATA];
|
|
VA_LIST Marker;
|
|
|
|
//
|
|
// Check driver debug mask value and global mask
|
|
//
|
|
if ((ErrorLevel & GetDebugFlag (DEBUG_AGENT_FLAG_PRINT_ERROR_LEVEL)) == 0) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Convert the DEBUG() message to an ASCII String
|
|
//
|
|
VA_START (Marker, Format);
|
|
AsciiVSPrint (Buffer, sizeof (Buffer), Format, Marker);
|
|
VA_END (Marker);
|
|
|
|
SendDebugMsgPacket (Buffer, AsciiStrLen (Buffer));
|
|
}
|
|
|
|
/**
|
|
Prints a debug message to the debug output device if the specified error level is enabled.
|
|
|
|
If any bit in ErrorLevel is also set in DebugPrintErrorLevelLib function
|
|
GetDebugPrintErrorLevel (), then print the message specified by Format and the
|
|
associated variable argument list to the debug output device.
|
|
|
|
If Format is NULL, then ASSERT().
|
|
|
|
@param[in] ErrorLevel The error level of the debug message.
|
|
@param[in] IsSend Flag of debug message to declare that the data is being sent or being received.
|
|
@param[in] Data Variable argument list whose contents are accessed
|
|
@param[in] Length based on the format string specified by Format.
|
|
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
DebugAgentDataMsgPrint (
|
|
IN UINT8 ErrorLevel,
|
|
IN BOOLEAN IsSend,
|
|
IN UINT8 *Data,
|
|
IN UINT8 Length
|
|
)
|
|
{
|
|
CHAR8 Buffer[DEBUG_DATA_MAXIMUM_REAL_DATA];
|
|
CHAR8 *DestBuffer;
|
|
UINTN Index;
|
|
|
|
//
|
|
// Check driver debug mask value and global mask
|
|
//
|
|
if ((ErrorLevel & GetDebugFlag (DEBUG_AGENT_FLAG_PRINT_ERROR_LEVEL)) == 0) {
|
|
return;
|
|
}
|
|
|
|
DestBuffer = Buffer;
|
|
if (IsSend) {
|
|
DestBuffer += AsciiSPrint (DestBuffer, DEBUG_DATA_MAXIMUM_REAL_DATA, "Sent data [ ");
|
|
} else {
|
|
DestBuffer += AsciiSPrint (DestBuffer, DEBUG_DATA_MAXIMUM_REAL_DATA, "Received data [ ");
|
|
}
|
|
|
|
Index = 0;
|
|
while (TRUE) {
|
|
if (DestBuffer - Buffer > DEBUG_DATA_MAXIMUM_REAL_DATA - 6) {
|
|
//
|
|
// If there was no enough space in buffer, send out the debug message,
|
|
// reserving 6 bytes is for the last data and end characters "]\n".
|
|
//
|
|
SendDebugMsgPacket (Buffer, DestBuffer - Buffer);
|
|
DestBuffer = Buffer;
|
|
}
|
|
DestBuffer += AsciiSPrint (DestBuffer, DEBUG_DATA_MAXIMUM_REAL_DATA - (DestBuffer - Buffer), "%02x ", Data[Index]);
|
|
Index ++;
|
|
if (Index >= Length) {
|
|
//
|
|
// The last character of debug message has been foramtted in buffer
|
|
//
|
|
DestBuffer += AsciiSPrint(DestBuffer, DEBUG_DATA_MAXIMUM_REAL_DATA - (DestBuffer - Buffer), "]\n");
|
|
SendDebugMsgPacket (Buffer, DestBuffer - Buffer);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Read remaing debug packet except for the start symbol
|
|
|
|
@param[in] Handle Pointer to Debug Port handle.
|
|
@param[in, out] DebugHeader Debug header buffer including start symbol.
|
|
|
|
@retval EFI_SUCCESS Read the symbol in BreakSymbol.
|
|
@retval EFI_CRC_ERROR CRC check fail.
|
|
@retval EFI_TIMEOUT Timeout occurs when reading debug packet.
|
|
@retval EFI_DEVICE_ERROR Receive the old or responsed packet.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
ReadRemainingBreakPacket (
|
|
IN DEBUG_PORT_HANDLE Handle,
|
|
IN OUT DEBUG_PACKET_HEADER *DebugHeader
|
|
)
|
|
{
|
|
UINT16 Crc;
|
|
DEBUG_AGENT_MAILBOX *Mailbox;
|
|
|
|
//
|
|
// Has received start symbol, try to read the rest part
|
|
//
|
|
if (DebugAgentReadBuffer (Handle, (UINT8 *)DebugHeader + OFFSET_OF (DEBUG_PACKET_HEADER, Command), sizeof (DEBUG_PACKET_HEADER) - OFFSET_OF (DEBUG_PACKET_HEADER, Command), READ_PACKET_TIMEOUT) == 0) {
|
|
//
|
|
// Timeout occur, exit
|
|
//
|
|
DebugAgentMsgPrint (DEBUG_AGENT_WARNING, "Timeout in Debug Timer interrupt\n");
|
|
return EFI_TIMEOUT;
|
|
}
|
|
|
|
Crc = DebugHeader->Crc;
|
|
DebugHeader->Crc = 0;
|
|
if (CalculateCrc16 ((UINT8 *)DebugHeader, DebugHeader->Length, 0) != Crc) {
|
|
DebugAgentMsgPrint (DEBUG_AGENT_WARNING, "Debug Timer CRC (%x) against (%x)\n", Crc, CalculateCrc16 ((UINT8 *) &DebugHeader, DebugHeader->Length, 0));
|
|
DebugAgentDataMsgPrint (DEBUG_AGENT_VERBOSE, FALSE, (UINT8 *)DebugHeader, DebugHeader->Length);
|
|
return EFI_CRC_ERROR;
|
|
}
|
|
Mailbox = GetMailboxPointer();
|
|
if (IS_REQUEST (DebugHeader)) {
|
|
if (DebugHeader->SequenceNo == (UINT8) (Mailbox->HostSequenceNo + 1)) {
|
|
//
|
|
// Only updagte HostSequenceNo for new command packet
|
|
//
|
|
UpdateMailboxContent (Mailbox, DEBUG_MAILBOX_HOST_SEQUENCE_NO_INDEX, DebugHeader->SequenceNo);
|
|
return EFI_SUCCESS;
|
|
}
|
|
if (DebugHeader->SequenceNo == Mailbox->HostSequenceNo) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
/**
|
|
Check if HOST is attached based on Mailbox.
|
|
|
|
@retval TRUE HOST is attached.
|
|
@retval FALSE HOST is not attached.
|
|
|
|
**/
|
|
BOOLEAN
|
|
IsHostAttached (
|
|
VOID
|
|
)
|
|
{
|
|
return (BOOLEAN) (GetDebugFlag (DEBUG_AGENT_FLAG_HOST_ATTACHED) == 1);
|
|
}
|
|
|
|
/**
|
|
Set HOST connect flag in Mailbox.
|
|
|
|
@param[in] Attached Attach status.
|
|
|
|
**/
|
|
VOID
|
|
SetHostAttached (
|
|
IN BOOLEAN Attached
|
|
)
|
|
{
|
|
DebugAgentMsgPrint (DEBUG_AGENT_INFO, "Attach status is %d\n", Attached);
|
|
SetDebugFlag (DEBUG_AGENT_FLAG_HOST_ATTACHED, (UINT32)Attached);
|
|
}
|
|
|
|
/**
|
|
Set debug setting of Debug Agent in Mailbox.
|
|
|
|
@param DebugSetting Pointer to Debug Setting defined by transfer protocol.
|
|
|
|
@retval RETURN_SUCCESS The setting is set successfully.
|
|
@retval RETURN_UNSUPPORTED The Key value is not supported.
|
|
|
|
**/
|
|
RETURN_STATUS
|
|
SetDebugSetting (
|
|
IN DEBUG_DATA_SET_DEBUG_SETTING *DebugSetting
|
|
)
|
|
{
|
|
RETURN_STATUS Status;
|
|
|
|
Status = RETURN_SUCCESS;
|
|
switch (DebugSetting->Key) {
|
|
case DEBUG_AGENT_SETTING_SMM_ENTRY_BREAK:
|
|
SetDebugFlag (DEBUG_AGENT_FLAG_BREAK_ON_NEXT_SMI, DebugSetting->Value);
|
|
break;
|
|
case DEBUG_AGENT_SETTING_PRINT_ERROR_LEVEL:
|
|
SetDebugFlag (DEBUG_AGENT_FLAG_PRINT_ERROR_LEVEL, DebugSetting->Value);
|
|
break;
|
|
case DEBUG_AGENT_SETTING_BOOT_SCRIPT_ENTRY_BREAK:
|
|
SetDebugFlag (DEBUG_AGENT_FLAG_BREAK_BOOT_SCRIPT, DebugSetting->Value);
|
|
break;
|
|
default:
|
|
Status = RETURN_UNSUPPORTED;
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Exectue GO command.
|
|
|
|
@param[in] CpuContext Pointer to saved CPU context.
|
|
|
|
**/
|
|
VOID
|
|
CommandGo (
|
|
IN DEBUG_CPU_CONTEXT *CpuContext
|
|
)
|
|
{
|
|
IA32_EFLAGS32 *Eflags;
|
|
|
|
Eflags = (IA32_EFLAGS32 *) &CpuContext->Eflags;
|
|
Eflags->Bits.TF = 0;
|
|
Eflags->Bits.RF = 1;
|
|
}
|
|
|
|
/**
|
|
Execute Stepping command.
|
|
|
|
@param[in] CpuContext Pointer to saved CPU context.
|
|
|
|
**/
|
|
VOID
|
|
CommandStepping (
|
|
IN DEBUG_CPU_CONTEXT *CpuContext
|
|
)
|
|
{
|
|
IA32_EFLAGS32 *Eflags;
|
|
|
|
Eflags = (IA32_EFLAGS32 *) &CpuContext->Eflags;
|
|
Eflags->Bits.TF = 1;
|
|
Eflags->Bits.RF = 1;
|
|
//
|
|
// Save and clear EFLAGS.IF to avoid interrupt happen when executing Stepping
|
|
//
|
|
SetDebugFlag (DEBUG_AGENT_FLAG_INTERRUPT_FLAG, Eflags->Bits.IF);
|
|
Eflags->Bits.IF = 0;
|
|
//
|
|
// Set Stepping Flag
|
|
//
|
|
SetDebugFlag (DEBUG_AGENT_FLAG_STEPPING, 1);
|
|
}
|
|
|
|
/**
|
|
Do some cleanup after Stepping command done.
|
|
|
|
@param[in] CpuContext Pointer to saved CPU context.
|
|
|
|
**/
|
|
VOID
|
|
CommandSteppingCleanup (
|
|
IN DEBUG_CPU_CONTEXT *CpuContext
|
|
)
|
|
{
|
|
IA32_EFLAGS32 *Eflags;
|
|
|
|
Eflags = (IA32_EFLAGS32 *) &CpuContext->Eflags;
|
|
//
|
|
// Restore EFLAGS.IF
|
|
//
|
|
Eflags->Bits.IF = GetDebugFlag (DEBUG_AGENT_FLAG_INTERRUPT_FLAG);
|
|
//
|
|
// Clear Stepping flag
|
|
//
|
|
SetDebugFlag (DEBUG_AGENT_FLAG_STEPPING, 0);
|
|
}
|
|
|
|
/**
|
|
Set debug register for hardware breakpoint.
|
|
|
|
@param[in] CpuContext Pointer to saved CPU context.
|
|
@param[in] SetHwBreakpoint Hardware breakpoint to be set.
|
|
|
|
**/
|
|
VOID
|
|
SetDebugRegister (
|
|
IN DEBUG_CPU_CONTEXT *CpuContext,
|
|
IN DEBUG_DATA_SET_HW_BREAKPOINT *SetHwBreakpoint
|
|
)
|
|
{
|
|
UINT8 RegisterIndex;
|
|
UINTN Dr7Value;
|
|
|
|
RegisterIndex = SetHwBreakpoint->Type.Index;
|
|
|
|
//
|
|
// Set debug address
|
|
//
|
|
* ((UINTN *) &CpuContext->Dr0 + RegisterIndex) = (UINTN) SetHwBreakpoint->Address;
|
|
|
|
Dr7Value = CpuContext->Dr7;
|
|
|
|
//
|
|
// Enable Gx, Lx
|
|
//
|
|
Dr7Value |= (UINTN) (0x3 << (RegisterIndex * 2));
|
|
//
|
|
// Set RWx and Lenx
|
|
//
|
|
Dr7Value &= (UINTN) (~(0xf << (16 + RegisterIndex * 4)));
|
|
Dr7Value |= (UINTN) ((SetHwBreakpoint->Type.Length << 2) | SetHwBreakpoint->Type.Access) << (16 + RegisterIndex * 4);
|
|
//
|
|
// Enable GE, LE
|
|
//
|
|
Dr7Value |= 0x300;
|
|
|
|
CpuContext->Dr7 = Dr7Value;
|
|
}
|
|
|
|
/**
|
|
Clear debug register for hardware breakpoint.
|
|
|
|
@param[in] CpuContext Pointer to saved CPU context.
|
|
@param[in] ClearHwBreakpoint Hardware breakpoint to be cleared.
|
|
|
|
**/
|
|
VOID
|
|
ClearDebugRegister (
|
|
IN DEBUG_CPU_CONTEXT *CpuContext,
|
|
IN DEBUG_DATA_CLEAR_HW_BREAKPOINT *ClearHwBreakpoint
|
|
)
|
|
{
|
|
if ((ClearHwBreakpoint->IndexMask & BIT0) != 0) {
|
|
CpuContext->Dr0 = 0;
|
|
CpuContext->Dr7 &= (UINTN)(~(0x3 << 0));
|
|
}
|
|
if ((ClearHwBreakpoint->IndexMask & BIT1) != 0) {
|
|
CpuContext->Dr1 = 0;
|
|
CpuContext->Dr7 &= (UINTN)(~(0x3 << 2));
|
|
}
|
|
if ((ClearHwBreakpoint->IndexMask & BIT2) != 0) {
|
|
CpuContext->Dr2 = 0;
|
|
CpuContext->Dr7 &= (UINTN)(~(0x3 << 4));
|
|
}
|
|
if ((ClearHwBreakpoint->IndexMask & BIT3) != 0) {
|
|
CpuContext->Dr3 = 0;
|
|
CpuContext->Dr7 &= (UINTN)(~(0x3 << 6));
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
Return the offset of FP / MMX / XMM registers in the FPU saved state by register index.
|
|
|
|
@param[in] Index Register index.
|
|
@param[out] Width Register width returned.
|
|
|
|
@return Offset in the FPU Save State.
|
|
|
|
**/
|
|
UINT16
|
|
ArchReadFxStatOffset (
|
|
IN UINT8 Index,
|
|
OUT UINT8 *Width
|
|
)
|
|
{
|
|
if (Index < SOFT_DEBUGGER_REGISTER_ST0) {
|
|
switch (Index) {
|
|
case SOFT_DEBUGGER_REGISTER_FP_FCW:
|
|
*Width = (UINT8) sizeof (UINT16);
|
|
return OFFSET_OF(DEBUG_DATA_FX_SAVE_STATE, Fcw);
|
|
|
|
case SOFT_DEBUGGER_REGISTER_FP_FSW:
|
|
*Width = (UINT8) sizeof (UINT16);
|
|
return OFFSET_OF(DEBUG_DATA_FX_SAVE_STATE, Fsw);
|
|
|
|
case SOFT_DEBUGGER_REGISTER_FP_FTW:
|
|
*Width = (UINT8) sizeof (UINT16);
|
|
return OFFSET_OF(DEBUG_DATA_FX_SAVE_STATE, Ftw);
|
|
|
|
case SOFT_DEBUGGER_REGISTER_FP_OPCODE:
|
|
*Width = (UINT8) sizeof (UINT16);
|
|
return OFFSET_OF(DEBUG_DATA_FX_SAVE_STATE, Opcode);
|
|
|
|
case SOFT_DEBUGGER_REGISTER_FP_EIP:
|
|
*Width = (UINT8) sizeof (UINT32);
|
|
return OFFSET_OF(DEBUG_DATA_FX_SAVE_STATE, Eip);
|
|
|
|
case SOFT_DEBUGGER_REGISTER_FP_CS:
|
|
*Width = (UINT8) sizeof (UINT16);
|
|
return OFFSET_OF(DEBUG_DATA_FX_SAVE_STATE, Cs);
|
|
|
|
case SOFT_DEBUGGER_REGISTER_FP_DATAOFFSET:
|
|
*Width = (UINT8) sizeof (UINT32);
|
|
return OFFSET_OF(DEBUG_DATA_FX_SAVE_STATE, DataOffset);
|
|
|
|
case SOFT_DEBUGGER_REGISTER_FP_DS:
|
|
*Width = (UINT8) sizeof (UINT16);
|
|
return OFFSET_OF(DEBUG_DATA_FX_SAVE_STATE, Ds);
|
|
|
|
case SOFT_DEBUGGER_REGISTER_FP_MXCSR:
|
|
*Width = (UINT8) sizeof (UINT32);
|
|
return OFFSET_OF(DEBUG_DATA_FX_SAVE_STATE, Mxcsr);
|
|
|
|
case SOFT_DEBUGGER_REGISTER_FP_MXCSR_MASK:
|
|
*Width = (UINT8) sizeof (UINT32);
|
|
return OFFSET_OF(DEBUG_DATA_FX_SAVE_STATE, Mxcsr_Mask);
|
|
}
|
|
}
|
|
|
|
if (Index <= SOFT_DEBUGGER_REGISTER_ST7) {
|
|
*Width = 10;
|
|
} else if (Index <= SOFT_DEBUGGER_REGISTER_XMM15) {
|
|
*Width = 16;
|
|
} else {
|
|
//
|
|
// MMX register
|
|
//
|
|
*Width = 8;
|
|
Index -= SOFT_DEBUGGER_REGISTER_MM0 - SOFT_DEBUGGER_REGISTER_ST0;
|
|
}
|
|
|
|
return OFFSET_OF (DEBUG_DATA_FX_SAVE_STATE, St0Mm0) + (Index - SOFT_DEBUGGER_REGISTER_ST0) * 16;
|
|
}
|
|
|
|
/**
|
|
Return the pointer of the register value in the CPU saved context.
|
|
|
|
@param[in] CpuContext Pointer to saved CPU context.
|
|
@param[in] Index Register index value.
|
|
@param[out] Width Data width to read.
|
|
|
|
@return The pointer in the CPU saved context.
|
|
|
|
**/
|
|
UINT8 *
|
|
ArchReadRegisterBuffer (
|
|
IN DEBUG_CPU_CONTEXT *CpuContext,
|
|
IN UINT8 Index,
|
|
OUT UINT8 *Width
|
|
)
|
|
{
|
|
UINT8 *Buffer;
|
|
|
|
if (Index < SOFT_DEBUGGER_REGISTER_FP_BASE) {
|
|
Buffer = (UINT8 *) CpuContext + OFFSET_OF (DEBUG_CPU_CONTEXT, Dr0) + Index * sizeof (UINTN);
|
|
*Width = (UINT8) sizeof (UINTN);
|
|
} else {
|
|
//
|
|
// FPU/MMX/XMM registers
|
|
//
|
|
Buffer = (UINT8 *) CpuContext + OFFSET_OF (DEBUG_CPU_CONTEXT, FxSaveState) + ArchReadFxStatOffset (Index, Width);
|
|
}
|
|
|
|
return Buffer;
|
|
}
|
|
|
|
/**
|
|
Send the packet without data to HOST.
|
|
|
|
@param[in] CommandType Type of Command.
|
|
@param[in] SequenceNo Sequence number.
|
|
|
|
**/
|
|
VOID
|
|
SendPacketWithoutData (
|
|
IN UINT8 CommandType,
|
|
IN UINT8 SequenceNo
|
|
)
|
|
{
|
|
DEBUG_PACKET_HEADER DebugHeader;
|
|
DEBUG_PORT_HANDLE Handle;
|
|
|
|
Handle = GetDebugPortHandle();
|
|
|
|
DebugHeader.StartSymbol = DEBUG_STARTING_SYMBOL_NORMAL;
|
|
DebugHeader.Command = CommandType;
|
|
DebugHeader.Length = sizeof (DEBUG_PACKET_HEADER);
|
|
DebugHeader.SequenceNo = SequenceNo;
|
|
DebugHeader.Crc = 0;
|
|
DebugHeader.Crc = CalculateCrc16 ((UINT8 *)&DebugHeader, sizeof (DEBUG_PACKET_HEADER), 0);
|
|
|
|
DebugAgentDataMsgPrint (DEBUG_AGENT_VERBOSE, TRUE, (UINT8 *) &DebugHeader, DebugHeader.Length);
|
|
DebugPortWriteBuffer (Handle, (UINT8 *) &DebugHeader, DebugHeader.Length);
|
|
}
|
|
|
|
/**
|
|
Send acknowledge packet to HOST.
|
|
|
|
@param[in] AckCommand Type of Acknowledge packet.
|
|
|
|
**/
|
|
VOID
|
|
SendAckPacket (
|
|
IN UINT8 AckCommand
|
|
)
|
|
{
|
|
UINT8 SequenceNo;
|
|
DEBUG_AGENT_MAILBOX *Mailbox;
|
|
|
|
if (AckCommand != DEBUG_COMMAND_OK) {
|
|
//
|
|
// This is not ACK OK packet
|
|
//
|
|
DebugAgentMsgPrint (DEBUG_AGENT_ERROR, "Send ACK(%d)\n", AckCommand);
|
|
}
|
|
Mailbox = GetMailboxPointer();
|
|
SequenceNo = Mailbox->HostSequenceNo;
|
|
DebugAgentMsgPrint (DEBUG_AGENT_INFO, "SendAckPacket: SequenceNo = %x\n", SequenceNo);
|
|
SendPacketWithoutData (AckCommand, SequenceNo);
|
|
UpdateMailboxContent (Mailbox, DEBUG_MAILBOX_LAST_ACK, AckCommand);
|
|
}
|
|
|
|
/**
|
|
Decompress the Data in place.
|
|
|
|
@param[in, out] Data The compressed data buffer.
|
|
The buffer is assumed large enough to hold the uncompressed data.
|
|
@param[in] Length The length of the compressed data buffer.
|
|
|
|
@return The length of the uncompressed data buffer.
|
|
**/
|
|
UINT8
|
|
DecompressDataInPlace (
|
|
IN OUT UINT8 *Data,
|
|
IN UINTN Length
|
|
)
|
|
{
|
|
UINTN Index;
|
|
UINT16 LastChar;
|
|
UINTN LastCharCount;
|
|
UINT8 CurrentChar;
|
|
|
|
LastChar = (UINT16) -1;
|
|
LastCharCount = 0;
|
|
for (Index = 0; Index < Length; Index++) {
|
|
CurrentChar = Data[Index];
|
|
if (LastCharCount == 2) {
|
|
LastCharCount = 0;
|
|
CopyMem (&Data[Index + CurrentChar], &Data[Index + 1], Length - Index - 1);
|
|
SetMem (&Data[Index], CurrentChar, (UINT8) LastChar);
|
|
LastChar = (UINT16) -1;
|
|
Index += CurrentChar - 1;
|
|
Length += CurrentChar - 1;
|
|
} else {
|
|
if (LastChar != CurrentChar) {
|
|
LastCharCount = 0;
|
|
}
|
|
LastCharCount++;
|
|
LastChar = CurrentChar;
|
|
}
|
|
}
|
|
|
|
ASSERT (Length <= DEBUG_DATA_MAXIMUM_REAL_DATA);
|
|
|
|
return (UINT8) Length;
|
|
}
|
|
|
|
/**
|
|
Receive valid packet from HOST.
|
|
|
|
@param[out] InputPacket Buffer to receive packet.
|
|
@param[out] BreakReceived TRUE means break-in symbol received.
|
|
FALSE means break-in symbol not received.
|
|
@param[out] IncompatibilityFlag If IncompatibilityFlag is not NULL, return
|
|
TRUE: Compatible packet received.
|
|
FALSE: Incompatible packet received.
|
|
@param[in] Timeout Time out value to wait for acknowlege from HOST.
|
|
The unit is microsecond.
|
|
@param[in] SkipStartSymbol TRUE: Skip time out when reading start symbol.
|
|
FALSE: Does not Skip time out when reading start symbol.
|
|
|
|
@retval RETURN_SUCCESS A valid package was reveived in InputPacket.
|
|
@retval RETURN_TIMEOUT Timeout occurs.
|
|
|
|
**/
|
|
RETURN_STATUS
|
|
ReceivePacket (
|
|
OUT UINT8 *InputPacket,
|
|
OUT BOOLEAN *BreakReceived,
|
|
OUT BOOLEAN *IncompatibilityFlag, OPTIONAL
|
|
IN UINTN Timeout,
|
|
IN BOOLEAN SkipStartSymbol
|
|
)
|
|
{
|
|
DEBUG_PACKET_HEADER *DebugHeader;
|
|
UINTN Received;
|
|
DEBUG_PORT_HANDLE Handle;
|
|
UINT16 Crc;
|
|
UINTN TimeoutForStartSymbol;
|
|
|
|
Handle = GetDebugPortHandle();
|
|
if (SkipStartSymbol) {
|
|
TimeoutForStartSymbol = 0;
|
|
} else {
|
|
TimeoutForStartSymbol = Timeout;
|
|
}
|
|
|
|
DebugHeader = (DEBUG_PACKET_HEADER *) InputPacket;
|
|
while (TRUE) {
|
|
//
|
|
// Find the valid start symbol
|
|
//
|
|
Received = DebugAgentReadBuffer (Handle, &DebugHeader->StartSymbol, sizeof (DebugHeader->StartSymbol), TimeoutForStartSymbol);
|
|
if (Received < sizeof (DebugHeader->StartSymbol)) {
|
|
DebugAgentMsgPrint (DEBUG_AGENT_WARNING, "DebugAgentReadBuffer(StartSymbol) timeout\n");
|
|
return RETURN_TIMEOUT;
|
|
}
|
|
|
|
if ((DebugHeader->StartSymbol != DEBUG_STARTING_SYMBOL_NORMAL) && (DebugHeader->StartSymbol != DEBUG_STARTING_SYMBOL_COMPRESS)) {
|
|
DebugAgentMsgPrint (DEBUG_AGENT_WARNING, "Invalid start symbol received [%02x]\n", DebugHeader->StartSymbol);
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Read Package header till field Length
|
|
//
|
|
Received = DebugAgentReadBuffer (
|
|
Handle,
|
|
(UINT8 *) DebugHeader + OFFSET_OF (DEBUG_PACKET_HEADER, Command),
|
|
OFFSET_OF (DEBUG_PACKET_HEADER, Length) + sizeof (DebugHeader->Length) - sizeof (DebugHeader->StartSymbol),
|
|
Timeout
|
|
);
|
|
if (Received == 0) {
|
|
DebugAgentMsgPrint (DEBUG_AGENT_ERROR, "DebugAgentReadBuffer(Command) timeout\n");
|
|
return RETURN_TIMEOUT;
|
|
}
|
|
if (DebugHeader->Length < sizeof (DEBUG_PACKET_HEADER)) {
|
|
if (IncompatibilityFlag != NULL) {
|
|
//
|
|
// This is one old version debug packet format, set Incompatibility flag
|
|
//
|
|
*IncompatibilityFlag = TRUE;
|
|
} else {
|
|
//
|
|
// Skip the bad small packet
|
|
//
|
|
continue;
|
|
}
|
|
} else {
|
|
//
|
|
// Read the payload data include the CRC field
|
|
//
|
|
Received = DebugAgentReadBuffer (Handle, &DebugHeader->SequenceNo, (UINT8) (DebugHeader->Length - OFFSET_OF (DEBUG_PACKET_HEADER, SequenceNo)), Timeout);
|
|
if (Received == 0) {
|
|
DebugAgentMsgPrint (DEBUG_AGENT_ERROR, "DebugAgentReadBuffer(SequenceNo) timeout\n");
|
|
return RETURN_TIMEOUT;
|
|
}
|
|
//
|
|
// Calculate the CRC of Debug Packet
|
|
//
|
|
Crc = DebugHeader->Crc;
|
|
DebugHeader->Crc = 0;
|
|
if (Crc == CalculateCrc16 ((UINT8 *) DebugHeader, DebugHeader->Length, 0)) {
|
|
break;
|
|
}
|
|
DebugAgentMsgPrint (DEBUG_AGENT_WARNING, "CRC Error (received CRC is %x)\n", Crc);
|
|
DebugAgentDataMsgPrint (DEBUG_AGENT_VERBOSE, FALSE, (UINT8 *) DebugHeader, DebugHeader->Length);
|
|
}
|
|
}
|
|
|
|
DebugAgentDataMsgPrint (DEBUG_AGENT_VERBOSE, FALSE, (UINT8 *) DebugHeader, DebugHeader->Length);
|
|
|
|
if (DebugHeader->StartSymbol == DEBUG_STARTING_SYMBOL_COMPRESS) {
|
|
DebugHeader->StartSymbol = DEBUG_STARTING_SYMBOL_NORMAL;
|
|
DebugHeader->Length = DecompressDataInPlace (
|
|
(UINT8 *) (DebugHeader + 1), DebugHeader->Length - sizeof (DEBUG_PACKET_HEADER)
|
|
) + sizeof (DEBUG_PACKET_HEADER);
|
|
}
|
|
return RETURN_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Receive acknowledge packet OK from HOST in specified time.
|
|
|
|
@param[in] Command The command type issued by TARGET.
|
|
@param[in] Timeout Time out value to wait for acknowlege from HOST.
|
|
The unit is microsecond.
|
|
@param[out] BreakReceived If BreakReceived is not NULL,
|
|
TRUE is retured if break-in symbol received.
|
|
FALSE is retured if break-in symbol not received.
|
|
@param[out] IncompatibilityFlag If IncompatibilityFlag is not NULL, return
|
|
TRUE: Compatible packet received.
|
|
FALSE: Incompatible packet received.
|
|
|
|
@retval RETRUEN_SUCCESS Succeed to receive acknowlege packet from HOST,
|
|
the type of acknowlege packet saved in Ack.
|
|
@retval RETURN_TIMEOUT Specified timeout value was up.
|
|
|
|
**/
|
|
RETURN_STATUS
|
|
SendCommandAndWaitForAckOK (
|
|
IN UINT8 Command,
|
|
IN UINTN Timeout,
|
|
OUT BOOLEAN *BreakReceived, OPTIONAL
|
|
OUT BOOLEAN *IncompatibilityFlag OPTIONAL
|
|
)
|
|
{
|
|
RETURN_STATUS Status;
|
|
UINT8 InputPacketBuffer[DEBUG_DATA_UPPER_LIMIT];
|
|
DEBUG_PACKET_HEADER *DebugHeader;
|
|
UINT8 SequenceNo;
|
|
UINT8 HostSequenceNo;
|
|
UINT8 RetryCount;
|
|
|
|
RetryCount = 3;
|
|
DebugHeader = (DEBUG_PACKET_HEADER *) InputPacketBuffer;
|
|
Status = RETURN_TIMEOUT;
|
|
while (RetryCount > 0) {
|
|
SequenceNo = GetMailboxPointer()->SequenceNo;
|
|
HostSequenceNo = GetMailboxPointer()->HostSequenceNo;
|
|
SendPacketWithoutData (Command, SequenceNo);
|
|
Status = ReceivePacket ((UINT8 *) DebugHeader, BreakReceived, IncompatibilityFlag, Timeout, FALSE);
|
|
if (Status == RETURN_TIMEOUT) {
|
|
if (Command == DEBUG_COMMAND_INIT_BREAK) {
|
|
RetryCount--;
|
|
} else {
|
|
DebugAgentMsgPrint (DEBUG_AGENT_WARNING, "TARGET: Timeout when waiting for ACK packet.\n");
|
|
}
|
|
continue;
|
|
}
|
|
ASSERT_EFI_ERROR (Status);
|
|
//
|
|
// Status == RETURN_SUCCESS
|
|
//
|
|
if (DebugHeader->Command == DEBUG_COMMAND_OK && DebugHeader->SequenceNo == SequenceNo) {
|
|
//
|
|
// Received Ack OK
|
|
//
|
|
UpdateMailboxContent (GetMailboxPointer(), DEBUG_MAILBOX_SEQUENCE_NO_INDEX, ++SequenceNo);
|
|
return Status;
|
|
}
|
|
if (DebugHeader->Command == DEBUG_COMMAND_GO && (DebugHeader->SequenceNo == HostSequenceNo || Command == DEBUG_COMMAND_INIT_BREAK)) {
|
|
//
|
|
// Received Old GO
|
|
//
|
|
if (Command == DEBUG_COMMAND_INIT_BREAK) {
|
|
DebugAgentMsgPrint (DEBUG_AGENT_WARNING, "TARGET: Receive GO() in last boot\n");
|
|
}
|
|
SendPacketWithoutData (DEBUG_COMMAND_OK, DebugHeader->SequenceNo);
|
|
}
|
|
}
|
|
|
|
ASSERT (Command == DEBUG_COMMAND_INIT_BREAK);
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Get current break cause.
|
|
|
|
@param[in] Vector Vector value of exception or interrupt.
|
|
@param[in] CpuContext Pointer to save CPU context.
|
|
|
|
@return The type of break cause defined by XXXX
|
|
|
|
**/
|
|
UINT8
|
|
GetBreakCause (
|
|
IN UINTN Vector,
|
|
IN DEBUG_CPU_CONTEXT *CpuContext
|
|
)
|
|
{
|
|
UINT8 Cause;
|
|
|
|
Cause = DEBUG_DATA_BREAK_CAUSE_UNKNOWN;
|
|
|
|
switch (Vector) {
|
|
case DEBUG_INT1_VECTOR:
|
|
case DEBUG_INT3_VECTOR:
|
|
|
|
if (Vector == DEBUG_INT1_VECTOR) {
|
|
//
|
|
// INT 1
|
|
//
|
|
if ((CpuContext->Dr6 & BIT14) != 0) {
|
|
Cause = DEBUG_DATA_BREAK_CAUSE_STEPPING;
|
|
//
|
|
// DR6.BIT14 Indicates (when set) that the debug exception was
|
|
// triggered by the single step execution mode.
|
|
// The single-step mode is the highest priority debug exception.
|
|
// This is single step, no need to check DR0, to ensure single step
|
|
// work in PeCoffExtraActionLib (right after triggering a breakpoint
|
|
// to report image load/unload).
|
|
//
|
|
return Cause;
|
|
|
|
} else {
|
|
Cause = DEBUG_DATA_BREAK_CAUSE_HW_BREAKPOINT;
|
|
}
|
|
} else {
|
|
//
|
|
// INT 3
|
|
//
|
|
Cause = DEBUG_DATA_BREAK_CAUSE_SW_BREAKPOINT;
|
|
}
|
|
|
|
switch (CpuContext->Dr0) {
|
|
case IMAGE_LOAD_SIGNATURE:
|
|
case IMAGE_UNLOAD_SIGNATURE:
|
|
|
|
if (CpuContext->Dr3 == IO_PORT_BREAKPOINT_ADDRESS) {
|
|
|
|
Cause = (UINT8) ((CpuContext->Dr0 == IMAGE_LOAD_SIGNATURE) ?
|
|
DEBUG_DATA_BREAK_CAUSE_IMAGE_LOAD : DEBUG_DATA_BREAK_CAUSE_IMAGE_UNLOAD);
|
|
}
|
|
break;
|
|
|
|
case SOFT_INTERRUPT_SIGNATURE:
|
|
|
|
if (CpuContext->Dr1 == MEMORY_READY_SIGNATURE) {
|
|
Cause = DEBUG_DATA_BREAK_CAUSE_MEMORY_READY;
|
|
CpuContext->Dr0 = 0;
|
|
} else if (CpuContext->Dr1 == SYSTEM_RESET_SIGNATURE) {
|
|
Cause = DEBUG_DATA_BREAK_CAUSE_SYSTEM_RESET;
|
|
CpuContext->Dr0 = 0;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case DEBUG_TIMER_VECTOR:
|
|
Cause = DEBUG_DATA_BREAK_CAUSE_USER_HALT;
|
|
break;
|
|
|
|
default:
|
|
if (Vector < 20) {
|
|
if (GetDebugFlag (DEBUG_AGENT_FLAG_STEPPING) == 1) {
|
|
//
|
|
// If stepping command is executing
|
|
//
|
|
Cause = DEBUG_DATA_BREAK_CAUSE_STEPPING;
|
|
} else {
|
|
Cause = DEBUG_DATA_BREAK_CAUSE_EXCEPTION;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
return Cause;
|
|
}
|
|
|
|
/**
|
|
Copy memory from source to destination with specified width.
|
|
|
|
@param[out] Dest A pointer to the destination buffer of the memory copy.
|
|
@param[in] Src A pointer to the source buffer of the memory copy.
|
|
@param[in] Count The number of data with specified width to copy from source to destination.
|
|
@param[in] Width Data width in byte.
|
|
|
|
**/
|
|
VOID
|
|
CopyMemByWidth (
|
|
OUT UINT8 *Dest,
|
|
IN UINT8 *Src,
|
|
IN UINT16 Count,
|
|
IN UINT8 Width
|
|
)
|
|
{
|
|
UINT8 *Destination;
|
|
UINT8 *Source;
|
|
INT8 Step;
|
|
|
|
if (Src > Dest) {
|
|
Destination = Dest;
|
|
Source = Src;
|
|
Step = Width;
|
|
} else {
|
|
//
|
|
// Copy memory from tail to avoid memory overlap
|
|
//
|
|
Destination = Dest + (Count - 1) * Width;
|
|
Source = Src + (Count - 1) * Width;
|
|
Step = -Width;
|
|
}
|
|
|
|
while (Count-- != 0) {
|
|
switch (Width) {
|
|
case 1:
|
|
*(UINT8 *) Destination = MmioRead8 ((UINTN) Source);
|
|
break;
|
|
case 2:
|
|
*(UINT16 *) Destination = MmioRead16 ((UINTN) Source);
|
|
break;
|
|
case 4:
|
|
*(UINT32 *) Destination = MmioRead32 ((UINTN) Source);
|
|
break;
|
|
case 8:
|
|
*(UINT64 *) Destination = MmioRead64 ((UINTN) Source);
|
|
break;
|
|
default:
|
|
ASSERT (FALSE);
|
|
}
|
|
Source += Step;
|
|
Destination += Step;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Compress the data buffer but do not modify the original buffer.
|
|
|
|
The compressed data is directly send to the debug channel.
|
|
Compressing in place doesn't work because the data may become larger
|
|
during compressing phase. ("3 3 ..." --> "3 3 0 ...")
|
|
The routine is expected to be called three times:
|
|
1. Compute the length of the compressed data buffer;
|
|
2. Compute the CRC of the compressed data buffer;
|
|
3. Compress the data and send to the debug channel.
|
|
|
|
@param[in] Handle The debug channel handle to send the compressed data buffer.
|
|
@param[in] Data The data buffer.
|
|
@param[in] Length The length of the data buffer.
|
|
@param[in] Send TRUE to send the compressed data buffer.
|
|
@param[out] CompressedLength Return the length of the compressed data buffer.
|
|
It may be larger than the Length in some cases.
|
|
@param[out] CompressedCrc Return the CRC of the compressed data buffer.
|
|
**/
|
|
VOID
|
|
CompressData (
|
|
IN DEBUG_PORT_HANDLE Handle,
|
|
IN UINT8 *Data,
|
|
IN UINT8 Length,
|
|
IN BOOLEAN Send,
|
|
OUT UINTN *CompressedLength, OPTIONAL
|
|
OUT UINT16 *CompressedCrc OPTIONAL
|
|
)
|
|
{
|
|
UINTN Index;
|
|
UINT8 LastChar;
|
|
UINT8 LastCharCount;
|
|
UINT8 CurrentChar;
|
|
UINTN CompressedIndex;
|
|
|
|
ASSERT (Length > 0);
|
|
LastChar = Data[0] + 1; // Just ensure it's different from the first byte.
|
|
LastCharCount = 0;
|
|
|
|
for (Index = 0, CompressedIndex = 0; Index <= Length; Index++) {
|
|
if (Index < Length) {
|
|
CurrentChar = Data[Index];
|
|
} else {
|
|
CurrentChar = (UINT8) LastChar + 1; // just ensure it's different from LastChar
|
|
}
|
|
if (LastChar != CurrentChar) {
|
|
if (LastCharCount == 1) {
|
|
CompressedIndex++;
|
|
if (CompressedCrc != NULL) {
|
|
*CompressedCrc = CalculateCrc16 (&LastChar, 1, *CompressedCrc);
|
|
}
|
|
if (Send) {
|
|
DebugPortWriteBuffer (Handle, &LastChar, 1);
|
|
}
|
|
|
|
} else if (LastCharCount >= 2) {
|
|
CompressedIndex += 3;
|
|
LastCharCount -= 2;
|
|
if (CompressedCrc != NULL) {
|
|
*CompressedCrc = CalculateCrc16 (&LastChar, 1, *CompressedCrc);
|
|
*CompressedCrc = CalculateCrc16 (&LastChar, 1, *CompressedCrc);
|
|
*CompressedCrc = CalculateCrc16 (&LastCharCount, 1, *CompressedCrc);
|
|
}
|
|
if (Send) {
|
|
DebugPortWriteBuffer (Handle, &LastChar, 1);
|
|
DebugPortWriteBuffer (Handle, &LastChar, 1);
|
|
DebugPortWriteBuffer (Handle, &LastCharCount, 1);
|
|
}
|
|
}
|
|
LastCharCount = 0;
|
|
}
|
|
LastCharCount++;
|
|
LastChar = CurrentChar;
|
|
}
|
|
|
|
if (CompressedLength != NULL) {
|
|
*CompressedLength = CompressedIndex;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Read memory with speicifed width and send packet with response data to HOST.
|
|
|
|
@param[in] Data Pointer to response data buffer.
|
|
@param[in] Count The number of data with specified Width.
|
|
@param[in] Width Data width in byte.
|
|
@param[in] DebugHeader Pointer to a buffer for creating response packet and receiving ACK packet,
|
|
to minimize the stack usage.
|
|
|
|
@retval RETURN_SUCCESS Response data was sent successfully.
|
|
|
|
**/
|
|
RETURN_STATUS
|
|
ReadMemoryAndSendResponsePacket (
|
|
IN UINT8 *Data,
|
|
IN UINT16 Count,
|
|
IN UINT8 Width,
|
|
IN DEBUG_PACKET_HEADER *DebugHeader
|
|
)
|
|
{
|
|
RETURN_STATUS Status;
|
|
BOOLEAN LastPacket;
|
|
DEBUG_PORT_HANDLE Handle;
|
|
UINT8 SequenceNo;
|
|
UINTN RemainingDataSize;
|
|
UINT8 CurrentDataSize;
|
|
UINTN CompressedDataSize;
|
|
|
|
Handle = GetDebugPortHandle();
|
|
|
|
RemainingDataSize = Count * Width;
|
|
while (TRUE) {
|
|
SequenceNo = GetMailboxPointer()->HostSequenceNo;
|
|
if (RemainingDataSize <= DEBUG_DATA_MAXIMUM_REAL_DATA) {
|
|
//
|
|
// If the remaining data is less one real packet size, this is the last data packet
|
|
//
|
|
CurrentDataSize = (UINT8) RemainingDataSize;
|
|
LastPacket = TRUE;
|
|
DebugHeader->Command = DEBUG_COMMAND_OK;
|
|
} else {
|
|
//
|
|
// Data is too larger to be sent in one packet, calculate the actual data size could
|
|
// be sent in one Maximum data packet
|
|
//
|
|
CurrentDataSize = (DEBUG_DATA_MAXIMUM_REAL_DATA / Width) * Width;
|
|
LastPacket = FALSE;
|
|
DebugHeader->Command = DEBUG_COMMAND_IN_PROGRESS;
|
|
}
|
|
//
|
|
// Construct the rest Debug header
|
|
//
|
|
DebugHeader->StartSymbol = DEBUG_STARTING_SYMBOL_NORMAL;
|
|
DebugHeader->Length = CurrentDataSize + sizeof (DEBUG_PACKET_HEADER);
|
|
DebugHeader->SequenceNo = SequenceNo;
|
|
DebugHeader->Crc = 0;
|
|
CopyMemByWidth ((UINT8 *) (DebugHeader + 1), Data, CurrentDataSize / Width, Width);
|
|
|
|
//
|
|
// Compression/decompression support was added since revision 0.4.
|
|
// Revision 0.3 shouldn't compress the packet.
|
|
//
|
|
if (DEBUG_AGENT_REVISION >= DEBUG_AGENT_REVISION_04) {
|
|
//
|
|
// Get the compressed data size without modifying the packet.
|
|
//
|
|
CompressData (
|
|
Handle,
|
|
(UINT8 *) (DebugHeader + 1),
|
|
CurrentDataSize,
|
|
FALSE,
|
|
&CompressedDataSize,
|
|
NULL
|
|
);
|
|
} else {
|
|
CompressedDataSize = CurrentDataSize;
|
|
}
|
|
if (CompressedDataSize < CurrentDataSize) {
|
|
DebugHeader->Length = (UINT8) CompressedDataSize + sizeof (DEBUG_PACKET_HEADER);
|
|
DebugHeader->StartSymbol = DEBUG_STARTING_SYMBOL_COMPRESS;
|
|
//
|
|
// Compute the CRC of the packet head without modifying the packet.
|
|
//
|
|
DebugHeader->Crc = CalculateCrc16 ((UINT8 *) DebugHeader, sizeof (DEBUG_PACKET_HEADER), 0);
|
|
CompressData (
|
|
Handle,
|
|
(UINT8 *) (DebugHeader + 1),
|
|
CurrentDataSize,
|
|
FALSE,
|
|
NULL,
|
|
&DebugHeader->Crc
|
|
);
|
|
//
|
|
// Send out the packet head.
|
|
//
|
|
DebugPortWriteBuffer (Handle, (UINT8 *) DebugHeader, sizeof (DEBUG_PACKET_HEADER));
|
|
//
|
|
// Compress and send out the packet data.
|
|
//
|
|
CompressData (
|
|
Handle,
|
|
(UINT8 *) (DebugHeader + 1),
|
|
CurrentDataSize,
|
|
TRUE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
} else {
|
|
|
|
//
|
|
// Calculate and fill the checksum, DebugHeader->Crc should be 0 before invoking CalculateCrc16 ()
|
|
//
|
|
DebugHeader->Crc = CalculateCrc16 ((UINT8 *) DebugHeader, DebugHeader->Length, 0);
|
|
|
|
DebugAgentDataMsgPrint (DEBUG_AGENT_VERBOSE, TRUE, (UINT8 *) DebugHeader, DebugHeader->Length);
|
|
|
|
DebugPortWriteBuffer (Handle, (UINT8 *) DebugHeader, DebugHeader->Length);
|
|
}
|
|
|
|
while (TRUE) {
|
|
Status = ReceivePacket ((UINT8 *) DebugHeader, NULL, NULL, READ_PACKET_TIMEOUT, FALSE);
|
|
if (Status == RETURN_TIMEOUT) {
|
|
DebugAgentMsgPrint (DEBUG_AGENT_WARNING, "TARGET: Timeout in SendDataResponsePacket()\n");
|
|
break;
|
|
}
|
|
if ((DebugHeader->Command == DEBUG_COMMAND_OK) && (DebugHeader->SequenceNo == SequenceNo) && LastPacket) {
|
|
//
|
|
// If this is the last packet, return RETURN_SUCCESS.
|
|
//
|
|
return RETURN_SUCCESS;
|
|
}
|
|
if ((DebugHeader->Command == DEBUG_COMMAND_CONTINUE) && (DebugHeader->SequenceNo == (UINT8) (SequenceNo + 1))) {
|
|
//
|
|
// Calculate the rest data size
|
|
//
|
|
Data += CurrentDataSize;
|
|
RemainingDataSize -= CurrentDataSize;
|
|
UpdateMailboxContent (GetMailboxPointer(), DEBUG_MAILBOX_HOST_SEQUENCE_NO_INDEX, DebugHeader->SequenceNo);
|
|
break;
|
|
}
|
|
if (DebugHeader->SequenceNo >= SequenceNo) {
|
|
DebugAgentMsgPrint (DEBUG_AGENT_WARNING, "TARGET: Received one old or new command(SequenceNo is %x, last SequenceNo is %x)\n", SequenceNo, DebugHeader->SequenceNo);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Send packet with response data to HOST.
|
|
|
|
@param[in] Data Pointer to response data buffer.
|
|
@param[in] DataSize Size of response data in byte.
|
|
@param[in, out] DebugHeader Pointer to a buffer for creating response packet and receiving ACK packet,
|
|
to minimize the stack usage.
|
|
|
|
@retval RETURN_SUCCESS Response data was sent successfully.
|
|
|
|
**/
|
|
RETURN_STATUS
|
|
SendDataResponsePacket (
|
|
IN UINT8 *Data,
|
|
IN UINT16 DataSize,
|
|
IN OUT DEBUG_PACKET_HEADER *DebugHeader
|
|
)
|
|
{
|
|
return ReadMemoryAndSendResponsePacket (Data, DataSize, 1, DebugHeader);
|
|
}
|
|
|
|
/**
|
|
Try to attach the HOST.
|
|
|
|
Send init break packet to HOST:
|
|
If no acknowlege received in specified Timeout, return RETURN_TIMEOUT.
|
|
If received acknowlege, check the revision of HOST.
|
|
Set Attach Flag if attach successfully.
|
|
|
|
@param[in] BreakCause Break cause of this break event.
|
|
@param[in] Timeout Time out value to wait for acknowlege from HOST.
|
|
The unit is microsecond.
|
|
@param[out] BreakReceived If BreakReceived is not NULL,
|
|
TRUE is retured if break-in symbol received.
|
|
FALSE is retured if break-in symbol not received.
|
|
**/
|
|
RETURN_STATUS
|
|
AttachHost (
|
|
IN UINT8 BreakCause,
|
|
IN UINTN Timeout,
|
|
OUT BOOLEAN *BreakReceived
|
|
)
|
|
{
|
|
RETURN_STATUS Status;
|
|
DEBUG_PORT_HANDLE Handle;
|
|
BOOLEAN IncompatibilityFlag;
|
|
|
|
IncompatibilityFlag = FALSE;
|
|
Handle = GetDebugPortHandle();
|
|
|
|
//
|
|
// Send init break and wait ack in Timeout
|
|
//
|
|
DebugPortWriteBuffer (Handle, (UINT8 *) mErrorMsgSendInitPacket, AsciiStrLen (mErrorMsgSendInitPacket));
|
|
if (BreakCause == DEBUG_DATA_BREAK_CAUSE_SYSTEM_RESET) {
|
|
Status = SendCommandAndWaitForAckOK (DEBUG_COMMAND_INIT_BREAK, Timeout, BreakReceived, &IncompatibilityFlag);
|
|
} else {
|
|
Status = SendCommandAndWaitForAckOK (DEBUG_COMMAND_ATTACH_BREAK, Timeout, BreakReceived, &IncompatibilityFlag);
|
|
}
|
|
if (IncompatibilityFlag) {
|
|
//
|
|
// If the incompatible Debug Packet received, the HOST should be running transfer protocol before DEBUG_AGENT_REVISION.
|
|
// It could be UDK Debugger for Windows v1.1/v1.2 or for Linux v0.8/v1.2.
|
|
//
|
|
DebugPortWriteBuffer (Handle, (UINT8 *) mErrorMsgVersionAlert, AsciiStrLen (mErrorMsgVersionAlert));
|
|
CpuDeadLoop ();
|
|
}
|
|
|
|
if (RETURN_ERROR (Status)) {
|
|
DebugPortWriteBuffer (Handle, (UINT8 *) mErrorMsgConnectFail, AsciiStrLen (mErrorMsgConnectFail));
|
|
} else {
|
|
DebugPortWriteBuffer (Handle, (UINT8 *) mErrorMsgConnectOK, AsciiStrLen (mErrorMsgConnectOK));
|
|
//
|
|
// Set Attach flag
|
|
//
|
|
SetHostAttached (TRUE);
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Send Break point packet to HOST.
|
|
|
|
Only the first breaking processor could sent BREAK_POINT packet.
|
|
|
|
@param[in] BreakCause Break cause of this break event.
|
|
@param[in] ProcessorIndex Processor index value.
|
|
@param[out] BreakReceived If BreakReceived is not NULL,
|
|
TRUE is retured if break-in symbol received.
|
|
FALSE is retured if break-in symbol not received.
|
|
|
|
**/
|
|
VOID
|
|
SendBreakPacketToHost (
|
|
IN UINT8 BreakCause,
|
|
IN UINT32 ProcessorIndex,
|
|
OUT BOOLEAN *BreakReceived
|
|
)
|
|
{
|
|
UINT8 InputCharacter;
|
|
DEBUG_PORT_HANDLE Handle;
|
|
|
|
Handle = GetDebugPortHandle();
|
|
|
|
if (IsHostAttached ()) {
|
|
DebugAgentMsgPrint (DEBUG_AGENT_INFO, "processor[%x]:Send Break Packet to HOST.\n", ProcessorIndex);
|
|
SendCommandAndWaitForAckOK (DEBUG_COMMAND_BREAK_POINT, READ_PACKET_TIMEOUT, BreakReceived, NULL);
|
|
} else {
|
|
DebugAgentMsgPrint (DEBUG_AGENT_INFO, "processor[%x]:Try to attach HOST.\n", ProcessorIndex);
|
|
//
|
|
// If HOST is not attached, try to attach it firstly.
|
|
//
|
|
//
|
|
// Poll Attach symbols from HOST and ack OK
|
|
//
|
|
do {
|
|
DebugAgentReadBuffer (Handle, &InputCharacter, 1, 0);
|
|
} while (InputCharacter != DEBUG_STARTING_SYMBOL_ATTACH);
|
|
SendAckPacket (DEBUG_COMMAND_OK);
|
|
|
|
//
|
|
// Try to attach HOST
|
|
//
|
|
while (AttachHost (BreakCause, 0, NULL) != RETURN_SUCCESS);
|
|
|
|
}
|
|
}
|
|
|
|
/**
|
|
The main function to process communication with HOST.
|
|
|
|
It received the command packet from HOST, and sent response data packet to HOST.
|
|
|
|
@param[in] Vector Vector value of exception or interrutp.
|
|
@param[in, out] CpuContext Pointer to saved CPU context.
|
|
@param[in] BreakReceived TRUE means break-in symbol received.
|
|
FALSE means break-in symbol not received.
|
|
|
|
**/
|
|
VOID
|
|
CommandCommunication (
|
|
IN UINTN Vector,
|
|
IN OUT DEBUG_CPU_CONTEXT *CpuContext,
|
|
IN BOOLEAN BreakReceived
|
|
)
|
|
{
|
|
RETURN_STATUS Status;
|
|
UINT8 InputPacketBuffer[DEBUG_DATA_UPPER_LIMIT + sizeof (UINT64) - 1];
|
|
DEBUG_PACKET_HEADER *DebugHeader;
|
|
UINT8 Width;
|
|
UINT8 Data8;
|
|
UINT32 Data32;
|
|
UINT64 Data64;
|
|
DEBUG_DATA_READ_MEMORY *MemoryRead;
|
|
DEBUG_DATA_WRITE_MEMORY *MemoryWrite;
|
|
DEBUG_DATA_READ_IO *IoRead;
|
|
DEBUG_DATA_WRITE_IO *IoWrite;
|
|
DEBUG_DATA_READ_REGISTER *RegisterRead;
|
|
DEBUG_DATA_WRITE_REGISTER *RegisterWrite;
|
|
UINT8 *RegisterBuffer;
|
|
DEBUG_DATA_READ_MSR *MsrRegisterRead;
|
|
DEBUG_DATA_WRITE_MSR *MsrRegisterWrite;
|
|
DEBUG_DATA_CPUID *Cpuid;
|
|
DEBUG_DATA_RESPONSE_BREAK_CAUSE BreakCause;
|
|
DEBUG_DATA_RESPONSE_CPUID CpuidResponse;
|
|
DEBUG_DATA_SEARCH_SIGNATURE *SearchSignature;
|
|
DEBUG_DATA_RESPONSE_GET_EXCEPTION Exception;
|
|
DEBUG_DATA_RESPONSE_GET_REVISION DebugAgentRevision;
|
|
DEBUG_DATA_SET_VIEWPOINT *SetViewPoint;
|
|
BOOLEAN HaltDeferred;
|
|
UINT32 ProcessorIndex;
|
|
DEBUG_AGENT_EXCEPTION_BUFFER AgentExceptionBuffer;
|
|
UINT32 IssuedViewPoint;
|
|
DEBUG_AGENT_MAILBOX *Mailbox;
|
|
UINT8 *AlignedDataPtr;
|
|
|
|
ProcessorIndex = 0;
|
|
IssuedViewPoint = 0;
|
|
HaltDeferred = BreakReceived;
|
|
|
|
if (MultiProcessorDebugSupport()) {
|
|
ProcessorIndex = GetProcessorIndex ();
|
|
SetCpuStopFlagByIndex (ProcessorIndex, TRUE);
|
|
if (mDebugMpContext.ViewPointIndex == ProcessorIndex) {
|
|
//
|
|
// Only the current view processor could set AgentInProgress Flag.
|
|
//
|
|
IssuedViewPoint = ProcessorIndex;
|
|
}
|
|
}
|
|
|
|
if (IssuedViewPoint == ProcessorIndex) {
|
|
//
|
|
// Set AgentInProgress Flag.
|
|
//
|
|
SetDebugFlag (DEBUG_AGENT_FLAG_AGENT_IN_PROGRESS, 1);
|
|
}
|
|
|
|
while (TRUE) {
|
|
|
|
if (MultiProcessorDebugSupport()) {
|
|
//
|
|
// Check if the current processor is HOST view point
|
|
//
|
|
if (mDebugMpContext.ViewPointIndex != ProcessorIndex) {
|
|
if (mDebugMpContext.RunCommandSet) {
|
|
//
|
|
// If HOST view point sets RUN flag, run GO command to leave
|
|
//
|
|
SetCpuStopFlagByIndex (ProcessorIndex, FALSE);
|
|
CommandGo (CpuContext);
|
|
break;
|
|
} else {
|
|
//
|
|
// Run into loop again
|
|
//
|
|
CpuPause ();
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
AcquireMpSpinLock (&mDebugMpContext.DebugPortSpinLock);
|
|
|
|
DebugHeader =(DEBUG_PACKET_HEADER *) InputPacketBuffer;
|
|
|
|
DebugAgentMsgPrint (DEBUG_AGENT_INFO, "TARGET: Try to get command from HOST...\n");
|
|
Status = ReceivePacket ((UINT8 *) DebugHeader, &BreakReceived, NULL, READ_PACKET_TIMEOUT, TRUE);
|
|
if (Status != RETURN_SUCCESS || !IS_REQUEST (DebugHeader)) {
|
|
DebugAgentMsgPrint (DEBUG_AGENT_WARNING, "TARGET: Get command[%x] sequenceno[%x] returned status is [%x] \n", DebugHeader->Command, DebugHeader->SequenceNo, Status);
|
|
DebugAgentMsgPrint (DEBUG_AGENT_WARNING, "TARGET: Get command failed or it's response packet not expected! \n");
|
|
ReleaseMpSpinLock (&mDebugMpContext.DebugPortSpinLock);
|
|
continue;
|
|
}
|
|
|
|
Mailbox = GetMailboxPointer ();
|
|
if (DebugHeader->SequenceNo == Mailbox->HostSequenceNo) {
|
|
DebugAgentMsgPrint (DEBUG_AGENT_WARNING, "TARGET: Receive one old command[%x] agaist command[%x]\n", DebugHeader->SequenceNo, Mailbox->HostSequenceNo);
|
|
SendAckPacket (Mailbox->LastAck);
|
|
ReleaseMpSpinLock (&mDebugMpContext.DebugPortSpinLock);
|
|
continue;
|
|
} else if (DebugHeader->SequenceNo == (UINT8) (Mailbox->HostSequenceNo + 1)) {
|
|
UpdateMailboxContent (Mailbox, DEBUG_MAILBOX_HOST_SEQUENCE_NO_INDEX, (UINT8) DebugHeader->SequenceNo);
|
|
} else {
|
|
DebugAgentMsgPrint (DEBUG_AGENT_WARNING, "Receive one invalid comamnd[%x] agaist command[%x]\n", DebugHeader->SequenceNo, Mailbox->HostSequenceNo);
|
|
ReleaseMpSpinLock (&mDebugMpContext.DebugPortSpinLock);
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Save CPU content before executing HOST commond
|
|
//
|
|
UpdateMailboxContent (Mailbox, DEBUG_MAILBOX_EXCEPTION_BUFFER_POINTER_INDEX, (UINT64)(UINTN) &AgentExceptionBuffer.JumpBuffer);
|
|
if (SetJump (&AgentExceptionBuffer.JumpBuffer) != 0) {
|
|
//
|
|
// If HOST command failed, continue to wait for HOST's next command
|
|
// If needed, agent could send exception info to HOST.
|
|
//
|
|
SendAckPacket (DEBUG_COMMAND_ABORT);
|
|
ReleaseMpSpinLock (&mDebugMpContext.DebugPortSpinLock);
|
|
continue;
|
|
}
|
|
|
|
DebugAgentMsgPrint (DEBUG_AGENT_INFO, "Processor[%x]:Received one command(%x)\n", mDebugMpContext.ViewPointIndex, DebugHeader->Command);
|
|
|
|
switch (DebugHeader->Command) {
|
|
|
|
case DEBUG_COMMAND_HALT:
|
|
SendAckPacket (DEBUG_COMMAND_HALT_DEFERRED);
|
|
HaltDeferred = TRUE;
|
|
BreakReceived = FALSE;
|
|
Status = RETURN_SUCCESS;
|
|
break;
|
|
|
|
case DEBUG_COMMAND_RESET:
|
|
SendAckPacket (DEBUG_COMMAND_OK);
|
|
SendAckPacket (DEBUG_COMMAND_OK);
|
|
SendAckPacket (DEBUG_COMMAND_OK);
|
|
ReleaseMpSpinLock (&mDebugMpContext.DebugPortSpinLock);
|
|
|
|
ResetCold ();
|
|
//
|
|
// Assume system resets in 2 seconds, otherwise send TIMEOUT packet.
|
|
// PCD can be used if 2 seconds isn't long enough for some platforms.
|
|
//
|
|
MicroSecondDelay (2000000);
|
|
UpdateMailboxContent (Mailbox, DEBUG_MAILBOX_HOST_SEQUENCE_NO_INDEX, Mailbox->HostSequenceNo + 1);
|
|
SendAckPacket (DEBUG_COMMAND_TIMEOUT);
|
|
SendAckPacket (DEBUG_COMMAND_TIMEOUT);
|
|
SendAckPacket (DEBUG_COMMAND_TIMEOUT);
|
|
break;
|
|
|
|
case DEBUG_COMMAND_GO:
|
|
CommandGo (CpuContext);
|
|
//
|
|
// Clear Dr0 to avoid to be recognized as IMAGE_LOAD/_UNLOAD again when hitting a breakpoint after GO
|
|
// If HOST changed Dr0 before GO, we will not change Dr0 here
|
|
//
|
|
Data8 = GetBreakCause (Vector, CpuContext);
|
|
if (Data8 == DEBUG_DATA_BREAK_CAUSE_IMAGE_LOAD || Data8 == DEBUG_DATA_BREAK_CAUSE_IMAGE_UNLOAD) {
|
|
CpuContext->Dr0 = 0;
|
|
}
|
|
|
|
if (!HaltDeferred) {
|
|
//
|
|
// If no HALT command received when being in-active mode
|
|
//
|
|
if (MultiProcessorDebugSupport()) {
|
|
Data32 = FindNextPendingBreakCpu ();
|
|
if (Data32 != -1) {
|
|
//
|
|
// If there are still others processors being in break state,
|
|
// send OK packet to HOST to finish this go command
|
|
//
|
|
SendAckPacket (DEBUG_COMMAND_OK);
|
|
CpuPause ();
|
|
//
|
|
// Set current view to the next breaking processor
|
|
//
|
|
mDebugMpContext.ViewPointIndex = Data32;
|
|
mDebugMpContext.BreakAtCpuIndex = mDebugMpContext.ViewPointIndex;
|
|
SetCpuBreakFlagByIndex (mDebugMpContext.ViewPointIndex, FALSE);
|
|
//
|
|
// Send break packet to HOST to let HOST break again
|
|
//
|
|
SendBreakPacketToHost (DEBUG_DATA_BREAK_CAUSE_UNKNOWN, mDebugMpContext.BreakAtCpuIndex, &BreakReceived);
|
|
//
|
|
// Continue to run into loop to read command packet from HOST
|
|
//
|
|
ReleaseMpSpinLock (&mDebugMpContext.DebugPortSpinLock);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If no else processor break, set stop bitmask,
|
|
// and set Running flag for all processors.
|
|
//
|
|
SetCpuStopFlagByIndex (ProcessorIndex, FALSE);
|
|
SetCpuRunningFlag (TRUE);
|
|
CpuPause ();
|
|
//
|
|
// Wait for all processors are in running state
|
|
//
|
|
while (TRUE) {
|
|
if (IsAllCpuRunning ()) {
|
|
break;
|
|
}
|
|
}
|
|
//
|
|
// Set BSP to be current view point.
|
|
//
|
|
SetDebugViewPoint (mDebugMpContext.BspIndex);
|
|
CpuPause ();
|
|
//
|
|
// Clear breaking processor index and running flag
|
|
//
|
|
mDebugMpContext.BreakAtCpuIndex = (UINT32) (-1);
|
|
SetCpuRunningFlag (FALSE);
|
|
}
|
|
|
|
//
|
|
// Send OK packet to HOST to finish this go command
|
|
//
|
|
SendAckPacket (DEBUG_COMMAND_OK);
|
|
|
|
ReleaseMpSpinLock (&mDebugMpContext.DebugPortSpinLock);
|
|
|
|
if (!IsHostAttached()) {
|
|
UpdateMailboxContent (Mailbox, DEBUG_MAILBOX_SEQUENCE_NO_INDEX, 0);
|
|
UpdateMailboxContent (Mailbox, DEBUG_MAILBOX_HOST_SEQUENCE_NO_INDEX, 0);
|
|
}
|
|
return;
|
|
|
|
} else {
|
|
//
|
|
// If reveived HALT command, need to defer the GO command
|
|
//
|
|
SendAckPacket (DEBUG_COMMAND_HALT_PROCESSED);
|
|
HaltDeferred = FALSE;
|
|
|
|
Vector = DEBUG_TIMER_VECTOR;
|
|
}
|
|
break;
|
|
|
|
case DEBUG_COMMAND_BREAK_CAUSE:
|
|
BreakCause.StopAddress = CpuContext->Eip;
|
|
if (MultiProcessorDebugSupport() && ProcessorIndex != mDebugMpContext.BreakAtCpuIndex) {
|
|
BreakCause.Cause = GetBreakCause (DEBUG_TIMER_VECTOR, CpuContext);
|
|
} else {
|
|
BreakCause.Cause = GetBreakCause (Vector, CpuContext);
|
|
}
|
|
SendDataResponsePacket ((UINT8 *) &BreakCause, (UINT16) sizeof (DEBUG_DATA_RESPONSE_BREAK_CAUSE), DebugHeader);
|
|
break;
|
|
|
|
case DEBUG_COMMAND_SET_HW_BREAKPOINT:
|
|
SetDebugRegister (CpuContext, (DEBUG_DATA_SET_HW_BREAKPOINT *) (DebugHeader + 1));
|
|
SendAckPacket (DEBUG_COMMAND_OK);
|
|
break;
|
|
|
|
case DEBUG_COMMAND_CLEAR_HW_BREAKPOINT:
|
|
ClearDebugRegister (CpuContext, (DEBUG_DATA_CLEAR_HW_BREAKPOINT *) (DebugHeader + 1));
|
|
SendAckPacket (DEBUG_COMMAND_OK);
|
|
break;
|
|
|
|
case DEBUG_COMMAND_SINGLE_STEPPING:
|
|
CommandStepping (CpuContext);
|
|
//
|
|
// Clear Dr0 to avoid to be recognized as IMAGE_LOAD/_UNLOAD again when hitting a breakpoint after GO
|
|
// If HOST changed Dr0 before GO, we will not change Dr0 here
|
|
//
|
|
Data8 = GetBreakCause (Vector, CpuContext);
|
|
if (Data8 == DEBUG_DATA_BREAK_CAUSE_IMAGE_LOAD || Data8 == DEBUG_DATA_BREAK_CAUSE_IMAGE_UNLOAD) {
|
|
CpuContext->Dr0 = 0;
|
|
}
|
|
|
|
mDebugMpContext.BreakAtCpuIndex = (UINT32) (-1);
|
|
ReleaseMpSpinLock (&mDebugMpContext.DebugPortSpinLock);
|
|
//
|
|
// Executing stepping command directly without sending ACK packet,
|
|
// ACK packet will be sent after stepping done.
|
|
//
|
|
return;
|
|
|
|
case DEBUG_COMMAND_SET_SW_BREAKPOINT:
|
|
Data64 = (UINTN) (((DEBUG_DATA_SET_SW_BREAKPOINT *) (DebugHeader + 1))->Address);
|
|
Data8 = *(UINT8 *) (UINTN) Data64;
|
|
*(UINT8 *) (UINTN) Data64 = DEBUG_SW_BREAKPOINT_SYMBOL;
|
|
Status = SendDataResponsePacket ((UINT8 *) &Data8, (UINT16) sizeof (UINT8), DebugHeader);
|
|
break;
|
|
|
|
case DEBUG_COMMAND_READ_MEMORY:
|
|
MemoryRead = (DEBUG_DATA_READ_MEMORY *) (DebugHeader + 1);
|
|
Status = ReadMemoryAndSendResponsePacket ((UINT8 *) (UINTN) MemoryRead->Address, MemoryRead->Count, MemoryRead->Width, DebugHeader);
|
|
break;
|
|
|
|
case DEBUG_COMMAND_WRITE_MEMORY:
|
|
MemoryWrite = (DEBUG_DATA_WRITE_MEMORY *) (DebugHeader + 1);
|
|
//
|
|
// Copy data into one memory with 8-byte alignment address
|
|
//
|
|
AlignedDataPtr = ALIGN_POINTER ((UINT8 *) &MemoryWrite->Data, sizeof (UINT64));
|
|
if (AlignedDataPtr != (UINT8 *) &MemoryWrite->Data) {
|
|
CopyMem (AlignedDataPtr, (UINT8 *) &MemoryWrite->Data, MemoryWrite->Count * MemoryWrite->Width);
|
|
}
|
|
CopyMemByWidth ((UINT8 *) (UINTN) MemoryWrite->Address, AlignedDataPtr, MemoryWrite->Count, MemoryWrite->Width);
|
|
SendAckPacket (DEBUG_COMMAND_OK);
|
|
break;
|
|
|
|
case DEBUG_COMMAND_READ_IO:
|
|
IoRead = (DEBUG_DATA_READ_IO *) (DebugHeader + 1);
|
|
switch (IoRead->Width) {
|
|
case 1:
|
|
Data64 = IoRead8 ((UINTN) IoRead->Port);
|
|
break;
|
|
case 2:
|
|
Data64 = IoRead16 ((UINTN) IoRead->Port);
|
|
break;
|
|
case 4:
|
|
Data64 = IoRead32 ((UINTN) IoRead->Port);
|
|
break;
|
|
case 8:
|
|
Data64 = IoRead64 ((UINTN) IoRead->Port);
|
|
break;
|
|
default:
|
|
Data64 = (UINT64) -1;
|
|
}
|
|
Status = SendDataResponsePacket ((UINT8 *) &Data64, IoRead->Width, DebugHeader);
|
|
break;
|
|
|
|
case DEBUG_COMMAND_WRITE_IO:
|
|
IoWrite = (DEBUG_DATA_WRITE_IO *) (DebugHeader + 1);
|
|
switch (IoWrite->Width) {
|
|
case 1:
|
|
Data64 = IoWrite8 ((UINTN) IoWrite->Port, *(UINT8 *) &IoWrite->Data);
|
|
break;
|
|
case 2:
|
|
Data64 = IoWrite16 ((UINTN) IoWrite->Port, *(UINT16 *) &IoWrite->Data);
|
|
break;
|
|
case 4:
|
|
Data64 = IoWrite32 ((UINTN) IoWrite->Port, *(UINT32 *) &IoWrite->Data);
|
|
break;
|
|
case 8:
|
|
Data64 = IoWrite64 ((UINTN) IoWrite->Port, *(UINT64 *) &IoWrite->Data);
|
|
break;
|
|
default:
|
|
Data64 = (UINT64) -1;
|
|
}
|
|
SendAckPacket (DEBUG_COMMAND_OK);
|
|
break;
|
|
|
|
case DEBUG_COMMAND_READ_ALL_REGISTERS:
|
|
Status = SendDataResponsePacket ((UINT8 *) CpuContext, sizeof (*CpuContext), DebugHeader);
|
|
break;
|
|
|
|
case DEBUG_COMMAND_READ_REGISTER:
|
|
RegisterRead = (DEBUG_DATA_READ_REGISTER *) (DebugHeader + 1);
|
|
|
|
if (RegisterRead->Index <= SOFT_DEBUGGER_REGISTER_MAX) {
|
|
RegisterBuffer = ArchReadRegisterBuffer (CpuContext, RegisterRead->Index, &Width);
|
|
Status = SendDataResponsePacket (RegisterBuffer, Width, DebugHeader);
|
|
} else {
|
|
Status = RETURN_UNSUPPORTED;
|
|
}
|
|
break;
|
|
|
|
case DEBUG_COMMAND_WRITE_REGISTER:
|
|
RegisterWrite = (DEBUG_DATA_WRITE_REGISTER *) (DebugHeader + 1);
|
|
if (RegisterWrite->Index <= SOFT_DEBUGGER_REGISTER_MAX) {
|
|
RegisterBuffer = ArchReadRegisterBuffer (CpuContext, RegisterWrite->Index, &Width);
|
|
ASSERT (Width == RegisterWrite->Length);
|
|
CopyMem (RegisterBuffer, RegisterWrite->Data, Width);
|
|
SendAckPacket (DEBUG_COMMAND_OK);
|
|
} else {
|
|
Status = RETURN_UNSUPPORTED;
|
|
}
|
|
break;
|
|
|
|
case DEBUG_COMMAND_ARCH_MODE:
|
|
Data8 = DEBUG_ARCH_SYMBOL;
|
|
Status = SendDataResponsePacket ((UINT8 *) &Data8, (UINT16) sizeof (UINT8), DebugHeader);
|
|
break;
|
|
|
|
case DEBUG_COMMAND_READ_MSR:
|
|
MsrRegisterRead = (DEBUG_DATA_READ_MSR *) (DebugHeader + 1);
|
|
Data64 = AsmReadMsr64 (MsrRegisterRead->Index);
|
|
Status = SendDataResponsePacket ((UINT8 *) &Data64, (UINT16) sizeof (UINT64), DebugHeader);
|
|
break;
|
|
|
|
case DEBUG_COMMAND_WRITE_MSR:
|
|
MsrRegisterWrite = (DEBUG_DATA_WRITE_MSR *) (DebugHeader + 1);
|
|
AsmWriteMsr64 (MsrRegisterWrite->Index, MsrRegisterWrite->Value);
|
|
SendAckPacket (DEBUG_COMMAND_OK);
|
|
break;
|
|
|
|
case DEBUG_COMMAND_SET_DEBUG_SETTING:
|
|
Status = SetDebugSetting ((DEBUG_DATA_SET_DEBUG_SETTING *)(DebugHeader + 1));
|
|
if (Status == RETURN_SUCCESS) {
|
|
SendAckPacket (DEBUG_COMMAND_OK);
|
|
}
|
|
break;
|
|
|
|
case DEBUG_COMMAND_GET_REVISION:
|
|
DebugAgentRevision.Revision = DEBUG_AGENT_REVISION;
|
|
DebugAgentRevision.Capabilities = DEBUG_AGENT_CAPABILITIES;
|
|
Status = SendDataResponsePacket ((UINT8 *) &DebugAgentRevision, (UINT16) sizeof (DEBUG_DATA_RESPONSE_GET_REVISION), DebugHeader);
|
|
break;
|
|
|
|
case DEBUG_COMMAND_GET_EXCEPTION:
|
|
Exception.ExceptionNum = (UINT8) Vector;
|
|
Exception.ExceptionData = (UINT32) CpuContext->ExceptionData;
|
|
Status = SendDataResponsePacket ((UINT8 *) &Exception, (UINT16) sizeof (DEBUG_DATA_RESPONSE_GET_EXCEPTION), DebugHeader);
|
|
break;
|
|
|
|
case DEBUG_COMMAND_SET_VIEWPOINT:
|
|
SetViewPoint = (DEBUG_DATA_SET_VIEWPOINT *) (DebugHeader + 1);
|
|
if (MultiProcessorDebugSupport()) {
|
|
if (IsCpuStopped (SetViewPoint->ViewPoint)) {
|
|
SetDebugViewPoint (SetViewPoint->ViewPoint);
|
|
SendAckPacket (DEBUG_COMMAND_OK);
|
|
} else {
|
|
//
|
|
// If CPU is not halted
|
|
//
|
|
SendAckPacket (DEBUG_COMMAND_NOT_SUPPORTED);
|
|
}
|
|
} else if (SetViewPoint->ViewPoint == 0) {
|
|
SendAckPacket (DEBUG_COMMAND_OK);
|
|
|
|
} else {
|
|
SendAckPacket (DEBUG_COMMAND_NOT_SUPPORTED);
|
|
}
|
|
|
|
break;
|
|
|
|
case DEBUG_COMMAND_GET_VIEWPOINT:
|
|
Data32 = mDebugMpContext.ViewPointIndex;
|
|
SendDataResponsePacket((UINT8 *) &Data32, (UINT16) sizeof (UINT32), DebugHeader);
|
|
break;
|
|
|
|
case DEBUG_COMMAND_MEMORY_READY:
|
|
Data8 = (UINT8) GetDebugFlag (DEBUG_AGENT_FLAG_MEMORY_READY);
|
|
SendDataResponsePacket (&Data8, (UINT16) sizeof (UINT8), DebugHeader);
|
|
break;
|
|
|
|
case DEBUG_COMMAND_DETACH:
|
|
SetHostAttached (FALSE);
|
|
SendAckPacket (DEBUG_COMMAND_OK);
|
|
break;
|
|
|
|
case DEBUG_COMMAND_CPUID:
|
|
Cpuid = (DEBUG_DATA_CPUID *) (DebugHeader + 1);
|
|
AsmCpuidEx (
|
|
Cpuid->Eax, Cpuid->Ecx,
|
|
&CpuidResponse.Eax, &CpuidResponse.Ebx,
|
|
&CpuidResponse.Ecx, &CpuidResponse.Edx
|
|
);
|
|
SendDataResponsePacket ((UINT8 *) &CpuidResponse, (UINT16) sizeof (CpuidResponse), DebugHeader);
|
|
break;
|
|
|
|
case DEBUG_COMMAND_SEARCH_SIGNATURE:
|
|
SearchSignature = (DEBUG_DATA_SEARCH_SIGNATURE *) (DebugHeader + 1);
|
|
if ((SearchSignature->Alignment != 0) &&
|
|
(SearchSignature->Alignment == GetPowerOfTwo32 (SearchSignature->Alignment))
|
|
) {
|
|
if (SearchSignature->Positive) {
|
|
for (
|
|
Data64 = ALIGN_VALUE ((UINTN) SearchSignature->Start, SearchSignature->Alignment);
|
|
Data64 <= SearchSignature->Start + SearchSignature->Count - SearchSignature->DataLength;
|
|
Data64 += SearchSignature->Alignment
|
|
) {
|
|
if (CompareMem ((VOID *) (UINTN) Data64, &SearchSignature->Data, SearchSignature->DataLength) == 0) {
|
|
break;
|
|
}
|
|
}
|
|
if (Data64 > SearchSignature->Start + SearchSignature->Count - SearchSignature->DataLength) {
|
|
Data64 = (UINT64) -1;
|
|
}
|
|
} else {
|
|
for (
|
|
Data64 = ALIGN_VALUE ((UINTN) SearchSignature->Start - SearchSignature->Alignment, SearchSignature->Alignment);
|
|
Data64 >= SearchSignature->Start - SearchSignature->Count;
|
|
Data64 -= SearchSignature->Alignment
|
|
) {
|
|
if (CompareMem ((VOID *) (UINTN) Data64, &SearchSignature->Data, SearchSignature->DataLength) == 0) {
|
|
break;
|
|
}
|
|
}
|
|
if (Data64 < SearchSignature->Start - SearchSignature->Count) {
|
|
Data64 = (UINT64) -1;
|
|
}
|
|
}
|
|
SendDataResponsePacket ((UINT8 *) &Data64, (UINT16) sizeof (Data64), DebugHeader);
|
|
} else {
|
|
Status = RETURN_UNSUPPORTED;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
SendAckPacket (DEBUG_COMMAND_NOT_SUPPORTED);
|
|
break;
|
|
}
|
|
|
|
if (Status == RETURN_UNSUPPORTED) {
|
|
SendAckPacket (DEBUG_COMMAND_NOT_SUPPORTED);
|
|
} else if (Status != RETURN_SUCCESS) {
|
|
SendAckPacket (DEBUG_COMMAND_ABORT);
|
|
}
|
|
|
|
ReleaseMpSpinLock (&mDebugMpContext.DebugPortSpinLock);
|
|
CpuPause ();
|
|
}
|
|
}
|
|
|
|
/**
|
|
C function called in interrupt handler.
|
|
|
|
@param[in] Vector Vector value of exception or interrutp.
|
|
@param[in] CpuContext Pointer to save CPU context.
|
|
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
InterruptProcess (
|
|
IN UINT32 Vector,
|
|
IN DEBUG_CPU_CONTEXT *CpuContext
|
|
)
|
|
{
|
|
UINT8 InputCharacter;
|
|
UINT8 BreakCause;
|
|
UINTN SavedEip;
|
|
BOOLEAN BreakReceived;
|
|
UINT32 ProcessorIndex;
|
|
UINT32 CurrentDebugTimerInitCount;
|
|
DEBUG_PORT_HANDLE Handle;
|
|
UINT8 Data8;
|
|
UINT8 *Al;
|
|
UINT32 IssuedViewPoint;
|
|
DEBUG_AGENT_EXCEPTION_BUFFER *ExceptionBuffer;
|
|
|
|
InputCharacter = 0;
|
|
ProcessorIndex = 0;
|
|
IssuedViewPoint = 0;
|
|
BreakReceived = FALSE;
|
|
|
|
if (mSkipBreakpoint) {
|
|
//
|
|
// If Skip Breakpoint flag is set, means communication is disturbed by hardware SMI, we need to ignore the break points in SMM
|
|
//
|
|
if ((Vector == DEBUG_INT1_VECTOR) || (Vector == DEBUG_INT3_VECTOR)) {
|
|
DebugPortWriteBuffer (GetDebugPortHandle(), (UINT8 *) mWarningMsgIngoreBreakpoint, AsciiStrLen (mWarningMsgIngoreBreakpoint));
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (MultiProcessorDebugSupport()) {
|
|
ProcessorIndex = GetProcessorIndex ();
|
|
//
|
|
// If this processor has alreay halted before, need to check it later
|
|
//
|
|
if (IsCpuStopped (ProcessorIndex)) {
|
|
IssuedViewPoint = ProcessorIndex;
|
|
}
|
|
}
|
|
|
|
if (IssuedViewPoint == ProcessorIndex && GetDebugFlag (DEBUG_AGENT_FLAG_STEPPING) != 1) {
|
|
//
|
|
// Check if this exception is issued by Debug Agent itself
|
|
// If yes, fill the debug agent exception buffer and LongJump() back to
|
|
// the saved CPU content in CommandCommunication()
|
|
// If exception is issued when executing Stepping, will be handled in
|
|
// exception handle procedure.
|
|
//
|
|
if (GetDebugFlag (DEBUG_AGENT_FLAG_AGENT_IN_PROGRESS) == 1) {
|
|
DebugAgentMsgPrint (
|
|
DEBUG_AGENT_ERROR,
|
|
"Debug agent meet one Exception, ExceptionNum is %d, EIP = 0x%x.\n",
|
|
Vector,
|
|
(UINTN)CpuContext->Eip
|
|
);
|
|
ExceptionBuffer = (DEBUG_AGENT_EXCEPTION_BUFFER *) (UINTN) GetMailboxPointer()->ExceptionBufferPointer;
|
|
ExceptionBuffer->ExceptionContent.ExceptionNum = (UINT8) Vector;
|
|
ExceptionBuffer->ExceptionContent.ExceptionData = (UINT32) CpuContext->ExceptionData;
|
|
LongJump ((BASE_LIBRARY_JUMP_BUFFER *)(UINTN)(ExceptionBuffer), 1);
|
|
}
|
|
}
|
|
|
|
if (MultiProcessorDebugSupport()) {
|
|
//
|
|
// If RUN commmand is executing, wait for it done.
|
|
//
|
|
while (mDebugMpContext.RunCommandSet) {
|
|
CpuPause ();
|
|
}
|
|
}
|
|
|
|
Handle = GetDebugPortHandle();
|
|
BreakCause = GetBreakCause (Vector, CpuContext);
|
|
switch (Vector) {
|
|
case DEBUG_INT1_VECTOR:
|
|
case DEBUG_INT3_VECTOR:
|
|
switch (BreakCause) {
|
|
case DEBUG_DATA_BREAK_CAUSE_SYSTEM_RESET:
|
|
if (AttachHost (BreakCause, READ_PACKET_TIMEOUT, &BreakReceived) != RETURN_SUCCESS) {
|
|
//
|
|
// Try to connect HOST, return if fails
|
|
//
|
|
break;
|
|
}
|
|
CommandCommunication (Vector, CpuContext, BreakReceived);
|
|
break;
|
|
|
|
case DEBUG_DATA_BREAK_CAUSE_STEPPING:
|
|
//
|
|
// Stepping is finished, send Ack package.
|
|
//
|
|
if (MultiProcessorDebugSupport()) {
|
|
mDebugMpContext.BreakAtCpuIndex = ProcessorIndex;
|
|
}
|
|
//
|
|
// Clear Stepping Flag and restore EFLAGS.IF
|
|
//
|
|
CommandSteppingCleanup (CpuContext);
|
|
SendAckPacket (DEBUG_COMMAND_OK);
|
|
CommandCommunication (Vector, CpuContext, BreakReceived);
|
|
break;
|
|
|
|
case DEBUG_DATA_BREAK_CAUSE_MEMORY_READY:
|
|
//
|
|
// Memory is ready
|
|
//
|
|
SendCommandAndWaitForAckOK (DEBUG_COMMAND_MEMORY_READY, READ_PACKET_TIMEOUT, &BreakReceived, NULL);
|
|
CommandCommunication (Vector, CpuContext, BreakReceived);
|
|
break;
|
|
|
|
case DEBUG_DATA_BREAK_CAUSE_IMAGE_LOAD:
|
|
case DEBUG_DATA_BREAK_CAUSE_IMAGE_UNLOAD:
|
|
//
|
|
// Set AL to DEBUG_AGENT_IMAGE_CONTINUE
|
|
//
|
|
Al = ArchReadRegisterBuffer (CpuContext, SOFT_DEBUGGER_REGISTER_AX, &Data8);
|
|
*Al = DEBUG_AGENT_IMAGE_CONTINUE;
|
|
|
|
if (!IsHostAttached ()) {
|
|
//
|
|
// If HOST is not connected for image load/unload, return
|
|
//
|
|
break;
|
|
}
|
|
//
|
|
// Continue to run the following common code
|
|
//
|
|
|
|
case DEBUG_DATA_BREAK_CAUSE_HW_BREAKPOINT:
|
|
case DEBUG_DATA_BREAK_CAUSE_SW_BREAKPOINT:
|
|
default:
|
|
//
|
|
// Send Break packet to HOST
|
|
//
|
|
AcquireMpSpinLock (&mDebugMpContext.DebugPortSpinLock);
|
|
//
|
|
// Only the first breaking processor could send BREAK_POINT to HOST
|
|
//
|
|
if (IsFirstBreakProcessor (ProcessorIndex)) {
|
|
SendBreakPacketToHost (BreakCause, ProcessorIndex, &BreakReceived);
|
|
}
|
|
ReleaseMpSpinLock (&mDebugMpContext.DebugPortSpinLock);
|
|
|
|
if (Vector == DEBUG_INT3_VECTOR) {
|
|
//
|
|
// go back address located "0xCC"
|
|
//
|
|
CpuContext->Eip--;
|
|
SavedEip = CpuContext->Eip;
|
|
CommandCommunication (Vector, CpuContext, BreakReceived);
|
|
if ((SavedEip == CpuContext->Eip) &&
|
|
(*(UINT8 *) (UINTN) CpuContext->Eip == DEBUG_SW_BREAKPOINT_SYMBOL)) {
|
|
//
|
|
// If this is not a software breakpoint set by HOST,
|
|
// restore EIP
|
|
//
|
|
CpuContext->Eip++;
|
|
}
|
|
} else {
|
|
CommandCommunication (Vector, CpuContext, BreakReceived);
|
|
}
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case DEBUG_TIMER_VECTOR:
|
|
|
|
AcquireMpSpinLock (&mDebugMpContext.DebugPortSpinLock);
|
|
|
|
if (MultiProcessorDebugSupport()) {
|
|
if (IsBsp (ProcessorIndex)) {
|
|
//
|
|
// If current processor is BSP, check Apic timer's init count if changed,
|
|
// it may be re-written when switching BSP.
|
|
// If it changed, re-initialize debug timer
|
|
//
|
|
CurrentDebugTimerInitCount = GetApicTimerInitCount ();
|
|
if (mDebugMpContext.DebugTimerInitCount != CurrentDebugTimerInitCount) {
|
|
InitializeDebugTimer (NULL, FALSE);
|
|
SaveAndSetDebugTimerInterrupt (TRUE);
|
|
}
|
|
}
|
|
|
|
if (!IsBsp (ProcessorIndex) || mDebugMpContext.IpiSentByAp) {
|
|
ReleaseMpSpinLock (&mDebugMpContext.DebugPortSpinLock);
|
|
//
|
|
// If current processor is not BSP or this is one IPI sent by AP
|
|
//
|
|
if (mDebugMpContext.BreakAtCpuIndex != (UINT32) (-1)) {
|
|
CommandCommunication (Vector, CpuContext, FALSE);
|
|
}
|
|
|
|
//
|
|
// Clear EOI before exiting interrupt process routine.
|
|
//
|
|
SendApicEoi ();
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Only BSP could run here
|
|
//
|
|
while (TRUE) {
|
|
//
|
|
// If there is data in debug port, will check whether it is break(attach/break-in) symbol,
|
|
// If yes, go into communication mode with HOST.
|
|
// If no, exit interrupt process.
|
|
//
|
|
if (DebugReadBreakSymbol (Handle, &InputCharacter) == EFI_NOT_FOUND) {
|
|
break;
|
|
}
|
|
|
|
if ((!IsHostAttached () && (InputCharacter == DEBUG_STARTING_SYMBOL_ATTACH)) ||
|
|
(IsHostAttached () && (InputCharacter == DEBUG_COMMAND_HALT)) ||
|
|
(IsHostAttached () && (InputCharacter == DEBUG_COMMAND_GO))
|
|
) {
|
|
DebugAgentMsgPrint (DEBUG_AGENT_VERBOSE, "Received data [%02x]\n", InputCharacter);
|
|
//
|
|
// Ack OK for break-in symbol
|
|
//
|
|
SendAckPacket (DEBUG_COMMAND_OK);
|
|
|
|
//
|
|
// If receive GO command in Debug Timer, means HOST may lost ACK packet before.
|
|
//
|
|
if (InputCharacter == DEBUG_COMMAND_GO) {
|
|
break;
|
|
}
|
|
|
|
if (!IsHostAttached ()) {
|
|
//
|
|
// Try to attach HOST, if no ack received after 200ms, return
|
|
//
|
|
if (AttachHost (BreakCause, READ_PACKET_TIMEOUT, &BreakReceived) != RETURN_SUCCESS) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (MultiProcessorDebugSupport()) {
|
|
if(FindNextPendingBreakCpu () != -1) {
|
|
SetCpuBreakFlagByIndex (ProcessorIndex, TRUE);
|
|
} else {
|
|
HaltOtherProcessors (ProcessorIndex);
|
|
}
|
|
}
|
|
ReleaseMpSpinLock (&mDebugMpContext.DebugPortSpinLock);
|
|
CommandCommunication (Vector, CpuContext, BreakReceived);
|
|
AcquireMpSpinLock (&mDebugMpContext.DebugPortSpinLock);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Clear EOI before exiting interrupt process routine.
|
|
//
|
|
SendApicEoi ();
|
|
|
|
ReleaseMpSpinLock (&mDebugMpContext.DebugPortSpinLock);
|
|
|
|
break;
|
|
|
|
default:
|
|
if (Vector <= DEBUG_EXCEPT_SIMD) {
|
|
DebugAgentMsgPrint (
|
|
DEBUG_AGENT_ERROR,
|
|
"Exception happened, ExceptionNum is %d, EIP = 0x%x.\n",
|
|
Vector,
|
|
(UINTN) CpuContext->Eip
|
|
);
|
|
if (BreakCause == DEBUG_DATA_BREAK_CAUSE_STEPPING) {
|
|
//
|
|
// If exception happened when executing Stepping, send Ack package.
|
|
// HOST consider Stepping command was finished.
|
|
//
|
|
if (MultiProcessorDebugSupport()) {
|
|
mDebugMpContext.BreakAtCpuIndex = ProcessorIndex;
|
|
}
|
|
//
|
|
// Clear Stepping flag and restore EFLAGS.IF
|
|
//
|
|
CommandSteppingCleanup (CpuContext);
|
|
SendAckPacket (DEBUG_COMMAND_OK);
|
|
} else {
|
|
//
|
|
// Exception occurs, send Break packet to HOST
|
|
//
|
|
AcquireMpSpinLock (&mDebugMpContext.DebugPortSpinLock);
|
|
//
|
|
// Only the first breaking processor could send BREAK_POINT to HOST
|
|
//
|
|
if (IsFirstBreakProcessor (ProcessorIndex)) {
|
|
SendBreakPacketToHost (BreakCause, ProcessorIndex, &BreakReceived);
|
|
}
|
|
ReleaseMpSpinLock (&mDebugMpContext.DebugPortSpinLock);
|
|
}
|
|
|
|
CommandCommunication (Vector, CpuContext, BreakReceived);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (MultiProcessorDebugSupport()) {
|
|
//
|
|
// Clear flag and wait for all processors run here
|
|
//
|
|
SetIpiSentByApFlag (FALSE);
|
|
while (mDebugMpContext.RunCommandSet) {
|
|
CpuPause ();
|
|
}
|
|
|
|
//
|
|
// Only current (view) processor could clean up AgentInProgress flag.
|
|
//
|
|
if (mDebugMpContext.ViewPointIndex == ProcessorIndex) {
|
|
IssuedViewPoint = mDebugMpContext.ViewPointIndex;
|
|
}
|
|
}
|
|
|
|
if (IssuedViewPoint == ProcessorIndex && GetDebugFlag (DEBUG_AGENT_FLAG_STEPPING) != 1) {
|
|
//
|
|
// If the command is not stepping, clean up AgentInProgress flag
|
|
//
|
|
SetDebugFlag (DEBUG_AGENT_FLAG_AGENT_IN_PROGRESS, 0);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|