/** @file
Usb3 Debug Port library instance
Copyright (c) 2013 - 2019, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "Usb3DebugPortLibInternal.h"
extern EFI_SMRAM_DESCRIPTOR mSmramCheckRanges[MAX_SMRAM_RANGE];
extern UINTN mSmramCheckRangeCount;
extern BOOLEAN mUsb3InSmm;
extern UINT64 mUsb3MmioSize;
extern BOOLEAN mUsb3GetCapSuccess;
GUID gUsb3DbgGuid = USB3_DBG_GUID;
USB3_DEBUG_PORT_CONTROLLER mUsb3DebugPort;
USB3_DEBUG_PORT_INSTANCE *mUsb3Instance = NULL;
/**
Return XHCI MMIO base address.
**/
EFI_PHYSICAL_ADDRESS
GetXhciBaseAddress (
VOID
)
{
UINT8 Bus;
UINT8 Device;
UINT8 Function;
EFI_PHYSICAL_ADDRESS Address;
UINT32 Low;
UINT32 High;
if (mUsb3DebugPort.Controller == 0) {
mUsb3DebugPort.Controller = GetUsb3DebugPortController();
}
Bus = mUsb3DebugPort.PciAddress.Bus;
Device = mUsb3DebugPort.PciAddress.Device;
Function = mUsb3DebugPort.PciAddress.Function;
Low = PciRead32 (PCI_LIB_ADDRESS(Bus, Device, Function, PCI_BASE_ADDRESSREG_OFFSET));
High = PciRead32 (PCI_LIB_ADDRESS(Bus, Device, Function, PCI_BASE_ADDRESSREG_OFFSET + 4));
Address = (EFI_PHYSICAL_ADDRESS) (LShiftU64 ((UINT64) High, 32) | Low);
//
// Mask other parts which are not part of base address
//
Address &= XHCI_BASE_ADDRESS_64_BIT_MASK;
return Address;
}
/**
Return XHCI debug instance address.
**/
USB3_DEBUG_PORT_INSTANCE *
GetUsb3DebugPortInstance (
VOID
)
{
USB3_DEBUG_PORT_INSTANCE *Instance;
EFI_PHYSICAL_ADDRESS XhcMmioBase;
UINT64 CapabilityPointer;
UINT32 Capability;
BOOLEAN Flag;
UINT8 Bus;
UINT8 Device;
UINT8 Function;
UINT16 Command;
USB3_DEBUG_PORT_CONTROLLER UsbDebugPort;
Instance = NULL;
XhcMmioBase = GetXhciBaseAddress ();
if ((XhcMmioBase == 0) || (XhcMmioBase == XHCI_BASE_ADDRESS_64_BIT_MASK)) {
return NULL;
}
if (mUsb3Instance != NULL) {
FixUsb3InstanceResource (mUsb3Instance, XhcMmioBase);
return mUsb3Instance;
}
Command = GetXhciPciCommand ();
UsbDebugPort.Controller = GetUsb3DebugPortController();
Bus = UsbDebugPort.PciAddress.Bus;
Device = UsbDebugPort.PciAddress.Device;
Function = UsbDebugPort.PciAddress.Function;
//
// Set Command Register
//
if ((Command & EFI_PCI_COMMAND_MEMORY_SPACE) == 0) {
PciWrite16(PCI_LIB_ADDRESS(Bus, Device, Function, PCI_COMMAND_OFFSET), Command | EFI_PCI_COMMAND_MEMORY_SPACE);
PciRead16(PCI_LIB_ADDRESS(Bus, Device, Function, PCI_COMMAND_OFFSET));
}
//
// Calculate capability offset from HCCPARAMS [16:31], in 32-bit words
//
CapabilityPointer = XhcMmioBase + (MmioRead32 ((UINTN)(XhcMmioBase + 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 list, quit
//
break;
}
CapabilityPointer += ((Capability & XHC_NEXT_CAPABILITY_MASK) >> 8) * 4;
Capability = MmioRead32 ((UINTN)CapabilityPointer);
}
if (Flag) {
Instance = (USB3_DEBUG_PORT_INSTANCE *)(UINTN) MmioRead32 ((UINTN) (CapabilityPointer + XHC_DC_DCDDI2));
if (Instance != NULL) {
FixUsb3InstanceResource (Instance, XhcMmioBase);
}
}
//
// Restore Command Register
//
PciWrite16(PCI_LIB_ADDRESS (Bus, Device, Function, PCI_COMMAND_OFFSET), Command);
return Instance;
}
/**
Initialize USB3 debug port.
This method invokes various internal functions to facilitate
detection and initialization of USB3 debug port.
@retval RETURN_SUCCESS The USB3 debug port was initialized.
**/
RETURN_STATUS
EFIAPI
USB3Initialize (
VOID
)
{
//
// Leave it empty, we assume PEI phase already do initialization
//
return RETURN_SUCCESS;
}
/**
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
)
{
USB3_DEBUG_PORT_INSTANCE UsbDbg;
USB3_DEBUG_PORT_INSTANCE *Instance;
EFI_PHYSICAL_ADDRESS Address;
EFI_STATUS Status;
if ((gST == NULL) || (gBS == NULL)) {
//
// gST and gBS have not been initialized yet
//
return EFI_DEVICE_ERROR;
}
Status = EfiGetSystemConfigurationTable (&gUsb3DbgGuid, (VOID **) &mUsb3Instance);
if (!EFI_ERROR (Status)) {
return RETURN_SUCCESS;
}
//
// It is first time to run DXE instance, copy Instance from Hob to ACPINvs
// NOTE: Hob is not ready at this time, so copy it from XHCI register.
//
Instance = GetUsb3DebugPortInstance ();
if (Instance == NULL) {
//
// Initialize USB debug
//
SetMem (&UsbDbg, sizeof(UsbDbg), 0);
DiscoverUsb3DebugPort (&UsbDbg);
if (UsbDbg.DebugSupport) {
InitializeUsb3DebugPort (&UsbDbg);
}
Instance = &UsbDbg;
}
Address = SIZE_4GB;
Status = gBS->AllocatePages (
AllocateMaxAddress,
EfiACPIMemoryNVS,
EFI_SIZE_TO_PAGES (sizeof (USB3_DEBUG_PORT_INSTANCE)),
&Address
);
if (EFI_ERROR (Status)) {
return Status;
}
CopyMem (
(VOID *)(UINTN)Address,
Instance,
sizeof (USB3_DEBUG_PORT_INSTANCE)
);
mUsb3Instance = (USB3_DEBUG_PORT_INSTANCE *)(UINTN)Address;
Status = gBS->InstallConfigurationTable (&gUsb3DbgGuid, mUsb3Instance);
if (EFI_ERROR (Status)) {
return Status;
}
if (mUsb3Instance->DebugSupport) {
SaveUsb3InstanceAddress (mUsb3Instance);
}
return RETURN_SUCCESS;
}
/**
Calculate the size of XHCI MMIO space.
@retval TURE The XHCI MMIO is in SMRAM ranges.
@retval FALSE The XHCI MMIO is out of SMRAM ranges.
**/
UINT64
CalculateMmioSize (
VOID
)
{
UINT8 Bus;
UINT8 Device;
UINT8 Function;
UINT32 Value;
UINT32 Mask;
UINT64 MmioSize;
UINT16 Command;
USB3_DEBUG_PORT_CONTROLLER UsbDebugPort;
EFI_PHYSICAL_ADDRESS XhcMmioBase;
UsbDebugPort.Controller = GetUsb3DebugPortController();
Bus = UsbDebugPort.PciAddress.Bus;
Device = UsbDebugPort.PciAddress.Device;
Function = UsbDebugPort.PciAddress.Function;
Mask = 0xFFFFFFF0;
MmioSize = 0;
XhcMmioBase = GetXhciBaseAddress ();
//
// Disable MSE
//
Command = PciRead16 (PCI_LIB_ADDRESS(Bus, Device, Function, PCI_COMMAND_OFFSET));
PciWrite16 (PCI_LIB_ADDRESS (Bus, Device, Function, PCI_COMMAND_OFFSET), Command & ~(EFI_PCI_COMMAND_MEMORY_SPACE));
//
// Get Mmio Size
//
PciWrite32 (PCI_LIB_ADDRESS(Bus, Device, Function, PCI_BASE_ADDRESSREG_OFFSET), 0xFFFFFFFF);
Value = PciRead32 (PCI_LIB_ADDRESS(Bus, Device, Function, PCI_BASE_ADDRESSREG_OFFSET));
switch (Value & 0x07) {
case 0x0:
//
// Memory space: anywhere in 32 bit address space
//
MmioSize = (~(Value & Mask)) + 1;
break;
case 0x4:
//
// Memory space: anywhere in 64 bit address space
//
MmioSize = Value & Mask;
PciWrite32 (PCI_LIB_ADDRESS(Bus, Device, Function, PCI_BASE_ADDRESSREG_OFFSET + 4), 0xFFFFFFFF);
Value = PciRead32 (PCI_LIB_ADDRESS(Bus, Device, Function, PCI_BASE_ADDRESSREG_OFFSET + 4));
//
// Fix the length to support some spefic 64 bit BAR
//
Value |= ((UINT32)(-1) << HighBitSet32 (Value));
//
// Calculate the size of 64bit bar
//
MmioSize |= LShiftU64 ((UINT64) Value, 32);
MmioSize = (~(MmioSize)) + 1;
break;
default:
//
// Unknown BAR type
//
MmioSize = (~(Value & Mask)) + 1;
break;
};
//
// Restore MMIO address
//
PciWrite32 (PCI_LIB_ADDRESS(Bus, Device, Function, PCI_BASE_ADDRESSREG_OFFSET), (UINT32)XhcMmioBase);
PciWrite32 (PCI_LIB_ADDRESS(Bus, Device, Function, PCI_BASE_ADDRESSREG_OFFSET + 4), (UINT32) (XhcMmioBase >> 32));
PciWrite16 (PCI_LIB_ADDRESS (Bus, Device, Function, PCI_COMMAND_OFFSET), Command | EFI_PCI_COMMAND_MEMORY_SPACE);
return MmioSize;
}
/**
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
Usb3DebugPortLibDxeConstructor (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_SMM_BASE2_PROTOCOL *SmmBase;
EFI_SMM_ACCESS2_PROTOCOL *SmmAccess;
UINTN Size;
EFI_STATUS Status;
//
// Do real initialization here, because we need copy data from Hob to ACPINvs.
// We must do it in constructor because it depends on UefiBootServicesTableLib.
//
if (FeaturePcdGet (PcdUsb3DebugFeatureEnable)) {
USB3InitializeReal ();
}
mUsb3MmioSize = CalculateMmioSize ();
if (gBS != NULL) {
SmmBase = NULL;
Status = gBS->LocateProtocol (&gEfiSmmBase2ProtocolGuid, NULL, (VOID **)&SmmBase);
if (!EFI_ERROR (Status)) {
SmmBase->InSmm(SmmBase, &mUsb3InSmm);
}
if (mUsb3InSmm) {
//
// Get SMRAM information
//
SmmAccess = NULL;
Status = gBS->LocateProtocol (&gEfiSmmAccess2ProtocolGuid, NULL, (VOID **)&SmmAccess);
if (!EFI_ERROR (Status)) {
Size = sizeof (mSmramCheckRanges);
Status = SmmAccess->GetCapabilities (SmmAccess, &Size, mSmramCheckRanges);
if (!EFI_ERROR (Status)) {
mSmramCheckRangeCount = Size / sizeof (EFI_SMRAM_DESCRIPTOR);
}
}
}
}
return EFI_SUCCESS;
}
/**
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;
if (gBS != NULL) {
Address = 0xFFFFFFFF;
Status = gBS->AllocatePages (
AllocateMaxAddress,
EfiACPIMemoryNVS,
EFI_SIZE_TO_PAGES (BufferSize),
&Address
);
if (!EFI_ERROR (Status)) {
Buf = (VOID *)(UINTN)Address;
}
}
return Buf;
}
/**
Check whether AllocatePages in permanent memory is ready.
@retval TRUE AllocatePages in permanent memory is ready.
@retval FALSE AllocatePages in permanent memory is not ready.
**/
BOOLEAN
IsAllocatePagesReady (
VOID
)
{
if (gBS != NULL) {
return TRUE;
}
return FALSE;
}