371 lines
7.3 KiB
C
371 lines
7.3 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
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "mi.h"
|
||
|
||
|
||
PVOID
|
||
MiMapPageInHyperSpace (
|
||
IN ULONG 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:
|
||
|
||
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.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
MMPTE TempPte;
|
||
PMMPTE PointerPte;
|
||
ULONG offset;
|
||
|
||
#if DBG
|
||
if (PageFrameIndex == 0) {
|
||
DbgPrint("attempt to map physical page 0 in hyper space\n");
|
||
KeBugCheck (MEMORY_MANAGEMENT);
|
||
}
|
||
#endif //DBG
|
||
|
||
LOCK_HYPERSPACE(OldIrql);
|
||
|
||
if( PageFrameIndex < MmKseg2Frame){
|
||
return (PVOID)(MM_KSEG0_BASE + (PageFrameIndex << PAGE_SHIFT));
|
||
}
|
||
|
||
PointerPte = MmFirstReservedMappingPte;
|
||
if (PointerPte->u.Hard.Valid == 1) {
|
||
|
||
//
|
||
// All the reserved PTEs have been used, make
|
||
// them all invalid.
|
||
//
|
||
|
||
MI_MAKING_MULTIPLE_PTES_INVALID (FALSE);
|
||
|
||
RtlZeroMemory (MmFirstReservedMappingPte,
|
||
(NUMBER_OF_MAPPING_PTES + 1) * sizeof(MMPTE));
|
||
|
||
//
|
||
// Use the page frame number field of the first PTE as an
|
||
// offset into the available mapping PTEs.
|
||
//
|
||
|
||
PointerPte->u.Hard.PageFrameNumber = NUMBER_OF_MAPPING_PTES;
|
||
|
||
//
|
||
// Flush entire TB only on this processor.
|
||
//
|
||
|
||
KeFlushEntireTb (TRUE, FALSE);
|
||
}
|
||
|
||
//
|
||
// Get offset to first free PTE.
|
||
//
|
||
|
||
offset = PointerPte->u.Hard.PageFrameNumber;
|
||
|
||
//
|
||
// 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 map the page.
|
||
//
|
||
|
||
return MiGetVirtualAddressMappedByPte (PointerPte);
|
||
}
|
||
|
||
PVOID
|
||
MiMapImageHeaderInHyperSpace (
|
||
IN ULONG 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;
|
||
PMMPTE PointerPte;
|
||
KIRQL OldIrql;
|
||
|
||
#if DBG
|
||
if (PageFrameIndex == 0) {
|
||
DbgPrint("attempt to map physical page 0 in hyper space\n");
|
||
KeBugCheck (MEMORY_MANAGEMENT);
|
||
}
|
||
#endif //DBG
|
||
|
||
PointerPte = MiGetPteAddress (IMAGE_MAPPING_PTE);
|
||
|
||
LOCK_PFN (OldIrql);
|
||
|
||
while (PointerPte->u.Long != 0) {
|
||
|
||
//
|
||
// If there is no event specified, set one up.
|
||
//
|
||
|
||
if (MmWorkingSetList->WaitingForImageMapping == (PKEVENT)NULL) {
|
||
|
||
//
|
||
// Set the global event into the field and wait for it.
|
||
//
|
||
|
||
MmWorkingSetList->WaitingForImageMapping = &MmImageMappingPteEvent;
|
||
}
|
||
|
||
//
|
||
// Release the PFN lock and wait on the event in an
|
||
// atomic operation.
|
||
//
|
||
|
||
KeEnterCriticalRegion();
|
||
UNLOCK_PFN_AND_THEN_WAIT(OldIrql);
|
||
|
||
KeWaitForSingleObject(MmWorkingSetList->WaitingForImageMapping,
|
||
Executive,
|
||
KernelMode,
|
||
FALSE,
|
||
(PLARGE_INTEGER)NULL);
|
||
KeLeaveCriticalRegion();
|
||
|
||
LOCK_PFN (OldIrql);
|
||
}
|
||
|
||
ASSERT (PointerPte->u.Long == 0);
|
||
|
||
TempPte = ValidPtePte;
|
||
TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
|
||
|
||
*PointerPte = TempPte;
|
||
|
||
UNLOCK_PFN (OldIrql);
|
||
|
||
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.
|
||
|
||
--*/
|
||
|
||
{
|
||
MMPTE TempPte;
|
||
PMMPTE PointerPte;
|
||
KIRQL OldIrql;
|
||
PKEVENT Event;
|
||
|
||
PointerPte = MiGetPteAddress (IMAGE_MAPPING_PTE);
|
||
|
||
TempPte.u.Long = 0;
|
||
|
||
LOCK_PFN (OldIrql);
|
||
|
||
//
|
||
// Capture the current state of the event field and clear it out.
|
||
//
|
||
|
||
Event = MmWorkingSetList->WaitingForImageMapping;
|
||
|
||
MmWorkingSetList->WaitingForImageMapping = (PKEVENT)NULL;
|
||
|
||
ASSERT (PointerPte->u.Long != 0);
|
||
|
||
KeFlushSingleTb (IMAGE_MAPPING_PTE,
|
||
TRUE,
|
||
FALSE,
|
||
(PHARDWARE_PTE)PointerPte,
|
||
TempPte.u.Flush);
|
||
|
||
UNLOCK_PFN (OldIrql);
|
||
|
||
if (Event != (PKEVENT)NULL) {
|
||
|
||
//
|
||
// If there was an event specified, set the event.
|
||
//
|
||
|
||
KePulseEvent (Event, 0, FALSE);
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
PVOID
|
||
MiMapPageToZeroInHyperSpace (
|
||
IN ULONG PageFrameIndex
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This procedure maps the specified physical page into hyper space
|
||
and returns the virtual address which maps the page.
|
||
|
||
NOTE: it maps it into the same location reserved for zeroing operations.
|
||
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:
|
||
|
||
Must be holding the PFN lock.
|
||
|
||
--*/
|
||
|
||
{
|
||
MMPTE TempPte;
|
||
PMMPTE PointerPte;
|
||
PVOID MappedAddress;
|
||
|
||
#if DBG
|
||
if (PageFrameIndex == 0) {
|
||
DbgPrint("attempt to map physical page 0 in hyper space\n");
|
||
KeBugCheck (MEMORY_MANAGEMENT);
|
||
}
|
||
#endif
|
||
|
||
MM_PFN_LOCK_ASSERT();
|
||
|
||
if (PageFrameIndex < MmKseg2Frame) {
|
||
return (PVOID)(MM_KSEG0_BASE + (PageFrameIndex << PAGE_SHIFT));
|
||
}
|
||
|
||
PointerPte = MiGetPteAddress (ZEROING_PAGE_PTE);
|
||
|
||
MappedAddress = MiGetVirtualAddressMappedByPte (PointerPte);
|
||
|
||
TempPte.u.Long = 0;
|
||
|
||
KeFlushSingleTb (MappedAddress,
|
||
TRUE,
|
||
FALSE,
|
||
(PHARDWARE_PTE)PointerPte,
|
||
TempPte.u.Flush);
|
||
|
||
TempPte = ValidPtePte;
|
||
TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
|
||
|
||
*PointerPte = TempPte;
|
||
|
||
return MappedAddress;
|
||
}
|