/** @file Initialization routines. Copyright (c) 2005 - 2013, Intel Corporation. All rights reserved.
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; }