mirror of
https://github.com/reactos/reactos.git
synced 2024-11-11 01:04:11 +00:00
1fb94b1cb5
sync with trunk (r49230) svn path=/branches/cmake-bringup/; revision=49246
1867 lines
60 KiB
C
1867 lines
60 KiB
C
/*
|
|
* PROJECT: ReactOS Kernel
|
|
* LICENSE: BSD - See COPYING.ARM in the top level directory
|
|
* FILE: ntoskrnl/mm/ARM3/sectopm.c
|
|
* PURPOSE: ARM Memory Manager Section Support
|
|
* PROGRAMMERS: ReactOS Portable Systems Group
|
|
*/
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
#include <ntoskrnl.h>
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
#line 15 "ARM³::SECTION"
|
|
#define MODULE_INVOLVED_IN_ARM3
|
|
#include "../ARM3/miarm.h"
|
|
|
|
/* GLOBALS ********************************************************************/
|
|
|
|
ACCESS_MASK MmMakeSectionAccess[8] =
|
|
{
|
|
SECTION_MAP_READ,
|
|
SECTION_MAP_READ,
|
|
SECTION_MAP_EXECUTE,
|
|
SECTION_MAP_EXECUTE | SECTION_MAP_READ,
|
|
SECTION_MAP_WRITE,
|
|
SECTION_MAP_READ,
|
|
SECTION_MAP_EXECUTE | SECTION_MAP_WRITE,
|
|
SECTION_MAP_EXECUTE | SECTION_MAP_READ
|
|
};
|
|
|
|
CHAR MmUserProtectionToMask1[16] =
|
|
{
|
|
0,
|
|
MM_NOACCESS,
|
|
MM_READONLY,
|
|
(CHAR)MM_INVALID_PROTECTION,
|
|
MM_READWRITE,
|
|
(CHAR)MM_INVALID_PROTECTION,
|
|
(CHAR)MM_INVALID_PROTECTION,
|
|
(CHAR)MM_INVALID_PROTECTION,
|
|
MM_WRITECOPY,
|
|
(CHAR)MM_INVALID_PROTECTION,
|
|
(CHAR)MM_INVALID_PROTECTION,
|
|
(CHAR)MM_INVALID_PROTECTION,
|
|
(CHAR)MM_INVALID_PROTECTION,
|
|
(CHAR)MM_INVALID_PROTECTION,
|
|
(CHAR)MM_INVALID_PROTECTION,
|
|
(CHAR)MM_INVALID_PROTECTION
|
|
};
|
|
|
|
CHAR MmUserProtectionToMask2[16] =
|
|
{
|
|
0,
|
|
MM_EXECUTE,
|
|
MM_EXECUTE_READ,
|
|
(CHAR)MM_INVALID_PROTECTION,
|
|
MM_EXECUTE_READWRITE,
|
|
(CHAR)MM_INVALID_PROTECTION,
|
|
(CHAR)MM_INVALID_PROTECTION,
|
|
(CHAR)MM_INVALID_PROTECTION,
|
|
MM_EXECUTE_WRITECOPY,
|
|
(CHAR)MM_INVALID_PROTECTION,
|
|
(CHAR)MM_INVALID_PROTECTION,
|
|
(CHAR)MM_INVALID_PROTECTION,
|
|
(CHAR)MM_INVALID_PROTECTION,
|
|
(CHAR)MM_INVALID_PROTECTION,
|
|
(CHAR)MM_INVALID_PROTECTION,
|
|
(CHAR)MM_INVALID_PROTECTION
|
|
};
|
|
|
|
MMSESSION MmSession;
|
|
|
|
/* PRIVATE FUNCTIONS **********************************************************/
|
|
|
|
ULONG
|
|
NTAPI
|
|
MiMakeProtectionMask(IN ULONG Protect)
|
|
{
|
|
ULONG Mask1, Mask2, ProtectMask;
|
|
|
|
/* PAGE_EXECUTE_WRITECOMBINE is theoretically the maximum */
|
|
if (Protect >= (PAGE_WRITECOMBINE * 2)) return MM_INVALID_PROTECTION;
|
|
|
|
/*
|
|
* Windows API protection mask can be understood as two bitfields, differing
|
|
* by whether or not execute rights are being requested
|
|
*/
|
|
Mask1 = Protect & 0xF;
|
|
Mask2 = (Protect >> 4) & 0xF;
|
|
|
|
/* Check which field is there */
|
|
if (!Mask1)
|
|
{
|
|
/* Mask2 must be there, use it to determine the PTE protection */
|
|
if (!Mask2) return MM_INVALID_PROTECTION;
|
|
ProtectMask = MmUserProtectionToMask2[Mask2];
|
|
}
|
|
else
|
|
{
|
|
/* Mask2 should not be there, use Mask1 to determine the PTE mask */
|
|
if (Mask2) return MM_INVALID_PROTECTION;
|
|
ProtectMask = MmUserProtectionToMask1[Mask1];
|
|
}
|
|
|
|
/* Make sure the final mask is a valid one */
|
|
if (ProtectMask == MM_INVALID_PROTECTION) return MM_INVALID_PROTECTION;
|
|
|
|
/* Check for PAGE_GUARD option */
|
|
if (Protect & PAGE_GUARD)
|
|
{
|
|
/* It's not valid on no-access, nocache, or writecombine pages */
|
|
if ((ProtectMask == MM_NOACCESS) ||
|
|
(Protect & (PAGE_NOCACHE | PAGE_WRITECOMBINE)))
|
|
{
|
|
/* Fail such requests */
|
|
return MM_INVALID_PROTECTION;
|
|
}
|
|
|
|
/* This actually turns on guard page in this scenario! */
|
|
ProtectMask |= MM_DECOMMIT;
|
|
}
|
|
|
|
/* Check for nocache option */
|
|
if (Protect & PAGE_NOCACHE)
|
|
{
|
|
/* The earlier check should've eliminated this possibility */
|
|
ASSERT((Protect & PAGE_GUARD) == 0);
|
|
|
|
/* Check for no-access page or write combine page */
|
|
if ((ProtectMask == MM_NOACCESS) || (Protect & PAGE_WRITECOMBINE))
|
|
{
|
|
/* Such a request is invalid */
|
|
return MM_INVALID_PROTECTION;
|
|
}
|
|
|
|
/* Add the PTE flag */
|
|
ProtectMask |= MM_NOCACHE;
|
|
}
|
|
|
|
/* Check for write combine option */
|
|
if (Protect & PAGE_WRITECOMBINE)
|
|
{
|
|
/* The two earlier scenarios should've caught this */
|
|
ASSERT((Protect & (PAGE_GUARD | PAGE_NOACCESS)) == 0);
|
|
|
|
/* Don't allow on no-access pages */
|
|
if (ProtectMask == MM_NOACCESS) return MM_INVALID_PROTECTION;
|
|
|
|
/* This actually turns on write-combine in this scenario! */
|
|
ProtectMask |= MM_NOACCESS;
|
|
}
|
|
|
|
/* Return the final MM PTE protection mask */
|
|
return ProtectMask;
|
|
}
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
MiInitializeSystemSpaceMap(IN PVOID InputSession OPTIONAL)
|
|
{
|
|
SIZE_T AllocSize, BitmapSize;
|
|
PMMSESSION Session;
|
|
|
|
/* For now, always use the global session */
|
|
ASSERT(InputSession == NULL);
|
|
Session = &MmSession;
|
|
|
|
/* Initialize the system space lock */
|
|
Session->SystemSpaceViewLockPointer = &Session->SystemSpaceViewLock;
|
|
KeInitializeGuardedMutex(Session->SystemSpaceViewLockPointer);
|
|
|
|
/* Set the start address */
|
|
Session->SystemSpaceViewStart = MiSystemViewStart;
|
|
|
|
/* Create a bitmap to describe system space */
|
|
BitmapSize = sizeof(RTL_BITMAP) + ((((MmSystemViewSize / MI_SYSTEM_VIEW_BUCKET_SIZE) + 31) / 32) * sizeof(ULONG));
|
|
Session->SystemSpaceBitMap = ExAllocatePoolWithTag(NonPagedPool,
|
|
BitmapSize,
|
|
' mM');
|
|
ASSERT(Session->SystemSpaceBitMap);
|
|
RtlInitializeBitMap(Session->SystemSpaceBitMap,
|
|
(PULONG)(Session->SystemSpaceBitMap + 1),
|
|
MmSystemViewSize / MI_SYSTEM_VIEW_BUCKET_SIZE);
|
|
|
|
/* Set system space fully empty to begin with */
|
|
RtlClearAllBits(Session->SystemSpaceBitMap);
|
|
|
|
/* Set default hash flags */
|
|
Session->SystemSpaceHashSize = 31;
|
|
Session->SystemSpaceHashKey = Session->SystemSpaceHashSize - 1;
|
|
Session->SystemSpaceHashEntries = 0;
|
|
|
|
/* Calculate how much space for the hash views we'll need */
|
|
AllocSize = sizeof(MMVIEW) * Session->SystemSpaceHashSize;
|
|
ASSERT(AllocSize < PAGE_SIZE);
|
|
|
|
/* Allocate and zero the view table */
|
|
Session->SystemSpaceViewTable = ExAllocatePoolWithTag(NonPagedPool,
|
|
AllocSize,
|
|
' mM');
|
|
ASSERT(Session->SystemSpaceViewTable != NULL);
|
|
RtlZeroMemory(Session->SystemSpaceViewTable, AllocSize);
|
|
|
|
/* Success */
|
|
return TRUE;
|
|
}
|
|
|
|
PVOID
|
|
NTAPI
|
|
MiInsertInSystemSpace(IN PMMSESSION Session,
|
|
IN ULONG Buckets,
|
|
IN PCONTROL_AREA ControlArea)
|
|
{
|
|
PVOID Base;
|
|
ULONG Entry, Hash, i;
|
|
PAGED_CODE();
|
|
|
|
/* Only global mappings supported for now */
|
|
ASSERT(Session == &MmSession);
|
|
|
|
/* Stay within 4GB and don't go past the number of hash entries available */
|
|
ASSERT(Buckets < MI_SYSTEM_VIEW_BUCKET_SIZE);
|
|
ASSERT(Session->SystemSpaceHashEntries < Session->SystemSpaceHashSize);
|
|
|
|
/* Find space where to map this view */
|
|
i = RtlFindClearBitsAndSet(Session->SystemSpaceBitMap, Buckets, 0);
|
|
ASSERT(i != 0xFFFFFFFF);
|
|
Base = (PVOID)((ULONG_PTR)Session->SystemSpaceViewStart + (i * MI_SYSTEM_VIEW_BUCKET_SIZE));
|
|
|
|
/* Get the hash entry for this allocation */
|
|
Entry = ((ULONG_PTR)Base & ~(MI_SYSTEM_VIEW_BUCKET_SIZE - 1)) + Buckets;
|
|
Hash = (Entry >> 16) % Session->SystemSpaceHashKey;
|
|
|
|
/* Loop hash entries until a free one is found */
|
|
while (Session->SystemSpaceViewTable[Hash].Entry)
|
|
{
|
|
/* Unless we overflow, in which case loop back at hash o */
|
|
if (++Hash >= Session->SystemSpaceHashSize) Hash = 0;
|
|
}
|
|
|
|
/* Add this entry into the hash table */
|
|
Session->SystemSpaceViewTable[Hash].Entry = Entry;
|
|
Session->SystemSpaceViewTable[Hash].ControlArea = ControlArea;
|
|
|
|
/* Hash entry found, increment total and return the base address */
|
|
Session->SystemSpaceHashEntries++;
|
|
return Base;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
MiAddMappedPtes(IN PMMPTE FirstPte,
|
|
IN PFN_NUMBER PteCount,
|
|
IN PCONTROL_AREA ControlArea)
|
|
{
|
|
MMPTE TempPte;
|
|
PMMPTE PointerPte, ProtoPte, LastProtoPte, LastPte;
|
|
PSUBSECTION Subsection;
|
|
|
|
/* ARM3 doesn't support this yet */
|
|
ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0);
|
|
ASSERT(ControlArea->u.Flags.Rom == 0);
|
|
ASSERT(ControlArea->FilePointer == NULL);
|
|
|
|
/* Sanity checks */
|
|
ASSERT(PteCount != 0);
|
|
ASSERT(ControlArea->NumberOfMappedViews >= 1);
|
|
ASSERT(ControlArea->NumberOfUserReferences >= 1);
|
|
ASSERT(ControlArea->NumberOfSectionReferences != 0);
|
|
ASSERT(ControlArea->u.Flags.BeingCreated == 0);
|
|
ASSERT(ControlArea->u.Flags.BeingDeleted == 0);
|
|
ASSERT(ControlArea->u.Flags.BeingPurged == 0);
|
|
|
|
/* Get the PTEs for the actual mapping */
|
|
PointerPte = FirstPte;
|
|
LastPte = FirstPte + PteCount;
|
|
|
|
/* Get the prototype PTEs that desribe the section mapping in the subsection */
|
|
Subsection = (PSUBSECTION)(ControlArea + 1);
|
|
ProtoPte = Subsection->SubsectionBase;
|
|
LastProtoPte = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
|
|
|
|
/* Loop the PTEs for the mapping */
|
|
while (PointerPte < LastPte)
|
|
{
|
|
/* We may have run out of prototype PTEs in this subsection */
|
|
if (ProtoPte >= LastProtoPte)
|
|
{
|
|
/* But we don't handle this yet */
|
|
UNIMPLEMENTED;
|
|
while (TRUE);
|
|
}
|
|
|
|
/* The PTE should be completely clear */
|
|
ASSERT(PointerPte->u.Long == 0);
|
|
|
|
/* Build the prototype PTE and write it */
|
|
MI_MAKE_PROTOTYPE_PTE(&TempPte, ProtoPte);
|
|
MI_WRITE_INVALID_PTE(PointerPte, TempPte);
|
|
|
|
/* Keep going */
|
|
PointerPte++;
|
|
ProtoPte++;
|
|
}
|
|
|
|
/* No failure path */
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
#if (_MI_PAGING_LEVELS == 2)
|
|
VOID
|
|
NTAPI
|
|
MiFillSystemPageDirectory(IN PVOID Base,
|
|
IN SIZE_T NumberOfBytes)
|
|
{
|
|
PMMPDE PointerPde, LastPde, SystemMapPde;
|
|
MMPDE TempPde;
|
|
PFN_NUMBER PageFrameIndex;
|
|
KIRQL OldIrql;
|
|
PAGED_CODE();
|
|
|
|
/* Find the PDEs needed for this mapping */
|
|
PointerPde = MiAddressToPde(Base);
|
|
LastPde = MiAddressToPde((PVOID)((ULONG_PTR)Base + NumberOfBytes - 1));
|
|
|
|
/* Find the system double-mapped PDE that describes this mapping */
|
|
SystemMapPde = &MmSystemPagePtes[((ULONG_PTR)PointerPde & (SYSTEM_PD_SIZE - 1)) / sizeof(MMPTE)];
|
|
|
|
/* Use the PDE template and loop the PDEs */
|
|
TempPde = ValidKernelPde;
|
|
while (PointerPde <= LastPde)
|
|
{
|
|
/* Lock the PFN database */
|
|
OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
|
|
|
|
/* Check if we don't already have this PDE mapped */
|
|
if (SystemMapPde->u.Hard.Valid == 0)
|
|
{
|
|
/* Grab a page for it */
|
|
PageFrameIndex = MiRemoveZeroPage(MI_GET_NEXT_COLOR());
|
|
ASSERT(PageFrameIndex);
|
|
TempPde.u.Hard.PageFrameNumber = PageFrameIndex;
|
|
|
|
/* Initialize its PFN entry, with the parent system page directory page table */
|
|
MiInitializePfnForOtherProcess(PageFrameIndex,
|
|
PointerPde,
|
|
MmSystemPageDirectory[(PointerPde - MiAddressToPde(NULL)) / PDE_COUNT]);
|
|
|
|
/* Make the system PDE entry valid */
|
|
MI_WRITE_VALID_PTE(SystemMapPde, TempPde);
|
|
|
|
/* The system PDE entry might be the PDE itself, so check for this */
|
|
if (PointerPde->u.Hard.Valid == 0)
|
|
{
|
|
/* It's different, so make the real PDE valid too */
|
|
MI_WRITE_VALID_PTE(PointerPde, TempPde);
|
|
}
|
|
}
|
|
|
|
/* Release the lock and keep going with the next PDE */
|
|
KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
|
|
SystemMapPde++;
|
|
PointerPde++;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
MiCheckPurgeAndUpMapCount(IN PCONTROL_AREA ControlArea,
|
|
IN BOOLEAN FailIfSystemViews)
|
|
{
|
|
KIRQL OldIrql;
|
|
|
|
/* Flag not yet supported */
|
|
ASSERT(FailIfSystemViews == FALSE);
|
|
|
|
/* Lock the PFN database */
|
|
OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
|
|
|
|
/* State not yet supported */
|
|
ASSERT(ControlArea->u.Flags.BeingPurged == 0);
|
|
|
|
/* Increase the reference counts */
|
|
ControlArea->NumberOfMappedViews++;
|
|
ControlArea->NumberOfUserReferences++;
|
|
ASSERT(ControlArea->NumberOfSectionReferences != 0);
|
|
|
|
/* Release the PFN lock and return success */
|
|
KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
PSUBSECTION
|
|
NTAPI
|
|
MiLocateSubsection(IN PMMVAD Vad,
|
|
IN ULONG_PTR Vpn)
|
|
{
|
|
PSUBSECTION Subsection;
|
|
PCONTROL_AREA ControlArea;
|
|
ULONG PteOffset;
|
|
|
|
/* Get the control area */
|
|
ControlArea = Vad->ControlArea;
|
|
ASSERT(ControlArea->u.Flags.Rom == 0);
|
|
ASSERT(ControlArea->u.Flags.Image == 0);
|
|
ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0);
|
|
|
|
/* Get the subsection */
|
|
Subsection = (PSUBSECTION)(ControlArea + 1);
|
|
|
|
/* We only support single-subsection segments */
|
|
ASSERT(Subsection->SubsectionBase != NULL);
|
|
ASSERT(Vad->FirstPrototypePte >= Subsection->SubsectionBase);
|
|
ASSERT(Vad->FirstPrototypePte < &Subsection->SubsectionBase[Subsection->PtesInSubsection]);
|
|
|
|
/* Compute the PTE offset */
|
|
PteOffset = (ULONG_PTR)Vpn - Vad->StartingVpn;
|
|
PteOffset += Vad->FirstPrototypePte - Subsection->SubsectionBase;
|
|
|
|
/* Again, we only support single-subsection segments */
|
|
ASSERT(PteOffset < 0xF0000000);
|
|
ASSERT(PteOffset < Subsection->PtesInSubsection);
|
|
|
|
/* Return the subsection */
|
|
return Subsection;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
MiSegmentDelete(IN PSEGMENT Segment)
|
|
{
|
|
PCONTROL_AREA ControlArea;
|
|
SEGMENT_FLAGS SegmentFlags;
|
|
PSUBSECTION Subsection;
|
|
PMMPTE PointerPte, LastPte, PteForProto;
|
|
MMPTE TempPte;
|
|
KIRQL OldIrql;
|
|
|
|
/* Capture data */
|
|
SegmentFlags = Segment->SegmentFlags;
|
|
ControlArea = Segment->ControlArea;
|
|
|
|
/* Make sure control area is on the right delete path */
|
|
ASSERT(ControlArea->u.Flags.BeingDeleted == 1);
|
|
ASSERT(ControlArea->WritableUserReferences == 0);
|
|
|
|
/* These things are not supported yet */
|
|
ASSERT(ControlArea->DereferenceList.Flink == NULL);
|
|
ASSERT(!(ControlArea->u.Flags.Image) & !(ControlArea->u.Flags.File));
|
|
ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0);
|
|
ASSERT(ControlArea->u.Flags.Rom == 0);
|
|
|
|
/* Get the subsection and PTEs for this segment */
|
|
Subsection = (PSUBSECTION)(ControlArea + 1);
|
|
PointerPte = Subsection->SubsectionBase;
|
|
LastPte = PointerPte + Segment->NonExtendedPtes;
|
|
|
|
/* Lock the PFN database */
|
|
OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
|
|
|
|
/* Check if the master PTE is invalid */
|
|
PteForProto = MiAddressToPte(PointerPte);
|
|
if (!PteForProto->u.Hard.Valid)
|
|
{
|
|
/* Fault it in */
|
|
MiMakeSystemAddressValidPfn(PointerPte, OldIrql);
|
|
}
|
|
|
|
/* Loop all the segment PTEs */
|
|
while (PointerPte < LastPte)
|
|
{
|
|
/* Check if it's time to switch master PTEs if we passed a PDE boundary */
|
|
if (!((ULONG_PTR)PointerPte & (PD_SIZE - 1)) &&
|
|
(PointerPte != Subsection->SubsectionBase))
|
|
{
|
|
/* Check if the master PTE is invalid */
|
|
PteForProto = MiAddressToPte(PointerPte);
|
|
if (!PteForProto->u.Hard.Valid)
|
|
{
|
|
/* Fault it in */
|
|
MiMakeSystemAddressValidPfn(PointerPte, OldIrql);
|
|
}
|
|
}
|
|
|
|
/* This should be a prototype PTE */
|
|
TempPte = *PointerPte;
|
|
ASSERT(SegmentFlags.LargePages == 0);
|
|
ASSERT(TempPte.u.Hard.Valid == 0);
|
|
ASSERT(TempPte.u.Soft.Prototype == 1);
|
|
|
|
/* Zero the PTE and keep going */
|
|
PointerPte->u.Long = 0;
|
|
PointerPte++;
|
|
}
|
|
|
|
/* Release the PFN lock */
|
|
KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
|
|
|
|
/* Free the structures */
|
|
ExFreePool(ControlArea);
|
|
ExFreePool(Segment);
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
MiCheckControlArea(IN PCONTROL_AREA ControlArea,
|
|
IN KIRQL OldIrql)
|
|
{
|
|
BOOLEAN DeleteSegment = FALSE;
|
|
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
|
|
|
|
/* Check if this is the last reference or view */
|
|
if (!(ControlArea->NumberOfMappedViews) &&
|
|
!(ControlArea->NumberOfSectionReferences))
|
|
{
|
|
/* There should be no more user references either */
|
|
ASSERT(ControlArea->NumberOfUserReferences == 0);
|
|
|
|
/* Not yet supported */
|
|
ASSERT(ControlArea->FilePointer == NULL);
|
|
|
|
/* The control area is being destroyed */
|
|
ControlArea->u.Flags.BeingDeleted = TRUE;
|
|
DeleteSegment = TRUE;
|
|
}
|
|
|
|
/* Release the PFN lock */
|
|
KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
|
|
|
|
/* Delete the segment if needed */
|
|
if (DeleteSegment)
|
|
{
|
|
/* No more user write references at all */
|
|
ASSERT(ControlArea->WritableUserReferences == 0);
|
|
MiSegmentDelete(ControlArea->Segment);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
MiRemoveMappedView(IN PEPROCESS CurrentProcess,
|
|
IN PMMVAD Vad)
|
|
{
|
|
KIRQL OldIrql;
|
|
PCONTROL_AREA ControlArea;
|
|
|
|
/* Get the control area */
|
|
ControlArea = Vad->ControlArea;
|
|
|
|
/* We only support non-extendable, non-image, pagefile-backed regular sections */
|
|
ASSERT(Vad->u.VadFlags.VadType == VadNone);
|
|
ASSERT(Vad->u2.VadFlags2.ExtendableFile == FALSE);
|
|
ASSERT(ControlArea);
|
|
ASSERT(ControlArea->FilePointer == NULL);
|
|
|
|
/* Delete the actual virtual memory pages */
|
|
MiDeleteVirtualAddresses(Vad->StartingVpn << PAGE_SHIFT,
|
|
(Vad->EndingVpn << PAGE_SHIFT) | (PAGE_SIZE - 1),
|
|
Vad);
|
|
|
|
/* Release the working set */
|
|
MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
|
|
|
|
/* Lock the PFN database */
|
|
OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
|
|
|
|
/* Remove references */
|
|
ControlArea->NumberOfMappedViews--;
|
|
ControlArea->NumberOfUserReferences--;
|
|
|
|
/* Check if it should be destroyed */
|
|
MiCheckControlArea(ControlArea, OldIrql);
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
MiMapViewInSystemSpace(IN PVOID Section,
|
|
IN PMMSESSION Session,
|
|
OUT PVOID *MappedBase,
|
|
IN OUT PSIZE_T ViewSize)
|
|
{
|
|
PVOID Base;
|
|
PCONTROL_AREA ControlArea;
|
|
ULONG Buckets, SectionSize;
|
|
NTSTATUS Status;
|
|
PAGED_CODE();
|
|
|
|
/* Only global mappings for now */
|
|
ASSERT(Session == &MmSession);
|
|
|
|
/* Get the control area, check for any flags ARM3 doesn't yet support */
|
|
ControlArea = ((PSECTION)Section)->Segment->ControlArea;
|
|
ASSERT(ControlArea->u.Flags.Image == 0);
|
|
ASSERT(ControlArea->FilePointer == NULL);
|
|
ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0);
|
|
ASSERT(ControlArea->u.Flags.Rom == 0);
|
|
ASSERT(ControlArea->u.Flags.WasPurged == 0);
|
|
|
|
/* Increase the reference and map count on the control area, no purges yet */
|
|
Status = MiCheckPurgeAndUpMapCount(ControlArea, FALSE);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
/* Get the section size at creation time */
|
|
SectionSize = ((PSECTION)Section)->SizeOfSection.LowPart;
|
|
|
|
/* If the caller didn't specify a view size, assume the whole section */
|
|
if (!(*ViewSize)) *ViewSize = SectionSize;
|
|
|
|
/* Check if the caller wanted a larger section than the view */
|
|
if (*ViewSize > SectionSize)
|
|
{
|
|
/* We should probably fail. FIXME TODO */
|
|
UNIMPLEMENTED;
|
|
while (TRUE);
|
|
}
|
|
|
|
/* Get the number of 64K buckets required for this mapping */
|
|
Buckets = *ViewSize / MI_SYSTEM_VIEW_BUCKET_SIZE;
|
|
if (*ViewSize & (MI_SYSTEM_VIEW_BUCKET_SIZE - 1)) Buckets++;
|
|
|
|
/* Check if the view is more than 4GB large */
|
|
if (Buckets >= MI_SYSTEM_VIEW_BUCKET_SIZE)
|
|
{
|
|
/* We should probably fail */
|
|
UNIMPLEMENTED;
|
|
while (TRUE);
|
|
}
|
|
|
|
/* Insert this view into system space and get a base address for it */
|
|
Base = MiInsertInSystemSpace(Session, Buckets, ControlArea);
|
|
ASSERT(Base);
|
|
|
|
#if (_MI_PAGING_LEVELS == 2)
|
|
/* Create the PDEs needed for this mapping, and double-map them if needed */
|
|
MiFillSystemPageDirectory(Base, Buckets * MI_SYSTEM_VIEW_BUCKET_SIZE);
|
|
#endif
|
|
|
|
/* Create the actual prototype PTEs for this mapping */
|
|
Status = MiAddMappedPtes(MiAddressToPte(Base),
|
|
BYTES_TO_PAGES(*ViewSize),
|
|
ControlArea);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
/* Return the base adress of the mapping and success */
|
|
*MappedBase = Base;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
MiMapViewOfDataSection(IN PCONTROL_AREA ControlArea,
|
|
IN PEPROCESS Process,
|
|
IN PVOID *BaseAddress,
|
|
IN PLARGE_INTEGER SectionOffset,
|
|
IN PSIZE_T ViewSize,
|
|
IN PSECTION Section,
|
|
IN SECTION_INHERIT InheritDisposition,
|
|
IN ULONG ProtectionMask,
|
|
IN ULONG CommitSize,
|
|
IN ULONG_PTR ZeroBits,
|
|
IN ULONG AllocationType)
|
|
{
|
|
PMMVAD Vad;
|
|
PETHREAD Thread = PsGetCurrentThread();
|
|
ULONG_PTR StartAddress, EndingAddress;
|
|
PSUBSECTION Subsection;
|
|
PSEGMENT Segment;
|
|
PFN_NUMBER PteOffset;
|
|
NTSTATUS Status;
|
|
|
|
/* Get the segment and subection for this section */
|
|
Segment = ControlArea->Segment;
|
|
Subsection = (PSUBSECTION)(ControlArea + 1);
|
|
|
|
/* Non-pagefile-backed sections not supported */
|
|
ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0);
|
|
ASSERT(ControlArea->u.Flags.Rom == 0);
|
|
ASSERT(ControlArea->FilePointer == NULL);
|
|
ASSERT(Segment->SegmentFlags.TotalNumberOfPtes4132 == 0);
|
|
|
|
/* Based sections not supported */
|
|
ASSERT(Section->Address.StartingVpn == 0);
|
|
|
|
/* These flags/parameters are not supported */
|
|
ASSERT((AllocationType & MEM_DOS_LIM) == 0);
|
|
ASSERT((AllocationType & MEM_RESERVE) == 0);
|
|
ASSERT(Process->VmTopDown == 0);
|
|
ASSERT(Section->u.Flags.CopyOnWrite == FALSE);
|
|
ASSERT(ZeroBits == 0);
|
|
|
|
/* First, increase the map count. No purging is supported yet */
|
|
Status = MiCheckPurgeAndUpMapCount(ControlArea, FALSE);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
/* Check if the caller specified the view size */
|
|
if (!(*ViewSize))
|
|
{
|
|
/* The caller did not, so pick a 64K aligned view size based on the offset */
|
|
SectionOffset->LowPart &= ~(_64K - 1);
|
|
*ViewSize = Section->SizeOfSection.QuadPart - SectionOffset->QuadPart;
|
|
}
|
|
else
|
|
{
|
|
/* A size was specified, align it to a 64K boundary */
|
|
*ViewSize += SectionOffset->LowPart & (_64K - 1);
|
|
|
|
/* Align the offset as well to make this an aligned map */
|
|
SectionOffset->LowPart &= ~((ULONG)_64K - 1);
|
|
}
|
|
|
|
/* We must be dealing with a 64KB aligned offset */
|
|
ASSERT((SectionOffset->LowPart & ((ULONG)_64K - 1)) == 0);
|
|
|
|
/* It's illegal to try to map more than 2GB */
|
|
if (*ViewSize >= 0x80000000) return STATUS_INVALID_VIEW_SIZE;
|
|
|
|
/* Within this section, figure out which PTEs will describe the view */
|
|
PteOffset = SectionOffset->QuadPart >> PAGE_SHIFT;
|
|
|
|
/* The offset must be in this segment's PTE chunk and it must be valid */
|
|
ASSERT(PteOffset < Segment->TotalNumberOfPtes);
|
|
ASSERT(((SectionOffset->QuadPart + *ViewSize + PAGE_SIZE - 1) >> PAGE_SHIFT) >= PteOffset);
|
|
|
|
/* In ARM3, only one subsection is used for now. It must contain these PTEs */
|
|
ASSERT(PteOffset < Subsection->PtesInSubsection);
|
|
ASSERT(Subsection->SubsectionBase != NULL);
|
|
|
|
/* In ARM3, only MEM_COMMIT is supported for now. The PTEs must've been committed */
|
|
ASSERT(Segment->NumberOfCommittedPages >= Segment->TotalNumberOfPtes);
|
|
|
|
/* Did the caller specify an address? */
|
|
if (!(*BaseAddress))
|
|
{
|
|
/* Which way should we search? */
|
|
if (AllocationType & MEM_TOP_DOWN)
|
|
{
|
|
/* No, find an address top-down */
|
|
Status = MiFindEmptyAddressRangeDownTree(*ViewSize,
|
|
(ULONG_PTR)MM_HIGHEST_VAD_ADDRESS,
|
|
_64K,
|
|
&Process->VadRoot,
|
|
&StartAddress,
|
|
(PMMADDRESS_NODE*)&Process->VadFreeHint);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
}
|
|
else
|
|
{
|
|
/* No, find an address bottom-up */
|
|
Status = MiFindEmptyAddressRangeInTree(*ViewSize,
|
|
_64K,
|
|
&Process->VadRoot,
|
|
(PMMADDRESS_NODE*)&Process->VadFreeHint,
|
|
&StartAddress);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* This (rather easy) code path is not yet implemented */
|
|
UNIMPLEMENTED;
|
|
while (TRUE);
|
|
}
|
|
|
|
/* Get the ending address, which is the last piece we need for the VAD */
|
|
EndingAddress = (StartAddress + *ViewSize - 1) | (PAGE_SIZE - 1);
|
|
|
|
/* A VAD can now be allocated. Do so and zero it out */
|
|
Vad = ExAllocatePoolWithTag(NonPagedPool, sizeof(MMVAD), 'ldaV');
|
|
ASSERT(Vad);
|
|
RtlZeroMemory(Vad, sizeof(MMVAD));
|
|
|
|
/* Write all the data required in the VAD for handling a fault */
|
|
Vad->StartingVpn = StartAddress >> PAGE_SHIFT;
|
|
Vad->EndingVpn = EndingAddress >> PAGE_SHIFT;
|
|
Vad->ControlArea = ControlArea;
|
|
Vad->u.VadFlags.Protection = ProtectionMask;
|
|
Vad->u2.VadFlags2.FileOffset = SectionOffset->QuadPart >> 16;
|
|
Vad->u2.VadFlags2.Inherit = (InheritDisposition == ViewShare);
|
|
if ((AllocationType & SEC_NO_CHANGE) || (Section->u.Flags.NoChange))
|
|
{
|
|
/* This isn't really implemented yet, but handle setting the flag */
|
|
Vad->u.VadFlags.NoChange = 1;
|
|
Vad->u2.VadFlags2.SecNoChange = 1;
|
|
}
|
|
|
|
/* Finally, write down the first and last prototype PTE */
|
|
Vad->FirstPrototypePte = &Subsection->SubsectionBase[PteOffset];
|
|
PteOffset += (Vad->EndingVpn - Vad->StartingVpn);
|
|
Vad->LastContiguousPte = &Subsection->SubsectionBase[PteOffset];
|
|
|
|
/* Make sure the last PTE is valid and still within the subsection */
|
|
ASSERT(PteOffset < Subsection->PtesInSubsection);
|
|
ASSERT(Vad->FirstPrototypePte <= Vad->LastContiguousPte);
|
|
|
|
/* FIXME: Should setup VAD bitmap */
|
|
Status = STATUS_SUCCESS;
|
|
|
|
/* Pretend as if we own the working set */
|
|
MiLockProcessWorkingSet(Process, Thread);
|
|
|
|
/* Insert the VAD */
|
|
MiInsertVad(Vad, Process);
|
|
|
|
/* Release the working set */
|
|
MiUnlockProcessWorkingSet(Process, Thread);
|
|
|
|
/* Windows stores this for accounting purposes, do so as well */
|
|
if (!Segment->u2.FirstMappedVa) Segment->u2.FirstMappedVa = (PVOID)StartAddress;
|
|
|
|
/* Finally, let the caller know where, and for what size, the view was mapped */
|
|
*ViewSize = (ULONG_PTR)EndingAddress - (ULONG_PTR)StartAddress + 1;
|
|
*BaseAddress = (PVOID)StartAddress;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
MiCreatePagingFileMap(OUT PSEGMENT *Segment,
|
|
IN PSIZE_T MaximumSize,
|
|
IN ULONG ProtectionMask,
|
|
IN ULONG AllocationAttributes)
|
|
{
|
|
SIZE_T SizeLimit;
|
|
PFN_NUMBER PteCount;
|
|
PMMPTE PointerPte;
|
|
MMPTE TempPte;
|
|
PCONTROL_AREA ControlArea;
|
|
PSEGMENT NewSegment;
|
|
PSUBSECTION Subsection;
|
|
PAGED_CODE();
|
|
|
|
/* No large pages in ARM3 yet */
|
|
ASSERT((AllocationAttributes & SEC_LARGE_PAGES) == 0);
|
|
|
|
/* Pagefile-backed sections need a known size */
|
|
if (!(*MaximumSize)) return STATUS_INVALID_PARAMETER_4;
|
|
|
|
/* Calculate the maximum size possible, given the Prototype PTEs we'll need */
|
|
SizeLimit = MAXULONG_PTR - sizeof(SEGMENT);
|
|
SizeLimit /= sizeof(MMPTE);
|
|
SizeLimit <<= PAGE_SHIFT;
|
|
|
|
/* Fail if this size is too big */
|
|
if (*MaximumSize > SizeLimit) return STATUS_SECTION_TOO_BIG;
|
|
|
|
/* Calculate how many Prototype PTEs will be needed */
|
|
PteCount = (*MaximumSize + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
|
|
|
/* For commited memory, we must have a valid protection mask */
|
|
if (AllocationAttributes & SEC_COMMIT) ASSERT(ProtectionMask != 0);
|
|
|
|
/* The segment contains all the Prototype PTEs, allocate it in paged pool */
|
|
NewSegment = ExAllocatePoolWithTag(PagedPool,
|
|
sizeof(SEGMENT) +
|
|
sizeof(MMPTE) * (PteCount - 1),
|
|
'tSmM');
|
|
ASSERT(NewSegment);
|
|
*Segment = NewSegment;
|
|
|
|
/* Now allocate the control area, which has the subsection structure */
|
|
ControlArea = ExAllocatePoolWithTag(NonPagedPool,
|
|
sizeof(CONTROL_AREA) + sizeof(SUBSECTION),
|
|
'tCmM');
|
|
ASSERT(ControlArea);
|
|
|
|
/* And zero it out, filling the basic segmnet pointer and reference fields */
|
|
RtlZeroMemory(ControlArea, sizeof(CONTROL_AREA) + sizeof(SUBSECTION));
|
|
ControlArea->Segment = NewSegment;
|
|
ControlArea->NumberOfSectionReferences = 1;
|
|
ControlArea->NumberOfUserReferences = 1;
|
|
|
|
/* Convert allocation attributes to control area flags */
|
|
if (AllocationAttributes & SEC_BASED) ControlArea->u.Flags.Based = 1;
|
|
if (AllocationAttributes & SEC_RESERVE) ControlArea->u.Flags.Reserve = 1;
|
|
if (AllocationAttributes & SEC_COMMIT) ControlArea->u.Flags.Commit = 1;
|
|
|
|
/* The subsection follows, write the mask, PTE count and point back to the CA */
|
|
Subsection = (PSUBSECTION)(ControlArea + 1);
|
|
Subsection->ControlArea = ControlArea;
|
|
Subsection->PtesInSubsection = PteCount;
|
|
Subsection->u.SubsectionFlags.Protection = ProtectionMask;
|
|
|
|
/* Zero out the segment's prototype PTEs, and link it with the control area */
|
|
PointerPte = &NewSegment->ThePtes[0];
|
|
RtlZeroMemory(NewSegment, sizeof(SEGMENT));
|
|
NewSegment->PrototypePte = PointerPte;
|
|
NewSegment->ControlArea = ControlArea;
|
|
|
|
/* Save some extra accounting data for the segment as well */
|
|
NewSegment->u1.CreatingProcess = PsGetCurrentProcess();
|
|
NewSegment->SizeOfSegment = PteCount * PAGE_SIZE;
|
|
NewSegment->TotalNumberOfPtes = PteCount;
|
|
NewSegment->NonExtendedPtes = PteCount;
|
|
|
|
/* The subsection's base address is the first Prototype PTE in the segment */
|
|
Subsection->SubsectionBase = PointerPte;
|
|
|
|
/* Start with an empty PTE, unless this is a commit operation */
|
|
TempPte.u.Long = 0;
|
|
if (AllocationAttributes & SEC_COMMIT)
|
|
{
|
|
/* In which case, write down the protection mask in the Prototype PTEs */
|
|
TempPte.u.Soft.Protection = ProtectionMask;
|
|
|
|
/* For accounting, also mark these pages as being committed */
|
|
NewSegment->NumberOfCommittedPages = PteCount;
|
|
}
|
|
|
|
/* The template PTE itself for the segment should also have the mask set */
|
|
NewSegment->SegmentPteTemplate.u.Soft.Protection = ProtectionMask;
|
|
|
|
/* Write out the prototype PTEs, for now they're simply demand zero */
|
|
RtlFillMemoryUlong(PointerPte, PteCount * sizeof(MMPTE), TempPte.u.Long);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
PFILE_OBJECT
|
|
NTAPI
|
|
MmGetFileObjectForSection(IN PVOID SectionObject)
|
|
{
|
|
PSECTION_OBJECT Section;
|
|
ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
|
|
ASSERT(SectionObject != NULL);
|
|
|
|
/* Check if it's an ARM3, or ReactOS section */
|
|
if ((ULONG_PTR)SectionObject & 1)
|
|
{
|
|
/* Return the file pointer stored in the control area */
|
|
Section = (PVOID)((ULONG_PTR)SectionObject & ~1);
|
|
return Section->Segment->ControlArea->FilePointer;
|
|
}
|
|
|
|
/* Return the file object */
|
|
return ((PROS_SECTION_OBJECT)SectionObject)->FileObject;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
MmGetFileNameForFileObject(IN PFILE_OBJECT FileObject,
|
|
OUT POBJECT_NAME_INFORMATION *ModuleName)
|
|
{
|
|
POBJECT_NAME_INFORMATION ObjectNameInfo;
|
|
NTSTATUS Status;
|
|
ULONG ReturnLength;
|
|
|
|
/* Allocate memory for our structure */
|
|
ObjectNameInfo = ExAllocatePoolWithTag(PagedPool, 1024, ' mM');
|
|
if (!ObjectNameInfo) return STATUS_NO_MEMORY;
|
|
|
|
/* Query the name */
|
|
Status = ObQueryNameString(FileObject,
|
|
ObjectNameInfo,
|
|
1024,
|
|
&ReturnLength);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Failed, free memory */
|
|
DPRINT1("Name query failed\n");
|
|
ExFreePoolWithTag(ObjectNameInfo, ' mM');
|
|
*ModuleName = NULL;
|
|
return Status;
|
|
}
|
|
|
|
/* Success */
|
|
*ModuleName = ObjectNameInfo;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
MmGetFileNameForSection(IN PVOID Section,
|
|
OUT POBJECT_NAME_INFORMATION *ModuleName)
|
|
{
|
|
PFILE_OBJECT FileObject;
|
|
|
|
/* Make sure it's an image section */
|
|
if ((ULONG_PTR)Section & 1)
|
|
{
|
|
/* Check ARM3 Section flag */
|
|
if (((PSECTION)((ULONG_PTR)Section & ~1))->u.Flags.Image == 0)
|
|
{
|
|
/* It's not, fail */
|
|
DPRINT1("Not an image section\n");
|
|
return STATUS_SECTION_NOT_IMAGE;
|
|
}
|
|
}
|
|
else if (!(((PROS_SECTION_OBJECT)Section)->AllocationAttributes & SEC_IMAGE))
|
|
{
|
|
/* It's not, fail */
|
|
DPRINT1("Not an image section\n");
|
|
return STATUS_SECTION_NOT_IMAGE;
|
|
}
|
|
|
|
/* Get the file object */
|
|
FileObject = MmGetFileObjectForSection(Section);
|
|
return MmGetFileNameForFileObject(FileObject, ModuleName);
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
MmGetFileNameForAddress(IN PVOID Address,
|
|
OUT PUNICODE_STRING ModuleName)
|
|
{
|
|
PVOID Section;
|
|
PMEMORY_AREA MemoryArea;
|
|
POBJECT_NAME_INFORMATION ModuleNameInformation;
|
|
PVOID AddressSpace;
|
|
NTSTATUS Status;
|
|
PFILE_OBJECT FileObject = NULL;
|
|
PMMVAD Vad;
|
|
PCONTROL_AREA ControlArea;
|
|
|
|
/* Lock address space */
|
|
AddressSpace = MmGetCurrentAddressSpace();
|
|
MmLockAddressSpace(AddressSpace);
|
|
|
|
/* Locate the memory area for the process by address */
|
|
MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, Address);
|
|
if (!MemoryArea)
|
|
{
|
|
/* Fail, the address does not exist */
|
|
InvalidAddress:
|
|
DPRINT1("Invalid address\n");
|
|
MmUnlockAddressSpace(AddressSpace);
|
|
return STATUS_INVALID_ADDRESS;
|
|
}
|
|
|
|
/* Check if it's a section view (RosMm section) or ARM3 section */
|
|
if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
|
|
{
|
|
/* Get the section pointer to the SECTION_OBJECT */
|
|
Section = MemoryArea->Data.SectionData.Section;
|
|
|
|
/* Unlock address space */
|
|
MmUnlockAddressSpace(AddressSpace);
|
|
|
|
/* Get the filename of the section */
|
|
Status = MmGetFileNameForSection(Section, &ModuleNameInformation);
|
|
}
|
|
else if (MemoryArea->Type == MEMORY_AREA_OWNED_BY_ARM3)
|
|
{
|
|
/* Get the VAD */
|
|
Vad = MiLocateAddress(Address);
|
|
if (!Vad) goto InvalidAddress;
|
|
|
|
/* Make sure it's not a VM VAD */
|
|
if (Vad->u.VadFlags.PrivateMemory == 1)
|
|
{
|
|
NotSection:
|
|
DPRINT1("Address is not a section\n");
|
|
MmUnlockAddressSpace(AddressSpace);
|
|
return STATUS_SECTION_NOT_IMAGE;
|
|
}
|
|
|
|
/* Get the control area */
|
|
ControlArea = Vad->ControlArea;
|
|
if (!(ControlArea) || !(ControlArea->u.Flags.Image)) goto NotSection;
|
|
|
|
/* Get the file object */
|
|
FileObject = ControlArea->FilePointer;
|
|
ASSERT(FileObject != NULL);
|
|
ObReferenceObject(FileObject);
|
|
|
|
/* Unlock address space */
|
|
MmUnlockAddressSpace(AddressSpace);
|
|
|
|
/* Get the filename of the file object */
|
|
Status = MmGetFileNameForFileObject(FileObject, &ModuleNameInformation);
|
|
|
|
/* Dereference it */
|
|
ObDereferenceObject(FileObject);
|
|
}
|
|
else
|
|
{
|
|
/* Trying to access virtual memory or something */
|
|
goto InvalidAddress;
|
|
}
|
|
|
|
/* Check if we were able to get the file object name */
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Init modulename */
|
|
RtlCreateUnicodeString(ModuleName,
|
|
ModuleNameInformation->Name.Buffer);
|
|
|
|
/* Free temp taged buffer from MmGetFileNameForFileObject() */
|
|
ExFreePoolWithTag(ModuleNameInformation, ' mM');
|
|
DPRINT("Found ModuleName %S by address %p\n", ModuleName->Buffer, Address);
|
|
}
|
|
|
|
/* Return status */
|
|
return Status;
|
|
}
|
|
|
|
/* PUBLIC FUNCTIONS ***********************************************************/
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
MmCreateArm3Section(OUT PVOID *SectionObject,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
|
|
IN PLARGE_INTEGER InputMaximumSize,
|
|
IN ULONG SectionPageProtection,
|
|
IN ULONG AllocationAttributes,
|
|
IN HANDLE FileHandle OPTIONAL,
|
|
IN PFILE_OBJECT FileObject OPTIONAL)
|
|
{
|
|
SECTION Section;
|
|
PSECTION NewSection;
|
|
PSUBSECTION Subsection;
|
|
PSEGMENT NewSegment;
|
|
NTSTATUS Status;
|
|
PCONTROL_AREA ControlArea;
|
|
ULONG ProtectionMask;
|
|
|
|
/* ARM3 does not yet support this */
|
|
ASSERT(FileHandle == NULL);
|
|
ASSERT(FileObject == NULL);
|
|
ASSERT((AllocationAttributes & SEC_LARGE_PAGES) == 0);
|
|
ASSERT((AllocationAttributes & SEC_BASED) == 0);
|
|
|
|
/* Make the same sanity checks that the Nt interface should've validated */
|
|
ASSERT((AllocationAttributes & ~(SEC_COMMIT | SEC_RESERVE | SEC_BASED |
|
|
SEC_LARGE_PAGES | SEC_IMAGE | SEC_NOCACHE |
|
|
SEC_NO_CHANGE)) == 0);
|
|
ASSERT((AllocationAttributes & (SEC_COMMIT | SEC_RESERVE | SEC_IMAGE)) != 0);
|
|
ASSERT(!((AllocationAttributes & SEC_IMAGE) &&
|
|
(AllocationAttributes & (SEC_COMMIT | SEC_RESERVE |
|
|
SEC_NOCACHE | SEC_NO_CHANGE))));
|
|
ASSERT(!((AllocationAttributes & SEC_COMMIT) && (AllocationAttributes & SEC_RESERVE)));
|
|
ASSERT(!((SectionPageProtection & PAGE_NOCACHE) ||
|
|
(SectionPageProtection & PAGE_WRITECOMBINE) ||
|
|
(SectionPageProtection & PAGE_GUARD) ||
|
|
(SectionPageProtection & PAGE_NOACCESS)));
|
|
|
|
/* Convert section flag to page flag */
|
|
if (AllocationAttributes & SEC_NOCACHE) SectionPageProtection |= PAGE_NOCACHE;
|
|
|
|
/* Check to make sure the protection is correct. Nt* does this already */
|
|
ProtectionMask = MiMakeProtectionMask(SectionPageProtection);
|
|
if (ProtectionMask == MM_INVALID_PROTECTION) return STATUS_INVALID_PAGE_PROTECTION;
|
|
|
|
/* A handle must be supplied with SEC_IMAGE, and this is the no-handle path */
|
|
if (AllocationAttributes & SEC_IMAGE) return STATUS_INVALID_FILE_FOR_SECTION;
|
|
|
|
/* So this must be a pagefile-backed section, create the mappings needed */
|
|
Status = MiCreatePagingFileMap(&NewSegment,
|
|
(PSIZE_T)InputMaximumSize,
|
|
ProtectionMask,
|
|
AllocationAttributes);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
/* Set the initial section object data */
|
|
Section.InitialPageProtection = SectionPageProtection;
|
|
Section.Segment = NULL;
|
|
Section.SizeOfSection.QuadPart = NewSegment->SizeOfSegment;
|
|
Section.Segment = NewSegment;
|
|
|
|
/* THe mapping created a control area and segment, save the flags */
|
|
ControlArea = NewSegment->ControlArea;
|
|
Section.u.LongFlags = ControlArea->u.LongFlags;
|
|
|
|
/* ARM3 cannot support these right now, make sure they're not being set */
|
|
ASSERT(ControlArea->u.Flags.Image == 0);
|
|
ASSERT(ControlArea->FilePointer == NULL);
|
|
ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0);
|
|
ASSERT(ControlArea->u.Flags.Rom == 0);
|
|
ASSERT(ControlArea->u.Flags.WasPurged == 0);
|
|
|
|
/* A pagefile-backed mapping only has one subsection, and this is all ARM3 supports */
|
|
Subsection = (PSUBSECTION)(ControlArea + 1);
|
|
ASSERT(Subsection->NextSubsection == NULL);
|
|
|
|
/* Create the actual section object, with enough space for the prototype PTEs */
|
|
Status = ObCreateObject(ExGetPreviousMode(),
|
|
MmSectionObjectType,
|
|
ObjectAttributes,
|
|
ExGetPreviousMode(),
|
|
NULL,
|
|
sizeof(SECTION),
|
|
sizeof(SECTION) +
|
|
NewSegment->TotalNumberOfPtes * sizeof(MMPTE),
|
|
sizeof(CONTROL_AREA) + sizeof(SUBSECTION),
|
|
(PVOID*)&NewSection);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
/* Now copy the local section object from the stack into this new object */
|
|
RtlCopyMemory(NewSection, &Section, sizeof(SECTION));
|
|
NewSection->Address.StartingVpn = 0;
|
|
|
|
/* Return the object and the creation status */
|
|
*SectionObject = (PVOID)NewSection;
|
|
return Status;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
MmMapViewOfArm3Section(IN PVOID SectionObject,
|
|
IN PEPROCESS Process,
|
|
IN OUT PVOID *BaseAddress,
|
|
IN ULONG_PTR ZeroBits,
|
|
IN SIZE_T CommitSize,
|
|
IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
|
|
IN OUT PSIZE_T ViewSize,
|
|
IN SECTION_INHERIT InheritDisposition,
|
|
IN ULONG AllocationType,
|
|
IN ULONG Protect)
|
|
{
|
|
KAPC_STATE ApcState;
|
|
BOOLEAN Attached = FALSE;
|
|
PSECTION Section;
|
|
PCONTROL_AREA ControlArea;
|
|
ULONG ProtectionMask;
|
|
NTSTATUS Status;
|
|
PAGED_CODE();
|
|
|
|
/* Get the segment and control area */
|
|
Section = (PSECTION)SectionObject;
|
|
ControlArea = Section->Segment->ControlArea;
|
|
|
|
/* These flags/states are not yet supported by ARM3 */
|
|
ASSERT(Section->u.Flags.Image == 0);
|
|
ASSERT(Section->u.Flags.NoCache == 0);
|
|
ASSERT(Section->u.Flags.WriteCombined == 0);
|
|
ASSERT((AllocationType & MEM_RESERVE) == 0);
|
|
ASSERT(ControlArea->u.Flags.PhysicalMemory == 0);
|
|
|
|
|
|
#if 0
|
|
/* FIXME: Check if the mapping protection is compatible with the create */
|
|
if (!MiIsProtectionCompatible(Section->InitialPageProtection, Protect))
|
|
{
|
|
DPRINT1("Mapping protection is incompatible\n");
|
|
return STATUS_SECTION_PROTECTION;
|
|
}
|
|
#endif
|
|
|
|
/* Check if the offset and size would cause an overflow */
|
|
if ((SectionOffset->QuadPart + *ViewSize) < SectionOffset->QuadPart)
|
|
{
|
|
DPRINT1("Section offset overflows\n");
|
|
return STATUS_INVALID_VIEW_SIZE;
|
|
}
|
|
|
|
/* Check if the offset and size are bigger than the section itself */
|
|
if ((SectionOffset->QuadPart + *ViewSize) > Section->SizeOfSection.QuadPart)
|
|
{
|
|
DPRINT1("Section offset is larger than section\n");
|
|
return STATUS_INVALID_VIEW_SIZE;
|
|
}
|
|
|
|
/* Check if the caller did not specify a view size */
|
|
if (!(*ViewSize))
|
|
{
|
|
/* Compute it for the caller */
|
|
*ViewSize = Section->SizeOfSection.QuadPart - SectionOffset->QuadPart;
|
|
|
|
/* Check if it's larger than 4GB or overflows into kernel-mode */
|
|
if ((*ViewSize > 0xFFFFFFFF) ||
|
|
(((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS - (ULONG_PTR)*BaseAddress) < *ViewSize))
|
|
{
|
|
DPRINT1("Section view won't fit\n");
|
|
return STATUS_INVALID_VIEW_SIZE;
|
|
}
|
|
}
|
|
|
|
/* Check if the commit size is larger than the view size */
|
|
if (CommitSize > *ViewSize)
|
|
{
|
|
DPRINT1("Attempting to commit more than the view itself\n");
|
|
return STATUS_INVALID_PARAMETER_5;
|
|
}
|
|
|
|
/* Check if the view size is larger than the section */
|
|
if (*ViewSize > Section->SizeOfSection.QuadPart)
|
|
{
|
|
DPRINT1("The view is larger than the section\n");
|
|
return STATUS_INVALID_VIEW_SIZE;
|
|
}
|
|
|
|
/* Compute and validate the protection mask */
|
|
ProtectionMask = MiMakeProtectionMask(Protect);
|
|
if (ProtectionMask == MM_INVALID_PROTECTION)
|
|
{
|
|
DPRINT1("The protection is invalid\n");
|
|
return STATUS_INVALID_PAGE_PROTECTION;
|
|
}
|
|
|
|
/* We only handle pagefile-backed sections, which cannot be writecombined */
|
|
if (Protect & PAGE_WRITECOMBINE)
|
|
{
|
|
DPRINT1("Cannot write combine a pagefile-backed section\n");
|
|
return STATUS_INVALID_PARAMETER_10;
|
|
}
|
|
|
|
/* Start by attaching to the current process if needed */
|
|
if (PsGetCurrentProcess() != Process)
|
|
{
|
|
KeStackAttachProcess(&Process->Pcb, &ApcState);
|
|
Attached = TRUE;
|
|
}
|
|
|
|
/* Lock the address space and make sure the process is alive */
|
|
MmLockAddressSpace(&Process->Vm);
|
|
if (!Process->VmDeleted)
|
|
{
|
|
/* Do the actual mapping */
|
|
Status = MiMapViewOfDataSection(ControlArea,
|
|
Process,
|
|
BaseAddress,
|
|
SectionOffset,
|
|
ViewSize,
|
|
Section,
|
|
InheritDisposition,
|
|
ProtectionMask,
|
|
CommitSize,
|
|
ZeroBits,
|
|
AllocationType);
|
|
}
|
|
else
|
|
{
|
|
/* The process is being terminated, fail */
|
|
DPRINT1("The process is dying\n");
|
|
Status = STATUS_PROCESS_IS_TERMINATING;
|
|
}
|
|
|
|
/* Unlock the address space and detatch if needed, then return status */
|
|
MmUnlockAddressSpace(&Process->Vm);
|
|
if (Attached) KeUnstackDetachProcess(&ApcState);
|
|
return Status;
|
|
}
|
|
|
|
/* SYSTEM CALLS ***************************************************************/
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtAreMappedFilesTheSame(IN PVOID File1MappedAsAnImage,
|
|
IN PVOID File2MappedAsFile)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
NtCreateSection(OUT PHANDLE SectionHandle,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
|
|
IN PLARGE_INTEGER MaximumSize OPTIONAL,
|
|
IN ULONG SectionPageProtection OPTIONAL,
|
|
IN ULONG AllocationAttributes,
|
|
IN HANDLE FileHandle OPTIONAL)
|
|
{
|
|
LARGE_INTEGER SafeMaximumSize;
|
|
PVOID SectionObject;
|
|
HANDLE Handle;
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
NTSTATUS Status;
|
|
PAGED_CODE();
|
|
|
|
/* Check for non-existing flags */
|
|
if ((AllocationAttributes & ~(SEC_COMMIT | SEC_RESERVE | SEC_BASED |
|
|
SEC_LARGE_PAGES | SEC_IMAGE | SEC_NOCACHE |
|
|
SEC_NO_CHANGE)))
|
|
{
|
|
if (!(AllocationAttributes & 1))
|
|
{
|
|
DPRINT1("Bogus allocation attribute: %lx\n", AllocationAttributes);
|
|
return STATUS_INVALID_PARAMETER_6;
|
|
}
|
|
}
|
|
|
|
/* Check for no allocation type */
|
|
if (!(AllocationAttributes & (SEC_COMMIT | SEC_RESERVE | SEC_IMAGE)))
|
|
{
|
|
DPRINT1("Missing allocation type in allocation attributes\n");
|
|
return STATUS_INVALID_PARAMETER_6;
|
|
}
|
|
|
|
/* Check for image allocation with invalid attributes */
|
|
if ((AllocationAttributes & SEC_IMAGE) &&
|
|
(AllocationAttributes & (SEC_COMMIT | SEC_RESERVE | SEC_LARGE_PAGES |
|
|
SEC_NOCACHE | SEC_NO_CHANGE)))
|
|
{
|
|
DPRINT1("Image allocation with invalid attributes\n");
|
|
return STATUS_INVALID_PARAMETER_6;
|
|
}
|
|
|
|
/* Check for allocation type is both commit and reserve */
|
|
if ((AllocationAttributes & SEC_COMMIT) && (AllocationAttributes & SEC_RESERVE))
|
|
{
|
|
DPRINT1("Commit and reserve in the same time\n");
|
|
return STATUS_INVALID_PARAMETER_6;
|
|
}
|
|
|
|
/* Now check for valid protection */
|
|
if ((SectionPageProtection & PAGE_NOCACHE) ||
|
|
(SectionPageProtection & PAGE_WRITECOMBINE) ||
|
|
(SectionPageProtection & PAGE_GUARD) ||
|
|
(SectionPageProtection & PAGE_NOACCESS))
|
|
{
|
|
DPRINT1("Sections don't support these protections\n");
|
|
return STATUS_INVALID_PAGE_PROTECTION;
|
|
}
|
|
|
|
/* Use a maximum size of zero, if none was specified */
|
|
SafeMaximumSize.QuadPart = 0;
|
|
|
|
/* Check for user-mode caller */
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
/* Enter SEH */
|
|
_SEH2_TRY
|
|
{
|
|
/* Safely check user-mode parameters */
|
|
if (MaximumSize) SafeMaximumSize = ProbeForReadLargeInteger(MaximumSize);
|
|
MaximumSize = &SafeMaximumSize;
|
|
ProbeForWriteHandle(SectionHandle);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Return the exception code */
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
else if (!MaximumSize) MaximumSize = &SafeMaximumSize;
|
|
|
|
/* Create the section */
|
|
Status = MmCreateSection(&SectionObject,
|
|
DesiredAccess,
|
|
ObjectAttributes,
|
|
MaximumSize,
|
|
SectionPageProtection,
|
|
AllocationAttributes,
|
|
FileHandle,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/* FIXME: Should zero last page for a file mapping */
|
|
|
|
/* Now insert the object */
|
|
Status = ObInsertObject(SectionObject,
|
|
NULL,
|
|
DesiredAccess,
|
|
0,
|
|
NULL,
|
|
&Handle);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Enter SEH */
|
|
_SEH2_TRY
|
|
{
|
|
/* Return the handle safely */
|
|
*SectionHandle = Handle;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Nothing here */
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
|
|
/* Return the status */
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtOpenSection(OUT PHANDLE SectionHandle,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN POBJECT_ATTRIBUTES ObjectAttributes)
|
|
{
|
|
HANDLE Handle;
|
|
NTSTATUS Status;
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
PAGED_CODE();
|
|
|
|
/* Check for user-mode caller */
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
/* Enter SEH */
|
|
_SEH2_TRY
|
|
{
|
|
/* Safely check user-mode parameters */
|
|
ProbeForWriteHandle(SectionHandle);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Return the exception code */
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
|
|
/* Try opening the object */
|
|
Status = ObOpenObjectByName(ObjectAttributes,
|
|
MmSectionObjectType,
|
|
PreviousMode,
|
|
NULL,
|
|
DesiredAccess,
|
|
NULL,
|
|
&Handle);
|
|
|
|
/* Enter SEH */
|
|
_SEH2_TRY
|
|
{
|
|
/* Return the handle safely */
|
|
*SectionHandle = Handle;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Nothing here */
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Return the status */
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtMapViewOfSection(IN HANDLE SectionHandle,
|
|
IN HANDLE ProcessHandle,
|
|
IN OUT PVOID* BaseAddress,
|
|
IN ULONG_PTR ZeroBits,
|
|
IN SIZE_T CommitSize,
|
|
IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
|
|
IN OUT PSIZE_T ViewSize,
|
|
IN SECTION_INHERIT InheritDisposition,
|
|
IN ULONG AllocationType,
|
|
IN ULONG Protect)
|
|
{
|
|
PVOID SafeBaseAddress;
|
|
LARGE_INTEGER SafeSectionOffset;
|
|
SIZE_T SafeViewSize;
|
|
PROS_SECTION_OBJECT Section;
|
|
PEPROCESS Process;
|
|
NTSTATUS Status;
|
|
ACCESS_MASK DesiredAccess;
|
|
ULONG ProtectionMask;
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
|
|
/* Check for invalid zero bits */
|
|
if (ZeroBits > 21) // per-arch?
|
|
{
|
|
DPRINT1("Invalid zero bits\n");
|
|
return STATUS_INVALID_PARAMETER_4;
|
|
}
|
|
|
|
/* Check for invalid inherit disposition */
|
|
if ((InheritDisposition > ViewUnmap) || (InheritDisposition < ViewShare))
|
|
{
|
|
DPRINT1("Invalid inherit disposition\n");
|
|
return STATUS_INVALID_PARAMETER_8;
|
|
}
|
|
|
|
/* Allow only valid allocation types */
|
|
if ((AllocationType & ~(MEM_TOP_DOWN | MEM_LARGE_PAGES | MEM_DOS_LIM |
|
|
SEC_NO_CHANGE | MEM_RESERVE)))
|
|
{
|
|
DPRINT1("Invalid allocation type\n");
|
|
return STATUS_INVALID_PARAMETER_9;
|
|
}
|
|
|
|
/* Convert the protection mask, and validate it */
|
|
ProtectionMask = MiMakeProtectionMask(Protect);
|
|
if (ProtectionMask == MM_INVALID_PROTECTION)
|
|
{
|
|
DPRINT1("Invalid page protection\n");
|
|
return STATUS_INVALID_PAGE_PROTECTION;
|
|
}
|
|
|
|
/* Now convert the protection mask into desired section access mask */
|
|
DesiredAccess = MmMakeSectionAccess[ProtectionMask & 0x7];
|
|
|
|
/* Assume no section offset */
|
|
SafeSectionOffset.QuadPart = 0;
|
|
|
|
/* Enter SEH */
|
|
_SEH2_TRY
|
|
{
|
|
/* Check for unsafe parameters */
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
/* Probe the parameters */
|
|
ProbeForWritePointer(BaseAddress);
|
|
ProbeForWriteSize_t(ViewSize);
|
|
}
|
|
|
|
/* Check if a section offset was given */
|
|
if (SectionOffset)
|
|
{
|
|
/* Check for unsafe parameters and capture section offset */
|
|
if (PreviousMode != KernelMode) ProbeForWriteLargeInteger(SectionOffset);
|
|
SafeSectionOffset = *SectionOffset;
|
|
}
|
|
|
|
/* Capture the other parameters */
|
|
SafeBaseAddress = *BaseAddress;
|
|
SafeViewSize = *ViewSize;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Return the exception code */
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Check for kernel-mode address */
|
|
if (SafeBaseAddress > MM_HIGHEST_VAD_ADDRESS)
|
|
{
|
|
DPRINT1("Kernel base not allowed\n");
|
|
return STATUS_INVALID_PARAMETER_3;
|
|
}
|
|
|
|
/* Check for range entering kernel-mode */
|
|
if (((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS - (ULONG_PTR)SafeBaseAddress) < SafeViewSize)
|
|
{
|
|
DPRINT1("Overflowing into kernel base not allowed\n");
|
|
return STATUS_INVALID_PARAMETER_3;
|
|
}
|
|
|
|
/* Check for invalid zero bits */
|
|
if (((ULONG_PTR)SafeBaseAddress + SafeViewSize) > (0xFFFFFFFF >> ZeroBits)) // arch?
|
|
{
|
|
DPRINT1("Invalid zero bits\n");
|
|
return STATUS_INVALID_PARAMETER_4;
|
|
}
|
|
|
|
/* Reference the process */
|
|
Status = ObReferenceObjectByHandle(ProcessHandle,
|
|
PROCESS_VM_OPERATION,
|
|
PsProcessType,
|
|
PreviousMode,
|
|
(PVOID*)&Process,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/* Reference the section */
|
|
Status = ObReferenceObjectByHandle(SectionHandle,
|
|
DesiredAccess,
|
|
MmSectionObjectType,
|
|
PreviousMode,
|
|
(PVOID*)&Section,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ObDereferenceObject(Process);
|
|
return Status;
|
|
}
|
|
|
|
/* Now do the actual mapping */
|
|
Status = MmMapViewOfSection(Section,
|
|
Process,
|
|
&SafeBaseAddress,
|
|
ZeroBits,
|
|
CommitSize,
|
|
&SafeSectionOffset,
|
|
&SafeViewSize,
|
|
InheritDisposition,
|
|
AllocationType,
|
|
Protect);
|
|
|
|
/* Check if this is an image for the current process */
|
|
if ((Section->AllocationAttributes & SEC_IMAGE) &&
|
|
(Process == PsGetCurrentProcess()) &&
|
|
((Status != STATUS_IMAGE_NOT_AT_BASE) ||
|
|
(Status != STATUS_CONFLICTING_ADDRESSES)))
|
|
{
|
|
/* Notify the debugger */
|
|
DbgkMapViewOfSection(Section,
|
|
SafeBaseAddress,
|
|
SafeSectionOffset.LowPart,
|
|
SafeViewSize);
|
|
}
|
|
|
|
/* Return data only on success */
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Enter SEH */
|
|
_SEH2_TRY
|
|
{
|
|
/* Return parameters to user */
|
|
*BaseAddress = SafeBaseAddress;
|
|
*ViewSize = SafeViewSize;
|
|
if (SectionOffset) *SectionOffset = SafeSectionOffset;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Nothing to do */
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
|
|
/* Dereference all objects and return status */
|
|
ObDereferenceObject(Section);
|
|
ObDereferenceObject(Process);
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtUnmapViewOfSection(IN HANDLE ProcessHandle,
|
|
IN PVOID BaseAddress)
|
|
{
|
|
PEPROCESS Process;
|
|
NTSTATUS Status;
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
|
|
/* Don't allowing mapping kernel views */
|
|
if ((PreviousMode == UserMode) && (BaseAddress > MM_HIGHEST_USER_ADDRESS))
|
|
{
|
|
DPRINT1("Trying to unmap a kernel view\n");
|
|
return STATUS_NOT_MAPPED_VIEW;
|
|
}
|
|
|
|
/* Reference the process */
|
|
Status = ObReferenceObjectByHandle(ProcessHandle,
|
|
PROCESS_VM_OPERATION,
|
|
PsProcessType,
|
|
PreviousMode,
|
|
(PVOID*)&Process,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/* Unmap the view */
|
|
Status = MmUnmapViewOfSection(Process, BaseAddress);
|
|
|
|
/* Dereference the process and return status */
|
|
ObDereferenceObject(Process);
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtExtendSection(IN HANDLE SectionHandle,
|
|
IN OUT PLARGE_INTEGER NewMaximumSize)
|
|
{
|
|
LARGE_INTEGER SafeNewMaximumSize;
|
|
PROS_SECTION_OBJECT Section;
|
|
NTSTATUS Status;
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
|
|
/* Check for user-mode parameters */
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
/* Enter SEH */
|
|
_SEH2_TRY
|
|
{
|
|
/* Probe and capture the maximum size, it's both read and write */
|
|
ProbeForWriteLargeInteger(NewMaximumSize);
|
|
SafeNewMaximumSize = *NewMaximumSize;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Return the exception code */
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
else
|
|
{
|
|
/* Just read the size directly */
|
|
SafeNewMaximumSize = *NewMaximumSize;
|
|
}
|
|
|
|
/* Reference the section */
|
|
Status = ObReferenceObjectByHandle(SectionHandle,
|
|
SECTION_EXTEND_SIZE,
|
|
MmSectionObjectType,
|
|
PreviousMode,
|
|
(PVOID*)&Section,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/* Really this should go in MmExtendSection */
|
|
if (!(Section->AllocationAttributes & SEC_FILE))
|
|
{
|
|
DPRINT1("Not extending a file\n");
|
|
ObDereferenceObject(Section);
|
|
return STATUS_SECTION_NOT_EXTENDED;
|
|
}
|
|
|
|
/* FIXME: Do the work */
|
|
|
|
/* Dereference the section */
|
|
ObDereferenceObject(Section);
|
|
|
|
/* Enter SEH */
|
|
_SEH2_TRY
|
|
{
|
|
/* Write back the new size */
|
|
*NewMaximumSize = SafeNewMaximumSize;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Nothing to do */
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Return the status */
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
/* PUBLIC FUNCTIONS ***********************************************************/
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
BOOLEAN
|
|
NTAPI
|
|
MmDisableModifiedWriteOfSection(IN PSECTION_OBJECT_POINTERS SectionObjectPointer)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
BOOLEAN
|
|
NTAPI
|
|
MmForceSectionClosed(IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
|
|
IN BOOLEAN DelayClose)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
MmMapViewInSessionSpace(IN PVOID Section,
|
|
OUT PVOID *MappedBase,
|
|
IN OUT PSIZE_T ViewSize)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
MmUnmapViewInSessionSpace(IN PVOID MappedBase)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
/* EOF */
|