EmbeddedPkg/AndroidFastboot: Introduce Android FastBoot Application
authorOlivier Martin <olivier.martin@arm.com>
Wed, 5 Mar 2014 04:15:44 +0000 (04:15 +0000)
committeroliviermartin <oliviermartin@6f19259b-4bc3-4df7-8a09-765794883524>
Wed, 5 Mar 2014 04:15:44 +0000 (04:15 +0000)
This application enables Android FastBoot on UEFI.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Olivier Martin <olivier.martin@arm.com>
git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@15312 6f19259b-4bc3-4df7-8a09-765794883524

EmbeddedPkg/Application/AndroidFastboot/AndroidBootImg.c [new file with mode: 0644]
EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.c [new file with mode: 0644]
EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.h [new file with mode: 0644]
EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.inf [new file with mode: 0644]
EmbeddedPkg/Application/AndroidFastboot/Arm/BootAndroidBootImg.c [new file with mode: 0644]
EmbeddedPkg/EmbeddedPkg.dsc

diff --git a/EmbeddedPkg/Application/AndroidFastboot/AndroidBootImg.c b/EmbeddedPkg/Application/AndroidFastboot/AndroidBootImg.c
new file mode 100644 (file)
index 0000000..bbca90f
--- /dev/null
@@ -0,0 +1,90 @@
+/** @file\r
+\r
+  Copyright (c) 2013-2014, ARM Ltd. All rights reserved.<BR>\r
+\r
+  This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions of the BSD License\r
+  which accompanies this distribution.  The full text of the license may be found at\r
+  http://opensource.org/licenses/bsd-license.php\r
+\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "AndroidFastbootApp.h"\r
+\r
+#define BOOT_MAGIC        "ANDROID!"\r
+#define BOOT_MAGIC_LENGTH sizeof (BOOT_MAGIC) - 1\r
+\r
+// Check Val (unsigned) is a power of 2 (has only one bit set)\r
+#define IS_POWER_OF_2(Val) (Val != 0 && ((Val & (Val - 1)) == 0))\r
+\r
+// No documentation for this really - sizes of fields has been determined\r
+// empirically.\r
+#pragma pack(1)\r
+typedef struct {\r
+  CHAR8   BootMagic[BOOT_MAGIC_LENGTH];\r
+  UINT32  KernelSize;\r
+  UINT32  KernelAddress;\r
+  UINT32  RamdiskSize;\r
+  UINT32  RamdiskAddress;\r
+  UINT32  SecondStageBootloaderSize;\r
+  UINT32  SecondStageBootloaderAddress;\r
+  UINT32  KernelTaggsAddress;\r
+  UINT32  PageSize;\r
+  UINT32  Reserved[2];\r
+  CHAR8   ProductName[16];\r
+  CHAR8   KernelArgs[BOOTIMG_KERNEL_ARGS_SIZE];\r
+  UINT32  Id[32];\r
+} ANDROID_BOOTIMG_HEADER;\r
+#pragma pack()\r
+\r
+// Find the kernel and ramdisk in an Android boot.img.\r
+// return EFI_INVALID_PARAMTER if the boot.img is invalid (i.e. doesn't have the\r
+//  right magic value),\r
+// return EFI_NOT_FOUND if there was no kernel in the boot.img.\r
+// Note that the Ramdisk is optional - *Ramdisk won't be touched if it isn't\r
+// present, but RamdiskSize will be set to 0.\r
+EFI_STATUS\r
+ParseAndroidBootImg (\r
+  IN  VOID    *BootImg,\r
+  OUT VOID   **Kernel,\r
+  OUT UINTN   *KernelSize,\r
+  OUT VOID   **Ramdisk,\r
+  OUT UINTN   *RamdiskSize,\r
+  OUT CHAR8   *KernelArgs\r
+  )\r
+{\r
+  ANDROID_BOOTIMG_HEADER   *Header;\r
+  UINT8                    *BootImgBytePtr;\r
+\r
+  // Cast to UINT8 so we can do pointer arithmetic\r
+  BootImgBytePtr = (UINT8 *) BootImg;\r
+\r
+  Header = (ANDROID_BOOTIMG_HEADER *) BootImg;\r
+\r
+  if (AsciiStrnCmp (Header->BootMagic, BOOT_MAGIC, BOOT_MAGIC_LENGTH) != 0) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (Header->KernelSize == 0) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  ASSERT (IS_POWER_OF_2 (Header->PageSize));\r
+\r
+  *KernelSize = Header->KernelSize;\r
+  *Kernel = BootImgBytePtr + Header->PageSize;\r
+  *RamdiskSize = Header->RamdiskSize;\r
+\r
+  if (Header->RamdiskSize != 0) {\r
+    *Ramdisk = (VOID *) (BootImgBytePtr\r
+                 + Header->PageSize\r
+                 + ALIGN_VALUE (Header->KernelSize, Header->PageSize));\r
+  }\r
+\r
+  AsciiStrnCpy (KernelArgs, Header->KernelArgs, BOOTIMG_KERNEL_ARGS_SIZE);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
diff --git a/EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.c b/EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.c
new file mode 100644 (file)
index 0000000..f380817
--- /dev/null
@@ -0,0 +1,524 @@
+/** @file\r
+\r
+  Copyright (c) 2013-2014, ARM Ltd. All rights reserved.<BR>\r
+\r
+  This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions of the BSD License\r
+  which accompanies this distribution.  The full text of the license may be found at\r
+  http://opensource.org/licenses/bsd-license.php\r
+\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "AndroidFastbootApp.h"\r
+\r
+#include <Protocol/AndroidFastbootTransport.h>\r
+#include <Protocol/AndroidFastbootPlatform.h>\r
+#include <Protocol/SimpleTextOut.h>\r
+#include <Protocol/SimpleTextIn.h>\r
+\r
+#include <Library/PcdLib.h>\r
+#include <Library/UefiRuntimeServicesTableLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/UefiApplicationEntryPoint.h>\r
+#include <Library/PrintLib.h>\r
+\r
+/*\r
+ * UEFI Application using the FASTBOOT_TRANSPORT_PROTOCOL and\r
+ * FASTBOOT_PLATFORM_PROTOCOL to implement the Android Fastboot protocol.\r
+ */\r
+\r
+STATIC FASTBOOT_TRANSPORT_PROTOCOL *mTransport;\r
+STATIC FASTBOOT_PLATFORM_PROTOCOL  *mPlatform;\r
+\r
+STATIC EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *mTextOut;\r
+\r
+typedef enum {\r
+  ExpectCmdState,\r
+  ExpectDataState,\r
+  FastbootStateMax\r
+} ANDROID_FASTBOOT_STATE;\r
+\r
+STATIC ANDROID_FASTBOOT_STATE mState = ExpectCmdState;\r
+\r
+// When in ExpectDataState, the number of bytes of data to expect:\r
+STATIC UINT32 mNumDataBytes;\r
+// .. and the number of bytes so far received this data phase\r
+STATIC UINT32 mBytesReceivedSoFar;\r
+// .. and the buffer to save data into\r
+STATIC UINT8 *mDataBuffer = NULL;\r
+\r
+// Event notify functions, from which gBS->Exit shouldn't be called, can signal\r
+// this event when the application should exit\r
+STATIC EFI_EVENT mFinishedEvent;\r
+\r
+STATIC EFI_EVENT mFatalSendErrorEvent;\r
+\r
+// This macro uses sizeof - only use it on arrays (i.e. string literals)\r
+#define SEND_LITERAL(Str) mTransport->Send (                  \\r
+                                        sizeof (Str) - 1,     \\r
+                                        Str,                  \\r
+                                        &mFatalSendErrorEvent \\r
+                                        )\r
+#define MATCH_CMD_LITERAL(Cmd, Buf) !AsciiStrnCmp (Cmd, Buf, sizeof (Cmd) - 1)\r
+\r
+#define IS_LOWERCASE_ASCII(Char) (Char >= 'a' && Char <= 'z')\r
+\r
+#define FASTBOOT_STRING_MAX_LENGTH  256\r
+#define FASTBOOT_COMMAND_MAX_LENGTH 64\r
+\r
+STATIC\r
+VOID\r
+HandleGetVar (\r
+  IN CHAR8 *CmdArg\r
+  )\r
+{\r
+  CHAR8      Response[FASTBOOT_COMMAND_MAX_LENGTH + 1] = "OKAY";\r
+  EFI_STATUS Status;\r
+\r
+  // Respond to getvar:version with 0.4 (version of Fastboot protocol)\r
+  if (!AsciiStrnCmp ("version", CmdArg, sizeof ("version") - 1 )) {\r
+    SEND_LITERAL ("OKAY" ANDROID_FASTBOOT_VERSION);\r
+  } else {\r
+    // All other variables are assumed to be platform specific\r
+    Status = mPlatform->GetVar (CmdArg, Response + 4);\r
+    if (EFI_ERROR (Status)) {\r
+      SEND_LITERAL ("FAILSomething went wrong when looking up the variable");\r
+    } else {\r
+      mTransport->Send (AsciiStrLen (Response), Response, &mFatalSendErrorEvent);\r
+    }\r
+  }\r
+}\r
+\r
+STATIC\r
+VOID\r
+HandleDownload (\r
+  IN CHAR8 *NumBytesString\r
+  )\r
+{\r
+  CHAR8       Response[12] = "DATA";\r
+  CHAR16      OutputString[FASTBOOT_STRING_MAX_LENGTH];\r
+\r
+  // Argument is 8-character ASCII string hex representation of number of bytes\r
+  // that will be sent in the data phase.\r
+  // Response is "DATA" + that same 8-character string.\r
+\r
+  // Replace any previously downloaded data\r
+  if (mDataBuffer != NULL) {\r
+    FreePool (mDataBuffer);\r
+    mDataBuffer = NULL;\r
+  }\r
+\r
+  // Parse out number of data bytes to expect\r
+  mNumDataBytes = AsciiStrHexToUint64 (NumBytesString);\r
+  if (mNumDataBytes == 0) {\r
+    mTextOut->OutputString (mTextOut, L"ERROR: Fail to get the number of bytes to download.\r\n");\r
+    SEND_LITERAL ("FAILFailed to get the number of bytes to download");\r
+    return;\r
+  }\r
+\r
+  UnicodeSPrint (OutputString, sizeof (OutputString), L"Downloading %d bytes\r\n", mNumDataBytes);\r
+  mTextOut->OutputString (mTextOut, OutputString);\r
+\r
+  mDataBuffer = AllocatePool (mNumDataBytes);\r
+  if (mDataBuffer == NULL) {\r
+    SEND_LITERAL ("FAILNot enough memory");\r
+  } else {\r
+    AsciiStrnCpy (Response + 4, NumBytesString, 8);\r
+    mTransport->Send (sizeof(Response), Response, &mFatalSendErrorEvent);\r
+\r
+    mState = ExpectDataState;\r
+    mBytesReceivedSoFar = 0;\r
+  }\r
+}\r
+\r
+STATIC\r
+VOID\r
+HandleFlash (\r
+  IN CHAR8 *PartitionName\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+  CHAR16      OutputString[FASTBOOT_STRING_MAX_LENGTH];\r
+\r
+  // Build output string\r
+  UnicodeSPrint (OutputString, sizeof (OutputString), L"Flashing partition %a\r\n", PartitionName);\r
+  mTextOut->OutputString (mTextOut, OutputString);\r
+\r
+  if (mDataBuffer == NULL) {\r
+    // Doesn't look like we were sent any data\r
+    SEND_LITERAL ("FAILNo data to flash");\r
+    return;\r
+  }\r
+\r
+  Status = mPlatform->FlashPartition (\r
+                        PartitionName,\r
+                        mNumDataBytes,\r
+                        mDataBuffer\r
+                        );\r
+  if (Status == EFI_NOT_FOUND) {\r
+    SEND_LITERAL ("FAILNo such partition.");\r
+    mTextOut->OutputString (mTextOut, L"No such partition.\r\n");\r
+  } else if (EFI_ERROR (Status)) {\r
+    SEND_LITERAL ("FAILError flashing partition.");\r
+    mTextOut->OutputString (mTextOut, L"Error flashing partition.\r\n");\r
+    DEBUG ((EFI_D_ERROR, "Couldn't flash image:  %r\n", Status));\r
+  } else {\r
+    mTextOut->OutputString (mTextOut, L"Done.\r\n");\r
+    SEND_LITERAL ("OKAY");\r
+  }\r
+}\r
+\r
+STATIC\r
+VOID\r
+HandleErase (\r
+  IN CHAR8 *PartitionName\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+  CHAR16      OutputString[FASTBOOT_STRING_MAX_LENGTH];\r
+\r
+  // Build output string\r
+  UnicodeSPrint (OutputString, sizeof (OutputString), L"Erasing partition %a\r\n", PartitionName);\r
+  mTextOut->OutputString (mTextOut, OutputString);\r
+\r
+  Status = mPlatform->ErasePartition (PartitionName);\r
+  if (EFI_ERROR (Status)) {\r
+    SEND_LITERAL ("FAILCheck device console.");\r
+    DEBUG ((EFI_D_ERROR, "Couldn't erase image:  %r\n", Status));\r
+  } else {\r
+    SEND_LITERAL ("OKAY");\r
+  }\r
+}\r
+\r
+STATIC\r
+VOID\r
+HandleBoot (\r
+  VOID\r
+  )\r
+{\r
+  EFI_STATUS Status;\r
+\r
+  mTextOut->OutputString (mTextOut, L"Booting downloaded image\r\n");\r
+\r
+  if (mDataBuffer == NULL) {\r
+    // Doesn't look like we were sent any data\r
+    SEND_LITERAL ("FAILNo image in memory");\r
+    return;\r
+  }\r
+\r
+  // We don't really have any choice but to report success, because once we\r
+  // boot we lose control of the system.\r
+  SEND_LITERAL ("OKAY");\r
+\r
+  Status = BootAndroidBootImg (mNumDataBytes, mDataBuffer);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((EFI_D_ERROR, "Failed to boot downloaded image: %r\n", Status));\r
+  }\r
+  // We shouldn't get here\r
+}\r
+\r
+STATIC\r
+VOID\r
+HandleOemCommand (\r
+  IN CHAR8 *Command\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+\r
+  Status = mPlatform->DoOemCommand (Command);\r
+  if (Status == EFI_NOT_FOUND) {\r
+    SEND_LITERAL ("FAILOEM Command not recognised.");\r
+  } else if (Status == EFI_DEVICE_ERROR) {\r
+    SEND_LITERAL ("FAILError while executing command");\r
+  } else if (EFI_ERROR (Status)) {\r
+    SEND_LITERAL ("FAIL");\r
+  } else {\r
+    SEND_LITERAL ("OKAY");\r
+  }\r
+}\r
+\r
+STATIC\r
+VOID\r
+AcceptCmd (\r
+  IN        UINTN  Size,\r
+  IN  CONST CHAR8 *Data\r
+  )\r
+{\r
+  CHAR8       Command[FASTBOOT_COMMAND_MAX_LENGTH + 1];\r
+\r
+  // Max command size is 64 bytes\r
+  if (Size > FASTBOOT_COMMAND_MAX_LENGTH) {\r
+    SEND_LITERAL ("FAILCommand too large");\r
+    return;\r
+  }\r
+\r
+  // Commands aren't null-terminated. Let's get a null-terminated version.\r
+  AsciiStrnCpy (Command, Data, Size);\r
+  Command[Size] = '\0';\r
+\r
+  // Parse command\r
+  if (MATCH_CMD_LITERAL ("getvar", Command)) {\r
+    HandleGetVar (Command + sizeof ("getvar"));\r
+  } else if (MATCH_CMD_LITERAL ("download", Command)) {\r
+    HandleDownload (Command + sizeof ("download"));\r
+  } else if (MATCH_CMD_LITERAL ("verify", Command)) {\r
+    SEND_LITERAL ("FAILNot supported");\r
+  } else if (MATCH_CMD_LITERAL ("flash", Command)) {\r
+    HandleFlash (Command + sizeof ("flash"));\r
+  } else if (MATCH_CMD_LITERAL ("erase", Command)) {\r
+    HandleErase (Command + sizeof ("erase"));\r
+  } else if (MATCH_CMD_LITERAL ("boot", Command)) {\r
+    HandleBoot ();\r
+  } else if (MATCH_CMD_LITERAL ("continue", Command)) {\r
+    SEND_LITERAL ("OKAY");\r
+    mTextOut->OutputString (mTextOut, L"Received 'continue' command. Exiting Fastboot mode\r\n");\r
+\r
+    gBS->SignalEvent (mFinishedEvent);\r
+  } else if (MATCH_CMD_LITERAL ("reboot", Command)) {\r
+    if (MATCH_CMD_LITERAL ("reboot-booloader", Command)) {\r
+      // fastboot_protocol.txt:\r
+      //    "reboot-bootloader    Reboot back into the bootloader."\r
+      // I guess this means reboot back into fastboot mode to save the user\r
+      // having to do whatever they did to get here again.\r
+      // Here we just reboot normally.\r
+      SEND_LITERAL ("INFOreboot-bootloader not supported, rebooting normally.");\r
+    }\r
+    SEND_LITERAL ("OKAY");\r
+    gRT->ResetSystem (EfiResetCold, EFI_SUCCESS, 0, NULL);\r
+\r
+    // Shouldn't get here\r
+    DEBUG ((EFI_D_ERROR, "Fastboot: gRT->ResetSystem didn't work\n"));\r
+  } else if (MATCH_CMD_LITERAL ("powerdown", Command)) {\r
+    SEND_LITERAL ("OKAY");\r
+    gRT->ResetSystem (EfiResetShutdown, EFI_SUCCESS, 0, NULL);\r
+\r
+    // Shouldn't get here\r
+    DEBUG ((EFI_D_ERROR, "Fastboot: gRT->ResetSystem didn't work\n"));\r
+  } else if (MATCH_CMD_LITERAL ("oem", Command)) {\r
+    // The "oem" command isn't in the specification, but it was observed in the\r
+    // wild, followed by a space, followed by the actual command.\r
+    HandleOemCommand (Command + sizeof ("oem"));\r
+  } else if (IS_LOWERCASE_ASCII (Command[0])) {\r
+    // Commands starting with lowercase ASCII characters are reserved for the\r
+    // Fastboot protocol. If we don't recognise it, it's probably the future\r
+    // and there are new commmands in the protocol.\r
+    // (By the way, the "oem" command mentioned above makes this reservation\r
+    //  redundant, but we handle it here to be spec-compliant)\r
+    SEND_LITERAL ("FAILCommand not recognised. Check Fastboot version.");\r
+  } else {\r
+    HandleOemCommand (Command);\r
+  }\r
+}\r
+\r
+STATIC\r
+VOID\r
+AcceptData (\r
+  IN  UINTN  Size,\r
+  IN  VOID  *Data\r
+  )\r
+{\r
+  UINT32 RemainingBytes = mNumDataBytes - mBytesReceivedSoFar;\r
+  CHAR16 OutputString[FASTBOOT_STRING_MAX_LENGTH];\r
+  STATIC UINTN Count = 0;\r
+\r
+  // Protocol doesn't say anything about sending extra data so just ignore it.\r
+  if (Size > RemainingBytes) {\r
+    Size = RemainingBytes;\r
+  }\r
+\r
+  CopyMem (&mDataBuffer[mBytesReceivedSoFar], Data, Size);\r
+\r
+  mBytesReceivedSoFar += Size;\r
+\r
+  // Show download progress. Don't do it for every packet  as outputting text\r
+  // might be time consuming - do it on the last packet and on every 32nd packet\r
+  if ((Count++ % 32) == 0 || Size == RemainingBytes) {\r
+    // (Note no newline in format string - it will overwrite the line each time)\r
+    UnicodeSPrint (\r
+      OutputString,\r
+      sizeof (OutputString),\r
+      L"\r%8d / %8d bytes downloaded (%d%%)",\r
+      mBytesReceivedSoFar,\r
+      mNumDataBytes,\r
+      (mBytesReceivedSoFar * 100) / mNumDataBytes // percentage\r
+      );\r
+    mTextOut->OutputString (mTextOut, OutputString);\r
+  }\r
+\r
+  if (mBytesReceivedSoFar == mNumDataBytes) {\r
+    // Download finished.\r
+\r
+    mTextOut->OutputString (mTextOut, L"\r\n");\r
+    SEND_LITERAL ("OKAY");\r
+    mState = ExpectCmdState;\r
+  }\r
+}\r
+\r
+/*\r
+  This is the NotifyFunction passed to CreateEvent in the FastbootAppEntryPoint\r
+  It will be called by the UEFI event framework when the transport protocol\r
+  implementation signals that data has been received from the Fastboot host.\r
+  The parameters are ignored.\r
+*/\r
+STATIC\r
+VOID\r
+DataReady (\r
+  IN EFI_EVENT  Event,\r
+  IN VOID      *Context\r
+  )\r
+{\r
+  UINTN       Size;\r
+  VOID       *Data;\r
+  EFI_STATUS  Status;\r
+\r
+  do {\r
+    Status = mTransport->Receive (&Size, &Data);\r
+    if (!EFI_ERROR (Status)) {\r
+      if (mState == ExpectCmdState) {\r
+        AcceptCmd (Size, (CHAR8 *) Data);\r
+      } else if (mState == ExpectDataState) {\r
+        AcceptData (Size, Data);\r
+      } else {\r
+        ASSERT (FALSE);\r
+      }\r
+      FreePool (Data);\r
+    }\r
+  } while (!EFI_ERROR (Status));\r
+\r
+  // Quit if there was a fatal error\r
+  if (Status != EFI_NOT_READY) {\r
+    ASSERT (Status == EFI_DEVICE_ERROR);\r
+    // (Put a newline at the beginning as we are probably in the data phase,\r
+    //  so the download progress line, with no '\n' is probably on the console)\r
+    mTextOut->OutputString (mTextOut, L"\r\nFatal error receiving data. Exiting.\r\n");\r
+    gBS->SignalEvent (mFinishedEvent);\r
+  }\r
+}\r
+\r
+/*\r
+  Event notify for a fatal error in transmission.\r
+*/\r
+STATIC\r
+VOID\r
+FatalErrorNotify (\r
+  IN EFI_EVENT  Event,\r
+  IN VOID      *Context\r
+  )\r
+{\r
+  mTextOut->OutputString (mTextOut, L"Fatal error sending command response. Exiting.\r\n");\r
+  gBS->SignalEvent (mFinishedEvent);\r
+}\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+FastbootAppEntryPoint (\r
+  IN EFI_HANDLE                            ImageHandle,\r
+  IN EFI_SYSTEM_TABLE                      *SystemTable\r
+  )\r
+{\r
+  EFI_STATUS                      Status;\r
+  EFI_EVENT                       ReceiveEvent;\r
+  EFI_EVENT                       WaitEventArray[2];\r
+  UINTN                           EventIndex;\r
+  EFI_SIMPLE_TEXT_INPUT_PROTOCOL *TextIn;\r
+\r
+  mDataBuffer = NULL;\r
+\r
+  Status = gBS->LocateProtocol (\r
+    &gAndroidFastbootTransportProtocolGuid,\r
+    NULL,\r
+    (VOID **) &mTransport\r
+    );\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't open Fastboot Transport Protocol: %r\n", Status));\r
+    return Status;\r
+  }\r
+\r
+  Status = gBS->LocateProtocol (&gAndroidFastbootPlatformProtocolGuid, NULL, (VOID **) &mPlatform);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't open Fastboot Platform Protocol: %r\n", Status));\r
+    return Status;\r
+  }\r
+\r
+  Status = mPlatform->Init ();\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't initialise Fastboot Platform Protocol: %r\n", Status));\r
+    return Status;\r
+  }\r
+\r
+  Status = gBS->LocateProtocol (&gEfiSimpleTextOutProtocolGuid, NULL, (VOID **) &mTextOut);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((EFI_D_ERROR,\r
+      "Fastboot: Couldn't open Text Output Protocol: %r\n", Status\r
+      ));\r
+    return Status;\r
+  }\r
+\r
+  Status = gBS->LocateProtocol (&gEfiSimpleTextInProtocolGuid, NULL, (VOID **) &TextIn);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't open Text Input Protocol: %r\n", Status));\r
+    return Status;\r
+  }\r
+\r
+  // Disable watchdog\r
+  Status = gBS->SetWatchdogTimer (0, 0x10000, 0, NULL);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't disable watchdog timer: %r\n", Status));\r
+  }\r
+\r
+  // Create event for receipt of data from the host\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_CALLBACK,\r
+                  DataReady,\r
+                  NULL,\r
+                  &ReceiveEvent\r
+                  );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  // Create event for exiting application when "continue" command is received\r
+  Status = gBS->CreateEvent (0, TPL_CALLBACK, NULL, NULL, &mFinishedEvent);\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  // Create event to pass to FASTBOOT_TRANSPORT_PROTOCOL.Send, signalling a\r
+  // fatal error\r
+  Status = gBS->CreateEvent (\r
+                 EVT_NOTIFY_SIGNAL,\r
+                 TPL_CALLBACK,\r
+                 FatalErrorNotify,\r
+                 NULL,\r
+                 &mFatalSendErrorEvent\r
+                 );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+\r
+  // Start listening for data\r
+  Status = mTransport->Start (\r
+    ReceiveEvent\r
+    );\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't start transport: %r\n", Status));\r
+    return Status;\r
+  }\r
+\r
+  // Talk to the user\r
+  mTextOut->OutputString (mTextOut,\r
+      L"Android Fastboot mode - version " ANDROID_FASTBOOT_VERSION ". Press any key to quit.\r\n");\r
+\r
+  // Quit when the user presses any key, or mFinishedEvent is signalled\r
+  WaitEventArray[0] = mFinishedEvent;\r
+  WaitEventArray[1] = TextIn->WaitForKey;\r
+  gBS->WaitForEvent (2, WaitEventArray, &EventIndex);\r
+\r
+  mTransport->Stop ();\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((EFI_D_ERROR, "Warning: Fastboot Transport Stop: %r\n", Status));\r
+  }\r
+  mPlatform->UnInit ();\r
+\r
+  return EFI_SUCCESS;\r
+}\r
diff --git a/EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.h b/EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.h
new file mode 100644 (file)
index 0000000..f62660f
--- /dev/null
@@ -0,0 +1,42 @@
+/** @file\r
+\r
+  Copyright (c) 2013-2014, ARM Ltd. All rights reserved.<BR>\r
+\r
+  This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions of the BSD License\r
+  which accompanies this distribution.  The full text of the license may be found at\r
+  http://opensource.org/licenses/bsd-license.php\r
+\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef __ANDROID_FASTBOOT_APP_H__\r
+#define __ANDROID_FASTBOOT_APP_H__\r
+\r
+#include <Library/BaseLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+\r
+#define BOOTIMG_KERNEL_ARGS_SIZE 512\r
+\r
+#define ANDROID_FASTBOOT_VERSION "0.4"\r
+\r
+EFI_STATUS\r
+BootAndroidBootImg (\r
+  IN  UINTN    BufferSize,\r
+  IN  VOID    *Buffer\r
+  );\r
+\r
+EFI_STATUS\r
+ParseAndroidBootImg (\r
+  IN  VOID    *BootImg,\r
+  OUT VOID   **Kernel,\r
+  OUT UINTN   *KernelSize,\r
+  OUT VOID   **Ramdisk,\r
+  OUT UINTN   *RamdiskSize,\r
+  OUT CHAR8   *KernelArgs\r
+  );\r
+\r
+#endif //ifdef __ANDROID_FASTBOOT_APP_H__\r
diff --git a/EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.inf b/EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.inf
new file mode 100644 (file)
index 0000000..ab9354c
--- /dev/null
@@ -0,0 +1,60 @@
+#/** @file\r
+#\r
+#  Copyright (c) 2013-2014, ARM Ltd. All rights reserved.<BR>\r
+#\r
+#  This program and the accompanying materials\r
+#  are licensed and made available under the terms and conditions of the BSD License\r
+#  which accompanies this distribution. The full text of the license may be found at\r
+#  http://opensource.org/licenses/bsd-license.php\r
+#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+#  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+#\r
+#\r
+#**/\r
+\r
+[Defines]\r
+  INF_VERSION                    = 0x00010005\r
+  BASE_NAME                      = AndroidFastbootApp\r
+  FILE_GUID                      = 9588502a-5370-11e3-8631-d7c5951364c8\r
+  MODULE_TYPE                    = UEFI_APPLICATION\r
+  VERSION_STRING                 = 1.0\r
+  ENTRY_POINT                    = FastbootAppEntryPoint\r
+\r
+[Sources.common]\r
+  AndroidFastbootApp.c\r
+  AndroidBootImg.c\r
+\r
+[Sources.ARM, Sources.AARCH64]\r
+  Arm/BootAndroidBootImg.c\r
+\r
+[LibraryClasses]\r
+  BaseLib\r
+  BaseMemoryLib\r
+  BdsLib\r
+  DebugLib\r
+  DevicePathLib\r
+  DxeServicesTableLib\r
+  MemoryAllocationLib\r
+  PcdLib\r
+  PrintLib\r
+  UefiApplicationEntryPoint\r
+  UefiBootServicesTableLib\r
+  UefiRuntimeServicesTableLib\r
+\r
+[Protocols]\r
+  gAndroidFastbootTransportProtocolGuid\r
+  gAndroidFastbootPlatformProtocolGuid\r
+  gEfiSimpleTextOutProtocolGuid\r
+  gEfiSimpleTextInProtocolGuid\r
+\r
+[Packages]\r
+  MdePkg/MdePkg.dec\r
+  MdeModulePkg/MdeModulePkg.dec\r
+  EmbeddedPkg/EmbeddedPkg.dec\r
+\r
+[Packages.ARM, Packages.AARCH64]\r
+  ArmPkg/ArmPkg.dec\r
+  ArmPlatformPkg/ArmPlatformPkg.dec\r
+\r
+[Guids.ARM, Guids.AARCH64]\r
+  gArmGlobalVariableGuid\r
diff --git a/EmbeddedPkg/Application/AndroidFastboot/Arm/BootAndroidBootImg.c b/EmbeddedPkg/Application/AndroidFastboot/Arm/BootAndroidBootImg.c
new file mode 100644 (file)
index 0000000..6f4b66b
--- /dev/null
@@ -0,0 +1,125 @@
+/** @file\r
+\r
+  Copyright (c) 2013-2014, ARM Ltd. All rights reserved.<BR>\r
+\r
+  This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions of the BSD License\r
+  which accompanies this distribution.  The full text of the license may be found at\r
+  http://opensource.org/licenses/bsd-license.php\r
+\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "AndroidFastbootApp.h"\r
+\r
+#include <Protocol/DevicePath.h>\r
+\r
+#include <Library/BdsLib.h>\r
+#include <Library/DevicePathLib.h>\r
+\r
+#include <Guid/ArmGlobalVariableHob.h>\r
+\r
+// Device Path representing an image in memory\r
+#pragma pack(1)\r
+typedef struct {\r
+  MEMMAP_DEVICE_PATH                      Node1;\r
+  EFI_DEVICE_PATH_PROTOCOL                End;\r
+} MEMORY_DEVICE_PATH;\r
+#pragma pack()\r
+\r
+STATIC CONST MEMORY_DEVICE_PATH MemoryDevicePathTemplate =\r
+{\r
+  {\r
+    {\r
+      HARDWARE_DEVICE_PATH,\r
+      HW_MEMMAP_DP,\r
+      {\r
+        (UINT8)(sizeof (MEMMAP_DEVICE_PATH)),\r
+        (UINT8)((sizeof (MEMMAP_DEVICE_PATH)) >> 8),\r
+      },\r
+    }, // Header\r
+    0, // StartingAddress (set at runtime)\r
+    0  // EndingAddress   (set at runtime)\r
+  }, // Node1\r
+  {\r
+    END_DEVICE_PATH_TYPE,\r
+    END_ENTIRE_DEVICE_PATH_SUBTYPE,\r
+    sizeof (EFI_DEVICE_PATH_PROTOCOL),\r
+    0\r
+  } // End\r
+};\r
+\r
+EFI_STATUS\r
+BootAndroidBootImg (\r
+  IN UINTN    BufferSize,\r
+  IN VOID    *Buffer\r
+  )\r
+{\r
+  EFI_DEVICE_PATH_PROTOCOL           *FdtDevicePath;\r
+  EFI_STATUS                          Status;\r
+  CHAR8                               KernelArgs[BOOTIMG_KERNEL_ARGS_SIZE];\r
+  VOID                               *Kernel;\r
+  UINTN                               KernelSize;\r
+  VOID                               *Ramdisk;\r
+  UINTN                               RamdiskSize;\r
+  MEMORY_DEVICE_PATH                  KernelDevicePath;\r
+  MEMORY_DEVICE_PATH*                 RamdiskDevicePath;\r
+\r
+  Status = ParseAndroidBootImg (\r
+            Buffer,\r
+            &Kernel,\r
+            &KernelSize,\r
+            &Ramdisk,\r
+            &RamdiskSize,\r
+            KernelArgs\r
+            );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  KernelDevicePath = MemoryDevicePathTemplate;\r
+\r
+  // Have to cast to UINTN before casting to EFI_PHYSICAL_ADDRESS in order to\r
+  // appease GCC.\r
+  KernelDevicePath.Node1.StartingAddress = (EFI_PHYSICAL_ADDRESS)(UINTN) Kernel;\r
+  KernelDevicePath.Node1.EndingAddress   = (EFI_PHYSICAL_ADDRESS)(UINTN) Kernel + KernelSize;\r
+\r
+  RamdiskDevicePath = NULL;\r
+  if (RamdiskSize != 0) {\r
+    RamdiskDevicePath = (MEMORY_DEVICE_PATH*)DuplicateDevicePath ((EFI_DEVICE_PATH_PROTOCOL*) &MemoryDevicePathTemplate);\r
+\r
+    RamdiskDevicePath->Node1.StartingAddress = (EFI_PHYSICAL_ADDRESS)(UINTN) Ramdisk;\r
+    RamdiskDevicePath->Node1.EndingAddress   = ((EFI_PHYSICAL_ADDRESS)(UINTN) Ramdisk) + RamdiskSize;\r
+  }\r
+\r
+  // Get the default FDT device path\r
+  Status = GetEnvironmentVariable ((CHAR16 *)L"Fdt", &gArmGlobalVariableGuid,\r
+             NULL, 0, (VOID **)&FdtDevicePath);\r
+  if (Status == EFI_NOT_FOUND) {\r
+    DEBUG ((EFI_D_ERROR, "Error: Please update FDT path in boot manager\n"));\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  Status = BdsBootLinuxFdt (\r
+              (EFI_DEVICE_PATH_PROTOCOL *) &KernelDevicePath,\r
+              (EFI_DEVICE_PATH_PROTOCOL *) RamdiskDevicePath,\r
+              KernelArgs,\r
+              FdtDevicePath\r
+              );\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((EFI_D_ERROR, "Couldn't Boot Linux: %d\n", Status));\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  if (RamdiskDevicePath) {\r
+    FreePool (RamdiskDevicePath);\r
+  }\r
+\r
+  // If we got here we do a confused face because BootLinuxFdt returned,\r
+  // reporting success.\r
+  DEBUG ((EFI_D_ERROR, "WARNING: BdsBootLinuxFdt returned EFI_SUCCESS.\n"));\r
+  return EFI_SUCCESS;\r
+}\r
index 9eaf7dc..a9db4ed 100644 (file)
   UefiDriverEntryPoint|MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf\r
   UefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf\r
 \r
-\r
   PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf\r
   EblCmdLib|EmbeddedPkg/Library/EblCmdLibNull/EblCmdLibNull.inf\r
   \r
   EblNetworkLib|EmbeddedPkg/Library/EblNetworkLib/EblNetworkLib.inf\r
-  \r
+\r
+  FdtLib|EmbeddedPkg/Library/FdtLib/FdtLib.inf\r
 \r
 [LibraryClasses.common.DXE_DRIVER]\r
   PcdLib|MdePkg/Library/DxePcdLib/DxePcdLib.inf\r
   ExtractGuidedSectionLib|EmbeddedPkg/Library/PrePiExtractGuidedSectionLib/PrePiExtractGuidedSectionLib.inf\r
 \r
 [LibraryClasses.ARM, LibraryClasses.AARCH64]\r
+  ArmGicLib|ArmPkg/Drivers/ArmGic/ArmGicLib.inf\r
+  ArmSmcLib|ArmPkg/Library/ArmSmcLib/ArmSmcLib.inf\r
+  BdsLib|ArmPkg/Library/BdsLib/BdsLib.inf\r
   SemihostLib|ArmPkg/Library/SemihostLib/SemihostLib.inf\r
   NULL|ArmPkg/Library/CompilerIntrinsicsLib/CompilerIntrinsicsLib.inf\r
 \r
+[LibraryClasses.ARM]\r
+  ArmLib|ArmPkg/Library/ArmLib/ArmV7/ArmV7Lib.inf\r
+\r
+[LibraryClasses.AARCH64]\r
+  ArmLib|ArmPkg/Library/ArmLib/AArch64/AArch64Lib.inf\r
+\r
 \r
 ################################################################################\r
 #\r
 #  RELEASE_*_IA32_DLINK_FLAGS = /ALIGN:4096\r
 #  *_*_IA32_CC_FLAGS = /D EFI_SPECIFICATION_VERSION=0x0002000A /D TIANO_RELEASE_VERSION=0x00080006\r
 \r
+[BuildOptions]\r
+  RVCT:*_*_ARM_PLATFORM_FLAGS == --cpu=7-A.security\r
+\r
 \r
 ################################################################################\r
 #\r
 \r
   EmbeddedPkg/Universal/MmcDxe/MmcDxe.inf\r
 \r
+  EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.inf\r
+\r
 [Components.IA32, Components.X64, Components.IPF, Components.ARM]\r
   EmbeddedPkg/GdbStub/GdbStub.inf\r