73a11801682e9c412968903f6a55f605a01b9283
[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 #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 KeReleaseQueuedSpinLock(LockQueueSystemSpaceLock, OldIrql);
113 PointerPte += (ClusterSize - NumberOfPtes);
114 break;
115 }
116
117 //
118 // Did we find exactly what you wanted?
119 //
120 if (NumberOfPtes == ClusterSize)
121 {
122 //
123 // Yes, fixup the cluster and decrease free system PTE count
124 //
125 PreviousPte->u.List.NextEntry = PointerPte->u.List.NextEntry;
126 MmTotalFreeSystemPtes[SystemPtePoolType] -= NumberOfPtes;
127 break;
128 }
129 }
130 else if (NumberOfPtes == 1)
131 {
132 //
133 // We have one PTE in this cluster, and it's all you want
134 //
135 PreviousPte->u.List.NextEntry = PointerPte->u.List.NextEntry;
136 MmTotalFreeSystemPtes[SystemPtePoolType]--;
137 break;
138 }
139
140 //
141 // We couldn't find what you wanted -- is this the last cluster?
142 //
143 if (PointerPte->u.List.NextEntry == ((ULONG)0xFFFFF))
144 {
145 //
146 // Fail
147 //
148 KeReleaseQueuedSpinLock(LockQueueSystemSpaceLock, OldIrql);
149 return NULL;
150 }
151
152 //
153 // Go to the next cluster
154 //
155 PreviousPte = PointerPte;
156 PointerPte = MmSystemPteBase + PointerPte->u.List.NextEntry;
157 ASSERT(PointerPte > PreviousPte);
158 }
159
160 //
161 // Flush the TLB and return the first PTE
162 //
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 ASSERT(PointerPte != NULL);
179 return PointerPte;
180 }
181
182 VOID
183 NTAPI
184 MiReleaseSystemPtes(IN PMMPTE StartingPte,
185 IN ULONG NumberOfPtes,
186 IN MMSYSTEM_PTE_POOL_TYPE SystemPtePoolType)
187 {
188 KIRQL OldIrql;
189 ULONG_PTR ClusterSize, CurrentSize;
190 PMMPTE CurrentPte, NextPte, PointerPte;
191
192 //
193 // Check to make sure the PTE address is within bounds
194 //
195 ASSERT(NumberOfPtes != 0);
196 ASSERT(StartingPte >= MmSystemPtesStart[SystemPtePoolType]);
197 ASSERT(StartingPte <= MmSystemPtesEnd[SystemPtePoolType]);
198
199 //
200 // Zero PTEs
201 //
202 RtlZeroMemory(StartingPte, NumberOfPtes * sizeof(MMPTE));
203 CurrentSize = (ULONG_PTR)(StartingPte - MmSystemPteBase);
204
205 //
206 // Acquire the system PTE lock
207 //
208 OldIrql = KeAcquireQueuedSpinLock(LockQueueSystemSpaceLock);
209
210 //
211 // Increase availability
212 //
213 MmTotalFreeSystemPtes[SystemPtePoolType] += NumberOfPtes;
214
215 //
216 // Get the free cluster and start going through them
217 //
218 CurrentPte = &MmFirstFreeSystemPte[SystemPtePoolType];
219 while (TRUE)
220 {
221 //
222 // Get the first real cluster of PTEs and check if it's ours
223 //
224 PointerPte = MmSystemPteBase + CurrentPte->u.List.NextEntry;
225 if (CurrentSize < CurrentPte->u.List.NextEntry)
226 {
227 //
228 // Sanity check
229 //
230 ASSERT(((StartingPte + NumberOfPtes) <= PointerPte) ||
231 (CurrentPte->u.List.NextEntry == ((ULONG)0xFFFFF)));
232
233 //
234 // Get the next cluster in case it's the one
235 //
236 NextPte = CurrentPte + 1;
237
238 //
239 // Check if this was actually a single-PTE entry
240 //
241 if (CurrentPte->u.List.OneEntry)
242 {
243 //
244 // We only have one page
245 //
246 ClusterSize = 1;
247 }
248 else
249 {
250 //
251 // The next cluster will have the page count
252 //
253 ClusterSize = (ULONG_PTR)NextPte->u.List.NextEntry;
254 }
255
256 //
257 // So check if this cluster actually describes the entire mapping
258 //
259 if ((CurrentPte + ClusterSize) == StartingPte)
260 {
261 //
262 // It does -- collapse the free PTEs into the next cluster
263 //
264 NumberOfPtes += ClusterSize;
265 NextPte->u.List.NextEntry = NumberOfPtes;
266 CurrentPte->u.List.OneEntry = 0;
267
268 //
269 // Make another pass
270 //
271 StartingPte = CurrentPte;
272 }
273 else
274 {
275 //
276 // There's still PTEs left -- make us into a cluster
277 //
278 StartingPte->u.List.NextEntry = CurrentPte->u.List.NextEntry;
279 CurrentPte->u.List.NextEntry = CurrentSize;
280
281 //
282 // Is there just one page left?
283 //
284 if (NumberOfPtes == 1)
285 {
286 //
287 // Then this actually becomes a single PTE entry
288 //
289 StartingPte->u.List.OneEntry = 1;
290 }
291 else
292 {
293 //
294 // Otherwise, create a new cluster for the remaining pages
295 //
296 StartingPte->u.List.OneEntry = 0;
297 NextPte = StartingPte + 1;
298 NextPte->u.List.NextEntry = NumberOfPtes;
299 }
300 }
301
302 //
303 // Now check if we've arrived at yet another cluster
304 //
305 if ((StartingPte + NumberOfPtes) == PointerPte)
306 {
307 //
308 // We'll collapse the next cluster into us
309 //
310 StartingPte->u.List.NextEntry = PointerPte->u.List.NextEntry;
311 StartingPte->u.List.OneEntry = 0;
312 NextPte = StartingPte + 1;
313
314 //
315 // Check if the cluster only had one page
316 //
317 if (PointerPte->u.List.OneEntry)
318 {
319 //
320 // So will we...
321 //
322 ClusterSize = 1;
323 }
324 else
325 {
326 //
327 // Otherwise, grab the page count from the next-next cluster
328 //
329 PointerPte++;
330 ClusterSize = (ULONG_PTR)PointerPte->u.List.NextEntry;
331 }
332
333 //
334 // And create the final combined cluster
335 //
336 NextPte->u.List.NextEntry = NumberOfPtes + ClusterSize;
337 }
338
339 //
340 // We released the PTEs into their cluster (and optimized the list)
341 //
342 KeReleaseQueuedSpinLock(LockQueueSystemSpaceLock, OldIrql);
343 break;
344 }
345
346 //
347 // Try the next cluster of PTEs...
348 //
349 CurrentPte = PointerPte;
350 }
351 }
352
353 VOID
354 NTAPI
355 MiInitializeSystemPtes(IN PMMPTE StartingPte,
356 IN ULONG NumberOfPtes,
357 IN MMSYSTEM_PTE_POOL_TYPE PoolType)
358 {
359 //
360 // Sanity checks
361 //
362 ASSERT(NumberOfPtes >= 1);
363
364 //
365 // Set the starting and ending PTE addresses for this space
366 //
367 MmSystemPteBase = (PVOID)PTE_BASE;
368 MmSystemPtesStart[PoolType] = StartingPte;
369 MmSystemPtesEnd[PoolType] = StartingPte + NumberOfPtes - 1;
370 DPRINT("System PTE space for %d starting at: %p and ending at: %p\n",
371 PoolType, MmSystemPtesStart[PoolType], MmSystemPtesEnd[PoolType]);
372
373 //
374 // Clear all the PTEs to start with
375 //
376 RtlZeroMemory(StartingPte, NumberOfPtes * sizeof(MMPTE));
377
378 //
379 // Make the first entry free and link it
380 //
381 StartingPte->u.List.NextEntry = ((ULONG)0xFFFFF);
382 MmFirstFreeSystemPte[PoolType].u.Long = 0;
383 MmFirstFreeSystemPte[PoolType].u.List.NextEntry = StartingPte -
384 MmSystemPteBase;
385
386 //
387 // The second entry stores the size of this PTE space
388 //
389 StartingPte++;
390 StartingPte->u.Long = 0;
391 StartingPte->u.List.NextEntry = NumberOfPtes;
392
393 //
394 // We also keep a global for it
395 //
396 MmTotalFreeSystemPtes[PoolType] = NumberOfPtes;
397
398 //
399 // Check if this is the system PTE space
400 //
401 if (PoolType == SystemPteSpace)
402 {
403 //
404 // Remember how many PTEs we have
405 //
406 MmTotalSystemPtes = NumberOfPtes;
407 }
408 }
409
410 /* EOF */