• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2 
3   Copyright (c) 2014, ARM Ltd. All rights reserved.<BR>
4   Copyright (c) 2016, Linaro Ltd. All rights reserved.<BR>
5 
6   This program and the accompanying materials
7   are licensed and made available under the terms and conditions of the BSD License
8   which accompanies this distribution.  The full text of the license may be found at
9   http://opensource.org/licenses/bsd-license.php
10 
11   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13 
14 **/
15 
16 /*
17   Implementation of the Android Fastboot Platform protocol, to be used by the
18   Fastboot UEFI application, for ARM Versatile Express platforms.
19 */
20 
21 #include <Protocol/AndroidFastbootPlatform.h>
22 #include <Protocol/BlockIo.h>
23 #include <Protocol/DiskIo.h>
24 
25 #include <Library/BaseLib.h>
26 #include <Library/BaseMemoryLib.h>
27 #include <Library/DebugLib.h>
28 #include <Library/DevicePathLib.h>
29 #include <Library/MemoryAllocationLib.h>
30 #include <Library/UefiBootServicesTableLib.h>
31 
32 #define FLASH_DEVICE_PATH_SIZE(DevPath) ( GetDevicePathSize (DevPath) - \
33                                             sizeof (EFI_DEVICE_PATH_PROTOCOL))
34 
35 #define PARTITION_NAME_MAX_LENGTH 72/2
36 
37 #define IS_ALPHA(Char) (((Char) <= L'z' && (Char) >= L'a') || \
38                         ((Char) <= L'Z' && (Char) >= L'Z'))
39 
40 typedef struct _FASTBOOT_PARTITION_LIST {
41   LIST_ENTRY  Link;
42   CHAR16      PartitionName[PARTITION_NAME_MAX_LENGTH];
43   EFI_HANDLE  PartitionHandle;
44 } FASTBOOT_PARTITION_LIST;
45 
46 STATIC LIST_ENTRY mPartitionListHead;
47 
48 /*
49   Helper to free the partition list
50 */
51 STATIC
52 VOID
FreePartitionList(VOID)53 FreePartitionList (
54   VOID
55   )
56 {
57   FASTBOOT_PARTITION_LIST *Entry;
58   FASTBOOT_PARTITION_LIST *NextEntry;
59 
60   Entry = (FASTBOOT_PARTITION_LIST *) GetFirstNode (&mPartitionListHead);
61   while (!IsNull (&mPartitionListHead, &Entry->Link)) {
62     NextEntry = (FASTBOOT_PARTITION_LIST *) GetNextNode (&mPartitionListHead, &Entry->Link);
63 
64     RemoveEntryList (&Entry->Link);
65     FreePool (Entry);
66 
67     Entry = NextEntry;
68   }
69 }
70 /*
71   Read the PartitionName fields from the GPT partition entries, putting them
72   into an allocated array that should later be freed.
73 */
74 STATIC
75 EFI_STATUS
ReadPartitionEntries(IN EFI_BLOCK_IO_PROTOCOL * BlockIo,OUT EFI_PARTITION_ENTRY ** PartitionEntries)76 ReadPartitionEntries (
77   IN  EFI_BLOCK_IO_PROTOCOL *BlockIo,
78   OUT EFI_PARTITION_ENTRY  **PartitionEntries
79   )
80 {
81   UINTN                       EntrySize;
82   UINTN                       NumEntries;
83   UINTN                       BufferSize;
84   UINT32                      MediaId;
85   EFI_PARTITION_TABLE_HEADER *GptHeader;
86   EFI_STATUS                  Status;
87 
88   MediaId = BlockIo->Media->MediaId;
89 
90   //
91   // Read size of Partition entry and number of entries from GPT header
92   //
93 
94   GptHeader = AllocatePool (BlockIo->Media->BlockSize);
95   if (GptHeader == NULL) {
96     return EFI_OUT_OF_RESOURCES;
97   }
98 
99   Status = BlockIo->ReadBlocks (BlockIo, MediaId, 1, BlockIo->Media->BlockSize, (VOID *) GptHeader);
100   if (EFI_ERROR (Status)) {
101     return Status;
102   }
103 
104   // Check there is a GPT on the media
105   if (GptHeader->Header.Signature != EFI_PTAB_HEADER_ID ||
106       GptHeader->MyLBA != 1) {
107     DEBUG ((EFI_D_ERROR,
108       "Fastboot platform: No GPT on flash. "
109       "Fastboot on Versatile Express does not support MBR.\n"
110       ));
111     return EFI_DEVICE_ERROR;
112   }
113 
114   EntrySize = GptHeader->SizeOfPartitionEntry;
115   NumEntries = GptHeader->NumberOfPartitionEntries;
116 
117   FreePool (GptHeader);
118 
119   ASSERT (EntrySize != 0);
120   ASSERT (NumEntries != 0);
121 
122   BufferSize = ALIGN_VALUE (EntrySize * NumEntries, BlockIo->Media->BlockSize);
123   *PartitionEntries = AllocatePool (BufferSize);
124   if (PartitionEntries == NULL) {
125     return EFI_OUT_OF_RESOURCES;
126   }
127 
128   Status = BlockIo->ReadBlocks (BlockIo, MediaId, 2, BufferSize, (VOID *) *PartitionEntries);
129   if (EFI_ERROR (Status)) {
130     FreePool (PartitionEntries);
131     return Status;
132   }
133 
134   return Status;
135 }
136 
137 
138 /*
139   Do any initialisation that needs to be done in order to be able to respond to
140   commands.
141 
142   @retval EFI_SUCCESS   Initialised successfully.
143   @retval !EFI_SUCCESS  Error in initialisation.
144 */
145 STATIC
146 EFI_STATUS
ArmFastbootPlatformInit(VOID)147 ArmFastbootPlatformInit (
148   VOID
149   )
150 {
151   EFI_STATUS                          Status;
152   EFI_DEVICE_PATH_PROTOCOL           *FlashDevicePath;
153   EFI_DEVICE_PATH_PROTOCOL           *FlashDevicePathDup;
154   EFI_DEVICE_PATH_PROTOCOL           *DevicePath;
155   EFI_DEVICE_PATH_PROTOCOL           *NextNode;
156   HARDDRIVE_DEVICE_PATH              *PartitionNode;
157   UINTN                               NumHandles;
158   EFI_HANDLE                         *AllHandles;
159   UINTN                               LoopIndex;
160   EFI_HANDLE                          FlashHandle;
161   EFI_BLOCK_IO_PROTOCOL              *FlashBlockIo;
162   EFI_PARTITION_ENTRY                *PartitionEntries;
163   FASTBOOT_PARTITION_LIST            *Entry;
164 
165   InitializeListHead (&mPartitionListHead);
166 
167   //
168   // Get EFI_HANDLES for all the partitions on the block devices pointed to by
169   // PcdFastbootFlashDevicePath, also saving their GPT partition labels.
170   // We will use these labels as the key in ArmFastbootPlatformFlashPartition.
171   // There's no way to find all of a device's children, so we get every handle
172   // in the system supporting EFI_BLOCK_IO_PROTOCOL and then filter out ones
173   // that don't represent partitions on the flash device.
174   //
175 
176   FlashDevicePath = ConvertTextToDevicePath ((CHAR16*)FixedPcdGetPtr (PcdAndroidFastbootNvmDevicePath));
177 
178   //
179   // Open the Disk IO protocol on the flash device - this will be used to read
180   // partition names out of the GPT entries
181   //
182   // Create another device path pointer because LocateDevicePath will modify it.
183   FlashDevicePathDup = FlashDevicePath;
184   Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &FlashDevicePathDup, &FlashHandle);
185   if (EFI_ERROR (Status)) {
186     DEBUG ((EFI_D_ERROR, "Warning: Couldn't locate Android NVM device (status: %r)\n", Status));
187     // Failing to locate partitions should not prevent to do other Android FastBoot actions
188     return EFI_SUCCESS;
189   }
190 
191   Status = gBS->OpenProtocol (
192                   FlashHandle,
193                   &gEfiBlockIoProtocolGuid,
194                   (VOID **) &FlashBlockIo,
195                   gImageHandle,
196                   NULL,
197                   EFI_OPEN_PROTOCOL_GET_PROTOCOL
198                   );
199   if (EFI_ERROR (Status)) {
200     DEBUG ((EFI_D_ERROR, "Fastboot platform: Couldn't open Android NVM device (status: %r)\n", Status));
201     return EFI_DEVICE_ERROR;
202   }
203 
204   // Read the GPT partition entry array into memory so we can get the partition names
205   Status = ReadPartitionEntries (FlashBlockIo, &PartitionEntries);
206   if (EFI_ERROR (Status)) {
207     DEBUG ((EFI_D_ERROR, "Warning: Failed to read partitions from Android NVM device (status: %r)\n", Status));
208     // Failing to locate partitions should not prevent to do other Android FastBoot actions
209     return EFI_SUCCESS;
210   }
211 
212   // Get every Block IO protocol instance installed in the system
213   Status = gBS->LocateHandleBuffer (
214                   ByProtocol,
215                   &gEfiBlockIoProtocolGuid,
216                   NULL,
217                   &NumHandles,
218                   &AllHandles
219                   );
220   ASSERT_EFI_ERROR (Status);
221 
222   // Filter out handles that aren't children of the flash device
223   for (LoopIndex = 0; LoopIndex < NumHandles; LoopIndex++) {
224     // Get the device path for the handle
225     Status = gBS->OpenProtocol (
226                     AllHandles[LoopIndex],
227                     &gEfiDevicePathProtocolGuid,
228                     (VOID **) &DevicePath,
229                     gImageHandle,
230                     NULL,
231                     EFI_OPEN_PROTOCOL_GET_PROTOCOL
232                     );
233     ASSERT_EFI_ERROR (Status);
234 
235     // Check if it is a sub-device of the flash device
236     if (!CompareMem (DevicePath, FlashDevicePath, FLASH_DEVICE_PATH_SIZE (FlashDevicePath))) {
237       // Device path starts with path of flash device. Check it isn't the flash
238       // device itself.
239       NextNode = NextDevicePathNode (DevicePath);
240       if (IsDevicePathEndType (NextNode)) {
241         continue;
242       }
243 
244       // Assert that this device path node represents a partition.
245       ASSERT (NextNode->Type == MEDIA_DEVICE_PATH &&
246               NextNode->SubType == MEDIA_HARDDRIVE_DP);
247 
248       PartitionNode = (HARDDRIVE_DEVICE_PATH *) NextNode;
249 
250       // Assert that the partition type is GPT. ReadPartitionEntries checks for
251       // presence of a GPT, so we should never find MBR partitions.
252       // ("MBRType" is a misnomer - this field is actually called "Partition
253       //  Format")
254       ASSERT (PartitionNode->MBRType == MBR_TYPE_EFI_PARTITION_TABLE_HEADER);
255 
256       // The firmware may install a handle for "partition 0", representing the
257       // whole device. Ignore it.
258       if (PartitionNode->PartitionNumber == 0) {
259         continue;
260       }
261 
262       //
263       // Add the partition handle to the list
264       //
265 
266       // Create entry
267       Entry = AllocatePool (sizeof (FASTBOOT_PARTITION_LIST));
268       if (Entry == NULL) {
269         Status = EFI_OUT_OF_RESOURCES;
270         FreePartitionList ();
271         goto Exit;
272       }
273 
274       // Copy handle and partition name
275       Entry->PartitionHandle = AllHandles[LoopIndex];
276       CopyMem (
277         Entry->PartitionName,
278         PartitionEntries[PartitionNode->PartitionNumber - 1].PartitionName, // Partition numbers start from 1.
279         PARTITION_NAME_MAX_LENGTH
280         );
281       InsertTailList (&mPartitionListHead, &Entry->Link);
282 
283       // Print a debug message if the partition label is empty or looks like
284       // garbage.
285       if (!IS_ALPHA (Entry->PartitionName[0])) {
286         DEBUG ((EFI_D_ERROR,
287           "Warning: Partition %d doesn't seem to have a GPT partition label. "
288           "You won't be able to flash it with Fastboot.\n",
289           PartitionNode->PartitionNumber
290           ));
291       }
292     }
293   }
294 
295 Exit:
296   FreePool (PartitionEntries);
297   FreePool (FlashDevicePath);
298   FreePool (AllHandles);
299   return Status;
300 
301 }
302 
303 /*
304   To be called when Fastboot is finished and we aren't rebooting or booting an
305   image. Undo initialisation, free resrouces.
306 */
307 STATIC
308 VOID
ArmFastbootPlatformUnInit(VOID)309 ArmFastbootPlatformUnInit (
310   VOID
311   )
312 {
313   FreePartitionList ();
314 }
315 
316 /*
317   Flash the partition named (according to a platform-specific scheme)
318   PartitionName, with the image pointed to by Buffer, whose size is BufferSize.
319 
320   @param[in] PartitionName  Null-terminated name of partition to write.
321   @param[in] BufferSize     Size of Buffer in byets.
322   @param[in] Buffer         Data to write to partition.
323 
324   @retval EFI_NOT_FOUND     No such partition.
325   @retval EFI_DEVICE_ERROR  Flashing failed.
326 */
327 STATIC
328 EFI_STATUS
ArmFastbootPlatformFlashPartition(IN CHAR8 * PartitionName,IN UINTN Size,IN VOID * Image)329 ArmFastbootPlatformFlashPartition (
330   IN CHAR8  *PartitionName,
331   IN UINTN   Size,
332   IN VOID   *Image
333   )
334 {
335   EFI_STATUS               Status;
336   EFI_BLOCK_IO_PROTOCOL   *BlockIo;
337   EFI_DISK_IO_PROTOCOL    *DiskIo;
338   UINT32                   MediaId;
339   UINTN                    PartitionSize;
340   FASTBOOT_PARTITION_LIST *Entry;
341   CHAR16                   PartitionNameUnicode[60];
342   BOOLEAN                  PartitionFound;
343 
344   AsciiStrToUnicodeStrS (PartitionName, PartitionNameUnicode,
345     ARRAY_SIZE (PartitionNameUnicode));
346 
347   PartitionFound = FALSE;
348   Entry = (FASTBOOT_PARTITION_LIST *) GetFirstNode (&(mPartitionListHead));
349   while (!IsNull (&mPartitionListHead, &Entry->Link)) {
350     // Search the partition list for the partition named by PartitionName
351     if (StrCmp (Entry->PartitionName, PartitionNameUnicode) == 0) {
352       PartitionFound = TRUE;
353       break;
354     }
355 
356    Entry = (FASTBOOT_PARTITION_LIST *) GetNextNode (&mPartitionListHead, &(Entry)->Link);
357   }
358   if (!PartitionFound) {
359     return EFI_NOT_FOUND;
360   }
361 
362   Status = gBS->OpenProtocol (
363                   Entry->PartitionHandle,
364                   &gEfiBlockIoProtocolGuid,
365                   (VOID **) &BlockIo,
366                   gImageHandle,
367                   NULL,
368                   EFI_OPEN_PROTOCOL_GET_PROTOCOL
369                   );
370   if (EFI_ERROR (Status)) {
371     DEBUG ((EFI_D_ERROR, "Fastboot platform: couldn't open Block IO for flash: %r\n", Status));
372     return EFI_NOT_FOUND;
373   }
374 
375   // Check image will fit on device
376   PartitionSize = (BlockIo->Media->LastBlock + 1) * BlockIo->Media->BlockSize;
377   if (PartitionSize < Size) {
378     DEBUG ((EFI_D_ERROR, "Partition not big enough.\n"));
379     DEBUG ((EFI_D_ERROR, "Partition Size:\t%d\nImage Size:\t%d\n", PartitionSize, Size));
380 
381     return EFI_VOLUME_FULL;
382   }
383 
384   MediaId = BlockIo->Media->MediaId;
385 
386   Status = gBS->OpenProtocol (
387                   Entry->PartitionHandle,
388                   &gEfiDiskIoProtocolGuid,
389                   (VOID **) &DiskIo,
390                   gImageHandle,
391                   NULL,
392                   EFI_OPEN_PROTOCOL_GET_PROTOCOL
393                   );
394   ASSERT_EFI_ERROR (Status);
395 
396   Status = DiskIo->WriteDisk (DiskIo, MediaId, 0, Size, Image);
397   if (EFI_ERROR (Status)) {
398     return Status;
399   }
400 
401   BlockIo->FlushBlocks(BlockIo);
402 
403   return Status;
404 }
405 
406 /*
407   Erase the partition named PartitionName.
408 
409   @param[in] PartitionName  Null-terminated name of partition to erase.
410 
411   @retval EFI_NOT_FOUND     No such partition.
412   @retval EFI_DEVICE_ERROR  Erasing failed.
413 */
414 STATIC
415 EFI_STATUS
ArmFastbootPlatformErasePartition(IN CHAR8 * Partition)416 ArmFastbootPlatformErasePartition (
417   IN CHAR8 *Partition
418   )
419 {
420   return EFI_SUCCESS;
421 }
422 
423 /*
424   If the variable referred to by Name exists, copy it (as a null-terminated
425   string) into Value. If it doesn't exist, put the Empty string in Value.
426 
427   Variable names and values may not be larger than 60 bytes, excluding the
428   terminal null character. This is a limitation of the Fastboot protocol.
429 
430   The Fastboot application will handle platform-nonspecific variables
431   (Currently "version" is the only one of these.)
432 
433   @param[in]  Name   Null-terminated name of Fastboot variable to retrieve.
434   @param[out] Value  Caller-allocated buffer for null-terminated value of
435                      variable.
436 
437   @retval EFI_SUCCESS       The variable was retrieved, or it doesn't exist.
438   @retval EFI_DEVICE_ERROR  There was an error looking up the variable. This
439                             does _not_ include the variable not existing.
440 */
441 STATIC
442 EFI_STATUS
ArmFastbootPlatformGetVar(IN CHAR8 * Name,OUT CHAR8 * Value)443 ArmFastbootPlatformGetVar (
444   IN  CHAR8   *Name,
445   OUT CHAR8   *Value
446   )
447 {
448   if (AsciiStrCmp (Name, "product")) {
449     AsciiStrCpyS (Value, 61, FixedPcdGetPtr (PcdFirmwareVendor));
450   } else {
451     *Value = '\0';
452   }
453   return EFI_SUCCESS;
454 }
455 
456 /*
457   React to an OEM-specific command.
458 
459   Future versions of this function might want to allow the platform to do some
460   extra communication with the host. A way to do this would be to add a function
461   to the FASTBOOT_TRANSPORT_PROTOCOL that allows the implementation of
462   DoOemCommand to replace the ReceiveEvent with its own, and to restore the old
463   one when it's finished.
464 
465   However at the moment although the specification allows it, the AOSP fastboot
466   host application doesn't handle receiving any data from the client, and it
467   doesn't support a data phase for OEM commands.
468 
469   @param[in] Command    Null-terminated command string.
470 
471   @retval EFI_SUCCESS       The command executed successfully.
472   @retval EFI_NOT_FOUND     The command wasn't recognised.
473   @retval EFI_DEVICE_ERROR  There was an error executing the command.
474 */
475 STATIC
476 EFI_STATUS
ArmFastbootPlatformOemCommand(IN CHAR8 * Command)477 ArmFastbootPlatformOemCommand (
478   IN  CHAR8   *Command
479   )
480 {
481   CHAR16 CommandUnicode[65];
482 
483   AsciiStrToUnicodeStrS (Command, CommandUnicode, ARRAY_SIZE (CommandUnicode));
484 
485   if (AsciiStrCmp (Command, "Demonstrate") == 0) {
486     DEBUG ((EFI_D_ERROR, "ARM OEM Fastboot command 'Demonstrate' received.\n"));
487     return EFI_SUCCESS;
488   } else {
489     DEBUG ((EFI_D_ERROR,
490       "VExpress: Unrecognised Fastboot OEM command: %s\n",
491       CommandUnicode
492       ));
493     return EFI_NOT_FOUND;
494   }
495 }
496 
497 STATIC FASTBOOT_PLATFORM_PROTOCOL mPlatformProtocol = {
498   ArmFastbootPlatformInit,
499   ArmFastbootPlatformUnInit,
500   ArmFastbootPlatformFlashPartition,
501   ArmFastbootPlatformErasePartition,
502   ArmFastbootPlatformGetVar,
503   ArmFastbootPlatformOemCommand
504 };
505 
506 EFI_STATUS
507 EFIAPI
ArmAndroidFastbootPlatformEntryPoint(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)508 ArmAndroidFastbootPlatformEntryPoint (
509   IN EFI_HANDLE                            ImageHandle,
510   IN EFI_SYSTEM_TABLE                      *SystemTable
511   )
512 {
513   return gBS->InstallProtocolInterface (
514                 &ImageHandle,
515                 &gAndroidFastbootPlatformProtocolGuid,
516                 EFI_NATIVE_INTERFACE,
517                 &mPlatformProtocol
518                 );
519 }
520