FASTCALL
MiCheckPdeForSessionSpace(IN PVOID Address)
{
- /* Code not yet tested */
- ASSERT(FALSE);
- return STATUS_NOT_IMPLEMENTED;
+ MMPTE TempPde;
+ PMMPTE PointerPde;
+ PVOID SessionPageTable;
+ ULONG Index;
+
+ /* Is this a session PTE? */
+ if (MI_IS_SESSION_PTE(Address))
+ {
+ /* Make sure the PDE for session space is valid */
+ PointerPde = MiAddressToPde(MmSessionSpace);
+ if (!PointerPde->u.Hard.Valid)
+ {
+ /* This means there's no valid session, bail out */
+ DbgPrint("MiCheckPdeForSessionSpace: No current session for PTE %p\n",
+ Address);
+ DbgBreakPoint();
+ return STATUS_ACCESS_VIOLATION;
+ }
+
+ /* Now get the session-specific page table for this address */
+ SessionPageTable = MiPteToAddress(Address);
+ PointerPde = MiPteToAddress(Address);
+ if (PointerPde->u.Hard.Valid) return STATUS_WAIT_1;
+
+ /* It's not valid, so find it in the page table array */
+ Index = ((ULONG_PTR)SessionPageTable - (ULONG_PTR)MmSessionBase) >> 22;
+ TempPde.u.Long = MmSessionSpace->PageTables[Index].u.Long;
+ if (TempPde.u.Hard.Valid)
+ {
+ /* The copy is valid, so swap it in */
+ InterlockedExchange((PLONG)PointerPde, TempPde.u.Long);
+ return STATUS_WAIT_1;
+ }
+
+ /* We don't seem to have allocated a page table for this address yet? */
+ DbgPrint("MiCheckPdeForSessionSpace: No Session PDE for PTE %p, %p\n",
+ PointerPde->u.Long, SessionPageTable);
+ DbgBreakPoint();
+ return STATUS_ACCESS_VIOLATION;
+ }
+
+ /* Is the address also a session address? If not, we're done */
+ if (!MI_IS_SESSION_ADDRESS(Address)) return STATUS_SUCCESS;
+
+ /* It is, so again get the PDE for session space */
+ PointerPde = MiAddressToPde(MmSessionSpace);
+ if (!PointerPde->u.Hard.Valid)
+ {
+ /* This means there's no valid session, bail out */
+ DbgPrint("MiCheckPdeForSessionSpace: No current session for VA %p\n",
+ Address);
+ DbgBreakPoint();
+ return STATUS_ACCESS_VIOLATION;
+ }
+
+ /* Now get the PDE for the address itself */
+ PointerPde = MiAddressToPde(Address);
+ if (!PointerPde->u.Hard.Valid)
+ {
+ /* Do the swap, we should be good to go */
+ Index = ((ULONG_PTR)Address - (ULONG_PTR)MmSessionBase) >> 22;
+ PointerPde->u.Long = MmSessionSpace->PageTables[Index].u.Long;
+ if (PointerPde->u.Hard.Valid) return STATUS_WAIT_1;
+
+ /* We had not allocated a page table for this session address yet, fail! */
+ DbgPrint("MiCheckPdeForSessionSpace: No Session PDE for VA %p, %p\n",
+ PointerPde->u.Long, Address);
+ DbgBreakPoint();
+ return STATUS_ACCESS_VIOLATION;
+ }
+
+ /* It's valid, so there's nothing to do */
+ return STATUS_SUCCESS;
}
NTSTATUS
/* Check if we need a zero page */
NeedZero = (OldIrql != MM_NOIRQL);
-#if 0
/* Session-backed image views must be zeroed */
if ((Process == HYDRA_PROCESS) &&
((MI_IS_SESSION_IMAGE_ADDRESS(Address)) ||
- ((Address >= (PVOID)MiSessionViewStart) &&
- (Address < (PVOID)MiSessionSpaceWs))))
+ ((Address >= MiSessionViewStart) && (Address < MiSessionSpaceWs))))
{
NeedZero = TRUE;
}
-#endif
+
/* Hardcode unknown color */
Color = 0xFFFFFFFF;
}
KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
return STATUS_SUCCESS;
}
- else
- {
- /* Not yet handled */
- ASSERT(FALSE);
- }
}
/* Check if this was a session PTE that needs to remap the session PDE */
if (MI_IS_SESSION_PTE(Address))
{
- /* Not yet handled */
- ASSERT(FALSE);
+ /* Do the remapping */
+ Status = MiCheckPdeForSessionSpace(Address);
+ if (!NT_SUCCESS(Status))
+ {
+ /* It failed, this address is invalid */
+ KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
+ (ULONG_PTR)Address,
+ StoreInstruction,
+ (ULONG_PTR)TrapInformation,
+ 6);
+ }
}
/* Check for a fault on the page table or hyperspace */
}
else
{
- /* Not yet handled */
- ASSERT(FALSE);
+ /* Use the session process and working set */
+ CurrentProcess = HYDRA_PROCESS;
+ WorkingSet = &MmSessionSpace->GlobalVirtualAddress->Vm;
+
+ /* Make sure we don't have a recursive working set lock */
+ if ((CurrentThread->OwnsSessionWorkingSetExclusive) ||
+ (CurrentThread->OwnsSessionWorkingSetShared))
+ {
+ /* Fail */
+ return STATUS_IN_PAGE_ERROR | 0x10000000;
+ }
}
/* Acquire the working set lock */
}
}
- /* Case not yet handled */
- ASSERT(!IsSessionAddress);
+ /* Check for read-only write in session space */
+ if ((IsSessionAddress) &&
+ (StoreInstruction) &&
+ !(TempPte.u.Hard.Write))
+ {
+ /* Sanity check */
+ ASSERT(MI_IS_SESSION_IMAGE_ADDRESS(Address));
+
+ /* Was this COW? */
+ if (TempPte.u.Hard.CopyOnWrite == 0)
+ {
+ /* Then this is not allowed */
+ KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
+ (ULONG_PTR)Address,
+ (ULONG_PTR)TempPte.u.Long,
+ (ULONG_PTR)TrapInformation,
+ 13);
+ }
+
+ /* Otherwise, handle COW */
+ ASSERT(FALSE);
+ }
/* Release the working set */
MiUnlockWorkingSet(CurrentThread, WorkingSet);
/* Get the prototype PTE! */
ProtoPte = MiProtoPteToPte(&TempPte);
- /* Case not yet handled */
- ASSERT(!IsSessionAddress);
+ /* Do we need to locate the prototype PTE in session space? */
+ if ((IsSessionAddress) &&
+ (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED))
+ {
+ /* Yep, go find it as well as the VAD for it */
+ ProtoPte = MiCheckVirtualAddress(Address,
+ &ProtectionCode,
+ &Vad);
+ ASSERT(ProtoPte != NULL);
+ }
}
else
{
//
// Get the Locale ID
//
-#if ROS_HAS_SESSIONS
return ((PMM_SESSION_SPACE)Process->Session)->LocaleId;
-#endif
}
}
//
// Session ID
//
- if (Process->Session) Peb->SessionId = 0; // MmGetSessionId(Process);
+ MmGetSessionId(Process);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Let go of the system PTE */
MiReleaseSystemPtes(PointerPte, 1, SystemPteSpace);
+
+ /* Add the process to the session */
+ MiSessionAddProcess(Process);
return TRUE;
}
#endif
/* Only support this */
ASSERT(Process->AddressSpaceInitialized == 2);
+ /* Remove from the session */
+ MiSessionRemoveProcess();
+
/* Lock the process address space from changes */
MmLockAddressSpace(&Process->Vm);
/* Free the VAD memory */
ExFreePool(Vad);
}
+
/* Delete the shared user data section */
MiDeleteVirtualAddresses(USER_SHARED_DATA, USER_SHARED_DATA, NULL);
/* Release the PFN lock */
KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
- /* No support for sessions yet */
- ASSERT(Process->Session == 0);
+ /* Drop a reference on the session */
+ if (Process->Session) MiReleaseProcessReferenceToSessionDataPage(Process->Session);
/* Clear out the PDE pages */
Process->Pcb.DirectoryTableBase[0] = 0;
KeReleaseQueuedSpinLock(LockQueueExpansionLock, OldIrql);
}
+ULONG
+NTAPI
+MmGetSessionId(IN PEPROCESS Process)
+{
+ PMM_SESSION_SPACE SessionGlobal;
+
+ /* The session leader is always session zero */
+ if (Process->Vm.Flags.SessionLeader == 1) return 0;
+
+ /* Otherwise, get the session global, and read the session ID from it */
+ SessionGlobal = (PMM_SESSION_SPACE)Process->Session;
+ if (!SessionGlobal) return 0;
+ return SessionGlobal->SessionId;
+}
+
+VOID
+NTAPI
+MiReleaseProcessReferenceToSessionDataPage(IN PMM_SESSION_SPACE SessionGlobal)
+{
+ ULONG i, SessionId;
+ PMMPTE PointerPte;
+ PFN_NUMBER PageFrameIndex[MI_SESSION_DATA_PAGES_MAXIMUM];
+ PMMPFN Pfn1;
+ KIRQL OldIrql;
+
+ /* Is there more than just this reference? If so, bail out */
+ if (InterlockedDecrement(&SessionGlobal->ProcessReferenceToSession)) return;
+
+ /* Get the session ID */
+ SessionId = SessionGlobal->SessionId;
+ DPRINT1("Last process in sessino %d going down!!!\n", SessionId);
+
+ /* Free the session page tables */
+ ExFreePool(SessionGlobal->PageTables);
+ ASSERT(!MI_IS_PHYSICAL_ADDRESS(SessionGlobal));
+
+ /* Capture the data page PFNs */
+ PointerPte = MiAddressToPte(SessionGlobal);
+ for (i = 0; i < MiSessionDataPages; i++)
+ {
+ PageFrameIndex[i] = PFN_FROM_PTE(PointerPte + i);
+ }
+
+ /* Release them */
+ MiReleaseSystemPtes(PointerPte, MiSessionDataPages, SystemPteSpace);
+
+ /* Mark them as deleted */
+ for (i = 0; i < MiSessionDataPages; i++)
+ {
+ Pfn1 = MI_PFN_ELEMENT(PageFrameIndex[i]);
+ MI_SET_PFN_DELETED(Pfn1);
+ }
+
+ /* Loop every data page and drop a reference count */
+ OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+ for (i = 0; i < MiSessionDataPages; i++)
+ {
+ /* Sanity check that the page is correct, then decrement it */
+ Pfn1 = MI_PFN_ELEMENT(PageFrameIndex[i]);
+ ASSERT(Pfn1->u2.ShareCount == 1);
+ ASSERT(Pfn1->u3.e2.ReferenceCount == 1);
+ MiDecrementShareCount(Pfn1, PageFrameIndex[i]);
+ }
+
+ /* Done playing with pages, release the lock */
+ KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+
+ /* Decrement the number of data pages */
+ InterlockedDecrement(&MmSessionDataPages);
+
+ /* Free this session ID from the session bitmap */
+ KeAcquireGuardedMutex(&MiSessionIdMutex);
+ ASSERT(RtlCheckBit(MiSessionIdBitmap, SessionId));
+ RtlClearBit(MiSessionIdBitmap, SessionId);
+ KeReleaseGuardedMutex(&MiSessionIdMutex);
+}
+
+VOID
+NTAPI
+MiSessionRemoveProcess(VOID)
+{
+ PEPROCESS CurrentProcess = PsGetCurrentProcess();
+
+ /* If the process isn't already in a session, or if it's the leader... */
+ if (!(CurrentProcess->Flags & PSF_PROCESS_IN_SESSION_BIT) ||
+ (CurrentProcess->Vm.Flags.SessionLeader))
+ {
+ /* Then there's nothing to do */
+ return;
+ }
+
+ /* Sanity check */
+ ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE);
+
+ /* Remove the process from the list ,and dereference the session */
+ RemoveEntryList(&CurrentProcess->SessionProcessLinks);
+ //MiDereferenceSession();
+}
+
+VOID
+NTAPI
+MiSessionAddProcess(IN PEPROCESS NewProcess)
+{
+ PMM_SESSION_SPACE SessionGlobal;
+
+ /* The current process must already be in a session */
+ if (!(PsGetCurrentProcess()->Flags & PSF_PROCESS_IN_SESSION_BIT)) return;
+
+ /* Sanity check */
+ ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE);
+
+ /* Get the global session */
+ SessionGlobal = MmSessionSpace->GlobalVirtualAddress;
+
+ /* Increment counters */
+ InterlockedIncrement((PLONG)&SessionGlobal->ReferenceCount);
+ InterlockedIncrement(&SessionGlobal->ResidentProcessCount);
+ InterlockedIncrement(&SessionGlobal->ProcessReferenceToSession);
+
+ /* Set the session pointer */
+ ASSERT(NewProcess->Session == NULL);
+ NewProcess->Session = SessionGlobal;
+
+ /* Insert it into the process list */
+ InsertTailList(&SessionGlobal->ProcessList, &NewProcess->SessionProcessLinks);
+
+ /* Set the flag */
+ PspSetProcessFlag(NewProcess, PSF_PROCESS_IN_SESSION_BIT);
+}
+
+NTSTATUS
+NTAPI
+MiSessionInitializeWorkingSetList(VOID)
+{
+ KIRQL OldIrql;
+ PMMPTE PointerPte, PointerPde;
+ MMPTE TempPte;
+ ULONG Color, Index;
+ PFN_NUMBER PageFrameIndex;
+ PMM_SESSION_SPACE SessionGlobal;
+ BOOLEAN AllocatedPageTable;
+ PMMWSL WorkingSetList;
+
+ /* Get pointers to session global and the session working set list */
+ SessionGlobal = MmSessionSpace->GlobalVirtualAddress;
+ WorkingSetList = (PMMWSL)MiSessionSpaceWs;
+
+ /* Fill out the two pointers */
+ MmSessionSpace->Vm.VmWorkingSetList = WorkingSetList;
+ MmSessionSpace->Wsle = (PMMWSLE)WorkingSetList->UsedPageTableEntries;
+
+ /* Get the PDE for the working set, and check if it's already allocated */
+ PointerPde = MiAddressToPde(WorkingSetList);
+ if (PointerPde->u.Hard.Valid == 1)
+ {
+ /* Nope, we'll have to do it */
+ ASSERT(PointerPde->u.Hard.Global == 0);
+ AllocatedPageTable = FALSE;
+ }
+ else
+ {
+ /* Yep, that makes our job easier */
+ AllocatedPageTable = TRUE;
+ }
+
+ /* Get the PTE for the working set */
+ PointerPte = MiAddressToPte(WorkingSetList);
+
+ /* Initialize the working set lock, and lock the PFN database */
+ ExInitializePushLock(&SessionGlobal->Vm.WorkingSetMutex);
+ //MmLockPageableSectionByHandle(ExPageLockHandle);
+ OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+
+ /* Check if we need a page table */
+ if (AllocatedPageTable == TRUE)
+ {
+ /* Get a zeroed colored zero page */
+ Color = MI_GET_NEXT_COLOR();
+ PageFrameIndex = MiRemoveZeroPageSafe(Color);
+ if (!PageFrameIndex)
+ {
+ /* No zero pages, grab a free one */
+ PageFrameIndex = MiRemoveAnyPage(Color);
+
+ /* Zero it outside the PFN lock */
+ KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+ MiZeroPhysicalPage(PageFrameIndex);
+ OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+ }
+
+ /* Write a valid PDE for it */
+ TempPte.u.Long = ValidKernelPdeLocal.u.Long;
+ TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
+ MI_WRITE_VALID_PTE(PointerPde, TempPte);
+
+ /* Add this into the list */
+ Index = ((ULONG_PTR)WorkingSetList - (ULONG_PTR)MmSessionBase) >> 22;
+ MmSessionSpace->PageTables[Index] = TempPte;
+
+ /* Initialize the page directory page, and now zero the working set list itself */
+ MiInitializePfnForOtherProcess(PageFrameIndex,
+ PointerPde,
+ MmSessionSpace->SessionPageDirectoryIndex);
+ KeZeroPages(PointerPte, PAGE_SIZE);
+ }
+
+ /* Get a zeroed colored zero page */
+ Color = MI_GET_NEXT_COLOR();
+ PageFrameIndex = MiRemoveZeroPageSafe(Color);
+ if (!PageFrameIndex)
+ {
+ /* No zero pages, grab a free one */
+ PageFrameIndex = MiRemoveAnyPage(Color);
+
+ /* Zero it outside the PFN lock */
+ KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+ MiZeroPhysicalPage(PageFrameIndex);
+ OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+ }
+
+ /* Write a valid PTE for it */
+ TempPte.u.Long = ValidKernelPteLocal.u.Long;
+ TempPte.u.Hard.Dirty = TRUE;
+ TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
+
+ /* Initialize the working set list page */
+ MiInitializePfnAndMakePteValid(PageFrameIndex, PointerPte, TempPte);
+
+ /* Now we can release the PFN database lock */
+ KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+
+ /* Fill out the working set structure */
+ MmSessionSpace->Vm.Flags.SessionSpace = 1;
+ MmSessionSpace->Vm.MinimumWorkingSetSize = 20;
+ MmSessionSpace->Vm.MaximumWorkingSetSize = 384;
+ WorkingSetList->LastEntry = 20;
+ WorkingSetList->HashTable = NULL;
+ WorkingSetList->HashTableSize = 0;
+ WorkingSetList->Wsle = MmSessionSpace->Wsle;
+
+ /* FIXME: Handle list insertions */
+ ASSERT(SessionGlobal->WsListEntry.Flink == NULL);
+ ASSERT(SessionGlobal->WsListEntry.Blink == NULL);
+ ASSERT(SessionGlobal->Vm.WorkingSetExpansionLinks.Flink == NULL);
+ ASSERT(SessionGlobal->Vm.WorkingSetExpansionLinks.Blink == NULL);
+
+ /* All done, return */
+ //MmUnlockPageableImageSection(ExPageLockHandle);
+ return STATUS_SUCCESS;
+}
+
NTSTATUS
NTAPI
MiSessionCreateInternal(OUT PULONG SessionId)
MiSessionLeader(Process);
}
- /* FIXME: Actually create a session */
+ /* Create the session */
KeEnterCriticalRegion();
Status = MiSessionCreateInternal(SessionId);
+ if (!NT_SUCCESS(Status))
+ {
+ KeLeaveCriticalRegion();
+ return Status;
+ }
+
+ /* Set up the session working set */
+ Status = MiSessionInitializeWorkingSetList();
+ if (!NT_SUCCESS(Status))
+ {
+ /* Fail */
+ //MiDereferenceSession();
+ ASSERT(FALSE);
+ KeLeaveCriticalRegion();
+ return Status;
+ }
+
+ /* All done */
KeLeaveCriticalRegion();
/* Set and assert the flags, and return */
+ MmSessionSpace->u.Flags.Initialized = 1;
PspSetProcessFlag(Process, PSF_PROCESS_IN_SESSION_BIT);
ASSERT(MiSessionLeaderExists == 1);
return Status;