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