mirror of
https://github.com/reactos/reactos.git
synced 2024-10-31 20:02:55 +00:00
e1c437438c
svn path=/trunk/; revision=4704
464 lines
11 KiB
C
464 lines
11 KiB
C
/* $Id: mdl.c,v 1.48 2003/05/17 15:28:58 ekohl Exp $
|
|
*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS kernel
|
|
* FILE: ntoskrnl/mm/mdl.c
|
|
* PURPOSE: Manipulates MDLs
|
|
* PROGRAMMER: David Welch (welch@cwcom.net)
|
|
* UPDATE HISTORY:
|
|
* 27/05/98: Created
|
|
*/
|
|
|
|
/* INCLUDES ****************************************************************/
|
|
|
|
#include <ddk/ntddk.h>
|
|
#include <internal/mm.h>
|
|
#include <internal/ps.h>
|
|
#include <internal/pool.h>
|
|
#include <ntos/minmax.h>
|
|
|
|
#define NDEBUG
|
|
#include <internal/debug.h>
|
|
|
|
/* GLOBALS *******************************************************************/
|
|
|
|
#define TAG_MDL TAG('M', 'M', 'D', 'L')
|
|
|
|
#define MI_MDL_MAPPING_REGION_SIZE (256*1024*1024)
|
|
|
|
static PVOID MiMdlMappingRegionBase = NULL;
|
|
static RTL_BITMAP MiMdlMappingRegionAllocMap;
|
|
static ULONG MiMdlMappingRegionHint;
|
|
static KSPIN_LOCK MiMdlMappingRegionLock;
|
|
|
|
/* FUNCTIONS *****************************************************************/
|
|
|
|
VOID
|
|
MmInitializeMdlImplementation(VOID)
|
|
{
|
|
MEMORY_AREA* Result;
|
|
NTSTATUS Status;
|
|
PVOID Buffer;
|
|
|
|
MiMdlMappingRegionHint = 0;
|
|
MiMdlMappingRegionBase = NULL;
|
|
|
|
MmLockAddressSpace(MmGetKernelAddressSpace());
|
|
Status = MmCreateMemoryArea(NULL,
|
|
MmGetKernelAddressSpace(),
|
|
MEMORY_AREA_MDL_MAPPING,
|
|
&MiMdlMappingRegionBase,
|
|
MI_MDL_MAPPING_REGION_SIZE,
|
|
0,
|
|
&Result,
|
|
FALSE,
|
|
FALSE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
MmUnlockAddressSpace(MmGetKernelAddressSpace());
|
|
KeBugCheck(0);
|
|
}
|
|
MmUnlockAddressSpace(MmGetKernelAddressSpace());
|
|
|
|
Buffer = ExAllocatePool(NonPagedPool, MI_MDL_MAPPING_REGION_SIZE / (PAGE_SIZE * 8));
|
|
|
|
RtlInitializeBitMap(&MiMdlMappingRegionAllocMap, Buffer, MI_MDL_MAPPING_REGION_SIZE / PAGE_SIZE);
|
|
RtlClearAllBits(&MiMdlMappingRegionAllocMap);
|
|
|
|
KeInitializeSpinLock(&MiMdlMappingRegionLock);
|
|
}
|
|
|
|
PVOID
|
|
MmGetMdlPageAddress(PMDL Mdl, PVOID Offset)
|
|
{
|
|
PULONG MdlPages;
|
|
|
|
MdlPages = (PULONG)(Mdl + 1);
|
|
|
|
return((PVOID)MdlPages[((ULONG)Offset) / PAGE_SIZE]);
|
|
}
|
|
|
|
VOID STDCALL
|
|
MmUnlockPages(PMDL Mdl)
|
|
/*
|
|
* FUNCTION: Unlocks the physical pages described by a given MDL
|
|
* ARGUMENTS:
|
|
* MemoryDescriptorList = MDL describing the buffer to be unlocked
|
|
* NOTES: The memory described by the specified MDL must have been locked
|
|
* previously by a call to MmProbeAndLockPages. As the pages unlocked, the
|
|
* MDL is updated
|
|
*/
|
|
{
|
|
ULONG i;
|
|
PULONG MdlPages;
|
|
|
|
/*
|
|
* FIXME: I don't know whether this right, but it looks sensible
|
|
*/
|
|
if ((Mdl->MdlFlags & MDL_SOURCE_IS_NONPAGED_POOL) ||
|
|
(Mdl->MdlFlags & MDL_IO_PAGE_READ))
|
|
{
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* FIXME: Seems sensible
|
|
*/
|
|
if (!(Mdl->MdlFlags & MDL_PAGES_LOCKED))
|
|
{
|
|
return;
|
|
}
|
|
|
|
MdlPages = (PULONG)(Mdl + 1);
|
|
for (i=0; i<(PAGE_ROUND_UP(Mdl->ByteCount+Mdl->ByteOffset)/PAGE_SIZE); i++)
|
|
{
|
|
MmUnlockPage((LARGE_INTEGER)(LONGLONG)MdlPages[i]);
|
|
MmDereferencePage((LARGE_INTEGER)(LONGLONG)MdlPages[i]);
|
|
}
|
|
Mdl->MdlFlags = Mdl->MdlFlags & (~MDL_PAGES_LOCKED);
|
|
}
|
|
|
|
PVOID STDCALL
|
|
MmMapLockedPages(PMDL Mdl, KPROCESSOR_MODE AccessMode)
|
|
/*
|
|
* FUNCTION: Maps the physical pages described by a given MDL
|
|
* ARGUMENTS:
|
|
* Mdl = Points to an MDL updated by MmProbeAndLockPages
|
|
* AccessMode = Specifies the portion of the address space to map the
|
|
* pages.
|
|
* RETURNS: The base virtual address that maps the locked pages for the
|
|
* range described by the MDL
|
|
*/
|
|
{
|
|
PVOID Base;
|
|
ULONG i;
|
|
PULONG MdlPages;
|
|
KIRQL oldIrql;
|
|
ULONG RegionSize;
|
|
ULONG StartingOffset;
|
|
|
|
DPRINT("MmMapLockedPages(Mdl %x, AccessMode %x)\n", Mdl, AccessMode);
|
|
|
|
if (Mdl->MdlFlags & MDL_SOURCE_IS_NONPAGED_POOL)
|
|
{
|
|
return(Mdl->MappedSystemVa);
|
|
}
|
|
|
|
if (AccessMode == UserMode)
|
|
{
|
|
DPRINT1("MDL mapping to user-mode not yet handled.\n");
|
|
KeBugCheck(0);
|
|
}
|
|
|
|
/* Calculate the number of pages required. */
|
|
RegionSize = PAGE_ROUND_UP(Mdl->ByteCount + Mdl->ByteOffset) / PAGE_SIZE;
|
|
|
|
/* Allocate that number of pages from the mdl mapping region. */
|
|
KeAcquireSpinLock(&MiMdlMappingRegionLock, &oldIrql);
|
|
|
|
StartingOffset = RtlFindClearBitsAndSet(&MiMdlMappingRegionAllocMap, RegionSize, MiMdlMappingRegionHint);
|
|
|
|
if (StartingOffset == 0xffffffff)
|
|
{
|
|
DPRINT1("Out of MDL mapping space\n");
|
|
KeBugCheck(0);
|
|
}
|
|
|
|
Base = MiMdlMappingRegionBase + StartingOffset * PAGE_SIZE;
|
|
|
|
if (MiMdlMappingRegionHint == StartingOffset)
|
|
{
|
|
MiMdlMappingRegionHint +=RegionSize;
|
|
}
|
|
|
|
KeReleaseSpinLock(&MiMdlMappingRegionLock, oldIrql);
|
|
|
|
/* Set the virtual mappings for the MDL pages. */
|
|
MdlPages = (PULONG)(Mdl + 1);
|
|
for (i = 0; i < RegionSize; i++)
|
|
{
|
|
NTSTATUS Status;
|
|
Status = MmCreateVirtualMapping(NULL,
|
|
(PVOID)((ULONG)Base+(i*PAGE_SIZE)),
|
|
PAGE_READWRITE,
|
|
(LARGE_INTEGER)(LONGLONG)MdlPages[i],
|
|
FALSE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DbgPrint("Unable to create virtual mapping\n");
|
|
KeBugCheck(0);
|
|
}
|
|
}
|
|
|
|
/* Mark the MDL has having being mapped. */
|
|
Mdl->MdlFlags = Mdl->MdlFlags | MDL_MAPPED_TO_SYSTEM_VA;
|
|
Mdl->MappedSystemVa = Base + Mdl->ByteOffset;
|
|
return(Base + Mdl->ByteOffset);
|
|
}
|
|
|
|
VOID STDCALL
|
|
MmUnmapLockedPages(PVOID BaseAddress, PMDL Mdl)
|
|
/*
|
|
* FUNCTION: Releases a mapping set up by a preceding call to MmMapLockedPages
|
|
* ARGUMENTS:
|
|
* BaseAddress = Base virtual address to which the pages were mapped
|
|
* MemoryDescriptorList = MDL describing the mapped pages
|
|
*/
|
|
{
|
|
KIRQL oldIrql;
|
|
ULONG i;
|
|
ULONG RegionSize;
|
|
ULONG Base;
|
|
|
|
DPRINT("MmUnmapLockedPages(BaseAddress %x, Mdl %x)\n", BaseAddress, Mdl);
|
|
|
|
/*
|
|
* In this case, the MDL has the same system address as the base address
|
|
* so there is no need to free it
|
|
*/
|
|
if (Mdl->MdlFlags & MDL_SOURCE_IS_NONPAGED_POOL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
/* Calculate the number of pages we mapped. */
|
|
RegionSize = PAGE_ROUND_UP(Mdl->ByteCount + Mdl->ByteOffset) / PAGE_SIZE;
|
|
BaseAddress -= Mdl->ByteOffset;
|
|
|
|
/* Unmap all the pages. */
|
|
for (i = 0; i < RegionSize; i++)
|
|
{
|
|
MmDeleteVirtualMapping(NULL,
|
|
BaseAddress + (i * PAGE_SIZE),
|
|
FALSE,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
|
|
KeAcquireSpinLock(&MiMdlMappingRegionLock, &oldIrql);
|
|
/* Deallocate all the pages used. */
|
|
Base = (ULONG)(BaseAddress - MiMdlMappingRegionBase) / PAGE_SIZE;
|
|
|
|
RtlClearBits(&MiMdlMappingRegionAllocMap, Base, RegionSize);
|
|
|
|
MiMdlMappingRegionHint = min (MiMdlMappingRegionHint, Base);
|
|
|
|
KeReleaseSpinLock(&MiMdlMappingRegionLock, oldIrql);
|
|
|
|
/* Reset the MDL state. */
|
|
Mdl->MdlFlags = Mdl->MdlFlags & ~MDL_MAPPED_TO_SYSTEM_VA;
|
|
Mdl->MappedSystemVa = NULL;
|
|
}
|
|
|
|
|
|
VOID
|
|
MmBuildMdlFromPages(PMDL Mdl, PULONG Pages)
|
|
{
|
|
ULONG i;
|
|
PULONG MdlPages;
|
|
|
|
Mdl->MdlFlags = Mdl->MdlFlags |
|
|
(MDL_PAGES_LOCKED | MDL_IO_PAGE_READ);
|
|
|
|
MdlPages = (PULONG)(Mdl + 1);
|
|
|
|
for (i=0;i<(PAGE_ROUND_UP(Mdl->ByteOffset+Mdl->ByteCount)/PAGE_SIZE);i++)
|
|
{
|
|
MdlPages[i] = Pages[i];
|
|
}
|
|
}
|
|
|
|
VOID STDCALL MmProbeAndLockPages (PMDL Mdl,
|
|
KPROCESSOR_MODE AccessMode,
|
|
LOCK_OPERATION Operation)
|
|
/*
|
|
* FUNCTION: Probes the specified pages, makes them resident and locks them
|
|
* ARGUMENTS:
|
|
* Mdl = MDL to probe
|
|
* AccessMode = Access at which to probe the buffer
|
|
* Operation = Operation to probe for
|
|
*/
|
|
{
|
|
PULONG MdlPages;
|
|
ULONG i, j;
|
|
ULONG NrPages;
|
|
NTSTATUS Status;
|
|
KPROCESSOR_MODE Mode;
|
|
PEPROCESS CurrentProcess;
|
|
|
|
DPRINT("MmProbeAndLockPages(Mdl %x)\n", Mdl);
|
|
|
|
/*
|
|
* FIXME: Check behaviour against NT
|
|
*/
|
|
if (Mdl->MdlFlags & MDL_PAGES_LOCKED)
|
|
{
|
|
return;
|
|
}
|
|
|
|
CurrentProcess = PsGetCurrentProcess();
|
|
|
|
if (Mdl->Process != CurrentProcess)
|
|
{
|
|
KeAttachProcess(Mdl->Process);
|
|
}
|
|
|
|
if (Mdl->StartVa >= (PVOID)KERNEL_BASE)
|
|
{
|
|
Mode = KernelMode;
|
|
}
|
|
else
|
|
{
|
|
Mode = UserMode;
|
|
}
|
|
|
|
/*
|
|
* Lock the pages
|
|
*/
|
|
|
|
MmLockAddressSpace(&Mdl->Process->AddressSpace);
|
|
MdlPages = (ULONG *)(Mdl + 1);
|
|
NrPages = PAGE_ROUND_UP(Mdl->ByteOffset + Mdl->ByteCount) / PAGE_SIZE;
|
|
for (i = 0; i < NrPages; i++)
|
|
{
|
|
PVOID Address;
|
|
|
|
Address = Mdl->StartVa + (i*PAGE_SIZE);
|
|
|
|
if (!MmIsPagePresent(NULL, Address))
|
|
{
|
|
Status = MmNotPresentFault(Mode, (ULONG)Address, TRUE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
for (j = 0; j < i; j++)
|
|
{
|
|
MmUnlockPage((LARGE_INTEGER)(LONGLONG)MdlPages[j]);
|
|
MmDereferencePage((LARGE_INTEGER)(LONGLONG)MdlPages[j]);
|
|
}
|
|
ExRaiseStatus(Status);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MmLockPage(MmGetPhysicalAddressForProcess(NULL, Address));
|
|
}
|
|
if ((Operation == IoWriteAccess || Operation == IoModifyAccess) &&
|
|
(!(MmGetPageProtect(NULL, (PVOID)Address) & PAGE_READWRITE)))
|
|
{
|
|
Status = MmAccessFault(Mode, (ULONG)Address, TRUE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
for (j = 0; j < i; j++)
|
|
{
|
|
MmUnlockPage((LARGE_INTEGER)(LONGLONG)MdlPages[j]);
|
|
MmDereferencePage(
|
|
(LARGE_INTEGER)(LONGLONG)MdlPages[j]);
|
|
}
|
|
ExRaiseStatus(Status);
|
|
}
|
|
}
|
|
MdlPages[i] = MmGetPhysicalAddressForProcess(NULL, Address).u.LowPart;
|
|
MmReferencePage((LARGE_INTEGER)(LONGLONG)MdlPages[i]);
|
|
}
|
|
MmUnlockAddressSpace(&Mdl->Process->AddressSpace);
|
|
if (Mdl->Process != CurrentProcess)
|
|
{
|
|
KeDetachProcess();
|
|
}
|
|
Mdl->MdlFlags = Mdl->MdlFlags | MDL_PAGES_LOCKED;
|
|
}
|
|
|
|
|
|
ULONG STDCALL MmSizeOfMdl (PVOID Base,
|
|
ULONG Length)
|
|
/*
|
|
* FUNCTION: Returns the number of bytes to allocate for an MDL describing
|
|
* the given address range
|
|
* ARGUMENTS:
|
|
* Base = base virtual address
|
|
* Length = number of bytes to map
|
|
*/
|
|
{
|
|
ULONG len;
|
|
|
|
len = ADDRESS_AND_SIZE_TO_SPAN_PAGES(Base,Length);
|
|
|
|
return(sizeof(MDL)+(len*sizeof(ULONG)));
|
|
}
|
|
|
|
|
|
VOID STDCALL
|
|
MmBuildMdlForNonPagedPool (PMDL Mdl)
|
|
/*
|
|
* FUNCTION: Fills in the corresponding physical page array of a given
|
|
* MDL for a buffer in nonpaged system space
|
|
* ARGUMENTS:
|
|
* Mdl = Points to an MDL that supplies a virtual address,
|
|
* byte offset and length
|
|
*/
|
|
{
|
|
ULONG va;
|
|
Mdl->MdlFlags = Mdl->MdlFlags |
|
|
(MDL_SOURCE_IS_NONPAGED_POOL | MDL_PAGES_LOCKED);
|
|
for (va=0; va < ((Mdl->Size - sizeof(MDL)) / sizeof(ULONG)); va++)
|
|
{
|
|
((PULONG)(Mdl + 1))[va] =
|
|
(MmGetPhysicalAddress(Mdl->StartVa + (va * PAGE_SIZE))).u.LowPart;
|
|
}
|
|
Mdl->MappedSystemVa = Mdl->StartVa + Mdl->ByteOffset;
|
|
}
|
|
|
|
|
|
PMDL STDCALL
|
|
MmCreateMdl (PMDL MemoryDescriptorList,
|
|
PVOID Base,
|
|
ULONG Length)
|
|
/*
|
|
* FUNCTION: Allocates and initalizes an MDL
|
|
* ARGUMENTS:
|
|
* MemoryDescriptorList = Points to MDL to initalize. If this is
|
|
* NULL then one is allocated
|
|
* Base = Base virtual address of the buffer
|
|
* Length = Length in bytes of the buffer
|
|
* RETURNS: A pointer to initalized MDL
|
|
*/
|
|
{
|
|
if (MemoryDescriptorList == NULL)
|
|
{
|
|
ULONG Size;
|
|
|
|
Size = MmSizeOfMdl(Base,Length);
|
|
MemoryDescriptorList =
|
|
(PMDL)ExAllocatePoolWithTag(NonPagedPool, Size, TAG_MDL);
|
|
if (MemoryDescriptorList == NULL)
|
|
{
|
|
return(NULL);
|
|
}
|
|
}
|
|
|
|
MmInitializeMdl(MemoryDescriptorList,Base,Length);
|
|
|
|
return(MemoryDescriptorList);
|
|
}
|
|
|
|
VOID STDCALL
|
|
MmMapMemoryDumpMdl (PVOID Unknown0)
|
|
/*
|
|
* FIXME: Has something to do with crash dumps. Do we want to implement
|
|
* this?
|
|
*/
|
|
{
|
|
UNIMPLEMENTED;
|
|
}
|
|
|
|
/* EOF */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|