* Sync to trunk HEAD (r53318).
[reactos.git] / boot / freeldr / freeldr / cache / cache.c
1 /*
2 * FreeLoader
3 * Copyright (C) 1998-2003 Brian Palmer <brianp@sginet.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include <freeldr.h>
21 #include <debug.h>
22
23 ///////////////////////////////////////////////////////////////////////////////////////
24 //
25 // Internal data
26 //
27 ///////////////////////////////////////////////////////////////////////////////////////
28 CACHE_DRIVE CacheManagerDrive;
29 BOOLEAN CacheManagerInitialized = FALSE;
30 BOOLEAN CacheManagerDataInvalid = FALSE;
31 ULONG CacheBlockCount = 0;
32 ULONG CacheSizeLimit = 0;
33 ULONG CacheSizeCurrent = 0;
34
35 BOOLEAN CacheInitializeDrive(UCHAR DriveNumber)
36 {
37 PCACHE_BLOCK NextCacheBlock;
38 GEOMETRY DriveGeometry;
39
40 // If we already have a cache for this drive then
41 // by all means lets keep it, unless it is a removable
42 // drive, in which case we'll invalidate the cache
43 if ((CacheManagerInitialized == TRUE) &&
44 (DriveNumber == CacheManagerDrive.DriveNumber) &&
45 (DriveNumber >= 0x80) &&
46 (CacheManagerDataInvalid != TRUE))
47 {
48 return TRUE;
49 }
50
51 CacheManagerDataInvalid = FALSE;
52
53 //
54 // If we have already been initialized then free
55 // the old data
56 //
57 if (CacheManagerInitialized)
58 {
59 CacheManagerInitialized = FALSE;
60
61 DPRINTM(DPRINT_CACHE, "CacheBlockCount: %d\n", CacheBlockCount);
62 DPRINTM(DPRINT_CACHE, "CacheSizeLimit: %d\n", CacheSizeLimit);
63 DPRINTM(DPRINT_CACHE, "CacheSizeCurrent: %d\n", CacheSizeCurrent);
64 //
65 // Loop through and free the cache blocks
66 //
67 while (!IsListEmpty(&CacheManagerDrive.CacheBlockHead))
68 {
69 NextCacheBlock = CONTAINING_RECORD(RemoveHeadList(&CacheManagerDrive.CacheBlockHead),
70 CACHE_BLOCK,
71 ListEntry);
72
73 MmHeapFree(NextCacheBlock->BlockData);
74 MmHeapFree(NextCacheBlock);
75 }
76 }
77
78 // Initialize the structure
79 RtlZeroMemory(&CacheManagerDrive, sizeof(CACHE_DRIVE));
80 InitializeListHead(&CacheManagerDrive.CacheBlockHead);
81 CacheManagerDrive.DriveNumber = DriveNumber;
82 if (!MachDiskGetDriveGeometry(DriveNumber, &DriveGeometry))
83 {
84 return FALSE;
85 }
86 CacheManagerDrive.BytesPerSector = DriveGeometry.BytesPerSector;
87
88 // Get the number of sectors in each cache block
89 CacheManagerDrive.BlockSize = MachDiskGetCacheableBlockCount(DriveNumber);
90
91 CacheBlockCount = 0;
92 CacheSizeLimit = TotalPagesInLookupTable / 8 * MM_PAGE_SIZE;
93 CacheSizeCurrent = 0;
94 if (CacheSizeLimit < (64 * 1024))
95 {
96 CacheSizeLimit = (64 * 1024);
97 }
98
99 CacheManagerInitialized = TRUE;
100
101 DPRINTM(DPRINT_CACHE, "Initializing BIOS drive 0x%x.\n", DriveNumber);
102 DPRINTM(DPRINT_CACHE, "BytesPerSector: %d.\n", CacheManagerDrive.BytesPerSector);
103 DPRINTM(DPRINT_CACHE, "BlockSize: %d.\n", CacheManagerDrive.BlockSize);
104 DPRINTM(DPRINT_CACHE, "CacheSizeLimit: %d.\n", CacheSizeLimit);
105
106 return TRUE;
107 }
108
109 VOID CacheInvalidateCacheData(VOID)
110 {
111 CacheManagerDataInvalid = TRUE;
112 }
113
114 BOOLEAN CacheReadDiskSectors(UCHAR DiskNumber, ULONGLONG StartSector, ULONG SectorCount, PVOID Buffer)
115 {
116 PCACHE_BLOCK CacheBlock;
117 ULONG StartBlock;
118 ULONG SectorOffsetInStartBlock;
119 ULONG CopyLengthInStartBlock;
120 ULONG EndBlock;
121 ULONG SectorOffsetInEndBlock;
122 ULONG BlockCount;
123 ULONG Idx;
124
125 DPRINTM(DPRINT_CACHE, "CacheReadDiskSectors() DiskNumber: 0x%x StartSector: %I64d SectorCount: %d Buffer: 0x%x\n", DiskNumber, StartSector, SectorCount, Buffer);
126
127 // If we aren't initialized yet then they can't do this
128 if (CacheManagerInitialized == FALSE)
129 {
130 return FALSE;
131 }
132
133 //
134 // Caculate which blocks we must cache
135 //
136 StartBlock = (ULONG)(StartSector / CacheManagerDrive.BlockSize);
137 SectorOffsetInStartBlock = (ULONG)(StartSector % CacheManagerDrive.BlockSize);
138 CopyLengthInStartBlock = (ULONG)((SectorCount > (CacheManagerDrive.BlockSize - SectorOffsetInStartBlock)) ? (CacheManagerDrive.BlockSize - SectorOffsetInStartBlock) : SectorCount);
139 EndBlock = (ULONG)((StartSector + (SectorCount - 1)) / CacheManagerDrive.BlockSize);
140 SectorOffsetInEndBlock = (ULONG)(1 + (StartSector + (SectorCount - 1)) % CacheManagerDrive.BlockSize);
141 BlockCount = (EndBlock - StartBlock) + 1;
142 DPRINTM(DPRINT_CACHE, "StartBlock: %d SectorOffsetInStartBlock: %d CopyLengthInStartBlock: %d EndBlock: %d SectorOffsetInEndBlock: %d BlockCount: %d\n", StartBlock, SectorOffsetInStartBlock, CopyLengthInStartBlock, EndBlock, SectorOffsetInEndBlock, BlockCount);
143
144 //
145 // Read the first block into the buffer
146 //
147 if (BlockCount > 0)
148 {
149 //
150 // Get cache block pointer (this forces the disk sectors into the cache memory)
151 //
152 CacheBlock = CacheInternalGetBlockPointer(&CacheManagerDrive, StartBlock);
153 if (CacheBlock == NULL)
154 {
155 return FALSE;
156 }
157
158 //
159 // Copy the portion requested into the buffer
160 //
161 RtlCopyMemory(Buffer,
162 (PVOID)((ULONG_PTR)CacheBlock->BlockData + (SectorOffsetInStartBlock * CacheManagerDrive.BytesPerSector)),
163 (CopyLengthInStartBlock * CacheManagerDrive.BytesPerSector));
164 DPRINTM(DPRINT_CACHE, "1 - RtlCopyMemory(0x%x, 0x%x, %d)\n", Buffer, ((ULONG_PTR)CacheBlock->BlockData + (SectorOffsetInStartBlock * CacheManagerDrive.BytesPerSector)), (CopyLengthInStartBlock * CacheManagerDrive.BytesPerSector));
165
166 //
167 // Update the buffer address
168 //
169 Buffer = (PVOID)((ULONG_PTR)Buffer + (CopyLengthInStartBlock * CacheManagerDrive.BytesPerSector));
170
171 //
172 // Update the block count
173 //
174 BlockCount--;
175 }
176
177 //
178 // Loop through the middle blocks and read them into the buffer
179 //
180 for (Idx=StartBlock+1; BlockCount>1; Idx++)
181 {
182 //
183 // Get cache block pointer (this forces the disk sectors into the cache memory)
184 //
185 CacheBlock = CacheInternalGetBlockPointer(&CacheManagerDrive, Idx);
186 if (CacheBlock == NULL)
187 {
188 return FALSE;
189 }
190
191 //
192 // Copy the portion requested into the buffer
193 //
194 RtlCopyMemory(Buffer,
195 CacheBlock->BlockData,
196 CacheManagerDrive.BlockSize * CacheManagerDrive.BytesPerSector);
197 DPRINTM(DPRINT_CACHE, "2 - RtlCopyMemory(0x%x, 0x%x, %d)\n", Buffer, CacheBlock->BlockData, CacheManagerDrive.BlockSize * CacheManagerDrive.BytesPerSector);
198
199 //
200 // Update the buffer address
201 //
202 Buffer = (PVOID)((ULONG_PTR)Buffer + (CacheManagerDrive.BlockSize * CacheManagerDrive.BytesPerSector));
203
204 //
205 // Update the block count
206 //
207 BlockCount--;
208 }
209
210 //
211 // Read the last block into the buffer
212 //
213 if (BlockCount > 0)
214 {
215 //
216 // Get cache block pointer (this forces the disk sectors into the cache memory)
217 //
218 CacheBlock = CacheInternalGetBlockPointer(&CacheManagerDrive, EndBlock);
219 if (CacheBlock == NULL)
220 {
221 return FALSE;
222 }
223
224 //
225 // Copy the portion requested into the buffer
226 //
227 RtlCopyMemory(Buffer,
228 CacheBlock->BlockData,
229 SectorOffsetInEndBlock * CacheManagerDrive.BytesPerSector);
230 DPRINTM(DPRINT_CACHE, "3 - RtlCopyMemory(0x%x, 0x%x, %d)\n", Buffer, CacheBlock->BlockData, SectorOffsetInEndBlock * CacheManagerDrive.BytesPerSector);
231
232 //
233 // Update the buffer address
234 //
235 Buffer = (PVOID)((ULONG_PTR)Buffer + (SectorOffsetInEndBlock * CacheManagerDrive.BytesPerSector));
236
237 //
238 // Update the block count
239 //
240 BlockCount--;
241 }
242
243 return TRUE;
244 }
245
246 #if 0
247 BOOLEAN CacheForceDiskSectorsIntoCache(UCHAR DiskNumber, ULONGLONG StartSector, ULONG SectorCount)
248 {
249 PCACHE_BLOCK CacheBlock;
250 ULONG StartBlock;
251 ULONG EndBlock;
252 ULONG BlockCount;
253 ULONG Idx;
254
255 DPRINTM(DPRINT_CACHE, "CacheForceDiskSectorsIntoCache() DiskNumber: 0x%x StartSector: %d SectorCount: %d\n", DiskNumber, StartSector, SectorCount);
256
257 // If we aren't initialized yet then they can't do this
258 if (CacheManagerInitialized == FALSE)
259 {
260 return FALSE;
261 }
262
263 //
264 // Caculate which blocks we must cache
265 //
266 StartBlock = StartSector / CacheManagerDrive.BlockSize;
267 EndBlock = (StartSector + SectorCount) / CacheManagerDrive.BlockSize;
268 BlockCount = (EndBlock - StartBlock) + 1;
269
270 //
271 // Loop through and cache them
272 //
273 for (Idx=StartBlock; Idx<(StartBlock+BlockCount); Idx++)
274 {
275 //
276 // Get cache block pointer (this forces the disk sectors into the cache memory)
277 //
278 CacheBlock = CacheInternalGetBlockPointer(&CacheManagerDrive, Idx);
279 if (CacheBlock == NULL)
280 {
281 return FALSE;
282 }
283
284 //
285 // Lock the sectors into the cache
286 //
287 CacheBlock->LockedInCache = TRUE;
288 }
289
290 return TRUE;
291 }
292 #endif
293
294 BOOLEAN CacheReleaseMemory(ULONG MinimumAmountToRelease)
295 {
296 ULONG AmountReleased;
297
298 DPRINTM(DPRINT_CACHE, "CacheReleaseMemory() MinimumAmountToRelease = %d\n", MinimumAmountToRelease);
299
300 // If we aren't initialized yet then they can't do this
301 if (CacheManagerInitialized == FALSE)
302 {
303 return FALSE;
304 }
305
306 // Loop through and try to free the requested amount of memory
307 for (AmountReleased=0; AmountReleased<MinimumAmountToRelease; )
308 {
309 // Try to free a block
310 // If this fails then break out of the loop
311 if (!CacheInternalFreeBlock(&CacheManagerDrive))
312 {
313 break;
314 }
315
316 // It succeeded so increment the amount of memory we have freed
317 AmountReleased += CacheManagerDrive.BlockSize * CacheManagerDrive.BytesPerSector;
318 }
319
320 // Return status
321 return (AmountReleased >= MinimumAmountToRelease);
322 }