/** @file CPU Platform Lib implementation. Copyright (c) 2019 - 2020 Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "CpuPlatformLibrary.h" #include #include #define SKIP_MICROCODE_CHECKSUM_CHECK 1 #define C6DRAM_ENABLE 1 #define C6DRAM_DISABLE 0 /** Return CPU Family ID @retval CPU_FAMILY CPU Family ID **/ CPU_FAMILY EFIAPI GetCpuFamily ( VOID ) { EFI_CPUID_REGISTER Cpuid; /// /// Read the CPUID information /// AsmCpuid (CPUID_VERSION_INFO, &Cpuid.RegEax, &Cpuid.RegEbx, &Cpuid.RegEcx, &Cpuid.RegEdx); return ((CPU_FAMILY) (Cpuid.RegEax & CPUID_FULL_FAMILY_MODEL)); } /** Return Cpu stepping type @retval UINT8 Cpu stepping type **/ CPU_STEPPING EFIAPI GetCpuStepping ( VOID ) { EFI_CPUID_REGISTER Cpuid; /// /// Read the CPUID information /// AsmCpuid (CPUID_VERSION_INFO, &Cpuid.RegEax, &Cpuid.RegEbx, &Cpuid.RegEcx, &Cpuid.RegEdx); return ((CPU_STEPPING) (Cpuid.RegEax & CPUID_FULL_STEPPING)); } /** Return CPU Sku @retval UINT8 CPU Sku **/ UINT8 EFIAPI GetCpuSku ( VOID ) { UINT8 CpuType; UINT16 CpuDid; UINT32 CpuFamilyModel; EFI_CPUID_REGISTER Cpuid; BOOLEAN SkuFound; SkuFound = TRUE; CpuType = EnumCpuUnknown; /// /// Read the CPUID & DID information /// AsmCpuid (CPUID_VERSION_INFO, &Cpuid.RegEax, &Cpuid.RegEbx, &Cpuid.RegEcx, &Cpuid.RegEdx); CpuFamilyModel = Cpuid.RegEax & CPUID_FULL_FAMILY_MODEL; CpuDid = PciSegmentRead16 (PCI_SEGMENT_LIB_ADDRESS (SA_SEG_NUM, SA_MC_BUS, SA_MC_DEV, SA_MC_FUN, R_SA_MC_DEVICE_ID)); switch (CpuFamilyModel) { case CPUID_FULL_FAMILY_MODEL_COMETLAKE_ULT: switch (CpuDid) { case V_SA_DEVICE_ID_CML_ULT_1: // CML ULT case V_SA_DEVICE_ID_CML_ULT_2: // CML ULT case V_SA_DEVICE_ID_CML_ULT_3: // CML ULT CpuType = EnumCpuUlt; break; } break; case CPUID_FULL_FAMILY_MODEL_COFFEELAKE_ULT_ULX: switch (CpuDid) { case V_SA_DEVICE_ID_KBL_MB_ULT_1: // KBL ULT OPI case V_SA_DEVICE_ID_CFL_ULT_1: // CFL ULT case V_SA_DEVICE_ID_CFL_ULT_2: // CFL ULT case V_SA_DEVICE_ID_CFL_ULT_3: // CFL ULT case V_SA_DEVICE_ID_CFL_ULT_4: // CFL ULT case V_SA_DEVICE_ID_CML_ULT_1: // CML ULT case V_SA_DEVICE_ID_CML_ULT_2: // CML ULT case V_SA_DEVICE_ID_CML_ULT_3: // CML ULT CpuType = EnumCpuUlt; break; default: SkuFound = FALSE; break; } break; case CPUID_FULL_FAMILY_MODEL_COFFEELAKE_DT_HALO: switch (CpuDid) { case V_SA_DEVICE_ID_KBL_DT_2: // DT case V_SA_DEVICE_ID_KBL_SVR_2: // Server case V_SA_DEVICE_ID_CFL_DT_1: // DT case V_SA_DEVICE_ID_CFL_DT_2: // DT case V_SA_DEVICE_ID_CFL_DT_3: // DT case V_SA_DEVICE_ID_CFL_DT_4: // DT case V_SA_DEVICE_ID_CFL_WS_1: // WorkStation case V_SA_DEVICE_ID_CFL_WS_2: // Workstation case V_SA_DEVICE_ID_CFL_WS_3: // Workstation case V_SA_DEVICE_ID_CFL_SVR_1: // Server case V_SA_DEVICE_ID_CFL_SVR_2: // Server case V_SA_DEVICE_ID_CFL_SVR_3: // Server CpuType = EnumCpuTrad; break; case V_SA_DEVICE_ID_KBL_HALO_2: // Halo case V_SA_DEVICE_ID_CFL_HALO_1: // Halo case V_SA_DEVICE_ID_CFL_HALO_2: // Halo case V_SA_DEVICE_ID_CFL_HALO_3: // Halo CpuType = EnumCpuHalo; break; default: SkuFound = FALSE; break; } break; default: SkuFound = FALSE; break; } #ifdef CFL_SIMICS CpuType = EnumCpuTrad; #else if (!SkuFound) { DEBUG ((DEBUG_ERROR, "Unsupported CPU SKU, Device ID: 0x%02X, CPUID: 0x%08X!\n", CpuDid, CpuFamilyModel)); ASSERT (FALSE); } #endif return CpuType; } /** Returns the processor microcode revision of the processor installed in the system. @retval Processor Microcode Revision **/ UINT32 GetCpuUcodeRevision ( VOID ) { AsmWriteMsr64 (MSR_IA32_BIOS_SIGN_ID, 0); AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, NULL); return (UINT32) RShiftU64 (AsmReadMsr64 (MSR_IA32_BIOS_SIGN_ID), 32); } /** Verify the DWORD type checksum @param[in] ChecksumAddr - The start address to be checkumed @param[in] ChecksumLen - The length of data to be checksumed @retval EFI_SUCCESS - Checksum correct @retval EFI_CRC_ERROR - Checksum incorrect **/ EFI_STATUS Checksum32Verify ( IN UINT32 *ChecksumAddr, IN UINT32 ChecksumLen ) { #if SKIP_MICROCODE_CHECKSUM_CHECK return EFI_SUCCESS; #else UINT32 Checksum; UINT32 Index; Checksum = 0; for (Index = 0; Index < ChecksumLen; Index++) { Checksum += ChecksumAddr[Index]; } return (Checksum == 0) ? EFI_SUCCESS : EFI_CRC_ERROR; #endif } /** This function checks the MCU revision to decide if BIOS needs to load microcode. @param[in] MicrocodePointer - Microcode in memory @param[in] Revision - Current CPU microcode revision @retval EFI_SUCCESS - BIOS needs to load microcode @retval EFI_ABORTED - Don't need to update microcode **/ EFI_STATUS CheckMcuRevision ( IN CPU_MICROCODE_HEADER *MicrocodePointer, IN UINT32 Revision ) { EFI_STATUS Status; Status = EFI_ABORTED; if ((MicrocodePointer->UpdateRevision & 0x80000000) || (MicrocodePointer->UpdateRevision > Revision) || (Revision == 0)) { Status = EFI_SUCCESS; } return Status; } /** Check if this microcode is correct one for processor @param[in] Cpuid - processor CPUID @param[in] MicrocodeEntryPoint - entry point of microcode @param[in] Revision - revision of microcode @retval CorrectMicrocode if this microcode is correct **/ BOOLEAN CheckMicrocode ( IN UINT32 Cpuid, IN CPU_MICROCODE_HEADER *MicrocodeEntryPoint, IN UINT32 *Revision ) { EFI_STATUS Status; UINT8 ExtendedIndex; MSR_IA32_PLATFORM_ID_REGISTER Msr; UINT32 ExtendedTableLength; UINT32 ExtendedTableCount; BOOLEAN CorrectMicrocode; CPU_MICROCODE_EXTENDED_TABLE *ExtendedTable; CPU_MICROCODE_EXTENDED_TABLE_HEADER *ExtendedTableHeader; Status = EFI_NOT_FOUND; ExtendedTableLength = 0; CorrectMicrocode = FALSE; if (MicrocodeEntryPoint == NULL) { return FALSE; } Msr.Uint64 = AsmReadMsr64 (MSR_IA32_PLATFORM_ID); /// /// Check if the microcode is for the Cpu and the version is newer /// and the update can be processed on the platform /// if ((MicrocodeEntryPoint->HeaderVersion == 0x00000001) && !EFI_ERROR (CheckMcuRevision (MicrocodeEntryPoint, *Revision)) ) { if ((MicrocodeEntryPoint->ProcessorId == Cpuid) && (MicrocodeEntryPoint->ProcessorFlags & (1 << (UINT8) Msr.Bits.PlatformId))) { if (MicrocodeEntryPoint->DataSize == 0) { Status = Checksum32Verify ((UINT32 *) MicrocodeEntryPoint, 2048 / sizeof (UINT32)); } else { Status = Checksum32Verify ( (UINT32 *) MicrocodeEntryPoint, (MicrocodeEntryPoint->DataSize + sizeof (CPU_MICROCODE_HEADER)) / sizeof (UINT32) ); } if (!EFI_ERROR (Status)) { CorrectMicrocode = TRUE; } } else if ((MicrocodeEntryPoint->DataSize != 0)) { /// /// Check the Extended Signature if the entended signature exist /// Only the data size != 0 the extended signature may exist /// ExtendedTableLength = MicrocodeEntryPoint->TotalSize - (MicrocodeEntryPoint->DataSize + sizeof (CPU_MICROCODE_HEADER)); if (ExtendedTableLength != 0) { /// /// Extended Table exist, check if the CPU in support list /// ExtendedTableHeader = (CPU_MICROCODE_EXTENDED_TABLE_HEADER *) ((UINT8 *) (MicrocodeEntryPoint) + MicrocodeEntryPoint->DataSize + 48); /// /// Calulate Extended Checksum /// if ((ExtendedTableLength % 4) == 0) { Status = Checksum32Verify ((UINT32 *) ExtendedTableHeader, ExtendedTableLength / sizeof (UINT32)); if (!EFI_ERROR (Status)) { /// /// Checksum correct /// ExtendedTableCount = ExtendedTableHeader->ExtendedSignatureCount; ExtendedTable = (CPU_MICROCODE_EXTENDED_TABLE *) (ExtendedTableHeader + 1); for (ExtendedIndex = 0; ExtendedIndex < ExtendedTableCount; ExtendedIndex++) { /// /// Verify Header /// if ((ExtendedTable->ProcessorSignature == Cpuid) && (ExtendedTable->ProcessorFlag & (1 << (UINT8) Msr.Bits.PlatformId))) { Status = Checksum32Verify ( (UINT32 *) ExtendedTable, sizeof (CPU_MICROCODE_EXTENDED_TABLE) / sizeof (UINT32) ); if (!EFI_ERROR (Status)) { /// /// Find one /// CorrectMicrocode = TRUE; break; } } ExtendedTable++; } } } } } } return CorrectMicrocode; } /** Check on the processor if SGX is supported. @retval TRUE if SGX supported @retval FALSE if SGX is not supported **/ BOOLEAN IsSgxSupported ( VOID ) { EFI_CPUID_REGISTER CpuidRegs; // // Processor support SGX feature by reading CPUID.(EAX=7,ECX=0):EBX[2] // AsmCpuidEx (CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS, 0, &CpuidRegs.RegEax,&CpuidRegs.RegEbx,&CpuidRegs.RegEcx,&CpuidRegs.RegEdx); /// /// SGX feature is supported with CPUID.(EAX=7,ECX=0):EBX[2]=1 /// PRMRR configuration enabled, MSR IA32_MTRRCAP (FEh) [12] == 1 /// if (((CpuidRegs.RegEbx & BIT2)) && (AsmReadMsr64 (MSR_IA32_MTRRCAP) & BIT12)) { return TRUE; } return FALSE; } /** Get processor generation @retval CPU_GENERATION Returns the executing thread's processor generation. **/ CPU_GENERATION GetCpuGeneration ( VOID ) { EFI_CPUID_REGISTER Cpuid; CPU_FAMILY CpuFamilyModel; CPU_GENERATION CpuGeneration; CpuGeneration = EnumCflCpu; /// /// Read the CPUID information /// AsmCpuid (CPUID_VERSION_INFO, &Cpuid.RegEax, &Cpuid.RegEbx, &Cpuid.RegEcx, &Cpuid.RegEdx); CpuFamilyModel = (CPU_FAMILY) (Cpuid.RegEax & CPUID_FULL_FAMILY_MODEL); switch (CpuFamilyModel) { case EnumCpuCflUltUlx: case EnumCpuCflDtHalo: CpuGeneration = EnumCflCpu; break; case EnumCpuCmlUlt: CpuGeneration = EnumCmlCpu; break; default: CpuGeneration = EnumCpuUnknownGeneration; ASSERT (FALSE); break; } return CpuGeneration; } /** Is Whiskey Lake CPU. @retval TRUE The CPUID corresponds with a Whiskey Lake CPU @retval FALSE The CPUID does not correspond with a Whiskey Lake CPU **/ BOOLEAN IsWhlCpu ( VOID ) { CPU_FAMILY CpuFamily; CPU_STEPPING CpuStepping; CpuFamily = GetCpuFamily (); CpuStepping = GetCpuStepping (); // // Check if it is Whiskey Lake CPU // if ((CpuFamily == EnumCpuCflUltUlx) && ((CpuStepping == EnumCflW0) || (CpuStepping == EnumCflV0))) { return TRUE; } return FALSE; }