| /** @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; | |
| } |