https://bugzilla.tianocore.org/show_bug.cgi?id=2264 Cc: Ray Ni <ray.ni@intel.com> Signed-off-by: Michael D Kinney <michael.d.kinney@intel.com> Reviewed-by: Ray Ni <ray.ni@intel.com>
		
			
				
	
	
		
			388 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			388 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
|   Initialization routines.
 | |
| 
 | |
| Copyright (c) 2005 - 2013, Intel Corporation. All rights reserved.<BR>
 | |
| SPDX-License-Identifier: BSD-2-Clause-Patent
 | |
| 
 | |
| **/
 | |
| 
 | |
| #include "Fat.h"
 | |
| 
 | |
| /**
 | |
| 
 | |
|   Allocates volume structure, detects FAT file system, installs protocol,
 | |
|   and initialize cache.
 | |
| 
 | |
|   @param  Handle                - The handle of parent device.
 | |
|   @param  DiskIo                - The DiskIo of parent device.
 | |
|   @param  DiskIo2               - The DiskIo2 of parent device.
 | |
|   @param  BlockIo               - The BlockIo of parent device.
 | |
| 
 | |
|   @retval EFI_SUCCESS           - Allocate a new volume successfully.
 | |
|   @retval EFI_OUT_OF_RESOURCES  - Can not allocate the memory.
 | |
|   @return Others                - Allocating a new volume failed.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| FatAllocateVolume (
 | |
|   IN  EFI_HANDLE                Handle,
 | |
|   IN  EFI_DISK_IO_PROTOCOL      *DiskIo,
 | |
|   IN  EFI_DISK_IO2_PROTOCOL     *DiskIo2,
 | |
|   IN  EFI_BLOCK_IO_PROTOCOL     *BlockIo
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   FAT_VOLUME  *Volume;
 | |
| 
 | |
|   //
 | |
|   // Allocate a volume structure
 | |
|   //
 | |
|   Volume = AllocateZeroPool (sizeof (FAT_VOLUME));
 | |
|   if (Volume == NULL) {
 | |
|     return EFI_OUT_OF_RESOURCES;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Initialize the structure
 | |
|   //
 | |
|   Volume->Signature                   = FAT_VOLUME_SIGNATURE;
 | |
|   Volume->Handle                      = Handle;
 | |
|   Volume->DiskIo                      = DiskIo;
 | |
|   Volume->DiskIo2                     = DiskIo2;
 | |
|   Volume->BlockIo                     = BlockIo;
 | |
|   Volume->MediaId                     = BlockIo->Media->MediaId;
 | |
|   Volume->ReadOnly                    = BlockIo->Media->ReadOnly;
 | |
|   Volume->VolumeInterface.Revision    = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION;
 | |
|   Volume->VolumeInterface.OpenVolume  = FatOpenVolume;
 | |
|   InitializeListHead (&Volume->CheckRef);
 | |
|   InitializeListHead (&Volume->DirCacheList);
 | |
|   //
 | |
|   // Initialize Root Directory entry
 | |
|   //
 | |
|   Volume->RootDirEnt.FileString       = Volume->RootFileString;
 | |
|   Volume->RootDirEnt.Entry.Attributes = FAT_ATTRIBUTE_DIRECTORY;
 | |
|   //
 | |
|   // Check to see if there's a file system on the volume
 | |
|   //
 | |
|   Status = FatOpenDevice (Volume);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     goto Done;
 | |
|   }
 | |
|   //
 | |
|   // Initialize cache
 | |
|   //
 | |
|   Status = FatInitializeDiskCache (Volume);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     goto Done;
 | |
|   }
 | |
|   //
 | |
|   // Install our protocol interfaces on the device's handle
 | |
|   //
 | |
|   Status = gBS->InstallMultipleProtocolInterfaces (
 | |
|                   &Volume->Handle,
 | |
|                   &gEfiSimpleFileSystemProtocolGuid,
 | |
|                   &Volume->VolumeInterface,
 | |
|                   NULL
 | |
|                   );
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     goto Done;
 | |
|   }
 | |
|   //
 | |
|   // Volume installed
 | |
|   //
 | |
|   DEBUG ((EFI_D_INIT, "Installed Fat filesystem on %p\n", Handle));
 | |
|   Volume->Valid = TRUE;
 | |
| 
 | |
| Done:
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     FatFreeVolume (Volume);
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
| 
 | |
|   Called by FatDriverBindingStop(), Abandon the volume.
 | |
| 
 | |
|   @param  Volume                - The volume to be abandoned.
 | |
| 
 | |
|   @retval EFI_SUCCESS           - Abandoned the volume successfully.
 | |
|   @return Others                - Can not uninstall the protocol interfaces.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| FatAbandonVolume (
 | |
|   IN FAT_VOLUME *Volume
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   BOOLEAN     LockedByMe;
 | |
| 
 | |
|   //
 | |
|   // Uninstall the protocol interface.
 | |
|   //
 | |
|   if (Volume->Handle != NULL) {
 | |
|     Status = gBS->UninstallMultipleProtocolInterfaces (
 | |
|                     Volume->Handle,
 | |
|                     &gEfiSimpleFileSystemProtocolGuid,
 | |
|                     &Volume->VolumeInterface,
 | |
|                     NULL
 | |
|                     );
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       return Status;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   LockedByMe = FALSE;
 | |
| 
 | |
|   //
 | |
|   // Acquire the lock.
 | |
|   // If the caller has already acquired the lock (which
 | |
|   // means we are in the process of some Fat operation),
 | |
|   // we can not acquire again.
 | |
|   //
 | |
|   Status = FatAcquireLockOrFail ();
 | |
|   if (!EFI_ERROR (Status)) {
 | |
|     LockedByMe = TRUE;
 | |
|   }
 | |
|   //
 | |
|   // The volume is still being used. Hence, set error flag for all OFiles still in
 | |
|   // use. In two cases, we could get here. One is EFI_MEDIA_CHANGED, the other is
 | |
|   // EFI_NO_MEDIA.
 | |
|   //
 | |
|   if (Volume->Root != NULL) {
 | |
|     FatSetVolumeError (
 | |
|       Volume->Root,
 | |
|       Volume->BlockIo->Media->MediaPresent ? EFI_MEDIA_CHANGED : EFI_NO_MEDIA
 | |
|       );
 | |
|   }
 | |
| 
 | |
|   Volume->Valid = FALSE;
 | |
| 
 | |
|   //
 | |
|   // Release the lock.
 | |
|   // If locked by me, this means DriverBindingStop is NOT
 | |
|   // called within an on-going Fat operation, so we should
 | |
|   // take responsibility to cleanup and free the volume.
 | |
|   // Otherwise, the DriverBindingStop is called within an on-going
 | |
|   // Fat operation, we shouldn't check reference, so just let outer
 | |
|   // FatCleanupVolume do the task.
 | |
|   //
 | |
|   if (LockedByMe) {
 | |
|     FatCleanupVolume (Volume, NULL, EFI_SUCCESS, NULL);
 | |
|     FatReleaseLock ();
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
| 
 | |
|   Detects FAT file system on Disk and set relevant fields of Volume.
 | |
| 
 | |
|   @param Volume                - The volume structure.
 | |
| 
 | |
|   @retval EFI_SUCCESS           - The Fat File System is detected successfully
 | |
|   @retval EFI_UNSUPPORTED       - The volume is not FAT file system.
 | |
|   @retval EFI_VOLUME_CORRUPTED  - The volume is corrupted.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| FatOpenDevice (
 | |
|   IN OUT FAT_VOLUME           *Volume
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS            Status;
 | |
|   UINT32                BlockSize;
 | |
|   UINT32                DirtyMask;
 | |
|   EFI_DISK_IO_PROTOCOL  *DiskIo;
 | |
|   FAT_BOOT_SECTOR       FatBs;
 | |
|   FAT_VOLUME_TYPE       FatType;
 | |
|   UINTN                 RootDirSectors;
 | |
|   UINTN                 FatLba;
 | |
|   UINTN                 RootLba;
 | |
|   UINTN                 FirstClusterLba;
 | |
|   UINTN                 Sectors;
 | |
|   UINTN                 SectorsPerFat;
 | |
|   UINT8                 SectorsPerClusterAlignment;
 | |
|   UINT8                 BlockAlignment;
 | |
| 
 | |
|   //
 | |
|   // Read the FAT_BOOT_SECTOR BPB info
 | |
|   // This is the only part of FAT code that uses parent DiskIo,
 | |
|   // Others use FatDiskIo which utilizes a Cache.
 | |
|   //
 | |
|   DiskIo  = Volume->DiskIo;
 | |
|   Status  = DiskIo->ReadDisk (DiskIo, Volume->MediaId, 0, sizeof (FatBs), &FatBs);
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((EFI_D_INIT, "FatOpenDevice: read of part_lba failed %r\n", Status));
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   FatType = FatUndefined;
 | |
| 
 | |
|   //
 | |
|   // Use LargeSectors if Sectors is 0
 | |
|   //
 | |
|   Sectors = FatBs.FatBsb.Sectors;
 | |
|   if (Sectors == 0) {
 | |
|     Sectors = FatBs.FatBsb.LargeSectors;
 | |
|   }
 | |
| 
 | |
|   SectorsPerFat = FatBs.FatBsb.SectorsPerFat;
 | |
|   if (SectorsPerFat == 0) {
 | |
|     SectorsPerFat = FatBs.FatBse.Fat32Bse.LargeSectorsPerFat;
 | |
|     FatType       = Fat32;
 | |
|   }
 | |
|   //
 | |
|   // Is boot sector a fat sector?
 | |
|   // (Note that so far we only know if the sector is FAT32 or not, we don't
 | |
|   // know if the sector is Fat16 or Fat12 until later when we can compute
 | |
|   // the volume size)
 | |
|   //
 | |
|   if (FatBs.FatBsb.ReservedSectors == 0 || FatBs.FatBsb.NumFats == 0 || Sectors == 0) {
 | |
|     return EFI_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   if ((FatBs.FatBsb.SectorSize & (FatBs.FatBsb.SectorSize - 1)) != 0) {
 | |
|     return EFI_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   BlockAlignment = (UINT8) HighBitSet32 (FatBs.FatBsb.SectorSize);
 | |
|   if (BlockAlignment > MAX_BLOCK_ALIGNMENT || BlockAlignment < MIN_BLOCK_ALIGNMENT) {
 | |
|     return EFI_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   if ((FatBs.FatBsb.SectorsPerCluster & (FatBs.FatBsb.SectorsPerCluster - 1)) != 0) {
 | |
|     return EFI_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   SectorsPerClusterAlignment = (UINT8) HighBitSet32 (FatBs.FatBsb.SectorsPerCluster);
 | |
|   if (SectorsPerClusterAlignment > MAX_SECTORS_PER_CLUSTER_ALIGNMENT) {
 | |
|     return EFI_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   if (FatBs.FatBsb.Media <= 0xf7 &&
 | |
|       FatBs.FatBsb.Media != 0xf0 &&
 | |
|       FatBs.FatBsb.Media != 0x00 &&
 | |
|       FatBs.FatBsb.Media != 0x01
 | |
|       ) {
 | |
|     return EFI_UNSUPPORTED;
 | |
|   }
 | |
|   //
 | |
|   // Initialize fields the volume information for this FatType
 | |
|   //
 | |
|   if (FatType != Fat32) {
 | |
|     if (FatBs.FatBsb.RootEntries == 0) {
 | |
|       return EFI_UNSUPPORTED;
 | |
|     }
 | |
|     //
 | |
|     // Unpack fat12, fat16 info
 | |
|     //
 | |
|     Volume->RootEntries = FatBs.FatBsb.RootEntries;
 | |
|   } else {
 | |
|     //
 | |
|     // If this is fat32, refuse to mount mirror-disabled volumes
 | |
|     //
 | |
|     if ((SectorsPerFat == 0 || FatBs.FatBse.Fat32Bse.FsVersion != 0) || (FatBs.FatBse.Fat32Bse.ExtendedFlags & 0x80)) {
 | |
|       return EFI_UNSUPPORTED;
 | |
|     }
 | |
|     //
 | |
|     // Unpack fat32 info
 | |
|     //
 | |
|     Volume->RootCluster = FatBs.FatBse.Fat32Bse.RootDirFirstCluster;
 | |
|   }
 | |
| 
 | |
|   Volume->NumFats           = FatBs.FatBsb.NumFats;
 | |
|   //
 | |
|   // Compute some fat locations
 | |
|   //
 | |
|   BlockSize                 = FatBs.FatBsb.SectorSize;
 | |
|   RootDirSectors            = ((Volume->RootEntries * sizeof (FAT_DIRECTORY_ENTRY)) + (BlockSize - 1)) / BlockSize;
 | |
| 
 | |
|   FatLba                    = FatBs.FatBsb.ReservedSectors;
 | |
|   RootLba                   = FatBs.FatBsb.NumFats * SectorsPerFat + FatLba;
 | |
|   FirstClusterLba           = RootLba + RootDirSectors;
 | |
| 
 | |
|   Volume->FatPos            = FatLba * BlockSize;
 | |
|   Volume->FatSize           = SectorsPerFat * BlockSize;
 | |
| 
 | |
|   Volume->VolumeSize        = LShiftU64 (Sectors, BlockAlignment);
 | |
|   Volume->RootPos           = LShiftU64 (RootLba, BlockAlignment);
 | |
|   Volume->FirstClusterPos   = LShiftU64 (FirstClusterLba, BlockAlignment);
 | |
|   Volume->MaxCluster        = (Sectors - FirstClusterLba) >> SectorsPerClusterAlignment;
 | |
|   Volume->ClusterAlignment  = (UINT8)(BlockAlignment + SectorsPerClusterAlignment);
 | |
|   Volume->ClusterSize       = (UINTN)1 << (Volume->ClusterAlignment);
 | |
| 
 | |
|   //
 | |
|   // If this is not a fat32, determine if it's a fat16 or fat12
 | |
|   //
 | |
|   if (FatType != Fat32) {
 | |
|     if (Volume->MaxCluster >= FAT_MAX_FAT16_CLUSTER) {
 | |
|       return EFI_VOLUME_CORRUPTED;
 | |
|     }
 | |
| 
 | |
|     FatType = Volume->MaxCluster < FAT_MAX_FAT12_CLUSTER ? Fat12 : Fat16;
 | |
|     //
 | |
|     // fat12 & fat16 fat-entries are 2 bytes
 | |
|     //
 | |
|     Volume->FatEntrySize = sizeof (UINT16);
 | |
|     DirtyMask            = FAT16_DIRTY_MASK;
 | |
|   } else {
 | |
|     if (Volume->MaxCluster < FAT_MAX_FAT16_CLUSTER) {
 | |
|       return EFI_VOLUME_CORRUPTED;
 | |
|     }
 | |
|     //
 | |
|     // fat32 fat-entries are 4 bytes
 | |
|     //
 | |
|     Volume->FatEntrySize = sizeof (UINT32);
 | |
|     DirtyMask            = FAT32_DIRTY_MASK;
 | |
|   }
 | |
|   //
 | |
|   // Get the DirtyValue and NotDirtyValue
 | |
|   // We should keep the initial value as the NotDirtyValue
 | |
|   // in case the volume is dirty already
 | |
|   //
 | |
|   if (FatType != Fat12) {
 | |
|     Status = FatAccessVolumeDirty (Volume, ReadDisk, &Volume->NotDirtyValue);
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       return Status;
 | |
|     }
 | |
| 
 | |
|     Volume->DirtyValue = Volume->NotDirtyValue & DirtyMask;
 | |
|   }
 | |
|   //
 | |
|   // If present, read the fat hint info
 | |
|   //
 | |
|   if (FatType == Fat32) {
 | |
|     Volume->FreeInfoPos = FatBs.FatBse.Fat32Bse.FsInfoSector * BlockSize;
 | |
|     if (FatBs.FatBse.Fat32Bse.FsInfoSector != 0) {
 | |
|       FatDiskIo (Volume, ReadDisk, Volume->FreeInfoPos, sizeof (FAT_INFO_SECTOR), &Volume->FatInfoSector, NULL);
 | |
|       if (Volume->FatInfoSector.Signature == FAT_INFO_SIGNATURE &&
 | |
|           Volume->FatInfoSector.InfoBeginSignature == FAT_INFO_BEGIN_SIGNATURE &&
 | |
|           Volume->FatInfoSector.InfoEndSignature == FAT_INFO_END_SIGNATURE &&
 | |
|           Volume->FatInfoSector.FreeInfo.ClusterCount <= Volume->MaxCluster
 | |
|           ) {
 | |
|         Volume->FreeInfoValid = TRUE;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   //
 | |
|   // Just make up a FreeInfo.NextCluster for use by allocate cluster
 | |
|   //
 | |
|   if (FAT_MIN_CLUSTER > Volume->FatInfoSector.FreeInfo.NextCluster ||
 | |
|      Volume->FatInfoSector.FreeInfo.NextCluster > Volume->MaxCluster + 1
 | |
|      ) {
 | |
|     Volume->FatInfoSector.FreeInfo.NextCluster = FAT_MIN_CLUSTER;
 | |
|   }
 | |
|   //
 | |
|   // We are now defining FAT Type
 | |
|   //
 | |
|   Volume->FatType = FatType;
 | |
|   ASSERT (FatType != FatUndefined);
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 |