[NTOSKRNL/MM]
authorJérôme Gardou <jerome.gardou@reactos.org>
Thu, 16 Feb 2012 17:24:15 +0000 (17:24 +0000)
committerJérôme Gardou <jerome.gardou@reactos.org>
Thu, 16 Feb 2012 17:24:15 +0000 (17:24 +0000)
  - First drop of a new implementation of NtAllocateVirtualMemory.
Very incomplete, don't hesitate to drop in and complete it.
For now, only MEM_RESERVE is kind of supported, as well as MEM_RESERVE|MEM_COMMIT

svn path=/trunk/; revision=55645

reactos/ntoskrnl/mm/ARM3/virtual.c
reactos/ntoskrnl/mm/anonmem.c

index 79223a0..3383704 100644 (file)
@@ -2773,4 +2773,359 @@ NtQueryVirtualMemory(IN HANDLE ProcessHandle,
     return Status;
 }
 
+#ifdef __USE_ARM3__
+/*
+* @implemented
+*/
+NTSTATUS
+NTAPI
+NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
+    IN OUT PVOID* UBaseAddress,
+    IN ULONG_PTR ZeroBits,
+    IN OUT PSIZE_T URegionSize,
+    IN ULONG AllocationType,
+    IN ULONG Protect)
+{
+    PEPROCESS Process;
+    ULONG Type;
+    NTSTATUS Status = STATUS_SUCCESS;
+    PVOID BaseAddress;
+    ULONG RegionSize;
+    PMMVAD Vad;
+    PMMADDRESS_NODE ParentNode;
+    ULONG_PTR StartVpn, EndVpn;
+    PHYSICAL_ADDRESS BoundaryAddressMultiple;
+    PEPROCESS CurrentProcess = PsGetCurrentProcess();
+    KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
+    KAPC_STATE ApcState;
+    ULONG ProtectionMask;
+    BOOLEAN Attached = FALSE;
+    BoundaryAddressMultiple.QuadPart = 0;
+    TABLE_SEARCH_RESULT Result;
+    
+    PAGED_CODE();
+
+    /* Check for valid Zero bits */
+    if (ZeroBits > 21)
+    {
+        DPRINT1("Too many zero bits\n");
+        return STATUS_INVALID_PARAMETER_3;
+    }
+
+    /* Check for valid Allocation Types */
+    if ((AllocationType & ~(MEM_COMMIT | MEM_RESERVE | MEM_RESET | MEM_PHYSICAL |
+                    MEM_TOP_DOWN | MEM_WRITE_WATCH)))
+    {
+        DPRINT1("Invalid Allocation Type\n");
+        return STATUS_INVALID_PARAMETER_5;
+    }
+
+    /* Check for at least one of these Allocation Types to be set */
+    if (!(AllocationType & (MEM_COMMIT | MEM_RESERVE | MEM_RESET)))
+    {
+        DPRINT1("No memory allocation base type\n");
+        return STATUS_INVALID_PARAMETER_5;
+    }
+
+    /* MEM_RESET is an exclusive flag, make sure that is valid too */
+    if ((AllocationType & MEM_RESET) && (AllocationType != MEM_RESET))
+    {
+        DPRINT1("Invalid use of MEM_RESET\n");
+        return STATUS_INVALID_PARAMETER_5;
+    }
+
+    /* Check if large pages are being used */
+    if (AllocationType & MEM_LARGE_PAGES)
+    {
+        /* Large page allocations MUST be committed */
+        if (!(AllocationType & MEM_COMMIT))
+        {
+            DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n");
+            return STATUS_INVALID_PARAMETER_5;
+        }
+
+        /* These flags are not allowed with large page allocations */
+        if (AllocationType & (MEM_PHYSICAL | MEM_RESET | MEM_WRITE_WATCH))
+        {
+            DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n");
+            return STATUS_INVALID_PARAMETER_5;
+        }
+    }
+
+    /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
+    if ((AllocationType & MEM_WRITE_WATCH) && !(AllocationType & MEM_RESERVE))
+    {
+        DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
+        return STATUS_INVALID_PARAMETER_5;
+    }
+
+    /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
+    if ((AllocationType & MEM_PHYSICAL) && !(AllocationType & MEM_RESERVE))
+    {
+        DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
+        return STATUS_INVALID_PARAMETER_5;
+    }
+
+    /* Check for valid MEM_PHYSICAL usage */
+    if (AllocationType & MEM_PHYSICAL)
+    {
+        /* Only these flags are allowed with MEM_PHYSIAL */
+        if (AllocationType & ~(MEM_RESERVE | MEM_TOP_DOWN | MEM_PHYSICAL))
+        {
+            DPRINT1("Using illegal flags with MEM_PHYSICAL\n");
+            return STATUS_INVALID_PARAMETER_5;
+        }
+
+        /* Then make sure PAGE_READWRITE is used */
+        if (Protect != PAGE_READWRITE)
+        {
+            DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
+            return STATUS_INVALID_PARAMETER_6;
+        }
+    }
+
+    /* Calculate the protection mask and make sure it's valid */
+    ProtectionMask = MiMakeProtectionMask(Protect);
+    if (ProtectionMask == MM_INVALID_PROTECTION)
+    {
+        DPRINT1("Invalid protection mask\n");
+        return STATUS_INVALID_PAGE_PROTECTION;
+    }
+
+    /* Enter SEH */
+    _SEH2_TRY
+    {
+        /* Check for user-mode parameters */
+        if (PreviousMode != KernelMode)
+        {
+            /* Make sure they are writable */
+            ProbeForWritePointer(UBaseAddress);
+            ProbeForWriteUlong(URegionSize);
+        }
+
+        /* Capture their values */
+        BaseAddress = *UBaseAddress;
+        RegionSize = *URegionSize;
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        /* Return the exception code */
+        _SEH2_YIELD(return _SEH2_GetExceptionCode());
+    }
+    _SEH2_END;
+    
+    /* Make sure there's a size specified */
+    if (!RegionSize)
+    {
+        DPRINT1("Region size is invalid (zero)\n");
+        return STATUS_INVALID_PARAMETER_4;
+    }
+    
+    RegionSize = PAGE_ROUND_UP((ULONG_PTR)BaseAddress + RegionSize) -
+        PAGE_ROUND_DOWN(BaseAddress);
+    BaseAddress = (PVOID)PAGE_ROUND_DOWN(BaseAddress);
+    StartVpn = (ULONG_PTR)BaseAddress >> PAGE_SHIFT;
+    EndVpn = ((ULONG_PTR)BaseAddress + RegionSize - 1) >> PAGE_SHIFT;
+
+    /* Make sure the allocation isn't past the VAD area */
+    if (BaseAddress >= MM_HIGHEST_VAD_ADDRESS)
+    {
+        DPRINT1("Virtual allocation base above User Space\n");
+        return STATUS_INVALID_PARAMETER_2;
+    }
+
+    /* Make sure the allocation wouldn't overflow past the VAD area */
+    if ((((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1) - (ULONG_PTR)BaseAddress) < RegionSize)
+    {
+        DPRINT1("Region size would overflow into kernel-memory\n");
+        return STATUS_INVALID_PARAMETER_4;
+    }
+
+    /* Check if this is for the current process */
+    if (ProcessHandle == NtCurrentProcess())
+    {
+        /* We already have the current process, no need to go through Ob */
+        Process = CurrentProcess;
+    }
+    else
+    {
+        /* Reference the handle for correct permissions */
+        Status = ObReferenceObjectByHandle(ProcessHandle,
+            PROCESS_VM_OPERATION,
+            PsProcessType,
+            PreviousMode,
+            (PVOID*)&Process,
+            NULL);
+        if (!NT_SUCCESS(Status)) return Status;
+
+        /* Check if not running in the current process */
+        if (CurrentProcess != Process)
+        {
+            /* Attach to it */
+            KeStackAttachProcess(&Process->Pcb, &ApcState);
+            Attached = TRUE;
+        }
+    }
+
+    /* Check for large page allocations */
+    if (AllocationType & MEM_LARGE_PAGES)
+    {
+        /* The lock memory privilege is required */
+        if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode))
+        {
+            /* Fail without it */
+            DPRINT1("Privilege not held for MEM_LARGE_PAGES\n");
+            if (Attached) KeUnstackDetachProcess(&ApcState);
+            if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
+            return STATUS_PRIVILEGE_NOT_HELD;
+        }
+    }
+
+
+    /*
+    * Copy on Write is reserved for system use. This case is a certain failure
+    * but there may be other cases...needs more testing
+    */
+    if ((!BaseAddress || (AllocationType & MEM_RESERVE)) &&
+            (Protect & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY)))
+    {
+        DPRINT1("Copy on write is not supported by VirtualAlloc\n");
+        if (Attached) KeUnstackDetachProcess(&ApcState);
+        if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
+        return STATUS_INVALID_PAGE_PROTECTION;
+    }
+
+    Type = (AllocationType & MEM_COMMIT) ? MEM_COMMIT : MEM_RESERVE;
+    DPRINT("Type %x\n", Type);
+
+    /* Lock the process address space */
+    KeAcquireGuardedMutex(&Process->AddressCreationLock);
+    
+    if(BaseAddress != 0)
+    {
+        /* 
+         * An address was provided. Let's see if we've already
+         * something there 
+         */
+        if(MiCheckForConflictingNode(StartVpn, EndVpn, &Process->VadRoot) != NULL)
+        {
+            /* Can't reserve twice the same range */
+            if(AllocationType & MEM_RESERVE)
+            {
+                Status = STATUS_CONFLICTING_ADDRESSES;
+                DPRINT1("Trying to reserve twice the same range.\n");
+                goto cleanup;
+            }
+            /* Great there's already something there. What shall we do ? */
+            if(AllocationType == MEM_RESET)
+            {
+                UNIMPLEMENTED;
+                /* Reset the dirty bits for each PTEs */
+                goto cleanup;
+            }
+            else
+            {
+                ASSERT(AllocationType & MEM_COMMIT);
+                UNIMPLEMENTED;
+                /* Mark the VAD as committed */
+                goto cleanup;
+            }
+        }
+        
+        /* There's nothing */
+        if(!(AllocationType & MEM_RESERVE))
+        {
+            Status = STATUS_ACCESS_DENIED;
+            goto cleanup;
+        }
+        
+        /* Now we can reserve our chunk of memory */
+        goto buildVad;
+    }
+    
+    /* No base address was given. */
+    if(!(AllocationType & MEM_RESERVE))
+    {
+        DPRINT1("Providing NULL base address witout MEM_RESERVE.\n");
+        ASSERT(FALSE);
+        Status = STATUS_INVALID_PARAMETER_5;
+        goto cleanup;
+    }
+    
+    /* Find an empty range in Address Space */
+    if(AllocationType & MEM_TOP_DOWN)
+    {
+        /* Top down allocation */
+        Result = MiFindEmptyAddressRangeDownTree(RegionSize,
+            (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS,
+            (ZeroBits > PAGE_SHIFT) ? 1 << ZeroBits : PAGE_SIZE,
+            &Process->VadRoot,
+            (PULONG_PTR)&BaseAddress,
+            &ParentNode);
+        
+        if(Result == TableFoundNode)
+        {
+            /* This means failure */
+            Status = STATUS_NO_MEMORY;
+            goto cleanup;
+        }
+    }
+    else
+    {
+        /* Good old bottom up allocation */
+        Status = MiFindEmptyAddressRangeInTree(RegionSize,
+            (ZeroBits > PAGE_SHIFT) ? 1 << ZeroBits : PAGE_SIZE,
+            &Process->VadRoot,
+            &ParentNode,
+            (PULONG_PTR)&BaseAddress);
+        if(!NT_SUCCESS(Status))
+        {
+            /* Failed... */
+            goto cleanup;
+        }
+    }
+    StartVpn = (ULONG_PTR)BaseAddress >> PAGE_SHIFT;
+    EndVpn = ((ULONG_PTR)BaseAddress + RegionSize - 1) >> PAGE_SHIFT;
+    
+    /* Build the Vad */
+buildVad:
+    Vad = ExAllocatePoolWithTag(NonPagedPool, sizeof(MMVAD), TAG_MVAD);
+    if(!Vad)
+    {
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto cleanup;
+    }
+    RtlZeroMemory(Vad, sizeof(MMVAD));
+    
+    /* Set min/max */
+    Vad->StartingVpn = StartVpn;
+    Vad->EndingVpn = EndVpn;
+    /* Set protection */
+    Vad->u.VadFlags.Protection = ProtectionMask;
+    /* Should it be already marked as committed ? */
+    if(AllocationType & MEM_COMMIT)
+        Vad->u.VadFlags.MemCommit = 1;
+    if(AllocationType & MEM_PHYSICAL)
+    {
+        UNIMPLEMENTED;
+        Vad->u.VadFlags.VadType = VadAwe;
+    }
+    /* Add it */
+    MiLockProcessWorkingSet(Process, PsGetCurrentThread());
+    MiInsertVad(Vad, Process);
+    MiUnlockProcessWorkingSet(Process, PsGetCurrentThread());
+
+    /* we're done */
+cleanup:
+    KeReleaseGuardedMutex(&Process->AddressCreationLock);
+    if (Attached) KeUnstackDetachProcess(&ApcState);
+    if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
+
+    *UBaseAddress = BaseAddress;
+    *URegionSize = RegionSize;
+    DPRINT("*UBaseAddress %x  *URegionSize %x\n", BaseAddress, RegionSize);
+
+    return Status;
+}
+#endif
 /* EOF */
index ce73607..4373dd6 100644 (file)
@@ -528,6 +528,7 @@ MiProtectVirtualMemory(IN PEPROCESS Process,
     return Status;
 }
 
+#ifndef __USE_ARM3__
 /*
 * @implemented
 */
@@ -900,6 +901,7 @@ NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
 
     return(STATUS_SUCCESS);
 }
+#endif // __USE_ARM3__
 
 static VOID
 MmFreeVirtualMemoryPage(PVOID Context,