ab4879214a6bc336693d5dd05550c6285738e52f
[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 UCHAR AllocMapBuffer[ROUND_UP(ALLOC_MAP_SIZE, 8) / 8];
30 static RTL_BITMAP AllocMap;
31 static KSPIN_LOCK AllocMapLock;
32 static ULONG AllocMapHint = 0;
33
34 static PVOID NonPagedPoolBase;
35
36 /* FUNCTIONS ***************************************************************/
37
38 VOID 
39 ExUnmapPage(PVOID Addr)
40 {
41    KIRQL oldIrql;
42    ULONG Base = (Addr - NonPagedPoolBase) / PAGE_SIZE;
43    
44    DPRINT("ExUnmapPage(Addr %x)\n",Addr);
45    
46    MmDeleteVirtualMapping(NULL, (PVOID)Addr, FALSE, NULL, NULL);
47    KeAcquireSpinLock(&AllocMapLock, &oldIrql);   
48    RtlClearBits(&AllocMap, Base, 1);
49    AllocMapHint = min(AllocMapHint, Base);
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    PVOID Addr;
103    ULONG Base;
104    NTSTATUS Status;
105
106    KeAcquireSpinLock(&AllocMapLock, &oldlvl);
107    Base = RtlFindClearBitsAndSet(&AllocMap, 1, AllocMapHint);
108    if (Base != 0xFFFFFFFF)
109    {
110       AllocMapHint = Base + 1;
111       KeReleaseSpinLock(&AllocMapLock, oldlvl);
112       Addr = NonPagedPoolBase + Base * PAGE_SIZE;
113       Status = MmCreateVirtualMapping(NULL, 
114                                       Addr, 
115                                       PAGE_READWRITE | PAGE_SYSTEM, 
116                                       PhysPage,
117                                       FALSE);
118       if (!NT_SUCCESS(Status))
119       {
120           DbgPrint("Unable to create virtual mapping\n");
121           KeBugCheck(0);
122       }
123       return Addr;
124    }
125    KeReleaseSpinLock(&AllocMapLock, oldlvl);
126    return NULL;
127 }
128
129 VOID 
130 MmInitKernelMap(PVOID BaseAddress)
131 {
132    NonPagedPoolBase = BaseAddress;
133    KeInitializeSpinLock(&AllocMapLock);
134    RtlInitializeBitMap(&AllocMap, (PVOID)&AllocMapBuffer, ALLOC_MAP_SIZE);
135    RtlClearAllBits(&AllocMap);
136 }
137
138 VOID
139 MiFreeNonPagedPoolRegion(PVOID Addr, ULONG Count, BOOLEAN Free)
140 {
141   ULONG i;
142   ULONG Base = (Addr - NonPagedPoolBase) / PAGE_SIZE;
143   KIRQL oldlvl;
144   
145   for (i = 0; i < Count; i++)
146   {
147       MmDeleteVirtualMapping(NULL, 
148                              Addr + (i * PAGE_SIZE), 
149                              Free, 
150                              NULL, 
151                              NULL);
152   }
153   KeAcquireSpinLock(&AllocMapLock, &oldlvl);
154   RtlClearBits(&AllocMap, Base, Count);
155   AllocMapHint = min(AllocMapHint, Base);
156   KeReleaseSpinLock(&AllocMapLock, oldlvl);
157 }
158
159 PVOID
160 MiAllocNonPagedPoolRegion(ULONG nr_pages)
161 /*
162  * FUNCTION: Allocates a region of pages within the nonpaged pool area
163  */
164 {
165    ULONG Base;
166    KIRQL oldlvl;
167
168    KeAcquireSpinLock(&AllocMapLock, &oldlvl);
169    Base = RtlFindClearBitsAndSet(&AllocMap, nr_pages, AllocMapHint);
170    if (Base == 0xFFFFFFFF)
171    {
172       DbgPrint("CRITICAL: Out of non-paged pool space\n");
173       KeBugCheck(0);
174    }
175    if (AllocMapHint == Base)
176    {
177       AllocMapHint += nr_pages;
178    }
179    KeReleaseSpinLock(&AllocMapLock, oldlvl);
180    DPRINT("returning %x\n",NonPagedPoolBase + Base * PAGE_SIZE);
181    return NonPagedPoolBase + Base * PAGE_SIZE;
182 }