3 # Copyright (c) 2014, ARM Ltd. All rights reserved.<BR>
5 # SPDX-License-Identifier: BSD-2-Clause-Patent
10 #include <Protocol/AndroidFastbootTransport.h>
11 #include <Protocol/Dhcp4.h>
12 #include <Protocol/Tcp4.h>
13 #include <Protocol/ServiceBinding.h>
14 #include <Protocol/SimpleTextOut.h>
16 #include <Library/BaseLib.h>
17 #include <Library/BaseMemoryLib.h>
18 #include <Library/DebugLib.h>
19 #include <Library/MemoryAllocationLib.h>
20 #include <Library/PrintLib.h>
21 #include <Library/UefiBootServicesTableLib.h>
22 #include <Library/UefiDriverEntryPoint.h>
23 #include <Library/UefiRuntimeServicesTableLib.h>
25 #define IP4_ADDR_TO_STRING(IpAddr, IpAddrString) UnicodeSPrint ( \
35 // Fastboot says max packet size is 512, but FASTBOOT_TRANSPORT_PROTOCOL
36 // doesn't place a limit on the size of buffers returned by Receive.
37 // (This isn't actually a packet size - it's just the size of the buffers we
38 // pass to the TCP driver to fill with received data.)
39 // We can achieve much better performance by doing this in larger chunks.
40 #define RX_FRAGMENT_SIZE 2048
42 STATIC EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*mTextOut
;
44 STATIC EFI_TCP4_PROTOCOL
*mTcpConnection
;
45 STATIC EFI_TCP4_PROTOCOL
*mTcpListener
;
47 STATIC EFI_EVENT mReceiveEvent
;
49 STATIC EFI_SERVICE_BINDING_PROTOCOL
*mTcpServiceBinding
;
50 STATIC EFI_HANDLE mTcpHandle
= NULL
;
52 // We only ever use one IO token for receive and one for transmit. To save
53 // repeatedly allocating and freeing, just allocate statically and re-use.
54 #define NUM_RX_TOKENS 16
55 #define TOKEN_NEXT(Index) (((Index) + 1) % NUM_RX_TOKENS)
57 STATIC UINTN mNextSubmitIndex
;
58 STATIC UINTN mNextReceiveIndex
;
59 STATIC EFI_TCP4_IO_TOKEN mReceiveToken
[NUM_RX_TOKENS
];
60 STATIC EFI_TCP4_RECEIVE_DATA mRxData
[NUM_RX_TOKENS
];
61 STATIC EFI_TCP4_IO_TOKEN mTransmitToken
;
62 STATIC EFI_TCP4_TRANSMIT_DATA mTxData
;
63 // We also reuse the accept token
64 STATIC EFI_TCP4_LISTEN_TOKEN mAcceptToken
;
65 // .. and the close token
66 STATIC EFI_TCP4_CLOSE_TOKEN mCloseToken
;
68 // List type for queued received packets
69 typedef struct _FASTBOOT_TCP_PACKET_LIST
{
73 } FASTBOOT_TCP_PACKET_LIST
;
75 STATIC LIST_ENTRY mPacketListHead
;
86 Helper function to set up a receive IO token and call Tcp->Receive
99 FragmentBuffer
= AllocatePool (RX_FRAGMENT_SIZE
);
100 ASSERT (FragmentBuffer
!= NULL
);
101 if (FragmentBuffer
== NULL
) {
102 DEBUG ((EFI_D_ERROR
, "TCP Fastboot out of resources"));
103 return EFI_OUT_OF_RESOURCES
;
106 mRxData
[mNextSubmitIndex
].DataLength
= RX_FRAGMENT_SIZE
;
107 mRxData
[mNextSubmitIndex
].FragmentTable
[0].FragmentLength
= RX_FRAGMENT_SIZE
;
108 mRxData
[mNextSubmitIndex
].FragmentTable
[0].FragmentBuffer
= FragmentBuffer
;
110 Status
= mTcpConnection
->Receive (mTcpConnection
, &mReceiveToken
[mNextSubmitIndex
]);
111 if (EFI_ERROR (Status
)) {
112 DEBUG ((EFI_D_ERROR
, "TCP Receive: %r\n", Status
));
113 FreePool (FragmentBuffer
);
116 mNextSubmitIndex
= TOKEN_NEXT (mNextSubmitIndex
);
121 Event notify function for when we have closed our TCP connection.
122 We can now start listening for another connection.
133 // Possible bug in EDK2 TCP4 driver: closing a connection doesn't remove its
134 // PCB from the list of live connections. Subsequent attempts to Configure()
135 // a TCP instance with the same local port will fail with INVALID_PARAMETER.
136 // Calling Configure with NULL is a workaround for this issue.
137 Status
= mTcpConnection
->Configure (mTcpConnection
, NULL
);
139 mTcpConnection
= NULL
;
141 Status
= mTcpListener
->Accept (mTcpListener
, &mAcceptToken
);
142 if (EFI_ERROR (Status
)) {
143 DEBUG ((EFI_D_ERROR
, "TCP Accept: %r\n", Status
));
155 for (Index
= 0; Index
< NUM_RX_TOKENS
; Index
++) {
156 gBS
->CloseEvent (mReceiveToken
[Index
].CompletionToken
.Event
);
161 Event notify function to be called when we receive TCP data.
172 FASTBOOT_TCP_PACKET_LIST
*NewEntry
;
173 EFI_TCP4_IO_TOKEN
*ReceiveToken
;
175 ReceiveToken
= &mReceiveToken
[mNextReceiveIndex
];
177 Status
= ReceiveToken
->CompletionToken
.Status
;
179 if (Status
== EFI_CONNECTION_FIN
) {
181 // Remote host closed connection. Close our end.
184 CloseReceiveEvents ();
186 Status
= mTcpConnection
->Close (mTcpConnection
, &mCloseToken
);
187 ASSERT_EFI_ERROR (Status
);
193 // Add an element to the receive queue
196 NewEntry
= AllocatePool (sizeof (FASTBOOT_TCP_PACKET_LIST
));
197 if (NewEntry
== NULL
) {
198 DEBUG ((EFI_D_ERROR
, "TCP Fastboot: Out of resources\n"));
202 mNextReceiveIndex
= TOKEN_NEXT (mNextReceiveIndex
);
204 if (!EFI_ERROR (Status
)) {
206 = ReceiveToken
->Packet
.RxData
->FragmentTable
[0].FragmentBuffer
;
208 = ReceiveToken
->Packet
.RxData
->FragmentTable
[0].FragmentLength
;
210 // Prepare to receive more data
211 SubmitRecieveToken();
213 // Fatal receive error. Put an entry with NULL in the queue, signifying
214 // to return EFI_DEVICE_ERROR from TcpFastbootTransportReceive.
215 NewEntry
->Buffer
= NULL
;
216 NewEntry
->BufferSize
= 0;
218 DEBUG ((EFI_D_ERROR
, "\nTCP Fastboot Receive error: %r\n", Status
));
221 InsertTailList (&mPacketListHead
, &NewEntry
->Link
);
223 Status
= gBS
->SignalEvent (mReceiveEvent
);
224 ASSERT_EFI_ERROR (Status
);
229 Event notify function to be called when we accept an incoming TCP connection.
239 EFI_TCP4_LISTEN_TOKEN
*AcceptToken
;
243 AcceptToken
= (EFI_TCP4_LISTEN_TOKEN
*) Context
;
244 Status
= AcceptToken
->CompletionToken
.Status
;
246 if (EFI_ERROR (Status
)) {
247 DEBUG ((EFI_D_ERROR
, "TCP Fastboot: Connection Error: %r\n", Status
));
250 DEBUG ((EFI_D_ERROR
, "TCP Fastboot: Connection Received.\n"));
253 // Accepting a new TCP connection creates a new instance of the TCP protocol.
254 // Open it and prepare to receive on it.
257 Status
= gBS
->OpenProtocol (
258 AcceptToken
->NewChildHandle
,
259 &gEfiTcp4ProtocolGuid
,
260 (VOID
**) &mTcpConnection
,
263 EFI_OPEN_PROTOCOL_GET_PROTOCOL
265 if (EFI_ERROR (Status
)) {
266 DEBUG ((EFI_D_ERROR
, "Open TCP Connection: %r\n", Status
));
270 mNextSubmitIndex
= 0;
271 mNextReceiveIndex
= 0;
273 for (Index
= 0; Index
< NUM_RX_TOKENS
; Index
++) {
274 Status
= gBS
->CreateEvent (
279 &(mReceiveToken
[Index
].CompletionToken
.Event
)
281 ASSERT_EFI_ERROR (Status
);
284 for (Index
= 0; Index
< NUM_RX_TOKENS
; Index
++) {
285 SubmitRecieveToken();
290 Set up TCP Fastboot transport: Configure the network device via DHCP then
291 start waiting for a TCP connection on the Fastboot port.
294 TcpFastbootTransportStart (
295 EFI_EVENT ReceiveEvent
299 EFI_HANDLE NetDeviceHandle
;
300 EFI_HANDLE
*HandleBuffer
;
301 EFI_IP4_MODE_DATA Ip4ModeData
;
303 CHAR16 IpAddrString
[16];
306 EFI_TCP4_CONFIG_DATA TcpConfigData
= {
307 0x00, // IPv4 Type of Service
308 255, // IPv4 Time to Live
310 TRUE
, // Use default address
311 { {0, 0, 0, 0} }, // IP Address (ignored - use default)
312 { {0, 0, 0, 0} }, // Subnet mask (ignored - use default)
313 FixedPcdGet32 (PcdAndroidFastbootTcpPort
), // Station port
314 { {0, 0, 0, 0} }, // Remote address: accept any
315 0, // Remote Port: accept any
316 FALSE
// ActiveFlag: be a "server"
318 NULL
// Default advanced TCP options
321 mReceiveEvent
= ReceiveEvent
;
322 InitializeListHead (&mPacketListHead
);
324 mTextOut
->OutputString (mTextOut
, L
"Initialising TCP Fastboot transport...\r\n");
327 // Open a passive TCP instance
330 Status
= gBS
->LocateHandleBuffer (
332 &gEfiTcp4ServiceBindingProtocolGuid
,
337 if (EFI_ERROR (Status
)) {
338 DEBUG ((EFI_D_ERROR
, "Find TCP Service Binding: %r\n", Status
));
342 // We just use the first network device
343 NetDeviceHandle
= HandleBuffer
[0];
345 Status
= gBS
->OpenProtocol (
347 &gEfiTcp4ServiceBindingProtocolGuid
,
348 (VOID
**) &mTcpServiceBinding
,
351 EFI_OPEN_PROTOCOL_GET_PROTOCOL
353 if (EFI_ERROR (Status
)) {
354 DEBUG ((EFI_D_ERROR
, "Open TCP Service Binding: %r\n", Status
));
358 Status
= mTcpServiceBinding
->CreateChild (mTcpServiceBinding
, &mTcpHandle
);
359 if (EFI_ERROR (Status
)) {
360 DEBUG ((EFI_D_ERROR
, "TCP ServiceBinding Create: %r\n", Status
));
364 Status
= gBS
->OpenProtocol (
366 &gEfiTcp4ProtocolGuid
,
367 (VOID
**) &mTcpListener
,
370 EFI_OPEN_PROTOCOL_GET_PROTOCOL
372 if (EFI_ERROR (Status
)) {
373 DEBUG ((EFI_D_ERROR
, "Open TCP Protocol: %r\n", Status
));
377 // Set up re-usable tokens
380 for (Index
= 0; Index
< NUM_RX_TOKENS
; Index
++) {
381 mRxData
[Index
].UrgentFlag
= FALSE
;
382 mRxData
[Index
].FragmentCount
= 1;
383 mReceiveToken
[Index
].Packet
.RxData
= &mRxData
[Index
];
387 mTxData
.Urgent
= FALSE
;
388 mTxData
.FragmentCount
= 1;
389 mTransmitToken
.Packet
.TxData
= &mTxData
;
391 Status
= gBS
->CreateEvent (
396 &mAcceptToken
.CompletionToken
.Event
398 ASSERT_EFI_ERROR (Status
);
400 Status
= gBS
->CreateEvent (
405 &mCloseToken
.CompletionToken
.Event
407 ASSERT_EFI_ERROR (Status
);
410 // Configure the TCP instance
413 Status
= mTcpListener
->Configure (mTcpListener
, &TcpConfigData
);
414 if (Status
== EFI_NO_MAPPING
) {
415 // Wait until the IP configuration process (probably DHCP) has finished
417 Status
= mTcpListener
->GetModeData (mTcpListener
,
422 ASSERT_EFI_ERROR (Status
);
423 } while (!Ip4ModeData
.IsConfigured
);
424 Status
= mTcpListener
->Configure (mTcpListener
, &TcpConfigData
);
425 } else if (EFI_ERROR (Status
)) {
426 DEBUG ((EFI_D_ERROR
, "TCP Configure: %r\n", Status
));
431 // Tell the user our address and hostname
433 IP4_ADDR_TO_STRING (Ip4ModeData
.ConfigData
.StationAddress
, IpAddrString
);
435 mTextOut
->OutputString (mTextOut
, L
"TCP Fastboot transport configured.");
436 mTextOut
->OutputString (mTextOut
, L
"\r\nIP address: ");
437 mTextOut
->OutputString (mTextOut
,IpAddrString
);
438 mTextOut
->OutputString (mTextOut
, L
"\r\n");
441 // Start listening for a connection
444 Status
= mTcpListener
->Accept (mTcpListener
, &mAcceptToken
);
445 if (EFI_ERROR (Status
)) {
446 DEBUG ((EFI_D_ERROR
, "TCP Accept: %r\n", Status
));
450 mTextOut
->OutputString (mTextOut
, L
"TCP Fastboot transport initialised.\r\n");
452 FreePool (HandleBuffer
);
458 TcpFastbootTransportStop (
462 EFI_TCP4_CLOSE_TOKEN CloseToken
;
465 FASTBOOT_TCP_PACKET_LIST
*Entry
;
466 FASTBOOT_TCP_PACKET_LIST
*NextEntry
;
468 // Close any existing TCP connection, blocking until it's done.
469 if (mTcpConnection
!= NULL
) {
470 CloseReceiveEvents ();
472 CloseToken
.AbortOnClose
= FALSE
;
474 Status
= gBS
->CreateEvent (0, 0, NULL
, NULL
, &CloseToken
.CompletionToken
.Event
);
475 ASSERT_EFI_ERROR (Status
);
477 Status
= mTcpConnection
->Close (mTcpConnection
, &CloseToken
);
478 ASSERT_EFI_ERROR (Status
);
480 Status
= gBS
->WaitForEvent (
482 &CloseToken
.CompletionToken
.Event
,
485 ASSERT_EFI_ERROR (Status
);
487 ASSERT_EFI_ERROR (CloseToken
.CompletionToken
.Status
);
489 // Possible bug in EDK2 TCP4 driver: closing a connection doesn't remove its
490 // PCB from the list of live connections. Subsequent attempts to Configure()
491 // a TCP instance with the same local port will fail with INVALID_PARAMETER.
492 // Calling Configure with NULL is a workaround for this issue.
493 Status
= mTcpConnection
->Configure (mTcpConnection
, NULL
);
494 ASSERT_EFI_ERROR (Status
);
498 gBS
->CloseEvent (mAcceptToken
.CompletionToken
.Event
);
500 // Stop listening for connections.
501 // Ideally we would do this with Cancel, but it isn't implemented by EDK2.
502 // So we just "reset this TCPv4 instance brutally".
503 Status
= mTcpListener
->Configure (mTcpListener
, NULL
);
504 ASSERT_EFI_ERROR (Status
);
506 Status
= mTcpServiceBinding
->DestroyChild (mTcpServiceBinding
, &mTcpHandle
);
508 // Free any data the user didn't pick up
509 Entry
= (FASTBOOT_TCP_PACKET_LIST
*) GetFirstNode (&mPacketListHead
);
510 while (!IsNull (&mPacketListHead
, &Entry
->Link
)) {
511 NextEntry
= (FASTBOOT_TCP_PACKET_LIST
*) GetNextNode (&mPacketListHead
, &Entry
->Link
);
513 RemoveEntryList (&Entry
->Link
);
515 FreePool (Entry
->Buffer
);
526 Event notify function for when data has been sent. Free resources and report
528 Context should point to the transmit IO token passed to
529 TcpConnection->Transmit.
540 Status
= mTransmitToken
.CompletionToken
.Status
;
541 if (EFI_ERROR (Status
)) {
542 DEBUG ((EFI_D_ERROR
, "TCP Fastboot transmit result: %r\n", Status
));
543 gBS
->SignalEvent (*(EFI_EVENT
*) Context
);
546 FreePool (mTransmitToken
.Packet
.TxData
->FragmentTable
[0].FragmentBuffer
);
550 TcpFastbootTransportSend (
552 IN CONST VOID
*Buffer
,
553 IN EFI_EVENT
*FatalErrorEvent
558 if (BufferSize
> 512) {
559 return EFI_INVALID_PARAMETER
;
563 // Build transmit IO token
566 // Create an event so we are notified when a transmission is complete.
567 // We use this to free resources and report errors.
568 Status
= gBS
->CreateEvent (
573 &mTransmitToken
.CompletionToken
.Event
575 ASSERT_EFI_ERROR (Status
);
577 mTxData
.DataLength
= BufferSize
;
579 mTxData
.FragmentTable
[0].FragmentLength
= BufferSize
;
580 mTxData
.FragmentTable
[0].FragmentBuffer
= AllocateCopyPool (
585 Status
= mTcpConnection
->Transmit (mTcpConnection
, &mTransmitToken
);
586 if (EFI_ERROR (Status
)) {
587 DEBUG ((EFI_D_ERROR
, "TCP Transmit: %r\n", Status
));
596 TcpFastbootTransportReceive (
597 OUT UINTN
*BufferSize
,
601 FASTBOOT_TCP_PACKET_LIST
*Entry
;
603 if (IsListEmpty (&mPacketListHead
)) {
604 return EFI_NOT_READY
;
607 Entry
= (FASTBOOT_TCP_PACKET_LIST
*) GetFirstNode (&mPacketListHead
);
609 if (Entry
->Buffer
== NULL
) {
610 // There was an error receiving this packet.
611 return EFI_DEVICE_ERROR
;
614 *Buffer
= Entry
->Buffer
;
615 *BufferSize
= Entry
->BufferSize
;
617 RemoveEntryList (&Entry
->Link
);
623 FASTBOOT_TRANSPORT_PROTOCOL mTransportProtocol
= {
624 TcpFastbootTransportStart
,
625 TcpFastbootTransportStop
,
626 TcpFastbootTransportSend
,
627 TcpFastbootTransportReceive
631 TcpFastbootTransportEntryPoint (
632 IN EFI_HANDLE ImageHandle
,
633 IN EFI_SYSTEM_TABLE
*SystemTable
639 Status
= gBS
->LocateProtocol(
640 &gEfiSimpleTextOutProtocolGuid
,
644 if (EFI_ERROR (Status
)) {
645 DEBUG ((EFI_D_ERROR
, "Fastboot: Open Text Output Protocol: %r\n", Status
));
649 Status
= gBS
->InstallProtocolInterface (
651 &gAndroidFastbootTransportProtocolGuid
,
652 EFI_NATIVE_INTERFACE
,
655 if (EFI_ERROR (Status
)) {
656 DEBUG ((EFI_D_ERROR
, "Fastboot: Install transport Protocol: %r\n", Status
));