Home | History | Annotate | Download | only in HiKeyFastbootDxe
      1 /** @file
      2 
      3   Copyright (c) 2014, ARM Ltd. All rights reserved.<BR>
      4   Copyright (c) 2015, Linaro Ltd. All rights reserved.
      5   Copyright (c) 2015, Hisilicon Ltd. All rights reserved.
      6 
      7   This program and the accompanying materials
      8   are licensed and made available under the terms and conditions of the BSD License
      9   which accompanies this distribution.  The full text of the license may be found at
     10   http://opensource.org/licenses/bsd-license.php
     11 
     12   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     13   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     14 
     15 **/
     16 
     17 /*
     18   Implementation of the Android Fastboot Platform protocol, to be used by the
     19   Fastboot UEFI application, for Hisilicon HiKey platform.
     20 */
     21 
     22 #include <Protocol/AndroidFastbootPlatform.h>
     23 #include <Protocol/BlockIo.h>
     24 #include <Protocol/DiskIo.h>
     25 #include <Protocol/EraseBlock.h>
     26 #include <Protocol/SimpleTextOut.h>
     27 
     28 #include <Library/BaseLib.h>
     29 #include <Library/BaseMemoryLib.h>
     30 #include <Library/DebugLib.h>
     31 #include <Library/DevicePathLib.h>
     32 #include <Library/MemoryAllocationLib.h>
     33 #include <Library/UefiBootServicesTableLib.h>
     34 #include <Library/UefiRuntimeServicesTableLib.h>
     35 #include <Library/PrintLib.h>
     36 #include <Library/TimerLib.h>
     37 
     38 #include <Guid/HiKeyVariable.h>
     39 
     40 #define FLASH_DEVICE_PATH_SIZE(DevPath) ( GetDevicePathSize (DevPath) - \
     41                                             sizeof (EFI_DEVICE_PATH_PROTOCOL))
     42 
     43 #define PARTITION_NAME_MAX_LENGTH 72/2
     44 
     45 #define IS_ALPHA(Char) (((Char) <= L'z' && (Char) >= L'a') || \
     46                         ((Char) <= L'Z' && (Char) >= L'Z'))
     47 #define IS_HEXCHAR(Char) (((Char) <= L'9' && (Char) >= L'0') || \
     48                           IS_ALPHA(Char))
     49 
     50 #define SERIAL_NUMBER_LENGTH      16
     51 #define BOOT_DEVICE_LENGTH        16
     52 
     53 #define HIKEY_ERASE_SIZE          (16 * 1024 * 1024)
     54 #define HIKEY_ERASE_BLOCKS        (HIKEY_ERASE_SIZE / EFI_PAGE_SIZE)
     55 
     56 typedef struct _FASTBOOT_PARTITION_LIST {
     57   LIST_ENTRY  Link;
     58   CHAR16      PartitionName[PARTITION_NAME_MAX_LENGTH];
     59   EFI_HANDLE  PartitionHandle;
     60   EFI_LBA     Lba;
     61 } FASTBOOT_PARTITION_LIST;
     62 
     63 STATIC LIST_ENTRY mPartitionListHead;
     64 
     65 STATIC EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *mTextOut;
     66 
     67 /*
     68   Helper to free the partition list
     69 */
     70 STATIC
     71 VOID
     72 FreePartitionList (
     73   VOID
     74   )
     75 {
     76   FASTBOOT_PARTITION_LIST *Entry;
     77   FASTBOOT_PARTITION_LIST *NextEntry;
     78 
     79   Entry = (FASTBOOT_PARTITION_LIST *) GetFirstNode (&mPartitionListHead);
     80   while (!IsNull (&mPartitionListHead, &Entry->Link)) {
     81     NextEntry = (FASTBOOT_PARTITION_LIST *) GetNextNode (&mPartitionListHead, &Entry->Link);
     82 
     83     RemoveEntryList (&Entry->Link);
     84     FreePool (Entry);
     85 
     86     Entry = NextEntry;
     87   }
     88 }
     89 /*
     90   Read the PartitionName fields from the GPT partition entries, putting them
     91   into an allocated array that should later be freed.
     92 */
     93 STATIC
     94 EFI_STATUS
     95 ReadPartitionEntries (
     96   IN  EFI_BLOCK_IO_PROTOCOL *BlockIo,
     97   OUT EFI_PARTITION_ENTRY  **PartitionEntries
     98   )
     99 {
    100   UINTN                       EntrySize;
    101   UINTN                       NumEntries;
    102   UINTN                       BufferSize;
    103   UINT32                      MediaId;
    104   EFI_PARTITION_TABLE_HEADER *GptHeader;
    105   EFI_STATUS                  Status;
    106 
    107   MediaId = BlockIo->Media->MediaId;
    108 
    109   //
    110   // Read size of Partition entry and number of entries from GPT header
    111   //
    112 
    113   GptHeader = AllocatePool (BlockIo->Media->BlockSize);
    114   if (GptHeader == NULL) {
    115     return EFI_OUT_OF_RESOURCES;
    116   }
    117 
    118   Status = BlockIo->ReadBlocks (BlockIo, MediaId, 1, BlockIo->Media->BlockSize, (VOID *) GptHeader);
    119   if (EFI_ERROR (Status)) {
    120     return Status;
    121   }
    122 
    123   // Check there is a GPT on the media
    124   if (GptHeader->Header.Signature != EFI_PTAB_HEADER_ID ||
    125       GptHeader->MyLBA != 1) {
    126     DEBUG ((EFI_D_ERROR,
    127       "Fastboot platform: No GPT on flash. "
    128       "Fastboot on Versatile Express does not support MBR.\n"
    129       ));
    130     return EFI_DEVICE_ERROR;
    131   }
    132 
    133   EntrySize = GptHeader->SizeOfPartitionEntry;
    134   NumEntries = GptHeader->NumberOfPartitionEntries;
    135 
    136   FreePool (GptHeader);
    137 
    138   ASSERT (EntrySize != 0);
    139   ASSERT (NumEntries != 0);
    140 
    141   BufferSize = ALIGN_VALUE (EntrySize * NumEntries, BlockIo->Media->BlockSize);
    142   *PartitionEntries = AllocatePool (BufferSize);
    143   if (PartitionEntries == NULL) {
    144     return EFI_OUT_OF_RESOURCES;
    145   }
    146 
    147   Status = BlockIo->ReadBlocks (BlockIo, MediaId, 2, BufferSize, (VOID *) *PartitionEntries);
    148   if (EFI_ERROR (Status)) {
    149     FreePool (PartitionEntries);
    150     return Status;
    151   }
    152 
    153   return Status;
    154 }
    155 
    156 
    157 /*
    158   Initialise: Open the Android NVM device and find the partitions on it. Save them in
    159   a list along with the "PartitionName" fields for their GPT entries.
    160   We will use these partition names as the key in
    161   HiKeyFastbootPlatformFlashPartition.
    162 */
    163 EFI_STATUS
    164 HiKeyFastbootPlatformInit (
    165   VOID
    166   )
    167 {
    168   EFI_STATUS                          Status;
    169   EFI_DEVICE_PATH_PROTOCOL           *FlashDevicePath;
    170   EFI_DEVICE_PATH_PROTOCOL           *FlashDevicePathDup;
    171   EFI_DEVICE_PATH_PROTOCOL           *DevicePath;
    172   EFI_DEVICE_PATH_PROTOCOL           *NextNode;
    173   HARDDRIVE_DEVICE_PATH              *PartitionNode;
    174   UINTN                               NumHandles;
    175   EFI_HANDLE                         *AllHandles;
    176   UINTN                               LoopIndex;
    177   EFI_HANDLE                          FlashHandle;
    178   EFI_BLOCK_IO_PROTOCOL              *FlashBlockIo;
    179   EFI_PARTITION_ENTRY                *PartitionEntries;
    180   FASTBOOT_PARTITION_LIST            *Entry;
    181 
    182   InitializeListHead (&mPartitionListHead);
    183 
    184   Status = gBS->LocateProtocol (&gEfiSimpleTextOutProtocolGuid, NULL, (VOID **) &mTextOut);
    185   if (EFI_ERROR (Status)) {
    186     DEBUG ((EFI_D_ERROR,
    187       "Fastboot platform: Couldn't open Text Output Protocol: %r\n", Status
    188       ));
    189     return Status;
    190   }
    191 
    192   //
    193   // Get EFI_HANDLES for all the partitions on the block devices pointed to by
    194   // PcdFastbootFlashDevicePath, also saving their GPT partition labels.
    195   // There's no way to find all of a device's children, so we get every handle
    196   // in the system supporting EFI_BLOCK_IO_PROTOCOL and then filter out ones
    197   // that don't represent partitions on the flash device.
    198   //
    199 
    200   FlashDevicePath = ConvertTextToDevicePath ((CHAR16*)FixedPcdGetPtr (PcdAndroidFastbootNvmDevicePath));
    201 
    202   //
    203   // Open the Disk IO protocol on the flash device - this will be used to read
    204   // partition names out of the GPT entries
    205   //
    206   // Create another device path pointer because LocateDevicePath will modify it.
    207   FlashDevicePathDup = FlashDevicePath;
    208   Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &FlashDevicePathDup, &FlashHandle);
    209   if (EFI_ERROR (Status)) {
    210     DEBUG ((EFI_D_ERROR, "Warning: Couldn't locate Android NVM device (status: %r)\n", Status));
    211     // Failing to locate partitions should not prevent to do other Android FastBoot actions
    212     return EFI_SUCCESS;
    213   }
    214 
    215   Status = gBS->OpenProtocol (
    216                   FlashHandle,
    217                   &gEfiBlockIoProtocolGuid,
    218                   (VOID **) &FlashBlockIo,
    219                   gImageHandle,
    220                   NULL,
    221                   EFI_OPEN_PROTOCOL_GET_PROTOCOL
    222                   );
    223   if (EFI_ERROR (Status)) {
    224     DEBUG ((EFI_D_ERROR, "Fastboot platform: Couldn't open Android NVM device (status: %r)\n", Status));
    225     return EFI_DEVICE_ERROR;
    226   }
    227 
    228   // Read the GPT partition entry array into memory so we can get the partition names
    229   Status = ReadPartitionEntries (FlashBlockIo, &PartitionEntries);
    230   if (EFI_ERROR (Status)) {
    231     DEBUG ((EFI_D_ERROR, "Warning: Failed to read partitions from Android NVM device (status: %r)\n", Status));
    232     // Failing to locate partitions should not prevent to do other Android FastBoot actions
    233     return EFI_SUCCESS;
    234   }
    235 
    236   // Get every Block IO protocol instance installed in the system
    237   Status = gBS->LocateHandleBuffer (
    238                   ByProtocol,
    239                   &gEfiBlockIoProtocolGuid,
    240                   NULL,
    241                   &NumHandles,
    242                   &AllHandles
    243                   );
    244   ASSERT_EFI_ERROR (Status);
    245 
    246   // Filter out handles that aren't children of the flash device
    247   for (LoopIndex = 0; LoopIndex < NumHandles; LoopIndex++) {
    248     // Get the device path for the handle
    249     Status = gBS->OpenProtocol (
    250                     AllHandles[LoopIndex],
    251                     &gEfiDevicePathProtocolGuid,
    252                     (VOID **) &DevicePath,
    253                     gImageHandle,
    254                     NULL,
    255                     EFI_OPEN_PROTOCOL_GET_PROTOCOL
    256                     );
    257     ASSERT_EFI_ERROR (Status);
    258 
    259     // Check if it is a sub-device of the flash device
    260     if (!CompareMem (DevicePath, FlashDevicePath, FLASH_DEVICE_PATH_SIZE (FlashDevicePath))) {
    261       // Device path starts with path of flash device. Check it isn't the flash
    262       // device itself.
    263       NextNode = NextDevicePathNode (DevicePath);
    264       if (IsDevicePathEndType (NextNode)) {
    265         // Create entry
    266         Entry = AllocatePool (sizeof (FASTBOOT_PARTITION_LIST));
    267         if (Entry == NULL) {
    268           Status = EFI_OUT_OF_RESOURCES;
    269           FreePartitionList ();
    270           goto Exit;
    271         }
    272 
    273         // Copy handle and partition name
    274         Entry->PartitionHandle = AllHandles[LoopIndex];
    275         StrCpy (Entry->PartitionName, L"ptable");
    276         InsertTailList (&mPartitionListHead, &Entry->Link);
    277         continue;
    278       }
    279 
    280       // Assert that this device path node represents a partition.
    281       ASSERT (NextNode->Type == MEDIA_DEVICE_PATH &&
    282               NextNode->SubType == MEDIA_HARDDRIVE_DP);
    283 
    284       PartitionNode = (HARDDRIVE_DEVICE_PATH *) NextNode;
    285 
    286       // Assert that the partition type is GPT. ReadPartitionEntries checks for
    287       // presence of a GPT, so we should never find MBR partitions.
    288       // ("MBRType" is a misnomer - this field is actually called "Partition
    289       //  Format")
    290       ASSERT (PartitionNode->MBRType == MBR_TYPE_EFI_PARTITION_TABLE_HEADER);
    291 
    292       // The firmware may install a handle for "partition 0", representing the
    293       // whole device. Ignore it.
    294       if (PartitionNode->PartitionNumber == 0) {
    295         continue;
    296       }
    297 
    298       //
    299       // Add the partition handle to the list
    300       //
    301 
    302       // Create entry
    303       Entry = AllocatePool (sizeof (FASTBOOT_PARTITION_LIST));
    304       if (Entry == NULL) {
    305         Status = EFI_OUT_OF_RESOURCES;
    306         FreePartitionList ();
    307         goto Exit;
    308       }
    309 
    310       // Copy handle and partition name
    311       Entry->PartitionHandle = AllHandles[LoopIndex];
    312       StrnCpy (
    313         Entry->PartitionName,
    314         PartitionEntries[PartitionNode->PartitionNumber - 1].PartitionName, // Partition numbers start from 1.
    315         PARTITION_NAME_MAX_LENGTH
    316         );
    317       Entry->Lba = PartitionEntries[PartitionNode->PartitionNumber - 1].StartingLBA;
    318       InsertTailList (&mPartitionListHead, &Entry->Link);
    319 
    320       // Print a debug message if the partition label is empty or looks like
    321       // garbage.
    322       if (!IS_ALPHA (Entry->PartitionName[0])) {
    323         DEBUG ((EFI_D_ERROR,
    324           "Warning: Partition %d doesn't seem to have a GPT partition label. "
    325           "You won't be able to flash it with Fastboot.\n",
    326           PartitionNode->PartitionNumber
    327           ));
    328       }
    329     }
    330   }
    331 
    332 Exit:
    333   FreePool (PartitionEntries);
    334   FreePool (FlashDevicePath);
    335   FreePool (AllHandles);
    336   return Status;
    337 
    338 }
    339 
    340 VOID
    341 HiKeyFastbootPlatformUnInit (
    342   VOID
    343   )
    344 {
    345   FreePartitionList ();
    346 }
    347 
    348 EFI_STATUS
    349 HiKeyFastbootPlatformFlashPartition (
    350   IN CHAR8  *PartitionName,
    351   IN UINTN   Size,
    352   IN VOID   *Image
    353   )
    354 {
    355   EFI_STATUS               Status;
    356   EFI_BLOCK_IO_PROTOCOL   *BlockIo;
    357   EFI_DISK_IO_PROTOCOL    *DiskIo;
    358   UINT32                   MediaId;
    359   UINTN                    PartitionSize;
    360   FASTBOOT_PARTITION_LIST *Entry;
    361   CHAR16                   PartitionNameUnicode[60];
    362   BOOLEAN                  PartitionFound;
    363   SPARSE_HEADER           *SparseHeader;
    364   CHUNK_HEADER            *ChunkHeader;
    365   UINTN                    Offset = 0;
    366   UINT32                   Chunk, EntrySize, EntryOffset;
    367   UINT32                  *FillVal, TmpCount, FillBuf[1024];
    368   VOID                    *Buffer;
    369 
    370 
    371   AsciiStrToUnicodeStr (PartitionName, PartitionNameUnicode);
    372 
    373   PartitionFound = FALSE;
    374   Entry = (FASTBOOT_PARTITION_LIST *) GetFirstNode (&(mPartitionListHead));
    375   while (!IsNull (&mPartitionListHead, &Entry->Link)) {
    376     // Search the partition list for the partition named by PartitionName
    377     if (StrCmp (Entry->PartitionName, PartitionNameUnicode) == 0) {
    378       PartitionFound = TRUE;
    379       break;
    380     }
    381 
    382    Entry = (FASTBOOT_PARTITION_LIST *) GetNextNode (&mPartitionListHead, &(Entry)->Link);
    383   }
    384   if (!PartitionFound) {
    385     return EFI_NOT_FOUND;
    386   }
    387 
    388   Status = gBS->OpenProtocol (
    389                   Entry->PartitionHandle,
    390                   &gEfiBlockIoProtocolGuid,
    391                   (VOID **) &BlockIo,
    392                   gImageHandle,
    393                   NULL,
    394                   EFI_OPEN_PROTOCOL_GET_PROTOCOL
    395                   );
    396   if (EFI_ERROR (Status)) {
    397     DEBUG ((EFI_D_ERROR, "Fastboot platform: couldn't open Block IO for flash: %r\n", Status));
    398     return EFI_NOT_FOUND;
    399   }
    400 
    401   SparseHeader=(SPARSE_HEADER *)Image;
    402 
    403   if (SparseHeader->Magic == SPARSE_HEADER_MAGIC) {
    404     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",
    405                 SparseHeader->Magic, SparseHeader->MajorVersion, SparseHeader->MinorVersion,  SparseHeader->FileHeaderSize,
    406                 SparseHeader->ChunkHeaderSize, SparseHeader->BlockSize, SparseHeader->TotalBlocks,
    407                 SparseHeader->TotalChunks, SparseHeader->ImageChecksum));
    408     if (SparseHeader->MajorVersion != 1) {
    409         DEBUG ((EFI_D_ERROR, "Sparse image version %d.%d not supported.\n",
    410                     SparseHeader->MajorVersion, SparseHeader->MinorVersion));
    411         return EFI_INVALID_PARAMETER;
    412     }
    413 
    414     Size = SparseHeader->BlockSize * SparseHeader->TotalBlocks;
    415   }
    416 
    417   // Check image will fit on device
    418   PartitionSize = (BlockIo->Media->LastBlock + 1) * BlockIo->Media->BlockSize;
    419   if (PartitionSize < Size) {
    420     DEBUG ((EFI_D_ERROR, "Partition not big enough.\n"));
    421     DEBUG ((EFI_D_ERROR, "Partition Size:\t%ld\nImage Size:\t%ld\n", PartitionSize, Size));
    422 
    423     return EFI_VOLUME_FULL;
    424   }
    425 
    426   MediaId = BlockIo->Media->MediaId;
    427 
    428   Status = gBS->OpenProtocol (
    429                   Entry->PartitionHandle,
    430                   &gEfiDiskIoProtocolGuid,
    431                   (VOID **) &DiskIo,
    432                   gImageHandle,
    433                   NULL,
    434                   EFI_OPEN_PROTOCOL_GET_PROTOCOL
    435                   );
    436   ASSERT_EFI_ERROR (Status);
    437 
    438   if (SparseHeader->Magic == SPARSE_HEADER_MAGIC) {
    439     CHAR16 OutputString[64];
    440     UINTN ChunkPrintDensity =
    441         SparseHeader->TotalChunks > 1600 ? SparseHeader->TotalChunks / 200 : 32;
    442 
    443     Image += SparseHeader->FileHeaderSize;
    444     for (Chunk = 0; Chunk < SparseHeader->TotalChunks; Chunk++) {
    445       UINTN WriteSize;
    446       ChunkHeader = (CHUNK_HEADER *)Image;
    447 
    448       // Show progress. Don't do it for every packet as outputting text
    449       // might be time consuming. ChunkPrintDensity is calculated to
    450       // provide an update every half percent change for large
    451       // downloads.
    452       if (Chunk % ChunkPrintDensity == 0) {
    453         UnicodeSPrint(OutputString, sizeof(OutputString),
    454                       L"\r%5d / %5d chunks written (%d%%)", Chunk,
    455                       SparseHeader->TotalChunks,
    456                      (Chunk * 100) / SparseHeader->TotalChunks);
    457         mTextOut->OutputString(mTextOut, OutputString);
    458       }
    459 
    460       DEBUG ((EFI_D_INFO, "Chunk #%d - Type: 0x%x Size: %d TotalSize: %d Offset %d\n",
    461                   (Chunk+1), ChunkHeader->ChunkType, ChunkHeader->ChunkSize,
    462                   ChunkHeader->TotalSize, Offset));
    463       Image += sizeof(CHUNK_HEADER);
    464       WriteSize=(SparseHeader->BlockSize) * ChunkHeader->ChunkSize;
    465       switch (ChunkHeader->ChunkType) {
    466         case CHUNK_TYPE_RAW:
    467           DEBUG ((EFI_D_INFO, "Writing %d at Offset %d\n", WriteSize, Offset));
    468           Status = DiskIo->WriteDisk (DiskIo, MediaId, Offset, WriteSize, Image);
    469           if (EFI_ERROR (Status)) {
    470             return Status;
    471           }
    472           Image+=WriteSize;
    473           break;
    474         case CHUNK_TYPE_FILL:
    475           //Assume fillVal is 0, and we can skip here
    476           FillVal = (UINT32 *)Image;
    477           Image += sizeof(UINT32);
    478           if (*FillVal != 0){
    479             mTextOut->OutputString(mTextOut, OutputString);
    480             for(TmpCount = 0; TmpCount < 1024; TmpCount++){
    481                 FillBuf[TmpCount] = *FillVal;
    482             }
    483             for (TmpCount= 0; TmpCount < WriteSize; TmpCount += sizeof(FillBuf)) {
    484                 if ((WriteSize - TmpCount) < sizeof(FillBuf)) {
    485                   Status = DiskIo->WriteDisk (DiskIo, MediaId, Offset + TmpCount, WriteSize - TmpCount, FillBuf);
    486                 } else {
    487                   Status = DiskIo->WriteDisk (DiskIo, MediaId, Offset + TmpCount, sizeof(FillBuf), FillBuf);
    488                 }
    489                 if (EFI_ERROR (Status)) {
    490                     return Status;
    491                 }
    492             }
    493           }
    494           break;
    495         case CHUNK_TYPE_DONT_CARE:
    496           break;
    497         case CHUNK_TYPE_CRC32:
    498           break;
    499         default:
    500           DEBUG ((EFI_D_ERROR, "Unknown Chunk Type: 0x%x", ChunkHeader->ChunkType));
    501           return EFI_PROTOCOL_ERROR;
    502       }
    503       Offset += WriteSize;
    504     }
    505 
    506     UnicodeSPrint(OutputString, sizeof(OutputString),
    507                   L"\r%5d / %5d chunks written (100%%)\r\n",
    508                   SparseHeader->TotalChunks, SparseHeader->TotalChunks);
    509     mTextOut->OutputString(mTextOut, OutputString);
    510   } else {
    511     if (AsciiStrCmp (PartitionName, "ptable") == 0) {
    512       Buffer = Image;
    513       if (AsciiStrnCmp (Buffer, "ENTRYHDR", 8) != 0) {
    514         DEBUG ((EFI_D_ERROR, "unknown ptable image\n"));
    515         return EFI_UNSUPPORTED;
    516       }
    517       Buffer += 8;
    518       if (AsciiStrnCmp (Buffer, "primary", 7) != 0) {
    519         DEBUG ((EFI_D_ERROR, "unknown ptable image\n"));
    520         return EFI_UNSUPPORTED;
    521       }
    522       Buffer += 8;
    523       EntryOffset = *(UINT32 *)Buffer * BlockIo->Media->BlockSize;
    524       Buffer += 4;
    525       EntrySize = *(UINT32 *)Buffer * BlockIo->Media->BlockSize;
    526       if ((EntrySize + 512) > Size) {
    527         DEBUG ((EFI_D_ERROR, "Entry size doesn't match\n"));
    528         return EFI_UNSUPPORTED;
    529       }
    530       Buffer = Image + 512;
    531       Status = DiskIo->WriteDisk (DiskIo, MediaId, EntryOffset, EntrySize, Buffer);
    532       if (EFI_ERROR (Status)) {
    533         return Status;
    534       }
    535 
    536       Buffer = Image + 16 + 12;
    537       if (AsciiStrnCmp (Buffer, "ENTRYHDR", 8) != 0)
    538         return Status;
    539       Buffer += 8;
    540       if (AsciiStrnCmp (Buffer, "second", 6) != 0)
    541         return Status;
    542       Buffer += 8;
    543       EntryOffset = *(UINT32 *)Buffer * BlockIo->Media->BlockSize;
    544       Buffer += 4;
    545       EntrySize = *(UINT32 *)Buffer * BlockIo->Media->BlockSize;
    546       if ((EntrySize + 512) > Size) {
    547         DEBUG ((EFI_D_ERROR, "Entry size doesn't match\n"));
    548         return EFI_UNSUPPORTED;
    549       }
    550       Buffer = Image + 512;
    551       Status = DiskIo->WriteDisk (DiskIo, MediaId, EntryOffset, EntrySize, Buffer);
    552     } else {
    553       Status = DiskIo->WriteDisk (DiskIo, MediaId, 0, Size, Image);
    554     }
    555     if (EFI_ERROR (Status)) {
    556       return Status;
    557     }
    558   }
    559 
    560   BlockIo->FlushBlocks(BlockIo);
    561   MicroSecondDelay (50000);
    562 
    563   return Status;
    564 }
    565 
    566 EFI_STATUS
    567 HiKeyFastbootPlatformErasePartition (
    568   IN CHAR8 *PartitionName
    569   )
    570 {
    571   EFI_STATUS                Status;
    572   EFI_BLOCK_IO_PROTOCOL    *BlockIo;
    573   EFI_BLOCK_IO_PROTOCOL    *MmcBlockIo;
    574   EFI_ERASE_BLOCK_PROTOCOL *EraseBlockProtocol;
    575   UINT32                    MediaId;
    576 //  UINTN                    PartitionSize;
    577   FASTBOOT_PARTITION_LIST  *Entry;
    578   CHAR16                    PartitionNameUnicode[60];
    579   BOOLEAN                   PartitionFound;
    580   UINTN                     NumHandles;
    581   EFI_HANDLE               *BufferHandle;
    582 
    583   AsciiStrToUnicodeStr (PartitionName, PartitionNameUnicode);
    584 
    585   PartitionFound = FALSE;
    586   Entry = (FASTBOOT_PARTITION_LIST *) GetFirstNode (&(mPartitionListHead));
    587   while (!IsNull (&mPartitionListHead, &Entry->Link)) {
    588     // Search the partition list for the partition named by PartitionName
    589     if (StrCmp (Entry->PartitionName, PartitionNameUnicode) == 0) {
    590       PartitionFound = TRUE;
    591       break;
    592     }
    593 
    594    Entry = (FASTBOOT_PARTITION_LIST *) GetNextNode (&mPartitionListHead, &(Entry)->Link);
    595   }
    596   if (!PartitionFound) {
    597     return EFI_NOT_FOUND;
    598   }
    599 
    600   Status = gBS->OpenProtocol (
    601                   Entry->PartitionHandle,
    602                   &gEfiBlockIoProtocolGuid,
    603                   (VOID **) &BlockIo,
    604                   gImageHandle,
    605                   NULL,
    606                   EFI_OPEN_PROTOCOL_GET_PROTOCOL
    607                   );
    608   if (EFI_ERROR (Status)) {
    609     DEBUG ((EFI_D_ERROR, "Fastboot platform: couldn't open Block IO for flash: %r\n", Status));
    610     return EFI_NOT_FOUND;
    611   }
    612 
    613   Status = gBS->LocateProtocol (&gEfiEraseBlockProtocolGuid, NULL, (VOID **) &EraseBlockProtocol);
    614   ASSERT_EFI_ERROR (Status);
    615 
    616   Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiEraseBlockProtocolGuid, NULL, &NumHandles, &BufferHandle);
    617   ASSERT_EFI_ERROR (Status);
    618 
    619   Status = gBS->HandleProtocol (
    620                   BufferHandle[0],
    621                   &gEfiBlockIoProtocolGuid,
    622                   (VOID **) &MmcBlockIo
    623                   );
    624   if (EFI_ERROR (Status)) {
    625     DEBUG ((EFI_D_ERROR, "Fastboot platform: couldn't open Block IO for MMC device: %r\n", Status));
    626     return EFI_NOT_FOUND;
    627   }
    628 
    629   MediaId = BlockIo->Media->MediaId;
    630 
    631 
    632 //  PartitionSize = (BlockIo->Media->LastBlock + 1) * BlockIo->Media->BlockSize;
    633 //  if (AsciiStrnCmp (PartitionName, "ptable", 6) == 0) {
    634     // partition table (GPT) cost 34 blocks
    635 //    PartitionSize = 34 * BlockIo->Media->BlockSize;
    636 //  }
    637 
    638   Status = EraseBlockProtocol->EraseBlocks (MmcBlockIo, MediaId, Entry->Lba, NULL, BlockIo->Media->LastBlock);
    639     if (EFI_ERROR (Status)) {
    640       DEBUG ((EFI_D_ERROR, "%a: Fail to erase at address 0x%x\n", __func__, Entry->Lba));
    641     }
    642   return Status;
    643 }
    644 
    645 EFI_STATUS
    646 HiKeyFastbootPlatformGetVar (
    647   IN  CHAR8   *Name,
    648   OUT CHAR8   *Value
    649   )
    650 {
    651   EFI_STATUS               Status;
    652   EFI_BLOCK_IO_PROTOCOL   *BlockIo;
    653   UINT64                   PartitionSize;
    654   FASTBOOT_PARTITION_LIST *Entry;
    655   CHAR16                   PartitionNameUnicode[60];
    656   BOOLEAN                  PartitionFound;
    657   CHAR16                   DataUnicode[17];
    658   UINTN                    VariableSize;
    659 
    660   if (!AsciiStrCmp (Name, "max-download-size")) {
    661     AsciiStrCpy (Value, FixedPcdGetPtr (PcdArmFastbootFlashLimit));
    662   } else if (!AsciiStrCmp (Name, "product")) {
    663     AsciiStrCpy (Value, FixedPcdGetPtr (PcdFirmwareVendor));
    664   } else if (!AsciiStrCmp (Name, "serialno")) {
    665     VariableSize = 17 * sizeof (CHAR16);
    666     Status = gRT->GetVariable (
    667                     (CHAR16 *)L"SerialNo",
    668                     &gHiKeyVariableGuid,
    669                     NULL,
    670                     &VariableSize,
    671                     &DataUnicode
    672                     );
    673     if (EFI_ERROR (Status)) {
    674       *Value = '\0';
    675       return EFI_NOT_FOUND;
    676     }
    677     DataUnicode[(VariableSize / sizeof(CHAR16)) - 1] = '\0';
    678     UnicodeStrToAsciiStr (DataUnicode, Value);
    679   } else if ( !AsciiStrnCmp (Name, "partition-size", 14)) {
    680     AsciiStrToUnicodeStr ((Name + 15), PartitionNameUnicode);
    681     PartitionFound = FALSE;
    682     Entry = (FASTBOOT_PARTITION_LIST *) GetFirstNode (&(mPartitionListHead));
    683     while (!IsNull (&mPartitionListHead, &Entry->Link)) {
    684       // Search the partition list for the partition named by PartitionName
    685       if (StrCmp (Entry->PartitionName, PartitionNameUnicode) == 0) {
    686         PartitionFound = TRUE;
    687         break;
    688       }
    689 
    690      Entry = (FASTBOOT_PARTITION_LIST *) GetNextNode (&mPartitionListHead, &(Entry)->Link);
    691     }
    692     if (!PartitionFound) {
    693       *Value = '\0';
    694       return EFI_NOT_FOUND;
    695     }
    696 
    697     Status = gBS->OpenProtocol (
    698                     Entry->PartitionHandle,
    699                     &gEfiBlockIoProtocolGuid,
    700                     (VOID **) &BlockIo,
    701                     gImageHandle,
    702                     NULL,
    703                     EFI_OPEN_PROTOCOL_GET_PROTOCOL
    704                     );
    705     if (EFI_ERROR (Status)) {
    706       DEBUG ((EFI_D_ERROR, "Fastboot platform: couldn't open Block IO for flash: %r\n", Status));
    707       *Value = '\0';
    708       return EFI_NOT_FOUND;
    709     }
    710 
    711     PartitionSize = (BlockIo->Media->LastBlock + 1) * BlockIo->Media->BlockSize;
    712     DEBUG ((EFI_D_ERROR, "Fastboot platform: check for partition-size:%a 0X%llx\n", Name, PartitionSize ));
    713     AsciiSPrint (Value, 12, "0x%llx", PartitionSize);
    714   } else if ( !AsciiStrnCmp (Name, "partition-type", 14)) {
    715       DEBUG ((EFI_D_ERROR, "Fastboot platform: check for partition-type:%a\n", (Name + 15) ));
    716     if ( !AsciiStrnCmp  ( (Name + 15) , "system", 6) || !AsciiStrnCmp  ( (Name + 15) , "userdata", 8)
    717             || !AsciiStrnCmp  ( (Name + 15) , "cache", 5)) {
    718       AsciiStrCpy (Value, "ext4");
    719     } else {
    720       AsciiStrCpy (Value, "raw");
    721     }
    722   } else {
    723     *Value = '\0';
    724   }
    725   return EFI_SUCCESS;
    726 }
    727 
    728 EFI_STATUS
    729 HiKeyFastbootPlatformOemCommand (
    730   IN  CHAR8   *Command
    731   )
    732 {
    733   CHAR16     CommandUnicode[65];
    734   UINTN      Index = 0, VariableSize;
    735   UINT16     AutoBoot, Data;
    736   EFI_STATUS Status;
    737 
    738   if (AsciiStrCmp (Command, "Demonstrate") == 0) {
    739     DEBUG ((EFI_D_ERROR, "ARM OEM Fastboot command 'Demonstrate' received.\n"));
    740     return EFI_SUCCESS;
    741   } else if (AsciiStrnCmp (Command, "autoboot", AsciiStrLen ("autoboot")) == 0) {
    742     Index += sizeof ("autoboot");
    743     while (TRUE) {
    744       if (Command[Index] == '\0')
    745         goto out;
    746       else if (Command[Index] == ' ')
    747         Index++;
    748       else
    749         break;
    750     }
    751     Data = AsciiStrDecimalToUintn (Command + Index);
    752 
    753     VariableSize = sizeof (UINT16);
    754     Status = gRT->GetVariable (
    755                     (CHAR16 *)L"HiKeyAutoBoot",
    756                     &gHiKeyVariableGuid,
    757                     NULL,
    758                     &VariableSize,
    759                     &AutoBoot
    760                     );
    761     if ((EFI_ERROR (Status) == 0) && (AutoBoot == Data)) {
    762       return EFI_SUCCESS;
    763     }
    764     AutoBoot = Data;
    765     Status = gRT->SetVariable (
    766                     (CHAR16*)L"HiKeyAutoBoot",
    767                     &gHiKeyVariableGuid,
    768                     EFI_VARIABLE_NON_VOLATILE       |
    769                     EFI_VARIABLE_BOOTSERVICE_ACCESS |
    770                     EFI_VARIABLE_RUNTIME_ACCESS,
    771                     sizeof (UINT16),
    772                     &AutoBoot
    773                     );
    774     return Status;
    775   } else {
    776     AsciiStrToUnicodeStr (Command + Index, CommandUnicode);
    777     DEBUG ((EFI_D_ERROR,
    778       "HiKey: Unrecognised Fastboot OEM command: %s\n",
    779       CommandUnicode
    780       ));
    781     return EFI_NOT_FOUND;
    782   }
    783 out:
    784   return EFI_NOT_FOUND;
    785 }
    786 
    787 FASTBOOT_PLATFORM_PROTOCOL mPlatformProtocol = {
    788   HiKeyFastbootPlatformInit,
    789   HiKeyFastbootPlatformUnInit,
    790   HiKeyFastbootPlatformFlashPartition,
    791   HiKeyFastbootPlatformErasePartition,
    792   HiKeyFastbootPlatformGetVar,
    793   HiKeyFastbootPlatformOemCommand
    794 };
    795 
    796 EFI_STATUS
    797 EFIAPI
    798 HiKeyFastbootPlatformEntryPoint (
    799   IN EFI_HANDLE                            ImageHandle,
    800   IN EFI_SYSTEM_TABLE                      *SystemTable
    801   )
    802 {
    803   return gBS->InstallProtocolInterface (
    804                 &ImageHandle,
    805                 &gAndroidFastbootPlatformProtocolGuid,
    806                 EFI_NATIVE_INTERFACE,
    807                 &mPlatformProtocol
    808                 );
    809 }
    810