Create a place for work on creating a bootloader capable of booting Windows NT (from...
[reactos.git] / 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
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #include <freeldr.h>
21
22 #define NDEBUG
23 #include <debug.h>
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 ULONG CacheSizeLimit = 0;
35 ULONG CacheSizeCurrent = 0;
36
37 BOOLEAN CacheInitializeDrive(ULONG 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 DbgPrint((DPRINT_CACHE, "CacheBlockCount: %d\n", CacheBlockCount));
64 DbgPrint((DPRINT_CACHE, "CacheSizeLimit: %d\n", CacheSizeLimit));
65 DbgPrint((DPRINT_CACHE, "CacheSizeCurrent: %d\n", CacheSizeCurrent));
66 //
67 // Loop through and free the cache blocks
68 //
69 while (CacheManagerDrive.CacheBlockHead != NULL)
70 {
71 NextCacheBlock = (PCACHE_BLOCK)RtlListGetNext((PLIST_ITEM)CacheManagerDrive.CacheBlockHead);
72
73 MmFreeMemory(CacheManagerDrive.CacheBlockHead->BlockData);
74 MmFreeMemory(CacheManagerDrive.CacheBlockHead);
75
76 CacheManagerDrive.CacheBlockHead = NextCacheBlock;
77 }
78 }
79
80 // Initialize the structure
81 RtlZeroMemory(&CacheManagerDrive, sizeof(CACHE_DRIVE));
82 CacheManagerDrive.DriveNumber = DriveNumber;
83 if (!MachDiskGetDriveGeometry(DriveNumber, &DriveGeometry))
84 {
85 return FALSE;
86 }
87 CacheManagerDrive.BytesPerSector = DriveGeometry.BytesPerSector;
88
89 // Get the number of sectors in each cache block
90 CacheManagerDrive.BlockSize = MachDiskGetCacheableBlockCount(DriveNumber);
91
92 CacheBlockCount = 0;
93 CacheSizeLimit = GetSystemMemorySize() / 8;
94 CacheSizeCurrent = 0;
95 if (CacheSizeLimit < (64 * 1024))
96 {
97 CacheSizeLimit = (64 * 1024);
98 }
99
100 CacheManagerInitialized = TRUE;
101
102 DbgPrint((DPRINT_CACHE, "Initializing BIOS drive 0x%x.\n", DriveNumber));
103 DbgPrint((DPRINT_CACHE, "BytesPerSector: %d.\n", CacheManagerDrive.BytesPerSector));
104 DbgPrint((DPRINT_CACHE, "BlockSize: %d.\n", CacheManagerDrive.BlockSize));
105 DbgPrint((DPRINT_CACHE, "CacheSizeLimit: %d.\n", CacheSizeLimit));
106
107 return TRUE;
108 }
109
110 VOID CacheInvalidateCacheData(VOID)
111 {
112 CacheManagerDataInvalid = TRUE;
113 }
114
115 BOOLEAN CacheReadDiskSectors(ULONG DiskNumber, ULONG StartSector, ULONG SectorCount, PVOID Buffer)
116 {
117 PCACHE_BLOCK CacheBlock;
118 ULONG StartBlock;
119 ULONG SectorOffsetInStartBlock;
120 ULONG CopyLengthInStartBlock;
121 ULONG EndBlock;
122 ULONG SectorOffsetInEndBlock;
123 ULONG BlockCount;
124 ULONG Idx;
125
126 DbgPrint((DPRINT_CACHE, "CacheReadDiskSectors() DiskNumber: 0x%x StartSector: %d SectorCount: %d Buffer: 0x%x\n", DiskNumber, StartSector, SectorCount, Buffer));
127
128 // If we aren't initialized yet then they can't do this
129 if (CacheManagerInitialized == FALSE)
130 {
131 return FALSE;
132 }
133
134 //
135 // Caculate which blocks we must cache
136 //
137 StartBlock = StartSector / CacheManagerDrive.BlockSize;
138 SectorOffsetInStartBlock = StartSector % CacheManagerDrive.BlockSize;
139 CopyLengthInStartBlock = (SectorCount > (CacheManagerDrive.BlockSize - SectorOffsetInStartBlock)) ? (CacheManagerDrive.BlockSize - SectorOffsetInStartBlock) : SectorCount;
140 EndBlock = (StartSector + (SectorCount - 1)) / CacheManagerDrive.BlockSize;
141 SectorOffsetInEndBlock = 1 + (StartSector + (SectorCount - 1)) % CacheManagerDrive.BlockSize;
142 BlockCount = (EndBlock - StartBlock) + 1;
143 DbgPrint((DPRINT_CACHE, "StartBlock: %d SectorOffsetInStartBlock: %d CopyLengthInStartBlock: %d EndBlock: %d SectorOffsetInEndBlock: %d BlockCount: %d\n", StartBlock, SectorOffsetInStartBlock, CopyLengthInStartBlock, EndBlock, SectorOffsetInEndBlock, BlockCount));
144
145 //
146 // Read the first block into the buffer
147 //
148 if (BlockCount > 0)
149 {
150 //
151 // Get cache block pointer (this forces the disk sectors into the cache memory)
152 //
153 CacheBlock = CacheInternalGetBlockPointer(&CacheManagerDrive, StartBlock);
154 if (CacheBlock == NULL)
155 {
156 return FALSE;
157 }
158
159 //
160 // Copy the portion requested into the buffer
161 //
162 RtlCopyMemory(Buffer,
163 (PVOID)((ULONG_PTR)CacheBlock->BlockData + (SectorOffsetInStartBlock * CacheManagerDrive.BytesPerSector)),
164 (CopyLengthInStartBlock * CacheManagerDrive.BytesPerSector));
165 DbgPrint((DPRINT_CACHE, "1 - RtlCopyMemory(0x%x, 0x%x, %d)\n", Buffer, ((ULONG_PTR)CacheBlock->BlockData + (SectorOffsetInStartBlock * CacheManagerDrive.BytesPerSector)), (CopyLengthInStartBlock * CacheManagerDrive.BytesPerSector)));
166
167 //
168 // Update the buffer address
169 //
170 Buffer = (PVOID)((ULONG_PTR)Buffer + (CopyLengthInStartBlock * CacheManagerDrive.BytesPerSector));
171
172 //
173 // Update the block count
174 //
175 BlockCount--;
176 }
177
178 //
179 // Loop through the middle blocks and read them into the buffer
180 //
181 for (Idx=StartBlock+1; BlockCount>1; Idx++)
182 {
183 //
184 // Get cache block pointer (this forces the disk sectors into the cache memory)
185 //
186 CacheBlock = CacheInternalGetBlockPointer(&CacheManagerDrive, Idx);
187 if (CacheBlock == NULL)
188 {
189 return FALSE;
190 }
191
192 //
193 // Copy the portion requested into the buffer
194 //
195 RtlCopyMemory(Buffer,
196 CacheBlock->BlockData,
197 CacheManagerDrive.BlockSize * CacheManagerDrive.BytesPerSector);
198 DbgPrint((DPRINT_CACHE, "2 - RtlCopyMemory(0x%x, 0x%x, %d)\n", Buffer, CacheBlock->BlockData, CacheManagerDrive.BlockSize * CacheManagerDrive.BytesPerSector));
199
200 //
201 // Update the buffer address
202 //
203 Buffer = (PVOID)((ULONG_PTR)Buffer + (CacheManagerDrive.BlockSize * CacheManagerDrive.BytesPerSector));
204
205 //
206 // Update the block count
207 //
208 BlockCount--;
209 }
210
211 //
212 // Read the last block into the buffer
213 //
214 if (BlockCount > 0)
215 {
216 //
217 // Get cache block pointer (this forces the disk sectors into the cache memory)
218 //
219 CacheBlock = CacheInternalGetBlockPointer(&CacheManagerDrive, EndBlock);
220 if (CacheBlock == NULL)
221 {
222 return FALSE;
223 }
224
225 //
226 // Copy the portion requested into the buffer
227 //
228 RtlCopyMemory(Buffer,
229 CacheBlock->BlockData,
230 SectorOffsetInEndBlock * CacheManagerDrive.BytesPerSector);
231 DbgPrint((DPRINT_CACHE, "3 - RtlCopyMemory(0x%x, 0x%x, %d)\n", Buffer, CacheBlock->BlockData, SectorOffsetInEndBlock * CacheManagerDrive.BytesPerSector));
232
233 //
234 // Update the buffer address
235 //
236 Buffer = (PVOID)((ULONG_PTR)Buffer + (SectorOffsetInEndBlock * CacheManagerDrive.BytesPerSector));
237
238 //
239 // Update the block count
240 //
241 BlockCount--;
242 }
243
244 return TRUE;
245 }
246
247 BOOLEAN CacheForceDiskSectorsIntoCache(ULONG DiskNumber, ULONG StartSector, ULONG SectorCount)
248 {
249 PCACHE_BLOCK CacheBlock;
250 ULONG StartBlock;
251 ULONG EndBlock;
252 ULONG BlockCount;
253 ULONG Idx;
254
255 DbgPrint((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
293 BOOLEAN CacheReleaseMemory(ULONG MinimumAmountToRelease)
294 {
295 ULONG AmountReleased;
296
297 DbgPrint((DPRINT_CACHE, "CacheReleaseMemory() MinimumAmountToRelease = %d\n", MinimumAmountToRelease));
298
299 // If we aren't initialized yet then they can't do this
300 if (CacheManagerInitialized == FALSE)
301 {
302 return FALSE;
303 }
304
305 // Loop through and try to free the requested amount of memory
306 for (AmountReleased=0; AmountReleased<MinimumAmountToRelease; )
307 {
308 // Try to free a block
309 // If this fails then break out of the loop
310 if (!CacheInternalFreeBlock(&CacheManagerDrive))
311 {
312 break;
313 }
314
315 // It succeeded so increment the amount of memory we have freed
316 AmountReleased += CacheManagerDrive.BlockSize * CacheManagerDrive.BytesPerSector;
317 }
318
319 // Return status
320 return (AmountReleased >= MinimumAmountToRelease);
321 }