test: forksup.c File Reference

Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

# forksup.c File Reference

`#include "mi.h"`

Go to the source code of this file.

## Defines

#define | MM_FORK_SUCCEEDED 0
#define | MM_FORK_FAILED 1

## Functions

VOID | MiUpPfnReferenceCount (IN PFN_NUMBER Page, IN USHORT Count)
VOID | MiDownPfnReferenceCount (IN PFN_NUMBER Page, IN USHORT Count)
VOID | MiUpControlAreaRefs (IN PCONTROL_AREA ControlArea)
ULONG | MiDoneWithThisPageGetAnother (IN PPFN_NUMBER PageFrameIndex, IN PMMPTE PointerPde, IN PEPROCESS CurrentProcess)
VOID | MiUpForkPageShareCount (IN PMMPFN PfnForkPtePage)
VOID | MiUpCloneProcessRefCount (IN PMMCLONE_DESCRIPTOR Clone)
VOID | MiUpCloneProtoRefCount (IN PMMCLONE_BLOCK CloneProto, IN PEPROCESS CurrentProcess)
ULONG | MiHandleForkTransitionPte (IN PMMPTE PointerPte, IN PMMPTE PointerNewPte, IN PMMCLONE_BLOCK ForkProtoPte)
VOID | MiDownShareCountFlushEntireTb (IN PFN_NUMBER PageFrameIndex)
VOID | MiBuildForkPageTable (IN PFN_NUMBER PageFrameIndex, IN PMMPTE PointerPde, IN PMMPTE PointerNewPde, IN PFN_NUMBER PdePhysicalPage, IN PMMPFN PfnPdPage)
VOID | MiRetrievePageDirectoryFrames (IN PFN_NUMBER RootPhysicalPage, OUT PPFN_NUMBER PageDirectoryFrames)
NTSTATUS | MiCloneProcessAddressSpace (IN PEPROCESS ProcessToClone, IN PEPROCESS ProcessToInitialize, IN PFN_NUMBER RootPhysicalPage, IN PFN_NUMBER HyperPhysicalPage)
ULONG | MiDecrementCloneBlockReference (IN PMMCLONE_DESCRIPTOR CloneDescriptor, IN PMMCLONE_BLOCK CloneBlock, IN PEPROCESS CurrentProcess)
VOID | MiWaitForForkToComplete (IN PEPROCESS CurrentProcess)

## Define Documentation

#define MM_FORK_FAILED 1

| Definition at line 92 of file forksup.c. Referenced by MiCloneProcessAddressSpace().

#define MM_FORK_SUCCEEDED 0

| Definition at line 91 of file forksup.c. Referenced by MiCloneProcessAddressSpace(), and MiDecrementCloneBlockReference().

## Function Documentation

VOID MiBuildForkPageTable | ( | IN PFN_NUMBER | PageFrameIndex,
|  | IN PMMPTE | PointerPde,
|  | IN PMMPTE | PointerNewPde,
|  | IN PFN_NUMBER | PdePhysicalPage,
|  | IN PMMPFN | PfnPdPage
| ) |

Definition at line 2380 of file forksup.c.

References ActiveAndValid, DemandZeroPde, LOCK_PFN, MI_PFN_ELEMENT, MI_SET_OWNER_IN_PTE, MI_WRITE_INVALID_PTE, _MMPFN::OriginalPte, _MMPFN::PteAddress, _MMPFN::PteFrame, TransitionPde, _MMPFN::u2, _MMPFN::u3, UNLOCK_PFN, and UserMode.

Referenced by MiCloneProcessAddressSpace().

    02387 {
    02388     KIRQL OldIrql;
    02389     PMMPFN Pfn1;
    02390
    02391     Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
    02392
    02393     //
    02394     // The PFN lock must be held while initializing the
    02395     // frame to prevent those scanning the database for
    02396     // free frames from taking it after we fill in the
    02397     // u2 field.
    02398     //
    02399
    02400     LOCK_PFN (OldIrql);
    02401
    02402     Pfn1->OriginalPte = DemandZeroPde;
    02403     Pfn1->u2.ShareCount = 1;
    02404     Pfn1->u3.e2.ReferenceCount = 1;
    02405     Pfn1->PteAddress = PointerPde;
    02406     Pfn1->u3.e1.Modified = 1;
    02407     Pfn1->u3.e1.PageLocation = ActiveAndValid;
    02408     Pfn1->PteFrame = PdePhysicalPage;
    02409
    02410     //
    02411     // Increment the share count for the page containing
    02412     // this PTE as the PTE is in transition.
    02413     //
    02414
    02415     PfnPdPage->u2.ShareCount += 1;
    02416
    02417     //
    02418     // Put the PDE into the transition state as it is not
    02419     // really mapped and decrement share count does not
    02420     // put private pages into transition, only prototypes.
    02421     //
    02422
    02423     MI_WRITE_INVALID_PTE (PointerNewPde, TransitionPde);
    02424
    02425     //
    02426     // Make the PTE owned by user mode.
    02427     //
    02428
    02429 #ifndef _ALPHA_
    02430     MI_SET_OWNER_IN_PTE (PointerNewPde, UserMode);
    02431 #endif //_ALPHA_
    02432     PointerNewPde->u.Trans.PageFrameNumber = PageFrameIndex;
    02433
    02434     UNLOCK_PFN (OldIrql);
    02435 }

NTSTATUS MiCloneProcessAddressSpace | ( | IN PEPROCESS | ProcessToClone,
|  | IN PEPROCESS | ProcessToInitialize,
|  | IN PFN_NUMBER | RootPhysicalPage,
|  | IN PFN_NUMBER | HyperPhysicalPage
| ) |

Definition at line 100 of file forksup.c.

References _EPROCESS::AddressSpaceDeleted, ASSERT, BYTE_OFFSET, _MMCLONE_DESCRIPTOR::CloneHeader, _MMCLONE_HEADER::ClonePtes, _MMVAD::ControlArea, DbgPrint, _MMCLONE_DESCRIPTOR::EndingVpn, _MMVAD::EndingVpn, ExAllocatePoolWithTag, EXCEPTION_EXECUTE_HANDLER, ExFreePool(), FALSE, _EPROCESS::ForkInProgress, _EPROCESS::ForkWasSuccessful, HighPagePriority, IS_PTE_NOT_DEMAND_ZERO, KeAttachProcess(), KeBugCheckEx(), KeDelayExecutionThread(), KeDetachProcess(), KernelMode, LOCK_ADDRESS_SPACE, LOCK_WS, MDL_PAGES_LOCKED, _MDL::MdlFlags, MI_GET_PAGE_FRAME_FROM_PTE, MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE, MI_GET_PROTECTION_FROM_SOFT_PTE, MI_GET_PROTECTION_FROM_WSLE, MI_INCREMENT_USED_PTES_BY_HANDLE, MI_MAKE_PROTECT_WRITE_COPY, MI_MAKE_VALID_PTE_WRITE_COPY, MI_PFN_ELEMENT, MI_PTE_LOOKUP_NEEDED, MI_VPN_TO_VA, MI_WRITE_INVALID_PTE, MiBuildForkPageTable(), MiCheckPdeForPagedPool(), MiDoesPdeExistAndMakeValid(), MiDoesPpeExistAndMakeValid, MiDoneWithThisPageGetAnother(), MiDownPfnReferenceCount(), MiDownShareCountFlushEntireTb(), MiGetFirstClone, MiGetFirstVad, MiGetNextClone, MiGetNextVad, MiGetPdeAddress, MiGetPdeOffset, MiGetPpePdeOffset, MiGetPteAddress, MiGetPteOffset, MiGetVirtualAddressMappedByPte, MiHandleForkTransitionPte(), MiInsertClone, MiInsertVad(), MiIsPteOnPdeBoundary, MiLocateCloneAddress, MiLocateWsle(), MiLockPagedAddress(), MiMakeSystemAddressValid(), MiMapSinglePage(), MiProtoAddressForPte, MiPteToProto, MiRemoveClone, MiRetrievePageDirectoryFrames(), MiUnlockPagedAddress(), MiUnmapSinglePage(), MiUpCloneProcessRefCount(), MiUpCloneProtoRefCount(), MiUpControlAreaRefs(), MiUpForkPageShareCount(), MiUpPfnReferenceCount(), MM_DBG_FORK, MM_DECOMMIT, MM_FORK_FAILED, MM_FORK_SUCCEEDED, MM_MAX_COMMIT, MM_VIEW_SHARE, MmCached, MMCLONE_BLOCK, MMCLONE_DESCRIPTOR, MMCLONE_HEADER, MmInitializeMdl, MmMapLockedPagesSpecifyCache(), MmShortTime, MmUnmapLockedPages(), MmWorkingSetList, MmWsle, NonPagedPool, NT_SUCCESS, NTSTATUS(), NULL, _EPROCESS::NumberOfPrivatePages, _MMCLONE_HEADER::NumberOfProcessReferences, _MMCLONE_HEADER::NumberOfPtes, _MMCLONE_DESCRIPTOR::NumberOfPtes, _MMCLONE_DESCRIPTOR::NumberOfReferences, _MMPFN::OriginalPte, PAGE_ALIGN, PAGE_SHIFT, PAGE_SIZE, PAGED_CODE, PagedPool, _MMCLONE_DESCRIPTOR::PagedPoolQuotaCharge, _MMVAD::Parent, _MMCLONE_DESCRIPTOR::Parent, _EPROCESS::PeakVirtualSize, _EPROCESS::PhysicalVadList, PMMCLONE_HEADER, PMMVAD_SHORT, PsChargePoolQuota(), PsGetCurrentProcess, PsGetCurrentThread, PsReturnPoolQuota(), _MMPFN::PteAddress, _MMPFN::PteFrame, _MMCLONE_DESCRIPTOR::StartingVpn, _MMVAD::StartingVpn, TRUE, _MMSUPPORT::u, _MMVAD::u, _MMPTE::u, _MMPFN::u1, _MMWSLE::u1, _MMVAD::u2, _MMVAD::u3, _MMPFN::u3, UNLOCK_ADDRESS_SPACE, UNLOCK_WS, _MMWSL::UsedPageTableEntries, _MI_PHYSICAL_VIEW::Vad, _EPROCESS::VirtualSize, _EPROCESS::Vm, WSLE_NULL_INDEX, and WSLE_NUMBER.

Referenced by MmInitializeProcessAddressSpace().

    00109                    :
    00110
    00111     This routine stands on its head to produce a copy of the specified
    00112     process's address space in the process to initialize.  This
    00113     is done by examining each virtual address descriptor's inherit
    00114     attributes.  If the pages described by the VAD should be inherited,
    00115     each PTE is examined and copied into the new address space.
    00116
    00117     For private pages, fork prototype PTEs are constructed and the pages
    00118     become shared, copy-on-write, between the two processes.
    00119
    00120
    00121 Arguments:
    00122
    00123     ProcessToClone - Supplies the process whose address space should be
    00124                      cloned.
    00125
    00126     ProcessToInitialize - Supplies the process whose address space is to
    00127                           be created.
    00128
    00129     RootPhysicalPage - Supplies the physical page number of the top level
    00130                        page (parent on 64-bit systems) directory
    00131                        of the process to initialize.
    00132
    00133     HyperPhysicalPage - Supplies the physical page number of the page table
    00134                         page which maps hyperspace for the process to
    00135                         initialize.  This is only needed for 32-bit systems.
    00136
    00137 Return Value:
    00138
    00139     None.
    00140
    00141 Environment:
    00142
    00143     Kernel mode, APCs disabled.
    00144
    00145 --*/
    00146
    00147 {
    00148     PFN_NUMBER PpePhysicalPage;
    00149     PFN_NUMBER PdePhysicalPage;
    00150     PEPROCESS CurrentProcess;
    00151     PMMPTE PdeBase;
    00152     PMMCLONE_HEADER CloneHeader;
    00153     PMMCLONE_BLOCK CloneProtos;
    00154     PMMCLONE_DESCRIPTOR CloneDescriptor;
    00155     PMMVAD NewVad;
    00156     PMMVAD Vad;
    00157     PMMVAD NextVad;
    00158     PMMVAD *VadList;
    00159     PMMVAD FirstNewVad;
    00160     PMMCLONE_DESCRIPTOR *CloneList;
    00161     PMMCLONE_DESCRIPTOR FirstNewClone;
    00162     PMMCLONE_DESCRIPTOR Clone;
    00163     PMMCLONE_DESCRIPTOR NextClone;
    00164     PMMCLONE_DESCRIPTOR NewClone;
    00165     ULONG Attached;
    00166     ULONG CloneFailed;
    00167     ULONG VadInsertFailed;
    00168     WSLE_NUMBER WorkingSetIndex;
    00169     PVOID VirtualAddress;
    00170     NTSTATUS status;
    00171     PMMPFN Pfn2;
    00172     PMMPFN PfnPdPage;
    00173     MMPTE TempPte;
    00174     MMPTE PteContents;
    00175 #if defined (_X86PAE_)
    00176     PMDL MdlPageDirectory;
    00177     PPFN_NUMBER MdlPageFrames;
    00178     PFN_NUMBER PageDirectoryFrames[PD_PER_SYSTEM];
    00179     PFN_NUMBER MdlHackPageDirectory[(sizeof(MDL)/sizeof(PFN_NUMBER)) + PD_PER_SYSTEM];
    00180 #endif
    00181     PFN_NUMBER MdlPage;
    00182     PFN_NUMBER MdlDirPage;
    00183     PMMPTE PointerPte;
    00184     PMMPTE PointerPde;
    00185     PMMPTE PointerPpe;
    00186     PMMPTE LastPte;
    00187     PMMPTE PointerNewPte;
    00188     PMMPTE NewPteMappedAddress;
    00189     PMMPTE PointerNewPde;
    00190     PLIST_ENTRY NextEntry;
    00191     PMI_PHYSICAL_VIEW PhysicalView;
    00192     PFN_NUMBER PageFrameIndex;
    00193     PFN_NUMBER PageDirFrameIndex;
    00194     PMMCLONE_BLOCK ForkProtoPte;
    00195     PMMCLONE_BLOCK CloneProto;
    00196     PMMCLONE_BLOCK LockedForkPte;
    00197     PMMPTE ContainingPte;
    00198     ULONG NumberOfForkPtes;
    00199     PFN_NUMBER NumberOfPrivatePages;
    00200     PFN_NUMBER PageTablePage;
    00201     SIZE_T TotalPagedPoolCharge;
    00202     SIZE_T TotalNonPagedPoolCharge;
    00203     PMMPFN PfnForkPtePage;
    00204     PVOID UsedPageTableEntries;
    00205     ULONG ReleasedWorkingSetMutex;
    00206     ULONG FirstTime;
    00207     ULONG Waited;
    00208     ULONG i;
    00209     ULONG PpePdeOffset;
    00210 #if defined (_WIN64)
    00211     PVOID UsedPageDirectoryEntries;
    00212     PMMPTE PointerNewPpe;
    00213     PMMPTE PpeBase;
    00214     PMMPFN PfnPpPage;
    00215 #else
    00216     PMMWSL HyperBase;
    00217 #endif
    00218
    00219     PageTablePage = 2;
    00220     NumberOfForkPtes = 0;
    00221     Attached = FALSE;
    00222     PageFrameIndex = (PFN_NUMBER)-1;
    00223     PageDirFrameIndex = (PFN_NUMBER)-1;
    00224
    00225 #if DBG
    00226     if (MmDebug & MM_DBG_FORK) {
    00227         DbgPrint("beginning clone operation process to clone = %lx\n",
    00228             ProcessToClone);
    00229     }
    00230 #endif //DBG
    00231
    00232     PAGED_CODE();
    00233
    00234     if (ProcessToClone != PsGetCurrentProcess()) {
    00235         Attached = TRUE;
    00236         KeAttachProcess (&ProcessToClone->Pcb);
    00237     }
    00238
    00239 #if defined (_X86PAE_)
    00240     MiRetrievePageDirectoryFrames (RootPhysicalPage, PageDirectoryFrames);
    00241 #endif
    00242
    00243     CurrentProcess = ProcessToClone;
    00244
    00245     //
    00246     // Get the working set mutex and the address creation mutex
    00247     // of the process to clone.  This prevents page faults while we
    00248     // are examining the address map and prevents virtual address space
    00249     // from being created or deleted.
    00250     //
    00251
    00252     LOCK_ADDRESS_SPACE (CurrentProcess);
    00253
    00254     //
    00255     // Write-watch VAD bitmaps are not currently duplicated
    00256     // so fork is not allowed.
    00257     //
    00258
    00259     if (CurrentProcess->Vm.u.Flags.WriteWatch == 1) {
    00260         status = STATUS_INVALID_PAGE_PROTECTION;
    00261         goto ErrorReturn1;
    00262     }
    00263
    00264     //
    00265     // Check for AWE regions as they are not duplicated so fork is not allowed.
    00266     // Note that since this is a readonly list walk, the address space mutex
    00267     // is sufficient to synchronize properly.
    00268     //
    00269
    00270     NextEntry = CurrentProcess->PhysicalVadList.Flink;
    00271     while (NextEntry != &CurrentProcess->PhysicalVadList) {
    00272
    00273         PhysicalView = CONTAINING_RECORD(NextEntry,
    00274                                          MI_PHYSICAL_VIEW,
    00275                                          ListEntry);
    00276
    00277         if (PhysicalView->Vad->u.VadFlags.UserPhysicalPages == 1) {
    00278             status = STATUS_INVALID_PAGE_PROTECTION;
    00279             goto ErrorReturn1;
    00280         }
    00281
    00282         NextEntry = NextEntry->Flink;
    00283     }
    00284
    00285     //
    00286     // Make sure the address space was not deleted, if so, return an error.
    00287     //
    00288
    00289     if (CurrentProcess->AddressSpaceDeleted != 0) {
    00290         status = STATUS_PROCESS_IS_TERMINATING;
    00291         goto ErrorReturn1;
    00292     }
    00293
    00294     //
    00295     // Attempt to acquire the needed pool before starting the
    00296     // clone operation, this allows an easier failure path in
    00297     // the case of insufficient system resources.
    00298     //
    00299
    00300     NumberOfPrivatePages = CurrentProcess->NumberOfPrivatePages;
    00301
    00302     CloneProtos = ExAllocatePoolWithTag (PagedPool, sizeof(MMCLONE_BLOCK) *
    00303                                                 NumberOfPrivatePages,
    00304                                                 'lCmM');
    00305     if (CloneProtos == NULL) {
    00306         status = STATUS_INSUFFICIENT_RESOURCES;
    00307         goto ErrorReturn1;
    00308     }
    00309
    00310     CloneHeader = ExAllocatePoolWithTag (NonPagedPool,
    00311                                          sizeof(MMCLONE_HEADER),
    00312                                          '  mM');
    00313     if (CloneHeader == NULL) {
    00314         status = STATUS_INSUFFICIENT_RESOURCES;
    00315         goto ErrorReturn2;
    00316     }
    00317
    00318     CloneDescriptor = ExAllocatePoolWithTag (NonPagedPool,
    00319                                              sizeof(MMCLONE_DESCRIPTOR),
    00320                                              '  mM');
    00321     if (CloneDescriptor == NULL) {
    00322         status = STATUS_INSUFFICIENT_RESOURCES;
    00323         goto ErrorReturn3;
    00324     }
    00325
    00326     Vad = MiGetFirstVad (CurrentProcess);
    00327     VadList = &FirstNewVad;
    00328
    00329     while (Vad != (PMMVAD)NULL) {
    00330
    00331         //
    00332         // If the VAD does not go to the child, ignore it.
    00333         //
    00334
    00335         if ((Vad->u.VadFlags.UserPhysicalPages == 0) &&
    00336
    00337             ((Vad->u.VadFlags.PrivateMemory == 1) ||
    00338             (Vad->u2.VadFlags2.Inherit == MM_VIEW_SHARE))) {
    00339
    00340             NewVad = ExAllocatePoolWithTag (NonPagedPool, sizeof(MMVAD), ' daV');
    00341
    00342             if (NewVad == NULL) {
    00343
    00344                 //
    00345                 // Unable to allocate pool for all the VADs.  Deallocate
    00346                 // all VADs and other pool obtained so far.
    00347                 //
    00348
    00349                 *VadList = (PMMVAD)NULL;
    00350                 status = STATUS_INSUFFICIENT_RESOURCES;
    00351                 goto ErrorReturn4;
    00352             }
    00353             *VadList = NewVad;
    00354             VadList = &NewVad->Parent;
    00355         }
    00356         Vad = MiGetNextVad (Vad);
    00357     }
    00358
    00359     //
    00360     // Terminate list of VADs for new process.
    00361     //
    00362
    00363     *VadList = (PMMVAD)NULL;
    00364
    00365     //
    00366     // Charge the current process the quota for the paged and nonpaged
    00367     // global structures.  This consists of the array of clone blocks
    00368     // in paged pool and the clone header in non-paged pool.
    00369     //
    00370
    00371     try {
    00372         PsChargePoolQuota (CurrentProcess, PagedPool, sizeof(MMCLONE_BLOCK) *
    00373                                                 NumberOfPrivatePages);
    00374         PageTablePage = 1;
    00375         PsChargePoolQuota (CurrentProcess, NonPagedPool, sizeof(MMCLONE_HEADER));
    00376         PageTablePage = 0;
    00377
    00378     } except (EXCEPTION_EXECUTE_HANDLER) {
    00379
    00380         //
    00381         // Unable to charge quota for the clone blocks.
    00382         //
    00383
    00384         status = GetExceptionCode();
    00385         goto ErrorReturn4;
    00386     }
    00387
    00388     LOCK_WS (CurrentProcess);
    00389
    00390     ASSERT (CurrentProcess->ForkInProgress == NULL);
    00391
    00392     //
    00393     // Indicate to the pager that the current process is being
    00394     // forked.  This blocks other threads in that process from
    00395     // modifying clone blocks counts and contents.
    00396     //
    00397
    00398     CurrentProcess->ForkInProgress = PsGetCurrentThread();
    00399
    00400 #if defined (_WIN64)
    00401
    00402     //
    00403     // Increment the reference count for the pages which are being "locked"
    00404     // in MDLs.  This prevents the page from being reused while it is
    00405     // being double mapped.  Note we have a count of 3 below because in addition
    00406     // to the PPE, we also set up a initial dummy PDE and PTE.
    00407     //
    00408
    00409     MiUpPfnReferenceCount (RootPhysicalPage, 3);
    00410
    00411     //
    00412     // Map the page directory parent page into the system address
    00413     // space.  This is accomplished by building an MDL to describe the
    00414     // Page directory parent page.
    00415     //
    00416
    00417     PpeBase = (PMMPTE)MiMapSinglePage (NULL,
    00418                                        RootPhysicalPage,
    00419                                        MmCached,
    00420                                        HighPagePriority);
    00421
    00422     if (PpeBase == NULL) {
    00423         MiDownPfnReferenceCount (RootPhysicalPage, 3);
    00424         CurrentProcess->ForkInProgress = NULL;
    00425         UNLOCK_WS (CurrentProcess);
    00426         status = STATUS_INSUFFICIENT_RESOURCES;
    00427         goto ErrorReturn4;
    00428     }
    00429
    00430     PfnPpPage = MI_PFN_ELEMENT (RootPhysicalPage);
    00431
    00432 #else
    00433
    00434 #if !defined (_X86PAE_)
    00435     MiUpPfnReferenceCount (RootPhysicalPage, 1);
    00436 #endif
    00437
    00438 #endif
    00439
    00440     //
    00441     // Initialize a page directory map so it can be
    00442     // unlocked in the loop and the end of the loop without
    00443     // any testing to see if has a valid value the first time through.
    00444     // Note this is a dummy map for 64-bit systems and a real one for 32-bit.
    00445     //
    00446
    00447 #if !defined (_X86PAE_)
    00448
    00449     MdlDirPage = RootPhysicalPage;
    00450
    00451     PdePhysicalPage = RootPhysicalPage;
    00452
    00453     PdeBase = (PMMPTE)MiMapSinglePage (NULL,
    00454                                        MdlDirPage,
    00455                                        MmCached,
    00456                                        HighPagePriority);
    00457
    00458     if (PdeBase == NULL) {
    00459 #if defined (_WIN64)
    00460         MiDownPfnReferenceCount (RootPhysicalPage, 3);
    00461         MiUnmapSinglePage (PpeBase);
    00462 #else
    00463         MiDownPfnReferenceCount (RootPhysicalPage, 1);
    00464 #endif
    00465         CurrentProcess->ForkInProgress = NULL;
    00466         UNLOCK_WS (CurrentProcess);
    00467         status = STATUS_INSUFFICIENT_RESOURCES;
    00468         goto ErrorReturn4;
    00469     }
    00470
    00471 #else
    00472
    00473     //
    00474     // All 4 page directory pages need to be mapped for PAE so the heavyweight
    00475     // mapping must be used.
    00476     //
    00477
    00478     MdlPageDirectory = (PMDL)&MdlHackPageDirectory[0];
    00479
    00480     MmInitializeMdl(MdlPageDirectory, (PVOID)PDE_BASE, PD_PER_SYSTEM * PAGE_SIZE);
    00481     MdlPageDirectory->MdlFlags |= MDL_PAGES_LOCKED;
    00482
    00483     MdlPageFrames = (PPFN_NUMBER)(MdlPageDirectory + 1);
    00484
    00485     for (i = 0; i < PD_PER_SYSTEM; i += 1) {
    00486         *(MdlPageFrames + i) = PageDirectoryFrames[i];
    00487         MiUpPfnReferenceCount (PageDirectoryFrames[i], 1);
    00488     }
    00489
    00490     PdePhysicalPage = RootPhysicalPage;
    00491
    00492     PdeBase = (PMMPTE)MmMapLockedPagesSpecifyCache (MdlPageDirectory,
    00493                                                     KernelMode,
    00494                                                     MmCached,
    00495                                                     NULL,
    00496                                                     FALSE,
    00497                                                     HighPagePriority);
    00498
    00499     if (PdeBase == NULL) {
    00500         for (i = 0; i < PD_PER_SYSTEM; i += 1) {
    00501             MiDownPfnReferenceCount (PageDirectoryFrames[i], 1);
    00502         }
    00503         CurrentProcess->ForkInProgress = NULL;
    00504         UNLOCK_WS (CurrentProcess);
    00505         status = STATUS_INSUFFICIENT_RESOURCES;
    00506         goto ErrorReturn4;
    00507     }
    00508
    00509 #endif
    00510
    00511     PfnPdPage = MI_PFN_ELEMENT (RootPhysicalPage);
    00512
    00513 #if !defined (_WIN64)
    00514
    00515     //
    00516     // Map hyperspace so target UsedPageTable entries can be incremented.
    00517     //
    00518
    00519     MiUpPfnReferenceCount (HyperPhysicalPage, 2);
    00520
    00521     HyperBase = (PMMWSL)MiMapSinglePage (NULL,
    00522                                          HyperPhysicalPage,
    00523                                          MmCached,
    00524                                          HighPagePriority);
    00525
    00526     if (HyperBase == NULL) {
    00527         MiDownPfnReferenceCount (HyperPhysicalPage, 2);
    00528 #if !defined (_X86PAE_)
    00529         MiDownPfnReferenceCount (RootPhysicalPage, 1);
    00530         MiUnmapSinglePage (PdeBase);
    00531 #else
    00532         for (i = 0; i < PD_PER_SYSTEM; i += 1) {
    00533             MiDownPfnReferenceCount (PageDirectoryFrames[i], 1);
    00534         }
    00535         MmUnmapLockedPages (PdeBase, MdlPageDirectory);
    00536 #endif
    00537         CurrentProcess->ForkInProgress = NULL;
    00538         UNLOCK_WS (CurrentProcess);
    00539         status = STATUS_INSUFFICIENT_RESOURCES;
    00540         goto ErrorReturn4;
    00541     }
    00542 #endif
    00543
    00544     //
    00545     // Initialize a page table MDL to lock and map the hyperspace page so it
    00546     // can be unlocked in the loop and the end of the loop without
    00547     // any testing to see if has a valid value the first time through.
    00548     //
    00549
    00550 #if defined (_WIN64)
    00551     MdlPage = RootPhysicalPage;
    00552 #else
    00553     MdlPage = HyperPhysicalPage;
    00554 #endif
    00555
    00556     NewPteMappedAddress = (PMMPTE)MiMapSinglePage (NULL,
    00557                                                    MdlPage,
    00558                                                    MmCached,
    00559                                                    HighPagePriority);
    00560
    00561     if (NewPteMappedAddress == NULL) {
    00562
    00563 #if defined (_WIN64)
    00564
    00565         MiDownPfnReferenceCount (RootPhysicalPage, 3);
    00566         MiUnmapSinglePage (PpeBase);
    00567         MiUnmapSinglePage (PdeBase);
    00568
    00569 #else
    00570         MiDownPfnReferenceCount (HyperPhysicalPage, 2);
    00571         MiUnmapSinglePage (HyperBase);
    00572 #if !defined (_X86PAE_)
    00573         MiDownPfnReferenceCount (RootPhysicalPage, 1);
    00574         MiUnmapSinglePage (PdeBase);
    00575 #else
    00576         for (i = 0; i < PD_PER_SYSTEM; i += 1) {
    00577             MiDownPfnReferenceCount (PageDirectoryFrames[i], 1);
    00578         }
    00579         MmUnmapLockedPages (PdeBase, MdlPageDirectory);
    00580 #endif
    00581
    00582 #endif
    00583
    00584         CurrentProcess->ForkInProgress = NULL;
    00585         UNLOCK_WS (CurrentProcess);
    00586         status = STATUS_INSUFFICIENT_RESOURCES;
    00587         goto ErrorReturn4;
    00588     }
    00589
    00590     PointerNewPte = NewPteMappedAddress;
    00591
    00592     //
    00593     // Build new clone prototype PTE block and descriptor, note that
    00594     // each prototype PTE has a reference count following it.
    00595     //
    00596
    00597     ForkProtoPte = CloneProtos;
    00598
    00599     LockedForkPte = ForkProtoPte;
    00600     MiLockPagedAddress (LockedForkPte, FALSE);
    00601
    00602     CloneHeader->NumberOfPtes = (ULONG)NumberOfPrivatePages;
    00603     CloneHeader->NumberOfProcessReferences = 1;
    00604     CloneHeader->ClonePtes = CloneProtos;
    00605
    00606
    00607
    00608     CloneDescriptor->StartingVpn = (ULONG_PTR)CloneProtos;
    00609     CloneDescriptor->EndingVpn = (ULONG_PTR)((ULONG_PTR)CloneProtos +
    00610                             NumberOfPrivatePages *
    00611                               sizeof(MMCLONE_BLOCK));
    00612     CloneDescriptor->NumberOfReferences = 0;
    00613     CloneDescriptor->NumberOfPtes = (ULONG)NumberOfPrivatePages;
    00614     CloneDescriptor->CloneHeader = CloneHeader;
    00615     CloneDescriptor->PagedPoolQuotaCharge = sizeof(MMCLONE_BLOCK) *
    00616                                 NumberOfPrivatePages;
    00617
    00618     //
    00619     // Insert the clone descriptor for this fork operation into the
    00620     // process which was cloned.
    00621     //
    00622
    00623     MiInsertClone (CloneDescriptor);
    00624
    00625     //
    00626     // Examine each virtual address descriptor and create the
    00627     // proper structures for the new process.
    00628     //
    00629
    00630     Vad = MiGetFirstVad (CurrentProcess);
    00631     NewVad = FirstNewVad;
    00632
    00633     while (Vad != (PMMVAD)NULL) {
    00634
    00635         //
    00636         // Examine the VAD to determine its type and inheritance
    00637         // attribute.
    00638         //
    00639
    00640         if ((Vad->u.VadFlags.UserPhysicalPages == 0) &&
    00641
    00642             ((Vad->u.VadFlags.PrivateMemory == 1) ||
    00643             (Vad->u2.VadFlags2.Inherit == MM_VIEW_SHARE))) {
    00644
    00645             //
    00646             // The virtual address descriptor should be shared in the
    00647             // forked process.
    00648             //
    00649
    00650             //
    00651             // Make a copy of the VAD for the new process, the new VADs
    00652             // are preallocated and linked together through the parent
    00653             // field.
    00654             //
    00655
    00656             NextVad = NewVad->Parent;
    00657
    00658
    00659             if (Vad->u.VadFlags.PrivateMemory == 1) {
    00660                 *(PMMVAD_SHORT)NewVad = *(PMMVAD_SHORT)Vad;
    00661                 NewVad->u.VadFlags.NoChange = 0;
    00662             } else {
    00663                 *NewVad = *Vad;
    00664             }
    00665
    00666             if (NewVad->u.VadFlags.NoChange) {
    00667                 if ((NewVad->u2.VadFlags2.OneSecured) ||
    00668                     (NewVad->u2.VadFlags2.MultipleSecured)) {
    00669
    00670                     //
    00671                     // Eliminate these as the memory was secured
    00672                     // only in this process, not in the new one.
    00673                     //
    00674
    00675                     NewVad->u2.VadFlags2.OneSecured = 0;
    00676                     NewVad->u2.VadFlags2.MultipleSecured = 0;
    00677                     NewVad->u2.VadFlags2.StoredInVad = 0;
    00678                     NewVad->u3.List.Flink = NULL;
    00679                     NewVad->u3.List.Blink = NULL;
    00680                 }
    00681                 if (NewVad->u2.VadFlags2.SecNoChange == 0) {
    00682                     NewVad->u.VadFlags.NoChange = 0;
    00683                 }
    00684             }
    00685             NewVad->Parent = NextVad;
    00686
    00687             //
    00688             // If the VAD refers to a section, up the view count for that
    00689             // section.  This requires the PFN mutex to be held.
    00690             //
    00691
    00692             if ((Vad->u.VadFlags.PrivateMemory == 0) &&
    00693                 (Vad->ControlArea != (PCONTROL_AREA)NULL)) {
    00694
    00695                 //
    00696                 // Increment the count of the number of views for the
    00697                 // section object.  This requires the PFN mutex to be held.
    00698                 //
    00699
    00700                 MiUpControlAreaRefs (Vad->ControlArea);
    00701             }
    00702
    00703             //
    00704             // Examine each PTE and create the appropriate PTE for the
    00705             // new process.
    00706             //
    00707
    00708             PointerPde = MiGetPdeAddress (MI_VPN_TO_VA (Vad->StartingVpn));
    00709             PointerPte = (volatile PMMPTE) MiGetPteAddress (
    00710                                           MI_VPN_TO_VA (Vad->StartingVpn));
    00711             LastPte = MiGetPteAddress (MI_VPN_TO_VA (Vad->EndingVpn));
    00712             FirstTime = TRUE;
    00713
    00714             while ((PMMPTE)PointerPte <= LastPte) {
    00715
    00716                 //
    00717                 // For each PTE contained in the VAD check the page table
    00718                 // page, and if non-zero, make the appropriate modifications
    00719                 // to copy the PTE to the new process.
    00720                 //
    00721
    00722                 if ((FirstTime) || MiIsPteOnPdeBoundary (PointerPte)) {
    00723
    00724                     PointerPpe = MiGetPdeAddress (PointerPte);
    00725                     PointerPde = MiGetPteAddress (PointerPte);
    00726
    00727                     do {
    00728
    00729                         while (!MiDoesPpeExistAndMakeValid (PointerPpe,
    00730                                                             CurrentProcess,
    00731                                                             FALSE,
    00732                                                             &Waited)) {
    00733
    00734                             //
    00735                             // Page directory parent is empty, go to the next one.
    00736                             //
    00737
    00738                             PointerPpe += 1;
    00739                             PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
    00740                             PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
    00741
    00742                             if ((PMMPTE)PointerPte > LastPte) {
    00743
    00744                                 //
    00745                                 // All done with this VAD, exit loop.
    00746                                 //
    00747
    00748                                 goto AllDone;
    00749                             }
    00750                         }
    00751
    00752                         Waited = 0;
    00753
    00754                         while (!MiDoesPdeExistAndMakeValid (PointerPde,
    00755                                                             CurrentProcess,
    00756                                                             FALSE,
    00757                                                             &Waited)) {
    00758
    00759                             //
    00760                             // This page directory is empty, go to the next one.
    00761                             //
    00762
    00763                             PointerPde += 1;
    00764                             PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
    00765
    00766                             if ((PMMPTE)PointerPte > LastPte) {
    00767
    00768                                 //
    00769                                 // All done with this VAD, exit loop.
    00770                                 //
    00771
    00772                                 goto AllDone;
    00773                             }
    00774 #if defined (_WIN64)
    00775                             if (MiIsPteOnPdeBoundary (PointerPde)) {
    00776                                 PointerPpe = MiGetPteAddress (PointerPde);
    00777                                 Waited = 1;
    00778                                 break;
    00779                             }
    00780 #endif
    00781                         }
    00782
    00783                     } while (Waited != 0);
    00784
    00785                     FirstTime = FALSE;
    00786
    00787 #if defined (_WIN64)
    00788                     //
    00789                     // Calculate the address of the PPE in the new process's
    00790                     // page directory parent page.
    00791                     //
    00792
    00793                     PointerNewPpe = &PpeBase[MiGetPdeOffset(PointerPte)];
    00794
    00795                     if (PointerNewPpe->u.Long == 0) {
    00796
    00797                         //
    00798                         // No physical page has been allocated yet, get a page
    00799                         // and map it in as a transition page.  This will
    00800                         // become a page table page for the new process.
    00801                         //
    00802
    00803                         ReleasedWorkingSetMutex =
    00804                                 MiDoneWithThisPageGetAnother (&PageDirFrameIndex,
    00805                                                               PointerPpe,
    00806                                                               CurrentProcess);
    00807
    00808                         MI_ZERO_USED_PAGETABLE_ENTRIES (MI_PFN_ELEMENT(PageDirFrameIndex));
    00809
    00810                         if (ReleasedWorkingSetMutex) {
    00811
    00812                             do {
    00813
    00814                                 MiDoesPpeExistAndMakeValid (PointerPpe,
    00815                                                             CurrentProcess,
    00816                                                             FALSE,
    00817                                                             &Waited);
    00818
    00819                                 Waited = 0;
    00820
    00821                                 MiDoesPdeExistAndMakeValid (PointerPde,
    00822                                                             CurrentProcess,
    00823                                                             FALSE,
    00824                                                             &Waited);
    00825                             } while (Waited != 0);
    00826                         }
    00827
    00828                         //
    00829                         // Hand initialize this PFN as normal initialization
    00830                         // would do it for the process whose context we are
    00831                         // attached to.
    00832                         //
    00833                         // The PFN lock must be held while initializing the
    00834                         // frame to prevent those scanning the database for
    00835                         // free frames from taking it after we fill in the
    00836                         // u2 field.
    00837                         //
    00838
    00839                         MiBuildForkPageTable (PageDirFrameIndex,
    00840                                               PointerPpe,
    00841                                               PointerNewPpe,
    00842                                               RootPhysicalPage,
    00843                                               PfnPpPage);
    00844
    00845                         //
    00846                         // Map the new page directory page into the system
    00847                         // portion of the address space.  Note that hyperspace
    00848                         // cannot be used as other operations (allocating
    00849                         // nonpaged pool at DPC level) could cause the
    00850                         // hyperspace page being used to be reused.
    00851                         //
    00852
    00853                         MiDownPfnReferenceCount (MdlDirPage, 1);
    00854
    00855                         MdlDirPage = PageDirFrameIndex;
    00856
    00857                         ASSERT (PdeBase != NULL);
    00858
    00859                         PdeBase = (PMMPTE)MiMapSinglePage (PdeBase,
    00860                                                            MdlDirPage,
    00861                                                            MmCached,
    00862                                                            HighPagePriority);
    00863
    00864                         MiUpPfnReferenceCount (MdlDirPage, 1);
    00865
    00866                         PointerNewPde = PdeBase;
    00867                     }
    00868                     else {
    00869                         ASSERT (PointerNewPpe->u.Hard.Valid == 1 ||
    00870                                 PointerNewPpe->u.Soft.Transition == 1);
    00871
    00872                         if (PointerNewPpe->u.Hard.Valid == 1) {
    00873                             PageDirFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerNewPpe);
    00874                         }
    00875                         else {
    00876                             PageDirFrameIndex = MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (PointerNewPpe);
    00877                         }
    00878                     }
    00879
    00880                     //
    00881                     // Calculate the address of the new PDE to build.
    00882                     // Note that FirstTime could be true, yet the page
    00883                     // directory page might already be built.
    00884                     //
    00885
    00886                     PointerNewPde = (PMMPTE)((ULONG_PTR)PAGE_ALIGN(PointerNewPde) |
    00887                                             BYTE_OFFSET (PointerPde));
    00888
    00889                     PdePhysicalPage = PageDirFrameIndex;
    00890
    00891                     PfnPdPage = MI_PFN_ELEMENT (PdePhysicalPage);
    00892
    00893                     UsedPageDirectoryEntries = (PVOID)PfnPdPage;
    00894 #endif
    00895
    00896                     //
    00897                     // Calculate the address of the PDE in the new process's
    00898                     // page directory page.
    00899                     //
    00900
    00901                     PpePdeOffset = MiGetPpePdeOffset(MiGetVirtualAddressMappedByPte(PointerPte));
    00902                     PointerNewPde = &PdeBase[PpePdeOffset];
    00903
    00904                     if (PointerNewPde->u.Long == 0) {
    00905
    00906                         //
    00907                         // No physical page has been allocated yet, get a page
    00908                         // and map it in as a transition page.  This will
    00909                         // become a page table page for the new process.
    00910                         //
    00911
    00912                         ReleasedWorkingSetMutex =
    00913                                 MiDoneWithThisPageGetAnother (&PageFrameIndex,
    00914                                                               PointerPde,
    00915                                                               CurrentProcess);
    00916
    00917                         if (ReleasedWorkingSetMutex) {
    00918
    00919                             do {
    00920
    00921                                 MiDoesPpeExistAndMakeValid (PointerPpe,
    00922                                                             CurrentProcess,
    00923                                                             FALSE,
    00924                                                             &Waited);
    00925
    00926                                 Waited = 0;
    00927
    00928                                 MiDoesPdeExistAndMakeValid (PointerPde,
    00929                                                             CurrentProcess,
    00930                                                             FALSE,
    00931                                                             &Waited);
    00932                             } while (Waited != 0);
    00933                         }
    00934
    00935                         //
    00936                         // Hand initialize this PFN as normal initialization
    00937                         // would do it for the process whose context we are
    00938                         // attached to.
    00939                         //
    00940                         // The PFN lock must be held while initializing the
    00941                         // frame to prevent those scanning the database for
    00942                         // free frames from taking it after we fill in the
    00943                         // u2 field.
    00944                         //
    00945
    00946 #if defined (_X86PAE_)
    00947                         PdePhysicalPage = PageDirectoryFrames[MiGetPdPteOffset(MiGetVirtualAddressMappedByPte(PointerPte))];
    00948                         PfnPdPage = MI_PFN_ELEMENT (PdePhysicalPage);
    00949 #endif
    00950
    00951                         MiBuildForkPageTable (PageFrameIndex,
    00952                                               PointerPde,
    00953                                               PointerNewPde,
    00954                                               PdePhysicalPage,
    00955                                               PfnPdPage);
    00956
    00957 #if defined (_WIN64)
    00958                         MI_INCREMENT_USED_PTES_BY_HANDLE (UsedPageDirectoryEntries);
    00959 #endif
    00960
    00961                         //
    00962                         // Map the new page table page into the system portion
    00963                         // of the address space.  Note that hyperspace
    00964                         // cannot be used as other operations (allocating
    00965                         // nonpaged pool at DPC level) could cause the
    00966                         // hyperspace page being used to be reused.
    00967                         //
    00968
    00969                         ASSERT (NewPteMappedAddress != NULL);
    00970
    00971                         MiDownPfnReferenceCount (MdlPage, 1);
    00972
    00973                         MdlPage = PageFrameIndex;
    00974
    00975                         PointerNewPte = (PMMPTE)MiMapSinglePage (NewPteMappedAddress,
    00976                                                                  MdlPage,
    00977                                                                  MmCached,
    00978                                                                  HighPagePriority);
    00979
    00980                         ASSERT (PointerNewPte != NULL);
    00981
    00982                         MiUpPfnReferenceCount (MdlPage, 1);
    00983                     }
    00984
    00985                     //
    00986                     // Calculate the address of the new PTE to build.
    00987                     // Note that FirstTime could be true, yet the page
    00988                     // table page already built.
    00989                     //
    00990
    00991                     PointerNewPte = (PMMPTE)((ULONG_PTR)PAGE_ALIGN(PointerNewPte) |
    00992                                             BYTE_OFFSET (PointerPte));
    00993
    00994 #ifdef _WIN64
    00995                     UsedPageTableEntries = (PVOID)MI_PFN_ELEMENT((PFN_NUMBER)PointerNewPde->u.Hard.PageFrameNumber);
    00996 #else
    00997 #if !defined (_X86PAE_)
    00998                     UsedPageTableEntries = (PVOID)&HyperBase->UsedPageTableEntries
    00999                                                 [MiGetPteOffset( PointerPte )];
    01000 #else
    01001                     UsedPageTableEntries = (PVOID)&HyperBase->UsedPageTableEntries
    01002                                                 [MiGetPpePdeOffset(MiGetVirtualAddressMappedByPte(PointerPte))];
    01003 #endif
    01004 #endif
    01005
    01006                 }
    01007
    01008                 //
    01009                 // Make the fork prototype PTE location resident.
    01010                 //
    01011
    01012                 if (PAGE_ALIGN (ForkProtoPte) != PAGE_ALIGN (LockedForkPte)) {
    01013                     MiUnlockPagedAddress (LockedForkPte, FALSE);
    01014                     LockedForkPte = ForkProtoPte;
    01015                     MiLockPagedAddress (LockedForkPte, FALSE);
    01016                 }
    01017
    01018                 MiMakeSystemAddressValid (PointerPte, CurrentProcess);
    01019
    01020                 PteContents = *PointerPte;
    01021
    01022                 //
    01023                 // Check each PTE.
    01024                 //
    01025
    01026                 if (PteContents.u.Long == 0) {
    01027                     NOTHING;
    01028
    01029                 } else if (PteContents.u.Hard.Valid == 1) {
    01030
    01031                     //
    01032                     // Valid.
    01033                     //
    01034
    01035                     Pfn2 = MI_PFN_ELEMENT (PteContents.u.Hard.PageFrameNumber);
    01036                     VirtualAddress = MiGetVirtualAddressMappedByPte (PointerPte);
    01037                     WorkingSetIndex = MiLocateWsle (VirtualAddress,
    01038                                                     MmWorkingSetList,
    01039                                                     Pfn2->u1.WsIndex);
    01040
    01041                     ASSERT (WorkingSetIndex != WSLE_NULL_INDEX);
    01042
    01043                     if (Pfn2->u3.e1.PrototypePte == 1) {
    01044
    01045                         //
    01046                         // This PTE is already in prototype PTE format.
    01047                         //
    01048
    01049                         //
    01050                         // This is a prototype PTE.  The PFN database does
    01051                         // not contain the contents of this PTE it contains
    01052                         // the contents of the prototype PTE.  This PTE must
    01053                         // be reconstructed to contain a pointer to the
    01054                         // prototype PTE.
    01055                         //
    01056                         // The working set list entry contains information about
    01057                         // how to reconstruct the PTE.
    01058                         //
    01059
    01060                         if (MmWsle[WorkingSetIndex].u1.e1.SameProtectAsProto
    01061                                                                         == 0) {
    01062
    01063                             //
    01064                             // The protection for the prototype PTE is in the
    01065                             // WSLE.
    01066                             //
    01067
    01068                             TempPte.u.Long = 0;
    01069                             TempPte.u.Soft.Protection =
    01070                                 MI_GET_PROTECTION_FROM_WSLE(&MmWsle[WorkingSetIndex]);
    01071                             TempPte.u.Soft.PageFileHigh = MI_PTE_LOOKUP_NEEDED;
    01072
    01073                         } else {
    01074
    01075                             //
    01076                             // The protection is in the prototype PTE.
    01077                             //
    01078
    01079                             TempPte.u.Long = MiProtoAddressForPte (
    01080                                                             Pfn2->PteAddress);
    01081  //                            TempPte.u.Proto.ForkType =
    01082  //                                        MmWsle[WorkingSetIndex].u1.e1.ForkType;
    01083                         }
    01084
    01085                         TempPte.u.Proto.Prototype = 1;
    01086                         MI_WRITE_INVALID_PTE (PointerNewPte, TempPte);
    01087
    01088                         //
    01089                         // A PTE is now non-zero, increment the used page
    01090                         // table entries counter.
    01091                         //
    01092
    01093                         MI_INCREMENT_USED_PTES_BY_HANDLE (UsedPageTableEntries);
    01094
    01095                         //
    01096                         // Check to see if this is a fork prototype PTE,
    01097                         // and if it is increment the reference count
    01098                         // which is in the longword following the PTE.
    01099                         //
    01100
    01101                         if (MiLocateCloneAddress ((PVOID)Pfn2->PteAddress) !=
    01102                                     (PMMCLONE_DESCRIPTOR)NULL) {
    01103
    01104                             //
    01105                             // The reference count field, or the prototype PTE
    01106                             // for that matter may not be in the working set.
    01107                             //
    01108
    01109                             CloneProto = (PMMCLONE_BLOCK)Pfn2->PteAddress;
    01110
    01111                             MiUpCloneProtoRefCount (CloneProto,
    01112                                                     CurrentProcess);
    01113
    01114                             if (PAGE_ALIGN (ForkProtoPte) !=
    01115                                                     PAGE_ALIGN (LockedForkPte)) {
    01116                                 MiUnlockPagedAddress (LockedForkPte, FALSE);
    01117                                 LockedForkPte = ForkProtoPte;
    01118                                 MiLockPagedAddress (LockedForkPte, FALSE);
    01119                             }
    01120
    01121                             MiMakeSystemAddressValid (PointerPte,
    01122                                                       CurrentProcess);
    01123                         }
    01124
    01125                     } else {
    01126
    01127                         //
    01128                         // This is a private page, create a fork prototype PTE
    01129                         // which becomes the "prototype" PTE for this page.
    01130                         // The protection is the same as that in the prototype'
    01131                         // PTE so the WSLE does not need to be updated.
    01132                         //
    01133
    01134                         MI_MAKE_VALID_PTE_WRITE_COPY (PointerPte);
    01135
    01136                         ForkProtoPte->ProtoPte = *PointerPte;
    01137                         ForkProtoPte->CloneRefCount = 2;
    01138
    01139                         //
    01140                         // Transform the PFN element to reference this new fork
    01141                         // prototype PTE.
    01142                         //
    01143
    01144                         Pfn2->PteAddress = &ForkProtoPte->ProtoPte;
    01145                         Pfn2->u3.e1.PrototypePte = 1;
    01146
    01147                         ContainingPte = MiGetPteAddress(&ForkProtoPte->ProtoPte);
    01148                         if (ContainingPte->u.Hard.Valid == 0) {
    01149 #if !defined (_WIN64)
    01150                             if (!NT_SUCCESS(MiCheckPdeForPagedPool (&ForkProtoPte->ProtoPte))) {
    01151 #endif
    01152                                 KeBugCheckEx (MEMORY_MANAGEMENT,
    01153                                               0x61940,
    01154                                               (ULONG_PTR)&ForkProtoPte->ProtoPte,
    01155                                               (ULONG_PTR)ContainingPte->u.Long,
    01156                                               (ULONG_PTR)MiGetVirtualAddressMappedByPte(&ForkProtoPte->ProtoPte));
    01157 #if !defined (_WIN64)
    01158                             }
    01159 #endif
    01160                         }
    01161                         Pfn2->PteFrame = MI_GET_PAGE_FRAME_FROM_PTE (ContainingPte);
    01162
    01163
    01164                         //
    01165                         // Increment the share count for the page containing the
    01166                         // fork prototype PTEs as we have just placed a valid
    01167                         // PTE into the page.
    01168                         //
    01169
    01170                         PfnForkPtePage = MI_PFN_ELEMENT (
    01171                                             ContainingPte->u.Hard.PageFrameNumber );
    01172
    01173                         MiUpForkPageShareCount (PfnForkPtePage);
    01174
    01175                         //
    01176                         // Change the protection in the PFN database to COPY
    01177                         // on write, if writable.
    01178                         //
    01179
    01180                         MI_MAKE_PROTECT_WRITE_COPY (Pfn2->OriginalPte);
    01181
    01182                         //
    01183                         // Put the protection into the WSLE and mark the WSLE
    01184                         // to indicate that the protection field for the PTE
    01185                         // is the same as the prototype PTE.
    01186                         //
    01187
    01188                         MmWsle[WorkingSetIndex].u1.e1.Protection =
    01189                             MI_GET_PROTECTION_FROM_SOFT_PTE(&Pfn2->OriginalPte);
    01190
    01191                         MmWsle[WorkingSetIndex].u1.e1.SameProtectAsProto = 1;
    01192
    01193                         TempPte.u.Long = MiProtoAddressForPte (Pfn2->PteAddress);
    01194                         TempPte.u.Proto.Prototype = 1;
    01195                         MI_WRITE_INVALID_PTE (PointerNewPte, TempPte);
    01196
    01197                         //
    01198                         // A PTE is now non-zero, increment the used page
    01199                         // table entries counter.
    01200                         //
    01201
    01202                         MI_INCREMENT_USED_PTES_BY_HANDLE (UsedPageTableEntries);
    01203
    01204                         //
    01205                         // One less private page (it's now shared).
    01206                         //
    01207
    01208                         CurrentProcess->NumberOfPrivatePages -= 1;
    01209
    01210                         ForkProtoPte += 1;
    01211                         NumberOfForkPtes += 1;
    01212
    01213                     }
    01214
    01215                 } else if (PteContents.u.Soft.Prototype == 1) {
    01216
    01217                     //
    01218                     // Prototype PTE, check to see if this is a fork
    01219                     // prototype PTE already.  Note that if COW is set,
    01220                     // the PTE can just be copied (fork compatible format).
    01221                     //
    01222
    01223                     MI_WRITE_INVALID_PTE (PointerNewPte, PteContents);
    01224
    01225                     //
    01226                     // A PTE is now non-zero, increment the used page
    01227                     // table entries counter.
    01228                     //
    01229
    01230                     MI_INCREMENT_USED_PTES_BY_HANDLE (UsedPageTableEntries);
    01231
    01232                     //
    01233                     // Check to see if this is a fork prototype PTE,
    01234                     // and if it is increment the reference count
    01235                     // which is in the longword following the PTE.
    01236                     //
    01237
    01238                     CloneProto = (PMMCLONE_BLOCK)(MiPteToProto(PointerPte));
    01239
    01240                     if (MiLocateCloneAddress ((PVOID)CloneProto) !=
    01241                                 (PMMCLONE_DESCRIPTOR)NULL) {
    01242
    01243                         //
    01244                         // The reference count field, or the prototype PTE
    01245                         // for that matter may not be in the working set.
    01246                         //
    01247
    01248                         MiUpCloneProtoRefCount (CloneProto,
    01249                                                 CurrentProcess);
    01250
    01251                         if (PAGE_ALIGN (ForkProtoPte) !=
    01252                                                 PAGE_ALIGN (LockedForkPte)) {
    01253                             MiUnlockPagedAddress (LockedForkPte, FALSE);
    01254                             LockedForkPte = ForkProtoPte;
    01255                             MiLockPagedAddress (LockedForkPte, FALSE);
    01256                         }
    01257
    01258                         MiMakeSystemAddressValid (PointerPte,
    01259                                                     CurrentProcess);
    01260                     }
    01261
    01262                 } else if (PteContents.u.Soft.Transition == 1) {
    01263
    01264                     //
    01265                     // Transition.
    01266                     //
    01267
    01268                     if (MiHandleForkTransitionPte (PointerPte,
    01269                                                    PointerNewPte,
    01270                                                    ForkProtoPte)) {
    01271                         //
    01272                         // PTE is no longer transition, try again.
    01273                         //
    01274
    01275                         continue;
    01276                     }
    01277
    01278                     //
    01279                     // A PTE is now non-zero, increment the used page
    01280                     // table entries counter.
    01281                     //
    01282
    01283                     MI_INCREMENT_USED_PTES_BY_HANDLE (UsedPageTableEntries);
    01284
    01285                     //
    01286                     // One less private page (it's now shared).
    01287                     //
    01288
    01289                     CurrentProcess->NumberOfPrivatePages -= 1;
    01290
    01291                     ForkProtoPte += 1;
    01292                     NumberOfForkPtes += 1;
    01293
    01294                 } else {
    01295
    01296                     //
    01297                     // Page file format (may be demand zero).
    01298                     //
    01299
    01300                     if (IS_PTE_NOT_DEMAND_ZERO (PteContents)) {
    01301
    01302                         if (PteContents.u.Soft.Protection == MM_DECOMMIT) {
    01303
    01304                             //
    01305                             // This is a decommitted PTE, just move it
    01306                             // over to the new process.  Don't increment
    01307                             // the count of private pages.
    01308                             //
    01309
    01310                             MI_WRITE_INVALID_PTE (PointerNewPte, PteContents);
    01311                         } else {
    01312
    01313                             //
    01314                             // The PTE is not demand zero, move the PTE to
    01315                             // a fork prototype PTE and make this PTE and
    01316                             // the new processes PTE refer to the fork
    01317                             // prototype PTE.
    01318                             //
    01319
    01320                             ForkProtoPte->ProtoPte = PteContents;
    01321
    01322                             //
    01323                             // Make the protection write-copy if writable.
    01324                             //
    01325
    01326                             MI_MAKE_PROTECT_WRITE_COPY (ForkProtoPte->ProtoPte);
    01327
    01328                             ForkProtoPte->CloneRefCount = 2;
    01329
    01330                             TempPte.u.Long =
    01331                                  MiProtoAddressForPte (&ForkProtoPte->ProtoPte);
    01332
    01333                             TempPte.u.Proto.Prototype = 1;
    01334
    01335                             MI_WRITE_INVALID_PTE (PointerPte, TempPte);
    01336                             MI_WRITE_INVALID_PTE (PointerNewPte, TempPte);
    01337
    01338                             //
    01339                             // One less private page (it's now shared).
    01340                             //
    01341
    01342                             CurrentProcess->NumberOfPrivatePages -= 1;
    01343
    01344                             ForkProtoPte += 1;
    01345                             NumberOfForkPtes += 1;
    01346                         }
    01347                     } else {
    01348
    01349                         //
    01350                         // The page is demand zero, make the new process's
    01351                         // page demand zero.
    01352                         //
    01353
    01354                         MI_WRITE_INVALID_PTE (PointerNewPte, PteContents);
    01355                     }
    01356
    01357                     //
    01358                     // A PTE is now non-zero, increment the used page
    01359                     // table entries counter.
    01360                     //
    01361
    01362                     MI_INCREMENT_USED_PTES_BY_HANDLE (UsedPageTableEntries);
    01363                 }
    01364
    01365                 PointerPte += 1;
    01366                 PointerNewPte += 1;
    01367
    01368             }  // end while for PTEs
    01369 AllDone:
    01370             NewVad = NewVad->Parent;
    01371         }
    01372         Vad = MiGetNextVad (Vad);
    01373
    01374     } // end while for VADs
    01375
    01376     //
    01377     // Unlock paged pool page.
    01378     //
    01379
    01380     MiUnlockPagedAddress (LockedForkPte, FALSE);
    01381
    01382     //
    01383     // Unmap the PD Page and hyper space page.
    01384     //
    01385
    01386 #if defined (_WIN64)
    01387     MiUnmapSinglePage (PpeBase);
    01388 #endif
    01389
    01390 #if !defined (_X86PAE_)
    01391     MiUnmapSinglePage (PdeBase);
    01392 #else
    01393     MmUnmapLockedPages (PdeBase, MdlPageDirectory);
    01394 #endif
    01395
    01396 #if !defined (_WIN64)
    01397     MiUnmapSinglePage (HyperBase);
    01398 #endif
    01399
    01400     MiUnmapSinglePage (NewPteMappedAddress);
    01401
    01402 #if defined (_WIN64)
    01403     MiDownPfnReferenceCount (RootPhysicalPage, 1);
    01404 #endif
    01405
    01406 #if defined (_X86PAE_)
    01407     for (i = 0; i < PD_PER_SYSTEM; i += 1) {
    01408         MiDownPfnReferenceCount (PageDirectoryFrames[i], 1);
    01409     }
    01410 #else
    01411     MiDownPfnReferenceCount (MdlDirPage, 1);
    01412 #endif
    01413
    01414 #if !defined (_WIN64)
    01415     MiDownPfnReferenceCount (HyperPhysicalPage, 1);
    01416 #endif
    01417
    01418     MiDownPfnReferenceCount (MdlPage, 1);
    01419
    01420     //
    01421     // Make the count of private pages match between the two processes.
    01422     //
    01423
    01424     ASSERT ((SPFN_NUMBER)CurrentProcess->NumberOfPrivatePages >= 0);
    01425
    01426     ProcessToInitialize->NumberOfPrivatePages =
    01427                                           CurrentProcess->NumberOfPrivatePages;
    01428
    01429     ASSERT (NumberOfForkPtes <= CloneDescriptor->NumberOfPtes);
    01430
    01431     if (NumberOfForkPtes != 0) {
    01432
    01433         //
    01434         // The number of fork PTEs is non-zero, set the values
    01435         // into the structures.
    01436         //
    01437
    01438         CloneHeader->NumberOfPtes = NumberOfForkPtes;
    01439         CloneDescriptor->NumberOfReferences = NumberOfForkPtes;
    01440         CloneDescriptor->NumberOfPtes = NumberOfForkPtes;
    01441
    01442     } else {
    01443
    01444         //
    01445         // There were no fork PTEs created.  Remove the clone descriptor
    01446         // from this process and clean up the related structures.
    01447         // Note - must be holding the working set mutex and not holding
    01448         // the PFN lock.
    01449         //
    01450
    01451         MiRemoveClone (CloneDescriptor);
    01452
    01453         UNLOCK_WS (CurrentProcess);
    01454
    01455         ExFreePool (CloneDescriptor->CloneHeader->ClonePtes);
    01456
    01457         ExFreePool (CloneDescriptor->CloneHeader);
    01458
    01459         //
    01460         // Return the pool for the global structures referenced by the
    01461         // clone descriptor.
    01462         //
    01463
    01464         PsReturnPoolQuota (CurrentProcess,
    01465                            PagedPool,
    01466                            CloneDescriptor->PagedPoolQuotaCharge);
    01467
    01468         PsReturnPoolQuota (CurrentProcess, NonPagedPool, sizeof(MMCLONE_HEADER));
    01469
    01470         ExFreePool (CloneDescriptor);
    01471
    01472         LOCK_WS (CurrentProcess);
    01473     }
    01474
    01475     MiDownShareCountFlushEntireTb (PageFrameIndex);
    01476
    01477 #if defined (_WIN64)
    01478     MiDownShareCountFlushEntireTb (PageDirFrameIndex);
    01479 #endif
    01480
    01481     PageFrameIndex = (PFN_NUMBER)-1;
    01482     PageDirFrameIndex = (PFN_NUMBER)-1;
    01483
    01484     //
    01485     // Copy the clone descriptors from this process to the new process.
    01486     //
    01487
    01488     Clone = MiGetFirstClone ();
    01489     CloneList = &FirstNewClone;
    01490     CloneFailed = FALSE;
    01491
    01492     while (Clone != (PMMCLONE_DESCRIPTOR)NULL) {
    01493
    01494         //
    01495         // Increment the count of processes referencing this clone block.
    01496         //
    01497
    01498         MiUpCloneProcessRefCount (Clone);
    01499
    01500         do {
    01501             NewClone = ExAllocatePoolWithTag (NonPagedPool,
    01502                                               sizeof( MMCLONE_DESCRIPTOR),
    01503                                               '  mM');
    01504
    01505             if (NewClone != NULL) {
    01506                 break;
    01507             }
    01508
    01509             //
    01510             // There are insufficient resources to continue this operation,
    01511             // however, to properly clean up at this point, all the
    01512             // clone headers must be allocated, so when the cloned process
    01513             // is deleted, the clone headers will be found.  if the pool
    01514             // is not readily available, loop periodically trying for it.
    01515             // Force the clone operation to fail so the pool will soon be
    01516             // released.
    01517             //
    01518
    01519             CloneFailed = TRUE;
    01520             status = STATUS_INSUFFICIENT_RESOURCES;
    01521
    01522             KeDelayExecutionThread (KernelMode,
    01523                                     FALSE,
    01524                                     (PLARGE_INTEGER)&MmShortTime);
    01525             continue;
    01526         } while (TRUE);
    01527
    01528         *NewClone = *Clone;
    01529
    01530         *CloneList = NewClone;
    01531         CloneList = &NewClone->Parent;
    01532         Clone = MiGetNextClone (Clone);
    01533     }
    01534
    01535     *CloneList = (PMMCLONE_DESCRIPTOR)NULL;
    01536
    01537     //
    01538     // Release the working set mutex and the address creation mutex from
    01539     // the current process as all the necessary information is now
    01540     // captured.
    01541     //
    01542
    01543     UNLOCK_WS (CurrentProcess);
    01544
    01545     CurrentProcess->ForkInProgress = NULL;
    01546
    01547     UNLOCK_ADDRESS_SPACE (CurrentProcess);
    01548
    01549     //
    01550     // As we have updated many PTEs to clear dirty bits, flush the
    01551     // TB cache.  Note that this was not done every time we changed
    01552     // a valid PTE so other threads could be modifying the address
    01553     // space without causing copy on writes. (Too bad).
    01554     //
    01555
    01556
    01557     //
    01558     // Attach to the process to initialize and insert the vad and clone
    01559     // descriptors into the tree.
    01560     //
    01561
    01562     if (Attached) {
    01563         KeDetachProcess ();
    01564         Attached = FALSE;
    01565     }
    01566
    01567     if (PsGetCurrentProcess() != ProcessToInitialize) {
    01568         Attached = TRUE;
    01569         KeAttachProcess (&ProcessToInitialize->Pcb);
    01570     }
    01571
    01572     CurrentProcess = ProcessToInitialize;
    01573
    01574     //
    01575     // We are now in the context of the new process, build the
    01576     // VAD list and the clone list.
    01577     //
    01578
    01579     Vad = FirstNewVad;
    01580     VadInsertFailed = FALSE;
    01581
    01582     LOCK_WS (CurrentProcess);
    01583
    01584     while (Vad != (PMMVAD)NULL) {
    01585
    01586         NextVad = Vad->Parent;
    01587
    01588         try {
    01589
    01590             if (VadInsertFailed) {
    01591                 Vad->u.VadFlags.CommitCharge = MM_MAX_COMMIT;
    01592             }
    01593
    01594             MiInsertVad (Vad);
    01595
    01596         } except (EXCEPTION_EXECUTE_HANDLER) {
    01597
    01598             //
    01599             // Charging quota for the VAD failed, set the
    01600             // remaining quota fields in this VAD and all
    01601             // subsequent VADs to zero so the VADs can be
    01602             // inserted and later deleted.
    01603             //
    01604
    01605             VadInsertFailed = TRUE;
    01606             status = GetExceptionCode();
    01607
    01608             //
    01609             // Do the loop again for this VAD.
    01610             //
    01611
    01612             continue;
    01613         }
    01614
    01615         //
    01616         // Update the current virtual size.
    01617         //
    01618
    01619         CurrentProcess->VirtualSize += PAGE_SIZE +
    01620                             ((Vad->EndingVpn - Vad->StartingVpn) >> PAGE_SHIFT);
    01621
    01622         Vad = NextVad;
    01623     }
    01624
    01625     UNLOCK_WS (CurrentProcess);
    01626     //MmUnlockCode (MiCloneProcessAddressSpace, 5000);
    01627
    01628     //
    01629     // Update the peak virtual size.
    01630     //
    01631
    01632     CurrentProcess->PeakVirtualSize = CurrentProcess->VirtualSize;
    01633
    01634     Clone = FirstNewClone;
    01635     TotalPagedPoolCharge = 0;
    01636     TotalNonPagedPoolCharge = 0;
    01637
    01638     while (Clone != (PMMCLONE_DESCRIPTOR)NULL) {
    01639
    01640         NextClone = Clone->Parent;
    01641         MiInsertClone (Clone);
    01642
    01643         //
    01644         // Calculate the page pool and non-paged pool to charge for these
    01645         // operations.
    01646         //
    01647
    01648         TotalPagedPoolCharge += Clone->PagedPoolQuotaCharge;
    01649         TotalNonPagedPoolCharge += sizeof(MMCLONE_HEADER);
    01650
    01651         Clone = NextClone;
    01652     }
    01653
    01654     if (CloneFailed || VadInsertFailed) {
    01655
    01656         CurrentProcess->ForkWasSuccessful = MM_FORK_FAILED;
    01657
    01658         if (Attached) {
    01659             KeDetachProcess ();
    01660         }
    01661         KdPrint(("MMFORK: vad insert failed\n"));
    01662
    01663         return status;
    01664     }
    01665
    01666     try {
    01667
    01668         PageTablePage = 1;
    01669         PsChargePoolQuota (CurrentProcess, PagedPool, TotalPagedPoolCharge);
    01670         PageTablePage = 0;
    01671         PsChargePoolQuota (CurrentProcess, NonPagedPool, TotalNonPagedPoolCharge);
    01672
    01673     } except (EXCEPTION_EXECUTE_HANDLER) {
    01674
    01675         if (PageTablePage == 0) {
    01676             PsReturnPoolQuota (CurrentProcess, PagedPool, TotalPagedPoolCharge);
    01677         }
    01678         KdPrint(("MMFORK: pool quota failed\n"));
    01679
    01680         CurrentProcess->ForkWasSuccessful = MM_FORK_FAILED;
    01681
    01682         if (Attached) {
    01683             KeDetachProcess ();
    01684         }
    01685         return GetExceptionCode();
    01686     }
    01687
    01688     ASSERT (ProcessToClone->ForkWasSuccessful == MM_FORK_SUCCEEDED);
    01689     ASSERT (CurrentProcess->ForkWasSuccessful == MM_FORK_SUCCEEDED);
    01690
    01691     if (Attached) {
    01692         KeDetachProcess ();
    01693     }
    01694
    01695 #if DBG
    01696     if (MmDebug & MM_DBG_FORK) {
    01697         DbgPrint("ending clone operation process to clone = %lx\n",
    01698             ProcessToClone);
    01699     }
    01700 #endif //DBG
    01701
    01702     return STATUS_SUCCESS;
    01703
    01704     //
    01705     // Error returns.
    01706     //
    01707
    01708 ErrorReturn4:
    01709         if (PageTablePage == 2) {
    01710             NOTHING;
    01711         }
    01712         else if (PageTablePage == 1) {
    01713             PsReturnPoolQuota (CurrentProcess, PagedPool, sizeof(MMCLONE_BLOCK) *
    01714                                 NumberOfPrivatePages);
    01715         }
    01716         else {
    01717             ASSERT (PageTablePage == 0);
    01718             PsReturnPoolQuota (CurrentProcess, PagedPool, sizeof(MMCLONE_BLOCK) *
    01719                                 NumberOfPrivatePages);
    01720             PsReturnPoolQuota (CurrentProcess, NonPagedPool, sizeof(MMCLONE_HEADER));
    01721         }
    01722
    01723         NewVad = FirstNewVad;
    01724         while (NewVad != NULL) {
    01725             Vad = NewVad->Parent;
    01726             ExFreePool (NewVad);
    01727             NewVad = Vad;
    01728         }
    01729
    01730         ExFreePool (CloneDescriptor);
    01731 ErrorReturn3:
    01732         ExFreePool (CloneHeader);
    01733 ErrorReturn2:
    01734         ExFreePool (CloneProtos);
    01735 ErrorReturn1:
    01736         UNLOCK_ADDRESS_SPACE (CurrentProcess);
    01737         ASSERT (CurrentProcess->ForkWasSuccessful == MM_FORK_SUCCEEDED);
    01738         if (Attached) {
    01739             KeDetachProcess ();
    01740         }
    01741         return status;
    01742 }

ULONG MiDecrementCloneBlockReference | ( | IN PMMCLONE_DESCRIPTOR | CloneDescriptor,
|  | IN PMMCLONE_BLOCK | CloneBlock,
|  | IN PEPROCESS | CurrentProcess
| ) |

Definition at line 1745 of file forksup.c.

References APC_LEVEL, ASSERT, _MMCLONE_BLOCK::CloneRefCount, DbgPrint, ExFreePool(), FALSE, FreePageList, IS_PTE_NOT_DEMAND_ZERO, KeBugCheckEx(), LOCK_PFN, LOCK_WS_REGARDLESS, MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE, MI_PFN_ELEMENT, MI_SET_PFN_DELETED, MiDecrementShareCount(), MiInsertPageInList(), MiMakeSystemAddressValidPfnWs(), MiReleasePageFileSpace(), MiRemoveClone, MiUnlinkPageFromList(), MiWaitForForkToComplete(), MM_DBG_FORK, MM_FORK_SUCCEEDED, MmPageLocationList, NonPagedPool, _MMPFN::OriginalPte, PagedPool, PsReturnPoolQuota(), _MMPFN::PteFrame, TRUE, _MMPTE::u, _MMPFN::u3, UNLOCK_PFN, UNLOCK_WS_REGARDLESS, and ZeroPte.

Referenced by MiCopyOnWrite(), MiDeletePte(), and MiDeleteValidAddress().

    01753                    :
    01754
    01755     This routine decrements the reference count field of a "fork prototype
    01756     PTE" (clone-block).  If the reference count becomes zero, the reference
    01757     count for the clone-descriptor is decremented and if that becomes zero,
    01758     it is deallocated and the number of process count for the clone header is
    01759     decremented.  If the number of process count becomes zero, the clone
    01760     header is deallocated.
    01761
    01762 Arguments:
    01763
    01764     CloneDescriptor - Supplies the clone descriptor which describes the
    01765                       clone block.
    01766
    01767     CloneBlock - Supplies the clone block to decrement the reference count of.
    01768
    01769     CurrentProcess - Supplies the current process.
    01770
    01771 Return Value:
    01772
    01773     TRUE if the working set mutex was released, FALSE if it was not.
    01774
    01775 Environment:
    01776
    01777     Kernel mode, APCs disabled, working set mutex and PFN mutex held.
    01778
    01779 --*/
    01780
    01781 {
    01782
    01783     ULONG MutexReleased;
    01784     MMPTE CloneContents;
    01785     PMMPFN Pfn3;
    01786     KIRQL OldIrql;
    01787     LONG NewCount;
    01788     LOGICAL WsHeldSafe;
    01789
    01790     MutexReleased = FALSE;
    01791     OldIrql = APC_LEVEL;
    01792
    01793     MutexReleased = MiMakeSystemAddressValidPfnWs (CloneBlock, CurrentProcess);
    01794
    01795     while (CurrentProcess->ForkInProgress) {
    01796         MiWaitForForkToComplete (CurrentProcess);
    01797         MiMakeSystemAddressValidPfnWs (CloneBlock, CurrentProcess);
    01798         MutexReleased = TRUE;
    01799     }
    01800
    01801     CloneBlock->CloneRefCount -= 1;
    01802     NewCount = CloneBlock->CloneRefCount;
    01803
    01804     ASSERT (NewCount >= 0);
    01805
    01806     if (NewCount == 0) {
    01807         CloneContents = CloneBlock->ProtoPte;
    01808     } else {
    01809         CloneContents = ZeroPte;
    01810     }
    01811
    01812     if ((NewCount == 0) && (CloneContents.u.Long != 0)) {
    01813
    01814         //
    01815         // The last reference to a fork prototype PTE
    01816         // has been removed.  Deallocate any page file
    01817         // space and the transition page, if any.
    01818         //
    01819
    01820
    01821         //
    01822         // Assert that the page is no longer valid.
    01823         //
    01824
    01825         ASSERT (CloneContents.u.Hard.Valid == 0);
    01826
    01827         //
    01828         // Assert that the PTE is not in subsection format (doesn't point
    01829         // to a file).
    01830         //
    01831
    01832         ASSERT (CloneContents.u.Soft.Prototype == 0);
    01833
    01834         if (CloneContents.u.Soft.Transition == 1) {
    01835
    01836             //
    01837             // Prototype PTE in transition, put the page
    01838             // on the free list.
    01839             //
    01840
    01841             Pfn3 = MI_PFN_ELEMENT (CloneContents.u.Trans.PageFrameNumber);
    01842             MI_SET_PFN_DELETED (Pfn3);
    01843
    01844             MiDecrementShareCount (Pfn3->PteFrame);
    01845
    01846             //
    01847             // Check the reference count for the page, if the reference
    01848             // count is zero and the page is not on the freelist,
    01849             // move the page to the free list, if the reference
    01850             // count is not zero, ignore this page.
    01851             // When the reference count goes to zero, it will be placed on the
    01852             // free list.
    01853             //
    01854
    01855             if ((Pfn3->u3.e2.ReferenceCount == 0) &&
    01856                 (Pfn3->u3.e1.PageLocation != FreePageList)) {
    01857
    01858                 MiUnlinkPageFromList (Pfn3);
    01859                 MiReleasePageFileSpace (Pfn3->OriginalPte);
    01860                 MiInsertPageInList (MmPageLocationList[FreePageList],
    01861                              MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE(&CloneContents));
    01862             }
    01863         } else {
    01864
    01865             if (IS_PTE_NOT_DEMAND_ZERO (CloneContents)) {
    01866                 MiReleasePageFileSpace (CloneContents);
    01867             }
    01868         }
    01869     }
    01870
    01871     //
    01872     // Decrement the number of references to the
    01873     // clone descriptor.
    01874     //
    01875
    01876     CloneDescriptor->NumberOfReferences -= 1;
    01877
    01878     if (CloneDescriptor->NumberOfReferences == 0) {
    01879
    01880         //
    01881         // There are no longer any PTEs in this process which refer
    01882         // to the fork prototype PTEs for this clone descriptor.
    01883         // Remove the CloneDescriptor and decrement the CloneHeader
    01884         // number of process's reference count.
    01885         //
    01886
    01887         CloneDescriptor->CloneHeader->NumberOfProcessReferences -= 1;
    01888
    01889         MiRemoveClone (CloneDescriptor);
    01890
    01891         if (CloneDescriptor->CloneHeader->NumberOfProcessReferences == 0) {
    01892
    01893             //
    01894             // There are no more processes pointing to this fork header
    01895             // blow it away.
    01896             //
    01897
    01898             UNLOCK_PFN (OldIrql);
    01899
    01900             //
    01901             // The working set lock may have been acquired safely or unsafely
    01902             // by our caller.  Handle both cases here and below.
    01903             //
    01904
    01905             UNLOCK_WS_REGARDLESS (CurrentProcess, WsHeldSafe);
    01906
    01907             MutexReleased = TRUE;
    01908
    01909
    01910 #if DBG
    01911             {
    01912
    01913             ULONG i;
    01914             PMMCLONE_BLOCK OldCloneBlock;
    01915
    01916             OldCloneBlock = CloneDescriptor->CloneHeader->ClonePtes;
    01917             for (i = 0; i < CloneDescriptor->CloneHeader->NumberOfPtes; i += 1) {
    01918                 if (OldCloneBlock->CloneRefCount != 0) {
    01919                     DbgPrint("fork block with non zero ref count %lx %lx %lx\n",
    01920                         OldCloneBlock, CloneDescriptor,
    01921                         CloneDescriptor->CloneHeader);
    01922                     KeBugCheckEx (MEMORY_MANAGEMENT, 1, 0, 0, 0);
    01923                 }
    01924             }
    01925
    01926             if (MmDebug & MM_DBG_FORK) {
    01927                 DbgPrint("removing clone header at address %lx\n",
    01928                         CloneDescriptor->CloneHeader);
    01929             }
    01930
    01931             }
    01932 #endif //DBG
    01933
    01934             ExFreePool (CloneDescriptor->CloneHeader->ClonePtes);
    01935
    01936             ExFreePool (CloneDescriptor->CloneHeader);
    01937
    01938             //
    01939             // The working set lock may have been acquired safely or unsafely
    01940             // by our caller.  Reacquire it in the same manner our caller did.
    01941             //
    01942
    01943             LOCK_WS_REGARDLESS (CurrentProcess, WsHeldSafe);
    01944
    01945             LOCK_PFN (OldIrql);
    01946         }
    01947
    01948 #if DBG
    01949         if (MmDebug & MM_DBG_FORK) {
    01950           DbgPrint("removing clone descriptor at address %lx\n",CloneDescriptor);
    01951         }
    01952 #endif //DBG
    01953
    01954         //
    01955         // Return the pool for the global structures referenced by the
    01956         // clone descriptor.
    01957         //
    01958
    01959         UNLOCK_PFN (OldIrql);
    01960
    01961         if (CurrentProcess->ForkWasSuccessful == MM_FORK_SUCCEEDED) {
    01962
    01963             PsReturnPoolQuota (CurrentProcess,
    01964                                PagedPool,
    01965                                CloneDescriptor->PagedPoolQuotaCharge);
    01966
    01967             PsReturnPoolQuota (CurrentProcess,
    01968                                NonPagedPool,
    01969                                sizeof(MMCLONE_HEADER));
    01970         }
    01971
    01972         ExFreePool (CloneDescriptor);
    01973         LOCK_PFN (OldIrql);
    01974     }
    01975
    01976     return MutexReleased;
    01977 }

ULONG MiDoneWithThisPageGetAnother | ( | IN PPFN_NUMBER | PageFrameIndex,
|  | IN PMMPTE | PointerPde,
|  | IN PEPROCESS | CurrentProcess
| ) |

Definition at line 2133 of file forksup.c.

References LOCK_PFN, MI_PAGE_COLOR_PTE_PROCESS, MiDecrementShareCountOnly, MiEnsureAvailablePageOrWait(), MiRemoveZeroPage(), NULL, and UNLOCK_PFN.

Referenced by MiCloneProcessAddressSpace().

    02139 {
    02140     KIRQL OldIrql;
    02141     ULONG ReleasedMutex;
    02142
    02143     LOCK_PFN (OldIrql);
    02144
    02145     if (*PageFrameIndex != (PFN_NUMBER)-1) {
    02146
    02147         //
    02148         // Decrement the share count of the last page which
    02149         // we operated on.
    02150         //
    02151
    02152         MiDecrementShareCountOnly (*PageFrameIndex);
    02153     }
    02154
    02155     ReleasedMutex =
    02156                   MiEnsureAvailablePageOrWait (
    02157                                         CurrentProcess,
    02158                                         NULL);
    02159
    02160     *PageFrameIndex = MiRemoveZeroPage (
    02161                    MI_PAGE_COLOR_PTE_PROCESS (PointerPde,
    02162                                               &CurrentProcess->NextPageColor));
    02163
    02164     UNLOCK_PFN (OldIrql);
    02165     return ReleasedMutex;
    02166 }

VOID MiDownPfnReferenceCount | ( | IN PFN_NUMBER | Page,
|  | IN USHORT | Count
| ) |

Definition at line 2095 of file forksup.c.

References Count, LOCK_PFN, MiDecrementReferenceCount(), UNLOCK_PFN, and USHORT.

Referenced by MiCloneProcessAddressSpace().

    02102 {
    02103     USHORT i;
    02104     KIRQL OldIrql;
    02105
    02106     LOCK_PFN (OldIrql);
    02107     for (i = 0; i < Count; i += 1) {
    02108         MiDecrementReferenceCount (Page);
    02109     }
    02110     UNLOCK_PFN (OldIrql);
    02111     return;
    02112 }

VOID MiDownShareCountFlushEntireTb | ( | IN PFN_NUMBER | PageFrameIndex | ) |

Definition at line 2341 of file forksup.c.

References FALSE, KeFlushEntireTb(), LOCK_PFN, MiDecrementShareCountOnly, and UNLOCK_PFN.

Referenced by MiCloneProcessAddressSpace().

    02345 {
    02346     KIRQL OldIrql;
    02347
    02348     LOCK_PFN (OldIrql);
    02349
    02350     if (PageFrameIndex != (PFN_NUMBER)-1) {
    02351
    02352         //
    02353         // Decrement the share count of the last page which
    02354         // we operated on.
    02355         //
    02356
    02357         MiDecrementShareCountOnly (PageFrameIndex);
    02358     }
    02359
    02360     KeFlushEntireTb (FALSE, FALSE);
    02361     UNLOCK_PFN (OldIrql);
    02362     return;
    02363 }

ULONG MiHandleForkTransitionPte | ( | IN PMMPTE | PointerPte,
|  | IN PMMPTE | PointerNewPte,
|  | IN PMMCLONE_BLOCK | ForkProtoPte
| ) |

Definition at line 2204 of file forksup.c.

References ASSERT, FALSE, KeBugCheckEx(), LOCK_PFN, MI_GET_PAGE_FRAME_FROM_PTE, MI_MAKE_PROTECT_WRITE_COPY, MI_PFN_ELEMENT, MI_WRITE_INVALID_PTE, MiCheckPdeForPagedPool(), MiDecrementShareCount(), MiGetPteAddress, MiGetVirtualAddressMappedByPte, MiProtoAddressForPte, NT_SUCCESS, _MMPFN::OriginalPte, _MMPFN::PteAddress, _MMPFN::PteFrame, TRUE, _MMPTE::u, _MMPFN::u2, _MMPFN::u3, and UNLOCK_PFN.

Referenced by MiCloneProcessAddressSpace().

    02210 {
    02211     KIRQL OldIrql;
    02212     PMMPFN Pfn2;
    02213     MMPTE PteContents;
    02214     PMMPTE ContainingPte;
    02215     PFN_NUMBER PageTablePage;
    02216     MMPTE TempPte;
    02217     PMMPFN PfnForkPtePage;
    02218
    02219
    02220     LOCK_PFN (OldIrql);
    02221
    02222     //
    02223     // Now that we have the PFN mutex which prevents pages from
    02224     // leaving the transition state, examine the PTE again to
    02225     // ensure that it is still transition.
    02226     //
    02227
    02228     PteContents = *(volatile PMMPTE)PointerPte;
    02229
    02230     if ((PteContents.u.Soft.Transition == 0) ||
    02231         (PteContents.u.Soft.Prototype == 1)) {
    02232
    02233         //
    02234         // The PTE is no longer in transition... do this
    02235         // loop again.
    02236         //
    02237
    02238         UNLOCK_PFN (OldIrql);
    02239         return TRUE;
    02240
    02241     } else {
    02242
    02243         //
    02244         // The PTE is still in transition, handle like a
    02245         // valid PTE.
    02246         //
    02247
    02248         Pfn2 = MI_PFN_ELEMENT (PteContents.u.Trans.PageFrameNumber);
    02249
    02250         //
    02251         // Assertion that PTE is not in prototype PTE format.
    02252         //
    02253
    02254         ASSERT (Pfn2->u3.e1.PrototypePte != 1);
    02255
    02256         //
    02257         // This is a private page in transition state,
    02258         // create a fork prototype PTE
    02259         // which becomes the "prototype" PTE for this page.
    02260         //
    02261
    02262         ForkProtoPte->ProtoPte = PteContents;
    02263
    02264         //
    02265         // Make the protection write-copy if writable.
    02266         //
    02267
    02268         MI_MAKE_PROTECT_WRITE_COPY (ForkProtoPte->ProtoPte);
    02269
    02270         ForkProtoPte->CloneRefCount = 2;
    02271
    02272         //
    02273         // Transform the PFN element to reference this new fork
    02274         // prototype PTE.
    02275         //
    02276
    02277         //
    02278         // Decrement the share count for the page table
    02279         // page which contains the PTE as it is no longer
    02280         // valid or in transition.
    02281         //
    02282         Pfn2->PteAddress = &ForkProtoPte->ProtoPte;
    02283         Pfn2->u3.e1.PrototypePte = 1;
    02284
    02285         //
    02286         // Make original PTE copy on write.
    02287         //
    02288
    02289         MI_MAKE_PROTECT_WRITE_COPY (Pfn2->OriginalPte);
    02290
    02291         ContainingPte = MiGetPteAddress(&ForkProtoPte->ProtoPte);
    02292
    02293         if (ContainingPte->u.Hard.Valid == 0) {
    02294 #if !defined (_WIN64)
    02295             if (!NT_SUCCESS(MiCheckPdeForPagedPool (&ForkProtoPte->ProtoPte))) {
    02296 #endif
    02297                 KeBugCheckEx (MEMORY_MANAGEMENT,
    02298                               0x61940,
    02299                               (ULONG_PTR)&ForkProtoPte->ProtoPte,
    02300                               (ULONG_PTR)ContainingPte->u.Long,
    02301                               (ULONG_PTR)MiGetVirtualAddressMappedByPte(&ForkProtoPte->ProtoPte));
    02302 #if !defined (_WIN64)
    02303             }
    02304 #endif
    02305         }
    02306
    02307         PageTablePage = Pfn2->PteFrame;
    02308
    02309         Pfn2->PteFrame = MI_GET_PAGE_FRAME_FROM_PTE (ContainingPte);
    02310
    02311         //
    02312         // Increment the share count for the page containing
    02313         // the fork prototype PTEs as we have just placed
    02314         // a transition PTE into the page.
    02315         //
    02316
    02317         PfnForkPtePage = MI_PFN_ELEMENT (
    02318                         ContainingPte->u.Hard.PageFrameNumber );
    02319
    02320         PfnForkPtePage->u2.ShareCount += 1;
    02321
    02322         TempPte.u.Long =
    02323                     MiProtoAddressForPte (Pfn2->PteAddress);
    02324         TempPte.u.Proto.Prototype = 1;
    02325         MI_WRITE_INVALID_PTE (PointerPte, TempPte);
    02326         MI_WRITE_INVALID_PTE (PointerNewPte, TempPte);
    02327
    02328         //
    02329         // Decrement the share count for the page table
    02330         // page which contains the PTE as it is no longer
    02331         // valid or in transition.
    02332         //
    02333
    02334         MiDecrementShareCount (PageTablePage);
    02335     }
    02336     UNLOCK_PFN (OldIrql);
    02337     return FALSE;
    02338 }

VOID MiRetrievePageDirectoryFrames | ( | IN PFN_NUMBER | RootPhysicalPage,
|  | OUT PPFN_NUMBER | PageDirectoryFrames
| ) |

| Referenced by MiCloneProcessAddressSpace().

VOID MiUpCloneProcessRefCount | ( | IN PMMCLONE_DESCRIPTOR | Clone | ) |

Definition at line 2169 of file forksup.c.

References LOCK_PFN, and UNLOCK_PFN.

Referenced by MiCloneProcessAddressSpace().

    02172 {
    02173     KIRQL OldIrql;
    02174
    02175     LOCK_PFN (OldIrql);
    02176
    02177     Clone->CloneHeader->NumberOfProcessReferences += 1;
    02178
    02179     UNLOCK_PFN (OldIrql);
    02180     return;
    02181 }

VOID MiUpCloneProtoRefCount | ( | IN PMMCLONE_BLOCK | CloneProto,
|  | IN PEPROCESS | CurrentProcess
| ) |

Definition at line 2184 of file forksup.c.

References LOCK_PFN, MiMakeSystemAddressValidPfnWs(), and UNLOCK_PFN.

Referenced by MiCloneProcessAddressSpace().

    02189 {
    02190     KIRQL OldIrql;
    02191
    02192     LOCK_PFN (OldIrql);
    02193
    02194     MiMakeSystemAddressValidPfnWs (CloneProto,
    02195                                    CurrentProcess);
    02196
    02197     CloneProto->CloneRefCount += 1;
    02198
    02199     UNLOCK_PFN (OldIrql);
    02200     return;
    02201 }

VOID MiUpControlAreaRefs | ( | IN PCONTROL_AREA | ControlArea | ) |

Definition at line 2115 of file forksup.c.

References LOCK_PFN, and UNLOCK_PFN.

Referenced by MiCloneProcessAddressSpace().

    02119 {
    02120     KIRQL OldIrql;
    02121
    02122     LOCK_PFN (OldIrql);
    02123
    02124     ControlArea->NumberOfMappedViews += 1;
    02125     ControlArea->NumberOfUserReferences += 1;
    02126
    02127     UNLOCK_PFN (OldIrql);
    02128     return;
    02129 }

VOID MiUpForkPageShareCount | ( | IN PMMPFN | PfnForkPtePage | ) |

Definition at line 2366 of file forksup.c.

References LOCK_PFN, and UNLOCK_PFN.

Referenced by MiCloneProcessAddressSpace().

    02369 {
    02370     KIRQL OldIrql;
    02371
    02372     LOCK_PFN (OldIrql);
    02373     PfnForkPtePage->u2.ShareCount += 1;
    02374
    02375     UNLOCK_PFN (OldIrql);
    02376     return;
    02377 }

VOID MiUpPfnReferenceCount | ( | IN PFN_NUMBER | Page,
|  | IN USHORT | Count
| ) |

Definition at line 2076 of file forksup.c.

References Count, LOCK_PFN, MI_PFN_ELEMENT, _MMPFN::u3, and UNLOCK_PFN.

Referenced by MiCloneProcessAddressSpace().

    02083 {
    02084     KIRQL OldIrql;
    02085     PMMPFN Pfn1;
    02086
    02087     Pfn1 = MI_PFN_ELEMENT (Page);
    02088     LOCK_PFN (OldIrql);
    02089     Pfn1->u3.e2.ReferenceCount += Count;
    02090     UNLOCK_PFN (OldIrql);
    02091     return;
    02092 }

VOID MiWaitForForkToComplete | ( | IN PEPROCESS | CurrentProcess | ) |

Definition at line 1980 of file forksup.c.

References APC_LEVEL, LOCK_ADDRESS_SPACE, LOCK_PFN, LOCK_WS_REGARDLESS, UNLOCK_ADDRESS_SPACE, UNLOCK_PFN, and UNLOCK_WS_REGARDLESS.

Referenced by MiCopyOnWrite(), MiDecrementCloneBlockReference(), MiResolveDemandZeroFault(), and MmAccessFault().

    01986                    :
    01987
    01988     This routine waits for the current process to complete a fork
    01989     operation.
    01990
    01991 Arguments:
    01992
    01993     CurrentProcess - Supplies the current process value.
    01994
    01995 Return Value:
    01996
    01997     None.
    01998
    01999 Environment:
    02000
    02001     Kernel mode, APCs disabled, working set mutex and PFN mutex held.
    02002
    02003 --*/
    02004
    02005 {
    02006     ULONG OldIrql;
    02007     LOGICAL WsHeldSafe;
    02008
    02009     //
    02010     // A fork operation is in progress and the count of clone-blocks
    02011     // and other structures may not be changed.  Release the mutexes
    02012     // and wait for the address creation mutex which governs the
    02013     // fork operation.
    02014     //
    02015
    02016     UNLOCK_PFN (APC_LEVEL);
    02017
    02018     //
    02019     // The working set mutex may have been acquired safely or unsafely
    02020     // by our caller.  Handle both cases here and below, carefully making sure
    02021     // that the OldIrql left in the WS mutex on return is the same as on entry.
    02022     //
    02023
    02024     OldIrql = CurrentProcess->WorkingSetLock.OldIrql;
    02025
    02026     UNLOCK_WS_REGARDLESS (CurrentProcess, WsHeldSafe);
    02027
    02028     //
    02029     // Explicitly acquire the working set lock safely as it may be released and
    02030     // reacquired by our caller.
    02031     //
    02032
    02033     LOCK_ADDRESS_SPACE (CurrentProcess);
    02034
    02035     //
    02036     // The working set lock may have been acquired safely or unsafely
    02037     // by our caller.  Reacquire it in the same manner our caller did.
    02038     //
    02039
    02040     LOCK_WS_REGARDLESS (CurrentProcess, WsHeldSafe);
    02041
    02042     //
    02043     // Release the address creation mutex, the working set mutex
    02044     // must be held to set the ForkInProgress field.
    02045     //
    02046
    02047     UNLOCK_ADDRESS_SPACE (CurrentProcess);
    02048
    02049     //
    02050     // Ensure the previous IRQL is correctly set to its entry value.
    02051     //
    02052
    02053     CurrentProcess->WorkingSetLock.OldIrql = OldIrql;
    02054
    02055     //
    02056     // Get the PFN lock again.
    02057     //
    02058
    02059     LOCK_PFN (OldIrql);
    02060     return;
    02061 }
    #if DBG