Signed-off-by: Ray Ni <ray...@intel.com> Cc: Eric Dong <eric.d...@intel.com> Cc: Laszlo Ersek <ler...@redhat.com> Cc: Rahul Kumar <rahul1.ku...@intel.com> --- UefiCpuPkg/Include/Library/MicrocodeLib.h | 120 +++++++ .../Library/MicrocodeLib/MicrocodeLib.c | 322 ++++++++++++++++++ .../Library/MicrocodeLib/MicrocodeLib.inf | 32 ++ UefiCpuPkg/UefiCpuPkg.dec | 5 +- UefiCpuPkg/UefiCpuPkg.dsc | 1 + 5 files changed, 479 insertions(+), 1 deletion(-) create mode 100644 UefiCpuPkg/Include/Library/MicrocodeLib.h create mode 100644 UefiCpuPkg/Library/MicrocodeLib/MicrocodeLib.c create mode 100644 UefiCpuPkg/Library/MicrocodeLib/MicrocodeLib.inf
diff --git a/UefiCpuPkg/Include/Library/MicrocodeLib.h b/UefiCpuPkg/Include/Library/MicrocodeLib.h new file mode 100644 index 0000000000..2570c43cce --- /dev/null +++ b/UefiCpuPkg/Include/Library/MicrocodeLib.h @@ -0,0 +1,120 @@ +/** @file + Public include file for Microcode library. + + Copyright (c) 2021, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __MICROCODE_LIB_H__ +#define __MICROCODE_LIB_H__ + +#include <Register/Intel/Microcode.h> +#include <Ppi/ShadowMicrocode.h> + +/** + Get microcode update signature of currently loaded microcode update. + + @return Microcode signature. +**/ +UINT32 +EFIAPI +GetProcessorMicrocodeSignature ( + VOID + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +#endif \ No newline at end of file diff --git a/UefiCpuPkg/Library/MicrocodeLib/MicrocodeLib.c b/UefiCpuPkg/Library/MicrocodeLib/MicrocodeLib.c new file mode 100644 index 0000000000..03a43fdae7 --- /dev/null +++ b/UefiCpuPkg/Library/MicrocodeLib/MicrocodeLib.c @@ -0,0 +1,322 @@ +/** @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; +} diff --git a/UefiCpuPkg/Library/MicrocodeLib/MicrocodeLib.inf b/UefiCpuPkg/Library/MicrocodeLib/MicrocodeLib.inf new file mode 100644 index 0000000000..c6f8f52e95 --- /dev/null +++ b/UefiCpuPkg/Library/MicrocodeLib/MicrocodeLib.inf @@ -0,0 +1,32 @@ +## @file +# Library for microcode verification and load. +# +# Copyright (c) 2021, Intel Corporation. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010006 + BASE_NAME = MicrocodeLib + FILE_GUID = EB8C72BC-8A48-4F80-996B-E52F68416D57 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = MicrocodeLib + +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# + +[Sources.common] + MicrocodeLib.c + +[Packages] + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + DebugLib diff --git a/UefiCpuPkg/UefiCpuPkg.dec b/UefiCpuPkg/UefiCpuPkg.dec index a639ce5412..62acb291f3 100644 --- a/UefiCpuPkg/UefiCpuPkg.dec +++ b/UefiCpuPkg/UefiCpuPkg.dec @@ -1,7 +1,7 @@ ## @file UefiCpuPkg.dec # This Package provides UEFI compatible CPU modules and libraries. # -# Copyright (c) 2007 - 2020, Intel Corporation. All rights reserved.<BR> +# Copyright (c) 2007 - 2021, Intel Corporation. All rights reserved.<BR> # # SPDX-License-Identifier: BSD-2-Clause-Patent # @@ -59,6 +59,9 @@ [LibraryClasses.IA32, LibraryClasses.X64] ## @libraryclass Provides function to get CPU cache information. CpuCacheInfoLib|Include/Library/CpuCacheInfoLib.h + ## @libraryclass Provides function for loading microcode. + MicrocodeLib|Include/Library/MicrocodeLib.h + [Guids] gUefiCpuPkgTokenSpaceGuid = { 0xac05bf33, 0x995a, 0x4ed4, { 0xaa, 0xb8, 0xef, 0x7a, 0xe8, 0xf, 0x5c, 0xb0 }} gMsegSmramGuid = { 0x5802bce4, 0xeeee, 0x4e33, { 0xa1, 0x30, 0xeb, 0xad, 0x27, 0xf0, 0xe4, 0x39 }} diff --git a/UefiCpuPkg/UefiCpuPkg.dsc b/UefiCpuPkg/UefiCpuPkg.dsc index 98c4c53465..b932cf63ec 100644 --- a/UefiCpuPkg/UefiCpuPkg.dsc +++ b/UefiCpuPkg/UefiCpuPkg.dsc @@ -60,6 +60,7 @@ [LibraryClasses] PeCoffExtraActionLib|MdePkg/Library/BasePeCoffExtraActionLibNull/BasePeCoffExtraActionLibNull.inf TpmMeasurementLib|MdeModulePkg/Library/TpmMeasurementLibNull/TpmMeasurementLibNull.inf VmgExitLib|UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf + MicrocodeLib|UefiCpuPkg/Library/MicrocodeLib/MicrocodeLib.inf [LibraryClasses.common.SEC] PlatformSecLib|UefiCpuPkg/Library/PlatformSecLibNull/PlatformSecLibNull.inf -- 2.27.0.windows.1 -=-=-=-=-=-=-=-=-=-=-=- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#73638): https://edk2.groups.io/g/devel/message/73638 Mute This Topic: https://groups.io/mt/81796731/21656 Group Owner: devel+ow...@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [arch...@mail-archive.com] -=-=-=-=-=-=-=-=-=-=-=-