-/** @file\r
-\r
- Copyright (c) 2014, ARM Ltd. All rights reserved.<BR>\r
- Copyright (c) 2016, Linaro 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
- Do any initialisation that needs to be done in order to be able to respond to\r
- commands.\r
-\r
- @retval EFI_SUCCESS Initialised successfully.\r
- @retval !EFI_SUCCESS Error in initialisation.\r
-*/\r
-STATIC\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
- // We will use these labels as the key in ArmFastbootPlatformFlashPartition.\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
- CopyMem (\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
-/*\r
- To be called when Fastboot is finished and we aren't rebooting or booting an\r
- image. Undo initialisation, free resrouces.\r
-*/\r
-STATIC\r
-VOID\r
-ArmFastbootPlatformUnInit (\r
- VOID\r
- )\r
-{\r
- FreePartitionList ();\r
-}\r
-\r
-/*\r
- Flash the partition named (according to a platform-specific scheme)\r
- PartitionName, with the image pointed to by Buffer, whose size is BufferSize.\r
-\r
- @param[in] PartitionName Null-terminated name of partition to write.\r
- @param[in] BufferSize Size of Buffer in byets.\r
- @param[in] Buffer Data to write to partition.\r
-\r
- @retval EFI_NOT_FOUND No such partition.\r
- @retval EFI_DEVICE_ERROR Flashing failed.\r
-*/\r
-STATIC\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
- AsciiStrToUnicodeStrS (PartitionName, PartitionNameUnicode,\r
- ARRAY_SIZE (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
-/*\r
- Erase the partition named PartitionName.\r
-\r
- @param[in] PartitionName Null-terminated name of partition to erase.\r
-\r
- @retval EFI_NOT_FOUND No such partition.\r
- @retval EFI_DEVICE_ERROR Erasing failed.\r
-*/\r
-STATIC\r
-EFI_STATUS\r
-ArmFastbootPlatformErasePartition (\r
- IN CHAR8 *Partition\r
- )\r
-{\r
- return EFI_SUCCESS;\r
-}\r
-\r
-/*\r
- If the variable referred to by Name exists, copy it (as a null-terminated\r
- string) into Value. If it doesn't exist, put the Empty string in Value.\r
-\r
- Variable names and values may not be larger than 60 bytes, excluding the\r
- terminal null character. This is a limitation of the Fastboot protocol.\r
-\r
- The Fastboot application will handle platform-nonspecific variables\r
- (Currently "version" is the only one of these.)\r
-\r
- @param[in] Name Null-terminated name of Fastboot variable to retrieve.\r
- @param[out] Value Caller-allocated buffer for null-terminated value of\r
- variable.\r
-\r
- @retval EFI_SUCCESS The variable was retrieved, or it doesn't exist.\r
- @retval EFI_DEVICE_ERROR There was an error looking up the variable. This\r
- does _not_ include the variable not existing.\r
-*/\r
-STATIC\r
-EFI_STATUS\r
-ArmFastbootPlatformGetVar (\r
- IN CHAR8 *Name,\r
- OUT CHAR8 *Value\r
- )\r
-{\r
- if (AsciiStrCmp (Name, "product")) {\r
- AsciiStrCpyS (Value, 61, FixedPcdGetPtr (PcdFirmwareVendor));\r
- } else {\r
- *Value = '\0';\r
- }\r
- return EFI_SUCCESS;\r
-}\r
-\r
-/*\r
- React to an OEM-specific command.\r
-\r
- Future versions of this function might want to allow the platform to do some\r
- extra communication with the host. A way to do this would be to add a function\r
- to the FASTBOOT_TRANSPORT_PROTOCOL that allows the implementation of\r
- DoOemCommand to replace the ReceiveEvent with its own, and to restore the old\r
- one when it's finished.\r
-\r
- However at the moment although the specification allows it, the AOSP fastboot\r
- host application doesn't handle receiving any data from the client, and it\r
- doesn't support a data phase for OEM commands.\r
-\r
- @param[in] Command Null-terminated command string.\r
-\r
- @retval EFI_SUCCESS The command executed successfully.\r
- @retval EFI_NOT_FOUND The command wasn't recognised.\r
- @retval EFI_DEVICE_ERROR There was an error executing the command.\r
-*/\r
-STATIC\r
-EFI_STATUS\r
-ArmFastbootPlatformOemCommand (\r
- IN CHAR8 *Command\r
- )\r
-{\r
- CHAR16 CommandUnicode[65];\r
-\r
- AsciiStrToUnicodeStrS (Command, CommandUnicode, ARRAY_SIZE (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
-STATIC 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
- return gBS->InstallProtocolInterface (\r
- &ImageHandle,\r
- &gAndroidFastbootPlatformProtocolGuid,\r
- EFI_NATIVE_INTERFACE,\r
- &mPlatformProtocol\r
- );\r
-}\r