summaryrefslogtreecommitdiff
path: root/ReferenceCode/Haswell/PowerManagement/Dxe/MiscFunctions.c
diff options
context:
space:
mode:
Diffstat (limited to 'ReferenceCode/Haswell/PowerManagement/Dxe/MiscFunctions.c')
-rw-r--r--ReferenceCode/Haswell/PowerManagement/Dxe/MiscFunctions.c853
1 files changed, 853 insertions, 0 deletions
diff --git a/ReferenceCode/Haswell/PowerManagement/Dxe/MiscFunctions.c b/ReferenceCode/Haswell/PowerManagement/Dxe/MiscFunctions.c
new file mode 100644
index 0000000..5ad9fc8
--- /dev/null
+++ b/ReferenceCode/Haswell/PowerManagement/Dxe/MiscFunctions.c
@@ -0,0 +1,853 @@
+/** @file
+ This file contains Processor Power Management ACPI related functions for
+ Haswell processors.
+
+ Acronyms:
+ PPM - Processor Power Management
+ TM - Thermal Monitor
+ IST - Intel(R) Speedstep technology
+ HT - Hyper-Threading Technology
+
+@copyright
+ Copyright (c) 2012-2013 Intel Corporation. All rights reserved
+ This software and associated documentation (if any) is furnished
+ under a license and may only be used or copied in accordance
+ with the terms of the license. Except as permitted by such
+ license, no part of this software or documentation may be
+ reproduced, stored in a retrieval system, or transmitted in any
+ form or by any means without the express written consent of
+ Intel Corporation.
+
+ This file contains an 'Intel Peripheral Driver' and 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
+
+**/
+#include "PowerMgmtCommon.h"
+
+///
+/// Table to convert PL1 / Pl2 Seconds into equivalent MSR values
+/// This table is used for TDP Time Window programming
+///
+UINT8 mSecondsToMsrValueMapTable[][2] = {
+ ///
+ /// Seconds, MSR Value
+ ///
+ { 1, 0x0A },
+ { 2, 0x0B },
+ { 3, 0x4B },
+ { 4, 0x0C },
+ { 5, 0x2C },
+ { 6, 0x4C },
+ { 7, 0x6C },
+ { 8, 0x0D },
+ { 10, 0x2D },
+ { 12, 0x4D },
+ { 14, 0x6D },
+ { 16, 0x0E },
+ { 20, 0x2E },
+ { 24, 0x4E },
+ { 28, 0x6E },
+ { 32, 0x0F },
+ { 40, 0x2F },
+ { 48, 0x4F },
+ { 56, 0x6F },
+ { 64, 0x10 },
+ { 80, 0x30 },
+ { 96, 0x50 },
+ { 112, 0x70 },
+ { 128, 0x11 },
+ { END_OF_TABLE, END_OF_TABLE }
+};
+
+///
+/// Table to convert PL3 Milli Seconds into equivalent MSR values
+/// This table is used for TDP Time Window programming
+///
+UINT8 mMilliSecondsToMsrValueMapTable[][2] = {
+ ///
+ /// MilliSeconds, MSR Value
+ ///
+ { 3, 0x41 },
+ { 4, 0x02 },
+ { 5, 0x22 },
+ { 6, 0x42 },
+ { 7, 0x62 },
+ { 8, 0x03 },
+ { 10, 0x23 },
+ { 12, 0x43 },
+ { 14, 0x63 },
+ { 16, 0x04 },
+ { 20, 0x24 },
+ { 24, 0x44 },
+ { 28, 0x64 },
+ { 32, 0x05 },
+ { 40, 0x25 },
+ { 48, 0x45 },
+ { 56, 0x65 },
+ { 64, 0x06 },
+ { END_OF_TABLE, END_OF_TABLE }
+};
+
+/**
+ This will perform Miscellaneous Power Management related programming.
+
+ @param[in] CtdpSupport Status of InitializeConfigurableTdp funtion
+**/
+VOID
+InitMiscFeatures (
+ EFI_STATUS CtdpSupport
+ )
+{
+ InitPchPowerSharing(mCpuPmConfig);
+ ///
+ /// Configure Package Turbo Power Limits
+ ///
+ if (CtdpSupport == EFI_SUCCESS) {
+ ConfigureCtdp (mCpuPmConfig);
+ } else {
+ ConfigurePowerLimitsNonConfigTdpSkus (mCpuPmConfig);
+ }
+
+ ///
+ /// This will perform PowerLimit 1 algorithm will be used to control Thermal Throttling features
+ ///
+ InitPl1ThermalControl (mCpuPmConfig);
+
+ ///
+ /// Configure PL3
+ ///
+ ConfigurePL3PowerLimits(mCpuPmConfig);
+
+ ///
+ /// Configure DDR RAPL PowerLimits
+ ///
+ ConfigureDdrPowerLimits(mCpuPmConfig);
+}
+
+/**
+ Private helper function to convert various Turbo Power Limit Time from Seconds to CPU units
+
+ @param[in] TimeInSeconds Time in seconds
+ @param[in] PowerLimitLevel Power Limit Level
+
+ @retval UINT8 Converted time in CPU units
+**/
+UINT8
+GetConvertedTime (
+ IN UINT32 TimeInSeconds,
+ IN UINT8 PowerLimitLevel
+ )
+{
+ UINT8 ConvertedPowerLimitTime;
+ UINT8 Index;
+
+ ///
+ /// Convert seconds to MSR value. Since not all values are programmable, we'll select
+ /// the entry from mapping table which is either equal to the user selected value. OR to a value in the mapping table
+ /// which is closest (but less than) to the user-selected value.
+ ///
+ ConvertedPowerLimitTime = 0;
+ switch(PowerLimitLevel) {
+ case PL12TimeWindowCovert:
+ ConvertedPowerLimitTime = mSecondsToMsrValueMapTable[0][1];
+ for (Index = 0; mSecondsToMsrValueMapTable[Index][0] != END_OF_TABLE; Index++) {
+ if (TimeInSeconds == mSecondsToMsrValueMapTable[Index][0]) {
+ ConvertedPowerLimitTime = mSecondsToMsrValueMapTable[Index][1];
+ break;
+ }
+ if (TimeInSeconds > mSecondsToMsrValueMapTable[Index][0]) {
+ ConvertedPowerLimitTime = mSecondsToMsrValueMapTable[Index][1];
+ } else {
+ break;
+ }
+ }
+ break;
+ case PL3TimeWindowConvert:
+ ConvertedPowerLimitTime = mMilliSecondsToMsrValueMapTable[0][1];
+ for (Index = 0; mMilliSecondsToMsrValueMapTable[Index][0] != END_OF_TABLE; Index++) {
+ if (TimeInSeconds == mMilliSecondsToMsrValueMapTable[Index][0]) {
+ ConvertedPowerLimitTime = mMilliSecondsToMsrValueMapTable[Index][1];
+ break;
+ }
+ if (TimeInSeconds > mMilliSecondsToMsrValueMapTable[Index][0]) {
+ ConvertedPowerLimitTime = mMilliSecondsToMsrValueMapTable[Index][1];
+ } else {
+ break;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ return ConvertedPowerLimitTime;
+}
+
+/**
+ Configure PMSYNC_TPR_CFG and PMSYNC_TPR_CFG2 using values returned by CPU BIOS Mail box
+
+ @param[in] CpuPmConfig Pointer to policy protocol instance
+**/
+VOID
+InitPchPowerSharing (
+ IN OUT POWER_MGMT_CONFIG *CpuPmConfig
+ )
+{
+ UINT32 PcodeMailBoxPchPowerLevels;
+ UINT32 MailBoxStatus;
+ UINT32 Rcba;
+ UINT32 Index;
+ UINT8 PchPowerLevel;
+ UINT32 Data32And;
+ UINT32 Data32Or;
+ UINT16 LpcDeviceId;
+ UINT8 PchRevId;
+ UINT8 IsLptLp;
+ CPU_STEPPING CpuSteppingId;
+ CPU_FAMILY CpuFamilyId;
+
+ CpuFamilyId = mPpmGlobalNvsAreaProtocol->Area->Cpuid & CPUID_FULL_FAMILY_MODEL;
+ CpuSteppingId = mPpmGlobalNvsAreaProtocol->Area->Cpuid & CPUID_FULL_STEPPING;
+
+ ///
+ /// PCH Power sharing supported only on HSW ULT.
+ ///
+ if (CpuFamilyId != EnumCpuHswUlt) {
+ return;
+ }
+
+ ///
+ /// Read PCH Power Limit from PCODE Mail Box.
+ ///
+ MailboxRead (MAILBOX_TYPE_PCODE,READ_PCH_POWER_LEVELS_CMD,&PcodeMailBoxPchPowerLevels,&MailBoxStatus);
+
+ DEBUG ((EFI_D_ERROR, "Read PCH Power Limit from PCODE Mail Box : %x \n",PcodeMailBoxPchPowerLevels));
+ Rcba = MmioRead32 (
+ MmPciAddress (
+ 0,
+ DEFAULT_PCI_BUS_NUMBER_PCH,
+ PCI_DEVICE_NUMBER_PCH_LPC,
+ PCI_FUNCTION_NUMBER_PCH_LPC,
+ R_PCH_LPC_RCBA
+ )
+ );
+ Rcba &= (UINT32) (~BIT0);
+ if (MailBoxStatus == PCODE_MAILBOX_CC_SUCCESS) {
+ ///
+ /// Program RCBA+PMSYNC_TPR_CONFIG PCH power limit values.
+ /// READ_PCH_POWER_LEVELS_CMD MailBox[0:5],MailBox[6:11],MailBox[12:17] to PCHReg [0:4],[8:12],[16:20]
+ ///
+ Data32And =0x0;
+ Data32Or =0x0;
+
+ for (Index = 0; Index < HSW_ULT_PCH_POWER_LEVELS; Index++) {
+ PchPowerLevel = PcodeMailBoxPchPowerLevels & 0x3F;
+ PcodeMailBoxPchPowerLevels = PcodeMailBoxPchPowerLevels >> 6;
+ Data32And |= 0x1F << (Index * 8);
+ Data32Or |= (PchPowerLevel & 0x1F) << (Index * 8);
+ }
+ Data32And = ~Data32And;
+ MmioAndThenOr32(Rcba+PMSYNC_TPR_CONFIG,Data32And,Data32Or);
+ SCRIPT_MEM_WRITE (
+ EFI_ACPI_S3_RESUME_SCRIPT_TABLE,
+ EfiBootScriptWidthUint32,
+ (UINTN) (Rcba + PMSYNC_TPR_CONFIG),
+ 1,
+ (VOID *) (UINTN) (Rcba + PMSYNC_TPR_CONFIG)
+ );
+ } else {
+ DEBUG ((EFI_D_ERROR, "Failure - Read PCH Power Limit from PCODE Mail Box\n"));
+ }
+ ///
+ /// Extended PCH power sharing supported on HSW ULT C0 & LPT-LP B0 and later
+ ///
+ ///
+ PchRevId = MmioRead8 (
+ MmPciAddress (0,
+ DEFAULT_PCI_BUS_NUMBER_PCH,
+ PCI_DEVICE_NUMBER_PCH_LPC,
+ PCI_FUNCTION_NUMBER_PCH_LPC,
+ R_PCH_LPC_RID)
+ );
+
+ LpcDeviceId = MmioRead16 (
+ MmPciAddress (0,
+ DEFAULT_PCI_BUS_NUMBER_PCH,
+ PCI_DEVICE_NUMBER_PCH_LPC,
+ PCI_FUNCTION_NUMBER_PCH_LPC,
+ R_PCH_LPC_DEVICE_ID)
+ );
+ IsLptLp = IS_PCH_LPTLP_LPC_DEVICE_ID(LpcDeviceId);
+
+ if(IsLptLp && (PchRevId < V_PCH_LPT_LPC_RID_2) && (CpuSteppingId < EnumHswUltC0)) {
+ return;
+ }
+ ///
+ /// Program RCBA+PMSYNC_TPR_CONFIG Extnded PCH power limit values.
+ /// READ_PCH_POWER_LEVELS_CMD-MailBox[23:18],READ_EXT_PCH_POWER_LEVELS_CMD- MailBox[6:11],MailBox[12:17],MailBox[18:22] to PCHReg [0:4],[8:12],[16:20],[24:28]
+ ///
+ Data32And = 0x1F;
+ Data32Or = (PcodeMailBoxPchPowerLevels & 0x1F);
+ ///
+ /// Read Extended PCH Power Limit from PCODE Mail Box.
+ ///
+ MailboxRead (MAILBOX_TYPE_PCODE,READ_EXT_PCH_POWER_LEVELS_CMD,&PcodeMailBoxPchPowerLevels,&MailBoxStatus);
+ DEBUG ((EFI_D_ERROR, "Read Extended PCH Power Limit from PCODE Mail Box : %x \n",PcodeMailBoxPchPowerLevels));
+ if (MailBoxStatus == PCODE_MAILBOX_CC_SUCCESS) {
+ for (Index = 1; Index < EXTENDED_PCH_POWER_LEVELS; Index++) {
+ PchPowerLevel = PcodeMailBoxPchPowerLevels & 0x3F;
+ PcodeMailBoxPchPowerLevels = PcodeMailBoxPchPowerLevels >> 6;
+ Data32And |= 0x1F << (Index * 8);
+ Data32Or |= (PchPowerLevel & 0x1F) << (Index * 8);
+ }
+ Data32And = ~Data32And;
+ MmioAndThenOr32(Rcba+PMSYNC_TPR_CONFIG2,Data32And,Data32Or);
+ SCRIPT_MEM_WRITE (
+ EFI_ACPI_S3_RESUME_SCRIPT_TABLE,
+ EfiBootScriptWidthUint32,
+ (UINTN) (Rcba + PMSYNC_TPR_CONFIG2),
+ 1,
+ (VOID *) (UINTN) (Rcba + PMSYNC_TPR_CONFIG2)
+ );
+ } else {
+ DEBUG ((EFI_D_ERROR, "Failure -Extended Read PCH Power Limit from PCODE Mail Box\n"));
+ }
+}
+
+/**
+ Locks down all settings.
+
+ @param[in] CpuPmConfig Pointer to PPM Policy structure.
+**/
+VOID
+PpmLockDown (
+ IN OUT POWER_MGMT_CONFIG *CpuPmConfig
+ )
+{
+ MSR_REGISTER TempMsr;
+ ///
+ /// Program PMG_CST_CONFIG MSR [15] (CFG lock bit)
+ ///
+ RunOnAllLogicalProcessors (ApSafeLockDown, CpuPmConfig);
+ ///
+ /// Lock Package power limit MSR
+ ///
+ TempMsr.Qword = AsmReadMsr64 (MSR_PACKAGE_POWER_LIMIT);
+ TempMsr.Dwords.High &= ~(B_POWER_LIMIT_LOCK);
+ if (CpuPmConfig->pTurboSettings->TurboPowerLimitLock) {
+ TempMsr.Dwords.High |= B_POWER_LIMIT_LOCK;
+ }
+ AsmWriteMsr64 (MSR_PACKAGE_POWER_LIMIT, TempMsr.Qword);
+ ///
+ /// Program the OverClocking Lock Bit.
+ ///
+ TempMsr.Qword = AsmReadMsr64 (MSR_FLEX_RATIO);
+ TempMsr.Dwords.Low &= ~(B_OVERCLOCKING_LOCK);
+ if (CpuPmConfig->pPpmLockEnables->OverclockingLock) {
+ TempMsr.Dwords.Low |= B_OVERCLOCKING_LOCK;
+ }
+ AsmWriteMsr64 (MSR_FLEX_RATIO, TempMsr.Qword);
+ ///
+ /// Program the PROCHOT_Lock
+ ///
+ TempMsr.Qword = AsmReadMsr64 (MSR_POWER_CTL);
+ TempMsr.Dwords.Low &= ~(B_MSR_POWER_CTL_PROC_HOT_LOCK);
+ if (CpuPmConfig->pPpmLockEnables->ProcHotLock) {
+ TempMsr.Dwords.Low |= B_MSR_POWER_CTL_PROC_HOT_LOCK;
+ }
+ AsmWriteMsr64 (MSR_POWER_CTL, TempMsr.Qword);
+ ///
+ /// Program Ddr RAPL LIMIT Lock
+ ///
+ TempMsr.Qword = AsmReadMsr64 (MSR_DDR_RAPL_LIMIT);
+ TempMsr.Dwords.High &= ~(B_POWER_LIMIT_LOCK);
+ if (CpuPmConfig->pTurboSettings->TurboPowerLimitLock) {
+ TempMsr.Dwords.High |= B_POWER_LIMIT_LOCK;
+ }
+ AsmWriteMsr64 (MSR_DDR_RAPL_LIMIT, TempMsr.Qword);
+
+ return;
+}
+
+/**
+ Lock MSR_PMG_CST_CONFIG.
+ This function must be MP safe.
+
+ @param[in] Buffer Not used (needed for API compatibility)
+
+ @retval EFI_SUCCESS Processor C-State locked successfully.
+**/
+VOID
+EFIAPI
+ApSafeLockDown (
+ IN OUT VOID *Buffer
+ )
+{
+ MSR_REGISTER PmCfgCtrl;
+ POWER_MGMT_CONFIG *CpuPmConfig;
+ UINT8 CfgLock;
+
+ CpuPmConfig = (POWER_MGMT_CONFIG *) Buffer;
+ if (CpuPmConfig == NULL) {
+ CfgLock = PPM_ENABLE;
+ } else {
+ CfgLock = (UINT8) CpuPmConfig->pPpmLockEnables->PmgCstCfgCtrlLock;
+ }
+ PmCfgCtrl.Qword = AsmReadMsr64 (MSR_PMG_CST_CONFIG);
+ PmCfgCtrl.Dwords.Low &= ~B_CST_CONTROL_LOCK;
+ if (CfgLock == PPM_ENABLE) {
+ PmCfgCtrl.Dwords.Low |= B_CST_CONTROL_LOCK;
+ }
+ AsmWriteMsr64 (MSR_PMG_CST_CONFIG, PmCfgCtrl.Qword);
+
+ return;
+}
+
+/**
+ Runs the specified procedure on all logical processors, passing in the
+ parameter buffer to the procedure.
+
+ @param[in] Procedure The function to be run.
+ @param[in] Buffer Pointer to a parameter buffer.
+
+ @retval EFI_SUCCESS
+**/
+EFI_STATUS
+RunOnAllLogicalProcessors (
+ IN OUT EFI_AP_PROCEDURE Procedure,
+ IN OUT VOID *Buffer
+ )
+{
+ EFI_STATUS Status;
+
+ ///
+ /// Run the procedure on all logical processors.
+ ///
+ (*Procedure)(Buffer);
+ Status = mMpService->StartupAllAPs (
+ mMpService,
+ (EFI_AP_PROCEDURE) Procedure,
+ TRUE,
+ NULL,
+ MP_TIMEOUT_FOR_STARTUP_ALL_APS,
+ Buffer,
+ NULL
+ );
+
+ return Status;
+}
+
+/**
+ Configures the RFI Tunning MSR (0xE3) for FIVR switching freq.
+
+ @param[in] CpuPmConfig Pointer to PPM Policy structure.
+**/
+VOID
+InitFivrSwitchingFreq (
+ IN OUT POWER_MGMT_CONFIG *CpuPmConfig
+ )
+{
+ MSR_REGISTER RfiTuningValue;
+ UINT16 FreqTuningOffsetValue;
+ UINT32 Remainder;
+
+ ///
+ /// Check if we have to change the RFI Freq Tunning offset.
+ /// Check PLATFORM_INFO MSR[25] == 1 before accessing the MSR_RFI_TUNNING
+ ///
+ if ((CpuPmConfig->RfiFreqTunningOffset != AUTO) &&
+ ((AsmReadMsr64 (MSR_PLATFORM_INFO)) & B_FIVR_RFI_TUNING_AVAIL)
+ ) {
+ ///
+ /// Convert the Policy Freq Tunning offset.
+ /// Target frequency encoding = int(value*2^16+0.5) for positive offsets and inv(int(value*2^16+0.5))+1 for negative offsets
+ ///
+ FreqTuningOffsetValue = (UINT16) DivU64x32Remainder (
+ (UINT64) (CpuPmConfig->RfiFreqTunningOffset * (1 << 16)),
+ 1000,
+ &Remainder
+ );
+ if (Remainder >= 500) {
+ FreqTuningOffsetValue += 1;
+ }
+ ///
+ /// Check if Freq Tunning offset value is -ve
+ ///
+ if (CpuPmConfig->RfiFreqTunningOffsetIsNegative == 1) {
+ FreqTuningOffsetValue = (UINT16) (~FreqTuningOffsetValue + 1);
+ }
+ ///
+ /// Write to the RFI_TUNING_MSR. System BIOS must set the desired frequency offset in bits 15:0 of this MSR.
+ ///
+ RfiTuningValue.Qword = AsmReadMsr64 (MSR_RFI_TUNNING);
+
+ ///
+ /// Set the Tuning Frequency
+ ///
+ RfiTuningValue.Qword = ((RfiTuningValue.Qword & V_FREQ_TUNNING_MASK) | FreqTuningOffsetValue);
+ AsmWriteMsr64 (MSR_RFI_TUNNING, RfiTuningValue.Qword);
+ }
+}
+
+/**
+ Update the SSDT table pointers and config DWORD CFGD with the PpmFlags current configuration value
+**/
+VOID
+PatchCpuPmTable (
+ VOID
+ )
+{
+ UINT8 *CurrPtr;
+ UINT32 *Signature;
+ SSDT_LAYOUT *SsdtPackage;
+
+ ///
+ /// Locate the SSDT package
+ ///
+ SsdtPackage = NULL;
+ CurrPtr = (UINT8 *) mCpuPmTable;
+ for (CurrPtr; CurrPtr <= ((UINT8 *) mCpuPmTable + mCpuPmTable->Length); CurrPtr++) {
+ Signature = (UINT32 *) (CurrPtr + 1);
+ if ((*CurrPtr == AML_NAME_OP) && *Signature == EFI_SIGNATURE_32 ('S', 'S', 'D', 'T')) {
+ ///
+ /// Update the SSDT table pointers for dynamically loaded tables
+ ///
+ SsdtPackage = (SSDT_LAYOUT *) CurrPtr;
+ ///
+ /// Set the P-State SSDT table information
+ ///
+ SsdtPackage->Cpu0IstAddr = (UINT32) (UINTN) mCpu0IstTable;
+ SsdtPackage->Cpu0IstLen = mCpu0IstTable->Length;
+ SsdtPackage->ApIstAddr = (UINT32) (UINTN) mApIstTable;
+ SsdtPackage->ApIstLen = mApIstTable->Length;
+ ///
+ /// Set the C-State SSDT table information
+ ///
+ SsdtPackage->Cpu0CstAddr = (UINT32) (UINTN) mCpu0CstTable;
+ SsdtPackage->Cpu0CstLen = mCpu0CstTable->Length;
+ SsdtPackage->ApCstAddr = (UINT32) (UINTN) mApCstTable;
+ SsdtPackage->ApCstLen = mApCstTable->Length;
+ }
+ ///
+ /// Update the PPM GlobalNvs area
+ ///
+ if ((*CurrPtr == AML_OPREGION_OP) && *Signature == EFI_SIGNATURE_32 ('P', 'P', 'M', 'T')) {
+ ASSERT_EFI_ERROR (*(UINT32 *) (CurrPtr + 1 + sizeof (*Signature) + 2) == 0xFFFF0000);
+ ASSERT_EFI_ERROR (*(UINT16 *) (CurrPtr + 1 + sizeof (*Signature) + 2 + sizeof (UINT32) + 1) == 0xAA55);
+ ///
+ /// PPM Global NVS Area address
+ ///
+ *(UINT32 *) (CurrPtr + 1 + sizeof (*Signature) + 2) = (UINT32) (UINTN) mPpmGlobalNvsAreaProtocol->Area;
+ ///
+ /// PPM Global NVS Area size
+ ///
+ *(UINT16 *) (CurrPtr + 1 + sizeof (*Signature) + 2 + sizeof (UINT32) + 1) = sizeof (PPM_GLOBAL_NVS_AREA);
+ break;
+ }
+ }
+ //
+ // Assert if we didn't update the PM table
+ //
+ ASSERT (SsdtPackage != NULL);
+
+ return;
+}
+
+/**
+ Locate the PPM ACPI tables data file and read ACPI SSDT tables.
+ Publish the appropriate SSDT based on current configuration and capabilities.
+
+ @retval EFI_SUCCESS - On success
+ @retval EFI_NOT_FOUND - Required firmware volume not found
+ @retval - Appropiate failure code on error
+**/
+EFI_STATUS
+InitializePpmAcpiTable (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE *HandleBuffer;
+ UINTN NumberOfHandles;
+ EFI_FV_FILETYPE FileType;
+ UINT32 FvStatus;
+ EFI_FV_FILE_ATTRIBUTES Attributes;
+ UINTN Size;
+ UINTN i;
+ EFI_FIRMWARE_VOLUME_PROTOCOL *FwVol;
+ INTN Instance;
+ EFI_ACPI_TABLE_VERSION Version;
+ EFI_ACPI_COMMON_HEADER *CurrentTable;
+ EFI_ACPI_DESCRIPTION_HEADER *TempTable;
+ UINTN AcpiTableHandle;
+
+ ///
+ /// Locate Firmware volume protocol.
+ /// There is little chance we can't find an FV protocol
+ ///
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiFirmwareVolumeProtocolGuid,
+ NULL,
+ &NumberOfHandles,
+ &HandleBuffer
+ );
+ ASSERT_EFI_ERROR (Status);
+ ///
+ /// Look for FV with ACPI storage file
+ ///
+ FwVol = NULL;
+ for (i = 0; i < NumberOfHandles; i++) {
+ ///
+ /// Get the protocol on this handle
+ /// This should not fail because of LocateHandleBuffer
+ ///
+ Status = gBS->HandleProtocol (
+ HandleBuffer[i],
+ &gEfiFirmwareVolumeProtocolGuid,
+ (VOID **) &FwVol
+ );
+ ASSERT_EFI_ERROR (Status);
+ ///
+ /// See if it has the ACPI storage file
+ ///
+ Size = 0;
+ FvStatus = 0;
+ Status = FwVol->ReadFile (
+ FwVol,
+ &gPowerMgmtAcpiTableStorageGuid,
+ NULL,
+ &Size,
+ &FileType,
+ &Attributes,
+ &FvStatus
+ );
+ ///
+ /// If we found it, then we are done
+ ///
+ if (Status == EFI_SUCCESS) {
+ break;
+ }
+ }
+ ///
+ /// Our exit status is determined by the success of the previous operations
+ /// If the protocol was found, Instance already points to it.
+ /// Free any allocated buffers
+ ///
+ FreePool (HandleBuffer);
+ ///
+ /// Sanity check that we found our data file
+ ///
+ ASSERT (FwVol != NULL);
+ if (FwVol == NULL) {
+ return EFI_NOT_FOUND;
+ }
+ ///
+ /// By default, a table belongs in all ACPI table versions published.
+ ///
+ Version = EFI_ACPI_TABLE_VERSION_1_0B | EFI_ACPI_TABLE_VERSION_2_0 | EFI_ACPI_TABLE_VERSION_3_0;
+ ///
+ /// Read tables from the storage file.
+ ///
+ Instance = 0;
+ CurrentTable = NULL;
+ while (Status == EFI_SUCCESS) {
+ Status = FwVol->ReadSection (
+ FwVol,
+ &gPowerMgmtAcpiTableStorageGuid,
+ EFI_SECTION_RAW,
+ Instance,
+ (VOID **) &CurrentTable,
+ &Size,
+ &FvStatus
+ );
+ if (!EFI_ERROR (Status)) {
+ ///
+ /// Check the table ID to modify the table
+ ///
+ switch (((EFI_ACPI_DESCRIPTION_HEADER *) CurrentTable)->OemTableId) {
+ case (EFI_SIGNATURE_64 ('C', 'p', 'u', '0', 'I', 's', 't', 0)):
+ mCpu0IstTable = (EFI_ACPI_DESCRIPTION_HEADER *) CurrentTable;
+ if (mPpmGlobalNvsAreaProtocol->Area->PpmFlags & PPM_EIST) {
+ ///
+ /// Patch the native _PSS package with the GV3 values
+ ///
+ Status = AcpiPatchPss ();
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+ break;
+ case (EFI_SIGNATURE_64 ('C', 'p', 'u', '0', 'C', 's', 't', 0)):
+ mCpu0CstTable = (EFI_ACPI_DESCRIPTION_HEADER *) CurrentTable;
+ break;
+ case (EFI_SIGNATURE_64 ('C', 'p', 'u', '0', 'T', 's', 't', 0)):
+ mCpu0TstTable = (EFI_ACPI_DESCRIPTION_HEADER *) CurrentTable;
+ break;
+ case (EFI_SIGNATURE_64 ('A', 'p', 'I', 's', 't', 0, 0, 0)):
+ mApIstTable = (EFI_ACPI_DESCRIPTION_HEADER *) CurrentTable;
+ break;
+ case (EFI_SIGNATURE_64 ('A', 'p', 'C', 's', 't', 0, 0, 0)):
+ mApCstTable = (EFI_ACPI_DESCRIPTION_HEADER *) CurrentTable;
+ break;
+ case (EFI_SIGNATURE_64 ('A', 'p', 'T', 's', 't', 0, 0, 0)):
+ mApTstTable = (EFI_ACPI_DESCRIPTION_HEADER *) CurrentTable;
+ break;
+ case (EFI_SIGNATURE_64 ('C', 'p', 'u', 'P', 'm', 0, 0, 0)):
+ mCpuPmTable = (EFI_ACPI_DESCRIPTION_HEADER *) CurrentTable;
+ break;
+ case (EFI_SIGNATURE_64 ('L', 'a', 'k', 'e', 'T','i', 'n', 'y')):
+ mLakeTinyTable = (EFI_ACPI_DESCRIPTION_HEADER *) CurrentTable;
+ break;
+ case (EFI_SIGNATURE_64 ('C', 't', 'd', 'p', 'B', 0, 0, 0)):
+ mCtdpTable = (EFI_ACPI_DESCRIPTION_HEADER *) CurrentTable;
+ break;
+ default:
+ break;
+ }
+ Instance++; // Increment the instance
+ CurrentTable = NULL;
+ }
+ }
+ ///
+ /// Statically load IST SSDT if EIST is enabled
+ ///
+ if (mPpmGlobalNvsAreaProtocol->Area->PpmFlags & PPM_EIST) {
+ AcpiTableHandle = 0;
+ Status = mAcpiTable->InstallAcpiTable (
+ mAcpiTable,
+ mCpu0IstTable,
+ mCpu0IstTable->Length,
+ &AcpiTableHandle
+ );
+ //
+ // Free this table as it has been copied into ACPI tables
+ //
+ FreePool (mCpu0IstTable);
+ }
+ ///
+ /// If we are CMP, then the PPM tables are dynamically loaded:
+ /// We need to publish the CpuPm table to the ACPI tables, and move the CST
+ /// tables that are dynamically loaded to a separate location so that we can fix the
+ /// addresses in the CpuPm table.
+ /// Otherwise (non-CMP):
+ /// We need to publish CPU 0 tables only, and CST tables only if CST is enabled
+ ///
+ if (mPpmGlobalNvsAreaProtocol->Area->PpmFlags & PPM_CMP) {
+ //
+ // Copy tables to our own location and checksum them
+ //
+ Status = (gBS->AllocatePool) (EfiReservedMemoryType, mApIstTable->Length, (VOID **) &TempTable);
+ ASSERT_EFI_ERROR (Status);
+ CopyMem (TempTable, mApIstTable, mApIstTable->Length);
+ FreePool (mApIstTable);
+ mApIstTable = TempTable;
+ AcpiChecksum (mApIstTable, mApIstTable->Length, EFI_FIELD_OFFSET (EFI_ACPI_DESCRIPTION_HEADER, Checksum));
+ Status = (gBS->AllocatePool) (EfiReservedMemoryType, mCpu0CstTable->Length, (VOID **) &TempTable);
+ ASSERT_EFI_ERROR (Status);
+ CopyMem (TempTable, mCpu0CstTable, mCpu0CstTable->Length);
+ FreePool (mCpu0CstTable);
+ mCpu0CstTable = TempTable;
+ AcpiChecksum (mCpu0CstTable, mCpu0CstTable->Length, EFI_FIELD_OFFSET (EFI_ACPI_DESCRIPTION_HEADER, Checksum));
+ Status = (gBS->AllocatePool) (EfiReservedMemoryType, mApCstTable->Length, (VOID **) &TempTable);
+ ASSERT_EFI_ERROR (Status);
+ CopyMem (TempTable, mApCstTable, mApCstTable->Length);
+ FreePool (mApCstTable);
+ mApCstTable = TempTable;
+ AcpiChecksum (mApCstTable, mApCstTable->Length, EFI_FIELD_OFFSET (EFI_ACPI_DESCRIPTION_HEADER, Checksum));
+ } else {
+ //
+ // CMP disabled, so statically load the tables
+ //
+ // Add CST SSDT if C states are enabled
+ //
+ if (mPpmGlobalNvsAreaProtocol->Area->PpmFlags & PPM_C_STATES) {
+ AcpiTableHandle = 0;
+ Status = mAcpiTable->InstallAcpiTable (
+ mAcpiTable,
+ mCpu0CstTable,
+ mCpu0CstTable->Length,
+ &AcpiTableHandle
+ );
+ }
+ ///
+ /// Since we are UP, there is no need for the CPU 1 tables
+ ///
+ ///
+ /// Free all tables, since they have been copied into ACPI tables by ACPI support protocol
+ ///
+ FreePool (mCpu0CstTable);
+ FreePool (mApIstTable);
+ FreePool (mApCstTable);
+ }
+ ///
+ /// Update the CpuPm SSDT table in the ACPI tables.
+ ///
+ PatchCpuPmTable ();
+ AcpiTableHandle = 0;
+ Status = mAcpiTable->InstallAcpiTable (
+ mAcpiTable,
+ mCpuPmTable,
+ mCpuPmTable->Length,
+ &AcpiTableHandle
+ );
+ FreePool (mCpuPmTable);
+ if (mPpmGlobalNvsAreaProtocol->Area->PpmFlags & PPM_TSTATES) {
+ ///
+ /// Load the Cpu0Tst SSDT table in the ACPI tables
+ ///
+ AcpiTableHandle = 0;
+ Status = mAcpiTable->InstallAcpiTable (
+ mAcpiTable,
+ mCpu0TstTable,
+ mCpu0TstTable->Length,
+ &AcpiTableHandle
+ );
+ FreePool (mCpu0TstTable);
+ ///
+ /// If the CMP is enabled then load the ApTst SSDT table in the ACPI tables
+ ///
+ if (mPpmGlobalNvsAreaProtocol->Area->PpmFlags & PPM_CMP) {
+ AcpiTableHandle = 0;
+ Status = mAcpiTable->InstallAcpiTable (
+ mAcpiTable,
+ mApTstTable,
+ mApTstTable->Length,
+ &AcpiTableHandle
+ );
+ }
+ }
+ FreePool (mApTstTable);
+ ///
+ /// Load LakeTiny SSDT only when it is enabled in policy and laketiny SSDT is included.
+ ///
+ if ((mCpuPmConfig->pFunctionEnables->LakeTiny) && (mLakeTinyTable != NULL)) {
+ AcpiTableHandle = 0;
+ Status = mAcpiTable->InstallAcpiTable (
+ mAcpiTable,
+ mLakeTinyTable,
+ mLakeTinyTable->Length,
+ &AcpiTableHandle
+ );
+ FreePool (mLakeTinyTable);
+ }
+ ///
+ /// Load Ctdp SSDT
+ ///
+ if (mCpuPmConfig->pTurboSettings->ConfigTdpBios == 1) {
+ AcpiTableHandle = 0;
+ Status = mAcpiTable->InstallAcpiTable (
+ mAcpiTable,
+ mCtdpTable,
+ mCtdpTable->Length,
+ &AcpiTableHandle
+ );
+ FreePool (mCtdpTable);
+ }
+
+ return Status;
+} \ No newline at end of file