3cd090b4d8279fbd6c7b0169ea2ccd3a46f4e530
[mirror_edk2.git] / MdeModulePkg / Universal / Network / PxeBcDxe / Pxe_bc_mtftp.c
1 /** @file\r
2 \r
3 Copyright (c) 2004 - 2007, Intel Corporation\r
4 All rights reserved. This program and the accompanying materials\r
5 are licensed and made available under the terms and conditions of the BSD License\r
6 which accompanies this distribution.  The full text of the license may be found at\r
7 http://opensource.org/licenses/bsd-license.php\r
8 \r
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
11 \r
12 Module Name:\r
13 \r
14     pxe_bc_mtftp.c\r
15 \r
16 Abstract:\r
17   TFTP and MTFTP (multicast TFTP) implementation.\r
18 \r
19 Revision History\r
20 \r
21 \r
22 **/\r
23 \r
24 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
25 \r
26 //\r
27 // The following #define is used to create a version that does not wait to\r
28 // open after a listen.  This is just for a special regression test of MTFTP\r
29 // server to make sure multiple opens are handled correctly.  Normally this\r
30 // next line should be a comment.\r
31 // #define SpecialNowaitVersion    // comment out for normal operation\r
32 //\r
33 \r
34 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
35 #include "Bc.h"\r
36 \r
37 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
38 UINT64\r
39 Swap64 (\r
40   UINT64 n\r
41   )\r
42 {\r
43   union {\r
44     UINT64  n;\r
45     UINT8   b[8];\r
46   } u;\r
47 \r
48   UINT8 t;\r
49 \r
50   u.n     = n;\r
51 \r
52   t       = u.b[0];\r
53   u.b[0]  = u.b[7];\r
54   u.b[7]  = t;\r
55 \r
56   t       = u.b[1];\r
57   u.b[1]  = u.b[6];\r
58   u.b[6]  = t;\r
59 \r
60   t       = u.b[2];\r
61   u.b[2]  = u.b[5];\r
62   u.b[5]  = t;\r
63 \r
64   t       = u.b[3];\r
65   u.b[3]  = u.b[4];\r
66   u.b[4]  = t;\r
67 \r
68   return u.n;\r
69 }\r
70 \r
71 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
72 \r
73 /**\r
74 \r
75   @return EFI_SUCCESS :=\r
76   @return EFI_TFTP_ERROR :=\r
77   @return other :=\r
78 \r
79 **/\r
80 STATIC\r
81 EFI_STATUS\r
82 TftpUdpRead (\r
83   PXE_BASECODE_DEVICE         *Private,\r
84   UINT16                      Operation,\r
85   VOID                        *HeaderPtr,\r
86   UINTN                       *BufferSizePtr,\r
87   VOID                        *BufferPtr,\r
88   EFI_IP_ADDRESS              *ServerIpPtr,\r
89   EFI_PXE_BASE_CODE_UDP_PORT  *ServerPortPtr,\r
90   EFI_IP_ADDRESS              *OurIpPtr,\r
91   EFI_PXE_BASE_CODE_UDP_PORT  *OurPortPtr,\r
92   UINT16                      Timeout\r
93   )\r
94 {\r
95   EFI_PXE_BASE_CODE_MODE  *PxeBcMode;\r
96   EFI_STATUS              Status;\r
97   EFI_EVENT               TimeoutEvent;\r
98   UINTN                   HeaderSize;\r
99 \r
100   //\r
101   //\r
102   //\r
103   Status = gBS->CreateEvent (\r
104                   EVT_TIMER,\r
105                   TPL_CALLBACK,\r
106                   NULL,\r
107                   NULL,\r
108                   &TimeoutEvent\r
109                   );\r
110 \r
111   if (EFI_ERROR (Status)) {\r
112     return Status;\r
113   }\r
114 \r
115   Status = gBS->SetTimer (\r
116                   TimeoutEvent,\r
117                   TimerRelative,\r
118                   Timeout * 10000000 + 1000000\r
119                   );\r
120 \r
121   if (EFI_ERROR (Status)) {\r
122     gBS->CloseEvent (TimeoutEvent);\r
123     return Status;\r
124   }\r
125   //\r
126   //\r
127   //\r
128   HeaderSize = Private->BigBlkNumFlag ? sizeof (struct Tftpv4Ack8) : sizeof (struct Tftpv4Ack);\r
129 \r
130 #define ERROR_MESSAGE_PTR ((struct Tftpv4Error *) HeaderPtr)\r
131 \r
132   Status = UdpRead (\r
133             Private,\r
134             Operation,\r
135             OurIpPtr,\r
136             OurPortPtr,\r
137             ServerIpPtr,\r
138             ServerPortPtr,\r
139             &HeaderSize,\r
140             HeaderPtr,\r
141             BufferSizePtr,\r
142             BufferPtr,\r
143             TimeoutEvent\r
144             );\r
145 \r
146   if (Status != EFI_SUCCESS || ERROR_MESSAGE_PTR->OpCode != HTONS (TFTP_ERROR)) {\r
147     gBS->CloseEvent (TimeoutEvent);\r
148     return Status;\r
149   }\r
150   //\r
151   // got an error packet\r
152   // write one byte error code followed by error message\r
153   //\r
154   PxeBcMode                       = Private->EfiBc.Mode;\r
155   PxeBcMode->TftpErrorReceived    = TRUE;\r
156   PxeBcMode->TftpError.ErrorCode  = (UINT8) NTOHS (ERROR_MESSAGE_PTR->ErrCode);\r
157   HeaderSize                      = MIN (*BufferSizePtr, sizeof PxeBcMode->TftpError.ErrorString);\r
158   CopyMem (PxeBcMode->TftpError.ErrorString, BufferPtr, HeaderSize);\r
159 \r
160   gBS->CloseEvent (TimeoutEvent);\r
161   return EFI_TFTP_ERROR;\r
162 }\r
163 \r
164 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
165 \r
166 /**\r
167 \r
168 \r
169 **/\r
170 STATIC\r
171 VOID\r
172 SendError (\r
173   PXE_BASECODE_DEVICE         *Private,\r
174   EFI_IP_ADDRESS              *ServerIpPtr,\r
175   EFI_PXE_BASE_CODE_UDP_PORT  *ServerPortPtr,\r
176   EFI_PXE_BASE_CODE_UDP_PORT  *OurPortPtr\r
177   )\r
178 {\r
179   struct Tftpv4Error  *ErrStr;\r
180   UINTN               Len;\r
181 \r
182   ErrStr            = (VOID *) Private->TftpErrorBuffer;\r
183   Len               = sizeof *ErrStr;\r
184 \r
185   ErrStr->OpCode    = HTONS (TFTP_ERROR);\r
186   ErrStr->ErrCode   = HTONS (TFTP_ERR_OPTION);\r
187   ErrStr->ErrMsg[0] = 0;\r
188 \r
189   UdpWrite (\r
190     Private,\r
191     EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT,\r
192     ServerIpPtr,\r
193     ServerPortPtr,\r
194     0,\r
195     0,\r
196     OurPortPtr,\r
197     0,\r
198     0,\r
199     &Len,\r
200     ErrStr\r
201     );\r
202 }\r
203 \r
204 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
205 \r
206 /**\r
207 \r
208 \r
209 **/\r
210 STATIC\r
211 EFI_STATUS\r
212 SendAckAndGetData (\r
213   PXE_BASECODE_DEVICE         *Private,\r
214   EFI_IP_ADDRESS              *ServerIpPtr,\r
215   EFI_PXE_BASE_CODE_UDP_PORT  *ServerPortPtr,\r
216   EFI_IP_ADDRESS              *ReplyIpPtr,\r
217   EFI_PXE_BASE_CODE_UDP_PORT  *OurPortPtr,\r
218   UINT16                      Timeout,\r
219   UINTN                       *ReplyLenPtr,\r
220   UINT8                       *PxeBcMode,\r
221   UINT64                      *BlockNumPtr,\r
222   BOOLEAN                     AckOnly\r
223   )\r
224 {\r
225   struct Tftpv4Data DataBuffer;\r
226   struct Tftpv4Ack  *Ack2Ptr;\r
227   struct Tftpv4Ack8 *Ack8Ptr;\r
228   EFI_STATUS        Status;\r
229   UINTN             Len;\r
230 \r
231   Ack2Ptr = (VOID *) Private->TftpAckBuffer;\r
232   Ack8Ptr = (VOID *) Private->TftpAckBuffer;\r
233 \r
234   if (Private->BigBlkNumFlag) {\r
235     Len               = sizeof (struct Tftpv4Ack8);\r
236 \r
237     Ack8Ptr->OpCode   = HTONS (TFTP_ACK8);\r
238     Ack8Ptr->BlockNum = Swap64 (*BlockNumPtr);\r
239 \r
240     Status = UdpWrite (\r
241               Private,\r
242               EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT,\r
243               ServerIpPtr,\r
244               ServerPortPtr,\r
245               0,\r
246               0,\r
247               OurPortPtr,\r
248               0,\r
249               0,\r
250               &Len,\r
251               Ack8Ptr\r
252               );\r
253 \r
254     if (EFI_ERROR (Status)) {\r
255       return Status;\r
256     }\r
257   } else {\r
258     Len               = sizeof (struct Tftpv4Ack);\r
259 \r
260     Ack2Ptr->OpCode   = HTONS (TFTP_ACK);\r
261     Ack2Ptr->BlockNum = HTONS ((UINT16) *BlockNumPtr);\r
262 \r
263     Status = UdpWrite (\r
264               Private,\r
265               EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT,\r
266               ServerIpPtr,\r
267               ServerPortPtr,\r
268               0,\r
269               0,\r
270               OurPortPtr,\r
271               0,\r
272               0,\r
273               &Len,\r
274               Ack2Ptr\r
275               );\r
276 \r
277     if (EFI_ERROR (Status)) {\r
278       return Status;\r
279     }\r
280   }\r
281 \r
282   if (AckOnly) {\r
283     //\r
284     // ACK of last packet.  This is just a courtesy.\r
285     // Do not wait for response.\r
286     //\r
287     return EFI_SUCCESS;\r
288   }\r
289   //\r
290   // read reply\r
291   //\r
292   Status = TftpUdpRead (\r
293             Private,\r
294             0,\r
295             &DataBuffer,\r
296             ReplyLenPtr,\r
297             PxeBcMode,\r
298             ServerIpPtr,\r
299             ServerPortPtr,\r
300             ReplyIpPtr,\r
301             OurPortPtr,\r
302             Timeout\r
303             );\r
304 \r
305   if (EFI_ERROR (Status) && Status != EFI_BUFFER_TOO_SMALL) {\r
306     return Status;\r
307   }\r
308   //\r
309   // got a good reply (so far)\r
310   // check for next data packet\r
311   //\r
312   if (!Private->BigBlkNumFlag && DataBuffer.Header.OpCode == HTONS (TFTP_DATA)) {\r
313     if (Status == EFI_BUFFER_TOO_SMALL) {\r
314       SendError (Private, ServerIpPtr, ServerPortPtr, OurPortPtr);\r
315     }\r
316 \r
317     *BlockNumPtr = NTOHS (DataBuffer.Header.BlockNum);\r
318     return Status;\r
319   }\r
320 \r
321   if (Private->BigBlkNumFlag && DataBuffer.Header.OpCode == HTONS (TFTP_DATA8)) {\r
322     if (Status == EFI_BUFFER_TOO_SMALL) {\r
323       SendError (Private, ServerIpPtr, ServerPortPtr, OurPortPtr);\r
324     }\r
325 \r
326     *BlockNumPtr = Swap64 (*(UINT64 *) &DataBuffer.Header.BlockNum);\r
327     return Status;\r
328   }\r
329 \r
330   return EFI_PROTOCOL_ERROR;\r
331 }\r
332 \r
333 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
334 \r
335 /**\r
336 \r
337 \r
338 **/\r
339 STATIC\r
340 EFI_STATUS\r
341 LockStepReceive (\r
342   PXE_BASECODE_DEVICE         *Private,\r
343   UINTN                       PacketSize,\r
344   UINT64                      *BufferSizePtr,\r
345   UINT64                      Offset,\r
346   UINT8                       *BufferPtr,\r
347   EFI_IP_ADDRESS              *ServerIpPtr,\r
348   EFI_PXE_BASE_CODE_UDP_PORT  *ServerPortPtr,\r
349   EFI_IP_ADDRESS              *ReplyIpPtr,\r
350   EFI_PXE_BASE_CODE_UDP_PORT  *OurPortPtr,\r
351   UINT64                      LastBlock,\r
352   UINT16                      Timeout,\r
353   IN BOOLEAN                  DontUseBuffer\r
354   )\r
355 {\r
356   EFI_STATUS  Status;\r
357   UINT64      BlockNum;\r
358   UINT64      BufferSize;\r
359   UINTN       Retries;\r
360   UINTN       SaveLen;\r
361   UINTN       ReplyLen;\r
362 \r
363   ReplyLen  = PacketSize;\r
364   BlockNum  = LastBlock;\r
365 \r
366   DEBUG ((DEBUG_INFO, "\nLockStepReceive()  PacketSize = %d", PacketSize));\r
367 \r
368   if (DontUseBuffer) {\r
369     BufferSize = PacketSize;\r
370   } else {\r
371     BufferSize = *BufferSizePtr - Offset;\r
372     BufferPtr += Offset;\r
373   }\r
374 \r
375   while (ReplyLen >= 512 && ReplyLen == PacketSize) {\r
376     if (BufferSize < PacketSize) {\r
377       ReplyLen = (UINTN) ((BufferSize > 0) ? BufferSize : 0);\r
378     }\r
379 \r
380     SaveLen = ReplyLen;\r
381 \r
382     //\r
383     // write an ack packet and get data - retry up to NUM_ACK_RETRIES on timeout\r
384     //\r
385     Retries = NUM_ACK_RETRIES;\r
386 \r
387     do {\r
388       ReplyLen = SaveLen;\r
389 \r
390       Status = SendAckAndGetData (\r
391                 Private,\r
392                 ServerIpPtr,\r
393                 ServerPortPtr,\r
394                 ReplyIpPtr,\r
395                 OurPortPtr,\r
396                 Timeout,\r
397                 (UINTN *) &ReplyLen,\r
398                 BufferPtr,\r
399                 &BlockNum,\r
400                 FALSE\r
401                 );\r
402 \r
403       if (!EFI_ERROR (Status) || Status == EFI_BUFFER_TOO_SMALL) {\r
404         if (BlockNum == LastBlock) {\r
405           DEBUG ((DEBUG_NET, "\nresend"));\r
406           //\r
407           // a resend - continue\r
408           //\r
409           Status = EFI_TIMEOUT;\r
410         } else if (Private->BigBlkNumFlag) {\r
411           if (BlockNum != ++LastBlock) {\r
412             DEBUG ((DEBUG_NET, "\nLockStepReceive()  Exit #1a"));\r
413             //\r
414             // not correct blocknum - error\r
415             //\r
416             return EFI_PROTOCOL_ERROR;\r
417           }\r
418         } else {\r
419           LastBlock = (LastBlock + 1) & 0xFFFF;\r
420           if (BlockNum != LastBlock) {\r
421             DEBUG ((DEBUG_NET, "\nLockStepReceive()  Exit #1b"));\r
422             return EFI_PROTOCOL_ERROR;\r
423             //\r
424             // not correct blocknum - error\r
425             //\r
426           }\r
427         }\r
428       }\r
429     } while (Status == EFI_TIMEOUT && --Retries);\r
430 \r
431     if (EFI_ERROR (Status)) {\r
432       if (Status != EFI_BUFFER_TOO_SMALL) {\r
433         SendError (Private, ServerIpPtr, ServerPortPtr, OurPortPtr);\r
434       }\r
435 \r
436       return Status;\r
437     }\r
438 \r
439     if (DontUseBuffer) {\r
440       BufferSize += ReplyLen;\r
441     } else {\r
442       BufferPtr += ReplyLen;\r
443       BufferSize -= ReplyLen;\r
444     }\r
445   }\r
446   //\r
447   // while (ReplyLen == PacketSize);\r
448   //\r
449   if (DontUseBuffer) {\r
450     if (BufferSizePtr != NULL) {\r
451       *BufferSizePtr = (BufferSize - PacketSize);\r
452     }\r
453   } else {\r
454     *BufferSizePtr -= BufferSize;\r
455   }\r
456 \r
457   /* Send ACK of last packet. */\r
458   ReplyLen = 0;\r
459 \r
460   SendAckAndGetData (\r
461     Private,\r
462     ServerIpPtr,\r
463     ServerPortPtr,\r
464     ReplyIpPtr,\r
465     OurPortPtr,\r
466     Timeout,\r
467     (UINTN *) &ReplyLen,\r
468     BufferPtr,\r
469     &BlockNum,\r
470     TRUE\r
471     );\r
472 \r
473   return EFI_SUCCESS;\r
474 }\r
475 \r
476 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
477 \r
478 //\r
479 // some literals\r
480 //\r
481 STATIC UINT8                      Mode[]          = MODE_BINARY;\r
482 STATIC UINT8                      BlockSizeOp[]   = OP_BLKSIZE;\r
483 STATIC UINT8                      TsizeOp[]       = OP_TFRSIZE;\r
484 STATIC UINT8                      OverwriteOp[]   = OP_OVERWRITE;\r
485 STATIC UINT8                      BigBlkNumOp[]   = OP_BIGBLKNUM;\r
486 STATIC EFI_PXE_BASE_CODE_UDP_PORT TftpRequestPort = TFTP_OPEN_PORT;\r
487 \r
488 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
489 \r
490 /**\r
491 \r
492   @return Pointer to value field if option found or NULL if not found.\r
493 \r
494 **/\r
495 STATIC\r
496 UINT8 *\r
497 FindOption (\r
498   UINT8 *OptionPtr,\r
499   INTN  OpLen,\r
500   UINT8 *OackPtr,\r
501   INTN  OackSize\r
502   )\r
503 {\r
504   if ((OackSize -= OpLen) <= 0) {\r
505     return NULL;\r
506   }\r
507 \r
508   do {\r
509     if (!CompareMem (OackPtr, OptionPtr, OpLen)) {\r
510       return OackPtr + OpLen;\r
511     }\r
512 \r
513     ++OackPtr;\r
514   } while (--OackSize);\r
515 \r
516   return NULL;\r
517 }\r
518 \r
519 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
520 #define BKSZOP      1 // block size\r
521 #define TSIZEOP     2 // transfer size\r
522 #define OVERWRITEOP 4 // overwrite\r
523 #define BIGBLKNUMOP 8 // big block numbers\r
524 STATIC\r
525 EFI_STATUS\r
526 TftpRwReq (\r
527   UINT16                      Req,\r
528   UINT16                      Options,\r
529   PXE_BASECODE_DEVICE         *Private,\r
530   EFI_IP_ADDRESS              *ServerIpPtr,\r
531   EFI_PXE_BASE_CODE_UDP_PORT  *ServerPortPtr,\r
532   EFI_PXE_BASE_CODE_UDP_PORT  *OurPortPtr,\r
533   UINT8                       *FilenamePtr,\r
534   UINTN                       *PacketSizePtr,\r
535   VOID                        *Buffer\r
536   )\r
537 {\r
538   union {\r
539     UINT8             Data[514];\r
540     struct Tftpv4Req  ReqStr;\r
541   } *u;\r
542 \r
543   UINT16  OpFlags;\r
544   INTN    Len;\r
545   INTN    TotalLen;\r
546   UINT8   *Ptr;\r
547 \r
548   if (*OurPortPtr == 0) {\r
549     OpFlags = EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT;\r
550   } else {\r
551     OpFlags = EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT;\r
552   }\r
553   //\r
554   // build the basic request - opcode, filename, mode\r
555   //\r
556   u                 = Buffer;\r
557   u->ReqStr.OpCode  = HTONS (Req);\r
558   TotalLen = sizeof (Mode) + sizeof (u->ReqStr.OpCode) + (Len = 1 + AsciiStrLen ((CHAR8 *) FilenamePtr));\r
559 \r
560   CopyMem (u->ReqStr.FileName, FilenamePtr, Len);\r
561   Ptr = (UINT8 *) (u->ReqStr.FileName + Len);\r
562 \r
563   CopyMem (Ptr, Mode, sizeof (Mode));\r
564   Ptr += sizeof (Mode);\r
565 \r
566   if (Options & BKSZOP) {\r
567     CopyMem (Ptr, BlockSizeOp, sizeof (BlockSizeOp));\r
568     UtoA10 (*PacketSizePtr, Ptr + sizeof (BlockSizeOp));\r
569 \r
570     TotalLen += (Len = 1 + AsciiStrLen ((CHAR8 *) (Ptr + sizeof (BlockSizeOp))) + sizeof (BlockSizeOp));\r
571 \r
572     Ptr += Len;\r
573   }\r
574 \r
575   if (Options & TSIZEOP) {\r
576     CopyMem (Ptr, TsizeOp, sizeof (TsizeOp));\r
577     CopyMem (Ptr + sizeof (TsizeOp), "0", 2);\r
578     TotalLen += sizeof (TsizeOp) + 2;\r
579     Ptr += sizeof (TsizeOp) + 2;\r
580   }\r
581 \r
582   if (Options & OVERWRITEOP) {\r
583     CopyMem (Ptr, OverwriteOp, sizeof (OverwriteOp));\r
584     CopyMem (Ptr + sizeof (OverwriteOp), "1", 2);\r
585     TotalLen += sizeof (OverwriteOp) + 2;\r
586     Ptr += sizeof (OverwriteOp) + 2;\r
587   }\r
588 \r
589   if (Options & BIGBLKNUMOP) {\r
590     CopyMem (Ptr, BigBlkNumOp, sizeof (BigBlkNumOp));\r
591     CopyMem (Ptr + sizeof (BigBlkNumOp), "8", 2);\r
592     TotalLen += sizeof (BigBlkNumOp) + 2;\r
593     Ptr += sizeof (BigBlkNumOp) + 2;\r
594   }\r
595   //\r
596   // send it\r
597   //\r
598   return UdpWrite (\r
599           Private,\r
600           OpFlags,\r
601           ServerIpPtr,\r
602           ServerPortPtr,\r
603           0,\r
604           0,\r
605           OurPortPtr,\r
606           0,\r
607           0,\r
608           (UINTN *) &TotalLen,\r
609           u\r
610           );\r
611 }\r
612 \r
613 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
614 \r
615 /**\r
616 \r
617 \r
618 **/\r
619 STATIC\r
620 EFI_STATUS\r
621 TftpRwReqwResp (\r
622   UINT16                      Req,\r
623   UINT16                      Options,\r
624   PXE_BASECODE_DEVICE         *Private,\r
625   VOID                        *HeaderPtr,\r
626   UINTN                       *PacketSizePtr,\r
627   UINTN                       *ReplyLenPtr,\r
628   VOID                        *BufferPtr,\r
629   EFI_IP_ADDRESS              *ServerIpPtr,\r
630   EFI_PXE_BASE_CODE_UDP_PORT  *ServerPortPtr,\r
631   EFI_PXE_BASE_CODE_UDP_PORT  *ServerReplyPortPtr,\r
632   EFI_PXE_BASE_CODE_UDP_PORT  *OurPortPtr,\r
633   UINT8                       *FilenamePtr,\r
634   UINT16                      Timeout\r
635   )\r
636 {\r
637   EFI_STATUS  Status;\r
638   UINTN       SaveReplyLen;\r
639   INTN        Retries;\r
640   UINT8       Buffer[514];\r
641 \r
642   SaveReplyLen            = *ReplyLenPtr;\r
643   Retries                 = 3;\r
644   Private->BigBlkNumFlag  = FALSE;\r
645   *OurPortPtr             = 0;\r
646   //\r
647   // generate random\r
648   //\r
649   do {\r
650     if (*OurPortPtr != 0) {\r
651       if (++ *OurPortPtr == 0) {\r
652         *OurPortPtr = PXE_RND_PORT_LOW;\r
653       }\r
654     }\r
655     //\r
656     // send request from our Ip = StationIp\r
657     //\r
658     if ((Status = TftpRwReq (\r
659                     Req,\r
660                     Options,\r
661                     Private,\r
662                     ServerIpPtr,\r
663                     ServerPortPtr,\r
664                     OurPortPtr,\r
665                     FilenamePtr,\r
666                     PacketSizePtr,\r
667                     Buffer\r
668                     )) != EFI_SUCCESS) {\r
669       DEBUG (\r
670         (DEBUG_WARN,\r
671         "\nTftpRwReqwResp()  Exit #1  %xh (%r)",\r
672         Status,\r
673         Status)\r
674         );\r
675 \r
676       return Status;\r
677     }\r
678     //\r
679     // read reply to our Ip = StationIp\r
680     //\r
681     *ReplyLenPtr = SaveReplyLen;\r
682 \r
683     Status = TftpUdpRead (\r
684               Private,\r
685               EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT,\r
686               HeaderPtr,\r
687               ReplyLenPtr,\r
688               BufferPtr,\r
689               ServerIpPtr,\r
690               ServerReplyPortPtr,\r
691               0,\r
692               OurPortPtr,\r
693               Timeout\r
694               );\r
695   } while (Status == EFI_TIMEOUT && --Retries);\r
696 \r
697   if (!Options || Status != EFI_TFTP_ERROR) {\r
698     DEBUG (\r
699       (DEBUG_WARN,\r
700       "\nTftpRwReqwResp()  Exit #2  %xh (%r)",\r
701       Status,\r
702       Status)\r
703       );\r
704     return Status;\r
705   }\r
706 \r
707   Status = TftpRwReqwResp (\r
708             Req,\r
709             0,\r
710             Private,\r
711             HeaderPtr,\r
712             PacketSizePtr,\r
713             ReplyLenPtr,\r
714             BufferPtr,\r
715             ServerIpPtr,\r
716             ServerPortPtr,\r
717             ServerReplyPortPtr,\r
718             OurPortPtr,\r
719             FilenamePtr,\r
720             Timeout\r
721             );\r
722 \r
723   DEBUG ((DEBUG_WARN, "\nTftpRwReqwResp()  Exit #3  %xh (%r)", Status, Status));\r
724 \r
725   return Status;\r
726 }\r
727 \r
728 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
729 \r
730 //\r
731 // mtftp listen\r
732 // read on mcast ip, cport, from sport, for data packet\r
733 // returns success if gets multicast last packet or all up to last block\r
734 // if not missing, then finished\r
735 //\r
736 \r
737 /**\r
738 \r
739 \r
740 **/\r
741 STATIC\r
742 EFI_STATUS\r
743 MtftpListen (\r
744   PXE_BASECODE_DEVICE           *Private,\r
745   UINT64                        *BufferSizePtr,\r
746   UINT8                         *BufferPtr,\r
747   EFI_IP_ADDRESS                *ServerIpPtr,\r
748   EFI_PXE_BASE_CODE_MTFTP_INFO  *MtftpInfoPtr,\r
749   UINT64                        *StartBlockPtr,\r
750   UINTN                         *NumMissedPtr,\r
751   UINT16                        TransTimeout,\r
752   UINT16                        ListenTimeout,\r
753   UINT64                        FinalBlock,\r
754   IN BOOLEAN                    DontUseBuffer\r
755   )\r
756 {\r
757   EFI_STATUS        Status;\r
758   struct Tftpv4Ack  Header;\r
759   UINT64            Offset;\r
760   UINT64            BlockNum;\r
761   UINT64            LastBlockNum;\r
762   UINT64            BufferSize;\r
763   UINTN             NumMissed;\r
764   UINTN             PacketSize;\r
765   UINTN             SaveReplyLen;\r
766   UINTN             ReplyLen;\r
767   UINT16            Timeout;\r
768 \r
769   LastBlockNum  = *StartBlockPtr;\r
770   Timeout       = ListenTimeout;\r
771   *NumMissedPtr = 0;\r
772   PacketSize    = 0;\r
773   BufferSize    = *BufferSizePtr;\r
774   ReplyLen      = MAX_TFTP_PKT_SIZE;;\r
775 \r
776   //\r
777   // receive\r
778   //\r
779   do {\r
780     if ((SaveReplyLen = ReplyLen) > BufferSize) {\r
781       SaveReplyLen = 0;\r
782     }\r
783 \r
784     /* %%TBD - add big block number support */\r
785 \r
786     //\r
787     // get data - loop on resends\r
788     //\r
789     do {\r
790       ReplyLen = SaveReplyLen;\r
791 \r
792       if ((Status = TftpUdpRead (\r
793                       Private,\r
794                       0,\r
795                       &Header,\r
796                       &ReplyLen,\r
797                       BufferPtr,\r
798                       ServerIpPtr,\r
799                       &MtftpInfoPtr->SPort,\r
800                       &MtftpInfoPtr->MCastIp,\r
801                       &MtftpInfoPtr->CPort,\r
802                       Timeout\r
803                       )) != EFI_SUCCESS) {\r
804         return Status;\r
805       }\r
806       //\r
807       // make sure a data packet\r
808       //\r
809       if (Header.OpCode != HTONS (TFTP_DATA)) {\r
810         return EFI_PROTOCOL_ERROR;\r
811       }\r
812     } while ((BlockNum = NTOHS (Header.BlockNum)) == LastBlockNum);\r
813 \r
814     //\r
815     // make sure still going up\r
816     //\r
817     if (LastBlockNum > BlockNum) {\r
818       return EFI_PROTOCOL_ERROR;\r
819     }\r
820 \r
821     if (BlockNum - LastBlockNum > 0xFFFFFFFF) {\r
822       return EFI_PROTOCOL_ERROR;\r
823     } else {\r
824       NumMissed = (UINTN) (BlockNum - LastBlockNum - 1);\r
825     }\r
826 \r
827     LastBlockNum = BlockNum;\r
828 \r
829     //\r
830     // if first time through, some reinitialization\r
831     //\r
832     if (!PacketSize) {\r
833       *StartBlockPtr  = BlockNum;\r
834       PacketSize      = ReplyLen;\r
835       Timeout         = TransTimeout;\r
836     } else {\r
837       *NumMissedPtr = (UINT16) (*NumMissedPtr + NumMissed);\r
838     }\r
839     //\r
840     // if missed packets, update start block,\r
841     // etc. and move packet to proper place in buffer\r
842     //\r
843     if (NumMissed) {\r
844       *StartBlockPtr = BlockNum;\r
845       if (!DontUseBuffer) {\r
846         Offset = NumMissed * PacketSize;\r
847         CopyMem (BufferPtr + Offset, BufferPtr, ReplyLen);\r
848         BufferPtr += Offset;\r
849         BufferSize -= Offset;\r
850       }\r
851     }\r
852 \r
853     if (!DontUseBuffer) {\r
854       BufferPtr += ReplyLen;\r
855       BufferSize -= ReplyLen;\r
856     }\r
857   } while (ReplyLen == PacketSize && BlockNum != FinalBlock);\r
858 \r
859   *BufferSizePtr = BufferSize;\r
860 \r
861   return EFI_SUCCESS;\r
862 }\r
863 \r
864 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
865 \r
866 /**\r
867 \r
868   @return // mtftp open session\r
869   @return // return code EFI_SUCCESS\r
870   @return //      and *CompletionStatusPtr = GOTUNI | GOTMULTI means done\r
871   @return //      and *CompletionStatusPtr = GOTMULTI means got first two multicast packets, use listen for rest\r
872   @return //      and *CompletionStatusPtr = 0 means did not get first two multicast packets, use listen for all\r
873   @retval GOTUNI  returns NO_DATA go will go to TFTP session)\r
874 \r
875 **/\r
876 STATIC\r
877 EFI_STATUS\r
878 MtftpOpen (\r
879   PXE_BASECODE_DEVICE                                               * Private,\r
880   UINT64                                                            *BufferSizePtr,\r
881   UINT8                                                             *BufferPtr,\r
882   UINTN                                                             *PacketSizePtr,\r
883   EFI_IP_ADDRESS                                                    * ServerIpPtr,\r
884   UINT8                                                             *FilenamePtr,\r
885   EFI_PXE_BASE_CODE_MTFTP_INFO                                      * MtftpInfoPtr,\r
886   UINT8                                                             *CompletionStatusPtr,\r
887 #define GOTUNI 1\r
888 #define GOTMULTI 2\r
889   IN BOOLEAN                    DontUseBuffer\r
890   )\r
891 {\r
892   EFI_STATUS        Status;\r
893   EFI_IP_ADDRESS    OurReplyIp;\r
894   struct Tftpv4Ack  Header;\r
895   INTN              ReplyLen;\r
896   INTN              Retries;\r
897   UINT8             *BufferPtr2;\r
898   UINT8             TmpBuf[514];\r
899 \r
900   Retries         = NUM_MTFTP_OPEN_RETRIES;\r
901   BufferPtr2      = BufferPtr;\r
902   *PacketSizePtr  = (UINTN) (MIN (*BufferSizePtr, MAX_TFTP_PKT_SIZE));\r
903 \r
904   do {\r
905     //\r
906     // send a read request\r
907     //\r
908     *CompletionStatusPtr = 0;\r
909 \r
910     if ((Status = TftpRwReq (\r
911                     TFTP_RRQ,\r
912                     0,\r
913                     Private,\r
914                     ServerIpPtr,\r
915                     &MtftpInfoPtr->SPort,\r
916                     &MtftpInfoPtr->CPort,\r
917                     FilenamePtr,\r
918                     PacketSizePtr,\r
919                     TmpBuf\r
920                     )) != EFI_SUCCESS) {\r
921       return Status;\r
922     }\r
923 \r
924     for (;;) {\r
925       //\r
926       // read reply\r
927       //\r
928       ZeroMem (&OurReplyIp, Private->IpLength);\r
929       ReplyLen = *PacketSizePtr;\r
930 \r
931       if ((Status = TftpUdpRead (\r
932                       Private,\r
933                       EFI_PXE_BASE_CODE_UDP_OPFLAGS_USE_FILTER,\r
934                       &Header,\r
935                       (UINTN *) &ReplyLen,\r
936                       BufferPtr2,\r
937                       ServerIpPtr,\r
938                       &MtftpInfoPtr->SPort,\r
939                       &OurReplyIp,\r
940                       &MtftpInfoPtr->CPort,\r
941                       MtftpInfoPtr->TransmitTimeout\r
942                       )) == EFI_SUCCESS) {\r
943         //\r
944         // check for first data packet\r
945         //\r
946         if (Header.OpCode != HTONS (TFTP_DATA)) {\r
947           return EFI_PROTOCOL_ERROR;\r
948         }\r
949         //\r
950         // check block num\r
951         //\r
952         if (Header.BlockNum != HTONS (1)) {\r
953           //\r
954           // it's not first\r
955           // if we are not the primary client,\r
956           // we probably got first and now second\r
957           // multicast but no unicast, so\r
958           // *CompletionStatusPtr = GOTMULTI - if this is\r
959           // the second, can just go on to listen\r
960           // starting with 2 as the last block\r
961           // received\r
962           //\r
963           if (Header.BlockNum != HTONS (2)) {\r
964             //\r
965             // not second\r
966             //\r
967             *CompletionStatusPtr = 0;\r
968           }\r
969 \r
970           return Status;\r
971         }\r
972 \r
973         //\r
974         // now actual\r
975         //\r
976         *PacketSizePtr = ReplyLen;\r
977         //\r
978         // see if a unicast data packet\r
979         //\r
980         if (!CompareMem (\r
981               &OurReplyIp,\r
982               &Private->EfiBc.Mode->StationIp,\r
983               Private->IpLength\r
984               )) {\r
985           *CompletionStatusPtr |= GOTUNI;\r
986           //\r
987           // it is\r
988           // if already got multicast packet,\r
989           // got em both\r
990           //\r
991           if (*CompletionStatusPtr & GOTMULTI) {\r
992             break;\r
993           }\r
994         } else if (!CompareMem (\r
995                     &OurReplyIp,\r
996                     &MtftpInfoPtr->MCastIp,\r
997                     Private->IpLength\r
998                     )) {\r
999           //\r
1000           // otherwise see if a multicast data packet\r
1001           //\r
1002           *CompletionStatusPtr |= GOTMULTI;\r
1003           //\r
1004           // it is\r
1005           // got first - bump pointer so that if\r
1006           // second multi comes along, we're OK\r
1007           //\r
1008           if (!DontUseBuffer) {\r
1009             BufferPtr2 = (UINT8 *) BufferPtr + ReplyLen;\r
1010           }\r
1011           //\r
1012           // if already got unicast packet,\r
1013           // got em both\r
1014           //\r
1015           if (*CompletionStatusPtr & GOTUNI) {\r
1016             break;\r
1017           }\r
1018         } else {\r
1019           //\r
1020           // else protocol error\r
1021           //\r
1022           return EFI_PROTOCOL_ERROR;\r
1023         }\r
1024       } else if (Status == EFI_TIMEOUT) {\r
1025         //\r
1026         // bad return code - if timed out, retry\r
1027         //\r
1028         break;\r
1029       } else {\r
1030         //\r
1031         // else just bad - failed MTFTP open\r
1032         //\r
1033         return Status;\r
1034       }\r
1035     }\r
1036   } while (Status == EFI_TIMEOUT && --Retries);\r
1037 \r
1038   if (Status != EFI_SUCCESS) {\r
1039     //\r
1040     // open failed\r
1041     //\r
1042     return Status;\r
1043   }\r
1044   //\r
1045   // got em both - go into receive mode\r
1046   // routine to read rest of file after a successful open (TFTP or MTFTP)\r
1047   // sends ACK and gets next data packet until short packet arrives,\r
1048   // then sends ACK and (hopefully) times out\r
1049   //\r
1050   return LockStepReceive (\r
1051           Private,\r
1052           (UINT16) ReplyLen,\r
1053           BufferSizePtr,\r
1054           ReplyLen,\r
1055           BufferPtr,\r
1056           ServerIpPtr,\r
1057           &MtftpInfoPtr->SPort,\r
1058           &MtftpInfoPtr->MCastIp,\r
1059           &MtftpInfoPtr->CPort,\r
1060           1,\r
1061           MtftpInfoPtr->TransmitTimeout,\r
1062           DontUseBuffer\r
1063           );\r
1064 }\r
1065 \r
1066 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
1067 \r
1068 /**\r
1069 \r
1070 \r
1071 **/\r
1072 STATIC\r
1073 EFI_STATUS\r
1074 MtftpDownload (\r
1075   PXE_BASECODE_DEVICE           *Private,\r
1076   UINT64                        *BufferSizePtr,\r
1077   UINT8                         *BufferPtr,\r
1078   EFI_IP_ADDRESS                *ServerIpPtr,\r
1079   UINT8                         *FilenamePtr,\r
1080   EFI_PXE_BASE_CODE_MTFTP_INFO  *MtftpInfoPtr,\r
1081   IN BOOLEAN                    DontUseBuffer\r
1082   )\r
1083 {\r
1084   EFI_PXE_BASE_CODE_IP_FILTER Filter;\r
1085   EFI_STATUS                  Status;\r
1086   UINT64                      StartBlock;\r
1087   UINT64                      LastBlock;\r
1088   UINT64                      LastStartBlock;\r
1089   UINT64                      BufferSize;\r
1090   UINTN                       Offset;\r
1091   UINTN                       NumMissed;\r
1092   UINT16                      TransTimeout;\r
1093   UINT16                      ListenTimeout;\r
1094   UINT8                       *BufferPtrLocal;\r
1095 \r
1096   TransTimeout      = MtftpInfoPtr->TransmitTimeout;\r
1097   ListenTimeout     = MtftpInfoPtr->ListenTimeout;\r
1098   LastBlock         = 0;\r
1099   LastStartBlock    = 0;\r
1100   Offset            = 0;\r
1101 \r
1102   Filter.Filters    = EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST;\r
1103   Filter.IpCnt      = 2;\r
1104   CopyMem (&Filter.IpList[0], &Private->EfiBc.Mode->StationIp, sizeof (EFI_IP_ADDRESS));\r
1105   CopyMem (&Filter.IpList[1], &MtftpInfoPtr->MCastIp, sizeof (EFI_IP_ADDRESS));\r
1106 \r
1107   if ((Status = IpFilter (Private, &Filter)) != EFI_SUCCESS) {\r
1108     return Status;\r
1109   }\r
1110 \r
1111   for (;;) {\r
1112     StartBlock  = LastStartBlock;\r
1113     BufferSize  = *BufferSizePtr - Offset;\r
1114 \r
1115     if (DontUseBuffer) {\r
1116     //\r
1117     // overwrie the temp buf\r
1118     //\r
1119       BufferPtrLocal = BufferPtr;\r
1120     } else {\r
1121       BufferPtrLocal = BufferPtr + Offset;\r
1122 \r
1123     }\r
1124     //\r
1125     // special !!! do not leave enabled in saved version on Source Safe\r
1126     // Following code put in in order to create a special version for regression\r
1127     // test of MTFTP server to make sure it handles mulitple opens correctly.\r
1128     // This code should NOT be enabled normally.\r
1129     //\r
1130     if (((Status = MtftpListen (\r
1131                       Private,\r
1132                       &BufferSize,\r
1133                       BufferPtrLocal,\r
1134                       ServerIpPtr,\r
1135                       MtftpInfoPtr,\r
1136                       &StartBlock,\r
1137                       &NumMissed,\r
1138                       TransTimeout,\r
1139                       ListenTimeout,\r
1140                       LastBlock,\r
1141                       DontUseBuffer\r
1142                       )) != EFI_SUCCESS) && (Status != EFI_TIMEOUT)) {\r
1143         return Status;\r
1144         //\r
1145         // failed\r
1146         //\r
1147     }\r
1148     //\r
1149     // if none were received, start block is not reset\r
1150     //\r
1151     if (StartBlock == LastStartBlock) {\r
1152       UINT8 CompStat;\r
1153 \r
1154       //\r
1155       // timed out with none received - try MTFTP open\r
1156       //\r
1157       if ((Status = MtftpOpen (\r
1158                       Private,\r
1159                       BufferSizePtr,\r
1160                       BufferPtr,\r
1161                       &Offset,\r
1162                       ServerIpPtr,\r
1163                       FilenamePtr,\r
1164                       MtftpInfoPtr,\r
1165                       &CompStat,\r
1166                       DontUseBuffer\r
1167                       )) != EFI_SUCCESS) {\r
1168         //\r
1169         // open failure - try TFTP\r
1170         //\r
1171         return Status;\r
1172       }\r
1173       //\r
1174       // return code EFI_SUCCESS\r
1175       // and *CompletionStatusPtr = GOTUNI | GOTMULTI means done\r
1176       // and *CompletionStatusPtr = GOTMULTI means got first two multicast packets, use listen for rest\r
1177       // and *CompletionStatusPtr = 0 means did not get first two multicast packets, use listen for all\r
1178       // (do not get = GOTUNI - returns NO_DATA go will go to TFTP session)\r
1179       //\r
1180       if (CompStat == (GOTUNI | GOTMULTI)) {\r
1181       //\r
1182       // finished - got it all\r
1183       //\r
1184         return Status;\r
1185       }\r
1186 \r
1187       if (CompStat) {\r
1188         //\r
1189         // offset is two packet lengths\r
1190         //\r
1191         Offset <<= 1;\r
1192         //\r
1193         // last block received\r
1194         //\r
1195         LastStartBlock = 2;\r
1196       } else {\r
1197         Offset          = 0;\r
1198         LastStartBlock  = 0;\r
1199       }\r
1200 \r
1201       ListenTimeout = TransTimeout;\r
1202       continue;\r
1203     }\r
1204     //\r
1205     // did we get the last block\r
1206     //\r
1207     if (Status == EFI_SUCCESS) {\r
1208       //\r
1209       // yes - set the file size if this was first time\r
1210       //\r
1211       if (!LastBlock) {\r
1212         *BufferSizePtr -= BufferSize;\r
1213       }\r
1214       //\r
1215       // if buffer was too small, finished\r
1216       //\r
1217       if (!DontUseBuffer) {\r
1218         return EFI_BUFFER_TOO_SMALL;\r
1219       }\r
1220       //\r
1221       // if we got them all, finished\r
1222       //\r
1223       if (!NumMissed && StartBlock == LastStartBlock + 1) {\r
1224         return Status;\r
1225       }\r
1226       //\r
1227       // did not get them all - set last block\r
1228       //\r
1229       LastBlock = (UINT16) (StartBlock - 1);\r
1230     }\r
1231     //\r
1232     // compute listen timeout\r
1233     //\r
1234     ListenTimeout = (UINT16) ((NumMissed > MtftpInfoPtr->ListenTimeout) ? 0 : (MtftpInfoPtr->ListenTimeout - NumMissed));\r
1235 \r
1236     //\r
1237     // reset\r
1238     //\r
1239     Offset          = 0;\r
1240     LastStartBlock  = 0;\r
1241   }\r
1242 }\r
1243 \r
1244 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
1245 \r
1246 /**\r
1247 \r
1248 \r
1249 **/\r
1250 STATIC\r
1251 EFI_STATUS\r
1252 TftpInfo (\r
1253   PXE_BASECODE_DEVICE         *Private,\r
1254   UINT64                      *BufferSizePtr,\r
1255   EFI_IP_ADDRESS              *ServerIpPtr,\r
1256   EFI_PXE_BASE_CODE_UDP_PORT  SrvPort,\r
1257   UINT8                       *FilenamePtr,\r
1258   UINTN                       *PacketSizePtr\r
1259   )\r
1260 {\r
1261   EFI_PXE_BASE_CODE_UDP_PORT  OurPort;\r
1262   EFI_PXE_BASE_CODE_UDP_PORT  ServerReplyPort;\r
1263   EFI_STATUS                  Status;\r
1264   UINT64                      BlockNum;\r
1265   UINTN                       Offset;\r
1266   UINTN                       ReplyLen;\r
1267   UINT8                       *Ptr;\r
1268 \r
1269   union {\r
1270     struct Tftpv4Oack OAck2Ptr;\r
1271     struct Tftpv4Ack  Ack2Ptr;\r
1272     struct Tftpv4Data Datastr;\r
1273   } u;\r
1274 \r
1275   OurPort         = 0;\r
1276   ServerReplyPort = 0;\r
1277   ReplyLen        = sizeof (u.Datastr.Data);\r
1278 \r
1279   //\r
1280   // send a write request with the blocksize option -\r
1281   // sets our IP and port - and receive reply - sets his port\r
1282   // will retry operation up to 3 times if no response,\r
1283   // and will retry without options on an error reply\r
1284   //\r
1285   if ((Status = TftpRwReqwResp (\r
1286                   TFTP_RRQ,\r
1287                   /* BIGBLKNUMOP | */BKSZOP | TSIZEOP,\r
1288                   Private,\r
1289                   &u,\r
1290                   PacketSizePtr,\r
1291                   &ReplyLen,\r
1292                   u.Datastr.Data,\r
1293                   ServerIpPtr,\r
1294                   &SrvPort,\r
1295                   &ServerReplyPort,\r
1296                   &OurPort,\r
1297                   FilenamePtr,\r
1298                   REQ_RESP_TIMEOUT\r
1299                   )) != EFI_SUCCESS) {\r
1300     DEBUG ((DEBUG_WARN, "\nTftpInfo()  Exit #1"));\r
1301     return Status;\r
1302   }\r
1303   //\r
1304   // check for good OACK\r
1305   //\r
1306   if (u.OAck2Ptr.OpCode == HTONS (TFTP_OACK)) {\r
1307     //\r
1308     // now parse it for options\r
1309     // bigblk#\r
1310     //\r
1311     Ptr = FindOption (\r
1312             BigBlkNumOp,\r
1313             sizeof (BigBlkNumOp),\r
1314             u.OAck2Ptr.OpAck[0].Option,\r
1315             ReplyLen + sizeof (u.Ack2Ptr.BlockNum)\r
1316             );\r
1317 \r
1318     if (Ptr != NULL) {\r
1319       if (AtoU (Ptr) == 8) {\r
1320         Private->BigBlkNumFlag = TRUE;\r
1321       } else {\r
1322         return EFI_PROTOCOL_ERROR;\r
1323       }\r
1324     }\r
1325     //\r
1326     // blksize\r
1327     //\r
1328     Ptr = FindOption (\r
1329             BlockSizeOp,\r
1330             sizeof (BlockSizeOp),\r
1331             u.OAck2Ptr.OpAck[0].Option,\r
1332             ReplyLen += sizeof (u.Ack2Ptr.BlockNum)\r
1333             );\r
1334 \r
1335     *PacketSizePtr = (Ptr) ? AtoU (Ptr) : 512;\r
1336 \r
1337     //\r
1338     // tsize\r
1339     //\r
1340     Ptr = FindOption (\r
1341             TsizeOp,\r
1342             sizeof (TsizeOp),\r
1343             u.OAck2Ptr.OpAck[0].Option,\r
1344             ReplyLen\r
1345             );\r
1346 \r
1347     if (Ptr != NULL) {\r
1348       *BufferSizePtr = AtoU64 (Ptr);\r
1349 \r
1350       //\r
1351       // teminate session with error\r
1352       //\r
1353       SendError (Private, ServerIpPtr, &ServerReplyPort, &OurPort);\r
1354 \r
1355       return EFI_SUCCESS;\r
1356     }\r
1357 \r
1358     Offset    = 0;\r
1359     BlockNum  = 0;\r
1360   } else {\r
1361     //\r
1362     // if MTFTP get filesize, return unsupported\r
1363     //\r
1364     if (SrvPort != TftpRequestPort) {\r
1365       SendError (Private, ServerIpPtr, &ServerReplyPort, &OurPort);\r
1366       DEBUG ((DEBUG_WARN, "\nTftpInfo()  Exit #3"));\r
1367       return EFI_UNSUPPORTED;\r
1368     }\r
1369 \r
1370     Offset    = ReplyLen;\r
1371     //\r
1372     // last block received\r
1373     //\r
1374     BlockNum  = 1;\r
1375   }\r
1376   //\r
1377   // does not support the option - do a download with no buffer\r
1378   //\r
1379   *BufferSizePtr = 0;\r
1380 \r
1381   Status = LockStepReceive (\r
1382             Private,\r
1383             (UINT16) ReplyLen,\r
1384             BufferSizePtr,\r
1385             Offset,\r
1386             (UINT8 *) &u,\r
1387             ServerIpPtr,\r
1388             &ServerReplyPort,\r
1389             &Private->EfiBc.Mode->StationIp,\r
1390             &OurPort,\r
1391             BlockNum,\r
1392             ACK_TIMEOUT,\r
1393             TRUE\r
1394             );\r
1395 \r
1396   if (Status != EFI_SUCCESS) {\r
1397     DEBUG ((DEBUG_WARN, "\nTftpInfo()  LockStepReceive() == %Xh", Status));\r
1398   }\r
1399 \r
1400   if (Status != EFI_BUFFER_TOO_SMALL) {\r
1401     return Status;\r
1402   }\r
1403 \r
1404   return EFI_SUCCESS;\r
1405 }\r
1406 \r
1407 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
1408 \r
1409 /**\r
1410 \r
1411 \r
1412 **/\r
1413 STATIC\r
1414 EFI_STATUS\r
1415 TftpDownload (\r
1416   PXE_BASECODE_DEVICE         *Private,\r
1417   UINT64                      *BufferSizePtr,\r
1418   UINT8                       *BufferPtr,\r
1419   EFI_IP_ADDRESS              *ServerIpPtr,\r
1420   UINT8                       *FilenamePtr,\r
1421   UINTN                       *PacketSizePtr,\r
1422   EFI_PXE_BASE_CODE_UDP_PORT  SrvPort,\r
1423   UINT16                      Req,\r
1424   IN BOOLEAN                  DontUseBuffer\r
1425   )\r
1426 {\r
1427   EFI_PXE_BASE_CODE_UDP_PORT  OurPort;\r
1428   EFI_PXE_BASE_CODE_UDP_PORT  ServerReplyPort;\r
1429   EFI_STATUS                  Status;\r
1430   UINT64                      Offset;\r
1431   UINT64                      BlockNum;\r
1432   UINTN                       ReplyLen;\r
1433   UINT8                       *Ptr;\r
1434 \r
1435   union {\r
1436     struct Tftpv4Ack    Ack2Ptr;\r
1437     struct Tftpv4Oack   OAck2Ptr;\r
1438     struct Tftpv4Data   Data;\r
1439     struct Tftpv4Ack8   Ack8Ptr;\r
1440     struct Tftpv4Data8  Data8;\r
1441   } U;\r
1442 \r
1443   OurPort         = 0;\r
1444   ServerReplyPort = 0;\r
1445   ReplyLen        = (UINTN) ((*BufferSizePtr > 0xFFFF) ? 0xFFFF : *BufferSizePtr);\r
1446 \r
1447   //\r
1448   // send a read request with the blocksize option - sets our IP and port\r
1449   // - and receive reply - sets his port will retry operation up to 3\r
1450   // times if no response, and will retry without options on an error\r
1451   // reply\r
1452   //\r
1453   if ((Status = TftpRwReqwResp (\r
1454                   Req,\r
1455                   /* BIGBLKNUMOP | */BKSZOP,\r
1456                   Private,\r
1457                   &U,\r
1458                   PacketSizePtr,\r
1459                   &ReplyLen,\r
1460                   BufferPtr,\r
1461                   ServerIpPtr,\r
1462                   &SrvPort,\r
1463                   &ServerReplyPort,\r
1464                   &OurPort,\r
1465                   FilenamePtr,\r
1466                   REQ_RESP_TIMEOUT\r
1467                   )) != EFI_SUCCESS) {\r
1468     DEBUG ((DEBUG_WARN, "\nTftpDownload()  Exit #1  %xh (%r)", Status, Status));\r
1469     return Status;\r
1470   }\r
1471   //\r
1472   // check for OACK\r
1473   //\r
1474   if (U.OAck2Ptr.OpCode == HTONS (TFTP_OACK)) {\r
1475     //\r
1476     // get the OACK\r
1477     //\r
1478     CopyMem (U.Data.Data, BufferPtr, ReplyLen);\r
1479 \r
1480     Ptr = FindOption (\r
1481             BigBlkNumOp,\r
1482             sizeof (BigBlkNumOp),\r
1483             U.OAck2Ptr.OpAck[0].Option,\r
1484             ReplyLen + sizeof (U.Ack2Ptr.BlockNum)\r
1485             );\r
1486 \r
1487     if (Ptr != NULL) {\r
1488       if (AtoU (Ptr) == 8) {\r
1489         Private->BigBlkNumFlag = TRUE;\r
1490       } else {\r
1491         return EFI_PROTOCOL_ERROR;\r
1492       }\r
1493     }\r
1494     //\r
1495     // now parse it for blocksize option\r
1496     //\r
1497     Ptr = FindOption (\r
1498             BlockSizeOp,\r
1499             sizeof (BlockSizeOp),\r
1500             U.OAck2Ptr.OpAck[0].Option,\r
1501             ReplyLen += sizeof (U.Ack2Ptr.BlockNum)\r
1502             );\r
1503 \r
1504     ReplyLen  = (Ptr != NULL) ? AtoU (Ptr) : 512;\r
1505 \r
1506     Offset    = 0;\r
1507     //\r
1508     // last block received\r
1509     //\r
1510     BlockNum  = 0;\r
1511   } else if (U.Ack2Ptr.OpCode != HTONS (TFTP_DATA) || U.Ack2Ptr.BlockNum != HTONS (1)) {\r
1512     //\r
1513     // or data\r
1514     //\r
1515     DEBUG ((DEBUG_WARN, "\nTftpDownload()  Exit #2  %xh (%r)", Status, Status));\r
1516 \r
1517     return EFI_PROTOCOL_ERROR;\r
1518   } else {\r
1519     //\r
1520     // got good data packet\r
1521     //\r
1522     Offset    = ReplyLen;\r
1523     //\r
1524     // last block received\r
1525     //\r
1526     BlockNum  = 1;\r
1527   }\r
1528 \r
1529   if (PacketSizePtr != NULL) {\r
1530     *PacketSizePtr = ReplyLen;\r
1531   }\r
1532   //\r
1533   // routine to read rest of file after a successful open (TFTP or MTFTP)\r
1534   // sends ACK and gets next data packet until short packet arrives, then sends\r
1535   // ACK and (hopefully) times out\r
1536   // if first packet has been read, BufferPtr and BufferSize must reflect fact\r
1537   //\r
1538   Status = LockStepReceive (\r
1539             Private,\r
1540             ReplyLen,\r
1541             BufferSizePtr,\r
1542             Offset,\r
1543             BufferPtr,\r
1544             ServerIpPtr,\r
1545             &ServerReplyPort,\r
1546             &Private->EfiBc.Mode->StationIp,\r
1547             &OurPort,\r
1548             BlockNum,\r
1549             ACK_TIMEOUT,\r
1550             DontUseBuffer\r
1551             );\r
1552 \r
1553   if (Status != EFI_SUCCESS) {\r
1554     DEBUG ((DEBUG_WARN, "\nTftpDownload()  Exit #3  %xh (%r)", Status, Status));\r
1555 \r
1556     if (Status == EFI_BUFFER_TOO_SMALL) {\r
1557       Status = TftpInfo (\r
1558                 Private,\r
1559                 BufferSizePtr,\r
1560                 ServerIpPtr,\r
1561                 SrvPort,\r
1562                 FilenamePtr,\r
1563                 PacketSizePtr\r
1564                 );\r
1565 \r
1566       if (!EFI_ERROR (Status)) {\r
1567         Status = EFI_BUFFER_TOO_SMALL;\r
1568       }\r
1569     }\r
1570   }\r
1571 \r
1572   return Status;\r
1573 }\r
1574 \r
1575 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
1576 \r
1577 /**\r
1578 \r
1579 \r
1580 **/\r
1581 STATIC\r
1582 EFI_STATUS\r
1583 TftpUpload (\r
1584   PXE_BASECODE_DEVICE *Private,\r
1585   UINT64              *BufferSizePtr,\r
1586   VOID                *BufferPtr,\r
1587   EFI_IP_ADDRESS      *ServerIpPtr,\r
1588   UINT8               *FilenamePtr,\r
1589   UINTN               *PacketSizePtr,\r
1590   BOOLEAN             Overwrite\r
1591   )\r
1592 {\r
1593   struct Tftpv4Ack            Header;\r
1594   EFI_PXE_BASE_CODE_UDP_PORT  OurPort;\r
1595   EFI_PXE_BASE_CODE_UDP_PORT  ServerReplyPort;\r
1596   EFI_STATUS                  Status;\r
1597   UINT64                      BlockNum;\r
1598   UINT64                      TransferSize;\r
1599   UINTN                       ReplyLen;\r
1600   UINTN                       TransferLen;\r
1601   UINT16                      Options;\r
1602   UINT8                       *Ptr;\r
1603 \r
1604   union {\r
1605     struct Tftpv4Oack OAck2Ptr;\r
1606     struct Tftpv4Ack  Ack2Ptr;\r
1607     struct Tftpv4Data Datastr;\r
1608   } u;\r
1609 \r
1610   OurPort         = 0;\r
1611   ServerReplyPort = 0;\r
1612   TransferSize    = *BufferSizePtr;\r
1613   ReplyLen        = sizeof (u.Datastr.Data);\r
1614   Options         = (UINT16) ((Overwrite) ? OVERWRITEOP | BKSZOP : BKSZOP);\r
1615 \r
1616   //\r
1617   // send a write request with the blocksize option - sets our IP and port -\r
1618   // and receive reply - sets his port\r
1619   // will retry operation up to 3 times if no response, and will retry without\r
1620   // options on an error reply\r
1621   //\r
1622   if ((Status = TftpRwReqwResp (\r
1623                   TFTP_WRQ,\r
1624                   Options,\r
1625                   Private,\r
1626                   &u,\r
1627                   PacketSizePtr,\r
1628                   &ReplyLen,\r
1629                   u.Datastr.Data,\r
1630                   ServerIpPtr,\r
1631                   &TftpRequestPort,\r
1632                   &ServerReplyPort,\r
1633                   &OurPort,\r
1634                   FilenamePtr,\r
1635                   REQ_RESP_TIMEOUT\r
1636                   )) != EFI_SUCCESS) {\r
1637     return Status;\r
1638   }\r
1639   //\r
1640   // check for OACK\r
1641   //\r
1642   if (u.OAck2Ptr.OpCode == HTONS (TFTP_OACK)) {\r
1643     //\r
1644     // parse it for blocksize option\r
1645     //\r
1646     Ptr = FindOption (\r
1647             BlockSizeOp,\r
1648             sizeof (BlockSizeOp),\r
1649             u.OAck2Ptr.OpAck[0].Option,\r
1650             ReplyLen += sizeof (u.Ack2Ptr.BlockNum)\r
1651             );\r
1652     *PacketSizePtr = (Ptr) ? AtoU (Ptr) : 512;\r
1653   }\r
1654   //\r
1655   // or ACK\r
1656   //\r
1657   else if (u.Ack2Ptr.OpCode == HTONS (TFTP_ACK)) {\r
1658     //\r
1659     // option was not supported\r
1660     //\r
1661     *PacketSizePtr = 512;\r
1662   } else {\r
1663     return EFI_PROTOCOL_ERROR;\r
1664   }\r
1665   //\r
1666   // loop\r
1667   //\r
1668   Header.OpCode   = HTONS (TFTP_DATA);\r
1669   BlockNum        = 1;\r
1670   Header.BlockNum = HTONS (1);\r
1671 \r
1672   do {\r
1673     UINTN HeaderSize;\r
1674     INTN  Retries;\r
1675 \r
1676     Retries     = NUM_ACK_RETRIES;\r
1677     HeaderSize  = sizeof (Header);\r
1678     TransferLen = (UINTN) (MIN (*PacketSizePtr, TransferSize));\r
1679 \r
1680     //\r
1681     // write a data packet and get an ack\r
1682     //\r
1683     do {\r
1684       //\r
1685       // write\r
1686       //\r
1687       if ((Status = UdpWrite (\r
1688                       Private,\r
1689                       EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT,\r
1690                       ServerIpPtr,\r
1691                       &ServerReplyPort,\r
1692                       0,\r
1693                       0,\r
1694                       &OurPort,\r
1695                       &HeaderSize,\r
1696                       &Header,\r
1697                       &TransferLen,\r
1698                       BufferPtr\r
1699                       )) != EFI_SUCCESS) {\r
1700         return Status;\r
1701       }\r
1702       //\r
1703       // read reply\r
1704       //\r
1705       ReplyLen = sizeof (u.Datastr.Data);\r
1706 \r
1707       if ((Status = TftpUdpRead (\r
1708                       Private,\r
1709                       0,\r
1710                       &u,\r
1711                       &ReplyLen,\r
1712                       u.Datastr.Data,\r
1713                       ServerIpPtr,\r
1714                       &ServerReplyPort,\r
1715                       0,\r
1716                       &OurPort,\r
1717                       ACK_TIMEOUT\r
1718                       )) == EFI_SUCCESS) {\r
1719         //\r
1720         // check for ACK for this data packet\r
1721         //\r
1722         if (u.Ack2Ptr.OpCode != HTONS (TFTP_ACK)) {\r
1723           return EFI_PROTOCOL_ERROR;\r
1724         }\r
1725 \r
1726         if (u.Ack2Ptr.BlockNum != Header.BlockNum) {\r
1727           //\r
1728           // not for this packet - continue\r
1729           //\r
1730           Status = EFI_TIMEOUT;\r
1731         }\r
1732       }\r
1733     } while (Status == EFI_TIMEOUT && --Retries);\r
1734 \r
1735     if (Status != EFI_SUCCESS) {\r
1736       return Status;\r
1737     }\r
1738 \r
1739     BufferPtr = (VOID *) ((UINT8 *) (BufferPtr) + TransferLen);\r
1740     TransferSize -= TransferLen;\r
1741     ++BlockNum;\r
1742     Header.BlockNum = HTONS ((UINT16) BlockNum);\r
1743   } while (TransferLen == *PacketSizePtr);\r
1744 \r
1745   return EFI_SUCCESS;\r
1746 }\r
1747 \r
1748 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
1749 \r
1750 /**\r
1751 \r
1752   @return *  EFI_INVALID_PARAMETER\r
1753   @return *  EFI_OUT_OF_RESOURCES\r
1754   @return *  EFI_BAD_BUFFER_SIZE\r
1755   @return *  Status is also returned from IpFilter(), TftpInfo(), MtftpDownload(),\r
1756   @return *  TftpDownload() and TftpUpload().\r
1757 \r
1758 **/\r
1759 EFI_STATUS\r
1760 PxeBcMtftp (\r
1761   PXE_BASECODE_DEVICE               *Private,\r
1762   IN EFI_PXE_BASE_CODE_TFTP_OPCODE  Operation,\r
1763   UINT64                            *BufferSizePtr,\r
1764   VOID                              *BufferPtr,\r
1765   EFI_IP_ADDRESS                    *ServerIpPtr,\r
1766   UINT8                             *FilenamePtr,\r
1767   UINTN                             *PacketSizePtr,\r
1768   IN EFI_PXE_BASE_CODE_MTFTP_INFO   *MtftpInfoPtr, OPTIONAL\r
1769   IN BOOLEAN                        Overwrite,\r
1770   IN BOOLEAN                        DontUseBuffer\r
1771   )\r
1772 {\r
1773   EFI_PXE_BASE_CODE_IP_FILTER Filter;\r
1774   EFI_STATUS                  StatCode;\r
1775   EFI_STATUS                  Status;\r
1776   UINT64                      BufferSizeLocal;\r
1777   UINTN                       PacketSize;\r
1778   UINT8                       *BufferPtrLocal;\r
1779 \r
1780   Filter.Filters  = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP;\r
1781   Filter.IpCnt    = 0;\r
1782   Filter.reserved = 0;\r
1783 \r
1784   /* No error has occurred, yet. */\r
1785   Private->EfiBc.Mode->TftpErrorReceived = FALSE;\r
1786 \r
1787   /* We must at least have an MTFTP server IP address and\r
1788    * a pointer to the buffer size.\r
1789    */\r
1790   if (!ServerIpPtr || !BufferSizePtr) {\r
1791     DEBUG ((DEBUG_WARN, "\nPxeBcMtftp()  Exit #1"));\r
1792 \r
1793     return EFI_INVALID_PARAMETER;\r
1794   }\r
1795 \r
1796   Private->Function = EFI_PXE_BASE_CODE_FUNCTION_MTFTP;\r
1797 \r
1798   //\r
1799   // make sure filter set to unicast at start\r
1800   //\r
1801   if ((StatCode = IpFilter (Private, &Filter)) != EFI_SUCCESS) {\r
1802     DEBUG (\r
1803       (DEBUG_NET,\r
1804       "\nPxeBcMtftp()  Exit  IpFilter() == %Xh",\r
1805       StatCode)\r
1806       );\r
1807 \r
1808     return StatCode;\r
1809   }\r
1810   //\r
1811   // set unset parms to default values\r
1812   //\r
1813   if (!PacketSizePtr) {\r
1814     *(PacketSizePtr = &PacketSize) = MAX_TFTP_PKT_SIZE;\r
1815   }\r
1816 \r
1817   if ((*PacketSizePtr > *BufferSizePtr) &&\r
1818     (Operation != EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE) &&\r
1819     (Operation != EFI_PXE_BASE_CODE_MTFTP_GET_FILE_SIZE)) {\r
1820     *PacketSizePtr = MAX ((UINTN) *BufferSizePtr, MIN_TFTP_PKT_SIZE);\r
1821   }\r
1822 \r
1823   if (*PacketSizePtr < MIN_TFTP_PKT_SIZE) {\r
1824     *PacketSizePtr = MIN_TFTP_PKT_SIZE;\r
1825     return EFI_INVALID_PARAMETER;\r
1826   }\r
1827 \r
1828   if (*PacketSizePtr > BUFFER_ALLOCATE_SIZE) {\r
1829     *PacketSizePtr = BUFFER_ALLOCATE_SIZE;\r
1830   }\r
1831 \r
1832   if (*PacketSizePtr > MAX_TFTP_PKT_SIZE) {\r
1833     *PacketSizePtr = MAX_TFTP_PKT_SIZE;\r
1834   }\r
1835 \r
1836   if (Operation == EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE) {\r
1837     StatCode = TftpInfo (\r
1838                 Private,\r
1839                 BufferSizePtr,\r
1840                 ServerIpPtr,\r
1841                 TftpRequestPort,\r
1842                 FilenamePtr,\r
1843                 PacketSizePtr\r
1844                 );\r
1845 \r
1846     if (StatCode != EFI_SUCCESS) {\r
1847       DEBUG (\r
1848         (DEBUG_WARN,\r
1849         "\nPxeBcMtftp()  Exit  TftpInfo() == %Xh",\r
1850         StatCode)\r
1851         );\r
1852     }\r
1853 \r
1854     return StatCode;\r
1855   }\r
1856 \r
1857   if (Operation == EFI_PXE_BASE_CODE_MTFTP_GET_FILE_SIZE) {\r
1858     if (!MtftpInfoPtr || !MtftpInfoPtr->SPort) {\r
1859       DEBUG ((DEBUG_WARN, "\nPxeBcMtftp()  Exit #2"));\r
1860       return EFI_INVALID_PARAMETER;\r
1861     } else {\r
1862       StatCode = TftpInfo (\r
1863                   Private,\r
1864                   BufferSizePtr,\r
1865                   ServerIpPtr,\r
1866                   MtftpInfoPtr->SPort,\r
1867                   FilenamePtr,\r
1868                   PacketSizePtr\r
1869                   );\r
1870 \r
1871       gBS->Stall (10000);\r
1872 \r
1873       if (StatCode != EFI_SUCCESS) {\r
1874         DEBUG (\r
1875           (DEBUG_WARN,\r
1876           "\nPxeBcMtftp()  Exit  TftpInfo() == %Xh",\r
1877           StatCode)\r
1878           );\r
1879       }\r
1880 \r
1881       return StatCode;\r
1882     }\r
1883   }\r
1884 \r
1885   if (!BufferPtr && !DontUseBuffer) {\r
1886     //\r
1887     // if dontusebuffer is false and no buffer???\r
1888     //\r
1889     DEBUG ((DEBUG_WARN, "\nPxeBcMtftp()  Exit #3"));\r
1890     //\r
1891     // DontUseBuffer can be true only for read_file operation\r
1892     //\r
1893     return EFI_INVALID_PARAMETER;\r
1894   }\r
1895 \r
1896   if (DontUseBuffer) {\r
1897     Status = gBS->AllocatePool (\r
1898                     EfiBootServicesData,\r
1899                     BUFFER_ALLOCATE_SIZE,\r
1900                     (VOID **) &BufferPtrLocal\r
1901                     );\r
1902 \r
1903     if (EFI_ERROR (Status) || BufferPtrLocal == NULL) {\r
1904       DEBUG ((DEBUG_NET, "\nPxeBcMtftp()  Exit #4"));\r
1905       return EFI_OUT_OF_RESOURCES;\r
1906     }\r
1907 \r
1908     BufferSizeLocal = BUFFER_ALLOCATE_SIZE;\r
1909   } else {\r
1910     if (!*BufferSizePtr && Operation != EFI_PXE_BASE_CODE_TFTP_WRITE_FILE) {\r
1911       DEBUG ((DEBUG_WARN, "\nPxeBcMtftp()  Exit #5"));\r
1912       return EFI_BAD_BUFFER_SIZE;\r
1913     }\r
1914 \r
1915     BufferPtrLocal  = BufferPtr;\r
1916     BufferSizeLocal = *BufferSizePtr;\r
1917   }\r
1918 \r
1919   switch (Operation) {\r
1920   case EFI_PXE_BASE_CODE_MTFTP_READ_FILE:\r
1921     if (FilenamePtr == NULL ||\r
1922         MtftpInfoPtr == NULL ||\r
1923         MtftpInfoPtr->MCastIp.Addr[0] == 0 ||\r
1924         MtftpInfoPtr->SPort == 0 ||\r
1925         MtftpInfoPtr->CPort == 0 ||\r
1926         MtftpInfoPtr->ListenTimeout == 0 ||\r
1927         MtftpInfoPtr->TransmitTimeout == 0\r
1928         ) {\r
1929       StatCode = EFI_INVALID_PARAMETER;\r
1930       break;\r
1931     }\r
1932     //\r
1933     // try MTFTP - if fails, drop into TFTP read\r
1934     //\r
1935     if ((StatCode = MtftpDownload (\r
1936                       Private,\r
1937                       &BufferSizeLocal,\r
1938                       BufferPtrLocal,\r
1939                       ServerIpPtr,\r
1940                       FilenamePtr,\r
1941                       MtftpInfoPtr,\r
1942                       DontUseBuffer\r
1943                       )) == EFI_SUCCESS || StatCode == EFI_BUFFER_TOO_SMALL) {\r
1944       if (BufferSizePtr /* %% !DontUseBuffer */ ) {\r
1945         *BufferSizePtr = BufferSizeLocal;\r
1946       }\r
1947 \r
1948       break;\r
1949     }\r
1950     //\r
1951     // go back to unicast\r
1952     //\r
1953     if ((StatCode = IpFilter (Private, &Filter)) != EFI_SUCCESS) {\r
1954       break;\r
1955     }\r
1956 \r
1957   /* fall thru */\r
1958   case EFI_PXE_BASE_CODE_TFTP_READ_FILE:\r
1959     if (FilenamePtr == NULL) {\r
1960       StatCode = EFI_INVALID_PARAMETER;\r
1961       break;\r
1962     }\r
1963 \r
1964     StatCode = TftpDownload (\r
1965                 Private,\r
1966                 &BufferSizeLocal,\r
1967                 BufferPtrLocal,\r
1968                 ServerIpPtr,\r
1969                 FilenamePtr,\r
1970                 PacketSizePtr,\r
1971                 TftpRequestPort,\r
1972                 TFTP_RRQ,\r
1973                 DontUseBuffer\r
1974                 );\r
1975 \r
1976     if (StatCode == EFI_SUCCESS || StatCode == EFI_BUFFER_TOO_SMALL) {\r
1977       if (BufferSizePtr /* !DontUseBuffer */ ) {\r
1978         *BufferSizePtr = BufferSizeLocal;\r
1979       }\r
1980     }\r
1981 \r
1982     break;\r
1983 \r
1984   case EFI_PXE_BASE_CODE_TFTP_WRITE_FILE:\r
1985     if (FilenamePtr == NULL || DontUseBuffer) {\r
1986       //\r
1987       // not a valid option\r
1988       //\r
1989       StatCode = EFI_INVALID_PARAMETER;\r
1990       break;\r
1991     }\r
1992 \r
1993     StatCode = TftpUpload (\r
1994                 Private,\r
1995                 BufferSizePtr,\r
1996                 BufferPtr,\r
1997                 ServerIpPtr,\r
1998                 FilenamePtr,\r
1999                 PacketSizePtr,\r
2000                 Overwrite\r
2001                 );\r
2002 \r
2003     if (StatCode != EFI_SUCCESS) {\r
2004       DEBUG (\r
2005         (DEBUG_WARN,\r
2006         "\nPxeBcMtftp()  Exit #6  %xh (%r)",\r
2007         StatCode,\r
2008         StatCode)\r
2009         );\r
2010     }\r
2011 \r
2012     return StatCode;\r
2013 \r
2014   case EFI_PXE_BASE_CODE_TFTP_READ_DIRECTORY:\r
2015     if (FilenamePtr == NULL || DontUseBuffer) {\r
2016       //\r
2017       // not a valid option\r
2018       //\r
2019       StatCode = EFI_INVALID_PARAMETER;\r
2020       break;\r
2021     }\r
2022 \r
2023     StatCode = TftpDownload (\r
2024                 Private,\r
2025                 BufferSizePtr,\r
2026                 BufferPtr,\r
2027                 ServerIpPtr,\r
2028                 FilenamePtr,\r
2029                 PacketSizePtr,\r
2030                 TftpRequestPort,\r
2031                 TFTP_DIR,\r
2032                 DontUseBuffer\r
2033                 );\r
2034 \r
2035     if (StatCode != EFI_SUCCESS) {\r
2036       DEBUG (\r
2037         (DEBUG_WARN,\r
2038         "\nPxeBcMtftp()  Exit #7  %xh (%r)",\r
2039         StatCode,\r
2040         StatCode)\r
2041         );\r
2042     }\r
2043 \r
2044     return StatCode;\r
2045 \r
2046   case EFI_PXE_BASE_CODE_MTFTP_READ_DIRECTORY:\r
2047     if (DontUseBuffer) {\r
2048       StatCode = EFI_INVALID_PARAMETER;\r
2049       break;\r
2050     }\r
2051 \r
2052     if (MtftpInfoPtr == NULL || !MtftpInfoPtr->SPort) {\r
2053       DEBUG (\r
2054         (DEBUG_WARN,\r
2055         "\nPxeBcMtftp()  Exit #9  %xh (%r)",\r
2056         EFI_INVALID_PARAMETER,\r
2057         EFI_INVALID_PARAMETER)\r
2058         );\r
2059 \r
2060       return EFI_INVALID_PARAMETER;\r
2061     }\r
2062 \r
2063     StatCode = TftpDownload (\r
2064                 Private,\r
2065                 BufferSizePtr,\r
2066                 BufferPtr,\r
2067                 ServerIpPtr,\r
2068                 (UINT8 *) "/",\r
2069                 PacketSizePtr,\r
2070                 MtftpInfoPtr->SPort,\r
2071                 TFTP_DIR,\r
2072                 DontUseBuffer\r
2073                 );\r
2074 \r
2075     break;\r
2076 \r
2077   default:\r
2078     StatCode = EFI_INVALID_PARAMETER;\r
2079   }\r
2080 \r
2081   if (DontUseBuffer) {\r
2082     gBS->FreePool (BufferPtrLocal);\r
2083   }\r
2084 \r
2085   if (StatCode != EFI_SUCCESS) {\r
2086     DEBUG (\r
2087       (DEBUG_WARN,\r
2088       "\nPxeBcMtftp()  Exit #8  %xh (%r)",\r
2089       StatCode,\r
2090       StatCode)\r
2091       );\r
2092   }\r
2093 \r
2094   gBS->Stall (10000);\r
2095 \r
2096   return StatCode;\r
2097 }\r
2098 \r
2099 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
2100 \r
2101 /**\r
2102 \r
2103   @return *  EFI_INVALID_PARAMETER\r
2104   @return *  Status is also returned from PxeBcMtftp();\r
2105 \r
2106 **/\r
2107 EFI_STATUS\r
2108 EFIAPI\r
2109 BcMtftp (\r
2110   IN EFI_PXE_BASE_CODE_PROTOCOL       * This,\r
2111   IN EFI_PXE_BASE_CODE_TFTP_OPCODE    Operation,\r
2112   IN OUT VOID                         *BufferPtr,\r
2113   IN BOOLEAN                          Overwrite,\r
2114   IN OUT UINT64                       *BufferSizePtr,\r
2115   IN UINTN                            *BlockSizePtr OPTIONAL,\r
2116   IN EFI_IP_ADDRESS                   * ServerIpPtr,\r
2117   IN UINT8                            *FilenamePtr,\r
2118   IN EFI_PXE_BASE_CODE_MTFTP_INFO     * MtftpInfoPtr OPTIONAL,\r
2119   IN BOOLEAN                          DontUseBuffer\r
2120   )\r
2121 {\r
2122   EFI_PXE_BASE_CODE_IP_FILTER Filter;\r
2123   EFI_STATUS                  StatCode;\r
2124   PXE_BASECODE_DEVICE         *Private;\r
2125 \r
2126   //\r
2127   // Lock the instance data and make sure started\r
2128   //\r
2129   StatCode = EFI_SUCCESS;\r
2130 \r
2131   if (This == NULL) {\r
2132     DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL"));\r
2133     return EFI_INVALID_PARAMETER;\r
2134   }\r
2135 \r
2136   Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE);\r
2137 \r
2138   if (Private == NULL) {\r
2139     DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL"));\r
2140     return EFI_INVALID_PARAMETER;\r
2141   }\r
2142 \r
2143   if (!IS_INADDR_UNICAST (ServerIpPtr)) {\r
2144       //\r
2145       // The station IP is not a unicast address.\r
2146       //\r
2147       return EFI_INVALID_PARAMETER;\r
2148   }\r
2149 \r
2150   EfiAcquireLock (&Private->Lock);\r
2151 \r
2152   if (This->Mode == NULL || !This->Mode->Started) {\r
2153     DEBUG ((DEBUG_ERROR, "BC was not started."));\r
2154     EfiReleaseLock (&Private->Lock);\r
2155     return EFI_NOT_STARTED;\r
2156   }\r
2157   //\r
2158   // Issue BC command\r
2159   //\r
2160   Filter.Filters  = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP;\r
2161   Filter.IpCnt    = 0;\r
2162   Filter.reserved = 0;\r
2163 \r
2164   DEBUG ((DEBUG_WARN, "\nBcMtftp()  Op=%d  Buf=%Xh", Operation, BufferPtr));\r
2165 \r
2166   StatCode = PxeBcMtftp (\r
2167               Private,\r
2168               Operation,\r
2169               BufferSizePtr,\r
2170               BufferPtr,\r
2171               ServerIpPtr,\r
2172               FilenamePtr,\r
2173               BlockSizePtr,\r
2174               MtftpInfoPtr,\r
2175               Overwrite,\r
2176               DontUseBuffer\r
2177               );\r
2178 \r
2179   //\r
2180   // restore to unicast\r
2181   //\r
2182   IpFilter (Private, &Filter);\r
2183 \r
2184   //\r
2185   // Unlock the instance data\r
2186   //\r
2187   EfiReleaseLock (&Private->Lock);\r
2188   return StatCode;\r
2189 }\r
2190 \r
2191 /* eof - PxeBcMtftp.c */\r