1/** @file
2  Cache implementation for EFI FAT File system driver.
3
4Copyright (c) 2005 - 2013, Intel Corporation. All rights reserved.<BR>
5This program and the accompanying materials are licensed and made available
6under the terms and conditions of the BSD License which accompanies this
7distribution. The full text of the license may be found at
8http://opensource.org/licenses/bsd-license.php
9
10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11WITHOUT 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**/
36STATIC
37VOID
38FatFlushDataCacheRange (
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**/
102STATIC
103EFI_STATUS
104FatExchangeCachePage (
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**/
174STATIC
175EFI_STATUS
176FatGetCachePage (
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**/
229STATIC
230EFI_STATUS
231FatAccessUnalignedCachePage (
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**/
297EFI_STATUS
298FatAccessCache (
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**/
397EFI_STATUS
398FatVolumeFlushCache (
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**/
450EFI_STATUS
451FatInitializeDiskCache (
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