123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551 |
- /** @file
- Routines supporting partition discovery and
- logical device reading
- Copyright (c) 2019 Intel Corporation. All rights reserved.<BR>
- SPDX-License-Identifier: BSD-2-Clause-Patent
- **/
- #include <IndustryStandard/Mbr.h>
- #include <Uefi/UefiGpt.h>
- #include <Library/BaseLib.h>
- #include "FatLitePeim.h"
- //
- // Assumption: 'a' and 'blocksize' are all UINT32 or UINT64.
- // If 'a' and 'blocksize' are not the same type, should use DivU64xU32 to calculate.
- //
- #define EFI_SIZE_TO_BLOCKS(a, blocksize) (((a) / (blocksize)) + (((a) % (blocksize)) ? 1 : 0))
- //
- // GPT Partition Entry Status
- //
- typedef struct {
- BOOLEAN OutOfRange;
- BOOLEAN Overlap;
- BOOLEAN OsSpecific;
- } EFI_PARTITION_ENTRY_STATUS;
- /**
- Check if the CRC field in the Partition table header is valid.
- @param[in] PartHeader Partition table header structure
- @retval TRUE the CRC is valid
- @retval FALSE the CRC is invalid
- **/
- BOOLEAN
- PartitionCheckGptHeaderCRC (
- IN EFI_PARTITION_TABLE_HEADER *PartHeader
- )
- {
- UINT32 GptHdrCrc;
- UINT32 Crc;
- GptHdrCrc = PartHeader->Header.CRC32;
- //
- // Set CRC field to zero when doing calculation
- //
- PartHeader->Header.CRC32 = 0;
- Crc = CalculateCrc32 (PartHeader, PartHeader->Header.HeaderSize);
- //
- // Restore Header CRC
- //
- PartHeader->Header.CRC32 = GptHdrCrc;
- return (GptHdrCrc == Crc);
- }
- /**
- Check if the CRC field in the Partition table header is valid
- for Partition entry array.
- @param[in] PartHeader Partition table header structure
- @param[in] PartEntry The partition entry array
- @retval TRUE the CRC is valid
- @retval FALSE the CRC is invalid
- **/
- BOOLEAN
- PartitionCheckGptEntryArrayCRC (
- IN EFI_PARTITION_TABLE_HEADER *PartHeader,
- IN EFI_PARTITION_ENTRY *PartEntry
- )
- {
- UINT32 Crc;
- UINTN Size;
- Size = (UINTN)MultU64x32 (PartHeader->NumberOfPartitionEntries, PartHeader->SizeOfPartitionEntry);
- Crc = CalculateCrc32 (PartEntry, Size);
- return (BOOLEAN)(PartHeader->PartitionEntryArrayCRC32 == Crc);
- }
- /**
- The function is used for valid GPT table. Both for Primary and Backup GPT header.
- @param[in] PrivateData The global memory map
- @param[in] ParentBlockDevNo The parent block device
- @param[in] IsPrimaryHeader Indicate to which header will be checked.
- @param[in] PartHdr Stores the partition table that is read
- @retval TRUE The partition table is valid
- @retval FALSE The partition table is not valid
- **/
- BOOLEAN
- PartitionCheckGptHeader (
- IN PEI_FAT_PRIVATE_DATA *PrivateData,
- IN UINTN ParentBlockDevNo,
- IN BOOLEAN IsPrimaryHeader,
- IN EFI_PARTITION_TABLE_HEADER *PartHdr
- )
- {
- PEI_FAT_BLOCK_DEVICE *ParentBlockDev;
- EFI_PEI_LBA Lba;
- EFI_PEI_LBA AlternateLba;
- EFI_PEI_LBA EntryArrayLastLba;
- UINT64 PartitionEntryArraySize;
- UINT64 PartitionEntryBlockNumb;
- UINT32 EntryArraySizeRemainder;
- ParentBlockDev = &(PrivateData->BlockDevice[ParentBlockDevNo]);
- if (IsPrimaryHeader) {
- Lba = PRIMARY_PART_HEADER_LBA;
- AlternateLba = ParentBlockDev->LastBlock;
- } else {
- Lba = ParentBlockDev->LastBlock;
- AlternateLba = PRIMARY_PART_HEADER_LBA;
- }
- if ((PartHdr->Header.Signature != EFI_PTAB_HEADER_ID) ||
- (PartHdr->Header.Revision != 0x00010000) ||
- (PartHdr->Header.HeaderSize < 92) ||
- (PartHdr->Header.HeaderSize > ParentBlockDev->BlockSize) ||
- (!PartitionCheckGptHeaderCRC (PartHdr)) ||
- (PartHdr->Header.Reserved != 0)
- )
- {
- DEBUG ((DEBUG_ERROR, "Invalid efi partition table header\n"));
- return FALSE;
- }
- //
- // | Block0 | Block1 |Block2 ~ FirstUsableLBA - 1|FirstUsableLBA, ... ,LastUsableLBA|LastUsableLBA+1 ~ LastBlock-1| LastBlock |
- // |Protective MBR|Primary Header|Entry Array(At Least 16384)| Partition | Entry Array(At Least 16384) |BackUp Header|
- //
- // 1. Protective MBR is fixed at Block 0.
- // 2. Primary Header is fixed at Block 1.
- // 3. Backup Header is fixed at LastBlock.
- // 4. Must be remain 128*128 bytes for primary entry array.
- // 5. Must be remain 128*128 bytes for backup entry array.
- // 6. SizeOfPartitionEntry must be equals to 128 * 2^n.
- //
- if ((PartHdr->MyLBA != Lba) ||
- (PartHdr->AlternateLBA != AlternateLba) ||
- (PartHdr->FirstUsableLBA < 2 + EFI_SIZE_TO_BLOCKS (EFI_GPT_PART_ENTRY_MIN_SIZE, ParentBlockDev->BlockSize)) ||
- (PartHdr->LastUsableLBA > ParentBlockDev->LastBlock - 1 - EFI_SIZE_TO_BLOCKS (EFI_GPT_PART_ENTRY_MIN_SIZE, ParentBlockDev->BlockSize)) ||
- (PartHdr->FirstUsableLBA > PartHdr->LastUsableLBA) ||
- (PartHdr->PartitionEntryLBA < 2) ||
- (PartHdr->PartitionEntryLBA > ParentBlockDev->LastBlock - 1) ||
- ((PartHdr->PartitionEntryLBA >= PartHdr->FirstUsableLBA) && (PartHdr->PartitionEntryLBA <= PartHdr->LastUsableLBA)) ||
- (PartHdr->SizeOfPartitionEntry%128 != 0) ||
- (PartHdr->SizeOfPartitionEntry != sizeof (EFI_PARTITION_ENTRY))
- )
- {
- DEBUG ((DEBUG_ERROR, "Invalid efi partition table header\n"));
- return FALSE;
- }
- //
- // Ensure the NumberOfPartitionEntries * SizeOfPartitionEntry doesn't overflow.
- //
- if (PartHdr->NumberOfPartitionEntries > DivU64x32 (MAX_UINTN, PartHdr->SizeOfPartitionEntry)) {
- DEBUG ((DEBUG_ERROR, "Memory overflow in GPT Entry Array\n"));
- return FALSE;
- }
- PartitionEntryArraySize = MultU64x32 (PartHdr->NumberOfPartitionEntries, PartHdr->SizeOfPartitionEntry);
- EntryArraySizeRemainder = 0;
- PartitionEntryBlockNumb = DivU64x32Remainder (PartitionEntryArraySize, ParentBlockDev->BlockSize, &EntryArraySizeRemainder);
- if (EntryArraySizeRemainder != 0) {
- PartitionEntryBlockNumb++;
- }
- if (IsPrimaryHeader) {
- EntryArrayLastLba = PartHdr->FirstUsableLBA;
- } else {
- EntryArrayLastLba = ParentBlockDev->LastBlock;
- }
- //
- // Make sure partition entry array not overlaps with partition area or the LastBlock.
- //
- if (PartHdr->PartitionEntryLBA + PartitionEntryBlockNumb > EntryArrayLastLba) {
- DEBUG ((DEBUG_ERROR, "GPT Partition Entry Array Error!\n"));
- DEBUG ((DEBUG_ERROR, "PartitionEntryArraySize = %lu.\n", PartitionEntryArraySize));
- DEBUG ((DEBUG_ERROR, "PartitionEntryLBA = %lu.\n", PartHdr->PartitionEntryLBA));
- DEBUG ((DEBUG_ERROR, "PartitionEntryBlockNumb = %lu.\n", PartitionEntryBlockNumb));
- DEBUG ((DEBUG_ERROR, "EntryArrayLastLba = %lu.\n", EntryArrayLastLba));
- return FALSE;
- }
- return TRUE;
- }
- /**
- This function is used to verify each partition in block device.
- @param[in] PrivateData The global memory map
- @param[in] ParentBlockDevNo The parent block device
- @param[in] PartHdr Stores the partition table that is read
- @retval TRUE The partition is valid
- @retval FALSE The partition is not valid
- **/
- BOOLEAN
- PartitionCheckGptEntryArray (
- IN PEI_FAT_PRIVATE_DATA *PrivateData,
- IN UINTN ParentBlockDevNo,
- IN EFI_PARTITION_TABLE_HEADER *PartHdr
- )
- {
- EFI_STATUS Status;
- PEI_FAT_BLOCK_DEVICE *ParentBlockDev;
- PEI_FAT_BLOCK_DEVICE *BlockDevPtr;
- UINT64 PartitionEntryArraySize;
- UINT64 PartitionEntryBlockNumb;
- UINT32 EntryArraySizeRemainder;
- EFI_PARTITION_ENTRY *PartitionEntryBuffer;
- EFI_PARTITION_ENTRY_STATUS *PartitionEntryStatus;
- BOOLEAN Found;
- EFI_LBA StartingLBA;
- EFI_LBA EndingLBA;
- UINTN Index;
- UINTN Index1;
- UINTN Index2;
- EFI_PARTITION_ENTRY *Entry;
- PartitionEntryBuffer = NULL;
- PartitionEntryStatus = NULL;
- ParentBlockDev = &(PrivateData->BlockDevice[ParentBlockDevNo]);
- Found = FALSE;
- PartitionEntryArraySize = MultU64x32 (PartHdr->NumberOfPartitionEntries, PartHdr->SizeOfPartitionEntry);
- EntryArraySizeRemainder = 0;
- PartitionEntryBlockNumb = DivU64x32Remainder (PartitionEntryArraySize, ParentBlockDev->BlockSize, &EntryArraySizeRemainder);
- if (EntryArraySizeRemainder != 0) {
- PartitionEntryBlockNumb++;
- }
- PartitionEntryArraySize = MultU64x32 (PartitionEntryBlockNumb, ParentBlockDev->BlockSize);
- PartitionEntryBuffer = (EFI_PARTITION_ENTRY *)AllocatePages (EFI_SIZE_TO_PAGES ((UINTN)PartitionEntryArraySize));
- if (PartitionEntryBuffer == NULL) {
- DEBUG ((DEBUG_ERROR, "Allocate memory error!\n"));
- goto EXIT;
- }
- PartitionEntryStatus = (EFI_PARTITION_ENTRY_STATUS *)AllocatePages (EFI_SIZE_TO_PAGES (PartHdr->NumberOfPartitionEntries * sizeof (EFI_PARTITION_ENTRY_STATUS)));
- if (PartitionEntryStatus == NULL) {
- DEBUG ((DEBUG_ERROR, "Allocate memory error!\n"));
- goto EXIT;
- }
- ZeroMem (PartitionEntryStatus, PartHdr->NumberOfPartitionEntries * sizeof (EFI_PARTITION_ENTRY_STATUS));
- Status = FatReadBlock (
- PrivateData,
- ParentBlockDevNo,
- PartHdr->PartitionEntryLBA,
- (UINTN)PartitionEntryArraySize,
- PartitionEntryBuffer
- );
- if (EFI_ERROR (Status)) {
- DEBUG ((DEBUG_ERROR, "Read partition entry array error!\n"));
- goto EXIT;
- }
- if (!PartitionCheckGptEntryArrayCRC (PartHdr, PartitionEntryBuffer)) {
- DEBUG ((DEBUG_ERROR, "Partition entries CRC check fail\n"));
- goto EXIT;
- }
- for (Index1 = 0; Index1 < PartHdr->NumberOfPartitionEntries; Index1++) {
- Entry = (EFI_PARTITION_ENTRY *)((UINT8 *)PartitionEntryBuffer + Index1 * PartHdr->SizeOfPartitionEntry);
- if (CompareGuid (&Entry->PartitionTypeGUID, &gEfiPartTypeUnusedGuid)) {
- continue;
- }
- StartingLBA = Entry->StartingLBA;
- EndingLBA = Entry->EndingLBA;
- if ((StartingLBA > EndingLBA) ||
- (StartingLBA < PartHdr->FirstUsableLBA) ||
- (StartingLBA > PartHdr->LastUsableLBA) ||
- (EndingLBA < PartHdr->FirstUsableLBA) ||
- (EndingLBA > PartHdr->LastUsableLBA)
- )
- {
- PartitionEntryStatus[Index1].OutOfRange = TRUE;
- continue;
- }
- if ((Entry->Attributes & BIT1) != 0) {
- //
- // If Bit 1 is set, this indicate that this is an OS specific GUID partition.
- //
- PartitionEntryStatus[Index1].OsSpecific = TRUE;
- }
- for (Index2 = Index1 + 1; Index2 < PartHdr->NumberOfPartitionEntries; Index2++) {
- Entry = (EFI_PARTITION_ENTRY *)((UINT8 *)PartitionEntryBuffer + Index2 * PartHdr->SizeOfPartitionEntry);
- if (CompareGuid (&Entry->PartitionTypeGUID, &gEfiPartTypeUnusedGuid)) {
- continue;
- }
- if ((Entry->EndingLBA >= StartingLBA) && (Entry->StartingLBA <= EndingLBA)) {
- //
- // This region overlaps with the Index1'th region
- //
- PartitionEntryStatus[Index1].Overlap = TRUE;
- PartitionEntryStatus[Index2].Overlap = TRUE;
- continue;
- }
- }
- }
- for (Index = 0; Index < PartHdr->NumberOfPartitionEntries; Index++) {
- if (CompareGuid (&PartitionEntryBuffer[Index].PartitionTypeGUID, &gEfiPartTypeUnusedGuid) ||
- PartitionEntryStatus[Index].OutOfRange ||
- PartitionEntryStatus[Index].Overlap ||
- PartitionEntryStatus[Index].OsSpecific)
- {
- //
- // Don't use null EFI Partition Entries, Invalid Partition Entries or OS specific
- // partition Entries
- //
- continue;
- }
- if (PrivateData->BlockDeviceCount >= PEI_FAT_MAX_BLOCK_DEVICE) {
- break;
- }
- Found = TRUE;
- BlockDevPtr = &(PrivateData->BlockDevice[PrivateData->BlockDeviceCount]);
- BlockDevPtr->BlockSize = ParentBlockDev->BlockSize;
- BlockDevPtr->LastBlock = PartitionEntryBuffer[Index].EndingLBA;
- BlockDevPtr->IoAlign = ParentBlockDev->IoAlign;
- BlockDevPtr->Logical = TRUE;
- BlockDevPtr->PartitionChecked = FALSE;
- BlockDevPtr->StartingPos = MultU64x32 (
- PartitionEntryBuffer[Index].StartingLBA,
- ParentBlockDev->BlockSize
- );
- BlockDevPtr->ParentDevNo = ParentBlockDevNo;
- PrivateData->BlockDeviceCount++;
- DEBUG ((DEBUG_INFO, "Find GPT Partition [0x%lx", PartitionEntryBuffer[Index].StartingLBA));
- DEBUG ((DEBUG_INFO, ", 0x%lx]\n", BlockDevPtr->LastBlock));
- DEBUG ((DEBUG_INFO, " BlockSize %x\n", BlockDevPtr->BlockSize));
- }
- EXIT:
- if (PartitionEntryBuffer != NULL) {
- FreePages (PartitionEntryBuffer, EFI_SIZE_TO_PAGES ((UINTN)PartitionEntryArraySize));
- }
- if (PartitionEntryStatus != NULL) {
- FreePages (PartitionEntryStatus, EFI_SIZE_TO_PAGES (PartHdr->NumberOfPartitionEntries * sizeof (EFI_PARTITION_ENTRY_STATUS)));
- }
- return Found;
- }
- /**
- The function is used to check GPT structure, include GPT header and GPT entry array.
- 1. Check GPT header.
- 2. Check partition entry array.
- 3. Check each partitions.
- @param[in] PrivateData The global memory map
- @param[in] ParentBlockDevNo The parent block device
- @param[in] IsPrimary Indicate primary or backup to be check
- @retval TRUE Primary or backup GPT structure is valid.
- @retval FALSE Both primary and backup are invalid.
- **/
- BOOLEAN
- PartitionCheckGptStructure (
- IN PEI_FAT_PRIVATE_DATA *PrivateData,
- IN UINTN ParentBlockDevNo,
- IN BOOLEAN IsPrimary
- )
- {
- EFI_STATUS Status;
- PEI_FAT_BLOCK_DEVICE *ParentBlockDev;
- EFI_PARTITION_TABLE_HEADER *PartHdr;
- EFI_PEI_LBA GptHeaderLBA;
- ParentBlockDev = &(PrivateData->BlockDevice[ParentBlockDevNo]);
- PartHdr = (EFI_PARTITION_TABLE_HEADER *)PrivateData->BlockData;
- if (IsPrimary) {
- GptHeaderLBA = PRIMARY_PART_HEADER_LBA;
- } else {
- GptHeaderLBA = ParentBlockDev->LastBlock;
- }
- Status = FatReadBlock (
- PrivateData,
- ParentBlockDevNo,
- GptHeaderLBA,
- ParentBlockDev->BlockSize,
- PartHdr
- );
- if (EFI_ERROR (Status)) {
- return FALSE;
- }
- if (!PartitionCheckGptHeader (PrivateData, ParentBlockDevNo, IsPrimary, PartHdr)) {
- return FALSE;
- }
- if (!PartitionCheckGptEntryArray (PrivateData, ParentBlockDevNo, PartHdr)) {
- return FALSE;
- }
- return TRUE;
- }
- /**
- This function is used to check protective MBR structure before checking GPT.
- @param[in] PrivateData The global memory map
- @param[in] ParentBlockDevNo The parent block device
- @retval TRUE Valid protective MBR
- @retval FALSE Invalid MBR
- **/
- BOOLEAN
- PartitionCheckProtectiveMbr (
- IN PEI_FAT_PRIVATE_DATA *PrivateData,
- IN UINTN ParentBlockDevNo
- )
- {
- EFI_STATUS Status;
- MASTER_BOOT_RECORD *ProtectiveMbr;
- MBR_PARTITION_RECORD *MbrPartition;
- PEI_FAT_BLOCK_DEVICE *ParentBlockDev;
- UINTN Index;
- ProtectiveMbr = (MASTER_BOOT_RECORD *)PrivateData->BlockData;
- ParentBlockDev = &(PrivateData->BlockDevice[ParentBlockDevNo]);
- //
- // Read Protective MBR
- //
- Status = FatReadBlock (
- PrivateData,
- ParentBlockDevNo,
- 0,
- ParentBlockDev->BlockSize,
- ProtectiveMbr
- );
- if (EFI_ERROR (Status)) {
- DEBUG ((DEBUG_ERROR, "GPT Error When Read Protective Mbr From Partition!\n"));
- return FALSE;
- }
- if (ProtectiveMbr->Signature != MBR_SIGNATURE) {
- DEBUG ((DEBUG_ERROR, "Protective Mbr Signature is invalid!\n"));
- return FALSE;
- }
- //
- // The partition define in UEFI Spec Table 17.
- // Boot Code, Unique MBR Disk Signature, Unknown.
- // These parts will not be used by UEFI, so we skip to check them.
- //
- for (Index = 0; Index < MAX_MBR_PARTITIONS; Index++) {
- MbrPartition = (MBR_PARTITION_RECORD *)&ProtectiveMbr->Partition[Index];
- if ((MbrPartition->BootIndicator == 0x00) &&
- (MbrPartition->StartSector == 0x02) &&
- (MbrPartition->OSIndicator == PMBR_GPT_PARTITION) &&
- (UNPACK_UINT32 (MbrPartition->StartingLBA) == 1)
- )
- {
- return TRUE;
- }
- }
- DEBUG ((DEBUG_ERROR, "Protective Mbr, All Partition Entry Are Empty!\n"));
- return FALSE;
- }
- /**
- This function is used for finding GPT partition on block device.
- As follow UEFI spec we should check protective MBR first and then
- try to check both primary/backup GPT structures.
- @param[in] PrivateData The global memory map
- @param[in] ParentBlockDevNo The parent block device
- @retval TRUE New partitions are detected and logical block devices
- are added to block device array
- @retval FALSE No new partitions are added
- **/
- BOOLEAN
- FatFindGptPartitions (
- IN PEI_FAT_PRIVATE_DATA *PrivateData,
- IN UINTN ParentBlockDevNo
- )
- {
- BOOLEAN Found;
- PEI_FAT_BLOCK_DEVICE *ParentBlockDev;
- if (ParentBlockDevNo > PEI_FAT_MAX_BLOCK_DEVICE - 1) {
- return FALSE;
- }
- ParentBlockDev = &(PrivateData->BlockDevice[ParentBlockDevNo]);
- if (ParentBlockDev->BlockSize > PEI_FAT_MAX_BLOCK_SIZE) {
- DEBUG ((DEBUG_ERROR, "Device BlockSize %x exceed FAT_MAX_BLOCK_SIZE\n", ParentBlockDev->BlockSize));
- return FALSE;
- }
- if (!PartitionCheckProtectiveMbr (PrivateData, ParentBlockDevNo)) {
- return FALSE;
- }
- Found = PartitionCheckGptStructure (PrivateData, ParentBlockDevNo, TRUE);
- if (!Found) {
- DEBUG ((DEBUG_ERROR, "Primary GPT Header Error, Try to Check Backup GPT Header!\n"));
- Found = PartitionCheckGptStructure (PrivateData, ParentBlockDevNo, FALSE);
- }
- if (Found) {
- ParentBlockDev->PartitionChecked = TRUE;
- }
- return Found;
- }
|