• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2 
3   Copyright (c) 2013-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 
10   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 
13 **/
14 
15 #include <Protocol/AndroidFastbootTransport.h>
16 #include <Protocol/AndroidFastbootPlatform.h>
17 #include <Protocol/SimpleTextOut.h>
18 #include <Protocol/SimpleTextIn.h>
19 
20 #include <Library/AbootimgLib.h>
21 #include <Library/BaseMemoryLib.h>
22 #include <Library/PcdLib.h>
23 #include <Library/PrintLib.h>
24 #include <Library/UefiApplicationEntryPoint.h>
25 #include <Library/UefiBootServicesTableLib.h>
26 #include <Library/UefiRuntimeServicesTableLib.h>
27 
28 #define ANDROID_FASTBOOT_VERSION    "0.7"
29 
30 #define SPARSE_HEADER_MAGIC         0xED26FF3A
31 #define CHUNK_TYPE_RAW              0xCAC1
32 #define CHUNK_TYPE_FILL             0xCAC2
33 #define CHUNK_TYPE_DONT_CARE        0xCAC3
34 #define CHUNK_TYPE_CRC32            0xCAC4
35 
36 #define FILL_BUF_SIZE               (16 * 1024 * 1024)
37 #define SPARSE_BLOCK_SIZE           4096
38 
39 #define IS_DEVICE_PATH_NODE(node,type,subtype) (((node)->Type == (type)) && ((node)->SubType == (subtype)))
40 
41 #define ALIGN(x, a)        (((x) + ((a) - 1)) & ~((a) - 1))
42 
43 typedef struct _SPARSE_HEADER {
44   UINT32       Magic;
45   UINT16       MajorVersion;
46   UINT16       MinorVersion;
47   UINT16       FileHeaderSize;
48   UINT16       ChunkHeaderSize;
49   UINT32       BlockSize;
50   UINT32       TotalBlocks;
51   UINT32       TotalChunks;
52   UINT32       ImageChecksum;
53 } SPARSE_HEADER;
54 
55 typedef struct _CHUNK_HEADER {
56   UINT16       ChunkType;
57   UINT16       Reserved1;
58   UINT32       ChunkSize;
59   UINT32       TotalSize;
60 } CHUNK_HEADER;
61 
62 /*
63  * UEFI Application using the FASTBOOT_TRANSPORT_PROTOCOL and
64  * FASTBOOT_PLATFORM_PROTOCOL to implement the Android Fastboot protocol.
65  */
66 
67 STATIC FASTBOOT_TRANSPORT_PROTOCOL *mTransport;
68 STATIC FASTBOOT_PLATFORM_PROTOCOL  *mPlatform;
69 
70 STATIC EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *mTextOut;
71 
72 typedef enum {
73   ExpectCmdState,
74   ExpectDataState,
75   FastbootStateMax
76 } ANDROID_FASTBOOT_STATE;
77 
78 STATIC ANDROID_FASTBOOT_STATE mState = ExpectCmdState;
79 
80 // When in ExpectDataState, the number of bytes of data to expect:
81 STATIC UINT64 mNumDataBytes;
82 // .. and the number of bytes so far received this data phase
83 STATIC UINT64 mBytesReceivedSoFar;
84 // .. and the buffer to save data into
85 STATIC UINT8 *mDataBuffer = NULL;
86 
87 // Event notify functions, from which gBS->Exit shouldn't be called, can signal
88 // this event when the application should exit
89 STATIC EFI_EVENT mFinishedEvent;
90 
91 STATIC EFI_EVENT mFatalSendErrorEvent;
92 
93 // This macro uses sizeof - only use it on arrays (i.e. string literals)
94 #define SEND_LITERAL(Str) mTransport->Send (                  \
95                                         sizeof (Str) - 1,     \
96                                         Str,                  \
97                                         &mFatalSendErrorEvent \
98                                         )
99 #define MATCH_CMD_LITERAL(Cmd, Buf) !AsciiStrnCmp (Cmd, Buf, sizeof (Cmd) - 1)
100 
101 #define IS_LOWERCASE_ASCII(Char) (Char >= 'a' && Char <= 'z')
102 
103 #define FASTBOOT_STRING_MAX_LENGTH  256
104 #define FASTBOOT_COMMAND_MAX_LENGTH 64
105 
106 STATIC
107 VOID
HandleGetVar(IN CHAR8 * CmdArg)108 HandleGetVar (
109   IN CHAR8 *CmdArg
110   )
111 {
112   CHAR8      Response[FASTBOOT_COMMAND_MAX_LENGTH + 1] = "OKAY";
113   EFI_STATUS Status;
114 
115   // Respond to getvar:version with 0.4 (version of Fastboot protocol)
116   if (!AsciiStrnCmp ("version", CmdArg, sizeof ("version") - 1 )) {
117     SEND_LITERAL ("OKAY" ANDROID_FASTBOOT_VERSION);
118   } else {
119     // All other variables are assumed to be platform specific
120     Status = mPlatform->GetVar (CmdArg, Response + 4);
121     if (EFI_ERROR (Status)) {
122       SEND_LITERAL ("FAILSomething went wrong when looking up the variable");
123     } else {
124       mTransport->Send (AsciiStrLen (Response), Response, &mFatalSendErrorEvent);
125     }
126   }
127 }
128 
129 STATIC
130 VOID
HandleDownload(IN CHAR8 * NumBytesString)131 HandleDownload (
132   IN CHAR8 *NumBytesString
133   )
134 {
135   CHAR8       Response[13];
136   CHAR16      OutputString[FASTBOOT_STRING_MAX_LENGTH];
137 
138   // Argument is 8-character ASCII string hex representation of number of bytes
139   // that will be sent in the data phase.
140   // Response is "DATA" + that same 8-character string.
141 
142   // Replace any previously downloaded data
143   if (mDataBuffer != NULL) {
144     FreePool (mDataBuffer);
145     mDataBuffer = NULL;
146   }
147 
148   // Parse out number of data bytes to expect
149   mNumDataBytes = AsciiStrHexToUint64 (NumBytesString);
150   if (mNumDataBytes == 0) {
151     mTextOut->OutputString (mTextOut, L"ERROR: Fail to get the number of bytes to download.\r\n");
152     SEND_LITERAL ("FAILFailed to get the number of bytes to download");
153     return;
154   }
155 
156   UnicodeSPrint (OutputString, sizeof (OutputString), L"Downloading %d bytes\r\n", mNumDataBytes);
157   mTextOut->OutputString (mTextOut, OutputString);
158 
159   mDataBuffer = AllocatePool (mNumDataBytes);
160   if (mDataBuffer == NULL) {
161     SEND_LITERAL ("FAILNot enough memory");
162   } else {
163     ZeroMem (Response, sizeof Response);
164     if (mTransport->RequestReceive) {
165       mTransport->RequestReceive (mNumDataBytes);
166     }
167     AsciiSPrint (Response, sizeof Response, "DATA%x",
168       (UINT32)mNumDataBytes);
169     mTransport->Send (sizeof Response - 1, Response, &mFatalSendErrorEvent);
170 
171     mState = ExpectDataState;
172     mBytesReceivedSoFar = 0;
173   }
174 }
175 
176 STATIC
177 EFI_STATUS
FlashSparseImage(IN CHAR8 * PartitionName,IN SPARSE_HEADER * SparseHeader)178 FlashSparseImage (
179   IN CHAR8         *PartitionName,
180   IN SPARSE_HEADER *SparseHeader
181   )
182 {
183   EFI_STATUS        Status = EFI_SUCCESS;
184   UINTN             Chunk, Offset = 0, Left, Count, FillBufSize;
185   VOID             *Image;
186   CHUNK_HEADER     *ChunkHeader;
187   VOID             *FillBuf;
188   CHAR16            OutputString[FASTBOOT_STRING_MAX_LENGTH];
189 
190   Image = (VOID *)SparseHeader;
191   Image += SparseHeader->FileHeaderSize;
192 
193   // allocate the fill buf with dynamic size
194   FillBufSize = FILL_BUF_SIZE;
195   while (FillBufSize >= SPARSE_BLOCK_SIZE) {
196     FillBuf = AllocatePool (FillBufSize);
197     if (FillBuf == NULL) {
198       FillBufSize = FillBufSize >> 1;
199     } else {
200       break;
201     }
202   };
203   if (FillBufSize < SPARSE_BLOCK_SIZE) {
204     UnicodeSPrint (
205       OutputString,
206       sizeof (OutputString),
207       L"Fail to allocate the fill buffer\n"
208       );
209     mTextOut->OutputString (mTextOut, OutputString);
210     return EFI_BUFFER_TOO_SMALL;
211   }
212 
213   for (Chunk = 0; Chunk < SparseHeader->TotalChunks; Chunk++) {
214     ChunkHeader = (CHUNK_HEADER *)Image;
215     DEBUG ((DEBUG_INFO, "Chunk #%d - Type: 0x%x Size: %d TotalSize: %d Offset %d\n",
216             (Chunk+1), ChunkHeader->ChunkType, ChunkHeader->ChunkSize,
217             ChunkHeader->TotalSize, Offset));
218     Image += sizeof (CHUNK_HEADER);
219     switch (ChunkHeader->ChunkType) {
220     case CHUNK_TYPE_RAW:
221       Status = mPlatform->FlashPartitionEx (
222                             PartitionName,
223                             Offset,
224                             ChunkHeader->ChunkSize * SparseHeader->BlockSize,
225                             Image
226                             );
227       if (EFI_ERROR (Status)) {
228         return Status;
229       }
230       Image += (UINTN)ChunkHeader->ChunkSize * SparseHeader->BlockSize;
231       Offset += (UINTN)ChunkHeader->ChunkSize * SparseHeader->BlockSize;
232       break;
233     case CHUNK_TYPE_FILL:
234       Left = (UINTN)ChunkHeader->ChunkSize * SparseHeader->BlockSize;
235       while (Left > 0) {
236         if (Left > FILL_BUF_SIZE) {
237           Count = FILL_BUF_SIZE;
238         } else {
239           Count = Left;
240         }
241         SetMem32 (FillBuf, Count, *(UINT32 *)Image);
242         Status = mPlatform->FlashPartitionEx (
243                               PartitionName,
244                               Offset,
245                               Count,
246                               FillBuf
247                               );
248         if (EFI_ERROR (Status)) {
249           return Status;
250         }
251         Offset += Count;
252         Left = Left - Count;
253       }
254       Image += sizeof (UINT32);
255       break;
256     case CHUNK_TYPE_DONT_CARE:
257       Offset += (UINTN)ChunkHeader->ChunkSize * SparseHeader->BlockSize;
258       break;
259     default:
260       UnicodeSPrint (
261         OutputString,
262         sizeof (OutputString),
263         L"Unsupported Chunk Type:0x%x\n",
264         ChunkHeader->ChunkType
265         );
266       mTextOut->OutputString (mTextOut, OutputString);
267       break;
268     }
269   }
270   FreePool ((VOID *)FillBuf);
271   return Status;
272 }
273 
274 STATIC
275 VOID
HandleFlash(IN CHAR8 * PartitionName)276 HandleFlash (
277   IN CHAR8 *PartitionName
278   )
279 {
280   EFI_STATUS        Status;
281   CHAR16            OutputString[FASTBOOT_STRING_MAX_LENGTH];
282   SPARSE_HEADER    *SparseHeader;
283 
284   // Build output string
285   UnicodeSPrint (OutputString, sizeof (OutputString), L"Flashing partition %a\r\n", PartitionName);
286   mTextOut->OutputString (mTextOut, OutputString);
287 
288   if (mDataBuffer == NULL) {
289     // Doesn't look like we were sent any data
290     SEND_LITERAL ("FAILNo data to flash");
291     return;
292   }
293 
294   SparseHeader = (SPARSE_HEADER *)mDataBuffer;
295   if (SparseHeader->Magic == SPARSE_HEADER_MAGIC) {
296     DEBUG ((DEBUG_INFO, "Sparse Magic: 0x%x Major: %d Minor: %d fhs: %d chs: %d bs: %d tbs: %d tcs: %d checksum: %d \n",
297                 SparseHeader->Magic, SparseHeader->MajorVersion, SparseHeader->MinorVersion,  SparseHeader->FileHeaderSize,
298                 SparseHeader->ChunkHeaderSize, SparseHeader->BlockSize, SparseHeader->TotalBlocks,
299                 SparseHeader->TotalChunks, SparseHeader->ImageChecksum));
300     if (SparseHeader->MajorVersion != 1) {
301         DEBUG ((DEBUG_ERROR, "Sparse image version %d.%d not supported.\n",
302                     SparseHeader->MajorVersion, SparseHeader->MinorVersion));
303         return;
304     }
305     Status = FlashSparseImage (PartitionName, SparseHeader);
306   } else {
307     Status = mPlatform->FlashPartition (
308                           PartitionName,
309                           mNumDataBytes,
310                           mDataBuffer
311                           );
312   }
313   switch (Status) {
314   case EFI_SUCCESS:
315     mTextOut->OutputString (mTextOut, L"Done.\r\n");
316     SEND_LITERAL ("OKAY");
317     break;
318   case EFI_NOT_FOUND:
319     SEND_LITERAL ("FAILNo such partition.");
320     mTextOut->OutputString (mTextOut, L"No such partition.\r\n");
321     break;
322   default:
323     SEND_LITERAL ("FAILError flashing partition.");
324     mTextOut->OutputString (mTextOut, L"Error flashing partition.\r\n");
325     DEBUG ((EFI_D_ERROR, "Couldn't flash image:\n"));
326     break;
327   }
328 }
329 
330 STATIC
331 VOID
HandleErase(IN CHAR8 * PartitionName)332 HandleErase (
333   IN CHAR8 *PartitionName
334   )
335 {
336   EFI_STATUS  Status;
337   CHAR16      OutputString[FASTBOOT_STRING_MAX_LENGTH];
338 
339   // Build output string
340   UnicodeSPrint (OutputString, sizeof (OutputString), L"Erasing partition %a\r\n", PartitionName);
341   mTextOut->OutputString (mTextOut, OutputString);
342 
343   Status = mPlatform->ErasePartition (PartitionName);
344   if (EFI_ERROR (Status)) {
345     SEND_LITERAL ("FAILCheck device console.");
346     DEBUG ((EFI_D_ERROR, "Couldn't erase image:  %r\n", Status));
347   } else {
348     SEND_LITERAL ("OKAY");
349   }
350 }
351 
352 STATIC
353 VOID
HandleBoot(VOID)354 HandleBoot (
355   VOID
356   )
357 {
358   CHAR16     *BootPathStr;
359 
360   mTextOut->OutputString (mTextOut, L"Booting downloaded image\r\n");
361 
362   if (mDataBuffer == NULL) {
363     // Doesn't look like we were sent any data
364     SEND_LITERAL ("FAILNo image in memory");
365     return;
366   }
367 
368   // We don't really have any choice but to report success, because once we
369   // boot we lose control of the system.
370   SEND_LITERAL ("OKAY");
371 
372   BootPathStr = (CHAR16 *)PcdGetPtr (PcdAndroidBootDevicePath);
373   AbootimgBootRam (mDataBuffer, mNumDataBytes, BootPathStr, NULL);
374   // We shouldn't get here
375 }
376 
377 STATIC
378 VOID
HandleOemCommand(IN CHAR8 * Command)379 HandleOemCommand (
380   IN CHAR8 *Command
381   )
382 {
383   EFI_STATUS  Status;
384 
385   Status = mPlatform->DoOemCommand (Command);
386   if (Status == EFI_NOT_FOUND) {
387     SEND_LITERAL ("FAILOEM Command not recognised.");
388   } else if (Status == EFI_DEVICE_ERROR) {
389     SEND_LITERAL ("FAILError while executing command");
390   } else if (EFI_ERROR (Status)) {
391     SEND_LITERAL ("FAIL");
392   } else {
393     SEND_LITERAL ("OKAY");
394   }
395 }
396 
397 STATIC
398 VOID
AcceptCmd(IN UINTN Size,IN CONST CHAR8 * Data)399 AcceptCmd (
400   IN        UINTN  Size,
401   IN  CONST CHAR8 *Data
402   )
403 {
404   EFI_STATUS  Status;
405   CHAR8       Command[FASTBOOT_COMMAND_MAX_LENGTH + 1];
406 
407   // Max command size is 64 bytes
408   if (Size > FASTBOOT_COMMAND_MAX_LENGTH) {
409     SEND_LITERAL ("FAILCommand too large");
410     return;
411   }
412 
413   // Commands aren't null-terminated. Let's get a null-terminated version.
414   AsciiStrnCpyS (Command, sizeof Command, Data, Size);
415 
416   // Parse command
417   if (MATCH_CMD_LITERAL ("getvar", Command)) {
418     HandleGetVar (Command + sizeof ("getvar"));
419   } else if (MATCH_CMD_LITERAL ("download", Command)) {
420     HandleDownload (Command + sizeof ("download"));
421   } else if (MATCH_CMD_LITERAL ("verify", Command)) {
422     SEND_LITERAL ("FAILNot supported");
423   } else if (MATCH_CMD_LITERAL ("flash", Command)) {
424     HandleFlash (Command + sizeof ("flash"));
425   } else if (MATCH_CMD_LITERAL ("erase", Command)) {
426     HandleErase (Command + sizeof ("erase"));
427   } else if (MATCH_CMD_LITERAL ("boot", Command)) {
428     HandleBoot ();
429   } else if (MATCH_CMD_LITERAL ("continue", Command)) {
430     SEND_LITERAL ("OKAY");
431     mTextOut->OutputString (mTextOut, L"Received 'continue' command. Exiting Fastboot mode\r\n");
432 
433     gBS->SignalEvent (mFinishedEvent);
434   } else if (MATCH_CMD_LITERAL ("reboot", Command)) {
435     if (MATCH_CMD_LITERAL ("reboot-bootloader", Command)) {
436       Status = mPlatform->DoOemCommand ("reboot-bootloader");
437       if (EFI_ERROR (Status)) {
438         SEND_LITERAL ("INFOreboot-bootloader not supported, rebooting normally.");
439       }
440     }
441     SEND_LITERAL ("OKAY");
442     gRT->ResetSystem (EfiResetCold, EFI_SUCCESS, 0, NULL);
443 
444     // Shouldn't get here
445     DEBUG ((EFI_D_ERROR, "Fastboot: gRT->ResetSystem didn't work\n"));
446   } else if (MATCH_CMD_LITERAL ("powerdown", Command)) {
447     SEND_LITERAL ("OKAY");
448     gRT->ResetSystem (EfiResetShutdown, EFI_SUCCESS, 0, NULL);
449 
450     // Shouldn't get here
451     DEBUG ((EFI_D_ERROR, "Fastboot: gRT->ResetSystem didn't work\n"));
452   } else if (MATCH_CMD_LITERAL ("oem", Command)) {
453     // The "oem" command isn't in the specification, but it was observed in the
454     // wild, followed by a space, followed by the actual command.
455     HandleOemCommand (Command + sizeof ("oem"));
456   } else if (IS_LOWERCASE_ASCII (Command[0])) {
457     // Commands starting with lowercase ASCII characters are reserved for the
458     // Fastboot protocol. If we don't recognise it, it's probably the future
459     // and there are new commmands in the protocol.
460     // (By the way, the "oem" command mentioned above makes this reservation
461     //  redundant, but we handle it here to be spec-compliant)
462     SEND_LITERAL ("FAILCommand not recognised. Check Fastboot version.");
463   } else {
464     HandleOemCommand (Command);
465   }
466 }
467 
468 STATIC
469 VOID
AcceptData(IN UINTN Size,IN VOID * Data)470 AcceptData (
471   IN  UINTN  Size,
472   IN  VOID  *Data
473   )
474 {
475   UINT32 RemainingBytes = mNumDataBytes - mBytesReceivedSoFar;
476   CHAR16 OutputString[FASTBOOT_STRING_MAX_LENGTH];
477   STATIC UINTN Count = 0;
478 
479   // Protocol doesn't say anything about sending extra data so just ignore it.
480   if (Size > RemainingBytes) {
481     Size = RemainingBytes;
482   }
483 
484   CopyMem (&mDataBuffer[mBytesReceivedSoFar], Data, Size);
485 
486   mBytesReceivedSoFar += Size;
487 
488   // Show download progress. Don't do it for every packet  as outputting text
489   // might be time consuming - do it on the last packet and on every 32nd packet
490   if ((Count++ % 32) == 0 || Size == RemainingBytes) {
491     // (Note no newline in format string - it will overwrite the line each time)
492     UnicodeSPrint (
493       OutputString,
494       sizeof (OutputString),
495       L"\r%8d / %8d bytes downloaded (%d%%)",
496       mBytesReceivedSoFar,
497       mNumDataBytes,
498       (mBytesReceivedSoFar * 100) / mNumDataBytes // percentage
499       );
500     mTextOut->OutputString (mTextOut, OutputString);
501   }
502 
503   if (mBytesReceivedSoFar == mNumDataBytes) {
504     // Download finished.
505 
506     mTextOut->OutputString (mTextOut, L"\r\n");
507     SEND_LITERAL ("OKAY");
508     mState = ExpectCmdState;
509   }
510 }
511 
512 /*
513   This is the NotifyFunction passed to CreateEvent in the FastbootAppEntryPoint
514   It will be called by the UEFI event framework when the transport protocol
515   implementation signals that data has been received from the Fastboot host.
516   The parameters are ignored.
517 */
518 STATIC
519 VOID
DataReady(IN EFI_EVENT Event,IN VOID * Context)520 DataReady (
521   IN EFI_EVENT  Event,
522   IN VOID      *Context
523   )
524 {
525   UINTN       Size;
526   VOID       *Data;
527   EFI_STATUS  Status;
528 
529   do {
530     // Indicate lower layer driver that how much bytes are expected.
531     if (mState == ExpectDataState) {
532       Size = mNumDataBytes;
533     } else {
534       Size = 0;
535     }
536     Status = mTransport->Receive (&Size, &Data);
537     if (!EFI_ERROR (Status)) {
538       if (mState == ExpectCmdState) {
539         AcceptCmd (Size, (CHAR8 *) Data);
540       } else if (mState == ExpectDataState) {
541         AcceptData (Size, Data);
542       } else {
543         ASSERT (FALSE);
544       }
545       FreePool (Data);
546     }
547   } while (!EFI_ERROR (Status));
548 
549   // Quit if there was a fatal error
550   if (Status != EFI_NOT_READY) {
551     ASSERT (Status == EFI_DEVICE_ERROR);
552     // (Put a newline at the beginning as we are probably in the data phase,
553     //  so the download progress line, with no '\n' is probably on the console)
554     mTextOut->OutputString (mTextOut, L"\r\nFatal error receiving data. Exiting.\r\n");
555     gBS->SignalEvent (mFinishedEvent);
556   }
557 }
558 
559 /*
560   Event notify for a fatal error in transmission.
561 */
562 STATIC
563 VOID
FatalErrorNotify(IN EFI_EVENT Event,IN VOID * Context)564 FatalErrorNotify (
565   IN EFI_EVENT  Event,
566   IN VOID      *Context
567   )
568 {
569   mTextOut->OutputString (mTextOut, L"Fatal error sending command response. Exiting.\r\n");
570   gBS->SignalEvent (mFinishedEvent);
571 }
572 
573 EFI_STATUS
574 EFIAPI
FastbootAppEntryPoint(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)575 FastbootAppEntryPoint (
576   IN EFI_HANDLE                            ImageHandle,
577   IN EFI_SYSTEM_TABLE                      *SystemTable
578   )
579 {
580   EFI_STATUS                      Status;
581   EFI_EVENT                       ReceiveEvent;
582   EFI_EVENT                       WaitEventArray[2];
583   UINTN                           EventIndex;
584   EFI_SIMPLE_TEXT_INPUT_PROTOCOL *TextIn;
585   EFI_INPUT_KEY                   Key;
586 
587   mDataBuffer = NULL;
588 
589   Status = gBS->LocateProtocol (
590     &gAndroidFastbootTransportProtocolGuid,
591     NULL,
592     (VOID **) &mTransport
593     );
594   if (EFI_ERROR (Status)) {
595     DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't open Fastboot Transport Protocol: %r\n", Status));
596     return Status;
597   }
598 
599   Status = gBS->LocateProtocol (&gAndroidFastbootPlatformProtocolGuid, NULL, (VOID **) &mPlatform);
600   if (EFI_ERROR (Status)) {
601     DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't open Fastboot Platform Protocol: %r\n", Status));
602     return Status;
603   }
604 
605   Status = mPlatform->Init ();
606   if (EFI_ERROR (Status)) {
607     DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't initialise Fastboot Platform Protocol: %r\n", Status));
608     return Status;
609   }
610 
611   Status = gBS->LocateProtocol (&gEfiSimpleTextOutProtocolGuid, NULL, (VOID **) &mTextOut);
612   if (EFI_ERROR (Status)) {
613     DEBUG ((EFI_D_ERROR,
614       "Fastboot: Couldn't open Text Output Protocol: %r\n", Status
615       ));
616     return Status;
617   }
618 
619   Status = gBS->LocateProtocol (&gEfiSimpleTextInProtocolGuid, NULL, (VOID **) &TextIn);
620   if (EFI_ERROR (Status)) {
621     DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't open Text Input Protocol: %r\n", Status));
622     return Status;
623   }
624 
625   // Disable watchdog
626   Status = gBS->SetWatchdogTimer (0, 0x10000, 0, NULL);
627   if (EFI_ERROR (Status)) {
628     DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't disable watchdog timer: %r\n", Status));
629   }
630 
631   // Create event for receipt of data from the host
632   Status = gBS->CreateEvent (
633                   EVT_NOTIFY_SIGNAL,
634                   TPL_CALLBACK,
635                   DataReady,
636                   NULL,
637                   &ReceiveEvent
638                   );
639   ASSERT_EFI_ERROR (Status);
640 
641   // Create event for exiting application when "continue" command is received
642   Status = gBS->CreateEvent (0, TPL_CALLBACK, NULL, NULL, &mFinishedEvent);
643   ASSERT_EFI_ERROR (Status);
644 
645   // Create event to pass to FASTBOOT_TRANSPORT_PROTOCOL.Send, signalling a
646   // fatal error
647   Status = gBS->CreateEvent (
648                  EVT_NOTIFY_SIGNAL,
649                  TPL_CALLBACK,
650                  FatalErrorNotify,
651                  NULL,
652                  &mFatalSendErrorEvent
653                  );
654   ASSERT_EFI_ERROR (Status);
655 
656 
657   // Start listening for data
658   Status = mTransport->Start (
659     ReceiveEvent
660     );
661   if (EFI_ERROR (Status)) {
662     DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't start transport: %r\n", Status));
663     return Status;
664   }
665 
666   // Talk to the user
667   mTextOut->OutputString (mTextOut,
668       L"Android Fastboot mode - version " ANDROID_FASTBOOT_VERSION ".\r\n");
669   mTextOut->OutputString (mTextOut, L"Press RETURN or SPACE key to quit.\r\n");
670 
671   // Quit when the user presses any key, or mFinishedEvent is signalled
672   WaitEventArray[0] = mFinishedEvent;
673   WaitEventArray[1] = TextIn->WaitForKey;
674   while (1) {
675     gBS->WaitForEvent (2, WaitEventArray, &EventIndex);
676     if (EventIndex == 0) {
677       break;
678     }
679     Status = TextIn->ReadKeyStroke (gST->ConIn, &Key);
680     if (Key.ScanCode == SCAN_NULL) {
681       if ((Key.UnicodeChar == CHAR_CARRIAGE_RETURN) || (Key.UnicodeChar == ' ')) {
682         break;
683       }
684     }
685   }
686 
687   mTransport->Stop ();
688   if (EFI_ERROR (Status)) {
689     DEBUG ((EFI_D_ERROR, "Warning: Fastboot Transport Stop: %r\n", Status));
690   }
691   mPlatform->UnInit ();
692 
693   return EFI_SUCCESS;
694 }
695