2002-06-04 David Welch <welch@whitehall1-5.seh.ox.ac.uk>
[reactos.git] / reactos / ntoskrnl / mm / marea.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
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 * PROJECT: ReactOS kernel
21 * FILE: ntoskrnl/mm/marea.c
22 * PURPOSE: Implements memory areas
23 * PROGRAMMER: David Welch (welch@mcmail.com)
24 * UPDATE HISTORY:
25 * Created 22/05/98
26 */
27
28 /* INCLUDES *****************************************************************/
29
30 #include <ddk/ntddk.h>
31 #include <internal/mm.h>
32 #include <internal/ps.h>
33 #include <internal/pool.h>
34
35 #define NDEBUG
36 #include <internal/debug.h>
37
38 /* GLOBALS *******************************************************************/
39
40 #define TAG_MAREA TAG('M', 'A', 'R', 'E')
41
42 /* FUNCTIONS *****************************************************************/
43
44 VOID MmDumpMemoryAreas(PLIST_ENTRY ListHead)
45 {
46 PLIST_ENTRY current_entry;
47 MEMORY_AREA* current;
48
49 DbgPrint("MmDumpMemoryAreas()\n");
50
51 current_entry = ListHead->Flink;
52 while (current_entry!=ListHead)
53 {
54 current = CONTAINING_RECORD(current_entry,MEMORY_AREA,Entry);
55 DbgPrint("Base %x Length %x End %x Attributes %x Flink %x\n",
56 current->BaseAddress,current->Length,
57 current->BaseAddress+current->Length,current->Attributes,
58 current->Entry.Flink);
59 current_entry = current_entry->Flink;
60 }
61 DbgPrint("Finished MmDumpMemoryAreas()\n");
62 }
63
64 MEMORY_AREA* MmOpenMemoryAreaByAddress(PMADDRESS_SPACE AddressSpace,
65 PVOID Address)
66 {
67 PLIST_ENTRY current_entry;
68 MEMORY_AREA* current;
69 PLIST_ENTRY previous_entry;
70
71 DPRINT("MmOpenMemoryAreaByAddress(AddressSpace %x, Address %x)\n",
72 AddressSpace, Address);
73
74 // MmDumpMemoryAreas(&AddressSpace->MAreaListHead);
75
76 previous_entry = &AddressSpace->MAreaListHead;
77 current_entry = AddressSpace->MAreaListHead.Flink;
78 while (current_entry != &AddressSpace->MAreaListHead)
79 {
80 current = CONTAINING_RECORD(current_entry,
81 MEMORY_AREA,
82 Entry);
83 DPRINT("Scanning %x BaseAddress %x Length %x\n",
84 current, current->BaseAddress, current->Length);
85 assert(current_entry->Blink->Flink == current_entry);
86 if (current_entry->Flink->Blink != current_entry)
87 {
88 DPRINT1("BaseAddress %x\n", current->BaseAddress);
89 DPRINT1("current_entry->Flink %x ", current_entry->Flink);
90 DPRINT1("&current_entry->Flink %x\n",
91 &current_entry->Flink);
92 DPRINT1("current_entry->Flink->Blink %x\n",
93 current_entry->Flink->Blink);
94 DPRINT1("&current_entry->Flink->Blink %x\n",
95 &current_entry->Flink->Blink);
96 DPRINT1("&current_entry->Flink %x\n",
97 &current_entry->Flink);
98 }
99 assert(current_entry->Flink->Blink == current_entry);
100 assert(previous_entry->Flink == current_entry);
101 if (current->BaseAddress <= Address &&
102 (current->BaseAddress + current->Length) > Address)
103 {
104 DPRINT("%s() = %x\n",__FUNCTION__,current);
105 return(current);
106 }
107 if (current->BaseAddress > Address)
108 {
109 DPRINT("%s() = NULL\n",__FUNCTION__);
110 return(NULL);
111 }
112 previous_entry = current_entry;
113 current_entry = current_entry->Flink;
114 }
115 DPRINT("%s() = NULL\n",__FUNCTION__);
116 return(NULL);
117 }
118
119 MEMORY_AREA* MmOpenMemoryAreaByRegion(PMADDRESS_SPACE AddressSpace,
120 PVOID Address,
121 ULONG Length)
122 {
123 PLIST_ENTRY current_entry;
124 MEMORY_AREA* current;
125 ULONG Extent;
126
127 DPRINT("MmOpenMemoryByRegion(AddressSpace %x, Address %x, Length %x)\n",
128 AddressSpace, Address, Length);
129
130 current_entry = AddressSpace->MAreaListHead.Flink;
131 while (current_entry != &AddressSpace->MAreaListHead)
132 {
133 current = CONTAINING_RECORD(current_entry,
134 MEMORY_AREA,
135 Entry);
136 DPRINT("current->BaseAddress %x current->Length %x\n",
137 current->BaseAddress,current->Length);
138 if (current->BaseAddress >= Address &&
139 current->BaseAddress < (Address+Length))
140 {
141 DPRINT("Finished MmOpenMemoryAreaByRegion() = %x\n",
142 current);
143 return(current);
144 }
145 Extent = (ULONG)current->BaseAddress + current->Length;
146 if (Extent > (ULONG)Address &&
147 Extent < (ULONG)(Address+Length))
148 {
149 DPRINT("Finished MmOpenMemoryAreaByRegion() = %x\n",
150 current);
151 return(current);
152 }
153 if (current->BaseAddress <= Address &&
154 Extent >= (ULONG)(Address+Length))
155 {
156 DPRINT("Finished MmOpenMemoryAreaByRegion() = %x\n",
157 current);
158 return(current);
159 }
160 if (current->BaseAddress >= (Address+Length))
161 {
162 DPRINT("Finished MmOpenMemoryAreaByRegion()= NULL\n",0);
163 return(NULL);
164 }
165 current_entry = current_entry->Flink;
166 }
167 DPRINT("Finished MmOpenMemoryAreaByRegion() = NULL\n",0);
168 return(NULL);
169 }
170
171 static VOID MmInsertMemoryArea(PMADDRESS_SPACE AddressSpace,
172 MEMORY_AREA* marea)
173 {
174 PLIST_ENTRY ListHead;
175 PLIST_ENTRY current_entry;
176 PLIST_ENTRY inserted_entry = &marea->Entry;
177 MEMORY_AREA* current;
178 MEMORY_AREA* next;
179
180 DPRINT("MmInsertMemoryArea(marea %x)\n", marea);
181 DPRINT("marea->BaseAddress %x\n", marea->BaseAddress);
182 DPRINT("marea->Length %x\n", marea->Length);
183
184 ListHead = &AddressSpace->MAreaListHead;
185
186 current_entry = ListHead->Flink;
187 CHECKPOINT;
188 if (IsListEmpty(ListHead))
189 {
190 CHECKPOINT;
191 InsertHeadList(ListHead,&marea->Entry);
192 DPRINT("Inserting at list head\n");
193 CHECKPOINT;
194 return;
195 }
196 CHECKPOINT;
197 current = CONTAINING_RECORD(current_entry,MEMORY_AREA,Entry);
198 CHECKPOINT;
199 if (current->BaseAddress > marea->BaseAddress)
200 {
201 CHECKPOINT;
202 InsertHeadList(ListHead,&marea->Entry);
203 DPRINT("Inserting at list head\n");
204 CHECKPOINT;
205 return;
206 }
207 CHECKPOINT;
208 while (current_entry->Flink!=ListHead)
209 {
210 // CHECKPOINT;
211 current = CONTAINING_RECORD(current_entry,MEMORY_AREA,Entry);
212 next = CONTAINING_RECORD(current_entry->Flink,MEMORY_AREA,Entry);
213 // assert(current->BaseAddress != marea->BaseAddress);
214 // assert(next->BaseAddress != marea->BaseAddress);
215 if (current->BaseAddress < marea->BaseAddress &&
216 current->Entry.Flink==ListHead)
217 {
218 DPRINT("Insert after %x\n", current_entry);
219 current_entry->Flink = inserted_entry;
220 inserted_entry->Flink=ListHead;
221 inserted_entry->Blink=current_entry;
222 ListHead->Blink = inserted_entry;
223 return;
224 }
225 if (current->BaseAddress < marea->BaseAddress &&
226 next->BaseAddress > marea->BaseAddress)
227 {
228 DPRINT("Inserting before %x\n", current_entry);
229 inserted_entry->Flink = current_entry->Flink;
230 inserted_entry->Blink = current_entry;
231 inserted_entry->Flink->Blink = inserted_entry;
232 current_entry->Flink=inserted_entry;
233 return;
234 }
235 current_entry = current_entry->Flink;
236 }
237 CHECKPOINT;
238 DPRINT("Inserting at list tail\n");
239 InsertTailList(ListHead,inserted_entry);
240 }
241
242 static PVOID MmFindGap(PMADDRESS_SPACE AddressSpace,
243 ULONG Length)
244 {
245 PLIST_ENTRY ListHead;
246 PLIST_ENTRY current_entry;
247 MEMORY_AREA* current;
248 MEMORY_AREA* next;
249 ULONG Gap;
250
251 DPRINT("MmFindGap(Length %x)\n",Length);
252
253 ListHead = &AddressSpace->MAreaListHead;
254
255 current_entry = ListHead->Flink;
256 while (current_entry->Flink!=ListHead)
257 {
258 current = CONTAINING_RECORD(current_entry,MEMORY_AREA,Entry);
259 next = CONTAINING_RECORD(current_entry->Flink,MEMORY_AREA,Entry);
260 DPRINT("current %x current->BaseAddress %x ",current,
261 current->BaseAddress);
262 DPRINT("current->Length %x\n",current->Length);
263 DPRINT("next %x next->BaseAddress %x ",next,next->BaseAddress);
264 Gap = (next->BaseAddress ) -(current->BaseAddress + current->Length);
265 DPRINT("Base %x Gap %x\n",current->BaseAddress,Gap);
266 if (Gap >= Length)
267 {
268 return(current->BaseAddress + PAGE_ROUND_UP(current->Length));
269 }
270 current_entry = current_entry->Flink;
271 }
272
273 if (current_entry == ListHead)
274 {
275 return((PVOID)AddressSpace->LowestAddress);
276 }
277
278 current = CONTAINING_RECORD(current_entry,MEMORY_AREA,Entry);
279 //DbgPrint("current %x returning %x\n",current,current->BaseAddress+
280 // current->Length);
281 return(current->BaseAddress + PAGE_ROUND_UP(current->Length));
282 }
283
284 NTSTATUS MmInitMemoryAreas(VOID)
285 /*
286 * FUNCTION: Initialize the memory area list
287 */
288 {
289 DPRINT("MmInitMemoryAreas()\n",0);
290 return(STATUS_SUCCESS);
291 }
292
293 NTSTATUS
294 MmFreeMemoryArea(PMADDRESS_SPACE AddressSpace,
295 PVOID BaseAddress,
296 ULONG Length,
297 VOID (*FreePage)(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address,
298 PHYSICAL_ADDRESS PhysAddr, SWAPENTRY SwapEntry, BOOLEAN Dirty),
299 PVOID FreePageContext)
300 {
301 MEMORY_AREA* MemoryArea;
302 ULONG i;
303
304 DPRINT("MmFreeMemoryArea(AddressSpace %x, BaseAddress %x, Length %x,"
305 "FreePageContext %d)\n",AddressSpace,BaseAddress,Length,FreePageContext);
306
307 MemoryArea = MmOpenMemoryAreaByAddress(AddressSpace,
308 BaseAddress);
309 if (MemoryArea == NULL)
310 {
311 KeBugCheck(0);
312 return(STATUS_UNSUCCESSFUL);
313 }
314 for (i=0; i<(PAGE_ROUND_UP(MemoryArea->Length)/PAGESIZE); i++)
315 {
316 PHYSICAL_ADDRESS PhysAddr = (PHYSICAL_ADDRESS)0LL;
317 BOOL Dirty;
318 SWAPENTRY SwapEntry = 0;
319
320 if (MmIsPageSwapEntry(AddressSpace->Process,
321 MemoryArea->BaseAddress + (i * PAGESIZE)))
322 {
323 MmDeletePageFileMapping(AddressSpace->Process,
324 MemoryArea->BaseAddress + (i * PAGESIZE),
325 &SwapEntry);
326 }
327 else
328 {
329 MmDeleteVirtualMapping(AddressSpace->Process,
330 MemoryArea->BaseAddress + (i*PAGESIZE),
331 FALSE, &Dirty, &PhysAddr);
332 }
333 if (FreePage != NULL)
334 {
335 FreePage(FreePageContext, MemoryArea,
336 MemoryArea->BaseAddress + (i * PAGESIZE), PhysAddr,
337 SwapEntry, Dirty);
338 }
339 }
340
341 RemoveEntryList(&MemoryArea->Entry);
342 ExFreePool(MemoryArea);
343
344 DPRINT("MmFreeMemoryArea() succeeded\n");
345
346 return(STATUS_SUCCESS);
347 }
348
349 PMEMORY_AREA MmSplitMemoryArea(PEPROCESS Process,
350 PMADDRESS_SPACE AddressSpace,
351 PMEMORY_AREA OriginalMemoryArea,
352 PVOID BaseAddress,
353 ULONG Length,
354 ULONG NewType,
355 ULONG NewAttributes)
356 {
357 PMEMORY_AREA Result;
358 PMEMORY_AREA Split;
359
360 Result = ExAllocatePoolWithTag(NonPagedPool, sizeof(MEMORY_AREA),
361 TAG_MAREA);
362 RtlZeroMemory(Result,sizeof(MEMORY_AREA));
363 Result->Type = NewType;
364 Result->BaseAddress = BaseAddress;
365 Result->Length = Length;
366 Result->Attributes = NewAttributes;
367 Result->LockCount = 0;
368 Result->Process = Process;
369
370 if (BaseAddress == OriginalMemoryArea->BaseAddress)
371 {
372 OriginalMemoryArea->BaseAddress = BaseAddress + Length;
373 OriginalMemoryArea->Length = OriginalMemoryArea->Length - Length;
374 MmInsertMemoryArea(AddressSpace, Result);
375 return(Result);
376 }
377 if ((BaseAddress + Length) ==
378 (OriginalMemoryArea->BaseAddress + OriginalMemoryArea->Length))
379 {
380 OriginalMemoryArea->Length = OriginalMemoryArea->Length - Length;
381 MmInsertMemoryArea(AddressSpace, Result);
382
383 return(Result);
384 }
385
386 Split = ExAllocatePoolWithTag(NonPagedPool, sizeof(MEMORY_AREA),
387 TAG_MAREA);
388 RtlCopyMemory(Split,OriginalMemoryArea,sizeof(MEMORY_AREA));
389 Split->BaseAddress = BaseAddress + Length;
390 Split->Length = OriginalMemoryArea->Length - (((ULONG)BaseAddress)
391 + Length);
392
393 OriginalMemoryArea->Length = BaseAddress - OriginalMemoryArea->BaseAddress;
394
395 return(Split);
396 }
397
398 NTSTATUS MmCreateMemoryArea(PEPROCESS Process,
399 PMADDRESS_SPACE AddressSpace,
400 ULONG Type,
401 PVOID* BaseAddress,
402 ULONG Length,
403 ULONG Attributes,
404 MEMORY_AREA** Result,
405 BOOL FixedAddress)
406 /*
407 * FUNCTION: Create a memory area
408 * ARGUMENTS:
409 * AddressSpace = Address space to create the area in
410 * Type = Type of the address space
411 * BaseAddress =
412 * Length = Length to allocate
413 * Attributes = Protection attributes for the memory area
414 * Result = Receives a pointer to the memory area on exit
415 * RETURNS: Status
416 * NOTES: Lock the address space before calling this function
417 */
418 {
419 DPRINT("MmCreateMemoryArea(Type %d, BaseAddress %x,"
420 "*BaseAddress %x, Length %x, Attributes %x, Result %x)\n",
421 Type,BaseAddress,*BaseAddress,Length,Attributes,Result);
422
423 if ((*BaseAddress)==0 && !FixedAddress)
424 {
425 *BaseAddress = MmFindGap(AddressSpace,
426 PAGE_ROUND_UP(Length) +(PAGESIZE*2));
427 if ((*BaseAddress)==0)
428 {
429 DPRINT("No suitable gap\n");
430 return(STATUS_NO_MEMORY);
431 }
432 (*BaseAddress)=(*BaseAddress)+PAGESIZE;
433 }
434 else
435 {
436 (*BaseAddress) = (PVOID)PAGE_ROUND_DOWN((*BaseAddress));
437 if (MmOpenMemoryAreaByRegion(AddressSpace,
438 *BaseAddress,
439 Length)!=NULL)
440 {
441 DPRINT("Memory area already occupied\n");
442 return(STATUS_CONFLICTING_ADDRESSES);
443 }
444 }
445
446 *Result = ExAllocatePoolWithTag(NonPagedPool, sizeof(MEMORY_AREA),
447 TAG_MAREA);
448 RtlZeroMemory(*Result,sizeof(MEMORY_AREA));
449 (*Result)->Type = Type;
450 (*Result)->BaseAddress = *BaseAddress;
451 (*Result)->Length = Length;
452 (*Result)->Attributes = Attributes;
453 (*Result)->LockCount = 0;
454 (*Result)->Process = Process;
455
456 MmInsertMemoryArea(AddressSpace, *Result);
457
458 DPRINT("MmCreateMemoryArea() succeeded\n");
459 return(STATUS_SUCCESS);
460 }