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