/** @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; | |
} |