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