/** @file | |
Debug Port Library implementation based on usb3 debug port. | |
Copyright (c) 2014 - 2018, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include <Base.h> | |
#include <PiDxe.h> | |
#include <Library/UefiBootServicesTableLib.h> | |
#include <Library/HobLib.h> | |
#include <Protocol/PciIo.h> | |
#include <Protocol/IoMmu.h> | |
#include <Protocol/DxeSmmReadyToLock.h> | |
#include "DebugCommunicationLibUsb3Internal.h" | |
GUID gUsb3DbgGuid = USB3_DBG_GUID; | |
USB3_DEBUG_PORT_HANDLE mUsb3Instance = { USB3DBG_UNINITIALIZED }; | |
EFI_PHYSICAL_ADDRESS mUsb3InstanceAddr = 0; | |
EFI_PHYSICAL_ADDRESS *mUsb3InstanceAddrPtr = NULL; | |
EFI_PCI_IO_PROTOCOL *mUsb3PciIo = NULL; | |
/** | |
Creates a named event that can be signaled. | |
This function creates an event using NotifyTpl, NotifyFunction. | |
If Name is NULL, then ASSERT(). | |
If NotifyTpl is not a legal TPL value, then ASSERT(). | |
If NotifyFunction is NULL, then ASSERT(). | |
@param Name Supplies the GUID name of the event. | |
@param NotifyTpl Supplies the task priority level of the event notifications. | |
@param NotifyFunction Supplies the function to notify when the event is signaled. | |
@param Event A pointer to the event created. | |
@retval EFI_SUCCESS A named event was created. | |
@retval EFI_OUT_OF_RESOURCES There are not enough resource to create the named event. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
Usb3NamedEventListen ( | |
IN CONST EFI_GUID *Name, | |
IN EFI_TPL NotifyTpl, | |
IN EFI_EVENT_NOTIFY NotifyFunction, | |
IN EFI_EVENT *Event | |
) | |
{ | |
EFI_STATUS Status; | |
VOID *RegistrationLocal; | |
ASSERT (Name != NULL); | |
ASSERT (NotifyFunction != NULL); | |
ASSERT (NotifyTpl <= TPL_HIGH_LEVEL); | |
// | |
// Create event | |
// | |
Status = gBS->CreateEvent ( | |
EVT_NOTIFY_SIGNAL, | |
NotifyTpl, | |
NotifyFunction, | |
NULL, | |
Event | |
); | |
ASSERT_EFI_ERROR (Status); | |
// | |
// Register for an installation of protocol interface | |
// | |
Status = gBS->RegisterProtocolNotify ( | |
(EFI_GUID *)Name, | |
*Event, | |
&RegistrationLocal | |
); | |
ASSERT_EFI_ERROR (Status); | |
return Status; | |
} | |
/** | |
USB3 map one DMA buffer. | |
@param PciIo Pointer to PciIo for USB3 debug port. | |
@param Address DMA buffer address to be mapped. | |
@param NumberOfBytes Number of bytes to be mapped. | |
**/ | |
VOID | |
Usb3MapOneDmaBuffer ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN EFI_PHYSICAL_ADDRESS Address, | |
IN UINTN NumberOfBytes | |
) | |
{ | |
EFI_STATUS Status; | |
VOID *HostAddress; | |
EFI_PHYSICAL_ADDRESS DeviceAddress; | |
VOID *Mapping; | |
HostAddress = (VOID *)(UINTN)Address; | |
Status = PciIo->Map ( | |
PciIo, | |
EfiPciIoOperationBusMasterCommonBuffer, | |
HostAddress, | |
&NumberOfBytes, | |
&DeviceAddress, | |
&Mapping | |
); | |
ASSERT_EFI_ERROR (Status); | |
ASSERT (DeviceAddress == ((EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress)); | |
} | |
/** | |
USB3 map DMA buffers. | |
@param Instance Pointer to USB3 debug port instance. | |
@param PciIo Pointer to PciIo for USB3 debug port. | |
**/ | |
VOID | |
Usb3MapDmaBuffers ( | |
IN USB3_DEBUG_PORT_HANDLE *Instance, | |
IN EFI_PCI_IO_PROTOCOL *PciIo | |
) | |
{ | |
Usb3MapOneDmaBuffer ( | |
PciIo, | |
Instance->UrbIn.Data, | |
XHCI_DEBUG_DEVICE_MAX_PACKET_SIZE * 2 + USB3_DEBUG_PORT_WRITE_MAX_PACKET_SIZE | |
); | |
Usb3MapOneDmaBuffer ( | |
PciIo, | |
Instance->TransferRingIn.RingSeg0, | |
sizeof (TRB_TEMPLATE) * TR_RING_TRB_NUMBER | |
); | |
Usb3MapOneDmaBuffer ( | |
PciIo, | |
Instance->TransferRingOut.RingSeg0, | |
sizeof (TRB_TEMPLATE) * TR_RING_TRB_NUMBER | |
); | |
Usb3MapOneDmaBuffer ( | |
PciIo, | |
Instance->EventRing.EventRingSeg0, | |
sizeof (TRB_TEMPLATE) * EVENT_RING_TRB_NUMBER | |
); | |
Usb3MapOneDmaBuffer ( | |
PciIo, | |
Instance->EventRing.ERSTBase, | |
sizeof (EVENT_RING_SEG_TABLE_ENTRY) * ERST_NUMBER | |
); | |
Usb3MapOneDmaBuffer ( | |
PciIo, | |
Instance->DebugCapabilityContext, | |
sizeof (XHC_DC_CONTEXT) | |
); | |
Usb3MapOneDmaBuffer ( | |
PciIo, | |
((XHC_DC_CONTEXT *)(UINTN)Instance->DebugCapabilityContext)->DbcInfoContext.String0DescAddress, | |
STRING0_DESC_LEN + MANU_DESC_LEN + PRODUCT_DESC_LEN + SERIAL_DESC_LEN | |
); | |
} | |
/** | |
Invoke a notification event | |
@param[in] Event Event whose notification function is being invoked. | |
@param[in] Context The pointer to the notification function's context, | |
which is implementation-dependent. | |
**/ | |
VOID | |
EFIAPI | |
Usb3DxeSmmReadyToLockNotify ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
) | |
{ | |
USB3_DEBUG_PORT_HANDLE *Instance; | |
DEBUG ((DEBUG_INFO, "%a()\n", __func__)); | |
Instance = GetUsb3DebugPortInstance (); | |
ASSERT (Instance != NULL); | |
Instance->InNotify = TRUE; | |
// | |
// For the case that the USB3 debug port instance and DMA buffers are | |
// from PEI HOB with IOMMU enabled. | |
// Reinitialize USB3 debug port with granted DXE DMA buffer accessible | |
// by SMM environment. | |
// | |
InitializeUsbDebugHardware (Instance); | |
// | |
// Wait some time for host to be ready after re-initialization. | |
// | |
MicroSecondDelay (1000000); | |
Instance->InNotify = FALSE; | |
gBS->CloseEvent (Event); | |
} | |
/** | |
USB3 get IOMMU protocol. | |
@return Pointer to IOMMU protocol. | |
**/ | |
EDKII_IOMMU_PROTOCOL * | |
Usb3GetIoMmu ( | |
VOID | |
) | |
{ | |
EFI_STATUS Status; | |
EDKII_IOMMU_PROTOCOL *IoMmu; | |
IoMmu = NULL; | |
Status = gBS->LocateProtocol ( | |
&gEdkiiIoMmuProtocolGuid, | |
NULL, | |
(VOID **)&IoMmu | |
); | |
if (!EFI_ERROR (Status) && (IoMmu != NULL)) { | |
return IoMmu; | |
} | |
return NULL; | |
} | |
/** | |
Invoke a notification event | |
@param[in] Event Event whose notification function is being invoked. | |
@param[in] Context The pointer to the notification function's context, | |
which is implementation-dependent. | |
**/ | |
VOID | |
EFIAPI | |
Usb3PciIoNotify ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
) | |
{ | |
EFI_STATUS Status; | |
UINTN PciIoHandleCount; | |
EFI_HANDLE *PciIoHandleBuffer; | |
UINTN Index; | |
EFI_PCI_IO_PROTOCOL *PciIo; | |
UINTN PciSegment; | |
UINTN PciBusNumber; | |
UINTN PciDeviceNumber; | |
UINTN PciFunctionNumber; | |
UINT32 PciAddress; | |
USB3_DEBUG_PORT_HANDLE *Instance; | |
EFI_EVENT SmmReadyToLockEvent; | |
Status = gBS->LocateHandleBuffer ( | |
ByProtocol, | |
&gEfiPciIoProtocolGuid, | |
NULL, | |
&PciIoHandleCount, | |
&PciIoHandleBuffer | |
); | |
if (!EFI_ERROR (Status) && | |
(PciIoHandleBuffer != NULL) && | |
(PciIoHandleCount != 0)) | |
{ | |
for (Index = 0; Index < PciIoHandleCount; Index++) { | |
Status = gBS->HandleProtocol ( | |
PciIoHandleBuffer[Index], | |
&gEfiPciIoProtocolGuid, | |
(VOID **)&PciIo | |
); | |
ASSERT_EFI_ERROR (Status); | |
Status = PciIo->GetLocation (PciIo, &PciSegment, &PciBusNumber, &PciDeviceNumber, &PciFunctionNumber); | |
ASSERT_EFI_ERROR (Status); | |
PciAddress = (UINT32)((PciBusNumber << 20) | (PciDeviceNumber << 15) | (PciFunctionNumber << 12)); | |
if (PciAddress == PcdGet32 (PcdUsbXhciPciAddress)) { | |
// | |
// Found the PciIo for USB3 debug port. | |
// | |
DEBUG ((DEBUG_INFO, "%a()\n", __func__)); | |
if (Usb3GetIoMmu () != NULL) { | |
Instance = GetUsb3DebugPortInstance (); | |
ASSERT (Instance != NULL); | |
if (Instance->Ready) { | |
Instance->InNotify = TRUE; | |
Usb3MapDmaBuffers (Instance, PciIo); | |
Instance->InNotify = FALSE; | |
if (Instance->FromHob) { | |
mUsb3PciIo = PciIo; | |
Usb3NamedEventListen ( | |
&gEfiDxeSmmReadyToLockProtocolGuid, | |
TPL_NOTIFY, | |
Usb3DxeSmmReadyToLockNotify, | |
&SmmReadyToLockEvent | |
); | |
} | |
} | |
} | |
gBS->CloseEvent (Event); | |
break; | |
} | |
} | |
gBS->FreePool (PciIoHandleBuffer); | |
} | |
} | |
/** | |
Return USB3 debug instance address pointer. | |
**/ | |
EFI_PHYSICAL_ADDRESS * | |
GetUsb3DebugPortInstanceAddrPtr ( | |
VOID | |
) | |
{ | |
if (mUsb3InstanceAddrPtr == NULL) { | |
// | |
// Use the local variables temporarily. | |
// | |
mUsb3InstanceAddr = (EFI_PHYSICAL_ADDRESS)(UINTN)&mUsb3Instance; | |
mUsb3InstanceAddrPtr = &mUsb3InstanceAddr; | |
} | |
return mUsb3InstanceAddrPtr; | |
} | |
/** | |
Allocates pages that are suitable for an OperationBusMasterCommonBuffer or | |
OperationBusMasterCommonBuffer64 mapping. | |
@param PciIo Pointer to PciIo for USB3 debug port. | |
@param Pages The number of pages to allocate. | |
@param Address A pointer to store the base system memory address of the | |
allocated range. | |
@retval EFI_SUCCESS The requested memory pages were allocated. | |
@retval EFI_UNSUPPORTED Attributes is unsupported. The only legal attribute bits are | |
MEMORY_WRITE_COMBINE and MEMORY_CACHED. | |
@retval EFI_INVALID_PARAMETER One or more parameters are invalid. | |
@retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated. | |
**/ | |
EFI_STATUS | |
Usb3AllocateDmaBuffer ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINTN Pages, | |
OUT VOID **Address | |
) | |
{ | |
EFI_STATUS Status; | |
*Address = NULL; | |
Status = PciIo->AllocateBuffer ( | |
PciIo, | |
AllocateAnyPages, | |
EfiRuntimeServicesData, | |
Pages, | |
Address, | |
0 | |
); | |
if (!EFI_ERROR (Status)) { | |
Usb3MapOneDmaBuffer ( | |
PciIo, | |
(EFI_PHYSICAL_ADDRESS)(UINTN)*Address, | |
EFI_PAGES_TO_SIZE (Pages) | |
); | |
} | |
return Status; | |
} | |
/** | |
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) { | |
if (mUsb3PciIo != NULL) { | |
Usb3AllocateDmaBuffer ( | |
mUsb3PciIo, | |
EFI_SIZE_TO_PAGES (BufferSize), | |
&Buf | |
); | |
} else { | |
TmpAddr = 0xFFFFFFFF; | |
Status = gBS->AllocatePages ( | |
AllocateMaxAddress, | |
EfiACPIMemoryNVS, | |
EFI_SIZE_TO_PAGES (BufferSize), | |
&TmpAddr | |
); | |
if (!EFI_ERROR (Status)) { | |
Buf = (VOID *)(UINTN)TmpAddr; | |
} | |
} | |
} | |
return Buf; | |
} | |
/** | |
The constructor function initialize USB3 debug port. | |
@param ImageHandle The firmware allocated handle for the EFI image. | |
@param SystemTable A pointer to the EFI System Table. | |
@retval EFI_SUCCESS The constructor always returns EFI_SUCCESS. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
DebugCommunicationUsb3DxeConstructor ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
EFI_PHYSICAL_ADDRESS *AddrPtr; | |
USB3_DEBUG_PORT_HANDLE *Instance; | |
EFI_PHYSICAL_ADDRESS Address; | |
EFI_STATUS Status; | |
EFI_EVENT Event; | |
Status = EfiGetSystemConfigurationTable (&gUsb3DbgGuid, (VOID **)&AddrPtr); | |
if (EFI_ERROR (Status) || (AddrPtr == NULL)) { | |
// | |
// Instead of using local variables, install system configuration table for | |
// the local instance and the buffer to save instance address pointer. | |
// | |
Address = SIZE_4GB; | |
Status = gBS->AllocatePages ( | |
AllocateMaxAddress, | |
EfiACPIMemoryNVS, | |
EFI_SIZE_TO_PAGES (sizeof (EFI_PHYSICAL_ADDRESS) + sizeof (USB3_DEBUG_PORT_HANDLE)), | |
&Address | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
AddrPtr = (EFI_PHYSICAL_ADDRESS *)(UINTN)Address; | |
ZeroMem (AddrPtr, sizeof (EFI_PHYSICAL_ADDRESS) + sizeof (USB3_DEBUG_PORT_HANDLE)); | |
Instance = (USB3_DEBUG_PORT_HANDLE *)(AddrPtr + 1); | |
CopyMem (Instance, &mUsb3Instance, sizeof (USB3_DEBUG_PORT_HANDLE)); | |
*AddrPtr = (EFI_PHYSICAL_ADDRESS)(UINTN)Instance; | |
Status = gBS->InstallConfigurationTable (&gUsb3DbgGuid, AddrPtr); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
} | |
if (mUsb3InstanceAddrPtr != NULL) { | |
*AddrPtr = *mUsb3InstanceAddrPtr; | |
} | |
mUsb3InstanceAddrPtr = AddrPtr; | |
Instance = GetUsb3DebugPortInstance (); | |
ASSERT (Instance != NULL); | |
if (Instance->PciIoEvent == 0) { | |
Status = Usb3NamedEventListen ( | |
&gEfiPciIoProtocolGuid, | |
TPL_NOTIFY, | |
Usb3PciIoNotify, | |
&Event | |
); | |
if (!EFI_ERROR (Status)) { | |
Instance->PciIoEvent = (EFI_PHYSICAL_ADDRESS)(UINTN)Event; | |
} | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
The destructor function. | |
@param ImageHandle The firmware allocated handle for the EFI image. | |
@param SystemTable A pointer to the EFI System Table. | |
@retval EFI_SUCCESS The destructor always returns EFI_SUCCESS. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
DebugCommunicationUsb3DxeDestructor ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
USB3_DEBUG_PORT_HANDLE *Instance; | |
Instance = GetUsb3DebugPortInstance (); | |
ASSERT (Instance != NULL); | |
if (Instance->PciIoEvent != 0) { | |
// | |
// Close the event created. | |
// | |
gBS->CloseEvent ((EFI_EVENT)(UINTN)Instance->PciIoEvent); | |
Instance->PciIoEvent = 0; | |
} | |
return EFI_SUCCESS; | |
} |