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