audk/FmpDevicePkg/FmpDxe/Dependency.c

680 lines
18 KiB
C
Raw Normal View History

FmdDevicePkg/FmpDxe: Support Fmp Capsule Dependency. REF: https://bugzilla.tianocore.org/show_bug.cgi?id=2421 Capsule Dependency is an incremental change of Fmp Capsule Update. The capsule format is extended to include a set of binary encoded dependency expression. The dependency expression is signed together with the Fmp payload and evaluated before update is applied. This feature is defined in UEFI Spec 2.8. The dependency evaluation has two steps: 1. Validate platform existing Fmp images' version satisfy the dependency expression in capsule image. 2. Validate the capsule image version satisfy all the platform existing Fmp image's dependency expression. If the dependency expression evaluates to FALSE, then the capsule update fails and last attempt status is set to LAST_ATTEMPT_STATUS_ERROR_UNSATISFIED_DEPENDENCIES. The dependency saving and getting is FmpDeviceLib implementation scope. The parameter "Image" of FmpDeviceSetImage and FmpDeviceGetImage function is extended to contain the dependency. The layout: +--------------------------+ | Dependency Op-codes | +--------------------------+ | Fmp Payload Image | +--------------------------+ 1. FmpDeviceSetImage is responsible for retrieving the dependency from the parameter "Image" and saving it to a protected storage. 2. FmpDeviceGetImage is responsible for retrieving the dependency from the storage where FmpDeviceSetImage saves dependency and combining it with the Fmp Payload Image into one buffer which is returned to the caller. This dependency will be populated into EFI_FIRMWARE_IMAGE_DESCRIPTOR and used for dependency evaluation. 3. FmpDeviceGetAttributes must set the bit IMAGE_ATTRIBUTE_DEPENDENCY to indicate the Fmp device supports Fmp Capsule Dependency feature. Cc: Michael D Kinney <michael.d.kinney@intel.com> Cc: Liming Gao <liming.gao@intel.com> Signed-off-by: Wei6 Xu <wei6.xu@intel.com> Reviewed-by: Liming Gao <liming.gao@intel.com>
2020-01-10 06:34:54 +01:00
/** @file
Supports Capsule Dependency Expression.
Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "FmpDxe.h"
#include "Dependency.h"
//
// Define the initial size of the dependency expression evaluation stack
//
#define DEPEX_STACK_SIZE_INCREMENT 0x1000
//
// Type of stack element
//
typedef enum {
BooleanType,
VersionType
} ELEMENT_TYPE;
//
// Value of stack element
//
typedef union {
BOOLEAN Boolean;
UINT32 Version;
} ELEMENT_VALUE;
//
// Stack element used to evaluate dependency expressions
//
typedef struct {
ELEMENT_VALUE Value;
ELEMENT_TYPE Type;
} DEPEX_ELEMENT;
//
// Global variable used to support dependency evaluation
//
UINTN mNumberOfFmpInstance = 0;
EFI_FIRMWARE_IMAGE_DESCRIPTOR **mFmpImageInfoBuf = NULL;
//
// Indicates the status of dependency check, default value is DEPENDENCIES_SATISFIED.
//
UINT8 mDependenciesCheckStatus = DEPENDENCIES_SATISFIED;
//
// Global stack used to evaluate dependency expressions
//
DEPEX_ELEMENT *mDepexEvaluationStack = NULL;
DEPEX_ELEMENT *mDepexEvaluationStackEnd = NULL;
DEPEX_ELEMENT *mDepexEvaluationStackPointer = NULL;
/**
Grow size of the Depex stack
@retval EFI_SUCCESS Stack successfully growed.
@retval EFI_OUT_OF_RESOURCES There is not enough system memory to grow the stack.
**/
EFI_STATUS
GrowDepexStack (
VOID
)
{
DEPEX_ELEMENT *NewStack;
UINTN Size;
Size = DEPEX_STACK_SIZE_INCREMENT;
if (mDepexEvaluationStack != NULL) {
Size = Size + (mDepexEvaluationStackEnd - mDepexEvaluationStack);
}
NewStack = AllocatePool (Size * sizeof (DEPEX_ELEMENT));
if (NewStack == NULL) {
return EFI_OUT_OF_RESOURCES;
}
if (mDepexEvaluationStack != NULL) {
//
// Copy to Old Stack to the New Stack
//
CopyMem (
NewStack,
mDepexEvaluationStack,
(mDepexEvaluationStackEnd - mDepexEvaluationStack) * sizeof (DEPEX_ELEMENT)
);
//
// Free The Old Stack
//
FreePool (mDepexEvaluationStack);
}
//
// Make the Stack pointer point to the old data in the new stack
//
mDepexEvaluationStackPointer = NewStack + (mDepexEvaluationStackPointer - mDepexEvaluationStack);
mDepexEvaluationStack = NewStack;
mDepexEvaluationStackEnd = NewStack + Size;
return EFI_SUCCESS;
}
/**
Push an element onto the Stack.
@param[in] Value Value to push.
@param[in] Type Element Type
@retval EFI_SUCCESS The value was pushed onto the stack.
@retval EFI_OUT_OF_RESOURCES There is not enough system memory to grow the stack.
@retval EFI_INVALID_PARAMETER Wrong stack element type.
**/
EFI_STATUS
Push (
IN UINT32 Value,
IN UINTN Type
)
{
EFI_STATUS Status;
DEPEX_ELEMENT Element;
//
// Check Type
//
if (Type != BooleanType && Type != VersionType) {
return EFI_INVALID_PARAMETER;
}
//
// Check for a stack overflow condition
//
if (mDepexEvaluationStackPointer == mDepexEvaluationStackEnd) {
//
// Grow the stack
//
Status = GrowDepexStack ();
if (EFI_ERROR (Status)) {
return Status;
}
}
Element.Value.Version = Value;
Element.Type = Type;
//
// Push the item onto the stack
//
*mDepexEvaluationStackPointer = Element;
mDepexEvaluationStackPointer++;
return EFI_SUCCESS;
}
/**
Pop an element from the stack.
@param[in] Value Element to pop.
@param[in] Type Type of element.
@retval EFI_SUCCESS The value was popped onto the stack.
@retval EFI_ACCESS_DENIED The pop operation underflowed the stack.
@retval EFI_INVALID_PARAMETER Type is mismatched.
**/
EFI_STATUS
Pop (
OUT DEPEX_ELEMENT *Element,
IN ELEMENT_TYPE Type
)
{
//
// Check for a stack underflow condition
//
if (mDepexEvaluationStackPointer == mDepexEvaluationStack) {
return EFI_ACCESS_DENIED;
}
//
// Pop the item off the stack
//
mDepexEvaluationStackPointer--;
*Element = *mDepexEvaluationStackPointer;
if ((*Element).Type != Type) {
return EFI_INVALID_PARAMETER;
}
return EFI_SUCCESS;
}
/**
Evaluate the dependencies.
@param[in] Dependencies Dependency expressions.
@param[in] DependenciesSize Size of Dependency expressions.
@retval TRUE Dependency expressions evaluate to TRUE.
@retval FALSE Dependency expressions evaluate to FALSE.
**/
BOOLEAN
EvaluateDependencies (
IN CONST EFI_FIRMWARE_IMAGE_DEP * Dependencies,
IN CONST UINTN DependenciesSize
)
{
EFI_STATUS Status;
UINT8 *Iterator;
UINT8 Index;
DEPEX_ELEMENT Element1;
DEPEX_ELEMENT Element2;
GUID ImageTypeId;
UINT32 Version;
if (Dependencies == NULL || DependenciesSize == 0) {
return FALSE;
}
//
// Clean out memory leaks in Depex Boolean stack. Leaks are only caused by
// incorrectly formed DEPEX expressions
//
mDepexEvaluationStackPointer = mDepexEvaluationStack;
Iterator = (UINT8 *) Dependencies->Dependencies;
while (Iterator < (UINT8 *) Dependencies->Dependencies + DependenciesSize) {
switch (*Iterator)
{
case EFI_FMP_DEP_PUSH_GUID:
if (Iterator + sizeof (EFI_GUID) >= (UINT8 *) Dependencies->Dependencies + DependenciesSize) {
Status = EFI_INVALID_PARAMETER;
goto Error;
}
CopyGuid (&ImageTypeId, (EFI_GUID *) (Iterator + 1));
Iterator = Iterator + sizeof (EFI_GUID);
for (Index = 0; Index < mNumberOfFmpInstance; Index ++){
if (mFmpImageInfoBuf[Index] == NULL) {
continue;
}
if(CompareGuid (&mFmpImageInfoBuf[Index]->ImageTypeId, &ImageTypeId)){
Status = Push (mFmpImageInfoBuf[Index]->Version, VersionType);
if (EFI_ERROR (Status)) {
goto Error;
}
break;
}
}
if (Index == mNumberOfFmpInstance) {
Status = EFI_NOT_FOUND;
goto Error;
}
break;
case EFI_FMP_DEP_PUSH_VERSION:
if (Iterator + sizeof (UINT32) >= (UINT8 *) Dependencies->Dependencies + DependenciesSize ) {
Status = EFI_INVALID_PARAMETER;
goto Error;
}
Version = *(UINT32 *) (Iterator + 1);
Status = Push (Version, VersionType);
if (EFI_ERROR (Status)) {
goto Error;
}
Iterator = Iterator + sizeof (UINT32);
break;
case EFI_FMP_DEP_VERSION_STR:
Iterator += AsciiStrnLenS ((CHAR8 *) Iterator, DependenciesSize - (Iterator - Dependencies->Dependencies));
break;
case EFI_FMP_DEP_AND:
Status = Pop (&Element1, BooleanType);
if (EFI_ERROR (Status)) {
goto Error;
}
Status = Pop (&Element2, BooleanType);
if (EFI_ERROR (Status)) {
goto Error;
}
Status = Push (Element1.Value.Boolean & Element2.Value.Boolean, BooleanType);
if (EFI_ERROR (Status)) {
goto Error;
}
break;
case EFI_FMP_DEP_OR:
Status = Pop (&Element1, BooleanType);
if (EFI_ERROR (Status)) {
goto Error;
}
Status = Pop(&Element2, BooleanType);
if (EFI_ERROR (Status)) {
goto Error;
}
Status = Push (Element1.Value.Boolean | Element2.Value.Boolean, BooleanType);
if (EFI_ERROR (Status)) {
goto Error;
}
break;
case EFI_FMP_DEP_NOT:
Status = Pop (&Element1, BooleanType);
if (EFI_ERROR (Status)) {
goto Error;
}
Status = Push (!(Element1.Value.Boolean), BooleanType);
if (EFI_ERROR (Status)) {
goto Error;
}
break;
case EFI_FMP_DEP_TRUE:
Status = Push (TRUE, BooleanType);
if (EFI_ERROR (Status)) {
goto Error;
}
break;
case EFI_FMP_DEP_FALSE:
Status = Push (FALSE, BooleanType);
if (EFI_ERROR (Status)) {
goto Error;
}
break;
case EFI_FMP_DEP_EQ:
Status = Pop (&Element1, VersionType);
if (EFI_ERROR (Status)) {
goto Error;
}
Status = Pop (&Element2, VersionType);
if (EFI_ERROR (Status)) {
goto Error;
}
Status = (Element1.Value.Version == Element2.Value.Version) ? Push (TRUE, BooleanType) : Push (FALSE, BooleanType);
if (EFI_ERROR (Status)) {
goto Error;
}
break;
case EFI_FMP_DEP_GT:
Status = Pop (&Element1, VersionType);
if (EFI_ERROR (Status)) {
goto Error;
}
Status = Pop (&Element2, VersionType);
if (EFI_ERROR (Status)) {
goto Error;
}
Status = (Element1.Value.Version > Element2.Value.Version) ? Push (TRUE, BooleanType) : Push (FALSE, BooleanType);
if (EFI_ERROR (Status)) {
goto Error;
}
break;
case EFI_FMP_DEP_GTE:
Status = Pop (&Element1, VersionType);
if (EFI_ERROR (Status)) {
goto Error;
}
Status = Pop (&Element2, VersionType);
if (EFI_ERROR (Status)) {
goto Error;
}
Status = (Element1.Value.Version >= Element2.Value.Version) ? Push (TRUE, BooleanType) : Push (FALSE, BooleanType);
if (EFI_ERROR (Status)) {
goto Error;
}
break;
case EFI_FMP_DEP_LT:
Status = Pop (&Element1, VersionType);
if (EFI_ERROR (Status)) {
goto Error;
}
Status = Pop (&Element2, VersionType);
if (EFI_ERROR (Status)) {
goto Error;
}
Status = (Element1.Value.Version < Element2.Value.Version) ? Push (TRUE, BooleanType) : Push (FALSE, BooleanType);
if (EFI_ERROR (Status)) {
goto Error;
}
break;
case EFI_FMP_DEP_LTE:
Status = Pop (&Element1, VersionType);
if (EFI_ERROR (Status)) {
goto Error;
}
Status = Pop (&Element2, VersionType);
if (EFI_ERROR (Status)) {
goto Error;
}
Status = (Element1.Value.Version <= Element2.Value.Version) ? Push (TRUE, BooleanType) : Push (FALSE, BooleanType);
if (EFI_ERROR (Status)) {
goto Error;
}
break;
case EFI_FMP_DEP_END:
Status = Pop (&Element1, BooleanType);
if (EFI_ERROR (Status)) {
goto Error;
}
return Element1.Value.Boolean;
default:
Status = EFI_INVALID_PARAMETER;
goto Error;
}
Iterator++;
}
Error:
DEBUG ((DEBUG_ERROR, "FmpDxe(%s): EvaluateDependencies() - RESULT = FALSE (Status = %r)\n", mImageIdName, Status));
return FALSE;
}
/**
Validate the dependency expression and output its size.
@param[in] ImageDepex Pointer to the EFI_FIRMWARE_IMAGE_DEP.
@param[in] MaxDepexSize Max size of the dependency.
@param[out] DepexSize Size of dependency.
@retval TRUE The capsule is valid.
@retval FALSE The capsule is invalid.
**/
BOOLEAN
ValidateImageDepex (
IN EFI_FIRMWARE_IMAGE_DEP *ImageDepex,
IN CONST UINTN MaxDepexSize,
OUT UINT32 *DepexSize
)
{
UINT8 *Depex;
*DepexSize = 0;
Depex = ImageDepex->Dependencies;
while (Depex < ImageDepex->Dependencies + MaxDepexSize) {
switch (*Depex)
{
case EFI_FMP_DEP_PUSH_GUID:
Depex += sizeof (EFI_GUID) + 1;
break;
case EFI_FMP_DEP_PUSH_VERSION:
Depex += sizeof (UINT32) + 1;
break;
case EFI_FMP_DEP_VERSION_STR:
Depex += AsciiStrnLenS ((CHAR8 *) Depex, ImageDepex->Dependencies + MaxDepexSize - Depex) + 1;
break;
case EFI_FMP_DEP_AND:
case EFI_FMP_DEP_OR:
case EFI_FMP_DEP_NOT:
case EFI_FMP_DEP_TRUE:
case EFI_FMP_DEP_FALSE:
case EFI_FMP_DEP_EQ:
case EFI_FMP_DEP_GT:
case EFI_FMP_DEP_GTE:
case EFI_FMP_DEP_LT:
case EFI_FMP_DEP_LTE:
Depex += 1;
break;
case EFI_FMP_DEP_END:
Depex += 1;
*DepexSize = (UINT32)(Depex - ImageDepex->Dependencies);
return TRUE;
default:
return FALSE;
}
}
return FALSE;
}
/**
Get the size of dependencies. Assume the dependencies is validated before
calling this function.
@param[in] Dependencies Pointer to the EFI_FIRMWARE_IMAGE_DEP.
@retval The size of dependencies.
**/
UINTN
GetDepexSize (
IN CONST EFI_FIRMWARE_IMAGE_DEP *Dependencies
)
{
UINTN Index;
if (Dependencies == NULL) {
return 0;
}
Index = 0;
while (Dependencies->Dependencies[Index] != EFI_FMP_DEP_END) {
Index ++;
}
return Index + 1;
}
/**
Check dependency for firmware update.
@param[in] ImageTypeId Image Type Id.
@param[in] Version New version.
@param[in] Dependencies The dependencies.
@param[in] DependenciesSize Size of the dependencies
@param[out] IsSatisfied Indicate the dependencies is satisfied or not.
@retval EFI_SUCCESS Dependency Evaluation is successful.
@retval Others Dependency Evaluation fails with unexpected error.
**/
EFI_STATUS
EvaluateImageDependencies (
IN CONST EFI_GUID ImageTypeId,
IN CONST UINT32 Version,
IN CONST EFI_FIRMWARE_IMAGE_DEP *Dependencies,
IN CONST UINT32 DependenciesSize,
OUT BOOLEAN *IsSatisfied
)
{
EFI_STATUS Status;
EFI_HANDLE *HandleBuffer;
UINTN Index;
EFI_FIRMWARE_MANAGEMENT_PROTOCOL *Fmp;
UINTN ImageInfoSize;
UINT32 FmpImageInfoDescriptorVer;
UINT8 FmpImageInfoCount;
UINTN DescriptorSize;
UINT32 PackageVersion;
CHAR16 *PackageVersionName;
UINTN DepexSize;
*IsSatisfied = TRUE;
PackageVersionName = NULL;
//
// Get ImageDescriptors of all FMP instances, and archive them for depex evaluation.
//
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiFirmwareManagementProtocolGuid,
NULL,
&mNumberOfFmpInstance,
&HandleBuffer
);
if (EFI_ERROR (Status)) {
return EFI_ABORTED;
}
mFmpImageInfoBuf = AllocatePool (sizeof(EFI_FIRMWARE_IMAGE_DESCRIPTOR *) * mNumberOfFmpInstance);
if (mFmpImageInfoBuf == NULL) {
return EFI_OUT_OF_RESOURCES;
}
for (Index = 0; Index < mNumberOfFmpInstance; Index ++) {
Status = gBS->HandleProtocol (
HandleBuffer[Index],
&gEfiFirmwareManagementProtocolGuid,
(VOID **) &Fmp
);
if (EFI_ERROR(Status)) {
continue;
}
ImageInfoSize = 0;
Status = Fmp->GetImageInfo (
Fmp,
&ImageInfoSize,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
);
if (Status != EFI_BUFFER_TOO_SMALL) {
continue;
}
mFmpImageInfoBuf[Index] = AllocateZeroPool (ImageInfoSize);
if (mFmpImageInfoBuf[Index] == NULL) {
continue;
}
Status = Fmp->GetImageInfo (
Fmp,
&ImageInfoSize, // ImageInfoSize
mFmpImageInfoBuf[Index], // ImageInfo
&FmpImageInfoDescriptorVer, // DescriptorVersion
&FmpImageInfoCount, // DescriptorCount
&DescriptorSize, // DescriptorSize
&PackageVersion, // PackageVersion
&PackageVersionName // PackageVersionName
);
if (EFI_ERROR(Status)) {
FreePool (mFmpImageInfoBuf[Index]);
mFmpImageInfoBuf[Index] = NULL;
continue;
}
if (PackageVersionName != NULL) {
FreePool (PackageVersionName);
PackageVersionName = NULL;
}
}
//
// Step 1 - Evaluate firmware image's depex, against the version of other Fmp instances.
//
if (Dependencies != NULL) {
*IsSatisfied = EvaluateDependencies (Dependencies, DependenciesSize);
}
if (!*IsSatisfied) {
goto cleanup;
}
//
// Step 2 - Evaluate the depex of all other Fmp instances, against the new version in
// the firmware image.
//
//
// Update the new version to mFmpImageInfoBuf.
//
for (Index = 0; Index < mNumberOfFmpInstance; Index ++) {
if (mFmpImageInfoBuf[Index] != NULL) {
if (CompareGuid (&ImageTypeId, &mFmpImageInfoBuf[Index]->ImageTypeId)) {
mFmpImageInfoBuf[Index]->Version = Version;
break;
}
}
}
//
// Evaluate the Dependencies one by one.
//
for (Index = 0; Index < mNumberOfFmpInstance; Index ++) {
if (mFmpImageInfoBuf[Index] != NULL) {
//
// Skip the Fmp instance to be "SetImage".
//
if (CompareGuid (&ImageTypeId, &mFmpImageInfoBuf[Index]->ImageTypeId)) {
continue;
}
if ((mFmpImageInfoBuf[Index]->AttributesSupported & IMAGE_ATTRIBUTE_DEPENDENCY) &&
mFmpImageInfoBuf[Index]->Dependencies != NULL) {
//
// Get the size of depex.
// Assume that the dependencies in EFI_FIRMWARE_IMAGE_DESCRIPTOR is validated when PopulateDescriptor().
//
DepexSize = GetDepexSize (mFmpImageInfoBuf[Index]->Dependencies);
if (DepexSize > 0) {
*IsSatisfied = EvaluateDependencies (mFmpImageInfoBuf[Index]->Dependencies, DepexSize);
if (!*IsSatisfied) {
break;
}
}
}
}
}
cleanup:
if (mFmpImageInfoBuf != NULL) {
for (Index = 0; Index < mNumberOfFmpInstance; Index ++) {
if (mFmpImageInfoBuf[Index] != NULL) {
FreePool (mFmpImageInfoBuf[Index]);
}
}
FreePool (mFmpImageInfoBuf);
}
return EFI_SUCCESS;
}