402069a634e3aa474fae620ad684fe17c84ba5f7
[reactos.git] / reactos / boot / freeldr / freeldr / mm / mm.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 #include <mm.h>
22 #include "mem.h"
23 #include <rtl.h>
24 #include <debug.h>
25 #include <ui.h>
26 #include <machine.h>
27
28
29 #ifdef DEBUG
30 ULONG AllocationCount = 0;
31
32 VOID VerifyHeap(VOID);
33 VOID DumpMemoryAllocMap(VOID);
34 VOID IncrementAllocationCount(VOID);
35 VOID DecrementAllocationCount(VOID);
36 VOID MemAllocTest(VOID);
37 #endif // DEBUG
38
39 PVOID MmAllocateMemory(ULONG MemorySize)
40 {
41 ULONG PagesNeeded;
42 ULONG FirstFreePageFromEnd;
43 PVOID MemPointer;
44
45 if (MemorySize == 0)
46 {
47 DbgPrint((DPRINT_MEMORY, "MmAllocateMemory() called for 0 bytes. Returning NULL.\n"));
48 UiMessageBoxCritical("Memory allocation failed: MmAllocateMemory() called for 0 bytes.");
49 return NULL;
50 }
51
52 // Find out how many blocks it will take to
53 // satisfy this allocation
54 PagesNeeded = ROUND_UP(MemorySize, MM_PAGE_SIZE) / MM_PAGE_SIZE;
55
56 // If we don't have enough available mem
57 // then return NULL
58 if (FreePagesInLookupTable < PagesNeeded)
59 {
60 DbgPrint((DPRINT_MEMORY, "Memory allocation failed. Not enough free memory to allocate %d bytes. AllocationCount: %d\n", MemorySize, AllocationCount));
61 UiMessageBoxCritical("Memory allocation failed: out of memory.");
62 return NULL;
63 }
64
65 FirstFreePageFromEnd = MmFindAvailablePagesFromEnd(PageLookupTableAddress, TotalPagesInLookupTable, PagesNeeded);
66
67 if (FirstFreePageFromEnd == 0)
68 {
69 DbgPrint((DPRINT_MEMORY, "Memory allocation failed. Not enough free memory to allocate %d bytes. AllocationCount: %d\n", MemorySize, AllocationCount));
70 UiMessageBoxCritical("Memory allocation failed: out of memory.");
71 return NULL;
72 }
73
74 MmAllocatePagesInLookupTable(PageLookupTableAddress, FirstFreePageFromEnd, PagesNeeded);
75
76 FreePagesInLookupTable -= PagesNeeded;
77 MemPointer = (PVOID)(FirstFreePageFromEnd * MM_PAGE_SIZE);
78
79 #ifdef DEBUG
80 IncrementAllocationCount();
81 DbgPrint((DPRINT_MEMORY, "Allocated %d bytes (%d pages) of memory starting at page %d. AllocCount: %d\n", MemorySize, PagesNeeded, FirstFreePageFromEnd, AllocationCount));
82 DbgPrint((DPRINT_MEMORY, "Memory allocation pointer: 0x%x\n", MemPointer));
83 //VerifyHeap();
84 #endif // DEBUG
85
86 // Now return the pointer
87 return MemPointer;
88 }
89
90 PVOID MmAllocateMemoryAtAddress(ULONG MemorySize, PVOID DesiredAddress)
91 {
92 ULONG PagesNeeded;
93 ULONG StartPageNumber;
94 PVOID MemPointer;
95
96 if (MemorySize == 0)
97 {
98 DbgPrint((DPRINT_MEMORY, "MmAllocateMemoryAtAddress() called for 0 bytes. Returning NULL.\n"));
99 UiMessageBoxCritical("Memory allocation failed: MmAllocateMemoryAtAddress() called for 0 bytes.");
100 return NULL;
101 }
102
103 // Find out how many blocks it will take to
104 // satisfy this allocation
105 PagesNeeded = ROUND_UP(MemorySize, MM_PAGE_SIZE) / MM_PAGE_SIZE;
106
107 // Get the starting page number
108 StartPageNumber = MmGetPageNumberFromAddress(DesiredAddress);
109
110 // If we don't have enough available mem
111 // then return NULL
112 if (FreePagesInLookupTable < PagesNeeded)
113 {
114 DbgPrint((DPRINT_MEMORY, "Memory allocation failed. Not enough free memory to allocate %d bytes. AllocationCount: %d\n", MemorySize, AllocationCount));
115 UiMessageBoxCritical("Memory allocation failed: out of memory.");
116 return NULL;
117 }
118
119 if (MmAreMemoryPagesAvailable(PageLookupTableAddress, TotalPagesInLookupTable, DesiredAddress, PagesNeeded) == FALSE)
120 {
121 DbgPrint((DPRINT_MEMORY, "Memory allocation failed. Not enough free memory to allocate %d bytes. AllocationCount: %d\n", MemorySize, AllocationCount));
122 UiMessageBoxCritical("Memory allocation failed: out of memory.");
123 return NULL;
124 }
125
126 MmAllocatePagesInLookupTable(PageLookupTableAddress, StartPageNumber, PagesNeeded);
127
128 FreePagesInLookupTable -= PagesNeeded;
129 MemPointer = (PVOID)(StartPageNumber * MM_PAGE_SIZE);
130
131 #ifdef DEBUG
132 IncrementAllocationCount();
133 DbgPrint((DPRINT_MEMORY, "Allocated %d bytes (%d pages) of memory starting at page %d. AllocCount: %d\n", MemorySize, PagesNeeded, StartPageNumber, AllocationCount));
134 DbgPrint((DPRINT_MEMORY, "Memory allocation pointer: 0x%x\n", MemPointer));
135 //VerifyHeap();
136 #endif // DEBUG
137
138 // Now return the pointer
139 return MemPointer;
140 }
141
142 PVOID MmAllocateHighestMemoryBelowAddress(ULONG MemorySize, PVOID DesiredAddress)
143 {
144 ULONG PagesNeeded;
145 ULONG FirstFreePageFromEnd;
146 ULONG DesiredAddressPageNumber;
147 PVOID MemPointer;
148
149 if (MemorySize == 0)
150 {
151 DbgPrint((DPRINT_MEMORY, "MmAllocateHighestMemoryBelowAddress() called for 0 bytes. Returning NULL.\n"));
152 UiMessageBoxCritical("Memory allocation failed: MmAllocateHighestMemoryBelowAddress() called for 0 bytes.");
153 return NULL;
154 }
155
156 // Find out how many blocks it will take to
157 // satisfy this allocation
158 PagesNeeded = ROUND_UP(MemorySize, MM_PAGE_SIZE) / MM_PAGE_SIZE;
159
160 // Get the page number for their desired address
161 DesiredAddressPageNumber = (ULONG)DesiredAddress / MM_PAGE_SIZE;
162
163 // If we don't have enough available mem
164 // then return NULL
165 if (FreePagesInLookupTable < PagesNeeded)
166 {
167 DbgPrint((DPRINT_MEMORY, "Memory allocation failed. Not enough free memory to allocate %d bytes. AllocationCount: %d\n", MemorySize, AllocationCount));
168 UiMessageBoxCritical("Memory allocation failed: out of memory.");
169 return NULL;
170 }
171
172 FirstFreePageFromEnd = MmFindAvailablePagesBeforePage(PageLookupTableAddress, TotalPagesInLookupTable, PagesNeeded, DesiredAddressPageNumber);
173
174 if (FirstFreePageFromEnd == 0)
175 {
176 DbgPrint((DPRINT_MEMORY, "Memory allocation failed. Not enough free memory to allocate %d bytes. AllocationCount: %d\n", MemorySize, AllocationCount));
177 UiMessageBoxCritical("Memory allocation failed: out of memory.");
178 return NULL;
179 }
180
181 MmAllocatePagesInLookupTable(PageLookupTableAddress, FirstFreePageFromEnd, PagesNeeded);
182
183 FreePagesInLookupTable -= PagesNeeded;
184 MemPointer = (PVOID)(FirstFreePageFromEnd * MM_PAGE_SIZE);
185
186 #ifdef DEBUG
187 IncrementAllocationCount();
188 DbgPrint((DPRINT_MEMORY, "Allocated %d bytes (%d pages) of memory starting at page %d. AllocCount: %d\n", MemorySize, PagesNeeded, FirstFreePageFromEnd, AllocationCount));
189 DbgPrint((DPRINT_MEMORY, "Memory allocation pointer: 0x%x\n", MemPointer));
190 //VerifyHeap();
191 #endif // DEBUG
192
193 // Now return the pointer
194 return MemPointer;
195 }
196
197 VOID MmFreeMemory(PVOID MemoryPointer)
198 {
199 ULONG PageNumber;
200 ULONG PageCount;
201 ULONG Idx;
202 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable = (PPAGE_LOOKUP_TABLE_ITEM)PageLookupTableAddress;
203
204 #ifdef DEBUG
205
206 // Make sure we didn't get a bogus pointer
207 if (MemoryPointer >= (PVOID)(TotalPagesInLookupTable * MM_PAGE_SIZE))
208 {
209 BugCheck((DPRINT_MEMORY, "Bogus memory pointer (0x%x) passed to MmFreeMemory()\n", MemoryPointer));
210 }
211 #endif // DEBUG
212
213 // Find out the page number of the first
214 // page of memory they allocated
215 PageNumber = MmGetPageNumberFromAddress(MemoryPointer);
216 PageCount = RealPageLookupTable[PageNumber].PageAllocationLength;
217
218 #ifdef DEBUG
219 // Make sure we didn't get a bogus pointer
220 if ((PageCount < 1) || (PageCount > (TotalPagesInLookupTable - PageNumber)))
221 {
222 BugCheck((DPRINT_MEMORY, "Invalid page count in lookup table. PageLookupTable[%d].PageAllocationLength = %d\n", PageNumber, RealPageLookupTable[PageNumber].PageAllocationLength));
223 }
224
225 // Loop through our array check all the pages
226 // to make sure they are allocated with a length of 0
227 for (Idx=PageNumber+1; Idx<(PageNumber + PageCount); Idx++)
228 {
229 if ((RealPageLookupTable[Idx].PageAllocated != 1) ||
230 (RealPageLookupTable[Idx].PageAllocationLength != 0))
231 {
232 BugCheck((DPRINT_MEMORY, "Invalid page entry in lookup table, PageAllocated should = 1 and PageAllocationLength should = 0 because this is not the first block in the run. PageLookupTable[%d].PageAllocated = %d PageLookupTable[%d].PageAllocationLength = %d\n", PageNumber, RealPageLookupTable[PageNumber].PageAllocated, PageNumber, RealPageLookupTable[PageNumber].PageAllocationLength));
233 }
234 }
235
236 #endif
237
238 // Loop through our array and mark all the
239 // blocks as free
240 for (Idx=PageNumber; Idx<(PageNumber + PageCount); Idx++)
241 {
242 RealPageLookupTable[Idx].PageAllocated = 0;
243 RealPageLookupTable[Idx].PageAllocationLength = 0;
244 }
245
246 FreePagesInLookupTable += PageCount;
247
248 #ifdef DEBUG
249 DecrementAllocationCount();
250 DbgPrint((DPRINT_MEMORY, "Freed %d pages of memory starting at page %d. AllocationCount: %d\n", PageCount, PageNumber, AllocationCount));
251 //VerifyHeap();
252 #endif // DEBUG
253 }
254
255 #ifdef DEBUG
256 VOID VerifyHeap(VOID)
257 {
258 ULONG Idx;
259 ULONG Idx2;
260 ULONG Count;
261 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable = (PPAGE_LOOKUP_TABLE_ITEM)PageLookupTableAddress;
262
263 if (DUMP_MEM_MAP_ON_VERIFY)
264 {
265 DumpMemoryAllocMap();
266 }
267
268 // Loop through the array and verify that
269 // everything is kosher
270 for (Idx=0; Idx<TotalPagesInLookupTable; Idx++)
271 {
272 // Check if this block is allocated
273 if (RealPageLookupTable[Idx].PageAllocated != 0)
274 {
275 // This is the first block in the run so it
276 // had better have a length that is within range
277 if ((RealPageLookupTable[Idx].PageAllocationLength < 1) || (RealPageLookupTable[Idx].PageAllocationLength > (TotalPagesInLookupTable - Idx)))
278 {
279 BugCheck((DPRINT_MEMORY, "Allocation length out of range in heap table. PageLookupTable[Idx].PageAllocationLength = %d\n", RealPageLookupTable[Idx].PageAllocationLength));
280 }
281
282 // Now go through and verify that the rest of
283 // this run has the blocks marked allocated
284 // with a length of zero but don't check the
285 // first one because we already did
286 Count = RealPageLookupTable[Idx].PageAllocationLength;
287 for (Idx2=1; Idx2<Count; Idx2++)
288 {
289 // Make sure it's allocated
290 if (RealPageLookupTable[Idx + Idx2].PageAllocated == 0)
291 {
292 BugCheck((DPRINT_MEMORY, "Lookup table indicates hole in memory allocation. RealPageLookupTable[Idx + Idx2].PageAllocated == 0\n"));
293 }
294
295 // Make sure the length is zero
296 if (RealPageLookupTable[Idx + Idx2].PageAllocationLength != 0)
297 {
298 BugCheck((DPRINT_MEMORY, "Allocation chain has non-zero value in non-first block in lookup table. RealPageLookupTable[Idx + Idx2].PageAllocationLength != 0\n"));
299 }
300 }
301
302 // Move on to the next run
303 Idx += (Count - 1);
304 }
305 else
306 {
307 // Nope, not allocated so make sure the length is zero
308 if (RealPageLookupTable[Idx].PageAllocationLength != 0)
309 {
310 BugCheck((DPRINT_MEMORY, "Free block is start of memory allocation. RealPageLookupTable[Idx].PageAllocationLength != 0\n"));
311 }
312 }
313 }
314 }
315
316 VOID DumpMemoryAllocMap(VOID)
317 {
318 ULONG Idx;
319 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable = (PPAGE_LOOKUP_TABLE_ITEM)PageLookupTableAddress;
320
321 DbgPrint((DPRINT_MEMORY, "----------- Memory Allocation Bitmap -----------\n"));
322
323 for (Idx=0; Idx<TotalPagesInLookupTable; Idx++)
324 {
325 if ((Idx % 32) == 0)
326 {
327 DbgPrint((DPRINT_MEMORY, "\n"));
328 DbgPrint((DPRINT_MEMORY, "%x:\t", (Idx * MM_PAGE_SIZE)));
329 }
330 else if ((Idx % 4) == 0)
331 {
332 DbgPrint((DPRINT_MEMORY, " "));
333 }
334
335 switch (RealPageLookupTable[Idx].PageAllocated)
336 {
337 case 0:
338 DbgPrint((DPRINT_MEMORY, "*"));
339 break;
340 case 1:
341 DbgPrint((DPRINT_MEMORY, "A"));
342 break;
343 case MEMTYPE_RESERVED:
344 DbgPrint((DPRINT_MEMORY, "R"));
345 break;
346 case MEMTYPE_ACPI_RECLAIM:
347 DbgPrint((DPRINT_MEMORY, "M"));
348 break;
349 case MEMTYPE_ACPI_NVS:
350 DbgPrint((DPRINT_MEMORY, "N"));
351 break;
352 default:
353 DbgPrint((DPRINT_MEMORY, "X"));
354 break;
355 }
356 }
357
358 DbgPrint((DPRINT_MEMORY, "\n"));
359 }
360
361 VOID IncrementAllocationCount(VOID)
362 {
363 AllocationCount++;
364 }
365
366 VOID DecrementAllocationCount(VOID)
367 {
368 AllocationCount--;
369 }
370
371 VOID MemAllocTest(VOID)
372 {
373 PVOID MemPtr1;
374 PVOID MemPtr2;
375 PVOID MemPtr3;
376 PVOID MemPtr4;
377 PVOID MemPtr5;
378
379 MemPtr1 = MmAllocateMemory(4096);
380 printf("MemPtr1: 0x%x\n", (int)MemPtr1);
381 MachConsGetCh();
382 MemPtr2 = MmAllocateMemory(4096);
383 printf("MemPtr2: 0x%x\n", (int)MemPtr2);
384 MachConsGetCh();
385 MemPtr3 = MmAllocateMemory(4096);
386 printf("MemPtr3: 0x%x\n", (int)MemPtr3);
387 DumpMemoryAllocMap();
388 VerifyHeap();
389 MachConsGetCh();
390
391 MmFreeMemory(MemPtr2);
392 MachConsGetCh();
393
394 MemPtr4 = MmAllocateMemory(2048);
395 printf("MemPtr4: 0x%x\n", (int)MemPtr4);
396 MachConsGetCh();
397 MemPtr5 = MmAllocateMemory(4096);
398 printf("MemPtr5: 0x%x\n", (int)MemPtr5);
399 MachConsGetCh();
400 }
401 #endif // DEBUG
402
403 ULONG GetSystemMemorySize(VOID)
404 {
405 return (TotalPagesInLookupTable * MM_PAGE_SIZE);
406 }