slimbootloader/BootloaderCommonPkg/Library/PartitionLib/PartitionLib.c

668 lines
19 KiB
C

/** @file
Routines supporting partition discovery
Copyright (c) 2006 - 2022, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <PiPei.h>
#include <IndustryStandard/Mbr.h>
#include <Uefi/UefiGpt.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/Crc32Lib.h>
#include <Library/DebugLib.h>
#include <Library/PartitionLib.h>
#include <Library/MediaAccessLib.h>
//
// Extract INT32 from char array
//
#define UNPACK_INT32(a) \
(INT32) ((((UINT8 *) a)[0] << 0) | (((UINT8 *) a)[1] << 8) | (((UINT8 *) a)[2] << 16) | (((UINT8 *) a)[3] << 24))
//
// Extract UINT32 from char array
//
#define UNPACK_UINT32(a) \
(UINT32) ((((UINT8 *) a)[0] << 0) | (((UINT8 *) a)[1] << 8) | (((UINT8 *) a)[2] << 16) | (((UINT8 *) a)[3] << 24))
CHAR8 *mPartTypeName[] = {
"UNKNOWN",
"MBR",
"GPT"
};
extern
EFI_STATUS
FindSpiPartitions (
IN OUT PART_BLOCK_DEVICE *PartBlockDev
);
/**
Convert partition type into readable string.
@param Type Partition type
@retval Ascii string for partition type
**/
CHAR8 *
GetPartitionTypeName (
IN UINTN Type
)
{
if (Type >= EnumPartTypeMax) {
Type = EnumPartTypeUnknown;
}
return mPartTypeName[Type];
}
/**
Test to see if the Mbr buffer is a valid MBR.
@param Mbr Parent Handle
@param LastLba Last Lba address on the device.
@retval TRUE Mbr is a Valid MBR
@retval FALSE Mbr is not a Valid MBR
**/
BOOLEAN
PartitionValidMbr (
IN MASTER_BOOT_RECORD *Mbr,
IN EFI_PEI_LBA LastLba
)
{
UINT32 StartingLBA;
UINT32 EndingLBA;
UINT32 NewEndingLBA;
INTN Index1;
INTN Index2;
BOOLEAN MbrValid;
if (Mbr->Signature != MBR_SIGNATURE) {
return FALSE;
}
//
// The BPB also has this signature, so it can not be used alone.
//
MbrValid = FALSE;
for (Index1 = 0; Index1 < MAX_MBR_PARTITIONS; Index1++) {
if (Mbr->Partition[Index1].OSIndicator == 0x00 || UNPACK_UINT32 (Mbr->Partition[Index1].SizeInLBA) == 0) {
continue;
}
MbrValid = TRUE;
StartingLBA = UNPACK_UINT32 (Mbr->Partition[Index1].StartingLBA);
EndingLBA = StartingLBA + UNPACK_UINT32 (Mbr->Partition[Index1].SizeInLBA) - 1;
if (EndingLBA > LastLba) {
//
// Compatability Errata:
// Some systems try to hide drive space with thier INT 13h driver
// This does not hide space from the OS driver. This means the MBR
// that gets created from DOS is smaller than the MBR created from
// a real OS (NT & Win98). This leads to BlockIo->LastBlock being
// wrong on some systems FDISKed by the OS.
//
// return FALSE Because no block devices on a system are implemented
// with INT 13h
//
return FALSE;
}
for (Index2 = Index1 + 1; Index2 < MAX_MBR_PARTITIONS; Index2++) {
if (Mbr->Partition[Index2].OSIndicator == 0x00 || UNPACK_INT32 (Mbr->Partition[Index2].SizeInLBA) == 0) {
continue;
}
NewEndingLBA = UNPACK_UINT32 (Mbr->Partition[Index2].StartingLBA) + UNPACK_UINT32 (Mbr->Partition[Index2].SizeInLBA) -
1;
if (NewEndingLBA >= StartingLBA && UNPACK_UINT32 (Mbr->Partition[Index2].StartingLBA) <= EndingLBA) {
//
// This region overlaps with the Index1'th region
//
return FALSE;
}
}
}
//
// Non of the regions overlapped so MBR is O.K.
//
return MbrValid;
}
/**
This function finds Mbr partitions. Main algorithm
is ported from DXE partition driver.
@param[in] PartBlockDev Parition block device pointer
@retval EFI_SUCCESS New partitions are detected and logical block devices
are added to block device array
@retval EFI_NOT_FOUND No New partitions are added
@retval EFI_DEVICE_ERROR Error occured for device access
**/
EFI_STATUS
FindMbrPartitions (
IN PART_BLOCK_DEVICE *PartBlockDev
)
{
EFI_STATUS Status;
MASTER_BOOT_RECORD *Mbr;
UINTN Index;
LOGICAL_BLOCK_DEVICE *BlockDev;
UINTN ParentBlockDevNo;
DEVICE_BLOCK_INFO *DevBlockInfo;
DevBlockInfo = &PartBlockDev->BlockInfo;
ParentBlockDevNo = PartBlockDev->HarewareDevice;
Mbr = (MASTER_BOOT_RECORD *) PartBlockDev->BlockData;
Status = MediaReadBlocks (
ParentBlockDevNo,
0,
DevBlockInfo->BlockSize,
Mbr
);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
Status = EFI_NOT_FOUND;
if (!PartitionValidMbr (Mbr, DevBlockInfo->BlockNum - 1)) {
return Status;
}
//
// We have a valid mbr - add each partition
//
for (Index = 0; Index < MAX_MBR_PARTITIONS; Index++) {
if (Mbr->Partition[Index].OSIndicator == 0x00 || UNPACK_INT32 (Mbr->Partition[Index].SizeInLBA) == 0) {
//
// Don't use null MBR entries
//
continue;
}
//
// Register this partition
//
if (PartBlockDev->BlockDeviceCount < PART_MAX_BLOCK_DEVICE) {
Status = EFI_SUCCESS;
BlockDev = & (PartBlockDev->BlockDevice[PartBlockDev->BlockDeviceCount]);
BlockDev->StartBlock = UNPACK_UINT32 (Mbr->Partition[Index].StartingLBA);
BlockDev->LastBlock = BlockDev->StartBlock + UNPACK_INT32 (Mbr->Partition[Index].SizeInLBA) - 1;
PartBlockDev->BlockDeviceCount++;
}
}
if (!EFI_ERROR (Status)) {
PartBlockDev->PartitionType = EnumPartTypeMbr;
}
PartBlockDev->PartitionChecked = TRUE;
return Status;
}
/**
Test to see if the GPT Entry Array CRC is valid.
@param GptEntries GPT Entry Array Buffer
@param Gpt GPT Header info
@param ReadSize Size of the GPT Array Buffer in bytes
@param DevBlockInfo Device block info
@param ParentBlockDevNo Hardware partition of the device to read
from
@retval TRUE GPT Entry Array has a valid CRC
@retval FALSE GPT Entry Array does not have
a valid CRC
**/
BOOLEAN
PartitionValidGptEntryArray (
IN OUT EFI_PARTITION_ENTRY *GptEntries,
IN EFI_PARTITION_TABLE_HEADER *Gpt,
IN UINTN ReadSize,
IN DEVICE_BLOCK_INFO *DevBlockInfo,
IN UINTN ParentBlockDevNo
)
{
UINT32 CrcOut;
EFI_STATUS Status;
EFI_LBA GptLba;
GptLba = Gpt->PartitionEntryLBA;
Status = MediaReadBlocks (ParentBlockDevNo, GptLba, ReadSize, GptEntries);
if (EFI_ERROR (Status)) {
return FALSE;
}
Status = CalculateCrc32WithType ((UINT8 *)GptEntries, ReadSize, Crc32TypeDefault, &CrcOut);
if (EFI_ERROR (Status) || (CrcOut != Gpt->PartitionEntryArrayCRC32)) {
return FALSE;
}
return TRUE;
}
/**
Test to see if the GPT Header CRC is valid.
@param Gpt GPT Header info
@param DevBlockInfo Device block info
@param ParentBlockDevNo Hardware partition of the device to read
from
@param Primary Inidicator to check primary or secondary
GPT Header
@retval TRUE GPT Header has a valid CRC
@retval FALSE GPT Header does not have
a valid CRC
**/
BOOLEAN
PartitionValidGpt (
IN OUT EFI_PARTITION_TABLE_HEADER *Gpt,
IN DEVICE_BLOCK_INFO *DevBlockInfo,
IN UINTN ParentBlockDevNo,
IN BOOLEAN Primary
)
{
UINT32 CrcOut;
EFI_STATUS Status;
EFI_PARTITION_TABLE_HEADER GptBuff;
Status = MediaReadBlocks (
ParentBlockDevNo,
(Primary) ? 1 : DevBlockInfo->BlockNum - 1,
DevBlockInfo->BlockSize,
Gpt
);
if (EFI_ERROR (Status)) {
return FALSE;
}
CopyMem (&GptBuff, Gpt, sizeof (EFI_PARTITION_TABLE_HEADER));
GptBuff.Header.CRC32 = 0;
Status = CalculateCrc32WithType ((UINT8 *) (&GptBuff), sizeof (EFI_PARTITION_TABLE_HEADER), Crc32TypeDefault, &CrcOut);
if (EFI_ERROR (Status) || (CrcOut != Gpt->Header.CRC32)) {
return FALSE;
}
return TRUE;
}
/**
This function finds Gpt partitions. Main algorithm
is ported from DXE partition driver.
@param[in] PartBlockDev Parition block device pointer
@retval EFI_SUCCESS New partitions are detected and logical block devices
are added to block device array
@retval EFI_NOT_FOUND No New partitions are added
@retval EFI_DEVICE_ERROR Error occured for device access
@retval EFI_CRC_ERROR Error occured with primary and secondary GPT CRC
@retval EFI_UNSUPPORTED The number of GPT entries exceeds the maximum number allowed
**/
EFI_STATUS
FindGptPartitions (
IN PART_BLOCK_DEVICE *PartBlockDev
)
{
EFI_STATUS Status;
UINTN Index;
UINTN ReadSize;
UINTN GptPartCnt;
UINTN ParentBlockDevNo;
LOGICAL_BLOCK_DEVICE *BlockDev;
MASTER_BOOT_RECORD *Mbr;
UINTN ProtectMbrStartLba;
UINTN ProtectMbrLbaSize;
EFI_PARTITION_TABLE_HEADER *Gpt;
UINT64 MaxBlkNum;
BOOLEAN IsGptValid;
BOOLEAN SecondaryHeader;
EFI_PARTITION_ENTRY *GptEntries;
DEVICE_BLOCK_INFO *DevBlockInfo;
DevBlockInfo = &PartBlockDev->BlockInfo;
ParentBlockDevNo = PartBlockDev->HarewareDevice;
SecondaryHeader = FALSE;
GptEntries = NULL;
if (DevBlockInfo->BlockSize == 0 || DevBlockInfo->BlockNum == 0) {
Status = EFI_INVALID_PARAMETER;
goto Done;
}
MaxBlkNum = DevBlockInfo->BlockNum;
Mbr = (MASTER_BOOT_RECORD *) PartBlockDev->BlockData;
Status = MediaReadBlocks (
ParentBlockDevNo,
0,
DevBlockInfo->BlockSize,
Mbr
);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
if (!PartitionValidMbr (Mbr, DevBlockInfo->BlockNum - 1)) {
DEBUG ((DEBUG_WARN, "Protective MBR is not valid format!\n"));
} else {
ProtectMbrStartLba = * ((UINT32 *) (Mbr->Partition[0].StartingLBA));
ProtectMbrLbaSize = * ((UINT32 *) (Mbr->Partition[0].SizeInLBA));
if ((ProtectMbrStartLba != 1) || (ProtectMbrLbaSize != (MaxBlkNum - 1))) {
DEBUG ((DEBUG_WARN, "Protective MBR does not protect the whole drive!\n"));
}
}
Gpt = (EFI_PARTITION_TABLE_HEADER *) PartBlockDev->BlockData;
IsGptValid = PartitionValidGpt (Gpt, DevBlockInfo, ParentBlockDevNo, TRUE);
if (!IsGptValid) {
SecondaryHeader = TRUE;
DEBUG ((DEBUG_WARN, "Primary GPT Header CalculateCrc32 Error!\n"));
IsGptValid = PartitionValidGpt (Gpt, DevBlockInfo, ParentBlockDevNo, FALSE);
if (!IsGptValid) {
DEBUG ((DEBUG_WARN, "Secondary GPT Header CalculateCrc32 Error!\n"));
Status = EFI_CRC_ERROR;
goto Done;
}
}
ReadSize = Gpt->NumberOfPartitionEntries * Gpt->SizeOfPartitionEntry;
ReadSize = (ReadSize % DevBlockInfo->BlockSize) == 0 ? ReadSize : DevBlockInfo->BlockSize * ((
ReadSize / DevBlockInfo->BlockSize) + 1);
GptEntries = (EFI_PARTITION_ENTRY *) AllocatePool (ReadSize);
if (GptEntries == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto Done;
}
IsGptValid = PartitionValidGptEntryArray (GptEntries, Gpt, ReadSize, DevBlockInfo, ParentBlockDevNo);
if (!IsGptValid) {
DEBUG ((DEBUG_ERROR, "GPT Array CalculateCrc32 Error!\n"));
if (SecondaryHeader) {
DEBUG ((DEBUG_ERROR, "Primary Header and Secondary GPT Array data is corrupt!\n"));
Status = EFI_CRC_ERROR;
goto Done;
}
IsGptValid = PartitionValidGpt (Gpt, DevBlockInfo, ParentBlockDevNo, FALSE);
if (!IsGptValid) {
DEBUG ((DEBUG_ERROR, "Secondary GPT Header CalculateCrc32 Error!\n"));
Status = EFI_CRC_ERROR;
goto Done;
}
IsGptValid = PartitionValidGptEntryArray (GptEntries, Gpt, ReadSize, DevBlockInfo, ParentBlockDevNo);
if (!IsGptValid) {
DEBUG ((DEBUG_ERROR, "Secondary GPT Array CalculateCrc32 Error!\n"));
Status = EFI_CRC_ERROR;
goto Done;
}
}
GptPartCnt = Gpt->NumberOfPartitionEntries;
Status = EFI_NOT_FOUND;
for (Index = 0; Index < GptPartCnt; Index++) {
if (CompareGuid (&GptEntries[Index].PartitionTypeGUID, &gEfiPartTypeUnusedGuid)) {
continue;
}
if (PartBlockDev->BlockDeviceCount < PART_MAX_BLOCK_DEVICE) {
Status = EFI_SUCCESS;
BlockDev = & (PartBlockDev->BlockDevice[PartBlockDev->BlockDeviceCount]);
BlockDev->StartBlock = GptEntries[Index].StartingLBA;
BlockDev->LastBlock = GptEntries[Index].EndingLBA;
DEBUG ((DEBUG_INFO, "Part %02d: %12s ", PartBlockDev->BlockDeviceCount, GptEntries[Index].PartitionName));
DEBUG ((DEBUG_INFO, "0x%08x--0x%08x, LBA count: 0x%x\n", (UINT32)BlockDev->StartBlock, \
(UINT32)BlockDev->LastBlock, (UINT32) (BlockDev->LastBlock - BlockDev->StartBlock + 1)));
PartBlockDev->BlockDeviceCount++;
}
}
Done:
if (GptEntries != NULL) {
FreePool (GptEntries);
}
if (!EFI_ERROR (Status)) {
PartBlockDev->PartitionType = EnumPartTypeGpt;
}
PartBlockDev->PartitionChecked = TRUE;
return Status;
}
/**
Get logical partition information.
This function will fill in logical partition information.
@param[in] SwPart Software partition index.
@param[in] PartHandle Partition handle.
@param[in,out] LogicBlkDev Pointer of the logic block device LOGICAL_BLOCK_DEVICE
structure to receive block start and end information.
@retval EFI_SUCCESS The logic partition information was filled successfully.
@retval EFI_INVALID_PARAMETER Invalid parameters.
**/
EFI_STATUS
EFIAPI
GetLogicalPartitionInfo (
IN UINT32 SwPart,
IN EFI_HANDLE PartHandle,
IN OUT LOGICAL_BLOCK_DEVICE *LogicBlkDev
)
{
PART_BLOCK_DEVICE *PartBlockDev;
// Validate parameters
PartBlockDev = (PART_BLOCK_DEVICE *)PartHandle;
if ( (LogicBlkDev == NULL) || (PartBlockDev == NULL) || \
(PartBlockDev->Signature != PART_INFO_SIGNATURE) ) {
return EFI_INVALID_PARAMETER;
}
if ((SwPart >= PartBlockDev->BlockDeviceCount) || (SwPart >= PART_MAX_BLOCK_DEVICE)) {
return EFI_INVALID_PARAMETER;
}
CopyMem (LogicBlkDev, &PartBlockDev->BlockDevice[SwPart], sizeof (LOGICAL_BLOCK_DEVICE));
return EFI_SUCCESS;
}
/**
Find partitions from OS boot medium
This function will check hardware partition for MBR, GPT or NONE parition.
@param[in] HwDevice The hardware device index.
@param[out] PartHandle The pointer to return parition handle
@retval EFI_SUCCESS The partition was found successfully.
@retval EFI_NOT_FOUND No partition was found.
@retval EFI_DEVICE_ERROR Error occured for device access.
@retval EFI_INVALID_PARAMETER Invalid parameters.
@retval EFI_OUT_OF_RESOURCES Insufficant memory resource pool.
**/
EFI_STATUS
EFIAPI
FindPartitions (
IN UINT32 HwDevice,
OUT EFI_HANDLE *PartHandle
)
{
EFI_STATUS Status;
DEVICE_BLOCK_INFO DevBlockInfo;
UINT8 *Buffer;
LOGICAL_BLOCK_DEVICE *BlockDev;
EFI_PARTITION_TABLE_HEADER *Gpt;
PART_BLOCK_DEVICE *PartBlockDev;
OS_BOOT_MEDIUM_TYPE CurrentMediaType;
BOOLEAN IsGptValid;
if (PartHandle == NULL) {
return EFI_INVALID_PARAMETER;
}
Status = MediaGetMediaInfo (HwDevice, &DevBlockInfo);
if (EFI_ERROR (Status)) {
return Status;
}
PartBlockDev = (PART_BLOCK_DEVICE *) AllocateZeroPool (sizeof (PART_BLOCK_DEVICE));
if (PartBlockDev == NULL) {
return EFI_OUT_OF_RESOURCES;
}
PartBlockDev->Signature = PART_INFO_SIGNATURE;
PartBlockDev->HarewareDevice = HwDevice;
CopyMem (&PartBlockDev->BlockInfo, &DevBlockInfo, sizeof (DEVICE_BLOCK_INFO));
CurrentMediaType = MediaGetInterfaceType();
if (CurrentMediaType == OsBootDeviceMemory || CurrentMediaType == OsBootDeviceSpi) {
// It is memory mapped SPI block device.
Status = FindSpiPartitions (PartBlockDev);
if (!EFI_ERROR (Status)) {
*PartHandle = (EFI_HANDLE)PartBlockDev;
}
return Status;
}
// Check primary GPT Partition first
Buffer = (UINT8 *) PartBlockDev->BlockData;
Gpt = (EFI_PARTITION_TABLE_HEADER *)Buffer;
IsGptValid = PartitionValidGpt (Gpt, &DevBlockInfo, PartBlockDev->HarewareDevice, TRUE);
if (!IsGptValid) {
// Check backup GPT as well
IsGptValid = PartitionValidGpt (Gpt, &DevBlockInfo, PartBlockDev->HarewareDevice, FALSE);
}
if (IsGptValid && ((UINT32)Gpt->Header.Signature == 0x20494645)) {
Status = FindGptPartitions (PartBlockDev);
} else {
Status = FindMbrPartitions (PartBlockDev);
}
// Check result
if (EFI_ERROR (Status)) {
// Could not find any partition, so assume no partitions
BlockDev = & (PartBlockDev->BlockDevice[0]);
BlockDev->StartBlock = 0;
BlockDev->LastBlock = DevBlockInfo.BlockNum - 1;
PartBlockDev->PartitionType = EnumPartTypeUnknown;
PartBlockDev->PartitionChecked = TRUE;
PartBlockDev->BlockDeviceCount = 1;
Status = EFI_SUCCESS;
}
DEBUG ((DEBUG_INFO, "Partition type: %a (%d logical partitions)\n", \
GetPartitionTypeName (PartBlockDev->PartitionType), \
PartBlockDev->BlockDeviceCount));
*PartHandle = (EFI_HANDLE)PartBlockDev;
return Status;
}
/**
Clean-up allocated memory/etc. used for partitions
@param[in] PartHandle The partition handle to clean-up
@retval none
**/
VOID
EFIAPI
ClosePartitions (
IN EFI_HANDLE PartHandle
)
{
PART_BLOCK_DEVICE *PartBlockDev;
PartBlockDev = NULL;
if (PartHandle != NULL) {
PartBlockDev = (PART_BLOCK_DEVICE *)PartHandle;
ASSERT (PartBlockDev->Signature == PART_INFO_SIGNATURE);
}
if ((PartBlockDev != NULL) && (PartBlockDev->Signature == PART_INFO_SIGNATURE)) {
FreePool (PartBlockDev);
}
}
/**
Get detected partition type
@param[in] PartHandle The partition handle to clean-up
@retval Partition type enum
**/
PARTITION_TYPE
EFIAPI
GetPartitionType (
IN EFI_HANDLE PartHandle
)
{
PART_BLOCK_DEVICE *PartBlockDev;
PARTITION_TYPE PartType;
PartType = EnumPartTypeMax;
PartBlockDev = (PART_BLOCK_DEVICE *)PartHandle;
if ((PartBlockDev != NULL) && (PartBlockDev->Signature == PART_INFO_SIGNATURE)) {
PartType = PartBlockDev->PartitionType;
}
return PartType;
}
/**
Get HW part no of the detected partition
@param[in] PartHandle The partition handle to clean-up
@param[in] HwPartNo HW part no.
@retval EFI_SUCCESS Found SW part no.
@retval EFI_INVALID_PARAMETER Invalid PartHandle
**/
EFI_STATUS
EFIAPI
GetPartitionCurrentPartNo (
IN EFI_HANDLE PartHandle,
OUT UINT32 *HwPartNo
)
{
PART_BLOCK_DEVICE *PartBlockDev;
PartBlockDev = (PART_BLOCK_DEVICE *)PartHandle;
if ((PartBlockDev == NULL) || (PartBlockDev->Signature != PART_INFO_SIGNATURE)) {
return EFI_INVALID_PARAMETER;
}
*HwPartNo = PartBlockDev->HarewareDevice;
return EFI_SUCCESS;
}