EmbeddedPkg/AndroidFastboot: Introduce Android FastBoot Application

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
diff --git a/EmbeddedPkg/Application/AndroidFastboot/AndroidBootImg.c b/EmbeddedPkg/Application/AndroidFastboot/AndroidBootImg.c
new file mode 100644
index 0000000..bbca90f
--- /dev/null
+++ b/EmbeddedPkg/Application/AndroidFastboot/AndroidBootImg.c
@@ -0,0 +1,90 @@
+/** @file

+

+  Copyright (c) 2013-2014, ARM Ltd. All rights reserved.<BR>

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "AndroidFastbootApp.h"

+

+#define BOOT_MAGIC        "ANDROID!"

+#define BOOT_MAGIC_LENGTH sizeof (BOOT_MAGIC) - 1

+

+// Check Val (unsigned) is a power of 2 (has only one bit set)

+#define IS_POWER_OF_2(Val) (Val != 0 && ((Val & (Val - 1)) == 0))

+

+// No documentation for this really - sizes of fields has been determined

+// empirically.

+#pragma pack(1)

+typedef struct {

+  CHAR8   BootMagic[BOOT_MAGIC_LENGTH];

+  UINT32  KernelSize;

+  UINT32  KernelAddress;

+  UINT32  RamdiskSize;

+  UINT32  RamdiskAddress;

+  UINT32  SecondStageBootloaderSize;

+  UINT32  SecondStageBootloaderAddress;

+  UINT32  KernelTaggsAddress;

+  UINT32  PageSize;

+  UINT32  Reserved[2];

+  CHAR8   ProductName[16];

+  CHAR8   KernelArgs[BOOTIMG_KERNEL_ARGS_SIZE];

+  UINT32  Id[32];

+} ANDROID_BOOTIMG_HEADER;

+#pragma pack()

+

+// Find the kernel and ramdisk in an Android boot.img.

+// return EFI_INVALID_PARAMTER if the boot.img is invalid (i.e. doesn't have the

+//  right magic value),

+// return EFI_NOT_FOUND if there was no kernel in the boot.img.

+// Note that the Ramdisk is optional - *Ramdisk won't be touched if it isn't

+// present, but RamdiskSize will be set to 0.

+EFI_STATUS

+ParseAndroidBootImg (

+  IN  VOID    *BootImg,

+  OUT VOID   **Kernel,

+  OUT UINTN   *KernelSize,

+  OUT VOID   **Ramdisk,

+  OUT UINTN   *RamdiskSize,

+  OUT CHAR8   *KernelArgs

+  )

+{

+  ANDROID_BOOTIMG_HEADER   *Header;

+  UINT8                    *BootImgBytePtr;

+

+  // Cast to UINT8 so we can do pointer arithmetic

+  BootImgBytePtr = (UINT8 *) BootImg;

+

+  Header = (ANDROID_BOOTIMG_HEADER *) BootImg;

+

+  if (AsciiStrnCmp (Header->BootMagic, BOOT_MAGIC, BOOT_MAGIC_LENGTH) != 0) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (Header->KernelSize == 0) {

+    return EFI_NOT_FOUND;

+  }

+

+  ASSERT (IS_POWER_OF_2 (Header->PageSize));

+

+  *KernelSize = Header->KernelSize;

+  *Kernel = BootImgBytePtr + Header->PageSize;

+  *RamdiskSize = Header->RamdiskSize;

+

+  if (Header->RamdiskSize != 0) {

+    *Ramdisk = (VOID *) (BootImgBytePtr

+                 + Header->PageSize

+                 + ALIGN_VALUE (Header->KernelSize, Header->PageSize));

+  }

+

+  AsciiStrnCpy (KernelArgs, Header->KernelArgs, BOOTIMG_KERNEL_ARGS_SIZE);

+

+  return EFI_SUCCESS;

+}

diff --git a/EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.c b/EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.c
new file mode 100644
index 0000000..f380817
--- /dev/null
+++ b/EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.c
@@ -0,0 +1,524 @@
+/** @file

+

+  Copyright (c) 2013-2014, ARM Ltd. All rights reserved.<BR>

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "AndroidFastbootApp.h"

+

+#include <Protocol/AndroidFastbootTransport.h>

+#include <Protocol/AndroidFastbootPlatform.h>

+#include <Protocol/SimpleTextOut.h>

+#include <Protocol/SimpleTextIn.h>

+

+#include <Library/PcdLib.h>

+#include <Library/UefiRuntimeServicesTableLib.h>

+#include <Library/BaseMemoryLib.h>

+#include <Library/UefiBootServicesTableLib.h>

+#include <Library/UefiApplicationEntryPoint.h>

+#include <Library/PrintLib.h>

+

+/*

+ * UEFI Application using the FASTBOOT_TRANSPORT_PROTOCOL and

+ * FASTBOOT_PLATFORM_PROTOCOL to implement the Android Fastboot protocol.

+ */

+

+STATIC FASTBOOT_TRANSPORT_PROTOCOL *mTransport;

+STATIC FASTBOOT_PLATFORM_PROTOCOL  *mPlatform;

+

+STATIC EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *mTextOut;

+

+typedef enum {

+  ExpectCmdState,

+  ExpectDataState,

+  FastbootStateMax

+} ANDROID_FASTBOOT_STATE;

+

+STATIC ANDROID_FASTBOOT_STATE mState = ExpectCmdState;

+

+// When in ExpectDataState, the number of bytes of data to expect:

+STATIC UINT32 mNumDataBytes;

+// .. and the number of bytes so far received this data phase

+STATIC UINT32 mBytesReceivedSoFar;

+// .. and the buffer to save data into

+STATIC UINT8 *mDataBuffer = NULL;

+

+// Event notify functions, from which gBS->Exit shouldn't be called, can signal

+// this event when the application should exit

+STATIC EFI_EVENT mFinishedEvent;

+

+STATIC EFI_EVENT mFatalSendErrorEvent;

+

+// This macro uses sizeof - only use it on arrays (i.e. string literals)

+#define SEND_LITERAL(Str) mTransport->Send (                  \

+                                        sizeof (Str) - 1,     \

+                                        Str,                  \

+                                        &mFatalSendErrorEvent \

+                                        )

+#define MATCH_CMD_LITERAL(Cmd, Buf) !AsciiStrnCmp (Cmd, Buf, sizeof (Cmd) - 1)

+

+#define IS_LOWERCASE_ASCII(Char) (Char >= 'a' && Char <= 'z')

+

+#define FASTBOOT_STRING_MAX_LENGTH  256

+#define FASTBOOT_COMMAND_MAX_LENGTH 64

+

+STATIC

+VOID

+HandleGetVar (

+  IN CHAR8 *CmdArg

+  )

+{

+  CHAR8      Response[FASTBOOT_COMMAND_MAX_LENGTH + 1] = "OKAY";

+  EFI_STATUS Status;

+

+  // Respond to getvar:version with 0.4 (version of Fastboot protocol)

+  if (!AsciiStrnCmp ("version", CmdArg, sizeof ("version") - 1 )) {

+    SEND_LITERAL ("OKAY" ANDROID_FASTBOOT_VERSION);

+  } else {

+    // All other variables are assumed to be platform specific

+    Status = mPlatform->GetVar (CmdArg, Response + 4);

+    if (EFI_ERROR (Status)) {

+      SEND_LITERAL ("FAILSomething went wrong when looking up the variable");

+    } else {

+      mTransport->Send (AsciiStrLen (Response), Response, &mFatalSendErrorEvent);

+    }

+  }

+}

+

+STATIC

+VOID

+HandleDownload (

+  IN CHAR8 *NumBytesString

+  )

+{

+  CHAR8       Response[12] = "DATA";

+  CHAR16      OutputString[FASTBOOT_STRING_MAX_LENGTH];

+

+  // Argument is 8-character ASCII string hex representation of number of bytes

+  // that will be sent in the data phase.

+  // Response is "DATA" + that same 8-character string.

+

+  // Replace any previously downloaded data

+  if (mDataBuffer != NULL) {

+    FreePool (mDataBuffer);

+    mDataBuffer = NULL;

+  }

+

+  // Parse out number of data bytes to expect

+  mNumDataBytes = AsciiStrHexToUint64 (NumBytesString);

+  if (mNumDataBytes == 0) {

+    mTextOut->OutputString (mTextOut, L"ERROR: Fail to get the number of bytes to download.\r\n");

+    SEND_LITERAL ("FAILFailed to get the number of bytes to download");

+    return;

+  }

+

+  UnicodeSPrint (OutputString, sizeof (OutputString), L"Downloading %d bytes\r\n", mNumDataBytes);

+  mTextOut->OutputString (mTextOut, OutputString);

+

+  mDataBuffer = AllocatePool (mNumDataBytes);

+  if (mDataBuffer == NULL) {

+    SEND_LITERAL ("FAILNot enough memory");

+  } else {

+    AsciiStrnCpy (Response + 4, NumBytesString, 8);

+    mTransport->Send (sizeof(Response), Response, &mFatalSendErrorEvent);

+

+    mState = ExpectDataState;

+    mBytesReceivedSoFar = 0;

+  }

+}

+

+STATIC

+VOID

+HandleFlash (

+  IN CHAR8 *PartitionName

+  )

+{

+  EFI_STATUS  Status;

+  CHAR16      OutputString[FASTBOOT_STRING_MAX_LENGTH];

+

+  // Build output string

+  UnicodeSPrint (OutputString, sizeof (OutputString), L"Flashing partition %a\r\n", PartitionName);

+  mTextOut->OutputString (mTextOut, OutputString);

+

+  if (mDataBuffer == NULL) {

+    // Doesn't look like we were sent any data

+    SEND_LITERAL ("FAILNo data to flash");

+    return;

+  }

+

+  Status = mPlatform->FlashPartition (

+                        PartitionName,

+                        mNumDataBytes,

+                        mDataBuffer

+                        );

+  if (Status == EFI_NOT_FOUND) {

+    SEND_LITERAL ("FAILNo such partition.");

+    mTextOut->OutputString (mTextOut, L"No such partition.\r\n");

+  } else if (EFI_ERROR (Status)) {

+    SEND_LITERAL ("FAILError flashing partition.");

+    mTextOut->OutputString (mTextOut, L"Error flashing partition.\r\n");

+    DEBUG ((EFI_D_ERROR, "Couldn't flash image:  %r\n", Status));

+  } else {

+    mTextOut->OutputString (mTextOut, L"Done.\r\n");

+    SEND_LITERAL ("OKAY");

+  }

+}

+

+STATIC

+VOID

+HandleErase (

+  IN CHAR8 *PartitionName

+  )

+{

+  EFI_STATUS  Status;

+  CHAR16      OutputString[FASTBOOT_STRING_MAX_LENGTH];

+

+  // Build output string

+  UnicodeSPrint (OutputString, sizeof (OutputString), L"Erasing partition %a\r\n", PartitionName);

+  mTextOut->OutputString (mTextOut, OutputString);

+

+  Status = mPlatform->ErasePartition (PartitionName);

+  if (EFI_ERROR (Status)) {

+    SEND_LITERAL ("FAILCheck device console.");

+    DEBUG ((EFI_D_ERROR, "Couldn't erase image:  %r\n", Status));

+  } else {

+    SEND_LITERAL ("OKAY");

+  }

+}

+

+STATIC

+VOID

+HandleBoot (

+  VOID

+  )

+{

+  EFI_STATUS Status;

+

+  mTextOut->OutputString (mTextOut, L"Booting downloaded image\r\n");

+

+  if (mDataBuffer == NULL) {

+    // Doesn't look like we were sent any data

+    SEND_LITERAL ("FAILNo image in memory");

+    return;

+  }

+

+  // We don't really have any choice but to report success, because once we

+  // boot we lose control of the system.

+  SEND_LITERAL ("OKAY");

+

+  Status = BootAndroidBootImg (mNumDataBytes, mDataBuffer);

+  if (EFI_ERROR (Status)) {

+    DEBUG ((EFI_D_ERROR, "Failed to boot downloaded image: %r\n", Status));

+  }

+  // We shouldn't get here

+}

+

+STATIC

+VOID

+HandleOemCommand (

+  IN CHAR8 *Command

+  )

+{

+  EFI_STATUS  Status;

+

+  Status = mPlatform->DoOemCommand (Command);

+  if (Status == EFI_NOT_FOUND) {

+    SEND_LITERAL ("FAILOEM Command not recognised.");

+  } else if (Status == EFI_DEVICE_ERROR) {

+    SEND_LITERAL ("FAILError while executing command");

+  } else if (EFI_ERROR (Status)) {

+    SEND_LITERAL ("FAIL");

+  } else {

+    SEND_LITERAL ("OKAY");

+  }

+}

+

+STATIC

+VOID

+AcceptCmd (

+  IN        UINTN  Size,

+  IN  CONST CHAR8 *Data

+  )

+{

+  CHAR8       Command[FASTBOOT_COMMAND_MAX_LENGTH + 1];

+

+  // Max command size is 64 bytes

+  if (Size > FASTBOOT_COMMAND_MAX_LENGTH) {

+    SEND_LITERAL ("FAILCommand too large");

+    return;

+  }

+

+  // Commands aren't null-terminated. Let's get a null-terminated version.

+  AsciiStrnCpy (Command, Data, Size);

+  Command[Size] = '\0';

+

+  // Parse command

+  if (MATCH_CMD_LITERAL ("getvar", Command)) {

+    HandleGetVar (Command + sizeof ("getvar"));

+  } else if (MATCH_CMD_LITERAL ("download", Command)) {

+    HandleDownload (Command + sizeof ("download"));

+  } else if (MATCH_CMD_LITERAL ("verify", Command)) {

+    SEND_LITERAL ("FAILNot supported");

+  } else if (MATCH_CMD_LITERAL ("flash", Command)) {

+    HandleFlash (Command + sizeof ("flash"));

+  } else if (MATCH_CMD_LITERAL ("erase", Command)) {

+    HandleErase (Command + sizeof ("erase"));

+  } else if (MATCH_CMD_LITERAL ("boot", Command)) {

+    HandleBoot ();

+  } else if (MATCH_CMD_LITERAL ("continue", Command)) {

+    SEND_LITERAL ("OKAY");

+    mTextOut->OutputString (mTextOut, L"Received 'continue' command. Exiting Fastboot mode\r\n");

+

+    gBS->SignalEvent (mFinishedEvent);

+  } else if (MATCH_CMD_LITERAL ("reboot", Command)) {

+    if (MATCH_CMD_LITERAL ("reboot-booloader", Command)) {

+      // fastboot_protocol.txt:

+      //    "reboot-bootloader    Reboot back into the bootloader."

+      // I guess this means reboot back into fastboot mode to save the user

+      // having to do whatever they did to get here again.

+      // Here we just reboot normally.

+      SEND_LITERAL ("INFOreboot-bootloader not supported, rebooting normally.");

+    }

+    SEND_LITERAL ("OKAY");

+    gRT->ResetSystem (EfiResetCold, EFI_SUCCESS, 0, NULL);

+

+    // Shouldn't get here

+    DEBUG ((EFI_D_ERROR, "Fastboot: gRT->ResetSystem didn't work\n"));

+  } else if (MATCH_CMD_LITERAL ("powerdown", Command)) {

+    SEND_LITERAL ("OKAY");

+    gRT->ResetSystem (EfiResetShutdown, EFI_SUCCESS, 0, NULL);

+

+    // Shouldn't get here

+    DEBUG ((EFI_D_ERROR, "Fastboot: gRT->ResetSystem didn't work\n"));

+  } else if (MATCH_CMD_LITERAL ("oem", Command)) {

+    // The "oem" command isn't in the specification, but it was observed in the

+    // wild, followed by a space, followed by the actual command.

+    HandleOemCommand (Command + sizeof ("oem"));

+  } else if (IS_LOWERCASE_ASCII (Command[0])) {

+    // Commands starting with lowercase ASCII characters are reserved for the

+    // Fastboot protocol. If we don't recognise it, it's probably the future

+    // and there are new commmands in the protocol.

+    // (By the way, the "oem" command mentioned above makes this reservation

+    //  redundant, but we handle it here to be spec-compliant)

+    SEND_LITERAL ("FAILCommand not recognised. Check Fastboot version.");

+  } else {

+    HandleOemCommand (Command);

+  }

+}

+

+STATIC

+VOID

+AcceptData (

+  IN  UINTN  Size,

+  IN  VOID  *Data

+  )

+{

+  UINT32 RemainingBytes = mNumDataBytes - mBytesReceivedSoFar;

+  CHAR16 OutputString[FASTBOOT_STRING_MAX_LENGTH];

+  STATIC UINTN Count = 0;

+

+  // Protocol doesn't say anything about sending extra data so just ignore it.

+  if (Size > RemainingBytes) {

+    Size = RemainingBytes;

+  }

+

+  CopyMem (&mDataBuffer[mBytesReceivedSoFar], Data, Size);

+

+  mBytesReceivedSoFar += Size;

+

+  // Show download progress. Don't do it for every packet  as outputting text

+  // might be time consuming - do it on the last packet and on every 32nd packet

+  if ((Count++ % 32) == 0 || Size == RemainingBytes) {

+    // (Note no newline in format string - it will overwrite the line each time)

+    UnicodeSPrint (

+      OutputString,

+      sizeof (OutputString),

+      L"\r%8d / %8d bytes downloaded (%d%%)",

+      mBytesReceivedSoFar,

+      mNumDataBytes,

+      (mBytesReceivedSoFar * 100) / mNumDataBytes // percentage

+      );

+    mTextOut->OutputString (mTextOut, OutputString);

+  }

+

+  if (mBytesReceivedSoFar == mNumDataBytes) {

+    // Download finished.

+

+    mTextOut->OutputString (mTextOut, L"\r\n");

+    SEND_LITERAL ("OKAY");

+    mState = ExpectCmdState;

+  }

+}

+

+/*

+  This is the NotifyFunction passed to CreateEvent in the FastbootAppEntryPoint

+  It will be called by the UEFI event framework when the transport protocol

+  implementation signals that data has been received from the Fastboot host.

+  The parameters are ignored.

+*/

+STATIC

+VOID

+DataReady (

+  IN EFI_EVENT  Event,

+  IN VOID      *Context

+  )

+{

+  UINTN       Size;

+  VOID       *Data;

+  EFI_STATUS  Status;

+

+  do {

+    Status = mTransport->Receive (&Size, &Data);

+    if (!EFI_ERROR (Status)) {

+      if (mState == ExpectCmdState) {

+        AcceptCmd (Size, (CHAR8 *) Data);

+      } else if (mState == ExpectDataState) {

+        AcceptData (Size, Data);

+      } else {

+        ASSERT (FALSE);

+      }

+      FreePool (Data);

+    }

+  } while (!EFI_ERROR (Status));

+

+  // Quit if there was a fatal error

+  if (Status != EFI_NOT_READY) {

+    ASSERT (Status == EFI_DEVICE_ERROR);

+    // (Put a newline at the beginning as we are probably in the data phase,

+    //  so the download progress line, with no '\n' is probably on the console)

+    mTextOut->OutputString (mTextOut, L"\r\nFatal error receiving data. Exiting.\r\n");

+    gBS->SignalEvent (mFinishedEvent);

+  }

+}

+

+/*

+  Event notify for a fatal error in transmission.

+*/

+STATIC

+VOID

+FatalErrorNotify (

+  IN EFI_EVENT  Event,

+  IN VOID      *Context

+  )

+{

+  mTextOut->OutputString (mTextOut, L"Fatal error sending command response. Exiting.\r\n");

+  gBS->SignalEvent (mFinishedEvent);

+}

+

+EFI_STATUS

+EFIAPI

+FastbootAppEntryPoint (

+  IN EFI_HANDLE                            ImageHandle,

+  IN EFI_SYSTEM_TABLE                      *SystemTable

+  )

+{

+  EFI_STATUS                      Status;

+  EFI_EVENT                       ReceiveEvent;

+  EFI_EVENT                       WaitEventArray[2];

+  UINTN                           EventIndex;

+  EFI_SIMPLE_TEXT_INPUT_PROTOCOL *TextIn;

+

+  mDataBuffer = NULL;

+

+  Status = gBS->LocateProtocol (

+    &gAndroidFastbootTransportProtocolGuid,

+    NULL,

+    (VOID **) &mTransport

+    );

+  if (EFI_ERROR (Status)) {

+    DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't open Fastboot Transport Protocol: %r\n", Status));

+    return Status;

+  }

+

+  Status = gBS->LocateProtocol (&gAndroidFastbootPlatformProtocolGuid, NULL, (VOID **) &mPlatform);

+  if (EFI_ERROR (Status)) {

+    DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't open Fastboot Platform Protocol: %r\n", Status));

+    return Status;

+  }

+

+  Status = mPlatform->Init ();

+  if (EFI_ERROR (Status)) {

+    DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't initialise Fastboot Platform Protocol: %r\n", Status));

+    return Status;

+  }

+

+  Status = gBS->LocateProtocol (&gEfiSimpleTextOutProtocolGuid, NULL, (VOID **) &mTextOut);

+  if (EFI_ERROR (Status)) {

+    DEBUG ((EFI_D_ERROR,

+      "Fastboot: Couldn't open Text Output Protocol: %r\n", Status

+      ));

+    return Status;

+  }

+

+  Status = gBS->LocateProtocol (&gEfiSimpleTextInProtocolGuid, NULL, (VOID **) &TextIn);

+  if (EFI_ERROR (Status)) {

+    DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't open Text Input Protocol: %r\n", Status));

+    return Status;

+  }

+

+  // Disable watchdog

+  Status = gBS->SetWatchdogTimer (0, 0x10000, 0, NULL);

+  if (EFI_ERROR (Status)) {

+    DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't disable watchdog timer: %r\n", Status));

+  }

+

+  // Create event for receipt of data from the host

+  Status = gBS->CreateEvent (

+                  EVT_NOTIFY_SIGNAL,

+                  TPL_CALLBACK,

+                  DataReady,

+                  NULL,

+                  &ReceiveEvent

+                  );

+  ASSERT_EFI_ERROR (Status);

+

+  // Create event for exiting application when "continue" command is received

+  Status = gBS->CreateEvent (0, TPL_CALLBACK, NULL, NULL, &mFinishedEvent);

+  ASSERT_EFI_ERROR (Status);

+

+  // Create event to pass to FASTBOOT_TRANSPORT_PROTOCOL.Send, signalling a

+  // fatal error

+  Status = gBS->CreateEvent (

+                 EVT_NOTIFY_SIGNAL,

+                 TPL_CALLBACK,

+                 FatalErrorNotify,

+                 NULL,

+                 &mFatalSendErrorEvent

+                 );

+  ASSERT_EFI_ERROR (Status);

+

+

+  // Start listening for data

+  Status = mTransport->Start (

+    ReceiveEvent

+    );

+  if (EFI_ERROR (Status)) {

+    DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't start transport: %r\n", Status));

+    return Status;

+  }

+

+  // Talk to the user

+  mTextOut->OutputString (mTextOut,

+      L"Android Fastboot mode - version " ANDROID_FASTBOOT_VERSION ". Press any key to quit.\r\n");

+

+  // Quit when the user presses any key, or mFinishedEvent is signalled

+  WaitEventArray[0] = mFinishedEvent;

+  WaitEventArray[1] = TextIn->WaitForKey;

+  gBS->WaitForEvent (2, WaitEventArray, &EventIndex);

+

+  mTransport->Stop ();

+  if (EFI_ERROR (Status)) {

+    DEBUG ((EFI_D_ERROR, "Warning: Fastboot Transport Stop: %r\n", Status));

+  }

+  mPlatform->UnInit ();

+

+  return EFI_SUCCESS;

+}

diff --git a/EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.h b/EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.h
new file mode 100644
index 0000000..f62660f
--- /dev/null
+++ b/EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.h
@@ -0,0 +1,42 @@
+/** @file

+

+  Copyright (c) 2013-2014, ARM Ltd. All rights reserved.<BR>

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef __ANDROID_FASTBOOT_APP_H__

+#define __ANDROID_FASTBOOT_APP_H__

+

+#include <Library/BaseLib.h>

+#include <Library/DebugLib.h>

+#include <Library/MemoryAllocationLib.h>

+

+#define BOOTIMG_KERNEL_ARGS_SIZE 512

+

+#define ANDROID_FASTBOOT_VERSION "0.4"

+

+EFI_STATUS

+BootAndroidBootImg (

+  IN  UINTN    BufferSize,

+  IN  VOID    *Buffer

+  );

+

+EFI_STATUS

+ParseAndroidBootImg (

+  IN  VOID    *BootImg,

+  OUT VOID   **Kernel,

+  OUT UINTN   *KernelSize,

+  OUT VOID   **Ramdisk,

+  OUT UINTN   *RamdiskSize,

+  OUT CHAR8   *KernelArgs

+  );

+

+#endif //ifdef __ANDROID_FASTBOOT_APP_H__

diff --git a/EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.inf b/EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.inf
new file mode 100644
index 0000000..ab9354c
--- /dev/null
+++ b/EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.inf
@@ -0,0 +1,60 @@
+#/** @file

+#

+#  Copyright (c) 2013-2014, ARM Ltd. All rights reserved.<BR>

+#

+#  This program and the accompanying materials

+#  are licensed and made available under the terms and conditions of the BSD License

+#  which accompanies this distribution. The full text of the license may be found at

+#  http://opensource.org/licenses/bsd-license.php

+#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+#  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+#

+#

+#**/

+

+[Defines]

+  INF_VERSION                    = 0x00010005

+  BASE_NAME                      = AndroidFastbootApp

+  FILE_GUID                      = 9588502a-5370-11e3-8631-d7c5951364c8

+  MODULE_TYPE                    = UEFI_APPLICATION

+  VERSION_STRING                 = 1.0

+  ENTRY_POINT                    = FastbootAppEntryPoint

+

+[Sources.common]

+  AndroidFastbootApp.c

+  AndroidBootImg.c

+

+[Sources.ARM, Sources.AARCH64]

+  Arm/BootAndroidBootImg.c

+

+[LibraryClasses]

+  BaseLib

+  BaseMemoryLib

+  BdsLib

+  DebugLib

+  DevicePathLib

+  DxeServicesTableLib

+  MemoryAllocationLib

+  PcdLib

+  PrintLib

+  UefiApplicationEntryPoint

+  UefiBootServicesTableLib

+  UefiRuntimeServicesTableLib

+

+[Protocols]

+  gAndroidFastbootTransportProtocolGuid

+  gAndroidFastbootPlatformProtocolGuid

+  gEfiSimpleTextOutProtocolGuid

+  gEfiSimpleTextInProtocolGuid

+

+[Packages]

+  MdePkg/MdePkg.dec

+  MdeModulePkg/MdeModulePkg.dec

+  EmbeddedPkg/EmbeddedPkg.dec

+

+[Packages.ARM, Packages.AARCH64]

+  ArmPkg/ArmPkg.dec

+  ArmPlatformPkg/ArmPlatformPkg.dec

+

+[Guids.ARM, Guids.AARCH64]

+  gArmGlobalVariableGuid

diff --git a/EmbeddedPkg/Application/AndroidFastboot/Arm/BootAndroidBootImg.c b/EmbeddedPkg/Application/AndroidFastboot/Arm/BootAndroidBootImg.c
new file mode 100644
index 0000000..6f4b66b
--- /dev/null
+++ b/EmbeddedPkg/Application/AndroidFastboot/Arm/BootAndroidBootImg.c
@@ -0,0 +1,125 @@
+/** @file

+

+  Copyright (c) 2013-2014, ARM Ltd. All rights reserved.<BR>

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "AndroidFastbootApp.h"

+

+#include <Protocol/DevicePath.h>

+

+#include <Library/BdsLib.h>

+#include <Library/DevicePathLib.h>

+

+#include <Guid/ArmGlobalVariableHob.h>

+

+// Device Path representing an image in memory

+#pragma pack(1)

+typedef struct {

+  MEMMAP_DEVICE_PATH                      Node1;

+  EFI_DEVICE_PATH_PROTOCOL                End;

+} MEMORY_DEVICE_PATH;

+#pragma pack()

+

+STATIC CONST MEMORY_DEVICE_PATH MemoryDevicePathTemplate =

+{

+  {

+    {

+      HARDWARE_DEVICE_PATH,

+      HW_MEMMAP_DP,

+      {

+        (UINT8)(sizeof (MEMMAP_DEVICE_PATH)),

+        (UINT8)((sizeof (MEMMAP_DEVICE_PATH)) >> 8),

+      },

+    }, // Header

+    0, // StartingAddress (set at runtime)

+    0  // EndingAddress   (set at runtime)

+  }, // Node1

+  {

+    END_DEVICE_PATH_TYPE,

+    END_ENTIRE_DEVICE_PATH_SUBTYPE,

+    sizeof (EFI_DEVICE_PATH_PROTOCOL),

+    0

+  } // End

+};

+

+EFI_STATUS

+BootAndroidBootImg (

+  IN UINTN    BufferSize,

+  IN VOID    *Buffer

+  )

+{

+  EFI_DEVICE_PATH_PROTOCOL           *FdtDevicePath;

+  EFI_STATUS                          Status;

+  CHAR8                               KernelArgs[BOOTIMG_KERNEL_ARGS_SIZE];

+  VOID                               *Kernel;

+  UINTN                               KernelSize;

+  VOID                               *Ramdisk;

+  UINTN                               RamdiskSize;

+  MEMORY_DEVICE_PATH                  KernelDevicePath;

+  MEMORY_DEVICE_PATH*                 RamdiskDevicePath;

+

+  Status = ParseAndroidBootImg (

+            Buffer,

+            &Kernel,

+            &KernelSize,

+            &Ramdisk,

+            &RamdiskSize,

+            KernelArgs

+            );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  KernelDevicePath = MemoryDevicePathTemplate;

+

+  // Have to cast to UINTN before casting to EFI_PHYSICAL_ADDRESS in order to

+  // appease GCC.

+  KernelDevicePath.Node1.StartingAddress = (EFI_PHYSICAL_ADDRESS)(UINTN) Kernel;

+  KernelDevicePath.Node1.EndingAddress   = (EFI_PHYSICAL_ADDRESS)(UINTN) Kernel + KernelSize;

+

+  RamdiskDevicePath = NULL;

+  if (RamdiskSize != 0) {

+    RamdiskDevicePath = (MEMORY_DEVICE_PATH*)DuplicateDevicePath ((EFI_DEVICE_PATH_PROTOCOL*) &MemoryDevicePathTemplate);

+

+    RamdiskDevicePath->Node1.StartingAddress = (EFI_PHYSICAL_ADDRESS)(UINTN) Ramdisk;

+    RamdiskDevicePath->Node1.EndingAddress   = ((EFI_PHYSICAL_ADDRESS)(UINTN) Ramdisk) + RamdiskSize;

+  }

+

+  // Get the default FDT device path

+  Status = GetEnvironmentVariable ((CHAR16 *)L"Fdt", &gArmGlobalVariableGuid,

+             NULL, 0, (VOID **)&FdtDevicePath);

+  if (Status == EFI_NOT_FOUND) {

+    DEBUG ((EFI_D_ERROR, "Error: Please update FDT path in boot manager\n"));

+    return EFI_DEVICE_ERROR;

+  }

+  ASSERT_EFI_ERROR (Status);

+

+  Status = BdsBootLinuxFdt (

+              (EFI_DEVICE_PATH_PROTOCOL *) &KernelDevicePath,

+              (EFI_DEVICE_PATH_PROTOCOL *) RamdiskDevicePath,

+              KernelArgs,

+              FdtDevicePath

+              );

+  if (EFI_ERROR (Status)) {

+    DEBUG ((EFI_D_ERROR, "Couldn't Boot Linux: %d\n", Status));

+    return EFI_DEVICE_ERROR;

+  }

+

+  if (RamdiskDevicePath) {

+    FreePool (RamdiskDevicePath);

+  }

+

+  // If we got here we do a confused face because BootLinuxFdt returned,

+  // reporting success.

+  DEBUG ((EFI_D_ERROR, "WARNING: BdsBootLinuxFdt returned EFI_SUCCESS.\n"));

+  return EFI_SUCCESS;

+}

diff --git a/EmbeddedPkg/EmbeddedPkg.dsc b/EmbeddedPkg/EmbeddedPkg.dsc
index 9eaf7dc..a9db4ed 100644
--- a/EmbeddedPkg/EmbeddedPkg.dsc
+++ b/EmbeddedPkg/EmbeddedPkg.dsc
@@ -93,12 +93,12 @@
   UefiDriverEntryPoint|MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf

   UefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf

 

-

   PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf

   EblCmdLib|EmbeddedPkg/Library/EblCmdLibNull/EblCmdLibNull.inf

   

   EblNetworkLib|EmbeddedPkg/Library/EblNetworkLib/EblNetworkLib.inf

-  

+

+  FdtLib|EmbeddedPkg/Library/FdtLib/FdtLib.inf

 

 [LibraryClasses.common.DXE_DRIVER]

   PcdLib|MdePkg/Library/DxePcdLib/DxePcdLib.inf

@@ -117,9 +117,18 @@
   ExtractGuidedSectionLib|EmbeddedPkg/Library/PrePiExtractGuidedSectionLib/PrePiExtractGuidedSectionLib.inf

 

 [LibraryClasses.ARM, LibraryClasses.AARCH64]

+  ArmGicLib|ArmPkg/Drivers/ArmGic/ArmGicLib.inf

+  ArmSmcLib|ArmPkg/Library/ArmSmcLib/ArmSmcLib.inf

+  BdsLib|ArmPkg/Library/BdsLib/BdsLib.inf

   SemihostLib|ArmPkg/Library/SemihostLib/SemihostLib.inf

   NULL|ArmPkg/Library/CompilerIntrinsicsLib/CompilerIntrinsicsLib.inf

 

+[LibraryClasses.ARM]

+  ArmLib|ArmPkg/Library/ArmLib/ArmV7/ArmV7Lib.inf

+

+[LibraryClasses.AARCH64]

+  ArmLib|ArmPkg/Library/ArmLib/AArch64/AArch64Lib.inf

+

 

 ################################################################################

 #

@@ -211,6 +220,9 @@
 #  RELEASE_*_IA32_DLINK_FLAGS = /ALIGN:4096

 #  *_*_IA32_CC_FLAGS = /D EFI_SPECIFICATION_VERSION=0x0002000A /D TIANO_RELEASE_VERSION=0x00080006

 

+[BuildOptions]

+  RVCT:*_*_ARM_PLATFORM_FLAGS == --cpu=7-A.security

+

 

 ################################################################################

 #

@@ -242,5 +254,7 @@
 

   EmbeddedPkg/Universal/MmcDxe/MmcDxe.inf

 

+  EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.inf

+

 [Components.IA32, Components.X64, Components.IPF, Components.ARM]

   EmbeddedPkg/GdbStub/GdbStub.inf