3707 lines
133 KiB
C
3707 lines
133 KiB
C
/** @file
|
|
Firmware Management Protocol implementation for Capsule Processor
|
|
|
|
;******************************************************************************
|
|
;* Copyright (c) 2020 - 2021, Insyde Software Corp. All Rights Reserved.
|
|
;*
|
|
;* You may not reproduce, distribute, publish, display, perform, modify, adapt,
|
|
;* transmit, broadcast, present, recite, release, license or otherwise exploit
|
|
;* any part of this publication in any form, by any means, without the prior
|
|
;* written permission of Insyde Software Corporation.
|
|
;*
|
|
;******************************************************************************
|
|
*/
|
|
|
|
/** @file
|
|
SetImage instance to update Microcode.
|
|
|
|
Caution: This module requires additional review when modified.
|
|
This module will have external input - uCode 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 - 2020, 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"
|
|
///
|
|
/// Last Attempt Status Values
|
|
///
|
|
#define LAST_ATTEMPT_STATUS_SUCCESS 0x00000000
|
|
#define LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL 0x00000001
|
|
#define LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES 0x00000002
|
|
#define LAST_ATTEMPT_STATUS_ERROR_INCORRECT_VERSION 0x00000003
|
|
#define LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT 0x00000004
|
|
#define LAST_ATTEMPT_STATUS_ERROR_AUTH_ERROR 0x00000005
|
|
#define LAST_ATTEMPT_STATUS_ERROR_PWR_EVT_AC 0x00000006
|
|
#define LAST_ATTEMPT_STATUS_ERROR_PWR_EVT_BATT 0x00000007
|
|
#define LAST_ATTEMPT_STATUS_ERROR_UNSATISFIED_DEPENDENCIES 0x00000008
|
|
|
|
STATIC UINT8 InsydeBiosGuardSig[] = {'$', 'P', 'F', 'A', 'T', 'H', 'D', 'R'};
|
|
STATIC UINT8 InsydeCertSig[] = {'$', 'P', 'F', 'A', 'T', 'C', 'E', 'R'};
|
|
STATIC UINT8 InsydeUcodeSig[] = {'$', '_', 'M', 'I', 'C', 'R', 'O', 'C', 'O', 'D', 'E', '_', 'I', 'M', 'G', '_'};
|
|
STATIC UINT8 InsydeCertHeaderSig[] = {'$', '_', 'P', 'F', 'A', 'T', '_', 'C', 'E', 'R', '_', 'I', 'M', 'G', '_'};
|
|
STATIC UINT8 InsydeBgupHeaderSig[] = {'$', '_', 'P', 'F', 'A', 'T', '_', 'H', 'D', 'R', '_', 'I', 'M', 'G', '_'};
|
|
|
|
typedef struct _INSYDE_BIOSGUARD_HEADER {
|
|
UINT8 Signature[8]; // $PFATHDR
|
|
UINT32 Version; // 0x00000002
|
|
UINT32 BlockCount; // Block Count (Total count of PUP blocks)
|
|
UINT32 BlockSize; // Block Size (Size of each PUP block)
|
|
UINT32 StructureSize; // INSYDE_BIOSGUARD_HEADER Structure Size
|
|
//UINT32 DataSizeList[BlockCount]; // Data size of each PUP block (<=64K)
|
|
} INSYDE_BIOSGUARD_HEADER;
|
|
|
|
typedef struct _INSYDE_CERT_HEADER {
|
|
UINT8 Signature[8]; // $PFATCER
|
|
UINT32 Version; // 0x00000001
|
|
UINT32 BlockCount; // Block Count (Total count of PUP blocks)
|
|
UINT32 BlockSize; // Block Size (Size of each PUP block)
|
|
UINT32 Checksum;
|
|
UINT8 Reserved[8];
|
|
} INSYDE_CERT_HEADER;
|
|
|
|
typedef struct {
|
|
VOID *InputData;
|
|
UINTN InputSize;
|
|
VOID *Bgup;
|
|
UINTN BgupSize;
|
|
VOID *Cert;
|
|
UINTN CertSize;
|
|
} BIOS_GUARD_DATA;
|
|
|
|
|
|
EFI_STATUS
|
|
TopSwapControl (
|
|
IN BOOLEAN Enable
|
|
)
|
|
{
|
|
//f (mBiosGuardEnabled) {
|
|
// //
|
|
// // TopSwap will be manipulated in BGUP script
|
|
// //
|
|
// return EFI_SUCCESS;
|
|
//}
|
|
|
|
ASSERT (PcdGet8 (PcdTopSwapEnableSwSmi) != 0xFF);
|
|
ASSERT (PcdGet8 (PcdTopSwapDisableSwSmi) != 0xFF);
|
|
|
|
if (Enable) {
|
|
DEBUG ((DEBUG_INFO, "Enable TopSwap via SwSmi (0x%x)\n", PcdGet8 (PcdTopSwapEnableSwSmi)));
|
|
IoWrite8 (R_PCH_IO_APM_CNT, PcdGet8 (PcdTopSwapEnableSwSmi));
|
|
} else {
|
|
DEBUG ((DEBUG_INFO, "Disable TopSwap via SwSmi (0x%x)\n", PcdGet8 (PcdTopSwapDisableSwSmi)));
|
|
IoWrite8 (R_PCH_IO_APM_CNT, PcdGet8 (PcdTopSwapDisableSwSmi));
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
GetUcodeCapsuleInfo (
|
|
IN CONST VOID *Capsule,
|
|
IN OUT H2O_CAPSULE_HEADER **H2oCapsuleHeader
|
|
) {
|
|
EFI_STATUS Status;
|
|
EFI_CAPSULE_HEADER *Datatbuffer;
|
|
UINT8 *Ptr;
|
|
UINTN Index;
|
|
|
|
Datatbuffer = ((EFI_CAPSULE_HEADER*)Capsule);
|
|
if (CompareGuid(&Datatbuffer->CapsuleGuid, PcdGetPtr (PcdWindowsUcodeFirmwareCapsuleGuid))) {
|
|
for (Index = 0; Index < Datatbuffer->CapsuleImageSize; Index++) {
|
|
Ptr = (UINT8 *)Datatbuffer + Datatbuffer->HeaderSize + Index;
|
|
|
|
if (CompareMem (Ptr, InsydeUcodeSig, sizeof (InsydeUcodeSig)) == 0) {
|
|
*H2oCapsuleHeader = (H2O_CAPSULE_HEADER *) Ptr;
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
Status = EFI_NOT_FOUND;
|
|
if ((*H2oCapsuleHeader)->RealSize != 0) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
EFI_STATUS
|
|
SetUpdateProgress (
|
|
IN SYSTEM_FIRMWARE_COMPONENT UpdatingComponent,
|
|
IN UINT32 UpdatingProgress
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
SYSTEM_FIRMWARE_UPDATE_PROGRESS UpdateProgress;
|
|
|
|
UpdateProgress.Component = UpdatingComponent;
|
|
UpdateProgress.Progress = UpdatingProgress;
|
|
|
|
Status = gRT->SetVariable (
|
|
SYSFW_UPDATE_PROGRESS_VARIABLE_NAME,
|
|
&gSysFwUpdateProgressGuid,
|
|
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
|
|
sizeof (SYSTEM_FIRMWARE_UPDATE_PROGRESS),
|
|
&UpdateProgress
|
|
);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
VOID
|
|
ClearUpdateProgress (
|
|
VOID
|
|
)
|
|
{
|
|
gRT->SetVariable (
|
|
L"FmpCapsuleInfo",
|
|
&gFmpCapsuleInfoGuid,
|
|
0,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
gRT->SetVariable (
|
|
SYSFW_UPDATE_PROGRESS_VARIABLE_NAME,
|
|
&gSysFwUpdateProgressGuid,
|
|
0,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
}
|
|
/**
|
|
Update a block on flash.
|
|
|
|
@param[in] Src Number of blocks to be erased
|
|
@param[in] NumBytes Contains the total size of the buffer.
|
|
@param[in] WriteAddress Target address to be updated
|
|
|
|
@retval EFI_SUCCESS. Operation is successful.
|
|
@retval EFI_INVALID_PARAMETER Src is NULL or WriteAddress is not well aligned.
|
|
@retval EFI_OUT_OF_RESOURCES Failed to allocate needed memory buffer.
|
|
@retval EFI_VOLUME_CORRUPTED The block is not updated as expected.
|
|
@retval Others If there is any device errors.
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
FlashUpdate (
|
|
IN UINT8 *Src,
|
|
IN UINTN NumBytes,
|
|
IN UINTN WriteAddress
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT8 *CompareBuffer;
|
|
UINTN Size;
|
|
|
|
if ((Src == NULL) || ((NumBytes % GetFlashBlockSize ()) != 0) || ((WriteAddress % SIZE_4KB) != 0)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
CompareBuffer = AllocateZeroPool (NumBytes);
|
|
if (CompareBuffer == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto Exit;
|
|
}
|
|
|
|
Status = FlashRead (
|
|
CompareBuffer,
|
|
(UINT8 *)WriteAddress,
|
|
NumBytes
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "Read flash address 0x%x error %r\n", WriteAddress));
|
|
goto Exit;
|
|
}
|
|
|
|
if (CompareMem (CompareBuffer, Src, NumBytes) == 0) {
|
|
//
|
|
// No need to update
|
|
//
|
|
Status = EFI_SUCCESS;
|
|
goto Exit;
|
|
}
|
|
|
|
Status = FlashErase (WriteAddress, NumBytes);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "Erase flash address 0x%x error %r\n", WriteAddress));
|
|
goto Exit;
|
|
}
|
|
|
|
Size = NumBytes;
|
|
Status = FlashProgram (
|
|
(UINT8 *)WriteAddress,
|
|
(UINT8 *)Src,
|
|
&Size,
|
|
WriteAddress
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "Program flash address 0x%x error %r\n", WriteAddress));
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Read buffer back to verify udpate status
|
|
//
|
|
ZeroMem (CompareBuffer, NumBytes);
|
|
Status = FlashRead (
|
|
CompareBuffer,
|
|
(UINT8 *)WriteAddress,
|
|
NumBytes
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "Read flash address 0x%x error %r\n", WriteAddress));
|
|
goto Exit;
|
|
}
|
|
|
|
if (CompareMem (CompareBuffer, Src, NumBytes) != 0) {
|
|
//
|
|
// Block is not updated as expected.
|
|
//
|
|
Status = EFI_VOLUME_CORRUPTED;
|
|
DEBUG ((DEBUG_ERROR, "FlashUpdate: Block is not updated as expected.\n"));
|
|
}
|
|
|
|
Exit:
|
|
if (CompareBuffer != NULL) {
|
|
FreePool (CompareBuffer);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
MicrocodeFlashWrite(
|
|
IN EFI_PHYSICAL_ADDRESS FlashAddress,
|
|
IN VOID *Buffer,
|
|
IN UINTN Length
|
|
)
|
|
{
|
|
EFI_PHYSICAL_ADDRESS AlignedFlashAddress;
|
|
VOID *AlignedBuffer;
|
|
UINTN AlignedLength;
|
|
UINTN OffsetHead;
|
|
UINTN OffsetTail;
|
|
EFI_STATUS Status;
|
|
|
|
|
|
DEBUG((EFI_D_INFO, "MicrocodeFlashWrite - 0x%x - 0x%x\n", (UINTN)FlashAddress, Length));
|
|
|
|
//
|
|
// Need make buffer 64K aligned to support ERASE
|
|
//
|
|
// [Aligned] FlashAddress Aligned]
|
|
// | | |
|
|
// V V V
|
|
// +--------------+========+------------+
|
|
// | OffsetHeader | Length | OffsetTail |
|
|
// +--------------+========+------------+
|
|
// ^
|
|
// |<-----------AlignedLength----------->
|
|
// |
|
|
// AlignedFlashAddress
|
|
//
|
|
OffsetHead = FlashAddress & (SIZE_4KB - 1);
|
|
OffsetTail = (FlashAddress + Length) & (SIZE_4KB - 1);
|
|
if (OffsetTail != 0) {
|
|
OffsetTail = SIZE_4KB - OffsetTail;
|
|
}
|
|
|
|
if ((OffsetHead != 0) || (OffsetTail != 0)) {
|
|
AlignedFlashAddress = FlashAddress - OffsetHead;
|
|
AlignedLength = Length + OffsetHead + OffsetTail;
|
|
|
|
AlignedBuffer = AllocatePool(AlignedLength);
|
|
if (AlignedBuffer == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
//
|
|
// Save original buffer. We can't use MMIO read because TopSwap bit may be toggled
|
|
// We must use SPI Read
|
|
//
|
|
//
|
|
// 1. Copy content in Header alignment area
|
|
//
|
|
if (OffsetHead != 0) {
|
|
Status = FlashRead (
|
|
(UINT8 *)AlignedBuffer,
|
|
(UINT8 *)AlignedFlashAddress,
|
|
OffsetHead
|
|
);
|
|
|
|
ASSERT(!EFI_ERROR(Status));
|
|
}
|
|
|
|
//
|
|
// 2. Copy content in Tail alignment area
|
|
//
|
|
if (OffsetTail != 0) {
|
|
Status = FlashRead (
|
|
(UINT8 *)AlignedBuffer + OffsetHead + Length,
|
|
(UINT8 *)(AlignedFlashAddress + OffsetHead + Length),
|
|
OffsetTail
|
|
);
|
|
|
|
ASSERT(!EFI_ERROR(Status));
|
|
}
|
|
|
|
//
|
|
// Override new buffer. Source is from memory. No need to use SPI
|
|
//
|
|
CopyMem((UINT8 *)AlignedBuffer + OffsetHead, Buffer, Length);
|
|
} else {
|
|
AlignedFlashAddress = FlashAddress;
|
|
AlignedBuffer = Buffer;
|
|
AlignedLength = Length;
|
|
}
|
|
|
|
Status = FlashUpdate (
|
|
(UINT8 *)AlignedBuffer,
|
|
AlignedLength,
|
|
(UINTN)AlignedFlashAddress
|
|
);
|
|
|
|
if ((OffsetHead != 0) || (OffsetTail != 0)) {
|
|
FreePool (AlignedBuffer);
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
/*
|
|
For fault tolerant test usage. Will clean it after moving to CR Common
|
|
|
|
*/
|
|
EFI_STATUS
|
|
TestDeadLoop(
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
BOOLEAN IsFirstDeadLoop;
|
|
UINTN DataSize;
|
|
Status = EFI_NOT_FOUND;
|
|
DataSize = sizeof(BOOLEAN);
|
|
Status = gRT->GetVariable(
|
|
L"IsFirstDeadLoop",
|
|
&gEfiCallerIdGuid,
|
|
NULL,
|
|
&DataSize,
|
|
&IsFirstDeadLoop
|
|
);
|
|
if (Status != EFI_NOT_FOUND) {
|
|
DEBUG ((DEBUG_INFO, "Recovery from crash! Skip deadloop.\n"));
|
|
return Status;
|
|
}
|
|
|
|
IsFirstDeadLoop = TRUE;
|
|
Status = gRT->SetVariable(
|
|
L"IsFirstDeadLoop",
|
|
&gEfiCallerIdGuid,
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
|
|
sizeof(BOOLEAN),
|
|
&IsFirstDeadLoop
|
|
);
|
|
|
|
DEBUG ((DEBUG_INFO, "Recovery from crash! deadloop.\n"));
|
|
//
|
|
// Either stop and manual remove power to cause crash or auto reset system can be used
|
|
//
|
|
CpuDeadLoop();
|
|
//gRT->ResetSystem (EfiResetCold, EFI_SUCCESS, 0, NULL);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Check if input CPU index is in new uCode targeted CPU list.
|
|
|
|
@param[in] CpuIndex Given CPU Index to check
|
|
@param[in] TargetProcessorBuf Target CPU processor buffer
|
|
@param[in] TargetProcessorNum number Target CPU processor in buffer
|
|
|
|
@retval TRUE Given CpuIndex is in target uCode CPU list
|
|
@retval FALSE Given CpuIndex is not in target uCode CPU list.
|
|
**/
|
|
BOOLEAN
|
|
IsCpuInTargetCpuList(
|
|
UINTN CpuIndex,
|
|
UINTN *TargetProcessorBuf,
|
|
UINTN TargetProcessorNum
|
|
)
|
|
{
|
|
UINTN Index;
|
|
|
|
for (Index = 0; Index < TargetProcessorNum; Index++ ) {
|
|
if (CpuIndex == TargetProcessorBuf[Index]) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/**
|
|
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
|
|
)
|
|
{
|
|
//
|
|
// Flash Block size alignment is not required for TopSwap Recovery storage selection
|
|
// It is a requirement only when we use Mechanism 2 Option 1.
|
|
//
|
|
ASSERT((PcdGet32(PcdFlashNvStorageMicrocodeBase) % SIZE_4KB) == 0 && (PcdGet32(PcdFlashNvStorageMicrocodeSize) % SIZE_4KB) == 0);
|
|
//
|
|
// Exclude FV header and FFS header
|
|
// We must ensure CpuMicrocodePatchAddress & PcdCpuMicrocodePatchRegionSize must be Flash block size aligned
|
|
//
|
|
// ToDo:
|
|
// 4KB is hardcode rightnow. We can use PcdFlashMicrocodeOffset in the future
|
|
//
|
|
*MicrocodePatchAddress = (VOID *)((UINTN)(FixedPcdGet32 (PcdFlashNvStorageMicrocodeBase) + SIZE_4KB));
|
|
*MicrocodePatchRegionSize = (UINTN)(FixedPcdGet32 (PcdFlashNvStorageMicrocodeSize) - SIZE_4KB) ;
|
|
|
|
DEBUG ((DEBUG_INFO, "MicrocodePatchAddress 0x%x\n", *MicrocodePatchAddress));
|
|
DEBUG ((DEBUG_INFO, "MicrocodePatchRegionSize 0x%x\n", *MicrocodePatchRegionSize));
|
|
|
|
if ((*MicrocodePatchAddress == NULL) || (*MicrocodePatchRegionSize == 0)) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/**
|
|
Calculates the checksum of the header of a file.
|
|
|
|
@param FileHeader Pointer to FFS File Header.
|
|
|
|
@return Checksum of the header.
|
|
Zero means the header is good.
|
|
Non-zero means the header is bad.
|
|
**/
|
|
UINT8
|
|
GetHeaderChecksum (
|
|
IN EFI_FFS_FILE_HEADER *FileHeader
|
|
)
|
|
{
|
|
EFI_FFS_FILE_HEADER2 TestFileHeader;
|
|
|
|
if (IS_FFS_FILE2 (FileHeader)) {
|
|
CopyMem (&TestFileHeader, FileHeader, sizeof (EFI_FFS_FILE_HEADER2));
|
|
//
|
|
// Ingore State and File field in FFS header.
|
|
// Set Header field to 0
|
|
//
|
|
TestFileHeader.State = 0;
|
|
TestFileHeader.IntegrityCheck.Checksum16 = 0;
|
|
|
|
return (0 - CalculateSum8 ((CONST UINT8 *) &TestFileHeader, sizeof (EFI_FFS_FILE_HEADER2)));
|
|
} else {
|
|
CopyMem (&TestFileHeader, FileHeader, sizeof (EFI_FFS_FILE_HEADER));
|
|
//
|
|
// Ingore State and File field in FFS header.
|
|
// Set Header filed to 0
|
|
//
|
|
TestFileHeader.State = 0;
|
|
TestFileHeader.IntegrityCheck.Checksum16 = 0;
|
|
|
|
return (0 - CalculateSum8 ((CONST UINT8 *) &TestFileHeader, sizeof (EFI_FFS_FILE_HEADER)));
|
|
}
|
|
}
|
|
|
|
/**
|
|
Exract uCode Payload, It follows RFC 4506 External Data Representation Standard (XDR)
|
|
each element is a variable-length opaque data. Elements are organized in Fixed-Length Array.
|
|
Now Array contains 4 entries below. Some elements are optional regarding platform configuration.
|
|
uCode capsule generater need to mark length n = 0 in variable-length opaque data element .
|
|
|
|
--------------------------------------
|
|
| UCodeFullRangeImage |
|
|
--------------------------------------
|
|
| UCodeFullRangeBgup (Optional) |
|
|
----------------------------
|
|
| uCode Version(Optional) |
|
|
--------------------------------------
|
|
| uCdeArray(Optional) |
|
|
--------------------------------------
|
|
|
|
@param[In] Image uCode Capsule payload
|
|
@param[In] ImageSize uCode Capsule payload length
|
|
@param[Out] EntryList List of parsed XDR entry
|
|
@param[Out] EntryCount Entry count in the list
|
|
|
|
@retval Status status of parsing uCode Capsule XDR formated data.
|
|
**/
|
|
EFI_STATUS
|
|
ExtractuCodeCapsuleXdrData (
|
|
IN VOID *Image,
|
|
IN UINTN ImageSize,
|
|
OUT UCODE_PAYLOAD_ENTRY **EntryList,
|
|
OUT UINTN *ListCount
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN Index;
|
|
UINT8 *PayloadXdr;
|
|
UINT8 *PayloadXdrEnd;
|
|
UCODE_PAYLOAD_ENTRY *XdrEntryArray;
|
|
UINTN XdrEntryLength;
|
|
|
|
ASSERT(((UINTN)Image % sizeof(UINT32)) == 0);
|
|
|
|
PayloadXdr = (UINT8 *)Image;
|
|
PayloadXdrEnd = (UINT8 *)Image + ImageSize;
|
|
Index = 0;
|
|
|
|
*EntryList = NULL;
|
|
*ListCount = 0;
|
|
|
|
XdrEntryArray = AllocateZeroPool(uCodeArrayMax * sizeof(UCODE_PAYLOAD_ENTRY));
|
|
if (XdrEntryArray == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Iterate each data entry within uCode capsule payload
|
|
//
|
|
while (PayloadXdr < PayloadXdrEnd) {
|
|
if ((PayloadXdr + sizeof (UINT32)) > PayloadXdrEnd) {
|
|
//
|
|
// Xdr described data and Xdr itself should not exceed payload boundary
|
|
//
|
|
DEBUG ((DEBUG_ERROR, "ExtractuCodeCapsuleXdrData: Xdr described data exceeds boundary.\n"));
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto FUNC_EXIT;
|
|
}
|
|
|
|
//
|
|
// Read key length stored in big-endian format
|
|
//
|
|
XdrEntryLength = SwapBytes32 (*(UINT32 *)(PayloadXdr));
|
|
|
|
//
|
|
// Point to the start of the uCode payload data entry
|
|
//
|
|
PayloadXdr += sizeof (UINT32);
|
|
if (PayloadXdr + XdrEntryLength > PayloadXdrEnd) {
|
|
//
|
|
// Xdr described data and Xdr itself should not exceed payload boundary
|
|
//
|
|
DEBUG ((DEBUG_ERROR, "ExtractuCodeCapsuleXdrData: Xdr described data exceeds boundary.\n"));
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto FUNC_EXIT;
|
|
}
|
|
|
|
//
|
|
// Check if there is free room to save payload entry
|
|
//
|
|
if (Index >= uCodeArrayMax) {
|
|
DEBUG ((DEBUG_ERROR, "ExtractuCodeCapsuleXdrData: Xdr described data exceeds maximum entry number %x.\n", uCodeArrayMax));
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto FUNC_EXIT;
|
|
}
|
|
|
|
XdrEntryArray[Index].EntryPtr = PayloadXdr;
|
|
XdrEntryArray[Index].EntrySize = XdrEntryLength;
|
|
|
|
Index++;
|
|
|
|
PayloadXdr += XdrEntryLength;
|
|
//
|
|
// According to RFC 4506, Chapter 3, each XDR descriptor must be aligned to UINT32/32bit boundry.
|
|
//
|
|
PayloadXdr = (UINT8 *)ALIGN_POINTER (PayloadXdr, sizeof (UINT32));
|
|
}
|
|
|
|
*EntryList = XdrEntryArray;
|
|
*ListCount = Index;
|
|
Status = EFI_SUCCESS;
|
|
|
|
DEBUG_CODE (
|
|
DEBUG ((DEBUG_INFO, "ExtractuCodeCapsuleXdrData %x entries are found in payload\n", *ListCount));
|
|
for (Index = 0; Index < *ListCount; Index++) {
|
|
DEBUG ((DEBUG_INFO, "Index %x Offset %x, Size %x\n", Index, XdrEntryArray[Index].EntryPtr - (UINT8 *)Image, XdrEntryArray[Index].EntrySize));
|
|
}
|
|
);
|
|
|
|
FUNC_EXIT:
|
|
if (EFI_ERROR(Status)) {
|
|
DEBUG ((DEBUG_ERROR, "uCode Paylod Xdr Parsing error\n"));
|
|
FreePool(XdrEntryArray);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Update Version info FFS inside uCode FV.
|
|
|
|
uCode FV is orgnized as follwing rules
|
|
|
|
----------------------------
|
|
| FV Header |
|
|
----------------------------
|
|
| Optional Ext Header |
|
|
----------------------------
|
|
| Version FFS |
|
|
----------------------------
|
|
| Optional Pad FFS |
|
|
----------------------------
|
|
| uCode FFS |
|
|
----------------------------
|
|
|
|
During update, we can update FFS + Optional Pad FFS to store new Version FFS. but must keep uCode FFS unchanged
|
|
|
|
@param[In] VerInfoFFS The content of New VersionInf
|
|
|
|
@retval Version File Payload. The Microcode Version file content is returned.
|
|
**/
|
|
EFI_STATUS
|
|
UpdateVersionInfo (
|
|
INTEL_MICROCODE_VERSION_FFS_DATA *NewVersion,
|
|
UINTN NewVersionSize
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT8 *uCodeFv;
|
|
UINTN Index;
|
|
UINTN VersionSize;
|
|
UINTN MaxVersionSize;
|
|
UINT8 *VersionAddr;
|
|
EFI_FFS_FILE_HEADER *uCodeFfsHdr;
|
|
EFI_FFS_FILE_HEADER *VersionFfsHdr;
|
|
|
|
uCodeFfsHdr = NULL;
|
|
VersionFfsHdr = NULL;
|
|
uCodeFv = NULL;
|
|
|
|
//
|
|
// Use SPI read instead of MMIO read bicoz we are using TopSwap. and MMIO is mapping to LastKnownGood
|
|
//
|
|
uCodeFv = AllocateZeroPool(PcdGet32(PcdFlashNvStorageMicrocodeSize));
|
|
if (uCodeFv == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Status = FlashRead (
|
|
(UINT8 *)uCodeFv, //DATA
|
|
(UINT8 *)(UINTN)PcdGet32(PcdFlashNvStorageMicrocodeBase), //ADDRESS
|
|
PcdGet32(PcdFlashNvStorageMicrocodeSize) //LENGTH
|
|
);
|
|
if (EFI_ERROR(Status)) {
|
|
goto FUNC_EXIT;
|
|
}
|
|
|
|
//
|
|
// Search uCode array FFS Header
|
|
//
|
|
for (Index = 0; Index < PcdGet32(PcdFlashNvStorageMicrocodeSize); Index++) {
|
|
if (CompareGuid((EFI_GUID *)&uCodeFv[Index], &gIntelMicrocodeVersionFfsFileGuid)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Index >= PcdGet32(PcdFlashNvStorageMicrocodeSize)) {
|
|
DEBUG ((DEBUG_ERROR, "Can't find Microcode Version FFS\n"));
|
|
ASSERT(FALSE);
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto FUNC_EXIT;
|
|
}
|
|
VersionFfsHdr = (EFI_FFS_FILE_HEADER *)&uCodeFv[Index];
|
|
|
|
//
|
|
// Search uCode array FFS Header
|
|
//
|
|
for (Index = 0; Index < PcdGet32(PcdFlashNvStorageMicrocodeSize); Index++) {
|
|
if (CompareGuid((EFI_GUID *)&uCodeFv[Index], &gIntelMicrocodeArrayFfsFileGuid)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Index >= PcdGet32(PcdFlashNvStorageMicrocodeSize)) {
|
|
DEBUG ((DEBUG_ERROR, "Can't find Microcode array FFS\n"));
|
|
ASSERT(FALSE);
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto FUNC_EXIT;
|
|
}
|
|
uCodeFfsHdr = (EFI_FFS_FILE_HEADER *)&uCodeFv[Index];
|
|
|
|
if (uCodeFfsHdr->Type != EFI_FV_FILETYPE_RAW || VersionFfsHdr->Type != EFI_FV_FILETYPE_RAW) {
|
|
DEBUG ((DEBUG_ERROR, "Can't find Microcode Version FFS\n"));
|
|
ASSERT(FALSE);
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto FUNC_EXIT;
|
|
}
|
|
|
|
|
|
if (IS_FFS_FILE2 (VersionFfsHdr)) {
|
|
VersionSize = FFS_FILE2_SIZE (VersionFfsHdr) - sizeof(EFI_FFS_FILE_HEADER2);
|
|
VersionAddr = (UINT8 *)VersionFfsHdr + sizeof (EFI_FFS_FILE_HEADER2);
|
|
MaxVersionSize = (UINTN)(PHYSICAL_ADDRESS)((UINT8 *)uCodeFfsHdr - (UINT8 *)VersionFfsHdr) - sizeof(EFI_FFS_FILE_HEADER2);
|
|
} else {
|
|
VersionSize = FFS_FILE_SIZE (VersionFfsHdr) - sizeof(EFI_FFS_FILE_HEADER); //0x40- 0x18 = 0x28
|
|
VersionAddr = (UINT8 *)VersionFfsHdr + sizeof (EFI_FFS_FILE_HEADER); //sizeof (EFI_FFS_FILE_HEADER) = 0x18
|
|
MaxVersionSize = (UINTN)(PHYSICAL_ADDRESS)((UINT8 *)uCodeFfsHdr - (UINT8 *)VersionFfsHdr) - sizeof(EFI_FFS_FILE_HEADER); //0xf88
|
|
}
|
|
|
|
//
|
|
// Update Version FFS based on current situation
|
|
//
|
|
if (NewVersionSize <= VersionSize) {
|
|
//
|
|
// New version content is smaller, Update Version data and mask remaing tail to all Zero
|
|
// Do not update current Version FFS Header
|
|
//
|
|
CopyMem(VersionAddr, NewVersion, NewVersionSize);
|
|
ZeroMem(VersionAddr + NewVersionSize, VersionSize - NewVersionSize);
|
|
|
|
MicrocodeFlashWrite((UINTN)PcdGet32(PcdFlashNvStorageMicrocodeBase) + VersionAddr - uCodeFv, VersionAddr, VersionSize);
|
|
} else if (NewVersionSize <= MaxVersionSize) {
|
|
//
|
|
// New version content is bigger. Expand version file to a bigger one
|
|
//
|
|
if (IS_FFS_FILE2 (VersionFfsHdr)) {
|
|
((EFI_FFS_FILE_HEADER2 *)VersionFfsHdr)->ExtendedSize = (UINT64)(MaxVersionSize + sizeof (EFI_FFS_FILE_HEADER2));
|
|
} else {
|
|
VersionFfsHdr->Size[0] = (UINT8)((MaxVersionSize + sizeof (EFI_FFS_FILE_HEADER)) & 0xFF);
|
|
VersionFfsHdr->Size[1] = (UINT8)(((MaxVersionSize + sizeof (EFI_FFS_FILE_HEADER)) & 0xFF00) >> 8);
|
|
VersionFfsHdr->Size[2] = (UINT8)(((MaxVersionSize + sizeof (EFI_FFS_FILE_HEADER)) & 0xFF0000) >> 16);
|
|
}
|
|
|
|
//
|
|
// Todo: Still need to check FFS_ATTRIB_CHECKSUM Attribute.
|
|
// Now assume this attribute bit is 0. CHECKSU Checksum.File = 0xAA
|
|
// Re-calcutate check sum bicoz size is changed
|
|
//
|
|
if ((VersionFfsHdr->Attributes & FFS_ATTRIB_CHECKSUM) == 0) {
|
|
VersionFfsHdr->IntegrityCheck.Checksum.Header = GetHeaderChecksum(VersionFfsHdr);
|
|
} else {
|
|
DEBUG ((DEBUG_ERROR, "Attribute FFS_ATTRIB_CHECKSUM not supported\n"));
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto FUNC_EXIT;
|
|
}
|
|
CopyMem(VersionAddr, NewVersion, NewVersionSize);
|
|
ZeroMem(VersionAddr + NewVersionSize, MaxVersionSize - NewVersionSize);
|
|
|
|
//
|
|
// Update entire Version FFS. Not its size changed to MaxVersionSize + FFS Header
|
|
//
|
|
if (IS_FFS_FILE2 (VersionFfsHdr)) {
|
|
MicrocodeFlashWrite(PcdGet32(PcdFlashNvStorageMicrocodeBase) + (UINT8 *)VersionFfsHdr - uCodeFv, VersionFfsHdr, MaxVersionSize + sizeof (EFI_FFS_FILE_HEADER2));
|
|
} else {
|
|
MicrocodeFlashWrite(PcdGet32(PcdFlashNvStorageMicrocodeBase) + (UINT8 *)VersionFfsHdr - uCodeFv, VersionFfsHdr, MaxVersionSize + sizeof (EFI_FFS_FILE_HEADER));
|
|
}
|
|
} else {
|
|
DEBUG ((DEBUG_ERROR, "Microcode Version FFS size 0x%x is bigger than MaxSize %x\n", NewVersionSize, MaxVersionSize));
|
|
ASSERT(FALSE);
|
|
Status = EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
FUNC_EXIT:
|
|
|
|
if (uCodeFv != NULL) {
|
|
FreePool(uCodeFv);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Get Microcode Version File Data. Caller is responsible for freeing this resource
|
|
|
|
@param[out] MicrocodePatchAddress The address of Microcode
|
|
@param[out] MicrocodePatchRegionSize The region size of Microcode
|
|
|
|
@retval Version File Payload. The Microcode Version file content is returned.
|
|
**/
|
|
EFI_STATUS
|
|
GetMicrocodeRegionVersionInfo (
|
|
OUT UINT8 **VersionInfo,
|
|
OUT UINTN *VersionInfoSize
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_HANDLE *HandleBuffer;
|
|
UINTN NumberOfHandles;
|
|
EFI_FV_FILETYPE FileType;
|
|
UINT32 FvStatus;
|
|
EFI_FV_FILE_ATTRIBUTES Attributes;
|
|
UINTN Size;
|
|
UINTN Index;
|
|
EFI_FIRMWARE_VOLUME2_PROTOCOL *FvInstance;
|
|
UINT8 *DataBuf;
|
|
|
|
DataBuf = NULL;
|
|
*VersionInfo = NULL;
|
|
*VersionInfoSize = 0;
|
|
|
|
//
|
|
// Locate FV protocol.
|
|
//
|
|
Status = gBS->LocateHandleBuffer (
|
|
ByProtocol,
|
|
&gEfiFirmwareVolume2ProtocolGuid,
|
|
NULL,
|
|
&NumberOfHandles,
|
|
&HandleBuffer
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
//
|
|
// Defined errors at this time are not found and out of resources.
|
|
//
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Looking for uCode FVB
|
|
//
|
|
for (Index = 0; Index < NumberOfHandles; Index++) {
|
|
//
|
|
// Get the protocol on this handle
|
|
// This should not fail because of LocateHandleBuffer
|
|
//
|
|
Status = gBS->HandleProtocol (
|
|
HandleBuffer[Index],
|
|
&gEfiFirmwareVolume2ProtocolGuid,
|
|
(VOID**) &FvInstance
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
DEBUG_CODE(
|
|
EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;
|
|
EFI_PHYSICAL_ADDRESS BaseAddr;
|
|
|
|
BaseAddr = 0;
|
|
Status = gBS->HandleProtocol (
|
|
HandleBuffer[Index],
|
|
&gEfiFirmwareVolumeBlockProtocolGuid,
|
|
(VOID**) &Fvb
|
|
);
|
|
Fvb->GetPhysicalAddress(Fvb, &BaseAddr);
|
|
DEBUG ((DEBUG_ERROR, "FVB BaseAddress 0x%x\n", BaseAddr));
|
|
);
|
|
|
|
//
|
|
// See if it has the Fmp Version File storage file
|
|
//
|
|
Status = FvInstance->ReadFile (
|
|
FvInstance,
|
|
&gIntelMicrocodeVersionFfsFileGuid,
|
|
NULL,
|
|
&Size,
|
|
&FileType,
|
|
&Attributes,
|
|
&FvStatus
|
|
);
|
|
|
|
//
|
|
// Found and file attribute is correct
|
|
//
|
|
if (Status == EFI_SUCCESS && FileType == EFI_FV_FILETYPE_RAW && Size >= sizeof(INTEL_MICROCODE_VERSION_FFS_DATA)) {
|
|
DataBuf = AllocatePool(Size);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (DataBuf != NULL) {
|
|
Status = FvInstance->ReadFile (
|
|
FvInstance,
|
|
&gIntelMicrocodeVersionFfsFileGuid,
|
|
&DataBuf,
|
|
&Size,
|
|
&FileType,
|
|
&Attributes,
|
|
&FvStatus
|
|
);
|
|
ASSERT_EFI_ERROR(Status);
|
|
|
|
*VersionInfo = DataBuf;
|
|
*VersionInfoSize = Size;
|
|
DEBUG ((DEBUG_INFO, "Found Version file for uCode Region\n"));
|
|
}
|
|
|
|
//
|
|
// Free any allocated buffers
|
|
//
|
|
gBS->FreePool (HandleBuffer);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Get Microcode update signature of currently loaded Microcode update.
|
|
|
|
@return Microcode signature.
|
|
|
|
**/
|
|
UINT32
|
|
GetCurrentMicrocodeSignature (
|
|
VOID
|
|
)
|
|
{
|
|
MSR_IA32_BIOS_SIGN_ID_REGISTER MsrSignId;
|
|
|
|
AsmWriteMsr64 (MSR_IA32_BIOS_SIGN_ID, 0);
|
|
AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, NULL);
|
|
MsrSignId.Uint64 = AsmReadMsr64 (MSR_IA32_BIOS_SIGN_ID);
|
|
return MsrSignId.Bits.MicrocodeUpdateSignature;
|
|
}
|
|
|
|
/**
|
|
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();
|
|
}
|
|
|
|
/**
|
|
Load Microcode on an Application Processor.
|
|
The function prototype for invoking a function on an Application Processor.
|
|
|
|
@param[in,out] Buffer The pointer to private data buffer.
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
MicrocodeLoadAp (
|
|
IN OUT VOID *Buffer
|
|
)
|
|
{
|
|
MICROCODE_LOAD_BUFFER *MicrocodeLoadBuffer;
|
|
|
|
MicrocodeLoadBuffer = Buffer;
|
|
MicrocodeLoadBuffer->Revision = LoadMicrocode (MicrocodeLoadBuffer->Address);
|
|
}
|
|
|
|
/**
|
|
Load new Microcode on this processor
|
|
|
|
@param[in] MicrocodeFmpPrivate The Microcode driver private data
|
|
@param[in] CpuIndex The index of the processor.
|
|
@param[in] Address The address of new Microcode.
|
|
|
|
@return Loaded Microcode signature.
|
|
|
|
**/
|
|
UINT32
|
|
LoadMicrocodeOnThis (
|
|
IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,
|
|
IN UINTN CpuIndex,
|
|
IN UINT64 Address
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_MP_SERVICES_PROTOCOL *MpService;
|
|
MICROCODE_LOAD_BUFFER MicrocodeLoadBuffer;
|
|
|
|
if (CpuIndex == MicrocodeFmpPrivate->BspIndex) {
|
|
return LoadMicrocode (Address);
|
|
} else {
|
|
MpService = MicrocodeFmpPrivate->MpService;
|
|
MicrocodeLoadBuffer.Address = Address;
|
|
MicrocodeLoadBuffer.Revision = 0;
|
|
Status = MpService->StartupThisAP (
|
|
MpService,
|
|
MicrocodeLoadAp,
|
|
CpuIndex,
|
|
NULL,
|
|
0,
|
|
&MicrocodeLoadBuffer,
|
|
NULL
|
|
);
|
|
ASSERT_EFI_ERROR(Status);
|
|
return MicrocodeLoadBuffer.Revision;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Collect processor information.
|
|
The function prototype for invoking a function on an Application Processor.
|
|
|
|
@param[in,out] Buffer The pointer to private data buffer.
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
CollectProcessorInfo (
|
|
IN OUT VOID *Buffer
|
|
)
|
|
{
|
|
PROCESSOR_INFO *ProcessorInfo;
|
|
|
|
ProcessorInfo = Buffer;
|
|
ProcessorInfo->ProcessorSignature = GetCurrentProcessorSignature();
|
|
ProcessorInfo->PlatformId = GetCurrentPlatformId();
|
|
ProcessorInfo->ProcessorFlags = 1 << ProcessorInfo->PlatformId;
|
|
ProcessorInfo->MicrocodeRevision = GetCurrentMicrocodeSignature();
|
|
|
|
}
|
|
|
|
/**
|
|
Get current Microcode information.
|
|
|
|
The ProcessorInformation (BspIndex/ProcessorCount/ProcessorInfo)
|
|
in MicrocodeFmpPrivate must be initialized.
|
|
|
|
The MicrocodeInformation (DescriptorCount/ImageDescriptor/MicrocodeInfo)
|
|
in MicrocodeFmpPrivate may not be 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;
|
|
EFI_STATUS Status;
|
|
UINT32 AttemptStatus;
|
|
UINTN TargetCpuIndex;
|
|
|
|
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;
|
|
}
|
|
|
|
TargetCpuIndex = (UINTN)-1;
|
|
Status = VerifyMicrocode(MicrocodeFmpPrivate, MicrocodeEntryPoint, TotalSize, FALSE, &AttemptStatus, NULL, &TargetCpuIndex);
|
|
|
|
if (!EFI_ERROR(Status)) {
|
|
IsInUse = TRUE;
|
|
ASSERT (TargetCpuIndex < MicrocodeFmpPrivate->ProcessorCount);
|
|
MicrocodeFmpPrivate->ProcessorInfo[TargetCpuIndex].MicrocodeIndex = Count;
|
|
//
|
|
// Always update logic CPU MicrocodeRevision with highest Supported Microcode Version.
|
|
// This logic complies with Physical CPU uCode patching logic
|
|
//
|
|
if (!MicrocodeFmpPrivate->ProcessorInfo[TargetCpuIndex].IsRealCpu) {
|
|
if ((MicrocodeFmpPrivate->ProcessorInfo[TargetCpuIndex].MicrocodeRevision == (UINT32)-1) ||
|
|
(MicrocodeFmpPrivate->ProcessorInfo[TargetCpuIndex].MicrocodeRevision < MicrocodeEntryPoint->UpdateRevision)) {
|
|
MicrocodeFmpPrivate->ProcessorInfo[TargetCpuIndex].MicrocodeRevision = MicrocodeEntryPoint->UpdateRevision;
|
|
}
|
|
}
|
|
} else {
|
|
IsInUse = FALSE;
|
|
}
|
|
|
|
if (ImageDescriptor != NULL && DescriptorCount > Count) {
|
|
ImageDescriptor[Count].ImageIndex = (UINT8)(Count + 1);
|
|
CopyGuid (&ImageDescriptor[Count].ImageTypeId, PcdGetPtr(PcdWindowsUcodeFirmwareCapsuleGuid));
|
|
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_4KB 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;
|
|
}
|
|
|
|
/**
|
|
Return matched processor information.
|
|
|
|
@param[in] MicrocodeFmpPrivate The Microcode driver private data
|
|
@param[in] ProcessorSignature The processor signature to be matched
|
|
@param[in] ProcessorFlags The processor flags to be matched
|
|
@param[in, out] TargetCpuIndex On input, the index of target CPU which tries to match the Microcode. (UINTN)-1 means to try all.
|
|
On output, the index of target CPU which matches the Microcode.
|
|
|
|
@return matched processor information.
|
|
**/
|
|
PROCESSOR_INFO *
|
|
GetMatchedProcessor (
|
|
IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,
|
|
IN UINT32 ProcessorSignature,
|
|
IN UINT32 ProcessorFlags,
|
|
IN OUT UINTN *TargetCpuIndex
|
|
)
|
|
{
|
|
UINTN Index;
|
|
|
|
if (*TargetCpuIndex != (UINTN)-1) {
|
|
Index = *TargetCpuIndex;
|
|
if (ProcessorSignature == MicrocodeFmpPrivate->ProcessorInfo[Index].ProcessorSignature &&
|
|
(ProcessorFlags & MicrocodeFmpPrivate->ProcessorInfo[Index].ProcessorFlags) != 0) {
|
|
return &MicrocodeFmpPrivate->ProcessorInfo[Index];
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
for (Index = 0; Index < MicrocodeFmpPrivate->ProcessorCount; Index++) {
|
|
if ((ProcessorSignature == MicrocodeFmpPrivate->ProcessorInfo[Index].ProcessorSignature) &&
|
|
((ProcessorFlags & MicrocodeFmpPrivate->ProcessorInfo[Index].ProcessorFlags) != 0)) {
|
|
*TargetCpuIndex = Index;
|
|
return &MicrocodeFmpPrivate->ProcessorInfo[Index];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
Verify Microcode.
|
|
if the uCode fails check sum. or version is lower than any of the processor that matches signature and flags
|
|
Or tryload == TRUE but uCode fails CPU authentication. Microcode verification fails
|
|
|
|
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[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().
|
|
@param[in, out] TargetCpuIndex On input, the index of target CPU which tries to match the Microcode. (UINTN)-1 means to try all.
|
|
On output, the index of target CPU which matches the Microcode.
|
|
|
|
@retval EFI_SUCCESS The Microcode image passes verification.
|
|
@retval EFI_VOLUME_CORRUPTED The Microcode image is corrupted.
|
|
@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 MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,
|
|
IN VOID *Image,
|
|
IN UINTN ImageSize,
|
|
IN BOOLEAN TryLoad,
|
|
OUT UINT32 *LastAttemptStatus,
|
|
OUT CHAR16 **AbortReason, OPTIONAL
|
|
IN OUT UINTN *TargetCpuIndex
|
|
)
|
|
{
|
|
UINTN Index;
|
|
CPU_MICROCODE_HEADER *MicrocodeEntryPoint;
|
|
UINTN TotalSize;
|
|
UINTN DataSize;
|
|
UINT32 CurrentRevision;
|
|
PROCESSOR_INFO *ProcessorInfo;
|
|
UINT32 CheckSum32;
|
|
UINT32 InCompleteSum32;
|
|
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 TotalSize
|
|
//
|
|
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 & (SIZE_1KB - 1)) != 0) {
|
|
DEBUG ((DEBUG_ERROR, "VerifyMicrocode - TotalSize is not multiples of 1024 bytes (1 KBytes)\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 - TotalSize not equal to ImageSize\n"));
|
|
*LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;
|
|
if (AbortReason != NULL) {
|
|
*AbortReason = AllocateCopyPool(sizeof(L"InvalidTotalSize"), L"InvalidTotalSize");
|
|
}
|
|
return EFI_VOLUME_CORRUPTED;
|
|
}
|
|
//
|
|
// Check DataSize
|
|
//
|
|
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 is not multiples of DWORDs\n"));
|
|
*LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;
|
|
if (AbortReason != NULL) {
|
|
*AbortReason = AllocateCopyPool(sizeof(L"InvalidDataSize"), L"InvalidDataSize");
|
|
}
|
|
return EFI_VOLUME_CORRUPTED;
|
|
}
|
|
//
|
|
// Check CheckSum32
|
|
//
|
|
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;
|
|
}
|
|
|
|
InCompleteSum32 = CheckSum32;
|
|
InCompleteSum32 -= MicrocodeEntryPoint->ProcessorSignature.Uint32;
|
|
InCompleteSum32 -= MicrocodeEntryPoint->ProcessorFlags;
|
|
InCompleteSum32 -= MicrocodeEntryPoint->Checksum;
|
|
|
|
//
|
|
// Check ProcessorSignature/ProcessorFlags
|
|
//
|
|
ProcessorInfo = GetMatchedProcessor (MicrocodeFmpPrivate, MicrocodeEntryPoint->ProcessorSignature.Uint32, MicrocodeEntryPoint->ProcessorFlags, TargetCpuIndex);
|
|
if (ProcessorInfo == NULL) {
|
|
CorrectMicrocode = FALSE;
|
|
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 incorrect
|
|
//
|
|
DEBUG ((DEBUG_ERROR, "VerifyMicrocode - The checksum for extended table is incorrect\n"));
|
|
} else {
|
|
//
|
|
// Checksum correct
|
|
//
|
|
ExtendedTableCount = ExtendedTableHeader->ExtendedSignatureCount;
|
|
if (ExtendedTableCount > (ExtendedTableLength - sizeof(CPU_MICROCODE_EXTENDED_TABLE_HEADER)) / sizeof(CPU_MICROCODE_EXTENDED_TABLE)) {
|
|
DEBUG ((DEBUG_ERROR, "VerifyMicrocode - ExtendedTableCount too big\n"));
|
|
} else {
|
|
ExtendedTable = (CPU_MICROCODE_EXTENDED_TABLE *)(ExtendedTableHeader + 1);
|
|
for (Index = 0; Index < ExtendedTableCount; Index++) {
|
|
CheckSum32 = InCompleteSum32;
|
|
CheckSum32 += ExtendedTable->ProcessorSignature.Uint32;
|
|
CheckSum32 += ExtendedTable->ProcessorFlag;
|
|
CheckSum32 += ExtendedTable->Checksum;
|
|
if (CheckSum32 != 0) {
|
|
DEBUG ((DEBUG_ERROR, "VerifyMicrocode - The checksum for ExtendedTable entry with index 0x%x is incorrect\n", Index));
|
|
} else {
|
|
//
|
|
// Verify Header
|
|
//
|
|
ProcessorInfo = GetMatchedProcessor (MicrocodeFmpPrivate, ExtendedTable->ProcessorSignature.Uint32, ExtendedTable->ProcessorFlag, TargetCpuIndex);
|
|
if (ProcessorInfo != NULL) {
|
|
//
|
|
// Find one
|
|
//
|
|
CorrectMicrocode = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
ExtendedTable++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!CorrectMicrocode) {
|
|
if (TryLoad) {
|
|
DEBUG ((DEBUG_ERROR, "VerifyMicrocode - fail on Current ProcessorSignature/ProcessorFlags\n"));
|
|
}
|
|
*LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INCORRECT_VERSION;
|
|
if (AbortReason != NULL) {
|
|
*AbortReason = AllocateCopyPool(sizeof(L"UnsupportedProcessorSignature/ProcessorFlags"), L"UnsupportedProcessorSignature/ProcessorFlags");
|
|
}
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Regard same revision as processor CurrentRevion can pass verification.
|
|
// Caller can double filter this case outside VerifyMicrocde
|
|
//
|
|
CurrentRevision = ProcessorInfo->MicrocodeRevision;
|
|
if ((CurrentRevision != (UINT32)-1) && (MicrocodeEntryPoint->UpdateRevision < CurrentRevision)) {
|
|
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;
|
|
}
|
|
|
|
//
|
|
// Check UpdateRevision and Load MCU when CPU is real hardware.
|
|
// If CPU definition comes from uCode Policy PCD, do not TryLoad
|
|
//
|
|
if (ProcessorInfo->IsRealCpu && TryLoad) {
|
|
CurrentRevision = LoadMicrocodeOnThis(MicrocodeFmpPrivate, ProcessorInfo->CpuIndex, (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 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 next FIT Microcode entrypoint.
|
|
|
|
@param[in] MicrocodeFmpPrivate The Microcode driver private data
|
|
@param[in] MicrocodeEntryPoint Current Microcode entrypoint
|
|
|
|
@return next FIT Microcode entrypoint.
|
|
**/
|
|
CPU_MICROCODE_HEADER *
|
|
GetNextFitMicrocode (
|
|
IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,
|
|
IN CPU_MICROCODE_HEADER *MicrocodeEntryPoint
|
|
)
|
|
{
|
|
UINTN Index;
|
|
|
|
for (Index = 0; Index < MicrocodeFmpPrivate->FitMicrocodeEntryCount; Index++) {
|
|
if (MicrocodeEntryPoint == MicrocodeFmpPrivate->FitMicrocodeInfo[Index].MicrocodeEntryPoint) {
|
|
if (Index == (UINTN) MicrocodeFmpPrivate->FitMicrocodeEntryCount - 1) {
|
|
// it is last one
|
|
return NULL;
|
|
} else {
|
|
// return next one
|
|
return MicrocodeFmpPrivate->FitMicrocodeInfo[Index + 1].MicrocodeEntryPoint;
|
|
}
|
|
}
|
|
}
|
|
|
|
ASSERT(FALSE);
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
Find empty FIT Microcode entrypoint.
|
|
|
|
@param[in] MicrocodeFmpPrivate The Microcode driver private data
|
|
@param[in] ImageSize The size of Microcode image buffer in bytes.
|
|
@param[out] AvailableSize Available size of the empty FIT Microcode entrypoint.
|
|
|
|
@return Empty FIT Microcode entrypoint.
|
|
**/
|
|
CPU_MICROCODE_HEADER *
|
|
FindEmptyFitMicrocode (
|
|
IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,
|
|
IN UINTN ImageSize,
|
|
OUT UINTN *AvailableSize
|
|
)
|
|
{
|
|
UINTN Index;
|
|
CPU_MICROCODE_HEADER *MicrocodeEntryPoint;
|
|
CPU_MICROCODE_HEADER *NextMicrocodeEntryPoint;
|
|
VOID *MicrocodePatchAddress;
|
|
UINTN MicrocodePatchRegionSize;
|
|
|
|
MicrocodePatchAddress = MicrocodeFmpPrivate->MicrocodePatchAddress;
|
|
MicrocodePatchRegionSize = MicrocodeFmpPrivate->MicrocodePatchRegionSize;
|
|
|
|
for (Index = 0; Index < MicrocodeFmpPrivate->FitMicrocodeEntryCount; Index++) {
|
|
if (MicrocodeFmpPrivate->FitMicrocodeInfo[Index].Empty) {
|
|
MicrocodeEntryPoint = MicrocodeFmpPrivate->FitMicrocodeInfo[Index].MicrocodeEntryPoint;
|
|
NextMicrocodeEntryPoint = GetNextFitMicrocode (MicrocodeFmpPrivate, MicrocodeEntryPoint);
|
|
if (NextMicrocodeEntryPoint != NULL) {
|
|
*AvailableSize = (UINTN) NextMicrocodeEntryPoint - (UINTN) MicrocodeEntryPoint;
|
|
} else {
|
|
*AvailableSize = (UINTN) MicrocodePatchAddress + MicrocodePatchRegionSize - (UINTN) MicrocodeEntryPoint;
|
|
}
|
|
if (*AvailableSize >= ImageSize) {
|
|
return MicrocodeEntryPoint;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
Find unused FIT Microcode entrypoint.
|
|
|
|
@param[in] MicrocodeFmpPrivate The Microcode driver private data
|
|
@param[in] ImageSize The size of Microcode image buffer in bytes.
|
|
@param[out] AvailableSize Available size of the unused FIT Microcode entrypoint.
|
|
|
|
@return Unused FIT Microcode entrypoint.
|
|
**/
|
|
CPU_MICROCODE_HEADER *
|
|
FindUnusedFitMicrocode (
|
|
IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,
|
|
IN UINTN ImageSize,
|
|
OUT UINTN *AvailableSize
|
|
)
|
|
{
|
|
UINTN Index;
|
|
UINTN CpuIndex;
|
|
CPU_MICROCODE_HEADER *MicrocodeEntryPoint;
|
|
CPU_MICROCODE_HEADER *NextMicrocodeEntryPoint;
|
|
VOID *MicrocodePatchAddress;
|
|
UINTN MicrocodePatchRegionSize;
|
|
|
|
MicrocodePatchAddress = MicrocodeFmpPrivate->MicrocodePatchAddress;
|
|
MicrocodePatchRegionSize = MicrocodeFmpPrivate->MicrocodePatchRegionSize;
|
|
|
|
//
|
|
// Get uCode Slot that doesn't match uCode selection Policy
|
|
//
|
|
for (Index = 0; Index < MicrocodeFmpPrivate->FitMicrocodeEntryCount; Index++) {
|
|
|
|
//
|
|
// If uCode in FIT slot matches uCode selection policy PCD, skip over it
|
|
//
|
|
for (CpuIndex = 0; CpuIndex < MicrocodeFmpPrivate->ProcessorCount; CpuIndex++) {
|
|
if (MicrocodeFmpPrivate->ProcessorInfo[CpuIndex].MicrocodeIndex == Index) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (CpuIndex < MicrocodeFmpPrivate->ProcessorCount) {
|
|
continue;
|
|
}
|
|
|
|
MicrocodeEntryPoint = MicrocodeFmpPrivate->FitMicrocodeInfo[Index].MicrocodeEntryPoint;
|
|
|
|
NextMicrocodeEntryPoint = GetNextFitMicrocode (MicrocodeFmpPrivate, MicrocodeEntryPoint);
|
|
if (NextMicrocodeEntryPoint != NULL) {
|
|
*AvailableSize = (UINTN) NextMicrocodeEntryPoint - (UINTN) MicrocodeEntryPoint;
|
|
} else {
|
|
*AvailableSize = (UINTN) MicrocodePatchAddress + MicrocodePatchRegionSize - (UINTN) MicrocodeEntryPoint;
|
|
}
|
|
|
|
if (*AvailableSize >= ImageSize) {
|
|
return MicrocodeEntryPoint;
|
|
}
|
|
|
|
}
|
|
|
|
DEBUG ((DEBUG_INFO, "Can't find right uCode slot that doesn't match uCode Selection Policy\n"));
|
|
|
|
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,
|
|
IN EFI_FIRMWARE_MANAGEMENT_UPDATE_IMAGE_PROGRESS Progress
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
DEBUG ((DEBUG_INFO, "PlatformUpdate:"));
|
|
DEBUG ((DEBUG_INFO, " Address - 0x%lx,", Address));
|
|
DEBUG ((DEBUG_INFO, " Length - 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 with FIT.
|
|
|
|
ToDo: skip reclaim. Reclaim could corrupt entire ucode range
|
|
|
|
@param[in] MicrocodeFmpPrivate The Microcode driver private data
|
|
@param[in] TargetMicrocodeEntryPoint Target Microcode entrypoint to be updated
|
|
@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
|
|
UpdateMicrocodeFlashRegionWithFit (
|
|
IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,
|
|
IN CPU_MICROCODE_HEADER *TargetMicrocodeEntryPoint,
|
|
IN VOID *Image,
|
|
IN UINTN ImageSize,
|
|
IN VOID *NewVersion, OPTIONAL
|
|
IN UINTN NewVersionSize, OPTIONAL
|
|
OUT UINT32 *LastAttemptStatus,
|
|
IN EFI_FIRMWARE_MANAGEMENT_UPDATE_IMAGE_PROGRESS Progress
|
|
)
|
|
{
|
|
VOID *MicrocodePatchAddress;
|
|
UINTN MicrocodePatchRegionSize;
|
|
UINTN TargetTotalSize;
|
|
EFI_STATUS Status;
|
|
VOID *MicrocodePatchScratchBuffer;
|
|
UINT8 *ScratchBufferPtr;
|
|
UINTN ScratchBufferSize;
|
|
UINTN RestSize;
|
|
UINTN AvailableSize;
|
|
VOID *NextMicrocodeEntryPoint;
|
|
VOID *EmptyFitMicrocodeEntry;
|
|
VOID *UnusedFitMicrocodeEntry;
|
|
|
|
DEBUG ((DEBUG_INFO, "UpdateMicrocodeFlashRegionWithFit: Image - 0x%x, size - 0x%x\n", Image, ImageSize));
|
|
|
|
SetUpdateProgress (UpdatinguCode, 71);
|
|
Progress(71);
|
|
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;
|
|
|
|
//
|
|
// Step 2. Update uCode Major region.
|
|
//
|
|
// Target data collection
|
|
//
|
|
TargetTotalSize = 0;
|
|
AvailableSize = 0;
|
|
if (TargetMicrocodeEntryPoint != NULL) {
|
|
if (TargetMicrocodeEntryPoint->DataSize == 0) {
|
|
TargetTotalSize = 2048;
|
|
} else {
|
|
TargetTotalSize = TargetMicrocodeEntryPoint->TotalSize;
|
|
}
|
|
DEBUG ((DEBUG_INFO, " TargetTotalSize - 0x%x\n", TargetTotalSize));
|
|
NextMicrocodeEntryPoint = GetNextFitMicrocode (MicrocodeFmpPrivate, TargetMicrocodeEntryPoint);
|
|
DEBUG ((DEBUG_INFO, " NextMicrocodeEntryPoint - 0x%x\n", NextMicrocodeEntryPoint));
|
|
if (NextMicrocodeEntryPoint != NULL) {
|
|
ASSERT ((UINTN) NextMicrocodeEntryPoint >= ((UINTN) TargetMicrocodeEntryPoint + TargetTotalSize));
|
|
AvailableSize = (UINTN) NextMicrocodeEntryPoint - (UINTN) TargetMicrocodeEntryPoint;
|
|
} else {
|
|
AvailableSize = (UINTN) MicrocodePatchAddress + MicrocodePatchRegionSize - (UINTN) TargetMicrocodeEntryPoint;
|
|
}
|
|
DEBUG ((DEBUG_INFO, " AvailableSize - 0x%x\n", AvailableSize));
|
|
ASSERT (AvailableSize >= TargetTotalSize);
|
|
}
|
|
//
|
|
// Total Size means the Microcode size.
|
|
// Available Size means the Microcode size plus the pad till (1) next Microcode or (2) the end.
|
|
//
|
|
// (1)
|
|
// +------+-----------+-----+------+===================+
|
|
// | MCU1 | Microcode | PAD | MCU2 | Empty |
|
|
// +------+-----------+-----+------+===================+
|
|
// | TotalSize |
|
|
// |<-AvailableSize->|
|
|
//
|
|
// (2)
|
|
// +------+-----------+===================+
|
|
// | MCU | Microcode | Empty |
|
|
// +------+-----------+===================+
|
|
// | TotalSize |
|
|
// |<- AvailableSize ->|
|
|
//
|
|
|
|
//
|
|
// Update based on policy
|
|
//
|
|
|
|
//
|
|
// 1. If there is enough space to override old one in situ, replace old microcode in situ.
|
|
//
|
|
if (AvailableSize >= ImageSize) {
|
|
DEBUG ((DEBUG_INFO, "Replace old microcode in situ\n"));
|
|
//
|
|
// +------+------------+------+===================+
|
|
// |Other | Old Image | ... | Empty |
|
|
// +------+------------+------+===================+
|
|
//
|
|
// +------+---------+--+------+===================+
|
|
// |Other |New Image|FF| ... | Empty |
|
|
// +------+---------+--+------+===================+
|
|
//
|
|
// 1.1. Copy new image
|
|
CopyMem (ScratchBufferPtr, Image, ImageSize);
|
|
ScratchBufferSize += ImageSize;
|
|
ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;
|
|
// 1.2. Pad 0xFF
|
|
RestSize = AvailableSize - ImageSize;
|
|
if (RestSize > 0) {
|
|
SetMem (ScratchBufferPtr, RestSize, 0xFF);
|
|
ScratchBufferSize += RestSize;
|
|
ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;
|
|
}
|
|
Status = UpdateMicrocode(
|
|
(UINTN)TargetMicrocodeEntryPoint,
|
|
MicrocodePatchScratchBuffer,
|
|
ScratchBufferSize,
|
|
LastAttemptStatus,
|
|
Progress
|
|
);
|
|
goto CONTEXT_CLEAR;
|
|
}
|
|
|
|
//
|
|
// 2. If there is empty FIT microcode entry with enough space, use it.
|
|
//
|
|
EmptyFitMicrocodeEntry = FindEmptyFitMicrocode (MicrocodeFmpPrivate, ImageSize, &AvailableSize);
|
|
if (EmptyFitMicrocodeEntry != NULL) {
|
|
DEBUG ((DEBUG_INFO, "Use empty FIT microcode entry\n"));
|
|
// 2.1. Copy new image
|
|
CopyMem (ScratchBufferPtr, Image, ImageSize);
|
|
ScratchBufferSize += ImageSize;
|
|
ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;
|
|
// 2.2. Pad 0xFF
|
|
RestSize = AvailableSize - ImageSize;
|
|
if (RestSize > 0) {
|
|
SetMem (ScratchBufferPtr, RestSize, 0xFF);
|
|
ScratchBufferSize += RestSize;
|
|
ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;
|
|
}
|
|
Status = UpdateMicrocode (
|
|
(UINTN) EmptyFitMicrocodeEntry,
|
|
MicrocodePatchScratchBuffer,
|
|
ScratchBufferSize,
|
|
LastAttemptStatus,
|
|
Progress
|
|
);
|
|
if (!EFI_ERROR (Status) && (TargetMicrocodeEntryPoint != NULL)) {
|
|
//
|
|
// Empty old microcode.
|
|
//
|
|
ScratchBufferPtr = MicrocodePatchScratchBuffer;
|
|
SetMem (ScratchBufferPtr, TargetTotalSize, 0xFF);
|
|
ScratchBufferSize = TargetTotalSize;
|
|
ScratchBufferPtr = (UINT8 *) MicrocodePatchScratchBuffer + ScratchBufferSize;
|
|
Status = UpdateMicrocode (
|
|
(UINTN) TargetMicrocodeEntryPoint,
|
|
MicrocodePatchScratchBuffer,
|
|
ScratchBufferSize,
|
|
LastAttemptStatus,
|
|
Progress
|
|
);
|
|
}
|
|
goto CONTEXT_CLEAR;
|
|
}
|
|
|
|
//
|
|
// 3. If there is unused microcode entry with enough space, use it.
|
|
//
|
|
UnusedFitMicrocodeEntry = FindUnusedFitMicrocode (MicrocodeFmpPrivate, ImageSize, &AvailableSize);
|
|
if (UnusedFitMicrocodeEntry != NULL) {
|
|
DEBUG ((DEBUG_INFO, "Use unused FIT microcode entry\n"));
|
|
// 3.1. Copy new image
|
|
CopyMem (ScratchBufferPtr, Image, ImageSize);
|
|
ScratchBufferSize += ImageSize;
|
|
ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;
|
|
// 3.2. Pad 0xFF
|
|
RestSize = AvailableSize - ImageSize;
|
|
if (RestSize > 0) {
|
|
SetMem (ScratchBufferPtr, RestSize, 0xFF);
|
|
ScratchBufferSize += RestSize;
|
|
ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;
|
|
}
|
|
Status = UpdateMicrocode (
|
|
(UINTN) UnusedFitMicrocodeEntry,
|
|
MicrocodePatchScratchBuffer,
|
|
ScratchBufferSize,
|
|
LastAttemptStatus,
|
|
Progress
|
|
);
|
|
if (!EFI_ERROR (Status) && (TargetMicrocodeEntryPoint != NULL)) {
|
|
//
|
|
// Empty old microcode.
|
|
//
|
|
ScratchBufferPtr = MicrocodePatchScratchBuffer;
|
|
SetMem (ScratchBufferPtr, TargetTotalSize, 0xFF);
|
|
ScratchBufferSize = TargetTotalSize;
|
|
ScratchBufferPtr = (UINT8 *) MicrocodePatchScratchBuffer + ScratchBufferSize;
|
|
Status = UpdateMicrocode (
|
|
(UINTN) TargetMicrocodeEntryPoint,
|
|
MicrocodePatchScratchBuffer,
|
|
ScratchBufferSize,
|
|
LastAttemptStatus,
|
|
Progress
|
|
);
|
|
}
|
|
goto CONTEXT_CLEAR;
|
|
}
|
|
|
|
//
|
|
// 4. No usable FIT microcode entry
|
|
// It is an expected failure. Recovery to interrupted update is not supprted
|
|
//
|
|
DEBUG ((DEBUG_ERROR, "No usable FIT microcode entry\n"));
|
|
*LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES;
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
|
|
CONTEXT_CLEAR:
|
|
//
|
|
// 5. Update Version FFS if update succeed
|
|
//
|
|
if (!EFI_ERROR(Status)) {
|
|
if (NewVersion != NULL && NewVersionSize >= sizeof(INTEL_MICROCODE_VERSION_FFS_DATA)) {
|
|
Status = UpdateVersionInfo (
|
|
(INTEL_MICROCODE_VERSION_FFS_DATA *)NewVersion,
|
|
NewVersionSize
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_INFO, "Update VersionFfs fail!\n"));
|
|
goto FUNC_EXIT;
|
|
}
|
|
DEBUG ((DEBUG_INFO, "Update VersionFfs succeed!\n"));
|
|
}
|
|
}
|
|
|
|
FUNC_EXIT:
|
|
//
|
|
// Todo: May need to add memory free for FIT case
|
|
//
|
|
//
|
|
if (MicrocodePatchScratchBuffer != NULL) {
|
|
FreePool(MicrocodePatchScratchBuffer);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Update Microcode flash region.
|
|
|
|
Todo: Ensure all Microcde are saved at 4KB boundary
|
|
|
|
@param[in] MicrocodeFmpPrivate The Microcode driver private data
|
|
@param[in] TargetMicrocodeEntryPoint Target Microcode entrypoint to be updated
|
|
@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 *TargetMicrocodeEntryPoint,
|
|
IN VOID *Image,
|
|
IN UINTN ImageSize,
|
|
OUT UINT32 *LastAttemptStatus,
|
|
IN EFI_FIRMWARE_MANAGEMENT_UPDATE_IMAGE_PROGRESS Progress
|
|
)
|
|
{
|
|
VOID *MicrocodePatchAddress;
|
|
UINTN MicrocodePatchRegionSize;
|
|
UINTN TargetTotalSize;
|
|
UINTN UsedRegionSize;
|
|
EFI_STATUS Status;
|
|
VOID *MicrocodePatchScratchBuffer;
|
|
UINT8 *ScratchBufferPtr;
|
|
UINTN ScratchBufferSize;
|
|
UINTN RestSize;
|
|
UINTN AvailableSize;
|
|
VOID *NextMicrocodeEntryPoint;
|
|
MICROCODE_INFO *MicrocodeInfo;
|
|
UINTN MicrocodeCount;
|
|
UINTN Index;
|
|
//EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION ProgressBarForegroundColor;
|
|
|
|
//ProgressBarForegroundColor.Raw = FixedPcdGet32(PcdFmpDeviceProgressColor);
|
|
|
|
DEBUG ((DEBUG_INFO, "UpdateMicrocodeFlashRegion: Image - 0x%x, size - 0x%x\n", Image, ImageSize));
|
|
|
|
SetUpdateProgress (UpdatinguCode, 71);
|
|
Progress(71);
|
|
//DisplayUpdateProgress (71, &ProgressBarForegroundColor);
|
|
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;
|
|
|
|
//
|
|
// Step 2. Update uCode Major region.
|
|
// Before this function, TopSwap region is now a mirror to Major uCode region after syncing with Major uCode,.
|
|
// After switching TopSwap bit, MMIO will be routed to TopSwap region and get same content as from Major region
|
|
//
|
|
DEBUG ((DEBUG_ERROR, "MicrocodeUpdate Phase 1 Start\n"));
|
|
|
|
//
|
|
// Target data collection
|
|
//
|
|
TargetTotalSize = 0;
|
|
AvailableSize = 0;
|
|
NextMicrocodeEntryPoint = NULL;
|
|
if (TargetMicrocodeEntryPoint != NULL) {
|
|
if (TargetMicrocodeEntryPoint->DataSize == 0) {
|
|
TargetTotalSize = 2048;
|
|
} else {
|
|
TargetTotalSize = TargetMicrocodeEntryPoint->TotalSize;
|
|
}
|
|
DEBUG ((DEBUG_INFO, " TargetTotalSize - 0x%x\n", TargetTotalSize));
|
|
NextMicrocodeEntryPoint = GetNextMicrocode(MicrocodeFmpPrivate, TargetMicrocodeEntryPoint);
|
|
DEBUG ((DEBUG_INFO, " NextMicrocodeEntryPoint - 0x%x\n", NextMicrocodeEntryPoint));
|
|
if (NextMicrocodeEntryPoint != NULL) {
|
|
ASSERT ((UINTN)NextMicrocodeEntryPoint >= ((UINTN)TargetMicrocodeEntryPoint + TargetTotalSize));
|
|
AvailableSize = (UINTN)NextMicrocodeEntryPoint - (UINTN)TargetMicrocodeEntryPoint;
|
|
} else {
|
|
AvailableSize = (UINTN)MicrocodePatchAddress + MicrocodePatchRegionSize - (UINTN)TargetMicrocodeEntryPoint;
|
|
}
|
|
DEBUG ((DEBUG_INFO, " AvailableSize - 0x%x\n", AvailableSize));
|
|
ASSERT (AvailableSize >= TargetTotalSize);
|
|
}
|
|
UsedRegionSize = GetCurrentMicrocodeUsedRegionSize(MicrocodeFmpPrivate);
|
|
DEBUG ((DEBUG_INFO, " UsedRegionSize - 0x%x\n", UsedRegionSize));
|
|
ASSERT (UsedRegionSize >= TargetTotalSize);
|
|
if (TargetMicrocodeEntryPoint != NULL) {
|
|
ASSERT ((UINTN)MicrocodePatchAddress + UsedRegionSize >= ((UINTN)TargetMicrocodeEntryPoint + TargetTotalSize));
|
|
}
|
|
//
|
|
// Total Size means the Microcode size.
|
|
// Available Size means the Microcode size plus the pad till (1) next 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"));
|
|
//
|
|
// +------+------------+------+===================+
|
|
// |Other | Old Image | ... | Empty |
|
|
// +------+------------+------+===================+
|
|
//
|
|
// +------+---------+--+------+===================+
|
|
// |Other |New Image|FF| ... | Empty |
|
|
// +------+---------+--+------+===================+
|
|
//
|
|
// 1.1. Copy new image
|
|
CopyMem (ScratchBufferPtr, Image, ImageSize);
|
|
ScratchBufferSize += ImageSize;
|
|
ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;
|
|
// 1.2. Pad 0xFF
|
|
RestSize = AvailableSize - ImageSize;
|
|
if (RestSize > 0) {
|
|
SetMem (ScratchBufferPtr, RestSize, 0xFF);
|
|
ScratchBufferSize += RestSize;
|
|
ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;
|
|
}
|
|
Status = UpdateMicrocode(
|
|
(UINTN)TargetMicrocodeEntryPoint,
|
|
MicrocodePatchScratchBuffer,
|
|
ScratchBufferSize,
|
|
LastAttemptStatus,
|
|
Progress
|
|
);
|
|
goto CONTEXT_CLEAR;
|
|
}
|
|
|
|
//
|
|
// 2. If there is enough space to remove old one and add new one, reorg and replace old microcode.
|
|
//
|
|
if (MicrocodePatchRegionSize - (UsedRegionSize - TargetTotalSize) >= ImageSize) {
|
|
if (TargetMicrocodeEntryPoint == 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,
|
|
Progress
|
|
);
|
|
} else {
|
|
DEBUG ((DEBUG_INFO, "Reorg and replace old microcode\n"));
|
|
//
|
|
// +------+------------+------+===================+
|
|
// |Other | Old Image | ... | Empty |
|
|
// +------+------------+------+===================+
|
|
//
|
|
// +------+---------------+------+================+
|
|
// |Other | New Image | ... | Empty |
|
|
// +------+---------------+------+================+
|
|
//
|
|
// 2.1. Copy new image
|
|
CopyMem (ScratchBufferPtr, Image, ImageSize);
|
|
ScratchBufferSize += ImageSize;
|
|
ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;
|
|
// 2.2. Copy rest images after the old image.
|
|
if (NextMicrocodeEntryPoint != 0) {
|
|
RestSize = (UINTN)MicrocodePatchAddress + UsedRegionSize - ((UINTN)NextMicrocodeEntryPoint);
|
|
CopyMem (ScratchBufferPtr, NextMicrocodeEntryPoint, RestSize);
|
|
ScratchBufferSize += RestSize;
|
|
ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;
|
|
}
|
|
Status = UpdateMicrocode(
|
|
(UINTN)TargetMicrocodeEntryPoint,
|
|
MicrocodePatchScratchBuffer,
|
|
ScratchBufferSize,
|
|
LastAttemptStatus,
|
|
Progress
|
|
);
|
|
}
|
|
goto CONTEXT_CLEAR;
|
|
}
|
|
|
|
//
|
|
// 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 (ScratchBufferPtr, Image, ImageSize);
|
|
ScratchBufferSize += ImageSize;
|
|
ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;
|
|
// 3.2. Copy some others to rest buffer
|
|
for (Index = 0; Index < MicrocodeCount; Index++) {
|
|
if (!MicrocodeInfo[Index].InUse) {
|
|
continue;
|
|
}
|
|
if (MicrocodeInfo[Index].MicrocodeEntryPoint == TargetMicrocodeEntryPoint) {
|
|
continue;
|
|
}
|
|
if (MicrocodeInfo[Index].TotalSize <= MicrocodePatchRegionSize - ScratchBufferSize) {
|
|
CopyMem (ScratchBufferPtr, MicrocodeInfo[Index].MicrocodeEntryPoint, MicrocodeInfo[Index].TotalSize);
|
|
ScratchBufferSize += MicrocodeInfo[Index].TotalSize;
|
|
ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;
|
|
}
|
|
}
|
|
// 3.3. Pad 0xFF
|
|
RestSize = MicrocodePatchRegionSize - ScratchBufferSize;
|
|
if (RestSize > 0) {
|
|
SetMem (ScratchBufferPtr, RestSize, 0xFF);
|
|
ScratchBufferSize += RestSize;
|
|
ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;
|
|
}
|
|
Status = UpdateMicrocode(
|
|
(UINTN)MicrocodePatchAddress,
|
|
MicrocodePatchScratchBuffer,
|
|
ScratchBufferSize,
|
|
LastAttemptStatus,
|
|
Progress
|
|
);
|
|
goto CONTEXT_CLEAR;
|
|
}
|
|
|
|
//
|
|
// 4. The new image size is bigger than the whole MCU region.
|
|
// It is an expected failure. Recovery to interrupted update is not supprted
|
|
//
|
|
DEBUG ((DEBUG_ERROR, "Microcode too big\n"));
|
|
*LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES;
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
|
|
CONTEXT_CLEAR:
|
|
|
|
ClearUpdateProgress ();
|
|
|
|
//FUNC_EXIT:
|
|
|
|
DEBUG ((DEBUG_ERROR, "MicrocodeUpdate Phase 1 End. Status 0x%x\n", Status));
|
|
|
|
if (MicrocodePatchScratchBuffer != NULL) {
|
|
FreePool(MicrocodePatchScratchBuffer);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Sort FIT microcode entries based upon MicrocodeEntryPoint, from low to high.
|
|
|
|
@param[in] MicrocodeFmpPrivate private data structure to be initialized.
|
|
|
|
**/
|
|
VOID
|
|
SortFitMicrocodeInfo (
|
|
IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate
|
|
)
|
|
{
|
|
FIT_MICROCODE_INFO *FitMicrocodeEntry;
|
|
FIT_MICROCODE_INFO *NextFitMicrocodeEntry;
|
|
FIT_MICROCODE_INFO TempFitMicrocodeEntry;
|
|
FIT_MICROCODE_INFO *FitMicrocodeEntryEnd;
|
|
|
|
FitMicrocodeEntry = MicrocodeFmpPrivate->FitMicrocodeInfo;
|
|
NextFitMicrocodeEntry = FitMicrocodeEntry + 1;
|
|
FitMicrocodeEntryEnd = MicrocodeFmpPrivate->FitMicrocodeInfo + MicrocodeFmpPrivate->FitMicrocodeEntryCount;
|
|
while (FitMicrocodeEntry < FitMicrocodeEntryEnd) {
|
|
while (NextFitMicrocodeEntry < FitMicrocodeEntryEnd) {
|
|
if (FitMicrocodeEntry->MicrocodeEntryPoint > NextFitMicrocodeEntry->MicrocodeEntryPoint) {
|
|
CopyMem (&TempFitMicrocodeEntry, FitMicrocodeEntry, sizeof (FIT_MICROCODE_INFO));
|
|
CopyMem (FitMicrocodeEntry, NextFitMicrocodeEntry, sizeof (FIT_MICROCODE_INFO));
|
|
CopyMem (NextFitMicrocodeEntry, &TempFitMicrocodeEntry, sizeof (FIT_MICROCODE_INFO));
|
|
}
|
|
|
|
NextFitMicrocodeEntry = NextFitMicrocodeEntry + 1;
|
|
}
|
|
|
|
FitMicrocodeEntry = FitMicrocodeEntry + 1;
|
|
NextFitMicrocodeEntry = FitMicrocodeEntry + 1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
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 corrupted.
|
|
@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
|
|
EFIAPI
|
|
MicrocodeSlotSpiWrite (
|
|
IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,
|
|
IN VOID *Image,
|
|
IN UINTN ImageSize,
|
|
IN VOID *NewVersion, OPTIONAL
|
|
IN UINTN NewVersionSize, OPTIONAL
|
|
OUT UINT32 *LastAttemptVersion,
|
|
OUT UINT32 *LastAttemptStatus,
|
|
OUT CHAR16 **AbortReason,
|
|
IN EFI_FIRMWARE_MANAGEMENT_UPDATE_IMAGE_PROGRESS Progress
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
VOID *AlignedImage;
|
|
CPU_MICROCODE_HEADER *TargetMicrocodeEntryPoint;
|
|
UINTN TargetMicrcodeIndex;
|
|
UINTN Index;
|
|
UINTN TargetCpuIndex;
|
|
UINTN CpuIndex;
|
|
PROCESSOR_INFO *ProcessorInfo;
|
|
UINTN *TargetProcessorBuf;
|
|
UINTN TargetProcessorNum;
|
|
MICROCODE_INFO *MicrocodeInfo;
|
|
|
|
AlignedImage = Image;
|
|
TargetMicrocodeEntryPoint = NULL;
|
|
ProcessorInfo = MicrocodeFmpPrivate->ProcessorInfo;
|
|
TargetMicrcodeIndex = (UINTN) - 1;
|
|
|
|
//
|
|
// MCU must be 16 bytes aligned
|
|
//
|
|
SetUpdateProgress (UpdatinguCode, 61);
|
|
Progress(61);
|
|
AlignedImage = AllocateCopyPool(ImageSize, Image); //only one microcode
|
|
if (AlignedImage == NULL) {
|
|
DEBUG ((DEBUG_ERROR, "Fail to allocate aligned image\n"));
|
|
*LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES;
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
TargetProcessorBuf = AllocateZeroPool(sizeof(UINTN) * MicrocodeFmpPrivate->ProcessorCount);
|
|
if (TargetProcessorBuf == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto FUNC_EXIT;
|
|
}
|
|
|
|
DEBUG ((DEBUG_INFO, "Start to verify new uCode!\n"));
|
|
|
|
//
|
|
// Check all physical/logic CPUs that new uCode can apply to
|
|
//
|
|
for (CpuIndex = 0, TargetProcessorNum = 0; CpuIndex < MicrocodeFmpPrivate->ProcessorCount; CpuIndex++) {
|
|
Status = VerifyMicrocode(
|
|
MicrocodeFmpPrivate,
|
|
AlignedImage,
|
|
ImageSize,
|
|
FALSE,
|
|
LastAttemptStatus,
|
|
AbortReason,
|
|
&CpuIndex
|
|
);
|
|
|
|
//
|
|
// Update must make sure new uCode can apply to CPU and version is higher than existing one.
|
|
//
|
|
if (Status == EFI_SUCCESS) {
|
|
|
|
MicrocodeInfo = &MicrocodeFmpPrivate->MicrocodeInfo[ProcessorInfo[CpuIndex].MicrocodeIndex];
|
|
|
|
DEBUG ((DEBUG_INFO, " New uCode Target CpuIndex found- 0x%x\n", CpuIndex));
|
|
DEBUG ((DEBUG_INFO, " New uCod Version 0x%x\n", ((CPU_MICROCODE_HEADER *)AlignedImage)->UpdateRevision));
|
|
DEBUG ((DEBUG_INFO, " Existing uCode Revision 0x%x\n", MicrocodeInfo->MicrocodeEntryPoint->UpdateRevision));
|
|
|
|
if ((ProcessorInfo[CpuIndex].MicrocodeIndex == (UINT32) -1 ||
|
|
((CPU_MICROCODE_HEADER *)AlignedImage)->UpdateRevision > MicrocodeInfo->MicrocodeEntryPoint->UpdateRevision)) {
|
|
TargetProcessorBuf[TargetProcessorNum] = CpuIndex;
|
|
TargetProcessorNum++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (TargetProcessorNum == 0) {
|
|
//
|
|
// New uCode is not in the uCode selection policy list. Ignore this uCode
|
|
//
|
|
DEBUG ((DEBUG_ERROR, "New uCode fails in verification! Quit Update.\n"));
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto FUNC_EXIT;
|
|
}
|
|
|
|
//
|
|
// Find a replacable uCode existing on flash. It has a smaller or equivalent CPU support set comparing with new uCode
|
|
// regarding platform supported CPU set.
|
|
//
|
|
for (Index = 0; Index < TargetProcessorNum; Index++) {
|
|
TargetCpuIndex = TargetProcessorBuf[Index];
|
|
|
|
//
|
|
// New uCode targeting CPU don't have an exisiting uCode to replace. Skip it
|
|
//
|
|
if (ProcessorInfo[TargetCpuIndex].MicrocodeIndex == (UINTN) -1) {
|
|
continue;
|
|
}
|
|
|
|
for (CpuIndex = 0; CpuIndex < MicrocodeFmpPrivate->ProcessorCount; CpuIndex++) {
|
|
//
|
|
// Only check Processor beyond new uCode support list
|
|
//
|
|
if (IsCpuInTargetCpuList(CpuIndex, TargetProcessorBuf, TargetProcessorNum)) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// If existing uCode is supporting a larger Processor list than new uCode , it can't be replcaed
|
|
//
|
|
if (ProcessorInfo[CpuIndex].MicrocodeIndex == ProcessorInfo[TargetCpuIndex].MicrocodeIndex) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Found an exisintg uCode that support no more processors beyond new uCode. It is replacable candidate
|
|
//
|
|
if (CpuIndex == MicrocodeFmpPrivate->ProcessorCount) {
|
|
TargetMicrcodeIndex = MicrocodeFmpPrivate->ProcessorInfo[TargetCpuIndex].MicrocodeIndex;
|
|
break;
|
|
}
|
|
}
|
|
|
|
DEBUG ((DEBUG_INFO, " TargetMicrocodeIndex - 0x%x\n", TargetMicrcodeIndex));
|
|
|
|
if (TargetMicrcodeIndex != (UINTN)-1) {
|
|
ASSERT (TargetMicrcodeIndex < MicrocodeFmpPrivate->DescriptorCount);
|
|
TargetMicrocodeEntryPoint = MicrocodeFmpPrivate->MicrocodeInfo[TargetMicrcodeIndex].MicrocodeEntryPoint;
|
|
} else {
|
|
TargetMicrocodeEntryPoint = NULL;
|
|
}
|
|
|
|
DEBUG ((DEBUG_INFO, " TargetMicrocodeEntryPoint - 0x%x\n", TargetMicrocodeEntryPoint));
|
|
DEBUG ((DEBUG_INFO, " Try to update, Fit mode or Non-fit mode!\n"));
|
|
|
|
//
|
|
// Record uCode update progress. Used for Seamless recovery from interrupted update
|
|
//
|
|
if (MicrocodeFmpPrivate->FitMicrocodeInfo != NULL) {
|
|
Status = UpdateMicrocodeFlashRegionWithFit (
|
|
MicrocodeFmpPrivate,
|
|
TargetMicrocodeEntryPoint,
|
|
AlignedImage,
|
|
ImageSize,
|
|
NewVersion,
|
|
NewVersionSize,
|
|
LastAttemptStatus,
|
|
Progress
|
|
);
|
|
} else {
|
|
Status = UpdateMicrocodeFlashRegion (
|
|
MicrocodeFmpPrivate,
|
|
TargetMicrocodeEntryPoint,
|
|
AlignedImage,
|
|
ImageSize,
|
|
LastAttemptStatus,
|
|
Progress
|
|
);
|
|
}
|
|
|
|
*LastAttemptVersion = ((INTEL_MICROCODE_VERSION_FFS_DATA *)NewVersion)->Version;
|
|
|
|
FUNC_EXIT:
|
|
|
|
FreePool(AlignedImage);
|
|
|
|
if (TargetProcessorBuf!= NULL) {
|
|
FreePool(TargetProcessorBuf);
|
|
}
|
|
|
|
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 corrupted.
|
|
@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
|
|
EFIAPI
|
|
MicrocodeSpiWrite (
|
|
IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,
|
|
IN VOID *Image,
|
|
IN UINTN ImageSize,
|
|
OUT UINT32 *LastAttemptVersion,
|
|
OUT UINT32 *LastAttemptStatus,
|
|
OUT CHAR16 **AbortReason,
|
|
IN EFI_FIRMWARE_MANAGEMENT_UPDATE_IMAGE_PROGRESS Progress
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
VOID *AlignedImage;
|
|
|
|
AlignedImage = Image;
|
|
|
|
if (ImageSize > PcdGet32(PcdFlashNvStorageMicrocodeSize)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
DEBUG ((DEBUG_INFO, "Update uCode region - Start\n"));
|
|
Status = UpdateMicrocode(
|
|
FixedPcdGet32 (PcdFlashNvStorageMicrocodeBase), // 0xFFC50000
|
|
AlignedImage,
|
|
ImageSize,
|
|
LastAttemptStatus,
|
|
Progress
|
|
);
|
|
DEBUG ((DEBUG_INFO, "Update uCode region - Done\n"));
|
|
//FUNC_EXIT:
|
|
|
|
FreePool(AlignedImage);
|
|
|
|
return Status;
|
|
}
|
|
/*
|
|
_________
|
|
| |
|
|
| Bgup |
|
|
|_______|
|
|
| |
|
|
| Data |
|
|
|_______|
|
|
| |
|
|
| Cert |
|
|
|_______|
|
|
|
|
*/
|
|
EFI_STATUS
|
|
GetBiosGuardData (
|
|
IN VOID *FullFirmwareImage,
|
|
IN UINTN FullFirmwareImageSize,
|
|
IN BIOS_GUARD_DATA *InputBuffer,
|
|
IN UINTN IndexOfBlock
|
|
) {
|
|
|
|
UINT8 *UcodeDataPtr;
|
|
UINT32 UcodeDataSize;
|
|
INSYDE_BIOSGUARD_HEADER *BgupHeaderPtr;
|
|
UINT8 *BgupDataPtr;
|
|
INSYDE_CERT_HEADER *CertHeaderPtr;
|
|
UINT8 *CertDataPtr;
|
|
H2O_CAPSULE_HEADER *H2oCapsuleHeader;
|
|
H2O_CAPSULE_HEADER *H2oCertHeader;
|
|
H2O_CAPSULE_HEADER *H2oBgupHeader;
|
|
BOOLEAN IsAlign64K;
|
|
EFI_CAPSULE_HEADER *Datatbuffer;
|
|
UINT8 *Ptr;
|
|
UINTN Index;
|
|
BOOLEAN IsFound;
|
|
|
|
UcodeDataPtr = NULL;
|
|
UcodeDataSize = 0;
|
|
BgupHeaderPtr = NULL;
|
|
BgupDataPtr = NULL;
|
|
CertHeaderPtr = NULL;
|
|
CertDataPtr = NULL;
|
|
H2oCapsuleHeader = NULL;
|
|
H2oCertHeader = NULL;
|
|
H2oBgupHeader = NULL;
|
|
IsAlign64K = FALSE;
|
|
IsFound = FALSE;
|
|
|
|
|
|
Datatbuffer = ((EFI_CAPSULE_HEADER*)FullFirmwareImage);
|
|
if (CompareGuid(&Datatbuffer->CapsuleGuid, PcdGetPtr (PcdWindowsUcodeFirmwareCapsuleGuid))) {
|
|
for (Index = 0; Index < Datatbuffer->CapsuleImageSize; Index++) {
|
|
Ptr = (UINT8 *)Datatbuffer + Datatbuffer->HeaderSize + Index;
|
|
if (CompareMem (Ptr, InsydeUcodeSig, sizeof (InsydeUcodeSig)) == 0) {
|
|
H2oCapsuleHeader = (H2O_CAPSULE_HEADER *) Ptr;
|
|
IsFound = TRUE;
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
if (IsFound) {
|
|
UcodeDataPtr = (UINT8*)H2oCapsuleHeader + sizeof(H2O_CAPSULE_HEADER);
|
|
UcodeDataSize = H2oCapsuleHeader->RealSize;
|
|
} else {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
//
|
|
// Get ucode data (FullMode)
|
|
//
|
|
if (UcodeDataSize % SIZE_64KB == 0) {
|
|
IsAlign64K = TRUE;
|
|
}
|
|
if (!IsAlign64K && (IndexOfBlock+1 == UcodeDataSize/SIZE_64KB)) {
|
|
InputBuffer->InputSize = UcodeDataSize - ((IndexOfBlock + 1)*SIZE_64KB);
|
|
InputBuffer->InputData = (VOID*)(UcodeDataPtr + IndexOfBlock*InputBuffer->InputSize);
|
|
} else {
|
|
InputBuffer->InputSize = SIZE_64KB;
|
|
InputBuffer->InputData = (VOID*)(UcodeDataPtr + IndexOfBlock*SIZE_64KB);
|
|
}
|
|
|
|
//
|
|
// Get Certificate header
|
|
//
|
|
H2oCertHeader = (H2O_CAPSULE_HEADER*)((UINT8*)UcodeDataPtr + H2oCapsuleHeader->AllocateSize);
|
|
if (CompareMem(H2oCertHeader->HeaderSignature , InsydeCertHeaderSig, sizeof(InsydeCertHeaderSig)) == 0) {
|
|
CertHeaderPtr = (INSYDE_CERT_HEADER*)((UINT8*)H2oCertHeader + sizeof(H2O_CAPSULE_HEADER));
|
|
} else {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
CertDataPtr = ((UINT8*)CertHeaderPtr + sizeof(INSYDE_CERT_HEADER)); //$PFATCER
|
|
InputBuffer->Cert = CertDataPtr + IndexOfBlock*CertHeaderPtr->BlockSize;
|
|
InputBuffer->CertSize = CertHeaderPtr->BlockSize;
|
|
|
|
//
|
|
// Get Bgup header
|
|
//
|
|
H2oBgupHeader = (H2O_CAPSULE_HEADER*)((UINT8*)CertHeaderPtr + H2oCertHeader->AllocateSize);
|
|
if (CompareMem(H2oBgupHeader->HeaderSignature , InsydeBgupHeaderSig, sizeof(InsydeBgupHeaderSig)) == 0) {
|
|
BgupHeaderPtr = (INSYDE_BIOSGUARD_HEADER*)((UINT8*)H2oBgupHeader + sizeof(H2O_CAPSULE_HEADER));
|
|
} else {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
BgupDataPtr = ((UINT8*)BgupHeaderPtr + BgupHeaderPtr->StructureSize); //$PFATHDR
|
|
InputBuffer->Bgup = BgupDataPtr + IndexOfBlock*BgupHeaderPtr->BlockSize;
|
|
InputBuffer->BgupSize = BgupHeaderPtr->BlockSize;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Update uCode via BiosGuard service.
|
|
|
|
@param[in] SystemFirmwareImage Points to the System Firmware (BIOS) image.
|
|
@param[in] SystemFirmwareImageSize The length of the System Firmware image in bytes.
|
|
@param[out] BgupImage Points to BgupImage which would be sent to BiosGaurd ACM along with SystemFirmwareImage.
|
|
BgupImage contains BGSL to update BIOS and certificate for authentication.
|
|
@param[out] BgupImageSize The length of the BgupImage in bytes.
|
|
|
|
@retval EFI_SUCCESS The System Firmware image is updated successfully.
|
|
@retval EFI_SECURITY_VIOLATION The update operation fails due to SVN check error.
|
|
@retval EFI_DEVICE_ERROR The update operation fails.
|
|
@retval EFI_INVALID_PARAMETER BgupImage is NULL or BgupImageSize is unexpected.
|
|
@retval Others The update operation fails.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
PerformBiosGuardUpdateuCode (
|
|
IN VOID *SystemFirmwareImage,
|
|
IN UINTN SystemFirmwareImageSize,
|
|
IN BIOS_GUARD_DATA *InputBuffer
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
BIOSGUARD_HOB *BiosGuardHobPtr;
|
|
EFI_PHYSICAL_ADDRESS BiosGuardMemAddress;
|
|
UINT32 BiosGuardMemSize;
|
|
EFI_PHYSICAL_ADDRESS BgupCertificate;
|
|
EFI_PHYSICAL_ADDRESS BgIoTrapAddress;
|
|
UINT8 *BiosGuardMemAddressPtr;
|
|
UINT8 *BgupCertificatePtr;
|
|
UINT64 BiosGuardStatus;
|
|
|
|
ASSERT (SystemFirmwareImage != NULL);
|
|
ASSERT (SystemFirmwareImageSize != 0);
|
|
ASSERT (InputBuffer != NULL);
|
|
|
|
BiosGuardHobPtr = NULL;
|
|
BiosGuardMemAddressPtr = NULL;
|
|
BgupCertificatePtr = NULL;
|
|
BiosGuardMemAddress = 0;
|
|
BiosGuardMemSize = 0;
|
|
BgIoTrapAddress = 0;
|
|
Status = EFI_NOT_STARTED;
|
|
|
|
//
|
|
// Get BIOS Guard Hob
|
|
//
|
|
BiosGuardHobPtr = GetFirstGuidHob (&gBiosGuardHobGuid);
|
|
if (BiosGuardHobPtr == NULL) {
|
|
DEBUG ((DEBUG_ERROR, "BIOS Guard HOB not available\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
BiosGuardMemAddress = (EFI_PHYSICAL_ADDRESS) BiosGuardHobPtr->BiosGuardMemAddress;
|
|
BiosGuardMemSize = (UINT32) LShiftU64 (BiosGuardHobPtr->BiosGuardMemSize, 20);
|
|
BgIoTrapAddress = (EFI_PHYSICAL_ADDRESS) BiosGuardHobPtr->BiosGuardIoTrapAddress;
|
|
|
|
BgupCertificate = (EFI_PHYSICAL_ADDRESS) (BiosGuardMemAddress + BiosGuardMemSize - BGUPC_MEMORY_OFFSET);
|
|
BiosGuardMemAddressPtr = (UINT8 *) BiosGuardMemAddress;
|
|
BgupCertificatePtr = (UINT8 *) BgupCertificate;
|
|
|
|
DEBUG ((DEBUG_INFO, "PerformBiosGuardUpdate: BiosGuardMemAddress = %x\n", BiosGuardMemAddress));
|
|
DEBUG ((DEBUG_INFO, "PerformBiosGuardUpdate: BiosGuardMemSize = %x\n", BiosGuardMemSize));
|
|
DEBUG ((DEBUG_INFO, "PerformBiosGuardUpdate: BgupCertificate = %x\n", BgupCertificate));
|
|
|
|
CopyMem (BiosGuardMemAddressPtr, InputBuffer->Bgup, InputBuffer->BgupSize); //bgup (bgup header (0x30)+ script (0xa0) == INSYDE_BIOSGUARD_HEADER.BlockSize)
|
|
CopyMem ((BiosGuardMemAddressPtr + InputBuffer->BgupSize), InputBuffer->InputData , InputBuffer->InputSize); //data (64k)
|
|
CopyMem (BgupCertificatePtr, InputBuffer->Cert, InputBuffer->CertSize); // cerificate , size = sizeof (BGUPC_ALGORITHM_1);
|
|
IoRead8 (BgIoTrapAddress);
|
|
CopyMem (&BiosGuardStatus, BiosGuardMemAddressPtr, sizeof (UINT64));
|
|
|
|
DEBUG ((DEBUG_INFO, "PerformBiosGuardUpdate: BIOS GUARD Bios Update Execution Result = 0x%016llX\n", BiosGuardStatus));
|
|
|
|
if ((BiosGuardStatus & 0xFFFF) != 0) {
|
|
switch (BiosGuardStatus) {
|
|
case ERR_BAD_SVN:
|
|
Status = EFI_SECURITY_VIOLATION;
|
|
break;
|
|
default:
|
|
Status = EFI_DEVICE_ERROR;
|
|
break;
|
|
}
|
|
} else if ((BiosGuardStatus & 0xFFFF0000) != 0) {
|
|
//
|
|
// BGUP execution succeds but function exits with extra error code
|
|
// 1 Erase error
|
|
// 2 Write error
|
|
// 3 Region error
|
|
//
|
|
DEBUG ((DEBUG_INFO, "PerformBiosGuardUpdate: extra data returns BIOS GUARD script If = %x\n", (BiosGuardStatus & 0xFFFF0000) >> 16));
|
|
Status = EFI_DEVICE_ERROR;
|
|
} else {
|
|
Status = EFI_SUCCESS;
|
|
}
|
|
|
|
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 corrupted.
|
|
@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
|
|
EFIAPI
|
|
MicrocodeBiosGuardWrite (
|
|
IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,
|
|
IN VOID *Image,
|
|
IN UINTN ImageSize,
|
|
OUT UINT32 *LastAttemptVersion,
|
|
OUT UINT32 *LastAttemptStatus,
|
|
OUT CHAR16 **AbortReason,
|
|
IN EFI_FIRMWARE_MANAGEMENT_UPDATE_IMAGE_PROGRESS Progress
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN Index;
|
|
BIOS_GUARD_DATA *InputData;
|
|
UINT32 Count;
|
|
|
|
if (PcdGet32(PcdFlashNvStorageMicrocodeSize)%SIZE_64KB == 0) {
|
|
Count = PcdGet32(PcdFlashNvStorageMicrocodeSize)/SIZE_64KB;
|
|
} else {
|
|
Count = PcdGet32(PcdFlashNvStorageMicrocodeSize)/SIZE_64KB + 1;
|
|
}
|
|
|
|
InputData = AllocateZeroPool(sizeof(BIOS_GUARD_DATA));
|
|
for (Index = 0; Index < Count ; Index++){
|
|
Status = GetBiosGuardData(Image, ImageSize, InputData, Index);
|
|
if (!EFI_ERROR(Status)) {
|
|
SetUpdateProgress (UpdatinguCode, 55 + 6*(UINT32)Index);
|
|
Progress(55 + 6*(UINT32)Index);
|
|
|
|
DEBUG ((DEBUG_INFO, "PerformBiosGuardUpdateuCode Index=%x\n",Index));
|
|
DEBUG ((DEBUG_INFO, "PerformBiosGuardUpdateuCode InputData->InputData=%x, InputData->InputSize=%x\n",InputData->InputData,InputData->InputSize));
|
|
DEBUG ((DEBUG_INFO, "PerformBiosGuardUpdateuCode InputData->Bgup=%x, InputData->BgupSize=%x\n",InputData->Bgup, InputData->BgupSize));
|
|
DEBUG ((DEBUG_INFO, "PerformBiosGuardUpdateuCode InputData->Cert=%x, InputData->CertSize=%x\n",InputData->Cert, InputData->CertSize));
|
|
|
|
Status = PerformBiosGuardUpdateuCode(Image, ImageSize, InputData);
|
|
if(EFI_ERROR(Status)) {
|
|
DEBUG ((DEBUG_INFO, "PerformBiosGuardUpdateuCode Error, Status = %r\n", Status));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
FreePool(InputData);
|
|
|
|
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 corrupted.
|
|
@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
|
|
EFIAPI
|
|
MicrocodeWrite (
|
|
IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,
|
|
IN VOID *Image,
|
|
IN UINTN ImageSize,
|
|
OUT UINT32 *LastAttemptVersion,
|
|
OUT UINT32 *LastAttemptStatus,
|
|
OUT CHAR16 **AbortReason,
|
|
IN EFI_FIRMWARE_MANAGEMENT_UPDATE_IMAGE_PROGRESS Progress
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UCODE_PAYLOAD_ENTRY *Entry;
|
|
UINTN EntryCount;
|
|
UINT8 *AlignedImage;
|
|
H2O_CAPSULE_HEADER *CapsuleHeader;
|
|
VOID *CapsulePtr;
|
|
UINTN CapsuleSize;
|
|
|
|
Entry = NULL;
|
|
AlignedImage = NULL;
|
|
CapsulePtr = NULL;
|
|
CapsuleSize = 0;
|
|
|
|
DEBUG ((DEBUG_INFO, "[%a] Enter!\n", __FUNCTION__));
|
|
|
|
CapsuleHeader = AllocatePool(sizeof (H2O_CAPSULE_HEADER));
|
|
Status = GetUcodeCapsuleInfo (Image, &CapsuleHeader);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
CapsulePtr = (VOID *)((UINT8*)CapsuleHeader + sizeof(H2O_CAPSULE_HEADER));
|
|
CapsuleSize = CapsuleHeader->RealSize;
|
|
//
|
|
// XDR RFC requres the Fixed-Length Array starting address to be UINT32 aligned
|
|
// Aligned buffer will be used later on
|
|
//
|
|
if (PcdGetBool(PcdBiosGuardEnable) == FALSE) {
|
|
if (((UINTN)CapsulePtr % sizeof(UINT32)) != 0) {
|
|
AlignedImage = AllocateCopyPool (CapsuleSize, CapsulePtr);
|
|
if (AlignedImage == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto EXIT;
|
|
}
|
|
CapsulePtr = AlignedImage;
|
|
}
|
|
|
|
//
|
|
// Extra All infomation from uCode Capsule payload. It is encoded
|
|
//
|
|
Status = ExtractuCodeCapsuleXdrData((VOID*)CapsulePtr, CapsuleSize, &Entry, &EntryCount);
|
|
if (EFI_ERROR(Status)) {
|
|
return Status;
|
|
}
|
|
DEBUG ((DEBUG_INFO, "[%a] Extract XDR header from payload!\n", __FUNCTION__));
|
|
}
|
|
|
|
//
|
|
// There are 3 update modes. Update policies are different
|
|
// 1) BiosGuard enable, only support full uCode range update. Do not consider
|
|
// 2) BiosGuard disable, full uCode range SPI update
|
|
// 3) BiosGuard disable, flexible uCode slot update controlled by uCode Selection PCD
|
|
//
|
|
SetUpdateProgress (UpdatinguCode, 51);
|
|
Progress(51);
|
|
if (PcdGetBool(PcdBiosGuardEnable)) {
|
|
SetUpdateProgress (UpdatinguCode, 55);
|
|
Progress(55);
|
|
|
|
TopSwapControl (TRUE);
|
|
DEBUG ((DEBUG_INFO, "Microcode full range update (BiosGuard Enable)\n"));
|
|
Status = MicrocodeBiosGuardWrite (
|
|
MicrocodeFmpPrivate,
|
|
Image,
|
|
ImageSize,
|
|
LastAttemptVersion,
|
|
LastAttemptStatus,
|
|
AbortReason,
|
|
Progress
|
|
);
|
|
TopSwapControl (FALSE);
|
|
} else if (Entry[uCodeFullRangeIndex].EntrySize > 0) {
|
|
SetUpdateProgress (UpdatinguCode, 55);
|
|
Progress(55);
|
|
TopSwapControl (TRUE);
|
|
DEBUG ((DEBUG_INFO, "Microcode full range update (BiosGuard Disable)\n"));
|
|
Status = MicrocodeSpiWrite(
|
|
MicrocodeFmpPrivate,
|
|
Entry[uCodeFullRangeIndex].EntryPtr,
|
|
Entry[uCodeFullRangeIndex].EntrySize,
|
|
LastAttemptVersion,
|
|
LastAttemptStatus,
|
|
AbortReason,
|
|
Progress
|
|
);
|
|
TopSwapControl (FALSE);
|
|
} else if (EntryCount == uCodeArrayMax && Entry[uCodeFullRangeIndex].EntrySize == 0 && Entry[uCodeArrayIndex].EntrySize > 0) {
|
|
TopSwapControl (TRUE);
|
|
DEBUG ((DEBUG_INFO, "Microcode slot update mode (BiosGuard Disabled)\n"));
|
|
Status = MicrocodeSlotSpiWrite(
|
|
MicrocodeFmpPrivate,
|
|
Entry[uCodeArrayIndex].EntryPtr,
|
|
Entry[uCodeArrayIndex].EntrySize,
|
|
Entry[uCodeVersionIndex].EntryPtr,
|
|
Entry[uCodeVersionIndex].EntrySize,
|
|
LastAttemptVersion,
|
|
LastAttemptStatus,
|
|
AbortReason,
|
|
Progress
|
|
);
|
|
TopSwapControl (FALSE);
|
|
} else {
|
|
ASSERT(FALSE);
|
|
Status = EFI_INVALID_PARAMETER;
|
|
DEBUG ((DEBUG_INFO, "[%a] Invalid capsule type!", __FUNCTION__));
|
|
}
|
|
|
|
SetUpdateProgress (UpdatinguCode, 90);
|
|
Progress(90);
|
|
DEBUG ((DEBUG_INFO, "Microcode update status 0x%x\n", Status));
|
|
|
|
EXIT:
|
|
|
|
if (Entry != NULL) {
|
|
FreePool(Entry);
|
|
}
|
|
|
|
if (AlignedImage != NULL) {
|
|
FreePool(AlignedImage);
|
|
}
|
|
|
|
ClearUpdateProgress ();
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Initialize FIT microcode information.
|
|
|
|
@param[in] MicrocodeFmpPrivate private data structure to be initialized.
|
|
|
|
@return EFI_SUCCESS FIT microcode information is initialized.
|
|
@return EFI_OUT_OF_RESOURCES No enough resource for the initialization.
|
|
@return EFI_DEVICE_ERROR There is something wrong in FIT microcode entry.
|
|
**/
|
|
EFI_STATUS
|
|
InitializeFitMicrocodeInfo (
|
|
IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate
|
|
)
|
|
{
|
|
UINT64 FitPointer;
|
|
FIRMWARE_INTERFACE_TABLE_ENTRY *FitEntry;
|
|
UINT32 EntryNum;
|
|
UINT32 MicrocodeEntryNum;
|
|
UINT32 Index;
|
|
UINTN Address;
|
|
VOID *MicrocodePatchAddress;
|
|
UINTN MicrocodePatchRegionSize;
|
|
FIT_MICROCODE_INFO *FitMicrocodeInfo;
|
|
FIT_MICROCODE_INFO *FitMicrocodeInfoNext;
|
|
CPU_MICROCODE_HEADER *MicrocodeEntryPoint;
|
|
CPU_MICROCODE_HEADER *MicrocodeEntryPointNext;
|
|
UINTN FitMicrocodeIndex;
|
|
MICROCODE_INFO *MicrocodeInfo;
|
|
UINTN MicrocodeIndex;
|
|
|
|
if (MicrocodeFmpPrivate->FitMicrocodeInfo != NULL) {
|
|
FreePool (MicrocodeFmpPrivate->FitMicrocodeInfo);
|
|
MicrocodeFmpPrivate->FitMicrocodeInfo = NULL;
|
|
MicrocodeFmpPrivate->FitMicrocodeEntryCount = 0;
|
|
}
|
|
|
|
FitPointer = *(UINT64 *) (UINTN) FIT_POINTER_ADDRESS;
|
|
if ((FitPointer == 0) ||
|
|
(FitPointer == 0xFFFFFFFFFFFFFFFF) ||
|
|
(FitPointer == 0xEEEEEEEEEEEEEEEE)) {
|
|
//
|
|
// No FIT table.
|
|
//
|
|
return EFI_SUCCESS;
|
|
}
|
|
FitEntry = (FIRMWARE_INTERFACE_TABLE_ENTRY *) (UINTN) FitPointer;
|
|
if ((FitEntry[0].Type != FIT_TYPE_00_HEADER) ||
|
|
(FitEntry[0].Address != FIT_TYPE_00_SIGNATURE)) {
|
|
//
|
|
// Invalid FIT table, treat it as no FIT table.
|
|
//
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EntryNum = *(UINT32 *)(&FitEntry[0].Size[0]) & 0xFFFFFF;
|
|
|
|
//
|
|
// Calculate microcode entry number.
|
|
//
|
|
MicrocodeEntryNum = 0;
|
|
for (Index = 0; Index < EntryNum; Index++) {
|
|
if (FitEntry[Index].Type == FIT_TYPE_01_MICROCODE) {
|
|
MicrocodeEntryNum++;
|
|
}
|
|
}
|
|
if (MicrocodeEntryNum == 0) {
|
|
//
|
|
// No FIT microcode entry.
|
|
//
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Allocate buffer.
|
|
//
|
|
MicrocodeFmpPrivate->FitMicrocodeInfo = AllocateZeroPool (MicrocodeEntryNum * sizeof (FIT_MICROCODE_INFO));
|
|
if (MicrocodeFmpPrivate->FitMicrocodeInfo == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
MicrocodeFmpPrivate->FitMicrocodeEntryCount = MicrocodeEntryNum;
|
|
|
|
MicrocodePatchAddress = MicrocodeFmpPrivate->MicrocodePatchAddress;
|
|
MicrocodePatchRegionSize = MicrocodeFmpPrivate->MicrocodePatchRegionSize;
|
|
|
|
//
|
|
// Collect microcode entry info.
|
|
//
|
|
MicrocodeEntryNum = 0;
|
|
for (Index = 0; Index < EntryNum; Index++) {
|
|
if (FitEntry[Index].Type == FIT_TYPE_01_MICROCODE) {
|
|
Address = (UINTN) FitEntry[Index].Address;
|
|
if ((Address < (UINTN) MicrocodePatchAddress) ||
|
|
(Address >= ((UINTN) MicrocodePatchAddress + MicrocodePatchRegionSize))) {
|
|
DEBUG ((
|
|
DEBUG_ERROR,
|
|
"InitializeFitMicrocodeInfo - Address (0x%x) is not in Microcode Region\n",
|
|
Address
|
|
));
|
|
goto ErrorExit;
|
|
}
|
|
FitMicrocodeInfo = &MicrocodeFmpPrivate->FitMicrocodeInfo[MicrocodeEntryNum];
|
|
FitMicrocodeInfo->MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) Address;
|
|
if ((*(UINT32 *) Address) == 0xFFFFFFFF) {
|
|
//
|
|
// It is the empty slot as long as the first dword is 0xFFFF_FFFF.
|
|
//
|
|
FitMicrocodeInfo->Empty = TRUE;
|
|
} else {
|
|
FitMicrocodeInfo->Empty = FALSE;
|
|
}
|
|
MicrocodeEntryNum++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Every microcode should have a FIT microcode entry.
|
|
//
|
|
for (MicrocodeIndex = 0; MicrocodeIndex < MicrocodeFmpPrivate->DescriptorCount; MicrocodeIndex++) {
|
|
MicrocodeInfo = &MicrocodeFmpPrivate->MicrocodeInfo[MicrocodeIndex];
|
|
for (FitMicrocodeIndex = 0; FitMicrocodeIndex < MicrocodeFmpPrivate->FitMicrocodeEntryCount; FitMicrocodeIndex++) {
|
|
FitMicrocodeInfo = &MicrocodeFmpPrivate->FitMicrocodeInfo[FitMicrocodeIndex];
|
|
if (MicrocodeInfo->MicrocodeEntryPoint == FitMicrocodeInfo->MicrocodeEntryPoint) {
|
|
FitMicrocodeInfo->InUse = MicrocodeInfo->InUse;
|
|
break;
|
|
}
|
|
}
|
|
if (FitMicrocodeIndex >= MicrocodeFmpPrivate->FitMicrocodeEntryCount) {
|
|
DEBUG ((
|
|
DEBUG_ERROR,
|
|
"InitializeFitMicrocodeInfo - There is no FIT microcode entry for Microcode (0x%x). It could be caused by incorrect uCode\n",
|
|
MicrocodeInfo->MicrocodeEntryPoint
|
|
));
|
|
ASSERT(FALSE);
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
|
|
SortFitMicrocodeInfo (MicrocodeFmpPrivate);
|
|
|
|
//
|
|
// Check overlap.
|
|
//
|
|
for (FitMicrocodeIndex = 0; FitMicrocodeIndex < MicrocodeFmpPrivate->FitMicrocodeEntryCount - 1; FitMicrocodeIndex++) {
|
|
FitMicrocodeInfo = &MicrocodeFmpPrivate->FitMicrocodeInfo[FitMicrocodeIndex];
|
|
MicrocodeEntryPoint = FitMicrocodeInfo->MicrocodeEntryPoint;
|
|
FitMicrocodeInfoNext = &MicrocodeFmpPrivate->FitMicrocodeInfo[FitMicrocodeIndex + 1];
|
|
MicrocodeEntryPointNext = FitMicrocodeInfoNext->MicrocodeEntryPoint;
|
|
if ((MicrocodeEntryPoint >= MicrocodeEntryPointNext) ||
|
|
((FitMicrocodeInfo->TotalSize != 0) &&
|
|
((UINTN) MicrocodeEntryPoint + FitMicrocodeInfo->TotalSize) >
|
|
(UINTN) MicrocodeEntryPointNext)) {
|
|
DEBUG ((
|
|
DEBUG_ERROR,
|
|
"InitializeFitMicrocodeInfo - There is overlap between FIT microcode entries (0x%x 0x%x)\n",
|
|
MicrocodeEntryPoint,
|
|
MicrocodeEntryPointNext
|
|
));
|
|
goto ErrorExit;
|
|
}
|
|
|
|
FitMicrocodeInfo->TotalSize = (UINTN)MicrocodeEntryPointNext - (UINTN)MicrocodeEntryPoint;
|
|
}
|
|
//
|
|
// Handle last Fit entry totalsize
|
|
//
|
|
FitMicrocodeInfo = &MicrocodeFmpPrivate->FitMicrocodeInfo[FitMicrocodeIndex];
|
|
FitMicrocodeInfo->TotalSize = (UINTN)MicrocodeFmpPrivate->MicrocodePatchAddress + MicrocodeFmpPrivate->MicrocodePatchRegionSize - (UINTN)FitMicrocodeInfo->MicrocodeEntryPoint;
|
|
|
|
DEBUG_CODE(
|
|
for (FitMicrocodeIndex = 0; FitMicrocodeIndex < MicrocodeFmpPrivate->FitMicrocodeEntryCount; FitMicrocodeIndex++) {
|
|
FitMicrocodeInfo = &MicrocodeFmpPrivate->FitMicrocodeInfo[FitMicrocodeIndex];
|
|
MicrocodeEntryPoint = FitMicrocodeInfo->MicrocodeEntryPoint;
|
|
|
|
DEBUG ((
|
|
DEBUG_ERROR,
|
|
"InitializeFitMicrocodeInfo - FIT microcode entries [0x%x 0x%x)\n",
|
|
MicrocodeEntryPoint,
|
|
(UINT8 *)MicrocodeEntryPoint + FitMicrocodeInfo->TotalSize
|
|
));
|
|
}
|
|
);
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
ErrorExit:
|
|
|
|
FreePool (MicrocodeFmpPrivate->FitMicrocodeInfo);
|
|
MicrocodeFmpPrivate->FitMicrocodeEntryCount = 0;
|
|
MicrocodeFmpPrivate->FitMicrocodeInfo = NULL;
|
|
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
/**
|
|
Initialize Processor Microcode Index.
|
|
|
|
@param[in] MicrocodeFmpPrivate private data structure to be initialized.
|
|
**/
|
|
VOID
|
|
InitializedProcessorMicrocodeIndex (
|
|
IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate
|
|
)
|
|
{
|
|
UINTN CpuIndex;
|
|
UINTN MicrocodeIndex;
|
|
UINT32 AttemptStatus;
|
|
EFI_STATUS Status;
|
|
MICROCODE_INFO *MicrocodeInfo;
|
|
PROCESSOR_INFO *ProcessorInfo;
|
|
|
|
for (CpuIndex = 0; CpuIndex < MicrocodeFmpPrivate->ProcessorCount; CpuIndex++) {
|
|
ProcessorInfo = &MicrocodeFmpPrivate->ProcessorInfo[CpuIndex];
|
|
if (ProcessorInfo->MicrocodeIndex != (UINTN)-1) {
|
|
continue;
|
|
}
|
|
|
|
for (MicrocodeIndex = 0; MicrocodeIndex < MicrocodeFmpPrivate->DescriptorCount; MicrocodeIndex++) {
|
|
MicrocodeInfo = &MicrocodeFmpPrivate->MicrocodeInfo[MicrocodeIndex];
|
|
if (!MicrocodeInfo->InUse) {
|
|
continue;
|
|
}
|
|
|
|
Status = VerifyMicrocode(
|
|
MicrocodeFmpPrivate,
|
|
MicrocodeInfo->MicrocodeEntryPoint, //FvMicrocodePatch 0X85000+0x1000 //0xffc51000
|
|
MicrocodeInfo->TotalSize, //FvMicrocodeSize - 0x1000 //0x1bc000
|
|
FALSE,
|
|
&AttemptStatus,
|
|
NULL,
|
|
&CpuIndex
|
|
);
|
|
DEBUG ((DEBUG_INFO, "InitializedProcessorMicrocodeIndex Microcode %x verify status %x, CPU Index %x\n", MicrocodeIndex, Status, CpuIndex));
|
|
if (!EFI_ERROR(Status)) {
|
|
ProcessorInfo->MicrocodeIndex = MicrocodeIndex;
|
|
//
|
|
// Update logic CPU Microcode Revision with highest uCode revision on flash. As MircocodeVersion can't be read from logic CPU defined by uCode Selection Policy PCD
|
|
// This logic complies with Physical CPU uCode patching logic
|
|
//
|
|
if (!ProcessorInfo->IsRealCpu) {
|
|
if ((ProcessorInfo->MicrocodeRevision == (UINT32) -1) ||
|
|
(ProcessorInfo->MicrocodeRevision < MicrocodeInfo->MicrocodeEntryPoint->UpdateRevision)) {
|
|
ProcessorInfo->MicrocodeRevision = MicrocodeInfo->MicrocodeEntryPoint->UpdateRevision;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Initialize Microcode Descriptor.
|
|
|
|
@param[in] MicrocodeFmpPrivate private data structure to be initialized.
|
|
|
|
@return EFI_SUCCESS Microcode Descriptor is initialized.
|
|
@return EFI_OUT_OF_RESOURCES No enough resource for the initialization.
|
|
**/
|
|
EFI_STATUS
|
|
InitializeMicrocodeDescriptor (
|
|
IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT8 CurrentMicrocodeCount;
|
|
|
|
CurrentMicrocodeCount = (UINT8)GetMicrocodeInfo (MicrocodeFmpPrivate, 0, NULL, NULL);
|
|
|
|
if (CurrentMicrocodeCount > MicrocodeFmpPrivate->DescriptorCount) {
|
|
if (MicrocodeFmpPrivate->ImageDescriptor != NULL) {
|
|
FreePool(MicrocodeFmpPrivate->ImageDescriptor);
|
|
MicrocodeFmpPrivate->ImageDescriptor = NULL;
|
|
}
|
|
if (MicrocodeFmpPrivate->MicrocodeInfo != NULL) {
|
|
FreePool(MicrocodeFmpPrivate->MicrocodeInfo);
|
|
MicrocodeFmpPrivate->MicrocodeInfo = NULL;
|
|
}
|
|
} else {
|
|
ZeroMem(MicrocodeFmpPrivate->ImageDescriptor, MicrocodeFmpPrivate->DescriptorCount * sizeof(EFI_FIRMWARE_IMAGE_DESCRIPTOR));
|
|
ZeroMem(MicrocodeFmpPrivate->MicrocodeInfo, MicrocodeFmpPrivate->DescriptorCount * sizeof(MICROCODE_INFO));
|
|
}
|
|
|
|
MicrocodeFmpPrivate->DescriptorCount = CurrentMicrocodeCount;
|
|
|
|
if (MicrocodeFmpPrivate->ImageDescriptor == NULL) {
|
|
MicrocodeFmpPrivate->ImageDescriptor = AllocateZeroPool(MicrocodeFmpPrivate->DescriptorCount * sizeof(EFI_FIRMWARE_IMAGE_DESCRIPTOR));
|
|
if (MicrocodeFmpPrivate->ImageDescriptor == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
}
|
|
if (MicrocodeFmpPrivate->MicrocodeInfo == NULL) {
|
|
MicrocodeFmpPrivate->MicrocodeInfo = AllocateZeroPool(MicrocodeFmpPrivate->DescriptorCount * sizeof(MICROCODE_INFO));
|
|
if (MicrocodeFmpPrivate->MicrocodeInfo == NULL) {
|
|
FreePool (MicrocodeFmpPrivate->ImageDescriptor);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
}
|
|
|
|
CurrentMicrocodeCount = (UINT8)GetMicrocodeInfo (MicrocodeFmpPrivate, MicrocodeFmpPrivate->DescriptorCount, MicrocodeFmpPrivate->ImageDescriptor, MicrocodeFmpPrivate->MicrocodeInfo);
|
|
ASSERT(CurrentMicrocodeCount == MicrocodeFmpPrivate->DescriptorCount);
|
|
|
|
InitializedProcessorMicrocodeIndex (MicrocodeFmpPrivate);
|
|
|
|
Status = InitializeFitMicrocodeInfo (MicrocodeFmpPrivate);
|
|
if (EFI_ERROR(Status)) {
|
|
//
|
|
// Error handling Fit Microcode init failure.
|
|
// Switch to non-fit update mode and don't return failure
|
|
//
|
|
// FreePool (MicrocodeFmpPrivate->ImageDescriptor);
|
|
// FreePool (MicrocodeFmpPrivate->MicrocodeInfo);
|
|
DEBUG ((DEBUG_ERROR, "InitializeFitMicrocodeInfo - %r\n", Status));
|
|
DEBUG ((DEBUG_ERROR, "Force switch to Non-FIT mode\n", Status));
|
|
|
|
MicrocodeFmpPrivate->FitMicrocodeEntryCount = 0;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Initialize MicrocodeFmpDriver multiprocessor information.
|
|
|
|
@param[in] MicrocodeFmpPrivate private data structure to be initialized.
|
|
|
|
@return EFI_SUCCESS Processor information is initialized.
|
|
@return EFI_OUT_OF_RESOURCES No enough resource for the initialization.
|
|
**/
|
|
EFI_STATUS
|
|
InitializeProcessorInfo (
|
|
IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_MP_SERVICES_PROTOCOL *MpService;
|
|
UINTN NumberOfPhysicalProcessors;
|
|
UINTN NumberOfEnabledProcessors;
|
|
UINTN Index;
|
|
UINTN CpuIndex;
|
|
UINTN BspIndex;
|
|
MICROCODE_SELECTION_POLICY *MicrocodePolicy;
|
|
UINTN PolicySize;
|
|
UINTN PolicyNum;
|
|
|
|
Status = gBS->LocateProtocol (&gEfiMpServiceProtocolGuid, NULL, (VOID **)&MpService);
|
|
ASSERT_EFI_ERROR(Status);
|
|
|
|
MicrocodeFmpPrivate->MpService = MpService;
|
|
MicrocodeFmpPrivate->ProcessorCount = 0;
|
|
MicrocodeFmpPrivate->ProcessorInfo = NULL;
|
|
|
|
Status = MpService->GetNumberOfProcessors (MpService, &NumberOfPhysicalProcessors, &NumberOfEnabledProcessors);
|
|
ASSERT_EFI_ERROR(Status);
|
|
MicrocodeFmpPrivate->ProcessorCount = NumberOfPhysicalProcessors; //8
|
|
|
|
Status = MpService->WhoAmI (MpService, &BspIndex);
|
|
ASSERT_EFI_ERROR(Status);
|
|
MicrocodeFmpPrivate->BspIndex = BspIndex;
|
|
|
|
//
|
|
// Collect PolicyProcessInfo from uCode selection PolicyPcd
|
|
//
|
|
MicrocodePolicy = (MICROCODE_SELECTION_POLICY *) PcdGetPtr(PcduCodeSelectionPolicy);
|
|
PolicySize = PcdGetSize(PcduCodeSelectionPolicy);
|
|
|
|
if ((PolicySize & 0x7) == 0) {
|
|
PolicyNum = (PolicySize >> 3);
|
|
} else {
|
|
DEBUG ((DEBUG_INFO, "PcduCodeSelectionPolicy size %x is not mutiple of 8. Treat it as empty\n", PolicySize));
|
|
PolicyNum = 0;
|
|
}
|
|
DEBUG ((DEBUG_INFO, "PcduCodeSelectionPolicy PolicyNum %x\n", PolicyNum));
|
|
|
|
MicrocodeFmpPrivate->ProcessorInfo = AllocateZeroPool (sizeof(PROCESSOR_INFO) * (NumberOfPhysicalProcessors + PolicyNum));
|
|
if (MicrocodeFmpPrivate->ProcessorInfo == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
for (CpuIndex = 0; CpuIndex < NumberOfPhysicalProcessors; CpuIndex++) {
|
|
MicrocodeFmpPrivate->ProcessorInfo[CpuIndex].CpuIndex = CpuIndex;
|
|
MicrocodeFmpPrivate->ProcessorInfo[CpuIndex].MicrocodeIndex = (UINTN)-1;
|
|
MicrocodeFmpPrivate->ProcessorInfo[CpuIndex].IsRealCpu = TRUE;
|
|
if (CpuIndex == BspIndex) {
|
|
CollectProcessorInfo (&MicrocodeFmpPrivate->ProcessorInfo[CpuIndex]);
|
|
} else {
|
|
Status = MpService->StartupThisAP (
|
|
MpService,
|
|
CollectProcessorInfo,
|
|
CpuIndex,
|
|
NULL,
|
|
0,
|
|
&MicrocodeFmpPrivate->ProcessorInfo[CpuIndex],
|
|
NULL
|
|
);
|
|
ASSERT_EFI_ERROR(Status);
|
|
}
|
|
}
|
|
|
|
//
|
|
// If uCode Selection Policy is not empty.
|
|
// 1) Validate current CPU setting with Selection Policy
|
|
// 2) Add CPU in Policy that isn't current CPU on board as Logic CPU (IsRealCpu = FALSE)
|
|
//
|
|
if (PolicyNum > 0) { //PolicyNum = 0
|
|
//
|
|
// Check if there is mis-configuration of PcduCodeSelectionPolicy that doesn't include current physical CPU on the platform
|
|
//
|
|
DEBUG ((DEBUG_INFO, "PolicyNum = 0x%08X\n", PolicyNum));
|
|
for (CpuIndex = 0; CpuIndex < NumberOfPhysicalProcessors; CpuIndex++) {
|
|
for (Index = 0; Index < PolicyNum; Index++) {
|
|
DEBUG ((DEBUG_INFO, "MicrocodeFmpPrivate->ProcessorInfo[%d].ProcessorSignature 0x%08X\n", CpuIndex, MicrocodeFmpPrivate->ProcessorInfo[CpuIndex].ProcessorSignature));
|
|
DEBUG ((DEBUG_INFO, "MicrocodePolicy[%d].ProcessorSignature 0x%08X\n", Index, MicrocodePolicy[Index].ProcessorSignature));
|
|
DEBUG ((DEBUG_INFO, "MicrocodeFmpPrivate->ProcessorInfo[%d].ProcessorFlags 0x%08X\n", CpuIndex, MicrocodeFmpPrivate->ProcessorInfo[CpuIndex].ProcessorFlags));
|
|
DEBUG ((DEBUG_INFO, "MicrocodePolicy[%d].ProcessorFlags 0x%08X\n", Index, MicrocodePolicy[Index].ProcessorFlags));
|
|
if ((MicrocodeFmpPrivate->ProcessorInfo[CpuIndex].ProcessorSignature == MicrocodePolicy[Index].ProcessorSignature) &&
|
|
(MicrocodeFmpPrivate->ProcessorInfo[CpuIndex].ProcessorFlags == MicrocodePolicy[Index].ProcessorFlags)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// if (Index == PolicyNum) {
|
|
// DEBUG ((DEBUG_ERROR, "Current CPU on Platform is not found in PcduCodeSelectionPolicy\n"));
|
|
// DEBUG ((DEBUG_ERROR, "ProcessorSignature %x\n", MicrocodeFmpPrivate->ProcessorInfo[CpuIndex].ProcessorSignature));
|
|
// DEBUG ((DEBUG_ERROR, "ProcessorFlags %x\n", MicrocodeFmpPrivate->ProcessorInfo[CpuIndex].ProcessorFlags));
|
|
|
|
// ASSERT(FALSE);
|
|
// return EFI_INVALID_PARAMETER;
|
|
// }
|
|
}
|
|
|
|
//
|
|
// Add Logic CPU that is defined in PcduCodeSelectionPolicy which is not current physical CPU on the platform
|
|
//
|
|
for (Index = 0; Index < PolicyNum; Index++) {
|
|
for (CpuIndex = 0; CpuIndex < NumberOfPhysicalProcessors; CpuIndex++) {
|
|
if ((MicrocodeFmpPrivate->ProcessorInfo[CpuIndex].ProcessorSignature == MicrocodePolicy[Index].ProcessorSignature) &&
|
|
(MicrocodeFmpPrivate->ProcessorInfo[CpuIndex].ProcessorFlags == MicrocodePolicy[Index].ProcessorFlags)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If CPU identified in PcduCodeSelectionPolicy is not present on this platform. Mark it to be a logic CPU
|
|
//
|
|
if (CpuIndex == NumberOfPhysicalProcessors) {
|
|
MicrocodeFmpPrivate->ProcessorInfo[MicrocodeFmpPrivate->ProcessorCount].CpuIndex = MicrocodeFmpPrivate->ProcessorCount;
|
|
MicrocodeFmpPrivate->ProcessorInfo[MicrocodeFmpPrivate->ProcessorCount].ProcessorSignature = MicrocodePolicy[Index].ProcessorSignature;
|
|
MicrocodeFmpPrivate->ProcessorInfo[MicrocodeFmpPrivate->ProcessorCount].ProcessorFlags = MicrocodePolicy[Index].ProcessorFlags;
|
|
MicrocodeFmpPrivate->ProcessorInfo[MicrocodeFmpPrivate->ProcessorCount].MicrocodeRevision = (UINT32)-1;
|
|
MicrocodeFmpPrivate->ProcessorInfo[MicrocodeFmpPrivate->ProcessorCount].MicrocodeIndex = (UINTN)-1;
|
|
MicrocodeFmpPrivate->ProcessorInfo[MicrocodeFmpPrivate->ProcessorCount].IsRealCpu = FALSE;
|
|
MicrocodeFmpPrivate->ProcessorCount++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Dump private information.
|
|
|
|
@param[in] MicrocodeFmpPrivate private data structure.
|
|
**/
|
|
VOID
|
|
DumpPrivateInfo (
|
|
IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate
|
|
)
|
|
{
|
|
UINTN Index;
|
|
PROCESSOR_INFO *ProcessorInfo;
|
|
MICROCODE_INFO *MicrocodeInfo;
|
|
EFI_FIRMWARE_IMAGE_DESCRIPTOR *ImageDescriptor;
|
|
FIT_MICROCODE_INFO *FitMicrocodeInfo;
|
|
|
|
DEBUG ((DEBUG_INFO, "ProcessorInfo:\n"));
|
|
DEBUG ((DEBUG_INFO, " ProcessorCount - 0x%x\n", MicrocodeFmpPrivate->ProcessorCount));
|
|
DEBUG ((DEBUG_INFO, " BspIndex - 0x%x\n", MicrocodeFmpPrivate->BspIndex));
|
|
|
|
ProcessorInfo = MicrocodeFmpPrivate->ProcessorInfo;
|
|
for (Index = 0; Index < MicrocodeFmpPrivate->ProcessorCount; Index++) {
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
" Existing ProcessorInfo[0x%x] %a - 0x%08x, 0x%02x, 0x%02x, 0x%08x, (0x%x)\n",
|
|
ProcessorInfo[Index].CpuIndex,
|
|
ProcessorInfo[Index].IsRealCpu ? "PhysicalCpu" : "LogicCpu",
|
|
ProcessorInfo[Index].ProcessorSignature,
|
|
ProcessorInfo[Index].PlatformId,
|
|
ProcessorInfo[Index].ProcessorFlags,
|
|
ProcessorInfo[Index].MicrocodeRevision,
|
|
ProcessorInfo[Index].MicrocodeIndex
|
|
));
|
|
}
|
|
|
|
DEBUG ((DEBUG_INFO, "MicrocodeInfo:\n"));
|
|
MicrocodeInfo = MicrocodeFmpPrivate->MicrocodeInfo;
|
|
DEBUG ((DEBUG_INFO, " MicrocodeRegion - 0x%x - 0x%x\n", MicrocodeFmpPrivate->MicrocodePatchAddress, MicrocodeFmpPrivate->MicrocodePatchRegionSize));
|
|
DEBUG ((DEBUG_INFO, " MicrocodeCount - 0x%x\n", MicrocodeFmpPrivate->DescriptorCount));
|
|
for (Index = 0; Index < MicrocodeFmpPrivate->DescriptorCount; Index++) {
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
" MicrocodeInfo[0x%x] - 0x%08x, 0x%08x, (0x%x)\n",
|
|
Index,
|
|
MicrocodeInfo[Index].MicrocodeEntryPoint,
|
|
MicrocodeInfo[Index].TotalSize,
|
|
MicrocodeInfo[Index].InUse
|
|
));
|
|
}
|
|
|
|
ImageDescriptor = MicrocodeFmpPrivate->ImageDescriptor;
|
|
DEBUG ((DEBUG_VERBOSE, "ImageDescriptor:\n"));
|
|
for (Index = 0; Index < MicrocodeFmpPrivate->DescriptorCount; Index++) {
|
|
DEBUG ((DEBUG_VERBOSE, " ImageDescriptor (%d)\n", Index));
|
|
DEBUG ((DEBUG_VERBOSE, " ImageIndex - 0x%x\n", ImageDescriptor[Index].ImageIndex));
|
|
DEBUG ((DEBUG_VERBOSE, " ImageTypeId - %g\n", &ImageDescriptor[Index].ImageTypeId));
|
|
DEBUG ((DEBUG_VERBOSE, " ImageId - 0x%lx\n", ImageDescriptor[Index].ImageId));
|
|
DEBUG ((DEBUG_VERBOSE, " ImageIdName - %s\n", ImageDescriptor[Index].ImageIdName));
|
|
DEBUG ((DEBUG_VERBOSE, " Version - 0x%x\n", ImageDescriptor[Index].Version));
|
|
DEBUG ((DEBUG_VERBOSE, " VersionName - %s\n", ImageDescriptor[Index].VersionName));
|
|
DEBUG ((DEBUG_VERBOSE, " Size - 0x%x\n", ImageDescriptor[Index].Size));
|
|
DEBUG ((DEBUG_VERBOSE, " AttributesSupported - 0x%lx\n", ImageDescriptor[Index].AttributesSupported));
|
|
DEBUG ((DEBUG_VERBOSE, " AttributesSetting - 0x%lx\n", ImageDescriptor[Index].AttributesSetting));
|
|
DEBUG ((DEBUG_VERBOSE, " Compatibilities - 0x%lx\n", ImageDescriptor[Index].Compatibilities));
|
|
DEBUG ((DEBUG_VERBOSE, " LowestSupportedImageVersion - 0x%x\n", ImageDescriptor[Index].LowestSupportedImageVersion));
|
|
DEBUG ((DEBUG_VERBOSE, " LastAttemptVersion - 0x%x\n", ImageDescriptor[Index].LastAttemptVersion));
|
|
DEBUG ((DEBUG_VERBOSE, " LastAttemptStatus - 0x%x\n", ImageDescriptor[Index].LastAttemptStatus));
|
|
DEBUG ((DEBUG_VERBOSE, " HardwareInstance - 0x%lx\n", ImageDescriptor[Index].HardwareInstance));
|
|
}
|
|
|
|
if (MicrocodeFmpPrivate->FitMicrocodeInfo != NULL) {
|
|
DEBUG ((DEBUG_INFO, "FitMicrocodeInfo:\n"));
|
|
FitMicrocodeInfo = MicrocodeFmpPrivate->FitMicrocodeInfo;
|
|
DEBUG ((DEBUG_INFO, " FitMicrocodeEntryCount - 0x%x\n", MicrocodeFmpPrivate->FitMicrocodeEntryCount));
|
|
for (Index = 0; Index < MicrocodeFmpPrivate->FitMicrocodeEntryCount; Index++) {
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
" FitMicrocodeInfo[0x%x] - 0x%08x, 0x%08x, (0x%x, 0x%x)\n",
|
|
Index,
|
|
FitMicrocodeInfo[Index].MicrocodeEntryPoint,
|
|
FitMicrocodeInfo[Index].TotalSize,
|
|
FitMicrocodeInfo[Index].InUse,
|
|
FitMicrocodeInfo[Index].Empty
|
|
));
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
Initialize MicrocodeFmpDriver private data structure.
|
|
|
|
@param[in] MicrocodeFmpPrivate private data structure to be initialized.
|
|
|
|
@return EFI_SUCCESS private data is initialized.
|
|
**/
|
|
EFI_STATUS
|
|
InitializeMicrocodePrivateData (
|
|
IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN VarSize;
|
|
BOOLEAN Result;
|
|
UINT32 AttemptVersion;
|
|
ESRT_STATUS AttemptStatus;
|
|
UINT64 OsIndications;
|
|
UINT64 Size;
|
|
|
|
MicrocodeFmpPrivate->Signature = MICROCODE_FMP_PRIVATE_DATA_SIGNATURE;
|
|
|
|
MicrocodeFmpPrivate->PackageVersion = 0x1;
|
|
MicrocodeFmpPrivate->PackageVersionName = L"Microcode";
|
|
|
|
VarSize = sizeof(UINT32);
|
|
Status = gRT->GetVariable (
|
|
ESRT_LAST_ATTEMPT_VERSION,
|
|
PcdGetPtr (PcdWindowsUcodeFirmwareCapsuleGuid),
|
|
NULL,
|
|
&VarSize,
|
|
&AttemptVersion
|
|
);
|
|
|
|
if (!EFI_ERROR(Status)) {
|
|
MicrocodeFmpPrivate->LastAttempt.LastAttemptVersion = AttemptVersion;
|
|
}
|
|
VarSize = sizeof(ESRT_STATUS);
|
|
Status = gRT->GetVariable (
|
|
ESRT_LAST_ATTEMPT_STATUS,
|
|
PcdGetPtr (PcdWindowsUcodeFirmwareCapsuleGuid),
|
|
NULL,
|
|
&VarSize,
|
|
&AttemptStatus
|
|
);
|
|
if (!EFI_ERROR(Status)) {
|
|
MicrocodeFmpPrivate->LastAttempt.LastAttemptStatus = AttemptStatus;
|
|
}
|
|
|
|
DEBUG ((DEBUG_INFO, "GetLastAttempt Version - 0x%x, State - 0x%x\n", MicrocodeFmpPrivate->LastAttempt.LastAttemptVersion, MicrocodeFmpPrivate->LastAttempt.LastAttemptStatus));
|
|
//FvMicodecode
|
|
Result = GetMicrocodeRegion(&MicrocodeFmpPrivate->MicrocodePatchAddress, &MicrocodeFmpPrivate->MicrocodePatchRegionSize);
|
|
if (!Result) {
|
|
DEBUG ((DEBUG_ERROR, "Fail to get Microcode Region\n"));
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
//Onboard micricodeversion.data
|
|
Status = GetMicrocodeRegionVersionInfo((UINT8 **)&MicrocodeFmpPrivate->VersionInfo, &MicrocodeFmpPrivate->VersionInfoSize);
|
|
if (EFI_ERROR(Status)) {
|
|
ASSERT(MicrocodeFmpPrivate->VersionInfo != NULL);
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
DEBUG ((DEBUG_INFO, "Current Microcode Region Version : %x\n", MicrocodeFmpPrivate->VersionInfo->Version));
|
|
DEBUG ((DEBUG_INFO, " Lowest Support Version : %x\n", MicrocodeFmpPrivate->VersionInfo->LowestSupportedVersion));
|
|
DEBUG ((DEBUG_INFO, " Version Info String : %s\n", MicrocodeFmpPrivate->VersionInfo->VersionString));
|
|
|
|
//
|
|
// Init Processor info. Processor Info comes from 2 sources
|
|
// 1. uCode Selection Policy PCD
|
|
// 2. Existing CPU onboard
|
|
//
|
|
Size = sizeof(UINT64);
|
|
OsIndications = 0;
|
|
Status = gRT->GetVariable (
|
|
L"OsIndications",
|
|
&gEfiGlobalVariableGuid,
|
|
NULL,
|
|
&Size,
|
|
&OsIndications
|
|
);
|
|
|
|
if ((OsIndications & (EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED))) {
|
|
|
|
Status = InitializeProcessorInfo (MicrocodeFmpPrivate);
|
|
if (EFI_ERROR(Status)) {
|
|
DEBUG ((DEBUG_ERROR, "InitializeProcessorInfo - %r\n", Status));
|
|
return Status;
|
|
}
|
|
|
|
Status = InitializeMicrocodeDescriptor(MicrocodeFmpPrivate);
|
|
if (EFI_ERROR(Status)) {
|
|
FreePool (MicrocodeFmpPrivate->ProcessorInfo);
|
|
DEBUG ((DEBUG_ERROR, "InitializeMicrocodeDescriptor - %r\n", Status));
|
|
return Status;
|
|
}
|
|
} else {
|
|
Status = EFI_SUCCESS;
|
|
}
|
|
DumpPrivateInfo (MicrocodeFmpPrivate);
|
|
|
|
return Status;
|
|
}
|