blob: df033c892a3fe00cb00dc2cbc53439e6163e06de [file]
/** @file
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.
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(). 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.<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 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;
}
DEBUG ((DEBUG_INFO, " Valid backup partition table\n"));
DEBUG ((DEBUG_INFO, " Restore primary partition table by the backup\n"));
//
// Both the restore write and the subsequent validation can fail (for
// example on write-protected media, or when the backup header's
// AlternateLBA points beyond the end of the device). Never fall through
// with PrimaryHeader left in an unrestored/invalid state; bail out so the
// partition table that gets used is always one that passed validation.
//
if (!PartitionRestoreGptTable (BlockIo, DiskIo, BackupHeader)) {
DEBUG ((DEBUG_INFO, " Restore primary partition table error\n"));
goto Done;
}
if (!PartitionValidGptTable (BlockIo, DiskIo, BackupHeader->AlternateLBA, PrimaryHeader)) {
DEBUG ((DEBUG_INFO, " Not Valid restored primary partition table\n"));
goto Done;
}
DEBUG ((DEBUG_INFO, " Restore primary 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;
}