From 29764be43051ebe4553f43ca8ac6da51072e1262 Mon Sep 17 00:00:00 2001 From: Sir Richard Date: Tue, 5 Oct 2010 14:36:09 +0000 Subject: [PATCH] [NTOS]: Implement ARM3 version of MmMapViewOfSection, only for ARM3 pagefile-backed sections, and without any special flag support. Tested and works great, with the new pagefault code correctly finding the Prototype PTE for the VAD. [NTOS]: Make every VAD insert also create a MEMORY_AREA. Now the two address space views should be completely synchronized and we can try removing the hack that was done for PEB/TEB support (which will remove the 200 thread regression). [NTOS]: Implement MiGetNextNode and MiFindEmptyAddressRangeInTree. svn path=/trunk/; revision=48997 --- reactos/ntoskrnl/mm/ARM3/section.c | 293 +++++++++++++++++++++++++++++ reactos/ntoskrnl/mm/ARM3/vadnode.c | 128 +++++++++++++ reactos/ntoskrnl/mm/section.c | 32 +++- 3 files changed, 450 insertions(+), 3 deletions(-) diff --git a/reactos/ntoskrnl/mm/ARM3/section.c b/reactos/ntoskrnl/mm/ARM3/section.c index a2505a81010..9c7902084f2 100644 --- a/reactos/ntoskrnl/mm/ARM3/section.c +++ b/reactos/ntoskrnl/mm/ARM3/section.c @@ -463,6 +463,159 @@ MiMapViewInSystemSpace(IN PVOID Section, return STATUS_SUCCESS; } +NTSTATUS +NTAPI +MiMapViewOfDataSection(IN PCONTROL_AREA ControlArea, + IN PEPROCESS Process, + IN PVOID *BaseAddress, + IN PLARGE_INTEGER SectionOffset, + IN PSIZE_T ViewSize, + IN PSECTION Section, + IN SECTION_INHERIT InheritDisposition, + IN ULONG ProtectionMask, + IN ULONG CommitSize, + IN ULONG_PTR ZeroBits, + IN ULONG AllocationType) +{ + PMMVAD Vad; + PETHREAD Thread = PsGetCurrentThread(); + ULONG_PTR StartAddress, EndingAddress; + PSUBSECTION Subsection; + PSEGMENT Segment; + PFN_NUMBER PteOffset; + NTSTATUS Status; + + /* Get the segment and subection for this section */ + Segment = ControlArea->Segment; + Subsection = (PSUBSECTION)(ControlArea + 1); + + /* Non-pagefile-backed sections not supported */ + ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0); + ASSERT(ControlArea->u.Flags.Rom == 0); + ASSERT(ControlArea->FilePointer == NULL); + ASSERT(Segment->SegmentFlags.TotalNumberOfPtes4132 == 0); + + /* Based sections not supported */ + ASSERT(Section->Address.StartingVpn == 0); + + /* These flags/parameters are not supported */ + ASSERT((AllocationType & MEM_DOS_LIM) == 0); + ASSERT((AllocationType & MEM_RESERVE) == 0); + ASSERT((AllocationType & MEM_TOP_DOWN) == 0); + ASSERT(Process->VmTopDown == 0); + ASSERT(Section->u.Flags.CopyOnWrite == FALSE); + ASSERT(ZeroBits == 0); + + /* First, increase the map count. No purging is supported yet */ + Status = MiCheckPurgeAndUpMapCount(ControlArea, FALSE); + ASSERT(NT_SUCCESS(Status)); + + /* Check if the caller specified the view size */ + if (!(*ViewSize)) + { + /* The caller did not, so pick a 64K aligned view size based on the offset */ + SectionOffset->LowPart &= ~(_64K - 1); + *ViewSize = Section->SizeOfSection.QuadPart - SectionOffset->QuadPart; + } + else + { + /* A size was specified, align it to a 64K boundary */ + *ViewSize += SectionOffset->LowPart & (_64K - 1); + + /* Align the offset as well to make this an aligned map */ + SectionOffset->LowPart &= ~((ULONG)_64K - 1); + } + + /* We must be dealing with a 64KB aligned offset */ + ASSERT((SectionOffset->LowPart & ((ULONG)_64K - 1)) == 0); + + /* It's illegal to try to map more than 2GB */ + if (*ViewSize >= 0x80000000) return STATUS_INVALID_VIEW_SIZE; + + /* Within this section, figure out which PTEs will describe the view */ + PteOffset = SectionOffset->QuadPart >> PAGE_SHIFT; + + /* The offset must be in this segment's PTE chunk and it must be valid */ + ASSERT(PteOffset < Segment->TotalNumberOfPtes); + ASSERT(((SectionOffset->QuadPart + *ViewSize + PAGE_SIZE - 1) >> PAGE_SHIFT) >= PteOffset); + + /* In ARM3, only one subsection is used for now. It must contain these PTEs */ + ASSERT(PteOffset < Subsection->PtesInSubsection); + ASSERT(Subsection->SubsectionBase != NULL); + + /* In ARM3, only MEM_COMMIT is supported for now. The PTEs must've been committed */ + ASSERT(Segment->NumberOfCommittedPages >= Segment->TotalNumberOfPtes); + + /* Did the caller specify an address? */ + if (!(*BaseAddress)) + { + /* No, find an address bottom-up */ + Status = MiFindEmptyAddressRangeInTree(*ViewSize, + _64K, + &Process->VadRoot, + (PMMADDRESS_NODE*)&Process->VadFreeHint, + &StartAddress); + ASSERT(NT_SUCCESS(Status)); + } + else + { + /* This (rather easy) code path is not yet implemented */ + UNIMPLEMENTED; + while (TRUE); + } + + /* Get the ending address, which is the last piece we need for the VAD */ + EndingAddress = (StartAddress + *ViewSize - 1) | (PAGE_SIZE - 1); + + /* A VAD can now be allocated. Do so and zero it out */ + Vad = ExAllocatePoolWithTag(NonPagedPool, sizeof(MMVAD), 'ldaV'); + ASSERT(Vad); + RtlZeroMemory(Vad, sizeof(MMVAD)); + + /* Write all the data required in the VAD for handling a fault */ + Vad->StartingVpn = StartAddress >> PAGE_SHIFT; + Vad->EndingVpn = EndingAddress >> PAGE_SHIFT; + Vad->ControlArea = ControlArea; + Vad->u.VadFlags.Protection = ProtectionMask; + Vad->u2.VadFlags2.FileOffset = SectionOffset->QuadPart >> 16; + Vad->u2.VadFlags2.Inherit = (InheritDisposition == ViewShare); + if ((AllocationType & SEC_NO_CHANGE) || (Section->u.Flags.NoChange)) + { + /* This isn't really implemented yet, but handle setting the flag */ + Vad->u.VadFlags.NoChange = 1; + Vad->u2.VadFlags2.SecNoChange = 1; + } + + /* Finally, write down the first and last prototype PTE */ + Vad->FirstPrototypePte = &Subsection->SubsectionBase[PteOffset]; + PteOffset += (Vad->EndingVpn - Vad->StartingVpn); + Vad->LastContiguousPte = &Subsection->SubsectionBase[PteOffset]; + + /* Make sure the last PTE is valid and still within the subsection */ + ASSERT(PteOffset < Subsection->PtesInSubsection); + ASSERT(Vad->FirstPrototypePte <= Vad->LastContiguousPte); + + /* FIXME: Should setup VAD bitmap */ + Status = STATUS_SUCCESS; + + /* Pretend as if we own the working set */ + MiLockProcessWorkingSet(Process, Thread); + + /* Insert the VAD */ + MiInsertVad(Vad, Process); + + /* Release the working set */ + MiUnlockProcessWorkingSet(Process, Thread); + + /* Windows stores this for accounting purposes, do so as well */ + if (!Segment->u2.FirstMappedVa) Segment->u2.FirstMappedVa = (PVOID)StartAddress; + + /* Finally, let the caller know where, and for what size, the view was mapped */ + *ViewSize = (ULONG_PTR)EndingAddress - (ULONG_PTR)StartAddress + 1; + *BaseAddress = (PVOID)StartAddress; + return STATUS_SUCCESS; +} + NTSTATUS NTAPI MiCreatePagingFileMap(OUT PSEGMENT *Segment, @@ -668,6 +821,146 @@ MmCreateArm3Section(OUT PVOID *SectionObject, return Status; } +/* + * @implemented + */ +NTSTATUS +NTAPI +MmMapViewOfArm3Section(IN PVOID SectionObject, + IN PEPROCESS Process, + IN OUT PVOID *BaseAddress, + IN ULONG_PTR ZeroBits, + IN SIZE_T CommitSize, + IN OUT PLARGE_INTEGER SectionOffset OPTIONAL, + IN OUT PSIZE_T ViewSize, + IN SECTION_INHERIT InheritDisposition, + IN ULONG AllocationType, + IN ULONG Protect) +{ + KAPC_STATE ApcState; + BOOLEAN Attached = FALSE; + PSECTION Section; + PCONTROL_AREA ControlArea; + ULONG ProtectionMask; + NTSTATUS Status; + PAGED_CODE(); + + /* Get the segment and control area */ + Section = (PSECTION)SectionObject; + ControlArea = Section->Segment->ControlArea; + + /* These flags/states are not yet supported by ARM3 */ + ASSERT(Section->u.Flags.Image == 0); + ASSERT(Section->u.Flags.NoCache == 0); + ASSERT(Section->u.Flags.WriteCombined == 0); + ASSERT((AllocationType & MEM_RESERVE) == 0); + ASSERT(ControlArea->u.Flags.PhysicalMemory == 0); + + +#if 0 + /* FIXME: Check if the mapping protection is compatible with the create */ + if (!MiIsProtectionCompatible(Section->InitialPageProtection, Protect)) + { + DPRINT1("Mapping protection is incompatible\n"); + return STATUS_SECTION_PROTECTION; + } +#endif + + /* Check if the offset and size would cause an overflow */ + if ((SectionOffset->QuadPart + *ViewSize) < SectionOffset->QuadPart) + { + DPRINT1("Section offset overflows\n"); + return STATUS_INVALID_VIEW_SIZE; + } + + /* Check if the offset and size are bigger than the section itself */ + if ((SectionOffset->QuadPart + *ViewSize) > Section->SizeOfSection.QuadPart) + { + DPRINT1("Section offset is larger than section\n"); + return STATUS_INVALID_VIEW_SIZE; + } + + /* Check if the caller did not specify a view size */ + if (!(*ViewSize)) + { + /* Compute it for the caller */ + *ViewSize = Section->SizeOfSection.QuadPart - SectionOffset->QuadPart; + + /* Check if it's larger than 4GB or overflows into kernel-mode */ + if ((*ViewSize > 0xFFFFFFFF) || + (((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS - (ULONG_PTR)*BaseAddress) < *ViewSize)) + { + DPRINT1("Section view won't fit\n"); + return STATUS_INVALID_VIEW_SIZE; + } + } + + /* Check if the commit size is larger than the view size */ + if (CommitSize > *ViewSize) + { + DPRINT1("Attempting to commit more than the view itself\n"); + return STATUS_INVALID_PARAMETER_5; + } + + /* Check if the view size is larger than the section */ + if (*ViewSize > Section->SizeOfSection.QuadPart) + { + DPRINT1("The view is larger than the section\n"); + return STATUS_INVALID_VIEW_SIZE; + } + + /* Compute and validate the protection mask */ + ProtectionMask = MiMakeProtectionMask(Protect); + if (ProtectionMask == MM_INVALID_PROTECTION) + { + DPRINT1("The protection is invalid\n"); + return STATUS_INVALID_PAGE_PROTECTION; + } + + /* We only handle pagefile-backed sections, which cannot be writecombined */ + if (Protect & PAGE_WRITECOMBINE) + { + DPRINT1("Cannot write combine a pagefile-backed section\n"); + return STATUS_INVALID_PARAMETER_10; + } + + /* Start by attaching to the current process if needed */ + if (PsGetCurrentProcess() != Process) + { + KeStackAttachProcess(&Process->Pcb, &ApcState); + Attached = TRUE; + } + + /* Lock the address space and make sure the process is alive */ + MmLockAddressSpace(&Process->Vm); + if (!Process->VmDeleted) + { + /* Do the actual mapping */ + Status = MiMapViewOfDataSection(ControlArea, + Process, + BaseAddress, + SectionOffset, + ViewSize, + Section, + InheritDisposition, + ProtectionMask, + CommitSize, + ZeroBits, + AllocationType); + } + else + { + /* The process is being terminated, fail */ + DPRINT1("The process is dying\n"); + Status = STATUS_PROCESS_IS_TERMINATING; + } + + /* Unlock the address space and detatch if needed, then return status */ + MmUnlockAddressSpace(&Process->Vm); + if (Attached) KeUnstackDetachProcess(&ApcState); + return Status; +} + /* SYSTEM CALLS ***************************************************************/ NTSTATUS diff --git a/reactos/ntoskrnl/mm/ARM3/vadnode.c b/reactos/ntoskrnl/mm/ARM3/vadnode.c index 1e18287e987..305ebbceeb4 100644 --- a/reactos/ntoskrnl/mm/ARM3/vadnode.c +++ b/reactos/ntoskrnl/mm/ARM3/vadnode.c @@ -121,6 +121,28 @@ MiInsertVad(IN PMMVAD Vad, /* Do the actual insert operation */ MiInsertNode(&Process->VadRoot, (PVOID)Vad, Parent, Result); + + /* Now insert an ARM3 MEMORY_AREA for this node, unless the insert was already from the MEMORY_AREA code */ + if (Vad->u.VadFlags.Spare == 0) + { + NTSTATUS Status; + PMEMORY_AREA MemoryArea; + PHYSICAL_ADDRESS BoundaryAddressMultiple; + SIZE_T Size; + PVOID AllocatedBase = (PVOID)(Vad->StartingVpn << PAGE_SHIFT); + BoundaryAddressMultiple.QuadPart = 0; + Size = ((Vad->EndingVpn + 1) - Vad->StartingVpn) << PAGE_SHIFT; + Status = MmCreateMemoryArea(&Process->Vm, + MEMORY_AREA_OWNED_BY_ARM3, + &AllocatedBase, + Size, + PAGE_READWRITE, + &MemoryArea, + TRUE, + 0, + BoundaryAddressMultiple); + ASSERT(NT_SUCCESS(Status)); + } } VOID @@ -181,6 +203,112 @@ MiGetPreviousNode(IN PMMADDRESS_NODE Node) return NULL; } +PMMADDRESS_NODE +NTAPI +MiGetNextNode(IN PMMADDRESS_NODE Node) +{ + PMMADDRESS_NODE Parent; + + /* Get the right child */ + if (RtlRightChildAvl(Node)) + { + /* Get left-most child */ + Node = RtlRightChildAvl(Node); + while (RtlLeftChildAvl(Node)) Node = RtlLeftChildAvl(Node); + return Node; + } + + Parent = RtlParentAvl(Node); + ASSERT(Parent != NULL); + while (Parent != Node) + { + /* The parent should be a left child, return the real predecessor */ + if (RtlIsLeftChildAvl(Node)) + { + /* Return it */ + return Parent; + } + + /* Keep lopping until we find our parent */ + Node = Parent; + Parent = RtlParentAvl(Node); + } + + /* Nothing found */ + return NULL; +} + +NTSTATUS +NTAPI +MiFindEmptyAddressRangeInTree(IN SIZE_T Length, + IN ULONG_PTR Alignment, + IN PMM_AVL_TABLE Table, + OUT PMMADDRESS_NODE *PreviousVad, + OUT PULONG_PTR Base) +{ + PMMADDRESS_NODE Node; + PMMADDRESS_NODE NextNode; + ULONG_PTR StartingVpn, HighestVpn, AlignmentVpn, LengthVpn, LowVpn; + ASSERT(Length != 0); + + /* Precompute page numbers for the length, alignment, and starting address */ + LengthVpn = (Length + (PAGE_SIZE - 1)) >> PAGE_SHIFT; + AlignmentVpn = Alignment >> PAGE_SHIFT; + StartingVpn = ROUND_DOWN((ULONG_PTR)MM_LOWEST_USER_ADDRESS >> PAGE_SHIFT, + AlignmentVpn); + + /* Check if the table is free, so the lowest possible address is available */ + if (!Table->NumberGenericTableElements) goto FoundAtBottom; + + /* Otherwise, follow the leftmost child of the right root node's child */ + Node = RtlRightChildAvl(&Table->BalancedRoot); + while (RtlLeftChildAvl(Node)) Node = RtlLeftChildAvl(Node); + + /* This is the node for the remaining gap at the bottom, can it be used? */ + if ((Node->StartingVpn > StartingVpn) && + (LengthVpn < Node->StartingVpn - StartingVpn)) + { +FoundAtBottom: + /* Use this VAD to store the allocation */ + *PreviousVad = NULL; + *Base = StartingVpn << PAGE_SHIFT; + return STATUS_SUCCESS; + } + + /* Otherwise, we start a search to find a gap */ + while (TRUE) + { + /* The last aligned page number in this entry */ + LowVpn = ROUND_DOWN(Node->EndingVpn + 1, AlignmentVpn); + + /* Keep going as long as there's still a next node */ + NextNode = MiGetNextNode(Node); + if (!NextNode) break; + + /* Can this allocation fit in this node? */ + if ((LengthVpn <= (NextNode->StartingVpn - LowVpn)) && + (NextNode->StartingVpn > LowVpn)) + { +Found: + /* Yes! Use this VAD to store the allocation */ + *PreviousVad = Node; + *Base = ROUND_DOWN((Node->EndingVpn << PAGE_SHIFT) | (PAGE_SIZE - 1), + Alignment); + return STATUS_SUCCESS; + } + + /* Try the next node */ + Node = NextNode; + } + + /* We're down to the last (top) VAD, will this allocation fit inside it? */ + HighestVpn = ((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1) >> PAGE_SHIFT; + if ((HighestVpn > LowVpn) && (LengthVpn <= HighestVpn - LowVpn)) goto Found; + + /* Nyet, there's no free address space for this allocation, so we'll fail */ + return STATUS_NO_MEMORY; +} + TABLE_SEARCH_RESULT NTAPI MiFindEmptyAddressRangeDownTree(IN SIZE_T Length, diff --git a/reactos/ntoskrnl/mm/section.c b/reactos/ntoskrnl/mm/section.c index a405a6ab8eb..f5bc663fdb5 100644 --- a/reactos/ntoskrnl/mm/section.c +++ b/reactos/ntoskrnl/mm/section.c @@ -3956,7 +3956,19 @@ NtQuerySection(IN HANDLE SectionHandle, - +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); + /********************************************************************** * NAME EXPORTED * MmMapViewOfSection @@ -4023,6 +4035,20 @@ MmMapViewOfSection(IN PVOID SectionObject, ULONG ViewOffset; NTSTATUS Status = STATUS_SUCCESS; + if ((ULONG_PTR)SectionObject & 1) + { + return MmMapViewOfArm3Section((PVOID)((ULONG_PTR)SectionObject & ~1), + Process, + BaseAddress, + ZeroBits, + CommitSize, + SectionOffset, + ViewSize, + InheritDisposition, + AllocationType, + Protect); + } + ASSERT(Process); if (!Protect || Protect & ~PAGE_FLAGS_VALID_FOR_SECTION) @@ -4280,10 +4306,10 @@ MmMapViewInSystemSpace (IN PVOID SectionObject, PROS_SECTION_OBJECT Section; PMMSUPPORT AddressSpace; NTSTATUS Status; - + PAGED_CODE(); + if ((ULONG_PTR)SectionObject & 1) { - PAGED_CODE(); extern PVOID MmSession; return MiMapViewInSystemSpace((PVOID)((ULONG_PTR)SectionObject & ~1), &MmSession,