3 Copyright (c) 2014, ARM Ltd. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
16 Implementation of the Android Fastboot Platform protocol, to be used by the
17 Fastboot UEFI application, for ARM Versatile Express platforms.
20 #include <Protocol/AndroidFastbootPlatform.h>
21 #include <Protocol/BlockIo.h>
22 #include <Protocol/DiskIo.h>
24 #include <Library/BaseLib.h>
25 #include <Library/BaseMemoryLib.h>
26 #include <Library/DebugLib.h>
27 #include <Library/DevicePathLib.h>
28 #include <Library/MemoryAllocationLib.h>
29 #include <Library/UefiBootServicesTableLib.h>
31 #define FLASH_DEVICE_PATH_SIZE(DevPath) ( GetDevicePathSize (DevPath) - \
32 sizeof (EFI_DEVICE_PATH_PROTOCOL))
34 #define PARTITION_NAME_MAX_LENGTH 72/2
36 #define IS_ALPHA(Char) (((Char) <= L'z' && (Char) >= L'a') || \
37 ((Char) <= L'Z' && (Char) >= L'Z'))
39 typedef struct _FASTBOOT_PARTITION_LIST
{
41 CHAR16 PartitionName
[PARTITION_NAME_MAX_LENGTH
];
42 EFI_HANDLE PartitionHandle
;
43 } FASTBOOT_PARTITION_LIST
;
45 STATIC LIST_ENTRY mPartitionListHead
;
48 Helper to free the partition list
56 FASTBOOT_PARTITION_LIST
*Entry
;
57 FASTBOOT_PARTITION_LIST
*NextEntry
;
59 Entry
= (FASTBOOT_PARTITION_LIST
*) GetFirstNode (&mPartitionListHead
);
60 while (!IsNull (&mPartitionListHead
, &Entry
->Link
)) {
61 NextEntry
= (FASTBOOT_PARTITION_LIST
*) GetNextNode (&mPartitionListHead
, &Entry
->Link
);
63 RemoveEntryList (&Entry
->Link
);
70 Read the PartitionName fields from the GPT partition entries, putting them
71 into an allocated array that should later be freed.
75 ReadPartitionEntries (
76 IN EFI_BLOCK_IO_PROTOCOL
*BlockIo
,
77 OUT EFI_PARTITION_ENTRY
**PartitionEntries
84 EFI_PARTITION_TABLE_HEADER
*GptHeader
;
87 MediaId
= BlockIo
->Media
->MediaId
;
90 // Read size of Partition entry and number of entries from GPT header
93 GptHeader
= AllocatePool (BlockIo
->Media
->BlockSize
);
94 if (GptHeader
== NULL
) {
95 return EFI_OUT_OF_RESOURCES
;
98 Status
= BlockIo
->ReadBlocks (BlockIo
, MediaId
, 1, BlockIo
->Media
->BlockSize
, (VOID
*) GptHeader
);
99 if (EFI_ERROR (Status
)) {
103 // Check there is a GPT on the media
104 if (GptHeader
->Header
.Signature
!= EFI_PTAB_HEADER_ID
||
105 GptHeader
->MyLBA
!= 1) {
107 "Fastboot platform: No GPT on flash. "
108 "Fastboot on Versatile Express does not support MBR.\n"
110 return EFI_DEVICE_ERROR
;
113 EntrySize
= GptHeader
->SizeOfPartitionEntry
;
114 NumEntries
= GptHeader
->NumberOfPartitionEntries
;
116 FreePool (GptHeader
);
118 ASSERT (EntrySize
!= 0);
119 ASSERT (NumEntries
!= 0);
121 BufferSize
= ALIGN_VALUE (EntrySize
* NumEntries
, BlockIo
->Media
->BlockSize
);
122 *PartitionEntries
= AllocatePool (BufferSize
);
123 if (PartitionEntries
== NULL
) {
124 return EFI_OUT_OF_RESOURCES
;
127 Status
= BlockIo
->ReadBlocks (BlockIo
, MediaId
, 2, BufferSize
, (VOID
*) *PartitionEntries
);
128 if (EFI_ERROR (Status
)) {
129 FreePool (PartitionEntries
);
138 Initialise: Open the Android NVM device and find the partitions on it. Save them in
139 a list along with the "PartitionName" fields for their GPT entries.
140 We will use these partition names as the key in
141 ArmFastbootPlatformFlashPartition.
144 ArmFastbootPlatformInit (
149 EFI_DEVICE_PATH_PROTOCOL
*FlashDevicePath
;
150 EFI_DEVICE_PATH_PROTOCOL
*FlashDevicePathDup
;
151 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
152 EFI_DEVICE_PATH_PROTOCOL
*NextNode
;
153 HARDDRIVE_DEVICE_PATH
*PartitionNode
;
155 EFI_HANDLE
*AllHandles
;
157 EFI_HANDLE FlashHandle
;
158 EFI_BLOCK_IO_PROTOCOL
*FlashBlockIo
;
159 EFI_PARTITION_ENTRY
*PartitionEntries
;
160 FASTBOOT_PARTITION_LIST
*Entry
;
162 InitializeListHead (&mPartitionListHead
);
165 // Get EFI_HANDLES for all the partitions on the block devices pointed to by
166 // PcdFastbootFlashDevicePath, also saving their GPT partition labels.
167 // There's no way to find all of a device's children, so we get every handle
168 // in the system supporting EFI_BLOCK_IO_PROTOCOL and then filter out ones
169 // that don't represent partitions on the flash device.
172 FlashDevicePath
= ConvertTextToDevicePath ((CHAR16
*)FixedPcdGetPtr (PcdAndroidFastbootNvmDevicePath
));
175 // Open the Disk IO protocol on the flash device - this will be used to read
176 // partition names out of the GPT entries
178 // Create another device path pointer because LocateDevicePath will modify it.
179 FlashDevicePathDup
= FlashDevicePath
;
180 Status
= gBS
->LocateDevicePath (&gEfiBlockIoProtocolGuid
, &FlashDevicePathDup
, &FlashHandle
);
181 if (EFI_ERROR (Status
)) {
182 DEBUG ((EFI_D_ERROR
, "Warning: Couldn't locate Android NVM device (status: %r)\n", Status
));
183 // Failing to locate partitions should not prevent to do other Android FastBoot actions
187 Status
= gBS
->OpenProtocol (
189 &gEfiBlockIoProtocolGuid
,
190 (VOID
**) &FlashBlockIo
,
193 EFI_OPEN_PROTOCOL_GET_PROTOCOL
195 if (EFI_ERROR (Status
)) {
196 DEBUG ((EFI_D_ERROR
, "Fastboot platform: Couldn't open Android NVM device (status: %r)\n", Status
));
197 return EFI_DEVICE_ERROR
;
200 // Read the GPT partition entry array into memory so we can get the partition names
201 Status
= ReadPartitionEntries (FlashBlockIo
, &PartitionEntries
);
202 if (EFI_ERROR (Status
)) {
203 DEBUG ((EFI_D_ERROR
, "Warning: Failed to read partitions from Android NVM device (status: %r)\n", Status
));
204 // Failing to locate partitions should not prevent to do other Android FastBoot actions
208 // Get every Block IO protocol instance installed in the system
209 Status
= gBS
->LocateHandleBuffer (
211 &gEfiBlockIoProtocolGuid
,
216 ASSERT_EFI_ERROR (Status
);
218 // Filter out handles that aren't children of the flash device
219 for (LoopIndex
= 0; LoopIndex
< NumHandles
; LoopIndex
++) {
220 // Get the device path for the handle
221 Status
= gBS
->OpenProtocol (
222 AllHandles
[LoopIndex
],
223 &gEfiDevicePathProtocolGuid
,
224 (VOID
**) &DevicePath
,
227 EFI_OPEN_PROTOCOL_GET_PROTOCOL
229 ASSERT_EFI_ERROR (Status
);
231 // Check if it is a sub-device of the flash device
232 if (!CompareMem (DevicePath
, FlashDevicePath
, FLASH_DEVICE_PATH_SIZE (FlashDevicePath
))) {
233 // Device path starts with path of flash device. Check it isn't the flash
235 NextNode
= NextDevicePathNode (DevicePath
);
236 if (IsDevicePathEndType (NextNode
)) {
240 // Assert that this device path node represents a partition.
241 ASSERT (NextNode
->Type
== MEDIA_DEVICE_PATH
&&
242 NextNode
->SubType
== MEDIA_HARDDRIVE_DP
);
244 PartitionNode
= (HARDDRIVE_DEVICE_PATH
*) NextNode
;
246 // Assert that the partition type is GPT. ReadPartitionEntries checks for
247 // presence of a GPT, so we should never find MBR partitions.
248 // ("MBRType" is a misnomer - this field is actually called "Partition
250 ASSERT (PartitionNode
->MBRType
== MBR_TYPE_EFI_PARTITION_TABLE_HEADER
);
252 // The firmware may install a handle for "partition 0", representing the
253 // whole device. Ignore it.
254 if (PartitionNode
->PartitionNumber
== 0) {
259 // Add the partition handle to the list
263 Entry
= AllocatePool (sizeof (FASTBOOT_PARTITION_LIST
));
265 Status
= EFI_OUT_OF_RESOURCES
;
266 FreePartitionList ();
270 // Copy handle and partition name
271 Entry
->PartitionHandle
= AllHandles
[LoopIndex
];
273 Entry
->PartitionName
,
274 PartitionEntries
[PartitionNode
->PartitionNumber
- 1].PartitionName
, // Partition numbers start from 1.
275 PARTITION_NAME_MAX_LENGTH
277 InsertTailList (&mPartitionListHead
, &Entry
->Link
);
279 // Print a debug message if the partition label is empty or looks like
281 if (!IS_ALPHA (Entry
->PartitionName
[0])) {
283 "Warning: Partition %d doesn't seem to have a GPT partition label. "
284 "You won't be able to flash it with Fastboot.\n",
285 PartitionNode
->PartitionNumber
292 FreePool (PartitionEntries
);
293 FreePool (FlashDevicePath
);
294 FreePool (AllHandles
);
300 ArmFastbootPlatformUnInit (
304 FreePartitionList ();
308 ArmFastbootPlatformFlashPartition (
309 IN CHAR8
*PartitionName
,
315 EFI_BLOCK_IO_PROTOCOL
*BlockIo
;
316 EFI_DISK_IO_PROTOCOL
*DiskIo
;
319 FASTBOOT_PARTITION_LIST
*Entry
;
320 CHAR16 PartitionNameUnicode
[60];
321 BOOLEAN PartitionFound
;
323 AsciiStrToUnicodeStr (PartitionName
, PartitionNameUnicode
);
325 PartitionFound
= FALSE
;
326 Entry
= (FASTBOOT_PARTITION_LIST
*) GetFirstNode (&(mPartitionListHead
));
327 while (!IsNull (&mPartitionListHead
, &Entry
->Link
)) {
328 // Search the partition list for the partition named by PartitionName
329 if (StrCmp (Entry
->PartitionName
, PartitionNameUnicode
) == 0) {
330 PartitionFound
= TRUE
;
334 Entry
= (FASTBOOT_PARTITION_LIST
*) GetNextNode (&mPartitionListHead
, &(Entry
)->Link
);
336 if (!PartitionFound
) {
337 return EFI_NOT_FOUND
;
340 Status
= gBS
->OpenProtocol (
341 Entry
->PartitionHandle
,
342 &gEfiBlockIoProtocolGuid
,
346 EFI_OPEN_PROTOCOL_GET_PROTOCOL
348 if (EFI_ERROR (Status
)) {
349 DEBUG ((EFI_D_ERROR
, "Fastboot platform: couldn't open Block IO for flash: %r\n", Status
));
350 return EFI_NOT_FOUND
;
353 // Check image will fit on device
354 PartitionSize
= (BlockIo
->Media
->LastBlock
+ 1) * BlockIo
->Media
->BlockSize
;
355 if (PartitionSize
< Size
) {
356 DEBUG ((EFI_D_ERROR
, "Partition not big enough.\n"));
357 DEBUG ((EFI_D_ERROR
, "Partition Size:\t%d\nImage Size:\t%d\n", PartitionSize
, Size
));
359 return EFI_VOLUME_FULL
;
362 MediaId
= BlockIo
->Media
->MediaId
;
364 Status
= gBS
->OpenProtocol (
365 Entry
->PartitionHandle
,
366 &gEfiDiskIoProtocolGuid
,
370 EFI_OPEN_PROTOCOL_GET_PROTOCOL
372 ASSERT_EFI_ERROR (Status
);
374 Status
= DiskIo
->WriteDisk (DiskIo
, MediaId
, 0, Size
, Image
);
375 if (EFI_ERROR (Status
)) {
379 BlockIo
->FlushBlocks(BlockIo
);
385 ArmFastbootPlatformErasePartition (
393 ArmFastbootPlatformGetVar (
398 if (AsciiStrCmp (Name
, "product")) {
399 AsciiStrCpy (Value
, FixedPcdGetPtr (PcdFirmwareVendor
));
407 ArmFastbootPlatformOemCommand (
411 CHAR16 CommandUnicode
[65];
413 AsciiStrToUnicodeStr (Command
, CommandUnicode
);
415 if (AsciiStrCmp (Command
, "Demonstrate") == 0) {
416 DEBUG ((EFI_D_ERROR
, "ARM OEM Fastboot command 'Demonstrate' received.\n"));
420 "VExpress: Unrecognised Fastboot OEM command: %s\n",
423 return EFI_NOT_FOUND
;
427 FASTBOOT_PLATFORM_PROTOCOL mPlatformProtocol
= {
428 ArmFastbootPlatformInit
,
429 ArmFastbootPlatformUnInit
,
430 ArmFastbootPlatformFlashPartition
,
431 ArmFastbootPlatformErasePartition
,
432 ArmFastbootPlatformGetVar
,
433 ArmFastbootPlatformOemCommand
438 ArmAndroidFastbootPlatformEntryPoint (
439 IN EFI_HANDLE ImageHandle
,
440 IN EFI_SYSTEM_TABLE
*SystemTable
443 return gBS
->InstallProtocolInterface (
445 &gAndroidFastbootPlatformProtocolGuid
,
446 EFI_NATIVE_INTERFACE
,