--- /dev/null
+/** @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 "MpLib.h"\r
+\r
+/**\r
+ Get microcode update signature of currently loaded microcode update.\r
+\r
+ @return Microcode signature.\r
+**/\r
+UINT32\r
+GetCurrentMicrocodeSignature (\r
+ VOID\r
+ )\r
+{\r
+ MSR_IA32_BIOS_SIGN_ID_REGISTER BiosSignIdMsr;\r
+\r
+ AsmWriteMsr64 (MSR_IA32_BIOS_SIGN_ID, 0);\r
+ AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, NULL);\r
+ BiosSignIdMsr.Uint64 = AsmReadMsr64 (MSR_IA32_BIOS_SIGN_ID);\r
+ return BiosSignIdMsr.Bits.MicrocodeUpdateSignature;\r
+}\r
+\r
+/**\r
+ Detect whether specified processor can find matching microcode patch and load it.\r
+\r
+ @param[in] PeiCpuMpData Pointer to PEI CPU MP Data\r
+**/\r
+VOID\r
+MicrocodeDetect (\r
+ IN CPU_MP_DATA *CpuMpData\r
+ )\r
+{\r
+ UINT64 MicrocodePatchAddress;\r
+ UINT64 MicrocodePatchRegionSize;\r
+ UINT32 ExtendedTableLength;\r
+ UINT32 ExtendedTableCount;\r
+ CPU_MICROCODE_EXTENDED_TABLE *ExtendedTable;\r
+ CPU_MICROCODE_EXTENDED_TABLE_HEADER *ExtendedTableHeader;\r
+ CPU_MICROCODE_HEADER *MicrocodeEntryPoint;\r
+ UINTN MicrocodeEnd;\r
+ UINTN Index;\r
+ UINT8 PlatformId;\r
+ CPUID_VERSION_INFO_EAX Eax;\r
+ UINT32 CurrentRevision;\r
+ UINT32 LatestRevision;\r
+ UINTN TotalSize;\r
+ UINT32 CheckSum32;\r
+ BOOLEAN CorrectMicrocode;\r
+ VOID *MicrocodeData;\r
+ MSR_IA32_PLATFORM_ID_REGISTER PlatformIdMsr;\r
+\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
+ CurrentRevision = GetCurrentMicrocodeSignature ();\r
+ if (CurrentRevision != 0) {\r
+ //\r
+ // Skip loading microcode if it has been loaded successfully\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 sCPUID data.\r
+ //\r
+ AsmCpuid (CPUID_VERSION_INFO, &Eax.Uint32, NULL, NULL, NULL);\r
+\r
+ //\r
+ // The index of platform information resides in bits 50:52 of MSR IA32_PLATFORM_ID\r
+ //\r
+ PlatformIdMsr.Uint64 = AsmReadMsr64 (MSR_IA32_PLATFORM_ID);\r
+ PlatformId = (UINT8) PlatformIdMsr.Bits.PlatformId;\r
+\r
+ LatestRevision = 0;\r
+ MicrocodeEnd = (UINTN) (MicrocodePatchAddress + MicrocodePatchRegionSize);\r
+ MicrocodeEntryPoint = (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
+ // because the padding data should not include 0x00000001 and it should be the repeated\r
+ // byte format (like 0xXYXYXYXY....).\r
+ //\r
+ if (MicrocodeEntryPoint->ProcessorSignature.Uint32 == Eax.Uint32 &&\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 (\r
+ (UINT32 *) MicrocodeEntryPoint,\r
+ MicrocodeEntryPoint->DataSize + sizeof (CPU_MICROCODE_HEADER)\r
+ );\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 +\r
+ sizeof (CPU_MICROCODE_HEADER));\r
+ if (ExtendedTableLength != 0) {\r
+ //\r
+ // Extended Table exist, check if the CPU in support list\r
+ //\r
+ ExtendedTableHeader = (CPU_MICROCODE_EXTENDED_TABLE_HEADER *) ((UINT8 *) (MicrocodeEntryPoint)\r
+ + MicrocodeEntryPoint->DataSize + sizeof (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 = (CPU_MICROCODE_EXTENDED_TABLE *) (ExtendedTableHeader + 1);\r
+ for (Index = 0; Index < ExtendedTableCount; Index ++) {\r
+ CheckSum32 = CalculateSum32 ((UINT32 *) ExtendedTable, sizeof(CPU_MICROCODE_EXTENDED_TABLE));\r
+ if (CheckSum32 == 0) {\r
+ //\r
+ // Verify Header\r
+ //\r
+ if ((ExtendedTable->ProcessorSignature.Uint32 == Eax.Uint32) &&\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 = (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
+ MicrocodeData = (VOID *) ((UINTN) MicrocodeEntryPoint + sizeof (CPU_MICROCODE_HEADER));\r
+ }\r
+\r
+ MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (((UINTN) MicrocodeEntryPoint) + TotalSize);\r
+ } while (((UINTN) MicrocodeEntryPoint < MicrocodeEnd));\r
+\r
+ if (LatestRevision > CurrentRevision) {\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
+ AsmWriteMsr64 (\r
+ MSR_IA32_BIOS_UPDT_TRIG,\r
+ (UINT64) (UINTN) MicrocodeData\r
+ );\r
+ //\r
+ // Get and check new microcode signature\r
+ //\r
+ CurrentRevision = GetCurrentMicrocodeSignature ();\r
+ if (CurrentRevision != LatestRevision) {\r
+ AcquireSpinLock(&CpuMpData->MpLock);\r
+ DEBUG ((EFI_D_ERROR, "Updated microcode signature [0x%08x] does not match \\r
+ loaded microcode signature [0x%08x]\n", CurrentRevision, LatestRevision));\r
+ ReleaseSpinLock(&CpuMpData->MpLock);\r
+ }\r
+ }\r
+}\r