From c6ff7c547bfc71da9d5e71a73984b9d3a4ea0809 Mon Sep 17 00:00:00 2001 From: Guo Mang Date: Thu, 27 Apr 2017 11:21:47 +0800 Subject: SourceLevelDebugPkg: Move to new location Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Guo Mang --- .../DebugAgent/DebugAgentCommon/DebugAgent.c | 2646 ++++++++++++++++++++ .../DebugAgent/DebugAgentCommon/DebugAgent.h | 504 ++++ .../Library/DebugAgent/DebugAgentCommon/DebugMp.c | 383 +++ .../Library/DebugAgent/DebugAgentCommon/DebugMp.h | 222 ++ .../DebugAgent/DebugAgentCommon/DebugTimer.c | 149 ++ .../DebugAgent/DebugAgentCommon/DebugTimer.h | 51 + .../DebugAgentCommon/Ia32/ArchDebugSupport.c | 116 + .../DebugAgentCommon/Ia32/ArchDebugSupport.h | 27 + .../DebugAgent/DebugAgentCommon/Ia32/AsmFuncs.S | 415 +++ .../DebugAgent/DebugAgentCommon/Ia32/AsmFuncs.asm | 422 ++++ .../DebugAgent/DebugAgentCommon/Ia32/AsmFuncs.nasm | 419 ++++ .../DebugAgentCommon/Ia32/DebugException.h | 36 + .../DebugAgentCommon/X64/ArchDebugSupport.c | 120 + .../DebugAgentCommon/X64/ArchDebugSupport.h | 27 + .../DebugAgent/DebugAgentCommon/X64/AsmFuncs.S | 431 ++++ .../DebugAgent/DebugAgentCommon/X64/AsmFuncs.asm | 405 +++ .../DebugAgent/DebugAgentCommon/X64/AsmFuncs.nasm | 405 +++ .../DebugAgentCommon/X64/DebugException.h | 36 + .../DebugAgent/DxeDebugAgent/DxeDebugAgentLib.c | 550 ++++ .../DebugAgent/DxeDebugAgent/DxeDebugAgentLib.h | 40 + .../Library/DebugAgent/DxeDebugAgent/SerialIo.c | 814 ++++++ .../Library/DebugAgent/DxeDebugAgentLib.inf | 104 + .../Library/DebugAgent/DxeDebugAgentLib.uni | 21 + .../SecPeiDebugAgent/SecPeiDebugAgentLib.c | 710 ++++++ .../SecPeiDebugAgent/SecPeiDebugAgentLib.h | 65 + .../Library/DebugAgent/SecPeiDebugAgentLib.inf | 94 + .../Library/DebugAgent/SecPeiDebugAgentLib.uni | 21 + .../DebugAgent/SmmDebugAgent/SmmDebugAgentLib.c | 381 +++ .../DebugAgent/SmmDebugAgent/SmmDebugAgentLib.h | 25 + .../Library/DebugAgent/SmmDebugAgentLib.inf | 89 + .../Library/DebugAgent/SmmDebugAgentLib.uni | 21 + .../DebugCommunicationLibSerialPort.c | 160 ++ .../DebugCommunicationLibSerialPort.inf | 38 + .../DebugCommunicationLibSerialPort.uni | 21 + .../DebugCommunicationLibUsb.c | 1098 ++++++++ .../DebugCommunicationLibUsb.inf | 56 + .../DebugCommunicationLibUsb.uni | 21 + .../DebugCommunicationLibUsb3Common.c | 1066 ++++++++ .../DebugCommunicationLibUsb3Dxe.c | 53 + .../DebugCommunicationLibUsb3Dxe.inf | 69 + .../DebugCommunicationLibUsb3Dxe.uni | 21 + .../DebugCommunicationLibUsb3Internal.h | 731 ++++++ .../DebugCommunicationLibUsb3Pei.c | 45 + .../DebugCommunicationLibUsb3Pei.inf | 70 + .../DebugCommunicationLibUsb3Pei.uni | 21 + .../DebugCommunicationLibUsb3Transfer.c | 578 +++++ .../PeCoffExtraActionLibDebug/Ia32/IntHandler.S | 28 + .../PeCoffExtraActionLibDebug/Ia32/IntHandler.asm | 33 + .../PeCoffExtraActionLibDebug/Ia32/IntHandler.nasm | 28 + .../Ia32/IntHandlerFuncs.c | 99 + .../PeCoffExtraActionLib.c | 230 ++ .../PeCoffExtraActionLib.h | 79 + .../PeCoffExtraActionLib.uni | 21 + .../PeCoffExtraActionLibDebug.inf | 59 + .../PeCoffExtraActionLibDebug/X64/IntHandler.S | 28 + .../PeCoffExtraActionLibDebug/X64/IntHandler.asm | 29 + .../PeCoffExtraActionLibDebug/X64/IntHandler.nasm | 29 + .../X64/IntHandlerFuncs.c | 101 + 58 files changed, 14561 insertions(+) create mode 100644 Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/DebugAgent.c create mode 100644 Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/DebugAgent.h create mode 100644 Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/DebugMp.c create mode 100644 Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/DebugMp.h create mode 100644 Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/DebugTimer.c create mode 100644 Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/DebugTimer.h create mode 100644 Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/Ia32/ArchDebugSupport.c create mode 100644 Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/Ia32/ArchDebugSupport.h create mode 100644 Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/Ia32/AsmFuncs.S create mode 100644 Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/Ia32/AsmFuncs.asm create mode 100644 Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/Ia32/AsmFuncs.nasm create mode 100644 Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/Ia32/DebugException.h create mode 100644 Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/X64/ArchDebugSupport.c create mode 100644 Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/X64/ArchDebugSupport.h create mode 100644 Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/X64/AsmFuncs.S create mode 100644 Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/X64/AsmFuncs.asm create mode 100644 Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/X64/AsmFuncs.nasm create mode 100644 Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/X64/DebugException.h create mode 100644 Core/SourceLevelDebugPkg/Library/DebugAgent/DxeDebugAgent/DxeDebugAgentLib.c create mode 100644 Core/SourceLevelDebugPkg/Library/DebugAgent/DxeDebugAgent/DxeDebugAgentLib.h create mode 100644 Core/SourceLevelDebugPkg/Library/DebugAgent/DxeDebugAgent/SerialIo.c create mode 100644 Core/SourceLevelDebugPkg/Library/DebugAgent/DxeDebugAgentLib.inf create mode 100644 Core/SourceLevelDebugPkg/Library/DebugAgent/DxeDebugAgentLib.uni create mode 100644 Core/SourceLevelDebugPkg/Library/DebugAgent/SecPeiDebugAgent/SecPeiDebugAgentLib.c create mode 100644 Core/SourceLevelDebugPkg/Library/DebugAgent/SecPeiDebugAgent/SecPeiDebugAgentLib.h create mode 100644 Core/SourceLevelDebugPkg/Library/DebugAgent/SecPeiDebugAgentLib.inf create mode 100644 Core/SourceLevelDebugPkg/Library/DebugAgent/SecPeiDebugAgentLib.uni create mode 100644 Core/SourceLevelDebugPkg/Library/DebugAgent/SmmDebugAgent/SmmDebugAgentLib.c create mode 100644 Core/SourceLevelDebugPkg/Library/DebugAgent/SmmDebugAgent/SmmDebugAgentLib.h create mode 100644 Core/SourceLevelDebugPkg/Library/DebugAgent/SmmDebugAgentLib.inf create mode 100644 Core/SourceLevelDebugPkg/Library/DebugAgent/SmmDebugAgentLib.uni create mode 100644 Core/SourceLevelDebugPkg/Library/DebugCommunicationLibSerialPort/DebugCommunicationLibSerialPort.c create mode 100644 Core/SourceLevelDebugPkg/Library/DebugCommunicationLibSerialPort/DebugCommunicationLibSerialPort.inf create mode 100644 Core/SourceLevelDebugPkg/Library/DebugCommunicationLibSerialPort/DebugCommunicationLibSerialPort.uni create mode 100644 Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb/DebugCommunicationLibUsb.c create mode 100644 Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb/DebugCommunicationLibUsb.inf create mode 100644 Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb/DebugCommunicationLibUsb.uni create mode 100644 Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Common.c create mode 100644 Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Dxe.c create mode 100644 Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Dxe.inf create mode 100644 Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Dxe.uni create mode 100644 Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Internal.h create mode 100644 Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Pei.c create mode 100644 Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Pei.inf create mode 100644 Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Pei.uni create mode 100644 Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Transfer.c create mode 100644 Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/Ia32/IntHandler.S create mode 100644 Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/Ia32/IntHandler.asm create mode 100644 Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/Ia32/IntHandler.nasm create mode 100644 Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/Ia32/IntHandlerFuncs.c create mode 100644 Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/PeCoffExtraActionLib.c create mode 100644 Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/PeCoffExtraActionLib.h create mode 100644 Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/PeCoffExtraActionLib.uni create mode 100644 Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/PeCoffExtraActionLibDebug.inf create mode 100644 Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/X64/IntHandler.S create mode 100644 Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/X64/IntHandler.asm create mode 100644 Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/X64/IntHandler.nasm create mode 100644 Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/X64/IntHandlerFuncs.c (limited to 'Core/SourceLevelDebugPkg/Library') diff --git a/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/DebugAgent.c b/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/DebugAgent.c new file mode 100644 index 0000000000..6f3c41933d --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/DebugAgent.c @@ -0,0 +1,2646 @@ +/** @file + Commond Debug Agent library implementition. It mainly includes + the first C function called by exception/interrupt handlers, + read/write debug packet to communication with HOST based on transfer + protocol. + + Copyright (c) 2010 - 2017, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "DebugAgent.h" +#include "Ia32/DebugException.h" + +GLOBAL_REMOVE_IF_UNREFERENCED CHAR8 mErrorMsgVersionAlert[] = "\rThe SourceLevelDebugPkg you are using requires a newer version of the Intel(R) UDK Debugger Tool.\r\n"; +GLOBAL_REMOVE_IF_UNREFERENCED CHAR8 mErrorMsgSendInitPacket[] = "\rSend INIT break packet and try to connect the HOST (Intel(R) UDK Debugger Tool v1.5) ...\r\n"; +GLOBAL_REMOVE_IF_UNREFERENCED CHAR8 mErrorMsgConnectOK[] = "HOST connection is successful!\r\n"; +GLOBAL_REMOVE_IF_UNREFERENCED CHAR8 mErrorMsgConnectFail[] = "HOST connection is failed!\r\n"; +GLOBAL_REMOVE_IF_UNREFERENCED CHAR8 mWarningMsgIngoreBreakpoint[] = "Ignore break point in SMM for SMI issued during DXE debugging!\r\n"; + +// +// Vector Handoff Info list used by Debug Agent for persist +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_VECTOR_HANDOFF_INFO mVectorHandoffInfoDebugAgent[] = { + { + DEBUG_EXCEPT_DIVIDE_ERROR, // Vector 0 + EFI_VECTOR_HANDOFF_HOOK_BEFORE, + EFI_DEBUG_AGENT_GUID + }, + { + DEBUG_EXCEPT_DEBUG, // Vector 1 + EFI_VECTOR_HANDOFF_DO_NOT_HOOK, + EFI_DEBUG_AGENT_GUID + }, + { + DEBUG_EXCEPT_NMI, // Vector 2 + EFI_VECTOR_HANDOFF_HOOK_BEFORE, + EFI_DEBUG_AGENT_GUID + }, + { + DEBUG_EXCEPT_BREAKPOINT, // Vector 3 + EFI_VECTOR_HANDOFF_DO_NOT_HOOK, + EFI_DEBUG_AGENT_GUID + }, + { + DEBUG_EXCEPT_OVERFLOW, // Vector 4 + EFI_VECTOR_HANDOFF_HOOK_BEFORE, + EFI_DEBUG_AGENT_GUID + }, + { + DEBUG_EXCEPT_BOUND, // Vector 5 + EFI_VECTOR_HANDOFF_HOOK_BEFORE, + EFI_DEBUG_AGENT_GUID + }, + { + DEBUG_EXCEPT_INVALID_OPCODE, // Vector 6 + EFI_VECTOR_HANDOFF_HOOK_BEFORE, + EFI_DEBUG_AGENT_GUID + }, + { + DEBUG_EXCEPT_DOUBLE_FAULT, // Vector 8 + EFI_VECTOR_HANDOFF_HOOK_BEFORE, + EFI_DEBUG_AGENT_GUID + }, + { + DEBUG_EXCEPT_INVALID_TSS, // Vector 10 + EFI_VECTOR_HANDOFF_HOOK_BEFORE, + EFI_DEBUG_AGENT_GUID + }, + { + DEBUG_EXCEPT_SEG_NOT_PRESENT, // Vector 11 + EFI_VECTOR_HANDOFF_HOOK_BEFORE, + EFI_DEBUG_AGENT_GUID + }, + { + DEBUG_EXCEPT_STACK_FAULT, // Vector 12 + EFI_VECTOR_HANDOFF_HOOK_BEFORE, + EFI_DEBUG_AGENT_GUID + }, + { + DEBUG_EXCEPT_GP_FAULT, // Vector 13 + EFI_VECTOR_HANDOFF_HOOK_BEFORE, + EFI_DEBUG_AGENT_GUID + }, + { + DEBUG_EXCEPT_PAGE_FAULT, // Vector 14 + EFI_VECTOR_HANDOFF_HOOK_BEFORE, + EFI_DEBUG_AGENT_GUID + }, + { + DEBUG_EXCEPT_FP_ERROR, // Vector 16 + EFI_VECTOR_HANDOFF_HOOK_BEFORE, + EFI_DEBUG_AGENT_GUID + }, + { + DEBUG_EXCEPT_ALIGNMENT_CHECK, // Vector 17 + EFI_VECTOR_HANDOFF_HOOK_BEFORE, + EFI_DEBUG_AGENT_GUID + }, + { + DEBUG_EXCEPT_MACHINE_CHECK, // Vector 18 + EFI_VECTOR_HANDOFF_HOOK_BEFORE, + EFI_DEBUG_AGENT_GUID + }, + { + DEBUG_EXCEPT_SIMD, // Vector 19 + EFI_VECTOR_HANDOFF_HOOK_BEFORE, + EFI_DEBUG_AGENT_GUID + }, + { + DEBUG_TIMER_VECTOR, // Vector 32 + EFI_VECTOR_HANDOFF_DO_NOT_HOOK, + EFI_DEBUG_AGENT_GUID + }, + { + DEBUG_MAILBOX_VECTOR, // Vector 33 + EFI_VECTOR_HANDOFF_DO_NOT_HOOK, + EFI_DEBUG_AGENT_GUID + }, + { + 0, + EFI_VECTOR_HANDOFF_LAST_ENTRY, + { 0 } + } +}; + +GLOBAL_REMOVE_IF_UNREFERENCED UINTN mVectorHandoffInfoCount = sizeof (mVectorHandoffInfoDebugAgent) / sizeof (EFI_VECTOR_HANDOFF_INFO); + +/** + Calculate CRC16 for target data. + + @param[in] Data The target data. + @param[in] DataSize The target data size. + @param[in] Crc Initial CRC. + + @return UINT16 The CRC16 value. + +**/ +UINT16 +CalculateCrc16 ( + IN UINT8 *Data, + IN UINTN DataSize, + IN UINT16 Crc + ) +{ + UINTN Index; + UINTN BitIndex; + + for (Index = 0; Index < DataSize; Index++) { + Crc ^= (UINT16)Data[Index]; + for (BitIndex = 0; BitIndex < 8; BitIndex++) { + if ((Crc & 0x8000) != 0) { + Crc <<= 1; + Crc ^= 0x1021; + } else { + Crc <<= 1; + } + } + } + return Crc; +} + + +/** + Read IDT entry to check if IDT entries are setup by Debug Agent. + + @retval TRUE IDT entries were setup by Debug Agent. + @retval FALSE IDT entries were not setup by Debug Agent. + +**/ +BOOLEAN +IsDebugAgentInitialzed ( + VOID + ) +{ + UINTN InterruptHandler; + + InterruptHandler = (UINTN) GetExceptionHandlerInIdtEntry (0); + if (InterruptHandler >= 4 && *(UINT32 *)(InterruptHandler - 4) == AGENT_HANDLER_SIGNATURE) { + return TRUE; + } else { + return FALSE; + } +} + +/** + Find and report module image info to HOST. + + @param[in] AlignSize Image aligned size. + +**/ +VOID +FindAndReportModuleImageInfo ( + IN UINTN AlignSize + ) +{ + UINTN Pe32Data; + PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; + + // + // Find Image Base + // + Pe32Data = PeCoffSerachImageBase ((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 foramtted in buffer + // + DestBuffer += AsciiSPrint(DestBuffer, DEBUG_DATA_MAXIMUM_REAL_DATA - (DestBuffer - Buffer), "]\n"); + SendDebugMsgPacket (Buffer, DestBuffer - Buffer); + break; + } + } +} + +/** + Read remaing debug packet except for the start symbol + + @param[in] Handle Pointer to Debug Port handle. + @param[in, out] DebugHeader Debug header buffer including start symbol. + + @retval EFI_SUCCESS Read the symbol in BreakSymbol. + @retval EFI_CRC_ERROR CRC check fail. + @retval EFI_TIMEOUT Timeout occurs when reading debug packet. + @retval EFI_DEVICE_ERROR Receive the old or responsed packet. + +**/ +EFI_STATUS +ReadRemainingBreakPacket ( + IN DEBUG_PORT_HANDLE Handle, + IN OUT DEBUG_PACKET_HEADER *DebugHeader + ) +{ + UINT16 Crc; + DEBUG_AGENT_MAILBOX *Mailbox; + + // + // Has received start symbol, try to read the rest part + // + if (DebugAgentReadBuffer (Handle, (UINT8 *)DebugHeader + OFFSET_OF (DEBUG_PACKET_HEADER, Command), sizeof (DEBUG_PACKET_HEADER) - OFFSET_OF (DEBUG_PACKET_HEADER, Command), READ_PACKET_TIMEOUT) == 0) { + // + // Timeout occur, exit + // + DebugAgentMsgPrint (DEBUG_AGENT_WARNING, "Timeout in Debug Timer interrupt\n"); + return EFI_TIMEOUT; + } + + Crc = DebugHeader->Crc; + DebugHeader->Crc = 0; + if (CalculateCrc16 ((UINT8 *)DebugHeader, DebugHeader->Length, 0) != Crc) { + DebugAgentMsgPrint (DEBUG_AGENT_WARNING, "Debug Timer CRC (%x) against (%x)\n", Crc, CalculateCrc16 ((UINT8 *) &DebugHeader, DebugHeader->Length, 0)); + DebugAgentDataMsgPrint (DEBUG_AGENT_VERBOSE, FALSE, (UINT8 *)DebugHeader, DebugHeader->Length); + return EFI_CRC_ERROR; + } + Mailbox = GetMailboxPointer(); + if (IS_REQUEST (DebugHeader)) { + if (DebugHeader->SequenceNo == (UINT8) (Mailbox->HostSequenceNo + 1)) { + // + // Only updagte HostSequenceNo for new command packet + // + UpdateMailboxContent (Mailbox, DEBUG_MAILBOX_HOST_SEQUENCE_NO_INDEX, DebugHeader->SequenceNo); + return EFI_SUCCESS; + } + if (DebugHeader->SequenceNo == Mailbox->HostSequenceNo) { + return EFI_SUCCESS; + } + } + + return EFI_DEVICE_ERROR; +} + +/** + Check if HOST is attached based on Mailbox. + + @retval TRUE HOST is attached. + @retval FALSE HOST is not attached. + +**/ +BOOLEAN +IsHostAttached ( + VOID + ) +{ + return (BOOLEAN) (GetDebugFlag (DEBUG_AGENT_FLAG_HOST_ATTACHED) == 1); +} + +/** + Set HOST connect flag in Mailbox. + + @param[in] Attached Attach status. + +**/ +VOID +SetHostAttached ( + IN BOOLEAN Attached + ) +{ + DebugAgentMsgPrint (DEBUG_AGENT_INFO, "Attach status is %d\n", Attached); + SetDebugFlag (DEBUG_AGENT_FLAG_HOST_ATTACHED, (UINT32)Attached); +} + +/** + Set debug setting of Debug Agent in Mailbox. + + @param DebugSetting Pointer to Debug Setting defined by transfer protocol. + + @retval RETURN_SUCCESS The setting is set successfully. + @retval RETURN_UNSUPPORTED The Key value is not supported. + +**/ +RETURN_STATUS +SetDebugSetting ( + IN DEBUG_DATA_SET_DEBUG_SETTING *DebugSetting + ) +{ + RETURN_STATUS Status; + + Status = RETURN_SUCCESS; + switch (DebugSetting->Key) { + case DEBUG_AGENT_SETTING_SMM_ENTRY_BREAK: + SetDebugFlag (DEBUG_AGENT_FLAG_BREAK_ON_NEXT_SMI, DebugSetting->Value); + break; + case DEBUG_AGENT_SETTING_PRINT_ERROR_LEVEL: + SetDebugFlag (DEBUG_AGENT_FLAG_PRINT_ERROR_LEVEL, DebugSetting->Value); + break; + case DEBUG_AGENT_SETTING_BOOT_SCRIPT_ENTRY_BREAK: + SetDebugFlag (DEBUG_AGENT_FLAG_BREAK_BOOT_SCRIPT, DebugSetting->Value); + break; + default: + Status = RETURN_UNSUPPORTED; + } + return Status; +} + +/** + Exectue GO command. + + @param[in] CpuContext Pointer to saved CPU context. + +**/ +VOID +CommandGo ( + IN DEBUG_CPU_CONTEXT *CpuContext + ) +{ + IA32_EFLAGS32 *Eflags; + + Eflags = (IA32_EFLAGS32 *) &CpuContext->Eflags; + Eflags->Bits.TF = 0; + Eflags->Bits.RF = 1; +} + +/** + Execute Stepping command. + + @param[in] CpuContext Pointer to saved CPU context. + +**/ +VOID +CommandStepping ( + IN DEBUG_CPU_CONTEXT *CpuContext + ) +{ + IA32_EFLAGS32 *Eflags; + + Eflags = (IA32_EFLAGS32 *) &CpuContext->Eflags; + Eflags->Bits.TF = 1; + Eflags->Bits.RF = 1; + // + // Save and clear EFLAGS.IF to avoid interrupt happen when executing Stepping + // + SetDebugFlag (DEBUG_AGENT_FLAG_INTERRUPT_FLAG, Eflags->Bits.IF); + Eflags->Bits.IF = 0; + // + // Set Stepping Flag + // + SetDebugFlag (DEBUG_AGENT_FLAG_STEPPING, 1); +} + +/** + Do some cleanup after Stepping command done. + + @param[in] CpuContext Pointer to saved CPU context. + +**/ +VOID +CommandSteppingCleanup ( + IN DEBUG_CPU_CONTEXT *CpuContext + ) +{ + IA32_EFLAGS32 *Eflags; + + Eflags = (IA32_EFLAGS32 *) &CpuContext->Eflags; + // + // Restore EFLAGS.IF + // + Eflags->Bits.IF = GetDebugFlag (DEBUG_AGENT_FLAG_INTERRUPT_FLAG); + // + // Clear Stepping flag + // + SetDebugFlag (DEBUG_AGENT_FLAG_STEPPING, 0); +} + +/** + Set debug register for hardware breakpoint. + + @param[in] CpuContext Pointer to saved CPU context. + @param[in] SetHwBreakpoint Hardware breakpoint to be set. + +**/ +VOID +SetDebugRegister ( + IN DEBUG_CPU_CONTEXT *CpuContext, + IN DEBUG_DATA_SET_HW_BREAKPOINT *SetHwBreakpoint + ) +{ + UINT8 RegisterIndex; + UINTN Dr7Value; + + RegisterIndex = SetHwBreakpoint->Type.Index; + + // + // Set debug address + // + * ((UINTN *) &CpuContext->Dr0 + RegisterIndex) = (UINTN) SetHwBreakpoint->Address; + + Dr7Value = CpuContext->Dr7; + + // + // Enable Gx, Lx + // + Dr7Value |= (UINTN) (0x3 << (RegisterIndex * 2)); + // + // Set RWx and Lenx + // + Dr7Value &= (UINTN) (~(0xf << (16 + RegisterIndex * 4))); + Dr7Value |= (UINTN) ((SetHwBreakpoint->Type.Length << 2) | SetHwBreakpoint->Type.Access) << (16 + RegisterIndex * 4); + // + // Enable GE, LE + // + Dr7Value |= 0x300; + + CpuContext->Dr7 = Dr7Value; +} + +/** + Clear debug register for hardware breakpoint. + + @param[in] CpuContext Pointer to saved CPU context. + @param[in] ClearHwBreakpoint Hardware breakpoint to be cleared. + +**/ +VOID +ClearDebugRegister ( + IN DEBUG_CPU_CONTEXT *CpuContext, + IN DEBUG_DATA_CLEAR_HW_BREAKPOINT *ClearHwBreakpoint + ) +{ + if ((ClearHwBreakpoint->IndexMask & BIT0) != 0) { + CpuContext->Dr0 = 0; + CpuContext->Dr7 &= (UINTN)(~(0x3 << 0)); + } + if ((ClearHwBreakpoint->IndexMask & BIT1) != 0) { + CpuContext->Dr1 = 0; + CpuContext->Dr7 &= (UINTN)(~(0x3 << 2)); + } + if ((ClearHwBreakpoint->IndexMask & BIT2) != 0) { + CpuContext->Dr2 = 0; + CpuContext->Dr7 &= (UINTN)(~(0x3 << 4)); + } + if ((ClearHwBreakpoint->IndexMask & BIT3) != 0) { + CpuContext->Dr3 = 0; + CpuContext->Dr7 &= (UINTN)(~(0x3 << 6)); + } +} + + +/** + Return the offset of FP / MMX / XMM registers in the FPU saved state by register index. + + @param[in] Index Register index. + @param[out] Width Register width returned. + + @return Offset in the FPU Save State. + +**/ +UINT16 +ArchReadFxStatOffset ( + IN UINT8 Index, + OUT UINT8 *Width + ) +{ + if (Index < SOFT_DEBUGGER_REGISTER_ST0) { + switch (Index) { + case SOFT_DEBUGGER_REGISTER_FP_FCW: + *Width = (UINT8) sizeof (UINT16); + return OFFSET_OF(DEBUG_DATA_FX_SAVE_STATE, Fcw); + + case SOFT_DEBUGGER_REGISTER_FP_FSW: + *Width = (UINT8) sizeof (UINT16); + return OFFSET_OF(DEBUG_DATA_FX_SAVE_STATE, Fsw); + + case SOFT_DEBUGGER_REGISTER_FP_FTW: + *Width = (UINT8) sizeof (UINT16); + return OFFSET_OF(DEBUG_DATA_FX_SAVE_STATE, Ftw); + + case SOFT_DEBUGGER_REGISTER_FP_OPCODE: + *Width = (UINT8) sizeof (UINT16); + return OFFSET_OF(DEBUG_DATA_FX_SAVE_STATE, Opcode); + + case SOFT_DEBUGGER_REGISTER_FP_EIP: + *Width = (UINT8) sizeof (UINT32); + return OFFSET_OF(DEBUG_DATA_FX_SAVE_STATE, Eip); + + case SOFT_DEBUGGER_REGISTER_FP_CS: + *Width = (UINT8) sizeof (UINT16); + return OFFSET_OF(DEBUG_DATA_FX_SAVE_STATE, Cs); + + case SOFT_DEBUGGER_REGISTER_FP_DATAOFFSET: + *Width = (UINT8) sizeof (UINT32); + return OFFSET_OF(DEBUG_DATA_FX_SAVE_STATE, DataOffset); + + case SOFT_DEBUGGER_REGISTER_FP_DS: + *Width = (UINT8) sizeof (UINT16); + return OFFSET_OF(DEBUG_DATA_FX_SAVE_STATE, Ds); + + case SOFT_DEBUGGER_REGISTER_FP_MXCSR: + *Width = (UINT8) sizeof (UINT32); + return OFFSET_OF(DEBUG_DATA_FX_SAVE_STATE, Mxcsr); + + case SOFT_DEBUGGER_REGISTER_FP_MXCSR_MASK: + *Width = (UINT8) sizeof (UINT32); + return OFFSET_OF(DEBUG_DATA_FX_SAVE_STATE, Mxcsr_Mask); + } + } + + if (Index <= SOFT_DEBUGGER_REGISTER_ST7) { + *Width = 10; + } else if (Index <= SOFT_DEBUGGER_REGISTER_XMM15) { + *Width = 16; + } else { + // + // MMX register + // + *Width = 8; + Index -= SOFT_DEBUGGER_REGISTER_MM0 - SOFT_DEBUGGER_REGISTER_ST0; + } + + return OFFSET_OF (DEBUG_DATA_FX_SAVE_STATE, St0Mm0) + (Index - SOFT_DEBUGGER_REGISTER_ST0) * 16; +} + +/** + Return the pointer of the register value in the CPU saved context. + + @param[in] CpuContext Pointer to saved CPU context. + @param[in] Index Register index value. + @param[out] Width Data width to read. + + @return The pointer in the CPU saved context. + +**/ +UINT8 * +ArchReadRegisterBuffer ( + IN DEBUG_CPU_CONTEXT *CpuContext, + IN UINT8 Index, + OUT UINT8 *Width + ) +{ + UINT8 *Buffer; + + if (Index < SOFT_DEBUGGER_REGISTER_FP_BASE) { + Buffer = (UINT8 *) CpuContext + OFFSET_OF (DEBUG_CPU_CONTEXT, Dr0) + Index * sizeof (UINTN); + *Width = (UINT8) sizeof (UINTN); + } else { + // + // FPU/MMX/XMM registers + // + Buffer = (UINT8 *) CpuContext + OFFSET_OF (DEBUG_CPU_CONTEXT, FxSaveState) + ArchReadFxStatOffset (Index, Width); + } + + return Buffer; +} + +/** + Send the packet without data to HOST. + + @param[in] CommandType Type of Command. + @param[in] SequenceNo Sequence number. + +**/ +VOID +SendPacketWithoutData ( + IN UINT8 CommandType, + IN UINT8 SequenceNo + ) +{ + DEBUG_PACKET_HEADER DebugHeader; + DEBUG_PORT_HANDLE Handle; + + Handle = GetDebugPortHandle(); + + DebugHeader.StartSymbol = DEBUG_STARTING_SYMBOL_NORMAL; + DebugHeader.Command = CommandType; + DebugHeader.Length = sizeof (DEBUG_PACKET_HEADER); + DebugHeader.SequenceNo = SequenceNo; + DebugHeader.Crc = 0; + DebugHeader.Crc = CalculateCrc16 ((UINT8 *)&DebugHeader, sizeof (DEBUG_PACKET_HEADER), 0); + + DebugAgentDataMsgPrint (DEBUG_AGENT_VERBOSE, TRUE, (UINT8 *) &DebugHeader, DebugHeader.Length); + DebugPortWriteBuffer (Handle, (UINT8 *) &DebugHeader, DebugHeader.Length); +} + +/** + Send acknowledge packet to HOST. + + @param[in] AckCommand Type of Acknowledge packet. + +**/ +VOID +SendAckPacket ( + IN UINT8 AckCommand + ) +{ + UINT8 SequenceNo; + DEBUG_AGENT_MAILBOX *Mailbox; + + if (AckCommand != DEBUG_COMMAND_OK) { + // + // This is not ACK OK packet + // + DebugAgentMsgPrint (DEBUG_AGENT_ERROR, "Send ACK(%d)\n", AckCommand); + } + Mailbox = GetMailboxPointer(); + SequenceNo = Mailbox->HostSequenceNo; + DebugAgentMsgPrint (DEBUG_AGENT_INFO, "SendAckPacket: SequenceNo = %x\n", SequenceNo); + SendPacketWithoutData (AckCommand, SequenceNo); + UpdateMailboxContent (Mailbox, DEBUG_MAILBOX_LAST_ACK, AckCommand); +} + +/** + Decompress the Data in place. + + @param[in, out] Data The compressed data buffer. + The buffer is assumed large enough to hold the uncompressed data. + @param[in] Length The length of the compressed data buffer. + + @return The length of the uncompressed data buffer. +**/ +UINT8 +DecompressDataInPlace ( + IN OUT UINT8 *Data, + IN UINTN Length + ) +{ + UINTN Index; + UINT16 LastChar; + UINTN LastCharCount; + UINT8 CurrentChar; + + LastChar = (UINT16) -1; + LastCharCount = 0; + for (Index = 0; Index < Length; Index++) { + CurrentChar = Data[Index]; + if (LastCharCount == 2) { + LastCharCount = 0; + CopyMem (&Data[Index + CurrentChar], &Data[Index + 1], Length - Index - 1); + SetMem (&Data[Index], CurrentChar, (UINT8) LastChar); + LastChar = (UINT16) -1; + Index += CurrentChar - 1; + Length += CurrentChar - 1; + } else { + if (LastChar != CurrentChar) { + LastCharCount = 0; + } + LastCharCount++; + LastChar = CurrentChar; + } + } + + ASSERT (Length <= DEBUG_DATA_MAXIMUM_REAL_DATA); + + return (UINT8) Length; +} + +/** + Receive valid packet from HOST. + + @param[out] InputPacket Buffer to receive packet. + @param[out] BreakReceived TRUE means break-in symbol received. + FALSE means break-in symbol not received. + @param[out] IncompatibilityFlag If IncompatibilityFlag is not NULL, return + TRUE: Compatible packet received. + FALSE: Incompatible packet received. + @param[in] Timeout Time out value to wait for acknowlege from HOST. + The unit is microsecond. + @param[in] SkipStartSymbol TRUE: Skip time out when reading start symbol. + FALSE: Does not Skip time out when reading start symbol. + + @retval RETURN_SUCCESS A valid package was reveived in InputPacket. + @retval RETURN_TIMEOUT Timeout occurs. + +**/ +RETURN_STATUS +ReceivePacket ( + OUT UINT8 *InputPacket, + OUT BOOLEAN *BreakReceived, + OUT BOOLEAN *IncompatibilityFlag, OPTIONAL + IN UINTN Timeout, + IN BOOLEAN SkipStartSymbol + ) +{ + DEBUG_PACKET_HEADER *DebugHeader; + UINTN Received; + DEBUG_PORT_HANDLE Handle; + UINT16 Crc; + UINTN TimeoutForStartSymbol; + + Handle = GetDebugPortHandle(); + if (SkipStartSymbol) { + TimeoutForStartSymbol = 0; + } else { + TimeoutForStartSymbol = Timeout; + } + + DebugHeader = (DEBUG_PACKET_HEADER *) InputPacket; + while (TRUE) { + // + // Find the valid start symbol + // + Received = DebugAgentReadBuffer (Handle, &DebugHeader->StartSymbol, sizeof (DebugHeader->StartSymbol), TimeoutForStartSymbol); + if (Received < sizeof (DebugHeader->StartSymbol)) { + DebugAgentMsgPrint (DEBUG_AGENT_WARNING, "DebugAgentReadBuffer(StartSymbol) timeout\n"); + return RETURN_TIMEOUT; + } + + if ((DebugHeader->StartSymbol != DEBUG_STARTING_SYMBOL_NORMAL) && (DebugHeader->StartSymbol != DEBUG_STARTING_SYMBOL_COMPRESS)) { + DebugAgentMsgPrint (DEBUG_AGENT_WARNING, "Invalid start symbol received [%02x]\n", DebugHeader->StartSymbol); + continue; + } + + // + // Read Package header till field Length + // + Received = DebugAgentReadBuffer ( + Handle, + (UINT8 *) DebugHeader + OFFSET_OF (DEBUG_PACKET_HEADER, Command), + OFFSET_OF (DEBUG_PACKET_HEADER, Length) + sizeof (DebugHeader->Length) - sizeof (DebugHeader->StartSymbol), + Timeout + ); + if (Received == 0) { + DebugAgentMsgPrint (DEBUG_AGENT_ERROR, "DebugAgentReadBuffer(Command) timeout\n"); + return RETURN_TIMEOUT; + } + if (DebugHeader->Length < sizeof (DEBUG_PACKET_HEADER)) { + if (IncompatibilityFlag != NULL) { + // + // This is one old version debug packet format, set Incompatibility flag + // + *IncompatibilityFlag = TRUE; + } else { + // + // Skip the bad small packet + // + continue; + } + } else { + // + // Read the payload data include the CRC field + // + Received = DebugAgentReadBuffer (Handle, &DebugHeader->SequenceNo, (UINT8) (DebugHeader->Length - OFFSET_OF (DEBUG_PACKET_HEADER, SequenceNo)), Timeout); + if (Received == 0) { + DebugAgentMsgPrint (DEBUG_AGENT_ERROR, "DebugAgentReadBuffer(SequenceNo) timeout\n"); + return RETURN_TIMEOUT; + } + // + // Calculate the CRC of Debug Packet + // + Crc = DebugHeader->Crc; + DebugHeader->Crc = 0; + if (Crc == CalculateCrc16 ((UINT8 *) DebugHeader, DebugHeader->Length, 0)) { + break; + } + DebugAgentMsgPrint (DEBUG_AGENT_WARNING, "CRC Error (received CRC is %x)\n", Crc); + DebugAgentDataMsgPrint (DEBUG_AGENT_VERBOSE, FALSE, (UINT8 *) DebugHeader, DebugHeader->Length); + } + } + + DebugAgentDataMsgPrint (DEBUG_AGENT_VERBOSE, FALSE, (UINT8 *) DebugHeader, DebugHeader->Length); + + if (DebugHeader->StartSymbol == DEBUG_STARTING_SYMBOL_COMPRESS) { + DebugHeader->StartSymbol = DEBUG_STARTING_SYMBOL_NORMAL; + DebugHeader->Length = DecompressDataInPlace ( + (UINT8 *) (DebugHeader + 1), DebugHeader->Length - sizeof (DEBUG_PACKET_HEADER) + ) + sizeof (DEBUG_PACKET_HEADER); + } + return RETURN_SUCCESS; +} + +/** + Receive acknowledge packet OK from HOST in specified time. + + @param[in] Command The command type issued by TARGET. + @param[in] Timeout Time out value to wait for acknowlege from HOST. + The unit is microsecond. + @param[out] BreakReceived If BreakReceived is not NULL, + TRUE is retured if break-in symbol received. + FALSE is retured if break-in symbol not received. + @param[out] IncompatibilityFlag If IncompatibilityFlag is not NULL, return + TRUE: Compatible packet received. + FALSE: Incompatible packet received. + + @retval RETRUEN_SUCCESS Succeed to receive acknowlege packet from HOST, + the type of acknowlege packet saved in Ack. + @retval RETURN_TIMEOUT Specified timeout value was up. + +**/ +RETURN_STATUS +SendCommandAndWaitForAckOK ( + IN UINT8 Command, + IN UINTN Timeout, + OUT BOOLEAN *BreakReceived, OPTIONAL + OUT BOOLEAN *IncompatibilityFlag OPTIONAL + ) +{ + RETURN_STATUS Status; + UINT8 InputPacketBuffer[DEBUG_DATA_UPPER_LIMIT]; + DEBUG_PACKET_HEADER *DebugHeader; + UINT8 SequenceNo; + UINT8 HostSequenceNo; + UINT8 RetryCount; + + RetryCount = 3; + DebugHeader = (DEBUG_PACKET_HEADER *) InputPacketBuffer; + Status = RETURN_TIMEOUT; + while (RetryCount > 0) { + SequenceNo = GetMailboxPointer()->SequenceNo; + HostSequenceNo = GetMailboxPointer()->HostSequenceNo; + SendPacketWithoutData (Command, SequenceNo); + Status = ReceivePacket ((UINT8 *) DebugHeader, BreakReceived, IncompatibilityFlag, Timeout, FALSE); + if (Status == RETURN_TIMEOUT) { + if (Command == DEBUG_COMMAND_INIT_BREAK) { + RetryCount--; + } else { + DebugAgentMsgPrint (DEBUG_AGENT_WARNING, "TARGET: Timeout when waiting for ACK packet.\n"); + } + continue; + } + ASSERT_EFI_ERROR (Status); + // + // Status == RETURN_SUCCESS + // + if (DebugHeader->Command == DEBUG_COMMAND_OK && DebugHeader->SequenceNo == SequenceNo) { + // + // Received Ack OK + // + UpdateMailboxContent (GetMailboxPointer(), DEBUG_MAILBOX_SEQUENCE_NO_INDEX, ++SequenceNo); + return Status; + } + if (DebugHeader->Command == DEBUG_COMMAND_GO && (DebugHeader->SequenceNo == HostSequenceNo || Command == DEBUG_COMMAND_INIT_BREAK)) { + // + // Received Old GO + // + if (Command == DEBUG_COMMAND_INIT_BREAK) { + DebugAgentMsgPrint (DEBUG_AGENT_WARNING, "TARGET: Receive GO() in last boot\n"); + } + SendPacketWithoutData (DEBUG_COMMAND_OK, DebugHeader->SequenceNo); + } + } + + ASSERT (Command == DEBUG_COMMAND_INIT_BREAK); + return Status; +} + +/** + Get current break cause. + + @param[in] Vector Vector value of exception or interrupt. + @param[in] CpuContext Pointer to save CPU context. + + @return The type of break cause defined by XXXX + +**/ +UINT8 +GetBreakCause ( + IN UINTN Vector, + IN DEBUG_CPU_CONTEXT *CpuContext + ) +{ + UINT8 Cause; + + Cause = DEBUG_DATA_BREAK_CAUSE_UNKNOWN; + + switch (Vector) { + case DEBUG_INT1_VECTOR: + case DEBUG_INT3_VECTOR: + + if (Vector == DEBUG_INT1_VECTOR) { + // + // INT 1 + // + if ((CpuContext->Dr6 & BIT14) != 0) { + Cause = DEBUG_DATA_BREAK_CAUSE_STEPPING; + // + // DR6.BIT14 Indicates (when set) that the debug exception was + // triggered by the single step execution mode. + // The single-step mode is the highest priority debug exception. + // This is single step, no need to check DR0, to ensure single step + // work in PeCoffExtraActionLib (right after triggering a breakpoint + // to report image load/unload). + // + return Cause; + + } else { + Cause = DEBUG_DATA_BREAK_CAUSE_HW_BREAKPOINT; + } + } else { + // + // INT 3 + // + Cause = DEBUG_DATA_BREAK_CAUSE_SW_BREAKPOINT; + } + + switch (CpuContext->Dr0) { + case IMAGE_LOAD_SIGNATURE: + case IMAGE_UNLOAD_SIGNATURE: + + if (CpuContext->Dr3 == IO_PORT_BREAKPOINT_ADDRESS) { + + Cause = (UINT8) ((CpuContext->Dr0 == IMAGE_LOAD_SIGNATURE) ? + DEBUG_DATA_BREAK_CAUSE_IMAGE_LOAD : DEBUG_DATA_BREAK_CAUSE_IMAGE_UNLOAD); + } + break; + + case SOFT_INTERRUPT_SIGNATURE: + + if (CpuContext->Dr1 == MEMORY_READY_SIGNATURE) { + Cause = DEBUG_DATA_BREAK_CAUSE_MEMORY_READY; + CpuContext->Dr0 = 0; + } else if (CpuContext->Dr1 == SYSTEM_RESET_SIGNATURE) { + Cause = DEBUG_DATA_BREAK_CAUSE_SYSTEM_RESET; + CpuContext->Dr0 = 0; + } + break; + + default: + break; + + } + + break; + + case DEBUG_TIMER_VECTOR: + Cause = DEBUG_DATA_BREAK_CAUSE_USER_HALT; + break; + + default: + if (Vector < 20) { + if (GetDebugFlag (DEBUG_AGENT_FLAG_STEPPING) == 1) { + // + // If stepping command is executing + // + Cause = DEBUG_DATA_BREAK_CAUSE_STEPPING; + } else { + Cause = DEBUG_DATA_BREAK_CAUSE_EXCEPTION; + } + } + break; + } + + return Cause; +} + +/** + Copy memory from source to destination with specified width. + + @param[out] Dest A pointer to the destination buffer of the memory copy. + @param[in] Src A pointer to the source buffer of the memory copy. + @param[in] Count The number of data with specified width to copy from source to destination. + @param[in] Width Data width in byte. + +**/ +VOID +CopyMemByWidth ( + OUT UINT8 *Dest, + IN UINT8 *Src, + IN UINT16 Count, + IN UINT8 Width + ) +{ + UINT8 *Destination; + UINT8 *Source; + INT8 Step; + + if (Src > Dest) { + Destination = Dest; + Source = Src; + Step = Width; + } else { + // + // Copy memory from tail to avoid memory overlap + // + Destination = Dest + (Count - 1) * Width; + Source = Src + (Count - 1) * Width; + Step = -Width; + } + + while (Count-- != 0) { + switch (Width) { + case 1: + *(UINT8 *) Destination = MmioRead8 ((UINTN) Source); + break; + case 2: + *(UINT16 *) Destination = MmioRead16 ((UINTN) Source); + break; + case 4: + *(UINT32 *) Destination = MmioRead32 ((UINTN) Source); + break; + case 8: + *(UINT64 *) Destination = MmioRead64 ((UINTN) Source); + break; + default: + ASSERT (FALSE); + } + Source += Step; + Destination += Step; + } +} + +/** + Compress the data buffer but do not modify the original buffer. + + The compressed data is directly send to the debug channel. + Compressing in place doesn't work because the data may become larger + during compressing phase. ("3 3 ..." --> "3 3 0 ...") + The routine is expected to be called three times: + 1. Compute the length of the compressed data buffer; + 2. Compute the CRC of the compressed data buffer; + 3. Compress the data and send to the debug channel. + + @param[in] Handle The debug channel handle to send the compressed data buffer. + @param[in] Data The data buffer. + @param[in] Length The length of the data buffer. + @param[in] Send TRUE to send the compressed data buffer. + @param[out] CompressedLength Return the length of the compressed data buffer. + It may be larger than the Length in some cases. + @param[out] CompressedCrc Return the CRC of the compressed data buffer. +**/ +VOID +CompressData ( + IN DEBUG_PORT_HANDLE Handle, + IN UINT8 *Data, + IN UINT8 Length, + IN BOOLEAN Send, + OUT UINTN *CompressedLength, OPTIONAL + OUT UINT16 *CompressedCrc OPTIONAL + ) +{ + UINTN Index; + UINT8 LastChar; + UINT8 LastCharCount; + UINT8 CurrentChar; + UINTN CompressedIndex; + + ASSERT (Length > 0); + LastChar = Data[0] + 1; // Just ensure it's different from the first byte. + LastCharCount = 0; + + for (Index = 0, CompressedIndex = 0; Index <= Length; Index++) { + if (Index < Length) { + CurrentChar = Data[Index]; + } else { + CurrentChar = (UINT8) LastChar + 1; // just ensure it's different from LastChar + } + if (LastChar != CurrentChar) { + if (LastCharCount == 1) { + CompressedIndex++; + if (CompressedCrc != NULL) { + *CompressedCrc = CalculateCrc16 (&LastChar, 1, *CompressedCrc); + } + if (Send) { + DebugPortWriteBuffer (Handle, &LastChar, 1); + } + + } else if (LastCharCount >= 2) { + CompressedIndex += 3; + LastCharCount -= 2; + if (CompressedCrc != NULL) { + *CompressedCrc = CalculateCrc16 (&LastChar, 1, *CompressedCrc); + *CompressedCrc = CalculateCrc16 (&LastChar, 1, *CompressedCrc); + *CompressedCrc = CalculateCrc16 (&LastCharCount, 1, *CompressedCrc); + } + if (Send) { + DebugPortWriteBuffer (Handle, &LastChar, 1); + DebugPortWriteBuffer (Handle, &LastChar, 1); + DebugPortWriteBuffer (Handle, &LastCharCount, 1); + } + } + LastCharCount = 0; + } + LastCharCount++; + LastChar = CurrentChar; + } + + if (CompressedLength != NULL) { + *CompressedLength = CompressedIndex; + } +} + +/** + Read memory with speicifed width and send packet with response data to HOST. + + @param[in] Data Pointer to response data buffer. + @param[in] Count The number of data with specified Width. + @param[in] Width Data width in byte. + @param[in] DebugHeader Pointer to a buffer for creating response packet and receiving ACK packet, + to minimize the stack usage. + + @retval RETURN_SUCCESS Response data was sent successfully. + +**/ +RETURN_STATUS +ReadMemoryAndSendResponsePacket ( + IN UINT8 *Data, + IN UINT16 Count, + IN UINT8 Width, + IN DEBUG_PACKET_HEADER *DebugHeader + ) +{ + RETURN_STATUS Status; + BOOLEAN LastPacket; + DEBUG_PORT_HANDLE Handle; + UINT8 SequenceNo; + UINTN RemainingDataSize; + UINT8 CurrentDataSize; + UINTN CompressedDataSize; + + Handle = GetDebugPortHandle(); + + RemainingDataSize = Count * Width; + while (TRUE) { + SequenceNo = GetMailboxPointer()->HostSequenceNo; + if (RemainingDataSize <= DEBUG_DATA_MAXIMUM_REAL_DATA) { + // + // If the remaining data is less one real packet size, this is the last data packet + // + CurrentDataSize = (UINT8) RemainingDataSize; + LastPacket = TRUE; + DebugHeader->Command = DEBUG_COMMAND_OK; + } else { + // + // Data is too larger to be sent in one packet, calculate the actual data size could + // be sent in one Maximum data packet + // + CurrentDataSize = (DEBUG_DATA_MAXIMUM_REAL_DATA / Width) * Width; + LastPacket = FALSE; + DebugHeader->Command = DEBUG_COMMAND_IN_PROGRESS; + } + // + // Construct the rest Debug header + // + DebugHeader->StartSymbol = DEBUG_STARTING_SYMBOL_NORMAL; + DebugHeader->Length = CurrentDataSize + sizeof (DEBUG_PACKET_HEADER); + DebugHeader->SequenceNo = SequenceNo; + DebugHeader->Crc = 0; + CopyMemByWidth ((UINT8 *) (DebugHeader + 1), Data, CurrentDataSize / Width, Width); + + // + // Compression/decompression support was added since revision 0.4. + // Revision 0.3 shouldn't compress the packet. + // + if (DEBUG_AGENT_REVISION >= DEBUG_AGENT_REVISION_04) { + // + // Get the compressed data size without modifying the packet. + // + CompressData ( + Handle, + (UINT8 *) (DebugHeader + 1), + CurrentDataSize, + FALSE, + &CompressedDataSize, + NULL + ); + } else { + CompressedDataSize = CurrentDataSize; + } + if (CompressedDataSize < CurrentDataSize) { + DebugHeader->Length = (UINT8) CompressedDataSize + sizeof (DEBUG_PACKET_HEADER); + DebugHeader->StartSymbol = DEBUG_STARTING_SYMBOL_COMPRESS; + // + // Compute the CRC of the packet head without modifying the packet. + // + DebugHeader->Crc = CalculateCrc16 ((UINT8 *) DebugHeader, sizeof (DEBUG_PACKET_HEADER), 0); + CompressData ( + Handle, + (UINT8 *) (DebugHeader + 1), + CurrentDataSize, + FALSE, + NULL, + &DebugHeader->Crc + ); + // + // Send out the packet head. + // + DebugPortWriteBuffer (Handle, (UINT8 *) DebugHeader, sizeof (DEBUG_PACKET_HEADER)); + // + // Compress and send out the packet data. + // + CompressData ( + Handle, + (UINT8 *) (DebugHeader + 1), + CurrentDataSize, + TRUE, + NULL, + NULL + ); + } else { + + // + // Calculate and fill the checksum, DebugHeader->Crc should be 0 before invoking CalculateCrc16 () + // + DebugHeader->Crc = CalculateCrc16 ((UINT8 *) DebugHeader, DebugHeader->Length, 0); + + DebugAgentDataMsgPrint (DEBUG_AGENT_VERBOSE, TRUE, (UINT8 *) DebugHeader, DebugHeader->Length); + + DebugPortWriteBuffer (Handle, (UINT8 *) DebugHeader, DebugHeader->Length); + } + + while (TRUE) { + Status = ReceivePacket ((UINT8 *) DebugHeader, NULL, NULL, READ_PACKET_TIMEOUT, FALSE); + if (Status == RETURN_TIMEOUT) { + DebugAgentMsgPrint (DEBUG_AGENT_WARNING, "TARGET: Timeout in SendDataResponsePacket()\n"); + break; + } + if ((DebugHeader->Command == DEBUG_COMMAND_OK) && (DebugHeader->SequenceNo == SequenceNo) && LastPacket) { + // + // If this is the last packet, return RETURN_SUCCESS. + // + return RETURN_SUCCESS; + } + if ((DebugHeader->Command == DEBUG_COMMAND_CONTINUE) && (DebugHeader->SequenceNo == (UINT8) (SequenceNo + 1))) { + // + // Calculate the rest data size + // + Data += CurrentDataSize; + RemainingDataSize -= CurrentDataSize; + UpdateMailboxContent (GetMailboxPointer(), DEBUG_MAILBOX_HOST_SEQUENCE_NO_INDEX, DebugHeader->SequenceNo); + break; + } + if (DebugHeader->SequenceNo >= SequenceNo) { + DebugAgentMsgPrint (DEBUG_AGENT_WARNING, "TARGET: Received one old or new command(SequenceNo is %x, last SequenceNo is %x)\n", SequenceNo, DebugHeader->SequenceNo); + break; + } + } + } +} + +/** + Send packet with response data to HOST. + + @param[in] Data Pointer to response data buffer. + @param[in] DataSize Size of response data in byte. + @param[in, out] DebugHeader Pointer to a buffer for creating response packet and receiving ACK packet, + to minimize the stack usage. + + @retval RETURN_SUCCESS Response data was sent successfully. + +**/ +RETURN_STATUS +SendDataResponsePacket ( + IN UINT8 *Data, + IN UINT16 DataSize, + IN OUT DEBUG_PACKET_HEADER *DebugHeader + ) +{ + return ReadMemoryAndSendResponsePacket (Data, DataSize, 1, DebugHeader); +} + +/** + Try to attach the HOST. + + Send init break packet to HOST: + If no acknowlege received in specified Timeout, return RETURN_TIMEOUT. + If received acknowlege, check the revision of HOST. + Set Attach Flag if attach successfully. + + @param[in] BreakCause Break cause of this break event. + @param[in] Timeout Time out value to wait for acknowlege from HOST. + The unit is microsecond. + @param[out] BreakReceived If BreakReceived is not NULL, + TRUE is retured if break-in symbol received. + FALSE is retured if break-in symbol not received. +**/ +RETURN_STATUS +AttachHost ( + IN UINT8 BreakCause, + IN UINTN Timeout, + OUT BOOLEAN *BreakReceived + ) +{ + RETURN_STATUS Status; + DEBUG_PORT_HANDLE Handle; + BOOLEAN IncompatibilityFlag; + + IncompatibilityFlag = FALSE; + Handle = GetDebugPortHandle(); + + // + // Send init break and wait ack in Timeout + // + DebugPortWriteBuffer (Handle, (UINT8 *) mErrorMsgSendInitPacket, AsciiStrLen (mErrorMsgSendInitPacket)); + if (BreakCause == DEBUG_DATA_BREAK_CAUSE_SYSTEM_RESET) { + Status = SendCommandAndWaitForAckOK (DEBUG_COMMAND_INIT_BREAK, Timeout, BreakReceived, &IncompatibilityFlag); + } else { + Status = SendCommandAndWaitForAckOK (DEBUG_COMMAND_ATTACH_BREAK, Timeout, BreakReceived, &IncompatibilityFlag); + } + if (IncompatibilityFlag) { + // + // If the incompatible Debug Packet received, the HOST should be running transfer protocol before DEBUG_AGENT_REVISION. + // It could be UDK Debugger for Windows v1.1/v1.2 or for Linux v0.8/v1.2. + // + DebugPortWriteBuffer (Handle, (UINT8 *) mErrorMsgVersionAlert, AsciiStrLen (mErrorMsgVersionAlert)); + CpuDeadLoop (); + } + + if (RETURN_ERROR (Status)) { + DebugPortWriteBuffer (Handle, (UINT8 *) mErrorMsgConnectFail, AsciiStrLen (mErrorMsgConnectFail)); + } else { + DebugPortWriteBuffer (Handle, (UINT8 *) mErrorMsgConnectOK, AsciiStrLen (mErrorMsgConnectOK)); + // + // Set Attach flag + // + SetHostAttached (TRUE); + } + return Status; +} + +/** + Send Break point packet to HOST. + + Only the first breaking processor could sent BREAK_POINT packet. + + @param[in] BreakCause Break cause of this break event. + @param[in] ProcessorIndex Processor index value. + @param[out] BreakReceived If BreakReceived is not NULL, + TRUE is retured if break-in symbol received. + FALSE is retured if break-in symbol not received. + +**/ +VOID +SendBreakPacketToHost ( + IN UINT8 BreakCause, + IN UINT32 ProcessorIndex, + OUT BOOLEAN *BreakReceived + ) +{ + UINT8 InputCharacter; + DEBUG_PORT_HANDLE Handle; + + Handle = GetDebugPortHandle(); + + if (IsHostAttached ()) { + DebugAgentMsgPrint (DEBUG_AGENT_INFO, "processor[%x]:Send Break Packet to HOST.\n", ProcessorIndex); + SendCommandAndWaitForAckOK (DEBUG_COMMAND_BREAK_POINT, READ_PACKET_TIMEOUT, BreakReceived, NULL); + } else { + DebugAgentMsgPrint (DEBUG_AGENT_INFO, "processor[%x]:Try to attach HOST.\n", ProcessorIndex); + // + // If HOST is not attached, try to attach it firstly. + // + // + // Poll Attach symbols from HOST and ack OK + // + do { + DebugAgentReadBuffer (Handle, &InputCharacter, 1, 0); + } while (InputCharacter != DEBUG_STARTING_SYMBOL_ATTACH); + SendAckPacket (DEBUG_COMMAND_OK); + + // + // Try to attach HOST + // + while (AttachHost (BreakCause, 0, NULL) != RETURN_SUCCESS); + + } +} + +/** + The main function to process communication with HOST. + + It received the command packet from HOST, and sent response data packet to HOST. + + @param[in] Vector Vector value of exception or interrutp. + @param[in, out] CpuContext Pointer to saved CPU context. + @param[in] BreakReceived TRUE means break-in symbol received. + FALSE means break-in symbol not received. + +**/ +VOID +CommandCommunication ( + IN UINTN Vector, + IN OUT DEBUG_CPU_CONTEXT *CpuContext, + IN BOOLEAN BreakReceived + ) +{ + RETURN_STATUS Status; + UINT8 InputPacketBuffer[DEBUG_DATA_UPPER_LIMIT + sizeof (UINT64) - 1]; + DEBUG_PACKET_HEADER *DebugHeader; + UINT8 Width; + UINT8 Data8; + UINT32 Data32; + UINT64 Data64; + DEBUG_DATA_READ_MEMORY *MemoryRead; + DEBUG_DATA_WRITE_MEMORY *MemoryWrite; + DEBUG_DATA_READ_IO *IoRead; + DEBUG_DATA_WRITE_IO *IoWrite; + DEBUG_DATA_READ_REGISTER *RegisterRead; + DEBUG_DATA_WRITE_REGISTER *RegisterWrite; + UINT8 *RegisterBuffer; + DEBUG_DATA_READ_MSR *MsrRegisterRead; + DEBUG_DATA_WRITE_MSR *MsrRegisterWrite; + DEBUG_DATA_CPUID *Cpuid; + DEBUG_DATA_RESPONSE_BREAK_CAUSE BreakCause; + DEBUG_DATA_RESPONSE_CPUID CpuidResponse; + DEBUG_DATA_SEARCH_SIGNATURE *SearchSignature; + DEBUG_DATA_RESPONSE_GET_EXCEPTION Exception; + DEBUG_DATA_RESPONSE_GET_REVISION DebugAgentRevision; + DEBUG_DATA_SET_VIEWPOINT *SetViewPoint; + BOOLEAN HaltDeferred; + UINT32 ProcessorIndex; + DEBUG_AGENT_EXCEPTION_BUFFER AgentExceptionBuffer; + UINT32 IssuedViewPoint; + DEBUG_AGENT_MAILBOX *Mailbox; + UINT8 *AlignedDataPtr; + + ProcessorIndex = 0; + IssuedViewPoint = 0; + HaltDeferred = BreakReceived; + + if (MultiProcessorDebugSupport()) { + ProcessorIndex = GetProcessorIndex (); + SetCpuStopFlagByIndex (ProcessorIndex, TRUE); + if (mDebugMpContext.ViewPointIndex == ProcessorIndex) { + // + // Only the current view processor could set AgentInProgress Flag. + // + IssuedViewPoint = ProcessorIndex; + } + } + + if (IssuedViewPoint == ProcessorIndex) { + // + // Set AgentInProgress Flag. + // + SetDebugFlag (DEBUG_AGENT_FLAG_AGENT_IN_PROGRESS, 1); + } + + while (TRUE) { + + if (MultiProcessorDebugSupport()) { + // + // Check if the current processor is HOST view point + // + if (mDebugMpContext.ViewPointIndex != ProcessorIndex) { + if (mDebugMpContext.RunCommandSet) { + // + // If HOST view point sets RUN flag, run GO command to leave + // + SetCpuStopFlagByIndex (ProcessorIndex, FALSE); + CommandGo (CpuContext); + break; + } else { + // + // Run into loop again + // + CpuPause (); + continue; + } + } + } + + AcquireMpSpinLock (&mDebugMpContext.DebugPortSpinLock); + + DebugHeader =(DEBUG_PACKET_HEADER *) InputPacketBuffer; + + DebugAgentMsgPrint (DEBUG_AGENT_INFO, "TARGET: Try to get command from HOST...\n"); + Status = ReceivePacket ((UINT8 *) DebugHeader, &BreakReceived, NULL, READ_PACKET_TIMEOUT, TRUE); + if (Status != RETURN_SUCCESS || !IS_REQUEST (DebugHeader)) { + DebugAgentMsgPrint (DEBUG_AGENT_WARNING, "TARGET: Get command[%x] sequenceno[%x] returned status is [%x] \n", DebugHeader->Command, DebugHeader->SequenceNo, Status); + DebugAgentMsgPrint (DEBUG_AGENT_WARNING, "TARGET: Get command failed or it's response packet not expected! \n"); + ReleaseMpSpinLock (&mDebugMpContext.DebugPortSpinLock); + continue; + } + + Mailbox = GetMailboxPointer (); + if (DebugHeader->SequenceNo == Mailbox->HostSequenceNo) { + DebugAgentMsgPrint (DEBUG_AGENT_WARNING, "TARGET: Receive one old command[%x] agaist command[%x]\n", DebugHeader->SequenceNo, Mailbox->HostSequenceNo); + SendAckPacket (Mailbox->LastAck); + ReleaseMpSpinLock (&mDebugMpContext.DebugPortSpinLock); + continue; + } else if (DebugHeader->SequenceNo == (UINT8) (Mailbox->HostSequenceNo + 1)) { + UpdateMailboxContent (Mailbox, DEBUG_MAILBOX_HOST_SEQUENCE_NO_INDEX, (UINT8) DebugHeader->SequenceNo); + } else { + DebugAgentMsgPrint (DEBUG_AGENT_WARNING, "Receive one invalid comamnd[%x] agaist command[%x]\n", DebugHeader->SequenceNo, Mailbox->HostSequenceNo); + ReleaseMpSpinLock (&mDebugMpContext.DebugPortSpinLock); + continue; + } + + // + // Save CPU content before executing HOST commond + // + UpdateMailboxContent (Mailbox, DEBUG_MAILBOX_EXCEPTION_BUFFER_POINTER_INDEX, (UINT64)(UINTN) &AgentExceptionBuffer.JumpBuffer); + if (SetJump (&AgentExceptionBuffer.JumpBuffer) != 0) { + // + // If HOST command failed, continue to wait for HOST's next command + // If needed, agent could send exception info to HOST. + // + SendAckPacket (DEBUG_COMMAND_ABORT); + ReleaseMpSpinLock (&mDebugMpContext.DebugPortSpinLock); + continue; + } + + DebugAgentMsgPrint (DEBUG_AGENT_INFO, "Processor[%x]:Received one command(%x)\n", mDebugMpContext.ViewPointIndex, DebugHeader->Command); + + switch (DebugHeader->Command) { + + case DEBUG_COMMAND_HALT: + SendAckPacket (DEBUG_COMMAND_HALT_DEFERRED); + HaltDeferred = TRUE; + BreakReceived = FALSE; + Status = RETURN_SUCCESS; + break; + + case DEBUG_COMMAND_RESET: + SendAckPacket (DEBUG_COMMAND_OK); + SendAckPacket (DEBUG_COMMAND_OK); + SendAckPacket (DEBUG_COMMAND_OK); + ReleaseMpSpinLock (&mDebugMpContext.DebugPortSpinLock); + + ResetCold (); + // + // Assume system resets in 2 seconds, otherwise send TIMEOUT packet. + // PCD can be used if 2 seconds isn't long enough for some platforms. + // + MicroSecondDelay (2000000); + UpdateMailboxContent (Mailbox, DEBUG_MAILBOX_HOST_SEQUENCE_NO_INDEX, Mailbox->HostSequenceNo + 1); + SendAckPacket (DEBUG_COMMAND_TIMEOUT); + SendAckPacket (DEBUG_COMMAND_TIMEOUT); + SendAckPacket (DEBUG_COMMAND_TIMEOUT); + break; + + case DEBUG_COMMAND_GO: + CommandGo (CpuContext); + // + // Clear Dr0 to avoid to be recognized as IMAGE_LOAD/_UNLOAD again when hitting a breakpoint after GO + // If HOST changed Dr0 before GO, we will not change Dr0 here + // + Data8 = GetBreakCause (Vector, CpuContext); + if (Data8 == DEBUG_DATA_BREAK_CAUSE_IMAGE_LOAD || Data8 == DEBUG_DATA_BREAK_CAUSE_IMAGE_UNLOAD) { + CpuContext->Dr0 = 0; + } + + if (!HaltDeferred) { + // + // If no HALT command received when being in-active mode + // + if (MultiProcessorDebugSupport()) { + Data32 = FindNextPendingBreakCpu (); + if (Data32 != -1) { + // + // If there are still others processors being in break state, + // send OK packet to HOST to finish this go command + // + SendAckPacket (DEBUG_COMMAND_OK); + CpuPause (); + // + // Set current view to the next breaking processor + // + mDebugMpContext.ViewPointIndex = Data32; + mDebugMpContext.BreakAtCpuIndex = mDebugMpContext.ViewPointIndex; + SetCpuBreakFlagByIndex (mDebugMpContext.ViewPointIndex, FALSE); + // + // Send break packet to HOST to let HOST break again + // + SendBreakPacketToHost (DEBUG_DATA_BREAK_CAUSE_UNKNOWN, mDebugMpContext.BreakAtCpuIndex, &BreakReceived); + // + // Continue to run into loop to read command packet from HOST + // + ReleaseMpSpinLock (&mDebugMpContext.DebugPortSpinLock); + break; + } + + // + // If no else processor break, set stop bitmask, + // and set Running flag for all processors. + // + SetCpuStopFlagByIndex (ProcessorIndex, FALSE); + SetCpuRunningFlag (TRUE); + CpuPause (); + // + // Wait for all processors are in running state + // + while (TRUE) { + if (IsAllCpuRunning ()) { + break; + } + } + // + // Set BSP to be current view point. + // + SetDebugViewPoint (mDebugMpContext.BspIndex); + CpuPause (); + // + // Clear breaking processor index and running flag + // + mDebugMpContext.BreakAtCpuIndex = (UINT32) (-1); + SetCpuRunningFlag (FALSE); + } + + // + // Send OK packet to HOST to finish this go command + // + SendAckPacket (DEBUG_COMMAND_OK); + + ReleaseMpSpinLock (&mDebugMpContext.DebugPortSpinLock); + + if (!IsHostAttached()) { + UpdateMailboxContent (Mailbox, DEBUG_MAILBOX_SEQUENCE_NO_INDEX, 0); + UpdateMailboxContent (Mailbox, DEBUG_MAILBOX_HOST_SEQUENCE_NO_INDEX, 0); + } + return; + + } else { + // + // If reveived HALT command, need to defer the GO command + // + SendAckPacket (DEBUG_COMMAND_HALT_PROCESSED); + HaltDeferred = FALSE; + + Vector = DEBUG_TIMER_VECTOR; + } + break; + + case DEBUG_COMMAND_BREAK_CAUSE: + BreakCause.StopAddress = CpuContext->Eip; + if (MultiProcessorDebugSupport() && ProcessorIndex != mDebugMpContext.BreakAtCpuIndex) { + BreakCause.Cause = GetBreakCause (DEBUG_TIMER_VECTOR, CpuContext); + } else { + BreakCause.Cause = GetBreakCause (Vector, CpuContext); + } + SendDataResponsePacket ((UINT8 *) &BreakCause, (UINT16) sizeof (DEBUG_DATA_RESPONSE_BREAK_CAUSE), DebugHeader); + break; + + case DEBUG_COMMAND_SET_HW_BREAKPOINT: + SetDebugRegister (CpuContext, (DEBUG_DATA_SET_HW_BREAKPOINT *) (DebugHeader + 1)); + SendAckPacket (DEBUG_COMMAND_OK); + break; + + case DEBUG_COMMAND_CLEAR_HW_BREAKPOINT: + ClearDebugRegister (CpuContext, (DEBUG_DATA_CLEAR_HW_BREAKPOINT *) (DebugHeader + 1)); + SendAckPacket (DEBUG_COMMAND_OK); + break; + + case DEBUG_COMMAND_SINGLE_STEPPING: + CommandStepping (CpuContext); + // + // Clear Dr0 to avoid to be recognized as IMAGE_LOAD/_UNLOAD again when hitting a breakpoint after GO + // If HOST changed Dr0 before GO, we will not change Dr0 here + // + Data8 = GetBreakCause (Vector, CpuContext); + if (Data8 == DEBUG_DATA_BREAK_CAUSE_IMAGE_LOAD || Data8 == DEBUG_DATA_BREAK_CAUSE_IMAGE_UNLOAD) { + CpuContext->Dr0 = 0; + } + + mDebugMpContext.BreakAtCpuIndex = (UINT32) (-1); + ReleaseMpSpinLock (&mDebugMpContext.DebugPortSpinLock); + // + // Executing stepping command directly without sending ACK packet, + // ACK packet will be sent after stepping done. + // + return; + + case DEBUG_COMMAND_SET_SW_BREAKPOINT: + Data64 = (UINTN) (((DEBUG_DATA_SET_SW_BREAKPOINT *) (DebugHeader + 1))->Address); + Data8 = *(UINT8 *) (UINTN) Data64; + *(UINT8 *) (UINTN) Data64 = DEBUG_SW_BREAKPOINT_SYMBOL; + Status = SendDataResponsePacket ((UINT8 *) &Data8, (UINT16) sizeof (UINT8), DebugHeader); + break; + + case DEBUG_COMMAND_READ_MEMORY: + MemoryRead = (DEBUG_DATA_READ_MEMORY *) (DebugHeader + 1); + Status = ReadMemoryAndSendResponsePacket ((UINT8 *) (UINTN) MemoryRead->Address, MemoryRead->Count, MemoryRead->Width, DebugHeader); + break; + + case DEBUG_COMMAND_WRITE_MEMORY: + MemoryWrite = (DEBUG_DATA_WRITE_MEMORY *) (DebugHeader + 1); + // + // Copy data into one memory with 8-byte alignment address + // + AlignedDataPtr = ALIGN_POINTER ((UINT8 *) &MemoryWrite->Data, sizeof (UINT64)); + if (AlignedDataPtr != (UINT8 *) &MemoryWrite->Data) { + CopyMem (AlignedDataPtr, (UINT8 *) &MemoryWrite->Data, MemoryWrite->Count * MemoryWrite->Width); + } + CopyMemByWidth ((UINT8 *) (UINTN) MemoryWrite->Address, AlignedDataPtr, MemoryWrite->Count, MemoryWrite->Width); + SendAckPacket (DEBUG_COMMAND_OK); + break; + + case DEBUG_COMMAND_READ_IO: + IoRead = (DEBUG_DATA_READ_IO *) (DebugHeader + 1); + switch (IoRead->Width) { + case 1: + Data64 = IoRead8 ((UINTN) IoRead->Port); + break; + case 2: + Data64 = IoRead16 ((UINTN) IoRead->Port); + break; + case 4: + Data64 = IoRead32 ((UINTN) IoRead->Port); + break; + case 8: + Data64 = IoRead64 ((UINTN) IoRead->Port); + break; + default: + Data64 = (UINT64) -1; + } + Status = SendDataResponsePacket ((UINT8 *) &Data64, IoRead->Width, DebugHeader); + break; + + case DEBUG_COMMAND_WRITE_IO: + IoWrite = (DEBUG_DATA_WRITE_IO *) (DebugHeader + 1); + switch (IoWrite->Width) { + case 1: + Data64 = IoWrite8 ((UINTN) IoWrite->Port, *(UINT8 *) &IoWrite->Data); + break; + case 2: + Data64 = IoWrite16 ((UINTN) IoWrite->Port, *(UINT16 *) &IoWrite->Data); + break; + case 4: + Data64 = IoWrite32 ((UINTN) IoWrite->Port, *(UINT32 *) &IoWrite->Data); + break; + case 8: + Data64 = IoWrite64 ((UINTN) IoWrite->Port, *(UINT64 *) &IoWrite->Data); + break; + default: + Data64 = (UINT64) -1; + } + SendAckPacket (DEBUG_COMMAND_OK); + break; + + case DEBUG_COMMAND_READ_ALL_REGISTERS: + Status = SendDataResponsePacket ((UINT8 *) CpuContext, sizeof (*CpuContext), DebugHeader); + break; + + case DEBUG_COMMAND_READ_REGISTER: + RegisterRead = (DEBUG_DATA_READ_REGISTER *) (DebugHeader + 1); + + if (RegisterRead->Index <= SOFT_DEBUGGER_REGISTER_MAX) { + RegisterBuffer = ArchReadRegisterBuffer (CpuContext, RegisterRead->Index, &Width); + Status = SendDataResponsePacket (RegisterBuffer, Width, DebugHeader); + } else { + Status = RETURN_UNSUPPORTED; + } + break; + + case DEBUG_COMMAND_WRITE_REGISTER: + RegisterWrite = (DEBUG_DATA_WRITE_REGISTER *) (DebugHeader + 1); + if (RegisterWrite->Index <= SOFT_DEBUGGER_REGISTER_MAX) { + RegisterBuffer = ArchReadRegisterBuffer (CpuContext, RegisterWrite->Index, &Width); + ASSERT (Width == RegisterWrite->Length); + CopyMem (RegisterBuffer, RegisterWrite->Data, Width); + SendAckPacket (DEBUG_COMMAND_OK); + } else { + Status = RETURN_UNSUPPORTED; + } + break; + + case DEBUG_COMMAND_ARCH_MODE: + Data8 = DEBUG_ARCH_SYMBOL; + Status = SendDataResponsePacket ((UINT8 *) &Data8, (UINT16) sizeof (UINT8), DebugHeader); + break; + + case DEBUG_COMMAND_READ_MSR: + MsrRegisterRead = (DEBUG_DATA_READ_MSR *) (DebugHeader + 1); + Data64 = AsmReadMsr64 (MsrRegisterRead->Index); + Status = SendDataResponsePacket ((UINT8 *) &Data64, (UINT16) sizeof (UINT64), DebugHeader); + break; + + case DEBUG_COMMAND_WRITE_MSR: + MsrRegisterWrite = (DEBUG_DATA_WRITE_MSR *) (DebugHeader + 1); + AsmWriteMsr64 (MsrRegisterWrite->Index, MsrRegisterWrite->Value); + SendAckPacket (DEBUG_COMMAND_OK); + break; + + case DEBUG_COMMAND_SET_DEBUG_SETTING: + Status = SetDebugSetting ((DEBUG_DATA_SET_DEBUG_SETTING *)(DebugHeader + 1)); + if (Status == RETURN_SUCCESS) { + SendAckPacket (DEBUG_COMMAND_OK); + } + break; + + case DEBUG_COMMAND_GET_REVISION: + DebugAgentRevision.Revision = DEBUG_AGENT_REVISION; + DebugAgentRevision.Capabilities = DEBUG_AGENT_CAPABILITIES; + Status = SendDataResponsePacket ((UINT8 *) &DebugAgentRevision, (UINT16) sizeof (DEBUG_DATA_RESPONSE_GET_REVISION), DebugHeader); + break; + + case DEBUG_COMMAND_GET_EXCEPTION: + Exception.ExceptionNum = (UINT8) Vector; + Exception.ExceptionData = (UINT32) CpuContext->ExceptionData; + Status = SendDataResponsePacket ((UINT8 *) &Exception, (UINT16) sizeof (DEBUG_DATA_RESPONSE_GET_EXCEPTION), DebugHeader); + break; + + case DEBUG_COMMAND_SET_VIEWPOINT: + SetViewPoint = (DEBUG_DATA_SET_VIEWPOINT *) (DebugHeader + 1); + if (MultiProcessorDebugSupport()) { + if (IsCpuStopped (SetViewPoint->ViewPoint)) { + SetDebugViewPoint (SetViewPoint->ViewPoint); + SendAckPacket (DEBUG_COMMAND_OK); + } else { + // + // If CPU is not halted + // + SendAckPacket (DEBUG_COMMAND_NOT_SUPPORTED); + } + } else if (SetViewPoint->ViewPoint == 0) { + SendAckPacket (DEBUG_COMMAND_OK); + + } else { + SendAckPacket (DEBUG_COMMAND_NOT_SUPPORTED); + } + + break; + + case DEBUG_COMMAND_GET_VIEWPOINT: + Data32 = mDebugMpContext.ViewPointIndex; + SendDataResponsePacket((UINT8 *) &Data32, (UINT16) sizeof (UINT32), DebugHeader); + break; + + case DEBUG_COMMAND_MEMORY_READY: + Data8 = (UINT8) GetDebugFlag (DEBUG_AGENT_FLAG_MEMORY_READY); + SendDataResponsePacket (&Data8, (UINT16) sizeof (UINT8), DebugHeader); + break; + + case DEBUG_COMMAND_DETACH: + SetHostAttached (FALSE); + SendAckPacket (DEBUG_COMMAND_OK); + break; + + case DEBUG_COMMAND_CPUID: + Cpuid = (DEBUG_DATA_CPUID *) (DebugHeader + 1); + AsmCpuidEx ( + Cpuid->Eax, Cpuid->Ecx, + &CpuidResponse.Eax, &CpuidResponse.Ebx, + &CpuidResponse.Ecx, &CpuidResponse.Edx + ); + SendDataResponsePacket ((UINT8 *) &CpuidResponse, (UINT16) sizeof (CpuidResponse), DebugHeader); + break; + + case DEBUG_COMMAND_SEARCH_SIGNATURE: + SearchSignature = (DEBUG_DATA_SEARCH_SIGNATURE *) (DebugHeader + 1); + if ((SearchSignature->Alignment != 0) && + (SearchSignature->Alignment == GetPowerOfTwo32 (SearchSignature->Alignment)) + ) { + if (SearchSignature->Positive) { + for ( + Data64 = ALIGN_VALUE ((UINTN) SearchSignature->Start, SearchSignature->Alignment); + Data64 <= SearchSignature->Start + SearchSignature->Count - SearchSignature->DataLength; + Data64 += SearchSignature->Alignment + ) { + if (CompareMem ((VOID *) (UINTN) Data64, &SearchSignature->Data, SearchSignature->DataLength) == 0) { + break; + } + } + if (Data64 > SearchSignature->Start + SearchSignature->Count - SearchSignature->DataLength) { + Data64 = (UINT64) -1; + } + } else { + for ( + Data64 = ALIGN_VALUE ((UINTN) SearchSignature->Start - SearchSignature->Alignment, SearchSignature->Alignment); + Data64 >= SearchSignature->Start - SearchSignature->Count; + Data64 -= SearchSignature->Alignment + ) { + if (CompareMem ((VOID *) (UINTN) Data64, &SearchSignature->Data, SearchSignature->DataLength) == 0) { + break; + } + } + if (Data64 < SearchSignature->Start - SearchSignature->Count) { + Data64 = (UINT64) -1; + } + } + SendDataResponsePacket ((UINT8 *) &Data64, (UINT16) sizeof (Data64), DebugHeader); + } else { + Status = RETURN_UNSUPPORTED; + } + break; + + default: + SendAckPacket (DEBUG_COMMAND_NOT_SUPPORTED); + break; + } + + if (Status == RETURN_UNSUPPORTED) { + SendAckPacket (DEBUG_COMMAND_NOT_SUPPORTED); + } else if (Status != RETURN_SUCCESS) { + SendAckPacket (DEBUG_COMMAND_ABORT); + } + + ReleaseMpSpinLock (&mDebugMpContext.DebugPortSpinLock); + CpuPause (); + } +} + +/** + C function called in interrupt handler. + + @param[in] Vector Vector value of exception or interrutp. + @param[in] CpuContext Pointer to save CPU context. + +**/ +VOID +EFIAPI +InterruptProcess ( + IN UINT32 Vector, + IN DEBUG_CPU_CONTEXT *CpuContext + ) +{ + UINT8 InputCharacter; + UINT8 BreakCause; + UINTN SavedEip; + BOOLEAN BreakReceived; + UINT32 ProcessorIndex; + UINT32 CurrentDebugTimerInitCount; + DEBUG_PORT_HANDLE Handle; + UINT8 Data8; + UINT8 *Al; + UINT32 IssuedViewPoint; + DEBUG_AGENT_EXCEPTION_BUFFER *ExceptionBuffer; + + InputCharacter = 0; + ProcessorIndex = 0; + IssuedViewPoint = 0; + BreakReceived = FALSE; + + if (mSkipBreakpoint) { + // + // If Skip Breakpoint flag is set, means communication is disturbed by hardware SMI, we need to ignore the break points in SMM + // + if ((Vector == DEBUG_INT1_VECTOR) || (Vector == DEBUG_INT3_VECTOR)) { + DebugPortWriteBuffer (GetDebugPortHandle(), (UINT8 *) mWarningMsgIngoreBreakpoint, AsciiStrLen (mWarningMsgIngoreBreakpoint)); + return; + } + } + + if (MultiProcessorDebugSupport()) { + ProcessorIndex = GetProcessorIndex (); + // + // If this processor has alreay halted before, need to check it later + // + if (IsCpuStopped (ProcessorIndex)) { + IssuedViewPoint = ProcessorIndex; + } + } + + if (IssuedViewPoint == ProcessorIndex && GetDebugFlag (DEBUG_AGENT_FLAG_STEPPING) != 1) { + // + // Check if this exception is issued by Debug Agent itself + // If yes, fill the debug agent exception buffer and LongJump() back to + // the saved CPU content in CommandCommunication() + // If exception is issued when executing Stepping, will be handled in + // exception handle procedure. + // + if (GetDebugFlag (DEBUG_AGENT_FLAG_AGENT_IN_PROGRESS) == 1) { + DebugAgentMsgPrint ( + DEBUG_AGENT_ERROR, + "Debug agent meet one Exception, ExceptionNum is %d, EIP = 0x%x.\n", + Vector, + (UINTN)CpuContext->Eip + ); + ExceptionBuffer = (DEBUG_AGENT_EXCEPTION_BUFFER *) (UINTN) GetMailboxPointer()->ExceptionBufferPointer; + ExceptionBuffer->ExceptionContent.ExceptionNum = (UINT8) Vector; + ExceptionBuffer->ExceptionContent.ExceptionData = (UINT32) CpuContext->ExceptionData; + LongJump ((BASE_LIBRARY_JUMP_BUFFER *)(UINTN)(ExceptionBuffer), 1); + } + } + + if (MultiProcessorDebugSupport()) { + // + // If RUN commmand is executing, wait for it done. + // + while (mDebugMpContext.RunCommandSet) { + CpuPause (); + } + } + + Handle = GetDebugPortHandle(); + BreakCause = GetBreakCause (Vector, CpuContext); + switch (Vector) { + case DEBUG_INT1_VECTOR: + case DEBUG_INT3_VECTOR: + switch (BreakCause) { + case DEBUG_DATA_BREAK_CAUSE_SYSTEM_RESET: + if (AttachHost (BreakCause, READ_PACKET_TIMEOUT, &BreakReceived) != RETURN_SUCCESS) { + // + // Try to connect HOST, return if fails + // + break; + } + CommandCommunication (Vector, CpuContext, BreakReceived); + break; + + case DEBUG_DATA_BREAK_CAUSE_STEPPING: + // + // Stepping is finished, send Ack package. + // + if (MultiProcessorDebugSupport()) { + mDebugMpContext.BreakAtCpuIndex = ProcessorIndex; + } + // + // Clear Stepping Flag and restore EFLAGS.IF + // + CommandSteppingCleanup (CpuContext); + SendAckPacket (DEBUG_COMMAND_OK); + CommandCommunication (Vector, CpuContext, BreakReceived); + break; + + case DEBUG_DATA_BREAK_CAUSE_MEMORY_READY: + // + // Memory is ready + // + SendCommandAndWaitForAckOK (DEBUG_COMMAND_MEMORY_READY, READ_PACKET_TIMEOUT, &BreakReceived, NULL); + CommandCommunication (Vector, CpuContext, BreakReceived); + break; + + case DEBUG_DATA_BREAK_CAUSE_IMAGE_LOAD: + case DEBUG_DATA_BREAK_CAUSE_IMAGE_UNLOAD: + // + // Set AL to DEBUG_AGENT_IMAGE_CONTINUE + // + Al = ArchReadRegisterBuffer (CpuContext, SOFT_DEBUGGER_REGISTER_AX, &Data8); + *Al = DEBUG_AGENT_IMAGE_CONTINUE; + + if (!IsHostAttached ()) { + // + // If HOST is not connected for image load/unload, return + // + break; + } + // + // Continue to run the following common code + // + + case DEBUG_DATA_BREAK_CAUSE_HW_BREAKPOINT: + case DEBUG_DATA_BREAK_CAUSE_SW_BREAKPOINT: + default: + // + // Send Break packet to HOST + // + AcquireMpSpinLock (&mDebugMpContext.DebugPortSpinLock); + // + // Only the first breaking processor could send BREAK_POINT to HOST + // + if (IsFirstBreakProcessor (ProcessorIndex)) { + SendBreakPacketToHost (BreakCause, ProcessorIndex, &BreakReceived); + } + ReleaseMpSpinLock (&mDebugMpContext.DebugPortSpinLock); + + if (Vector == DEBUG_INT3_VECTOR) { + // + // go back address located "0xCC" + // + CpuContext->Eip--; + SavedEip = CpuContext->Eip; + CommandCommunication (Vector, CpuContext, BreakReceived); + if ((SavedEip == CpuContext->Eip) && + (*(UINT8 *) (UINTN) CpuContext->Eip == DEBUG_SW_BREAKPOINT_SYMBOL)) { + // + // If this is not a software breakpoint set by HOST, + // restore EIP + // + CpuContext->Eip++; + } + } else { + CommandCommunication (Vector, CpuContext, BreakReceived); + } + break; + } + + break; + + case DEBUG_TIMER_VECTOR: + + AcquireMpSpinLock (&mDebugMpContext.DebugPortSpinLock); + + if (MultiProcessorDebugSupport()) { + if (IsBsp (ProcessorIndex)) { + // + // If current processor is BSP, check Apic timer's init count if changed, + // it may be re-written when switching BSP. + // If it changed, re-initialize debug timer + // + CurrentDebugTimerInitCount = GetApicTimerInitCount (); + if (mDebugMpContext.DebugTimerInitCount != CurrentDebugTimerInitCount) { + InitializeDebugTimer (NULL, FALSE); + SaveAndSetDebugTimerInterrupt (TRUE); + } + } + + if (!IsBsp (ProcessorIndex) || mDebugMpContext.IpiSentByAp) { + ReleaseMpSpinLock (&mDebugMpContext.DebugPortSpinLock); + // + // If current processor is not BSP or this is one IPI sent by AP + // + if (mDebugMpContext.BreakAtCpuIndex != (UINT32) (-1)) { + CommandCommunication (Vector, CpuContext, FALSE); + } + + // + // Clear EOI before exiting interrupt process routine. + // + SendApicEoi (); + break; + } + } + + // + // Only BSP could run here + // + while (TRUE) { + // + // If there is data in debug port, will check whether it is break(attach/break-in) symbol, + // If yes, go into communication mode with HOST. + // If no, exit interrupt process. + // + if (DebugReadBreakSymbol (Handle, &InputCharacter) == EFI_NOT_FOUND) { + break; + } + + if ((!IsHostAttached () && (InputCharacter == DEBUG_STARTING_SYMBOL_ATTACH)) || + (IsHostAttached () && (InputCharacter == DEBUG_COMMAND_HALT)) || + (IsHostAttached () && (InputCharacter == DEBUG_COMMAND_GO)) + ) { + DebugAgentMsgPrint (DEBUG_AGENT_VERBOSE, "Received data [%02x]\n", InputCharacter); + // + // Ack OK for break-in symbol + // + SendAckPacket (DEBUG_COMMAND_OK); + + // + // If receive GO command in Debug Timer, means HOST may lost ACK packet before. + // + if (InputCharacter == DEBUG_COMMAND_GO) { + break; + } + + if (!IsHostAttached ()) { + // + // Try to attach HOST, if no ack received after 200ms, return + // + if (AttachHost (BreakCause, READ_PACKET_TIMEOUT, &BreakReceived) != RETURN_SUCCESS) { + break; + } + } + + if (MultiProcessorDebugSupport()) { + if(FindNextPendingBreakCpu () != -1) { + SetCpuBreakFlagByIndex (ProcessorIndex, TRUE); + } else { + HaltOtherProcessors (ProcessorIndex); + } + } + ReleaseMpSpinLock (&mDebugMpContext.DebugPortSpinLock); + CommandCommunication (Vector, CpuContext, BreakReceived); + AcquireMpSpinLock (&mDebugMpContext.DebugPortSpinLock); + break; + } + } + + // + // Clear EOI before exiting interrupt process routine. + // + SendApicEoi (); + + ReleaseMpSpinLock (&mDebugMpContext.DebugPortSpinLock); + + break; + + default: + if (Vector <= DEBUG_EXCEPT_SIMD) { + DebugAgentMsgPrint ( + DEBUG_AGENT_ERROR, + "Exception happened, ExceptionNum is %d, EIP = 0x%x.\n", + Vector, + (UINTN) CpuContext->Eip + ); + if (BreakCause == DEBUG_DATA_BREAK_CAUSE_STEPPING) { + // + // If exception happened when executing Stepping, send Ack package. + // HOST consider Stepping command was finished. + // + if (MultiProcessorDebugSupport()) { + mDebugMpContext.BreakAtCpuIndex = ProcessorIndex; + } + // + // Clear Stepping flag and restore EFLAGS.IF + // + CommandSteppingCleanup (CpuContext); + SendAckPacket (DEBUG_COMMAND_OK); + } else { + // + // Exception occurs, send Break packet to HOST + // + AcquireMpSpinLock (&mDebugMpContext.DebugPortSpinLock); + // + // Only the first breaking processor could send BREAK_POINT to HOST + // + if (IsFirstBreakProcessor (ProcessorIndex)) { + SendBreakPacketToHost (BreakCause, ProcessorIndex, &BreakReceived); + } + ReleaseMpSpinLock (&mDebugMpContext.DebugPortSpinLock); + } + + CommandCommunication (Vector, CpuContext, BreakReceived); + } + break; + } + + if (MultiProcessorDebugSupport()) { + // + // Clear flag and wait for all processors run here + // + SetIpiSentByApFlag (FALSE); + while (mDebugMpContext.RunCommandSet) { + CpuPause (); + } + + // + // Only current (view) processor could clean up AgentInProgress flag. + // + if (mDebugMpContext.ViewPointIndex == ProcessorIndex) { + IssuedViewPoint = mDebugMpContext.ViewPointIndex; + } + } + + if (IssuedViewPoint == ProcessorIndex && GetDebugFlag (DEBUG_AGENT_FLAG_STEPPING) != 1) { + // + // If the command is not stepping, clean up AgentInProgress flag + // + SetDebugFlag (DEBUG_AGENT_FLAG_AGENT_IN_PROGRESS, 0); + } + + return; +} + diff --git a/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/DebugAgent.h b/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/DebugAgent.h new file mode 100644 index 0000000000..e09ead9cf0 --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/DebugAgent.h @@ -0,0 +1,504 @@ +/** @file + Command header of for Debug Agent library instance. + + Copyright (c) 2010 - 2016, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _DEBUG_AGENT_H_ +#define _DEBUG_AGENT_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "DebugMp.h" +#include "DebugTimer.h" +#include "ArchDebugSupport.h" +#include "DebugException.h" + +// +// These macros may be already defined in DebugAgentLib.h +// +#define DEBUG_AGENT_INIT_PEI 9 +#define DEBUG_AGENT_INIT_DXE_LOAD 10 +#define DEBUG_AGENT_INIT_DXE_UNLOAD 11 +#define DEBUG_AGENT_INIT_THUNK_PEI_IA32TOX64 12 + +#define DEBUG_INT1_VECTOR DEBUG_EXCEPT_DEBUG +#define DEBUG_INT3_VECTOR DEBUG_EXCEPT_BREAKPOINT +#define DEBUG_TIMER_VECTOR 32 +#define DEBUG_MAILBOX_VECTOR 33 + +// +// Timeout value for reading packet (unit is microsecond) +// +#define READ_PACKET_TIMEOUT (500 * 1000) +#define DEBUG_TIMER_INTERVAL (100 * 1000) + +#define SOFT_INTERRUPT_SIGNATURE SIGNATURE_32('S','O','F','T') +#define SYSTEM_RESET_SIGNATURE SIGNATURE_32('S','Y','S','R') +#define MEMORY_READY_SIGNATURE SIGNATURE_32('M','E','M','R') + +extern UINTN Exception0Handle; +extern UINTN TimerInterruptHandle; +extern UINT32 ExceptionStubHeaderSize; +extern BOOLEAN mSkipBreakpoint; +extern EFI_VECTOR_HANDOFF_INFO mVectorHandoffInfoDebugAgent[]; +extern UINTN mVectorHandoffInfoCount; + +// +// CPU exception information issued by debug agent +// +typedef struct { + // + // This field is used to save CPU content before executing HOST command + // + BASE_LIBRARY_JUMP_BUFFER JumpBuffer; + // + // This field returns the exception information issued by the HOST command + // + DEBUG_DATA_RESPONSE_GET_EXCEPTION ExceptionContent; +} DEBUG_AGENT_EXCEPTION_BUFFER; + +#define DEBUG_AGENT_FLAG_HOST_ATTACHED BIT0 +#define DEBUG_AGENT_FLAG_AGENT_IN_PROGRESS BIT1 +#define DEBUG_AGENT_FLAG_MEMORY_READY BIT2 +#define DEBUG_AGENT_FLAG_STEPPING BIT3 +#define DEBUG_AGENT_FLAG_CHECK_MAILBOX_IN_HOB BIT4 +#define DEBUG_AGENT_FLAG_INIT_ARCH BIT5|BIT6 +#define DEBUG_AGENT_FLAG_INTERRUPT_FLAG BIT7 +#define DEBUG_AGENT_FLAG_BREAK_ON_NEXT_SMI BIT32 +#define DEBUG_AGENT_FLAG_PRINT_ERROR_LEVEL (BIT33|BIT34|BIT35|BIT36) +#define DEBUG_AGENT_FLAG_BREAK_BOOT_SCRIPT BIT37 + +#define DEBUG_MAILBOX_DEBUG_FLAG_INDEX 1 +#define DEBUG_MAILBOX_DEBUG_PORT_HANDLE_INDEX 2 +#define DEBUG_MAILBOX_EXCEPTION_BUFFER_POINTER_INDEX 3 +#define DEBUG_MAILBOX_LAST_ACK 4 +#define DEBUG_MAILBOX_SEQUENCE_NO_INDEX 5 +#define DEBUG_MAILBOX_HOST_SEQUENCE_NO_INDEX 6 +#define DEBUG_MAILBOX_DEBUG_TIMER_FREQUENCY 7 + +#pragma pack(1) +typedef union { + struct { + // + // Lower 32 bits to store the status of DebugAgent + // + UINT32 HostAttached : 1; // 1: HOST is attached + UINT32 AgentInProgress : 1; // 1: Debug Agent is communicating with HOST + UINT32 MemoryReady : 1; // 1: Memory is ready + UINT32 SteppingFlag : 1; // 1: Agent is running stepping command + UINT32 CheckMailboxInHob : 1; // 1: Need to check mailbox saved in HOB + UINT32 InitArch : 2; // value of DEBUG_DATA_RESPONSE_ARCH_MODE + UINT32 InterruptFlag : 1; // 1: EFLAGS.IF is set + UINT32 Reserved1 : 24; + // + // Higher 32bits to control the behavior of DebugAgent + // + UINT32 BreakOnNextSmi : 1; // 1: Break on next SMI + UINT32 PrintErrorLevel : 4; // Bitmask of print error level for debug message + UINT32 BreakOnBootScript : 1; // 1: Break before executing boot script + UINT32 Reserved2 : 26; + } Bits; + UINT64 Uint64; +} DEBUG_AGENT_FLAG; + +typedef struct { + DEBUG_AGENT_FLAG DebugFlag; + UINT64 DebugPortHandle; + // + // Pointer to DEBUG_AGENT_EXCEPTION_BUFFER + // + UINT64 ExceptionBufferPointer; + UINT8 LastAck; // The last ack packet type + UINT8 SequenceNo; + UINT8 HostSequenceNo; + UINT32 DebugTimerFrequency; + UINT8 CheckSum; // Mailbox checksum + UINT8 ToBeCheckSum; // To be Mailbox checksum at the next +} DEBUG_AGENT_MAILBOX; +#pragma pack() + +/// +/// Byte packed structure for an IA-32 Interrupt Gate Descriptor. +/// +typedef union { + struct { + UINT32 OffsetLow:16; ///< Offset bits 15..0. + UINT32 Selector:16; ///< Selector. + UINT32 Reserved_0:8; ///< Reserved. + UINT32 GateType:8; ///< Gate Type. See #defines above. + UINT32 OffsetHigh:16; ///< Offset bits 31..16. + } Bits; + UINT64 Uint64; +} IA32_IDT_ENTRY; + + +typedef union { + struct { + UINT32 LimitLow : 16; + UINT32 BaseLow : 16; + UINT32 BaseMid : 8; + UINT32 Type : 4; + UINT32 System : 1; + UINT32 Dpl : 2; + UINT32 Present : 1; + UINT32 LimitHigh : 4; + UINT32 Software : 1; + UINT32 Reserved : 1; + UINT32 DefaultSize : 1; + UINT32 Granularity : 1; + UINT32 BaseHigh : 8; + } Bits; + UINT64 Uint64; +} IA32_GDT; + +/** + Initialize IDT entries to support source level debug. + +**/ +VOID +InitializeDebugIdt ( + VOID + ); + +/** + Read register value from saved CPU context. + + @param[in] CpuContext Pointer to saved CPU context. + @param[in] Index Register index value. + @param[in] Width Data width to read. + + @return The address of register value. + +**/ +UINT8 * +ArchReadRegisterBuffer ( + IN DEBUG_CPU_CONTEXT *CpuContext, + IN UINT8 Index, + IN UINT8 *Width + ); + +/** + 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. + @retval RETURN_DEVICE_ERROR Cannot receive DEBUG_COMMAND_OK from HOST. + +**/ +RETURN_STATUS +SendDataResponsePacket ( + IN UINT8 *Data, + IN UINT16 DataSize, + IN OUT DEBUG_PACKET_HEADER *DebugHeader + ); + +/** + Check if HOST is attached based on Mailbox. + + @retval TRUE HOST is attached. + @retval FALSE HOST is not attached. + +**/ +BOOLEAN +IsHostAttached ( + VOID + ); + +/** + Get Debug Agent Mailbox pointer. + + @return Mailbox pointer. + +**/ +DEBUG_AGENT_MAILBOX * +GetMailboxPointer ( + VOID + ); + +/** + Get debug port handle. + + @return Debug port handle. + +**/ +DEBUG_PORT_HANDLE +GetDebugPortHandle ( + VOID + ); + +/** + Read the Attach/Break-in symbols from the debug port. + + @param[in] Handle Pointer to Debug Port handle. + @param[out] BreakSymbol Returned break symbol. + + @retval EFI_SUCCESS Read the symbol in BreakSymbol. + @retval EFI_NOT_FOUND No read the break symbol. + +**/ +EFI_STATUS +DebugReadBreakSymbol ( + IN DEBUG_PORT_HANDLE Handle, + OUT UINT8 *BreakSymbol + ); + +/** + 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, + ... + ); + +/** + Trigger one software interrupt to debug agent to handle it. + + @param[in] Signature Software interrupt signature. + +**/ +VOID +TriggerSoftInterrupt ( + IN UINT32 Signature + ); + +/** + Check if debug agent support multi-processor. + + @retval TRUE Multi-processor is supported. + @retval FALSE Multi-processor is not supported. + +**/ +BOOLEAN +MultiProcessorDebugSupport ( + VOID + ); + +/** + Find and report module image info to HOST. + + @param[in] AlignSize Image aligned size. + +**/ +VOID +FindAndReportModuleImageInfo ( + IN UINTN AlignSize + ); + +/** + 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 + ); + +/** + Calculate Mailbox checksum and update the checksum field. + + @param[in] Mailbox Debug Agent Mailbox pointer. + +**/ +VOID +UpdateMailboxChecksum ( + IN DEBUG_AGENT_MAILBOX *Mailbox + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + Get debug flag in mailbox. + + @param[in] FlagMask Debug flag mask value. + + @return Debug flag value. + +**/ +UINT32 +GetDebugFlag ( + IN UINT64 FlagMask + ); + +/** + 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 mail box. + +**/ +VOID +UpdateMailboxContent ( + IN DEBUG_AGENT_MAILBOX *Mailbox, + IN UINTN Index, + IN UINT64 Value + ); + +/** + Retrieve exception handler from IDT table by ExceptionNum. + + @param[in] ExceptionNum Exception number + + @return Exception handler + +**/ +VOID * +GetExceptionHandlerInIdtEntry ( + IN UINTN ExceptionNum + ); + +/** + Set exception handler in IDT table by ExceptionNum. + + @param[in] ExceptionNum Exception number + @param[in] ExceptionHandler Exception Handler to be set + +**/ +VOID +SetExceptionHandlerInIdtEntry ( + IN UINTN ExceptionNum, + IN VOID *ExceptionHandler + ); + +/** + 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 + ); + +/** + 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. + +**/ +EFI_STATUS +ReadRemainingBreakPacket ( + IN DEBUG_PORT_HANDLE Handle, + IN OUT DEBUG_PACKET_HEADER *DebugHeader + ); + +/** + Read data from debug channel 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 OUT UINT8 *Buffer, + IN UINTN NumberOfBytes, + IN UINTN Timeout + ); + +#endif + diff --git a/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/DebugMp.c b/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/DebugMp.c new file mode 100644 index 0000000000..d49d4ee323 --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/DebugMp.c @@ -0,0 +1,383 @@ +/** @file + Multi-Processor support functions implementation. + + Copyright (c) 2010 - 2016, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "DebugAgent.h" + +GLOBAL_REMOVE_IF_UNREFERENCED DEBUG_MP_CONTEXT volatile mDebugMpContext = {0,0,0,{0},{0},0,0,0,0,FALSE,FALSE}; + +GLOBAL_REMOVE_IF_UNREFERENCED DEBUG_CPU_DATA volatile mDebugCpuData = {0}; + +/** + Acquire a spin lock when Multi-processor supported. + + It will block in the function if cannot get the access control. + If Multi-processor is not supported, return directly. + + @param[in, out] MpSpinLock A pointer to the spin lock. + +**/ +VOID +AcquireMpSpinLock ( + IN OUT SPIN_LOCK *MpSpinLock + ) +{ + if (!MultiProcessorDebugSupport()) { + return; + } + + while (TRUE) { + if (AcquireSpinLockOrFail (MpSpinLock)) { + break; + } + CpuPause (); + continue; + } +} + +/** + Release a spin lock when Multi-processor supported. + + @param[in, out] MpSpinLock A pointer to the spin lock. + +**/ +VOID +ReleaseMpSpinLock ( + IN OUT SPIN_LOCK *MpSpinLock + ) +{ + if (!MultiProcessorDebugSupport()) { + return; + } + + ReleaseSpinLock (MpSpinLock); +} + +/** + Break the other processor by send IPI. + + @param[in] CurrentProcessorIndex Current processor index value. + +**/ +VOID +HaltOtherProcessors ( + IN UINT32 CurrentProcessorIndex + ) +{ + DebugAgentMsgPrint (DEBUG_AGENT_INFO, "processor[%x]:Try to halt other processors.\n", CurrentProcessorIndex); + if (!IsBsp (CurrentProcessorIndex)) { + SetIpiSentByApFlag (TRUE);; + } + + mDebugMpContext.BreakAtCpuIndex = CurrentProcessorIndex; + + // + // Set the debug viewpoint to the current breaking CPU. + // + SetDebugViewPoint (CurrentProcessorIndex); + + // + // Send fixed IPI to other processors. + // + SendFixedIpiAllExcludingSelf (DEBUG_TIMER_VECTOR); + +} + +/** + Get the current processor's index. + + @return Processor index value. + +**/ +UINT32 +GetProcessorIndex ( + VOID + ) +{ + UINT32 Index; + UINT16 LocalApicID; + + LocalApicID = (UINT16) GetApicId (); + + AcquireMpSpinLock (&mDebugMpContext.MpContextSpinLock); + + for (Index = 0; Index < mDebugCpuData.CpuCount; Index ++) { + if (mDebugCpuData.ApicID[Index] == LocalApicID) { + break; + } + } + + if (Index == mDebugCpuData.CpuCount) { + mDebugCpuData.ApicID[Index] = LocalApicID; + mDebugCpuData.CpuCount ++ ; + } + + ReleaseMpSpinLock (&mDebugMpContext.MpContextSpinLock); + + return Index; +} + +/** + Check if the specified processor is BSP or not. + + @param[in] ProcessorIndex Processor index value. + + @retval TRUE It is BSP. + @retval FALSE It isn't BSP. + +**/ +BOOLEAN +IsBsp ( + IN UINT32 ProcessorIndex + ) +{ + MSR_IA32_APIC_BASE_REGISTER MsrApicBase; + + // + // If there are less than 2 CPUs detected, then the currently executing CPU + // must be the BSP. This avoids an access to an MSR that may not be supported + // on single core CPUs. + // + if (mDebugCpuData.CpuCount < 2) { + return TRUE; + } + + MsrApicBase.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE); + if (MsrApicBase.Bits.BSP == 1) { + if (mDebugMpContext.BspIndex != ProcessorIndex) { + AcquireMpSpinLock (&mDebugMpContext.MpContextSpinLock); + mDebugMpContext.BspIndex = ProcessorIndex; + ReleaseMpSpinLock (&mDebugMpContext.MpContextSpinLock); + } + return TRUE; + } else { + return FALSE; + } +} + +/** + Set processor stop flag bitmask in MP context. + + @param[in] ProcessorIndex Processor index value. + @param[in] StopFlag TRUE means set stop flag. + FALSE means clean break flag. + +**/ +VOID +SetCpuStopFlagByIndex ( + IN UINT32 ProcessorIndex, + IN BOOLEAN StopFlag + ) +{ + UINT8 Value; + UINTN Index; + + AcquireMpSpinLock (&mDebugMpContext.MpContextSpinLock); + + Value = mDebugMpContext.CpuStopStatusMask[ProcessorIndex / 8]; + Index = ProcessorIndex % 8; + if (StopFlag) { + Value = BitFieldWrite8 (Value, Index, Index, 1); + } else { + Value = BitFieldWrite8 (Value, Index, Index, 0); + } + mDebugMpContext.CpuStopStatusMask[ProcessorIndex / 8] = Value; + + ReleaseMpSpinLock (&mDebugMpContext.MpContextSpinLock); +} + +/** + Set processor break flag bitmask in MP context. + + @param[in] ProcessorIndex Processor index value. + @param[in] BreakFlag TRUE means set break flag. + FALSE means clean break flag. + +**/ +VOID +SetCpuBreakFlagByIndex ( + IN UINT32 ProcessorIndex, + IN BOOLEAN BreakFlag + ) +{ + UINT8 Value; + UINTN Index; + + AcquireMpSpinLock (&mDebugMpContext.MpContextSpinLock); + + Value = mDebugMpContext.CpuBreakMask[ProcessorIndex / 8]; + Index = ProcessorIndex % 8; + if (BreakFlag) { + Value = BitFieldWrite8 (Value, Index, Index, 1); + } else { + Value = BitFieldWrite8 (Value, Index, Index, 0); + } + mDebugMpContext.CpuBreakMask[ProcessorIndex / 8] = Value; + + ReleaseMpSpinLock (&mDebugMpContext.MpContextSpinLock); +} + +/** + Check if processor is stopped already. + + @param[in] ProcessorIndex Processor index value. + + @retval TRUE Processor is stopped already. + @retval TRUE Processor isn't stopped. + +**/ +BOOLEAN +IsCpuStopped ( + IN UINT32 ProcessorIndex + ) +{ + UINT8 CpuMask; + + CpuMask = (UINT8) (1 << (ProcessorIndex % 8)); + + if ((mDebugMpContext.CpuStopStatusMask[ProcessorIndex / 8] & CpuMask) != 0) { + return TRUE; + } else { + return FALSE; + } +} + +/** + Set the run command flag. + + @param[in] RunningFlag TRUE means run command flag is set. + FALSE means run command flag is cleared. + +**/ +VOID +SetCpuRunningFlag ( + IN BOOLEAN RunningFlag + ) +{ + AcquireMpSpinLock (&mDebugMpContext.MpContextSpinLock); + mDebugMpContext.RunCommandSet = RunningFlag; + ReleaseMpSpinLock (&mDebugMpContext.MpContextSpinLock); +} + +/** + Set the current view point to be debugged. + + @param[in] ProcessorIndex Processor index value. + +**/ +VOID +SetDebugViewPoint ( + IN UINT32 ProcessorIndex + ) +{ + AcquireMpSpinLock (&mDebugMpContext.MpContextSpinLock); + mDebugMpContext.ViewPointIndex = ProcessorIndex; + ReleaseMpSpinLock (&mDebugMpContext.MpContextSpinLock); +} + +/** + Set the IPI send by BPS/AP flag. + + @param[in] IpiSentByApFlag TRUE means this IPI is sent by AP. + FALSE means this IPI is sent by BSP. + +**/ +VOID +SetIpiSentByApFlag ( + IN BOOLEAN IpiSentByApFlag + ) +{ + AcquireMpSpinLock (&mDebugMpContext.MpContextSpinLock); + mDebugMpContext.IpiSentByAp = IpiSentByApFlag; + ReleaseMpSpinLock (&mDebugMpContext.MpContextSpinLock); +} + +/** + Check the next pending breaking CPU. + + @retval others There is at least one processor broken, the minimum + index number of Processor returned. + @retval -1 No any processor broken. + +**/ +UINT32 +FindNextPendingBreakCpu ( + VOID + ) +{ + UINT32 Index; + + for (Index = 0; Index < DEBUG_CPU_MAX_COUNT / 8; Index ++) { + if (mDebugMpContext.CpuBreakMask[Index] != 0) { + return (UINT32) LowBitSet32 (mDebugMpContext.CpuBreakMask[Index]) + Index * 8; + } + } + return (UINT32)-1; +} + +/** + Check if all processors are in running status. + + @retval TRUE All processors run. + @retval FALSE At least one processor does not run. + +**/ +BOOLEAN +IsAllCpuRunning ( + VOID + ) +{ + UINTN Index; + + for (Index = 0; Index < DEBUG_CPU_MAX_COUNT / 8; Index ++) { + if (mDebugMpContext.CpuStopStatusMask[Index] != 0) { + return FALSE; + } + } + return TRUE; +} + +/** + Check if the current processor is the first breaking processor. + + If yes, halt other processors. + + @param[in] ProcessorIndex Processor index value. + + @return TRUE This processor is the first breaking processor. + @return FALSE This processor is not the first breaking processor. + +**/ +BOOLEAN +IsFirstBreakProcessor ( + IN UINT32 ProcessorIndex + ) +{ + if (MultiProcessorDebugSupport()) { + if (mDebugMpContext.BreakAtCpuIndex != (UINT32) -1) { + // + // The current processor is not the first breaking one. + // + SetCpuBreakFlagByIndex (ProcessorIndex, TRUE); + return FALSE; + } else { + // + // If no any processor breaks, try to halt other processors + // + HaltOtherProcessors (ProcessorIndex); + return TRUE; + } + } + return TRUE; +} + diff --git a/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/DebugMp.h b/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/DebugMp.h new file mode 100644 index 0000000000..5bb50960ca --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/DebugMp.h @@ -0,0 +1,222 @@ +/** @file + Header file for Multi-Processor support. + + Copyright (c) 2010 - 2013, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _DEBUG_MP_H_ +#define _DEBUG_MP_H_ + +#define DEBUG_CPU_MAX_COUNT 256 + +typedef struct { + UINT32 CpuCount; ///< Processor count + UINT16 ApicID[DEBUG_CPU_MAX_COUNT]; ///< Record the local apic id for each processor +} DEBUG_CPU_DATA; + +typedef struct { + SPIN_LOCK MpContextSpinLock; ///< Lock for writting MP context + SPIN_LOCK DebugPortSpinLock; ///< Lock for access debug port + SPIN_LOCK MailboxSpinLock; ///< Lock for accessing mail box + UINT8 CpuBreakMask[DEBUG_CPU_MAX_COUNT/8]; ///< Bitmask of all breaking CPUs + UINT8 CpuStopStatusMask[DEBUG_CPU_MAX_COUNT/8]; ///< Bitmask of CPU stop status + UINT32 ViewPointIndex; ///< Current view point to be debugged + UINT32 BspIndex; ///< Processor index value of BSP + UINT32 BreakAtCpuIndex; ///< Processor index value of the current breaking CPU + UINT32 DebugTimerInitCount; ///< Record BSP's init timer count + BOOLEAN IpiSentByAp; ///< TRUR: IPI is sent by AP. TALSE: IPI is sent by BSP + BOOLEAN RunCommandSet; ///< TRUE: RUN commmand is executing. FALSE : RUN command has been executed. +} DEBUG_MP_CONTEXT; + +extern DEBUG_MP_CONTEXT volatile mDebugMpContext; +extern DEBUG_CPU_DATA volatile mDebugCpuData; + +/** + Break the other processor by send IPI. + + @param[in] CurrentProcessorIndex Current processor index value. + +**/ +VOID +HaltOtherProcessors ( + IN UINT32 CurrentProcessorIndex + ); + +/** + Get the current processor's index. + + @return Processor index value. + +**/ +UINT32 +GetProcessorIndex ( + VOID + ); + +/** + Acquire a spin lock when Multi-processor supported. + + It will block in the function if cannot get the access control. + If Multi-processor is not supported, return directly. + + @param[in, out] MpSpinLock A pointer to the spin lock. + +**/ +VOID +AcquireMpSpinLock ( + IN OUT SPIN_LOCK *MpSpinLock + ); + +/** + Release a spin lock when Multi-processor supported. + + @param[in, out] MpSpinLock A pointer to the spin lock. + +**/ +VOID +ReleaseMpSpinLock ( + IN OUT SPIN_LOCK *MpSpinLock + ); + +/** + Check if the specified processor is BSP or not. + + @param[in] ProcessorIndex Processor index value. + + @retval TRUE It is BSP. + @retval FALSE It isn't BSP. + +**/ +BOOLEAN +IsBsp ( + IN UINT32 ProcessorIndex + ); + +/** + Set processor stop flag bitmask in MP context. + + @param[in] ProcessorIndex Processor index value. + @param[in] StopFlag TRUE means set stop flag. + FALSE means clean break flag. + +**/ +VOID +SetCpuStopFlagByIndex ( + IN UINT32 ProcessorIndex, + IN BOOLEAN StopFlag + ); + +/** + Set processor break flag bitmask in MP context. + + @param[in] ProcessorIndex Processor index value. + @param[in] BreakFlag TRUE means set break flag. + FALSE means clean break flag. + +**/ +VOID +SetCpuBreakFlagByIndex ( + IN UINT32 ProcessorIndex, + IN BOOLEAN BreakFlag + ); + +/** + Check if processor is stopped already. + + @param[in] ProcessorIndex Processor index value. + + @retval TRUE Processor is stopped already. + @retval FALSE Processor isn't stopped. + +**/ +BOOLEAN +IsCpuStopped ( + IN UINT32 ProcessorIndex + ); + +/** + Set the run command flag. + + @param[in] RunningFlag TRUE means run command flag is set. + FALSE means run command flag is cleared. + +**/ +VOID +SetCpuRunningFlag ( + IN BOOLEAN RunningFlag + ); + +/** + Set the current view point to be debugged. + + @param[in] ProcessorIndex Processor index value. + +**/ +VOID +SetDebugViewPoint ( + IN UINT32 ProcessorIndex + ); + +/** + Set the IPI send by BPS/AP flag. + + @param[in] IpiSentByApFlag TRUE means this IPI is sent by AP. + FALSE means this IPI is sent by BSP. + +**/ +VOID +SetIpiSentByApFlag ( + IN BOOLEAN IpiSentByApFlag + ); + +/** + Check the next pending breaking CPU. + + @retval others There is at least one processor broken, the minimum + index number of Processor returned. + @retval -1 No any processor broken. + +**/ +UINT32 +FindNextPendingBreakCpu ( + VOID + ); + +/** + Check if all processors are in running status. + + @retval TRUE All processors run. + @retval FALSE At least one processor does not run. + +**/ +BOOLEAN +IsAllCpuRunning ( + VOID + ); + +/** + Check if the current processor is the first breaking processor. + + If yes, halt other processors. + + @param[in] ProcessorIndex Processor index value. + + @return TRUE This processor is the first breaking processor. + @return FALSE This processor is not the first breaking processor. + +**/ +BOOLEAN +IsFirstBreakProcessor ( + IN UINT32 ProcessorIndex + ); + +#endif + diff --git a/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/DebugTimer.c b/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/DebugTimer.c new file mode 100644 index 0000000000..d01d1ae484 --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/DebugTimer.c @@ -0,0 +1,149 @@ +/** @file + Code for debug timer to support debug agent library implementation. + + Copyright (c) 2010 - 2015, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "DebugAgent.h" + +/** + Initialize CPU local APIC timer. + + @param[out] TimerFrequency Local APIC timer frequency returned. + @param[in] DumpFlag If TRUE, dump Local APIC timer's parameter. + + @return 32-bit Local APIC timer init count. +**/ +UINT32 +InitializeDebugTimer ( + OUT UINT32 *TimerFrequency, + IN BOOLEAN DumpFlag + ) +{ + UINTN ApicTimerDivisor; + UINT32 InitialCount; + UINT32 ApicTimerFrequency; + + InitializeLocalApicSoftwareEnable (TRUE); + GetApicTimerState (&ApicTimerDivisor, NULL, NULL); + ApicTimerFrequency = PcdGet32(PcdFSBClock) / (UINT32)ApicTimerDivisor; + // + // Cpu Local Apic timer interrupt frequency, it is set to 0.1s + // + InitialCount = (UINT32)DivU64x32 ( + MultU64x64 ( + ApicTimerFrequency, + DEBUG_TIMER_INTERVAL + ), + 1000000u + ); + + InitializeApicTimer (ApicTimerDivisor, InitialCount, TRUE, DEBUG_TIMER_VECTOR); + // + // Disable Debug Timer interrupt to avoid it is delivered before Debug Port + // is initialized + // + DisableApicTimerInterrupt (); + + if (DumpFlag) { + DEBUG ((EFI_D_INFO, "Debug Timer: FSB Clock = %d\n", PcdGet32(PcdFSBClock))); + DEBUG ((EFI_D_INFO, "Debug Timer: Divisor = %d\n", ApicTimerDivisor)); + DEBUG ((EFI_D_INFO, "Debug Timer: Frequency = %d\n", ApicTimerFrequency)); + DEBUG ((EFI_D_INFO, "Debug Timer: InitialCount = %d\n", InitialCount)); + } + if (TimerFrequency != NULL) { + *TimerFrequency = ApicTimerFrequency; + } + return InitialCount; +} + +/** + Enable/Disable the interrupt of debug timer and return the interrupt state + prior to the operation. + + If EnableStatus is TRUE, enable the interrupt of debug timer. + If EnableStatus is FALSE, disable the interrupt of debug timer. + + @param[in] EnableStatus Enable/Disable. + + @retval TRUE Debug timer interrupt were enabled on entry to this call. + @retval FALSE Debug timer interrupt were disabled on entry to this call. + +**/ +BOOLEAN +EFIAPI +SaveAndSetDebugTimerInterrupt ( + IN BOOLEAN EnableStatus + ) +{ + BOOLEAN OldDebugTimerInterruptState; + + OldDebugTimerInterruptState = GetApicTimerInterruptState (); + + if (OldDebugTimerInterruptState != EnableStatus) { + if (EnableStatus) { + EnableApicTimerInterrupt (); + } else { + DisableApicTimerInterrupt (); + } + // + // Validate the Debug Timer interrupt state + // This will make additional delay after Local Apic Timer interrupt state is changed. + // Thus, CPU could handle the potential pending interrupt of Local Apic timer. + // + while (GetApicTimerInterruptState () != EnableStatus) { + CpuPause (); + } + } + + return OldDebugTimerInterruptState; +} + +/** + Check if the timer is time out. + + @param[in] TimerCycle Timer initial count. + @param[in] Timer The start timer from the begin. + @param[in] TimeoutTicker Ticker number need time out. + + @return TRUE Timer time out occurs. + @retval FALSE Timer does not time out. + +**/ +BOOLEAN +IsDebugTimerTimeout ( + IN UINT32 TimerCycle, + IN UINT32 Timer, + IN UINT32 TimeoutTicker + ) +{ + UINT64 CurrentTimer; + UINT64 Delta; + + CurrentTimer = GetApicTimerCurrentCount (); + + // + // This timer counter counts down. Check for roll over condition. + // If CurrentTimer is equal to Timer, it does not mean that roll over + // happened. + // + if (CurrentTimer <= Timer) { + Delta = Timer - CurrentTimer; + } else { + // + // Handle one roll-over. + // + Delta = TimerCycle - (CurrentTimer - Timer) + 1; + } + + return (BOOLEAN) (Delta >= TimeoutTicker); +} + diff --git a/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/DebugTimer.h b/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/DebugTimer.h new file mode 100644 index 0000000000..e480fa3e0c --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/DebugTimer.h @@ -0,0 +1,51 @@ +/** @file + Header file for debug timer to support debug agent library implementation. + + Copyright (c) 2010 - 2015, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _DEBUG_TIMER_H_ +#define _DEBUG_TIMER_H_ + +/** + Initialize CPU local APIC timer. + + @param[out] TimerFrequency Local APIC timer frequency returned. + @param[in] DumpFlag If TRUE, dump Local APIC timer's parameter. + + @return 32-bit Local APIC timer init count. +**/ +UINT32 +InitializeDebugTimer ( + OUT UINT32 *TimerFrequency, + IN BOOLEAN DumpFlag + ); + +/** + Check if the timer is time out. + + @param[in] TimerCycle Timer initial count. + @param[in] Timer The start timer from the begin. + @param[in] TimeoutTicker Ticker number need time out. + + @return TRUE Timer time out occurs. + @retval FALSE Timer does not time out. + +**/ +BOOLEAN +IsDebugTimerTimeout ( + IN UINT32 TimerCycle, + IN UINT32 Timer, + IN UINT32 TimeoutTicker + ); + +#endif + diff --git a/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/Ia32/ArchDebugSupport.c b/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/Ia32/ArchDebugSupport.c new file mode 100644 index 0000000000..1aef63f206 --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/Ia32/ArchDebugSupport.c @@ -0,0 +1,116 @@ +/** @file + Supporting functions for IA32 architecture. + + Copyright (c) 2010 - 2015, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "DebugAgent.h" + +/** + Initialize IDT entries to support source level debug. + +**/ +VOID +InitializeDebugIdt ( + VOID + ) +{ + IA32_IDT_GATE_DESCRIPTOR *IdtEntry; + UINTN InterruptHandler; + IA32_DESCRIPTOR IdtDescriptor; + UINTN Index; + UINT16 CodeSegment; + UINT32 RegEdx; + + AsmReadIdtr (&IdtDescriptor); + + // + // Use current CS as the segment selector of interrupt gate in IDT + // + CodeSegment = AsmReadCs (); + + IdtEntry = (IA32_IDT_GATE_DESCRIPTOR *) IdtDescriptor.Base; + + for (Index = 0; Index < 20; Index ++) { + if (((PcdGet32 (PcdExceptionsIgnoredByDebugger) & ~(BIT1 | BIT3)) & (1 << Index)) != 0) { + // + // If the exception is masked to be reserved except for INT1 and INT3, skip it + // + continue; + } + InterruptHandler = (UINTN)&Exception0Handle + Index * ExceptionStubHeaderSize; + IdtEntry[Index].Bits.OffsetLow = (UINT16)(UINTN)InterruptHandler; + IdtEntry[Index].Bits.OffsetHigh = (UINT16)((UINTN)InterruptHandler >> 16); + IdtEntry[Index].Bits.Selector = CodeSegment; + IdtEntry[Index].Bits.GateType = IA32_IDT_GATE_TYPE_INTERRUPT_32; + } + + InterruptHandler = (UINTN) &TimerInterruptHandle; + IdtEntry[DEBUG_TIMER_VECTOR].Bits.OffsetLow = (UINT16)(UINTN)InterruptHandler; + IdtEntry[DEBUG_TIMER_VECTOR].Bits.OffsetHigh = (UINT16)((UINTN)InterruptHandler >> 16); + IdtEntry[DEBUG_TIMER_VECTOR].Bits.Selector = CodeSegment; + IdtEntry[DEBUG_TIMER_VECTOR].Bits.GateType = IA32_IDT_GATE_TYPE_INTERRUPT_32; + + // + // If the CPU supports Debug Extensions(CPUID:01 EDX:BIT2), then + // Set DE flag in CR4 to enable IO breakpoint + // + AsmCpuid (1, NULL, NULL, NULL, &RegEdx); + if ((RegEdx & BIT2) != 0) { + AsmWriteCr4 (AsmReadCr4 () | BIT3); + } +} + +/** + Retrieve exception handler from IDT table by ExceptionNum. + + @param[in] ExceptionNum Exception number + + @return Exception handler + +**/ +VOID * +GetExceptionHandlerInIdtEntry ( + IN UINTN ExceptionNum + ) +{ + IA32_IDT_GATE_DESCRIPTOR *IdtEntry; + IA32_DESCRIPTOR IdtDescriptor; + + AsmReadIdtr (&IdtDescriptor); + IdtEntry = (IA32_IDT_GATE_DESCRIPTOR *) IdtDescriptor.Base; + + return (VOID *) (((UINTN)IdtEntry[ExceptionNum].Bits.OffsetLow) | + (((UINTN)IdtEntry[ExceptionNum].Bits.OffsetHigh) << 16)); +} + +/** + Set exception handler in IDT table by ExceptionNum. + + @param[in] ExceptionNum Exception number + @param[in] ExceptionHandler Exception Handler to be set + +**/ +VOID +SetExceptionHandlerInIdtEntry ( + IN UINTN ExceptionNum, + IN VOID *ExceptionHandler + ) +{ + IA32_IDT_GATE_DESCRIPTOR *IdtEntry; + IA32_DESCRIPTOR IdtDescriptor; + + AsmReadIdtr (&IdtDescriptor); + IdtEntry = (IA32_IDT_GATE_DESCRIPTOR *) IdtDescriptor.Base; + + IdtEntry[ExceptionNum].Bits.OffsetLow = (UINT16)(UINTN)ExceptionHandler; + IdtEntry[ExceptionNum].Bits.OffsetHigh = (UINT16)((UINTN)ExceptionHandler >> 16); +} diff --git a/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/Ia32/ArchDebugSupport.h b/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/Ia32/ArchDebugSupport.h new file mode 100644 index 0000000000..fa1c2f194c --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/Ia32/ArchDebugSupport.h @@ -0,0 +1,27 @@ +/** @file + IA32 specific defintions for debug agent library instance. + + Copyright (c) 2010 - 2012, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _ARCH_DEBUG_SUPPORT_H_ +#define _ARCH_DEBUG_SUPPORT_H_ + +#include "ProcessorContext.h" +#include "TransferProtocol.h" + +#define DEBUG_SW_BREAKPOINT_SYMBOL 0xcc +#define DEBUG_ARCH_SYMBOL DEBUG_DATA_BREAK_CPU_ARCH_IA32 + +typedef DEBUG_DATA_IA32_FX_SAVE_STATE DEBUG_DATA_FX_SAVE_STATE; +typedef DEBUG_DATA_IA32_SYSTEM_CONTEXT DEBUG_CPU_CONTEXT; + +#endif diff --git a/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/Ia32/AsmFuncs.S b/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/Ia32/AsmFuncs.S new file mode 100644 index 0000000000..a6a3da6a6b --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/Ia32/AsmFuncs.S @@ -0,0 +1,415 @@ +#------------------------------------------------------------------------------ +# +# Copyright (c) 2010 - 2015, Intel Corporation. All rights reserved.
+# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php. +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# Module Name: +# +# AsmFuncs.S +# +# Abstract: +# +# Debug interrupt handle functions. +# +#------------------------------------------------------------------------------ + +#include "DebugException.h" + +ASM_GLOBAL ASM_PFX(InterruptProcess) +ASM_GLOBAL ASM_PFX(Exception0Handle) +ASM_GLOBAL ASM_PFX(ExceptionStubHeaderSize) +ASM_GLOBAL ASM_PFX(TimerInterruptHandle) +ASM_GLOBAL ASM_PFX(CommonEntry) + +.macro AGENT_HANDLER_SIGNATURE + .byte 0x41, 0x47, 0x54, 0x48 # AGENT_HANDLER_SIGNATURE SIGNATURE_32('A','G','T','H') +.endm + +.data + +ASM_PFX(ExceptionStubHeaderSize): .long ASM_PFX(Exception1Handle) - ASM_PFX(Exception0Handle) + +.text + +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception0Handle): + cli + pushl %eax + mov $0, %eax + jmp ASM_PFX(CommonEntry) +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception1Handle): + cli + pushl %eax + mov $1, %eax + jmp ASM_PFX(CommonEntry) +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception2Handle): + cli + pushl %eax + mov $2, %eax + jmp ASM_PFX(CommonEntry) +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception3Handle): + cli + pushl %eax + mov $3, %eax + jmp ASM_PFX(CommonEntry) +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception4Handle): + cli + pushl %eax + mov $4, %eax + jmp ASM_PFX(CommonEntry) +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception5Handle): + cli + pushl %eax + mov $5, %eax + jmp ASM_PFX(CommonEntry) +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception6Handle): + cli + pushl %eax + mov $6, %eax + jmp ASM_PFX(CommonEntry) +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception7Handle): + cli + pushl %eax + mov $7, %eax + jmp ASM_PFX(CommonEntry) +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception8Handle): + cli + pushl %eax + mov $8, %eax + jmp ASM_PFX(CommonEntry) +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception9Handle): + cli + pushl %eax + mov $9, %eax + jmp ASM_PFX(CommonEntry) +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception10Handle): + cli + pushl %eax + mov $10, %eax + jmp ASM_PFX(CommonEntry) +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception11Handle): + cli + pushl %eax + mov $11, %eax + jmp ASM_PFX(CommonEntry) +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception12Handle): + cli + pushl %eax + mov $12, %eax + jmp ASM_PFX(CommonEntry) +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception13Handle): + cli + pushl %eax + mov $13, %eax + jmp ASM_PFX(CommonEntry) +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception14Handle): + cli + pushl %eax + mov $14, %eax + jmp ASM_PFX(CommonEntry) +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception15Handle): + cli + pushl %eax + mov $15, %eax + jmp ASM_PFX(CommonEntry) +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception16Handle): + cli + pushl %eax + mov $16, %eax + jmp ASM_PFX(CommonEntry) +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception17Handle): + cli + pushl %eax + mov $17, %eax + jmp ASM_PFX(CommonEntry) +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception18Handle): + cli + pushl %eax + mov $18, %eax + jmp ASM_PFX(CommonEntry) +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception19Handle): + cli + pushl %eax + mov $19, %eax + jmp ASM_PFX(CommonEntry) +AGENT_HANDLER_SIGNATURE +ASM_PFX(TimerInterruptHandle): + cli + pushl %eax + mov $32, %eax + jmp ASM_PFX(CommonEntry) + + +ASM_PFX(CommonEntry): + +#---------------------------------------; +# _CommonEntry ; +#----------------------------------------------------------------------------; +# The follow algorithm is used for the common interrupt routine. +# Entry from each interrupt with a push eax and eax=interrupt number +# +# +---------------------+ +# + EFlags + +# +---------------------+ +# + CS + +# +---------------------+ +# + EIP + +# +---------------------+ +# + Error Code + +# +---------------------+ +# + EAX / Vector Number + +# +---------------------+ +# + EBP + +# +---------------------+ <-- EBP +# + +# We need to determine if any extra data was pushed by the exception + cmpl $DEBUG_EXCEPT_DOUBLE_FAULT, %eax + je NoExtrPush + cmpl $DEBUG_EXCEPT_INVALID_TSS, %eax + je NoExtrPush + cmpl $DEBUG_EXCEPT_SEG_NOT_PRESENT, %eax + je NoExtrPush + cmpl $DEBUG_EXCEPT_STACK_FAULT, %eax + je NoExtrPush + cmpl $DEBUG_EXCEPT_GP_FAULT, %eax + je NoExtrPush + cmpl $DEBUG_EXCEPT_PAGE_FAULT, %eax + je NoExtrPush + cmpl $DEBUG_EXCEPT_ALIGNMENT_CHECK, %eax + je NoExtrPush + + pushl (%esp) + movl $0, 4(%esp) + +NoExtrPush: + + pushl %ebp + movl %esp,%ebp + + # + # Align stack to make sure that EFI_FX_SAVE_STATE_IA32 of EFI_SYSTEM_CONTEXT_IA32 + # is 16-byte aligned + # + andl $0xfffffff0,%esp + subl $12,%esp + +## UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax; + pushl 0x4(%ebp) + pushl %ebx + pushl %ecx + pushl %edx + mov %eax, %ebx # save vector in ebx + leal 24(%ebp),%ecx + pushl %ecx # save original ESP + pushl (%ebp) + pushl %esi + pushl %edi + +## UINT32 Cr0, Cr1, Cr2, Cr3, Cr4; +## insure FXSAVE/FXRSTOR is enabled in CR4... +## ... while we're at it, make sure DE is also enabled... + mov $1, %eax + pushl %ebx # temporarily save value of ebx on stack + cpuid # use CPUID to determine if FXSAVE/FXRESTOR + # and DE are supported + popl %ebx # retore value of ebx that was overwritten + # by CPUID + movl %cr4, %eax + pushl %eax # push cr4 firstly + testl $BIT24, %edx # Test for FXSAVE/FXRESTOR support + jz L1 + orl $BIT9, %eax # Set CR4.OSFXSR +L1: + testl $BIT2, %edx # Test for Debugging Extensions support + jz L2 + orl $BIT3, %eax # Set CR4.DE +L2: + movl %eax, %cr4 + movl %cr3, %eax + pushl %eax + movl %cr2, %eax + pushl %eax + xorl %eax,%eax + pushl %eax + movl %cr0, %eax + pushl %eax + +## UINT32 Gs, Fs, Es, Ds, Cs, Ss; + movl %ss,%eax + pushl %eax + movzwl 16(%ebp), %eax + pushl %eax + movl %ds,%eax + pushl %eax + movl %es,%eax + pushl %eax + movl %fs,%eax + pushl %eax + movl %gs,%eax + pushl %eax + +## UINT32 Eip; + pushl 12(%ebp) + +## UINT32 Gdtr[2], Idtr[2]; + subl $8,%esp + sidt (%esp) + subl $8,%esp + sgdt (%esp) + +## UINT32 Ldtr, Tr; + xorl %eax,%eax + strl %eax + pushl %eax + sldtl %eax + pushl %eax + +## UINT32 EFlags; + pushl 20(%ebp) + +## UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; + movl %dr7, %eax + pushl %eax +## clear Dr7 while executing debugger itself + xorl %eax,%eax + movl %eax, %dr7 + + movl %dr6, %eax + pushl %eax +## insure all status bits in dr6 are clear... + xorl %eax,%eax + movl %eax, %dr6 + + movl %dr3, %eax + pushl %eax + movl %dr2, %eax + pushl %eax + movl %dr1, %eax + pushl %eax + movl %dr0, %eax + pushl %eax + +## FX_SAVE_STATE_IA32 FxSaveState; + subl $512,%esp + movl %esp,%edi + testl $BIT24, %edx # Test for FXSAVE/FXRESTOR support. + # edx still contains result from CPUID above + jz L3 + .byte 0x0f, 0xae, 0x07 # fxsave [edi] +L3: + +## save the exception data + pushl 8(%esp) + +## Clear Direction Flag + cld + +## Prepare parameter and call C function + pushl %esp + pushl %ebx + call ASM_PFX(InterruptProcess) + addl $8,%esp + +## skip the exception data + addl $4,%esp + +## FX_SAVE_STATE_IA32 FxSaveState; + movl %esp,%esi + movl $1, %eax + cpuid # use CPUID to determine if FXSAVE/FXRESTOR + # are supported + testl $BIT24, %edx # Test for FXSAVE/FXRESTOR support + jz L4 + .byte 0x0f, 0xae, 0x0e # fxrstor [esi] +L4: + addl $512,%esp + +## UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; + popl %eax + movl %eax, %dr0 + popl %eax + movl %eax, %dr1 + popl %eax + movl %eax, %dr2 + popl %eax + movl %eax, %dr3 +## skip restore of dr6. We cleared dr6 during the context save. + addl $4,%esp + popl %eax + movl %eax, %dr7 + +## UINT32 EFlags; + popl 20(%ebp) + +## UINT32 Ldtr, Tr; +## UINT32 Gdtr[2], Idtr[2]; +## Best not let anyone mess with these particular registers... + addl $24,%esp + +## UINT32 Eip; + pop 12(%ebp) + +## UINT32 Gs, Fs, Es, Ds, Cs, Ss; +## NOTE - modified segment registers could hang the debugger... We +## could attempt to insulate ourselves against this possibility, +## but that poses risks as well. +## + popl %gs + popl %fs + popl %es + popl %ds + popl 16(%ebp) + popl %ss + +## UINT32 Cr0, Cr1, Cr2, Cr3, Cr4; + popl %eax + movl %eax, %cr0 + addl $4,%esp # not for Cr1 + popl %eax + movl %eax, %cr2 + popl %eax + movl %eax, %cr3 + popl %eax + movl %eax, %cr4 + +## UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax; + popl %edi + popl %esi + addl $4,%esp # not for ebp + addl $4,%esp # not for esp + popl %edx + popl %ecx + popl %ebx + popl %eax + + movl %ebp,%esp + popl %ebp + addl $8,%esp # skip eax + iretl + diff --git a/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/Ia32/AsmFuncs.asm b/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/Ia32/AsmFuncs.asm new file mode 100644 index 0000000000..44ed6f7710 --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/Ia32/AsmFuncs.asm @@ -0,0 +1,422 @@ +;------------------------------------------------------------------------------ +; +; Copyright (c) 2010 - 2015, Intel Corporation. All rights reserved.
+; This program and the accompanying materials +; are licensed and made available under the terms and conditions of the BSD License +; which accompanies this distribution. The full text of the license may be found at +; http://opensource.org/licenses/bsd-license.php. +; +; THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +; WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +; +; Module Name: +; +; AsmFuncs.asm +; +; Abstract: +; +; Debug interrupt handle functions. +; +;------------------------------------------------------------------------------ + +#include "DebugException.h" + +.686p +.xmm +.model flat,c + +; +; InterruptProcess() +; +InterruptProcess PROTO C + +public Exception0Handle, TimerInterruptHandle, ExceptionStubHeaderSize + +AGENT_HANDLER_SIGNATURE MACRO + db 41h, 47h, 54h, 48h ; SIGNATURE_32('A','G','T','H') +ENDM + +.data + +ExceptionStubHeaderSize DD Exception1Handle - Exception0Handle +CommonEntryAddr DD CommonEntry + +.code + +AGENT_HANDLER_SIGNATURE +Exception0Handle: + cli + push eax + mov eax, 0 + jmp dword ptr [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception1Handle: + cli + push eax + mov eax, 1 + jmp dword ptr [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception2Handle: + cli + push eax + mov eax, 2 + jmp dword ptr [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception3Handle: + cli + push eax + mov eax, 3 + jmp dword ptr [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception4Handle: + cli + push eax + mov eax, 4 + jmp dword ptr [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception5Handle: + cli + push eax + mov eax, 5 + jmp dword ptr [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception6Handle: + cli + push eax + mov eax, 6 + jmp dword ptr [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception7Handle: + cli + push eax + mov eax, 7 + jmp dword ptr [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception8Handle: + cli + push eax + mov eax, 8 + jmp dword ptr [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception9Handle: + cli + push eax + mov eax, 9 + jmp dword ptr [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception10Handle: + cli + push eax + mov eax, 10 + jmp dword ptr [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception11Handle: + cli + push eax + mov eax, 11 + jmp dword ptr [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception12Handle: + cli + push eax + mov eax, 12 + jmp dword ptr [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception13Handle: + cli + push eax + mov eax, 13 + jmp dword ptr [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception14Handle: + cli + push eax + mov eax, 14 + jmp dword ptr [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception15Handle: + cli + push eax + mov eax, 15 + jmp dword ptr [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception16Handle: + cli + push eax + mov eax, 16 + jmp dword ptr [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception17Handle: + cli + push eax + mov eax, 17 + jmp dword ptr [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception18Handle: + cli + push eax + mov eax, 18 + jmp dword ptr [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception19Handle: + cli + push eax + mov eax, 19 + jmp dword ptr [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +TimerInterruptHandle: + cli + push eax + mov eax, 32 + jmp dword ptr [CommonEntryAddr] + +CommonEntry: +; +; +---------------------+ +; + EFlags + +; +---------------------+ +; + CS + +; +---------------------+ +; + EIP + +; +---------------------+ +; + Error Code + +; +---------------------+ +; + EAX / Vector Number + +; +---------------------+ +; + EBP + +; +---------------------+ <-- EBP +; + cmp eax, DEBUG_EXCEPT_DOUBLE_FAULT + je NoExtrPush + cmp eax, DEBUG_EXCEPT_INVALID_TSS + je NoExtrPush + cmp eax, DEBUG_EXCEPT_SEG_NOT_PRESENT + je NoExtrPush + cmp eax, DEBUG_EXCEPT_STACK_FAULT + je NoExtrPush + cmp eax, DEBUG_EXCEPT_GP_FAULT + je NoExtrPush + cmp eax, DEBUG_EXCEPT_PAGE_FAULT + je NoExtrPush + cmp eax, DEBUG_EXCEPT_ALIGNMENT_CHECK + je NoExtrPush + + push [esp] + mov dword ptr [esp + 4], 0 + +NoExtrPush: + + push ebp + mov ebp, esp ; save esp in ebp + ; + ; Make stack 16-byte alignment to make sure save fxrstor later + ; + and esp, 0fffffff0h + sub esp, 12 + + ; store UINT32 Edi, Esi, Ebp, Ebx, Edx, Ecx, Eax; + push dword ptr [ebp + 4] ; original eax + push ebx + push ecx + push edx + mov ebx, eax ; save vector in ebx + mov eax, ebp + add eax, 4 * 6 + push eax ; original ESP + push dword ptr [ebp] ; EBP + push esi + push edi + + ;; UINT32 Cr0, Cr1, Cr2, Cr3, Cr4; + ;; insure FXSAVE/FXRSTOR is enabled in CR4... + ;; ... while we're at it, make sure DE is also enabled... + mov eax, 1 + push ebx ; temporarily save value of ebx on stack + cpuid ; use CPUID to determine if FXSAVE/FXRESTOR and + ; DE are supported + pop ebx ; retore value of ebx that was overwritten by CPUID + mov eax, cr4 + push eax ; push cr4 firstly + test edx, BIT24 ; Test for FXSAVE/FXRESTOR support + jz @F + or eax, BIT9 ; Set CR4.OSFXSR +@@: + test edx, BIT2 ; Test for Debugging Extensions support + jz @F + or eax, BIT3 ; Set CR4.DE +@@: + mov cr4, eax + mov eax, cr3 + push eax + mov eax, cr2 + push eax + push 0 ; cr0 will not saved??? + mov eax, cr0 + push eax + + xor ecx, ecx + mov ecx, Ss + push ecx + mov ecx, Cs + push ecx + mov ecx, Ds + push ecx + mov ecx, Es + push ecx + mov ecx, Fs + push ecx + mov ecx, Gs + push ecx + + ;; EIP + mov ecx, [ebp + 4 * 3] ; EIP + push ecx + + ;; UINT32 Gdtr[2], Idtr[2]; + sub esp, 8 + sidt fword ptr [esp] + sub esp, 8 + sgdt fword ptr [esp] + + ;; UINT32 Ldtr, Tr; + xor eax, eax + str ax + push eax + sldt ax + push eax + + ;; EFlags + mov ecx, [ebp + 4 * 5] + push ecx + + ;; UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; + mov eax, dr7 + push eax + + ;; clear Dr7 while executing debugger itself + xor eax, eax + mov dr7, eax + + ;; Dr6 + mov eax, dr6 + push eax + + ;; insure all status bits in dr6 are clear... + xor eax, eax + mov dr6, eax + + mov eax, dr3 + push eax + mov eax, dr2 + push eax + mov eax, dr1 + push eax + mov eax, dr0 + push eax + + ;; Clear Direction Flag + cld + + ;; FX_SAVE_STATE_IA32 FxSaveState; + sub esp, 512 + mov edi, esp + ;; Clear the buffer + xor eax, eax + mov ecx, 128 ;= 512 / 4 + rep stosd + mov edi, esp + + test edx, BIT24 ; Test for FXSAVE/FXRESTOR support. + ; edx still contains result from CPUID above + jz @F + db 0fh, 0aeh, 00000111y ;fxsave [edi] +@@: + + ;; save the exception data + push dword ptr [ebp + 8] + + ; call the C interrupt process function + push esp ; Structure + push ebx ; vector + call InterruptProcess + add esp, 8 + + ; skip the exception data + add esp, 4 + + ;; FX_SAVE_STATE_IA32 FxSaveState; + mov esi, esp + mov eax, 1 + cpuid ; use CPUID to determine if FXSAVE/FXRESTOR are supported + test edx, BIT24 ; Test for FXSAVE/FXRESTOR support + jz @F + db 0fh, 0aeh, 00001110y ; fxrstor [esi] +@@: + add esp, 512 + + ;; UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; + pop eax + mov dr0, eax + pop eax + mov dr1, eax + pop eax + mov dr2, eax + pop eax + mov dr3, eax + ;; skip restore of dr6. We cleared dr6 during the context save. + add esp, 4 + pop eax + mov dr7, eax + + ;; set EFlags + pop dword ptr [ebp + 4 * 5] ; set EFLAGS in stack + + ;; UINT32 Ldtr, Tr; + ;; UINT32 Gdtr[2], Idtr[2]; + ;; Best not let anyone mess with these particular registers... + add esp, 24 + + ;; UINT32 Eip; + pop dword ptr [ebp + 4 * 3] ; set EIP in stack + + ;; UINT32 Gs, Fs, Es, Ds, Cs, Ss; + ;; NOTE - modified segment registers could hang the debugger... We + ;; could attempt to insulate ourselves against this possibility, + ;; but that poses risks as well. + ;; + pop gs + pop fs + pop es + pop ds + pop dword ptr [ebp + 4 * 4] ; set CS in stack + pop ss + + ;; UINT32 Cr0, Cr1, Cr2, Cr3, Cr4; + pop eax + mov cr0, eax + add esp, 4 ; skip for Cr1 + pop eax + mov cr2, eax + pop eax + mov cr3, eax + pop eax + mov cr4, eax + + ;; restore general register + pop edi + pop esi + pop dword ptr [ebp] ; save updated ebp + pop dword ptr [ebp + 4] ; save updated esp + pop edx + pop ecx + pop ebx + pop eax + + mov esp, ebp + pop ebp ; restore ebp maybe updated + pop esp ; restore esp maybe updated + sub esp, 4 * 3 ; restore interupt pushced stack + + iretd + +END diff --git a/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/Ia32/AsmFuncs.nasm b/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/Ia32/AsmFuncs.nasm new file mode 100644 index 0000000000..ac057a668c --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/Ia32/AsmFuncs.nasm @@ -0,0 +1,419 @@ +;------------------------------------------------------------------------------ +; +; Copyright (c) 2010 - 2015, Intel Corporation. All rights reserved.
+; This program and the accompanying materials +; are licensed and made available under the terms and conditions of the BSD License +; which accompanies this distribution. The full text of the license may be found at +; http://opensource.org/licenses/bsd-license.php. +; +; THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +; WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +; +; Module Name: +; +; AsmFuncs.nasm +; +; Abstract: +; +; Debug interrupt handle functions. +; +;------------------------------------------------------------------------------ + +#include "DebugException.h" + +; +; InterruptProcess() +; +extern ASM_PFX(InterruptProcess) + +global ASM_PFX(Exception0Handle) +global ASM_PFX(TimerInterruptHandle) +global ASM_PFX(ExceptionStubHeaderSize) + +%macro AGENT_HANDLER_SIGNATURE 0 + db 0x41, 0x47, 0x54, 0x48 ; SIGNATURE_32('A','G','T','H') +%endmacro + +SECTION .data + +ASM_PFX(ExceptionStubHeaderSize): DD Exception1Handle - ASM_PFX(Exception0Handle) +CommonEntryAddr: DD CommonEntry + +SECTION .text + +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception0Handle): + cli + push eax + mov eax, 0 + jmp dword [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception1Handle: + cli + push eax + mov eax, 1 + jmp dword [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception2Handle: + cli + push eax + mov eax, 2 + jmp dword [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception3Handle: + cli + push eax + mov eax, 3 + jmp dword [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception4Handle: + cli + push eax + mov eax, 4 + jmp dword [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception5Handle: + cli + push eax + mov eax, 5 + jmp dword [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception6Handle: + cli + push eax + mov eax, 6 + jmp dword [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception7Handle: + cli + push eax + mov eax, 7 + jmp dword [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception8Handle: + cli + push eax + mov eax, 8 + jmp dword [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception9Handle: + cli + push eax + mov eax, 9 + jmp dword [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception10Handle: + cli + push eax + mov eax, 10 + jmp dword [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception11Handle: + cli + push eax + mov eax, 11 + jmp dword [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception12Handle: + cli + push eax + mov eax, 12 + jmp dword [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception13Handle: + cli + push eax + mov eax, 13 + jmp dword [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception14Handle: + cli + push eax + mov eax, 14 + jmp dword [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception15Handle: + cli + push eax + mov eax, 15 + jmp dword [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception16Handle: + cli + push eax + mov eax, 16 + jmp dword [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception17Handle: + cli + push eax + mov eax, 17 + jmp dword [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception18Handle: + cli + push eax + mov eax, 18 + jmp dword [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception19Handle: + cli + push eax + mov eax, 19 + jmp dword [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +ASM_PFX(TimerInterruptHandle): + cli + push eax + mov eax, 32 + jmp dword [CommonEntryAddr] + +CommonEntry: +; +; +---------------------+ +; + EFlags + +; +---------------------+ +; + CS + +; +---------------------+ +; + EIP + +; +---------------------+ +; + Error Code + +; +---------------------+ +; + EAX / Vector Number + +; +---------------------+ +; + EBP + +; +---------------------+ <-- EBP +; + cmp eax, DEBUG_EXCEPT_DOUBLE_FAULT + je NoExtrPush + cmp eax, DEBUG_EXCEPT_INVALID_TSS + je NoExtrPush + cmp eax, DEBUG_EXCEPT_SEG_NOT_PRESENT + je NoExtrPush + cmp eax, DEBUG_EXCEPT_STACK_FAULT + je NoExtrPush + cmp eax, DEBUG_EXCEPT_GP_FAULT + je NoExtrPush + cmp eax, DEBUG_EXCEPT_PAGE_FAULT + je NoExtrPush + cmp eax, DEBUG_EXCEPT_ALIGNMENT_CHECK + je NoExtrPush + + push dword [esp] + mov dword [esp + 4], 0 + +NoExtrPush: + + push ebp + mov ebp, esp ; save esp in ebp + ; + ; Make stack 16-byte alignment to make sure save fxrstor later + ; + and esp, 0xfffffff0 + sub esp, 12 + + ; store UINT32 Edi, Esi, Ebp, Ebx, Edx, Ecx, Eax; + push dword [ebp + 4] ; original eax + push ebx + push ecx + push edx + mov ebx, eax ; save vector in ebx + mov eax, ebp + add eax, 4 * 6 + push eax ; original ESP + push dword [ebp] ; EBP + push esi + push edi + + ;; UINT32 Cr0, Cr1, Cr2, Cr3, Cr4; + ;; insure FXSAVE/FXRSTOR is enabled in CR4... + ;; ... while we're at it, make sure DE is also enabled... + mov eax, 1 + push ebx ; temporarily save value of ebx on stack + cpuid ; use CPUID to determine if FXSAVE/FXRESTOR and + ; DE are supported + pop ebx ; retore value of ebx that was overwritten by CPUID + mov eax, cr4 + push eax ; push cr4 firstly + test edx, BIT24 ; Test for FXSAVE/FXRESTOR support + jz .0 + or eax, BIT9 ; Set CR4.OSFXSR +.0: + test edx, BIT2 ; Test for Debugging Extensions support + jz .1 + or eax, BIT3 ; Set CR4.DE +.1: + mov cr4, eax + mov eax, cr3 + push eax + mov eax, cr2 + push eax + push 0 ; cr0 will not saved??? + mov eax, cr0 + push eax + + xor ecx, ecx + mov ecx, Ss + push ecx + mov ecx, Cs + push ecx + mov ecx, Ds + push ecx + mov ecx, Es + push ecx + mov ecx, Fs + push ecx + mov ecx, Gs + push ecx + + ;; EIP + mov ecx, [ebp + 4 * 3] ; EIP + push ecx + + ;; UINT32 Gdtr[2], Idtr[2]; + sub esp, 8 + sidt [esp] + sub esp, 8 + sgdt [esp] + + ;; UINT32 Ldtr, Tr; + xor eax, eax + str ax + push eax + sldt ax + push eax + + ;; EFlags + mov ecx, [ebp + 4 * 5] + push ecx + + ;; UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; + mov eax, dr7 + push eax + + ;; clear Dr7 while executing debugger itself + xor eax, eax + mov dr7, eax + + ;; Dr6 + mov eax, dr6 + push eax + + ;; insure all status bits in dr6 are clear... + xor eax, eax + mov dr6, eax + + mov eax, dr3 + push eax + mov eax, dr2 + push eax + mov eax, dr1 + push eax + mov eax, dr0 + push eax + + ;; Clear Direction Flag + cld + + ;; FX_SAVE_STATE_IA32 FxSaveState; + sub esp, 512 + mov edi, esp + ;; Clear the buffer + xor eax, eax + mov ecx, 128 ;= 512 / 4 + rep stosd + mov edi, esp + + test edx, BIT24 ; Test for FXSAVE/FXRESTOR support. + ; edx still contains result from CPUID above + jz .2 + db 0xf, 0xae, 00000111y ;fxsave [edi] +.2: + + ;; save the exception data + push dword [ebp + 8] + + ; call the C interrupt process function + push esp ; Structure + push ebx ; vector + call ASM_PFX(InterruptProcess) + add esp, 8 + + ; skip the exception data + add esp, 4 + + ;; FX_SAVE_STATE_IA32 FxSaveState; + mov esi, esp + mov eax, 1 + cpuid ; use CPUID to determine if FXSAVE/FXRESTOR are supported + test edx, BIT24 ; Test for FXSAVE/FXRESTOR support + jz .3 + db 0xf, 0xae, 00001110y ; fxrstor [esi] +.3: + add esp, 512 + + ;; UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; + pop eax + mov dr0, eax + pop eax + mov dr1, eax + pop eax + mov dr2, eax + pop eax + mov dr3, eax + ;; skip restore of dr6. We cleared dr6 during the context save. + add esp, 4 + pop eax + mov dr7, eax + + ;; set EFlags + pop dword [ebp + 4 * 5] ; set EFLAGS in stack + + ;; UINT32 Ldtr, Tr; + ;; UINT32 Gdtr[2], Idtr[2]; + ;; Best not let anyone mess with these particular registers... + add esp, 24 + + ;; UINT32 Eip; + pop dword [ebp + 4 * 3] ; set EIP in stack + + ;; UINT32 Gs, Fs, Es, Ds, Cs, Ss; + ;; NOTE - modified segment registers could hang the debugger... We + ;; could attempt to insulate ourselves against this possibility, + ;; but that poses risks as well. + ;; + pop gs + pop fs + pop es + pop ds + pop dword [ebp + 4 * 4] ; set CS in stack + pop ss + + ;; UINT32 Cr0, Cr1, Cr2, Cr3, Cr4; + pop eax + mov cr0, eax + add esp, 4 ; skip for Cr1 + pop eax + mov cr2, eax + pop eax + mov cr3, eax + pop eax + mov cr4, eax + + ;; restore general register + pop edi + pop esi + pop dword [ebp] ; save updated ebp + pop dword [ebp + 4] ; save updated esp + pop edx + pop ecx + pop ebx + pop eax + + mov esp, ebp + pop ebp ; restore ebp maybe updated + pop esp ; restore esp maybe updated + sub esp, 4 * 3 ; restore interupt pushced stack + + iretd + diff --git a/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/Ia32/DebugException.h b/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/Ia32/DebugException.h new file mode 100644 index 0000000000..13a7d23a0d --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/Ia32/DebugException.h @@ -0,0 +1,36 @@ +/** @file + Exception defintions. + + Copyright (c) 2010, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _DEBUG_EXCEPTION_H_ +#define _DEBUG_EXCEPTION_H_ + +#define DEBUG_EXCEPT_DIVIDE_ERROR 0 +#define DEBUG_EXCEPT_DEBUG 1 +#define DEBUG_EXCEPT_NMI 2 +#define DEBUG_EXCEPT_BREAKPOINT 3 +#define DEBUG_EXCEPT_OVERFLOW 4 +#define DEBUG_EXCEPT_BOUND 5 +#define DEBUG_EXCEPT_INVALID_OPCODE 6 +#define DEBUG_EXCEPT_DOUBLE_FAULT 8 +#define DEBUG_EXCEPT_INVALID_TSS 10 +#define DEBUG_EXCEPT_SEG_NOT_PRESENT 11 +#define DEBUG_EXCEPT_STACK_FAULT 12 +#define DEBUG_EXCEPT_GP_FAULT 13 +#define DEBUG_EXCEPT_PAGE_FAULT 14 +#define DEBUG_EXCEPT_FP_ERROR 16 +#define DEBUG_EXCEPT_ALIGNMENT_CHECK 17 +#define DEBUG_EXCEPT_MACHINE_CHECK 18 +#define DEBUG_EXCEPT_SIMD 19 + +#endif diff --git a/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/X64/ArchDebugSupport.c b/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/X64/ArchDebugSupport.c new file mode 100644 index 0000000000..08cef0154f --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/X64/ArchDebugSupport.c @@ -0,0 +1,120 @@ +/** @file + Supporting functions for X64 architecture. + + Copyright (c) 2010 - 2015, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "DebugAgent.h" + +/** + Initialize IDT entries to support source level debug. + +**/ +VOID +InitializeDebugIdt ( + VOID + ) +{ + IA32_IDT_GATE_DESCRIPTOR *IdtEntry; + UINTN InterruptHandler; + IA32_DESCRIPTOR IdtDescriptor; + UINTN Index; + UINT16 CodeSegment; + UINT32 RegEdx; + + AsmReadIdtr (&IdtDescriptor); + + // + // Use current CS as the segment selector of interrupt gate in IDT + // + CodeSegment = AsmReadCs (); + + IdtEntry = (IA32_IDT_GATE_DESCRIPTOR *) IdtDescriptor.Base; + + for (Index = 0; Index < 20; Index ++) { + if (((PcdGet32 (PcdExceptionsIgnoredByDebugger) & ~(BIT1 | BIT3)) & (1 << Index)) != 0) { + // + // If the exception is masked to be reserved except for INT1 and INT3, skip it + // + continue; + } + InterruptHandler = (UINTN)&Exception0Handle + Index * ExceptionStubHeaderSize; + IdtEntry[Index].Bits.OffsetLow = (UINT16)(UINTN)InterruptHandler; + IdtEntry[Index].Bits.OffsetHigh = (UINT16)((UINTN)InterruptHandler >> 16); + IdtEntry[Index].Bits.OffsetUpper = (UINT32)((UINTN)InterruptHandler >> 32); + IdtEntry[Index].Bits.Selector = CodeSegment; + IdtEntry[Index].Bits.GateType = IA32_IDT_GATE_TYPE_INTERRUPT_32; + } + + InterruptHandler = (UINTN) &TimerInterruptHandle; + IdtEntry[DEBUG_TIMER_VECTOR].Bits.OffsetLow = (UINT16)(UINTN)InterruptHandler; + IdtEntry[DEBUG_TIMER_VECTOR].Bits.OffsetHigh = (UINT16)((UINTN)InterruptHandler >> 16); + IdtEntry[DEBUG_TIMER_VECTOR].Bits.OffsetUpper = (UINT32)((UINTN)InterruptHandler >> 32); + IdtEntry[DEBUG_TIMER_VECTOR].Bits.Selector = CodeSegment; + IdtEntry[DEBUG_TIMER_VECTOR].Bits.GateType = IA32_IDT_GATE_TYPE_INTERRUPT_32; + + // + // If the CPU supports Debug Extensions(CPUID:01 EDX:BIT2), then + // Set DE flag in CR4 to enable IO breakpoint + // + AsmCpuid (1, NULL, NULL, NULL, &RegEdx); + if ((RegEdx & BIT2) != 0) { + AsmWriteCr4 (AsmReadCr4 () | BIT3); + } +} + +/** + Retrieve exception handler from IDT table by ExceptionNum. + + @param[in] ExceptionNum Exception number + + @return Exception handler + +**/ +VOID * +GetExceptionHandlerInIdtEntry ( + IN UINTN ExceptionNum + ) +{ + IA32_IDT_GATE_DESCRIPTOR *IdtEntry; + IA32_DESCRIPTOR IdtDescriptor; + + AsmReadIdtr (&IdtDescriptor); + IdtEntry = (IA32_IDT_GATE_DESCRIPTOR *) IdtDescriptor.Base; + + return (VOID *) (IdtEntry[ExceptionNum].Bits.OffsetLow | + (((UINTN)IdtEntry[ExceptionNum].Bits.OffsetHigh) << 16) | + (((UINTN)IdtEntry[ExceptionNum].Bits.OffsetUpper) << 32)); +} + +/** + Set exception handler in IDT table by ExceptionNum. + + @param[in] ExceptionNum Exception number + @param[in] ExceptionHandler Exception Handler to be set + +**/ +VOID +SetExceptionHandlerInIdtEntry ( + IN UINTN ExceptionNum, + IN VOID *ExceptionHandler + ) +{ + IA32_IDT_GATE_DESCRIPTOR *IdtEntry; + IA32_DESCRIPTOR IdtDescriptor; + + AsmReadIdtr (&IdtDescriptor); + IdtEntry = (IA32_IDT_GATE_DESCRIPTOR *) IdtDescriptor.Base; + + IdtEntry[ExceptionNum].Bits.OffsetLow = (UINT16)(UINTN)ExceptionHandler; + IdtEntry[ExceptionNum].Bits.OffsetHigh = (UINT16)((UINTN)ExceptionHandler >> 16); + IdtEntry[ExceptionNum].Bits.OffsetUpper = (UINT32)((UINTN)ExceptionHandler >> 32); +} diff --git a/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/X64/ArchDebugSupport.h b/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/X64/ArchDebugSupport.h new file mode 100644 index 0000000000..26e6d8a7d9 --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/X64/ArchDebugSupport.h @@ -0,0 +1,27 @@ +/** @file + X64 specific defintions for debug agent library instance. + + Copyright (c) 2010 - 2012, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _ARCH_DEBUG_SUPPORT_H_ +#define _ARCH_DEBUG_SUPPORT_H_ + +#include "ProcessorContext.h" +#include "TransferProtocol.h" + +#define DEBUG_SW_BREAKPOINT_SYMBOL 0xcc +#define DEBUG_ARCH_SYMBOL DEBUG_DATA_BREAK_CPU_ARCH_X64 + +typedef DEBUG_DATA_X64_FX_SAVE_STATE DEBUG_DATA_FX_SAVE_STATE; +typedef DEBUG_DATA_X64_SYSTEM_CONTEXT DEBUG_CPU_CONTEXT; + +#endif diff --git a/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/X64/AsmFuncs.S b/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/X64/AsmFuncs.S new file mode 100644 index 0000000000..cdda41fb27 --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/X64/AsmFuncs.S @@ -0,0 +1,431 @@ +#------------------------------------------------------------------------------ +# +# Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.
+# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php. +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# Module Name: +# +# AsmFuncs.S +# +# Abstract: +# +# Debug interrupt handle functions. +# +#------------------------------------------------------------------------------ + +#include "DebugException.h" + +ASM_GLOBAL ASM_PFX(InterruptProcess) + +ASM_GLOBAL ASM_PFX(Exception0Handle) +ASM_GLOBAL ASM_PFX(ExceptionStubHeaderSize) +ASM_GLOBAL ASM_PFX(TimerInterruptHandle) +ASM_GLOBAL ASM_PFX(CommonEntry) + +.macro AGENT_HANDLER_SIGNATURE + .byte 0x41, 0x47, 0x54, 0x48 # AGENT_HANDLER_SIGNATURE SIGNATURE_32('A','G','T','H') +.endm + +.data + +ASM_PFX(ExceptionStubHeaderSize): .long ASM_PFX(Exception1Handle) - ASM_PFX(Exception0Handle) + + +.text + +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception0Handle): + cli + pushq %rcx + mov $0, %rcx + jmp ASM_PFX(CommonEntry) +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception1Handle): + cli + pushq %rcx + mov $1, %rcx + jmp ASM_PFX(CommonEntry) +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception2Handle): + cli + pushq %rcx + mov $2, %rcx + jmp ASM_PFX(CommonEntry) +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception3Handle): + cli + pushq %rcx + mov $3, %rcx + jmp ASM_PFX(CommonEntry) +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception4Handle): + cli + pushq %rcx + mov $4, %rcx + jmp ASM_PFX(CommonEntry) +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception5Handle): + cli + pushq %rcx + mov $5, %rcx + jmp ASM_PFX(CommonEntry) +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception6Handle): + cli + pushq %rcx + mov $6, %rcx + jmp ASM_PFX(CommonEntry) +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception7Handle): + cli + pushq %rcx + mov $7, %rcx + jmp ASM_PFX(CommonEntry) +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception8Handle): + cli + pushq %rcx + mov $8, %rcx + jmp ASM_PFX(CommonEntry) +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception9Handle): + cli + pushq %rcx + mov $9, %rcx + jmp ASM_PFX(CommonEntry) +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception10Handle): + cli + pushq %rcx + mov $10, %rcx + jmp ASM_PFX(CommonEntry) +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception11Handle): + cli + pushq %rcx + mov $11, %rcx + jmp ASM_PFX(CommonEntry) +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception12Handle): + cli + pushq %rcx + mov $12, %rcx + jmp ASM_PFX(CommonEntry) +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception13Handle): + cli + pushq %rcx + mov $13, %rcx + jmp ASM_PFX(CommonEntry) +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception14Handle): + cli + pushq %rcx + mov $14, %rcx + jmp ASM_PFX(CommonEntry) +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception15Handle): + cli + pushq %rcx + mov $15, %rcx + jmp ASM_PFX(CommonEntry) +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception16Handle): + cli + pushq %rcx + mov $16, %rcx + jmp ASM_PFX(CommonEntry) +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception17Handle): + cli + pushq %rcx + mov $17, %rcx + jmp ASM_PFX(CommonEntry) +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception18Handle): + cli + pushq %rcx + mov $18, %rcx + jmp ASM_PFX(CommonEntry) +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception19Handle): + cli + pushq %rcx + mov $19, %rcx + jmp ASM_PFX(CommonEntry) +AGENT_HANDLER_SIGNATURE +ASM_PFX(TimerInterruptHandle): + cli + pushq %rcx + mov $32, %rcx + jmp ASM_PFX(CommonEntry) + + +ASM_PFX(CommonEntry): + +#---------------------------------------; +# CommonInterruptEntry ; +#---------------------------------------; +# The follow algorithm is used for the common interrupt routine. + +# +# +---------------------+ <-- 16-byte aligned ensured by processor +# + Old SS + +# +---------------------+ +# + Old RSP + +# +---------------------+ +# + RFlags + +# +---------------------+ +# + CS + +# +---------------------+ +# + RIP + +# +---------------------+ +# + Error Code + +# +---------------------+ +# + RCX / Vector Number + +# +---------------------+ +# + RBP + +# +---------------------+ <-- RBP, 16-byte aligned +# + +# We need to determine if any extra data was pushed by the exception + cmpq $DEBUG_EXCEPT_DOUBLE_FAULT, %rcx + je NoExtrPush + cmpq $DEBUG_EXCEPT_INVALID_TSS, %rcx + je NoExtrPush + cmpq $DEBUG_EXCEPT_SEG_NOT_PRESENT, %rcx + je NoExtrPush + cmpq $DEBUG_EXCEPT_STACK_FAULT, %rcx + je NoExtrPush + cmpq $DEBUG_EXCEPT_GP_FAULT, %rcx + je NoExtrPush + cmpq $DEBUG_EXCEPT_PAGE_FAULT, %rcx + je NoExtrPush + cmpq $DEBUG_EXCEPT_ALIGNMENT_CHECK, %rcx + je NoExtrPush + + pushq (%rsp) + movq $0, 8(%rsp) + +NoExtrPush: + # + # All interrupt handlers are invoked through interrupt gates, so + # IF flag automatically cleared at the entry point + pushq %rbp + movq %rsp, %rbp + + # + # Since here the stack pointer is 16-byte aligned, so + # EFI_FX_SAVE_STATE_X64 of EFI_SYSTEM_CONTEXT_x64 + # is 16-byte aligned + # + +## UINT64 R8, R9, R10, R11, R12, R13, R14, R15; + pushq %r15 + pushq %r14 + pushq %r13 + pushq %r12 + pushq %r11 + pushq %r10 + pushq %r9 + pushq %r8 + + movq %cr8, %r8 + pushq %r8 + +## UINT64 Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax; + pushq %rax + pushq %rbx + pushq 8(%rbp) # original rcx + pushq %rdx + pushq 48(%rbp) # original rsp + pushq (%rbp) # original rbp + pushq %rsi + pushq %rdi + +## UINT64 Cr0, Cr1, Cr2, Cr3, Cr4; + movq %cr4, %rax + orq $0x208, %rax + movq %rax, %cr4 + pushq %rax + movq %cr3, %rax + pushq %rax + movq %cr2, %rax + pushq %rax + xorq %rax, %rax + pushq %rax + movq %cr0, %rax + pushq %rax + +## UINT64 Gs, Fs, Es, Ds, Cs, Ss; insure high 16 bits of each is zero + xorq %rax, %rax # set rax to 0 + movzwq 56(%rbp), %rax +# movq %ss, %rax + pushq %rax + movzwq 32(%rbp), %rax +# movq %cs, %rax + pushq %rax + mov %ds, %rax + pushq %rax + mov %es, %rax + pushq %rax + mov %fs, %rax + pushq %rax + mov %gs, %rax + pushq %rax + +## UINT64 Rip; + pushq 24(%rbp) + +## UINT64 Gdtr[2], Idtr[2]; + subq $16, %rsp + sidt (%rsp) + subq $16, %rsp + sgdt (%rsp) + +## UINT64 Ldtr, Tr; + xorq %rax, %rax + strw %ax + pushq %rax + sldtw %ax + pushq %rax + +## UINT64 RFlags; + pushq 40(%rbp) + +## UINT64 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; + movq %dr7, %rax + pushq %rax +## clear Dr7 while executing debugger itself + xorq %rax, %rax + movq %rax, %dr7 + + movq %dr6, %rax + pushq %rax +## insure all status bits in dr6 are clear... + xorq %rax, %rax + movq %rax, %dr6 + + movq %dr3, %rax + pushq %rax + movq %dr2, %rax + pushq %rax + movq %dr1, %rax + pushq %rax + movq %dr0, %rax + pushq %rax + +## FX_SAVE_STATE_X64 FxSaveState; + subq $512, %rsp + movq %rsp, %rdi + .byte 0x0f, 0xae, 0b00000111 + +## save the exception data; + pushq 16(%rbp) + +## Clear Direction Flag + cld + +## Prepare parameter and call +# movq 8(%rbp), %rcx + movq %rsp, %rdx + movq %rcx, %r15 # save vector in r15 + # + # Per X64 calling convention, allocate maximum parameter stack space + # and make sure RSP is 16-byte aligned + # + subq $(32 + 8), %rsp + call ASM_PFX(InterruptProcess) + addq $(32 + 8), %rsp + +## skip the exception data; + addq $8, %rsp + +## FX_SAVE_STATE_X64 FxSaveState; + + movq %rsp, %rsi + .byte 0x0f, 0xae, 0b00001110 + addq $512, %rsp + +## UINT64 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; + popq %rax + movq %rax, %dr0 + popq %rax + movq %rax, %dr1 + popq %rax + movq %rax, %dr2 + popq %rax + movq %rax, %dr3 +## skip restore of dr6. We cleared dr6 during the context save. + addq $8, %rsp + popq %rax + movq %rax, %dr7 + +## UINT64 RFlags; + popq 40(%rbp) + +## UINT64 Ldtr, Tr; +## UINT64 Gdtr[2], Idtr[2]; +## Best not let anyone mess with these particular registers... + addq $48, %rsp + +## UINT64 Rip; + popq 24(%rbp) + +## UINT64 Gs, Fs, Es, Ds, Cs, Ss; + popq %rax + # mov gs, rax ; not for gs + popq %rax + # mov fs, rax ; not for fs + # (X64 will not use fs and gs, so we do not restore it) + popq %rax + mov %rax, %es + popq %rax + mov %rax, %ds + popq 32(%rbp) + popq 56(%rbp) + +## UINT64 Cr0, Cr1, Cr2, Cr3, Cr4, Cr8; + popq %rax + movq %rax, %cr0 + addq $8, %rsp + popq %rax + movq %rax, %cr2 + popq %rax + movq %rax, %cr3 + popq %rax + movq %rax, %cr4 + +## UINT64 Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax; +## UINT64 R8, R9, R10, R11, R12, R13, R14, R15; + popq %rdi + popq %rsi + addq $8, %rsp + addq $8, %rsp + popq %rdx + popq %rcx + popq %rbx + popq %rax + + popq %r8 + movq %r8, %cr8 + + popq %r8 + popq %r9 + popq %r10 + popq %r11 + popq %r12 + popq %r13 + popq %r14 + popq %r15 + + movq %rbp, %rsp + popq %rbp + addq $16, %rsp + iretq diff --git a/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/X64/AsmFuncs.asm b/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/X64/AsmFuncs.asm new file mode 100644 index 0000000000..6d01f64d0f --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/X64/AsmFuncs.asm @@ -0,0 +1,405 @@ +;------------------------------------------------------------------------------ +; +; Copyright (c) 2010 - 2015, Intel Corporation. All rights reserved.
+; This program and the accompanying materials +; are licensed and made available under the terms and conditions of the BSD License +; which accompanies this distribution. The full text of the license may be found at +; http://opensource.org/licenses/bsd-license.php. +; +; THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +; WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +; +; Module Name: +; +; AsmFuncs.asm +; +; Abstract: +; +; Debug interrupt handle functions. +; +;------------------------------------------------------------------------------ + +#include "DebugException.h" + + +externdef InterruptProcess:near + +data SEGMENT + +public Exception0Handle, TimerInterruptHandle, ExceptionStubHeaderSize + +AGENT_HANDLER_SIGNATURE MACRO + db 41h, 47h, 54h, 48h ; SIGNATURE_32('A','G','T','H') +ENDM + +ExceptionStubHeaderSize dd Exception1Handle - Exception0Handle ; +CommonEntryAddr dq CommonEntry ; + +.code + +AGENT_HANDLER_SIGNATURE +Exception0Handle: + cli + push rcx + mov rcx, 0 + jmp qword ptr [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception1Handle: + cli + push rcx + mov rcx, 1 + jmp qword ptr [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception2Handle: + cli + push rcx + mov rcx, 2 + jmp qword ptr [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception3Handle: + cli + push rcx + mov rcx, 3 + jmp qword ptr [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception4Handle: + cli + push rcx + mov rcx, 4 + jmp qword ptr [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception5Handle: + cli + push rcx + mov rcx, 5 + jmp qword ptr [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception6Handle: + cli + push rcx + mov rcx, 6 + jmp qword ptr [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception7Handle: + cli + push rcx + mov rcx, 7 + jmp qword ptr [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception8Handle: + cli + push rcx + mov rcx, 8 + jmp qword ptr [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception9Handle: + cli + push rcx + mov rcx, 9 + jmp qword ptr [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception10Handle: + cli + push rcx + mov rcx, 10 + jmp qword ptr [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception11Handle: + cli + push rcx + mov rcx, 11 + jmp qword ptr [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception12Handle: + cli + push rcx + mov rcx, 12 + jmp qword ptr [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception13Handle: + cli + push rcx + mov rcx, 13 + jmp qword ptr [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception14Handle: + cli + push rcx + mov rcx, 14 + jmp qword ptr [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception15Handle: + cli + push rcx + mov rcx, 15 + jmp qword ptr [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception16Handle: + cli + push rcx + mov rcx, 16 + jmp qword ptr [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception17Handle: + cli + push rcx + mov rcx, 17 + jmp qword ptr [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception18Handle: + cli + push rcx + mov rcx, 18 + jmp qword ptr [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception19Handle: + cli + push rcx + mov rcx, 19 + jmp qword ptr [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +TimerInterruptHandle: + cli + push rcx + mov rcx, 32 + jmp qword ptr [CommonEntryAddr] + +CommonEntry: + ; We need to determine if any extra data was pushed by the exception + cmp rcx, DEBUG_EXCEPT_DOUBLE_FAULT + je NoExtrPush + cmp rcx, DEBUG_EXCEPT_INVALID_TSS + je NoExtrPush + cmp rcx, DEBUG_EXCEPT_SEG_NOT_PRESENT + je NoExtrPush + cmp rcx, DEBUG_EXCEPT_STACK_FAULT + je NoExtrPush + cmp rcx, DEBUG_EXCEPT_GP_FAULT + je NoExtrPush + cmp rcx, DEBUG_EXCEPT_PAGE_FAULT + je NoExtrPush + cmp rcx, DEBUG_EXCEPT_ALIGNMENT_CHECK + je NoExtrPush + + push [rsp] + mov qword ptr [rsp + 8], 0 + +NoExtrPush: + push rbp + mov rbp, rsp + + ; store UINT64 r8, r9, r10, r11, r12, r13, r14, r15; + push r15 + push r14 + push r13 + push r12 + push r11 + push r10 + push r9 + push r8 + + mov r8, cr8 + push r8 + + ; store UINT64 Rdi, Rsi, Rbp, Rsp, Rdx, Rcx, Rbx, Rax; + push rax + push rbx + push qword ptr [rbp + 8] ; original rcx + push rdx + push qword ptr [rbp + 6 * 8] ; original rsp + push qword ptr [rbp] ; original rbp + push rsi + push rdi + + ;; UINT32 Cr0, Cr1, Cr2, Cr3, Cr4; + ;; insure FXSAVE/FXRSTOR is enabled in CR4... + ;; ... while we're at it, make sure DE is also enabled... + mov rax, cr4 + or rax, 208h + mov cr4, rax + push rax + mov rax, cr3 + push rax + mov rax, cr2 + push rax + push 0 + mov rax, cr0 + push rax + + xor rax, rax + mov rax, Ss + push rax + mov rax, Cs + push rax + mov rax, Ds + push rax + mov rax, Es + push rax + mov rax, Fs + push rax + mov rax, Gs + push rax + + ;; EIP + mov rax, [rbp + 8 * 3] ; EIP + push rax + + ;; UINT64 Gdtr[2], Idtr[2]; + sub rsp, 16 + sidt fword ptr [rsp] + sub rsp, 16 + sgdt fword ptr [rsp] + + ;; UINT64 Ldtr, Tr; + xor rax, rax + str ax + push rax + sldt ax + push rax + + ;; EFlags + mov rax, [rbp + 8 * 5] + push rax + + ;; UINT64 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; + mov rax, dr7 + push rax + + ;; clear Dr7 while executing debugger itself + xor rax, rax + mov dr7, rax + + ;; Dr6 + mov rax, dr6 + push rax + + ;; insure all status bits in dr6 are clear... + xor rax, rax + mov dr6, rax + + mov rax, dr3 + push rax + mov rax, dr2 + push rax + mov rax, dr1 + push rax + mov rax, dr0 + push rax + + ;; Clear Direction Flag + cld + + sub rsp, 512 + mov rdi, rsp + ;; Clear the buffer + xor rax, rax + push rcx + mov rcx, 64 ;= 512 / 8 + rep stosq + pop rcx + mov rdi, rsp + db 0fh, 0aeh, 00000111y ;fxsave [rdi] + + ;; save the exception data + push qword ptr [rbp + 16] + + ; call the C interrupt process function + mov rdx, rsp ; Structure + mov r15, rcx ; save vector in r15 + + ; + ; Per X64 calling convention, allocate maximum parameter stack space + ; and make sure RSP is 16-byte aligned + ; + sub rsp, 32 + 8 + call InterruptProcess + add rsp, 32 + 8 + + ;; skip the exception data + add rsp, 8 + + mov rsi, rsp + db 0fh, 0aeh, 00001110y ; fxrstor [rsi] + add rsp, 512 + + ;; UINT64 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; + pop rax + mov dr0, rax + pop rax + mov dr1, rax + pop rax + mov dr2, rax + pop rax + mov dr3, rax + ;; skip restore of dr6. We cleared dr6 during the context save. + add rsp, 8 + pop rax + mov dr7, rax + + ;; set EFlags + pop qword ptr [rbp + 8 * 5] + + ;; UINT64 Ldtr, Tr; + ;; UINT64 Gdtr[2], Idtr[2]; + ;; Best not let anyone mess with these particular registers... + add rsp, 24 * 2 + + ;; UINT64 Eip; + pop qword ptr [rbp + 8 * 3] ; set EIP in stack + + ;; UINT64 Gs, Fs, Es, Ds, Cs, Ss; + ;; NOTE - modified segment registers could hang the debugger... We + ;; could attempt to insulate ourselves against this possibility, + ;; but that poses risks as well. + ;; + pop rax + pop rax + pop rax + mov es, rax + pop rax + mov ds, rax + pop qword ptr [rbp + 8 * 4] ; Set CS in stack + pop rax + mov ss, rax + + ;; UINT64 Cr0, Cr1, Cr2, Cr3, Cr4; + pop rax + mov cr0, rax + add rsp, 8 ; skip for Cr1 + pop rax + mov cr2, rax + pop rax + mov cr3, rax + pop rax + mov cr4, rax + + ;; restore general register + pop rdi + pop rsi + add rsp, 8 ; skip rbp + add rsp, 8 ; skip rsp + pop rdx + pop rcx + pop rbx + pop rax + + pop r8 + mov cr8, r8 + + ; store UINT64 r8, r9, r10, r11, r12, r13, r14, r15; + pop r8 + pop r9 + pop r10 + pop r11 + pop r12 + pop r13 + pop r14 + pop r15 + + mov rsp, rbp + pop rbp + add rsp, 16 ; skip rcx and error code + + iretq + +END diff --git a/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/X64/AsmFuncs.nasm b/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/X64/AsmFuncs.nasm new file mode 100644 index 0000000000..dcb5f9dbb9 --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/X64/AsmFuncs.nasm @@ -0,0 +1,405 @@ +;------------------------------------------------------------------------------ +; +; Copyright (c) 2016, Intel Corporation. All rights reserved.
+; This program and the accompanying materials +; are licensed and made available under the terms and conditions of the BSD License +; which accompanies this distribution. The full text of the license may be found at +; http://opensource.org/licenses/bsd-license.php. +; +; THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +; WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +; +; Module Name: +; +; AsmFuncs.nasm +; +; Abstract: +; +; Debug interrupt handle functions. +; +;------------------------------------------------------------------------------ + +#include "DebugException.h" + +SECTION .data + +extern ASM_PFX(InterruptProcess) +global ASM_PFX(Exception0Handle) +global ASM_PFX(TimerInterruptHandle) +global ASM_PFX(ExceptionStubHeaderSize) + +%macro AGENT_HANDLER_SIGNATURE 0 + db 0x41, 0x47, 0x54, 0x48 ; SIGNATURE_32('A','G','T','H') +%endmacro + +ASM_PFX(ExceptionStubHeaderSize): dd Exception1Handle - ASM_PFX(Exception0Handle) ; +CommonEntryAddr: dq CommonEntry ; + +DEFAULT REL +SECTION .text + +AGENT_HANDLER_SIGNATURE +ASM_PFX(Exception0Handle): + cli + push rcx + mov rcx, dword 0 + jmp qword [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception1Handle: + cli + push rcx + mov rcx, dword 1 + jmp qword [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception2Handle: + cli + push rcx + mov rcx, dword 2 + jmp qword [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception3Handle: + cli + push rcx + mov rcx, dword 3 + jmp qword [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception4Handle: + cli + push rcx + mov rcx, dword 4 + jmp qword [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception5Handle: + cli + push rcx + mov rcx, dword 5 + jmp qword [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception6Handle: + cli + push rcx + mov rcx, dword 6 + jmp qword [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception7Handle: + cli + push rcx + mov rcx, dword 7 + jmp qword [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception8Handle: + cli + push rcx + mov rcx, dword 8 + jmp qword [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception9Handle: + cli + push rcx + mov rcx, dword 9 + jmp qword [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception10Handle: + cli + push rcx + mov rcx, dword 10 + jmp qword [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception11Handle: + cli + push rcx + mov rcx, dword 11 + jmp qword [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception12Handle: + cli + push rcx + mov rcx, dword 12 + jmp qword [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception13Handle: + cli + push rcx + mov rcx, dword 13 + jmp qword [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception14Handle: + cli + push rcx + mov rcx, dword 14 + jmp qword [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception15Handle: + cli + push rcx + mov rcx, dword 15 + jmp qword [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception16Handle: + cli + push rcx + mov rcx, dword 16 + jmp qword [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception17Handle: + cli + push rcx + mov rcx, dword 17 + jmp qword [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception18Handle: + cli + push rcx + mov rcx, dword 18 + jmp qword [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +Exception19Handle: + cli + push rcx + mov rcx, dword 19 + jmp qword [CommonEntryAddr] +AGENT_HANDLER_SIGNATURE +ASM_PFX(TimerInterruptHandle): + cli + push rcx + mov rcx, dword 32 + jmp qword [CommonEntryAddr] + +CommonEntry: + ; We need to determine if any extra data was pushed by the exception + cmp rcx, DEBUG_EXCEPT_DOUBLE_FAULT + je NoExtrPush + cmp rcx, DEBUG_EXCEPT_INVALID_TSS + je NoExtrPush + cmp rcx, DEBUG_EXCEPT_SEG_NOT_PRESENT + je NoExtrPush + cmp rcx, DEBUG_EXCEPT_STACK_FAULT + je NoExtrPush + cmp rcx, DEBUG_EXCEPT_GP_FAULT + je NoExtrPush + cmp rcx, DEBUG_EXCEPT_PAGE_FAULT + je NoExtrPush + cmp rcx, DEBUG_EXCEPT_ALIGNMENT_CHECK + je NoExtrPush + + push qword [rsp] + mov qword [rsp + 8], 0 + +NoExtrPush: + push rbp + mov rbp, rsp + + ; store UINT64 r8, r9, r10, r11, r12, r13, r14, r15; + push r15 + push r14 + push r13 + push r12 + push r11 + push r10 + push r9 + push r8 + + mov r8, cr8 + push r8 + + ; store UINT64 Rdi, Rsi, Rbp, Rsp, Rdx, Rcx, Rbx, Rax; + push rax + push rbx + push qword [rbp + 8] ; original rcx + push rdx + push qword [rbp + 6 * 8] ; original rsp + push qword [rbp] ; original rbp + push rsi + push rdi + + ;; UINT32 Cr0, Cr1, Cr2, Cr3, Cr4; + ;; insure FXSAVE/FXRSTOR is enabled in CR4... + ;; ... while we're at it, make sure DE is also enabled... + mov rax, cr4 + or rax, 0x208 + mov cr4, rax + push rax + mov rax, cr3 + push rax + mov rax, cr2 + push rax + push 0 + mov rax, cr0 + push rax + + xor rax, rax + mov rax, Ss + push rax + mov rax, Cs + push rax + mov rax, Ds + push rax + mov rax, Es + push rax + mov rax, Fs + push rax + mov rax, Gs + push rax + + ;; EIP + mov rax, [rbp + 8 * 3] ; EIP + push rax + + ;; UINT64 Gdtr[2], Idtr[2]; + sub rsp, 16 + sidt [rsp] + sub rsp, 16 + sgdt [rsp] + + ;; UINT64 Ldtr, Tr; + xor rax, rax + str ax + push rax + sldt ax + push rax + + ;; EFlags + mov rax, [rbp + 8 * 5] + push rax + + ;; UINT64 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; + mov rax, dr7 + push rax + + ;; clear Dr7 while executing debugger itself + xor rax, rax + mov dr7, rax + + ;; Dr6 + mov rax, dr6 + push rax + + ;; insure all status bits in dr6 are clear... + xor rax, rax + mov dr6, rax + + mov rax, dr3 + push rax + mov rax, dr2 + push rax + mov rax, dr1 + push rax + mov rax, dr0 + push rax + + ;; Clear Direction Flag + cld + + sub rsp, 512 + mov rdi, rsp + ;; Clear the buffer + xor rax, rax + push rcx + mov rcx, dword 64 ;= 512 / 8 + rep stosq + pop rcx + mov rdi, rsp + db 0xf, 0xae, 00000111y ;fxsave [rdi] + + ;; save the exception data + push qword [rbp + 16] + + ; call the C interrupt process function + mov rdx, rsp ; Structure + mov r15, rcx ; save vector in r15 + + ; + ; Per X64 calling convention, allocate maximum parameter stack space + ; and make sure RSP is 16-byte aligned + ; + sub rsp, 32 + 8 + call ASM_PFX(InterruptProcess) + add rsp, 32 + 8 + + ;; skip the exception data + add rsp, 8 + + mov rsi, rsp + db 0xf, 0xae, 00001110y ; fxrstor [rsi] + add rsp, 512 + + ;; UINT64 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; + pop rax + mov dr0, rax + pop rax + mov dr1, rax + pop rax + mov dr2, rax + pop rax + mov dr3, rax + ;; skip restore of dr6. We cleared dr6 during the context save. + add rsp, 8 + pop rax + mov dr7, rax + + ;; set EFlags + pop qword [rbp + 8 * 5] + + ;; UINT64 Ldtr, Tr; + ;; UINT64 Gdtr[2], Idtr[2]; + ;; Best not let anyone mess with these particular registers... + add rsp, 24 * 2 + + ;; UINT64 Eip; + pop qword [rbp + 8 * 3] ; set EIP in stack + + ;; UINT64 Gs, Fs, Es, Ds, Cs, Ss; + ;; NOTE - modified segment registers could hang the debugger... We + ;; could attempt to insulate ourselves against this possibility, + ;; but that poses risks as well. + ;; + pop rax + pop rax + pop rax + mov es, rax + pop rax + mov ds, rax + pop qword [rbp + 8 * 4] ; Set CS in stack + pop rax + mov ss, rax + + ;; UINT64 Cr0, Cr1, Cr2, Cr3, Cr4; + pop rax + mov cr0, rax + add rsp, 8 ; skip for Cr1 + pop rax + mov cr2, rax + pop rax + mov cr3, rax + pop rax + mov cr4, rax + + ;; restore general register + pop rdi + pop rsi + add rsp, 8 ; skip rbp + add rsp, 8 ; skip rsp + pop rdx + pop rcx + pop rbx + pop rax + + pop r8 + mov cr8, r8 + + ; store UINT64 r8, r9, r10, r11, r12, r13, r14, r15; + pop r8 + pop r9 + pop r10 + pop r11 + pop r12 + pop r13 + pop r14 + pop r15 + + mov rsp, rbp + pop rbp + add rsp, 16 ; skip rcx and error code + + iretq + diff --git a/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/X64/DebugException.h b/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/X64/DebugException.h new file mode 100644 index 0000000000..13a7d23a0d --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugAgent/DebugAgentCommon/X64/DebugException.h @@ -0,0 +1,36 @@ +/** @file + Exception defintions. + + Copyright (c) 2010, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _DEBUG_EXCEPTION_H_ +#define _DEBUG_EXCEPTION_H_ + +#define DEBUG_EXCEPT_DIVIDE_ERROR 0 +#define DEBUG_EXCEPT_DEBUG 1 +#define DEBUG_EXCEPT_NMI 2 +#define DEBUG_EXCEPT_BREAKPOINT 3 +#define DEBUG_EXCEPT_OVERFLOW 4 +#define DEBUG_EXCEPT_BOUND 5 +#define DEBUG_EXCEPT_INVALID_OPCODE 6 +#define DEBUG_EXCEPT_DOUBLE_FAULT 8 +#define DEBUG_EXCEPT_INVALID_TSS 10 +#define DEBUG_EXCEPT_SEG_NOT_PRESENT 11 +#define DEBUG_EXCEPT_STACK_FAULT 12 +#define DEBUG_EXCEPT_GP_FAULT 13 +#define DEBUG_EXCEPT_PAGE_FAULT 14 +#define DEBUG_EXCEPT_FP_ERROR 16 +#define DEBUG_EXCEPT_ALIGNMENT_CHECK 17 +#define DEBUG_EXCEPT_MACHINE_CHECK 18 +#define DEBUG_EXCEPT_SIMD 19 + +#endif diff --git a/Core/SourceLevelDebugPkg/Library/DebugAgent/DxeDebugAgent/DxeDebugAgentLib.c b/Core/SourceLevelDebugPkg/Library/DebugAgent/DxeDebugAgent/DxeDebugAgentLib.c new file mode 100644 index 0000000000..c74a1f6be3 --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugAgent/DxeDebugAgent/DxeDebugAgentLib.c @@ -0,0 +1,550 @@ +/** @file + Debug Agent library implementition for Dxe Core and Dxr modules. + + Copyright (c) 2010 - 2017, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "DxeDebugAgentLib.h" + +DEBUG_AGENT_MAILBOX mMailbox; +DEBUG_AGENT_MAILBOX *mMailboxPointer = NULL; +IA32_IDT_GATE_DESCRIPTOR mIdtEntryTable[33]; +BOOLEAN mDxeCoreFlag = FALSE; +BOOLEAN mMultiProcessorDebugSupport = FALSE; +VOID *mSavedIdtTable = NULL; +UINTN mSaveIdtTableSize = 0; +BOOLEAN mDebugAgentInitialized = FALSE; +BOOLEAN mSkipBreakpoint = FALSE; + +/** + Check if debug agent support multi-processor. + + @retval TRUE Multi-processor is supported. + @retval FALSE Multi-processor is not supported. + +**/ +BOOLEAN +MultiProcessorDebugSupport ( + VOID + ) +{ + return mMultiProcessorDebugSupport; +} + +/** + Internal constructor worker function. + + It will register one callback function on EFI PCD Protocol. + It will allocate the NVS memory to store Mailbox and install configuration table + in system table to store its pointer. + +**/ +VOID +InternalConstructorWorker ( + VOID + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS Address; + BOOLEAN DebugTimerInterruptState; + DEBUG_AGENT_MAILBOX *Mailbox; + DEBUG_AGENT_MAILBOX *NewMailbox; + EFI_HOB_GUID_TYPE *GuidHob; + EFI_VECTOR_HANDOFF_INFO *VectorHandoffInfo; + + // + // Check persisted vector handoff info + // + Status = EFI_SUCCESS; + GuidHob = GetFirstGuidHob (&gEfiVectorHandoffInfoPpiGuid); + if (GuidHob != NULL && !mDxeCoreFlag) { + // + // Check if configuration table is installed or not if GUIDed HOB existed, + // only when Debug Agent is not linked by DXE Core + // + Status = EfiGetSystemConfigurationTable (&gEfiVectorHandoffTableGuid, (VOID **) &VectorHandoffInfo); + } + if (GuidHob == NULL || Status != EFI_SUCCESS) { + // + // Install configuration table for persisted vector handoff info if GUIDed HOB cannot be found or + // configuration table does not exist + // + Status = gBS->InstallConfigurationTable (&gEfiVectorHandoffTableGuid, (VOID *) &mVectorHandoffInfoDebugAgent[0]); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "DebugAgent: Cannot install configuration table for persisted vector handoff info!\n")); + CpuDeadLoop (); + } + } + + // + // Install EFI Serial IO protocol on debug port + // + InstallSerialIo (); + + Address = 0; + Status = gBS->AllocatePages ( + AllocateAnyPages, + EfiACPIMemoryNVS, + EFI_SIZE_TO_PAGES (sizeof(DEBUG_AGENT_MAILBOX) + PcdGet16(PcdDebugPortHandleBufferSize)), + &Address + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "DebugAgent: Cannot install configuration table for mailbox!\n")); + CpuDeadLoop (); + } + + DebugTimerInterruptState = SaveAndSetDebugTimerInterrupt (FALSE); + + NewMailbox = (DEBUG_AGENT_MAILBOX *) (UINTN) Address; + // + // Copy Mailbox and Debug Port Handle buffer to new location in ACPI NVS memory, because original Mailbox + // and Debug Port Handle buffer may be free at runtime, SMM debug agent needs to access them + // + Mailbox = GetMailboxPointer (); + CopyMem (NewMailbox, Mailbox, sizeof (DEBUG_AGENT_MAILBOX)); + CopyMem (NewMailbox + 1, (VOID *)(UINTN)Mailbox->DebugPortHandle, PcdGet16(PcdDebugPortHandleBufferSize)); + // + // Update Debug Port Handle in new Mailbox + // + UpdateMailboxContent (NewMailbox, DEBUG_MAILBOX_DEBUG_PORT_HANDLE_INDEX, (UINT64)(UINTN)(NewMailbox + 1)); + mMailboxPointer = NewMailbox; + + DebugTimerInterruptState = SaveAndSetDebugTimerInterrupt (DebugTimerInterruptState); + + Status = gBS->InstallConfigurationTable (&gEfiDebugAgentGuid, (VOID *) mMailboxPointer); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "DebugAgent: Failed to install configuration for mailbox!\n")); + CpuDeadLoop (); + } +} + +/** + Debug Agent constructor function. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval RETURN_SUCCESS When this function completed. + +**/ +RETURN_STATUS +EFIAPI +DxeDebugAgentLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + if (mDxeCoreFlag) { + // + // Invoke internal constructor function only when DXE core links this library instance + // + InternalConstructorWorker (); + } + + return RETURN_SUCCESS; +} + +/** + Get the pointer to Mailbox from the configuration table. + + @return Pointer to Mailbox. + +**/ +DEBUG_AGENT_MAILBOX * +GetMailboxFromConfigurationTable ( + VOID + ) +{ + EFI_STATUS Status; + DEBUG_AGENT_MAILBOX *Mailbox; + + Status = EfiGetSystemConfigurationTable (&gEfiDebugAgentGuid, (VOID **) &Mailbox); + if (Status == EFI_SUCCESS && Mailbox != NULL) { + VerifyMailboxChecksum (Mailbox); + return Mailbox; + } else { + return NULL; + } +} + +/** + Get the pointer to Mailbox from the GUIDed HOB. + + @param[in] HobStart The starting HOB pointer to search from. + + @return Pointer to Mailbox. + +**/ +DEBUG_AGENT_MAILBOX * +GetMailboxFromHob ( + IN VOID *HobStart + ) +{ + EFI_HOB_GUID_TYPE *GuidHob; + UINT64 *MailboxLocation; + DEBUG_AGENT_MAILBOX *Mailbox; + + GuidHob = GetNextGuidHob (&gEfiDebugAgentGuid, HobStart); + if (GuidHob == NULL) { + return NULL; + } + MailboxLocation = (UINT64 *) (GET_GUID_HOB_DATA(GuidHob)); + Mailbox = (DEBUG_AGENT_MAILBOX *)(UINTN)(*MailboxLocation); + VerifyMailboxChecksum (Mailbox); + + return Mailbox; +} + +/** + Get Debug Agent Mailbox pointer. + + @return Mailbox pointer. + +**/ +DEBUG_AGENT_MAILBOX * +GetMailboxPointer ( + VOID + ) +{ + AcquireMpSpinLock (&mDebugMpContext.MailboxSpinLock); + VerifyMailboxChecksum (mMailboxPointer); + ReleaseMpSpinLock (&mDebugMpContext.MailboxSpinLock); + return mMailboxPointer; +} + +/** + Get debug port handle. + + @return Debug port handle. + +**/ +DEBUG_PORT_HANDLE +GetDebugPortHandle ( + VOID + ) +{ + return (DEBUG_PORT_HANDLE) (UINTN)(GetMailboxPointer ()->DebugPortHandle); +} + +/** + Worker function to set up Debug Agent environment. + + This function will set up IDT table and initialize the IDT entries and + initialize CPU LOCAL APIC timer. + It also tries to connect HOST if Debug Agent was not initialized before. + + @param[in] Mailbox Pointer to Mailbox. + +**/ +VOID +SetupDebugAgentEnvironment ( + IN DEBUG_AGENT_MAILBOX *Mailbox + ) +{ + IA32_DESCRIPTOR Idtr; + UINT16 IdtEntryCount; + UINT64 DebugPortHandle; + UINT32 DebugTimerFrequency; + + if (mMultiProcessorDebugSupport) { + InitializeSpinLock (&mDebugMpContext.MpContextSpinLock); + InitializeSpinLock (&mDebugMpContext.DebugPortSpinLock); + InitializeSpinLock (&mDebugMpContext.MailboxSpinLock); + // + // Clear Break CPU index value + // + mDebugMpContext.BreakAtCpuIndex = (UINT32) -1; + } + + // + // Get original IDT address and size. + // + AsmReadIdtr ((IA32_DESCRIPTOR *) &Idtr); + IdtEntryCount = (UINT16) ((Idtr.Limit + 1) / sizeof (IA32_IDT_GATE_DESCRIPTOR)); + if (IdtEntryCount < 33) { + ZeroMem (&mIdtEntryTable, sizeof (IA32_IDT_GATE_DESCRIPTOR) * 33); + // + // Copy original IDT table into new one + // + CopyMem (&mIdtEntryTable, (VOID *) Idtr.Base, Idtr.Limit + 1); + // + // Load new IDT table + // + Idtr.Limit = (UINT16) (sizeof (IA32_IDT_GATE_DESCRIPTOR) * 33 - 1); + Idtr.Base = (UINTN) &mIdtEntryTable; + AsmWriteIdtr ((IA32_DESCRIPTOR *) &Idtr); + } + + // + // Initialize the IDT table entries to support source level debug. + // + InitializeDebugIdt (); + + // + // If mMailboxPointer is not set before, set it + // + if (mMailboxPointer == NULL) { + if (Mailbox != NULL) { + // + // If Mailbox exists, copy it into one global variable + // + CopyMem (&mMailbox, Mailbox, sizeof (DEBUG_AGENT_MAILBOX)); + } else { + ZeroMem (&mMailbox, sizeof (DEBUG_AGENT_MAILBOX)); + } + mMailboxPointer = &mMailbox; + } + + // + // Initialize Debug Timer hardware and save its initial count and frequency + // + mDebugMpContext.DebugTimerInitCount = InitializeDebugTimer (&DebugTimerFrequency, TRUE); + UpdateMailboxContent (mMailboxPointer, DEBUG_MAILBOX_DEBUG_TIMER_FREQUENCY, DebugTimerFrequency); + // + // Initialize debug communication port + // + DebugPortHandle = (UINT64) (UINTN)DebugPortInitialize ((VOID *)(UINTN)mMailboxPointer->DebugPortHandle, NULL); + UpdateMailboxContent (mMailboxPointer, DEBUG_MAILBOX_DEBUG_PORT_HANDLE_INDEX, DebugPortHandle); + + if (Mailbox == NULL) { + // + // Trigger one software interrupt to inform HOST + // + TriggerSoftInterrupt (SYSTEM_RESET_SIGNATURE); + SetDebugFlag (DEBUG_AGENT_FLAG_MEMORY_READY, 1); + // + // Memory has been ready + // + if (IsHostAttached ()) { + // + // Trigger one software interrupt to inform HOST + // + TriggerSoftInterrupt (MEMORY_READY_SIGNATURE); + } + } +} + + +/** + Initialize debug agent. + + This function is used to set up debug environment for DXE phase. + + If this function is called by DXE Core, Context must be the pointer + to HOB list which will be used to get GUIDed HOB. It will enable + interrupt to support break-in feature. + If this function is called by DXE module, Context must be NULL. It + will enable interrupt to support break-in feature. + + @param[in] InitFlag Init flag is used to decide initialize process. + @param[in] Context Context needed according to InitFlag. + @param[in] Function Continue function called by debug agent library; it was + optional. + +**/ +VOID +EFIAPI +InitializeDebugAgent ( + IN UINT32 InitFlag, + IN VOID *Context, OPTIONAL + IN DEBUG_AGENT_CONTINUE Function OPTIONAL + ) +{ + UINT64 *MailboxLocation; + DEBUG_AGENT_MAILBOX *Mailbox; + BOOLEAN InterruptStatus; + VOID *HobList; + IA32_DESCRIPTOR IdtDescriptor; + IA32_DESCRIPTOR *Ia32Idtr; + IA32_IDT_ENTRY *Ia32IdtEntry; + BOOLEAN PeriodicMode; + UINTN TimerCycle; + + if (InitFlag == DEBUG_AGENT_INIT_DXE_AP) { + // + // Check if CPU APIC Timer is working, otherwise initialize it. + // + InitializeLocalApicSoftwareEnable (TRUE); + GetApicTimerState (NULL, &PeriodicMode, NULL); + TimerCycle = GetApicTimerInitCount (); + if (!PeriodicMode || TimerCycle == 0) { + InitializeDebugTimer (NULL, FALSE); + } + // + // Invoked by AP, enable interrupt to let AP could receive IPI from other processors + // + EnableInterrupts (); + return ; + } + + // + // Disable Debug Timer interrupt + // + SaveAndSetDebugTimerInterrupt (FALSE); + // + // Save and disable original interrupt status + // + InterruptStatus = SaveAndDisableInterrupts (); + + // + // Try to get mailbox firstly + // + HobList = NULL; + Mailbox = NULL; + MailboxLocation = NULL; + + switch (InitFlag) { + + case DEBUG_AGENT_INIT_DXE_LOAD: + // + // Check if Debug Agent has been initialized before + // + if (IsDebugAgentInitialzed ()) { + DEBUG ((EFI_D_INFO, "Debug Agent: The former agent will be overwritten by the new one!\n")); + } + + mMultiProcessorDebugSupport = TRUE; + // + // Save original IDT table + // + AsmReadIdtr (&IdtDescriptor); + mSaveIdtTableSize = IdtDescriptor.Limit + 1; + mSavedIdtTable = AllocateCopyPool (mSaveIdtTableSize, (VOID *) IdtDescriptor.Base); + // + // Check if Debug Agent initialized in DXE phase + // + Mailbox = GetMailboxFromConfigurationTable (); + if (Mailbox == NULL) { + // + // Try to get mailbox from GUIDed HOB build in PEI + // + HobList = GetHobList (); + Mailbox = GetMailboxFromHob (HobList); + } + // + // Set up Debug Agent Environment and try to connect HOST if required + // + SetupDebugAgentEnvironment (Mailbox); + // + // For DEBUG_AGENT_INIT_S3, needn't to install configuration table and EFI Serial IO protocol + // For DEBUG_AGENT_INIT_DXE_CORE, InternalConstructorWorker() will invoked in Constructor() + // + InternalConstructorWorker (); + // + // Enable Debug Timer interrupt + // + SaveAndSetDebugTimerInterrupt (TRUE); + // + // Enable interrupt to receive Debug Timer interrupt + // + EnableInterrupts (); + + mDebugAgentInitialized = TRUE; + FindAndReportModuleImageInfo (SIZE_4KB); + + *(EFI_STATUS *)Context = EFI_SUCCESS; + + break; + + case DEBUG_AGENT_INIT_DXE_UNLOAD: + if (mDebugAgentInitialized) { + if (IsHostAttached ()) { + *(EFI_STATUS *)Context = EFI_ACCESS_DENIED; + // + // Enable Debug Timer interrupt again + // + SaveAndSetDebugTimerInterrupt (TRUE); + } else { + // + // Restore original IDT table + // + AsmReadIdtr (&IdtDescriptor); + IdtDescriptor.Limit = (UINT16) (mSaveIdtTableSize - 1); + CopyMem ((VOID *) IdtDescriptor.Base, mSavedIdtTable, mSaveIdtTableSize); + AsmWriteIdtr (&IdtDescriptor); + FreePool (mSavedIdtTable); + mDebugAgentInitialized = FALSE; + *(EFI_STATUS *)Context = EFI_SUCCESS; + } + } else { + *(EFI_STATUS *)Context = EFI_NOT_STARTED; + } + + // + // Restore interrupt state. + // + SetInterruptState (InterruptStatus); + break; + + case DEBUG_AGENT_INIT_DXE_CORE: + mDxeCoreFlag = TRUE; + mMultiProcessorDebugSupport = TRUE; + // + // Try to get mailbox from GUIDed HOB build in PEI + // + HobList = Context; + Mailbox = GetMailboxFromHob (HobList); + // + // Set up Debug Agent Environment and try to connect HOST if required + // + SetupDebugAgentEnvironment (Mailbox); + // + // Enable Debug Timer interrupt + // + SaveAndSetDebugTimerInterrupt (TRUE); + // + // Enable interrupt to receive Debug Timer interrupt + // + EnableInterrupts (); + + break; + + case DEBUG_AGENT_INIT_S3: + + if (Context != NULL) { + Ia32Idtr = (IA32_DESCRIPTOR *) Context; + Ia32IdtEntry = (IA32_IDT_ENTRY *)(Ia32Idtr->Base); + MailboxLocation = (UINT64 *) ((UINTN) Ia32IdtEntry[DEBUG_MAILBOX_VECTOR].Bits.OffsetLow + + ((UINTN) Ia32IdtEntry[DEBUG_MAILBOX_VECTOR].Bits.OffsetHigh << 16)); + Mailbox = (DEBUG_AGENT_MAILBOX *)(UINTN)(*MailboxLocation); + VerifyMailboxChecksum (Mailbox); + } + // + // Save Mailbox pointer in global variable + // + mMailboxPointer = Mailbox; + // + // Set up Debug Agent Environment and try to connect HOST if required + // + SetupDebugAgentEnvironment (Mailbox); + // + // Disable interrupt + // + DisableInterrupts (); + FindAndReportModuleImageInfo (SIZE_4KB); + if (GetDebugFlag (DEBUG_AGENT_FLAG_BREAK_BOOT_SCRIPT) == 1) { + // + // If Boot Script entry break is set, code will be break at here. + // + CpuBreakpoint (); + } + break; + + default: + // + // Only DEBUG_AGENT_INIT_PREMEM_SEC and DEBUG_AGENT_INIT_POSTMEM_SEC are allowed for this + // Debug Agent library instance. + // + DEBUG ((EFI_D_ERROR, "Debug Agent: The InitFlag value is not allowed!\n")); + CpuDeadLoop (); + break; + } +} diff --git a/Core/SourceLevelDebugPkg/Library/DebugAgent/DxeDebugAgent/DxeDebugAgentLib.h b/Core/SourceLevelDebugPkg/Library/DebugAgent/DxeDebugAgent/DxeDebugAgentLib.h new file mode 100644 index 0000000000..536fb71dc5 --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugAgent/DxeDebugAgent/DxeDebugAgentLib.h @@ -0,0 +1,40 @@ +/** @file + Header file for Dxe Core Debug Agent Library instance. + + Copyright (c) 2010 - 2013, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _DXE_CORE_DEBUG_AGENT_LIB_H_ +#define _DXE_CORE_DEBUG_AGENT_LIB_H_ + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "DebugAgent.h" + +/** + Install EFI Serial IO protocol based on Debug Communication Library. + +**/ +VOID +InstallSerialIo ( + VOID + ); + +#endif diff --git a/Core/SourceLevelDebugPkg/Library/DebugAgent/DxeDebugAgent/SerialIo.c b/Core/SourceLevelDebugPkg/Library/DebugAgent/DxeDebugAgent/SerialIo.c new file mode 100644 index 0000000000..cb3b623038 --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugAgent/DxeDebugAgent/SerialIo.c @@ -0,0 +1,814 @@ +/** @file + Install Serial IO Protocol that layers on top of a Debug Communication Library instance. + + Copyright (c) 2012 - 2015, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "DxeDebugAgentLib.h" + +// +// Serial I/O Protocol Interface defintions. +// + +/** + Reset serial device. + + @param[in] This Pointer to EFI_SERIAL_IO_PROTOCOL. + + @retval EFI_SUCCESS Reset successfully. + +**/ +EFI_STATUS +EFIAPI +SerialReset ( + IN EFI_SERIAL_IO_PROTOCOL *This + ); + +/** + Set new attributes to a serial device. + + @param[in] This Pointer to EFI_SERIAL_IO_PROTOCOL. + @param[in] BaudRate The baudrate of the serial device. + @param[in] ReceiveFifoDepth The depth of receive FIFO buffer. + @param[in] Timeout The request timeout for a single char. + @param[in] Parity The type of parity used in serial device. + @param[in] DataBits Number of databits used in serial device. + @param[in] StopBits Number of stopbits used in serial device. + + @retval EFI_SUCCESS The new attributes were set. + @retval EFI_INVALID_PARAMETER One or more attributes have an unsupported value. + @retval EFI_DEVICE_ERROR The serial device is not functioning correctly (no return). + +**/ +EFI_STATUS +EFIAPI +SerialSetAttributes ( + IN EFI_SERIAL_IO_PROTOCOL *This, + IN UINT64 BaudRate, + IN UINT32 ReceiveFifoDepth, + IN UINT32 Timeout, + IN EFI_PARITY_TYPE Parity, + IN UINT8 DataBits, + IN EFI_STOP_BITS_TYPE StopBits + ); + +/** + Set Control Bits. + + @param[in] This Pointer to EFI_SERIAL_IO_PROTOCOL. + @param[in] Control Control bits that can be settable. + + @retval EFI_SUCCESS New Control bits were set successfully. + @retval EFI_UNSUPPORTED The Control bits wanted to set are not supported. + +**/ +EFI_STATUS +EFIAPI +SerialSetControl ( + IN EFI_SERIAL_IO_PROTOCOL *This, + IN UINT32 Control + ); + +/** + Get ControlBits. + + @param[in] This Pointer to EFI_SERIAL_IO_PROTOCOL. + @param[out] Control Control signals of the serial device. + + @retval EFI_SUCCESS Get Control signals successfully. + +**/ +EFI_STATUS +EFIAPI +SerialGetControl ( + IN EFI_SERIAL_IO_PROTOCOL *This, + OUT UINT32 *Control + ); + +/** + Write the specified number of bytes to serial device. + + @param[in] This Pointer to EFI_SERIAL_IO_PROTOCOL. + @param[in, out] BufferSize On input the size of Buffer, on output the amount of + data actually written. + @param[in] Buffer The buffer of data to write. + + @retval EFI_SUCCESS The data were written successfully. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_TIMEOUT The write operation was stopped due to timeout. + +**/ +EFI_STATUS +EFIAPI +SerialWrite ( + IN EFI_SERIAL_IO_PROTOCOL *This, + IN OUT UINTN *BufferSize, + IN VOID *Buffer + ); + +/** + Read the specified number of bytes from serial device. + + @param[in] This Pointer to EFI_SERIAL_IO_PROTOCOL. + @param[in, out] BufferSize On input the size of Buffer, on output the amount of + data returned in buffer. + @param[out] Buffer The buffer to return the data into. + + @retval EFI_SUCCESS The data were read successfully. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_TIMEOUT The read operation was stopped due to timeout. + +**/ +EFI_STATUS +EFIAPI +SerialRead ( + IN EFI_SERIAL_IO_PROTOCOL *This, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ); + +// +// Serial Driver Defaults +// +#define SERIAL_PORT_DEFAULT_RECEIVE_FIFO_DEPTH 1 +#define SERIAL_PORT_DEFAULT_TIMEOUT 1000000 +#define SERIAL_PORT_DEFAULT_CONTROL_MASK 0 +#define SERIAL_PORT_LOOPBACK_BUFFER_FULL BIT8 + +// +// EFI_SERIAL_IO_MODE instance +// +EFI_SERIAL_IO_MODE mSerialIoMode = { + SERIAL_PORT_DEFAULT_CONTROL_MASK, + SERIAL_PORT_DEFAULT_TIMEOUT, + 0, // default BaudRate + SERIAL_PORT_DEFAULT_RECEIVE_FIFO_DEPTH, + 0, // default DataBits + 0, // default Parity + 0 // default StopBits +}; + +// +// EFI_SERIAL_IO_PROTOCOL instance +// +EFI_SERIAL_IO_PROTOCOL mSerialIo = { + SERIAL_IO_INTERFACE_REVISION, + SerialReset, + SerialSetAttributes, + SerialSetControl, + SerialGetControl, + SerialWrite, + SerialRead, + &mSerialIoMode +}; + +// +// Serial IO Device Path definition +// +typedef struct { + VENDOR_DEVICE_PATH VendorDevicePath; + UART_DEVICE_PATH UartDevicePath; + EFI_DEVICE_PATH_PROTOCOL EndDevicePath; +} SERIAL_IO_DEVICE_PATH; + +// +// Serial IO Device Patch instance +// +SERIAL_IO_DEVICE_PATH mSerialIoDevicePath = { + { + { + HARDWARE_DEVICE_PATH, + HW_VENDOR_DP, + { + (UINT8) (sizeof (VENDOR_DEVICE_PATH)), + (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8) + } + }, + EFI_DEBUG_AGENT_GUID, + }, + { + { + MESSAGING_DEVICE_PATH, + MSG_UART_DP, + { + (UINT8) (sizeof (UART_DEVICE_PATH)), + (UINT8) ((sizeof (UART_DEVICE_PATH)) >> 8) + } + }, + 0, + 0, // default BaudRate + 0, // default DataBits + 0, // default Parity + 0, // default StopBits + }, + { + END_DEVICE_PATH_TYPE, + END_ENTIRE_DEVICE_PATH_SUBTYPE, + { + END_DEVICE_PATH_LENGTH, + 0 + } + } +}; + +#define DEBGU_SERIAL_IO_FIFO_DEPTH 10 +// +// Data buffer for Terminal input character and Debug Symbols. +// The depth is DEBGU_SERIAL_IO_FIFO_DEPTH. +// Fields: +// First UINT8: The index of the first data in array Data[]. +// Last UINT8: The index, which you can put a new data into array Data[]. +// Surplus UINT8: Identify how many data you can put into array Data[]. +// Data[] UINT8: An array, which used to store data. +// +typedef struct { + UINT8 First; + UINT8 Last; + UINT8 Surplus; + UINT8 Data[DEBGU_SERIAL_IO_FIFO_DEPTH]; +} DEBUG_SERIAL_FIFO; + +// +// Global Varibles +// +EFI_HANDLE mSerialIoHandle = NULL; +UINTN mLoopbackBuffer = 0; +DEBUG_SERIAL_FIFO mSerialFifoForTerminal = {0, 0, DEBGU_SERIAL_IO_FIFO_DEPTH, { 0 }}; +DEBUG_SERIAL_FIFO mSerialFifoForDebug = {0, 0, DEBGU_SERIAL_IO_FIFO_DEPTH, { 0 }}; + +/** + Detect whether specific FIFO is empty or not. + + @param[in] Fifo A pointer to the Data Structure DEBUG_SERIAL_FIFO. + + @return whether specific FIFO is empty or not. + +**/ +BOOLEAN +IsDebugTermianlFifoEmpty ( + IN DEBUG_SERIAL_FIFO *Fifo + ) +{ + if (Fifo->Surplus == DEBGU_SERIAL_IO_FIFO_DEPTH) { + return TRUE; + } + + return FALSE; +} + +/** + Detect whether specific FIFO is full or not. + + @param[in] Fifo A pointer to the Data Structure DEBUG_SERIAL_FIFO. + + @return whether specific FIFO is full or not. + +**/ +BOOLEAN +IsDebugTerminalFifoFull ( + IN DEBUG_SERIAL_FIFO *Fifo + ) + +{ + if (Fifo->Surplus == 0) { + return TRUE; + } + + return FALSE; +} + +/** + Add data to specific FIFO. + + @param[in] Fifo A pointer to the Data Structure DEBUG_SERIAL_FIFO. + @param[in] Data The data added to FIFO. + + @retval EFI_SUCCESS Add data to specific FIFO successfully. + @retval EFI_OUT_OF_RESOURCE Failed to add data because FIFO is already full. + +**/ +EFI_STATUS +DebugTerminalFifoAdd ( + IN DEBUG_SERIAL_FIFO *Fifo, + IN UINT8 Data + ) + +{ + // + // if FIFO full can not add data + // + if (IsDebugTerminalFifoFull (Fifo)) { + return EFI_OUT_OF_RESOURCES; + } + // + // FIFO is not full can add data + // + Fifo->Data[Fifo->Last] = Data; + Fifo->Surplus--; + Fifo->Last++; + if (Fifo->Last == DEBGU_SERIAL_IO_FIFO_DEPTH) { + Fifo->Last = 0; + } + + return EFI_SUCCESS; +} + +/** + Remove data from specific FIFO. + + @param[in] Fifo A pointer to the Data Structure DEBUG_SERIAL_FIFO. + @param[out] Data The data removed from FIFO. + + @retval EFI_SUCCESS Remove data from specific FIFO successfully. + @retval EFI_OUT_OF_RESOURCE Failed to remove data because FIFO is empty. + +**/ +EFI_STATUS +DebugTerminalFifoRemove ( + IN DEBUG_SERIAL_FIFO *Fifo, + OUT UINT8 *Data + ) +{ + // + // if FIFO is empty, no data can remove + // + if (IsDebugTermianlFifoEmpty (Fifo)) { + return EFI_OUT_OF_RESOURCES; + } + // + // FIFO is not empty, can remove data + // + *Data = Fifo->Data[Fifo->First]; + Fifo->Surplus++; + Fifo->First++; + if (Fifo->First == DEBGU_SERIAL_IO_FIFO_DEPTH) { + Fifo->First = 0; + } + + return EFI_SUCCESS; +} + +/** + Install EFI Serial IO protocol based on Debug Communication Library. + +**/ +VOID +InstallSerialIo ( + VOID + ) +{ + EFI_STATUS Status; + + Status = gBS->InstallMultipleProtocolInterfaces ( + &mSerialIoHandle, + &gEfiDevicePathProtocolGuid, &mSerialIoDevicePath, + &gEfiSerialIoProtocolGuid, &mSerialIo, + NULL + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Debug Agent: Failed to install EFI Serial IO Protocol on Debug Port!\n")); + } +} + +/** + Reset serial device. + + @param[in] This Pointer to EFI_SERIAL_IO_PROTOCOL. + + @retval EFI_SUCCESS Reset successfully. + +**/ +EFI_STATUS +EFIAPI +SerialReset ( + IN EFI_SERIAL_IO_PROTOCOL *This + ) +{ + mSerialIoMode.ControlMask = SERIAL_PORT_DEFAULT_CONTROL_MASK; + mLoopbackBuffer = 0; + // + // Not reset serial devcie hardware indeed. + // + return EFI_SUCCESS; +} + +/** + Set new attributes to a serial device. + + @param[in] This Pointer to EFI_SERIAL_IO_PROTOCOL. + @param[in] BaudRate The baudrate of the serial device. + @param[in] ReceiveFifoDepth The depth of receive FIFO buffer. + @param[in] Timeout The request timeout for a single char. + @param[in] Parity The type of parity used in serial device. + @param[in] DataBits Number of databits used in serial device. + @param[in] StopBits Number of stopbits used in serial device. + + @retval EFI_SUCCESS The new attributes were set. + @retval EFI_INVALID_PARAMETER One or more attributes have an unsupported value. + @retval EFI_DEVICE_ERROR The serial device is not functioning correctly (no return). + +**/ +EFI_STATUS +EFIAPI +SerialSetAttributes ( + IN EFI_SERIAL_IO_PROTOCOL *This, + IN UINT64 BaudRate, + IN UINT32 ReceiveFifoDepth, + IN UINT32 Timeout, + IN EFI_PARITY_TYPE Parity, + IN UINT8 DataBits, + IN EFI_STOP_BITS_TYPE StopBits + ) +{ + // + // The Debug Communication Library CAN NOT change communications parameters (if it has) + // actually. Because it also has no any idea on what parameters are based on, we cannot + // check the input parameters (like BaudRate, Parity, DataBits and StopBits). + // + + // + // Update the Timeout value in the mode structure based on the request. + // The Debug Communication Library can not support a timeout on writes, but the timeout on + // reads can be provided by this module. + // + if (Timeout == 0) { + mSerialIoMode.Timeout = SERIAL_PORT_DEFAULT_TIMEOUT; + } else { + mSerialIoMode.Timeout = Timeout; + } + + // + // Update the ReceiveFifoDepth value in the mode structure based on the request. + // This module assumes that the Debug Communication Library uses a FIFO depth of + // SERIAL_PORT_DEFAULT_RECEIVE_FIFO_DEPTH. The Debug Communication Library may actually be + // using a larger FIFO, but there is no way to tell. + // + if (ReceiveFifoDepth == 0 || ReceiveFifoDepth >= SERIAL_PORT_DEFAULT_RECEIVE_FIFO_DEPTH) { + mSerialIoMode.ReceiveFifoDepth = SERIAL_PORT_DEFAULT_RECEIVE_FIFO_DEPTH; + } else { + return EFI_INVALID_PARAMETER; + } + + return EFI_SUCCESS; +} + +/** + Set Control Bits. + + @param[in] This Pointer to EFI_SERIAL_IO_PROTOCOL. + @param[in] Control Control bits that can be settable. + + @retval EFI_SUCCESS New Control bits were set successfully. + @retval EFI_UNSUPPORTED The Control bits wanted to set are not supported. + +**/ +EFI_STATUS +EFIAPI +SerialSetControl ( + IN EFI_SERIAL_IO_PROTOCOL *This, + IN UINT32 Control + ) +{ + // + // The only control bit supported by this module is software loopback. + // If any other bit is set, then return an error + // + if ((Control & (~EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE)) != 0) { + return EFI_UNSUPPORTED; + } + mSerialIoMode.ControlMask = Control; + return EFI_SUCCESS; +} + +/** + Get ControlBits. + + @param[in] This Pointer to EFI_SERIAL_IO_PROTOCOL. + @param[out] Control Control signals of the serial device. + + @retval EFI_SUCCESS Get Control signals successfully. + +**/ +EFI_STATUS +EFIAPI +SerialGetControl ( + IN EFI_SERIAL_IO_PROTOCOL *This, + OUT UINT32 *Control + ) +{ + DEBUG_PORT_HANDLE Handle; + BOOLEAN DebugTimerInterruptState; + EFI_TPL Tpl; + + // + // Raise TPL to prevent recursion from EFI timer interrupts + // + Tpl = gBS->RaiseTPL (TPL_NOTIFY); + + // + // Save and disable Debug Timer interrupt to avoid it to access Debug Port + // + DebugTimerInterruptState = SaveAndSetDebugTimerInterrupt (FALSE); + Handle = GetDebugPortHandle (); + + // + // Always assume the output buffer is empty and the Debug Communication Library can process + // more write requests. + // + *Control = mSerialIoMode.ControlMask | EFI_SERIAL_OUTPUT_BUFFER_EMPTY; + + // + // Check to see if the Terminal FIFO is empty and + // check to see if the input buffer in the Debug Communication Library is empty + // + if (!IsDebugTermianlFifoEmpty (&mSerialFifoForTerminal) || DebugPortPollBuffer (Handle)) { + *Control &= ~EFI_SERIAL_INPUT_BUFFER_EMPTY; + } + + // + // Restore Debug Timer interrupt + // + SaveAndSetDebugTimerInterrupt (DebugTimerInterruptState); + + // + // Restore to original TPL + // + gBS->RestoreTPL (Tpl); + + return EFI_SUCCESS; +} + +/** + Write the specified number of bytes to serial device. + + @param[in] This Pointer to EFI_SERIAL_IO_PROTOCOL. + @param[in, out] BufferSize On input the size of Buffer, on output the amount of + data actually written. + @param[in] Buffer The buffer of data to write. + + @retval EFI_SUCCESS The data were written successfully. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_TIMEOUT The write operation was stopped due to timeout. + +**/ +EFI_STATUS +EFIAPI +SerialWrite ( + IN EFI_SERIAL_IO_PROTOCOL *This, + IN OUT UINTN *BufferSize, + IN VOID *Buffer + ) +{ + DEBUG_PORT_HANDLE Handle; + BOOLEAN DebugTimerInterruptState; + EFI_TPL Tpl; + + // + // Raise TPL to prevent recursion from EFI timer interrupts + // + Tpl = gBS->RaiseTPL (TPL_NOTIFY); + + // + // Save and disable Debug Timer interrupt to avoid it to access Debug Port + // + DebugTimerInterruptState = SaveAndSetDebugTimerInterrupt (FALSE); + Handle = GetDebugPortHandle (); + + if ((mSerialIoMode.ControlMask & EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE) != 0) { + if (*BufferSize == 0) { + return EFI_SUCCESS; + } + if ((mLoopbackBuffer & SERIAL_PORT_LOOPBACK_BUFFER_FULL) != 0) { + *BufferSize = 0; + return EFI_TIMEOUT; + } + mLoopbackBuffer = SERIAL_PORT_LOOPBACK_BUFFER_FULL | *(UINT8 *)Buffer; + *BufferSize = 1; + } else { + *BufferSize = DebugPortWriteBuffer (Handle, Buffer, *BufferSize); + } + + // + // Restore Debug Timer interrupt + // + SaveAndSetDebugTimerInterrupt (DebugTimerInterruptState); + + // + // Restore to original TPL + // + gBS->RestoreTPL (Tpl); + + return EFI_SUCCESS; +} + +/** + Read the specified number of bytes from serial device. + + @param[in] This Pointer to EFI_SERIAL_IO_PROTOCOL. + @param[in, out] BufferSize On input the size of Buffer, on output the amount of + data returned in buffer. + @param[out] Buffer The buffer to return the data into. + + @retval EFI_SUCCESS The data were read successfully. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_TIMEOUT The read operation was stopped due to timeout. + +**/ +EFI_STATUS +EFIAPI +SerialRead ( + IN EFI_SERIAL_IO_PROTOCOL *This, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + UINTN Index; + UINT8 *Uint8Buffer; + BOOLEAN DebugTimerInterruptState; + EFI_TPL Tpl; + DEBUG_PORT_HANDLE Handle; + DEBUG_PACKET_HEADER DebugHeader; + UINT8 *Data8; + + // + // Raise TPL to prevent recursion from EFI timer interrupts + // + Tpl = gBS->RaiseTPL (TPL_NOTIFY); + + // + // Save and disable Debug Timer interrupt to avoid it to access Debug Port + // + DebugTimerInterruptState = SaveAndSetDebugTimerInterrupt (FALSE); + Handle = GetDebugPortHandle (); + + Data8 = (UINT8 *) &DebugHeader; + Uint8Buffer = (UINT8 *)Buffer; + if ((mSerialIoMode.ControlMask & EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE) != 0) { + if ((mLoopbackBuffer & SERIAL_PORT_LOOPBACK_BUFFER_FULL) == 0) { + return EFI_TIMEOUT; + } + *Uint8Buffer = (UINT8)(mLoopbackBuffer & 0xff); + mLoopbackBuffer = 0; + *BufferSize = 1; + } else { + for (Index = 0; Index < *BufferSize; Index++) { + // + // Read input character from terminal FIFO firstly + // + Status = DebugTerminalFifoRemove (&mSerialFifoForTerminal, Data8); + if (Status == EFI_SUCCESS) { + *Uint8Buffer = *Data8; + Uint8Buffer ++; + continue; + } + // + // Read the input character from Debug Port + // + if (!DebugPortPollBuffer (Handle)) { + break; + } + DebugAgentReadBuffer (Handle, Data8, 1, 0); + + if (*Data8 == DEBUG_STARTING_SYMBOL_ATTACH) { + // + // Add the debug symbol into Debug FIFO + // + DebugAgentMsgPrint (DEBUG_AGENT_INFO, "Terminal Timer attach symbol received %x", *Data8); + DebugTerminalFifoAdd (&mSerialFifoForDebug, *Data8); + } else if (*Data8 == DEBUG_STARTING_SYMBOL_NORMAL) { + Status = ReadRemainingBreakPacket (Handle, &DebugHeader); + if (Status == EFI_SUCCESS) { + DebugAgentMsgPrint (DEBUG_AGENT_INFO, "Terminal Timer break symbol received %x", DebugHeader.Command); + DebugTerminalFifoAdd (&mSerialFifoForDebug, DebugHeader.Command); + } + if (Status == EFI_TIMEOUT) { + continue; + } + } else { + *Uint8Buffer = *Data8; + Uint8Buffer ++; + } + } + *BufferSize = (UINTN)Uint8Buffer - (UINTN)Buffer; + } + + // + // Restore Debug Timer interrupt + // + SaveAndSetDebugTimerInterrupt (DebugTimerInterruptState); + + // + // Restore to original TPL + // + gBS->RestoreTPL (Tpl); + + return EFI_SUCCESS; +} + +/** + Read the Attach/Break-in symbols from the debug port. + + @param[in] Handle Pointer to Debug Port handle. + @param[out] BreakSymbol Returned break symbol. + + @retval EFI_SUCCESS Read the symbol in BreakSymbol. + @retval EFI_NOT_FOUND No read the break symbol. + +**/ +EFI_STATUS +DebugReadBreakFromDebugPort ( + IN DEBUG_PORT_HANDLE Handle, + OUT UINT8 *BreakSymbol + ) +{ + EFI_STATUS Status; + DEBUG_PACKET_HEADER DebugHeader; + UINT8 *Data8; + + *BreakSymbol = 0; + // + // If Debug Port buffer has data, read it till it was break symbol or Debug Port buffer empty. + // + Data8 = (UINT8 *) &DebugHeader; + while (TRUE) { + // + // If start symbol is not received + // + if (!DebugPortPollBuffer (Handle)) { + // + // If no data in Debug Port, exit + // + break; + } + // + // Try to read the start symbol + // + DebugAgentReadBuffer (Handle, Data8, 1, 0); + if (*Data8 == DEBUG_STARTING_SYMBOL_ATTACH) { + DebugAgentMsgPrint (DEBUG_AGENT_INFO, "Debug Timer attach symbol received %x", *Data8); + *BreakSymbol = *Data8; + return EFI_SUCCESS; + } + if (*Data8 == DEBUG_STARTING_SYMBOL_NORMAL) { + Status = ReadRemainingBreakPacket (Handle, &DebugHeader); + if (Status == EFI_SUCCESS) { + DebugAgentMsgPrint (DEBUG_AGENT_INFO, "Debug Timer break symbol received %x", DebugHeader.Command); + *BreakSymbol = DebugHeader.Command; + return EFI_SUCCESS; + } + if (Status == EFI_TIMEOUT) { + break; + } + } else { + // + // Add to Terminal FIFO + // + DebugTerminalFifoAdd (&mSerialFifoForTerminal, *Data8); + } + } + + return EFI_NOT_FOUND; +} + +/** + Read the Attach/Break-in symbols. + + @param[in] Handle Pointer to Debug Port handle. + @param[out] BreakSymbol Returned break symbol. + + @retval EFI_SUCCESS Read the symbol in BreakSymbol. + @retval EFI_NOT_FOUND No read the break symbol. + +**/ +EFI_STATUS +DebugReadBreakSymbol ( + IN DEBUG_PORT_HANDLE Handle, + OUT UINT8 *BreakSymbol + ) +{ + EFI_STATUS Status; + UINT8 Data8; + + // + // Read break symbol from debug FIFO firstly + // + Status = DebugTerminalFifoRemove (&mSerialFifoForDebug, &Data8); + if (Status == EFI_SUCCESS) { + *BreakSymbol = Data8; + return EFI_SUCCESS; + } else { + // + // Read Break symbol from debug port + // + return DebugReadBreakFromDebugPort (Handle, BreakSymbol); + } +} diff --git a/Core/SourceLevelDebugPkg/Library/DebugAgent/DxeDebugAgentLib.inf b/Core/SourceLevelDebugPkg/Library/DebugAgent/DxeDebugAgentLib.inf new file mode 100644 index 0000000000..ce36345bab --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugAgent/DxeDebugAgentLib.inf @@ -0,0 +1,104 @@ +## @file +# Debug Agent library instance for Dxe Core and Dxe modules. +# +# Copyright (c) 2010 - 2016, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php. +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = DxeDebugAgentLib + MODULE_UNI_FILE = DxeDebugAgentLib.uni + FILE_GUID = BA6BAD25-B814-4747-B0B0-0FBB61D40B90 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 0.8 + LIBRARY_CLASS = DebugAgentLib|DXE_CORE DXE_DRIVER + + CONSTRUCTOR = DxeDebugAgentLibConstructor + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources.common] + DxeDebugAgent/DxeDebugAgentLib.c + DxeDebugAgent/DxeDebugAgentLib.h + DxeDebugAgent/SerialIo.c + DebugAgentCommon/DebugAgent.c + DebugAgentCommon/DebugAgent.h + DebugAgentCommon/DebugTimer.c + DebugAgentCommon/DebugTimer.h + DebugAgentCommon/DebugMp.c + DebugAgentCommon/DebugMp.h + +[Sources.Ia32] + DebugAgentCommon/Ia32/AsmFuncs.S + DebugAgentCommon/Ia32/AsmFuncs.asm + DebugAgentCommon/Ia32/AsmFuncs.nasm + DebugAgentCommon/Ia32/ArchDebugSupport.h + DebugAgentCommon/Ia32/ArchDebugSupport.c + DebugAgentCommon/Ia32/DebugException.h + +[Sources.X64] + DebugAgentCommon/X64/AsmFuncs.S + DebugAgentCommon/X64/AsmFuncs.asm + DebugAgentCommon/X64/AsmFuncs.nasm + DebugAgentCommon/X64/ArchDebugSupport.h + DebugAgentCommon/X64/ArchDebugSupport.c + DebugAgentCommon/X64/DebugException.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + SourceLevelDebugPkg/SourceLevelDebugPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + ResetSystemLib + IoLib + HobLib + DebugCommunicationLib + UefiBootServicesTableLib + UefiLib + PcdLib + SynchronizationLib + MemoryAllocationLib + LocalApicLib + TimerLib + PrintLib + PeCoffGetEntryPointLib + PeCoffExtraActionLib + MemoryAllocationLib + +[Guids] + ## PRODUCES ## SystemTable + ## CONSUMES ## HOB + gEfiDebugAgentGuid + ## SOMETIMES_CONSUMES ## SystemTable + ## SOMETIMES_PRODUCES ## SystemTable + gEfiVectorHandoffTableGuid + +[Ppis] + gEfiVectorHandoffInfoPpiGuid ## UNDEFINED + +[Protocols] + gEfiSerialIoProtocolGuid ## SOMETIMES_PRODUCES + gEfiDevicePathProtocolGuid ## SOMETIMES_PRODUCES + +[Pcd] + gEfiMdePkgTokenSpaceGuid.PcdFSBClock ## SOMETIMES_CONSUMES + gEfiSourceLevelDebugPkgTokenSpaceGuid.PcdExceptionsIgnoredByDebugger ## SOMETIMES_CONSUMES + gEfiSourceLevelDebugPkgTokenSpaceGuid.PcdDebugPortHandleBufferSize ## CONSUMES + diff --git a/Core/SourceLevelDebugPkg/Library/DebugAgent/DxeDebugAgentLib.uni b/Core/SourceLevelDebugPkg/Library/DebugAgent/DxeDebugAgentLib.uni new file mode 100644 index 0000000000..af29766280 --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugAgent/DxeDebugAgentLib.uni @@ -0,0 +1,21 @@ +// /** @file +// Debug Agent library instance for Dxe Core and Dxe modules. +// +// Debug Agent library instance for DXE Core and DXE modules. +// +// Copyright (c) 2010 - 2014, Intel Corporation. All rights reserved.
+// +// This program and the accompanying materials +// are licensed and made available under the terms and conditions of the BSD License +// which accompanies this distribution. The full text of the license may be found at +// http://opensource.org/licenses/bsd-license.php. +// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Debug Agent library instance for DXE Core and DXE modules" + +#string STR_MODULE_DESCRIPTION #language en-US "Debug Agent library instance for DXE Core and DXE modules." + diff --git a/Core/SourceLevelDebugPkg/Library/DebugAgent/SecPeiDebugAgent/SecPeiDebugAgentLib.c b/Core/SourceLevelDebugPkg/Library/DebugAgent/SecPeiDebugAgent/SecPeiDebugAgentLib.c new file mode 100644 index 0000000000..b717e33197 --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugAgent/SecPeiDebugAgent/SecPeiDebugAgentLib.c @@ -0,0 +1,710 @@ +/** @file + SEC Core Debug Agent Library instance implementition. + + Copyright (c) 2010 - 2017, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "SecPeiDebugAgentLib.h" + +GLOBAL_REMOVE_IF_UNREFERENCED BOOLEAN mSkipBreakpoint = FALSE; + + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_PEI_VECTOR_HANDOFF_INFO_PPI mVectorHandoffInfoPpi = { + &mVectorHandoffInfoDebugAgent[0] +}; + +// +// Ppis to be installed +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_PEI_PPI_DESCRIPTOR mVectorHandoffInfoPpiList[] = { + { + (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gEfiVectorHandoffInfoPpiGuid, + &mVectorHandoffInfoPpi + } +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_PEI_NOTIFY_DESCRIPTOR mMemoryDiscoveredNotifyList[1] = { + { + (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gEfiPeiMemoryDiscoveredPpiGuid, + DebugAgentCallbackMemoryDiscoveredPpi + } +}; + +/** + Check if debug agent support multi-processor. + + @retval TRUE Multi-processor is supported. + @retval FALSE Multi-processor is not supported. + +**/ +BOOLEAN +MultiProcessorDebugSupport ( + VOID + ) +{ + return FALSE; +} + +/** + Read the Attach/Break-in symbols from the debug port. + + @param[in] Handle Pointer to Debug Port handle. + @param[out] BreakSymbol Returned break symbol. + + @retval EFI_SUCCESS Read the symbol in BreakSymbol. + @retval EFI_NOT_FOUND No read the break symbol. + +**/ +EFI_STATUS +DebugReadBreakSymbol ( + IN DEBUG_PORT_HANDLE Handle, + OUT UINT8 *BreakSymbol + ) +{ + EFI_STATUS Status; + DEBUG_PACKET_HEADER DebugHeader; + UINT8 *Data8; + + *BreakSymbol = 0; + // + // If Debug Port buffer has data, read it till it was break symbol or Debug Port buffer empty. + // + Data8 = (UINT8 *) &DebugHeader; + while (TRUE) { + // + // If start symbol is not received + // + if (!DebugPortPollBuffer (Handle)) { + // + // If no data in Debug Port, exit + // + break; + } + // + // Try to read the start symbol + // + DebugAgentReadBuffer (Handle, Data8, 1, 0); + if (*Data8 == DEBUG_STARTING_SYMBOL_ATTACH) { + *BreakSymbol = *Data8; + DebugAgentMsgPrint (DEBUG_AGENT_INFO, "Debug Timer attach symbol received %x", *BreakSymbol); + return EFI_SUCCESS; + } + if (*Data8 == DEBUG_STARTING_SYMBOL_NORMAL) { + Status = ReadRemainingBreakPacket (Handle, &DebugHeader); + if (Status == EFI_SUCCESS) { + *BreakSymbol = DebugHeader.Command; + DebugAgentMsgPrint (DEBUG_AGENT_INFO, "Debug Timer break symbol received %x", *BreakSymbol); + return EFI_SUCCESS; + } + if (Status == EFI_TIMEOUT) { + break; + } + } + } + + return EFI_NOT_FOUND; +} + +/** + Get the pointer to location saved Mailbox pointer from IDT entry. + +**/ +VOID * +GetLocationSavedMailboxPointerInIdtEntry ( + VOID + ) +{ + UINTN *MailboxLocation; + + MailboxLocation = (UINTN *) GetExceptionHandlerInIdtEntry (DEBUG_MAILBOX_VECTOR); + // + // *MailboxLocation is the pointer to Mailbox + // + VerifyMailboxChecksum ((DEBUG_AGENT_MAILBOX *) (*MailboxLocation)); + return MailboxLocation; +} + +/** + Set the pointer of Mailbox into IDT entry before memory is ready. + + @param[in] MailboxLocation Pointer to location saved Mailbox pointer. + +**/ +VOID +SetLocationSavedMailboxPointerInIdtEntry ( + IN VOID *MailboxLocation + ) +{ + SetExceptionHandlerInIdtEntry (DEBUG_MAILBOX_VECTOR, MailboxLocation); +} + +/** + Get the location of Mailbox pointer from the GUIDed HOB. + + @return Pointer to the location saved Mailbox pointer. + +**/ +UINT64 * +GetMailboxLocationFromHob ( + VOID + ) +{ + EFI_HOB_GUID_TYPE *GuidHob; + + GuidHob = GetFirstGuidHob (&gEfiDebugAgentGuid); + if (GuidHob == NULL) { + return NULL; + } + return (UINT64 *) (GET_GUID_HOB_DATA(GuidHob)); +} + +/** + Get Debug Agent Mailbox pointer. + + @return Mailbox pointer. + +**/ +DEBUG_AGENT_MAILBOX * +GetMailboxPointer ( + VOID + ) +{ + UINT64 DebugPortHandle; + UINT64 *MailboxLocationInIdt; + UINT64 *MailboxLocationInHob; + DEBUG_AGENT_MAILBOX *Mailbox; + + // + // Get mailbox from IDT entry firstly + // + MailboxLocationInIdt = GetLocationSavedMailboxPointerInIdtEntry (); + Mailbox = (DEBUG_AGENT_MAILBOX *)(UINTN)(*MailboxLocationInIdt); + // + // Cannot used GetDebugFlag() to get Debug Flag to avoid GetMailboxPointer() nested + // + if (Mailbox->DebugFlag.Bits.CheckMailboxInHob != 1 || + Mailbox->DebugFlag.Bits.InitArch != DEBUG_ARCH_SYMBOL) { + // + // If mailbox was setup in SEC or the current CPU arch is different from the init arch + // Debug Agent initialized, return the mailbox from IDT entry directly. + // Otherwise, we need to check the mailbox location saved in GUIDed HOB further. + // + return Mailbox; + } + + MailboxLocationInHob = GetMailboxLocationFromHob (); + // + // Compare mailbox in IDT enry with mailbox in HOB, + // need to fix mailbox location if HOB moved by PEI CORE + // + if (MailboxLocationInHob != MailboxLocationInIdt && MailboxLocationInHob != NULL) { + Mailbox = (DEBUG_AGENT_MAILBOX *)(UINTN)(*MailboxLocationInHob); + // + // Fix up Debug Port handler and save new mailbox in IDT entry + // + Mailbox = (DEBUG_AGENT_MAILBOX *)((UINTN)Mailbox + ((UINTN)(MailboxLocationInHob) - (UINTN)MailboxLocationInIdt)); + DebugPortHandle = (UINTN)Mailbox->DebugPortHandle + ((UINTN)(MailboxLocationInHob) - (UINTN)MailboxLocationInIdt); + UpdateMailboxContent (Mailbox, DEBUG_MAILBOX_DEBUG_PORT_HANDLE_INDEX, DebugPortHandle); + *MailboxLocationInHob = (UINT64)(UINTN)Mailbox; + SetLocationSavedMailboxPointerInIdtEntry (MailboxLocationInHob); + // + // Clean CheckMailboxInHob flag + // + Mailbox->DebugFlag.Bits.CheckMailboxInHob = 0; + UpdateMailboxChecksum (Mailbox); + } + + return Mailbox; +} + +/** + Get debug port handle. + + @return Debug port handle. + +**/ +DEBUG_PORT_HANDLE +GetDebugPortHandle ( + VOID + ) +{ + DEBUG_AGENT_MAILBOX *DebugAgentMailbox; + + DebugAgentMailbox = GetMailboxPointer (); + + return (DEBUG_PORT_HANDLE) (UINTN)(DebugAgentMailbox->DebugPortHandle); +} + +/** + Debug Agent provided notify callback function on Memory Discovered PPI. + + @param[in] PeiServices Indirect reference to the PEI Services Table. + @param[in] NotifyDescriptor Address of the notification descriptor data structure. + @param[in] Ppi Address of the PPI that was installed. + + @retval EFI_SUCCESS If the function completed successfully. + +**/ +EFI_STATUS +EFIAPI +DebugAgentCallbackMemoryDiscoveredPpi ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor, + IN VOID *Ppi + ) +{ + EFI_STATUS Status; + DEBUG_AGENT_MAILBOX *Mailbox; + BOOLEAN InterruptStatus; + EFI_PHYSICAL_ADDRESS Address; + DEBUG_AGENT_MAILBOX *NewMailbox; + UINT64 *MailboxLocationInHob; + + // + // Save and disable original interrupt status + // + InterruptStatus = SaveAndDisableInterrupts (); + + // + // Allocate ACPI NVS memory for new Mailbox and Debug Port Handle buffer + // + Status = PeiServicesAllocatePages ( + EfiACPIMemoryNVS, + EFI_SIZE_TO_PAGES (sizeof(DEBUG_AGENT_MAILBOX) + PcdGet16(PcdDebugPortHandleBufferSize)), + &Address + ); + ASSERT_EFI_ERROR (Status); + NewMailbox = (DEBUG_AGENT_MAILBOX *) (UINTN) Address; + // + // Copy Mailbox and Debug Port Handle buffer to new location in ACPI NVS memory, because original Mailbox + // and Debug Port Handle buffer in the allocated pool that may be marked as free by DXE Core after DXE Core + // reallocates the HOB. + // + Mailbox = GetMailboxPointer (); + CopyMem (NewMailbox, Mailbox, sizeof (DEBUG_AGENT_MAILBOX)); + CopyMem (NewMailbox + 1, (VOID *)(UINTN)Mailbox->DebugPortHandle, PcdGet16(PcdDebugPortHandleBufferSize)); + // + // Update Mailbox Location pointer in GUIDed HOB and IDT entry with new one + // + MailboxLocationInHob = GetMailboxLocationFromHob (); + ASSERT (MailboxLocationInHob != NULL); + *MailboxLocationInHob = (UINT64)(UINTN)NewMailbox; + SetLocationSavedMailboxPointerInIdtEntry (MailboxLocationInHob); + // + // Update Debug Port Handle in new Mailbox + // + UpdateMailboxContent (NewMailbox, DEBUG_MAILBOX_DEBUG_PORT_HANDLE_INDEX, (UINT64)(UINTN)(NewMailbox + 1)); + // + // Set physical memory ready flag + // + SetDebugFlag (DEBUG_AGENT_FLAG_MEMORY_READY, 1); + + if (IsHostAttached ()) { + // + // Trigger one software interrupt to inform HOST + // + TriggerSoftInterrupt (MEMORY_READY_SIGNATURE); + } + + // + // Restore interrupt state. + // + SetInterruptState (InterruptStatus); + + return EFI_SUCCESS; +} + +/** + Initialize debug agent. + + This function is used to set up debug environment for SEC and PEI phase. + + If InitFlag is DEBUG_AGENT_INIT_PREMEM_SEC, it will overirde IDT table entries + and initialize debug port. It will enable interrupt to support break-in feature. + It will set up debug agent Mailbox in cache-as-ramfrom. It will be called before + physical memory is ready. + If InitFlag is DEBUG_AGENT_INIT_POSTMEM_SEC, debug agent will build one GUIDed + HOB to copy debug agent Mailbox. It will be called after physical memory is ready. + + This function is used to set up debug environment to support source level debugging. + If certain Debug Agent Library instance has to save some private data in the stack, + this function must work on the mode that doesn't return to the caller, then + the caller needs to wrap up all rest of logic after InitializeDebugAgent() into one + function and pass it into InitializeDebugAgent(). InitializeDebugAgent() is + responsible to invoke the passing-in function at the end of InitializeDebugAgent(). + + If the parameter Function is not NULL, Debug Agent Library instance will invoke it by + passing in the Context to be its parameter. + + If Function() is NULL, Debug Agent Library instance will return after setup debug + environment. + + @param[in] InitFlag Init flag is used to decide the initialize process. + @param[in] Context Context needed according to InitFlag; it was optional. + @param[in] Function Continue function called by debug agent library; it was + optional. + +**/ +VOID +EFIAPI +InitializeDebugAgent ( + IN UINT32 InitFlag, + IN VOID *Context, OPTIONAL + IN DEBUG_AGENT_CONTINUE Function OPTIONAL + ) +{ + DEBUG_AGENT_MAILBOX *Mailbox; + DEBUG_AGENT_MAILBOX *NewMailbox; + DEBUG_AGENT_MAILBOX MailboxInStack; + DEBUG_AGENT_PHASE2_CONTEXT Phase2Context; + DEBUG_AGENT_CONTEXT_POSTMEM_SEC *DebugAgentContext; + EFI_STATUS Status; + IA32_DESCRIPTOR *Ia32Idtr; + IA32_IDT_ENTRY *Ia32IdtEntry; + UINT64 DebugPortHandle; + UINT64 MailboxLocation; + UINT64 *MailboxLocationPointer; + EFI_PHYSICAL_ADDRESS Address; + UINT32 DebugTimerFrequency; + BOOLEAN CpuInterruptState; + + // + // Disable interrupts and save current interrupt state + // + CpuInterruptState = SaveAndDisableInterrupts(); + + switch (InitFlag) { + + case DEBUG_AGENT_INIT_PREMEM_SEC: + + InitializeDebugIdt (); + + MailboxLocation = (UINT64)(UINTN)&MailboxInStack; + Mailbox = &MailboxInStack; + ZeroMem ((VOID *) Mailbox, sizeof (DEBUG_AGENT_MAILBOX)); + // + // Get and save debug port handle and set the length of memory block. + // + SetLocationSavedMailboxPointerInIdtEntry (&MailboxLocation); + // + // Force error message could be printed during the first shakehand between Target/HOST. + // + SetDebugFlag (DEBUG_AGENT_FLAG_PRINT_ERROR_LEVEL, DEBUG_AGENT_ERROR); + // + // Save init arch type when debug agent initialized + // + SetDebugFlag (DEBUG_AGENT_FLAG_INIT_ARCH, DEBUG_ARCH_SYMBOL); + // + // Initialize Debug Timer hardware and save its frequency + // + InitializeDebugTimer (&DebugTimerFrequency, TRUE); + UpdateMailboxContent (Mailbox, DEBUG_MAILBOX_DEBUG_TIMER_FREQUENCY, DebugTimerFrequency); + + Phase2Context.InitFlag = InitFlag; + Phase2Context.Context = Context; + Phase2Context.Function = Function; + DebugPortInitialize ((VOID *) &Phase2Context, InitializeDebugAgentPhase2); + // + // If reaches here, it means Debug Port initialization failed. + // + DEBUG ((EFI_D_ERROR, "Debug Agent: Debug port initialization failed.\n")); + + break; + + case DEBUG_AGENT_INIT_POSTMEM_SEC: + Mailbox = GetMailboxPointer (); + // + // Memory has been ready + // + SetDebugFlag (DEBUG_AGENT_FLAG_MEMORY_READY, 1); + if (IsHostAttached ()) { + // + // Trigger one software interrupt to inform HOST + // + TriggerSoftInterrupt (MEMORY_READY_SIGNATURE); + } + // + // Install Vector Handoff Info PPI to persist vectors used by Debug Agent + // + Status = PeiServicesInstallPpi (&mVectorHandoffInfoPpiList[0]); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "DebugAgent: Failed to install Vector Handoff Info PPI!\n")); + CpuDeadLoop (); + } + // + // Fix up Debug Port handle address and mailbox address + // + DebugAgentContext = (DEBUG_AGENT_CONTEXT_POSTMEM_SEC *) Context; + if (DebugAgentContext != NULL) { + DebugPortHandle = (UINT64)(UINT32)(Mailbox->DebugPortHandle + DebugAgentContext->StackMigrateOffset); + UpdateMailboxContent (Mailbox, DEBUG_MAILBOX_DEBUG_PORT_HANDLE_INDEX, DebugPortHandle); + Mailbox = (DEBUG_AGENT_MAILBOX *) ((UINTN) Mailbox + DebugAgentContext->StackMigrateOffset); + MailboxLocation = (UINT64)(UINTN)Mailbox; + // + // Build mailbox location in HOB and fix-up its address + // + MailboxLocationPointer = BuildGuidDataHob ( + &gEfiDebugAgentGuid, + &MailboxLocation, + sizeof (UINT64) + ); + MailboxLocationPointer = (UINT64 *) ((UINTN) MailboxLocationPointer + DebugAgentContext->HeapMigrateOffset); + } else { + // + // DebugAgentContext is NULL. Then, Mailbox can directly be copied into memory. + // Allocate ACPI NVS memory for new Mailbox and Debug Port Handle buffer + // + Status = PeiServicesAllocatePages ( + EfiACPIMemoryNVS, + EFI_SIZE_TO_PAGES (sizeof(DEBUG_AGENT_MAILBOX) + PcdGet16(PcdDebugPortHandleBufferSize)), + &Address + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "DebugAgent: Failed to allocate pages!\n")); + CpuDeadLoop (); + } + NewMailbox = (DEBUG_AGENT_MAILBOX *) (UINTN) Address; + // + // Copy Mailbox and Debug Port Handle buffer to new location in ACPI NVS memory, because original Mailbox + // and Debug Port Handle buffer in the allocated pool that may be marked as free by DXE Core after DXE Core + // reallocates the HOB. + // + CopyMem (NewMailbox, Mailbox, sizeof (DEBUG_AGENT_MAILBOX)); + CopyMem (NewMailbox + 1, (VOID *)(UINTN)Mailbox->DebugPortHandle, PcdGet16(PcdDebugPortHandleBufferSize)); + UpdateMailboxContent (NewMailbox, DEBUG_MAILBOX_DEBUG_PORT_HANDLE_INDEX, (UINT64)(UINTN)(NewMailbox + 1)); + MailboxLocation = (UINT64)(UINTN)NewMailbox; + // + // Build mailbox location in HOB + // + MailboxLocationPointer = BuildGuidDataHob ( + &gEfiDebugAgentGuid, + &MailboxLocation, + sizeof (UINT64) + ); + } + // + // Update IDT entry to save the location saved mailbox pointer + // + SetLocationSavedMailboxPointerInIdtEntry (MailboxLocationPointer); + break; + + case DEBUG_AGENT_INIT_PEI: + if (Context == NULL) { + DEBUG ((EFI_D_ERROR, "DebugAgent: Input parameter Context cannot be NULL!\n")); + CpuDeadLoop (); + } + // + // Check if Debug Agent has initialized before + // + if (IsDebugAgentInitialzed()) { + DEBUG ((EFI_D_WARN, "Debug Agent: It has already initialized in SEC Core!\n")); + break; + } + // + // Install Vector Handoff Info PPI to persist vectors used by Debug Agent + // + Status = PeiServicesInstallPpi (&mVectorHandoffInfoPpiList[0]); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "DebugAgent: Failed to install Vector Handoff Info PPI!\n")); + CpuDeadLoop (); + } + // + // Set up IDT entries + // + InitializeDebugIdt (); + // + // Build mailbox in HOB and setup Mailbox Set In Pei flag + // + Mailbox = AllocateZeroPool (sizeof (DEBUG_AGENT_MAILBOX)); + if (Mailbox == NULL) { + DEBUG ((EFI_D_ERROR, "DebugAgent: Failed to allocate memory!\n")); + CpuDeadLoop (); + } else { + MailboxLocation = (UINT64)(UINTN)Mailbox; + MailboxLocationPointer = BuildGuidDataHob ( + &gEfiDebugAgentGuid, + &MailboxLocation, + sizeof (UINT64) + ); + // + // Initialize Debug Timer hardware and save its frequency + // + InitializeDebugTimer (&DebugTimerFrequency, TRUE); + UpdateMailboxContent (Mailbox, DEBUG_MAILBOX_DEBUG_TIMER_FREQUENCY, DebugTimerFrequency); + // + // Update IDT entry to save the location pointer saved mailbox pointer + // + SetLocationSavedMailboxPointerInIdtEntry (MailboxLocationPointer); + } + // + // Save init arch type when debug agent initialized + // + SetDebugFlag (DEBUG_AGENT_FLAG_INIT_ARCH, DEBUG_ARCH_SYMBOL); + // + // Register for a callback once memory has been initialized. + // If memery has been ready, the callback funtion will be invoked immediately + // + Status = PeiServicesNotifyPpi (&mMemoryDiscoveredNotifyList[0]); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "DebugAgent: Failed to register memory discovered callback function!\n")); + CpuDeadLoop (); + } + // + // Set HOB check flag if memory has not been ready yet + // + if (GetDebugFlag (DEBUG_AGENT_FLAG_MEMORY_READY) == 0) { + SetDebugFlag (DEBUG_AGENT_FLAG_CHECK_MAILBOX_IN_HOB, 1); + } + + Phase2Context.InitFlag = InitFlag; + Phase2Context.Context = Context; + Phase2Context.Function = Function; + DebugPortInitialize ((VOID *) &Phase2Context, InitializeDebugAgentPhase2); + + FindAndReportModuleImageInfo (4); + + break; + + case DEBUG_AGENT_INIT_THUNK_PEI_IA32TOX64: + if (Context == NULL) { + DEBUG ((EFI_D_ERROR, "DebugAgent: Input parameter Context cannot be NULL!\n")); + CpuDeadLoop (); + } else { + Ia32Idtr = (IA32_DESCRIPTOR *) Context; + Ia32IdtEntry = (IA32_IDT_ENTRY *)(Ia32Idtr->Base); + MailboxLocationPointer = (UINT64 *) ((UINTN) Ia32IdtEntry[DEBUG_MAILBOX_VECTOR].Bits.OffsetLow + + ((UINTN) Ia32IdtEntry[DEBUG_MAILBOX_VECTOR].Bits.OffsetHigh << 16)); + Mailbox = (DEBUG_AGENT_MAILBOX *) (UINTN)(*MailboxLocationPointer); + // + // Mailbox should valid and setup before executing thunk code + // + VerifyMailboxChecksum (Mailbox); + + DebugPortHandle = (UINT64) (UINTN)DebugPortInitialize ((VOID *)(UINTN)Mailbox->DebugPortHandle, NULL); + UpdateMailboxContent (Mailbox, DEBUG_MAILBOX_DEBUG_PORT_HANDLE_INDEX, DebugPortHandle); + // + // Set up IDT entries + // + InitializeDebugIdt (); + // + // Update IDT entry to save location pointer saved the mailbox pointer + // + SetLocationSavedMailboxPointerInIdtEntry (MailboxLocationPointer); + + FindAndReportModuleImageInfo (4); + } + break; + + default: + // + // Only DEBUG_AGENT_INIT_PREMEM_SEC and DEBUG_AGENT_INIT_POSTMEM_SEC are allowed for this + // Debug Agent library instance. + // + DEBUG ((EFI_D_ERROR, "Debug Agent: The InitFlag value is not allowed!\n")); + CpuDeadLoop (); + break; + } + + if (InitFlag == DEBUG_AGENT_INIT_POSTMEM_SEC) { + // + // Restore CPU Interrupt state and keep debug timer interrupt state as is + // in DEBUG_AGENT_INIT_POSTMEM_SEC case + // + SetInterruptState (CpuInterruptState); + } else { + // + // Enable Debug Timer interrupt + // + SaveAndSetDebugTimerInterrupt (TRUE); + // + // Enable CPU interrupts so debug timer interrupts can be delivered + // + EnableInterrupts (); + } + // + // If Function is not NULL, invoke it always whatever debug agent was initialized sucesssfully or not. + // + if (Function != NULL) { + Function (Context); + } + // + // Set return status for DEBUG_AGENT_INIT_PEI + // + if (InitFlag == DEBUG_AGENT_INIT_PEI && Context != NULL) { + *(EFI_STATUS *)Context = EFI_SUCCESS; + } +} + +/** + Caller provided function to be invoked at the end of DebugPortInitialize(). + + Refer to the description for DebugPortInitialize() for more details. + + @param[in] Context The first input argument of DebugPortInitialize(). + @param[in] DebugPortHandle Debug port handle created by Debug Communication Library. + +**/ +VOID +EFIAPI +InitializeDebugAgentPhase2 ( + IN VOID *Context, + IN DEBUG_PORT_HANDLE DebugPortHandle + ) +{ + DEBUG_AGENT_PHASE2_CONTEXT *Phase2Context; + UINT64 *MailboxLocation; + DEBUG_AGENT_MAILBOX *Mailbox; + EFI_SEC_PEI_HAND_OFF *SecCoreData; + UINT16 BufferSize; + UINT64 NewDebugPortHandle; + + Phase2Context = (DEBUG_AGENT_PHASE2_CONTEXT *) Context; + MailboxLocation = GetLocationSavedMailboxPointerInIdtEntry (); + Mailbox = (DEBUG_AGENT_MAILBOX *)(UINTN)(*MailboxLocation); + BufferSize = PcdGet16(PcdDebugPortHandleBufferSize); + if (Phase2Context->InitFlag == DEBUG_AGENT_INIT_PEI && BufferSize != 0) { + NewDebugPortHandle = (UINT64)(UINTN)AllocateCopyPool (BufferSize, DebugPortHandle); + } else { + NewDebugPortHandle = (UINT64)(UINTN)DebugPortHandle; + } + UpdateMailboxContent (Mailbox, DEBUG_MAILBOX_DEBUG_PORT_HANDLE_INDEX, NewDebugPortHandle); + + // + // Trigger one software interrupt to inform HOST + // + TriggerSoftInterrupt (SYSTEM_RESET_SIGNATURE); + + if (Phase2Context->InitFlag == DEBUG_AGENT_INIT_PREMEM_SEC) { + // + // If Temporary RAM region is below 128 MB, then send message to + // host to disable low memory filtering. + // + SecCoreData = (EFI_SEC_PEI_HAND_OFF *)Phase2Context->Context; + if ((UINTN)SecCoreData->TemporaryRamBase < BASE_128MB && IsHostAttached ()) { + SetDebugFlag (DEBUG_AGENT_FLAG_MEMORY_READY, 1); + TriggerSoftInterrupt (MEMORY_READY_SIGNATURE); + } + // + // Enable Debug Timer interrupt + // + SaveAndSetDebugTimerInterrupt (TRUE); + // + // Enable CPU interrupts so debug timer interrupts can be delivered + // + EnableInterrupts (); + // + // Call continuation function if it is not NULL. + // + Phase2Context->Function (Phase2Context->Context); + } +} diff --git a/Core/SourceLevelDebugPkg/Library/DebugAgent/SecPeiDebugAgent/SecPeiDebugAgentLib.h b/Core/SourceLevelDebugPkg/Library/DebugAgent/SecPeiDebugAgent/SecPeiDebugAgentLib.h new file mode 100644 index 0000000000..300bc35c40 --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugAgent/SecPeiDebugAgent/SecPeiDebugAgentLib.h @@ -0,0 +1,65 @@ +/** @file + Header file for Sec Core Debug Agent Library instance. + + Copyright (c) 2010 - 2013, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _SEC_CORE_DEBUG_AGENT_LIB_H_ +#define _SEC_CORE_DEBUG_AGENT_LIB_H_ + +#include +#include +#include +#include +#include "DebugAgent.h" + +typedef struct { + UINT32 InitFlag; + VOID *Context; + DEBUG_AGENT_CONTINUE Function; +} DEBUG_AGENT_PHASE2_CONTEXT; + +/** + Caller provided function to be invoked at the end of DebugPortInitialize(). + + Refer to the description for DebugPortInitialize() for more details. + + @param[in] Context The first input argument of DebugPortInitialize(). + @param[in] DebugPortHandle Debug port handle created by Debug Communication Library. + +**/ +VOID +EFIAPI +InitializeDebugAgentPhase2 ( + IN VOID *Context, + IN DEBUG_PORT_HANDLE DebugPortHandle + ); + +/** + Debug Agent provided notify callback function on Memory Discovered PPI. + + @param[in] PeiServices Indirect reference to the PEI Services Table. + @param[in] NotifyDescriptor Address of the notification descriptor data structure. + @param[in] Ppi Address of the PPI that was installed. + + @retval EFI_SUCCESS If the function completed successfully. + +**/ +EFI_STATUS +EFIAPI +DebugAgentCallbackMemoryDiscoveredPpi ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor, + IN VOID *Ppi + ); + +#endif + diff --git a/Core/SourceLevelDebugPkg/Library/DebugAgent/SecPeiDebugAgentLib.inf b/Core/SourceLevelDebugPkg/Library/DebugAgent/SecPeiDebugAgentLib.inf new file mode 100644 index 0000000000..12c2a71b78 --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugAgent/SecPeiDebugAgentLib.inf @@ -0,0 +1,94 @@ +## @file +# Debug Agent library instance for SEC Core and PEI modules. +# +# Copyright (c) 2010 - 2016, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php. +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = SecPeiDebugAgentLib + MODULE_UNI_FILE = SecPeiDebugAgentLib.uni + FILE_GUID = 508B7D59-CD4E-4a6b-A45B-6D3B2D90111E + MODULE_TYPE = PEIM + VERSION_STRING = 0.8 + LIBRARY_CLASS = DebugAgentLib|SEC PEIM + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources.common] + SecPeiDebugAgent/SecPeiDebugAgentLib.c + SecPeiDebugAgent/SecPeiDebugAgentLib.h + DebugAgentCommon/DebugAgent.c + DebugAgentCommon/DebugAgent.h + DebugAgentCommon/DebugTimer.c + DebugAgentCommon/DebugTimer.h + DebugAgentCommon/DebugMp.c + DebugAgentCommon/DebugMp.h + +[Sources.Ia32] + DebugAgentCommon/Ia32/AsmFuncs.S + DebugAgentCommon/Ia32/AsmFuncs.asm + DebugAgentCommon/Ia32/AsmFuncs.nasm + DebugAgentCommon/Ia32/ArchDebugSupport.h + DebugAgentCommon/Ia32/ArchDebugSupport.c + DebugAgentCommon/Ia32/DebugException.h + +[Sources.X64] + DebugAgentCommon/X64/AsmFuncs.S + DebugAgentCommon/X64/AsmFuncs.asm + DebugAgentCommon/X64/AsmFuncs.nasm + DebugAgentCommon/X64/ArchDebugSupport.h + DebugAgentCommon/X64/ArchDebugSupport.c + DebugAgentCommon/X64/DebugException.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + SourceLevelDebugPkg/SourceLevelDebugPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + ResetSystemLib + IoLib + HobLib + PcdLib + DebugCommunicationLib + SynchronizationLib + LocalApicLib + DebugLib + TimerLib + PrintLib + PeiServicesLib + MemoryAllocationLib + PeCoffGetEntryPointLib + PeCoffExtraActionLib + +[Ppis] + gEfiPeiMemoryDiscoveredPpiGuid ## NOTIFY + gEfiVectorHandoffInfoPpiGuid ## PRODUCES + +[Guids] + ## PRODUCES ## HOB + ## CONSUMES ## HOB + gEfiDebugAgentGuid + +[Pcd] + gEfiMdePkgTokenSpaceGuid.PcdFSBClock ## SOMETIMES_CONSUMES + gEfiSourceLevelDebugPkgTokenSpaceGuid.PcdExceptionsIgnoredByDebugger ## SOMETIMES_CONSUMES + gEfiSourceLevelDebugPkgTokenSpaceGuid.PcdDebugPortHandleBufferSize ## SOMETIMES_CONSUMES + diff --git a/Core/SourceLevelDebugPkg/Library/DebugAgent/SecPeiDebugAgentLib.uni b/Core/SourceLevelDebugPkg/Library/DebugAgent/SecPeiDebugAgentLib.uni new file mode 100644 index 0000000000..389e2af7fd --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugAgent/SecPeiDebugAgentLib.uni @@ -0,0 +1,21 @@ +// /** @file +// Debug Agent library instance for SEC Core and PEI modules. +// +// Debug Agent library instance for SEC Core and PEI modules. +// +// Copyright (c) 2010 - 2014, Intel Corporation. All rights reserved.
+// +// This program and the accompanying materials +// are licensed and made available under the terms and conditions of the BSD License +// which accompanies this distribution. The full text of the license may be found at +// http://opensource.org/licenses/bsd-license.php. +// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Debug Agent library instance for SEC Core and PEI modules" + +#string STR_MODULE_DESCRIPTION #language en-US "Debug Agent library instance for SEC Core and PEI modules." + diff --git a/Core/SourceLevelDebugPkg/Library/DebugAgent/SmmDebugAgent/SmmDebugAgentLib.c b/Core/SourceLevelDebugPkg/Library/DebugAgent/SmmDebugAgent/SmmDebugAgentLib.c new file mode 100644 index 0000000000..11afd329fa --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugAgent/SmmDebugAgent/SmmDebugAgentLib.c @@ -0,0 +1,381 @@ +/** @file + Debug Agent library implementition. + + Copyright (c) 2010 - 2017, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "SmmDebugAgentLib.h" + +DEBUG_AGENT_MAILBOX *mMailboxPointer = NULL; +DEBUG_AGENT_MAILBOX mLocalMailbox; +UINTN mSavedDebugRegisters[6]; +IA32_IDT_GATE_DESCRIPTOR mIdtEntryTable[33]; +BOOLEAN mSkipBreakpoint = FALSE; +BOOLEAN mSmmDebugIdtInitFlag = FALSE; + +CHAR8 mWarningMsgIgnoreSmmEntryBreak[] = "Ignore smmentrybreak setting for SMI issued during DXE debugging!\r\n"; + +/** + Check if debug agent support multi-processor. + + @retval TRUE Multi-processor is supported. + @retval FALSE Multi-processor is not supported. + +**/ +BOOLEAN +MultiProcessorDebugSupport ( + VOID + ) +{ + return FALSE; +} + +/** + Read the Attach/Break-in symbols from the debug port. + + @param[in] Handle Pointer to Debug Port handle. + @param[out] BreakSymbol Returned break symbol. + + @retval EFI_SUCCESS Read the symbol in BreakSymbol. + @retval EFI_NOT_FOUND No read the break symbol. + +**/ +EFI_STATUS +DebugReadBreakSymbol ( + IN DEBUG_PORT_HANDLE Handle, + OUT UINT8 *BreakSymbol + ) +{ + // + // Smm instance has no debug timer to poll break symbol. + // + return EFI_NOT_FOUND; +} + +/** + Get the pointer to Mailbox from the GUIDed HOB. + + @return Pointer to Mailbox. + +**/ +DEBUG_AGENT_MAILBOX * +GetMailboxFromHob ( + VOID + ) +{ + EFI_HOB_GUID_TYPE *GuidHob; + UINT64 *MailboxLocation; + DEBUG_AGENT_MAILBOX *Mailbox; + + GuidHob = GetFirstGuidHob (&gEfiDebugAgentGuid); + if (GuidHob == NULL) { + return NULL; + } + MailboxLocation = (UINT64 *) (GET_GUID_HOB_DATA(GuidHob)); + Mailbox = (DEBUG_AGENT_MAILBOX *)(UINTN)(*MailboxLocation); + VerifyMailboxChecksum (Mailbox); + + return Mailbox; +} + +/** + Get Debug Agent Mailbox pointer. + + @return Mailbox pointer. + +**/ +DEBUG_AGENT_MAILBOX * +GetMailboxPointer ( + VOID + ) +{ + VerifyMailboxChecksum (mMailboxPointer); + return mMailboxPointer; +} + +/** + Get debug port handle. + + @return Debug port handle. + +**/ +DEBUG_PORT_HANDLE +GetDebugPortHandle ( + VOID + ) +{ + return (DEBUG_PORT_HANDLE) (UINTN)(GetMailboxPointer()->DebugPortHandle); +} + +/** + Store debug register when SMI exit. + +**/ +VOID +SaveDebugRegister ( + VOID + ) +{ + mSavedDebugRegisters[0] = AsmReadDr0 (); + mSavedDebugRegisters[1] = AsmReadDr1 (); + mSavedDebugRegisters[2] = AsmReadDr2 (); + mSavedDebugRegisters[3] = AsmReadDr3 (); + mSavedDebugRegisters[4] = AsmReadDr6 (); + mSavedDebugRegisters[5] = AsmReadDr7 (); +} + +/** + Restore debug register when SMI exit. + +**/ +VOID +RestoreDebugRegister ( + VOID + ) +{ + AsmWriteDr7 (0); + AsmWriteDr0 (mSavedDebugRegisters[0]); + AsmWriteDr1 (mSavedDebugRegisters[1]); + AsmWriteDr2 (mSavedDebugRegisters[2]); + AsmWriteDr3 (mSavedDebugRegisters[3]); + AsmWriteDr6 (mSavedDebugRegisters[4]); + AsmWriteDr7 (mSavedDebugRegisters[5]); +} + +/** + Initialize debug agent. + + This function is used to set up debug enviroment for source level debug + in SMM code. + + If InitFlag is DEBUG_AGENT_INIT_SMM, it will overirde IDT table entries + and initialize debug port. It will get debug agent Mailbox from GUIDed HOB, + it it exists, debug agent wiil copied it into the local Mailbox in SMM space. + it will overirde IDT table entries and initialize debug port. Context will be + NULL. + If InitFlag is DEBUG_AGENT_INIT_ENTER_SMI, debug agent will save Debug + Registers and get local Mailbox in SMM space. Context will be NULL. + If InitFlag is DEBUG_AGENT_INIT_EXIT_SMI, debug agent will restore Debug + Registers. Context will be NULL. + + @param[in] InitFlag Init flag is used to decide initialize process. + @param[in] Context Context needed according to InitFlag. + @param[in] Function Continue function called by debug agent library; it was + optional. + +**/ +VOID +EFIAPI +InitializeDebugAgent ( + IN UINT32 InitFlag, + IN VOID *Context, OPTIONAL + IN DEBUG_AGENT_CONTINUE Function OPTIONAL + ) +{ + EFI_STATUS Status; + UINT64 DebugPortHandle; + IA32_IDT_GATE_DESCRIPTOR IdtEntry[33]; + IA32_DESCRIPTOR IdtDescriptor; + IA32_DESCRIPTOR *Ia32Idtr; + IA32_IDT_ENTRY *Ia32IdtEntry; + IA32_DESCRIPTOR Idtr; + UINT16 IdtEntryCount; + DEBUG_AGENT_MAILBOX *Mailbox; + UINT64 *MailboxLocation; + UINT32 DebugTimerFrequency; + BOOLEAN PeriodicMode; + UINTN TimerCycle; + + switch (InitFlag) { + case DEBUG_AGENT_INIT_SMM: + // + // Install configuration table for persisted vector handoff info + // + Status = gSmst->SmmInstallConfigurationTable ( + gSmst, + &gEfiVectorHandoffTableGuid, + (VOID *) &mVectorHandoffInfoDebugAgent[0], + sizeof (EFI_VECTOR_HANDOFF_INFO) * mVectorHandoffInfoCount + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "DebugAgent: Cannot install configuration table for persisted vector handoff info!\n")); + CpuDeadLoop (); + } + // + // Check if Debug Agent initialized in DXE phase + // + Status = EfiGetSystemConfigurationTable (&gEfiDebugAgentGuid, (VOID **) &Mailbox); + if (Status == EFI_SUCCESS && Mailbox != NULL) { + VerifyMailboxChecksum (Mailbox); + mMailboxPointer = Mailbox; + break; + } + // + // Check if Debug Agent initialized in SEC/PEI phase + // + Mailbox = GetMailboxFromHob (); + if (Mailbox != NULL) { + mMailboxPointer = Mailbox; + break; + } + // + // Debug Agent was not initialized before, use the local mailbox. + // + ZeroMem (&mLocalMailbox, sizeof (DEBUG_AGENT_MAILBOX)); + Mailbox = &mLocalMailbox; + // + // Save original IDT entries + // + AsmReadIdtr (&IdtDescriptor); + CopyMem (&IdtEntry, (VOID *)IdtDescriptor.Base, 33 * sizeof(IA32_IDT_GATE_DESCRIPTOR)); + // + // Initialized Debug Agent + // + InitializeDebugIdt (); + // + // Initialize Debug Timer hardware and save its frequency + // + InitializeDebugTimer (&DebugTimerFrequency, TRUE); + UpdateMailboxContent (Mailbox, DEBUG_MAILBOX_DEBUG_TIMER_FREQUENCY, DebugTimerFrequency); + + DebugPortHandle = (UINT64) (UINTN)DebugPortInitialize ((DEBUG_PORT_HANDLE) (UINTN)Mailbox->DebugPortHandle, NULL); + UpdateMailboxContent (Mailbox, DEBUG_MAILBOX_DEBUG_PORT_HANDLE_INDEX, DebugPortHandle); + mMailboxPointer = Mailbox; + // + // Trigger one software interrupt to inform HOST + // + TriggerSoftInterrupt (SYSTEM_RESET_SIGNATURE); + + SetDebugFlag (DEBUG_AGENT_FLAG_MEMORY_READY, 1); + // + // Memory has been ready + // + if (IsHostAttached ()) { + // + // Trigger one software interrupt to inform HOST + // + TriggerSoftInterrupt (MEMORY_READY_SIGNATURE); + } + // + // Find and report PE/COFF image info to HOST + // + FindAndReportModuleImageInfo (SIZE_4KB); + // + // Restore saved IDT entries + // + CopyMem ((VOID *)IdtDescriptor.Base, &IdtEntry, 33 * sizeof(IA32_IDT_GATE_DESCRIPTOR)); + + break; + + case DEBUG_AGENT_INIT_ENTER_SMI: + SaveDebugRegister (); + if (!mSmmDebugIdtInitFlag) { + // + // We only need to initialize Debug IDT table at first SMI entry + // after SMM relocation. + // + InitializeDebugIdt (); + mSmmDebugIdtInitFlag = TRUE; + } + // + // Check if CPU APIC Timer is working, otherwise initialize it. + // + InitializeLocalApicSoftwareEnable (TRUE); + GetApicTimerState (NULL, &PeriodicMode, NULL); + TimerCycle = GetApicTimerInitCount (); + if (!PeriodicMode || TimerCycle == 0) { + InitializeDebugTimer (NULL, FALSE); + } + Mailbox = GetMailboxPointer (); + if (GetDebugFlag (DEBUG_AGENT_FLAG_AGENT_IN_PROGRESS) == 1) { + // + // If Debug Agent has been communicaton state with HOST, we need skip + // any break points set in SMM, set Skip Breakpoint flag + // + mSkipBreakpoint = TRUE; + } + if (GetDebugFlag (DEBUG_AGENT_FLAG_BREAK_ON_NEXT_SMI) == 1) { + if (mSkipBreakpoint) { + // + // Print warning message if ignore smm entry break + // + DebugPortWriteBuffer ((DEBUG_PORT_HANDLE) (UINTN)Mailbox->DebugPortHandle, + (UINT8 *)mWarningMsgIgnoreSmmEntryBreak, + AsciiStrLen (mWarningMsgIgnoreSmmEntryBreak) + ); + } else { + // + // If SMM entry break is set, SMM code will be break at here. + // + CpuBreakpoint (); + } + } + break; + + case DEBUG_AGENT_INIT_EXIT_SMI: + Mailbox = GetMailboxPointer (); + // + // Clear Skip Breakpoint flag + // + mSkipBreakpoint = FALSE; + RestoreDebugRegister (); + break; + + case DEBUG_AGENT_INIT_THUNK_PEI_IA32TOX64: + if (Context == NULL) { + DEBUG ((EFI_D_ERROR, "DebugAgent: Input parameter Context cannot be NULL!\n")); + CpuDeadLoop (); + } else { + Ia32Idtr = (IA32_DESCRIPTOR *) Context; + Ia32IdtEntry = (IA32_IDT_ENTRY *)(Ia32Idtr->Base); + MailboxLocation = (UINT64 *) ((UINTN) Ia32IdtEntry[DEBUG_MAILBOX_VECTOR].Bits.OffsetLow + + ((UINTN) Ia32IdtEntry[DEBUG_MAILBOX_VECTOR].Bits.OffsetHigh << 16)); + mMailboxPointer = (DEBUG_AGENT_MAILBOX *)(UINTN)(*MailboxLocation); + VerifyMailboxChecksum (mMailboxPointer); + // + // Get original IDT address and size. + // + AsmReadIdtr ((IA32_DESCRIPTOR *) &Idtr); + IdtEntryCount = (UINT16) ((Idtr.Limit + 1) / sizeof (IA32_IDT_GATE_DESCRIPTOR)); + if (IdtEntryCount < 33) { + Idtr.Limit = (UINT16) (sizeof (IA32_IDT_GATE_DESCRIPTOR) * 33 - 1); + Idtr.Base = (UINTN) &mIdtEntryTable; + ZeroMem (&mIdtEntryTable, Idtr.Limit + 1); + AsmWriteIdtr ((IA32_DESCRIPTOR *) &Idtr); + } + + InitializeDebugIdt (); + // + // Initialize Debug Timer hardware and save its frequency + // + InitializeDebugTimer (&DebugTimerFrequency, TRUE); + UpdateMailboxContent (mMailboxPointer, DEBUG_MAILBOX_DEBUG_TIMER_FREQUENCY, DebugTimerFrequency); + // + // Enable Debug Timer interrupt and CPU interrupt + // + SaveAndSetDebugTimerInterrupt (TRUE); + EnableInterrupts (); + + FindAndReportModuleImageInfo (SIZE_4KB); + } + break; + + default: + // + // Only DEBUG_AGENT_INIT_PREMEM_SEC and DEBUG_AGENT_INIT_POSTMEM_SEC are allowed for this + // Debug Agent library instance. + // + DEBUG ((EFI_D_ERROR, "Debug Agent: The InitFlag value is not allowed!\n")); + CpuDeadLoop (); + break; + } +} + diff --git a/Core/SourceLevelDebugPkg/Library/DebugAgent/SmmDebugAgent/SmmDebugAgentLib.h b/Core/SourceLevelDebugPkg/Library/DebugAgent/SmmDebugAgent/SmmDebugAgentLib.h new file mode 100644 index 0000000000..f30a892e21 --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugAgent/SmmDebugAgent/SmmDebugAgentLib.h @@ -0,0 +1,25 @@ +/** @file + Header file for Smm Debug Agent Library instance. + + Copyright (c) 2010 - 2013, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _SMM_DEBUG_AGENT_LIB_H_ +#define _SMM_DEBUG_AGENT_LIB_H_ + +#include + +#include +#include + +#include "DebugAgent.h" + +#endif diff --git a/Core/SourceLevelDebugPkg/Library/DebugAgent/SmmDebugAgentLib.inf b/Core/SourceLevelDebugPkg/Library/DebugAgent/SmmDebugAgentLib.inf new file mode 100644 index 0000000000..1fa5745b1c --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugAgent/SmmDebugAgentLib.inf @@ -0,0 +1,89 @@ +## @file +# Debug Agent library instance for SMM modules. +# +# Copyright (c) 2010 - 2016, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php. +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = SmmDebugAgentLib + MODULE_UNI_FILE = SmmDebugAgentLib.uni + FILE_GUID = CB07D74C-598F-4268-A5D1-644FB4A481E8 + MODULE_TYPE = DXE_SMM_DRIVER + VERSION_STRING = 0.8 + LIBRARY_CLASS = DebugAgentLib|DXE_SMM_DRIVER + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources.common] + SmmDebugAgent/SmmDebugAgentLib.c + SmmDebugAgent/SmmDebugAgentLib.h + DebugAgentCommon/DebugAgent.c + DebugAgentCommon/DebugAgent.h + DebugAgentCommon/DebugTimer.c + DebugAgentCommon/DebugTimer.h + DebugAgentCommon/DebugMp.c + DebugAgentCommon/DebugMp.h + +[Sources.Ia32] + DebugAgentCommon/Ia32/AsmFuncs.S + DebugAgentCommon/Ia32/AsmFuncs.asm + DebugAgentCommon/Ia32/AsmFuncs.nasm + DebugAgentCommon/Ia32/ArchDebugSupport.h + DebugAgentCommon/Ia32/ArchDebugSupport.c + DebugAgentCommon/Ia32/DebugException.h + +[Sources.X64] + DebugAgentCommon/X64/AsmFuncs.S + DebugAgentCommon/X64/AsmFuncs.asm + DebugAgentCommon/X64/AsmFuncs.nasm + DebugAgentCommon/X64/ArchDebugSupport.h + DebugAgentCommon/X64/ArchDebugSupport.c + DebugAgentCommon/X64/DebugException.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + SourceLevelDebugPkg/SourceLevelDebugPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + ResetSystemLib + IoLib + DebugCommunicationLib + UefiLib + PcdLib + SynchronizationLib + LocalApicLib + TimerLib + PrintLib + PeCoffExtraActionLib + PeCoffGetEntryPointLib + SmmServicesTableLib + +[Guids] + ## CONSUMES ## SystemTable + ## CONSUMES ## HOB + gEfiDebugAgentGuid + gEfiVectorHandoffTableGuid ## PRODUCES ## GUID # SMM Configuration Table + +[Pcd] + gEfiMdePkgTokenSpaceGuid.PcdFSBClock ## SOMETIMES_CONSUMES + # Skip Page Fault exception (14) by default in SMM + gEfiSourceLevelDebugPkgTokenSpaceGuid.PcdExceptionsIgnoredByDebugger|0x00004000 ## SOMETIMES_CONSUMES + diff --git a/Core/SourceLevelDebugPkg/Library/DebugAgent/SmmDebugAgentLib.uni b/Core/SourceLevelDebugPkg/Library/DebugAgent/SmmDebugAgentLib.uni new file mode 100644 index 0000000000..74d7b718e6 --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugAgent/SmmDebugAgentLib.uni @@ -0,0 +1,21 @@ +// /** @file +// Debug Agent library instance for SMM modules. +// +// Debug Agent library instance for SMM modules. +// +// Copyright (c) 2010 - 2014, Intel Corporation. All rights reserved.
+// +// This program and the accompanying materials +// are licensed and made available under the terms and conditions of the BSD License +// which accompanies this distribution. The full text of the license may be found at +// http://opensource.org/licenses/bsd-license.php. +// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Debug Agent library instance for SMM modules" + +#string STR_MODULE_DESCRIPTION #language en-US "Debug Agent library instance for SMM modules." + diff --git a/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibSerialPort/DebugCommunicationLibSerialPort.c b/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibSerialPort/DebugCommunicationLibSerialPort.c new file mode 100644 index 0000000000..6a854ff2a2 --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibSerialPort/DebugCommunicationLibSerialPort.c @@ -0,0 +1,160 @@ +/** @file + Debug Port Library implementation based on serial port. + + Copyright (c) 2010 - 2015, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include + +#include +#include +#include + +/** + Initialize the debug port. + + This function will initialize debug port to get it ready for data transmition. If + certain Debug Communication Library instance has to save some private data in the + stack, this function must work on the mode that doesn't return to the caller, then + the caller needs to wrap up all rest of logic after DebugPortInitialize() into one + function and pass it into DebugPortInitialize(). DebugPortInitialize() is + responsible to invoke the passing-in function at the end of DebugPortInitialize(). + + If the parameter Function is not NULL, Debug Communication Library instance will + invoke it by passing in the Context to be the first parameter. Debug Communication + Library instance could create one debug port handle to be the second parameter + passing into the Function. Debug Communication Library instance also could pass + NULL to be the second parameter if it doesn't create the debug port handle. + + If the parameter Function is NULL, and Context is not NULL. At this time, Context + is the debug port handle created by the previous Debug Communication Library + instance. + a) If the instance can understand and continue use the private data of the previous + instance, it could return the same handle as passed in (as Context parameter). + b) If the instance does not understand, or does not want to continue use the + private data of the previous instance, it could ignore the input Context parameter + and create the new handle to be returned. + + If Function() is NULL and Context is NULL, Debug Communication Library could create a + new handle and return it. NULL is also a valid handle to be returned. + + @param[in] Context Context needed by callback function; it was optional. + @param[in] Function Continue function called by Debug Communication library; + it was optional. + + @return The debug port handle created by Debug Communication Library if Function + is not NULL. + +**/ +DEBUG_PORT_HANDLE +EFIAPI +DebugPortInitialize ( + IN VOID *Context, + IN DEBUG_PORT_CONTINUE Function + ) +{ + RETURN_STATUS Status; + + Status = SerialPortInitialize (); + if (RETURN_ERROR(Status)) { + DEBUG ((EFI_D_ERROR, "Debug Serial Port: Initialization failed!\n")); + } + + if (Function != NULL) { + Function (Context, NULL); + } + + return NULL; +} + +/** + Read data from debug device and save the datas 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 +EFIAPI +DebugPortReadBuffer ( + IN DEBUG_PORT_HANDLE Handle, + IN UINT8 *Buffer, + IN UINTN NumberOfBytes, + IN UINTN Timeout + ) +{ + if (NumberOfBytes != 1 || Buffer == NULL || Timeout != 0) { + return 0; + } + + return SerialPortRead (Buffer, 1); +} + +/** + Write data from buffer to debug device. + + Writes NumberOfBytes data bytes from Buffer to the debug device. + The number of bytes actually written to the debug device is returned. + If the return value is less than NumberOfBytes, then the write operation failed. + If NumberOfBytes is zero, then return 0. + + @param Handle Debug port handle. + @param Buffer Pointer to the data buffer to be written. + @param NumberOfBytes Number of bytes to written to the debug device. + + @retval 0 NumberOfBytes is 0. + @retval >0 The number of bytes written to the debug device. + If this value is less than NumberOfBytes, then the read operation failed. + +**/ +UINTN +EFIAPI +DebugPortWriteBuffer ( + IN DEBUG_PORT_HANDLE Handle, + IN UINT8 *Buffer, + IN UINTN NumberOfBytes + ) +{ + return SerialPortWrite (Buffer, NumberOfBytes); +} + +/** + Polls a debug device to see if there is any data waiting to be read. + + Polls a debug device to see if there is any data waiting to be read. + If there is data waiting to be read from the debug device, then TRUE is returned. + If there is no data waiting to be read from the debug device, then FALSE is returned. + + @param Handle Debug port handle. + + @retval TRUE Data is waiting to be read from the debug device. + @retval FALSE There is no data waiting to be read from the serial device. + +**/ +BOOLEAN +EFIAPI +DebugPortPollBuffer ( + IN DEBUG_PORT_HANDLE Handle + ) +{ + return SerialPortPoll (); +} + diff --git a/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibSerialPort/DebugCommunicationLibSerialPort.inf b/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibSerialPort/DebugCommunicationLibSerialPort.inf new file mode 100644 index 0000000000..50b24c21d1 --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibSerialPort/DebugCommunicationLibSerialPort.inf @@ -0,0 +1,38 @@ +## @file +# Debug Communication Library instance based on serila port. +# +# Copyright (c) 2010 - 2014, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php. +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = DebugCommunicationLibSerialPort + MODULE_UNI_FILE = DebugCommunicationLibSerialPort.uni + FILE_GUID = 8CC435C5-6330-4269-B0C3-E3BD05C86FB8 + MODULE_TYPE = BASE + VERSION_STRING = 0.7 + LIBRARY_CLASS = DebugCommunicationLib + +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources.common] + DebugCommunicationLibSerialPort.c + +[Packages] + MdePkg/MdePkg.dec + SourceLevelDebugPkg/SourceLevelDebugPkg.dec + +[LibraryClasses] + SerialPortLib + DebugLib diff --git a/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibSerialPort/DebugCommunicationLibSerialPort.uni b/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibSerialPort/DebugCommunicationLibSerialPort.uni new file mode 100644 index 0000000000..7c7d8c4138 --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibSerialPort/DebugCommunicationLibSerialPort.uni @@ -0,0 +1,21 @@ +// /** @file +// Debug Communication Library instance based on serila port. +// +// Debug Communication Library instance based on serial port. +// +// Copyright (c) 2010 - 2014, Intel Corporation. All rights reserved.
+// +// This program and the accompanying materials +// are licensed and made available under the terms and conditions of the BSD License +// which accompanies this distribution. The full text of the license may be found at +// http://opensource.org/licenses/bsd-license.php. +// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Debug Communication Library instance based on serial port" + +#string STR_MODULE_DESCRIPTION #language en-US "Debug Communication Library instance based on serial port." + diff --git a/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb/DebugCommunicationLibUsb.c b/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb/DebugCommunicationLibUsb.c new file mode 100644 index 0000000000..d996f80f59 --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb/DebugCommunicationLibUsb.c @@ -0,0 +1,1098 @@ +/** @file + Debug Port Library implementation based on usb debug port. + + Copyright (c) 2010 - 2017, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SETUP_PID 0x2D +#define INPUT_PID 0x69 +#define OUTPUT_PID 0xE1 +#define ERROR_PID 0x55 +#define DATA0_PID 0xC3 +#define DATA1_PID 0x4B +#define DATA2_PID 0x87 +#define MDATA_PID 0x0F +#define ACK_PID 0xD2 +#define NAK_PID 0x5A +#define STALL_PID 0x1E +#define NYET_PID 0x96 + +#define PCI_CAPABILITY_ID_DEBUG_PORT 0x0A +#define USB_DEBUG_PORT_MAX_PACKET_SIZE 0x08 + +#define USB_DEBUG_PORT_IN_USE BIT10 +#define USB_DEBUG_PORT_ENABLE BIT28 +#define USB_DEBUG_PORT_OWNER BIT30 + +#define USB_PORT_LINE_STATUS_LS 0x400 +#define USB_PORT_LINE_STATUS_MASK 0xC00 + +// +// Usb debug device descriptor, which is defined at +// USB2 Debug Device Specification. +// +typedef struct _USB_DEBUG_PORT_DESCRIPTOR { + UINT8 Length; + UINT8 DescriptorType; + UINT8 DebugInEndpoint; + UINT8 DebugOutEndpoint; +}USB_DEBUG_PORT_DESCRIPTOR; + +USB_DEVICE_REQUEST mDebugCommunicationLibUsbGetDebugDescriptor = { + 0x80, + USB_REQ_GET_DESCRIPTOR, + (UINT16)(0x0A << 8), + 0x0000, + sizeof(USB_DEBUG_PORT_DESCRIPTOR) + }; + +USB_DEVICE_REQUEST mDebugCommunicationLibUsbSetDebugFeature = { + 0x0, + USB_REQ_SET_FEATURE, + (UINT16)(0x06), + 0x0000, + 0x0 + }; + +USB_DEVICE_REQUEST mDebugCommunicationLibUsbSetDebugAddress = { + 0x0, + USB_REQ_SET_ADDRESS, + (UINT16)(0x7F), + 0x0000, + 0x0 + }; + +// +// Usb debug port register file, which is defined at +// EHCI Specification. +// +typedef struct _USB_DEBUG_PORT_REGISTER { + UINT32 ControlStatus; + UINT8 TokenPid; + UINT8 SendPid; + UINT8 ReceivedPid; + UINT8 Reserved1; + UINT8 DataBuffer[8]; + UINT8 UsbEndPoint; + UINT8 UsbAddress; + UINT8 Reserved2; + UINT8 Reserved3; +}USB_DEBUG_PORT_REGISTER; + +// +// The state machine of usb debug port +// +#define USBDBG_NO_DEV 0 // No device present at debug port +#define USBDBG_NO_DBG_CAB 1 // The device attached is not usb debug cable +#define USBDBG_DBG_CAB 2 // The device attached is usb debug cable +#define USBDBG_INIT_DONE 4 // The usb debug cable device is initialized +#define USBDBG_RESET 8 // The system is reset + +#pragma pack(1) +// +// The internal data structure of DEBUG_PORT_HANDLE, which stores some +// important datum which are used across various phases. +// +typedef struct _USB_DEBUG_PORT_HANDLE{ + // + // The usb debug port memory BAR number in EHCI configuration space. + // + UINT8 DebugPortBarNumber; + UINT8 Initialized; + // + // The offset of usb debug port registers in EHCI memory range. + // + UINT16 DebugPortOffset; + // + // The usb debug port memory BAR address. + // + UINT32 UsbDebugPortMemoryBase; + // + // The EHCI memory BAR address. + // + UINT32 EhciMemoryBase; + // + // The Bulk In endpoint toggle bit. + // + UINT8 BulkInToggle; + // + // The Bulk Out endpoint toggle bit. + // + UINT8 BulkOutToggle; + // + // The available data length in the following data buffer. + // + UINT8 DataCount; + // + // The data buffer. Maximum length is 8 bytes. + // + UINT8 Data[8]; +} USB_DEBUG_PORT_HANDLE; +#pragma pack() + +// +// The global variable which can be used after memory is ready. +// +USB_DEBUG_PORT_HANDLE mDebugCommunicationLibUsbDebugPortHandle; + +/** + Calculate the usb debug port bar address. + + @param DebugPortOffset Get usb debug port offset in the usb debug port memory space. + @param DebugPortBarNumbar Get the bar number at which usb debug port is located. + + @retval RETURN_UNSUPPORTED The usb host controller does not supported usb debug port capability. + @retval RETURN_SUCCESS Get bar and offset successfully. + +**/ +RETURN_STATUS +EFIAPI +CalculateUsbDebugPortBar ( + OUT UINT16 *DebugPortOffset, + OUT UINT8 *DebugPortBarNumbar + ) +{ + UINT16 PciStatus; + UINT16 VendorId; + UINT16 DeviceId; + UINT8 ProgInterface; + UINT8 SubClassCode; + UINT8 BaseCode; + UINT8 CapabilityPtr; + UINT8 CapabilityId; + + VendorId = PciRead16 (PcdGet32(PcdUsbEhciPciAddress) + PCI_VENDOR_ID_OFFSET); + DeviceId = PciRead16 (PcdGet32(PcdUsbEhciPciAddress) + PCI_DEVICE_ID_OFFSET); + + if ((VendorId == 0xFFFF) || (DeviceId == 0xFFFF)) { + return RETURN_UNSUPPORTED; + } + + ProgInterface = PciRead8 (PcdGet32(PcdUsbEhciPciAddress) + PCI_CLASSCODE_OFFSET); + SubClassCode = PciRead8 (PcdGet32(PcdUsbEhciPciAddress) + PCI_CLASSCODE_OFFSET + 1); + BaseCode = PciRead8 (PcdGet32(PcdUsbEhciPciAddress) + PCI_CLASSCODE_OFFSET + 2); + + if ((ProgInterface != PCI_IF_EHCI) || (SubClassCode != PCI_CLASS_SERIAL_USB) || (BaseCode != PCI_CLASS_SERIAL)) { + return RETURN_UNSUPPORTED; + } + + // + // Enable Ehci Host Controller MMIO Space. + // + PciStatus = PciRead16 (PcdGet32(PcdUsbEhciPciAddress) + PCI_PRIMARY_STATUS_OFFSET); + + if ((PciStatus & EFI_PCI_STATUS_CAPABILITY) == 0) { + // + // The Pci Device Doesn't Support Capability Pointer. + // + return RETURN_UNSUPPORTED; + } + + // + // Get Pointer To Capability List + // + CapabilityPtr = PciRead8(PcdGet32(PcdUsbEhciPciAddress) + PCI_CAPBILITY_POINTER_OFFSET); + + // + // Find Capability ID 0xA, Which Is For Debug Port + // + while (CapabilityPtr != 0) { + CapabilityId = PciRead8(PcdGet32(PcdUsbEhciPciAddress) + CapabilityPtr); + if (CapabilityId == PCI_CAPABILITY_ID_DEBUG_PORT) { + break; + } + CapabilityPtr = PciRead8(PcdGet32(PcdUsbEhciPciAddress) + CapabilityPtr + 1); + } + + // + // No Debug Port Capability Found + // + if (CapabilityPtr == 0) { + return RETURN_UNSUPPORTED; + } + + // + // Get The Base Address Of Debug Port Register In Debug Port Capability Register + // + *DebugPortOffset = (UINT16)(PciRead16(PcdGet32(PcdUsbEhciPciAddress) + CapabilityPtr + 2) & 0x1FFF); + *DebugPortBarNumbar = (UINT8)((PciRead16(PcdGet32(PcdUsbEhciPciAddress) + CapabilityPtr + 2) >> 13) - 1); + + return RETURN_SUCCESS; +} + +/** + Do a usb IN transaction by usb debug port. + + @param DebugPortRegister Pointer to the base address of usb debug port register interface. + @param Buffer Pointer to the buffer receiving data. + @param Length Number of bytes of the received data. + @param Token The token PID for each USB transaction. + @param Addr The usb device address for usb transaction. + @param Ep The endpoint for usb transaction. + @param DataToggle The toggle bit used at usb transaction. + + @retval RETURN_SUCCESS The IN transaction is executed successfully. + @retval RETURN_INVALID_PARAMETER The parameters passed in are invalid. + @retval RETURN_DEVICE_ERROR The IN transaction comes across error. + +**/ +RETURN_STATUS +EFIAPI +UsbDebugPortIn ( + IN USB_DEBUG_PORT_REGISTER *DebugPortRegister, + IN OUT UINT8 *Buffer, + OUT UINT8 *Length, + IN UINT8 Token, + IN UINT8 Addr, + IN UINT8 Ep, + IN UINT8 DataToggle + ) +{ + UINTN Index; + + if (Length == NULL) { + return RETURN_INVALID_PARAMETER; + } + *Length = 0; + + DebugPortRegister->TokenPid = Token; + if (DataToggle != 0) { + DebugPortRegister->SendPid = DATA1_PID; + } else { + DebugPortRegister->SendPid = DATA0_PID; + } + + DebugPortRegister->UsbAddress = (UINT8)(Addr & 0x7F); + DebugPortRegister->UsbEndPoint = (UINT8)(Ep & 0xF); + + // + // Clearing W/R bit to indicate it's a READ operation + // + MmioAnd32((UINTN)&DebugPortRegister->ControlStatus, (UINT32)~BIT4); + + // + // Setting GO bit as well as clearing DONE bit + // + MmioOr32((UINTN)&DebugPortRegister->ControlStatus, (UINT32)BIT5); + + // + // Wait for completing the request + // + while ((MmioRead32((UINTN)&DebugPortRegister->ControlStatus) & (UINT32)BIT16) == 0) { + if ((MmioRead32((UINTN)&DebugPortRegister->ControlStatus) & (USB_DEBUG_PORT_OWNER | USB_DEBUG_PORT_IN_USE | USB_DEBUG_PORT_ENABLE)) + != (USB_DEBUG_PORT_OWNER | USB_DEBUG_PORT_IN_USE | USB_DEBUG_PORT_ENABLE)) { + return RETURN_DEVICE_ERROR; + } + } + + // + // Clearing DONE bit by writing 1 + // + MmioOr32((UINTN)&DebugPortRegister->ControlStatus, BIT16); + + // + // Check if the request is executed successfully or not. + // + if ((MmioRead32((UINTN)&DebugPortRegister->ControlStatus)) & BIT6) { + return RETURN_DEVICE_ERROR; + } + + // + // Make sure the received data are not beyond the allowable maximum length - 8 byte + // + if (((MmioRead32((UINTN)&DebugPortRegister->ControlStatus)) & 0xF) > USB_DEBUG_PORT_MAX_PACKET_SIZE) { + return RETURN_DEVICE_ERROR; + } + + *Length = (UINT8)(MmioRead32((UINTN)&DebugPortRegister->ControlStatus) & 0xF); + if (*Length > 8) { + return RETURN_DEVICE_ERROR; + } + + for (Index = 0; Index < *Length; Index++) { + Buffer[Index] = DebugPortRegister->DataBuffer[Index]; + } + return RETURN_SUCCESS; +} + +/** + Do a usb SETUP/OUT transaction by usb debug port. + + @param DebugPortRegister Pointer to the base address of usb debug port register interface. + @param Buffer Pointer to the buffer receiving data. + @param Length Number of bytes of the received data. + @param Token The token PID for each USB transaction. + @param Addr The usb device address for usb transaction. + @param Ep The endpoint for usb transaction. + @param DataToggle The toggle bit used at usb transaction. + + @retval RETURN_SUCCESS The IN transaction is executed successfully. + @retval RETURN_INVALID_PARAMETER The parameters passed in are invalid. + @retval RETURN_DEVICE_ERROR The IN transaction comes across error. + +**/ +RETURN_STATUS +EFIAPI +UsbDebugPortOut ( + IN USB_DEBUG_PORT_REGISTER *DebugPortRegister, + IN UINT8 *Buffer, + IN UINT8 Length, + IN UINT8 Token, + IN UINT8 Addr, + IN UINT8 Ep, + IN UINT8 DataToggle + ) +{ + UINT8 Index; + + if (Length > 8) { + return RETURN_INVALID_PARAMETER; + } + + DebugPortRegister->TokenPid = Token; + if (DataToggle != 0) { + DebugPortRegister->SendPid = DATA1_PID; + } else { + DebugPortRegister->SendPid = DATA0_PID; + } + DebugPortRegister->UsbAddress = (UINT8)(Addr & 0x7F); + DebugPortRegister->UsbEndPoint = (UINT8)(Ep & 0xF); + + // + // Fill in the data length and corresponding data. + // + MmioAnd32((UINTN)&DebugPortRegister->ControlStatus, (UINT32)~0xF); + MmioOr32((UINTN)&DebugPortRegister->ControlStatus, Length & 0xF); + for (Index = 0; Index < Length; Index++) { + DebugPortRegister->DataBuffer[Index] = Buffer[Index]; + } + + // + // Setting W/R bit to indicate it's a WRITE operation + // + MmioOr32((UINTN)&DebugPortRegister->ControlStatus, BIT4); + // + // Setting GO bit as well as clearing DONE bit + // + MmioOr32((UINTN)&DebugPortRegister->ControlStatus, BIT5); + + // + // Wait for completing the request + // + while ((MmioRead32((UINTN)&DebugPortRegister->ControlStatus) & BIT16) == 0) { + if ((MmioRead32((UINTN)&DebugPortRegister->ControlStatus) & (USB_DEBUG_PORT_OWNER | USB_DEBUG_PORT_IN_USE | USB_DEBUG_PORT_ENABLE)) + != (USB_DEBUG_PORT_OWNER | USB_DEBUG_PORT_IN_USE | USB_DEBUG_PORT_ENABLE)) { + return RETURN_DEVICE_ERROR; + } + } + + // + // Clearing DONE bit by writing 1 + // + MmioOr32((UINTN)&DebugPortRegister->ControlStatus, BIT16); + + // + // Check if the request is executed successfully or not. + // + if ((MmioRead32((UINTN)&DebugPortRegister->ControlStatus)) & BIT6) { + return RETURN_DEVICE_ERROR; + } + + // + // Make sure the sent data are not beyond the allowable maximum length - 8 byte + // + if (((MmioRead32((UINTN)&DebugPortRegister->ControlStatus)) & 0xF) > USB_DEBUG_PORT_MAX_PACKET_SIZE) { + return RETURN_DEVICE_ERROR; + } + + return RETURN_SUCCESS; +} + +/** + Do a usb control transfer by usb debug port. + + @param DebugPortRegister Pointer to the base address of usb debug port register interface. + @param SetupPacket The token PID for each USB transaction. + @param Addr The usb device address for usb transaction. + @param Ep The endpoint for usb transaction. + @param Data Pointer to the buffer receiving data. + @param DataLength Number of bytes of the received data. + + @retval RETURN_SUCCESS The IN transaction is executed successfully. + @retval RETURN_INVALID_PARAMETER The parameters passed in are invalid. + @retval RETURN_DEVICE_ERROR The IN transaction comes across error. + +**/ +RETURN_STATUS +EFIAPI +UsbDebugPortControlTransfer ( + IN USB_DEBUG_PORT_REGISTER *DebugPortRegister, + IN USB_DEVICE_REQUEST *SetupPacket, + IN UINT8 Addr, + IN UINT8 Ep, + OUT UINT8 *Data, + IN OUT UINT8 *DataLength + ) +{ + RETURN_STATUS Status; + UINT8 Temp; + UINT8 ReturnStatus[8]; + + // + // Setup Phase + // + Status = UsbDebugPortOut(DebugPortRegister, (UINT8 *)SetupPacket, (UINT8)sizeof(USB_DEVICE_REQUEST), SETUP_PID, Addr, Ep, 0); + if (RETURN_ERROR(Status)) { + return Status; + } + + // + // Data Phase + // + if (DataLength != 0) { + if ((SetupPacket->RequestType & BIT7) != 0) { + // + // Get Data From Device + // + Status = UsbDebugPortIn(DebugPortRegister, Data, DataLength, INPUT_PID, Addr, Ep, 1); + if (RETURN_ERROR(Status)) { + return Status; + } + } else { + // + // Send Data To Device + // + Status = UsbDebugPortOut(DebugPortRegister, Data, *DataLength, OUTPUT_PID, Addr, Ep, 1); + if (RETURN_ERROR(Status)) { + return Status; + } + } + } + + // + // Status Phase + // + if ((SetupPacket->RequestType & BIT7) != 0) { + // + // For READ operation, Data Toggle in Status Phase Should be 1. + // + Status = UsbDebugPortOut(DebugPortRegister, NULL, 0, OUTPUT_PID, Addr, Ep, 1); + } else { + // + // For WRITE operation, Data Toggle in Status Phase Should be 1. + // + Status = UsbDebugPortIn(DebugPortRegister, ReturnStatus, &Temp, INPUT_PID, Addr, Ep, 1); + } + + return Status; +} + +/** + Check if it needs to re-initialize usb debug port hardware. + + During different phases switch, such as SEC to PEI or PEI to DXE or DXE to SMM, we should check + whether the usb debug port hardware configuration is changed. Such case can be triggered by + Pci bus resource allocation and so on. + + @param Handle Debug port handle. + + @retval TRUE The usb debug port hardware configuration is changed. + @retval FALSE The usb debug port hardware configuration is not changed. + +**/ +BOOLEAN +EFIAPI +NeedReinitializeHardware( + IN USB_DEBUG_PORT_HANDLE *Handle + ) +{ + UINT16 PciCmd; + UINT32 UsbDebugPortMemoryBase; + UINT32 EhciMemoryBase; + BOOLEAN Status; + USB_DEBUG_PORT_REGISTER *UsbDebugPortRegister; + + Status = FALSE; + + EhciMemoryBase = 0xFFFFFC00 & PciRead32(PcdGet32(PcdUsbEhciPciAddress) + PCI_BASE_ADDRESSREG_OFFSET); + if (EhciMemoryBase != Handle->EhciMemoryBase) { + Handle->EhciMemoryBase = EhciMemoryBase; + Status = TRUE; + } + + UsbDebugPortMemoryBase = 0xFFFFFC00 & PciRead32(PcdGet32(PcdUsbEhciPciAddress) + PCI_BASE_ADDRESSREG_OFFSET + Handle->DebugPortBarNumber * 4); + if (UsbDebugPortMemoryBase != Handle->UsbDebugPortMemoryBase) { + Handle->UsbDebugPortMemoryBase = UsbDebugPortMemoryBase; + Status = TRUE; + } + + // + // Enable Ehci Memory Space Access + // + PciCmd = PciRead16 (PcdGet32(PcdUsbEhciPciAddress) + PCI_COMMAND_OFFSET); + if (((PciCmd & EFI_PCI_COMMAND_MEMORY_SPACE) == 0) || ((PciCmd & EFI_PCI_COMMAND_BUS_MASTER) == 0)) { + PciCmd |= EFI_PCI_COMMAND_MEMORY_SPACE | EFI_PCI_COMMAND_BUS_MASTER; + PciWrite16(PcdGet32(PcdUsbEhciPciAddress) + PCI_COMMAND_OFFSET, PciCmd); + Status = TRUE; + } + + // + // If the owner and in_use bit is not set, it means system is doing cold/warm boot or EHCI host controller is reset by system software. + // + UsbDebugPortRegister = (USB_DEBUG_PORT_REGISTER *)((UINTN)Handle->UsbDebugPortMemoryBase + Handle->DebugPortOffset); + if ((MmioRead32((UINTN)&UsbDebugPortRegister->ControlStatus) & (USB_DEBUG_PORT_OWNER | USB_DEBUG_PORT_ENABLE | USB_DEBUG_PORT_IN_USE)) + != (USB_DEBUG_PORT_OWNER | USB_DEBUG_PORT_ENABLE | USB_DEBUG_PORT_IN_USE)) { + Status = TRUE; + } + + if (Handle->Initialized == USBDBG_RESET) { + Status = TRUE; + } else if (Handle->Initialized != USBDBG_INIT_DONE) { + Status = TRUE; + } + return Status; +} + +/** + Initialize usb debug port hardware. + + 1. reset ehci host controller. + 2. set right port to debug port. + 3. find a usb debug device is attached by getting debug device descriptor. + 4. set address for the usb debug device. + 5. configure the usb debug device to debug mode. + + @param Handle Debug port handle. + + @retval TRUE The usb debug port hardware configuration is changed. + @retval FALSE The usb debug port hardware configuration is not changed. + +**/ +RETURN_STATUS +EFIAPI +InitializeUsbDebugHardware ( + IN USB_DEBUG_PORT_HANDLE *Handle +) +{ + RETURN_STATUS Status; + USB_DEBUG_PORT_REGISTER *UsbDebugPortRegister; + USB_DEBUG_PORT_DESCRIPTOR UsbDebugPortDescriptor; + UINT32 *PortStatus; + UINT32 *UsbCmd; + UINT32 *UsbStatus; + UINT32 *UsbHCSParam; + UINT8 DebugPortNumber; + UINT8 Length; + + UsbDebugPortRegister = (USB_DEBUG_PORT_REGISTER *)((UINTN)Handle->UsbDebugPortMemoryBase + Handle->DebugPortOffset); + UsbHCSParam = (UINT32 *)((UINTN)Handle->EhciMemoryBase + 0x04); + UsbCmd = (UINT32 *)((UINTN)Handle->EhciMemoryBase + 0x20); + UsbStatus = (UINT32 *)((UINTN)Handle->EhciMemoryBase + 0x24); + + // + // Check if the debug port is enabled and owned by myself. + // + if (((MmioRead32((UINTN)&UsbDebugPortRegister->ControlStatus) & (USB_DEBUG_PORT_OWNER | USB_DEBUG_PORT_IN_USE)) + != (USB_DEBUG_PORT_OWNER | USB_DEBUG_PORT_IN_USE)) || (Handle->Initialized == USBDBG_RESET)) { + DEBUG (( + EFI_D_INFO, + "UsbDbg: Need to reset the host controller. ControlStatus = %08x\n", + MmioRead32((UINTN)&UsbDebugPortRegister->ControlStatus) + )); + // + // If the host controller is halted, then reset and restart it. + // + if ((MmioRead32((UINTN)UsbStatus) & BIT12) != 0) { + DEBUG ((EFI_D_INFO, "UsbDbg: Reset the host controller.\n")); + // + // reset the host controller. + // + MmioOr32((UINTN)UsbCmd, BIT1); + // + // ensure that the host controller is reset. + // + while ((MmioRead32((UINTN)UsbCmd) & BIT1) != 0); + + MmioOr32((UINTN)UsbCmd, BIT0); + // ensure that the host controller is started (HALTED bit must be cleared) + while ((MmioRead32((UINTN)UsbStatus) & BIT12) != 0); + } + + // + // First get the ownership of port 0. + // + MmioOr32((UINTN)&UsbDebugPortRegister->ControlStatus, USB_DEBUG_PORT_OWNER | USB_DEBUG_PORT_IN_USE); + + MicroSecondDelay (200000); + } + // + // Find out which port is used as debug port. + // + DebugPortNumber = (UINT8)((MmioRead32((UINTN)UsbHCSParam) & 0x00F00000) >> 20); + // + // Should find a device is connected at debug port + // + PortStatus = (UINT32 *)((UINTN)Handle->EhciMemoryBase + 0x64 + (DebugPortNumber - 1) * 4); + if (!(MmioRead32((UINTN)PortStatus) & BIT0)) { + Handle->Initialized = USBDBG_NO_DEV; + return RETURN_NOT_FOUND; + } + + if (Handle->Initialized != USBDBG_INIT_DONE || + (MmioRead32 ((UINTN) &UsbDebugPortRegister->ControlStatus) & USB_DEBUG_PORT_ENABLE) == 0) { + DEBUG ((EFI_D_INFO, "UsbDbg: Reset the debug port.\n")); + // + // Reset the debug port + // + MmioOr32((UINTN)PortStatus, BIT8); + MicroSecondDelay (500000); + MmioAnd32((UINTN)PortStatus, (UINT32)~BIT8); + while (MmioRead32((UINTN)PortStatus) & BIT8); + + // + // The port enabled bit should be set by HW. + // + if ((MmioRead32((UINTN)PortStatus) & BIT2) == 0) { + Handle->Initialized = USBDBG_NO_DBG_CAB; + return RETURN_DEVICE_ERROR; + } + + // + // Enable Usb Debug Port Capability + // + MmioOr32((UINTN)&UsbDebugPortRegister->ControlStatus, USB_DEBUG_PORT_ENABLE); + + // + // initialize the data toggle used by bulk in/out endpoint. + // + Handle->BulkInToggle = 0; + Handle->BulkOutToggle = 0; + + // + // set usb debug device address as 0x7F. + // + Status = UsbDebugPortControlTransfer (UsbDebugPortRegister, &mDebugCommunicationLibUsbSetDebugAddress, 0x0, 0x0, NULL, NULL); + if (RETURN_ERROR(Status)) { + // + // The device can not work well. + // + Handle->Initialized = USBDBG_NO_DBG_CAB; + return Status; + } + + // + // Start to communicate with Usb Debug Device to see if the attached device is usb debug device or not. + // + Length = (UINT8)sizeof (USB_DEBUG_PORT_DESCRIPTOR); + + // + // Get debug descriptor. + // + Status = UsbDebugPortControlTransfer (UsbDebugPortRegister, &mDebugCommunicationLibUsbGetDebugDescriptor, 0x7F, 0x0, (UINT8*)&UsbDebugPortDescriptor, &Length); + if (RETURN_ERROR(Status)) { + // + // The device is not a usb debug device. + // + Handle->Initialized = USBDBG_NO_DBG_CAB; + return Status; + } + + if (Length != sizeof(USB_DEBUG_PORT_DESCRIPTOR)) { + Handle->Initialized = USBDBG_NO_DBG_CAB; + return RETURN_DEVICE_ERROR; + } + + // + // enable the usb debug feature. + // + Status = UsbDebugPortControlTransfer (UsbDebugPortRegister, &mDebugCommunicationLibUsbSetDebugFeature, 0x7F, 0x0, NULL, NULL); + if (RETURN_ERROR(Status)) { + // + // The device can not work well. + // + Handle->Initialized = USBDBG_NO_DBG_CAB; + return Status; + } + + Handle->Initialized = USBDBG_DBG_CAB; + } + + // + // Set initialized flag + // + Handle->Initialized = USBDBG_INIT_DONE; + + return RETURN_SUCCESS; +} + +/** + Read data from debug device and save the datas 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 +EFIAPI +DebugPortReadBuffer ( + IN DEBUG_PORT_HANDLE Handle, + IN UINT8 *Buffer, + IN UINTN NumberOfBytes, + IN UINTN Timeout + ) +{ + USB_DEBUG_PORT_HANDLE *UsbDebugPortHandle; + RETURN_STATUS Status; + UINT8 Index; + + if (NumberOfBytes != 1 || Buffer == NULL || Timeout != 0) { + return 0; + } + + // + // If Handle is NULL, it means memory is ready for use. + // Use global variable to store handle value. + // + if (Handle == NULL) { + UsbDebugPortHandle = &mDebugCommunicationLibUsbDebugPortHandle; + } else { + UsbDebugPortHandle = (USB_DEBUG_PORT_HANDLE *)Handle; + } + + if (NeedReinitializeHardware(UsbDebugPortHandle)) { + Status = InitializeUsbDebugHardware (UsbDebugPortHandle); + if (RETURN_ERROR(Status)) { + return 0; + } + } + + // + // Read data from buffer + // + if (UsbDebugPortHandle->DataCount < 1) { + return 0; + } else { + *Buffer = UsbDebugPortHandle->Data[0]; + for (Index = 0; Index < UsbDebugPortHandle->DataCount - 1; Index++) { + if ((Index + 1) >= USB_DEBUG_PORT_MAX_PACKET_SIZE) { + return 0; + } + UsbDebugPortHandle->Data[Index] = UsbDebugPortHandle->Data[Index + 1]; + } + UsbDebugPortHandle->DataCount = (UINT8)(UsbDebugPortHandle->DataCount - 1); + return 1; + } +} + +/** + Write data from buffer to debug device. + + Writes NumberOfBytes data bytes from Buffer to the debug device. + The number of bytes actually written to the debug device is returned. + If the return value is less than NumberOfBytes, then the write operation failed. + If NumberOfBytes is zero, then return 0. + + @param Handle Debug port handle. + @param Buffer Pointer to the data buffer to be written. + @param NumberOfBytes Number of bytes to written to the debug device. + + @retval 0 NumberOfBytes is 0. + @retval >0 The number of bytes written to the debug device. + If this value is less than NumberOfBytes, then the read operation failed. + +**/ +UINTN +EFIAPI +DebugPortWriteBuffer ( + IN DEBUG_PORT_HANDLE Handle, + IN UINT8 *Buffer, + IN UINTN NumberOfBytes + ) +{ + USB_DEBUG_PORT_HANDLE *UsbDebugPortHandle; + USB_DEBUG_PORT_REGISTER *UsbDebugPortRegister; + RETURN_STATUS Status; + UINT8 Sent; + UINTN Total; + UINT8 ReceivedPid; + + if (NumberOfBytes == 0 || Buffer == NULL) { + return 0; + } + + Sent = 0; + Total = 0; + + // + // If Handle is NULL, it means memory is ready for use. + // Use global variable to store handle value. + // + if (Handle == NULL) { + UsbDebugPortHandle = &mDebugCommunicationLibUsbDebugPortHandle; + } else { + UsbDebugPortHandle = (USB_DEBUG_PORT_HANDLE *)Handle; + } + + if (NeedReinitializeHardware(UsbDebugPortHandle)) { + Status = InitializeUsbDebugHardware (UsbDebugPortHandle); + if (RETURN_ERROR(Status)) { + return 0; + } + } + + UsbDebugPortRegister = (USB_DEBUG_PORT_REGISTER *)((UINTN)UsbDebugPortHandle->UsbDebugPortMemoryBase + UsbDebugPortHandle->DebugPortOffset); + + while ((Total < NumberOfBytes)) { + if (NumberOfBytes - Total > USB_DEBUG_PORT_MAX_PACKET_SIZE) { + Sent = USB_DEBUG_PORT_MAX_PACKET_SIZE; + } else { + Sent = (UINT8)(NumberOfBytes - Total); + } + + Status = UsbDebugPortOut(UsbDebugPortRegister, Buffer + Total, Sent, OUTPUT_PID, 0x7F, 0x01, UsbDebugPortHandle->BulkOutToggle); + + if (RETURN_ERROR(Status)) { + return Total; + } + + ReceivedPid = (MmioRead8((UINTN)&UsbDebugPortRegister->ReceivedPid)); + // + // If received a NAK_PID on write transaction, it means the usb debug device is busy and can not handle this transaction. + // should send the packet again. + // + if (ReceivedPid == NAK_PID) { + Sent = 0; + } else { + UsbDebugPortHandle->BulkOutToggle ^= 1; + } + Total += Sent; + } + return Total; +} + +/** + Polls a debug device to see if there is any data waiting to be read. + + Polls a debug device to see if there is any data waiting to be read. + If there is data waiting to be read from the debug device, then TRUE is returned. + If there is no data waiting to be read from the debug device, then FALSE is returned. + + @param Handle Debug port handle. + + @retval TRUE Data is waiting to be read from the debug device. + @retval FALSE There is no data waiting to be read from the serial device. + +**/ +BOOLEAN +EFIAPI +DebugPortPollBuffer ( + IN DEBUG_PORT_HANDLE Handle + ) +{ + USB_DEBUG_PORT_HANDLE *UsbDebugPortHandle; + USB_DEBUG_PORT_REGISTER *UsbDebugPortRegister; + UINT8 Length; + UINT8 Index; + RETURN_STATUS Status; + + // + // If Handle is NULL, it means memory is ready for use. + // Use global variable to store handle value. + // + if (Handle == NULL) { + UsbDebugPortHandle = &mDebugCommunicationLibUsbDebugPortHandle; + } else { + UsbDebugPortHandle = (USB_DEBUG_PORT_HANDLE *)Handle; + } + + if (NeedReinitializeHardware(UsbDebugPortHandle)) { + Status = InitializeUsbDebugHardware(UsbDebugPortHandle); + if (RETURN_ERROR(Status)) { + return FALSE; + } + } + + // + // If the data buffer is not empty, then return TRUE directly. + // else initialize a usb read transaction and read data to the data buffer. + // + if (UsbDebugPortHandle->DataCount != 0) { + return TRUE; + } + + UsbDebugPortRegister = (USB_DEBUG_PORT_REGISTER *)((UINTN)UsbDebugPortHandle->UsbDebugPortMemoryBase + UsbDebugPortHandle->DebugPortOffset); + + UsbDebugPortRegister->TokenPid = INPUT_PID; + if (UsbDebugPortHandle->BulkInToggle == 0) { + UsbDebugPortRegister->SendPid = DATA0_PID; + } else { + UsbDebugPortRegister->SendPid = DATA1_PID; + } + UsbDebugPortRegister->UsbAddress = 0x7F; + UsbDebugPortRegister->UsbEndPoint = 0x82 & 0x0F; + + // + // Clearing W/R bit to indicate it's a READ operation + // + MmioAnd32((UINTN)&UsbDebugPortRegister->ControlStatus, (UINT32)~BIT4); + // + // Setting GO bit as well as clearing DONE bit + // + MmioOr32((UINTN)&UsbDebugPortRegister->ControlStatus, (UINT32)BIT5); + + // + // Wait for completing the request + // + while ((MmioRead32((UINTN)&UsbDebugPortRegister->ControlStatus) & (UINT32)BIT16) == 0) { + if ((MmioRead32((UINTN)&UsbDebugPortRegister->ControlStatus) & (USB_DEBUG_PORT_OWNER | USB_DEBUG_PORT_IN_USE | USB_DEBUG_PORT_ENABLE)) + != (USB_DEBUG_PORT_OWNER | USB_DEBUG_PORT_IN_USE | USB_DEBUG_PORT_ENABLE)) { + return FALSE; + } + } + + if ((MmioRead32((UINTN)&UsbDebugPortRegister->ControlStatus)) & BIT6) { + return FALSE; + } + + Length = (UINT8)(MmioRead32((UINTN)&UsbDebugPortRegister->ControlStatus) & 0xF); + + if (Length > 8) { + return FALSE; + } + + UsbDebugPortHandle->BulkInToggle ^= 1; + + if (Length == 0) { + return FALSE; + } + + for (Index = 0; Index < Length; Index++) { + UsbDebugPortHandle->Data[Index] = UsbDebugPortRegister->DataBuffer[Index]; + } + UsbDebugPortHandle->DataCount = Length; + + return TRUE; +} + +/** + Initialize the debug port. + + If Function is not NULL, Debug Communication Library will call this function + by passing in the Context to be the first parameter. If needed, Debug Communication + Library will create one debug port handle to be the second argument passing in + calling the Function, otherwise it will pass NULL to be the second argument of + Function. + + If Function is NULL, and Context is not NULL, the Debug Communication Library could + a) Return the same handle as passed in (as Context parameter). + b) Ignore the input Context parameter and create new handle to be returned. + + If parameter Function is NULL and Context is NULL, Debug Communication Library could + created a new handle if needed and return it, otherwise it will return NULL. + + @param[in] Context Context needed by callback function; it was optional. + @param[in] Function Continue function called by Debug Communication library; + it was optional. + + @return The debug port handle created by Debug Communication Library if Function + is not NULL. + +**/ +DEBUG_PORT_HANDLE +EFIAPI +DebugPortInitialize ( + IN VOID *Context, + IN DEBUG_PORT_CONTINUE Function + ) +{ + RETURN_STATUS Status; + USB_DEBUG_PORT_HANDLE Handle; + + // + // Validate the PCD PcdDebugPortHandleBufferSize value + // + ASSERT (PcdGet16 (PcdDebugPortHandleBufferSize) == sizeof (USB_DEBUG_PORT_HANDLE)); + + if (Function == NULL && Context != NULL) { + return (DEBUG_PORT_HANDLE *) Context; + } + ZeroMem(&Handle, sizeof (USB_DEBUG_PORT_HANDLE)); + + Status = CalculateUsbDebugPortBar(&Handle.DebugPortOffset, &Handle.DebugPortBarNumber); + if (RETURN_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "UsbDbg: the pci device pointed by PcdUsbEhciPciAddress is not EHCI host controller or does not support debug port capability!\n")); + goto Exit; + } + + Handle.EhciMemoryBase = 0xFFFFFC00 & PciRead32(PcdGet32(PcdUsbEhciPciAddress) + PCI_BASE_ADDRESSREG_OFFSET); + + if (Handle.EhciMemoryBase == 0) { + // + // Usb Debug Port MMIO Space Is Not Enabled. Assumption here that DebugPortBase is zero + // + PciWrite32(PcdGet32(PcdUsbEhciPciAddress) + PCI_BASE_ADDRESSREG_OFFSET, PcdGet32(PcdUsbEhciMemorySpaceBase)); + Handle.EhciMemoryBase = 0xFFFFFC00 & PciRead32(PcdGet32(PcdUsbEhciPciAddress) + PCI_BASE_ADDRESSREG_OFFSET); + } + + Handle.UsbDebugPortMemoryBase = 0xFFFFFC00 & PciRead32(PcdGet32(PcdUsbEhciPciAddress) + PCI_BASE_ADDRESSREG_OFFSET + Handle.DebugPortBarNumber * 4); + + if (Handle.UsbDebugPortMemoryBase == 0) { + // + // Usb Debug Port MMIO Space Is Not Enabled. Assumption here that DebugPortBase is zero + // + PciWrite32(PcdGet32(PcdUsbEhciPciAddress) + PCI_BASE_ADDRESSREG_OFFSET + Handle.DebugPortBarNumber * 4, PcdGet32(PcdUsbDebugPortMemorySpaceBase)); + Handle.UsbDebugPortMemoryBase = 0xFFFFFC00 & PciRead32(PcdGet32(PcdUsbEhciPciAddress) + PCI_BASE_ADDRESSREG_OFFSET + Handle.DebugPortBarNumber * 4); + } + + Handle.Initialized = USBDBG_RESET; + + if (NeedReinitializeHardware(&Handle)) { + DEBUG ((EFI_D_ERROR, "UsbDbg: Start EHCI debug port initialization!\n")); + Status = InitializeUsbDebugHardware (&Handle); + if (RETURN_ERROR(Status)) { + DEBUG ((EFI_D_ERROR, "UsbDbg: Failed, please check if USB debug cable is plugged into EHCI debug port correctly!\n")); + goto Exit; + } + } + +Exit: + + if (Function != NULL) { + Function (Context, &Handle); + } else { + CopyMem(&mDebugCommunicationLibUsbDebugPortHandle, &Handle, sizeof (USB_DEBUG_PORT_HANDLE)); + } + + return (DEBUG_PORT_HANDLE)(UINTN)&mDebugCommunicationLibUsbDebugPortHandle; +} + diff --git a/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb/DebugCommunicationLibUsb.inf b/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb/DebugCommunicationLibUsb.inf new file mode 100644 index 0000000000..028b04afbf --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb/DebugCommunicationLibUsb.inf @@ -0,0 +1,56 @@ +## @file +# Debug Communication Library instance based on usb debug port. +# +# Copyright (c) 2010 - 2015, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php. +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = DebugCommunicationLibUsb + MODULE_UNI_FILE = DebugCommunicationLibUsb.uni + FILE_GUID = 87438836-AD8D-4e3e-9249-895120A67240 + MODULE_TYPE = BASE + VERSION_STRING = 0.7 + LIBRARY_CLASS = DebugCommunicationLib + +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources.common] + DebugCommunicationLibUsb.c + +[Packages] + MdePkg/MdePkg.dec + SourceLevelDebugPkg/SourceLevelDebugPkg.dec + +[Pcd] + # The memory BAR of usb debug port, it may be different with the memory bar of ehci host controller. + # Note that the memory BAR address is only used before Pci bus resource allocation. + gEfiSourceLevelDebugPkgTokenSpaceGuid.PcdUsbDebugPortMemorySpaceBase ## SOMETIMES_CONSUMES + # The memory BAR of ehci host controller, in which usb debug feature is enabled. + # Note that the memory BAR address is only used before Pci bus resource allocation. + gEfiSourceLevelDebugPkgTokenSpaceGuid.PcdUsbEhciMemorySpaceBase ## SOMETIMES_CONSUMES + # The pci address of ehci host controller, in which usb debug feature is enabled. + # The format of pci address please refer to SourceLevelDebugPkg.dec + gEfiSourceLevelDebugPkgTokenSpaceGuid.PcdUsbEhciPciAddress ## CONSUMES + # The value of data buffer size used for USB debug port handle. + # It should be equal to sizeof (USB_DEBUG_PORT_HANDLE). + gEfiSourceLevelDebugPkgTokenSpaceGuid.PcdDebugPortHandleBufferSize|23 ## SOMETIMES_CONSUMES + +[LibraryClasses] + TimerLib + IoLib + PciLib + PcdLib + DebugLib + diff --git a/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb/DebugCommunicationLibUsb.uni b/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb/DebugCommunicationLibUsb.uni new file mode 100644 index 0000000000..86983f2681 --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb/DebugCommunicationLibUsb.uni @@ -0,0 +1,21 @@ +// /** @file +// Debug Communication Library instance based on usb debug port. +// +// Debug Communication Library instance based on USB debug port. +// +// Copyright (c) 2010 - 2014, Intel Corporation. All rights reserved.
+// +// This program and the accompanying materials +// are licensed and made available under the terms and conditions of the BSD License +// which accompanies this distribution. The full text of the license may be found at +// http://opensource.org/licenses/bsd-license.php. +// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Debug Communication Library instance based on USB debug port" + +#string STR_MODULE_DESCRIPTION #language en-US "Debug Communication Library instance based on USB debug port." + diff --git a/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Common.c b/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Common.c new file mode 100644 index 0000000000..04e25e3ec2 --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Common.c @@ -0,0 +1,1066 @@ +/** @file + Debug Port Library implementation based on usb3 debug port. + + Copyright (c) 2014 - 2015, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "DebugCommunicationLibUsb3Internal.h" + +// +// The global variable which can be used after memory is ready. +// +USB3_DEBUG_PORT_HANDLE mDebugCommunicationLibUsb3DebugPortHandle; + +UINT16 mString0Desc[] = { + // String Descriptor Type + Length + ( USB_DESC_TYPE_STRING << 8 ) + STRING0_DESC_LEN, + 0x0409 +}; + +UINT16 mManufacturerStrDesc[] = { + // String Descriptor Type + Length + ( USB_DESC_TYPE_STRING << 8 ) + MANU_DESC_LEN, + 'I', 'n', 't', 'e', 'l' +}; + +UINT16 mProductStrDesc[] = { + // String Descriptor Type + Length + ( USB_DESC_TYPE_STRING << 8 ) + PRODUCT_DESC_LEN, + 'U', 'S', 'B', ' ', '3', '.', '0', ' ', 'D', 'e', 'b', 'u', 'g', ' ', 'C', 'a', 'b', 'l', 'e' +}; + +UINT16 mSerialNumberStrDesc[] = { + // String Descriptor Type + Length + ( USB_DESC_TYPE_STRING << 8 ) + SERIAL_DESC_LEN, + '1' +}; + +/** + Sets bits as per the enabled bit positions in the mask. + + @param[in, out] Register UINTN register + @param[in] BitMask 32-bit mask +**/ +VOID +XhcSetR32Bit( + IN OUT UINTN Register, + IN UINT32 BitMask + ) +{ + UINT32 RegisterValue; + + RegisterValue = MmioRead32 (Register); + RegisterValue |= (UINT32)(BitMask); + MmioWrite32 (Register, RegisterValue); +} + +/** + Clears bits as per the enabled bit positions in the mask. + + @param[in, out] Register UINTN register + @param[in] BitMask 32-bit mask +**/ +VOID +XhcClearR32Bit( + IN OUT UINTN Register, + IN UINT32 BitMask + ) +{ + UINT32 RegisterValue; + + RegisterValue = MmioRead32 (Register); + RegisterValue &= ~BitMask; + MmioWrite32 (Register, RegisterValue); +} + +/** + Write the data to the XHCI debug register. + + @param Handle Debug port handle. + @param Offset The offset of the runtime register. + @param Data The data to write. + +**/ +VOID +XhcWriteDebugReg ( + IN USB3_DEBUG_PORT_HANDLE *Handle, + IN UINT32 Offset, + IN UINT32 Data + ) +{ + EFI_PHYSICAL_ADDRESS DebugCapabilityBase; + + DebugCapabilityBase = Handle->DebugCapabilityBase; + MmioWrite32 ((UINTN)(DebugCapabilityBase + Offset), Data); + + return; +} + +/** + Read XHCI debug register. + + @param Handle Debug port handle. + @param Offset The offset of the runtime register. + + @return The register content read + +**/ +UINT32 +XhcReadDebugReg ( + IN USB3_DEBUG_PORT_HANDLE *Handle, + IN UINT32 Offset + ) +{ + UINT32 Data; + EFI_PHYSICAL_ADDRESS DebugCapabilityBase; + + DebugCapabilityBase = Handle->DebugCapabilityBase; + Data = MmioRead32 ((UINTN)(DebugCapabilityBase + Offset)); + + return Data; +} + +/** + Set one bit of the runtime register while keeping other bits. + + @param Handle Debug port handle. + @param Offset The offset of the runtime register. + @param Bit The bit mask of the register to set. + +**/ +VOID +XhcSetDebugRegBit ( + IN USB3_DEBUG_PORT_HANDLE *Handle, + IN UINT32 Offset, + IN UINT32 Bit + ) +{ + UINT32 Data; + + Data = XhcReadDebugReg (Handle, Offset); + Data |= Bit; + XhcWriteDebugReg (Handle, Offset, Data); +} + +/** + Program and eanble XHCI MMIO base address. + + @return XHCI MMIO base address. + +**/ +EFI_PHYSICAL_ADDRESS +ProgramXhciBaseAddress ( + VOID + ) +{ + UINT16 PciCmd; + UINT32 Low; + UINT32 High; + EFI_PHYSICAL_ADDRESS XhciMmioBase; + + Low = PciRead32 (PcdGet32(PcdUsbXhciPciAddress) + PCI_BASE_ADDRESSREG_OFFSET); + High = PciRead32 (PcdGet32(PcdUsbXhciPciAddress) + PCI_BASE_ADDRESSREG_OFFSET + 4); + XhciMmioBase = (EFI_PHYSICAL_ADDRESS) (LShiftU64 ((UINT64) High, 32) | Low); + XhciMmioBase &= XHCI_BASE_ADDRESS_64_BIT_MASK; + + if ((XhciMmioBase == 0) || (XhciMmioBase == XHCI_BASE_ADDRESS_64_BIT_MASK)) { + XhciMmioBase = PcdGet64(PcdUsbXhciMemorySpaceBase); + PciWrite32(PcdGet32(PcdUsbXhciPciAddress) + PCI_BASE_ADDRESSREG_OFFSET, XhciMmioBase & 0xFFFFFFFF); + PciWrite32(PcdGet32(PcdUsbXhciPciAddress) + PCI_BASE_ADDRESSREG_OFFSET + 4, (RShiftU64 (XhciMmioBase, 32) & 0xFFFFFFFF)); + } + + PciCmd = PciRead16 (PcdGet32(PcdUsbXhciPciAddress) + PCI_COMMAND_OFFSET); + if (((PciCmd & EFI_PCI_COMMAND_MEMORY_SPACE) == 0) || ((PciCmd & EFI_PCI_COMMAND_BUS_MASTER) == 0)) { + PciCmd |= EFI_PCI_COMMAND_MEMORY_SPACE | EFI_PCI_COMMAND_BUS_MASTER; + PciWrite16(PcdGet32(PcdUsbXhciPciAddress) + PCI_COMMAND_OFFSET, PciCmd); + } + + return XhciMmioBase; +} + +/** + Update XHC MMIO base address when MMIO base address is changed. + + @param Handle Debug port handle. + @param XhciMmioBase XHCI MMIO base address. + +**/ +VOID +UpdateXhcResource ( + IN OUT USB3_DEBUG_PORT_HANDLE *Handle, + IN EFI_PHYSICAL_ADDRESS XhciMmioBase + ) +{ + if ((Handle == NULL) || (Handle->XhciMmioBase == XhciMmioBase)) { + return; + } + + // + // Need fix Handle data according to new XHCI MMIO base address. + // + Handle->XhciMmioBase = XhciMmioBase; + Handle->DebugCapabilityBase = XhciMmioBase + Handle->DebugCapabilityOffset; + Handle->XhciOpRegister = XhciMmioBase + MmioRead8 ((UINTN)XhciMmioBase); +} + +/** + Calculate the usb debug port bar address. + + @param Handle Debug port handle. + + @retval RETURN_UNSUPPORTED The usb host controller does not supported usb debug port capability. + @retval RETURN_SUCCESS Get bar and offset successfully. + +**/ +RETURN_STATUS +EFIAPI +CalculateUsbDebugPortMmioBase ( + USB3_DEBUG_PORT_HANDLE *Handle + ) +{ + UINT16 VendorId; + UINT16 DeviceId; + UINT8 ProgInterface; + UINT8 SubClassCode; + UINT8 BaseCode; + BOOLEAN Flag; + UINT32 Capability; + EFI_PHYSICAL_ADDRESS CapabilityPointer; + UINT8 CapLength; + + VendorId = PciRead16 (PcdGet32(PcdUsbXhciPciAddress) + PCI_VENDOR_ID_OFFSET); + DeviceId = PciRead16 (PcdGet32(PcdUsbXhciPciAddress) + PCI_DEVICE_ID_OFFSET); + + if ((VendorId == 0xFFFF) || (DeviceId == 0xFFFF)) { + goto Done; + } + + ProgInterface = PciRead8 (PcdGet32(PcdUsbXhciPciAddress) + PCI_CLASSCODE_OFFSET); + SubClassCode = PciRead8 (PcdGet32(PcdUsbXhciPciAddress) + PCI_CLASSCODE_OFFSET + 1); + BaseCode = PciRead8 (PcdGet32(PcdUsbXhciPciAddress) + PCI_CLASSCODE_OFFSET + 2); + + if ((ProgInterface != PCI_IF_XHCI) || (SubClassCode != PCI_CLASS_SERIAL_USB) || (BaseCode != PCI_CLASS_SERIAL)) { + goto Done; + } + + CapLength = MmioRead8 ((UINTN) Handle->XhciMmioBase); + + // + // Get capability pointer from HCCPARAMS at offset 0x10 + // + CapabilityPointer = Handle->XhciMmioBase + (MmioRead32 ((UINTN)(Handle->XhciMmioBase + XHC_HCCPARAMS_OFFSET)) >> 16) * 4; + + // + // Search XHCI debug capability + // + Flag = FALSE; + Capability = MmioRead32 ((UINTN)CapabilityPointer); + while (TRUE) { + if ((Capability & XHC_CAPABILITY_ID_MASK) == PCI_CAPABILITY_ID_DEBUG_PORT) { + Flag = TRUE; + break; + } + if ((((Capability & XHC_NEXT_CAPABILITY_MASK) >> 8) & XHC_CAPABILITY_ID_MASK) == 0) { + // + // Reach the end of capability list, quit + // + break; + } + CapabilityPointer += ((Capability & XHC_NEXT_CAPABILITY_MASK) >> 8) * 4; + Capability = MmioRead32 ((UINTN)CapabilityPointer); + } + + if (!Flag) { + goto Done; + } + + // + // USB3 debug capability is supported. + // + Handle->DebugCapabilityBase = CapabilityPointer; + Handle->DebugCapabilityOffset = CapabilityPointer - Handle->XhciMmioBase; + Handle->XhciOpRegister = Handle->XhciMmioBase + CapLength; + Handle->Initialized = USB3DBG_DBG_CAB; + return RETURN_SUCCESS; + +Done: + Handle->Initialized = USB3DBG_NO_DBG_CAB; + return RETURN_UNSUPPORTED; +} + +/** + Check if it needs to re-initialize usb debug port hardware. + + During different phases switch, such as SEC to PEI or PEI to DXE or DXE to SMM, we should check + whether the usb debug port hardware configuration is changed. Such case can be triggered by + Pci bus resource allocation and so on. + + @param Handle Debug port handle. + + @retval TRUE The usb debug port hardware configuration is changed. + @retval FALSE The usb debug port hardware configuration is not changed. + +**/ +BOOLEAN +EFIAPI +NeedReinitializeHardware( + IN USB3_DEBUG_PORT_HANDLE *Handle + ) +{ + BOOLEAN Result; + volatile UINT32 Dcctrl; + + Result = FALSE; + + // + // If DCE bit, it means USB3 debug is not enabled. + // + Dcctrl = XhcReadDebugReg (Handle, XHC_DC_DCCTRL); + if ((Dcctrl & BIT0) == 0) { + Result = TRUE; + } + + return Result; +} + +/** + Create XHCI event ring. + + @param Handle Debug port handle. + @param EventRing The created event ring. + +**/ +EFI_STATUS +CreateEventRing ( + IN USB3_DEBUG_PORT_HANDLE *Handle, + OUT EVENT_RING *EventRing + ) +{ + VOID *Buf; + EVENT_RING_SEG_TABLE_ENTRY *ERSTBase; + + ASSERT (EventRing != NULL); + + // + // Allocate Event Ring + // + Buf = AllocateAlignBuffer (sizeof (TRB_TEMPLATE) * EVENT_RING_TRB_NUMBER); + ASSERT (Buf != NULL); + ASSERT (((UINTN) Buf & 0x3F) == 0); + ZeroMem (Buf, sizeof (TRB_TEMPLATE) * EVENT_RING_TRB_NUMBER); + + EventRing->EventRingSeg0 = (EFI_PHYSICAL_ADDRESS)(UINTN) Buf; + EventRing->TrbNumber = EVENT_RING_TRB_NUMBER; + EventRing->EventRingDequeue = (EFI_PHYSICAL_ADDRESS)(UINTN) EventRing->EventRingSeg0; + EventRing->EventRingEnqueue = (EFI_PHYSICAL_ADDRESS)(UINTN) EventRing->EventRingSeg0; + + // + // Software maintains an Event Ring Consumer Cycle State (CCS) bit, initializing it to '1' + // and toggling it every time the Event Ring Dequeue Pointer wraps back to the beginning of the Event Ring. + // + EventRing->EventRingCCS = 1; + + // + // Allocate Event Ring Segment Table Entry 0 in Event Ring Segment Table + // + Buf = AllocateAlignBuffer (sizeof (EVENT_RING_SEG_TABLE_ENTRY) * ERST_NUMBER); + ASSERT (Buf != NULL); + ASSERT (((UINTN) Buf & 0x3F) == 0); + ZeroMem (Buf, sizeof (EVENT_RING_SEG_TABLE_ENTRY) * ERST_NUMBER); + + ERSTBase = (EVENT_RING_SEG_TABLE_ENTRY *) Buf; + EventRing->ERSTBase = (EFI_PHYSICAL_ADDRESS)(UINTN) ERSTBase; + + // + // Fill Event Segment address + // + ERSTBase->PtrLo = XHC_LOW_32BIT (EventRing->EventRingSeg0); + ERSTBase->PtrHi = XHC_HIGH_32BIT (EventRing->EventRingSeg0); + ERSTBase->RingTrbSize = EVENT_RING_TRB_NUMBER; + + // + // Program the Interrupter Event Ring Dequeue Pointer (DCERDP) register (7.6.4.1) + // + XhcWriteDebugReg ( + Handle, + XHC_DC_DCERDP, + XHC_LOW_32BIT((UINT64)(UINTN)EventRing->EventRingDequeue) + ); + + XhcWriteDebugReg ( + Handle, + XHC_DC_DCERDP + 4, + XHC_HIGH_32BIT((UINT64)(UINTN)EventRing->EventRingDequeue) + ); + + // + // Program the Debug Capability Event Ring Segment Table Base Address (DCERSTBA) register(7.6.4.1) + // + XhcWriteDebugReg ( + Handle, + XHC_DC_DCERSTBA, + XHC_LOW_32BIT((UINT64)(UINTN)ERSTBase) + ); + + XhcWriteDebugReg ( + Handle, + XHC_DC_DCERSTBA + 4, + XHC_HIGH_32BIT((UINT64)(UINTN)ERSTBase) + ); + + // + // Program the Debug Capability Event Ring Segment Table Size (DCERSTSZ) register(7.6.4.1) + // + XhcWriteDebugReg ( + Handle, + XHC_DC_DCERSTSZ, + ERST_NUMBER + ); + return EFI_SUCCESS; +} + +/** + Create XHCI transfer ring. + + @param Handle Debug port handle. + @param TrbNum The number of TRB in the ring. + @param TransferRing The created transfer ring. + +**/ +VOID +CreateTransferRing ( + IN USB3_DEBUG_PORT_HANDLE *Handle, + IN UINT32 TrbNum, + OUT TRANSFER_RING *TransferRing + ) +{ + VOID *Buf; + LINK_TRB *EndTrb; + + Buf = AllocateAlignBuffer (sizeof (TRB_TEMPLATE) * TrbNum); + ASSERT (Buf != NULL); + ASSERT (((UINTN) Buf & 0xF) == 0); + ZeroMem (Buf, sizeof (TRB_TEMPLATE) * TrbNum); + + TransferRing->RingSeg0 = (EFI_PHYSICAL_ADDRESS)(UINTN) Buf; + TransferRing->TrbNumber = TrbNum; + TransferRing->RingEnqueue = TransferRing->RingSeg0; + TransferRing->RingDequeue = TransferRing->RingSeg0; + TransferRing->RingPCS = 1; + // + // 4.9.2 Transfer Ring Management + // To form a ring (or circular queue) a Link TRB may be inserted at the end of a ring to + // point to the first TRB in the ring. + // + EndTrb = (LINK_TRB *) ((UINTN)Buf + sizeof (TRB_TEMPLATE) * (TrbNum - 1)); + EndTrb->Type = TRB_TYPE_LINK; + EndTrb->PtrLo = XHC_LOW_32BIT (Buf); + EndTrb->PtrHi = XHC_HIGH_32BIT (Buf); + // + // Toggle Cycle (TC). When set to '1', the xHC shall toggle its interpretation of the Cycle bit. + // + EndTrb->TC = 1; + // + // Set Cycle bit as other TRB PCS init value + // + EndTrb->CycleBit = 0; +} + +/** + Create debug capability context for XHC debug device. + + @param Handle Debug port handle. + + @retval EFI_SUCCESS The bit successfully changed by host controller. + @retval EFI_TIMEOUT The time out occurred. + +**/ +EFI_STATUS +CreateDebugCapabilityContext ( + IN USB3_DEBUG_PORT_HANDLE *Handle + ) +{ + VOID *Buf; + XHC_DC_CONTEXT *DebugCapabilityContext; + UINT8 *String0Desc; + UINT8 *ManufacturerStrDesc; + UINT8 *ProductStrDesc; + UINT8 *SerialNumberStrDesc; + + // + // Allocate debug device context + // + Buf = AllocateAlignBuffer (sizeof (XHC_DC_CONTEXT)); + ASSERT (Buf != NULL); + ASSERT (((UINTN) Buf & 0xF) == 0); + ZeroMem (Buf, sizeof (XHC_DC_CONTEXT)); + + DebugCapabilityContext = (XHC_DC_CONTEXT *)(UINTN) Buf; + Handle->DebugCapabilityContext = (EFI_PHYSICAL_ADDRESS)(UINTN) DebugCapabilityContext; + + // + // Initialize DbcInfoContext. + // + DebugCapabilityContext->DbcInfoContext.String0Length = STRING0_DESC_LEN; + DebugCapabilityContext->DbcInfoContext.ManufacturerStrLength = MANU_DESC_LEN; + DebugCapabilityContext->DbcInfoContext.ProductStrLength = PRODUCT_DESC_LEN; + DebugCapabilityContext->DbcInfoContext.SerialNumberStrLength = SERIAL_DESC_LEN; + + // + // Initialize EpOutContext. + // + DebugCapabilityContext->EpOutContext.CErr = 0x3; + DebugCapabilityContext->EpOutContext.EPType = ED_BULK_OUT; + DebugCapabilityContext->EpOutContext.MaxPacketSize = XHCI_DEBUG_DEVICE_MAX_PACKET_SIZE; + DebugCapabilityContext->EpOutContext.AverageTRBLength = 0x1000; + + // + // Initialize EpInContext. + // + DebugCapabilityContext->EpInContext.CErr = 0x3; + DebugCapabilityContext->EpInContext.EPType = ED_BULK_IN; + DebugCapabilityContext->EpInContext.MaxPacketSize = XHCI_DEBUG_DEVICE_MAX_PACKET_SIZE; + DebugCapabilityContext->EpInContext.AverageTRBLength = 0x1000; + + // + // Update string descriptor address + // + String0Desc = (UINT8 *) AllocateAlignBuffer (STRING0_DESC_LEN + MANU_DESC_LEN + PRODUCT_DESC_LEN + SERIAL_DESC_LEN); + ASSERT (String0Desc != NULL); + ZeroMem (String0Desc, STRING0_DESC_LEN + MANU_DESC_LEN + PRODUCT_DESC_LEN + SERIAL_DESC_LEN); + CopyMem (String0Desc, mString0Desc, STRING0_DESC_LEN); + DebugCapabilityContext->DbcInfoContext.String0DescAddress = (UINT64)(UINTN)String0Desc; + + ManufacturerStrDesc = String0Desc + STRING0_DESC_LEN; + CopyMem (ManufacturerStrDesc, mManufacturerStrDesc, MANU_DESC_LEN); + DebugCapabilityContext->DbcInfoContext.ManufacturerStrDescAddress = (UINT64)(UINTN)ManufacturerStrDesc; + + ProductStrDesc = ManufacturerStrDesc + MANU_DESC_LEN; + CopyMem (ProductStrDesc, mProductStrDesc, PRODUCT_DESC_LEN); + DebugCapabilityContext->DbcInfoContext.ProductStrDescAddress = (UINT64)(UINTN)ProductStrDesc; + + SerialNumberStrDesc = ProductStrDesc + PRODUCT_DESC_LEN; + CopyMem (SerialNumberStrDesc, mSerialNumberStrDesc, SERIAL_DESC_LEN); + DebugCapabilityContext->DbcInfoContext.SerialNumberStrDescAddress = (UINT64)(UINTN)SerialNumberStrDesc; + + // + // Allocate and initialize the Transfer Ring for the Input Endpoint Context. + // + ZeroMem (&Handle->TransferRingIn, sizeof (TRANSFER_RING)); + CreateTransferRing (Handle, TR_RING_TRB_NUMBER, &Handle->TransferRingIn); + DebugCapabilityContext->EpInContext.PtrLo = XHC_LOW_32BIT (Handle->TransferRingIn.RingSeg0) | BIT0; + DebugCapabilityContext->EpInContext.PtrHi = XHC_HIGH_32BIT (Handle->TransferRingIn.RingSeg0); + + // + // Allocate and initialize the Transfer Ring for the Output Endpoint Context. + // + ZeroMem (&Handle->TransferRingOut, sizeof (TRANSFER_RING)); + CreateTransferRing (Handle, TR_RING_TRB_NUMBER, &Handle->TransferRingOut); + DebugCapabilityContext->EpOutContext.PtrLo = XHC_LOW_32BIT (Handle->TransferRingOut.RingSeg0) | BIT0; + DebugCapabilityContext->EpOutContext.PtrHi = XHC_HIGH_32BIT (Handle->TransferRingOut.RingSeg0); + + // + // Program the Debug Capability Context Pointer (DCCP) register(7.6.8.7) + // + XhcWriteDebugReg ( + Handle, + XHC_DC_DCCP, + XHC_LOW_32BIT((UINT64)(UINTN)DebugCapabilityContext) + ); + XhcWriteDebugReg ( + Handle, + XHC_DC_DCCP + 4, + XHC_HIGH_32BIT((UINT64)(UINTN)DebugCapabilityContext) + ); + return EFI_SUCCESS; +} + +/** + Check if debug device is running. + + @param Handle Debug port handle. + +**/ +VOID +XhcDetectDebugCapabilityReady ( + IN USB3_DEBUG_PORT_HANDLE *Handle + ) +{ + UINT64 TimeOut; + volatile UINT32 Dcctrl; + + TimeOut = 1; + if (Handle->Initialized == USB3DBG_DBG_CAB) { + // + // As detection is slow in seconds, wait for longer timeout for the first time. + // If first initialization is failed, we will try to enable debug device in the + // Poll function invoked by timer. + // + TimeOut = DivU64x32 (PcdGet64 (PcdUsbXhciDebugDetectTimeout), XHC_POLL_DELAY) + 1; + } + + do { + // + // Check if debug device is in configured state + // + Dcctrl = XhcReadDebugReg (Handle, XHC_DC_DCCTRL); + if ((Dcctrl & BIT0) != 0) { + // + // Set the flag to indicate debug device is in configured state + // + Handle->Ready = TRUE; + break; + } + MicroSecondDelay (XHC_POLL_DELAY); + TimeOut--; + } while (TimeOut != 0); +} + +/** + Initialize usb debug port hardware. + + @param Handle Debug port handle. + + @retval TRUE The usb debug port hardware configuration is changed. + @retval FALSE The usb debug port hardware configuration is not changed. + +**/ +RETURN_STATUS +EFIAPI +InitializeUsbDebugHardware ( + IN USB3_DEBUG_PORT_HANDLE *Handle + ) +{ + RETURN_STATUS Status; + UINT8 *Buffer; + UINTN Index; + UINT8 TotalUsb3Port; + EFI_PHYSICAL_ADDRESS XhciOpRegister; + + XhciOpRegister = Handle->XhciOpRegister; + TotalUsb3Port = MmioRead32 (((UINTN) Handle->XhciMmioBase + XHC_HCSPARAMS1_OFFSET)) >> 24; + + if (Handle->Initialized == USB3DBG_NOT_ENABLED) { + // + // If XHCI supports debug capability, hardware resource has been allocated, + // but it has not been enabled, try to enable again. + // + goto Enable; + } + + // + // Initialize for PEI phase when AllocatePages can work. + // Allocate data buffer with max packet size for data read and data poll. + // Allocate data buffer for data write. + // + Buffer = AllocateAlignBuffer (XHCI_DEBUG_DEVICE_MAX_PACKET_SIZE * 2 + USB3_DEBUG_PORT_WRITE_MAX_PACKET_SIZE); + if (Buffer == NULL) { + // + // AllocatePages can not still work now, return fail and do not initialize now. + // + return RETURN_NOT_READY; + } + + // + // Reset port to get debug device discovered + // + for (Index = 0; Index < TotalUsb3Port; Index++) { + XhcSetR32Bit ((UINTN)XhciOpRegister + XHC_PORTSC_OFFSET + Index * 0x10, BIT4); + MicroSecondDelay (10 * 1000); + } + + // + // Construct the buffer for read, poll and write. + // + Handle->UrbIn.Data = (EFI_PHYSICAL_ADDRESS)(UINTN) Buffer; + Handle->Data = (EFI_PHYSICAL_ADDRESS)(UINTN) Buffer + XHCI_DEBUG_DEVICE_MAX_PACKET_SIZE; + Handle->UrbOut.Data = Handle->UrbIn.Data + XHCI_DEBUG_DEVICE_MAX_PACKET_SIZE * 2; + + // + // Initialize event ring + // + ZeroMem (&Handle->EventRing, sizeof (EVENT_RING)); + Status = CreateEventRing (Handle, &Handle->EventRing); + ASSERT_EFI_ERROR (Status); + + // + // Init IN and OUT endpoint context + // + Status = CreateDebugCapabilityContext (Handle); + ASSERT_EFI_ERROR (Status); + + // + // Init DCDDI1 and DCDDI2 + // + XhcWriteDebugReg ( + Handle, + XHC_DC_DCDDI1, + (UINT32)((XHCI_DEBUG_DEVICE_VENDOR_ID << 16) | XHCI_DEBUG_DEVICE_PROTOCOL) + ); + + XhcWriteDebugReg ( + Handle, + XHC_DC_DCDDI2, + (UINT32)((XHCI_DEBUG_DEVICE_REVISION << 16) | XHCI_DEBUG_DEVICE_PRODUCT_ID) + ); + +Enable: + if ((Handle->Initialized == USB3DBG_NOT_ENABLED) && (!Handle->ChangePortPower)) { + // + // If the first time detection is failed, turn port power off and on in order to + // reset port status this time, then try to check if debug device is ready again. + // + for (Index = 0; Index < TotalUsb3Port; Index++) { + XhcClearR32Bit ((UINTN)XhciOpRegister + XHC_PORTSC_OFFSET + Index * 0x10, BIT9); + MicroSecondDelay (XHC_DEBUG_PORT_ON_OFF_DELAY); + XhcSetR32Bit ((UINTN)XhciOpRegister + XHC_PORTSC_OFFSET + Index * 0x10, BIT9); + MicroSecondDelay (XHC_DEBUG_PORT_ON_OFF_DELAY); + Handle->ChangePortPower = TRUE; + } + } + + // + // Set DCE bit and LSE bit to "1" in DCCTRL in first initialization + // + XhcSetDebugRegBit (Handle, XHC_DC_DCCTRL, BIT1|BIT31); + + XhcDetectDebugCapabilityReady (Handle); + + Status = RETURN_SUCCESS; + if (!Handle->Ready) { + Handle->Initialized = USB3DBG_NOT_ENABLED; + Status = RETURN_NOT_READY; + } else { + Handle->Initialized = USB3DBG_ENABLED; + } + + return Status; +} + +/** + 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 +EFIAPI +DebugPortReadBuffer ( + IN DEBUG_PORT_HANDLE Handle, + IN UINT8 *Buffer, + IN UINTN NumberOfBytes, + IN UINTN Timeout + ) +{ + USB3_DEBUG_PORT_HANDLE *UsbDebugPortHandle; + RETURN_STATUS Status; + UINT8 Index; + UINT8 *Data; + + if (NumberOfBytes != 1 || Buffer == NULL || Timeout != 0) { + return 0; + } + + // + // If Handle is NULL, it means memory is ready for use. + // Use global variable to store handle value. + // + if (Handle == NULL) { + UsbDebugPortHandle = &mDebugCommunicationLibUsb3DebugPortHandle; + } else { + UsbDebugPortHandle = (USB3_DEBUG_PORT_HANDLE *)Handle; + } + + if (UsbDebugPortHandle->Initialized == USB3DBG_NO_DBG_CAB) { + return 0; + } + + if (NeedReinitializeHardware(UsbDebugPortHandle)) { + Status = InitializeUsbDebugHardware (UsbDebugPortHandle); + if (RETURN_ERROR(Status)) { + return 0; + } + } + + Data = (UINT8 *)(UINTN)UsbDebugPortHandle->Data; + + // + // Read data from buffer + // + if (UsbDebugPortHandle->DataCount < 1) { + return 0; + } else { + *Buffer = Data[0]; + + for (Index = 0; Index < UsbDebugPortHandle->DataCount - 1; Index++) { + if ((Index + 1) >= XHCI_DEBUG_DEVICE_MAX_PACKET_SIZE) { + return 0; + } + Data[Index] = Data[Index + 1]; + } + UsbDebugPortHandle->DataCount = (UINT8)(UsbDebugPortHandle->DataCount - 1); + return 1; + } +} + +/** + Write data from buffer to debug device. + + Writes NumberOfBytes data bytes from Buffer to the debug device. + The number of bytes actually written to the debug device is returned. + If the return value is less than NumberOfBytes, then the write operation failed. + If NumberOfBytes is zero, then return 0. + + @param Handle Debug port handle. + @param Buffer Pointer to the data buffer to be written. + @param NumberOfBytes Number of bytes to written to the debug device. + + @retval 0 NumberOfBytes is 0. + @retval >0 The number of bytes written to the debug device. + If this value is less than NumberOfBytes, then the read operation failed. + +**/ +UINTN +EFIAPI +DebugPortWriteBuffer ( + IN DEBUG_PORT_HANDLE Handle, + IN UINT8 *Buffer, + IN UINTN NumberOfBytes + ) +{ + USB3_DEBUG_PORT_HANDLE *UsbDebugPortHandle; + RETURN_STATUS Status; + UINTN Sent; + UINTN Total; + EFI_PHYSICAL_ADDRESS XhciMmioBase; + UINTN Index; + + if (NumberOfBytes == 0 || Buffer == NULL) { + return 0; + } + + Sent = 0; + Total = 0; + + // + // If Handle is NULL, it means memory is ready for use. + // Use global variable to store handle value. + // + if (Handle == NULL) { + UsbDebugPortHandle = &mDebugCommunicationLibUsb3DebugPortHandle; + } else { + UsbDebugPortHandle = (USB3_DEBUG_PORT_HANDLE *)Handle; + } + + if (UsbDebugPortHandle->Initialized == USB3DBG_NO_DBG_CAB) { + return 0; + } + + // + // MMIO base address is possible to clear, set it if it is cleared. (XhciMemorySpaceClose in PchUsbCommon.c) + // + XhciMmioBase = ProgramXhciBaseAddress (); + + UpdateXhcResource (UsbDebugPortHandle, XhciMmioBase); + + if (NeedReinitializeHardware(UsbDebugPortHandle)) { + Status = InitializeUsbDebugHardware (UsbDebugPortHandle); + if (RETURN_ERROR(Status)) { + return 0; + } + } + + // + // When host is trying to send data, write will be blocked. + // Poll to see if there is any data sent by host at first. + // + DebugPortPollBuffer (Handle); + + Index = 0; + while ((Total < NumberOfBytes)) { + if (NumberOfBytes - Total > USB3_DEBUG_PORT_WRITE_MAX_PACKET_SIZE) { + Sent = USB3_DEBUG_PORT_WRITE_MAX_PACKET_SIZE; + } else { + Sent = (UINT8)(NumberOfBytes - Total); + } + Status = XhcDataTransfer (UsbDebugPortHandle, EfiUsbDataOut, Buffer + Total, &Sent, DATA_TRANSFER_WRITE_TIMEOUT); + Total += Sent; + } + + return Total; +} + +/** + Polls a debug device to see if there is any data waiting to be read. + + Polls a debug device to see if there is any data waiting to be read. + If there is data waiting to be read from the debug device, then TRUE is returned. + If there is no data waiting to be read from the debug device, then FALSE is returned. + + @param Handle Debug port handle. + + @retval TRUE Data is waiting to be read from the debug device. + @retval FALSE There is no data waiting to be read from the serial device. + +**/ +BOOLEAN +EFIAPI +DebugPortPollBuffer ( + IN DEBUG_PORT_HANDLE Handle + ) +{ + USB3_DEBUG_PORT_HANDLE *UsbDebugPortHandle; + UINTN Length; + RETURN_STATUS Status; + EFI_PHYSICAL_ADDRESS XhciMmioBase; + + // + // If Handle is NULL, it means memory is ready for use. + // Use global variable to store handle value. + // + if (Handle == NULL) { + UsbDebugPortHandle = &mDebugCommunicationLibUsb3DebugPortHandle; + } else { + UsbDebugPortHandle = (USB3_DEBUG_PORT_HANDLE *)Handle; + } + + if (UsbDebugPortHandle->Initialized == USB3DBG_NO_DBG_CAB) { + return 0; + } + + XhciMmioBase = ProgramXhciBaseAddress (); + UpdateXhcResource (UsbDebugPortHandle, XhciMmioBase); + + if (NeedReinitializeHardware(UsbDebugPortHandle)) { + Status = InitializeUsbDebugHardware(UsbDebugPortHandle); + if (RETURN_ERROR(Status)) { + return FALSE; + } + } + + // + // If the data buffer is not empty, then return TRUE directly. + // Otherwise initialize a usb read transaction and read data to internal data buffer. + // + if (UsbDebugPortHandle->DataCount != 0) { + return TRUE; + } + + // + // Read data as much as we can + // + Length = XHCI_DEBUG_DEVICE_MAX_PACKET_SIZE; + XhcDataTransfer (Handle, EfiUsbDataIn, (VOID *)(UINTN)UsbDebugPortHandle->Data, &Length, DATA_TRANSFER_POLL_TIMEOUT); + + if (Length > XHCI_DEBUG_DEVICE_MAX_PACKET_SIZE) { + return FALSE; + } + + if (Length == 0) { + return FALSE; + } + + // + // Store data into internal buffer for use later + // + UsbDebugPortHandle->DataCount = (UINT8) Length; + return TRUE; +} + +/** + Initialize the debug port. + + If Function is not NULL, Debug Communication Library will call this function + by passing in the Context to be the first parameter. If needed, Debug Communication + Library will create one debug port handle to be the second argument passing in + calling the Function, otherwise it will pass NULL to be the second argument of + Function. + + If Function is NULL, and Context is not NULL, the Debug Communication Library could + a) Return the same handle as passed in (as Context parameter). + b) Ignore the input Context parameter and create new handle to be returned. + + If parameter Function is NULL and Context is NULL, Debug Communication Library could + created a new handle if needed and return it, otherwise it will return NULL. + + @param[in] Context Context needed by callback function; it was optional. + @param[in] Function Continue function called by Debug Communication library; + it was optional. + + @return The debug port handle created by Debug Communication Library if Function + is not NULL. + +**/ +DEBUG_PORT_HANDLE +EFIAPI +DebugPortInitialize ( + IN VOID *Context, + IN DEBUG_PORT_CONTINUE Function + ) +{ + RETURN_STATUS Status; + USB3_DEBUG_PORT_HANDLE Handle; + USB3_DEBUG_PORT_HANDLE *UsbDebugPortHandle; + + // + // Validate the PCD PcdDebugPortHandleBufferSize value + // + ASSERT (PcdGet16 (PcdDebugPortHandleBufferSize) == sizeof (USB3_DEBUG_PORT_HANDLE)); + + if (Function == NULL && Context != NULL) { + UsbDebugPortHandle = (USB3_DEBUG_PORT_HANDLE *)Context; + } else { + ZeroMem(&Handle, sizeof (USB3_DEBUG_PORT_HANDLE)); + UsbDebugPortHandle = &Handle; + } + + if (Function == NULL && Context != NULL) { + return (DEBUG_PORT_HANDLE *) Context; + } + + // + // Read 64-bit MMIO base address + // + UsbDebugPortHandle->XhciMmioBase = ProgramXhciBaseAddress (); + + Status = CalculateUsbDebugPortMmioBase (UsbDebugPortHandle); + if (RETURN_ERROR (Status)) { + goto Exit; + } + + if (NeedReinitializeHardware(&Handle)) { + Status = InitializeUsbDebugHardware (&Handle); + if (RETURN_ERROR(Status)) { + goto Exit; + } + } + +Exit: + + if (Function != NULL) { + Function (Context, &Handle); + } else { + CopyMem(&mDebugCommunicationLibUsb3DebugPortHandle, &Handle, sizeof (USB3_DEBUG_PORT_HANDLE)); + } + + return (DEBUG_PORT_HANDLE)(UINTN)&mDebugCommunicationLibUsb3DebugPortHandle; +} diff --git a/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Dxe.c b/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Dxe.c new file mode 100644 index 0000000000..c4a8a47682 --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Dxe.c @@ -0,0 +1,53 @@ +/** @file + Debug Port Library implementation based on usb3 debug port. + + Copyright (c) 2014 - 2016, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include +#include +#include +#include "DebugCommunicationLibUsb3Internal.h" + +/** + Allocate aligned memory for XHC's usage. + + @param BufferSize The size, in bytes, of the Buffer. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID* +AllocateAlignBuffer ( + IN UINTN BufferSize + ) +{ + EFI_PHYSICAL_ADDRESS TmpAddr; + EFI_STATUS Status; + VOID *Buf; + + Buf = NULL; + + if (gBS != NULL) { + TmpAddr = 0xFFFFFFFF; + Status = gBS->AllocatePages ( + AllocateMaxAddress, + EfiACPIMemoryNVS, + EFI_SIZE_TO_PAGES (BufferSize), + &TmpAddr + ); + if (!EFI_ERROR (Status)) { + Buf = (VOID *) (UINTN) TmpAddr; + } + } + + return Buf; +} diff --git a/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Dxe.inf b/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Dxe.inf new file mode 100644 index 0000000000..c029219aef --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Dxe.inf @@ -0,0 +1,69 @@ +## @file +# Debug Communication Library instance based on usb3 debug port. +# +# Copyright (c) 2014 - 2015, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php. +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = DebugCommunicationLibUsb3Dxe + MODULE_UNI_FILE = DebugCommunicationLibUsb3Dxe.uni + FILE_GUID = C41F8C82-B3E6-47e0-A61D-0F9E429E6996 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + LIBRARY_CLASS = DebugCommunicationLib|DXE_CORE DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SAL_DRIVER DXE_SMM_DRIVER UEFI_APPLICATION UEFI_DRIVER SMM_CORE + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF +# + +[Sources] + DebugCommunicationLibUsb3Dxe.c + DebugCommunicationLibUsb3Transfer.c + DebugCommunicationLibUsb3Common.c + DebugCommunicationLibUsb3Internal.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + IntelFrameworkPkg/IntelFrameworkPkg.dec + SourceLevelDebugPkg/SourceLevelDebugPkg.dec + +[Pcd] + # The memory BAR of ehci host controller, in which usb debug feature is enabled. + # Note that the memory BAR address is only used before Pci bus resource allocation. + gEfiSourceLevelDebugPkgTokenSpaceGuid.PcdUsbXhciMemorySpaceBase ## SOMETIMES_CONSUMES + + # The pci address of ehci host controller, in which usb debug feature is enabled. + # The format of pci address please refer to SourceLevelDebugPkg.dec + gEfiSourceLevelDebugPkgTokenSpaceGuid.PcdUsbXhciPciAddress ## CONSUMES + + # Per XHCI spec, software shall impose a timeout between the detection of the Debug Host + # connection and the DbC Run transition to 1. This PCD specifies the timeout value in microsecond. + gEfiSourceLevelDebugPkgTokenSpaceGuid.PcdUsbXhciDebugDetectTimeout ## SOMETIMES_CONSUMES + + # The value of data buffer size used for USB debug port handle. + # It should be equal to sizeof (USB3_DEBUG_PORT_HANDLE). + gEfiSourceLevelDebugPkgTokenSpaceGuid.PcdDebugPortHandleBufferSize|239 ## SOMETIMES_CONSUMES + +[LibraryClasses] + BaseLib + PcdLib + IoLib + PciLib + TimerLib + UefiBootServicesTableLib + UefiLib + BaseMemoryLib + \ No newline at end of file diff --git a/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Dxe.uni b/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Dxe.uni new file mode 100644 index 0000000000..9a19c412fb --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Dxe.uni @@ -0,0 +1,21 @@ +// /** @file +// Debug Communication Library instance based on USB3 debug port for DXE and SMM modules. +// +// Debug Communication Library instance based on USB3 debug port for DXE and SMM modules. +// +// Copyright (c) 2014, Intel Corporation. All rights reserved.
+// +// This program and the accompanying materials +// are licensed and made available under the terms and conditions of the BSD License +// which accompanies this distribution. The full text of the license may be found at +// http://opensource.org/licenses/bsd-license.php. +// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Debug Communication Library instance based on USB3 debug port for DXE and SMM modules" + +#string STR_MODULE_DESCRIPTION #language en-US "Debug Communication Library instance based on USB3 debug port for DXE and SMM modules." + diff --git a/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Internal.h b/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Internal.h new file mode 100644 index 0000000000..356485c5f6 --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Internal.h @@ -0,0 +1,731 @@ +/** @file + Debug Port Library implementation based on usb3 debug port. + + Copyright (c) 2014 - 2015, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __USB3_DEBUG_PORT_LIB_INTERNAL__ +#define __USB3_DEBUG_PORT_LIB_INTERNAL__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// +// USB Debug GUID value +// +#define USB3_DBG_GUID \ + { \ + 0xb2a56f4d, 0x9177, 0x4fc8, { 0xa6, 0x77, 0xdd, 0x96, 0x3e, 0xb4, 0xcb, 0x1b } \ + } + +// +// The state machine of usb debug port +// +#define USB3DBG_NO_DBG_CAB 0 // The XHCI host controller does not support debug capability +#define USB3DBG_DBG_CAB 1 // The XHCI host controller supports debug capability +#define USB3DBG_ENABLED 2 // The XHCI debug device is enabled +#define USB3DBG_NOT_ENABLED 4 // The XHCI debug device is not enabled + +#define USB3_DEBUG_PORT_WRITE_MAX_PACKET_SIZE 0x08 + +// +// MaxPacketSize for DbC Endpoint Descriptor IN and OUT +// +#define XHCI_DEBUG_DEVICE_MAX_PACKET_SIZE 0x400 + +#define XHCI_DEBUG_DEVICE_VENDOR_ID 0x0525 +#define XHCI_DEBUG_DEVICE_PRODUCT_ID 0x127A +#define XHCI_DEBUG_DEVICE_PROTOCOL 0xFF +#define XHCI_DEBUG_DEVICE_REVISION 0x00 + +#define XHCI_BASE_ADDRESS_64_BIT_MASK 0xFFFFFFFFFFFF0000ULL +#define XHCI_BASE_ADDRESS_32_BIT_MASK 0xFFFF0000 + +#define PCI_CAPABILITY_ID_DEBUG_PORT 0x0A +#define XHC_HCCPARAMS_OFFSET 0x10 +#define XHC_CAPABILITY_ID_MASK 0xFF +#define XHC_NEXT_CAPABILITY_MASK 0xFF00 + +#define XHC_HCSPARAMS1_OFFSET 0x4 // Structural Parameters 1 +#define XHC_USBCMD_OFFSET 0x0 // USB Command Register Offset +#define XHC_USBSTS_OFFSET 0x4 // USB Status Register Offset +#define XHC_PORTSC_OFFSET 0x400 // Port Status and Control Register Offset + +#define XHC_USBCMD_RUN BIT0 // Run/Stop +#define XHC_USBCMD_RESET BIT1 // Host Controller Reset + +#define XHC_USBSTS_HALT BIT0 + +// +// Indicate the timeout when data is transferred in microsecond. 0 means infinite timeout. +// +#define DATA_TRANSFER_WRITE_TIMEOUT 0 +#define DATA_TRANSFER_READ_TIMEOUT 50000 +#define DATA_TRANSFER_POLL_TIMEOUT 1000 +#define XHC_DEBUG_PORT_1_MILLISECOND 1000 +// +// XHCI port power off/on delay +// +#define XHC_DEBUG_PORT_ON_OFF_DELAY 100000 + +// +// USB debug device string descritpor (header size + unicode string length) +// +#define STRING0_DESC_LEN 4 +#define MANU_DESC_LEN 12 +#define PRODUCT_DESC_LEN 40 +#define SERIAL_DESC_LEN 4 + +// +// Debug Capability Register Offset +// +#define XHC_DC_DCID 0x0 +#define XHC_DC_DCDB 0x4 +#define XHC_DC_DCERSTSZ 0x8 +#define XHC_DC_DCERSTBA 0x10 +#define XHC_DC_DCERDP 0x18 +#define XHC_DC_DCCTRL 0x20 +#define XHC_DC_DCST 0x24 +#define XHC_DC_DCPORTSC 0x28 +#define XHC_DC_DCCP 0x30 +#define XHC_DC_DCDDI1 0x38 +#define XHC_DC_DCDDI2 0x3C + +#define TRB_TYPE_LINK 6 + +#define ERST_NUMBER 0x01 +#define TR_RING_TRB_NUMBER 0x100 +#define EVENT_RING_TRB_NUMBER 0x200 + +#define ED_BULK_OUT 2 +#define ED_BULK_IN 6 + +#define XHC_LOW_32BIT(Addr64) ((UINT32)(((UINTN)(Addr64)) & 0xFFFFFFFF)) +#define XHC_HIGH_32BIT(Addr64) ((UINT32)(RShiftU64((UINT64)(UINTN)(Addr64), 32) & 0xFFFFFFFF)) +#define XHC_BIT_IS_SET(Data, Bit) ((BOOLEAN)(((Data) & (Bit)) == (Bit))) + +// +// Endpoint Type (EP Type). +// +#define ED_NOT_VALID 0 +#define ED_ISOCH_OUT 1 +#define ED_BULK_OUT 2 +#define ED_INTERRUPT_OUT 3 +#define ED_CONTROL_BIDIR 4 +#define ED_ISOCH_IN 5 +#define ED_BULK_IN 6 +#define ED_INTERRUPT_IN 7 + +// +// 6.4.5 TRB Completion Codes +// +#define TRB_COMPLETION_INVALID 0 +#define TRB_COMPLETION_SUCCESS 1 +#define TRB_COMPLETION_DATA_BUFFER_ERROR 2 +#define TRB_COMPLETION_BABBLE_ERROR 3 +#define TRB_COMPLETION_USB_TRANSACTION_ERROR 4 +#define TRB_COMPLETION_TRB_ERROR 5 +#define TRB_COMPLETION_STALL_ERROR 6 +#define TRB_COMPLETION_SHORT_PACKET 13 + +// +// 6.4.6 TRB Types +// +#define TRB_TYPE_NORMAL 1 +#define TRB_TYPE_SETUP_STAGE 2 +#define TRB_TYPE_DATA_STAGE 3 +#define TRB_TYPE_STATUS_STAGE 4 +#define TRB_TYPE_ISOCH 5 +#define TRB_TYPE_LINK 6 +#define TRB_TYPE_EVENT_DATA 7 +#define TRB_TYPE_NO_OP 8 +#define TRB_TYPE_EN_SLOT 9 +#define TRB_TYPE_DIS_SLOT 10 +#define TRB_TYPE_ADDRESS_DEV 11 +#define TRB_TYPE_CON_ENDPOINT 12 +#define TRB_TYPE_EVALU_CONTXT 13 +#define TRB_TYPE_RESET_ENDPOINT 14 +#define TRB_TYPE_STOP_ENDPOINT 15 +#define TRB_TYPE_SET_TR_DEQUE 16 +#define TRB_TYPE_RESET_DEV 17 +#define TRB_TYPE_GET_PORT_BANW 21 +#define TRB_TYPE_FORCE_HEADER 22 +#define TRB_TYPE_NO_OP_COMMAND 23 +#define TRB_TYPE_TRANS_EVENT 32 +#define TRB_TYPE_COMMAND_COMPLT_EVENT 33 +#define TRB_TYPE_PORT_STATUS_CHANGE_EVENT 34 +#define TRB_TYPE_HOST_CONTROLLER_EVENT 37 +#define TRB_TYPE_DEVICE_NOTIFI_EVENT 38 +#define TRB_TYPE_MFINDEX_WRAP_EVENT 39 + +// +// Convert millisecond to microsecond. +// +#define XHC_1_MILLISECOND (1000) +#define XHC_POLL_DELAY (1000) +#define XHC_GENERIC_TIMEOUT (10 * 1000) + +#define EFI_USB_SPEED_FULL 0x0000 ///< 12 Mb/s, USB 1.1 OHCI and UHCI HC. +#define EFI_USB_SPEED_LOW 0x0001 ///< 1 Mb/s, USB 1.1 OHCI and UHCI HC. +#define EFI_USB_SPEED_HIGH 0x0002 ///< 480 Mb/s, USB 2.0 EHCI HC. +#define EFI_USB_SPEED_SUPER 0x0003 ///< 4.8 Gb/s, USB 3.0 XHCI HC. + +// +// Transfer types, used in URB to identify the transfer type +// +#define XHC_CTRL_TRANSFER 0x01 +#define XHC_BULK_TRANSFER 0x02 +#define XHC_INT_TRANSFER_SYNC 0x04 +#define XHC_INT_TRANSFER_ASYNC 0x08 +#define XHC_INT_ONLY_TRANSFER_ASYNC 0x10 + +// +// USB Transfer Results +// +#define EFI_USB_NOERROR 0x00 +#define EFI_USB_ERR_NOTEXECUTE 0x01 +#define EFI_USB_ERR_STALL 0x02 +#define EFI_USB_ERR_BUFFER 0x04 +#define EFI_USB_ERR_BABBLE 0x08 +#define EFI_USB_ERR_NAK 0x10 +#define EFI_USB_ERR_CRC 0x20 +#define EFI_USB_ERR_TIMEOUT 0x40 +#define EFI_USB_ERR_BITSTUFF 0x80 +#define EFI_USB_ERR_SYSTEM 0x100 + +#pragma pack(1) + +// +// 7.6.9 OUT/IN EP Context: 64 bytes +// 7.6.9.2 When used by the DbC it is always a 64 byte data structure +// +typedef struct _ENDPOINT_CONTEXT_64 { + UINT32 EPState:3; + UINT32 RsvdZ1:5; + UINT32 Mult:2; // set to 0 + UINT32 MaxPStreams:5; // set to 0 + UINT32 LSA:1; // set to 0 + UINT32 Interval:8; // set to 0 + UINT32 RsvdZ2:8; + + UINT32 RsvdZ3:1; + UINT32 CErr:2; + UINT32 EPType:3; + UINT32 RsvdZ4:1; + UINT32 HID:1; // set to 0 + UINT32 MaxBurstSize:8; + UINT32 MaxPacketSize:16; + + UINT32 PtrLo; + + UINT32 PtrHi; + + UINT32 AverageTRBLength:16; + UINT32 MaxESITPayload:16; // set to 0 + + UINT32 RsvdZ5; // Reserved + UINT32 RsvdZ6; + UINT32 RsvdZ7; + + UINT32 RsvdZ8; + UINT32 RsvdZ9; + UINT32 RsvdZ10; + UINT32 RsvdZ11; + + UINT32 RsvdZ12; + UINT32 RsvdZ13; + UINT32 RsvdZ14; + UINT32 RsvdZ15; +} ENDPOINT_CONTEXT_64; + +// +// 6.4.1.1 Normal TRB: 16 bytes +// A Normal TRB is used in several ways; exclusively on Bulk and Interrupt Transfer Rings for normal and +// Scatter/Gather operations, to define additional data buffers for Scatter/Gather operations on Isoch Transfer +// Rings, and to define the Data stage information for Control Transfer Rings. +// +typedef struct _TRANSFER_TRB_NORMAL { + UINT32 TRBPtrLo; + + UINT32 TRBPtrHi; + + UINT32 Length:17; + UINT32 TDSize:5; + UINT32 IntTarget:10; + + UINT32 CycleBit:1; + UINT32 ENT:1; + UINT32 ISP:1; + UINT32 NS:1; + UINT32 CH:1; + UINT32 IOC:1; + UINT32 IDT:1; + UINT32 RsvdZ1:2; + UINT32 BEI:1; + UINT32 Type:6; + UINT32 RsvdZ2:16; +} TRANSFER_TRB_NORMAL; + +// +// 6.4.2.1 Transfer Event TRB: 16 bytes +// A Transfer Event provides the completion status associated with a Transfer TRB. Refer to section 4.11.3.1 +// for more information on the use and operation of Transfer Events. +// +typedef struct _EVT_TRB_TRANSFER { + UINT32 TRBPtrLo; + + UINT32 TRBPtrHi; + + UINT32 Length:24; + UINT32 Completecode:8; + + UINT32 CycleBit:1; + UINT32 RsvdZ1:1; + UINT32 ED:1; + UINT32 RsvdZ2:7; + UINT32 Type:6; + UINT32 EndpointId:5; + UINT32 RsvdZ3:3; + UINT32 SlotId:8; +} EVT_TRB_TRANSFER; + +// +// 6.4.4.1 Link TRB: 16 bytes +// A Link TRB provides support for non-contiguous TRB Rings. +// +typedef struct _LINK_TRB { + UINT32 PtrLo; + + UINT32 PtrHi; + + UINT32 RsvdZ1:22; + UINT32 InterTarget:10; + + UINT32 CycleBit:1; + UINT32 TC:1; + UINT32 RsvdZ2:2; + UINT32 CH:1; + UINT32 IOC:1; + UINT32 RsvdZ3:4; + UINT32 Type:6; + UINT32 RsvdZ4:16; +} LINK_TRB; + +// +// TRB Template: 16 bytes +// +typedef struct _TRB_TEMPLATE { + UINT32 Parameter1; + + UINT32 Parameter2; + + UINT32 Status; + + UINT32 CycleBit:1; + UINT32 RsvdZ1:9; + UINT32 Type:6; + UINT32 Control:16; +} TRB_TEMPLATE; + +// +// Refer to XHCI 6.5 Event Ring Segment Table: 16 bytes +// +typedef struct _EVENT_RING_SEG_TABLE_ENTRY { + UINT32 PtrLo; + UINT32 PtrHi; + UINT32 RingTrbSize:16; + UINT32 RsvdZ1:16; + UINT32 RsvdZ2; +} EVENT_RING_SEG_TABLE_ENTRY; + +// +// Size: 40 bytes +// +typedef struct _EVENT_RING { + EFI_PHYSICAL_ADDRESS ERSTBase; + EFI_PHYSICAL_ADDRESS EventRingSeg0; + UINT32 TrbNumber; + EFI_PHYSICAL_ADDRESS EventRingEnqueue; + EFI_PHYSICAL_ADDRESS EventRingDequeue; + UINT32 EventRingCCS; +} EVENT_RING; + +// Size: 32 bytes +typedef struct _TRANSFER_RING { + EFI_PHYSICAL_ADDRESS RingSeg0; + UINT32 TrbNumber; + EFI_PHYSICAL_ADDRESS RingEnqueue; + EFI_PHYSICAL_ADDRESS RingDequeue; + UINT32 RingPCS; +} TRANSFER_RING; + +// +// Size: 64 bytes +// +typedef struct _DBC_INFO_CONTEXT { + UINT64 String0DescAddress; + UINT64 ManufacturerStrDescAddress; + UINT64 ProductStrDescAddress; + UINT64 SerialNumberStrDescAddress; + UINT64 String0Length:8; + UINT64 ManufacturerStrLength:8; + UINT64 ProductStrLength:8; + UINT64 SerialNumberStrLength:8; + UINT64 RsvdZ1:32; + UINT64 RsvdZ2; + UINT64 RsvdZ3; + UINT64 RsvdZ4; +} DBC_INFO_CONTEXT; + +// +// Debug Capability Context Data Structure: 192 bytes +// +typedef struct _XHC_DC_CONTEXT { + DBC_INFO_CONTEXT DbcInfoContext; + ENDPOINT_CONTEXT_64 EpOutContext; + ENDPOINT_CONTEXT_64 EpInContext; +} XHC_DC_CONTEXT; + +// +// Size: 16 bytes +// +typedef union _TRB { + TRB_TEMPLATE TrbTemplate; + TRANSFER_TRB_NORMAL TrbNormal; +} TRB; + +/// +/// USB data transfer direction +/// +typedef enum { + EfiUsbDataIn, + EfiUsbDataOut, + EfiUsbNoData +} EFI_USB_DATA_DIRECTION; + +// +// URB (Usb Request Block) contains information for all kinds of +// usb requests. +// +typedef struct _URB { + // + // Transfer data buffer + // + EFI_PHYSICAL_ADDRESS Data; + UINT32 DataLen; + + // + // Execute result + // + UINT32 Result; + // + // Completed data length + // + UINT32 Completed; + // + // Tranfer Ring info + // + EFI_PHYSICAL_ADDRESS Ring; + EFI_PHYSICAL_ADDRESS Trb; + BOOLEAN Finished; + EFI_USB_DATA_DIRECTION Direction; +} URB; + +typedef struct _USB3_DEBUG_PORT_INSTANCE { + UINT8 Initialized; + + // + // The flag indicates debug device is ready + // + BOOLEAN DebugSupport; + + // + // The flag indicates debug device is ready + // + BOOLEAN Ready; + + // + // The flag indicates if USB 3.0 ports has been turn off/on power + // + BOOLEAN ChangePortPower; + + // + // XHCI MMIO Base address + // + EFI_PHYSICAL_ADDRESS XhciMmioBase; + + // + // XHCI OP RegisterBase address + // + EFI_PHYSICAL_ADDRESS XhciOpRegister; + + // + // XHCI Debug Register Base Address + // + EFI_PHYSICAL_ADDRESS DebugCapabilityBase; + + // + // XHCI Debug Capability offset + // + UINT64 DebugCapabilityOffset; + + // + // XHCI Debug Context Address + // + EFI_PHYSICAL_ADDRESS DebugCapabilityContext; + + // + // Transfer Ring + // + TRANSFER_RING TransferRingOut; + TRANSFER_RING TransferRingIn; + + // + // EventRing + // + EVENT_RING EventRing; + + // + // URB - Read + // + URB UrbOut; + + // + // URB - Write + // + URB UrbIn; + + // + // The available data length in the following data buffer. + // + UINT8 DataCount; + // + // The data buffer address for data read and poll. + // + EFI_PHYSICAL_ADDRESS Data; +} USB3_DEBUG_PORT_HANDLE; + +#pragma pack() + +/** + Read XHCI debug register. + + @param Handle Debug port handle. + @param Offset The offset of the debug register. + + @return The register content read + +**/ +UINT32 +XhcReadDebugReg ( + IN USB3_DEBUG_PORT_HANDLE *Handle, + IN UINT32 Offset + ); + +/** + Set one bit of the debug register while keeping other bits. + + @param Handle Debug port handle. + @param Offset The offset of the debug register. + @param Bit The bit mask of the register to set. + +**/ +VOID +XhcSetDebugRegBit ( + IN USB3_DEBUG_PORT_HANDLE *Handle, + IN UINT32 Offset, + IN UINT32 Bit + ); + +/** + Write the data to the debug register. + + @param Handle Debug port handle. + @param Offset The offset of the debug register. + @param Data The data to write. + +**/ +VOID +XhcWriteDebugReg ( + IN USB3_DEBUG_PORT_HANDLE *Handle, + IN UINT32 Offset, + IN UINT32 Data + ); + +/** + Discover the USB3 debug device. + + @param Handle Debug port handle. + + @retval RETURN_SUCCESS The serial device was initialized. + @retval RETURN_DEVICE_ERROR The serial device could not be initialized. + +**/ +RETURN_STATUS +DiscoverUsb3DebugPort( + USB3_DEBUG_PORT_HANDLE *Handle + ); + +/** + Initialize the Serial Device hardware. + + @param Handle Debug port handle. + + @retval RETURN_SUCCESS The serial device was initialized successfully. + @retval !RETURN_SUCCESS Error. + +**/ +RETURN_STATUS +InitializeUsb3DebugPort ( + USB3_DEBUG_PORT_HANDLE *Handle + ); + +/** + Return XHCI MMIO base address. + +**/ +EFI_PHYSICAL_ADDRESS +GetXhciBaseAddress ( + VOID + ); + +/** + Verifies if the bit positions specified by a mask are set in a register. + + @param[in, out] Register UNITN register + @param[in] BitMask 32-bit mask + + @return BOOLEAN - TRUE if all bits specified by the mask are enabled. + - FALSE even if one of the bits specified by the mask + is not enabled. +**/ +BOOLEAN +XhcIsBitSet( + UINTN Register, + UINT32 BitMask + ); + +/** + Sets bits as per the enabled bit positions in the mask. + + @param[in, out] Register UINTN register + @param[in] BitMask 32-bit mask +**/ +VOID +XhcSetR32Bit( + UINTN Register, + UINT32 BitMask + ); + +/** + Clears bits as per the enabled bit positions in the mask. + + @param[in, out] Register UINTN register + @param[in] BitMask 32-bit mask +**/ +VOID +XhcClearR32Bit( + IN OUT UINTN Register, + IN UINT32 BitMask + ); + +/** + Initialize USB3 debug port. + + This method invokes various internal functions to facilitate + detection and initialization of USB3 debug port. + + @retval RETURN_SUCCESS The serial device was initialized. +**/ +RETURN_STATUS +EFIAPI +USB3Initialize ( + VOID + ); + +/** + Return command register value in XHCI controller. + +**/ +UINT16 +GetXhciPciCommand ( + VOID + ); + +/** + Allocate aligned memory for XHC's usage. + + @param BufferSize The size, in bytes, of the Buffer. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID* +AllocateAlignBuffer ( + IN UINTN BufferSize + ); + +/** + The real function to initialize USB3 debug port. + + This method invokes various internal functions to facilitate + detection and initialization of USB3 debug port. + + @retval RETURN_SUCCESS The serial device was initialized. +**/ +RETURN_STATUS +EFIAPI +USB3InitializeReal ( + VOID + ); + +/** + Submits bulk transfer to a bulk endpoint of a USB device. + + @param Handle The instance of debug device. + @param Direction The direction of data transfer. + @param Data Array of pointers to the buffers of data to transmit + from or receive into. + @param DataLength The lenght of the data buffer. + @param Timeout Indicates the maximum time, in millisecond, which + the transfer is allowed to complete. + + @retval EFI_SUCCESS The transfer was completed successfully. + @retval EFI_OUT_OF_RESOURCES The transfer failed due to lack of resource. + @retval EFI_INVALID_PARAMETER Some parameters are invalid. + @retval EFI_TIMEOUT The transfer failed due to timeout. + @retval EFI_DEVICE_ERROR The transfer failed due to host controller error. + +**/ +EFI_STATUS +EFIAPI +XhcDataTransfer ( + IN USB3_DEBUG_PORT_HANDLE *Handle, + IN EFI_USB_DATA_DIRECTION Direction, + IN OUT VOID *Data, + IN OUT UINTN *DataLength, + IN UINTN Timeout + ); + +#endif //__SERIAL_PORT_LIB_USB__ diff --git a/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Pei.c b/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Pei.c new file mode 100644 index 0000000000..be3174e75f --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Pei.c @@ -0,0 +1,45 @@ +/** @file + Debug Port Library implementation based on usb3 debug port. + + Copyright (c) 2014, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include +#include +#include "DebugCommunicationLibUsb3Internal.h" + +/** + Allocate aligned memory for XHC's usage. + + @param BufferSize The size, in bytes, of the Buffer. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID* +AllocateAlignBuffer ( + IN UINTN BufferSize + ) +{ + VOID *Buf; + EFI_PHYSICAL_ADDRESS Address; + EFI_STATUS Status; + + Buf = NULL; + Status = PeiServicesAllocatePages (EfiACPIMemoryNVS, EFI_SIZE_TO_PAGES (BufferSize), &Address); + if (EFI_ERROR (Status)) { + Buf = NULL; + } else { + Buf = (VOID *)(UINTN) Address; + } + return Buf; +} + diff --git a/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Pei.inf b/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Pei.inf new file mode 100644 index 0000000000..74dcdd124b --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Pei.inf @@ -0,0 +1,70 @@ +## @file +# Debug Communication Library instance based on usb3 debug port. +# +# Copyright (c) 2014 - 2015, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php. +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = DebugCommunicationLibUsb3Pei + MODULE_UNI_FILE = DebugCommunicationLibUsb3Pei.uni + FILE_GUID = 106C877F-C2BA-4c46-876C-BDFE6171CD7E + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + LIBRARY_CLASS = DebugCommunicationLib|PEIM PEI_CORE + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF +# + +[Sources] + DebugCommunicationLibUsb3Pei.c + DebugCommunicationLibUsb3Transfer.c + DebugCommunicationLibUsb3Common.c + DebugCommunicationLibUsb3Internal.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + IntelFrameworkPkg/IntelFrameworkPkg.dec + SourceLevelDebugPkg/SourceLevelDebugPkg.dec + +[Pcd] + # The memory BAR of ehci host controller, in which usb debug feature is enabled. + # Note that the memory BAR address is only used before Pci bus resource allocation. + gEfiSourceLevelDebugPkgTokenSpaceGuid.PcdUsbXhciMemorySpaceBase ## SOMETIMES_CONSUMES + + # The pci address of ehci host controller, in which usb debug feature is enabled. + # The format of pci address please refer to SourceLevelDebugPkg.dec + gEfiSourceLevelDebugPkgTokenSpaceGuid.PcdUsbXhciPciAddress ## CONSUMES + + # Per XHCI spec, software shall impose a timeout between the detection of the Debug Host + # connection and the DbC Run transition to 1. This PCD specifies the timeout value in microsecond. + gEfiSourceLevelDebugPkgTokenSpaceGuid.PcdUsbXhciDebugDetectTimeout ## SOMETIMES_CONSUMES + + # The value of data buffer size used for USB debug port handle. + # It should be equal to sizeof (USB3_DEBUG_PORT_HANDLE). + gEfiSourceLevelDebugPkgTokenSpaceGuid.PcdDebugPortHandleBufferSize|239 ## SOMETIMES_CONSUMES + +[LibraryClasses] + BaseLib + PcdLib + IoLib + PciLib + TimerLib + BaseMemoryLib + PeiServicesLib + +[Depex.common.PEIM] + gEfiPeiMemoryDiscoveredPpiGuid diff --git a/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Pei.uni b/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Pei.uni new file mode 100644 index 0000000000..7b34f01901 --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Pei.uni @@ -0,0 +1,21 @@ +// /** @file +// Debug Communication Library instance based on USB3 debug port for PEI modules. +// +// Debug Communication Library instance based on USB3 debug port for PEI modules. +// +// Copyright (c) 2014, Intel Corporation. All rights reserved.
+// +// This program and the accompanying materials +// are licensed and made available under the terms and conditions of the BSD License +// which accompanies this distribution. The full text of the license may be found at +// http://opensource.org/licenses/bsd-license.php. +// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Debug Communication Library instance based on USB3 debug port for PEI modules" + +#string STR_MODULE_DESCRIPTION #language en-US "Debug Communication Library instance based on USB3 debug port for PEI modules." + diff --git a/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Transfer.c b/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Transfer.c new file mode 100644 index 0000000000..dbff493624 --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Transfer.c @@ -0,0 +1,578 @@ +/** @file + Debug Port Library implementation based on usb3 debug port. + + Copyright (c) 2014 - 2015, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ +#include "DebugCommunicationLibUsb3Internal.h" + +/** + Synchronize the specified transfer ring to update the enqueue and dequeue pointer. + + @param Handle Debug port handle. + @param TrsRing The transfer ring to sync. + + @retval EFI_SUCCESS The transfer ring is synchronized successfully. + +**/ +EFI_STATUS +EFIAPI +XhcSyncTrsRing ( + IN USB3_DEBUG_PORT_HANDLE *Handle, + IN TRANSFER_RING *TrsRing + ) +{ + UINTN Index; + TRB_TEMPLATE *TrsTrb; + UINT32 CycleBit; + + ASSERT (TrsRing != NULL); + + // + // Calculate the latest RingEnqueue and RingPCS + // + TrsTrb = (TRB_TEMPLATE *)(UINTN) TrsRing->RingEnqueue; + + ASSERT (TrsTrb != NULL); + + for (Index = 0; Index < TrsRing->TrbNumber; Index++) { + if (TrsTrb->CycleBit != (TrsRing->RingPCS & BIT0)) { + break; + } + TrsTrb++; + if ((UINT8) TrsTrb->Type == TRB_TYPE_LINK) { + ASSERT (((LINK_TRB*)TrsTrb)->TC != 0); + // + // set cycle bit in Link TRB as normal + // + ((LINK_TRB*)TrsTrb)->CycleBit = TrsRing->RingPCS & BIT0; + // + // Toggle PCS maintained by software + // + TrsRing->RingPCS = (TrsRing->RingPCS & BIT0) ? 0 : 1; + TrsTrb = (TRB_TEMPLATE *)(UINTN)((TrsTrb->Parameter1 | LShiftU64 ((UINT64)TrsTrb->Parameter2, 32)) & ~0x0F); + } + } + ASSERT (Index != TrsRing->TrbNumber); + + if ((EFI_PHYSICAL_ADDRESS)(UINTN) TrsTrb != TrsRing->RingEnqueue) { + TrsRing->RingEnqueue = (EFI_PHYSICAL_ADDRESS)(UINTN) TrsTrb; + } + + // + // Clear the Trb context for enqueue, but reserve the PCS bit which indicates free Trb. + // + CycleBit = TrsTrb->CycleBit; + ZeroMem (TrsTrb, sizeof (TRB_TEMPLATE)); + TrsTrb->CycleBit = CycleBit; + + return EFI_SUCCESS; +} + +/** + Synchronize the specified event ring to update the enqueue and dequeue pointer. + + @param Handle Debug port handle. + @param EvtRing The event ring to sync. + + @retval EFI_SUCCESS The event ring is synchronized successfully. + +**/ +EFI_STATUS +EFIAPI +XhcSyncEventRing ( + IN USB3_DEBUG_PORT_HANDLE *Handle, + IN EVENT_RING *EvtRing + ) +{ + UINTN Index; + TRB_TEMPLATE *EvtTrb1; + + ASSERT (EvtRing != NULL); + + // + // Calculate the EventRingEnqueue and EventRingCCS. + // Note: only support single Segment + // + EvtTrb1 = (TRB_TEMPLATE *)(UINTN) EvtRing->EventRingDequeue; + + for (Index = 0; Index < EvtRing->TrbNumber; Index++) { + if (EvtTrb1->CycleBit != EvtRing->EventRingCCS) { + break; + } + + EvtTrb1++; + + if ((UINTN)EvtTrb1 >= ((UINTN) EvtRing->EventRingSeg0 + sizeof (TRB_TEMPLATE) * EvtRing->TrbNumber)) { + EvtTrb1 = (TRB_TEMPLATE *)(UINTN) EvtRing->EventRingSeg0; + EvtRing->EventRingCCS = (EvtRing->EventRingCCS) ? 0 : 1; + } + } + + if (Index < EvtRing->TrbNumber) { + EvtRing->EventRingEnqueue = (EFI_PHYSICAL_ADDRESS)(UINTN)EvtTrb1; + } else { + ASSERT (FALSE); + } + + return EFI_SUCCESS; +} + +/** + Check if there is a new generated event. + + @param Handle Debug port handle. + @param EvtRing The event ring to check. + @param NewEvtTrb The new event TRB found. + + @retval EFI_SUCCESS Found a new event TRB at the event ring. + @retval EFI_NOT_READY The event ring has no new event. + +**/ +EFI_STATUS +EFIAPI +XhcCheckNewEvent ( + IN USB3_DEBUG_PORT_HANDLE *Handle, + IN EVENT_RING *EvtRing, + OUT TRB_TEMPLATE **NewEvtTrb + ) +{ + EFI_STATUS Status; + TRB_TEMPLATE *EvtTrb; + + ASSERT (EvtRing != NULL); + + EvtTrb = (TRB_TEMPLATE *)(UINTN) EvtRing->EventRingDequeue; + *NewEvtTrb = (TRB_TEMPLATE *)(UINTN) EvtRing->EventRingDequeue; + + if (EvtRing->EventRingDequeue == EvtRing->EventRingEnqueue) { + return EFI_NOT_READY; + } + + Status = EFI_SUCCESS; + + EvtRing->EventRingDequeue += sizeof (TRB_TEMPLATE); + // + // If the dequeue pointer is beyond the ring, then roll-back it to the begining of the ring. + // + if ((UINTN)EvtRing->EventRingDequeue >= ((UINTN) EvtRing->EventRingSeg0 + sizeof (TRB_TEMPLATE) * EvtRing->TrbNumber)) { + EvtRing->EventRingDequeue = EvtRing->EventRingSeg0; + } + + return Status; +} + +/** + Check if the Trb is a transaction of the URB. + + @param Ring The transfer ring to be checked. + @param Trb The TRB to be checked. + + @retval TRUE It is a transaction of the URB. + @retval FALSE It is not any transaction of the URB. + +**/ +BOOLEAN +IsTrbInTrsRing ( + IN TRANSFER_RING *Ring, + IN TRB_TEMPLATE *Trb + ) +{ + TRB_TEMPLATE *CheckedTrb; + UINTN Index; + + CheckedTrb = (TRB_TEMPLATE *)(UINTN) Ring->RingSeg0; + + ASSERT (Ring->TrbNumber == TR_RING_TRB_NUMBER); + + for (Index = 0; Index < Ring->TrbNumber; Index++) { + if (Trb == CheckedTrb) { + return TRUE; + } + CheckedTrb++; + } + + return FALSE; +} + +/** + Check the URB's execution result and update the URB's + result accordingly. + + @param Handle Debug port handle. + @param Urb The URB to check result. + +**/ +VOID +XhcCheckUrbResult ( + IN USB3_DEBUG_PORT_HANDLE *Handle, + IN URB *Urb + ) +{ + EVT_TRB_TRANSFER *EvtTrb; + TRB_TEMPLATE *TRBPtr; + UINTN Index; + EFI_STATUS Status; + URB *CheckedUrb; + UINT64 XhcDequeue; + UINT32 High; + UINT32 Low; + + ASSERT ((Handle != NULL) && (Urb != NULL)); + + if (Urb->Finished) { + goto EXIT; + } + + EvtTrb = NULL; + + // + // Traverse the event ring to find out all new events from the previous check. + // + XhcSyncEventRing (Handle, &Handle->EventRing); + + for (Index = 0; Index < Handle->EventRing.TrbNumber; Index++) { + + Status = XhcCheckNewEvent (Handle, &Handle->EventRing, ((TRB_TEMPLATE **)&EvtTrb)); + if (Status == EFI_NOT_READY) { + // + // All new events are handled, return directly. + // + goto EXIT; + } + + if ((EvtTrb->Type != TRB_TYPE_COMMAND_COMPLT_EVENT) && (EvtTrb->Type != TRB_TYPE_TRANS_EVENT)) { + continue; + } + + TRBPtr = (TRB_TEMPLATE *)(UINTN)(EvtTrb->TRBPtrLo | LShiftU64 ((UINT64) EvtTrb->TRBPtrHi, 32)); + + if (IsTrbInTrsRing ((TRANSFER_RING *)(UINTN)(Urb->Ring), TRBPtr)) { + CheckedUrb = Urb; + } else if (IsTrbInTrsRing ((TRANSFER_RING *)(UINTN)(Handle->UrbIn.Ring), TRBPtr)) { + // + // If it is read event and it should be generated by poll, and current operation is write, we need save data into internal buffer. + // Internal buffer is used by next read. + // + Handle->DataCount = (UINT8) (Handle->UrbIn.DataLen - EvtTrb->Length); + CopyMem ((VOID *)(UINTN)Handle->Data, (VOID *)(UINTN)Handle->UrbIn.Data, Handle->DataCount); + // + // Fill this TRB complete with CycleBit, otherwise next read will fail with old TRB. + // + TRBPtr->CycleBit = (TRBPtr->CycleBit & BIT0) ? 0 : 1; + continue; + } else { + continue; + } + + if ((EvtTrb->Completecode == TRB_COMPLETION_SHORT_PACKET) || + (EvtTrb->Completecode == TRB_COMPLETION_SUCCESS)) { + // + // The length of data which were transferred. + // + CheckedUrb->Completed += (((TRANSFER_TRB_NORMAL*)TRBPtr)->Length - EvtTrb->Length); + } else { + CheckedUrb->Result |= EFI_USB_ERR_TIMEOUT; + } + // + // This Urb has been processed + // + CheckedUrb->Finished = TRUE; + } + +EXIT: + // + // Advance event ring to last available entry + // + // Some 3rd party XHCI external cards don't support single 64-bytes width register access, + // So divide it to two 32-bytes width register access. + // + Low = XhcReadDebugReg (Handle, XHC_DC_DCERDP); + High = XhcReadDebugReg (Handle, XHC_DC_DCERDP + 4); + XhcDequeue = (UINT64)(LShiftU64((UINT64)High, 32) | Low); + + if ((XhcDequeue & (~0x0F)) != ((UINT64)(UINTN)Handle->EventRing.EventRingDequeue & (~0x0F))) { + // + // Some 3rd party XHCI external cards don't support single 64-bytes width register access, + // So divide it to two 32-bytes width register access. + // + XhcWriteDebugReg (Handle, XHC_DC_DCERDP, XHC_LOW_32BIT (Handle->EventRing.EventRingDequeue)); + XhcWriteDebugReg (Handle, XHC_DC_DCERDP + 4, XHC_HIGH_32BIT (Handle->EventRing.EventRingDequeue)); + } +} + +/** + Ring the door bell to notify XHCI there is a transaction to be executed. + + @param Handle Debug port handle. + @param Urb The pointer to URB. + + @retval EFI_SUCCESS Successfully ring the door bell. + +**/ +EFI_STATUS +EFIAPI +XhcRingDoorBell ( + IN USB3_DEBUG_PORT_HANDLE *Handle, + IN URB *Urb + ) +{ + UINT32 Dcdb; + + // + // 7.6.8.2 DCDB Register + // + Dcdb = (Urb->Direction == EfiUsbDataIn) ? 0x100 : 0x0; + + XhcWriteDebugReg ( + Handle, + XHC_DC_DCDB, + Dcdb + ); + + return EFI_SUCCESS; +} + +/** + Execute the transfer by polling the URB. This is a synchronous operation. + + @param Handle Debug port handle. + @param Urb The URB to execute. + @param Timeout The time to wait before abort, in microsecond. + +**/ +VOID +XhcExecTransfer ( + IN USB3_DEBUG_PORT_HANDLE *Handle, + IN URB *Urb, + IN UINTN Timeout + ) +{ + TRANSFER_RING *Ring; + TRB_TEMPLATE *Trb; + UINTN Loop; + UINTN Index; + + Loop = Timeout / XHC_DEBUG_PORT_1_MILLISECOND; + if (Timeout == 0) { + Loop = 0xFFFFFFFF; + } + XhcRingDoorBell (Handle, Urb); + // + // Event Ring Not Empty bit can only be set to 1 by XHC after ringing door bell with some delay. + // + for (Index = 0; Index < Loop; Index++) { + XhcCheckUrbResult (Handle, Urb); + if (Urb->Finished) { + break; + } + MicroSecondDelay (XHC_DEBUG_PORT_1_MILLISECOND); + } + if (Index == Loop) { + // + // If time out occurs. + // + Urb->Result |= EFI_USB_ERR_TIMEOUT; + } + // + // If URB transfer is error, restore transfer ring to original value before URB transfer + // This will make the current transfer TRB is always at the latest unused one in transfer ring. + // + Ring = (TRANSFER_RING *)(UINTN) Urb->Ring; + if ((Urb->Result != EFI_USB_NOERROR) && (Urb->Direction == EfiUsbDataIn)) { + // + // Adjust Enqueue pointer + // + Ring->RingEnqueue = Urb->Trb; + // + // Clear CCS flag for next use + // + Trb = (TRB_TEMPLATE *)(UINTN) Urb->Trb; + Trb->CycleBit = ((~Ring->RingPCS) & BIT0); + } else { + // + // Update transfer ring for next transfer. + // + XhcSyncTrsRing (Handle, Ring); + } +} + +/** + Create a transfer TRB. + + @param Handle Debug port handle. + @param Urb The urb used to construct the transfer TRB. + + @return Created TRB or NULL + +**/ +EFI_STATUS +XhcCreateTransferTrb ( + IN USB3_DEBUG_PORT_HANDLE *Handle, + IN URB *Urb + ) +{ + TRANSFER_RING *EPRing; + TRB *Trb; + + if (Urb->Direction == EfiUsbDataIn) { + EPRing = &Handle->TransferRingIn; + } else { + EPRing = &Handle->TransferRingOut; + } + + Urb->Ring = (EFI_PHYSICAL_ADDRESS)(UINTN) EPRing; + XhcSyncTrsRing (Handle, EPRing); + + Urb->Trb = EPRing->RingEnqueue; + Trb = (TRB *)(UINTN)EPRing->RingEnqueue; + Trb->TrbNormal.TRBPtrLo = XHC_LOW_32BIT (Urb->Data); + Trb->TrbNormal.TRBPtrHi = XHC_HIGH_32BIT (Urb->Data); + Trb->TrbNormal.Length = Urb->DataLen; + Trb->TrbNormal.TDSize = 0; + Trb->TrbNormal.IntTarget = 0; + Trb->TrbNormal.ISP = 1; + Trb->TrbNormal.IOC = 1; + Trb->TrbNormal.Type = TRB_TYPE_NORMAL; + + // + // Update the cycle bit to indicate this TRB has been consumed. + // + Trb->TrbNormal.CycleBit = EPRing->RingPCS & BIT0; + + return EFI_SUCCESS; +} + +/** + Create a new URB for a new transaction. + + @param Handle Debug port handle. + @param Direction The direction of data flow. + @param Data The user data to transfer + @param DataLen The length of data buffer + + @return Created URB or NULL + +**/ +URB* +XhcCreateUrb ( + IN USB3_DEBUG_PORT_HANDLE *Handle, + IN EFI_USB_DATA_DIRECTION Direction, + IN VOID *Data, + IN UINTN DataLen + ) +{ + EFI_STATUS Status; + URB *Urb; + EFI_PHYSICAL_ADDRESS UrbData; + + if (Direction == EfiUsbDataIn) { + Urb = &Handle->UrbIn; + } else { + Urb = &Handle->UrbOut; + } + + UrbData = Urb->Data; + + ZeroMem (Urb, sizeof (URB)); + Urb->Direction = Direction; + + // + // Allocate memory to move data from CAR or SMRAM to normal memory + // to make XHCI DMA successfully + // re-use the pre-allocate buffer in PEI to avoid DXE memory service or gBS are not ready + // + Urb->Data = UrbData; + + if (Direction == EfiUsbDataIn) { + // + // Do not break URB data in buffer as it may contain the data which were just put in via DMA by XHC + // + Urb->DataLen = (UINT32) DataLen; + } else { + // + // Put data into URB data out buffer which will create TRBs + // + ZeroMem ((VOID*)(UINTN) Urb->Data, DataLen); + CopyMem ((VOID*)(UINTN) Urb->Data, Data, DataLen); + Urb->DataLen = (UINT32) DataLen; + } + + Status = XhcCreateTransferTrb (Handle, Urb); + ASSERT_EFI_ERROR (Status); + + return Urb; +} + +/** + Submits bulk transfer to a bulk endpoint of a USB device. + + @param Handle Debug port handle. + @param Direction The direction of data transfer. + @param Data Array of pointers to the buffers of data to transmit + from or receive into. + @param DataLength The lenght of the data buffer. + @param Timeout Indicates the maximum time, in microsecond, which + the transfer is allowed to complete. + + @retval EFI_SUCCESS The transfer was completed successfully. + @retval EFI_OUT_OF_RESOURCES The transfer failed due to lack of resource. + @retval EFI_INVALID_PARAMETER Some parameters are invalid. + @retval EFI_TIMEOUT The transfer failed due to timeout. + @retval EFI_DEVICE_ERROR The transfer failed due to host controller error. + +**/ +EFI_STATUS +EFIAPI +XhcDataTransfer ( + IN USB3_DEBUG_PORT_HANDLE *Handle, + IN EFI_USB_DATA_DIRECTION Direction, + IN OUT VOID *Data, + IN OUT UINTN *DataLength, + IN UINTN Timeout + ) +{ + URB *Urb; + EFI_STATUS Status; + + // + // Validate the parameters + // + if ((DataLength == NULL) || (*DataLength == 0) || (Data == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Create a new URB, insert it into the asynchronous + // schedule list, then poll the execution status. + // + Urb = XhcCreateUrb (Handle, Direction, Data, *DataLength); + ASSERT (Urb != NULL); + + XhcExecTransfer (Handle, Urb, Timeout); + + *DataLength = Urb->Completed; + + Status = EFI_TIMEOUT; + if (Urb->Result == EFI_USB_NOERROR) { + Status = EFI_SUCCESS; + } + + if (Direction == EfiUsbDataIn) { + // + // Move data from internal buffer to outside buffer (outside buffer may be in SMRAM...) + // SMRAM does not allow to do DMA, so we create an internal buffer. + // + CopyMem (Data, (VOID *)(UINTN)Urb->Data, *DataLength); + } + + return Status; +} + diff --git a/Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/Ia32/IntHandler.S b/Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/Ia32/IntHandler.S new file mode 100644 index 0000000000..69c903788c --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/Ia32/IntHandler.S @@ -0,0 +1,28 @@ +#------------------------------------------------------------------------------ +# +# Copyright (c) 2013, Intel Corporation. All rights reserved.
+# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php. +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# Module Name: +# +# IntHandler.S +# +# Abstract: +# +# Assembly interrupt handler function. +# +#------------------------------------------------------------------------------ + +ASM_GLOBAL ASM_PFX(AsmInterruptHandle) + +.text +ASM_PFX(AsmInterruptHandle): + cli + movb $1, %al + iretl diff --git a/Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/Ia32/IntHandler.asm b/Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/Ia32/IntHandler.asm new file mode 100644 index 0000000000..ce9c03b490 --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/Ia32/IntHandler.asm @@ -0,0 +1,33 @@ +;------------------------------------------------------------------------------ +; +; Copyright (c) 2013, Intel Corporation. All rights reserved.
+; This program and the accompanying materials +; are licensed and made available under the terms and conditions of the BSD License +; which accompanies this distribution. The full text of the license may be found at +; http://opensource.org/licenses/bsd-license.php. +; +; THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +; WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +; +; Module Name: +; +; IntHandler.asm +; +; Abstract: +; +; Assembly interrupt handler function. +; +;------------------------------------------------------------------------------ + + +.686p +.model flat,c + +public AsmInterruptHandle + +.code +AsmInterruptHandle: + cli + mov al, 1 + iretd +END diff --git a/Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/Ia32/IntHandler.nasm b/Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/Ia32/IntHandler.nasm new file mode 100644 index 0000000000..9d7d2a2560 --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/Ia32/IntHandler.nasm @@ -0,0 +1,28 @@ +;------------------------------------------------------------------------------ +; +; Copyright (c) 2016, Intel Corporation. All rights reserved.
+; This program and the accompanying materials +; are licensed and made available under the terms and conditions of the BSD License +; which accompanies this distribution. The full text of the license may be found at +; http://opensource.org/licenses/bsd-license.php. +; +; THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +; WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +; +; Module Name: +; +; IntHandler.nasm +; +; Abstract: +; +; Assembly interrupt handler function. +; +;------------------------------------------------------------------------------ + +global ASM_PFX(AsmInterruptHandle) + +SECTION .text +ASM_PFX(AsmInterruptHandle): + cli + mov al, 1 + iretd diff --git a/Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/Ia32/IntHandlerFuncs.c b/Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/Ia32/IntHandlerFuncs.c new file mode 100644 index 0000000000..ccf3fb1842 --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/Ia32/IntHandlerFuncs.c @@ -0,0 +1,99 @@ +/** @file + Ia32 arch functions to access IDT vector. + + Copyright (c) 2013, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include + +/** + Read IDT entry to check if IDT entries are setup by Debug Agent. + + @param[in] IdtDescriptor Pointer to IDT Descriptor. + @param[in] InterruptType Interrupt type. + + @retval TRUE IDT entries were setup by Debug Agent. + @retval FALSE IDT entries were not setuo by Debug Agent. + +**/ +BOOLEAN +CheckDebugAgentHandler ( + IN IA32_DESCRIPTOR *IdtDescriptor, + IN UINTN InterruptType + ) +{ + IA32_IDT_GATE_DESCRIPTOR *IdtEntry; + UINTN InterruptHandler; + + IdtEntry = (IA32_IDT_GATE_DESCRIPTOR *) IdtDescriptor->Base; + if (IdtEntry == NULL) { + return FALSE; + } + + InterruptHandler = IdtEntry[InterruptType].Bits.OffsetLow + + (IdtEntry[InterruptType].Bits.OffsetHigh << 16); + if (InterruptHandler >= sizeof (UINT32) && *(UINT32 *)(InterruptHandler - sizeof (UINT32)) == AGENT_HANDLER_SIGNATURE) { + return TRUE; + } else { + return FALSE; + } +} + +/** + Save IDT entry for INT1 and update it. + + @param[in] IdtDescriptor Pointer to IDT Descriptor. + @param[out] SavedIdtEntry Original IDT entry returned. + +**/ +VOID +SaveAndUpdateIdtEntry1 ( + IN IA32_DESCRIPTOR *IdtDescriptor, + OUT IA32_IDT_GATE_DESCRIPTOR *SavedIdtEntry + ) +{ + IA32_IDT_GATE_DESCRIPTOR *IdtEntry; + UINT16 CodeSegment; + UINTN InterruptHandler; + + IdtEntry = (IA32_IDT_GATE_DESCRIPTOR *) IdtDescriptor->Base; + CopyMem (SavedIdtEntry, &IdtEntry[1], sizeof (IA32_IDT_GATE_DESCRIPTOR)); + + // + // Use current CS as the segment selector of interrupt gate in IDT + // + CodeSegment = AsmReadCs (); + + InterruptHandler = (UINTN) &AsmInterruptHandle; + IdtEntry[1].Bits.OffsetLow = (UINT16)(UINTN)InterruptHandler; + IdtEntry[1].Bits.OffsetHigh = (UINT16)((UINTN)InterruptHandler >> 16); + IdtEntry[1].Bits.Selector = CodeSegment; + IdtEntry[1].Bits.GateType = IA32_IDT_GATE_TYPE_INTERRUPT_32; +} + +/** + Restore IDT entry for INT1. + + @param[in] IdtDescriptor Pointer to IDT Descriptor. + @param[in] RestoredIdtEntry IDT entry to be restored. + +**/ +VOID +RestoreIdtEntry1 ( + IN IA32_DESCRIPTOR *IdtDescriptor, + IN IA32_IDT_GATE_DESCRIPTOR *RestoredIdtEntry + ) +{ + IA32_IDT_GATE_DESCRIPTOR *IdtEntry; + + IdtEntry = (IA32_IDT_GATE_DESCRIPTOR *) IdtDescriptor->Base; + CopyMem (&IdtEntry[1], RestoredIdtEntry, sizeof (IA32_IDT_GATE_DESCRIPTOR)); +} diff --git a/Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/PeCoffExtraActionLib.c b/Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/PeCoffExtraActionLib.c new file mode 100644 index 0000000000..87420709f6 --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/PeCoffExtraActionLib.c @@ -0,0 +1,230 @@ +/** @file + PE/Coff Extra Action library instances. + + Copyright (c) 2010 - 2015, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include + +/** + Check if the hardware breakpoint in Drx is enabled by checking the Lx and Gx bit in Dr7. + + It assumes that DebugAgent will set both Lx and Gx bit when setting up the hardware breakpoint. + + + @param RegisterIndex Index of Dr register. The value range is from 0 to 3. + @param Dr7 Value of Dr7 register. + + @return TRUE The hardware breakpoint specified in the Drx is enabled. + @return FALSE The hardware breakpoint specified in the Drx is disabled. + +**/ +BOOLEAN +IsDrxEnabled ( + IN UINT8 RegisterIndex, + IN UINTN Dr7 + ) +{ + return (BOOLEAN) (((Dr7 >> (RegisterIndex * 2)) & (BIT0 | BIT1)) == (BIT0 | BIT1)); +} + +/** + Common routine to report the PE/COFF image loading/relocating or unloading event. + + If ImageContext is NULL, then ASSERT(). + + @param ImageContext Pointer to the image context structure that describes the + PE/COFF image. + @param Signature IMAGE_LOAD_SIGNATURE or IMAGE_UNLOAD_SIGNATURE. + +**/ +VOID +PeCoffLoaderExtraActionCommon ( + IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext, + IN UINTN Signature + ) +{ + BOOLEAN InterruptState; + UINTN Dr0; + UINTN Dr1; + UINTN Dr2; + UINTN Dr3; + UINTN Dr7; + UINTN Cr4; + UINTN NewDr7; + UINT8 LoadImageMethod; + UINT8 DebugAgentStatus; + IA32_DESCRIPTOR IdtDescriptor; + IA32_IDT_GATE_DESCRIPTOR OriginalIdtEntry; + BOOLEAN IdtEntryHooked; + UINT32 RegEdx; + + ASSERT (ImageContext != NULL); + + if (ImageContext->PdbPointer != NULL) { + DEBUG((EFI_D_ERROR, " PDB = %a\n", ImageContext->PdbPointer)); + } + + // + // Disable interrupts and save the current interrupt state + // + InterruptState = SaveAndDisableInterrupts (); + + IdtEntryHooked = FALSE; + LoadImageMethod = PcdGet8 (PcdDebugLoadImageMethod); + if (LoadImageMethod == DEBUG_LOAD_IMAGE_METHOD_IO_HW_BREAKPOINT) { + // + // If the CPU does not support Debug Extensions(CPUID:01 EDX:BIT2) + // then force use of DEBUG_LOAD_IMAGE_METHOD_SOFT_INT3 + // + AsmCpuid (1, NULL, NULL, NULL, &RegEdx); + if ((RegEdx & BIT2) == 0) { + LoadImageMethod = DEBUG_LOAD_IMAGE_METHOD_SOFT_INT3; + } + } + AsmReadIdtr (&IdtDescriptor); + if (LoadImageMethod == DEBUG_LOAD_IMAGE_METHOD_SOFT_INT3) { + if (!CheckDebugAgentHandler (&IdtDescriptor, SOFT_INT_VECTOR_NUM)) { + // + // Do not trigger INT3 if Debug Agent did not setup IDT entries. + // + return; + } + } else { + if (!CheckDebugAgentHandler (&IdtDescriptor, IO_HW_BREAKPOINT_VECTOR_NUM)) { + // + // Save and update IDT entry for INT1 + // + SaveAndUpdateIdtEntry1 (&IdtDescriptor, &OriginalIdtEntry); + IdtEntryHooked = TRUE; + } + } + + // + // Save Debug Register State + // + Dr0 = AsmReadDr0 (); + Dr1 = AsmReadDr1 (); + Dr2 = AsmReadDr2 (); + Dr3 = AsmReadDr3 (); + Dr7 = AsmReadDr7 () | BIT10; // H/w sets bit 10, some simulators don't + Cr4 = AsmReadCr4 (); + + // + // DR0 = Signature + // DR1 = The address of the Null-terminated ASCII string for the PE/COFF image's PDB file name + // DR2 = The pointer to the ImageContext structure + // DR3 = IO_PORT_BREAKPOINT_ADDRESS + // DR7 = Disables all HW breakpoints except for DR3 I/O port access of length 1 byte + // CR4 = Make sure DE(BIT3) is set + // + AsmWriteDr7 (BIT10); + AsmWriteDr0 (Signature); + AsmWriteDr1 ((UINTN) ImageContext->PdbPointer); + AsmWriteDr2 ((UINTN) ImageContext); + AsmWriteDr3 (IO_PORT_BREAKPOINT_ADDRESS); + + if (LoadImageMethod == DEBUG_LOAD_IMAGE_METHOD_IO_HW_BREAKPOINT) { + AsmWriteDr7 (0x20000480); + AsmWriteCr4 (Cr4 | BIT3); + // + // Do an IN from IO_PORT_BREAKPOINT_ADDRESS to generate a HW breakpoint until the port + // returns a read value other than DEBUG_AGENT_IMAGE_WAIT + // + do { + DebugAgentStatus = IoRead8 (IO_PORT_BREAKPOINT_ADDRESS); + } while (DebugAgentStatus == DEBUG_AGENT_IMAGE_WAIT); + + } else if (LoadImageMethod == DEBUG_LOAD_IMAGE_METHOD_SOFT_INT3) { + // + // Generate a software break point. + // + CpuBreakpoint (); + } + + // + // Restore Debug Register State only when Host didn't change it inside exception handler. + // E.g.: User halts the target and sets the HW breakpoint while target is + // in the above exception handler + // + NewDr7 = AsmReadDr7 () | BIT10; // H/w sets bit 10, some simulators don't + if (!IsDrxEnabled (0, NewDr7) && (AsmReadDr0 () == 0 || AsmReadDr0 () == Signature)) { + // + // If user changed Dr3 (by setting HW bp in the above exception handler, + // we will not set Dr0 to 0 in GO/STEP handler because the break cause is not IMAGE_LOAD/_UNLOAD. + // + AsmWriteDr0 (Dr0); + } + if (!IsDrxEnabled (1, NewDr7) && (AsmReadDr1 () == (UINTN) ImageContext->PdbPointer)) { + AsmWriteDr1 (Dr1); + } + if (!IsDrxEnabled (2, NewDr7) && (AsmReadDr2 () == (UINTN) ImageContext)) { + AsmWriteDr2 (Dr2); + } + if (!IsDrxEnabled (3, NewDr7) && (AsmReadDr3 () == IO_PORT_BREAKPOINT_ADDRESS)) { + AsmWriteDr3 (Dr3); + } + if (LoadImageMethod == DEBUG_LOAD_IMAGE_METHOD_IO_HW_BREAKPOINT) { + if (AsmReadCr4 () == (Cr4 | BIT3)) { + AsmWriteCr4 (Cr4); + } + if (NewDr7 == 0x20000480) { + AsmWriteDr7 (Dr7); + } + } else if (LoadImageMethod == DEBUG_LOAD_IMAGE_METHOD_SOFT_INT3) { + if (NewDr7 == BIT10) { + AsmWriteDr7 (Dr7); + } + } + // + // Restore original IDT entry for INT1 if it was hooked. + // + if (IdtEntryHooked) { + RestoreIdtEntry1 (&IdtDescriptor, &OriginalIdtEntry); + } + // + // Restore the interrupt state + // + SetInterruptState (InterruptState); +} + +/** + Performs additional actions after a PE/COFF image has been loaded and relocated. + + @param ImageContext Pointer to the image context structure that describes the + PE/COFF image that has already been loaded and relocated. + +**/ +VOID +EFIAPI +PeCoffLoaderRelocateImageExtraAction ( + IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext + ) +{ + PeCoffLoaderExtraActionCommon (ImageContext, IMAGE_LOAD_SIGNATURE); +} + +/** + Performs additional actions just before a PE/COFF image is unloaded. Any resources + that were allocated by PeCoffLoaderRelocateImageExtraAction() must be freed. + + @param ImageContext Pointer to the image context structure that describes the + PE/COFF image that is being unloaded. + +**/ +VOID +EFIAPI +PeCoffLoaderUnloadImageExtraAction ( + IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext + ) +{ + PeCoffLoaderExtraActionCommon (ImageContext, IMAGE_UNLOAD_SIGNATURE); +} diff --git a/Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/PeCoffExtraActionLib.h b/Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/PeCoffExtraActionLib.h new file mode 100644 index 0000000000..3f919287e8 --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/PeCoffExtraActionLib.h @@ -0,0 +1,79 @@ +/** @file + PE/Coff Extra Action library instances, it will report image debug info. + + Copyright (c) 2013, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _PE_COFF_EXTRA_ACTION_LIB_H_ +#define _PE_COFF_EXTRA_ACTION_LIB_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DEBUG_LOAD_IMAGE_METHOD_IO_HW_BREAKPOINT 1 +#define DEBUG_LOAD_IMAGE_METHOD_SOFT_INT3 2 + +#define IO_HW_BREAKPOINT_VECTOR_NUM 1 +#define SOFT_INT_VECTOR_NUM 3 + +extern UINTN AsmInterruptHandle; + +/** + Read IDT entry to check if IDT entries are setup by Debug Agent. + + @param[in] IdtDescriptor Pointer to IDT Descriptor. + @param[in] InterruptType Interrupt type. + + @retval TRUE IDT entries were setup by Debug Agent. + @retval FALSE IDT entries were not setuo by Debug Agent. + +**/ +BOOLEAN +CheckDebugAgentHandler ( + IN IA32_DESCRIPTOR *IdtDescriptor, + IN UINTN InterruptType + ); + +/** + Save IDT entry for INT1 and update it. + + @param[in] IdtDescriptor Pointer to IDT Descriptor. + @param[out] SavedIdtEntry Original IDT entry returned. + +**/ +VOID +SaveAndUpdateIdtEntry1 ( + IN IA32_DESCRIPTOR *IdtDescriptor, + OUT IA32_IDT_GATE_DESCRIPTOR *SavedIdtEntry + ); + +/** + Restore IDT entry for INT1. + + @param[in] IdtDescriptor Pointer to IDT Descriptor. + @param[in] RestoredIdtEntry IDT entry to be restored. + +**/ +VOID +RestoreIdtEntry1 ( + IN IA32_DESCRIPTOR *IdtDescriptor, + IN IA32_IDT_GATE_DESCRIPTOR *RestoredIdtEntry + ); + +#endif + diff --git a/Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/PeCoffExtraActionLib.uni b/Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/PeCoffExtraActionLib.uni new file mode 100644 index 0000000000..0b50e05fa7 --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/PeCoffExtraActionLib.uni @@ -0,0 +1,21 @@ +// /** @file +// PeCoffExtraAction Library to support source level debug. +// +// PeCoffExtraAction Library to support source level debug. +// +// Copyright (c) 2010 - 2014, Intel Corporation. All rights reserved.
+// +// This program and the accompanying materials +// are licensed and made available under the terms and conditions of the BSD License +// which accompanies this distribution. The full text of the license may be found at +// http://opensource.org/licenses/bsd-license.php. +// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "PeCoffExtraAction Library to support source level debug" + +#string STR_MODULE_DESCRIPTION #language en-US "PeCoffExtraAction Library to support source level debug." + diff --git a/Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/PeCoffExtraActionLibDebug.inf b/Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/PeCoffExtraActionLibDebug.inf new file mode 100644 index 0000000000..1206e31dd4 --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/PeCoffExtraActionLibDebug.inf @@ -0,0 +1,59 @@ +## @file +# PeCoffExtraAction Library to support source level debug. +# +# Copyright (c) 2010 - 2016, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php. +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = PeCoffExtraActionLib + MODULE_UNI_FILE = PeCoffExtraActionLib.uni + FILE_GUID = 8F01CBD5-E069-44d7-90C9-35F0318603AD + MODULE_TYPE = BASE + VERSION_STRING = 0.8 + LIBRARY_CLASS = PeCoffExtraActionLib + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources.common] + PeCoffExtraActionLib.h + PeCoffExtraActionLib.c + +[Sources.IA32] + Ia32/IntHandlerFuncs.c + Ia32/IntHandler.asm + Ia32/IntHandler.nasm + Ia32/IntHandler.S + +[Sources.X64] + X64/IntHandlerFuncs.c + X64/IntHandler.asm + X64/IntHandler.nasm + X64/IntHandler.S + +[Packages] + MdePkg/MdePkg.dec + SourceLevelDebugPkg/SourceLevelDebugPkg.dec + +[LibraryClasses] + BaseLib + DebugLib + IoLib + PcdLib + +[Pcd] + gEfiSourceLevelDebugPkgTokenSpaceGuid.PcdDebugLoadImageMethod ## CONSUMES + diff --git a/Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/X64/IntHandler.S b/Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/X64/IntHandler.S new file mode 100644 index 0000000000..108b40cf6d --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/X64/IntHandler.S @@ -0,0 +1,28 @@ +#------------------------------------------------------------------------------ +# +# Copyright (c) 2013, Intel Corporation. All rights reserved.
+# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php. +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# Module Name: +# +# IntHandler.S +# +# Abstract: +# +# Assembly interrupt handler function. +# +#------------------------------------------------------------------------------ + +ASM_GLOBAL ASM_PFX(AsmInterruptHandle) + +.text +ASM_PFX(AsmInterruptHandle): + cli + movb $1, %al + iretq diff --git a/Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/X64/IntHandler.asm b/Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/X64/IntHandler.asm new file mode 100644 index 0000000000..93f4d6f17f --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/X64/IntHandler.asm @@ -0,0 +1,29 @@ +;------------------------------------------------------------------------------ +; +; Copyright (c) 2013, Intel Corporation. All rights reserved.
+; This program and the accompanying materials +; are licensed and made available under the terms and conditions of the BSD License +; which accompanies this distribution. The full text of the license may be found at +; http://opensource.org/licenses/bsd-license.php. +; +; THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +; WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +; +; Module Name: +; +; IntHandler.asm +; +; Abstract: +; +; Assembly interrupt handler function. +; +;------------------------------------------------------------------------------ + +public AsmInterruptHandle + +.code +AsmInterruptHandle: + cli + mov al, 1 + iretq +END diff --git a/Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/X64/IntHandler.nasm b/Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/X64/IntHandler.nasm new file mode 100644 index 0000000000..e53805b3cc --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/X64/IntHandler.nasm @@ -0,0 +1,29 @@ +;------------------------------------------------------------------------------ +; +; Copyright (c) 2016, Intel Corporation. All rights reserved.
+; This program and the accompanying materials +; are licensed and made available under the terms and conditions of the BSD License +; which accompanies this distribution. The full text of the license may be found at +; http://opensource.org/licenses/bsd-license.php. +; +; THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +; WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +; +; Module Name: +; +; IntHandler.nasm +; +; Abstract: +; +; Assembly interrupt handler function. +; +;------------------------------------------------------------------------------ + +global ASM_PFX(AsmInterruptHandle) + +DEFAULT REL +SECTION .text +ASM_PFX(AsmInterruptHandle): + cli + mov al, 1 + iretq diff --git a/Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/X64/IntHandlerFuncs.c b/Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/X64/IntHandlerFuncs.c new file mode 100644 index 0000000000..5431187d06 --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/X64/IntHandlerFuncs.c @@ -0,0 +1,101 @@ +/** @file + X64 arch function to access IDT vector. + + Copyright (c) 2013, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include + +/** + Read IDT entry to check if IDT entries are setup by Debug Agent. + + @param[in] IdtDescriptor Pointer to IDT Descriptor. + @param[in] InterruptType Interrupt type. + + @retval TRUE IDT entries were setup by Debug Agent. + @retval FALSE IDT entries were not setuo by Debug Agent. + +**/ +BOOLEAN +CheckDebugAgentHandler ( + IN IA32_DESCRIPTOR *IdtDescriptor, + IN UINTN InterruptType + ) +{ + IA32_IDT_GATE_DESCRIPTOR *IdtEntry; + UINTN InterruptHandler; + + IdtEntry = (IA32_IDT_GATE_DESCRIPTOR *) IdtDescriptor->Base; + if (IdtEntry == NULL) { + return FALSE; + } + + InterruptHandler = IdtEntry[InterruptType].Bits.OffsetLow + + (((UINTN)IdtEntry[InterruptType].Bits.OffsetHigh) << 16) + + (((UINTN)IdtEntry[InterruptType].Bits.OffsetUpper) << 32); + if (InterruptHandler >= sizeof (UINT32) && *(UINT32 *)(InterruptHandler - sizeof (UINT32)) == AGENT_HANDLER_SIGNATURE) { + return TRUE; + } else { + return FALSE; + } +} + +/** + Save IDT entry for INT1 and update it. + + @param[in] IdtDescriptor Pointer to IDT Descriptor. + @param[out] SavedIdtEntry Original IDT entry returned. + +**/ +VOID +SaveAndUpdateIdtEntry1 ( + IN IA32_DESCRIPTOR *IdtDescriptor, + OUT IA32_IDT_GATE_DESCRIPTOR *SavedIdtEntry + ) +{ + IA32_IDT_GATE_DESCRIPTOR *IdtEntry; + UINT16 CodeSegment; + UINTN InterruptHandler; + + IdtEntry = (IA32_IDT_GATE_DESCRIPTOR *) IdtDescriptor->Base; + CopyMem (SavedIdtEntry, &IdtEntry[1], sizeof (IA32_IDT_GATE_DESCRIPTOR)); + + // + // Use current CS as the segment selector of interrupt gate in IDT + // + CodeSegment = AsmReadCs (); + + InterruptHandler = (UINTN) &AsmInterruptHandle; + IdtEntry[1].Bits.OffsetLow = (UINT16)(UINTN)InterruptHandler; + IdtEntry[1].Bits.OffsetHigh = (UINT16)((UINTN)InterruptHandler >> 16); + IdtEntry[1].Bits.OffsetUpper = (UINT32)((UINTN)InterruptHandler >> 32); + IdtEntry[1].Bits.Selector = CodeSegment; + IdtEntry[1].Bits.GateType = IA32_IDT_GATE_TYPE_INTERRUPT_32; +} + +/** + Restore IDT entry for INT1. + + @param[in] IdtDescriptor Pointer to IDT Descriptor. + @param[in] RestoredIdtEntry IDT entry to be restored. + +**/ +VOID +RestoreIdtEntry1 ( + IN IA32_DESCRIPTOR *IdtDescriptor, + IN IA32_IDT_GATE_DESCRIPTOR *RestoredIdtEntry + ) +{ + IA32_IDT_GATE_DESCRIPTOR *IdtEntry; + + IdtEntry = (IA32_IDT_GATE_DESCRIPTOR *) IdtDescriptor->Base; + CopyMem (&IdtEntry[1], RestoredIdtEntry, sizeof (IA32_IDT_GATE_DESCRIPTOR)); +} -- cgit v1.2.3