/** @file | |
Routines supporting partition discovery and | |
logical device reading | |
Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR> | |
This program and the accompanying materials are licensed and made available | |
under the terms and conditions of the BSD License which accompanies this | |
distribution. The full text of the license may be found at | |
http://opensource.org/licenses/bsd-license.php | |
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
**/ | |
#include <IndustryStandard/Mbr.h> | |
#include <IndustryStandard/ElTorito.h> | |
#include "FatLitePeim.h" | |
/** | |
This function finds Eltorito partitions. Main algorithm | |
is ported from DXE partition driver. | |
@param PrivateData The global memory map | |
@param 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 | |
FatFindEltoritoPartitions ( | |
IN PEI_FAT_PRIVATE_DATA *PrivateData, | |
IN UINTN ParentBlockDevNo | |
); | |
/** | |
This function finds Mbr partitions. Main algorithm | |
is ported from DXE partition driver. | |
@param PrivateData The global memory map | |
@param 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 | |
FatFindMbrPartitions ( | |
IN PEI_FAT_PRIVATE_DATA *PrivateData, | |
IN UINTN ParentBlockDevNo | |
); | |
/** | |
This function finds partitions (logical devices) in physical block devices. | |
@param PrivateData Global memory map for accessing global variables. | |
**/ | |
VOID | |
FatFindPartitions ( | |
IN PEI_FAT_PRIVATE_DATA *PrivateData | |
) | |
{ | |
BOOLEAN Found; | |
UINTN Index; | |
do { | |
Found = FALSE; | |
for (Index = 0; Index < PrivateData->BlockDeviceCount; Index++) { | |
if (!PrivateData->BlockDevice[Index].PartitionChecked) { | |
Found = FatFindMbrPartitions (PrivateData, Index); | |
if (!Found) { | |
Found = FatFindEltoritoPartitions (PrivateData, Index); | |
} | |
} | |
} | |
} while (Found && PrivateData->BlockDeviceCount <= PEI_FAT_MAX_BLOCK_DEVICE); | |
} | |
/** | |
This function finds Eltorito partitions. Main algorithm | |
is ported from DXE partition driver. | |
@param PrivateData The global memory map | |
@param 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 | |
FatFindEltoritoPartitions ( | |
IN PEI_FAT_PRIVATE_DATA *PrivateData, | |
IN UINTN ParentBlockDevNo | |
) | |
{ | |
EFI_STATUS Status; | |
BOOLEAN Found; | |
PEI_FAT_BLOCK_DEVICE *BlockDev; | |
PEI_FAT_BLOCK_DEVICE *ParentBlockDev; | |
UINT32 VolDescriptorLba; | |
UINT32 Lba; | |
CDROM_VOLUME_DESCRIPTOR *VolDescriptor; | |
ELTORITO_CATALOG *Catalog; | |
UINTN Check; | |
UINTN Index; | |
UINTN MaxIndex; | |
UINT16 *CheckBuffer; | |
UINT32 SubBlockSize; | |
UINT32 SectorCount; | |
UINT32 VolSpaceSize; | |
if (ParentBlockDevNo > PEI_FAT_MAX_BLOCK_DEVICE - 1) { | |
return FALSE; | |
} | |
Found = FALSE; | |
ParentBlockDev = &(PrivateData->BlockDevice[ParentBlockDevNo]); | |
VolSpaceSize = 0; | |
// | |
// CD_ROM has the fixed block size as 2048 bytes | |
// | |
if (ParentBlockDev->BlockSize != 2048) { | |
return FALSE; | |
} | |
VolDescriptor = (CDROM_VOLUME_DESCRIPTOR *) PrivateData->BlockData; | |
Catalog = (ELTORITO_CATALOG *) VolDescriptor; | |
// | |
// the ISO-9660 volume descriptor starts at 32k on the media | |
// and CD_ROM has the fixed block size as 2048 bytes, so... | |
// | |
VolDescriptorLba = 15; | |
// | |
// ((16*2048) / Media->BlockSize) - 1; | |
// | |
// Loop: handle one volume descriptor per time | |
// | |
while (TRUE) { | |
VolDescriptorLba += 1; | |
if (VolDescriptorLba > ParentBlockDev->LastBlock) { | |
// | |
// We are pointing past the end of the device so exit | |
// | |
break; | |
} | |
Status = FatReadBlock ( | |
PrivateData, | |
ParentBlockDevNo, | |
VolDescriptorLba, | |
ParentBlockDev->BlockSize, | |
VolDescriptor | |
); | |
if (EFI_ERROR (Status)) { | |
break; | |
} | |
// | |
// Check for valid volume descriptor signature | |
// | |
if (VolDescriptor->Unknown.Type == CDVOL_TYPE_END || | |
CompareMem (VolDescriptor->Unknown.Id, CDVOL_ID, sizeof (VolDescriptor->Unknown.Id)) != 0 | |
) { | |
// | |
// end of Volume descriptor list | |
// | |
break; | |
} | |
// | |
// Read the Volume Space Size from Primary Volume Descriptor 81-88 byte | |
// | |
if (VolDescriptor->Unknown.Type == CDVOL_TYPE_CODED) { | |
VolSpaceSize = VolDescriptor->PrimaryVolume.VolSpaceSize[1]; | |
} | |
// | |
// Is it an El Torito volume descriptor? | |
// | |
if (CompareMem ( | |
VolDescriptor->BootRecordVolume.SystemId, | |
CDVOL_ELTORITO_ID, | |
sizeof (CDVOL_ELTORITO_ID) - 1 | |
) != 0) { | |
continue; | |
} | |
// | |
// Read in the boot El Torito boot catalog | |
// | |
Lba = UNPACK_INT32 (VolDescriptor->BootRecordVolume.EltCatalog); | |
if (Lba > ParentBlockDev->LastBlock) { | |
continue; | |
} | |
Status = FatReadBlock ( | |
PrivateData, | |
ParentBlockDevNo, | |
Lba, | |
ParentBlockDev->BlockSize, | |
Catalog | |
); | |
if (EFI_ERROR (Status)) { | |
continue; | |
} | |
// | |
// We don't care too much about the Catalog header's contents, but we do want | |
// to make sure it looks like a Catalog header | |
// | |
if (Catalog->Catalog.Indicator != ELTORITO_ID_CATALOG || Catalog->Catalog.Id55AA != 0xAA55) { | |
continue; | |
} | |
Check = 0; | |
CheckBuffer = (UINT16 *) Catalog; | |
for (Index = 0; Index < sizeof (ELTORITO_CATALOG) / sizeof (UINT16); Index += 1) { | |
Check += CheckBuffer[Index]; | |
} | |
if ((Check & 0xFFFF) != 0) { | |
continue; | |
} | |
MaxIndex = ParentBlockDev->BlockSize / sizeof (ELTORITO_CATALOG); | |
for (Index = 1; Index < MaxIndex; Index += 1) { | |
// | |
// Next entry | |
// | |
Catalog += 1; | |
// | |
// Check this entry | |
// | |
if (Catalog->Boot.Indicator != ELTORITO_ID_SECTION_BOOTABLE || Catalog->Boot.Lba == 0) { | |
continue; | |
} | |
SubBlockSize = 512; | |
SectorCount = Catalog->Boot.SectorCount; | |
switch (Catalog->Boot.MediaType) { | |
case ELTORITO_NO_EMULATION: | |
SubBlockSize = ParentBlockDev->BlockSize; | |
SectorCount = Catalog->Boot.SectorCount; | |
break; | |
case ELTORITO_HARD_DISK: | |
break; | |
case ELTORITO_12_DISKETTE: | |
SectorCount = 0x50 * 0x02 * 0x0F; | |
break; | |
case ELTORITO_14_DISKETTE: | |
SectorCount = 0x50 * 0x02 * 0x12; | |
break; | |
case ELTORITO_28_DISKETTE: | |
SectorCount = 0x50 * 0x02 * 0x24; | |
break; | |
default: | |
SectorCount = 0; | |
SubBlockSize = ParentBlockDev->BlockSize; | |
break; | |
} | |
if (SectorCount < 2) { | |
SectorCount = (VolSpaceSize > ParentBlockDev->LastBlock + 1) ? (UINT32) (ParentBlockDev->LastBlock - Catalog->Boot.Lba + 1) : (UINT32) (VolSpaceSize - Catalog->Boot.Lba); | |
} | |
// | |
// Register this partition | |
// | |
if (PrivateData->BlockDeviceCount < PEI_FAT_MAX_BLOCK_DEVICE) { | |
Found = TRUE; | |
BlockDev = &(PrivateData->BlockDevice[PrivateData->BlockDeviceCount]); | |
BlockDev->BlockSize = SubBlockSize; | |
BlockDev->LastBlock = SectorCount - 1; | |
BlockDev->IoAlign = ParentBlockDev->IoAlign; | |
BlockDev->Logical = TRUE; | |
BlockDev->PartitionChecked = FALSE; | |
BlockDev->StartingPos = MultU64x32 (Catalog->Boot.Lba, ParentBlockDev->BlockSize); | |
BlockDev->ParentDevNo = ParentBlockDevNo; | |
PrivateData->BlockDeviceCount++; | |
} | |
} | |
} | |
ParentBlockDev->PartitionChecked = TRUE; | |
return Found; | |
} | |
/** | |
Test to see if the Mbr buffer is a valid MBR | |
@param Mbr Parent Handle | |
@param LastLba Last Lba address on the device. | |
@retval TRUE Mbr is a Valid MBR | |
@retval FALSE Mbr is not a Valid MBR | |
**/ | |
BOOLEAN | |
PartitionValidMbr ( | |
IN MASTER_BOOT_RECORD *Mbr, | |
IN EFI_PEI_LBA LastLba | |
) | |
{ | |
UINT32 StartingLBA; | |
UINT32 EndingLBA; | |
UINT32 NewEndingLBA; | |
INTN Index1; | |
INTN Index2; | |
BOOLEAN MbrValid; | |
if (Mbr->Signature != MBR_SIGNATURE) { | |
return FALSE; | |
} | |
// | |
// The BPB also has this signature, so it can not be used alone. | |
// | |
MbrValid = FALSE; | |
for (Index1 = 0; Index1 < MAX_MBR_PARTITIONS; Index1++) { | |
if (Mbr->Partition[Index1].OSIndicator == 0x00 || UNPACK_UINT32 (Mbr->Partition[Index1].SizeInLBA) == 0) { | |
continue; | |
} | |
MbrValid = TRUE; | |
StartingLBA = UNPACK_UINT32 (Mbr->Partition[Index1].StartingLBA); | |
EndingLBA = StartingLBA + UNPACK_UINT32 (Mbr->Partition[Index1].SizeInLBA) - 1; | |
if (EndingLBA > LastLba) { | |
// | |
// Compatability Errata: | |
// Some systems try to hide drive space with thier INT 13h driver | |
// This does not hide space from the OS driver. This means the MBR | |
// that gets created from DOS is smaller than the MBR created from | |
// a real OS (NT & Win98). This leads to BlockIo->LastBlock being | |
// wrong on some systems FDISKed by the OS. | |
// | |
// return FALSE Because no block devices on a system are implemented | |
// with INT 13h | |
// | |
return FALSE; | |
} | |
for (Index2 = Index1 + 1; Index2 < MAX_MBR_PARTITIONS; Index2++) { | |
if (Mbr->Partition[Index2].OSIndicator == 0x00 || UNPACK_INT32 (Mbr->Partition[Index2].SizeInLBA) == 0) { | |
continue; | |
} | |
NewEndingLBA = UNPACK_UINT32 (Mbr->Partition[Index2].StartingLBA) + UNPACK_UINT32 (Mbr->Partition[Index2].SizeInLBA) - 1; | |
if (NewEndingLBA >= StartingLBA && UNPACK_UINT32 (Mbr->Partition[Index2].StartingLBA) <= EndingLBA) { | |
// | |
// This region overlaps with the Index1'th region | |
// | |
return FALSE; | |
} | |
} | |
} | |
// | |
// Non of the regions overlapped so MBR is O.K. | |
// | |
return MbrValid; | |
} | |
/** | |
This function finds Mbr partitions. Main algorithm | |
is ported from DXE partition driver. | |
@param PrivateData The global memory map | |
@param 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 | |
FatFindMbrPartitions ( | |
IN PEI_FAT_PRIVATE_DATA *PrivateData, | |
IN UINTN ParentBlockDevNo | |
) | |
{ | |
EFI_STATUS Status; | |
MASTER_BOOT_RECORD *Mbr; | |
UINTN Index; | |
BOOLEAN Found; | |
PEI_FAT_BLOCK_DEVICE *ParentBlockDev; | |
PEI_FAT_BLOCK_DEVICE *BlockDev; | |
if (ParentBlockDevNo > PEI_FAT_MAX_BLOCK_DEVICE - 1) { | |
return FALSE; | |
} | |
ParentBlockDev = &(PrivateData->BlockDevice[ParentBlockDevNo]); | |
Found = FALSE; | |
Mbr = (MASTER_BOOT_RECORD *) PrivateData->BlockData; | |
Status = FatReadBlock ( | |
PrivateData, | |
ParentBlockDevNo, | |
0, | |
ParentBlockDev->BlockSize, | |
Mbr | |
); | |
if (EFI_ERROR (Status) || !PartitionValidMbr (Mbr, ParentBlockDev->LastBlock)) { | |
goto Done; | |
} | |
// | |
// We have a valid mbr - add each partition | |
// | |
for (Index = 0; Index < MAX_MBR_PARTITIONS; Index++) { | |
if (Mbr->Partition[Index].OSIndicator == 0x00 || UNPACK_INT32 (Mbr->Partition[Index].SizeInLBA) == 0) { | |
// | |
// Don't use null MBR entries | |
// | |
continue; | |
} | |
// | |
// Register this partition | |
// | |
if (PrivateData->BlockDeviceCount < PEI_FAT_MAX_BLOCK_DEVICE) { | |
Found = TRUE; | |
BlockDev = &(PrivateData->BlockDevice[PrivateData->BlockDeviceCount]); | |
BlockDev->BlockSize = MBR_SIZE; | |
BlockDev->LastBlock = UNPACK_INT32 (Mbr->Partition[Index].SizeInLBA) - 1; | |
BlockDev->IoAlign = ParentBlockDev->IoAlign; | |
BlockDev->Logical = TRUE; | |
BlockDev->PartitionChecked = FALSE; | |
BlockDev->StartingPos = MultU64x32 ( | |
UNPACK_INT32 (Mbr->Partition[Index].StartingLBA), | |
ParentBlockDev->BlockSize | |
); | |
BlockDev->ParentDevNo = ParentBlockDevNo; | |
PrivateData->BlockDeviceCount++; | |
} | |
} | |
Done: | |
ParentBlockDev->PartitionChecked = TRUE; | |
return Found; | |
} |