--- /dev/null
+/** @file\r
+ The implementation for the 'tftp' Shell command.\r
+\r
+ Copyright (c) 2015, ARM Ltd. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+**/\r
+\r
+#include "UefiShellTftpCommandLib.h"\r
+\r
+/*\r
+ Constant strings and definitions related to the message indicating the amount of\r
+ progress in the dowloading of a TFTP file.\r
+*/\r
+\r
+// Frame for the progression slider\r
+STATIC CONST CHAR16 mTftpProgressFrame[] = L"[ ]";\r
+\r
+// Number of steps in the progression slider\r
+#define TFTP_PROGRESS_SLIDER_STEPS ((sizeof (mTftpProgressFrame) / sizeof (CHAR16)) - 3)\r
+\r
+// Size in number of characters plus one (final zero) of the message to\r
+// indicate the progress of a TFTP download. The format is "[(progress slider:\r
+// 40 characters)] (nb of KBytes downloaded so far: 7 characters) Kb". There\r
+// are thus the number of characters in mTftpProgressFrame[] plus 11 characters\r
+// (2 // spaces, "Kb" and seven characters for the number of KBytes).\r
+#define TFTP_PROGRESS_MESSAGE_SIZE ((sizeof (mTftpProgressFrame) / sizeof (CHAR16)) + 12)\r
+\r
+// String to delete the TFTP progress message to be able to update it :\r
+// (TFTP_PROGRESS_MESSAGE_SIZE-1) '\b'\r
+STATIC CONST CHAR16 mTftpProgressDelete[] = L"\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b";\r
+\r
+STATIC BOOLEAN StringToUint16 (\r
+ IN CONST CHAR16 *ValueStr,\r
+ OUT UINT16 *Value\r
+ );\r
+\r
+STATIC EFI_STATUS GetNicName (\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN UINTN Index,\r
+ OUT CHAR16 *NicName\r
+ );\r
+\r
+STATIC EFI_STATUS CreateServiceChildAndOpenProtocol (\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_GUID *ServiceBindingProtocolGuid,\r
+ IN EFI_GUID *ProtocolGuid,\r
+ OUT EFI_HANDLE *ChildHandle,\r
+ OUT VOID **Interface\r
+ );\r
+\r
+STATIC VOID CloseProtocolAndDestroyServiceChild (\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_GUID *ServiceBindingProtocolGuid,\r
+ IN EFI_GUID *ProtocolGuid,\r
+ IN EFI_HANDLE ChildHandle\r
+ );\r
+\r
+STATIC EFI_STATUS GetFileSize (\r
+ IN EFI_MTFTP4_PROTOCOL *Mtftp4,\r
+ IN CONST CHAR8 *FilePath,\r
+ OUT UINTN *FileSize\r
+ );\r
+\r
+STATIC EFI_STATUS DownloadFile (\r
+ IN EFI_MTFTP4_PROTOCOL *Mtftp4,\r
+ IN CONST CHAR16 *FilePath,\r
+ IN CONST CHAR8 *AsciiFilePath,\r
+ IN UINTN FileSize,\r
+ OUT VOID **Data\r
+ );\r
+\r
+STATIC EFI_STATUS CheckPacket (\r
+ IN EFI_MTFTP4_PROTOCOL *This,\r
+ IN EFI_MTFTP4_TOKEN *Token,\r
+ IN UINT16 PacketLen,\r
+ IN EFI_MTFTP4_PACKET *Packet\r
+ );\r
+\r
+EFI_MTFTP4_CONFIG_DATA DefaultMtftp4ConfigData = {\r
+ TRUE, // Use default setting\r
+ { { 0, 0, 0, 0 } }, // StationIp - Not relevant as UseDefaultSetting=TRUE\r
+ { { 0, 0, 0, 0 } }, // SubnetMask - Not relevant as UseDefaultSetting=TRUE\r
+ 0, // LocalPort - Automatically assigned port number.\r
+ { { 0, 0, 0, 0 } }, // GatewayIp - Not relevant as UseDefaultSetting=TRUE\r
+ { { 0, 0, 0, 0 } }, // ServerIp - Not known yet\r
+ 69, // InitialServerPort - Standard TFTP server port\r
+ 6, // TryCount - Max number of retransmissions.\r
+ 4 // TimeoutValue - Retransmission timeout in seconds.\r
+};\r
+\r
+STATIC CONST SHELL_PARAM_ITEM ParamList[] = {\r
+ {L"-i", TypeValue},\r
+ {L"-l", TypeValue},\r
+ {L"-r", TypeValue},\r
+ {L"-c", TypeValue},\r
+ {L"-t", TypeValue},\r
+ {NULL , TypeMax}\r
+ };\r
+\r
+/**\r
+ Function for 'tftp' command.\r
+\r
+ @param[in] ImageHandle Handle to the Image (NULL if Internal).\r
+ @param[in] SystemTable Pointer to the System Table (NULL if Internal).\r
+\r
+ @return SHELL_SUCCESS The 'tftp' command completed successfully.\r
+ @return SHELL_ABORTED The Shell Library initialization failed.\r
+ @return SHELL_INVALID_PARAMETER At least one of the command's arguments is\r
+ not valid.\r
+ @return SHELL_OUT_OF_RESOURCES A memory allocation failed.\r
+ @return SHELL_NOT_FOUND Network Interface Card not found or server\r
+ error or file error.\r
+\r
+**/\r
+SHELL_STATUS\r
+EFIAPI\r
+ShellCommandRunTftp (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ SHELL_STATUS ShellStatus;\r
+ EFI_STATUS Status;\r
+ LIST_ENTRY *CheckPackage;\r
+ CHAR16 *ProblemParam;\r
+ UINTN ParamCount;\r
+ CONST CHAR16 *UserNicName;\r
+ BOOLEAN NicFound;\r
+ CONST CHAR16 *ValueStr;\r
+ CONST CHAR16 *RemoteFilePath;\r
+ CHAR8 *AsciiRemoteFilePath;\r
+ CONST CHAR16 *Walker;\r
+ CONST CHAR16 *LocalFilePath;\r
+ EFI_MTFTP4_CONFIG_DATA Mtftp4ConfigData;\r
+ EFI_HANDLE *Handles;\r
+ UINTN HandleCount;\r
+ UINTN NicNumber;\r
+ CHAR16 NicName[IP4_NIC_NAME_LENGTH];\r
+ EFI_HANDLE ControllerHandle;\r
+ EFI_HANDLE Mtftp4ChildHandle;\r
+ EFI_MTFTP4_PROTOCOL *Mtftp4;\r
+ UINTN FileSize;\r
+ VOID *Data;\r
+ SHELL_FILE_HANDLE FileHandle;\r
+\r
+ ShellStatus = SHELL_INVALID_PARAMETER;\r
+ ProblemParam = NULL;\r
+ NicFound = FALSE;\r
+ AsciiRemoteFilePath = NULL;\r
+ Handles = NULL;\r
+\r
+ //\r
+ // Initialize the Shell library (we must be in non-auto-init...)\r
+ //\r
+ Status = ShellInitialize ();\r
+ if (EFI_ERROR (Status)) {\r
+ ASSERT_EFI_ERROR (Status);\r
+ return SHELL_ABORTED;\r
+ }\r
+\r
+ //\r
+ // Parse the command line.\r
+ //\r
+ Status = ShellCommandLineParse (ParamList, &CheckPackage, &ProblemParam, TRUE);\r
+ if (EFI_ERROR (Status)) {\r
+ if ((Status == EFI_VOLUME_CORRUPTED) &&\r
+ (ProblemParam != NULL) ) {\r
+ ShellPrintHiiEx (\r
+ -1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), gShellTftpHiiHandle,\r
+ L"tftp", ProblemParam\r
+ );\r
+ FreePool (ProblemParam);\r
+ } else {\r
+ ASSERT (FALSE);\r
+ }\r
+ goto Error;\r
+ }\r
+\r
+ //\r
+ // Check the number of parameters\r
+ //\r
+ ParamCount = ShellCommandLineGetCount (CheckPackage);\r
+ if (ParamCount > 4) {\r
+ ShellPrintHiiEx (\r
+ -1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_MANY),\r
+ gShellTftpHiiHandle, L"tftp"\r
+ );\r
+ goto Error;\r
+ }\r
+ if (ParamCount < 3) {\r
+ ShellPrintHiiEx (\r
+ -1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_FEW),\r
+ gShellTftpHiiHandle, L"tftp"\r
+ );\r
+ goto Error;\r
+ }\r
+\r
+ Mtftp4ConfigData = DefaultMtftp4ConfigData;\r
+\r
+ //\r
+ // Check the host IPv4 address\r
+ //\r
+ ValueStr = ShellCommandLineGetRawValue (CheckPackage, 1);\r
+ Status = NetLibStrToIp4 (ValueStr, &Mtftp4ConfigData.ServerIp);\r
+ if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (\r
+ -1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV),\r
+ gShellTftpHiiHandle, L"tftp", ValueStr\r
+ );\r
+ goto Error;\r
+ }\r
+\r
+ RemoteFilePath = ShellCommandLineGetRawValue (CheckPackage, 2);\r
+ AsciiRemoteFilePath = AllocatePool (\r
+ (StrLen (RemoteFilePath) + 1) * sizeof (CHAR8)\r
+ );\r
+ if (AsciiRemoteFilePath == NULL) {\r
+ ShellStatus = SHELL_OUT_OF_RESOURCES;\r
+ goto Error;\r
+ }\r
+ UnicodeStrToAsciiStr (RemoteFilePath, AsciiRemoteFilePath);\r
+\r
+ if (ParamCount == 4) {\r
+ LocalFilePath = ShellCommandLineGetRawValue (CheckPackage, 3);\r
+ } else {\r
+ Walker = RemoteFilePath + StrLen (RemoteFilePath);\r
+ while ((--Walker) >= RemoteFilePath) {\r
+ if ((*Walker == L'\\') ||\r
+ (*Walker == L'/' ) ) {\r
+ break;\r
+ }\r
+ }\r
+ LocalFilePath = Walker + 1;\r
+ }\r
+\r
+ //\r
+ // Get the name of the Network Interface Card to be used if any.\r
+ //\r
+ UserNicName = ShellCommandLineGetValue (CheckPackage, L"-i");\r
+\r
+ ValueStr = ShellCommandLineGetValue (CheckPackage, L"-l");\r
+ if (ValueStr != NULL) {\r
+ if (!StringToUint16 (ValueStr, &Mtftp4ConfigData.LocalPort)) {\r
+ goto Error;\r
+ }\r
+ }\r
+\r
+ ValueStr = ShellCommandLineGetValue (CheckPackage, L"-r");\r
+ if (ValueStr != NULL) {\r
+ if (!StringToUint16 (ValueStr, &Mtftp4ConfigData.InitialServerPort)) {\r
+ goto Error;\r
+ }\r
+ }\r
+\r
+ ValueStr = ShellCommandLineGetValue (CheckPackage, L"-c");\r
+ if (ValueStr != NULL) {\r
+ if (!StringToUint16 (ValueStr, &Mtftp4ConfigData.TryCount)) {\r
+ goto Error;\r
+ }\r
+ }\r
+\r
+ ValueStr = ShellCommandLineGetValue (CheckPackage, L"-t");\r
+ if (ValueStr != NULL) {\r
+ if (!StringToUint16 (ValueStr, &Mtftp4ConfigData.TimeoutValue)) {\r
+ goto Error;\r
+ }\r
+ if (Mtftp4ConfigData.TimeoutValue == 0) {\r
+ ShellPrintHiiEx (\r
+ -1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV),\r
+ gShellTftpHiiHandle, L"tftp", ValueStr\r
+ );\r
+ goto Error;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Locate all MTFTP4 Service Binding protocols\r
+ //\r
+ ShellStatus = SHELL_NOT_FOUND;\r
+ Status = gBS->LocateHandleBuffer (\r
+ ByProtocol,\r
+ &gEfiManagedNetworkServiceBindingProtocolGuid,\r
+ NULL,\r
+ &HandleCount,\r
+ &Handles\r
+ );\r
+ if (EFI_ERROR (Status) || (HandleCount == 0)) {\r
+ ShellPrintHiiEx (\r
+ -1, -1, NULL, STRING_TOKEN (STR_TFTP_ERR_NO_NIC),\r
+ gShellTftpHiiHandle\r
+ );\r
+ goto Error;\r
+ }\r
+\r
+ for (NicNumber = 0;\r
+ (NicNumber < HandleCount) && (ShellStatus != SHELL_SUCCESS);\r
+ NicNumber++) {\r
+ ControllerHandle = Handles[NicNumber];\r
+ Data = NULL;\r
+\r
+ Status = GetNicName (ControllerHandle, NicNumber, NicName);\r
+ if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (\r
+ -1, -1, NULL, STRING_TOKEN (STR_TFTP_ERR_NIC_NAME),\r
+ gShellTftpHiiHandle, NicNumber, Status\r
+ );\r
+ continue;\r
+ }\r
+\r
+ if (UserNicName != NULL) {\r
+ if (StrCmp (NicName, UserNicName) != 0) {\r
+ continue;\r
+ }\r
+ NicFound = TRUE;\r
+ }\r
+\r
+ Status = CreateServiceChildAndOpenProtocol (\r
+ ControllerHandle,\r
+ &gEfiMtftp4ServiceBindingProtocolGuid,\r
+ &gEfiMtftp4ProtocolGuid,\r
+ &Mtftp4ChildHandle,\r
+ (VOID**)&Mtftp4\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (\r
+ -1, -1, NULL, STRING_TOKEN (STR_TFTP_ERR_OPEN_PROTOCOL),\r
+ gShellTftpHiiHandle, NicName, Status\r
+ );\r
+ continue;\r
+ }\r
+\r
+ Status = Mtftp4->Configure (Mtftp4, &Mtftp4ConfigData);\r
+ if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (\r
+ -1, -1, NULL, STRING_TOKEN (STR_TFTP_ERR_CONFIGURE),\r
+ gShellTftpHiiHandle, NicName, Status\r
+ );\r
+ goto NextHandle;\r
+ }\r
+\r
+ Status = GetFileSize (Mtftp4, AsciiRemoteFilePath, &FileSize);\r
+ if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (\r
+ -1, -1, NULL, STRING_TOKEN (STR_TFTP_ERR_FILE_SIZE),\r
+ gShellTftpHiiHandle, RemoteFilePath, NicName, Status\r
+ );\r
+ goto NextHandle;\r
+ }\r
+\r
+ Status = DownloadFile (Mtftp4, RemoteFilePath, AsciiRemoteFilePath, FileSize, &Data);\r
+ if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (\r
+ -1, -1, NULL, STRING_TOKEN (STR_TFTP_ERR_DOWNLOAD),\r
+ gShellTftpHiiHandle, RemoteFilePath, NicName, Status\r
+ );\r
+ goto NextHandle;\r
+ }\r
+\r
+ if (!EFI_ERROR (ShellFileExists (LocalFilePath))) {\r
+ ShellDeleteFileByName (LocalFilePath);\r
+ }\r
+\r
+ Status = ShellOpenFileByName (\r
+ LocalFilePath,\r
+ &FileHandle,\r
+ EFI_FILE_MODE_CREATE |\r
+ EFI_FILE_MODE_WRITE |\r
+ EFI_FILE_MODE_READ,\r
+ 0\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (\r
+ -1, -1, NULL, STRING_TOKEN (STR_GEN_FILE_OPEN_FAIL),\r
+ gShellTftpHiiHandle, L"tftp", LocalFilePath\r
+ );\r
+ goto NextHandle;\r
+ }\r
+\r
+ Status = ShellWriteFile (FileHandle, &FileSize, Data);\r
+ if (!EFI_ERROR (Status)) {\r
+ ShellStatus = SHELL_SUCCESS;\r
+ } else {\r
+ ShellPrintHiiEx (\r
+ -1, -1, NULL, STRING_TOKEN (STR_TFTP_ERR_WRITE),\r
+ gShellTftpHiiHandle, LocalFilePath, Status\r
+ );\r
+ }\r
+ ShellCloseFile (&FileHandle);\r
+\r
+ NextHandle:\r
+\r
+ if (Data != NULL) {\r
+ gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)Data, EFI_SIZE_TO_PAGES (FileSize));\r
+ }\r
+\r
+ CloseProtocolAndDestroyServiceChild (\r
+ ControllerHandle,\r
+ &gEfiMtftp4ServiceBindingProtocolGuid,\r
+ &gEfiMtftp4ProtocolGuid,\r
+ Mtftp4ChildHandle\r
+ );\r
+ }\r
+\r
+ if ((UserNicName != NULL) && (NicFound == FALSE)) {\r
+ ShellPrintHiiEx (\r
+ -1, -1, NULL, STRING_TOKEN (STR_TFTP_ERR_NIC_NOT_FOUND),\r
+ gShellTftpHiiHandle, UserNicName\r
+ );\r
+ }\r
+\r
+ Error:\r
+\r
+ ShellCommandLineFreeVarList (CheckPackage);\r
+ if (AsciiRemoteFilePath != NULL) {\r
+ FreePool (AsciiRemoteFilePath);\r
+ }\r
+ if (Handles != NULL) {\r
+ FreePool (Handles);\r
+ }\r
+\r
+ return ShellStatus;\r
+}\r
+\r
+/**\r
+ Check and convert the UINT16 option values of the 'tftp' command\r
+\r
+ @param[in] ValueStr Value as an Unicode encoded string\r
+ @param[out] Value UINT16 value\r
+\r
+ @return TRUE The value was returned.\r
+ @return FALSE A parsing error occured.\r
+**/\r
+STATIC\r
+BOOLEAN\r
+StringToUint16 (\r
+ IN CONST CHAR16 *ValueStr,\r
+ OUT UINT16 *Value\r
+ )\r
+{\r
+ UINTN Val;\r
+\r
+ Val = ShellStrToUintn (ValueStr);\r
+ if (Val > MAX_UINT16) {\r
+ ShellPrintHiiEx (\r
+ -1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV),\r
+ gShellTftpHiiHandle, L"tftp", ValueStr\r
+ );\r
+ return FALSE;\r
+ }\r
+\r
+ *Value = Val;\r
+ return TRUE;\r
+}\r
+\r
+/**\r
+ Get the name of the NIC.\r
+\r
+ @param[in] ControllerHandle The network physical device handle.\r
+ @param[in] NicNumber The network physical device number.\r
+ @param[out] NicName Address where to store the NIC name.\r
+ The memory area has to be at least\r
+ IP4_NIC_NAME_LENGTH bytes wide.\r
+\r
+ @return EFI_SUCCESS The name of the NIC was returned.\r
+ @return Others The creation of the child for the Managed\r
+ Network Service failed or the opening of\r
+ the Managed Network Protocol failed or\r
+ the operational parameters for the\r
+ Managed Network Protocol could not be\r
+ read.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+GetNicName (\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN UINTN NicNumber,\r
+ OUT CHAR16 *NicName\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_HANDLE MnpHandle;\r
+ EFI_MANAGED_NETWORK_PROTOCOL *Mnp;\r
+ EFI_SIMPLE_NETWORK_MODE SnpMode;\r
+\r
+ Status = CreateServiceChildAndOpenProtocol (\r
+ ControllerHandle,\r
+ &gEfiManagedNetworkServiceBindingProtocolGuid,\r
+ &gEfiManagedNetworkProtocolGuid,\r
+ &MnpHandle,\r
+ (VOID**)&Mnp\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+\r
+ Status = Mnp->GetModeData (Mnp, NULL, &SnpMode);\r
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_STARTED)) {\r
+ goto Error;\r
+ }\r
+\r
+ UnicodeSPrint (\r
+ NicName,\r
+ IP4_NIC_NAME_LENGTH,\r
+ SnpMode.IfType == NET_IFTYPE_ETHERNET ?\r
+ L"eth%d" :\r
+ L"unk%d" ,\r
+ NicNumber\r
+ );\r
+\r
+ Status = EFI_SUCCESS;\r
+\r
+Error:\r
+\r
+ if (MnpHandle != NULL) {\r
+ CloseProtocolAndDestroyServiceChild (\r
+ ControllerHandle,\r
+ &gEfiManagedNetworkServiceBindingProtocolGuid,\r
+ &gEfiManagedNetworkProtocolGuid,\r
+ MnpHandle\r
+ );\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Create a child for the service identified by its service binding protocol GUID\r
+ and get from the child the interface of the protocol identified by its GUID.\r
+\r
+ @param[in] ControllerHandle Controller handle.\r
+ @param[in] ServiceBindingProtocolGuid Service binding protocol GUID of the\r
+ service to be created.\r
+ @param[in] ProtocolGuid GUID of the protocol to be open.\r
+ @param[out] ChildHandle Address where the handler of the\r
+ created child is returned. NULL is\r
+ returned in case of error.\r
+ @param[out] Interface Address where a pointer to the\r
+ protocol interface is returned in\r
+ case of success.\r
+\r
+ @return EFI_SUCCESS The child was created and the protocol opened.\r
+ @return Others Either the creation of the child or the opening\r
+ of the protocol failed.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+CreateServiceChildAndOpenProtocol (\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_GUID *ServiceBindingProtocolGuid,\r
+ IN EFI_GUID *ProtocolGuid,\r
+ OUT EFI_HANDLE *ChildHandle,\r
+ OUT VOID **Interface\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ *ChildHandle = NULL;\r
+ Status = NetLibCreateServiceChild (\r
+ ControllerHandle,\r
+ gImageHandle,\r
+ ServiceBindingProtocolGuid,\r
+ ChildHandle\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ Status = gBS->OpenProtocol (\r
+ *ChildHandle,\r
+ ProtocolGuid,\r
+ Interface,\r
+ gImageHandle,\r
+ ControllerHandle,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ NetLibDestroyServiceChild (\r
+ ControllerHandle,\r
+ gImageHandle,\r
+ ServiceBindingProtocolGuid,\r
+ *ChildHandle\r
+ );\r
+ *ChildHandle = NULL;\r
+ }\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Close the protocol identified by its GUID on the child handle of the service\r
+ identified by its service binding protocol GUID, then destroy the child\r
+ handle.\r
+\r
+ @param[in] ControllerHandle Controller handle.\r
+ @param[in] ServiceBindingProtocolGuid Service binding protocol GUID of the\r
+ service to be destroyed.\r
+ @param[in] ProtocolGuid GUID of the protocol to be closed.\r
+ @param[in] ChildHandle Handle of the child to be destroyed.\r
+\r
+**/\r
+STATIC\r
+VOID\r
+CloseProtocolAndDestroyServiceChild (\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_GUID *ServiceBindingProtocolGuid,\r
+ IN EFI_GUID *ProtocolGuid,\r
+ IN EFI_HANDLE ChildHandle\r
+ )\r
+{\r
+ gBS->CloseProtocol (\r
+ ChildHandle,\r
+ ProtocolGuid,\r
+ gImageHandle,\r
+ ControllerHandle\r
+ );\r
+\r
+ NetLibDestroyServiceChild (\r
+ ControllerHandle,\r
+ gImageHandle,\r
+ ServiceBindingProtocolGuid,\r
+ ChildHandle\r
+ );\r
+}\r
+\r
+/**\r
+ Worker function that gets the size in numbers of bytes of a file from a TFTP\r
+ server before to download the file.\r
+\r
+ @param[in] Mtftp4 MTFTP4 protocol interface\r
+ @param[in] FilePath Path of the file, ASCII encoded\r
+ @param[out] FileSize Address where to store the file size in number of\r
+ bytes.\r
+\r
+ @retval EFI_SUCCESS The size of the file was returned.\r
+ @retval EFI_UNSUPPORTED The server does not support the "tsize" option.\r
+ @retval Others Error when retrieving the information from the server\r
+ (see EFI_MTFTP4_PROTOCOL.GetInfo() status codes)\r
+ or error when parsing the response of the server.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+GetFileSize (\r
+ IN EFI_MTFTP4_PROTOCOL *Mtftp4,\r
+ IN CONST CHAR8 *FilePath,\r
+ OUT UINTN *FileSize\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_MTFTP4_OPTION ReqOpt[1];\r
+ EFI_MTFTP4_PACKET *Packet;\r
+ UINT32 PktLen;\r
+ EFI_MTFTP4_OPTION *TableOfOptions;\r
+ EFI_MTFTP4_OPTION *Option;\r
+ UINT32 OptCnt;\r
+ UINT8 OptBuf[128];\r
+\r
+ ReqOpt[0].OptionStr = (UINT8*)"tsize";\r
+ OptBuf[0] = '0';\r
+ OptBuf[1] = 0;\r
+ ReqOpt[0].ValueStr = OptBuf;\r
+\r
+ Status = Mtftp4->GetInfo (\r
+ Mtftp4,\r
+ NULL,\r
+ (UINT8*)FilePath,\r
+ NULL,\r
+ 1,\r
+ ReqOpt,\r
+ &PktLen,\r
+ &Packet\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+\r
+ Status = Mtftp4->ParseOptions (\r
+ Mtftp4,\r
+ PktLen,\r
+ Packet,\r
+ (UINT32 *) &OptCnt,\r
+ &TableOfOptions\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+\r
+ Option = TableOfOptions;\r
+ while (OptCnt != 0) {\r
+ if (AsciiStrnCmp ((CHAR8 *)Option->OptionStr, "tsize", 5) == 0) {\r
+ *FileSize = AsciiStrDecimalToUintn ((CHAR8 *)Option->ValueStr);\r
+ break;\r
+ }\r
+ OptCnt--;\r
+ Option++;\r
+ }\r
+ FreePool (TableOfOptions);\r
+\r
+ if (OptCnt == 0) {\r
+ Status = EFI_UNSUPPORTED;\r
+ }\r
+\r
+Error :\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Worker function that download the data of a file from a TFTP server given\r
+ the path of the file and its size.\r
+\r
+ @param[in] Mtftp4 MTFTP4 protocol interface\r
+ @param[in] FilePath Path of the file, Unicode encoded\r
+ @param[in] AsciiFilePath Path of the file, ASCII encoded\r
+ @param[in] FileSize Size of the file in number of bytes\r
+ @param[out] Data Address where to store the address of the buffer\r
+ where the data of the file were downloaded in\r
+ case of success.\r
+\r
+ @retval EFI_SUCCESS The file was downloaded.\r
+ @retval EFI_OUT_OF_RESOURCES A memory allocation failed.\r
+ @retval Others The downloading of the file from the server failed\r
+ (see EFI_MTFTP4_PROTOCOL.ReadFile() status codes).\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+DownloadFile (\r
+ IN EFI_MTFTP4_PROTOCOL *Mtftp4,\r
+ IN CONST CHAR16 *FilePath,\r
+ IN CONST CHAR8 *AsciiFilePath,\r
+ IN UINTN FileSize,\r
+ OUT VOID **Data\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_PHYSICAL_ADDRESS PagesAddress;\r
+ VOID *Buffer;\r
+ DOWNLOAD_CONTEXT *TftpContext;\r
+ EFI_MTFTP4_TOKEN Mtftp4Token;\r
+\r
+ // Downloaded file can be large. BS.AllocatePages() is more faster\r
+ // than AllocatePool() and avoid fragmentation.\r
+ // The downloaded file could be an EFI application. Marking the\r
+ // allocated page as EfiBootServicesCode would allow to execute a\r
+ // potential downloaded EFI application.\r
+ Status = gBS->AllocatePages (\r
+ AllocateAnyPages,\r
+ EfiBootServicesCode,\r
+ EFI_SIZE_TO_PAGES (FileSize),\r
+ &PagesAddress\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Buffer = (VOID*)(UINTN)PagesAddress;\r
+ TftpContext = AllocatePool (sizeof (DOWNLOAD_CONTEXT));\r
+ if (TftpContext == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Error;\r
+ }\r
+ TftpContext->FileSize = FileSize;\r
+ TftpContext->DownloadedNbOfBytes = 0;\r
+ TftpContext->LastReportedNbOfBytes = 0;\r
+\r
+ ZeroMem (&Mtftp4Token, sizeof (EFI_MTFTP4_TOKEN));\r
+ Mtftp4Token.Filename = (UINT8*)AsciiFilePath;\r
+ Mtftp4Token.BufferSize = FileSize;\r
+ Mtftp4Token.Buffer = Buffer;\r
+ Mtftp4Token.CheckPacket = CheckPacket;\r
+ Mtftp4Token.Context = (VOID*)TftpContext;\r
+\r
+ ShellPrintHiiEx (\r
+ -1, -1, NULL, STRING_TOKEN (STR_TFTP_DOWNLOADING),\r
+ gShellTftpHiiHandle, FilePath\r
+ );\r
+\r
+ Status = Mtftp4->ReadFile (Mtftp4, &Mtftp4Token);\r
+ ShellPrintHiiEx (\r
+ -1, -1, NULL, STRING_TOKEN (STR_GEN_CRLF),\r
+ gShellTftpHiiHandle\r
+ );\r
+\r
+Error :\r
+\r
+ if (TftpContext == NULL) {\r
+ FreePool (TftpContext);\r
+ }\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ gBS->FreePages (PagesAddress, EFI_SIZE_TO_PAGES (FileSize));\r
+ return Status;\r
+ }\r
+\r
+ *Data = Buffer;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Update the progress of a file download\r
+ This procedure is called each time a new TFTP packet is received.\r
+\r
+ @param[in] This MTFTP4 protocol interface\r
+ @param[in] Token Parameters for the download of the file\r
+ @param[in] PacketLen Length of the packet\r
+ @param[in] Packet Address of the packet\r
+\r
+ @retval EFI_SUCCESS All packets are accepted.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+CheckPacket (\r
+ IN EFI_MTFTP4_PROTOCOL *This,\r
+ IN EFI_MTFTP4_TOKEN *Token,\r
+ IN UINT16 PacketLen,\r
+ IN EFI_MTFTP4_PACKET *Packet\r
+ )\r
+{\r
+ DOWNLOAD_CONTEXT *Context;\r
+ CHAR16 Progress[TFTP_PROGRESS_MESSAGE_SIZE];\r
+ UINT64 NbOfKb;\r
+ UINTN Index;\r
+ UINTN LastStep;\r
+ UINTN Step;\r
+\r
+ if ((NTOHS (Packet->OpCode)) != EFI_MTFTP4_OPCODE_DATA) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ Context = (DOWNLOAD_CONTEXT*)Token->Context;\r
+ if (Context->DownloadedNbOfBytes == 0) {\r
+ ShellPrintEx (-1, -1, L"%s 0 Kb", mTftpProgressFrame);\r
+ }\r
+\r
+ //\r
+ // The data in the packet are prepended with two UINT16 :\r
+ // . OpCode = EFI_MTFTP4_OPCODE_DATA\r
+ // . Block = the number of this block of data\r
+ //\r
+ Context->DownloadedNbOfBytes += PacketLen - sizeof (Packet->OpCode)\r
+ - sizeof (Packet->Data.Block);\r
+ NbOfKb = Context->DownloadedNbOfBytes / 1024;\r
+\r
+ Progress[0] = L'\0';\r
+ LastStep = (Context->LastReportedNbOfBytes * TFTP_PROGRESS_SLIDER_STEPS) /\r
+ Context->FileSize;\r
+ Step = (Context->DownloadedNbOfBytes * TFTP_PROGRESS_SLIDER_STEPS) /\r
+ Context->FileSize;\r
+ if (Step <= LastStep) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ ShellPrintEx (-1, -1, L"%s", mTftpProgressDelete);\r
+\r
+ StrCpy (Progress, mTftpProgressFrame);\r
+ for (Index = 1; Index < Step; Index++) {\r
+ Progress[Index] = L'=';\r
+ }\r
+ Progress[Step] = L'>';\r
+\r
+ UnicodeSPrint (\r
+ Progress + (sizeof (mTftpProgressFrame) / sizeof (CHAR16)) - 1,\r
+ sizeof (Progress) - sizeof (mTftpProgressFrame),\r
+ L" %7d Kb",\r
+ NbOfKb\r
+ );\r
+ Context->LastReportedNbOfBytes = Context->DownloadedNbOfBytes;\r
+\r
+ ShellPrintEx (-1, -1, L"%s", Progress);\r
+\r
+ return EFI_SUCCESS;\r
+}\r