--- /dev/null
+/** @file\r
+\r
+ Copyright (c) 2014, ARM Ltd. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+/*\r
+ Implementation of the Android Fastboot Platform protocol, to be used by the\r
+ Fastboot UEFI application, for ARM Versatile Express platforms.\r
+*/\r
+\r
+#include <Protocol/AndroidFastbootPlatform.h>\r
+#include <Protocol/BlockIo.h>\r
+#include <Protocol/DiskIo.h>\r
+\r
+#include <Library/BaseLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/DevicePathLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+\r
+#define FLASH_DEVICE_PATH_SIZE(DevPath) ( GetDevicePathSize (DevPath) - \\r
+ sizeof (EFI_DEVICE_PATH_PROTOCOL))\r
+\r
+#define PARTITION_NAME_MAX_LENGTH 72/2\r
+\r
+#define IS_ALPHA(Char) (((Char) <= L'z' && (Char) >= L'a') || \\r
+ ((Char) <= L'Z' && (Char) >= L'Z'))\r
+\r
+typedef struct _FASTBOOT_PARTITION_LIST {\r
+ LIST_ENTRY Link;\r
+ CHAR16 PartitionName[PARTITION_NAME_MAX_LENGTH];\r
+ EFI_HANDLE PartitionHandle;\r
+} FASTBOOT_PARTITION_LIST;\r
+\r
+STATIC LIST_ENTRY mPartitionListHead;\r
+\r
+/*\r
+ Helper to free the partition list\r
+*/\r
+STATIC\r
+VOID\r
+FreePartitionList (\r
+ VOID\r
+ )\r
+{\r
+ FASTBOOT_PARTITION_LIST *Entry;\r
+ FASTBOOT_PARTITION_LIST *NextEntry;\r
+\r
+ Entry = (FASTBOOT_PARTITION_LIST *) GetFirstNode (&mPartitionListHead);\r
+ while (!IsNull (&mPartitionListHead, &Entry->Link)) {\r
+ NextEntry = (FASTBOOT_PARTITION_LIST *) GetNextNode (&mPartitionListHead, &Entry->Link);\r
+\r
+ RemoveEntryList (&Entry->Link);\r
+ FreePool (Entry);\r
+\r
+ Entry = NextEntry;\r
+ }\r
+}\r
+/*\r
+ Read the PartitionName fields from the GPT partition entries, putting them\r
+ into an allocated array that should later be freed.\r
+*/\r
+STATIC\r
+EFI_STATUS\r
+ReadPartitionEntries (\r
+ IN EFI_BLOCK_IO_PROTOCOL *BlockIo,\r
+ OUT EFI_PARTITION_ENTRY **PartitionEntries\r
+ )\r
+{\r
+ UINTN EntrySize;\r
+ UINTN NumEntries;\r
+ UINTN BufferSize;\r
+ UINT32 MediaId;\r
+ EFI_PARTITION_TABLE_HEADER *GptHeader;\r
+ EFI_STATUS Status;\r
+\r
+ MediaId = BlockIo->Media->MediaId;\r
+\r
+ //\r
+ // Read size of Partition entry and number of entries from GPT header\r
+ //\r
+\r
+ GptHeader = AllocatePool (BlockIo->Media->BlockSize);\r
+ if (GptHeader == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Status = BlockIo->ReadBlocks (BlockIo, MediaId, 1, BlockIo->Media->BlockSize, (VOID *) GptHeader);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ // Check there is a GPT on the media\r
+ if (GptHeader->Header.Signature != EFI_PTAB_HEADER_ID ||\r
+ GptHeader->MyLBA != 1) {\r
+ DEBUG ((EFI_D_ERROR,\r
+ "Fastboot platform: No GPT on flash. "\r
+ "Fastboot on Versatile Express does not support MBR.\n"\r
+ ));\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ EntrySize = GptHeader->SizeOfPartitionEntry;\r
+ NumEntries = GptHeader->NumberOfPartitionEntries;\r
+\r
+ FreePool (GptHeader);\r
+\r
+ ASSERT (EntrySize != 0);\r
+ ASSERT (NumEntries != 0);\r
+\r
+ BufferSize = ALIGN_VALUE (EntrySize * NumEntries, BlockIo->Media->BlockSize);\r
+ *PartitionEntries = AllocatePool (BufferSize);\r
+ if (PartitionEntries == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Status = BlockIo->ReadBlocks (BlockIo, MediaId, 2, BufferSize, (VOID *) *PartitionEntries);\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (PartitionEntries);\r
+ return Status;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/*\r
+ Initialise: Open the Android NVM device and find the partitions on it. Save them in\r
+ a list along with the "PartitionName" fields for their GPT entries.\r
+ We will use these partition names as the key in\r
+ ArmFastbootPlatformFlashPartition.\r
+*/\r
+EFI_STATUS\r
+ArmFastbootPlatformInit (\r
+ VOID\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_DEVICE_PATH_PROTOCOL *FlashDevicePath;\r
+ EFI_DEVICE_PATH_PROTOCOL *FlashDevicePathDup;\r
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
+ EFI_DEVICE_PATH_PROTOCOL *NextNode;\r
+ HARDDRIVE_DEVICE_PATH *PartitionNode;\r
+ UINTN NumHandles;\r
+ EFI_HANDLE *AllHandles;\r
+ UINTN LoopIndex;\r
+ EFI_HANDLE FlashHandle;\r
+ EFI_BLOCK_IO_PROTOCOL *FlashBlockIo;\r
+ EFI_PARTITION_ENTRY *PartitionEntries;\r
+ FASTBOOT_PARTITION_LIST *Entry;\r
+\r
+ InitializeListHead (&mPartitionListHead);\r
+\r
+ //\r
+ // Get EFI_HANDLES for all the partitions on the block devices pointed to by\r
+ // PcdFastbootFlashDevicePath, also saving their GPT partition labels.\r
+ // There's no way to find all of a device's children, so we get every handle\r
+ // in the system supporting EFI_BLOCK_IO_PROTOCOL and then filter out ones\r
+ // that don't represent partitions on the flash device.\r
+ //\r
+\r
+ FlashDevicePath = ConvertTextToDevicePath ((CHAR16*)FixedPcdGetPtr (PcdAndroidFastbootNvmDevicePath));\r
+\r
+ //\r
+ // Open the Disk IO protocol on the flash device - this will be used to read\r
+ // partition names out of the GPT entries\r
+ //\r
+ // Create another device path pointer because LocateDevicePath will modify it.\r
+ FlashDevicePathDup = FlashDevicePath;\r
+ Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &FlashDevicePathDup, &FlashHandle);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_ERROR, "Warning: Couldn't locate Android NVM device (status: %r)\n", Status));\r
+ // Failing to locate partitions should not prevent to do other Android FastBoot actions\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ Status = gBS->OpenProtocol (\r
+ FlashHandle,\r
+ &gEfiBlockIoProtocolGuid,\r
+ (VOID **) &FlashBlockIo,\r
+ gImageHandle,\r
+ NULL,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_ERROR, "Fastboot platform: Couldn't open Android NVM device (status: %r)\n", Status));\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ // Read the GPT partition entry array into memory so we can get the partition names\r
+ Status = ReadPartitionEntries (FlashBlockIo, &PartitionEntries);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_ERROR, "Warning: Failed to read partitions from Android NVM device (status: %r)\n", Status));\r
+ // Failing to locate partitions should not prevent to do other Android FastBoot actions\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ // Get every Block IO protocol instance installed in the system\r
+ Status = gBS->LocateHandleBuffer (\r
+ ByProtocol,\r
+ &gEfiBlockIoProtocolGuid,\r
+ NULL,\r
+ &NumHandles,\r
+ &AllHandles\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ // Filter out handles that aren't children of the flash device\r
+ for (LoopIndex = 0; LoopIndex < NumHandles; LoopIndex++) {\r
+ // Get the device path for the handle\r
+ Status = gBS->OpenProtocol (\r
+ AllHandles[LoopIndex],\r
+ &gEfiDevicePathProtocolGuid,\r
+ (VOID **) &DevicePath,\r
+ gImageHandle,\r
+ NULL,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ // Check if it is a sub-device of the flash device\r
+ if (!CompareMem (DevicePath, FlashDevicePath, FLASH_DEVICE_PATH_SIZE (FlashDevicePath))) {\r
+ // Device path starts with path of flash device. Check it isn't the flash\r
+ // device itself.\r
+ NextNode = NextDevicePathNode (DevicePath);\r
+ if (IsDevicePathEndType (NextNode)) {\r
+ continue;\r
+ }\r
+\r
+ // Assert that this device path node represents a partition.\r
+ ASSERT (NextNode->Type == MEDIA_DEVICE_PATH &&\r
+ NextNode->SubType == MEDIA_HARDDRIVE_DP);\r
+\r
+ PartitionNode = (HARDDRIVE_DEVICE_PATH *) NextNode;\r
+\r
+ // Assert that the partition type is GPT. ReadPartitionEntries checks for\r
+ // presence of a GPT, so we should never find MBR partitions.\r
+ // ("MBRType" is a misnomer - this field is actually called "Partition\r
+ // Format")\r
+ ASSERT (PartitionNode->MBRType == MBR_TYPE_EFI_PARTITION_TABLE_HEADER);\r
+\r
+ // The firmware may install a handle for "partition 0", representing the\r
+ // whole device. Ignore it.\r
+ if (PartitionNode->PartitionNumber == 0) {\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // Add the partition handle to the list\r
+ //\r
+\r
+ // Create entry\r
+ Entry = AllocatePool (sizeof (FASTBOOT_PARTITION_LIST));\r
+ if (Entry == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ FreePartitionList ();\r
+ goto Exit;\r
+ }\r
+\r
+ // Copy handle and partition name\r
+ Entry->PartitionHandle = AllHandles[LoopIndex];\r
+ StrnCpy (\r
+ Entry->PartitionName,\r
+ PartitionEntries[PartitionNode->PartitionNumber - 1].PartitionName, // Partition numbers start from 1.\r
+ PARTITION_NAME_MAX_LENGTH\r
+ );\r
+ InsertTailList (&mPartitionListHead, &Entry->Link);\r
+\r
+ // Print a debug message if the partition label is empty or looks like\r
+ // garbage.\r
+ if (!IS_ALPHA (Entry->PartitionName[0])) {\r
+ DEBUG ((EFI_D_ERROR,\r
+ "Warning: Partition %d doesn't seem to have a GPT partition label. "\r
+ "You won't be able to flash it with Fastboot.\n",\r
+ PartitionNode->PartitionNumber\r
+ ));\r
+ }\r
+ }\r
+ }\r
+\r
+Exit:\r
+ FreePool (PartitionEntries);\r
+ FreePool (FlashDevicePath);\r
+ FreePool (AllHandles);\r
+ return Status;\r
+\r
+}\r
+\r
+VOID\r
+ArmFastbootPlatformUnInit (\r
+ VOID\r
+ )\r
+{\r
+ FreePartitionList ();\r
+}\r
+\r
+EFI_STATUS\r
+ArmFastbootPlatformFlashPartition (\r
+ IN CHAR8 *PartitionName,\r
+ IN UINTN Size,\r
+ IN VOID *Image\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_BLOCK_IO_PROTOCOL *BlockIo;\r
+ EFI_DISK_IO_PROTOCOL *DiskIo;\r
+ UINT32 MediaId;\r
+ UINTN PartitionSize;\r
+ FASTBOOT_PARTITION_LIST *Entry;\r
+ CHAR16 PartitionNameUnicode[60];\r
+ BOOLEAN PartitionFound;\r
+\r
+ AsciiStrToUnicodeStr (PartitionName, PartitionNameUnicode);\r
+\r
+ PartitionFound = FALSE;\r
+ Entry = (FASTBOOT_PARTITION_LIST *) GetFirstNode (&(mPartitionListHead));\r
+ while (!IsNull (&mPartitionListHead, &Entry->Link)) {\r
+ // Search the partition list for the partition named by PartitionName\r
+ if (StrCmp (Entry->PartitionName, PartitionNameUnicode) == 0) {\r
+ PartitionFound = TRUE;\r
+ break;\r
+ }\r
+\r
+ Entry = (FASTBOOT_PARTITION_LIST *) GetNextNode (&mPartitionListHead, &(Entry)->Link);\r
+ }\r
+ if (!PartitionFound) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ Status = gBS->OpenProtocol (\r
+ Entry->PartitionHandle,\r
+ &gEfiBlockIoProtocolGuid,\r
+ (VOID **) &BlockIo,\r
+ gImageHandle,\r
+ NULL,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_ERROR, "Fastboot platform: couldn't open Block IO for flash: %r\n", Status));\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ // Check image will fit on device\r
+ PartitionSize = (BlockIo->Media->LastBlock + 1) * BlockIo->Media->BlockSize;\r
+ if (PartitionSize < Size) {\r
+ DEBUG ((EFI_D_ERROR, "Partition not big enough.\n"));\r
+ DEBUG ((EFI_D_ERROR, "Partition Size:\t%d\nImage Size:\t%d\n", PartitionSize, Size));\r
+\r
+ return EFI_VOLUME_FULL;\r
+ }\r
+\r
+ MediaId = BlockIo->Media->MediaId;\r
+\r
+ Status = gBS->OpenProtocol (\r
+ Entry->PartitionHandle,\r
+ &gEfiDiskIoProtocolGuid,\r
+ (VOID **) &DiskIo,\r
+ gImageHandle,\r
+ NULL,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ Status = DiskIo->WriteDisk (DiskIo, MediaId, 0, Size, Image);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ BlockIo->FlushBlocks(BlockIo);\r
+\r
+ return Status;\r
+}\r
+\r
+EFI_STATUS\r
+ArmFastbootPlatformErasePartition (\r
+ IN CHAR8 *Partition\r
+ )\r
+{\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+EFI_STATUS\r
+ArmFastbootPlatformGetVar (\r
+ IN CHAR8 *Name,\r
+ OUT CHAR8 *Value\r
+ )\r
+{\r
+ if (AsciiStrCmp (Name, "product")) {\r
+ AsciiStrCpy (Value, FixedPcdGetPtr (PcdFirmwareVendor));\r
+ } else {\r
+ *Value = '\0';\r
+ }\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+EFI_STATUS\r
+ArmFastbootPlatformOemCommand (\r
+ IN CHAR8 *Command\r
+ )\r
+{\r
+ CHAR16 CommandUnicode[65];\r
+\r
+ AsciiStrToUnicodeStr (Command, CommandUnicode);\r
+\r
+ if (AsciiStrCmp (Command, "Demonstrate") == 0) {\r
+ DEBUG ((EFI_D_ERROR, "ARM OEM Fastboot command 'Demonstrate' received.\n"));\r
+ return EFI_SUCCESS;\r
+ } else {\r
+ DEBUG ((EFI_D_ERROR,\r
+ "VExpress: Unrecognised Fastboot OEM command: %s\n",\r
+ CommandUnicode\r
+ ));\r
+ return EFI_NOT_FOUND;\r
+ }\r
+}\r
+\r
+FASTBOOT_PLATFORM_PROTOCOL mPlatformProtocol = {\r
+ ArmFastbootPlatformInit,\r
+ ArmFastbootPlatformUnInit,\r
+ ArmFastbootPlatformFlashPartition,\r
+ ArmFastbootPlatformErasePartition,\r
+ ArmFastbootPlatformGetVar,\r
+ ArmFastbootPlatformOemCommand\r
+};\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+ArmAndroidFastbootPlatformEntryPoint (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ gImageHandle = ImageHandle;\r
+\r
+ return gBS->InstallProtocolInterface (\r
+ &ImageHandle,\r
+ &gAndroidFastbootPlatformProtocolGuid,\r
+ EFI_NATIVE_INTERFACE,\r
+ &mPlatformProtocol\r
+ );\r
+}\r