/** @file CPU Common Lib implementation. @copyright INTEL CONFIDENTIAL Copyright 2014 - 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define INTERRUPT_VECTOR_NUMBER 256 /// /// Table to convert Seconds into equivalent MSR values /// This table is used for PL1, Pl2 and RATL TDP Time Window programming /// GLOBAL_REMOVE_IF_UNREFERENCED UINT16 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 }, { 160, 0x31 }, { 192, 0x51 }, { 224, 0x71 }, { 256, 0x12 }, { 320, 0x32 }, { 384, 0x52 }, { 448, 0x72 }, { END_OF_TIMETABLE, END_OF_TIMETABLE } }; /// /// Table to convert Milli Seconds into equivalent MSR values /// This table is used for Pl3 and RATL TDP Time Window programming /// GLOBAL_REMOVE_IF_UNREFERENCED UINT16 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 }, { 55, 0x65 }, { 56, 0x65 }, { 64, 0x06 }, { 156, 0x27 }, { 375, 0x48 }, { 500, 0x09 }, { 750, 0x49 }, { END_OF_TIMETABLE, END_OF_TIMETABLE } }; /** Set up flags in CR4 for XMM instruction enabling **/ VOID EFIAPI XmmInit ( VOID ) { CPUID_VERSION_INFO_EDX CpuidVersionInfoEdx; UINTN Cr0; UINTN Cr4; /// /// Read the CPUID information /// AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &CpuidVersionInfoEdx.Uint32); /// /// Check whether SSE2 is supported /// if (CpuidVersionInfoEdx.Bits.SSE2 == 1) { /// /// Enable XMM /// Cr0 = AsmReadCr0 (); Cr0 |= BIT1; AsmWriteCr0 (Cr0); Cr4 = AsmReadCr4 (); Cr4 |= (UINTN) (BIT9 | BIT10); AsmWriteCr4 (Cr4); } } /** Private helper function to convert various Turbo Power Limit Time from Seconds to CPU units @param[in] TimeInSeconds Time in seconds @param[in] TimeWindowConvType Time Window Convert Type @retval UINT8 Converted time in CPU units **/ UINT8 GetConvertedTime ( IN UINT32 TimeInSeconds, IN TIME_WINDOW_CONV TimeWindowConvType ) { 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 (TimeWindowConvType) { case SecondsTimeWindowConvert: ConvertedPowerLimitTime = (UINT8) mSecondsToMsrValueMapTable[0][1]; for (Index = 0; mSecondsToMsrValueMapTable[Index][0] != END_OF_TIMETABLE; Index++) { if (TimeInSeconds == mSecondsToMsrValueMapTable[Index][0]) { ConvertedPowerLimitTime = (UINT8) mSecondsToMsrValueMapTable[Index][1]; break; } if (TimeInSeconds > mSecondsToMsrValueMapTable[Index][0]) { ConvertedPowerLimitTime = (UINT8) mSecondsToMsrValueMapTable[Index][1]; } else { break; } } break; case MilliSecondsTimeWindowConvert: ConvertedPowerLimitTime = (UINT8) mMilliSecondsToMsrValueMapTable[0][1]; for (Index = 0; mMilliSecondsToMsrValueMapTable[Index][0] != END_OF_TIMETABLE; Index++) { if (TimeInSeconds == mMilliSecondsToMsrValueMapTable[Index][0]) { ConvertedPowerLimitTime = (UINT8) mMilliSecondsToMsrValueMapTable[Index][1]; break; } if (TimeInSeconds > mMilliSecondsToMsrValueMapTable[Index][0]) { ConvertedPowerLimitTime = (UINT8) mMilliSecondsToMsrValueMapTable[Index][1]; } else { break; } } break; default: break; } return ConvertedPowerLimitTime; } /** Get APIC ID of processor @retval APIC ID of processor **/ UINT32 GetCpuApicId ( VOID ) { CPUID_VERSION_INFO_EBX CpuidVersionInfoEbx; AsmCpuid ( CPUID_VERSION_INFO, NULL, &CpuidVersionInfoEbx.Uint32, NULL, NULL ); return (UINT32) (CpuidVersionInfoEbx.Bits.InitialLocalApicId); } /** Programs XAPIC registers. @param[in] Bsp - Is this BSP? **/ VOID ProgramXApic ( BOOLEAN Bsp ) { UINT64 ApicBaseReg; EFI_PHYSICAL_ADDRESS ApicBase; volatile UINT32 *EntryAddress; UINT32 EntryValue; ApicBaseReg = AsmReadMsr64 (MSR_IA32_APIC_BASE); ApicBase = ApicBaseReg & 0xffffff000ULL; /// /// Program the spurious vector entry /// EntryAddress = (UINT32 *) (UINTN) (ApicBase + XAPIC_SPURIOUS_VECTOR_OFFSET); EntryValue = *EntryAddress; EntryValue &= 0xFFFFFD0F; EntryValue |= 0x10F; *EntryAddress = EntryValue; /// /// Program the LINT1 vector entry as extINT /// EntryAddress = (UINT32 *) (UINTN) (ApicBase + XAPIC_LVT_LINT0_OFFSET); EntryValue = *EntryAddress; if (Bsp) { EntryValue &= 0xFFFE00FF; EntryValue |= 0x700; } else { EntryValue |= 0x10000; } *EntryAddress = EntryValue; /// /// Program the LINT1 vector entry as NMI /// EntryAddress = (UINT32 *) (UINTN) (ApicBase + XAPIC_LVT_LINT1_OFFSET); EntryValue = *EntryAddress; EntryValue &= 0xFFFE00FF; if (Bsp) { EntryValue |= 0x400; } else { EntryValue |= 0x10400; } *EntryAddress = EntryValue; } /** This function returns the maximum number of cores supported in this physical processor package by leverging CPUID_CACHE_PARAMS with offset 26. @retval Maximum number of supported cores in the package. **/ UINT8 EFIAPI GetMaxSupportedCoreCount ( VOID ) { CPUID_CACHE_PARAMS_EAX CpuidCacheParamsEax; AsmCpuidEx ( CPUID_CACHE_PARAMS, 0, &CpuidCacheParamsEax.Uint32, NULL, NULL, NULL ); return (UINT8) (CpuidCacheParamsEax.Bits.MaximumAddressableIdsForProcessorCores) + 1; } /** This function returns the supported number of threads per core, and supported total threads in the physical processor package by leverging CPUID_EXTENDED_TOPOLOGY with index 1 & 2. @param[out] *ThreadsPerCore - variable that will store Maximum enabled threads per core. @param[out] *Threads - variable that will store supported total threads. **/ VOID EFIAPI GetSupportedThreadCount ( OUT UINT8 *ThreadsPerCore, OPTIONAL OUT UINT8 *Threads OPTIONAL ) { CPUID_EXTENDED_TOPOLOGY_EBX CpuidExtendedTopologyEbx; AsmCpuidEx (CPUID_EXTENDED_TOPOLOGY, 0, NULL, &CpuidExtendedTopologyEbx.Uint32, NULL, NULL); if (ThreadsPerCore != NULL) { *ThreadsPerCore = (UINT8) CpuidExtendedTopologyEbx.Uint32; } if (Threads != NULL) { AsmCpuidEx (CPUID_EXTENDED_TOPOLOGY, 1, NULL, &CpuidExtendedTopologyEbx.Uint32, NULL, NULL); *Threads = (UINT8) CpuidExtendedTopologyEbx.Uint32; } } /** This function returns the enabled Cores and Threads by leverging MSR_CORE_THREAD_COUNT. @param[out] *CoreCount - variable that will store enabled cores. @param[out] *ThreadCount - variable that will store enabled threads. **/ VOID EFIAPI GetEnabledCoreThreadCount ( OUT UINT8 *CoreCount, OPTIONAL OUT UINT8 *ThreadCount OPTIONAL ) { MSR_CORE_THREAD_COUNT_REGISTER MsrCoreThreadCount; /// /// Read MSR_CORE_THREAD_COUNT (0x35) /// MsrCoreThreadCount.Uint64 = AsmReadMsr64 (MSR_CORE_THREAD_COUNT); if (CoreCount != NULL) { *CoreCount = (UINT8) MsrCoreThreadCount.Bits.Corecount; } if (ThreadCount != NULL) { *ThreadCount = (UINT8) MsrCoreThreadCount.Bits.Threadcount; } } /** This function returns the maximum number of dies and packages. Currently, the number of dies and packages will be one for client. @param[out] *NumberOfDiesPerPackage - variable that will store Maximum dies per package @param[out] *NumberOfPackages - variable that will store Maximum Packages **/ VOID EFIAPI CpuGetNumberofDiesAndPackages ( OUT UINT16 *NumberOfDiesPerPackage, OPTIONAL OUT UINT16 *NumberOfPackages OPTIONAL ) { /// /// For client, the number of dies and packages will be one /// if (NumberOfDiesPerPackage != NULL) { *NumberOfDiesPerPackage = 1; } if (NumberOfPackages != NULL) { *NumberOfPackages = 1; } } /** Check to see if the executing thread is BSP @retval TRUE Executing thread is BSP @retval FALSE Executing thread is AP **/ BOOLEAN IsBsp ( VOID ) { MSR_IA32_APIC_BASE_REGISTER Msr; Msr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE); return (BOOLEAN) (Msr.Bits.BSP == 1); } /** Stop PBE timer if system is in Boot Guard boot @retval EFI_SUCCESS - Stop PBE timer @retval EFI_UNSUPPORTED - Not in Boot Guard boot mode. **/ EFI_STATUS StopPbeTimer ( VOID ) { UINT64 BootGuardBootStatus; UINT64 BootGuardOperationMode; MSR_ANC_INITIAL_BOOT_BLOCK_COMPLETE_REGISTER MsrAncInitialBootBlockComplete; if (IsBootGuardSupported ()) { BootGuardBootStatus = (*(UINT64 *) (UINTN) (TXT_PUBLIC_BASE + R_CPU_BOOT_GUARD_BOOTSTATUS) & (BIT63|BIT62)); BootGuardOperationMode = AsmReadMsr64 (MSR_BOOT_GUARD_SACM_INFO) & (B_BOOT_GUARD_SACM_INFO_MEASURED_BOOT | B_BOOT_GUARD_SACM_INFO_VERIFIED_BOOT); // // Stop PBET if Verified/Measured/NEM bit is set in MSR 0x13A or // Boot Guard fails to launch or fails to execute successfully for avoiding brick platform // if (BootGuardBootStatus == V_CPU_BOOT_GUARD_LOAD_ACM_SUCCESS) { if (BootGuardOperationMode == 0) { DEBUG ((DEBUG_INFO, "Platform is in Legacy boot mode.\n")); return EFI_UNSUPPORTED; } else { DEBUG ((DEBUG_INFO, "Platform in Boot Guard Boot mode.\n")); } } else { DEBUG ((DEBUG_ERROR, "Boot Guard ACM launch failed or ACM execution failed.\n")); } DEBUG ((DEBUG_INFO, "Disable PBET\n")); MsrAncInitialBootBlockComplete.Uint64 = 0; MsrAncInitialBootBlockComplete.Bits.Done = 1; AsmWriteMsr64 (MSR_ANC_INITIAL_BOOT_BLOCK_COMPLETE, MsrAncInitialBootBlockComplete.Uint64); } else { DEBUG ((DEBUG_WARN, "Boot Guard is not supported.\n")); return EFI_UNSUPPORTED; } return EFI_SUCCESS; } /** Return if C6DRAM is Enabled. @retval TRUE - C6DRAM is enabled. @retval FALSE - C6DRAM is disabled. **/ BOOLEAN GetC6DramStatus ( VOID ) { UINT32 MailboxStatus; UINT32 MailboxData; EFI_STATUS Status; PCODE_MAILBOX_INTERFACE MailboxCommand; /// /// If C6DRAM is enabled, PCODE mailbox returns mailbox data bit 0 = 1. /// MailboxCommand.InterfaceData = 0; MailboxCommand.Fields.Command = MAILBOX_PCODE_CMD_READ_C6DRAM_CONFIG; Status = MailboxRead (MAILBOX_TYPE_PCODE, MailboxCommand.InterfaceData, &MailboxData, &MailboxStatus); if (Status != EFI_SUCCESS || MailboxStatus != EFI_SUCCESS) { DEBUG ((DEBUG_ERROR, "Mailbox write command failed unexpectedly, C6DRAM is not supported. MailboxStatus = %x, Mailbox command return status %r \n", MailboxStatus, Status)); return FALSE; } return (MailboxData & B_MAILBOX_BIOS_ALLOW_C6DRAM) == B_MAILBOX_BIOS_ALLOW_C6DRAM; } /** Set C6DRAM Enable/Disable and return if enabled or not. @param[in] C6DramStateRequest - Policy setting of C6DRAM @retval TRUE - C6DRAM is enabled. @retval FALSE - C6DRAM is disabled. **/ BOOLEAN SetC6Dram ( UINT32 C6DramStateRequest ) { UINT32 MailboxStatus; UINT32 MailboxData; EFI_STATUS Status; PCODE_MAILBOX_INTERFACE MailboxCommand; MailboxData = 0x0; // // Returning Heuristics Policy Data for C6DRAM. // if (C6DramStateRequest) { MailboxData = B_MAILBOX_BIOS_ALLOW_C6DRAM; } MailboxCommand.InterfaceData = 0; MailboxCommand.Fields.Command = MAILBOX_PCODE_CMD_WRITE_C6DRAM_CONFIG; Status = MailboxWrite (MAILBOX_TYPE_PCODE, MailboxCommand.InterfaceData, MailboxData, &MailboxStatus); if (Status != EFI_SUCCESS || MailboxStatus != EFI_SUCCESS) { DEBUG ((DEBUG_ERROR, "Mailbox write command failed, C6DRAM is not supported. MailboxStatus = %x, Mailbox command return status %x \n", MailboxStatus, Status)); return FALSE; } return GetC6DramStatus (); } /** Check on the processor if PRMRR is supported. @param[in] IsBspInt Check to see if the executing thread is BSP. @retval TRUE if PRMRR supported @retval FALSE if PRMRR is not supported **/ BOOLEAN IsPrmrrSupported ( BOOLEAN IsBspInt ) { /// /// PRMRR configuration enabled, MSR IA32_MTRRCAP (FEh) [12] == 1 /// if ((AsmReadMsr64 (MSR_IA32_MTRRCAP) & BIT12) != 0) { return TRUE; } // // Continue patch load without PRMRR initialization. // if (IsBspInt) { DEBUG ((DEBUG_INFO, "PRMRR configuration not supported.\n")); } return FALSE; } /** Set the PRMRR MSRs. @param PrmrrBase PRMRR base address @param PrmrrSize PRMRR size @retval RETURN_SUCCESS PRMRR MSRs are set successfully. @retval RETURN_UNSUPPORTED PRMRR is not supported. @retval RETURN_ACCESS_DENIED PRMRR MSRs are already locked and cannot be changed. */ RETURN_STATUS SetPrmrrMsr ( UINT64 PrmrrBase, UINT32 PrmrrSize ) { UINT64 ValidPrmrrBitsMask; UINT64 PrmrrMask; UINT32 MaxExtendedFunction; CPUID_VIR_PHY_ADDRESS_SIZE_EAX VirPhyAddressSize; MSR_PRMRR_MASK_REGISTER MsrPrmrrMask; MSR_PRMRR_BASE_0_REGISTER MsrPrmrrBase0; if (!IsPrmrrSupported (TRUE)) { return RETURN_UNSUPPORTED; } MsrPrmrrMask.Uint64 = AsmReadMsr64 (MSR_PRMRR_MASK); ASSERT (MsrPrmrrMask.Bits.L == 0); if (MsrPrmrrMask.Bits.L != 0) { DEBUG ((DEBUG_ERROR, "PRMRR region is already locked and cannot be changed!\n")); return RETURN_ACCESS_DENIED; } // // If PRMRR size is 0, only need to set bit lock on MSR 0x1f5 // if (PrmrrSize == 0) { // // Need to lock mask MSRs even if PRMRR size is zero // DEBUG ((DEBUG_INFO, "PRMRR size is zero, only Locking PRMRR MASK MSR\n")); MsrPrmrrMask.Bits.L = 1; AsmWriteMsr64 (MSR_PRMRR_MASK, MsrPrmrrMask.Uint64); } else { VirPhyAddressSize.Bits.PhysicalAddressBits = 36; // // Check whether CPU supports to return max physical address size // AsmCpuid (CPUID_EXTENDED_FUNCTION, &MaxExtendedFunction, NULL, NULL, NULL); if (MaxExtendedFunction >= CPUID_VIR_PHY_ADDRESS_SIZE) { // // PRMRR Mask should always use the Physical Address bits returned from SoC Capabilities - CPUID.80000008H:EAX // AsmCpuid (CPUID_VIR_PHY_ADDRESS_SIZE, &VirPhyAddressSize.Uint32, NULL, NULL, NULL); } ValidPrmrrBitsMask = (LShiftU64 (1, VirPhyAddressSize.Bits.PhysicalAddressBits) - 1) & 0xfffffffffffff000; PrmrrMask = ValidPrmrrBitsMask & (~((UINT64) (PrmrrSize - 1))); // // Set the PRMRR Base MSR with base, memory type, and configured status.Set PRMRR Mask MSR with the mask and lock bit. // The valid bit on PRMRR Mask MSR will be set later by the microcode. // MsrPrmrrBase0.Uint64 = PrmrrBase; MsrPrmrrBase0.Bits.Memtype = CACHE_WRITEBACK; MsrPrmrrBase0.Bits.Configured = 1; AsmWriteMsr64 (GetPrmrrBaseMsrAddress (), MsrPrmrrBase0.Uint64); MsrPrmrrMask.Uint64 = PrmrrMask; MsrPrmrrMask.Bits.L = 1; AsmWriteMsr64 (MSR_PRMRR_MASK, MsrPrmrrMask.Uint64); } return RETURN_SUCCESS; } /** This function is to check if CPU support XSAVE or not. @param[in] @retval TRUE XSAVE is supported. @retval FALSE XSAVE is unsupported. **/ BOOLEAN IsCpuSupportXsave ( VOID ) { CPUID_VERSION_INFO_ECX Ecx; AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, &Ecx.Uint32, NULL); /// /// Check if cpu supported XSAVE. /// return (BOOLEAN) (Ecx.Bits.XSAVE == 1); }