| /** @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 ((DEBUG_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 ((DEBUG_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; | |
| } |