Signed-off-by: jljusten Reviewed-by: mdkinney (based on FatPkg commit bead7f219277e063ed28589de8ddd01cf180c1a8) [jordan.l.justen@intel.com: Use script to relicense to 2-clause BSD] Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Jordan Justen <jordan.l.justen@intel.com> Acked-by: Mark Doran <mark.doran@intel.com> Acked-by: Laszlo Ersek <lersek@redhat.com>
		
			
				
	
	
		
			467 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			467 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @file
 | 
						|
  Routines supporting partition discovery and 
 | 
						|
  logical device reading
 | 
						|
 | 
						|
Copyright (c) 2006 - 2008, 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;
 | 
						|
}
 |