VOID PsSetThreadHardErrorsAreDisabled( __in PETHREAD Thread, __in BOOLEAN HardErrorsAreDisabled ) { if (HardErrorsAreDisabled) { PS_SET_BITS (&Thread->CrossThreadFlags, PS_CROSS_THREAD_FLAGS_HARD_ERRORS_DISABLED); } else { PS_CLEAR_BITS (&Thread->CrossThreadFlags, PS_CROSS_THREAD_FLAGS_HARD_ERRORS_DISABLED); } }
BOOLEAN MmCreateProcessAddressSpace ( IN ULONG MinimumWorkingSetSize, IN PEPROCESS NewProcess, OUT PULONG_PTR DirectoryTableBase ) /*++ Routine Description: This routine creates an address space which maps the system portion and contains a hyper space entry. Arguments: MinimumWorkingSetSize - Supplies the minimum working set size for this address space. This value is only used to ensure that ample physical pages exist to create this process. NewProcess - Supplies a pointer to the process object being created. DirectoryTableBase - Returns the value of the newly created address space's Page Directory (PD) page and hyper space page. Return Value: Returns TRUE if an address space was successfully created, FALSE if ample physical pages do not exist. Environment: Kernel mode. APCs Disabled. --*/ { LOGICAL FlushTbNeeded; PFN_NUMBER PageDirectoryIndex; PFN_NUMBER HyperSpaceIndex; PFN_NUMBER PageContainingWorkingSet; PFN_NUMBER VadBitMapPage; MMPTE TempPte; MMPTE TempPte2; PEPROCESS CurrentProcess; KIRQL OldIrql; PMMPFN Pfn1; ULONG Color; PMMPTE PointerPte; ULONG PdeOffset; PMMPTE MappingPte; PMMPTE PointerFillPte; PMMPTE CurrentAddressSpacePde; // // Charge commitment for the page directory pages, working set page table // page, and working set list. If Vad bitmap lookups are enabled, then // charge for a page or two for that as well. // if (MiChargeCommitment (MM_PROCESS_COMMIT_CHARGE, NULL) == FALSE) { return FALSE; } FlushTbNeeded = FALSE; CurrentProcess = PsGetCurrentProcess (); NewProcess->NextPageColor = (USHORT) (RtlRandom (&MmProcessColorSeed)); KeInitializeSpinLock (&NewProcess->HyperSpaceLock); // // Get the PFN lock to get physical pages. // LOCK_PFN (OldIrql); // // Check to make sure the physical pages are available. // if (MI_NONPAGEABLE_MEMORY_AVAILABLE() <= (SPFN_NUMBER)MinimumWorkingSetSize){ UNLOCK_PFN (OldIrql); MiReturnCommitment (MM_PROCESS_COMMIT_CHARGE); // // Indicate no directory base was allocated. // return FALSE; } MM_TRACK_COMMIT (MM_DBG_COMMIT_PROCESS_CREATE, MM_PROCESS_COMMIT_CHARGE); MI_DECREMENT_RESIDENT_AVAILABLE (MinimumWorkingSetSize, MM_RESAVAIL_ALLOCATE_CREATE_PROCESS); // // Allocate a page directory page. // if (MmAvailablePages < MM_HIGH_LIMIT) { MiEnsureAvailablePageOrWait (NULL, OldIrql); } Color = MI_PAGE_COLOR_PTE_PROCESS (PDE_BASE, &CurrentProcess->NextPageColor); PageDirectoryIndex = MiRemoveZeroPageMayReleaseLocks (Color, OldIrql); Pfn1 = MI_PFN_ELEMENT (PageDirectoryIndex); if (Pfn1->u3.e1.CacheAttribute != MiCached) { Pfn1->u3.e1.CacheAttribute = MiCached; FlushTbNeeded = TRUE; } // // Allocate the hyper space page table page. // if (MmAvailablePages < MM_HIGH_LIMIT) { MiEnsureAvailablePageOrWait (NULL, OldIrql); } Color = MI_PAGE_COLOR_PTE_PROCESS (MiGetPdeAddress(HYPER_SPACE), &CurrentProcess->NextPageColor); HyperSpaceIndex = MiRemoveZeroPageMayReleaseLocks (Color, OldIrql); Pfn1 = MI_PFN_ELEMENT (HyperSpaceIndex); if (Pfn1->u3.e1.CacheAttribute != MiCached) { Pfn1->u3.e1.CacheAttribute = MiCached; FlushTbNeeded = TRUE; } // // Remove page(s) for the VAD bitmap. // if (MmAvailablePages < MM_HIGH_LIMIT) { MiEnsureAvailablePageOrWait (NULL, OldIrql); } Color = MI_PAGE_COLOR_VA_PROCESS (MmWorkingSetList, &CurrentProcess->NextPageColor); VadBitMapPage = MiRemoveZeroPageMayReleaseLocks (Color, OldIrql); Pfn1 = MI_PFN_ELEMENT (VadBitMapPage); if (Pfn1->u3.e1.CacheAttribute != MiCached) { Pfn1->u3.e1.CacheAttribute = MiCached; FlushTbNeeded = TRUE; } // // Remove a page for the working set list. // if (MmAvailablePages < MM_HIGH_LIMIT) { MiEnsureAvailablePageOrWait (NULL, OldIrql); } Color = MI_PAGE_COLOR_VA_PROCESS (MmWorkingSetList, &CurrentProcess->NextPageColor); PageContainingWorkingSet = MiRemoveZeroPageMayReleaseLocks (Color, OldIrql); Pfn1 = MI_PFN_ELEMENT (PageContainingWorkingSet); if (Pfn1->u3.e1.CacheAttribute != MiCached) { Pfn1->u3.e1.CacheAttribute = MiCached; FlushTbNeeded = TRUE; } UNLOCK_PFN (OldIrql); if (FlushTbNeeded == TRUE) { MI_FLUSH_TB_FOR_CACHED_ATTRIBUTE (); } ASSERT (NewProcess->AddressSpaceInitialized == 0); PS_SET_BITS (&NewProcess->Flags, PS_PROCESS_FLAGS_ADDRESS_SPACE1); ASSERT (NewProcess->AddressSpaceInitialized == 1); NewProcess->Vm.MinimumWorkingSetSize = MinimumWorkingSetSize; NewProcess->WorkingSetPage = PageContainingWorkingSet; INITIALIZE_DIRECTORY_TABLE_BASE (&DirectoryTableBase[0], PageDirectoryIndex); INITIALIZE_DIRECTORY_TABLE_BASE (&DirectoryTableBase[1], HyperSpaceIndex); // // Initialize the page reserved for hyper space. // TempPte = ValidPdePde; MI_SET_GLOBAL_STATE (TempPte, 0); MappingPte = MiReserveSystemPtes (1, SystemPteSpace); if (MappingPte != NULL) { MI_MAKE_VALID_KERNEL_PTE (TempPte2, HyperSpaceIndex, MM_READWRITE, MappingPte); MI_SET_PTE_DIRTY (TempPte2); MI_WRITE_VALID_PTE (MappingPte, TempPte2); PointerPte = MiGetVirtualAddressMappedByPte (MappingPte); } else { PointerPte = MiMapPageInHyperSpace (CurrentProcess, HyperSpaceIndex, &OldIrql); } TempPte.u.Hard.PageFrameNumber = VadBitMapPage; PointerPte[MiGetPteOffset(VAD_BITMAP_SPACE)] = TempPte; TempPte.u.Hard.PageFrameNumber = PageContainingWorkingSet; PointerPte[MiGetPteOffset(MmWorkingSetList)] = TempPte; if (MappingPte != NULL) { MiReleaseSystemPtes (MappingPte, 1, SystemPteSpace); } else { MiUnmapPageInHyperSpace (CurrentProcess, PointerPte, OldIrql); } // // Set the PTE address in the PFN for the page directory page. // Pfn1 = MI_PFN_ELEMENT (PageDirectoryIndex); Pfn1->PteAddress = (PMMPTE)PDE_BASE; TempPte = ValidPdePde; TempPte.u.Hard.PageFrameNumber = HyperSpaceIndex; MI_SET_GLOBAL_STATE (TempPte, 0); // // Add the new process to our internal list prior to filling any // system PDEs so if a system PDE changes (large page map or unmap) // it can mark this process for a subsequent update. // ASSERT (NewProcess->Pcb.DirectoryTableBase[0] == 0); LOCK_EXPANSION (OldIrql); InsertTailList (&MmProcessList, &NewProcess->MmProcessLinks); UNLOCK_EXPANSION (OldIrql); // // Map the page directory page in hyperspace. // MappingPte = MiReserveSystemPtes (1, SystemPteSpace); if (MappingPte != NULL) { MI_MAKE_VALID_KERNEL_PTE (TempPte2, PageDirectoryIndex, MM_READWRITE, MappingPte); MI_SET_PTE_DIRTY (TempPte2); MI_WRITE_VALID_PTE (MappingPte, TempPte2); PointerPte = MiGetVirtualAddressMappedByPte (MappingPte); } else { PointerPte = MiMapPageInHyperSpace (CurrentProcess, PageDirectoryIndex, &OldIrql); } PdeOffset = MiGetPdeOffset (MmSystemRangeStart); PointerFillPte = &PointerPte[PdeOffset]; CurrentAddressSpacePde = MiGetPdeAddress (MmSystemRangeStart); RtlCopyMemory (PointerFillPte, CurrentAddressSpacePde, PAGE_SIZE - PdeOffset * sizeof (MMPTE)); // // Map the working set page table page. // PdeOffset = MiGetPdeOffset (HYPER_SPACE); PointerPte[PdeOffset] = TempPte; // // Zero the remaining page directory range used to map the working // set list and its hash. // PdeOffset += 1; ASSERT (MiGetPdeOffset (MmHyperSpaceEnd) >= PdeOffset); MiZeroMemoryPte (&PointerPte[PdeOffset], (MiGetPdeOffset (MmHyperSpaceEnd) - PdeOffset + 1)); // // Recursively map the page directory page so it points to itself. // TempPte.u.Hard.PageFrameNumber = PageDirectoryIndex; PointerPte[MiGetPdeOffset(PTE_BASE)] = TempPte; if (MappingPte != NULL) { MiReleaseSystemPtes (MappingPte, 1, SystemPteSpace); } else { MiUnmapPageInHyperSpace (CurrentProcess, PointerPte, OldIrql); } InterlockedExchangeAddSizeT (&MmProcessCommit, MM_PROCESS_COMMIT_CHARGE); // // Up the session space reference count. // MiSessionAddProcess (NewProcess); return TRUE; }
NTSTATUS Ke386CallBios ( IN ULONG BiosCommand, IN OUT PCONTEXT BiosArguments ) /*++ Routine Description: This function invokes specified ROM BIOS code by executing "INT BiosCommand." Before executing the BIOS code, this function will setup VDM context, change stack pointer ...etc. If for some reason the operation fails, a status code will be returned. Otherwise, this function always returns success regardless of the result of the BIOS call. N.B. This implementation relies on the fact that the direct I/O access operations between apps are serialized by win user. Arguments: BiosCommand - Supplies which ROM BIOS function to invoke. BiosArguments - Supplies a pointer to the context which will be used to invoke ROM BIOS. Return Value: NTSTATUS code to specify the failure. --*/ { PVDM_TIB VdmTib; PUCHAR BaseAddress = (PUCHAR)V86_CODE_ADDRESS; PTEB UserInt10Teb = (PTEB)INT_10_TEB; PKTSS Tss; PKPROCESS Process; PKTHREAD Thread; USHORT OldIopmOffset, OldIoMapBase; PVDM_PROCESS_OBJECTS VdmObjects; ULONG ContextLength; UCHAR ThreadDebugActiveMask; // // Map in ROM BIOS area to perform the int 10 code // try { RtlZeroMemory(UserInt10Teb, sizeof(TEB)); // // Write "Int BiosCommand; bop" to reserved user space (0x1000). // Later control will transfer to the user space to execute // these two instructions. // *BaseAddress++ = INT_OPCODE; *BaseAddress++ = (UCHAR)BiosCommand; *(PULONG)BaseAddress = V86_BOP_OPCODE; // // Set up Vdm(v86) context to execute the int BiosCommand // instruction by copying user supplied context to VdmContext // and updating the control registers to predefined values. // // // We want to use a constant number for the int10. // // Create a fake TEB so we can switch the thread to it while we // do an int10 // UserInt10Teb->Vdm = (PVOID)VDM_TIB_ADDRESS; VdmTib = (PVDM_TIB)VDM_TIB_ADDRESS; RtlZeroMemory(VdmTib, sizeof(VDM_TIB)); VdmTib->Size = sizeof(VDM_TIB); *FIXED_NTVDMSTATE_LINEAR_PC_AT = 0; // // extended registers are never going to matter to // an Int10 call, so only copy the old part of the // context record. // ContextLength = FIELD_OFFSET(CONTEXT, ExtendedRegisters); RtlCopyMemory(&(VdmTib->VdmContext), BiosArguments, ContextLength); VdmTib->VdmContext.SegCs = (ULONG)BaseAddress >> 4; VdmTib->VdmContext.SegSs = (ULONG)BaseAddress >> 4; VdmTib->VdmContext.Eip = 0; VdmTib->VdmContext.Esp = 2 * PAGE_SIZE - sizeof(ULONG); VdmTib->VdmContext.EFlags |= EFLAGS_V86_MASK | EFLAGS_INTERRUPT_MASK; VdmTib->VdmContext.ContextFlags = CONTEXT_FULL; } except (EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); } // // The vdm kernel code finds the Tib by looking at a pointer cached in // kernel memory, which was probed at Vdm creation time. Since the // creation semantics for this vdm are peculiar, we do something similar // here. // // // We never get here on a process that is a real vdm. If we do, // bad things will happen (pool leak, failure to execute dos and // windows apps). // ASSERT(PsGetCurrentProcess()->VdmObjects == NULL); VdmObjects = ExAllocatePoolWithTag (NonPagedPool, sizeof(VDM_PROCESS_OBJECTS), ' eK' ); // // Since we are doing this on behalf of CSR not a user process, we aren't // charging quota. // if (VdmObjects == NULL) { return STATUS_NO_MEMORY; } // // We are only initializing the VdmTib pointer, because that's the only // part of the VdmObjects we use for ROM calls. We aren't set up // to simulate interrupts, or any of the other stuff that would be done // in a conventional vdm // RtlZeroMemory( VdmObjects, sizeof(VDM_PROCESS_OBJECTS)); VdmObjects->VdmTib = VdmTib; PsGetCurrentProcess()->VdmObjects = VdmObjects; PS_SET_BITS(&PsGetCurrentProcess()->Flags, PS_PROCESS_FLAGS_VDM_ALLOWED); // // Since we are going to v86 mode and accessing some I/O ports, we // need to make sure the IopmOffset is set correctly across context // swap and the I/O bit map has all the bits cleared. // N.B. This implementation assumes that there is only one full // screen DOS app and the io access between full screen DOS // app and the server code is serialized by win user. That // means even we change the IOPM, the full screen dos app won't // be able to run on this IOPM. // * In another words, IF THERE IS // * MORE THAN ONE FULL SCREEN DOS APPS, THIS CODE IS BROKEN.* // // NOTE This code works on the assumption that winuser serializes // direct I/O access operations. // // // Call the bios from the processor which booted the machine. // Thread = KeGetCurrentThread(); KeSetSystemAffinityThread(1); Tss = KeGetPcr()->TSS; // // Save away the original IOPM bit map and clear all the IOPM bits // to allow v86 int 10 code to access ALL the io ports. // // // Make sure there are at least 2 IOPM maps. // ASSERT(KeGetPcr()->GDT[KGDT_TSS / 8].LimitLow >= (0x2000 + IOPM_OFFSET - 1)); RtlCopyMemory (Ki386IopmSaveArea, (PVOID)&Tss->IoMaps[0].IoMap, PAGE_SIZE * 2 ); RtlZeroMemory ((PVOID)&Tss->IoMaps[0].IoMap, PAGE_SIZE * 2); Process = Thread->ApcState.Process; OldIopmOffset = Process->IopmOffset; OldIoMapBase = Tss->IoMapBase; Process->IopmOffset = (USHORT)(IOPM_OFFSET); // Set Process IoPmOffset before Tss->IoMapBase = (USHORT)(IOPM_OFFSET); // updating Tss IoMapBase // // The context setup for the BIOS will not have valid debug registers // in it, don't try to load them. // ThreadDebugActiveMask = (UCHAR) Thread->Header.DebugActive; Thread->Header.DebugActive = (BOOLEAN) 0; // // Call ASM routine to switch stack to exit to v86 mode to // run Int BiosCommand. // Ki386SetupAndExitToV86Code(UserInt10Teb); // // After we return from v86 mode, the control comes here. // // Restore Thread's DebugActive flag. // Thread->Header.DebugActive = (BOOLEAN) ThreadDebugActiveMask; // // Restore old IOPM // RtlCopyMemory ((PVOID)&Tss->IoMaps[0].IoMap, Ki386IopmSaveArea, PAGE_SIZE * 2 ); Process->IopmOffset = OldIopmOffset; Tss->IoMapBase = OldIoMapBase; // // Restore old affinity for current thread. // KeRevertToUserAffinityThread(); // // Copy 16 bit vdm context back to caller. // // Extended register state is not going to matter, // so copy only the old part of the context record. // ContextLength = FIELD_OFFSET(CONTEXT, ExtendedRegisters); RtlCopyMemory(BiosArguments, &(VdmTib->VdmContext), ContextLength); BiosArguments->ContextFlags = CONTEXT_FULL; // // Free the pool used for the VdmTib pointer // PsGetCurrentProcess()->VdmObjects = NULL; PS_CLEAR_BITS(&PsGetCurrentProcess()->Flags, PS_PROCESS_FLAGS_VDM_ALLOWED); ExFreePool(VdmObjects); return STATUS_SUCCESS; }