REF: https://bugzilla.tianocore.org/show_bug.cgi?id=3737 Apply uncrustify changes to .c/.h files in the MdeModulePkg package Cc: Andrew Fish <afish@apple.com> Cc: Leif Lindholm <leif@nuviainc.com> Cc: Michael D Kinney <michael.d.kinney@intel.com> Signed-off-by: Michael Kubacki <michael.kubacki@microsoft.com> Reviewed-by: Liming Gao <gaoliming@byosoft.com.cn>
		
			
				
	
	
		
			884 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			884 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @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.
 | 
						|
 | 
						|
  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.
 | 
						|
 | 
						|
Copyright (c) 2018 Qualcomm Datacenter Technologies, Inc.
 | 
						|
Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR>
 | 
						|
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.
 | 
						|
 | 
						|
  Caution: This function may receive untrusted input.
 | 
						|
  The GPT partition table is external input, so this routine
 | 
						|
  will do basic validation for GPT partition table before install
 | 
						|
  child handle for each GPT partition.
 | 
						|
 | 
						|
  @param[in]  This       Calling context.
 | 
						|
  @param[in]  Handle     Parent Handle.
 | 
						|
  @param[in]  DiskIo     Parent DiskIo interface.
 | 
						|
  @param[in]  DiskIo2    Parent DiskIo2 interface.
 | 
						|
  @param[in]  BlockIo    Parent BlockIo interface.
 | 
						|
  @param[in]  BlockIo2   Parent BlockIo2 interface.
 | 
						|
  @param[in]  DevicePath Parent Device Path.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS           Valid GPT disk.
 | 
						|
  @retval EFI_MEDIA_CHANGED     Media changed Detected.
 | 
						|
  @retval other                 Not a valid GPT disk.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
PartitionInstallGptChildHandles (
 | 
						|
  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,
 | 
						|
  IN  EFI_HANDLE                   Handle,
 | 
						|
  IN  EFI_DISK_IO_PROTOCOL         *DiskIo,
 | 
						|
  IN  EFI_DISK_IO2_PROTOCOL        *DiskIo2,
 | 
						|
  IN  EFI_BLOCK_IO_PROTOCOL        *BlockIo,
 | 
						|
  IN  EFI_BLOCK_IO2_PROTOCOL       *BlockIo2,
 | 
						|
  IN  EFI_DEVICE_PATH_PROTOCOL     *DevicePath
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS                   Status;
 | 
						|
  UINT32                       BlockSize;
 | 
						|
  EFI_LBA                      LastBlock;
 | 
						|
  MASTER_BOOT_RECORD           *ProtectiveMbr;
 | 
						|
  EFI_PARTITION_TABLE_HEADER   *PrimaryHeader;
 | 
						|
  EFI_PARTITION_TABLE_HEADER   *BackupHeader;
 | 
						|
  EFI_PARTITION_ENTRY          *PartEntry;
 | 
						|
  EFI_PARTITION_ENTRY          *Entry;
 | 
						|
  EFI_PARTITION_ENTRY_STATUS   *PEntryStatus;
 | 
						|
  UINTN                        Index;
 | 
						|
  EFI_STATUS                   GptValidStatus;
 | 
						|
  HARDDRIVE_DEVICE_PATH        HdDev;
 | 
						|
  UINT32                       MediaId;
 | 
						|
  EFI_PARTITION_INFO_PROTOCOL  PartitionInfo;
 | 
						|
 | 
						|
  ProtectiveMbr = NULL;
 | 
						|
  PrimaryHeader = NULL;
 | 
						|
  BackupHeader  = NULL;
 | 
						|
  PartEntry     = NULL;
 | 
						|
  PEntryStatus  = NULL;
 | 
						|
 | 
						|
  BlockSize = BlockIo->Media->BlockSize;
 | 
						|
  LastBlock = BlockIo->Media->LastBlock;
 | 
						|
  MediaId   = BlockIo->Media->MediaId;
 | 
						|
 | 
						|
  DEBUG ((DEBUG_INFO, " BlockSize : %d \n", BlockSize));
 | 
						|
  DEBUG ((DEBUG_INFO, " LastBlock : %lx \n", LastBlock));
 | 
						|
 | 
						|
  GptValidStatus = EFI_NOT_FOUND;
 | 
						|
 | 
						|
  //
 | 
						|
  // Ensure the block size can hold the MBR
 | 
						|
  //
 | 
						|
  if (BlockSize < sizeof (MASTER_BOOT_RECORD)) {
 | 
						|
    return EFI_NOT_FOUND;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Allocate a buffer for the Protective MBR
 | 
						|
  //
 | 
						|
  ProtectiveMbr = AllocatePool (BlockSize);
 | 
						|
  if (ProtectiveMbr == NULL) {
 | 
						|
    return EFI_NOT_FOUND;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Read the Protective MBR from LBA #0
 | 
						|
  //
 | 
						|
  Status = DiskIo->ReadDisk (
 | 
						|
                     DiskIo,
 | 
						|
                     MediaId,
 | 
						|
                     0,
 | 
						|
                     BlockSize,
 | 
						|
                     ProtectiveMbr
 | 
						|
                     );
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    GptValidStatus = Status;
 | 
						|
    goto Done;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Verify that the Protective MBR is valid
 | 
						|
  //
 | 
						|
  for (Index = 0; Index < MAX_MBR_PARTITIONS; Index++) {
 | 
						|
    if ((ProtectiveMbr->Partition[Index].OSIndicator == PMBR_GPT_PARTITION) &&
 | 
						|
        (UNPACK_UINT32 (ProtectiveMbr->Partition[Index].StartingLBA) == 1)
 | 
						|
        )
 | 
						|
    {
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (Index == MAX_MBR_PARTITIONS) {
 | 
						|
    goto Done;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Allocate the GPT structures
 | 
						|
  //
 | 
						|
  PrimaryHeader = AllocateZeroPool (sizeof (EFI_PARTITION_TABLE_HEADER));
 | 
						|
  if (PrimaryHeader == NULL) {
 | 
						|
    goto Done;
 | 
						|
  }
 | 
						|
 | 
						|
  BackupHeader = AllocateZeroPool (sizeof (EFI_PARTITION_TABLE_HEADER));
 | 
						|
  if (BackupHeader == NULL) {
 | 
						|
    goto Done;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Check primary and backup partition tables
 | 
						|
  //
 | 
						|
  if (!PartitionValidGptTable (BlockIo, DiskIo, PRIMARY_PART_HEADER_LBA, PrimaryHeader)) {
 | 
						|
    DEBUG ((DEBUG_INFO, " Not Valid primary partition table\n"));
 | 
						|
 | 
						|
    if (!PartitionValidGptTable (BlockIo, DiskIo, LastBlock, BackupHeader)) {
 | 
						|
      DEBUG ((DEBUG_INFO, " Not Valid backup partition table\n"));
 | 
						|
      goto Done;
 | 
						|
    } else {
 | 
						|
      DEBUG ((DEBUG_INFO, " Valid backup partition table\n"));
 | 
						|
      DEBUG ((DEBUG_INFO, " Restore primary partition table by the backup\n"));
 | 
						|
      if (!PartitionRestoreGptTable (BlockIo, DiskIo, BackupHeader)) {
 | 
						|
        DEBUG ((DEBUG_INFO, " Restore primary partition table error\n"));
 | 
						|
      }
 | 
						|
 | 
						|
      if (PartitionValidGptTable (BlockIo, DiskIo, BackupHeader->AlternateLBA, PrimaryHeader)) {
 | 
						|
        DEBUG ((DEBUG_INFO, " Restore backup partition table success\n"));
 | 
						|
      }
 | 
						|
    }
 | 
						|
  } else if (!PartitionValidGptTable (BlockIo, DiskIo, PrimaryHeader->AlternateLBA, BackupHeader)) {
 | 
						|
    DEBUG ((DEBUG_INFO, " Valid primary and !Valid backup partition table\n"));
 | 
						|
    DEBUG ((DEBUG_INFO, " Restore backup partition table by the primary\n"));
 | 
						|
    if (!PartitionRestoreGptTable (BlockIo, DiskIo, PrimaryHeader)) {
 | 
						|
      DEBUG ((DEBUG_INFO, " Restore backup partition table error\n"));
 | 
						|
    }
 | 
						|
 | 
						|
    if (PartitionValidGptTable (BlockIo, DiskIo, PrimaryHeader->AlternateLBA, BackupHeader)) {
 | 
						|
      DEBUG ((DEBUG_INFO, " Restore backup partition table success\n"));
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  DEBUG ((DEBUG_INFO, " Valid primary and Valid backup partition table\n"));
 | 
						|
 | 
						|
  //
 | 
						|
  // Read the EFI Partition Entries
 | 
						|
  //
 | 
						|
  PartEntry = AllocatePool (PrimaryHeader->NumberOfPartitionEntries * PrimaryHeader->SizeOfPartitionEntry);
 | 
						|
  if (PartEntry == NULL) {
 | 
						|
    DEBUG ((DEBUG_ERROR, "Allocate pool error\n"));
 | 
						|
    goto Done;
 | 
						|
  }
 | 
						|
 | 
						|
  Status = DiskIo->ReadDisk (
 | 
						|
                     DiskIo,
 | 
						|
                     MediaId,
 | 
						|
                     MultU64x32 (PrimaryHeader->PartitionEntryLBA, BlockSize),
 | 
						|
                     PrimaryHeader->NumberOfPartitionEntries * (PrimaryHeader->SizeOfPartitionEntry),
 | 
						|
                     PartEntry
 | 
						|
                     );
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    GptValidStatus = Status;
 | 
						|
    DEBUG ((DEBUG_ERROR, " Partition Entry ReadDisk error\n"));
 | 
						|
    goto Done;
 | 
						|
  }
 | 
						|
 | 
						|
  DEBUG ((DEBUG_INFO, " Partition entries read block success\n"));
 | 
						|
 | 
						|
  DEBUG ((DEBUG_INFO, " Number of partition entries: %d\n", PrimaryHeader->NumberOfPartitionEntries));
 | 
						|
 | 
						|
  PEntryStatus = AllocateZeroPool (PrimaryHeader->NumberOfPartitionEntries * sizeof (EFI_PARTITION_ENTRY_STATUS));
 | 
						|
  if (PEntryStatus == NULL) {
 | 
						|
    DEBUG ((DEBUG_ERROR, "Allocate pool error\n"));
 | 
						|
    goto Done;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Check the integrity of partition entries
 | 
						|
  //
 | 
						|
  PartitionCheckGptEntry (PrimaryHeader, PartEntry, PEntryStatus);
 | 
						|
 | 
						|
  //
 | 
						|
  // If we got this far the GPT layout of the disk is valid and we should return true
 | 
						|
  //
 | 
						|
  GptValidStatus = EFI_SUCCESS;
 | 
						|
 | 
						|
  //
 | 
						|
  // Create child device handles
 | 
						|
  //
 | 
						|
  for (Index = 0; Index < PrimaryHeader->NumberOfPartitionEntries; Index++) {
 | 
						|
    Entry = (EFI_PARTITION_ENTRY *)((UINT8 *)PartEntry + Index * PrimaryHeader->SizeOfPartitionEntry);
 | 
						|
    if (CompareGuid (&Entry->PartitionTypeGUID, &gEfiPartTypeUnusedGuid) ||
 | 
						|
        PEntryStatus[Index].OutOfRange ||
 | 
						|
        PEntryStatus[Index].Overlap ||
 | 
						|
        PEntryStatus[Index].OsSpecific
 | 
						|
        )
 | 
						|
    {
 | 
						|
      //
 | 
						|
      // Don't use null EFI Partition Entries, Invalid Partition Entries or OS specific
 | 
						|
      // partition Entries
 | 
						|
      //
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    ZeroMem (&HdDev, sizeof (HdDev));
 | 
						|
    HdDev.Header.Type    = MEDIA_DEVICE_PATH;
 | 
						|
    HdDev.Header.SubType = MEDIA_HARDDRIVE_DP;
 | 
						|
    SetDevicePathNodeLength (&HdDev.Header, sizeof (HdDev));
 | 
						|
 | 
						|
    HdDev.PartitionNumber = (UINT32)Index + 1;
 | 
						|
    HdDev.MBRType         = MBR_TYPE_EFI_PARTITION_TABLE_HEADER;
 | 
						|
    HdDev.SignatureType   = SIGNATURE_TYPE_GUID;
 | 
						|
    HdDev.PartitionStart  = Entry->StartingLBA;
 | 
						|
    HdDev.PartitionSize   = Entry->EndingLBA - Entry->StartingLBA + 1;
 | 
						|
    CopyMem (HdDev.Signature, &Entry->UniquePartitionGUID, sizeof (EFI_GUID));
 | 
						|
 | 
						|
    ZeroMem (&PartitionInfo, sizeof (EFI_PARTITION_INFO_PROTOCOL));
 | 
						|
    PartitionInfo.Revision = EFI_PARTITION_INFO_PROTOCOL_REVISION;
 | 
						|
    PartitionInfo.Type     = PARTITION_TYPE_GPT;
 | 
						|
    if (CompareGuid (&Entry->PartitionTypeGUID, &gEfiPartTypeSystemPartGuid)) {
 | 
						|
      PartitionInfo.System = 1;
 | 
						|
    }
 | 
						|
 | 
						|
    CopyMem (&PartitionInfo.Info.Gpt, Entry, sizeof (EFI_PARTITION_ENTRY));
 | 
						|
 | 
						|
    DEBUG ((DEBUG_INFO, " Index : %d\n", (UINT32)Index));
 | 
						|
    DEBUG ((DEBUG_INFO, " Start LBA : %lx\n", (UINT64)HdDev.PartitionStart));
 | 
						|
    DEBUG ((DEBUG_INFO, " End LBA : %lx\n", (UINT64)Entry->EndingLBA));
 | 
						|
    DEBUG ((DEBUG_INFO, " Partition size: %lx\n", (UINT64)HdDev.PartitionSize));
 | 
						|
    DEBUG ((DEBUG_INFO, " Start : %lx", MultU64x32 (Entry->StartingLBA, BlockSize)));
 | 
						|
    DEBUG ((DEBUG_INFO, " End : %lx\n", MultU64x32 (Entry->EndingLBA, BlockSize)));
 | 
						|
 | 
						|
    Status = PartitionInstallChildHandle (
 | 
						|
               This,
 | 
						|
               Handle,
 | 
						|
               DiskIo,
 | 
						|
               DiskIo2,
 | 
						|
               BlockIo,
 | 
						|
               BlockIo2,
 | 
						|
               DevicePath,
 | 
						|
               (EFI_DEVICE_PATH_PROTOCOL *)&HdDev,
 | 
						|
               &PartitionInfo,
 | 
						|
               Entry->StartingLBA,
 | 
						|
               Entry->EndingLBA,
 | 
						|
               BlockSize,
 | 
						|
               &Entry->PartitionTypeGUID
 | 
						|
               );
 | 
						|
  }
 | 
						|
 | 
						|
  DEBUG ((DEBUG_INFO, "Prepare to Free Pool\n"));
 | 
						|
 | 
						|
Done:
 | 
						|
  if (ProtectiveMbr != NULL) {
 | 
						|
    FreePool (ProtectiveMbr);
 | 
						|
  }
 | 
						|
 | 
						|
  if (PrimaryHeader != NULL) {
 | 
						|
    FreePool (PrimaryHeader);
 | 
						|
  }
 | 
						|
 | 
						|
  if (BackupHeader != NULL) {
 | 
						|
    FreePool (BackupHeader);
 | 
						|
  }
 | 
						|
 | 
						|
  if (PartEntry != NULL) {
 | 
						|
    FreePool (PartEntry);
 | 
						|
  }
 | 
						|
 | 
						|
  if (PEntryStatus != NULL) {
 | 
						|
    FreePool (PEntryStatus);
 | 
						|
  }
 | 
						|
 | 
						|
  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);
 | 
						|
}
 |