/** @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; }