Sync with trunk 48067
[reactos.git] / ntoskrnl / mm / ARM3 / syspte.c
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 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 #line 15 "ARMĀ³::SYSPTE"
16 #define MODULE_INVOLVED_IN_ARM3
17 #include "../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
28 /* PRIVATE FUNCTIONS **********************************************************/
29
30 PMMPTE
31 NTAPI
32 MiReserveAlignedSystemPtes(IN ULONG NumberOfPtes,
33 IN MMSYSTEM_PTE_POOL_TYPE SystemPtePoolType,
34 IN ULONG Alignment)
35 {
36 KIRQL OldIrql;
37 PMMPTE PointerPte, NextPte, PreviousPte;
38 ULONG_PTR ClusterSize;
39
40 //
41 // Sanity check
42 //
43 ASSERT(Alignment <= PAGE_SIZE);
44
45 //
46 // Lock the system PTE space
47 //
48 OldIrql = KeAcquireQueuedSpinLock(LockQueueSystemSpaceLock);
49
50 //
51 // Get the first free cluster and make sure we have PTEs available
52 //
53 PointerPte = &MmFirstFreeSystemPte[SystemPtePoolType];
54 if (PointerPte->u.List.NextEntry == ((ULONG)0xFFFFF))
55 {
56 //
57 // Fail
58 //
59 KeReleaseQueuedSpinLock(LockQueueSystemSpaceLock, OldIrql);
60 return NULL;
61 }
62
63 //
64 // Now move to the first free system PTE cluster
65 //
66 PreviousPte = PointerPte;
67 PointerPte = MmSystemPteBase + PointerPte->u.List.NextEntry;
68
69 //
70 // Loop each cluster
71 //
72 while (TRUE)
73 {
74 //
75 // Check if we're done to only one PTE left
76 //
77 if (!PointerPte->u.List.OneEntry)
78 {
79 //
80 // Keep track of the next cluster in case we have to relink
81 //
82 NextPte = PointerPte + 1;
83
84 //
85 // Can this cluster satisfy the request?
86 //
87 ClusterSize = (ULONG_PTR)NextPte->u.List.NextEntry;
88 if (NumberOfPtes < ClusterSize)
89 {
90 //
91 // It can, and it will leave just one PTE left
92 //
93 if ((ClusterSize - NumberOfPtes) == 1)
94 {
95 //
96 // This cluster becomes a single system PTE entry
97 //
98 PointerPte->u.List.OneEntry = 1;
99 }
100 else
101 {
102 //
103 // Otherwise, the next cluster aborbs what's left
104 //
105 NextPte->u.List.NextEntry = ClusterSize - NumberOfPtes;
106 }
107
108 //
109 // Decrement the free count and move to the next starting PTE
110 //
111 MmTotalFreeSystemPtes[SystemPtePoolType] -= NumberOfPtes;
112 PointerPte += (ClusterSize - NumberOfPtes);
113 break;
114 }
115
116 //
117 // Did we find exactly what you wanted?
118 //
119 if (NumberOfPtes == ClusterSize)
120 {
121 //
122 // Yes, fixup the cluster and decrease free system PTE count
123 //
124 PreviousPte->u.List.NextEntry = PointerPte->u.List.NextEntry;
125 MmTotalFreeSystemPtes[SystemPtePoolType] -= NumberOfPtes;
126 break;
127 }
128 }
129 else if (NumberOfPtes == 1)
130 {
131 //
132 // We have one PTE in this cluster, and it's all you want
133 //
134 PreviousPte->u.List.NextEntry = PointerPte->u.List.NextEntry;
135 MmTotalFreeSystemPtes[SystemPtePoolType]--;
136 break;
137 }
138
139 //
140 // We couldn't find what you wanted -- is this the last cluster?
141 //
142 if (PointerPte->u.List.NextEntry == ((ULONG)0xFFFFF))
143 {
144 //
145 // Fail
146 //
147 KeReleaseQueuedSpinLock(LockQueueSystemSpaceLock, OldIrql);
148 return NULL;
149 }
150
151 //
152 // Go to the next cluster
153 //
154 PreviousPte = PointerPte;
155 PointerPte = MmSystemPteBase + PointerPte->u.List.NextEntry;
156 ASSERT(PointerPte > PreviousPte);
157 }
158
159 //
160 // Release the lock, flush the TLB and return the first PTE
161 //
162 KeReleaseQueuedSpinLock(LockQueueSystemSpaceLock, OldIrql);
163 KeFlushProcessTb();
164 return PointerPte;
165 }
166
167 PMMPTE
168 NTAPI
169 MiReserveSystemPtes(IN ULONG NumberOfPtes,
170 IN MMSYSTEM_PTE_POOL_TYPE SystemPtePoolType)
171 {
172 PMMPTE PointerPte;
173
174 //
175 // Use the extended function
176 //
177 PointerPte = MiReserveAlignedSystemPtes(NumberOfPtes, SystemPtePoolType, 0);
178
179 //
180 // Check if allocation failed
181 //
182 if (!PointerPte)
183 {
184 //
185 // Warn that we are out of memory
186 //
187 DPRINT1("MiReserveSystemPtes: Failed to reserve %lu PTE(s)!\n", NumberOfPtes);
188 }
189
190 //
191 // Return the PTE Pointer
192 //
193 return PointerPte;
194 }
195
196 VOID
197 NTAPI
198 MiReleaseSystemPtes(IN PMMPTE StartingPte,
199 IN ULONG NumberOfPtes,
200 IN MMSYSTEM_PTE_POOL_TYPE SystemPtePoolType)
201 {
202 KIRQL OldIrql;
203 ULONG_PTR ClusterSize, CurrentSize;
204 PMMPTE CurrentPte, NextPte, PointerPte;
205
206 //
207 // Check to make sure the PTE address is within bounds
208 //
209 ASSERT(NumberOfPtes != 0);
210 ASSERT(StartingPte >= MmSystemPtesStart[SystemPtePoolType]);
211 ASSERT(StartingPte <= MmSystemPtesEnd[SystemPtePoolType]);
212
213 //
214 // Zero PTEs
215 //
216 RtlZeroMemory(StartingPte, NumberOfPtes * sizeof(MMPTE));
217 CurrentSize = (ULONG_PTR)(StartingPte - MmSystemPteBase);
218
219 //
220 // Acquire the system PTE lock
221 //
222 OldIrql = KeAcquireQueuedSpinLock(LockQueueSystemSpaceLock);
223
224 //
225 // Increase availability
226 //
227 MmTotalFreeSystemPtes[SystemPtePoolType] += NumberOfPtes;
228
229 //
230 // Get the free cluster and start going through them
231 //
232 CurrentPte = &MmFirstFreeSystemPte[SystemPtePoolType];
233 while (TRUE)
234 {
235 //
236 // Get the first real cluster of PTEs and check if it's ours
237 //
238 PointerPte = MmSystemPteBase + CurrentPte->u.List.NextEntry;
239 if (CurrentSize < CurrentPte->u.List.NextEntry)
240 {
241 //
242 // Sanity check
243 //
244 ASSERT(((StartingPte + NumberOfPtes) <= PointerPte) ||
245 (CurrentPte->u.List.NextEntry == ((ULONG)0xFFFFF)));
246
247 //
248 // Get the next cluster in case it's the one
249 //
250 NextPte = CurrentPte + 1;
251
252 //
253 // Check if this was actually a single-PTE entry
254 //
255 if (CurrentPte->u.List.OneEntry)
256 {
257 //
258 // We only have one page
259 //
260 ClusterSize = 1;
261 }
262 else
263 {
264 //
265 // The next cluster will have the page count
266 //
267 ClusterSize = (ULONG_PTR)NextPte->u.List.NextEntry;
268 }
269
270 //
271 // So check if this cluster actually describes the entire mapping
272 //
273 if ((CurrentPte + ClusterSize) == StartingPte)
274 {
275 //
276 // It does -- collapse the free PTEs into the next cluster
277 //
278 NumberOfPtes += ClusterSize;
279 NextPte->u.List.NextEntry = NumberOfPtes;
280 CurrentPte->u.List.OneEntry = 0;
281
282 //
283 // Make another pass
284 //
285 StartingPte = CurrentPte;
286 }
287 else
288 {
289 //
290 // There's still PTEs left -- make us into a cluster
291 //
292 StartingPte->u.List.NextEntry = CurrentPte->u.List.NextEntry;
293 CurrentPte->u.List.NextEntry = CurrentSize;
294
295 //
296 // Is there just one page left?
297 //
298 if (NumberOfPtes == 1)
299 {
300 //
301 // Then this actually becomes a single PTE entry
302 //
303 StartingPte->u.List.OneEntry = 1;
304 }
305 else
306 {
307 //
308 // Otherwise, create a new cluster for the remaining pages
309 //
310 StartingPte->u.List.OneEntry = 0;
311 NextPte = StartingPte + 1;
312 NextPte->u.List.NextEntry = NumberOfPtes;
313 }
314 }
315
316 //
317 // Now check if we've arrived at yet another cluster
318 //
319 if ((StartingPte + NumberOfPtes) == PointerPte)
320 {
321 //
322 // We'll collapse the next cluster into us
323 //
324 StartingPte->u.List.NextEntry = PointerPte->u.List.NextEntry;
325 StartingPte->u.List.OneEntry = 0;
326 NextPte = StartingPte + 1;
327
328 //
329 // Check if the cluster only had one page
330 //
331 if (PointerPte->u.List.OneEntry)
332 {
333 //
334 // So will we...
335 //
336 ClusterSize = 1;
337 }
338 else
339 {
340 //
341 // Otherwise, grab the page count from the next-next cluster
342 //
343 PointerPte++;
344 ClusterSize = (ULONG_PTR)PointerPte->u.List.NextEntry;
345 }
346
347 //
348 // And create the final combined cluster
349 //
350 NextPte->u.List.NextEntry = NumberOfPtes + ClusterSize;
351 }
352
353 //
354 // We released the PTEs into their cluster (and optimized the list)
355 //
356 KeReleaseQueuedSpinLock(LockQueueSystemSpaceLock, OldIrql);
357 break;
358 }
359
360 //
361 // Try the next cluster of PTEs...
362 //
363 CurrentPte = PointerPte;
364 }
365 }
366
367 VOID
368 NTAPI
369 MiInitializeSystemPtes(IN PMMPTE StartingPte,
370 IN ULONG NumberOfPtes,
371 IN MMSYSTEM_PTE_POOL_TYPE PoolType)
372 {
373 //
374 // Sanity checks
375 //
376 ASSERT(NumberOfPtes >= 1);
377
378 //
379 // Set the starting and ending PTE addresses for this space
380 //
381 MmSystemPteBase = (PVOID)PTE_BASE;
382 MmSystemPtesStart[PoolType] = StartingPte;
383 MmSystemPtesEnd[PoolType] = StartingPte + NumberOfPtes - 1;
384 DPRINT("System PTE space for %d starting at: %p and ending at: %p\n",
385 PoolType, MmSystemPtesStart[PoolType], MmSystemPtesEnd[PoolType]);
386
387 //
388 // Clear all the PTEs to start with
389 //
390 RtlZeroMemory(StartingPte, NumberOfPtes * sizeof(MMPTE));
391
392 //
393 // Make the first entry free and link it
394 //
395 StartingPte->u.List.NextEntry = ((ULONG)0xFFFFF);
396 MmFirstFreeSystemPte[PoolType].u.Long = 0;
397 MmFirstFreeSystemPte[PoolType].u.List.NextEntry = StartingPte -
398 MmSystemPteBase;
399
400 //
401 // The second entry stores the size of this PTE space
402 //
403 StartingPte++;
404 StartingPte->u.Long = 0;
405 StartingPte->u.List.NextEntry = NumberOfPtes;
406
407 //
408 // We also keep a global for it
409 //
410 MmTotalFreeSystemPtes[PoolType] = NumberOfPtes;
411
412 //
413 // Check if this is the system PTE space
414 //
415 if (PoolType == SystemPteSpace)
416 {
417 //
418 // Remember how many PTEs we have
419 //
420 MmTotalSystemPtes = NumberOfPtes;
421 }
422 }
423
424 /* EOF */