• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
ShellCommandRunTftp(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)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
StringToUint16(IN CONST CHAR16 * ValueStr,OUT UINT16 * Value)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
GetNicName(IN EFI_HANDLE ControllerHandle,IN UINTN NicNumber,OUT CHAR16 * NicName)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
CreateServiceChildAndOpenProtocol(IN EFI_HANDLE ControllerHandle,IN EFI_GUID * ServiceBindingProtocolGuid,IN EFI_GUID * ProtocolGuid,OUT EFI_HANDLE * ChildHandle,OUT VOID ** Interface)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
CloseProtocolAndDestroyServiceChild(IN EFI_HANDLE ControllerHandle,IN EFI_GUID * ServiceBindingProtocolGuid,IN EFI_GUID * ProtocolGuid,IN EFI_HANDLE ChildHandle)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
GetFileSize(IN EFI_MTFTP4_PROTOCOL * Mtftp4,IN CONST CHAR8 * FilePath,OUT UINTN * FileSize)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
DownloadFile(IN EFI_MTFTP4_PROTOCOL * Mtftp4,IN CONST CHAR16 * FilePath,IN CONST CHAR8 * AsciiFilePath,IN UINTN FileSize,IN UINT16 BlockSize,OUT VOID ** Data)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
CheckPacket(IN EFI_MTFTP4_PROTOCOL * This,IN EFI_MTFTP4_TOKEN * Token,IN UINT16 PacketLen,IN EFI_MTFTP4_PACKET * Packet)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