blob: 6fed932ee479bc1093f1c3f38f7a0c924a8f9d51 [file] [log] [blame]
/*
* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of The Linux Foundation nor
* the names of its contributors may be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Uefi/UefiSpec.h>
#include "PartitionTableUpdate.h"
#include <Library/LinuxLoaderLib.h>
STATIC BOOLEAN FlashingGpt;
STATIC BOOLEAN ParseSecondaryGpt;
struct StoragePartInfo Ptable[MAX_LUNS];
struct PartitionEntry PtnEntries[MAX_NUM_PARTITIONS];
STATIC UINT32 MaxLuns;
STATIC CHAR16 CurrentSlot[MAX_SLOT_SUFFIX_SZ];
STATIC CHAR16 ActiveSlot[MAX_SLOT_SUFFIX_SZ];
STATIC UINT32 PartitionCount;
STATIC BOOLEAN MultiSlotBoot;
CHAR16* GetCurrentSlotSuffix() {
return ActiveSlot;
}
VOID SetCurrentSlotSuffix(CHAR16* SlotSuffix) {
StrnCpyS(ActiveSlot, MAX_SLOT_SUFFIX_SZ, SlotSuffix, StrLen(SlotSuffix));
return;
}
UINT32 GetMaxLuns() {
return MaxLuns;
}
UINT32 GetPartitionLunFromIndex(UINTN Index)
{
return PtnEntries[Index].lun;
}
VOID GetPartitionCount(UINT32 *Val) {
*Val = PartitionCount;
return;
}
VOID SetMultiSlotBootVal(BOOLEAN Val) {
MultiSlotBoot = Val;
return;
}
INT32 GetPartitionIdxInLun(CHAR16 *Pname, UINTN Lun)
{
UINTN n;
UINTN RelativeIndex = 0;
for (n = 0; n < PartitionCount; n++) {
if (Lun == PtnEntries[n].lun) {
if (!StrCmp(Pname, PtnEntries[n].PartEntry.PartitionName))
return RelativeIndex;
RelativeIndex++;
}
}
return INVALID_PTN;
}
VOID UpdatePartitionEntries()
{
UINT32 i;
UINT32 j;
UINT32 Index = 0;
EFI_STATUS Status;
EFI_PARTITION_ENTRY *PartEntry;
CHAR8 PartitionNameAscii[MAX_GPT_NAME_SIZE];
PartitionCount = 0;
/*Nullify the PtnEntries array before using it*/
SetMem((VOID*) PtnEntries, (sizeof(PtnEntries[0]) * MAX_NUM_PARTITIONS), 0);
for (i = 0; i < MaxLuns; i++) {
for (j = 0; (j < Ptable[i].MaxHandles) && (Index < MAX_NUM_PARTITIONS); j++, Index++) {
Status = gBS->HandleProtocol(Ptable[i].HandleInfoList[j].Handle, &gEfiPartitionRecordGuid, (VOID **)&PartEntry);
PartitionCount++;
if (EFI_ERROR(Status)) {
DEBUG((EFI_D_VERBOSE, "Selected Lun : %d, handle: %d does not have partition record, ignore\n", i,j));
PtnEntries[Index].lun = i;
continue;
}
CopyMem((&PtnEntries[Index]), PartEntry, sizeof(PartEntry[0]));
PtnEntries[Index].lun = i;
}
}
}
INT32 GetPartitionIndex(CHAR16 *Pname)
{
INT32 i;
for (i = 0; i < PartitionCount; i++) {
if (!StrCmp(PtnEntries[i].PartEntry.PartitionName, Pname))
return i;
}
return INVALID_PTN;
}
STATIC EFI_STATUS GetStorageHandle(INTN Lun, HandleInfo *BlockIoHandle, UINTN *MaxHandles)
{
EFI_STATUS Status = EFI_INVALID_PARAMETER;
UINT32 Attribs = 0;
PartiSelectFilter HandleFilter;
//UFS LUN GUIDs
EFI_GUID LunGuids[] = {
gEfiUfsLU0Guid,
gEfiUfsLU1Guid,
gEfiUfsLU2Guid,
gEfiUfsLU3Guid,
gEfiUfsLU4Guid,
gEfiUfsLU5Guid,
gEfiUfsLU6Guid,
gEfiUfsLU7Guid,
};
Attribs |= BLK_IO_SEL_SELECT_ROOT_DEVICE_ONLY;
HandleFilter.PartitionType = 0;
HandleFilter.VolumeName = 0;
if (Lun == NO_LUN) {
HandleFilter.RootDeviceType = &gEfiEmmcUserPartitionGuid;
Status = GetBlkIOHandles(Attribs, &HandleFilter, BlockIoHandle, MaxHandles);
if (EFI_ERROR (Status)) {
DEBUG((EFI_D_ERROR, "Error getting block IO handle for Emmc\n"));
return Status;
}
} else {
HandleFilter.RootDeviceType = &LunGuids[Lun];
Status = GetBlkIOHandles(Attribs, &HandleFilter, BlockIoHandle, MaxHandles);
if (EFI_ERROR (Status)) {
DEBUG((EFI_D_ERROR, "Error getting block IO handle for Lun:%x\n", Lun));
return Status;
}
}
return Status;
}
void UpdatePartitionAttributes()
{
UINT32 BlkSz;
UINT8 *GptHdr = NULL;
UINT8 *GptHdrPtr = NULL;
UINTN MaxGptSz;
UINT32 Offset;
UINT32 MaxPtnCount = 0;
UINT32 PtnEntrySz = 0;
UINT32 i = 0;
UINT8 *PtnEntriesPtr;
UINT8 *Ptn_Entries;
UINT32 CrcVal = 0;
UINT32 Iter;
UINT32 HdrSz = GPT_HEADER_SIZE;
UINT64 DeviceDensity;
UINT64 CardSizeSec;
EFI_STATUS Status;
INTN Lun;
EFI_BLOCK_IO_PROTOCOL *BlockIo=NULL;
HandleInfo BlockIoHandle[MAX_HANDLEINF_LST_SIZE];
UINTN MaxHandles = MAX_HANDLEINF_LST_SIZE;
for( Lun = 0; Lun < MaxLuns; Lun++) {
Status = GetStorageHandle(Lun, BlockIoHandle, &MaxHandles);
if (Status || (MaxHandles != 1)) {
DEBUG((EFI_D_ERROR, "Failed to get the BlockIo for the device %r\n",Status));
return;
}
BlockIo = BlockIoHandle[0].BlkIo;
DeviceDensity = (BlockIo->Media->LastBlock + 1) * BlockIo->Media->BlockSize;
BlkSz = BlockIo->Media->BlockSize;
MaxGptSz = GPT_HDR_AND_PTN_ENTRIES * BlkSz;
CardSizeSec = (DeviceDensity) / BlkSz;
Offset = PRIMARY_HDR_LBA;
GptHdr = AllocatePool(MaxGptSz);
if (!GptHdr) {
DEBUG ((EFI_D_ERROR, "Unable to Allocate Memory for GptHdr \n"));
return;
}
SetMem((VOID *) GptHdr, MaxGptSz, 0);
GptHdrPtr = GptHdr;
/* This loop iterates twice to update both primary and backup Gpt*/
for (Iter= 0; Iter < 2; Iter++) {
Status = BlockIo->ReadBlocks (BlockIo, BlockIo->Media->MediaId, Offset, MaxGptSz, GptHdr);
if(EFI_ERROR(Status)) {
DEBUG ((EFI_D_ERROR, "Unable to read the media \n"));
return;
}
if(Iter == 0x1) {
/* This is the back up GPT */
Ptn_Entries = (CHAR8 *)GptHdr;
GptHdr = GptHdr + ((GPT_HDR_AND_PTN_ENTRIES - 1) * BlkSz);
} else
/* otherwise we are at the primary gpt */
Ptn_Entries = (CHAR8 *)GptHdr + BlkSz;
PtnEntriesPtr = Ptn_Entries;
for (i = 0;i < PartitionCount;i++) {
/*If GUID is not present, then it is BlkIo Handle of the Lun. Skip*/
if (!(PtnEntries[i].PartEntry.PartitionTypeGUID.Data1)) {
DEBUG((EFI_D_VERBOSE, " Skipping Lun:%d, i=%d\n", Lun, i));
continue;
}
/* Partition table is populated with entries from lun 0 to max lun.
* break out of the loop once we see the partition lun is > current lun */
if (PtnEntries[i].lun > Lun)
break;
/* Find the entry where the partition table for 'lun' starts and then update the attributes */
if (PtnEntries[i].lun != Lun)
continue;
/* Update the partition attributes and partiton GUID values */
PUT_LONG_LONG(&PtnEntriesPtr[ATTRIBUTE_FLAG_OFFSET], PtnEntries[i].PartEntry.Attributes);
CopyMem((VOID *)PtnEntriesPtr, (VOID *)&PtnEntries[i].PartEntry.PartitionTypeGUID, GUID_SIZE);
/* point to the next partition entry */
PtnEntriesPtr += PARTITION_ENTRY_SIZE;
}
MaxPtnCount = GET_LWORD_FROM_BYTE(&GptHdr[PARTITION_COUNT_OFFSET]);
PtnEntrySz = GET_LWORD_FROM_BYTE(&GptHdr[PENTRY_SIZE_OFFSET]);
Status = gBS->CalculateCrc32(Ptn_Entries, ((MaxPtnCount) * (PtnEntrySz)),&CrcVal);
if (Status != EFI_SUCCESS) {
DEBUG((EFI_D_ERROR, "Error Calculating CRC32 on the Gpt header: %x\n", Status));
return;
}
PUT_LONG(&GptHdr[PARTITION_CRC_OFFSET], CrcVal);
/*Write CRC to 0 before we calculate the crc of the GPT header*/
CrcVal = 0;
PUT_LONG(&GptHdr[HEADER_CRC_OFFSET], CrcVal);
Status = gBS->CalculateCrc32(GptHdr, HdrSz, &CrcVal);
if (Status != EFI_SUCCESS) {
DEBUG((EFI_D_ERROR, "Error Calculating CRC32 on the Gpt header: %x\n", Status));
return;
}
PUT_LONG(&GptHdr[HEADER_CRC_OFFSET], CrcVal);
if (Iter == 0x1)
/* Write the backup GPT header, which is at an offset of CardSizeSec - GPT_HDR_AND_PTN_ENTRIES in blocks*/
Status = BlockIo->WriteBlocks(BlockIo, BlockIo->Media->MediaId, Offset, MaxGptSz, (VOID *)Ptn_Entries);
else
/* Write the primary GPT header, which is at an offset of BlkSz */
Status = BlockIo->WriteBlocks(BlockIo, BlockIo->Media->MediaId, Offset, MaxGptSz, (VOID *)GptHdr);
if (EFI_ERROR(Status)) {
DEBUG((EFI_D_ERROR, "Error writing primary GPT header: %r\n", Status));
return;
}
Offset = CardSizeSec - GPT_HDR_AND_PTN_ENTRIES;
}
FreePool(GptHdrPtr);
}
}
VOID MarkPtnActive(CHAR16 *ActiveSlot)
{
UINT32 i;
for (i = 0; i < PartitionCount; i++) {
/* Mark all the slots with current ActiveSlot as active */
if (StrStr(PtnEntries[i].PartEntry.PartitionName, ActiveSlot))
PtnEntries[i].PartEntry.Attributes |= PART_ATT_ACTIVE_VAL;
else
PtnEntries[i].PartEntry.Attributes &= ~PART_ATT_ACTIVE_VAL;
}
/* Update the partition table */
UpdatePartitionAttributes();
}
STATIC VOID SwapPtnGuid(EFI_PARTITION_ENTRY *p1, EFI_PARTITION_ENTRY *p2)
{
EFI_GUID Temp;
if (p1 == NULL || p2 == NULL)
return;
CopyMem((VOID *)&Temp, (VOID *)&p1->PartitionTypeGUID, sizeof(EFI_GUID));
CopyMem((VOID *)&p1->PartitionTypeGUID, (VOID *)&p2->PartitionTypeGUID, sizeof(EFI_GUID));
CopyMem((VOID *)&p2->PartitionTypeGUID, (VOID *)&Temp, sizeof(EFI_GUID));
}
VOID SwitchPtnSlots(CONST CHAR16 *SetActive)
{
UINT32 i, j;
CONST CHAR16 *BootParts[] = { L"rpm",
L"tz",
L"pmic",
L"modem",
L"hyp",
L"cmnlib",
L"cmnlib64",
L"keymaster",
L"devcfg",
L"abl",
L"apdp"};
UINT32 Sz = ARRAY_SIZE(BootParts);
struct PartitionEntry *PtnCurrent = NULL;
struct PartitionEntry *PtnNew = NULL;
CHAR16 CurSlot[BOOT_PART_SIZE];
CHAR16 NewSlot[BOOT_PART_SIZE];
CHAR16 SetInactive[MAX_SLOT_SUFFIX_SZ];
UINT32 UfsBootLun = 0;
BOOLEAN UfsGet = TRUE;
BOOLEAN UfsSet = FALSE;
EFI_STATUS Status;
/* Create the partition name string for active and non active slots*/
if (!StrnCmp(SetActive, L"_a", StrLen(L"_a")))
StrnCpyS(SetInactive, MAX_SLOT_SUFFIX_SZ, L"_b", StrLen(L"_b"));
else
StrnCpyS(SetInactive, MAX_SLOT_SUFFIX_SZ, L"_a", StrLen(L"_a"));
for (j = 0; j < Sz; j++) {
StrnCpyS(CurSlot, BOOT_PART_SIZE, BootParts[j], StrLen(BootParts[j]));
StrnCatS(CurSlot, BOOT_PART_SIZE, SetInactive, StrLen(SetInactive));
StrnCpyS(NewSlot, BOOT_PART_SIZE, BootParts[j], StrLen(BootParts[j]));
StrnCatS(NewSlot, BOOT_PART_SIZE, SetActive, StrLen(SetActive));
/* Find the pointer to partition table entry for active and non-active slots*/
for (i = 0; i < PartitionCount; i++) {
if (!StrnCmp(PtnEntries[i].PartEntry.PartitionName, CurSlot, StrLen(CurSlot))) {
PtnCurrent = &PtnEntries[i];
} else if (!StrnCmp(PtnEntries[i].PartEntry.PartitionName, NewSlot, StrLen(NewSlot))) {
PtnNew = &PtnEntries[i];
}
}
/* Swap the guids for the slots */
SwapPtnGuid(&PtnCurrent->PartEntry, &PtnNew->PartEntry);
SetMem(CurSlot, BOOT_PART_SIZE, 0);
SetMem(NewSlot, BOOT_PART_SIZE, 0);
PtnCurrent = PtnNew = NULL;
}
UfsGetSetBootLun(&UfsBootLun, UfsGet);
// Special case for XBL is to change the bootlun instead of swapping the guid
if (UfsBootLun == 0x1 && !StrnCmp(SetActive, L"_b", StrLen(L"_b"))) {
DEBUG((EFI_D_INFO, "Switching the boot lun from 1 to 2\n"));
UfsBootLun = 0x2;
}
else if (UfsBootLun == 0x2 && !StrnCmp(SetActive, L"_a", StrLen(L"_a"))) {
DEBUG((EFI_D_INFO, "Switching the boot lun from 2 to 1\n"));
UfsBootLun = 0x1;
}
UfsGetSetBootLun(&UfsBootLun, UfsSet);
}
EFI_STATUS
EnumeratePartitions ()
{
EFI_STATUS Status;
PartiSelectFilter HandleFilter;
UINT32 Attribs = 0;
UINT32 i;
INT32 Lun = NO_LUN;
//UFS LUN GUIDs
EFI_GUID LunGuids[] = {
gEfiUfsLU0Guid,
gEfiUfsLU1Guid,
gEfiUfsLU2Guid,
gEfiUfsLU3Guid,
gEfiUfsLU4Guid,
gEfiUfsLU5Guid,
gEfiUfsLU6Guid,
gEfiUfsLU7Guid,
};
SetMem((VOID*) Ptable, (sizeof(struct StoragePartInfo) * MAX_LUNS), 0);
/* By default look for emmc partitions if not found look for UFS */
Attribs |= BLK_IO_SEL_MATCH_ROOT_DEVICE;
Ptable[0].MaxHandles = ARRAY_SIZE(Ptable[0].HandleInfoList);
HandleFilter.PartitionType = 0;
HandleFilter.VolumeName = 0;
HandleFilter.RootDeviceType = &gEfiEmmcUserPartitionGuid;
Status = GetBlkIOHandles(Attribs, &HandleFilter, &Ptable[0].HandleInfoList[0], &Ptable[0].MaxHandles);
/* For Emmc devices the Lun concept does not exist, we will always one lun and the lun number is '0'
* to have the partition selection implementation same acros
*/
if (Status == EFI_SUCCESS && Ptable[0].MaxHandles > 0) {
Lun = 0;
MaxLuns = 1;
}
/* If the media is not emmc then look for UFS */
else if (EFI_ERROR (Status) || Ptable[0].MaxHandles == 0) {
/* By default max 8 luns are supported but HW could be configured to use only few of them or all of them
* Based on the information read update the MaxLuns to reflect the max supported luns */
for (i = 0 ; i < MAX_LUNS; i++) {
Ptable[i].MaxHandles = ARRAY_SIZE(Ptable[i].HandleInfoList);
HandleFilter.PartitionType = 0;
HandleFilter.VolumeName = 0;
HandleFilter.RootDeviceType = &LunGuids[i];
Status = GetBlkIOHandles(Attribs, &HandleFilter, &Ptable[i].HandleInfoList[0], &Ptable[i].MaxHandles);
/* If we fail to get block for a lun that means the lun is not configured and unsed, ignore the error
* and continue with the next Lun */
if (EFI_ERROR (Status)) {
DEBUG((EFI_D_ERROR, "Error getting block IO handle for %d lun, Lun may be unused\n", i));
continue;
}
}
MaxLuns = i;
} else {
DEBUG((EFI_D_ERROR, "Error populating block IO handles\n"));
return EFI_NOT_FOUND;
}
return Status;
}
/*Function to provide has-slot info
*Pname: the partition name
*return: 1 or 0.
*/
BOOLEAN PartitionHasMultiSlot(CONST CHAR16 *Pname)
{
UINT32 i;
UINT32 j;
UINT32 SlotCount = 0;
UINT32 Len = StrLen(Pname);
/*If MultiSlot is set just return the value avoid for loop everytime*/
if (MultiSlotBoot)
return MultiSlotBoot;
for (i = 0; i < PartitionCount; i++) {
if(!(StrnCmp(PtnEntries[i].PartEntry.PartitionName, Pname, Len))) {
if (PtnEntries[i].PartEntry.PartitionName[Len] == L'_' &&
(PtnEntries[i].PartEntry.PartitionName[Len+1] == L'a' ||
PtnEntries[i].PartEntry.PartitionName[Len+1] == L'b'))
SlotCount++;
}
}
if (SlotCount > MIN_SLOTS)
MultiSlotBoot = TRUE;
else
MultiSlotBoot = FALSE;
return MultiSlotBoot;
}
VOID FindPtnActiveSlot()
{
UINT32 i;
CHAR16 *Suffix = NULL;
UINT32 HighPriority = 0;
CHAR16 DefaultActive[MAX_SLOT_SUFFIX_SZ]= L"_a";
UINT32 Unbootable = 0;
CHAR16 SlotInfo[MAX_SLOT_SUFFIX_SZ];
/*Traverse through partition entries,count matching slots with boot */
for (i = 0; i < PartitionCount; i++) {
/* We determine the active slot chain based on the attributes of boot partition */
if(!(StrnCmp(PtnEntries[i].PartEntry.PartitionName, L"boot", StrLen(L"boot")))) {
Suffix = PtnEntries[i].PartEntry.PartitionName + StrLen(L"boot");
if ((HighPriority < (PtnEntries[i].PartEntry.Attributes & PART_ATT_PRIORITY_VAL))
&& !(PtnEntries[i].PartEntry.Attributes & PART_ATT_UNBOOTABLE_VAL) &&
PtnEntries[i].PartEntry.Attributes & PART_ATT_ACTIVE_VAL) {
HighPriority = (PtnEntries[i].PartEntry.Attributes & PART_ATT_PRIORITY_VAL);
StrnCpyS(ActiveSlot, MAX_SLOT_SUFFIX_SZ, Suffix, StrLen(Suffix));
}
if (PtnEntries[i].PartEntry.Attributes & PART_ATT_UNBOOTABLE_VAL) {
StrnCpyS(SlotInfo, MAX_SLOT_SUFFIX_SZ, Suffix, StrLen(Suffix));
SetMem(ActiveSlot, sizeof(ActiveSlot), 0);
Unbootable++;
}
}
}
if (Unbootable == (MAX_SLOTS - 1)) {
if (SlotInfo[1] == L'a')
StrnCpyS(ActiveSlot, MAX_SLOT_SUFFIX_SZ, L"_b", StrLen(L"_b"));
else
StrnCpyS(ActiveSlot, MAX_SLOT_SUFFIX_SZ, L"_a", StrLen(L"_a"));
}
/* Probably we are booting for the first time and the active slot is not set using
* fastboot set_active, so default to slot 'a'
*/
if (!Unbootable && !ActiveSlot[0] && !HighPriority) {
StrnCpyS(ActiveSlot, MAX_SLOT_SUFFIX_SZ, DefaultActive, StrLen(DefaultActive));
for (i = 0; i < PartitionCount; i++) {
if (!(StrnCmp(PtnEntries[i].PartEntry.PartitionName, L"boot_a", StrLen(L"boot_a")))) {
PtnEntries[i].PartEntry.Attributes |=
(PART_ATT_PRIORITY_VAL | PART_ATT_ACTIVE_VAL | PART_ATT_MAX_RETRY_COUNT_VAL) &
(~PART_ATT_SUCCESSFUL_VAL & ~PART_ATT_UNBOOTABLE_VAL);
}
}
}
if (!ActiveSlot[0] && !ActiveSlot[1]) {
DEBUG((EFI_D_ERROR, "ERROR: NO ACTIVE SLOT FOUND\n"));
ASSERT(0);
}
UpdatePartitionAttributes();
StrnCpyS(CurrentSlot, MAX_SLOT_SUFFIX_SZ, ActiveSlot, StrLen(ActiveSlot));
if (Unbootable)
SwitchPtnSlots(CurrentSlot);
return;
}
/* If we are here after marking the current slot as unbootable, then we
* switch the slots for the entire bootchain so we are booting all the images
* from the new slot and reboot the device so that bootchain is picked from new slot
*/
STATIC VOID MarkSlotUnbootable()
{
CHAR16 PartName[MAX_GPT_NAME_SIZE];
UINT32 i;
SwitchPtnSlots(CurrentSlot);
StrnCpyS(PartName, MAX_GPT_NAME_SIZE, L"boot",StrLen(L"boot"));
StrnCatS(PartName, MAX_GPT_NAME_SIZE, CurrentSlot, StrLen(CurrentSlot));
for (i = 0; i < PartitionCount; i++) {
if(!StrnCmp(PtnEntries[i].PartEntry.PartitionName, PartName, StrLen(PartName))) {
/*select the slot and increase the priority = 7,retry-count =7,slot_successful = 0 and slot_unbootable =0*/
PtnEntries[i].PartEntry.Attributes =
(PtnEntries[i].PartEntry.Attributes | PART_ATT_PRIORITY_VAL |
PART_ATT_ACTIVE_VAL | PART_ATT_MAX_RETRY_COUNT_VAL) &
(~PART_ATT_SUCCESSFUL_VAL & ~PART_ATT_UNBOOTABLE_VAL);
}
}
UpdatePartitionAttributes();
DEBUG((EFI_D_INFO, "Rebooting\n"));
gRT->ResetSystem (EfiResetCold, EFI_SUCCESS, 0, NULL);
// Shouldn't get here
DEBUG ((EFI_D_ERROR, "Fastboot: gRT->Resetystem didn't work\n"));
return;
}
/*Function to get high priority bootable slot
*Note: Updates the BootableSlot with high
*priority boot slot. If no high priority slot
*avaiable and all slots marked as unbootable,
*then update BootableSlot with recovery.
*/
VOID FindBootableSlot(CHAR16 *BootableSlot, UINT32 BootableSlotSizeMax)
{
/* Only two slots are supported */
UINT32 RetryCount = 0;
INT32 Index;
UINT32 SlotUnbootable = 0;
UINT32 i;
UINT32 BootLun = 0;
struct PartitionEntry *PartEntryPtr;
UINT32 UfsBootLun = 0;
BOOLEAN UfsGet = TRUE;
TryNextSlot:
FindPtnActiveSlot();
/* If we are here after marking the current slot as unbootable, then we
* switch the slots for the entire bootchain so we are booting all the images
* from the new slot and reboot the device so that bootchain is picked from new slot
*/
if (SlotUnbootable)
MarkSlotUnbootable();
StrnCpyS(BootableSlot, BootableSlotSizeMax, L"boot", StrLen(L"boot"));
StrnCatS(BootableSlot, BootableSlotSizeMax, CurrentSlot, StrLen(CurrentSlot));
UfsGetSetBootLun(&UfsBootLun,UfsGet);
if (UfsBootLun == 0x1 && !StrCmp(CurrentSlot, L"_a"))
DEBUG((EFI_D_INFO,"Booting from slot (%s) , BootableSlot = %s\n", CurrentSlot, BootableSlot));
else if (UfsBootLun == 0x2 && !StrCmp(CurrentSlot, L"_b"))
DEBUG((EFI_D_INFO,"Booting from slot (%s) , BootableSlot = %s\n", CurrentSlot, BootableSlot));
else {
DEBUG((EFI_D_ERROR,"Boot lun: %x and Currentslot: %s do not match\n", UfsBootLun, CurrentSlot));
*BootableSlot = '\0';
return;
}
Index = GetPartitionIndex(BootableSlot);
if (Index == INVALID_PTN) {
DEBUG((EFI_D_ERROR, "Invalid partition index for BootableSlot=%s \n", BootableSlot));
return;
}
PartEntryPtr = &PtnEntries[Index];
/*if slot_successful is set do normal bootup*/
if (PartEntryPtr->PartEntry.Attributes & PART_ATT_SUCCESSFUL_VAL) {
return;
} else {
/*if retry-count > 0,decrement it, do normal boot*/
if((RetryCount = ((PartEntryPtr->PartEntry.Attributes & PART_ATT_MAX_RETRY_COUNT_VAL) >> PART_ATT_MAX_RETRY_CNT_BIT))) {
DEBUG((EFI_D_INFO, "Continue booting without decrementing retry count =%d\n", RetryCount));
} else {
/*else mark slot as unbootable update fields then go for next slot*/
PartEntryPtr->PartEntry.Attributes |= PART_ATT_UNBOOTABLE_VAL & ~PART_ATT_ACTIVE_VAL & ~PART_ATT_PRIORITY_VAL;
StrnCpyS(BootableSlot, BootableSlotSizeMax, "", StrLen(""));
SlotUnbootable++;
goto TryNextSlot;
}
}
UpdatePartitionAttributes();
}
STATIC INTN PartitionVerifyMbrSignature(UINT32 Sz, UINT8 *Gpt)
{
if ((MBR_SIGNATURE + 1) > Sz)
{
DEBUG((EFI_D_ERROR, "Gpt Image size is invalid\n"));
return FAILURE;
}
/* Check for the signature */
if ((Gpt[MBR_SIGNATURE] != MBR_SIGNATURE_BYTE_0) ||
(Gpt[MBR_SIGNATURE + 1] != MBR_SIGNATURE_BYTE_1))
{
DEBUG((EFI_D_ERROR, "MBR signature do not match\n"));
return FAILURE;
}
return SUCCESS;
}
STATIC INTN MbrGetPartitionType(UINT32 Sz, UINT8 *Gpt, UINT32 *Ptype)
{
UINT32 PtypeOffset = MBR_PARTITION_RECORD + OS_TYPE;
if (Sz < (PtypeOffset + sizeof(*Ptype)))
{
DEBUG((EFI_D_ERROR, "Input gpt image does not have gpt partition record data\n"));
return FAILURE;
}
*Ptype = Gpt[PtypeOffset];
return SUCCESS;
}
STATIC INTN PartitionGetType(UINT32 Sz, UINT8 *Gpt, UINT32 *Ptype)
{
INTN Ret;
Ret = PartitionVerifyMbrSignature(Sz, Gpt);
if (!Ret)
{
/* MBR signature match, this coulb be MBR, MBR + EBR or GPT */
Ret = MbrGetPartitionType(Sz, Gpt, Ptype);
if (!Ret)
{
if (*Ptype == GPT_PROTECTIVE)
*Ptype = PARTITION_TYPE_GPT;
else
*Ptype = PARTITION_TYPE_MBR;
}
}
else
{
/* This could be GPT back up */
*Ptype = PARTITION_TYPE_GPT_BACKUP;
Ret = SUCCESS;
}
return Ret;
}
STATIC INTN ParseGptHeader(struct GptHeaderData *GptHeader, UINT8 *GptBuffer, UINTN DeviceDensity, UINT32 BlkSz)
{
UINT32 CrcOrig;
UINT32 CrcVal;
UINT32 CurrentLba;
EFI_STATUS Status;
if (((UINT32 *) GptBuffer)[0] != GPT_SIGNATURE_2 || ((UINT32 *) GptBuffer)[1] != GPT_SIGNATURE_1)
{
DEBUG((EFI_D_ERROR, "Gpt signature is not correct\n"));
return FAILURE;
}
GptHeader->HeaderSz = GET_LWORD_FROM_BYTE(&GptBuffer[HEADER_SIZE_OFFSET]);
/* Validate the header size */
if (GptHeader->HeaderSz < GPT_HEADER_SIZE)
{
DEBUG((EFI_D_ERROR, "GPT Header size is too small: %u\n", GptHeader->HeaderSz));
return FAILURE;
}
if (GptHeader->HeaderSz > BlkSz)
{
DEBUG((EFI_D_ERROR, "GPT Header is too large: %u\n", GptHeader->HeaderSz));
return FAILURE;
}
CrcOrig = GET_LWORD_FROM_BYTE(&GptBuffer[HEADER_CRC_OFFSET]);
/* CRC value is computed by setting this field to 0, and computing the 32-bit CRC for HeaderSize bytes */
CrcVal = 0;
PUT_LONG(&GptBuffer[HEADER_CRC_OFFSET], CrcVal);
Status = gBS->CalculateCrc32(GptBuffer, GptHeader->HeaderSz, &CrcVal);
if (Status != EFI_SUCCESS)
{
DEBUG((EFI_D_ERROR, "Error Calculating CRC32 on the Gpt header: %x\n", Status));
return FAILURE;
}
if (CrcVal != CrcOrig)
{
DEBUG((EFI_D_ERROR, "Header CRC mismatch CrcVal = %u and CrcOrig = %u\n", CrcVal, CrcOrig));
return FAILURE;
}
else
PUT_LONG(&GptBuffer[HEADER_CRC_OFFSET], CrcVal);
CurrentLba = GET_LLWORD_FROM_BYTE(&GptBuffer[PRIMARY_HEADER_OFFSET]);
GptHeader->FirstUsableLba = GET_LLWORD_FROM_BYTE(&GptBuffer[FIRST_USABLE_LBA_OFFSET]);
GptHeader->MaxPtCnt = GET_LWORD_FROM_BYTE(&GptBuffer[PARTITION_COUNT_OFFSET]);
GptHeader->PartEntrySz = GET_LWORD_FROM_BYTE(&GptBuffer[PENTRY_SIZE_OFFSET]);
GptHeader->LastUsableLba = GET_LLWORD_FROM_BYTE(&GptBuffer[LAST_USABLE_LBA_OFFSET]);
if (!ParseSecondaryGpt)
{
if (CurrentLba != GPT_LBA)
{
DEBUG((EFI_D_ERROR, "GPT first usable LBA mismatch\n"));
return FAILURE;
}
}
/* Check for first lba should be within valid range */
if (GptHeader->FirstUsableLba > (DeviceDensity / BlkSz))
{
DEBUG((EFI_D_ERROR, "FirstUsableLba: %u out of Device capacity\n", GptHeader->FirstUsableLba));
return FAILURE;
}
/* Check for Last lba should be within valid range */
if (GptHeader->LastUsableLba > (DeviceDensity / BlkSz))
{
DEBUG((EFI_D_ERROR, "LastUsableLba: %u out of device capacity\n", GptHeader->LastUsableLba));
return FAILURE;
}
if (GptHeader->PartEntrySz != GPT_PART_ENTRY_SIZE)
{
DEBUG((EFI_D_ERROR, "Invalid partition entry size: %u\n", GptHeader->PartEntrySz));
return FAILURE;
}
if (GptHeader->MaxPtCnt > (MIN_PARTITION_ARRAY_SIZE / (GptHeader->PartEntrySz)))
{
DEBUG((EFI_D_ERROR, "Invalid Max Partition Count: %u\n", GptHeader->MaxPtCnt));
return FAILURE;
}
/* Todo: Check CRC during reading partition table*/
if (!FlashingGpt)
{
}
return SUCCESS;
}
STATIC INTN PatchGpt (
UINT8 *Gpt, UINTN DeviceDensity, UINT32 PartEntryArrSz,
struct GptHeaderData *GptHeader, UINT32 BlkSz)
{
UINT8 *PrimaryGptHeader;
UINT8 *SecondaryGptHeader;
UINTN NumSectors;
UINT32 Offset;
UINT32 TotalPart = 0;
UINT32 LastPartOffset;
UINT8 *PartitionEntryArrStart;
UINT32 CrcVal;
EFI_STATUS Status;
NumSectors = DeviceDensity / BlkSz;
/* Update the primary and backup GPT header offset with the sector location */
PrimaryGptHeader = (Gpt + BlkSz);
/* Patch primary GPT */
PUT_LONG_LONG(PrimaryGptHeader + BACKUP_HEADER_OFFSET, (UINTN) (NumSectors - 1));
PUT_LONG_LONG(PrimaryGptHeader + LAST_USABLE_LBA_OFFSET, (UINTN) (NumSectors - 34));
/* Patch Backup GPT */
Offset = (2 * PartEntryArrSz);
SecondaryGptHeader = Offset + BlkSz + PrimaryGptHeader;
PUT_LONG_LONG(SecondaryGptHeader + PRIMARY_HEADER_OFFSET, (UINTN)1);
PUT_LONG_LONG(SecondaryGptHeader + LAST_USABLE_LBA_OFFSET, (UINTN) (NumSectors - 34));
PUT_LONG_LONG(SecondaryGptHeader + PARTITION_ENTRIES_OFFSET, (UINTN) (NumSectors - 33));
/* Patch the last partition */
while (*(PrimaryGptHeader + BlkSz + TotalPart * PARTITION_ENTRY_SIZE) != 0)
TotalPart++;
LastPartOffset = (TotalPart - 1) * PARTITION_ENTRY_SIZE + PARTITION_ENTRY_LAST_LBA;
PUT_LONG_LONG(PrimaryGptHeader + BlkSz + LastPartOffset, (UINTN) (NumSectors - 34));
PUT_LONG_LONG(PrimaryGptHeader + BlkSz + LastPartOffset + PartEntryArrSz, (UINTN) (NumSectors - 34));
/* Update CRC of the partition entry array for both headers */
PartitionEntryArrStart = PrimaryGptHeader + BlkSz;
Status = gBS->CalculateCrc32(PartitionEntryArrStart, (GptHeader->MaxPtCnt * GptHeader->PartEntrySz), &CrcVal);
if (EFI_ERROR(Status))
{
DEBUG((EFI_D_ERROR, "Error calculating CRC for primary partition entry\n"));
return FAILURE;
}
PUT_LONG(PrimaryGptHeader + PARTITION_CRC_OFFSET, CrcVal);
Status = gBS->CalculateCrc32(PartitionEntryArrStart + PartEntryArrSz, (GptHeader->MaxPtCnt * GptHeader->PartEntrySz), &CrcVal);
if (EFI_ERROR(Status))
{
DEBUG((EFI_D_ERROR, "Error calculating CRC for secondary partition entry\n"));
return FAILURE;
}
PUT_LONG(SecondaryGptHeader + PARTITION_CRC_OFFSET, CrcVal);
/* Clear Header CRC field values & recalculate */
PUT_LONG(PrimaryGptHeader + HEADER_CRC_OFFSET, 0);
Status = gBS->CalculateCrc32(PrimaryGptHeader, GPT_HEADER_SIZE, &CrcVal);
if (EFI_ERROR(Status))
{
DEBUG((EFI_D_ERROR, "Error calculating CRC for primary gpt header\n"));
return FAILURE;
}
PUT_LONG(PrimaryGptHeader + HEADER_CRC_OFFSET, CrcVal);
PUT_LONG(SecondaryGptHeader + HEADER_CRC_OFFSET, 0);
Status = gBS->CalculateCrc32(SecondaryGptHeader, GPT_HEADER_SIZE, &CrcVal);
if (EFI_ERROR(Status))
{
DEBUG((EFI_D_ERROR, "Error calculating CRC for secondary gpt header\n"));
return FAILURE;
}
PUT_LONG(SecondaryGptHeader + HEADER_CRC_OFFSET, CrcVal);
return SUCCESS;
}
STATIC INTN WriteGpt(INTN Lun, UINT32 Sz, UINT8 *Gpt)
{
INTN Ret = 1;
struct GptHeaderData GptHeader;
UINTN BackupHeaderLba;
UINT32 MaxPtCnt = 0;
UINT8 *PartEntryArrSt;
UINT32 Offset;
UINT32 PartEntryArrSz;
UINTN DeviceDensity;
UINT32 BlkSz;
UINT8 *PrimaryGptHdr = NULL;
UINT8 *SecondaryGptHdr = NULL;
EFI_STATUS Status;
UINTN BackUpGptLba;
UINTN PartitionEntryLba;
EFI_BLOCK_IO_PROTOCOL *BlockIo = NULL;
HandleInfo BlockIoHandle[MAX_HANDLEINF_LST_SIZE];
UINTN MaxHandles = MAX_HANDLEINF_LST_SIZE;
Ret = GetStorageHandle(Lun, BlockIoHandle, &MaxHandles);
if (Ret || (MaxHandles != 1))
{
DEBUG((EFI_D_ERROR, "Failed to get the BlockIo for the device\n"));
return Ret;
}
BlockIo = BlockIoHandle[0].BlkIo;
DeviceDensity = (BlockIo->Media->LastBlock + 1) * BlockIo->Media->BlockSize;
BlkSz = BlockIo->Media->BlockSize;
/* Verity that passed block has valid GPT primary header */
PrimaryGptHdr = (Gpt + BlkSz);
Ret = ParseGptHeader(&GptHeader, PrimaryGptHdr, DeviceDensity, BlkSz);
if (Ret)
{
DEBUG((EFI_D_ERROR, "GPT: Error processing primary GPT header\n"));
return Ret;
}
/* Check if a valid back up GPT is present */
PartEntryArrSz = GptHeader.PartEntrySz * GptHeader.MaxPtCnt;
if (PartEntryArrSz < MIN_PARTITION_ARRAY_SIZE)
PartEntryArrSz = MIN_PARTITION_ARRAY_SIZE;
/* Back up partition is stored in the reverse order with back GPT, followed by
* part entries, find the offset to back up GPT */
Offset = (2 * PartEntryArrSz);
SecondaryGptHdr = Offset + BlkSz + PrimaryGptHdr;
ParseSecondaryGpt = TRUE;
Ret = ParseGptHeader(&GptHeader, SecondaryGptHdr, DeviceDensity, BlkSz);
if (Ret)
{
DEBUG((EFI_D_ERROR, "GPT: Error processing backup GPT header\n"));
return Ret;
}
Ret = PatchGpt(Gpt, DeviceDensity, PartEntryArrSz, &GptHeader, BlkSz);
if (Ret)
{
DEBUG((EFI_D_ERROR, "Failed to patch GPT\n"));
return Ret;
}
/* Erase the entire card */
Status = ErasePartition(BlockIo, BlockIoHandle[0].Handle);
if (Status != EFI_SUCCESS) {
DEBUG((EFI_D_ERROR, "Error erasing the storage device: %r\n", Status));
return FAILURE;
}
/* write the protective MBR */
Status = BlockIo->WriteBlocks(BlockIo, BlockIo->Media->MediaId, 0, BlkSz, (VOID *)Gpt);
if (EFI_ERROR(Status))
{
DEBUG((EFI_D_ERROR, "Error writing protective MBR: %x\n", Status));
return FAILURE;
}
/* Write the primary GPT header, which is at an offset of BlkSz */
Status = BlockIo->WriteBlocks(BlockIo, BlockIo->Media->MediaId, 1, BlkSz, (VOID *)PrimaryGptHdr);
if (EFI_ERROR(Status))
{
DEBUG((EFI_D_ERROR, "Error writing primary GPT header: %r\n", Status));
return FAILURE;
}
/* Write the back up GPT header */
BackUpGptLba = GET_LLWORD_FROM_BYTE(&PrimaryGptHdr[BACKUP_HEADER_OFFSET]);
Status = BlockIo->WriteBlocks(BlockIo, BlockIo->Media->MediaId, BackUpGptLba, BlkSz, (VOID *)SecondaryGptHdr);
if (EFI_ERROR(Status))
{
DEBUG((EFI_D_ERROR, "Error writing secondary GPT header: %x\n", Status));
return FAILURE;
}
/* write Partition Entries for primary partition table*/
PartEntryArrSt = PrimaryGptHdr + BlkSz;
PartitionEntryLba = GET_LLWORD_FROM_BYTE(&PrimaryGptHdr[PARTITION_ENTRIES_OFFSET]);
Status = BlockIo->WriteBlocks(BlockIo, BlockIo->Media->MediaId, PartitionEntryLba, PartEntryArrSz, (VOID *)PartEntryArrSt);
if (EFI_ERROR(Status))
{
DEBUG((EFI_D_ERROR, "Error writing partition entries array for Primary Table: %x\n", Status));
return FAILURE;
}
/* write Partition Entries for secondary partition table*/
PartEntryArrSt = PrimaryGptHdr + BlkSz + PartEntryArrSz;
PartitionEntryLba = GET_LLWORD_FROM_BYTE(&SecondaryGptHdr[PARTITION_ENTRIES_OFFSET]);
Status = BlockIo->WriteBlocks(BlockIo, BlockIo->Media->MediaId, PartitionEntryLba, PartEntryArrSz, (VOID *)PartEntryArrSt);
if (EFI_ERROR(Status))
{
DEBUG((EFI_D_ERROR, "Error writing partition entries array for Secondary Table: %x\n", Status));
return FAILURE;
}
FlashingGpt = 0;
SetMem((VOID *)PrimaryGptHdr, Sz, 0x0);
DEBUG((EFI_D_ERROR, "Updated Partition Table Successfully\n"));
return SUCCESS;
}
EFI_STATUS UpdatePartitionTable(UINT8 *GptImage, UINT32 Sz, INTN Lun, struct StoragePartInfo *Ptable)
{
EFI_STATUS Status = EFI_SUCCESS;
UINT32 Ptype;
INTN Ret;
/* Check if the partition type is GPT */
Ret = PartitionGetType(Sz, GptImage, &Ptype);
if (Ret != 0)
{
DEBUG((EFI_D_ERROR, "Failed to get partition type from input gpt image\n"));
return EFI_NOT_FOUND;
}
switch (Ptype)
{
case PARTITION_TYPE_GPT:
DEBUG((EFI_D_INFO, "Updating GPT partition\n"));
FlashingGpt = TRUE;
Ret = WriteGpt(Lun, Sz, GptImage);
if (Ret != 0)
{
DEBUG((EFI_D_ERROR, "Failed to write Gpt partition: %x\n", Ret));
return EFI_VOLUME_CORRUPTED;
}
break;
default:
DEBUG((EFI_D_ERROR, "Invalid Partition type: %x\n",Ptype));
Status = EFI_UNSUPPORTED;
break;
}
return Status;
}