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 ((DEBUG_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 ((DEBUG_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 ((DEBUG_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 ((DEBUG_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 ((DEBUG_ERROR
, "\nTCP Fastboot Receive error: %r\n", Status
));
221 InsertTailList (&mPacketListHead
, &NewEntry
->Link
);
223 Status
= gBS
->SignalEvent (mReceiveEvent
);
224 ASSERT_EFI_ERROR (Status
);
228 Event notify function to be called when we accept an incoming TCP connection.
238 EFI_TCP4_LISTEN_TOKEN
*AcceptToken
;
242 AcceptToken
= (EFI_TCP4_LISTEN_TOKEN
*)Context
;
243 Status
= AcceptToken
->CompletionToken
.Status
;
245 if (EFI_ERROR (Status
)) {
246 DEBUG ((DEBUG_ERROR
, "TCP Fastboot: Connection Error: %r\n", Status
));
250 DEBUG ((DEBUG_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 ((DEBUG_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
313 }, // IP Address (ignored - use default)
316 }, // Subnet mask (ignored - use default)
317 FixedPcdGet32 (PcdAndroidFastbootTcpPort
), // Station port
320 }, // Remote address: accept any
321 0, // Remote Port: accept any
322 FALSE
// ActiveFlag: be a "server"
324 NULL
// Default advanced TCP options
327 mReceiveEvent
= ReceiveEvent
;
328 InitializeListHead (&mPacketListHead
);
330 mTextOut
->OutputString (mTextOut
, L
"Initialising TCP Fastboot transport...\r\n");
333 // Open a passive TCP instance
336 Status
= gBS
->LocateHandleBuffer (
338 &gEfiTcp4ServiceBindingProtocolGuid
,
343 if (EFI_ERROR (Status
)) {
344 DEBUG ((DEBUG_ERROR
, "Find TCP Service Binding: %r\n", Status
));
348 // We just use the first network device
349 NetDeviceHandle
= HandleBuffer
[0];
351 Status
= gBS
->OpenProtocol (
353 &gEfiTcp4ServiceBindingProtocolGuid
,
354 (VOID
**)&mTcpServiceBinding
,
357 EFI_OPEN_PROTOCOL_GET_PROTOCOL
359 if (EFI_ERROR (Status
)) {
360 DEBUG ((DEBUG_ERROR
, "Open TCP Service Binding: %r\n", Status
));
364 Status
= mTcpServiceBinding
->CreateChild (mTcpServiceBinding
, &mTcpHandle
);
365 if (EFI_ERROR (Status
)) {
366 DEBUG ((DEBUG_ERROR
, "TCP ServiceBinding Create: %r\n", Status
));
370 Status
= gBS
->OpenProtocol (
372 &gEfiTcp4ProtocolGuid
,
373 (VOID
**)&mTcpListener
,
376 EFI_OPEN_PROTOCOL_GET_PROTOCOL
378 if (EFI_ERROR (Status
)) {
379 DEBUG ((DEBUG_ERROR
, "Open TCP Protocol: %r\n", Status
));
383 // Set up re-usable tokens
386 for (Index
= 0; Index
< NUM_RX_TOKENS
; Index
++) {
387 mRxData
[Index
].UrgentFlag
= FALSE
;
388 mRxData
[Index
].FragmentCount
= 1;
389 mReceiveToken
[Index
].Packet
.RxData
= &mRxData
[Index
];
393 mTxData
.Urgent
= FALSE
;
394 mTxData
.FragmentCount
= 1;
395 mTransmitToken
.Packet
.TxData
= &mTxData
;
397 Status
= gBS
->CreateEvent (
402 &mAcceptToken
.CompletionToken
.Event
404 ASSERT_EFI_ERROR (Status
);
406 Status
= gBS
->CreateEvent (
411 &mCloseToken
.CompletionToken
.Event
413 ASSERT_EFI_ERROR (Status
);
416 // Configure the TCP instance
419 Status
= mTcpListener
->Configure (mTcpListener
, &TcpConfigData
);
420 if (Status
== EFI_NO_MAPPING
) {
421 // Wait until the IP configuration process (probably DHCP) has finished
423 Status
= mTcpListener
->GetModeData (
431 ASSERT_EFI_ERROR (Status
);
432 } while (!Ip4ModeData
.IsConfigured
);
434 Status
= mTcpListener
->Configure (mTcpListener
, &TcpConfigData
);
435 } else if (EFI_ERROR (Status
)) {
436 DEBUG ((DEBUG_ERROR
, "TCP Configure: %r\n", Status
));
441 // Tell the user our address and hostname
443 IP4_ADDR_TO_STRING (Ip4ModeData
.ConfigData
.StationAddress
, IpAddrString
);
445 mTextOut
->OutputString (mTextOut
, L
"TCP Fastboot transport configured.");
446 mTextOut
->OutputString (mTextOut
, L
"\r\nIP address: ");
447 mTextOut
->OutputString (mTextOut
, IpAddrString
);
448 mTextOut
->OutputString (mTextOut
, L
"\r\n");
451 // Start listening for a connection
454 Status
= mTcpListener
->Accept (mTcpListener
, &mAcceptToken
);
455 if (EFI_ERROR (Status
)) {
456 DEBUG ((DEBUG_ERROR
, "TCP Accept: %r\n", Status
));
460 mTextOut
->OutputString (mTextOut
, L
"TCP Fastboot transport initialised.\r\n");
462 FreePool (HandleBuffer
);
468 TcpFastbootTransportStop (
472 EFI_TCP4_CLOSE_TOKEN CloseToken
;
475 FASTBOOT_TCP_PACKET_LIST
*Entry
;
476 FASTBOOT_TCP_PACKET_LIST
*NextEntry
;
478 // Close any existing TCP connection, blocking until it's done.
479 if (mTcpConnection
!= NULL
) {
480 CloseReceiveEvents ();
482 CloseToken
.AbortOnClose
= FALSE
;
484 Status
= gBS
->CreateEvent (0, 0, NULL
, NULL
, &CloseToken
.CompletionToken
.Event
);
485 ASSERT_EFI_ERROR (Status
);
487 Status
= mTcpConnection
->Close (mTcpConnection
, &CloseToken
);
488 ASSERT_EFI_ERROR (Status
);
490 Status
= gBS
->WaitForEvent (
492 &CloseToken
.CompletionToken
.Event
,
495 ASSERT_EFI_ERROR (Status
);
497 ASSERT_EFI_ERROR (CloseToken
.CompletionToken
.Status
);
499 // Possible bug in EDK2 TCP4 driver: closing a connection doesn't remove its
500 // PCB from the list of live connections. Subsequent attempts to Configure()
501 // a TCP instance with the same local port will fail with INVALID_PARAMETER.
502 // Calling Configure with NULL is a workaround for this issue.
503 Status
= mTcpConnection
->Configure (mTcpConnection
, NULL
);
504 ASSERT_EFI_ERROR (Status
);
507 gBS
->CloseEvent (mAcceptToken
.CompletionToken
.Event
);
509 // Stop listening for connections.
510 // Ideally we would do this with Cancel, but it isn't implemented by EDK2.
511 // So we just "reset this TCPv4 instance brutally".
512 Status
= mTcpListener
->Configure (mTcpListener
, NULL
);
513 ASSERT_EFI_ERROR (Status
);
515 Status
= mTcpServiceBinding
->DestroyChild (mTcpServiceBinding
, mTcpHandle
);
517 // Free any data the user didn't pick up
518 Entry
= (FASTBOOT_TCP_PACKET_LIST
*)GetFirstNode (&mPacketListHead
);
519 while (!IsNull (&mPacketListHead
, &Entry
->Link
)) {
520 NextEntry
= (FASTBOOT_TCP_PACKET_LIST
*)GetNextNode (&mPacketListHead
, &Entry
->Link
);
522 RemoveEntryList (&Entry
->Link
);
524 FreePool (Entry
->Buffer
);
536 Event notify function for when data has been sent. Free resources and report
538 Context should point to the transmit IO token passed to
539 TcpConnection->Transmit.
550 Status
= mTransmitToken
.CompletionToken
.Status
;
551 if (EFI_ERROR (Status
)) {
552 DEBUG ((DEBUG_ERROR
, "TCP Fastboot transmit result: %r\n", Status
));
553 gBS
->SignalEvent (*(EFI_EVENT
*)Context
);
556 FreePool (mTransmitToken
.Packet
.TxData
->FragmentTable
[0].FragmentBuffer
);
560 TcpFastbootTransportSend (
562 IN CONST VOID
*Buffer
,
563 IN EFI_EVENT
*FatalErrorEvent
568 if (BufferSize
> 512) {
569 return EFI_INVALID_PARAMETER
;
573 // Build transmit IO token
576 // Create an event so we are notified when a transmission is complete.
577 // We use this to free resources and report errors.
578 Status
= gBS
->CreateEvent (
583 &mTransmitToken
.CompletionToken
.Event
585 ASSERT_EFI_ERROR (Status
);
587 mTxData
.DataLength
= BufferSize
;
589 mTxData
.FragmentTable
[0].FragmentLength
= BufferSize
;
590 mTxData
.FragmentTable
[0].FragmentBuffer
= AllocateCopyPool (
595 Status
= mTcpConnection
->Transmit (mTcpConnection
, &mTransmitToken
);
596 if (EFI_ERROR (Status
)) {
597 DEBUG ((DEBUG_ERROR
, "TCP Transmit: %r\n", Status
));
605 TcpFastbootTransportReceive (
606 OUT UINTN
*BufferSize
,
610 FASTBOOT_TCP_PACKET_LIST
*Entry
;
612 if (IsListEmpty (&mPacketListHead
)) {
613 return EFI_NOT_READY
;
616 Entry
= (FASTBOOT_TCP_PACKET_LIST
*)GetFirstNode (&mPacketListHead
);
618 if (Entry
->Buffer
== NULL
) {
619 // There was an error receiving this packet.
620 return EFI_DEVICE_ERROR
;
623 *Buffer
= Entry
->Buffer
;
624 *BufferSize
= Entry
->BufferSize
;
626 RemoveEntryList (&Entry
->Link
);
632 FASTBOOT_TRANSPORT_PROTOCOL mTransportProtocol
= {
633 TcpFastbootTransportStart
,
634 TcpFastbootTransportStop
,
635 TcpFastbootTransportSend
,
636 TcpFastbootTransportReceive
640 TcpFastbootTransportEntryPoint (
641 IN EFI_HANDLE ImageHandle
,
642 IN EFI_SYSTEM_TABLE
*SystemTable
647 Status
= gBS
->LocateProtocol (
648 &gEfiSimpleTextOutProtocolGuid
,
652 if (EFI_ERROR (Status
)) {
653 DEBUG ((DEBUG_ERROR
, "Fastboot: Open Text Output Protocol: %r\n", Status
));
657 Status
= gBS
->InstallProtocolInterface (
659 &gAndroidFastbootTransportProtocolGuid
,
660 EFI_NATIVE_INTERFACE
,
663 if (EFI_ERROR (Status
)) {
664 DEBUG ((DEBUG_ERROR
, "Fastboot: Install transport Protocol: %r\n", Status
));