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