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

1015 lines
30 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. wrtwatch.c
  5. Abstract:
  6. This module contains the routines to support write watch.
  7. Author:
  8. Landy Wang (landyw) 28-Jul-1999
  9. Revision History:
  10. --*/
  11. #include "mi.h"
  12. #define COPY_STACK_SIZE 256
  13. NTSTATUS
  14. NtGetWriteWatch (
  15. IN HANDLE ProcessHandle,
  16. IN ULONG Flags,
  17. IN PVOID BaseAddress,
  18. IN SIZE_T RegionSize,
  19. IN OUT PVOID *UserAddressArray,
  20. IN OUT PULONG_PTR EntriesInUserAddressArray,
  21. OUT PULONG Granularity
  22. )
  23. /*++
  24. Routine Description:
  25. This function returns the write watch status of the argument region.
  26. UserAddressArray is filled with the base address of each page that has
  27. been written to since the last NtResetWriteWatch call (or if no
  28. NtResetWriteWatch calls have been made, then each page written since
  29. this address space was created).
  30. Arguments:
  31. ProcessHandle - Supplies an open handle to a process object.
  32. Flags - Supplies WRITE_WATCH_FLAG_RESET or nothing.
  33. BaseAddress - An address within a region of pages to be queried. This
  34. value must lie within a private memory region with the
  35. write-watch attribute already set.
  36. RegionSize - The size of the region in bytes beginning at the base address
  37. specified.
  38. UserAddressArray - Supplies a pointer to user memory to store the user
  39. addresses modified since the last reset.
  40. UserAddressArrayEntries - Supplies a pointer to how many user addresses
  41. can be returned in this call. This is then filled
  42. with the exact number of addresses actually
  43. returned.
  44. Granularity - Supplies a pointer to a variable to receive the size of
  45. modified granule in bytes.
  46. Return Value:
  47. Various NTSTATUS codes.
  48. --*/
  49. {
  50. PMMPFN Pfn1;
  51. LOGICAL First;
  52. LOGICAL UserWritten;
  53. PVOID EndAddress;
  54. PMMVAD Vad;
  55. KIRQL OldIrql;
  56. PEPROCESS Process;
  57. PMMPTE NextPte;
  58. PMMPTE PointerPte;
  59. PMMPTE PointerPde;
  60. PMMPTE PointerPpe;
  61. PMMPTE PointerPxe;
  62. PMMPTE EndPte;
  63. NTSTATUS Status;
  64. PVOID PoolArea;
  65. PVOID *PoolAreaPointer;
  66. ULONG_PTR StackArray[COPY_STACK_SIZE];
  67. MMPTE PteContents;
  68. ULONG_PTR NumberOfBytes;
  69. PRTL_BITMAP BitMap;
  70. ULONG BitMapIndex;
  71. ULONG NextBitMapIndex;
  72. PMI_PHYSICAL_VIEW PhysicalView;
  73. ULONG_PTR PagesWritten;
  74. ULONG_PTR NumberOfPages;
  75. LOGICAL Attached;
  76. KPROCESSOR_MODE PreviousMode;
  77. PFN_NUMBER PageFrameIndex;
  78. ULONG WorkingSetIndex;
  79. MMPTE TempPte;
  80. MMPTE PreviousPte;
  81. KAPC_STATE ApcState;
  82. PETHREAD CurrentThread;
  83. PEPROCESS CurrentProcess;
  84. MMPTE_FLUSH_LIST PteFlushList;
  85. TABLE_SEARCH_RESULT SearchResult;
  86. PteFlushList.Count = 0;
  87. ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
  88. if ((Flags & ~WRITE_WATCH_FLAG_RESET) != 0) {
  89. return STATUS_INVALID_PARAMETER_2;
  90. }
  91. CurrentThread = PsGetCurrentThread ();
  92. CurrentProcess = PsGetCurrentProcessByThread(CurrentThread);
  93. PreviousMode = KeGetPreviousModeByThread(&CurrentThread->Tcb);
  94. //
  95. // Establish an exception handler, probe the specified addresses
  96. // for write access and capture the initial values.
  97. //
  98. try {
  99. if (PreviousMode != KernelMode) {
  100. //
  101. // Make sure the specified starting and ending addresses are
  102. // within the user part of the virtual address space.
  103. //
  104. if (BaseAddress > MM_HIGHEST_VAD_ADDRESS) {
  105. return STATUS_INVALID_PARAMETER_2;
  106. }
  107. if ((((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1) - (ULONG_PTR)BaseAddress) <
  108. RegionSize) {
  109. return STATUS_INVALID_PARAMETER_3;
  110. }
  111. //
  112. // Capture the number of pages.
  113. //
  114. ProbeForWritePointer (EntriesInUserAddressArray);
  115. NumberOfPages = *EntriesInUserAddressArray;
  116. if (NumberOfPages == 0) {
  117. return STATUS_INVALID_PARAMETER_5;
  118. }
  119. if (NumberOfPages > (MAXULONG_PTR / sizeof(ULONG_PTR))) {
  120. return STATUS_INVALID_PARAMETER_5;
  121. }
  122. ProbeForWrite (UserAddressArray,
  123. NumberOfPages * sizeof (PVOID),
  124. sizeof(PVOID));
  125. ProbeForWriteUlong (Granularity);
  126. }
  127. else {
  128. NumberOfPages = *EntriesInUserAddressArray;
  129. ASSERT (NumberOfPages != 0);
  130. }
  131. } except (ExSystemExceptionFilter()) {
  132. //
  133. // If an exception occurs during the probe or capture
  134. // of the initial values, then handle the exception and
  135. // return the exception code as the status value.
  136. //
  137. return GetExceptionCode();
  138. }
  139. //
  140. // Carefully probe and capture the user virtual address array.
  141. //
  142. PoolArea = (PVOID)&StackArray[0];
  143. NumberOfBytes = NumberOfPages * sizeof(ULONG_PTR);
  144. if (NumberOfPages > COPY_STACK_SIZE) {
  145. PoolArea = ExAllocatePoolWithTag (NonPagedPool,
  146. NumberOfBytes,
  147. 'cGmM');
  148. if (PoolArea == NULL) {
  149. return STATUS_INSUFFICIENT_RESOURCES;
  150. }
  151. }
  152. PoolAreaPointer = (PVOID *)PoolArea;
  153. Attached = FALSE;
  154. //
  155. // Reference the specified process handle for VM_OPERATION access.
  156. //
  157. if (ProcessHandle == NtCurrentProcess()) {
  158. Process = CurrentProcess;
  159. }
  160. else {
  161. Status = ObReferenceObjectByHandle (ProcessHandle,
  162. PROCESS_VM_OPERATION,
  163. PsProcessType,
  164. PreviousMode,
  165. (PVOID *)&Process,
  166. NULL);
  167. if (!NT_SUCCESS(Status)) {
  168. goto ErrorReturn0;
  169. }
  170. }
  171. EndAddress = (PVOID)((PCHAR)BaseAddress + RegionSize - 1);
  172. PagesWritten = 0;
  173. if (BaseAddress > EndAddress) {
  174. Status = STATUS_INVALID_PARAMETER_4;
  175. goto ErrorReturn;
  176. }
  177. //
  178. // If the specified process is not the current process, attach
  179. // to the specified process.
  180. //
  181. if (CurrentProcess != Process) {
  182. KeStackAttachProcess (&Process->Pcb, &ApcState);
  183. Attached = TRUE;
  184. }
  185. Vad = NULL;
  186. SATISFY_OVERZEALOUS_COMPILER (PhysicalView = NULL);
  187. First = TRUE;
  188. PointerPte = MiGetPteAddress (BaseAddress);
  189. EndPte = MiGetPteAddress (EndAddress);
  190. PointerPde = MiGetPdeAddress (BaseAddress);
  191. PointerPpe = MiGetPpeAddress (BaseAddress);
  192. PointerPxe = MiGetPxeAddress (BaseAddress);
  193. LOCK_WS (Process);
  194. LOCK_PFN (OldIrql);
  195. if (Process->PhysicalVadRoot == NULL) {
  196. UNLOCK_PFN (OldIrql);
  197. UNLOCK_WS (Process);
  198. Status = STATUS_INVALID_PARAMETER_1;
  199. goto ErrorReturn;
  200. }
  201. //
  202. // Lookup the element and save the result.
  203. //
  204. SearchResult = MiFindNodeOrParent (Process->PhysicalVadRoot,
  205. MI_VA_TO_VPN (BaseAddress),
  206. (PMMADDRESS_NODE *) &PhysicalView);
  207. if ((SearchResult == TableFoundNode) &&
  208. (PhysicalView->Vad->u.VadFlags.WriteWatch == 1) &&
  209. (BaseAddress >= MI_VPN_TO_VA (PhysicalView->StartingVpn)) &&
  210. (EndAddress <= MI_VPN_TO_VA_ENDING (PhysicalView->EndingVpn))) {
  211. Vad = PhysicalView->Vad;
  212. }
  213. else {
  214. //
  215. // No virtual address is marked for write-watch at the specified base
  216. // address, return an error.
  217. //
  218. UNLOCK_PFN (OldIrql);
  219. UNLOCK_WS (Process);
  220. Status = STATUS_INVALID_PARAMETER_1;
  221. goto ErrorReturn;
  222. }
  223. ASSERT (Process->Flags & PS_PROCESS_FLAGS_USING_WRITE_WATCH);
  224. //
  225. // Extract the write watch status for each page in the range.
  226. // Note the PFN lock must be held to ensure atomicity.
  227. //
  228. BitMap = PhysicalView->u.BitMap;
  229. BaseAddress = MiGetVirtualAddressMappedByPte (PointerPte);
  230. BitMapIndex = (ULONG)(((PCHAR)BaseAddress - (PCHAR)(Vad->StartingVpn << PAGE_SHIFT)) >> PAGE_SHIFT);
  231. ASSERT (BitMapIndex < BitMap->SizeOfBitMap);
  232. ASSERT (BitMapIndex + (EndPte - PointerPte) < BitMap->SizeOfBitMap);
  233. while (PointerPte <= EndPte) {
  234. ASSERT (BitMapIndex < BitMap->SizeOfBitMap);
  235. UserWritten = FALSE;
  236. //
  237. // If the PTE is marked dirty (or writable) OR the BitMap says it's
  238. // dirtied, then let the caller know.
  239. //
  240. if (RtlCheckBit (BitMap, BitMapIndex) == 1) {
  241. UserWritten = TRUE;
  242. //
  243. // Note that a chunk of bits cannot be cleared at once because
  244. // the user array may overflow at any time. If the user specifies
  245. // a bad address and the results cannot be written out, then it's
  246. // his own fault that he won't know which bits were cleared !
  247. //
  248. if (Flags & WRITE_WATCH_FLAG_RESET) {
  249. RtlClearBit (BitMap, BitMapIndex);
  250. goto ClearPteIfValid;
  251. }
  252. }
  253. else {
  254. ClearPteIfValid:
  255. //
  256. // If the page table page is not present, then the dirty bit
  257. // has already been captured to the write watch bitmap.
  258. // Unfortunately all the entries in the page cannot be skipped
  259. // as the write watch bitmap must be checked for each PTE.
  260. //
  261. #if (_MI_PAGING_LEVELS >= 4)
  262. if (PointerPxe->u.Hard.Valid == 0) {
  263. //
  264. // Skip the entire extended page parent if the bitmap permits.
  265. // The search starts at BitMapIndex (not BitMapIndex + 1) to
  266. // avoid wraps.
  267. //
  268. NextBitMapIndex = RtlFindSetBits (BitMap, 1, BitMapIndex);
  269. PointerPxe += 1;
  270. PointerPpe = MiGetVirtualAddressMappedByPte (PointerPxe);
  271. PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
  272. NextPte = MiGetVirtualAddressMappedByPte (PointerPde);
  273. //
  274. // Compare the bitmap jump with the PTE jump and take
  275. // the lesser of the two.
  276. //
  277. if ((NextBitMapIndex == NO_BITS_FOUND) ||
  278. ((ULONG)(NextPte - PointerPte) < (NextBitMapIndex - BitMapIndex))) {
  279. BitMapIndex += (ULONG)(NextPte - PointerPte);
  280. PointerPte = NextPte;
  281. }
  282. else {
  283. PointerPte += (NextBitMapIndex - BitMapIndex);
  284. BitMapIndex = NextBitMapIndex;
  285. }
  286. PointerPde = MiGetPteAddress (PointerPte);
  287. PointerPpe = MiGetPdeAddress (PointerPte);
  288. PointerPxe = MiGetPpeAddress (PointerPte);
  289. BaseAddress = MiGetVirtualAddressMappedByPte (PointerPte);
  290. continue;
  291. }
  292. #endif
  293. #if (_MI_PAGING_LEVELS >= 3)
  294. if (PointerPpe->u.Hard.Valid == 0) {
  295. //
  296. // Skip the entire page parent if the bitmap permits.
  297. // The search starts at BitMapIndex (not BitMapIndex + 1) to
  298. // avoid wraps.
  299. //
  300. NextBitMapIndex = RtlFindSetBits (BitMap, 1, BitMapIndex);
  301. PointerPpe += 1;
  302. PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
  303. NextPte = MiGetVirtualAddressMappedByPte (PointerPde);
  304. //
  305. // Compare the bitmap jump with the PTE jump and take
  306. // the lesser of the two.
  307. //
  308. if ((NextBitMapIndex == NO_BITS_FOUND) ||
  309. ((ULONG)(NextPte - PointerPte) < (NextBitMapIndex - BitMapIndex))) {
  310. BitMapIndex += (ULONG)(NextPte - PointerPte);
  311. PointerPte = NextPte;
  312. }
  313. else {
  314. PointerPte += (NextBitMapIndex - BitMapIndex);
  315. BitMapIndex = NextBitMapIndex;
  316. }
  317. PointerPde = MiGetPteAddress (PointerPte);
  318. PointerPpe = MiGetPdeAddress (PointerPte);
  319. PointerPxe = MiGetPpeAddress (PointerPte);
  320. BaseAddress = MiGetVirtualAddressMappedByPte (PointerPte);
  321. continue;
  322. }
  323. #endif
  324. if (PointerPde->u.Hard.Valid == 0) {
  325. //
  326. // Skip the entire page directory if the bitmap permits.
  327. // The search starts at BitMapIndex (not BitMapIndex + 1) to
  328. // avoid wraps.
  329. //
  330. NextBitMapIndex = RtlFindSetBits (BitMap, 1, BitMapIndex);
  331. PointerPde += 1;
  332. NextPte = MiGetVirtualAddressMappedByPte (PointerPde);
  333. //
  334. // Compare the bitmap jump with the PTE jump and take
  335. // the lesser of the two.
  336. //
  337. if ((NextBitMapIndex == NO_BITS_FOUND) ||
  338. ((ULONG)(NextPte - PointerPte) < (NextBitMapIndex - BitMapIndex))) {
  339. BitMapIndex += (ULONG)(NextPte - PointerPte);
  340. PointerPte = NextPte;
  341. }
  342. else {
  343. PointerPte += (NextBitMapIndex - BitMapIndex);
  344. BitMapIndex = NextBitMapIndex;
  345. }
  346. PointerPde = MiGetPteAddress (PointerPte);
  347. PointerPpe = MiGetPdeAddress (PointerPte);
  348. PointerPxe = MiGetPpeAddress (PointerPte);
  349. BaseAddress = MiGetVirtualAddressMappedByPte (PointerPte);
  350. continue;
  351. }
  352. PteContents = *PointerPte;
  353. if ((PteContents.u.Hard.Valid == 1) &&
  354. (MI_IS_PTE_DIRTY(PteContents))) {
  355. ASSERT (MI_PFN_ELEMENT(MI_GET_PAGE_FRAME_FROM_PTE(&PteContents))->u3.e1.PrototypePte == 0);
  356. UserWritten = TRUE;
  357. if (Flags & WRITE_WATCH_FLAG_RESET) {
  358. //
  359. // For the uniprocessor x86, just the dirty bit is
  360. // cleared. For all other platforms, the PTE writable
  361. // bit must be disabled now so future writes trigger
  362. // write watch updates.
  363. //
  364. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (&PteContents);
  365. Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
  366. ASSERT (Pfn1->u3.e1.PrototypePte == 0);
  367. MI_MAKE_VALID_PTE (TempPte,
  368. PageFrameIndex,
  369. Pfn1->OriginalPte.u.Soft.Protection,
  370. PointerPte);
  371. #if defined(_MIALT4K_)
  372. //
  373. // Preserve the split protections if they exist.
  374. //
  375. TempPte.u.Hard.Cache = PteContents.u.Hard.Cache;
  376. #endif
  377. WorkingSetIndex = MI_GET_WORKING_SET_FROM_PTE (&PteContents);
  378. MI_SET_PTE_IN_WORKING_SET (&TempPte, WorkingSetIndex);
  379. //
  380. // Flush the TB as the protection of a valid PTE is
  381. // being changed.
  382. //
  383. PreviousPte = *PointerPte;
  384. ASSERT (PreviousPte.u.Hard.Valid == 1);
  385. MI_WRITE_VALID_PTE_NEW_PROTECTION (PointerPte, TempPte);
  386. if (PteFlushList.Count < MM_MAXIMUM_FLUSH_COUNT) {
  387. PteFlushList.FlushVa[PteFlushList.Count] = BaseAddress;
  388. PteFlushList.Count += 1;
  389. }
  390. ASSERT (PreviousPte.u.Hard.Valid == 1);
  391. //
  392. // A page's protection is being changed, on certain
  393. // hardware the dirty bit should be ORed into the
  394. // modify bit in the PFN element.
  395. //
  396. MI_CAPTURE_DIRTY_BIT_TO_PFN (&PreviousPte, Pfn1);
  397. }
  398. }
  399. }
  400. if (UserWritten == TRUE) {
  401. *PoolAreaPointer = BaseAddress;
  402. PoolAreaPointer += 1;
  403. PagesWritten += 1;
  404. if (PagesWritten == NumberOfPages) {
  405. //
  406. // User array isn't big enough to take any more. The API
  407. // (inherited from Win9x) is defined to return at this point.
  408. //
  409. break;
  410. }
  411. }
  412. PointerPte += 1;
  413. if (MiIsPteOnPdeBoundary(PointerPte)) {
  414. PointerPde = MiGetPteAddress (PointerPte);
  415. if (MiIsPteOnPdeBoundary(PointerPde)) {
  416. PointerPpe = MiGetPdeAddress (PointerPte);
  417. #if (_MI_PAGING_LEVELS >= 4)
  418. if (MiIsPteOnPdeBoundary(PointerPpe)) {
  419. PointerPxe = MiGetPpeAddress (PointerPte);
  420. }
  421. #endif
  422. }
  423. }
  424. BitMapIndex += 1;
  425. BaseAddress = (PVOID)((PCHAR)BaseAddress + PAGE_SIZE);
  426. }
  427. if (PteFlushList.Count != 0) {
  428. MiFlushPteList (&PteFlushList, FALSE);
  429. }
  430. UNLOCK_PFN (OldIrql);
  431. UNLOCK_WS (Process);
  432. Status = STATUS_SUCCESS;
  433. ErrorReturn:
  434. if (Attached == TRUE) {
  435. KeUnstackDetachProcess (&ApcState);
  436. Attached = FALSE;
  437. }
  438. if (ProcessHandle != NtCurrentProcess()) {
  439. ObDereferenceObject (Process);
  440. }
  441. if (Status == STATUS_SUCCESS) {
  442. //
  443. // Return all results to the caller.
  444. //
  445. try {
  446. RtlCopyMemory (UserAddressArray,
  447. PoolArea,
  448. PagesWritten * sizeof (PVOID));
  449. *EntriesInUserAddressArray = PagesWritten;
  450. *Granularity = PAGE_SIZE;
  451. } except (ExSystemExceptionFilter()) {
  452. Status = GetExceptionCode();
  453. }
  454. }
  455. ErrorReturn0:
  456. if (PoolArea != (PVOID)&StackArray[0]) {
  457. ExFreePool (PoolArea);
  458. }
  459. return Status;
  460. }
  461. NTSTATUS
  462. NtResetWriteWatch (
  463. IN HANDLE ProcessHandle,
  464. IN PVOID BaseAddress,
  465. IN SIZE_T RegionSize
  466. )
  467. /*++
  468. Routine Description:
  469. This function clears the write watch status of the argument region.
  470. This allows callers to "forget" old writes and only see new ones from
  471. this point on.
  472. Arguments:
  473. ProcessHandle - Supplies an open handle to a process object.
  474. BaseAddress - An address within a region of pages to be reset. This
  475. value must lie within a private memory region with the
  476. write-watch attribute already set.
  477. RegionSize - The size of the region in bytes beginning at the base address
  478. specified.
  479. Return Value:
  480. Various NTSTATUS codes.
  481. --*/
  482. {
  483. PVOID EndAddress;
  484. PMMVAD Vad;
  485. PMMPFN Pfn1;
  486. KIRQL OldIrql;
  487. PEPROCESS Process;
  488. PMMPTE PointerPte;
  489. PMMPTE PointerPde;
  490. PMMPTE PointerPpe;
  491. PMMPTE PointerPxe;
  492. PMMPTE EndPte;
  493. NTSTATUS Status;
  494. MMPTE PreviousPte;
  495. MMPTE PteContents;
  496. MMPTE TempPte;
  497. PRTL_BITMAP BitMap;
  498. ULONG BitMapIndex;
  499. PMI_PHYSICAL_VIEW PhysicalView;
  500. LOGICAL First;
  501. LOGICAL Attached;
  502. KPROCESSOR_MODE PreviousMode;
  503. PFN_NUMBER PageFrameIndex;
  504. ULONG WorkingSetIndex;
  505. KAPC_STATE ApcState;
  506. PETHREAD CurrentThread;
  507. PEPROCESS CurrentProcess;
  508. MMPTE_FLUSH_LIST PteFlushList;
  509. TABLE_SEARCH_RESULT SearchResult;
  510. PteFlushList.Count = 0;
  511. ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
  512. if (BaseAddress > MM_HIGHEST_VAD_ADDRESS) {
  513. return STATUS_INVALID_PARAMETER_2;
  514. }
  515. if ((((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1) - (ULONG_PTR)BaseAddress) <
  516. RegionSize) {
  517. return STATUS_INVALID_PARAMETER_3;
  518. }
  519. //
  520. // Reference the specified process handle for VM_OPERATION access.
  521. //
  522. CurrentThread = PsGetCurrentThread ();
  523. CurrentProcess = PsGetCurrentProcessByThread(CurrentThread);
  524. if (ProcessHandle == NtCurrentProcess()) {
  525. Process = CurrentProcess;
  526. }
  527. else {
  528. PreviousMode = KeGetPreviousModeByThread(&CurrentThread->Tcb);
  529. Status = ObReferenceObjectByHandle (ProcessHandle,
  530. PROCESS_VM_OPERATION,
  531. PsProcessType,
  532. PreviousMode,
  533. (PVOID *)&Process,
  534. NULL);
  535. if (!NT_SUCCESS(Status)) {
  536. return Status;
  537. }
  538. }
  539. Attached = FALSE;
  540. EndAddress = (PVOID)((PCHAR)BaseAddress + RegionSize - 1);
  541. if (BaseAddress > EndAddress) {
  542. Status = STATUS_INVALID_PARAMETER_3;
  543. goto ErrorReturn;
  544. }
  545. //
  546. // If the specified process is not the current process, attach
  547. // to the specified process.
  548. //
  549. if (CurrentProcess != Process) {
  550. KeStackAttachProcess (&Process->Pcb, &ApcState);
  551. Attached = TRUE;
  552. }
  553. Vad = NULL;
  554. First = TRUE;
  555. SATISFY_OVERZEALOUS_COMPILER (PhysicalView = NULL);
  556. PointerPte = MiGetPteAddress (BaseAddress);
  557. EndPte = MiGetPteAddress (EndAddress);
  558. LOCK_WS (Process);
  559. LOCK_PFN (OldIrql);
  560. if (Process->PhysicalVadRoot == NULL) {
  561. UNLOCK_PFN (OldIrql);
  562. UNLOCK_WS (Process);
  563. Status = STATUS_INVALID_PARAMETER_1;
  564. goto ErrorReturn;
  565. }
  566. //
  567. // Lookup the element and save the result.
  568. //
  569. SearchResult = MiFindNodeOrParent (Process->PhysicalVadRoot,
  570. MI_VA_TO_VPN (BaseAddress),
  571. (PMMADDRESS_NODE *) &PhysicalView);
  572. if ((SearchResult == TableFoundNode) &&
  573. (PhysicalView->Vad->u.VadFlags.WriteWatch == 1) &&
  574. (BaseAddress >= MI_VPN_TO_VA (PhysicalView->StartingVpn)) &&
  575. (EndAddress <= MI_VPN_TO_VA_ENDING (PhysicalView->EndingVpn))) {
  576. Vad = PhysicalView->Vad;
  577. }
  578. else {
  579. //
  580. // No virtual address is marked for write-watch at the specified base
  581. // address, return an error.
  582. //
  583. UNLOCK_PFN (OldIrql);
  584. UNLOCK_WS (Process);
  585. Status = STATUS_INVALID_PARAMETER_1;
  586. goto ErrorReturn;
  587. }
  588. ASSERT (Process->Flags & PS_PROCESS_FLAGS_USING_WRITE_WATCH);
  589. //
  590. // Clear the write watch status (and PTE writable/dirty bits) for each page
  591. // in the range. Note if the PTE is not currently valid, then the write
  592. // watch bit has already been captured to the bitmap. Hence only valid PTEs
  593. // need adjusting.
  594. //
  595. // The PFN lock must be held to ensure atomicity.
  596. //
  597. BitMap = PhysicalView->u.BitMap;
  598. BaseAddress = MiGetVirtualAddressMappedByPte (PointerPte);
  599. BitMapIndex = (ULONG)(((PCHAR)BaseAddress - (PCHAR)(Vad->StartingVpn << PAGE_SHIFT)) >> PAGE_SHIFT);
  600. ASSERT (BitMapIndex < BitMap->SizeOfBitMap);
  601. ASSERT (BitMapIndex + (EndPte - PointerPte) < BitMap->SizeOfBitMap);
  602. RtlClearBits (BitMap, BitMapIndex, (ULONG)(EndPte - PointerPte + 1));
  603. while (PointerPte <= EndPte) {
  604. //
  605. // If the page table page is not present, then the dirty bit
  606. // has already been captured to the write watch bitmap. So skip it.
  607. //
  608. if ((First == TRUE) || MiIsPteOnPdeBoundary(PointerPte)) {
  609. First = FALSE;
  610. PointerPpe = MiGetPpeAddress (BaseAddress);
  611. PointerPxe = MiGetPxeAddress (BaseAddress);
  612. #if (_MI_PAGING_LEVELS >= 4)
  613. if (PointerPxe->u.Hard.Valid == 0) {
  614. PointerPxe += 1;
  615. PointerPpe = MiGetVirtualAddressMappedByPte (PointerPxe);
  616. PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
  617. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  618. BaseAddress = MiGetVirtualAddressMappedByPte (PointerPte);
  619. continue;
  620. }
  621. #endif
  622. #if (_MI_PAGING_LEVELS >= 3)
  623. if (PointerPpe->u.Hard.Valid == 0) {
  624. PointerPpe += 1;
  625. PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
  626. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  627. BaseAddress = MiGetVirtualAddressMappedByPte (PointerPte);
  628. continue;
  629. }
  630. #endif
  631. PointerPde = MiGetPdeAddress (BaseAddress);
  632. if (PointerPde->u.Hard.Valid == 0) {
  633. PointerPde += 1;
  634. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  635. BaseAddress = MiGetVirtualAddressMappedByPte (PointerPte);
  636. continue;
  637. }
  638. }
  639. //
  640. // If the PTE is marked dirty (or writable) OR the BitMap says it's
  641. // dirtied, then let the caller know.
  642. //
  643. PteContents = *PointerPte;
  644. if ((PteContents.u.Hard.Valid == 1) &&
  645. (MI_IS_PTE_DIRTY(PteContents))) {
  646. //
  647. // For the uniprocessor x86, just the dirty bit is cleared.
  648. // For all other platforms, the PTE writable bit must be
  649. // disabled now so future writes trigger write watch updates.
  650. //
  651. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (&PteContents);
  652. Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
  653. ASSERT (Pfn1->u3.e1.PrototypePte == 0);
  654. MI_MAKE_VALID_PTE (TempPte,
  655. PageFrameIndex,
  656. Pfn1->OriginalPte.u.Soft.Protection,
  657. PointerPte);
  658. #if defined(_MIALT4K_)
  659. //
  660. // Preserve the split protections if they exist.
  661. //
  662. TempPte.u.Hard.Cache = PteContents.u.Hard.Cache;
  663. #endif
  664. WorkingSetIndex = MI_GET_WORKING_SET_FROM_PTE (&PteContents);
  665. MI_SET_PTE_IN_WORKING_SET (&TempPte, WorkingSetIndex);
  666. //
  667. // Flush the TB as the protection of a valid PTE is being changed.
  668. //
  669. PreviousPte = *PointerPte;
  670. ASSERT (PreviousPte.u.Hard.Valid == 1);
  671. MI_WRITE_VALID_PTE_NEW_PROTECTION (PointerPte, TempPte);
  672. if (PteFlushList.Count < MM_MAXIMUM_FLUSH_COUNT) {
  673. PteFlushList.FlushVa[PteFlushList.Count] = BaseAddress;
  674. PteFlushList.Count += 1;
  675. }
  676. ASSERT (PreviousPte.u.Hard.Valid == 1);
  677. //
  678. // A page's protection is being changed, on certain
  679. // hardware the dirty bit should be ORed into the
  680. // modify bit in the PFN element.
  681. //
  682. MI_CAPTURE_DIRTY_BIT_TO_PFN (&PreviousPte, Pfn1);
  683. }
  684. PointerPte += 1;
  685. BaseAddress = (PVOID)((PCHAR)BaseAddress + PAGE_SIZE);
  686. }
  687. if (PteFlushList.Count != 0) {
  688. MiFlushPteList (&PteFlushList, FALSE);
  689. }
  690. UNLOCK_PFN (OldIrql);
  691. UNLOCK_WS (Process);
  692. Status = STATUS_SUCCESS;
  693. ErrorReturn:
  694. if (Attached == TRUE) {
  695. KeUnstackDetachProcess (&ApcState);
  696. Attached = FALSE;
  697. }
  698. if (ProcessHandle != NtCurrentProcess()) {
  699. ObDereferenceObject (Process);
  700. }
  701. return Status;
  702. }
  703. VOID
  704. MiCaptureWriteWatchDirtyBit (
  705. IN PEPROCESS Process,
  706. IN PVOID VirtualAddress
  707. )
  708. /*++
  709. Routine Description:
  710. This routine sets the write watch bit corresponding to the argument
  711. virtual address.
  712. Arguments:
  713. Process - Supplies a pointer to an executive process structure.
  714. VirtualAddress - Supplies the modified virtual address.
  715. Return Value:
  716. None.
  717. Environment:
  718. Kernel mode, working set mutex and PFN lock held.
  719. --*/
  720. {
  721. PMMVAD Vad;
  722. PMI_PHYSICAL_VIEW PhysicalView;
  723. PRTL_BITMAP BitMap;
  724. ULONG BitMapIndex;
  725. TABLE_SEARCH_RESULT SearchResult;
  726. MM_PFN_LOCK_ASSERT();
  727. ASSERT (Process->Flags & PS_PROCESS_FLAGS_USING_WRITE_WATCH);
  728. //
  729. // This process has (or had) write watch VADs. Search now
  730. // for a write watch region encapsulating the PTE being
  731. // invalidated.
  732. //
  733. ASSERT (Process->PhysicalVadRoot != NULL);
  734. SearchResult = MiFindNodeOrParent (Process->PhysicalVadRoot,
  735. MI_VA_TO_VPN (VirtualAddress),
  736. (PMMADDRESS_NODE *) &PhysicalView);
  737. if ((SearchResult == TableFoundNode) &&
  738. (PhysicalView->Vad->u.VadFlags.WriteWatch == 1) &&
  739. (VirtualAddress >= MI_VPN_TO_VA (PhysicalView->StartingVpn)) &&
  740. (VirtualAddress <= MI_VPN_TO_VA_ENDING (PhysicalView->EndingVpn))) {
  741. //
  742. // The write watch bitmap must be updated.
  743. //
  744. Vad = PhysicalView->Vad;
  745. BitMap = PhysicalView->u.BitMap;
  746. BitMapIndex = (ULONG)(((PCHAR)VirtualAddress - (PCHAR)(Vad->StartingVpn << PAGE_SHIFT)) >> PAGE_SHIFT);
  747. ASSERT (BitMapIndex < BitMap->SizeOfBitMap);
  748. MI_SET_BIT (BitMap->Buffer, BitMapIndex);
  749. }
  750. return;
  751. }