1 /** @file
2 
3   Copyright (c) 2014, ARM Ltd. All rights reserved.<BR>
4 
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
9 
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.
12 
13 **/
14 
15 /*
16   Implementation of the Android Fastboot Platform protocol, to be used by the
17   Fastboot UEFI application, for ARM Versatile Express platforms.
18 */
19 
20 #include <Protocol/AndroidFastbootPlatform.h>
21 #include <Protocol/BlockIo.h>
22 #include <Protocol/DiskIo.h>
23 
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>
30 
31 #define FLASH_DEVICE_PATH_SIZE(DevPath) ( GetDevicePathSize (DevPath) - \
32                                             sizeof (EFI_DEVICE_PATH_PROTOCOL))
33 
34 #define PARTITION_NAME_MAX_LENGTH 72/2
35 
36 #define IS_ALPHA(Char) (((Char) <= L'z' && (Char) >= L'a') || \
37                         ((Char) <= L'Z' && (Char) >= L'Z'))
38 
39 typedef struct _FASTBOOT_PARTITION_LIST {
40   LIST_ENTRY  Link;
41   CHAR16      PartitionName[PARTITION_NAME_MAX_LENGTH];
42   EFI_HANDLE  PartitionHandle;
43 } FASTBOOT_PARTITION_LIST;
44 
45 STATIC LIST_ENTRY mPartitionListHead;
46 
47 /*
48   Helper to free the partition list
49 */
50 STATIC
51 VOID
FreePartitionList(VOID)52 FreePartitionList (
53   VOID
54   )
55 {
56   FASTBOOT_PARTITION_LIST *Entry;
57   FASTBOOT_PARTITION_LIST *NextEntry;
58 
59   Entry = (FASTBOOT_PARTITION_LIST *) GetFirstNode (&mPartitionListHead);
60   while (!IsNull (&mPartitionListHead, &Entry->Link)) {
61     NextEntry = (FASTBOOT_PARTITION_LIST *) GetNextNode (&mPartitionListHead, &Entry->Link);
62 
63     RemoveEntryList (&Entry->Link);
64     FreePool (Entry);
65 
66     Entry = NextEntry;
67   }
68 }
69 /*
70   Read the PartitionName fields from the GPT partition entries, putting them
71   into an allocated array that should later be freed.
72 */
73 STATIC
74 EFI_STATUS
ReadPartitionEntries(IN EFI_BLOCK_IO_PROTOCOL * BlockIo,OUT EFI_PARTITION_ENTRY ** PartitionEntries)75 ReadPartitionEntries (
76   IN  EFI_BLOCK_IO_PROTOCOL *BlockIo,
77   OUT EFI_PARTITION_ENTRY  **PartitionEntries
78   )
79 {
80   UINTN                       EntrySize;
81   UINTN                       NumEntries;
82   UINTN                       BufferSize;
83   UINT32                      MediaId;
84   EFI_PARTITION_TABLE_HEADER *GptHeader;
85   EFI_STATUS                  Status;
86 
87   MediaId = BlockIo->Media->MediaId;
88 
89   //
90   // Read size of Partition entry and number of entries from GPT header
91   //
92 
93   GptHeader = AllocatePool (BlockIo->Media->BlockSize);
94   if (GptHeader == NULL) {
95     return EFI_OUT_OF_RESOURCES;
96   }
97 
98   Status = BlockIo->ReadBlocks (BlockIo, MediaId, 1, BlockIo->Media->BlockSize, (VOID *) GptHeader);
99   if (EFI_ERROR (Status)) {
100     return Status;
101   }
102 
103   // Check there is a GPT on the media
104   if (GptHeader->Header.Signature != EFI_PTAB_HEADER_ID ||
105       GptHeader->MyLBA != 1) {
106     DEBUG ((EFI_D_ERROR,
107       "Fastboot platform: No GPT on flash. "
108       "Fastboot on Versatile Express does not support MBR.\n"
109       ));
110     return EFI_DEVICE_ERROR;
111   }
112 
113   EntrySize = GptHeader->SizeOfPartitionEntry;
114   NumEntries = GptHeader->NumberOfPartitionEntries;
115 
116   FreePool (GptHeader);
117 
118   ASSERT (EntrySize != 0);
119   ASSERT (NumEntries != 0);
120 
121   BufferSize = ALIGN_VALUE (EntrySize * NumEntries, BlockIo->Media->BlockSize);
122   *PartitionEntries = AllocatePool (BufferSize);
123   if (PartitionEntries == NULL) {
124     return EFI_OUT_OF_RESOURCES;
125   }
126 
127   Status = BlockIo->ReadBlocks (BlockIo, MediaId, 2, BufferSize, (VOID *) *PartitionEntries);
128   if (EFI_ERROR (Status)) {
129     FreePool (PartitionEntries);
130     return Status;
131   }
132 
133   return Status;
134 }
135 
136 
137 /*
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.
142 */
143 EFI_STATUS
ArmFastbootPlatformInit(VOID)144 ArmFastbootPlatformInit (
145   VOID
146   )
147 {
148   EFI_STATUS                          Status;
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;
154   UINTN                               NumHandles;
155   EFI_HANDLE                         *AllHandles;
156   UINTN                               LoopIndex;
157   EFI_HANDLE                          FlashHandle;
158   EFI_BLOCK_IO_PROTOCOL              *FlashBlockIo;
159   EFI_PARTITION_ENTRY                *PartitionEntries;
160   FASTBOOT_PARTITION_LIST            *Entry;
161 
162   InitializeListHead (&mPartitionListHead);
163 
164   //
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.
170   //
171 
172   FlashDevicePath = ConvertTextToDevicePath ((CHAR16*)FixedPcdGetPtr (PcdAndroidFastbootNvmDevicePath));
173 
174   //
175   // Open the Disk IO protocol on the flash device - this will be used to read
176   // partition names out of the GPT entries
177   //
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
184     return EFI_SUCCESS;
185   }
186 
187   Status = gBS->OpenProtocol (
188                   FlashHandle,
189                   &gEfiBlockIoProtocolGuid,
190                   (VOID **) &FlashBlockIo,
191                   gImageHandle,
192                   NULL,
193                   EFI_OPEN_PROTOCOL_GET_PROTOCOL
194                   );
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;
198   }
199 
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
205     return EFI_SUCCESS;
206   }
207 
208   // Get every Block IO protocol instance installed in the system
209   Status = gBS->LocateHandleBuffer (
210                   ByProtocol,
211                   &gEfiBlockIoProtocolGuid,
212                   NULL,
213                   &NumHandles,
214                   &AllHandles
215                   );
216   ASSERT_EFI_ERROR (Status);
217 
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,
225                     gImageHandle,
226                     NULL,
227                     EFI_OPEN_PROTOCOL_GET_PROTOCOL
228                     );
229     ASSERT_EFI_ERROR (Status);
230 
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
234       // device itself.
235       NextNode = NextDevicePathNode (DevicePath);
236       if (IsDevicePathEndType (NextNode)) {
237         continue;
238       }
239 
240       // Assert that this device path node represents a partition.
241       ASSERT (NextNode->Type == MEDIA_DEVICE_PATH &&
242               NextNode->SubType == MEDIA_HARDDRIVE_DP);
243 
244       PartitionNode = (HARDDRIVE_DEVICE_PATH *) NextNode;
245 
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
249       //  Format")
250       ASSERT (PartitionNode->MBRType == MBR_TYPE_EFI_PARTITION_TABLE_HEADER);
251 
252       // The firmware may install a handle for "partition 0", representing the
253       // whole device. Ignore it.
254       if (PartitionNode->PartitionNumber == 0) {
255         continue;
256       }
257 
258       //
259       // Add the partition handle to the list
260       //
261 
262       // Create entry
263       Entry = AllocatePool (sizeof (FASTBOOT_PARTITION_LIST));
264       if (Entry == NULL) {
265         Status = EFI_OUT_OF_RESOURCES;
266         FreePartitionList ();
267         goto Exit;
268       }
269 
270       // Copy handle and partition name
271       Entry->PartitionHandle = AllHandles[LoopIndex];
272       StrnCpy (
273         Entry->PartitionName,
274         PartitionEntries[PartitionNode->PartitionNumber - 1].PartitionName, // Partition numbers start from 1.
275         PARTITION_NAME_MAX_LENGTH
276         );
277       InsertTailList (&mPartitionListHead, &Entry->Link);
278 
279       // Print a debug message if the partition label is empty or looks like
280       // garbage.
281       if (!IS_ALPHA (Entry->PartitionName[0])) {
282         DEBUG ((EFI_D_ERROR,
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
286           ));
287       }
288     }
289   }
290 
291 Exit:
292   FreePool (PartitionEntries);
293   FreePool (FlashDevicePath);
294   FreePool (AllHandles);
295   return Status;
296 
297 }
298 
299 VOID
ArmFastbootPlatformUnInit(VOID)300 ArmFastbootPlatformUnInit (
301   VOID
302   )
303 {
304   FreePartitionList ();
305 }
306 
307 EFI_STATUS
ArmFastbootPlatformFlashPartition(IN CHAR8 * PartitionName,IN UINTN Size,IN VOID * Image)308 ArmFastbootPlatformFlashPartition (
309   IN CHAR8  *PartitionName,
310   IN UINTN   Size,
311   IN VOID   *Image
312   )
313 {
314   EFI_STATUS               Status;
315   EFI_BLOCK_IO_PROTOCOL   *BlockIo;
316   EFI_DISK_IO_PROTOCOL    *DiskIo;
317   UINT32                   MediaId;
318   UINTN                    PartitionSize;
319   FASTBOOT_PARTITION_LIST *Entry;
320   CHAR16                   PartitionNameUnicode[60];
321   BOOLEAN                  PartitionFound;
322   SPARSE_HEADER           *SparseHeader;
323   CHUNK_HEADER            *ChunkHeader;
324   UINTN                    Offset = 0;
325   UINT32                   Chunk;
326 
327 
328   AsciiStrToUnicodeStr (PartitionName, PartitionNameUnicode);
329 
330   PartitionFound = FALSE;
331   Entry = (FASTBOOT_PARTITION_LIST *) GetFirstNode (&(mPartitionListHead));
332   while (!IsNull (&mPartitionListHead, &Entry->Link)) {
333     // Search the partition list for the partition named by PartitionName
334     if (StrCmp (Entry->PartitionName, PartitionNameUnicode) == 0) {
335       PartitionFound = TRUE;
336       break;
337     }
338 
339    Entry = (FASTBOOT_PARTITION_LIST *) GetNextNode (&mPartitionListHead, &(Entry)->Link);
340   }
341   if (!PartitionFound) {
342     return EFI_NOT_FOUND;
343   }
344 
345   Status = gBS->OpenProtocol (
346                   Entry->PartitionHandle,
347                   &gEfiBlockIoProtocolGuid,
348                   (VOID **) &BlockIo,
349                   gImageHandle,
350                   NULL,
351                   EFI_OPEN_PROTOCOL_GET_PROTOCOL
352                   );
353   if (EFI_ERROR (Status)) {
354     DEBUG ((EFI_D_ERROR, "Fastboot platform: couldn't open Block IO for flash: %r\n", Status));
355     return EFI_NOT_FOUND;
356   }
357 
358   SparseHeader=(SPARSE_HEADER *)Image;
359 
360   if (SparseHeader->Magic == SPARSE_HEADER_MAGIC) {
361     DEBUG ((EFI_D_INFO, "Sparse Magic: 0x%x Major: %d Minor: %d fhs: %d chs: %d bs: %d tbs: %d tcs: %d checksum: %d \n",
362                 SparseHeader->Magic, SparseHeader->MajorVersion, SparseHeader->MinorVersion,  SparseHeader->FileHeaderSize,
363                 SparseHeader->ChunkHeaderSize, SparseHeader->BlockSize, SparseHeader->TotalBlocks,
364                 SparseHeader->TotalChunks, SparseHeader->ImageChecksum));
365     if (SparseHeader->MajorVersion != 1) {
366         DEBUG ((EFI_D_ERROR, "Sparse image version %d.%d not supported.\n",
367                     SparseHeader->MajorVersion, SparseHeader->MinorVersion));
368         return EFI_INVALID_PARAMETER;
369     }
370 
371     Size = SparseHeader->BlockSize * SparseHeader->TotalBlocks;
372   }
373 
374   // Check image will fit on device
375   PartitionSize = (BlockIo->Media->LastBlock + 1) * BlockIo->Media->BlockSize;
376   if (PartitionSize < Size) {
377     DEBUG ((EFI_D_ERROR, "Partition not big enough.\n"));
378     DEBUG ((EFI_D_ERROR, "Partition Size:\t%d\nImage Size:\t%d\n", PartitionSize, Size));
379 
380     return EFI_VOLUME_FULL;
381   }
382 
383   MediaId = BlockIo->Media->MediaId;
384 
385   Status = gBS->OpenProtocol (
386                   Entry->PartitionHandle,
387                   &gEfiDiskIoProtocolGuid,
388                   (VOID **) &DiskIo,
389                   gImageHandle,
390                   NULL,
391                   EFI_OPEN_PROTOCOL_GET_PROTOCOL
392                   );
393   ASSERT_EFI_ERROR (Status);
394 
395   if (SparseHeader->Magic == SPARSE_HEADER_MAGIC) {
396     Image += SparseHeader->FileHeaderSize;
397     for (Chunk = 0; Chunk < SparseHeader->TotalChunks; Chunk++) {
398       UINTN WriteSize;
399       ChunkHeader = (CHUNK_HEADER *)Image;
400       DEBUG ((EFI_D_INFO, "Chunk #%d - Type: 0x%x Size: %d TotalSize: %d Offset %d\n",
401                   (Chunk+1), ChunkHeader->ChunkType, ChunkHeader->ChunkSize,
402                   ChunkHeader->TotalSize, Offset));
403       Image += sizeof(CHUNK_HEADER);
404       WriteSize=(SparseHeader->BlockSize) * ChunkHeader->ChunkSize;
405       switch (ChunkHeader->ChunkType) {
406         case CHUNK_TYPE_RAW:
407           DEBUG ((EFI_D_INFO, "Writing %d at Offset %d\n", WriteSize, Offset));
408           Status = DiskIo->WriteDisk (DiskIo, MediaId, Offset, WriteSize, Image);
409           if (EFI_ERROR (Status)) {
410             return Status;
411           }
412           Image+=WriteSize;
413           break;
414         case CHUNK_TYPE_DONT_CARE:
415           break;
416         case CHUNK_TYPE_CRC32:
417           break;
418         default:
419           DEBUG ((EFI_D_ERROR, "Unknown Chunk Type: 0x%x"));
420           return EFI_PROTOCOL_ERROR;
421       }
422       Offset += WriteSize;
423     }
424   } else {
425     Status = DiskIo->WriteDisk (DiskIo, MediaId, 0, Size, Image);
426     if (EFI_ERROR (Status)) {
427       return Status;
428     }
429   }
430 
431   BlockIo->FlushBlocks(BlockIo);
432 
433   return Status;
434 }
435 
436 EFI_STATUS
ArmFastbootPlatformErasePartition(IN CHAR8 * Partition)437 ArmFastbootPlatformErasePartition (
438   IN CHAR8 *Partition
439   )
440 {
441   return EFI_SUCCESS;
442 }
443 
444 EFI_STATUS
ArmFastbootPlatformGetVar(IN CHAR8 * Name,OUT CHAR8 * Value)445 ArmFastbootPlatformGetVar (
446   IN  CHAR8   *Name,
447   OUT CHAR8   *Value
448   )
449 {
450   if (AsciiStrCmp (Name, "product")) {
451     AsciiStrCpy (Value, FixedPcdGetPtr (PcdFirmwareVendor));
452   } else {
453     *Value = '\0';
454   }
455   return EFI_SUCCESS;
456 }
457 
458 EFI_STATUS
ArmFastbootPlatformOemCommand(IN CHAR8 * Command)459 ArmFastbootPlatformOemCommand (
460   IN  CHAR8   *Command
461   )
462 {
463   CHAR16 CommandUnicode[65];
464 
465   AsciiStrToUnicodeStr (Command, CommandUnicode);
466 
467   if (AsciiStrCmp (Command, "Demonstrate") == 0) {
468     DEBUG ((EFI_D_ERROR, "ARM OEM Fastboot command 'Demonstrate' received.\n"));
469     return EFI_SUCCESS;
470   } else {
471     DEBUG ((EFI_D_ERROR,
472       "VExpress: Unrecognised Fastboot OEM command: %s\n",
473       CommandUnicode
474       ));
475     return EFI_NOT_FOUND;
476   }
477 }
478 
479 FASTBOOT_PLATFORM_PROTOCOL mPlatformProtocol = {
480   ArmFastbootPlatformInit,
481   ArmFastbootPlatformUnInit,
482   ArmFastbootPlatformFlashPartition,
483   ArmFastbootPlatformErasePartition,
484   ArmFastbootPlatformGetVar,
485   ArmFastbootPlatformOemCommand
486 };
487 
488 EFI_STATUS
489 EFIAPI
ArmAndroidFastbootPlatformEntryPoint(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)490 ArmAndroidFastbootPlatformEntryPoint (
491   IN EFI_HANDLE                            ImageHandle,
492   IN EFI_SYSTEM_TABLE                      *SystemTable
493   )
494 {
495   return gBS->InstallProtocolInterface (
496                 &ImageHandle,
497                 &gAndroidFastbootPlatformProtocolGuid,
498                 EFI_NATIVE_INTERFACE,
499                 &mPlatformProtocol
500                 );
501 }
502