1 /** @file
2   Mtftp6 support functions implementation.
3 
4   Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.<BR>
5 
6   This program and the accompanying materials
7   are licensed and made available under the terms and conditions of the BSD License
8   which accompanies this distribution.  The full text of the license may be found at
9   http://opensource.org/licenses/bsd-license.php.
10 
11   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13 
14 **/
15 
16 #include "Mtftp6Impl.h"
17 
18 
19 /**
20   Allocate a MTFTP block range, then init it to the range of [Start, End].
21 
22   @param[in]  Start                  The start block number.
23   @param[in]  End                    The last block number in the range.
24 
25   @return Range                      The range of the allocated block buffer.
26 
27 **/
28 MTFTP6_BLOCK_RANGE *
Mtftp6AllocateRange(IN UINT16 Start,IN UINT16 End)29 Mtftp6AllocateRange (
30   IN UINT16                 Start,
31   IN UINT16                 End
32   )
33 {
34   MTFTP6_BLOCK_RANGE        *Range;
35 
36   Range = AllocateZeroPool (sizeof (MTFTP6_BLOCK_RANGE));
37 
38   if (Range == NULL) {
39     return NULL;
40   }
41 
42   InitializeListHead (&Range->Link);
43   Range->Start  = Start;
44   Range->End    = End;
45   Range->Bound  = End;
46 
47   return Range;
48 }
49 
50 
51 /**
52   Initialize the block range for either RRQ or WRQ. RRQ and WRQ have
53   different requirements for Start and End. For example, during startup,
54   WRQ initializes its whole valid block range to [0, 0xffff]. This
55   is bacause the server will send an ACK0 to inform the user to start the
56   upload. When the client receives an ACK0, it will remove 0 from the range,
57   get the next block number, which is 1, then upload the BLOCK1. For RRQ
58   without option negotiation, the server will directly send the BLOCK1
59   in response to the client's RRQ. When received BLOCK1, the client will
60   remove it from the block range and send an ACK. It also works if there
61   is option negotiation.
62 
63   @param[in]  Head                   The block range head to initialize.
64   @param[in]  Start                  The Start block number.
65   @param[in]  End                    The last block number.
66 
67   @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory for initial block range.
68   @retval EFI_SUCCESS            The initial block range is created.
69 
70 **/
71 EFI_STATUS
Mtftp6InitBlockRange(IN LIST_ENTRY * Head,IN UINT16 Start,IN UINT16 End)72 Mtftp6InitBlockRange (
73   IN LIST_ENTRY             *Head,
74   IN UINT16                 Start,
75   IN UINT16                 End
76   )
77 {
78   MTFTP6_BLOCK_RANGE        *Range;
79 
80   Range = Mtftp6AllocateRange (Start, End);
81 
82   if (Range == NULL) {
83     return EFI_OUT_OF_RESOURCES;
84   }
85 
86   InsertTailList (Head, &Range->Link);
87   return EFI_SUCCESS;
88 }
89 
90 
91 /**
92   Get the first valid block number on the range list.
93 
94   @param[in]  Head                   The block range head.
95 
96   @retval     ==-1                   If the block range is empty.
97   @retval     >-1                    The first valid block number.
98 
99 **/
100 INTN
Mtftp6GetNextBlockNum(IN LIST_ENTRY * Head)101 Mtftp6GetNextBlockNum (
102   IN LIST_ENTRY              *Head
103   )
104 {
105   MTFTP6_BLOCK_RANGE  *Range;
106 
107   if (IsListEmpty (Head)) {
108     return -1;
109   }
110 
111   Range = NET_LIST_HEAD (Head, MTFTP6_BLOCK_RANGE, Link);
112   return Range->Start;
113 }
114 
115 
116 /**
117   Set the last block number of the block range list. It
118   removes all the blocks after the Last. MTFTP initialize the
119   block range to the maximum possible range, such as [0, 0xffff]
120   for WRQ. When it gets the last block number, it calls
121   this function to set the last block number.
122 
123   @param[in]  Head                   The block range list.
124   @param[in]  Last                   The last block number.
125 
126 **/
127 VOID
Mtftp6SetLastBlockNum(IN LIST_ENTRY * Head,IN UINT16 Last)128 Mtftp6SetLastBlockNum (
129   IN LIST_ENTRY             *Head,
130   IN UINT16                 Last
131   )
132 {
133   MTFTP6_BLOCK_RANGE        *Range;
134 
135   //
136   // Iterate from the tail to head to remove the block number
137   // after the last.
138   //
139   while (!IsListEmpty (Head)) {
140     Range = NET_LIST_TAIL (Head, MTFTP6_BLOCK_RANGE, Link);
141 
142     if (Range->Start > Last) {
143       RemoveEntryList (&Range->Link);
144       FreePool (Range);
145       continue;
146     }
147 
148     if (Range->End > Last) {
149       Range->End = Last;
150     }
151     return ;
152   }
153 }
154 
155 
156 /**
157   Remove the block number from the block range list.
158 
159   @param[in]  Head                   The block range list to remove from.
160   @param[in]  Num                    The block number to remove.
161   @param[in]  Completed              Whether Num is the last block number
162   @param[out] TotalBlock             The continuous block number in all
163 
164   @retval EFI_NOT_FOUND          The block number isn't in the block range list.
165   @retval EFI_SUCCESS            The block number has been removed from the list.
166   @retval EFI_OUT_OF_RESOURCES   Failed to allocate resources.
167 
168 **/
169 EFI_STATUS
Mtftp6RemoveBlockNum(IN LIST_ENTRY * Head,IN UINT16 Num,IN BOOLEAN Completed,OUT UINT64 * TotalBlock)170 Mtftp6RemoveBlockNum (
171   IN LIST_ENTRY             *Head,
172   IN UINT16                 Num,
173   IN BOOLEAN                Completed,
174   OUT UINT64                *TotalBlock
175   )
176 {
177   MTFTP6_BLOCK_RANGE        *Range;
178   MTFTP6_BLOCK_RANGE        *NewRange;
179   LIST_ENTRY                *Entry;
180 
181   NET_LIST_FOR_EACH (Entry, Head) {
182 
183     //
184     // Each block represents a hole [Start, End] in the file,
185     // skip to the first range with End >= Num
186     //
187     Range = NET_LIST_USER_STRUCT (Entry, MTFTP6_BLOCK_RANGE, Link);
188 
189     if (Range->End < Num) {
190       continue;
191     }
192 
193     //
194     // There are three different cases for Start
195     // 1. (Start > Num) && (End >= Num):
196     //    because all the holes before this one has the condition of
197     //    End < Num, so this block number has been removed.
198     //
199     // 2. (Start == Num) && (End >= Num):
200     //    Need to increase the Start by one, and if End == Num, this
201     //    hole has been removed completely, remove it.
202     //
203     // 3. (Start < Num) && (End >= Num):
204     //    if End == Num, only need to decrease the End by one because
205     //    we have (Start < Num) && (Num == End), so (Start <= End - 1).
206     //    if (End > Num), the hold is splited into two holes, with
207     //    [Start, Num - 1] and [Num + 1, End].
208     //
209     if (Range->Start > Num) {
210       return EFI_NOT_FOUND;
211 
212     } else if (Range->Start == Num) {
213       Range->Start++;
214 
215       //
216       // Note that: RFC 1350 does not mention block counter roll-over,
217       // but several TFTP hosts implement the roll-over be able to accept
218       // transfers of unlimited size. There is no consensus, however, whether
219       // the counter should wrap around to zero or to one. Many implementations
220       // wrap to zero, because this is the simplest to implement. Here we choose
221       // this solution.
222       //
223       *TotalBlock  = Num;
224 
225       if (Range->Round > 0) {
226         *TotalBlock += Range->Bound +  MultU64x32 ((UINT64) (Range->Round -1), (UINT32)(Range->Bound + 1)) + 1;
227       }
228 
229       if (Range->Start > Range->Bound) {
230         Range->Start = 0;
231         Range->Round ++;
232       }
233 
234       if ((Range->Start > Range->End) || Completed) {
235         RemoveEntryList (&Range->Link);
236         FreePool (Range);
237       }
238 
239       return EFI_SUCCESS;
240 
241     } else {
242       if (Range->End == Num) {
243         Range->End--;
244       } else {
245         NewRange = Mtftp6AllocateRange ((UINT16) (Num + 1), (UINT16) Range->End);
246 
247         if (NewRange == NULL) {
248           return EFI_OUT_OF_RESOURCES;
249         }
250 
251         Range->End = Num - 1;
252         NetListInsertAfter (&Range->Link, &NewRange->Link);
253       }
254 
255       return EFI_SUCCESS;
256     }
257   }
258 
259   return EFI_NOT_FOUND;
260 }
261 
262 
263 /**
264   Configure the opened Udp6 instance until the corresponding Ip6 instance
265   has been configured.
266 
267   @param[in]  UdpIo                  The pointer to the Udp6 Io.
268   @param[in]  UdpCfgData             The pointer to the Udp6 configure data.
269 
270   @retval EFI_SUCCESS            Configure the Udp6 instance successfully.
271   @retval EFI_NO_MAPPING         The corresponding Ip6 instance has not
272                                  been configured yet.
273 
274 **/
275 EFI_STATUS
Mtftp6GetMapping(IN UDP_IO * UdpIo,IN EFI_UDP6_CONFIG_DATA * UdpCfgData)276 Mtftp6GetMapping (
277   IN UDP_IO                 *UdpIo,
278   IN EFI_UDP6_CONFIG_DATA   *UdpCfgData
279   )
280 {
281   EFI_IP6_MODE_DATA         Ip6Mode;
282   EFI_UDP6_PROTOCOL         *Udp6;
283   EFI_STATUS                Status;
284   EFI_EVENT                 Event;
285 
286   Event  = NULL;
287   Udp6   = UdpIo->Protocol.Udp6;
288 
289   //
290   // Create a timer to check whether the Ip6 instance configured or not.
291   //
292   Status = gBS->CreateEvent (
293                   EVT_TIMER,
294                   TPL_CALLBACK,
295                   NULL,
296                   NULL,
297                   &Event
298                   );
299   if (EFI_ERROR (Status)) {
300     goto ON_EXIT;
301   }
302 
303   Status = gBS->SetTimer (
304                   Event,
305                   TimerRelative,
306                   MTFTP6_GET_MAPPING_TIMEOUT * MTFTP6_TICK_PER_SECOND
307                   );
308   if (EFI_ERROR (Status)) {
309     goto ON_EXIT;
310   }
311 
312   //
313   // Check the Ip6 mode data till timeout.
314   //
315   while (EFI_ERROR (gBS->CheckEvent (Event))) {
316 
317     Udp6->Poll (Udp6);
318 
319     Status = Udp6->GetModeData (Udp6, NULL, &Ip6Mode, NULL, NULL);
320 
321     if (!EFI_ERROR (Status)) {
322 
323       if  (Ip6Mode.IsConfigured) {
324         //
325         // Continue to configure the Udp6 instance.
326         //
327         Status = Udp6->Configure (Udp6, UdpCfgData);
328       } else {
329         Status = EFI_NO_MAPPING;
330       }
331     }
332   }
333 
334 ON_EXIT:
335 
336   if (Event != NULL) {
337     gBS->CloseEvent (Event);
338   }
339 
340   return Status;
341 }
342 
343 
344 /**
345   The dummy configure routine for create a new Udp6 Io.
346 
347   @param[in]  UdpIo                  The pointer to the Udp6 Io.
348   @param[in]  Context                The pointer to the context.
349 
350   @retval EFI_SUCCESS                This value is always returned.
351 
352 **/
353 EFI_STATUS
354 EFIAPI
Mtftp6ConfigDummyUdpIo(IN UDP_IO * UdpIo,IN VOID * Context)355 Mtftp6ConfigDummyUdpIo (
356   IN UDP_IO                 *UdpIo,
357   IN VOID                   *Context
358   )
359 {
360   return EFI_SUCCESS;
361 }
362 
363 
364 /**
365   The configure routine for Mtftp6 instance to transmit/receive.
366 
367   @param[in]  UdpIo                  The pointer to the Udp6 Io.
368   @param[in]  ServerIp               The pointer to the server address.
369   @param[in]  ServerPort             The pointer to the server port.
370   @param[in]  LocalIp                The pointer to the local address.
371   @param[in]  LocalPort              The pointer to the local port.
372 
373   @retval EFI_SUCCESS            Configured the Udp6 Io for Mtftp6 successfully.
374   @retval EFI_NO_MAPPING         The corresponding Ip6 instance has not been
375                                  configured yet.
376 
377 **/
378 EFI_STATUS
Mtftp6ConfigUdpIo(IN UDP_IO * UdpIo,IN EFI_IPv6_ADDRESS * ServerIp,IN UINT16 ServerPort,IN EFI_IPv6_ADDRESS * LocalIp,IN UINT16 LocalPort)379 Mtftp6ConfigUdpIo (
380   IN UDP_IO                 *UdpIo,
381   IN EFI_IPv6_ADDRESS       *ServerIp,
382   IN UINT16                 ServerPort,
383   IN EFI_IPv6_ADDRESS       *LocalIp,
384   IN UINT16                 LocalPort
385   )
386 {
387   EFI_STATUS                Status;
388   EFI_UDP6_PROTOCOL         *Udp6;
389   EFI_UDP6_CONFIG_DATA      *Udp6Cfg;
390 
391   Udp6    = UdpIo->Protocol.Udp6;
392   Udp6Cfg = &(UdpIo->Config.Udp6);
393 
394   ZeroMem (Udp6Cfg, sizeof (EFI_UDP6_CONFIG_DATA));
395 
396   //
397   // Set the Udp6 Io configure data.
398   //
399   Udp6Cfg->AcceptPromiscuous  = FALSE;
400   Udp6Cfg->AcceptAnyPort      = FALSE;
401   Udp6Cfg->AllowDuplicatePort = FALSE;
402   Udp6Cfg->TrafficClass       = 0;
403   Udp6Cfg->HopLimit           = 128;
404   Udp6Cfg->ReceiveTimeout     = 0;
405   Udp6Cfg->TransmitTimeout    = 0;
406   Udp6Cfg->StationPort        = LocalPort;
407   Udp6Cfg->RemotePort         = ServerPort;
408 
409   CopyMem (
410     &Udp6Cfg->StationAddress,
411     LocalIp,
412     sizeof (EFI_IPv6_ADDRESS)
413     );
414 
415   CopyMem (
416     &Udp6Cfg->RemoteAddress,
417     ServerIp,
418     sizeof (EFI_IPv6_ADDRESS)
419     );
420 
421   //
422   // Configure the Udp6 instance with current configure data.
423   //
424   Status = Udp6->Configure (Udp6, Udp6Cfg);
425 
426   if (Status == EFI_NO_MAPPING) {
427 
428     return Mtftp6GetMapping (UdpIo, Udp6Cfg);
429   }
430 
431   return Status;
432 }
433 
434 
435 /**
436   Build and transmit the request packet for the Mtftp6 instance.
437 
438   @param[in]  Instance               The pointer to the Mtftp6 instance.
439   @param[in]  Operation              The operation code of this packet.
440 
441   @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory for the request.
442   @retval EFI_SUCCESS            The request is built and sent.
443   @retval Others                 Failed to transmit the packet.
444 
445 **/
446 EFI_STATUS
Mtftp6SendRequest(IN MTFTP6_INSTANCE * Instance,IN UINT16 Operation)447 Mtftp6SendRequest (
448   IN MTFTP6_INSTANCE        *Instance,
449   IN UINT16                 Operation
450   )
451 {
452   EFI_MTFTP6_PACKET         *Packet;
453   EFI_MTFTP6_OPTION         *Options;
454   EFI_MTFTP6_TOKEN          *Token;
455   RETURN_STATUS             Status;
456   NET_BUF                   *Nbuf;
457   UINT8                     *Mode;
458   UINT8                     *Cur;
459   UINTN                     Index;
460   UINT32                    BufferLength;
461   UINTN                     FileNameLength;
462   UINTN                     ModeLength;
463   UINTN                     OptionStrLength;
464   UINTN                     ValueStrLength;
465 
466   Token   = Instance->Token;
467   Options = Token->OptionList;
468   Mode    = Token->ModeStr;
469 
470   if (Mode == NULL) {
471     Mode = (UINT8 *) "octet";
472   }
473 
474   //
475   // The header format of RRQ/WRQ packet is:
476   //
477   //   2 bytes     string    1 byte     string   1 byte
478   //   ------------------------------------------------
479   //  | Opcode |  Filename  |   0  |    Mode    |   0  |
480   //   ------------------------------------------------
481   //
482   // The common option format is:
483   //
484   //    string     1 byte     string   1 byte
485   //   ---------------------------------------
486   //  | OptionStr |   0  |  ValueStr  |   0   |
487   //   ---------------------------------------
488   //
489 
490   //
491   // Compute the size of new Mtftp6 packet.
492   //
493   FileNameLength = AsciiStrLen ((CHAR8 *) Token->Filename);
494   ModeLength     = AsciiStrLen ((CHAR8 *) Mode);
495   BufferLength   = (UINT32) FileNameLength + (UINT32) ModeLength + 4;
496 
497   for (Index = 0; Index < Token->OptionCount; Index++) {
498     OptionStrLength = AsciiStrLen ((CHAR8 *) Options[Index].OptionStr);
499     ValueStrLength  = AsciiStrLen ((CHAR8 *) Options[Index].ValueStr);
500     BufferLength   += (UINT32) OptionStrLength + (UINT32) ValueStrLength + 2;
501   }
502 
503   //
504   // Allocate a packet then copy the data.
505   //
506   if ((Nbuf = NetbufAlloc (BufferLength)) == NULL) {
507     return EFI_OUT_OF_RESOURCES;
508   }
509 
510   //
511   // Copy the opcode, filename and mode into packet.
512   //
513   Packet         = (EFI_MTFTP6_PACKET *) NetbufAllocSpace (Nbuf, BufferLength, FALSE);
514   ASSERT (Packet != NULL);
515 
516   Packet->OpCode = HTONS (Operation);
517   BufferLength  -= sizeof (Packet->OpCode);
518 
519   Cur            = Packet->Rrq.Filename;
520   Status         = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Token->Filename);
521   ASSERT_EFI_ERROR (Status);
522   BufferLength  -= (UINT32) (FileNameLength + 1);
523   Cur           += FileNameLength + 1;
524   Status         = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Mode);
525   ASSERT_EFI_ERROR (Status);
526   BufferLength  -= (UINT32) (ModeLength + 1);
527   Cur           += ModeLength + 1;
528 
529   //
530   // Copy all the extension options into the packet.
531   //
532   for (Index = 0; Index < Token->OptionCount; ++Index) {
533     OptionStrLength = AsciiStrLen ((CHAR8 *) Options[Index].OptionStr);
534     ValueStrLength  = AsciiStrLen ((CHAR8 *) Options[Index].ValueStr);
535 
536     Status          = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Options[Index].OptionStr);
537     ASSERT_EFI_ERROR (Status);
538     BufferLength   -= (UINT32) (OptionStrLength + 1);
539     Cur            += OptionStrLength + 1;
540 
541     Status          = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Options[Index].ValueStr);
542     ASSERT_EFI_ERROR (Status);
543     BufferLength   -= (UINT32) (ValueStrLength + 1);
544     Cur            += ValueStrLength + 1;
545 
546   }
547 
548   //
549   // Save the packet buf for retransmit
550   //
551   if (Instance->LastPacket != NULL) {
552     NetbufFree (Instance->LastPacket);
553   }
554 
555   Instance->LastPacket = Nbuf;
556   Instance->CurRetry   = 0;
557 
558   return Mtftp6TransmitPacket (Instance, Nbuf);
559 }
560 
561 
562 /**
563   Build and send an error packet.
564 
565   @param[in]  Instance               The pointer to the Mtftp6 instance.
566   @param[in]  ErrCode                The error code in the packet.
567   @param[in]  ErrInfo                The error message in the packet.
568 
569   @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory for the error packet.
570   @retval EFI_SUCCESS            The error packet is transmitted.
571   @retval Others                 Failed to transmit the packet.
572 
573 **/
574 EFI_STATUS
Mtftp6SendError(IN MTFTP6_INSTANCE * Instance,IN UINT16 ErrCode,IN UINT8 * ErrInfo)575 Mtftp6SendError (
576   IN MTFTP6_INSTANCE        *Instance,
577   IN UINT16                 ErrCode,
578   IN UINT8*                 ErrInfo
579   )
580 {
581   NET_BUF                   *Nbuf;
582   EFI_MTFTP6_PACKET         *TftpError;
583   UINT32                    Len;
584 
585   //
586   // Allocate a packet then copy the data.
587   //
588   Len  = (UINT32) (AsciiStrLen ((CHAR8 *) ErrInfo) + sizeof (EFI_MTFTP6_ERROR_HEADER));
589   Nbuf = NetbufAlloc (Len);
590 
591   if (Nbuf == NULL) {
592     return EFI_OUT_OF_RESOURCES;
593   }
594 
595   TftpError = (EFI_MTFTP6_PACKET *) NetbufAllocSpace (Nbuf, Len, FALSE);
596 
597   if (TftpError == NULL) {
598     NetbufFree (Nbuf);
599     return EFI_OUT_OF_RESOURCES;
600   }
601 
602   TftpError->OpCode          = HTONS (EFI_MTFTP6_OPCODE_ERROR);
603   TftpError->Error.ErrorCode = HTONS (ErrCode);
604 
605   AsciiStrCpyS ((CHAR8 *) TftpError->Error.ErrorMessage, AsciiStrLen ((CHAR8 *) ErrInfo) + 1 , (CHAR8 *) ErrInfo);
606 
607   //
608   // Save the packet buf for retransmit
609   //
610   if (Instance->LastPacket != NULL) {
611     NetbufFree (Instance->LastPacket);
612   }
613 
614   Instance->LastPacket = Nbuf;
615   Instance->CurRetry   = 0;
616 
617   return Mtftp6TransmitPacket (Instance, Nbuf);
618 }
619 
620 
621 /**
622   The callback function called when the packet is transmitted.
623 
624   @param[in]  Packet                 The pointer to the packet.
625   @param[in]  UdpEpt                 The pointer to the Udp6 access point.
626   @param[in]  IoStatus               The result of the transmission.
627   @param[in]  Context                The pointer to the context.
628 
629 **/
630 VOID
631 EFIAPI
Mtftp6OnPacketSent(IN NET_BUF * Packet,IN UDP_END_POINT * UdpEpt,IN EFI_STATUS IoStatus,IN VOID * Context)632 Mtftp6OnPacketSent (
633   IN NET_BUF                   *Packet,
634   IN UDP_END_POINT             *UdpEpt,
635   IN EFI_STATUS                IoStatus,
636   IN VOID                      *Context
637   )
638 {
639   NetbufFree (Packet);
640   *(BOOLEAN *) Context = TRUE;
641 }
642 
643 
644 /**
645   Send the packet for the Mtftp6 instance.
646 
647   @param[in]  Instance               The pointer to the Mtftp6 instance.
648   @param[in]  Packet                 The pointer to the packet to be sent.
649 
650   @retval EFI_SUCCESS            The packet was sent out
651   @retval Others                 Failed to transmit the packet.
652 
653 **/
654 EFI_STATUS
Mtftp6TransmitPacket(IN MTFTP6_INSTANCE * Instance,IN NET_BUF * Packet)655 Mtftp6TransmitPacket (
656   IN MTFTP6_INSTANCE        *Instance,
657   IN NET_BUF                *Packet
658   )
659 {
660   EFI_UDP6_PROTOCOL         *Udp6;
661   EFI_UDP6_CONFIG_DATA      Udp6CfgData;
662   EFI_STATUS                Status;
663   UINT16                    *Temp;
664   UINT16                    Value;
665   UINT16                    OpCode;
666 
667   ZeroMem (&Udp6CfgData, sizeof(EFI_UDP6_CONFIG_DATA));
668   Udp6 = Instance->UdpIo->Protocol.Udp6;
669 
670   //
671   // Set the live time of the packet.
672   //
673   Instance->PacketToLive = Instance->IsMaster ? Instance->Timeout : (Instance->Timeout * 2);
674 
675   Temp   = (UINT16 *) NetbufGetByte (Packet, 0, NULL);
676   ASSERT (Temp != NULL);
677 
678   Value  = *Temp;
679   OpCode = NTOHS (Value);
680 
681   if (OpCode == EFI_MTFTP6_OPCODE_RRQ || OpCode == EFI_MTFTP6_OPCODE_DIR || OpCode == EFI_MTFTP6_OPCODE_WRQ) {
682     //
683     // For the Rrq, Dir, Wrq requests of the operation, configure the Udp6Io as
684     // (serverip, 69, localip, localport) to send.
685     // Usually local address and local port are both default as zero.
686     //
687     Status = Udp6->Configure (Udp6, NULL);
688 
689     if (EFI_ERROR (Status)) {
690       return Status;
691     }
692 
693     Status = Mtftp6ConfigUdpIo (
694                Instance->UdpIo,
695                &Instance->ServerIp,
696                Instance->ServerCmdPort,
697                &Instance->Config->StationIp,
698                Instance->Config->LocalPort
699                );
700 
701     if (EFI_ERROR (Status)) {
702       return Status;
703     }
704 
705     //
706     // Get the current local address and port by get Udp6 mode data.
707     //
708     Status = Udp6->GetModeData (Udp6, &Udp6CfgData, NULL, NULL, NULL);
709     if (EFI_ERROR (Status)) {
710       return Status;
711     }
712 
713     NET_GET_REF (Packet);
714 
715     Instance->IsTransmitted = FALSE;
716 
717     Status = UdpIoSendDatagram (
718                Instance->UdpIo,
719                Packet,
720                NULL,
721                NULL,
722                Mtftp6OnPacketSent,
723                &Instance->IsTransmitted
724                );
725 
726     if (EFI_ERROR (Status)) {
727       NET_PUT_REF (Packet);
728       return Status;
729     }
730 
731     //
732     // Poll till the packet sent out from the ip6 queue.
733     //
734     gBS->RestoreTPL (Instance->OldTpl);
735 
736     while (!Instance->IsTransmitted) {
737       Udp6->Poll (Udp6);
738     }
739 
740     Instance->OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
741 
742     //
743     // For the subsequent exchange of such requests, reconfigure the Udp6Io as
744     // (serverip, 0, localip, localport) to receive.
745     // Currently local address and local port are specified by Udp6 mode data.
746     //
747     Status = Udp6->Configure (Udp6, NULL);
748 
749     if (EFI_ERROR (Status)) {
750       return Status;
751     }
752 
753     Status = Mtftp6ConfigUdpIo (
754                Instance->UdpIo,
755                &Instance->ServerIp,
756                Instance->ServerDataPort,
757                &Udp6CfgData.StationAddress,
758                Udp6CfgData.StationPort
759                );
760   } else {
761     //
762     // For the data exchange, configure the Udp6Io as (serverip, dataport,
763     // localip, localport) to send/receive.
764     // Currently local address and local port are specified by Udp6 mode data.
765     //
766     Status = Udp6->GetModeData (Udp6, &Udp6CfgData, NULL, NULL, NULL);
767     if (EFI_ERROR (Status)) {
768       return Status;
769     }
770 
771     if (Udp6CfgData.RemotePort != Instance->ServerDataPort) {
772 
773       Status = Udp6->Configure (Udp6, NULL);
774 
775       if (EFI_ERROR (Status)) {
776         return Status;
777       }
778 
779       Status = Mtftp6ConfigUdpIo (
780                  Instance->UdpIo,
781                  &Instance->ServerIp,
782                  Instance->ServerDataPort,
783                  &Udp6CfgData.StationAddress,
784                  Udp6CfgData.StationPort
785                  );
786 
787       if (EFI_ERROR (Status)) {
788         return Status;
789       }
790     }
791 
792     NET_GET_REF (Packet);
793 
794     Instance->IsTransmitted = FALSE;
795 
796     Status = UdpIoSendDatagram (
797                Instance->UdpIo,
798                Packet,
799                NULL,
800                NULL,
801                Mtftp6OnPacketSent,
802                &Instance->IsTransmitted
803                );
804 
805     if (EFI_ERROR (Status)) {
806       NET_PUT_REF (Packet);
807     }
808 
809     //
810     // Poll till the packet sent out from the ip6 queue.
811     //
812     gBS->RestoreTPL (Instance->OldTpl);
813 
814     while (!Instance->IsTransmitted) {
815       Udp6->Poll (Udp6);
816     }
817 
818     Instance->OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
819   }
820 
821   return Status;
822 }
823 
824 
825 /**
826   Check packet for GetInfo callback routine.
827 
828   GetInfo is implemented with EfiMtftp6ReadFile. It's used to inspect
829   the first packet from server, then abort the session.
830 
831   @param[in]  This                   The pointer to the Mtftp6 protocol.
832   @param[in]  Token                  The pointer to the Mtftp6 token.
833   @param[in]  PacketLen              The length of the packet.
834   @param[in]  Packet                 The pointer to the received packet.
835 
836   @retval EFI_ABORTED            Abort the Mtftp6 operation.
837 
838 **/
839 EFI_STATUS
840 EFIAPI
Mtftp6CheckPacket(IN EFI_MTFTP6_PROTOCOL * This,IN EFI_MTFTP6_TOKEN * Token,IN UINT16 PacketLen,IN EFI_MTFTP6_PACKET * Packet)841 Mtftp6CheckPacket (
842   IN EFI_MTFTP6_PROTOCOL    *This,
843   IN EFI_MTFTP6_TOKEN       *Token,
844   IN UINT16                 PacketLen,
845   IN EFI_MTFTP6_PACKET      *Packet
846   )
847 {
848   MTFTP6_GETINFO_CONTEXT    *Context;
849   UINT16                    OpCode;
850 
851   Context = (MTFTP6_GETINFO_CONTEXT *) Token->Context;
852   OpCode  = NTOHS (Packet->OpCode);
853 
854   //
855   // Set the GetInfo's return status according to the OpCode.
856   //
857   switch (OpCode) {
858   case EFI_MTFTP6_OPCODE_ERROR:
859     Context->Status = EFI_TFTP_ERROR;
860     break;
861 
862   case EFI_MTFTP6_OPCODE_OACK:
863     Context->Status = EFI_SUCCESS;
864     break;
865 
866   default:
867     Context->Status = EFI_PROTOCOL_ERROR;
868   }
869 
870   //
871   // Allocate buffer then copy the packet over. Use gBS->AllocatePool
872   // in case NetAllocatePool will implements something tricky.
873   //
874   *(Context->Packet) = AllocateZeroPool (PacketLen);
875 
876   if (*(Context->Packet) == NULL) {
877     Context->Status = EFI_OUT_OF_RESOURCES;
878     return EFI_ABORTED;
879   }
880 
881   *(Context->PacketLen) = PacketLen;
882   CopyMem (*(Context->Packet), Packet, PacketLen);
883 
884   return EFI_ABORTED;
885 }
886 
887 
888 /**
889   Clean up the current Mtftp6 operation.
890 
891   @param[in]  Instance               The pointer to the Mtftp6 instance.
892   @param[in]  Result                 The result to be returned to the user.
893 
894 **/
895 VOID
Mtftp6OperationClean(IN MTFTP6_INSTANCE * Instance,IN EFI_STATUS Result)896 Mtftp6OperationClean (
897   IN MTFTP6_INSTANCE        *Instance,
898   IN EFI_STATUS             Result
899   )
900 {
901   LIST_ENTRY                *Entry;
902   LIST_ENTRY                *Next;
903   MTFTP6_BLOCK_RANGE        *Block;
904 
905   //
906   // Clean up the current token and event.
907   //
908   if (Instance->Token != NULL) {
909     Instance->Token->Status = Result;
910     if (Instance->Token->Event != NULL) {
911       gBS->SignalEvent (Instance->Token->Event);
912     }
913     Instance->Token = NULL;
914   }
915 
916   //
917   // Clean up the corresponding Udp6Io.
918   //
919   if (Instance->UdpIo != NULL) {
920     UdpIoCleanIo (Instance->UdpIo);
921   }
922 
923   if (Instance->McastUdpIo != NULL) {
924     gBS->CloseProtocol (
925            Instance->McastUdpIo->UdpHandle,
926            &gEfiUdp6ProtocolGuid,
927            Instance->McastUdpIo->Image,
928            Instance->Handle
929            );
930     UdpIoFreeIo (Instance->McastUdpIo);
931     Instance->McastUdpIo = NULL;
932   }
933 
934   //
935   // Clean up the stored last packet.
936   //
937   if (Instance->LastPacket != NULL) {
938     NetbufFree (Instance->LastPacket);
939     Instance->LastPacket = NULL;
940   }
941 
942   NET_LIST_FOR_EACH_SAFE (Entry, Next, &Instance->BlkList) {
943     Block = NET_LIST_USER_STRUCT (Entry, MTFTP6_BLOCK_RANGE, Link);
944     RemoveEntryList (Entry);
945     FreePool (Block);
946   }
947 
948   //
949   // Reinitialize the corresponding fields of the Mtftp6 operation.
950   //
951   ZeroMem (&Instance->ExtInfo, sizeof (MTFTP6_EXT_OPTION_INFO));
952   ZeroMem (&Instance->ServerIp, sizeof (EFI_IPv6_ADDRESS));
953   ZeroMem (&Instance->McastIp, sizeof (EFI_IPv6_ADDRESS));
954 
955   Instance->ServerCmdPort  = 0;
956   Instance->ServerDataPort = 0;
957   Instance->McastPort      = 0;
958   Instance->BlkSize        = 0;
959   Instance->LastBlk        = 0;
960   Instance->PacketToLive   = 0;
961   Instance->MaxRetry       = 0;
962   Instance->CurRetry       = 0;
963   Instance->Timeout        = 0;
964   Instance->IsMaster       = TRUE;
965 }
966 
967 
968 /**
969   Start the Mtftp6 instance to perform the operation, such as read file,
970   write file, and read directory.
971 
972   @param[in]  This                   The MTFTP session.
973   @param[in]  Token                  The token than encapsues the user's request.
974   @param[in]  OpCode                 The operation to perform.
975 
976   @retval EFI_INVALID_PARAMETER  Some of the parameters are invalid.
977   @retval EFI_NOT_STARTED        The MTFTP session hasn't been configured.
978   @retval EFI_ALREADY_STARTED    There is pending operation for the session.
979   @retval EFI_SUCCESS            The operation is successfully started.
980 
981 **/
982 EFI_STATUS
Mtftp6OperationStart(IN EFI_MTFTP6_PROTOCOL * This,IN EFI_MTFTP6_TOKEN * Token,IN UINT16 OpCode)983 Mtftp6OperationStart (
984   IN EFI_MTFTP6_PROTOCOL    *This,
985   IN EFI_MTFTP6_TOKEN       *Token,
986   IN UINT16                 OpCode
987   )
988 {
989   MTFTP6_INSTANCE           *Instance;
990   EFI_STATUS                Status;
991 
992   if (This == NULL ||
993       Token == NULL ||
994       Token->Filename == NULL ||
995       (Token->OptionCount != 0 && Token->OptionList == NULL) ||
996       (Token->OverrideData != NULL && !NetIp6IsValidUnicast (&Token->OverrideData->ServerIp))
997       ) {
998     return EFI_INVALID_PARAMETER;
999   }
1000 
1001   //
1002   // At least define one method to collect the data for download.
1003   //
1004   if ((OpCode == EFI_MTFTP6_OPCODE_RRQ || OpCode == EFI_MTFTP6_OPCODE_DIR) &&
1005       Token->Buffer == NULL &&
1006       Token->CheckPacket == NULL
1007       ) {
1008     return EFI_INVALID_PARAMETER;
1009   }
1010 
1011   //
1012   // At least define one method to provide the data for upload.
1013   //
1014   if (OpCode == EFI_MTFTP6_OPCODE_WRQ && Token->Buffer == NULL && Token->PacketNeeded == NULL) {
1015     return EFI_INVALID_PARAMETER;
1016   }
1017 
1018   Instance = MTFTP6_INSTANCE_FROM_THIS (This);
1019 
1020   if (Instance->Config == NULL) {
1021     return EFI_NOT_STARTED;
1022   }
1023 
1024   if (Instance->Token != NULL) {
1025     return EFI_ACCESS_DENIED;
1026   }
1027 
1028   Status           = EFI_SUCCESS;
1029   Instance->OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
1030 
1031   //
1032   // Parse the extension options in the request packet.
1033   //
1034   if (Token->OptionCount != 0) {
1035 
1036     Status = Mtftp6ParseExtensionOption (
1037                Token->OptionList,
1038                Token->OptionCount,
1039                TRUE,
1040                &Instance->ExtInfo
1041                );
1042 
1043     if (EFI_ERROR (Status)) {
1044       goto ON_ERROR;
1045     }
1046   }
1047 
1048   //
1049   // Initialize runtime data from config data or override data.
1050   //
1051   Instance->Token           = Token;
1052   Instance->ServerCmdPort   = Instance->Config->InitialServerPort;
1053   Instance->ServerDataPort  = 0;
1054   Instance->MaxRetry        = Instance->Config->TryCount;
1055   Instance->Timeout         = Instance->Config->TimeoutValue;
1056   Instance->IsMaster        = TRUE;
1057 
1058   CopyMem (
1059     &Instance->ServerIp,
1060     &Instance->Config->ServerIp,
1061     sizeof (EFI_IPv6_ADDRESS)
1062     );
1063 
1064   if (Token->OverrideData != NULL) {
1065     Instance->ServerCmdPort = Token->OverrideData->ServerPort;
1066     Instance->MaxRetry      = Token->OverrideData->TryCount;
1067     Instance->Timeout       = Token->OverrideData->TimeoutValue;
1068 
1069     CopyMem (
1070       &Instance->ServerIp,
1071       &Token->OverrideData->ServerIp,
1072       sizeof (EFI_IPv6_ADDRESS)
1073       );
1074   }
1075 
1076   //
1077   // Set default value for undefined parameters.
1078   //
1079   if (Instance->ServerCmdPort == 0) {
1080     Instance->ServerCmdPort = MTFTP6_DEFAULT_SERVER_CMD_PORT;
1081   }
1082   if (Instance->BlkSize == 0) {
1083     Instance->BlkSize = MTFTP6_DEFAULT_BLK_SIZE;
1084   }
1085   if (Instance->MaxRetry == 0) {
1086     Instance->MaxRetry = MTFTP6_DEFAULT_MAX_RETRY;
1087   }
1088   if (Instance->Timeout == 0) {
1089     Instance->Timeout = MTFTP6_DEFAULT_TIMEOUT;
1090   }
1091 
1092   Token->Status = EFI_NOT_READY;
1093 
1094   //
1095   // Switch the routines by the operation code.
1096   //
1097   switch (OpCode) {
1098   case EFI_MTFTP6_OPCODE_RRQ:
1099     Status = Mtftp6RrqStart (Instance, OpCode);
1100     break;
1101 
1102   case EFI_MTFTP6_OPCODE_DIR:
1103     Status = Mtftp6RrqStart (Instance, OpCode);
1104     break;
1105 
1106   case EFI_MTFTP6_OPCODE_WRQ:
1107     Status = Mtftp6WrqStart (Instance, OpCode);
1108     break;
1109 
1110   default:
1111     Status = EFI_DEVICE_ERROR;
1112     goto ON_ERROR;
1113   }
1114 
1115   if (EFI_ERROR (Status)) {
1116     goto ON_ERROR;
1117   }
1118 
1119   //
1120   // Return immediately for asynchronous or poll the instance for synchronous.
1121   //
1122   gBS->RestoreTPL (Instance->OldTpl);
1123 
1124   if (Token->Event == NULL) {
1125     while (Token->Status == EFI_NOT_READY) {
1126       This->Poll (This);
1127     }
1128     return Token->Status;
1129   }
1130 
1131   return EFI_SUCCESS;
1132 
1133 ON_ERROR:
1134 
1135   Mtftp6OperationClean (Instance, Status);
1136   gBS->RestoreTPL (Instance->OldTpl);
1137 
1138   return Status;
1139 }
1140 
1141 
1142 /**
1143   The timer ticking routine for the Mtftp6 instance.
1144 
1145   @param[in]  Event                  The pointer to the ticking event.
1146   @param[in]  Context                The pointer to the context.
1147 
1148 **/
1149 VOID
1150 EFIAPI
Mtftp6OnTimerTick(IN EFI_EVENT Event,IN VOID * Context)1151 Mtftp6OnTimerTick (
1152   IN EFI_EVENT              Event,
1153   IN VOID                   *Context
1154   )
1155 {
1156   MTFTP6_SERVICE            *Service;
1157   MTFTP6_INSTANCE           *Instance;
1158   LIST_ENTRY                *Entry;
1159   LIST_ENTRY                *Next;
1160   EFI_MTFTP6_TOKEN          *Token;
1161   EFI_STATUS                Status;
1162 
1163   Service = (MTFTP6_SERVICE *) Context;
1164 
1165   //
1166   // Iterate through all the children of the Mtftp service instance. Time
1167   // out the packet. If maximum retries reached, clean the session up.
1168   //
1169   NET_LIST_FOR_EACH_SAFE (Entry, Next, &Service->Children) {
1170 
1171     Instance = NET_LIST_USER_STRUCT (Entry, MTFTP6_INSTANCE, Link);
1172 
1173     if (Instance->Token == NULL) {
1174       continue;
1175     }
1176 
1177     if (Instance->PacketToLive > 0) {
1178       Instance->PacketToLive--;
1179       continue;
1180     }
1181 
1182     Instance->CurRetry++;
1183     Token = Instance->Token;
1184 
1185     if (Token->TimeoutCallback != NULL) {
1186       //
1187       // Call the timeout callback routine if has.
1188       //
1189       Status = Token->TimeoutCallback (&Instance->Mtftp6, Token);
1190 
1191       if (EFI_ERROR (Status)) {
1192         Mtftp6SendError (
1193            Instance,
1194            EFI_MTFTP6_ERRORCODE_REQUEST_DENIED,
1195            (UINT8 *) "User aborted the transfer in time out"
1196            );
1197         Mtftp6OperationClean (Instance, EFI_ABORTED);
1198         continue;
1199       }
1200     }
1201 
1202     //
1203     // Retransmit the packet if haven't reach the maxmium retry count,
1204     // otherwise exit the transfer.
1205     //
1206     if (Instance->CurRetry < Instance->MaxRetry) {
1207       Mtftp6TransmitPacket (Instance, Instance->LastPacket);
1208     } else {
1209       Mtftp6OperationClean (Instance, EFI_TIMEOUT);
1210       continue;
1211     }
1212   }
1213 }
1214