Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3711 lines
92 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name:
  4. altperm.c
  5. Abstract:
  6. This module contains the routines to support 4K pages on IA64.
  7. An alternate set of permissions is kept that are on 4K boundaries.
  8. Permissions are kept for all memory, not just split pages
  9. and the information is updated on any call to NtVirtualProtect()
  10. and NtAllocateVirtualMemory().
  11. Author:
  12. Koichi Yamada 18-Aug-1998
  13. Landy Wang (landyw) 02-June-1997
  14. Revision History:
  15. --*/
  16. #include "mi.h"
  17. #if defined(_MIALT4K_)
  18. ULONG
  19. MiFindProtectionForNativePte (
  20. PVOID VirtualAddress
  21. );
  22. VOID
  23. MiFillZeroFor4kPage (
  24. IN PVOID BaseAddress,
  25. IN PEPROCESS Process
  26. );
  27. VOID
  28. MiResetAccessBitForNativePtes (
  29. IN PVOID StartVirtual,
  30. IN PVOID EndVirtual,
  31. IN PEPROCESS Process
  32. );
  33. LOGICAL
  34. MiIsSplitPage (
  35. IN PVOID Virtual
  36. );
  37. VOID
  38. MiCopyOnWriteFor4kPage (
  39. PVOID VirtualAddress
  40. );
  41. VOID
  42. MiCheckDemandZeroCopyOnWriteFor4kPage (
  43. PVOID VirtualAddress,
  44. PEPROCESS Process
  45. );
  46. VOID
  47. MiCheckVirtualAddressFor4kPage (
  48. PVOID VirtualAddress,
  49. PEPROCESS Process
  50. );
  51. LOGICAL
  52. MiIsNativeGuardPage (
  53. IN PVOID VirtualAddress
  54. );
  55. VOID
  56. MiSetNativePteProtection (
  57. IN PVOID VirtualAddress,
  58. IN ULONGLONG NewPteProtection,
  59. IN LOGICAL PageIsSplit,
  60. IN PEPROCESS CurrentProcess
  61. );
  62. extern PMMPTE MmPteHit;
  63. NTSTATUS
  64. MmX86Fault (
  65. IN ULONG_PTR FaultStatus,
  66. IN PVOID VirtualAddress,
  67. IN KPROCESSOR_MODE PreviousMode,
  68. IN PVOID TrapInformation
  69. )
  70. /*++
  71. Routine Description:
  72. This function is called by the kernel on data or instruction
  73. access faults if CurrentProcess->Wow64Process is non-NULL and the
  74. faulting address is within the first 2GB.
  75. This routine determines the type of fault by checking the alternate
  76. 4Kb granular page table and calls MmAccessFault() if necessary to
  77. handle the page fault or the write fault.
  78. Arguments:
  79. FaultStatus - Supplies fault status information bits.
  80. VirtualAddress - Supplies the virtual address which caused the fault.
  81. PreviousMode - Supplies the mode (kernel or user) in which the fault
  82. occurred.
  83. TrapInformation - Opaque information about the trap, interpreted by the
  84. kernel, not Mm. Needed to allow fast interlocked access
  85. to operate correctly.
  86. Return Value:
  87. Returns the status of the fault handling operation. Can be one of:
  88. - Success.
  89. - Access Violation.
  90. - Guard Page Violation.
  91. - In-page Error.
  92. Environment:
  93. Kernel mode, APCs disabled.
  94. --*/
  95. {
  96. ULONG i;
  97. PMMPTE PointerAltPte;
  98. PMMPTE PointerAltPteForNativePage;
  99. MMPTE AltPteContents;
  100. PMMPTE PointerPte;
  101. PMMPTE PointerPde;
  102. ULONGLONG NewPteProtection;
  103. LOGICAL FillZero;
  104. LOGICAL PageIsSplit;
  105. LOGICAL SharedPageFault;
  106. LOGICAL NativeGuardPage;
  107. PEPROCESS CurrentProcess;
  108. PWOW64_PROCESS Wow64Process;
  109. KIRQL PreviousIrql;
  110. KIRQL OldIrql;
  111. NTSTATUS status;
  112. ULONG OriginalProtection;
  113. ULONGLONG ProtectionMaskOriginal;
  114. PMMPTE ProtoPte;
  115. PMMPFN Pfn1;
  116. PVOID OriginalVirtualAddress;
  117. ASSERT (VirtualAddress < (PVOID)MM_MAX_WOW64_ADDRESS);
  118. PreviousIrql = KeGetCurrentIrql ();
  119. if (PreviousIrql > APC_LEVEL) {
  120. return MmAccessFault (FaultStatus,
  121. VirtualAddress,
  122. PreviousMode,
  123. TrapInformation);
  124. }
  125. NewPteProtection = 0;
  126. FillZero = FALSE;
  127. PageIsSplit = FALSE;
  128. SharedPageFault = FALSE;
  129. NativeGuardPage = FALSE;
  130. PointerAltPteForNativePage = NULL;
  131. OriginalVirtualAddress = VirtualAddress;
  132. CurrentProcess = PsGetCurrentProcess ();
  133. Wow64Process = CurrentProcess->Wow64Process;
  134. PointerPte = MiGetPteAddress (VirtualAddress);
  135. PointerAltPte = MiGetAltPteAddress (VirtualAddress);
  136. #if DBG
  137. if (PointerPte == MmPteHit) {
  138. DbgPrint ("MM: PTE hit at %p\n", MmPteHit);
  139. DbgBreakPoint ();
  140. }
  141. #endif
  142. //
  143. // Acquire the alternate table mutex, also blocking APCs.
  144. //
  145. LOCK_ALTERNATE_TABLE (Wow64Process);
  146. //
  147. // If a fork operation is in progress and the faulting thread
  148. // is not the thread performing the fork operation, block until
  149. // the fork is completed.
  150. //
  151. if (CurrentProcess->ForkInProgress != NULL) {
  152. UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  153. KeLowerIrql (PreviousIrql);
  154. LOCK_WS (CurrentProcess);
  155. if (MiWaitForForkToComplete (CurrentProcess, FALSE) == FALSE) {
  156. ASSERT (FALSE);
  157. }
  158. UNLOCK_WS (CurrentProcess);
  159. return STATUS_SUCCESS;
  160. }
  161. //
  162. // Check to see if the protection is registered in the alternate entry.
  163. //
  164. if (MI_CHECK_BIT (Wow64Process->AltPermBitmap,
  165. MI_VA_TO_VPN(VirtualAddress)) == 0) {
  166. MiCheckVirtualAddressFor4kPage (VirtualAddress, CurrentProcess);
  167. }
  168. //
  169. // Read the alternate PTE contents.
  170. //
  171. AltPteContents = *PointerAltPte;
  172. //
  173. // Check to see if the alternate entry is no access.
  174. //
  175. if (AltPteContents.u.Alt.NoAccess != 0) {
  176. //
  177. // This 4KB page is no access.
  178. //
  179. status = STATUS_ACCESS_VIOLATION;
  180. #if DBG
  181. if (MmDebug & MM_DBG_STOP_ON_ACCVIO) {
  182. DbgPrint ("MM:access violation - %p\n",OriginalVirtualAddress);
  183. MiFormatPte (PointerPte);
  184. DbgBreakPoint ();
  185. }
  186. #endif
  187. goto return_status;
  188. }
  189. //
  190. // Check to see if the alternate entry is empty or if anyone has made any
  191. // commitments for the shared pages.
  192. //
  193. if ((AltPteContents.u.Long == 0) ||
  194. ((AltPteContents.u.Alt.Commit == 0) && (AltPteContents.u.Alt.Private == 0))) {
  195. //
  196. // If empty, get the protection information and fill the entry.
  197. //
  198. LOCK_WS (CurrentProcess);
  199. ProtoPte = MiCheckVirtualAddress (VirtualAddress, &OriginalProtection);
  200. if (ProtoPte != NULL) {
  201. if (OriginalProtection == MM_UNKNOWN_PROTECTION) {
  202. if (!MI_IS_PHYSICAL_ADDRESS(ProtoPte)) {
  203. PointerPde = MiGetPteAddress (ProtoPte);
  204. LOCK_PFN (OldIrql);
  205. if (PointerPde->u.Hard.Valid == 0) {
  206. MiMakeSystemAddressValidPfn (ProtoPte);
  207. }
  208. Pfn1 = MI_PFN_ELEMENT (PointerPde->u.Hard.PageFrameNumber);
  209. MI_ADD_LOCKED_PAGE_CHARGE(Pfn1, 28);
  210. Pfn1->u3.e2.ReferenceCount += 1;
  211. ASSERT (Pfn1->u3.e2.ReferenceCount > 1);
  212. UNLOCK_PFN (OldIrql);
  213. }
  214. else {
  215. Pfn1 = NULL;
  216. }
  217. OriginalProtection =
  218. MiMakeProtectionMask (MiGetPageProtection (ProtoPte,
  219. CurrentProcess,
  220. FALSE));
  221. //
  222. // Unlock the page containing the prototype PTEs.
  223. //
  224. if (Pfn1 != NULL) {
  225. ASSERT (!MI_IS_PHYSICAL_ADDRESS(ProtoPte));
  226. LOCK_PFN (OldIrql);
  227. ASSERT (Pfn1->u3.e2.ReferenceCount > 1);
  228. MI_REMOVE_LOCKED_PAGE_CHARGE(Pfn1, 29);
  229. Pfn1->u3.e2.ReferenceCount -= 1;
  230. UNLOCK_PFN (OldIrql);
  231. }
  232. }
  233. UNLOCK_WS (CurrentProcess);
  234. if (OriginalProtection == MM_INVALID_PROTECTION) {
  235. status = STATUS_ACCESS_VIOLATION;
  236. #if DBG
  237. if (MmDebug & MM_DBG_STOP_ON_ACCVIO) {
  238. DbgPrint ("MM:access violation - %p\n",OriginalVirtualAddress);
  239. MiFormatPte (PointerPte);
  240. DbgBreakPoint ();
  241. }
  242. #endif
  243. goto return_status;
  244. }
  245. if (OriginalProtection != MM_NOACCESS) {
  246. ProtectionMaskOriginal = MiMakeProtectionAteMask (OriginalProtection);
  247. SharedPageFault = TRUE;
  248. ProtectionMaskOriginal |= MM_ATE_COMMIT;
  249. AltPteContents.u.Long = ProtectionMaskOriginal;
  250. AltPteContents.u.Alt.Protection = OriginalProtection;
  251. //
  252. // Atomically update the PTE.
  253. //
  254. PointerAltPte->u.Long = AltPteContents.u.Long;
  255. }
  256. }
  257. else {
  258. UNLOCK_WS (CurrentProcess);
  259. }
  260. }
  261. if (AltPteContents.u.Alt.Commit == 0) {
  262. //
  263. // If the page is not committed, return an STATUS_ACCESS_VIOLATION.
  264. //
  265. status = STATUS_ACCESS_VIOLATION;
  266. #if DBG
  267. if (MmDebug & MM_DBG_STOP_ON_ACCVIO) {
  268. DbgPrint ("MM:access violation - %p\n",OriginalVirtualAddress);
  269. MiFormatPte (PointerPte);
  270. DbgBreakPoint ();
  271. }
  272. #endif
  273. goto return_status;
  274. }
  275. //
  276. // Check whether the faulting page is split into 4k pages.
  277. //
  278. PageIsSplit = MiIsSplitPage (VirtualAddress);
  279. //
  280. // Get the real protection for the native PTE.
  281. //
  282. NewPteProtection = MiFindProtectionForNativePte (VirtualAddress);
  283. //
  284. // Set the Protection for the native PTE
  285. //
  286. MiSetNativePteProtection (VirtualAddress,
  287. NewPteProtection,
  288. PageIsSplit,
  289. CurrentProcess);
  290. //
  291. // Check the indirect PTE reference case. If so, set the protection for
  292. // the indirect PTE too.
  293. //
  294. if (AltPteContents.u.Alt.PteIndirect != 0) {
  295. PointerPte = (PMMPTE)(AltPteContents.u.Alt.PteOffset + PTE_UBASE);
  296. VirtualAddress = MiGetVirtualAddressMappedByPte (PointerPte);
  297. NewPteProtection = AltPteContents.u.Long & ALT_PROTECTION_MASK;
  298. if (AltPteContents.u.Alt.CopyOnWrite != 0) {
  299. NewPteProtection |= MM_PTE_COPY_ON_WRITE_MASK;
  300. }
  301. MiSetNativePteProtection (VirtualAddress,
  302. NewPteProtection,
  303. FALSE,
  304. CurrentProcess);
  305. }
  306. //
  307. // Since we release the AltTable lock before calling MmAccessFault,
  308. // there is a chance that two threads may execute concurrently inside
  309. // MmAccessFault, which would yield bad results since the initial native
  310. // PTE for the page has only READ protection on it. So if two threads
  311. // fault on the same address, one of them will execute through all of
  312. // this routine, however the other one will just return STATUS_SUCCESS
  313. // which will cause another fault to happen in which the protections
  314. // will be fixed on the native page.
  315. //
  316. // Note that in addition to the dual thread case there is also the case
  317. // of a single thread which also has an overlapped I/O pending (for example)
  318. // which can trigger an APC completion memory copy to the same page.
  319. // Protect against this by remaining at APC_LEVEL until clearing the
  320. // inpage in progress in the alternate PTE.
  321. //
  322. if (MI_ALT_PTE_IN_PAGE_IN_PROGRESS (PointerAltPte) != 0) {
  323. //
  324. // Release the Alt PTE lock
  325. //
  326. UNLOCK_ALTERNATE_TABLE (Wow64Process);
  327. //
  328. // Flush the TB as MiSetNativePteProtection may have edited the PTE.
  329. //
  330. KiFlushSingleTb (TRUE, OriginalVirtualAddress);
  331. //
  332. // Delay execution so that if this is a high priority thread,
  333. // it won't starve the other thread (that's doing the actual inpage)
  334. // as it may be running at a lower priority.
  335. //
  336. KeDelayExecutionThread (KernelMode, FALSE, (PLARGE_INTEGER)&MmShortTime);
  337. return STATUS_SUCCESS;
  338. }
  339. //
  340. // The faulting 4kb page must be a valid page, but we need to resolve it
  341. // on a case by case basis.
  342. //
  343. ASSERT (AltPteContents.u.Long != 0);
  344. ASSERT (AltPteContents.u.Alt.Commit != 0);
  345. if (AltPteContents.u.Alt.Accessed == 0) {
  346. //
  347. // When PointerAte->u.Hard.Accessed is zero, there are 4 possibilities:
  348. //
  349. // 1. Lowest Protection
  350. // 2. 4kb Demand Zero
  351. // 3. GUARD page fault
  352. // 4. This 4kb page is no access, but the other 4K page(s) within
  353. // the native page has accessible permissions.
  354. //
  355. if (AltPteContents.u.Alt.FillZero != 0) {
  356. //
  357. // Schedule it later.
  358. //
  359. FillZero = TRUE;
  360. }
  361. if ((AltPteContents.u.Alt.Protection & MM_GUARD_PAGE) != 0) {
  362. goto CheckGuardPage;
  363. }
  364. if (FillZero == FALSE) {
  365. //
  366. // This 4kb page has permission set to no access.
  367. //
  368. status = STATUS_ACCESS_VIOLATION;
  369. #if DBG
  370. if (MmDebug & MM_DBG_STOP_ON_ACCVIO) {
  371. DbgPrint ("MM:access violation - %p\n",OriginalVirtualAddress);
  372. MiFormatPte (PointerPte);
  373. DbgBreakPoint ();
  374. }
  375. #endif
  376. goto return_status;
  377. }
  378. }
  379. if (MI_FAULT_STATUS_INDICATES_EXECUTION(FaultStatus)) {
  380. //
  381. // Execute permission is already given to IA32 by setting it in
  382. // MI_MAKE_VALID_PTE().
  383. //
  384. }
  385. else if (MI_FAULT_STATUS_INDICATES_WRITE(FaultStatus)) {
  386. //
  387. // Check to see if this is a copy-on-write page.
  388. //
  389. if (AltPteContents.u.Alt.CopyOnWrite != 0) {
  390. //
  391. // Let MmAccessFault() perform the copy-on-write.
  392. //
  393. status = MmAccessFault (FaultStatus,
  394. VirtualAddress,
  395. PreviousMode,
  396. TrapInformation);
  397. if (NT_SUCCESS(status)) {
  398. MiCopyOnWriteFor4kPage (OriginalVirtualAddress);
  399. }
  400. goto return_status;
  401. }
  402. if (AltPteContents.u.Hard.Write == 0) {
  403. status = STATUS_ACCESS_VIOLATION;
  404. #if DBG
  405. if (MmDebug & MM_DBG_STOP_ON_ACCVIO) {
  406. DbgPrint ("MM:access violation - %p\n",OriginalVirtualAddress);
  407. MiFormatPte (PointerPte);
  408. DbgBreakPoint ();
  409. }
  410. #endif
  411. goto return_status;
  412. }
  413. }
  414. CheckGuardPage:
  415. //
  416. // Indicate that we have begun updating the PTE for this page.
  417. // Subsequent faults on this native page will be restarted.
  418. // This should happen only if the PTE isn't valid.
  419. //
  420. PointerAltPteForNativePage = MiGetAltPteAddress (PAGE_ALIGN (VirtualAddress));
  421. for (i = 0; i < SPLITS_PER_PAGE; i += 1) {
  422. PointerAltPteForNativePage->u.Alt.InPageInProgress = TRUE;
  423. PointerAltPteForNativePage += 1;
  424. }
  425. //
  426. // Let MmAccessFault() perform an inpage, dirty-bit setting, etc.
  427. //
  428. // Release the alternate table mutex but stay at APC_LEVEL to prevent an
  429. // incoming APC that references the same page from deadlocking this thread.
  430. // It is safe to drop below APC_LEVEL only after the in progress bit in
  431. // the alternate PTE has been cleared.
  432. //
  433. Wow64Process->AlternateTableAcquiredUnsafe = MI_MUTEX_ACQUIRED_UNSAFE;
  434. UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  435. status = MmAccessFault (FaultStatus,
  436. VirtualAddress,
  437. PreviousMode,
  438. TrapInformation);
  439. LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  440. for (i = 0; i < SPLITS_PER_PAGE; i += 1) {
  441. PointerAltPteForNativePage -= 1;
  442. PointerAltPteForNativePage->u.Alt.InPageInProgress = FALSE;
  443. }
  444. AltPteContents = *PointerAltPte;
  445. if ((AltPteContents.u.Alt.Protection & MM_GUARD_PAGE) != 0) {
  446. AltPteContents = *PointerAltPte;
  447. AltPteContents.u.Alt.Protection &= ~MM_GUARD_PAGE;
  448. AltPteContents.u.Alt.Accessed = 1;
  449. PointerAltPte->u.Long = AltPteContents.u.Long;
  450. if ((status != STATUS_PAGE_FAULT_GUARD_PAGE) &&
  451. (status != STATUS_STACK_OVERFLOW)) {
  452. UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  453. KeLowerIrql (PreviousIrql);
  454. status = MiCheckForUserStackOverflow (VirtualAddress);
  455. KeRaiseIrql (APC_LEVEL, &PreviousIrql);
  456. LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  457. }
  458. }
  459. else if (status == STATUS_GUARD_PAGE_VIOLATION) {
  460. //
  461. // Native PTE has the guard bit set, but the AltPte
  462. // doesn't have it.
  463. //
  464. NativeGuardPage = MiIsNativeGuardPage (VirtualAddress);
  465. if (NativeGuardPage == TRUE) {
  466. status = STATUS_SUCCESS;
  467. }
  468. }
  469. else if ((SharedPageFault == TRUE) && (status == STATUS_ACCESS_VIOLATION)) {
  470. PointerAltPte->u.Alt.Commit = 0;
  471. }
  472. return_status:
  473. KiFlushSingleTb (TRUE, OriginalVirtualAddress);
  474. if (FillZero == TRUE) {
  475. MiFillZeroFor4kPage (VirtualAddress, CurrentProcess);
  476. }
  477. UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  478. KeLowerIrql (PreviousIrql);
  479. return status;
  480. }
  481. ULONG
  482. MiFindProtectionForNativePte (
  483. IN PVOID VirtualAddress
  484. )
  485. /*++
  486. Routine Description:
  487. This function finds the protection for the native PTE.
  488. Arguments:
  489. VirtualAddress - Supplies a virtual address to be examined for
  490. the protection of the PTE.
  491. Return Value:
  492. The protection code.
  493. Environment:
  494. --*/
  495. {
  496. ULONG i;
  497. ULONG ProtectionCode;
  498. PMMPTE PointerAltPte;
  499. MMPTE AltPteContents;
  500. ProtectionCode = 0;
  501. PointerAltPte = MiGetAltPteAddress (PAGE_ALIGN(VirtualAddress));
  502. for (i = 0; i < SPLITS_PER_PAGE; i += 1) {
  503. AltPteContents.u.Long = PointerAltPte->u.Long;
  504. if (AltPteContents.u.Alt.PteIndirect == 0) {
  505. ProtectionCode |= (PointerAltPte->u.Long & ALT_PROTECTION_MASK);
  506. }
  507. PointerAltPte += 1;
  508. }
  509. return ProtectionCode;
  510. }
  511. //
  512. // Define and initialize the protection conversion table for the
  513. // Alternate Permision Table Entries.
  514. //
  515. ULONGLONG MmProtectToAteMask[32] = {
  516. MM_PTE_NOACCESS | MM_ATE_NOACCESS,
  517. MM_PTE_EXECUTE_READ | MM_PTE_ACCESS_MASK,
  518. MM_PTE_EXECUTE_READ | MM_PTE_ACCESS_MASK,
  519. MM_PTE_EXECUTE_READ | MM_PTE_ACCESS_MASK,
  520. MM_PTE_EXECUTE_READWRITE | MM_PTE_ACCESS_MASK,
  521. MM_PTE_EXECUTE_READ | MM_PTE_ACCESS_MASK | MM_ATE_COPY_ON_WRITE,
  522. MM_PTE_EXECUTE_READWRITE | MM_PTE_ACCESS_MASK,
  523. MM_PTE_EXECUTE_READ | MM_PTE_ACCESS_MASK | MM_ATE_COPY_ON_WRITE,
  524. MM_PTE_NOACCESS | MM_ATE_NOACCESS,
  525. MM_PTE_EXECUTE_READ | MM_PTE_ACCESS_MASK,
  526. MM_PTE_EXECUTE_READ | MM_PTE_ACCESS_MASK,
  527. MM_PTE_EXECUTE_READ | MM_PTE_ACCESS_MASK,
  528. MM_PTE_EXECUTE_READWRITE | MM_PTE_ACCESS_MASK,
  529. MM_PTE_EXECUTE_READ | MM_PTE_ACCESS_MASK | MM_ATE_COPY_ON_WRITE,
  530. MM_PTE_EXECUTE_READWRITE | MM_PTE_ACCESS_MASK,
  531. MM_PTE_EXECUTE_READ | MM_PTE_ACCESS_MASK | MM_ATE_COPY_ON_WRITE,
  532. MM_PTE_NOACCESS | MM_ATE_NOACCESS,
  533. MM_PTE_EXECUTE_READ,
  534. MM_PTE_EXECUTE_READ,
  535. MM_PTE_EXECUTE_READ,
  536. MM_PTE_EXECUTE_READWRITE,
  537. MM_PTE_EXECUTE_READ | MM_ATE_COPY_ON_WRITE,
  538. MM_PTE_EXECUTE_READWRITE,
  539. MM_PTE_EXECUTE_READ | MM_ATE_COPY_ON_WRITE,
  540. MM_PTE_NOACCESS | MM_ATE_NOACCESS,
  541. MM_PTE_EXECUTE_READ,
  542. MM_PTE_EXECUTE_READ,
  543. MM_PTE_EXECUTE_READ,
  544. MM_PTE_EXECUTE_READWRITE,
  545. MM_PTE_EXECUTE_READ | MM_ATE_COPY_ON_WRITE,
  546. MM_PTE_EXECUTE_READWRITE,
  547. MM_PTE_EXECUTE_READ | MM_ATE_COPY_ON_WRITE
  548. };
  549. #define MiMakeProtectionAteMask(NewProtect) MmProtectToAteMask[NewProtect]
  550. VOID
  551. MiProtectFor4kPage (
  552. IN PVOID Base,
  553. IN SIZE_T Size,
  554. IN ULONG NewProtect,
  555. IN ULONG Flags,
  556. IN PEPROCESS Process
  557. )
  558. /*++
  559. Routine Description:
  560. This routine sets the permissions on the alternate bitmap (based on
  561. 4K page sizes). The base and size are assumed to be aligned for
  562. 4K pages already.
  563. Arguments:
  564. Base - Supplies the base address (assumed to be 4K aligned already).
  565. Size - Supplies the size to be protected (assumed to be 4K aligned already).
  566. NewProtect - Supplies the protection for the new pages.
  567. Flags - Supplies the alternate table entry request flags.
  568. Process - Supplies a pointer to the process in which to create the
  569. protections on the alternate table.
  570. Return Value:
  571. None.
  572. Environment:
  573. Kernel mode. Address creation mutex held at APC_LEVEL.
  574. --*/
  575. {
  576. ULONG i;
  577. RTL_BITMAP BitMap;
  578. ULONG NumberOfPtes;
  579. ULONG Starting4KVpn;
  580. PVOID Starting4KAddress;
  581. PVOID Ending4KAddress;
  582. PVOID VirtualAddress;
  583. ULONG NewProtectNotCopy;
  584. ULONGLONG ProtectionMask;
  585. ULONGLONG ProtectionMaskNotCopy;
  586. PMMPTE StartAltPte;
  587. PMMPTE EndAltPte;
  588. PMMPTE StartAltPte0;
  589. PMMPTE EndAltPte0;
  590. PWOW64_PROCESS Wow64Process;
  591. PVOID Virtual[MM_MAXIMUM_FLUSH_COUNT];
  592. MMPTE AltPteContents;
  593. MMPTE TempAltPte;
  594. Starting4KAddress = Base;
  595. Ending4KAddress = (PCHAR)Base + Size - 1;
  596. //
  597. // If the addresses are not WOW64 then nothing needs to be done here.
  598. //
  599. if ((Starting4KAddress >= (PVOID)MM_MAX_WOW64_ADDRESS) ||
  600. (Ending4KAddress >= (PVOID)MM_MAX_WOW64_ADDRESS)) {
  601. return;
  602. }
  603. //
  604. // Set up the protection to be used for this range of addresses.
  605. //
  606. ProtectionMask = MiMakeProtectionAteMask (NewProtect);
  607. if ((NewProtect & MM_COPY_ON_WRITE_MASK) == MM_COPY_ON_WRITE_MASK) {
  608. NewProtectNotCopy = NewProtect & ~MM_PROTECTION_COPY_MASK;
  609. ProtectionMaskNotCopy = MiMakeProtectionAteMask (NewProtectNotCopy);
  610. }
  611. else {
  612. NewProtectNotCopy = NewProtect;
  613. ProtectionMaskNotCopy = ProtectionMask;
  614. }
  615. if (Flags & ALT_COMMIT) {
  616. ProtectionMask |= MM_ATE_COMMIT;
  617. ProtectionMaskNotCopy |= MM_ATE_COMMIT;
  618. }
  619. //
  620. // Get the entry in the table for each of these addresses.
  621. //
  622. StartAltPte = MiGetAltPteAddress (Starting4KAddress);
  623. EndAltPte = MiGetAltPteAddress (Ending4KAddress);
  624. NumberOfPtes = (ULONG) ADDRESS_AND_SIZE_TO_SPAN_PAGES (Starting4KAddress,
  625. (ULONG_PTR)Ending4KAddress -
  626. (ULONG_PTR)Starting4KAddress);
  627. ASSERT (NumberOfPtes != 0);
  628. //
  629. // Ensure the proper native TB entries get flushed.
  630. //
  631. if (NumberOfPtes < MM_MAXIMUM_FLUSH_COUNT) {
  632. VirtualAddress = PAGE_ALIGN (Starting4KAddress);
  633. for (i = 0; i < NumberOfPtes; i += 1) {
  634. Virtual[i] = (PVOID)VirtualAddress;
  635. VirtualAddress = (PVOID)((ULONG_PTR)VirtualAddress + PAGE_SIZE);
  636. }
  637. }
  638. StartAltPte0 = MiGetAltPteAddress (PAGE_ALIGN (Starting4KAddress));
  639. EndAltPte0 = MiGetAltPteAddress ((ULONG_PTR)PAGE_ALIGN(Ending4KAddress)+PAGE_SIZE-1);
  640. Wow64Process = Process->Wow64Process;
  641. Starting4KVpn = (ULONG) MI_VA_TO_VPN (Starting4KAddress);
  642. //
  643. // Acquire the mutex guarding the alternate page table.
  644. //
  645. LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  646. if (!(Flags & ALT_ALLOCATE) &&
  647. (MI_CHECK_BIT(Wow64Process->AltPermBitmap, Starting4KVpn) == 0)) {
  648. UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  649. return;
  650. }
  651. //
  652. // Change all of the protections.
  653. //
  654. while (StartAltPte <= EndAltPte) {
  655. AltPteContents.u.Long = StartAltPte->u.Long;
  656. TempAltPte.u.Long = ProtectionMask;
  657. TempAltPte.u.Alt.Protection = NewProtect;
  658. if (!(Flags & ALT_ALLOCATE)) {
  659. if (AltPteContents.u.Alt.Private != 0) {
  660. //
  661. // If it is already private, don't make it writecopy.
  662. //
  663. TempAltPte.u.Long = ProtectionMaskNotCopy;
  664. TempAltPte.u.Alt.Protection = NewProtectNotCopy;
  665. //
  666. // Private is sticky bit
  667. //
  668. TempAltPte.u.Alt.Private = 1;
  669. }
  670. if (AltPteContents.u.Alt.FillZero != 0) {
  671. TempAltPte.u.Alt.Accessed = 0;
  672. TempAltPte.u.Alt.FillZero = 1;
  673. }
  674. //
  675. // Leave the other sticky attribute bits
  676. //
  677. TempAltPte.u.Alt.Lock = AltPteContents.u.Alt.Lock;
  678. TempAltPte.u.Alt.PteIndirect = AltPteContents.u.Alt.PteIndirect;
  679. TempAltPte.u.Alt.PteOffset = AltPteContents.u.Alt.PteOffset;
  680. }
  681. if (Flags & ALT_CHANGE) {
  682. //
  683. // If it is a change request, make commit sticky.
  684. //
  685. TempAltPte.u.Alt.Commit = AltPteContents.u.Alt.Commit;
  686. }
  687. //
  688. // Atomic PTE update.
  689. //
  690. StartAltPte->u.Long = TempAltPte.u.Long;
  691. StartAltPte += 1;
  692. }
  693. if (Flags & ALT_ALLOCATE) {
  694. //
  695. // Fill the empty Alt PTE as NoAccess ATE at the end.
  696. //
  697. while (EndAltPte <= EndAltPte0) {
  698. if (EndAltPte->u.Long == 0) {
  699. TempAltPte.u.Long = EndAltPte->u.Long;
  700. TempAltPte.u.Alt.NoAccess = 1;
  701. //
  702. // Atomic PTE update.
  703. //
  704. EndAltPte->u.Long = TempAltPte.u.Long;
  705. }
  706. EndAltPte += 1;
  707. }
  708. //
  709. // Update the permission bitmap.
  710. //
  711. // Initialize the bitmap inline for speed.
  712. //
  713. BitMap.SizeOfBitMap = MM_MAX_WOW64_ADDRESS >> PTI_SHIFT;
  714. BitMap.Buffer = Wow64Process->AltPermBitmap;
  715. RtlSetBits (&BitMap, Starting4KVpn, NumberOfPtes);
  716. }
  717. MiResetAccessBitForNativePtes (Starting4KAddress, Ending4KAddress, Process);
  718. if (NumberOfPtes < MM_MAXIMUM_FLUSH_COUNT) {
  719. KeFlushMultipleTb (NumberOfPtes,
  720. &Virtual[0],
  721. TRUE,
  722. TRUE,
  723. NULL,
  724. ZeroPte.u.Flush);
  725. }
  726. else {
  727. KeFlushEntireTb (TRUE, TRUE);
  728. }
  729. UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  730. }
  731. VOID
  732. MiProtectMapFileFor4kPage (
  733. IN PVOID Base,
  734. IN SIZE_T Size,
  735. IN ULONG NewProtect,
  736. IN SIZE_T CommitSize,
  737. IN PMMPTE PointerPte,
  738. IN PMMPTE LastPte,
  739. IN PEPROCESS Process
  740. )
  741. /*++
  742. Routine Description:
  743. This routine sets the permissions on the alternate bitmap (based on
  744. 4K page sizes). The base and size are assumed to be aligned for
  745. 4K pages already.
  746. Arguments:
  747. Base - Supplies the base address (assumed to be 4K aligned already).
  748. Size - Supplies the size to be protected (assumed to be 4K aligned already).
  749. NewProtect - Supplies the protection for the new pages.
  750. CommitSize - Supplies the commit size.
  751. PointerPte - Supplies the starting PTE.
  752. LastPte - Supplies the last PTE.
  753. Process - Supplies a pointer to the process in which to create the
  754. protections on the alternate table.
  755. Return Value:
  756. None.
  757. Environment:
  758. Kernel mode. Address creation mutex held at APC_LEVEL.
  759. --*/
  760. {
  761. RTL_BITMAP BitMap;
  762. PVOID Starting4KAddress;
  763. PVOID Ending4KAddress;
  764. ULONGLONG ProtectionMask;
  765. PMMPTE StartAltPte;
  766. PMMPTE EndAltPte;
  767. PMMPTE EndAltPte0;
  768. PWOW64_PROCESS Wow64Process;
  769. MMPTE TempAltPte;
  770. PMMPTE LastCommitPte;
  771. Wow64Process = Process->Wow64Process;
  772. Starting4KAddress = Base;
  773. Ending4KAddress = (PCHAR)Base + Size - 1;
  774. //
  775. // If the addresses are not WOW64 then nothing needs to be done here.
  776. //
  777. if ((Starting4KAddress >= (PVOID)MM_MAX_WOW64_ADDRESS) ||
  778. (Ending4KAddress >= (PVOID)MM_MAX_WOW64_ADDRESS)) {
  779. return;
  780. }
  781. //
  782. // Set up the protection to be used for this range of addresses.
  783. //
  784. ProtectionMask = MiMakeProtectionAteMask (NewProtect);
  785. //
  786. // Get the entry in the table for each of these addresses.
  787. //
  788. StartAltPte = MiGetAltPteAddress (Starting4KAddress);
  789. EndAltPte = MiGetAltPteAddress (Ending4KAddress);
  790. EndAltPte0 = MiGetAltPteAddress((ULONG_PTR)PAGE_ALIGN(Ending4KAddress)+PAGE_SIZE-1);
  791. LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  792. ExAcquireFastMutexUnsafe (&MmSectionCommitMutex);
  793. //
  794. // And then change all of the protections.
  795. //
  796. LastCommitPte = PointerPte + BYTES_TO_PAGES(CommitSize);
  797. TempAltPte.u.Long = ProtectionMask;
  798. TempAltPte.u.Alt.Protection = NewProtect;
  799. while (StartAltPte <= EndAltPte) {
  800. if (PointerPte < LastCommitPte) {
  801. TempAltPte.u.Alt.Commit = 1;
  802. }
  803. else if ((PointerPte <= LastPte) && (PointerPte->u.Long != 0)) {
  804. TempAltPte.u.Alt.Commit = 1;
  805. }
  806. else {
  807. TempAltPte.u.Alt.Commit = 0;
  808. }
  809. //
  810. // Atomic PTE update.
  811. //
  812. StartAltPte->u.Long = TempAltPte.u.Long;
  813. StartAltPte += 1;
  814. if (((ULONG_PTR)StartAltPte & ((SPLITS_PER_PAGE * sizeof(MMPTE))-1)) == 0) {
  815. PointerPte += 1;
  816. }
  817. }
  818. ExReleaseFastMutexUnsafe (&MmSectionCommitMutex);
  819. //
  820. // Fill the empty Alt PTE as NoAccess ATE at the end.
  821. //
  822. while (EndAltPte <= EndAltPte0) {
  823. if (EndAltPte->u.Long == 0) {
  824. TempAltPte.u.Long = EndAltPte->u.Long;
  825. TempAltPte.u.Alt.NoAccess = 1;
  826. //
  827. // Atomic PTE size update.
  828. //
  829. EndAltPte->u.Long = TempAltPte.u.Long;
  830. }
  831. EndAltPte += 1;
  832. }
  833. //
  834. // Initialize the bitmap inline for speed.
  835. //
  836. BitMap.SizeOfBitMap = MM_MAX_WOW64_ADDRESS >> PTI_SHIFT;
  837. BitMap.Buffer = Wow64Process->AltPermBitmap;
  838. RtlSetBits (&BitMap,
  839. (ULONG) MI_VA_TO_VPN (Base),
  840. (ULONG) (MI_VA_TO_VPN (Ending4KAddress) - MI_VA_TO_VPN (Base) + 1));
  841. UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  842. }
  843. VOID
  844. MiProtectImageFileFor4kPage (
  845. IN PVOID Base,
  846. IN SIZE_T Size,
  847. IN PMMPTE PointerPte,
  848. IN PEPROCESS Process
  849. )
  850. /*++
  851. Environment:
  852. Kernel mode. Address creation mutex held at APC_LEVEL.
  853. --*/
  854. {
  855. RTL_BITMAP BitMap;
  856. PVOID Starting4KAddress;
  857. PVOID Ending4KAddress;
  858. ULONGLONG ProtectionMask;
  859. PMMPTE StartAltPte;
  860. PMMPTE EndAltPte;
  861. PMMPTE EndAltPte0;
  862. PWOW64_PROCESS Wow64Process;
  863. MMPTE TempAltPte;
  864. MMPTE TempPte;
  865. ULONG NewProtect;
  866. KIRQL OldIrql;
  867. Wow64Process = Process->Wow64Process;
  868. Starting4KAddress = Base;
  869. Ending4KAddress = (PCHAR)Base + Size - 1;
  870. //
  871. // If the addresses are not WOW64 then nothing needs to be done here.
  872. //
  873. if ((Starting4KAddress >= (PVOID)MM_MAX_WOW64_ADDRESS) ||
  874. (Ending4KAddress >= (PVOID)MM_MAX_WOW64_ADDRESS)) {
  875. return;
  876. }
  877. //
  878. // Get the entry in the table for each of these addresses.
  879. //
  880. StartAltPte = MiGetAltPteAddress (Starting4KAddress);
  881. EndAltPte = MiGetAltPteAddress (Ending4KAddress);
  882. EndAltPte0 = MiGetAltPteAddress((ULONG_PTR)PAGE_ALIGN(Ending4KAddress)+PAGE_SIZE-1);
  883. LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  884. //
  885. // And then change all of the protections.
  886. //
  887. while (StartAltPte <= EndAltPte) {
  888. //
  889. // Get the original protection information from the prototype PTEs.
  890. //
  891. LOCK_WS_UNSAFE (Process);
  892. LOCK_PFN (OldIrql);
  893. MiMakeSystemAddressValidPfnWs (PointerPte, Process);
  894. TempPte = *PointerPte;
  895. UNLOCK_PFN (OldIrql);
  896. NewProtect =
  897. MiMakeProtectionMask(MiGetPageProtection(&TempPte, Process, TRUE));
  898. ASSERT (NewProtect != MM_INVALID_PROTECTION);
  899. UNLOCK_WS_UNSAFE (Process);
  900. //
  901. // If demand-zero and copy-on-write, remove copy-on-write.
  902. //
  903. if ((!IS_PTE_NOT_DEMAND_ZERO(TempPte)) &&
  904. (TempPte.u.Soft.Protection & MM_COPY_ON_WRITE_MASK)) {
  905. NewProtect = NewProtect & ~MM_PROTECTION_COPY_MASK;
  906. }
  907. ProtectionMask = MiMakeProtectionAteMask (NewProtect);
  908. ProtectionMask |= MM_ATE_COMMIT;
  909. TempAltPte.u.Long = ProtectionMask;
  910. TempAltPte.u.Alt.Protection = NewProtect;
  911. if ((NewProtect & MM_PROTECTION_COPY_MASK) == 0) {
  912. //
  913. // If the copy-on-write is removed, make it private.
  914. //
  915. TempAltPte.u.Alt.Private = 1;
  916. }
  917. //
  918. // Atomic PTE update.
  919. //
  920. MiFillMemoryPte (StartAltPte,
  921. SPLITS_PER_PAGE * sizeof(MMPTE),
  922. TempAltPte.u.Long);
  923. StartAltPte += SPLITS_PER_PAGE;
  924. PointerPte += 1;
  925. }
  926. //
  927. // Fill the empty Alt PTE as NoAccess ATE at the end.
  928. //
  929. while (EndAltPte <= EndAltPte0) {
  930. if (EndAltPte->u.Long == 0) {
  931. TempAltPte.u.Long = EndAltPte->u.Long;
  932. TempAltPte.u.Alt.NoAccess = 1;
  933. //
  934. // Atomic PTE size update.
  935. //
  936. EndAltPte->u.Long = TempAltPte.u.Long;
  937. }
  938. EndAltPte += 1;
  939. }
  940. //
  941. // Initialize the bitmap inline for speed.
  942. //
  943. BitMap.SizeOfBitMap = MM_MAX_WOW64_ADDRESS >> PTI_SHIFT;
  944. BitMap.Buffer = Wow64Process->AltPermBitmap;
  945. RtlSetBits (&BitMap,
  946. (ULONG) MI_VA_TO_VPN (Base),
  947. (ULONG) (MI_VA_TO_VPN (Ending4KAddress) - MI_VA_TO_VPN (Base) + 1));
  948. UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  949. }
  950. VOID
  951. MiReleaseFor4kPage (
  952. IN PVOID StartVirtual,
  953. IN PVOID EndVirtual,
  954. IN PEPROCESS Process
  955. )
  956. /*++
  957. Routine Description:
  958. This function releases a region of pages within the virtual address
  959. space of the subject process.
  960. Arguments:
  961. StartVirtual - Supplies the start address of the region of pages
  962. to be released.
  963. EndVirtual - Supplies the end address of the region of pages to be released.
  964. Process - Supplies a pointer to the process in which to release the
  965. region of pages.
  966. Return Value:
  967. None.
  968. Environment:
  969. Kernel mode. Address creation mutex held at APC_LEVEL.
  970. --*/
  971. {
  972. RTL_BITMAP BitMap;
  973. PMMPTE StartAltPte;
  974. PMMPTE EndAltPte;
  975. MMPTE TempAltPte;
  976. ULONG_PTR VirtualAddress;
  977. PVOID OriginalStartVa, OriginalEndVa;
  978. ULONG i;
  979. PWOW64_PROCESS Wow64Process;
  980. PVOID Virtual[MM_MAXIMUM_FLUSH_COUNT];
  981. ULONG FlushCount;
  982. PFN_NUMBER NumberOfAltPtes;
  983. ULONG NumberOfPtes;
  984. ASSERT(StartVirtual <= EndVirtual);
  985. FlushCount = 0;
  986. OriginalStartVa = StartVirtual;
  987. OriginalEndVa = EndVirtual;
  988. Wow64Process = Process->Wow64Process;
  989. StartAltPte = MiGetAltPteAddress (StartVirtual);
  990. EndAltPte = MiGetAltPteAddress (EndVirtual);
  991. NumberOfAltPtes = EndAltPte - StartAltPte + 1;
  992. TempAltPte.u.Long = 0;
  993. TempAltPte.u.Alt.NoAccess = 1;
  994. TempAltPte.u.Alt.FillZero = 1;
  995. StartVirtual = PAGE_ALIGN(StartVirtual);
  996. VirtualAddress = (ULONG_PTR)StartVirtual;
  997. NumberOfPtes = (ULONG) ADDRESS_AND_SIZE_TO_SPAN_PAGES (StartVirtual,
  998. (ULONG_PTR)EndVirtual -
  999. (ULONG_PTR)StartVirtual);
  1000. ASSERT (NumberOfPtes != 0);
  1001. //
  1002. // Ensure the proper native TB entries get flushed.
  1003. //
  1004. if (NumberOfPtes < MM_MAXIMUM_FLUSH_COUNT) {
  1005. for (i = 0; i < NumberOfPtes; i += 1) {
  1006. Virtual[i] = (PVOID)VirtualAddress;
  1007. VirtualAddress += PAGE_SIZE;
  1008. }
  1009. VirtualAddress = (ULONG_PTR)StartVirtual;
  1010. }
  1011. LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  1012. MiFillMemoryPte (StartAltPte,
  1013. NumberOfAltPtes * sizeof(MMPTE),
  1014. TempAltPte.u.Long);
  1015. StartAltPte += NumberOfAltPtes;
  1016. while (VirtualAddress <= (ULONG_PTR)EndVirtual) {
  1017. StartAltPte = MiGetAltPteAddress(VirtualAddress);
  1018. TempAltPte = *StartAltPte;
  1019. i = 0;
  1020. //
  1021. // Note that this check must be made as the ATE fill above may not
  1022. // have begun on a native page boundary and this scan always does.
  1023. //
  1024. while (TempAltPte.u.Long == StartAltPte->u.Long) {
  1025. i += 1;
  1026. if (i == SPLITS_PER_PAGE) {
  1027. while (i != 0) {
  1028. StartAltPte->u.Long = 0;
  1029. StartAltPte -= 1;
  1030. i -= 1;
  1031. }
  1032. break;
  1033. }
  1034. StartAltPte += 1;
  1035. }
  1036. VirtualAddress += PAGE_SIZE;
  1037. }
  1038. MiResetAccessBitForNativePtes (StartVirtual, EndVirtual, Process);
  1039. //
  1040. // Mark the native released pages as non-split so they re-synced
  1041. // at MmX86Fault() time. NOTE: StartVirtual should be aligned on
  1042. // the native page size before executing this code.
  1043. //
  1044. if (BYTE_OFFSET (OriginalStartVa) != 0) {
  1045. if (MiArePreceding4kPagesAllocated (OriginalStartVa) != FALSE) {
  1046. StartVirtual = PAGE_ALIGN ((ULONG_PTR)StartVirtual + PAGE_SIZE);
  1047. }
  1048. }
  1049. EndVirtual = (PVOID) ((ULONG_PTR)EndVirtual | (PAGE_SIZE - 1));
  1050. if (BYTE_OFFSET (OriginalEndVa) != (PAGE_SIZE - 1)) {
  1051. if (MiAreFollowing4kPagesAllocated (OriginalEndVa) != FALSE) {
  1052. EndVirtual = (PVOID) ((ULONG_PTR)EndVirtual - PAGE_SIZE);
  1053. }
  1054. }
  1055. if (StartVirtual < EndVirtual) {
  1056. //
  1057. // Initialize the bitmap inline for speed.
  1058. //
  1059. BitMap.SizeOfBitMap = MM_MAX_WOW64_ADDRESS >> PTI_SHIFT;
  1060. BitMap.Buffer = Wow64Process->AltPermBitmap;
  1061. RtlClearBits (&BitMap,
  1062. (ULONG) MI_VA_TO_VPN (StartVirtual),
  1063. (ULONG) (MI_VA_TO_VPN (EndVirtual) - MI_VA_TO_VPN (StartVirtual) + 1));
  1064. }
  1065. if (NumberOfPtes < MM_MAXIMUM_FLUSH_COUNT) {
  1066. KeFlushMultipleTb (FlushCount,
  1067. &Virtual[0],
  1068. TRUE,
  1069. TRUE,
  1070. NULL,
  1071. ZeroPte.u.Flush);
  1072. }
  1073. else {
  1074. KeFlushEntireTb (TRUE, TRUE);
  1075. }
  1076. UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  1077. }
  1078. VOID
  1079. MiDecommitFor4kPage (
  1080. IN PVOID StartVirtual,
  1081. IN PVOID EndVirtual,
  1082. IN PEPROCESS Process
  1083. )
  1084. /*++
  1085. Routine Description:
  1086. This function decommits a region of pages within the virtual address
  1087. space of a subject process.
  1088. Arguments:
  1089. StartVirtual - Supplies the start address of the region of pages
  1090. to be decommitted.
  1091. EndVirtual - Supplies the end address of the region of the pages
  1092. to be decommitted.
  1093. Process - Supplies a pointer to the process in which to decommit a
  1094. a region of pages.
  1095. Return Value:
  1096. None.
  1097. Environment:
  1098. Address space mutex held at APC_LEVEL.
  1099. --*/
  1100. {
  1101. PMMPTE StartAltPte;
  1102. PMMPTE EndAltPte;
  1103. MMPTE TempAltPte;
  1104. ULONG_PTR VirtualAddress;
  1105. PWOW64_PROCESS Wow64Process;
  1106. PVOID Virtual[MM_MAXIMUM_FLUSH_COUNT];
  1107. ULONG i;
  1108. ULONG NumberOfPtes;
  1109. Wow64Process = Process->Wow64Process;
  1110. ASSERT(StartVirtual <= EndVirtual);
  1111. StartAltPte = MiGetAltPteAddress (StartVirtual);
  1112. EndAltPte = MiGetAltPteAddress (EndVirtual);
  1113. NumberOfPtes = (ULONG) ADDRESS_AND_SIZE_TO_SPAN_PAGES (StartVirtual,
  1114. (ULONG_PTR)EndVirtual -
  1115. (ULONG_PTR)StartVirtual);
  1116. ASSERT (NumberOfPtes != 0);
  1117. //
  1118. // Ensure the proper native TB entries get flushed.
  1119. //
  1120. if (NumberOfPtes < MM_MAXIMUM_FLUSH_COUNT) {
  1121. VirtualAddress = (ULONG_PTR)StartVirtual;
  1122. for (i = 0; i < NumberOfPtes; i += 1) {
  1123. Virtual[i] = (PVOID)VirtualAddress;
  1124. VirtualAddress += PAGE_SIZE;
  1125. }
  1126. }
  1127. LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  1128. while (StartAltPte <= EndAltPte) {
  1129. TempAltPte.u.Long = StartAltPte->u.Long;
  1130. TempAltPte.u.Alt.Commit = 0;
  1131. TempAltPte.u.Alt.Accessed = 0;
  1132. TempAltPte.u.Alt.FillZero = 1;
  1133. //
  1134. // Atomic PTE update.
  1135. //
  1136. StartAltPte->u.Long = TempAltPte.u.Long;
  1137. StartAltPte += 1;
  1138. }
  1139. //
  1140. // Flush the TB.
  1141. //
  1142. MiResetAccessBitForNativePtes (StartVirtual, EndVirtual, Process);
  1143. if (NumberOfPtes < MM_MAXIMUM_FLUSH_COUNT) {
  1144. KeFlushMultipleTb (NumberOfPtes,
  1145. &Virtual[0],
  1146. TRUE,
  1147. TRUE,
  1148. NULL,
  1149. ZeroPte.u.Flush);
  1150. }
  1151. else {
  1152. KeFlushEntireTb (TRUE, TRUE);
  1153. }
  1154. UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  1155. }
  1156. VOID
  1157. MiDeleteFor4kPage (
  1158. IN PVOID StartVirtual,
  1159. IN PVOID EndVirtual,
  1160. IN PEPROCESS Process
  1161. )
  1162. /*++
  1163. Routine Description:
  1164. This function deletes a region of pages within the virtual address
  1165. space of the subject process.
  1166. Arguments:
  1167. StartVirtual - Supplies the start address of the region of pages
  1168. to be deleted.
  1169. EndVirtual - Supplies the end address of the region of pages
  1170. to be deleted.
  1171. Process - Supplies a pointer to the process in which to delete
  1172. the region of pages.
  1173. Return Value:
  1174. None.
  1175. Environment:
  1176. Kernel mode. Address creation mutex held at APC_LEVEL.
  1177. --*/
  1178. {
  1179. ULONG i;
  1180. ULONG NumberOfPtes;
  1181. RTL_BITMAP BitMap;
  1182. PMMPTE EndAltPte;
  1183. PMMPTE StartAltPte;
  1184. ULONG_PTR VirtualAddress;
  1185. PWOW64_PROCESS Wow64Process;
  1186. PFN_NUMBER NumberOfAltPtes;
  1187. PVOID Virtual[MM_MAXIMUM_FLUSH_COUNT];
  1188. Wow64Process = Process->Wow64Process;
  1189. ASSERT(StartVirtual <= EndVirtual);
  1190. StartAltPte = MiGetAltPteAddress (StartVirtual);
  1191. EndAltPte = MiGetAltPteAddress (EndVirtual);
  1192. NumberOfAltPtes = EndAltPte - StartAltPte + 1;
  1193. VirtualAddress = (ULONG_PTR)StartVirtual;
  1194. NumberOfPtes = (ULONG) ADDRESS_AND_SIZE_TO_SPAN_PAGES (StartVirtual,
  1195. (ULONG_PTR)EndVirtual -
  1196. (ULONG_PTR)StartVirtual);
  1197. ASSERT (NumberOfPtes != 0);
  1198. //
  1199. // Ensure the proper native TB entries get flushed.
  1200. //
  1201. if (NumberOfPtes < MM_MAXIMUM_FLUSH_COUNT) {
  1202. VirtualAddress = (ULONG_PTR)StartVirtual;
  1203. for (i = 0; i < NumberOfPtes; i += 1) {
  1204. Virtual[i] = (PVOID)VirtualAddress;
  1205. VirtualAddress += PAGE_SIZE;
  1206. }
  1207. }
  1208. LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  1209. MiFillMemoryPte (StartAltPte,
  1210. NumberOfAltPtes * sizeof(MMPTE),
  1211. ZeroPte.u.Long);
  1212. //
  1213. // StartVirtual and EndVirtual are already aligned to the native
  1214. // PAGE_SIZE so no need to readjust them before removing the split markers.
  1215. //
  1216. // Initialize the bitmap inline for speed.
  1217. //
  1218. BitMap.SizeOfBitMap = MM_MAX_WOW64_ADDRESS >> PTI_SHIFT;
  1219. BitMap.Buffer = Wow64Process->AltPermBitmap;
  1220. RtlClearBits (&BitMap,
  1221. (ULONG) MI_VA_TO_VPN (StartVirtual),
  1222. (ULONG) (MI_VA_TO_VPN (EndVirtual) - MI_VA_TO_VPN (StartVirtual) + 1));
  1223. MiResetAccessBitForNativePtes (StartVirtual, EndVirtual, Process);
  1224. if (NumberOfPtes < MM_MAXIMUM_FLUSH_COUNT) {
  1225. KeFlushMultipleTb (NumberOfPtes,
  1226. &Virtual[0],
  1227. TRUE,
  1228. TRUE,
  1229. NULL,
  1230. ZeroPte.u.Flush);
  1231. }
  1232. else {
  1233. KeFlushEntireTb (TRUE, TRUE);
  1234. }
  1235. UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  1236. }
  1237. LOGICAL
  1238. MiIsSplitPage (
  1239. IN PVOID Virtual
  1240. )
  1241. {
  1242. PMMPTE AltPte;
  1243. MMPTE PteContents;
  1244. ULONG i;
  1245. Virtual = PAGE_ALIGN(Virtual);
  1246. AltPte = MiGetAltPteAddress(Virtual);
  1247. PteContents = *AltPte;
  1248. for (i = 0; i < SPLITS_PER_PAGE; i += 1) {
  1249. if ((AltPte->u.Long != 0) &&
  1250. ((AltPte->u.Alt.Commit == 0) ||
  1251. (AltPte->u.Alt.Accessed == 0) ||
  1252. (AltPte->u.Alt.CopyOnWrite != 0) ||
  1253. (AltPte->u.Alt.PteIndirect != 0) ||
  1254. (AltPte->u.Alt.FillZero != 0))) {
  1255. //
  1256. // If it is a NoAccess, FillZero or Guard page, CopyOnWrite,
  1257. // mark it as a split page.
  1258. //
  1259. return TRUE;
  1260. }
  1261. if (PteContents.u.Long != AltPte->u.Long) {
  1262. //
  1263. // If the next 4kb page is different from the 1st 4k page
  1264. // the page is split.
  1265. //
  1266. return TRUE;
  1267. }
  1268. AltPte += 1;
  1269. }
  1270. return FALSE;
  1271. }
  1272. LOGICAL
  1273. MiArePreceding4kPagesAllocated (
  1274. IN PVOID VirtualAddress
  1275. )
  1276. /*++
  1277. Routine Description:
  1278. This function checks to see if the specified virtual address contains any
  1279. preceding 4k allocations within the native page.
  1280. Arguments:
  1281. VirtualAddress - Supplies the virtual address to check.
  1282. Return Value:
  1283. TRUE if the address has preceding 4k pages, FALSE if not.
  1284. Environment:
  1285. Kernel mode, address creation mutex held, APCs disabled.
  1286. --*/
  1287. {
  1288. PMMPTE AltPte;
  1289. PMMPTE AltPteEnd;
  1290. ASSERT (BYTE_OFFSET (VirtualAddress) != 0);
  1291. AltPte = MiGetAltPteAddress (PAGE_ALIGN(VirtualAddress));
  1292. AltPteEnd = MiGetAltPteAddress (VirtualAddress);
  1293. //
  1294. // No need to hold the AltPte mutex as the address space mutex
  1295. // is held which prevents allocation or deletion of the AltPte entries
  1296. // inside the table.
  1297. //
  1298. while (AltPte != AltPteEnd) {
  1299. if ((AltPte->u.Long == 0) ||
  1300. ((AltPte->u.Alt.NoAccess == 1) && (AltPte->u.Alt.Protection != MM_NOACCESS))) {
  1301. //
  1302. // The page's alternate PTE hasn't been allocated yet to the process
  1303. // or it's marked no access.
  1304. //
  1305. NOTHING;
  1306. }
  1307. else {
  1308. return TRUE;
  1309. }
  1310. AltPte += 1;
  1311. }
  1312. return FALSE;
  1313. }
  1314. LOGICAL
  1315. MiAreFollowing4kPagesAllocated (
  1316. IN PVOID VirtualAddress
  1317. )
  1318. /*++
  1319. Routine Description:
  1320. This function checks to see if the specified virtual address contains any
  1321. following 4k allocations within the native page.
  1322. Arguments:
  1323. VirtualAddress - Supplies the virtual address to check.
  1324. Return Value:
  1325. TRUE if the address has following 4k pages, FALSE if not.
  1326. Environment:
  1327. Kernel mode, address creation mutex held, APCs disabled.
  1328. --*/
  1329. {
  1330. PMMPTE AltPte;
  1331. PMMPTE AltPteEnd;
  1332. ASSERT (BYTE_OFFSET (VirtualAddress) != 0);
  1333. AltPteEnd = MiGetAltPteAddress (PAGE_ALIGN ((ULONG_PTR)VirtualAddress + PAGE_SIZE));
  1334. AltPte = MiGetAltPteAddress (VirtualAddress) + 1;
  1335. ASSERT (AltPte < AltPteEnd);
  1336. //
  1337. // No need to hold the AltPte mutex as the address space mutex
  1338. // is held which prevents allocation or deletion of the AltPte entries
  1339. // inside the table.
  1340. //
  1341. while (AltPte != AltPteEnd) {
  1342. if ((AltPte->u.Long == 0) ||
  1343. ((AltPte->u.Alt.NoAccess == 1) && (AltPte->u.Alt.Protection != MM_NOACCESS))) {
  1344. //
  1345. // The page's alternate PTE hasn't been allocated yet to the process
  1346. // or it's marked no access.
  1347. //
  1348. NOTHING;
  1349. }
  1350. else {
  1351. return TRUE;
  1352. }
  1353. AltPte += 1;
  1354. }
  1355. return FALSE;
  1356. }
  1357. VOID
  1358. MiResetAccessBitForNativePtes (
  1359. IN PVOID StartVirtual,
  1360. IN PVOID EndVirtual,
  1361. IN PEPROCESS Process
  1362. )
  1363. /*++
  1364. Routine Description:
  1365. This function resets the access bit of the native PTEs if the bitmap
  1366. indicates it is a split page.
  1367. Arguments:
  1368. StartVirtual - Supplies the start address of the region of pages
  1369. to be inspected.
  1370. EndVirtual - Supplies the end address of the region of the pages
  1371. to be inspected.
  1372. Bitmap - Supplies the pointer to the process.
  1373. Return Value:
  1374. None.
  1375. Environment:
  1376. Alternate table mutex held at APC_LEVEL.
  1377. --*/
  1378. {
  1379. PMMPTE PointerPte;
  1380. PMMPTE PointerPde;
  1381. PMMPTE PointerPpe;
  1382. LOGICAL FirstTime;
  1383. ULONG Waited;
  1384. PWOW64_PROCESS Wow64Process;
  1385. Wow64Process = Process->Wow64Process;
  1386. StartVirtual = PAGE_ALIGN(StartVirtual);
  1387. PointerPte = MiGetPteAddress (StartVirtual);
  1388. FirstTime = TRUE;
  1389. LOCK_WS_UNSAFE (Process);
  1390. while (StartVirtual <= EndVirtual) {
  1391. if ((FirstTime == TRUE) || MiIsPteOnPdeBoundary (PointerPte)) {
  1392. PointerPde = MiGetPteAddress (PointerPte);
  1393. PointerPpe = MiGetPdeAddress (PointerPte);
  1394. if (MiDoesPpeExistAndMakeValid (PointerPpe,
  1395. Process,
  1396. FALSE,
  1397. &Waited) == FALSE) {
  1398. //
  1399. // This page directory parent entry is empty,
  1400. // go to the next one.
  1401. //
  1402. PointerPpe += 1;
  1403. PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
  1404. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  1405. StartVirtual = MiGetVirtualAddressMappedByPte (PointerPte);
  1406. continue;
  1407. }
  1408. if (MiDoesPdeExistAndMakeValid (PointerPde,
  1409. Process,
  1410. FALSE,
  1411. &Waited) == FALSE) {
  1412. //
  1413. // This page directory entry is empty,
  1414. // go to the next one.
  1415. //
  1416. PointerPde += 1;
  1417. PointerPte = MiGetVirtualAddressMappedByPte(PointerPde);
  1418. StartVirtual = MiGetVirtualAddressMappedByPte(PointerPte);
  1419. continue;
  1420. }
  1421. FirstTime = FALSE;
  1422. }
  1423. if ((MI_CHECK_BIT(Wow64Process->AltPermBitmap, MI_VA_TO_VPN(StartVirtual))) &&
  1424. ((PointerPte->u.Hard.Valid != 0) && (PointerPte->u.Hard.Accessed != 0))) {
  1425. PointerPte->u.Hard.Accessed = 0;
  1426. }
  1427. PointerPte += 1;
  1428. StartVirtual = (PVOID)((ULONG_PTR)StartVirtual + PAGE_SIZE);
  1429. }
  1430. UNLOCK_WS_UNSAFE (Process);
  1431. }
  1432. VOID
  1433. MiQueryRegionFor4kPage (
  1434. IN PVOID BaseAddress,
  1435. IN PVOID EndAddress,
  1436. IN OUT PSIZE_T RegionSize,
  1437. IN OUT PULONG RegionState,
  1438. IN OUT PULONG RegionProtect,
  1439. IN PEPROCESS Process
  1440. )
  1441. /*++
  1442. Routine Description:
  1443. This function checks the size of the region which has the same memory
  1444. state.
  1445. Arguments:
  1446. BaseAddress - Supplies the base address of the region of pages
  1447. to be queried.
  1448. EndAddress - Supplies the end of address of the region of pages
  1449. to be queried.
  1450. RegionSize - Supplies the original region size. Returns a region
  1451. size for 4k pages if different.
  1452. RegionState - Supplies the original region state. Returns a region
  1453. state for 4k pages if different.
  1454. RegionProtect - Supplies the original protection. Returns a protection
  1455. for 4k pages if different.
  1456. Process - Supplies a pointer to the process to be queried.
  1457. Return Value:
  1458. Returns the size of the region.
  1459. Environment:
  1460. Kernel mode. Address creation mutex held at APC_LEVEL.
  1461. --*/
  1462. {
  1463. PMMPTE AltPte;
  1464. MMPTE AltContents;
  1465. PVOID Va;
  1466. PWOW64_PROCESS Wow64Process;
  1467. //
  1468. // If above the Wow64 max address, just return.
  1469. //
  1470. if (((UINT_PTR) BaseAddress >= MM_MAX_WOW64_ADDRESS) ||
  1471. ((UINT_PTR) EndAddress >= MM_MAX_WOW64_ADDRESS)) {
  1472. return;
  1473. }
  1474. AltPte = MiGetAltPteAddress (BaseAddress);
  1475. Wow64Process = Process->Wow64Process;
  1476. LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  1477. if (MI_CHECK_BIT (Wow64Process->AltPermBitmap,
  1478. MI_VA_TO_VPN(BaseAddress)) == 0) {
  1479. UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  1480. return;
  1481. }
  1482. AltContents.u.Long = AltPte->u.Long;
  1483. if (AltContents.u.Long == 0) {
  1484. UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  1485. return;
  1486. }
  1487. *RegionProtect = MI_CONVERT_FROM_PTE_PROTECTION(AltContents.u.Alt.Protection);
  1488. if (AltContents.u.Alt.Commit != 0) {
  1489. *RegionState = MEM_COMMIT;
  1490. }
  1491. else {
  1492. if ((AltPte->u.Long == 0) ||
  1493. ((AltPte->u.Alt.NoAccess == 1) && (AltPte->u.Alt.Protection != MM_NOACCESS))) {
  1494. *RegionState = MEM_FREE;
  1495. *RegionProtect = PAGE_NOACCESS;
  1496. } else {
  1497. *RegionState = MEM_RESERVE;
  1498. *RegionProtect = 0;
  1499. }
  1500. }
  1501. Va = BaseAddress;
  1502. while ((ULONG_PTR)Va < (ULONG_PTR)EndAddress) {
  1503. Va = (PVOID)((ULONG_PTR)Va + PAGE_4K);
  1504. AltPte += 1;
  1505. if ((AltPte->u.Alt.Protection != AltContents.u.Alt.Protection) ||
  1506. (AltPte->u.Alt.Commit != AltContents.u.Alt.Commit)) {
  1507. //
  1508. // The state for this address does not match, calculate
  1509. // size and return.
  1510. //
  1511. break;
  1512. }
  1513. }
  1514. UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  1515. *RegionSize = (SIZE_T)((ULONG_PTR)Va - (ULONG_PTR)BaseAddress);
  1516. }
  1517. ULONG
  1518. MiQueryProtectionFor4kPage (
  1519. IN PVOID BaseAddress,
  1520. IN PEPROCESS Process
  1521. )
  1522. /*++
  1523. Routine Description:
  1524. This function queries the protection for a specified 4k page.
  1525. Arguments:
  1526. BaseAddress - Supplies a base address of the 4k page.
  1527. Process - Supplies a pointer to the relevant process.
  1528. Return Value:
  1529. Returns the protection of the 4k page.
  1530. Environment:
  1531. Kernel mode. Address creation mutex held at APC_LEVEL.
  1532. --*/
  1533. {
  1534. ULONG Protection;
  1535. PMMPTE PointerAltPte;
  1536. PWOW64_PROCESS Wow64Process;
  1537. Wow64Process = Process->Wow64Process;
  1538. PointerAltPte = MiGetAltPteAddress (BaseAddress);
  1539. Protection = 0;
  1540. LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  1541. if (MI_CHECK_BIT(Wow64Process->AltPermBitmap,
  1542. MI_VA_TO_VPN(BaseAddress)) != 0) {
  1543. Protection = (ULONG)PointerAltPte->u.Alt.Protection;
  1544. }
  1545. UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  1546. return Protection;
  1547. }
  1548. //
  1549. // Note 1 is added to the charge to account for the page table page.
  1550. //
  1551. #define MI_ALTERNATE_PAGE_TABLE_CHARGE ((((MM_MAX_WOW64_ADDRESS >> PAGE_4K_SHIFT) * sizeof (MMPTE)) >> PAGE_SHIFT) + 1)
  1552. NTSTATUS
  1553. MiInitializeAlternateTable (
  1554. IN PEPROCESS Process
  1555. )
  1556. /*++
  1557. Routine Description:
  1558. This function initializes the alternate table for the specified process.
  1559. Arguments:
  1560. Process - Supplies a pointer to the process to initialize the alternate
  1561. table for.
  1562. Return Value:
  1563. NTSTATUS.
  1564. Environment:
  1565. --*/
  1566. {
  1567. PULONG AltTablePointer;
  1568. PWOW64_PROCESS Wow64Process;
  1569. //
  1570. // Charge commitment now for the alternate PTE table pages as they will
  1571. // need to be dynamically created later at fault time.
  1572. //
  1573. if (MiChargeCommitment (MI_ALTERNATE_PAGE_TABLE_CHARGE, NULL) == FALSE) {
  1574. return STATUS_COMMITMENT_LIMIT;
  1575. }
  1576. AltTablePointer = (PULONG)ExAllocatePoolWithTag (NonPagedPool,
  1577. (MM_MAX_WOW64_ADDRESS >> PTI_SHIFT)/8,
  1578. 'AlmM');
  1579. if (AltTablePointer == NULL) {
  1580. MiReturnCommitment (MI_ALTERNATE_PAGE_TABLE_CHARGE);
  1581. return STATUS_NO_MEMORY;
  1582. }
  1583. RtlZeroMemory (AltTablePointer, (MM_MAX_WOW64_ADDRESS >> PTI_SHIFT)/8);
  1584. Wow64Process = Process->Wow64Process;
  1585. Wow64Process->AltPermBitmap = AltTablePointer;
  1586. ExInitializeFastMutex (&Wow64Process->AlternateTableLock);
  1587. return STATUS_SUCCESS;
  1588. }
  1589. VOID
  1590. MiDuplicateAlternateTable (
  1591. IN PEPROCESS CurrentProcess,
  1592. IN PEPROCESS ProcessToInitialize
  1593. )
  1594. /*++
  1595. Routine Description:
  1596. This function duplicates the alternate table bitmap and the alternate PTEs
  1597. themselves for the specified process.
  1598. Arguments:
  1599. Process - Supplies a pointer to the process whose alternate information
  1600. should be copied.
  1601. ProcessToInitialize - Supplies a pointer to the target process who should
  1602. receive the new alternate information.
  1603. Return Value:
  1604. None.
  1605. Environment:
  1606. Kernel mode, APCs disabled, working set and address space mutex
  1607. and ForkInProgress flag held.
  1608. --*/
  1609. {
  1610. PVOID Source;
  1611. KAPC_STATE ApcState;
  1612. PMMPTE PointerPte;
  1613. PMMPTE PointerAltPte;
  1614. PMMPTE PointerPde;
  1615. ULONG_PTR Va;
  1616. ULONG i;
  1617. ULONG j;
  1618. ULONG Waited;
  1619. KIRQL OldIrql;
  1620. //
  1621. // It's not necessary to acquire the alternate table mutex since both the
  1622. // address space and ForkInProgress resources are held on entry.
  1623. //
  1624. RtlCopyMemory (ProcessToInitialize->Wow64Process->AltPermBitmap,
  1625. CurrentProcess->Wow64Process->AltPermBitmap,
  1626. (MM_MAX_WOW64_ADDRESS >> PTI_SHIFT)/8);
  1627. //
  1628. // Since the PPE for the Alternate Table is shared with hyperspace,
  1629. // we can assume it is always present without performing
  1630. // MiDoesPpeExistAndMakeValid().
  1631. //
  1632. PointerPde = MiGetPdeAddress (ALT4KB_PERMISSION_TABLE_START);
  1633. PointerPte = MiGetPteAddress (ALT4KB_PERMISSION_TABLE_START);
  1634. Va = (ULONG_PTR)ALT4KB_PERMISSION_TABLE_START;
  1635. LOCK_PFN (OldIrql);
  1636. while (Va < (ULONG_PTR) ALT4KB_PERMISSION_TABLE_END) {
  1637. while (MiDoesPdeExistAndMakeValid (PointerPde,
  1638. CurrentProcess,
  1639. TRUE,
  1640. &Waited) == FALSE) {
  1641. //
  1642. // This page directory entry is empty, go to the next one.
  1643. //
  1644. PointerPde += 1;
  1645. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  1646. Va = (ULONG_PTR)MiGetVirtualAddressMappedByPte (PointerPte);
  1647. if (Va > (ULONG_PTR) ALT4KB_PERMISSION_TABLE_END) {
  1648. UNLOCK_PFN (OldIrql);
  1649. return;
  1650. }
  1651. }
  1652. //
  1653. // Duplicate any addresses that exist in the parent, bringing them
  1654. // in from disk or materializing them as necessary. Note the
  1655. // KSEG address is used for each parent address to avoid allocating
  1656. // a system PTE for this mapping as this routine cannot fail (the
  1657. // overall fork is too far along to tolerate a failure).
  1658. //
  1659. for (i = 0; i < PTE_PER_PAGE; i += 1) {
  1660. if (PointerPte->u.Long != 0) {
  1661. if (MiDoesPdeExistAndMakeValid (PointerPte,
  1662. CurrentProcess,
  1663. TRUE,
  1664. &Waited) == TRUE) {
  1665. UNLOCK_PFN (OldIrql);
  1666. ASSERT (PointerPte->u.Hard.Valid == 1);
  1667. Source = KSEG_ADDRESS (PointerPte->u.Hard.PageFrameNumber);
  1668. KeStackAttachProcess (&ProcessToInitialize->Pcb, &ApcState);
  1669. RtlCopyMemory ((PVOID)Va, Source, PAGE_SIZE);
  1670. //
  1671. // Eliminate any bits that should NOT be copied.
  1672. //
  1673. PointerAltPte = (PMMPTE) Va;
  1674. for (j = 0; j < PTE_PER_PAGE; j += 1) {
  1675. if (PointerAltPte->u.Alt.InPageInProgress == 1) {
  1676. PointerAltPte->u.Alt.InPageInProgress = 0;
  1677. }
  1678. PointerAltPte += 1;
  1679. }
  1680. KeUnstackDetachProcess (&ApcState);
  1681. LOCK_PFN (OldIrql);
  1682. }
  1683. }
  1684. Va += PAGE_SIZE;
  1685. PointerPte += 1;
  1686. }
  1687. PointerPde += 1;
  1688. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  1689. Va = (ULONG_PTR)MiGetVirtualAddressMappedByPte (PointerPte);
  1690. }
  1691. UNLOCK_PFN (OldIrql);
  1692. //
  1693. // Initialize the child's 32-bit PEB to be the same as the parent's.
  1694. //
  1695. ProcessToInitialize->Wow64Process->Wow64 = CurrentProcess->Wow64Process->Wow64;
  1696. return;
  1697. }
  1698. VOID
  1699. MiDeleteAlternateTable (
  1700. IN PEPROCESS Process
  1701. )
  1702. /*++
  1703. Routine Description:
  1704. This function deletes the alternate table for the specified process.
  1705. Arguments:
  1706. Process - Supplies a pointer to the process to delete the alternate
  1707. table for.
  1708. Return Value:
  1709. None.
  1710. Environment:
  1711. Kernel mode, APCs disabled, working set mutex held.
  1712. --*/
  1713. {
  1714. PMMPTE PointerPte;
  1715. PMMPTE PointerPde;
  1716. ULONG_PTR Va;
  1717. ULONG_PTR TempVa;
  1718. ULONG i;
  1719. ULONG Waited;
  1720. MMPTE_FLUSH_LIST PteFlushList;
  1721. PWOW64_PROCESS Wow64Process;
  1722. KIRQL OldIrql;
  1723. Wow64Process = Process->Wow64Process;
  1724. if (Wow64Process->AltPermBitmap == NULL) {
  1725. //
  1726. // This is only NULL (and Wow64Process non-NULL) if a memory allocation
  1727. // failed during process creation.
  1728. //
  1729. return;
  1730. }
  1731. //
  1732. // Since the PPE for the Alternate Table is shared with hyperspace,
  1733. // we can assume it is always present without performing
  1734. // MiDoesPpeExistAndMakeValid().
  1735. //
  1736. PointerPde = MiGetPdeAddress (ALT4KB_PERMISSION_TABLE_START);
  1737. PointerPte = MiGetPteAddress (ALT4KB_PERMISSION_TABLE_START);
  1738. Va = (ULONG_PTR)ALT4KB_PERMISSION_TABLE_START;
  1739. PteFlushList.Count = 0;
  1740. LOCK_PFN (OldIrql);
  1741. while (Va < (ULONG_PTR) ALT4KB_PERMISSION_TABLE_END) {
  1742. while (MiDoesPdeExistAndMakeValid (PointerPde,
  1743. Process,
  1744. TRUE,
  1745. &Waited) == FALSE) {
  1746. //
  1747. // This page directory entry is empty, go to the next one.
  1748. //
  1749. PointerPde += 1;
  1750. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  1751. Va = (ULONG_PTR)MiGetVirtualAddressMappedByPte (PointerPte);
  1752. if (Va > (ULONG_PTR) ALT4KB_PERMISSION_TABLE_END) {
  1753. goto delete_end;
  1754. }
  1755. }
  1756. //
  1757. // Delete the PTE entries mapping the Alternate Table.
  1758. //
  1759. TempVa = Va;
  1760. for (i = 0; i < PTE_PER_PAGE; i += 1) {
  1761. if (PointerPte->u.Long != 0) {
  1762. if (IS_PTE_NOT_DEMAND_ZERO (*PointerPte)) {
  1763. MiDeletePte (PointerPte,
  1764. (PVOID)TempVa,
  1765. TRUE,
  1766. Process,
  1767. NULL,
  1768. &PteFlushList);
  1769. } else {
  1770. *PointerPte = ZeroPte;
  1771. }
  1772. }
  1773. TempVa += PAGE_4K;
  1774. PointerPte += 1;
  1775. }
  1776. //
  1777. // Delete the PDE entry mapping the Alternate Table.
  1778. //
  1779. TempVa = (ULONG_PTR)MiGetVirtualAddressMappedByPte(PointerPde);
  1780. MiDeletePte (PointerPde,
  1781. (PVOID)TempVa,
  1782. TRUE,
  1783. Process,
  1784. NULL,
  1785. &PteFlushList);
  1786. MiFlushPteList (&PteFlushList, FALSE, ZeroPte);
  1787. PointerPde += 1;
  1788. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  1789. Va = (ULONG_PTR)MiGetVirtualAddressMappedByPte (PointerPte);
  1790. }
  1791. delete_end:
  1792. MiFlushPteList (&PteFlushList, FALSE, ZeroPte);
  1793. UNLOCK_PFN (OldIrql);
  1794. ExFreePool (Wow64Process->AltPermBitmap);
  1795. Wow64Process->AltPermBitmap = NULL;
  1796. MiReturnCommitment (MI_ALTERNATE_PAGE_TABLE_CHARGE);
  1797. return;
  1798. }
  1799. VOID
  1800. MiFillZeroFor4kPage (
  1801. IN PVOID VirtualAddress,
  1802. IN PEPROCESS Process
  1803. )
  1804. /*++
  1805. Routine Description:
  1806. This function zeroes the specified 4k page.
  1807. Arguments:
  1808. VirtualAddress - Supplies the base address of the 4k page.
  1809. Process - Supplies the relevant process.
  1810. Return Value:
  1811. None.
  1812. Environment:
  1813. Alternate PTE mutex held.
  1814. --*/
  1815. {
  1816. PVOID BaseAddress;
  1817. PMMPTE PointerAltPte;
  1818. PMMPTE PointerPde;
  1819. PMMPTE PointerPpe;
  1820. PMMPTE PointerPte;
  1821. MMPTE TempAltContents;
  1822. MMPTE PteContents;
  1823. ULONG Waited;
  1824. PWOW64_PROCESS Wow64Process;
  1825. Wow64Process = Process->Wow64Process;
  1826. PointerAltPte = MiGetAltPteAddress (VirtualAddress);
  1827. PointerPte = MiGetPteAddress(VirtualAddress);
  1828. PointerPde = MiGetPdeAddress(VirtualAddress);
  1829. PointerPpe = MiGetPpeAddress(VirtualAddress);
  1830. do {
  1831. if (PointerAltPte->u.Alt.FillZero == 0) {
  1832. //
  1833. // Another thread has already completed the zero operation.
  1834. //
  1835. return;
  1836. }
  1837. //
  1838. // Make the PPE and PDE valid as well as the
  1839. // page table for the original PTE. This guarantees
  1840. // TB forward progress for the TB indirect fault.
  1841. //
  1842. LOCK_WS_UNSAFE (Process);
  1843. if (MiDoesPpeExistAndMakeValid (PointerPpe,
  1844. Process,
  1845. FALSE,
  1846. &Waited) == FALSE) {
  1847. PteContents.u.Long = 0;
  1848. }
  1849. else if (MiDoesPdeExistAndMakeValid (PointerPde,
  1850. Process,
  1851. FALSE,
  1852. &Waited) == FALSE) {
  1853. PteContents.u.Long = 0;
  1854. }
  1855. else {
  1856. //
  1857. // Now it is safe to read PointerPte.
  1858. //
  1859. PteContents = *PointerPte;
  1860. }
  1861. //
  1862. // The alternate PTE may have been trimmed during the period prior to
  1863. // the working set mutex being acquired or when it was released prior
  1864. // to being reacquired.
  1865. //
  1866. if (MmIsAddressValid (PointerAltPte) == TRUE) {
  1867. break;
  1868. }
  1869. UNLOCK_WS_UNSAFE (Process);
  1870. } while (TRUE);
  1871. TempAltContents.u.Long = PointerAltPte->u.Long;
  1872. if (PteContents.u.Hard.Valid != 0) {
  1873. BaseAddress = KSEG_ADDRESS(PteContents.u.Hard.PageFrameNumber);
  1874. BaseAddress =
  1875. (PVOID)((ULONG_PTR)BaseAddress +
  1876. ((ULONG_PTR)PAGE_4K_ALIGN(VirtualAddress) & (PAGE_SIZE-1)));
  1877. RtlZeroMemory(BaseAddress, PAGE_4K);
  1878. UNLOCK_WS_UNSAFE (Process);
  1879. TempAltContents.u.Alt.FillZero = 0;
  1880. TempAltContents.u.Alt.Accessed = 1;
  1881. }
  1882. else {
  1883. UNLOCK_WS_UNSAFE (Process);
  1884. TempAltContents.u.Alt.Accessed = 0;
  1885. }
  1886. PointerAltPte->u.Long = TempAltContents.u.Long;
  1887. }
  1888. VOID
  1889. MiRemoveAliasedVadsApc (
  1890. IN PKAPC Apc,
  1891. OUT PKNORMAL_ROUTINE *NormalRoutine,
  1892. IN OUT PVOID NormalContext,
  1893. IN OUT PVOID *SystemArgument1,
  1894. IN OUT PVOID *SystemArgument2
  1895. )
  1896. {
  1897. ULONG i;
  1898. PALIAS_VAD_INFO2 AliasBase;
  1899. PEPROCESS Process;
  1900. PALIAS_VAD_INFO AliasInformation;
  1901. UNREFERENCED_PARAMETER (Apc);
  1902. UNREFERENCED_PARAMETER (NormalContext);
  1903. UNREFERENCED_PARAMETER (SystemArgument2);
  1904. Process = PsGetCurrentProcess ();
  1905. AliasInformation = (PALIAS_VAD_INFO) *SystemArgument1;
  1906. AliasBase = (PALIAS_VAD_INFO2)(AliasInformation + 1);
  1907. LOCK_ADDRESS_SPACE (Process);
  1908. for (i = 0; i < AliasInformation->NumberOfEntries; i += 1) {
  1909. ASSERT (AliasBase->BaseAddress < _2gb);
  1910. MiUnsecureVirtualMemory (AliasBase->SecureHandle, TRUE);
  1911. MiUnmapViewOfSection (Process,
  1912. (PVOID) (ULONG_PTR)AliasBase->BaseAddress,
  1913. TRUE);
  1914. AliasBase += 1;
  1915. }
  1916. UNLOCK_ADDRESS_SPACE (Process);
  1917. ExFreePool (AliasInformation);
  1918. //
  1919. // Clear the normal routine so this routine doesn't get called again
  1920. // for the same request.
  1921. //
  1922. *NormalRoutine = NULL;
  1923. }
  1924. VOID
  1925. MiRemoveAliasedVads (
  1926. IN PEPROCESS Process,
  1927. IN PMMVAD Vad
  1928. )
  1929. /*++
  1930. Routine Description:
  1931. This function removes all aliased VADs spawned earlier from the
  1932. argument VAD.
  1933. Arguments:
  1934. Process - Supplies the EPROCESS pointer to the current process.
  1935. Vad - Supplies a pointer to the VAD describing the range being removed.
  1936. Return Value:
  1937. None.
  1938. Environment:
  1939. Kernel mode, address creation and working set mutexes held, APCs disabled.
  1940. --*/
  1941. {
  1942. PALIAS_VAD_INFO AliasInformation;
  1943. ASSERT (Process->Wow64Process != NULL);
  1944. AliasInformation = ((PMMVAD_LONG)Vad)->AliasInformation;
  1945. ASSERT (AliasInformation != NULL);
  1946. if ((Process->Flags & PS_PROCESS_FLAGS_VM_DELETED) == 0) {
  1947. //
  1948. // This process is still alive so queue an APC to delete each aliased
  1949. // VAD. This is because the deletion must also get rid of page table
  1950. // commitment which requires that it search (and modify) VAD trees,
  1951. // etc - but the address space mutex is already held and the caller
  1952. // is not generally prepared for all this to change at this point.
  1953. //
  1954. KeInitializeApc (&AliasInformation->Apc,
  1955. &PsGetCurrentThread()->Tcb,
  1956. OriginalApcEnvironment,
  1957. (PKKERNEL_ROUTINE) MiRemoveAliasedVadsApc,
  1958. NULL,
  1959. (PKNORMAL_ROUTINE) MiRemoveAliasedVadsApc,
  1960. KernelMode,
  1961. (PVOID) AliasInformation);
  1962. KeInsertQueueApc (&AliasInformation->Apc, AliasInformation, NULL, 0);
  1963. }
  1964. else {
  1965. //
  1966. // This process is exiting so all the VADs are being rundown anyway.
  1967. // Just free the pool and let normal rundown handle the aliases.
  1968. //
  1969. ExFreePool (AliasInformation);
  1970. }
  1971. }
  1972. PVOID
  1973. MiDuplicateAliasVadList (
  1974. IN PMMVAD Vad
  1975. )
  1976. {
  1977. SIZE_T AliasInfoSize;
  1978. PALIAS_VAD_INFO AliasInfo;
  1979. PALIAS_VAD_INFO NewAliasInfo;
  1980. AliasInfo = ((PMMVAD_LONG)Vad)->AliasInformation;
  1981. ASSERT (AliasInfo != NULL);
  1982. AliasInfoSize = sizeof (ALIAS_VAD_INFO) + AliasInfo->MaximumEntries * sizeof (ALIAS_VAD_INFO2);
  1983. NewAliasInfo = ExAllocatePoolWithTag (NonPagedPool,
  1984. AliasInfoSize,
  1985. 'AdaV');
  1986. if (NewAliasInfo != NULL) {
  1987. RtlCopyMemory (NewAliasInfo, AliasInfo, AliasInfoSize);
  1988. }
  1989. return NewAliasInfo;
  1990. }
  1991. #define ALIAS_VAD_INCREMENT 4
  1992. NTSTATUS
  1993. MiSetCopyPagesFor4kPage (
  1994. IN PEPROCESS Process,
  1995. IN PMMVAD Vad,
  1996. IN OUT PVOID StartingAddress,
  1997. IN OUT PVOID EndingAddress,
  1998. IN ULONG NewProtection,
  1999. OUT PMMVAD *CallerNewVad
  2000. )
  2001. /*++
  2002. Routine Description:
  2003. This function creates another map for the existing mapped view space
  2004. and gives it copy-on-write protection. This is called when
  2005. SetProtectionOnSection() tries to change the protection from
  2006. non-copy-on-write to copy-on-write. Since a large native page cannot be
  2007. split to shared and copy-on-write 4kb pages, references to the
  2008. copy-on-write page(s) need to be fixed to reference the
  2009. new mapped view space and this is done through the smart TB handler
  2010. and the alternate page table entries.
  2011. Arguments:
  2012. Process - Supplies the EPROCESS pointer to the current process.
  2013. Vad - Supplies a pointer to the VAD describing the range to protect.
  2014. StartingAddress - Supplies a pointer to the starting address to protect.
  2015. EndingAddress - Supplies a pointer to the ending address to the protect.
  2016. NewProtect - Supplies the new protection to set.
  2017. CallerNewVad - Returns the new VAD the caller should use for this range.
  2018. Return Value:
  2019. NTSTATUS.
  2020. Environment:
  2021. Kernel mode, address creation mutex held, APCs disabled.
  2022. --*/
  2023. {
  2024. PALIAS_VAD_INFO2 AliasBase;
  2025. HANDLE Handle;
  2026. PMMVAD_LONG NewVad;
  2027. SIZE_T AliasInfoSize;
  2028. PALIAS_VAD_INFO AliasInfo;
  2029. PALIAS_VAD_INFO NewAliasInfo;
  2030. LARGE_INTEGER SectionOffset;
  2031. SIZE_T CapturedViewSize;
  2032. PVOID CapturedBase;
  2033. PVOID Va;
  2034. PVOID VaEnd;
  2035. PVOID Alias;
  2036. PMMPTE PointerPte;
  2037. PMMPTE AltPte;
  2038. MMPTE AltPteContents;
  2039. LOGICAL AliasReferenced;
  2040. SECTION Section;
  2041. PCONTROL_AREA ControlArea;
  2042. NTSTATUS status;
  2043. PWOW64_PROCESS Wow64Process;
  2044. ULONGLONG ProtectionMask;
  2045. ULONGLONG ProtectionMaskNotCopy;
  2046. ULONG NewProtectNotCopy;
  2047. AliasReferenced = FALSE;
  2048. StartingAddress = PAGE_ALIGN(StartingAddress);
  2049. EndingAddress = (PVOID)((ULONG_PTR)PAGE_ALIGN(EndingAddress) + PAGE_SIZE - 1);
  2050. SectionOffset.QuadPart = (ULONG_PTR)MI_64K_ALIGN((ULONG_PTR)StartingAddress -
  2051. (ULONG_PTR)(Vad->StartingVpn << PAGE_SHIFT));
  2052. CapturedBase = NULL;
  2053. Va = MI_VPN_TO_VA (Vad->StartingVpn);
  2054. VaEnd = MI_VPN_TO_VA_ENDING (Vad->EndingVpn);
  2055. CapturedViewSize = (ULONG_PTR)VaEnd - (ULONG_PTR)Va + 1L;
  2056. ControlArea = Vad->ControlArea;
  2057. RtlZeroMemory ((PVOID)&Section, sizeof(Section));
  2058. status = MiMapViewOfDataSection (ControlArea,
  2059. Process,
  2060. &CapturedBase,
  2061. &SectionOffset,
  2062. &CapturedViewSize,
  2063. &Section,
  2064. ViewShare,
  2065. (ULONG)Vad->u.VadFlags.Protection,
  2066. 0,
  2067. 0,
  2068. 0);
  2069. if (!NT_SUCCESS (status)) {
  2070. return status;
  2071. }
  2072. Handle = MiSecureVirtualMemory (CapturedBase,
  2073. CapturedViewSize,
  2074. PAGE_READONLY,
  2075. TRUE);
  2076. if (Handle == NULL) {
  2077. MiUnmapViewOfSection (Process, CapturedBase, FALSE);
  2078. return STATUS_INSUFFICIENT_RESOURCES;
  2079. }
  2080. //
  2081. // If the original VAD is a short or regular VAD, it needs to be
  2082. // reallocated as a large VAD. Note that a short VAD that was
  2083. // previously converted to a long VAD here will still be marked
  2084. // as private memory, thus to handle this case the NoChange bit
  2085. // must also be tested.
  2086. //
  2087. if (((Vad->u.VadFlags.PrivateMemory) && (Vad->u.VadFlags.NoChange == 0))
  2088. ||
  2089. (Vad->u2.VadFlags2.LongVad == 0)) {
  2090. if (Vad->u.VadFlags.PrivateMemory == 0) {
  2091. ASSERT (Vad->u2.VadFlags2.OneSecured == 0);
  2092. ASSERT (Vad->u2.VadFlags2.MultipleSecured == 0);
  2093. }
  2094. AliasInfoSize = sizeof (ALIAS_VAD_INFO) + ALIAS_VAD_INCREMENT * sizeof (ALIAS_VAD_INFO2);
  2095. AliasInfo = ExAllocatePoolWithTag (NonPagedPool,
  2096. AliasInfoSize,
  2097. 'AdaV');
  2098. if (AliasInfo == NULL) {
  2099. MiUnsecureVirtualMemory (Handle, TRUE);
  2100. MiUnmapViewOfSection (Process, CapturedBase, TRUE);
  2101. return STATUS_INSUFFICIENT_RESOURCES;
  2102. }
  2103. AliasInfo->NumberOfEntries = 0;
  2104. AliasInfo->MaximumEntries = ALIAS_VAD_INCREMENT;
  2105. NewVad = ExAllocatePoolWithTag (NonPagedPool,
  2106. sizeof(MMVAD_LONG),
  2107. 'ldaV');
  2108. if (NewVad == NULL) {
  2109. ExFreePool (AliasInfo);
  2110. MiUnsecureVirtualMemory (Handle, TRUE);
  2111. MiUnmapViewOfSection (Process, CapturedBase, TRUE);
  2112. return STATUS_INSUFFICIENT_RESOURCES;
  2113. }
  2114. RtlZeroMemory (NewVad, sizeof(MMVAD_LONG));
  2115. if (Vad->u.VadFlags.PrivateMemory) {
  2116. RtlCopyMemory (NewVad, Vad, sizeof(MMVAD_SHORT));
  2117. }
  2118. else {
  2119. RtlCopyMemory (NewVad, Vad, sizeof(MMVAD));
  2120. }
  2121. NewVad->u2.VadFlags2.LongVad = 1;
  2122. NewVad->AliasInformation = AliasInfo;
  2123. //
  2124. // Replace the current VAD with this expanded VAD.
  2125. //
  2126. LOCK_WS_UNSAFE (Process);
  2127. if (Vad->Parent) {
  2128. if (Vad->Parent->RightChild == Vad) {
  2129. Vad->Parent->RightChild = (PMMVAD) NewVad;
  2130. }
  2131. else {
  2132. ASSERT (Vad->Parent->LeftChild == Vad);
  2133. Vad->Parent->LeftChild = (PMMVAD) NewVad;
  2134. }
  2135. }
  2136. else {
  2137. Process->VadRoot = NewVad;
  2138. }
  2139. if (Vad->LeftChild) {
  2140. Vad->LeftChild->Parent = (PMMVAD) NewVad;
  2141. }
  2142. if (Vad->RightChild) {
  2143. Vad->RightChild->Parent = (PMMVAD) NewVad;
  2144. }
  2145. if (Process->VadHint == Vad) {
  2146. Process->VadHint = (PMMVAD) NewVad;
  2147. }
  2148. if (Process->VadFreeHint == Vad) {
  2149. Process->VadFreeHint = (PMMVAD) NewVad;
  2150. }
  2151. if ((Vad->u.VadFlags.PhysicalMapping == 1) ||
  2152. (Vad->u.VadFlags.WriteWatch == 1)) {
  2153. MiPhysicalViewAdjuster (Process, Vad, (PMMVAD) NewVad);
  2154. }
  2155. UNLOCK_WS_UNSAFE (Process);
  2156. ExFreePool (Vad);
  2157. Vad = (PMMVAD) NewVad;
  2158. }
  2159. else {
  2160. AliasInfo = (PALIAS_VAD_INFO) ((PMMVAD_LONG)Vad)->AliasInformation;
  2161. if (AliasInfo == NULL) {
  2162. AliasInfoSize = sizeof (ALIAS_VAD_INFO) + ALIAS_VAD_INCREMENT * sizeof (ALIAS_VAD_INFO2);
  2163. }
  2164. else if (AliasInfo->NumberOfEntries >= AliasInfo->MaximumEntries) {
  2165. AliasInfoSize = sizeof (ALIAS_VAD_INFO) + (AliasInfo->MaximumEntries + ALIAS_VAD_INCREMENT) * sizeof (ALIAS_VAD_INFO2);
  2166. }
  2167. else {
  2168. AliasInfoSize = 0;
  2169. }
  2170. if (AliasInfoSize != 0) {
  2171. NewAliasInfo = ExAllocatePoolWithTag (NonPagedPool,
  2172. AliasInfoSize,
  2173. 'AdaV');
  2174. if (NewAliasInfo == NULL) {
  2175. MiUnsecureVirtualMemory (Handle, TRUE);
  2176. MiUnmapViewOfSection (Process, CapturedBase, TRUE);
  2177. return STATUS_INSUFFICIENT_RESOURCES;
  2178. }
  2179. if (AliasInfo != NULL) {
  2180. RtlCopyMemory (NewAliasInfo, AliasInfo, AliasInfoSize - ALIAS_VAD_INCREMENT * sizeof (ALIAS_VAD_INFO2));
  2181. NewAliasInfo->MaximumEntries += ALIAS_VAD_INCREMENT;
  2182. ExFreePool (AliasInfo);
  2183. }
  2184. else {
  2185. NewAliasInfo->NumberOfEntries = 0;
  2186. NewAliasInfo->MaximumEntries = ALIAS_VAD_INCREMENT;
  2187. }
  2188. AliasInfo = NewAliasInfo;
  2189. }
  2190. }
  2191. *CallerNewVad = Vad;
  2192. Va = StartingAddress;
  2193. VaEnd = EndingAddress;
  2194. Alias = (PVOID)((ULONG_PTR)CapturedBase + ((ULONG_PTR)StartingAddress & (X64K - 1)));
  2195. ProtectionMask = MiMakeProtectionAteMask (NewProtection);
  2196. NewProtectNotCopy = NewProtection & ~MM_PROTECTION_COPY_MASK;
  2197. ProtectionMaskNotCopy = MiMakeProtectionAteMask (NewProtectNotCopy);
  2198. Wow64Process = Process->Wow64Process;
  2199. AltPte = MiGetAltPteAddress (Va);
  2200. LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  2201. while (Va <= VaEnd) {
  2202. PointerPte = MiGetPteAddress (Alias);
  2203. AltPteContents.u.Long = AltPte->u.Long;
  2204. //
  2205. // If this address is NOT copy-on-write, AND it is not already
  2206. // redirected through an indirect entry, then redirect it now to
  2207. // the alias VAD which points at the original section.
  2208. //
  2209. if ((AltPteContents.u.Alt.CopyOnWrite == 0) &&
  2210. (AltPteContents.u.Alt.PteIndirect == 0)) {
  2211. AltPteContents.u.Alt.PteOffset = (ULONG_PTR)PointerPte - PTE_UBASE;
  2212. AltPteContents.u.Alt.PteIndirect = 1;
  2213. AltPte->u.Long = AltPteContents.u.Long;
  2214. AliasReferenced = TRUE;
  2215. }
  2216. Va = (PVOID)((ULONG_PTR)Va + PAGE_4K);
  2217. Alias = (PVOID)((ULONG_PTR)Alias + PAGE_4K);
  2218. AltPte += 1;
  2219. }
  2220. UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  2221. ASSERT (AliasInfo->NumberOfEntries < AliasInfo->MaximumEntries);
  2222. if (AliasReferenced == TRUE) {
  2223. //
  2224. // The alias view of the shared section was referenced so chain it so
  2225. // the alias view can be :
  2226. //
  2227. // a) easily duplicated if the process subsequently forks.
  2228. //
  2229. // AND
  2230. //
  2231. // b) deleted when/if the original VAD is deleted later.
  2232. //
  2233. AliasBase = (PALIAS_VAD_INFO2)(AliasInfo + 1);
  2234. AliasBase += AliasInfo->NumberOfEntries;
  2235. ASSERT (CapturedBase < (PVOID)(ULONG_PTR)_2gb);
  2236. AliasBase->BaseAddress = (ULONG)(ULONG_PTR)CapturedBase;
  2237. AliasBase->SecureHandle = Handle;
  2238. AliasInfo->NumberOfEntries += 1;
  2239. }
  2240. else {
  2241. //
  2242. // The alias view of the shared section wasn't referenced, delete it.
  2243. //
  2244. MiUnsecureVirtualMemory (Handle, TRUE);
  2245. MiUnmapViewOfSection (Process, CapturedBase, TRUE);
  2246. }
  2247. PS_SET_BITS (&Process->Flags, PS_PROCESS_FLAGS_WOW64_SPLIT_PAGES);
  2248. return STATUS_SUCCESS;
  2249. }
  2250. VOID
  2251. MiLockFor4kPage (
  2252. IN PVOID CapturedBase,
  2253. IN SIZE_T CapturedRegionSize,
  2254. IN PEPROCESS Process
  2255. )
  2256. /*++
  2257. Routine Description:
  2258. This function adds the page locked attribute to the alternate table entries.
  2259. Arguments:
  2260. CapturedBase - Supplies the base address to be locked.
  2261. CapturedRegionSize - Supplies the size of the region to be locked.
  2262. Process - Supplies a pointer to the process object.
  2263. Return Value:
  2264. None.
  2265. Environment:
  2266. Kernel mode, address creation mutex held.
  2267. --*/
  2268. {
  2269. PWOW64_PROCESS Wow64Process;
  2270. PVOID EndingAddress;
  2271. PMMPTE StartAltPte;
  2272. PMMPTE EndAltPte;
  2273. Wow64Process = Process->Wow64Process;
  2274. EndingAddress = (PVOID)((ULONG_PTR)CapturedBase + CapturedRegionSize - 1);
  2275. StartAltPte = MiGetAltPteAddress(CapturedBase);
  2276. EndAltPte = MiGetAltPteAddress(EndingAddress);
  2277. LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  2278. while (StartAltPte <= EndAltPte) {
  2279. StartAltPte->u.Alt.Lock = 1;
  2280. StartAltPte += 1;
  2281. }
  2282. UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  2283. }
  2284. NTSTATUS
  2285. MiUnlockFor4kPage (
  2286. IN PVOID CapturedBase,
  2287. IN SIZE_T CapturedRegionSize,
  2288. IN PEPROCESS Process
  2289. )
  2290. /*++
  2291. Routine Description:
  2292. This function removes the page locked attribute from the
  2293. alternate table entries.
  2294. Arguments:
  2295. CapturedBase - Supplies the base address to be unlocked.
  2296. CapturedRegionSize - Supplies the size of the region to be unlocked.
  2297. Process - Supplies a pointer to the process object.
  2298. Return Value:
  2299. NTSTATUS.
  2300. Environment:
  2301. Kernel mode, address creation and working set mutexes held.
  2302. Note this routine releases and reacquires the working set mutex !!!
  2303. --*/
  2304. {
  2305. PMMPTE StartAltPte;
  2306. PMMPTE EndAltPte;
  2307. PWOW64_PROCESS Wow64Process;
  2308. PVOID EndingAddress;
  2309. NTSTATUS Status;
  2310. UNLOCK_WS_UNSAFE (Process);
  2311. Status = STATUS_SUCCESS;
  2312. Wow64Process = Process->Wow64Process;
  2313. EndingAddress = (PVOID)((ULONG_PTR)CapturedBase + CapturedRegionSize - 1);
  2314. StartAltPte = MiGetAltPteAddress (CapturedBase);
  2315. EndAltPte = MiGetAltPteAddress (EndingAddress);
  2316. LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  2317. while (StartAltPte <= EndAltPte) {
  2318. if (StartAltPte->u.Alt.Lock == 0) {
  2319. Status = STATUS_NOT_LOCKED;
  2320. goto StatusReturn;
  2321. }
  2322. StartAltPte += 1;
  2323. }
  2324. StartAltPte = MiGetAltPteAddress (CapturedBase);
  2325. while (StartAltPte <= EndAltPte) {
  2326. StartAltPte->u.Alt.Lock = 0;
  2327. StartAltPte += 1;
  2328. }
  2329. StatusReturn:
  2330. UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  2331. LOCK_WS_UNSAFE (Process);
  2332. return Status;
  2333. }
  2334. LOGICAL
  2335. MiShouldBeUnlockedFor4kPage (
  2336. IN PVOID VirtualAddress,
  2337. IN PEPROCESS Process
  2338. )
  2339. /*++
  2340. Routine Description:
  2341. This function examines whether the pape should be unlocked.
  2342. Arguments:
  2343. VirtualAddress - Supplies the virtual address to be examined.
  2344. Process - Supplies a pointer to the process object.
  2345. Return Value:
  2346. None.
  2347. Environment:
  2348. Kernel mode, address creation and working set mutexes held.
  2349. Note this routine releases and reacquires the working set mutex !!!
  2350. --*/
  2351. {
  2352. PMMPTE StartAltPte;
  2353. PMMPTE EndAltPte;
  2354. PWOW64_PROCESS Wow64Process;
  2355. PVOID VirtualAligned;
  2356. PVOID EndingAddress;
  2357. LOGICAL PageUnlocked;
  2358. UNLOCK_WS_UNSAFE (Process);
  2359. PageUnlocked = TRUE;
  2360. Wow64Process = Process->Wow64Process;
  2361. VirtualAligned = PAGE_ALIGN(VirtualAddress);
  2362. EndingAddress = (PVOID)((ULONG_PTR)VirtualAligned + PAGE_SIZE - 1);
  2363. StartAltPte = MiGetAltPteAddress (VirtualAligned);
  2364. EndAltPte = MiGetAltPteAddress (EndingAddress);
  2365. LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  2366. while (StartAltPte <= EndAltPte) {
  2367. if (StartAltPte->u.Alt.Lock != 0) {
  2368. PageUnlocked = FALSE;
  2369. }
  2370. StartAltPte += 1;
  2371. }
  2372. UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  2373. LOCK_WS_UNSAFE (Process);
  2374. return PageUnlocked;
  2375. }
  2376. VOID
  2377. MiCopyOnWriteFor4kPage (
  2378. IN PVOID VirtualAddress
  2379. )
  2380. /*++
  2381. Routine Description:
  2382. This function changes the protection of the alternate pages for a copy on
  2383. write native page.
  2384. Arguments:
  2385. VirtualAddress - Supplies the virtual address which caused the
  2386. copy-on-write fault.
  2387. Return Value:
  2388. None.
  2389. Environment:
  2390. Kernel mode, alternate table mutex held.
  2391. --*/
  2392. {
  2393. PMMPTE PointerAltPte;
  2394. MMPTE TempAltPte;
  2395. ULONG i;
  2396. PointerAltPte = MiGetAltPteAddress (PAGE_ALIGN(VirtualAddress));
  2397. for (i = 0; i < SPLITS_PER_PAGE; i += 1) {
  2398. TempAltPte.u.Long = PointerAltPte->u.Long;
  2399. if ((TempAltPte.u.Alt.Commit != 0) &&
  2400. (TempAltPte.u.Alt.CopyOnWrite != 0)) {
  2401. TempAltPte.u.Alt.CopyOnWrite = 0;
  2402. TempAltPte.u.Alt.Private = 1;
  2403. TempAltPte.u.Hard.Write = 1;
  2404. TempAltPte.u.Alt.Protection =
  2405. MI_MAKE_PROTECT_NOT_WRITE_COPY(PointerAltPte->u.Alt.Protection);
  2406. PointerAltPte->u.Long = TempAltPte.u.Long;
  2407. }
  2408. //
  2409. // Atomically update the PTE.
  2410. //
  2411. PointerAltPte += 1;
  2412. }
  2413. }
  2414. ULONG
  2415. MiMakeProtectForNativePage (
  2416. IN PVOID VirtualAddress,
  2417. IN ULONG NewProtect,
  2418. IN PEPROCESS Process
  2419. )
  2420. /*++
  2421. Routine Description:
  2422. This function makes a page protection mask for native pages.
  2423. Arguments:
  2424. VirtualAddress - Supplies the virtual address for the protection mask.
  2425. NewProtect - Supplies the original protection.
  2426. Process - Supplies a pointer to the process object.
  2427. Return Value:
  2428. None.
  2429. Environment:
  2430. Kernel mode.
  2431. --*/
  2432. {
  2433. PWOW64_PROCESS Wow64Process;
  2434. Wow64Process = Process->Wow64Process;
  2435. if (MI_CHECK_BIT(Wow64Process->AltPermBitmap,
  2436. MI_VA_TO_VPN(VirtualAddress)) != 0) {
  2437. if (NewProtect & PAGE_NOACCESS) {
  2438. NewProtect &= ~PAGE_NOACCESS;
  2439. NewProtect |= PAGE_EXECUTE_READWRITE;
  2440. }
  2441. if (NewProtect & PAGE_READONLY) {
  2442. NewProtect &= ~PAGE_READONLY;
  2443. NewProtect |= PAGE_EXECUTE_READWRITE;
  2444. }
  2445. if (NewProtect & PAGE_EXECUTE) {
  2446. NewProtect &= ~PAGE_EXECUTE;
  2447. NewProtect |= PAGE_EXECUTE_READWRITE;
  2448. }
  2449. if (NewProtect & PAGE_EXECUTE_READ) {
  2450. NewProtect &= ~PAGE_EXECUTE_READ;
  2451. NewProtect |= PAGE_EXECUTE_READWRITE;
  2452. }
  2453. //
  2454. // Remove PAGE_GUARD as it is emulated by the Alternate Table.
  2455. //
  2456. if (NewProtect & PAGE_GUARD) {
  2457. NewProtect &= ~PAGE_GUARD;
  2458. }
  2459. }
  2460. return NewProtect;
  2461. }
  2462. VOID
  2463. MiCheckVirtualAddressFor4kPage (
  2464. IN PVOID VirtualAddress,
  2465. IN PEPROCESS Process
  2466. )
  2467. /*++
  2468. Routine Description:
  2469. This function checks the virtual address to see if it is a 4K page.
  2470. Arguments:
  2471. VirtualAddress - Supplies the virtual address to check.
  2472. Process - Supplies a pointer to the process object.
  2473. Return Value:
  2474. None.
  2475. Environment:
  2476. Kernel mode, alternate table mutex held.
  2477. --*/
  2478. {
  2479. PMMPTE ProtoPte;
  2480. PMMPTE PointerAltPte;
  2481. PMMPTE PointerPde;
  2482. MMPTE AltPteContents;
  2483. ULONG OriginalProtection;
  2484. ULONGLONG ProtectionMaskOriginal;
  2485. PWOW64_PROCESS Wow64Process;
  2486. KIRQL OldIrql;
  2487. PMMPFN Pfn1;
  2488. ULONG i;
  2489. ULONG_PTR StartVpn;
  2490. Wow64Process = Process->Wow64Process;
  2491. LOCK_WS_UNSAFE (Process);
  2492. ProtoPte = MiCheckVirtualAddress (VirtualAddress, &OriginalProtection);
  2493. if (OriginalProtection == MM_UNKNOWN_PROTECTION) {
  2494. if (!MI_IS_PHYSICAL_ADDRESS(ProtoPte)) {
  2495. PointerPde = MiGetPteAddress (ProtoPte);
  2496. LOCK_PFN (OldIrql);
  2497. if (PointerPde->u.Hard.Valid == 0) {
  2498. MiMakeSystemAddressValidPfn (ProtoPte);
  2499. }
  2500. Pfn1 = MI_PFN_ELEMENT (PointerPde->u.Hard.PageFrameNumber);
  2501. MI_ADD_LOCKED_PAGE_CHARGE(Pfn1, 32);
  2502. Pfn1->u3.e2.ReferenceCount += 1;
  2503. ASSERT (Pfn1->u3.e2.ReferenceCount > 1);
  2504. UNLOCK_PFN (OldIrql);
  2505. }
  2506. else {
  2507. Pfn1 = NULL;
  2508. }
  2509. OriginalProtection =
  2510. MiMakeProtectionMask(MiGetPageProtection(ProtoPte, Process, FALSE));
  2511. //
  2512. // Unlock the page containing the prototype PTEs.
  2513. //
  2514. if (Pfn1 != NULL) {
  2515. ASSERT (!MI_IS_PHYSICAL_ADDRESS(ProtoPte));
  2516. LOCK_PFN (OldIrql);
  2517. ASSERT (Pfn1->u3.e2.ReferenceCount > 1);
  2518. MI_REMOVE_LOCKED_PAGE_CHARGE(Pfn1, 33);
  2519. Pfn1->u3.e2.ReferenceCount -= 1;
  2520. UNLOCK_PFN (OldIrql);
  2521. }
  2522. if (OriginalProtection == MM_INVALID_PROTECTION) {
  2523. UNLOCK_WS_UNSAFE (Process);
  2524. return;
  2525. }
  2526. }
  2527. UNLOCK_WS_UNSAFE (Process);
  2528. ProtectionMaskOriginal = MiMakeProtectionAteMask (OriginalProtection);
  2529. ProtectionMaskOriginal |= MM_ATE_COMMIT;
  2530. PointerAltPte = MiGetAltPteAddress (PAGE_ALIGN(VirtualAddress));
  2531. AltPteContents.u.Long = ProtectionMaskOriginal;
  2532. AltPteContents.u.Alt.Protection = OriginalProtection;
  2533. for (i = 0; i < SPLITS_PER_PAGE; i += 1) {
  2534. //
  2535. // Update the alternate PTEs.
  2536. //
  2537. PointerAltPte->u.Long = AltPteContents.u.Long;
  2538. PointerAltPte += 1;
  2539. }
  2540. //
  2541. // Update the bitmap.
  2542. //
  2543. StartVpn = MI_VA_TO_VPN (VirtualAddress);
  2544. MI_SET_BIT (Wow64Process->AltPermBitmap, StartVpn);
  2545. }
  2546. LOGICAL
  2547. MiIsNativeGuardPage (
  2548. IN PVOID VirtualAddress
  2549. )
  2550. /*++
  2551. Routine Description:
  2552. This function checks to see if any of the AltPtes has the Guard bit set.
  2553. Arguments:
  2554. VirtualAddress - Supplies the virtual address to check.
  2555. Return Value:
  2556. None.
  2557. Environment:
  2558. Kernel mode, alternate table mutex held.
  2559. --*/
  2560. {
  2561. ULONG i;
  2562. PMMPTE PointerAltPte;
  2563. PointerAltPte = MiGetAltPteAddress (PAGE_ALIGN(VirtualAddress));
  2564. for (i = 0; i < SPLITS_PER_PAGE; i += 1) {
  2565. if ((PointerAltPte->u.Alt.Protection & MM_GUARD_PAGE) != 0) {
  2566. return TRUE;
  2567. }
  2568. PointerAltPte += 1;
  2569. }
  2570. return FALSE;
  2571. }
  2572. VOID
  2573. MiSetNativePteProtection (
  2574. PVOID VirtualAddress,
  2575. ULONGLONG NewPteProtection,
  2576. LOGICAL PageIsSplit,
  2577. PEPROCESS CurrentProcess
  2578. )
  2579. {
  2580. MMPTE PteContents;
  2581. MMPTE TempPte;
  2582. PMMPTE PointerPte;
  2583. PMMPTE PointerPde;
  2584. PMMPTE PointerPpe;
  2585. ULONG Waited;
  2586. PointerPte = MiGetPteAddress (VirtualAddress);
  2587. PointerPde = MiGetPdeAddress (VirtualAddress);
  2588. PointerPpe = MiGetPpeAddress (VirtualAddress);
  2589. //
  2590. // Block APCs and acquire the working set lock.
  2591. //
  2592. LOCK_WS (CurrentProcess);
  2593. //
  2594. // Make the PPE and PDE exist and valid.
  2595. //
  2596. if (MiDoesPpeExistAndMakeValid (PointerPpe,
  2597. CurrentProcess,
  2598. FALSE,
  2599. &Waited) == FALSE) {
  2600. UNLOCK_WS (CurrentProcess);
  2601. return;
  2602. }
  2603. if (MiDoesPdeExistAndMakeValid (PointerPde,
  2604. CurrentProcess,
  2605. FALSE,
  2606. &Waited) == FALSE) {
  2607. UNLOCK_WS (CurrentProcess);
  2608. return;
  2609. }
  2610. //
  2611. // Now it is safe to read PointerPte.
  2612. //
  2613. PteContents = *PointerPte;
  2614. //
  2615. // Check to see if the protection for the native page should be set
  2616. // and if the access bit of the PTE should be set.
  2617. //
  2618. if (PteContents.u.Hard.Valid != 0) {
  2619. TempPte = PteContents;
  2620. //
  2621. // Perform PTE protection mask corrections.
  2622. //
  2623. TempPte.u.Long |= NewPteProtection;
  2624. if (PteContents.u.Hard.Accessed == 0) {
  2625. TempPte.u.Hard.Accessed = 1;
  2626. if (PageIsSplit == TRUE) {
  2627. TempPte.u.Hard.Cache = MM_PTE_CACHE_RESERVED;
  2628. }
  2629. }
  2630. MI_WRITE_VALID_PTE_NEW_PROTECTION(PointerPte, TempPte);
  2631. }
  2632. UNLOCK_WS (CurrentProcess);
  2633. }
  2634. #endif