3 Copyright (c) 2004 - 2007, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
17 TFTP and MTFTP (multicast TFTP) implementation.
24 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
27 // The following #define is used to create a version that does not wait to
28 // open after a listen. This is just for a special regression test of MTFTP
29 // server to make sure multiple opens are handled correctly. Normally this
30 // next line should be a comment.
31 // #define SpecialNowaitVersion // comment out for normal operation
34 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
37 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
71 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
75 @return EFI_SUCCESS :=
76 @return EFI_TFTP_ERROR :=
83 PXE_BASECODE_DEVICE
*Private
,
88 EFI_IP_ADDRESS
*ServerIpPtr
,
89 EFI_PXE_BASE_CODE_UDP_PORT
*ServerPortPtr
,
90 EFI_IP_ADDRESS
*OurIpPtr
,
91 EFI_PXE_BASE_CODE_UDP_PORT
*OurPortPtr
,
95 EFI_PXE_BASE_CODE_MODE
*PxeBcMode
;
97 EFI_EVENT TimeoutEvent
;
103 Status
= gBS
->CreateEvent (
111 if (EFI_ERROR (Status
)) {
115 Status
= gBS
->SetTimer (
118 Timeout
* 10000000 + 1000000
121 if (EFI_ERROR (Status
)) {
122 gBS
->CloseEvent (TimeoutEvent
);
128 HeaderSize
= Private
->BigBlkNumFlag
? sizeof (struct Tftpv4Ack8
) : sizeof (struct Tftpv4Ack
);
130 #define ERROR_MESSAGE_PTR ((struct Tftpv4Error *) HeaderPtr)
146 if (Status
!= EFI_SUCCESS
|| ERROR_MESSAGE_PTR
->OpCode
!= HTONS (TFTP_ERROR
)) {
147 gBS
->CloseEvent (TimeoutEvent
);
151 // got an error packet
152 // write one byte error code followed by error message
154 PxeBcMode
= Private
->EfiBc
.Mode
;
155 PxeBcMode
->TftpErrorReceived
= TRUE
;
156 PxeBcMode
->TftpError
.ErrorCode
= (UINT8
) NTOHS (ERROR_MESSAGE_PTR
->ErrCode
);
157 HeaderSize
= MIN (*BufferSizePtr
, sizeof PxeBcMode
->TftpError
.ErrorString
);
158 CopyMem (PxeBcMode
->TftpError
.ErrorString
, BufferPtr
, HeaderSize
);
160 gBS
->CloseEvent (TimeoutEvent
);
161 return EFI_TFTP_ERROR
;
164 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
173 PXE_BASECODE_DEVICE
*Private
,
174 EFI_IP_ADDRESS
*ServerIpPtr
,
175 EFI_PXE_BASE_CODE_UDP_PORT
*ServerPortPtr
,
176 EFI_PXE_BASE_CODE_UDP_PORT
*OurPortPtr
179 struct Tftpv4Error
*ErrStr
;
182 ErrStr
= (VOID
*) Private
->TftpErrorBuffer
;
183 Len
= sizeof *ErrStr
;
185 ErrStr
->OpCode
= HTONS (TFTP_ERROR
);
186 ErrStr
->ErrCode
= HTONS (TFTP_ERR_OPTION
);
187 ErrStr
->ErrMsg
[0] = 0;
191 EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT
,
204 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
213 PXE_BASECODE_DEVICE
*Private
,
214 EFI_IP_ADDRESS
*ServerIpPtr
,
215 EFI_PXE_BASE_CODE_UDP_PORT
*ServerPortPtr
,
216 EFI_IP_ADDRESS
*ReplyIpPtr
,
217 EFI_PXE_BASE_CODE_UDP_PORT
*OurPortPtr
,
225 struct Tftpv4Data DataBuffer
;
226 struct Tftpv4Ack
*Ack2Ptr
;
227 struct Tftpv4Ack8
*Ack8Ptr
;
231 Ack2Ptr
= (VOID
*) Private
->TftpAckBuffer
;
232 Ack8Ptr
= (VOID
*) Private
->TftpAckBuffer
;
234 if (Private
->BigBlkNumFlag
) {
235 Len
= sizeof (struct Tftpv4Ack8
);
237 Ack8Ptr
->OpCode
= HTONS (TFTP_ACK8
);
238 Ack8Ptr
->BlockNum
= Swap64 (*BlockNumPtr
);
242 EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT
,
254 if (EFI_ERROR (Status
)) {
258 Len
= sizeof (struct Tftpv4Ack
);
260 Ack2Ptr
->OpCode
= HTONS (TFTP_ACK
);
261 Ack2Ptr
->BlockNum
= HTONS ((UINT16
) *BlockNumPtr
);
265 EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT
,
277 if (EFI_ERROR (Status
)) {
284 // ACK of last packet. This is just a courtesy.
285 // Do not wait for response.
292 Status
= TftpUdpRead (
305 if (EFI_ERROR (Status
) && Status
!= EFI_BUFFER_TOO_SMALL
) {
309 // got a good reply (so far)
310 // check for next data packet
312 if (!Private
->BigBlkNumFlag
&& DataBuffer
.Header
.OpCode
== HTONS (TFTP_DATA
)) {
313 if (Status
== EFI_BUFFER_TOO_SMALL
) {
314 SendError (Private
, ServerIpPtr
, ServerPortPtr
, OurPortPtr
);
317 *BlockNumPtr
= NTOHS (DataBuffer
.Header
.BlockNum
);
321 if (Private
->BigBlkNumFlag
&& DataBuffer
.Header
.OpCode
== HTONS (TFTP_DATA8
)) {
322 if (Status
== EFI_BUFFER_TOO_SMALL
) {
323 SendError (Private
, ServerIpPtr
, ServerPortPtr
, OurPortPtr
);
326 *BlockNumPtr
= Swap64 (*(UINT64
*) &DataBuffer
.Header
.BlockNum
);
330 return EFI_PROTOCOL_ERROR
;
333 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
342 PXE_BASECODE_DEVICE
*Private
,
344 UINT64
*BufferSizePtr
,
347 EFI_IP_ADDRESS
*ServerIpPtr
,
348 EFI_PXE_BASE_CODE_UDP_PORT
*ServerPortPtr
,
349 EFI_IP_ADDRESS
*ReplyIpPtr
,
350 EFI_PXE_BASE_CODE_UDP_PORT
*OurPortPtr
,
353 IN BOOLEAN DontUseBuffer
363 ReplyLen
= PacketSize
;
364 BlockNum
= LastBlock
;
366 DEBUG ((DEBUG_INFO
, "\nLockStepReceive() PacketSize = %d", PacketSize
));
369 BufferSize
= PacketSize
;
371 BufferSize
= *BufferSizePtr
- Offset
;
375 while (ReplyLen
>= 512 && ReplyLen
== PacketSize
) {
376 if (BufferSize
< PacketSize
) {
377 ReplyLen
= (UINTN
) ((BufferSize
> 0) ? BufferSize
: 0);
383 // write an ack packet and get data - retry up to NUM_ACK_RETRIES on timeout
385 Retries
= NUM_ACK_RETRIES
;
390 Status
= SendAckAndGetData (
403 if (!EFI_ERROR (Status
) || Status
== EFI_BUFFER_TOO_SMALL
) {
404 if (BlockNum
== LastBlock
) {
405 DEBUG ((DEBUG_NET
, "\nresend"));
407 // a resend - continue
409 Status
= EFI_TIMEOUT
;
410 } else if (Private
->BigBlkNumFlag
) {
411 if (BlockNum
!= ++LastBlock
) {
412 DEBUG ((DEBUG_NET
, "\nLockStepReceive() Exit #1a"));
414 // not correct blocknum - error
416 return EFI_PROTOCOL_ERROR
;
419 LastBlock
= (LastBlock
+ 1) & 0xFFFF;
420 if (BlockNum
!= LastBlock
) {
421 DEBUG ((DEBUG_NET
, "\nLockStepReceive() Exit #1b"));
422 return EFI_PROTOCOL_ERROR
;
424 // not correct blocknum - error
429 } while (Status
== EFI_TIMEOUT
&& --Retries
);
431 if (EFI_ERROR (Status
)) {
432 if (Status
!= EFI_BUFFER_TOO_SMALL
) {
433 SendError (Private
, ServerIpPtr
, ServerPortPtr
, OurPortPtr
);
440 BufferSize
+= ReplyLen
;
442 BufferPtr
+= ReplyLen
;
443 BufferSize
-= ReplyLen
;
447 // while (ReplyLen == PacketSize);
450 if (BufferSizePtr
!= NULL
) {
451 *BufferSizePtr
= (BufferSize
- PacketSize
);
454 *BufferSizePtr
-= BufferSize
;
457 /* Send ACK of last packet. */
476 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
481 STATIC UINT8 Mode
[] = MODE_BINARY
;
482 STATIC UINT8 BlockSizeOp
[] = OP_BLKSIZE
;
483 STATIC UINT8 TsizeOp
[] = OP_TFRSIZE
;
484 STATIC UINT8 OverwriteOp
[] = OP_OVERWRITE
;
485 STATIC UINT8 BigBlkNumOp
[] = OP_BIGBLKNUM
;
486 STATIC EFI_PXE_BASE_CODE_UDP_PORT TftpRequestPort
= TFTP_OPEN_PORT
;
488 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
492 @return Pointer to value field if option found or NULL if not found.
504 if ((OackSize
-= OpLen
) <= 0) {
509 if (!CompareMem (OackPtr
, OptionPtr
, OpLen
)) {
510 return OackPtr
+ OpLen
;
514 } while (--OackSize
);
519 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
520 #define BKSZOP 1 // block size
521 #define TSIZEOP 2 // transfer size
522 #define OVERWRITEOP 4 // overwrite
523 #define BIGBLKNUMOP 8 // big block numbers
529 PXE_BASECODE_DEVICE
*Private
,
530 EFI_IP_ADDRESS
*ServerIpPtr
,
531 EFI_PXE_BASE_CODE_UDP_PORT
*ServerPortPtr
,
532 EFI_PXE_BASE_CODE_UDP_PORT
*OurPortPtr
,
534 UINTN
*PacketSizePtr
,
540 struct Tftpv4Req ReqStr
;
548 if (*OurPortPtr
== 0) {
549 OpFlags
= EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT
| EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT
;
551 OpFlags
= EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT
;
554 // build the basic request - opcode, filename, mode
557 u
->ReqStr
.OpCode
= HTONS (Req
);
558 TotalLen
= sizeof (Mode
) + sizeof (u
->ReqStr
.OpCode
) + (Len
= 1 + AsciiStrLen ((CHAR8
*) FilenamePtr
));
560 CopyMem (u
->ReqStr
.FileName
, FilenamePtr
, Len
);
561 Ptr
= (UINT8
*) (u
->ReqStr
.FileName
+ Len
);
563 CopyMem (Ptr
, Mode
, sizeof (Mode
));
564 Ptr
+= sizeof (Mode
);
566 if (Options
& BKSZOP
) {
567 CopyMem (Ptr
, BlockSizeOp
, sizeof (BlockSizeOp
));
568 UtoA10 (*PacketSizePtr
, Ptr
+ sizeof (BlockSizeOp
));
570 TotalLen
+= (Len
= 1 + AsciiStrLen ((CHAR8
*) (Ptr
+ sizeof (BlockSizeOp
))) + sizeof (BlockSizeOp
));
575 if (Options
& TSIZEOP
) {
576 CopyMem (Ptr
, TsizeOp
, sizeof (TsizeOp
));
577 CopyMem (Ptr
+ sizeof (TsizeOp
), "0", 2);
578 TotalLen
+= sizeof (TsizeOp
) + 2;
579 Ptr
+= sizeof (TsizeOp
) + 2;
582 if (Options
& OVERWRITEOP
) {
583 CopyMem (Ptr
, OverwriteOp
, sizeof (OverwriteOp
));
584 CopyMem (Ptr
+ sizeof (OverwriteOp
), "1", 2);
585 TotalLen
+= sizeof (OverwriteOp
) + 2;
586 Ptr
+= sizeof (OverwriteOp
) + 2;
589 if (Options
& BIGBLKNUMOP
) {
590 CopyMem (Ptr
, BigBlkNumOp
, sizeof (BigBlkNumOp
));
591 CopyMem (Ptr
+ sizeof (BigBlkNumOp
), "8", 2);
592 TotalLen
+= sizeof (BigBlkNumOp
) + 2;
593 Ptr
+= sizeof (BigBlkNumOp
) + 2;
613 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
624 PXE_BASECODE_DEVICE
*Private
,
626 UINTN
*PacketSizePtr
,
629 EFI_IP_ADDRESS
*ServerIpPtr
,
630 EFI_PXE_BASE_CODE_UDP_PORT
*ServerPortPtr
,
631 EFI_PXE_BASE_CODE_UDP_PORT
*ServerReplyPortPtr
,
632 EFI_PXE_BASE_CODE_UDP_PORT
*OurPortPtr
,
642 SaveReplyLen
= *ReplyLenPtr
;
644 Private
->BigBlkNumFlag
= FALSE
;
650 if (*OurPortPtr
!= 0) {
651 if (++ *OurPortPtr
== 0) {
652 *OurPortPtr
= PXE_RND_PORT_LOW
;
656 // send request from our Ip = StationIp
658 if ((Status
= TftpRwReq (
671 "\nTftpRwReqwResp() Exit #1 %xh (%r)",
679 // read reply to our Ip = StationIp
681 *ReplyLenPtr
= SaveReplyLen
;
683 Status
= TftpUdpRead (
685 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT
,
695 } while (Status
== EFI_TIMEOUT
&& --Retries
);
697 if (!Options
|| Status
!= EFI_TFTP_ERROR
) {
700 "\nTftpRwReqwResp() Exit #2 %xh (%r)",
707 Status
= TftpRwReqwResp (
723 DEBUG ((DEBUG_WARN
, "\nTftpRwReqwResp() Exit #3 %xh (%r)", Status
, Status
));
728 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
732 // read on mcast ip, cport, from sport, for data packet
733 // returns success if gets multicast last packet or all up to last block
734 // if not missing, then finished
744 PXE_BASECODE_DEVICE
*Private
,
745 UINT64
*BufferSizePtr
,
747 EFI_IP_ADDRESS
*ServerIpPtr
,
748 EFI_PXE_BASE_CODE_MTFTP_INFO
*MtftpInfoPtr
,
749 UINT64
*StartBlockPtr
,
752 UINT16 ListenTimeout
,
754 IN BOOLEAN DontUseBuffer
758 struct Tftpv4Ack Header
;
769 LastBlockNum
= *StartBlockPtr
;
770 Timeout
= ListenTimeout
;
773 BufferSize
= *BufferSizePtr
;
774 ReplyLen
= MAX_TFTP_PKT_SIZE
;;
780 if ((SaveReplyLen
= ReplyLen
) > BufferSize
) {
784 /* %%TBD - add big block number support */
787 // get data - loop on resends
790 ReplyLen
= SaveReplyLen
;
792 if ((Status
= TftpUdpRead (
799 &MtftpInfoPtr
->SPort
,
800 &MtftpInfoPtr
->MCastIp
,
801 &MtftpInfoPtr
->CPort
,
807 // make sure a data packet
809 if (Header
.OpCode
!= HTONS (TFTP_DATA
)) {
810 return EFI_PROTOCOL_ERROR
;
812 } while ((BlockNum
= NTOHS (Header
.BlockNum
)) == LastBlockNum
);
815 // make sure still going up
817 if (LastBlockNum
> BlockNum
) {
818 return EFI_PROTOCOL_ERROR
;
821 if (BlockNum
- LastBlockNum
> 0xFFFFFFFF) {
822 return EFI_PROTOCOL_ERROR
;
824 NumMissed
= (UINTN
) (BlockNum
- LastBlockNum
- 1);
827 LastBlockNum
= BlockNum
;
830 // if first time through, some reinitialization
833 *StartBlockPtr
= BlockNum
;
834 PacketSize
= ReplyLen
;
835 Timeout
= TransTimeout
;
837 *NumMissedPtr
= (UINT16
) (*NumMissedPtr
+ NumMissed
);
840 // if missed packets, update start block,
841 // etc. and move packet to proper place in buffer
844 *StartBlockPtr
= BlockNum
;
845 if (!DontUseBuffer
) {
846 Offset
= NumMissed
* PacketSize
;
847 CopyMem (BufferPtr
+ Offset
, BufferPtr
, ReplyLen
);
849 BufferSize
-= Offset
;
853 if (!DontUseBuffer
) {
854 BufferPtr
+= ReplyLen
;
855 BufferSize
-= ReplyLen
;
857 } while (ReplyLen
== PacketSize
&& BlockNum
!= FinalBlock
);
859 *BufferSizePtr
= BufferSize
;
864 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
868 @return // mtftp open session
869 @return // return code EFI_SUCCESS
870 @return // and *CompletionStatusPtr = GOTUNI | GOTMULTI means done
871 @return // and *CompletionStatusPtr = GOTMULTI means got first two multicast packets, use listen for rest
872 @return // and *CompletionStatusPtr = 0 means did not get first two multicast packets, use listen for all
873 @retval GOTUNI returns NO_DATA go will go to TFTP session)
879 PXE_BASECODE_DEVICE
* Private
,
880 UINT64
*BufferSizePtr
,
882 UINTN
*PacketSizePtr
,
883 EFI_IP_ADDRESS
* ServerIpPtr
,
885 EFI_PXE_BASE_CODE_MTFTP_INFO
* MtftpInfoPtr
,
886 UINT8
*CompletionStatusPtr
,
889 IN BOOLEAN DontUseBuffer
893 EFI_IP_ADDRESS OurReplyIp
;
894 struct Tftpv4Ack Header
;
900 Retries
= NUM_MTFTP_OPEN_RETRIES
;
901 BufferPtr2
= BufferPtr
;
902 *PacketSizePtr
= (UINTN
) (MIN (*BufferSizePtr
, MAX_TFTP_PKT_SIZE
));
906 // send a read request
908 *CompletionStatusPtr
= 0;
910 if ((Status
= TftpRwReq (
915 &MtftpInfoPtr
->SPort
,
916 &MtftpInfoPtr
->CPort
,
928 ZeroMem (&OurReplyIp
, Private
->IpLength
);
929 ReplyLen
= *PacketSizePtr
;
931 if ((Status
= TftpUdpRead (
933 EFI_PXE_BASE_CODE_UDP_OPFLAGS_USE_FILTER
,
938 &MtftpInfoPtr
->SPort
,
940 &MtftpInfoPtr
->CPort
,
941 MtftpInfoPtr
->TransmitTimeout
944 // check for first data packet
946 if (Header
.OpCode
!= HTONS (TFTP_DATA
)) {
947 return EFI_PROTOCOL_ERROR
;
952 if (Header
.BlockNum
!= HTONS (1)) {
955 // if we are not the primary client,
956 // we probably got first and now second
957 // multicast but no unicast, so
958 // *CompletionStatusPtr = GOTMULTI - if this is
959 // the second, can just go on to listen
960 // starting with 2 as the last block
963 if (Header
.BlockNum
!= HTONS (2)) {
967 *CompletionStatusPtr
= 0;
976 *PacketSizePtr
= ReplyLen
;
978 // see if a unicast data packet
982 &Private
->EfiBc
.Mode
->StationIp
,
985 *CompletionStatusPtr
|= GOTUNI
;
988 // if already got multicast packet,
991 if (*CompletionStatusPtr
& GOTMULTI
) {
994 } else if (!CompareMem (
996 &MtftpInfoPtr
->MCastIp
,
1000 // otherwise see if a multicast data packet
1002 *CompletionStatusPtr
|= GOTMULTI
;
1005 // got first - bump pointer so that if
1006 // second multi comes along, we're OK
1008 if (!DontUseBuffer
) {
1009 BufferPtr2
= (UINT8
*) BufferPtr
+ ReplyLen
;
1012 // if already got unicast packet,
1015 if (*CompletionStatusPtr
& GOTUNI
) {
1020 // else protocol error
1022 return EFI_PROTOCOL_ERROR
;
1024 } else if (Status
== EFI_TIMEOUT
) {
1026 // bad return code - if timed out, retry
1031 // else just bad - failed MTFTP open
1036 } while (Status
== EFI_TIMEOUT
&& --Retries
);
1038 if (Status
!= EFI_SUCCESS
) {
1045 // got em both - go into receive mode
1046 // routine to read rest of file after a successful open (TFTP or MTFTP)
1047 // sends ACK and gets next data packet until short packet arrives,
1048 // then sends ACK and (hopefully) times out
1050 return LockStepReceive (
1057 &MtftpInfoPtr
->SPort
,
1058 &MtftpInfoPtr
->MCastIp
,
1059 &MtftpInfoPtr
->CPort
,
1061 MtftpInfoPtr
->TransmitTimeout
,
1066 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1075 PXE_BASECODE_DEVICE
*Private
,
1076 UINT64
*BufferSizePtr
,
1078 EFI_IP_ADDRESS
*ServerIpPtr
,
1080 EFI_PXE_BASE_CODE_MTFTP_INFO
*MtftpInfoPtr
,
1081 IN BOOLEAN DontUseBuffer
1084 EFI_PXE_BASE_CODE_IP_FILTER Filter
;
1088 UINT64 LastStartBlock
;
1092 UINT16 TransTimeout
;
1093 UINT16 ListenTimeout
;
1094 UINT8
*BufferPtrLocal
;
1096 TransTimeout
= MtftpInfoPtr
->TransmitTimeout
;
1097 ListenTimeout
= MtftpInfoPtr
->ListenTimeout
;
1102 Filter
.Filters
= EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST
;
1104 Filter
.IpList
[0] = Private
->EfiBc
.Mode
->StationIp
;
1105 Filter
.IpList
[1] = MtftpInfoPtr
->MCastIp
;
1107 if ((Status
= IpFilter (Private
, &Filter
)) != EFI_SUCCESS
) {
1112 StartBlock
= LastStartBlock
;
1113 BufferSize
= *BufferSizePtr
- Offset
;
1115 if (DontUseBuffer
) {
1117 // overwrie the temp buf
1119 BufferPtrLocal
= BufferPtr
;
1121 BufferPtrLocal
= BufferPtr
+ Offset
;
1125 // special !!! do not leave enabled in saved version on Source Safe
1126 // Following code put in in order to create a special version for regression
1127 // test of MTFTP server to make sure it handles mulitple opens correctly.
1128 // This code should NOT be enabled normally.
1130 #ifdef SpecialNowaitVersion
1131 #pragma message ("This is special version for MTFTP regression test")
1132 if (StartBlock
|| !LastBlock
)
1134 if (((Status
= MtftpListen (
1146 )) != EFI_SUCCESS
) && (Status
!= EFI_TIMEOUT
)) {
1153 // if none were received, start block is not reset
1155 if (StartBlock
== LastStartBlock
) {
1159 // timed out with none received - try MTFTP open
1161 if ((Status
= MtftpOpen (
1171 )) != EFI_SUCCESS
) {
1173 // open failure - try TFTP
1178 // return code EFI_SUCCESS
1179 // and *CompletionStatusPtr = GOTUNI | GOTMULTI means done
1180 // and *CompletionStatusPtr = GOTMULTI means got first two multicast packets, use listen for rest
1181 // and *CompletionStatusPtr = 0 means did not get first two multicast packets, use listen for all
1182 // (do not get = GOTUNI - returns NO_DATA go will go to TFTP session)
1184 if (CompStat
== (GOTUNI
| GOTMULTI
)) {
1186 // finished - got it all
1193 // offset is two packet lengths
1197 // last block received
1205 ListenTimeout
= TransTimeout
;
1209 // did we get the last block
1211 if (Status
== EFI_SUCCESS
) {
1213 // yes - set the file size if this was first time
1216 *BufferSizePtr
-= BufferSize
;
1219 // if buffer was too small, finished
1221 if (!DontUseBuffer
) {
1222 return EFI_BUFFER_TOO_SMALL
;
1225 // if we got them all, finished
1227 if (!NumMissed
&& StartBlock
== LastStartBlock
+ 1) {
1231 // did not get them all - set last block
1233 LastBlock
= (UINT16
) (StartBlock
- 1);
1236 // compute listen timeout
1238 ListenTimeout
= (UINT16
) ((NumMissed
> MtftpInfoPtr
->ListenTimeout
) ? 0 : (MtftpInfoPtr
->ListenTimeout
- NumMissed
));
1248 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1257 PXE_BASECODE_DEVICE
*Private
,
1258 UINT64
*BufferSizePtr
,
1259 EFI_IP_ADDRESS
*ServerIpPtr
,
1260 EFI_PXE_BASE_CODE_UDP_PORT SrvPort
,
1262 UINTN
*PacketSizePtr
1265 EFI_PXE_BASE_CODE_UDP_PORT OurPort
;
1266 EFI_PXE_BASE_CODE_UDP_PORT ServerReplyPort
;
1274 struct Tftpv4Oack OAck2Ptr
;
1275 struct Tftpv4Ack Ack2Ptr
;
1276 struct Tftpv4Data Datastr
;
1280 ServerReplyPort
= 0;
1281 ReplyLen
= sizeof (u
.Datastr
.Data
);
1284 // send a write request with the blocksize option -
1285 // sets our IP and port - and receive reply - sets his port
1286 // will retry operation up to 3 times if no response,
1287 // and will retry without options on an error reply
1289 if ((Status
= TftpRwReqwResp (
1291 /* BIGBLKNUMOP | */BKSZOP
| TSIZEOP
,
1303 )) != EFI_SUCCESS
) {
1304 DEBUG ((DEBUG_WARN
, "\nTftpInfo() Exit #1"));
1308 // check for good OACK
1310 if (u
.OAck2Ptr
.OpCode
== HTONS (TFTP_OACK
)) {
1312 // now parse it for options
1317 sizeof (BigBlkNumOp
),
1318 u
.OAck2Ptr
.OpAck
[0].Option
,
1319 ReplyLen
+ sizeof (u
.Ack2Ptr
.BlockNum
)
1323 if (AtoU (Ptr
) == 8) {
1324 Private
->BigBlkNumFlag
= TRUE
;
1326 return EFI_PROTOCOL_ERROR
;
1334 sizeof (BlockSizeOp
),
1335 u
.OAck2Ptr
.OpAck
[0].Option
,
1336 ReplyLen
+= sizeof (u
.Ack2Ptr
.BlockNum
)
1339 *PacketSizePtr
= (Ptr
) ? AtoU (Ptr
) : 512;
1347 u
.OAck2Ptr
.OpAck
[0].Option
,
1352 *BufferSizePtr
= AtoU64 (Ptr
);
1355 // teminate session with error
1357 SendError (Private
, ServerIpPtr
, &ServerReplyPort
, &OurPort
);
1366 // if MTFTP get filesize, return unsupported
1368 if (SrvPort
!= TftpRequestPort
) {
1369 SendError (Private
, ServerIpPtr
, &ServerReplyPort
, &OurPort
);
1370 DEBUG ((DEBUG_WARN
, "\nTftpInfo() Exit #3"));
1371 return EFI_UNSUPPORTED
;
1376 // last block received
1381 // does not support the option - do a download with no buffer
1385 Status
= LockStepReceive (
1393 &Private
->EfiBc
.Mode
->StationIp
,
1400 if (Status
!= EFI_SUCCESS
) {
1401 DEBUG ((DEBUG_WARN
, "\nTftpInfo() LockStepReceive() == %Xh", Status
));
1404 if (Status
!= EFI_BUFFER_TOO_SMALL
) {
1411 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1420 PXE_BASECODE_DEVICE
*Private
,
1421 UINT64
*BufferSizePtr
,
1423 EFI_IP_ADDRESS
*ServerIpPtr
,
1425 UINTN
*PacketSizePtr
,
1426 EFI_PXE_BASE_CODE_UDP_PORT SrvPort
,
1428 IN BOOLEAN DontUseBuffer
1431 EFI_PXE_BASE_CODE_UDP_PORT OurPort
;
1432 EFI_PXE_BASE_CODE_UDP_PORT ServerReplyPort
;
1440 struct Tftpv4Ack Ack2Ptr
;
1441 struct Tftpv4Oack OAck2Ptr
;
1442 struct Tftpv4Data Data
;
1443 struct Tftpv4Ack8 Ack8Ptr
;
1444 struct Tftpv4Data8 Data8
;
1448 ServerReplyPort
= 0;
1449 ReplyLen
= (UINTN
) ((*BufferSizePtr
> 0xFFFF) ? 0xFFFF : *BufferSizePtr
);
1452 // send a read request with the blocksize option - sets our IP and port
1453 // - and receive reply - sets his port will retry operation up to 3
1454 // times if no response, and will retry without options on an error
1457 if ((Status
= TftpRwReqwResp (
1459 /* BIGBLKNUMOP | */BKSZOP
,
1471 )) != EFI_SUCCESS
) {
1472 DEBUG ((DEBUG_WARN
, "\nTftpDownload() Exit #1 %xh (%r)", Status
, Status
));
1478 if (U
.OAck2Ptr
.OpCode
== HTONS (TFTP_OACK
)) {
1482 CopyMem (U
.Data
.Data
, BufferPtr
, ReplyLen
);
1486 sizeof (BigBlkNumOp
),
1487 U
.OAck2Ptr
.OpAck
[0].Option
,
1488 ReplyLen
+ sizeof (U
.Ack2Ptr
.BlockNum
)
1492 if (AtoU (Ptr
) == 8) {
1493 Private
->BigBlkNumFlag
= TRUE
;
1495 return EFI_PROTOCOL_ERROR
;
1499 // now parse it for blocksize option
1503 sizeof (BlockSizeOp
),
1504 U
.OAck2Ptr
.OpAck
[0].Option
,
1505 ReplyLen
+= sizeof (U
.Ack2Ptr
.BlockNum
)
1508 ReplyLen
= (Ptr
!= NULL
) ? AtoU (Ptr
) : 512;
1512 // last block received
1515 } else if (U
.Ack2Ptr
.OpCode
!= HTONS (TFTP_DATA
) || U
.Ack2Ptr
.BlockNum
!= HTONS (1)) {
1519 DEBUG ((DEBUG_WARN
, "\nTftpDownload() Exit #2 %xh (%r)", Status
, Status
));
1521 return EFI_PROTOCOL_ERROR
;
1524 // got good data packet
1528 // last block received
1533 if (PacketSizePtr
!= NULL
) {
1534 *PacketSizePtr
= ReplyLen
;
1537 // routine to read rest of file after a successful open (TFTP or MTFTP)
1538 // sends ACK and gets next data packet until short packet arrives, then sends
1539 // ACK and (hopefully) times out
1540 // if first packet has been read, BufferPtr and BufferSize must reflect fact
1542 Status
= LockStepReceive (
1550 &Private
->EfiBc
.Mode
->StationIp
,
1557 if (Status
!= EFI_SUCCESS
) {
1558 DEBUG ((DEBUG_WARN
, "\nTftpDownload() Exit #3 %xh (%r)", Status
, Status
));
1560 if (Status
== EFI_BUFFER_TOO_SMALL
) {
1570 if (!EFI_ERROR (Status
)) {
1571 Status
= EFI_BUFFER_TOO_SMALL
;
1579 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1588 PXE_BASECODE_DEVICE
*Private
,
1589 UINT64
*BufferSizePtr
,
1591 EFI_IP_ADDRESS
*ServerIpPtr
,
1593 UINTN
*PacketSizePtr
,
1597 struct Tftpv4Ack Header
;
1598 EFI_PXE_BASE_CODE_UDP_PORT OurPort
;
1599 EFI_PXE_BASE_CODE_UDP_PORT ServerReplyPort
;
1602 UINT64 TransferSize
;
1609 struct Tftpv4Oack OAck2Ptr
;
1610 struct Tftpv4Ack Ack2Ptr
;
1611 struct Tftpv4Data Datastr
;
1615 ServerReplyPort
= 0;
1616 TransferSize
= *BufferSizePtr
;
1617 ReplyLen
= sizeof (u
.Datastr
.Data
);
1618 Options
= (UINT16
) ((Overwrite
) ? OVERWRITEOP
| BKSZOP
: BKSZOP
);
1621 // send a write request with the blocksize option - sets our IP and port -
1622 // and receive reply - sets his port
1623 // will retry operation up to 3 times if no response, and will retry without
1624 // options on an error reply
1626 if ((Status
= TftpRwReqwResp (
1640 )) != EFI_SUCCESS
) {
1646 if (u
.OAck2Ptr
.OpCode
== HTONS (TFTP_OACK
)) {
1648 // parse it for blocksize option
1652 sizeof (BlockSizeOp
),
1653 u
.OAck2Ptr
.OpAck
[0].Option
,
1654 ReplyLen
+= sizeof (u
.Ack2Ptr
.BlockNum
)
1656 *PacketSizePtr
= (Ptr
) ? AtoU (Ptr
) : 512;
1661 else if (u
.Ack2Ptr
.OpCode
== HTONS (TFTP_ACK
)) {
1663 // option was not supported
1665 *PacketSizePtr
= 512;
1667 return EFI_PROTOCOL_ERROR
;
1672 Header
.OpCode
= HTONS (TFTP_DATA
);
1674 Header
.BlockNum
= HTONS (1);
1680 Retries
= NUM_ACK_RETRIES
;
1681 HeaderSize
= sizeof (Header
);
1682 TransferLen
= (UINTN
) (MIN (*PacketSizePtr
, TransferSize
));
1685 // write a data packet and get an ack
1691 if ((Status
= UdpWrite (
1693 EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT
,
1703 )) != EFI_SUCCESS
) {
1709 ReplyLen
= sizeof (u
.Datastr
.Data
);
1711 if ((Status
= TftpUdpRead (
1722 )) == EFI_SUCCESS
) {
1724 // check for ACK for this data packet
1726 if (u
.Ack2Ptr
.OpCode
!= HTONS (TFTP_ACK
)) {
1727 return EFI_PROTOCOL_ERROR
;
1730 if (u
.Ack2Ptr
.BlockNum
!= Header
.BlockNum
) {
1732 // not for this packet - continue
1734 Status
= EFI_TIMEOUT
;
1737 } while (Status
== EFI_TIMEOUT
&& --Retries
);
1739 if (Status
!= EFI_SUCCESS
) {
1743 BufferPtr
= (VOID
*) ((UINT8
*) (BufferPtr
) + TransferLen
);
1744 TransferSize
-= TransferLen
;
1746 Header
.BlockNum
= HTONS ((UINT16
) BlockNum
);
1747 } while (TransferLen
== *PacketSizePtr
);
1752 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1756 @return * EFI_INVALID_PARAMETER
1757 @return * EFI_OUT_OF_RESOURCES
1758 @return * EFI_BAD_BUFFER_SIZE
1759 @return * Status is also returned from IpFilter(), TftpInfo(), MtftpDownload(),
1760 @return * TftpDownload() and TftpUpload().
1765 PXE_BASECODE_DEVICE
*Private
,
1766 IN EFI_PXE_BASE_CODE_TFTP_OPCODE Operation
,
1767 UINT64
*BufferSizePtr
,
1769 EFI_IP_ADDRESS
*ServerIpPtr
,
1771 UINTN
*PacketSizePtr
,
1772 IN EFI_PXE_BASE_CODE_MTFTP_INFO
*MtftpInfoPtr
, OPTIONAL
1773 IN BOOLEAN Overwrite
,
1774 IN BOOLEAN DontUseBuffer
1777 EFI_PXE_BASE_CODE_IP_FILTER Filter
;
1778 EFI_STATUS StatCode
;
1780 UINT64 BufferSizeLocal
;
1782 UINT8
*BufferPtrLocal
;
1784 Filter
.Filters
= EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP
;
1786 Filter
.reserved
= 0;
1788 /* No error has occurred, yet. */
1789 Private
->EfiBc
.Mode
->TftpErrorReceived
= FALSE
;
1791 /* We must at least have an MTFTP server IP address and
1792 * a pointer to the buffer size.
1794 if (!ServerIpPtr
|| !BufferSizePtr
) {
1795 DEBUG ((DEBUG_WARN
, "\nPxeBcMtftp() Exit #1"));
1797 return EFI_INVALID_PARAMETER
;
1800 Private
->Function
= EFI_PXE_BASE_CODE_FUNCTION_MTFTP
;
1803 // make sure filter set to unicast at start
1805 if ((StatCode
= IpFilter (Private
, &Filter
)) != EFI_SUCCESS
) {
1808 "\nPxeBcMtftp() Exit IpFilter() == %Xh",
1815 // set unset parms to default values
1817 if (!PacketSizePtr
) {
1818 *(PacketSizePtr
= &PacketSize
) = MAX_TFTP_PKT_SIZE
;
1821 if ((*PacketSizePtr
> *BufferSizePtr
) &&
1822 (Operation
!= EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE
) &&
1823 (Operation
!= EFI_PXE_BASE_CODE_MTFTP_GET_FILE_SIZE
)) {
1824 *PacketSizePtr
= MAX ((UINTN
) *BufferSizePtr
, MIN_TFTP_PKT_SIZE
);
1827 if (*PacketSizePtr
< MIN_TFTP_PKT_SIZE
) {
1828 *PacketSizePtr
= MIN_TFTP_PKT_SIZE
;
1829 return EFI_INVALID_PARAMETER
;
1832 if (*PacketSizePtr
> BUFFER_ALLOCATE_SIZE
) {
1833 *PacketSizePtr
= BUFFER_ALLOCATE_SIZE
;
1836 if (*PacketSizePtr
> MAX_TFTP_PKT_SIZE
) {
1837 *PacketSizePtr
= MAX_TFTP_PKT_SIZE
;
1840 if (Operation
== EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE
) {
1841 StatCode
= TftpInfo (
1850 if (StatCode
!= EFI_SUCCESS
) {
1853 "\nPxeBcMtftp() Exit TftpInfo() == %Xh",
1861 if (Operation
== EFI_PXE_BASE_CODE_MTFTP_GET_FILE_SIZE
) {
1862 if (!MtftpInfoPtr
|| !MtftpInfoPtr
->SPort
) {
1863 DEBUG ((DEBUG_WARN
, "\nPxeBcMtftp() Exit #2"));
1864 return EFI_INVALID_PARAMETER
;
1866 StatCode
= TftpInfo (
1870 MtftpInfoPtr
->SPort
,
1877 if (StatCode
!= EFI_SUCCESS
) {
1880 "\nPxeBcMtftp() Exit TftpInfo() == %Xh",
1889 if (!BufferPtr
&& !DontUseBuffer
) {
1891 // if dontusebuffer is false and no buffer???
1893 DEBUG ((DEBUG_WARN
, "\nPxeBcMtftp() Exit #3"));
1895 // DontUseBuffer can be true only for read_file operation
1897 return EFI_INVALID_PARAMETER
;
1900 if (DontUseBuffer
) {
1901 Status
= gBS
->AllocatePool (
1902 EfiBootServicesData
,
1903 BUFFER_ALLOCATE_SIZE
,
1904 (VOID
**) &BufferPtrLocal
1907 if (EFI_ERROR (Status
) || BufferPtrLocal
== NULL
) {
1908 DEBUG ((DEBUG_NET
, "\nPxeBcMtftp() Exit #4"));
1909 return EFI_OUT_OF_RESOURCES
;
1912 BufferSizeLocal
= BUFFER_ALLOCATE_SIZE
;
1914 if (!*BufferSizePtr
&& Operation
!= EFI_PXE_BASE_CODE_TFTP_WRITE_FILE
) {
1915 DEBUG ((DEBUG_WARN
, "\nPxeBcMtftp() Exit #5"));
1916 return EFI_BAD_BUFFER_SIZE
;
1919 BufferPtrLocal
= BufferPtr
;
1920 BufferSizeLocal
= *BufferSizePtr
;
1923 switch (Operation
) {
1924 case EFI_PXE_BASE_CODE_MTFTP_READ_FILE
:
1925 if (FilenamePtr
== NULL
||
1926 MtftpInfoPtr
== NULL
||
1927 MtftpInfoPtr
->MCastIp
.Addr
[0] == 0 ||
1928 MtftpInfoPtr
->SPort
== 0 ||
1929 MtftpInfoPtr
->CPort
== 0 ||
1930 MtftpInfoPtr
->ListenTimeout
== 0 ||
1931 MtftpInfoPtr
->TransmitTimeout
== 0
1933 StatCode
= EFI_INVALID_PARAMETER
;
1937 // try MTFTP - if fails, drop into TFTP read
1939 if ((StatCode
= MtftpDownload (
1947 )) == EFI_SUCCESS
|| StatCode
== EFI_BUFFER_TOO_SMALL
) {
1948 if (BufferSizePtr
/* %% !DontUseBuffer */ ) {
1949 *BufferSizePtr
= BufferSizeLocal
;
1955 // go back to unicast
1957 if ((StatCode
= IpFilter (Private
, &Filter
)) != EFI_SUCCESS
) {
1962 case EFI_PXE_BASE_CODE_TFTP_READ_FILE
:
1963 if (FilenamePtr
== NULL
) {
1964 StatCode
= EFI_INVALID_PARAMETER
;
1968 StatCode
= TftpDownload (
1980 if (StatCode
== EFI_SUCCESS
|| StatCode
== EFI_BUFFER_TOO_SMALL
) {
1981 if (BufferSizePtr
/* !DontUseBuffer */ ) {
1982 *BufferSizePtr
= BufferSizeLocal
;
1988 case EFI_PXE_BASE_CODE_TFTP_WRITE_FILE
:
1989 if (FilenamePtr
== NULL
|| DontUseBuffer
) {
1991 // not a valid option
1993 StatCode
= EFI_INVALID_PARAMETER
;
1997 StatCode
= TftpUpload (
2007 if (StatCode
!= EFI_SUCCESS
) {
2010 "\nPxeBcMtftp() Exit #6 %xh (%r)",
2018 case EFI_PXE_BASE_CODE_TFTP_READ_DIRECTORY
:
2019 if (FilenamePtr
== NULL
|| DontUseBuffer
) {
2021 // not a valid option
2023 StatCode
= EFI_INVALID_PARAMETER
;
2027 StatCode
= TftpDownload (
2039 if (StatCode
!= EFI_SUCCESS
) {
2042 "\nPxeBcMtftp() Exit #7 %xh (%r)",
2050 case EFI_PXE_BASE_CODE_MTFTP_READ_DIRECTORY
:
2051 if (DontUseBuffer
) {
2052 StatCode
= EFI_INVALID_PARAMETER
;
2056 if (MtftpInfoPtr
== NULL
|| !MtftpInfoPtr
->SPort
) {
2059 "\nPxeBcMtftp() Exit #9 %xh (%r)",
2060 EFI_INVALID_PARAMETER
,
2061 EFI_INVALID_PARAMETER
)
2064 return EFI_INVALID_PARAMETER
;
2067 StatCode
= TftpDownload (
2074 MtftpInfoPtr
->SPort
,
2082 StatCode
= EFI_INVALID_PARAMETER
;
2085 if (DontUseBuffer
) {
2086 gBS
->FreePool (BufferPtrLocal
);
2089 if (StatCode
!= EFI_SUCCESS
) {
2092 "\nPxeBcMtftp() Exit #8 %xh (%r)",
2103 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2107 @return * EFI_INVALID_PARAMETER
2108 @return * Status is also returned from PxeBcMtftp();
2114 IN EFI_PXE_BASE_CODE_PROTOCOL
* This
,
2115 IN EFI_PXE_BASE_CODE_TFTP_OPCODE Operation
,
2116 IN OUT VOID
*BufferPtr
,
2117 IN BOOLEAN Overwrite
,
2118 IN OUT UINT64
*BufferSizePtr
,
2119 IN UINTN
*BlockSizePtr OPTIONAL
,
2120 IN EFI_IP_ADDRESS
* ServerIpPtr
,
2121 IN UINT8
*FilenamePtr
,
2122 IN EFI_PXE_BASE_CODE_MTFTP_INFO
* MtftpInfoPtr OPTIONAL
,
2123 IN BOOLEAN DontUseBuffer
2126 EFI_PXE_BASE_CODE_IP_FILTER Filter
;
2127 EFI_STATUS StatCode
;
2128 PXE_BASECODE_DEVICE
*Private
;
2131 // Lock the instance data and make sure started
2133 StatCode
= EFI_SUCCESS
;
2136 DEBUG ((DEBUG_ERROR
, "BC *This pointer == NULL"));
2137 return EFI_INVALID_PARAMETER
;
2140 Private
= CR (This
, PXE_BASECODE_DEVICE
, EfiBc
, PXE_BASECODE_DEVICE_SIGNATURE
);
2142 if (Private
== NULL
) {
2143 DEBUG ((DEBUG_ERROR
, "PXE_BASECODE_DEVICE poiner == NULL"));
2144 return EFI_INVALID_PARAMETER
;
2147 if (!IS_INADDR_UNICAST (ServerIpPtr
)) {
2149 // The station IP is not a unicast address.
2151 return EFI_INVALID_PARAMETER
;
2154 EfiAcquireLock (&Private
->Lock
);
2156 if (This
->Mode
== NULL
|| !This
->Mode
->Started
) {
2157 DEBUG ((DEBUG_ERROR
, "BC was not started."));
2158 EfiReleaseLock (&Private
->Lock
);
2159 return EFI_NOT_STARTED
;
2164 Filter
.Filters
= EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP
;
2166 Filter
.reserved
= 0;
2168 DEBUG ((DEBUG_WARN
, "\nBcMtftp() Op=%d Buf=%Xh", Operation
, BufferPtr
));
2170 StatCode
= PxeBcMtftp (
2184 // restore to unicast
2186 IpFilter (Private
, &Filter
);
2189 // Unlock the instance data
2191 EfiReleaseLock (&Private
->Lock
);
2195 /* eof - PxeBcMtftp.c */