diff --git a/MdeModulePkg/Include/Library/GptLib.h b/MdeModulePkg/Include/Library/GptLib.h new file mode 100644 index 0000000000..404d8630b7 --- /dev/null +++ b/MdeModulePkg/Include/Library/GptLib.h @@ -0,0 +1,97 @@ +/** @file + Shared GUID Partition Table (GPT) parsing and validation routines. + + These routines decode and validate a disk partitioned with the GPT scheme + as described in the UEFI specification. They are shared between the + Partition driver (which installs child handles) and other consumers that + need to parse the same on-disk GPT layout. + + Caution: These routines may receive untrusted input. The GPT partition + table is external input and must be validated carefully to avoid security + issues like buffer overflow and integer overflow. + +Copyright (c) 2026, SUSE LLC. All rights reserved.
+Copyright (c) 2018 Qualcomm Datacenter Technologies, Inc. +Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#pragma once + +#include +#include +#include +#include + +// +// GPT Partition Entry Status +// +typedef struct { + BOOLEAN OutOfRange; + BOOLEAN Overlap; + BOOLEAN OsSpecific; +} EFI_PARTITION_ENTRY_STATUS; + +/** + Read GPT partition table header from the given LBA and validate it. + + Caution: This function may receive untrusted input. + The GPT partition table header is external input, so this routine + will do basic validation for GPT partition table header before return. + + @param[in] BlockIo Parent BlockIo interface. + @param[in] DiskIo Disk Io protocol. + @param[in] Lba The starting Lba of the Partition Table. + @param[out] PartHeader Stores the partition table that is read. + + @retval TRUE The partition table is valid. + @retval FALSE The partition table is not valid. + +**/ +BOOLEAN +PartitionValidGptTable ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN EFI_LBA Lba, + OUT EFI_PARTITION_TABLE_HEADER *PartHeader + ); + +/** + Restore Partition Table to its alternate place + (Primary -> Backup or Backup -> Primary). + + @param[in] BlockIo Parent BlockIo interface. + @param[in] DiskIo Disk Io Protocol. + @param[in] PartHeader Partition table header structure. + + @retval TRUE Restoring succeeds. + @retval FALSE Restoring failed. + +**/ +BOOLEAN +PartitionRestoreGptTable ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN EFI_PARTITION_TABLE_HEADER *PartHeader + ); + +/** + Check GPT partition entries and report the status of each entry. + + Caution: This function may receive untrusted input. + The GPT partition entry is external input, so this routine + will do basic validation for GPT partition entry and report status. + + @param[in] PartHeader Partition table header structure. + @param[in] PartEntry The partition entry array. + @param[out] PEntryStatus The partition entry status array + recording the status of each partition. + +**/ +VOID +PartitionCheckGptEntry ( + IN EFI_PARTITION_TABLE_HEADER *PartHeader, + IN EFI_PARTITION_ENTRY *PartEntry, + OUT EFI_PARTITION_ENTRY_STATUS *PEntryStatus + ); diff --git a/MdeModulePkg/Library/GptLib/Gpt.c b/MdeModulePkg/Library/GptLib/Gpt.c new file mode 100644 index 0000000000..e62a2299f5 --- /dev/null +++ b/MdeModulePkg/Library/GptLib/Gpt.c @@ -0,0 +1,548 @@ +/** @file + Decode a hard disk partitioned with the GPT scheme in the UEFI 2.0 + specification. + + Caution: This file requires additional review when modified. + This driver will have external input - disk partition. + This external input must be validated carefully to avoid security issue like + buffer overflow, integer overflow. + + PartitionValidGptTable(), PartitionCheckGptEntry() routine will accept disk + partition content and validate the GPT table and GPT entry. + +Copyright (c) 2026, SUSE LLC. All rights reserved.
+Copyright (c) 2018 Qualcomm Datacenter Technologies, Inc. +Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include +#include + +/** + Check if the CRC field in the Partition table header is valid + for Partition entry array. + + @param[in] BlockIo Parent BlockIo interface + @param[in] DiskIo Disk Io Protocol. + @param[in] PartHeader Partition table header structure + + @retval TRUE the CRC is valid + @retval FALSE the CRC is invalid + +**/ +STATIC +BOOLEAN +PartitionCheckGptEntryArrayCRC ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN EFI_PARTITION_TABLE_HEADER *PartHeader + ); + +/** + Checks the CRC32 value in the table header. + + @param MaxSize Max Size limit + @param Size The size of the table + @param Hdr Table to check + + @return TRUE CRC Valid + @return FALSE CRC Invalid + +**/ +STATIC +BOOLEAN +PartitionCheckCrcAltSize ( + IN UINTN MaxSize, + IN UINTN Size, + IN OUT EFI_TABLE_HEADER *Hdr + ); + +/** + Checks the CRC32 value in the table header. + + @param MaxSize Max Size limit + @param Hdr Table to check + + @return TRUE CRC Valid + @return FALSE CRC Invalid + +**/ +STATIC +BOOLEAN +PartitionCheckCrc ( + IN UINTN MaxSize, + IN OUT EFI_TABLE_HEADER *Hdr + ); + +/** + Updates the CRC32 value in the table header. + + @param Size The size of the table + @param Hdr Table to update + +**/ +STATIC +VOID +PartitionSetCrcAltSize ( + IN UINTN Size, + IN OUT EFI_TABLE_HEADER *Hdr + ); + +/** + Updates the CRC32 value in the table header. + + @param Hdr Table to update + +**/ +STATIC +VOID +PartitionSetCrc ( + IN OUT EFI_TABLE_HEADER *Hdr + ); + +/** + This routine will read GPT partition table header and return it. + + Caution: This function may receive untrusted input. + The GPT partition table header is external input, so this routine + will do basic validation for GPT partition table header before return. + + @param[in] BlockIo Parent BlockIo interface. + @param[in] DiskIo Disk Io protocol. + @param[in] Lba The starting Lba of the Partition Table + @param[out] PartHeader Stores the partition table that is read + + @retval TRUE The partition table is valid + @retval FALSE The partition table is not valid + +**/ +BOOLEAN +PartitionValidGptTable ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN EFI_LBA Lba, + OUT EFI_PARTITION_TABLE_HEADER *PartHeader + ) +{ + EFI_STATUS Status; + UINT32 BlockSize; + EFI_PARTITION_TABLE_HEADER *PartHdr; + UINT32 MediaId; + + BlockSize = BlockIo->Media->BlockSize; + MediaId = BlockIo->Media->MediaId; + PartHdr = AllocateZeroPool (BlockSize); + + if (PartHdr == NULL) { + DEBUG ((DEBUG_ERROR, "Allocate pool error\n")); + return FALSE; + } + + // + // Read the EFI Partition Table Header + // + Status = DiskIo->ReadDisk ( + DiskIo, + MediaId, + MultU64x32 (Lba, BlockSize), + BlockSize, + PartHdr + ); + if (EFI_ERROR (Status)) { + FreePool (PartHdr); + return FALSE; + } + + if ((PartHdr->Header.Signature != EFI_PTAB_HEADER_ID) || + !PartitionCheckCrc (BlockSize, &PartHdr->Header) || + (PartHdr->MyLBA != Lba) || + (PartHdr->SizeOfPartitionEntry < sizeof (EFI_PARTITION_ENTRY)) + ) + { + DEBUG ((DEBUG_INFO, "Invalid efi partition table header\n")); + FreePool (PartHdr); + return FALSE; + } + + // + // Ensure the NumberOfPartitionEntries * SizeOfPartitionEntry doesn't overflow. + // + if (PartHdr->NumberOfPartitionEntries > DivU64x32 (MAX_UINTN, PartHdr->SizeOfPartitionEntry)) { + FreePool (PartHdr); + return FALSE; + } + + CopyMem (PartHeader, PartHdr, sizeof (EFI_PARTITION_TABLE_HEADER)); + if (!PartitionCheckGptEntryArrayCRC (BlockIo, DiskIo, PartHeader)) { + FreePool (PartHdr); + return FALSE; + } + + DEBUG ((DEBUG_INFO, " Valid efi partition table header\n")); + FreePool (PartHdr); + return TRUE; +} + +/** + Check if the CRC field in the Partition table header is valid + for Partition entry array. + + @param[in] BlockIo Parent BlockIo interface + @param[in] DiskIo Disk Io Protocol. + @param[in] PartHeader Partition table header structure + + @retval TRUE the CRC is valid + @retval FALSE the CRC is invalid + +**/ +STATIC +BOOLEAN +PartitionCheckGptEntryArrayCRC ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN EFI_PARTITION_TABLE_HEADER *PartHeader + ) +{ + EFI_STATUS Status; + UINT8 *Ptr; + UINT32 Crc; + UINTN Size; + + // + // Read the EFI Partition Entries + // + Ptr = AllocatePool (PartHeader->NumberOfPartitionEntries * PartHeader->SizeOfPartitionEntry); + if (Ptr == NULL) { + DEBUG ((DEBUG_ERROR, " Allocate pool error\n")); + return FALSE; + } + + Status = DiskIo->ReadDisk ( + DiskIo, + BlockIo->Media->MediaId, + MultU64x32 (PartHeader->PartitionEntryLBA, BlockIo->Media->BlockSize), + PartHeader->NumberOfPartitionEntries * PartHeader->SizeOfPartitionEntry, + Ptr + ); + if (EFI_ERROR (Status)) { + FreePool (Ptr); + return FALSE; + } + + Size = PartHeader->NumberOfPartitionEntries * PartHeader->SizeOfPartitionEntry; + + Status = gBS->CalculateCrc32 (Ptr, Size, &Crc); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "CheckPEntryArrayCRC: Crc calculation failed\n")); + FreePool (Ptr); + return FALSE; + } + + FreePool (Ptr); + + return (BOOLEAN)(PartHeader->PartitionEntryArrayCRC32 == Crc); +} + +/** + Restore Partition Table to its alternate place + (Primary -> Backup or Backup -> Primary). + + @param[in] BlockIo Parent BlockIo interface. + @param[in] DiskIo Disk Io Protocol. + @param[in] PartHeader Partition table header structure. + + @retval TRUE Restoring succeeds + @retval FALSE Restoring failed + +**/ +BOOLEAN +PartitionRestoreGptTable ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN EFI_PARTITION_TABLE_HEADER *PartHeader + ) +{ + EFI_STATUS Status; + UINTN BlockSize; + EFI_PARTITION_TABLE_HEADER *PartHdr; + EFI_LBA PEntryLBA; + UINT8 *Ptr; + UINT32 MediaId; + + PartHdr = NULL; + Ptr = NULL; + + BlockSize = BlockIo->Media->BlockSize; + MediaId = BlockIo->Media->MediaId; + + PartHdr = AllocateZeroPool (BlockSize); + + if (PartHdr == NULL) { + DEBUG ((DEBUG_ERROR, "Allocate pool error\n")); + return FALSE; + } + + PEntryLBA = (PartHeader->MyLBA == PRIMARY_PART_HEADER_LBA) ? \ + (PartHeader->LastUsableLBA + 1) : \ + (PRIMARY_PART_HEADER_LBA + 1); + + CopyMem (PartHdr, PartHeader, sizeof (EFI_PARTITION_TABLE_HEADER)); + + PartHdr->MyLBA = PartHeader->AlternateLBA; + PartHdr->AlternateLBA = PartHeader->MyLBA; + PartHdr->PartitionEntryLBA = PEntryLBA; + PartitionSetCrc ((EFI_TABLE_HEADER *)PartHdr); + + Status = DiskIo->WriteDisk ( + DiskIo, + MediaId, + MultU64x32 (PartHdr->MyLBA, (UINT32)BlockSize), + BlockSize, + PartHdr + ); + if (EFI_ERROR (Status)) { + goto Done; + } + + Ptr = AllocatePool (PartHeader->NumberOfPartitionEntries * PartHeader->SizeOfPartitionEntry); + if (Ptr == NULL) { + DEBUG ((DEBUG_ERROR, " Allocate pool error\n")); + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + Status = DiskIo->ReadDisk ( + DiskIo, + MediaId, + MultU64x32 (PartHeader->PartitionEntryLBA, (UINT32)BlockSize), + PartHeader->NumberOfPartitionEntries * PartHeader->SizeOfPartitionEntry, + Ptr + ); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = DiskIo->WriteDisk ( + DiskIo, + MediaId, + MultU64x32 (PEntryLBA, (UINT32)BlockSize), + PartHeader->NumberOfPartitionEntries * PartHeader->SizeOfPartitionEntry, + Ptr + ); + +Done: + FreePool (PartHdr); + + if (Ptr != NULL) { + FreePool (Ptr); + } + + if (EFI_ERROR (Status)) { + return FALSE; + } + + return TRUE; +} + +/** + This routine will check GPT partition entry and return entry status. + + Caution: This function may receive untrusted input. + The GPT partition entry is external input, so this routine + will do basic validation for GPT partition entry and report status. + + @param[in] PartHeader Partition table header structure + @param[in] PartEntry The partition entry array + @param[out] PEntryStatus the partition entry status array + recording the status of each partition + +**/ +VOID +PartitionCheckGptEntry ( + IN EFI_PARTITION_TABLE_HEADER *PartHeader, + IN EFI_PARTITION_ENTRY *PartEntry, + OUT EFI_PARTITION_ENTRY_STATUS *PEntryStatus + ) +{ + EFI_LBA StartingLBA; + EFI_LBA EndingLBA; + EFI_PARTITION_ENTRY *Entry; + UINTN Index1; + UINTN Index2; + + DEBUG ((DEBUG_INFO, " start check partition entries\n")); + for (Index1 = 0; Index1 < PartHeader->NumberOfPartitionEntries; Index1++) { + Entry = (EFI_PARTITION_ENTRY *)((UINT8 *)PartEntry + Index1 * PartHeader->SizeOfPartitionEntry); + if (CompareGuid (&Entry->PartitionTypeGUID, &gEfiPartTypeUnusedGuid)) { + continue; + } + + StartingLBA = Entry->StartingLBA; + EndingLBA = Entry->EndingLBA; + if ((StartingLBA > EndingLBA) || + (StartingLBA < PartHeader->FirstUsableLBA) || + (StartingLBA > PartHeader->LastUsableLBA) || + (EndingLBA < PartHeader->FirstUsableLBA) || + (EndingLBA > PartHeader->LastUsableLBA) + ) + { + PEntryStatus[Index1].OutOfRange = TRUE; + continue; + } + + if ((Entry->Attributes & BIT1) != 0) { + // + // If Bit 1 is set, this indicate that this is an OS specific GUID partition. + // + PEntryStatus[Index1].OsSpecific = TRUE; + } + + for (Index2 = Index1 + 1; Index2 < PartHeader->NumberOfPartitionEntries; Index2++) { + Entry = (EFI_PARTITION_ENTRY *)((UINT8 *)PartEntry + Index2 * PartHeader->SizeOfPartitionEntry); + if (CompareGuid (&Entry->PartitionTypeGUID, &gEfiPartTypeUnusedGuid)) { + continue; + } + + if ((Entry->EndingLBA >= StartingLBA) && (Entry->StartingLBA <= EndingLBA)) { + // + // This region overlaps with the Index1'th region + // + PEntryStatus[Index1].Overlap = TRUE; + PEntryStatus[Index2].Overlap = TRUE; + continue; + } + } + } + + DEBUG ((DEBUG_INFO, " End check partition entries\n")); +} + +/** + Updates the CRC32 value in the table header. + + @param Hdr Table to update + +**/ +STATIC +VOID +PartitionSetCrc ( + IN OUT EFI_TABLE_HEADER *Hdr + ) +{ + PartitionSetCrcAltSize (Hdr->HeaderSize, Hdr); +} + +/** + Updates the CRC32 value in the table header. + + @param Size The size of the table + @param Hdr Table to update + +**/ +STATIC +VOID +PartitionSetCrcAltSize ( + IN UINTN Size, + IN OUT EFI_TABLE_HEADER *Hdr + ) +{ + UINT32 Crc; + + Hdr->CRC32 = 0; + gBS->CalculateCrc32 ((UINT8 *)Hdr, Size, &Crc); + Hdr->CRC32 = Crc; +} + +/** + Checks the CRC32 value in the table header. + + @param MaxSize Max Size limit + @param Hdr Table to check + + @return TRUE CRC Valid + @return FALSE CRC Invalid + +**/ +STATIC +BOOLEAN +PartitionCheckCrc ( + IN UINTN MaxSize, + IN OUT EFI_TABLE_HEADER *Hdr + ) +{ + return PartitionCheckCrcAltSize (MaxSize, Hdr->HeaderSize, Hdr); +} + +/** + Checks the CRC32 value in the table header. + + @param MaxSize Max Size limit + @param Size The size of the table + @param Hdr Table to check + + @return TRUE CRC Valid + @return FALSE CRC Invalid + +**/ +STATIC +BOOLEAN +PartitionCheckCrcAltSize ( + IN UINTN MaxSize, + IN UINTN Size, + IN OUT EFI_TABLE_HEADER *Hdr + ) +{ + UINT32 Crc; + UINT32 OrgCrc; + EFI_STATUS Status; + + Crc = 0; + + if (Size == 0) { + // + // If header size is 0 CRC will pass so return FALSE here + // + return FALSE; + } + + if ((MaxSize != 0) && (Size > MaxSize)) { + DEBUG ((DEBUG_ERROR, "CheckCrc32: Size > MaxSize\n")); + return FALSE; + } + + // + // clear old crc from header + // + OrgCrc = Hdr->CRC32; + Hdr->CRC32 = 0; + + Status = gBS->CalculateCrc32 ((UINT8 *)Hdr, Size, &Crc); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "CheckCrc32: Crc calculation failed\n")); + return FALSE; + } + + // + // set results + // + Hdr->CRC32 = Crc; + + // + // return status + // + DEBUG_CODE_BEGIN (); + if (OrgCrc != Crc) { + DEBUG ((DEBUG_ERROR, "CheckCrc32: Crc check failed\n")); + } + + DEBUG_CODE_END (); + + return (BOOLEAN)(OrgCrc == Crc); +} diff --git a/MdeModulePkg/Library/GptLib/GptLib.inf b/MdeModulePkg/Library/GptLib/GptLib.inf new file mode 100644 index 0000000000..f110ee875a --- /dev/null +++ b/MdeModulePkg/Library/GptLib/GptLib.inf @@ -0,0 +1,49 @@ +## @file +# Shared GUID Partition Table (GPT) parsing and validation library. +# +# Provides the GPT table parsing, validation and restore routines that are +# shared between the Partition driver and other consumers that must parse the +# same on-disk GPT layout. +# +# Caution: This library requires additional review when modified. +# This library will have external input - disk partition. +# This external input must be validated carefully to avoid security issue like +# buffer overflow, integer overflow. +# +# Copyright (c) 2026, SUSE LLC. All rights reserved.
+# Copyright (c) 2018 Qualcomm Datacenter Technologies, Inc. +# Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = GptLib + FILE_GUID = E228F30C-C7D5-4A93-BBA7-B4E22327E289 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + LIBRARY_CLASS = GptLib + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC AARCH64 RISCV64 LOONGARCH64 +# + +[Sources] + Gpt.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + MemoryAllocationLib + UefiBootServicesTableLib + +[Guids] + gEfiPartTypeUnusedGuid ## SOMETIMES_CONSUMES ## GUID diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec index fac0f8ebed..c817a32b81 100644 --- a/MdeModulePkg/MdeModulePkg.dec +++ b/MdeModulePkg/MdeModulePkg.dec @@ -32,6 +32,9 @@ Core/PrivateInclude [LibraryClasses] + ## @libraryclass Provides GPT partition table parsing and validation routines. + GptLib|Include/Library/GptLib.h + ## @libraryclass Defines a set of methods to reset whole system. ResetSystemLib|Include/Library/ResetSystemLib.h diff --git a/MdeModulePkg/MdeModulePkg.dsc b/MdeModulePkg/MdeModulePkg.dsc index b758fa940b..177703211d 100644 --- a/MdeModulePkg/MdeModulePkg.dsc +++ b/MdeModulePkg/MdeModulePkg.dsc @@ -46,6 +46,7 @@ PeCoffLib|MdePkg/Library/BasePeCoffLib/BasePeCoffLib.inf PeCoffGetEntryPointLib|MdePkg/Library/BasePeCoffGetEntryPointLib/BasePeCoffGetEntryPointLib.inf SortLib|MdeModulePkg/Library/BaseSortLib/BaseSortLib.inf + GptLib|MdeModulePkg/Library/GptLib/GptLib.inf # # UEFI & PI # @@ -215,6 +216,7 @@ MdeModulePkg/Logo/Logo.inf MdeModulePkg/Logo/LogoDxe.inf MdeModulePkg/Library/BaseSortLib/BaseSortLib.inf + MdeModulePkg/Library/GptLib/GptLib.inf MdeModulePkg/Library/BootDiscoveryPolicyUiLib/BootDiscoveryPolicyUiLib.inf MdeModulePkg/Library/BootMaintenanceManagerUiLib/BootMaintenanceManagerUiLib.inf MdeModulePkg/Library/BootManagerUiLib/BootManagerUiLib.inf diff --git a/MdeModulePkg/Universal/Disk/PartitionDxe/Gpt.c b/MdeModulePkg/Universal/Disk/PartitionDxe/Gpt.c index 5bcf94d587..d74880038d 100644 --- a/MdeModulePkg/Universal/Disk/PartitionDxe/Gpt.c +++ b/MdeModulePkg/Universal/Disk/PartitionDxe/Gpt.c @@ -1,6 +1,5 @@ /** @file - Decode a hard disk partitioned with the GPT scheme in the UEFI 2.0 - specification. + Install GPT partition child handles for the Partition driver. Caution: This file requires additional review when modified. This driver will have external input - disk partition. @@ -8,10 +7,9 @@ buffer overflow, integer overflow. PartitionInstallGptChildHandles() routine will read disk partition content and - do basic validation before PartitionInstallChildHandle(). - - PartitionValidGptTable(), PartitionCheckGptEntry() routine will accept disk - partition content and validate the GPT table and GPT entry. + do basic validation before PartitionInstallChildHandle(). The GPT table + parsing and validation helpers it relies on live in the shared GPT parser + (GptLib/Gpt.c). Copyright (c) 2018 Qualcomm Datacenter Technologies, Inc. Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.
@@ -21,146 +19,6 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include "Partition.h" -/** - Install child handles if the Handle supports GPT partition structure. - - Caution: This function may receive untrusted input. - The GPT partition table header is external input, so this routine - will do basic validation for GPT partition table header before return. - - @param[in] BlockIo Parent BlockIo interface. - @param[in] DiskIo Disk Io protocol. - @param[in] Lba The starting Lba of the Partition Table - @param[out] PartHeader Stores the partition table that is read - - @retval TRUE The partition table is valid - @retval FALSE The partition table is not valid - -**/ -BOOLEAN -PartitionValidGptTable ( - IN EFI_BLOCK_IO_PROTOCOL *BlockIo, - IN EFI_DISK_IO_PROTOCOL *DiskIo, - IN EFI_LBA Lba, - OUT EFI_PARTITION_TABLE_HEADER *PartHeader - ); - -/** - Check if the CRC field in the Partition table header is valid - for Partition entry array. - - @param[in] BlockIo Parent BlockIo interface - @param[in] DiskIo Disk Io Protocol. - @param[in] PartHeader Partition table header structure - - @retval TRUE the CRC is valid - @retval FALSE the CRC is invalid - -**/ -BOOLEAN -PartitionCheckGptEntryArrayCRC ( - IN EFI_BLOCK_IO_PROTOCOL *BlockIo, - IN EFI_DISK_IO_PROTOCOL *DiskIo, - IN EFI_PARTITION_TABLE_HEADER *PartHeader - ); - -/** - Restore Partition Table to its alternate place - (Primary -> Backup or Backup -> Primary). - - @param[in] BlockIo Parent BlockIo interface. - @param[in] DiskIo Disk Io Protocol. - @param[in] PartHeader Partition table header structure. - - @retval TRUE Restoring succeeds - @retval FALSE Restoring failed - -**/ -BOOLEAN -PartitionRestoreGptTable ( - IN EFI_BLOCK_IO_PROTOCOL *BlockIo, - IN EFI_DISK_IO_PROTOCOL *DiskIo, - IN EFI_PARTITION_TABLE_HEADER *PartHeader - ); - -/** - This routine will check GPT partition entry and return entry status. - - Caution: This function may receive untrusted input. - The GPT partition entry is external input, so this routine - will do basic validation for GPT partition entry and report status. - - @param[in] PartHeader Partition table header structure - @param[in] PartEntry The partition entry array - @param[out] PEntryStatus the partition entry status array - recording the status of each partition - -**/ -VOID -PartitionCheckGptEntry ( - IN EFI_PARTITION_TABLE_HEADER *PartHeader, - IN EFI_PARTITION_ENTRY *PartEntry, - OUT EFI_PARTITION_ENTRY_STATUS *PEntryStatus - ); - -/** - Checks the CRC32 value in the table header. - - @param MaxSize Max Size limit - @param Size The size of the table - @param Hdr Table to check - - @return TRUE CRC Valid - @return FALSE CRC Invalid - -**/ -BOOLEAN -PartitionCheckCrcAltSize ( - IN UINTN MaxSize, - IN UINTN Size, - IN OUT EFI_TABLE_HEADER *Hdr - ); - -/** - Checks the CRC32 value in the table header. - - @param MaxSize Max Size limit - @param Hdr Table to check - - @return TRUE CRC Valid - @return FALSE CRC Invalid - -**/ -BOOLEAN -PartitionCheckCrc ( - IN UINTN MaxSize, - IN OUT EFI_TABLE_HEADER *Hdr - ); - -/** - Updates the CRC32 value in the table header. - - @param Size The size of the table - @param Hdr Table to update - -**/ -VOID -PartitionSetCrcAltSize ( - IN UINTN Size, - IN OUT EFI_TABLE_HEADER *Hdr - ); - -/** - Updates the CRC32 value in the table header. - - @param Hdr Table to update - -**/ -VOID -PartitionSetCrc ( - IN OUT EFI_TABLE_HEADER *Hdr - ); - /** Install child handles if the Handle supports GPT partition structure. @@ -446,438 +304,3 @@ Done: return GptValidStatus; } - -/** - This routine will read GPT partition table header and return it. - - Caution: This function may receive untrusted input. - The GPT partition table header is external input, so this routine - will do basic validation for GPT partition table header before return. - - @param[in] BlockIo Parent BlockIo interface. - @param[in] DiskIo Disk Io protocol. - @param[in] Lba The starting Lba of the Partition Table - @param[out] PartHeader Stores the partition table that is read - - @retval TRUE The partition table is valid - @retval FALSE The partition table is not valid - -**/ -BOOLEAN -PartitionValidGptTable ( - IN EFI_BLOCK_IO_PROTOCOL *BlockIo, - IN EFI_DISK_IO_PROTOCOL *DiskIo, - IN EFI_LBA Lba, - OUT EFI_PARTITION_TABLE_HEADER *PartHeader - ) -{ - EFI_STATUS Status; - UINT32 BlockSize; - EFI_PARTITION_TABLE_HEADER *PartHdr; - UINT32 MediaId; - - BlockSize = BlockIo->Media->BlockSize; - MediaId = BlockIo->Media->MediaId; - PartHdr = AllocateZeroPool (BlockSize); - - if (PartHdr == NULL) { - DEBUG ((DEBUG_ERROR, "Allocate pool error\n")); - return FALSE; - } - - // - // Read the EFI Partition Table Header - // - Status = DiskIo->ReadDisk ( - DiskIo, - MediaId, - MultU64x32 (Lba, BlockSize), - BlockSize, - PartHdr - ); - if (EFI_ERROR (Status)) { - FreePool (PartHdr); - return FALSE; - } - - if ((PartHdr->Header.Signature != EFI_PTAB_HEADER_ID) || - !PartitionCheckCrc (BlockSize, &PartHdr->Header) || - (PartHdr->MyLBA != Lba) || - (PartHdr->SizeOfPartitionEntry < sizeof (EFI_PARTITION_ENTRY)) - ) - { - DEBUG ((DEBUG_INFO, "Invalid efi partition table header\n")); - FreePool (PartHdr); - return FALSE; - } - - // - // Ensure the NumberOfPartitionEntries * SizeOfPartitionEntry doesn't overflow. - // - if (PartHdr->NumberOfPartitionEntries > DivU64x32 (MAX_UINTN, PartHdr->SizeOfPartitionEntry)) { - FreePool (PartHdr); - return FALSE; - } - - CopyMem (PartHeader, PartHdr, sizeof (EFI_PARTITION_TABLE_HEADER)); - if (!PartitionCheckGptEntryArrayCRC (BlockIo, DiskIo, PartHeader)) { - FreePool (PartHdr); - return FALSE; - } - - DEBUG ((DEBUG_INFO, " Valid efi partition table header\n")); - FreePool (PartHdr); - return TRUE; -} - -/** - Check if the CRC field in the Partition table header is valid - for Partition entry array. - - @param[in] BlockIo Parent BlockIo interface - @param[in] DiskIo Disk Io Protocol. - @param[in] PartHeader Partition table header structure - - @retval TRUE the CRC is valid - @retval FALSE the CRC is invalid - -**/ -BOOLEAN -PartitionCheckGptEntryArrayCRC ( - IN EFI_BLOCK_IO_PROTOCOL *BlockIo, - IN EFI_DISK_IO_PROTOCOL *DiskIo, - IN EFI_PARTITION_TABLE_HEADER *PartHeader - ) -{ - EFI_STATUS Status; - UINT8 *Ptr; - UINT32 Crc; - UINTN Size; - - // - // Read the EFI Partition Entries - // - Ptr = AllocatePool (PartHeader->NumberOfPartitionEntries * PartHeader->SizeOfPartitionEntry); - if (Ptr == NULL) { - DEBUG ((DEBUG_ERROR, " Allocate pool error\n")); - return FALSE; - } - - Status = DiskIo->ReadDisk ( - DiskIo, - BlockIo->Media->MediaId, - MultU64x32 (PartHeader->PartitionEntryLBA, BlockIo->Media->BlockSize), - PartHeader->NumberOfPartitionEntries * PartHeader->SizeOfPartitionEntry, - Ptr - ); - if (EFI_ERROR (Status)) { - FreePool (Ptr); - return FALSE; - } - - Size = PartHeader->NumberOfPartitionEntries * PartHeader->SizeOfPartitionEntry; - - Status = gBS->CalculateCrc32 (Ptr, Size, &Crc); - if (EFI_ERROR (Status)) { - DEBUG ((DEBUG_ERROR, "CheckPEntryArrayCRC: Crc calculation failed\n")); - FreePool (Ptr); - return FALSE; - } - - FreePool (Ptr); - - return (BOOLEAN)(PartHeader->PartitionEntryArrayCRC32 == Crc); -} - -/** - Restore Partition Table to its alternate place - (Primary -> Backup or Backup -> Primary). - - @param[in] BlockIo Parent BlockIo interface. - @param[in] DiskIo Disk Io Protocol. - @param[in] PartHeader Partition table header structure. - - @retval TRUE Restoring succeeds - @retval FALSE Restoring failed - -**/ -BOOLEAN -PartitionRestoreGptTable ( - IN EFI_BLOCK_IO_PROTOCOL *BlockIo, - IN EFI_DISK_IO_PROTOCOL *DiskIo, - IN EFI_PARTITION_TABLE_HEADER *PartHeader - ) -{ - EFI_STATUS Status; - UINTN BlockSize; - EFI_PARTITION_TABLE_HEADER *PartHdr; - EFI_LBA PEntryLBA; - UINT8 *Ptr; - UINT32 MediaId; - - PartHdr = NULL; - Ptr = NULL; - - BlockSize = BlockIo->Media->BlockSize; - MediaId = BlockIo->Media->MediaId; - - PartHdr = AllocateZeroPool (BlockSize); - - if (PartHdr == NULL) { - DEBUG ((DEBUG_ERROR, "Allocate pool error\n")); - return FALSE; - } - - PEntryLBA = (PartHeader->MyLBA == PRIMARY_PART_HEADER_LBA) ? \ - (PartHeader->LastUsableLBA + 1) : \ - (PRIMARY_PART_HEADER_LBA + 1); - - CopyMem (PartHdr, PartHeader, sizeof (EFI_PARTITION_TABLE_HEADER)); - - PartHdr->MyLBA = PartHeader->AlternateLBA; - PartHdr->AlternateLBA = PartHeader->MyLBA; - PartHdr->PartitionEntryLBA = PEntryLBA; - PartitionSetCrc ((EFI_TABLE_HEADER *)PartHdr); - - Status = DiskIo->WriteDisk ( - DiskIo, - MediaId, - MultU64x32 (PartHdr->MyLBA, (UINT32)BlockSize), - BlockSize, - PartHdr - ); - if (EFI_ERROR (Status)) { - goto Done; - } - - Ptr = AllocatePool (PartHeader->NumberOfPartitionEntries * PartHeader->SizeOfPartitionEntry); - if (Ptr == NULL) { - DEBUG ((DEBUG_ERROR, " Allocate pool error\n")); - Status = EFI_OUT_OF_RESOURCES; - goto Done; - } - - Status = DiskIo->ReadDisk ( - DiskIo, - MediaId, - MultU64x32 (PartHeader->PartitionEntryLBA, (UINT32)BlockSize), - PartHeader->NumberOfPartitionEntries * PartHeader->SizeOfPartitionEntry, - Ptr - ); - if (EFI_ERROR (Status)) { - goto Done; - } - - Status = DiskIo->WriteDisk ( - DiskIo, - MediaId, - MultU64x32 (PEntryLBA, (UINT32)BlockSize), - PartHeader->NumberOfPartitionEntries * PartHeader->SizeOfPartitionEntry, - Ptr - ); - -Done: - FreePool (PartHdr); - - if (Ptr != NULL) { - FreePool (Ptr); - } - - if (EFI_ERROR (Status)) { - return FALSE; - } - - return TRUE; -} - -/** - This routine will check GPT partition entry and return entry status. - - Caution: This function may receive untrusted input. - The GPT partition entry is external input, so this routine - will do basic validation for GPT partition entry and report status. - - @param[in] PartHeader Partition table header structure - @param[in] PartEntry The partition entry array - @param[out] PEntryStatus the partition entry status array - recording the status of each partition - -**/ -VOID -PartitionCheckGptEntry ( - IN EFI_PARTITION_TABLE_HEADER *PartHeader, - IN EFI_PARTITION_ENTRY *PartEntry, - OUT EFI_PARTITION_ENTRY_STATUS *PEntryStatus - ) -{ - EFI_LBA StartingLBA; - EFI_LBA EndingLBA; - EFI_PARTITION_ENTRY *Entry; - UINTN Index1; - UINTN Index2; - - DEBUG ((DEBUG_INFO, " start check partition entries\n")); - for (Index1 = 0; Index1 < PartHeader->NumberOfPartitionEntries; Index1++) { - Entry = (EFI_PARTITION_ENTRY *)((UINT8 *)PartEntry + Index1 * PartHeader->SizeOfPartitionEntry); - if (CompareGuid (&Entry->PartitionTypeGUID, &gEfiPartTypeUnusedGuid)) { - continue; - } - - StartingLBA = Entry->StartingLBA; - EndingLBA = Entry->EndingLBA; - if ((StartingLBA > EndingLBA) || - (StartingLBA < PartHeader->FirstUsableLBA) || - (StartingLBA > PartHeader->LastUsableLBA) || - (EndingLBA < PartHeader->FirstUsableLBA) || - (EndingLBA > PartHeader->LastUsableLBA) - ) - { - PEntryStatus[Index1].OutOfRange = TRUE; - continue; - } - - if ((Entry->Attributes & BIT1) != 0) { - // - // If Bit 1 is set, this indicate that this is an OS specific GUID partition. - // - PEntryStatus[Index1].OsSpecific = TRUE; - } - - for (Index2 = Index1 + 1; Index2 < PartHeader->NumberOfPartitionEntries; Index2++) { - Entry = (EFI_PARTITION_ENTRY *)((UINT8 *)PartEntry + Index2 * PartHeader->SizeOfPartitionEntry); - if (CompareGuid (&Entry->PartitionTypeGUID, &gEfiPartTypeUnusedGuid)) { - continue; - } - - if ((Entry->EndingLBA >= StartingLBA) && (Entry->StartingLBA <= EndingLBA)) { - // - // This region overlaps with the Index1'th region - // - PEntryStatus[Index1].Overlap = TRUE; - PEntryStatus[Index2].Overlap = TRUE; - continue; - } - } - } - - DEBUG ((DEBUG_INFO, " End check partition entries\n")); -} - -/** - Updates the CRC32 value in the table header. - - @param Hdr Table to update - -**/ -VOID -PartitionSetCrc ( - IN OUT EFI_TABLE_HEADER *Hdr - ) -{ - PartitionSetCrcAltSize (Hdr->HeaderSize, Hdr); -} - -/** - Updates the CRC32 value in the table header. - - @param Size The size of the table - @param Hdr Table to update - -**/ -VOID -PartitionSetCrcAltSize ( - IN UINTN Size, - IN OUT EFI_TABLE_HEADER *Hdr - ) -{ - UINT32 Crc; - - Hdr->CRC32 = 0; - gBS->CalculateCrc32 ((UINT8 *)Hdr, Size, &Crc); - Hdr->CRC32 = Crc; -} - -/** - Checks the CRC32 value in the table header. - - @param MaxSize Max Size limit - @param Hdr Table to check - - @return TRUE CRC Valid - @return FALSE CRC Invalid - -**/ -BOOLEAN -PartitionCheckCrc ( - IN UINTN MaxSize, - IN OUT EFI_TABLE_HEADER *Hdr - ) -{ - return PartitionCheckCrcAltSize (MaxSize, Hdr->HeaderSize, Hdr); -} - -/** - Checks the CRC32 value in the table header. - - @param MaxSize Max Size limit - @param Size The size of the table - @param Hdr Table to check - - @return TRUE CRC Valid - @return FALSE CRC Invalid - -**/ -BOOLEAN -PartitionCheckCrcAltSize ( - IN UINTN MaxSize, - IN UINTN Size, - IN OUT EFI_TABLE_HEADER *Hdr - ) -{ - UINT32 Crc; - UINT32 OrgCrc; - EFI_STATUS Status; - - Crc = 0; - - if (Size == 0) { - // - // If header size is 0 CRC will pass so return FALSE here - // - return FALSE; - } - - if ((MaxSize != 0) && (Size > MaxSize)) { - DEBUG ((DEBUG_ERROR, "CheckCrc32: Size > MaxSize\n")); - return FALSE; - } - - // - // clear old crc from header - // - OrgCrc = Hdr->CRC32; - Hdr->CRC32 = 0; - - Status = gBS->CalculateCrc32 ((UINT8 *)Hdr, Size, &Crc); - if (EFI_ERROR (Status)) { - DEBUG ((DEBUG_ERROR, "CheckCrc32: Crc calculation failed\n")); - return FALSE; - } - - // - // set results - // - Hdr->CRC32 = Crc; - - // - // return status - // - DEBUG_CODE_BEGIN (); - if (OrgCrc != Crc) { - DEBUG ((DEBUG_ERROR, "CheckCrc32: Crc check failed\n")); - } - - DEBUG_CODE_END (); - - return (BOOLEAN)(OrgCrc == Crc); -} diff --git a/MdeModulePkg/Universal/Disk/PartitionDxe/Partition.h b/MdeModulePkg/Universal/Disk/PartitionDxe/Partition.h index 8deafbe313..8c598d0bc1 100644 --- a/MdeModulePkg/Universal/Disk/PartitionDxe/Partition.h +++ b/MdeModulePkg/Universal/Disk/PartitionDxe/Partition.h @@ -31,6 +31,8 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include #include +#include + #include #include #include @@ -93,15 +95,6 @@ extern EFI_COMPONENT_NAME2_PROTOCOL gPartitionComponentName2; (((UINT8 *) a)[2] << 16) | \ (((UINT8 *) a)[3] << 24) ) -// -// GPT Partition Entry Status -// -typedef struct { - BOOLEAN OutOfRange; - BOOLEAN Overlap; - BOOLEAN OsSpecific; -} EFI_PARTITION_ENTRY_STATUS; - // // Function Prototypes // diff --git a/MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxe.inf b/MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxe.inf index 14ab6ae198..6450941af8 100644 --- a/MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxe.inf +++ b/MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxe.inf @@ -47,10 +47,12 @@ [Packages] MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec [LibraryClasses] DevicePathLib + GptLib UefiBootServicesTableLib MemoryAllocationLib BaseMemoryLib