• 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 "AndroidFastbootApp.h"
16 
17 #include <Protocol/AndroidFastbootTransport.h>
18 #include <Protocol/AndroidFastbootPlatform.h>
19 #include <Protocol/SimpleTextOut.h>
20 #include <Protocol/SimpleTextIn.h>
21 
22 #include <Library/ArmLib.h>
23 #include <Library/PcdLib.h>
24 #include <Library/UefiRuntimeServicesTableLib.h>
25 #include <Library/BaseMemoryLib.h>
26 #include <Library/UefiBootServicesTableLib.h>
27 #include <Library/UefiApplicationEntryPoint.h>
28 #include <Library/PrintLib.h>
29 
30 /*
31  * UEFI Application using the FASTBOOT_TRANSPORT_PROTOCOL and
32  * FASTBOOT_PLATFORM_PROTOCOL to implement the Android Fastboot protocol.
33  */
34 
35 STATIC FASTBOOT_TRANSPORT_PROTOCOL *mTransport;
36 STATIC FASTBOOT_PLATFORM_PROTOCOL  *mPlatform;
37 
38 STATIC EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *mTextOut;
39 
40 typedef enum {
41   ExpectCmdState,
42   ExpectDataState,
43   FastbootStateMax
44 } ANDROID_FASTBOOT_STATE;
45 
46 STATIC ANDROID_FASTBOOT_STATE mState = ExpectCmdState;
47 
48 // When in ExpectDataState, the number of bytes of data to expect:
49 STATIC UINTN mNumDataBytes;
50 // .. and the number of bytes so far received this data phase
51 STATIC UINTN mBytesReceivedSoFar;
52 // .. and the buffer to save data into
53 STATIC UINT8 *mDataBuffer = NULL;
54 
55 // Event notify functions, from which gBS->Exit shouldn't be called, can signal
56 // this event when the application should exit
57 STATIC EFI_EVENT mFinishedEvent;
58 
59 STATIC EFI_EVENT mFatalSendErrorEvent;
60 
61 // This macro uses sizeof - only use it on arrays (i.e. string literals)
62 #define SEND_LITERAL(Str) mTransport->Send (                  \
63                                         sizeof (Str) - 1,     \
64                                         Str,                  \
65                                         &mFatalSendErrorEvent \
66                                         )
67 #define MATCH_CMD_LITERAL(Cmd, Buf) !AsciiStrnCmp (Cmd, Buf, sizeof (Cmd) - 1)
68 
69 #define IS_LOWERCASE_ASCII(Char) (Char >= 'a' && Char <= 'z')
70 
71 #define FASTBOOT_STRING_MAX_LENGTH  256
72 #define FASTBOOT_COMMAND_MAX_LENGTH 64
73 
74 STATIC
75 VOID
HandleGetVar(IN CHAR8 * CmdArg)76 HandleGetVar (
77   IN CHAR8 *CmdArg
78   )
79 {
80   CHAR8      Response[FASTBOOT_COMMAND_MAX_LENGTH + 1] = "OKAY";
81   EFI_STATUS Status;
82 
83   // Respond to getvar:version with 0.4 (version of Fastboot protocol)
84   if (!AsciiStrnCmp ("version", CmdArg, sizeof ("version") - 1 )) {
85     SEND_LITERAL ("OKAY" ANDROID_FASTBOOT_VERSION);
86   } else {
87     // All other variables are assumed to be platform specific
88     Status = mPlatform->GetVar (CmdArg, Response + 4);
89     if (EFI_ERROR (Status)) {
90       SEND_LITERAL ("FAILSomething went wrong when looking up the variable");
91     } else {
92       mTransport->Send (AsciiStrLen (Response), Response, &mFatalSendErrorEvent);
93     }
94   }
95 }
96 
97 STATIC
98 VOID
HandleDownload(IN CHAR8 * NumBytesString)99 HandleDownload (
100   IN CHAR8 *NumBytesString
101   )
102 {
103   CHAR8       Response[12] = "DATA";
104   CHAR16      OutputString[FASTBOOT_STRING_MAX_LENGTH];
105 
106   // Argument is 8-character ASCII string hex representation of number of bytes
107   // that will be sent in the data phase.
108   // Response is "DATA" + that same 8-character string.
109 
110   // Replace any previously downloaded data
111   if (mDataBuffer != NULL) {
112     FreePool (mDataBuffer);
113     mDataBuffer = NULL;
114   }
115 
116   // Parse out number of data bytes to expect
117   mNumDataBytes = AsciiStrHexToUint64 (NumBytesString);
118   if (mNumDataBytes == 0) {
119     mTextOut->OutputString (mTextOut, L"ERROR: Fail to get the number of bytes to download.\r\n");
120     SEND_LITERAL ("FAILFailed to get the number of bytes to download");
121     return;
122   }
123 
124   UnicodeSPrint (OutputString, sizeof (OutputString), L"Downloading %d bytes\r\n", mNumDataBytes);
125   mTextOut->OutputString (mTextOut, OutputString);
126 
127   mDataBuffer = AllocatePool (mNumDataBytes);
128   if (mDataBuffer == NULL) {
129     SEND_LITERAL ("FAILNot enough memory");
130   } else {
131     AsciiStrnCpy (Response + 4, NumBytesString, 8);
132     mTransport->Send (sizeof(Response), Response, &mFatalSendErrorEvent);
133 
134     mState = ExpectDataState;
135     mBytesReceivedSoFar = 0;
136   }
137 }
138 
139 STATIC
140 VOID
HandleFlash(IN CHAR8 * PartitionName)141 HandleFlash (
142   IN CHAR8 *PartitionName
143   )
144 {
145   EFI_STATUS  Status;
146   CHAR16      OutputString[FASTBOOT_STRING_MAX_LENGTH];
147 
148   // Build output string
149   UnicodeSPrint (OutputString, sizeof (OutputString), L"Flashing partition %a\r\n", PartitionName);
150   mTextOut->OutputString (mTextOut, OutputString);
151 
152   if (mDataBuffer == NULL) {
153     // Doesn't look like we were sent any data
154     SEND_LITERAL ("FAILNo data to flash");
155     return;
156   }
157 
158   Status = mPlatform->FlashPartition (
159                         PartitionName,
160                         mNumDataBytes,
161                         mDataBuffer
162                         );
163   if (Status == EFI_NOT_FOUND) {
164     SEND_LITERAL ("FAILNo such partition.");
165     mTextOut->OutputString (mTextOut, L"No such partition.\r\n");
166   } else if (EFI_ERROR (Status)) {
167     SEND_LITERAL ("FAILError flashing partition.");
168     mTextOut->OutputString (mTextOut, L"Error flashing partition.\r\n");
169     DEBUG ((EFI_D_ERROR, "Couldn't flash image:  %r\n", Status));
170   } else {
171     mTextOut->OutputString (mTextOut, L"Done.\r\n");
172     SEND_LITERAL ("OKAY");
173   }
174 }
175 
176 STATIC
177 VOID
HandleErase(IN CHAR8 * PartitionName)178 HandleErase (
179   IN CHAR8 *PartitionName
180   )
181 {
182   EFI_STATUS  Status;
183   CHAR16      OutputString[FASTBOOT_STRING_MAX_LENGTH];
184 
185   // Build output string
186   UnicodeSPrint (OutputString, sizeof (OutputString), L"Erasing partition %a\r\n", PartitionName);
187   mTextOut->OutputString (mTextOut, OutputString);
188 
189   Status = mPlatform->ErasePartition (PartitionName);
190   if (EFI_ERROR (Status)) {
191     SEND_LITERAL ("FAILCheck device console.");
192     DEBUG ((EFI_D_ERROR, "Couldn't erase image:  %r\n", Status));
193   } else {
194     SEND_LITERAL ("OKAY");
195   }
196 }
197 
198 STATIC
199 VOID
HandleBoot(VOID)200 HandleBoot (
201   VOID
202   )
203 {
204   EFI_STATUS Status;
205 
206   mTextOut->OutputString (mTextOut, L"Booting downloaded image\r\n");
207 
208   if (mDataBuffer == NULL) {
209     // Doesn't look like we were sent any data
210     SEND_LITERAL ("FAILNo image in memory");
211     return;
212   }
213 
214   // We don't really have any choice but to report success, because once we
215   // boot we lose control of the system.
216   SEND_LITERAL ("OKAY");
217 
218   Status = BootAndroidBootImg (mNumDataBytes, mDataBuffer);
219   if (EFI_ERROR (Status)) {
220     DEBUG ((EFI_D_ERROR, "Failed to boot downloaded image: %r\n", Status));
221   }
222   // We shouldn't get here
223 }
224 
225 STATIC
226 VOID
HandleOemCommand(IN CHAR8 * Command)227 HandleOemCommand (
228   IN CHAR8 *Command
229   )
230 {
231   EFI_STATUS  Status;
232 
233   Status = mPlatform->DoOemCommand (Command);
234   if (Status == EFI_NOT_FOUND) {
235     SEND_LITERAL ("FAILOEM Command not recognised.");
236   } else if (Status == EFI_DEVICE_ERROR) {
237     SEND_LITERAL ("FAILError while executing command");
238   } else if (EFI_ERROR (Status)) {
239     SEND_LITERAL ("FAIL");
240   } else {
241     SEND_LITERAL ("OKAY");
242   }
243 }
244 
245 STATIC
246 VOID
AcceptCmd(IN UINTN Size,IN CONST CHAR8 * Data)247 AcceptCmd (
248   IN        UINTN  Size,
249   IN  CONST CHAR8 *Data
250   )
251 {
252   CHAR8       Command[FASTBOOT_COMMAND_MAX_LENGTH + 1];
253 
254   // Max command size is 64 bytes
255   if (Size > FASTBOOT_COMMAND_MAX_LENGTH) {
256     SEND_LITERAL ("FAILCommand too large");
257     return;
258   }
259 
260   // Commands aren't null-terminated. Let's get a null-terminated version.
261   AsciiStrnCpy (Command, Data, Size);
262   Command[Size] = '\0';
263 
264   // Parse command
265   if (MATCH_CMD_LITERAL ("getvar", Command)) {
266     HandleGetVar (Command + sizeof ("getvar"));
267   } else if (MATCH_CMD_LITERAL ("download", Command)) {
268     HandleDownload (Command + sizeof ("download"));
269   } else if (MATCH_CMD_LITERAL ("verify", Command)) {
270     SEND_LITERAL ("FAILNot supported");
271   } else if (MATCH_CMD_LITERAL ("flash", Command)) {
272     HandleFlash (Command + sizeof ("flash"));
273   } else if (MATCH_CMD_LITERAL ("erase", Command)) {
274     HandleErase (Command + sizeof ("erase"));
275   } else if (MATCH_CMD_LITERAL ("boot", Command)) {
276     HandleBoot ();
277   } else if (MATCH_CMD_LITERAL ("continue", Command)) {
278     SEND_LITERAL ("OKAY");
279     mTextOut->OutputString (mTextOut, L"Received 'continue' command. Exiting Fastboot mode\r\n");
280 
281     gBS->SignalEvent (mFinishedEvent);
282   } else if (MATCH_CMD_LITERAL ("reboot", Command)) {
283     EFI_RESET_TYPE rtype = EfiResetCold;
284     if (MATCH_CMD_LITERAL ("reboot-bootloader", Command)) {
285       // fastboot_protocol.txt:
286       //    "reboot-bootloader    Reboot back into the bootloader."
287 #define REBOOT_REASON_ADDR             0x05F01000
288 #define REBOOT_REASON_BOOTLOADER       0x77665500
289       UINT32 *addr = (UINT32*)REBOOT_REASON_ADDR;
290       /* Write REBOOT_BOOTLOADER to the reason address */
291       *addr = REBOOT_REASON_BOOTLOADER;
292       rtype = EfiResetWarm;
293       ArmCleanDataCache();
294     }
295     SEND_LITERAL ("OKAY");
296     gRT->ResetSystem (rtype, EFI_SUCCESS, 0, NULL);
297 
298     // Shouldn't get here
299     DEBUG ((EFI_D_ERROR, "Fastboot: gRT->ResetSystem didn't work\n"));
300   } else if (MATCH_CMD_LITERAL ("powerdown", Command)) {
301     SEND_LITERAL ("OKAY");
302     gRT->ResetSystem (EfiResetShutdown, EFI_SUCCESS, 0, NULL);
303 
304     // Shouldn't get here
305     DEBUG ((EFI_D_ERROR, "Fastboot: gRT->ResetSystem didn't work\n"));
306   } else if (MATCH_CMD_LITERAL ("oem", Command)) {
307     // The "oem" command isn't in the specification, but it was observed in the
308     // wild, followed by a space, followed by the actual command.
309     HandleOemCommand (Command + sizeof ("oem"));
310   } else if (IS_LOWERCASE_ASCII (Command[0])) {
311     // Commands starting with lowercase ASCII characters are reserved for the
312     // Fastboot protocol. If we don't recognise it, it's probably the future
313     // and there are new commmands in the protocol.
314     // (By the way, the "oem" command mentioned above makes this reservation
315     //  redundant, but we handle it here to be spec-compliant)
316     SEND_LITERAL ("FAILCommand not recognised. Check Fastboot version.");
317   } else {
318     HandleOemCommand (Command);
319   }
320 }
321 
322 STATIC
323 VOID
AcceptData(IN UINTN Size,IN VOID * Data)324 AcceptData (
325   IN  UINTN  Size,
326   IN  VOID  *Data
327   )
328 {
329   UINT32 RemainingBytes = mNumDataBytes - mBytesReceivedSoFar;
330   CHAR16 OutputString[FASTBOOT_STRING_MAX_LENGTH];
331   STATIC UINTN Count = 0;
332 
333   // Protocol doesn't say anything about sending extra data so just ignore it.
334   if (Size > RemainingBytes) {
335     Size = RemainingBytes;
336   }
337 
338   CopyMem (&mDataBuffer[mBytesReceivedSoFar], Data, Size);
339 
340   mBytesReceivedSoFar += Size;
341 
342   // Show download progress. Don't do it for every packet  as outputting text
343   // might be time consuming - do it on the last packet and on every 32nd packet
344   if ((Count++ % 32) == 0 || Size == RemainingBytes) {
345     // (Note no newline in format string - it will overwrite the line each time)
346     UnicodeSPrint (
347       OutputString,
348       sizeof (OutputString),
349       L"\r%8d / %8d bytes downloaded (%d%%)",
350       mBytesReceivedSoFar,
351       mNumDataBytes,
352       (mBytesReceivedSoFar * 100) / mNumDataBytes // percentage
353       );
354     mTextOut->OutputString (mTextOut, OutputString);
355   }
356 
357   if (mBytesReceivedSoFar == mNumDataBytes) {
358     // Download finished.
359 
360     mTextOut->OutputString (mTextOut, L"\r\n");
361     SEND_LITERAL ("OKAY");
362     mState = ExpectCmdState;
363   }
364 }
365 
366 /*
367   This is the NotifyFunction passed to CreateEvent in the FastbootAppEntryPoint
368   It will be called by the UEFI event framework when the transport protocol
369   implementation signals that data has been received from the Fastboot host.
370   The parameters are ignored.
371 */
372 STATIC
373 VOID
DataReady(IN EFI_EVENT Event,IN VOID * Context)374 DataReady (
375   IN EFI_EVENT  Event,
376   IN VOID      *Context
377   )
378 {
379   UINTN       Size;
380   VOID       *Data;
381   EFI_STATUS  Status;
382 
383   do {
384     Status = mTransport->Receive (&Size, &Data);
385     if (!EFI_ERROR (Status)) {
386       if (mState == ExpectCmdState) {
387         AcceptCmd (Size, (CHAR8 *) Data);
388       } else if (mState == ExpectDataState) {
389         AcceptData (Size, Data);
390       } else {
391         ASSERT (FALSE);
392       }
393       FreePool (Data);
394     }
395   } while (!EFI_ERROR (Status));
396 
397   // Quit if there was a fatal error
398   if (Status != EFI_NOT_READY) {
399     ASSERT (Status == EFI_DEVICE_ERROR);
400     // (Put a newline at the beginning as we are probably in the data phase,
401     //  so the download progress line, with no '\n' is probably on the console)
402     mTextOut->OutputString (mTextOut, L"\r\nFatal error receiving data. Exiting.\r\n");
403     gBS->SignalEvent (mFinishedEvent);
404   }
405 }
406 
407 /*
408   Event notify for a fatal error in transmission.
409 */
410 STATIC
411 VOID
FatalErrorNotify(IN EFI_EVENT Event,IN VOID * Context)412 FatalErrorNotify (
413   IN EFI_EVENT  Event,
414   IN VOID      *Context
415   )
416 {
417   mTextOut->OutputString (mTextOut, L"Fatal error sending command response. Exiting.\r\n");
418   gBS->SignalEvent (mFinishedEvent);
419 }
420 
421 EFI_STATUS
422 EFIAPI
FastbootAppEntryPoint(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)423 FastbootAppEntryPoint (
424   IN EFI_HANDLE                            ImageHandle,
425   IN EFI_SYSTEM_TABLE                      *SystemTable
426   )
427 {
428   EFI_STATUS                      Status;
429   EFI_EVENT                       ReceiveEvent;
430   EFI_EVENT                       WaitEventArray[2];
431   UINTN                           EventIndex;
432   EFI_SIMPLE_TEXT_INPUT_PROTOCOL *TextIn;
433 
434   mDataBuffer = NULL;
435 
436   Status = gBS->LocateProtocol (
437     &gAndroidFastbootTransportProtocolGuid,
438     NULL,
439     (VOID **) &mTransport
440     );
441   if (EFI_ERROR (Status)) {
442     DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't open Fastboot Transport Protocol: %r\n", Status));
443     return Status;
444   }
445 
446   Status = gBS->LocateProtocol (&gAndroidFastbootPlatformProtocolGuid, NULL, (VOID **) &mPlatform);
447   if (EFI_ERROR (Status)) {
448     DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't open Fastboot Platform Protocol: %r\n", Status));
449     return Status;
450   }
451 
452   Status = mPlatform->Init ();
453   if (EFI_ERROR (Status)) {
454     DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't initialise Fastboot Platform Protocol: %r\n", Status));
455     return Status;
456   }
457 
458   Status = gBS->LocateProtocol (&gEfiSimpleTextOutProtocolGuid, NULL, (VOID **) &mTextOut);
459   if (EFI_ERROR (Status)) {
460     DEBUG ((EFI_D_ERROR,
461       "Fastboot: Couldn't open Text Output Protocol: %r\n", Status
462       ));
463     return Status;
464   }
465 
466   Status = gBS->LocateProtocol (&gEfiSimpleTextInProtocolGuid, NULL, (VOID **) &TextIn);
467   if (EFI_ERROR (Status)) {
468     DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't open Text Input Protocol: %r\n", Status));
469     return Status;
470   }
471 
472   // Disable watchdog
473   Status = gBS->SetWatchdogTimer (0, 0x10000, 0, NULL);
474   if (EFI_ERROR (Status)) {
475     DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't disable watchdog timer: %r\n", Status));
476   }
477 
478   // Create event for receipt of data from the host
479   Status = gBS->CreateEvent (
480                   EVT_NOTIFY_SIGNAL,
481                   TPL_CALLBACK,
482                   DataReady,
483                   NULL,
484                   &ReceiveEvent
485                   );
486   ASSERT_EFI_ERROR (Status);
487 
488   // Create event for exiting application when "continue" command is received
489   Status = gBS->CreateEvent (0, TPL_CALLBACK, NULL, NULL, &mFinishedEvent);
490   ASSERT_EFI_ERROR (Status);
491 
492   // Create event to pass to FASTBOOT_TRANSPORT_PROTOCOL.Send, signalling a
493   // fatal error
494   Status = gBS->CreateEvent (
495                  EVT_NOTIFY_SIGNAL,
496                  TPL_CALLBACK,
497                  FatalErrorNotify,
498                  NULL,
499                  &mFatalSendErrorEvent
500                  );
501   ASSERT_EFI_ERROR (Status);
502 
503 
504   // Start listening for data
505   Status = mTransport->Start (
506     ReceiveEvent
507     );
508   if (EFI_ERROR (Status)) {
509     DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't start transport: %r\n", Status));
510     return Status;
511   }
512 
513   // Talk to the user
514   mTextOut->OutputString (mTextOut,
515       L"Android Fastboot mode - version " ANDROID_FASTBOOT_VERSION ". Press any key to quit.\r\n");
516 
517   // Quit when the user presses any key, or mFinishedEvent is signalled
518   WaitEventArray[0] = mFinishedEvent;
519   WaitEventArray[1] = TextIn->WaitForKey;
520   gBS->WaitForEvent (2, WaitEventArray, &EventIndex);
521 
522   mTransport->Stop ();
523   if (EFI_ERROR (Status)) {
524     DEBUG ((EFI_D_ERROR, "Warning: Fastboot Transport Stop: %r\n", Status));
525   }
526   mPlatform->UnInit ();
527 
528   return EFI_SUCCESS;
529 }
530