mirror of
https://github.com/reactos/reactos.git
synced 2025-05-27 13:08:23 +00:00
- Major rewrite of Memory Descriptor List (MDL) implementation (moving it towards using System PTEs).
- MmCreateMdl, MmSizeOfMdl: No Change. - MmBuildMdlForNonPagedPool: Do not use MmGetPfnForProcess, just normal PMMPTE manipulation. - This seems to cause issues in certain scenarios, because in ReactOS, nonpaged pool, a resident and guaranteed resources, does not always have its PDEs mapped! - By calling MmGetPfnForProcess, this wound up in the annals of ReactOS mm code, which lazy-remapped the PDE. We detected this issue specifically in the cache manager, and fixed it there. It should not appear anywhere else. - MmAllocatePagesForMdl, MmAllocatePagesForMdlEx, MmFreePagesFromMdl: - The *Ex function is now implemented. - Allocating pages now uses MiAllocatePagesForMdl, which is based on the older MmAllocPagesSpecifyRange. - The code is cleaner, better commented, and better handles partial MDLs. - Cache flags are still ignored (so the Ex functionality isn't really there). - MmMapLockedPages, MmMapLockedPagesSpecifyCache, MmUnmapLockedPages: - These functions now use System PTEs for the mappings, instead of the hacked-up "MDL Mapping Space". - This frees up 256MB of Kernel Virtual Address Space. - Takes advantage of all System PTE functionality. - Once again, optimizations in the System PTE code will be felt here. - For user-space mappings however, the old code is still kept and used. - MiMapLockedPagesInUserSpace and MiUnMapLockedPagesInUserSpace are now in virtual.c and provide this. - MmProbeAndLockPages, MmUnlockPages: - The pages are actually probed now, in SEH. This did not seem to happen before (did someone misread the function's name?) - Probe for write is only done for write access to user pages (as documented). - We do not probe/check for write access for kernel requests (force Operation to be IoReadAccess). - Proper locking is used now: Address Space lock for user mappings, PFN lock for kernel mappings. - Faulting in pages (to make them available before locking) is now done outside the address space/PFN lock. - You don't want to be holding a spinlock/mutex while doing disk I/O! - For write/modify access, if the PTE is not writable, fail the request since the PTE protection overrides. - However, if the PTE is writable but also copy on write, then we'll fault the page in for write access, which is a legitimate operation for certain user-mode scenarios. - The old version always provided the CopyOnWrite behavior, even for non-CopyOnWrite pages! - Reference and lock every valid page that has a PFN entry (non-I/O Pages). - The older code did not seem to lock pages that had to be faulted in (weren't already valid). - Cleanup the cleanup code (no pun intended). Because we now mark the pages as locked early-on, and because of changes in MmUnlockPages, we can simply use MmUnlockPages in case of error, since it will be able to fully back-out and references/locks that we did. - Previous code attempted to do this on its own, in a pretty inconsistent manner, which would leave page leaks (both in references and lock count). - In MmUnlockPages, not as many changes, but we now: - Still make sure that an I/O Mapping MDL doesn't have valid PFN database pages (non-I/O). - An MDL can cover pages that are both I/O mapped and RAM mapped, so we have to unlock/dereference the latter instead of skipping them as the old code did. - Use the PFN lock when checking pages and unlocking/dereferencing them. - Overall, non-complete MDLs are now marked by having a -1 PFN, and the MDL code has been updated to early-break out of page-scanning loops and/or ignore such pages, which can happen in a sparse MDL. - Implementation has been tested on VMWare and QEMU for a variety of tasks and was found to be reliable and stable. svn path=/trunk/; revision=41707
This commit is contained in:
parent
fdf20e8c9a
commit
bd27f08336
8 changed files with 1736 additions and 923 deletions
|
@ -247,6 +247,17 @@ WriteCacheSegment(PCACHE_SEGMENT CacheSeg)
|
||||||
{
|
{
|
||||||
Size = CacheSeg->Bcb->CacheSegmentSize;
|
Size = CacheSeg->Bcb->CacheSegmentSize;
|
||||||
}
|
}
|
||||||
|
//
|
||||||
|
// Nonpaged pool PDEs in ReactOS must actually be synchronized between the
|
||||||
|
// MmGlobalPageDirectory and the real system PDE directory. What a mess...
|
||||||
|
//
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
MmGetPfnForProcess(NULL, (PVOID)((ULONG_PTR)CacheSeg->BaseAddress + (i << PAGE_SHIFT)));
|
||||||
|
} while (++i < (Size >> PAGE_SHIFT));
|
||||||
|
}
|
||||||
Mdl = alloca(MmSizeOfMdl(CacheSeg->BaseAddress, Size));
|
Mdl = alloca(MmSizeOfMdl(CacheSeg->BaseAddress, Size));
|
||||||
MmInitializeMdl(Mdl, CacheSeg->BaseAddress, Size);
|
MmInitializeMdl(Mdl, CacheSeg->BaseAddress, Size);
|
||||||
MmBuildMdlForNonPagedPool(Mdl);
|
MmBuildMdlForNonPagedPool(Mdl);
|
||||||
|
|
1318
reactos/ntoskrnl/mm/ARM3/mdlsup.c
Normal file
1318
reactos/ntoskrnl/mm/ARM3/mdlsup.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -53,6 +53,8 @@ extern PMMPTE MiFirstReservedZeroingPte;
|
||||||
extern MI_PFN_CACHE_ATTRIBUTE MiPlatformCacheAttributes[2][MmMaximumCacheType];
|
extern MI_PFN_CACHE_ATTRIBUTE MiPlatformCacheAttributes[2][MmMaximumCacheType];
|
||||||
extern PPHYSICAL_MEMORY_DESCRIPTOR MmPhysicalMemoryBlock;
|
extern PPHYSICAL_MEMORY_DESCRIPTOR MmPhysicalMemoryBlock;
|
||||||
extern ULONG MmBootImageSize;
|
extern ULONG MmBootImageSize;
|
||||||
|
extern PMMPTE MmSystemPtesStart[MaximumPtePoolTypes];
|
||||||
|
extern PMMPTE MmSystemPtesEnd[MaximumPtePoolTypes];
|
||||||
|
|
||||||
VOID
|
VOID
|
||||||
NTAPI
|
NTAPI
|
||||||
|
@ -106,4 +108,31 @@ MiCheckForContiguousMemory(
|
||||||
IN MI_PFN_CACHE_ATTRIBUTE CacheAttribute
|
IN MI_PFN_CACHE_ATTRIBUTE CacheAttribute
|
||||||
);
|
);
|
||||||
|
|
||||||
|
PMDL
|
||||||
|
NTAPI
|
||||||
|
MiAllocatePagesForMdl(
|
||||||
|
IN PHYSICAL_ADDRESS LowAddress,
|
||||||
|
IN PHYSICAL_ADDRESS HighAddress,
|
||||||
|
IN PHYSICAL_ADDRESS SkipBytes,
|
||||||
|
IN SIZE_T TotalBytes,
|
||||||
|
IN MI_PFN_CACHE_ATTRIBUTE CacheAttribute,
|
||||||
|
IN ULONG Flags
|
||||||
|
);
|
||||||
|
|
||||||
|
PVOID
|
||||||
|
NTAPI
|
||||||
|
MiMapLockedPagesInUserSpace(
|
||||||
|
IN PMDL Mdl,
|
||||||
|
IN PVOID BaseVa,
|
||||||
|
IN MEMORY_CACHING_TYPE CacheType,
|
||||||
|
IN PVOID BaseAddress
|
||||||
|
);
|
||||||
|
|
||||||
|
VOID
|
||||||
|
NTAPI
|
||||||
|
MiUnmapLockedPagesInUserSpace(
|
||||||
|
IN PVOID BaseAddress,
|
||||||
|
IN PMDL Mdl
|
||||||
|
);
|
||||||
|
|
||||||
/* EOF */
|
/* EOF */
|
||||||
|
|
|
@ -432,13 +432,280 @@ MiFindContiguousPages(IN PFN_NUMBER LowestPfn,
|
||||||
}
|
}
|
||||||
} while (++i != MmPhysicalMemoryBlock->NumberOfRuns);
|
} while (++i != MmPhysicalMemoryBlock->NumberOfRuns);
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// And if we get here, it means no suitable physical memory runs were found
|
// And if we get here, it means no suitable physical memory runs were found
|
||||||
//
|
//
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PMDL
|
||||||
|
NTAPI
|
||||||
|
MiAllocatePagesForMdl(IN PHYSICAL_ADDRESS LowAddress,
|
||||||
|
IN PHYSICAL_ADDRESS HighAddress,
|
||||||
|
IN PHYSICAL_ADDRESS SkipBytes,
|
||||||
|
IN SIZE_T TotalBytes,
|
||||||
|
IN MI_PFN_CACHE_ATTRIBUTE CacheAttribute,
|
||||||
|
IN ULONG MdlFlags)
|
||||||
|
{
|
||||||
|
PMDL Mdl;
|
||||||
|
PFN_NUMBER PageCount, LowPage, HighPage, SkipPages, PagesFound = 0, Page;
|
||||||
|
PPFN_NUMBER MdlPage, LastMdlPage;
|
||||||
|
KIRQL OldIrql;
|
||||||
|
PLIST_ENTRY ListEntry;
|
||||||
|
PPHYSICAL_PAGE Pfn1;
|
||||||
|
ULONG LookForZeroedPages;
|
||||||
|
ASSERT (KeGetCurrentIrql() <= APC_LEVEL);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Convert the low address into a PFN
|
||||||
|
//
|
||||||
|
LowPage = (PFN_NUMBER)(LowAddress.QuadPart >> PAGE_SHIFT);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Convert, and normalize, the high address into a PFN
|
||||||
|
//
|
||||||
|
HighPage = (PFN_NUMBER)(HighAddress.QuadPart >> PAGE_SHIFT);
|
||||||
|
if (HighPage > MmHighestPhysicalPage) HighPage = MmHighestPhysicalPage;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Validate skipbytes and convert them into pages
|
||||||
|
//
|
||||||
|
if (BYTE_OFFSET(SkipBytes.LowPart)) return NULL;
|
||||||
|
SkipPages = (PFN_NUMBER)(SkipBytes.QuadPart >> PAGE_SHIFT);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Now compute the number of pages the MDL will cover
|
||||||
|
//
|
||||||
|
PageCount = (PFN_NUMBER)ADDRESS_AND_SIZE_TO_SPAN_PAGES(0, TotalBytes);
|
||||||
|
do
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Try creating an MDL for these many pages
|
||||||
|
//
|
||||||
|
Mdl = MmCreateMdl(NULL, NULL, PageCount << PAGE_SHIFT);
|
||||||
|
if (Mdl) break;
|
||||||
|
|
||||||
|
//
|
||||||
|
// This function is not required to return the amount of pages requested
|
||||||
|
// In fact, it can return as little as 1 page, and callers are supposed
|
||||||
|
// to deal with this scenario. So re-attempt the allocation with less
|
||||||
|
// pages than before, and see if it worked this time.
|
||||||
|
//
|
||||||
|
PageCount -= (PageCount >> 4);
|
||||||
|
} while (PageCount);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Wow, not even a single page was around!
|
||||||
|
//
|
||||||
|
if (!Mdl) return NULL;
|
||||||
|
|
||||||
|
//
|
||||||
|
// This is where the page array starts....
|
||||||
|
//
|
||||||
|
MdlPage = (PPFN_NUMBER)(Mdl + 1);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Lock the PFN database
|
||||||
|
//
|
||||||
|
OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Are we looking for any pages, without discriminating?
|
||||||
|
//
|
||||||
|
if ((LowPage == 0) && (HighPage == MmHighestPhysicalPage))
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Well then, let's go shopping
|
||||||
|
//
|
||||||
|
while (PagesFound < PageCount)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Do we have zeroed pages?
|
||||||
|
//
|
||||||
|
if (!IsListEmpty(&FreeZeroedPageListHead))
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Grab a zero page
|
||||||
|
//
|
||||||
|
ListEntry = RemoveTailList(&FreeZeroedPageListHead);
|
||||||
|
}
|
||||||
|
else if (!IsListEmpty(&FreeUnzeroedPageListHead))
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Nope, grab an unzeroed page
|
||||||
|
//
|
||||||
|
ListEntry = RemoveTailList(&FreeUnzeroedPageListHead);
|
||||||
|
UnzeroedPageCount--;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// This is not good... hopefully we have at least SOME pages
|
||||||
|
//
|
||||||
|
ASSERT(PagesFound);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Get the PFN entry for this page
|
||||||
|
//
|
||||||
|
Pfn1 = CONTAINING_RECORD(ListEntry, PHYSICAL_PAGE, ListEntry);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Make sure it's really free
|
||||||
|
//
|
||||||
|
ASSERT(Pfn1->Flags.Type == MM_PHYSICAL_PAGE_FREE);
|
||||||
|
ASSERT(Pfn1->MapCount == 0);
|
||||||
|
ASSERT(Pfn1->ReferenceCount == 0);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Allocate it and mark it
|
||||||
|
//
|
||||||
|
Pfn1->Flags.Type = MM_PHYSICAL_PAGE_USED;
|
||||||
|
Pfn1->Flags.Consumer = MC_NPPOOL;
|
||||||
|
Pfn1->Flags.StartOfAllocation = 1;
|
||||||
|
Pfn1->Flags.EndOfAllocation = 1;
|
||||||
|
Pfn1->ReferenceCount = 1;
|
||||||
|
Pfn1->LockCount = 0;
|
||||||
|
Pfn1->MapCount = 0;
|
||||||
|
Pfn1->SavedSwapEntry = 0;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Decrease available pages
|
||||||
|
//
|
||||||
|
MmStats.NrSystemPages++;
|
||||||
|
MmStats.NrFreePages--;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Save it into the MDL
|
||||||
|
//
|
||||||
|
*MdlPage++ = MiGetPfnEntryIndex(Pfn1);
|
||||||
|
PagesFound++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// You want specific range of pages. We'll do this in two runs
|
||||||
|
//
|
||||||
|
for (LookForZeroedPages = 1; LookForZeroedPages >= 0; LookForZeroedPages--)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Scan the range you specified
|
||||||
|
//
|
||||||
|
for (Page = LowPage; Page < HighPage; Page++)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Get the PFN entry for this page
|
||||||
|
//
|
||||||
|
Pfn1 = MiGetPfnEntry(Page);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Make sure it's free and if this is our first pass, zeroed
|
||||||
|
//
|
||||||
|
if (Pfn1->Flags.Type != MM_PHYSICAL_PAGE_FREE) continue;
|
||||||
|
if (Pfn1->Flags.Zero != LookForZeroedPages) continue;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Sanity checks
|
||||||
|
//
|
||||||
|
ASSERT(Pfn1->MapCount == 0);
|
||||||
|
ASSERT(Pfn1->ReferenceCount == 0);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Now setup the page and mark it
|
||||||
|
//
|
||||||
|
Pfn1->Flags.Type = MM_PHYSICAL_PAGE_USED;
|
||||||
|
Pfn1->Flags.Consumer = MC_NPPOOL;
|
||||||
|
Pfn1->ReferenceCount = 1;
|
||||||
|
Pfn1->Flags.StartOfAllocation = 1;
|
||||||
|
Pfn1->Flags.EndOfAllocation = 1;
|
||||||
|
Pfn1->LockCount = 0;
|
||||||
|
Pfn1->MapCount = 0;
|
||||||
|
Pfn1->SavedSwapEntry = 0;
|
||||||
|
|
||||||
|
//
|
||||||
|
// If this page was unzeroed, we've consumed such a page
|
||||||
|
//
|
||||||
|
if (!Pfn1->Flags.Zero) UnzeroedPageCount--;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Decrease available pages
|
||||||
|
//
|
||||||
|
MmStats.NrSystemPages++;
|
||||||
|
MmStats.NrFreePages--;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Save this page into the MDL
|
||||||
|
//
|
||||||
|
*MdlPage++ = Page;
|
||||||
|
if (++PagesFound == PageCount) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// If the first pass was enough, don't keep going, otherwise, go again
|
||||||
|
//
|
||||||
|
if (PagesFound == PageCount) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Now release the PFN count
|
||||||
|
//
|
||||||
|
KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
|
||||||
|
|
||||||
|
//
|
||||||
|
// We might've found less pages, but not more ;-)
|
||||||
|
//
|
||||||
|
if (PagesFound != PageCount) ASSERT(PagesFound < PageCount);
|
||||||
|
if (!PagesFound)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// If we didn' tfind any pages at all, fail
|
||||||
|
//
|
||||||
|
DPRINT1("NO MDL PAGES!\n");
|
||||||
|
ExFreePool(Mdl);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Write out how many pages we found
|
||||||
|
//
|
||||||
|
Mdl->ByteCount = (ULONG)(PagesFound << PAGE_SHIFT);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Terminate the MDL array if there's certain missing pages
|
||||||
|
//
|
||||||
|
if (PagesFound != PageCount) *MdlPage = -1;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Now go back and loop over all the MDL pages
|
||||||
|
//
|
||||||
|
MdlPage = (PPFN_NUMBER)(Mdl + 1);
|
||||||
|
LastMdlPage = MdlPage + PagesFound;
|
||||||
|
while (MdlPage < LastMdlPage)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Check if we've reached the end
|
||||||
|
//
|
||||||
|
Page = *MdlPage;
|
||||||
|
if (Page == -1) break;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Get the PFN entry for the page and check if we should zero it out
|
||||||
|
//
|
||||||
|
Pfn1 = MiGetPfnEntry(Page);
|
||||||
|
if (Pfn1->Flags.Zero == 0) MiZeroPage(Page);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// We're done, mark the pages as locked (should we lock them, though???)
|
||||||
|
//
|
||||||
|
Mdl->Process = NULL;
|
||||||
|
Mdl->MdlFlags |= MDL_PAGES_LOCKED;
|
||||||
|
return Mdl;
|
||||||
|
}
|
||||||
|
|
||||||
PFN_TYPE
|
PFN_TYPE
|
||||||
NTAPI
|
NTAPI
|
||||||
MmAllocEarlyPage(VOID)
|
MmAllocEarlyPage(VOID)
|
||||||
|
|
|
@ -1,918 +0,0 @@
|
||||||
/*
|
|
||||||
* COPYRIGHT: See COPYING in the top level directory
|
|
||||||
* PROJECT: ReactOS kernel
|
|
||||||
* FILE: ntoskrnl/mm/mdl.c
|
|
||||||
* PURPOSE: Manipulates MDLs
|
|
||||||
*
|
|
||||||
* PROGRAMMERS: David Welch (welch@cwcom.net)
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* INCLUDES ****************************************************************/
|
|
||||||
|
|
||||||
#include <ntoskrnl.h>
|
|
||||||
#define NDEBUG
|
|
||||||
#include <debug.h>
|
|
||||||
|
|
||||||
#if defined (ALLOC_PRAGMA)
|
|
||||||
#pragma alloc_text(INIT, MmInitializeMdlImplementation)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* GLOBALS *******************************************************************/
|
|
||||||
|
|
||||||
#define TAG_MDL TAG('M', 'D', 'L', ' ')
|
|
||||||
#define MI_MDL_MAPPING_REGION_SIZE (256*1024*1024)
|
|
||||||
|
|
||||||
PVOID MiMdlMappingRegionBase = NULL;
|
|
||||||
RTL_BITMAP MiMdlMappingRegionAllocMap;
|
|
||||||
ULONG MiMdlMappingRegionHint;
|
|
||||||
KSPIN_LOCK MiMdlMappingRegionLock;
|
|
||||||
|
|
||||||
/* PRIVATE FUNCTIONS **********************************************************/
|
|
||||||
|
|
||||||
VOID
|
|
||||||
INIT_FUNCTION
|
|
||||||
NTAPI
|
|
||||||
MmInitializeMdlImplementation(VOID)
|
|
||||||
{
|
|
||||||
MEMORY_AREA* Result;
|
|
||||||
NTSTATUS Status;
|
|
||||||
PVOID Buffer;
|
|
||||||
PHYSICAL_ADDRESS BoundaryAddressMultiple;
|
|
||||||
|
|
||||||
BoundaryAddressMultiple.QuadPart = 0;
|
|
||||||
MiMdlMappingRegionHint = 0;
|
|
||||||
MiMdlMappingRegionBase = NULL;
|
|
||||||
|
|
||||||
MmLockAddressSpace(MmGetKernelAddressSpace());
|
|
||||||
Status = MmCreateMemoryArea(MmGetKernelAddressSpace(),
|
|
||||||
MEMORY_AREA_MDL_MAPPING,
|
|
||||||
&MiMdlMappingRegionBase,
|
|
||||||
MI_MDL_MAPPING_REGION_SIZE,
|
|
||||||
PAGE_READWRITE,
|
|
||||||
&Result,
|
|
||||||
FALSE,
|
|
||||||
0,
|
|
||||||
BoundaryAddressMultiple);
|
|
||||||
if (!NT_SUCCESS(Status))
|
|
||||||
{
|
|
||||||
MmUnlockAddressSpace(MmGetKernelAddressSpace());
|
|
||||||
KeBugCheck(MEMORY_MANAGEMENT);
|
|
||||||
}
|
|
||||||
MmUnlockAddressSpace(MmGetKernelAddressSpace());
|
|
||||||
|
|
||||||
Buffer = ExAllocatePoolWithTag(NonPagedPool,
|
|
||||||
MI_MDL_MAPPING_REGION_SIZE / (PAGE_SIZE * 8),
|
|
||||||
TAG_MDL);
|
|
||||||
|
|
||||||
RtlInitializeBitMap(&MiMdlMappingRegionAllocMap, Buffer, MI_MDL_MAPPING_REGION_SIZE / PAGE_SIZE);
|
|
||||||
RtlClearAllBits(&MiMdlMappingRegionAllocMap);
|
|
||||||
|
|
||||||
KeInitializeSpinLock(&MiMdlMappingRegionLock);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* PUBLIC FUNCTIONS ***********************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @implemented
|
|
||||||
*/
|
|
||||||
PMDL
|
|
||||||
NTAPI
|
|
||||||
MmCreateMdl(IN PMDL Mdl,
|
|
||||||
IN PVOID Base,
|
|
||||||
IN ULONG Length)
|
|
||||||
{
|
|
||||||
ULONG Size;
|
|
||||||
|
|
||||||
/* Check if we don't have an MDL built */
|
|
||||||
if (!Mdl)
|
|
||||||
{
|
|
||||||
/* Calcualte the size we'll need and allocate the MDL */
|
|
||||||
Size = MmSizeOfMdl(Base, Length);
|
|
||||||
Mdl = ExAllocatePoolWithTag(NonPagedPool, Size, TAG_MDL);
|
|
||||||
if (!Mdl) return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Initialize it */
|
|
||||||
MmInitializeMdl(Mdl, Base, Length);
|
|
||||||
DPRINT("Creating MDL: %p\n", Mdl);
|
|
||||||
DPRINT("Base: %p. Length: %lx\n", Base, Length);
|
|
||||||
return Mdl;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @implemented
|
|
||||||
*/
|
|
||||||
ULONG
|
|
||||||
NTAPI
|
|
||||||
MmSizeOfMdl(IN PVOID Base,
|
|
||||||
IN ULONG Length)
|
|
||||||
{
|
|
||||||
/* Return the MDL size */
|
|
||||||
return sizeof(MDL) + (ADDRESS_AND_SIZE_TO_SPAN_PAGES(Base, Length) * sizeof(PFN_NUMBER));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @implemented
|
|
||||||
*/
|
|
||||||
VOID
|
|
||||||
NTAPI
|
|
||||||
MmBuildMdlForNonPagedPool(IN PMDL Mdl)
|
|
||||||
{
|
|
||||||
ULONG i;
|
|
||||||
ULONG PageCount;
|
|
||||||
PPFN_NUMBER MdlPages;
|
|
||||||
PVOID Base;
|
|
||||||
DPRINT("Building MDL: %p\n", Mdl);
|
|
||||||
|
|
||||||
/* Sanity checks */
|
|
||||||
ASSERT(Mdl->ByteCount != 0);
|
|
||||||
ASSERT((Mdl->MdlFlags & (MDL_PAGES_LOCKED |
|
|
||||||
MDL_MAPPED_TO_SYSTEM_VA |
|
|
||||||
MDL_SOURCE_IS_NONPAGED_POOL |
|
|
||||||
MDL_PARTIAL)) == 0);
|
|
||||||
|
|
||||||
/* We know the MDL isn't associated to a process now */
|
|
||||||
Mdl->Process = NULL;
|
|
||||||
|
|
||||||
/* Get page and VA information */
|
|
||||||
MdlPages = (PPFN_NUMBER)(Mdl + 1);
|
|
||||||
Base = Mdl->StartVa;
|
|
||||||
|
|
||||||
/* Set the system address and now get the page count */
|
|
||||||
Mdl->MappedSystemVa = (PVOID)((ULONG_PTR)Base + Mdl->ByteOffset);
|
|
||||||
PageCount = ADDRESS_AND_SIZE_TO_SPAN_PAGES(Mdl->MappedSystemVa, Mdl->ByteCount);
|
|
||||||
ASSERT(PageCount != 0);
|
|
||||||
|
|
||||||
/* Go through each page */
|
|
||||||
for (i = 0; i < PageCount; i++)
|
|
||||||
{
|
|
||||||
/* Map it */
|
|
||||||
*MdlPages++ = MmGetPfnForProcess(NULL,
|
|
||||||
(PVOID)((ULONG_PTR)Base + (i * PAGE_SIZE)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set the final flag */
|
|
||||||
Mdl->MdlFlags |= MDL_SOURCE_IS_NONPAGED_POOL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @implemented
|
|
||||||
*/
|
|
||||||
VOID
|
|
||||||
NTAPI
|
|
||||||
MmFreePagesFromMdl(IN PMDL Mdl)
|
|
||||||
{
|
|
||||||
PVOID Base;
|
|
||||||
PPFN_NUMBER Pages;
|
|
||||||
LONG NumberOfPages;
|
|
||||||
DPRINT("Freeing MDL: %p\n", Mdl);
|
|
||||||
|
|
||||||
/* Sanity checks */
|
|
||||||
ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
|
|
||||||
ASSERT((Mdl->MdlFlags & MDL_IO_SPACE) == 0);
|
|
||||||
ASSERT(((ULONG_PTR)Mdl->StartVa & (PAGE_SIZE - 1)) == 0);
|
|
||||||
|
|
||||||
/* Get address and page information */
|
|
||||||
Base = (PVOID)((ULONG_PTR)Mdl->StartVa + Mdl->ByteOffset);
|
|
||||||
NumberOfPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(Base, Mdl->ByteCount);
|
|
||||||
|
|
||||||
/* Loop all the MDL pages */
|
|
||||||
Pages = (PPFN_NUMBER)(Mdl + 1);
|
|
||||||
while (--NumberOfPages >= 0)
|
|
||||||
{
|
|
||||||
/* Dereference each one of them */
|
|
||||||
MmDereferencePage(Pages[NumberOfPages]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remove the pages locked flag */
|
|
||||||
Mdl->MdlFlags &= ~MDL_PAGES_LOCKED;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @implemented
|
|
||||||
*/
|
|
||||||
PVOID
|
|
||||||
NTAPI
|
|
||||||
MmMapLockedPages(IN PMDL Mdl,
|
|
||||||
IN KPROCESSOR_MODE AccessMode)
|
|
||||||
{
|
|
||||||
/* Call the extended version */
|
|
||||||
return MmMapLockedPagesSpecifyCache(Mdl,
|
|
||||||
AccessMode,
|
|
||||||
MmCached,
|
|
||||||
NULL,
|
|
||||||
TRUE,
|
|
||||||
HighPagePriority);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @implemented
|
|
||||||
*/
|
|
||||||
VOID
|
|
||||||
NTAPI
|
|
||||||
MmUnlockPages(IN PMDL Mdl)
|
|
||||||
{
|
|
||||||
ULONG i;
|
|
||||||
PPFN_NUMBER MdlPages;
|
|
||||||
PFN_NUMBER Page;
|
|
||||||
PEPROCESS Process;
|
|
||||||
PVOID Base;
|
|
||||||
ULONG Flags, PageCount;
|
|
||||||
DPRINT("Unlocking MDL: %p\n", Mdl);
|
|
||||||
|
|
||||||
/* Sanity checks */
|
|
||||||
ASSERT((Mdl->MdlFlags & MDL_PAGES_LOCKED) != 0);
|
|
||||||
ASSERT((Mdl->MdlFlags & MDL_SOURCE_IS_NONPAGED_POOL) == 0);
|
|
||||||
ASSERT((Mdl->MdlFlags & MDL_PARTIAL) == 0);
|
|
||||||
ASSERT(Mdl->ByteCount != 0);
|
|
||||||
|
|
||||||
/* Get the process associated and capture the flags which are volatile */
|
|
||||||
Process = Mdl->Process;
|
|
||||||
Flags = Mdl->MdlFlags;
|
|
||||||
|
|
||||||
/* Automagically undo any calls to MmGetSystemAddressForMdl's for this mdl */
|
|
||||||
if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA)
|
|
||||||
{
|
|
||||||
/* Unmap the pages from system spage */
|
|
||||||
MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get the page count */
|
|
||||||
MdlPages = (PPFN_NUMBER)(Mdl + 1);
|
|
||||||
Base = (PVOID)((ULONG_PTR)Mdl->StartVa + Mdl->ByteOffset);
|
|
||||||
PageCount = ADDRESS_AND_SIZE_TO_SPAN_PAGES(Base, Mdl->ByteCount);
|
|
||||||
ASSERT(PageCount != 0);
|
|
||||||
|
|
||||||
/* We don't support AWE */
|
|
||||||
if (Flags & MDL_DESCRIBES_AWE) ASSERT(FALSE);
|
|
||||||
|
|
||||||
/* Check if the buffer is mapped I/O space */
|
|
||||||
if (Flags & MDL_IO_SPACE)
|
|
||||||
{
|
|
||||||
/* Check if this was a wirte */
|
|
||||||
if (Flags & MDL_WRITE_OPERATION)
|
|
||||||
{
|
|
||||||
/* Windows keeps track of the modified bit */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if we have a process */
|
|
||||||
if (Process)
|
|
||||||
{
|
|
||||||
/* Handle the accounting of locked pages */
|
|
||||||
ASSERT(Process->NumberOfLockedPages > 0);
|
|
||||||
InterlockedExchangeAddSizeT(&Process->NumberOfLockedPages,
|
|
||||||
-PageCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We're done */
|
|
||||||
Mdl->MdlFlags &= ~MDL_IO_SPACE;
|
|
||||||
Mdl->MdlFlags &= ~MDL_PAGES_LOCKED;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if we have a process */
|
|
||||||
if (Process)
|
|
||||||
{
|
|
||||||
/* Handle the accounting of locked pages */
|
|
||||||
ASSERT(Process->NumberOfLockedPages > 0);
|
|
||||||
InterlockedExchangeAddSizeT(&Process->NumberOfLockedPages,
|
|
||||||
-PageCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Scan each page */
|
|
||||||
for (i = 0; i < PageCount; i++)
|
|
||||||
{
|
|
||||||
/* Get the page entry */
|
|
||||||
|
|
||||||
/* Unlock and dereference it */
|
|
||||||
Page = MdlPages[i];
|
|
||||||
MmUnlockPage(Page);
|
|
||||||
MmDereferencePage(Page);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We're done */
|
|
||||||
Mdl->MdlFlags &= ~MDL_PAGES_LOCKED;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @implemented
|
|
||||||
*/
|
|
||||||
VOID
|
|
||||||
NTAPI
|
|
||||||
MmUnmapLockedPages(IN PVOID BaseAddress,
|
|
||||||
IN PMDL Mdl)
|
|
||||||
{
|
|
||||||
KIRQL oldIrql;
|
|
||||||
ULONG i, PageCount;
|
|
||||||
ULONG Base;
|
|
||||||
MEMORY_AREA *MemoryArea;
|
|
||||||
DPRINT("Unmapping MDL: %p\n", Mdl);
|
|
||||||
DPRINT("Base: %p\n", BaseAddress);
|
|
||||||
|
|
||||||
/* Sanity check */
|
|
||||||
ASSERT(Mdl->ByteCount != 0);
|
|
||||||
|
|
||||||
/* Check if this is a kernel request */
|
|
||||||
if (BaseAddress > MM_HIGHEST_USER_ADDRESS)
|
|
||||||
{
|
|
||||||
/* Get base and count information */
|
|
||||||
Base = (ULONG_PTR)Mdl->StartVa + Mdl->ByteOffset;
|
|
||||||
PageCount = ADDRESS_AND_SIZE_TO_SPAN_PAGES(Base, Mdl->ByteCount);
|
|
||||||
|
|
||||||
/* Sanity checks */
|
|
||||||
ASSERT((Mdl->MdlFlags & MDL_PARENT_MAPPED_SYSTEM_VA) == 0);
|
|
||||||
ASSERT(PageCount != 0);
|
|
||||||
ASSERT(Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA);
|
|
||||||
|
|
||||||
/* ReactOS does not support this flag */
|
|
||||||
if (Mdl->MdlFlags & MDL_FREE_EXTRA_PTES) ASSERT(FALSE);
|
|
||||||
|
|
||||||
/* Remove flags */
|
|
||||||
Mdl->MdlFlags &= ~(MDL_MAPPED_TO_SYSTEM_VA |
|
|
||||||
MDL_PARTIAL_HAS_BEEN_MAPPED |
|
|
||||||
MDL_FREE_EXTRA_PTES);
|
|
||||||
|
|
||||||
/* If we came from non-paged pool, on ReactOS, we can leave */
|
|
||||||
if (Mdl->MdlFlags & MDL_SOURCE_IS_NONPAGED_POOL) return;
|
|
||||||
|
|
||||||
/* Loop each page */
|
|
||||||
BaseAddress = PAGE_ALIGN(BaseAddress);
|
|
||||||
for (i = 0; i < PageCount; i++)
|
|
||||||
{
|
|
||||||
/* Delete it */
|
|
||||||
MmDeleteVirtualMapping(NULL,
|
|
||||||
(PVOID)((ULONG_PTR)BaseAddress + (i * PAGE_SIZE)),
|
|
||||||
FALSE,
|
|
||||||
NULL,
|
|
||||||
NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Lock the mapping region */
|
|
||||||
KeAcquireSpinLock(&MiMdlMappingRegionLock, &oldIrql);
|
|
||||||
|
|
||||||
/* Deallocate all the pages used. */
|
|
||||||
Base = ((ULONG_PTR)BaseAddress - (ULONG_PTR)MiMdlMappingRegionBase) / PAGE_SIZE;
|
|
||||||
RtlClearBits(&MiMdlMappingRegionAllocMap, Base, PageCount);
|
|
||||||
MiMdlMappingRegionHint = min(MiMdlMappingRegionHint, Base);
|
|
||||||
|
|
||||||
/* Release the lock */
|
|
||||||
KeReleaseSpinLock(&MiMdlMappingRegionLock, oldIrql);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Sanity check */
|
|
||||||
ASSERT(Mdl->Process == PsGetCurrentProcess());
|
|
||||||
|
|
||||||
/* Find the memory area */
|
|
||||||
MemoryArea = MmLocateMemoryAreaByAddress(&Mdl->Process->Vm,
|
|
||||||
BaseAddress);
|
|
||||||
ASSERT(MemoryArea);
|
|
||||||
|
|
||||||
/* Free it */
|
|
||||||
MmFreeMemoryArea(&Mdl->Process->Vm,
|
|
||||||
MemoryArea,
|
|
||||||
NULL,
|
|
||||||
NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @implemented
|
|
||||||
*/
|
|
||||||
VOID
|
|
||||||
NTAPI
|
|
||||||
MmProbeAndLockPages(IN PMDL Mdl,
|
|
||||||
IN KPROCESSOR_MODE AccessMode,
|
|
||||||
IN LOCK_OPERATION Operation)
|
|
||||||
{
|
|
||||||
PPFN_TYPE MdlPages;
|
|
||||||
PVOID Base, Address;
|
|
||||||
ULONG i, j;
|
|
||||||
ULONG NrPages;
|
|
||||||
NTSTATUS Status = STATUS_SUCCESS;
|
|
||||||
PFN_TYPE Page;
|
|
||||||
PEPROCESS CurrentProcess;
|
|
||||||
PETHREAD Thread;
|
|
||||||
PMMSUPPORT AddressSpace;
|
|
||||||
KIRQL OldIrql = KeGetCurrentIrql();
|
|
||||||
DPRINT("Probing MDL: %p\n", Mdl);
|
|
||||||
|
|
||||||
/* Sanity checks */
|
|
||||||
ASSERT(Mdl->ByteCount != 0);
|
|
||||||
ASSERT(((ULONG)Mdl->ByteOffset & ~(PAGE_SIZE - 1)) == 0);
|
|
||||||
ASSERT(((ULONG_PTR)Mdl->StartVa & (PAGE_SIZE - 1)) == 0);
|
|
||||||
ASSERT((Mdl->MdlFlags & (MDL_PAGES_LOCKED |
|
|
||||||
MDL_MAPPED_TO_SYSTEM_VA |
|
|
||||||
MDL_SOURCE_IS_NONPAGED_POOL |
|
|
||||||
MDL_PARTIAL |
|
|
||||||
MDL_IO_SPACE)) == 0);
|
|
||||||
|
|
||||||
/* Get page and base information */
|
|
||||||
MdlPages = (PPFN_NUMBER)(Mdl + 1);
|
|
||||||
Base = (PVOID)Mdl->StartVa;
|
|
||||||
Address = (PVOID)((ULONG_PTR)Base + Mdl->ByteOffset);
|
|
||||||
NrPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(Address, Mdl->ByteCount);
|
|
||||||
ASSERT(NrPages != 0);
|
|
||||||
|
|
||||||
/* Check if this is an MDL in I/O Space */
|
|
||||||
if (Mdl->StartVa >= MmSystemRangeStart &&
|
|
||||||
MmGetPfnForProcess(NULL, Mdl->StartVa) >= MmHighestPhysicalPage)
|
|
||||||
{
|
|
||||||
/* Just loop each page */
|
|
||||||
for (i = 0; i < NrPages; i++)
|
|
||||||
{
|
|
||||||
/* And map it */
|
|
||||||
MdlPages[i] = MmGetPfnForProcess(NULL,
|
|
||||||
(PVOID)((ULONG_PTR)Mdl->StartVa + (i * PAGE_SIZE)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set the flags and exit */
|
|
||||||
Mdl->MdlFlags |= MDL_PAGES_LOCKED|MDL_IO_SPACE;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get the thread and process */
|
|
||||||
Thread = PsGetCurrentThread();
|
|
||||||
if (Address <= MM_HIGHEST_USER_ADDRESS)
|
|
||||||
{
|
|
||||||
/* Get the process */
|
|
||||||
CurrentProcess = PsGetCurrentProcess();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* No process */
|
|
||||||
CurrentProcess = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check what kind of operaiton this is */
|
|
||||||
if (Operation != IoReadAccess)
|
|
||||||
{
|
|
||||||
/* Set the write flag */
|
|
||||||
Mdl->MdlFlags |= MDL_WRITE_OPERATION;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Remove the write flag */
|
|
||||||
Mdl->MdlFlags &= ~(MDL_WRITE_OPERATION);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if this came from kernel mode */
|
|
||||||
if (Base >= MM_HIGHEST_USER_ADDRESS)
|
|
||||||
{
|
|
||||||
/* We should not have a process */
|
|
||||||
ASSERT(CurrentProcess == NULL);
|
|
||||||
Mdl->Process = NULL;
|
|
||||||
AddressSpace = MmGetKernelAddressSpace();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Sanity checks */
|
|
||||||
ASSERT(NrPages != 0);
|
|
||||||
ASSERT(CurrentProcess == PsGetCurrentProcess());
|
|
||||||
|
|
||||||
/* Track locked pages */
|
|
||||||
InterlockedExchangeAddSizeT(&CurrentProcess->NumberOfLockedPages,
|
|
||||||
NrPages);
|
|
||||||
|
|
||||||
/* Save the process */
|
|
||||||
Mdl->Process = CurrentProcess;
|
|
||||||
|
|
||||||
/* Use the process lock */
|
|
||||||
AddressSpace = &CurrentProcess->Vm;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Lock the pages
|
|
||||||
*/
|
|
||||||
if (OldIrql < DISPATCH_LEVEL)
|
|
||||||
MmLockAddressSpace(AddressSpace);
|
|
||||||
else
|
|
||||||
MmAcquirePageListLock(&OldIrql);
|
|
||||||
|
|
||||||
for (i = 0; i < NrPages; i++)
|
|
||||||
{
|
|
||||||
PVOID Address;
|
|
||||||
|
|
||||||
Address = (char*)Mdl->StartVa + (i*PAGE_SIZE);
|
|
||||||
|
|
||||||
if (!MmIsPagePresent(NULL, Address))
|
|
||||||
{
|
|
||||||
/* Fault the page in */
|
|
||||||
Status = MmAccessFault(FALSE, Address, AccessMode, NULL);
|
|
||||||
if (!NT_SUCCESS(Status))
|
|
||||||
{
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
MmLockPage(MmGetPfnForProcess(NULL, Address));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((Operation == IoWriteAccess || Operation == IoModifyAccess) &&
|
|
||||||
(!(MmGetPageProtect(NULL, (PVOID)Address) & PAGE_READWRITE)))
|
|
||||||
{
|
|
||||||
Status = MmAccessFault(TRUE, Address, AccessMode, NULL);
|
|
||||||
if (!NT_SUCCESS(Status))
|
|
||||||
{
|
|
||||||
for (j = 0; j < i; j++)
|
|
||||||
{
|
|
||||||
Page = MdlPages[j];
|
|
||||||
if (Page < MmHighestPhysicalPage)
|
|
||||||
{
|
|
||||||
MmUnlockPage(Page);
|
|
||||||
MmDereferencePage(Page);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Page = MmGetPfnForProcess(NULL, Address);
|
|
||||||
MdlPages[i] = Page;
|
|
||||||
if (Page >= MmHighestPhysicalPage)
|
|
||||||
{
|
|
||||||
Mdl->MdlFlags |= MDL_IO_SPACE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
MmReferencePage(Page);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
if (OldIrql < DISPATCH_LEVEL)
|
|
||||||
MmUnlockAddressSpace(AddressSpace);
|
|
||||||
else
|
|
||||||
MmReleasePageListLock(OldIrql);
|
|
||||||
|
|
||||||
if (!NT_SUCCESS(Status))
|
|
||||||
ExRaiseStatus(STATUS_ACCESS_VIOLATION);
|
|
||||||
Mdl->MdlFlags |= MDL_PAGES_LOCKED;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @implemented
|
|
||||||
*/
|
|
||||||
PMDL
|
|
||||||
NTAPI
|
|
||||||
MmAllocatePagesForMdl(IN PHYSICAL_ADDRESS LowAddress,
|
|
||||||
IN PHYSICAL_ADDRESS HighAddress,
|
|
||||||
IN PHYSICAL_ADDRESS SkipBytes,
|
|
||||||
IN SIZE_T Totalbytes)
|
|
||||||
{
|
|
||||||
PMDL Mdl;
|
|
||||||
PPFN_TYPE Pages;
|
|
||||||
ULONG NumberOfPagesWanted, NumberOfPagesAllocated;
|
|
||||||
ULONG Ret;
|
|
||||||
DPRINT("Allocating pages: %p\n", LowAddress.LowPart);
|
|
||||||
|
|
||||||
/* SkipBytes must be a multiple of the page size */
|
|
||||||
if (BYTE_OFFSET(SkipBytes.LowPart)) return NULL;
|
|
||||||
|
|
||||||
/* Create the actual MDL */
|
|
||||||
Mdl = MmCreateMdl(NULL, NULL, Totalbytes);
|
|
||||||
if (!Mdl) return NULL;
|
|
||||||
|
|
||||||
/* Allocate pages into the MDL */
|
|
||||||
NumberOfPagesAllocated = 0;
|
|
||||||
NumberOfPagesWanted = PAGE_ROUND_UP(Mdl->ByteCount + Mdl->ByteOffset) / PAGE_SIZE;
|
|
||||||
Pages = (PPFN_TYPE)(Mdl + 1);
|
|
||||||
while (NumberOfPagesWanted > 0)
|
|
||||||
{
|
|
||||||
Ret = MmAllocPagesSpecifyRange(MC_NPPOOL,
|
|
||||||
LowAddress,
|
|
||||||
HighAddress,
|
|
||||||
NumberOfPagesWanted,
|
|
||||||
Pages + NumberOfPagesAllocated);
|
|
||||||
if (Ret == (ULONG)-1) break;
|
|
||||||
|
|
||||||
NumberOfPagesAllocated += Ret;
|
|
||||||
NumberOfPagesWanted -= Ret;
|
|
||||||
|
|
||||||
if (SkipBytes.QuadPart == 0) break;
|
|
||||||
LowAddress.QuadPart += SkipBytes.QuadPart;
|
|
||||||
HighAddress.QuadPart += SkipBytes.QuadPart;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If nothing was allocated, fail */
|
|
||||||
if (NumberOfPagesWanted)
|
|
||||||
{
|
|
||||||
/* Free our MDL */
|
|
||||||
ExFreePool(Mdl);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Zero out the MDL pages */
|
|
||||||
//RtlZeroMemory(LowAddress.LowPart, NumberOfPagesAllocated * PAGE_SIZE);
|
|
||||||
|
|
||||||
/* Return the MDL */
|
|
||||||
Mdl->MdlFlags |= MDL_PAGES_LOCKED;
|
|
||||||
Mdl->ByteCount = (ULONG)(NumberOfPagesAllocated * PAGE_SIZE);
|
|
||||||
return Mdl;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @unimplemented
|
|
||||||
*/
|
|
||||||
PMDL
|
|
||||||
NTAPI
|
|
||||||
MmAllocatePagesForMdlEx(IN PHYSICAL_ADDRESS LowAddress,
|
|
||||||
IN PHYSICAL_ADDRESS HighAddress,
|
|
||||||
IN PHYSICAL_ADDRESS SkipBytes,
|
|
||||||
IN SIZE_T Totalbytes,
|
|
||||||
IN MEMORY_CACHING_TYPE CacheType,
|
|
||||||
IN ULONG Flags)
|
|
||||||
{
|
|
||||||
UNIMPLEMENTED;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @implemented
|
|
||||||
*/
|
|
||||||
PVOID
|
|
||||||
NTAPI
|
|
||||||
MmMapLockedPagesSpecifyCache(IN PMDL Mdl,
|
|
||||||
IN KPROCESSOR_MODE AccessMode,
|
|
||||||
IN MEMORY_CACHING_TYPE CacheType,
|
|
||||||
IN PVOID BaseAddress,
|
|
||||||
IN ULONG BugCheckOnFailure,
|
|
||||||
IN MM_PAGE_PRIORITY Priority)
|
|
||||||
{
|
|
||||||
PVOID Base;
|
|
||||||
PULONG MdlPages;
|
|
||||||
KIRQL oldIrql;
|
|
||||||
ULONG PageCount;
|
|
||||||
ULONG StartingOffset;
|
|
||||||
PEPROCESS CurrentProcess;
|
|
||||||
NTSTATUS Status;
|
|
||||||
ULONG Protect;
|
|
||||||
MEMORY_AREA *Result;
|
|
||||||
LARGE_INTEGER BoundaryAddressMultiple;
|
|
||||||
DPRINT("Mapping MDL: %p\n", Mdl);
|
|
||||||
DPRINT("Base: %p\n", BaseAddress);
|
|
||||||
|
|
||||||
/* Sanity checks */
|
|
||||||
ASSERT(Mdl->ByteCount != 0);
|
|
||||||
|
|
||||||
/* Get the base */
|
|
||||||
Base = (PVOID)((ULONG_PTR)Mdl->StartVa + Mdl->ByteOffset);
|
|
||||||
|
|
||||||
/* Set default page protection */
|
|
||||||
Protect = PAGE_READWRITE;
|
|
||||||
if (CacheType == MmNonCached) Protect |= PAGE_NOCACHE;
|
|
||||||
|
|
||||||
/* Handle kernel case first */
|
|
||||||
if (AccessMode == KernelMode)
|
|
||||||
{
|
|
||||||
/* Get the list of pages and count */
|
|
||||||
MdlPages = (PPFN_NUMBER)(Mdl + 1);
|
|
||||||
PageCount = ADDRESS_AND_SIZE_TO_SPAN_PAGES(Base, Mdl->ByteCount);
|
|
||||||
|
|
||||||
/* Sanity checks */
|
|
||||||
ASSERT((Mdl->MdlFlags & (MDL_MAPPED_TO_SYSTEM_VA |
|
|
||||||
MDL_SOURCE_IS_NONPAGED_POOL |
|
|
||||||
MDL_PARTIAL_HAS_BEEN_MAPPED)) == 0);
|
|
||||||
ASSERT((Mdl->MdlFlags & (MDL_PAGES_LOCKED | MDL_PARTIAL)) != 0);
|
|
||||||
|
|
||||||
/* Allocate that number of pages from the mdl mapping region. */
|
|
||||||
KeAcquireSpinLock(&MiMdlMappingRegionLock, &oldIrql);
|
|
||||||
StartingOffset = RtlFindClearBitsAndSet(&MiMdlMappingRegionAllocMap,
|
|
||||||
PageCount,
|
|
||||||
MiMdlMappingRegionHint);
|
|
||||||
if (StartingOffset == 0xffffffff)
|
|
||||||
{
|
|
||||||
KeReleaseSpinLock(&MiMdlMappingRegionLock, oldIrql);
|
|
||||||
DPRINT("Out of MDL mapping space\n");
|
|
||||||
if ((Mdl->MdlFlags & MDL_MAPPING_CAN_FAIL) || !BugCheckOnFailure)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
KeBugCheck(MEMORY_MANAGEMENT);
|
|
||||||
}
|
|
||||||
Base = (PVOID)((ULONG_PTR)MiMdlMappingRegionBase + StartingOffset * PAGE_SIZE);
|
|
||||||
if (MiMdlMappingRegionHint == StartingOffset) MiMdlMappingRegionHint += PageCount;
|
|
||||||
KeReleaseSpinLock(&MiMdlMappingRegionLock, oldIrql);
|
|
||||||
|
|
||||||
/* Set the virtual mappings for the MDL pages. */
|
|
||||||
if (Mdl->MdlFlags & MDL_IO_SPACE)
|
|
||||||
{
|
|
||||||
/* Map the pages */
|
|
||||||
Status = MmCreateVirtualMappingUnsafe(NULL,
|
|
||||||
Base,
|
|
||||||
Protect,
|
|
||||||
MdlPages,
|
|
||||||
PageCount);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Map the pages */
|
|
||||||
Status = MmCreateVirtualMapping(NULL,
|
|
||||||
Base,
|
|
||||||
Protect,
|
|
||||||
MdlPages,
|
|
||||||
PageCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if the mapping suceeded */
|
|
||||||
if (!NT_SUCCESS(Status))
|
|
||||||
{
|
|
||||||
/* If it can fail, return NULL */
|
|
||||||
if (Mdl->MdlFlags & MDL_MAPPING_CAN_FAIL) return NULL;
|
|
||||||
|
|
||||||
/* Should we bugcheck? */
|
|
||||||
if (!BugCheckOnFailure) return NULL;
|
|
||||||
|
|
||||||
/* Yes, crash the system */
|
|
||||||
KeBugCheckEx(NO_MORE_SYSTEM_PTES, 0, PageCount, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mark it as mapped */
|
|
||||||
ASSERT((Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA) == 0);
|
|
||||||
Mdl->MdlFlags |= MDL_MAPPED_TO_SYSTEM_VA;
|
|
||||||
|
|
||||||
/* Check if it was partial */
|
|
||||||
if (Mdl->MdlFlags & MDL_PARTIAL)
|
|
||||||
{
|
|
||||||
/* Write the appropriate flag here too */
|
|
||||||
Mdl->MdlFlags |= MDL_PARTIAL_HAS_BEEN_MAPPED;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Save the mapped address */
|
|
||||||
Base = (PVOID)((ULONG_PTR)Base + Mdl->ByteOffset);
|
|
||||||
Mdl->MappedSystemVa = Base;
|
|
||||||
return Base;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Calculate the number of pages required. */
|
|
||||||
MdlPages = (PPFN_NUMBER)(Mdl + 1);
|
|
||||||
PageCount = PAGE_ROUND_UP(Mdl->ByteCount + Mdl->ByteOffset) / PAGE_SIZE;
|
|
||||||
|
|
||||||
BoundaryAddressMultiple.QuadPart = 0;
|
|
||||||
Base = BaseAddress;
|
|
||||||
|
|
||||||
CurrentProcess = PsGetCurrentProcess();
|
|
||||||
|
|
||||||
MmLockAddressSpace(&CurrentProcess->Vm);
|
|
||||||
Status = MmCreateMemoryArea(&CurrentProcess->Vm,
|
|
||||||
MEMORY_AREA_MDL_MAPPING,
|
|
||||||
&Base,
|
|
||||||
PageCount * PAGE_SIZE,
|
|
||||||
Protect,
|
|
||||||
&Result,
|
|
||||||
(Base != NULL),
|
|
||||||
0,
|
|
||||||
BoundaryAddressMultiple);
|
|
||||||
MmUnlockAddressSpace(&CurrentProcess->Vm);
|
|
||||||
if (!NT_SUCCESS(Status))
|
|
||||||
{
|
|
||||||
if (Mdl->MdlFlags & MDL_MAPPING_CAN_FAIL)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Throw exception */
|
|
||||||
ExRaiseStatus(STATUS_ACCESS_VIOLATION);
|
|
||||||
ASSERT(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set the virtual mappings for the MDL pages. */
|
|
||||||
if (Mdl->MdlFlags & MDL_IO_SPACE)
|
|
||||||
{
|
|
||||||
/* Map the pages */
|
|
||||||
Status = MmCreateVirtualMappingUnsafe(CurrentProcess,
|
|
||||||
Base,
|
|
||||||
Protect,
|
|
||||||
MdlPages,
|
|
||||||
PageCount);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Map the pages */
|
|
||||||
Status = MmCreateVirtualMapping(CurrentProcess,
|
|
||||||
Base,
|
|
||||||
Protect,
|
|
||||||
MdlPages,
|
|
||||||
PageCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if the mapping suceeded */
|
|
||||||
if (!NT_SUCCESS(Status))
|
|
||||||
{
|
|
||||||
/* If it can fail, return NULL */
|
|
||||||
if (Mdl->MdlFlags & MDL_MAPPING_CAN_FAIL) return NULL;
|
|
||||||
|
|
||||||
/* Throw exception */
|
|
||||||
ExRaiseStatus(STATUS_ACCESS_VIOLATION);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return the base */
|
|
||||||
Base = (PVOID)((ULONG_PTR)Base + Mdl->ByteOffset);
|
|
||||||
return Base;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @unimplemented
|
|
||||||
*/
|
|
||||||
NTSTATUS
|
|
||||||
NTAPI
|
|
||||||
MmAdvanceMdl(IN PMDL Mdl,
|
|
||||||
IN ULONG NumberOfBytes)
|
|
||||||
{
|
|
||||||
UNIMPLEMENTED;
|
|
||||||
return STATUS_NOT_IMPLEMENTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @unimplemented
|
|
||||||
*/
|
|
||||||
PVOID
|
|
||||||
NTAPI
|
|
||||||
MmMapLockedPagesWithReservedMapping(IN PVOID MappingAddress,
|
|
||||||
IN ULONG PoolTag,
|
|
||||||
IN PMDL MemoryDescriptorList,
|
|
||||||
IN MEMORY_CACHING_TYPE CacheType)
|
|
||||||
{
|
|
||||||
UNIMPLEMENTED;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @unimplemented
|
|
||||||
*/
|
|
||||||
VOID
|
|
||||||
NTAPI
|
|
||||||
MmUnmapReservedMapping(IN PVOID BaseAddress,
|
|
||||||
IN ULONG PoolTag,
|
|
||||||
IN PMDL MemoryDescriptorList)
|
|
||||||
{
|
|
||||||
UNIMPLEMENTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @unimplemented
|
|
||||||
*/
|
|
||||||
NTSTATUS
|
|
||||||
NTAPI
|
|
||||||
MmPrefetchPages(IN ULONG NumberOfLists,
|
|
||||||
IN PREAD_LIST *ReadLists)
|
|
||||||
{
|
|
||||||
UNIMPLEMENTED;
|
|
||||||
return STATUS_NOT_IMPLEMENTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @unimplemented
|
|
||||||
*/
|
|
||||||
NTSTATUS
|
|
||||||
NTAPI
|
|
||||||
MmProtectMdlSystemAddress(IN PMDL MemoryDescriptorList,
|
|
||||||
IN ULONG NewProtect)
|
|
||||||
{
|
|
||||||
UNIMPLEMENTED;
|
|
||||||
return STATUS_NOT_IMPLEMENTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @unimplemented
|
|
||||||
*/
|
|
||||||
VOID
|
|
||||||
NTAPI
|
|
||||||
MmProbeAndLockProcessPages(IN OUT PMDL MemoryDescriptorList,
|
|
||||||
IN PEPROCESS Process,
|
|
||||||
IN KPROCESSOR_MODE AccessMode,
|
|
||||||
IN LOCK_OPERATION Operation)
|
|
||||||
{
|
|
||||||
UNIMPLEMENTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @unimplemented
|
|
||||||
*/
|
|
||||||
VOID
|
|
||||||
NTAPI
|
|
||||||
MmProbeAndLockSelectedPages(IN OUT PMDL MemoryDescriptorList,
|
|
||||||
IN LARGE_INTEGER PageList[],
|
|
||||||
IN KPROCESSOR_MODE AccessMode,
|
|
||||||
IN LOCK_OPERATION Operation)
|
|
||||||
{
|
|
||||||
UNIMPLEMENTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @unimplemented
|
|
||||||
*/
|
|
||||||
VOID
|
|
||||||
NTAPI
|
|
||||||
MmMapMemoryDumpMdl(IN PMDL Mdl)
|
|
||||||
{
|
|
||||||
UNIMPLEMENTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* EOF */
|
|
||||||
|
|
|
@ -393,9 +393,6 @@ MmInit1(VOID)
|
||||||
/* Initialize paged pool */
|
/* Initialize paged pool */
|
||||||
MmInitializePagedPool();
|
MmInitializePagedPool();
|
||||||
|
|
||||||
/* Initialize MDLs */
|
|
||||||
MmInitializeMdlImplementation();
|
|
||||||
|
|
||||||
/* Initialize working sets */
|
/* Initialize working sets */
|
||||||
MmInitializeMemoryConsumer(MC_USER, MmTrimUserMemory);
|
MmInitializeMemoryConsumer(MC_USER, MmTrimUserMemory);
|
||||||
|
|
||||||
|
|
|
@ -622,6 +622,115 @@ MiProtectVirtualMemory(IN PEPROCESS Process,
|
||||||
return Status;
|
return Status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PVOID
|
||||||
|
NTAPI
|
||||||
|
MiMapLockedPagesInUserSpace(IN PMDL Mdl,
|
||||||
|
IN PVOID BaseVa,
|
||||||
|
IN MEMORY_CACHING_TYPE CacheType,
|
||||||
|
IN PVOID BaseAddress)
|
||||||
|
{
|
||||||
|
PVOID Base;
|
||||||
|
PPFN_NUMBER MdlPages;
|
||||||
|
ULONG PageCount;
|
||||||
|
PEPROCESS CurrentProcess;
|
||||||
|
NTSTATUS Status;
|
||||||
|
ULONG Protect;
|
||||||
|
MEMORY_AREA *Result;
|
||||||
|
LARGE_INTEGER BoundaryAddressMultiple;
|
||||||
|
|
||||||
|
/* Calculate the number of pages required. */
|
||||||
|
MdlPages = (PPFN_NUMBER)(Mdl + 1);
|
||||||
|
PageCount = PAGE_ROUND_UP(Mdl->ByteCount + Mdl->ByteOffset) / PAGE_SIZE;
|
||||||
|
|
||||||
|
/* Set default page protection */
|
||||||
|
Protect = PAGE_READWRITE;
|
||||||
|
if (CacheType == MmNonCached) Protect |= PAGE_NOCACHE;
|
||||||
|
|
||||||
|
BoundaryAddressMultiple.QuadPart = 0;
|
||||||
|
Base = BaseAddress;
|
||||||
|
|
||||||
|
CurrentProcess = PsGetCurrentProcess();
|
||||||
|
|
||||||
|
MmLockAddressSpace(&CurrentProcess->Vm);
|
||||||
|
Status = MmCreateMemoryArea(&CurrentProcess->Vm,
|
||||||
|
MEMORY_AREA_MDL_MAPPING,
|
||||||
|
&Base,
|
||||||
|
PageCount * PAGE_SIZE,
|
||||||
|
Protect,
|
||||||
|
&Result,
|
||||||
|
(Base != NULL),
|
||||||
|
0,
|
||||||
|
BoundaryAddressMultiple);
|
||||||
|
MmUnlockAddressSpace(&CurrentProcess->Vm);
|
||||||
|
if (!NT_SUCCESS(Status))
|
||||||
|
{
|
||||||
|
if (Mdl->MdlFlags & MDL_MAPPING_CAN_FAIL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Throw exception */
|
||||||
|
ExRaiseStatus(STATUS_ACCESS_VIOLATION);
|
||||||
|
ASSERT(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the virtual mappings for the MDL pages. */
|
||||||
|
if (Mdl->MdlFlags & MDL_IO_SPACE)
|
||||||
|
{
|
||||||
|
/* Map the pages */
|
||||||
|
Status = MmCreateVirtualMappingUnsafe(CurrentProcess,
|
||||||
|
Base,
|
||||||
|
Protect,
|
||||||
|
MdlPages,
|
||||||
|
PageCount);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Map the pages */
|
||||||
|
Status = MmCreateVirtualMapping(CurrentProcess,
|
||||||
|
Base,
|
||||||
|
Protect,
|
||||||
|
MdlPages,
|
||||||
|
PageCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if the mapping suceeded */
|
||||||
|
if (!NT_SUCCESS(Status))
|
||||||
|
{
|
||||||
|
/* If it can fail, return NULL */
|
||||||
|
if (Mdl->MdlFlags & MDL_MAPPING_CAN_FAIL) return NULL;
|
||||||
|
|
||||||
|
/* Throw exception */
|
||||||
|
ExRaiseStatus(STATUS_ACCESS_VIOLATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return the base */
|
||||||
|
Base = (PVOID)((ULONG_PTR)Base + Mdl->ByteOffset);
|
||||||
|
return Base;
|
||||||
|
}
|
||||||
|
|
||||||
|
VOID
|
||||||
|
NTAPI
|
||||||
|
MiUnmapLockedPagesInUserSpace(IN PVOID BaseAddress,
|
||||||
|
IN PMDL Mdl)
|
||||||
|
{
|
||||||
|
PMEMORY_AREA MemoryArea;
|
||||||
|
|
||||||
|
/* Sanity check */
|
||||||
|
ASSERT(Mdl->Process == PsGetCurrentProcess());
|
||||||
|
|
||||||
|
/* Find the memory area */
|
||||||
|
MemoryArea = MmLocateMemoryAreaByAddress(&Mdl->Process->Vm,
|
||||||
|
BaseAddress);
|
||||||
|
ASSERT(MemoryArea);
|
||||||
|
|
||||||
|
/* Free it */
|
||||||
|
MmFreeMemoryArea(&Mdl->Process->Vm,
|
||||||
|
MemoryArea,
|
||||||
|
NULL,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
/* PUBLIC FUNCTIONS ***********************************************************/
|
/* PUBLIC FUNCTIONS ***********************************************************/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -366,6 +366,7 @@
|
||||||
<file>hypermap.c</file>
|
<file>hypermap.c</file>
|
||||||
<file>init.c</file>
|
<file>init.c</file>
|
||||||
<file>iosup.c</file>
|
<file>iosup.c</file>
|
||||||
|
<file>mdlsup.c</file>
|
||||||
<file>pool.c</file>
|
<file>pool.c</file>
|
||||||
<file>procsup.c</file>
|
<file>procsup.c</file>
|
||||||
<file>syspte.c</file>
|
<file>syspte.c</file>
|
||||||
|
@ -375,7 +376,6 @@
|
||||||
<file>dbgpool.c</file>
|
<file>dbgpool.c</file>
|
||||||
<file>freelist.c</file>
|
<file>freelist.c</file>
|
||||||
<file>marea.c</file>
|
<file>marea.c</file>
|
||||||
<file>mdlsup.c</file>
|
|
||||||
<file>mmfault.c</file>
|
<file>mmfault.c</file>
|
||||||
<file>mmsup.c</file>
|
<file>mmsup.c</file>
|
||||||
<file>mminit.c</file>
|
<file>mminit.c</file>
|
||||||
|
|
Loading…
Reference in a new issue