Home | History | Annotate | Download | only in AndroidFastboot
      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
     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
     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
    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
    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
    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
    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
    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
    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
    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
    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
    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