3 # Copyright (c) 2014, ARM Ltd. All rights reserved.<BR>
5 # This program and the accompanying materials
6 # are licensed and made available under the terms and conditions of the BSD License
7 # which accompanies this distribution. The full text of the license may be found at
8 # http://opensource.org/licenses/bsd-license.php
9 # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15 #include <Protocol/AndroidFastbootTransport.h>
16 #include <Protocol/Dhcp4.h>
17 #include <Protocol/Tcp4.h>
18 #include <Protocol/ServiceBinding.h>
19 #include <Protocol/SimpleTextOut.h>
21 #include <Library/BaseLib.h>
22 #include <Library/BaseMemoryLib.h>
23 #include <Library/DebugLib.h>
24 #include <Library/MemoryAllocationLib.h>
25 #include <Library/PrintLib.h>
26 #include <Library/UefiBootServicesTableLib.h>
27 #include <Library/UefiDriverEntryPoint.h>
28 #include <Library/UefiRuntimeServicesTableLib.h>
30 #include <Guid/Hostname.h>
32 #define IP4_ADDR_TO_STRING(IpAddr, IpAddrString) UnicodeSPrint ( \
42 // Fastboot says max packet size is 512, but FASTBOOT_TRANSPORT_PROTOCOL
43 // doesn't place a limit on the size of buffers returned by Receive.
44 // (This isn't actually a packet size - it's just the size of the buffers we
45 // pass to the TCP driver to fill with received data.)
46 // We can achieve much better performance by doing this in larger chunks.
47 #define RX_FRAGMENT_SIZE 2048
49 STATIC EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*mTextOut
;
51 STATIC EFI_TCP4_PROTOCOL
*mTcpConnection
;
52 STATIC EFI_TCP4_PROTOCOL
*mTcpListener
;
54 STATIC EFI_EVENT mReceiveEvent
;
56 STATIC EFI_SERVICE_BINDING_PROTOCOL
*mTcpServiceBinding
;
57 STATIC EFI_HANDLE mTcpHandle
= NULL
;
59 // We only ever use one IO token for receive and one for transmit. To save
60 // repeatedly allocating and freeing, just allocate statically and re-use.
61 #define NUM_RX_TOKENS 16
62 #define TOKEN_NEXT(Index) (((Index) + 1) % NUM_RX_TOKENS)
64 STATIC UINTN mNextSubmitIndex
;
65 STATIC UINTN mNextReceiveIndex
;
66 STATIC EFI_TCP4_IO_TOKEN mReceiveToken
[NUM_RX_TOKENS
];
67 STATIC EFI_TCP4_RECEIVE_DATA mRxData
[NUM_RX_TOKENS
];
68 STATIC EFI_TCP4_IO_TOKEN mTransmitToken
;
69 STATIC EFI_TCP4_TRANSMIT_DATA mTxData
;
70 // We also reuse the accept token
71 STATIC EFI_TCP4_LISTEN_TOKEN mAcceptToken
;
72 // .. and the close token
73 STATIC EFI_TCP4_CLOSE_TOKEN mCloseToken
;
75 // List type for queued received packets
76 typedef struct _FASTBOOT_TCP_PACKET_LIST
{
80 } FASTBOOT_TCP_PACKET_LIST
;
82 STATIC LIST_ENTRY mPacketListHead
;
93 Helper function to set up a receive IO token and call Tcp->Receive
102 VOID
*FragmentBuffer
;
104 Status
= EFI_SUCCESS
;
106 FragmentBuffer
= AllocatePool (RX_FRAGMENT_SIZE
);
107 ASSERT (FragmentBuffer
!= NULL
);
108 if (FragmentBuffer
== NULL
) {
109 DEBUG ((EFI_D_ERROR
, "TCP Fastboot out of resources"));
110 return EFI_OUT_OF_RESOURCES
;
113 mRxData
[mNextSubmitIndex
].DataLength
= RX_FRAGMENT_SIZE
;
114 mRxData
[mNextSubmitIndex
].FragmentTable
[0].FragmentLength
= RX_FRAGMENT_SIZE
;
115 mRxData
[mNextSubmitIndex
].FragmentTable
[0].FragmentBuffer
= FragmentBuffer
;
117 Status
= mTcpConnection
->Receive (mTcpConnection
, &mReceiveToken
[mNextSubmitIndex
]);
118 if (EFI_ERROR (Status
)) {
119 DEBUG ((EFI_D_ERROR
, "TCP Receive: %r\n", Status
));
120 FreePool (FragmentBuffer
);
123 mNextSubmitIndex
= TOKEN_NEXT (mNextSubmitIndex
);
128 Event notify function for when we have closed our TCP connection.
129 We can now start listening for another connection.
140 // Possible bug in EDK2 TCP4 driver: closing a connection doesn't remove its
141 // PCB from the list of live connections. Subsequent attempts to Configure()
142 // a TCP instance with the same local port will fail with INVALID_PARAMETER.
143 // Calling Configure with NULL is a workaround for this issue.
144 Status
= mTcpConnection
->Configure (mTcpConnection
, NULL
);
146 mTcpConnection
= NULL
;
148 Status
= mTcpListener
->Accept (mTcpListener
, &mAcceptToken
);
149 if (EFI_ERROR (Status
)) {
150 DEBUG ((EFI_D_ERROR
, "TCP Accept: %r\n", Status
));
162 for (Index
= 0; Index
< NUM_RX_TOKENS
; Index
++) {
163 gBS
->CloseEvent (mReceiveToken
[Index
].CompletionToken
.Event
);
168 Event notify function to be called when we receive TCP data.
179 FASTBOOT_TCP_PACKET_LIST
*NewEntry
;
180 EFI_TCP4_IO_TOKEN
*ReceiveToken
;
182 ReceiveToken
= &mReceiveToken
[mNextReceiveIndex
];
184 Status
= ReceiveToken
->CompletionToken
.Status
;
186 if (Status
== EFI_CONNECTION_FIN
) {
188 // Remote host closed connection. Close our end.
191 CloseReceiveEvents ();
193 Status
= mTcpConnection
->Close (mTcpConnection
, &mCloseToken
);
194 ASSERT_EFI_ERROR (Status
);
200 // Add an element to the receive queue
203 NewEntry
= AllocatePool (sizeof (FASTBOOT_TCP_PACKET_LIST
));
204 if (NewEntry
== NULL
) {
205 DEBUG ((EFI_D_ERROR
, "TCP Fastboot: Out of resources\n"));
209 mNextReceiveIndex
= TOKEN_NEXT (mNextReceiveIndex
);
211 if (!EFI_ERROR (Status
)) {
213 = ReceiveToken
->Packet
.RxData
->FragmentTable
[0].FragmentBuffer
;
215 = ReceiveToken
->Packet
.RxData
->FragmentTable
[0].FragmentLength
;
217 // Prepare to receive more data
218 SubmitRecieveToken();
220 // Fatal receive error. Put an entry with NULL in the queue, signifying
221 // to return EFI_DEVICE_ERROR from TcpFastbootTransportReceive.
222 NewEntry
->Buffer
= NULL
;
223 NewEntry
->BufferSize
= 0;
225 DEBUG ((EFI_D_ERROR
, "\nTCP Fastboot Receive error: %r\n", Status
));
228 InsertTailList (&mPacketListHead
, &NewEntry
->Link
);
230 Status
= gBS
->SignalEvent (mReceiveEvent
);
231 ASSERT_EFI_ERROR (Status
);
236 Event notify function to be called when we accept an incoming TCP connection.
246 EFI_TCP4_LISTEN_TOKEN
*AcceptToken
;
250 AcceptToken
= (EFI_TCP4_LISTEN_TOKEN
*) Context
;
251 Status
= AcceptToken
->CompletionToken
.Status
;
253 if (EFI_ERROR (Status
)) {
254 DEBUG ((EFI_D_ERROR
, "TCP Fastboot: Connection Error: %r\n", Status
));
257 DEBUG ((EFI_D_ERROR
, "TCP Fastboot: Connection Received.\n"));
260 // Accepting a new TCP connection creates a new instance of the TCP protocol.
261 // Open it and prepare to receive on it.
264 Status
= gBS
->OpenProtocol (
265 AcceptToken
->NewChildHandle
,
266 &gEfiTcp4ProtocolGuid
,
267 (VOID
**) &mTcpConnection
,
270 EFI_OPEN_PROTOCOL_GET_PROTOCOL
272 if (EFI_ERROR (Status
)) {
273 DEBUG ((EFI_D_ERROR
, "Open TCP Connection: %r\n", Status
));
277 mNextSubmitIndex
= 0;
278 mNextReceiveIndex
= 0;
280 for (Index
= 0; Index
< NUM_RX_TOKENS
; Index
++) {
281 Status
= gBS
->CreateEvent (
286 &(mReceiveToken
[Index
].CompletionToken
.Event
)
288 ASSERT_EFI_ERROR (Status
);
291 for (Index
= 0; Index
< NUM_RX_TOKENS
; Index
++) {
292 SubmitRecieveToken();
297 Set up TCP Fastboot transport: Configure the network device via DHCP then
298 start waiting for a TCP connection on the Fastboot port.
301 TcpFastbootTransportStart (
302 EFI_EVENT ReceiveEvent
306 EFI_HANDLE NetDeviceHandle
;
307 EFI_HANDLE
*HandleBuffer
;
308 EFI_IP4_MODE_DATA Ip4ModeData
;
310 UINTN HostnameSize
= 256;
312 CHAR16 HostnameUnicode
[256] = L
"<no hostname>";
313 CHAR16 IpAddrString
[16];
316 EFI_TCP4_CONFIG_DATA TcpConfigData
= {
317 0x00, // IPv4 Type of Service
318 255, // IPv4 Time to Live
320 TRUE
, // Use default address
321 { {0, 0, 0, 0} }, // IP Address (ignored - use default)
322 { {0, 0, 0, 0} }, // Subnet mask (ignored - use default)
323 FixedPcdGet32 (PcdAndroidFastbootTcpPort
), // Station port
324 { {0, 0, 0, 0} }, // Remote address: accept any
325 0, // Remote Port: accept any
326 FALSE
// ActiveFlag: be a "server"
328 NULL
// Default advanced TCP options
331 mReceiveEvent
= ReceiveEvent
;
332 InitializeListHead (&mPacketListHead
);
334 mTextOut
->OutputString (mTextOut
, L
"Initialising TCP Fastboot transport...\r\n");
337 // Open a passive TCP instance
340 Status
= gBS
->LocateHandleBuffer (
342 &gEfiTcp4ServiceBindingProtocolGuid
,
347 if (EFI_ERROR (Status
)) {
348 DEBUG ((EFI_D_ERROR
, "Find TCP Service Binding: %r\n", Status
));
352 // We just use the first network device
353 NetDeviceHandle
= HandleBuffer
[0];
355 Status
= gBS
->OpenProtocol (
357 &gEfiTcp4ServiceBindingProtocolGuid
,
358 (VOID
**) &mTcpServiceBinding
,
361 EFI_OPEN_PROTOCOL_GET_PROTOCOL
363 if (EFI_ERROR (Status
)) {
364 DEBUG ((EFI_D_ERROR
, "Open TCP Service Binding: %r\n", Status
));
368 Status
= mTcpServiceBinding
->CreateChild (mTcpServiceBinding
, &mTcpHandle
);
369 if (EFI_ERROR (Status
)) {
370 DEBUG ((EFI_D_ERROR
, "TCP ServiceBinding Create: %r\n", Status
));
374 Status
= gBS
->OpenProtocol (
376 &gEfiTcp4ProtocolGuid
,
377 (VOID
**) &mTcpListener
,
380 EFI_OPEN_PROTOCOL_GET_PROTOCOL
382 if (EFI_ERROR (Status
)) {
383 DEBUG ((EFI_D_ERROR
, "Open TCP Protocol: %r\n", Status
));
387 // Set up re-usable tokens
390 for (Index
= 0; Index
< NUM_RX_TOKENS
; Index
++) {
391 mRxData
[Index
].UrgentFlag
= FALSE
;
392 mRxData
[Index
].FragmentCount
= 1;
393 mReceiveToken
[Index
].Packet
.RxData
= &mRxData
[Index
];
397 mTxData
.Urgent
= FALSE
;
398 mTxData
.FragmentCount
= 1;
399 mTransmitToken
.Packet
.TxData
= &mTxData
;
401 Status
= gBS
->CreateEvent (
406 &mAcceptToken
.CompletionToken
.Event
408 ASSERT_EFI_ERROR (Status
);
410 Status
= gBS
->CreateEvent (
415 &mCloseToken
.CompletionToken
.Event
417 ASSERT_EFI_ERROR (Status
);
420 // Configure the TCP instance
423 Status
= mTcpListener
->Configure (mTcpListener
, &TcpConfigData
);
424 if (Status
== EFI_NO_MAPPING
) {
425 // Wait until the IP configuration process (probably DHCP) has finished
427 Status
= mTcpListener
->GetModeData (mTcpListener
,
432 ASSERT_EFI_ERROR (Status
);
433 } while (!Ip4ModeData
.IsConfigured
);
434 Status
= mTcpListener
->Configure (mTcpListener
, &TcpConfigData
);
435 } else if (EFI_ERROR (Status
)) {
436 DEBUG ((EFI_D_ERROR
, "TCP Configure: %r\n", Status
));
441 // Tell the user our address and hostname
443 IP4_ADDR_TO_STRING (Ip4ModeData
.ConfigData
.StationAddress
, IpAddrString
);
446 Status
= gRT
->GetVariable (
448 &gEfiHostnameVariableGuid
,
453 if (!EFI_ERROR (Status
) && HostnameSize
!= 0) {
454 AsciiStrToUnicodeStr (Hostname
, HostnameUnicode
);
457 // Hostname variable is not null-terminated.
458 Hostname
[HostnameSize
] = L
'\0';
460 mTextOut
->OutputString (mTextOut
, L
"TCP Fastboot transport configured.");
461 mTextOut
->OutputString (mTextOut
, L
"\r\nIP address: ");
462 mTextOut
->OutputString (mTextOut
,IpAddrString
);
463 mTextOut
->OutputString (mTextOut
, L
"\r\n");
464 mTextOut
->OutputString (mTextOut
, L
"\r\nhostname: ");
465 mTextOut
->OutputString (mTextOut
, HostnameUnicode
);
466 mTextOut
->OutputString (mTextOut
, L
"\r\n");
469 // Start listening for a connection
472 Status
= mTcpListener
->Accept (mTcpListener
, &mAcceptToken
);
473 if (EFI_ERROR (Status
)) {
474 DEBUG ((EFI_D_ERROR
, "TCP Accept: %r\n", Status
));
478 mTextOut
->OutputString (mTextOut
, L
"TCP Fastboot transport initialised.\r\n");
480 FreePool (HandleBuffer
);
486 TcpFastbootTransportStop (
490 EFI_TCP4_CLOSE_TOKEN CloseToken
;
493 FASTBOOT_TCP_PACKET_LIST
*Entry
;
494 FASTBOOT_TCP_PACKET_LIST
*NextEntry
;
496 // Close any existing TCP connection, blocking until it's done.
497 if (mTcpConnection
!= NULL
) {
498 CloseReceiveEvents ();
500 CloseToken
.AbortOnClose
= FALSE
;
502 Status
= gBS
->CreateEvent (0, 0, NULL
, NULL
, &CloseToken
.CompletionToken
.Event
);
503 ASSERT_EFI_ERROR (Status
);
505 Status
= mTcpConnection
->Close (mTcpConnection
, &CloseToken
);
506 ASSERT_EFI_ERROR (Status
);
508 Status
= gBS
->WaitForEvent (
510 &CloseToken
.CompletionToken
.Event
,
513 ASSERT_EFI_ERROR (Status
);
515 ASSERT_EFI_ERROR (CloseToken
.CompletionToken
.Status
);
517 // Possible bug in EDK2 TCP4 driver: closing a connection doesn't remove its
518 // PCB from the list of live connections. Subsequent attempts to Configure()
519 // a TCP instance with the same local port will fail with INVALID_PARAMETER.
520 // Calling Configure with NULL is a workaround for this issue.
521 Status
= mTcpConnection
->Configure (mTcpConnection
, NULL
);
522 ASSERT_EFI_ERROR (Status
);
526 gBS
->CloseEvent (mAcceptToken
.CompletionToken
.Event
);
528 // Stop listening for connections.
529 // Ideally we would do this with Cancel, but it isn't implemented by EDK2.
530 // So we just "reset this TCPv4 instance brutally".
531 Status
= mTcpListener
->Configure (mTcpListener
, NULL
);
532 ASSERT_EFI_ERROR (Status
);
534 Status
= mTcpServiceBinding
->DestroyChild (mTcpServiceBinding
, &mTcpHandle
);
536 // Free any data the user didn't pick up
537 Entry
= (FASTBOOT_TCP_PACKET_LIST
*) GetFirstNode (&mPacketListHead
);
538 while (!IsNull (&mPacketListHead
, &Entry
->Link
)) {
539 NextEntry
= (FASTBOOT_TCP_PACKET_LIST
*) GetNextNode (&mPacketListHead
, &Entry
->Link
);
541 RemoveEntryList (&Entry
->Link
);
543 FreePool (Entry
->Buffer
);
554 Event notify function for when data has been sent. Free resources and report
556 Context should point to the transmit IO token passed to
557 TcpConnection->Transmit.
568 Status
= mTransmitToken
.CompletionToken
.Status
;
569 if (EFI_ERROR (Status
)) {
570 DEBUG ((EFI_D_ERROR
, "TCP Fastboot transmit result: %r\n", Status
));
571 gBS
->SignalEvent (*(EFI_EVENT
*) Context
);
574 FreePool (mTransmitToken
.Packet
.TxData
->FragmentTable
[0].FragmentBuffer
);
578 TcpFastbootTransportSend (
580 IN CONST VOID
*Buffer
,
581 IN EFI_EVENT
*FatalErrorEvent
586 if (BufferSize
> 512) {
587 return EFI_INVALID_PARAMETER
;
591 // Build transmit IO token
594 // Create an event so we are notified when a transmission is complete.
595 // We use this to free resources and report errors.
596 Status
= gBS
->CreateEvent (
601 &mTransmitToken
.CompletionToken
.Event
603 ASSERT_EFI_ERROR (Status
);
605 mTxData
.DataLength
= BufferSize
;
607 mTxData
.FragmentTable
[0].FragmentLength
= BufferSize
;
608 mTxData
.FragmentTable
[0].FragmentBuffer
= AllocateCopyPool (
613 Status
= mTcpConnection
->Transmit (mTcpConnection
, &mTransmitToken
);
614 if (EFI_ERROR (Status
)) {
615 DEBUG ((EFI_D_ERROR
, "TCP Transmit: %r\n", Status
));
624 TcpFastbootTransportReceive (
625 OUT UINTN
*BufferSize
,
629 FASTBOOT_TCP_PACKET_LIST
*Entry
;
631 if (IsListEmpty (&mPacketListHead
)) {
632 return EFI_NOT_READY
;
635 Entry
= (FASTBOOT_TCP_PACKET_LIST
*) GetFirstNode (&mPacketListHead
);
637 if (Entry
->Buffer
== NULL
) {
638 // There was an error receiving this packet.
639 return EFI_DEVICE_ERROR
;
642 *Buffer
= Entry
->Buffer
;
643 *BufferSize
= Entry
->BufferSize
;
645 RemoveEntryList (&Entry
->Link
);
651 FASTBOOT_TRANSPORT_PROTOCOL mTransportProtocol
= {
652 TcpFastbootTransportStart
,
653 TcpFastbootTransportStop
,
654 TcpFastbootTransportSend
,
655 TcpFastbootTransportReceive
659 TcpFastbootTransportEntryPoint (
660 IN EFI_HANDLE ImageHandle
,
661 IN EFI_SYSTEM_TABLE
*SystemTable
667 Status
= gBS
->LocateProtocol(
668 &gEfiSimpleTextOutProtocolGuid
,
672 if (EFI_ERROR (Status
)) {
673 DEBUG ((EFI_D_ERROR
, "Fastboot: Open Text Output Protocol: %r\n", Status
));
677 Status
= gBS
->InstallProtocolInterface (
679 &gAndroidFastbootTransportProtocolGuid
,
680 EFI_NATIVE_INTERFACE
,
683 if (EFI_ERROR (Status
)) {
684 DEBUG ((EFI_D_ERROR
, "Fastboot: Install transport Protocol: %r\n", Status
));