/* * PROJECT: ReactOS Kernel * LICENSE: BSD - See COPYING.ARM in the top level directory * FILE: ntoskrnl/mm/ARM3/section.c * PURPOSE: ARM Memory Manager Section Support * PROGRAMMERS: ReactOS Portable Systems Group */ /* INCLUDES *******************************************************************/ #include #define NDEBUG #include #define MODULE_INVOLVED_IN_ARM3 #include /* 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 }; ACCESS_MASK MmMakeFileAccess[8] = { FILE_READ_DATA, FILE_READ_DATA, FILE_EXECUTE, FILE_EXECUTE | FILE_READ_DATA, FILE_WRITE_DATA | FILE_READ_DATA, FILE_READ_DATA, FILE_EXECUTE | FILE_WRITE_DATA | FILE_READ_DATA, FILE_EXECUTE | FILE_READ_DATA }; 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 }; ULONG MmCompatibleProtectionMask[8] = { PAGE_NOACCESS, PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY, PAGE_NOACCESS | PAGE_EXECUTE, PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY | PAGE_EXECUTE | PAGE_EXECUTE_READ, PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY | PAGE_READWRITE, PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY, PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY | PAGE_READWRITE | PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY, PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY | PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_WRITECOPY }; MMSESSION MmSession; KGUARDED_MUTEX MmSectionCommitMutex; MM_AVL_TABLE MmSectionBasedRoot; KGUARDED_MUTEX MmSectionBasedMutex; PVOID MmHighSectionBase; /* PRIVATE FUNCTIONS **********************************************************/ BOOLEAN NTAPI MiIsProtectionCompatible(IN ULONG SectionPageProtection, IN ULONG NewSectionPageProtection) { ULONG ProtectionMask, CompatibleMask; /* Calculate the protection mask and make sure it's valid */ ProtectionMask = MiMakeProtectionMask(SectionPageProtection); if (ProtectionMask == MM_INVALID_PROTECTION) { DPRINT1("Invalid protection mask\n"); return FALSE; } /* Calculate the compatible mask */ CompatibleMask = MmCompatibleProtectionMask[ProtectionMask & 0x7] | PAGE_GUARD | PAGE_NOCACHE | PAGE_WRITECOMBINE; /* See if the mapping protection is compatible with the create protection */ return ((CompatibleMask | NewSectionPageProtection) == CompatibleMask); } 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_GUARDPAGE; } /* 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 PMMSESSION InputSession OPTIONAL) { SIZE_T AllocSize, BitmapSize, Size; PVOID ViewStart; PMMSESSION Session; /* Check if this a session or system space */ if (InputSession) { /* Use the input session */ Session = InputSession; ViewStart = MiSessionViewStart; Size = MmSessionViewSize; } else { /* Use the system space "session" */ Session = &MmSession; ViewStart = MiSystemViewStart; Size = MmSystemViewSize; } /* Initialize the system space lock */ Session->SystemSpaceViewLockPointer = &Session->SystemSpaceViewLock; KeInitializeGuardedMutex(Session->SystemSpaceViewLockPointer); /* Set the start address */ Session->SystemSpaceViewStart = ViewStart; /* Create a bitmap to describe system space */ BitmapSize = sizeof(RTL_BITMAP) + ((((Size / MI_SYSTEM_VIEW_BUCKET_SIZE) + 31) / 32) * sizeof(ULONG)); Session->SystemSpaceBitMap = ExAllocatePoolWithTag(NonPagedPool, BitmapSize, TAG_MM); ASSERT(Session->SystemSpaceBitMap); RtlInitializeBitMap(Session->SystemSpaceBitMap, (PULONG)(Session->SystemSpaceBitMap + 1), (ULONG)(Size / 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(Session == &MmSession ? NonPagedPool : PagedPool, AllocSize, TAG_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, HashSize; PMMVIEW OldTable; PAGED_CODE(); /* Stay within 4GB */ ASSERT(Buckets < MI_SYSTEM_VIEW_BUCKET_SIZE); /* Lock system space */ KeAcquireGuardedMutex(Session->SystemSpaceViewLockPointer); /* Check if we're going to exhaust hash entries */ if ((Session->SystemSpaceHashEntries + 8) > Session->SystemSpaceHashSize) { /* Double the hash size */ HashSize = Session->SystemSpaceHashSize * 2; /* Save the old table and allocate a new one */ OldTable = Session->SystemSpaceViewTable; Session->SystemSpaceViewTable = ExAllocatePoolWithTag(Session == &MmSession ? NonPagedPool : PagedPool, HashSize * sizeof(MMVIEW), TAG_MM); if (!Session->SystemSpaceViewTable) { /* Failed to allocate a new table, keep the old one for now */ Session->SystemSpaceViewTable = OldTable; } else { /* Clear the new table and set the new ahsh and key */ RtlZeroMemory(Session->SystemSpaceViewTable, HashSize * sizeof(MMVIEW)); Session->SystemSpaceHashSize = HashSize; Session->SystemSpaceHashKey = Session->SystemSpaceHashSize - 1; /* Loop the old table */ for (i = 0; i < Session->SystemSpaceHashSize / 2; i++) { /* Check if the entry was valid */ if (OldTable[i].Entry) { /* Re-hash the old entry and search for space in the new table */ Hash = (OldTable[i].Entry >> 16) % Session->SystemSpaceHashKey; while (Session->SystemSpaceViewTable[Hash].Entry) { /* Loop back at the beginning if we had an overflow */ if (++Hash >= Session->SystemSpaceHashSize) Hash = 0; } /* Write the old entry in the new table */ Session->SystemSpaceViewTable[Hash] = OldTable[i]; } } /* Free the old table */ ExFreePool(OldTable); } } /* Check if we ran out */ if (Session->SystemSpaceHashEntries == Session->SystemSpaceHashSize) { DPRINT1("Ran out of system view hash entries\n"); KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer); return NULL; } /* Find space where to map this view */ i = RtlFindClearBitsAndSet(Session->SystemSpaceBitMap, Buckets, 0); if (i == 0xFFFFFFFF) { /* Out of space, fail */ Session->BitmapFailures++; DPRINT1("Out of system view space\n"); KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer); return NULL; } /* Compute the base address */ 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++; KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer); return Base; } static NTSTATUS MiAddMappedPtes(IN PMMPTE FirstPte, IN PFN_NUMBER PteCount, IN PCONTROL_AREA ControlArea, IN LONGLONG SectionOffset) { MMPTE TempPte; PMMPTE PointerPte, ProtoPte, LastProtoPte, LastPte; PSUBSECTION Subsection; /* Mapping at offset not supported yet */ ASSERT(SectionOffset == 0); /* 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 */ ASSERT(FALSE); } /* 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; } VOID NTAPI MiFillSystemPageDirectory(IN PVOID Base, IN SIZE_T NumberOfBytes) { PMMPDE PointerPde, LastPde, SystemMapPde; MMPDE TempPde; PFN_NUMBER PageFrameIndex, ParentPage; KIRQL OldIrql; PAGED_CODE(); /* Find the PDEs needed for this mapping */ PointerPde = MiAddressToPde(Base); LastPde = MiAddressToPde((PVOID)((ULONG_PTR)Base + NumberOfBytes - 1)); #if (_MI_PAGING_LEVELS == 2) /* Find the system double-mapped PDE that describes this mapping */ SystemMapPde = &MmSystemPagePtes[((ULONG_PTR)PointerPde & (SYSTEM_PD_SIZE - 1)) / sizeof(MMPTE)]; #else /* We don't have a double mapping */ SystemMapPde = PointerPde; #endif /* Use the PDE template and loop the PDEs */ TempPde = ValidKernelPde; while (PointerPde <= LastPde) { /* Lock the PFN database */ OldIrql = MiAcquirePfnLock(); /* Check if we don't already have this PDE mapped */ if (SystemMapPde->u.Hard.Valid == 0) { /* Grab a page for it */ MI_SET_USAGE(MI_USAGE_PAGE_TABLE); MI_SET_PROCESS2(PsGetCurrentProcess()->ImageFileName); PageFrameIndex = MiRemoveZeroPage(MI_GET_NEXT_COLOR()); ASSERT(PageFrameIndex); TempPde.u.Hard.PageFrameNumber = PageFrameIndex; #if (_MI_PAGING_LEVELS == 2) ParentPage = MmSystemPageDirectory[(PointerPde - MiAddressToPde(NULL)) / PDE_PER_PAGE]; #else ParentPage = MiPdeToPpe(PointerPde)->u.Hard.PageFrameNumber; #endif /* Initialize its PFN entry, with the parent system page directory page table */ MiInitializePfnForOtherProcess(PageFrameIndex, (PMMPTE)PointerPde, ParentPage); /* Make the system PDE entry valid */ MI_WRITE_VALID_PDE(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_PDE(PointerPde, TempPde); } } /* Release the lock and keep going with the next PDE */ MiReleasePfnLock(OldIrql); SystemMapPde++; PointerPde++; } } NTSTATUS NTAPI MiCheckPurgeAndUpMapCount(IN PCONTROL_AREA ControlArea, IN BOOLEAN FailIfSystemViews) { KIRQL OldIrql; /* Flag not yet supported */ ASSERT(FailIfSystemViews == FALSE); /* Lock the PFN database */ OldIrql = MiAcquirePfnLock(); /* 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 */ MiReleasePfnLock(OldIrql); return STATUS_SUCCESS; } PSUBSECTION NTAPI MiLocateSubsection(IN PMMVAD Vad, IN ULONG_PTR Vpn) { PSUBSECTION Subsection; PCONTROL_AREA ControlArea; ULONG_PTR 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 = 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; PMMPFN Pfn1; PFN_NUMBER PageFrameIndex; 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 = MiAcquirePfnLock(); /* 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 (MiIsPteOnPdeBoundary(PointerPte) && (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); /* See if we should clean things up */ if (!(ControlArea->u.Flags.Image) && !(ControlArea->u.Flags.File)) { /* * This is a section backed by the pagefile. Now that it doesn't exist anymore, * we can give everything back to the system. */ ASSERT(TempPte.u.Soft.Prototype == 0); if (TempPte.u.Soft.Transition == 1) { /* We can give the page back for other use */ DPRINT("Releasing page for transition PTE %p\n", PointerPte); PageFrameIndex = PFN_FROM_PTE(&TempPte); Pfn1 = MI_PFN_ELEMENT(PageFrameIndex); /* As this is a paged-backed section, nobody should reference it anymore (no cache or whatever) */ ASSERT(Pfn1->u3.ReferenceCount == 0); /* And it should be in standby or modified list */ ASSERT((Pfn1->u3.e1.PageLocation == ModifiedPageList) || (Pfn1->u3.e1.PageLocation == StandbyPageList)); /* Unlink it and put it back in free list */ MiUnlinkPageFromList(Pfn1); /* Temporarily mark this as active and make it free again */ Pfn1->u3.e1.PageLocation = ActiveAndValid; MI_SET_PFN_DELETED(Pfn1); MiInsertPageInFreeList(PageFrameIndex); } else if (TempPte.u.Soft.PageFileHigh != 0) { /* Should not happen for now */ ASSERT(FALSE); } } else { /* unsupported for now */ ASSERT(FALSE); /* File-backed section must have prototype PTEs */ ASSERT(TempPte.u.Soft.Prototype == 1); } /* Zero the PTE and keep going */ PointerPte->u.Long = 0; PointerPte++; } /* Release the PFN lock */ MiReleasePfnLock(OldIrql); /* Free the structures */ ExFreePool(ControlArea); ExFreePool(Segment); } VOID NTAPI MiCheckControlArea(IN PCONTROL_AREA ControlArea, IN KIRQL OldIrql) { BOOLEAN DeleteSegment = FALSE; MI_ASSERT_PFN_LOCK_HELD(); /* 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 */ MiReleasePfnLock(OldIrql); /* Delete the segment if needed */ if (DeleteSegment) { /* No more user write references at all */ ASSERT(ControlArea->WritableUserReferences == 0); MiSegmentDelete(ControlArea->Segment); } } VOID NTAPI MiDereferenceControlArea(IN PCONTROL_AREA ControlArea) { KIRQL OldIrql; /* Lock the PFN database */ OldIrql = MiAcquirePfnLock(); /* Drop reference counts */ ControlArea->NumberOfMappedViews--; ControlArea->NumberOfUserReferences--; /* Check if it's time to delete the CA. This releases the lock */ MiCheckControlArea(ControlArea, OldIrql); } VOID NTAPI MiRemoveMappedView(IN PEPROCESS CurrentProcess, IN PMMVAD Vad) { KIRQL OldIrql; PCONTROL_AREA ControlArea; PETHREAD CurrentThread = PsGetCurrentThread(); /* 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 */ MiUnlockProcessWorkingSetUnsafe(CurrentProcess, CurrentThread); /* Lock the PFN database */ OldIrql = MiAcquirePfnLock(); /* Remove references */ ControlArea->NumberOfMappedViews--; ControlArea->NumberOfUserReferences--; /* Check if it should be destroyed */ MiCheckControlArea(ControlArea, OldIrql); } NTSTATUS NTAPI MiUnmapViewOfSection(IN PEPROCESS Process, IN PVOID BaseAddress, IN ULONG Flags) { PMEMORY_AREA MemoryArea; BOOLEAN Attached = FALSE; KAPC_STATE ApcState; PMMVAD Vad; PVOID DbgBase = NULL; SIZE_T RegionSize; NTSTATUS Status; PETHREAD CurrentThread = PsGetCurrentThread(); PEPROCESS CurrentProcess = PsGetCurrentProcess(); PAGED_CODE(); /* Check if we need to lock the address space */ if (!Flags) MmLockAddressSpace(&Process->Vm); /* Check for Mm Region */ MemoryArea = MmLocateMemoryAreaByAddress(&Process->Vm, BaseAddress); if ((MemoryArea) && (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3)) { /* Call Mm API */ NTSTATUS Status = MiRosUnmapViewOfSection(Process, BaseAddress, Process->ProcessExiting); if (!Flags) MmUnlockAddressSpace(&Process->Vm); return Status; } /* Check if we should attach to the process */ if (CurrentProcess != Process) { /* The process is different, do an attach */ KeStackAttachProcess(&Process->Pcb, &ApcState); Attached = TRUE; } /* Check if the process is already dead */ if (Process->VmDeleted) { /* Fail the call */ DPRINT1("Process died!\n"); if (!Flags) MmUnlockAddressSpace(&Process->Vm); Status = STATUS_PROCESS_IS_TERMINATING; goto Quickie; } /* Find the VAD for the address and make sure it's a section VAD */ Vad = MiLocateAddress(BaseAddress); if (!(Vad) || (Vad->u.VadFlags.PrivateMemory)) { /* Couldn't find it, or invalid VAD, fail */ DPRINT1("No VAD or invalid VAD\n"); if (!Flags) MmUnlockAddressSpace(&Process->Vm); Status = STATUS_NOT_MAPPED_VIEW; goto Quickie; } /* We should be attached */ ASSERT(Process == PsGetCurrentProcess()); /* We need the base address for the debugger message on image-backed VADs */ if (Vad->u.VadFlags.VadType == VadImageMap) { DbgBase = (PVOID)(Vad->StartingVpn >> PAGE_SHIFT); } /* Compute the size of the VAD region */ RegionSize = PAGE_SIZE + ((Vad->EndingVpn - Vad->StartingVpn) << PAGE_SHIFT); /* For SEC_NO_CHANGE sections, we need some extra checks */ if (Vad->u.VadFlags.NoChange == 1) { /* Are we allowed to mess with this VAD? */ Status = MiCheckSecuredVad(Vad, (PVOID)(Vad->StartingVpn >> PAGE_SHIFT), RegionSize, MM_DELETE_CHECK); if (!NT_SUCCESS(Status)) { /* We failed */ DPRINT1("Trying to unmap protected VAD!\n"); if (!Flags) MmUnlockAddressSpace(&Process->Vm); goto Quickie; } } /* Not currently supported */ ASSERT(Vad->u.VadFlags.VadType != VadRotatePhysical); /* FIXME: Remove VAD charges */ /* Lock the working set */ MiLockProcessWorkingSetUnsafe(Process, CurrentThread); /* Remove the VAD */ ASSERT(Process->VadRoot.NumberGenericTableElements >= 1); MiRemoveNode((PMMADDRESS_NODE)Vad, &Process->VadRoot); PsReturnProcessNonPagedPoolQuota(Process, sizeof(MMVAD_LONG)); /* Remove the PTEs for this view, which also releases the working set lock */ MiRemoveMappedView(Process, Vad); /* FIXME: Remove commitment */ /* Update performance counter and release the lock */ Process->VirtualSize -= RegionSize; if (!Flags) MmUnlockAddressSpace(&Process->Vm); /* Destroy the VAD and return success */ ExFreePool(Vad); Status = STATUS_SUCCESS; /* Failure and success case -- send debugger message, detach, and return */ Quickie: if (DbgBase) DbgkUnMapViewOfSection(DbgBase); if (Attached) KeUnstackDetachProcess(&ApcState); return Status; } NTSTATUS NTAPI MiSessionCommitPageTables(IN PVOID StartVa, IN PVOID EndVa) { KIRQL OldIrql; ULONG Color, Index; PMMPDE StartPde, EndPde; MMPDE TempPde = ValidKernelPdeLocal; PMMPFN Pfn1; PFN_NUMBER PageCount = 0, ActualPages = 0, PageFrameNumber; /* Windows sanity checks */ ASSERT(StartVa >= (PVOID)MmSessionBase); ASSERT(EndVa < (PVOID)MiSessionSpaceEnd); ASSERT(PAGE_ALIGN(EndVa) == EndVa); /* Get the start and end PDE, then loop each one */ StartPde = MiAddressToPde(StartVa); EndPde = MiAddressToPde((PVOID)((ULONG_PTR)EndVa - 1)); Index = ((ULONG_PTR)StartVa - (ULONG_PTR)MmSessionBase) >> 22; while (StartPde <= EndPde) { #ifndef _M_AMD64 /* If we don't already have a page table for it, increment count */ if (MmSessionSpace->PageTables[Index].u.Long == 0) PageCount++; #endif /* Move to the next one */ StartPde++; Index++; } /* If there's no page tables to create, bail out */ if (PageCount == 0) return STATUS_SUCCESS; /* Reset the start PDE and index */ StartPde = MiAddressToPde(StartVa); Index = ((ULONG_PTR)StartVa - (ULONG_PTR)MmSessionBase) >> 22; /* Loop each PDE while holding the working set lock */ // MiLockWorkingSet(PsGetCurrentThread(), // &MmSessionSpace->GlobalVirtualAddress->Vm); #ifdef _M_AMD64 _WARN("MiSessionCommitPageTables halfplemented for amd64") DBG_UNREFERENCED_LOCAL_VARIABLE(OldIrql); DBG_UNREFERENCED_LOCAL_VARIABLE(Color); DBG_UNREFERENCED_LOCAL_VARIABLE(TempPde); DBG_UNREFERENCED_LOCAL_VARIABLE(Pfn1); DBG_UNREFERENCED_LOCAL_VARIABLE(PageFrameNumber); ASSERT(FALSE); #else while (StartPde <= EndPde) { /* Check if we already have a page table */ if (MmSessionSpace->PageTables[Index].u.Long == 0) { /* We don't, so the PDE shouldn't be ready yet */ ASSERT(StartPde->u.Hard.Valid == 0); /* ReactOS check to avoid MiEnsureAvailablePageOrWait */ ASSERT(MmAvailablePages >= 32); /* Acquire the PFN lock and grab a zero page */ OldIrql = MiAcquirePfnLock(); MI_SET_USAGE(MI_USAGE_PAGE_TABLE); MI_SET_PROCESS2(PsGetCurrentProcess()->ImageFileName); Color = (++MmSessionSpace->Color) & MmSecondaryColorMask; PageFrameNumber = MiRemoveZeroPage(Color); TempPde.u.Hard.PageFrameNumber = PageFrameNumber; MI_WRITE_VALID_PDE(StartPde, TempPde); /* Write the page table in session space structure */ ASSERT(MmSessionSpace->PageTables[Index].u.Long == 0); MmSessionSpace->PageTables[Index] = TempPde; /* Initialize the PFN */ MiInitializePfnForOtherProcess(PageFrameNumber, StartPde, MmSessionSpace->SessionPageDirectoryIndex); /* And now release the lock */ MiReleasePfnLock(OldIrql); /* Get the PFN entry and make sure there's no event for it */ Pfn1 = MI_PFN_ELEMENT(PageFrameNumber); ASSERT(Pfn1->u1.Event == NULL); /* Increment the number of pages */ ActualPages++; } /* Move to the next PDE */ StartPde++; Index++; } #endif /* Make sure we didn't do more pages than expected */ ASSERT(ActualPages <= PageCount); /* Release the working set lock */ // MiUnlockWorkingSet(PsGetCurrentThread(), // &MmSessionSpace->GlobalVirtualAddress->Vm); /* If we did at least one page... */ if (ActualPages) { /* Update the performance counters! */ InterlockedExchangeAddSizeT(&MmSessionSpace->NonPageablePages, ActualPages); InterlockedExchangeAddSizeT(&MmSessionSpace->CommittedPages, ActualPages); } /* Return status */ return STATUS_SUCCESS; } NTSTATUS MiMapViewInSystemSpace( _In_ PVOID Section, _In_ PMMSESSION Session, _Outptr_result_bytebuffer_ (*ViewSize) PVOID *MappedBase, _Inout_ PSIZE_T ViewSize, _Inout_ PLARGE_INTEGER SectionOffset) { PVOID Base; PCONTROL_AREA ControlArea; ULONG Buckets; LONGLONG SectionSize; NTSTATUS Status; PAGED_CODE(); /* 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.QuadPart; /* If the caller didn't specify a view size, assume until the end of the section */ if (!(*ViewSize)) { /* Check for overflow first */ if ((SectionSize - SectionOffset->QuadPart) > SIZE_T_MAX) { DPRINT1("Section end is too far away from the specified offset.\n"); MiDereferenceControlArea(ControlArea); return STATUS_INVALID_VIEW_SIZE; } *ViewSize = SectionSize - SectionOffset->QuadPart; } /* Check overflow */ if ((SectionOffset->QuadPart + *ViewSize) < SectionOffset->QuadPart) { DPRINT1("Integer overflow between size & offset!\n"); MiDereferenceControlArea(ControlArea); return STATUS_INVALID_VIEW_SIZE; } /* Check if the caller wanted a larger section than the view */ if (SectionOffset->QuadPart + *ViewSize > SectionSize) { /* Fail */ DPRINT1("View is too large\n"); MiDereferenceControlArea(ControlArea); return STATUS_INVALID_VIEW_SIZE; } /* Get the number of 64K buckets required for this mapping */ Buckets = (ULONG)(*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) { /* Fail */ DPRINT1("View is too large\n"); MiDereferenceControlArea(ControlArea); return STATUS_INVALID_VIEW_SIZE; } /* Insert this view into system space and get a base address for it */ Base = MiInsertInSystemSpace(Session, Buckets, ControlArea); if (!Base) { /* Fail */ DPRINT1("Out of system space\n"); MiDereferenceControlArea(ControlArea); return STATUS_NO_MEMORY; } /* What's the underlying session? */ if (Session == &MmSession) { /* Create the PDEs needed for this mapping, and double-map them if needed */ MiFillSystemPageDirectory(Base, Buckets * MI_SYSTEM_VIEW_BUCKET_SIZE); Status = STATUS_SUCCESS; } else { /* Create the PDEs needed for this mapping */ Status = MiSessionCommitPageTables(Base, (PVOID)((ULONG_PTR)Base + Buckets * MI_SYSTEM_VIEW_BUCKET_SIZE)); ASSERT(NT_SUCCESS(Status)); } /* Create the actual prototype PTEs for this mapping */ Status = MiAddMappedPtes(MiAddressToPte(Base), BYTES_TO_PAGES(*ViewSize), ControlArea, SectionOffset->QuadPart); ASSERT(NT_SUCCESS(Status)); /* Return the base adress of the mapping and success */ *MappedBase = Base; return STATUS_SUCCESS; } VOID NTAPI MiSetControlAreaSymbolsLoaded(IN PCONTROL_AREA ControlArea) { KIRQL OldIrql; ASSERT(KeGetCurrentIrql() <= APC_LEVEL); OldIrql = MiAcquirePfnLock(); ControlArea->u.Flags.DebugSymbolsLoaded |= 1; ASSERT(OldIrql <= APC_LEVEL); MiReleasePfnLock(OldIrql); ASSERT(KeGetCurrentIrql() <= APC_LEVEL); } VOID NTAPI MiLoadUserSymbols(IN PCONTROL_AREA ControlArea, IN PVOID BaseAddress, IN PEPROCESS Process) { NTSTATUS Status; ANSI_STRING FileNameA; PLIST_ENTRY NextEntry; PUNICODE_STRING FileName; PIMAGE_NT_HEADERS NtHeaders; PLDR_DATA_TABLE_ENTRY LdrEntry; FileName = &ControlArea->FilePointer->FileName; if (FileName->Length == 0) { return; } /* Acquire module list lock */ KeEnterCriticalRegion(); ExAcquireResourceExclusiveLite(&PsLoadedModuleResource, TRUE); /* Browse list to try to find current module */ for (NextEntry = MmLoadedUserImageList.Flink; NextEntry != &MmLoadedUserImageList; NextEntry = NextEntry->Flink) { /* Get the entry */ LdrEntry = CONTAINING_RECORD(NextEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); /* If already in the list, increase load count */ if (LdrEntry->DllBase == BaseAddress) { ++LdrEntry->LoadCount; break; } } /* Not in the list, we'll add it */ if (NextEntry == &MmLoadedUserImageList) { /* Allocate our element, taking to the name string and its null char */ LdrEntry = ExAllocatePoolWithTag(NonPagedPool, FileName->Length + sizeof(UNICODE_NULL) + sizeof(*LdrEntry), 'bDmM'); if (LdrEntry) { memset(LdrEntry, 0, FileName->Length + sizeof(UNICODE_NULL) + sizeof(*LdrEntry)); _SEH2_TRY { /* Get image checksum and size */ NtHeaders = RtlImageNtHeader(BaseAddress); if (NtHeaders) { LdrEntry->SizeOfImage = NtHeaders->OptionalHeader.SizeOfImage; LdrEntry->CheckSum = NtHeaders->OptionalHeader.CheckSum; } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { ExFreePoolWithTag(LdrEntry, 'bDmM'); ExReleaseResourceLite(&PsLoadedModuleResource); KeLeaveCriticalRegion(); _SEH2_YIELD(return); } _SEH2_END; /* Fill all the details */ LdrEntry->DllBase = BaseAddress; LdrEntry->FullDllName.Buffer = (PVOID)((ULONG_PTR)LdrEntry + sizeof(*LdrEntry)); LdrEntry->FullDllName.Length = FileName->Length; LdrEntry->FullDllName.MaximumLength = FileName->Length + sizeof(UNICODE_NULL); memcpy(LdrEntry->FullDllName.Buffer, FileName->Buffer, FileName->Length); LdrEntry->FullDllName.Buffer[LdrEntry->FullDllName.Length / sizeof(WCHAR)] = UNICODE_NULL; LdrEntry->LoadCount = 1; /* Insert! */ InsertHeadList(&MmLoadedUserImageList, &LdrEntry->InLoadOrderLinks); } } /* Release locks */ ExReleaseResourceLite(&PsLoadedModuleResource); KeLeaveCriticalRegion(); /* Load symbols */ Status = RtlUnicodeStringToAnsiString(&FileNameA, FileName, TRUE); if (NT_SUCCESS(Status)) { DbgLoadImageSymbols(&FileNameA, BaseAddress, (ULONG_PTR)Process->UniqueProcessId); RtlFreeAnsiString(&FileNameA); } } 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 SIZE_T CommitSize, IN ULONG_PTR ZeroBits, IN ULONG AllocationType) { PMMVAD_LONG Vad; ULONG_PTR StartAddress; ULONG_PTR ViewSizeInPages; PSUBSECTION Subsection; PSEGMENT Segment; PFN_NUMBER PteOffset; NTSTATUS Status; ULONG QuotaCharge = 0, QuotaExcess = 0; PMMPTE PointerPte, LastPte; MMPTE TempPte; ULONG Granularity = MM_VIRTMEM_GRANULARITY; DPRINT("Mapping ARM3 data section\n"); /* Get the segment for this section */ Segment = ControlArea->Segment; #ifdef _M_IX86 /* ALlow being less restrictive on x86. */ if (AllocationType & MEM_DOS_LIM) Granularity = PAGE_SIZE; #endif /* One can only reserve a file-based mapping, not shared memory! */ if ((AllocationType & MEM_RESERVE) && !(ControlArea->FilePointer)) { return STATUS_INVALID_PARAMETER_9; } /* First, increase the map count. No purging is supported yet */ Status = MiCheckPurgeAndUpMapCount(ControlArea, FALSE); if (!NT_SUCCESS(Status)) return Status; /* Check if the caller specified the view size */ if (!(*ViewSize)) { LONGLONG ViewSizeLL; /* The caller did not, so pick a 64K aligned view size based on the offset */ SectionOffset->LowPart &= ~(_64K - 1); /* Calculate size and make sure this fits */ if (!NT_SUCCESS(RtlLongLongSub(Section->SizeOfSection.QuadPart, SectionOffset->QuadPart, &ViewSizeLL)) || !NT_SUCCESS(RtlLongLongToSIZET(ViewSizeLL, ViewSize)) || (*ViewSize > MAXLONG_PTR)) { MiDereferenceControlArea(ControlArea); return STATUS_INVALID_VIEW_SIZE; } } else { /* A size was specified, align it to a 64K boundary * and check for overflow or huge value. */ if (!NT_SUCCESS(RtlSIZETAdd(*ViewSize, SectionOffset->LowPart & (_64K - 1), ViewSize)) || (*ViewSize > MAXLONG_PTR)) { MiDereferenceControlArea(ControlArea); return STATUS_INVALID_VIEW_SIZE; } /* 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. This is a Windows ASSERT */ ASSERT((SectionOffset->LowPart & ((ULONG)_64K - 1)) == 0); /* Windows ASSERTs for this flag */ ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0); /* Get the subsection. We don't support LARGE_CONTROL_AREA in ARM3 */ ASSERT(ControlArea->u.Flags.Rom == 0); Subsection = (PSUBSECTION)(ControlArea + 1); /* Sections with extended segments are not supported in ARM3 */ ASSERT(Segment->SegmentFlags.TotalNumberOfPtes4132 == 0); /* Within this section, figure out which PTEs will describe the view */ PteOffset = (PFN_NUMBER)(SectionOffset->QuadPart >> PAGE_SHIFT); /* The offset must be in this segment's PTE chunk and it must be valid. Windows ASSERTs */ 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); /* In ARM3, only page-file backed sections (shared memory) are supported now */ ASSERT(ControlArea->FilePointer == NULL); /* Windows ASSERTs for this too -- there must be a subsection base address */ ASSERT(Subsection->SubsectionBase != NULL); /* Compute how much commit space the segment will take */ if ((CommitSize) && (Segment->NumberOfCommittedPages < Segment->TotalNumberOfPtes)) { /* Charge for the maximum pages */ QuotaCharge = BYTES_TO_PAGES(CommitSize); } /* ARM3 does not currently support large pages */ ASSERT(Segment->SegmentFlags.LargePages == 0); /* Calculate how many pages the region spans */ ViewSizeInPages = BYTES_TO_PAGES(*ViewSize); /* A VAD can now be allocated. Do so and zero it out */ /* FIXME: we are allocating a LONG VAD for ReactOS compatibility only */ ASSERT((AllocationType & MEM_RESERVE) == 0); /* ARM3 does not support this */ Vad = ExAllocatePoolWithTag(NonPagedPool, sizeof(MMVAD_LONG), 'ldaV'); if (!Vad) { MiDereferenceControlArea(ControlArea); return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(Vad, sizeof(MMVAD_LONG)); Vad->u4.Banked = (PVOID)(ULONG_PTR)0xDEADBABEDEADBABEULL; /* Write all the data required in the VAD for handling a fault */ Vad->ControlArea = ControlArea; Vad->u.VadFlags.CommitCharge = 0; Vad->u.VadFlags.Protection = ProtectionMask; Vad->u2.VadFlags2.FileOffset = (ULONG)(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 += ViewSizeInPages - 1; ASSERT(PteOffset < Subsection->PtesInSubsection); Vad->LastContiguousPte = &Subsection->SubsectionBase[PteOffset]; /* Make sure the prototype PTE ranges make sense, this is a Windows ASSERT */ ASSERT(Vad->FirstPrototypePte <= Vad->LastContiguousPte); /* FIXME: Should setup VAD bitmap */ Status = STATUS_SUCCESS; /* Check if anything was committed */ if (QuotaCharge) { /* Set the start and end PTE addresses, and pick the template PTE */ PointerPte = Vad->FirstPrototypePte; LastPte = PointerPte + BYTES_TO_PAGES(CommitSize); TempPte = Segment->SegmentPteTemplate; /* Acquire the commit lock and loop all prototype PTEs to be committed */ KeAcquireGuardedMutex(&MmSectionCommitMutex); while (PointerPte < LastPte) { /* Make sure the PTE is already invalid */ if (PointerPte->u.Long == 0) { /* And write the invalid PTE */ MI_WRITE_INVALID_PTE(PointerPte, TempPte); } else { /* The PTE is valid, so skip it */ QuotaExcess++; } /* Move to the next PTE */ PointerPte++; } /* Now check how many pages exactly we committed, and update accounting */ ASSERT(QuotaCharge >= QuotaExcess); QuotaCharge -= QuotaExcess; Segment->NumberOfCommittedPages += QuotaCharge; ASSERT(Segment->NumberOfCommittedPages <= Segment->TotalNumberOfPtes); /* Now that we're done, release the lock */ KeReleaseGuardedMutex(&MmSectionCommitMutex); } /* Is it SEC_BASED, or did the caller manually specify an address? */ if (*BaseAddress != NULL) { /* Just align what the caller gave us */ StartAddress = ALIGN_DOWN_BY((ULONG_PTR)*BaseAddress, Granularity); } else if (Section->Address.StartingVpn != 0) { /* It is a SEC_BASED mapping, use the address that was generated */ StartAddress = Section->Address.StartingVpn + SectionOffset->LowPart; } else { StartAddress = 0; } Status = PsChargeProcessNonPagedPoolQuota(Process, sizeof(MMVAD_LONG)); if (!NT_SUCCESS(Status)) { ExFreePoolWithTag(Vad, 'ldaV'); MiDereferenceControlArea(ControlArea); KeAcquireGuardedMutex(&MmSectionCommitMutex); Segment->NumberOfCommittedPages -= QuotaCharge; KeReleaseGuardedMutex(&MmSectionCommitMutex); return Status; } /* Insert the VAD */ Status = MiInsertVadEx((PMMVAD)Vad, &StartAddress, ViewSizeInPages * PAGE_SIZE, MAXULONG_PTR >> ZeroBits, Granularity, AllocationType); if (!NT_SUCCESS(Status)) { ExFreePoolWithTag(Vad, 'ldaV'); MiDereferenceControlArea(ControlArea); KeAcquireGuardedMutex(&MmSectionCommitMutex); Segment->NumberOfCommittedPages -= QuotaCharge; KeReleaseGuardedMutex(&MmSectionCommitMutex); PsReturnProcessNonPagedPoolQuota(PsGetCurrentProcess(), sizeof(MMVAD_LONG)); return Status; } /* 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 = ViewSizeInPages * PAGE_SIZE; *BaseAddress = (PVOID)StartAddress; DPRINT("Start and region: 0x%p, 0x%p\n", *BaseAddress, *ViewSize); return STATUS_SUCCESS; } VOID NTAPI MiSubsectionConsistent(IN PSUBSECTION Subsection) { /* ReactOS only supports systems with 4K pages and 4K sectors */ ASSERT(Subsection->u.SubsectionFlags.SectorEndOffset == 0); /* Therefore, then number of PTEs should be equal to the number of sectors */ if (Subsection->NumberOfFullSectors != Subsection->PtesInSubsection) { /* Break and warn if this is inconsistent */ DPRINT1("Mm: Subsection inconsistent (%x vs %x)\n", Subsection->NumberOfFullSectors, Subsection->PtesInSubsection); DbgBreakPoint(); } } NTSTATUS NTAPI MiCreateDataFileMap(IN PFILE_OBJECT File, OUT PSEGMENT *Segment, IN PSIZE_T MaximumSize, IN ULONG SectionPageProtection, IN ULONG AllocationAttributes, IN ULONG IgnoreFileSizing) { /* Not yet implemented */ ASSERT(FALSE); *Segment = NULL; return STATUS_NOT_IMPLEMENTED; } static NTSTATUS NTAPI MiCreatePagingFileMap(OUT PSEGMENT *Segment, IN PLARGE_INTEGER MaximumSize, IN ULONG ProtectionMask, IN ULONG AllocationAttributes) { ULONGLONG SizeLimit; PFN_COUNT 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 || !MaximumSize->QuadPart || MaximumSize->QuadPart < 0) return STATUS_INVALID_PARAMETER_4; /* Calculate the maximum size possible, given the Prototype PTEs we'll need */ SizeLimit = MmSizeOfPagedPoolInBytes - sizeof(SEGMENT); SizeLimit /= sizeof(MMPTE); SizeLimit <<= PAGE_SHIFT; /* Fail if this size is too big */ if (MaximumSize->QuadPart > SizeLimit) { return STATUS_SECTION_TOO_BIG; } /* Calculate how many Prototype PTEs will be needed */ PteCount = (PFN_COUNT)((MaximumSize->QuadPart + 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'); if (!NewSegment) { return STATUS_INSUFFICIENT_RESOURCES; } *Segment = NewSegment; /* Now allocate the control area, which has the subsection structure */ ControlArea = ExAllocatePoolWithTag(NonPagedPool, sizeof(CONTROL_AREA) + sizeof(SUBSECTION), 'tCmM'); if (!ControlArea) { ExFreePoolWithTag(Segment, 'tSmM'); return STATUS_INSUFFICIENT_RESOURCES; } /* 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; /* We just allocated it */ ControlArea->u.Flags.BeingCreated = 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 = ((ULONGLONG)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 */ #ifdef _WIN64 RtlFillMemoryUlonglong(PointerPte, PteCount * sizeof(MMPTE), TempPte.u.Long); #else RtlFillMemoryUlong(PointerPte, PteCount * sizeof(MMPTE), TempPte.u.Long); #endif return STATUS_SUCCESS; } NTSTATUS NTAPI MiGetFileObjectForSectionAddress( IN PVOID Address, OUT PFILE_OBJECT *FileObject) { PMMVAD Vad; PCONTROL_AREA ControlArea; /* Get the VAD */ Vad = MiLocateAddress(Address); if (Vad == NULL) { /* Fail, the address does not exist */ DPRINT1("Invalid address\n"); return STATUS_INVALID_ADDRESS; } /* Check if this is a RosMm memory area */ if (Vad->u.VadFlags.Spare != 0) { PMEMORY_AREA MemoryArea = (PMEMORY_AREA)Vad; /* Check if it's a section view (RosMm section) */ if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW) { /* Get the section pointer to the SECTION_OBJECT */ *FileObject = MemoryArea->SectionData.Segment->FileObject; } else { #ifdef NEWCC ASSERT(MemoryArea->Type == MEMORY_AREA_CACHE); DPRINT1("Address is a cache section!\n"); return STATUS_SECTION_NOT_IMAGE; #else ASSERT(FALSE); return STATUS_SECTION_NOT_IMAGE; #endif } } else { /* Make sure it's not a VM VAD */ if (Vad->u.VadFlags.PrivateMemory == 1) { DPRINT1("Address is not a section\n"); return STATUS_SECTION_NOT_IMAGE; } /* Get the control area */ ControlArea = Vad->ControlArea; if (!(ControlArea) || !(ControlArea->u.Flags.Image)) { DPRINT1("Address is not a section\n"); return STATUS_SECTION_NOT_IMAGE; } /* Get the file object */ *FileObject = ControlArea->FilePointer; } /* Return success */ return STATUS_SUCCESS; } PFILE_OBJECT NTAPI MmGetFileObjectForSection(IN PVOID SectionObject) { PSECTION Section = SectionObject; ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); ASSERT(SectionObject != NULL); /* Check if it's an ARM3, or ReactOS section */ if (MiIsRosSectionObject(SectionObject) == FALSE) { /* Return the file pointer stored in the control area */ return Section->Segment->ControlArea->FilePointer; } /* Return the file object */ return ((PMM_SECTION_SEGMENT)Section->Segment)->FileObject; } static PFILE_OBJECT MiGetFileObjectForVad( _In_ PMMVAD Vad) { PCONTROL_AREA ControlArea; PFILE_OBJECT FileObject; /* Check if this is a RosMm memory area */ if (Vad->u.VadFlags.Spare != 0) { PMEMORY_AREA MemoryArea = (PMEMORY_AREA)Vad; /* Check if it's a section view (RosMm section) */ if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW) { /* Get the section pointer to the SECTION_OBJECT */ FileObject = MemoryArea->SectionData.Segment->FileObject; } else { #ifdef NEWCC ASSERT(MemoryArea->Type == MEMORY_AREA_CACHE); DPRINT1("VAD is a cache section!\n"); #else ASSERT(FALSE); #endif return NULL; } } else { /* Make sure it's not a VM VAD */ if (Vad->u.VadFlags.PrivateMemory == 1) { DPRINT1("VAD is not a section\n"); return NULL; } /* Get the control area */ ControlArea = Vad->ControlArea; if ((ControlArea == NULL) || !ControlArea->u.Flags.Image) { DPRINT1("Address is not a section\n"); return NULL; } /* Get the file object */ FileObject = ControlArea->FilePointer; } /* Return the file object */ return FileObject; } VOID NTAPI MmGetImageInformation (OUT PSECTION_IMAGE_INFORMATION ImageInformation) { PSECTION SectionObject; /* Get the section object of this process*/ SectionObject = PsGetCurrentProcess()->SectionObject; ASSERT(SectionObject != NULL); ASSERT(MiIsRosSectionObject(SectionObject) == TRUE); if (SectionObject->u.Flags.Image == 0) { RtlZeroMemory(ImageInformation, sizeof(*ImageInformation)); return; } /* Return the image information */ *ImageInformation = ((PMM_IMAGE_SECTION_OBJECT)SectionObject->Segment)->ImageInformation; } 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, TAG_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, TAG_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; PSECTION SectionObject = Section; /* Make sure it's an image section */ if (SectionObject->u.Flags.Image == 0) { /* 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) { POBJECT_NAME_INFORMATION ModuleNameInformation; PVOID AddressSpace; NTSTATUS Status; PMMVAD Vad; PFILE_OBJECT FileObject = NULL; /* Lock address space */ AddressSpace = MmGetCurrentAddressSpace(); MmLockAddressSpace(AddressSpace); /* Get the VAD */ Vad = MiLocateAddress(Address); if (Vad == NULL) { /* Fail, the address does not exist */ DPRINT1("No VAD at address %p\n", Address); MmUnlockAddressSpace(AddressSpace); return STATUS_INVALID_ADDRESS; } /* Get the file object pointer for the VAD */ FileObject = MiGetFileObjectForVad(Vad); if (FileObject == NULL) { DPRINT1("Failed to get file object for Address %p\n", Address); MmUnlockAddressSpace(AddressSpace); return STATUS_SECTION_NOT_IMAGE; } /* Reference the file object */ ObReferenceObject(FileObject); /* Unlock address space */ MmUnlockAddressSpace(AddressSpace); /* Get the filename of the file object */ Status = MmGetFileNameForFileObject(FileObject, &ModuleNameInformation); /* Dereference the file object */ ObDereferenceObject(FileObject); /* Check if we were able to get the file object name */ if (NT_SUCCESS(Status)) { /* Init modulename */ if (!RtlCreateUnicodeString(ModuleName, ModuleNameInformation->Name.Buffer)) Status = STATUS_INSUFFICIENT_RESOURCES; /* Free temp taged buffer from MmGetFileNameForFileObject() */ ExFreePoolWithTag(ModuleNameInformation, TAG_MM); DPRINT("Found ModuleName %wZ by address %p\n", ModuleName, Address); } /* Return status */ return Status; } NTSTATUS NTAPI MiQueryMemorySectionName(IN HANDLE ProcessHandle, IN PVOID BaseAddress, OUT PVOID MemoryInformation, IN SIZE_T MemoryInformationLength, OUT PSIZE_T ReturnLength) { PEPROCESS Process; NTSTATUS Status; UNICODE_STRING ModuleFileName; PMEMORY_SECTION_NAME SectionName = NULL; KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); Status = ObReferenceObjectByHandle(ProcessHandle, PROCESS_QUERY_INFORMATION, NULL, PreviousMode, (PVOID*)(&Process), NULL); if (!NT_SUCCESS(Status)) { DPRINT("MiQueryMemorySectionName: ObReferenceObjectByHandle returned %x\n",Status); return Status; } Status = MmGetFileNameForAddress(BaseAddress, &ModuleFileName); if (NT_SUCCESS(Status)) { SectionName = MemoryInformation; if (PreviousMode != KernelMode) { _SEH2_TRY { RtlInitEmptyUnicodeString(&SectionName->SectionFileName, (PWSTR)(SectionName + 1), MemoryInformationLength - sizeof(MEMORY_SECTION_NAME)); RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName); if (ReturnLength) *ReturnLength = ModuleFileName.Length + sizeof(MEMORY_SECTION_NAME); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; } else { RtlInitEmptyUnicodeString(&SectionName->SectionFileName, (PWSTR)(SectionName + 1), MemoryInformationLength - sizeof(MEMORY_SECTION_NAME)); RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName); if (ReturnLength) *ReturnLength = ModuleFileName.Length + sizeof(MEMORY_SECTION_NAME); } RtlFreeUnicodeString(&ModuleFileName); } ObDereferenceObject(Process); return Status; } VOID NTAPI MiFlushTbAndCapture(IN PMMVAD FoundVad, IN PMMPTE PointerPte, IN ULONG ProtectionMask, IN PMMPFN Pfn1, IN BOOLEAN UpdateDirty) { MMPTE TempPte, PreviousPte; KIRQL OldIrql; BOOLEAN RebuildPte = FALSE; // // User for sanity checking later on // PreviousPte = *PointerPte; // // Build the PTE and acquire the PFN lock // MI_MAKE_HARDWARE_PTE_USER(&TempPte, PointerPte, ProtectionMask, PreviousPte.u.Hard.PageFrameNumber); OldIrql = MiAcquirePfnLock(); // // We don't support I/O mappings in this path yet // ASSERT(Pfn1 != NULL); ASSERT(Pfn1->u3.e1.CacheAttribute != MiWriteCombined); // // Make sure new protection mask doesn't get in conflict and fix it if it does // if (Pfn1->u3.e1.CacheAttribute == MiCached) { // // This is a cached PFN // if (ProtectionMask & (MM_NOCACHE | MM_NOACCESS)) { RebuildPte = TRUE; ProtectionMask &= ~(MM_NOCACHE | MM_NOACCESS); } } else if (Pfn1->u3.e1.CacheAttribute == MiNonCached) { // // This is a non-cached PFN // if ((ProtectionMask & (MM_NOCACHE | MM_NOACCESS)) != MM_NOCACHE) { RebuildPte = TRUE; ProtectionMask &= ~MM_NOACCESS; ProtectionMask |= MM_NOCACHE; } } if (RebuildPte) { MI_MAKE_HARDWARE_PTE_USER(&TempPte, PointerPte, ProtectionMask, PreviousPte.u.Hard.PageFrameNumber); } // // Write the new PTE, making sure we are only changing the bits // MI_UPDATE_VALID_PTE(PointerPte, TempPte); // // Flush the TLB // ASSERT(PreviousPte.u.Hard.Valid == 1); KeFlushCurrentTb(); ASSERT(PreviousPte.u.Hard.Valid == 1); // // Windows updates the relevant PFN1 information, we currently don't. // if (UpdateDirty && PreviousPte.u.Hard.Dirty) { if (!Pfn1->u3.e1.Modified) { DPRINT1("FIXME: Mark PFN as dirty\n"); } } // // Not supported in ARM3 // ASSERT(FoundVad->u.VadFlags.VadType != VadWriteWatch); // // Release the PFN lock, we are done // MiReleasePfnLock(OldIrql); } // // NOTE: This function gets a lot more complicated if we want Copy-on-Write support // NTSTATUS NTAPI MiSetProtectionOnSection(IN PEPROCESS Process, IN PMMVAD FoundVad, IN PVOID StartingAddress, IN PVOID EndingAddress, IN ULONG NewProtect, OUT PULONG CapturedOldProtect, IN ULONG DontCharge, OUT PULONG Locked) { PMMPTE PointerPte, LastPte; MMPTE TempPte, PteContents; PMMPDE PointerPde; PMMPFN Pfn1; ULONG ProtectionMask, QuotaCharge = 0; PETHREAD Thread = PsGetCurrentThread(); PAGED_CODE(); // // Tell caller nothing is being locked // *Locked = FALSE; // // This function should only be used for section VADs. Windows ASSERT */ // ASSERT(FoundVad->u.VadFlags.PrivateMemory == 0); // // We don't support these features in ARM3 // ASSERT(FoundVad->u.VadFlags.VadType != VadImageMap); ASSERT(FoundVad->u2.VadFlags2.CopyOnWrite == 0); // // Convert and validate the protection mask // ProtectionMask = MiMakeProtectionMask(NewProtect); if (ProtectionMask == MM_INVALID_PROTECTION) { DPRINT1("Invalid section protect\n"); return STATUS_INVALID_PAGE_PROTECTION; } // // Get the PTE and PDE for the address, as well as the final PTE // MiLockProcessWorkingSetUnsafe(Process, Thread); PointerPde = MiAddressToPde(StartingAddress); PointerPte = MiAddressToPte(StartingAddress); LastPte = MiAddressToPte(EndingAddress); // // Make the PDE valid, and check the status of the first PTE // MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL); if (PointerPte->u.Long) { // // Not supported in ARM3 // ASSERT(FoundVad->u.VadFlags.VadType != VadRotatePhysical); // // Capture the page protection and make the PDE valid // *CapturedOldProtect = MiGetPageProtection(PointerPte); MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL); } else { // // Only pagefile-backed section VADs are supported for now // ASSERT(FoundVad->u.VadFlags.VadType != VadImageMap); // // Grab the old protection from the VAD itself // *CapturedOldProtect = MmProtectToValue[FoundVad->u.VadFlags.Protection]; } // // Loop all the PTEs now // MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL); while (PointerPte <= LastPte) { // // Check if we've crossed a PDE boundary and make the new PDE valid too // if (MiIsPteOnPdeBoundary(PointerPte)) { PointerPde = MiPteToPde(PointerPte); MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL); } // // Capture the PTE and see what we're dealing with // PteContents = *PointerPte; if (PteContents.u.Long == 0) { // // This used to be a zero PTE and it no longer is, so we must add a // reference to the pagetable. // MiIncrementPageTableReferences(MiPteToAddress(PointerPte)); // // Create the demand-zero prototype PTE // TempPte = PrototypePte; TempPte.u.Soft.Protection = ProtectionMask; MI_WRITE_INVALID_PTE(PointerPte, TempPte); } else if (PteContents.u.Hard.Valid == 1) { // // Get the PFN entry // Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(&PteContents)); // // We don't support these yet // ASSERT((NewProtect & (PAGE_NOACCESS | PAGE_GUARD)) == 0); ASSERT(Pfn1->u3.e1.PrototypePte == 0); // // Write the protection mask and write it with a TLB flush // Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask; MiFlushTbAndCapture(FoundVad, PointerPte, ProtectionMask, Pfn1, TRUE); } else { // // We don't support these cases yet // ASSERT(PteContents.u.Soft.Prototype == 0); ASSERT(PteContents.u.Soft.Transition == 0); // // The PTE is already demand-zero, just update the protection mask // PointerPte->u.Soft.Protection = ProtectionMask; } PointerPte++; } // // Unlock the working set and update quota charges if needed, then return // MiUnlockProcessWorkingSetUnsafe(Process, Thread); if ((QuotaCharge > 0) && (!DontCharge)) { FoundVad->u.VadFlags.CommitCharge -= QuotaCharge; Process->CommitCharge -= QuotaCharge; } return STATUS_SUCCESS; } VOID NTAPI MiRemoveMappedPtes(IN PVOID BaseAddress, IN ULONG NumberOfPtes, IN PCONTROL_AREA ControlArea, IN PMMSUPPORT Ws) { PMMPTE PointerPte, ProtoPte;//, FirstPte; PMMPDE PointerPde, SystemMapPde; PMMPFN Pfn1, Pfn2; MMPTE PteContents; KIRQL OldIrql; DPRINT("Removing mapped view at: 0x%p\n", BaseAddress); ASSERT(Ws == NULL); /* Get the PTE and loop each one */ PointerPte = MiAddressToPte(BaseAddress); //FirstPte = PointerPte; while (NumberOfPtes) { /* Check if the PTE is already valid */ PteContents = *PointerPte; if (PteContents.u.Hard.Valid == 1) { /* Get the PFN entry */ Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(&PteContents)); /* Get the PTE */ PointerPde = MiPteToPde(PointerPte); /* Lock the PFN database and make sure this isn't a mapped file */ OldIrql = MiAcquirePfnLock(); ASSERT(((Pfn1->u3.e1.PrototypePte) && (Pfn1->OriginalPte.u.Soft.Prototype)) == 0); /* Mark the page as modified accordingly */ if (MI_IS_PAGE_DIRTY(&PteContents)) Pfn1->u3.e1.Modified = 1; /* Was the PDE invalid */ if (PointerPde->u.Long == 0) { #if (_MI_PAGING_LEVELS == 2) /* Find the system double-mapped PDE that describes this mapping */ SystemMapPde = &MmSystemPagePtes[((ULONG_PTR)PointerPde & (SYSTEM_PD_SIZE - 1)) / sizeof(MMPTE)]; /* Make it valid */ ASSERT(SystemMapPde->u.Hard.Valid == 1); MI_WRITE_VALID_PDE(PointerPde, *SystemMapPde); #else DBG_UNREFERENCED_LOCAL_VARIABLE(SystemMapPde); ASSERT(FALSE); #endif } /* Dereference the PDE and the PTE */ Pfn2 = MiGetPfnEntry(PFN_FROM_PTE(PointerPde)); MiDecrementShareCount(Pfn2, PFN_FROM_PTE(PointerPde)); DBG_UNREFERENCED_LOCAL_VARIABLE(Pfn2); MiDecrementShareCount(Pfn1, PFN_FROM_PTE(&PteContents)); /* Release the PFN lock */ MiReleasePfnLock(OldIrql); } else { /* Windows ASSERT */ ASSERT((PteContents.u.Long == 0) || (PteContents.u.Soft.Prototype == 1)); /* Check if this is a prototype pointer PTE */ if (PteContents.u.Soft.Prototype == 1) { /* Get the prototype PTE */ ProtoPte = MiProtoPteToPte(&PteContents); /* We don't support anything else atm */ ASSERT(ProtoPte->u.Long == 0); } } /* Make the PTE into a zero PTE */ PointerPte->u.Long = 0; /* Move to the next PTE */ PointerPte++; NumberOfPtes--; } /* Flush the TLB */ KeFlushCurrentTb(); /* Acquire the PFN lock */ OldIrql = MiAcquirePfnLock(); /* Decrement the accounting counters */ ControlArea->NumberOfUserReferences--; ControlArea->NumberOfMappedViews--; /* Check if we should destroy the CA and release the lock */ MiCheckControlArea(ControlArea, OldIrql); } ULONG NTAPI MiRemoveFromSystemSpace(IN PMMSESSION Session, IN PVOID Base, OUT PCONTROL_AREA *ControlArea) { ULONG Hash, Size, Count = 0; ULONG_PTR Entry; PAGED_CODE(); /* Compute the hash for this entry and loop trying to find it */ Entry = (ULONG_PTR)Base >> 16; Hash = Entry % Session->SystemSpaceHashKey; while ((Session->SystemSpaceViewTable[Hash].Entry >> 16) != Entry) { /* Check if we overflew past the end of the hash table */ if (++Hash >= Session->SystemSpaceHashSize) { /* Reset the hash to zero and keep searching from the bottom */ Hash = 0; if (++Count == 2) { /* But if we overflew twice, then this is not a real mapping */ KeBugCheckEx(DRIVER_UNMAPPING_INVALID_VIEW, (ULONG_PTR)Base, 1, 0, 0); } } } /* One less entry */ Session->SystemSpaceHashEntries--; /* Extract the size and clear the entry */ Size = Session->SystemSpaceViewTable[Hash].Entry & 0xFFFF; Session->SystemSpaceViewTable[Hash].Entry = 0; /* Return the control area and the size */ *ControlArea = Session->SystemSpaceViewTable[Hash].ControlArea; return Size; } NTSTATUS NTAPI MiUnmapViewInSystemSpace(IN PMMSESSION Session, IN PVOID MappedBase) { ULONG Size; PCONTROL_AREA ControlArea; PAGED_CODE(); /* Remove this mapping */ KeAcquireGuardedMutex(Session->SystemSpaceViewLockPointer); Size = MiRemoveFromSystemSpace(Session, MappedBase, &ControlArea); /* Clear the bits for this mapping */ RtlClearBits(Session->SystemSpaceBitMap, (ULONG)(((ULONG_PTR)MappedBase - (ULONG_PTR)Session->SystemSpaceViewStart) >> 16), Size); /* Convert the size from a bit size into the actual size */ Size = Size * (_64K >> PAGE_SHIFT); /* Remove the PTEs now */ MiRemoveMappedPtes(MappedBase, Size, ControlArea, NULL); KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer); /* Return success */ return STATUS_SUCCESS; } /* 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, Segment; NTSTATUS Status; PCONTROL_AREA ControlArea; ULONG ProtectionMask, ControlAreaSize, Size, NonPagedCharge, PagedCharge; KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); BOOLEAN FileLock = FALSE, KernelCall = FALSE; KIRQL OldIrql; PFILE_OBJECT File; BOOLEAN UserRefIncremented = FALSE; PVOID PreviousSectionPointer; /* 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) { DPRINT1("Invalid protection mask\n"); return STATUS_INVALID_PAGE_PROTECTION; } /* Check if this is going to be a data or image backed file section */ if ((FileHandle) || (FileObject)) { /* These cannot be mapped with large pages */ if (AllocationAttributes & SEC_LARGE_PAGES) return STATUS_INVALID_PARAMETER_6; /* For now, only support the mechanism through a file handle */ ASSERT(FileObject == NULL); /* Reference the file handle to get the object */ Status = ObReferenceObjectByHandle(FileHandle, MmMakeFileAccess[ProtectionMask], IoFileObjectType, PreviousMode, (PVOID*)&File, NULL); if (!NT_SUCCESS(Status)) return Status; /* Make sure Cc has been doing its job */ if (!File->SectionObjectPointer) { /* This is not a valid file system-based file, fail */ ObDereferenceObject(File); return STATUS_INVALID_FILE_FOR_SECTION; } /* Image-file backed sections are not yet supported */ ASSERT((AllocationAttributes & SEC_IMAGE) == 0); /* Compute the size of the control area, and allocate it */ ControlAreaSize = sizeof(CONTROL_AREA) + sizeof(MSUBSECTION); ControlArea = ExAllocatePoolWithTag(NonPagedPool, ControlAreaSize, 'aCmM'); if (!ControlArea) { ObDereferenceObject(File); return STATUS_INSUFFICIENT_RESOURCES; } /* Zero it out */ RtlZeroMemory(ControlArea, ControlAreaSize); /* Did we get a handle, or an object? */ if (FileHandle) { /* We got a file handle so we have to lock down the file */ #if 0 Status = FsRtlAcquireToCreateMappedSection(File, SectionPageProtection); if (!NT_SUCCESS(Status)) { ExFreePool(ControlArea); ObDereferenceObject(File); return Status; } #else /* ReactOS doesn't support this API yet, so do nothing */ Status = STATUS_SUCCESS; #endif /* Update the top-level IRP so that drivers know what's happening */ IoSetTopLevelIrp((PIRP)FSRTL_FSP_TOP_LEVEL_IRP); FileLock = TRUE; } /* Lock the PFN database while we play with the section pointers */ OldIrql = MiAcquirePfnLock(); /* Image-file backed sections are not yet supported */ ASSERT((AllocationAttributes & SEC_IMAGE) == 0); /* There should not already be a control area for this file */ ASSERT(File->SectionObjectPointer->DataSectionObject == NULL); NewSegment = NULL; /* Write down that this CA is being created, and set it */ ControlArea->u.Flags.BeingCreated = TRUE; ASSERT((AllocationAttributes & SEC_IMAGE) == 0); PreviousSectionPointer = File->SectionObjectPointer; File->SectionObjectPointer->DataSectionObject = ControlArea; /* We can release the PFN lock now */ MiReleasePfnLock(OldIrql); /* We don't support previously-mapped file */ ASSERT(NewSegment == NULL); /* Image-file backed sections are not yet supported */ ASSERT((AllocationAttributes & SEC_IMAGE) == 0); /* So we always create a data file map */ Status = MiCreateDataFileMap(File, &Segment, (PSIZE_T)InputMaximumSize, SectionPageProtection, AllocationAttributes, KernelCall); if (!NT_SUCCESS(Status)) { /* Lock the PFN database while we play with the section pointers */ OldIrql = MiAcquirePfnLock(); /* Reset the waiting-for-deletion event */ ASSERT(ControlArea->WaitingForDeletion == NULL); ControlArea->WaitingForDeletion = NULL; /* Set the file pointer NULL flag */ ASSERT(ControlArea->u.Flags.FilePointerNull == 0); ControlArea->u.Flags.FilePointerNull = TRUE; /* Delete the data section object */ ASSERT((AllocationAttributes & SEC_IMAGE) == 0); File->SectionObjectPointer->DataSectionObject = NULL; /* No longer being created */ ControlArea->u.Flags.BeingCreated = FALSE; /* We can release the PFN lock now */ MiReleasePfnLock(OldIrql); /* Check if we locked and set the IRP */ if (FileLock) { /* Undo */ IoSetTopLevelIrp(NULL); //FsRtlReleaseFile(File); } /* Free the control area and de-ref the file object */ ExFreePool(ControlArea); ObDereferenceObject(File); /* All done */ return Status; } /* On success, we expect this */ ASSERT(PreviousSectionPointer == File->SectionObjectPointer); /* Check if a maximum size was specified */ if (!InputMaximumSize->QuadPart) { /* Nope, use the segment size */ Section.SizeOfSection.QuadPart = (LONGLONG)Segment->SizeOfSegment; } else { /* Yep, use the entered size */ Section.SizeOfSection.QuadPart = InputMaximumSize->QuadPart; } } else { /* A handle must be supplied with SEC_IMAGE, as this is the no-handle path */ if (AllocationAttributes & SEC_IMAGE) return STATUS_INVALID_FILE_FOR_SECTION; /* Not yet supported */ ASSERT((AllocationAttributes & SEC_LARGE_PAGES) == 0); /* So this must be a pagefile-backed section, create the mappings needed */ Status = MiCreatePagingFileMap(&NewSegment, InputMaximumSize, ProtectionMask, AllocationAttributes); if (!NT_SUCCESS(Status)) return Status; /* Set the size here, and read the control area */ Section.SizeOfSection.QuadPart = NewSegment->SizeOfSegment; ControlArea = NewSegment->ControlArea; /* MiCreatePagingFileMap increments user references */ UserRefIncremented = TRUE; } /* Did we already have a segment? */ if (!NewSegment) { /* This must be the file path and we created a segment */ NewSegment = Segment; ASSERT(File != NULL); /* Acquire the PFN lock while we set control area flags */ OldIrql = MiAcquirePfnLock(); /* We don't support this race condition yet, so assume no waiters */ ASSERT(ControlArea->WaitingForDeletion == NULL); ControlArea->WaitingForDeletion = NULL; /* Image-file backed sections are not yet supported, nor ROM images */ ASSERT((AllocationAttributes & SEC_IMAGE) == 0); ASSERT(Segment->ControlArea->u.Flags.Rom == 0); /* Take off the being created flag, and then release the lock */ ControlArea->u.Flags.BeingCreated = FALSE; MiReleasePfnLock(OldIrql); } /* Check if we locked the file earlier */ if (FileLock) { /* Reset the top-level IRP and release the lock */ IoSetTopLevelIrp(NULL); //FsRtlReleaseFile(File); FileLock = FALSE; } /* Set the initial section object data */ Section.InitialPageProtection = SectionPageProtection; /* The mapping created a control area and segment, save the flags */ Section.Segment = NewSegment; Section.u.LongFlags = ControlArea->u.LongFlags; /* Check if this is a user-mode read-write non-image file mapping */ if (!(FileObject) && (SectionPageProtection & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE)) && !(ControlArea->u.Flags.Image) && (ControlArea->FilePointer)) { /* Add a reference and set the flag */ Section.u.Flags.UserWritable = TRUE; InterlockedIncrement((volatile LONG*)&ControlArea->WritableUserReferences); } /* Check for image mappings or page file mappings */ if ((ControlArea->u.Flags.Image) || !(ControlArea->FilePointer)) { /* Charge the segment size, and allocate a subsection */ PagedCharge = sizeof(SECTION) + NewSegment->TotalNumberOfPtes * sizeof(MMPTE); Size = sizeof(SUBSECTION); } else { /* Charge nothing, and allocate a mapped subsection */ PagedCharge = 0; Size = sizeof(MSUBSECTION); } /* Check if this is a normal CA */ ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0); ASSERT(ControlArea->u.Flags.Rom == 0); /* Charge only a CA, and the subsection is right after */ NonPagedCharge = sizeof(CONTROL_AREA); Subsection = (PSUBSECTION)(ControlArea + 1); /* We only support single-subsection mappings */ NonPagedCharge += Size; ASSERT(Subsection->NextSubsection == NULL); /* Create the actual section object, with enough space for the prototype PTEs */ Status = ObCreateObject(PreviousMode, MmSectionObjectType, ObjectAttributes, PreviousMode, NULL, sizeof(SECTION), PagedCharge, NonPagedCharge, (PVOID*)&NewSection); if (!NT_SUCCESS(Status)) { /* Check if this is a user-mode read-write non-image file mapping */ if (!(FileObject) && (SectionPageProtection & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE)) && !(ControlArea->u.Flags.Image) && (ControlArea->FilePointer)) { /* Remove a reference and check the flag */ ASSERT(Section.u.Flags.UserWritable == 1); InterlockedDecrement((volatile LONG*)&ControlArea->WritableUserReferences); } /* Check if a user reference was added */ if (UserRefIncremented) { /* Acquire the PFN lock while we change counters */ OldIrql = MiAcquirePfnLock(); /* Decrement the accounting counters */ ControlArea->NumberOfSectionReferences--; ASSERT((LONG)ControlArea->NumberOfUserReferences > 0); ControlArea->NumberOfUserReferences--; /* Check if we should destroy the CA and release the lock */ MiCheckControlArea(ControlArea, OldIrql); } /* Return the failure code */ return Status; } /* NOTE: Past this point, all failures will be handled by Ob upon ref->0 */ /* Now copy the local section object from the stack into this new object */ RtlCopyMemory(NewSection, &Section, sizeof(SECTION)); NewSection->Address.StartingVpn = 0; /* For now, only user calls are supported */ ASSERT(KernelCall == FALSE); NewSection->u.Flags.UserReference = TRUE; /* Is this a "based" allocation, in which all mappings are identical? */ if (AllocationAttributes & SEC_BASED) { /* Lock the VAD tree during the search */ KeAcquireGuardedMutex(&MmSectionBasedMutex); /* Is it a brand new ControArea ? */ if (ControlArea->u.Flags.BeingCreated == 1) { ASSERT(ControlArea->u.Flags.Based == 1); /* Then we must find a global address, top-down */ Status = MiFindEmptyAddressRangeDownBasedTree((SIZE_T)ControlArea->Segment->SizeOfSegment, (ULONG_PTR)MmHighSectionBase, _64K, &MmSectionBasedRoot, (ULONG_PTR*)&ControlArea->Segment->BasedAddress); if (!NT_SUCCESS(Status)) { /* No way to find a valid range. */ KeReleaseGuardedMutex(&MmSectionBasedMutex); ControlArea->u.Flags.Based = 0; NewSection->u.Flags.Based = 0; ObDereferenceObject(NewSection); return Status; } /* Compute the ending address and insert it into the VAD tree */ NewSection->Address.StartingVpn = (ULONG_PTR)ControlArea->Segment->BasedAddress; NewSection->Address.EndingVpn = NewSection->Address.StartingVpn + NewSection->SizeOfSection.LowPart - 1; MiInsertBasedSection(NewSection); } else { /* FIXME : Should we deny section creation if SEC_BASED is not set ? Can we have two different section objects on the same based address ? Investigate !*/ ASSERT(FALSE); } KeReleaseGuardedMutex(&MmSectionBasedMutex); } /* The control area is not being created anymore */ if (ControlArea->u.Flags.BeingCreated == 1) { /* Acquire the PFN lock while we set control area flags */ OldIrql = MiAcquirePfnLock(); /* Take off the being created flag, and then release the lock */ ControlArea->u.Flags.BeingCreated = 0; NewSection->u.Flags.BeingCreated = 0; MiReleasePfnLock(OldIrql); } /* Migrate the attribute into a flag */ if (AllocationAttributes & SEC_NO_CHANGE) NewSection->u.Flags.NoChange = TRUE; /* If R/W access is not requested, this might eventually become a CoW mapping */ if (!(SectionPageProtection & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE))) { NewSection->u.Flags.CopyOnWrite = TRUE; } /* Write down if this was a kernel call */ ControlArea->u.Flags.WasPurged |= KernelCall; ASSERT(ControlArea->u.Flags.WasPurged == FALSE); /* Make sure the segment and the section are the same size, or the section is smaller */ ASSERT((ULONG64)NewSection->SizeOfSection.QuadPart <= NewSection->Segment->SizeOfSegment); /* 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; ULONG64 CalculatedViewSize; 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(ControlArea->u.Flags.PhysicalMemory == 0); /* FIXME */ if ((AllocationType & MEM_RESERVE) != 0) { DPRINT1("MmMapViewOfArm3Section called with MEM_RESERVE, this is not implemented yet!!!\n"); return STATUS_NOT_IMPLEMENTED; } /* 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; } /* Check if the offset and size would cause an overflow */ if (((ULONG64)SectionOffset->QuadPart + *ViewSize) < (ULONG64)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 (((ULONG64)SectionOffset->QuadPart + *ViewSize) > (ULONG64)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 */ CalculatedViewSize = Section->SizeOfSection.QuadPart - SectionOffset->QuadPart; /* Check if it's larger than 4GB or overflows into kernel-mode */ if (!NT_SUCCESS(RtlULongLongToSIZET(CalculatedViewSize, ViewSize)) || (((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS - (ULONG_PTR)*BaseAddress) < CalculatedViewSize)) { 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 > (ULONG64)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; } /* Do the actual mapping */ Status = MiMapViewOfDataSection(ControlArea, Process, BaseAddress, SectionOffset, ViewSize, Section, InheritDisposition, ProtectionMask, CommitSize, ZeroBits, AllocationType); /* Detach if needed, then return status */ if (Attached) KeUnstackDetachProcess(&ApcState); return Status; } /* * @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; } /* * @implemented */ NTSTATUS NTAPI MmMapViewInSessionSpace(IN PVOID Section, OUT PVOID *MappedBase, IN OUT PSIZE_T ViewSize) { PAGED_CODE(); LARGE_INTEGER SectionOffset; // HACK if (MiIsRosSectionObject(Section)) { return MmMapViewInSystemSpace(Section, MappedBase, ViewSize); } /* Process must be in a session */ if (PsGetCurrentProcess()->ProcessInSession == FALSE) { DPRINT1("Process is not in session\n"); return STATUS_NOT_MAPPED_VIEW; } /* Use the system space API, but with the session view instead */ ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE); SectionOffset.QuadPart = 0; return MiMapViewInSystemSpace(Section, &MmSessionSpace->Session, MappedBase, ViewSize, &SectionOffset); } /* * @implemented */ NTSTATUS NTAPI MmUnmapViewInSessionSpace(IN PVOID MappedBase) { PAGED_CODE(); // HACK if (!MI_IS_SESSION_ADDRESS(MappedBase)) { return MmUnmapViewInSystemSpace(MappedBase); } /* Process must be in a session */ if (PsGetCurrentProcess()->ProcessInSession == FALSE) { DPRINT1("Proess is not in session\n"); return STATUS_NOT_MAPPED_VIEW; } /* Use the system space API, but with the session view instead */ ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE); return MiUnmapViewInSystemSpace(&MmSessionSpace->Session, MappedBase); } /* * @implemented */ NTSTATUS NTAPI MmUnmapViewOfSection(IN PEPROCESS Process, IN PVOID BaseAddress) { return MiUnmapViewOfSection(Process, BaseAddress, 0); } /* * @implemented */ NTSTATUS NTAPI MmUnmapViewInSystemSpace(IN PVOID MappedBase) { PMEMORY_AREA MemoryArea; PAGED_CODE(); /* Was this mapped by RosMm? */ MmLockAddressSpace(MmGetKernelAddressSpace()); MemoryArea = MmLocateMemoryAreaByAddress(MmGetKernelAddressSpace(), MappedBase); if ((MemoryArea) && (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3)) { NTSTATUS Status = MiRosUnmapViewInSystemSpace(MappedBase); MmUnlockAddressSpace(MmGetKernelAddressSpace()); return Status; } MmUnlockAddressSpace(MmGetKernelAddressSpace()); /* It was not, call the ARM3 routine */ return MiUnmapViewInSystemSpace(&MmSession, MappedBase); } /* * @implemented */ NTSTATUS NTAPI MmCommitSessionMappedView(IN PVOID MappedBase, IN SIZE_T ViewSize) { ULONG_PTR StartAddress, EndingAddress, Base; ULONG Hash, Count = 0, Size, QuotaCharge; PMMSESSION Session; PMMPTE LastProtoPte, PointerPte, ProtoPte; PCONTROL_AREA ControlArea; PSEGMENT Segment; PSUBSECTION Subsection; MMPTE TempPte; PAGED_CODE(); /* Make sure the base isn't past the session view range */ if ((MappedBase < MiSessionViewStart) || (MappedBase >= (PVOID)((ULONG_PTR)MiSessionViewStart + MmSessionViewSize))) { DPRINT1("Base outside of valid range\n"); return STATUS_INVALID_PARAMETER_1; } /* Make sure the size isn't past the session view range */ if (((ULONG_PTR)MiSessionViewStart + MmSessionViewSize - (ULONG_PTR)MappedBase) < ViewSize) { DPRINT1("Size outside of valid range\n"); return STATUS_INVALID_PARAMETER_2; } /* Sanity check */ ASSERT(ViewSize != 0); /* Process must be in a session */ if (PsGetCurrentProcess()->ProcessInSession == FALSE) { DPRINT1("Process is not in session\n"); return STATUS_NOT_MAPPED_VIEW; } /* Compute the correctly aligned base and end addresses */ StartAddress = (ULONG_PTR)PAGE_ALIGN(MappedBase); EndingAddress = ((ULONG_PTR)MappedBase + ViewSize - 1) | (PAGE_SIZE - 1); /* Sanity check and grab the session */ ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE); Session = &MmSessionSpace->Session; /* Get the hash entry for this allocation */ Hash = (StartAddress >> 16) % Session->SystemSpaceHashKey; /* Lock system space */ KeAcquireGuardedMutex(Session->SystemSpaceViewLockPointer); /* Loop twice so we can try rolling over if needed */ while (TRUE) { /* Extract the size and base addresses from the entry */ Base = Session->SystemSpaceViewTable[Hash].Entry & ~0xFFFF; Size = Session->SystemSpaceViewTable[Hash].Entry & 0xFFFF; /* Convert the size to bucket chunks */ Size *= MI_SYSTEM_VIEW_BUCKET_SIZE; /* Bail out if this entry fits in here */ if ((StartAddress >= Base) && (EndingAddress < (Base + Size))) break; /* Check if we overflew past the end of the hash table */ if (++Hash >= Session->SystemSpaceHashSize) { /* Reset the hash to zero and keep searching from the bottom */ Hash = 0; if (++Count == 2) { /* But if we overflew twice, then this is not a real mapping */ KeBugCheckEx(DRIVER_UNMAPPING_INVALID_VIEW, Base, 2, 0, 0); } } } /* Make sure the view being mapped is not file-based */ ControlArea = Session->SystemSpaceViewTable[Hash].ControlArea; if (ControlArea->FilePointer != NULL) { /* It is, so we have to bail out */ DPRINT1("Only page-filed backed sections can be commited\n"); KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer); return STATUS_ALREADY_COMMITTED; } /* Get the subsection. We don't support LARGE_CONTROL_AREA in ARM3 */ ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0); ASSERT(ControlArea->u.Flags.Rom == 0); Subsection = (PSUBSECTION)(ControlArea + 1); /* Get the start and end PTEs -- make sure the end PTE isn't past the end */ ProtoPte = Subsection->SubsectionBase + ((StartAddress - Base) >> PAGE_SHIFT); QuotaCharge = MiAddressToPte(EndingAddress) - MiAddressToPte(StartAddress) + 1; LastProtoPte = ProtoPte + QuotaCharge; if (LastProtoPte >= Subsection->SubsectionBase + Subsection->PtesInSubsection) { DPRINT1("PTE is out of bounds\n"); KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer); return STATUS_INVALID_PARAMETER_2; } /* Acquire the commit lock and count all the non-committed PTEs */ KeAcquireGuardedMutexUnsafe(&MmSectionCommitMutex); PointerPte = ProtoPte; while (PointerPte < LastProtoPte) { if (PointerPte->u.Long) QuotaCharge--; PointerPte++; } /* Was everything committed already? */ if (!QuotaCharge) { /* Nothing to do! */ KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex); KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer); return STATUS_SUCCESS; } /* Pick the segment and template PTE */ Segment = ControlArea->Segment; TempPte = Segment->SegmentPteTemplate; ASSERT(TempPte.u.Long != 0); /* Loop all prototype PTEs to be committed */ PointerPte = ProtoPte; while (PointerPte < LastProtoPte) { /* Make sure the PTE is already invalid */ if (PointerPte->u.Long == 0) { /* And write the invalid PTE */ MI_WRITE_INVALID_PTE(PointerPte, TempPte); } /* Move to the next PTE */ PointerPte++; } /* Check if we had at least one page charged */ if (QuotaCharge) { /* Update the accounting data */ Segment->NumberOfCommittedPages += QuotaCharge; InterlockedExchangeAddSizeT(&MmSharedCommit, QuotaCharge); } /* Release all */ KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex); KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer); return STATUS_SUCCESS; } VOID NTAPI MiDeleteARM3Section(PVOID ObjectBody) { PSECTION SectionObject; PCONTROL_AREA ControlArea; KIRQL OldIrql; SectionObject = (PSECTION)ObjectBody; if (SectionObject->u.Flags.Based == 1) { /* Remove the node from the global section address tree */ KeAcquireGuardedMutex(&MmSectionBasedMutex); MiRemoveNode(&SectionObject->Address, &MmSectionBasedRoot); KeReleaseGuardedMutex(&MmSectionBasedMutex); } /* Lock the PFN database */ OldIrql = MiAcquirePfnLock(); ASSERT(SectionObject->Segment); ASSERT(SectionObject->Segment->ControlArea); ControlArea = SectionObject->Segment->ControlArea; /* Dereference */ ControlArea->NumberOfSectionReferences--; ControlArea->NumberOfUserReferences--; ASSERT(ControlArea->u.Flags.BeingDeleted == 0); /* Check it. It will delete it if there is no more reference to it */ MiCheckControlArea(ControlArea, OldIrql); } ULONG NTAPI MmDoesFileHaveUserWritableReferences(IN PSECTION_OBJECT_POINTERS SectionPointer) { UNIMPLEMENTED_ONCE; return 0; } /* SYSTEM CALLS ***************************************************************/ NTSTATUS NTAPI NtAreMappedFilesTheSame(IN PVOID File1MappedAsAnImage, IN PVOID File2MappedAsFile) { PVOID AddressSpace; PMMVAD Vad1, Vad2; PFILE_OBJECT FileObject1, FileObject2; NTSTATUS Status; /* Lock address space */ AddressSpace = MmGetCurrentAddressSpace(); MmLockAddressSpace(AddressSpace); /* Get the VAD for Address 1 */ Vad1 = MiLocateAddress(File1MappedAsAnImage); if (Vad1 == NULL) { /* Fail, the address does not exist */ DPRINT1("No VAD at address 1 %p\n", File1MappedAsAnImage); Status = STATUS_INVALID_ADDRESS; goto Exit; } /* Get the VAD for Address 2 */ Vad2 = MiLocateAddress(File2MappedAsFile); if (Vad2 == NULL) { /* Fail, the address does not exist */ DPRINT1("No VAD at address 2 %p\n", File2MappedAsFile); Status = STATUS_INVALID_ADDRESS; goto Exit; } /* Get the file object pointer for VAD 1 */ FileObject1 = MiGetFileObjectForVad(Vad1); if (FileObject1 == NULL) { DPRINT1("Failed to get file object for Address 1 %p\n", File1MappedAsAnImage); Status = STATUS_CONFLICTING_ADDRESSES; goto Exit; } /* Get the file object pointer for VAD 2 */ FileObject2 = MiGetFileObjectForVad(Vad2); if (FileObject2 == NULL) { DPRINT1("Failed to get file object for Address 2 %p\n", File2MappedAsFile); Status = STATUS_CONFLICTING_ADDRESSES; goto Exit; } /* Make sure Vad1 is an image mapping */ if (Vad1->u.VadFlags.VadType != VadImageMap) { DPRINT1("Address 1 (%p) is not an image mapping\n", File1MappedAsAnImage); Status = STATUS_NOT_SAME_DEVICE; goto Exit; } /* SectionObjectPointer is equal if the files are equal */ if (FileObject1->SectionObjectPointer == FileObject2->SectionObjectPointer) { Status = STATUS_SUCCESS; } else { Status = STATUS_NOT_SAME_DEVICE; } Exit: /* Unlock address space */ MmUnlockAddressSpace(AddressSpace); return Status; } /* * @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; /* Check that MaximumSize is valid if backed by paging file */ if ((!FileHandle) && (!MaximumSize->QuadPart)) return STATUS_INVALID_PARAMETER_4; /* 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; PSECTION Section; PEPROCESS Process; NTSTATUS Status; ACCESS_MASK DesiredAccess; ULONG ProtectionMask; KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); #if defined(_M_IX86) || defined(_M_AMD64) static const ULONG ValidAllocationType = (MEM_TOP_DOWN | MEM_LARGE_PAGES | MEM_DOS_LIM | SEC_NO_CHANGE | MEM_RESERVE); #else static const ULONG ValidAllocationType = (MEM_TOP_DOWN | MEM_LARGE_PAGES | SEC_NO_CHANGE | MEM_RESERVE); #endif /* 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 & ~ValidAllocationType) { 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 (ZeroBits) { if (ZeroBits > MI_MAX_ZERO_BITS) { DPRINT1("Invalid zero bits\n"); return STATUS_INVALID_PARAMETER_4; } if ((((ULONG_PTR)SafeBaseAddress << ZeroBits) >> ZeroBits) != (ULONG_PTR)SafeBaseAddress) { DPRINT1("Invalid zero bits\n"); return STATUS_INVALID_PARAMETER_4; } if (((((ULONG_PTR)SafeBaseAddress + SafeViewSize) << ZeroBits) >> ZeroBits) != ((ULONG_PTR)SafeBaseAddress + SafeViewSize)) { 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; } if (Section->u.Flags.PhysicalMemory) { if (PreviousMode == UserMode && SafeSectionOffset.QuadPart + SafeViewSize > MmHighestPhysicalPage << PAGE_SHIFT) { DPRINT1("Denying map past highest physical page.\n"); ObDereferenceObject(Section); ObDereferenceObject(Process); return STATUS_INVALID_PARAMETER_6; } } else if (!(AllocationType & MEM_DOS_LIM)) { /* Check for non-allocation-granularity-aligned BaseAddress */ if (SafeBaseAddress != ALIGN_DOWN_POINTER_BY(SafeBaseAddress, MM_VIRTMEM_GRANULARITY)) { DPRINT("BaseAddress is not at 64-kilobyte address boundary.\n"); ObDereferenceObject(Section); ObDereferenceObject(Process); return STATUS_MAPPED_ALIGNMENT; } /* Do the same for the section offset */ if (SafeSectionOffset.LowPart != ALIGN_DOWN_BY(SafeSectionOffset.LowPart, MM_VIRTMEM_GRANULARITY)) { DPRINT("SectionOffset is not at 64-kilobyte address boundary.\n"); ObDereferenceObject(Section); ObDereferenceObject(Process); return STATUS_MAPPED_ALIGNMENT; } } /* Now do the actual mapping */ Status = MmMapViewOfSection(Section, Process, &SafeBaseAddress, ZeroBits, CommitSize, &SafeSectionOffset, &SafeViewSize, InheritDisposition, AllocationType, Protect); /* Return data only on success */ if (NT_SUCCESS(Status)) { /* Check if this is an image for the current process */ if ((Section->u.Flags.Image) && (Process == PsGetCurrentProcess()) && (Status != STATUS_IMAGE_NOT_AT_BASE)) { /* Notify the debugger */ DbgkMapViewOfSection(Section, SafeBaseAddress, SafeSectionOffset.LowPart, SafeViewSize); } /* 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 = MiUnmapViewOfSection(Process, BaseAddress, 0); /* Dereference the process and return status */ ObDereferenceObject(Process); return Status; } NTSTATUS NTAPI NtExtendSection(IN HANDLE SectionHandle, IN OUT PLARGE_INTEGER NewMaximumSize) { LARGE_INTEGER SafeNewMaximumSize; PSECTION 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; Status = MmExtendSection(Section, &SafeNewMaximumSize); /* Dereference the section */ ObDereferenceObject(Section); if (NT_SUCCESS(Status)) { _SEH2_TRY { /* Write back the new size */ *NewMaximumSize = SafeNewMaximumSize; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; } /* Return the status */ return STATUS_NOT_IMPLEMENTED; } /* EOF */