diff options
Diffstat (limited to 'ReferenceCode/Haswell/PowerManagement/Dxe/MiscFunctions.c')
-rw-r--r-- | ReferenceCode/Haswell/PowerManagement/Dxe/MiscFunctions.c | 853 |
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 |