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