diff options
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/DebugAgent.c')
-rw-r--r-- | src/VBox/Devices/EFI/Firmware/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/DebugAgent.c | 2640 |
1 files changed, 2640 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/DebugAgent.c b/src/VBox/Devices/EFI/Firmware/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/DebugAgent.c new file mode 100644 index 00000000..1ee01e02 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/DebugAgent.c @@ -0,0 +1,2640 @@ +/** @file + Commond 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 ((EFI_D_ERROR, "DebugAgent: Mailbox checksum error, stack or heap crashed!\n")); + DEBUG ((EFI_D_ERROR, "DebugAgent: CheckSum = %x, Mailbox->CheckSum = %x, Mailbox->ToBeCheckSum = %x\n", CheckSum, Mailbox->CheckSum, Mailbox->ToBeCheckSum)); + CpuDeadLoop (); + } +} + +/** + Update Mailbox content by index. + + @param[in] Mailbox Debug Agent Mailbox pointer. + @param[in] Index Mailbox content index. + @param[in] Value Value to be set into Mailbox. + +**/ +VOID +UpdateMailboxContent ( + IN DEBUG_AGENT_MAILBOX *Mailbox, + IN UINTN Index, + IN UINT64 Value + ) +{ + AcquireMpSpinLock (&mDebugMpContext.MailboxSpinLock); + switch (Index) { + case DEBUG_MAILBOX_DEBUG_FLAG_INDEX: + Mailbox->ToBeCheckSum = Mailbox->CheckSum + CalculateSum8 ((UINT8 *)&Mailbox->DebugFlag.Uint64, sizeof(UINT64)) + - CalculateSum8 ((UINT8 *)&Value, sizeof(UINT64)); + Mailbox->DebugFlag.Uint64 = Value; + break; + case DEBUG_MAILBOX_DEBUG_PORT_HANDLE_INDEX: + Mailbox->ToBeCheckSum = Mailbox->CheckSum + CalculateSum8 ((UINT8 *)&Mailbox->DebugPortHandle, sizeof(UINTN)) + - CalculateSum8 ((UINT8 *)&Value, sizeof(UINTN)); + Mailbox->DebugPortHandle = (UINTN) Value; + break; + case DEBUG_MAILBOX_EXCEPTION_BUFFER_POINTER_INDEX: + Mailbox->ToBeCheckSum = Mailbox->CheckSum + CalculateSum8 ((UINT8 *)&Mailbox->ExceptionBufferPointer, sizeof(UINTN)) + - CalculateSum8 ((UINT8 *)&Value, sizeof(UINTN)); + Mailbox->ExceptionBufferPointer = (UINTN) Value; + break; + case DEBUG_MAILBOX_LAST_ACK: + Mailbox->ToBeCheckSum = Mailbox->CheckSum + CalculateSum8 ((UINT8 *)&Mailbox->LastAck, sizeof(UINT8)) + - CalculateSum8 ((UINT8 *)&Value, sizeof(UINT8)); + Mailbox->LastAck = (UINT8) Value; + break; + case DEBUG_MAILBOX_SEQUENCE_NO_INDEX: + Mailbox->ToBeCheckSum = Mailbox->CheckSum + CalculateSum8 ((UINT8 *)&Mailbox->SequenceNo, sizeof(UINT8)) + - CalculateSum8 ((UINT8 *)&Value, sizeof(UINT8)); + Mailbox->SequenceNo = (UINT8) Value; + break; + case DEBUG_MAILBOX_HOST_SEQUENCE_NO_INDEX: + Mailbox->ToBeCheckSum = Mailbox->CheckSum + CalculateSum8 ((UINT8 *)&Mailbox->HostSequenceNo, sizeof(UINT8)) + - CalculateSum8 ((UINT8 *)&Value, sizeof(UINT8)); + Mailbox->HostSequenceNo = (UINT8) Value; + break; + case DEBUG_MAILBOX_DEBUG_TIMER_FREQUENCY: + Mailbox->ToBeCheckSum = Mailbox->CheckSum + CalculateSum8 ((UINT8 *)&Mailbox->DebugTimerFrequency, sizeof(UINT32)) + - CalculateSum8 ((UINT8 *)&Value, sizeof(UINT32)); + Mailbox->DebugTimerFrequency = (UINT32) Value; + break; + } + UpdateMailboxChecksum (Mailbox); + ReleaseMpSpinLock (&mDebugMpContext.MailboxSpinLock); +} + +/** + Read data from debug device and save the data in buffer. + + Reads NumberOfBytes data bytes from a debug device into the buffer + specified by Buffer. The number of bytes actually read is returned. + If the return value is less than NumberOfBytes, then the rest operation failed. + If NumberOfBytes is zero, then return 0. + + @param Handle Debug port handle. + @param Buffer Pointer to the data buffer to store the data read from the debug device. + @param NumberOfBytes Number of bytes which will be read. + @param Timeout Timeout value for reading from debug device. It unit is Microsecond. + + @retval 0 Read data failed, no data is to be read. + @retval >0 Actual number of bytes read from debug device. + +**/ +UINTN +DebugAgentReadBuffer ( + IN DEBUG_PORT_HANDLE Handle, + IN UINT8 *Buffer, + IN UINTN NumberOfBytes, + IN UINTN Timeout + ) +{ + UINTN Index; + UINT32 Begin; + UINT32 TimeoutTicker; + UINT32 TimerRound; + UINT32 TimerFrequency; + UINT32 TimerCycle; + + Begin = 0; + TimeoutTicker = 0; + TimerRound = 0; + TimerFrequency = GetMailboxPointer()->DebugTimerFrequency; + TimerCycle = GetApicTimerInitCount (); + + if (Timeout != 0) { + Begin = GetApicTimerCurrentCount (); + TimeoutTicker = (UINT32) DivU64x32 ( + MultU64x64 ( + TimerFrequency, + Timeout + ), + 1000000u + ); + TimerRound = (UINT32) DivU64x32Remainder (TimeoutTicker, TimerCycle / 2, &TimeoutTicker); + } + Index = 0; + while (Index < NumberOfBytes) { + if (DebugPortPollBuffer (Handle)) { + DebugPortReadBuffer (Handle, Buffer + Index, 1, 0); + Index ++; + continue; + } + if (Timeout != 0) { + if (TimerRound == 0) { + if (IsDebugTimerTimeout (TimerCycle, Begin, TimeoutTicker)) { + // + // If time out occurs. + // + return 0; + } + } else { + if (IsDebugTimerTimeout (TimerCycle, Begin, TimerCycle / 2)) { + TimerRound --; + Begin = GetApicTimerCurrentCount (); + } + } + } + } + + return Index; +} + +/** + Set debug flag in mailbox. + + @param[in] FlagMask Debug flag mask value. + @param[in] FlagValue Debug flag value. + +**/ +VOID +SetDebugFlag ( + IN UINT64 FlagMask, + IN UINT32 FlagValue + ) +{ + DEBUG_AGENT_MAILBOX *Mailbox; + UINT64 Data64; + + Mailbox = GetMailboxPointer (); + Data64 = (Mailbox->DebugFlag.Uint64 & ~FlagMask) | + (LShiftU64 ((UINT64)FlagValue, LowBitSet64 (FlagMask)) & FlagMask); + UpdateMailboxContent (Mailbox, DEBUG_MAILBOX_DEBUG_FLAG_INDEX, Data64); +} + +/** + Get debug flag in mailbox. + + @param[in] FlagMask Debug flag mask value. + + @return Debug flag value. + +**/ +UINT32 +GetDebugFlag ( + IN UINT64 FlagMask + ) +{ + DEBUG_AGENT_MAILBOX *Mailbox; + UINT32 DebugFlag; + + Mailbox = GetMailboxPointer (); + DebugFlag = (UINT32) RShiftU64 (Mailbox->DebugFlag.Uint64 & FlagMask, LowBitSet64 (FlagMask)); + + return DebugFlag; +} + +/** + Send a debug message packet to the debug port. + + @param[in] Buffer The debug message. + @param[in] Length The length of debug message. + +**/ +VOID +SendDebugMsgPacket ( + IN CHAR8 *Buffer, + IN UINTN Length + ) +{ + DEBUG_PACKET_HEADER DebugHeader; + DEBUG_PORT_HANDLE Handle; + + Handle = GetDebugPortHandle(); + + DebugHeader.StartSymbol = DEBUG_STARTING_SYMBOL_NORMAL; + DebugHeader.Command = DEBUG_COMMAND_PRINT_MESSAGE; + DebugHeader.Length = sizeof (DEBUG_PACKET_HEADER) + (UINT8) Length; + DebugHeader.SequenceNo = 0xEE; + DebugHeader.Crc = 0; + DebugHeader.Crc = CalculateCrc16 ( + (UINT8 *)Buffer, Length, + CalculateCrc16 ((UINT8 *)&DebugHeader, sizeof (DEBUG_PACKET_HEADER), 0) + ); + + DebugPortWriteBuffer (Handle, (UINT8 *)&DebugHeader, sizeof (DEBUG_PACKET_HEADER)); + DebugPortWriteBuffer (Handle, (UINT8 *)Buffer, Length); +} + +/** + Prints a debug message to the debug port if the specified error level is enabled. + + If any bit in ErrorLevel is also set in Mainbox, then print the message specified + by Format and the associated variable argument list to the debug port. + + @param[in] ErrorLevel The error level of the debug message. + @param[in] Format Format string for the debug message to print. + @param[in] ... Variable argument list whose contents are accessed + based on the format string specified by Format. + +**/ +VOID +EFIAPI +DebugAgentMsgPrint ( + IN UINT8 ErrorLevel, + IN CHAR8 *Format, + ... + ) +{ + CHAR8 Buffer[DEBUG_DATA_MAXIMUM_REAL_DATA]; + VA_LIST Marker; + + // + // Check driver debug mask value and global mask + // + if ((ErrorLevel & GetDebugFlag (DEBUG_AGENT_FLAG_PRINT_ERROR_LEVEL)) == 0) { + return; + } + + // + // Convert the DEBUG() message to an ASCII String + // + VA_START (Marker, Format); + AsciiVSPrint (Buffer, sizeof (Buffer), Format, Marker); + VA_END (Marker); + + SendDebugMsgPacket (Buffer, AsciiStrLen (Buffer)); +} + +/** + Prints a debug message to the debug output device if the specified error level is enabled. + + If any bit in ErrorLevel is also set in DebugPrintErrorLevelLib function + GetDebugPrintErrorLevel (), then print the message specified by Format and the + associated variable argument list to the debug output device. + + If Format is NULL, then ASSERT(). + + @param[in] ErrorLevel The error level of the debug message. + @param[in] IsSend Flag of debug message to declare that the data is being sent or being received. + @param[in] Data Variable argument list whose contents are accessed + @param[in] Length based on the format string specified by Format. + +**/ +VOID +EFIAPI +DebugAgentDataMsgPrint ( + IN UINT8 ErrorLevel, + IN BOOLEAN IsSend, + IN UINT8 *Data, + IN UINT8 Length + ) +{ + CHAR8 Buffer[DEBUG_DATA_MAXIMUM_REAL_DATA]; + CHAR8 *DestBuffer; + UINTN Index; + + // + // Check driver debug mask value and global mask + // + if ((ErrorLevel & GetDebugFlag (DEBUG_AGENT_FLAG_PRINT_ERROR_LEVEL)) == 0) { + return; + } + + DestBuffer = Buffer; + if (IsSend) { + DestBuffer += AsciiSPrint (DestBuffer, DEBUG_DATA_MAXIMUM_REAL_DATA, "Sent data [ "); + } else { + DestBuffer += AsciiSPrint (DestBuffer, DEBUG_DATA_MAXIMUM_REAL_DATA, "Received data [ "); + } + + Index = 0; + while (TRUE) { + if (DestBuffer - Buffer > DEBUG_DATA_MAXIMUM_REAL_DATA - 6) { + // + // If there was no enough space in buffer, send out the debug message, + // reserving 6 bytes is for the last data and end characters "]\n". + // + SendDebugMsgPacket (Buffer, DestBuffer - Buffer); + DestBuffer = Buffer; + } + DestBuffer += AsciiSPrint (DestBuffer, DEBUG_DATA_MAXIMUM_REAL_DATA - (DestBuffer - Buffer), "%02x ", Data[Index]); + Index ++; + if (Index >= Length) { + // + // The last character of debug message has been formatted in buffer + // + DestBuffer += AsciiSPrint(DestBuffer, DEBUG_DATA_MAXIMUM_REAL_DATA - (DestBuffer - Buffer), "]\n"); + SendDebugMsgPacket (Buffer, DestBuffer - Buffer); + break; + } + } +} + +/** + Read remaing debug packet except for the start symbol + + @param[in] Handle Pointer to Debug Port handle. + @param[in, out] DebugHeader Debug header buffer including start symbol. + + @retval EFI_SUCCESS Read the symbol in BreakSymbol. + @retval EFI_CRC_ERROR CRC check fail. + @retval EFI_TIMEOUT Timeout occurs when reading debug packet. + @retval EFI_DEVICE_ERROR Receive the old or responsed packet. + +**/ +EFI_STATUS +ReadRemainingBreakPacket ( + IN DEBUG_PORT_HANDLE Handle, + IN OUT DEBUG_PACKET_HEADER *DebugHeader + ) +{ + UINT16 Crc; + DEBUG_AGENT_MAILBOX *Mailbox; + + // + // Has received start symbol, try to read the rest part + // + if (DebugAgentReadBuffer (Handle, (UINT8 *)DebugHeader + OFFSET_OF (DEBUG_PACKET_HEADER, Command), sizeof (DEBUG_PACKET_HEADER) - OFFSET_OF (DEBUG_PACKET_HEADER, Command), READ_PACKET_TIMEOUT) == 0) { + // + // Timeout occur, exit + // + DebugAgentMsgPrint (DEBUG_AGENT_WARNING, "Timeout in Debug Timer interrupt\n"); + return EFI_TIMEOUT; + } + + Crc = DebugHeader->Crc; + DebugHeader->Crc = 0; + if (CalculateCrc16 ((UINT8 *)DebugHeader, DebugHeader->Length, 0) != Crc) { + DebugAgentMsgPrint (DEBUG_AGENT_WARNING, "Debug Timer CRC (%x) against (%x)\n", Crc, CalculateCrc16 ((UINT8 *) &DebugHeader, DebugHeader->Length, 0)); + DebugAgentDataMsgPrint (DEBUG_AGENT_VERBOSE, FALSE, (UINT8 *)DebugHeader, DebugHeader->Length); + return EFI_CRC_ERROR; + } + Mailbox = GetMailboxPointer(); + if (IS_REQUEST (DebugHeader)) { + if (DebugHeader->SequenceNo == (UINT8) (Mailbox->HostSequenceNo + 1)) { + // + // Only updagte HostSequenceNo for new command packet + // + UpdateMailboxContent (Mailbox, DEBUG_MAILBOX_HOST_SEQUENCE_NO_INDEX, DebugHeader->SequenceNo); + return EFI_SUCCESS; + } + if (DebugHeader->SequenceNo == Mailbox->HostSequenceNo) { + return EFI_SUCCESS; + } + } + + return EFI_DEVICE_ERROR; +} + +/** + Check if HOST is attached based on Mailbox. + + @retval TRUE HOST is attached. + @retval FALSE HOST is not attached. + +**/ +BOOLEAN +IsHostAttached ( + VOID + ) +{ + return (BOOLEAN) (GetDebugFlag (DEBUG_AGENT_FLAG_HOST_ATTACHED) == 1); +} + +/** + Set HOST connect flag in Mailbox. + + @param[in] Attached Attach status. + +**/ +VOID +SetHostAttached ( + IN BOOLEAN Attached + ) +{ + DebugAgentMsgPrint (DEBUG_AGENT_INFO, "Attach status is %d\n", Attached); + SetDebugFlag (DEBUG_AGENT_FLAG_HOST_ATTACHED, (UINT32)Attached); +} + +/** + Set debug setting of Debug Agent in Mailbox. + + @param DebugSetting Pointer to Debug Setting defined by transfer protocol. + + @retval RETURN_SUCCESS The setting is set successfully. + @retval RETURN_UNSUPPORTED The Key value is not supported. + +**/ +RETURN_STATUS +SetDebugSetting ( + IN DEBUG_DATA_SET_DEBUG_SETTING *DebugSetting + ) +{ + RETURN_STATUS Status; + + Status = RETURN_SUCCESS; + switch (DebugSetting->Key) { + case DEBUG_AGENT_SETTING_SMM_ENTRY_BREAK: + SetDebugFlag (DEBUG_AGENT_FLAG_BREAK_ON_NEXT_SMI, DebugSetting->Value); + break; + case DEBUG_AGENT_SETTING_PRINT_ERROR_LEVEL: + SetDebugFlag (DEBUG_AGENT_FLAG_PRINT_ERROR_LEVEL, DebugSetting->Value); + break; + case DEBUG_AGENT_SETTING_BOOT_SCRIPT_ENTRY_BREAK: + SetDebugFlag (DEBUG_AGENT_FLAG_BREAK_BOOT_SCRIPT, DebugSetting->Value); + break; + default: + Status = RETURN_UNSUPPORTED; + } + return Status; +} + +/** + 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; +} + |