From 680742607132a7733880407453b5f792699d7143 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Wed, 15 Jul 2015 15:39:53 +0000 Subject: [PATCH] ShellPkg: Add optional 'tftp' EFI Shell command This 'tftp' command allows to download a file from a TFTP server. A specific network interface can be chosen in case there are multiple interfaces. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Ronald Cron Reviewed-by: Olivier Martin Reviewed-by: Jaben Carsey git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@18015 6f19259b-4bc3-4df7-8a09-765794883524 --- ShellPkg/Include/Guid/ShellLibHiiGuid.h | 7 + .../Library/UefiShellTftpCommandLib/Tftp.c | 880 ++++++++++++++++++ .../UefiShellTftpCommandLib.c | 97 ++ .../UefiShellTftpCommandLib.h | 63 ++ .../UefiShellTftpCommandLib.inf | 61 ++ .../UefiShellTftpCommandLib.uni | Bin 0 -> 8748 bytes ShellPkg/ShellPkg.dec | 1 + ShellPkg/ShellPkg.dsc | 3 + 8 files changed, 1112 insertions(+) create mode 100644 ShellPkg/Library/UefiShellTftpCommandLib/Tftp.c create mode 100644 ShellPkg/Library/UefiShellTftpCommandLib/UefiShellTftpCommandLib.c create mode 100644 ShellPkg/Library/UefiShellTftpCommandLib/UefiShellTftpCommandLib.h create mode 100644 ShellPkg/Library/UefiShellTftpCommandLib/UefiShellTftpCommandLib.inf create mode 100644 ShellPkg/Library/UefiShellTftpCommandLib/UefiShellTftpCommandLib.uni diff --git a/ShellPkg/Include/Guid/ShellLibHiiGuid.h b/ShellPkg/Include/Guid/ShellLibHiiGuid.h index dc694f2915..62c2e72ec9 100644 --- a/ShellPkg/Include/Guid/ShellLibHiiGuid.h +++ b/ShellPkg/Include/Guid/ShellLibHiiGuid.h @@ -54,6 +54,12 @@ { \ 0xf3d301bb, 0xf4a5, 0x45a8, { 0xb0, 0xb7, 0xfa, 0x99, 0x9c, 0x62, 0x37, 0xae } \ } +#define SHELL_TFTP_HII_GUID \ + { \ + 0x738a9314, 0x82c1, 0x4592, { 0x8f, 0xf7, 0xc1, 0xbd, 0xf1, 0xb2, 0x0e, 0xd4 } \ + } + + #define SHELL_BCFG_HII_GUID \ { \ 0x5f5f605d, 0x1583, 0x4a2d, {0xa6, 0xb2, 0xeb, 0x12, 0xda, 0xb4, 0xa2, 0xb6 } \ @@ -67,6 +73,7 @@ extern EFI_GUID gShellLevel1HiiGuid; extern EFI_GUID gShellLevel2HiiGuid; extern EFI_GUID gShellLevel3HiiGuid; extern EFI_GUID gShellNetwork1HiiGuid; +extern EFI_GUID gShellTftpHiiGuid; extern EFI_GUID gShellBcfgHiiGuid; #endif diff --git a/ShellPkg/Library/UefiShellTftpCommandLib/Tftp.c b/ShellPkg/Library/UefiShellTftpCommandLib/Tftp.c new file mode 100644 index 0000000000..b872afdb89 --- /dev/null +++ b/ShellPkg/Library/UefiShellTftpCommandLib/Tftp.c @@ -0,0 +1,880 @@ +/** @file + The implementation for the 'tftp' Shell command. + + Copyright (c) 2015, ARM Ltd. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +**/ + +#include "UefiShellTftpCommandLib.h" + +/* + Constant strings and definitions related to the message indicating the amount of + progress in the dowloading of a TFTP file. +*/ + +// Frame for the progression slider +STATIC CONST CHAR16 mTftpProgressFrame[] = L"[ ]"; + +// Number of steps in the progression slider +#define TFTP_PROGRESS_SLIDER_STEPS ((sizeof (mTftpProgressFrame) / sizeof (CHAR16)) - 3) + +// Size in number of characters plus one (final zero) of the message to +// indicate the progress of a TFTP download. The format is "[(progress slider: +// 40 characters)] (nb of KBytes downloaded so far: 7 characters) Kb". There +// are thus the number of characters in mTftpProgressFrame[] plus 11 characters +// (2 // spaces, "Kb" and seven characters for the number of KBytes). +#define TFTP_PROGRESS_MESSAGE_SIZE ((sizeof (mTftpProgressFrame) / sizeof (CHAR16)) + 12) + +// String to delete the TFTP progress message to be able to update it : +// (TFTP_PROGRESS_MESSAGE_SIZE-1) '\b' +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"; + +STATIC BOOLEAN StringToUint16 ( + IN CONST CHAR16 *ValueStr, + OUT UINT16 *Value + ); + +STATIC EFI_STATUS GetNicName ( + IN EFI_HANDLE ControllerHandle, + IN UINTN Index, + OUT CHAR16 *NicName + ); + +STATIC EFI_STATUS CreateServiceChildAndOpenProtocol ( + IN EFI_HANDLE ControllerHandle, + IN EFI_GUID *ServiceBindingProtocolGuid, + IN EFI_GUID *ProtocolGuid, + OUT EFI_HANDLE *ChildHandle, + OUT VOID **Interface + ); + +STATIC VOID CloseProtocolAndDestroyServiceChild ( + IN EFI_HANDLE ControllerHandle, + IN EFI_GUID *ServiceBindingProtocolGuid, + IN EFI_GUID *ProtocolGuid, + IN EFI_HANDLE ChildHandle + ); + +STATIC EFI_STATUS GetFileSize ( + IN EFI_MTFTP4_PROTOCOL *Mtftp4, + IN CONST CHAR8 *FilePath, + OUT UINTN *FileSize + ); + +STATIC EFI_STATUS DownloadFile ( + IN EFI_MTFTP4_PROTOCOL *Mtftp4, + IN CONST CHAR16 *FilePath, + IN CONST CHAR8 *AsciiFilePath, + IN UINTN FileSize, + OUT VOID **Data + ); + +STATIC EFI_STATUS CheckPacket ( + IN EFI_MTFTP4_PROTOCOL *This, + IN EFI_MTFTP4_TOKEN *Token, + IN UINT16 PacketLen, + IN EFI_MTFTP4_PACKET *Packet + ); + +EFI_MTFTP4_CONFIG_DATA DefaultMtftp4ConfigData = { + TRUE, // Use default setting + { { 0, 0, 0, 0 } }, // StationIp - Not relevant as UseDefaultSetting=TRUE + { { 0, 0, 0, 0 } }, // SubnetMask - Not relevant as UseDefaultSetting=TRUE + 0, // LocalPort - Automatically assigned port number. + { { 0, 0, 0, 0 } }, // GatewayIp - Not relevant as UseDefaultSetting=TRUE + { { 0, 0, 0, 0 } }, // ServerIp - Not known yet + 69, // InitialServerPort - Standard TFTP server port + 6, // TryCount - Max number of retransmissions. + 4 // TimeoutValue - Retransmission timeout in seconds. +}; + +STATIC CONST SHELL_PARAM_ITEM ParamList[] = { + {L"-i", TypeValue}, + {L"-l", TypeValue}, + {L"-r", TypeValue}, + {L"-c", TypeValue}, + {L"-t", TypeValue}, + {NULL , TypeMax} + }; + +/** + Function for 'tftp' command. + + @param[in] ImageHandle Handle to the Image (NULL if Internal). + @param[in] SystemTable Pointer to the System Table (NULL if Internal). + + @return SHELL_SUCCESS The 'tftp' command completed successfully. + @return SHELL_ABORTED The Shell Library initialization failed. + @return SHELL_INVALID_PARAMETER At least one of the command's arguments is + not valid. + @return SHELL_OUT_OF_RESOURCES A memory allocation failed. + @return SHELL_NOT_FOUND Network Interface Card not found or server + error or file error. + +**/ +SHELL_STATUS +EFIAPI +ShellCommandRunTftp ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + SHELL_STATUS ShellStatus; + EFI_STATUS Status; + LIST_ENTRY *CheckPackage; + CHAR16 *ProblemParam; + UINTN ParamCount; + CONST CHAR16 *UserNicName; + BOOLEAN NicFound; + CONST CHAR16 *ValueStr; + CONST CHAR16 *RemoteFilePath; + CHAR8 *AsciiRemoteFilePath; + CONST CHAR16 *Walker; + CONST CHAR16 *LocalFilePath; + EFI_MTFTP4_CONFIG_DATA Mtftp4ConfigData; + EFI_HANDLE *Handles; + UINTN HandleCount; + UINTN NicNumber; + CHAR16 NicName[IP4_NIC_NAME_LENGTH]; + EFI_HANDLE ControllerHandle; + EFI_HANDLE Mtftp4ChildHandle; + EFI_MTFTP4_PROTOCOL *Mtftp4; + UINTN FileSize; + VOID *Data; + SHELL_FILE_HANDLE FileHandle; + + ShellStatus = SHELL_INVALID_PARAMETER; + ProblemParam = NULL; + NicFound = FALSE; + AsciiRemoteFilePath = NULL; + Handles = NULL; + + // + // Initialize the Shell library (we must be in non-auto-init...) + // + Status = ShellInitialize (); + if (EFI_ERROR (Status)) { + ASSERT_EFI_ERROR (Status); + return SHELL_ABORTED; + } + + // + // Parse the command line. + // + Status = ShellCommandLineParse (ParamList, &CheckPackage, &ProblemParam, TRUE); + if (EFI_ERROR (Status)) { + if ((Status == EFI_VOLUME_CORRUPTED) && + (ProblemParam != NULL) ) { + ShellPrintHiiEx ( + -1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), gShellTftpHiiHandle, + L"tftp", ProblemParam + ); + FreePool (ProblemParam); + } else { + ASSERT (FALSE); + } + goto Error; + } + + // + // Check the number of parameters + // + ParamCount = ShellCommandLineGetCount (CheckPackage); + if (ParamCount > 4) { + ShellPrintHiiEx ( + -1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_MANY), + gShellTftpHiiHandle, L"tftp" + ); + goto Error; + } + if (ParamCount < 3) { + ShellPrintHiiEx ( + -1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_FEW), + gShellTftpHiiHandle, L"tftp" + ); + goto Error; + } + + Mtftp4ConfigData = DefaultMtftp4ConfigData; + + // + // Check the host IPv4 address + // + ValueStr = ShellCommandLineGetRawValue (CheckPackage, 1); + Status = NetLibStrToIp4 (ValueStr, &Mtftp4ConfigData.ServerIp); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx ( + -1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), + gShellTftpHiiHandle, L"tftp", ValueStr + ); + goto Error; + } + + RemoteFilePath = ShellCommandLineGetRawValue (CheckPackage, 2); + AsciiRemoteFilePath = AllocatePool ( + (StrLen (RemoteFilePath) + 1) * sizeof (CHAR8) + ); + if (AsciiRemoteFilePath == NULL) { + ShellStatus = SHELL_OUT_OF_RESOURCES; + goto Error; + } + UnicodeStrToAsciiStr (RemoteFilePath, AsciiRemoteFilePath); + + if (ParamCount == 4) { + LocalFilePath = ShellCommandLineGetRawValue (CheckPackage, 3); + } else { + Walker = RemoteFilePath + StrLen (RemoteFilePath); + while ((--Walker) >= RemoteFilePath) { + if ((*Walker == L'\\') || + (*Walker == L'/' ) ) { + break; + } + } + LocalFilePath = Walker + 1; + } + + // + // Get the name of the Network Interface Card to be used if any. + // + UserNicName = ShellCommandLineGetValue (CheckPackage, L"-i"); + + ValueStr = ShellCommandLineGetValue (CheckPackage, L"-l"); + if (ValueStr != NULL) { + if (!StringToUint16 (ValueStr, &Mtftp4ConfigData.LocalPort)) { + goto Error; + } + } + + ValueStr = ShellCommandLineGetValue (CheckPackage, L"-r"); + if (ValueStr != NULL) { + if (!StringToUint16 (ValueStr, &Mtftp4ConfigData.InitialServerPort)) { + goto Error; + } + } + + ValueStr = ShellCommandLineGetValue (CheckPackage, L"-c"); + if (ValueStr != NULL) { + if (!StringToUint16 (ValueStr, &Mtftp4ConfigData.TryCount)) { + goto Error; + } + } + + ValueStr = ShellCommandLineGetValue (CheckPackage, L"-t"); + if (ValueStr != NULL) { + if (!StringToUint16 (ValueStr, &Mtftp4ConfigData.TimeoutValue)) { + goto Error; + } + if (Mtftp4ConfigData.TimeoutValue == 0) { + ShellPrintHiiEx ( + -1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), + gShellTftpHiiHandle, L"tftp", ValueStr + ); + goto Error; + } + } + + // + // Locate all MTFTP4 Service Binding protocols + // + ShellStatus = SHELL_NOT_FOUND; + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiManagedNetworkServiceBindingProtocolGuid, + NULL, + &HandleCount, + &Handles + ); + if (EFI_ERROR (Status) || (HandleCount == 0)) { + ShellPrintHiiEx ( + -1, -1, NULL, STRING_TOKEN (STR_TFTP_ERR_NO_NIC), + gShellTftpHiiHandle + ); + goto Error; + } + + for (NicNumber = 0; + (NicNumber < HandleCount) && (ShellStatus != SHELL_SUCCESS); + NicNumber++) { + ControllerHandle = Handles[NicNumber]; + Data = NULL; + + Status = GetNicName (ControllerHandle, NicNumber, NicName); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx ( + -1, -1, NULL, STRING_TOKEN (STR_TFTP_ERR_NIC_NAME), + gShellTftpHiiHandle, NicNumber, Status + ); + continue; + } + + if (UserNicName != NULL) { + if (StrCmp (NicName, UserNicName) != 0) { + continue; + } + NicFound = TRUE; + } + + Status = CreateServiceChildAndOpenProtocol ( + ControllerHandle, + &gEfiMtftp4ServiceBindingProtocolGuid, + &gEfiMtftp4ProtocolGuid, + &Mtftp4ChildHandle, + (VOID**)&Mtftp4 + ); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx ( + -1, -1, NULL, STRING_TOKEN (STR_TFTP_ERR_OPEN_PROTOCOL), + gShellTftpHiiHandle, NicName, Status + ); + continue; + } + + Status = Mtftp4->Configure (Mtftp4, &Mtftp4ConfigData); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx ( + -1, -1, NULL, STRING_TOKEN (STR_TFTP_ERR_CONFIGURE), + gShellTftpHiiHandle, NicName, Status + ); + goto NextHandle; + } + + Status = GetFileSize (Mtftp4, AsciiRemoteFilePath, &FileSize); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx ( + -1, -1, NULL, STRING_TOKEN (STR_TFTP_ERR_FILE_SIZE), + gShellTftpHiiHandle, RemoteFilePath, NicName, Status + ); + goto NextHandle; + } + + Status = DownloadFile (Mtftp4, RemoteFilePath, AsciiRemoteFilePath, FileSize, &Data); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx ( + -1, -1, NULL, STRING_TOKEN (STR_TFTP_ERR_DOWNLOAD), + gShellTftpHiiHandle, RemoteFilePath, NicName, Status + ); + goto NextHandle; + } + + if (!EFI_ERROR (ShellFileExists (LocalFilePath))) { + ShellDeleteFileByName (LocalFilePath); + } + + Status = ShellOpenFileByName ( + LocalFilePath, + &FileHandle, + EFI_FILE_MODE_CREATE | + EFI_FILE_MODE_WRITE | + EFI_FILE_MODE_READ, + 0 + ); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx ( + -1, -1, NULL, STRING_TOKEN (STR_GEN_FILE_OPEN_FAIL), + gShellTftpHiiHandle, L"tftp", LocalFilePath + ); + goto NextHandle; + } + + Status = ShellWriteFile (FileHandle, &FileSize, Data); + if (!EFI_ERROR (Status)) { + ShellStatus = SHELL_SUCCESS; + } else { + ShellPrintHiiEx ( + -1, -1, NULL, STRING_TOKEN (STR_TFTP_ERR_WRITE), + gShellTftpHiiHandle, LocalFilePath, Status + ); + } + ShellCloseFile (&FileHandle); + + NextHandle: + + if (Data != NULL) { + gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)Data, EFI_SIZE_TO_PAGES (FileSize)); + } + + CloseProtocolAndDestroyServiceChild ( + ControllerHandle, + &gEfiMtftp4ServiceBindingProtocolGuid, + &gEfiMtftp4ProtocolGuid, + Mtftp4ChildHandle + ); + } + + if ((UserNicName != NULL) && (NicFound == FALSE)) { + ShellPrintHiiEx ( + -1, -1, NULL, STRING_TOKEN (STR_TFTP_ERR_NIC_NOT_FOUND), + gShellTftpHiiHandle, UserNicName + ); + } + + Error: + + ShellCommandLineFreeVarList (CheckPackage); + if (AsciiRemoteFilePath != NULL) { + FreePool (AsciiRemoteFilePath); + } + if (Handles != NULL) { + FreePool (Handles); + } + + return ShellStatus; +} + +/** + Check and convert the UINT16 option values of the 'tftp' command + + @param[in] ValueStr Value as an Unicode encoded string + @param[out] Value UINT16 value + + @return TRUE The value was returned. + @return FALSE A parsing error occured. +**/ +STATIC +BOOLEAN +StringToUint16 ( + IN CONST CHAR16 *ValueStr, + OUT UINT16 *Value + ) +{ + UINTN Val; + + Val = ShellStrToUintn (ValueStr); + if (Val > MAX_UINT16) { + ShellPrintHiiEx ( + -1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), + gShellTftpHiiHandle, L"tftp", ValueStr + ); + return FALSE; + } + + *Value = Val; + return TRUE; +} + +/** + Get the name of the NIC. + + @param[in] ControllerHandle The network physical device handle. + @param[in] NicNumber The network physical device number. + @param[out] NicName Address where to store the NIC name. + The memory area has to be at least + IP4_NIC_NAME_LENGTH bytes wide. + + @return EFI_SUCCESS The name of the NIC was returned. + @return Others The creation of the child for the Managed + Network Service failed or the opening of + the Managed Network Protocol failed or + the operational parameters for the + Managed Network Protocol could not be + read. +**/ +STATIC +EFI_STATUS +GetNicName ( + IN EFI_HANDLE ControllerHandle, + IN UINTN NicNumber, + OUT CHAR16 *NicName + ) +{ + EFI_STATUS Status; + EFI_HANDLE MnpHandle; + EFI_MANAGED_NETWORK_PROTOCOL *Mnp; + EFI_SIMPLE_NETWORK_MODE SnpMode; + + Status = CreateServiceChildAndOpenProtocol ( + ControllerHandle, + &gEfiManagedNetworkServiceBindingProtocolGuid, + &gEfiManagedNetworkProtocolGuid, + &MnpHandle, + (VOID**)&Mnp + ); + if (EFI_ERROR (Status)) { + goto Error; + } + + Status = Mnp->GetModeData (Mnp, NULL, &SnpMode); + if (EFI_ERROR (Status) && (Status != EFI_NOT_STARTED)) { + goto Error; + } + + UnicodeSPrint ( + NicName, + IP4_NIC_NAME_LENGTH, + SnpMode.IfType == NET_IFTYPE_ETHERNET ? + L"eth%d" : + L"unk%d" , + NicNumber + ); + + Status = EFI_SUCCESS; + +Error: + + if (MnpHandle != NULL) { + CloseProtocolAndDestroyServiceChild ( + ControllerHandle, + &gEfiManagedNetworkServiceBindingProtocolGuid, + &gEfiManagedNetworkProtocolGuid, + MnpHandle + ); + } + + return Status; +} + +/** + Create a child for the service identified by its service binding protocol GUID + and get from the child the interface of the protocol identified by its GUID. + + @param[in] ControllerHandle Controller handle. + @param[in] ServiceBindingProtocolGuid Service binding protocol GUID of the + service to be created. + @param[in] ProtocolGuid GUID of the protocol to be open. + @param[out] ChildHandle Address where the handler of the + created child is returned. NULL is + returned in case of error. + @param[out] Interface Address where a pointer to the + protocol interface is returned in + case of success. + + @return EFI_SUCCESS The child was created and the protocol opened. + @return Others Either the creation of the child or the opening + of the protocol failed. +**/ +STATIC +EFI_STATUS +CreateServiceChildAndOpenProtocol ( + IN EFI_HANDLE ControllerHandle, + IN EFI_GUID *ServiceBindingProtocolGuid, + IN EFI_GUID *ProtocolGuid, + OUT EFI_HANDLE *ChildHandle, + OUT VOID **Interface + ) +{ + EFI_STATUS Status; + + *ChildHandle = NULL; + Status = NetLibCreateServiceChild ( + ControllerHandle, + gImageHandle, + ServiceBindingProtocolGuid, + ChildHandle + ); + if (!EFI_ERROR (Status)) { + Status = gBS->OpenProtocol ( + *ChildHandle, + ProtocolGuid, + Interface, + gImageHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + NetLibDestroyServiceChild ( + ControllerHandle, + gImageHandle, + ServiceBindingProtocolGuid, + *ChildHandle + ); + *ChildHandle = NULL; + } + } + + return Status; +} + +/** + Close the protocol identified by its GUID on the child handle of the service + identified by its service binding protocol GUID, then destroy the child + handle. + + @param[in] ControllerHandle Controller handle. + @param[in] ServiceBindingProtocolGuid Service binding protocol GUID of the + service to be destroyed. + @param[in] ProtocolGuid GUID of the protocol to be closed. + @param[in] ChildHandle Handle of the child to be destroyed. + +**/ +STATIC +VOID +CloseProtocolAndDestroyServiceChild ( + IN EFI_HANDLE ControllerHandle, + IN EFI_GUID *ServiceBindingProtocolGuid, + IN EFI_GUID *ProtocolGuid, + IN EFI_HANDLE ChildHandle + ) +{ + gBS->CloseProtocol ( + ChildHandle, + ProtocolGuid, + gImageHandle, + ControllerHandle + ); + + NetLibDestroyServiceChild ( + ControllerHandle, + gImageHandle, + ServiceBindingProtocolGuid, + ChildHandle + ); +} + +/** + Worker function that gets the size in numbers of bytes of a file from a TFTP + server before to download the file. + + @param[in] Mtftp4 MTFTP4 protocol interface + @param[in] FilePath Path of the file, ASCII encoded + @param[out] FileSize Address where to store the file size in number of + bytes. + + @retval EFI_SUCCESS The size of the file was returned. + @retval EFI_UNSUPPORTED The server does not support the "tsize" option. + @retval Others Error when retrieving the information from the server + (see EFI_MTFTP4_PROTOCOL.GetInfo() status codes) + or error when parsing the response of the server. +**/ +STATIC +EFI_STATUS +GetFileSize ( + IN EFI_MTFTP4_PROTOCOL *Mtftp4, + IN CONST CHAR8 *FilePath, + OUT UINTN *FileSize + ) +{ + EFI_STATUS Status; + EFI_MTFTP4_OPTION ReqOpt[1]; + EFI_MTFTP4_PACKET *Packet; + UINT32 PktLen; + EFI_MTFTP4_OPTION *TableOfOptions; + EFI_MTFTP4_OPTION *Option; + UINT32 OptCnt; + UINT8 OptBuf[128]; + + ReqOpt[0].OptionStr = (UINT8*)"tsize"; + OptBuf[0] = '0'; + OptBuf[1] = 0; + ReqOpt[0].ValueStr = OptBuf; + + Status = Mtftp4->GetInfo ( + Mtftp4, + NULL, + (UINT8*)FilePath, + NULL, + 1, + ReqOpt, + &PktLen, + &Packet + ); + + if (EFI_ERROR (Status)) { + goto Error; + } + + Status = Mtftp4->ParseOptions ( + Mtftp4, + PktLen, + Packet, + (UINT32 *) &OptCnt, + &TableOfOptions + ); + if (EFI_ERROR (Status)) { + goto Error; + } + + Option = TableOfOptions; + while (OptCnt != 0) { + if (AsciiStrnCmp ((CHAR8 *)Option->OptionStr, "tsize", 5) == 0) { + *FileSize = AsciiStrDecimalToUintn ((CHAR8 *)Option->ValueStr); + break; + } + OptCnt--; + Option++; + } + FreePool (TableOfOptions); + + if (OptCnt == 0) { + Status = EFI_UNSUPPORTED; + } + +Error : + + return Status; +} + +/** + Worker function that download the data of a file from a TFTP server given + the path of the file and its size. + + @param[in] Mtftp4 MTFTP4 protocol interface + @param[in] FilePath Path of the file, Unicode encoded + @param[in] AsciiFilePath Path of the file, ASCII encoded + @param[in] FileSize Size of the file in number of bytes + @param[out] Data Address where to store the address of the buffer + where the data of the file were downloaded in + case of success. + + @retval EFI_SUCCESS The file was downloaded. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. + @retval Others The downloading of the file from the server failed + (see EFI_MTFTP4_PROTOCOL.ReadFile() status codes). + +**/ +STATIC +EFI_STATUS +DownloadFile ( + IN EFI_MTFTP4_PROTOCOL *Mtftp4, + IN CONST CHAR16 *FilePath, + IN CONST CHAR8 *AsciiFilePath, + IN UINTN FileSize, + OUT VOID **Data + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS PagesAddress; + VOID *Buffer; + DOWNLOAD_CONTEXT *TftpContext; + EFI_MTFTP4_TOKEN Mtftp4Token; + + // Downloaded file can be large. BS.AllocatePages() is more faster + // than AllocatePool() and avoid fragmentation. + // The downloaded file could be an EFI application. Marking the + // allocated page as EfiBootServicesCode would allow to execute a + // potential downloaded EFI application. + Status = gBS->AllocatePages ( + AllocateAnyPages, + EfiBootServicesCode, + EFI_SIZE_TO_PAGES (FileSize), + &PagesAddress + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Buffer = (VOID*)(UINTN)PagesAddress; + TftpContext = AllocatePool (sizeof (DOWNLOAD_CONTEXT)); + if (TftpContext == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + TftpContext->FileSize = FileSize; + TftpContext->DownloadedNbOfBytes = 0; + TftpContext->LastReportedNbOfBytes = 0; + + ZeroMem (&Mtftp4Token, sizeof (EFI_MTFTP4_TOKEN)); + Mtftp4Token.Filename = (UINT8*)AsciiFilePath; + Mtftp4Token.BufferSize = FileSize; + Mtftp4Token.Buffer = Buffer; + Mtftp4Token.CheckPacket = CheckPacket; + Mtftp4Token.Context = (VOID*)TftpContext; + + ShellPrintHiiEx ( + -1, -1, NULL, STRING_TOKEN (STR_TFTP_DOWNLOADING), + gShellTftpHiiHandle, FilePath + ); + + Status = Mtftp4->ReadFile (Mtftp4, &Mtftp4Token); + ShellPrintHiiEx ( + -1, -1, NULL, STRING_TOKEN (STR_GEN_CRLF), + gShellTftpHiiHandle + ); + +Error : + + if (TftpContext == NULL) { + FreePool (TftpContext); + } + + if (EFI_ERROR (Status)) { + gBS->FreePages (PagesAddress, EFI_SIZE_TO_PAGES (FileSize)); + return Status; + } + + *Data = Buffer; + + return EFI_SUCCESS; +} + +/** + Update the progress of a file download + This procedure is called each time a new TFTP packet is received. + + @param[in] This MTFTP4 protocol interface + @param[in] Token Parameters for the download of the file + @param[in] PacketLen Length of the packet + @param[in] Packet Address of the packet + + @retval EFI_SUCCESS All packets are accepted. + +**/ +STATIC +EFI_STATUS +CheckPacket ( + IN EFI_MTFTP4_PROTOCOL *This, + IN EFI_MTFTP4_TOKEN *Token, + IN UINT16 PacketLen, + IN EFI_MTFTP4_PACKET *Packet + ) +{ + DOWNLOAD_CONTEXT *Context; + CHAR16 Progress[TFTP_PROGRESS_MESSAGE_SIZE]; + UINT64 NbOfKb; + UINTN Index; + UINTN LastStep; + UINTN Step; + + if ((NTOHS (Packet->OpCode)) != EFI_MTFTP4_OPCODE_DATA) { + return EFI_SUCCESS; + } + + Context = (DOWNLOAD_CONTEXT*)Token->Context; + if (Context->DownloadedNbOfBytes == 0) { + ShellPrintEx (-1, -1, L"%s 0 Kb", mTftpProgressFrame); + } + + // + // The data in the packet are prepended with two UINT16 : + // . OpCode = EFI_MTFTP4_OPCODE_DATA + // . Block = the number of this block of data + // + Context->DownloadedNbOfBytes += PacketLen - sizeof (Packet->OpCode) + - sizeof (Packet->Data.Block); + NbOfKb = Context->DownloadedNbOfBytes / 1024; + + Progress[0] = L'\0'; + LastStep = (Context->LastReportedNbOfBytes * TFTP_PROGRESS_SLIDER_STEPS) / + Context->FileSize; + Step = (Context->DownloadedNbOfBytes * TFTP_PROGRESS_SLIDER_STEPS) / + Context->FileSize; + if (Step <= LastStep) { + return EFI_SUCCESS; + } + + ShellPrintEx (-1, -1, L"%s", mTftpProgressDelete); + + StrCpy (Progress, mTftpProgressFrame); + for (Index = 1; Index < Step; Index++) { + Progress[Index] = L'='; + } + Progress[Step] = L'>'; + + UnicodeSPrint ( + Progress + (sizeof (mTftpProgressFrame) / sizeof (CHAR16)) - 1, + sizeof (Progress) - sizeof (mTftpProgressFrame), + L" %7d Kb", + NbOfKb + ); + Context->LastReportedNbOfBytes = Context->DownloadedNbOfBytes; + + ShellPrintEx (-1, -1, L"%s", Progress); + + return EFI_SUCCESS; +} diff --git a/ShellPkg/Library/UefiShellTftpCommandLib/UefiShellTftpCommandLib.c b/ShellPkg/Library/UefiShellTftpCommandLib/UefiShellTftpCommandLib.c new file mode 100644 index 0000000000..22c81b8d2a --- /dev/null +++ b/ShellPkg/Library/UefiShellTftpCommandLib/UefiShellTftpCommandLib.c @@ -0,0 +1,97 @@ +/** @file + Main file for NULL named library for 'tftp' Shell command functions. + + Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.
+ Copyright (c) 2015, ARM Ltd. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ +#include "UefiShellTftpCommandLib.h" + +CONST CHAR16 gShellTftpFileName[] = L"ShellCommand"; +EFI_HANDLE gShellTftpHiiHandle = NULL; + +/** + Return the file name of the help text file if not using HII. + + @return The string pointer to the file name. +**/ +CONST CHAR16* +EFIAPI +ShellCommandGetManFileNameTftp ( + VOID + ) +{ + return gShellTftpFileName; +} + +/** + Constructor for the Shell Tftp Command library. + + Install the handlers for Tftp UEFI Shell command. + + @param ImageHandle The image handle of the process. + @param SystemTable The EFI System Table pointer. + + @retval EFI_SUCCESS The Shell command handlers were installed sucessfully. + @retval EFI_UNSUPPORTED The Shell level required was not found. +**/ +EFI_STATUS +EFIAPI +ShellTftpCommandLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + gShellTftpHiiHandle = NULL; + + // + // check our bit of the profiles mask + // + if ((PcdGet8 (PcdShellProfileMask) & BIT3) == 0) { + return EFI_SUCCESS; + } + + gShellTftpHiiHandle = HiiAddPackages ( + &gShellTftpHiiGuid, gImageHandle, + UefiShellTftpCommandLibStrings, NULL + ); + if (gShellTftpHiiHandle == NULL) { + return EFI_DEVICE_ERROR; + } + // + // Install our Shell command handler + // + ShellCommandRegisterCommandName ( + L"tftp", ShellCommandRunTftp, ShellCommandGetManFileNameTftp, 0, + L"tftp", TRUE , gShellTftpHiiHandle, STRING_TOKEN (STR_GET_HELP_TFTP) + ); + + return EFI_SUCCESS; +} + +/** + Destructor for the library. free any resources. + + @param ImageHandle The image handle of the process. + @param SystemTable The EFI System Table pointer. +**/ +EFI_STATUS +EFIAPI +ShellTftpCommandLibDestructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + if (gShellTftpHiiHandle != NULL) { + HiiRemovePackages (gShellTftpHiiHandle); + } + return EFI_SUCCESS; +} diff --git a/ShellPkg/Library/UefiShellTftpCommandLib/UefiShellTftpCommandLib.h b/ShellPkg/Library/UefiShellTftpCommandLib/UefiShellTftpCommandLib.h new file mode 100644 index 0000000000..a73b86c85b --- /dev/null +++ b/ShellPkg/Library/UefiShellTftpCommandLib/UefiShellTftpCommandLib.h @@ -0,0 +1,63 @@ +/** @file + header file for NULL named library for 'tftp' Shell command functions. + + Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.
+ Copyright (c) 2015, ARM Ltd. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _UEFI_SHELL_TFTP_COMMAND_LIB_H_ +#define _UEFI_SHELL_TFTP_COMMAND_LIB_H_ + +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern EFI_HANDLE gShellTftpHiiHandle; + +typedef struct { + UINT64 FileSize; + UINT64 DownloadedNbOfBytes; + UINT64 LastReportedNbOfBytes; +} DOWNLOAD_CONTEXT; + +/** + Function for 'tftp' command. + + @param[in] ImageHandle Handle to the Image (NULL if Internal). + @param[in] SystemTable Pointer to the System Table (NULL if Internal). +**/ +SHELL_STATUS +EFIAPI +ShellCommandRunTftp ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ); + +#endif /* _UEFI_SHELL_TFTP_COMMAND_LIB_H_ */ diff --git a/ShellPkg/Library/UefiShellTftpCommandLib/UefiShellTftpCommandLib.inf b/ShellPkg/Library/UefiShellTftpCommandLib/UefiShellTftpCommandLib.inf new file mode 100644 index 0000000000..43b367d388 --- /dev/null +++ b/ShellPkg/Library/UefiShellTftpCommandLib/UefiShellTftpCommandLib.inf @@ -0,0 +1,61 @@ +## @file +# Provides Shell 'tftp' command functions +# +# Copyright (c) 2010 - 2015, Intel Corporation. All rights reserved.
+# Copyright (c) 2015, ARM Ltd. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# +## + +[Defines] + INF_VERSION = 0x00010006 + BASE_NAME = UefiShellTftpCommandLib + FILE_GUID = D2B61A25-9835-4E5D-906A-15615E1FF668 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + LIBRARY_CLASS = NULL|UEFI_APPLICATION UEFI_DRIVER + CONSTRUCTOR = ShellTftpCommandLibConstructor + DESTRUCTOR = ShellTftpCommandLibDestructor + +[Sources.common] + UefiShellTftpCommandLib.uni + UefiShellTftpCommandLib.c + UefiShellTftpCommandLib.h + Tftp.c + +[Packages] + MdePkg/MdePkg.dec + ShellPkg/ShellPkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + MemoryAllocationLib + BaseLib + BaseMemoryLib + DebugLib + ShellCommandLib + ShellLib + UefiLib + UefiRuntimeServicesTableLib + UefiBootServicesTableLib + PcdLib + HiiLib + FileHandleLib + NetLib + +[Pcd] + gEfiShellPkgTokenSpaceGuid.PcdShellProfileMask ## CONSUMES + +[Protocols] + gEfiManagedNetworkServiceBindingProtocolGuid ## CONSUMES + gEfiMtftp4ServiceBindingProtocolGuid ## CONSUMES + +[Guids] + gShellTftpHiiGuid ## CONSUMES ## HII diff --git a/ShellPkg/Library/UefiShellTftpCommandLib/UefiShellTftpCommandLib.uni b/ShellPkg/Library/UefiShellTftpCommandLib/UefiShellTftpCommandLib.uni new file mode 100644 index 0000000000000000000000000000000000000000..607a3602d2a3aad30619c0844162e7399c0aba9a GIT binary patch literal 8748 zcmc(ldruoj6vgN7O8pKiM~Jiv2HG@gQCo?JNl_`F*o3x8g#cr)!Xvc}lz#ZO=Qr1v zS?{i61F0fpdmpoN&pr1!v;6zde3%cJK40qZ^YEFD=I?&9@HDK3FLZx3T!#1IHuOR_ zoP^Ub(6z6^LRbu6hq>X=H@fyyI1V@AOy2|j&4rzC5RSq-efL6J_t$jiTA#Bx-?@%1 z;#|ysE(r%ZzYWhcax>KRbUl0@R>G_BV-8almZy>ePQ$dIqq)1Y#W>$al!Ro{rYC8a zLUkbdT^(IWRu-0ZpAkvVempx4ePQiI?Cm(RjH@MHx*C^h=ALFeiL;;U9IeoPpb^LM z{78878h16~M0d_39wc>jbf($dCd|@{PoL@DQDhEG%>bbBb_TE|_@a1ajljnu1% zg1Hltf$sDq71TZp$iUmZV}`qzp_6-VB{XGAtdz%+u--&2j`YpEquO>QlNn*d_(E1+ z=V*N>oOz4DD(E}9ZngpQUm8=yG|G!{d8<)}!{;xD41j2m;}vfn$Yx8?_wjSQvM-IV zgb1eVI_}9n^Folku{gdp~;fp`;Qi-HI8PqqXrDUUNiKOKVRmMUzgf)amdowl58+AFiL)nS1FcV_Hwd%%hu_Z$ z6ZWX-I`{GGz8I|Kp7mHY9nD|&<;(cPzPw>ufBUkOdx6b9`kfG53CW41Wl>_1KO>Kv zk?a}Gx}>`;X@03+)1AmBBc8}wUPt=jt**QkZbSP-Pv@;M4(Ym(?`gj3zlS63NTZvN zfOAJoTAtXC@$|Y9>HTq16R8u}*y_kApH@X@Wr1Xg*?V$qd2U6q)rfiSV{yDI?CS!|d(MYnKQ5{V!No$FxeKAGV*jKy~|6Af~wS;4`a`Bs)nER&5e`Vd1pIf|=!&mje-zV!%ET%ql5bfuE6cyX$J=bj38#9JVMz2k` zS&mqXg~#Xw{;gPDU#h0jv7|_ttW%XeturQCsXEq;e=6-#j%q5-e*XyC6}`1DJ^!xo z)|9P^S@!?4<(h1{Cu?r%cR6;%kF{l6en;hCCCaKPMd_!|Z%_V751#i?*YIJ!dL#d& z=Oo)$?j$39NVmu45X<+L{aebT>#~)tTc*h&qrEvRmU6zQe%d}l+4H=rKBf{By^OWX z%UssLB9pDLR`4v?r!Vm;Fy02EHFsS1>e+N(eY&j_%P8GrhI!}CcWPMgYbpEUdtnbK#pIXSiQcrW% z3LOA)no*8d_EDZ6E3Y`4OKWNcOeeG>>8!YmDfW0Z-2(&N#E#hLXj)!W}cVo|uWvnx`{Tp`p&c%>N(Vt=ypX8hvVb>ae<2kcS6@!y3%KaNXZ*qLshwKG+ z{pe>P!NZdtwkPu;AA2#DE%3J_k97uXqCt2q`Dal(>#0A)7D%kiWk1Xrn@$6GB9D`W zAIcT-ZSUD?rZY9pI@3kubLmo!0MB>qt2X6ZbYLmtmNh6}aVAsV9^IXT5%wMFG!vV} zK0DPn{2`wq=}He{JmWviFdO}5T*fc%&5##8-&04udU}3Ixbqd1dO8`0*PvO+fVb=L zTt{T`_RzQCoO|iLL772TiM}riS7?~>uruR2Ad6%6U-57AHP45(vb>h>6F==(FXiWu_Ui)&G2CCh1CHQx2J4z~(r(OT3>*L+k* zY@62!{AuiwvP3WHYdMzpGHB0UH#|^TsA5(Fn{lG0iRuv*T6dAhWvF410%!?p!VFX`|rE_M7c3XTs+U5g{oP*sSGU z1$)CtcCn74I3m5XEWYK!&(YKJMj0njLp1}#=o`IR$?6PqKWI-BiN&`K)7i`-p3FTR z<6yCh2m8kQjS%3$iNV>}V}dE4GyFbYjOG@ZJZa3iJ$us4b`8s%*hsf6GqO@