xref: /reactos/ntoskrnl/mm/ARM3/vadnode.c (revision d831bc4f)
1c2c66affSColin Finck /*
2c2c66affSColin Finck  * PROJECT:         ReactOS Kernel
3c2c66affSColin Finck  * LICENSE:         BSD - See COPYING.ARM in the top level directory
4c2c66affSColin Finck  * FILE:            ntoskrnl/mm/ARM3/vadnode.c
5c2c66affSColin Finck  * PURPOSE:         ARM Memory Manager VAD Node Algorithms
6c2c66affSColin Finck  * PROGRAMMERS:     ReactOS Portable Systems Group
7c2c66affSColin Finck  *                  Timo Kreuzer (timo.kreuzer@reactos.org)
8c2c66affSColin Finck  */
9c2c66affSColin Finck 
10c2c66affSColin Finck /* INCLUDES *******************************************************************/
11c2c66affSColin Finck 
12c2c66affSColin Finck #include <ntoskrnl.h>
13c2c66affSColin Finck #define NDEBUG
14c2c66affSColin Finck #include <debug.h>
15c2c66affSColin Finck 
16c2c66affSColin Finck #define MODULE_INVOLVED_IN_ARM3
17c2c66affSColin Finck #include <mm/ARM3/miarm.h>
18c2c66affSColin Finck 
19c2c66affSColin Finck /* Include Mm version of AVL support */
20c2c66affSColin Finck #include "miavl.h"
21c2c66affSColin Finck #include <sdk/lib/rtl/avlsupp.c>
22c2c66affSColin Finck 
23c2c66affSColin Finck /* GLOBALS ********************************************************************/
24c2c66affSColin Finck 
25c2c66affSColin Finck CHAR MmReadWrite[32] =
26c2c66affSColin Finck {
27c2c66affSColin Finck     MM_NO_ACCESS_ALLOWED, MM_READ_ONLY_ALLOWED, MM_READ_ONLY_ALLOWED,
28c2c66affSColin Finck     MM_READ_ONLY_ALLOWED, MM_READ_WRITE_ALLOWED, MM_READ_WRITE_ALLOWED,
29c2c66affSColin Finck     MM_READ_WRITE_ALLOWED, MM_READ_WRITE_ALLOWED,
30c2c66affSColin Finck 
31c2c66affSColin Finck     MM_NO_ACCESS_ALLOWED, MM_READ_ONLY_ALLOWED, MM_READ_ONLY_ALLOWED,
32c2c66affSColin Finck     MM_READ_ONLY_ALLOWED, MM_READ_WRITE_ALLOWED, MM_READ_WRITE_ALLOWED,
33c2c66affSColin Finck     MM_READ_WRITE_ALLOWED, MM_READ_WRITE_ALLOWED,
34c2c66affSColin Finck 
35c2c66affSColin Finck     MM_NO_ACCESS_ALLOWED, MM_READ_ONLY_ALLOWED, MM_READ_ONLY_ALLOWED,
36c2c66affSColin Finck     MM_READ_ONLY_ALLOWED, MM_READ_WRITE_ALLOWED, MM_READ_WRITE_ALLOWED,
37c2c66affSColin Finck     MM_READ_WRITE_ALLOWED, MM_READ_WRITE_ALLOWED,
38c2c66affSColin Finck 
39c2c66affSColin Finck     MM_NO_ACCESS_ALLOWED, MM_READ_ONLY_ALLOWED, MM_READ_ONLY_ALLOWED,
40c2c66affSColin Finck     MM_READ_ONLY_ALLOWED, MM_READ_WRITE_ALLOWED, MM_READ_WRITE_ALLOWED,
41c2c66affSColin Finck     MM_READ_WRITE_ALLOWED, MM_READ_WRITE_ALLOWED,
42c2c66affSColin Finck };
43c2c66affSColin Finck 
44c2c66affSColin Finck /* FUNCTIONS ******************************************************************/
45c2c66affSColin Finck 
46*d831bc4fSTimo Kreuzer extern MM_AVL_TABLE MiRosKernelVadRoot;
47*d831bc4fSTimo Kreuzer 
48*d831bc4fSTimo Kreuzer #if DBG
49*d831bc4fSTimo Kreuzer 
50*d831bc4fSTimo Kreuzer static
51*d831bc4fSTimo Kreuzer VOID
MiDbgAssertIsLockedForRead(_In_ PMM_AVL_TABLE Table)52*d831bc4fSTimo Kreuzer MiDbgAssertIsLockedForRead(_In_ PMM_AVL_TABLE Table)
53*d831bc4fSTimo Kreuzer {
54*d831bc4fSTimo Kreuzer     if (Table == &MmSectionBasedRoot)
55*d831bc4fSTimo Kreuzer     {
56*d831bc4fSTimo Kreuzer         /* Need to hold MmSectionBasedMutex */
57*d831bc4fSTimo Kreuzer         ASSERT(MmSectionBasedMutex.Owner == KeGetCurrentThread());
58*d831bc4fSTimo Kreuzer     }
59*d831bc4fSTimo Kreuzer     else if (Table == &MiRosKernelVadRoot)
60*d831bc4fSTimo Kreuzer     {
61*d831bc4fSTimo Kreuzer         /* Need to hold either the system working-set lock or
62*d831bc4fSTimo Kreuzer            the idle process' AddressCreationLock */
63*d831bc4fSTimo Kreuzer         ASSERT(PsGetCurrentThread()->OwnsSystemWorkingSetExclusive ||
64*d831bc4fSTimo Kreuzer                PsGetCurrentThread()->OwnsSystemWorkingSetShared ||
65*d831bc4fSTimo Kreuzer                (PsIdleProcess->AddressCreationLock.Owner == KeGetCurrentThread()));
66*d831bc4fSTimo Kreuzer     }
67*d831bc4fSTimo Kreuzer     else
68*d831bc4fSTimo Kreuzer     {
69*d831bc4fSTimo Kreuzer         /* Need to hold either the process working-set lock or
70*d831bc4fSTimo Kreuzer            the current process' AddressCreationLock */
71*d831bc4fSTimo Kreuzer         PEPROCESS Process = CONTAINING_RECORD(Table, EPROCESS, VadRoot);
72*d831bc4fSTimo Kreuzer         ASSERT(MI_WS_OWNER(Process) ||
73*d831bc4fSTimo Kreuzer                (Process->AddressCreationLock.Owner == KeGetCurrentThread()));
74*d831bc4fSTimo Kreuzer     }
75*d831bc4fSTimo Kreuzer }
76*d831bc4fSTimo Kreuzer 
77*d831bc4fSTimo Kreuzer static
78*d831bc4fSTimo Kreuzer VOID
MiDbgAssertIsLockedForWrite(_In_ PMM_AVL_TABLE Table)79*d831bc4fSTimo Kreuzer MiDbgAssertIsLockedForWrite(_In_ PMM_AVL_TABLE Table)
80*d831bc4fSTimo Kreuzer {
81*d831bc4fSTimo Kreuzer     if (Table == &MmSectionBasedRoot)
82*d831bc4fSTimo Kreuzer     {
83*d831bc4fSTimo Kreuzer         /* Need to hold MmSectionBasedMutex */
84*d831bc4fSTimo Kreuzer         ASSERT(MmSectionBasedMutex.Owner == KeGetCurrentThread());
85*d831bc4fSTimo Kreuzer     }
86*d831bc4fSTimo Kreuzer     else if (Table == &MiRosKernelVadRoot)
87*d831bc4fSTimo Kreuzer     {
88*d831bc4fSTimo Kreuzer         /* Need to hold both the system working-set lock exclusive and
89*d831bc4fSTimo Kreuzer            the idle process' AddressCreationLock */
90*d831bc4fSTimo Kreuzer         ASSERT(PsGetCurrentThread()->OwnsSystemWorkingSetExclusive);
91*d831bc4fSTimo Kreuzer         ASSERT(PsIdleProcess->AddressCreationLock.Owner == KeGetCurrentThread());
92*d831bc4fSTimo Kreuzer     }
93*d831bc4fSTimo Kreuzer     else
94*d831bc4fSTimo Kreuzer     {
95*d831bc4fSTimo Kreuzer         /* Need to hold both the process working-set lock exclusive and
96*d831bc4fSTimo Kreuzer            the current process' AddressCreationLock */
97*d831bc4fSTimo Kreuzer         PEPROCESS Process = CONTAINING_RECORD(Table, EPROCESS, VadRoot);
98*d831bc4fSTimo Kreuzer         ASSERT(Process == PsGetCurrentProcess());
99*d831bc4fSTimo Kreuzer         ASSERT(PsGetCurrentThread()->OwnsProcessWorkingSetExclusive);
100*d831bc4fSTimo Kreuzer         ASSERT(Process->AddressCreationLock.Owner == KeGetCurrentThread());
101*d831bc4fSTimo Kreuzer     }
102*d831bc4fSTimo Kreuzer }
103*d831bc4fSTimo Kreuzer 
104*d831bc4fSTimo Kreuzer #define ASSERT_LOCKED_FOR_READ(Table) MiDbgAssertIsLockedForRead(Table)
105*d831bc4fSTimo Kreuzer #define ASSERT_LOCKED_FOR_WRITE(Table) MiDbgAssertIsLockedForWrite(Table)
106*d831bc4fSTimo Kreuzer 
107*d831bc4fSTimo Kreuzer #else // DBG
108*d831bc4fSTimo Kreuzer 
109*d831bc4fSTimo Kreuzer #define ASSERT_LOCKED_FOR_READ(Table)
110*d831bc4fSTimo Kreuzer #define ASSERT_LOCKED_FOR_WRITE(Table)
111*d831bc4fSTimo Kreuzer 
112*d831bc4fSTimo Kreuzer #endif // DBG
113*d831bc4fSTimo Kreuzer 
114c2c66affSColin Finck PMMVAD
115c2c66affSColin Finck NTAPI
MiLocateAddress(IN PVOID VirtualAddress)116c2c66affSColin Finck MiLocateAddress(IN PVOID VirtualAddress)
117c2c66affSColin Finck {
118c2c66affSColin Finck     PMMVAD FoundVad;
119c2c66affSColin Finck     ULONG_PTR Vpn;
120c2c66affSColin Finck     PMM_AVL_TABLE Table = &PsGetCurrentProcess()->VadRoot;
121c2c66affSColin Finck     TABLE_SEARCH_RESULT SearchResult;
122c2c66affSColin Finck 
123*d831bc4fSTimo Kreuzer     ASSERT_LOCKED_FOR_READ(Table);
124*d831bc4fSTimo Kreuzer 
125c2c66affSColin Finck     /* Start with the the hint */
126c2c66affSColin Finck     FoundVad = (PMMVAD)Table->NodeHint;
127c2c66affSColin Finck     if (!FoundVad) return NULL;
128c2c66affSColin Finck 
129c2c66affSColin Finck     /* Check if this VPN is in the hint, if so, use it */
130c2c66affSColin Finck     Vpn = (ULONG_PTR)VirtualAddress >> PAGE_SHIFT;
131c2c66affSColin Finck     if ((Vpn >= FoundVad->StartingVpn) && (Vpn <= FoundVad->EndingVpn)) return FoundVad;
132c2c66affSColin Finck 
133c2c66affSColin Finck     /* VAD hint didn't work, go look for it */
134c2c66affSColin Finck     SearchResult = RtlpFindAvlTableNodeOrParent(Table,
135c2c66affSColin Finck                                                 (PVOID)Vpn,
136c2c66affSColin Finck                                                 (PMMADDRESS_NODE*)&FoundVad);
137c2c66affSColin Finck     if (SearchResult != TableFoundNode) return NULL;
138c2c66affSColin Finck 
139c2c66affSColin Finck     /* We found it, update the hint */
140c2c66affSColin Finck     ASSERT(FoundVad != NULL);
141c2c66affSColin Finck     ASSERT((Vpn >= FoundVad->StartingVpn) && (Vpn <= FoundVad->EndingVpn));
142*d831bc4fSTimo Kreuzer 
143*d831bc4fSTimo Kreuzer     /* We allow this (atomic) update without exclusive lock, because it's a hint only */
144c2c66affSColin Finck     Table->NodeHint = FoundVad;
145c2c66affSColin Finck     return FoundVad;
146c2c66affSColin Finck }
147c2c66affSColin Finck 
148c2c66affSColin Finck TABLE_SEARCH_RESULT
149c2c66affSColin Finck NTAPI
MiCheckForConflictingNode(IN ULONG_PTR StartVpn,IN ULONG_PTR EndVpn,IN PMM_AVL_TABLE Table,OUT PMMADDRESS_NODE * NodeOrParent)150c2c66affSColin Finck MiCheckForConflictingNode(IN ULONG_PTR StartVpn,
151c2c66affSColin Finck                           IN ULONG_PTR EndVpn,
152c2c66affSColin Finck                           IN PMM_AVL_TABLE Table,
153c2c66affSColin Finck                           OUT PMMADDRESS_NODE *NodeOrParent)
154c2c66affSColin Finck {
155c2c66affSColin Finck     PMMADDRESS_NODE ParentNode, CurrentNode;
156c2c66affSColin Finck 
157*d831bc4fSTimo Kreuzer     ASSERT_LOCKED_FOR_READ(Table);
158*d831bc4fSTimo Kreuzer 
159c2c66affSColin Finck     /* If the tree is empty, there is no conflict */
160c2c66affSColin Finck     if (Table->NumberGenericTableElements == 0) return TableEmptyTree;
161c2c66affSColin Finck 
162c2c66affSColin Finck     /* Start looping from the root node */
163c2c66affSColin Finck     CurrentNode = RtlRightChildAvl(&Table->BalancedRoot);
164c2c66affSColin Finck     ASSERT(CurrentNode != NULL);
165c2c66affSColin Finck     while (CurrentNode)
166c2c66affSColin Finck     {
167c2c66affSColin Finck         ParentNode = CurrentNode;
168c2c66affSColin Finck 
169c2c66affSColin Finck         /* This address comes after */
170c2c66affSColin Finck         if (StartVpn > CurrentNode->EndingVpn)
171c2c66affSColin Finck         {
172c2c66affSColin Finck             /* Keep searching on the right */
173c2c66affSColin Finck             CurrentNode = RtlRightChildAvl(CurrentNode);
174c2c66affSColin Finck         }
175c2c66affSColin Finck         else if (EndVpn < CurrentNode->StartingVpn)
176c2c66affSColin Finck         {
177c2c66affSColin Finck             /* This address ends before the node starts, search on the left */
178c2c66affSColin Finck             CurrentNode = RtlLeftChildAvl(CurrentNode);
179c2c66affSColin Finck         }
180c2c66affSColin Finck         else
181c2c66affSColin Finck         {
182c2c66affSColin Finck             /* This address is part of this node, return it */
183c2c66affSColin Finck             *NodeOrParent = ParentNode;
184c2c66affSColin Finck             return TableFoundNode;
185c2c66affSColin Finck         }
186c2c66affSColin Finck     }
187c2c66affSColin Finck 
188c2c66affSColin Finck     /* There is no more child, save the current node as parent */
189c2c66affSColin Finck     *NodeOrParent = ParentNode;
190c2c66affSColin Finck     if (StartVpn > ParentNode->EndingVpn)
191c2c66affSColin Finck     {
192c2c66affSColin Finck         return TableInsertAsRight;
193c2c66affSColin Finck     }
194c2c66affSColin Finck     else
195c2c66affSColin Finck     {
196c2c66affSColin Finck         return TableInsertAsLeft;
197c2c66affSColin Finck     }
198c2c66affSColin Finck }
199c2c66affSColin Finck 
200c2c66affSColin Finck VOID
201c2c66affSColin Finck NTAPI
MiInsertNode(IN PMM_AVL_TABLE Table,IN PMMADDRESS_NODE NewNode,IN PMMADDRESS_NODE Parent,IN TABLE_SEARCH_RESULT Result)202c2c66affSColin Finck MiInsertNode(IN PMM_AVL_TABLE Table,
203c2c66affSColin Finck              IN PMMADDRESS_NODE NewNode,
204c2c66affSColin Finck              IN PMMADDRESS_NODE Parent,
205c2c66affSColin Finck              IN TABLE_SEARCH_RESULT Result)
206c2c66affSColin Finck {
207c2c66affSColin Finck     PMMVAD_LONG Vad;
208c2c66affSColin Finck 
209*d831bc4fSTimo Kreuzer     ASSERT_LOCKED_FOR_WRITE(Table);
210*d831bc4fSTimo Kreuzer 
211c2c66affSColin Finck     /* Insert it into the tree */
212c2c66affSColin Finck     RtlpInsertAvlTreeNode(Table, NewNode, Parent, Result);
213c2c66affSColin Finck 
214c2c66affSColin Finck     /* Now insert an ARM3 MEMORY_AREA for this node, unless the insert was already from the MEMORY_AREA code */
215c2c66affSColin Finck     Vad = (PMMVAD_LONG)NewNode;
216c2c66affSColin Finck     if (Vad->u.VadFlags.Spare == 0)
217c2c66affSColin Finck     {
218c2c66affSColin Finck         NTSTATUS Status;
219c2c66affSColin Finck         PMEMORY_AREA MemoryArea;
220c2c66affSColin Finck         SIZE_T Size;
221c2c66affSColin Finck         PEPROCESS Process = CONTAINING_RECORD(Table, EPROCESS, VadRoot);
222c2c66affSColin Finck         PVOID AllocatedBase = (PVOID)(Vad->StartingVpn << PAGE_SHIFT);
223c2c66affSColin Finck 
224c2c66affSColin Finck         Size = ((Vad->EndingVpn + 1) - Vad->StartingVpn) << PAGE_SHIFT;
225c2c66affSColin Finck 
226c2c66affSColin Finck         if (AllocatedBase == NULL)
227c2c66affSColin Finck         {
228c2c66affSColin Finck             AllocatedBase = (PVOID)(ULONG_PTR)1;
229c2c66affSColin Finck             Size -= 1;
230c2c66affSColin Finck         }
231c2c66affSColin Finck 
232c2c66affSColin Finck         Status = MmCreateMemoryArea(&Process->Vm,
233c2c66affSColin Finck                                     MEMORY_AREA_OWNED_BY_ARM3,
234c2c66affSColin Finck                                     &AllocatedBase,
235c2c66affSColin Finck                                     Size,
236c2c66affSColin Finck                                     PAGE_READWRITE,
237c2c66affSColin Finck                                     &MemoryArea,
238c2c66affSColin Finck                                     0,
239c2c66affSColin Finck                                     PAGE_SIZE);
240c2c66affSColin Finck         ASSERT(NT_SUCCESS(Status));
241c2c66affSColin Finck 
242c2c66affSColin Finck         /* Check if this is VM VAD */
243c2c66affSColin Finck         if (Vad->ControlArea == NULL)
244c2c66affSColin Finck         {
245c2c66affSColin Finck             /* We store the reactos MEMORY_AREA here */
246c2c66affSColin Finck             Vad->FirstPrototypePte = (PMMPTE)MemoryArea;
247c2c66affSColin Finck         }
248c2c66affSColin Finck         else
249c2c66affSColin Finck         {
250c2c66affSColin Finck             /* This is a section VAD. Store the MAREA here for now */
2512dade10dSTimo Kreuzer             ASSERT(Vad->u4.Banked == (PVOID)(ULONG_PTR)0xDEADBABEDEADBABEULL);
252c2c66affSColin Finck             Vad->u4.Banked = (PVOID)MemoryArea;
253c2c66affSColin Finck         }
254c2c66affSColin Finck     }
255c2c66affSColin Finck }
256c2c66affSColin Finck 
257c2c66affSColin Finck VOID
258c2c66affSColin Finck NTAPI
MiInsertVad(IN PMMVAD Vad,IN PMM_AVL_TABLE VadRoot)259c2c66affSColin Finck MiInsertVad(IN PMMVAD Vad,
260c2c66affSColin Finck             IN PMM_AVL_TABLE VadRoot)
261c2c66affSColin Finck {
262c2c66affSColin Finck     TABLE_SEARCH_RESULT Result;
263c2c66affSColin Finck     PMMADDRESS_NODE Parent = NULL;
264c2c66affSColin Finck 
265*d831bc4fSTimo Kreuzer     ASSERT_LOCKED_FOR_WRITE(VadRoot);
266*d831bc4fSTimo Kreuzer 
267c2c66affSColin Finck     /* Validate the VAD and set it as the current hint */
268c2c66affSColin Finck     ASSERT(Vad->EndingVpn >= Vad->StartingVpn);
269c2c66affSColin Finck     VadRoot->NodeHint = Vad;
270c2c66affSColin Finck 
271c2c66affSColin Finck     /* Find the parent VAD and where this child should be inserted */
272c2c66affSColin Finck     Result = RtlpFindAvlTableNodeOrParent(VadRoot, (PVOID)Vad->StartingVpn, &Parent);
273c2c66affSColin Finck     ASSERT(Result != TableFoundNode);
274c2c66affSColin Finck     ASSERT((Parent != NULL) || (Result == TableEmptyTree));
275c2c66affSColin Finck 
276c2c66affSColin Finck     /* Do the actual insert operation */
277c2c66affSColin Finck     MiInsertNode(VadRoot, (PVOID)Vad, Parent, Result);
278c2c66affSColin Finck }
279c2c66affSColin Finck 
280c2c66affSColin Finck NTSTATUS
281c2c66affSColin Finck NTAPI
MiInsertVadEx(_Inout_ PMMVAD Vad,_In_ ULONG_PTR * BaseAddress,_In_ SIZE_T ViewSize,_In_ ULONG_PTR HighestAddress,_In_ ULONG_PTR Alignment,_In_ ULONG AllocationType)282c2c66affSColin Finck MiInsertVadEx(
283c2c66affSColin Finck     _Inout_ PMMVAD Vad,
284c2c66affSColin Finck     _In_ ULONG_PTR *BaseAddress,
285c2c66affSColin Finck     _In_ SIZE_T ViewSize,
286c2c66affSColin Finck     _In_ ULONG_PTR HighestAddress,
287c2c66affSColin Finck     _In_ ULONG_PTR Alignment,
288c2c66affSColin Finck     _In_ ULONG AllocationType)
289c2c66affSColin Finck {
290c2c66affSColin Finck     ULONG_PTR StartingAddress, EndingAddress;
291c2c66affSColin Finck     PEPROCESS CurrentProcess;
292c2c66affSColin Finck     PETHREAD CurrentThread;
293c2c66affSColin Finck     TABLE_SEARCH_RESULT Result;
294c2c66affSColin Finck     PMMADDRESS_NODE Parent;
295c2c66affSColin Finck 
296c2c66affSColin Finck     /* Align the view size to pages */
297c2c66affSColin Finck     ViewSize = ALIGN_UP_BY(ViewSize, PAGE_SIZE);
298c2c66affSColin Finck 
299c2c66affSColin Finck     /* Get the current process */
300c2c66affSColin Finck     CurrentProcess = PsGetCurrentProcess();
301c2c66affSColin Finck 
302c2c66affSColin Finck     /* Acquire the address creation lock and make sure the process is alive */
303c2c66affSColin Finck     KeAcquireGuardedMutex(&CurrentProcess->AddressCreationLock);
304c2c66affSColin Finck     if (CurrentProcess->VmDeleted)
305c2c66affSColin Finck     {
306c2c66affSColin Finck         KeReleaseGuardedMutex(&CurrentProcess->AddressCreationLock);
307c2c66affSColin Finck         DPRINT1("The process is dying\n");
308c2c66affSColin Finck         return STATUS_PROCESS_IS_TERMINATING;
309c2c66affSColin Finck     }
310c2c66affSColin Finck 
311c2c66affSColin Finck     /* Did the caller specify an address? */
312c2c66affSColin Finck     if (*BaseAddress == 0)
313c2c66affSColin Finck     {
314c2c66affSColin Finck         /* Make sure HighestAddress is not too large */
315c2c66affSColin Finck         HighestAddress = min(HighestAddress, (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS);
316c2c66affSColin Finck 
317c2c66affSColin Finck         /* Which way should we search? */
318c2c66affSColin Finck         if ((AllocationType & MEM_TOP_DOWN) || CurrentProcess->VmTopDown)
319c2c66affSColin Finck         {
320c2c66affSColin Finck             /* Find an address top-down */
321c2c66affSColin Finck             Result = MiFindEmptyAddressRangeDownTree(ViewSize,
322c2c66affSColin Finck                                                      HighestAddress,
323c2c66affSColin Finck                                                      Alignment,
324c2c66affSColin Finck                                                      &CurrentProcess->VadRoot,
325c2c66affSColin Finck                                                      &StartingAddress,
326c2c66affSColin Finck                                                      &Parent);
327c2c66affSColin Finck         }
328c2c66affSColin Finck         else
329c2c66affSColin Finck         {
330c2c66affSColin Finck             /* Find an address bottom-up */
331c2c66affSColin Finck             Result = MiFindEmptyAddressRangeInTree(ViewSize,
332c2c66affSColin Finck                                                    Alignment,
333c2c66affSColin Finck                                                    &CurrentProcess->VadRoot,
334c2c66affSColin Finck                                                    &Parent,
335c2c66affSColin Finck                                                    &StartingAddress);
336c2c66affSColin Finck         }
337c2c66affSColin Finck 
338c2c66affSColin Finck         /* Get the ending address, which is the last piece we need for the VAD */
339c2c66affSColin Finck         EndingAddress = StartingAddress + ViewSize - 1;
340c2c66affSColin Finck 
341c2c66affSColin Finck         /* Check if we found a suitable location */
342c2c66affSColin Finck         if ((Result == TableFoundNode) || (EndingAddress > HighestAddress))
343c2c66affSColin Finck         {
344c2c66affSColin Finck             DPRINT1("Not enough free space to insert this VAD node!\n");
345c2c66affSColin Finck             KeReleaseGuardedMutex(&CurrentProcess->AddressCreationLock);
346c2c66affSColin Finck             return STATUS_NO_MEMORY;
347c2c66affSColin Finck         }
348c2c66affSColin Finck 
349c2c66affSColin Finck         ASSERT(StartingAddress != 0);
350c2c66affSColin Finck         ASSERT(StartingAddress < (ULONG_PTR)HighestAddress);
351c2c66affSColin Finck         ASSERT(EndingAddress > StartingAddress);
352c2c66affSColin Finck     }
353c2c66affSColin Finck     else
354c2c66affSColin Finck     {
355c2c66affSColin Finck         /* Calculate the starting and ending address */
356c2c66affSColin Finck         StartingAddress = ALIGN_DOWN_BY(*BaseAddress, Alignment);
357c2c66affSColin Finck         EndingAddress = StartingAddress + ViewSize - 1;
358c2c66affSColin Finck 
359c2c66affSColin Finck         /* Make sure it doesn't conflict with an existing allocation */
360c2c66affSColin Finck         Result = MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT,
361c2c66affSColin Finck                                            EndingAddress >> PAGE_SHIFT,
362c2c66affSColin Finck                                            &CurrentProcess->VadRoot,
363c2c66affSColin Finck                                            &Parent);
364c2c66affSColin Finck         if (Result == TableFoundNode)
365c2c66affSColin Finck         {
366f5471b7bSBernhard Feichtinger             DPRINT("Given address conflicts with existing node\n");
367c2c66affSColin Finck             KeReleaseGuardedMutex(&CurrentProcess->AddressCreationLock);
368c2c66affSColin Finck             return STATUS_CONFLICTING_ADDRESSES;
369c2c66affSColin Finck         }
370c2c66affSColin Finck     }
371c2c66affSColin Finck 
372c2c66affSColin Finck     /* Now set the VAD address */
373c2c66affSColin Finck     Vad->StartingVpn = StartingAddress >> PAGE_SHIFT;
374c2c66affSColin Finck     Vad->EndingVpn = EndingAddress >> PAGE_SHIFT;
375c2c66affSColin Finck 
376c2c66affSColin Finck     /* Check if we already need to charge for the pages */
377c2c66affSColin Finck     if ((Vad->u.VadFlags.PrivateMemory && Vad->u.VadFlags.MemCommit) ||
378c2c66affSColin Finck         (!Vad->u.VadFlags.PrivateMemory &&
379c2c66affSColin Finck          (Vad->u.VadFlags.Protection & PAGE_WRITECOPY)))
380c2c66affSColin Finck     {
381c2c66affSColin Finck         /* Set the commit charge */
382c2c66affSColin Finck         Vad->u.VadFlags.CommitCharge = ViewSize / PAGE_SIZE;
383c2c66affSColin Finck     }
384c2c66affSColin Finck 
385c2c66affSColin Finck     /* Check if the VAD is to be secured */
386c2c66affSColin Finck     if (Vad->u2.VadFlags2.OneSecured)
387c2c66affSColin Finck     {
388c2c66affSColin Finck         /* This *must* be a long VAD! */
389c2c66affSColin Finck         ASSERT(Vad->u2.VadFlags2.LongVad);
390c2c66affSColin Finck 
391c2c66affSColin Finck         /* Yeah this is retarded, I didn't invent it! */
392c2c66affSColin Finck         ((PMMVAD_LONG)Vad)->u3.Secured.StartVpn = StartingAddress;
393c2c66affSColin Finck         ((PMMVAD_LONG)Vad)->u3.Secured.EndVpn = EndingAddress;
394c2c66affSColin Finck     }
395c2c66affSColin Finck 
396c2c66affSColin Finck     /* Lock the working set */
397c2c66affSColin Finck     CurrentThread = PsGetCurrentThread();
398c2c66affSColin Finck     MiLockProcessWorkingSetUnsafe(CurrentProcess, CurrentThread);
399c2c66affSColin Finck 
400c2c66affSColin Finck     /* Insert the VAD */
401c2c66affSColin Finck     CurrentProcess->VadRoot.NodeHint = Vad;
402c2c66affSColin Finck     MiInsertNode(&CurrentProcess->VadRoot, (PVOID)Vad, Parent, Result);
403c2c66affSColin Finck 
404c2c66affSColin Finck     /* Release the working set */
405c2c66affSColin Finck     MiUnlockProcessWorkingSetUnsafe(CurrentProcess, CurrentThread);
406c2c66affSColin Finck 
407c2c66affSColin Finck     /* Update the process' virtual size, and peak virtual size */
408c2c66affSColin Finck     CurrentProcess->VirtualSize += ViewSize;
409c2c66affSColin Finck     if (CurrentProcess->VirtualSize > CurrentProcess->PeakVirtualSize)
410c2c66affSColin Finck     {
411c2c66affSColin Finck         CurrentProcess->PeakVirtualSize = CurrentProcess->VirtualSize;
412c2c66affSColin Finck     }
413c2c66affSColin Finck 
414c2c66affSColin Finck     /* Unlock the address space */
415c2c66affSColin Finck     KeReleaseGuardedMutex(&CurrentProcess->AddressCreationLock);
416c2c66affSColin Finck 
417c2c66affSColin Finck     *BaseAddress = StartingAddress;
418c2c66affSColin Finck     return STATUS_SUCCESS;
419c2c66affSColin Finck }
420c2c66affSColin Finck 
421c2c66affSColin Finck VOID
422c2c66affSColin Finck NTAPI
MiInsertBasedSection(IN PSECTION Section)423c2c66affSColin Finck MiInsertBasedSection(IN PSECTION Section)
424c2c66affSColin Finck {
425c2c66affSColin Finck     TABLE_SEARCH_RESULT Result;
426c2c66affSColin Finck     PMMADDRESS_NODE Parent = NULL;
427c2c66affSColin Finck     ASSERT(Section->Address.EndingVpn >= Section->Address.StartingVpn);
428c2c66affSColin Finck 
429*d831bc4fSTimo Kreuzer     ASSERT_LOCKED_FOR_WRITE(&MmSectionBasedRoot);
430*d831bc4fSTimo Kreuzer 
431c2c66affSColin Finck     /* Find the parent VAD and where this child should be inserted */
432c2c66affSColin Finck     Result = RtlpFindAvlTableNodeOrParent(&MmSectionBasedRoot, (PVOID)Section->Address.StartingVpn, &Parent);
433c2c66affSColin Finck     ASSERT(Result != TableFoundNode);
434c2c66affSColin Finck     ASSERT((Parent != NULL) || (Result == TableEmptyTree));
435c2c66affSColin Finck     MiInsertNode(&MmSectionBasedRoot, &Section->Address, Parent, Result);
436c2c66affSColin Finck }
437c2c66affSColin Finck 
438c2c66affSColin Finck VOID
439c2c66affSColin Finck NTAPI
MiRemoveNode(IN PMMADDRESS_NODE Node,IN PMM_AVL_TABLE Table)440c2c66affSColin Finck MiRemoveNode(IN PMMADDRESS_NODE Node,
441c2c66affSColin Finck              IN PMM_AVL_TABLE Table)
442c2c66affSColin Finck {
443c2c66affSColin Finck     PMMVAD_LONG Vad;
444c2c66affSColin Finck 
445*d831bc4fSTimo Kreuzer     ASSERT_LOCKED_FOR_WRITE(Table);
446*d831bc4fSTimo Kreuzer 
447c2c66affSColin Finck     /* Call the AVL code */
448c2c66affSColin Finck     RtlpDeleteAvlTreeNode(Table, Node);
449c2c66affSColin Finck 
450c2c66affSColin Finck     /* Decrease element count */
451c2c66affSColin Finck     Table->NumberGenericTableElements--;
452c2c66affSColin Finck 
453c2c66affSColin Finck     /* Check if this node was the hint */
454c2c66affSColin Finck     if (Table->NodeHint == Node)
455c2c66affSColin Finck     {
456c2c66affSColin Finck         /* Get a new hint, unless we're empty now, in which case nothing */
457c2c66affSColin Finck         if (!Table->NumberGenericTableElements) Table->NodeHint = NULL;
458c2c66affSColin Finck         else Table->NodeHint = Table->BalancedRoot.RightChild;
459c2c66affSColin Finck     }
460c2c66affSColin Finck 
461c2c66affSColin Finck     /* Free the node from ReactOS view as well */
462c2c66affSColin Finck     Vad = (PMMVAD_LONG)Node;
463c2c66affSColin Finck     if ((Table != &MmSectionBasedRoot) && (Vad->u.VadFlags.Spare == 0))
464c2c66affSColin Finck     {
465c2c66affSColin Finck         PMEMORY_AREA MemoryArea;
466c2c66affSColin Finck         PEPROCESS Process;
467c2c66affSColin Finck 
468c2c66affSColin Finck         /* Check if this is VM VAD */
469c2c66affSColin Finck         if (Vad->ControlArea == NULL)
470c2c66affSColin Finck         {
471c2c66affSColin Finck             /* We store the ReactOS MEMORY_AREA here */
472c2c66affSColin Finck             MemoryArea = (PMEMORY_AREA)Vad->FirstPrototypePte;
473c2c66affSColin Finck         }
474c2c66affSColin Finck         else
475c2c66affSColin Finck         {
476c2c66affSColin Finck             /* This is a section VAD. We store the ReactOS MEMORY_AREA here */
477c2c66affSColin Finck             MemoryArea = (PMEMORY_AREA)Vad->u4.Banked;
478c2c66affSColin Finck         }
479c2c66affSColin Finck 
480c2c66affSColin Finck         /* Make sure one actually still exists */
481c2c66affSColin Finck         if (MemoryArea)
482c2c66affSColin Finck         {
483c2c66affSColin Finck             /* Make sure we have not already freed it */
4842dade10dSTimo Kreuzer             ASSERT(MemoryArea != (PVOID)(ULONG_PTR)0xDEADBAB1DEADBAB1ULL);
485c2c66affSColin Finck 
486c2c66affSColin Finck             /* Get the process */
487c2c66affSColin Finck             Process = CONTAINING_RECORD(Table, EPROCESS, VadRoot);
488c2c66affSColin Finck 
489c2c66affSColin Finck             /* We only create fake memory-areas for ARM3 VADs */
490c2c66affSColin Finck             ASSERT(MemoryArea->Type == MEMORY_AREA_OWNED_BY_ARM3);
491c2c66affSColin Finck             ASSERT(MemoryArea->Vad == NULL);
492c2c66affSColin Finck 
493c2c66affSColin Finck             /* Free it */
494c2c66affSColin Finck             MmFreeMemoryArea(&Process->Vm, MemoryArea, NULL, NULL);
495c2c66affSColin Finck 
496c2c66affSColin Finck             /* Check if this is VM VAD */
497c2c66affSColin Finck             if (Vad->ControlArea == NULL)
498c2c66affSColin Finck             {
499c2c66affSColin Finck                 /* Delete the pointer to it */
5002dade10dSTimo Kreuzer                 Vad->FirstPrototypePte = (PVOID)(ULONG_PTR)0xDEADBAB1DEADBAB1ULL;
501c2c66affSColin Finck             }
502c2c66affSColin Finck             else
503c2c66affSColin Finck             {
504c2c66affSColin Finck                 /* Delete the pointer to it */
5052dade10dSTimo Kreuzer                 Vad->u4.Banked = (PVOID)(ULONG_PTR)0xDEADBAB1DEADBAB1ULL;
506c2c66affSColin Finck             }
507c2c66affSColin Finck         }
508c2c66affSColin Finck     }
509c2c66affSColin Finck }
510c2c66affSColin Finck 
511c2c66affSColin Finck PMMADDRESS_NODE
512c2c66affSColin Finck NTAPI
MiGetPreviousNode(IN PMMADDRESS_NODE Node)513c2c66affSColin Finck MiGetPreviousNode(IN PMMADDRESS_NODE Node)
514c2c66affSColin Finck {
515c2c66affSColin Finck     PMMADDRESS_NODE Parent;
516c2c66affSColin Finck 
517c2c66affSColin Finck     /* Get the left child */
518c2c66affSColin Finck     if (RtlLeftChildAvl(Node))
519c2c66affSColin Finck     {
520c2c66affSColin Finck         /* Get right-most child */
521c2c66affSColin Finck         Node = RtlLeftChildAvl(Node);
522c2c66affSColin Finck         while (RtlRightChildAvl(Node)) Node = RtlRightChildAvl(Node);
523c2c66affSColin Finck         return Node;
524c2c66affSColin Finck     }
525c2c66affSColin Finck 
526c2c66affSColin Finck     Parent = RtlParentAvl(Node);
527c2c66affSColin Finck     ASSERT(Parent != NULL);
528c2c66affSColin Finck     while (Parent != Node)
529c2c66affSColin Finck     {
530c2c66affSColin Finck         /* The parent should be a right child, return the real predecessor */
531c2c66affSColin Finck         if (RtlIsRightChildAvl(Node))
532c2c66affSColin Finck         {
533c2c66affSColin Finck             /* Return it unless it's the root */
534c2c66affSColin Finck             if (Parent == RtlParentAvl(Parent)) Parent = NULL;
535c2c66affSColin Finck             return Parent;
536c2c66affSColin Finck         }
537c2c66affSColin Finck 
538c2c66affSColin Finck         /* Keep lopping until we find our parent */
539c2c66affSColin Finck         Node = Parent;
540c2c66affSColin Finck         Parent = RtlParentAvl(Node);
541c2c66affSColin Finck     }
542c2c66affSColin Finck 
543c2c66affSColin Finck     /* Nothing found */
544c2c66affSColin Finck     return NULL;
545c2c66affSColin Finck }
546c2c66affSColin Finck 
547c2c66affSColin Finck PMMADDRESS_NODE
548c2c66affSColin Finck NTAPI
MiGetNextNode(IN PMMADDRESS_NODE Node)549c2c66affSColin Finck MiGetNextNode(IN PMMADDRESS_NODE Node)
550c2c66affSColin Finck {
551c2c66affSColin Finck     PMMADDRESS_NODE Parent;
552c2c66affSColin Finck 
553c2c66affSColin Finck     /* Get the right child */
554c2c66affSColin Finck     if (RtlRightChildAvl(Node))
555c2c66affSColin Finck     {
556c2c66affSColin Finck         /* Get left-most child */
557c2c66affSColin Finck         Node = RtlRightChildAvl(Node);
558c2c66affSColin Finck         while (RtlLeftChildAvl(Node)) Node = RtlLeftChildAvl(Node);
559c2c66affSColin Finck         return Node;
560c2c66affSColin Finck     }
561c2c66affSColin Finck 
562c2c66affSColin Finck     Parent = RtlParentAvl(Node);
563c2c66affSColin Finck     ASSERT(Parent != NULL);
564c2c66affSColin Finck     while (Parent != Node)
565c2c66affSColin Finck     {
566c2c66affSColin Finck         /* The parent should be a left child, return the real predecessor */
567c2c66affSColin Finck         if (RtlIsLeftChildAvl(Node))
568c2c66affSColin Finck         {
569c2c66affSColin Finck             /* Return it */
570c2c66affSColin Finck             return Parent;
571c2c66affSColin Finck         }
572c2c66affSColin Finck 
573c2c66affSColin Finck         /* Keep lopping until we find our parent */
574c2c66affSColin Finck         Node = Parent;
575c2c66affSColin Finck         Parent = RtlParentAvl(Node);
576c2c66affSColin Finck     }
577c2c66affSColin Finck 
578c2c66affSColin Finck     /* Nothing found */
579c2c66affSColin Finck     return NULL;
580c2c66affSColin Finck }
581c2c66affSColin Finck 
582c2c66affSColin Finck TABLE_SEARCH_RESULT
583c2c66affSColin Finck NTAPI
MiFindEmptyAddressRangeInTree(IN SIZE_T Length,IN ULONG_PTR Alignment,IN PMM_AVL_TABLE Table,OUT PMMADDRESS_NODE * PreviousVad,OUT PULONG_PTR Base)584c2c66affSColin Finck MiFindEmptyAddressRangeInTree(IN SIZE_T Length,
585c2c66affSColin Finck                               IN ULONG_PTR Alignment,
586c2c66affSColin Finck                               IN PMM_AVL_TABLE Table,
587c2c66affSColin Finck                               OUT PMMADDRESS_NODE *PreviousVad,
588c2c66affSColin Finck                               OUT PULONG_PTR Base)
589c2c66affSColin Finck {
590c2c66affSColin Finck     PMMADDRESS_NODE Node, PreviousNode;
591c2c66affSColin Finck     ULONG_PTR PageCount, AlignmentVpn, LowVpn, HighestVpn;
592c2c66affSColin Finck     ASSERT(Length != 0);
593c2c66affSColin Finck 
594*d831bc4fSTimo Kreuzer     ASSERT_LOCKED_FOR_READ(Table);
595*d831bc4fSTimo Kreuzer 
596c2c66affSColin Finck     /* Calculate page numbers for the length, alignment, and starting address */
597c2c66affSColin Finck     PageCount = BYTES_TO_PAGES(Length);
598c2c66affSColin Finck     AlignmentVpn = Alignment >> PAGE_SHIFT;
599c2c66affSColin Finck     LowVpn = ALIGN_UP_BY((ULONG_PTR)MM_LOWEST_USER_ADDRESS >> PAGE_SHIFT, AlignmentVpn);
600c2c66affSColin Finck 
601c2c66affSColin Finck     /* Check for kernel mode table (memory areas) */
602c2c66affSColin Finck     if (Table->Unused == 1)
603c2c66affSColin Finck     {
604c2c66affSColin Finck         LowVpn = ALIGN_UP_BY((ULONG_PTR)MmSystemRangeStart >> PAGE_SHIFT, AlignmentVpn);
605c2c66affSColin Finck     }
606c2c66affSColin Finck 
607c2c66affSColin Finck     /* Check if the table is empty */
608c2c66affSColin Finck     if (Table->NumberGenericTableElements == 0)
609c2c66affSColin Finck     {
610c2c66affSColin Finck         /* Tree is empty, the candidate address is already the best one */
611c2c66affSColin Finck         *Base = LowVpn << PAGE_SHIFT;
612c2c66affSColin Finck         return TableEmptyTree;
613c2c66affSColin Finck     }
614c2c66affSColin Finck 
615c2c66affSColin Finck     /* Otherwise, follow the leftmost child of the right root node's child */
616c2c66affSColin Finck     Node = RtlRightChildAvl(&Table->BalancedRoot);
617c2c66affSColin Finck     while (RtlLeftChildAvl(Node)) Node = RtlLeftChildAvl(Node);
618c2c66affSColin Finck 
619c2c66affSColin Finck     /* Start a search to find a gap */
620c2c66affSColin Finck     PreviousNode = NULL;
621c2c66affSColin Finck     while (Node != NULL)
622c2c66affSColin Finck     {
623c2c66affSColin Finck         /* Check if the gap below the current node is suitable */
624c2c66affSColin Finck         if (Node->StartingVpn >= LowVpn + PageCount)
625c2c66affSColin Finck         {
626c2c66affSColin Finck             /* There is enough space to add our node */
627c2c66affSColin Finck             *Base = LowVpn << PAGE_SHIFT;
628c2c66affSColin Finck 
629c2c66affSColin Finck             /* Can we use the current node as parent? */
630c2c66affSColin Finck             if (RtlLeftChildAvl(Node) == NULL)
631c2c66affSColin Finck             {
632c2c66affSColin Finck                 /* Node has no left child, so use it as parent */
633c2c66affSColin Finck                 *PreviousVad = Node;
634c2c66affSColin Finck                 return TableInsertAsLeft;
635c2c66affSColin Finck             }
636c2c66affSColin Finck             else
637c2c66affSColin Finck             {
638c2c66affSColin Finck                 /* Node has a left child, this means that the previous node is
639c2c66affSColin Finck                    the right-most child of it's left child and can be used as
640c2c66affSColin Finck                    the parent. In case we use the space before the left-most
641c2c66affSColin Finck                    node, it's left child must be NULL. */
642c2c66affSColin Finck                 ASSERT(PreviousNode != NULL);
643c2c66affSColin Finck                 ASSERT(RtlRightChildAvl(PreviousNode) == NULL);
644c2c66affSColin Finck                 *PreviousVad = PreviousNode;
645c2c66affSColin Finck                 return TableInsertAsRight;
646c2c66affSColin Finck             }
647c2c66affSColin Finck         }
648c2c66affSColin Finck 
649c2c66affSColin Finck         /* The next candidate is above the current node */
650c2c66affSColin Finck         if (Node->EndingVpn >= LowVpn)
651c2c66affSColin Finck             LowVpn = ALIGN_UP_BY(Node->EndingVpn + 1, AlignmentVpn);
652c2c66affSColin Finck 
653c2c66affSColin Finck         /* Remember the current node and go to the next node */
654c2c66affSColin Finck         PreviousNode = Node;
655c2c66affSColin Finck         Node = MiGetNextNode(Node);
656c2c66affSColin Finck     }
657c2c66affSColin Finck 
658c2c66affSColin Finck     /* We're up to the highest VAD, will this allocation fit above it? */
659c2c66affSColin Finck     HighestVpn = ((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1) / PAGE_SIZE;
660c2c66affSColin Finck 
661c2c66affSColin Finck     /* Check for kernel mode table (memory areas) */
662c2c66affSColin Finck     if (Table->Unused == 1)
663c2c66affSColin Finck     {
664c2c66affSColin Finck         HighestVpn = ALIGN_UP_BY((ULONG_PTR)(LONG_PTR)-1 >> PAGE_SHIFT, AlignmentVpn);
665c2c66affSColin Finck     }
666c2c66affSColin Finck 
667c2c66affSColin Finck     if (HighestVpn >= LowVpn + PageCount)
668c2c66affSColin Finck     {
669c2c66affSColin Finck         /* Yes! Use this VAD to store the allocation */
670c2c66affSColin Finck         *PreviousVad = PreviousNode;
671c2c66affSColin Finck         *Base = LowVpn << PAGE_SHIFT;
672c2c66affSColin Finck         return TableInsertAsRight;
673c2c66affSColin Finck     }
674c2c66affSColin Finck 
675c2c66affSColin Finck     /* Nyet, there's no free address space for this allocation, so we'll fail */
676c2c66affSColin Finck     return TableFoundNode;
677c2c66affSColin Finck }
678c2c66affSColin Finck 
679c2c66affSColin Finck TABLE_SEARCH_RESULT
680c2c66affSColin Finck NTAPI
MiFindEmptyAddressRangeDownTree(IN SIZE_T Length,IN ULONG_PTR BoundaryAddress,IN ULONG_PTR Alignment,IN PMM_AVL_TABLE Table,OUT PULONG_PTR Base,OUT PMMADDRESS_NODE * Parent)681c2c66affSColin Finck MiFindEmptyAddressRangeDownTree(IN SIZE_T Length,
682c2c66affSColin Finck                                 IN ULONG_PTR BoundaryAddress,
683c2c66affSColin Finck                                 IN ULONG_PTR Alignment,
684c2c66affSColin Finck                                 IN PMM_AVL_TABLE Table,
685c2c66affSColin Finck                                 OUT PULONG_PTR Base,
686c2c66affSColin Finck                                 OUT PMMADDRESS_NODE *Parent)
687c2c66affSColin Finck {
688c66b6665STimo Kreuzer     PMMADDRESS_NODE Node, OldNode = NULL, Child;
689c2c66affSColin Finck     ULONG_PTR LowVpn, HighVpn, AlignmentVpn;
690c2c66affSColin Finck     PFN_NUMBER PageCount;
691c2c66affSColin Finck 
692*d831bc4fSTimo Kreuzer     ASSERT_LOCKED_FOR_READ(Table);
693*d831bc4fSTimo Kreuzer 
694c2c66affSColin Finck     /* Sanity checks */
695c2c66affSColin Finck     ASSERT(BoundaryAddress);
696c2c66affSColin Finck     ASSERT(BoundaryAddress <= ((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS));
697c2c66affSColin Finck     ASSERT((Alignment & (PAGE_SIZE - 1)) == 0);
698c2c66affSColin Finck 
699c2c66affSColin Finck     /* Calculate page numbers for the length and alignment */
700c2c66affSColin Finck     Length = ROUND_TO_PAGES(Length);
701c2c66affSColin Finck     PageCount = Length >> PAGE_SHIFT;
702c2c66affSColin Finck     AlignmentVpn = Alignment / PAGE_SIZE;
703c2c66affSColin Finck 
704c2c66affSColin Finck     /* Check for kernel mode table (memory areas) */
705c2c66affSColin Finck     if (Table->Unused == 1)
706c2c66affSColin Finck     {
707c2c66affSColin Finck         LowVpn = ALIGN_UP_BY((ULONG_PTR)MmSystemRangeStart >> PAGE_SHIFT, AlignmentVpn);
708c2c66affSColin Finck     }
709c2c66affSColin Finck     else
710c2c66affSColin Finck     {
711c2c66affSColin Finck         LowVpn = ALIGN_UP_BY((ULONG_PTR)MM_LOWEST_USER_ADDRESS, Alignment);
712c2c66affSColin Finck     }
713c2c66affSColin Finck 
714c2c66affSColin Finck     /* Check if there is enough space below the boundary */
715c2c66affSColin Finck     if ((LowVpn + Length) > (BoundaryAddress + 1))
716c2c66affSColin Finck     {
717c2c66affSColin Finck         return TableFoundNode;
718c2c66affSColin Finck     }
719c2c66affSColin Finck 
720c2c66affSColin Finck     /* Check if the table is empty */
721c2c66affSColin Finck     if (Table->NumberGenericTableElements == 0)
722c2c66affSColin Finck     {
723c2c66affSColin Finck         /* Tree is empty, the candidate address is already the best one */
724c2c66affSColin Finck         *Base = ALIGN_DOWN_BY(BoundaryAddress + 1 - Length, Alignment);
725c2c66affSColin Finck         return TableEmptyTree;
726c2c66affSColin Finck     }
727c2c66affSColin Finck 
728c2c66affSColin Finck     /* Calculate the initial upper margin */
729c2c66affSColin Finck     HighVpn = (BoundaryAddress + 1) >> PAGE_SHIFT;
730c2c66affSColin Finck 
731c2c66affSColin Finck     /* Starting from the root, follow the right children until we found a node
732c2c66affSColin Finck        that ends above the boundary */
733c2c66affSColin Finck     Node = RtlRightChildAvl(&Table->BalancedRoot);
734c2c66affSColin Finck     while ((Node->EndingVpn < HighVpn) &&
735c2c66affSColin Finck            ((Child = RtlRightChildAvl(Node)) != NULL)) Node = Child;
736c2c66affSColin Finck 
737c2c66affSColin Finck     /* Now loop the Vad nodes */
738c2c66affSColin Finck     while (Node)
739c2c66affSColin Finck     {
740c2c66affSColin Finck         /* Calculate the lower margin */
741c2c66affSColin Finck         LowVpn = ALIGN_UP_BY(Node->EndingVpn + 1, AlignmentVpn);
742c2c66affSColin Finck 
743c2c66affSColin Finck         /* Check if the current bounds are suitable */
744c2c66affSColin Finck         if ((HighVpn > LowVpn) && ((HighVpn - LowVpn) >= PageCount))
745c2c66affSColin Finck         {
746c2c66affSColin Finck             /* There is enough space to add our node */
747c2c66affSColin Finck             LowVpn = ALIGN_DOWN_BY(HighVpn - PageCount, AlignmentVpn);
748c2c66affSColin Finck             *Base = LowVpn << PAGE_SHIFT;
749c2c66affSColin Finck 
750c2c66affSColin Finck             /* Can we use the current node as parent? */
751c2c66affSColin Finck             if (!RtlRightChildAvl(Node))
752c2c66affSColin Finck             {
753c2c66affSColin Finck                 /* Node has no right child, so use it as parent */
754c2c66affSColin Finck                 *Parent = Node;
755c2c66affSColin Finck                 return TableInsertAsRight;
756c2c66affSColin Finck             }
757c2c66affSColin Finck             else
758c2c66affSColin Finck             {
759c66b6665STimo Kreuzer                 /* Node has a right child. This means we must have already
760c66b6665STimo Kreuzer                    moved one node left from the right-most node we started
761c66b6665STimo Kreuzer                    with, thus we already have an OldNode! */
762c66b6665STimo Kreuzer                 ASSERT(OldNode != NULL);
763c66b6665STimo Kreuzer 
764c66b6665STimo Kreuzer                 /* The node we had before is the most left grandchild of
765c66b6665STimo Kreuzer                    that right child, use it as parent. */
766c66b6665STimo Kreuzer                 ASSERT(RtlLeftChildAvl(OldNode) == NULL);
767c2c66affSColin Finck                 *Parent = OldNode;
768c2c66affSColin Finck                 return TableInsertAsLeft;
769c2c66affSColin Finck             }
770c2c66affSColin Finck         }
771c2c66affSColin Finck 
772c2c66affSColin Finck         /* Update the upper margin if necessary */
773c2c66affSColin Finck         if (Node->StartingVpn < HighVpn) HighVpn = Node->StartingVpn;
774c2c66affSColin Finck 
775c2c66affSColin Finck         /* Remember the current node and go to the previous node */
776c2c66affSColin Finck         OldNode = Node;
777c2c66affSColin Finck         Node = MiGetPreviousNode(Node);
778c2c66affSColin Finck     }
779c2c66affSColin Finck 
780c2c66affSColin Finck     /* Check if there's enough space before the lowest Vad */
781c2c66affSColin Finck     LowVpn = ALIGN_UP_BY((ULONG_PTR)MI_LOWEST_VAD_ADDRESS, Alignment) / PAGE_SIZE;
782c2c66affSColin Finck     if ((HighVpn > LowVpn) && ((HighVpn - LowVpn) >= PageCount))
783c2c66affSColin Finck     {
784c2c66affSColin Finck         /* There is enough space to add our address */
785c2c66affSColin Finck         LowVpn = ALIGN_DOWN_BY(HighVpn - PageCount, Alignment >> PAGE_SHIFT);
786c2c66affSColin Finck         *Base = LowVpn << PAGE_SHIFT;
787c2c66affSColin Finck         *Parent = OldNode;
788c2c66affSColin Finck         return TableInsertAsLeft;
789c2c66affSColin Finck     }
790c2c66affSColin Finck 
791c2c66affSColin Finck     /* No address space left at all */
792c2c66affSColin Finck     *Base = 0;
793c2c66affSColin Finck     *Parent = NULL;
794c2c66affSColin Finck     return TableFoundNode;
795c2c66affSColin Finck }
796c2c66affSColin Finck 
797c2c66affSColin Finck NTSTATUS
798c2c66affSColin Finck NTAPI
MiFindEmptyAddressRangeDownBasedTree(IN SIZE_T Length,IN ULONG_PTR BoundaryAddress,IN ULONG_PTR Alignment,IN PMM_AVL_TABLE Table,OUT PULONG_PTR Base)799c2c66affSColin Finck MiFindEmptyAddressRangeDownBasedTree(IN SIZE_T Length,
800c2c66affSColin Finck                                      IN ULONG_PTR BoundaryAddress,
801c2c66affSColin Finck                                      IN ULONG_PTR Alignment,
802c2c66affSColin Finck                                      IN PMM_AVL_TABLE Table,
803c2c66affSColin Finck                                      OUT PULONG_PTR Base)
804c2c66affSColin Finck {
805c2c66affSColin Finck     PMMADDRESS_NODE Node, LowestNode;
806c2c66affSColin Finck     ULONG_PTR LowVpn, BestVpn;
807c2c66affSColin Finck 
808*d831bc4fSTimo Kreuzer     ASSERT_LOCKED_FOR_READ(Table);
809*d831bc4fSTimo Kreuzer 
810c2c66affSColin Finck     /* Sanity checks */
811c2c66affSColin Finck     ASSERT(Table == &MmSectionBasedRoot);
812c2c66affSColin Finck     ASSERT(BoundaryAddress);
813c2c66affSColin Finck     ASSERT(BoundaryAddress <= ((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1));
814c2c66affSColin Finck 
815c2c66affSColin Finck     /* Compute page length, make sure the boundary address is valid */
816c2c66affSColin Finck     Length = ROUND_TO_PAGES(Length);
817c2c66affSColin Finck     if ((BoundaryAddress + 1) < Length) return STATUS_NO_MEMORY;
818c2c66affSColin Finck 
819c2c66affSColin Finck     /* Check if the table is empty */
820c2c66affSColin Finck     BestVpn = ROUND_DOWN(BoundaryAddress + 1 - Length, Alignment);
821c2c66affSColin Finck     if (Table->NumberGenericTableElements == 0)
822c2c66affSColin Finck     {
823c2c66affSColin Finck         /* Tree is empty, the candidate address is already the best one */
824c2c66affSColin Finck         *Base = BestVpn;
825c2c66affSColin Finck         return STATUS_SUCCESS;
826c2c66affSColin Finck     }
827c2c66affSColin Finck 
828c2c66affSColin Finck     /* Go to the right-most node which should be the biggest address */
829c2c66affSColin Finck     Node = Table->BalancedRoot.RightChild;
830c2c66affSColin Finck     while (RtlRightChildAvl(Node)) Node = RtlRightChildAvl(Node);
831c2c66affSColin Finck 
832c2c66affSColin Finck     /* Check if we can fit in here */
833c2c66affSColin Finck     LowVpn = ROUND_UP(Node->EndingVpn + 1, Alignment);
834c2c66affSColin Finck     if ((LowVpn < BoundaryAddress) && (Length <= (BoundaryAddress - LowVpn)))
835c2c66affSColin Finck     {
836c2c66affSColin Finck #if (NTDDI_VERSION >= NTDDI_VISTA)
837c2c66affSColin Finck         /* Return the address. */
838c2c66affSColin Finck         *Base = BestVpn;
839c2c66affSColin Finck #else
840c2c66affSColin Finck         /* Note: this is a compatibility hack that mimics a bug in the 2k3
841c2c66affSColin Finck            kernel. It will can waste up to Alignment bytes of memory above
842c2c66affSColin Finck            the allocation. This bug was fixed in Windows Vista */
843c2c66affSColin Finck         *Base = ROUND_DOWN(BoundaryAddress - Length, Alignment);
844c2c66affSColin Finck #endif
845c2c66affSColin Finck         return STATUS_SUCCESS;
846c2c66affSColin Finck     }
847c2c66affSColin Finck 
848c2c66affSColin Finck     /* Now loop the Vad nodes */
849c2c66affSColin Finck     do
850c2c66affSColin Finck     {
851c2c66affSColin Finck         /* Break out if we've reached the last node */
852c2c66affSColin Finck         LowestNode = MiGetPreviousNode(Node);
853c2c66affSColin Finck         if (!LowestNode) break;
854c2c66affSColin Finck 
855c2c66affSColin Finck         /* Check if this node could contain the requested address */
856c2c66affSColin Finck         LowVpn = ROUND_UP(LowestNode->EndingVpn + 1, Alignment);
857c2c66affSColin Finck         if ((LowestNode->EndingVpn < BestVpn) &&
858c2c66affSColin Finck             (LowVpn < Node->StartingVpn) &&
859c2c66affSColin Finck             (Length <= (Node->StartingVpn - LowVpn)))
860c2c66affSColin Finck         {
861c2c66affSColin Finck             /* Check if we need to take BoundaryAddress into account */
862c2c66affSColin Finck             if (BoundaryAddress < Node->StartingVpn)
863c2c66affSColin Finck             {
864c2c66affSColin Finck                 /* Return the optimal VPN address */
865c2c66affSColin Finck                 *Base = BestVpn;
866c2c66affSColin Finck                 return STATUS_SUCCESS;
867c2c66affSColin Finck             }
868c2c66affSColin Finck             else
869c2c66affSColin Finck             {
870c2c66affSColin Finck                 /* The upper margin is given by the Node's starting address */
871c2c66affSColin Finck                 *Base = ROUND_DOWN(Node->StartingVpn - Length, Alignment);
872c2c66affSColin Finck                 return STATUS_SUCCESS;
873c2c66affSColin Finck             }
874c2c66affSColin Finck         }
875c2c66affSColin Finck 
876c2c66affSColin Finck         /* Move to the next node */
877c2c66affSColin Finck         Node = LowestNode;
878c2c66affSColin Finck     } while (TRUE);
879c2c66affSColin Finck 
880c2c66affSColin Finck     /* Check if there's enough space before the lowest Vad */
881c2c66affSColin Finck     if ((Node->StartingVpn > (ULONG_PTR)MI_LOWEST_VAD_ADDRESS) &&
882c2c66affSColin Finck         ((Node->StartingVpn - (ULONG_PTR)MI_LOWEST_VAD_ADDRESS) >= Length))
883c2c66affSColin Finck     {
884c2c66affSColin Finck         /* Check if it fits in perfectly */
885c2c66affSColin Finck         if (BoundaryAddress < Node->StartingVpn)
886c2c66affSColin Finck         {
887c2c66affSColin Finck             /* Return the optimal VPN address */
888c2c66affSColin Finck             *Base = BestVpn;
889c2c66affSColin Finck             return STATUS_SUCCESS;
890c2c66affSColin Finck         }
891c2c66affSColin Finck 
892c2c66affSColin Finck         /* Return an aligned base address within this node */
893c2c66affSColin Finck         *Base = ROUND_DOWN(Node->StartingVpn - Length, Alignment);
894c2c66affSColin Finck         return STATUS_SUCCESS;
895c2c66affSColin Finck     }
896c2c66affSColin Finck 
897c2c66affSColin Finck     /* No address space left at all */
898c2c66affSColin Finck     return STATUS_NO_MEMORY;
899c2c66affSColin Finck }
900c2c66affSColin Finck 
901c2c66affSColin Finck NTSTATUS
902c2c66affSColin Finck NTAPI
MiCheckSecuredVad(IN PMMVAD Vad,IN PVOID Base,IN SIZE_T Size,IN ULONG ProtectionMask)903c2c66affSColin Finck MiCheckSecuredVad(IN PMMVAD Vad,
904c2c66affSColin Finck                   IN PVOID Base,
905c2c66affSColin Finck                   IN SIZE_T Size,
906c2c66affSColin Finck                   IN ULONG ProtectionMask)
907c2c66affSColin Finck {
908c2c66affSColin Finck     ULONG_PTR StartAddress, EndAddress;
909c2c66affSColin Finck 
910c2c66affSColin Finck     /* Compute start and end address */
911c2c66affSColin Finck     StartAddress = (ULONG_PTR)Base;
912c2c66affSColin Finck     EndAddress = StartAddress + Size - 1;
913c2c66affSColin Finck 
914c2c66affSColin Finck     /* Are we deleting/unmapping, or changing? */
915c2c66affSColin Finck     if (ProtectionMask < MM_DELETE_CHECK)
916c2c66affSColin Finck     {
917c2c66affSColin Finck         /* Changing... are we allowed to do so? */
918c2c66affSColin Finck         if ((Vad->u.VadFlags.NoChange == 1) &&
919c2c66affSColin Finck             (Vad->u2.VadFlags2.SecNoChange == 1) &&
920c2c66affSColin Finck             (Vad->u.VadFlags.Protection != ProtectionMask))
921c2c66affSColin Finck         {
922c2c66affSColin Finck             /* Nope, bail out */
923c2c66affSColin Finck             DPRINT1("Trying to mess with a no-change VAD!\n");
924c2c66affSColin Finck             return STATUS_INVALID_PAGE_PROTECTION;
925c2c66affSColin Finck         }
926c2c66affSColin Finck     }
927c2c66affSColin Finck     else
928c2c66affSColin Finck     {
929c2c66affSColin Finck         /* This is allowed */
930c2c66affSColin Finck         ProtectionMask = 0;
931c2c66affSColin Finck     }
932c2c66affSColin Finck 
933c2c66affSColin Finck     /* ARM3 doesn't support this yet */
934c2c66affSColin Finck     ASSERT(Vad->u2.VadFlags2.MultipleSecured == 0);
935c2c66affSColin Finck 
936c2c66affSColin Finck     /* Is this a one-secured VAD, like a TEB or PEB? */
937c2c66affSColin Finck     if (Vad->u2.VadFlags2.OneSecured)
938c2c66affSColin Finck     {
939c2c66affSColin Finck         /* Is this allocation being described by the VAD? */
940c2c66affSColin Finck         if ((StartAddress <= ((PMMVAD_LONG)Vad)->u3.Secured.EndVpn) &&
941c2c66affSColin Finck             (EndAddress >= ((PMMVAD_LONG)Vad)->u3.Secured.StartVpn))
942c2c66affSColin Finck         {
943c2c66affSColin Finck             /* Guard page? */
944c2c66affSColin Finck             if (ProtectionMask & MM_DECOMMIT)
945c2c66affSColin Finck             {
946c2c66affSColin Finck                 DPRINT1("Not allowed to change protection on guard page!\n");
947c2c66affSColin Finck                 return STATUS_INVALID_PAGE_PROTECTION;
948c2c66affSColin Finck             }
949c2c66affSColin Finck 
950c2c66affSColin Finck             /* ARM3 doesn't have read-only VADs yet */
951c2c66affSColin Finck             ASSERT(Vad->u2.VadFlags2.ReadOnly == 0);
952c2c66affSColin Finck 
953c2c66affSColin Finck             /* Check if read-write protections are allowed */
954c2c66affSColin Finck             if (MmReadWrite[ProtectionMask] < MM_READ_WRITE_ALLOWED)
955c2c66affSColin Finck             {
956c2c66affSColin Finck                 DPRINT1("Invalid protection mask for RW access!\n");
957c2c66affSColin Finck                 return STATUS_INVALID_PAGE_PROTECTION;
958c2c66affSColin Finck             }
959c2c66affSColin Finck         }
960c2c66affSColin Finck     }
961c2c66affSColin Finck 
962c2c66affSColin Finck     /* All good, allow the change */
963c2c66affSColin Finck     return STATUS_SUCCESS;
964c2c66affSColin Finck }
965c2c66affSColin Finck 
966c2c66affSColin Finck /* EOF */
967c2c66affSColin Finck 
968