/** @file | |
Scan for an UDF file system on a formatted media. | |
Caution: This file requires additional review when modified. | |
This driver will have external input - CD/DVD media. | |
This external input must be validated carefully to avoid security issue like | |
buffer overflow, integer overflow. | |
FindUdfFileSystem() routine will consume the media properties and do basic | |
validation. | |
Copyright (c) 2018 Qualcomm Datacenter Technologies, Inc. | |
Copyright (C) 2014-2017 Paulo Alcantara <pcacjr@zytor.com> | |
Copyright (c) 2018, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "Partition.h" | |
#define MAX_CORRECTION_BLOCKS_NUM 512u | |
// | |
// C5BD4D42-1A76-4996-8956-73CDA326CD0A | |
// | |
#define EFI_UDF_DEVICE_PATH_GUID \ | |
{ 0xC5BD4D42, 0x1A76, 0x4996, \ | |
{ 0x89, 0x56, 0x73, 0xCD, 0xA3, 0x26, 0xCD, 0x0A } \ | |
} | |
typedef struct { | |
VENDOR_DEVICE_PATH DevicePath; | |
EFI_DEVICE_PATH_PROTOCOL End; | |
} UDF_DEVICE_PATH; | |
// | |
// Vendor-Defined Device Path GUID for UDF file system | |
// | |
EFI_GUID gUdfDevPathGuid = EFI_UDF_DEVICE_PATH_GUID; | |
// | |
// Vendor-Defined Media Device Path for UDF file system | |
// | |
UDF_DEVICE_PATH gUdfDevicePath = { | |
{ | |
{ MEDIA_DEVICE_PATH, MEDIA_VENDOR_DP, | |
{ sizeof (VENDOR_DEVICE_PATH), 0 } | |
}, | |
EFI_UDF_DEVICE_PATH_GUID | |
}, | |
{ END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, | |
{ sizeof (EFI_DEVICE_PATH_PROTOCOL), 0 } | |
} | |
}; | |
/** | |
Find the anchor volume descriptor pointer. | |
@param[in] BlockIo BlockIo interface. | |
@param[in] DiskIo DiskIo interface. | |
@param[out] AnchorPoint Anchor volume descriptor pointer. | |
@param[out] LastRecordedBlock Last recorded block. | |
@retval EFI_SUCCESS Anchor volume descriptor pointer found. | |
@retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. | |
@retval other Anchor volume descriptor pointer not found. | |
**/ | |
EFI_STATUS | |
FindAnchorVolumeDescriptorPointer ( | |
IN EFI_BLOCK_IO_PROTOCOL *BlockIo, | |
IN EFI_DISK_IO_PROTOCOL *DiskIo, | |
OUT UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER *AnchorPoint, | |
OUT EFI_LBA *LastRecordedBlock | |
) | |
{ | |
EFI_STATUS Status; | |
UINT32 BlockSize; | |
EFI_LBA EndLBA; | |
UDF_DESCRIPTOR_TAG *DescriptorTag; | |
UINTN AvdpsCount; | |
UINTN Size; | |
UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER *AnchorPoints; | |
INTN Index; | |
UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER *AnchorPointPtr; | |
EFI_LBA LastAvdpBlockNum; | |
// | |
// UDF 2.60, 2.2.3 Anchor Volume Descriptor Pointer | |
// | |
// An Anchor Volume Descriptor Pointer structure shall be recorded in at | |
// least 2 of the following 3 locations on the media: Logical Sector 256, | |
// N - 256 or N, where N is the last *addressable* sector of a volume. | |
// | |
// To figure out what logical sector N is, the SCSI commands READ CAPACITY and | |
// READ TRACK INFORMATION are used, however many drives or medias report their | |
// "last recorded block" wrongly. Although, READ CAPACITY returns the last | |
// readable data block but there might be unwritten blocks, which are located | |
// outside any track and therefore AVDP will not be found at block N. | |
// | |
// That said, we define a magic number of 512 blocks to be used as correction | |
// when attempting to find AVDP and define last block number. | |
// | |
BlockSize = BlockIo->Media->BlockSize; | |
EndLBA = BlockIo->Media->LastBlock; | |
*LastRecordedBlock = EndLBA; | |
AvdpsCount = 0; | |
// | |
// Check if the block size of the underlying media can hold the data of an | |
// Anchor Volume Descriptor Pointer | |
// | |
if (BlockSize < sizeof (UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER)) { | |
DEBUG (( | |
DEBUG_ERROR, | |
"%a: Media block size 0x%x unable to hold an AVDP.\n", | |
__func__, | |
BlockSize | |
)); | |
return EFI_UNSUPPORTED; | |
} | |
// | |
// Find AVDP at block 256 | |
// | |
Status = DiskIo->ReadDisk ( | |
DiskIo, | |
BlockIo->Media->MediaId, | |
MultU64x32 (256, BlockSize), | |
sizeof (*AnchorPoint), | |
AnchorPoint | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
DescriptorTag = &AnchorPoint->DescriptorTag; | |
// | |
// Check if read block is a valid AVDP descriptor | |
// | |
if (DescriptorTag->TagIdentifier == UdfAnchorVolumeDescriptorPointer) { | |
DEBUG ((DEBUG_INFO, "%a: found AVDP at block %d\n", __func__, 256)); | |
AvdpsCount++; | |
} | |
// | |
// Find AVDP at block N - 256 | |
// | |
Status = DiskIo->ReadDisk ( | |
DiskIo, | |
BlockIo->Media->MediaId, | |
MultU64x32 ((UINT64)EndLBA - 256, BlockSize), | |
sizeof (*AnchorPoint), | |
AnchorPoint | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Check if read block is a valid AVDP descriptor | |
// | |
if ((DescriptorTag->TagIdentifier == UdfAnchorVolumeDescriptorPointer) && | |
(++AvdpsCount == 2)) | |
{ | |
DEBUG (( | |
DEBUG_INFO, | |
"%a: found AVDP at block %Ld\n", | |
__func__, | |
EndLBA - 256 | |
)); | |
return EFI_SUCCESS; | |
} | |
// | |
// Check if at least one AVDP was found in previous locations | |
// | |
if (AvdpsCount == 0) { | |
return EFI_VOLUME_CORRUPTED; | |
} | |
// | |
// Find AVDP at block N | |
// | |
Status = DiskIo->ReadDisk ( | |
DiskIo, | |
BlockIo->Media->MediaId, | |
MultU64x32 ((UINT64)EndLBA, BlockSize), | |
sizeof (*AnchorPoint), | |
AnchorPoint | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Check if read block is a valid AVDP descriptor | |
// | |
if (DescriptorTag->TagIdentifier == UdfAnchorVolumeDescriptorPointer) { | |
return EFI_SUCCESS; | |
} | |
// | |
// No AVDP found at block N. Possibly drive/media returned bad last recorded | |
// block, or it is part of unwritten data blocks and outside any track. | |
// | |
// Search backwards for an AVDP from block N-1 through | |
// N-MAX_CORRECTION_BLOCKS_NUM. If any AVDP is found, then correct last block | |
// number for the new UDF partition child handle. | |
// | |
Size = MAX_CORRECTION_BLOCKS_NUM * BlockSize; | |
AnchorPoints = AllocateZeroPool (Size); | |
if (AnchorPoints == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
// | |
// Read consecutive MAX_CORRECTION_BLOCKS_NUM disk blocks | |
// | |
Status = DiskIo->ReadDisk ( | |
DiskIo, | |
BlockIo->Media->MediaId, | |
MultU64x32 ((UINT64)EndLBA - MAX_CORRECTION_BLOCKS_NUM, BlockSize), | |
Size, | |
AnchorPoints | |
); | |
if (EFI_ERROR (Status)) { | |
goto Out_Free; | |
} | |
Status = EFI_VOLUME_CORRUPTED; | |
// | |
// Search for AVDP from blocks N-1 through N-MAX_CORRECTION_BLOCKS_NUM | |
// | |
for (Index = MAX_CORRECTION_BLOCKS_NUM - 2; Index >= 0; Index--) { | |
AnchorPointPtr = (VOID *)((UINTN)AnchorPoints + Index * BlockSize); | |
DescriptorTag = &AnchorPointPtr->DescriptorTag; | |
// | |
// Check if read block is a valid AVDP descriptor | |
// | |
if (DescriptorTag->TagIdentifier == UdfAnchorVolumeDescriptorPointer) { | |
// | |
// Calculate last recorded block number | |
// | |
LastAvdpBlockNum = EndLBA - (MAX_CORRECTION_BLOCKS_NUM - Index); | |
DEBUG (( | |
DEBUG_WARN, | |
"%a: found AVDP at block %Ld\n", | |
__func__, | |
LastAvdpBlockNum | |
)); | |
DEBUG (( | |
DEBUG_WARN, | |
"%a: correcting last block from %Ld to %Ld\n", | |
__func__, | |
EndLBA, | |
LastAvdpBlockNum | |
)); | |
// | |
// Save read AVDP from last block | |
// | |
CopyMem (AnchorPoint, AnchorPointPtr, sizeof (*AnchorPointPtr)); | |
// | |
// Set last recorded block number | |
// | |
*LastRecordedBlock = LastAvdpBlockNum; | |
Status = EFI_SUCCESS; | |
break; | |
} | |
} | |
Out_Free: | |
FreePool (AnchorPoints); | |
return Status; | |
} | |
/** | |
Find UDF volume identifiers in a Volume Recognition Sequence. | |
@param[in] BlockIo BlockIo interface. | |
@param[in] DiskIo DiskIo interface. | |
@retval EFI_SUCCESS UDF volume identifiers were found. | |
@retval EFI_NOT_FOUND UDF volume identifiers were not found. | |
@retval other Failed to perform disk I/O. | |
**/ | |
EFI_STATUS | |
FindUdfVolumeIdentifiers ( | |
IN EFI_BLOCK_IO_PROTOCOL *BlockIo, | |
IN EFI_DISK_IO_PROTOCOL *DiskIo | |
) | |
{ | |
EFI_STATUS Status; | |
UINT64 Offset; | |
UINT64 EndDiskOffset; | |
CDROM_VOLUME_DESCRIPTOR VolDescriptor; | |
CDROM_VOLUME_DESCRIPTOR TerminatingVolDescriptor; | |
ZeroMem ((VOID *)&TerminatingVolDescriptor, sizeof (CDROM_VOLUME_DESCRIPTOR)); | |
// | |
// Start Volume Recognition Sequence | |
// | |
EndDiskOffset = MultU64x32 ( | |
BlockIo->Media->LastBlock, | |
BlockIo->Media->BlockSize | |
); | |
for (Offset = UDF_VRS_START_OFFSET; Offset < EndDiskOffset; | |
Offset += UDF_LOGICAL_SECTOR_SIZE) | |
{ | |
// | |
// Check if block device has a Volume Structure Descriptor and an Extended | |
// Area. | |
// | |
Status = DiskIo->ReadDisk ( | |
DiskIo, | |
BlockIo->Media->MediaId, | |
Offset, | |
sizeof (CDROM_VOLUME_DESCRIPTOR), | |
(VOID *)&VolDescriptor | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
if (CompareMem ( | |
(VOID *)VolDescriptor.Unknown.Id, | |
(VOID *)UDF_BEA_IDENTIFIER, | |
sizeof (VolDescriptor.Unknown.Id) | |
) == 0) | |
{ | |
break; | |
} | |
if ((CompareMem ( | |
(VOID *)VolDescriptor.Unknown.Id, | |
(VOID *)CDVOL_ID, | |
sizeof (VolDescriptor.Unknown.Id) | |
) != 0) || | |
(CompareMem ( | |
(VOID *)&VolDescriptor, | |
(VOID *)&TerminatingVolDescriptor, | |
sizeof (CDROM_VOLUME_DESCRIPTOR) | |
) == 0)) | |
{ | |
return EFI_NOT_FOUND; | |
} | |
} | |
// | |
// Look for "NSR0{2,3}" identifiers in the Extended Area. | |
// | |
Offset += UDF_LOGICAL_SECTOR_SIZE; | |
if (Offset >= EndDiskOffset) { | |
return EFI_NOT_FOUND; | |
} | |
Status = DiskIo->ReadDisk ( | |
DiskIo, | |
BlockIo->Media->MediaId, | |
Offset, | |
sizeof (CDROM_VOLUME_DESCRIPTOR), | |
(VOID *)&VolDescriptor | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
if ((CompareMem ( | |
(VOID *)VolDescriptor.Unknown.Id, | |
(VOID *)UDF_NSR2_IDENTIFIER, | |
sizeof (VolDescriptor.Unknown.Id) | |
) != 0) && | |
(CompareMem ( | |
(VOID *)VolDescriptor.Unknown.Id, | |
(VOID *)UDF_NSR3_IDENTIFIER, | |
sizeof (VolDescriptor.Unknown.Id) | |
) != 0)) | |
{ | |
return EFI_NOT_FOUND; | |
} | |
// | |
// Look for "TEA01" identifier in the Extended Area | |
// | |
Offset += UDF_LOGICAL_SECTOR_SIZE; | |
if (Offset >= EndDiskOffset) { | |
return EFI_NOT_FOUND; | |
} | |
Status = DiskIo->ReadDisk ( | |
DiskIo, | |
BlockIo->Media->MediaId, | |
Offset, | |
sizeof (CDROM_VOLUME_DESCRIPTOR), | |
(VOID *)&VolDescriptor | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
if (CompareMem ( | |
(VOID *)VolDescriptor.Unknown.Id, | |
(VOID *)UDF_TEA_IDENTIFIER, | |
sizeof (VolDescriptor.Unknown.Id) | |
) != 0) | |
{ | |
return EFI_NOT_FOUND; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Check if Logical Volume Descriptor is supported by current EDK2 UDF file | |
system implementation. | |
@param[in] LogicalVolDesc Logical Volume Descriptor pointer. | |
@retval TRUE Logical Volume Descriptor is supported. | |
@retval FALSE Logical Volume Descriptor is not supported. | |
**/ | |
BOOLEAN | |
IsLogicalVolumeDescriptorSupported ( | |
UDF_LOGICAL_VOLUME_DESCRIPTOR *LogicalVolDesc | |
) | |
{ | |
// | |
// Check for a valid UDF revision range | |
// | |
switch (LogicalVolDesc->DomainIdentifier.Suffix.Domain.UdfRevision) { | |
case 0x0102: | |
case 0x0150: | |
case 0x0200: | |
case 0x0201: | |
case 0x0250: | |
case 0x0260: | |
break; | |
default: | |
return FALSE; | |
} | |
// | |
// Check for a single Partition Map | |
// | |
if (LogicalVolDesc->NumberOfPartitionMaps > 1) { | |
return FALSE; | |
} | |
// | |
// UDF 1.02 revision supports only Type 1 (Physical) partitions, but | |
// let's check it any way. | |
// | |
// PartitionMap[0] -> type | |
// PartitionMap[1] -> length (in bytes) | |
// | |
if ((LogicalVolDesc->PartitionMaps[0] != 1) || | |
(LogicalVolDesc->PartitionMaps[1] != 6)) | |
{ | |
return FALSE; | |
} | |
return TRUE; | |
} | |
/** | |
Find UDF logical volume location and whether it is supported by current EDK2 | |
UDF file system implementation. | |
@param[in] BlockIo BlockIo interface. | |
@param[in] DiskIo DiskIo interface. | |
@param[in] AnchorPoint Anchor volume descriptor pointer. | |
@param[in] LastRecordedBlock Last recorded block in media. | |
@param[out] MainVdsStartBlock Main VDS starting block number. | |
@param[out] MainVdsEndBlock Main VDS ending block number. | |
@retval EFI_SUCCESS UDF logical volume was found. | |
@retval EFI_VOLUME_CORRUPTED UDF file system structures are corrupted. | |
@retval EFI_UNSUPPORTED UDF logical volume is not supported. | |
@retval other Failed to perform disk I/O. | |
**/ | |
EFI_STATUS | |
FindLogicalVolumeLocation ( | |
IN EFI_BLOCK_IO_PROTOCOL *BlockIo, | |
IN EFI_DISK_IO_PROTOCOL *DiskIo, | |
IN UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER *AnchorPoint, | |
IN EFI_LBA LastRecordedBlock, | |
OUT UINT64 *MainVdsStartBlock, | |
OUT UINT64 *MainVdsEndBlock | |
) | |
{ | |
EFI_STATUS Status; | |
UINT32 BlockSize; | |
UDF_EXTENT_AD *ExtentAd; | |
UINT64 SeqBlocksNum; | |
UINT64 SeqStartBlock; | |
UINT64 GuardMainVdsStartBlock; | |
VOID *Buffer; | |
UINT64 SeqEndBlock; | |
BOOLEAN StopSequence; | |
UINTN LvdsCount; | |
UDF_LOGICAL_VOLUME_DESCRIPTOR *LogicalVolDesc; | |
UDF_DESCRIPTOR_TAG *DescriptorTag; | |
BlockSize = BlockIo->Media->BlockSize; | |
ExtentAd = &AnchorPoint->MainVolumeDescriptorSequenceExtent; | |
// | |
// UDF 2.60, 2.2.3.1 struct MainVolumeDescriptorSequenceExtent | |
// | |
// The Main Volume Descriptor Sequence Extent shall have a minimum length of | |
// 16 logical sectors. | |
// | |
// Also make sure it does not exceed maximum number of blocks in the disk. | |
// | |
SeqBlocksNum = DivU64x32 ((UINT64)ExtentAd->ExtentLength, BlockSize); | |
if ((SeqBlocksNum < 16) || ((EFI_LBA)SeqBlocksNum > LastRecordedBlock + 1)) { | |
return EFI_VOLUME_CORRUPTED; | |
} | |
// | |
// Check for valid Volume Descriptor Sequence starting block number | |
// | |
SeqStartBlock = (UINT64)ExtentAd->ExtentLocation; | |
if ((SeqStartBlock > LastRecordedBlock) || | |
(SeqStartBlock + SeqBlocksNum - 1 > LastRecordedBlock)) | |
{ | |
return EFI_VOLUME_CORRUPTED; | |
} | |
GuardMainVdsStartBlock = SeqStartBlock; | |
// | |
// Allocate buffer for reading disk blocks | |
// | |
Buffer = AllocateZeroPool ((UINTN)BlockSize); | |
if (Buffer == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
SeqEndBlock = SeqStartBlock + SeqBlocksNum; | |
StopSequence = FALSE; | |
LvdsCount = 0; | |
Status = EFI_VOLUME_CORRUPTED; | |
// | |
// Start Main Volume Descriptor Sequence | |
// | |
for ( ; SeqStartBlock < SeqEndBlock && !StopSequence; SeqStartBlock++) { | |
// | |
// Read disk block | |
// | |
Status = BlockIo->ReadBlocks ( | |
BlockIo, | |
BlockIo->Media->MediaId, | |
SeqStartBlock, | |
BlockSize, | |
Buffer | |
); | |
if (EFI_ERROR (Status)) { | |
goto Out_Free; | |
} | |
DescriptorTag = Buffer; | |
// | |
// ECMA 167, 8.4.1 Contents of a Volume Descriptor Sequence | |
// | |
// - A Volume Descriptor Sequence shall contain one or more Primary Volume | |
// Descriptors. | |
// - A Volume Descriptor Sequence shall contain zero or more Implementation | |
// Use Volume Descriptors. | |
// - A Volume Descriptor Sequence shall contain zero or more Partition | |
// Descriptors. | |
// - A Volume Descriptor Sequence shall contain zero or more Logical Volume | |
// Descriptors. | |
// - A Volume Descriptor Sequence shall contain zero or more Unallocated | |
// Space Descriptors. | |
// | |
switch (DescriptorTag->TagIdentifier) { | |
case UdfPrimaryVolumeDescriptor: | |
case UdfImplemenationUseVolumeDescriptor: | |
case UdfPartitionDescriptor: | |
case UdfUnallocatedSpaceDescriptor: | |
break; | |
case UdfLogicalVolumeDescriptor: | |
LogicalVolDesc = Buffer; | |
// | |
// Check for existence of a single LVD and whether it is supported by | |
// current EDK2 UDF file system implementation. | |
// | |
if ((++LvdsCount > 1) || | |
!IsLogicalVolumeDescriptorSupported (LogicalVolDesc)) | |
{ | |
Status = EFI_UNSUPPORTED; | |
StopSequence = TRUE; | |
} | |
break; | |
case UdfTerminatingDescriptor: | |
// | |
// Stop the sequence when we find a Terminating Descriptor | |
// (aka Unallocated Sector), se we don't have to walk all the unallocated | |
// area unnecessarily. | |
// | |
StopSequence = TRUE; | |
break; | |
default: | |
// | |
// An invalid Volume Descriptor has been found in the sequece. Volume is | |
// corrupted. | |
// | |
Status = EFI_VOLUME_CORRUPTED; | |
goto Out_Free; | |
} | |
} | |
// | |
// Check if LVD was found | |
// | |
if (!EFI_ERROR (Status) && (LvdsCount == 1)) { | |
*MainVdsStartBlock = GuardMainVdsStartBlock; | |
// | |
// We do not need to read either LVD or PD descriptors to know the last | |
// valid block in the found UDF file system. It's already | |
// LastRecordedBlock. | |
// | |
*MainVdsEndBlock = LastRecordedBlock; | |
Status = EFI_SUCCESS; | |
} | |
Out_Free: | |
// | |
// Free block read buffer | |
// | |
FreePool (Buffer); | |
return Status; | |
} | |
/** | |
Find a supported UDF file system in block device. | |
@attention This is boundary function that may receive untrusted input. | |
@attention The input is from Partition. | |
The CD/DVD media is the external input, so this routine will do basic | |
validation for the media. | |
@param[in] BlockIo BlockIo interface. | |
@param[in] DiskIo DiskIo interface. | |
@param[out] StartingLBA UDF file system starting LBA. | |
@param[out] EndingLBA UDF file system starting LBA. | |
@retval EFI_SUCCESS UDF file system was found. | |
@retval other UDF file system was not found. | |
**/ | |
EFI_STATUS | |
FindUdfFileSystem ( | |
IN EFI_BLOCK_IO_PROTOCOL *BlockIo, | |
IN EFI_DISK_IO_PROTOCOL *DiskIo, | |
OUT EFI_LBA *StartingLBA, | |
OUT EFI_LBA *EndingLBA | |
) | |
{ | |
EFI_STATUS Status; | |
UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER AnchorPoint; | |
EFI_LBA LastRecordedBlock; | |
// | |
// Find UDF volume identifiers | |
// | |
Status = FindUdfVolumeIdentifiers (BlockIo, DiskIo); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Find Anchor Volume Descriptor Pointer | |
// | |
Status = FindAnchorVolumeDescriptorPointer ( | |
BlockIo, | |
DiskIo, | |
&AnchorPoint, | |
&LastRecordedBlock | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Find Logical Volume location | |
// | |
Status = FindLogicalVolumeLocation ( | |
BlockIo, | |
DiskIo, | |
&AnchorPoint, | |
LastRecordedBlock, | |
(UINT64 *)StartingLBA, | |
(UINT64 *)EndingLBA | |
); | |
return Status; | |
} | |
/** | |
Install child handles if the Handle supports UDF/ECMA-167 volume format. | |
@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 Child handle(s) was added. | |
@retval EFI_MEDIA_CHANGED Media changed Detected. | |
@retval other no child handle was added. | |
**/ | |
EFI_STATUS | |
PartitionInstallUdfChildHandles ( | |
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 | |
) | |
{ | |
UINT32 RemainderByMediaBlockSize; | |
EFI_STATUS Status; | |
EFI_BLOCK_IO_MEDIA *Media; | |
EFI_PARTITION_INFO_PROTOCOL PartitionInfo; | |
EFI_LBA StartingLBA; | |
EFI_LBA EndingLBA; | |
BOOLEAN ChildCreated; | |
Media = BlockIo->Media; | |
ChildCreated = FALSE; | |
// | |
// Check if UDF logical block size is multiple of underlying device block size | |
// | |
DivU64x32Remainder ( | |
UDF_LOGICAL_SECTOR_SIZE, // Dividend | |
Media->BlockSize, // Divisor | |
&RemainderByMediaBlockSize // Remainder | |
); | |
if (RemainderByMediaBlockSize != 0) { | |
return EFI_NOT_FOUND; | |
} | |
// | |
// Detect El Torito feature first. | |
// And always continue to search for UDF. | |
// | |
Status = PartitionInstallElToritoChildHandles ( | |
This, | |
Handle, | |
DiskIo, | |
DiskIo2, | |
BlockIo, | |
BlockIo2, | |
DevicePath | |
); | |
if (!EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_INFO, "PartitionDxe: El Torito standard found on handle 0x%p.\n", Handle)); | |
ChildCreated = TRUE; | |
} | |
// | |
// Search for an UDF file system on block device | |
// | |
Status = FindUdfFileSystem (BlockIo, DiskIo, &StartingLBA, &EndingLBA); | |
if (EFI_ERROR (Status)) { | |
return (ChildCreated ? EFI_SUCCESS : EFI_NOT_FOUND); | |
} | |
// | |
// Create Partition Info protocol for UDF file system | |
// | |
ZeroMem (&PartitionInfo, sizeof (EFI_PARTITION_INFO_PROTOCOL)); | |
PartitionInfo.Revision = EFI_PARTITION_INFO_PROTOCOL_REVISION; | |
PartitionInfo.Type = PARTITION_TYPE_OTHER; | |
// | |
// Install partition child handle for UDF file system | |
// | |
Status = PartitionInstallChildHandle ( | |
This, | |
Handle, | |
DiskIo, | |
DiskIo2, | |
BlockIo, | |
BlockIo2, | |
DevicePath, | |
(EFI_DEVICE_PATH_PROTOCOL *)&gUdfDevicePath, | |
&PartitionInfo, | |
StartingLBA, | |
EndingLBA, | |
Media->BlockSize, | |
NULL | |
); | |
if (EFI_ERROR (Status)) { | |
return (ChildCreated ? EFI_SUCCESS : Status); | |
} | |
return EFI_SUCCESS; | |
} |