/** @file
Usb 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"
GUID gUsb3DbgGuid = USB3_DBG_GUID;
/**
USB3 IOMMU PPI notify.
@param[in] PeiServices Pointer to PEI Services Table.
@param[in] NotifyDesc Pointer to the descriptor for the Notification event that
caused this function to execute.
@param[in] Ppi Pointer to the PPI data associated with this function.
@retval EFI_STATUS Always return EFI_SUCCESS
**/
EFI_STATUS
EFIAPI
Usb3IoMmuPpiNotify (
IN EFI_PEI_SERVICES **PeiServices,
IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDesc,
IN VOID *Ppi
)
{
USB3_DEBUG_PORT_INSTANCE *Instance;
Instance = GetUsb3DebugPortInstance ();
ASSERT (Instance != NULL);
//
// Reinitialize USB3 debug port with granted DMA buffer from IOMMU PPI.
//
InitializeUsb3DebugPort (Instance);
SaveUsb3InstanceAddress (Instance);
return EFI_SUCCESS;
}
EFI_PEI_NOTIFY_DESCRIPTOR mUsb3IoMmuPpiNotifyDesc = {
(EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
&gEdkiiIoMmuPpiGuid,
Usb3IoMmuPpiNotify
};
/**
Allocates pages that are suitable for an OperationBusMasterCommonBuffer or
OperationBusMasterCommonBuffer64 mapping.
@param IoMmu Pointer to IOMMU PPI.
@param Pages The number of pages to allocate.
@param HostAddress A pointer to store the base system memory address of the
allocated range.
@param DeviceAddress The resulting map address for the bus master PCI controller to use to
access the hosts HostAddress.
@param Mapping A resulting value to pass to Unmap().
@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
IoMmuAllocateBuffer (
IN EDKII_IOMMU_PPI *IoMmu,
IN UINTN Pages,
OUT VOID **HostAddress,
OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,
OUT VOID **Mapping
)
{
EFI_STATUS Status;
UINTN NumberOfBytes;
*HostAddress = NULL;
*DeviceAddress = 0;
*Mapping = NULL;
Status = IoMmu->AllocateBuffer (
IoMmu,
EfiRuntimeServicesData,
Pages,
HostAddress,
0
);
if (EFI_ERROR (Status)) {
return EFI_OUT_OF_RESOURCES;
}
NumberOfBytes = EFI_PAGES_TO_SIZE (Pages);
Status = IoMmu->Map (
IoMmu,
EdkiiIoMmuOperationBusMasterCommonBuffer,
*HostAddress,
&NumberOfBytes,
DeviceAddress,
Mapping
);
if (EFI_ERROR (Status)) {
IoMmu->FreeBuffer (IoMmu, Pages, *HostAddress);
*HostAddress = NULL;
return EFI_OUT_OF_RESOURCES;
}
Status = IoMmu->SetAttribute (
IoMmu,
*Mapping,
EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE
);
if (EFI_ERROR (Status)) {
IoMmu->Unmap (IoMmu, *Mapping);
IoMmu->FreeBuffer (IoMmu, Pages, *HostAddress);
*Mapping = NULL;
*HostAddress = NULL;
return Status;
}
return Status;
}
/**
USB3 get IOMMU PPI.
@return Pointer to IOMMU PPI.
**/
EDKII_IOMMU_PPI *
Usb3GetIoMmu (
VOID
)
{
EFI_STATUS Status;
EDKII_IOMMU_PPI *IoMmu;
CONST EFI_PEI_SERVICES **PeiServices;
PeiServices = GetPeiServicesTablePointer ();
IoMmu = NULL;
Status = (*PeiServices)->LocatePpi (
PeiServices,
&gEdkiiIoMmuPpiGuid,
0,
NULL,
(VOID **) &IoMmu
);
if (!EFI_ERROR (Status) && (IoMmu != NULL)) {
return IoMmu;
}
return NULL;
}
/**
Return XHCI MMIO base address.
**/
EFI_PHYSICAL_ADDRESS
GetXhciBaseAddress (
VOID
)
{
UINT8 Bus;
UINT8 Device;
UINT8 Function;
USB3_DEBUG_PORT_CONTROLLER UsbDebugPort;
EFI_PHYSICAL_ADDRESS Address;
UINT32 Low;
UINT32 High;
UsbDebugPort.Controller = GetUsb3DebugPortController();
Bus = UsbDebugPort.PciAddress.Bus;
Device = UsbDebugPort.PciAddress.Device;
Function = UsbDebugPort.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_PEI_HOB_POINTERS Hob;
EFI_PHYSICAL_ADDRESS XhcMmioBase;
Hob.Raw = GetFirstGuidHob (&gUsb3DbgGuid);
if (Hob.Raw == NULL) {
return NULL;
}
Instance = GET_GUID_HOB_DATA (Hob.Guid);
//
// Update XHCI MMIO base address
//
XhcMmioBase = GetXhciBaseAddress ();
FixUsb3InstanceResource (Instance, XhcMmioBase);
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 serial device was initialized.
**/
RETURN_STATUS
EFIAPI
USB3Initialize (
VOID
)
{
return EFI_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
)
{
EFI_STATUS Status;
USB3_DEBUG_PORT_INSTANCE UsbDbg;
VOID *DataPtr;
EFI_PEI_HOB_POINTERS Hob;
CONST EFI_PEI_SERVICES **PeiServices;
PeiServices = GetPeiServicesTablePointer ();
//
// USB Initilization has to be done only once. So this function should just return when
// it is called more than once.
//
Hob.Raw = GetFirstGuidHob (&gUsb3DbgGuid);
if (Hob.Raw != NULL) {
return RETURN_SUCCESS;
}
//
// Initialize USB debug for PEI at the first time
//
SetMem (&UsbDbg, sizeof(UsbDbg), 0);
UsbDbg.FromHob = TRUE;
DiscoverUsb3DebugPort (&UsbDbg);
if (UsbDbg.DebugSupport) {
InitializeUsb3DebugPort (&UsbDbg);
}
if (UsbDbg.Ready && (Usb3GetIoMmu () == NULL)) {
Status = (*PeiServices)->NotifyPpi (PeiServices, &mUsb3IoMmuPpiNotifyDesc);
ASSERT_EFI_ERROR (Status);
}
//
// Save Instance into HOB
//
DataPtr = BuildGuidDataHob (
&gUsb3DbgGuid,
(VOID*) &UsbDbg,
sizeof (UsbDbg)
);
if (UsbDbg.DebugSupport) {
SaveUsb3InstanceAddress ((USB3_DEBUG_PORT_INSTANCE *) DataPtr);
}
return RETURN_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;
CONST EFI_PEI_SERVICES **PeiServices;
EFI_STATUS Status;
VOID *MemoryDiscoveredPpi;
EDKII_IOMMU_PPI *IoMmu;
VOID *HostAddress;
VOID *Mapping;
Buf = NULL;
PeiServices = GetPeiServicesTablePointer ();
//
// Make sure the allocated memory is physical memory.
//
Status = (*PeiServices)->LocatePpi (
PeiServices,
&gEfiPeiMemoryDiscoveredPpiGuid,
0,
NULL,
(VOID **) &MemoryDiscoveredPpi
);
if (!EFI_ERROR (Status)) {
IoMmu = Usb3GetIoMmu ();
if (IoMmu != NULL) {
Status = IoMmuAllocateBuffer (
IoMmu,
EFI_SIZE_TO_PAGES (BufferSize),
&HostAddress,
&Address,
&Mapping
);
if (!EFI_ERROR (Status)) {
ASSERT (Address == ((EFI_PHYSICAL_ADDRESS) (UINTN) HostAddress));
Buf = (VOID *)(UINTN) Address;
}
} else {
Status = (*PeiServices)->AllocatePages (
PeiServices,
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
)
{
CONST EFI_PEI_SERVICES **PeiServices;
EFI_STATUS Status;
VOID *MemoryDiscoveredPpi;
EDKII_IOMMU_PPI *IoMmu;
VOID *HostAddress;
PeiServices = GetPeiServicesTablePointer ();
//
// Make sure the allocated memory is physical memory.
//
Status = (*PeiServices)->LocatePpi (
PeiServices,
&gEfiPeiMemoryDiscoveredPpiGuid,
0,
NULL,
(VOID **) &MemoryDiscoveredPpi
);
if (!EFI_ERROR (Status)) {
Status = (*PeiServices)->LocatePpi (
PeiServices,
&gEdkiiIoMmuPpiGuid,
0,
NULL,
(VOID **) &IoMmu
);
if (!EFI_ERROR (Status)) {
Status = IoMmu->AllocateBuffer (
IoMmu,
EfiRuntimeServicesData,
1,
&HostAddress,
0
);
if (EFI_ERROR (Status)) {
//
// DMA protection has been enabled,
// but DMA buffer could not be allocated yet.
//
return FALSE;
}
IoMmu->FreeBuffer (
IoMmu,
1,
HostAddress
);
}
return TRUE;
}
return FALSE;
}