0
0
mirror of https://github.com/edk2-porting/edk2-msm synced 2025-04-29 08:27:58 +00:00
Files
2022-11-13 21:31:31 +08:00

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;
}