/** @file CPU PCIe SMM Driver Entry Copyright (c) 2021, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "SaLateInitSmm.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include GLOBAL_REMOVE_IF_UNREFERENCED UINT8 mSaBusNumber; // // @note: // These temp bus numbers cannot be used in runtime (hot-plug). // These can be used only during boot. // GLOBAL_REMOVE_IF_UNREFERENCED UINT8 mTempRootPortBusNumMin; GLOBAL_REMOVE_IF_UNREFERENCED UINT8 mTempRootPortBusNumMax; GLOBAL_REMOVE_IF_UNREFERENCED CPU_PCIE_ROOT_PORT_CONFIG mCpuPcieRootPortConfig[CPU_PCIE_MAX_ROOT_PORTS]; GLOBAL_REMOVE_IF_UNREFERENCED BOOLEAN mCpuPciePmTrapExecuted = FALSE; GLOBAL_REMOVE_IF_UNREFERENCED PCH_PCIE_DEVICE_OVERRIDE *mDevAspmOverride; GLOBAL_REMOVE_IF_UNREFERENCED UINT32 mNumOfDevAspmOverride; /** An IoTrap callback to config PCIE power management settings **/ VOID CpuPciePmIoTrapSmiCallback ( VOID ) { UINT32 PortIndex; UINT64 RpBase; UINT8 MaxPciePortNum; UINTN RpDevice; UINTN RpFunction; MaxPciePortNum = GetMaxCpuPciePortNum (); for (PortIndex = 0; PortIndex < MaxPciePortNum; PortIndex++) { GetCpuPcieRpDevFun (PortIndex, &RpDevice, &RpFunction); RpBase = PCI_SEGMENT_LIB_ADDRESS (SA_SEG_NUM, SA_MC_BUS, (UINT32) RpDevice, (UINT32) RpFunction, 0); if (PciSegmentRead16 (RpBase) != 0xFFFF) { mDevAspmOverride = NULL; mNumOfDevAspmOverride = 0; RootportDownstreamPmConfiguration ( SA_SEG_NUM, SA_MC_BUS, (UINT8)RpDevice, (UINT8)RpFunction, mTempRootPortBusNumMin, mTempRootPortBusNumMax, &mCpuPcieRootPortConfig[PortIndex].PcieRpCommonConfig, mNumOfDevAspmOverride, mDevAspmOverride ); } } } /** Program Common Clock and ASPM of Downstream Devices @param[in] PortIndex Pcie Root Port Number @param[in] RpDevice Pcie Root Pci Device Number @param[in] RpFunction Pcie Root Pci Function Number @retval EFI_SUCCESS Root port complete successfully @retval EFI_UNSUPPORTED PMC has invalid vendor ID **/ EFI_STATUS CpuPcieSmi ( IN UINT8 PortIndex, IN UINT8 RpDevice, IN UINT8 RpFunction ) { UINT8 SecBus; UINT8 SubBus; UINT64 RpBase; UINT64 EpBase; UINT8 EpPcieCapPtr; UINT8 EpMaxSpeed; BOOLEAN DownstreamDevicePresent; UINT32 Timeout; UINT32 MaxLinkSpeed; RpBase = PCI_SEGMENT_LIB_ADDRESS ( SA_SEG_NUM, SA_MC_BUS, (UINT32) RpDevice, (UINT32) RpFunction, 0 ); if (PciSegmentRead16 (RpBase + PCI_VENDOR_ID_OFFSET) == 0xFFFF) { DEBUG((DEBUG_INFO, "PCIe controller is disabled, return!!\n")); return EFI_SUCCESS; } // // Check presence detect state. Here the endpoint must be detected using PDS rather than // the usual LinkActive check, because PDS changes immediately and LA takes a few milliseconds to stabilize // DownstreamDevicePresent = !!(PciSegmentRead16 (RpBase + R_PCIE_SLSTS) & B_PCIE_SLSTS_PDS); if (DownstreamDevicePresent) { /// /// Make sure the link is active before trying to talk to device behind it /// Wait up to 100ms, according to PCIE spec chapter 6.7.3.3 /// Timeout = 100 * 1000; while (CpuPcieIsLinkActive(RpBase) == 0) { MicroSecondDelay (10); Timeout-=10; if (Timeout == 0) { DEBUG((DEBUG_INFO, "PDS set but timeout while waiting for LA bit to get set!!!\n")); return EFI_NOT_FOUND; } } SecBus = PciSegmentRead8 (RpBase + PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET); SubBus = PciSegmentRead8 (RpBase + PCI_BRIDGE_SUBORDINATE_BUS_REGISTER_OFFSET); ASSERT (SecBus != 0 && SubBus != 0); if (SecBus == 0) { DEBUG((DEBUG_INFO, "Secondary Bus is 0, return!!!\n")); return EFI_NOT_FOUND; } RootportDownstreamConfiguration ( SA_SEG_NUM, SA_MC_BUS, RpDevice, RpFunction, mTempRootPortBusNumMin, mTempRootPortBusNumMax, EnumCpuPcie ); RootportDownstreamPmConfiguration ( SA_SEG_NUM, SA_MC_BUS, RpDevice, RpFunction, mTempRootPortBusNumMin, mTempRootPortBusNumMax, &mCpuPcieRootPortConfig[PortIndex].PcieRpCommonConfig, mNumOfDevAspmOverride, mDevAspmOverride ); // // Perform Equalization // EpBase = PCI_SEGMENT_LIB_ADDRESS (SA_SEG_NUM, SecBus, 0, 0, 0); EpPcieCapPtr = PcieFindCapId (SA_SEG_NUM, SecBus, 0, 0, EFI_PCI_CAPABILITY_ID_PCIEXP); EpMaxSpeed = PciSegmentRead8 (EpBase + EpPcieCapPtr + R_PCIE_LCAP_OFFSET) & B_PCIE_LCAP_MLS; MaxLinkSpeed = CpuPcieGetMaxLinkSpeed (RpBase); if (EpMaxSpeed < MaxLinkSpeed) { MaxLinkSpeed = EpMaxSpeed; } if (EpMaxSpeed >= V_PCIE_LCAP_MLS_GEN3 && EpMaxSpeed <= V_PCIE_LCAP_MLS_GEN4) { PciSegmentAndThenOr16 (RpBase + R_PCIE_LCTL2, (UINT16)~B_PCIE_LCTL2_TLS, (UINT16)MaxLinkSpeed); PciSegmentOr32 (RpBase + R_PCIE_LCTL3, B_PCIE_LCTL3_PE); PciSegmentOr32 (RpBase + R_PCIE_LCTL, B_PCIE_LCTL_RL); } } return EFI_SUCCESS; } /** PCIE Hotplug SMI call back function for each Root port @param[in] DispatchHandle Handle of this dispatch function @param[in] RpContext Rootport context, which contains RootPort Index, and RootPort PCI BDF. **/ VOID EFIAPI CpuPcieSmiRpHandlerFunction ( IN EFI_HANDLE DispatchHandle, IN PCH_PCIE_SMI_RP_CONTEXT *RpContext ) { CpuPcieSmi (RpContext->RpIndex, RpContext->DevNum, RpContext->FuncNum); } /** PCIE Link Active State Change Hotplug SMI call back function for all Root ports @param[in] DispatchHandle Handle of this dispatch function @param[in] RpContext Rootport context, which contains RootPort Index, and RootPort PCI BDF. **/ VOID EFIAPI CpuPcieLinkActiveStateChange ( IN EFI_HANDLE DispatchHandle, IN PCH_PCIE_SMI_RP_CONTEXT *RpContext ) { return; } /** PCIE Link Equalization Request SMI call back function for all Root ports @param[in] DispatchHandle Handle of this dispatch function @param[in] RpContext Rootport context, which contains RootPort Index, and RootPort PCI BDF. **/ VOID EFIAPI CpuPcieLinkEqHandlerFunction ( IN EFI_HANDLE DispatchHandle, IN PCH_PCIE_SMI_RP_CONTEXT *RpContext ) { /// /// From PCI Express specification, the PCIe device can request for Link Equalization. When the /// Link Equalization is requested by the device, an SMI will be generated by PCIe RP when /// enabled and the SMI subroutine would invoke the Software Preset/Coefficient Search /// software to re-equalize the link. /// return; } /** An IoTrap callback to config PCIE power management settings @param[in] DispatchHandle - The handle of this callback, obtained when registering @param[in] DispatchContext - Pointer to the EFI_SMM_IO_TRAP_DISPATCH_CALLBACK_CONTEXT **/ VOID EFIAPI CpuPcieIoTrapSmiCallback ( IN EFI_HANDLE DispatchHandle, IN EFI_SMM_IO_TRAP_CONTEXT *CallbackContext, IN OUT VOID *CommBuffer, IN OUT UINTN *CommBufferSize ) { if (CallbackContext->WriteData == CpuPciePmTrap) { if (mCpuPciePmTrapExecuted == FALSE) { CpuPciePmIoTrapSmiCallback (); mCpuPciePmTrapExecuted = TRUE; } } else { ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER); } } /** This function clear the Io trap executed flag before enter S3 @param[in] Handle Handle of the callback @param[in] Context The dispatch context @retval EFI_SUCCESS SA register saved **/ EFI_STATUS EFIAPI CpuPcieS3EntryCallBack ( IN EFI_HANDLE Handle, IN CONST VOID *Context OPTIONAL, IN OUT VOID *CommBuffer OPTIONAL, IN OUT UINTN *CommBufferSize OPTIONAL ) { mCpuPciePmTrapExecuted = FALSE; return EFI_SUCCESS; } /** Register PCIE Hotplug SMI dispatch function to handle Hotplug enabling @param[in] ImageHandle The image handle of this module @param[in] SystemTable The EFI System Table @retval EFI_SUCCESS The function completes successfully **/ EFI_STATUS EFIAPI InitializeCpuPcieSmm ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_STATUS Status; UINT8 PortIndex; UINT8 Data8; UINT32 Data32Or; UINT32 Data32And; UINT64 RpBase; UINTN RpDevice; UINTN RpFunction; EFI_HANDLE PcieHandle; EFI_HANDLE PchIoTrapHandle; PCH_PCIE_SMI_DISPATCH_PROTOCOL *PchPcieSmiDispatchProtocol; EFI_SMM_IO_TRAP_REGISTER_CONTEXT PchIoTrapContext; EFI_SMM_SX_REGISTER_CONTEXT SxDispatchContext; SA_IOTRAP_SMI_PROTOCOL *CpuPcieIoTrapProtocol; EFI_HANDLE SxDispatchHandle; UINT8 MaxPciePortNum; CPU_PCIE_HOB *CpuPcieHob; DEBUG ((DEBUG_INFO, "InitializeCpuPcieSmm () Start\n")); MaxPciePortNum = GetMaxCpuPciePortNum (); // // Locate Pch Pcie Smi Dispatch Protocol // Status = gSmst->SmmLocateProtocol (&gPchPcieSmiDispatchProtocolGuid, NULL, (VOID**) &PchPcieSmiDispatchProtocol); ASSERT_EFI_ERROR (Status); mTempRootPortBusNumMin = PcdGet8 (PcdSiliconInitTempPciBusMin); mTempRootPortBusNumMax = PcdGet8 (PcdSiliconInitTempPciBusMax); /// /// Locate HOB for CPU PCIe /// CpuPcieHob = GetFirstGuidHob(&gCpuPcieHobGuid); if (CpuPcieHob != NULL) { ASSERT (sizeof mCpuPcieRootPortConfig == sizeof CpuPcieHob->RootPort); CopyMem ( mCpuPcieRootPortConfig, &(CpuPcieHob->RootPort), sizeof (mCpuPcieRootPortConfig) ); } // // Throught all PCIE root port function and register the SMI Handler for enabled ports. // for (PortIndex = 0; PortIndex < MaxPciePortNum; PortIndex++) { GetCpuPcieRpDevFun (PortIndex, &RpDevice, &RpFunction); RpBase = PCI_SEGMENT_LIB_ADDRESS (SA_SEG_NUM, SA_MC_BUS, (UINT32) RpDevice, (UINT32) RpFunction, 0); // // Skip the root port function which is not enabled // if (PciSegmentRead32 (RpBase) == 0xFFFFFFFF) { continue; } // // Register SMI Handlers for Hot Plug and Link Active State Change // Data8 = PciSegmentRead8 (RpBase + R_PCIE_SLCAP); if (Data8 & B_PCIE_SLCAP_HPC) { PcieHandle = NULL; Status = PchPcieSmiDispatchProtocol->HotPlugRegister ( PchPcieSmiDispatchProtocol, CpuPcieSmiRpHandlerFunction, (PortIndex + CpuRpIndex0), &PcieHandle ); ASSERT_EFI_ERROR (Status); Status = PchPcieSmiDispatchProtocol->LinkActiveRegister ( PchPcieSmiDispatchProtocol, CpuPcieLinkActiveStateChange, (PortIndex + CpuRpIndex0), &PcieHandle ); ASSERT_EFI_ERROR (Status); Data32Or = B_PCIE_MPC_HPME; Data32And = (UINT32) ~B_PCIE_MPC_HPME; S3BootScriptSaveMemReadWrite ( S3BootScriptWidthUint32, PcdGet64 (PcdSiPciExpressBaseAddress) + RpBase + R_PCIE_MPC, &Data32Or, /// Data to be ORed &Data32And /// Data to be ANDed ); } // // Register SMI Handler for Link Equalization Request from Gen 3 Devices. // Data8 = PciSegmentRead8 (RpBase + R_PCIE_LCAP); if ((Data8 & B_PCIE_LCAP_MLS) == V_PCIE_LCAP_MLS_GEN3) { Status = PchPcieSmiDispatchProtocol->LinkEqRegister ( PchPcieSmiDispatchProtocol, CpuPcieLinkEqHandlerFunction, (PortIndex + CpuRpIndex0), &PcieHandle ); ASSERT_EFI_ERROR (Status); } } ASSERT_EFI_ERROR (Status); PchIoTrapContext.Type = WriteTrap; PchIoTrapContext.Length = 4; PchIoTrapContext.Address = 0; Status = mPchIoTrap->Register ( mPchIoTrap, (EFI_SMM_HANDLER_ENTRY_POINT2) CpuPcieIoTrapSmiCallback, &PchIoTrapContext, &PchIoTrapHandle ); ASSERT_EFI_ERROR (Status); // // Install the SA Pcie IoTrap protocol // (gBS->AllocatePool) (EfiBootServicesData, sizeof (SA_IOTRAP_SMI_PROTOCOL), (VOID **)&CpuPcieIoTrapProtocol); CpuPcieIoTrapProtocol->SaIotrapSmiAddress = PchIoTrapContext.Address; Status = gBS->InstallMultipleProtocolInterfaces ( &ImageHandle, &gCpuPcieIoTrapProtocolGuid, CpuPcieIoTrapProtocol, NULL ); // // Register the callback for S3 entry // SxDispatchContext.Type = SxS3; SxDispatchContext.Phase = SxEntry; Status = mSxDispatch->Register ( mSxDispatch, CpuPcieS3EntryCallBack, &SxDispatchContext, &SxDispatchHandle ); ASSERT_EFI_ERROR (Status); DEBUG ((DEBUG_INFO, "InitializeCpuPcieSmm, IoTrap @ %x () End\n", PchIoTrapContext.Address)); return EFI_SUCCESS; }