554 lines
12 KiB
C
554 lines
12 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1989 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
hypermap.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module contains the routines which map physical pages into
|
|||
|
reserved PTEs within hyper space.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Lou Perazzoli (loup) 5-Apr-1989
|
|||
|
Landy Wang (landyw) 02-June-1997
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "mi.h"
|
|||
|
|
|||
|
PMMPTE MiFirstReservedZeroingPte;
|
|||
|
|
|||
|
KEVENT MiImageMappingPteEvent;
|
|||
|
|
|||
|
#pragma alloc_text(PAGE,MiMapImageHeaderInHyperSpace)
|
|||
|
#pragma alloc_text(PAGE,MiUnmapImageHeaderInHyperSpace)
|
|||
|
|
|||
|
|
|||
|
PVOID
|
|||
|
MiMapPageInHyperSpace (
|
|||
|
IN PEPROCESS Process,
|
|||
|
IN PFN_NUMBER PageFrameIndex,
|
|||
|
IN PKIRQL OldIrql
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This procedure maps the specified physical page into hyper space
|
|||
|
and returns the virtual address which maps the page.
|
|||
|
|
|||
|
************************************
|
|||
|
* *
|
|||
|
* Returns with a spin lock held!!! *
|
|||
|
* *
|
|||
|
************************************
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Process - Supplies the current process.
|
|||
|
|
|||
|
PageFrameIndex - Supplies the physical page number to map.
|
|||
|
|
|||
|
OldIrql - Supplies a pointer in which to return the entry IRQL.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Returns the address where the requested page was mapped.
|
|||
|
|
|||
|
RETURNS WITH THE HYPERSPACE SPIN LOCK HELD!!!!
|
|||
|
|
|||
|
The routine MiUnmapHyperSpaceMap MUST be called to release the lock!!!!
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
Kernel mode.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
MMPTE TempPte;
|
|||
|
PMMPTE PointerPte;
|
|||
|
PFN_NUMBER offset;
|
|||
|
|
|||
|
ASSERT (PageFrameIndex != 0);
|
|||
|
|
|||
|
PointerPte = MmFirstReservedMappingPte;
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER (Process); // Workaround compiler W4 bug.
|
|||
|
|
|||
|
LOCK_HYPERSPACE (Process, OldIrql);
|
|||
|
|
|||
|
//
|
|||
|
// Get offset to first free PTE.
|
|||
|
//
|
|||
|
|
|||
|
offset = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
|
|||
|
|
|||
|
if (offset == 0) {
|
|||
|
|
|||
|
//
|
|||
|
// All the reserved PTEs have been used, make them all invalid.
|
|||
|
//
|
|||
|
|
|||
|
MI_MAKING_MULTIPLE_PTES_INVALID (FALSE);
|
|||
|
|
|||
|
#if DBG
|
|||
|
{
|
|||
|
PMMPTE LastPte;
|
|||
|
|
|||
|
LastPte = PointerPte + NUMBER_OF_MAPPING_PTES;
|
|||
|
|
|||
|
do {
|
|||
|
ASSERT (LastPte->u.Long == 0);
|
|||
|
LastPte -= 1;
|
|||
|
} while (LastPte > PointerPte);
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// Use the page frame number field of the first PTE as an
|
|||
|
// offset into the available mapping PTEs.
|
|||
|
//
|
|||
|
|
|||
|
offset = NUMBER_OF_MAPPING_PTES;
|
|||
|
|
|||
|
//
|
|||
|
// Flush entire TB only on processors executing this process.
|
|||
|
//
|
|||
|
|
|||
|
KeFlushEntireTb (TRUE, FALSE);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Change offset for next time through.
|
|||
|
//
|
|||
|
|
|||
|
PointerPte->u.Hard.PageFrameNumber = offset - 1;
|
|||
|
|
|||
|
//
|
|||
|
// Point to free entry and make it valid.
|
|||
|
//
|
|||
|
|
|||
|
PointerPte += offset;
|
|||
|
ASSERT (PointerPte->u.Hard.Valid == 0);
|
|||
|
|
|||
|
|
|||
|
TempPte = ValidPtePte;
|
|||
|
TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
|
|||
|
*PointerPte = TempPte;
|
|||
|
|
|||
|
//
|
|||
|
// Return the VA that maps the page.
|
|||
|
//
|
|||
|
|
|||
|
return MiGetVirtualAddressMappedByPte (PointerPte);
|
|||
|
}
|
|||
|
|
|||
|
PVOID
|
|||
|
MiMapPageInHyperSpaceAtDpc (
|
|||
|
IN PEPROCESS Process,
|
|||
|
IN PFN_NUMBER PageFrameIndex
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This procedure maps the specified physical page into hyper space
|
|||
|
and returns the virtual address which maps the page.
|
|||
|
|
|||
|
************************************
|
|||
|
* *
|
|||
|
* Returns with a spin lock held!!! *
|
|||
|
* *
|
|||
|
************************************
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Process - Supplies the current process.
|
|||
|
|
|||
|
PageFrameIndex - Supplies the physical page number to map.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Returns the address where the requested page was mapped.
|
|||
|
|
|||
|
RETURNS WITH THE HYPERSPACE SPIN LOCK HELD!!!!
|
|||
|
|
|||
|
The routine MiUnmapHyperSpaceMap MUST be called to release the lock!!!!
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
Kernel mode, DISPATCH_LEVEL on entry.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
MMPTE TempPte;
|
|||
|
PMMPTE PointerPte;
|
|||
|
PFN_NUMBER offset;
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER (Process); // Workaround compiler W4 bug.
|
|||
|
|
|||
|
ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL);
|
|||
|
ASSERT (PageFrameIndex != 0);
|
|||
|
|
|||
|
LOCK_HYPERSPACE_AT_DPC (Process);
|
|||
|
|
|||
|
//
|
|||
|
// Get offset to first free PTE.
|
|||
|
//
|
|||
|
|
|||
|
PointerPte = MmFirstReservedMappingPte;
|
|||
|
|
|||
|
offset = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
|
|||
|
|
|||
|
if (offset == 0) {
|
|||
|
|
|||
|
//
|
|||
|
// All the reserved PTEs have been used, make them all invalid.
|
|||
|
//
|
|||
|
|
|||
|
MI_MAKING_MULTIPLE_PTES_INVALID (FALSE);
|
|||
|
|
|||
|
#if DBG
|
|||
|
{
|
|||
|
PMMPTE LastPte;
|
|||
|
|
|||
|
LastPte = PointerPte + NUMBER_OF_MAPPING_PTES;
|
|||
|
|
|||
|
do {
|
|||
|
ASSERT (LastPte->u.Long == 0);
|
|||
|
LastPte -= 1;
|
|||
|
} while (LastPte > PointerPte);
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// Use the page frame number field of the first PTE as an
|
|||
|
// offset into the available mapping PTEs.
|
|||
|
//
|
|||
|
|
|||
|
offset = NUMBER_OF_MAPPING_PTES;
|
|||
|
|
|||
|
//
|
|||
|
// Flush entire TB only on processors executing this process.
|
|||
|
//
|
|||
|
|
|||
|
KeFlushEntireTb (TRUE, FALSE);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Change offset for next time through.
|
|||
|
//
|
|||
|
|
|||
|
PointerPte->u.Hard.PageFrameNumber = offset - 1;
|
|||
|
|
|||
|
//
|
|||
|
// Point to free entry and make it valid.
|
|||
|
//
|
|||
|
|
|||
|
PointerPte += offset;
|
|||
|
ASSERT (PointerPte->u.Hard.Valid == 0);
|
|||
|
|
|||
|
|
|||
|
TempPte = ValidPtePte;
|
|||
|
TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
|
|||
|
*PointerPte = TempPte;
|
|||
|
|
|||
|
//
|
|||
|
// Return the VA that maps the page.
|
|||
|
//
|
|||
|
|
|||
|
return MiGetVirtualAddressMappedByPte (PointerPte);
|
|||
|
}
|
|||
|
|
|||
|
PVOID
|
|||
|
MiMapImageHeaderInHyperSpace (
|
|||
|
IN PFN_NUMBER PageFrameIndex
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This procedure maps the specified physical page into the
|
|||
|
PTE within hyper space reserved explicitly for image page
|
|||
|
header mapping. By reserving an explicit PTE for mapping
|
|||
|
the PTE, page faults can occur while the PTE is mapped within
|
|||
|
hyperspace and no other hyperspace maps will affect this PTE.
|
|||
|
|
|||
|
Note that if another thread attempts to map an image at the
|
|||
|
same time, it will be forced into a wait state until the
|
|||
|
header is "unmapped".
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
PageFrameIndex - Supplies the physical page number to map.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Returns the virtual address where the specified physical page was
|
|||
|
mapped.
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
Kernel mode.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
MMPTE TempPte;
|
|||
|
MMPTE OriginalPte;
|
|||
|
PMMPTE PointerPte;
|
|||
|
PVOID FlushVaPointer;
|
|||
|
|
|||
|
ASSERT (PageFrameIndex != 0);
|
|||
|
|
|||
|
TempPte = ValidPtePte;
|
|||
|
TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
|
|||
|
|
|||
|
FlushVaPointer = (PVOID) IMAGE_MAPPING_PTE;
|
|||
|
|
|||
|
//
|
|||
|
// Ensure both modified and accessed bits are set so the hardware doesn't
|
|||
|
// ever write this PTE.
|
|||
|
//
|
|||
|
|
|||
|
ASSERT (TempPte.u.Hard.Dirty == 1);
|
|||
|
ASSERT (TempPte.u.Hard.Accessed == 1);
|
|||
|
|
|||
|
PointerPte = MiGetPteAddress (IMAGE_MAPPING_PTE);
|
|||
|
|
|||
|
do {
|
|||
|
OriginalPte.u.Long = 0;
|
|||
|
|
|||
|
OriginalPte.u.Long = InterlockedCompareExchangePte (
|
|||
|
PointerPte,
|
|||
|
TempPte.u.Long,
|
|||
|
OriginalPte.u.Long);
|
|||
|
|
|||
|
if (OriginalPte.u.Long == 0) {
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Another thread modified the PTE just before us or the PTE was
|
|||
|
// already in use. This should be very rare - go the long way.
|
|||
|
//
|
|||
|
|
|||
|
InterlockedIncrement ((PLONG)&MmWorkingSetList->NumberOfImageWaiters);
|
|||
|
|
|||
|
//
|
|||
|
// Deliberately wait with a timeout since the PTE release runs
|
|||
|
// without lock synchronization so there is the extremely rare
|
|||
|
// race window which the timeout saves us from.
|
|||
|
//
|
|||
|
|
|||
|
KeWaitForSingleObject (&MiImageMappingPteEvent,
|
|||
|
Executive,
|
|||
|
KernelMode,
|
|||
|
FALSE,
|
|||
|
(PLARGE_INTEGER)&MmOneSecond);
|
|||
|
|
|||
|
InterlockedDecrement ((PLONG)&MmWorkingSetList->NumberOfImageWaiters);
|
|||
|
|
|||
|
} while (TRUE);
|
|||
|
|
|||
|
//
|
|||
|
// Use KeFlushMultiple even though only one PTE is being flushed
|
|||
|
// because this interface provides a way to just flush the
|
|||
|
// specified TB entry without writing a PTE and we always want
|
|||
|
// to do interlocked writes to this PTE.
|
|||
|
//
|
|||
|
// Note the flush must be made across all processors as this thread
|
|||
|
// may migrate. Also this must be done here instead of in the unmap
|
|||
|
// in order to support lock-free operation.
|
|||
|
//
|
|||
|
|
|||
|
KeFlushMultipleTb (1,
|
|||
|
&FlushVaPointer,
|
|||
|
TRUE,
|
|||
|
TRUE,
|
|||
|
NULL,
|
|||
|
TempPte.u.Flush);
|
|||
|
|
|||
|
return (PVOID) MiGetVirtualAddressMappedByPte (PointerPte);
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
MiUnmapImageHeaderInHyperSpace (
|
|||
|
VOID
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This procedure unmaps the PTE reserved for mapping the image
|
|||
|
header, flushes the TB, and, if the WaitingForImageMapping field
|
|||
|
is not NULL, sets the specified event.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
Kernel mode.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PMMPTE PointerPte;
|
|||
|
|
|||
|
PointerPte = MiGetPteAddress (IMAGE_MAPPING_PTE);
|
|||
|
|
|||
|
//
|
|||
|
// Capture the number of waiters.
|
|||
|
//
|
|||
|
|
|||
|
ASSERT (PointerPte->u.Long != 0);
|
|||
|
|
|||
|
#if defined (_WIN64)
|
|||
|
InterlockedExchange64 ((PLONG64)PointerPte, ZeroPte.u.Long);
|
|||
|
#elif defined(_X86PAE_)
|
|||
|
KeInterlockedSwapPte ((PHARDWARE_PTE)PointerPte,
|
|||
|
(PHARDWARE_PTE)&ZeroPte.u.Long);
|
|||
|
#else
|
|||
|
InterlockedExchange ((PLONG)PointerPte, ZeroPte.u.Long);
|
|||
|
#endif
|
|||
|
|
|||
|
if (MmWorkingSetList->NumberOfImageWaiters != 0) {
|
|||
|
|
|||
|
//
|
|||
|
// If there are any threads waiting, wake them all now. Note this
|
|||
|
// will wake threads in other processes as well, but it is very
|
|||
|
// rare that there are any waiters in the entire system period.
|
|||
|
//
|
|||
|
|
|||
|
KePulseEvent (&MiImageMappingPteEvent, 0, FALSE);
|
|||
|
}
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
PVOID
|
|||
|
MiMapPageToZeroInHyperSpace (
|
|||
|
IN PFN_NUMBER PageFrameIndex
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This procedure maps the specified physical page into hyper space
|
|||
|
and returns the virtual address which maps the page.
|
|||
|
|
|||
|
This is only to be used by THE zeroing page thread.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
PageFrameIndex - Supplies the physical page number to map.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Returns the virtual address where the specified physical page was
|
|||
|
mapped.
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
PASSIVE_LEVEL.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PFN_NUMBER offset;
|
|||
|
MMPTE TempPte;
|
|||
|
PMMPTE PointerPte;
|
|||
|
|
|||
|
ASSERT (PageFrameIndex != 0);
|
|||
|
|
|||
|
ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
|
|||
|
|
|||
|
PointerPte = MiFirstReservedZeroingPte;
|
|||
|
|
|||
|
//
|
|||
|
// Get offset to first free PTE.
|
|||
|
//
|
|||
|
|
|||
|
offset = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
|
|||
|
|
|||
|
if (offset == 0) {
|
|||
|
|
|||
|
//
|
|||
|
// All the reserved PTEs have been used, make them all invalid.
|
|||
|
//
|
|||
|
|
|||
|
MI_MAKING_MULTIPLE_PTES_INVALID (FALSE);
|
|||
|
|
|||
|
#if DBG
|
|||
|
{
|
|||
|
PMMPTE LastPte;
|
|||
|
|
|||
|
LastPte = PointerPte + NUMBER_OF_ZEROING_PTES;
|
|||
|
|
|||
|
do {
|
|||
|
ASSERT (LastPte->u.Long == 0);
|
|||
|
LastPte -= 1;
|
|||
|
} while (LastPte > PointerPte);
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// Use the page frame number field of the first PTE as an
|
|||
|
// offset into the available zeroing PTEs.
|
|||
|
//
|
|||
|
|
|||
|
offset = NUMBER_OF_ZEROING_PTES;
|
|||
|
PointerPte->u.Hard.PageFrameNumber = offset;
|
|||
|
|
|||
|
//
|
|||
|
// Flush entire TB only on processors executing this process as this
|
|||
|
// thread may migrate there at any time.
|
|||
|
//
|
|||
|
|
|||
|
KeFlushEntireTb (TRUE, FALSE);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Change offset for next time through.
|
|||
|
//
|
|||
|
|
|||
|
PointerPte->u.Hard.PageFrameNumber = offset - 1;
|
|||
|
|
|||
|
//
|
|||
|
// Point to free entry and make it valid.
|
|||
|
//
|
|||
|
|
|||
|
PointerPte += offset;
|
|||
|
ASSERT (PointerPte->u.Hard.Valid == 0);
|
|||
|
|
|||
|
TempPte = ValidPtePte;
|
|||
|
TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
|
|||
|
*PointerPte = TempPte;
|
|||
|
|
|||
|
//
|
|||
|
// Return the VA that maps the page.
|
|||
|
//
|
|||
|
|
|||
|
return MiGetVirtualAddressMappedByPte (PointerPte);
|
|||
|
}
|