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 #include <Guid/Hostname.h> 31 32 #define IP4_ADDR_TO_STRING(IpAddr, IpAddrString) UnicodeSPrint ( \ 33 IpAddrString, \ 34 16 * 2, \ 35 L"%d.%d.%d.%d", \ 36 IpAddr.Addr[0], \ 37 IpAddr.Addr[1], \ 38 IpAddr.Addr[2], \ 39 IpAddr.Addr[3] \ 40 ); 41 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 48 49 STATIC EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *mTextOut; 50 51 STATIC EFI_TCP4_PROTOCOL *mTcpConnection; 52 STATIC EFI_TCP4_PROTOCOL *mTcpListener; 53 54 STATIC EFI_EVENT mReceiveEvent; 55 56 STATIC EFI_SERVICE_BINDING_PROTOCOL *mTcpServiceBinding; 57 STATIC EFI_HANDLE mTcpHandle = NULL; 58 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) 63 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; 74 75 // List type for queued received packets 76 typedef struct _FASTBOOT_TCP_PACKET_LIST { 77 LIST_ENTRY Link; 78 VOID *Buffer; 79 UINTN BufferSize; 80 } FASTBOOT_TCP_PACKET_LIST; 81 82 STATIC LIST_ENTRY mPacketListHead; 83 84 STATIC 85 VOID 86 EFIAPI 87 DataReceived ( 88 IN EFI_EVENT Event, 89 IN VOID *Context 90 ); 91 92 /* 93 Helper function to set up a receive IO token and call Tcp->Receive 94 */ 95 STATIC 96 EFI_STATUS 97 SubmitRecieveToken ( 98 VOID 99 ) 100 { 101 EFI_STATUS Status; 102 VOID *FragmentBuffer; 103 104 Status = EFI_SUCCESS; 105 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; 111 } 112 113 mRxData[mNextSubmitIndex].DataLength = RX_FRAGMENT_SIZE; 114 mRxData[mNextSubmitIndex].FragmentTable[0].FragmentLength = RX_FRAGMENT_SIZE; 115 mRxData[mNextSubmitIndex].FragmentTable[0].FragmentBuffer = FragmentBuffer; 116 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); 121 } 122 123 mNextSubmitIndex = TOKEN_NEXT (mNextSubmitIndex); 124 return Status; 125 } 126 127 /* 128 Event notify function for when we have closed our TCP connection. 129 We can now start listening for another connection. 130 */ 131 STATIC 132 VOID 133 ConnectionClosed ( 134 IN EFI_EVENT Event, 135 IN VOID *Context 136 ) 137 { 138 EFI_STATUS Status; 139 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); 145 146 mTcpConnection = NULL; 147 148 Status = mTcpListener->Accept (mTcpListener, &mAcceptToken); 149 if (EFI_ERROR (Status)) { 150 DEBUG ((EFI_D_ERROR, "TCP Accept: %r\n", Status)); 151 } 152 } 153 154 STATIC 155 VOID 156 CloseReceiveEvents ( 157 VOID 158 ) 159 { 160 UINTN Index; 161 162 for (Index = 0; Index < NUM_RX_TOKENS; Index++) { 163 gBS->CloseEvent (mReceiveToken[Index].CompletionToken.Event); 164 } 165 } 166 167 /* 168 Event notify function to be called when we receive TCP data. 169 */ 170 STATIC 171 VOID 172 EFIAPI 173 DataReceived ( 174 IN EFI_EVENT Event, 175 IN VOID *Context 176 ) 177 { 178 EFI_STATUS Status; 179 FASTBOOT_TCP_PACKET_LIST *NewEntry; 180 EFI_TCP4_IO_TOKEN *ReceiveToken; 181 182 ReceiveToken = &mReceiveToken[mNextReceiveIndex]; 183 184 Status = ReceiveToken->CompletionToken.Status; 185 186 if (Status == EFI_CONNECTION_FIN) { 187 // 188 // Remote host closed connection. Close our end. 189 // 190 191 CloseReceiveEvents (); 192 193 Status = mTcpConnection->Close (mTcpConnection, &mCloseToken); 194 ASSERT_EFI_ERROR (Status); 195 196 return; 197 } 198 199 // 200 // Add an element to the receive queue 201 // 202 203 NewEntry = AllocatePool (sizeof (FASTBOOT_TCP_PACKET_LIST)); 204 if (NewEntry == NULL) { 205 DEBUG ((EFI_D_ERROR, "TCP Fastboot: Out of resources\n")); 206 return; 207 } 208 209 mNextReceiveIndex = TOKEN_NEXT (mNextReceiveIndex); 210 211 if (!EFI_ERROR (Status)) { 212 NewEntry->Buffer 213 = ReceiveToken->Packet.RxData->FragmentTable[0].FragmentBuffer; 214 NewEntry->BufferSize 215 = ReceiveToken->Packet.RxData->FragmentTable[0].FragmentLength; 216 217 // Prepare to receive more data 218 SubmitRecieveToken(); 219 } else { 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; 224 225 DEBUG ((EFI_D_ERROR, "\nTCP Fastboot Receive error: %r\n", Status)); 226 } 227 228 InsertTailList (&mPacketListHead, &NewEntry->Link); 229 230 Status = gBS->SignalEvent (mReceiveEvent); 231 ASSERT_EFI_ERROR (Status); 232 } 233 234 235 /* 236 Event notify function to be called when we accept an incoming TCP connection. 237 */ 238 STATIC 239 VOID 240 EFIAPI 241 ConnectionAccepted ( 242 IN EFI_EVENT Event, 243 IN VOID *Context 244 ) 245 { 246 EFI_TCP4_LISTEN_TOKEN *AcceptToken; 247 EFI_STATUS Status; 248 UINTN Index; 249 250 AcceptToken = (EFI_TCP4_LISTEN_TOKEN *) Context; 251 Status = AcceptToken->CompletionToken.Status; 252 253 if (EFI_ERROR (Status)) { 254 DEBUG ((EFI_D_ERROR, "TCP Fastboot: Connection Error: %r\n", Status)); 255 return; 256 } 257 DEBUG ((EFI_D_ERROR, "TCP Fastboot: Connection Received.\n")); 258 259 // 260 // Accepting a new TCP connection creates a new instance of the TCP protocol. 261 // Open it and prepare to receive on it. 262 // 263 264 Status = gBS->OpenProtocol ( 265 AcceptToken->NewChildHandle, 266 &gEfiTcp4ProtocolGuid, 267 (VOID **) &mTcpConnection, 268 gImageHandle, 269 NULL, 270 EFI_OPEN_PROTOCOL_GET_PROTOCOL 271 ); 272 if (EFI_ERROR (Status)) { 273 DEBUG ((EFI_D_ERROR, "Open TCP Connection: %r\n", Status)); 274 return; 275 } 276 277 mNextSubmitIndex = 0; 278 mNextReceiveIndex = 0; 279 280 for (Index = 0; Index < NUM_RX_TOKENS; Index++) { 281 Status = gBS->CreateEvent ( 282 EVT_NOTIFY_SIGNAL, 283 TPL_CALLBACK, 284 DataReceived, 285 NULL, 286 &(mReceiveToken[Index].CompletionToken.Event) 287 ); 288 ASSERT_EFI_ERROR (Status); 289 } 290 291 for (Index = 0; Index < NUM_RX_TOKENS; Index++) { 292 SubmitRecieveToken(); 293 } 294 } 295 296 /* 297 Set up TCP Fastboot transport: Configure the network device via DHCP then 298 start waiting for a TCP connection on the Fastboot port. 299 */ 300 EFI_STATUS 301 TcpFastbootTransportStart ( 302 EFI_EVENT ReceiveEvent 303 ) 304 { 305 EFI_STATUS Status; 306 EFI_HANDLE NetDeviceHandle; 307 EFI_HANDLE *HandleBuffer; 308 EFI_IP4_MODE_DATA Ip4ModeData; 309 UINTN NumHandles; 310 UINTN HostnameSize = 256; 311 CHAR8 Hostname[256]; 312 CHAR16 HostnameUnicode[256] = L"<no hostname>"; 313 CHAR16 IpAddrString[16]; 314 UINTN Index; 315 316 EFI_TCP4_CONFIG_DATA TcpConfigData = { 317 0x00, // IPv4 Type of Service 318 255, // IPv4 Time to Live 319 { // AccessPoint: 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" 327 }, 328 NULL // Default advanced TCP options 329 }; 330 331 mReceiveEvent = ReceiveEvent; 332 InitializeListHead (&mPacketListHead); 333 334 mTextOut->OutputString (mTextOut, L"Initialising TCP Fastboot transport...\r\n"); 335 336 // 337 // Open a passive TCP instance 338 // 339 340 Status = gBS->LocateHandleBuffer ( 341 ByProtocol, 342 &gEfiTcp4ServiceBindingProtocolGuid, 343 NULL, 344 &NumHandles, 345 &HandleBuffer 346 ); 347 if (EFI_ERROR (Status)) { 348 DEBUG ((EFI_D_ERROR, "Find TCP Service Binding: %r\n", Status)); 349 return Status; 350 } 351 352 // We just use the first network device 353 NetDeviceHandle = HandleBuffer[0]; 354 355 Status = gBS->OpenProtocol ( 356 NetDeviceHandle, 357 &gEfiTcp4ServiceBindingProtocolGuid, 358 (VOID **) &mTcpServiceBinding, 359 gImageHandle, 360 NULL, 361 EFI_OPEN_PROTOCOL_GET_PROTOCOL 362 ); 363 if (EFI_ERROR (Status)) { 364 DEBUG ((EFI_D_ERROR, "Open TCP Service Binding: %r\n", Status)); 365 return Status; 366 } 367 368 Status = mTcpServiceBinding->CreateChild (mTcpServiceBinding, &mTcpHandle); 369 if (EFI_ERROR (Status)) { 370 DEBUG ((EFI_D_ERROR, "TCP ServiceBinding Create: %r\n", Status)); 371 return Status; 372 } 373 374 Status = gBS->OpenProtocol ( 375 mTcpHandle, 376 &gEfiTcp4ProtocolGuid, 377 (VOID **) &mTcpListener, 378 gImageHandle, 379 NULL, 380 EFI_OPEN_PROTOCOL_GET_PROTOCOL 381 ); 382 if (EFI_ERROR (Status)) { 383 DEBUG ((EFI_D_ERROR, "Open TCP Protocol: %r\n", Status)); 384 } 385 386 // 387 // Set up re-usable tokens 388 // 389 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]; 394 } 395 396 mTxData.Push = TRUE; 397 mTxData.Urgent = FALSE; 398 mTxData.FragmentCount = 1; 399 mTransmitToken.Packet.TxData = &mTxData; 400 401 Status = gBS->CreateEvent ( 402 EVT_NOTIFY_SIGNAL, 403 TPL_CALLBACK, 404 ConnectionAccepted, 405 &mAcceptToken, 406 &mAcceptToken.CompletionToken.Event 407 ); 408 ASSERT_EFI_ERROR (Status); 409 410 Status = gBS->CreateEvent ( 411 EVT_NOTIFY_SIGNAL, 412 TPL_CALLBACK, 413 ConnectionClosed, 414 &mCloseToken, 415 &mCloseToken.CompletionToken.Event 416 ); 417 ASSERT_EFI_ERROR (Status); 418 419 // 420 // Configure the TCP instance 421 // 422 423 Status = mTcpListener->Configure (mTcpListener, &TcpConfigData); 424 if (Status == EFI_NO_MAPPING) { 425 // Wait until the IP configuration process (probably DHCP) has finished 426 do { 427 Status = mTcpListener->GetModeData (mTcpListener, 428 NULL, NULL, 429 &Ip4ModeData, 430 NULL, NULL 431 ); 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)); 437 return Status; 438 } 439 440 // 441 // Tell the user our address and hostname 442 // 443 IP4_ADDR_TO_STRING (Ip4ModeData.ConfigData.StationAddress, IpAddrString); 444 445 // Look up hostname 446 Status = gRT->GetVariable ( 447 L"Hostname", 448 &gEfiHostnameVariableGuid, 449 NULL, 450 &HostnameSize, 451 &Hostname 452 ); 453 if (!EFI_ERROR (Status) && HostnameSize != 0) { 454 AsciiStrToUnicodeStr (Hostname, HostnameUnicode); 455 } 456 457 // Hostname variable is not null-terminated. 458 Hostname[HostnameSize] = L'\0'; 459 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"); 467 468 // 469 // Start listening for a connection 470 // 471 472 Status = mTcpListener->Accept (mTcpListener, &mAcceptToken); 473 if (EFI_ERROR (Status)) { 474 DEBUG ((EFI_D_ERROR, "TCP Accept: %r\n", Status)); 475 return Status; 476 } 477 478 mTextOut->OutputString (mTextOut, L"TCP Fastboot transport initialised.\r\n"); 479 480 FreePool (HandleBuffer); 481 482 return EFI_SUCCESS; 483 } 484 485 EFI_STATUS 486 TcpFastbootTransportStop ( 487 VOID 488 ) 489 { 490 EFI_TCP4_CLOSE_TOKEN CloseToken; 491 EFI_STATUS Status; 492 UINTN EventIndex; 493 FASTBOOT_TCP_PACKET_LIST *Entry; 494 FASTBOOT_TCP_PACKET_LIST *NextEntry; 495 496 // Close any existing TCP connection, blocking until it's done. 497 if (mTcpConnection != NULL) { 498 CloseReceiveEvents (); 499 500 CloseToken.AbortOnClose = FALSE; 501 502 Status = gBS->CreateEvent (0, 0, NULL, NULL, &CloseToken.CompletionToken.Event); 503 ASSERT_EFI_ERROR (Status); 504 505 Status = mTcpConnection->Close (mTcpConnection, &CloseToken); 506 ASSERT_EFI_ERROR (Status); 507 508 Status = gBS->WaitForEvent ( 509 1, 510 &CloseToken.CompletionToken.Event, 511 &EventIndex 512 ); 513 ASSERT_EFI_ERROR (Status); 514 515 ASSERT_EFI_ERROR (CloseToken.CompletionToken.Status); 516 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); 523 } 524 525 526 gBS->CloseEvent (mAcceptToken.CompletionToken.Event); 527 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); 533 534 Status = mTcpServiceBinding->DestroyChild (mTcpServiceBinding, &mTcpHandle); 535 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); 540 541 RemoveEntryList (&Entry->Link); 542 if (Entry->Buffer) { 543 FreePool (Entry->Buffer); 544 } 545 FreePool (Entry); 546 547 Entry = NextEntry; 548 } 549 550 return EFI_SUCCESS; 551 } 552 553 /* 554 Event notify function for when data has been sent. Free resources and report 555 errors. 556 Context should point to the transmit IO token passed to 557 TcpConnection->Transmit. 558 */ 559 STATIC 560 VOID 561 DataSent ( 562 EFI_EVENT Event, 563 VOID *Context 564 ) 565 { 566 EFI_STATUS Status; 567 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); 572 } 573 574 FreePool (mTransmitToken.Packet.TxData->FragmentTable[0].FragmentBuffer); 575 } 576 577 EFI_STATUS 578 TcpFastbootTransportSend ( 579 IN UINTN BufferSize, 580 IN CONST VOID *Buffer, 581 IN EFI_EVENT *FatalErrorEvent 582 ) 583 { 584 EFI_STATUS Status; 585 586 if (BufferSize > 512) { 587 return EFI_INVALID_PARAMETER; 588 } 589 590 // 591 // Build transmit IO token 592 // 593 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 ( 597 EVT_NOTIFY_SIGNAL, 598 TPL_CALLBACK, 599 DataSent, 600 FatalErrorEvent, 601 &mTransmitToken.CompletionToken.Event 602 ); 603 ASSERT_EFI_ERROR (Status); 604 605 mTxData.DataLength = BufferSize; 606 607 mTxData.FragmentTable[0].FragmentLength = BufferSize; 608 mTxData.FragmentTable[0].FragmentBuffer = AllocateCopyPool ( 609 BufferSize, 610 Buffer 611 ); 612 613 Status = mTcpConnection->Transmit (mTcpConnection, &mTransmitToken); 614 if (EFI_ERROR (Status)) { 615 DEBUG ((EFI_D_ERROR, "TCP Transmit: %r\n", Status)); 616 return Status; 617 } 618 619 return EFI_SUCCESS; 620 } 621 622 623 EFI_STATUS 624 TcpFastbootTransportReceive ( 625 OUT UINTN *BufferSize, 626 OUT VOID **Buffer 627 ) 628 { 629 FASTBOOT_TCP_PACKET_LIST *Entry; 630 631 if (IsListEmpty (&mPacketListHead)) { 632 return EFI_NOT_READY; 633 } 634 635 Entry = (FASTBOOT_TCP_PACKET_LIST *) GetFirstNode (&mPacketListHead); 636 637 if (Entry->Buffer == NULL) { 638 // There was an error receiving this packet. 639 return EFI_DEVICE_ERROR; 640 } 641 642 *Buffer = Entry->Buffer; 643 *BufferSize = Entry->BufferSize; 644 645 RemoveEntryList (&Entry->Link); 646 FreePool (Entry); 647 648 return EFI_SUCCESS; 649 } 650 651 FASTBOOT_TRANSPORT_PROTOCOL mTransportProtocol = { 652 TcpFastbootTransportStart, 653 TcpFastbootTransportStop, 654 TcpFastbootTransportSend, 655 TcpFastbootTransportReceive 656 }; 657 658 EFI_STATUS 659 TcpFastbootTransportEntryPoint ( 660 IN EFI_HANDLE ImageHandle, 661 IN EFI_SYSTEM_TABLE *SystemTable 662 ) 663 { 664 EFI_STATUS Status; 665 666 667 Status = gBS->LocateProtocol( 668 &gEfiSimpleTextOutProtocolGuid, 669 NULL, 670 (VOID **) &mTextOut 671 ); 672 if (EFI_ERROR (Status)) { 673 DEBUG ((EFI_D_ERROR, "Fastboot: Open Text Output Protocol: %r\n", Status)); 674 return Status; 675 } 676 677 Status = gBS->InstallProtocolInterface ( 678 &ImageHandle, 679 &gAndroidFastbootTransportProtocolGuid, 680 EFI_NATIVE_INTERFACE, 681 &mTransportProtocol 682 ); 683 if (EFI_ERROR (Status)) { 684 DEBUG ((EFI_D_ERROR, "Fastboot: Install transport Protocol: %r\n", Status)); 685 } 686 687 return Status; 688 } 689