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 // Check if allocation failed 258 // 259 if (!PointerPte) 260 { 261 // 262 // Warn that we are out of memory 263 // 264 DPRINT1("MiReserveSystemPtes: Failed to reserve %lu PTE(s)!\n", NumberOfPtes); 265 } 266 267 // 268 // Return the PTE Pointer 269 // 270 return PointerPte; 271 } 272 273 VOID 274 NTAPI 275 MiReleaseSystemPtes(IN PMMPTE StartingPte, 276 IN ULONG NumberOfPtes, 277 IN MMSYSTEM_PTE_POOL_TYPE SystemPtePoolType) 278 { 279 KIRQL OldIrql; 280 ULONG ClusterSize; 281 PMMPTE PreviousPte, NextPte, InsertPte; 282 283 // 284 // Check to make sure the PTE address is within bounds 285 // 286 ASSERT(NumberOfPtes != 0); 287 ASSERT(StartingPte >= MmSystemPtesStart[SystemPtePoolType]); 288 ASSERT(StartingPte + NumberOfPtes - 1 <= MmSystemPtesEnd[SystemPtePoolType]); 289 290 // 291 // Zero PTEs 292 // 293 RtlZeroMemory(StartingPte, NumberOfPtes * sizeof(MMPTE)); 294 295 // 296 // Acquire the System PTE lock 297 // 298 OldIrql = KeAcquireQueuedSpinLock(LockQueueSystemSpaceLock); 299 300 // 301 // Increase availability 302 // 303 MmTotalFreeSystemPtes[SystemPtePoolType] += NumberOfPtes; 304 305 // 306 // Step through the cluster list to find where to insert the PTEs 307 // 308 PreviousPte = &MmFirstFreeSystemPte[SystemPtePoolType]; 309 InsertPte = NULL; 310 311 while (PreviousPte->u.List.NextEntry != MM_EMPTY_PTE_LIST) 312 { 313 // 314 // Get the next cluster and its size 315 // 316 NextPte = MmSystemPteBase + PreviousPte->u.List.NextEntry; 317 ClusterSize = MI_GET_CLUSTER_SIZE(NextPte); 318 319 // 320 // Check if this cluster is adjacent to the PTEs being released 321 // 322 if ((NextPte + ClusterSize == StartingPte) || 323 (StartingPte + NumberOfPtes == NextPte)) 324 { 325 // 326 // Add the PTEs in the cluster to the PTEs being released 327 // 328 NumberOfPtes += ClusterSize; 329 330 if (NextPte < StartingPte) 331 StartingPte = NextPte; 332 333 // 334 // Unlink this cluster and zero it 335 // 336 PreviousPte->u.List.NextEntry = NextPte->u.List.NextEntry; 337 338 if (NextPte->u.List.OneEntry == 0) 339 { 340 NextPte->u.Long = 0; 341 NextPte++; 342 } 343 NextPte->u.Long = 0; 344 345 // 346 // Invalidate the previously found insertion location, if any 347 // 348 InsertPte = NULL; 349 } 350 else 351 { 352 // 353 // Check if the insertion location is right before this cluster 354 // 355 if ((InsertPte == NULL) && (NumberOfPtes <= ClusterSize)) 356 InsertPte = PreviousPte; 357 358 // 359 // On to the next cluster 360 // 361 PreviousPte = NextPte; 362 } 363 } 364 365 // 366 // If no insertion location was found, use the tail of the list 367 // 368 if (InsertPte == NULL) 369 InsertPte = PreviousPte; 370 371 // 372 // Create a new cluster using the PTEs being released 373 // 374 if (NumberOfPtes != 1) 375 { 376 StartingPte->u.List.OneEntry = 0; 377 378 NextPte = StartingPte + 1; 379 NextPte->u.List.NextEntry = NumberOfPtes; 380 } 381 else 382 StartingPte->u.List.OneEntry = 1; 383 384 // 385 // Link the new cluster into the cluster list at the insertion location 386 // 387 StartingPte->u.List.NextEntry = InsertPte->u.List.NextEntry; 388 InsertPte->u.List.NextEntry = StartingPte - MmSystemPteBase; 389 390 // 391 // Release the System PTE lock 392 // 393 KeReleaseQueuedSpinLock(LockQueueSystemSpaceLock, OldIrql); 394 } 395 396 INIT_FUNCTION 397 VOID 398 NTAPI 399 MiInitializeSystemPtes(IN PMMPTE StartingPte, 400 IN ULONG NumberOfPtes, 401 IN MMSYSTEM_PTE_POOL_TYPE PoolType) 402 { 403 // 404 // Sanity checks 405 // 406 ASSERT(NumberOfPtes >= 1); 407 408 // 409 // Set the starting and ending PTE addresses for this space 410 // 411 MmSystemPteBase = MI_SYSTEM_PTE_BASE; 412 MmSystemPtesStart[PoolType] = StartingPte; 413 MmSystemPtesEnd[PoolType] = StartingPte + NumberOfPtes - 1; 414 DPRINT("System PTE space for %d starting at: %p and ending at: %p\n", 415 PoolType, MmSystemPtesStart[PoolType], MmSystemPtesEnd[PoolType]); 416 417 // 418 // Clear all the PTEs to start with 419 // 420 RtlZeroMemory(StartingPte, NumberOfPtes * sizeof(MMPTE)); 421 422 // 423 // Make the first entry free and link it 424 // 425 StartingPte->u.List.NextEntry = MM_EMPTY_PTE_LIST; 426 MmFirstFreeSystemPte[PoolType].u.Long = 0; 427 MmFirstFreeSystemPte[PoolType].u.List.NextEntry = StartingPte - 428 MmSystemPteBase; 429 430 // 431 // The second entry stores the size of this PTE space 432 // 433 StartingPte++; 434 StartingPte->u.Long = 0; 435 StartingPte->u.List.NextEntry = NumberOfPtes; 436 437 // 438 // We also keep a global for it 439 // 440 MmTotalFreeSystemPtes[PoolType] = NumberOfPtes; 441 442 // 443 // Check if this is the system PTE space 444 // 445 if (PoolType == SystemPteSpace) 446 { 447 // 448 // Remember how many PTEs we have 449 // 450 MmTotalSystemPtes = NumberOfPtes; 451 } 452 } 453 454 /* EOF */ 455