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