3 * Copyright (C) 1998, 1999, 2000, 2001, 2002 ReactOS Team
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.
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.
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.
21 * PROJECT: ReactOS kernel
22 * FILE: ntoskrnl/mm/region.c
23 * PROGRAMMER: David Welch
27 /* INCLUDE *****************************************************************/
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>
38 #include <internal/debug.h>
40 /* GLOBALS *******************************************************************/
42 #define TAG_MM_REGION TAG('M', 'R', 'G', 'N')
44 /* FUNCTIONS *****************************************************************/
47 InsertAfterEntry(PLIST_ENTRY Previous,
50 * FUNCTION: Insert a list entry after another entry in the list
53 Previous->Flink->Blink = Entry;
55 Entry->Flink = Previous->Flink;
56 Entry->Blink = Previous;
58 Previous->Flink = Entry;
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)
67 PMM_REGION NewRegion1;
68 PMM_REGION NewRegion2;
71 /* Allocate this in front otherwise the failure case is too difficult. */
72 NewRegion2 = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_REGION),
74 if (NewRegion2 == NULL)
79 /* Create the new region. */
80 NewRegion1 = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_REGION),
82 if (NewRegion1 == NULL)
84 ExFreePool(NewRegion2);
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);
96 * Call our helper function to do the changes on the addresses contained
97 * in the initial region.
99 AlterFunc(AddressSpace, StartAddress, InternalLength, InitialRegion->Type,
100 InitialRegion->Protect, NewType, NewProtect);
103 * If necessary create a new region for the portion of the initial region
104 * beyond the range of addresses to alter.
106 if ((InitialBaseAddress + InitialRegion->Length) > (StartAddress + Length))
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);
117 ExFreePool(NewRegion2);
120 /* Either remove or shrink the initial region. */
121 if (InitialBaseAddress == StartAddress)
123 RemoveEntryList(&InitialRegion->RegionListEntry);
124 ExFreePool(InitialRegion);
128 InitialRegion->Length = StartAddress - InitialBaseAddress;
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)
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;
148 * Find the first region containing part of the range of addresses to
151 InitialRegion = MmFindRegion(BaseAddress, RegionListHead, StartAddress,
152 &InitialBaseAddress);
153 if ((StartAddress + Length) >
154 (InitialBaseAddress + InitialRegion->Length))
156 RemainingLength = (StartAddress + Length) -
157 (InitialBaseAddress + InitialRegion->Length);
164 * If necessary then split the region into the affected and unaffected parts.
166 if (InitialRegion->Type != NewType || InitialRegion->Protect != NewProtect)
168 NewRegion = MmSplitRegion(InitialRegion, InitialBaseAddress,
169 StartAddress, Length, NewType, NewProtect,
170 AddressSpace, AlterFunc);
171 if (NewRegion == NULL)
173 return(STATUS_NO_MEMORY);
178 NewRegion = InitialRegion;
182 * Free any complete regions that are containing in the range of addresses
183 * and call the helper function to actually do the changes.
185 CurrentEntry = NewRegion->RegionListEntry.Flink;
186 CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION,
188 CurrentBaseAddress = StartAddress + NewRegion->Length;
189 while (RemainingLength > 0 && CurrentRegion->Length <= RemainingLength)
191 if (CurrentRegion->Type != NewType &&
192 CurrentRegion->Protect != NewProtect)
194 AlterFunc(AddressSpace, CurrentBaseAddress, CurrentRegion->Length,
195 CurrentRegion->Type, CurrentRegion->Protect,
196 NewType, NewProtect);
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,
209 * Split any final region.
211 if (RemainingLength > 0)
213 CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION,
215 if (CurrentRegion->Type != NewType &&
216 CurrentRegion->Protect != NewProtect)
218 AlterFunc(AddressSpace, CurrentBaseAddress, CurrentRegion->Length,
219 CurrentRegion->Type, CurrentRegion->Protect,
220 NewType, NewProtect);
222 NewRegion->Length += RemainingLength;
223 CurrentRegion->Length -= RemainingLength;
227 * If the region after the new region has the same type then merge them.
229 if (NewRegion->RegionListEntry.Flink != RegionListHead)
231 CurrentEntry = NewRegion->RegionListEntry.Flink;
232 CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION,
234 if (CurrentRegion->Type == NewRegion->Type &&
235 CurrentRegion->Protect == NewRegion->Protect)
237 NewRegion->Length += CurrentRegion->Length;
238 RemoveEntryList(&CurrentRegion->RegionListEntry);
239 ExFreePool(CurrentRegion);
244 * If the region before the new region has the same type then merge them.
246 if (NewRegion->RegionListEntry.Blink != RegionListHead)
248 CurrentEntry = NewRegion->RegionListEntry.Blink;
249 CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION,
251 if (CurrentRegion->Type == NewRegion->Type &&
252 CurrentRegion->Protect == NewRegion->Protect)
254 NewRegion->Length += CurrentRegion->Length;
255 RemoveEntryList(&CurrentRegion->RegionListEntry);
256 ExFreePool(CurrentRegion);
260 return(STATUS_SUCCESS);
264 MmInitialiseRegion(PLIST_ENTRY RegionListHead, ULONG Length, ULONG Type,
269 Region = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_REGION),
272 Region->Protect = Protect;
273 Region->Length = Length;
274 InitializeListHead(RegionListHead);
275 InsertHeadList(RegionListHead, &Region->RegionListEntry);
279 MmFindRegion(PVOID BaseAddress, PLIST_ENTRY RegionListHead, PVOID Address,
280 PVOID* RegionBaseAddress)
282 PLIST_ENTRY current_entry;
284 PVOID StartAddress = BaseAddress;
286 current_entry = RegionListHead->Flink;
287 while (current_entry != RegionListHead)
289 current = CONTAINING_RECORD(current_entry, MM_REGION, RegionListEntry);
291 if (StartAddress <= Address &&
292 (StartAddress + current->Length) > Address)
294 if (RegionBaseAddress != NULL)
296 *RegionBaseAddress = StartAddress;
301 current_entry = current_entry->Flink;
302 StartAddress += current->Length;