xref: /reactos/ntoskrnl/mm/region.c (revision 84ccccab)
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, SIZE_T Length, ULONG NewType,
36               ULONG NewProtect, PMMSUPPORT AddressSpace,
37               PMM_ALTER_REGION_FUNC AlterFunc)
38 {
39     PMM_REGION NewRegion1;
40     PMM_REGION NewRegion2;
41     SIZE_T 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         ExFreePoolWithTag(NewRegion2, TAG_MM_REGION);
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         ExFreePoolWithTag(NewRegion2, TAG_MM_REGION);
90     }
91 
92     /* Either remove or shrink the initial region. */
93     if (InitialBaseAddress == StartAddress)
94     {
95         RemoveEntryList(&InitialRegion->RegionListEntry);
96         ExFreePoolWithTag(InitialRegion, TAG_MM_REGION);
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, SIZE_T 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     SIZE_T 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     /*
127      * If necessary then split the region into the affected and unaffected parts.
128      */
129     if (InitialRegion->Type != NewType || InitialRegion->Protect != NewProtect)
130     {
131         NewRegion = MmSplitRegion(InitialRegion, InitialBaseAddress,
132                                   StartAddress, Length, NewType, NewProtect,
133                                   AddressSpace, AlterFunc);
134         if (NewRegion == NULL)
135         {
136             return(STATUS_NO_MEMORY);
137         }
138         if(NewRegion->Length < Length)
139             RemainingLength = Length - NewRegion->Length;
140         else
141             RemainingLength = 0;
142     }
143     else
144     {
145         NewRegion = InitialRegion;
146         if(((ULONG_PTR)InitialBaseAddress + NewRegion->Length) <
147                 ((ULONG_PTR)StartAddress + Length))
148             RemainingLength = ((ULONG_PTR)StartAddress + Length) - ((ULONG_PTR)InitialBaseAddress + NewRegion->Length);
149         else
150             RemainingLength = 0;
151     }
152 
153     /*
154      * Free any complete regions that are containing in the range of addresses
155      * and call the helper function to actually do the changes.
156      */
157     CurrentEntry = NewRegion->RegionListEntry.Flink;
158     CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION,
159                                       RegionListEntry);
160     CurrentBaseAddress = (char*)StartAddress + NewRegion->Length;
161     while (RemainingLength > 0 && CurrentRegion->Length <= RemainingLength &&
162             CurrentEntry != RegionListHead)
163     {
164         if (CurrentRegion->Type != NewType ||
165                 CurrentRegion->Protect != NewProtect)
166         {
167             AlterFunc(AddressSpace, CurrentBaseAddress, CurrentRegion->Length,
168                       CurrentRegion->Type, CurrentRegion->Protect,
169                       NewType, NewProtect);
170         }
171 
172         CurrentBaseAddress = (PVOID)((ULONG_PTR)CurrentBaseAddress + CurrentRegion->Length);
173         NewRegion->Length += CurrentRegion->Length;
174         RemainingLength -= CurrentRegion->Length;
175         CurrentEntry = CurrentEntry->Flink;
176         RemoveEntryList(&CurrentRegion->RegionListEntry);
177         ExFreePoolWithTag(CurrentRegion, TAG_MM_REGION);
178         CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION,
179                                           RegionListEntry);
180     }
181 
182     /*
183      * Split any final region.
184      */
185     if (RemainingLength > 0 && CurrentEntry != RegionListHead)
186     {
187         CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION,
188                                           RegionListEntry);
189         if (CurrentRegion->Type != NewType ||
190                 CurrentRegion->Protect != NewProtect)
191         {
192             AlterFunc(AddressSpace, CurrentBaseAddress, RemainingLength,
193                       CurrentRegion->Type, CurrentRegion->Protect,
194                       NewType, NewProtect);
195         }
196         NewRegion->Length += RemainingLength;
197         CurrentRegion->Length -= RemainingLength;
198     }
199 
200     /*
201      * If the region after the new region has the same type then merge them.
202      */
203     if (NewRegion->RegionListEntry.Flink != RegionListHead)
204     {
205         CurrentEntry = NewRegion->RegionListEntry.Flink;
206         CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION,
207                                           RegionListEntry);
208         if (CurrentRegion->Type == NewRegion->Type &&
209                 CurrentRegion->Protect == NewRegion->Protect)
210         {
211             NewRegion->Length += CurrentRegion->Length;
212             RemoveEntryList(&CurrentRegion->RegionListEntry);
213             ExFreePoolWithTag(CurrentRegion, TAG_MM_REGION);
214         }
215     }
216 
217     /*
218      * If the region before the new region has the same type then merge them.
219      */
220     if (NewRegion->RegionListEntry.Blink != RegionListHead)
221     {
222         CurrentEntry = NewRegion->RegionListEntry.Blink;
223         CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION,
224                                           RegionListEntry);
225         if (CurrentRegion->Type == NewRegion->Type &&
226                 CurrentRegion->Protect == NewRegion->Protect)
227         {
228             NewRegion->Length += CurrentRegion->Length;
229             RemoveEntryList(&CurrentRegion->RegionListEntry);
230             ExFreePoolWithTag(CurrentRegion, TAG_MM_REGION);
231         }
232     }
233 
234     return(STATUS_SUCCESS);
235 }
236 
237 VOID
238 NTAPI
239 MmInitializeRegion(PLIST_ENTRY RegionListHead, SIZE_T Length, ULONG Type,
240                    ULONG Protect)
241 {
242     PMM_REGION Region;
243 
244     Region = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_REGION),
245                                    TAG_MM_REGION);
246     if (!Region) return;
247 
248     Region->Type = Type;
249     Region->Protect = Protect;
250     Region->Length = Length;
251     InitializeListHead(RegionListHead);
252     InsertHeadList(RegionListHead, &Region->RegionListEntry);
253 }
254 
255 PMM_REGION
256 NTAPI
257 MmFindRegion(PVOID BaseAddress, PLIST_ENTRY RegionListHead, PVOID Address,
258              PVOID* RegionBaseAddress)
259 {
260     PLIST_ENTRY current_entry;
261     PMM_REGION current;
262     PVOID StartAddress = BaseAddress;
263 
264     current_entry = RegionListHead->Flink;
265     while (current_entry != RegionListHead)
266     {
267         current = CONTAINING_RECORD(current_entry, MM_REGION, RegionListEntry);
268 
269         if (StartAddress <= Address &&
270                 ((char*)StartAddress + current->Length) > (char*)Address)
271         {
272             if (RegionBaseAddress != NULL)
273             {
274                 *RegionBaseAddress = StartAddress;
275             }
276             return(current);
277         }
278 
279         current_entry = current_entry->Flink;
280 
281         StartAddress = (PVOID)((ULONG_PTR)StartAddress + current->Length);
282 
283     }
284     return(NULL);
285 }
286