Home | History | Annotate | Download | only in EnhancedFatDxe
      1 /** @file
      2   Cache implementation for EFI FAT File system driver.
      3 
      4 Copyright (c) 2005 - 2013, Intel Corporation. All rights reserved.<BR>
      5 This program and the accompanying materials are licensed and made available
      6 under the terms and conditions of the BSD License which accompanies this
      7 distribution. The full text of the license may be found at
      8 http://opensource.org/licenses/bsd-license.php
      9 
     10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     12 
     13 **/
     14 
     15 #include "Fat.h"
     16 
     17 /**
     18 
     19   This function is used by the Data Cache.
     20 
     21   When this function is called by write command, all entries in this range
     22   are older than the contents in disk, so they are invalid; just mark them invalid.
     23 
     24   When this function is called by read command, if any entry in this range
     25   is dirty, it means that the relative info directly readed from media is older than
     26   than the info in the cache; So need to update the relative info in the Buffer.
     27 
     28   @param  Volume                - FAT file system volume.
     29   @param  IoMode                - This function is called by read command or write command
     30   @param  StartPageNo           - First PageNo to be checked in the cache.
     31   @param  EndPageNo             - Last PageNo to be checked in the cache.
     32   @param  Buffer                - The user buffer need to update. Only when doing the read command
     33                           and there is dirty cache in the cache range, this parameter will be used.
     34 
     35 **/
     36 STATIC
     37 VOID
     38 FatFlushDataCacheRange (
     39   IN  FAT_VOLUME         *Volume,
     40   IN  IO_MODE            IoMode,
     41   IN  UINTN              StartPageNo,
     42   IN  UINTN              EndPageNo,
     43   OUT UINT8              *Buffer
     44   )
     45 {
     46   UINTN       PageNo;
     47   UINTN       GroupNo;
     48   UINTN       GroupMask;
     49   UINTN       PageSize;
     50   UINT8       PageAlignment;
     51   DISK_CACHE  *DiskCache;
     52   CACHE_TAG   *CacheTag;
     53   UINT8       *BaseAddress;
     54 
     55   DiskCache     = &Volume->DiskCache[CacheData];
     56   BaseAddress   = DiskCache->CacheBase;
     57   GroupMask     = DiskCache->GroupMask;
     58   PageAlignment = DiskCache->PageAlignment;
     59   PageSize      = (UINTN)1 << PageAlignment;
     60 
     61   for (PageNo = StartPageNo; PageNo < EndPageNo; PageNo++) {
     62     GroupNo   = PageNo & GroupMask;
     63     CacheTag  = &DiskCache->CacheTag[GroupNo];
     64     if (CacheTag->RealSize > 0 && CacheTag->PageNo == PageNo) {
     65       //
     66       // When reading data form disk directly, if some dirty data
     67       // in cache is in this rang, this data in the Buffer need to
     68       // be updated with the cache's dirty data.
     69       //
     70       if (IoMode == ReadDisk) {
     71         if (CacheTag->Dirty) {
     72           CopyMem (
     73             Buffer + ((PageNo - StartPageNo) << PageAlignment),
     74             BaseAddress + (GroupNo << PageAlignment),
     75             PageSize
     76             );
     77         }
     78       } else {
     79         //
     80         // Make all valid entries in this range invalid.
     81         //
     82         CacheTag->RealSize = 0;
     83       }
     84     }
     85   }
     86 }
     87 
     88 /**
     89 
     90   Exchange the cache page with the image on the disk
     91 
     92   @param  Volume                - FAT file system volume.
     93   @param  DataType              - Indicate the cache type.
     94   @param  IoMode                - Indicate whether to load this page from disk or store this page to disk.
     95   @param  CacheTag              - The Cache Tag for the current cache page.
     96   @param  Task                    point to task instance.
     97 
     98   @retval EFI_SUCCESS           - Cache page exchanged successfully.
     99   @return Others                - An error occurred when exchanging cache page.
    100 
    101 **/
    102 STATIC
    103 EFI_STATUS
    104 FatExchangeCachePage (
    105   IN FAT_VOLUME         *Volume,
    106   IN CACHE_DATA_TYPE    DataType,
    107   IN IO_MODE            IoMode,
    108   IN CACHE_TAG          *CacheTag,
    109   IN FAT_TASK           *Task
    110   )
    111 {
    112   EFI_STATUS  Status;
    113   UINTN       GroupNo;
    114   UINTN       PageNo;
    115   UINTN       WriteCount;
    116   UINTN       RealSize;
    117   UINT64      EntryPos;
    118   UINT64      MaxSize;
    119   DISK_CACHE  *DiskCache;
    120   VOID        *PageAddress;
    121   UINT8       PageAlignment;
    122 
    123   DiskCache     = &Volume->DiskCache[DataType];
    124   PageNo        = CacheTag->PageNo;
    125   GroupNo       = PageNo & DiskCache->GroupMask;
    126   PageAlignment = DiskCache->PageAlignment;
    127   PageAddress   = DiskCache->CacheBase + (GroupNo << PageAlignment);
    128   EntryPos      = DiskCache->BaseAddress + LShiftU64 (PageNo, PageAlignment);
    129   RealSize      = CacheTag->RealSize;
    130   if (IoMode == ReadDisk) {
    131     RealSize  = (UINTN)1 << PageAlignment;
    132     MaxSize   = DiskCache->LimitAddress - EntryPos;
    133     if (MaxSize < RealSize) {
    134       DEBUG ((EFI_D_INFO, "FatDiskIo: Cache Page OutBound occurred! \n"));
    135       RealSize = (UINTN) MaxSize;
    136     }
    137   }
    138 
    139   WriteCount = 1;
    140   if (DataType == CacheFat && IoMode == WriteDisk) {
    141     WriteCount = Volume->NumFats;
    142   }
    143 
    144   do {
    145     //
    146     // Only fat table writing will execute more than once
    147     //
    148     Status = FatDiskIo (Volume, IoMode, EntryPos, RealSize, PageAddress, Task);
    149     if (EFI_ERROR (Status)) {
    150       return Status;
    151     }
    152 
    153     EntryPos += Volume->FatSize;
    154   } while (--WriteCount > 0);
    155 
    156   CacheTag->Dirty     = FALSE;
    157   CacheTag->RealSize  = RealSize;
    158   return EFI_SUCCESS;
    159 }
    160 
    161 /**
    162 
    163   Get one cache page by specified PageNo.
    164 
    165   @param  Volume                - FAT file system volume.
    166   @param  CacheDataType         - The cache type: CACHE_FAT or CACHE_DATA.
    167   @param  PageNo                - PageNo to match with the cache.
    168   @param  CacheTag              - The Cache Tag for the current cache page.
    169 
    170   @retval EFI_SUCCESS           - Get the cache page successfully.
    171   @return other                 - An error occurred when accessing data.
    172 
    173 **/
    174 STATIC
    175 EFI_STATUS
    176 FatGetCachePage (
    177   IN FAT_VOLUME         *Volume,
    178   IN CACHE_DATA_TYPE    CacheDataType,
    179   IN UINTN              PageNo,
    180   IN CACHE_TAG          *CacheTag
    181   )
    182 {
    183   EFI_STATUS  Status;
    184   UINTN       OldPageNo;
    185 
    186   OldPageNo = CacheTag->PageNo;
    187   if (CacheTag->RealSize > 0 && OldPageNo == PageNo) {
    188     //
    189     // Cache Hit occurred
    190     //
    191     return EFI_SUCCESS;
    192   }
    193 
    194   //
    195   // Write dirty cache page back to disk
    196   //
    197   if (CacheTag->RealSize > 0 && CacheTag->Dirty) {
    198     Status = FatExchangeCachePage (Volume, CacheDataType, WriteDisk, CacheTag, NULL);
    199     if (EFI_ERROR (Status)) {
    200       return Status;
    201     }
    202   }
    203   //
    204   // Load new data from disk;
    205   //
    206   CacheTag->PageNo  = PageNo;
    207   Status            = FatExchangeCachePage (Volume, CacheDataType, ReadDisk, CacheTag, NULL);
    208 
    209   return Status;
    210 }
    211 
    212 /**
    213 
    214   Read Length bytes from the position of Offset into Buffer, or
    215   write Length bytes from Buffer into the position of Offset.
    216 
    217   @param  Volume                - FAT file system volume.
    218   @param  CacheDataType         - The type of cache: CACHE_DATA or CACHE_FAT.
    219   @param  IoMode                - Indicate the type of disk access.
    220   @param  PageNo                - The number of unaligned cache page.
    221   @param  Offset                - The starting byte of cache page.
    222   @param  Length                - The number of bytes that is read or written
    223   @param  Buffer                - Buffer containing cache data.
    224 
    225   @retval EFI_SUCCESS           - The data was accessed correctly.
    226   @return Others                - An error occurred when accessing unaligned cache page.
    227 
    228 **/
    229 STATIC
    230 EFI_STATUS
    231 FatAccessUnalignedCachePage (
    232   IN     FAT_VOLUME        *Volume,
    233   IN     CACHE_DATA_TYPE   CacheDataType,
    234   IN     IO_MODE           IoMode,
    235   IN     UINTN             PageNo,
    236   IN     UINTN             Offset,
    237   IN     UINTN             Length,
    238   IN OUT VOID              *Buffer
    239   )
    240 {
    241   EFI_STATUS  Status;
    242   VOID        *Source;
    243   VOID        *Destination;
    244   DISK_CACHE  *DiskCache;
    245   CACHE_TAG   *CacheTag;
    246   UINTN       GroupNo;
    247 
    248   DiskCache = &Volume->DiskCache[CacheDataType];
    249   GroupNo   = PageNo & DiskCache->GroupMask;
    250   CacheTag  = &DiskCache->CacheTag[GroupNo];
    251   Status    = FatGetCachePage (Volume, CacheDataType, PageNo, CacheTag);
    252   if (!EFI_ERROR (Status)) {
    253     Source      = DiskCache->CacheBase + (GroupNo << DiskCache->PageAlignment) + Offset;
    254     Destination = Buffer;
    255     if (IoMode != ReadDisk) {
    256       CacheTag->Dirty   = TRUE;
    257       DiskCache->Dirty  = TRUE;
    258       Destination       = Source;
    259       Source            = Buffer;
    260     }
    261 
    262     CopyMem (Destination, Source, Length);
    263   }
    264 
    265   return Status;
    266 }
    267 
    268 /**
    269 
    270   Read BufferSize bytes from the position of Offset into Buffer,
    271   or write BufferSize bytes from Buffer into the position of Offset.
    272 
    273   Base on the parameter of CACHE_DATA_TYPE, the data access will be divided into
    274   the access of FAT cache (CACHE_FAT) and the access of Data cache (CACHE_DATA):
    275 
    276   1. Access of FAT cache (CACHE_FAT): Access the data in the FAT cache, if there is cache
    277      page hit, just return the cache page; else update the related cache page and return
    278      the right cache page.
    279   2. Access of Data cache (CACHE_DATA):
    280      The access data will be divided into UnderRun data, Aligned data and OverRun data;
    281      The UnderRun data and OverRun data will be accessed by the Data cache,
    282      but the Aligned data will be accessed with disk directly.
    283 
    284   @param  Volume                - FAT file system volume.
    285   @param  CacheDataType         - The type of cache: CACHE_DATA or CACHE_FAT.
    286   @param  IoMode                - Indicate the type of disk access.
    287   @param  Offset                - The starting byte offset to read from.
    288   @param  BufferSize            - Size of Buffer.
    289   @param  Buffer                - Buffer containing cache data.
    290   @param  Task                    point to task instance.
    291 
    292   @retval EFI_SUCCESS           - The data was accessed correctly.
    293   @retval EFI_MEDIA_CHANGED     - The MediaId does not match the current device.
    294   @return Others                - An error occurred when accessing cache.
    295 
    296 **/
    297 EFI_STATUS
    298 FatAccessCache (
    299   IN     FAT_VOLUME         *Volume,
    300   IN     CACHE_DATA_TYPE    CacheDataType,
    301   IN     IO_MODE            IoMode,
    302   IN     UINT64             Offset,
    303   IN     UINTN              BufferSize,
    304   IN OUT UINT8              *Buffer,
    305   IN     FAT_TASK           *Task
    306   )
    307 {
    308   EFI_STATUS  Status;
    309   UINTN       PageSize;
    310   UINTN       UnderRun;
    311   UINTN       OverRun;
    312   UINTN       AlignedSize;
    313   UINTN       Length;
    314   UINTN       PageNo;
    315   UINTN       AlignedPageCount;
    316   UINTN       OverRunPageNo;
    317   DISK_CACHE  *DiskCache;
    318   UINT64      EntryPos;
    319   UINT8       PageAlignment;
    320 
    321   ASSERT (Volume->CacheBuffer != NULL);
    322 
    323   Status        = EFI_SUCCESS;
    324   DiskCache     = &Volume->DiskCache[CacheDataType];
    325   EntryPos      = Offset - DiskCache->BaseAddress;
    326   PageAlignment = DiskCache->PageAlignment;
    327   PageSize      = (UINTN)1 << PageAlignment;
    328   PageNo        = (UINTN) RShiftU64 (EntryPos, PageAlignment);
    329   UnderRun      = ((UINTN) EntryPos) & (PageSize - 1);
    330 
    331   if (UnderRun > 0) {
    332     Length = PageSize - UnderRun;
    333     if (Length > BufferSize) {
    334       Length = BufferSize;
    335     }
    336 
    337     Status = FatAccessUnalignedCachePage (Volume, CacheDataType, IoMode, PageNo, UnderRun, Length, Buffer);
    338     if (EFI_ERROR (Status)) {
    339       return Status;
    340     }
    341 
    342     Buffer     += Length;
    343     BufferSize -= Length;
    344     PageNo++;
    345   }
    346 
    347   AlignedPageCount  = BufferSize >> PageAlignment;
    348   OverRunPageNo     = PageNo + AlignedPageCount;
    349   //
    350   // The access of the Aligned data
    351   //
    352   if (AlignedPageCount > 0) {
    353     //
    354     // Accessing fat table cannot have alignment data
    355     //
    356     ASSERT (CacheDataType == CacheData);
    357 
    358     EntryPos    = Volume->RootPos + LShiftU64 (PageNo, PageAlignment);
    359     AlignedSize = AlignedPageCount << PageAlignment;
    360     Status      = FatDiskIo (Volume, IoMode, EntryPos, AlignedSize, Buffer, Task);
    361     if (EFI_ERROR (Status)) {
    362       return Status;
    363     }
    364     //
    365     // If these access data over laps the relative cache range, these cache pages need
    366     // to be updated.
    367     //
    368     FatFlushDataCacheRange (Volume, IoMode, PageNo, OverRunPageNo, Buffer);
    369     Buffer      += AlignedSize;
    370     BufferSize  -= AlignedSize;
    371   }
    372   //
    373   // The access of the OverRun data
    374   //
    375   OverRun = BufferSize;
    376   if (OverRun > 0) {
    377     //
    378     // Last read is not a complete page
    379     //
    380     Status = FatAccessUnalignedCachePage (Volume, CacheDataType, IoMode, OverRunPageNo, 0, OverRun, Buffer);
    381   }
    382 
    383   return Status;
    384 }
    385 
    386 /**
    387 
    388   Flush all the dirty cache back, include the FAT cache and the Data cache.
    389 
    390   @param  Volume                - FAT file system volume.
    391   @param  Task                    point to task instance.
    392 
    393   @retval EFI_SUCCESS           - Flush all the dirty cache back successfully
    394   @return other                 - An error occurred when writing the data into the disk
    395 
    396 **/
    397 EFI_STATUS
    398 FatVolumeFlushCache (
    399   IN FAT_VOLUME         *Volume,
    400   IN FAT_TASK           *Task
    401   )
    402 {
    403   EFI_STATUS      Status;
    404   CACHE_DATA_TYPE CacheDataType;
    405   UINTN           GroupIndex;
    406   UINTN           GroupMask;
    407   DISK_CACHE      *DiskCache;
    408   CACHE_TAG       *CacheTag;
    409 
    410   for (CacheDataType = (CACHE_DATA_TYPE) 0; CacheDataType < CacheMaxType; CacheDataType++) {
    411     DiskCache = &Volume->DiskCache[CacheDataType];
    412     if (DiskCache->Dirty) {
    413       //
    414       // Data cache or fat cache is dirty, write the dirty data back
    415       //
    416       GroupMask = DiskCache->GroupMask;
    417       for (GroupIndex = 0; GroupIndex <= GroupMask; GroupIndex++) {
    418         CacheTag = &DiskCache->CacheTag[GroupIndex];
    419         if (CacheTag->RealSize > 0 && CacheTag->Dirty) {
    420           //
    421           // Write back all Dirty Data Cache Page to disk
    422           //
    423           Status = FatExchangeCachePage (Volume, CacheDataType, WriteDisk, CacheTag, Task);
    424           if (EFI_ERROR (Status)) {
    425             return Status;
    426           }
    427         }
    428       }
    429 
    430       DiskCache->Dirty = FALSE;
    431     }
    432   }
    433   //
    434   // Flush the block device.
    435   //
    436   Status = Volume->BlockIo->FlushBlocks (Volume->BlockIo);
    437   return Status;
    438 }
    439 
    440 /**
    441 
    442   Initialize the disk cache according to Volume's FatType.
    443 
    444   @param  Volume                - FAT file system volume.
    445 
    446   @retval EFI_SUCCESS           - The disk cache is successfully initialized.
    447   @retval EFI_OUT_OF_RESOURCES  - Not enough memory to allocate disk cache.
    448 
    449 **/
    450 EFI_STATUS
    451 FatInitializeDiskCache (
    452   IN FAT_VOLUME         *Volume
    453   )
    454 {
    455   DISK_CACHE  *DiskCache;
    456   UINTN       FatCacheGroupCount;
    457   UINTN       DataCacheSize;
    458   UINTN       FatCacheSize;
    459   UINT8       *CacheBuffer;
    460 
    461   DiskCache = Volume->DiskCache;
    462   //
    463   // Configure the parameters of disk cache
    464   //
    465   if (Volume->FatType == Fat12) {
    466     FatCacheGroupCount                  = FAT_FATCACHE_GROUP_MIN_COUNT;
    467     DiskCache[CacheFat].PageAlignment  = FAT_FATCACHE_PAGE_MIN_ALIGNMENT;
    468     DiskCache[CacheData].PageAlignment = FAT_DATACACHE_PAGE_MIN_ALIGNMENT;
    469   } else {
    470     FatCacheGroupCount                  = FAT_FATCACHE_GROUP_MAX_COUNT;
    471     DiskCache[CacheFat].PageAlignment  = FAT_FATCACHE_PAGE_MAX_ALIGNMENT;
    472     DiskCache[CacheData].PageAlignment = FAT_DATACACHE_PAGE_MAX_ALIGNMENT;
    473   }
    474 
    475   DiskCache[CacheData].GroupMask     = FAT_DATACACHE_GROUP_COUNT - 1;
    476   DiskCache[CacheData].BaseAddress   = Volume->RootPos;
    477   DiskCache[CacheData].LimitAddress  = Volume->VolumeSize;
    478   DiskCache[CacheFat].GroupMask      = FatCacheGroupCount - 1;
    479   DiskCache[CacheFat].BaseAddress    = Volume->FatPos;
    480   DiskCache[CacheFat].LimitAddress   = Volume->FatPos + Volume->FatSize;
    481   FatCacheSize                        = FatCacheGroupCount << DiskCache[CacheFat].PageAlignment;
    482   DataCacheSize                       = FAT_DATACACHE_GROUP_COUNT << DiskCache[CacheData].PageAlignment;
    483   //
    484   // Allocate the Fat Cache buffer
    485   //
    486   CacheBuffer = AllocateZeroPool (FatCacheSize + DataCacheSize);
    487   if (CacheBuffer == NULL) {
    488     return EFI_OUT_OF_RESOURCES;
    489   }
    490 
    491   Volume->CacheBuffer             = CacheBuffer;
    492   DiskCache[CacheFat].CacheBase  = CacheBuffer;
    493   DiskCache[CacheData].CacheBase = CacheBuffer + FatCacheSize;
    494   return EFI_SUCCESS;
    495 }
    496