mirror of
https://github.com/edk2-porting/edk2-msm
synced 2025-04-29 08:27:58 +00:00
976 lines
32 KiB
C
976 lines
32 KiB
C
/*
|
|
* Copyright (c) 2015-2020, 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 "AutoGen.h"
|
|
#include <Library/BootSlotLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/UefiLib.h>
|
|
#include <Uefi.h>
|
|
#include <Uefi/UefiSpec.h>
|
|
|
|
struct StoragePartInfo Ptable[MAX_LUNS];
|
|
struct PartitionEntry PtnEntries[MAX_NUM_PARTITIONS];
|
|
STATIC UINT32 MaxLuns;
|
|
STATIC UINT32 PartitionCount;
|
|
STATIC struct PartitionEntry PtnEntriesBak[MAX_NUM_PARTITIONS];
|
|
|
|
STATIC struct BootPartsLinkedList *HeadNode;
|
|
STATIC EFI_STATUS GetActiveSlot(Slot *ActiveSlot);
|
|
|
|
Slot GetCurrentSlotSuffix(VOID)
|
|
{
|
|
Slot CurrentSlot = {{0}};
|
|
BOOLEAN IsMultiSlot = PartitionHasMultiSlot((CONST CHAR16 *)L"boot");
|
|
|
|
if (IsMultiSlot == FALSE) {
|
|
return CurrentSlot;
|
|
}
|
|
|
|
GetActiveSlot(&CurrentSlot);
|
|
return CurrentSlot;
|
|
}
|
|
|
|
VOID GetPartitionCount(UINT32 *Val)
|
|
{
|
|
*Val = PartitionCount;
|
|
return;
|
|
}
|
|
|
|
VOID UpdatePartitionEntries(VOID)
|
|
{
|
|
UINT32 i;
|
|
UINT32 j;
|
|
UINT32 Index = 0;
|
|
EFI_STATUS Status;
|
|
EFI_PARTITION_ENTRY *PartEntry;
|
|
|
|
PartitionCount = 0;
|
|
/*Nullify the PtnEntries array before using it*/
|
|
gBS->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 =
|
|
GetPartitionEntry(Ptable[i].HandleInfoList[j].Handle, &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;
|
|
}
|
|
gBS->CopyMem((&PtnEntries[Index]), PartEntry, sizeof(PartEntry[0]));
|
|
PtnEntries[Index].lun = i;
|
|
}
|
|
}
|
|
/* Back up the ptn entries */
|
|
gBS->CopyMem(PtnEntriesBak, PtnEntries, sizeof(PtnEntries));
|
|
}
|
|
|
|
INT32
|
|
GetPartitionIndex(CHAR16 *Pname)
|
|
{
|
|
INT32 i;
|
|
|
|
for (i = 0; i < PartitionCount; i++) {
|
|
if (!StrnCmp(
|
|
PtnEntries[i].PartEntry.PartitionName, Pname,
|
|
ARRAY_SIZE(PtnEntries[i].PartEntry.PartitionName))) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return INVALID_PTN;
|
|
}
|
|
|
|
STATIC EFI_STATUS
|
|
GetStorageHandle(INT32 Lun, HandleInfo *BlockIoHandle, UINT32 *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 = NULL;
|
|
HandleFilter.VolumeName = NULL;
|
|
|
|
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;
|
|
}
|
|
|
|
STATIC BOOLEAN IsUpdatePartitionAttributes()
|
|
{
|
|
if (CompareMem(PtnEntries, PtnEntriesBak, sizeof(PtnEntries))) {
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
UINT64 GetPartitionSize(EFI_BLOCK_IO_PROTOCOL *BlockIo)
|
|
{
|
|
UINT64 PartitionSize;
|
|
|
|
if (!BlockIo) {
|
|
DEBUG((EFI_D_ERROR, "Invalid parameter, pleae check BlockIo info!!!\n"));
|
|
return 0;
|
|
}
|
|
|
|
if (CHECK_ADD64(BlockIo->Media->LastBlock, 1)) {
|
|
DEBUG((EFI_D_ERROR, "Integer overflow while adding LastBlock and 1\n"));
|
|
return 0;
|
|
}
|
|
|
|
if ((MAX_UINT64 / (BlockIo->Media->LastBlock + 1)) <
|
|
(UINT64)BlockIo->Media->BlockSize) {
|
|
DEBUG(
|
|
(EFI_D_ERROR,
|
|
"Integer overflow while multiplying LastBlock and BlockSize\n"));
|
|
return 0;
|
|
}
|
|
|
|
PartitionSize = (BlockIo->Media->LastBlock + 1) * BlockIo->Media->BlockSize;
|
|
return PartitionSize;
|
|
}
|
|
|
|
VOID UpdatePartitionAttributes(UINT32 UpdateType)
|
|
{
|
|
UINT32 BlkSz;
|
|
UINT8 * GptHdr = NULL;
|
|
UINT8 * GptHdrPtr = NULL;
|
|
UINTN MaxGptPartEntrySzBytes;
|
|
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;
|
|
INT32 Lun;
|
|
EFI_BLOCK_IO_PROTOCOL *BlockIo = NULL;
|
|
HandleInfo BlockIoHandle[MAX_HANDLEINF_LST_SIZE];
|
|
UINT32 MaxHandles = MAX_HANDLEINF_LST_SIZE;
|
|
CHAR8 BootDeviceType[BOOT_DEV_NAME_SIZE_MAX];
|
|
UINT32 PartEntriesblocks = 0;
|
|
BOOLEAN SkipUpdation;
|
|
UINT64 Attr;
|
|
struct PartitionEntry *InMemPtnEnt;
|
|
|
|
/* The PtnEntries is the same as PtnEntriesBak by default
|
|
* It needs to update attributes or GUID when PtnEntries is changed
|
|
*/
|
|
if (!IsUpdatePartitionAttributes()) {
|
|
return;
|
|
}
|
|
|
|
GetRootDeviceType(BootDeviceType, BOOT_DEV_NAME_SIZE_MAX);
|
|
for (Lun = 0; Lun < MaxLuns; Lun++) {
|
|
|
|
if (!AsciiStrnCmp(BootDeviceType, "EMMC", AsciiStrLen("EMMC"))) {
|
|
Status = GetStorageHandle(NO_LUN, BlockIoHandle, &MaxHandles);
|
|
}
|
|
else if (!AsciiStrnCmp(BootDeviceType, "UFS", AsciiStrLen("UFS"))) {
|
|
Status = GetStorageHandle(Lun, BlockIoHandle, &MaxHandles);
|
|
}
|
|
else {
|
|
DEBUG((EFI_D_ERROR, "Unsupported boot device type\n"));
|
|
return;
|
|
}
|
|
|
|
if (Status != EFI_SUCCESS) {
|
|
DEBUG(
|
|
(EFI_D_ERROR, "Failed to get BlkIo for device. MaxHandles:%d - %r\n",
|
|
MaxHandles, Status));
|
|
return;
|
|
}
|
|
if (MaxHandles != 1) {
|
|
DEBUG(
|
|
(EFI_D_VERBOSE,
|
|
"Failed to get the BlockIo for device. MaxHandle:%d, %r\n",
|
|
MaxHandles, Status));
|
|
continue;
|
|
}
|
|
|
|
BlockIo = BlockIoHandle[0].BlkIo;
|
|
DeviceDensity = GetPartitionSize(BlockIo);
|
|
if (!DeviceDensity) {
|
|
return;
|
|
}
|
|
BlkSz = BlockIo->Media->BlockSize;
|
|
PartEntriesblocks = MAX_PARTITION_ENTRIES_SZ / BlkSz;
|
|
MaxGptPartEntrySzBytes = (GPT_HDR_BLOCKS + PartEntriesblocks) * BlkSz;
|
|
CardSizeSec = (DeviceDensity) / BlkSz;
|
|
Offset = PRIMARY_HDR_LBA;
|
|
GptHdr = AllocateZeroPool(MaxGptPartEntrySzBytes);
|
|
if (!GptHdr) {
|
|
DEBUG((EFI_D_ERROR, "Unable to Allocate Memory for GptHdr \n"));
|
|
return;
|
|
}
|
|
|
|
GptHdrPtr = GptHdr;
|
|
|
|
/* This loop iterates twice to update both primary and backup Gpt*/
|
|
for (Iter = 0; Iter < 2;
|
|
Iter++, (Offset = CardSizeSec - MaxGptPartEntrySzBytes / BlkSz)) {
|
|
SkipUpdation = TRUE;
|
|
Status = BlockIo->ReadBlocks(
|
|
BlockIo, BlockIo->Media->MediaId, Offset, MaxGptPartEntrySzBytes,
|
|
GptHdr);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
DEBUG((EFI_D_ERROR, "Unable to read the media \n"));
|
|
goto Exit;
|
|
}
|
|
|
|
if (Iter == 0x1) {
|
|
/* This is the back up GPT */
|
|
Ptn_Entries = GptHdr;
|
|
GptHdr = GptHdr + ((PartEntriesblocks)*BlkSz);
|
|
}
|
|
else
|
|
/* otherwise we are at the primary gpt */
|
|
Ptn_Entries = GptHdr + BlkSz;
|
|
|
|
PtnEntriesPtr = Ptn_Entries;
|
|
|
|
for (i = 0; i < PartitionCount; i++) {
|
|
InMemPtnEnt = (struct PartitionEntry *)PtnEntriesPtr;
|
|
/*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;
|
|
}
|
|
|
|
if (!AsciiStrnCmp(BootDeviceType, "UFS", AsciiStrLen("UFS"))) {
|
|
/* 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;
|
|
}
|
|
Attr = GET_LLWORD_FROM_BYTE(&PtnEntriesPtr[ATTRIBUTE_FLAG_OFFSET]);
|
|
if (UpdateType & PARTITION_GUID_MASK) {
|
|
if (CompareMem(
|
|
&InMemPtnEnt->PartEntry.PartitionTypeGUID,
|
|
&PtnEntries[i].PartEntry.PartitionTypeGUID,
|
|
sizeof(EFI_GUID))) {
|
|
/* Update the partition GUID values */
|
|
gBS->CopyMem(
|
|
(VOID *)PtnEntriesPtr,
|
|
(VOID *)&PtnEntries[i].PartEntry.PartitionTypeGUID, GUID_SIZE);
|
|
/* Update the PtnEntriesBak for next comparison */
|
|
gBS->CopyMem(
|
|
(VOID *)&PtnEntriesBak[i].PartEntry.PartitionTypeGUID,
|
|
(VOID *)&PtnEntries[i].PartEntry.PartitionTypeGUID, GUID_SIZE);
|
|
SkipUpdation = FALSE;
|
|
}
|
|
}
|
|
|
|
if (UpdateType & PARTITION_ATTRIBUTES_MASK) {
|
|
/* If GUID is not present, then it is back up GPT, update it
|
|
* If GUID is present, and the GUID is matched, update it
|
|
*/
|
|
if (!(InMemPtnEnt->PartEntry.PartitionTypeGUID.Data1) ||
|
|
!CompareMem(
|
|
&InMemPtnEnt->PartEntry.PartitionTypeGUID,
|
|
&PtnEntries[i].PartEntry.PartitionTypeGUID,
|
|
sizeof(EFI_GUID))) {
|
|
if (Attr != PtnEntries[i].PartEntry.Attributes) {
|
|
/* Update the partition attributes */
|
|
PUT_LONG_LONG(
|
|
&PtnEntriesPtr[ATTRIBUTE_FLAG_OFFSET],
|
|
PtnEntries[i].PartEntry.Attributes);
|
|
/* Update the PtnEntriesBak for next comparison */
|
|
PtnEntriesBak[i].PartEntry.Attributes =
|
|
PtnEntries[i].PartEntry.Attributes;
|
|
SkipUpdation = FALSE;
|
|
}
|
|
}
|
|
else {
|
|
if (InMemPtnEnt->PartEntry.PartitionTypeGUID.Data1) {
|
|
DEBUG((EFI_D_ERROR, "Error in GPT header, GUID is not match!\n"));
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* point to the next partition entry */
|
|
PtnEntriesPtr += PARTITION_ENTRY_SIZE;
|
|
}
|
|
|
|
if (SkipUpdation)
|
|
continue;
|
|
|
|
MaxPtnCount = GET_LWORD_FROM_BYTE(&GptHdr[PARTITION_COUNT_OFFSET]);
|
|
PtnEntrySz = GET_LWORD_FROM_BYTE(&GptHdr[PENTRY_SIZE_OFFSET]);
|
|
|
|
if (((UINT64)(MaxPtnCount)*PtnEntrySz) > MAX_PARTITION_ENTRIES_SZ) {
|
|
DEBUG(
|
|
(EFI_D_ERROR,
|
|
"Invalid GPT header fields MaxPtnCount = %x, PtnEntrySz = %x\n",
|
|
MaxPtnCount, PtnEntrySz));
|
|
goto Exit;
|
|
}
|
|
|
|
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));
|
|
goto Exit;
|
|
}
|
|
|
|
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));
|
|
goto Exit;
|
|
}
|
|
|
|
PUT_LONG(&GptHdr[HEADER_CRC_OFFSET], CrcVal);
|
|
|
|
if (Iter == 0x1)
|
|
/* Write the backup GPT header, which is at an offset of CardSizeSec -
|
|
* MaxGptPartEntrySzBytes/BlkSz in blocks*/
|
|
Status = BlockIo->WriteBlocks(
|
|
BlockIo, BlockIo->Media->MediaId, Offset, MaxGptPartEntrySzBytes,
|
|
(VOID *)Ptn_Entries);
|
|
else
|
|
/* Write the primary GPT header, which is at an offset of BlkSz */
|
|
Status = BlockIo->WriteBlocks(
|
|
BlockIo, BlockIo->Media->MediaId, Offset, MaxGptPartEntrySzBytes,
|
|
(VOID *)GptHdr);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
DEBUG((EFI_D_ERROR, "Error writing primary GPT header: %r\n", Status));
|
|
goto Exit;
|
|
}
|
|
}
|
|
FreePool(GptHdrPtr);
|
|
GptHdrPtr = NULL;
|
|
}
|
|
|
|
Exit:
|
|
if (GptHdrPtr) {
|
|
FreePool(GptHdrPtr);
|
|
GptHdrPtr = NULL;
|
|
}
|
|
}
|
|
|
|
STATIC 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(PARTITION_ATTRIBUTES);
|
|
}
|
|
|
|
STATIC VOID SwapPtnGuid(EFI_PARTITION_ENTRY *p1, EFI_PARTITION_ENTRY *p2)
|
|
{
|
|
EFI_GUID Temp;
|
|
|
|
if (p1 == NULL || p2 == NULL)
|
|
return;
|
|
gBS->CopyMem((VOID *)&Temp, (VOID *)&p1->PartitionTypeGUID, sizeof(EFI_GUID));
|
|
gBS->CopyMem(
|
|
(VOID *)&p1->PartitionTypeGUID, (VOID *)&p2->PartitionTypeGUID,
|
|
sizeof(EFI_GUID));
|
|
gBS->CopyMem((VOID *)&p2->PartitionTypeGUID, (VOID *)&Temp, sizeof(EFI_GUID));
|
|
}
|
|
|
|
STATIC EFI_STATUS GetMultiSlotPartsList(VOID)
|
|
{
|
|
UINT32 i = 0;
|
|
UINT32 j = 0;
|
|
UINT32 Len = 0;
|
|
UINT32 PtnLen = 0;
|
|
CHAR16 * SearchString = NULL;
|
|
struct BootPartsLinkedList *TempNode = NULL;
|
|
|
|
for (i = 0; i < PartitionCount; i++) {
|
|
SearchString = PtnEntries[i].PartEntry.PartitionName;
|
|
if (!SearchString[0])
|
|
continue;
|
|
|
|
for (j = i + 1; j < PartitionCount; j++) {
|
|
if (!PtnEntries[j].PartEntry.PartitionName[0])
|
|
continue;
|
|
Len = StrLen(SearchString);
|
|
PtnLen = StrLen(PtnEntries[j].PartEntry.PartitionName);
|
|
|
|
/*Need to compare till "boot_"a hence skip last Char from StrLen value*/
|
|
if ((PtnLen == Len) &&
|
|
!StrnCmp(
|
|
PtnEntries[j].PartEntry.PartitionName, SearchString, Len - 1) &&
|
|
(StrStr(SearchString, (CONST CHAR16 *)L"_a") ||
|
|
StrStr(SearchString, (CONST CHAR16 *)L"_b"))) {
|
|
TempNode = AllocateZeroPool(sizeof(struct BootPartsLinkedList));
|
|
if (TempNode) {
|
|
/*Skip _a/_b from partition name*/
|
|
StrnCpyS(
|
|
TempNode->PartName, sizeof(TempNode->PartName), SearchString,
|
|
Len - 2);
|
|
TempNode->Next = HeadNode;
|
|
HeadNode = TempNode;
|
|
}
|
|
else {
|
|
DEBUG(
|
|
(EFI_D_ERROR,
|
|
"Unable to Allocate Memory for MultiSlot Partition list\n"));
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
STATIC VOID SwitchPtnSlots(CONST CHAR16 *SetActive)
|
|
{
|
|
UINT32 i;
|
|
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;
|
|
struct BootPartsLinkedList *TempNode = NULL;
|
|
EFI_STATUS Status;
|
|
CHAR8 BootDeviceType[BOOT_DEV_NAME_SIZE_MAX];
|
|
|
|
/* Create the partition name string for active and non active slots*/
|
|
if (!StrnCmp(SetActive, (CONST CHAR16 *)L"_a", StrLen((CONST CHAR16 *)L"_a")))
|
|
StrnCpyS(
|
|
SetInactive, MAX_SLOT_SUFFIX_SZ, (CONST CHAR16 *)L"_b",
|
|
StrLen((CONST CHAR16 *)L"_b"));
|
|
else
|
|
StrnCpyS(
|
|
SetInactive, MAX_SLOT_SUFFIX_SZ, (CONST CHAR16 *)L"_a",
|
|
StrLen((CONST CHAR16 *)L"_a"));
|
|
|
|
if (!HeadNode) {
|
|
Status = GetMultiSlotPartsList();
|
|
if (Status != EFI_SUCCESS) {
|
|
DEBUG((EFI_D_INFO, "Unable to get GetMultiSlotPartsList\n"));
|
|
return;
|
|
}
|
|
}
|
|
|
|
for (TempNode = HeadNode; TempNode; TempNode = TempNode->Next) {
|
|
gBS->SetMem(CurSlot, BOOT_PART_SIZE, 0);
|
|
gBS->SetMem(NewSlot, BOOT_PART_SIZE, 0);
|
|
|
|
StrnCpyS(
|
|
CurSlot, BOOT_PART_SIZE, TempNode->PartName,
|
|
StrLen(TempNode->PartName));
|
|
StrnCatS(CurSlot, BOOT_PART_SIZE, SetInactive, StrLen(SetInactive));
|
|
|
|
StrnCpyS(
|
|
NewSlot, BOOT_PART_SIZE, TempNode->PartName,
|
|
StrLen(TempNode->PartName));
|
|
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);
|
|
PtnCurrent = PtnNew = NULL;
|
|
}
|
|
|
|
GetRootDeviceType(BootDeviceType, BOOT_DEV_NAME_SIZE_MAX);
|
|
if (!AsciiStrnCmp(BootDeviceType, "UFS", AsciiStrLen("UFS"))) {
|
|
UfsGetSetBootLun(&UfsBootLun, UfsGet);
|
|
// Special case for XBL is to change the bootlun instead of swapping the
|
|
// guid
|
|
if (UfsBootLun == 0x1 &&
|
|
!StrnCmp(
|
|
SetActive, (CONST CHAR16 *)L"_b", StrLen((CONST CHAR16 *)L"_b"))) {
|
|
DEBUG((EFI_D_INFO, "Switching the boot lun from 1 to 2\n"));
|
|
UfsBootLun = 0x2;
|
|
}
|
|
else if (
|
|
UfsBootLun == 0x2 &&
|
|
!StrnCmp(
|
|
SetActive, (CONST CHAR16 *)L"_a", StrLen((CONST CHAR16 *)L"_a"))) {
|
|
DEBUG((EFI_D_INFO, "Switching the boot lun from 2 to 1\n"));
|
|
UfsBootLun = 0x1;
|
|
}
|
|
UfsGetSetBootLun(&UfsBootLun, UfsSet);
|
|
}
|
|
|
|
UpdatePartitionAttributes(PARTITION_GUID);
|
|
}
|
|
|
|
EFI_STATUS
|
|
EnumeratePartitions(VOID)
|
|
{
|
|
EFI_STATUS Status;
|
|
PartiSelectFilter HandleFilter;
|
|
UINT32 Attribs = 0;
|
|
UINT32 i;
|
|
// UFS LUN GUIDs
|
|
EFI_GUID LunGuids[] = {
|
|
gEfiUfsLU0Guid, gEfiUfsLU1Guid, gEfiUfsLU2Guid, gEfiUfsLU3Guid,
|
|
gEfiUfsLU4Guid, gEfiUfsLU5Guid, gEfiUfsLU6Guid, gEfiUfsLU7Guid,
|
|
};
|
|
|
|
gBS->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 = NULL;
|
|
HandleFilter.VolumeName = NULL;
|
|
HandleFilter.RootDeviceType = &gEfiEmmcUserPartitionGuid;
|
|
|
|
Status = GetBlkIOHandles(
|
|
Attribs, &HandleFilter, &Ptable[0].HandleInfoList[0],
|
|
&Ptable[0].MaxHandles);
|
|
if (Status == EFI_SUCCESS && Ptable[0].MaxHandles > 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 = NULL;
|
|
HandleFilter.VolumeName = NULL;
|
|
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 SlotCount = 0;
|
|
UINT32 Len = StrLen(Pname);
|
|
|
|
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'))
|
|
if (++SlotCount > MIN_SLOTS) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
STATIC struct PartitionEntry *GetBootPartitionEntry(Slot *BootSlot)
|
|
{
|
|
INT32 Index = INVALID_PTN;
|
|
|
|
if (StrnCmp(
|
|
(CONST CHAR16 *)L"_a", BootSlot->Suffix, StrLen(BootSlot->Suffix)) ==
|
|
0) {
|
|
Index = GetPartitionIndex((CHAR16 *)L"boot_a");
|
|
}
|
|
else if (
|
|
StrnCmp(
|
|
(CONST CHAR16 *)L"_b", BootSlot->Suffix, StrLen(BootSlot->Suffix)) ==
|
|
0) {
|
|
Index = GetPartitionIndex((CHAR16 *)L"boot_b");
|
|
}
|
|
else {
|
|
DEBUG(
|
|
(EFI_D_ERROR,
|
|
"GetBootPartitionEntry: No boot partition "
|
|
"entry for slot %s\n",
|
|
BootSlot->Suffix));
|
|
return NULL;
|
|
}
|
|
|
|
if (Index == INVALID_PTN) {
|
|
DEBUG(
|
|
(EFI_D_ERROR,
|
|
"GetBootPartitionEntry: No boot partition entry "
|
|
"for slot %s, invalid index\n",
|
|
BootSlot->Suffix));
|
|
return NULL;
|
|
}
|
|
return &PtnEntries[Index];
|
|
}
|
|
|
|
BOOLEAN IsSlotBootable(Slot *BootSlot)
|
|
{
|
|
struct PartitionEntry *BootPartition = NULL;
|
|
BootPartition = GetBootPartitionEntry(BootSlot);
|
|
if (BootPartition == NULL) {
|
|
DEBUG(
|
|
(EFI_D_ERROR,
|
|
"IsCurrentSlotBootable: No boot partition "
|
|
"entry for slot %s\n",
|
|
BootSlot->Suffix));
|
|
return FALSE;
|
|
}
|
|
DEBUG(
|
|
(EFI_D_VERBOSE, "Slot suffix %s Part Attr 0x%lx\n", BootSlot->Suffix,
|
|
BootPartition->PartEntry.Attributes));
|
|
|
|
if (!(BootPartition->PartEntry.Attributes & PART_ATT_UNBOOTABLE_VAL) &&
|
|
BootPartition->PartEntry.Attributes & PART_ATT_SUCCESSFUL_VAL) {
|
|
DEBUG((EFI_D_VERBOSE, "Slot %s is bootable\n", BootSlot->Suffix));
|
|
return TRUE;
|
|
}
|
|
|
|
DEBUG((EFI_D_VERBOSE, "Slot %s is unbootable \n", BootSlot->Suffix));
|
|
return FALSE;
|
|
}
|
|
|
|
EFI_STATUS ClearUnbootable(Slot *BootSlot)
|
|
{
|
|
struct PartitionEntry *BootEntry = NULL;
|
|
BootEntry = GetBootPartitionEntry(BootSlot);
|
|
if (BootEntry == NULL) {
|
|
DEBUG(
|
|
(EFI_D_ERROR, "ClearUnbootable: No boot partition entry for slot %s\n",
|
|
BootSlot->Suffix));
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
BootEntry->PartEntry.Attributes &= ~PART_ATT_UNBOOTABLE_VAL;
|
|
BootEntry->PartEntry.Attributes |= PART_ATT_MAX_RETRY_COUNT_VAL;
|
|
UpdatePartitionAttributes(PARTITION_ATTRIBUTES);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
BOOLEAN
|
|
IsSuffixEmpty(Slot *CheckSlot)
|
|
{
|
|
if (CheckSlot == NULL) {
|
|
return TRUE;
|
|
}
|
|
|
|
if (StrLen(CheckSlot->Suffix) == 0) {
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
STATIC EFI_STATUS GetActiveSlot(Slot *ActiveSlot)
|
|
{
|
|
EFI_STATUS Status = EFI_SUCCESS;
|
|
Slot Slots[] = {{L"_a"}, {L"_b"}};
|
|
UINT64 Priority = 0;
|
|
|
|
if (ActiveSlot == NULL) {
|
|
DEBUG((EFI_D_ERROR, "GetActiveSlot: bad parameter\n"));
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
for (UINTN SlotIndex = 0; SlotIndex < ARRAY_SIZE(Slots); SlotIndex++) {
|
|
struct PartitionEntry *BootPartition =
|
|
GetBootPartitionEntry(&Slots[SlotIndex]);
|
|
UINT64 BootPriority = 0;
|
|
if (BootPartition == NULL) {
|
|
DEBUG(
|
|
(EFI_D_ERROR,
|
|
"GetActiveSlot: No boot partition "
|
|
"entry for slot %s\n",
|
|
Slots[SlotIndex].Suffix));
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
BootPriority =
|
|
(BootPartition->PartEntry.Attributes & PART_ATT_PRIORITY_VAL) >>
|
|
PART_ATT_PRIORITY_BIT;
|
|
|
|
if ((BootPartition->PartEntry.Attributes & PART_ATT_ACTIVE_VAL) &&
|
|
(BootPriority > Priority)) {
|
|
GUARD(StrnCpyS(
|
|
ActiveSlot->Suffix, ARRAY_SIZE(ActiveSlot->Suffix),
|
|
Slots[SlotIndex].Suffix, StrLen(Slots[SlotIndex].Suffix)));
|
|
Priority = BootPriority;
|
|
}
|
|
}
|
|
|
|
DEBUG(
|
|
(EFI_D_VERBOSE, "GetActiveSlot: found active slot %s, priority %d\n",
|
|
ActiveSlot->Suffix, Priority));
|
|
|
|
if (IsSuffixEmpty(ActiveSlot) == TRUE) {
|
|
/* Check for first boot and set default slot */
|
|
/* For First boot all A/B attributes for the slot would be 0 */
|
|
UINT64 BootPriority = 0;
|
|
UINT64 RetryCount = 0;
|
|
struct PartitionEntry *SlotA = GetBootPartitionEntry(&Slots[0]);
|
|
if (SlotA == NULL) {
|
|
DEBUG(
|
|
(EFI_D_ERROR,
|
|
"GetActiveSlot: First Boot: No boot partition "
|
|
"entry for slot %s\n",
|
|
Slots[0].Suffix));
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
BootPriority = (SlotA->PartEntry.Attributes & PART_ATT_PRIORITY_VAL) >>
|
|
PART_ATT_PRIORITY_BIT;
|
|
RetryCount = (SlotA->PartEntry.Attributes & PART_ATT_MAX_RETRY_COUNT_VAL) >>
|
|
PART_ATT_MAX_RETRY_CNT_BIT;
|
|
|
|
if ((SlotA->PartEntry.Attributes & PART_ATT_ACTIVE_VAL) == 0 &&
|
|
(SlotA->PartEntry.Attributes & PART_ATT_SUCCESSFUL_VAL) == 0 &&
|
|
(SlotA->PartEntry.Attributes & PART_ATT_UNBOOTABLE_VAL) == 0 &&
|
|
BootPriority == 0) {
|
|
|
|
DEBUG(
|
|
(EFI_D_INFO, "GetActiveSlot: First boot: set "
|
|
"default slot _a\n"));
|
|
SlotA->PartEntry.Attributes &=
|
|
(~PART_ATT_SUCCESSFUL_VAL & ~PART_ATT_UNBOOTABLE_VAL);
|
|
SlotA->PartEntry.Attributes |=
|
|
(PART_ATT_PRIORITY_VAL | PART_ATT_ACTIVE_VAL |
|
|
PART_ATT_MAX_RETRY_COUNT_VAL);
|
|
|
|
GUARD(StrnCpyS(
|
|
ActiveSlot->Suffix, ARRAY_SIZE(ActiveSlot->Suffix), Slots[0].Suffix,
|
|
StrLen(Slots[0].Suffix)));
|
|
UpdatePartitionAttributes(PARTITION_ATTRIBUTES);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
DEBUG((EFI_D_ERROR, "GetActiveSlot: No active slot found\n"));
|
|
DEBUG(
|
|
(EFI_D_ERROR,
|
|
"GetActiveSlot: Slot attr: Priority %ld, Retry "
|
|
"%ld, Active %ld, Success %ld, unboot %ld\n",
|
|
BootPriority, RetryCount,
|
|
(SlotA->PartEntry.Attributes & PART_ATT_ACTIVE_VAL) >>
|
|
PART_ATT_ACTIVE_BIT,
|
|
(SlotA->PartEntry.Attributes & PART_ATT_SUCCESSFUL_VAL),
|
|
(SlotA->PartEntry.Attributes & PART_ATT_UNBOOTABLE_VAL)));
|
|
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
SetActiveSlot(Slot *NewSlot, BOOLEAN ResetSuccessBit, BOOLEAN SetSuccessBit)
|
|
{
|
|
EFI_STATUS Status = EFI_SUCCESS;
|
|
Slot CurrentSlot = {{0}};
|
|
Slot * AlternateSlot = NULL;
|
|
Slot Slots[] = {{L"_a"}, {L"_b"}};
|
|
BOOLEAN UfsGet = TRUE;
|
|
BOOLEAN UfsSet = FALSE;
|
|
UINT32 UfsBootLun = 0;
|
|
CHAR8 BootDeviceType[BOOT_DEV_NAME_SIZE_MAX];
|
|
struct PartitionEntry *BootEntry = NULL;
|
|
|
|
if (NewSlot == NULL) {
|
|
DEBUG((EFI_D_ERROR, "SetActiveSlot: input parameter invalid\n"));
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
GUARD(GetActiveSlot(&CurrentSlot));
|
|
|
|
if (StrnCmp(NewSlot->Suffix, Slots[0].Suffix, StrLen(Slots[0].Suffix)) == 0) {
|
|
AlternateSlot = &Slots[1];
|
|
}
|
|
else {
|
|
AlternateSlot = &Slots[0];
|
|
}
|
|
|
|
BootEntry = GetBootPartitionEntry(NewSlot);
|
|
if (BootEntry == NULL) {
|
|
DEBUG(
|
|
(EFI_D_ERROR, "SetActiveSlot: No boot partition entry for slot %s\n",
|
|
NewSlot->Suffix));
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
BootEntry->PartEntry.Attributes |=
|
|
(PART_ATT_PRIORITY_VAL | PART_ATT_ACTIVE_VAL |
|
|
PART_ATT_MAX_RETRY_COUNT_VAL);
|
|
|
|
BootEntry->PartEntry.Attributes &= (~PART_ATT_UNBOOTABLE_VAL);
|
|
|
|
if (ResetSuccessBit &&
|
|
(BootEntry->PartEntry.Attributes & PART_ATT_SUCCESSFUL_VAL)) {
|
|
BootEntry->PartEntry.Attributes &= (~PART_ATT_SUCCESSFUL_VAL);
|
|
}
|
|
|
|
if (SetSuccessBit &&
|
|
((BootEntry->PartEntry.Attributes & PART_ATT_SUCCESSFUL_VAL) == 0)) {
|
|
BootEntry->PartEntry.Attributes |= PART_ATT_SUCCESSFUL_VAL;
|
|
}
|
|
|
|
/* Reduce the priority and clear the active flag for alternate slot*/
|
|
BootEntry = GetBootPartitionEntry(AlternateSlot);
|
|
if (BootEntry == NULL) {
|
|
DEBUG(
|
|
(EFI_D_ERROR, "SetActiveSlot: No boot partition entry for slot %s\n",
|
|
AlternateSlot->Suffix));
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
BootEntry->PartEntry.Attributes &=
|
|
(~PART_ATT_PRIORITY_VAL & ~PART_ATT_ACTIVE_VAL);
|
|
BootEntry->PartEntry.Attributes |=
|
|
(((UINT64)MAX_PRIORITY - 1) << PART_ATT_PRIORITY_BIT);
|
|
|
|
UpdatePartitionAttributes(PARTITION_ATTRIBUTES);
|
|
if (StrnCmp(
|
|
CurrentSlot.Suffix, NewSlot->Suffix, StrLen(CurrentSlot.Suffix)) ==
|
|
0) {
|
|
DEBUG(
|
|
(EFI_D_INFO, "SetActiveSlot: %s already active slot\n",
|
|
NewSlot->Suffix));
|
|
|
|
/* Check if BootLun is matching with Slot */
|
|
GetRootDeviceType(BootDeviceType, BOOT_DEV_NAME_SIZE_MAX);
|
|
if (!AsciiStrnCmp(BootDeviceType, "UFS", AsciiStrLen("UFS"))) {
|
|
UfsGetSetBootLun(&UfsBootLun, UfsGet);
|
|
if (UfsBootLun == 0x1 && !StrnCmp(
|
|
CurrentSlot.Suffix, (CONST CHAR16 *)L"_b",
|
|
StrLen((CONST CHAR16 *)L"_b"))) {
|
|
DEBUG((EFI_D_INFO, "Boot lun mismatch switch from 1 to 2\n"));
|
|
DEBUG((EFI_D_INFO, "Reboot Required\n"));
|
|
UfsBootLun = 0x2;
|
|
UfsGetSetBootLun(&UfsBootLun, UfsSet);
|
|
}
|
|
else if (
|
|
UfsBootLun == 0x2 && !StrnCmp(
|
|
CurrentSlot.Suffix, (CONST CHAR16 *)L"_a",
|
|
StrLen((CONST CHAR16 *)L"_a"))) {
|
|
DEBUG((EFI_D_INFO, "Boot lun mismatch switch from 2 to 1\n"));
|
|
DEBUG((EFI_D_INFO, "Reboot Required\n"));
|
|
UfsBootLun = 0x1;
|
|
UfsGetSetBootLun(&UfsBootLun, UfsSet);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
DEBUG(
|
|
(EFI_D_INFO, "Alternate slot %s, New slot %s\n", AlternateSlot->Suffix,
|
|
NewSlot->Suffix));
|
|
SwitchPtnSlots(NewSlot->Suffix);
|
|
MarkPtnActive(NewSlot->Suffix);
|
|
}
|
|
return EFI_SUCCESS;
|
|
} |