/** @file | |
Helper functions for configuring or getting the parameters relating to HTTP Boot. | |
Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "HttpBootDxe.h" | |
#include <Library/UefiBootManagerLib.h> | |
CHAR16 mHttpBootConfigStorageName[] = L"HTTP_BOOT_CONFIG_IFR_NVDATA"; | |
/** | |
Add new boot option for HTTP boot. | |
@param[in] Private Pointer to the driver private data. | |
@param[in] UsingIpv6 Set to TRUE if creating boot option for IPv6. | |
@param[in] Description The description text of the boot option. | |
@param[in] Uri The URI string of the boot file. | |
@retval EFI_SUCCESS The boot option is created successfully. | |
@retval Others Failed to create new boot option. | |
**/ | |
EFI_STATUS | |
HttpBootAddBootOption ( | |
IN HTTP_BOOT_PRIVATE_DATA *Private, | |
IN BOOLEAN UsingIpv6, | |
IN CHAR16 *Description, | |
IN CHAR16 *Uri | |
) | |
{ | |
EFI_DEV_PATH *Node; | |
EFI_DEVICE_PATH_PROTOCOL *TmpDevicePath; | |
EFI_DEVICE_PATH_PROTOCOL *NewDevicePath; | |
UINTN Length; | |
CHAR8 AsciiUri[URI_STR_MAX_SIZE]; | |
EFI_STATUS Status; | |
UINTN Index; | |
EFI_BOOT_MANAGER_LOAD_OPTION NewOption; | |
NewDevicePath = NULL; | |
Node = NULL; | |
TmpDevicePath = NULL; | |
if (StrLen (Description) == 0) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Convert the scheme to all lower case. | |
// | |
for (Index = 0; Index < StrLen (Uri); Index++) { | |
if (Uri[Index] == L':') { | |
break; | |
} | |
if ((Uri[Index] >= L'A') && (Uri[Index] <= L'Z')) { | |
Uri[Index] -= (CHAR16)(L'A' - L'a'); | |
} | |
} | |
// | |
// Only accept empty URI, or http and https URI. | |
// | |
if ((StrLen (Uri) != 0) && (StrnCmp (Uri, L"http://", 7) != 0) && (StrnCmp (Uri, L"https://", 8) != 0)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Create a new device path by appending the IP node and URI node to | |
// the driver's parent device path | |
// | |
if (!UsingIpv6) { | |
Node = AllocateZeroPool (sizeof (IPv4_DEVICE_PATH)); | |
if (Node == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
goto ON_EXIT; | |
} | |
Node->Ipv4.Header.Type = MESSAGING_DEVICE_PATH; | |
Node->Ipv4.Header.SubType = MSG_IPv4_DP; | |
SetDevicePathNodeLength (Node, sizeof (IPv4_DEVICE_PATH)); | |
} else { | |
Node = AllocateZeroPool (sizeof (IPv6_DEVICE_PATH)); | |
if (Node == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
goto ON_EXIT; | |
} | |
Node->Ipv6.Header.Type = MESSAGING_DEVICE_PATH; | |
Node->Ipv6.Header.SubType = MSG_IPv6_DP; | |
SetDevicePathNodeLength (Node, sizeof (IPv6_DEVICE_PATH)); | |
} | |
TmpDevicePath = AppendDevicePathNode (Private->ParentDevicePath, (EFI_DEVICE_PATH_PROTOCOL *)Node); | |
FreePool (Node); | |
if (TmpDevicePath == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
// | |
// Update the URI node with the input boot file URI. | |
// | |
UnicodeStrToAsciiStrS (Uri, AsciiUri, sizeof (AsciiUri)); | |
Length = sizeof (EFI_DEVICE_PATH_PROTOCOL) + AsciiStrSize (AsciiUri); | |
Node = AllocatePool (Length); | |
if (Node == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
FreePool (TmpDevicePath); | |
goto ON_EXIT; | |
} | |
Node->DevPath.Type = MESSAGING_DEVICE_PATH; | |
Node->DevPath.SubType = MSG_URI_DP; | |
SetDevicePathNodeLength (Node, Length); | |
CopyMem ((UINT8 *)Node + sizeof (EFI_DEVICE_PATH_PROTOCOL), AsciiUri, AsciiStrSize (AsciiUri)); | |
NewDevicePath = AppendDevicePathNode (TmpDevicePath, (EFI_DEVICE_PATH_PROTOCOL *)Node); | |
FreePool (Node); | |
FreePool (TmpDevicePath); | |
if (NewDevicePath == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
goto ON_EXIT; | |
} | |
// | |
// Add a new load option. | |
// | |
Status = EfiBootManagerInitializeLoadOption ( | |
&NewOption, | |
LoadOptionNumberUnassigned, | |
LoadOptionTypeBoot, | |
LOAD_OPTION_ACTIVE, | |
Description, | |
NewDevicePath, | |
NULL, | |
0 | |
); | |
if (EFI_ERROR (Status)) { | |
goto ON_EXIT; | |
} | |
Status = EfiBootManagerAddLoadOptionVariable (&NewOption, (UINTN)-1); | |
EfiBootManagerFreeLoadOption (&NewOption); | |
ON_EXIT: | |
if (NewDevicePath != NULL) { | |
FreePool (NewDevicePath); | |
} | |
return Status; | |
} | |
/** | |
This function allows the caller to request the current | |
configuration for one or more named elements. The resulting | |
string is in <ConfigAltResp> format. Also, any and all alternative | |
configuration strings shall be appended to the end of the | |
current configuration string. If they are, they must appear | |
after the current configuration. They must contain the same | |
routing (GUID, NAME, PATH) as the current configuration string. | |
They must have an additional description indicating the type of | |
alternative configuration the string represents, | |
"ALTCFG=<StringToken>". That <StringToken> (when | |
converted from Hex UNICODE to binary) is a reference to a | |
string in the associated string pack. | |
@param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. | |
@param[in] Request A null-terminated Unicode string in | |
<ConfigRequest> format. Note that this | |
includes the routing information as well as | |
the configurable name / value pairs. It is | |
invalid for this string to be in | |
<MultiConfigRequest> 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 string is filled with the | |
values corresponding to all requested | |
names. | |
@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_PARAMETER For example, passing in a NULL | |
for the Request parameter | |
would result in this type of | |
error. In this case, the | |
Progress parameter would be | |
set to NULL. | |
@retval EFI_NOT_FOUND Routing data doesn't match any | |
known driver. Progress set to the | |
first character in the routing header. | |
Note: There is no requirement that the | |
driver validate the routing data. It | |
must skip the <ConfigHdr> in order to | |
process the names. | |
@retval EFI_INVALID_PARAMETER Illegal syntax. Progress set | |
to most recent "&" before the | |
error or the beginning of the | |
string. | |
@retval EFI_INVALID_PARAMETER Unknown name. Progress points | |
to the & before the name in | |
question. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
HttpBootFormExtractConfig ( | |
IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, | |
IN CONST EFI_STRING Request, | |
OUT EFI_STRING *Progress, | |
OUT EFI_STRING *Results | |
) | |
{ | |
EFI_STATUS Status; | |
UINTN BufferSize; | |
HTTP_BOOT_FORM_CALLBACK_INFO *CallbackInfo; | |
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, &gHttpBootConfigGuid, mHttpBootConfigStorageName)) { | |
return EFI_NOT_FOUND; | |
} | |
ConfigRequestHdr = NULL; | |
ConfigRequest = NULL; | |
AllocatedRequest = FALSE; | |
Size = 0; | |
CallbackInfo = HTTP_BOOT_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS (This); | |
// | |
// Convert buffer data to <ConfigResp> by helper function BlockToConfig() | |
// | |
BufferSize = sizeof (HTTP_BOOT_CONFIG_IFR_NVDATA); | |
ZeroMem (&CallbackInfo->HttpBootNvData, BufferSize); | |
StrCpyS (CallbackInfo->HttpBootNvData.Description, DESCRIPTION_STR_MAX_SIZE / sizeof (CHAR16), HTTP_BOOT_DEFAULT_DESCRIPTION_STR); | |
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 (&gHttpBootConfigGuid, mHttpBootConfigStorageName, CallbackInfo->ChildHandle); | |
Size = (StrLen (ConfigRequestHdr) + 32 + 1) * sizeof (CHAR16); | |
ConfigRequest = AllocateZeroPool (Size); | |
if (ConfigRequest == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
AllocatedRequest = TRUE; | |
UnicodeSPrint (ConfigRequest, Size, L"%s&OFFSET=0&WIDTH=%016LX", ConfigRequestHdr, (UINT64)BufferSize); | |
FreePool (ConfigRequestHdr); | |
} | |
Status = gHiiConfigRouting->BlockToConfig ( | |
gHiiConfigRouting, | |
ConfigRequest, | |
(UINT8 *)&CallbackInfo->HttpBootNvData, | |
BufferSize, | |
Results, | |
Progress | |
); | |
// | |
// Free the allocated config request string. | |
// | |
if (AllocatedRequest) { | |
FreePool (ConfigRequest); | |
ConfigRequest = NULL; | |
} | |
// | |
// Set Progress string to the original request string. | |
// | |
if (Request == NULL) { | |
*Progress = NULL; | |
} else if (StrStr (Request, L"OFFSET") == NULL) { | |
*Progress = Request + StrLen (Request); | |
} | |
return Status; | |
} | |
/** | |
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. | |
@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 | |
beginning 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 | |
HttpBootFormRouteConfig ( | |
IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, | |
IN CONST EFI_STRING Configuration, | |
OUT EFI_STRING *Progress | |
) | |
{ | |
EFI_STATUS Status; | |
UINTN BufferSize; | |
HTTP_BOOT_FORM_CALLBACK_INFO *CallbackInfo; | |
HTTP_BOOT_PRIVATE_DATA *Private; | |
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, &gHttpBootConfigGuid, mHttpBootConfigStorageName)) { | |
return EFI_NOT_FOUND; | |
} | |
CallbackInfo = HTTP_BOOT_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS (This); | |
Private = HTTP_BOOT_PRIVATE_DATA_FROM_CALLBACK_INFO (CallbackInfo); | |
BufferSize = sizeof (HTTP_BOOT_CONFIG_IFR_NVDATA); | |
ZeroMem (&CallbackInfo->HttpBootNvData, BufferSize); | |
Status = gHiiConfigRouting->ConfigToBlock ( | |
gHiiConfigRouting, | |
Configuration, | |
(UINT8 *)&CallbackInfo->HttpBootNvData, | |
&BufferSize, | |
Progress | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Create a new boot option according to the configuration data. | |
// | |
HttpBootAddBootOption ( | |
Private, | |
(CallbackInfo->HttpBootNvData.IpVersion == HTTP_BOOT_IP_VERSION_6) ? TRUE : FALSE, | |
CallbackInfo->HttpBootNvData.Description, | |
CallbackInfo->HttpBootNvData.Uri | |
); | |
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, out] 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 | |
HttpBootFormCallback ( | |
IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, | |
IN EFI_BROWSER_ACTION Action, | |
IN EFI_QUESTION_ID QuestionId, | |
IN UINT8 Type, | |
IN OUT EFI_IFR_TYPE_VALUE *Value, | |
OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest | |
) | |
{ | |
EFI_INPUT_KEY Key; | |
CHAR16 *Uri; | |
UINTN UriLen; | |
CHAR8 *AsciiUri; | |
HTTP_BOOT_FORM_CALLBACK_INFO *CallbackInfo; | |
EFI_STATUS Status; | |
Uri = NULL; | |
UriLen = 0; | |
AsciiUri = NULL; | |
Status = EFI_SUCCESS; | |
if ((This == NULL) || (Value == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
CallbackInfo = HTTP_BOOT_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS (This); | |
if (Action != EFI_BROWSER_ACTION_CHANGING) { | |
return EFI_UNSUPPORTED; | |
} | |
switch (QuestionId) { | |
case KEY_INITIATOR_URI: | |
// | |
// Get user input URI string | |
// | |
Uri = HiiGetString (CallbackInfo->RegisteredHandle, Value->string, NULL); | |
if (Uri == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// The URI should be either an empty string (for corporate environment) ,or http(s) for home environment. | |
// Pop up a message box for the unsupported URI. | |
// | |
if (StrLen (Uri) != 0) { | |
UriLen = StrLen (Uri) + 1; | |
AsciiUri = AllocateZeroPool (UriLen); | |
if (AsciiUri == NULL) { | |
FreePool (Uri); | |
return EFI_OUT_OF_RESOURCES; | |
} | |
UnicodeStrToAsciiStrS (Uri, AsciiUri, UriLen); | |
Status = HttpBootCheckUriScheme (AsciiUri); | |
if (Status == EFI_INVALID_PARAMETER) { | |
DEBUG ((DEBUG_ERROR, "HttpBootFormCallback: %r.\n", Status)); | |
CreatePopUp ( | |
EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, | |
&Key, | |
L"ERROR: Unsupported URI!", | |
L"Only supports HTTP and HTTPS", | |
NULL | |
); | |
} else if (Status == EFI_ACCESS_DENIED) { | |
DEBUG ((DEBUG_ERROR, "HttpBootFormCallback: %r.\n", Status)); | |
CreatePopUp ( | |
EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, | |
&Key, | |
L"ERROR: Unsupported URI!", | |
L"HTTP is disabled", | |
NULL | |
); | |
} | |
} | |
if (Uri != NULL) { | |
FreePool (Uri); | |
} | |
if (AsciiUri != NULL) { | |
FreePool (AsciiUri); | |
} | |
break; | |
default: | |
break; | |
} | |
return Status; | |
} | |
/** | |
Initialize the configuration form. | |
@param[in] Private Pointer to the driver private data. | |
@retval EFI_SUCCESS The configuration form is initialized. | |
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory. | |
**/ | |
EFI_STATUS | |
HttpBootConfigFormInit ( | |
IN HTTP_BOOT_PRIVATE_DATA *Private | |
) | |
{ | |
EFI_STATUS Status; | |
HTTP_BOOT_FORM_CALLBACK_INFO *CallbackInfo; | |
VENDOR_DEVICE_PATH VendorDeviceNode; | |
CHAR16 *MacString; | |
CHAR16 *OldMenuString; | |
CHAR16 MenuString[128]; | |
CallbackInfo = &Private->CallbackInfo; | |
if (CallbackInfo->Initialized) { | |
return EFI_SUCCESS; | |
} | |
CallbackInfo->Signature = HTTP_BOOT_FORM_CALLBACK_INFO_SIGNATURE; | |
// | |
// Construct device path node for EFI HII Config Access protocol, | |
// which consists of controller physical device path and one hardware | |
// vendor guid node. | |
// | |
ZeroMem (&VendorDeviceNode, sizeof (VENDOR_DEVICE_PATH)); | |
VendorDeviceNode.Header.Type = HARDWARE_DEVICE_PATH; | |
VendorDeviceNode.Header.SubType = HW_VENDOR_DP; | |
CopyGuid (&VendorDeviceNode.Guid, &gEfiCallerIdGuid); | |
SetDevicePathNodeLength (&VendorDeviceNode.Header, sizeof (VENDOR_DEVICE_PATH)); | |
CallbackInfo->HiiVendorDevicePath = AppendDevicePathNode ( | |
Private->ParentDevicePath, | |
(EFI_DEVICE_PATH_PROTOCOL *)&VendorDeviceNode | |
); | |
if (CallbackInfo->HiiVendorDevicePath == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
goto Error; | |
} | |
CallbackInfo->ConfigAccess.ExtractConfig = HttpBootFormExtractConfig; | |
CallbackInfo->ConfigAccess.RouteConfig = HttpBootFormRouteConfig; | |
CallbackInfo->ConfigAccess.Callback = HttpBootFormCallback; | |
// | |
// Install Device Path Protocol and Config Access protocol to driver handle. | |
// | |
Status = gBS->InstallMultipleProtocolInterfaces ( | |
&CallbackInfo->ChildHandle, | |
&gEfiDevicePathProtocolGuid, | |
CallbackInfo->HiiVendorDevicePath, | |
&gEfiHiiConfigAccessProtocolGuid, | |
&CallbackInfo->ConfigAccess, | |
NULL | |
); | |
if (EFI_ERROR (Status)) { | |
goto Error; | |
} | |
// | |
// Publish our HII data. | |
// | |
CallbackInfo->RegisteredHandle = HiiAddPackages ( | |
&gHttpBootConfigGuid, | |
CallbackInfo->ChildHandle, | |
HttpBootDxeStrings, | |
HttpBootConfigVfrBin, | |
NULL | |
); | |
if (CallbackInfo->RegisteredHandle == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
goto Error; | |
} | |
// | |
// Append MAC string in the menu help string | |
// | |
Status = NetLibGetMacString (Private->Controller, NULL, &MacString); | |
if (!EFI_ERROR (Status)) { | |
OldMenuString = HiiGetString ( | |
CallbackInfo->RegisteredHandle, | |
STRING_TOKEN (STR_HTTP_BOOT_CONFIG_FORM_HELP), | |
NULL | |
); | |
UnicodeSPrint (MenuString, 128, L"%s (MAC:%s)", OldMenuString, MacString); | |
HiiSetString ( | |
CallbackInfo->RegisteredHandle, | |
STRING_TOKEN (STR_HTTP_BOOT_CONFIG_FORM_HELP), | |
MenuString, | |
NULL | |
); | |
FreePool (MacString); | |
FreePool (OldMenuString); | |
CallbackInfo->Initialized = TRUE; | |
return EFI_SUCCESS; | |
} | |
Error: | |
HttpBootConfigFormUnload (Private); | |
return Status; | |
} | |
/** | |
Unload the configuration form, this includes: delete all the configuration | |
entries, uninstall the form callback protocol, and free the resources used. | |
The form will only be unload completely when both IP4 and IP6 stack are stopped. | |
@param[in] Private Pointer to the driver private data. | |
@retval EFI_SUCCESS The configuration form is unloaded. | |
@retval Others Failed to unload the form. | |
**/ | |
EFI_STATUS | |
HttpBootConfigFormUnload ( | |
IN HTTP_BOOT_PRIVATE_DATA *Private | |
) | |
{ | |
HTTP_BOOT_FORM_CALLBACK_INFO *CallbackInfo; | |
if ((Private->Ip4Nic != NULL) || (Private->Ip6Nic != NULL)) { | |
// | |
// Only unload the configuration form when both IP4 and IP6 stack are stopped. | |
// | |
return EFI_SUCCESS; | |
} | |
CallbackInfo = &Private->CallbackInfo; | |
if (CallbackInfo->ChildHandle != NULL) { | |
// | |
// Uninstall EFI_HII_CONFIG_ACCESS_PROTOCOL | |
// | |
gBS->UninstallMultipleProtocolInterfaces ( | |
CallbackInfo->ChildHandle, | |
&gEfiDevicePathProtocolGuid, | |
CallbackInfo->HiiVendorDevicePath, | |
&gEfiHiiConfigAccessProtocolGuid, | |
&CallbackInfo->ConfigAccess, | |
NULL | |
); | |
CallbackInfo->ChildHandle = NULL; | |
} | |
if (CallbackInfo->HiiVendorDevicePath != NULL) { | |
FreePool (CallbackInfo->HiiVendorDevicePath); | |
CallbackInfo->HiiVendorDevicePath = NULL; | |
} | |
if (CallbackInfo->RegisteredHandle != NULL) { | |
// | |
// Remove HII package list | |
// | |
HiiRemovePackages (CallbackInfo->RegisteredHandle); | |
CallbackInfo->RegisteredHandle = NULL; | |
} | |
return EFI_SUCCESS; | |
} |