summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Application
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
commitf215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch)
tree6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Application
parentInitial commit. (diff)
downloadvirtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz
virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Application')
-rw-r--r--src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Application/AndroidBoot/AndroidBootApp.c134
-rw-r--r--src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Application/AndroidBoot/AndroidBootApp.inf58
-rw-r--r--src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Application/AndroidFastboot/AndroidBootImg.c60
-rw-r--r--src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.c529
-rw-r--r--src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.h37
-rw-r--r--src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.inf53
-rw-r--r--src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Application/AndroidFastboot/Arm/BootAndroidBootImg.c178
7 files changed, 1049 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Application/AndroidBoot/AndroidBootApp.c b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Application/AndroidBoot/AndroidBootApp.c
new file mode 100644
index 00000000..4633e8d3
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Application/AndroidBoot/AndroidBootApp.c
@@ -0,0 +1,134 @@
+/** @file
+
+ Copyright (c) 2013-2014, ARM Ltd. All rights reserved.<BR>
+ Copyright (c) 2017, Linaro. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/AndroidBootImgLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+#include <Protocol/BlockIo.h>
+#include <Protocol/DevicePathFromText.h>
+
+/* Validate the node is media hard drive type */
+EFI_STATUS
+ValidateAndroidMediaDevicePath (
+ IN EFI_DEVICE_PATH *DevicePath
+ )
+{
+ EFI_DEVICE_PATH_PROTOCOL *Node, *NextNode;
+
+ NextNode = DevicePath;
+ while (NextNode != NULL) {
+ Node = NextNode;
+ if (Node->Type == MEDIA_DEVICE_PATH &&
+ Node->SubType == MEDIA_HARDDRIVE_DP) {
+ return EFI_SUCCESS;
+ }
+ NextNode = NextDevicePathNode (Node);
+ }
+ return EFI_INVALID_PARAMETER;
+}
+
+EFI_STATUS
+EFIAPI
+AndroidBootAppEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ CHAR16 *BootPathStr;
+ EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL *EfiDevicePathFromTextProtocol;
+ EFI_DEVICE_PATH *DevicePath;
+ EFI_BLOCK_IO_PROTOCOL *BlockIo;
+ UINT32 MediaId, BlockSize;
+ VOID *Buffer;
+ EFI_HANDLE Handle;
+ UINTN BootImgSize;
+
+ BootPathStr = (CHAR16 *)PcdGetPtr (PcdAndroidBootDevicePath);
+ ASSERT (BootPathStr != NULL);
+ Status = gBS->LocateProtocol (&gEfiDevicePathFromTextProtocolGuid, NULL,
+ (VOID **)&EfiDevicePathFromTextProtocol);
+ ASSERT_EFI_ERROR(Status);
+ DevicePath = (EFI_DEVICE_PATH *)EfiDevicePathFromTextProtocol->ConvertTextToDevicePath (BootPathStr);
+ ASSERT (DevicePath != NULL);
+
+ Status = ValidateAndroidMediaDevicePath (DevicePath);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid,
+ &DevicePath, &Handle);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->OpenProtocol (
+ Handle,
+ &gEfiBlockIoProtocolGuid,
+ (VOID **) &BlockIo,
+ gImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Failed to get BlockIo: %r\n", Status));
+ return Status;
+ }
+
+ MediaId = BlockIo->Media->MediaId;
+ BlockSize = BlockIo->Media->BlockSize;
+ Buffer = AllocatePages (EFI_SIZE_TO_PAGES (sizeof(ANDROID_BOOTIMG_HEADER)));
+ if (Buffer == NULL) {
+ return EFI_BUFFER_TOO_SMALL;
+ }
+ /* Load header of boot.img */
+ Status = BlockIo->ReadBlocks (
+ BlockIo,
+ MediaId,
+ 0,
+ BlockSize,
+ Buffer
+ );
+ Status = AndroidBootImgGetImgSize (Buffer, &BootImgSize);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Failed to get AndroidBootImg Size: %r\n", Status));
+ return Status;
+ }
+ BootImgSize = ALIGN_VALUE (BootImgSize, BlockSize);
+ FreePages (Buffer, EFI_SIZE_TO_PAGES (sizeof(ANDROID_BOOTIMG_HEADER)));
+
+ /* Both PartitionStart and PartitionSize are counted as block size. */
+ Buffer = AllocatePages (EFI_SIZE_TO_PAGES (BootImgSize));
+ if (Buffer == NULL) {
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ /* Load header of boot.img */
+ Status = BlockIo->ReadBlocks (
+ BlockIo,
+ MediaId,
+ 0,
+ BootImgSize,
+ Buffer
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Failed to read blocks: %r\n", Status));
+ goto EXIT;
+ }
+
+ Status = AndroidBootImgBoot (Buffer, BootImgSize);
+
+EXIT:
+ return Status;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Application/AndroidBoot/AndroidBootApp.inf b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Application/AndroidBoot/AndroidBootApp.inf
new file mode 100644
index 00000000..149a524a
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Application/AndroidBoot/AndroidBootApp.inf
@@ -0,0 +1,58 @@
+#/** @file
+#
+# Copyright (c) 2013-2015, ARM Ltd. All rights reserved.<BR>
+# Copyright (c) 2017, Linaro. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#
+#**/
+
+[Defines]
+ INF_VERSION = 0x00010019
+ BASE_NAME = AndroidBootApp
+ FILE_GUID = 3a738b36-b9c5-4763-abbd-6cbd4b25f9ff
+ MODULE_TYPE = UEFI_APPLICATION
+ VERSION_STRING = 1.0
+ ENTRY_POINT = AndroidBootAppEntryPoint
+
+[Sources.common]
+ AndroidBootApp.c
+
+[LibraryClasses]
+ AndroidBootImgLib
+ BaseLib
+ BaseMemoryLib
+ DebugLib
+ DevicePathLib
+ DxeServicesTableLib
+ FdtLib
+ MemoryAllocationLib
+ PcdLib
+ PrintLib
+ UefiApplicationEntryPoint
+ UefiBootServicesTableLib
+ UefiLib
+ UefiRuntimeServicesTableLib
+
+[Protocols]
+ gAndroidFastbootPlatformProtocolGuid
+ gEfiBlockIoProtocolGuid
+ gEfiDevicePathFromTextProtocolGuid
+ gEfiSimpleTextOutProtocolGuid
+ gEfiSimpleTextInProtocolGuid
+
+[Packages]
+ EmbeddedPkg/EmbeddedPkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ MdePkg/MdePkg.dec
+
+[Packages.ARM, Packages.AARCH64]
+ ArmPkg/ArmPkg.dec
+ ArmPlatformPkg/ArmPlatformPkg.dec
+
+[Guids]
+ gFdtTableGuid
+
+[Pcd]
+ gEmbeddedTokenSpaceGuid.PcdAndroidBootDevicePath
diff --git a/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Application/AndroidFastboot/AndroidBootImg.c b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Application/AndroidFastboot/AndroidBootImg.c
new file mode 100644
index 00000000..63bda4c2
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Application/AndroidFastboot/AndroidBootImg.c
@@ -0,0 +1,60 @@
+/** @file
+
+ Copyright (c) 2013-2014, ARM Ltd. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "AndroidFastbootApp.h"
+
+// Find the kernel and ramdisk in an Android boot.img.
+// return EFI_INVALID_PARAMETER 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 ((CONST CHAR8 *)Header->BootMagic, ANDROID_BOOT_MAGIC,
+ ANDROID_BOOT_MAGIC_LENGTH) != 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Header->KernelSize == 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ ASSERT (IS_VALID_ANDROID_PAGE_SIZE (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));
+ }
+
+ AsciiStrnCpyS (KernelArgs, ANDROID_BOOTIMG_KERNEL_ARGS_SIZE, Header->KernelArgs,
+ ANDROID_BOOTIMG_KERNEL_ARGS_SIZE);
+
+ return EFI_SUCCESS;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.c b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.c
new file mode 100644
index 00000000..112ed479
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.c
@@ -0,0 +1,529 @@
+/** @file
+
+ Copyright (c) 2013-2014, ARM Ltd. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#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 UINT64 mNumDataBytes;
+// .. and the number of bytes so far received this data phase
+STATIC UINT64 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[13];
+ 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 {
+ ZeroMem (Response, sizeof Response);
+ AsciiSPrint (Response, sizeof Response, "DATA%x",
+ (UINT32)mNumDataBytes);
+ mTransport->Send (sizeof Response - 1, 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.
+ AsciiStrnCpyS (Command, sizeof Command, Data, Size);
+
+ // 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 commands 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;
+ EFI_INPUT_KEY Key;
+
+ 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 RETURN or SPACE key to quit.\r\n");
+
+ // Quit when the user presses any key, or mFinishedEvent is signalled
+ WaitEventArray[0] = mFinishedEvent;
+ WaitEventArray[1] = TextIn->WaitForKey;
+ while (1) {
+ gBS->WaitForEvent (2, WaitEventArray, &EventIndex);
+ Status = TextIn->ReadKeyStroke (gST->ConIn, &Key);
+ if (Key.ScanCode == SCAN_NULL) {
+ if ((Key.UnicodeChar == CHAR_CARRIAGE_RETURN) ||
+ (Key.UnicodeChar == L' ')) {
+ break;
+ }
+ }
+ }
+
+ 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/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.h b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.h
new file mode 100644
index 00000000..196028e1
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.h
@@ -0,0 +1,37 @@
+/** @file
+
+ Copyright (c) 2013-2014, ARM Ltd. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __ANDROID_FASTBOOT_APP_H__
+#define __ANDROID_FASTBOOT_APP_H__
+
+#include <Library/AndroidBootImgLib.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/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.inf b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.inf
new file mode 100644
index 00000000..66007012
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.inf
@@ -0,0 +1,53 @@
+#/** @file
+#
+# Copyright (c) 2013-2015, ARM Ltd. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#
+#**/
+
+[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
+ DebugLib
+ DevicePathLib
+ DxeServicesTableLib
+ MemoryAllocationLib
+ PcdLib
+ PrintLib
+ UefiApplicationEntryPoint
+ UefiBootServicesTableLib
+ UefiLib
+ UefiRuntimeServicesTableLib
+
+[Protocols]
+ gAndroidFastbootTransportProtocolGuid
+ gAndroidFastbootPlatformProtocolGuid
+ gEfiLoadedImageProtocolGuid
+ gEfiSimpleTextOutProtocolGuid
+ gEfiSimpleTextInProtocolGuid
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ EmbeddedPkg/EmbeddedPkg.dec
+
+[Packages.ARM, Packages.AARCH64]
+ ArmPkg/ArmPkg.dec
+ ArmPlatformPkg/ArmPlatformPkg.dec
diff --git a/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Application/AndroidFastboot/Arm/BootAndroidBootImg.c b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Application/AndroidFastboot/Arm/BootAndroidBootImg.c
new file mode 100644
index 00000000..c61e5290
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Application/AndroidFastboot/Arm/BootAndroidBootImg.c
@@ -0,0 +1,178 @@
+/** @file
+
+ Copyright (c) 2013-2015, ARM Ltd. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "AndroidFastbootApp.h"
+
+#include <Protocol/DevicePath.h>
+#include <Protocol/LoadedImage.h>
+
+#include <Library/DevicePathLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.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
+};
+
+
+/**
+ Start an EFI Application from a Device Path
+
+ @param ParentImageHandle Handle of the calling image
+ @param DevicePath Location of the EFI Application
+
+ @retval EFI_SUCCESS All drivers have been connected
+ @retval EFI_NOT_FOUND The Linux kernel Device Path has not been found
+ @retval EFI_OUT_OF_RESOURCES There is not enough resource memory to store the matching results.
+
+**/
+STATIC
+EFI_STATUS
+StartEfiApplication (
+ IN EFI_HANDLE ParentImageHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ IN UINTN LoadOptionsSize,
+ IN VOID* LoadOptions
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE ImageHandle;
+ EFI_LOADED_IMAGE_PROTOCOL* LoadedImage;
+
+ // Load the image from the device path with Boot Services function
+ Status = gBS->LoadImage (TRUE, ParentImageHandle, DevicePath, NULL, 0,
+ &ImageHandle);
+ if (EFI_ERROR (Status)) {
+ //
+ // With EFI_SECURITY_VIOLATION retval, the Image was loaded and an ImageHandle was created
+ // with a valid EFI_LOADED_IMAGE_PROTOCOL, but the image can not be started right now.
+ // If the caller doesn't have the option to defer the execution of an image, we should
+ // unload image for the EFI_SECURITY_VIOLATION to avoid resource leak.
+ //
+ if (Status == EFI_SECURITY_VIOLATION) {
+ gBS->UnloadImage (ImageHandle);
+ }
+ return Status;
+ }
+
+ // Passed LoadOptions to the EFI Application
+ if (LoadOptionsSize != 0) {
+ Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid,
+ (VOID **) &LoadedImage);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ LoadedImage->LoadOptionsSize = LoadOptionsSize;
+ LoadedImage->LoadOptions = LoadOptions;
+ }
+
+ // Before calling the image, enable the Watchdog Timer for the 5 Minute period
+ gBS->SetWatchdogTimer (5 * 60, 0x0000, 0x00, NULL);
+ // Start the image
+ Status = gBS->StartImage (ImageHandle, NULL, NULL);
+ // Clear the Watchdog Timer after the image returns
+ gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL);
+
+ return Status;
+}
+
+EFI_STATUS
+BootAndroidBootImg (
+ IN UINTN BufferSize,
+ IN VOID *Buffer
+ )
+{
+ EFI_STATUS Status;
+ CHAR8 KernelArgs[ANDROID_BOOTIMG_KERNEL_ARGS_SIZE];
+ VOID *Kernel;
+ UINTN KernelSize;
+ VOID *Ramdisk;
+ UINTN RamdiskSize;
+ MEMORY_DEVICE_PATH KernelDevicePath;
+ CHAR16 *LoadOptions, *NewLoadOptions;
+
+ 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;
+
+ // Initialize Linux command line
+ LoadOptions = CatSPrint (NULL, L"%a", KernelArgs);
+ if (LoadOptions == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ if (RamdiskSize != 0) {
+ NewLoadOptions = CatSPrint (LoadOptions, L" initrd=0x%x,0x%x",
+ (UINTN)Ramdisk, RamdiskSize);
+ FreePool (LoadOptions);
+ if (NewLoadOptions == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ LoadOptions = NewLoadOptions;
+ }
+
+ Status = StartEfiApplication (gImageHandle,
+ (EFI_DEVICE_PATH_PROTOCOL *) &KernelDevicePath,
+ StrSize (LoadOptions),
+ LoadOptions);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "Couldn't Boot Linux: %d\n", Status));
+ Status = EFI_DEVICE_ERROR;
+ goto FreeLoadOptions;
+ }
+
+ // 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;
+
+FreeLoadOptions:
+ FreePool (LoadOptions);
+ return Status;
+}