reactos/ntoskrnl/mm/ARM3/section.c
Sylvain Petreolle 1fb94b1cb5 [CMAKE]
sync with trunk (r49230)

svn path=/branches/cmake-bringup/; revision=49246
2010-10-23 22:14:59 +00:00

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 */