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
FatFlushDataCacheRange(IN FAT_VOLUME * Volume,IN IO_MODE IoMode,IN UINTN StartPageNo,IN UINTN EndPageNo,OUT UINT8 * Buffer)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
FatExchangeCachePage(IN FAT_VOLUME * Volume,IN CACHE_DATA_TYPE DataType,IN IO_MODE IoMode,IN CACHE_TAG * CacheTag,IN FAT_TASK * Task)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
FatGetCachePage(IN FAT_VOLUME * Volume,IN CACHE_DATA_TYPE CacheDataType,IN UINTN PageNo,IN CACHE_TAG * CacheTag)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
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)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
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)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
FatVolumeFlushCache(IN FAT_VOLUME * Volume,IN FAT_TASK * Task)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
FatInitializeDiskCache(IN FAT_VOLUME * Volume)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