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.

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