diff options
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootConfig.c')
-rw-r--r-- | src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootConfig.c | 700 |
1 files changed, 700 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootConfig.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootConfig.c new file mode 100644 index 00000000..3814d196 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootConfig.c @@ -0,0 +1,700 @@ +/** @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 ((EFI_D_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 ((EFI_D_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; +} |