mirror of https://github.com/acidanthera/audk.git
387 lines
12 KiB
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;
|
|
}
|