Signed-off-by: Ray Ni <ray.ni@intel.com> Reviewed-by: Eric Dong <eric.dong@intel.com> Acked-by: Laszlo Ersek <lersek@redhat.com> Cc: Rahul Kumar <rahul1.kumar@intel.com>
		
			
				
	
	
		
			375 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			375 lines
		
	
	
		
			13 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",
 | 
						|
    __FUNCTION__, 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",
 | 
						|
      __FUNCTION__, 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", __FUNCTION__));
 | 
						|
    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",
 | 
						|
    __FUNCTION__, *Address, *RegionSize
 | 
						|
    ));
 | 
						|
 | 
						|
  return TRUE;
 | 
						|
}
 |