diff options
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/SourceLevelDebugPkg/Library/DebugAgent/DxeDebugAgent')
3 files changed, 1386 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/SourceLevelDebugPkg/Library/DebugAgent/DxeDebugAgent/DxeDebugAgentLib.c b/src/VBox/Devices/EFI/Firmware/SourceLevelDebugPkg/Library/DebugAgent/DxeDebugAgent/DxeDebugAgentLib.c new file mode 100644 index 00000000..a298b284 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/SourceLevelDebugPkg/Library/DebugAgent/DxeDebugAgent/DxeDebugAgentLib.c @@ -0,0 +1,544 @@ +/** @file + Debug Agent library implementation for Dxe Core and Dxr modules. + + Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#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/src/VBox/Devices/EFI/Firmware/SourceLevelDebugPkg/Library/DebugAgent/DxeDebugAgent/DxeDebugAgentLib.h b/src/VBox/Devices/EFI/Firmware/SourceLevelDebugPkg/Library/DebugAgent/DxeDebugAgent/DxeDebugAgentLib.h new file mode 100644 index 00000000..8d1d917a --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/SourceLevelDebugPkg/Library/DebugAgent/DxeDebugAgent/DxeDebugAgentLib.h @@ -0,0 +1,34 @@ +/** @file + Header file for Dxe Core Debug Agent Library instance. + + Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _DXE_CORE_DEBUG_AGENT_LIB_H_ +#define _DXE_CORE_DEBUG_AGENT_LIB_H_ + +#include <PiDxe.h> + +#include <Protocol/SerialIo.h> +#include <Protocol/DevicePath.h> +#include <Protocol/PiPcd.h> + +#include <Library/UefiBootServicesTableLib.h> +#include <Library/UefiLib.h> +#include <Library/DevicePathLib.h> +#include <Library/MemoryAllocationLib.h> + +#include "DebugAgent.h" + +/** + Install EFI Serial IO protocol based on Debug Communication Library. + +**/ +VOID +InstallSerialIo ( + VOID + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/SourceLevelDebugPkg/Library/DebugAgent/DxeDebugAgent/SerialIo.c b/src/VBox/Devices/EFI/Firmware/SourceLevelDebugPkg/Library/DebugAgent/DxeDebugAgent/SerialIo.c new file mode 100644 index 00000000..b5176ac9 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/SourceLevelDebugPkg/Library/DebugAgent/DxeDebugAgent/SerialIo.c @@ -0,0 +1,808 @@ +/** @file + Install Serial IO Protocol that layers on top of a Debug Communication Library instance. + + Copyright (c) 2012 - 2018, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "DxeDebugAgentLib.h" + +// +// Serial I/O Protocol Interface definitions. +// + +/** + 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 Variables +// +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 device 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); + } +} |