/** @file Processor Power Management initialization code. This code determines current user configuration and modifies and loads ASL as well as initializing chipset and processor features to enable the proper power management. Acronyms: - PPM: Processor Power Management - TM: Thermal Monitor - IST: Intel(R) Speedstep technology - HT: Hyper-Threading Technology - ITBM: Intel(R) Turbo Boost Max Technology 3.0 @copyright INTEL CONFIDENTIAL Copyright 1999 - 2021 Intel Corporation. The source code contained or described herein and all documents related to the source code ("Material") are owned by Intel Corporation or its suppliers or licensors. Title to the Material remains with Intel Corporation or its suppliers and licensors. The Material may contain trade secrets and proprietary and confidential information of Intel Corporation and its suppliers and licensors, and is protected by worldwide copyright and trade secret laws and treaty provisions. No part of the Material may be used, copied, reproduced, modified, published, uploaded, posted, transmitted, distributed, or disclosed in any way without Intel's prior express written permission. No license under any patent, copyright, trade secret or other intellectual property right is granted to or conferred upon you by disclosure or delivery of the Materials, either expressly, by implication, inducement, estoppel or otherwise. Any license under such intellectual property rights must be express and approved by Intel in writing. Unless otherwise agreed by Intel in writing, you may not remove or alter this notice or any other notice embedded in Materials by Intel or Intel's suppliers or licensors in any way. This file contains an 'Intel Peripheral Driver' and is uniquely identified as "Intel Reference Module" and is licensed for Intel CPUs and chipsets under the terms of your license agreement with Intel or your vendor. This file may be modified by the user, subject to additional terms of the license agreement. @par Specification Reference: **/ #include "PowerMgmtInit.h" #include #include #include #include #include // // Global variables // GLOBAL_REMOVE_IF_UNREFERENCED CPU_NVS_AREA_PROTOCOL *gCpuNvsAreaProtocol = NULL; ///< CPU GlobalNvs Protocol #if FixedPcdGetBool(PcdBiosGuardEnable) == 1 GLOBAL_REMOVE_IF_UNREFERENCED BIOSGUARD_NVS_AREA_PROTOCOL *gBiosGuardNvsAreaProtocol = NULL; ///< BIOS Guard GlobalNvs Protocol #endif // // FVID Table Information // Default FVID table // One header field plus states // GLOBAL_REMOVE_IF_UNREFERENCED UINT16 mNumberOfStates = 0; GLOBAL_REMOVE_IF_UNREFERENCED FVID_TABLE *mFvidPointer = NULL; // // Globals to support updating ACPI Tables // GLOBAL_REMOVE_IF_UNREFERENCED EFI_ACPI_TABLE_PROTOCOL *mAcpiTable = NULL; GLOBAL_REMOVE_IF_UNREFERENCED EFI_ACPI_DESCRIPTION_HEADER *mCpu0IstTable = NULL; GLOBAL_REMOVE_IF_UNREFERENCED EFI_ACPI_DESCRIPTION_HEADER *mApIstTable = NULL; GLOBAL_REMOVE_IF_UNREFERENCED EFI_ACPI_DESCRIPTION_HEADER *mCpu0CstTable = NULL; GLOBAL_REMOVE_IF_UNREFERENCED EFI_ACPI_DESCRIPTION_HEADER *mApCstTable = NULL; GLOBAL_REMOVE_IF_UNREFERENCED EFI_ACPI_DESCRIPTION_HEADER *mCpuSsdtTable = NULL; GLOBAL_REMOVE_IF_UNREFERENCED EFI_ACPI_DESCRIPTION_HEADER *mCpu0TstTable = NULL; GLOBAL_REMOVE_IF_UNREFERENCED EFI_ACPI_DESCRIPTION_HEADER *mApTstTable = NULL; GLOBAL_REMOVE_IF_UNREFERENCED EFI_ACPI_DESCRIPTION_HEADER *mCpu0HwpTable = NULL; GLOBAL_REMOVE_IF_UNREFERENCED EFI_ACPI_DESCRIPTION_HEADER *mApHwpTable = NULL; GLOBAL_REMOVE_IF_UNREFERENCED EFI_ACPI_DESCRIPTION_HEADER *mCpu0PsdTable = NULL; GLOBAL_REMOVE_IF_UNREFERENCED EFI_ACPI_DESCRIPTION_HEADER *mApPsdTable = NULL; GLOBAL_REMOVE_IF_UNREFERENCED CPU_INIT_DATA_HOB *mCpuInitDataHob = NULL; GLOBAL_REMOVE_IF_UNREFERENCED EFI_MP_SERVICES_PROTOCOL *mMpService = NULL; GLOBAL_REMOVE_IF_UNREFERENCED UINT32 mCoreIndex = 0; typedef struct _MAILBOX_READ { UINT32 *MailboxDataPtr; UINT32 *MailboxStatus; } MAILBOX_READ; /** This function to be used with StartUpAllAps for retrieving the Max Core Frequency of all Cores. @param[in] VOID *Buffer **/ VOID EFIAPI MailboxReadFavoredCore ( IN OUT VOID *Buffer ) { MAILBOX_READ *MailboxReadParameters; UINT32 FuseCoreMax; UINT32 MailboxStatus; OC_MAILBOX_INTERFACE MailboxCommand; MailboxReadParameters = (MAILBOX_READ *) Buffer; MailboxCommand.InterfaceData = 0; MailboxCommand.Fields.CommandCompletion = MAILBOX_OC_CMD_READ_PER_CORE_RATIO_LIMITS_CMD; MailboxRead (MAILBOX_TYPE_OC, MailboxCommand.InterfaceData, &FuseCoreMax, &MailboxStatus); MailboxReadParameters->MailboxDataPtr[mCoreIndex] = FuseCoreMax; MailboxReadParameters->MailboxStatus[mCoreIndex] = MailboxStatus; } /** Initialize the power management support. This function will do boot time configuration: Detect HW capabilities and SW configuration Initialize HW and software state (primarily MSR and ACPI tables) @param[in] ImageHandle Pointer to the loaded image protocol for this driver @param[in] SystemTable Pointer to the EFI System Table @retval EFI_SUCCESS The driver installes/initialized correctly. @retval Driver will ASSERT in debug builds on error. PPM functionality is considered critical for mobile systems. **/ EFI_STATUS EFIAPI InitPowerManagement ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_STATUS Status; VOID *Hob; /// /// Get CPU Init Data Hob /// Hob = GetFirstGuidHob (&gCpuInitDataHobGuid); if (Hob == NULL) { DEBUG ((DEBUG_ERROR, "CPU Data HOB not available\n")); return EFI_NOT_FOUND; } mCpuInitDataHob = (CPU_INIT_DATA_HOB *) ((UINTN) Hob + sizeof (EFI_HOB_GUID_TYPE)); /// /// Initialize the Global pointer for VID Table /// mFvidPointer = (FVID_TABLE *) &(mCpuInitDataHob->FvidTable); /// /// Initialize Power management Global variables /// InitPowerManagementGlobalVariables (); /// Initialize CPU Power management code (Patch and install CPU ACPI tables, save S3 boot script info) /// Status = InitPpmDxe (); ASSERT_EFI_ERROR (Status); return EFI_SUCCESS; } /** Initializes the platform power management global variable. This must be called prior to any of the functions being used. **/ VOID InitPowerManagementGlobalVariables ( VOID ) { EFI_STATUS Status; UINT32 Index; MAILBOX_READ MailboxReadParameters; UINTN NumberOfCores; UINTN LogProcNum; UINTN BspIndex; MSR_CORE_THREAD_COUNT_REGISTER MsrCoreThreadCount; UINT8 LowestMaxPerf; UINT8 EnableItbm; EFI_PROCESSOR_INFORMATION MpContext; UINT32 BigCoreNumber; UINT32 AtomCoreNumber; UINT8 ApCoreType; #if FixedPcdGetBool(PcdBiosGuardEnable) == 1 BIOSGUARD_HOB *BiosGuardHobPtr; #endif NumberOfCores = 0; LogProcNum = 0; MailboxReadParameters.MailboxDataPtr = NULL; BspIndex = 0; BigCoreNumber = 0; AtomCoreNumber = 0; /// /// Locate CPU Nvs Protocol. /// Status = gBS->LocateProtocol ( &gCpuNvsAreaProtocolGuid, NULL, (VOID **) &gCpuNvsAreaProtocol ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "CPU GloableNvs protocol not found")); } ASSERT_EFI_ERROR (Status); if (gCpuNvsAreaProtocol != NULL) { gCpuNvsAreaProtocol->Area->Cpuid = GetCpuFamily () | GetCpuStepping (); } #if FixedPcdGetBool(PcdBiosGuardEnable) == 1 /// /// Get BIOS Guard Config Hob /// BiosGuardHobPtr = GetFirstGuidHob (&gBiosGuardHobGuid); if (BiosGuardHobPtr != NULL) { /// /// Locate BIOS Guard Nvs Protocol. /// Status = gBS->LocateProtocol ( &gBiosGuardNvsAreaProtocolGuid, NULL, (VOID **) &gBiosGuardNvsAreaProtocol ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "BIOS Guard Global Nvs protocol not found")); } }else { DEBUG ((DEBUG_ERROR, "BIOS Guard Hob not found, BIOS Guard Global Nvs protocol not initialized")); } #endif /// /// Intel Turbo Boost Technology support, locate MP services /// if (IsItbmSupported ()) { // // Locate the MP services protocol // Find the MP Protocol. This is an MP platform, so MP protocol must be there. // Status = gBS->LocateProtocol (&gEfiMpServiceProtocolGuid, NULL, (VOID **) &mMpService); ASSERT_EFI_ERROR (Status); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "Located MP protocol Status = %r\n", Status)); return; } ZeroMem (&MpContext, sizeof (EFI_PROCESSOR_INFORMATION)); MsrCoreThreadCount.Uint64 = AsmReadMsr64 (MSR_CORE_THREAD_COUNT); NumberOfCores = (UINTN) MsrCoreThreadCount.Bits.Corecount; LogProcNum = (UINTN) MsrCoreThreadCount.Bits.Threadcount; MailboxReadParameters.MailboxDataPtr = AllocateZeroPool (sizeof (UINT32) * NumberOfCores); MailboxReadParameters.MailboxStatus = AllocateZeroPool (sizeof (EFI_STATUS) * NumberOfCores); if ((MailboxReadParameters.MailboxDataPtr == NULL) || (MailboxReadParameters.MailboxStatus == NULL)) { return; } Status = mMpService->WhoAmI (mMpService, &BspIndex); // // Read Favored Core Information from each core index // for (Index = 0; Index < LogProcNum; Index++) { Status = mMpService->GetProcessorInfo ( mMpService, (UINTN)Index, &MpContext ); if (EFI_ERROR (Status)) { return; } if (MpContext.Location.Thread != 0) { // Continue when the processor is not 1st thread of Core, i.e. the 2nd, 3rd or 4th thread of Core. continue; } if (Index == BspIndex){ MailboxReadFavoredCore ((VOID*) &MailboxReadParameters); mCoreIndex++; BigCoreNumber++; } else { mMpService->StartupThisAP ( mMpService, (EFI_AP_PROCEDURE) MailboxReadFavoredCore, Index, NULL, 0, (VOID*) &MailboxReadParameters, NULL ); mCoreIndex++; ApCoreType = 0; mMpService->StartupThisAP ( mMpService, (EFI_AP_PROCEDURE) DetectCoreType, Index, NULL, 0, (UINT8 *) &ApCoreType, NULL ); if (ApCoreType == CPUID_CORE_TYPE_INTEL_CORE) { BigCoreNumber++; } else if (ApCoreType == CPUID_CORE_TYPE_INTEL_ATOM) { AtomCoreNumber++; } } } /// /// Find the lowest of core max ratio from OCMB 0x1C[15:8] FusedP0 max ratio /// LowestMaxPerf = 0xFF; for (Index = 0; Index < BigCoreNumber; Index++) { DEBUG((DEBUG_INFO, "Itbm: Highest performance fused ratio [%d] = %d \n",Index, ((MailboxReadParameters.MailboxDataPtr [Index] >> 8) & 0xFF))); DEBUG((DEBUG_INFO, "Itbm: Highest performance current ratio [%d] = %d \n",Index, (MailboxReadParameters.MailboxDataPtr [Index] & 0xFF))); if (LowestMaxPerf > (UINT8) ((MailboxReadParameters.MailboxDataPtr [Index] >> 8) & 0xFF)) { LowestMaxPerf = (UINT8) ((MailboxReadParameters.MailboxDataPtr [Index] >> 8) & 0xFF); } } DEBUG((DEBUG_INFO, "Itbm: Highest performance of slowest core LowestMaxPerf = 0x%x \n", LowestMaxPerf)); if (gCpuNvsAreaProtocol != NULL) { /// /// We check OCMB 0x1C[15:8] FusedP0 max ratio to identify ITBMT supported /// non-ITBMT: All cores are the same value /// ITBMT: Difference value among all cores /// EnableItbm = 0; if (gCpuNvsAreaProtocol->Area->EnableItbm) { for (Index = 0; Index < BigCoreNumber; Index++) { if (LowestMaxPerf < (UINT8) ((MailboxReadParameters.MailboxDataPtr [Index] >> 8) & 0xFF)) { EnableItbm = 1; break; } } } DEBUG((DEBUG_INFO, "Itbm: Itbm Supported = 0x%x \n", EnableItbm)); // // Update NVS with ITBMT support and Highest Performance of the slowest core. // gCpuNvsAreaProtocol->Area->EnableItbm = EnableItbm; gCpuNvsAreaProtocol->Area->LowestMaxPerf = LowestMaxPerf; } } /// /// Locate ACPI table protocol /// Status = gBS->LocateProtocol (&gEfiAcpiTableProtocolGuid, NULL, (VOID **) &mAcpiTable); ASSERT_EFI_ERROR (Status); return; } /** Initialize the processor power management based on hardware capabilities and user configuration settings. @retval EFI_SUCCESS - on success @retval Appropiate failure code on error **/ EFI_STATUS InitPpmDxe ( VOID ) { EFI_STATUS Status; Status = EFI_SUCCESS; Status = InitCpuAcpiTable (); if (EFI_ERROR (Status)) { return Status; } /// /// Initialise Miscellaneous features /// InitMiscFeatures (); return Status; }