1 /** @file
2   This library is used to share code between UEFI network stack modules.
3   It provides the helper routines to access TCP service.
4 
5 Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7 
8 **/
9 
10 #include <Uefi.h>
11 
12 #include <Library/TcpIoLib.h>
13 #include <Library/BaseLib.h>
14 #include <Library/DebugLib.h>
15 #include <Library/UefiBootServicesTableLib.h>
16 #include <Library/MemoryAllocationLib.h>
17 #include <Library/BaseMemoryLib.h>
18 
19 /**
20   The common notify function associated with various TcpIo events.
21 
22   @param[in]  Event   The event signaled.
23   @param[in]  Context The context.
24 
25 **/
26 VOID
27 EFIAPI
TcpIoCommonNotify(IN EFI_EVENT Event,IN VOID * Context)28 TcpIoCommonNotify (
29   IN EFI_EVENT  Event,
30   IN VOID       *Context
31   )
32 {
33   if ((Event == NULL) || (Context == NULL)) {
34     return ;
35   }
36 
37   *((BOOLEAN *) Context) = TRUE;
38 }
39 
40 /**
41   The internal function for delay configuring TCP6 when IP6 driver is still in DAD.
42 
43   @param[in]  Tcp6               The EFI_TCP6_PROTOCOL protocol instance.
44   @param[in]  Tcp6ConfigData     The Tcp6 configuration data.
45 
46   @retval EFI_SUCCESS            The operational settings successfully
47                                  completed.
48   @retval EFI_INVALID_PARAMETER  One or more parameters are invalid.
49   @retval Others                 Failed to finish the operation.
50 
51 **/
52 EFI_STATUS
TcpIoGetMapping(IN EFI_TCP6_PROTOCOL * Tcp6,IN EFI_TCP6_CONFIG_DATA * Tcp6ConfigData)53 TcpIoGetMapping (
54   IN EFI_TCP6_PROTOCOL    *Tcp6,
55   IN EFI_TCP6_CONFIG_DATA *Tcp6ConfigData
56   )
57 {
58   EFI_STATUS              Status;
59   EFI_EVENT               Event;
60 
61   if ((Tcp6 == NULL) || (Tcp6ConfigData == NULL)) {
62     return EFI_INVALID_PARAMETER;
63   }
64 
65   Event  = NULL;
66   Status = gBS->CreateEvent (
67                   EVT_TIMER,
68                   TPL_CALLBACK,
69                   NULL,
70                   NULL,
71                   &Event
72                   );
73   if (EFI_ERROR (Status)) {
74     goto ON_EXIT;
75   }
76 
77   Status = gBS->SetTimer (
78                   Event,
79                   TimerRelative,
80                   TCP_GET_MAPPING_TIMEOUT
81                   );
82 
83   if (EFI_ERROR (Status)) {
84     goto ON_EXIT;
85   }
86 
87   while (EFI_ERROR (gBS->CheckEvent (Event))) {
88 
89     Tcp6->Poll (Tcp6);
90 
91     Status = Tcp6->Configure (Tcp6, Tcp6ConfigData);
92 
93     if (!EFI_ERROR (Status)) {
94       break;
95     }
96   }
97 
98 ON_EXIT:
99 
100   if (Event != NULL) {
101     gBS->CloseEvent (Event);
102   }
103 
104   return Status;
105 }
106 
107 /**
108   Create a TCP socket with the specified configuration data.
109 
110   @param[in]  Image      The handle of the driver image.
111   @param[in]  Controller The handle of the controller.
112   @param[in]  TcpVersion The version of Tcp, TCP_VERSION_4 or TCP_VERSION_6.
113   @param[in]  ConfigData The Tcp configuration data.
114   @param[out] TcpIo      The TcpIo.
115 
116   @retval EFI_SUCCESS            The TCP socket is created and configured.
117   @retval EFI_INVALID_PARAMETER  One or more parameters are invalid.
118   @retval EFI_UNSUPPORTED        One or more of the control options are not
119                                  supported in the implementation.
120   @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory.
121   @retval Others                 Failed to create the TCP socket or configure it.
122 
123 **/
124 EFI_STATUS
125 EFIAPI
TcpIoCreateSocket(IN EFI_HANDLE Image,IN EFI_HANDLE Controller,IN UINT8 TcpVersion,IN TCP_IO_CONFIG_DATA * ConfigData,OUT TCP_IO * TcpIo)126 TcpIoCreateSocket (
127   IN EFI_HANDLE             Image,
128   IN EFI_HANDLE             Controller,
129   IN UINT8                  TcpVersion,
130   IN TCP_IO_CONFIG_DATA     *ConfigData,
131   OUT TCP_IO                *TcpIo
132   )
133 {
134   EFI_STATUS                Status;
135   EFI_EVENT                 Event;
136   EFI_GUID                  *ServiceBindingGuid;
137   EFI_GUID                  *ProtocolGuid;
138   VOID                      **Interface;
139   EFI_TCP4_OPTION           ControlOption;
140   EFI_TCP4_CONFIG_DATA      Tcp4ConfigData;
141   EFI_TCP4_ACCESS_POINT     *AccessPoint4;
142   EFI_TCP4_PROTOCOL         *Tcp4;
143   EFI_TCP6_CONFIG_DATA      Tcp6ConfigData;
144   EFI_TCP6_ACCESS_POINT     *AccessPoint6;
145   EFI_TCP6_PROTOCOL         *Tcp6;
146   EFI_TCP4_RECEIVE_DATA     *RxData;
147 
148   if ((Image == NULL) || (Controller == NULL) || (ConfigData == NULL) || (TcpIo == NULL)) {
149     return EFI_INVALID_PARAMETER;
150   }
151 
152   Tcp4 = NULL;
153   Tcp6 = NULL;
154 
155   ZeroMem (TcpIo, sizeof (TCP_IO));
156 
157   if (TcpVersion == TCP_VERSION_4) {
158     ServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid;
159     ProtocolGuid       = &gEfiTcp4ProtocolGuid;
160     Interface          = (VOID **) (&TcpIo->Tcp.Tcp4);
161   } else if (TcpVersion == TCP_VERSION_6) {
162     ServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid;
163     ProtocolGuid       = &gEfiTcp6ProtocolGuid;
164     Interface          = (VOID **) (&TcpIo->Tcp.Tcp6);
165   } else {
166     return EFI_UNSUPPORTED;
167   }
168 
169   TcpIo->TcpVersion = TcpVersion;
170 
171   //
172   // Create the TCP child instance and get the TCP protocol.
173   //
174   Status = NetLibCreateServiceChild (
175              Controller,
176              Image,
177              ServiceBindingGuid,
178              &TcpIo->Handle
179              );
180   if (EFI_ERROR (Status)) {
181     return Status;
182   }
183 
184   Status = gBS->OpenProtocol (
185                   TcpIo->Handle,
186                   ProtocolGuid,
187                   Interface,
188                   Image,
189                   Controller,
190                   EFI_OPEN_PROTOCOL_BY_DRIVER
191                   );
192   if (EFI_ERROR (Status) || (*Interface == NULL)) {
193     goto ON_ERROR;
194   }
195 
196   if (TcpVersion == TCP_VERSION_4) {
197     Tcp4             = TcpIo->Tcp.Tcp4;
198   } else {
199     Tcp6             = TcpIo->Tcp.Tcp6;
200   }
201 
202   TcpIo->Image       = Image;
203   TcpIo->Controller  = Controller;
204 
205   //
206   // Set the configuration parameters.
207   //
208   ControlOption.ReceiveBufferSize       = 0x200000;
209   ControlOption.SendBufferSize          = 0x200000;
210   ControlOption.MaxSynBackLog           = 0;
211   ControlOption.ConnectionTimeout       = 0;
212   ControlOption.DataRetries             = 6;
213   ControlOption.FinTimeout              = 0;
214   ControlOption.TimeWaitTimeout         = 0;
215   ControlOption.KeepAliveProbes         = 4;
216   ControlOption.KeepAliveTime           = 0;
217   ControlOption.KeepAliveInterval       = 0;
218   ControlOption.EnableNagle             = FALSE;
219   ControlOption.EnableTimeStamp         = FALSE;
220   ControlOption.EnableWindowScaling     = TRUE;
221   ControlOption.EnableSelectiveAck      = FALSE;
222   ControlOption.EnablePathMtuDiscovery  = FALSE;
223 
224   if (TcpVersion == TCP_VERSION_4) {
225     Tcp4ConfigData.TypeOfService        = 8;
226     Tcp4ConfigData.TimeToLive           = 255;
227     Tcp4ConfigData.ControlOption        = &ControlOption;
228 
229     AccessPoint4                        = &Tcp4ConfigData.AccessPoint;
230 
231     ZeroMem (AccessPoint4, sizeof (EFI_TCP4_ACCESS_POINT));
232     AccessPoint4->StationPort           = ConfigData->Tcp4IoConfigData.StationPort;
233     AccessPoint4->RemotePort            = ConfigData->Tcp4IoConfigData.RemotePort;
234     AccessPoint4->ActiveFlag            = ConfigData->Tcp4IoConfigData.ActiveFlag;
235 
236     CopyMem (
237       &AccessPoint4->StationAddress,
238       &ConfigData->Tcp4IoConfigData.LocalIp,
239       sizeof (EFI_IPv4_ADDRESS)
240       );
241     CopyMem (
242       &AccessPoint4->SubnetMask,
243       &ConfigData->Tcp4IoConfigData.SubnetMask,
244       sizeof (EFI_IPv4_ADDRESS)
245       );
246     CopyMem (
247       &AccessPoint4->RemoteAddress,
248       &ConfigData->Tcp4IoConfigData.RemoteIp,
249       sizeof (EFI_IPv4_ADDRESS)
250       );
251 
252     ASSERT (Tcp4 != NULL);
253 
254     //
255     // Configure the TCP4 protocol.
256     //
257     Status = Tcp4->Configure (Tcp4, &Tcp4ConfigData);
258     if (EFI_ERROR (Status)) {
259       goto ON_ERROR;
260     }
261 
262     if (!EFI_IP4_EQUAL (&ConfigData->Tcp4IoConfigData.Gateway, &mZeroIp4Addr)) {
263       //
264       // The gateway is not zero. Add the default route manually.
265       //
266       Status = Tcp4->Routes (
267                        Tcp4,
268                        FALSE,
269                        &mZeroIp4Addr,
270                        &mZeroIp4Addr,
271                        &ConfigData->Tcp4IoConfigData.Gateway
272                        );
273       if (EFI_ERROR (Status)) {
274         goto ON_ERROR;
275       }
276     }
277   } else {
278     Tcp6ConfigData.TrafficClass         = 0;
279     Tcp6ConfigData.HopLimit             = 255;
280     Tcp6ConfigData.ControlOption        = (EFI_TCP6_OPTION *) &ControlOption;
281 
282     AccessPoint6                        = &Tcp6ConfigData.AccessPoint;
283 
284     ZeroMem (AccessPoint6, sizeof (EFI_TCP6_ACCESS_POINT));
285     AccessPoint6->StationPort           = ConfigData->Tcp6IoConfigData.StationPort;
286     AccessPoint6->RemotePort            = ConfigData->Tcp6IoConfigData.RemotePort;
287     AccessPoint6->ActiveFlag            = ConfigData->Tcp6IoConfigData.ActiveFlag;
288 
289     IP6_COPY_ADDRESS (&AccessPoint6->RemoteAddress, &ConfigData->Tcp6IoConfigData.RemoteIp);
290 
291 
292     ASSERT (Tcp6 != NULL);
293     //
294     // Configure the TCP6 protocol.
295     //
296     Status = Tcp6->Configure (Tcp6, &Tcp6ConfigData);
297     if (Status == EFI_NO_MAPPING) {
298       Status = TcpIoGetMapping (Tcp6, &Tcp6ConfigData);
299     }
300 
301     if (EFI_ERROR (Status)) {
302       goto ON_ERROR;
303     }
304   }
305 
306   //
307   // Create events for variuos asynchronous operations.
308   //
309   Status = gBS->CreateEvent (
310                   EVT_NOTIFY_SIGNAL,
311                   TPL_NOTIFY,
312                   TcpIoCommonNotify,
313                   &TcpIo->IsConnDone,
314                   &Event
315                   );
316   if (EFI_ERROR (Status)) {
317     goto ON_ERROR;
318   }
319 
320   TcpIo->ConnToken.Tcp4Token.CompletionToken.Event = Event;
321 
322   Status = gBS->CreateEvent (
323                   EVT_NOTIFY_SIGNAL,
324                   TPL_NOTIFY,
325                   TcpIoCommonNotify,
326                   &TcpIo->IsListenDone,
327                   &Event
328                   );
329   if (EFI_ERROR (Status)) {
330     goto ON_ERROR;
331   }
332 
333   TcpIo->ListenToken.Tcp4Token.CompletionToken.Event = Event;
334 
335   Status = gBS->CreateEvent (
336                   EVT_NOTIFY_SIGNAL,
337                   TPL_NOTIFY,
338                   TcpIoCommonNotify,
339                   &TcpIo->IsTxDone,
340                   &Event
341                   );
342   if (EFI_ERROR (Status)) {
343     goto ON_ERROR;
344   }
345 
346   TcpIo->TxToken.Tcp4Token.CompletionToken.Event = Event;
347 
348 
349   Status = gBS->CreateEvent (
350                   EVT_NOTIFY_SIGNAL,
351                   TPL_NOTIFY,
352                   TcpIoCommonNotify,
353                   &TcpIo->IsRxDone,
354                   &Event
355                   );
356   if (EFI_ERROR (Status)) {
357     goto ON_ERROR;
358   }
359 
360   TcpIo->RxToken.Tcp4Token.CompletionToken.Event = Event;
361 
362   RxData = (EFI_TCP4_RECEIVE_DATA *) AllocateZeroPool (sizeof (EFI_TCP4_RECEIVE_DATA));
363   if (RxData == NULL) {
364     Status = EFI_OUT_OF_RESOURCES;
365     goto ON_ERROR;
366   }
367 
368   TcpIo->RxToken.Tcp4Token.Packet.RxData = RxData;
369 
370   Status = gBS->CreateEvent (
371                   EVT_NOTIFY_SIGNAL,
372                   TPL_NOTIFY,
373                   TcpIoCommonNotify,
374                   &TcpIo->IsCloseDone,
375                   &Event
376                   );
377   if (EFI_ERROR (Status)) {
378     goto ON_ERROR;
379   }
380 
381   TcpIo->CloseToken.Tcp4Token.CompletionToken.Event = Event;
382 
383 
384   return EFI_SUCCESS;
385 
386 ON_ERROR:
387 
388   TcpIoDestroySocket (TcpIo);
389 
390   return Status;
391 }
392 
393 /**
394   Destroy the socket.
395 
396   @param[in]  TcpIo The TcpIo which wraps the socket to be destroyed.
397 
398 **/
399 VOID
400 EFIAPI
TcpIoDestroySocket(IN TCP_IO * TcpIo)401 TcpIoDestroySocket (
402   IN TCP_IO                 *TcpIo
403   )
404 {
405   EFI_EVENT                 Event;
406   EFI_TCP4_PROTOCOL         *Tcp4;
407   EFI_TCP6_PROTOCOL         *Tcp6;
408   UINT8                     TcpVersion;
409   EFI_GUID                  *ServiceBindingGuid;
410   EFI_GUID                  *ProtocolGuid;
411   EFI_HANDLE                ChildHandle;
412 
413   if (TcpIo == NULL) {
414     return ;
415   }
416 
417   TcpVersion = TcpIo->TcpVersion;
418 
419   if ((TcpVersion != TCP_VERSION_4) && (TcpVersion != TCP_VERSION_6)) {
420     return ;
421   }
422 
423   Event = TcpIo->ConnToken.Tcp4Token.CompletionToken.Event;
424 
425   if (Event != NULL) {
426     gBS->CloseEvent (Event);
427   }
428 
429   Event = TcpIo->ListenToken.Tcp4Token.CompletionToken.Event;
430 
431   if (Event != NULL) {
432     gBS->CloseEvent (Event);
433   }
434 
435   Event = TcpIo->TxToken.Tcp4Token.CompletionToken.Event;
436 
437   if (Event != NULL) {
438     gBS->CloseEvent (Event);
439   }
440 
441   Event = TcpIo->RxToken.Tcp4Token.CompletionToken.Event;
442 
443   if (Event != NULL) {
444     gBS->CloseEvent (Event);
445   }
446 
447   Event = TcpIo->CloseToken.Tcp4Token.CompletionToken.Event;
448 
449   if (Event != NULL) {
450     gBS->CloseEvent (Event);
451   }
452 
453   if (TcpIo->RxToken.Tcp4Token.Packet.RxData != NULL) {
454     FreePool (TcpIo->RxToken.Tcp4Token.Packet.RxData);
455   }
456 
457   Tcp4 = NULL;
458   Tcp6 = NULL;
459 
460 
461   if (TcpVersion == TCP_VERSION_4) {
462     ServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid;
463     ProtocolGuid       = &gEfiTcp4ProtocolGuid;
464     Tcp4 = TcpIo->Tcp.Tcp4;
465     if (Tcp4 != NULL) {
466       Tcp4->Configure (Tcp4, NULL);
467     }
468   } else {
469     ServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid;
470     ProtocolGuid       = &gEfiTcp6ProtocolGuid;
471     Tcp6 = TcpIo->Tcp.Tcp6;
472     if (Tcp6 != NULL) {
473       Tcp6->Configure (Tcp6, NULL);
474     }
475   }
476 
477   if ((Tcp4 != NULL) || (Tcp6 != NULL)) {
478 
479     gBS->CloseProtocol (
480            TcpIo->Handle,
481            ProtocolGuid,
482            TcpIo->Image,
483            TcpIo->Controller
484            );
485   }
486 
487   ChildHandle = NULL;
488 
489   if (TcpIo->IsListenDone) {
490     if (TcpVersion == TCP_VERSION_4) {
491       Tcp4 = TcpIo->NewTcp.Tcp4;
492       if (Tcp4 != NULL) {
493         Tcp4->Configure (Tcp4, NULL);
494         ChildHandle = TcpIo->ListenToken.Tcp4Token.NewChildHandle;
495       }
496     } else {
497       Tcp6 = TcpIo->NewTcp.Tcp6;
498       if (Tcp6 != NULL) {
499         Tcp6->Configure (Tcp6, NULL);
500         ChildHandle = TcpIo->ListenToken.Tcp6Token.NewChildHandle;
501       }
502     }
503 
504     if (ChildHandle != NULL) {
505 
506       gBS->CloseProtocol (
507              ChildHandle,
508              ProtocolGuid,
509              TcpIo->Image,
510              TcpIo->Controller
511              );
512     }
513   }
514 
515   NetLibDestroyServiceChild (
516     TcpIo->Controller,
517     TcpIo->Image,
518     ServiceBindingGuid,
519     TcpIo->Handle
520     );
521 }
522 
523 /**
524   Connect to the other endpoint of the TCP socket.
525 
526   @param[in, out]  TcpIo     The TcpIo wrapping the TCP socket.
527   @param[in]       Timeout   The time to wait for connection done. Set to NULL for infinite wait.
528 
529   @retval EFI_SUCCESS            Connect to the other endpoint of the TCP socket
530                                  successfully.
531   @retval EFI_TIMEOUT            Failed to connect to the other endpoint of the
532                                  TCP socket in the specified time period.
533   @retval EFI_INVALID_PARAMETER  One or more parameters are invalid.
534   @retval EFI_UNSUPPORTED        One or more of the control options are not
535                                  supported in the implementation.
536   @retval Others                 Other errors as indicated.
537 
538 **/
539 EFI_STATUS
540 EFIAPI
TcpIoConnect(IN OUT TCP_IO * TcpIo,IN EFI_EVENT Timeout OPTIONAL)541 TcpIoConnect (
542   IN OUT TCP_IO             *TcpIo,
543   IN     EFI_EVENT          Timeout        OPTIONAL
544   )
545 {
546   EFI_TCP4_PROTOCOL         *Tcp4;
547   EFI_TCP6_PROTOCOL         *Tcp6;
548   EFI_STATUS                Status;
549 
550   if ((TcpIo == NULL) || (TcpIo->Tcp.Tcp4 == NULL)) {
551     return EFI_INVALID_PARAMETER;
552   }
553 
554   TcpIo->IsConnDone = FALSE;
555 
556   Tcp4 = NULL;
557   Tcp6 = NULL;
558 
559   if (TcpIo->TcpVersion == TCP_VERSION_4) {
560     Tcp4   = TcpIo->Tcp.Tcp4;
561     Status = Tcp4->Connect (Tcp4, &TcpIo->ConnToken.Tcp4Token);
562   } else if (TcpIo->TcpVersion == TCP_VERSION_6) {
563     Tcp6   = TcpIo->Tcp.Tcp6;
564     Status = Tcp6->Connect (Tcp6, &TcpIo->ConnToken.Tcp6Token);
565   } else {
566     return EFI_UNSUPPORTED;
567   }
568 
569   if (EFI_ERROR (Status)) {
570     return Status;
571   }
572 
573   while (!TcpIo->IsConnDone && ((Timeout == NULL) || EFI_ERROR (gBS->CheckEvent (Timeout)))) {
574     if (TcpIo->TcpVersion == TCP_VERSION_4) {
575       Tcp4->Poll (Tcp4);
576     } else {
577       Tcp6->Poll (Tcp6);
578     }
579   }
580 
581   if (!TcpIo->IsConnDone) {
582     if (TcpIo->TcpVersion == TCP_VERSION_4) {
583       Tcp4->Cancel (Tcp4, &TcpIo->ConnToken.Tcp4Token.CompletionToken);
584     } else {
585       Tcp6->Cancel (Tcp6, &TcpIo->ConnToken.Tcp6Token.CompletionToken);
586     }
587     Status = EFI_TIMEOUT;
588   } else {
589     Status = TcpIo->ConnToken.Tcp4Token.CompletionToken.Status;
590   }
591 
592   return Status;
593 }
594 
595 /**
596   Accept the incomding request from the other endpoint of the TCP socket.
597 
598   @param[in, out]  TcpIo     The TcpIo wrapping the TCP socket.
599   @param[in]       Timeout   The time to wait for connection done. Set to NULL for infinite wait.
600 
601 
602   @retval EFI_SUCCESS            Connect to the other endpoint of the TCP socket
603                                  successfully.
604   @retval EFI_INVALID_PARAMETER  One or more parameters are invalid.
605   @retval EFI_UNSUPPORTED        One or more of the control options are not
606                                  supported in the implementation.
607 
608   @retval EFI_TIMEOUT            Failed to connect to the other endpoint of the
609                                  TCP socket in the specified time period.
610   @retval Others                 Other errors as indicated.
611 
612 **/
613 EFI_STATUS
614 EFIAPI
TcpIoAccept(IN OUT TCP_IO * TcpIo,IN EFI_EVENT Timeout OPTIONAL)615 TcpIoAccept (
616   IN OUT TCP_IO             *TcpIo,
617   IN     EFI_EVENT          Timeout        OPTIONAL
618   )
619 {
620   EFI_STATUS                Status;
621   EFI_GUID                  *ProtocolGuid;
622   EFI_TCP4_PROTOCOL         *Tcp4;
623   EFI_TCP6_PROTOCOL         *Tcp6;
624 
625   if ((TcpIo == NULL) || (TcpIo->Tcp.Tcp4 == NULL)) {
626     return EFI_INVALID_PARAMETER;
627   }
628 
629   TcpIo->IsListenDone = FALSE;
630 
631   Tcp4 = NULL;
632   Tcp6 = NULL;
633 
634   if (TcpIo->TcpVersion == TCP_VERSION_4) {
635     Tcp4   = TcpIo->Tcp.Tcp4;
636     Status = Tcp4->Accept (Tcp4, &TcpIo->ListenToken.Tcp4Token);
637   } else if (TcpIo->TcpVersion == TCP_VERSION_6) {
638     Tcp6   = TcpIo->Tcp.Tcp6;
639     Status = Tcp6->Accept (Tcp6, &TcpIo->ListenToken.Tcp6Token);
640   } else {
641     return EFI_UNSUPPORTED;
642   }
643 
644   if (EFI_ERROR (Status)) {
645     return Status;
646   }
647 
648   while (!TcpIo->IsListenDone && ((Timeout == NULL) || EFI_ERROR (gBS->CheckEvent (Timeout)))) {
649     if (TcpIo->TcpVersion == TCP_VERSION_4) {
650       Tcp4->Poll (Tcp4);
651     } else {
652       Tcp6->Poll (Tcp6);
653     }
654   }
655 
656   if (!TcpIo->IsListenDone) {
657     if (TcpIo->TcpVersion == TCP_VERSION_4) {
658       Tcp4->Cancel (Tcp4, &TcpIo->ListenToken.Tcp4Token.CompletionToken);
659     } else {
660       Tcp6->Cancel (Tcp6, &TcpIo->ListenToken.Tcp6Token.CompletionToken);
661     }
662     Status = EFI_TIMEOUT;
663   } else {
664     Status = TcpIo->ListenToken.Tcp4Token.CompletionToken.Status;
665   }
666 
667   //
668   // The new TCP instance handle created for the established connection is
669   // in ListenToken.
670   //
671   if (!EFI_ERROR (Status)) {
672     if (TcpIo->TcpVersion == TCP_VERSION_4) {
673       ProtocolGuid = &gEfiTcp4ProtocolGuid;
674     } else {
675       ProtocolGuid = &gEfiTcp6ProtocolGuid;
676     }
677 
678     Status = gBS->OpenProtocol (
679                     TcpIo->ListenToken.Tcp4Token.NewChildHandle,
680                     ProtocolGuid,
681                     (VOID **) (&TcpIo->NewTcp.Tcp4),
682                     TcpIo->Image,
683                     TcpIo->Controller,
684                     EFI_OPEN_PROTOCOL_BY_DRIVER
685                     );
686 
687   }
688 
689   return Status;
690 }
691 
692 /**
693   Reset the socket.
694 
695   @param[in, out]  TcpIo The TcpIo wrapping the TCP socket.
696 
697 **/
698 VOID
699 EFIAPI
TcpIoReset(IN OUT TCP_IO * TcpIo)700 TcpIoReset (
701   IN OUT TCP_IO             *TcpIo
702   )
703 {
704   EFI_TCP4_PROTOCOL         *Tcp4;
705   EFI_TCP6_PROTOCOL         *Tcp6;
706   EFI_STATUS                Status;
707 
708   if ((TcpIo == NULL) || (TcpIo->Tcp.Tcp4 == NULL)) {
709     return ;
710   }
711 
712   TcpIo->IsCloseDone = FALSE;
713   Tcp4               = NULL;
714   Tcp6               = NULL;
715 
716   if (TcpIo->TcpVersion == TCP_VERSION_4) {
717     TcpIo->CloseToken.Tcp4Token.AbortOnClose = TRUE;
718     Tcp4 = TcpIo->Tcp.Tcp4;
719     Status = Tcp4->Close (Tcp4, &TcpIo->CloseToken.Tcp4Token);
720   } else if (TcpIo->TcpVersion == TCP_VERSION_6) {
721     TcpIo->CloseToken.Tcp6Token.AbortOnClose = TRUE;
722     Tcp6 = TcpIo->Tcp.Tcp6;
723     Status = Tcp6->Close (Tcp6, &TcpIo->CloseToken.Tcp6Token);
724   } else {
725     return ;
726   }
727 
728   if (EFI_ERROR (Status)) {
729     return ;
730   }
731 
732   while (!TcpIo->IsCloseDone) {
733     if (TcpIo->TcpVersion == TCP_VERSION_4) {
734       Tcp4->Poll (Tcp4);
735     } else {
736       Tcp6->Poll (Tcp6);
737     }
738   }
739 }
740 
741 
742 /**
743   Transmit the Packet to the other endpoint of the socket.
744 
745   @param[in]   TcpIo           The TcpIo wrapping the TCP socket.
746   @param[in]   Packet          The packet to transmit.
747 
748   @retval EFI_SUCCESS            The packet is trasmitted.
749   @retval EFI_INVALID_PARAMETER  One or more parameters are invalid.
750   @retval EFI_UNSUPPORTED        One or more of the control options are not
751                                  supported in the implementation.
752   @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory.
753   @retval EFI_DEVICE_ERROR       An unexpected network or system error occurred.
754   @retval Others                 Other errors as indicated.
755 
756 **/
757 EFI_STATUS
758 EFIAPI
TcpIoTransmit(IN TCP_IO * TcpIo,IN NET_BUF * Packet)759 TcpIoTransmit (
760   IN TCP_IO                 *TcpIo,
761   IN NET_BUF                *Packet
762   )
763 {
764   EFI_STATUS                Status;
765   VOID                      *Data;
766   EFI_TCP4_PROTOCOL         *Tcp4;
767   EFI_TCP6_PROTOCOL         *Tcp6;
768   UINTN                     Size;
769 
770   if ((TcpIo == NULL) || (TcpIo->Tcp.Tcp4 == NULL)|| (Packet == NULL)) {
771     return EFI_INVALID_PARAMETER;
772   }
773 
774   if (TcpIo->TcpVersion == TCP_VERSION_4) {
775 
776     Size = sizeof (EFI_TCP4_TRANSMIT_DATA) +
777            (Packet->BlockOpNum - 1) * sizeof (EFI_TCP4_FRAGMENT_DATA);
778   } else if (TcpIo->TcpVersion == TCP_VERSION_6) {
779     Size = sizeof (EFI_TCP6_TRANSMIT_DATA) +
780            (Packet->BlockOpNum - 1) * sizeof (EFI_TCP6_FRAGMENT_DATA);
781   } else {
782     return EFI_UNSUPPORTED;
783   }
784 
785   Data = AllocatePool (Size);
786   if (Data == NULL) {
787     return EFI_OUT_OF_RESOURCES;
788   }
789 
790   ((EFI_TCP4_TRANSMIT_DATA *) Data)->Push        = TRUE;
791   ((EFI_TCP4_TRANSMIT_DATA *) Data)->Urgent      = FALSE;
792   ((EFI_TCP4_TRANSMIT_DATA *) Data)->DataLength  = Packet->TotalSize;
793 
794   //
795   // Build the fragment table.
796   //
797   ((EFI_TCP4_TRANSMIT_DATA *) Data)->FragmentCount = Packet->BlockOpNum;
798 
799   NetbufBuildExt (
800     Packet,
801     (NET_FRAGMENT *) &((EFI_TCP4_TRANSMIT_DATA *) Data)->FragmentTable[0],
802     &((EFI_TCP4_TRANSMIT_DATA *) Data)->FragmentCount
803     );
804 
805   Tcp4   = NULL;
806   Tcp6   = NULL;
807   Status = EFI_DEVICE_ERROR;
808 
809   //
810   // Trasnmit the packet.
811   //
812   if (TcpIo->TcpVersion == TCP_VERSION_4) {
813     TcpIo->TxToken.Tcp4Token.Packet.TxData = (EFI_TCP4_TRANSMIT_DATA *) Data;
814     Tcp4    = TcpIo->Tcp.Tcp4;
815     if (TcpIo->IsListenDone) {
816       Tcp4 = TcpIo->NewTcp.Tcp4;
817     }
818 
819     if (Tcp4 == NULL) {
820       goto ON_EXIT;
821     }
822 
823     Status  = Tcp4->Transmit (Tcp4, &TcpIo->TxToken.Tcp4Token);
824   } else {
825     TcpIo->TxToken.Tcp6Token.Packet.TxData = (EFI_TCP6_TRANSMIT_DATA *) Data;
826     Tcp6    = TcpIo->Tcp.Tcp6;
827     if (TcpIo->IsListenDone) {
828       Tcp6 = TcpIo->NewTcp.Tcp6;
829     }
830 
831     if (Tcp6 == NULL) {
832       goto ON_EXIT;
833     }
834 
835     Status  = Tcp6->Transmit (Tcp6, &TcpIo->TxToken.Tcp6Token);
836   }
837 
838   if (EFI_ERROR (Status)) {
839     goto ON_EXIT;
840   }
841 
842   while (!TcpIo->IsTxDone) {
843     if (TcpIo->TcpVersion == TCP_VERSION_4) {
844       Tcp4->Poll (Tcp4);
845     } else {
846       Tcp6->Poll (Tcp6);
847     }
848   }
849 
850   TcpIo->IsTxDone  = FALSE;
851   Status           = TcpIo->TxToken.Tcp4Token.CompletionToken.Status;
852 
853 ON_EXIT:
854 
855   FreePool (Data);
856 
857   return Status;
858 }
859 
860 /**
861   Receive data from the socket.
862 
863   @param[in, out]  TcpIo       The TcpIo which wraps the socket to be destroyed.
864   @param[in]       Packet      The buffer to hold the data copy from the socket rx buffer.
865   @param[in]       AsyncMode   Is this receive asyncronous or not.
866   @param[in]       Timeout     The time to wait for receiving the amount of data the Packet
867                                can hold. Set to NULL for infinite wait.
868 
869   @retval EFI_SUCCESS            The required amount of data is received from the socket.
870   @retval EFI_INVALID_PARAMETER  One or more parameters are invalid.
871   @retval EFI_DEVICE_ERROR       An unexpected network or system error occurred.
872   @retval EFI_OUT_OF_RESOURCES   Failed to allocate momery.
873   @retval EFI_TIMEOUT            Failed to receive the required amount of data in the
874                                  specified time period.
875   @retval Others                 Other errors as indicated.
876 
877 **/
878 EFI_STATUS
879 EFIAPI
TcpIoReceive(IN OUT TCP_IO * TcpIo,IN NET_BUF * Packet,IN BOOLEAN AsyncMode,IN EFI_EVENT Timeout OPTIONAL)880 TcpIoReceive (
881   IN OUT TCP_IO             *TcpIo,
882   IN     NET_BUF            *Packet,
883   IN     BOOLEAN            AsyncMode,
884   IN     EFI_EVENT          Timeout       OPTIONAL
885   )
886 {
887   EFI_TCP4_PROTOCOL         *Tcp4;
888   EFI_TCP6_PROTOCOL         *Tcp6;
889   EFI_TCP4_RECEIVE_DATA     *RxData;
890   EFI_STATUS                Status;
891   NET_FRAGMENT              *Fragment;
892   UINT32                    FragmentCount;
893   UINT32                    CurrentFragment;
894 
895   if ((TcpIo == NULL) || (TcpIo->Tcp.Tcp4 == NULL)|| (Packet == NULL)) {
896     return EFI_INVALID_PARAMETER;
897   }
898 
899   RxData = TcpIo->RxToken.Tcp4Token.Packet.RxData;
900   if (RxData == NULL) {
901     return EFI_INVALID_PARAMETER;
902   }
903 
904   Tcp4 = NULL;
905   Tcp6 = NULL;
906 
907   if (TcpIo->TcpVersion == TCP_VERSION_4) {
908     Tcp4 = TcpIo->Tcp.Tcp4;
909 
910     if (TcpIo->IsListenDone) {
911       Tcp4 = TcpIo->NewTcp.Tcp4;
912     }
913 
914     if (Tcp4 == NULL) {
915       return EFI_DEVICE_ERROR;
916     }
917 
918   } else if (TcpIo->TcpVersion == TCP_VERSION_6) {
919     Tcp6 = TcpIo->Tcp.Tcp6;
920 
921     if (TcpIo->IsListenDone) {
922       Tcp6 = TcpIo->NewTcp.Tcp6;
923     }
924 
925     if (Tcp6 == NULL) {
926       return EFI_DEVICE_ERROR;
927     }
928 
929   } else {
930     return EFI_UNSUPPORTED;
931   }
932 
933   FragmentCount = Packet->BlockOpNum;
934   Fragment      = AllocatePool (FragmentCount * sizeof (NET_FRAGMENT));
935   if (Fragment == NULL) {
936     Status = EFI_OUT_OF_RESOURCES;
937     goto ON_EXIT;
938   }
939   //
940   // Build the fragment table.
941   //
942   NetbufBuildExt (Packet, Fragment, &FragmentCount);
943 
944   RxData->FragmentCount         = 1;
945   CurrentFragment               = 0;
946   Status                        = EFI_SUCCESS;
947 
948   while (CurrentFragment < FragmentCount) {
949     RxData->DataLength                       = Fragment[CurrentFragment].Len;
950     RxData->FragmentTable[0].FragmentLength  = Fragment[CurrentFragment].Len;
951     RxData->FragmentTable[0].FragmentBuffer  = Fragment[CurrentFragment].Bulk;
952 
953     if (TcpIo->TcpVersion == TCP_VERSION_4) {
954       Status = Tcp4->Receive (Tcp4, &TcpIo->RxToken.Tcp4Token);
955     } else {
956       Status = Tcp6->Receive (Tcp6, &TcpIo->RxToken.Tcp6Token);
957     }
958 
959     if (EFI_ERROR (Status)) {
960       goto ON_EXIT;
961     }
962 
963     while (!TcpIo->IsRxDone && ((Timeout == NULL) || EFI_ERROR (gBS->CheckEvent (Timeout)))) {
964       //
965       // Poll until some data is received or an error occurs.
966       //
967       if (TcpIo->TcpVersion == TCP_VERSION_4) {
968         Tcp4->Poll (Tcp4);
969       } else {
970         Tcp6->Poll (Tcp6);
971       }
972     }
973 
974     if (!TcpIo->IsRxDone) {
975       //
976       // Timeout occurs, cancel the receive request.
977       //
978       if (TcpIo->TcpVersion == TCP_VERSION_4) {
979         Tcp4->Cancel (Tcp4, &TcpIo->RxToken.Tcp4Token.CompletionToken);
980       } else {
981         Tcp6->Cancel (Tcp6, &TcpIo->RxToken.Tcp6Token.CompletionToken);
982       }
983 
984       Status = EFI_TIMEOUT;
985       goto ON_EXIT;
986     } else {
987       TcpIo->IsRxDone = FALSE;
988     }
989 
990     Status = TcpIo->RxToken.Tcp4Token.CompletionToken.Status;
991 
992     if (EFI_ERROR (Status)) {
993       goto ON_EXIT;
994     }
995 
996     Fragment[CurrentFragment].Len -= RxData->FragmentTable[0].FragmentLength;
997     if (Fragment[CurrentFragment].Len == 0) {
998       CurrentFragment++;
999     } else {
1000       Fragment[CurrentFragment].Bulk += RxData->FragmentTable[0].FragmentLength;
1001     }
1002   }
1003 
1004 ON_EXIT:
1005 
1006   if (Fragment != NULL) {
1007     FreePool (Fragment);
1008   }
1009 
1010   return Status;
1011 }
1012