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.
19 /* $Id: region.c,v 1.6 2003/12/30 18:52:05 fireball Exp $
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
= ((char*)InitialBaseAddress
+ InitialRegion
->Length
) - (char*)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 (((char*)InitialBaseAddress
+ InitialRegion
->Length
) > ((char*)StartAddress
+ Length
))
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
);
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
= (char*)StartAddress
- (char*)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 (((char*)StartAddress
+ Length
) >
154 ((char*)InitialBaseAddress
+ InitialRegion
->Length
))
156 RemainingLength
= ((char*)StartAddress
+ Length
) -
157 ((char*)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
= (char*)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 #if defined(__GNUC__)
199 CurrentBaseAddress
+= CurrentRegion
->Length
;
202 char* pTemp
= CurrentBaseAddress
;
203 pTemp
+= CurrentRegion
->Length
;
204 CurrentBaseAddress
= pTemp
;
207 NewRegion
->Length
+= CurrentRegion
->Length
;
208 RemainingLength
-= CurrentRegion
->Length
;
209 CurrentEntry
= CurrentEntry
->Flink
;
210 RemoveEntryList(&CurrentRegion
->RegionListEntry
);
211 ExFreePool(CurrentRegion
);
212 CurrentRegion
= CONTAINING_RECORD(CurrentEntry
, MM_REGION
,
217 * Split any final region.
219 if (RemainingLength
> 0)
221 CurrentRegion
= CONTAINING_RECORD(CurrentEntry
, MM_REGION
,
223 if (CurrentRegion
->Type
!= NewType
&&
224 CurrentRegion
->Protect
!= NewProtect
)
226 AlterFunc(AddressSpace
, CurrentBaseAddress
, CurrentRegion
->Length
,
227 CurrentRegion
->Type
, CurrentRegion
->Protect
,
228 NewType
, NewProtect
);
230 NewRegion
->Length
+= RemainingLength
;
231 CurrentRegion
->Length
-= RemainingLength
;
235 * If the region after the new region has the same type then merge them.
237 if (NewRegion
->RegionListEntry
.Flink
!= RegionListHead
)
239 CurrentEntry
= NewRegion
->RegionListEntry
.Flink
;
240 CurrentRegion
= CONTAINING_RECORD(CurrentEntry
, MM_REGION
,
242 if (CurrentRegion
->Type
== NewRegion
->Type
&&
243 CurrentRegion
->Protect
== NewRegion
->Protect
)
245 NewRegion
->Length
+= CurrentRegion
->Length
;
246 RemoveEntryList(&CurrentRegion
->RegionListEntry
);
247 ExFreePool(CurrentRegion
);
252 * If the region before the new region has the same type then merge them.
254 if (NewRegion
->RegionListEntry
.Blink
!= RegionListHead
)
256 CurrentEntry
= NewRegion
->RegionListEntry
.Blink
;
257 CurrentRegion
= CONTAINING_RECORD(CurrentEntry
, MM_REGION
,
259 if (CurrentRegion
->Type
== NewRegion
->Type
&&
260 CurrentRegion
->Protect
== NewRegion
->Protect
)
262 NewRegion
->Length
+= CurrentRegion
->Length
;
263 RemoveEntryList(&CurrentRegion
->RegionListEntry
);
264 ExFreePool(CurrentRegion
);
268 return(STATUS_SUCCESS
);
272 MmInitialiseRegion(PLIST_ENTRY RegionListHead
, ULONG Length
, ULONG Type
,
277 Region
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MM_REGION
),
280 Region
->Protect
= Protect
;
281 Region
->Length
= Length
;
282 InitializeListHead(RegionListHead
);
283 InsertHeadList(RegionListHead
, &Region
->RegionListEntry
);
287 MmFindRegion(PVOID BaseAddress
, PLIST_ENTRY RegionListHead
, PVOID Address
,
288 PVOID
* RegionBaseAddress
)
290 PLIST_ENTRY current_entry
;
292 PVOID StartAddress
= BaseAddress
;
294 current_entry
= RegionListHead
->Flink
;
295 while (current_entry
!= RegionListHead
)
297 current
= CONTAINING_RECORD(current_entry
, MM_REGION
, RegionListEntry
);
299 if (StartAddress
<= Address
&&
300 ((char*)StartAddress
+ current
->Length
) > (char*)Address
)
302 if (RegionBaseAddress
!= NULL
)
304 *RegionBaseAddress
= StartAddress
;
309 current_entry
= current_entry
->Flink
;
310 #if defined(__GNUC__)
311 StartAddress
+= current
->Length
;
314 char* pTemp
= StartAddress
;
315 pTemp
+= current
->Length
;
316 StartAddress
= pTemp
;