/** @file CPU Platform Lib implementation. Copyright (c) 2017, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "CpuPlatformLibrary.h" #include #include #define SKIP_MICROCODE_CHECKSUM_CHECK 1 /** 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 = MmioRead16 (MmPciBase (0, 0, 0) + 2); switch (CpuFamilyModel) { case CPUID_FULL_FAMILY_MODEL_SKYLAKE_ULT_ULX: switch (CpuDid) { case V_SA_DEVICE_ID_SKL_MB_ULT_1: // ULT OPI case V_SA_DEVICE_ID_KBL_MB_ULT_1: // KBL ULT OPI CpuType = EnumCpuUlt; break; case V_SA_DEVICE_ID_SKL_MB_ULX_2: // ULX OPI case V_SA_DEVICE_ID_SKL_MB_ULX_3: // ULX OPI case V_SA_DEVICE_ID_KBL_MB_ULX_1: // ULX OPI CpuType = EnumCpuUlx; break; default: SkuFound = FALSE; break; } break; case CPUID_FULL_FAMILY_MODEL_SKYLAKE_DT_HALO: switch (CpuDid) { case V_SA_DEVICE_ID_SKL_DT_1: // DT case V_SA_DEVICE_ID_SKL_DT_2: // DT case V_SA_DEVICE_ID_KBL_DT_1: // DT case V_SA_DEVICE_ID_KBL_DT_2: // DT CpuType = EnumCpuTrad; break; case V_SA_DEVICE_ID_SKL_HALO_1: // Halo case V_SA_DEVICE_ID_SKL_HALO_2: // Halo case V_SA_DEVICE_ID_KBL_HALO_1: // Halo case V_SA_DEVICE_ID_KBL_HALO_2: // Halo case V_SA_DEVICE_ID_SKL_SVR_1: // Server case V_SA_DEVICE_ID_SKL_SVR_2: // Server case V_SA_DEVICE_ID_KBL_SVR_1: // Server case V_SA_DEVICE_ID_KBL_SVR_2: // Server CpuType = EnumCpuHalo; break; default: SkuFound = FALSE; break; } break; case CPUID_FULL_FAMILY_MODEL_KABYLAKE_ULT_ULX: switch (CpuDid) { case V_SA_DEVICE_ID_KBL_MB_ULT_1: // KBL ULT OPI case V_SA_DEVICE_ID_KBLR_MB_ULT_1: // KBL-R ULT CpuType = EnumCpuUlt; break; case V_SA_DEVICE_ID_KBL_MB_ULX_1: // ULX OPI CpuType = EnumCpuUlx; break; default: SkuFound = FALSE; break; } break; case CPUID_FULL_FAMILY_MODEL_KABYLAKE_DT_HALO: switch (CpuDid) { case V_SA_DEVICE_ID_KBL_DT_1: // DT case V_SA_DEVICE_ID_KBL_DT_2: // DT case V_SA_DEVICE_ID_CFL_DT: // DT CpuType = EnumCpuTrad; break; case V_SA_DEVICE_ID_KBL_HALO_1: // Halo case V_SA_DEVICE_ID_KBL_HALO_2: // Halo case V_SA_DEVICE_ID_KBL_SVR_1: // Server case V_SA_DEVICE_ID_KBL_SVR_2: // Server CpuType = EnumCpuHalo; break; default: SkuFound = FALSE; break; } break; default: SkuFound = FALSE; break; } if (!SkuFound) { DEBUG ((DEBUG_ERROR, "Unsupported CPU SKU, Device ID: 0x%02X, CPUID: 0x%08X!\n", CpuDid, CpuFamilyModel)); ASSERT (FALSE); } 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. @dot digraph G { subgraph cluster_c0 { node [shape = box]; b1[label="Read CPUID(EAX=7,ECX=0):EBX[2] \nto check SGX feature" fontsize=12 style=filled color=lightblue]; b2[label="Return TRUE" fontsize=12 style=filled color=lightblue]; b3[label="Return FALSE" fontsize=12 style=filled color=lightblue]; node [shape = ellipse]; e1[label="Start" fontsize=12 style=filled color=lightblue]; e2[label="End" fontsize=12 style=filled color=lightblue]; node [shape = diamond,style=filled,color=lightblue]; d1[label="Are SGX feature supported and \nPRMRR configuration enabled" fontsize=12]; label = "IsSgxSupported Flow"; fontsize=15; fontcolor=black; color=lightblue; e1 -> b1 b1 -> d1 d1 -> b2 [label="Yes" fontsize=9] d1 -> b3 [label="No" fontsize=9] b2 -> e2 b3 -> e2 } } @enddot @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 only on SKL and later, /// 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 EnumSklCpu Executing thread is Skylake @retval EnumKblCpu Executing thread is Kabylake **/ CPU_GENERATION GetCpuGeneration ( VOID ) { EFI_CPUID_REGISTER Cpuid; CPU_FAMILY CpuFamilyModel; CPU_STEPPING CpuStepping; CPU_GENERATION CpuGeneration; CpuGeneration = EnumSklCpu; /// /// 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); CpuStepping = (CPU_STEPPING) (Cpuid.RegEax & CPUID_FULL_STEPPING); switch (CpuFamilyModel) { case EnumCpuKblUltUlx: case EnumCpuKblDtHalo: CpuGeneration = EnumKblCpu; break; case EnumCpuSklUltUlx: case EnumCpuSklDtHalo: if (((CpuStepping > EnumSklMaxUltUlxStep) && (CpuStepping <= EnumKblMaxUltUlxStep)) || ((CpuStepping > EnumSklMaxDtHaloStep) && (CpuStepping <= EnumKblMaxDtHaloStep))) { CpuGeneration = EnumKblCpu; } break; default: CpuGeneration = EnumSklCpu; break; } return CpuGeneration; }