summaryrefslogtreecommitdiff
path: root/ReferenceCode/Haswell/CpuInit/Dxe/Microcode.c
diff options
context:
space:
mode:
Diffstat (limited to 'ReferenceCode/Haswell/CpuInit/Dxe/Microcode.c')
-rw-r--r--ReferenceCode/Haswell/CpuInit/Dxe/Microcode.c547
1 files changed, 547 insertions, 0 deletions
diff --git a/ReferenceCode/Haswell/CpuInit/Dxe/Microcode.c b/ReferenceCode/Haswell/CpuInit/Dxe/Microcode.c
new file mode 100644
index 0000000..3f54a7d
--- /dev/null
+++ b/ReferenceCode/Haswell/CpuInit/Dxe/Microcode.c
@@ -0,0 +1,547 @@
+/** @file
+ CPU Microcode update driver
+
+@copyright
+ Copyright (c) 1999 - 2012 Intel Corporation. All rights reserved
+ This software and associated documentation (if any) is furnished
+ under a license and may only be used or copied in accordance
+ with the terms of the license. Except as permitted by such
+ license, no part of this software or documentation may be
+ reproduced, stored in a retrieval system, or transmitted in any
+ form or by any means without the express written consent of
+ Intel Corporation.
+
+ This file contains an 'Intel Peripheral Driver' and is
+ licensed for Intel CPUs and chipsets under the terms of your
+ license agreement with Intel or your vendor. This file may
+ be modified by the user, subject to additional terms of the
+ license agreement
+
+**/
+#if !defined(EDK_RELEASE_VERSION) || (EDK_RELEASE_VERSION < 0x00020000)
+#include "EdkIIGlueDxe.h"
+#include "CpuInitDxe.h"
+#include "ProcessorData.h"
+#include "MpCommon.h"
+#include "MpService.h"
+#endif
+
+extern DXE_CPU_PLATFORM_POLICY_PROTOCOL *mPlatformCpu;
+
+///
+/// static type so this variable only can be accessed in this file
+///
+static UINT32 mMcuFirstLoadDone = FALSE;
+UINT32 mMcuLoadCount;
+
+///
+/// Array of pointers which each points to 1 microcode update binary (in memory)
+///
+EFI_CPU_MICROCODE_HEADER **mMicrocodePointerBuffer;
+
+///
+/// Function declarations
+///
+EFI_STATUS
+FindLoadMicrocode (
+ IN UINT32 Cpuid,
+ IN EFI_CPU_MICROCODE_HEADER **MicrocodePointerBuffer,
+ IN OUT UINT32 *Revision
+ );
+
+/**
+ To indicate the first microcode load is done
+**/
+VOID
+McuFirstLoadDone (
+ VOID
+ )
+{
+ mMcuFirstLoadDone = TRUE;
+}
+
+/**
+ Wait till all primary threads done the microcode load
+**/
+VOID
+WaitForPrimaryThreadMcuUpdate (
+ VOID
+ )
+{
+ UINTN CoreNumber;
+ CoreNumber = RShiftU64 (AsmReadMsr64 (MSR_CORE_THREAD_COUNT), 16) & 0xffff;
+ if (IsSecondaryThread ()) {
+ while (mMcuLoadCount < CoreNumber) {
+ CpuPause ();
+ }
+ }
+}
+
+/**
+ This function checks the MCU revision to decide if BIOS needs to load
+ microcode.
+
+ @param[in] MicrocodePointer - Microcode in memory
+ @param[in] Revision - Current CPU microcode revision
+
+ @retval EFI_SUCCESS - BIOS needs to load microcode
+ @retval EFI_ABORTED - Don't need to update microcode
+**/
+EFI_STATUS
+CheckMcuRevision (
+ IN EFI_CPU_MICROCODE_HEADER *MicrocodePointer,
+ IN UINT32 *Revision
+ )
+{
+ EFI_STATUS Status;
+ Status = EFI_ABORTED;
+
+ if (mMcuFirstLoadDone) {
+ if (MicrocodePointer->UpdateRevision & 0x80000000 ||
+ (MicrocodePointer->UpdateRevision > 0 && MicrocodePointer->UpdateRevision > *Revision)
+ ) {
+ Status = EFI_SUCCESS;
+ }
+ } else {
+ if (*Revision == 0) {
+ Status = EFI_SUCCESS;
+ }
+ }
+
+ return Status;
+}
+
+/**
+ This will locate a processor microcode and if it finds a newer revision, it will
+ load it to the processor.
+
+ @param[in] MicrocodePointerBuffer - The Array of pointers which each points to 1 microcode update binary (in memory)
+ @param[in] FailedRevision - The microcode revision that fails to be loaded
+
+ @retval EFI_SUCCESS - A new microcode update is loaded
+ @retval Other - Due to some reason, no new microcode update is loaded
+**/
+EFI_STATUS
+InitializeMicrocode (
+ IN EFI_CPU_MICROCODE_HEADER **MicrocodePointerBuffer,
+ OUT UINT32 *FailedRevision
+ )
+{
+ EFI_STATUS Status;
+ EFI_CPUID_REGISTER Cpuid;
+ UINT32 UcodeRevision;
+
+ AsmCpuid (
+ CPUID_VERSION_INFO,
+ &Cpuid.RegEax,
+ &Cpuid.RegEbx,
+ &Cpuid.RegEcx,
+ &Cpuid.RegEdx
+ );
+
+ WaitForPrimaryThreadMcuUpdate ();
+ UcodeRevision = GetCpuUcodeRevision ();
+ Status = FindLoadMicrocode (
+ Cpuid.RegEax,
+ MicrocodePointerBuffer,
+ &UcodeRevision
+ );
+ *FailedRevision = UcodeRevision;
+
+ InterlockedIncrement (&mMcuLoadCount);
+
+ return Status;
+}
+
+/**
+ This will load the microcode to the processors.
+
+ @param[in] MicrocodeEntryPoint - The microcode update pointer
+ @param[in] Revision - The current (before load this microcode update) microcode revision
+
+ @retval EFI_SUCCESS - Microcode loaded
+ @retval EFI_LOAD_ERROR - Microcode not loaded
+**/
+EFI_STATUS
+LoadMicrocode (
+ IN EFI_CPU_MICROCODE_HEADER *MicrocodeEntryPoint,
+ IN UINT32 *Revision
+ )
+{
+ ///
+ /// Load the Processor Microcode
+ ///
+ AsmWriteMsr64 (
+ MSR_IA32_BIOS_UPDT_TRIG,
+ (UINT64) ((UINTN) MicrocodeEntryPoint + sizeof (EFI_CPU_MICROCODE_HEADER))
+ );
+
+ ///
+ /// Verify that the microcode has been loaded
+ ///
+ if (GetCpuUcodeRevision () == *Revision) {
+ return EFI_LOAD_ERROR;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Verify the DWORD type checksum
+
+ @param[in] ChecksumAddr - The start address to be checksumed
+ @param[in] ChecksumLen - The length of data that will be checksumed
+
+ @retval EFI_SUCCESS - Checksum correct
+ @retval EFI_CRC_ERROR - Checksum incorrect
+**/
+EFI_STATUS
+Checksum32Verify (
+ IN UINT32 *ChecksumAddr,
+ IN UINT32 ChecksumLen
+ )
+{
+ UINT32 Checksum;
+ UINT32 Index;
+
+ Checksum = 0;
+
+ for (Index = 0; Index < ChecksumLen; Index++) {
+ Checksum += ChecksumAddr[Index];
+ }
+
+ return (Checksum == 0) ? EFI_SUCCESS : EFI_CRC_ERROR;
+}
+
+/**
+ Check if this microcode is correct one for processor
+
+ @param[in] Cpuid - processor CPUID
+ @param[in] MicrocodeEntryPoint - entry point of microcode
+ @param[in] Revision - revision of microcode
+
+ @retval CorrectMicrocode if this microcode is correct
+**/
+BOOLEAN
+CheckMicrocode (
+ IN UINTN Cpuid,
+ IN EFI_CPU_MICROCODE_HEADER *MicrocodeEntryPoint,
+ IN UINT32 *Revision
+ )
+{
+ EFI_STATUS Status;
+ UINT8 ExtendedIndex;
+ UINT8 MsrPlatform;
+ UINT32 ExtendedTableLength;
+ UINT32 ExtendedTableCount;
+ BOOLEAN CorrectMicrocode;
+ EFI_CPU_MICROCODE_EXTENDED_TABLE *ExtendedTable;
+ EFI_CPU_MICROCODE_EXTENDED_TABLE_HEADER *ExtendedTableHeader;
+
+ Status = EFI_NOT_FOUND;
+ ExtendedTableLength = 0;
+ CorrectMicrocode = FALSE;
+
+ ///
+ /// The index of platform information resides in bits 50:52 of MSR IA32_PLATFORM_ID
+ ///
+ MsrPlatform = (UINT8) (RShiftU64 (AsmReadMsr64 (MSR_IA32_PLATFORM_ID), 50) & 0x07);
+
+ ///
+ /// Check if the microcode is for the Cpu and the version is newer
+ /// and the update can be processed on the platform
+ ///
+ if ((MicrocodeEntryPoint->HeaderVersion == 0x00000001) &&
+ !EFI_ERROR (CheckMcuRevision (MicrocodeEntryPoint, Revision))
+ ) {
+ if ((MicrocodeEntryPoint->ProcessorId == Cpuid) && (MicrocodeEntryPoint->ProcessorFlags & (1 << MsrPlatform))) {
+ if (MicrocodeEntryPoint->DataSize == 0) {
+ Status = Checksum32Verify ((UINT32 *) MicrocodeEntryPoint, 2048 / sizeof (UINT32));
+ } else {
+ Status = Checksum32Verify (
+ (UINT32 *) MicrocodeEntryPoint,
+ (MicrocodeEntryPoint->DataSize + sizeof (EFI_CPU_MICROCODE_HEADER)) / sizeof (UINT32)
+ );
+ }
+
+ if (!EFI_ERROR (Status)) {
+ CorrectMicrocode = TRUE;
+ }
+ } else if ((MicrocodeEntryPoint->DataSize != 0)) {
+ ///
+ /// Check the Extended Signature if the entended signature exist
+ /// Only the data size != 0 the extended signature may exist
+ ///
+ ExtendedTableLength = MicrocodeEntryPoint->TotalSize - (MicrocodeEntryPoint->DataSize + sizeof (EFI_CPU_MICROCODE_HEADER));
+ if (ExtendedTableLength != 0) {
+ ///
+ /// Extended Table exist, check if the CPU in support list
+ ///
+ ExtendedTableHeader = (EFI_CPU_MICROCODE_EXTENDED_TABLE_HEADER *) ((UINT8 *) (MicrocodeEntryPoint) + MicrocodeEntryPoint->DataSize + 48);
+ ///
+ /// Calulate Extended Checksum
+ ///
+ if ((ExtendedTableLength % 4) == 0) {
+ Status = Checksum32Verify ((UINT32 *) ExtendedTableHeader, ExtendedTableLength / sizeof (UINT32));
+ if (!EFI_ERROR (Status)) {
+ ///
+ /// Checksum correct
+ ///
+ ExtendedTableCount = ExtendedTableHeader->ExtendedSignatureCount;
+ ExtendedTable = (EFI_CPU_MICROCODE_EXTENDED_TABLE *) (ExtendedTableHeader + 1);
+ for (ExtendedIndex = 0; ExtendedIndex < ExtendedTableCount; ExtendedIndex++) {
+ ///
+ /// Verify Header
+ ///
+ if ((ExtendedTable->ProcessorSignature == Cpuid) && (ExtendedTable->ProcessorFlag & (1 << MsrPlatform))) {
+ Status = Checksum32Verify (
+ (UINT32 *) ExtendedTable,
+ sizeof (EFI_CPU_MICROCODE_EXTENDED_TABLE) / sizeof (UINT32)
+ );
+ if (!EFI_ERROR (Status)) {
+ ///
+ /// Find one
+ ///
+ CorrectMicrocode = TRUE;
+ break;
+ }
+ }
+
+ ExtendedTable++;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return CorrectMicrocode;
+}
+
+/**
+ Find microcode data
+
+ @param[in] Cpuid - processor CPUID
+ @param[in] MicrocodePointerBuffer - the pointer for microcode buffer
+ @param[in] Revision - revision of microcode
+
+ @retval The pointer of microcode header
+**/
+EFI_CPU_MICROCODE_HEADER *
+FindMicrocode (
+ IN UINTN Cpuid,
+ IN EFI_CPU_MICROCODE_HEADER **MicrocodePointerBuffer,
+ IN UINT32 *Revision
+ )
+{
+ EFI_STATUS Status;
+ EFI_CPU_MICROCODE_HEADER *MicrocodeEntryPoint;
+ UINT8 Index;
+ BOOLEAN CorrectMicrocode;
+
+ Status = EFI_NOT_FOUND;
+ MicrocodeEntryPoint = NULL;
+ CorrectMicrocode = FALSE;
+
+ Index = 0;
+ while (MicrocodePointerBuffer[Index] != NULL) {
+ MicrocodeEntryPoint = MicrocodePointerBuffer[Index];
+ CorrectMicrocode = CheckMicrocode (Cpuid, MicrocodeEntryPoint, Revision);
+
+ if (CorrectMicrocode) {
+ break;
+ }
+
+ Index++;
+ }
+
+ if (!CorrectMicrocode) {
+ MicrocodeEntryPoint = NULL;
+ }
+
+ return MicrocodeEntryPoint;
+}
+
+/**
+ This will locate a processor microcode and if it finds a newer revision, it will
+ load it to the processor.
+
+ @param[in] Cpuid - Data returned by cpuid instruction
+ @param[in] MicrocodePointerBuffer - The Array of pointers which each points to 1 microcode update binary (in memory)
+ @param[in] Revision - As input parameter, the current microcode revision;
+ as output parameter, the microcode revision after microcode update is loaded
+
+ @retval EFI_SUCCESS - A new microcode update is loaded
+ @retval Other - Due to some reason, no new microcode update is loaded
+**/
+EFI_STATUS
+FindLoadMicrocode (
+ IN UINT32 Cpuid,
+ IN EFI_CPU_MICROCODE_HEADER **MicrocodePointerBuffer,
+ IN OUT UINT32 *Revision
+ )
+{
+ EFI_STATUS Status;
+ EFI_CPU_MICROCODE_HEADER *MicrocodeEntryPoint;
+
+ Status = EFI_NOT_FOUND;
+
+ MicrocodeEntryPoint = FindMicrocode (
+ Cpuid,
+ MicrocodePointerBuffer,
+ Revision
+ );
+
+ if (MicrocodeEntryPoint != NULL) {
+ Status = LoadMicrocode (MicrocodeEntryPoint, Revision);
+ *Revision = MicrocodeEntryPoint->UpdateRevision;
+ }
+
+ return Status;
+}
+
+/**
+ Load all microcode updates to memory. Since in S3 resume boot path, CPUs should be
+ patched again, these microcode updates are copied to OS reserved memory.
+
+ @retval EFI_SUCCESS - All microcode updates are loaded to memory successfully
+ @retval EFI_OUT_OF_RESOURCES - Not enough memory to accomodate all the microcode updates
+**/
+EFI_STATUS
+LoadAllMicrocodeUpdates (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ EFI_CPU_MICROCODE_HEADER *MicrocodeEntryPoint;
+ EFI_CPU_MICROCODE_HEADER *MicrocodeBuffer;
+ UINTN MicrocodeNumber;
+ UINTN Index;
+ UINTN TotalSize[NUMBER_OF_MICROCODE_UPDATE + 1];
+ EFI_CPUID_REGISTER Cpuid;
+ UINT32 UcodeRevision;
+
+ AsmCpuid (
+ CPUID_VERSION_INFO,
+ &Cpuid.RegEax,
+ &Cpuid.RegEbx,
+ &Cpuid.RegEcx,
+ &Cpuid.RegEdx
+ );
+
+ UcodeRevision = 0;
+ MicrocodeNumber = 0;
+ Status = (gBS->AllocatePool)
+ (
+ EfiReservedMemoryType, sizeof (EFI_CPU_MICROCODE_HEADER *) * (NUMBER_OF_MICROCODE_UPDATE + 1), (VOID *)
+ (&mMicrocodePointerBuffer)
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ ZeroMem (mMicrocodePointerBuffer, sizeof (EFI_CPU_MICROCODE_HEADER *) * (NUMBER_OF_MICROCODE_UPDATE + 1));
+
+ MicrocodeEntryPoint = NULL;
+ while (TRUE) {
+ if (MicrocodeNumber > NUMBER_OF_MICROCODE_UPDATE) {
+ DEBUG ((EFI_D_INFO, "CPU Too Many Microcode available > %d\n", (UINTN) NUMBER_OF_MICROCODE_UPDATE));
+ Status = EFI_SUCCESS;
+ break;
+ }
+ ///
+ /// Initialize it to 0
+ ///
+ TotalSize[MicrocodeNumber] = 0;
+ ///
+ /// Continue to try to find patch
+ ///
+ Status = mPlatformCpu->CpuConfig->RetrieveMicrocode (mPlatformCpu, (VOID *) &MicrocodeEntryPoint);
+
+ if (EFI_ERROR (Status)) {
+ if (Status == EFI_NOT_FOUND) {
+ Status = EFI_SUCCESS;
+ }
+ break;
+
+ } else {
+ if (!CheckMicrocode (Cpuid.RegEax, MicrocodeEntryPoint, &UcodeRevision)) {
+ continue;
+ }
+
+ if (MicrocodeEntryPoint->DataSize == 0) {
+ TotalSize[MicrocodeNumber] = 2048;
+ } else {
+ TotalSize[MicrocodeNumber] = MicrocodeEntryPoint->TotalSize;
+ }
+
+ Status = AllocateReservedMemoryBelow4G (
+ TotalSize[MicrocodeNumber],
+ &MicrocodeBuffer
+ );
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+
+ CopyMem (MicrocodeBuffer, MicrocodeEntryPoint, TotalSize[MicrocodeNumber]);
+ mMicrocodePointerBuffer[MicrocodeNumber] = MicrocodeBuffer;
+ MicrocodeNumber++;
+ }
+ }
+
+ if (EFI_ERROR (Status)) {
+ Index = 0;
+ while ((Index <= NUMBER_OF_MICROCODE_UPDATE) && (mMicrocodePointerBuffer[Index] != NULL)) {
+ (gBS->FreePages)((EFI_PHYSICAL_ADDRESS) (UINTN) mMicrocodePointerBuffer[Index], EFI_SIZE_TO_PAGES (TotalSize[Index]));
+ mMicrocodePointerBuffer[Index] = NULL;
+ Index++;
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Check if loading microcode update fails, if so, report proper status code
+
+ @param[in] CpuNumber - The CPU number
+ @param[in] Status - The return value of InitializeMicrocode()
+ @param[in] FailedRevision - The revision of the microcode update that failed to be loaded
+
+ @retval EFI_SUCCESS - The status is check and proper status code is reported
+**/
+EFI_STATUS
+CheckMicrocodeUpdate (
+ IN UINTN CpuNumber,
+ IN EFI_STATUS Status,
+ IN UINT32 FailedRevision
+ )
+{
+ EFI_STATUS_CODE_VALUE StatusCode;
+
+ if (!EFI_ERROR (Status)) {
+ return EFI_SUCCESS;
+ }
+ if (Status == EFI_LOAD_ERROR) {
+ StatusCode = EFI_COMPUTING_UNIT_HOST_PROCESSOR | EFI_CU_HP_EC_MICROCODE_UPDATE;
+ } else if (Status == EFI_NOT_FOUND) {
+ if (GetCpuUcodeRevision () != 0) {
+ ///
+ /// Some other driver (for example, SEC) already updated CPU microcode
+ ///
+ return EFI_SUCCESS;
+ }
+ StatusCode = EFI_COMPUTING_UNIT_HOST_PROCESSOR | EFI_CU_HP_EC_NO_MICROCODE_UPDATE;
+ } else {
+ return Status;
+ }
+ ///
+ /// ReportStatusCode UEFI service can't be called by AP currently, so call by BSP only
+ ///
+ if (CpuNumber == mMPSystemData->BSP) {
+ return ReportStatusCode (
+ EFI_ERROR_MINOR | EFI_ERROR_CODE,
+ StatusCode
+ );
+ } else {
+ return Status;
+ }
+}