| /** @file | |
| Cache implementation for EFI FAT File system driver. | |
| Copyright (c) 2005 - 2013, Intel Corporation. All rights reserved.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include "Fat.h" | |
| /** | |
| This function is used by the Data Cache. | |
| When this function is called by write command, all entries in this range | |
| are older than the contents in disk, so they are invalid; just mark them invalid. | |
| When this function is called by read command, if any entry in this range | |
| is dirty, it means that the relative info directly read from media is older than | |
| than the info in the cache; So need to update the relative info in the Buffer. | |
| @param Volume - FAT file system volume. | |
| @param IoMode - This function is called by read command or write command | |
| @param StartPageNo - First PageNo to be checked in the cache. | |
| @param EndPageNo - Last PageNo to be checked in the cache. | |
| @param Buffer - The user buffer need to update. Only when doing the read command | |
| and there is dirty cache in the cache range, this parameter will be used. | |
| **/ | |
| STATIC | |
| VOID | |
| FatFlushDataCacheRange ( | |
| IN FAT_VOLUME *Volume, | |
| IN IO_MODE IoMode, | |
| IN UINTN StartPageNo, | |
| IN UINTN EndPageNo, | |
| OUT UINT8 *Buffer | |
| ) | |
| { | |
| UINTN PageNo; | |
| UINTN GroupNo; | |
| UINTN GroupMask; | |
| UINTN PageSize; | |
| UINT8 PageAlignment; | |
| DISK_CACHE *DiskCache; | |
| CACHE_TAG *CacheTag; | |
| UINT8 *BaseAddress; | |
| DiskCache = &Volume->DiskCache[CacheData]; | |
| BaseAddress = DiskCache->CacheBase; | |
| GroupMask = DiskCache->GroupMask; | |
| PageAlignment = DiskCache->PageAlignment; | |
| PageSize = (UINTN)1 << PageAlignment; | |
| for (PageNo = StartPageNo; PageNo < EndPageNo; PageNo++) { | |
| GroupNo = PageNo & GroupMask; | |
| CacheTag = &DiskCache->CacheTag[GroupNo]; | |
| if ((CacheTag->RealSize > 0) && (CacheTag->PageNo == PageNo)) { | |
| // | |
| // When reading data form disk directly, if some dirty data | |
| // in cache is in this rang, this data in the Buffer need to | |
| // be updated with the cache's dirty data. | |
| // | |
| if (IoMode == ReadDisk) { | |
| if (CacheTag->Dirty) { | |
| CopyMem ( | |
| Buffer + ((PageNo - StartPageNo) << PageAlignment), | |
| BaseAddress + (GroupNo << PageAlignment), | |
| PageSize | |
| ); | |
| } | |
| } else { | |
| // | |
| // Make all valid entries in this range invalid. | |
| // | |
| CacheTag->RealSize = 0; | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| Exchange the cache page with the image on the disk | |
| @param Volume - FAT file system volume. | |
| @param DataType - Indicate the cache type. | |
| @param IoMode - Indicate whether to load this page from disk or store this page to disk. | |
| @param CacheTag - The Cache Tag for the current cache page. | |
| @param Task point to task instance. | |
| @retval EFI_SUCCESS - Cache page exchanged successfully. | |
| @return Others - An error occurred when exchanging cache page. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| FatExchangeCachePage ( | |
| IN FAT_VOLUME *Volume, | |
| IN CACHE_DATA_TYPE DataType, | |
| IN IO_MODE IoMode, | |
| IN CACHE_TAG *CacheTag, | |
| IN FAT_TASK *Task | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINTN GroupNo; | |
| UINTN PageNo; | |
| UINTN WriteCount; | |
| UINTN RealSize; | |
| UINT64 EntryPos; | |
| UINT64 MaxSize; | |
| DISK_CACHE *DiskCache; | |
| VOID *PageAddress; | |
| UINT8 PageAlignment; | |
| DiskCache = &Volume->DiskCache[DataType]; | |
| PageNo = CacheTag->PageNo; | |
| GroupNo = PageNo & DiskCache->GroupMask; | |
| PageAlignment = DiskCache->PageAlignment; | |
| PageAddress = DiskCache->CacheBase + (GroupNo << PageAlignment); | |
| EntryPos = DiskCache->BaseAddress + LShiftU64 (PageNo, PageAlignment); | |
| RealSize = CacheTag->RealSize; | |
| if (IoMode == ReadDisk) { | |
| RealSize = (UINTN)1 << PageAlignment; | |
| MaxSize = DiskCache->LimitAddress - EntryPos; | |
| if (MaxSize < RealSize) { | |
| DEBUG ((DEBUG_INFO, "FatDiskIo: Cache Page OutBound occurred! \n")); | |
| RealSize = (UINTN)MaxSize; | |
| } | |
| } | |
| WriteCount = 1; | |
| if ((DataType == CacheFat) && (IoMode == WriteDisk)) { | |
| WriteCount = Volume->NumFats; | |
| } | |
| do { | |
| // | |
| // Only fat table writing will execute more than once | |
| // | |
| Status = FatDiskIo (Volume, IoMode, EntryPos, RealSize, PageAddress, Task); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| EntryPos += Volume->FatSize; | |
| } while (--WriteCount > 0); | |
| CacheTag->Dirty = FALSE; | |
| CacheTag->RealSize = RealSize; | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Get one cache page by specified PageNo. | |
| @param Volume - FAT file system volume. | |
| @param CacheDataType - The cache type: CACHE_FAT or CACHE_DATA. | |
| @param PageNo - PageNo to match with the cache. | |
| @param CacheTag - The Cache Tag for the current cache page. | |
| @retval EFI_SUCCESS - Get the cache page successfully. | |
| @return other - An error occurred when accessing data. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| FatGetCachePage ( | |
| IN FAT_VOLUME *Volume, | |
| IN CACHE_DATA_TYPE CacheDataType, | |
| IN UINTN PageNo, | |
| IN CACHE_TAG *CacheTag | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINTN OldPageNo; | |
| OldPageNo = CacheTag->PageNo; | |
| if ((CacheTag->RealSize > 0) && (OldPageNo == PageNo)) { | |
| // | |
| // Cache Hit occurred | |
| // | |
| return EFI_SUCCESS; | |
| } | |
| // | |
| // Write dirty cache page back to disk | |
| // | |
| if ((CacheTag->RealSize > 0) && CacheTag->Dirty) { | |
| Status = FatExchangeCachePage (Volume, CacheDataType, WriteDisk, CacheTag, NULL); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| } | |
| // | |
| // Load new data from disk; | |
| // | |
| CacheTag->PageNo = PageNo; | |
| Status = FatExchangeCachePage (Volume, CacheDataType, ReadDisk, CacheTag, NULL); | |
| return Status; | |
| } | |
| /** | |
| Read Length bytes from the position of Offset into Buffer, or | |
| write Length bytes from Buffer into the position of Offset. | |
| @param Volume - FAT file system volume. | |
| @param CacheDataType - The type of cache: CACHE_DATA or CACHE_FAT. | |
| @param IoMode - Indicate the type of disk access. | |
| @param PageNo - The number of unaligned cache page. | |
| @param Offset - The starting byte of cache page. | |
| @param Length - The number of bytes that is read or written | |
| @param Buffer - Buffer containing cache data. | |
| @retval EFI_SUCCESS - The data was accessed correctly. | |
| @return Others - An error occurred when accessing unaligned cache page. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| FatAccessUnalignedCachePage ( | |
| IN FAT_VOLUME *Volume, | |
| IN CACHE_DATA_TYPE CacheDataType, | |
| IN IO_MODE IoMode, | |
| IN UINTN PageNo, | |
| IN UINTN Offset, | |
| IN UINTN Length, | |
| IN OUT VOID *Buffer | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| VOID *Source; | |
| VOID *Destination; | |
| DISK_CACHE *DiskCache; | |
| CACHE_TAG *CacheTag; | |
| UINTN GroupNo; | |
| DiskCache = &Volume->DiskCache[CacheDataType]; | |
| GroupNo = PageNo & DiskCache->GroupMask; | |
| CacheTag = &DiskCache->CacheTag[GroupNo]; | |
| Status = FatGetCachePage (Volume, CacheDataType, PageNo, CacheTag); | |
| if (!EFI_ERROR (Status)) { | |
| Source = DiskCache->CacheBase + (GroupNo << DiskCache->PageAlignment) + Offset; | |
| Destination = Buffer; | |
| if (IoMode != ReadDisk) { | |
| CacheTag->Dirty = TRUE; | |
| DiskCache->Dirty = TRUE; | |
| Destination = Source; | |
| Source = Buffer; | |
| } | |
| CopyMem (Destination, Source, Length); | |
| } | |
| return Status; | |
| } | |
| /** | |
| Read BufferSize bytes from the position of Offset into Buffer, | |
| or write BufferSize bytes from Buffer into the position of Offset. | |
| Base on the parameter of CACHE_DATA_TYPE, the data access will be divided into | |
| the access of FAT cache (CACHE_FAT) and the access of Data cache (CACHE_DATA): | |
| 1. Access of FAT cache (CACHE_FAT): Access the data in the FAT cache, if there is cache | |
| page hit, just return the cache page; else update the related cache page and return | |
| the right cache page. | |
| 2. Access of Data cache (CACHE_DATA): | |
| The access data will be divided into UnderRun data, Aligned data and OverRun data; | |
| The UnderRun data and OverRun data will be accessed by the Data cache, | |
| but the Aligned data will be accessed with disk directly. | |
| @param Volume - FAT file system volume. | |
| @param CacheDataType - The type of cache: CACHE_DATA or CACHE_FAT. | |
| @param IoMode - Indicate the type of disk access. | |
| @param Offset - The starting byte offset to read from. | |
| @param BufferSize - Size of Buffer. | |
| @param Buffer - Buffer containing cache data. | |
| @param Task point to task instance. | |
| @retval EFI_SUCCESS - The data was accessed correctly. | |
| @retval EFI_MEDIA_CHANGED - The MediaId does not match the current device. | |
| @return Others - An error occurred when accessing cache. | |
| **/ | |
| EFI_STATUS | |
| FatAccessCache ( | |
| IN FAT_VOLUME *Volume, | |
| IN CACHE_DATA_TYPE CacheDataType, | |
| IN IO_MODE IoMode, | |
| IN UINT64 Offset, | |
| IN UINTN BufferSize, | |
| IN OUT UINT8 *Buffer, | |
| IN FAT_TASK *Task | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINTN PageSize; | |
| UINTN UnderRun; | |
| UINTN OverRun; | |
| UINTN AlignedSize; | |
| UINTN Length; | |
| UINTN PageNo; | |
| UINTN AlignedPageCount; | |
| UINTN OverRunPageNo; | |
| DISK_CACHE *DiskCache; | |
| UINT64 EntryPos; | |
| UINT8 PageAlignment; | |
| ASSERT (Volume->CacheBuffer != NULL); | |
| Status = EFI_SUCCESS; | |
| DiskCache = &Volume->DiskCache[CacheDataType]; | |
| EntryPos = Offset - DiskCache->BaseAddress; | |
| PageAlignment = DiskCache->PageAlignment; | |
| PageSize = (UINTN)1 << PageAlignment; | |
| PageNo = (UINTN)RShiftU64 (EntryPos, PageAlignment); | |
| UnderRun = ((UINTN)EntryPos) & (PageSize - 1); | |
| if (UnderRun > 0) { | |
| Length = PageSize - UnderRun; | |
| if (Length > BufferSize) { | |
| Length = BufferSize; | |
| } | |
| Status = FatAccessUnalignedCachePage (Volume, CacheDataType, IoMode, PageNo, UnderRun, Length, Buffer); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| Buffer += Length; | |
| BufferSize -= Length; | |
| PageNo++; | |
| } | |
| AlignedPageCount = BufferSize >> PageAlignment; | |
| OverRunPageNo = PageNo + AlignedPageCount; | |
| // | |
| // The access of the Aligned data | |
| // | |
| if (AlignedPageCount > 0) { | |
| // | |
| // Accessing fat table cannot have alignment data | |
| // | |
| ASSERT (CacheDataType == CacheData); | |
| EntryPos = Volume->RootPos + LShiftU64 (PageNo, PageAlignment); | |
| AlignedSize = AlignedPageCount << PageAlignment; | |
| Status = FatDiskIo (Volume, IoMode, EntryPos, AlignedSize, Buffer, Task); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| // | |
| // If these access data over laps the relative cache range, these cache pages need | |
| // to be updated. | |
| // | |
| FatFlushDataCacheRange (Volume, IoMode, PageNo, OverRunPageNo, Buffer); | |
| Buffer += AlignedSize; | |
| BufferSize -= AlignedSize; | |
| } | |
| // | |
| // The access of the OverRun data | |
| // | |
| OverRun = BufferSize; | |
| if (OverRun > 0) { | |
| // | |
| // Last read is not a complete page | |
| // | |
| Status = FatAccessUnalignedCachePage (Volume, CacheDataType, IoMode, OverRunPageNo, 0, OverRun, Buffer); | |
| } | |
| return Status; | |
| } | |
| /** | |
| Flush all the dirty cache back, include the FAT cache and the Data cache. | |
| @param Volume - FAT file system volume. | |
| @param Task point to task instance. | |
| @retval EFI_SUCCESS - Flush all the dirty cache back successfully | |
| @return other - An error occurred when writing the data into the disk | |
| **/ | |
| EFI_STATUS | |
| FatVolumeFlushCache ( | |
| IN FAT_VOLUME *Volume, | |
| IN FAT_TASK *Task | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| CACHE_DATA_TYPE CacheDataType; | |
| UINTN GroupIndex; | |
| UINTN GroupMask; | |
| DISK_CACHE *DiskCache; | |
| CACHE_TAG *CacheTag; | |
| for (CacheDataType = (CACHE_DATA_TYPE)0; CacheDataType < CacheMaxType; CacheDataType++) { | |
| DiskCache = &Volume->DiskCache[CacheDataType]; | |
| if (DiskCache->Dirty) { | |
| // | |
| // Data cache or fat cache is dirty, write the dirty data back | |
| // | |
| GroupMask = DiskCache->GroupMask; | |
| for (GroupIndex = 0; GroupIndex <= GroupMask; GroupIndex++) { | |
| CacheTag = &DiskCache->CacheTag[GroupIndex]; | |
| if ((CacheTag->RealSize > 0) && CacheTag->Dirty) { | |
| // | |
| // Write back all Dirty Data Cache Page to disk | |
| // | |
| Status = FatExchangeCachePage (Volume, CacheDataType, WriteDisk, CacheTag, Task); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| } | |
| } | |
| DiskCache->Dirty = FALSE; | |
| } | |
| } | |
| // | |
| // Flush the block device. | |
| // | |
| Status = Volume->BlockIo->FlushBlocks (Volume->BlockIo); | |
| return Status; | |
| } | |
| /** | |
| Initialize the disk cache according to Volume's FatType. | |
| @param Volume - FAT file system volume. | |
| @retval EFI_SUCCESS - The disk cache is successfully initialized. | |
| @retval EFI_OUT_OF_RESOURCES - Not enough memory to allocate disk cache. | |
| **/ | |
| EFI_STATUS | |
| FatInitializeDiskCache ( | |
| IN FAT_VOLUME *Volume | |
| ) | |
| { | |
| DISK_CACHE *DiskCache; | |
| UINTN FatCacheGroupCount; | |
| UINTN DataCacheSize; | |
| UINTN FatCacheSize; | |
| UINT8 *CacheBuffer; | |
| DiskCache = Volume->DiskCache; | |
| // | |
| // Configure the parameters of disk cache | |
| // | |
| if (Volume->FatType == Fat12) { | |
| FatCacheGroupCount = FAT_FATCACHE_GROUP_MIN_COUNT; | |
| DiskCache[CacheFat].PageAlignment = FAT_FATCACHE_PAGE_MIN_ALIGNMENT; | |
| DiskCache[CacheData].PageAlignment = FAT_DATACACHE_PAGE_MIN_ALIGNMENT; | |
| } else { | |
| FatCacheGroupCount = FAT_FATCACHE_GROUP_MAX_COUNT; | |
| DiskCache[CacheFat].PageAlignment = FAT_FATCACHE_PAGE_MAX_ALIGNMENT; | |
| DiskCache[CacheData].PageAlignment = FAT_DATACACHE_PAGE_MAX_ALIGNMENT; | |
| } | |
| DiskCache[CacheData].GroupMask = FAT_DATACACHE_GROUP_COUNT - 1; | |
| DiskCache[CacheData].BaseAddress = Volume->RootPos; | |
| DiskCache[CacheData].LimitAddress = Volume->VolumeSize; | |
| DiskCache[CacheFat].GroupMask = FatCacheGroupCount - 1; | |
| DiskCache[CacheFat].BaseAddress = Volume->FatPos; | |
| DiskCache[CacheFat].LimitAddress = Volume->FatPos + Volume->FatSize; | |
| FatCacheSize = FatCacheGroupCount << DiskCache[CacheFat].PageAlignment; | |
| DataCacheSize = FAT_DATACACHE_GROUP_COUNT << DiskCache[CacheData].PageAlignment; | |
| // | |
| // Allocate the Fat Cache buffer | |
| // | |
| CacheBuffer = AllocateZeroPool (FatCacheSize + DataCacheSize); | |
| if (CacheBuffer == NULL) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| Volume->CacheBuffer = CacheBuffer; | |
| DiskCache[CacheFat].CacheBase = CacheBuffer; | |
| DiskCache[CacheData].CacheBase = CacheBuffer + FatCacheSize; | |
| return EFI_SUCCESS; | |
| } |