1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/mm/i386/page.c
5 * PURPOSE: Low level memory management manipulation
6 *
7 * PROGRAMMERS: David Welch (welch@cwcom.net)
8 */
9
10 /* INCLUDES ***************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 #include <mm/ARM3/miarm.h>
17
18 #ifndef _MI_PAGING_LEVELS
19 #error "Dude, fix your stuff before using this file"
20 #endif
21
22 /* GLOBALS *****************************************************************/
23 const
24 ULONG_PTR
25 MmProtectToPteMask[32] =
26 {
27 //
28 // These are the base MM_ protection flags
29 //
30 0,
31 PTE_READONLY | PTE_ENABLE_CACHE,
32 PTE_EXECUTE | PTE_ENABLE_CACHE,
33 PTE_EXECUTE_READ | PTE_ENABLE_CACHE,
34 PTE_READWRITE | PTE_ENABLE_CACHE,
35 PTE_WRITECOPY | PTE_ENABLE_CACHE,
36 PTE_EXECUTE_READWRITE | PTE_ENABLE_CACHE,
37 PTE_EXECUTE_WRITECOPY | PTE_ENABLE_CACHE,
38 //
39 // These OR in the MM_NOCACHE flag
40 //
41 0,
42 PTE_READONLY | PTE_DISABLE_CACHE,
43 PTE_EXECUTE | PTE_DISABLE_CACHE,
44 PTE_EXECUTE_READ | PTE_DISABLE_CACHE,
45 PTE_READWRITE | PTE_DISABLE_CACHE,
46 PTE_WRITECOPY | PTE_DISABLE_CACHE,
47 PTE_EXECUTE_READWRITE | PTE_DISABLE_CACHE,
48 PTE_EXECUTE_WRITECOPY | PTE_DISABLE_CACHE,
49 //
50 // These OR in the MM_DECOMMIT flag, which doesn't seem supported on x86/64/ARM
51 //
52 0,
53 PTE_READONLY | PTE_ENABLE_CACHE,
54 PTE_EXECUTE | PTE_ENABLE_CACHE,
55 PTE_EXECUTE_READ | PTE_ENABLE_CACHE,
56 PTE_READWRITE | PTE_ENABLE_CACHE,
57 PTE_WRITECOPY | PTE_ENABLE_CACHE,
58 PTE_EXECUTE_READWRITE | PTE_ENABLE_CACHE,
59 PTE_EXECUTE_WRITECOPY | PTE_ENABLE_CACHE,
60 //
61 // These OR in the MM_NOACCESS flag, which seems to enable WriteCombining?
62 //
63 0,
64 PTE_READONLY | PTE_WRITECOMBINED_CACHE,
65 PTE_EXECUTE | PTE_WRITECOMBINED_CACHE,
66 PTE_EXECUTE_READ | PTE_WRITECOMBINED_CACHE,
67 PTE_READWRITE | PTE_WRITECOMBINED_CACHE,
68 PTE_WRITECOPY | PTE_WRITECOMBINED_CACHE,
69 PTE_EXECUTE_READWRITE | PTE_WRITECOMBINED_CACHE,
70 PTE_EXECUTE_WRITECOPY | PTE_WRITECOMBINED_CACHE,
71 };
72
73 const
74 ULONG MmProtectToValue[32] =
75 {
76 PAGE_NOACCESS,
77 PAGE_READONLY,
78 PAGE_EXECUTE,
79 PAGE_EXECUTE_READ,
80 PAGE_READWRITE,
81 PAGE_WRITECOPY,
82 PAGE_EXECUTE_READWRITE,
83 PAGE_EXECUTE_WRITECOPY,
84 PAGE_NOACCESS,
85 PAGE_NOCACHE | PAGE_READONLY,
86 PAGE_NOCACHE | PAGE_EXECUTE,
87 PAGE_NOCACHE | PAGE_EXECUTE_READ,
88 PAGE_NOCACHE | PAGE_READWRITE,
89 PAGE_NOCACHE | PAGE_WRITECOPY,
90 PAGE_NOCACHE | PAGE_EXECUTE_READWRITE,
91 PAGE_NOCACHE | PAGE_EXECUTE_WRITECOPY,
92 PAGE_NOACCESS,
93 PAGE_GUARD | PAGE_READONLY,
94 PAGE_GUARD | PAGE_EXECUTE,
95 PAGE_GUARD | PAGE_EXECUTE_READ,
96 PAGE_GUARD | PAGE_READWRITE,
97 PAGE_GUARD | PAGE_WRITECOPY,
98 PAGE_GUARD | PAGE_EXECUTE_READWRITE,
99 PAGE_GUARD | PAGE_EXECUTE_WRITECOPY,
100 PAGE_NOACCESS,
101 PAGE_WRITECOMBINE | PAGE_READONLY,
102 PAGE_WRITECOMBINE | PAGE_EXECUTE,
103 PAGE_WRITECOMBINE | PAGE_EXECUTE_READ,
104 PAGE_WRITECOMBINE | PAGE_READWRITE,
105 PAGE_WRITECOMBINE | PAGE_WRITECOPY,
106 PAGE_WRITECOMBINE | PAGE_EXECUTE_READWRITE,
107 PAGE_WRITECOMBINE | PAGE_EXECUTE_WRITECOPY
108 };
109
110 /* FUNCTIONS ***************************************************************/
111
112 NTSTATUS
113 NTAPI
114 MiFillSystemPageDirectory(IN PVOID Base,
115 IN SIZE_T NumberOfBytes);
116
117 static
118 BOOLEAN
MiIsPageTablePresent(PVOID Address)119 MiIsPageTablePresent(PVOID Address)
120 {
121 #if _MI_PAGING_LEVELS == 2
122 BOOLEAN Ret = MmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)] != 0;
123
124 /* Some sanity check while we're here */
125 ASSERT(Ret == (MiAddressToPde(Address)->u.Hard.Valid != 0));
126
127 return Ret;
128 #else
129 PMMPDE PointerPde;
130 PMMPPE PointerPpe;
131 #if _MI_PAGING_LEVELS == 4
132 PMMPXE PointerPxe;
133 #endif
134 PMMPFN Pfn;
135
136 /* Make sure we're locked */
137 ASSERT((PsGetCurrentThread()->OwnsProcessWorkingSetExclusive) || (PsGetCurrentThread()->OwnsProcessWorkingSetShared));
138
139 /* Must not hold the PFN lock! */
140 ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
141
142 /* Check if PXE or PPE have references first. */
143 #if _MI_PAGING_LEVELS == 4
144 PointerPxe = MiAddressToPxe(Address);
145 if ((PointerPxe->u.Hard.Valid == 1) || (PointerPxe->u.Soft.Transition == 1))
146 {
147 Pfn = MiGetPfnEntry(PFN_FROM_PXE(PointerPxe));
148 if (Pfn->OriginalPte.u.Soft.UsedPageTableEntries == 0)
149 return FALSE;
150 }
151 else if (PointerPxe->u.Soft.UsedPageTableEntries == 0)
152 {
153 return FALSE;
154 }
155
156 if (PointerPxe->u.Hard.Valid == 0)
157 {
158 MiMakeSystemAddressValid(MiPteToAddress(PointerPxe), PsGetCurrentProcess());
159 }
160 #endif
161
162 PointerPpe = MiAddressToPpe(Address);
163 if ((PointerPpe->u.Hard.Valid == 1) || (PointerPpe->u.Soft.Transition == 1))
164 {
165 Pfn = MiGetPfnEntry(PFN_FROM_PPE(PointerPpe));
166 if (Pfn->OriginalPte.u.Soft.UsedPageTableEntries == 0)
167 return FALSE;
168 }
169 else if (PointerPpe->u.Soft.UsedPageTableEntries == 0)
170 {
171 return FALSE;
172 }
173
174 if (PointerPpe->u.Hard.Valid == 0)
175 {
176 MiMakeSystemAddressValid(MiPteToAddress(PointerPpe), PsGetCurrentProcess());
177 }
178
179 PointerPde = MiAddressToPde(Address);
180 if ((PointerPde->u.Hard.Valid == 0) && (PointerPde->u.Soft.Transition == 0))
181 {
182 return PointerPde->u.Soft.UsedPageTableEntries != 0;
183 }
184
185 /* This lies on the PFN */
186 Pfn = MiGetPfnEntry(PFN_FROM_PDE(PointerPde));
187 return Pfn->OriginalPte.u.Soft.UsedPageTableEntries != 0;
188 #endif
189 }
190
191 PFN_NUMBER
192 NTAPI
MmGetPfnForProcess(PEPROCESS Process,PVOID Address)193 MmGetPfnForProcess(PEPROCESS Process,
194 PVOID Address)
195 {
196 PMMPTE PointerPte;
197 PFN_NUMBER Page;
198
199 /* Must be called for user mode only */
200 ASSERT(Process != NULL);
201 ASSERT(Address < MmSystemRangeStart);
202
203 /* And for our process */
204 ASSERT(Process == PsGetCurrentProcess());
205
206 /* Lock for reading */
207 MiLockProcessWorkingSetShared(Process, PsGetCurrentThread());
208
209 if (!MiIsPageTablePresent(Address))
210 {
211 MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
212 return 0;
213 }
214
215 /* Make sure we can read the PTE */
216 MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
217
218 PointerPte = MiAddressToPte(Address);
219 Page = PointerPte->u.Hard.Valid ? PFN_FROM_PTE(PointerPte) : 0;
220
221 MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
222 return Page;
223 }
224
225 /**
226 * @brief Deletes the virtual mapping and optionally gives back the page & dirty bit.
227 *
228 * @param Process - The process this address belongs to, or NULL if system address.
229 * @param Address - The address to unmap.
230 * @param WasDirty - Optional param receiving the dirty bit of the PTE.
231 * @param Page - Optional param receiving the page number previously mapped to this address.
232 *
233 * @return Whether there was actually a page mapped at the given address.
234 */
_Success_(return)235 _Success_(return)
236 BOOLEAN
237 MmDeleteVirtualMappingEx(
238 _Inout_opt_ PEPROCESS Process,
239 _In_ PVOID Address,
240 _Out_opt_ BOOLEAN* WasDirty,
241 _Out_opt_ PPFN_NUMBER Page,
242 _In_ BOOLEAN IsPhysical)
243 {
244 PMMPTE PointerPte;
245 MMPTE OldPte;
246 BOOLEAN ValidPde;
247
248 OldPte.u.Long = 0;
249
250 DPRINT("MmDeleteVirtualMapping(%p, %p, %p, %p)\n", Process, Address, WasDirty, Page);
251
252 ASSERT(((ULONG_PTR)Address % PAGE_SIZE) == 0);
253
254 /* And we should be at low IRQL */
255 ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
256
257 /* Make sure our PDE is valid, and that everything is going fine */
258 if (Process == NULL)
259 {
260 if (Address < MmSystemRangeStart)
261 {
262 DPRINT1("NULL process given for user-mode mapping at %p\n", Address);
263 KeBugCheck(MEMORY_MANAGEMENT);
264 }
265 #if (_MI_PAGING_LEVELS == 2)
266 ValidPde = MiSynchronizeSystemPde(MiAddressToPde(Address));
267 #else
268 ValidPde = MiIsPdeForAddressValid(Address);
269 #endif
270 }
271 else
272 {
273 if ((Address >= MmSystemRangeStart) || Add2Ptr(Address, PAGE_SIZE) >= MmSystemRangeStart)
274 {
275 DPRINT1("Process %p given for kernel-mode mapping at %p\n", Process, Address);
276 KeBugCheck(MEMORY_MANAGEMENT);
277 }
278
279 /* Only for current process !!! */
280 ASSERT(Process == PsGetCurrentProcess());
281 MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
282
283 ValidPde = MiIsPageTablePresent(Address);
284 if (ValidPde)
285 {
286 MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
287 }
288 }
289
290 /* Get the PTE if we're having anything */
291 if (ValidPde)
292 {
293 PointerPte = MiAddressToPte(Address);
294 OldPte.u.Long = InterlockedExchangePte(PointerPte, 0);
295
296 KeInvalidateTlbEntry(Address);
297
298 if (OldPte.u.Long != 0)
299 {
300 /* It must have been present, or not a swap entry */
301 ASSERT(OldPte.u.Hard.Valid || !FlagOn(OldPte.u.Long, 0x800));
302 if (WasDirty != NULL)
303 {
304 *WasDirty = !!OldPte.u.Hard.Dirty;
305 }
306 if (Page != NULL)
307 {
308 *Page = OldPte.u.Hard.PageFrameNumber;
309 }
310 }
311 }
312
313 if (Process != NULL)
314 {
315 /* Remove PDE reference, if needed */
316 if (OldPte.u.Long != 0)
317 {
318 if (MiDecrementPageTableReferences(Address) == 0)
319 {
320 KIRQL OldIrql = MiAcquirePfnLock();
321 MiDeletePde(MiAddressToPde(Address), Process);
322 MiReleasePfnLock(OldIrql);
323 }
324 }
325
326 if (!IsPhysical && OldPte.u.Hard.Valid)
327 {
328 PMMPFN Pfn1;
329 KIRQL OldIrql;
330
331 OldIrql = MiAcquirePfnLock();
332 Pfn1 = &MmPfnDatabase[OldPte.u.Hard.PageFrameNumber];
333 ASSERT(Pfn1->u3.e1.PageLocation == ActiveAndValid);
334 ASSERT(Pfn1->u2.ShareCount > 0);
335 if (--Pfn1->u2.ShareCount == 0)
336 {
337 Pfn1->u3.e1.PageLocation = TransitionPage;
338 }
339 MiReleasePfnLock(OldIrql);
340 }
341
342 MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
343 }
344
345 return OldPte.u.Long != 0;
346 }
347
_Success_(return)348 _Success_(return)
349 BOOLEAN
350 MmDeleteVirtualMapping(
351 _Inout_opt_ PEPROCESS Process,
352 _In_ PVOID Address,
353 _Out_opt_ BOOLEAN * WasDirty,
354 _Out_opt_ PPFN_NUMBER Page)
355 {
356 return MmDeleteVirtualMappingEx(Process, Address, WasDirty, Page, FALSE);
357 }
358
_Success_(return)359 _Success_(return)
360 BOOLEAN
361 MmDeletePhysicalMapping(
362 _Inout_opt_ PEPROCESS Process,
363 _In_ PVOID Address,
364 _Out_opt_ BOOLEAN * WasDirty,
365 _Out_opt_ PPFN_NUMBER Page)
366 {
367 return MmDeleteVirtualMappingEx(Process, Address, WasDirty, Page, TRUE);
368 }
369
370 VOID
371 NTAPI
MmDeletePageFileMapping(PEPROCESS Process,PVOID Address,SWAPENTRY * SwapEntry)372 MmDeletePageFileMapping(
373 PEPROCESS Process,
374 PVOID Address,
375 SWAPENTRY* SwapEntry)
376 {
377 PMMPTE PointerPte;
378 MMPTE OldPte;
379
380 /* This should not be called for kernel space anymore */
381 ASSERT(Process != NULL);
382 ASSERT(Address < MmSystemRangeStart);
383
384 /* And we don't support deleting for other process */
385 ASSERT(Process == PsGetCurrentProcess());
386
387 /* And we should be at low IRQL */
388 ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
389
390 /* We are tinkering with the PDE here. Ensure it will be there */
391 MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
392
393 /* Callers must ensure there is actually something there */
394 ASSERT(MiAddressToPde(Address)->u.Long != 0);
395
396 MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
397
398 PointerPte = MiAddressToPte(Address);
399 OldPte.u.Long = InterlockedExchangePte(PointerPte, 0);
400 /* This must be a swap entry ! */
401 if (!FlagOn(OldPte.u.Long, 0x800) || OldPte.u.Hard.Valid)
402 {
403 KeBugCheckEx(MEMORY_MANAGEMENT, OldPte.u.Long, (ULONG_PTR)Process, (ULONG_PTR)Address, 0);
404 }
405
406 /* This used to be a non-zero PTE, now we can let the PDE go. */
407 if (MiDecrementPageTableReferences(Address) == 0)
408 {
409 /* We can let it go */
410 KIRQL OldIrql = MiAcquirePfnLock();
411 MiDeletePde(MiPteToPde(PointerPte), Process);
412 MiReleasePfnLock(OldIrql);
413 }
414
415 MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
416
417 *SwapEntry = OldPte.u.Long >> 1;
418 }
419
420 BOOLEAN
421 NTAPI
MmIsPagePresent(PEPROCESS Process,PVOID Address)422 MmIsPagePresent(PEPROCESS Process, PVOID Address)
423 {
424 BOOLEAN Ret;
425
426 if (Address >= MmSystemRangeStart)
427 {
428 ASSERT(Process == NULL);
429 #if _MI_PAGING_LEVELS == 2
430 if (!MiSynchronizeSystemPde(MiAddressToPde(Address)))
431 #else
432 if (!MiIsPdeForAddressValid(Address))
433 #endif
434 {
435 /* It can't be present if there is no PDE */
436 return FALSE;
437 }
438
439 return MiAddressToPte(Address)->u.Hard.Valid;
440 }
441
442 ASSERT(Process != NULL);
443 ASSERT(Process == PsGetCurrentProcess());
444
445 MiLockProcessWorkingSetShared(Process, PsGetCurrentThread());
446
447 if (!MiIsPageTablePresent(Address))
448 {
449 /* It can't be present if there is no PDE */
450 MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
451 return FALSE;
452 }
453
454 MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
455
456 Ret = MiAddressToPte(Address)->u.Hard.Valid;
457
458 MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
459
460 return Ret;
461 }
462
463 BOOLEAN
464 NTAPI
MmIsDisabledPage(PEPROCESS Process,PVOID Address)465 MmIsDisabledPage(PEPROCESS Process, PVOID Address)
466 {
467 BOOLEAN Ret;
468 PMMPTE PointerPte;
469
470 if (Address >= MmSystemRangeStart)
471 {
472 ASSERT(Process == NULL);
473 #if _MI_PAGING_LEVELS == 2
474 if (!MiSynchronizeSystemPde(MiAddressToPde(Address)))
475 #else
476 if (!MiIsPdeForAddressValid(Address))
477 #endif
478 {
479 /* It's not disabled if it's not present */
480 return FALSE;
481 }
482 }
483 else
484 {
485 ASSERT(Process != NULL);
486 ASSERT(Process == PsGetCurrentProcess());
487
488 MiLockProcessWorkingSetShared(Process, PsGetCurrentThread());
489
490 if (!MiIsPageTablePresent(Address))
491 {
492 /* It can't be disabled if there is no PDE */
493 MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
494 return FALSE;
495 }
496
497 MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
498 }
499
500 PointerPte = MiAddressToPte(Address);
501 Ret = !PointerPte->u.Hard.Valid
502 && !FlagOn(PointerPte->u.Long, 0x800)
503 && (PointerPte->u.Hard.PageFrameNumber != 0);
504
505 if (Address < MmSystemRangeStart)
506 MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
507
508 return Ret;
509 }
510
511 BOOLEAN
512 NTAPI
MmIsPageSwapEntry(PEPROCESS Process,PVOID Address)513 MmIsPageSwapEntry(PEPROCESS Process, PVOID Address)
514 {
515 BOOLEAN Ret;
516 PMMPTE PointerPte;
517
518 /* We never set swap entries for kernel addresses */
519 if (Address >= MmSystemRangeStart)
520 {
521 ASSERT(Process == NULL);
522 return FALSE;
523 }
524
525 ASSERT(Process != NULL);
526 ASSERT(Process == PsGetCurrentProcess());
527
528 MiLockProcessWorkingSetShared(Process, PsGetCurrentThread());
529
530 if (!MiIsPageTablePresent(Address))
531 {
532 /* There can't be a swap entry if there is no PDE */
533 MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
534 return FALSE;
535 }
536
537 MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
538
539 PointerPte = MiAddressToPte(Address);
540 Ret = !PointerPte->u.Hard.Valid && FlagOn(PointerPte->u.Long, 0x800);
541
542 MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
543
544 return Ret;
545 }
546
547 VOID
548 NTAPI
MmGetPageFileMapping(PEPROCESS Process,PVOID Address,SWAPENTRY * SwapEntry)549 MmGetPageFileMapping(PEPROCESS Process, PVOID Address, SWAPENTRY* SwapEntry)
550 {
551 PMMPTE PointerPte;
552
553 /* We never set swap entries for kernel addresses */
554 if (Address >= MmSystemRangeStart)
555 {
556 ASSERT(Process == NULL);
557 *SwapEntry = 0;
558 return;
559 }
560
561 ASSERT(Process != NULL);
562 ASSERT(Process == PsGetCurrentProcess());
563
564 MiLockProcessWorkingSetShared(Process, PsGetCurrentThread());
565
566 if (!MiIsPageTablePresent(Address))
567 {
568 /* There can't be a swap entry if there is no PDE */
569 MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
570 *SwapEntry = 0;
571 return;
572 }
573
574 MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
575
576 PointerPte = MiAddressToPte(Address);
577 if (!PointerPte->u.Hard.Valid && FlagOn(PointerPte->u.Long, 0x800))
578 *SwapEntry = PointerPte->u.Long >> 1;
579 else
580 *SwapEntry = 0;
581
582 MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
583 }
584
585 NTSTATUS
586 NTAPI
MmCreatePageFileMapping(PEPROCESS Process,PVOID Address,SWAPENTRY SwapEntry)587 MmCreatePageFileMapping(PEPROCESS Process,
588 PVOID Address,
589 SWAPENTRY SwapEntry)
590 {
591 PMMPTE PointerPte;
592 ULONG_PTR Pte;
593
594 /* This should not be called for kernel space anymore */
595 ASSERT(Process != NULL);
596 ASSERT(Address < MmSystemRangeStart);
597
598 /* And we don't support creating for other process */
599 ASSERT(Process == PsGetCurrentProcess());
600
601 if (SwapEntry & ((ULONG_PTR)1 << (RTL_BITS_OF(SWAPENTRY) - 1)))
602 {
603 KeBugCheck(MEMORY_MANAGEMENT);
604 }
605
606 /* We are tinkering with the PDE here. Ensure it will be there */
607 ASSERT(Process == PsGetCurrentProcess());
608 MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
609
610 MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
611
612 PointerPte = MiAddressToPte(Address);
613 Pte = InterlockedExchangePte(PointerPte, SwapEntry << 1);
614 if (Pte != 0)
615 {
616 KeBugCheckEx(MEMORY_MANAGEMENT, SwapEntry, (ULONG_PTR)Process, (ULONG_PTR)Address, 0);
617 }
618
619 /* This used to be a 0 PTE, now we need a valid PDE to keep it around */
620 MiIncrementPageTableReferences(Address);
621 MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
622
623 return STATUS_SUCCESS;
624 }
625
626
627 NTSTATUS
628 NTAPI
MmCreateVirtualMappingUnsafeEx(_Inout_opt_ PEPROCESS Process,_In_ PVOID Address,_In_ ULONG flProtect,_In_ PFN_NUMBER Page,_In_ BOOLEAN IsPhysical)629 MmCreateVirtualMappingUnsafeEx(
630 _Inout_opt_ PEPROCESS Process,
631 _In_ PVOID Address,
632 _In_ ULONG flProtect,
633 _In_ PFN_NUMBER Page,
634 _In_ BOOLEAN IsPhysical)
635 {
636 ULONG ProtectionMask;
637 PMMPTE PointerPte;
638 MMPTE TempPte;
639 ULONG_PTR Pte;
640
641 DPRINT("MmCreateVirtualMappingUnsafe(%p, %p, %lu, %x)\n",
642 Process, Address, flProtect, Page);
643
644 ASSERT(((ULONG_PTR)Address % PAGE_SIZE) == 0);
645
646 ProtectionMask = MiMakeProtectionMask(flProtect);
647 /* Caller must have checked ! */
648 ASSERT(ProtectionMask != MM_INVALID_PROTECTION);
649 ASSERT(ProtectionMask != MM_NOACCESS);
650 ASSERT(ProtectionMask != MM_ZERO_ACCESS);
651
652 /* Make sure our PDE is valid, and that everything is going fine */
653 if (Process == NULL)
654 {
655 /* We don't support this in legacy Mm for kernel mappings */
656 ASSERT(ProtectionMask != MM_WRITECOPY);
657 ASSERT(ProtectionMask != MM_EXECUTE_WRITECOPY);
658
659 if (Address < MmSystemRangeStart)
660 {
661 DPRINT1("NULL process given for user-mode mapping at %p\n", Address);
662 KeBugCheck(MEMORY_MANAGEMENT);
663 }
664 #if _MI_PAGING_LEVELS == 2
665 if (!MiSynchronizeSystemPde(MiAddressToPde(Address)))
666 MiFillSystemPageDirectory(Address, PAGE_SIZE);
667 #endif
668 }
669 else
670 {
671 if ((Address >= MmSystemRangeStart) || Add2Ptr(Address, PAGE_SIZE) >= MmSystemRangeStart)
672 {
673 DPRINT1("Process %p given for kernel-mode mapping at %p -- 1 page starting at %Ix\n",
674 Process, Address, Page);
675 KeBugCheck(MEMORY_MANAGEMENT);
676 }
677
678 /* Only for current process !!! */
679 ASSERT(Process == PsGetCurrentProcess());
680 MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
681
682 MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
683 }
684
685 PointerPte = MiAddressToPte(Address);
686
687 MI_MAKE_HARDWARE_PTE(&TempPte, PointerPte, ProtectionMask, Page);
688
689 Pte = InterlockedExchangePte(PointerPte, TempPte.u.Long);
690 /* There should not have been anything valid here */
691 if (Pte != 0)
692 {
693 DPRINT1("Bad PTE %lx at %p for %p\n", Pte, PointerPte, Address);
694 KeBugCheck(MEMORY_MANAGEMENT);
695 }
696
697 if (!IsPhysical)
698 {
699 PMMPFN Pfn1;
700 KIRQL OldIrql;
701
702 OldIrql = MiAcquirePfnLock();
703 Pfn1 = &MmPfnDatabase[TempPte.u.Hard.PageFrameNumber];
704 Pfn1->u2.ShareCount++;
705 Pfn1->u3.e1.PageLocation = ActiveAndValid;
706 MiReleasePfnLock(OldIrql);
707 }
708
709 /* We don't need to flush the TLB here because it only caches valid translations
710 * and we're moving this PTE from invalid to valid so it can't be cached right now */
711
712 if (Address < MmSystemRangeStart)
713 {
714 /* Add PDE reference */
715 MiIncrementPageTableReferences(Address);
716 MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
717 }
718
719 return(STATUS_SUCCESS);
720 }
721
722 NTSTATUS
723 NTAPI
MmCreateVirtualMappingUnsafe(_Inout_opt_ PEPROCESS Process,_In_ PVOID Address,_In_ ULONG flProtect,_In_ PFN_NUMBER Page)724 MmCreateVirtualMappingUnsafe(
725 _Inout_opt_ PEPROCESS Process,
726 _In_ PVOID Address,
727 _In_ ULONG flProtect,
728 _In_ PFN_NUMBER Page)
729 {
730 return MmCreateVirtualMappingUnsafeEx(Process, Address, flProtect, Page, FALSE);
731 }
732
733 NTSTATUS
734 NTAPI
MmCreatePhysicalMapping(_Inout_opt_ PEPROCESS Process,_In_ PVOID Address,_In_ ULONG flProtect,_In_ PFN_NUMBER Page)735 MmCreatePhysicalMapping(
736 _Inout_opt_ PEPROCESS Process,
737 _In_ PVOID Address,
738 _In_ ULONG flProtect,
739 _In_ PFN_NUMBER Page)
740 {
741 return MmCreateVirtualMappingUnsafeEx(Process, Address, flProtect, Page, TRUE);
742 }
743
744 NTSTATUS
745 NTAPI
MmCreateVirtualMapping(PEPROCESS Process,PVOID Address,ULONG flProtect,PFN_NUMBER Page)746 MmCreateVirtualMapping(PEPROCESS Process,
747 PVOID Address,
748 ULONG flProtect,
749 PFN_NUMBER Page)
750 {
751 ASSERT((ULONG_PTR)Address % PAGE_SIZE == 0);
752 if (!MmIsPageInUse(Page))
753 {
754 DPRINT1("Page %lx is not in use\n", Page);
755 KeBugCheck(MEMORY_MANAGEMENT);
756 }
757
758 return MmCreateVirtualMappingUnsafe(Process, Address, flProtect, Page);
759 }
760
761 ULONG
762 NTAPI
MmGetPageProtect(PEPROCESS Process,PVOID Address)763 MmGetPageProtect(PEPROCESS Process, PVOID Address)
764 {
765 PMMPTE PointerPte;
766 ULONG Protect;
767
768 if (Address >= MmSystemRangeStart)
769 {
770 ASSERT(Process == NULL);
771
772 #if _MI_PAGING_LEVELS == 2
773 if (!MiSynchronizeSystemPde(MiAddressToPde(Address)))
774 #else
775 if (!MiIsPdeForAddressValid(Address))
776 #endif
777 {
778 return PAGE_NOACCESS;
779 }
780 }
781 else
782 {
783 ASSERT(Address < MmSystemRangeStart);
784 ASSERT(Process != NULL);
785
786 ASSERT(Process == PsGetCurrentProcess());
787
788 MiLockProcessWorkingSetShared(Process, PsGetCurrentThread());
789
790 if (!MiIsPageTablePresent(Address))
791 {
792 /* It can't be present if there is no PDE */
793 MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
794 return PAGE_NOACCESS;
795 }
796
797 MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
798 }
799
800 PointerPte = MiAddressToPte(Address);
801
802 if (!PointerPte->u.Flush.Valid)
803 {
804 Protect = PAGE_NOACCESS;
805 }
806 else
807 {
808 if (PointerPte->u.Flush.CopyOnWrite)
809 Protect = PAGE_WRITECOPY;
810 else if (PointerPte->u.Flush.Write)
811 Protect = PAGE_READWRITE;
812 else
813 Protect = PAGE_READONLY;
814 #if _MI_PAGING_LEVELS >= 3
815 /* PAE & AMD64 long mode support NoExecute bit */
816 if (!PointerPte->u.Flush.NoExecute)
817 Protect <<= 4;
818 #endif
819 if (PointerPte->u.Flush.CacheDisable)
820 Protect |= PAGE_NOCACHE;
821 if (PointerPte->u.Flush.WriteThrough)
822 Protect |= PAGE_WRITETHROUGH;
823 }
824
825 if (Address < MmSystemRangeStart)
826 MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
827
828 return(Protect);
829 }
830
831 VOID
832 NTAPI
MmSetPageProtect(PEPROCESS Process,PVOID Address,ULONG flProtect)833 MmSetPageProtect(PEPROCESS Process, PVOID Address, ULONG flProtect)
834 {
835 ULONG ProtectionMask;
836 PMMPTE PointerPte;
837 MMPTE TempPte, OldPte;
838
839 DPRINT("MmSetPageProtect(Process %p Address %p flProtect %x)\n",
840 Process, Address, flProtect);
841
842 ASSERT(Process != NULL);
843 ASSERT(Address < MmSystemRangeStart);
844
845 ASSERT(Process == PsGetCurrentProcess());
846
847 ProtectionMask = MiMakeProtectionMask(flProtect);
848 /* Caller must have checked ! */
849 ASSERT(ProtectionMask != MM_INVALID_PROTECTION);
850
851 MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
852
853 MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
854
855 PointerPte = MiAddressToPte(Address);
856
857 /* Sanity check */
858 ASSERT(PointerPte->u.Hard.Owner == 1);
859
860 TempPte.u.Long = 0;
861 TempPte.u.Hard.PageFrameNumber = PointerPte->u.Hard.PageFrameNumber;
862 TempPte.u.Long |= MmProtectToPteMask[ProtectionMask];
863 TempPte.u.Hard.Owner = 1;
864
865 /* Only set valid bit if we have to */
866 if ((ProtectionMask != MM_NOACCESS) && !FlagOn(ProtectionMask, MM_GUARDPAGE))
867 TempPte.u.Hard.Valid = 1;
868
869 /* Keep dirty & accessed bits */
870 TempPte.u.Hard.Accessed = PointerPte->u.Hard.Accessed;
871 TempPte.u.Hard.Dirty = PointerPte->u.Hard.Dirty;
872
873 OldPte.u.Long = InterlockedExchangePte(PointerPte, TempPte.u.Long);
874
875 // We should be able to bring a page back from PAGE_NOACCESS
876 if (!OldPte.u.Hard.Valid && (FlagOn(OldPte.u.Long, 0x800) || (OldPte.u.Hard.PageFrameNumber == 0)))
877 {
878 DPRINT1("Invalid Pte %lx\n", OldPte.u.Long);
879 KeBugCheck(MEMORY_MANAGEMENT);
880 }
881
882 if (OldPte.u.Long != TempPte.u.Long)
883 KeInvalidateTlbEntry(Address);
884
885 MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
886 }
887
888 VOID
889 NTAPI
MmSetDirtyBit(PEPROCESS Process,PVOID Address,BOOLEAN Bit)890 MmSetDirtyBit(PEPROCESS Process, PVOID Address, BOOLEAN Bit)
891 {
892 PMMPTE PointerPte;
893
894 DPRINT("MmSetDirtyBit(Process %p Address %p Bit %x)\n",
895 Process, Address, Bit);
896
897 ASSERT(Process != NULL);
898 ASSERT(Address < MmSystemRangeStart);
899
900 ASSERT(Process == PsGetCurrentProcess());
901
902 MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
903
904 MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
905
906 PointerPte = MiAddressToPte(Address);
907 // We shouldnl't set dirty bit on non-mapped addresses
908 if (!PointerPte->u.Hard.Valid && (FlagOn(PointerPte->u.Long, 0x800) || (PointerPte->u.Hard.PageFrameNumber == 0)))
909 {
910 DPRINT1("Invalid Pte %lx\n", PointerPte->u.Long);
911 KeBugCheck(MEMORY_MANAGEMENT);
912 }
913
914 PointerPte->u.Hard.Dirty = !!Bit;
915
916 if (!Bit)
917 KeInvalidateTlbEntry(Address);
918
919 MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
920 }
921
922 CODE_SEG("INIT")
923 VOID
924 NTAPI
MmInitGlobalKernelPageDirectory(VOID)925 MmInitGlobalKernelPageDirectory(VOID)
926 {
927 /* Nothing to do here */
928 }
929
930 #ifdef _M_IX86
931 BOOLEAN
Mmi386MakeKernelPageTableGlobal(PVOID Address)932 Mmi386MakeKernelPageTableGlobal(PVOID Address)
933 {
934 PMMPDE PointerPde = MiAddressToPde(Address);
935 PMMPTE PointerPte = MiAddressToPte(Address);
936
937 if (PointerPde->u.Hard.Valid == 0)
938 {
939 if (!MiSynchronizeSystemPde(PointerPde))
940 return FALSE;
941 return PointerPte->u.Hard.Valid != 0;
942 }
943 return FALSE;
944 }
945 #endif
946
947 /* EOF */
948