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