Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

929 lines
20 KiB

/*++
Module Name:
region.c
Abstract:
This module implements the region space management code.
Author:
Landy Wang (landyw) 18-Feb-1999
Koichi Yamada (kyamada) 18-Feb-1999
Environment:
Kernel mode only.
Revision History:
--*/
#include "ki.h"
VOID
KiSetRegionRegister (
PVOID VirtualAddress,
ULONGLONG Contents
);
#define KiMakeValidRegionRegister(Rid, Ps) \
(((ULONGLONG)Rid << RR_RID) | (Ps << RR_PS) | (1 << RR_VE))
ULONG KiMaximumRid = MAXIMUM_RID;
VOID
KiSyncNewRegionIdTarget (
IN PULONG SignalDone,
IN PVOID Parameter1,
IN PVOID Parameter2,
IN PVOID Parameter3
)
/*++
Routine Description:
This is the target function for synchronizing the region IDs.
Arguments:
SignalDone - Supplies a pointer to a variable that is cleared when the
requested operation has been performed.
Parameter1 - Not used.
Parameter2 - Not used.
Parameter3 - Not used.
Return Value:
None.
--*/
{
#if !defined(NT_UP)
PKPROCESS Process;
PREGION_MAP_INFO ProcessRegion;
PREGION_MAP_INFO MappedSession;
//
// get KPROCESS from PCR for MP synchronization
//
Process = (PKPROCESS)PCR->Pcb;
ProcessRegion = &Process->ProcessRegion;
MappedSession = Process->SessionMapInfo;
KiAcquireSpinLock(&KiMasterRidLock);
if (ProcessRegion->SequenceNumber != KiMasterSequence) {
KiMasterRid += 1;
ProcessRegion->RegionId = KiMasterRid;
ProcessRegion->SequenceNumber = KiMasterSequence;
}
KiSetRegionRegister(MM_LOWEST_USER_ADDRESS,
KiMakeValidRegionRegister(ProcessRegion->RegionId, PAGE_SHIFT));
KiFlushFixedDataTb(TRUE, PDE_UTBASE);
KeFillFixedEntryTb((PHARDWARE_PTE)&Process->DirectoryTableBase[0],
(PVOID)PDE_UTBASE,
PAGE_SHIFT,
DTR_UTBASE_INDEX);
if (MappedSession->SequenceNumber != KiMasterSequence) {
KiMasterRid += 1;
MappedSession->RegionId = KiMasterRid;
MappedSession->SequenceNumber = KiMasterSequence;
}
KiSetRegionRegister((PVOID)SADDRESS_BASE,
KiMakeValidRegionRegister(MappedSession->RegionId, PAGE_SHIFT));
KiFlushFixedDataTb(TRUE, PDE_STBASE);
KeFillFixedEntryTb((PHARDWARE_PTE)&Process->SessionParentBase,
(PVOID)PDE_STBASE,
PAGE_SHIFT,
DTR_STBASE_INDEX);
KiReleaseSpinLock(&KiMasterRidLock);
KiIpiSignalPacketDone(SignalDone);
KeFlushCurrentTb();
#else
UNREFERENCED_PARAMETER (SignalDone);
#endif
UNREFERENCED_PARAMETER (Parameter1);
UNREFERENCED_PARAMETER (Parameter2);
UNREFERENCED_PARAMETER (Parameter3);
return;
}
BOOLEAN
KiSyncNewRegionId(
IN PREGION_MAP_INFO ProcessRegion,
IN PREGION_MAP_INFO SessionRegion,
IN BOOLEAN RegionFlushRequired
)
/*++
Routine Description:
Generate a new region id and synchronize the region IDs on all the
processors if necessary. If the region IDs wrap then flush all
processor TBs.
Arguments:
ProcessRegion - Supplies a REGION_MAP_INFO user space pointer.
SessionRegion - Supplies a REGION_MAP_INFO session space pointer.
LockHeld - KiRegionSwapLock is held by the caller
Return Value:
TRUE - if the region id has been recycled.
FALSE -- if the region id has not been recycled.
Notes:
This routine called by KiSwapProcess and KeAttachSessionSpace.
Environment:
Kernel mode.
Called at SYNCH_LEVEL
--*/
{
ULONG i;
LOGICAL RidRecycled;
#if !defined(NT_UP)
KAFFINITY TargetProcessors;
KIRQL OldIrql;
#endif
ULONGLONG PrSequence;
ULONGLONG SeSequence;
RidRecycled = FALSE;
ASSERT (KeGetCurrentIrql () == SYNCH_LEVEL);
//
// copy the KPROCESS pointer for MP region synchronization
//
PCR->Pcb = (PVOID)KeGetCurrentThread()->ApcState.Process;
//
// Invalidx1ate the ForwardProgressTb buffer
//
for (i = 0; i < MAXIMUM_FWP_BUFFER_ENTRY; i += 1) {
PCR->ForwardProgressBuffer[(i*2)+1] = 0;
}
not_recycled:
#if !defined(NT_UP)
KeRaiseIrql(HIGH_LEVEL, &OldIrql);
#endif
PrSequence = ProcessRegion->SequenceNumber;
SeSequence = SessionRegion->SequenceNumber;
if ((PrSequence == KiMasterSequence) && (SeSequence == KiMasterSequence)) {
KiSetRegionRegister(MM_LOWEST_USER_ADDRESS,
KiMakeValidRegionRegister(ProcessRegion->RegionId,
PAGE_SHIFT));
KiSetRegionRegister((PVOID)SADDRESS_BASE,
KiMakeValidRegionRegister(SessionRegion->RegionId,
PAGE_SHIFT));
#if !defined(NT_UP)
KeLowerIrql(OldIrql);
#endif
#if !defined(NT_UP)
if (RegionFlushRequired) {
KiAcquireSpinLock (&KiRegionSwapLock);
goto RegionFlush;
}
#endif
UNREFERENCED_PARAMETER (RegionFlushRequired);
return FALSE;
}
#if !defined(NT_UP)
KeLowerIrql(OldIrql);
KiAcquireSpinLock (&KiRegionSwapLock);
#endif
PrSequence = ProcessRegion->SequenceNumber;
SeSequence = SessionRegion->SequenceNumber;
if (PrSequence != KiMasterSequence) {
if (KiMasterRid + 1 > KiMaximumRid) {
RidRecycled = TRUE;
} else {
KiMasterRid += 1;
ProcessRegion->RegionId = KiMasterRid;
ProcessRegion->SequenceNumber = KiMasterSequence;
}
}
if ((RidRecycled == FALSE) && (SeSequence != KiMasterSequence)) {
if (KiMasterRid + 1 > KiMaximumRid) {
RidRecycled = TRUE;
} else {
KiMasterRid += 1;
SessionRegion->RegionId = KiMasterRid;
SessionRegion->SequenceNumber = KiMasterSequence;
}
}
if (RidRecycled == FALSE) {
KiReleaseSpinLock(&KiRegionSwapLock);
goto not_recycled;
}
//
// The region ID must be recycled.
//
KiMasterRid = START_PROCESS_RID;
//
// Since KiMasterSequence is 64-bits wide, it will
// not be recycled in your life time.
//
if (KiMasterSequence + 1 > MAXIMUM_SEQUENCE) {
KiMasterSequence = START_SEQUENCE;
} else {
KiMasterSequence += 1;
}
//
// Update the new process's ProcessRid and ProcessSequence.
//
ProcessRegion->RegionId = KiMasterRid;
ProcessRegion->SequenceNumber = KiMasterSequence;
KiSetRegionRegister(MM_LOWEST_USER_ADDRESS,
KiMakeValidRegionRegister(ProcessRegion->RegionId, PAGE_SHIFT));
KiMasterRid += 1;
SessionRegion->RegionId = KiMasterRid;
SessionRegion->SequenceNumber = KiMasterSequence;
KiSetRegionRegister((PVOID)SADDRESS_BASE,
KiMakeValidRegionRegister(SessionRegion->RegionId, PAGE_SHIFT));
#if !defined(NT_UP)
RegionFlush:
//
// Broadcast Region Id sync.
//
TargetProcessors = KeActiveProcessors;
TargetProcessors &= PCR->NotMember;
if (TargetProcessors != 0) {
KiIpiSendPacket(TargetProcessors,
KiSyncNewRegionIdTarget,
(PVOID)TRUE,
NULL,
NULL);
}
#endif
KeFlushCurrentTb();
#if !defined(NT_UP)
//
// Wait until all target processors have finished.
//
if (TargetProcessors != 0) {
KiIpiStallOnPacketTargets(TargetProcessors);
}
#endif
#if !defined(NT_UP)
KiReleaseSpinLock (&KiRegionSwapLock);
#endif
return TRUE;
}
VOID
KeEnableSessionSharing(
IN PREGION_MAP_INFO SessionMapInfo,
IN PFN_NUMBER SessionParentPage
)
/*++
Routine Description:
This routine initializes a session for use. This includes :
1. Allocating a new region ID for the session.
2. Updating the current region register with this new RID.
3. Updating the SessionMapInfo fields so context switches will work.
4. Updating SessionParentBase fields so context switches will work.
Upon return from this routine, the session will be available for
sharing by the current and other processes.
Arguments:
SessionMapInfo - Supplies a session map info to be shared.
SessionParentPage - Supplies the top level parent page mapping the
argument session space.
Return Value:
None.
Environment:
Kernel mode.
--*/
{
ULONG i;
#if !defined(NT_UP)
KAFFINITY TargetProcessors;
#endif
PKPROCESS Process;
PKTHREAD Thread;
KIRQL OldIrql;
Thread = KeGetCurrentThread();
Process = Thread->ApcState.Process;
OldIrql = KeRaiseIrqlToSynchLevel();
INITIALIZE_DIRECTORY_TABLE_BASE (&Process->SessionParentBase,
SessionParentPage);
//
// Invalidate the ForwardProgressTb buffer.
//
for (i = 0; i < MAXIMUM_FWP_BUFFER_ENTRY; i += 1) {
PCR->ForwardProgressBuffer[(i*2)+1] = 0;
}
#if !defined(NT_UP)
KiAcquireSpinLock(&KiRegionSwapLock);
#endif
if (KiMasterRid + 1 > KiMaximumRid) {
//
// The region ID must be recycled.
//
KiMasterRid = START_PROCESS_RID;
//
// Since KiMasterSequence is 64-bits wide, it will
// not be recycled in your life time.
//
if (KiMasterSequence + 1 > MAXIMUM_SEQUENCE) {
KiMasterSequence = START_SEQUENCE;
} else {
KiMasterSequence += 1;
}
}
//
// Update the newly created session's RegionId and SequenceNumber.
//
KiMasterRid += 1;
Process->SessionMapInfo = SessionMapInfo;
SessionMapInfo->RegionId = KiMasterRid;
SessionMapInfo->SequenceNumber = KiMasterSequence;
KiSetRegionRegister((PVOID)SADDRESS_BASE,
KiMakeValidRegionRegister(SessionMapInfo->RegionId,
PAGE_SHIFT));
//
// Note that all processors must be notified because this thread could
// context switch onto another processor. If that processor was already
// running a thread from this same process, no region register update
// would occur otherwise.
//
#if !defined(NT_UP)
//
// Broadcast Region Id sync.
//
TargetProcessors = KeActiveProcessors;
TargetProcessors &= PCR->NotMember;
if (TargetProcessors != 0) {
KiIpiSendPacket(TargetProcessors,
KiSyncNewRegionIdTarget,
(PVOID)TRUE,
NULL,
NULL);
}
#endif
KeFlushCurrentTb();
KeFillFixedEntryTb((PHARDWARE_PTE)&Process->SessionParentBase,
(PVOID)PDE_STBASE,
PAGE_SHIFT,
DTR_STBASE_INDEX);
#if !defined(NT_UP)
//
// Wait until all target processors have finished.
//
if (TargetProcessors != 0) {
KiIpiStallOnPacketTargets(TargetProcessors);
}
KiReleaseSpinLock(&KiRegionSwapLock);
#endif
KeLowerIrql(OldIrql);
}
VOID
KeAttachSessionSpace(
PREGION_MAP_INFO SessionMapInfo,
IN PFN_NUMBER SessionParentPage
)
/*++
Routine Description:
This routine attaches the current process to the specified session.
This includes:
1. Updating the current region register with the target RID.
2. Updating the SessionMapInfo fields so context switches will work.
3. Updating SessionParentBase fields so context switches will work.
Arguments:
SessionMapInfo - Supplies the target session map info.
SessionParentPage - Supplies the top level parent page mapping the
argument session space.
Return Value:
None.
Environment:
Kernel mode.
--*/
{
KIRQL OldIrql;
PKTHREAD Thread;
PKPROCESS Process;
Thread = KeGetCurrentThread();
Process = Thread->ApcState.Process;
OldIrql = KeRaiseIrqlToSynchLevel();
ASSERT(SessionMapInfo != NULL);
//
// Attach to the specified session.
//
INITIALIZE_DIRECTORY_TABLE_BASE (&Process->SessionParentBase,
SessionParentPage);
Process->SessionMapInfo = SessionMapInfo;
//
// Note that all processors must be notified because this thread could
// context switch onto another processor. If that processor was already
// running a thread from this same process, no region register update
// would occur. Hence KiRegionFlushRequired is set under ContextSwap lock
// protection to signify this to KiSyncNewRegionId.
//
KiSyncNewRegionId(&Process->ProcessRegion, SessionMapInfo, TRUE);
KiFlushFixedDataTb(TRUE, PDE_STBASE);
KeFillFixedEntryTb((PHARDWARE_PTE)&Process->SessionParentBase,
(PVOID)PDE_STBASE,
PAGE_SHIFT,
DTR_STBASE_INDEX);
KeLowerIrql(OldIrql);
}
VOID
KiSyncSessionTarget(
IN PULONG SignalDone,
IN PKPROCESS Process,
IN PVOID Parameter1,
IN PVOID Parameter2
)
/*++
Routine Description:
This is the target function for synchronizing the new session
region ID. This routine is called when the session space is removed
and all the processors need to be notified.
Arguments:
SignalDone - Supplies a pointer to a variable that is cleared when the
requested operation has been performed.
Process - Supplies a KPROCESS pointer which needs to be synchronized.
Return Value:
None.
Environment:
Kernel mode.
--*/
{
UNREFERENCED_PARAMETER (Parameter1);
UNREFERENCED_PARAMETER (Parameter2);
#if !defined(NT_UP)
//
// Check to see if the current process is the process that needs to be
// synchronized.
//
if (Process == (PKPROCESS)PCR->Pcb) {
KiAcquireSpinLock(&KiMasterRidLock);
//
// Disable the session region.
//
KiSetRegionRegister((PVOID)SADDRESS_BASE,
KiMakeValidRegionRegister(Process->SessionMapInfo->RegionId, PAGE_SHIFT));
KiFlushFixedDataTb(TRUE, (PVOID)PDE_STBASE);
KeFillFixedEntryTb((PHARDWARE_PTE)&Process->SessionParentBase,
(PVOID)PDE_STBASE,
PAGE_SHIFT,
DTR_STBASE_INDEX);
KiReleaseSpinLock(&KiMasterRidLock);
}
KiIpiSignalPacketDone(SignalDone);
#else
UNREFERENCED_PARAMETER (SignalDone);
UNREFERENCED_PARAMETER (Process);
#endif
return;
}
VOID
KeDetachSessionSpace(
IN PREGION_MAP_INFO NullSessionMapInfo,
IN PFN_NUMBER NullSessionPage
)
/*++
Routine Description:
This routine detaches the current process from the current session
space.
This includes:
1. Updating the current region register.
2. Updating the SessionMapInfo fields so context switches will work.
3. Updating SessionParentBase fields so context switches will work.
Arguments:
SessionMapInfo - Supplies a new session map information to use (the
existing session map info is discarded). This is usually
a NULL entry.
NullSessionPage - Supplies the new top level parent page to use.
Return Value:
None.
Environment:
Kernel mode.
--*/
{
KIRQL OldIrql;
PKTHREAD Thread;
PKPROCESS Process;
#if !defined(NT_UP)
KAFFINITY TargetProcessors;
#endif
//
// Raise IRQL to DISPATCH_LEVEL and lock the dispatcher database.
//
Thread = KeGetCurrentThread();
Process = Thread->ApcState.Process;
OldIrql = KeRaiseIrqlToSynchLevel();
INITIALIZE_DIRECTORY_TABLE_BASE (&Process->SessionParentBase,
NullSessionPage);
Process->SessionMapInfo = NullSessionMapInfo;
#if !defined(NT_UP)
KiAcquireSpinLock(&KiRegionSwapLock);
//
// Broadcast the region ID sync.
//
TargetProcessors = KeActiveProcessors;
TargetProcessors &= PCR->NotMember;
if (TargetProcessors != 0) {
KiIpiSendPacket(TargetProcessors,
KiSyncSessionTarget,
Process,
NULL,
NULL);
}
#endif
KiSetRegionRegister((PVOID)SADDRESS_BASE,
KiMakeValidRegionRegister(NullSessionMapInfo->RegionId, PAGE_SHIFT));
KiFlushFixedDataTb(TRUE, PDE_STBASE);
KeFillFixedEntryTb((PHARDWARE_PTE)&Process->SessionParentBase,
(PVOID)PDE_STBASE,
PAGE_SHIFT,
DTR_STBASE_INDEX);
#if !defined(NT_UP)
//
// Wait until all target processors have finished.
//
if (TargetProcessors != 0) {
KiIpiStallOnPacketTargets(TargetProcessors);
}
KiReleaseSpinLock(&KiRegionSwapLock);
#endif
KeLowerIrql(OldIrql);
}
VOID
KeAddSessionSpace(
IN PKPROCESS Process,
IN PREGION_MAP_INFO SessionMapInfo,
IN PFN_NUMBER SessionParentPage
)
/*++
Routine Description:
Add the session map info to the KPROCESS of the new process.
This includes:
1. Updating the SessionMapInfo fields so context switches will work.
2. Updating SessionParentBase fields so context switches will work.
Note the dispatcher lock is not needed since the process can't run yet.
Arguments:
Process - Supplies a pointer to the process being created.
SessionMapInfo - Supplies a pointer to the SessionMapInfo.
Return Value:
None.
Environment:
Kernel mode, APCs disabled.
--*/
{
Process->SessionMapInfo = SessionMapInfo;
INITIALIZE_DIRECTORY_TABLE_BASE (&Process->SessionParentBase,
SessionParentPage);
}
VOID
KiAttachRegion(
IN PKPROCESS Process
)
/*++
Routine Description:
Attaches the regions of the specified process
Arguments:
Process - Supplies a pointer to the process
Return Value:
None.
Environment:
Kernel mode, KiRegionSwapLock is held.
--*/
{
PREGION_MAP_INFO ProcessRegion;
PREGION_MAP_INFO MappedSession;
ProcessRegion = &Process->ProcessRegion;
MappedSession = Process->SessionMapInfo;
//
// attach the target user space
//
KiSetRegionRegister(MM_LOWEST_USER_ADDRESS,
KiMakeValidRegionRegister(ProcessRegion->RegionId, PAGE_SHIFT));
//
// attach the target session space
//
KiSetRegionRegister((PVOID)SADDRESS_BASE,
KiMakeValidRegionRegister(MappedSession->RegionId, PAGE_SHIFT));
}
VOID
KiDetachRegion(
VOID
)
/*++
Routine Description:
Restores the origial regions
Arguments:
VOID
Return Value:
None.
Environment:
Kernel mode, KiRegionSwapLock is held.
--*/
{
PKPROCESS Process;
PREGION_MAP_INFO ProcessRegion;
PREGION_MAP_INFO MappedSession;
//
// use KPROCESS from PCR
//
Process = (PKPROCESS)PCR->Pcb;
ProcessRegion = &Process->ProcessRegion;
MappedSession = Process->SessionMapInfo;
//
// attach the original user space
//
KiSetRegionRegister(MM_LOWEST_USER_ADDRESS,
KiMakeValidRegionRegister(ProcessRegion->RegionId, PAGE_SHIFT));
//
// attach the original session space
//
KiSetRegionRegister((PVOID)SADDRESS_BASE,
KiMakeValidRegionRegister(MappedSession->RegionId, PAGE_SHIFT));
}