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