audk/UefiCpuPkg/Library/MpInitLib/Microcode.c

387 lines
12 KiB
C

/** @file
Implementation of loading microcode on processors.
Copyright (c) 2015 - 2021, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "MpLib.h"
/**
Detect whether specified processor can find matching microcode patch and load it.
@param[in] CpuMpData The pointer to CPU MP Data structure.
@param[in] ProcessorNumber The handle number of the processor. The range is
from 0 to the total number of logical processors
minus 1.
**/
VOID
MicrocodeDetect (
IN CPU_MP_DATA *CpuMpData,
IN UINTN ProcessorNumber
)
{
CPU_MICROCODE_HEADER *Microcode;
UINTN MicrocodeEnd;
CPU_AP_DATA *BspData;
UINT32 LatestRevision;
CPU_MICROCODE_HEADER *LatestMicrocode;
UINT32 ThreadId;
EDKII_PEI_MICROCODE_CPU_ID MicrocodeCpuId;
if (CpuMpData->MicrocodePatchRegionSize == 0) {
//
// There is no microcode patches
//
return;
}
GetProcessorLocationByApicId (GetInitialApicId (), NULL, NULL, &ThreadId);
if (ThreadId != 0) {
//
// Skip loading microcode if it is not the first thread in one core.
//
return;
}
GetProcessorMicrocodeCpuId (&MicrocodeCpuId);
if (ProcessorNumber != (UINTN)CpuMpData->BspNumber) {
//
// Direct use microcode of BSP if AP is the same as BSP.
// Assume BSP calls this routine() before AP.
//
BspData = &(CpuMpData->CpuData[CpuMpData->BspNumber]);
if ((BspData->ProcessorSignature == MicrocodeCpuId.ProcessorSignature) &&
(BspData->PlatformId == MicrocodeCpuId.PlatformId) &&
(BspData->MicrocodeEntryAddr != 0))
{
LatestMicrocode = (CPU_MICROCODE_HEADER *)(UINTN)BspData->MicrocodeEntryAddr;
LatestRevision = LatestMicrocode->UpdateRevision;
goto LoadMicrocode;
}
}
//
// BSP or AP which is different from BSP runs here
// Use 0 as the starting revision to search for microcode because MicrocodePatchInfo HOB needs
// the latest microcode location even it's loaded to the processor.
//
LatestRevision = 0;
LatestMicrocode = NULL;
Microcode = (CPU_MICROCODE_HEADER *)(UINTN)CpuMpData->MicrocodePatchAddress;
MicrocodeEnd = (UINTN)Microcode + (UINTN)CpuMpData->MicrocodePatchRegionSize;
do {
if (!IsValidMicrocode (Microcode, MicrocodeEnd - (UINTN)Microcode, LatestRevision, &MicrocodeCpuId, 1, TRUE)) {
//
// 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.
//
Microcode = (CPU_MICROCODE_HEADER *)((UINTN)Microcode + SIZE_1KB);
continue;
}
LatestMicrocode = Microcode;
LatestRevision = LatestMicrocode->UpdateRevision;
Microcode = (CPU_MICROCODE_HEADER *)(((UINTN)Microcode) + GetMicrocodeLength (Microcode));
} while ((UINTN)Microcode < MicrocodeEnd);
LoadMicrocode:
if (LatestRevision != 0) {
//
// Save the detected microcode patch entry address (including the microcode
// patch header) for each processor even it's the same as the loaded one.
// It will be used when building the microcode patch cache HOB.
//
CpuMpData->CpuData[ProcessorNumber].MicrocodeEntryAddr = (UINTN)LatestMicrocode;
}
if (LatestRevision > GetProcessorMicrocodeSignature ()) {
//
// 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.
//
LoadMicrocode (LatestMicrocode);
}
//
// It's possible that the microcode fails to load. Just capture the CPU microcode revision after loading.
//
CpuMpData->CpuData[ProcessorNumber].MicrocodeRevision = GetProcessorMicrocodeSignature ();
}
/**
Actual worker function that shadows the required microcode patches into memory.
@param[in, out] CpuMpData The pointer to CPU MP Data structure.
@param[in] Patches The pointer to an array of information on
the microcode patches that will be loaded
into memory.
@param[in] PatchCount The number of microcode patches that will
be loaded into memory.
@param[in] TotalLoadSize The total size of all the microcode patches
to be loaded.
**/
VOID
ShadowMicrocodePatchWorker (
IN OUT CPU_MP_DATA *CpuMpData,
IN MICROCODE_PATCH_INFO *Patches,
IN UINTN PatchCount,
IN UINTN TotalLoadSize
)
{
UINTN Index;
VOID *MicrocodePatchInRam;
UINT8 *Walker;
ASSERT ((Patches != NULL) && (PatchCount != 0));
MicrocodePatchInRam = AllocatePages (EFI_SIZE_TO_PAGES (TotalLoadSize));
if (MicrocodePatchInRam == NULL) {
return;
}
//
// Load all the required microcode patches into memory
//
for (Walker = MicrocodePatchInRam, Index = 0; Index < PatchCount; Index++) {
CopyMem (
Walker,
(VOID *)Patches[Index].Address,
Patches[Index].Size
);
Walker += Patches[Index].Size;
}
//
// Update the microcode patch related fields in CpuMpData
//
CpuMpData->MicrocodePatchAddress = (UINTN)MicrocodePatchInRam;
CpuMpData->MicrocodePatchRegionSize = TotalLoadSize;
DEBUG ((
DEBUG_INFO,
"%a: Required microcode patches have been loaded at 0x%lx, with size 0x%lx.\n",
__func__,
CpuMpData->MicrocodePatchAddress,
CpuMpData->MicrocodePatchRegionSize
));
return;
}
/**
Shadow the required microcode patches data into memory according to PCD
PcdCpuMicrocodePatchAddress and PcdCpuMicrocodePatchRegionSize.
@param[in, out] CpuMpData The pointer to CPU MP Data structure.
**/
VOID
ShadowMicrocodePatchByPcd (
IN OUT CPU_MP_DATA *CpuMpData
)
{
UINTN Index;
CPU_MICROCODE_HEADER *MicrocodeEntryPoint;
UINTN MicrocodeEnd;
UINTN TotalSize;
MICROCODE_PATCH_INFO *PatchInfoBuffer;
UINTN MaxPatchNumber;
UINTN PatchCount;
UINTN TotalLoadSize;
EDKII_PEI_MICROCODE_CPU_ID *MicrocodeCpuIds;
BOOLEAN Valid;
//
// Initialize the microcode patch related fields in CpuMpData as the values
// specified by the PCD pair. If the microcode patches are loaded into memory,
// these fields will be updated.
//
CpuMpData->MicrocodePatchAddress = PcdGet64 (PcdCpuMicrocodePatchAddress);
CpuMpData->MicrocodePatchRegionSize = PcdGet64 (PcdCpuMicrocodePatchRegionSize);
MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *)(UINTN)CpuMpData->MicrocodePatchAddress;
MicrocodeEnd = (UINTN)MicrocodeEntryPoint +
(UINTN)CpuMpData->MicrocodePatchRegionSize;
if ((MicrocodeEntryPoint == NULL) || ((UINTN)MicrocodeEntryPoint == MicrocodeEnd)) {
//
// There is no microcode patches
//
return;
}
PatchCount = 0;
MaxPatchNumber = DEFAULT_MAX_MICROCODE_PATCH_NUM;
TotalLoadSize = 0;
PatchInfoBuffer = AllocatePool (MaxPatchNumber * sizeof (MICROCODE_PATCH_INFO));
if (PatchInfoBuffer == NULL) {
return;
}
MicrocodeCpuIds = AllocatePages (
EFI_SIZE_TO_PAGES (CpuMpData->CpuCount * sizeof (EDKII_PEI_MICROCODE_CPU_ID))
);
if (MicrocodeCpuIds == NULL) {
FreePool (PatchInfoBuffer);
return;
}
for (Index = 0; Index < CpuMpData->CpuCount; Index++) {
MicrocodeCpuIds[Index].PlatformId = CpuMpData->CpuData[Index].PlatformId;
MicrocodeCpuIds[Index].ProcessorSignature = CpuMpData->CpuData[Index].ProcessorSignature;
}
//
// Process the header of each microcode patch within the region.
// The purpose is to decide which microcode patch(es) will be loaded into memory.
// Microcode checksum is not verified because it's slow when performing on flash.
//
do {
Valid = IsValidMicrocode (
MicrocodeEntryPoint,
MicrocodeEnd - (UINTN)MicrocodeEntryPoint,
0,
MicrocodeCpuIds,
CpuMpData->CpuCount,
FALSE
);
if (!Valid) {
//
// Padding data between the microcode patches, skip 1KB to check next entry.
//
MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *)(((UINTN)MicrocodeEntryPoint) + SIZE_1KB);
continue;
}
PatchCount++;
if (PatchCount > MaxPatchNumber) {
//
// Current 'PatchInfoBuffer' cannot hold the information, double the size
// and allocate a new buffer.
//
if (MaxPatchNumber > MAX_UINTN / 2 / sizeof (MICROCODE_PATCH_INFO)) {
//
// Overflow check for MaxPatchNumber
//
goto OnExit;
}
PatchInfoBuffer = ReallocatePool (
MaxPatchNumber * sizeof (MICROCODE_PATCH_INFO),
2 * MaxPatchNumber * sizeof (MICROCODE_PATCH_INFO),
PatchInfoBuffer
);
if (PatchInfoBuffer == NULL) {
goto OnExit;
}
MaxPatchNumber = MaxPatchNumber * 2;
}
TotalSize = GetMicrocodeLength (MicrocodeEntryPoint);
//
// Store the information of this microcode patch
//
PatchInfoBuffer[PatchCount - 1].Address = (UINTN)MicrocodeEntryPoint;
PatchInfoBuffer[PatchCount - 1].Size = TotalSize;
TotalLoadSize += TotalSize;
//
// Process the next microcode patch
//
MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *)((UINTN)MicrocodeEntryPoint + TotalSize);
} while ((UINTN)MicrocodeEntryPoint < MicrocodeEnd);
if (PatchCount != 0) {
DEBUG ((
DEBUG_INFO,
"%a: 0x%x microcode patches will be loaded into memory, with size 0x%x.\n",
__func__,
PatchCount,
TotalLoadSize
));
ShadowMicrocodePatchWorker (CpuMpData, PatchInfoBuffer, PatchCount, TotalLoadSize);
}
OnExit:
if (PatchInfoBuffer != NULL) {
FreePool (PatchInfoBuffer);
}
FreePages (MicrocodeCpuIds, EFI_SIZE_TO_PAGES (CpuMpData->CpuCount * sizeof (EDKII_PEI_MICROCODE_CPU_ID)));
}
/**
Shadow the required microcode patches data into memory.
@param[in, out] CpuMpData The pointer to CPU MP Data structure.
**/
VOID
ShadowMicrocodeUpdatePatch (
IN OUT CPU_MP_DATA *CpuMpData
)
{
EFI_STATUS Status;
Status = PlatformShadowMicrocode (CpuMpData);
if (EFI_ERROR (Status)) {
ShadowMicrocodePatchByPcd (CpuMpData);
}
}
/**
Get the cached microcode patch base address and size from the microcode patch
information cache HOB.
@param[out] Address Base address of the microcode patches data.
It will be updated if the microcode patch
information cache HOB is found.
@param[out] RegionSize Size of the microcode patches data.
It will be updated if the microcode patch
information cache HOB is found.
@retval TRUE The microcode patch information cache HOB is found.
@retval FALSE The microcode patch information cache HOB is not found.
**/
BOOLEAN
GetMicrocodePatchInfoFromHob (
UINT64 *Address,
UINT64 *RegionSize
)
{
EFI_HOB_GUID_TYPE *GuidHob;
EDKII_MICROCODE_PATCH_HOB *MicrocodePathHob;
GuidHob = GetFirstGuidHob (&gEdkiiMicrocodePatchHobGuid);
if (GuidHob == NULL) {
DEBUG ((DEBUG_INFO, "%a: Microcode patch cache HOB is not found.\n", __func__));
return FALSE;
}
MicrocodePathHob = GET_GUID_HOB_DATA (GuidHob);
*Address = MicrocodePathHob->MicrocodePatchAddress;
*RegionSize = MicrocodePathHob->MicrocodePatchRegionSize;
DEBUG ((
DEBUG_INFO,
"%a: MicrocodeBase = 0x%lx, MicrocodeSize = 0x%lx\n",
__func__,
*Address,
*RegionSize
));
return TRUE;
}