Esempio n. 1
0
VOID
FASTCALL
MiDecrementShareCount (
    IN PMMPFN Pfn1,
    IN PFN_NUMBER PageFrameIndex
    )

/*++

Routine Description:

    This routine decrements the share count within the PFN element
    for the specified physical page.  If the share count becomes
    zero the corresponding PTE is converted to the transition state
    and the reference count is decremented and the ValidPte count
    of the PTEframe is decremented.

Arguments:

    Pfn1 - Supplies the PFN database entry to decrement.

    PageFrameIndex - Supplies the physical page number of which to decrement
                     the share count.

Return Value:

    None.

Environment:

    Must be holding the PFN database lock with APCs disabled.

--*/

{
    ULONG FreeBit;
    MMPTE TempPte;
    PMMPTE PointerPte;
    PEPROCESS Process;

    ASSERT (PageFrameIndex > 0);
    ASSERT (MI_IS_PFN (PageFrameIndex));
    ASSERT (Pfn1 == MI_PFN_ELEMENT (PageFrameIndex));

    if (Pfn1->u3.e1.PageLocation != ActiveAndValid &&
        Pfn1->u3.e1.PageLocation != StandbyPageList) {
            KeBugCheckEx (PFN_LIST_CORRUPT,
                      0x99,
                      PageFrameIndex,
                      Pfn1->u3.e1.PageLocation,
                      0);
    }

    Pfn1->u2.ShareCount -= 1;

    ASSERT (Pfn1->u2.ShareCount < 0xF000000);

    if (Pfn1->u2.ShareCount == 0) {

        if (PERFINFO_IS_GROUP_ON(PERF_MEMORY)) {
            PERFINFO_PFN_INFORMATION PerfInfoPfn;

            PerfInfoPfn.PageFrameIndex = PageFrameIndex;
            PerfInfoLogBytes(PERFINFO_LOG_TYPE_ZEROSHARECOUNT, 
                             &PerfInfoPfn, 
                             sizeof(PerfInfoPfn));
        }

        //
        // The share count is now zero, decrement the reference count
        // for the PFN element and turn the referenced PTE into
        // the transition state if it refers to a prototype PTE.
        // PTEs which are not prototype PTEs do not need to be placed
        // into transition as they are placed in transition when
        // they are removed from the working set (working set free routine).
        //

        //
        // If the PTE referenced by this PFN element is actually
        // a prototype PTE, it must be mapped into hyperspace and
        // then operated on.
        //

        if (Pfn1->u3.e1.PrototypePte == 1) {

            if (MiIsProtoAddressValid (Pfn1->PteAddress)) {
                Process = NULL;
                PointerPte = Pfn1->PteAddress;
            }
            else {

                //
                // The address is not valid in this process, map it into
                // hyperspace so it can be operated upon.
                //

                Process = PsGetCurrentProcess ();
                PointerPte = (PMMPTE) MiMapPageInHyperSpaceAtDpc(Process, Pfn1->u4.PteFrame);
                PointerPte = (PMMPTE)((PCHAR)PointerPte +
                                        MiGetByteOffset(Pfn1->PteAddress));
            }

            TempPte = *PointerPte;

            MI_MAKE_VALID_PTE_TRANSITION (TempPte,
                                          Pfn1->OriginalPte.u.Soft.Protection);
            MI_WRITE_INVALID_PTE (PointerPte, TempPte);

            if (Process != NULL) {
                MiUnmapPageInHyperSpaceFromDpc (Process, PointerPte);
            }

            //
            // There is no need to flush the translation buffer at this
            // time as we only invalidated a prototype PTE.
            //
        }

        //
        // Change the page location to inactive (from active and valid).
        //

        Pfn1->u3.e1.PageLocation = TransitionPage;

        //
        // Decrement the reference count as the share count is now zero.
        //

        MM_PFN_LOCK_ASSERT();

        ASSERT (Pfn1->u3.e2.ReferenceCount != 0);

        if (Pfn1->u3.e2.ReferenceCount == 1) {

            if (MI_IS_PFN_DELETED (Pfn1)) {

                Pfn1->u3.e2.ReferenceCount = 0;

                //
                // There is no referenced PTE for this page, delete the page
                // file space (if any), and place the page on the free list.
                //

                ASSERT (Pfn1->OriginalPte.u.Soft.Prototype == 0);

                FreeBit = GET_PAGING_FILE_OFFSET (Pfn1->OriginalPte);

                if ((FreeBit != 0) && (FreeBit != MI_PTE_LOOKUP_NEEDED)) {
                    MiReleaseConfirmedPageFileSpace (Pfn1->OriginalPte);
                }

                //
                // Temporarily mark the frame as active and valid so that
                // MiIdentifyPfn knows it is safe to walk back through the
                // containing frames for a more accurate identification.
                // Note the page will be immediately re-marked as it is
                // inserted into the freelist.
                //

                Pfn1->u3.e1.PageLocation = ActiveAndValid;

                MiInsertPageInFreeList (PageFrameIndex);
            }
            else {
                MiDecrementReferenceCount (Pfn1, PageFrameIndex);
            }
        }
        else {
            InterlockedDecrementPfn ((PSHORT)&Pfn1->u3.e2.ReferenceCount);
        }
    }

    return;
}
Esempio n. 2
0
VOID
MiUpdateSystemPdes (
    IN PEPROCESS Process
    )

/*++

Routine Description:

    This routine updates the system PDEs, typically due to a large page
    system PTE mapping being created or destroyed.  This is rare.

    Note this is only needed for 32-bit platforms (64-bit platforms share
    a common top level system page).

Arguments:

    Process - Supplies a pointer to the process to update.

Return Value:

    None.

Environment:

    Kernel mode, expansion lock held.

    The caller acquired the expansion lock prior to clearing the update
    bit from this process.  We must update the PDEs prior to releasing
    it so that any new updates can also be rippled.

--*/

{
    ULONG i;
    ULONG PdeOffset;
    ULONG PdeEndOffset;
    MMPTE TempPte;
    PFN_NUMBER PageDirectoryIndex;
    PFN_NUMBER TargetPageDirectoryIndex;
    PEPROCESS CurrentProcess;
    PMMPTE PointerPte;
    PMMPTE PointerPde;
    PMMPTE TargetPdePage;
    PMMPTE TargetAddressSpacePde;

    ASSERT (KeGetCurrentIrql () == DISPATCH_LEVEL);

    CurrentProcess = PsGetCurrentProcess ();

    //
    // Map the page directory page in hyperspace.
    // Note for PAE, this is the high 1GB virtual only.
    //

    ASSERT (Process->Pcb.DirectoryTableBase[0] != 0);
    TargetPageDirectoryIndex = Process->Pcb.DirectoryTableBase[0] >> PAGE_SHIFT;

    ASSERT (PsInitialSystemProcess != NULL);
    ASSERT (PsInitialSystemProcess->Pcb.DirectoryTableBase[0] != 0);
    PageDirectoryIndex = PsInitialSystemProcess->Pcb.DirectoryTableBase[0] >> PAGE_SHIFT;

    TempPte = ValidKernelPte;
    TempPte.u.Hard.PageFrameNumber = TargetPageDirectoryIndex;
    ASSERT (MiLargePageHyperPte->u.Long == 0);
    MI_WRITE_VALID_PTE (MiLargePageHyperPte, TempPte);
    TargetPdePage = MiGetVirtualAddressMappedByPte (MiLargePageHyperPte);

    //
    // Map the system process page directory as we know that's always kept
    // up to date.
    //

    PointerPte = MiMapPageInHyperSpaceAtDpc (CurrentProcess,
                                             PageDirectoryIndex);

    //
    // Copy all system PTE ranges.
    //

    for (i = 0; i < MiPteRangeIndex; i += 1) {

        PdeOffset = MiGetPdeOffset (MiPteRanges[i].StartingVa);
        PdeEndOffset = MiGetPdeOffset (MiPteRanges[i].EndingVa);

        PointerPde = &PointerPte[PdeOffset];
        TargetAddressSpacePde = &TargetPdePage[PdeOffset];

        RtlCopyMemory (TargetAddressSpacePde,
                       PointerPde,
                       (PdeEndOffset - PdeOffset + 1) * sizeof (MMPTE));

    }

    MiUnmapPageInHyperSpaceFromDpc (CurrentProcess, PointerPte);

    //
    // Just invalidate the mapping on the current processor as we cannot
    // have context switched.
    //

    MI_WRITE_ZERO_PTE (MiLargePageHyperPte);
    MI_FLUSH_CURRENT_TB_SINGLE (TargetPdePage);

    return;
}
Esempio n. 3
0
VOID
MiUpdateSystemPdes (
    IN PEPROCESS Process
    )

/*++

Routine Description:

    This routine updates the system PDEs, typically due to a large page
    system PTE mapping being created or destroyed.  This is rare.

    Note this is only needed for 32-bit platforms (64-bit platforms share
    a common top level system page).

Arguments:

    Process - Supplies a pointer to the process to update.

Return Value:

    None.

Environment:

    Kernel mode, expansion lock held.

    The caller acquired the expansion lock prior to clearing the update
    bit from this process.  We must update the PDEs prior to releasing
    it so that any new updates can also be rippled.

--*/

{
    ULONG PdeOffset;
    ULONG PdeEndOffset;
    LOGICAL LowPtes;
    PVOID VirtualAddress;
    MMPTE TempPte;
    PFN_NUMBER PageDirectoryIndex;
    PFN_NUMBER TargetPageDirectoryIndex;
    PEPROCESS CurrentProcess;
    PMMPTE PointerPte;
    PMMPTE PointerPde;
    PMMPTE TargetPdePage;
    PMMPTE TargetAddressSpacePde;
    PMMPTE PaeTop;
    ULONG i;

    ASSERT (KeGetCurrentIrql () == DISPATCH_LEVEL);

    CurrentProcess = PsGetCurrentProcess ();

    //
    // Map the page directory page in hyperspace.
    // Note for PAE, this is the high 1GB virtual only.
    //

    PaeTop = Process->PaeTop;
    ASSERT (PaeTop != NULL);
    PaeTop += 3;
    ASSERT (PaeTop->u.Hard.Valid == 1);
    TargetPageDirectoryIndex = (PFN_NUMBER)(PaeTop->u.Hard.PageFrameNumber);

    PaeTop = &MiSystemPaeVa.PteEntry[PD_PER_SYSTEM - 1];
    ASSERT (PaeTop->u.Hard.Valid == 1);
    PageDirectoryIndex = (PFN_NUMBER)(PaeTop->u.Hard.PageFrameNumber);

    TempPte = ValidKernelPte;
    TempPte.u.Hard.PageFrameNumber = TargetPageDirectoryIndex;
    ASSERT (MiLargePageHyperPte->u.Long == 0);
    MI_WRITE_VALID_PTE (MiLargePageHyperPte, TempPte);
    TargetPdePage = MiGetVirtualAddressMappedByPte (MiLargePageHyperPte);

    LowPtes = FALSE;

    //
    // Map the system process page directory as we know that's always kept
    // up to date.
    //

    PointerPte = MiMapPageInHyperSpaceAtDpc (CurrentProcess,
                                             PageDirectoryIndex);

    //
    // Copy all system PTE ranges that reside in the top 1GB.
    //

    for (i = 0; i < MiPteRangeIndex; i += 1) {

        VirtualAddress = MiPteRanges[i].StartingVa;

        if (VirtualAddress < (PVOID) 0xC0000000) {
            LowPtes = TRUE;
            continue;
        }

        PdeOffset = MiGetPdeOffset (VirtualAddress);
        PdeEndOffset = MiGetPdeOffset (MiPteRanges[i].EndingVa);

        PointerPde = &PointerPte[PdeOffset];
        TargetAddressSpacePde = &TargetPdePage[PdeOffset];

        RtlCopyMemory (TargetAddressSpacePde,
                       PointerPde,
                       (PdeEndOffset - PdeOffset + 1) * sizeof (MMPTE));

    }

    MiUnmapPageInHyperSpaceFromDpc (CurrentProcess, PointerPte);

    //
    // Just invalidate the mapping on the current processor as we cannot
    // have context switched.
    //

    MI_WRITE_ZERO_PTE (MiLargePageHyperPte);
    MI_FLUSH_CURRENT_TB_SINGLE (TargetPdePage);

    ASSERT (MmSystemRangeStart >= (PVOID) 0x80000000);

    //
    // Copy low additional system PTE ranges (if they exist).
    //

    if (LowPtes == TRUE) {
            
        ASSERT (MmSystemRangeStart < (PVOID) 0xC0000000);

        //
        // Map the target process' page directory.
        //

        PaeTop = Process->PaeTop;
        ASSERT (PaeTop != NULL);
        PaeTop += 2;
        ASSERT (PaeTop->u.Hard.Valid == 1);
        TargetPageDirectoryIndex = (PFN_NUMBER)(PaeTop->u.Hard.PageFrameNumber);

        TempPte.u.Hard.PageFrameNumber = TargetPageDirectoryIndex;
        ASSERT (MiLargePageHyperPte->u.Long == 0);
        MI_WRITE_VALID_PTE (MiLargePageHyperPte, TempPte);

        //
        // Map the system's page directory.
        //

        PaeTop = &MiSystemPaeVa.PteEntry[PD_PER_SYSTEM - 2];
        ASSERT (PaeTop->u.Hard.Valid == 1);
        PageDirectoryIndex = (PFN_NUMBER)(PaeTop->u.Hard.PageFrameNumber);

        PdeOffset = MiGetPdeOffset (MmSystemRangeStart);
        TargetAddressSpacePde = &TargetPdePage[PdeOffset];

        PointerPte = MiMapPageInHyperSpaceAtDpc (CurrentProcess,
                                                 PageDirectoryIndex);

        //
        // Copy all system ranges that reside in the 3rd GB.
        //
    
        for (i = 0; i < MiPteRangeIndex; i += 1) {
    
            VirtualAddress = MiPteRanges[i].StartingVa;
    
            if (VirtualAddress < (PVOID) 0xC0000000) {
    
                PdeOffset = MiGetPdeOffset (VirtualAddress);
                PdeEndOffset = MiGetPdeOffset (MiPteRanges[i].EndingVa);
        
                PointerPde = &PointerPte[PdeOffset];
                TargetAddressSpacePde = &TargetPdePage[PdeOffset];
        
                RtlCopyMemory (TargetAddressSpacePde,
                               PointerPde,
                               (PdeEndOffset - PdeOffset + 1) * sizeof (MMPTE));
            } 
        }
    
        MiUnmapPageInHyperSpaceFromDpc (CurrentProcess, PointerPte);

        //
        // Just invalidate the mapping on the current processor as we cannot
        // have context switched.
        //

        MI_WRITE_ZERO_PTE (MiLargePageHyperPte);
        MI_FLUSH_CURRENT_TB_SINGLE (TargetPdePage);
    }

    return;
}