update for HEAD-2003091401
[reactos.git] / ntoskrnl / mm / marea.c
1 /*
2  *  ReactOS kernel
3  *  Copyright (C) 1998, 1999, 2000, 2001, 2003 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    previous_entry = &AddressSpace->MAreaListHead;
75    current_entry = AddressSpace->MAreaListHead.Flink;
76    while (current_entry != &AddressSpace->MAreaListHead)
77      {
78         current = CONTAINING_RECORD(current_entry,
79                                     MEMORY_AREA,
80                                     Entry);
81         assert(current_entry->Blink->Flink == current_entry);
82         assert(current_entry->Flink->Blink == current_entry);
83         assert(previous_entry->Flink == current_entry);
84         if (current->BaseAddress <= Address &&
85             (current->BaseAddress + current->Length) > Address)
86           {
87              DPRINT("%s() = %x\n",__FUNCTION__,current);
88              return(current);
89           }
90         if (current->BaseAddress > Address)
91           {
92              DPRINT("%s() = NULL\n",__FUNCTION__);
93              return(NULL);
94           }
95         previous_entry = current_entry;
96         current_entry = current_entry->Flink;
97      }
98    DPRINT("%s() = NULL\n",__FUNCTION__);
99    return(NULL);
100 }
101
102 MEMORY_AREA* MmOpenMemoryAreaByRegion(PMADDRESS_SPACE AddressSpace, 
103                                       PVOID Address,
104                                       ULONG Length)
105 {
106    PLIST_ENTRY current_entry;
107    MEMORY_AREA* current;
108    ULONG Extent;
109    
110    DPRINT("MmOpenMemoryByRegion(AddressSpace %x, Address %x, Length %x)\n",
111           AddressSpace, Address, Length);
112    
113    current_entry = AddressSpace->MAreaListHead.Flink;
114    while (current_entry != &AddressSpace->MAreaListHead)
115      {
116         current = CONTAINING_RECORD(current_entry,
117                                     MEMORY_AREA,
118                                     Entry);
119         DPRINT("current->BaseAddress %x current->Length %x\n",
120                current->BaseAddress,current->Length);
121         if (current->BaseAddress >= Address &&
122             current->BaseAddress < (Address+Length))
123           {
124              DPRINT("Finished MmOpenMemoryAreaByRegion() = %x\n",
125                     current);
126              return(current);
127           }
128         Extent = (ULONG)current->BaseAddress + current->Length;
129         if (Extent > (ULONG)Address &&
130             Extent < (ULONG)(Address+Length))
131           {
132              DPRINT("Finished MmOpenMemoryAreaByRegion() = %x\n",
133                     current);
134              return(current);
135           }
136         if (current->BaseAddress <= Address &&
137             Extent >= (ULONG)(Address+Length))
138           {
139              DPRINT("Finished MmOpenMemoryAreaByRegion() = %x\n",
140                     current);
141              return(current);
142           }
143         if (current->BaseAddress >= (Address+Length))
144           {
145              DPRINT("Finished MmOpenMemoryAreaByRegion()= NULL\n",0);
146              return(NULL);
147           }
148         current_entry = current_entry->Flink;
149      }
150    DPRINT("Finished MmOpenMemoryAreaByRegion() = NULL\n",0);
151    return(NULL);
152 }
153
154 static VOID MmInsertMemoryArea(PMADDRESS_SPACE AddressSpace,
155                                MEMORY_AREA* marea)
156 {
157    PLIST_ENTRY ListHead;
158    PLIST_ENTRY current_entry;
159    PLIST_ENTRY inserted_entry = &marea->Entry;
160    MEMORY_AREA* current;
161    MEMORY_AREA* next;   
162    
163    DPRINT("MmInsertMemoryArea(marea %x)\n", marea);
164    DPRINT("marea->BaseAddress %x\n", marea->BaseAddress);
165    DPRINT("marea->Length %x\n", marea->Length);
166    
167    ListHead = &AddressSpace->MAreaListHead;
168    
169    current_entry = ListHead->Flink;
170    if (IsListEmpty(ListHead))
171      {
172         InsertHeadList(ListHead,&marea->Entry);
173         return;
174      }
175    current = CONTAINING_RECORD(current_entry,MEMORY_AREA,Entry);
176    if (current->BaseAddress > marea->BaseAddress)
177      {
178         InsertHeadList(ListHead,&marea->Entry);
179         return;
180      }
181    while (current_entry->Flink!=ListHead)
182      {
183         current = CONTAINING_RECORD(current_entry,MEMORY_AREA,Entry);
184         next = CONTAINING_RECORD(current_entry->Flink,MEMORY_AREA,Entry);
185         if (current->BaseAddress < marea->BaseAddress &&
186             current->Entry.Flink==ListHead)
187           {
188              current_entry->Flink = inserted_entry;
189              inserted_entry->Flink=ListHead;
190              inserted_entry->Blink=current_entry;
191              ListHead->Blink = inserted_entry;
192              return;
193           }
194         if (current->BaseAddress < marea->BaseAddress &&
195             next->BaseAddress > marea->BaseAddress)
196           {
197              inserted_entry->Flink = current_entry->Flink;
198              inserted_entry->Blink = current_entry;
199              inserted_entry->Flink->Blink = inserted_entry;
200              current_entry->Flink=inserted_entry;
201              return;
202           }
203         current_entry = current_entry->Flink;
204      }
205    InsertTailList(ListHead,inserted_entry);
206 }
207
208
209 PVOID MmFindGapBottomUp(PMADDRESS_SPACE AddressSpace, ULONG Length)
210 {
211    PLIST_ENTRY ListHead;
212    PLIST_ENTRY current_entry;
213    MEMORY_AREA* current;
214    MEMORY_AREA* next;
215    ULONG Gap;
216    PVOID Address;
217    
218    DPRINT("MmFindGapBottomUp(Length %x)\n",Length);
219    
220    ListHead = &AddressSpace->MAreaListHead;
221    
222    current_entry = ListHead->Flink;
223    while (current_entry->Flink!=ListHead)
224      {
225         current = CONTAINING_RECORD(current_entry,MEMORY_AREA,Entry);
226         next = CONTAINING_RECORD(current_entry->Flink,MEMORY_AREA,Entry);
227         Gap = next->BaseAddress - (current->BaseAddress + PAGE_ROUND_UP(current->Length));
228         if (Gap >= Length)
229           {
230              return(current->BaseAddress + PAGE_ROUND_UP(current->Length));
231           }
232         current_entry = current_entry->Flink;
233      }
234    
235    if (current_entry == ListHead)
236      {
237         Address = (PVOID)AddressSpace->LowestAddress;
238      }
239    else
240      {
241         current = CONTAINING_RECORD(current_entry,MEMORY_AREA,Entry);
242         Address = current->BaseAddress + PAGE_ROUND_UP(current->Length);
243      }
244    /* Check if enough space for the block */
245    if (AddressSpace->LowestAddress < KERNEL_BASE)
246      {
247         if ((ULONG)Address >= KERNEL_BASE || Length > KERNEL_BASE - (ULONG)Address)
248           {
249              return NULL;
250           }
251      }
252    else
253      {
254         if (Length >= 0xFFFFFFFF - (ULONG)Address)
255           {
256              return NULL;
257           }
258      }
259    return Address;
260 }
261
262
263 PVOID MmFindGapTopDown(PMADDRESS_SPACE AddressSpace, ULONG Length)
264 {
265   PLIST_ENTRY ListHead;
266   PLIST_ENTRY current_entry;
267   MEMORY_AREA* current;
268   ULONG Gap;
269   PVOID Address;
270   PVOID TopAddress;
271   PVOID BottomAddress;
272   PVOID HighestAddress;
273
274   DPRINT("MmFindGapTopDown(Length %lx)\n",Length);
275
276   if (AddressSpace->LowestAddress < KERNEL_BASE) //(ULONG_PTR)MmSystemRangeStart)
277     {
278       HighestAddress = MmHighestUserAddress;
279     }
280   else
281     {
282       HighestAddress = (PVOID)0xFFFFFFFF;
283     }
284
285   TopAddress = HighestAddress;
286
287   ListHead = &AddressSpace->MAreaListHead;
288   current_entry = ListHead->Blink;
289   while (current_entry->Blink != ListHead)
290     {
291       current = CONTAINING_RECORD(current_entry,MEMORY_AREA,Entry);
292       BottomAddress = current->BaseAddress + PAGE_ROUND_UP(current->Length);
293       DPRINT("Base %p  Length %lx\n", current->BaseAddress, PAGE_ROUND_UP(current->Length));
294
295       if (BottomAddress < HighestAddress)
296         {
297           Gap = TopAddress - BottomAddress + 1;
298           DPRINT("Bottom %p  Top %p  Gap %lx\n", BottomAddress, TopAddress, Gap);
299           if (Gap >= Length)
300             {
301               DPRINT("Found gap at %p\n", TopAddress - Length);
302               return(TopAddress - Length + 1);
303             }
304           TopAddress = current->BaseAddress - 1;
305         }
306       current_entry = current_entry->Blink;
307     }
308
309   if (current_entry == ListHead)
310     {
311       Address = (PVOID)HighestAddress - Length + 1;
312     }
313   else
314     {
315       Address = TopAddress - Length + 1;
316     }
317
318   /* Check if enough space for the block */
319   if (AddressSpace->LowestAddress < KERNEL_BASE)
320     {
321       if ((ULONG)Address >= KERNEL_BASE || Length > KERNEL_BASE - (ULONG)Address)
322         {
323           DPRINT("Failed to find gap\n");
324           return NULL;
325         }
326     }
327    else
328     {
329       if (Length >= 0xFFFFFFFF - (ULONG)Address)
330         {
331           DPRINT("Failed to find gap\n");
332           return NULL;
333         }
334     }
335
336   DPRINT("Found gap at %p\n", Address);
337   return Address;
338 }
339
340
341 PVOID MmFindGap(PMADDRESS_SPACE AddressSpace, ULONG Length, BOOL TopDown)
342 {
343   if (TopDown)
344     return MmFindGapTopDown(AddressSpace, Length);
345
346   return MmFindGapBottomUp(AddressSpace, Length);
347 }
348
349
350 NTSTATUS MmInitMemoryAreas(VOID)
351 /*
352  * FUNCTION: Initialize the memory area list
353  */
354 {
355    DPRINT("MmInitMemoryAreas()\n",0);
356    return(STATUS_SUCCESS);
357 }
358
359 NTSTATUS 
360 MmFreeMemoryArea(PMADDRESS_SPACE AddressSpace,
361                  PVOID BaseAddress,
362                  ULONG Length,
363                  VOID (*FreePage)(PVOID Context, MEMORY_AREA* MemoryArea, 
364                                   PVOID Address, PHYSICAL_ADDRESS PhysAddr, 
365                                   SWAPENTRY SwapEntry, BOOLEAN Dirty),
366                  PVOID FreePageContext)
367 {
368    MEMORY_AREA* MemoryArea;
369    ULONG i;
370    PEPROCESS CurrentProcess = PsGetCurrentProcess();
371    
372    DPRINT("MmFreeMemoryArea(AddressSpace %x, BaseAddress %x, Length %x,"
373            "FreePageContext %d)\n",AddressSpace,BaseAddress,Length,
374           FreePageContext);
375
376    MemoryArea = MmOpenMemoryAreaByAddress(AddressSpace,
377                                           BaseAddress);
378    if (MemoryArea == NULL)
379      {
380         KEBUGCHECK(0);
381         return(STATUS_UNSUCCESSFUL);
382      }
383    if (AddressSpace->Process != NULL && 
384        AddressSpace->Process != CurrentProcess)
385      {
386        KeAttachProcess(AddressSpace->Process);
387      }
388    for (i=0; i<(PAGE_ROUND_UP(MemoryArea->Length)/PAGE_SIZE); i++)
389      {
390        PHYSICAL_ADDRESS PhysAddr = (PHYSICAL_ADDRESS)0LL;
391        BOOL Dirty = FALSE;
392        SWAPENTRY SwapEntry = 0;
393
394        if (MmIsPageSwapEntry(AddressSpace->Process,
395                              MemoryArea->BaseAddress + (i * PAGE_SIZE)))
396          {
397            MmDeletePageFileMapping(AddressSpace->Process,
398                                    MemoryArea->BaseAddress + (i * PAGE_SIZE),
399                                    &SwapEntry);
400          }
401        else
402          {
403            MmDeleteVirtualMapping(AddressSpace->Process, 
404                                   MemoryArea->BaseAddress + (i*PAGE_SIZE),
405                                   FALSE, &Dirty, &PhysAddr);
406
407          }
408        if (FreePage != NULL)
409          {
410            FreePage(FreePageContext, MemoryArea,
411                     MemoryArea->BaseAddress + (i * PAGE_SIZE), PhysAddr, 
412                     SwapEntry, Dirty);
413          }
414      }
415    if (AddressSpace->Process != NULL &&
416        AddressSpace->Process != CurrentProcess)
417      {
418        KeDetachProcess();
419      }
420    RemoveEntryList(&MemoryArea->Entry);
421    ExFreePool(MemoryArea);
422    
423    DPRINT("MmFreeMemoryArea() succeeded\n");
424    
425    return(STATUS_SUCCESS);
426 }
427
428 PMEMORY_AREA MmSplitMemoryArea(PEPROCESS Process,
429                                PMADDRESS_SPACE AddressSpace,
430                                PMEMORY_AREA OriginalMemoryArea,
431                                PVOID BaseAddress,
432                                ULONG Length,
433                                ULONG NewType,
434                                ULONG NewAttributes)
435 {
436    PMEMORY_AREA Result;
437    PMEMORY_AREA Split;
438    
439    Result = ExAllocatePoolWithTag(NonPagedPool, sizeof(MEMORY_AREA), 
440                                   TAG_MAREA);
441    RtlZeroMemory(Result,sizeof(MEMORY_AREA));
442    Result->Type = NewType;
443    Result->BaseAddress = BaseAddress;
444    Result->Length = Length;
445    Result->Attributes = NewAttributes;
446    Result->LockCount = 0;
447    Result->Process = Process;
448    
449    if (BaseAddress == OriginalMemoryArea->BaseAddress)
450      {
451         OriginalMemoryArea->BaseAddress = BaseAddress + Length;
452         OriginalMemoryArea->Length = OriginalMemoryArea->Length - Length;
453         MmInsertMemoryArea(AddressSpace, Result);
454         return(Result);
455      }
456    if ((BaseAddress + Length) == 
457        (OriginalMemoryArea->BaseAddress + OriginalMemoryArea->Length))
458      {
459         OriginalMemoryArea->Length = OriginalMemoryArea->Length - Length; 
460         MmInsertMemoryArea(AddressSpace, Result);
461
462         return(Result);
463      }
464       
465    Split = ExAllocatePoolWithTag(NonPagedPool, sizeof(MEMORY_AREA),
466                                  TAG_MAREA);
467    RtlCopyMemory(Split,OriginalMemoryArea,sizeof(MEMORY_AREA));
468    Split->BaseAddress = BaseAddress + Length;
469    Split->Length = OriginalMemoryArea->Length - (((ULONG)BaseAddress) 
470                                                  + Length);
471    
472    OriginalMemoryArea->Length = BaseAddress - OriginalMemoryArea->BaseAddress;
473       
474    return(Split);
475 }
476
477 NTSTATUS MmCreateMemoryArea(PEPROCESS Process,
478                             PMADDRESS_SPACE AddressSpace,
479                             ULONG Type,
480                             PVOID* BaseAddress,
481                             ULONG Length,
482                             ULONG Attributes,
483                             MEMORY_AREA** Result,
484                             BOOL FixedAddress,
485                             BOOL TopDown)
486 /*
487  * FUNCTION: Create a memory area
488  * ARGUMENTS:
489  *     AddressSpace = Address space to create the area in
490  *     Type = Type of the address space
491  *     BaseAddress = 
492  *     Length = Length to allocate
493  *     Attributes = Protection attributes for the memory area
494  *     Result = Receives a pointer to the memory area on exit
495  * RETURNS: Status
496  * NOTES: Lock the address space before calling this function
497  */
498 {
499    ULONG tmpLength;
500    DPRINT("MmCreateMemoryArea(Type %d, BaseAddress %x,"
501            "*BaseAddress %x, Length %x, Attributes %x, Result %x)\n",
502            Type,BaseAddress,*BaseAddress,Length,Attributes,Result);
503
504    if ((*BaseAddress) == 0 && !FixedAddress)
505      {
506         tmpLength = PAGE_ROUND_UP(Length);
507         *BaseAddress = MmFindGap(AddressSpace,
508                                  PAGE_ROUND_UP(Length) +(PAGE_SIZE*2),
509                                  TopDown);
510         if ((*BaseAddress) == 0)
511           {
512              DPRINT("No suitable gap\n");
513              return(STATUS_NO_MEMORY);
514           }
515         (*BaseAddress)=(*BaseAddress)+PAGE_SIZE;
516      }
517    else
518      {
519         tmpLength = (ULONG)*BaseAddress + Length - PAGE_ROUND_DOWN((*BaseAddress));
520         (*BaseAddress) = (PVOID)PAGE_ROUND_DOWN((*BaseAddress));
521         if (MmOpenMemoryAreaByRegion(AddressSpace,
522                                      *BaseAddress,
523                                      tmpLength)!=NULL)
524           {
525              DPRINT("Memory area already occupied\n");
526              return(STATUS_CONFLICTING_ADDRESSES);
527           }
528      }
529    
530    *Result = ExAllocatePoolWithTag(NonPagedPool, sizeof(MEMORY_AREA),
531                                    TAG_MAREA);
532    RtlZeroMemory(*Result,sizeof(MEMORY_AREA));
533    (*Result)->Type = Type;
534    (*Result)->BaseAddress = *BaseAddress;
535    (*Result)->Length = tmpLength;
536    (*Result)->Attributes = Attributes;
537    (*Result)->LockCount = 0;
538    (*Result)->Process = Process;
539    (*Result)->PageOpCount = 0;
540    (*Result)->DeleteInProgress = FALSE;
541    
542    MmInsertMemoryArea(AddressSpace, *Result);
543    
544    DPRINT("MmCreateMemoryArea() succeeded\n");
545    return(STATUS_SUCCESS);
546 }