diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
commit | f215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch) | |
tree | 6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Application | |
parent | Initial commit. (diff) | |
download | virtualbox-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')
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; +} |