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