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

Ni, Ray ray.ni at intel.com
Fri Apr 2 05:58:04 UTC 2021


Signed-off-by: Ray Ni <ray.ni at intel.com>
Cc: Eric Dong <eric.dong at intel.com>
Cc: Laszlo Ersek <lersek at redhat.com>
Cc: Rahul Kumar <rahul1.kumar at 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/1813853
Group Owner: devel+owner at edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [edk2-devel-archive at redhat.com]
-=-=-=-=-=-=-=-=-=-=-=-






More information about the edk2-devel-archive mailing list