update for HEAD-2003021201
[reactos.git] / ntoskrnl / mm / mdl.c
1 /* $Id$
2  *
3  * COPYRIGHT:    See COPYING in the top level directory
4  * PROJECT:      ReactOS kernel
5  * FILE:         ntoskrnl/mm/mdl.c
6  * PURPOSE:      Manipulates MDLs
7  * PROGRAMMER:   David Welch (welch@cwcom.net)
8  * UPDATE HISTORY: 
9  *               27/05/98: Created
10  */
11
12 /* INCLUDES ****************************************************************/
13
14 #include <ddk/ntddk.h>
15 #include <internal/mm.h>
16 #include <internal/ps.h>
17 #include <internal/pool.h>
18 #include <ntos/minmax.h>
19
20 #define NDEBUG
21 #include <internal/debug.h>
22
23 /* GLOBALS *******************************************************************/
24
25 #define TAG_MDL    TAG('M', 'M', 'D', 'L')
26
27 #define MI_MDL_MAPPING_REGION_SIZE       (256*1024*1024)
28
29 static PVOID MiMdlMappingRegionBase = NULL;
30 static RTL_BITMAP MiMdlMappingRegionAllocMap;
31 static ULONG MiMdlMappingRegionHint;
32 static KSPIN_LOCK MiMdlMappingRegionLock;
33
34 /* FUNCTIONS *****************************************************************/
35
36 VOID
37 MmInitializeMdlImplementation(VOID)
38 {
39   MEMORY_AREA* Result;
40   NTSTATUS Status;
41   PVOID Buffer;
42
43   MiMdlMappingRegionHint = 0;
44   MiMdlMappingRegionBase = NULL;
45
46   MmLockAddressSpace(MmGetKernelAddressSpace());
47   Status = MmCreateMemoryArea(NULL,
48                               MmGetKernelAddressSpace(),
49                               MEMORY_AREA_MDL_MAPPING,
50                               &MiMdlMappingRegionBase,
51                               MI_MDL_MAPPING_REGION_SIZE,
52                               0,
53                               &Result,
54                               FALSE);
55   if (!NT_SUCCESS(Status))
56     {
57       MmUnlockAddressSpace(MmGetKernelAddressSpace());
58       KeBugCheck(0);
59     }
60   MmUnlockAddressSpace(MmGetKernelAddressSpace());
61
62   Buffer = ExAllocatePool(NonPagedPool, MI_MDL_MAPPING_REGION_SIZE / (PAGE_SIZE * 8));
63
64   RtlInitializeBitMap(&MiMdlMappingRegionAllocMap, Buffer, MI_MDL_MAPPING_REGION_SIZE / PAGE_SIZE);
65   RtlClearAllBits(&MiMdlMappingRegionAllocMap);
66
67   KeInitializeSpinLock(&MiMdlMappingRegionLock);
68 }
69
70 PVOID 
71 MmGetMdlPageAddress(PMDL Mdl, PVOID Offset)
72 {
73    PULONG MdlPages;
74    
75    MdlPages = (PULONG)(Mdl + 1);
76    
77    return((PVOID)MdlPages[((ULONG)Offset) / PAGE_SIZE]);
78 }
79
80 VOID STDCALL 
81 MmUnlockPages(PMDL Mdl)
82 /*
83  * FUNCTION: Unlocks the physical pages described by a given MDL
84  * ARGUMENTS:
85  *      MemoryDescriptorList = MDL describing the buffer to be unlocked
86  * NOTES: The memory described by the specified MDL must have been locked
87  * previously by a call to MmProbeAndLockPages. As the pages unlocked, the
88  * MDL is updated
89  */
90 {
91    ULONG i;
92    PULONG MdlPages;
93    
94    /* 
95     * FIXME: I don't know whether this right, but it looks sensible 
96     */
97    if ((Mdl->MdlFlags & MDL_SOURCE_IS_NONPAGED_POOL) ||
98        (Mdl->MdlFlags & MDL_IO_PAGE_READ))
99      {
100         return;
101      }
102    
103    /*
104     * FIXME: Seems sensible 
105     */
106    if (!(Mdl->MdlFlags & MDL_PAGES_LOCKED))
107      {
108         return;
109      }
110    
111    MdlPages = (PULONG)(Mdl + 1);
112    for (i=0; i<(PAGE_ROUND_UP(Mdl->ByteCount+Mdl->ByteOffset)/PAGE_SIZE); i++)
113      {
114         MmUnlockPage((LARGE_INTEGER)(LONGLONG)MdlPages[i]);
115         MmDereferencePage((LARGE_INTEGER)(LONGLONG)MdlPages[i]);
116      }   
117    Mdl->MdlFlags = Mdl->MdlFlags & (~MDL_PAGES_LOCKED);
118 }
119
120 PVOID STDCALL
121 MmMapLockedPages(PMDL Mdl, KPROCESSOR_MODE AccessMode)
122 /*
123  * FUNCTION: Maps the physical pages described by a given MDL
124  * ARGUMENTS:
125  *       Mdl = Points to an MDL updated by MmProbeAndLockPages
126  *       AccessMode = Specifies the portion of the address space to map the
127  *                    pages.
128  * RETURNS: The base virtual address that maps the locked pages for the
129  * range described by the MDL
130  */
131 {
132    PVOID Base;
133    ULONG i;
134    PULONG MdlPages;
135    KIRQL oldIrql;
136    ULONG RegionSize;
137    ULONG StartingOffset;
138    
139    DPRINT("MmMapLockedPages(Mdl %x, AccessMode %x)\n", Mdl, AccessMode);
140
141    if (Mdl->MdlFlags & MDL_SOURCE_IS_NONPAGED_POOL)
142      {
143        return(Mdl->MappedSystemVa);
144      }
145
146    if (AccessMode == UserMode)
147      {
148        DPRINT1("MDL mapping to user-mode not yet handled.\n");
149        KeBugCheck(0);
150      }
151
152    /* Calculate the number of pages required. */
153    RegionSize = PAGE_ROUND_UP(Mdl->ByteCount + Mdl->ByteOffset) / PAGE_SIZE;
154
155    /* Allocate that number of pages from the mdl mapping region. */
156    KeAcquireSpinLock(&MiMdlMappingRegionLock, &oldIrql);
157
158    StartingOffset = RtlFindClearBitsAndSet(&MiMdlMappingRegionAllocMap, RegionSize, MiMdlMappingRegionHint);
159   
160    if (StartingOffset == 0xffffffff)
161    {
162       DPRINT1("Out of MDL mapping space\n");
163       KeBugCheck(0);
164    }
165
166    Base = MiMdlMappingRegionBase + StartingOffset * PAGE_SIZE;
167
168    if (MiMdlMappingRegionHint == StartingOffset)
169    {
170        MiMdlMappingRegionHint +=RegionSize; 
171    }
172
173    KeReleaseSpinLock(&MiMdlMappingRegionLock, oldIrql);
174
175    /* Set the virtual mappings for the MDL pages. */
176    MdlPages = (PULONG)(Mdl + 1);
177    for (i = 0; i < RegionSize; i++)
178      {
179        NTSTATUS Status;
180        Status = MmCreateVirtualMapping(NULL,
181                                        (PVOID)((ULONG)Base+(i*PAGE_SIZE)),
182                                        PAGE_READWRITE,
183                                        (LARGE_INTEGER)(LONGLONG)MdlPages[i],
184                                        FALSE);
185        if (!NT_SUCCESS(Status))
186          {
187            DbgPrint("Unable to create virtual mapping\n");
188            KeBugCheck(0);
189          }
190      }
191
192    /* Mark the MDL has having being mapped. */
193    Mdl->MdlFlags = Mdl->MdlFlags | MDL_MAPPED_TO_SYSTEM_VA;
194    Mdl->MappedSystemVa = Base + Mdl->ByteOffset;
195    return(Base + Mdl->ByteOffset);
196 }
197
198 VOID STDCALL 
199 MmUnmapLockedPages(PVOID BaseAddress, PMDL Mdl)
200 /*
201  * FUNCTION: Releases a mapping set up by a preceding call to MmMapLockedPages
202  * ARGUMENTS:
203  *         BaseAddress = Base virtual address to which the pages were mapped
204  *         MemoryDescriptorList = MDL describing the mapped pages
205  */
206 {
207   KIRQL oldIrql;
208   ULONG i;
209   ULONG RegionSize;
210   ULONG Base;
211
212   DPRINT("MmUnmapLockedPages(BaseAddress %x, Mdl %x)\n", BaseAddress, Mdl);
213
214   /*
215    * In this case, the MDL has the same system address as the base address
216    * so there is no need to free it
217    */
218   if (Mdl->MdlFlags & MDL_SOURCE_IS_NONPAGED_POOL)
219     {
220       return;
221     }
222
223   /* Calculate the number of pages we mapped. */
224   RegionSize = PAGE_ROUND_UP(Mdl->ByteCount + Mdl->ByteOffset) / PAGE_SIZE;
225   BaseAddress -= Mdl->ByteOffset;
226
227   /* Unmap all the pages. */
228   for (i = 0; i < RegionSize; i++)
229     {
230       MmDeleteVirtualMapping(NULL, 
231                              BaseAddress + (i * PAGE_SIZE),
232                              FALSE,
233                              NULL,
234                              NULL);
235     }
236
237   KeAcquireSpinLock(&MiMdlMappingRegionLock, &oldIrql);
238   /* Deallocate all the pages used. */
239   Base = (ULONG)(BaseAddress - MiMdlMappingRegionBase) / PAGE_SIZE;
240   
241   RtlClearBits(&MiMdlMappingRegionAllocMap, Base, RegionSize);
242
243   MiMdlMappingRegionHint = min (MiMdlMappingRegionHint, Base);
244
245   KeReleaseSpinLock(&MiMdlMappingRegionLock, oldIrql);
246   
247   /* Reset the MDL state. */
248   Mdl->MdlFlags = Mdl->MdlFlags & ~MDL_MAPPED_TO_SYSTEM_VA;
249   Mdl->MappedSystemVa = NULL;
250 }
251
252
253 VOID 
254 MmBuildMdlFromPages(PMDL Mdl, PULONG Pages)
255 {
256    ULONG i;
257    PULONG MdlPages;
258    
259    Mdl->MdlFlags = Mdl->MdlFlags | 
260      (MDL_PAGES_LOCKED | MDL_IO_PAGE_READ);
261    
262    MdlPages = (PULONG)(Mdl + 1);
263    
264    for (i=0;i<(PAGE_ROUND_UP(Mdl->ByteOffset+Mdl->ByteCount)/PAGE_SIZE);i++)
265      {
266         MdlPages[i] = Pages[i];
267      }
268 }
269
270 VOID STDCALL MmProbeAndLockPages (PMDL Mdl,
271                                   KPROCESSOR_MODE AccessMode,
272                                   LOCK_OPERATION Operation)
273 /*
274  * FUNCTION: Probes the specified pages, makes them resident and locks them
275  * ARGUMENTS:
276  *          Mdl = MDL to probe
277  *          AccessMode = Access at which to probe the buffer
278  *          Operation = Operation to probe for
279  */
280 {
281    PULONG MdlPages;
282    ULONG i, j;
283    ULONG NrPages;
284    NTSTATUS Status;
285    KPROCESSOR_MODE Mode;
286    PEPROCESS CurrentProcess;
287
288    DPRINT("MmProbeAndLockPages(Mdl %x)\n", Mdl);
289    
290    /*
291     * FIXME: Check behaviour against NT
292     */
293    if (Mdl->MdlFlags & MDL_PAGES_LOCKED)
294      {
295         return;
296      }
297    
298    CurrentProcess = PsGetCurrentProcess();
299
300    if (Mdl->Process != CurrentProcess)
301      {
302        KeAttachProcess(Mdl->Process);
303      }
304
305    if (Mdl->StartVa >= (PVOID)KERNEL_BASE)
306      {
307        Mode = KernelMode;
308      }
309    else
310      {
311        Mode = UserMode;
312      }
313
314    /*
315     * Lock the pages
316     */
317
318    MmLockAddressSpace(&Mdl->Process->AddressSpace);
319    MdlPages = (ULONG *)(Mdl + 1);      
320    NrPages = PAGE_ROUND_UP(Mdl->ByteOffset + Mdl->ByteCount) / PAGE_SIZE;
321    for (i = 0; i < NrPages; i++)
322      {
323         PVOID Address;
324         
325         Address = Mdl->StartVa + (i*PAGE_SIZE);       
326         
327         if (!MmIsPagePresent(NULL, Address))
328           {
329             Status = MmNotPresentFault(Mode, (ULONG)Address, TRUE);
330             if (!NT_SUCCESS(Status))
331               {
332                 for (j = 0; j < i; j++)
333                   {
334                     MmUnlockPage((LARGE_INTEGER)(LONGLONG)MdlPages[j]);
335                     MmDereferencePage((LARGE_INTEGER)(LONGLONG)MdlPages[j]);
336                   }
337                 ExRaiseStatus(Status);
338               }
339           }
340         else
341           {
342             MmLockPage(MmGetPhysicalAddressForProcess(NULL, Address));
343           }
344         if ((Operation == IoWriteAccess || Operation == IoModifyAccess) &&
345             (!(MmGetPageProtect(NULL, (PVOID)Address) & PAGE_READWRITE)))
346           {
347             Status = MmAccessFault(Mode, (ULONG)Address, TRUE);
348             if (!NT_SUCCESS(Status))
349               {
350                 for (j = 0; j < i; j++)
351                   {
352                         MmUnlockPage((LARGE_INTEGER)(LONGLONG)MdlPages[j]);
353                         MmDereferencePage(
354                                          (LARGE_INTEGER)(LONGLONG)MdlPages[j]);
355                   }
356                 ExRaiseStatus(Status);
357               }
358           }
359         MdlPages[i] = MmGetPhysicalAddressForProcess(NULL, Address).u.LowPart;
360         MmReferencePage((LARGE_INTEGER)(LONGLONG)MdlPages[i]);
361      }
362    MmUnlockAddressSpace(&Mdl->Process->AddressSpace);
363    if (Mdl->Process != CurrentProcess)
364      {
365        KeDetachProcess();
366      }
367    Mdl->MdlFlags = Mdl->MdlFlags | MDL_PAGES_LOCKED;
368 }
369
370
371 ULONG STDCALL MmSizeOfMdl (PVOID        Base,
372                            ULONG        Length)
373 /*
374  * FUNCTION: Returns the number of bytes to allocate for an MDL describing
375  * the given address range
376  * ARGUMENTS:
377  *         Base = base virtual address
378  *         Length = number of bytes to map
379  */
380 {
381    ULONG len;
382    
383    len = ADDRESS_AND_SIZE_TO_SPAN_PAGES(Base,Length);
384    
385    return(sizeof(MDL)+(len*sizeof(ULONG)));
386 }
387
388
389 VOID STDCALL 
390 MmBuildMdlForNonPagedPool (PMDL Mdl)
391 /*
392  * FUNCTION: Fills in the corresponding physical page array of a given 
393  * MDL for a buffer in nonpaged system space
394  * ARGUMENTS:
395  *        Mdl = Points to an MDL that supplies a virtual address, 
396  *              byte offset and length
397  */
398 {
399    ULONG va;
400    Mdl->MdlFlags = Mdl->MdlFlags | 
401      (MDL_SOURCE_IS_NONPAGED_POOL | MDL_PAGES_LOCKED);
402    for (va=0; va < ((Mdl->Size - sizeof(MDL)) / sizeof(ULONG)); va++)
403      {
404         ((PULONG)(Mdl + 1))[va] =
405             (MmGetPhysicalAddress(Mdl->StartVa + (va * PAGE_SIZE))).u.LowPart;
406      }
407    Mdl->MappedSystemVa = Mdl->StartVa + Mdl->ByteOffset;
408 }
409
410
411 PMDL STDCALL 
412 MmCreateMdl (PMDL       MemoryDescriptorList,
413              PVOID      Base,
414              ULONG      Length)
415 /*
416  * FUNCTION: Allocates and initalizes an MDL
417  * ARGUMENTS:
418  *          MemoryDescriptorList = Points to MDL to initalize. If this is
419  *                                 NULL then one is allocated
420  *          Base = Base virtual address of the buffer
421  *          Length = Length in bytes of the buffer
422  * RETURNS: A pointer to initalized MDL
423  */
424 {
425    if (MemoryDescriptorList == NULL)
426      {
427         ULONG Size;
428         
429         Size = MmSizeOfMdl(Base,Length);
430         MemoryDescriptorList = 
431           (PMDL)ExAllocatePoolWithTag(NonPagedPool, Size, TAG_MDL);
432         if (MemoryDescriptorList == NULL)
433           {
434              return(NULL);
435           }
436      }
437    
438    MmInitializeMdl(MemoryDescriptorList,Base,Length);
439    
440    return(MemoryDescriptorList);
441 }
442
443 VOID STDCALL 
444 MmMapMemoryDumpMdl (PVOID       Unknown0)
445 /*
446  * FIXME: Has something to do with crash dumps. Do we want to implement
447  * this?
448  */
449 {
450    UNIMPLEMENTED;
451 }
452
453 /* EOF */
454
455
456
457
458
459
460
461
462