summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJiewen Yao <jiewen.yao@intel.com>2016-09-21 11:15:03 +0800
committerJiewen Yao <jiewen.yao@intel.com>2016-11-08 22:43:22 +0800
commit882668595c32e9e0adeaaf0bd0062f88b464fc18 (patch)
treedb62aa420a2633eb6d34d0f41b969965a9a6d6d9
parentc7829aa0e90c19241f9d08e22b14874837de487b (diff)
downloadedk2-882668595c32e9e0adeaaf0bd0062f88b464fc18.zip
edk2-882668595c32e9e0adeaaf0bd0062f88b464fc18.tar.gz
edk2-882668595c32e9e0adeaaf0bd0062f88b464fc18.tar.bz2
UefiCpuPkg/MicrocodeUpdate: Add MicrocodeUpdate component.
MicrocodeUpdate supports update Microcode region via UEFI FMP capsule. MicrocodeUpdate SetImage() will perform the Microcode version, ProcessorSignature/ProcessorFlag, and try to load microcode. If and only if the Microcode is loaded successfully, and new Microcode will be updated to system flash region. Cc: Jeff Fan <jeff.fan@intel.com> Cc: Feng Tian <feng.tian@intel.com> Cc: Star Zeng <star.zeng@intel.com> Cc: Michael D Kinney <michael.d.kinney@intel.com> Cc: Liming Gao <liming.gao@intel.com> Cc: Chao Zhang <chao.b.zhang@intel.com> Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Jiewen Yao <jiewen.yao@intel.com> Reviewed-by: Jeff Fan <jeff.fan@intel.com> Reviewed-by: Michael Kinney <michael.d.kinney@intel.com>
-rw-r--r--UefiCpuPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeFmp.c537
-rw-r--r--UefiCpuPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeUpdate.c784
-rw-r--r--UefiCpuPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeUpdate.h403
-rw-r--r--UefiCpuPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeUpdateDxe.inf68
-rw-r--r--UefiCpuPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeUpdateDxe.uni21
-rw-r--r--UefiCpuPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeUpdateDxeExtra.uni20
6 files changed, 1833 insertions, 0 deletions
diff --git a/UefiCpuPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeFmp.c b/UefiCpuPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeFmp.c
new file mode 100644
index 0000000..df3563d
--- /dev/null
+++ b/UefiCpuPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeFmp.c
@@ -0,0 +1,537 @@
+/** @file
+ Produce FMP instance for Microcode.
+
+ Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "MicrocodeUpdate.h"
+
+//
+// MicrocodeFmp driver private data
+//
+MICROCODE_FMP_PRIVATE_DATA *mMicrocodeFmpPrivate = NULL;
+
+EFI_FIRMWARE_MANAGEMENT_PROTOCOL mFirmwareManagementProtocol = {
+ FmpGetImageInfo,
+ FmpGetImage,
+ FmpSetImage,
+ FmpCheckImage,
+ FmpGetPackageInfo,
+ FmpSetPackageInfo
+};
+
+/**
+ Initialize Microcode Descriptor.
+
+ @param[in] MicrocodeFmpPrivate private data structure to be initialized.
+
+ @return EFI_SUCCESS Microcode Descriptor is initialized.
+**/
+EFI_STATUS
+InitializeMicrocodeDescriptor (
+ IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate
+ );
+
+/**
+ Returns information about the current firmware image(s) of the device.
+
+ This function allows a copy of the current firmware image to be created and saved.
+ The saved copy could later been used, for example, in firmware image recovery or rollback.
+
+ @param[in] This A pointer to the EFI_FIRMWARE_MANAGEMENT_PROTOCOL instance.
+ @param[in, out] ImageInfoSize A pointer to the size, in bytes, of the ImageInfo buffer.
+ On input, this is the size of the buffer allocated by the caller.
+ On output, it is the size of the buffer returned by the firmware
+ if the buffer was large enough, or the size of the buffer needed
+ to contain the image(s) information if the buffer was too small.
+ @param[in, out] ImageInfo A pointer to the buffer in which firmware places the current image(s)
+ information. The information is an array of EFI_FIRMWARE_IMAGE_DESCRIPTORs.
+ @param[out] DescriptorVersion A pointer to the location in which firmware returns the version number
+ associated with the EFI_FIRMWARE_IMAGE_DESCRIPTOR.
+ @param[out] DescriptorCount A pointer to the location in which firmware returns the number of
+ descriptors or firmware images within this device.
+ @param[out] DescriptorSize A pointer to the location in which firmware returns the size, in bytes,
+ of an individual EFI_FIRMWARE_IMAGE_DESCRIPTOR.
+ @param[out] PackageVersion A version number that represents all the firmware images in the device.
+ The format is vendor specific and new version must have a greater value
+ than the old version. If PackageVersion is not supported, the value is
+ 0xFFFFFFFF. A value of 0xFFFFFFFE indicates that package version comparison
+ is to be performed using PackageVersionName. A value of 0xFFFFFFFD indicates
+ that package version update is in progress.
+ @param[out] PackageVersionName A pointer to a pointer to a null-terminated string representing the
+ package version name. 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 device was successfully updated with the new image.
+ @retval EFI_BUFFER_TOO_SMALL The ImageInfo buffer was too small. The current buffer size
+ needed to hold the image(s) information is returned in ImageInfoSize.
+ @retval EFI_INVALID_PARAMETER ImageInfoSize is NULL.
+ @retval EFI_DEVICE_ERROR Valid information could not be returned. Possible corrupted image.
+
+**/
+EFI_STATUS
+EFIAPI
+FmpGetImageInfo (
+ IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This,
+ IN OUT UINTN *ImageInfoSize,
+ IN OUT EFI_FIRMWARE_IMAGE_DESCRIPTOR *ImageInfo,
+ OUT UINT32 *DescriptorVersion,
+ OUT UINT8 *DescriptorCount,
+ OUT UINTN *DescriptorSize,
+ OUT UINT32 *PackageVersion,
+ OUT CHAR16 **PackageVersionName
+ )
+{
+ MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate;
+ UINTN Index;
+
+ MicrocodeFmpPrivate = MICROCODE_FMP_PRIVATE_DATA_FROM_FMP(This);
+
+ if(ImageInfoSize == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (*ImageInfoSize < sizeof(EFI_FIRMWARE_IMAGE_DESCRIPTOR) * MicrocodeFmpPrivate->DescriptorCount) {
+ *ImageInfoSize = sizeof(EFI_FIRMWARE_IMAGE_DESCRIPTOR) * MicrocodeFmpPrivate->DescriptorCount;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ if (ImageInfo == NULL ||
+ DescriptorVersion == NULL ||
+ DescriptorCount == NULL ||
+ DescriptorSize == NULL ||
+ PackageVersion == NULL ||
+ PackageVersionName == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *ImageInfoSize = sizeof(EFI_FIRMWARE_IMAGE_DESCRIPTOR) * MicrocodeFmpPrivate->DescriptorCount;
+ *DescriptorSize = sizeof(EFI_FIRMWARE_IMAGE_DESCRIPTOR);
+ *DescriptorCount = MicrocodeFmpPrivate->DescriptorCount;
+ *DescriptorVersion = EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION;
+
+ //
+ // supports 1 ImageInfo descriptor
+ //
+ CopyMem(&ImageInfo[0], MicrocodeFmpPrivate->ImageDescriptor, sizeof(EFI_FIRMWARE_IMAGE_DESCRIPTOR) * MicrocodeFmpPrivate->DescriptorCount);
+ for (Index = 0; Index < MicrocodeFmpPrivate->DescriptorCount; Index++) {
+ if ((ImageInfo[Index].AttributesSetting & IMAGE_ATTRIBUTE_IN_USE) != 0) {
+ ImageInfo[Index].LastAttemptVersion = MicrocodeFmpPrivate->LastAttempt.LastAttemptVersion;
+ ImageInfo[Index].LastAttemptStatus = MicrocodeFmpPrivate->LastAttempt.LastAttemptStatus;
+ }
+ }
+
+ //
+ // package version
+ //
+ *PackageVersion = MicrocodeFmpPrivate->PackageVersion;
+ if (MicrocodeFmpPrivate->PackageVersionName != NULL) {
+ *PackageVersionName = AllocateCopyPool(StrSize(MicrocodeFmpPrivate->PackageVersionName), MicrocodeFmpPrivate->PackageVersionName);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Retrieves a copy of the current firmware image of the device.
+
+ This function allows a copy of the current firmware image to be created and saved.
+ The saved copy could later been used, for example, in firmware image recovery or rollback.
+
+ @param[in] This A pointer to the EFI_FIRMWARE_MANAGEMENT_PROTOCOL instance.
+ @param[in] ImageIndex A unique number identifying the firmware image(s) within the device.
+ The number is between 1 and DescriptorCount.
+ @param[in,out] Image Points to the buffer where the current image is copied to.
+ @param[in,out] ImageSize On entry, points to the size of the buffer pointed to by Image, in bytes.
+ On return, points to the length of the image, in bytes.
+
+ @retval EFI_SUCCESS The device was successfully updated with the new image.
+ @retval EFI_BUFFER_TOO_SMALL The buffer specified by ImageSize is too small to hold the
+ image. The current buffer size needed to hold the image is returned
+ in ImageSize.
+ @retval EFI_INVALID_PARAMETER The Image was NULL.
+ @retval EFI_NOT_FOUND The current image is not copied to the buffer.
+ @retval EFI_UNSUPPORTED The operation is not supported.
+ @retval EFI_SECURITY_VIOLATIO The operation could not be performed due to an authentication failure.
+
+**/
+EFI_STATUS
+EFIAPI
+FmpGetImage (
+ IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This,
+ IN UINT8 ImageIndex,
+ IN OUT VOID *Image,
+ IN OUT UINTN *ImageSize
+ )
+{
+ MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate;
+ EFI_STATUS Status;
+
+ if (Image == NULL || ImageSize == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ MicrocodeFmpPrivate = MICROCODE_FMP_PRIVATE_DATA_FROM_FMP(This);
+
+ if (ImageIndex == 0 || ImageIndex > MicrocodeFmpPrivate->DescriptorCount || ImageSize == NULL || Image == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = MicrocodeRead(ImageIndex, (VOID *)Image, ImageSize);
+ return Status;
+}
+
+/**
+ Updates the firmware image of the device.
+
+ This function updates the hardware with the new firmware image.
+ This function returns EFI_UNSUPPORTED if the firmware image is not updatable.
+ If the firmware image is updatable, the function should perform the following minimal validations
+ before proceeding to do the firmware image update.
+ - Validate the image authentication if image has attribute
+ IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED. The function returns
+ EFI_SECURITY_VIOLATION if the validation fails.
+ - Validate the image is a supported image for this device. The function returns EFI_ABORTED if
+ the image is unsupported. The function can optionally provide more detailed information on
+ why the image is not a supported image.
+ - Validate the data from VendorCode if not null. Image validation must be performed before
+ VendorCode data validation. VendorCode data is ignored or considered invalid if image
+ validation failed. The function returns EFI_ABORTED if the data is invalid.
+
+ VendorCode enables vendor to implement vendor-specific firmware image update policy. Null if
+ the caller did not specify the policy or use the default policy. As an example, vendor can implement
+ a policy to allow an option to force a firmware image update when the abort reason is due to the new
+ firmware image version is older than the current firmware image version or bad image checksum.
+ Sensitive operations such as those wiping the entire firmware image and render the device to be
+ non-functional should be encoded in the image itself rather than passed with the VendorCode.
+ AbortReason enables vendor to have the option to provide a more detailed description of the abort
+ reason to the caller.
+
+ @param[in] This A pointer to the EFI_FIRMWARE_MANAGEMENT_PROTOCOL instance.
+ @param[in] ImageIndex A unique number identifying the firmware image(s) within the device.
+ The number is between 1 and DescriptorCount.
+ @param[in] Image Points to the new image.
+ @param[in] ImageSize Size of the new image in bytes.
+ @param[in] VendorCode This enables vendor to implement vendor-specific firmware image update policy.
+ Null indicates the caller did not specify the policy or use the default policy.
+ @param[in] Progress A function used by the driver to report the progress of the firmware update.
+ @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 device was successfully updated with the new image.
+ @retval EFI_ABORTED The operation is aborted.
+ @retval EFI_INVALID_PARAMETER The Image was NULL.
+ @retval EFI_UNSUPPORTED The operation is not supported.
+ @retval EFI_SECURITY_VIOLATIO The operation could not be performed due to an authentication failure.
+
+**/
+EFI_STATUS
+EFIAPI
+FmpSetImage (
+ IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This,
+ IN UINT8 ImageIndex,
+ IN CONST VOID *Image,
+ IN UINTN ImageSize,
+ IN CONST VOID *VendorCode,
+ IN EFI_FIRMWARE_MANAGEMENT_UPDATE_IMAGE_PROGRESS Progress,
+ OUT CHAR16 **AbortReason
+ )
+{
+ EFI_STATUS Status;
+ EFI_STATUS VarStatus;
+ MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate;
+
+ if (Image == NULL || AbortReason == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ MicrocodeFmpPrivate = MICROCODE_FMP_PRIVATE_DATA_FROM_FMP(This);
+ *AbortReason = NULL;
+
+ if (ImageIndex == 0 || ImageIndex > MicrocodeFmpPrivate->DescriptorCount || Image == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = MicrocodeWrite(ImageIndex, (VOID *)Image, ImageSize, &MicrocodeFmpPrivate->LastAttempt.LastAttemptVersion, &MicrocodeFmpPrivate->LastAttempt.LastAttemptStatus, AbortReason);
+ DEBUG((DEBUG_INFO, "SetImage - LastAttemp Version - 0x%x, State - 0x%x\n", MicrocodeFmpPrivate->LastAttempt.LastAttemptVersion, MicrocodeFmpPrivate->LastAttempt.LastAttemptStatus));
+ VarStatus = gRT->SetVariable(
+ MICROCODE_FMP_LAST_ATTEMPT_VARIABLE_NAME,
+ &gEfiCallerIdGuid,
+ EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ sizeof(MicrocodeFmpPrivate->LastAttempt),
+ &MicrocodeFmpPrivate->LastAttempt
+ );
+ DEBUG((DEBUG_INFO, "SetLastAttemp - %r\n", VarStatus));
+
+ if (!EFI_ERROR(Status)) {
+ InitializeMicrocodeDescriptor(MicrocodeFmpPrivate);
+ }
+
+ return Status;
+}
+
+/**
+ Checks if the firmware image is valid for the device.
+
+ This function allows firmware update application to validate the firmware image without
+ invoking the SetImage() first.
+
+ @param[in] This A pointer to the EFI_FIRMWARE_MANAGEMENT_PROTOCOL instance.
+ @param[in] ImageIndex A unique number identifying the firmware image(s) within the device.
+ The number is between 1 and DescriptorCount.
+ @param[in] Image Points to the new image.
+ @param[in] ImageSize Size of the new image in bytes.
+ @param[out] ImageUpdatable Indicates if the new image is valid for update. It also provides,
+ if available, additional information if the image is invalid.
+
+ @retval EFI_SUCCESS The image was successfully checked.
+ @retval EFI_INVALID_PARAMETER The Image was NULL.
+ @retval EFI_UNSUPPORTED The operation is not supported.
+ @retval EFI_SECURITY_VIOLATIO The operation could not be performed due to an authentication failure.
+
+**/
+EFI_STATUS
+EFIAPI
+FmpCheckImage (
+ IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This,
+ IN UINT8 ImageIndex,
+ IN CONST VOID *Image,
+ IN UINTN ImageSize,
+ OUT UINT32 *ImageUpdatable
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Returns information about the firmware package.
+
+ This function returns package information.
+
+ @param[in] This A pointer to the EFI_FIRMWARE_MANAGEMENT_PROTOCOL instance.
+ @param[out] PackageVersion A version number that represents all the firmware images in the device.
+ The format is vendor specific and new version must have a greater value
+ than the old version. If PackageVersion is not supported, the value is
+ 0xFFFFFFFF. A value of 0xFFFFFFFE indicates that package version
+ comparison is to be performed using PackageVersionName. A value of
+ 0xFFFFFFFD indicates that package version update is in progress.
+ @param[out] PackageVersionName A pointer to a pointer to a null-terminated string representing
+ the package version name. 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[out] PackageVersionNameMaxLen The maximum length of package version name if device supports update of
+ package version name. A value of 0 indicates the device does not support
+ update of package version name. Length is the number of Unicode characters,
+ including the terminating null character.
+ @param[out] AttributesSupported Package attributes that are supported by this device. See 'Package Attribute
+ Definitions' for possible returned values of this parameter. A value of 1
+ indicates the attribute is supported and the current setting value is
+ indicated in AttributesSetting. A value of 0 indicates the attribute is not
+ supported and the current setting value in AttributesSetting is meaningless.
+ @param[out] AttributesSetting Package attributes. See 'Package Attribute Definitions' for possible returned
+ values of this parameter
+
+ @retval EFI_SUCCESS The package information was successfully returned.
+ @retval EFI_UNSUPPORTED The operation is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+FmpGetPackageInfo (
+ IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This,
+ OUT UINT32 *PackageVersion,
+ OUT CHAR16 **PackageVersionName,
+ OUT UINT32 *PackageVersionNameMaxLen,
+ OUT UINT64 *AttributesSupported,
+ OUT UINT64 *AttributesSetting
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Updates information about the firmware package.
+
+ This function updates package information.
+ This function returns EFI_UNSUPPORTED if the package information is not updatable.
+ VendorCode enables vendor to implement vendor-specific package information update policy.
+ Null if the caller did not specify this policy or use the default policy.
+
+ @param[in] This A pointer to the EFI_FIRMWARE_MANAGEMENT_PROTOCOL instance.
+ @param[in] Image Points to the authentication image.
+ Null if authentication is not required.
+ @param[in] ImageSize Size of the authentication image in bytes.
+ 0 if authentication is not required.
+ @param[in] VendorCode This enables vendor to implement vendor-specific firmware
+ image update policy.
+ Null indicates the caller did not specify this policy or use
+ the default policy.
+ @param[in] PackageVersion The new package version.
+ @param[in] PackageVersionName A pointer to the new null-terminated Unicode string representing
+ the package version name.
+ The string length is equal to or less than the value returned in
+ PackageVersionNameMaxLen.
+
+ @retval EFI_SUCCESS The device was successfully updated with the new package
+ information.
+ @retval EFI_INVALID_PARAMETER The PackageVersionName length is longer than the value
+ returned in PackageVersionNameMaxLen.
+ @retval EFI_UNSUPPORTED The operation is not supported.
+ @retval EFI_SECURITY_VIOLATIO The operation could not be performed due to an authentication failure.
+
+**/
+EFI_STATUS
+EFIAPI
+FmpSetPackageInfo (
+ IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This,
+ IN CONST VOID *Image,
+ IN UINTN ImageSize,
+ IN CONST VOID *VendorCode,
+ IN UINT32 PackageVersion,
+ IN CONST CHAR16 *PackageVersionName
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Initialize Microcode Descriptor.
+
+ @param[in] MicrocodeFmpPrivate private data structure to be initialized.
+
+ @return EFI_SUCCESS Microcode Descriptor is initialized.
+**/
+EFI_STATUS
+InitializeMicrocodeDescriptor (
+ IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate
+ )
+{
+ UINT8 CurrentMicrocodeCount;
+
+ CurrentMicrocodeCount = (UINT8)GetMicrocodeInfo(NULL, 0);
+
+ if (CurrentMicrocodeCount > MicrocodeFmpPrivate->DescriptorCount) {
+ if (MicrocodeFmpPrivate->ImageDescriptor != NULL) {
+ FreePool(MicrocodeFmpPrivate->ImageDescriptor);
+ MicrocodeFmpPrivate->ImageDescriptor = NULL;
+ }
+ } else {
+ ZeroMem(MicrocodeFmpPrivate->ImageDescriptor, MicrocodeFmpPrivate->DescriptorCount * sizeof(EFI_FIRMWARE_IMAGE_DESCRIPTOR));
+ }
+
+ 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;
+ }
+ }
+
+ CurrentMicrocodeCount = (UINT8)GetMicrocodeInfo(MicrocodeFmpPrivate->ImageDescriptor, MicrocodeFmpPrivate->DescriptorCount);
+ ASSERT(CurrentMicrocodeCount == MicrocodeFmpPrivate->DescriptorCount);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Initialize MicrocodeFmpDriver private data structure.
+
+ @param[in] MicrocodeFmpPrivate private data structure to be initialized.
+
+ @return EFI_SUCCESS private data is initialized.
+**/
+EFI_STATUS
+InitializePrivateData (
+ IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate
+ )
+{
+ EFI_STATUS Status;
+ EFI_STATUS VarStatus;
+ UINTN VarSize;
+
+ MicrocodeFmpPrivate->Signature = MICROCODE_FMP_PRIVATE_DATA_SIGNATURE;
+ MicrocodeFmpPrivate->Handle = NULL;
+ CopyMem(&MicrocodeFmpPrivate->Fmp, &mFirmwareManagementProtocol, sizeof(EFI_FIRMWARE_MANAGEMENT_PROTOCOL));
+
+ MicrocodeFmpPrivate->PackageVersion = 0x1;
+ MicrocodeFmpPrivate->PackageVersionName = L"Microcode";
+
+ MicrocodeFmpPrivate->LastAttempt.LastAttemptVersion = 0x0;
+ MicrocodeFmpPrivate->LastAttempt.LastAttemptStatus = 0x0;
+ VarSize = sizeof(MicrocodeFmpPrivate->LastAttempt);
+ VarStatus = gRT->GetVariable(
+ MICROCODE_FMP_LAST_ATTEMPT_VARIABLE_NAME,
+ &gEfiCallerIdGuid,
+ NULL,
+ &VarSize,
+ &MicrocodeFmpPrivate->LastAttempt
+ );
+ DEBUG((DEBUG_INFO, "GetLastAttemp - %r\n", VarStatus));
+ DEBUG((DEBUG_INFO, "GetLastAttemp Version - 0x%x, State - 0x%x\n", MicrocodeFmpPrivate->LastAttempt.LastAttemptVersion, MicrocodeFmpPrivate->LastAttempt.LastAttemptStatus));
+
+ Status = InitializeMicrocodeDescriptor(MicrocodeFmpPrivate);
+
+ return Status;
+}
+
+/**
+ Microcode FMP module entrypoint
+
+ @param[in] ImageHandle The firmware allocated handle for the EFI image.
+ @param[in] SystemTable A pointer to the EFI System Table.
+
+ @return EFI_SUCCESS Microcode FMP module is initialized.
+**/
+EFI_STATUS
+EFIAPI
+MicrocodeFmpMain (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Initialize MicrocodeFmpPrivateData
+ //
+ mMicrocodeFmpPrivate = AllocateZeroPool (sizeof(MICROCODE_FMP_PRIVATE_DATA));
+ if (mMicrocodeFmpPrivate == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = InitializePrivateData(mMicrocodeFmpPrivate);
+ if (EFI_ERROR(Status)) {
+ FreePool(mMicrocodeFmpPrivate);
+ mMicrocodeFmpPrivate = NULL;
+ return Status;
+ }
+
+ //
+ // Install FMP protocol.
+ //
+ Status = gBS->InstallProtocolInterface (
+ &mMicrocodeFmpPrivate->Handle,
+ &gEfiFirmwareManagementProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &mMicrocodeFmpPrivate->Fmp
+ );
+ if (EFI_ERROR (Status)) {
+ FreePool(mMicrocodeFmpPrivate);
+ mMicrocodeFmpPrivate = NULL;
+ return Status;
+ }
+
+ return Status;
+}
diff --git a/UefiCpuPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeUpdate.c b/UefiCpuPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeUpdate.c
new file mode 100644
index 0000000..2b2d3ac
--- /dev/null
+++ b/UefiCpuPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeUpdate.c
@@ -0,0 +1,784 @@
+/** @file
+ SetImage instance to update Microcode.
+
+ Caution: This module requires additional review when modified.
+ This module will have external input - capsule image.
+ This external input must be validated carefully to avoid security issue like
+ buffer overflow, integer overflow.
+
+ MicrocodeWrite() and VerifyMicrocode() will receive untrusted input and do basic validation.
+
+ Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "MicrocodeUpdate.h"
+
+/**
+ 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 UINT64 *MicrocodePatchAddress,
+ OUT UINT64 *MicrocodePatchRegionSize
+ )
+{
+ *MicrocodePatchAddress = PcdGet64(PcdCpuMicrocodePatchAddress);
+ *MicrocodePatchRegionSize = PcdGet64(PcdCpuMicrocodePatchRegionSize);
+
+ if ((*MicrocodePatchAddress == 0) || (*MicrocodePatchRegionSize == 0)) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ Get Microcode update signature of currently loaded Microcode update.
+
+ @return Microcode signature.
+
+**/
+UINT32
+GetCurrentMicrocodeSignature (
+ VOID
+ )
+{
+ UINT64 Signature;
+
+ AsmWriteMsr64(MSR_IA32_BIOS_SIGN_ID, 0);
+ AsmCpuid(CPUID_VERSION_INFO, NULL, NULL, NULL, NULL);
+ Signature = AsmReadMsr64(MSR_IA32_BIOS_SIGN_ID);
+ return (UINT32)RShiftU64(Signature, 32);
+}
+
+/**
+ Get current processor signature.
+
+ @return current processor signature.
+**/
+UINT32
+GetCurrentProcessorSignature (
+ VOID
+ )
+{
+ UINT32 RegEax;
+ AsmCpuid(CPUID_VERSION_INFO, &RegEax, NULL, NULL, NULL);
+ return RegEax;
+}
+
+/**
+ Get current platform ID.
+
+ @return current platform ID.
+**/
+UINT8
+GetCurrentPlatformId (
+ VOID
+ )
+{
+ UINT8 PlatformId;
+
+ PlatformId = (UINT8)AsmMsrBitFieldRead64(MSR_IA32_PLATFORM_ID, 50, 52);
+ return PlatformId;
+}
+
+/**
+ Load new Microcode.
+
+ @param[in] Address The address of new Microcode.
+
+ @return Loaded Microcode signature.
+
+**/
+UINT32
+LoadMicrocode (
+ IN UINT64 Address
+ )
+{
+ AsmWriteMsr64(MSR_IA32_BIOS_UPDT_TRIG, Address);
+ return GetCurrentMicrocodeSignature();
+}
+
+/**
+ Get current Microcode information.
+
+ @param[out] ImageDescriptor Microcode ImageDescriptor
+ @param[in] DescriptorCount The count of Microcode ImageDescriptor allocated.
+
+ @return Microcode count
+**/
+UINTN
+GetMicrocodeInfo (
+ OUT EFI_FIRMWARE_IMAGE_DESCRIPTOR *ImageDescriptor, OPTIONAL
+ IN UINTN DescriptorCount OPTIONAL
+ )
+{
+ BOOLEAN Result;
+ UINT64 MicrocodePatchAddress;
+ UINT64 MicrocodePatchRegionSize;
+ CPU_MICROCODE_HEADER *MicrocodeEntryPoint;
+ UINTN MicrocodeEnd;
+ UINTN TotalSize;
+ UINTN Count;
+ UINT64 ImageAttributes;
+ UINT32 CurrentRevision;
+
+ Result = GetMicrocodeRegion(&MicrocodePatchAddress, &MicrocodePatchRegionSize);
+ if (!Result) {
+ DEBUG((DEBUG_ERROR, "Fail to get Microcode Region\n"));
+ return 0;
+ }
+ DEBUG((DEBUG_INFO, "Microcode Region - 0x%lx - 0x%lx\n", MicrocodePatchAddress, MicrocodePatchRegionSize));
+
+ Count = 0;
+ CurrentRevision = GetCurrentMicrocodeSignature();
+
+ 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;
+ }
+
+ if (ImageDescriptor != NULL && DescriptorCount > Count) {
+ ImageDescriptor[Count].ImageIndex = (UINT8)(Count + 1);
+ CopyGuid (&ImageDescriptor[Count].ImageTypeId, &gMicrocodeFmpImageTypeIdGuid);
+ ImageDescriptor[Count].ImageId = LShiftU64(MicrocodeEntryPoint->ProcessorFlags, 32) + MicrocodeEntryPoint->ProcessorSignature.Uint32;
+ ImageDescriptor[Count].ImageIdName = NULL;
+ ImageDescriptor[Count].Version = MicrocodeEntryPoint->UpdateRevision;
+ ImageDescriptor[Count].VersionName = NULL;
+ ImageDescriptor[Count].Size = TotalSize;
+ ImageAttributes = IMAGE_ATTRIBUTE_IMAGE_UPDATABLE | IMAGE_ATTRIBUTE_RESET_REQUIRED;
+ if (CurrentRevision == MicrocodeEntryPoint->UpdateRevision) {
+ 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;
+ }
+ } else {
+ //
+ // It is the padding data between the microcode patches for microcode patches alignment.
+ // Because the microcode patch is the multiple of 1-KByte, the padding data should not
+ // exist if the microcode patch alignment value is not larger than 1-KByte. So, the microcode
+ // alignment value should be larger than 1-KByte. We could skip SIZE_1KB padding data to
+ // find the next possible microcode patch header.
+ //
+ MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (((UINTN) MicrocodeEntryPoint) + SIZE_1KB);
+ continue;
+ }
+
+ Count++;
+ ASSERT(Count < 0xFF);
+
+ //
+ // Get the next patch.
+ //
+ MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (((UINTN) MicrocodeEntryPoint) + TotalSize);
+ } while (((UINTN) MicrocodeEntryPoint < MicrocodeEnd));
+
+ return Count;
+}
+
+/**
+ Read Microcode.
+
+ @param[in] ImageIndex The index of Microcode image.
+ @param[in, out] Image The Microcode image buffer.
+ @param[in, out] ImageSize The size of Microcode image buffer in bytes.
+
+ @retval EFI_SUCCESS The Microcode image is read.
+ @retval EFI_NOT_FOUND The Microcode image is not found.
+**/
+EFI_STATUS
+MicrocodeRead (
+ IN UINTN ImageIndex,
+ IN OUT VOID *Image,
+ IN OUT UINTN *ImageSize
+ )
+{
+ BOOLEAN Result;
+ UINT64 MicrocodePatchAddress;
+ UINT64 MicrocodePatchRegionSize;
+ CPU_MICROCODE_HEADER *MicrocodeEntryPoint;
+ UINTN MicrocodeEnd;
+ UINTN TotalSize;
+ UINTN Count;
+
+ Result = GetMicrocodeRegion(&MicrocodePatchAddress, &MicrocodePatchRegionSize);
+ if (!Result) {
+ DEBUG((DEBUG_ERROR, "Fail to get Microcode Region\n"));
+ return EFI_NOT_FOUND;
+ }
+ DEBUG((DEBUG_INFO, "Microcode Region - 0x%lx - 0x%lx\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;
+ }
+
+ } else {
+ //
+ // It is the padding data between the microcode patches for microcode patches alignment.
+ // Because the microcode patch is the multiple of 1-KByte, the padding data should not
+ // exist if the microcode patch alignment value is not larger than 1-KByte. So, the microcode
+ // alignment value should be larger than 1-KByte. We could skip SIZE_1KB padding data to
+ // find the next possible microcode patch header.
+ //
+ MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *)(((UINTN)MicrocodeEntryPoint) + SIZE_1KB);
+ continue;
+ }
+
+ Count++;
+ ASSERT(Count < 0xFF);
+
+ //
+ // Get the next patch.
+ //
+ MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *)(((UINTN)MicrocodeEntryPoint) + TotalSize);
+ } while (((UINTN)MicrocodeEntryPoint < MicrocodeEnd));
+
+ return EFI_NOT_FOUND;
+}
+
+/**
+ Verify Microcode.
+
+ Caution: This function may receive untrusted input.
+
+ @param[in] Image The Microcode image buffer.
+ @param[in] ImageSize The size of Microcode image buffer in bytes.
+ @param[in] TryLoad Try to load Microcode or not.
+ @param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.
+ @param[out] AbortReason A pointer to a pointer to a null-terminated string providing more
+ details for the aborted operation. The buffer is allocated by this function
+ with AllocatePool(), and it is the caller's responsibility to free it with a
+ call to FreePool().
+
+ @retval EFI_SUCCESS The Microcode image passes verification.
+ @retval EFI_VOLUME_CORRUPTED The Microcode image is corrupt.
+ @retval EFI_INCOMPATIBLE_VERSION The Microcode image version is incorrect.
+ @retval EFI_UNSUPPORTED The Microcode ProcessorSignature or ProcessorFlags is incorrect.
+ @retval EFI_SECURITY_VIOLATION The Microcode image fails to load.
+**/
+EFI_STATUS
+VerifyMicrocode (
+ IN VOID *Image,
+ IN UINTN ImageSize,
+ IN BOOLEAN TryLoad,
+ OUT UINT32 *LastAttemptStatus,
+ OUT CHAR16 **AbortReason
+ )
+{
+ UINTN Index;
+ CPU_MICROCODE_HEADER *MicrocodeEntryPoint;
+ UINTN TotalSize;
+ UINTN DataSize;
+ UINT32 CurrentRevision;
+ UINT32 CurrentProcessorSignature;
+ UINT8 CurrentPlatformId;
+ UINT32 CheckSum32;
+ UINTN ExtendedTableLength;
+ UINT32 ExtendedTableCount;
+ CPU_MICROCODE_EXTENDED_TABLE *ExtendedTable;
+ CPU_MICROCODE_EXTENDED_TABLE_HEADER *ExtendedTableHeader;
+ BOOLEAN CorrectMicrocode;
+
+ //
+ // Check HeaderVersion
+ //
+ MicrocodeEntryPoint = Image;
+ if (MicrocodeEntryPoint->HeaderVersion != 0x1) {
+ DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on HeaderVersion\n"));
+ *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;
+ if (AbortReason != NULL) {
+ *AbortReason = AllocateCopyPool(sizeof(L"InvalidHeaderVersion"), L"InvalidHeaderVersion");
+ }
+ return EFI_INCOMPATIBLE_VERSION;
+ }
+ //
+ // Check LoaderRevision
+ //
+ if (MicrocodeEntryPoint->LoaderRevision != 0x1) {
+ DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on LoaderRevision\n"));
+ *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;
+ if (AbortReason != NULL) {
+ *AbortReason = AllocateCopyPool(sizeof(L"InvalidLoaderVersion"), L"InvalidLoaderVersion");
+ }
+ return EFI_INCOMPATIBLE_VERSION;
+ }
+ //
+ // Check Size
+ //
+ if (MicrocodeEntryPoint->DataSize == 0) {
+ TotalSize = 2048;
+ } else {
+ TotalSize = MicrocodeEntryPoint->TotalSize;
+ }
+ if (TotalSize <= sizeof(CPU_MICROCODE_HEADER)) {
+ DEBUG((DEBUG_ERROR, "VerifyMicrocode - TotalSize too small\n"));
+ *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;
+ if (AbortReason != NULL) {
+ *AbortReason = AllocateCopyPool(sizeof(L"InvalidTotalSize"), L"InvalidTotalSize");
+ }
+ return EFI_VOLUME_CORRUPTED;
+ }
+ if (TotalSize != ImageSize) {
+ DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on TotalSize\n"));
+ *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;
+ if (AbortReason != NULL) {
+ *AbortReason = AllocateCopyPool(sizeof(L"InvalidTotalSize"), L"InvalidTotalSize");
+ }
+ return EFI_VOLUME_CORRUPTED;
+ }
+ //
+ // Check CheckSum32
+ //
+ if (MicrocodeEntryPoint->DataSize == 0) {
+ DataSize = 2048 - sizeof(CPU_MICROCODE_HEADER);
+ } else {
+ DataSize = MicrocodeEntryPoint->DataSize;
+ }
+ if (DataSize > TotalSize - sizeof(CPU_MICROCODE_HEADER)) {
+ DEBUG((DEBUG_ERROR, "VerifyMicrocode - DataSize too big\n"));
+ *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;
+ if (AbortReason != NULL) {
+ *AbortReason = AllocateCopyPool(sizeof(L"InvalidDataSize"), L"InvalidDataSize");
+ }
+ return EFI_VOLUME_CORRUPTED;
+ }
+ if ((DataSize & 0x3) != 0) {
+ DEBUG((DEBUG_ERROR, "VerifyMicrocode - DataSize not aligned\n"));
+ *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;
+ if (AbortReason != NULL) {
+ *AbortReason = AllocateCopyPool(sizeof(L"InvalidDataSize"), L"InvalidDataSize");
+ }
+ return EFI_VOLUME_CORRUPTED;
+ }
+ CheckSum32 = CalculateSum32((UINT32 *)MicrocodeEntryPoint, DataSize + sizeof(CPU_MICROCODE_HEADER));
+ if (CheckSum32 != 0) {
+ DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on CheckSum32\n"));
+ *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;
+ if (AbortReason != NULL) {
+ *AbortReason = AllocateCopyPool(sizeof(L"InvalidChecksum"), L"InvalidChecksum");
+ }
+ return EFI_VOLUME_CORRUPTED;
+ }
+
+ //
+ // Check ProcessorSignature/ProcessorFlags
+ //
+ CorrectMicrocode = FALSE;
+ CurrentProcessorSignature = GetCurrentProcessorSignature();
+ CurrentPlatformId = GetCurrentPlatformId();
+ if ((MicrocodeEntryPoint->ProcessorSignature.Uint32 != CurrentProcessorSignature) ||
+ ((MicrocodeEntryPoint->ProcessorFlags & (1 << CurrentPlatformId)) == 0)) {
+ ExtendedTableLength = TotalSize - (DataSize + sizeof(CPU_MICROCODE_HEADER));
+ if (ExtendedTableLength != 0) {
+ //
+ // Extended Table exist, check if the CPU in support list
+ //
+ ExtendedTableHeader = (CPU_MICROCODE_EXTENDED_TABLE_HEADER *)((UINT8 *)(MicrocodeEntryPoint) + DataSize + sizeof(CPU_MICROCODE_HEADER));
+ //
+ // Calculate Extended Checksum
+ //
+ if ((ExtendedTableLength > sizeof(CPU_MICROCODE_EXTENDED_TABLE_HEADER)) && ((ExtendedTableLength & 0x3) != 0)) {
+ CheckSum32 = CalculateSum32((UINT32 *)ExtendedTableHeader, ExtendedTableLength);
+ if (CheckSum32 == 0) {
+ //
+ // Checksum correct
+ //
+ ExtendedTableCount = ExtendedTableHeader->ExtendedSignatureCount;
+ if (ExtendedTableCount <= (ExtendedTableLength - sizeof(CPU_MICROCODE_EXTENDED_TABLE_HEADER)) / sizeof(CPU_MICROCODE_EXTENDED_TABLE)) {
+ ExtendedTable = (CPU_MICROCODE_EXTENDED_TABLE *)(ExtendedTableHeader + 1);
+ for (Index = 0; Index < ExtendedTableCount; Index++) {
+ CheckSum32 = CalculateSum32((UINT32 *)ExtendedTable, sizeof(CPU_MICROCODE_EXTENDED_TABLE));
+ if (CheckSum32 == 0) {
+ //
+ // Verify Header
+ //
+ if ((ExtendedTable->ProcessorSignature.Uint32 == CurrentProcessorSignature) &&
+ (ExtendedTable->ProcessorFlag & (1 << CurrentPlatformId))) {
+ //
+ // Find one
+ //
+ CorrectMicrocode = TRUE;
+ break;
+ }
+ }
+ ExtendedTable++;
+ }
+ }
+ }
+ }
+ }
+ if (!CorrectMicrocode) {
+ DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on CurrentProcessorSignature/ProcessorFlags\n"));
+ *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INCORRECT_VERSION;
+ if (AbortReason != NULL) {
+ if (MicrocodeEntryPoint->ProcessorSignature.Uint32 != CurrentProcessorSignature) {
+ *AbortReason = AllocateCopyPool(sizeof(L"UnsupportedProcessSignature"), L"UnsupportedProcessSignature");
+ } else {
+ *AbortReason = AllocateCopyPool(sizeof(L"UnsupportedProcessorFlags"), L"UnsupportedProcessorFlags");
+ }
+ }
+ return EFI_UNSUPPORTED;
+ }
+ }
+
+ //
+ // Check UpdateRevision
+ //
+ CurrentRevision = GetCurrentMicrocodeSignature();
+ if (MicrocodeEntryPoint->UpdateRevision < CurrentRevision) {
+ DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on UpdateRevision\n"));
+ *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INCORRECT_VERSION;
+ if (AbortReason != NULL) {
+ *AbortReason = AllocateCopyPool(sizeof(L"IncorrectRevision"), L"IncorrectRevision");
+ }
+ return EFI_INCOMPATIBLE_VERSION;
+ }
+
+ //
+ // try load MCU
+ //
+ if (TryLoad) {
+ CurrentRevision = LoadMicrocode((UINTN)MicrocodeEntryPoint + sizeof(CPU_MICROCODE_HEADER));
+ if (MicrocodeEntryPoint->UpdateRevision != CurrentRevision) {
+ DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on LoadMicrocode\n"));
+ *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_AUTH_ERROR;
+ if (AbortReason != NULL) {
+ *AbortReason = AllocateCopyPool(sizeof(L"InvalidData"), L"InvalidData");
+ }
+ return EFI_SECURITY_VIOLATION;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Get current Microcode in used.
+
+ @return current Microcode in used.
+**/
+VOID *
+GetCurrentMicrocodeInUse (
+ VOID
+ )
+{
+ BOOLEAN Result;
+ EFI_STATUS Status;
+ UINT64 MicrocodePatchAddress;
+ UINT64 MicrocodePatchRegionSize;
+ CPU_MICROCODE_HEADER *MicrocodeEntryPoint;
+ UINTN MicrocodeEnd;
+ UINTN TotalSize;
+ UINTN Count;
+ UINT32 AttemptStatus;
+
+ Result = GetMicrocodeRegion(&MicrocodePatchAddress, &MicrocodePatchRegionSize);
+ if (!Result) {
+ DEBUG((DEBUG_ERROR, "Fail to get Microcode Region\n"));
+ return NULL;
+ }
+ DEBUG((DEBUG_INFO, "Microcode Region - 0x%lx - 0x%lx\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;
+ }
+ Status = VerifyMicrocode(MicrocodeEntryPoint, TotalSize, FALSE, &AttemptStatus, NULL);
+ if (!EFI_ERROR(Status)) {
+ return MicrocodeEntryPoint;
+ }
+
+ } else {
+ //
+ // It is the padding data between the microcode patches for microcode patches alignment.
+ // Because the microcode patch is the multiple of 1-KByte, the padding data should not
+ // exist if the microcode patch alignment value is not larger than 1-KByte. So, the microcode
+ // alignment value should be larger than 1-KByte. We could skip SIZE_1KB padding data to
+ // find the next possible microcode patch header.
+ //
+ MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *)(((UINTN)MicrocodeEntryPoint) + SIZE_1KB);
+ continue;
+ }
+
+ Count++;
+ ASSERT(Count < 0xFF);
+
+ //
+ // Get the next patch.
+ //
+ MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *)(((UINTN)MicrocodeEntryPoint) + TotalSize);
+ } while (((UINTN)MicrocodeEntryPoint < MicrocodeEnd));
+
+ return NULL;
+}
+
+/**
+ Get current Microcode used region size.
+
+ @return current Microcode used region size.
+**/
+UINTN
+GetCurrentMicrocodeUsedRegionSize (
+ VOID
+ )
+{
+ BOOLEAN Result;
+ UINT64 MicrocodePatchAddress;
+ UINT64 MicrocodePatchRegionSize;
+ CPU_MICROCODE_HEADER *MicrocodeEntryPoint;
+ UINTN MicrocodeEnd;
+ UINTN TotalSize;
+ UINTN Count;
+ UINTN MicrocodeUsedEnd;
+
+ Result = GetMicrocodeRegion(&MicrocodePatchAddress, &MicrocodePatchRegionSize);
+ if (!Result) {
+ DEBUG((DEBUG_ERROR, "Fail to get Microcode Region\n"));
+ return 0;
+ }
+ DEBUG((DEBUG_INFO, "Microcode Region - 0x%lx - 0x%lx\n", MicrocodePatchAddress, MicrocodePatchRegionSize));
+
+ MicrocodeUsedEnd = (UINTN)MicrocodePatchAddress;
+ 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;
+ }
+
+ } else {
+ //
+ // It is the padding data between the microcode patches for microcode patches alignment.
+ // Because the microcode patch is the multiple of 1-KByte, the padding data should not
+ // exist if the microcode patch alignment value is not larger than 1-KByte. So, the microcode
+ // alignment value should be larger than 1-KByte. We could skip SIZE_1KB padding data to
+ // find the next possible microcode patch header.
+ //
+ MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *)(((UINTN)MicrocodeEntryPoint) + SIZE_1KB);
+ continue;
+ }
+
+ Count++;
+ ASSERT(Count < 0xFF);
+ MicrocodeUsedEnd = (UINTN)MicrocodeEntryPoint;
+
+ //
+ // Get the next patch.
+ //
+ MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *)(((UINTN)MicrocodeEntryPoint) + TotalSize);
+ } while (((UINTN)MicrocodeEntryPoint < MicrocodeEnd));
+
+ return MicrocodeUsedEnd - (UINTN)MicrocodePatchAddress;
+}
+
+/**
+ Update Microcode.
+
+ @param[in] Address The flash address of Microcode.
+ @param[in] Image The Microcode image buffer.
+ @param[in] ImageSize The size of Microcode image buffer in bytes.
+ @param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.
+
+ @retval EFI_SUCCESS The Microcode image is updated.
+ @retval EFI_WRITE_PROTECTED The flash device is read only.
+**/
+EFI_STATUS
+UpdateMicrocode (
+ IN UINT64 Address,
+ IN VOID *Image,
+ IN UINTN ImageSize,
+ OUT UINT32 *LastAttemptStatus
+ )
+{
+ EFI_STATUS Status;
+
+ DEBUG((DEBUG_INFO, "PlatformUpdate:"));
+ DEBUG((DEBUG_INFO, " Address - 0x%lx,", Address));
+ DEBUG((DEBUG_INFO, " Legnth - 0x%x\n", ImageSize));
+
+ Status = MicrocodeFlashWrite (
+ Address,
+ Image,
+ ImageSize
+ );
+ if (!EFI_ERROR(Status)) {
+ *LastAttemptStatus = LAST_ATTEMPT_STATUS_SUCCESS;
+ } else {
+ *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL;
+ }
+ return Status;
+}
+
+/**
+ Write Microcode.
+
+ Caution: This function may receive untrusted input.
+
+ @param[in] ImageIndex The index of Microcode image.
+ @param[in] Image The Microcode image buffer.
+ @param[in] ImageSize The size of Microcode image buffer in bytes.
+ @param[out] LastAttemptVersion The last attempt version, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.
+ @param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.
+ @param[out] AbortReason A pointer to a pointer to a null-terminated string providing more
+ details for the aborted operation. The buffer is allocated by this function
+ with AllocatePool(), and it is the caller's responsibility to free it with a
+ call to FreePool().
+
+ @retval EFI_SUCCESS The Microcode image is written.
+ @retval EFI_VOLUME_CORRUPTED The Microcode image is corrupt.
+ @retval EFI_INCOMPATIBLE_VERSION The Microcode image version is incorrect.
+ @retval EFI_SECURITY_VIOLATION The Microcode image fails to load.
+ @retval EFI_WRITE_PROTECTED The flash device is read only.
+**/
+EFI_STATUS
+MicrocodeWrite (
+ IN UINTN ImageIndex,
+ IN VOID *Image,
+ IN UINTN ImageSize,
+ OUT UINT32 *LastAttemptVersion,
+ OUT UINT32 *LastAttemptStatus,
+ OUT CHAR16 **AbortReason
+ )
+{
+ BOOLEAN Result;
+ EFI_STATUS Status;
+ UINT64 MicrocodePatchAddress;
+ UINT64 MicrocodePatchRegionSize;
+ CPU_MICROCODE_HEADER *CurrentMicrocodeEntryPoint;
+ UINTN CurrentTotalSize;
+ UINTN UsedRegionSize;
+ VOID *AlignedImage;
+
+ Result = GetMicrocodeRegion(&MicrocodePatchAddress, &MicrocodePatchRegionSize);
+ if (!Result) {
+ DEBUG((DEBUG_ERROR, "Fail to get Microcode Region\n"));
+ return EFI_NOT_FOUND;
+ }
+
+ CurrentTotalSize = 0;
+ CurrentMicrocodeEntryPoint = GetCurrentMicrocodeInUse();
+ if (CurrentMicrocodeEntryPoint != NULL) {
+ if (CurrentMicrocodeEntryPoint->DataSize == 0) {
+ CurrentTotalSize = 2048;
+ } else {
+ CurrentTotalSize = CurrentMicrocodeEntryPoint->TotalSize;
+ }
+ }
+
+ //
+ // MCU must be 16 bytes aligned
+ //
+ AlignedImage = AllocateCopyPool(ImageSize, Image);
+ if (AlignedImage == NULL) {
+ DEBUG((DEBUG_ERROR, "Fail to allocate aligned image\n"));
+ *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES;
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ *LastAttemptVersion = ((CPU_MICROCODE_HEADER *)Image)->UpdateRevision;
+ Status = VerifyMicrocode(AlignedImage, ImageSize, TRUE, LastAttemptStatus, AbortReason);
+ if (EFI_ERROR(Status)) {
+ DEBUG((DEBUG_ERROR, "Fail to verify Microcode Region\n"));
+ FreePool(AlignedImage);
+ return Status;
+ }
+ DEBUG((DEBUG_INFO, "Pass VerifyMicrocode\n"));
+
+ if (CurrentTotalSize < ImageSize) {
+ UsedRegionSize = GetCurrentMicrocodeUsedRegionSize();
+ if (MicrocodePatchRegionSize - UsedRegionSize >= ImageSize) {
+ //
+ // Append
+ //
+ DEBUG((DEBUG_INFO, "Append new microcode\n"));
+ Status = UpdateMicrocode(MicrocodePatchAddress + UsedRegionSize, AlignedImage, ImageSize, LastAttemptStatus);
+ } else if (MicrocodePatchRegionSize >= ImageSize) {
+ //
+ // Ignor all others and just add this one from beginning.
+ //
+ DEBUG((DEBUG_INFO, "Add new microcode from beginning\n"));
+ Status = UpdateMicrocode(MicrocodePatchAddress, AlignedImage, ImageSize, LastAttemptStatus);
+ } else {
+ DEBUG((DEBUG_ERROR, "Microcode too big\n"));
+ *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES;
+ Status = EFI_OUT_OF_RESOURCES;
+ }
+ } else {
+ //
+ // Replace
+ //
+ DEBUG((DEBUG_INFO, "Replace old microcode\n"));
+ Status = UpdateMicrocode((UINTN)CurrentMicrocodeEntryPoint, AlignedImage, ImageSize, LastAttemptStatus);
+ }
+
+ FreePool(AlignedImage);
+
+ return Status;
+}
+
+
diff --git a/UefiCpuPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeUpdate.h b/UefiCpuPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeUpdate.h
new file mode 100644
index 0000000..77e8441
--- /dev/null
+++ b/UefiCpuPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeUpdate.h
@@ -0,0 +1,403 @@
+/** @file
+ Microcode update header file.
+
+ Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _MICROCODE_FMP_H_
+#define _MICROCODE_FMP_H_
+
+#include <PiDxe.h>
+
+#include <Guid/SystemResourceTable.h>
+#include <Guid/MicrocodeFmp.h>
+
+#include <Protocol/FirmwareManagement.h>
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PcdLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/DevicePathLib.h>
+#include <Library/HobLib.h>
+#include <Library/MicrocodeFlashAccessLib.h>
+
+#include <Register/Cpuid.h>
+#include <Register/Msr.h>
+#include <Register/Microcode.h>
+
+#define MICROCODE_FMP_PRIVATE_DATA_SIGNATURE SIGNATURE_32('M', 'C', 'U', 'F')
+
+//
+// Microcode FMP private data structure.
+//
+
+typedef struct {
+ UINT32 LastAttemptVersion;
+ UINT32 LastAttemptStatus;
+} MICROCODE_FMP_LAST_ATTEMPT_VARIABLE;
+
+struct _MICROCODE_FMP_PRIVATE_DATA {
+ UINT32 Signature;
+ EFI_FIRMWARE_MANAGEMENT_PROTOCOL Fmp;
+ EFI_HANDLE Handle;
+ UINT8 DescriptorCount;
+ EFI_FIRMWARE_IMAGE_DESCRIPTOR *ImageDescriptor;
+ UINT32 PackageVersion;
+ CHAR16 *PackageVersionName;
+ MICROCODE_FMP_LAST_ATTEMPT_VARIABLE LastAttempt;
+};
+
+typedef struct _MICROCODE_FMP_PRIVATE_DATA MICROCODE_FMP_PRIVATE_DATA;
+
+#define MICROCODE_FMP_LAST_ATTEMPT_VARIABLE_NAME L"MicrocodeLastAttempVar"
+
+/**
+ Returns a pointer to the MICROCODE_FMP_PRIVATE_DATA structure from the input a as Fmp.
+
+ If the signatures matches, then a pointer to the data structure that contains
+ a specified field of that data structure is returned.
+
+ @param a Pointer to the field specified by ServiceBinding within
+ a data structure of type MICROCODE_FMP_PRIVATE_DATA.
+
+**/
+#define MICROCODE_FMP_PRIVATE_DATA_FROM_FMP(a) \
+ CR ( \
+ (a), \
+ MICROCODE_FMP_PRIVATE_DATA, \
+ Fmp, \
+ MICROCODE_FMP_PRIVATE_DATA_SIGNATURE \
+ )
+
+/**
+ Get current Microcode information.
+
+ @param[out] ImageDescriptor Microcode ImageDescriptor
+ @param[in] DescriptorCount The count of Microcode ImageDescriptor allocated.
+
+ @return Microcode count
+**/
+UINTN
+GetMicrocodeInfo (
+ OUT EFI_FIRMWARE_IMAGE_DESCRIPTOR *ImageDescriptor, OPTIONAL
+ IN UINTN DescriptorCount OPTIONAL
+ );
+
+/**
+ Read Microcode.
+
+ @param[in] ImageIndex The index of Microcode image.
+ @param[in, out] Image The Microcode image buffer.
+ @param[in, out] ImageSize The size of Microcode image buffer in bytes.
+
+ @retval EFI_SUCCESS The Microcode image is read.
+ @retval EFI_NOT_FOUND The Microcode image is not found.
+**/
+EFI_STATUS
+MicrocodeRead (
+ IN UINTN ImageIndex,
+ IN OUT VOID *Image,
+ IN OUT UINTN *ImageSize
+ );
+
+/**
+ Write Microcode.
+
+ @param[in] ImageIndex The index of Microcode image.
+ @param[in] Image The Microcode image buffer.
+ @param[in] ImageSize The size of Microcode image buffer in bytes.
+ @param[out] LastAttemptVersion The last attempt version, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.
+ @param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.
+ @param[out] AbortReason A pointer to a pointer to a null-terminated string providing more
+ details for the aborted operation. The buffer is allocated by this function
+ with AllocatePool(), and it is the caller's responsibility to free it with a
+ call to FreePool().
+
+ @retval EFI_SUCCESS The Microcode image is written.
+ @retval EFI_VOLUME_CORRUPTED The Microcode image is corrupt.
+ @retval EFI_INCOMPATIBLE_VERSION The Microcode image version is incorrect.
+ @retval EFI_SECURITY_VIOLATION The Microcode image fails to load.
+ @retval EFI_WRITE_PROTECTED The flash device is read only.
+**/
+EFI_STATUS
+MicrocodeWrite (
+ IN UINTN ImageIndex,
+ IN VOID *Image,
+ IN UINTN ImageSize,
+ OUT UINT32 *LastAttemptVersion,
+ OUT UINT32 *LastAttemptStatus,
+ OUT CHAR16 **AbortReason
+ );
+
+/**
+ Returns information about the current firmware image(s) of the device.
+
+ This function allows a copy of the current firmware image to be created and saved.
+ The saved copy could later been used, for example, in firmware image recovery or rollback.
+
+ @param[in] This A pointer to the EFI_FIRMWARE_MANAGEMENT_PROTOCOL instance.
+ @param[in, out] ImageInfoSize A pointer to the size, in bytes, of the ImageInfo buffer.
+ On input, this is the size of the buffer allocated by the caller.
+ On output, it is the size of the buffer returned by the firmware
+ if the buffer was large enough, or the size of the buffer needed
+ to contain the image(s) information if the buffer was too small.
+ @param[in, out] ImageInfo A pointer to the buffer in which firmware places the current image(s)
+ information. The information is an array of EFI_FIRMWARE_IMAGE_DESCRIPTORs.
+ @param[out] DescriptorVersion A pointer to the location in which firmware returns the version number
+ associated with the EFI_FIRMWARE_IMAGE_DESCRIPTOR.
+ @param[out] DescriptorCount A pointer to the location in which firmware returns the number of
+ descriptors or firmware images within this device.
+ @param[out] DescriptorSize A pointer to the location in which firmware returns the size, in bytes,
+ of an individual EFI_FIRMWARE_IMAGE_DESCRIPTOR.
+ @param[out] PackageVersion A version number that represents all the firmware images in the device.
+ The format is vendor specific and new version must have a greater value
+ than the old version. If PackageVersion is not supported, the value is
+ 0xFFFFFFFF. A value of 0xFFFFFFFE indicates that package version comparison
+ is to be performed using PackageVersionName. A value of 0xFFFFFFFD indicates
+ that package version update is in progress.
+ @param[out] PackageVersionName A pointer to a pointer to a null-terminated string representing the
+ package version name. 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 device was successfully updated with the new image.
+ @retval EFI_BUFFER_TOO_SMALL The ImageInfo buffer was too small. The current buffer size
+ needed to hold the image(s) information is returned in ImageInfoSize.
+ @retval EFI_INVALID_PARAMETER ImageInfoSize is NULL.
+ @retval EFI_DEVICE_ERROR Valid information could not be returned. Possible corrupted image.
+
+**/
+EFI_STATUS
+EFIAPI
+FmpGetImageInfo (
+ IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This,
+ IN OUT UINTN *ImageInfoSize,
+ IN OUT EFI_FIRMWARE_IMAGE_DESCRIPTOR *ImageInfo,
+ OUT UINT32 *DescriptorVersion,
+ OUT UINT8 *DescriptorCount,
+ OUT UINTN *DescriptorSize,
+ OUT UINT32 *PackageVersion,
+ OUT CHAR16 **PackageVersionName
+ );
+
+/**
+ Retrieves a copy of the current firmware image of the device.
+
+ This function allows a copy of the current firmware image to be created and saved.
+ The saved copy could later been used, for example, in firmware image recovery or rollback.
+
+ @param[in] This A pointer to the EFI_FIRMWARE_MANAGEMENT_PROTOCOL instance.
+ @param[in] ImageIndex A unique number identifying the firmware image(s) within the device.
+ The number is between 1 and DescriptorCount.
+ @param[in,out] Image Points to the buffer where the current image is copied to.
+ @param[in,out] ImageSize On entry, points to the size of the buffer pointed to by Image, in bytes.
+ On return, points to the length of the image, in bytes.
+
+ @retval EFI_SUCCESS The device was successfully updated with the new image.
+ @retval EFI_BUFFER_TOO_SMALL The buffer specified by ImageSize is too small to hold the
+ image. The current buffer size needed to hold the image is returned
+ in ImageSize.
+ @retval EFI_INVALID_PARAMETER The Image was NULL.
+ @retval EFI_NOT_FOUND The current image is not copied to the buffer.
+ @retval EFI_UNSUPPORTED The operation is not supported.
+ @retval EFI_SECURITY_VIOLATIO The operation could not be performed due to an authentication failure.
+
+**/
+EFI_STATUS
+EFIAPI
+FmpGetImage (
+ IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This,
+ IN UINT8 ImageIndex,
+ IN OUT VOID *Image,
+ IN OUT UINTN *ImageSize
+ );
+
+/**
+ Updates the firmware image of the device.
+
+ This function updates the hardware with the new firmware image.
+ This function returns EFI_UNSUPPORTED if the firmware image is not updatable.
+ If the firmware image is updatable, the function should perform the following minimal validations
+ before proceeding to do the firmware image update.
+ - Validate the image authentication if image has attribute
+ IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED. The function returns
+ EFI_SECURITY_VIOLATION if the validation fails.
+ - Validate the image is a supported image for this device. The function returns EFI_ABORTED if
+ the image is unsupported. The function can optionally provide more detailed information on
+ why the image is not a supported image.
+ - Validate the data from VendorCode if not null. Image validation must be performed before
+ VendorCode data validation. VendorCode data is ignored or considered invalid if image
+ validation failed. The function returns EFI_ABORTED if the data is invalid.
+
+ VendorCode enables vendor to implement vendor-specific firmware image update policy. Null if
+ the caller did not specify the policy or use the default policy. As an example, vendor can implement
+ a policy to allow an option to force a firmware image update when the abort reason is due to the new
+ firmware image version is older than the current firmware image version or bad image checksum.
+ Sensitive operations such as those wiping the entire firmware image and render the device to be
+ non-functional should be encoded in the image itself rather than passed with the VendorCode.
+ AbortReason enables vendor to have the option to provide a more detailed description of the abort
+ reason to the caller.
+
+ @param[in] This A pointer to the EFI_FIRMWARE_MANAGEMENT_PROTOCOL instance.
+ @param[in] ImageIndex A unique number identifying the firmware image(s) within the device.
+ The number is between 1 and DescriptorCount.
+ @param[in] Image Points to the new image.
+ @param[in] ImageSize Size of the new image in bytes.
+ @param[in] VendorCode This enables vendor to implement vendor-specific firmware image update policy.
+ Null indicates the caller did not specify the policy or use the default policy.
+ @param[in] Progress A function used by the driver to report the progress of the firmware update.
+ @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 device was successfully updated with the new image.
+ @retval EFI_ABORTED The operation is aborted.
+ @retval EFI_INVALID_PARAMETER The Image was NULL.
+ @retval EFI_UNSUPPORTED The operation is not supported.
+ @retval EFI_SECURITY_VIOLATIO The operation could not be performed due to an authentication failure.
+
+**/
+EFI_STATUS
+EFIAPI
+FmpSetImage (
+ IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This,
+ IN UINT8 ImageIndex,
+ IN CONST VOID *Image,
+ IN UINTN ImageSize,
+ IN CONST VOID *VendorCode,
+ IN EFI_FIRMWARE_MANAGEMENT_UPDATE_IMAGE_PROGRESS Progress,
+ OUT CHAR16 **AbortReason
+ );
+
+/**
+ Checks if the firmware image is valid for the device.
+
+ This function allows firmware update application to validate the firmware image without
+ invoking the SetImage() first.
+
+ @param[in] This A pointer to the EFI_FIRMWARE_MANAGEMENT_PROTOCOL instance.
+ @param[in] ImageIndex A unique number identifying the firmware image(s) within the device.
+ The number is between 1 and DescriptorCount.
+ @param[in] Image Points to the new image.
+ @param[in] ImageSize Size of the new image in bytes.
+ @param[out] ImageUpdatable Indicates if the new image is valid for update. It also provides,
+ if available, additional information if the image is invalid.
+
+ @retval EFI_SUCCESS The image was successfully checked.
+ @retval EFI_INVALID_PARAMETER The Image was NULL.
+ @retval EFI_UNSUPPORTED The operation is not supported.
+ @retval EFI_SECURITY_VIOLATIO The operation could not be performed due to an authentication failure.
+
+**/
+EFI_STATUS
+EFIAPI
+FmpCheckImage (
+ IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This,
+ IN UINT8 ImageIndex,
+ IN CONST VOID *Image,
+ IN UINTN ImageSize,
+ OUT UINT32 *ImageUpdatable
+ );
+
+/**
+ Returns information about the firmware package.
+
+ This function returns package information.
+
+ @param[in] This A pointer to the EFI_FIRMWARE_MANAGEMENT_PROTOCOL instance.
+ @param[out] PackageVersion A version number that represents all the firmware images in the device.
+ The format is vendor specific and new version must have a greater value
+ than the old version. If PackageVersion is not supported, the value is
+ 0xFFFFFFFF. A value of 0xFFFFFFFE indicates that package version
+ comparison is to be performed using PackageVersionName. A value of
+ 0xFFFFFFFD indicates that package version update is in progress.
+ @param[out] PackageVersionName A pointer to a pointer to a null-terminated string representing
+ the package version name. 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[out] PackageVersionNameMaxLen The maximum length of package version name if device supports update of
+ package version name. A value of 0 indicates the device does not support
+ update of package version name. Length is the number of Unicode characters,
+ including the terminating null character.
+ @param[out] AttributesSupported Package attributes that are supported by this device. See 'Package Attribute
+ Definitions' for possible returned values of this parameter. A value of 1
+ indicates the attribute is supported and the current setting value is
+ indicated in AttributesSetting. A value of 0 indicates the attribute is not
+ supported and the current setting value in AttributesSetting is meaningless.
+ @param[out] AttributesSetting Package attributes. See 'Package Attribute Definitions' for possible returned
+ values of this parameter
+
+ @retval EFI_SUCCESS The package information was successfully returned.
+ @retval EFI_UNSUPPORTED The operation is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+FmpGetPackageInfo (
+ IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This,
+ OUT UINT32 *PackageVersion,
+ OUT CHAR16 **PackageVersionName,
+ OUT UINT32 *PackageVersionNameMaxLen,
+ OUT UINT64 *AttributesSupported,
+ OUT UINT64 *AttributesSetting
+ );
+
+/**
+ Updates information about the firmware package.
+
+ This function updates package information.
+ This function returns EFI_UNSUPPORTED if the package information is not updatable.
+ VendorCode enables vendor to implement vendor-specific package information update policy.
+ Null if the caller did not specify this policy or use the default policy.
+
+ @param[in] This A pointer to the EFI_FIRMWARE_MANAGEMENT_PROTOCOL instance.
+ @param[in] Image Points to the authentication image.
+ Null if authentication is not required.
+ @param[in] ImageSize Size of the authentication image in bytes.
+ 0 if authentication is not required.
+ @param[in] VendorCode This enables vendor to implement vendor-specific firmware
+ image update policy.
+ Null indicates the caller did not specify this policy or use
+ the default policy.
+ @param[in] PackageVersion The new package version.
+ @param[in] PackageVersionName A pointer to the new null-terminated Unicode string representing
+ the package version name.
+ The string length is equal to or less than the value returned in
+ PackageVersionNameMaxLen.
+
+ @retval EFI_SUCCESS The device was successfully updated with the new package
+ information.
+ @retval EFI_INVALID_PARAMETER The PackageVersionName length is longer than the value
+ returned in PackageVersionNameMaxLen.
+ @retval EFI_UNSUPPORTED The operation is not supported.
+ @retval EFI_SECURITY_VIOLATIO The operation could not be performed due to an authentication failure.
+
+**/
+EFI_STATUS
+EFIAPI
+FmpSetPackageInfo (
+ IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This,
+ IN CONST VOID *Image,
+ IN UINTN ImageSize,
+ IN CONST VOID *VendorCode,
+ IN UINT32 PackageVersion,
+ IN CONST CHAR16 *PackageVersionName
+ );
+
+#endif
+
diff --git a/UefiCpuPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeUpdateDxe.inf b/UefiCpuPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeUpdateDxe.inf
new file mode 100644
index 0000000..437d300
--- /dev/null
+++ b/UefiCpuPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeUpdateDxe.inf
@@ -0,0 +1,68 @@
+## @file
+# Microcode FMP update driver.
+#
+# Produce FMP instance to update Microcode.
+#
+# Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = MicrocodeUpdateDxe
+ MODULE_UNI_FILE = MicrocodeUpdateDxe.uni
+ FILE_GUID = 0565365C-2FE1-4F88-B3BE-624C04623A20
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = MicrocodeFmpMain
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = X64
+#
+
+[Sources]
+ MicrocodeFmp.c
+ MicrocodeUpdate.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ UefiCpuPkg/UefiCpuPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ UefiLib
+ BaseMemoryLib
+ DebugLib
+ PcdLib
+ MemoryAllocationLib
+ UefiBootServicesTableLib
+ HobLib
+ UefiRuntimeServicesTableLib
+ UefiDriverEntryPoint
+ MicrocodeFlashAccessLib
+
+[Guids]
+ gMicrocodeFmpImageTypeIdGuid
+
+[Protocols]
+ gEfiFirmwareManagementProtocolGuid ## SOMTIMES_PRODUCE
+
+[Pcd]
+ gUefiCpuPkgTokenSpaceGuid.PcdCpuMicrocodePatchAddress ## CONSUMES
+ gUefiCpuPkgTokenSpaceGuid.PcdCpuMicrocodePatchRegionSize ## CONSUMES
+
+[Depex]
+ gEfiVariableArchProtocolGuid
+
+[UserExtensions.TianoCore."ExtraFiles"]
+ MicrocodeUpdateDxeExtra.uni
+
diff --git a/UefiCpuPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeUpdateDxe.uni b/UefiCpuPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeUpdateDxe.uni
new file mode 100644
index 0000000..1b0d494
--- /dev/null
+++ b/UefiCpuPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeUpdateDxe.uni
@@ -0,0 +1,21 @@
+// /** @file
+// Microcode FMP update driver.
+//
+// Produce FMP instance to update Microcode.
+//
+// Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+//
+// This program and the accompanying materials
+// are licensed and made available under the terms and conditions of the BSD License
+// which accompanies this distribution. The full text of the license may be found at
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Microcode FMP update driver."
+
+#string STR_MODULE_DESCRIPTION #language en-US "Produce FMP instance to update Microcode."
diff --git a/UefiCpuPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeUpdateDxeExtra.uni b/UefiCpuPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeUpdateDxeExtra.uni
new file mode 100644
index 0000000..b667f12
--- /dev/null
+++ b/UefiCpuPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeUpdateDxeExtra.uni
@@ -0,0 +1,20 @@
+// /** @file
+// MicrocodeUpdateDxe Localized Strings and Content
+//
+// Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+//
+// This program and the accompanying materials
+// are licensed and made available under the terms and conditions of the BSD License
+// which accompanies this distribution. The full text of the license may be found at
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"MicrocodeUpdate DXE Driver"
+
+