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)
10 /* INCLUDES *******************************************************************/
16 #define MODULE_INVOLVED_IN_ARM3
17 #include <mm/ARM3/miarm.h>
19 /* GLOBALS ********************************************************************/
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
34 4, 4, 4, 4, 4, 4, 4, 4 // 16
36 LONG MmSysPteListBySizeCount
[5];
38 /* PRIVATE FUNCTIONS **********************************************************/
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.
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.
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.
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
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.
71 MI_GET_CLUSTER_SIZE(IN PMMPTE Pte
)
74 // First check for a single PTE
76 if (Pte
->u
.List
.OneEntry
)
80 // Then read the size from the trailing PTE
83 return (ULONG
)Pte
->u
.List
.NextEntry
;
88 MiReserveAlignedSystemPtes(IN ULONG NumberOfPtes
,
89 IN MMSYSTEM_PTE_POOL_TYPE SystemPtePoolType
,
93 PMMPTE PreviousPte
, NextPte
, ReturnPte
;
99 ASSERT(Alignment
<= PAGE_SIZE
);
102 // Acquire the System PTE lock
104 OldIrql
= KeAcquireQueuedSpinLock(LockQueueSystemSpaceLock
);
107 // Find the last cluster in the list that doesn't contain enough PTEs
109 PreviousPte
= &MmFirstFreeSystemPte
[SystemPtePoolType
];
111 while (PreviousPte
->u
.List
.NextEntry
!= MM_EMPTY_PTE_LIST
)
114 // Get the next cluster and its size
116 NextPte
= MmSystemPteBase
+ PreviousPte
->u
.List
.NextEntry
;
117 ClusterSize
= MI_GET_CLUSTER_SIZE(NextPte
);
120 // Check if this cluster contains enough PTEs
122 if (NumberOfPtes
<= ClusterSize
)
126 // On to the next cluster
128 PreviousPte
= NextPte
;
132 // Make sure we didn't reach the end of the cluster list
134 if (PreviousPte
->u
.List
.NextEntry
== MM_EMPTY_PTE_LIST
)
137 // Release the System PTE lock and return failure
139 KeReleaseQueuedSpinLock(LockQueueSystemSpaceLock
, OldIrql
);
144 // Unlink the cluster
146 PreviousPte
->u
.List
.NextEntry
= NextPte
->u
.List
.NextEntry
;
149 // Check if the reservation spans the whole cluster
151 if (ClusterSize
== NumberOfPtes
)
154 // Return the first PTE of this cluster
161 if (NextPte
->u
.List
.OneEntry
== 0)
171 // Divide the cluster into two parts
173 ClusterSize
-= NumberOfPtes
;
174 ReturnPte
= NextPte
+ ClusterSize
;
177 // Set the size of the first cluster, zero the second if needed
179 if (ClusterSize
== 1)
181 NextPte
->u
.List
.OneEntry
= 1;
182 ReturnPte
->u
.Long
= 0;
187 NextPte
->u
.List
.NextEntry
= ClusterSize
;
191 // Step through the cluster list to find out where to insert the first
193 PreviousPte
= &MmFirstFreeSystemPte
[SystemPtePoolType
];
195 while (PreviousPte
->u
.List
.NextEntry
!= MM_EMPTY_PTE_LIST
)
198 // Get the next cluster
200 NextPte
= MmSystemPteBase
+ PreviousPte
->u
.List
.NextEntry
;
203 // Check if the cluster to insert is smaller or of equal size
205 if (ClusterSize
<= MI_GET_CLUSTER_SIZE(NextPte
))
209 // On to the next cluster
211 PreviousPte
= NextPte
;
215 // Retrieve the first cluster and link it back into the cluster list
217 NextPte
= ReturnPte
- ClusterSize
;
219 NextPte
->u
.List
.NextEntry
= PreviousPte
->u
.List
.NextEntry
;
220 PreviousPte
->u
.List
.NextEntry
= NextPte
- MmSystemPteBase
;
224 // Decrease availability
226 MmTotalFreeSystemPtes
[SystemPtePoolType
] -= NumberOfPtes
;
229 // Release the System PTE lock
231 KeReleaseQueuedSpinLock(LockQueueSystemSpaceLock
, OldIrql
);
239 // Return the reserved PTEs
246 MiReserveSystemPtes(IN ULONG NumberOfPtes
,
247 IN MMSYSTEM_PTE_POOL_TYPE SystemPtePoolType
)
252 // Use the extended function
254 PointerPte
= MiReserveAlignedSystemPtes(NumberOfPtes
, SystemPtePoolType
, 0);
257 // Check if allocation failed
262 // Warn that we are out of memory
264 DPRINT1("MiReserveSystemPtes: Failed to reserve %lu PTE(s)!\n", NumberOfPtes
);
268 // Return the PTE Pointer
275 MiReleaseSystemPtes(IN PMMPTE StartingPte
,
276 IN ULONG NumberOfPtes
,
277 IN MMSYSTEM_PTE_POOL_TYPE SystemPtePoolType
)
281 PMMPTE PreviousPte
, NextPte
, InsertPte
;
284 // Check to make sure the PTE address is within bounds
286 ASSERT(NumberOfPtes
!= 0);
287 ASSERT(StartingPte
>= MmSystemPtesStart
[SystemPtePoolType
]);
288 ASSERT(StartingPte
+ NumberOfPtes
- 1 <= MmSystemPtesEnd
[SystemPtePoolType
]);
293 RtlZeroMemory(StartingPte
, NumberOfPtes
* sizeof(MMPTE
));
296 // Acquire the System PTE lock
298 OldIrql
= KeAcquireQueuedSpinLock(LockQueueSystemSpaceLock
);
301 // Increase availability
303 MmTotalFreeSystemPtes
[SystemPtePoolType
] += NumberOfPtes
;
306 // Step through the cluster list to find where to insert the PTEs
308 PreviousPte
= &MmFirstFreeSystemPte
[SystemPtePoolType
];
311 while (PreviousPte
->u
.List
.NextEntry
!= MM_EMPTY_PTE_LIST
)
314 // Get the next cluster and its size
316 NextPte
= MmSystemPteBase
+ PreviousPte
->u
.List
.NextEntry
;
317 ClusterSize
= MI_GET_CLUSTER_SIZE(NextPte
);
320 // Check if this cluster is adjacent to the PTEs being released
322 if ((NextPte
+ ClusterSize
== StartingPte
) ||
323 (StartingPte
+ NumberOfPtes
== NextPte
))
326 // Add the PTEs in the cluster to the PTEs being released
328 NumberOfPtes
+= ClusterSize
;
330 if (NextPte
< StartingPte
)
331 StartingPte
= NextPte
;
334 // Unlink this cluster and zero it
336 PreviousPte
->u
.List
.NextEntry
= NextPte
->u
.List
.NextEntry
;
338 if (NextPte
->u
.List
.OneEntry
== 0)
346 // Invalidate the previously found insertion location, if any
353 // Check if the insertion location is right before this cluster
355 if ((InsertPte
== NULL
) && (NumberOfPtes
<= ClusterSize
))
356 InsertPte
= PreviousPte
;
359 // On to the next cluster
361 PreviousPte
= NextPte
;
366 // If no insertion location was found, use the tail of the list
368 if (InsertPte
== NULL
)
369 InsertPte
= PreviousPte
;
372 // Create a new cluster using the PTEs being released
374 if (NumberOfPtes
!= 1)
376 StartingPte
->u
.List
.OneEntry
= 0;
378 NextPte
= StartingPte
+ 1;
379 NextPte
->u
.List
.NextEntry
= NumberOfPtes
;
382 StartingPte
->u
.List
.OneEntry
= 1;
385 // Link the new cluster into the cluster list at the insertion location
387 StartingPte
->u
.List
.NextEntry
= InsertPte
->u
.List
.NextEntry
;
388 InsertPte
->u
.List
.NextEntry
= StartingPte
- MmSystemPteBase
;
391 // Release the System PTE lock
393 KeReleaseQueuedSpinLock(LockQueueSystemSpaceLock
, OldIrql
);
399 MiInitializeSystemPtes(IN PMMPTE StartingPte
,
400 IN ULONG NumberOfPtes
,
401 IN MMSYSTEM_PTE_POOL_TYPE PoolType
)
406 ASSERT(NumberOfPtes
>= 1);
409 // Set the starting and ending PTE addresses for this space
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
]);
418 // Clear all the PTEs to start with
420 RtlZeroMemory(StartingPte
, NumberOfPtes
* sizeof(MMPTE
));
423 // Make the first entry free and link it
425 StartingPte
->u
.List
.NextEntry
= MM_EMPTY_PTE_LIST
;
426 MmFirstFreeSystemPte
[PoolType
].u
.Long
= 0;
427 MmFirstFreeSystemPte
[PoolType
].u
.List
.NextEntry
= StartingPte
-
431 // The second entry stores the size of this PTE space
434 StartingPte
->u
.Long
= 0;
435 StartingPte
->u
.List
.NextEntry
= NumberOfPtes
;
438 // We also keep a global for it
440 MmTotalFreeSystemPtes
[PoolType
] = NumberOfPtes
;
443 // Check if this is the system PTE space
445 if (PoolType
== SystemPteSpace
)
448 // Remember how many PTEs we have
450 MmTotalSystemPtes
= NumberOfPtes
;