UefiCpuPkg/MpInitLib: Add MicrocodeDetect() and load microcode on BSP

v4:
  1. ProcessorSignature is updated to CPU_MICROCODE_PROCESSOR_SIGNATURE
     instead of UINT32.

Cc: Michael Kinney <michael.d.kinney@intel.com>
Cc: Feng Tian <feng.tian@intel.com>
Cc: Giri P Mudusuru <giri.p.mudusuru@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Jeff Fan <jeff.fan@intel.com>
Reviewed-by: Michael Kinney <michael.d.kinney@intel.com>
Tested-by: Laszlo Ersek <lersek@redhat.com>
Tested-by: Michael Kinney <michael.d.kinney@intel.com>
diff --git a/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf b/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
index e9a2725..03a8994 100644
--- a/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
+++ b/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
@@ -39,6 +39,7 @@
   DxeMpLib.c

   MpLib.c

   MpLib.h

+  Microcode.c

 

 [Packages]

   MdePkg/MdePkg.dec

diff --git a/UefiCpuPkg/Library/MpInitLib/Microcode.c b/UefiCpuPkg/Library/MpInitLib/Microcode.c
new file mode 100644
index 0000000..0fd8e8c
--- /dev/null
+++ b/UefiCpuPkg/Library/MpInitLib/Microcode.c
@@ -0,0 +1,216 @@
+/** @file

+  Implementation of loading microcode on processors.

+

+  Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.<BR>

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "MpLib.h"

+

+/**

+  Get microcode update signature of currently loaded microcode update.

+

+  @return  Microcode signature.

+**/

+UINT32

+GetCurrentMicrocodeSignature (

+  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;

+}

+

+/**

+  Detect whether specified processor can find matching microcode patch and load it.

+

+  @param[in] PeiCpuMpData        Pointer to PEI CPU MP Data

+**/

+VOID

+MicrocodeDetect (

+  IN CPU_MP_DATA             *CpuMpData

+  )

+{

+  UINT64                                  MicrocodePatchAddress;

+  UINT64                                  MicrocodePatchRegionSize;

+  UINT32                                  ExtendedTableLength;

+  UINT32                                  ExtendedTableCount;

+  CPU_MICROCODE_EXTENDED_TABLE            *ExtendedTable;

+  CPU_MICROCODE_EXTENDED_TABLE_HEADER     *ExtendedTableHeader;

+  CPU_MICROCODE_HEADER                    *MicrocodeEntryPoint;

+  UINTN                                   MicrocodeEnd;

+  UINTN                                   Index;

+  UINT8                                   PlatformId;

+  CPUID_VERSION_INFO_EAX                  Eax;

+  UINT32                                  CurrentRevision;

+  UINT32                                  LatestRevision;

+  UINTN                                   TotalSize;

+  UINT32                                  CheckSum32;

+  BOOLEAN                                 CorrectMicrocode;

+  VOID                                    *MicrocodeData;

+  MSR_IA32_PLATFORM_ID_REGISTER           PlatformIdMsr;

+

+  MicrocodePatchAddress    = PcdGet64 (PcdCpuMicrocodePatchAddress);

+  MicrocodePatchRegionSize = PcdGet64 (PcdCpuMicrocodePatchRegionSize);

+  if (MicrocodePatchRegionSize == 0) {

+    //

+    // There is no microcode patches

+    //

+    return;

+  }

+

+  CurrentRevision = GetCurrentMicrocodeSignature ();

+  if (CurrentRevision != 0) {

+    //

+    // Skip loading microcode if it has been loaded successfully

+    //

+    return;

+  }

+

+  ExtendedTableLength = 0;

+  //

+  // Here data of CPUID leafs have not been collected into context buffer, so

+  // GetProcessorCpuid() cannot be used here to retrieve sCPUID data.

+  //

+  AsmCpuid (CPUID_VERSION_INFO, &Eax.Uint32, NULL, NULL, NULL);

+

+  //

+  // The index of platform information resides in bits 50:52 of MSR IA32_PLATFORM_ID

+  //

+  PlatformIdMsr.Uint64 = AsmReadMsr64 (MSR_IA32_PLATFORM_ID);

+  PlatformId = (UINT8) PlatformIdMsr.Bits.PlatformId;

+

+  LatestRevision = 0;

+  MicrocodeEnd = (UINTN) (MicrocodePatchAddress + MicrocodePatchRegionSize);

+  MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (UINTN) MicrocodePatchAddress;

+  do {

+    //

+    // Check if the microcode is for the Cpu and the version is newer

+    // and the update can be processed on the platform

+    //

+    CorrectMicrocode = FALSE;

+    if (MicrocodeEntryPoint->HeaderVersion == 0x1) {

+      //

+      // It is the microcode header. It is not the padding data between microcode patches

+      // because the padding data should not include 0x00000001 and it should be the repeated

+      // byte format (like 0xXYXYXYXY....).

+      //

+      if (MicrocodeEntryPoint->ProcessorSignature.Uint32 == Eax.Uint32 &&

+          MicrocodeEntryPoint->UpdateRevision > LatestRevision &&

+          (MicrocodeEntryPoint->ProcessorFlags & (1 << PlatformId))

+          ) {

+        if (MicrocodeEntryPoint->DataSize == 0) {

+          CheckSum32 = CalculateSum32 ((UINT32 *) MicrocodeEntryPoint, 2048);

+        } else {

+          CheckSum32 = CalculateSum32 (

+                         (UINT32 *) MicrocodeEntryPoint,

+                         MicrocodeEntryPoint->DataSize + sizeof (CPU_MICROCODE_HEADER)

+                         );

+        }

+        if (CheckSum32 == 0) {

+          CorrectMicrocode = TRUE;

+        }

+      } else if ((MicrocodeEntryPoint->DataSize != 0) &&

+                 (MicrocodeEntryPoint->UpdateRevision > LatestRevision)) {

+        ExtendedTableLength = MicrocodeEntryPoint->TotalSize - (MicrocodeEntryPoint->DataSize +

+                                sizeof (CPU_MICROCODE_HEADER));

+        if (ExtendedTableLength != 0) {

+          //

+          // Extended Table exist, check if the CPU in support list

+          //

+          ExtendedTableHeader = (CPU_MICROCODE_EXTENDED_TABLE_HEADER *) ((UINT8 *) (MicrocodeEntryPoint)

+                                  + MicrocodeEntryPoint->DataSize + sizeof (CPU_MICROCODE_HEADER));

+          //

+          // Calculate Extended Checksum

+          //

+          if ((ExtendedTableLength % 4) == 0) {

+            CheckSum32 = CalculateSum32 ((UINT32 *) ExtendedTableHeader, ExtendedTableLength);

+            if (CheckSum32 == 0) {

+              //

+              // Checksum correct

+              //

+              ExtendedTableCount = ExtendedTableHeader->ExtendedSignatureCount;

+              ExtendedTable      = (CPU_MICROCODE_EXTENDED_TABLE *) (ExtendedTableHeader + 1);

+              for (Index = 0; Index < ExtendedTableCount; Index ++) {

+                CheckSum32 = CalculateSum32 ((UINT32 *) ExtendedTable, sizeof(CPU_MICROCODE_EXTENDED_TABLE));

+                if (CheckSum32 == 0) {

+                  //

+                  // Verify Header

+                  //

+                  if ((ExtendedTable->ProcessorSignature.Uint32 == Eax.Uint32) &&

+                      (ExtendedTable->ProcessorFlag & (1 << PlatformId)) ) {

+                    //

+                    // Find one

+                    //

+                    CorrectMicrocode = TRUE;

+                    break;

+                  }

+                }

+                ExtendedTable ++;

+              }

+            }

+          }

+        }

+      }

+    } else {

+      //

+      // It is the padding data between the microcode patches for microcode patches alignment.

+      // Because the microcode patch is the multiple of 1-KByte, the padding data should not

+      // exist if the microcode patch alignment value is not larger than 1-KByte. So, the microcode

+      // alignment value should be larger than 1-KByte. We could skip SIZE_1KB padding data to

+      // find the next possible microcode patch header.

+      //

+      MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (((UINTN) MicrocodeEntryPoint) + SIZE_1KB);

+      continue;

+    }

+    //

+    // Get the next patch.

+    //

+    if (MicrocodeEntryPoint->DataSize == 0) {

+      TotalSize = 2048;

+    } else {

+      TotalSize = MicrocodeEntryPoint->TotalSize;

+    }

+

+    if (CorrectMicrocode) {

+      LatestRevision = MicrocodeEntryPoint->UpdateRevision;

+      MicrocodeData = (VOID *) ((UINTN) MicrocodeEntryPoint + sizeof (CPU_MICROCODE_HEADER));

+    }

+

+    MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (((UINTN) MicrocodeEntryPoint) + TotalSize);

+  } while (((UINTN) MicrocodeEntryPoint < MicrocodeEnd));

+

+  if (LatestRevision > CurrentRevision) {

+    //

+    // BIOS only authenticate updates that contain a numerically larger revision

+    // than the currently loaded revision, where Current Signature < New Update

+    // Revision. A processor with no loaded update is considered to have a

+    // revision equal to zero.

+    //

+    AsmWriteMsr64 (

+        MSR_IA32_BIOS_UPDT_TRIG,

+        (UINT64) (UINTN) MicrocodeData

+        );

+    //

+    // Get and check new microcode signature

+    //

+    CurrentRevision = GetCurrentMicrocodeSignature ();

+    if (CurrentRevision != LatestRevision) {

+      AcquireSpinLock(&CpuMpData->MpLock);

+      DEBUG ((EFI_D_ERROR, "Updated microcode signature [0x%08x] does not match \

+                loaded microcode signature [0x%08x]\n", CurrentRevision, LatestRevision));

+      ReleaseSpinLock(&CpuMpData->MpLock);

+    }

+  }

+}

diff --git a/UefiCpuPkg/Library/MpInitLib/MpLib.c b/UefiCpuPkg/Library/MpInitLib/MpLib.c
index 0832228..7384f5d 100644
--- a/UefiCpuPkg/Library/MpInitLib/MpLib.c
+++ b/UefiCpuPkg/Library/MpInitLib/MpLib.c
@@ -294,6 +294,11 @@
     CpuMpData->CpuData[Index].StartupApSignal =

       (UINT32 *)(MonitorBuffer + MonitorFilterSize * Index);

   }

+  //

+  // Load Microcode on BSP

+  //

+  MicrocodeDetect (CpuMpData);

+  //

   // Store BSP's MTRR setting

   //

   MtrrGetAllMtrrs (&CpuMpData->MtrrTable);

diff --git a/UefiCpuPkg/Library/MpInitLib/MpLib.h b/UefiCpuPkg/Library/MpInitLib/MpLib.h
index ca8bd44..625d061 100644
--- a/UefiCpuPkg/Library/MpInitLib/MpLib.h
+++ b/UefiCpuPkg/Library/MpInitLib/MpLib.h
@@ -232,5 +232,15 @@
   OUT MP_ASSEMBLY_ADDRESS_MAP    *AddressMap

   );

 

+/**

+  Detect whether specified processor can find matching microcode patch and load it.

+

+  @param[in] PeiCpuMpData        Pointer to PEI CPU MP Data

+**/

+VOID

+MicrocodeDetect (

+  IN CPU_MP_DATA             *CpuMpData

+  );

+

 #endif

 

diff --git a/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf b/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
index c195a38..0c6873d 100644
--- a/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
+++ b/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
@@ -39,6 +39,7 @@
   PeiMpLib.c

   MpLib.c

   MpLib.h

+  Microcode.c

 

 [Packages]

   MdePkg/MdePkg.dec