Cc: Eric Dong <eric.dong@intel.com> Cc: Ruiyu Ni <ruiyu.ni@intel.com> Cc: Jiewen Yao <jiewen.yao@intel.com> Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Star Zeng <star.zeng@intel.com> Reviewed-by: Eric Dong <eric.dong@intel.com>
		
			
				
	
	
		
			215 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			215 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @file
 | 
						|
  Implementation of loading microcode on processors.
 | 
						|
 | 
						|
  Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
 | 
						|
  This program and the accompanying materials
 | 
						|
  are licensed and made available under the terms and conditions of the BSD License
 | 
						|
  which accompanies this distribution.  The full text of the license may be found at
 | 
						|
  http://opensource.org/licenses/bsd-license.php
 | 
						|
 | 
						|
  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
 | 
						|
  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 | 
						|
 | 
						|
**/
 | 
						|
 | 
						|
#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.
 | 
						|
 | 
						|
  @param[in]  CpuMpData  The pointer to CPU MP Data structure.
 | 
						|
**/
 | 
						|
VOID
 | 
						|
MicrocodeDetect (
 | 
						|
  IN CPU_MP_DATA             *CpuMpData
 | 
						|
  )
 | 
						|
{
 | 
						|
  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;
 | 
						|
  BOOLEAN                                 CorrectMicrocode;
 | 
						|
  VOID                                    *MicrocodeData;
 | 
						|
  MSR_IA32_PLATFORM_ID_REGISTER           PlatformIdMsr;
 | 
						|
 | 
						|
  if (CpuMpData->MicrocodePatchRegionSize == 0) {
 | 
						|
    //
 | 
						|
    // There is no microcode patches
 | 
						|
    //
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  CurrentRevision = GetCurrentMicrocodeSignature ();
 | 
						|
  if (CurrentRevision != 0) {
 | 
						|
    //
 | 
						|
    // Skip loading microcode if it has been loaded successfully
 | 
						|
    //
 | 
						|
    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;
 | 
						|
 | 
						|
  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->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))
 | 
						|
          ) {
 | 
						|
        if (MicrocodeEntryPoint->DataSize == 0) {
 | 
						|
          CheckSum32 = CalculateSum32 ((UINT32 *) MicrocodeEntryPoint, 2048);
 | 
						|
        } else {
 | 
						|
          CheckSum32 = CalculateSum32 (
 | 
						|
                         (UINT32 *) MicrocodeEntryPoint,
 | 
						|
                         MicrocodeEntryPoint->DataSize + sizeof (CPU_MICROCODE_HEADER)
 | 
						|
                         );
 | 
						|
        }
 | 
						|
        if (CheckSum32 == 0) {
 | 
						|
          CorrectMicrocode = TRUE;
 | 
						|
        }
 | 
						|
      } 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) {
 | 
						|
            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 ++) {
 | 
						|
                CheckSum32 = CalculateSum32 ((UINT32 *) ExtendedTable, sizeof(CPU_MICROCODE_EXTENDED_TABLE));
 | 
						|
                if (CheckSum32 == 0) {
 | 
						|
                  //
 | 
						|
                  // Verify Header
 | 
						|
                  //
 | 
						|
                  if ((ExtendedTable->ProcessorSignature.Uint32 == Eax.Uint32) &&
 | 
						|
                      (ExtendedTable->ProcessorFlag & (1 << PlatformId)) ) {
 | 
						|
                    //
 | 
						|
                    // Find one
 | 
						|
                    //
 | 
						|
                    CorrectMicrocode = TRUE;
 | 
						|
                    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));
 | 
						|
 | 
						|
  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);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 |