69809310b7db88f4861f3fc700c74746ed30a223
[reactos.git] / ntoskrnl / mm / kmap.c
1 /* $Id$
2  *
3  * COPYRIGHT:    See COPYING in the top level directory
4  * PROJECT:      ReactOS kernel
5  * FILE:         ntoskrnl/mm/kmap.c
6  * PURPOSE:      Implements the kernel memory pool
7  * PROGRAMMER:   David Welch (welch@cwcom.net)
8  */
9
10 /* INCLUDES ****************************************************************/
11
12 #include <ddk/ntddk.h>
13 #include <internal/mm.h>
14 #include <internal/ntoskrnl.h>
15 #include <internal/pool.h>
16 #include <ntos/minmax.h>
17
18 #define NDEBUG
19 #include <internal/debug.h>
20
21 /* GLOBALS *****************************************************************/
22
23 #define ALLOC_MAP_SIZE (NONPAGED_POOL_SIZE / PAGE_SIZE)
24
25 /*
26  * One bit for each page in the kmalloc region
27  *      If set then the page is used by a kmalloc block
28  */
29 static ULONG AllocMap[ALLOC_MAP_SIZE/32]={0,};
30 static KSPIN_LOCK AllocMapLock;
31 static ULONG AllocMapHint = 1;
32
33 static PVOID NonPagedPoolBase;
34
35 /* FUNCTIONS ***************************************************************/
36
37 VOID 
38 ExUnmapPage(PVOID Addr)
39 {
40    KIRQL oldIrql;
41    ULONG i = (Addr - NonPagedPoolBase) / PAGE_SIZE;
42    
43    DPRINT("ExUnmapPage(Addr %x)\n",Addr);
44    DPRINT("i %x\n",i);
45    
46    MmDeleteVirtualMapping(NULL, (PVOID)Addr, FALSE, NULL, NULL);
47    KeAcquireSpinLock(&AllocMapLock, &oldIrql);   
48    AllocMap[i / 32] &= (~(1 << (i % 32)));
49    AllocMapHint = min(AllocMapHint, i);
50    KeReleaseSpinLock(&AllocMapLock, oldIrql);
51 }
52
53 PVOID 
54 ExAllocatePage(VOID)
55 {
56   PHYSICAL_ADDRESS PhysPage;
57   NTSTATUS Status;
58
59   Status = MmRequestPageMemoryConsumer(MC_NPPOOL, FALSE, &PhysPage);
60   if (!NT_SUCCESS(Status))
61     {
62       return(NULL);
63     }
64
65   return(ExAllocatePageWithPhysPage(PhysPage));
66 }
67
68 NTSTATUS
69 MiZeroPage(PHYSICAL_ADDRESS PhysPage)
70 {
71   PVOID TempAddress;
72
73   TempAddress = ExAllocatePageWithPhysPage(PhysPage);
74   if (TempAddress == NULL)
75     {
76       return(STATUS_NO_MEMORY);
77     }
78   memset(TempAddress, 0, PAGE_SIZE);
79   ExUnmapPage(TempAddress);
80   return(STATUS_SUCCESS);
81 }
82
83 NTSTATUS
84 MiCopyFromUserPage(PHYSICAL_ADDRESS DestPhysPage, PVOID SourceAddress)
85 {
86   PVOID TempAddress;
87
88   TempAddress = ExAllocatePageWithPhysPage(DestPhysPage);
89   if (TempAddress == NULL)
90     {
91       return(STATUS_NO_MEMORY);
92     }
93   memcpy(TempAddress, SourceAddress, PAGE_SIZE);
94   ExUnmapPage(TempAddress);
95   return(STATUS_SUCCESS);
96 }
97
98 PVOID
99 ExAllocatePageWithPhysPage(PHYSICAL_ADDRESS PhysPage)
100 {
101    KIRQL oldlvl;
102    ULONG addr;
103    ULONG i;
104    NTSTATUS Status;
105
106    KeAcquireSpinLock(&AllocMapLock, &oldlvl);
107    for (i = AllocMapHint; i < ALLOC_MAP_SIZE; i++)
108      {
109        if (!(AllocMap[i / 32] & (1 << (i % 32))))
110          {
111             DPRINT("i %x\n",i);
112             AllocMap[i / 32] |= (1 << (i % 32));
113             AllocMapHint = i + 1;
114             addr = (ULONG)(NonPagedPoolBase + (i*PAGE_SIZE));
115             Status = MmCreateVirtualMapping(NULL, 
116                                             (PVOID)addr, 
117                                             PAGE_READWRITE | PAGE_SYSTEM, 
118                                             PhysPage,
119                                             FALSE);
120             if (!NT_SUCCESS(Status))
121               {
122                 DbgPrint("Unable to create virtual mapping\n");
123                 KeBugCheck(0);
124               }
125             KeReleaseSpinLock(&AllocMapLock, oldlvl);
126             return((PVOID)addr);
127          }
128      }
129    KeReleaseSpinLock(&AllocMapLock, oldlvl);
130    return(NULL);
131 }
132
133 VOID 
134 MmInitKernelMap(PVOID BaseAddress)
135 {
136    NonPagedPoolBase = BaseAddress;
137    KeInitializeSpinLock(&AllocMapLock);
138 }
139
140 VOID
141 MiFreeNonPagedPoolRegion(PVOID Addr, ULONG Count, BOOLEAN Free)
142 {
143   ULONG i;
144   ULONG Base = (Addr - NonPagedPoolBase) / PAGE_SIZE;
145   ULONG Offset;
146   KIRQL oldlvl;
147   
148   KeAcquireSpinLock(&AllocMapLock, &oldlvl);
149   AllocMapHint = min(AllocMapHint, Base);
150   for (i = 0; i < Count; i++)
151     {
152       Offset = Base + i;
153       AllocMap[Offset / 32] &= (~(1 << (Offset % 32)));       
154       MmDeleteVirtualMapping(NULL, 
155                              Addr + (i * PAGE_SIZE), 
156                              Free, 
157                              NULL, 
158                              NULL);
159     }
160   KeReleaseSpinLock(&AllocMapLock, oldlvl);
161 }
162
163 PVOID
164 MiAllocNonPagedPoolRegion(ULONG nr_pages)
165 /*
166  * FUNCTION: Allocates a region of pages within the nonpaged pool area
167  */
168 {
169    unsigned int start = 0;
170    unsigned int length = 0;
171    unsigned int i,j;
172    KIRQL oldlvl;
173
174    KeAcquireSpinLock(&AllocMapLock, &oldlvl);
175    for (i=AllocMapHint; i<ALLOC_MAP_SIZE;i++)
176      {
177        if (!(AllocMap[i/32] & (1 << (i % 32))))
178           {
179              if (length == 0)
180                {
181                   start=i;
182                   length = 1;
183                }
184              else
185                {
186                   length++;
187                }
188              if (length==nr_pages)
189                {
190                  AllocMapHint = start + length;
191                  for (j=start;j<(start+length);j++)
192                    {
193                      AllocMap[j / 32] |= (1 << (j % 32));
194                    }
195                  DPRINT("returning %x\n",((start*PAGE_SIZE)+NonPagedPoolBase));
196                  KeReleaseSpinLock(&AllocMapLock, oldlvl);
197                  return(((start*PAGE_SIZE)+NonPagedPoolBase));
198                }
199           }
200        else
201          {
202            start=0;
203            length=0;
204          }
205      }
206    DbgPrint("CRITICAL: Out of non-paged pool space\n");
207    KeBugCheck(0);
208    return(0);
209 }