/** @file | |
Common Debug Agent library implementation. 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 - 2018, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#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; | |
PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; | |
// | |
// Find Image Base | |
// | |
Pe32Data = PeCoffSearchImageBase ((UINTN)mErrorMsgVersionAlert); | |
if (Pe32Data != 0) { | |
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 ((DEBUG_ERROR, "DebugAgent: Mailbox checksum error, stack or heap crashed!\n")); | |
DEBUG ((DEBUG_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 formatted in buffer | |
// | |
DestBuffer += AsciiSPrint (DestBuffer, DEBUG_DATA_MAXIMUM_REAL_DATA - (DestBuffer - Buffer), "]\n"); | |
SendDebugMsgPacket (Buffer, DestBuffer - Buffer); | |
break; | |
} | |
} | |
} | |
/** | |
Read remaining 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 response 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 update 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; | |
} | |
/** | |
Execute 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 acknowledge 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 received 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 acknowledge from HOST. | |
The unit is microsecond. | |
@param[out] BreakReceived If BreakReceived is not NULL, | |
TRUE is returned if break-in symbol received. | |
FALSE is returned if break-in symbol not received. | |
@param[out] IncompatibilityFlag If IncompatibilityFlag is not NULL, return | |
TRUE: Compatible packet received. | |
FALSE: Incompatible packet received. | |
@retval RETURN_SUCCESS Succeed to receive acknowledge packet from HOST, | |
the type of acknowledge 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 specified 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 (PcdGet32 (PcdTransferProtocolRevision) >= 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 acknowledge received in specified Timeout, return RETURN_TIMEOUT. | |
If received acknowledge, 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 acknowledge from HOST. | |
The unit is microsecond. | |
@param[out] BreakReceived If BreakReceived is not NULL, | |
TRUE is returned if break-in symbol received. | |
FALSE is returned 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 PcdTransferProtocolRevision. | |
// 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 returned if break-in symbol received. | |
FALSE is returned 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 interrupt. | |
@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] against 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 command[%x] against command[%x]\n", DebugHeader->SequenceNo, Mailbox->HostSequenceNo); | |
ReleaseMpSpinLock (&mDebugMpContext.DebugPortSpinLock); | |
continue; | |
} | |
// | |
// Save CPU content before executing HOST command | |
// | |
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 received 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 = PcdGet32 (PcdTransferProtocolRevision); | |
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 interrupt. | |
@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 already 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 command 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 (DebugAgentIsBsp (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 (!DebugAgentIsBsp (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; | |
} |