/** @file | |
Sample platform variable cleanup library implementation. | |
Copyright (c) 2015 - 2017, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "PlatVarCleanup.h" | |
VAR_ERROR_FLAG mLastVarErrorFlag = VAR_ERROR_FLAG_NO_ERROR; | |
EDKII_VAR_CHECK_PROTOCOL *mVarCheck = NULL; | |
/// | |
/// The flag to indicate whether the platform has left the DXE phase of execution. | |
/// | |
BOOLEAN mEndOfDxe = FALSE; | |
EFI_EVENT mPlatVarCleanupLibEndOfDxeEvent = NULL; | |
LIST_ENTRY mUserVariableList = INITIALIZE_LIST_HEAD_VARIABLE (mUserVariableList); | |
UINT16 mUserVariableCount = 0; | |
UINT16 mMarkedUserVariableCount = 0; | |
EFI_GUID mVariableCleanupHiiGuid = VARIABLE_CLEANUP_HII_GUID; | |
CHAR16 mVarStoreName[] = L"VariableCleanup"; | |
HII_VENDOR_DEVICE_PATH mVarCleanupHiiVendorDevicePath = { | |
{ | |
{ | |
HARDWARE_DEVICE_PATH, | |
HW_VENDOR_DP, | |
{ | |
(UINT8)(sizeof (VENDOR_DEVICE_PATH)), | |
(UINT8)((sizeof (VENDOR_DEVICE_PATH)) >> 8) | |
} | |
}, | |
VARIABLE_CLEANUP_HII_GUID | |
}, | |
{ | |
END_DEVICE_PATH_TYPE, | |
END_ENTIRE_DEVICE_PATH_SUBTYPE, | |
{ | |
(UINT8)(sizeof (EFI_DEVICE_PATH_PROTOCOL)), | |
(UINT8)((sizeof (EFI_DEVICE_PATH_PROTOCOL)) >> 8) | |
} | |
} | |
}; | |
/** | |
Internal get variable error flag. | |
@return Variable error flag. | |
**/ | |
VAR_ERROR_FLAG | |
InternalGetVarErrorFlag ( | |
VOID | |
) | |
{ | |
EFI_STATUS Status; | |
UINTN Size; | |
VAR_ERROR_FLAG ErrorFlag; | |
Size = sizeof (ErrorFlag); | |
Status = gRT->GetVariable ( | |
VAR_ERROR_FLAG_NAME, | |
&gEdkiiVarErrorFlagGuid, | |
NULL, | |
&Size, | |
&ErrorFlag | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_INFO, "%s - not found\n", VAR_ERROR_FLAG_NAME)); | |
return VAR_ERROR_FLAG_NO_ERROR; | |
} | |
return ErrorFlag; | |
} | |
/** | |
Is user variable? | |
@param[in] Name Pointer to variable name. | |
@param[in] Guid Pointer to vendor guid. | |
@retval TRUE User variable. | |
@retval FALSE System variable. | |
**/ | |
BOOLEAN | |
IsUserVariable ( | |
IN CHAR16 *Name, | |
IN EFI_GUID *Guid | |
) | |
{ | |
EFI_STATUS Status; | |
VAR_CHECK_VARIABLE_PROPERTY Property; | |
if (mVarCheck == NULL) { | |
gBS->LocateProtocol ( | |
&gEdkiiVarCheckProtocolGuid, | |
NULL, | |
(VOID **)&mVarCheck | |
); | |
} | |
ASSERT (mVarCheck != NULL); | |
ZeroMem (&Property, sizeof (Property)); | |
Status = mVarCheck->VariablePropertyGet ( | |
Name, | |
Guid, | |
&Property | |
); | |
if (EFI_ERROR (Status)) { | |
// | |
// No property, it is user variable. | |
// | |
DEBUG ((DEBUG_INFO, "PlatformVarCleanup - User variable: %g:%s\n", Guid, Name)); | |
return TRUE; | |
} | |
// DEBUG ((DEBUG_INFO, "PlatformVarCleanup - Variable Property: %g:%s\n", Guid, Name)); | |
// DEBUG ((DEBUG_INFO, " Revision - 0x%04x\n", Property.Revision)); | |
// DEBUG ((DEBUG_INFO, " Property - 0x%04x\n", Property.Property)); | |
// DEBUG ((DEBUG_INFO, " Attribute - 0x%08x\n", Property.Attributes)); | |
// DEBUG ((DEBUG_INFO, " MinSize - 0x%x\n", Property.MinSize)); | |
// DEBUG ((DEBUG_INFO, " MaxSize - 0x%x\n", Property.MaxSize)); | |
return FALSE; | |
} | |
/** | |
Find user variable node by variable GUID. | |
@param[in] Guid Pointer to vendor guid. | |
@return Pointer to user variable node. | |
**/ | |
USER_VARIABLE_NODE * | |
FindUserVariableNodeByGuid ( | |
IN EFI_GUID *Guid | |
) | |
{ | |
USER_VARIABLE_NODE *UserVariableNode; | |
LIST_ENTRY *Link; | |
for (Link = mUserVariableList.ForwardLink | |
; Link != &mUserVariableList | |
; Link = Link->ForwardLink) | |
{ | |
UserVariableNode = USER_VARIABLE_FROM_LINK (Link); | |
if (CompareGuid (Guid, &UserVariableNode->Guid)) { | |
// | |
// Found it. | |
// | |
return UserVariableNode; | |
} | |
} | |
// | |
// Create new one if not found. | |
// | |
UserVariableNode = AllocateZeroPool (sizeof (*UserVariableNode)); | |
ASSERT (UserVariableNode != NULL); | |
UserVariableNode->Signature = USER_VARIABLE_NODE_SIGNATURE; | |
CopyGuid (&UserVariableNode->Guid, Guid); | |
// | |
// (36 chars of "########-####-####-####-############" + 1 space + 1 terminator) * sizeof (CHAR16). | |
// | |
UserVariableNode->PromptString = AllocatePool ((36 + 2) * sizeof (CHAR16)); | |
ASSERT (UserVariableNode->PromptString != NULL); | |
UnicodeSPrint (UserVariableNode->PromptString, (36 + 2) * sizeof (CHAR16), L" %g", &UserVariableNode->Guid); | |
InitializeListHead (&UserVariableNode->NameLink); | |
InsertTailList (&mUserVariableList, &UserVariableNode->Link); | |
return UserVariableNode; | |
} | |
/** | |
Create user variable node. | |
**/ | |
VOID | |
CreateUserVariableNode ( | |
VOID | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_STATUS GetVariableStatus; | |
CHAR16 *VarName; | |
UINTN MaxVarNameSize; | |
UINTN VarNameSize; | |
UINTN MaxDataSize; | |
UINTN DataSize; | |
VOID *Data; | |
UINT32 Attributes; | |
EFI_GUID Guid; | |
USER_VARIABLE_NODE *UserVariableNode; | |
USER_VARIABLE_NAME_NODE *UserVariableNameNode; | |
UINT16 Index; | |
UINTN StringSize; | |
// | |
// Initialize 128 * sizeof (CHAR16) variable name size. | |
// | |
MaxVarNameSize = 128 * sizeof (CHAR16); | |
VarName = AllocateZeroPool (MaxVarNameSize); | |
ASSERT (VarName != NULL); | |
// | |
// Initialize 0x1000 variable data size. | |
// | |
MaxDataSize = 0x1000; | |
Data = AllocateZeroPool (MaxDataSize); | |
ASSERT (Data != NULL); | |
Index = 0; | |
do { | |
VarNameSize = MaxVarNameSize; | |
Status = gRT->GetNextVariableName (&VarNameSize, VarName, &Guid); | |
if (Status == EFI_BUFFER_TOO_SMALL) { | |
VarName = ReallocatePool (MaxVarNameSize, VarNameSize, VarName); | |
ASSERT (VarName != NULL); | |
MaxVarNameSize = VarNameSize; | |
Status = gRT->GetNextVariableName (&VarNameSize, VarName, &Guid); | |
} | |
if (!EFI_ERROR (Status)) { | |
if (IsUserVariable (VarName, &Guid)) { | |
DataSize = MaxDataSize; | |
GetVariableStatus = gRT->GetVariable (VarName, &Guid, &Attributes, &DataSize, Data); | |
if (GetVariableStatus == EFI_BUFFER_TOO_SMALL) { | |
Data = ReallocatePool (MaxDataSize, DataSize, Data); | |
ASSERT (Data != NULL); | |
MaxDataSize = DataSize; | |
GetVariableStatus = gRT->GetVariable (VarName, &Guid, &Attributes, &DataSize, Data); | |
} | |
ASSERT_EFI_ERROR (GetVariableStatus); | |
if ((Attributes & EFI_VARIABLE_NON_VOLATILE) != 0) { | |
UserVariableNode = FindUserVariableNodeByGuid (&Guid); | |
ASSERT (UserVariableNode != NULL); | |
// | |
// Different variables that have same variable GUID share same user variable node. | |
// | |
UserVariableNameNode = AllocateZeroPool (sizeof (*UserVariableNameNode)); | |
ASSERT (UserVariableNameNode != NULL); | |
UserVariableNameNode->Signature = USER_VARIABLE_NAME_NODE_SIGNATURE; | |
UserVariableNameNode->Name = AllocateCopyPool (VarNameSize, VarName); | |
UserVariableNameNode->Attributes = Attributes; | |
UserVariableNameNode->DataSize = DataSize; | |
UserVariableNameNode->Index = Index; | |
UserVariableNameNode->QuestionId = (EFI_QUESTION_ID)(USER_VARIABLE_QUESTION_ID + Index); | |
// | |
// 2 space * sizeof (CHAR16) + StrSize. | |
// | |
StringSize = 2 * sizeof (CHAR16) + StrSize (UserVariableNameNode->Name); | |
UserVariableNameNode->PromptString = AllocatePool (StringSize); | |
ASSERT (UserVariableNameNode->PromptString != NULL); | |
UnicodeSPrint (UserVariableNameNode->PromptString, StringSize, L" %s", UserVariableNameNode->Name); | |
// | |
// (33 chars of "Attribtues = 0x and DataSize = 0x" + 1 terminator + (sizeof (UINT32) + sizeof (UINTN)) * 2) * sizeof (CHAR16). | |
// | |
StringSize = (33 + 1 + (sizeof (UINT32) + sizeof (UINTN)) * 2) * sizeof (CHAR16); | |
UserVariableNameNode->HelpString = AllocatePool (StringSize); | |
ASSERT (UserVariableNameNode->HelpString != NULL); | |
UnicodeSPrint (UserVariableNameNode->HelpString, StringSize, L"Attribtues = 0x%08x and DataSize = 0x%x", UserVariableNameNode->Attributes, UserVariableNameNode->DataSize); | |
UserVariableNameNode->Deleted = FALSE; | |
InsertTailList (&UserVariableNode->NameLink, &UserVariableNameNode->Link); | |
Index++; | |
} | |
} | |
} | |
} while (Status != EFI_NOT_FOUND); | |
mUserVariableCount = Index; | |
ASSERT (mUserVariableCount <= MAX_USER_VARIABLE_COUNT); | |
DEBUG ((DEBUG_INFO, "PlatformVarCleanup - User variable count: 0x%04x\n", mUserVariableCount)); | |
FreePool (VarName); | |
FreePool (Data); | |
} | |
/** | |
Destroy user variable nodes. | |
**/ | |
VOID | |
DestroyUserVariableNode ( | |
VOID | |
) | |
{ | |
USER_VARIABLE_NODE *UserVariableNode; | |
LIST_ENTRY *Link; | |
USER_VARIABLE_NAME_NODE *UserVariableNameNode; | |
LIST_ENTRY *NameLink; | |
while (mUserVariableList.ForwardLink != &mUserVariableList) { | |
Link = mUserVariableList.ForwardLink; | |
UserVariableNode = USER_VARIABLE_FROM_LINK (Link); | |
RemoveEntryList (&UserVariableNode->Link); | |
while (UserVariableNode->NameLink.ForwardLink != &UserVariableNode->NameLink) { | |
NameLink = UserVariableNode->NameLink.ForwardLink; | |
UserVariableNameNode = USER_VARIABLE_NAME_FROM_LINK (NameLink); | |
RemoveEntryList (&UserVariableNameNode->Link); | |
FreePool (UserVariableNameNode->Name); | |
FreePool (UserVariableNameNode->PromptString); | |
FreePool (UserVariableNameNode->HelpString); | |
FreePool (UserVariableNameNode); | |
} | |
FreePool (UserVariableNode->PromptString); | |
FreePool (UserVariableNode); | |
} | |
} | |
/** | |
Create a time based data payload by concatenating the EFI_VARIABLE_AUTHENTICATION_2 | |
descriptor with the input data. NO authentication is required in this function. | |
@param[in, out] DataSize On input, the size of Data buffer in bytes. | |
On output, the size of data returned in Data | |
buffer in bytes. | |
@param[in, out] Data On input, Pointer to data buffer to be wrapped or | |
pointer to NULL to wrap an empty payload. | |
On output, Pointer to the new payload date buffer allocated from pool, | |
it's caller's responsibility to free the memory after using it. | |
@retval EFI_SUCCESS Create time based payload successfully. | |
@retval EFI_OUT_OF_RESOURCES There are not enough memory resourses to create time based payload. | |
@retval EFI_INVALID_PARAMETER The parameter is invalid. | |
@retval Others Unexpected error happens. | |
**/ | |
EFI_STATUS | |
CreateTimeBasedPayload ( | |
IN OUT UINTN *DataSize, | |
IN OUT UINT8 **Data | |
) | |
{ | |
EFI_STATUS Status; | |
UINT8 *NewData; | |
UINT8 *Payload; | |
UINTN PayloadSize; | |
EFI_VARIABLE_AUTHENTICATION_2 *DescriptorData; | |
UINTN DescriptorSize; | |
EFI_TIME Time; | |
if ((Data == NULL) || (DataSize == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// At user physical presence, the variable does not need to be signed but the | |
// parameters to the SetVariable() call still need to be prepared as authenticated | |
// variable. So we create EFI_VARIABLE_AUTHENTICATED_2 descriptor without certificate | |
// data in it. | |
// | |
Payload = *Data; | |
PayloadSize = *DataSize; | |
DescriptorSize = OFFSET_OF (EFI_VARIABLE_AUTHENTICATION_2, AuthInfo) + OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData); | |
NewData = (UINT8 *)AllocateZeroPool (DescriptorSize + PayloadSize); | |
if (NewData == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
if ((Payload != NULL) && (PayloadSize != 0)) { | |
CopyMem (NewData + DescriptorSize, Payload, PayloadSize); | |
} | |
DescriptorData = (EFI_VARIABLE_AUTHENTICATION_2 *)(NewData); | |
ZeroMem (&Time, sizeof (EFI_TIME)); | |
Status = gRT->GetTime (&Time, NULL); | |
if (EFI_ERROR (Status)) { | |
FreePool (NewData); | |
return Status; | |
} | |
Time.Pad1 = 0; | |
Time.Nanosecond = 0; | |
Time.TimeZone = 0; | |
Time.Daylight = 0; | |
Time.Pad2 = 0; | |
CopyMem (&DescriptorData->TimeStamp, &Time, sizeof (EFI_TIME)); | |
DescriptorData->AuthInfo.Hdr.dwLength = OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData); | |
DescriptorData->AuthInfo.Hdr.wRevision = 0x0200; | |
DescriptorData->AuthInfo.Hdr.wCertificateType = WIN_CERT_TYPE_EFI_GUID; | |
CopyGuid (&DescriptorData->AuthInfo.CertType, &gEfiCertPkcs7Guid); | |
if (Payload != NULL) { | |
FreePool (Payload); | |
} | |
*DataSize = DescriptorSize + PayloadSize; | |
*Data = NewData; | |
return EFI_SUCCESS; | |
} | |
/** | |
Create a counter based data payload by concatenating the EFI_VARIABLE_AUTHENTICATION | |
descriptor with the input data. NO authentication is required in this function. | |
@param[in, out] DataSize On input, the size of Data buffer in bytes. | |
On output, the size of data returned in Data | |
buffer in bytes. | |
@param[in, out] Data On input, Pointer to data buffer to be wrapped or | |
pointer to NULL to wrap an empty payload. | |
On output, Pointer to the new payload date buffer allocated from pool, | |
it's caller's responsibility to free the memory after using it. | |
@retval EFI_SUCCESS Create counter based payload successfully. | |
@retval EFI_OUT_OF_RESOURCES There are not enough memory resourses to create time based payload. | |
@retval EFI_INVALID_PARAMETER The parameter is invalid. | |
@retval Others Unexpected error happens. | |
**/ | |
EFI_STATUS | |
CreateCounterBasedPayload ( | |
IN OUT UINTN *DataSize, | |
IN OUT UINT8 **Data | |
) | |
{ | |
EFI_STATUS Status; | |
UINT8 *NewData; | |
UINT8 *Payload; | |
UINTN PayloadSize; | |
EFI_VARIABLE_AUTHENTICATION *DescriptorData; | |
UINTN DescriptorSize; | |
UINT64 MonotonicCount; | |
if ((Data == NULL) || (DataSize == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// At user physical presence, the variable does not need to be signed but the | |
// parameters to the SetVariable() call still need to be prepared as authenticated | |
// variable. So we create EFI_VARIABLE_AUTHENTICATED descriptor without certificate | |
// data in it. | |
// | |
Payload = *Data; | |
PayloadSize = *DataSize; | |
DescriptorSize = (OFFSET_OF (EFI_VARIABLE_AUTHENTICATION, AuthInfo)) + \ | |
(OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData)) + \ | |
sizeof (EFI_CERT_BLOCK_RSA_2048_SHA256); | |
NewData = (UINT8 *)AllocateZeroPool (DescriptorSize + PayloadSize); | |
if (NewData == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
if ((Payload != NULL) && (PayloadSize != 0)) { | |
CopyMem (NewData + DescriptorSize, Payload, PayloadSize); | |
} | |
DescriptorData = (EFI_VARIABLE_AUTHENTICATION *)(NewData); | |
Status = gBS->GetNextMonotonicCount (&MonotonicCount); | |
if (EFI_ERROR (Status)) { | |
FreePool (NewData); | |
return Status; | |
} | |
DescriptorData->MonotonicCount = MonotonicCount; | |
DescriptorData->AuthInfo.Hdr.dwLength = OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData) + sizeof (EFI_CERT_BLOCK_RSA_2048_SHA256); | |
DescriptorData->AuthInfo.Hdr.wRevision = 0x0200; | |
DescriptorData->AuthInfo.Hdr.wCertificateType = WIN_CERT_TYPE_EFI_GUID; | |
CopyGuid (&DescriptorData->AuthInfo.CertType, &gEfiCertTypeRsa2048Sha256Guid); | |
if (Payload != NULL) { | |
FreePool (Payload); | |
} | |
*DataSize = DescriptorSize + PayloadSize; | |
*Data = NewData; | |
return EFI_SUCCESS; | |
} | |
/** | |
Delete user variable. | |
@param[in] DeleteAll Delete all user variables. | |
@param[in] VariableCleanupData Pointer to variable cleanup data. | |
**/ | |
VOID | |
DeleteUserVariable ( | |
IN BOOLEAN DeleteAll, | |
IN VARIABLE_CLEANUP_DATA *VariableCleanupData OPTIONAL | |
) | |
{ | |
EFI_STATUS Status; | |
USER_VARIABLE_NODE *UserVariableNode; | |
LIST_ENTRY *Link; | |
USER_VARIABLE_NAME_NODE *UserVariableNameNode; | |
LIST_ENTRY *NameLink; | |
UINTN DataSize; | |
UINT8 *Data; | |
for (Link = mUserVariableList.ForwardLink | |
; Link != &mUserVariableList | |
; Link = Link->ForwardLink) | |
{ | |
UserVariableNode = USER_VARIABLE_FROM_LINK (Link); | |
for (NameLink = UserVariableNode->NameLink.ForwardLink | |
; NameLink != &UserVariableNode->NameLink | |
; NameLink = NameLink->ForwardLink) | |
{ | |
UserVariableNameNode = USER_VARIABLE_NAME_FROM_LINK (NameLink); | |
if (!UserVariableNameNode->Deleted && (DeleteAll || ((VariableCleanupData != NULL) && (VariableCleanupData->UserVariable[UserVariableNameNode->Index] == TRUE)))) { | |
DEBUG ((DEBUG_INFO, "PlatformVarCleanup - Delete variable: %g:%s\n", &UserVariableNode->Guid, UserVariableNameNode->Name)); | |
if ((UserVariableNameNode->Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0) { | |
DataSize = 0; | |
Data = NULL; | |
Status = CreateTimeBasedPayload (&DataSize, &Data); | |
if (!EFI_ERROR (Status)) { | |
Status = gRT->SetVariable (UserVariableNameNode->Name, &UserVariableNode->Guid, UserVariableNameNode->Attributes, DataSize, Data); | |
FreePool (Data); | |
} | |
} else if ((UserVariableNameNode->Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) != 0) { | |
DataSize = 0; | |
Data = NULL; | |
Status = CreateCounterBasedPayload (&DataSize, &Data); | |
if (!EFI_ERROR (Status)) { | |
Status = gRT->SetVariable (UserVariableNameNode->Name, &UserVariableNode->Guid, UserVariableNameNode->Attributes, DataSize, Data); | |
FreePool (Data); | |
} | |
} else { | |
Status = gRT->SetVariable (UserVariableNameNode->Name, &UserVariableNode->Guid, 0, 0, NULL); | |
} | |
if (!EFI_ERROR (Status)) { | |
UserVariableNameNode->Deleted = TRUE; | |
} else { | |
DEBUG ((DEBUG_INFO, "PlatformVarCleanup - Delete variable fail: %g:%s\n", &UserVariableNode->Guid, UserVariableNameNode->Name)); | |
} | |
} | |
} | |
} | |
} | |
/** | |
This function allows a caller to extract the current configuration for one | |
or more named elements from the target driver. | |
@param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. | |
@param[in] Request A null-terminated Unicode string in <ConfigRequest> format. | |
@param[out] Progress On return, points to a character in the Request string. | |
Points to the string's null terminator if request was successful. | |
Points to the most recent '&' before the first failing name/value | |
pair (or the beginning of the string if the failure is in the | |
first name/value pair) if the request was not successful. | |
@param[out] Results A null-terminated Unicode string in <ConfigAltResp> format which | |
has all values filled in for the names in the Request string. | |
String to be allocated by the called function. | |
@retval EFI_SUCCESS The Results is filled with the requested values. | |
@retval EFI_OUT_OF_RESOURCES Not enough memory to store the results. | |
@retval EFI_INVALID_PARAMETER Request is illegal syntax, or unknown name. | |
@retval EFI_NOT_FOUND Routing data doesn't match any storage in this driver. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
VariableCleanupHiiExtractConfig ( | |
IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, | |
IN CONST EFI_STRING Request, | |
OUT EFI_STRING *Progress, | |
OUT EFI_STRING *Results | |
) | |
{ | |
EFI_STATUS Status; | |
VARIABLE_CLEANUP_HII_PRIVATE_DATA *Private; | |
UINTN BufferSize; | |
EFI_STRING ConfigRequestHdr; | |
EFI_STRING ConfigRequest; | |
BOOLEAN AllocatedRequest; | |
UINTN Size; | |
if ((Progress == NULL) || (Results == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
*Progress = Request; | |
if ((Request != NULL) && !HiiIsConfigHdrMatch (Request, &mVariableCleanupHiiGuid, mVarStoreName)) { | |
return EFI_NOT_FOUND; | |
} | |
ConfigRequestHdr = NULL; | |
ConfigRequest = NULL; | |
AllocatedRequest = FALSE; | |
Size = 0; | |
Private = VARIABLE_CLEANUP_HII_PRIVATE_FROM_THIS (This); | |
// | |
// Convert buffer data to <ConfigResp> by helper function BlockToConfig(). | |
// | |
BufferSize = sizeof (VARIABLE_CLEANUP_DATA); | |
ConfigRequest = Request; | |
if ((Request == NULL) || (StrStr (Request, L"OFFSET") == NULL)) { | |
// | |
// Request has no request element, construct full request string. | |
// Allocate and fill a buffer large enough to hold the <ConfigHdr> template | |
// followed by "&OFFSET=0&WIDTH=WWWWWWWWWWWWWWWW" followed by a Null-terminator. | |
// | |
ConfigRequestHdr = HiiConstructConfigHdr ( | |
&mVariableCleanupHiiGuid, | |
mVarStoreName, | |
Private->DriverHandle | |
); | |
Size = (StrLen (ConfigRequestHdr) + 32 + 1) * sizeof (CHAR16); | |
ConfigRequest = AllocateZeroPool (Size); | |
ASSERT (ConfigRequest != NULL); | |
AllocatedRequest = TRUE; | |
UnicodeSPrint (ConfigRequest, Size, L"%s&OFFSET=0&WIDTH=%016LX", ConfigRequestHdr, (UINT64)BufferSize); | |
FreePool (ConfigRequestHdr); | |
} | |
Status = Private->ConfigRouting->BlockToConfig ( | |
Private->ConfigRouting, | |
ConfigRequest, | |
(UINT8 *)&Private->VariableCleanupData, | |
BufferSize, | |
Results, | |
Progress | |
); | |
ASSERT_EFI_ERROR (Status); | |
// | |
// Free the allocated config request string. | |
// | |
if (AllocatedRequest) { | |
FreePool (ConfigRequest); | |
ConfigRequest = NULL; | |
} | |
// | |
// Set Progress string to the original request string or the string's null terminator. | |
// | |
if (Request == NULL) { | |
*Progress = NULL; | |
} else if (StrStr (Request, L"OFFSET") == NULL) { | |
*Progress = Request + StrLen (Request); | |
} | |
return Status; | |
} | |
/** | |
Update user variable form. | |
@param[in] Private Points to the VARIABLE_CLEANUP_HII_PRIVATE_DATA. | |
**/ | |
VOID | |
UpdateUserVariableForm ( | |
IN VARIABLE_CLEANUP_HII_PRIVATE_DATA *Private | |
) | |
{ | |
EFI_STRING_ID PromptStringToken; | |
EFI_STRING_ID HelpStringToken; | |
VOID *StartOpCodeHandle; | |
VOID *EndOpCodeHandle; | |
EFI_IFR_GUID_LABEL *StartLabel; | |
EFI_IFR_GUID_LABEL *EndLabel; | |
USER_VARIABLE_NODE *UserVariableNode; | |
LIST_ENTRY *Link; | |
USER_VARIABLE_NAME_NODE *UserVariableNameNode; | |
LIST_ENTRY *NameLink; | |
BOOLEAN Created; | |
// | |
// Init OpCode Handle. | |
// | |
StartOpCodeHandle = HiiAllocateOpCodeHandle (); | |
ASSERT (StartOpCodeHandle != NULL); | |
EndOpCodeHandle = HiiAllocateOpCodeHandle (); | |
ASSERT (EndOpCodeHandle != NULL); | |
// | |
// Create Hii Extend Label OpCode as the start opcode. | |
// | |
StartLabel = (EFI_IFR_GUID_LABEL *)HiiCreateGuidOpCode (StartOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL)); | |
StartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL; | |
StartLabel->Number = LABEL_START; | |
// | |
// Create Hii Extend Label OpCode as the end opcode. | |
// | |
EndLabel = (EFI_IFR_GUID_LABEL *)HiiCreateGuidOpCode (EndOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL)); | |
EndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL; | |
EndLabel->Number = LABEL_END; | |
HiiUpdateForm ( | |
Private->HiiHandle, | |
&mVariableCleanupHiiGuid, | |
FORM_ID_VARIABLE_CLEANUP, | |
StartOpCodeHandle, // LABEL_START | |
EndOpCodeHandle // LABEL_END | |
); | |
for (Link = mUserVariableList.ForwardLink | |
; Link != &mUserVariableList | |
; Link = Link->ForwardLink) | |
{ | |
UserVariableNode = USER_VARIABLE_FROM_LINK (Link); | |
// | |
// Create checkbox opcode for variables in the same variable GUID space. | |
// | |
Created = FALSE; | |
for (NameLink = UserVariableNode->NameLink.ForwardLink | |
; NameLink != &UserVariableNode->NameLink | |
; NameLink = NameLink->ForwardLink) | |
{ | |
UserVariableNameNode = USER_VARIABLE_NAME_FROM_LINK (NameLink); | |
if (!UserVariableNameNode->Deleted) { | |
if (!Created) { | |
// | |
// Create subtitle opcode for variable GUID. | |
// | |
PromptStringToken = HiiSetString (Private->HiiHandle, 0, UserVariableNode->PromptString, NULL); | |
HiiCreateSubTitleOpCode (StartOpCodeHandle, PromptStringToken, 0, 0, 0); | |
Created = TRUE; | |
} | |
// | |
// Only create opcode for the non-deleted variables. | |
// | |
PromptStringToken = HiiSetString (Private->HiiHandle, 0, UserVariableNameNode->PromptString, NULL); | |
HelpStringToken = HiiSetString (Private->HiiHandle, 0, UserVariableNameNode->HelpString, NULL); | |
HiiCreateCheckBoxOpCode ( | |
StartOpCodeHandle, | |
UserVariableNameNode->QuestionId, | |
VARIABLE_CLEANUP_VARSTORE_ID, | |
(UINT16)(USER_VARIABLE_VAR_OFFSET + UserVariableNameNode->Index), | |
PromptStringToken, | |
HelpStringToken, | |
EFI_IFR_FLAG_CALLBACK, | |
Private->VariableCleanupData.UserVariable[UserVariableNameNode->Index], | |
NULL | |
); | |
} | |
} | |
} | |
HiiCreateSubTitleOpCode ( | |
StartOpCodeHandle, | |
STRING_TOKEN (STR_NULL_STRING), | |
0, | |
0, | |
0 | |
); | |
// | |
// Create the "Apply changes" and "Discard changes" tags. | |
// | |
HiiCreateActionOpCode ( | |
StartOpCodeHandle, | |
SAVE_AND_EXIT_QUESTION_ID, | |
STRING_TOKEN (STR_SAVE_AND_EXIT), | |
STRING_TOKEN (STR_NULL_STRING), | |
EFI_IFR_FLAG_CALLBACK, | |
0 | |
); | |
HiiCreateActionOpCode ( | |
StartOpCodeHandle, | |
NO_SAVE_AND_EXIT_QUESTION_ID, | |
STRING_TOKEN (STR_NO_SAVE_AND_EXIT), | |
STRING_TOKEN (STR_NULL_STRING), | |
EFI_IFR_FLAG_CALLBACK, | |
0 | |
); | |
HiiUpdateForm ( | |
Private->HiiHandle, | |
&mVariableCleanupHiiGuid, | |
FORM_ID_VARIABLE_CLEANUP, | |
StartOpCodeHandle, // LABEL_START | |
EndOpCodeHandle // LABEL_END | |
); | |
HiiFreeOpCodeHandle (StartOpCodeHandle); | |
HiiFreeOpCodeHandle (EndOpCodeHandle); | |
} | |
/** | |
This function applies changes in a driver's configuration. | |
Input is a Configuration, which has the routing data for this | |
driver followed by name / value configuration pairs. The driver | |
must apply those pairs to its configurable storage. If the | |
driver's configuration is stored in a linear block of data | |
and the driver's name / value pairs are in <BlockConfig> | |
format, it may use the ConfigToBlock helper function (above) to | |
simplify the job. Currently not implemented. | |
@param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. | |
@param[in] Configuration A null-terminated Unicode string in | |
<ConfigString> format. | |
@param[out] Progress A pointer to a string filled in with the | |
offset of the most recent '&' before the | |
first failing name / value pair (or the | |
beginn ing of the string if the failure | |
is in the first name / value pair) or | |
the terminating NULL if all was | |
successful. | |
@retval EFI_SUCCESS The results have been distributed or are | |
awaiting distribution. | |
@retval EFI_OUT_OF_RESOURCES Not enough memory to store the | |
parts of the results that must be | |
stored awaiting possible future | |
protocols. | |
@retval EFI_INVALID_PARAMETERS Passing in a NULL for the | |
Results parameter would result | |
in this type of error. | |
@retval EFI_NOT_FOUND Target for the specified routing data | |
was not found. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
VariableCleanupHiiRouteConfig ( | |
IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, | |
IN CONST EFI_STRING Configuration, | |
OUT EFI_STRING *Progress | |
) | |
{ | |
EFI_STATUS Status; | |
VARIABLE_CLEANUP_HII_PRIVATE_DATA *Private; | |
UINTN BufferSize; | |
if (Progress == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
*Progress = Configuration; | |
if (Configuration == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Check routing data in <ConfigHdr>. | |
// Note: there is no name for Name/Value storage, only GUID will be checked. | |
// | |
if (!HiiIsConfigHdrMatch (Configuration, &mVariableCleanupHiiGuid, mVarStoreName)) { | |
return EFI_NOT_FOUND; | |
} | |
Private = VARIABLE_CLEANUP_HII_PRIVATE_FROM_THIS (This); | |
// | |
// Get Buffer Storage data. | |
// | |
BufferSize = sizeof (VARIABLE_CLEANUP_DATA); | |
// | |
// Convert <ConfigResp> to buffer data by helper function ConfigToBlock(). | |
// | |
Status = Private->ConfigRouting->ConfigToBlock ( | |
Private->ConfigRouting, | |
Configuration, | |
(UINT8 *)&Private->VariableCleanupData, | |
&BufferSize, | |
Progress | |
); | |
ASSERT_EFI_ERROR (Status); | |
DeleteUserVariable (FALSE, &Private->VariableCleanupData); | |
// | |
// For "F10" hotkey to refresh the form. | |
// | |
// UpdateUserVariableForm (Private); | |
return EFI_SUCCESS; | |
} | |
/** | |
This function is called to provide results data to the driver. | |
This data consists of a unique key that is used to identify | |
which data is either being passed back or being asked for. | |
@param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. | |
@param[in] Action Specifies the type of action taken by the browser. | |
@param[in] QuestionId A unique value which is sent to the original | |
exporting driver so that it can identify the type | |
of data to expect. The format of the data tends to | |
vary based on the opcode that generated the callback. | |
@param[in] Type The type of value for the question. | |
@param[in] Value A pointer to the data being sent to the original | |
exporting driver. | |
@param[out] ActionRequest On return, points to the action requested by the | |
callback function. | |
@retval EFI_SUCCESS The callback successfully handled the action. | |
@retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the | |
variable and its data. | |
@retval EFI_DEVICE_ERROR The variable could not be saved. | |
@retval EFI_UNSUPPORTED The specified Action is not supported by the | |
callback. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
VariableCleanupHiiCallback ( | |
IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, | |
IN EFI_BROWSER_ACTION Action, | |
IN EFI_QUESTION_ID QuestionId, | |
IN UINT8 Type, | |
IN EFI_IFR_TYPE_VALUE *Value, | |
OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest | |
) | |
{ | |
VARIABLE_CLEANUP_HII_PRIVATE_DATA *Private; | |
VARIABLE_CLEANUP_DATA *VariableCleanupData; | |
Private = VARIABLE_CLEANUP_HII_PRIVATE_FROM_THIS (This); | |
if ((Action != EFI_BROWSER_ACTION_CHANGING) && (Action != EFI_BROWSER_ACTION_CHANGED)) { | |
// | |
// All other action return unsupported. | |
// | |
return EFI_UNSUPPORTED; | |
} | |
// | |
// Retrieve uncommitted data from Form Browser. | |
// | |
VariableCleanupData = &Private->VariableCleanupData; | |
HiiGetBrowserData (&mVariableCleanupHiiGuid, mVarStoreName, sizeof (VARIABLE_CLEANUP_DATA), (UINT8 *)VariableCleanupData); | |
if (Action == EFI_BROWSER_ACTION_CHANGING) { | |
if (Value == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
} else if (Action == EFI_BROWSER_ACTION_CHANGED) { | |
if ((Value == NULL) || (ActionRequest == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if ((QuestionId >= USER_VARIABLE_QUESTION_ID) && (QuestionId < USER_VARIABLE_QUESTION_ID + MAX_USER_VARIABLE_COUNT)) { | |
if (Value->b) { | |
// | |
// Means one user variable checkbox is marked to delete but not press F10 or "Commit Changes and Exit" menu. | |
// | |
mMarkedUserVariableCount++; | |
ASSERT (mMarkedUserVariableCount <= mUserVariableCount); | |
if (mMarkedUserVariableCount == mUserVariableCount) { | |
// | |
// All user variables have been marked, then also mark the SelectAll checkbox. | |
// | |
VariableCleanupData->SelectAll = TRUE; | |
} | |
} else { | |
// | |
// Means one user variable checkbox is unmarked. | |
// | |
mMarkedUserVariableCount--; | |
// | |
// Also unmark the SelectAll checkbox. | |
// | |
VariableCleanupData->SelectAll = FALSE; | |
} | |
} else { | |
switch (QuestionId) { | |
case SELECT_ALL_QUESTION_ID: | |
if (Value->b) { | |
// | |
// Means the SelectAll checkbox is marked to delete all user variables but not press F10 or "Commit Changes and Exit" menu. | |
// | |
SetMem (VariableCleanupData->UserVariable, sizeof (VariableCleanupData->UserVariable), TRUE); | |
mMarkedUserVariableCount = mUserVariableCount; | |
} else { | |
// | |
// Means the SelectAll checkbox is unmarked. | |
// | |
SetMem (VariableCleanupData->UserVariable, sizeof (VariableCleanupData->UserVariable), FALSE); | |
mMarkedUserVariableCount = 0; | |
} | |
break; | |
case SAVE_AND_EXIT_QUESTION_ID: | |
DeleteUserVariable (FALSE, VariableCleanupData); | |
*ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_SUBMIT_EXIT; | |
break; | |
case NO_SAVE_AND_EXIT_QUESTION_ID: | |
// | |
// Restore local maintain data. | |
// | |
*ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_DISCARD_EXIT; | |
break; | |
default: | |
break; | |
} | |
} | |
} | |
// | |
// Pass changed uncommitted data back to Form Browser. | |
// | |
HiiSetBrowserData (&mVariableCleanupHiiGuid, mVarStoreName, sizeof (VARIABLE_CLEANUP_DATA), (UINT8 *)VariableCleanupData, NULL); | |
return EFI_SUCCESS; | |
} | |
/** | |
Platform variable cleanup. | |
@param[in] Flag Variable error flag. | |
@param[in] Type Variable cleanup type. | |
If it is VarCleanupManually, the interface must be called after console connected. | |
@retval EFI_SUCCESS No error or error processed. | |
@retval EFI_UNSUPPORTED The specified Flag or Type is not supported. | |
For example, system error may be not supported to process and Platform should have mechanism to reset system to manufacture mode. | |
Another, if system and user variables are wanted to be distinguished to process, the interface must be called after EndOfDxe. | |
@retval EFI_OUT_OF_RESOURCES Not enough resource to process the error. | |
@retval EFI_INVALID_PARAMETER The specified Flag or Type is an invalid value. | |
@retval Others Other failure occurs. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
PlatformVarCleanup ( | |
IN VAR_ERROR_FLAG Flag, | |
IN VAR_CLEANUP_TYPE Type | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_FORM_BROWSER2_PROTOCOL *FormBrowser2; | |
VARIABLE_CLEANUP_HII_PRIVATE_DATA *Private; | |
if (!mEndOfDxe) { | |
// | |
// This implementation must be called after EndOfDxe. | |
// | |
return EFI_UNSUPPORTED; | |
} | |
if ((Type >= VarCleanupMax) || ((Flag & ((VAR_ERROR_FLAG)(VAR_ERROR_FLAG_SYSTEM_ERROR & VAR_ERROR_FLAG_USER_ERROR))) == 0)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (Flag == VAR_ERROR_FLAG_NO_ERROR) { | |
// | |
// Just return success if no error. | |
// | |
return EFI_SUCCESS; | |
} | |
if ((Flag & (~((VAR_ERROR_FLAG)VAR_ERROR_FLAG_SYSTEM_ERROR))) == 0) { | |
// | |
// This sample does not support system variables cleanup. | |
// | |
DEBUG ((DEBUG_ERROR, "NOTICE - VAR_ERROR_FLAG_SYSTEM_ERROR\n")); | |
DEBUG ((DEBUG_ERROR, "Platform should have mechanism to reset system to manufacture mode\n")); | |
return EFI_UNSUPPORTED; | |
} | |
// | |
// Continue to process VAR_ERROR_FLAG_USER_ERROR. | |
// | |
// | |
// Create user variable nodes for the following processing. | |
// | |
CreateUserVariableNode (); | |
switch (Type) { | |
case VarCleanupAll: | |
DeleteUserVariable (TRUE, NULL); | |
// | |
// Destroyed the created user variable nodes | |
// | |
DestroyUserVariableNode (); | |
return EFI_SUCCESS; | |
break; | |
case VarCleanupManually: | |
// | |
// Locate FormBrowser2 protocol. | |
// | |
Status = gBS->LocateProtocol (&gEfiFormBrowser2ProtocolGuid, NULL, (VOID **)&FormBrowser2); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Private = AllocateZeroPool (sizeof (VARIABLE_CLEANUP_HII_PRIVATE_DATA)); | |
if (Private == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
Private->Signature = VARIABLE_CLEANUP_HII_PRIVATE_SIGNATURE; | |
Private->ConfigAccess.ExtractConfig = VariableCleanupHiiExtractConfig; | |
Private->ConfigAccess.RouteConfig = VariableCleanupHiiRouteConfig; | |
Private->ConfigAccess.Callback = VariableCleanupHiiCallback; | |
Status = gBS->LocateProtocol ( | |
&gEfiHiiConfigRoutingProtocolGuid, | |
NULL, | |
(VOID **)&Private->ConfigRouting | |
); | |
if (EFI_ERROR (Status)) { | |
goto Done; | |
} | |
// | |
// Install Device Path Protocol and Config Access protocol to driver handle. | |
// | |
Status = gBS->InstallMultipleProtocolInterfaces ( | |
&Private->DriverHandle, | |
&gEfiDevicePathProtocolGuid, | |
&mVarCleanupHiiVendorDevicePath, | |
&gEfiHiiConfigAccessProtocolGuid, | |
&Private->ConfigAccess, | |
NULL | |
); | |
if (EFI_ERROR (Status)) { | |
goto Done; | |
} | |
// | |
// Publish our HII data. | |
// | |
Private->HiiHandle = HiiAddPackages ( | |
&mVariableCleanupHiiGuid, | |
Private->DriverHandle, | |
PlatformVarCleanupLibStrings, | |
PlatVarCleanupBin, | |
NULL | |
); | |
if (Private->HiiHandle == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
goto Done; | |
} | |
UpdateUserVariableForm (Private); | |
Status = FormBrowser2->SendForm ( | |
FormBrowser2, | |
&Private->HiiHandle, | |
1, | |
NULL, | |
0, | |
NULL, | |
NULL | |
); | |
break; | |
default: | |
return EFI_UNSUPPORTED; | |
break; | |
} | |
Done: | |
if (Private->DriverHandle != NULL) { | |
gBS->UninstallMultipleProtocolInterfaces ( | |
Private->DriverHandle, | |
&gEfiDevicePathProtocolGuid, | |
&mVarCleanupHiiVendorDevicePath, | |
&gEfiHiiConfigAccessProtocolGuid, | |
&Private->ConfigAccess, | |
NULL | |
); | |
} | |
if (Private->HiiHandle != NULL) { | |
HiiRemovePackages (Private->HiiHandle); | |
} | |
FreePool (Private); | |
// | |
// Destroyed the created user variable nodes | |
// | |
DestroyUserVariableNode (); | |
return Status; | |
} | |
/** | |
Get last boot variable error flag. | |
@return Last boot variable error flag. | |
**/ | |
VAR_ERROR_FLAG | |
EFIAPI | |
GetLastBootVarErrorFlag ( | |
VOID | |
) | |
{ | |
return mLastVarErrorFlag; | |
} | |
/** | |
Notification function of END_OF_DXE. | |
This is a notification function registered on END_OF_DXE event. | |
@param[in] Event Event whose notification function is being invoked. | |
@param[in] Context Pointer to the notification function's context. | |
**/ | |
VOID | |
EFIAPI | |
PlatformVarCleanupEndOfDxeEvent ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
) | |
{ | |
mEndOfDxe = TRUE; | |
} | |
/** | |
The constructor function caches the pointer to VarCheck protocol and last boot variable error flag. | |
The constructor function locates VarCheck protocol from protocol database. | |
It will ASSERT() if that operation fails and it will always return EFI_SUCCESS. | |
@param ImageHandle The firmware allocated handle for the EFI image. | |
@param SystemTable A pointer to the EFI System Table. | |
@retval EFI_SUCCESS The constructor always returns EFI_SUCCESS. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
PlatformVarCleanupLibConstructor ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
EFI_STATUS Status; | |
mLastVarErrorFlag = InternalGetVarErrorFlag (); | |
DEBUG ((DEBUG_INFO, "mLastVarErrorFlag - 0x%02x\n", mLastVarErrorFlag)); | |
// | |
// Register EFI_END_OF_DXE_EVENT_GROUP_GUID event. | |
// | |
Status = gBS->CreateEventEx ( | |
EVT_NOTIFY_SIGNAL, | |
TPL_CALLBACK, | |
PlatformVarCleanupEndOfDxeEvent, | |
NULL, | |
&gEfiEndOfDxeEventGroupGuid, | |
&mPlatVarCleanupLibEndOfDxeEvent | |
); | |
ASSERT_EFI_ERROR (Status); | |
return EFI_SUCCESS; | |
} | |
/** | |
The destructor function closes the End of DXE event. | |
@param ImageHandle The firmware allocated handle for the EFI image. | |
@param SystemTable A pointer to the EFI System Table. | |
@retval EFI_SUCCESS The destructor completed successfully. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
PlatformVarCleanupLibDestructor ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
EFI_STATUS Status; | |
// | |
// Close the End of DXE event. | |
// | |
Status = gBS->CloseEvent (mPlatVarCleanupLibEndOfDxeEvent); | |
ASSERT_EFI_ERROR (Status); | |
return EFI_SUCCESS; | |
} |