Topics

[PATCH 1/4] UefiCpuPkg: Add MicrocodeLib for loading microcode


Ni, Ray
 

Signed-off-by: Ray Ni <ray.ni@intel.com>
Cc: Eric Dong <eric.dong@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Rahul Kumar <rahul1.kumar@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=0D
+ Public include file for Microcode library.=0D
+=0D
+ Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>=0D
+ SPDX-License-Identifier: BSD-2-Clause-Patent=0D
+=0D
+**/=0D
+=0D
+#ifndef __MICROCODE_LIB_H__=0D
+#define __MICROCODE_LIB_H__=0D
+=0D
+#include <Register/Intel/Microcode.h>=0D
+#include <Ppi/ShadowMicrocode.h>=0D
+=0D
+/**=0D
+ Get microcode update signature of currently loaded microcode update.=0D
+=0D
+ @return Microcode signature.=0D
+**/=0D
+UINT32=0D
+EFIAPI=0D
+GetProcessorMicrocodeSignature (=0D
+ VOID=0D
+ );=0D
+=0D
+/**=0D
+ Get the processor signature and platform ID for current processor.=0D
+=0D
+ @param MicrocodeCpuId Return the processor signature and platform ID.=0D
+**/=0D
+VOID=0D
+EFIAPI=0D
+GetProcessorMicrocodeCpuId (=0D
+ EDKII_PEI_MICROCODE_CPU_ID *MicrocodeCpuId=0D
+ );=0D
+=0D
+/**=0D
+ Return the total size of the microcode entry.=0D
+=0D
+ Logic follows pseudo code in SDM as below:=0D
+=0D
+ N =3D 512=0D
+ If (Update.DataSize !=3D 00000000H)=0D
+ N =3D Update.TotalSize / 4=0D
+=0D
+ If Microcode is NULL, then ASSERT.=0D
+=0D
+ @param Microcode Pointer to the microcode entry.=0D
+=0D
+ @return The microcode total size.=0D
+**/=0D
+UINT32=0D
+EFIAPI=0D
+GetMicrocodeLength (=0D
+ IN CPU_MICROCODE_HEADER *Microcode=0D
+ );=0D
+=0D
+/**=0D
+ Load the microcode to the processor.=0D
+=0D
+ If Microcode is NULL, then ASSERT.=0D
+=0D
+ @param Microcode Pointer to the microcode entry.=0D
+**/=0D
+VOID=0D
+EFIAPI=0D
+LoadMicrocode (=0D
+ IN CPU_MICROCODE_HEADER *Microcode=0D
+ );=0D
+=0D
+/**=0D
+ Detect whether specified processor can find matching microcode patch and=
load it.=0D
+=0D
+ Microcode format is as below:=0D
+ +----------------------------------------+------------------------------=
-------------------+=0D
+ | CPU_MICROCODE_HEADER | =
|=0D
+ +----------------------------------------+ =
V=0D
+ | Update Data | =
CPU_MICROCODE_HEADER.Checksum=0D
+ +----------------------------------------+-------+ =
^=0D
+ | CPU_MICROCODE_EXTENDED_TABLE_HEADER | | =
|=0D
+ +----------------------------------------+ V =
|=0D
+ | CPU_MICROCODE_EXTENDED_TABLE[0] | CPU_MICROCODE_EXTENDED_TABLE=
_HEADER.Checksum |=0D
+ | CPU_MICROCODE_EXTENDED_TABLE[1] | ^ =
|=0D
+ | ... | | =
|=0D
+ +----------------------------------------+-------+----------------------=
-------------------+=0D
+=0D
+ There may by multiple CPU_MICROCODE_EXTENDED_TABLE in this format.=0D
+ The count of CPU_MICROCODE_EXTENDED_TABLE is indicated by ExtendedSignat=
ureCount=0D
+ of CPU_MICROCODE_EXTENDED_TABLE_HEADER structure.=0D
+=0D
+ If Microcode is NULL, then ASSERT.=0D
+=0D
+ @param Microcode Pointer to a microcode entry.=0D
+ @param MicrocodeLength The total length of the microcode entry.=0D
+ @param MinimumRevision The microcode whose revision <=3D MinimumRev=
ision is treated as invalid.=0D
+ Caller can supply value get from GetProcesso=
rMicrocodeSignature() to check=0D
+ whether the microcode is newer than loaded o=
ne.=0D
+ Caller can supply 0 to treat any revision (e=
xcept 0) microcode as valid.=0D
+ @param MicrocodeCpuIds Pointer to an array of processor signature a=
nd platform ID that represents=0D
+ a set of processors.=0D
+ Caller can supply zero-element array to skip=
the processor signature and=0D
+ platform ID check.=0D
+ @param MicrocodeCpuIdCount The number of elements in MicrocodeCpuIds.=0D
+ @param VerifyChecksum FALSE to skip all the checksum verifications=
.=0D
+=0D
+ @retval TRUE The microcode is valid.=0D
+ @retval FALSE The microcode is invalid.=0D
+**/=0D
+BOOLEAN=0D
+EFIAPI=0D
+IsValidMicrocode (=0D
+ IN CPU_MICROCODE_HEADER *Microcode,=0D
+ IN UINTN MicrocodeLength,=0D
+ IN UINT32 MinimumRevision,=0D
+ IN EDKII_PEI_MICROCODE_CPU_ID *MicrocodeCpuIds,=0D
+ IN UINTN MicrocodeCpuIdCount,=0D
+ IN BOOLEAN VerifyChecksum=0D
+ );=0D
+=0D
+#endif
\ No newline at end of file
diff --git a/UefiCpuPkg/Library/MicrocodeLib/MicrocodeLib.c b/UefiCpuPkg/Li=
brary/MicrocodeLib/MicrocodeLib.c
new file mode 100644
index 0000000000..03a43fdae7
--- /dev/null
+++ b/UefiCpuPkg/Library/MicrocodeLib/MicrocodeLib.c
@@ -0,0 +1,322 @@
+/** @file=0D
+ Implementation of MicrocodeLib.=0D
+=0D
+ Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>=0D
+ SPDX-License-Identifier: BSD-2-Clause-Patent=0D
+=0D
+**/=0D
+=0D
+#include <Uefi/UefiBaseType.h>=0D
+#include <Register/Intel/Cpuid.h>=0D
+#include <Register/Intel/ArchitecturalMsr.h>=0D
+#include <Register/Intel/Microcode.h>=0D
+#include <Library/BaseLib.h>=0D
+#include <Library/DebugLib.h>=0D
+#include <Ppi/ShadowMicrocode.h>=0D
+=0D
+/**=0D
+ Get microcode update signature of currently loaded microcode update.=0D
+=0D
+ @return Microcode signature.=0D
+**/=0D
+UINT32=0D
+EFIAPI=0D
+GetProcessorMicrocodeSignature (=0D
+ VOID=0D
+ )=0D
+{=0D
+ MSR_IA32_BIOS_SIGN_ID_REGISTER BiosSignIdMsr;=0D
+=0D
+ AsmWriteMsr64 (MSR_IA32_BIOS_SIGN_ID, 0);=0D
+ AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, NULL);=0D
+ BiosSignIdMsr.Uint64 =3D AsmReadMsr64 (MSR_IA32_BIOS_SIGN_ID);=0D
+ return BiosSignIdMsr.Bits.MicrocodeUpdateSignature;=0D
+}=0D
+=0D
+/**=0D
+ Get the processor signature and platform ID for current processor.=0D
+=0D
+ @param MicrocodeCpuId Return the processor signature and platform ID.=0D
+**/=0D
+VOID=0D
+EFIAPI=0D
+GetProcessorMicrocodeCpuId (=0D
+ EDKII_PEI_MICROCODE_CPU_ID *MicrocodeCpuId=0D
+ )=0D
+{=0D
+ MSR_IA32_PLATFORM_ID_REGISTER PlatformIdMsr;=0D
+=0D
+ ASSERT (MicrocodeCpuId !=3D NULL);=0D
+=0D
+ PlatformIdMsr.Uint64 =3D AsmReadMsr64 (MSR_IA32_PLATFORM_ID);=0D
+ MicrocodeCpuId->PlatformId =3D (UINT8) PlatformIdMsr.Bits.PlatformId;=0D
+ AsmCpuid (CPUID_VERSION_INFO, &MicrocodeCpuId->ProcessorSignature, NULL,=
NULL, NULL);=0D
+}=0D
+=0D
+/**=0D
+ Return the total size of the microcode entry.=0D
+=0D
+ Logic follows pseudo code in SDM as below:=0D
+=0D
+ N =3D 512=0D
+ If (Update.DataSize !=3D 00000000H)=0D
+ N =3D Update.TotalSize / 4=0D
+=0D
+ If Microcode is NULL, then ASSERT.=0D
+=0D
+ @param Microcode Pointer to the microcode entry.=0D
+=0D
+ @return The microcode total size.=0D
+**/=0D
+UINT32=0D
+EFIAPI=0D
+GetMicrocodeLength (=0D
+ IN CPU_MICROCODE_HEADER *Microcode=0D
+ )=0D
+{=0D
+ UINT32 TotalSize;=0D
+=0D
+ ASSERT (Microcode !=3D NULL);=0D
+=0D
+ TotalSize =3D 2048;=0D
+ if (Microcode->DataSize !=3D 0) {=0D
+ TotalSize =3D Microcode->TotalSize;=0D
+ }=0D
+ return TotalSize;=0D
+}=0D
+=0D
+/**=0D
+ Load the microcode to the processor.=0D
+=0D
+ If Microcode is NULL, then ASSERT.=0D
+=0D
+ @param Microcode Pointer to the microcode entry.=0D
+**/=0D
+VOID=0D
+EFIAPI=0D
+LoadMicrocode (=0D
+ IN CPU_MICROCODE_HEADER *Microcode=0D
+ )=0D
+{=0D
+ ASSERT (Microcode !=3D NULL);=0D
+=0D
+ AsmWriteMsr64 (MSR_IA32_BIOS_UPDT_TRIG, (UINT64) (UINTN) (Microcode + 1)=
);=0D
+}=0D
+=0D
+/**=0D
+ Determine if a microcode patch matchs the specific processor signature a=
nd flag.=0D
+=0D
+ @param[in] ProcessorSignature The processor signature field value in=
a=0D
+ microcode patch.=0D
+ @param[in] ProcessorFlags The processor flags field value in a=0D
+ microcode patch.=0D
+ @param[in] MicrocodeCpuId A pointer to an array of EDKII_PEI_MIC=
ROCODE_CPU_ID=0D
+ structures.=0D
+ @param[in] MicrocodeCpuIdCount Number of elements in MicrocodeCpuId a=
rray.=0D
+=0D
+ @retval TRUE The specified microcode patch matches to one of the Mic=
rocodeCpuId.=0D
+ @retval FALSE The specified microcode patch doesn't match to any of t=
he MicrocodeCpuId.=0D
+**/=0D
+BOOLEAN=0D
+IsProcessorMatchedMicrocode (=0D
+ IN UINT32 ProcessorSignature,=0D
+ IN UINT32 ProcessorFlags,=0D
+ IN EDKII_PEI_MICROCODE_CPU_ID *MicrocodeCpuId,=0D
+ IN UINTN MicrocodeCpuIdCount=0D
+ )=0D
+{=0D
+ UINTN Index;=0D
+=0D
+ if (MicrocodeCpuIdCount =3D=3D 0) {=0D
+ return TRUE;=0D
+ }=0D
+=0D
+ for (Index =3D 0; Index < MicrocodeCpuIdCount; Index++) {=0D
+ if ((ProcessorSignature =3D=3D MicrocodeCpuId[Index].ProcessorSignatur=
e) &&=0D
+ (ProcessorFlags & (1 << MicrocodeCpuId[Index].PlatformId)) !=3D 0)=
{=0D
+ return TRUE;=0D
+ }=0D
+ }=0D
+=0D
+ return FALSE;=0D
+}=0D
+=0D
+/**=0D
+ Detect whether specified processor can find matching microcode patch and=
load it.=0D
+=0D
+ Microcode format is as below:=0D
+ +----------------------------------------+------------------------------=
-------------------+=0D
+ | CPU_MICROCODE_HEADER | =
|=0D
+ +----------------------------------------+ =
V=0D
+ | Update Data | =
CPU_MICROCODE_HEADER.Checksum=0D
+ +----------------------------------------+-------+ =
^=0D
+ | CPU_MICROCODE_EXTENDED_TABLE_HEADER | | =
|=0D
+ +----------------------------------------+ V =
|=0D
+ | CPU_MICROCODE_EXTENDED_TABLE[0] | CPU_MICROCODE_EXTENDED_TABLE=
_HEADER.Checksum |=0D
+ | CPU_MICROCODE_EXTENDED_TABLE[1] | ^ =
|=0D
+ | ... | | =
|=0D
+ +----------------------------------------+-------+----------------------=
-------------------+=0D
+=0D
+ There may by multiple CPU_MICROCODE_EXTENDED_TABLE in this format.=0D
+ The count of CPU_MICROCODE_EXTENDED_TABLE is indicated by ExtendedSignat=
ureCount=0D
+ of CPU_MICROCODE_EXTENDED_TABLE_HEADER structure.=0D
+=0D
+ If Microcode is NULL, then ASSERT.=0D
+=0D
+ @param Microcode Pointer to a microcode entry.=0D
+ @param MicrocodeLength The total length of the microcode entry.=0D
+ @param MinimumRevision The microcode whose revision <=3D MinimumRev=
ision is treated as invalid.=0D
+ Caller can supply value get from GetProcesso=
rMicrocodeSignature() to check=0D
+ whether the microcode is newer than loaded o=
ne.=0D
+ Caller can supply 0 to treat any revision (e=
xcept 0) microcode as valid.=0D
+ @param MicrocodeCpuIds Pointer to an array of processor signature a=
nd platform ID that represents=0D
+ a set of processors.=0D
+ Caller can supply zero-element array to skip=
the processor signature and=0D
+ platform ID check.=0D
+ @param MicrocodeCpuIdCount The number of elements in MicrocodeCpuIds.=0D
+ @param VerifyChecksum FALSE to skip all the checksum verifications=
.=0D
+=0D
+ @retval TRUE The microcode is valid.=0D
+ @retval FALSE The microcode is invalid.=0D
+**/=0D
+BOOLEAN=0D
+EFIAPI=0D
+IsValidMicrocode (=0D
+ IN CPU_MICROCODE_HEADER *Microcode,=0D
+ IN UINTN MicrocodeLength,=0D
+ IN UINT32 MinimumRevision,=0D
+ IN EDKII_PEI_MICROCODE_CPU_ID *MicrocodeCpuIds,=0D
+ IN UINTN MicrocodeCpuIdCount,=0D
+ IN BOOLEAN VerifyChecksum=0D
+ )=0D
+{=0D
+ UINTN Index;=0D
+ UINT32 DataSize;=0D
+ UINT32 TotalSize;=0D
+ CPU_MICROCODE_EXTENDED_TABLE *ExtendedTable;=0D
+ CPU_MICROCODE_EXTENDED_TABLE_HEADER *ExtendedTableHeader;=0D
+ UINT32 ExtendedTableLength;=0D
+ UINT32 Sum32;=0D
+ BOOLEAN Match;=0D
+=0D
+ ASSERT (Microcode !=3D NULL);=0D
+=0D
+ //=0D
+ // It's invalid when:=0D
+ // the input microcode buffer is so small that even cannot contain the=
header.=0D
+ // the input microcode buffer is so large that exceeds MAX_ADDRESS.=0D
+ //=0D
+ if ((MicrocodeLength < sizeof (CPU_MICROCODE_HEADER)) || (MicrocodeLengt=
h > (MAX_ADDRESS - (UINTN) Microcode))) {=0D
+ return FALSE;=0D
+ }=0D
+=0D
+ //=0D
+ // Per SDM, HeaderVersion and LoaderRevision should both be 1.=0D
+ //=0D
+ if ((Microcode->HeaderVersion !=3D 1) || (Microcode->LoaderRevision !=3D=
1)) {=0D
+ return FALSE;=0D
+ }=0D
+=0D
+ //=0D
+ // The microcode revision should be larger than the minimum revision.=0D
+ //=0D
+ if (Microcode->UpdateRevision <=3D MinimumRevision) {=0D
+ return FALSE;=0D
+ }=0D
+=0D
+ DataSize =3D Microcode->DataSize;=0D
+ if (DataSize =3D=3D 0) {=0D
+ DataSize =3D 2000;=0D
+ }=0D
+=0D
+ //=0D
+ // Per SDM, DataSize should be multiple of DWORDs.=0D
+ //=0D
+ if ((DataSize % 4) !=3D 0) {=0D
+ return FALSE;=0D
+ }=0D
+=0D
+ TotalSize =3D GetMicrocodeLength (Microcode);=0D
+=0D
+ //=0D
+ // Check whether the whole microcode is within the buffer.=0D
+ // TotalSize should be multiple of 1024.=0D
+ //=0D
+ if (((TotalSize % SIZE_1KB) !=3D 0) || (TotalSize > MicrocodeLength)) {=
=0D
+ return FALSE;=0D
+ }=0D
+=0D
+ //=0D
+ // The summation of all DWORDs in microcode should be zero.=0D
+ //=0D
+ if (VerifyChecksum && (CalculateSum32 ((UINT32 *) Microcode, TotalSize) =
!=3D 0)) {=0D
+ return FALSE;=0D
+ }=0D
+=0D
+ Sum32 =3D Microcode->ProcessorSignature.Uint32 + Microcode->ProcessorFla=
gs + Microcode->Checksum;=0D
+=0D
+ //=0D
+ // Check the processor signature and platform ID in the primary header.=
=0D
+ //=0D
+ Match =3D IsProcessorMatchedMicrocode (=0D
+ Microcode->ProcessorSignature.Uint32,=0D
+ Microcode->ProcessorFlags,=0D
+ MicrocodeCpuIds,=0D
+ MicrocodeCpuIdCount=0D
+ );=0D
+ if (Match) {=0D
+ return TRUE;=0D
+ }=0D
+=0D
+ ExtendedTableLength =3D TotalSize - (DataSize + sizeof (CPU_MICROCODE_HE=
ADER));=0D
+ if ((ExtendedTableLength < sizeof (CPU_MICROCODE_EXTENDED_TABLE_HEADER))=
|| ((ExtendedTableLength % 4) !=3D 0)) {=0D
+ return FALSE;=0D
+ }=0D
+ //=0D
+ // Extended Table exist, check if the CPU in support list=0D
+ //=0D
+ ExtendedTableHeader =3D (CPU_MICROCODE_EXTENDED_TABLE_HEADER *) ((UINTN)=
(Microcode + 1) + DataSize);=0D
+ if (ExtendedTableHeader->ExtendedSignatureCount > MAX_UINT32 / sizeof (C=
PU_MICROCODE_EXTENDED_TABLE)) {=0D
+ return FALSE;=0D
+ }=0D
+ if (ExtendedTableHeader->ExtendedSignatureCount * sizeof (CPU_MICROCODE_=
EXTENDED_TABLE)=0D
+ > ExtendedTableLength - sizeof (CPU_MICROCODE_EXTENDED_TABLE_HEADER)=
) {=0D
+ return FALSE;=0D
+ }=0D
+ //=0D
+ // Check the extended table checksum=0D
+ //=0D
+ if (VerifyChecksum && (CalculateSum32 ((UINT32 *) ExtendedTableHeader, E=
xtendedTableLength) !=3D 0)) {=0D
+ return FALSE;=0D
+ }=0D
+=0D
+ ExtendedTable =3D (CPU_MICROCODE_EXTENDED_TABLE *) (ExtendedTableHeader =
+ 1);=0D
+ for (Index =3D 0; Index < ExtendedTableHeader->ExtendedSignatureCount; I=
ndex ++) {=0D
+ if (VerifyChecksum &&=0D
+ (ExtendedTable[Index].ProcessorSignature.Uint32 + ExtendedTable[In=
dex].ProcessorFlag=0D
+ + ExtendedTable[Index].Checksum !=3D Sum32)) {=0D
+ //=0D
+ // The extended table entry is valid when the summation of Processor=
Signature, Processor Flags=0D
+ // and Checksum equal to the coresponding summation from primary hea=
der. Because:=0D
+ // CalculateSum32 (Header + Update Binary) =3D=3D 0=0D
+ // CalculateSum32 (Header + Update Binary)=0D
+ // - (Header.ProcessorSignature + Header.ProcessorFlag + Head=
er.Checksum)=0D
+ // + (Extended.ProcessorSignature + Extended.ProcessorFlag + =
Extended.Checksum) =3D=3D 0=0D
+ // So,=0D
+ // (Header.ProcessorSignature + Header.ProcessorFlag + Header.Che=
cksum)=0D
+ // =3D=3D (Extended.ProcessorSignature + Extended.ProcessorFlag =
+ Extended.Checksum)=0D
+ //=0D
+ continue;=0D
+ }=0D
+ Match =3D IsProcessorMatchedMicrocode (=0D
+ ExtendedTable[Index].ProcessorSignature.Uint32,=0D
+ ExtendedTable[Index].ProcessorFlag,=0D
+ MicrocodeCpuIds,=0D
+ MicrocodeCpuIdCount=0D
+ );=0D
+ if (Match) {=0D
+ return TRUE;=0D
+ }=0D
+ }=0D
+ return FALSE;=0D
+}=0D
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=0D
+# Library for microcode verification and load.=0D
+#=0D
+# Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>=0D
+#=0D
+# SPDX-License-Identifier: BSD-2-Clause-Patent=0D
+#=0D
+#=0D
+##=0D
+=0D
+[Defines]=0D
+ INF_VERSION =3D 0x00010006=0D
+ BASE_NAME =3D MicrocodeLib=0D
+ FILE_GUID =3D EB8C72BC-8A48-4F80-996B-E52F68416D57=
=0D
+ MODULE_TYPE =3D BASE=0D
+ VERSION_STRING =3D 1.0=0D
+ LIBRARY_CLASS =3D MicrocodeLib=0D
+=0D
+#=0D
+# VALID_ARCHITECTURES =3D IA32 X64 EBC=0D
+#=0D
+=0D
+[Sources.common]=0D
+ MicrocodeLib.c=0D
+=0D
+[Packages]=0D
+ MdePkg/MdePkg.dec=0D
+ UefiCpuPkg/UefiCpuPkg.dec=0D
+=0D
+[LibraryClasses]=0D
+ BaseLib=0D
+ DebugLib=0D
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=0D
# This Package provides UEFI compatible CPU modules and libraries.=0D
#=0D
-# Copyright (c) 2007 - 2020, Intel Corporation. All rights reserved.<BR>=0D
+# Copyright (c) 2007 - 2021, Intel Corporation. All rights reserved.<BR>=0D
#=0D
# SPDX-License-Identifier: BSD-2-Clause-Patent=0D
#=0D
@@ -59,6 +59,9 @@ [LibraryClasses.IA32, LibraryClasses.X64]
## @libraryclass Provides function to get CPU cache information.=0D
CpuCacheInfoLib|Include/Library/CpuCacheInfoLib.h=0D
=0D
+ ## @libraryclass Provides function for loading microcode.=0D
+ MicrocodeLib|Include/Library/MicrocodeLib.h=0D
+=0D
[Guids]=0D
gUefiCpuPkgTokenSpaceGuid =3D { 0xac05bf33, 0x995a, 0x4ed4, { 0xaa,=
0xb8, 0xef, 0x7a, 0xe8, 0xf, 0x5c, 0xb0 }}=0D
gMsegSmramGuid =3D { 0x5802bce4, 0xeeee, 0x4e33, { 0xa1,=
0x30, 0xeb, 0xad, 0x27, 0xf0, 0xe4, 0x39 }}=0D
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/BasePeC=
offExtraActionLibNull.inf=0D
TpmMeasurementLib|MdeModulePkg/Library/TpmMeasurementLibNull/TpmMeasurem=
entLibNull.inf=0D
VmgExitLib|UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf=0D
+ MicrocodeLib|UefiCpuPkg/Library/MicrocodeLib/MicrocodeLib.inf=0D
=0D
[LibraryClasses.common.SEC]=0D
PlatformSecLib|UefiCpuPkg/Library/PlatformSecLibNull/PlatformSecLibNull.=
inf=0D
--=20
2.27.0.windows.1


Laszlo Ersek
 

On 04/02/21 07:58, Ni, Ray wrote:
Signed-off-by: Ray Ni <ray.ni@intel.com>
Cc: Eric Dong <eric.dong@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Rahul Kumar <rahul1.kumar@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
With the BZ ref added:

Acked-by: Laszlo Ersek <lersek@redhat.com>

Thanks
Laszlo


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


Dong, Eric
 

Reviewed-by: Eric Dong <eric.dong@intel.com>

-----Original Message-----
From: Ni, Ray <ray.ni@intel.com>
Sent: Friday, April 2, 2021 1:58 PM
To: devel@edk2.groups.io
Cc: Dong, Eric <eric.dong@intel.com>; Laszlo Ersek <lersek@redhat.com>; Kumar, Rahul1 <rahul1.kumar@intel.com>
Subject: [PATCH 1/4] UefiCpuPkg: Add MicrocodeLib for loading microcode

Signed-off-by: Ray Ni <ray.ni@intel.com>
Cc: Eric Dong <eric.dong@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Rahul Kumar <rahul1.kumar@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