branch update for HEAD-2003021201
[reactos.git] / 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    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 PVOID MmFindGap(PMADDRESS_SPACE AddressSpace, ULONG Length)
209 {
210    PLIST_ENTRY ListHead;
211    PLIST_ENTRY current_entry;
212    MEMORY_AREA* current;
213    MEMORY_AREA* next;
214    ULONG Gap;
215    PVOID Address;
216    
217    DPRINT("MmFindGap(Length %x)\n",Length);
218    
219    ListHead = &AddressSpace->MAreaListHead;
220      
221    current_entry = ListHead->Flink;
222    while (current_entry->Flink!=ListHead)
223      {
224         current = CONTAINING_RECORD(current_entry,MEMORY_AREA,Entry);
225         next = CONTAINING_RECORD(current_entry->Flink,MEMORY_AREA,Entry);
226         Gap = next->BaseAddress - (current->BaseAddress + PAGE_ROUND_UP(current->Length));
227         if (Gap >= Length)
228           {
229              return(current->BaseAddress + PAGE_ROUND_UP(current->Length));
230           }
231         current_entry = current_entry->Flink;
232      }
233    
234    if (current_entry == ListHead)
235      {
236         Address = (PVOID)AddressSpace->LowestAddress;
237      }
238    else
239      {
240         current = CONTAINING_RECORD(current_entry,MEMORY_AREA,Entry);
241         Address = current->BaseAddress + PAGE_ROUND_UP(current->Length);
242      }
243    /* Check if enough space for the block */
244    if (AddressSpace->LowestAddress < KERNEL_BASE)
245      {
246         if ((ULONG)Address >= KERNEL_BASE || Length > KERNEL_BASE - (ULONG)Address)
247           {
248              return NULL;
249           }
250      }
251    else
252      {
253         if (Length >= 0xFFFFFFFF - (ULONG)Address)
254           {
255              return NULL;
256           }
257      }
258    return Address;
259 }
260
261 NTSTATUS MmInitMemoryAreas(VOID)
262 /*
263  * FUNCTION: Initialize the memory area list
264  */
265 {
266    DPRINT("MmInitMemoryAreas()\n",0);
267    return(STATUS_SUCCESS);
268 }
269
270 NTSTATUS 
271 MmFreeMemoryArea(PMADDRESS_SPACE AddressSpace,
272                  PVOID BaseAddress,
273                  ULONG Length,
274                  VOID (*FreePage)(PVOID Context, MEMORY_AREA* MemoryArea, 
275                                   PVOID Address, PHYSICAL_ADDRESS PhysAddr, 
276                                   SWAPENTRY SwapEntry, BOOLEAN Dirty),
277                  PVOID FreePageContext)
278 {
279    MEMORY_AREA* MemoryArea;
280    ULONG i;
281    
282    DPRINT("MmFreeMemoryArea(AddressSpace %x, BaseAddress %x, Length %x,"
283            "FreePageContext %d)\n",AddressSpace,BaseAddress,Length,
284           FreePageContext);
285
286    MemoryArea = MmOpenMemoryAreaByAddress(AddressSpace,
287                                           BaseAddress);
288    if (MemoryArea == NULL)
289      {       
290         KeBugCheck(0);
291         return(STATUS_UNSUCCESSFUL);
292      }
293    for (i=0; i<(PAGE_ROUND_UP(MemoryArea->Length)/PAGE_SIZE); i++)
294      {
295        PHYSICAL_ADDRESS PhysAddr = (PHYSICAL_ADDRESS)0LL;
296        BOOL Dirty = FALSE;
297        SWAPENTRY SwapEntry = 0;
298
299        if (MmIsPageSwapEntry(AddressSpace->Process,
300                              MemoryArea->BaseAddress + (i * PAGE_SIZE)))
301          {
302            MmDeletePageFileMapping(AddressSpace->Process,
303                                    MemoryArea->BaseAddress + (i * PAGE_SIZE),
304                                    &SwapEntry);
305          }
306        else
307          {
308            MmDeleteVirtualMapping(AddressSpace->Process, 
309                                   MemoryArea->BaseAddress + (i*PAGE_SIZE),
310                                   FALSE, &Dirty, &PhysAddr);
311          }
312        if (FreePage != NULL)
313          {
314            FreePage(FreePageContext, MemoryArea,
315                     MemoryArea->BaseAddress + (i * PAGE_SIZE), PhysAddr, 
316                     SwapEntry, Dirty);
317          }
318      }
319    
320    RemoveEntryList(&MemoryArea->Entry);
321    ExFreePool(MemoryArea);
322    
323    DPRINT("MmFreeMemoryArea() succeeded\n");
324    
325    return(STATUS_SUCCESS);
326 }
327
328 PMEMORY_AREA MmSplitMemoryArea(PEPROCESS Process,
329                                PMADDRESS_SPACE AddressSpace,
330                                PMEMORY_AREA OriginalMemoryArea,
331                                PVOID BaseAddress,
332                                ULONG Length,
333                                ULONG NewType,
334                                ULONG NewAttributes)
335 {
336    PMEMORY_AREA Result;
337    PMEMORY_AREA Split;
338    
339    Result = ExAllocatePoolWithTag(NonPagedPool, sizeof(MEMORY_AREA), 
340                                   TAG_MAREA);
341    RtlZeroMemory(Result,sizeof(MEMORY_AREA));
342    Result->Type = NewType;
343    Result->BaseAddress = BaseAddress;
344    Result->Length = Length;
345    Result->Attributes = NewAttributes;
346    Result->LockCount = 0;
347    Result->Process = Process;
348    
349    if (BaseAddress == OriginalMemoryArea->BaseAddress)
350      {
351         OriginalMemoryArea->BaseAddress = BaseAddress + Length;
352         OriginalMemoryArea->Length = OriginalMemoryArea->Length - Length;
353         MmInsertMemoryArea(AddressSpace, Result);
354         return(Result);
355      }
356    if ((BaseAddress + Length) == 
357        (OriginalMemoryArea->BaseAddress + OriginalMemoryArea->Length))
358      {
359         OriginalMemoryArea->Length = OriginalMemoryArea->Length - Length; 
360         MmInsertMemoryArea(AddressSpace, Result);
361
362         return(Result);
363      }
364       
365    Split = ExAllocatePoolWithTag(NonPagedPool, sizeof(MEMORY_AREA),
366                                  TAG_MAREA);
367    RtlCopyMemory(Split,OriginalMemoryArea,sizeof(MEMORY_AREA));
368    Split->BaseAddress = BaseAddress + Length;
369    Split->Length = OriginalMemoryArea->Length - (((ULONG)BaseAddress) 
370                                                  + Length);
371    
372    OriginalMemoryArea->Length = BaseAddress - OriginalMemoryArea->BaseAddress;
373       
374    return(Split);
375 }
376
377 NTSTATUS MmCreateMemoryArea(PEPROCESS Process,
378                             PMADDRESS_SPACE AddressSpace,
379                             ULONG Type,
380                             PVOID* BaseAddress,
381                             ULONG Length,
382                             ULONG Attributes,
383                             MEMORY_AREA** Result,
384                             BOOL FixedAddress)
385 /*
386  * FUNCTION: Create a memory area
387  * ARGUMENTS:
388  *     AddressSpace = Address space to create the area in
389  *     Type = Type of the address space
390  *     BaseAddress = 
391  *     Length = Length to allocate
392  *     Attributes = Protection attributes for the memory area
393  *     Result = Receives a pointer to the memory area on exit
394  * RETURNS: Status
395  * NOTES: Lock the address space before calling this function
396  */
397 {
398    ULONG tmpLength;
399    DPRINT("MmCreateMemoryArea(Type %d, BaseAddress %x,"
400            "*BaseAddress %x, Length %x, Attributes %x, Result %x)\n",
401            Type,BaseAddress,*BaseAddress,Length,Attributes,Result);
402
403    if ((*BaseAddress)==0 && !FixedAddress)
404      {
405         tmpLength = PAGE_ROUND_UP(Length);
406         *BaseAddress = MmFindGap(AddressSpace,
407                                  PAGE_ROUND_UP(Length) +(PAGE_SIZE*2));
408         if ((*BaseAddress)==0)
409           {
410              DPRINT("No suitable gap\n");
411              return(STATUS_NO_MEMORY);
412           }
413         (*BaseAddress)=(*BaseAddress)+PAGE_SIZE;
414      }
415    else
416      { 
417         tmpLength = (ULONG)*BaseAddress + Length - PAGE_ROUND_DOWN((*BaseAddress));
418         (*BaseAddress) = (PVOID)PAGE_ROUND_DOWN((*BaseAddress));
419         if (MmOpenMemoryAreaByRegion(AddressSpace,
420                                      *BaseAddress,
421                                      tmpLength)!=NULL)
422           {
423              DPRINT("Memory area already occupied\n");
424              return(STATUS_CONFLICTING_ADDRESSES);
425           }
426      }
427    
428    *Result = ExAllocatePoolWithTag(NonPagedPool, sizeof(MEMORY_AREA),
429                                    TAG_MAREA);
430    RtlZeroMemory(*Result,sizeof(MEMORY_AREA));
431    (*Result)->Type = Type;
432    (*Result)->BaseAddress = *BaseAddress;
433    (*Result)->Length = tmpLength;
434    (*Result)->Attributes = Attributes;
435    (*Result)->LockCount = 0;
436    (*Result)->Process = Process;
437    (*Result)->PageOpCount = 0;
438    (*Result)->DeleteInProgress = FALSE;
439    
440    MmInsertMemoryArea(AddressSpace, *Result);
441    
442    DPRINT("MmCreateMemoryArea() succeeded\n");
443    return(STATUS_SUCCESS);
444 }