UefiCpuPkg/CpuMpPei: Load microcode if found newer revision
[mirror_edk2.git] / UefiCpuPkg / CpuMpPei / Microcode.c
index eb2e76b6a76ca14d2bbde4ad12abed08db616559..9dcbf99db3b6e5d676ac2d5d12bbaacaeadcc4ec 100644 (file)
-/** @file
-  Implementation of loading microcode on processors.
-
-  Copyright (c) 2015, 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 "CpuMpPei.h"
-
-/**
-  Get microcode update signature of currently loaded microcode update.
-
-  @return  Microcode signature.
-
-**/
-UINT32
-GetCurrentMicrocodeSignature (
-  VOID
-  )
-{
-  UINT64 Signature;
-
-  AsmWriteMsr64 (EFI_MSR_IA32_BIOS_SIGN_ID, 0);
-  AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, NULL);
-  Signature = AsmReadMsr64 (EFI_MSR_IA32_BIOS_SIGN_ID);
-  return (UINT32) RShiftU64 (Signature, 32);
-}
-
-/**
-  Detect whether specified processor can find matching microcode patch and load it.
-
-**/
-VOID
-MicrocodeDetect (
-  VOID
-  )
-{
-  UINT64                                  MicrocodePatchAddress;
-  UINT64                                  MicrocodePatchRegionSize;
-  UINT32                                  ExtendedTableLength;
-  UINT32                                  ExtendedTableCount;
-  EFI_CPU_MICROCODE_EXTENDED_TABLE        *ExtendedTable;
-  EFI_CPU_MICROCODE_EXTENDED_TABLE_HEADER *ExtendedTableHeader;
-  EFI_CPU_MICROCODE_HEADER                *MicrocodeEntryPoint;
-  UINTN                                   MicrocodeEnd;
-  UINTN                                   Index;
-  UINT8                                   PlatformId;
-  UINT32                                  RegEax;
-  UINT32                                  LatestRevision;
-  UINTN                                   TotalSize;
-  UINT32                                  CheckSum32;
-  BOOLEAN                                 CorrectMicrocode;
-  INT32                                   CurrentSignature;
-  MICROCODE_INFO                          MicrocodeInfo;
-
-  ZeroMem (&MicrocodeInfo, sizeof (MICROCODE_INFO));
-  MicrocodePatchAddress    = PcdGet64 (PcdCpuMicrocodePatchAddress);
-  MicrocodePatchRegionSize = PcdGet64 (PcdCpuMicrocodePatchRegionSize);
-  if (MicrocodePatchRegionSize == 0) {
-    //
-    // There is no microcode patches
-    //
-    return;
-  }
-
-  ExtendedTableLength = 0;
-  //
-  // Here data of CPUID leafs have not been collected into context buffer, so
-  // GetProcessorCpuid() cannot be used here to retrieve CPUID data.
-  //
-  AsmCpuid (CPUID_VERSION_INFO, &RegEax, NULL, NULL, NULL);
-
-  //
-  // The index of platform information resides in bits 50:52 of MSR IA32_PLATFORM_ID
-  //
-  PlatformId = (UINT8) AsmMsrBitFieldRead64 (EFI_MSR_IA32_PLATFORM_ID, 50, 52);
-
-  LatestRevision = 0;
-  MicrocodeEnd = (UINTN) (MicrocodePatchAddress + MicrocodePatchRegionSize);
-  MicrocodeEntryPoint = (EFI_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
-      // becasue the padding data should not include 0x00000001 and it should be the repeated
-      // byte format (like 0xXYXYXYXY....).
-      //
-      if (MicrocodeEntryPoint->ProcessorId == RegEax &&
-          MicrocodeEntryPoint->UpdateRevision > LatestRevision &&
-          (MicrocodeEntryPoint->ProcessorFlags & (1 << PlatformId))
-          ) {
-        if (MicrocodeEntryPoint->DataSize == 0) {
-          CheckSum32 = CalculateSum32 ((UINT32 *)MicrocodeEntryPoint, 2048);
-        } else {
-          CheckSum32 = CalculateSum32 ((UINT32 *)MicrocodeEntryPoint, MicrocodeEntryPoint->DataSize + sizeof(EFI_CPU_MICROCODE_HEADER));
-        }
-        if (CheckSum32 == 0) {
-          CorrectMicrocode = TRUE;
-        }
-      } else if ((MicrocodeEntryPoint->DataSize != 0) &&
-                 (MicrocodeEntryPoint->UpdateRevision > LatestRevision)) {
-        ExtendedTableLength = MicrocodeEntryPoint->TotalSize - (MicrocodeEntryPoint->DataSize + sizeof (EFI_CPU_MICROCODE_HEADER));
-        if (ExtendedTableLength != 0) {
-          //
-          // Extended Table exist, check if the CPU in support list
-          //
-          ExtendedTableHeader = (EFI_CPU_MICROCODE_EXTENDED_TABLE_HEADER *)((UINT8 *)(MicrocodeEntryPoint) + MicrocodeEntryPoint->DataSize + sizeof (EFI_CPU_MICROCODE_HEADER));
-          //
-          // Calculate Extended Checksum
-          //
-          if ((ExtendedTableLength % 4) == 0) {
-            CheckSum32 = CalculateSum32 ((UINT32 *)ExtendedTableHeader, ExtendedTableLength);
-            if (CheckSum32 == 0) {
-              //
-              // Checksum correct
-              //
-              ExtendedTableCount = ExtendedTableHeader->ExtendedSignatureCount;
-              ExtendedTable      = (EFI_CPU_MICROCODE_EXTENDED_TABLE *)(ExtendedTableHeader + 1);
-              for (Index = 0; Index < ExtendedTableCount; Index ++) {
-                CheckSum32 = CalculateSum32 ((UINT32 *)ExtendedTable, sizeof(EFI_CPU_MICROCODE_EXTENDED_TABLE));
-                if (CheckSum32 == 0) {
-                  //
-                  // Verify Header
-                  //
-                  if ((ExtendedTable->ProcessorSignature == RegEax) &&
-                      (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 = (EFI_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;
-      MicrocodeInfo.MicrocodeData = (VOID *)((UINTN)MicrocodeEntryPoint + sizeof (EFI_CPU_MICROCODE_HEADER));
-      MicrocodeInfo.MicrocodeSize = TotalSize;
-      MicrocodeInfo.ProcessorId = RegEax;
-    }
-
-    MicrocodeEntryPoint = (EFI_CPU_MICROCODE_HEADER *) (((UINTN) MicrocodeEntryPoint) + TotalSize);
-  } while (((UINTN) MicrocodeEntryPoint < MicrocodeEnd));
-
-  if (LatestRevision > 0) {
-    //
-    // Get microcode update signature of currently loaded microcode update
-    //
-    CurrentSignature = GetCurrentMicrocodeSignature ();
-    //
-    // If no microcode update has been loaded, then trigger microcode load.
-    //
-    if (CurrentSignature == 0) {
-      AsmWriteMsr64 (
-        EFI_MSR_IA32_BIOS_UPDT_TRIG,
-        (UINT64) (UINTN) MicrocodeInfo.MicrocodeData
-        );
-      MicrocodeInfo.Load = TRUE;
-    } else {
-      MicrocodeInfo.Load = FALSE;
-    }
-  }
-}
+/** @file\r
+  Implementation of loading microcode on processors.\r
+\r
+  Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.<BR>\r
+  This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions of the BSD License\r
+  which accompanies this distribution.  The full text of the license may be found at\r
+  http://opensource.org/licenses/bsd-license.php\r
+\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "CpuMpPei.h"\r
+\r
+/**\r
+  Get microcode update signature of currently loaded microcode update.\r
+\r
+  @return  Microcode signature.\r
+\r
+**/\r
+UINT32\r
+GetCurrentMicrocodeSignature (\r
+  VOID\r
+  )\r
+{\r
+  UINT64 Signature;\r
+\r
+  AsmWriteMsr64 (EFI_MSR_IA32_BIOS_SIGN_ID, 0);\r
+  AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, NULL);\r
+  Signature = AsmReadMsr64 (EFI_MSR_IA32_BIOS_SIGN_ID);\r
+  return (UINT32) RShiftU64 (Signature, 32);\r
+}\r
+\r
+/**\r
+  Detect whether specified processor can find matching microcode patch and load it.\r
+\r
+**/\r
+VOID\r
+MicrocodeDetect (\r
+  VOID\r
+  )\r
+{\r
+  UINT64                                  MicrocodePatchAddress;\r
+  UINT64                                  MicrocodePatchRegionSize;\r
+  UINT32                                  ExtendedTableLength;\r
+  UINT32                                  ExtendedTableCount;\r
+  EFI_CPU_MICROCODE_EXTENDED_TABLE        *ExtendedTable;\r
+  EFI_CPU_MICROCODE_EXTENDED_TABLE_HEADER *ExtendedTableHeader;\r
+  EFI_CPU_MICROCODE_HEADER                *MicrocodeEntryPoint;\r
+  UINTN                                   MicrocodeEnd;\r
+  UINTN                                   Index;\r
+  UINT8                                   PlatformId;\r
+  UINT32                                  RegEax;\r
+  UINT32                                  LatestRevision;\r
+  UINTN                                   TotalSize;\r
+  UINT32                                  CheckSum32;\r
+  BOOLEAN                                 CorrectMicrocode;\r
+  MICROCODE_INFO                          MicrocodeInfo;\r
+\r
+  ZeroMem (&MicrocodeInfo, sizeof (MICROCODE_INFO));\r
+  MicrocodePatchAddress    = PcdGet64 (PcdCpuMicrocodePatchAddress);\r
+  MicrocodePatchRegionSize = PcdGet64 (PcdCpuMicrocodePatchRegionSize);\r
+  if (MicrocodePatchRegionSize == 0) {\r
+    //\r
+    // There is no microcode patches\r
+    //\r
+    return;\r
+  }\r
+\r
+  ExtendedTableLength = 0;\r
+  //\r
+  // Here data of CPUID leafs have not been collected into context buffer, so\r
+  // GetProcessorCpuid() cannot be used here to retrieve CPUID data.\r
+  //\r
+  AsmCpuid (CPUID_VERSION_INFO, &RegEax, NULL, NULL, NULL);\r
+\r
+  //\r
+  // The index of platform information resides in bits 50:52 of MSR IA32_PLATFORM_ID\r
+  //\r
+  PlatformId = (UINT8) AsmMsrBitFieldRead64 (EFI_MSR_IA32_PLATFORM_ID, 50, 52);\r
+\r
+  LatestRevision = 0;\r
+  MicrocodeEnd = (UINTN) (MicrocodePatchAddress + MicrocodePatchRegionSize);\r
+  MicrocodeEntryPoint = (EFI_CPU_MICROCODE_HEADER *) (UINTN) MicrocodePatchAddress;\r
+  do {\r
+    //\r
+    // Check if the microcode is for the Cpu and the version is newer\r
+    // and the update can be processed on the platform\r
+    //\r
+    CorrectMicrocode = FALSE;\r
+    if (MicrocodeEntryPoint->HeaderVersion == 0x1) {\r
+      //\r
+      // It is the microcode header. It is not the padding data between microcode patches\r
+      // becasue the padding data should not include 0x00000001 and it should be the repeated\r
+      // byte format (like 0xXYXYXYXY....).\r
+      //\r
+      if (MicrocodeEntryPoint->ProcessorId == RegEax &&\r
+          MicrocodeEntryPoint->UpdateRevision > LatestRevision &&\r
+          (MicrocodeEntryPoint->ProcessorFlags & (1 << PlatformId))\r
+          ) {\r
+        if (MicrocodeEntryPoint->DataSize == 0) {\r
+          CheckSum32 = CalculateSum32 ((UINT32 *)MicrocodeEntryPoint, 2048);\r
+        } else {\r
+          CheckSum32 = CalculateSum32 ((UINT32 *)MicrocodeEntryPoint, MicrocodeEntryPoint->DataSize + sizeof(EFI_CPU_MICROCODE_HEADER));\r
+        }\r
+        if (CheckSum32 == 0) {\r
+          CorrectMicrocode = TRUE;\r
+        }\r
+      } else if ((MicrocodeEntryPoint->DataSize != 0) &&\r
+                 (MicrocodeEntryPoint->UpdateRevision > LatestRevision)) {\r
+        ExtendedTableLength = MicrocodeEntryPoint->TotalSize - (MicrocodeEntryPoint->DataSize + sizeof (EFI_CPU_MICROCODE_HEADER));\r
+        if (ExtendedTableLength != 0) {\r
+          //\r
+          // Extended Table exist, check if the CPU in support list\r
+          //\r
+          ExtendedTableHeader = (EFI_CPU_MICROCODE_EXTENDED_TABLE_HEADER *)((UINT8 *)(MicrocodeEntryPoint) + MicrocodeEntryPoint->DataSize + sizeof (EFI_CPU_MICROCODE_HEADER));\r
+          //\r
+          // Calculate Extended Checksum\r
+          //\r
+          if ((ExtendedTableLength % 4) == 0) {\r
+            CheckSum32 = CalculateSum32 ((UINT32 *)ExtendedTableHeader, ExtendedTableLength);\r
+            if (CheckSum32 == 0) {\r
+              //\r
+              // Checksum correct\r
+              //\r
+              ExtendedTableCount = ExtendedTableHeader->ExtendedSignatureCount;\r
+              ExtendedTable      = (EFI_CPU_MICROCODE_EXTENDED_TABLE *)(ExtendedTableHeader + 1);\r
+              for (Index = 0; Index < ExtendedTableCount; Index ++) {\r
+                CheckSum32 = CalculateSum32 ((UINT32 *)ExtendedTable, sizeof(EFI_CPU_MICROCODE_EXTENDED_TABLE));\r
+                if (CheckSum32 == 0) {\r
+                  //\r
+                  // Verify Header\r
+                  //\r
+                  if ((ExtendedTable->ProcessorSignature == RegEax) &&\r
+                      (ExtendedTable->ProcessorFlag & (1 << PlatformId)) ) {\r
+                    //\r
+                    // Find one\r
+                    //\r
+                    CorrectMicrocode = TRUE;\r
+                    break;\r
+                  }\r
+                }\r
+                ExtendedTable ++;\r
+              }\r
+            }\r
+          }\r
+        }\r
+      }\r
+    } else {\r
+      //\r
+      // It is the padding data between the microcode patches for microcode patches alignment.\r
+      // Because the microcode patch is the multiple of 1-KByte, the padding data should not\r
+      // exist if the microcode patch alignment value is not larger than 1-KByte. So, the microcode\r
+      // alignment value should be larger than 1-KByte. We could skip SIZE_1KB padding data to\r
+      // find the next possible microcode patch header.\r
+      //\r
+      MicrocodeEntryPoint = (EFI_CPU_MICROCODE_HEADER *) (((UINTN) MicrocodeEntryPoint) + SIZE_1KB);\r
+      continue;\r
+    }\r
+    //\r
+    // Get the next patch.\r
+    //\r
+    if (MicrocodeEntryPoint->DataSize == 0) {\r
+      TotalSize = 2048;\r
+    } else {\r
+      TotalSize = MicrocodeEntryPoint->TotalSize;\r
+    }\r
+\r
+    if (CorrectMicrocode) {\r
+      LatestRevision = MicrocodeEntryPoint->UpdateRevision;\r
+      MicrocodeInfo.MicrocodeData = (VOID *)((UINTN)MicrocodeEntryPoint + sizeof (EFI_CPU_MICROCODE_HEADER));\r
+      MicrocodeInfo.MicrocodeSize = TotalSize;\r
+      MicrocodeInfo.ProcessorId = RegEax;\r
+    }\r
+\r
+    MicrocodeEntryPoint = (EFI_CPU_MICROCODE_HEADER *) (((UINTN) MicrocodeEntryPoint) + TotalSize);\r
+  } while (((UINTN) MicrocodeEntryPoint < MicrocodeEnd));\r
+\r
+  if (LatestRevision > 0) {\r
+    //\r
+    // BIOS only authenticate updates that contain a numerically larger revision\r
+    // than the currently loaded revision, where Current Signature < New Update\r
+    // Revision. A processor with no loaded update is considered to have a\r
+    // revision equal to zero.\r
+    //\r
+    if (LatestRevision > GetCurrentMicrocodeSignature ()) {\r
+      AsmWriteMsr64 (\r
+        EFI_MSR_IA32_BIOS_UPDT_TRIG,\r
+        (UINT64) (UINTN) MicrocodeInfo.MicrocodeData\r
+        );\r
+      //\r
+      // Get and verify new microcode signature\r
+      //\r
+      ASSERT (LatestRevision == GetCurrentMicrocodeSignature ());\r
+      MicrocodeInfo.Load = TRUE;\r
+    } else {\r
+      MicrocodeInfo.Load = FALSE;\r
+    }\r
+  }\r
+}\r