1 /** @file 2 # 3 # Copyright (c) 2014, ARM Ltd. All rights reserved.<BR> 4 # 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. 11 # 12 # 13 #**/ 14 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> 20 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> 29 30 #define IP4_ADDR_TO_STRING(IpAddr, IpAddrString) UnicodeSPrint ( \ 31 IpAddrString, \ 32 16 * 2, \ 33 L"%d.%d.%d.%d", \ 34 IpAddr.Addr[0], \ 35 IpAddr.Addr[1], \ 36 IpAddr.Addr[2], \ 37 IpAddr.Addr[3] \ 38 ); 39 40 // Fastboot says max packet size is 512, but FASTBOOT_TRANSPORT_PROTOCOL 41 // doesn't place a limit on the size of buffers returned by Receive. 42 // (This isn't actually a packet size - it's just the size of the buffers we 43 // pass to the TCP driver to fill with received data.) 44 // We can achieve much better performance by doing this in larger chunks. 45 #define RX_FRAGMENT_SIZE 2048 46 47 STATIC EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *mTextOut; 48 49 STATIC EFI_TCP4_PROTOCOL *mTcpConnection; 50 STATIC EFI_TCP4_PROTOCOL *mTcpListener; 51 52 STATIC EFI_EVENT mReceiveEvent; 53 54 STATIC EFI_SERVICE_BINDING_PROTOCOL *mTcpServiceBinding; 55 STATIC EFI_HANDLE mTcpHandle = NULL; 56 57 // We only ever use one IO token for receive and one for transmit. To save 58 // repeatedly allocating and freeing, just allocate statically and re-use. 59 #define NUM_RX_TOKENS 16 60 #define TOKEN_NEXT(Index) (((Index) + 1) % NUM_RX_TOKENS) 61 62 STATIC UINTN mNextSubmitIndex; 63 STATIC UINTN mNextReceiveIndex; 64 STATIC EFI_TCP4_IO_TOKEN mReceiveToken[NUM_RX_TOKENS]; 65 STATIC EFI_TCP4_RECEIVE_DATA mRxData[NUM_RX_TOKENS]; 66 STATIC EFI_TCP4_IO_TOKEN mTransmitToken; 67 STATIC EFI_TCP4_TRANSMIT_DATA mTxData; 68 // We also reuse the accept token 69 STATIC EFI_TCP4_LISTEN_TOKEN mAcceptToken; 70 // .. and the close token 71 STATIC EFI_TCP4_CLOSE_TOKEN mCloseToken; 72 73 // List type for queued received packets 74 typedef struct _FASTBOOT_TCP_PACKET_LIST { 75 LIST_ENTRY Link; 76 VOID *Buffer; 77 UINTN BufferSize; 78 } FASTBOOT_TCP_PACKET_LIST; 79 80 STATIC LIST_ENTRY mPacketListHead; 81 82 STATIC 83 VOID 84 EFIAPI 85 DataReceived ( 86 IN EFI_EVENT Event, 87 IN VOID *Context 88 ); 89 90 /* 91 Helper function to set up a receive IO token and call Tcp->Receive 92 */ 93 STATIC 94 EFI_STATUS 95 SubmitRecieveToken ( 96 VOID 97 ) 98 { 99 EFI_STATUS Status; 100 VOID *FragmentBuffer; 101 102 Status = EFI_SUCCESS; 103 104 FragmentBuffer = AllocatePool (RX_FRAGMENT_SIZE); 105 ASSERT (FragmentBuffer != NULL); 106 if (FragmentBuffer == NULL) { 107 DEBUG ((EFI_D_ERROR, "TCP Fastboot out of resources")); 108 return EFI_OUT_OF_RESOURCES; 109 } 110 111 mRxData[mNextSubmitIndex].DataLength = RX_FRAGMENT_SIZE; 112 mRxData[mNextSubmitIndex].FragmentTable[0].FragmentLength = RX_FRAGMENT_SIZE; 113 mRxData[mNextSubmitIndex].FragmentTable[0].FragmentBuffer = FragmentBuffer; 114 115 Status = mTcpConnection->Receive (mTcpConnection, &mReceiveToken[mNextSubmitIndex]); 116 if (EFI_ERROR (Status)) { 117 DEBUG ((EFI_D_ERROR, "TCP Receive: %r\n", Status)); 118 FreePool (FragmentBuffer); 119 } 120 121 mNextSubmitIndex = TOKEN_NEXT (mNextSubmitIndex); 122 return Status; 123 } 124 125 /* 126 Event notify function for when we have closed our TCP connection. 127 We can now start listening for another connection. 128 */ 129 STATIC 130 VOID 131 ConnectionClosed ( 132 IN EFI_EVENT Event, 133 IN VOID *Context 134 ) 135 { 136 EFI_STATUS Status; 137 138 // Possible bug in EDK2 TCP4 driver: closing a connection doesn't remove its 139 // PCB from the list of live connections. Subsequent attempts to Configure() 140 // a TCP instance with the same local port will fail with INVALID_PARAMETER. 141 // Calling Configure with NULL is a workaround for this issue. 142 Status = mTcpConnection->Configure (mTcpConnection, NULL); 143 144 mTcpConnection = NULL; 145 146 Status = mTcpListener->Accept (mTcpListener, &mAcceptToken); 147 if (EFI_ERROR (Status)) { 148 DEBUG ((EFI_D_ERROR, "TCP Accept: %r\n", Status)); 149 } 150 } 151 152 STATIC 153 VOID 154 CloseReceiveEvents ( 155 VOID 156 ) 157 { 158 UINTN Index; 159 160 for (Index = 0; Index < NUM_RX_TOKENS; Index++) { 161 gBS->CloseEvent (mReceiveToken[Index].CompletionToken.Event); 162 } 163 } 164 165 /* 166 Event notify function to be called when we receive TCP data. 167 */ 168 STATIC 169 VOID 170 EFIAPI 171 DataReceived ( 172 IN EFI_EVENT Event, 173 IN VOID *Context 174 ) 175 { 176 EFI_STATUS Status; 177 FASTBOOT_TCP_PACKET_LIST *NewEntry; 178 EFI_TCP4_IO_TOKEN *ReceiveToken; 179 180 ReceiveToken = &mReceiveToken[mNextReceiveIndex]; 181 182 Status = ReceiveToken->CompletionToken.Status; 183 184 if (Status == EFI_CONNECTION_FIN) { 185 // 186 // Remote host closed connection. Close our end. 187 // 188 189 CloseReceiveEvents (); 190 191 Status = mTcpConnection->Close (mTcpConnection, &mCloseToken); 192 ASSERT_EFI_ERROR (Status); 193 194 return; 195 } 196 197 // 198 // Add an element to the receive queue 199 // 200 201 NewEntry = AllocatePool (sizeof (FASTBOOT_TCP_PACKET_LIST)); 202 if (NewEntry == NULL) { 203 DEBUG ((EFI_D_ERROR, "TCP Fastboot: Out of resources\n")); 204 return; 205 } 206 207 mNextReceiveIndex = TOKEN_NEXT (mNextReceiveIndex); 208 209 if (!EFI_ERROR (Status)) { 210 NewEntry->Buffer 211 = ReceiveToken->Packet.RxData->FragmentTable[0].FragmentBuffer; 212 NewEntry->BufferSize 213 = ReceiveToken->Packet.RxData->FragmentTable[0].FragmentLength; 214 215 // Prepare to receive more data 216 SubmitRecieveToken(); 217 } else { 218 // Fatal receive error. Put an entry with NULL in the queue, signifying 219 // to return EFI_DEVICE_ERROR from TcpFastbootTransportReceive. 220 NewEntry->Buffer = NULL; 221 NewEntry->BufferSize = 0; 222 223 DEBUG ((EFI_D_ERROR, "\nTCP Fastboot Receive error: %r\n", Status)); 224 } 225 226 InsertTailList (&mPacketListHead, &NewEntry->Link); 227 228 Status = gBS->SignalEvent (mReceiveEvent); 229 ASSERT_EFI_ERROR (Status); 230 } 231 232 233 /* 234 Event notify function to be called when we accept an incoming TCP connection. 235 */ 236 STATIC 237 VOID 238 EFIAPI 239 ConnectionAccepted ( 240 IN EFI_EVENT Event, 241 IN VOID *Context 242 ) 243 { 244 EFI_TCP4_LISTEN_TOKEN *AcceptToken; 245 EFI_STATUS Status; 246 UINTN Index; 247 248 AcceptToken = (EFI_TCP4_LISTEN_TOKEN *) Context; 249 Status = AcceptToken->CompletionToken.Status; 250 251 if (EFI_ERROR (Status)) { 252 DEBUG ((EFI_D_ERROR, "TCP Fastboot: Connection Error: %r\n", Status)); 253 return; 254 } 255 DEBUG ((EFI_D_ERROR, "TCP Fastboot: Connection Received.\n")); 256 257 // 258 // Accepting a new TCP connection creates a new instance of the TCP protocol. 259 // Open it and prepare to receive on it. 260 // 261 262 Status = gBS->OpenProtocol ( 263 AcceptToken->NewChildHandle, 264 &gEfiTcp4ProtocolGuid, 265 (VOID **) &mTcpConnection, 266 gImageHandle, 267 NULL, 268 EFI_OPEN_PROTOCOL_GET_PROTOCOL 269 ); 270 if (EFI_ERROR (Status)) { 271 DEBUG ((EFI_D_ERROR, "Open TCP Connection: %r\n", Status)); 272 return; 273 } 274 275 mNextSubmitIndex = 0; 276 mNextReceiveIndex = 0; 277 278 for (Index = 0; Index < NUM_RX_TOKENS; Index++) { 279 Status = gBS->CreateEvent ( 280 EVT_NOTIFY_SIGNAL, 281 TPL_CALLBACK, 282 DataReceived, 283 NULL, 284 &(mReceiveToken[Index].CompletionToken.Event) 285 ); 286 ASSERT_EFI_ERROR (Status); 287 } 288 289 for (Index = 0; Index < NUM_RX_TOKENS; Index++) { 290 SubmitRecieveToken(); 291 } 292 } 293 294 /* 295 Set up TCP Fastboot transport: Configure the network device via DHCP then 296 start waiting for a TCP connection on the Fastboot port. 297 */ 298 EFI_STATUS 299 TcpFastbootTransportStart ( 300 EFI_EVENT ReceiveEvent 301 ) 302 { 303 EFI_STATUS Status; 304 EFI_HANDLE NetDeviceHandle; 305 EFI_HANDLE *HandleBuffer; 306 EFI_IP4_MODE_DATA Ip4ModeData; 307 UINTN NumHandles; 308 CHAR16 IpAddrString[16]; 309 UINTN Index; 310 311 EFI_TCP4_CONFIG_DATA TcpConfigData = { 312 0x00, // IPv4 Type of Service 313 255, // IPv4 Time to Live 314 { // AccessPoint: 315 TRUE, // Use default address 316 { {0, 0, 0, 0} }, // IP Address (ignored - use default) 317 { {0, 0, 0, 0} }, // Subnet mask (ignored - use default) 318 FixedPcdGet32 (PcdAndroidFastbootTcpPort), // Station port 319 { {0, 0, 0, 0} }, // Remote address: accept any 320 0, // Remote Port: accept any 321 FALSE // ActiveFlag: be a "server" 322 }, 323 NULL // Default advanced TCP options 324 }; 325 326 mReceiveEvent = ReceiveEvent; 327 InitializeListHead (&mPacketListHead); 328 329 mTextOut->OutputString (mTextOut, L"Initialising TCP Fastboot transport...\r\n"); 330 331 // 332 // Open a passive TCP instance 333 // 334 335 Status = gBS->LocateHandleBuffer ( 336 ByProtocol, 337 &gEfiTcp4ServiceBindingProtocolGuid, 338 NULL, 339 &NumHandles, 340 &HandleBuffer 341 ); 342 if (EFI_ERROR (Status)) { 343 DEBUG ((EFI_D_ERROR, "Find TCP Service Binding: %r\n", Status)); 344 return Status; 345 } 346 347 // We just use the first network device 348 NetDeviceHandle = HandleBuffer[0]; 349 350 Status = gBS->OpenProtocol ( 351 NetDeviceHandle, 352 &gEfiTcp4ServiceBindingProtocolGuid, 353 (VOID **) &mTcpServiceBinding, 354 gImageHandle, 355 NULL, 356 EFI_OPEN_PROTOCOL_GET_PROTOCOL 357 ); 358 if (EFI_ERROR (Status)) { 359 DEBUG ((EFI_D_ERROR, "Open TCP Service Binding: %r\n", Status)); 360 return Status; 361 } 362 363 Status = mTcpServiceBinding->CreateChild (mTcpServiceBinding, &mTcpHandle); 364 if (EFI_ERROR (Status)) { 365 DEBUG ((EFI_D_ERROR, "TCP ServiceBinding Create: %r\n", Status)); 366 return Status; 367 } 368 369 Status = gBS->OpenProtocol ( 370 mTcpHandle, 371 &gEfiTcp4ProtocolGuid, 372 (VOID **) &mTcpListener, 373 gImageHandle, 374 NULL, 375 EFI_OPEN_PROTOCOL_GET_PROTOCOL 376 ); 377 if (EFI_ERROR (Status)) { 378 DEBUG ((EFI_D_ERROR, "Open TCP Protocol: %r\n", Status)); 379 } 380 381 // 382 // Set up re-usable tokens 383 // 384 385 for (Index = 0; Index < NUM_RX_TOKENS; Index++) { 386 mRxData[Index].UrgentFlag = FALSE; 387 mRxData[Index].FragmentCount = 1; 388 mReceiveToken[Index].Packet.RxData = &mRxData[Index]; 389 } 390 391 mTxData.Push = TRUE; 392 mTxData.Urgent = FALSE; 393 mTxData.FragmentCount = 1; 394 mTransmitToken.Packet.TxData = &mTxData; 395 396 Status = gBS->CreateEvent ( 397 EVT_NOTIFY_SIGNAL, 398 TPL_CALLBACK, 399 ConnectionAccepted, 400 &mAcceptToken, 401 &mAcceptToken.CompletionToken.Event 402 ); 403 ASSERT_EFI_ERROR (Status); 404 405 Status = gBS->CreateEvent ( 406 EVT_NOTIFY_SIGNAL, 407 TPL_CALLBACK, 408 ConnectionClosed, 409 &mCloseToken, 410 &mCloseToken.CompletionToken.Event 411 ); 412 ASSERT_EFI_ERROR (Status); 413 414 // 415 // Configure the TCP instance 416 // 417 418 Status = mTcpListener->Configure (mTcpListener, &TcpConfigData); 419 if (Status == EFI_NO_MAPPING) { 420 // Wait until the IP configuration process (probably DHCP) has finished 421 do { 422 Status = mTcpListener->GetModeData (mTcpListener, 423 NULL, NULL, 424 &Ip4ModeData, 425 NULL, NULL 426 ); 427 ASSERT_EFI_ERROR (Status); 428 } while (!Ip4ModeData.IsConfigured); 429 Status = mTcpListener->Configure (mTcpListener, &TcpConfigData); 430 } else if (EFI_ERROR (Status)) { 431 DEBUG ((EFI_D_ERROR, "TCP Configure: %r\n", Status)); 432 return Status; 433 } 434 435 // 436 // Tell the user our address and hostname 437 // 438 IP4_ADDR_TO_STRING (Ip4ModeData.ConfigData.StationAddress, IpAddrString); 439 440 mTextOut->OutputString (mTextOut, L"TCP Fastboot transport configured."); 441 mTextOut->OutputString (mTextOut, L"\r\nIP address: "); 442 mTextOut->OutputString (mTextOut ,IpAddrString); 443 mTextOut->OutputString (mTextOut, L"\r\n"); 444 445 // 446 // Start listening for a connection 447 // 448 449 Status = mTcpListener->Accept (mTcpListener, &mAcceptToken); 450 if (EFI_ERROR (Status)) { 451 DEBUG ((EFI_D_ERROR, "TCP Accept: %r\n", Status)); 452 return Status; 453 } 454 455 mTextOut->OutputString (mTextOut, L"TCP Fastboot transport initialised.\r\n"); 456 457 FreePool (HandleBuffer); 458 459 return EFI_SUCCESS; 460 } 461 462 EFI_STATUS 463 TcpFastbootTransportStop ( 464 VOID 465 ) 466 { 467 EFI_TCP4_CLOSE_TOKEN CloseToken; 468 EFI_STATUS Status; 469 UINTN EventIndex; 470 FASTBOOT_TCP_PACKET_LIST *Entry; 471 FASTBOOT_TCP_PACKET_LIST *NextEntry; 472 473 // Close any existing TCP connection, blocking until it's done. 474 if (mTcpConnection != NULL) { 475 CloseReceiveEvents (); 476 477 CloseToken.AbortOnClose = FALSE; 478 479 Status = gBS->CreateEvent (0, 0, NULL, NULL, &CloseToken.CompletionToken.Event); 480 ASSERT_EFI_ERROR (Status); 481 482 Status = mTcpConnection->Close (mTcpConnection, &CloseToken); 483 ASSERT_EFI_ERROR (Status); 484 485 Status = gBS->WaitForEvent ( 486 1, 487 &CloseToken.CompletionToken.Event, 488 &EventIndex 489 ); 490 ASSERT_EFI_ERROR (Status); 491 492 ASSERT_EFI_ERROR (CloseToken.CompletionToken.Status); 493 494 // Possible bug in EDK2 TCP4 driver: closing a connection doesn't remove its 495 // PCB from the list of live connections. Subsequent attempts to Configure() 496 // a TCP instance with the same local port will fail with INVALID_PARAMETER. 497 // Calling Configure with NULL is a workaround for this issue. 498 Status = mTcpConnection->Configure (mTcpConnection, NULL); 499 ASSERT_EFI_ERROR (Status); 500 } 501 502 503 gBS->CloseEvent (mAcceptToken.CompletionToken.Event); 504 505 // Stop listening for connections. 506 // Ideally we would do this with Cancel, but it isn't implemented by EDK2. 507 // So we just "reset this TCPv4 instance brutally". 508 Status = mTcpListener->Configure (mTcpListener, NULL); 509 ASSERT_EFI_ERROR (Status); 510 511 Status = mTcpServiceBinding->DestroyChild (mTcpServiceBinding, &mTcpHandle); 512 513 // Free any data the user didn't pick up 514 Entry = (FASTBOOT_TCP_PACKET_LIST *) GetFirstNode (&mPacketListHead); 515 while (!IsNull (&mPacketListHead, &Entry->Link)) { 516 NextEntry = (FASTBOOT_TCP_PACKET_LIST *) GetNextNode (&mPacketListHead, &Entry->Link); 517 518 RemoveEntryList (&Entry->Link); 519 if (Entry->Buffer) { 520 FreePool (Entry->Buffer); 521 } 522 FreePool (Entry); 523 524 Entry = NextEntry; 525 } 526 527 return EFI_SUCCESS; 528 } 529 530 /* 531 Event notify function for when data has been sent. Free resources and report 532 errors. 533 Context should point to the transmit IO token passed to 534 TcpConnection->Transmit. 535 */ 536 STATIC 537 VOID 538 DataSent ( 539 EFI_EVENT Event, 540 VOID *Context 541 ) 542 { 543 EFI_STATUS Status; 544 545 Status = mTransmitToken.CompletionToken.Status; 546 if (EFI_ERROR (Status)) { 547 DEBUG ((EFI_D_ERROR, "TCP Fastboot transmit result: %r\n", Status)); 548 gBS->SignalEvent (*(EFI_EVENT *) Context); 549 } 550 551 FreePool (mTransmitToken.Packet.TxData->FragmentTable[0].FragmentBuffer); 552 } 553 554 EFI_STATUS 555 TcpFastbootTransportSend ( 556 IN UINTN BufferSize, 557 IN CONST VOID *Buffer, 558 IN EFI_EVENT *FatalErrorEvent 559 ) 560 { 561 EFI_STATUS Status; 562 563 if (BufferSize > 512) { 564 return EFI_INVALID_PARAMETER; 565 } 566 567 // 568 // Build transmit IO token 569 // 570 571 // Create an event so we are notified when a transmission is complete. 572 // We use this to free resources and report errors. 573 Status = gBS->CreateEvent ( 574 EVT_NOTIFY_SIGNAL, 575 TPL_CALLBACK, 576 DataSent, 577 FatalErrorEvent, 578 &mTransmitToken.CompletionToken.Event 579 ); 580 ASSERT_EFI_ERROR (Status); 581 582 mTxData.DataLength = BufferSize; 583 584 mTxData.FragmentTable[0].FragmentLength = BufferSize; 585 mTxData.FragmentTable[0].FragmentBuffer = AllocateCopyPool ( 586 BufferSize, 587 Buffer 588 ); 589 590 Status = mTcpConnection->Transmit (mTcpConnection, &mTransmitToken); 591 if (EFI_ERROR (Status)) { 592 DEBUG ((EFI_D_ERROR, "TCP Transmit: %r\n", Status)); 593 return Status; 594 } 595 596 return EFI_SUCCESS; 597 } 598 599 600 EFI_STATUS 601 TcpFastbootTransportReceive ( 602 OUT UINTN *BufferSize, 603 OUT VOID **Buffer 604 ) 605 { 606 FASTBOOT_TCP_PACKET_LIST *Entry; 607 608 if (IsListEmpty (&mPacketListHead)) { 609 return EFI_NOT_READY; 610 } 611 612 Entry = (FASTBOOT_TCP_PACKET_LIST *) GetFirstNode (&mPacketListHead); 613 614 if (Entry->Buffer == NULL) { 615 // There was an error receiving this packet. 616 return EFI_DEVICE_ERROR; 617 } 618 619 *Buffer = Entry->Buffer; 620 *BufferSize = Entry->BufferSize; 621 622 RemoveEntryList (&Entry->Link); 623 FreePool (Entry); 624 625 return EFI_SUCCESS; 626 } 627 628 FASTBOOT_TRANSPORT_PROTOCOL mTransportProtocol = { 629 TcpFastbootTransportStart, 630 TcpFastbootTransportStop, 631 TcpFastbootTransportSend, 632 TcpFastbootTransportReceive 633 }; 634 635 EFI_STATUS 636 TcpFastbootTransportEntryPoint ( 637 IN EFI_HANDLE ImageHandle, 638 IN EFI_SYSTEM_TABLE *SystemTable 639 ) 640 { 641 EFI_STATUS Status; 642 643 644 Status = gBS->LocateProtocol( 645 &gEfiSimpleTextOutProtocolGuid, 646 NULL, 647 (VOID **) &mTextOut 648 ); 649 if (EFI_ERROR (Status)) { 650 DEBUG ((EFI_D_ERROR, "Fastboot: Open Text Output Protocol: %r\n", Status)); 651 return Status; 652 } 653 654 Status = gBS->InstallProtocolInterface ( 655 &ImageHandle, 656 &gAndroidFastbootTransportProtocolGuid, 657 EFI_NATIVE_INTERFACE, 658 &mTransportProtocol 659 ); 660 if (EFI_ERROR (Status)) { 661 DEBUG ((EFI_D_ERROR, "Fastboot: Install transport Protocol: %r\n", Status)); 662 } 663 664 return Status; 665 } 666