audk/UefiCpuPkg/Library/MpInitLib/Microcode.c

328 lines
13 KiB
C

/** @file
Implementation of loading microcode on processors.
Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#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.
Microcode Payload as the following format:
+----------------------------------------+------------------+
| CPU_MICROCODE_HEADER | |
+----------------------------------------+ CheckSum Part1 |
| Microcode Binary | |
+----------------------------------------+------------------+
| CPU_MICROCODE_EXTENDED_TABLE_HEADER | |
+----------------------------------------+ CheckSum Part2 |
| CPU_MICROCODE_EXTENDED_TABLE | |
| ... | |
+----------------------------------------+------------------+
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.
When we are trying to verify the CheckSum32 with extended table.
We should use the fields of exnteded table to replace the corresponding
fields in CPU_MICROCODE_HEADER structure, and recalculate the
CheckSum32 with CPU_MICROCODE_HEADER + Microcode Binary. We named
it as CheckSum Part3.
The CheckSum Part2 is used to verify the CPU_MICROCODE_EXTENDED_TABLE_HEADER
and CPU_MICROCODE_EXTENDED_TABLE parts. We should make sure CheckSum Part2
is correct before we are going to verify each CPU_MICROCODE_EXTENDED_TABLE.
Only ProcessorSignature, ProcessorFlag and CheckSum are different between
CheckSum Part1 and CheckSum Part3. To avoid multiple computing CheckSum Part3.
Save an in-complete CheckSum32 from CheckSum Part1 for common parts.
When we are going to calculate CheckSum32, just should use the corresponding part
of the ProcessorSignature, ProcessorFlag and CheckSum with in-complete CheckSum32.
Notes: CheckSum32 is not a strong verification.
It does not guarantee that the data has not been modified.
CPU has its own mechanism to verify Microcode Binary part.
@param[in] CpuMpData The pointer to CPU MP Data structure.
@param[in] IsBspCallIn Indicate whether the caller is BSP or not.
**/
VOID
MicrocodeDetect (
IN CPU_MP_DATA *CpuMpData,
IN BOOLEAN IsBspCallIn
)
{
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;
UINT32 InCompleteCheckSum32;
BOOLEAN CorrectMicrocode;
VOID *MicrocodeData;
MSR_IA32_PLATFORM_ID_REGISTER PlatformIdMsr;
UINT32 ProcessorFlags;
UINT32 ThreadId;
//
// set ProcessorFlags to suppress incorrect compiler/analyzer warnings
//
ProcessorFlags = 0;
if (CpuMpData->MicrocodePatchRegionSize == 0) {
//
// There is no microcode patches
//
return;
}
CurrentRevision = GetCurrentMicrocodeSignature ();
if (CurrentRevision != 0 && !IsBspCallIn) {
//
// Skip loading microcode if it has been loaded successfully
//
return;
}
GetProcessorLocationByApicId (GetInitialApicId (), NULL, NULL, &ThreadId);
if (ThreadId != 0) {
//
// Skip loading microcode if it is not the first thread in one core.
//
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, &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;
//
// Check whether AP has same processor with BSP.
// If yes, direct use microcode info saved by BSP.
//
if (!IsBspCallIn) {
if ((CpuMpData->ProcessorSignature == Eax.Uint32) &&
(CpuMpData->ProcessorFlags & (1 << PlatformId)) != 0) {
MicrocodeData = (VOID *)(UINTN) CpuMpData->MicrocodeDataAddress;
LatestRevision = CpuMpData->MicrocodeRevision;
goto Done;
}
}
LatestRevision = 0;
MicrocodeData = NULL;
MicrocodeEnd = (UINTN) (CpuMpData->MicrocodePatchAddress + CpuMpData->MicrocodePatchRegionSize);
MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (UINTN) CpuMpData->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->DataSize == 0) {
TotalSize = sizeof (CPU_MICROCODE_HEADER) + 2000;
} else {
TotalSize = sizeof (CPU_MICROCODE_HEADER) + MicrocodeEntryPoint->DataSize;
}
///
/// Check overflow and whether TotalSize is aligned with 4 bytes.
///
if ( ((UINTN)MicrocodeEntryPoint + TotalSize) > MicrocodeEnd ||
(TotalSize & 0x3) != 0
) {
MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (((UINTN) MicrocodeEntryPoint) + SIZE_1KB);
continue;
}
//
// Save an in-complete CheckSum32 from CheckSum Part1 for common parts.
//
InCompleteCheckSum32 = CalculateSum32 (
(UINT32 *) MicrocodeEntryPoint,
TotalSize
);
InCompleteCheckSum32 -= MicrocodeEntryPoint->ProcessorSignature.Uint32;
InCompleteCheckSum32 -= MicrocodeEntryPoint->ProcessorFlags;
InCompleteCheckSum32 -= MicrocodeEntryPoint->Checksum;
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))
) {
//
// Calculate CheckSum Part1.
//
CheckSum32 = InCompleteCheckSum32;
CheckSum32 += MicrocodeEntryPoint->ProcessorSignature.Uint32;
CheckSum32 += MicrocodeEntryPoint->ProcessorFlags;
CheckSum32 += MicrocodeEntryPoint->Checksum;
if (CheckSum32 == 0) {
CorrectMicrocode = TRUE;
ProcessorFlags = MicrocodeEntryPoint->ProcessorFlags;
}
} 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) {
//
// Calculate CheckSum Part2.
//
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 ++) {
//
// Calculate CheckSum Part3.
//
CheckSum32 = InCompleteCheckSum32;
CheckSum32 += ExtendedTable->ProcessorSignature.Uint32;
CheckSum32 += ExtendedTable->ProcessorFlag;
CheckSum32 += ExtendedTable->Checksum;
if (CheckSum32 == 0) {
//
// Verify Header
//
if ((ExtendedTable->ProcessorSignature.Uint32 == Eax.Uint32) &&
(ExtendedTable->ProcessorFlag & (1 << PlatformId)) ) {
//
// Find one
//
CorrectMicrocode = TRUE;
ProcessorFlags = ExtendedTable->ProcessorFlag;
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));
Done:
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.
//
ASSERT (MicrocodeData != NULL);
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);
}
}
if (IsBspCallIn && (LatestRevision != 0)) {
//
// Save BSP processor info and microcode info for later AP use.
//
CpuMpData->ProcessorSignature = Eax.Uint32;
CpuMpData->ProcessorFlags = ProcessorFlags;
CpuMpData->MicrocodeDataAddress = (UINTN) MicrocodeData;
CpuMpData->MicrocodeRevision = LatestRevision;
DEBUG ((DEBUG_INFO, "BSP Microcode:: signature [0x%08x], ProcessorFlags [0x%08x], \
MicroData [0x%08x], Revision [0x%08x]\n", Eax.Uint32, ProcessorFlags, (UINTN) MicrocodeData, LatestRevision));
}
}