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.8 2004/06/13 14:05:53 navaraf 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
);
165 * If necessary then split the region into the affected and unaffected parts.
167 if (InitialRegion
->Type
!= NewType
|| InitialRegion
->Protect
!= NewProtect
)
169 NewRegion
= MmSplitRegion(InitialRegion
, InitialBaseAddress
,
170 StartAddress
, Length
, NewType
, NewProtect
,
171 AddressSpace
, AlterFunc
);
172 if (NewRegion
== NULL
)
174 return(STATUS_NO_MEMORY
);
179 NewRegion
= InitialRegion
;
183 * Free any complete regions that are containing in the range of addresses
184 * and call the helper function to actually do the changes.
186 CurrentEntry
= NewRegion
->RegionListEntry
.Flink
;
187 CurrentRegion
= CONTAINING_RECORD(CurrentEntry
, MM_REGION
,
189 CurrentBaseAddress
= (char*)StartAddress
+ NewRegion
->Length
;
190 while (RemainingLength
> 0 && CurrentRegion
->Length
<= RemainingLength
&&
191 CurrentEntry
!= RegionListHead
)
193 if (CurrentRegion
->Type
!= NewType
&&
194 CurrentRegion
->Protect
!= NewProtect
)
196 AlterFunc(AddressSpace
, CurrentBaseAddress
, CurrentRegion
->Length
,
197 CurrentRegion
->Type
, CurrentRegion
->Protect
,
198 NewType
, NewProtect
);
200 #if defined(__GNUC__)
201 CurrentBaseAddress
+= CurrentRegion
->Length
;
205 char* pTemp
= CurrentBaseAddress
;
206 pTemp
+= CurrentRegion
->Length
;
207 CurrentBaseAddress
= pTemp
;
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
,
220 * Split any final region.
222 if (RemainingLength
> 0 && CurrentEntry
!= RegionListHead
)
224 CurrentRegion
= CONTAINING_RECORD(CurrentEntry
, MM_REGION
,
226 if (CurrentRegion
->Type
!= NewType
&&
227 CurrentRegion
->Protect
!= NewProtect
)
229 AlterFunc(AddressSpace
, CurrentBaseAddress
, CurrentRegion
->Length
,
230 CurrentRegion
->Type
, CurrentRegion
->Protect
,
231 NewType
, NewProtect
);
233 NewRegion
->Length
+= RemainingLength
;
234 CurrentRegion
->Length
-= RemainingLength
;
238 * If the region after the new region has the same type then merge them.
240 if (NewRegion
->RegionListEntry
.Flink
!= RegionListHead
)
242 CurrentEntry
= NewRegion
->RegionListEntry
.Flink
;
243 CurrentRegion
= CONTAINING_RECORD(CurrentEntry
, MM_REGION
,
245 if (CurrentRegion
->Type
== NewRegion
->Type
&&
246 CurrentRegion
->Protect
== NewRegion
->Protect
)
248 NewRegion
->Length
+= CurrentRegion
->Length
;
249 RemoveEntryList(&CurrentRegion
->RegionListEntry
);
250 ExFreePool(CurrentRegion
);
255 * If the region before the new region has the same type then merge them.
257 if (NewRegion
->RegionListEntry
.Blink
!= RegionListHead
)
259 CurrentEntry
= NewRegion
->RegionListEntry
.Blink
;
260 CurrentRegion
= CONTAINING_RECORD(CurrentEntry
, MM_REGION
,
262 if (CurrentRegion
->Type
== NewRegion
->Type
&&
263 CurrentRegion
->Protect
== NewRegion
->Protect
)
265 NewRegion
->Length
+= CurrentRegion
->Length
;
266 RemoveEntryList(&CurrentRegion
->RegionListEntry
);
267 ExFreePool(CurrentRegion
);
271 return(STATUS_SUCCESS
);
275 MmInitialiseRegion(PLIST_ENTRY RegionListHead
, ULONG Length
, ULONG Type
,
280 Region
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MM_REGION
),
283 Region
->Protect
= Protect
;
284 Region
->Length
= Length
;
285 InitializeListHead(RegionListHead
);
286 InsertHeadList(RegionListHead
, &Region
->RegionListEntry
);
290 MmFindRegion(PVOID BaseAddress
, PLIST_ENTRY RegionListHead
, PVOID Address
,
291 PVOID
* RegionBaseAddress
)
293 PLIST_ENTRY current_entry
;
295 PVOID StartAddress
= BaseAddress
;
297 current_entry
= RegionListHead
->Flink
;
298 while (current_entry
!= RegionListHead
)
300 current
= CONTAINING_RECORD(current_entry
, MM_REGION
, RegionListEntry
);
302 if (StartAddress
<= Address
&&
303 ((char*)StartAddress
+ current
->Length
) > (char*)Address
)
305 if (RegionBaseAddress
!= NULL
)
307 *RegionBaseAddress
= StartAddress
;
312 current_entry
= current_entry
->Flink
;
313 #if defined(__GNUC__)
315 StartAddress
+= current
->Length
;
319 char* pTemp
= StartAddress
;
320 pTemp
+= current
->Length
;
321 StartAddress
= pTemp
;