1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: BSD - See COPYING.ARM in the top level directory 4 * FILE: ntoskrnl/mm/ARM3/syspte.c 5 * PURPOSE: ARM Memory Manager System PTE Allocator 6 * PROGRAMMERS: ReactOS Portable Systems Group 7 * Roel Messiant (roel.messiant@reactos.org) 8 */ 9 10 /* INCLUDES *******************************************************************/ 11 12 #include <ntoskrnl.h> 13 #define NDEBUG 14 #include <debug.h> 15 16 #define MODULE_INVOLVED_IN_ARM3 17 #include <mm/ARM3/miarm.h> 18 19 /* GLOBALS ********************************************************************/ 20 21 PMMPTE MmSystemPteBase; 22 PMMPTE MmSystemPtesStart[MaximumPtePoolTypes]; 23 PMMPTE MmSystemPtesEnd[MaximumPtePoolTypes]; 24 MMPTE MmFirstFreeSystemPte[MaximumPtePoolTypes]; 25 ULONG MmTotalFreeSystemPtes[MaximumPtePoolTypes]; 26 ULONG MmTotalSystemPtes; 27 ULONG MiNumberOfExtraSystemPdes; 28 const ULONG MmSysPteIndex[5] = { 1, 2, 4, 8, 16 }; 29 const UCHAR MmSysPteTables[] = { 0, // 1 30 0, // 1 31 1, // 2 32 2, 2, // 4 33 3, 3, 3, 3, // 8 34 4, 4, 4, 4, 4, 4, 4, 4 // 16 35 }; 36 LONG MmSysPteListBySizeCount[5]; 37 38 /* PRIVATE FUNCTIONS **********************************************************/ 39 40 // 41 // The free System Page Table Entries are stored in a bunch of clusters, 42 // each consisting of one or more PTEs. These PTE clusters are connected 43 // in a singly linked list, ordered by increasing cluster size. 44 // 45 // A cluster consisting of a single PTE is marked by having the OneEntry flag 46 // of its PTE set. The forward link is contained in the NextEntry field. 47 // 48 // Clusters containing multiple PTEs have the OneEntry flag of their first PTE 49 // reset. The NextEntry field of the first PTE contains the forward link, and 50 // the size of the cluster is stored in the NextEntry field of its second PTE. 51 // 52 // Reserving PTEs currently happens by walking the linked list until a cluster 53 // is found that contains the requested amount of PTEs or more. This cluster 54 // is removed from the list, and the requested amount of PTEs is taken from the 55 // tail of this cluster. If any PTEs remain in the cluster, the linked list is 56 // walked again until a second cluster is found that contains the same amount 57 // of PTEs or more. The first cluster is then inserted in front of the second 58 // one. 59 // 60 // Releasing PTEs currently happens by walking the whole linked list, recording 61 // the first cluster that contains the amount of PTEs to release or more. When 62 // a cluster is found that is adjacent to the PTEs being released, this cluster 63 // is removed from the list and subsequently added to the PTEs being released. 64 // This ensures no two clusters are adjacent, which maximizes their size. 65 // After the walk is complete, a new cluster is created that contains the PTEs 66 // being released, which is then inserted in front of the recorded cluster. 67 // 68 69 FORCEINLINE 70 ULONG 71 MI_GET_CLUSTER_SIZE(IN PMMPTE Pte) 72 { 73 // 74 // First check for a single PTE 75 // 76 if (Pte->u.List.OneEntry) 77 return 1; 78 79 // 80 // Then read the size from the trailing PTE 81 // 82 Pte++; 83 return (ULONG)Pte->u.List.NextEntry; 84 } 85 86 PMMPTE 87 NTAPI 88 MiReserveAlignedSystemPtes(IN ULONG NumberOfPtes, 89 IN MMSYSTEM_PTE_POOL_TYPE SystemPtePoolType, 90 IN ULONG Alignment) 91 { 92 KIRQL OldIrql; 93 PMMPTE PreviousPte, NextPte, ReturnPte; 94 ULONG ClusterSize; 95 96 // 97 // Sanity check 98 // 99 ASSERT(Alignment <= PAGE_SIZE); 100 101 // 102 // Acquire the System PTE lock 103 // 104 OldIrql = KeAcquireQueuedSpinLock(LockQueueSystemSpaceLock); 105 106 // 107 // Find the last cluster in the list that doesn't contain enough PTEs 108 // 109 PreviousPte = &MmFirstFreeSystemPte[SystemPtePoolType]; 110 111 while (PreviousPte->u.List.NextEntry != MM_EMPTY_PTE_LIST) 112 { 113 // 114 // Get the next cluster and its size 115 // 116 NextPte = MmSystemPteBase + PreviousPte->u.List.NextEntry; 117 ClusterSize = MI_GET_CLUSTER_SIZE(NextPte); 118 119 // 120 // Check if this cluster contains enough PTEs 121 // 122 if (NumberOfPtes <= ClusterSize) 123 break; 124 125 // 126 // On to the next cluster 127 // 128 PreviousPte = NextPte; 129 } 130 131 // 132 // Make sure we didn't reach the end of the cluster list 133 // 134 if (PreviousPte->u.List.NextEntry == MM_EMPTY_PTE_LIST) 135 { 136 // 137 // Release the System PTE lock and return failure 138 // 139 KeReleaseQueuedSpinLock(LockQueueSystemSpaceLock, OldIrql); 140 return NULL; 141 } 142 143 // 144 // Unlink the cluster 145 // 146 PreviousPte->u.List.NextEntry = NextPte->u.List.NextEntry; 147 148 // 149 // Check if the reservation spans the whole cluster 150 // 151 if (ClusterSize == NumberOfPtes) 152 { 153 // 154 // Return the first PTE of this cluster 155 // 156 ReturnPte = NextPte; 157 158 // 159 // Zero the cluster 160 // 161 if (NextPte->u.List.OneEntry == 0) 162 { 163 NextPte->u.Long = 0; 164 NextPte++; 165 } 166 NextPte->u.Long = 0; 167 } 168 else 169 { 170 // 171 // Divide the cluster into two parts 172 // 173 ClusterSize -= NumberOfPtes; 174 ReturnPte = NextPte + ClusterSize; 175 176 // 177 // Set the size of the first cluster, zero the second if needed 178 // 179 if (ClusterSize == 1) 180 { 181 NextPte->u.List.OneEntry = 1; 182 ReturnPte->u.Long = 0; 183 } 184 else 185 { 186 NextPte++; 187 NextPte->u.List.NextEntry = ClusterSize; 188 } 189 190 // 191 // Step through the cluster list to find out where to insert the first 192 // 193 PreviousPte = &MmFirstFreeSystemPte[SystemPtePoolType]; 194 195 while (PreviousPte->u.List.NextEntry != MM_EMPTY_PTE_LIST) 196 { 197 // 198 // Get the next cluster 199 // 200 NextPte = MmSystemPteBase + PreviousPte->u.List.NextEntry; 201 202 // 203 // Check if the cluster to insert is smaller or of equal size 204 // 205 if (ClusterSize <= MI_GET_CLUSTER_SIZE(NextPte)) 206 break; 207 208 // 209 // On to the next cluster 210 // 211 PreviousPte = NextPte; 212 } 213 214 // 215 // Retrieve the first cluster and link it back into the cluster list 216 // 217 NextPte = ReturnPte - ClusterSize; 218 219 NextPte->u.List.NextEntry = PreviousPte->u.List.NextEntry; 220 PreviousPte->u.List.NextEntry = NextPte - MmSystemPteBase; 221 } 222 223 // 224 // Decrease availability 225 // 226 MmTotalFreeSystemPtes[SystemPtePoolType] -= NumberOfPtes; 227 228 // 229 // Release the System PTE lock 230 // 231 KeReleaseQueuedSpinLock(LockQueueSystemSpaceLock, OldIrql); 232 233 // 234 // Flush the TLB 235 // 236 KeFlushProcessTb(); 237 238 // 239 // Return the reserved PTEs 240 // 241 return ReturnPte; 242 } 243 244 PMMPTE 245 NTAPI 246 MiReserveSystemPtes(IN ULONG NumberOfPtes, 247 IN MMSYSTEM_PTE_POOL_TYPE SystemPtePoolType) 248 { 249 PMMPTE PointerPte; 250 251 // 252 // Use the extended function 253 // 254 PointerPte = MiReserveAlignedSystemPtes(NumberOfPtes, SystemPtePoolType, 0); 255 256 // 257 // Return the PTE Pointer 258 // 259 return PointerPte; 260 } 261 262 VOID 263 NTAPI 264 MiReleaseSystemPtes(IN PMMPTE StartingPte, 265 IN ULONG NumberOfPtes, 266 IN MMSYSTEM_PTE_POOL_TYPE SystemPtePoolType) 267 { 268 KIRQL OldIrql; 269 ULONG ClusterSize; 270 PMMPTE PreviousPte, NextPte, InsertPte; 271 272 // 273 // Check to make sure the PTE address is within bounds 274 // 275 ASSERT(NumberOfPtes != 0); 276 ASSERT(StartingPte >= MmSystemPtesStart[SystemPtePoolType]); 277 ASSERT(StartingPte + NumberOfPtes - 1 <= MmSystemPtesEnd[SystemPtePoolType]); 278 279 // 280 // Zero PTEs 281 // 282 RtlZeroMemory(StartingPte, NumberOfPtes * sizeof(MMPTE)); 283 284 // 285 // Acquire the System PTE lock 286 // 287 OldIrql = KeAcquireQueuedSpinLock(LockQueueSystemSpaceLock); 288 289 // 290 // Increase availability 291 // 292 MmTotalFreeSystemPtes[SystemPtePoolType] += NumberOfPtes; 293 294 // 295 // Step through the cluster list to find where to insert the PTEs 296 // 297 PreviousPte = &MmFirstFreeSystemPte[SystemPtePoolType]; 298 InsertPte = NULL; 299 300 while (PreviousPte->u.List.NextEntry != MM_EMPTY_PTE_LIST) 301 { 302 // 303 // Get the next cluster and its size 304 // 305 NextPte = MmSystemPteBase + PreviousPte->u.List.NextEntry; 306 ClusterSize = MI_GET_CLUSTER_SIZE(NextPte); 307 308 // 309 // Check if this cluster is adjacent to the PTEs being released 310 // 311 if ((NextPte + ClusterSize == StartingPte) || 312 (StartingPte + NumberOfPtes == NextPte)) 313 { 314 // 315 // Add the PTEs in the cluster to the PTEs being released 316 // 317 NumberOfPtes += ClusterSize; 318 319 if (NextPte < StartingPte) 320 StartingPte = NextPte; 321 322 // 323 // Unlink this cluster and zero it 324 // 325 PreviousPte->u.List.NextEntry = NextPte->u.List.NextEntry; 326 327 if (NextPte->u.List.OneEntry == 0) 328 { 329 NextPte->u.Long = 0; 330 NextPte++; 331 } 332 NextPte->u.Long = 0; 333 334 // 335 // Invalidate the previously found insertion location, if any 336 // 337 InsertPte = NULL; 338 } 339 else 340 { 341 // 342 // Check if the insertion location is right before this cluster 343 // 344 if ((InsertPte == NULL) && (NumberOfPtes <= ClusterSize)) 345 InsertPte = PreviousPte; 346 347 // 348 // On to the next cluster 349 // 350 PreviousPte = NextPte; 351 } 352 } 353 354 // 355 // If no insertion location was found, use the tail of the list 356 // 357 if (InsertPte == NULL) 358 InsertPte = PreviousPte; 359 360 // 361 // Create a new cluster using the PTEs being released 362 // 363 if (NumberOfPtes != 1) 364 { 365 StartingPte->u.List.OneEntry = 0; 366 367 NextPte = StartingPte + 1; 368 NextPte->u.List.NextEntry = NumberOfPtes; 369 } 370 else 371 StartingPte->u.List.OneEntry = 1; 372 373 // 374 // Link the new cluster into the cluster list at the insertion location 375 // 376 StartingPte->u.List.NextEntry = InsertPte->u.List.NextEntry; 377 InsertPte->u.List.NextEntry = StartingPte - MmSystemPteBase; 378 379 // 380 // Release the System PTE lock 381 // 382 KeReleaseQueuedSpinLock(LockQueueSystemSpaceLock, OldIrql); 383 } 384 385 CODE_SEG("INIT") 386 VOID 387 NTAPI 388 MiInitializeSystemPtes(IN PMMPTE StartingPte, 389 IN ULONG NumberOfPtes, 390 IN MMSYSTEM_PTE_POOL_TYPE PoolType) 391 { 392 // 393 // Sanity checks 394 // 395 ASSERT(NumberOfPtes >= 1); 396 397 // 398 // Set the starting and ending PTE addresses for this space 399 // 400 MmSystemPteBase = MI_SYSTEM_PTE_BASE; 401 MmSystemPtesStart[PoolType] = StartingPte; 402 MmSystemPtesEnd[PoolType] = StartingPte + NumberOfPtes - 1; 403 DPRINT("System PTE space for %d starting at: %p and ending at: %p\n", 404 PoolType, MmSystemPtesStart[PoolType], MmSystemPtesEnd[PoolType]); 405 406 // 407 // Clear all the PTEs to start with 408 // 409 RtlZeroMemory(StartingPte, NumberOfPtes * sizeof(MMPTE)); 410 411 // 412 // Make the first entry free and link it 413 // 414 StartingPte->u.List.NextEntry = MM_EMPTY_PTE_LIST; 415 MmFirstFreeSystemPte[PoolType].u.Long = 0; 416 MmFirstFreeSystemPte[PoolType].u.List.NextEntry = StartingPte - 417 MmSystemPteBase; 418 419 // 420 // The second entry stores the size of this PTE space 421 // 422 StartingPte++; 423 StartingPte->u.Long = 0; 424 StartingPte->u.List.NextEntry = NumberOfPtes; 425 426 // 427 // We also keep a global for it 428 // 429 MmTotalFreeSystemPtes[PoolType] = NumberOfPtes; 430 431 // 432 // Check if this is the system PTE space 433 // 434 if (PoolType == SystemPteSpace) 435 { 436 // 437 // Remember how many PTEs we have 438 // 439 MmTotalSystemPtes = NumberOfPtes; 440 } 441 } 442 443 /* EOF */ 444