/** @file Implementation of MicrocodeLib. Copyright (c) 2021, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include #include #include /** Get microcode update signature of currently loaded microcode update. @return Microcode signature. **/ UINT32 EFIAPI GetProcessorMicrocodeSignature ( 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; } /** Get the processor signature and platform ID for current processor. @param MicrocodeCpuId Return the processor signature and platform ID. **/ VOID EFIAPI GetProcessorMicrocodeCpuId ( EDKII_PEI_MICROCODE_CPU_ID *MicrocodeCpuId ) { MSR_IA32_PLATFORM_ID_REGISTER PlatformIdMsr; ASSERT (MicrocodeCpuId != NULL); PlatformIdMsr.Uint64 = AsmReadMsr64 (MSR_IA32_PLATFORM_ID); MicrocodeCpuId->PlatformId = (UINT8)PlatformIdMsr.Bits.PlatformId; AsmCpuid (CPUID_VERSION_INFO, &MicrocodeCpuId->ProcessorSignature, NULL, NULL, NULL); } /** Return the total size of the microcode entry. Logic follows pseudo code in SDM as below: N = 512 If (Update.DataSize != 00000000H) N = Update.TotalSize / 4 If Microcode is NULL, then ASSERT. @param Microcode Pointer to the microcode entry. @return The microcode total size. **/ UINT32 EFIAPI GetMicrocodeLength ( IN CPU_MICROCODE_HEADER *Microcode ) { UINT32 TotalSize; ASSERT (Microcode != NULL); TotalSize = 2048; if (Microcode->DataSize != 0) { TotalSize = Microcode->TotalSize; } return TotalSize; } /** Load the microcode to the processor. If Microcode is NULL, then ASSERT. @param Microcode Pointer to the microcode entry. **/ VOID EFIAPI LoadMicrocode ( IN CPU_MICROCODE_HEADER *Microcode ) { ASSERT (Microcode != NULL); AsmWriteMsr64 (MSR_IA32_BIOS_UPDT_TRIG, (UINT64)(UINTN)(Microcode + 1)); } /** Determine if a microcode patch matchs the specific processor signature and flag. @param[in] ProcessorSignature The processor signature field value in a microcode patch. @param[in] ProcessorFlags The processor flags field value in a microcode patch. @param[in] MicrocodeCpuId A pointer to an array of EDKII_PEI_MICROCODE_CPU_ID structures. @param[in] MicrocodeCpuIdCount Number of elements in MicrocodeCpuId array. @retval TRUE The specified microcode patch matches to one of the MicrocodeCpuId. @retval FALSE The specified microcode patch doesn't match to any of the MicrocodeCpuId. **/ BOOLEAN IsProcessorMatchedMicrocode ( IN UINT32 ProcessorSignature, IN UINT32 ProcessorFlags, IN EDKII_PEI_MICROCODE_CPU_ID *MicrocodeCpuId, IN UINTN MicrocodeCpuIdCount ) { UINTN Index; if (MicrocodeCpuIdCount == 0) { return TRUE; } for (Index = 0; Index < MicrocodeCpuIdCount; Index++) { if ((ProcessorSignature == MicrocodeCpuId[Index].ProcessorSignature) && ((ProcessorFlags & (1 << MicrocodeCpuId[Index].PlatformId)) != 0)) { return TRUE; } } return FALSE; } /** Detect whether specified processor can find matching microcode patch and load it. Microcode format is as below: +----------------------------------------+-------------------------------------------------+ | CPU_MICROCODE_HEADER | | +----------------------------------------+ V | Update Data | CPU_MICROCODE_HEADER.Checksum +----------------------------------------+-------+ ^ | CPU_MICROCODE_EXTENDED_TABLE_HEADER | | | +----------------------------------------+ V | | CPU_MICROCODE_EXTENDED_TABLE[0] | CPU_MICROCODE_EXTENDED_TABLE_HEADER.Checksum | | CPU_MICROCODE_EXTENDED_TABLE[1] | ^ | | ... | | | +----------------------------------------+-------+-----------------------------------------+ There may by multiple CPU_MICROCODE_EXTENDED_TABLE in this format. The count of CPU_MICROCODE_EXTENDED_TABLE is indicated by ExtendedSignatureCount of CPU_MICROCODE_EXTENDED_TABLE_HEADER structure. If Microcode is NULL, then ASSERT. @param Microcode Pointer to a microcode entry. @param MicrocodeLength The total length of the microcode entry. @param MinimumRevision The microcode whose revision <= MinimumRevision is treated as invalid. Caller can supply value get from GetProcessorMicrocodeSignature() to check whether the microcode is newer than loaded one. Caller can supply 0 to treat any revision (except 0) microcode as valid. @param MicrocodeCpuIds Pointer to an array of processor signature and platform ID that represents a set of processors. Caller can supply zero-element array to skip the processor signature and platform ID check. @param MicrocodeCpuIdCount The number of elements in MicrocodeCpuIds. @param VerifyChecksum FALSE to skip all the checksum verifications. @retval TRUE The microcode is valid. @retval FALSE The microcode is invalid. **/ BOOLEAN EFIAPI IsValidMicrocode ( IN CPU_MICROCODE_HEADER *Microcode, IN UINTN MicrocodeLength, IN UINT32 MinimumRevision, IN EDKII_PEI_MICROCODE_CPU_ID *MicrocodeCpuIds, IN UINTN MicrocodeCpuIdCount, IN BOOLEAN VerifyChecksum ) { UINTN Index; UINT32 DataSize; UINT32 TotalSize; CPU_MICROCODE_EXTENDED_TABLE *ExtendedTable; CPU_MICROCODE_EXTENDED_TABLE_HEADER *ExtendedTableHeader; UINT32 ExtendedTableLength; UINT32 Sum32; BOOLEAN Match; ASSERT (Microcode != NULL); // // It's invalid when: // the input microcode buffer is so small that even cannot contain the header. // the input microcode buffer is so large that exceeds MAX_ADDRESS. // if ((MicrocodeLength < sizeof (CPU_MICROCODE_HEADER)) || (MicrocodeLength > (MAX_ADDRESS - (UINTN)Microcode))) { return FALSE; } // // Per SDM, HeaderVersion and LoaderRevision should both be 1. // if ((Microcode->HeaderVersion != 1) || (Microcode->LoaderRevision != 1)) { return FALSE; } // // The microcode revision should be larger than the minimum revision. // if (Microcode->UpdateRevision <= MinimumRevision) { return FALSE; } DataSize = Microcode->DataSize; if (DataSize == 0) { DataSize = 2000; } // // Per SDM, DataSize should be multiple of DWORDs. // if ((DataSize % 4) != 0) { return FALSE; } TotalSize = GetMicrocodeLength (Microcode); // // Check whether the whole microcode is within the buffer. // TotalSize should be multiple of 1024. // if (((TotalSize % SIZE_1KB) != 0) || (TotalSize > MicrocodeLength)) { return FALSE; } // // The summation of all DWORDs in microcode should be zero. // if (VerifyChecksum && (CalculateSum32 ((UINT32 *)Microcode, TotalSize) != 0)) { return FALSE; } Sum32 = Microcode->ProcessorSignature.Uint32 + Microcode->ProcessorFlags + Microcode->Checksum; // // Check the processor signature and platform ID in the primary header. // Match = IsProcessorMatchedMicrocode ( Microcode->ProcessorSignature.Uint32, Microcode->ProcessorFlags, MicrocodeCpuIds, MicrocodeCpuIdCount ); if (Match) { return TRUE; } ExtendedTableLength = TotalSize - (DataSize + sizeof (CPU_MICROCODE_HEADER)); if ((ExtendedTableLength < sizeof (CPU_MICROCODE_EXTENDED_TABLE_HEADER)) || ((ExtendedTableLength % 4) != 0)) { return FALSE; } // // Extended Table exist, check if the CPU in support list // ExtendedTableHeader = (CPU_MICROCODE_EXTENDED_TABLE_HEADER *)((UINTN)(Microcode + 1) + DataSize); if (ExtendedTableHeader->ExtendedSignatureCount > MAX_UINT32 / sizeof (CPU_MICROCODE_EXTENDED_TABLE)) { return FALSE; } if (ExtendedTableHeader->ExtendedSignatureCount * sizeof (CPU_MICROCODE_EXTENDED_TABLE) > ExtendedTableLength - sizeof (CPU_MICROCODE_EXTENDED_TABLE_HEADER)) { return FALSE; } // // Check the extended table checksum // if (VerifyChecksum && (CalculateSum32 ((UINT32 *)ExtendedTableHeader, ExtendedTableLength) != 0)) { return FALSE; } ExtendedTable = (CPU_MICROCODE_EXTENDED_TABLE *)(ExtendedTableHeader + 1); for (Index = 0; Index < ExtendedTableHeader->ExtendedSignatureCount; Index++) { if (VerifyChecksum && (ExtendedTable[Index].ProcessorSignature.Uint32 + ExtendedTable[Index].ProcessorFlag + ExtendedTable[Index].Checksum != Sum32)) { // // The extended table entry is valid when the summation of Processor Signature, Processor Flags // and Checksum equal to the coresponding summation from primary header. Because: // CalculateSum32 (Header + Update Binary) == 0 // CalculateSum32 (Header + Update Binary) // - (Header.ProcessorSignature + Header.ProcessorFlag + Header.Checksum) // + (Extended.ProcessorSignature + Extended.ProcessorFlag + Extended.Checksum) == 0 // So, // (Header.ProcessorSignature + Header.ProcessorFlag + Header.Checksum) // == (Extended.ProcessorSignature + Extended.ProcessorFlag + Extended.Checksum) // continue; } Match = IsProcessorMatchedMicrocode ( ExtendedTable[Index].ProcessorSignature.Uint32, ExtendedTable[Index].ProcessorFlag, MicrocodeCpuIds, MicrocodeCpuIdCount ); if (Match) { return TRUE; } } return FALSE; }