mirror of https://github.com/acidanthera/audk.git
931 lines
35 KiB
C
931 lines
35 KiB
C
/** @file
|
|
SetImage instance to update Microcode.
|
|
|
|
Caution: This module requires additional review when modified.
|
|
This module will have external input - capsule image.
|
|
This external input must be validated carefully to avoid security issue like
|
|
buffer overflow, integer overflow.
|
|
|
|
MicrocodeWrite() and VerifyMicrocode() will receive untrusted input and do basic validation.
|
|
|
|
Copyright (c) 2016, 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 "MicrocodeUpdate.h"
|
|
|
|
|
|
/**
|
|
Verify Microcode.
|
|
|
|
Caution: This function may receive untrusted input.
|
|
|
|
@param[in] Image The Microcode image buffer.
|
|
@param[in] ImageSize The size of Microcode image buffer in bytes.
|
|
@param[in] TryLoad Try to load Microcode or not.
|
|
@param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.
|
|
@param[out] AbortReason A pointer to a pointer to a null-terminated string providing more
|
|
details for the aborted operation. The buffer is allocated by this function
|
|
with AllocatePool(), and it is the caller's responsibility to free it with a
|
|
call to FreePool().
|
|
|
|
@retval EFI_SUCCESS The Microcode image passes verification.
|
|
@retval EFI_VOLUME_CORRUPTED The Microcode image is corrupt.
|
|
@retval EFI_INCOMPATIBLE_VERSION The Microcode image version is incorrect.
|
|
@retval EFI_UNSUPPORTED The Microcode ProcessorSignature or ProcessorFlags is incorrect.
|
|
@retval EFI_SECURITY_VIOLATION The Microcode image fails to load.
|
|
**/
|
|
EFI_STATUS
|
|
VerifyMicrocode (
|
|
IN VOID *Image,
|
|
IN UINTN ImageSize,
|
|
IN BOOLEAN TryLoad,
|
|
OUT UINT32 *LastAttemptStatus,
|
|
OUT CHAR16 **AbortReason
|
|
);
|
|
|
|
/**
|
|
Get Microcode Region.
|
|
|
|
@param[out] MicrocodePatchAddress The address of Microcode
|
|
@param[out] MicrocodePatchRegionSize The region size of Microcode
|
|
|
|
@retval TRUE The Microcode region is returned.
|
|
@retval FALSE No Microcode region.
|
|
**/
|
|
BOOLEAN
|
|
GetMicrocodeRegion (
|
|
OUT VOID **MicrocodePatchAddress,
|
|
OUT UINTN *MicrocodePatchRegionSize
|
|
)
|
|
{
|
|
*MicrocodePatchAddress = (VOID *)(UINTN)PcdGet64(PcdCpuMicrocodePatchAddress);
|
|
*MicrocodePatchRegionSize = (UINTN)PcdGet64(PcdCpuMicrocodePatchRegionSize);
|
|
|
|
if ((*MicrocodePatchAddress == NULL) || (*MicrocodePatchRegionSize == 0)) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
Get Microcode update signature of currently loaded Microcode update.
|
|
|
|
@return Microcode signature.
|
|
|
|
**/
|
|
UINT32
|
|
GetCurrentMicrocodeSignature (
|
|
VOID
|
|
)
|
|
{
|
|
UINT64 Signature;
|
|
|
|
AsmWriteMsr64(MSR_IA32_BIOS_SIGN_ID, 0);
|
|
AsmCpuid(CPUID_VERSION_INFO, NULL, NULL, NULL, NULL);
|
|
Signature = AsmReadMsr64(MSR_IA32_BIOS_SIGN_ID);
|
|
return (UINT32)RShiftU64(Signature, 32);
|
|
}
|
|
|
|
/**
|
|
Get current processor signature.
|
|
|
|
@return current processor signature.
|
|
**/
|
|
UINT32
|
|
GetCurrentProcessorSignature (
|
|
VOID
|
|
)
|
|
{
|
|
UINT32 RegEax;
|
|
AsmCpuid(CPUID_VERSION_INFO, &RegEax, NULL, NULL, NULL);
|
|
return RegEax;
|
|
}
|
|
|
|
/**
|
|
Get current platform ID.
|
|
|
|
@return current platform ID.
|
|
**/
|
|
UINT8
|
|
GetCurrentPlatformId (
|
|
VOID
|
|
)
|
|
{
|
|
UINT8 PlatformId;
|
|
|
|
PlatformId = (UINT8)AsmMsrBitFieldRead64(MSR_IA32_PLATFORM_ID, 50, 52);
|
|
return PlatformId;
|
|
}
|
|
|
|
/**
|
|
Load new Microcode.
|
|
|
|
@param[in] Address The address of new Microcode.
|
|
|
|
@return Loaded Microcode signature.
|
|
|
|
**/
|
|
UINT32
|
|
LoadMicrocode (
|
|
IN UINT64 Address
|
|
)
|
|
{
|
|
AsmWriteMsr64(MSR_IA32_BIOS_UPDT_TRIG, Address);
|
|
return GetCurrentMicrocodeSignature();
|
|
}
|
|
|
|
/**
|
|
If the Microcode is used by current processor.
|
|
|
|
@param[in] MicrocodeEntryPoint The Microcode buffer
|
|
|
|
@retval TRUE The Microcode is used by current processor.
|
|
@retval FALSE The Microcode is NOT used by current processor.
|
|
**/
|
|
BOOLEAN
|
|
IsMicrocodeInUse (
|
|
IN CPU_MICROCODE_HEADER *MicrocodeEntryPoint
|
|
)
|
|
{
|
|
UINT32 AttemptStatus;
|
|
UINTN TotalSize;
|
|
EFI_STATUS Status;
|
|
|
|
if (MicrocodeEntryPoint->HeaderVersion == 0x1 && MicrocodeEntryPoint->LoaderRevision == 0x1) {
|
|
//
|
|
// It is the microcode header. It is not the padding data between microcode patches
|
|
// becasue the padding data should not include 0x00000001 and it should be the repeated
|
|
// byte format (like 0xXYXYXYXY....).
|
|
//
|
|
if (MicrocodeEntryPoint->DataSize == 0) {
|
|
TotalSize = 2048;
|
|
} else {
|
|
TotalSize = MicrocodeEntryPoint->TotalSize;
|
|
}
|
|
Status = VerifyMicrocode(MicrocodeEntryPoint, TotalSize, FALSE, &AttemptStatus, NULL);
|
|
if (!EFI_ERROR(Status)) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
Get current Microcode information.
|
|
|
|
NOTE: The DescriptorCount/ImageDescriptor/MicrocodeInfo in MicrocodeFmpPrivate
|
|
are not avaiable in this function.
|
|
|
|
@param[in] MicrocodeFmpPrivate The Microcode driver private data
|
|
@param[in] DescriptorCount The count of Microcode ImageDescriptor allocated.
|
|
@param[out] ImageDescriptor Microcode ImageDescriptor
|
|
@param[out] MicrocodeInfo Microcode information
|
|
|
|
@return Microcode count
|
|
**/
|
|
UINTN
|
|
GetMicrocodeInfo (
|
|
IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,
|
|
IN UINTN DescriptorCount, OPTIONAL
|
|
OUT EFI_FIRMWARE_IMAGE_DESCRIPTOR *ImageDescriptor, OPTIONAL
|
|
OUT MICROCODE_INFO *MicrocodeInfo OPTIONAL
|
|
)
|
|
{
|
|
VOID *MicrocodePatchAddress;
|
|
UINTN MicrocodePatchRegionSize;
|
|
CPU_MICROCODE_HEADER *MicrocodeEntryPoint;
|
|
UINTN MicrocodeEnd;
|
|
UINTN TotalSize;
|
|
UINTN Count;
|
|
UINT64 ImageAttributes;
|
|
BOOLEAN IsInUse;
|
|
|
|
MicrocodePatchAddress = MicrocodeFmpPrivate->MicrocodePatchAddress;
|
|
MicrocodePatchRegionSize = MicrocodeFmpPrivate->MicrocodePatchRegionSize;
|
|
|
|
DEBUG((DEBUG_INFO, "Microcode Region - 0x%x - 0x%x\n", MicrocodePatchAddress, MicrocodePatchRegionSize));
|
|
|
|
Count = 0;
|
|
|
|
MicrocodeEnd = (UINTN)MicrocodePatchAddress + MicrocodePatchRegionSize;
|
|
MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (UINTN) MicrocodePatchAddress;
|
|
do {
|
|
if (MicrocodeEntryPoint->HeaderVersion == 0x1 && MicrocodeEntryPoint->LoaderRevision == 0x1) {
|
|
//
|
|
// It is the microcode header. It is not the padding data between microcode patches
|
|
// becasue the padding data should not include 0x00000001 and it should be the repeated
|
|
// byte format (like 0xXYXYXYXY....).
|
|
//
|
|
if (MicrocodeEntryPoint->DataSize == 0) {
|
|
TotalSize = 2048;
|
|
} else {
|
|
TotalSize = MicrocodeEntryPoint->TotalSize;
|
|
}
|
|
|
|
IsInUse = IsMicrocodeInUse (MicrocodeEntryPoint);
|
|
|
|
if (ImageDescriptor != NULL && DescriptorCount > Count) {
|
|
ImageDescriptor[Count].ImageIndex = (UINT8)(Count + 1);
|
|
CopyGuid (&ImageDescriptor[Count].ImageTypeId, &gMicrocodeFmpImageTypeIdGuid);
|
|
ImageDescriptor[Count].ImageId = LShiftU64(MicrocodeEntryPoint->ProcessorFlags, 32) + MicrocodeEntryPoint->ProcessorSignature.Uint32;
|
|
ImageDescriptor[Count].ImageIdName = NULL;
|
|
ImageDescriptor[Count].Version = MicrocodeEntryPoint->UpdateRevision;
|
|
ImageDescriptor[Count].VersionName = NULL;
|
|
ImageDescriptor[Count].Size = TotalSize;
|
|
ImageAttributes = IMAGE_ATTRIBUTE_IMAGE_UPDATABLE | IMAGE_ATTRIBUTE_RESET_REQUIRED;
|
|
if (IsInUse) {
|
|
ImageAttributes |= IMAGE_ATTRIBUTE_IN_USE;
|
|
}
|
|
ImageDescriptor[Count].AttributesSupported = ImageAttributes | IMAGE_ATTRIBUTE_IN_USE;
|
|
ImageDescriptor[Count].AttributesSetting = ImageAttributes;
|
|
ImageDescriptor[Count].Compatibilities = 0;
|
|
ImageDescriptor[Count].LowestSupportedImageVersion = MicrocodeEntryPoint->UpdateRevision; // do not support rollback
|
|
ImageDescriptor[Count].LastAttemptVersion = 0;
|
|
ImageDescriptor[Count].LastAttemptStatus = 0;
|
|
ImageDescriptor[Count].HardwareInstance = 0;
|
|
}
|
|
if (MicrocodeInfo != NULL && DescriptorCount > Count) {
|
|
MicrocodeInfo[Count].MicrocodeEntryPoint = MicrocodeEntryPoint;
|
|
MicrocodeInfo[Count].TotalSize = TotalSize;
|
|
MicrocodeInfo[Count].InUse = IsInUse;
|
|
}
|
|
} 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;
|
|
}
|
|
|
|
Count++;
|
|
ASSERT(Count < 0xFF);
|
|
|
|
//
|
|
// Get the next patch.
|
|
//
|
|
MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (((UINTN) MicrocodeEntryPoint) + TotalSize);
|
|
} while (((UINTN) MicrocodeEntryPoint < MicrocodeEnd));
|
|
|
|
return Count;
|
|
}
|
|
|
|
/**
|
|
Verify Microcode.
|
|
|
|
Caution: This function may receive untrusted input.
|
|
|
|
@param[in] Image The Microcode image buffer.
|
|
@param[in] ImageSize The size of Microcode image buffer in bytes.
|
|
@param[in] TryLoad Try to load Microcode or not.
|
|
@param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.
|
|
@param[out] AbortReason A pointer to a pointer to a null-terminated string providing more
|
|
details for the aborted operation. The buffer is allocated by this function
|
|
with AllocatePool(), and it is the caller's responsibility to free it with a
|
|
call to FreePool().
|
|
|
|
@retval EFI_SUCCESS The Microcode image passes verification.
|
|
@retval EFI_VOLUME_CORRUPTED The Microcode image is corrupt.
|
|
@retval EFI_INCOMPATIBLE_VERSION The Microcode image version is incorrect.
|
|
@retval EFI_UNSUPPORTED The Microcode ProcessorSignature or ProcessorFlags is incorrect.
|
|
@retval EFI_SECURITY_VIOLATION The Microcode image fails to load.
|
|
**/
|
|
EFI_STATUS
|
|
VerifyMicrocode (
|
|
IN VOID *Image,
|
|
IN UINTN ImageSize,
|
|
IN BOOLEAN TryLoad,
|
|
OUT UINT32 *LastAttemptStatus,
|
|
OUT CHAR16 **AbortReason
|
|
)
|
|
{
|
|
UINTN Index;
|
|
CPU_MICROCODE_HEADER *MicrocodeEntryPoint;
|
|
UINTN TotalSize;
|
|
UINTN DataSize;
|
|
UINT32 CurrentRevision;
|
|
UINT32 CurrentProcessorSignature;
|
|
UINT8 CurrentPlatformId;
|
|
UINT32 CheckSum32;
|
|
UINTN ExtendedTableLength;
|
|
UINT32 ExtendedTableCount;
|
|
CPU_MICROCODE_EXTENDED_TABLE *ExtendedTable;
|
|
CPU_MICROCODE_EXTENDED_TABLE_HEADER *ExtendedTableHeader;
|
|
BOOLEAN CorrectMicrocode;
|
|
|
|
//
|
|
// Check HeaderVersion
|
|
//
|
|
MicrocodeEntryPoint = Image;
|
|
if (MicrocodeEntryPoint->HeaderVersion != 0x1) {
|
|
DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on HeaderVersion\n"));
|
|
*LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;
|
|
if (AbortReason != NULL) {
|
|
*AbortReason = AllocateCopyPool(sizeof(L"InvalidHeaderVersion"), L"InvalidHeaderVersion");
|
|
}
|
|
return EFI_INCOMPATIBLE_VERSION;
|
|
}
|
|
//
|
|
// Check LoaderRevision
|
|
//
|
|
if (MicrocodeEntryPoint->LoaderRevision != 0x1) {
|
|
DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on LoaderRevision\n"));
|
|
*LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;
|
|
if (AbortReason != NULL) {
|
|
*AbortReason = AllocateCopyPool(sizeof(L"InvalidLoaderVersion"), L"InvalidLoaderVersion");
|
|
}
|
|
return EFI_INCOMPATIBLE_VERSION;
|
|
}
|
|
//
|
|
// Check Size
|
|
//
|
|
if (MicrocodeEntryPoint->DataSize == 0) {
|
|
TotalSize = 2048;
|
|
} else {
|
|
TotalSize = MicrocodeEntryPoint->TotalSize;
|
|
}
|
|
if (TotalSize <= sizeof(CPU_MICROCODE_HEADER)) {
|
|
DEBUG((DEBUG_ERROR, "VerifyMicrocode - TotalSize too small\n"));
|
|
*LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;
|
|
if (AbortReason != NULL) {
|
|
*AbortReason = AllocateCopyPool(sizeof(L"InvalidTotalSize"), L"InvalidTotalSize");
|
|
}
|
|
return EFI_VOLUME_CORRUPTED;
|
|
}
|
|
if (TotalSize != ImageSize) {
|
|
DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on TotalSize\n"));
|
|
*LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;
|
|
if (AbortReason != NULL) {
|
|
*AbortReason = AllocateCopyPool(sizeof(L"InvalidTotalSize"), L"InvalidTotalSize");
|
|
}
|
|
return EFI_VOLUME_CORRUPTED;
|
|
}
|
|
//
|
|
// Check CheckSum32
|
|
//
|
|
if (MicrocodeEntryPoint->DataSize == 0) {
|
|
DataSize = 2048 - sizeof(CPU_MICROCODE_HEADER);
|
|
} else {
|
|
DataSize = MicrocodeEntryPoint->DataSize;
|
|
}
|
|
if (DataSize > TotalSize - sizeof(CPU_MICROCODE_HEADER)) {
|
|
DEBUG((DEBUG_ERROR, "VerifyMicrocode - DataSize too big\n"));
|
|
*LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;
|
|
if (AbortReason != NULL) {
|
|
*AbortReason = AllocateCopyPool(sizeof(L"InvalidDataSize"), L"InvalidDataSize");
|
|
}
|
|
return EFI_VOLUME_CORRUPTED;
|
|
}
|
|
if ((DataSize & 0x3) != 0) {
|
|
DEBUG((DEBUG_ERROR, "VerifyMicrocode - DataSize not aligned\n"));
|
|
*LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;
|
|
if (AbortReason != NULL) {
|
|
*AbortReason = AllocateCopyPool(sizeof(L"InvalidDataSize"), L"InvalidDataSize");
|
|
}
|
|
return EFI_VOLUME_CORRUPTED;
|
|
}
|
|
CheckSum32 = CalculateSum32((UINT32 *)MicrocodeEntryPoint, DataSize + sizeof(CPU_MICROCODE_HEADER));
|
|
if (CheckSum32 != 0) {
|
|
DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on CheckSum32\n"));
|
|
*LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;
|
|
if (AbortReason != NULL) {
|
|
*AbortReason = AllocateCopyPool(sizeof(L"InvalidChecksum"), L"InvalidChecksum");
|
|
}
|
|
return EFI_VOLUME_CORRUPTED;
|
|
}
|
|
|
|
//
|
|
// Check ProcessorSignature/ProcessorFlags
|
|
//
|
|
CorrectMicrocode = FALSE;
|
|
CurrentProcessorSignature = GetCurrentProcessorSignature();
|
|
CurrentPlatformId = GetCurrentPlatformId();
|
|
if ((MicrocodeEntryPoint->ProcessorSignature.Uint32 != CurrentProcessorSignature) ||
|
|
((MicrocodeEntryPoint->ProcessorFlags & (1 << CurrentPlatformId)) == 0)) {
|
|
ExtendedTableLength = TotalSize - (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) + DataSize + sizeof(CPU_MICROCODE_HEADER));
|
|
//
|
|
// Calculate Extended Checksum
|
|
//
|
|
if ((ExtendedTableLength > sizeof(CPU_MICROCODE_EXTENDED_TABLE_HEADER)) && ((ExtendedTableLength & 0x3) != 0)) {
|
|
CheckSum32 = CalculateSum32((UINT32 *)ExtendedTableHeader, ExtendedTableLength);
|
|
if (CheckSum32 == 0) {
|
|
//
|
|
// Checksum correct
|
|
//
|
|
ExtendedTableCount = ExtendedTableHeader->ExtendedSignatureCount;
|
|
if (ExtendedTableCount <= (ExtendedTableLength - sizeof(CPU_MICROCODE_EXTENDED_TABLE_HEADER)) / sizeof(CPU_MICROCODE_EXTENDED_TABLE)) {
|
|
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 == CurrentProcessorSignature) &&
|
|
(ExtendedTable->ProcessorFlag & (1 << CurrentPlatformId))) {
|
|
//
|
|
// Find one
|
|
//
|
|
CorrectMicrocode = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
ExtendedTable++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!CorrectMicrocode) {
|
|
if (TryLoad) {
|
|
DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on CurrentProcessorSignature/ProcessorFlags\n"));
|
|
}
|
|
*LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INCORRECT_VERSION;
|
|
if (AbortReason != NULL) {
|
|
if (MicrocodeEntryPoint->ProcessorSignature.Uint32 != CurrentProcessorSignature) {
|
|
*AbortReason = AllocateCopyPool(sizeof(L"UnsupportedProcessSignature"), L"UnsupportedProcessSignature");
|
|
} else {
|
|
*AbortReason = AllocateCopyPool(sizeof(L"UnsupportedProcessorFlags"), L"UnsupportedProcessorFlags");
|
|
}
|
|
}
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check UpdateRevision
|
|
//
|
|
CurrentRevision = GetCurrentMicrocodeSignature();
|
|
if (MicrocodeEntryPoint->UpdateRevision < CurrentRevision) {
|
|
if (TryLoad) {
|
|
DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on UpdateRevision\n"));
|
|
}
|
|
*LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INCORRECT_VERSION;
|
|
if (AbortReason != NULL) {
|
|
*AbortReason = AllocateCopyPool(sizeof(L"IncorrectRevision"), L"IncorrectRevision");
|
|
}
|
|
return EFI_INCOMPATIBLE_VERSION;
|
|
}
|
|
|
|
//
|
|
// try load MCU
|
|
//
|
|
if (TryLoad) {
|
|
CurrentRevision = LoadMicrocode((UINTN)MicrocodeEntryPoint + sizeof(CPU_MICROCODE_HEADER));
|
|
if (MicrocodeEntryPoint->UpdateRevision != CurrentRevision) {
|
|
DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on LoadMicrocode\n"));
|
|
*LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_AUTH_ERROR;
|
|
if (AbortReason != NULL) {
|
|
*AbortReason = AllocateCopyPool(sizeof(L"InvalidData"), L"InvalidData");
|
|
}
|
|
return EFI_SECURITY_VIOLATION;
|
|
}
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Get current Microcode in used.
|
|
|
|
@param[in] MicrocodeFmpPrivate The Microcode driver private data
|
|
|
|
@return current Microcode in used.
|
|
**/
|
|
VOID *
|
|
GetCurrentMicrocodeInUse (
|
|
IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate
|
|
)
|
|
{
|
|
UINTN Index;
|
|
|
|
for (Index = 0; Index < MicrocodeFmpPrivate->DescriptorCount; Index++) {
|
|
if (!MicrocodeFmpPrivate->MicrocodeInfo[Index].InUse) {
|
|
continue;
|
|
}
|
|
if (IsMicrocodeInUse (MicrocodeFmpPrivate->MicrocodeInfo[Index].MicrocodeEntryPoint)) {
|
|
return MicrocodeFmpPrivate->MicrocodeInfo[Index].MicrocodeEntryPoint;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
Get next Microcode entrypoint.
|
|
|
|
@param[in] MicrocodeFmpPrivate The Microcode driver private data
|
|
@param[in] MicrocodeEntryPoint Current Microcode entrypoint
|
|
|
|
@return next Microcode entrypoint.
|
|
**/
|
|
CPU_MICROCODE_HEADER *
|
|
GetNextMicrocode (
|
|
IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,
|
|
IN CPU_MICROCODE_HEADER *MicrocodeEntryPoint
|
|
)
|
|
{
|
|
UINTN Index;
|
|
|
|
for (Index = 0; Index < MicrocodeFmpPrivate->DescriptorCount; Index++) {
|
|
if (MicrocodeEntryPoint == MicrocodeFmpPrivate->MicrocodeInfo[Index].MicrocodeEntryPoint) {
|
|
if (Index == (UINTN)MicrocodeFmpPrivate->DescriptorCount - 1) {
|
|
// it is last one
|
|
return NULL;
|
|
} else {
|
|
// return next one
|
|
return MicrocodeFmpPrivate->MicrocodeInfo[Index + 1].MicrocodeEntryPoint;
|
|
}
|
|
}
|
|
}
|
|
|
|
ASSERT(FALSE);
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
Get current Microcode used region size.
|
|
|
|
@param[in] MicrocodeFmpPrivate The Microcode driver private data
|
|
|
|
@return current Microcode used region size.
|
|
**/
|
|
UINTN
|
|
GetCurrentMicrocodeUsedRegionSize (
|
|
IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate
|
|
)
|
|
{
|
|
if (MicrocodeFmpPrivate->DescriptorCount == 0) {
|
|
return 0;
|
|
}
|
|
|
|
return (UINTN)MicrocodeFmpPrivate->MicrocodeInfo[MicrocodeFmpPrivate->DescriptorCount - 1].MicrocodeEntryPoint
|
|
+ (UINTN)MicrocodeFmpPrivate->MicrocodeInfo[MicrocodeFmpPrivate->DescriptorCount - 1].TotalSize
|
|
- (UINTN)MicrocodeFmpPrivate->MicrocodePatchAddress;
|
|
}
|
|
|
|
/**
|
|
Update Microcode.
|
|
|
|
@param[in] Address The flash address of Microcode.
|
|
@param[in] Image The Microcode image buffer.
|
|
@param[in] ImageSize The size of Microcode image buffer in bytes.
|
|
@param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.
|
|
|
|
@retval EFI_SUCCESS The Microcode image is updated.
|
|
@retval EFI_WRITE_PROTECTED The flash device is read only.
|
|
**/
|
|
EFI_STATUS
|
|
UpdateMicrocode (
|
|
IN UINT64 Address,
|
|
IN VOID *Image,
|
|
IN UINTN ImageSize,
|
|
OUT UINT32 *LastAttemptStatus
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
DEBUG((DEBUG_INFO, "PlatformUpdate:"));
|
|
DEBUG((DEBUG_INFO, " Address - 0x%lx,", Address));
|
|
DEBUG((DEBUG_INFO, " Legnth - 0x%x\n", ImageSize));
|
|
|
|
Status = MicrocodeFlashWrite (
|
|
Address,
|
|
Image,
|
|
ImageSize
|
|
);
|
|
if (!EFI_ERROR(Status)) {
|
|
*LastAttemptStatus = LAST_ATTEMPT_STATUS_SUCCESS;
|
|
} else {
|
|
*LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL;
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Update Microcode flash region.
|
|
|
|
@param[in] MicrocodeFmpPrivate The Microcode driver private data
|
|
@param[in] CurrentMicrocodeEntryPoint Current Microcode entrypoint
|
|
@param[in] Image The Microcode image buffer.
|
|
@param[in] ImageSize The size of Microcode image buffer in bytes.
|
|
@param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.
|
|
|
|
@retval EFI_SUCCESS The Microcode image is written.
|
|
@retval EFI_WRITE_PROTECTED The flash device is read only.
|
|
**/
|
|
EFI_STATUS
|
|
UpdateMicrocodeFlashRegion (
|
|
IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,
|
|
IN CPU_MICROCODE_HEADER *CurrentMicrocodeEntryPoint,
|
|
IN VOID *Image,
|
|
IN UINTN ImageSize,
|
|
OUT UINT32 *LastAttemptStatus
|
|
)
|
|
{
|
|
VOID *MicrocodePatchAddress;
|
|
UINTN MicrocodePatchRegionSize;
|
|
UINTN CurrentTotalSize;
|
|
UINTN UsedRegionSize;
|
|
EFI_STATUS Status;
|
|
VOID *MicrocodePatchScratchBuffer;
|
|
UINT8 *ScratchBufferPtr;
|
|
UINTN ScratchBufferSize;
|
|
UINTN RestSize;
|
|
UINTN AvailableSize;
|
|
VOID *NextMicrocodeEntryPoint;
|
|
MICROCODE_INFO *MicrocodeInfo;
|
|
UINTN MicrocodeCount;
|
|
UINTN Index;
|
|
|
|
DEBUG((DEBUG_INFO, "UpdateMicrocodeFlashRegion: Image - 0x%x, size - 0x%x\n", Image, ImageSize));
|
|
|
|
MicrocodePatchAddress = MicrocodeFmpPrivate->MicrocodePatchAddress;
|
|
MicrocodePatchRegionSize = MicrocodeFmpPrivate->MicrocodePatchRegionSize;
|
|
|
|
MicrocodePatchScratchBuffer = AllocateZeroPool (MicrocodePatchRegionSize);
|
|
if (MicrocodePatchScratchBuffer == NULL) {
|
|
DEBUG((DEBUG_ERROR, "Fail to allocate Microcode Scratch buffer\n"));
|
|
*LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES;
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
ScratchBufferPtr = MicrocodePatchScratchBuffer;
|
|
ScratchBufferSize = 0;
|
|
|
|
//
|
|
// Current data collection
|
|
//
|
|
CurrentTotalSize = 0;
|
|
AvailableSize = 0;
|
|
NextMicrocodeEntryPoint = NULL;
|
|
if (CurrentMicrocodeEntryPoint != NULL) {
|
|
if (CurrentMicrocodeEntryPoint->DataSize == 0) {
|
|
CurrentTotalSize = 2048;
|
|
} else {
|
|
CurrentTotalSize = CurrentMicrocodeEntryPoint->TotalSize;
|
|
}
|
|
DEBUG((DEBUG_INFO, " CurrentTotalSize - 0x%x\n", CurrentTotalSize));
|
|
NextMicrocodeEntryPoint = GetNextMicrocode(MicrocodeFmpPrivate, CurrentMicrocodeEntryPoint);
|
|
DEBUG((DEBUG_INFO, " NextMicrocodeEntryPoint - 0x%x\n", NextMicrocodeEntryPoint));
|
|
if (NextMicrocodeEntryPoint != NULL) {
|
|
ASSERT ((UINTN)NextMicrocodeEntryPoint >= ((UINTN)CurrentMicrocodeEntryPoint + CurrentTotalSize));
|
|
AvailableSize = (UINTN)NextMicrocodeEntryPoint - (UINTN)CurrentMicrocodeEntryPoint;
|
|
} else {
|
|
AvailableSize = (UINTN)MicrocodePatchAddress + MicrocodePatchRegionSize - (UINTN)CurrentMicrocodeEntryPoint;
|
|
}
|
|
DEBUG((DEBUG_INFO, " AvailableSize - 0x%x\n", AvailableSize));
|
|
}
|
|
ASSERT (AvailableSize >= CurrentTotalSize);
|
|
UsedRegionSize = GetCurrentMicrocodeUsedRegionSize(MicrocodeFmpPrivate);
|
|
DEBUG((DEBUG_INFO, " UsedRegionSize - 0x%x\n", UsedRegionSize));
|
|
ASSERT (UsedRegionSize >= CurrentTotalSize);
|
|
if (CurrentMicrocodeEntryPoint != NULL) {
|
|
ASSERT ((UINTN)MicrocodePatchAddress + UsedRegionSize >= ((UINTN)CurrentMicrocodeEntryPoint + CurrentTotalSize));
|
|
}
|
|
//
|
|
// Total Size means the Microcode data size.
|
|
// Available Size means the Microcode data size plus the pad till next (1) Microcode or (2) the end.
|
|
//
|
|
// (1)
|
|
// +------+-----------+-----+------+===================+
|
|
// | MCU1 | Microcode | PAD | MCU2 | Empty |
|
|
// +------+-----------+-----+------+===================+
|
|
// | TotalSize |
|
|
// |<-AvailableSize->|
|
|
// |<- UsedRegionSize ->|
|
|
//
|
|
// (2)
|
|
// +------+-----------+===================+
|
|
// | MCU | Microcode | Empty |
|
|
// +------+-----------+===================+
|
|
// | TotalSize |
|
|
// |<- AvailableSize ->|
|
|
// |<-UsedRegionSize->|
|
|
//
|
|
|
|
//
|
|
// Update based on policy
|
|
//
|
|
|
|
//
|
|
// 1. If there is enough space to update old one in situ, replace old microcode in situ.
|
|
//
|
|
if (AvailableSize >= ImageSize) {
|
|
DEBUG((DEBUG_INFO, "Replace old microcode in situ\n"));
|
|
//
|
|
// +------+------------+------+===================+
|
|
// |Other1| Old Image |Other2| Empty |
|
|
// +------+------------+------+===================+
|
|
//
|
|
// +------+---------+--+------+===================+
|
|
// |Other1|New Image|FF|Other2| Empty |
|
|
// +------+---------+--+------+===================+
|
|
//
|
|
// 1.1. Copy new image
|
|
CopyMem (ScratchBufferPtr, Image, ImageSize);
|
|
ScratchBufferSize += ImageSize;
|
|
ScratchBufferPtr = (UINT8 *)ScratchBufferPtr + ScratchBufferSize;
|
|
// 1.2. Pad 0xFF
|
|
RestSize = AvailableSize - ImageSize;
|
|
if (RestSize > 0) {
|
|
SetMem (ScratchBufferPtr, RestSize, 0xFF);
|
|
ScratchBufferSize += RestSize;
|
|
ScratchBufferPtr = (UINT8 *)ScratchBufferPtr + ScratchBufferSize;
|
|
}
|
|
Status = UpdateMicrocode((UINTN)CurrentMicrocodeEntryPoint, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus);
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// 2. If there is enough space to remove old one and add new one, reorg and replace old microcode.
|
|
//
|
|
if (MicrocodePatchRegionSize - (UsedRegionSize - CurrentTotalSize) >= ImageSize) {
|
|
if (CurrentMicrocodeEntryPoint == NULL) {
|
|
DEBUG((DEBUG_INFO, "Append new microcode\n"));
|
|
//
|
|
// +------+------------+------+===================+
|
|
// |Other1| Other |Other2| Empty |
|
|
// +------+------------+------+===================+
|
|
//
|
|
// +------+------------+------+-----------+=======+
|
|
// |Other1| Other |Other2| New Image | Empty |
|
|
// +------+------------+------+-----------+=======+
|
|
//
|
|
Status = UpdateMicrocode((UINTN)MicrocodePatchAddress + UsedRegionSize, Image, ImageSize, LastAttemptStatus);
|
|
} else {
|
|
DEBUG((DEBUG_INFO, "Reorg and replace old microcode\n"));
|
|
//
|
|
// +------+------------+------+===================+
|
|
// |Other1| Old Image |Other2| Empty |
|
|
// +------+------------+------+===================+
|
|
//
|
|
// +------+---------------+------+================+
|
|
// |Other1| New Image |Other2| Empty |
|
|
// +------+---------------+------+================+
|
|
//
|
|
// 2.1. Copy new image
|
|
CopyMem (MicrocodePatchScratchBuffer, Image, ImageSize);
|
|
ScratchBufferSize += ImageSize;
|
|
ScratchBufferPtr = (UINT8 *)ScratchBufferPtr + ScratchBufferSize;
|
|
// 2.2. Copy rest images after the old image.
|
|
if (NextMicrocodeEntryPoint != 0) {
|
|
RestSize = (UINTN)MicrocodePatchAddress + UsedRegionSize - ((UINTN)NextMicrocodeEntryPoint);
|
|
CopyMem (ScratchBufferPtr, (UINT8 *)CurrentMicrocodeEntryPoint + CurrentTotalSize, RestSize);
|
|
ScratchBufferSize += RestSize;
|
|
ScratchBufferPtr = (UINT8 *)ScratchBufferPtr + ScratchBufferSize;
|
|
}
|
|
Status = UpdateMicrocode((UINTN)CurrentMicrocodeEntryPoint, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus);
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// 3. The new image can be put in MCU region, but not all others can be put.
|
|
// So all the unused MCU is removed.
|
|
//
|
|
if (MicrocodePatchRegionSize >= ImageSize) {
|
|
//
|
|
// +------+------------+------+===================+
|
|
// |Other1| Old Image |Other2| Empty |
|
|
// +------+------------+------+===================+
|
|
//
|
|
// +-------------------------------------+--------+
|
|
// | New Image | Other |
|
|
// +-------------------------------------+--------+
|
|
//
|
|
DEBUG((DEBUG_INFO, "Add new microcode from beginning\n"));
|
|
|
|
MicrocodeCount = MicrocodeFmpPrivate->DescriptorCount;
|
|
MicrocodeInfo = MicrocodeFmpPrivate->MicrocodeInfo;
|
|
|
|
// 3.1. Copy new image
|
|
CopyMem (MicrocodePatchScratchBuffer, Image, ImageSize);
|
|
ScratchBufferSize += ImageSize;
|
|
ScratchBufferPtr = (UINT8 *)ScratchBufferPtr + ScratchBufferSize;
|
|
// 3.2. Copy some others to rest buffer
|
|
for (Index = 0; Index < MicrocodeCount; Index++) {
|
|
if (!MicrocodeInfo[Index].InUse) {
|
|
continue;
|
|
}
|
|
if (MicrocodeInfo[Index].MicrocodeEntryPoint == CurrentMicrocodeEntryPoint) {
|
|
continue;
|
|
}
|
|
if (MicrocodeInfo[Index].TotalSize <= MicrocodePatchRegionSize - ScratchBufferSize) {
|
|
CopyMem (ScratchBufferPtr, MicrocodeInfo[Index].MicrocodeEntryPoint, MicrocodeInfo[Index].TotalSize);
|
|
ScratchBufferSize += MicrocodeInfo[Index].TotalSize;
|
|
ScratchBufferPtr = (UINT8 *)ScratchBufferPtr + ScratchBufferSize;
|
|
}
|
|
}
|
|
// 3.3. Pad 0xFF
|
|
RestSize = MicrocodePatchRegionSize - ScratchBufferSize;
|
|
if (RestSize > 0) {
|
|
SetMem (ScratchBufferPtr, RestSize, 0xFF);
|
|
ScratchBufferSize += RestSize;
|
|
ScratchBufferPtr = (UINT8 *)ScratchBufferPtr + ScratchBufferSize;
|
|
}
|
|
Status = UpdateMicrocode((UINTN)MicrocodePatchAddress, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus);
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// 4. The new image size is bigger than the whole MCU region.
|
|
//
|
|
DEBUG((DEBUG_ERROR, "Microcode too big\n"));
|
|
*LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES;
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Write Microcode.
|
|
|
|
Caution: This function may receive untrusted input.
|
|
|
|
@param[in] MicrocodeFmpPrivate The Microcode driver private data
|
|
@param[in] Image The Microcode image buffer.
|
|
@param[in] ImageSize The size of Microcode image buffer in bytes.
|
|
@param[out] LastAttemptVersion The last attempt version, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.
|
|
@param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.
|
|
@param[out] AbortReason A pointer to a pointer to a null-terminated string providing more
|
|
details for the aborted operation. The buffer is allocated by this function
|
|
with AllocatePool(), and it is the caller's responsibility to free it with a
|
|
call to FreePool().
|
|
|
|
@retval EFI_SUCCESS The Microcode image is written.
|
|
@retval EFI_VOLUME_CORRUPTED The Microcode image is corrupt.
|
|
@retval EFI_INCOMPATIBLE_VERSION The Microcode image version is incorrect.
|
|
@retval EFI_SECURITY_VIOLATION The Microcode image fails to load.
|
|
@retval EFI_WRITE_PROTECTED The flash device is read only.
|
|
**/
|
|
EFI_STATUS
|
|
MicrocodeWrite (
|
|
IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,
|
|
IN VOID *Image,
|
|
IN UINTN ImageSize,
|
|
OUT UINT32 *LastAttemptVersion,
|
|
OUT UINT32 *LastAttemptStatus,
|
|
OUT CHAR16 **AbortReason
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
VOID *AlignedImage;
|
|
CPU_MICROCODE_HEADER *CurrentMicrocodeEntryPoint;
|
|
|
|
//
|
|
// We must get Current MicrocodeEntrypoint *before* VerifyMicrocode,
|
|
// because the MicrocodeSignature might be updated in VerifyMicrocode.
|
|
//
|
|
CurrentMicrocodeEntryPoint = GetCurrentMicrocodeInUse(MicrocodeFmpPrivate);
|
|
DEBUG((DEBUG_INFO, " CurrentMicrocodeEntryPoint - 0x%x\n", CurrentMicrocodeEntryPoint));
|
|
|
|
//
|
|
// MCU must be 16 bytes aligned
|
|
//
|
|
AlignedImage = AllocateCopyPool(ImageSize, Image);
|
|
if (AlignedImage == NULL) {
|
|
DEBUG((DEBUG_ERROR, "Fail to allocate aligned image\n"));
|
|
*LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES;
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
*LastAttemptVersion = ((CPU_MICROCODE_HEADER *)Image)->UpdateRevision;
|
|
Status = VerifyMicrocode(AlignedImage, ImageSize, TRUE, LastAttemptStatus, AbortReason);
|
|
if (EFI_ERROR(Status)) {
|
|
DEBUG((DEBUG_ERROR, "Fail to verify Microcode Region\n"));
|
|
FreePool(AlignedImage);
|
|
return Status;
|
|
}
|
|
DEBUG((DEBUG_INFO, "Pass VerifyMicrocode\n"));
|
|
|
|
Status = UpdateMicrocodeFlashRegion(
|
|
MicrocodeFmpPrivate,
|
|
CurrentMicrocodeEntryPoint,
|
|
AlignedImage,
|
|
ImageSize,
|
|
LastAttemptStatus
|
|
);
|
|
|
|
FreePool(AlignedImage);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|