[NTOS:MM]
[reactos.git] / reactos / ntoskrnl / mm / ARM3 / section.c
index 0e7083e..a71dcd7 100644 (file)
@@ -1121,7 +1121,7 @@ MiMapViewInSystemSpace(IN PVOID Section,
         Status = MiSessionCommitPageTables(Base,
                                            (PVOID)((ULONG_PTR)Base +
                                            Buckets * MI_SYSTEM_VIEW_BUCKET_SIZE));
-        NT_ASSERT(NT_SUCCESS(Status));
+        ASSERT(NT_SUCCESS(Status));
     }
 
     /* Create the actual prototype PTEs for this mapping */
@@ -1269,20 +1269,25 @@ MiMapViewOfDataSection(IN PCONTROL_AREA ControlArea,
     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;
     }
 
-    /* This flag determines alignment, but ARM3 does not yet support it */
-    ASSERT((AllocationType & MEM_DOS_LIM) == 0);
-
     /* First, increase the map count. No purging is supported yet */
     Status = MiCheckPurgeAndUpMapCount(ControlArea, FALSE);
     if (!NT_SUCCESS(Status)) return Status;
@@ -1432,7 +1437,7 @@ MiMapViewOfDataSection(IN PCONTROL_AREA ControlArea,
     if (*BaseAddress != NULL)
     {
         /* Just align what the caller gave us */
-        StartAddress = ROUND_UP((ULONG_PTR)*BaseAddress, _64K);
+        StartAddress = ALIGN_DOWN_BY((ULONG_PTR)*BaseAddress, Granularity);
     }
     else if (Section->Address.StartingVpn != 0)
     {
@@ -1449,7 +1454,7 @@ MiMapViewOfDataSection(IN PCONTROL_AREA ControlArea,
                            &StartAddress,
                            ViewSizeInPages * PAGE_SIZE,
                            MAXULONG_PTR >> ZeroBits,
-                           MM_VIRTMEM_GRANULARITY,
+                           Granularity,
                            AllocationType);
     if (!NT_SUCCESS(Status))
     {
@@ -1559,6 +1564,9 @@ MiCreatePagingFileMap(OUT PSEGMENT *Segment,
     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;
@@ -1948,7 +1956,7 @@ MiFlushTbAndCapture(IN PMMVAD FoundVad,
                     IN PMMPTE PointerPte,
                     IN ULONG ProtectionMask,
                     IN PMMPFN Pfn1,
-                    IN BOOLEAN CaptureDirtyBit)
+                    IN BOOLEAN UpdateDirty)
 {
     MMPTE TempPte, PreviousPte;
     KIRQL OldIrql;
@@ -2024,7 +2032,13 @@ MiFlushTbAndCapture(IN PMMVAD FoundVad,
     //
     // Windows updates the relevant PFN1 information, we currently don't.
     //
-    if (CaptureDirtyBit) DPRINT1("Warning, not handling dirty bit\n");
+    if (UpdateDirty && PreviousPte.u.Hard.Dirty)
+    {
+        if (!Pfn1->u3.e1.Modified)
+        {
+            DPRINT1("FIXME: Mark PFN as dirty\n");
+        }
+    }
 
     //
     // Not supported in ARM3
@@ -2411,6 +2425,7 @@ MmCreateArm3Section(OUT PVOID *SectionObject,
     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 */
@@ -2508,6 +2523,7 @@ MmCreateArm3Section(OUT PVOID *SectionObject,
 
         /* 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;
 
@@ -2527,8 +2543,47 @@ MmCreateArm3Section(OUT PVOID *SectionObject,
                                      SectionPageProtection,
                                      AllocationAttributes,
                                      KernelCall);
+        if (!NT_SUCCESS(Status))
+        {
+            /* Lock the PFN database while we play with the section pointers */
+            OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+
+            /* 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 */
+            KeReleaseQueuedSpinLock(LockQueuePfnLock, 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);
-        ASSERT(NT_SUCCESS(Status));
 
         /* Check if a maximum size was specified */
         if (!InputMaximumSize->QuadPart)
@@ -2560,6 +2615,9 @@ MmCreateArm3Section(OUT PVOID *SectionObject,
         /* 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? */
@@ -2604,16 +2662,16 @@ MmCreateArm3Section(OUT PVOID *SectionObject,
     /* 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 == 0) &&
-        (ControlArea->FilePointer != NULL))
+        !(ControlArea->u.Flags.Image) &&
+        (ControlArea->FilePointer))
     {
         /* Add a reference and set the flag */
-        Section.u.Flags.UserWritable = 1;
-        InterlockedIncrement((PLONG)&ControlArea->WritableUserReferences);
+        Section.u.Flags.UserWritable = TRUE;
+        InterlockedIncrement((volatile LONG*)&ControlArea->WritableUserReferences);
     }
 
     /* Check for image mappings or page file mappings */
-    if ((ControlArea->u.Flags.Image == 1) || !(ControlArea->FilePointer))
+    if ((ControlArea->u.Flags.Image) || !(ControlArea->FilePointer))
     {
         /* Charge the segment size, and allocate a subsection */
         PagedCharge = sizeof(SECTION) + NewSegment->TotalNumberOfPtes * sizeof(MMPTE);
@@ -2648,7 +2706,39 @@ MmCreateArm3Section(OUT PVOID *SectionObject,
                             PagedCharge,
                             NonPagedCharge,
                             (PVOID*)&NewSection);
-    ASSERT(NT_SUCCESS(Status));
+    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 = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+
+            /* 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));
@@ -2658,49 +2748,69 @@ MmCreateArm3Section(OUT PVOID *SectionObject,
     ASSERT(KernelCall == FALSE);
     NewSection->u.Flags.UserReference = TRUE;
 
-    /* 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;
-    }
-
     /* Is this a "based" allocation, in which all mappings are identical? */
     if (AllocationAttributes & SEC_BASED)
     {
-        /* Convert the flag, and make sure the section isn't too big */
-        NewSection->u.Flags.Based = TRUE;
-        if ((ULONGLONG)NewSection->SizeOfSection.QuadPart >
-            (ULONG_PTR)MmHighSectionBase)
-        {
-            DPRINT1("BASED section is too large\n");
-            ObDereferenceObject(NewSection);
-            return STATUS_NO_MEMORY;
-        }
-
         /* Lock the VAD tree during the search */
         KeAcquireGuardedMutex(&MmSectionBasedMutex);
 
-        /* Find an address top-down */
-        Status = MiFindEmptyAddressRangeDownBasedTree(NewSection->SizeOfSection.LowPart,
-                                                      (ULONG_PTR)MmHighSectionBase,
-                                                      _64K,
-                                                      &MmSectionBasedRoot,
-                                                      &NewSection->Address.StartingVpn);
-        ASSERT(NT_SUCCESS(Status));
+        /* 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);
 
-        /* Compute the ending address and insert it into the VAD tree */
-        NewSection->Address.EndingVpn = NewSection->Address.StartingVpn +
-                                        NewSection->SizeOfSection.LowPart -
-                                        1;
-        MiInsertBasedSection(NewSection);
+            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);
+        }
 
-        /* Finally release the lock */
         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 = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+
+        /* Take off the being created flag, and then release the lock */
+        ControlArea->u.Flags.BeingCreated = 0;
+        NewSection->u.Flags.BeingCreated = 0;
+
+        KeReleaseQueuedSpinLock(LockQueuePfnLock, 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);
@@ -2841,7 +2951,7 @@ MmMapViewOfArm3Section(IN PVOID SectionObject,
                                     ZeroBits,
                                     AllocationType);
 
-    /* Detatch if needed, then return status */
+    /* Detach if needed, then return status */
     if (Attached) KeUnstackDetachProcess(&ApcState);
     return Status;
 }
@@ -3137,6 +3247,14 @@ MiDeleteARM3Section(PVOID ObjectBody)
 
     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 = KeAcquireQueuedSpinLock(LockQueuePfnLock);
 
@@ -3155,6 +3273,14 @@ MiDeleteARM3Section(PVOID ObjectBody)
     MiCheckControlArea(ControlArea, OldIrql);
 }
 
+ULONG
+NTAPI
+MmDoesFileHaveUserWritableReferences(IN PSECTION_OBJECT_POINTERS SectionPointer)
+{
+    UNIMPLEMENTED;
+    return 0;
+}
+
 /* SYSTEM CALLS ***************************************************************/
 
 NTSTATUS
@@ -3439,13 +3565,13 @@ NtMapViewOfSection(IN HANDLE SectionHandle,
     ACCESS_MASK DesiredAccess;
     ULONG ProtectionMask;
     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
-
-    /* Check for invalid zero bits */
-    if (ZeroBits > 21) // per-arch?
-    {
-        DPRINT1("Invalid zero bits\n");
-        return STATUS_INVALID_PARAMETER_4;
-    }
+#ifdef _M_IX86
+    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))
@@ -3455,8 +3581,7 @@ NtMapViewOfSection(IN HANDLE SectionHandle,
     }
 
     /* Allow only valid allocation types */
-    if ((AllocationType & ~(MEM_TOP_DOWN | MEM_LARGE_PAGES | MEM_DOS_LIM |
-                            SEC_NO_CHANGE | MEM_RESERVE)))
+    if (AllocationType & ~ValidAllocationType)
     {
         DPRINT1("Invalid allocation type\n");
         return STATUS_INVALID_PARAMETER_9;
@@ -3470,13 +3595,6 @@ NtMapViewOfSection(IN HANDLE SectionHandle,
         return STATUS_INVALID_PAGE_PROTECTION;
     }
 
-    /* Check for non-allocation-granularity-aligned BaseAddress */
-    if (BaseAddress && (*BaseAddress != ALIGN_DOWN_POINTER_BY(*BaseAddress, MM_VIRTMEM_GRANULARITY)))
-    {
-       DPRINT("BaseAddress is not at 64-kilobyte address boundary.");
-       return STATUS_MAPPED_ALIGNMENT;
-    }
-
     /* Now convert the protection mask into desired section access mask */
     DesiredAccess = MmMakeSectionAccess[ProtectionMask & 0x7];
 
@@ -3528,10 +3646,25 @@ NtMapViewOfSection(IN HANDLE SectionHandle,
     }
 
     /* Check for invalid zero bits */
-    if (((ULONG_PTR)SafeBaseAddress + SafeViewSize) > (0xFFFFFFFF >> ZeroBits)) // arch?
+    if (ZeroBits)
     {
-        DPRINT1("Invalid zero bits\n");
-        return STATUS_INVALID_PARAMETER_4;
+        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 */
@@ -3556,6 +3689,39 @@ NtMapViewOfSection(IN HANDLE SectionHandle,
         return Status;
     }
 
+    if (MiIsRosSectionObject(Section) &&
+        (Section->AllocationAttributes & SEC_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,
@@ -3572,7 +3738,8 @@ NtMapViewOfSection(IN HANDLE SectionHandle,
     if (NT_SUCCESS(Status))
     {
         /* Check if this is an image for the current process */
-        if ((Section->AllocationAttributes & SEC_IMAGE) &&
+        if (MiIsRosSectionObject(Section) &&
+            (Section->AllocationAttributes & SEC_IMAGE) &&
             (Process == PsGetCurrentProcess()) &&
             (Status != STATUS_IMAGE_NOT_AT_BASE))
         {