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