update for HEAD-2003021201
[reactos.git] / ntoskrnl / mm / region.c
1 /*
2  *  ReactOS kernel
3  *  Copyright (C) 1998, 1999, 2000, 2001, 2002 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 /* $Id$
20  *
21  * PROJECT:     ReactOS kernel
22  * FILE:        ntoskrnl/mm/region.c
23  * PROGRAMMER:  David Welch
24  * PURPOSE:     
25  */
26  
27 /* INCLUDE *****************************************************************/
28
29 #include <ddk/ntddk.h>
30 #include <internal/mm.h>
31 #include <internal/ob.h>
32 #include <internal/io.h>
33 #include <internal/ps.h>
34 #include <internal/pool.h>
35 #include <ntos/minmax.h>
36
37 #define NDEBUG
38 #include <internal/debug.h>
39
40 /* GLOBALS *******************************************************************/
41
42 #define TAG_MM_REGION    TAG('M', 'R', 'G', 'N')
43
44 /* FUNCTIONS *****************************************************************/
45
46 VOID STATIC 
47 InsertAfterEntry(PLIST_ENTRY Previous,
48                  PLIST_ENTRY Entry)
49 /*
50  * FUNCTION: Insert a list entry after another entry in the list
51  */
52 {
53    Previous->Flink->Blink = Entry;
54    
55    Entry->Flink = Previous->Flink;
56    Entry->Blink = Previous;
57    
58    Previous->Flink = Entry;
59 }
60
61 PMM_REGION STATIC
62 MmSplitRegion(PMM_REGION InitialRegion, PVOID InitialBaseAddress,
63               PVOID StartAddress, ULONG Length, ULONG NewType,
64               ULONG NewProtect, PMADDRESS_SPACE AddressSpace,
65               PMM_ALTER_REGION_FUNC AlterFunc)
66 {
67   PMM_REGION NewRegion1; 
68   PMM_REGION NewRegion2;
69   ULONG InternalLength;
70    
71   /* Allocate this in front otherwise the failure case is too difficult. */
72   NewRegion2 = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_REGION),
73                                      TAG_MM_REGION);
74   if (NewRegion2 == NULL)
75     {
76       return(NULL);
77     }
78
79   /* Create the new region. */
80   NewRegion1 = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_REGION),
81                                      TAG_MM_REGION);
82   if (NewRegion1 == NULL)
83     {
84       ExFreePool(NewRegion2);
85       return(NULL);
86     }
87   NewRegion1->Type = NewType;
88   NewRegion1->Protect = NewProtect;
89   InternalLength = (InitialBaseAddress + InitialRegion->Length) - StartAddress;
90   InternalLength = min(InternalLength, Length);
91   NewRegion1->Length = InternalLength;
92   InsertAfterEntry(&InitialRegion->RegionListEntry,
93                    &NewRegion1->RegionListEntry);
94
95   /* 
96    * Call our helper function to do the changes on the addresses contained
97    * in the initial region.
98    */
99   AlterFunc(AddressSpace, StartAddress, InternalLength, InitialRegion->Type, 
100             InitialRegion->Protect, NewType, NewProtect);
101
102   /*
103    * If necessary create a new region for the portion of the initial region
104    * beyond the range of addresses to alter.
105    */
106   if ((InitialBaseAddress + InitialRegion->Length) > (StartAddress + Length))
107     {
108       NewRegion2->Type = InitialRegion->Type;
109       NewRegion2->Protect = InitialRegion->Protect;
110       NewRegion2->Length = (InitialBaseAddress + InitialRegion->Length) - 
111         (StartAddress + Length);
112       InsertAfterEntry(&NewRegion1->RegionListEntry, 
113                        &NewRegion2->RegionListEntry);           
114     }
115   else
116     {
117       ExFreePool(NewRegion2);
118     }
119
120   /* Either remove or shrink the initial region. */
121   if (InitialBaseAddress == StartAddress)
122     {
123       RemoveEntryList(&InitialRegion->RegionListEntry);
124       ExFreePool(InitialRegion);
125     }
126   else
127     {
128       InitialRegion->Length = StartAddress - InitialBaseAddress;
129     }
130   
131   return(NewRegion1);
132 }
133
134 NTSTATUS
135 MmAlterRegion(PMADDRESS_SPACE AddressSpace, PVOID BaseAddress, 
136               PLIST_ENTRY RegionListHead, PVOID StartAddress, ULONG Length, 
137               ULONG NewType, ULONG NewProtect, PMM_ALTER_REGION_FUNC AlterFunc)
138 {
139   PMM_REGION InitialRegion;
140   PVOID InitialBaseAddress;  
141   PMM_REGION NewRegion;
142   PLIST_ENTRY CurrentEntry;
143   PMM_REGION CurrentRegion = NULL;
144   PVOID CurrentBaseAddress;
145   ULONG RemainingLength;
146
147   /*
148    * Find the first region containing part of the range of addresses to
149    * be altered.
150    */
151   InitialRegion = MmFindRegion(BaseAddress, RegionListHead, StartAddress,
152                                &InitialBaseAddress);
153   if ((StartAddress + Length) >
154       (InitialBaseAddress + InitialRegion->Length))
155     {
156       RemainingLength = (StartAddress + Length) - 
157         (InitialBaseAddress + InitialRegion->Length);
158     }
159   else
160     {
161       RemainingLength = 0;
162     }
163   /*
164    * If necessary then split the region into the affected and unaffected parts.
165    */
166   if (InitialRegion->Type != NewType || InitialRegion->Protect != NewProtect)
167     {
168       NewRegion = MmSplitRegion(InitialRegion, InitialBaseAddress, 
169                                 StartAddress, Length, NewType, NewProtect, 
170                                 AddressSpace, AlterFunc);
171       if (NewRegion == NULL)
172         {
173           return(STATUS_NO_MEMORY);
174         }
175     }
176   else
177     {
178       NewRegion = InitialRegion;
179     }
180   
181   /*
182    * Free any complete regions that are containing in the range of addresses
183    * and call the helper function to actually do the changes.
184    */
185   CurrentEntry = NewRegion->RegionListEntry.Flink;
186   CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION, 
187                                     RegionListEntry);
188   CurrentBaseAddress = StartAddress + NewRegion->Length;
189   while (RemainingLength > 0 && CurrentRegion->Length <= RemainingLength)
190     {
191       if (CurrentRegion->Type != NewType &&
192           CurrentRegion->Protect != NewProtect)
193         {
194           AlterFunc(AddressSpace, CurrentBaseAddress, CurrentRegion->Length, 
195                     CurrentRegion->Type, CurrentRegion->Protect,
196                     NewType, NewProtect);
197         }
198       CurrentBaseAddress += CurrentRegion->Length;
199       NewRegion->Length += CurrentRegion->Length;
200       RemainingLength -= CurrentRegion->Length;
201       CurrentEntry = CurrentEntry->Flink;      
202       RemoveEntryList(&CurrentRegion->RegionListEntry);
203       ExFreePool(CurrentRegion);
204       CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION, 
205                                         RegionListEntry);
206     }
207
208   /*
209    * Split any final region.
210    */
211   if (RemainingLength > 0)
212     {
213       CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION, 
214                                         RegionListEntry);
215       if (CurrentRegion->Type != NewType &&
216           CurrentRegion->Protect != NewProtect)
217         {
218           AlterFunc(AddressSpace, CurrentBaseAddress, CurrentRegion->Length, 
219                     CurrentRegion->Type, CurrentRegion->Protect,
220                     NewType, NewProtect);
221         }
222       NewRegion->Length += RemainingLength;
223       CurrentRegion->Length -= RemainingLength;
224     }
225
226   /*
227    * If the region after the new region has the same type then merge them.
228    */
229   if (NewRegion->RegionListEntry.Flink != RegionListHead)
230     {
231       CurrentEntry = NewRegion->RegionListEntry.Flink;
232       CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION, 
233                                         RegionListEntry);
234       if (CurrentRegion->Type == NewRegion->Type &&
235           CurrentRegion->Protect == NewRegion->Protect)
236         {
237           NewRegion->Length += CurrentRegion->Length;
238           RemoveEntryList(&CurrentRegion->RegionListEntry);
239           ExFreePool(CurrentRegion);
240         }
241     }
242
243   /*
244    * If the region before the new region has the same type then merge them.
245    */
246   if (NewRegion->RegionListEntry.Blink != RegionListHead)
247     {
248       CurrentEntry = NewRegion->RegionListEntry.Blink;
249       CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION, 
250                                         RegionListEntry);
251       if (CurrentRegion->Type == NewRegion->Type &&
252           CurrentRegion->Protect == NewRegion->Protect)
253         {
254           NewRegion->Length += CurrentRegion->Length;
255           RemoveEntryList(&CurrentRegion->RegionListEntry);
256           ExFreePool(CurrentRegion);
257         }
258     }
259
260   return(STATUS_SUCCESS);
261 }
262
263 VOID
264 MmInitialiseRegion(PLIST_ENTRY RegionListHead, ULONG Length, ULONG Type,
265                    ULONG Protect)
266 {
267   PMM_REGION Region;
268
269   Region = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_REGION),
270                                  TAG_MM_REGION);
271   Region->Type = Type;
272   Region->Protect = Protect;
273   Region->Length = Length;
274   InitializeListHead(RegionListHead);
275   InsertHeadList(RegionListHead, &Region->RegionListEntry);
276 }
277
278 PMM_REGION
279 MmFindRegion(PVOID BaseAddress, PLIST_ENTRY RegionListHead, PVOID Address,
280              PVOID* RegionBaseAddress)
281 {
282   PLIST_ENTRY current_entry;
283   PMM_REGION current;
284   PVOID StartAddress = BaseAddress;
285   
286   current_entry = RegionListHead->Flink;
287   while (current_entry != RegionListHead)
288     {
289       current = CONTAINING_RECORD(current_entry, MM_REGION, RegionListEntry);
290
291       if (StartAddress <= Address && 
292           (StartAddress + current->Length) > Address)
293         {
294           if (RegionBaseAddress != NULL)
295             {
296               *RegionBaseAddress = StartAddress;
297             }
298           return(current);
299         }
300
301       current_entry = current_entry->Flink;
302       StartAddress += current->Length;
303     }
304   return(NULL);
305 }