summaryrefslogtreecommitdiff
path: root/Platform/ARM/VExpressPkg/Drivers/ArmVExpressFastBootDxe/ArmVExpressFastBoot.c
diff options
context:
space:
mode:
Diffstat (limited to 'Platform/ARM/VExpressPkg/Drivers/ArmVExpressFastBootDxe/ArmVExpressFastBoot.c')
-rw-r--r--Platform/ARM/VExpressPkg/Drivers/ArmVExpressFastBootDxe/ArmVExpressFastBoot.c519
1 files changed, 519 insertions, 0 deletions
diff --git a/Platform/ARM/VExpressPkg/Drivers/ArmVExpressFastBootDxe/ArmVExpressFastBoot.c b/Platform/ARM/VExpressPkg/Drivers/ArmVExpressFastBootDxe/ArmVExpressFastBoot.c
new file mode 100644
index 0000000000..a01bf3c671
--- /dev/null
+++ b/Platform/ARM/VExpressPkg/Drivers/ArmVExpressFastBootDxe/ArmVExpressFastBoot.c
@@ -0,0 +1,519 @@
+/** @file
+
+ Copyright (c) 2014, ARM Ltd. All rights reserved.<BR>
+ Copyright (c) 2016, Linaro 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.
+
+**/
+
+/*
+ Implementation of the Android Fastboot Platform protocol, to be used by the
+ Fastboot UEFI application, for ARM Versatile Express platforms.
+*/
+
+#include <Protocol/AndroidFastbootPlatform.h>
+#include <Protocol/BlockIo.h>
+#include <Protocol/DiskIo.h>
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+#define FLASH_DEVICE_PATH_SIZE(DevPath) ( GetDevicePathSize (DevPath) - \
+ sizeof (EFI_DEVICE_PATH_PROTOCOL))
+
+#define PARTITION_NAME_MAX_LENGTH 72/2
+
+#define IS_ALPHA(Char) (((Char) <= L'z' && (Char) >= L'a') || \
+ ((Char) <= L'Z' && (Char) >= L'Z'))
+
+typedef struct _FASTBOOT_PARTITION_LIST {
+ LIST_ENTRY Link;
+ CHAR16 PartitionName[PARTITION_NAME_MAX_LENGTH];
+ EFI_HANDLE PartitionHandle;
+} FASTBOOT_PARTITION_LIST;
+
+STATIC LIST_ENTRY mPartitionListHead;
+
+/*
+ Helper to free the partition list
+*/
+STATIC
+VOID
+FreePartitionList (
+ VOID
+ )
+{
+ FASTBOOT_PARTITION_LIST *Entry;
+ FASTBOOT_PARTITION_LIST *NextEntry;
+
+ Entry = (FASTBOOT_PARTITION_LIST *) GetFirstNode (&mPartitionListHead);
+ while (!IsNull (&mPartitionListHead, &Entry->Link)) {
+ NextEntry = (FASTBOOT_PARTITION_LIST *) GetNextNode (&mPartitionListHead, &Entry->Link);
+
+ RemoveEntryList (&Entry->Link);
+ FreePool (Entry);
+
+ Entry = NextEntry;
+ }
+}
+/*
+ Read the PartitionName fields from the GPT partition entries, putting them
+ into an allocated array that should later be freed.
+*/
+STATIC
+EFI_STATUS
+ReadPartitionEntries (
+ IN EFI_BLOCK_IO_PROTOCOL *BlockIo,
+ OUT EFI_PARTITION_ENTRY **PartitionEntries
+ )
+{
+ UINTN EntrySize;
+ UINTN NumEntries;
+ UINTN BufferSize;
+ UINT32 MediaId;
+ EFI_PARTITION_TABLE_HEADER *GptHeader;
+ EFI_STATUS Status;
+
+ MediaId = BlockIo->Media->MediaId;
+
+ //
+ // Read size of Partition entry and number of entries from GPT header
+ //
+
+ GptHeader = AllocatePool (BlockIo->Media->BlockSize);
+ if (GptHeader == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = BlockIo->ReadBlocks (BlockIo, MediaId, 1, BlockIo->Media->BlockSize, (VOID *) GptHeader);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ // Check there is a GPT on the media
+ if (GptHeader->Header.Signature != EFI_PTAB_HEADER_ID ||
+ GptHeader->MyLBA != 1) {
+ DEBUG ((EFI_D_ERROR,
+ "Fastboot platform: No GPT on flash. "
+ "Fastboot on Versatile Express does not support MBR.\n"
+ ));
+ return EFI_DEVICE_ERROR;
+ }
+
+ EntrySize = GptHeader->SizeOfPartitionEntry;
+ NumEntries = GptHeader->NumberOfPartitionEntries;
+
+ FreePool (GptHeader);
+
+ ASSERT (EntrySize != 0);
+ ASSERT (NumEntries != 0);
+
+ BufferSize = ALIGN_VALUE (EntrySize * NumEntries, BlockIo->Media->BlockSize);
+ *PartitionEntries = AllocatePool (BufferSize);
+ if (PartitionEntries == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = BlockIo->ReadBlocks (BlockIo, MediaId, 2, BufferSize, (VOID *) *PartitionEntries);
+ if (EFI_ERROR (Status)) {
+ FreePool (PartitionEntries);
+ return Status;
+ }
+
+ return Status;
+}
+
+
+/*
+ Do any initialisation that needs to be done in order to be able to respond to
+ commands.
+
+ @retval EFI_SUCCESS Initialised successfully.
+ @retval !EFI_SUCCESS Error in initialisation.
+*/
+STATIC
+EFI_STATUS
+ArmFastbootPlatformInit (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ EFI_DEVICE_PATH_PROTOCOL *FlashDevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *FlashDevicePathDup;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *NextNode;
+ HARDDRIVE_DEVICE_PATH *PartitionNode;
+ UINTN NumHandles;
+ EFI_HANDLE *AllHandles;
+ UINTN LoopIndex;
+ EFI_HANDLE FlashHandle;
+ EFI_BLOCK_IO_PROTOCOL *FlashBlockIo;
+ EFI_PARTITION_ENTRY *PartitionEntries;
+ FASTBOOT_PARTITION_LIST *Entry;
+
+ InitializeListHead (&mPartitionListHead);
+
+ //
+ // Get EFI_HANDLES for all the partitions on the block devices pointed to by
+ // PcdFastbootFlashDevicePath, also saving their GPT partition labels.
+ // We will use these labels as the key in ArmFastbootPlatformFlashPartition.
+ // There's no way to find all of a device's children, so we get every handle
+ // in the system supporting EFI_BLOCK_IO_PROTOCOL and then filter out ones
+ // that don't represent partitions on the flash device.
+ //
+
+ FlashDevicePath = ConvertTextToDevicePath ((CHAR16*)FixedPcdGetPtr (PcdAndroidFastbootNvmDevicePath));
+
+ //
+ // Open the Disk IO protocol on the flash device - this will be used to read
+ // partition names out of the GPT entries
+ //
+ // Create another device path pointer because LocateDevicePath will modify it.
+ FlashDevicePathDup = FlashDevicePath;
+ Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &FlashDevicePathDup, &FlashHandle);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "Warning: Couldn't locate Android NVM device (status: %r)\n", Status));
+ // Failing to locate partitions should not prevent to do other Android FastBoot actions
+ return EFI_SUCCESS;
+ }
+
+ Status = gBS->OpenProtocol (
+ FlashHandle,
+ &gEfiBlockIoProtocolGuid,
+ (VOID **) &FlashBlockIo,
+ gImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "Fastboot platform: Couldn't open Android NVM device (status: %r)\n", Status));
+ return EFI_DEVICE_ERROR;
+ }
+
+ // Read the GPT partition entry array into memory so we can get the partition names
+ Status = ReadPartitionEntries (FlashBlockIo, &PartitionEntries);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "Warning: Failed to read partitions from Android NVM device (status: %r)\n", Status));
+ // Failing to locate partitions should not prevent to do other Android FastBoot actions
+ return EFI_SUCCESS;
+ }
+
+ // Get every Block IO protocol instance installed in the system
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiBlockIoProtocolGuid,
+ NULL,
+ &NumHandles,
+ &AllHandles
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ // Filter out handles that aren't children of the flash device
+ for (LoopIndex = 0; LoopIndex < NumHandles; LoopIndex++) {
+ // Get the device path for the handle
+ Status = gBS->OpenProtocol (
+ AllHandles[LoopIndex],
+ &gEfiDevicePathProtocolGuid,
+ (VOID **) &DevicePath,
+ gImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ // Check if it is a sub-device of the flash device
+ if (!CompareMem (DevicePath, FlashDevicePath, FLASH_DEVICE_PATH_SIZE (FlashDevicePath))) {
+ // Device path starts with path of flash device. Check it isn't the flash
+ // device itself.
+ NextNode = NextDevicePathNode (DevicePath);
+ if (IsDevicePathEndType (NextNode)) {
+ continue;
+ }
+
+ // Assert that this device path node represents a partition.
+ ASSERT (NextNode->Type == MEDIA_DEVICE_PATH &&
+ NextNode->SubType == MEDIA_HARDDRIVE_DP);
+
+ PartitionNode = (HARDDRIVE_DEVICE_PATH *) NextNode;
+
+ // Assert that the partition type is GPT. ReadPartitionEntries checks for
+ // presence of a GPT, so we should never find MBR partitions.
+ // ("MBRType" is a misnomer - this field is actually called "Partition
+ // Format")
+ ASSERT (PartitionNode->MBRType == MBR_TYPE_EFI_PARTITION_TABLE_HEADER);
+
+ // The firmware may install a handle for "partition 0", representing the
+ // whole device. Ignore it.
+ if (PartitionNode->PartitionNumber == 0) {
+ continue;
+ }
+
+ //
+ // Add the partition handle to the list
+ //
+
+ // Create entry
+ Entry = AllocatePool (sizeof (FASTBOOT_PARTITION_LIST));
+ if (Entry == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ FreePartitionList ();
+ goto Exit;
+ }
+
+ // Copy handle and partition name
+ Entry->PartitionHandle = AllHandles[LoopIndex];
+ CopyMem (
+ Entry->PartitionName,
+ PartitionEntries[PartitionNode->PartitionNumber - 1].PartitionName, // Partition numbers start from 1.
+ PARTITION_NAME_MAX_LENGTH
+ );
+ InsertTailList (&mPartitionListHead, &Entry->Link);
+
+ // Print a debug message if the partition label is empty or looks like
+ // garbage.
+ if (!IS_ALPHA (Entry->PartitionName[0])) {
+ DEBUG ((EFI_D_ERROR,
+ "Warning: Partition %d doesn't seem to have a GPT partition label. "
+ "You won't be able to flash it with Fastboot.\n",
+ PartitionNode->PartitionNumber
+ ));
+ }
+ }
+ }
+
+Exit:
+ FreePool (PartitionEntries);
+ FreePool (FlashDevicePath);
+ FreePool (AllHandles);
+ return Status;
+
+}
+
+/*
+ To be called when Fastboot is finished and we aren't rebooting or booting an
+ image. Undo initialisation, free resrouces.
+*/
+STATIC
+VOID
+ArmFastbootPlatformUnInit (
+ VOID
+ )
+{
+ FreePartitionList ();
+}
+
+/*
+ Flash the partition named (according to a platform-specific scheme)
+ PartitionName, with the image pointed to by Buffer, whose size is BufferSize.
+
+ @param[in] PartitionName Null-terminated name of partition to write.
+ @param[in] BufferSize Size of Buffer in byets.
+ @param[in] Buffer Data to write to partition.
+
+ @retval EFI_NOT_FOUND No such partition.
+ @retval EFI_DEVICE_ERROR Flashing failed.
+*/
+STATIC
+EFI_STATUS
+ArmFastbootPlatformFlashPartition (
+ IN CHAR8 *PartitionName,
+ IN UINTN Size,
+ IN VOID *Image
+ )
+{
+ EFI_STATUS Status;
+ EFI_BLOCK_IO_PROTOCOL *BlockIo;
+ EFI_DISK_IO_PROTOCOL *DiskIo;
+ UINT32 MediaId;
+ UINTN PartitionSize;
+ FASTBOOT_PARTITION_LIST *Entry;
+ CHAR16 PartitionNameUnicode[60];
+ BOOLEAN PartitionFound;
+
+ AsciiStrToUnicodeStrS (PartitionName, PartitionNameUnicode,
+ ARRAY_SIZE (PartitionNameUnicode));
+
+ PartitionFound = FALSE;
+ Entry = (FASTBOOT_PARTITION_LIST *) GetFirstNode (&(mPartitionListHead));
+ while (!IsNull (&mPartitionListHead, &Entry->Link)) {
+ // Search the partition list for the partition named by PartitionName
+ if (StrCmp (Entry->PartitionName, PartitionNameUnicode) == 0) {
+ PartitionFound = TRUE;
+ break;
+ }
+
+ Entry = (FASTBOOT_PARTITION_LIST *) GetNextNode (&mPartitionListHead, &(Entry)->Link);
+ }
+ if (!PartitionFound) {
+ return EFI_NOT_FOUND;
+ }
+
+ Status = gBS->OpenProtocol (
+ Entry->PartitionHandle,
+ &gEfiBlockIoProtocolGuid,
+ (VOID **) &BlockIo,
+ gImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "Fastboot platform: couldn't open Block IO for flash: %r\n", Status));
+ return EFI_NOT_FOUND;
+ }
+
+ // Check image will fit on device
+ PartitionSize = (BlockIo->Media->LastBlock + 1) * BlockIo->Media->BlockSize;
+ if (PartitionSize < Size) {
+ DEBUG ((EFI_D_ERROR, "Partition not big enough.\n"));
+ DEBUG ((EFI_D_ERROR, "Partition Size:\t%d\nImage Size:\t%d\n", PartitionSize, Size));
+
+ return EFI_VOLUME_FULL;
+ }
+
+ MediaId = BlockIo->Media->MediaId;
+
+ Status = gBS->OpenProtocol (
+ Entry->PartitionHandle,
+ &gEfiDiskIoProtocolGuid,
+ (VOID **) &DiskIo,
+ gImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ Status = DiskIo->WriteDisk (DiskIo, MediaId, 0, Size, Image);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ BlockIo->FlushBlocks(BlockIo);
+
+ return Status;
+}
+
+/*
+ Erase the partition named PartitionName.
+
+ @param[in] PartitionName Null-terminated name of partition to erase.
+
+ @retval EFI_NOT_FOUND No such partition.
+ @retval EFI_DEVICE_ERROR Erasing failed.
+*/
+STATIC
+EFI_STATUS
+ArmFastbootPlatformErasePartition (
+ IN CHAR8 *Partition
+ )
+{
+ return EFI_SUCCESS;
+}
+
+/*
+ If the variable referred to by Name exists, copy it (as a null-terminated
+ string) into Value. If it doesn't exist, put the Empty string in Value.
+
+ Variable names and values may not be larger than 60 bytes, excluding the
+ terminal null character. This is a limitation of the Fastboot protocol.
+
+ The Fastboot application will handle platform-nonspecific variables
+ (Currently "version" is the only one of these.)
+
+ @param[in] Name Null-terminated name of Fastboot variable to retrieve.
+ @param[out] Value Caller-allocated buffer for null-terminated value of
+ variable.
+
+ @retval EFI_SUCCESS The variable was retrieved, or it doesn't exist.
+ @retval EFI_DEVICE_ERROR There was an error looking up the variable. This
+ does _not_ include the variable not existing.
+*/
+STATIC
+EFI_STATUS
+ArmFastbootPlatformGetVar (
+ IN CHAR8 *Name,
+ OUT CHAR8 *Value
+ )
+{
+ if (AsciiStrCmp (Name, "product")) {
+ AsciiStrCpyS (Value, 61, FixedPcdGetPtr (PcdFirmwareVendor));
+ } else {
+ *Value = '\0';
+ }
+ return EFI_SUCCESS;
+}
+
+/*
+ React to an OEM-specific command.
+
+ Future versions of this function might want to allow the platform to do some
+ extra communication with the host. A way to do this would be to add a function
+ to the FASTBOOT_TRANSPORT_PROTOCOL that allows the implementation of
+ DoOemCommand to replace the ReceiveEvent with its own, and to restore the old
+ one when it's finished.
+
+ However at the moment although the specification allows it, the AOSP fastboot
+ host application doesn't handle receiving any data from the client, and it
+ doesn't support a data phase for OEM commands.
+
+ @param[in] Command Null-terminated command string.
+
+ @retval EFI_SUCCESS The command executed successfully.
+ @retval EFI_NOT_FOUND The command wasn't recognised.
+ @retval EFI_DEVICE_ERROR There was an error executing the command.
+*/
+STATIC
+EFI_STATUS
+ArmFastbootPlatformOemCommand (
+ IN CHAR8 *Command
+ )
+{
+ CHAR16 CommandUnicode[65];
+
+ AsciiStrToUnicodeStrS (Command, CommandUnicode, ARRAY_SIZE (CommandUnicode));
+
+ if (AsciiStrCmp (Command, "Demonstrate") == 0) {
+ DEBUG ((EFI_D_ERROR, "ARM OEM Fastboot command 'Demonstrate' received.\n"));
+ return EFI_SUCCESS;
+ } else {
+ DEBUG ((EFI_D_ERROR,
+ "VExpress: Unrecognised Fastboot OEM command: %s\n",
+ CommandUnicode
+ ));
+ return EFI_NOT_FOUND;
+ }
+}
+
+STATIC FASTBOOT_PLATFORM_PROTOCOL mPlatformProtocol = {
+ ArmFastbootPlatformInit,
+ ArmFastbootPlatformUnInit,
+ ArmFastbootPlatformFlashPartition,
+ ArmFastbootPlatformErasePartition,
+ ArmFastbootPlatformGetVar,
+ ArmFastbootPlatformOemCommand
+};
+
+EFI_STATUS
+EFIAPI
+ArmAndroidFastbootPlatformEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ return gBS->InstallProtocolInterface (
+ &ImageHandle,
+ &gAndroidFastbootPlatformProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &mPlatformProtocol
+ );
+}