Home | History | Annotate | Download | only in UefiShellTftpCommandLib
      1 /** @file
      2   The implementation for the 'tftp' Shell command.
      3 
      4   Copyright (c) 2015, ARM Ltd. All rights reserved.<BR>
      5   Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved. <BR>
      6   (C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>
      7 
      8   This program and the accompanying materials
      9   are licensed and made available under the terms and conditions of the BSD License
     10   which accompanies this distribution.  The full text of the license may be found at
     11   http://opensource.org/licenses/bsd-license.php.
     12 
     13   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     14   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     15 **/
     16 
     17 #include "UefiShellTftpCommandLib.h"
     18 
     19 #define IP4_CONFIG2_INTERFACE_INFO_NAME_LENGTH 32
     20 
     21 /*
     22    Constant strings and definitions related to the message indicating the amount of
     23    progress in the dowloading of a TFTP file.
     24 */
     25 
     26 // Frame for the progression slider
     27 STATIC CONST CHAR16 mTftpProgressFrame[] = L"[                                        ]";
     28 
     29 // Number of steps in the progression slider
     30 #define TFTP_PROGRESS_SLIDER_STEPS  ((sizeof (mTftpProgressFrame) / sizeof (CHAR16)) - 3)
     31 
     32 // Size in number of characters plus one (final zero) of the message to
     33 // indicate the progress of a TFTP download. The format is "[(progress slider:
     34 // 40 characters)] (nb of KBytes downloaded so far: 7 characters) Kb". There
     35 // are thus the number of characters in mTftpProgressFrame[] plus 11 characters
     36 // (2 // spaces, "Kb" and seven characters for the number of KBytes).
     37 #define TFTP_PROGRESS_MESSAGE_SIZE  ((sizeof (mTftpProgressFrame) / sizeof (CHAR16)) + 12)
     38 
     39 // String to delete the TFTP progress message to be able to update it :
     40 // (TFTP_PROGRESS_MESSAGE_SIZE-1) '\b'
     41 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";
     42 
     43 /**
     44   Check and convert the UINT16 option values of the 'tftp' command
     45 
     46   @param[in]  ValueStr  Value as an Unicode encoded string
     47   @param[out] Value     UINT16 value
     48 
     49   @return     TRUE      The value was returned.
     50   @return     FALSE     A parsing error occured.
     51 **/
     52 STATIC
     53 BOOLEAN
     54 StringToUint16 (
     55   IN   CONST CHAR16  *ValueStr,
     56   OUT  UINT16        *Value
     57   );
     58 
     59 /**
     60   Get the name of the NIC.
     61 
     62   @param[in]   ControllerHandle  The network physical device handle.
     63   @param[in]   NicNumber         The network physical device number.
     64   @param[out]  NicName           Address where to store the NIC name.
     65                                  The memory area has to be at least
     66                                  IP4_CONFIG2_INTERFACE_INFO_NAME_LENGTH
     67                                  double byte wide.
     68 
     69   @return  EFI_SUCCESS  The name of the NIC was returned.
     70   @return  Others       The creation of the child for the Managed
     71                         Network Service failed or the opening of
     72                         the Managed Network Protocol failed or
     73                         the operational parameters for the
     74                         Managed Network Protocol could not be
     75                         read.
     76 **/
     77 STATIC
     78 EFI_STATUS
     79 GetNicName (
     80   IN   EFI_HANDLE  ControllerHandle,
     81   IN   UINTN       NicNumber,
     82   OUT  CHAR16      *NicName
     83   );
     84 
     85 /**
     86   Create a child for the service identified by its service binding protocol GUID
     87   and get from the child the interface of the protocol identified by its GUID.
     88 
     89   @param[in]   ControllerHandle            Controller handle.
     90   @param[in]   ServiceBindingProtocolGuid  Service binding protocol GUID of the
     91                                            service to be created.
     92   @param[in]   ProtocolGuid                GUID of the protocol to be open.
     93   @param[out]  ChildHandle                 Address where the handler of the
     94                                            created child is returned. NULL is
     95                                            returned in case of error.
     96   @param[out]  Interface                   Address where a pointer to the
     97                                            protocol interface is returned in
     98                                            case of success.
     99 
    100   @return  EFI_SUCCESS  The child was created and the protocol opened.
    101   @return  Others       Either the creation of the child or the opening
    102                         of the protocol failed.
    103 **/
    104 STATIC
    105 EFI_STATUS
    106 CreateServiceChildAndOpenProtocol (
    107   IN   EFI_HANDLE  ControllerHandle,
    108   IN   EFI_GUID    *ServiceBindingProtocolGuid,
    109   IN   EFI_GUID    *ProtocolGuid,
    110   OUT  EFI_HANDLE  *ChildHandle,
    111   OUT  VOID        **Interface
    112   );
    113 
    114 /**
    115   Close the protocol identified by its GUID on the child handle of the service
    116   identified by its service binding protocol GUID, then destroy the child
    117   handle.
    118 
    119   @param[in]  ControllerHandle            Controller handle.
    120   @param[in]  ServiceBindingProtocolGuid  Service binding protocol GUID of the
    121                                           service to be destroyed.
    122   @param[in]  ProtocolGuid                GUID of the protocol to be closed.
    123   @param[in]  ChildHandle                 Handle of the child to be destroyed.
    124 
    125 **/
    126 STATIC
    127 VOID
    128 CloseProtocolAndDestroyServiceChild (
    129   IN  EFI_HANDLE  ControllerHandle,
    130   IN  EFI_GUID    *ServiceBindingProtocolGuid,
    131   IN  EFI_GUID    *ProtocolGuid,
    132   IN  EFI_HANDLE  ChildHandle
    133   );
    134 
    135 /**
    136   Worker function that gets the size in numbers of bytes of a file from a TFTP
    137   server before to download the file.
    138 
    139   @param[in]   Mtftp4    MTFTP4 protocol interface
    140   @param[in]   FilePath  Path of the file, ASCII encoded
    141   @param[out]  FileSize  Address where to store the file size in number of
    142                          bytes.
    143 
    144   @retval  EFI_SUCCESS      The size of the file was returned.
    145   @retval  EFI_UNSUPPORTED  The server does not support the "tsize" option.
    146   @retval  Others           Error when retrieving the information from the server
    147                             (see EFI_MTFTP4_PROTOCOL.GetInfo() status codes)
    148                             or error when parsing the response of the server.
    149 **/
    150 STATIC
    151 EFI_STATUS
    152 GetFileSize (
    153   IN   EFI_MTFTP4_PROTOCOL  *Mtftp4,
    154   IN   CONST CHAR8          *FilePath,
    155   OUT  UINTN                *FileSize
    156   );
    157 
    158 /**
    159   Worker function that download the data of a file from a TFTP server given
    160   the path of the file and its size.
    161 
    162   @param[in]   Mtftp4         MTFTP4 protocol interface
    163   @param[in]   FilePath       Path of the file, Unicode encoded
    164   @param[in]   AsciiFilePath  Path of the file, ASCII encoded
    165   @param[in]   FileSize       Size of the file in number of bytes
    166   @param[in]   BlockSize      Value of the TFTP blksize option
    167   @param[out]  Data           Address where to store the address of the buffer
    168                               where the data of the file were downloaded in
    169                               case of success.
    170 
    171   @retval  EFI_SUCCESS           The file was downloaded.
    172   @retval  EFI_OUT_OF_RESOURCES  A memory allocation failed.
    173   @retval  Others                The downloading of the file from the server failed
    174                                  (see EFI_MTFTP4_PROTOCOL.ReadFile() status codes).
    175 
    176 **/
    177 STATIC
    178 EFI_STATUS
    179 DownloadFile (
    180   IN   EFI_MTFTP4_PROTOCOL  *Mtftp4,
    181   IN   CONST CHAR16         *FilePath,
    182   IN   CONST CHAR8          *AsciiFilePath,
    183   IN   UINTN                FileSize,
    184   IN   UINT16               BlockSize,
    185   OUT  VOID                 **Data
    186   );
    187 
    188 /**
    189   Update the progress of a file download
    190   This procedure is called each time a new TFTP packet is received.
    191 
    192   @param[in]  This       MTFTP4 protocol interface
    193   @param[in]  Token      Parameters for the download of the file
    194   @param[in]  PacketLen  Length of the packet
    195   @param[in]  Packet     Address of the packet
    196 
    197   @retval  EFI_SUCCESS  All packets are accepted.
    198 
    199 **/
    200 STATIC
    201 EFI_STATUS
    202 EFIAPI
    203 CheckPacket (
    204   IN EFI_MTFTP4_PROTOCOL  *This,
    205   IN EFI_MTFTP4_TOKEN     *Token,
    206   IN UINT16               PacketLen,
    207   IN EFI_MTFTP4_PACKET    *Packet
    208   );
    209 
    210 EFI_MTFTP4_CONFIG_DATA DefaultMtftp4ConfigData = {
    211   TRUE,                             // Use default setting
    212   { { 0, 0, 0, 0 } },               // StationIp         - Not relevant as UseDefaultSetting=TRUE
    213   { { 0, 0, 0, 0 } },               // SubnetMask        - Not relevant as UseDefaultSetting=TRUE
    214   0,                                // LocalPort         - Automatically assigned port number.
    215   { { 0, 0, 0, 0 } },               // GatewayIp         - Not relevant as UseDefaultSetting=TRUE
    216   { { 0, 0, 0, 0 } },               // ServerIp          - Not known yet
    217   69,                               // InitialServerPort - Standard TFTP server port
    218   6,                                // TryCount          - Max number of retransmissions.
    219   4                                 // TimeoutValue      - Retransmission timeout in seconds.
    220 };
    221 
    222 STATIC CONST SHELL_PARAM_ITEM ParamList[] = {
    223   {L"-i", TypeValue},
    224   {L"-l", TypeValue},
    225   {L"-r", TypeValue},
    226   {L"-c", TypeValue},
    227   {L"-t", TypeValue},
    228   {L"-s", TypeValue},
    229   {NULL , TypeMax}
    230   };
    231 
    232 ///
    233 /// The default block size (512) of tftp is defined in the RFC1350.
    234 ///
    235 #define MTFTP_DEFAULT_BLKSIZE      512
    236 ///
    237 /// The valid range of block size option is defined in the RFC2348.
    238 ///
    239 #define MTFTP_MIN_BLKSIZE          8
    240 #define MTFTP_MAX_BLKSIZE          65464
    241 
    242 
    243 /**
    244   Function for 'tftp' command.
    245 
    246   @param[in] ImageHandle  Handle to the Image (NULL if Internal).
    247   @param[in] SystemTable  Pointer to the System Table (NULL if Internal).
    248 
    249   @return  SHELL_SUCCESS            The 'tftp' command completed successfully.
    250   @return  SHELL_ABORTED            The Shell Library initialization failed.
    251   @return  SHELL_INVALID_PARAMETER  At least one of the command's arguments is
    252                                     not valid.
    253   @return  SHELL_OUT_OF_RESOURCES   A memory allocation failed.
    254   @return  SHELL_NOT_FOUND          Network Interface Card not found or server
    255                                     error or file error.
    256 
    257 **/
    258 SHELL_STATUS
    259 EFIAPI
    260 ShellCommandRunTftp (
    261   IN EFI_HANDLE        ImageHandle,
    262   IN EFI_SYSTEM_TABLE  *SystemTable
    263   )
    264 {
    265   SHELL_STATUS            ShellStatus;
    266   EFI_STATUS              Status;
    267   LIST_ENTRY              *CheckPackage;
    268   CHAR16                  *ProblemParam;
    269   UINTN                   ParamCount;
    270   CONST CHAR16            *UserNicName;
    271   BOOLEAN                 NicFound;
    272   CONST CHAR16            *ValueStr;
    273   CONST CHAR16            *RemoteFilePath;
    274   CHAR8                   *AsciiRemoteFilePath;
    275   UINTN                   FilePathSize;
    276   CONST CHAR16            *Walker;
    277   CONST CHAR16            *LocalFilePath;
    278   EFI_MTFTP4_CONFIG_DATA  Mtftp4ConfigData;
    279   EFI_HANDLE              *Handles;
    280   UINTN                   HandleCount;
    281   UINTN                   NicNumber;
    282   CHAR16                  NicName[IP4_CONFIG2_INTERFACE_INFO_NAME_LENGTH];
    283   EFI_HANDLE              ControllerHandle;
    284   EFI_HANDLE              Mtftp4ChildHandle;
    285   EFI_MTFTP4_PROTOCOL     *Mtftp4;
    286   UINTN                   FileSize;
    287   VOID                    *Data;
    288   SHELL_FILE_HANDLE       FileHandle;
    289   UINT16                  BlockSize;
    290 
    291   ShellStatus         = SHELL_INVALID_PARAMETER;
    292   ProblemParam        = NULL;
    293   NicFound            = FALSE;
    294   AsciiRemoteFilePath = NULL;
    295   Handles             = NULL;
    296   FileSize            = 0;
    297   BlockSize           = MTFTP_DEFAULT_BLKSIZE;
    298 
    299   //
    300   // Initialize the Shell library (we must be in non-auto-init...)
    301   //
    302   Status = ShellInitialize ();
    303   if (EFI_ERROR (Status)) {
    304     ASSERT_EFI_ERROR (Status);
    305     return SHELL_ABORTED;
    306   }
    307 
    308   //
    309   // Parse the command line.
    310   //
    311   Status = ShellCommandLineParse (ParamList, &CheckPackage, &ProblemParam, TRUE);
    312   if (EFI_ERROR (Status)) {
    313     if ((Status == EFI_VOLUME_CORRUPTED) &&
    314         (ProblemParam != NULL) ) {
    315       ShellPrintHiiEx (
    316         -1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), gShellTftpHiiHandle,
    317         L"tftp", ProblemParam
    318         );
    319       FreePool (ProblemParam);
    320     } else {
    321       ASSERT (FALSE);
    322     }
    323     goto Error;
    324   }
    325 
    326   //
    327   // Check the number of parameters
    328   //
    329   ParamCount = ShellCommandLineGetCount (CheckPackage);
    330   if (ParamCount > 4) {
    331     ShellPrintHiiEx (
    332       -1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_MANY),
    333       gShellTftpHiiHandle, L"tftp"
    334       );
    335     goto Error;
    336   }
    337   if (ParamCount < 3) {
    338     ShellPrintHiiEx (
    339       -1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_FEW),
    340       gShellTftpHiiHandle, L"tftp"
    341       );
    342     goto Error;
    343   }
    344 
    345   CopyMem (&Mtftp4ConfigData, &DefaultMtftp4ConfigData, sizeof (EFI_MTFTP4_CONFIG_DATA));
    346 
    347   //
    348   // Check the host IPv4 address
    349   //
    350   ValueStr = ShellCommandLineGetRawValue (CheckPackage, 1);
    351   Status = NetLibStrToIp4 (ValueStr, &Mtftp4ConfigData.ServerIp);
    352   if (EFI_ERROR (Status)) {
    353     ShellPrintHiiEx (
    354       -1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV),
    355       gShellTftpHiiHandle, L"tftp", ValueStr
    356     );
    357     goto Error;
    358   }
    359 
    360   RemoteFilePath = ShellCommandLineGetRawValue (CheckPackage, 2);
    361   ASSERT(RemoteFilePath != NULL);
    362   FilePathSize = StrLen (RemoteFilePath) + 1;
    363   AsciiRemoteFilePath = AllocatePool (FilePathSize);
    364   if (AsciiRemoteFilePath == NULL) {
    365     ShellStatus = SHELL_OUT_OF_RESOURCES;
    366     goto Error;
    367   }
    368   UnicodeStrToAsciiStrS (RemoteFilePath, AsciiRemoteFilePath, FilePathSize);
    369 
    370   if (ParamCount == 4) {
    371     LocalFilePath = ShellCommandLineGetRawValue (CheckPackage, 3);
    372   } else {
    373     Walker = RemoteFilePath + StrLen (RemoteFilePath);
    374     while ((--Walker) >= RemoteFilePath) {
    375       if ((*Walker == L'\\') ||
    376           (*Walker == L'/' )    ) {
    377         break;
    378       }
    379     }
    380     LocalFilePath = Walker + 1;
    381   }
    382 
    383   //
    384   // Get the name of the Network Interface Card to be used if any.
    385   //
    386   UserNicName = ShellCommandLineGetValue (CheckPackage, L"-i");
    387 
    388   ValueStr = ShellCommandLineGetValue (CheckPackage, L"-l");
    389   if (ValueStr != NULL) {
    390     if (!StringToUint16 (ValueStr, &Mtftp4ConfigData.LocalPort)) {
    391       goto Error;
    392     }
    393   }
    394 
    395   ValueStr = ShellCommandLineGetValue (CheckPackage, L"-r");
    396   if (ValueStr != NULL) {
    397     if (!StringToUint16 (ValueStr, &Mtftp4ConfigData.InitialServerPort)) {
    398       goto Error;
    399     }
    400   }
    401 
    402   ValueStr = ShellCommandLineGetValue (CheckPackage, L"-c");
    403   if (ValueStr != NULL) {
    404     if (!StringToUint16 (ValueStr, &Mtftp4ConfigData.TryCount)) {
    405       goto Error;
    406     }
    407   }
    408 
    409   ValueStr = ShellCommandLineGetValue (CheckPackage, L"-t");
    410   if (ValueStr != NULL) {
    411     if (!StringToUint16 (ValueStr, &Mtftp4ConfigData.TimeoutValue)) {
    412       goto Error;
    413     }
    414     if (Mtftp4ConfigData.TimeoutValue == 0) {
    415       ShellPrintHiiEx (
    416         -1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV),
    417         gShellTftpHiiHandle, L"tftp", ValueStr
    418       );
    419       goto Error;
    420     }
    421   }
    422 
    423   ValueStr = ShellCommandLineGetValue (CheckPackage, L"-s");
    424   if (ValueStr != NULL) {
    425     if (!StringToUint16 (ValueStr, &BlockSize)) {
    426       goto Error;
    427     }
    428     if (BlockSize < MTFTP_MIN_BLKSIZE || BlockSize > MTFTP_MAX_BLKSIZE) {
    429       ShellPrintHiiEx (
    430         -1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV),
    431         gShellTftpHiiHandle, L"tftp", ValueStr
    432       );
    433       goto Error;
    434     }
    435   }
    436 
    437   //
    438   // Locate all MTFTP4 Service Binding protocols
    439   //
    440   ShellStatus = SHELL_NOT_FOUND;
    441   Status = gBS->LocateHandleBuffer (
    442                  ByProtocol,
    443                  &gEfiManagedNetworkServiceBindingProtocolGuid,
    444                  NULL,
    445                  &HandleCount,
    446                  &Handles
    447                  );
    448   if (EFI_ERROR (Status) || (HandleCount == 0)) {
    449     ShellPrintHiiEx (
    450       -1, -1, NULL, STRING_TOKEN (STR_TFTP_ERR_NO_NIC),
    451       gShellTftpHiiHandle
    452     );
    453     goto Error;
    454   }
    455 
    456   for (NicNumber = 0;
    457        (NicNumber < HandleCount) && (ShellStatus != SHELL_SUCCESS);
    458        NicNumber++) {
    459     ControllerHandle = Handles[NicNumber];
    460     Data = NULL;
    461 
    462     Status = GetNicName (ControllerHandle, NicNumber, NicName);
    463     if (EFI_ERROR (Status)) {
    464       ShellPrintHiiEx (
    465         -1, -1, NULL, STRING_TOKEN (STR_TFTP_ERR_NIC_NAME),
    466         gShellTftpHiiHandle, NicNumber, Status
    467       );
    468       continue;
    469     }
    470 
    471     if (UserNicName != NULL) {
    472       if (StrCmp (NicName, UserNicName) != 0) {
    473         continue;
    474       }
    475       NicFound = TRUE;
    476     }
    477 
    478     Status = CreateServiceChildAndOpenProtocol (
    479                ControllerHandle,
    480                &gEfiMtftp4ServiceBindingProtocolGuid,
    481                &gEfiMtftp4ProtocolGuid,
    482                &Mtftp4ChildHandle,
    483                (VOID**)&Mtftp4
    484                );
    485     if (EFI_ERROR (Status)) {
    486       ShellPrintHiiEx (
    487         -1, -1, NULL, STRING_TOKEN (STR_TFTP_ERR_OPEN_PROTOCOL),
    488         gShellTftpHiiHandle, NicName, Status
    489       );
    490       continue;
    491     }
    492 
    493     Status = Mtftp4->Configure (Mtftp4, &Mtftp4ConfigData);
    494     if (EFI_ERROR (Status)) {
    495       ShellPrintHiiEx (
    496         -1, -1, NULL, STRING_TOKEN (STR_TFTP_ERR_CONFIGURE),
    497         gShellTftpHiiHandle, NicName, Status
    498       );
    499       goto NextHandle;
    500     }
    501 
    502     Status = GetFileSize (Mtftp4, AsciiRemoteFilePath, &FileSize);
    503     if (EFI_ERROR (Status)) {
    504       ShellPrintHiiEx (
    505         -1, -1, NULL, STRING_TOKEN (STR_TFTP_ERR_FILE_SIZE),
    506         gShellTftpHiiHandle, RemoteFilePath, NicName, Status
    507       );
    508       goto NextHandle;
    509     }
    510 
    511     Status = DownloadFile (Mtftp4, RemoteFilePath, AsciiRemoteFilePath, FileSize, BlockSize, &Data);
    512     if (EFI_ERROR (Status)) {
    513       ShellPrintHiiEx (
    514         -1, -1, NULL, STRING_TOKEN (STR_TFTP_ERR_DOWNLOAD),
    515         gShellTftpHiiHandle, RemoteFilePath, NicName, Status
    516       );
    517       goto NextHandle;
    518     }
    519 
    520     if (!EFI_ERROR (ShellFileExists (LocalFilePath))) {
    521       ShellDeleteFileByName (LocalFilePath);
    522     }
    523 
    524     Status = ShellOpenFileByName (
    525                LocalFilePath,
    526                &FileHandle,
    527                EFI_FILE_MODE_CREATE |
    528                EFI_FILE_MODE_WRITE  |
    529                EFI_FILE_MODE_READ,
    530                0
    531                );
    532     if (EFI_ERROR (Status)) {
    533       ShellPrintHiiEx (
    534         -1, -1, NULL, STRING_TOKEN (STR_GEN_FILE_OPEN_FAIL),
    535         gShellTftpHiiHandle, L"tftp", LocalFilePath
    536       );
    537       goto NextHandle;
    538     }
    539 
    540     Status = ShellWriteFile (FileHandle, &FileSize, Data);
    541     if (!EFI_ERROR (Status)) {
    542       ShellStatus = SHELL_SUCCESS;
    543     } else {
    544       ShellPrintHiiEx (
    545         -1, -1, NULL, STRING_TOKEN (STR_TFTP_ERR_WRITE),
    546         gShellTftpHiiHandle, LocalFilePath, Status
    547       );
    548     }
    549     ShellCloseFile (&FileHandle);
    550 
    551     NextHandle:
    552 
    553     if (Data != NULL) {
    554       gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)Data, EFI_SIZE_TO_PAGES (FileSize));
    555     }
    556 
    557     CloseProtocolAndDestroyServiceChild (
    558       ControllerHandle,
    559       &gEfiMtftp4ServiceBindingProtocolGuid,
    560       &gEfiMtftp4ProtocolGuid,
    561       Mtftp4ChildHandle
    562       );
    563   }
    564 
    565   if ((UserNicName != NULL) && (!NicFound)) {
    566     ShellPrintHiiEx (
    567       -1, -1, NULL, STRING_TOKEN (STR_TFTP_ERR_NIC_NOT_FOUND),
    568       gShellTftpHiiHandle, UserNicName
    569     );
    570   }
    571 
    572   Error:
    573 
    574   ShellCommandLineFreeVarList (CheckPackage);
    575   if (AsciiRemoteFilePath != NULL) {
    576     FreePool (AsciiRemoteFilePath);
    577   }
    578   if (Handles != NULL) {
    579     FreePool (Handles);
    580   }
    581 
    582   return ShellStatus;
    583 }
    584 
    585 /**
    586   Check and convert the UINT16 option values of the 'tftp' command
    587 
    588   @param[in]  ValueStr  Value as an Unicode encoded string
    589   @param[out] Value     UINT16 value
    590 
    591   @return     TRUE      The value was returned.
    592   @return     FALSE     A parsing error occured.
    593 **/
    594 STATIC
    595 BOOLEAN
    596 StringToUint16 (
    597   IN   CONST CHAR16  *ValueStr,
    598   OUT  UINT16        *Value
    599   )
    600 {
    601   UINTN  Val;
    602 
    603   Val = ShellStrToUintn (ValueStr);
    604   if (Val > MAX_UINT16) {
    605     ShellPrintHiiEx (
    606       -1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV),
    607       gShellTftpHiiHandle, L"tftp", ValueStr
    608     );
    609     return FALSE;
    610   }
    611 
    612   *Value = (UINT16)Val;
    613   return TRUE;
    614 }
    615 
    616 /**
    617   Get the name of the NIC.
    618 
    619   @param[in]   ControllerHandle  The network physical device handle.
    620   @param[in]   NicNumber         The network physical device number.
    621   @param[out]  NicName           Address where to store the NIC name.
    622                                  The memory area has to be at least
    623                                  IP4_CONFIG2_INTERFACE_INFO_NAME_LENGTH
    624                                  double byte wide.
    625 
    626   @return  EFI_SUCCESS  The name of the NIC was returned.
    627   @return  Others       The creation of the child for the Managed
    628                         Network Service failed or the opening of
    629                         the Managed Network Protocol failed or
    630                         the operational parameters for the
    631                         Managed Network Protocol could not be
    632                         read.
    633 **/
    634 STATIC
    635 EFI_STATUS
    636 GetNicName (
    637   IN   EFI_HANDLE  ControllerHandle,
    638   IN   UINTN       NicNumber,
    639   OUT  CHAR16      *NicName
    640   )
    641 {
    642   EFI_STATUS                    Status;
    643   EFI_HANDLE                    MnpHandle;
    644   EFI_MANAGED_NETWORK_PROTOCOL  *Mnp;
    645   EFI_SIMPLE_NETWORK_MODE       SnpMode;
    646 
    647   Status = CreateServiceChildAndOpenProtocol (
    648              ControllerHandle,
    649              &gEfiManagedNetworkServiceBindingProtocolGuid,
    650              &gEfiManagedNetworkProtocolGuid,
    651              &MnpHandle,
    652              (VOID**)&Mnp
    653              );
    654   if (EFI_ERROR (Status)) {
    655     goto Error;
    656   }
    657 
    658   Status = Mnp->GetModeData (Mnp, NULL, &SnpMode);
    659   if (EFI_ERROR (Status) && (Status != EFI_NOT_STARTED)) {
    660     goto Error;
    661   }
    662 
    663   UnicodeSPrint (
    664     NicName,
    665     IP4_CONFIG2_INTERFACE_INFO_NAME_LENGTH,
    666     SnpMode.IfType == NET_IFTYPE_ETHERNET ?
    667     L"eth%d" :
    668     L"unk%d" ,
    669     NicNumber
    670     );
    671 
    672   Status = EFI_SUCCESS;
    673 
    674 Error:
    675 
    676   if (MnpHandle != NULL) {
    677     CloseProtocolAndDestroyServiceChild (
    678       ControllerHandle,
    679       &gEfiManagedNetworkServiceBindingProtocolGuid,
    680       &gEfiManagedNetworkProtocolGuid,
    681       MnpHandle
    682       );
    683   }
    684 
    685   return Status;
    686 }
    687 
    688 /**
    689   Create a child for the service identified by its service binding protocol GUID
    690   and get from the child the interface of the protocol identified by its GUID.
    691 
    692   @param[in]   ControllerHandle            Controller handle.
    693   @param[in]   ServiceBindingProtocolGuid  Service binding protocol GUID of the
    694                                            service to be created.
    695   @param[in]   ProtocolGuid                GUID of the protocol to be open.
    696   @param[out]  ChildHandle                 Address where the handler of the
    697                                            created child is returned. NULL is
    698                                            returned in case of error.
    699   @param[out]  Interface                   Address where a pointer to the
    700                                            protocol interface is returned in
    701                                            case of success.
    702 
    703   @return  EFI_SUCCESS  The child was created and the protocol opened.
    704   @return  Others       Either the creation of the child or the opening
    705                         of the protocol failed.
    706 **/
    707 STATIC
    708 EFI_STATUS
    709 CreateServiceChildAndOpenProtocol (
    710   IN   EFI_HANDLE  ControllerHandle,
    711   IN   EFI_GUID    *ServiceBindingProtocolGuid,
    712   IN   EFI_GUID    *ProtocolGuid,
    713   OUT  EFI_HANDLE  *ChildHandle,
    714   OUT  VOID        **Interface
    715   )
    716 {
    717   EFI_STATUS  Status;
    718 
    719   *ChildHandle = NULL;
    720   Status = NetLibCreateServiceChild (
    721              ControllerHandle,
    722              gImageHandle,
    723              ServiceBindingProtocolGuid,
    724              ChildHandle
    725              );
    726   if (!EFI_ERROR (Status)) {
    727     Status = gBS->OpenProtocol (
    728                     *ChildHandle,
    729                     ProtocolGuid,
    730                     Interface,
    731                     gImageHandle,
    732                     ControllerHandle,
    733                     EFI_OPEN_PROTOCOL_GET_PROTOCOL
    734                     );
    735     if (EFI_ERROR (Status)) {
    736       NetLibDestroyServiceChild (
    737         ControllerHandle,
    738         gImageHandle,
    739         ServiceBindingProtocolGuid,
    740         *ChildHandle
    741         );
    742       *ChildHandle = NULL;
    743     }
    744   }
    745 
    746   return Status;
    747 }
    748 
    749 /**
    750   Close the protocol identified by its GUID on the child handle of the service
    751   identified by its service binding protocol GUID, then destroy the child
    752   handle.
    753 
    754   @param[in]  ControllerHandle            Controller handle.
    755   @param[in]  ServiceBindingProtocolGuid  Service binding protocol GUID of the
    756                                           service to be destroyed.
    757   @param[in]  ProtocolGuid                GUID of the protocol to be closed.
    758   @param[in]  ChildHandle                 Handle of the child to be destroyed.
    759 
    760 **/
    761 STATIC
    762 VOID
    763 CloseProtocolAndDestroyServiceChild (
    764   IN  EFI_HANDLE  ControllerHandle,
    765   IN  EFI_GUID    *ServiceBindingProtocolGuid,
    766   IN  EFI_GUID    *ProtocolGuid,
    767   IN  EFI_HANDLE  ChildHandle
    768   )
    769 {
    770   gBS->CloseProtocol (
    771          ChildHandle,
    772          ProtocolGuid,
    773          gImageHandle,
    774          ControllerHandle
    775          );
    776 
    777   NetLibDestroyServiceChild (
    778     ControllerHandle,
    779     gImageHandle,
    780     ServiceBindingProtocolGuid,
    781     ChildHandle
    782     );
    783 }
    784 
    785 /**
    786   Worker function that gets the size in numbers of bytes of a file from a TFTP
    787   server before to download the file.
    788 
    789   @param[in]   Mtftp4    MTFTP4 protocol interface
    790   @param[in]   FilePath  Path of the file, ASCII encoded
    791   @param[out]  FileSize  Address where to store the file size in number of
    792                          bytes.
    793 
    794   @retval  EFI_SUCCESS      The size of the file was returned.
    795   @retval  EFI_UNSUPPORTED  The server does not support the "tsize" option.
    796   @retval  Others           Error when retrieving the information from the server
    797                             (see EFI_MTFTP4_PROTOCOL.GetInfo() status codes)
    798                             or error when parsing the response of the server.
    799 **/
    800 STATIC
    801 EFI_STATUS
    802 GetFileSize (
    803   IN   EFI_MTFTP4_PROTOCOL  *Mtftp4,
    804   IN   CONST CHAR8          *FilePath,
    805   OUT  UINTN                *FileSize
    806   )
    807 {
    808   EFI_STATUS         Status;
    809   EFI_MTFTP4_OPTION  ReqOpt[1];
    810   EFI_MTFTP4_PACKET  *Packet;
    811   UINT32             PktLen;
    812   EFI_MTFTP4_OPTION  *TableOfOptions;
    813   EFI_MTFTP4_OPTION  *Option;
    814   UINT32             OptCnt;
    815   UINT8              OptBuf[128];
    816 
    817   ReqOpt[0].OptionStr = (UINT8*)"tsize";
    818   OptBuf[0] = '0';
    819   OptBuf[1] = 0;
    820   ReqOpt[0].ValueStr = OptBuf;
    821 
    822   Status = Mtftp4->GetInfo (
    823              Mtftp4,
    824              NULL,
    825              (UINT8*)FilePath,
    826              NULL,
    827              1,
    828              ReqOpt,
    829              &PktLen,
    830              &Packet
    831              );
    832 
    833   if (EFI_ERROR (Status)) {
    834     goto Error;
    835   }
    836 
    837   Status = Mtftp4->ParseOptions (
    838                      Mtftp4,
    839                      PktLen,
    840                      Packet,
    841                      (UINT32 *) &OptCnt,
    842                      &TableOfOptions
    843                      );
    844   if (EFI_ERROR (Status)) {
    845     goto Error;
    846   }
    847 
    848   Option = TableOfOptions;
    849   while (OptCnt != 0) {
    850     if (AsciiStrnCmp ((CHAR8 *)Option->OptionStr, "tsize", 5) == 0) {
    851       *FileSize = AsciiStrDecimalToUintn ((CHAR8 *)Option->ValueStr);
    852       break;
    853     }
    854     OptCnt--;
    855     Option++;
    856   }
    857   FreePool (TableOfOptions);
    858 
    859   if (OptCnt == 0) {
    860     Status = EFI_UNSUPPORTED;
    861   }
    862 
    863 Error :
    864 
    865   return Status;
    866 }
    867 
    868 /**
    869   Worker function that download the data of a file from a TFTP server given
    870   the path of the file and its size.
    871 
    872   @param[in]   Mtftp4         MTFTP4 protocol interface
    873   @param[in]   FilePath       Path of the file, Unicode encoded
    874   @param[in]   AsciiFilePath  Path of the file, ASCII encoded
    875   @param[in]   FileSize       Size of the file in number of bytes
    876   @param[in]   BlockSize      Value of the TFTP blksize option
    877   @param[out]  Data           Address where to store the address of the buffer
    878                               where the data of the file were downloaded in
    879                               case of success.
    880 
    881   @retval  EFI_SUCCESS           The file was downloaded.
    882   @retval  EFI_OUT_OF_RESOURCES  A memory allocation failed.
    883   @retval  Others                The downloading of the file from the server failed
    884                                  (see EFI_MTFTP4_PROTOCOL.ReadFile() status codes).
    885 
    886 **/
    887 STATIC
    888 EFI_STATUS
    889 DownloadFile (
    890   IN   EFI_MTFTP4_PROTOCOL  *Mtftp4,
    891   IN   CONST CHAR16         *FilePath,
    892   IN   CONST CHAR8          *AsciiFilePath,
    893   IN   UINTN                FileSize,
    894   IN   UINT16               BlockSize,
    895   OUT  VOID                 **Data
    896   )
    897 {
    898   EFI_STATUS            Status;
    899   EFI_PHYSICAL_ADDRESS  PagesAddress;
    900   VOID                  *Buffer;
    901   DOWNLOAD_CONTEXT      *TftpContext;
    902   EFI_MTFTP4_TOKEN      Mtftp4Token;
    903   EFI_MTFTP4_OPTION     ReqOpt;
    904   UINT8                 OptBuf[10];
    905 
    906   // Downloaded file can be large. BS.AllocatePages() is more faster
    907   // than AllocatePool() and avoid fragmentation.
    908   // The downloaded file could be an EFI application. Marking the
    909   // allocated page as EfiBootServicesCode would allow to execute a
    910   // potential downloaded EFI application.
    911   Status = gBS->AllocatePages (
    912                    AllocateAnyPages,
    913                    EfiBootServicesCode,
    914                    EFI_SIZE_TO_PAGES (FileSize),
    915                    &PagesAddress
    916                    );
    917   if (EFI_ERROR (Status)) {
    918     return Status;
    919   }
    920 
    921   Buffer = (VOID*)(UINTN)PagesAddress;
    922   TftpContext = AllocatePool (sizeof (DOWNLOAD_CONTEXT));
    923   if (TftpContext == NULL) {
    924     Status = EFI_OUT_OF_RESOURCES;
    925     goto Error;
    926   }
    927   TftpContext->FileSize = FileSize;
    928   TftpContext->DownloadedNbOfBytes   = 0;
    929   TftpContext->LastReportedNbOfBytes = 0;
    930 
    931   ZeroMem (&Mtftp4Token, sizeof (EFI_MTFTP4_TOKEN));
    932   Mtftp4Token.Filename    = (UINT8*)AsciiFilePath;
    933   Mtftp4Token.BufferSize  = FileSize;
    934   Mtftp4Token.Buffer      = Buffer;
    935   Mtftp4Token.CheckPacket = CheckPacket;
    936   Mtftp4Token.Context     = (VOID*)TftpContext;
    937   if (BlockSize != MTFTP_DEFAULT_BLKSIZE) {
    938     ReqOpt.OptionStr = (UINT8 *) "blksize";
    939     AsciiSPrint ((CHAR8 *)OptBuf, sizeof (OptBuf), "%d", BlockSize);
    940     ReqOpt.ValueStr  = OptBuf;
    941 
    942     Mtftp4Token.OptionCount = 1;
    943     Mtftp4Token.OptionList  = &ReqOpt;
    944   }
    945 
    946   ShellPrintHiiEx (
    947     -1, -1, NULL, STRING_TOKEN (STR_TFTP_DOWNLOADING),
    948     gShellTftpHiiHandle, FilePath
    949     );
    950 
    951   Status = Mtftp4->ReadFile (Mtftp4, &Mtftp4Token);
    952   ShellPrintHiiEx (
    953     -1, -1, NULL, STRING_TOKEN (STR_GEN_CRLF),
    954     gShellTftpHiiHandle
    955     );
    956 
    957 Error :
    958 
    959   if (TftpContext == NULL) {
    960     FreePool (TftpContext);
    961   }
    962 
    963   if (EFI_ERROR (Status)) {
    964     gBS->FreePages (PagesAddress, EFI_SIZE_TO_PAGES (FileSize));
    965     return Status;
    966   }
    967 
    968   *Data = Buffer;
    969 
    970   return EFI_SUCCESS;
    971 }
    972 
    973 /**
    974   Update the progress of a file download
    975   This procedure is called each time a new TFTP packet is received.
    976 
    977   @param[in]  This       MTFTP4 protocol interface
    978   @param[in]  Token      Parameters for the download of the file
    979   @param[in]  PacketLen  Length of the packet
    980   @param[in]  Packet     Address of the packet
    981 
    982   @retval  EFI_SUCCESS  All packets are accepted.
    983 
    984 **/
    985 STATIC
    986 EFI_STATUS
    987 EFIAPI
    988 CheckPacket (
    989   IN EFI_MTFTP4_PROTOCOL  *This,
    990   IN EFI_MTFTP4_TOKEN     *Token,
    991   IN UINT16               PacketLen,
    992   IN EFI_MTFTP4_PACKET    *Packet
    993   )
    994 {
    995   DOWNLOAD_CONTEXT  *Context;
    996   CHAR16            Progress[TFTP_PROGRESS_MESSAGE_SIZE];
    997   UINTN             NbOfKb;
    998   UINTN             Index;
    999   UINTN             LastStep;
   1000   UINTN             Step;
   1001   EFI_STATUS        Status;
   1002 
   1003   if ((NTOHS (Packet->OpCode)) != EFI_MTFTP4_OPCODE_DATA) {
   1004     return EFI_SUCCESS;
   1005   }
   1006 
   1007   Context = (DOWNLOAD_CONTEXT*)Token->Context;
   1008   if (Context->DownloadedNbOfBytes == 0) {
   1009     ShellPrintEx (-1, -1, L"%s       0 Kb", mTftpProgressFrame);
   1010   }
   1011 
   1012   //
   1013   // The data in the packet are prepended with two UINT16 :
   1014   // . OpCode = EFI_MTFTP4_OPCODE_DATA
   1015   // . Block  = the number of this block of data
   1016   //
   1017   Context->DownloadedNbOfBytes += PacketLen - sizeof (Packet->OpCode)
   1018                                             - sizeof (Packet->Data.Block);
   1019   NbOfKb = Context->DownloadedNbOfBytes / 1024;
   1020 
   1021   Progress[0] = L'\0';
   1022   LastStep  = (Context->LastReportedNbOfBytes * TFTP_PROGRESS_SLIDER_STEPS) / Context->FileSize;
   1023   Step      = (Context->DownloadedNbOfBytes * TFTP_PROGRESS_SLIDER_STEPS) / Context->FileSize;
   1024 
   1025   if (Step <= LastStep) {
   1026     return EFI_SUCCESS;
   1027   }
   1028 
   1029   ShellPrintEx (-1, -1, L"%s", mTftpProgressDelete);
   1030 
   1031   Status = StrCpyS (Progress, TFTP_PROGRESS_MESSAGE_SIZE, mTftpProgressFrame);
   1032   if (EFI_ERROR(Status)) {
   1033     return Status;
   1034   }
   1035   for (Index = 1; Index < Step; Index++) {
   1036     Progress[Index] = L'=';
   1037   }
   1038   Progress[Step] = L'>';
   1039 
   1040   UnicodeSPrint (
   1041     Progress + (sizeof (mTftpProgressFrame) / sizeof (CHAR16)) - 1,
   1042     sizeof (Progress) - sizeof (mTftpProgressFrame),
   1043     L" %7d Kb",
   1044     NbOfKb
   1045     );
   1046   Context->LastReportedNbOfBytes = Context->DownloadedNbOfBytes;
   1047 
   1048   ShellPrintEx (-1, -1, L"%s", Progress);
   1049 
   1050   return EFI_SUCCESS;
   1051 }
   1052