f27cb55c122a5bfe82d5b1fc9c1edbd69169a43c
[reactos.git] / reactos / 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: region.c,v 1.8 2004/06/13 14:05:53 navaraf Exp $
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 = ((char*)InitialBaseAddress + InitialRegion->Length) - (char*)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 (((char*)InitialBaseAddress + InitialRegion->Length) > ((char*)StartAddress + Length))
107 {
108 NewRegion2->Type = InitialRegion->Type;
109 NewRegion2->Protect = InitialRegion->Protect;
110 NewRegion2->Length = ((char*)InitialBaseAddress + InitialRegion->Length) -
111 ((char*)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 = (char*)StartAddress - (char*)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 (((char*)StartAddress + Length) >
154 ((char*)InitialBaseAddress + InitialRegion->Length))
155 {
156 RemainingLength = ((char*)StartAddress + Length) -
157 ((char*)InitialBaseAddress + InitialRegion->Length);
158 }
159 else
160 {
161 RemainingLength = 0;
162 }
163
164 /*
165 * If necessary then split the region into the affected and unaffected parts.
166 */
167 if (InitialRegion->Type != NewType || InitialRegion->Protect != NewProtect)
168 {
169 NewRegion = MmSplitRegion(InitialRegion, InitialBaseAddress,
170 StartAddress, Length, NewType, NewProtect,
171 AddressSpace, AlterFunc);
172 if (NewRegion == NULL)
173 {
174 return(STATUS_NO_MEMORY);
175 }
176 }
177 else
178 {
179 NewRegion = InitialRegion;
180 }
181
182 /*
183 * Free any complete regions that are containing in the range of addresses
184 * and call the helper function to actually do the changes.
185 */
186 CurrentEntry = NewRegion->RegionListEntry.Flink;
187 CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION,
188 RegionListEntry);
189 CurrentBaseAddress = (char*)StartAddress + NewRegion->Length;
190 while (RemainingLength > 0 && CurrentRegion->Length <= RemainingLength &&
191 CurrentEntry != RegionListHead)
192 {
193 if (CurrentRegion->Type != NewType &&
194 CurrentRegion->Protect != NewProtect)
195 {
196 AlterFunc(AddressSpace, CurrentBaseAddress, CurrentRegion->Length,
197 CurrentRegion->Type, CurrentRegion->Protect,
198 NewType, NewProtect);
199 }
200 #if defined(__GNUC__)
201 CurrentBaseAddress += CurrentRegion->Length;
202 #else
203
204 {
205 char* pTemp = CurrentBaseAddress;
206 pTemp += CurrentRegion->Length;
207 CurrentBaseAddress = pTemp;
208 }
209 #endif
210 NewRegion->Length += CurrentRegion->Length;
211 RemainingLength -= CurrentRegion->Length;
212 CurrentEntry = CurrentEntry->Flink;
213 RemoveEntryList(&CurrentRegion->RegionListEntry);
214 ExFreePool(CurrentRegion);
215 CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION,
216 RegionListEntry);
217 }
218
219 /*
220 * Split any final region.
221 */
222 if (RemainingLength > 0 && CurrentEntry != RegionListHead)
223 {
224 CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION,
225 RegionListEntry);
226 if (CurrentRegion->Type != NewType &&
227 CurrentRegion->Protect != NewProtect)
228 {
229 AlterFunc(AddressSpace, CurrentBaseAddress, CurrentRegion->Length,
230 CurrentRegion->Type, CurrentRegion->Protect,
231 NewType, NewProtect);
232 }
233 NewRegion->Length += RemainingLength;
234 CurrentRegion->Length -= RemainingLength;
235 }
236
237 /*
238 * If the region after the new region has the same type then merge them.
239 */
240 if (NewRegion->RegionListEntry.Flink != RegionListHead)
241 {
242 CurrentEntry = NewRegion->RegionListEntry.Flink;
243 CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION,
244 RegionListEntry);
245 if (CurrentRegion->Type == NewRegion->Type &&
246 CurrentRegion->Protect == NewRegion->Protect)
247 {
248 NewRegion->Length += CurrentRegion->Length;
249 RemoveEntryList(&CurrentRegion->RegionListEntry);
250 ExFreePool(CurrentRegion);
251 }
252 }
253
254 /*
255 * If the region before the new region has the same type then merge them.
256 */
257 if (NewRegion->RegionListEntry.Blink != RegionListHead)
258 {
259 CurrentEntry = NewRegion->RegionListEntry.Blink;
260 CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION,
261 RegionListEntry);
262 if (CurrentRegion->Type == NewRegion->Type &&
263 CurrentRegion->Protect == NewRegion->Protect)
264 {
265 NewRegion->Length += CurrentRegion->Length;
266 RemoveEntryList(&CurrentRegion->RegionListEntry);
267 ExFreePool(CurrentRegion);
268 }
269 }
270
271 return(STATUS_SUCCESS);
272 }
273
274 VOID
275 MmInitialiseRegion(PLIST_ENTRY RegionListHead, ULONG Length, ULONG Type,
276 ULONG Protect)
277 {
278 PMM_REGION Region;
279
280 Region = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_REGION),
281 TAG_MM_REGION);
282 Region->Type = Type;
283 Region->Protect = Protect;
284 Region->Length = Length;
285 InitializeListHead(RegionListHead);
286 InsertHeadList(RegionListHead, &Region->RegionListEntry);
287 }
288
289 PMM_REGION
290 MmFindRegion(PVOID BaseAddress, PLIST_ENTRY RegionListHead, PVOID Address,
291 PVOID* RegionBaseAddress)
292 {
293 PLIST_ENTRY current_entry;
294 PMM_REGION current;
295 PVOID StartAddress = BaseAddress;
296
297 current_entry = RegionListHead->Flink;
298 while (current_entry != RegionListHead)
299 {
300 current = CONTAINING_RECORD(current_entry, MM_REGION, RegionListEntry);
301
302 if (StartAddress <= Address &&
303 ((char*)StartAddress + current->Length) > (char*)Address)
304 {
305 if (RegionBaseAddress != NULL)
306 {
307 *RegionBaseAddress = StartAddress;
308 }
309 return(current);
310 }
311
312 current_entry = current_entry->Flink;
313 #if defined(__GNUC__)
314
315 StartAddress += current->Length;
316 #else
317
318 {
319 char* pTemp = StartAddress;
320 pTemp += current->Length;
321 StartAddress = pTemp;
322 }
323 #endif
324
325 }
326 return(NULL);
327 }