| /** @file | |
| Implementation of MicrocodeLib. | |
| Copyright (c) 2021, Intel Corporation. All rights reserved.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include <Uefi/UefiBaseType.h> | |
| #include <Register/Intel/Cpuid.h> | |
| #include <Register/Intel/ArchitecturalMsr.h> | |
| #include <Register/Intel/Microcode.h> | |
| #include <Library/BaseLib.h> | |
| #include <Library/DebugLib.h> | |
| #include <Ppi/ShadowMicrocode.h> | |
| /** | |
| Get microcode update signature of currently loaded microcode update. | |
| @return Microcode signature. | |
| **/ | |
| UINT32 | |
| EFIAPI | |
| GetProcessorMicrocodeSignature ( | |
| VOID | |
| ) | |
| { | |
| MSR_IA32_BIOS_SIGN_ID_REGISTER BiosSignIdMsr; | |
| AsmWriteMsr64 (MSR_IA32_BIOS_SIGN_ID, 0); | |
| AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, NULL); | |
| BiosSignIdMsr.Uint64 = AsmReadMsr64 (MSR_IA32_BIOS_SIGN_ID); | |
| return BiosSignIdMsr.Bits.MicrocodeUpdateSignature; | |
| } | |
| /** | |
| Get the processor signature and platform ID for current processor. | |
| @param MicrocodeCpuId Return the processor signature and platform ID. | |
| **/ | |
| VOID | |
| EFIAPI | |
| GetProcessorMicrocodeCpuId ( | |
| EDKII_PEI_MICROCODE_CPU_ID *MicrocodeCpuId | |
| ) | |
| { | |
| MSR_IA32_PLATFORM_ID_REGISTER PlatformIdMsr; | |
| ASSERT (MicrocodeCpuId != NULL); | |
| PlatformIdMsr.Uint64 = AsmReadMsr64 (MSR_IA32_PLATFORM_ID); | |
| MicrocodeCpuId->PlatformId = (UINT8)PlatformIdMsr.Bits.PlatformId; | |
| AsmCpuid (CPUID_VERSION_INFO, &MicrocodeCpuId->ProcessorSignature, NULL, NULL, NULL); | |
| } | |
| /** | |
| Return the total size of the microcode entry. | |
| Logic follows pseudo code in SDM as below: | |
| N = 512 | |
| If (Update.DataSize != 00000000H) | |
| N = Update.TotalSize / 4 | |
| If Microcode is NULL, then ASSERT. | |
| @param Microcode Pointer to the microcode entry. | |
| @return The microcode total size. | |
| **/ | |
| UINT32 | |
| EFIAPI | |
| GetMicrocodeLength ( | |
| IN CPU_MICROCODE_HEADER *Microcode | |
| ) | |
| { | |
| UINT32 TotalSize; | |
| ASSERT (Microcode != NULL); | |
| TotalSize = 2048; | |
| if (Microcode->DataSize != 0) { | |
| TotalSize = Microcode->TotalSize; | |
| } | |
| return TotalSize; | |
| } | |
| /** | |
| Load the microcode to the processor. | |
| If Microcode is NULL, then ASSERT. | |
| @param Microcode Pointer to the microcode entry. | |
| **/ | |
| VOID | |
| EFIAPI | |
| LoadMicrocode ( | |
| IN CPU_MICROCODE_HEADER *Microcode | |
| ) | |
| { | |
| ASSERT (Microcode != NULL); | |
| AsmWriteMsr64 (MSR_IA32_BIOS_UPDT_TRIG, (UINT64)(UINTN)(Microcode + 1)); | |
| } | |
| /** | |
| Determine if a microcode patch matchs the specific processor signature and flag. | |
| @param[in] ProcessorSignature The processor signature field value in a | |
| microcode patch. | |
| @param[in] ProcessorFlags The processor flags field value in a | |
| microcode patch. | |
| @param[in] MicrocodeCpuId A pointer to an array of EDKII_PEI_MICROCODE_CPU_ID | |
| structures. | |
| @param[in] MicrocodeCpuIdCount Number of elements in MicrocodeCpuId array. | |
| @retval TRUE The specified microcode patch matches to one of the MicrocodeCpuId. | |
| @retval FALSE The specified microcode patch doesn't match to any of the MicrocodeCpuId. | |
| **/ | |
| BOOLEAN | |
| IsProcessorMatchedMicrocode ( | |
| IN UINT32 ProcessorSignature, | |
| IN UINT32 ProcessorFlags, | |
| IN EDKII_PEI_MICROCODE_CPU_ID *MicrocodeCpuId, | |
| IN UINTN MicrocodeCpuIdCount | |
| ) | |
| { | |
| UINTN Index; | |
| if (MicrocodeCpuIdCount == 0) { | |
| return TRUE; | |
| } | |
| for (Index = 0; Index < MicrocodeCpuIdCount; Index++) { | |
| if ((ProcessorSignature == MicrocodeCpuId[Index].ProcessorSignature) && | |
| ((ProcessorFlags & (1 << MicrocodeCpuId[Index].PlatformId)) != 0)) | |
| { | |
| return TRUE; | |
| } | |
| } | |
| return FALSE; | |
| } | |
| /** | |
| Detect whether specified processor can find matching microcode patch and load it. | |
| Microcode format is as below: | |
| +----------------------------------------+-------------------------------------------------+ | |
| | CPU_MICROCODE_HEADER | | | |
| +----------------------------------------+ V | |
| | Update Data | CPU_MICROCODE_HEADER.Checksum | |
| +----------------------------------------+-------+ ^ | |
| | CPU_MICROCODE_EXTENDED_TABLE_HEADER | | | | |
| +----------------------------------------+ V | | |
| | CPU_MICROCODE_EXTENDED_TABLE[0] | CPU_MICROCODE_EXTENDED_TABLE_HEADER.Checksum | | |
| | CPU_MICROCODE_EXTENDED_TABLE[1] | ^ | | |
| | ... | | | | |
| +----------------------------------------+-------+-----------------------------------------+ | |
| There may by multiple CPU_MICROCODE_EXTENDED_TABLE in this format. | |
| The count of CPU_MICROCODE_EXTENDED_TABLE is indicated by ExtendedSignatureCount | |
| of CPU_MICROCODE_EXTENDED_TABLE_HEADER structure. | |
| If Microcode is NULL, then ASSERT. | |
| @param Microcode Pointer to a microcode entry. | |
| @param MicrocodeLength The total length of the microcode entry. | |
| @param MinimumRevision The microcode whose revision <= MinimumRevision is treated as invalid. | |
| Caller can supply value get from GetProcessorMicrocodeSignature() to check | |
| whether the microcode is newer than loaded one. | |
| Caller can supply 0 to treat any revision (except 0) microcode as valid. | |
| @param MicrocodeCpuIds Pointer to an array of processor signature and platform ID that represents | |
| a set of processors. | |
| Caller can supply zero-element array to skip the processor signature and | |
| platform ID check. | |
| @param MicrocodeCpuIdCount The number of elements in MicrocodeCpuIds. | |
| @param VerifyChecksum FALSE to skip all the checksum verifications. | |
| @retval TRUE The microcode is valid. | |
| @retval FALSE The microcode is invalid. | |
| **/ | |
| BOOLEAN | |
| EFIAPI | |
| IsValidMicrocode ( | |
| IN CPU_MICROCODE_HEADER *Microcode, | |
| IN UINTN MicrocodeLength, | |
| IN UINT32 MinimumRevision, | |
| IN EDKII_PEI_MICROCODE_CPU_ID *MicrocodeCpuIds, | |
| IN UINTN MicrocodeCpuIdCount, | |
| IN BOOLEAN VerifyChecksum | |
| ) | |
| { | |
| UINTN Index; | |
| UINT32 DataSize; | |
| UINT32 TotalSize; | |
| CPU_MICROCODE_EXTENDED_TABLE *ExtendedTable; | |
| CPU_MICROCODE_EXTENDED_TABLE_HEADER *ExtendedTableHeader; | |
| UINT32 ExtendedTableLength; | |
| UINT32 Sum32; | |
| BOOLEAN Match; | |
| ASSERT (Microcode != NULL); | |
| // | |
| // It's invalid when: | |
| // the input microcode buffer is so small that even cannot contain the header. | |
| // the input microcode buffer is so large that exceeds MAX_ADDRESS. | |
| // | |
| if ((MicrocodeLength < sizeof (CPU_MICROCODE_HEADER)) || (MicrocodeLength > (MAX_ADDRESS - (UINTN)Microcode))) { | |
| return FALSE; | |
| } | |
| // | |
| // Per SDM, HeaderVersion and LoaderRevision should both be 1. | |
| // | |
| if ((Microcode->HeaderVersion != 1) || (Microcode->LoaderRevision != 1)) { | |
| return FALSE; | |
| } | |
| // | |
| // The microcode revision should be larger than the minimum revision. | |
| // | |
| if (Microcode->UpdateRevision <= MinimumRevision) { | |
| return FALSE; | |
| } | |
| DataSize = Microcode->DataSize; | |
| if (DataSize == 0) { | |
| DataSize = 2000; | |
| } | |
| // | |
| // Per SDM, DataSize should be multiple of DWORDs. | |
| // | |
| if ((DataSize % 4) != 0) { | |
| return FALSE; | |
| } | |
| TotalSize = GetMicrocodeLength (Microcode); | |
| // | |
| // Check whether the whole microcode is within the buffer. | |
| // TotalSize should be multiple of 1024. | |
| // | |
| if (((TotalSize % SIZE_1KB) != 0) || (TotalSize > MicrocodeLength)) { | |
| return FALSE; | |
| } | |
| // | |
| // The summation of all DWORDs in microcode should be zero. | |
| // | |
| if (VerifyChecksum && (CalculateSum32 ((UINT32 *)Microcode, TotalSize) != 0)) { | |
| return FALSE; | |
| } | |
| Sum32 = Microcode->ProcessorSignature.Uint32 + Microcode->ProcessorFlags + Microcode->Checksum; | |
| // | |
| // Check the processor signature and platform ID in the primary header. | |
| // | |
| Match = IsProcessorMatchedMicrocode ( | |
| Microcode->ProcessorSignature.Uint32, | |
| Microcode->ProcessorFlags, | |
| MicrocodeCpuIds, | |
| MicrocodeCpuIdCount | |
| ); | |
| if (Match) { | |
| return TRUE; | |
| } | |
| ExtendedTableLength = TotalSize - (DataSize + sizeof (CPU_MICROCODE_HEADER)); | |
| if ((ExtendedTableLength < sizeof (CPU_MICROCODE_EXTENDED_TABLE_HEADER)) || ((ExtendedTableLength % 4) != 0)) { | |
| return FALSE; | |
| } | |
| // | |
| // Extended Table exist, check if the CPU in support list | |
| // | |
| ExtendedTableHeader = (CPU_MICROCODE_EXTENDED_TABLE_HEADER *)((UINTN)(Microcode + 1) + DataSize); | |
| if (ExtendedTableHeader->ExtendedSignatureCount > MAX_UINT32 / sizeof (CPU_MICROCODE_EXTENDED_TABLE)) { | |
| return FALSE; | |
| } | |
| if (ExtendedTableHeader->ExtendedSignatureCount * sizeof (CPU_MICROCODE_EXTENDED_TABLE) | |
| > ExtendedTableLength - sizeof (CPU_MICROCODE_EXTENDED_TABLE_HEADER)) | |
| { | |
| return FALSE; | |
| } | |
| // | |
| // Check the extended table checksum | |
| // | |
| if (VerifyChecksum && (CalculateSum32 ((UINT32 *)ExtendedTableHeader, ExtendedTableLength) != 0)) { | |
| return FALSE; | |
| } | |
| ExtendedTable = (CPU_MICROCODE_EXTENDED_TABLE *)(ExtendedTableHeader + 1); | |
| for (Index = 0; Index < ExtendedTableHeader->ExtendedSignatureCount; Index++) { | |
| if (VerifyChecksum && | |
| (ExtendedTable[Index].ProcessorSignature.Uint32 + ExtendedTable[Index].ProcessorFlag | |
| + ExtendedTable[Index].Checksum != Sum32)) | |
| { | |
| // | |
| // The extended table entry is valid when the summation of Processor Signature, Processor Flags | |
| // and Checksum equal to the coresponding summation from primary header. Because: | |
| // CalculateSum32 (Header + Update Binary) == 0 | |
| // CalculateSum32 (Header + Update Binary) | |
| // - (Header.ProcessorSignature + Header.ProcessorFlag + Header.Checksum) | |
| // + (Extended.ProcessorSignature + Extended.ProcessorFlag + Extended.Checksum) == 0 | |
| // So, | |
| // (Header.ProcessorSignature + Header.ProcessorFlag + Header.Checksum) | |
| // == (Extended.ProcessorSignature + Extended.ProcessorFlag + Extended.Checksum) | |
| // | |
| continue; | |
| } | |
| Match = IsProcessorMatchedMicrocode ( | |
| ExtendedTable[Index].ProcessorSignature.Uint32, | |
| ExtendedTable[Index].ProcessorFlag, | |
| MicrocodeCpuIds, | |
| MicrocodeCpuIdCount | |
| ); | |
| if (Match) { | |
| return TRUE; | |
| } | |
| } | |
| return FALSE; | |
| } |