[THEMES]
[reactos.git] / reactos / ntoskrnl / mm / i386 / page.c
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 managment 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 #include "../ARM3/miarm.h"
16
17 #if defined (ALLOC_PRAGMA)
18 #pragma alloc_text(INIT, MmInitGlobalKernelPageDirectory)
19 #pragma alloc_text(INIT, MiInitPageDirectoryMap)
20 #endif
21
22
23 /* GLOBALS *****************************************************************/
24
25 #define PA_BIT_PRESENT (0)
26 #define PA_BIT_READWRITE (1)
27 #define PA_BIT_USER (2)
28 #define PA_BIT_WT (3)
29 #define PA_BIT_CD (4)
30 #define PA_BIT_ACCESSED (5)
31 #define PA_BIT_DIRTY (6)
32 #define PA_BIT_GLOBAL (8)
33
34 #define PA_PRESENT (1 << PA_BIT_PRESENT)
35 #define PA_READWRITE (1 << PA_BIT_READWRITE)
36 #define PA_USER (1 << PA_BIT_USER)
37 #define PA_DIRTY (1 << PA_BIT_DIRTY)
38 #define PA_WT (1 << PA_BIT_WT)
39 #define PA_CD (1 << PA_BIT_CD)
40 #define PA_ACCESSED (1 << PA_BIT_ACCESSED)
41 #define PA_GLOBAL (1 << PA_BIT_GLOBAL)
42
43 #define HYPERSPACE (0xc0400000)
44 #define IS_HYPERSPACE(v) (((ULONG)(v) >= HYPERSPACE && (ULONG)(v) < HYPERSPACE + 0x400000))
45
46 #define PTE_TO_PFN(X) ((X) >> PAGE_SHIFT)
47 #define PFN_TO_PTE(X) ((X) << PAGE_SHIFT)
48
49 #if defined(__GNUC__)
50 #define PTE_TO_PAGE(X) ((LARGE_INTEGER)(LONGLONG)(PAGE_MASK(X)))
51 #else
52 __inline LARGE_INTEGER PTE_TO_PAGE(ULONG npage)
53 {
54 LARGE_INTEGER dummy;
55 dummy.QuadPart = (LONGLONG)(PAGE_MASK(npage));
56 return dummy;
57 }
58 #endif
59
60 const
61 ULONG
62 MmProtectToPteMask[32] =
63 {
64 //
65 // These are the base MM_ protection flags
66 //
67 0,
68 PTE_READONLY | PTE_ENABLE_CACHE,
69 PTE_EXECUTE | PTE_ENABLE_CACHE,
70 PTE_EXECUTE_READ | PTE_ENABLE_CACHE,
71 PTE_READWRITE | PTE_ENABLE_CACHE,
72 PTE_WRITECOPY | PTE_ENABLE_CACHE,
73 PTE_EXECUTE_READWRITE | PTE_ENABLE_CACHE,
74 PTE_EXECUTE_WRITECOPY | PTE_ENABLE_CACHE,
75 //
76 // These OR in the MM_NOCACHE flag
77 //
78 0,
79 PTE_READONLY | PTE_DISABLE_CACHE,
80 PTE_EXECUTE | PTE_DISABLE_CACHE,
81 PTE_EXECUTE_READ | PTE_DISABLE_CACHE,
82 PTE_READWRITE | PTE_DISABLE_CACHE,
83 PTE_WRITECOPY | PTE_DISABLE_CACHE,
84 PTE_EXECUTE_READWRITE | PTE_DISABLE_CACHE,
85 PTE_EXECUTE_WRITECOPY | PTE_DISABLE_CACHE,
86 //
87 // These OR in the MM_DECOMMIT flag, which doesn't seem supported on x86/64/ARM
88 //
89 0,
90 PTE_READONLY | PTE_ENABLE_CACHE,
91 PTE_EXECUTE | PTE_ENABLE_CACHE,
92 PTE_EXECUTE_READ | PTE_ENABLE_CACHE,
93 PTE_READWRITE | PTE_ENABLE_CACHE,
94 PTE_WRITECOPY | PTE_ENABLE_CACHE,
95 PTE_EXECUTE_READWRITE | PTE_ENABLE_CACHE,
96 PTE_EXECUTE_WRITECOPY | PTE_ENABLE_CACHE,
97 //
98 // These OR in the MM_NOACCESS flag, which seems to enable WriteCombining?
99 //
100 0,
101 PTE_READONLY | PTE_WRITECOMBINED_CACHE,
102 PTE_EXECUTE | PTE_WRITECOMBINED_CACHE,
103 PTE_EXECUTE_READ | PTE_WRITECOMBINED_CACHE,
104 PTE_READWRITE | PTE_WRITECOMBINED_CACHE,
105 PTE_WRITECOPY | PTE_WRITECOMBINED_CACHE,
106 PTE_EXECUTE_READWRITE | PTE_WRITECOMBINED_CACHE,
107 PTE_EXECUTE_WRITECOPY | PTE_WRITECOMBINED_CACHE,
108 };
109
110 const
111 ULONG MmProtectToValue[32] =
112 {
113 PAGE_NOACCESS,
114 PAGE_READONLY,
115 PAGE_EXECUTE,
116 PAGE_EXECUTE_READ,
117 PAGE_READWRITE,
118 PAGE_WRITECOPY,
119 PAGE_EXECUTE_READWRITE,
120 PAGE_EXECUTE_WRITECOPY,
121 PAGE_NOACCESS,
122 PAGE_NOCACHE | PAGE_READONLY,
123 PAGE_NOCACHE | PAGE_EXECUTE,
124 PAGE_NOCACHE | PAGE_EXECUTE_READ,
125 PAGE_NOCACHE | PAGE_READWRITE,
126 PAGE_NOCACHE | PAGE_WRITECOPY,
127 PAGE_NOCACHE | PAGE_EXECUTE_READWRITE,
128 PAGE_NOCACHE | PAGE_EXECUTE_WRITECOPY,
129 PAGE_NOACCESS,
130 PAGE_GUARD | PAGE_READONLY,
131 PAGE_GUARD | PAGE_EXECUTE,
132 PAGE_GUARD | PAGE_EXECUTE_READ,
133 PAGE_GUARD | PAGE_READWRITE,
134 PAGE_GUARD | PAGE_WRITECOPY,
135 PAGE_GUARD | PAGE_EXECUTE_READWRITE,
136 PAGE_GUARD | PAGE_EXECUTE_WRITECOPY,
137 PAGE_NOACCESS,
138 PAGE_WRITECOMBINE | PAGE_READONLY,
139 PAGE_WRITECOMBINE | PAGE_EXECUTE,
140 PAGE_WRITECOMBINE | PAGE_EXECUTE_READ,
141 PAGE_WRITECOMBINE | PAGE_READWRITE,
142 PAGE_WRITECOMBINE | PAGE_WRITECOPY,
143 PAGE_WRITECOMBINE | PAGE_EXECUTE_READWRITE,
144 PAGE_WRITECOMBINE | PAGE_EXECUTE_WRITECOPY
145 };
146
147 /* FUNCTIONS ***************************************************************/
148
149 static BOOLEAN MmUnmapPageTable(PULONG Pt);
150
151 VOID
152 MiFlushTlb(PULONG Pt, PVOID Address)
153 {
154 if ((Pt && MmUnmapPageTable(Pt)) || Address >= MmSystemRangeStart)
155 {
156 KeInvalidateTlbEntry(Address);
157 }
158 }
159
160 static ULONG
161 ProtectToPTE(ULONG flProtect)
162 {
163 ULONG Attributes = 0;
164
165 if (flProtect & (PAGE_NOACCESS|PAGE_GUARD))
166 {
167 Attributes = 0;
168 }
169 else if (flProtect & PAGE_IS_WRITABLE)
170 {
171 Attributes = PA_PRESENT | PA_READWRITE;
172 }
173 else if (flProtect & (PAGE_IS_READABLE | PAGE_IS_EXECUTABLE))
174 {
175 Attributes = PA_PRESENT;
176 }
177 else
178 {
179 DPRINT1("Unknown main protection type.\n");
180 KeBugCheck(MEMORY_MANAGEMENT);
181 }
182
183 if (flProtect & PAGE_SYSTEM)
184 {
185 }
186 else
187 {
188 Attributes = Attributes | PA_USER;
189 }
190 if (flProtect & PAGE_NOCACHE)
191 {
192 Attributes = Attributes | PA_CD;
193 }
194 if (flProtect & PAGE_WRITETHROUGH)
195 {
196 Attributes = Attributes | PA_WT;
197 }
198 return(Attributes);
199 }
200
201 /* Taken from ARM3/pagfault.c */
202 FORCEINLINE
203 BOOLEAN
204 MiSynchronizeSystemPde(PMMPDE PointerPde)
205 {
206 MMPDE SystemPde;
207 ULONG Index;
208
209 /* Get the Index from the PDE */
210 Index = ((ULONG_PTR)PointerPde & (SYSTEM_PD_SIZE - 1)) / sizeof(MMPTE);
211
212 /* Copy the PDE from the double-mapped system page directory */
213 SystemPde = MmSystemPagePtes[Index];
214 *PointerPde = SystemPde;
215
216 /* Make sure we re-read the PDE and PTE */
217 KeMemoryBarrierWithoutFence();
218
219 /* Return, if we had success */
220 return SystemPde.u.Hard.Valid != 0;
221 }
222
223 NTSTATUS
224 NTAPI
225 MiDispatchFault(IN BOOLEAN StoreInstruction,
226 IN PVOID Address,
227 IN PMMPTE PointerPte,
228 IN PMMPTE PointerProtoPte,
229 IN BOOLEAN Recursive,
230 IN PEPROCESS Process,
231 IN PVOID TrapInformation,
232 IN PVOID Vad);
233
234 NTSTATUS
235 NTAPI
236 MiFillSystemPageDirectory(IN PVOID Base,
237 IN SIZE_T NumberOfBytes);
238
239 static PULONG
240 MmGetPageTableForProcess(PEPROCESS Process, PVOID Address, BOOLEAN Create)
241 {
242 PFN_NUMBER Pfn;
243 PULONG Pt;
244 PMMPDE PointerPde;
245
246 if (Address < MmSystemRangeStart)
247 {
248 /* We should have a process for user land addresses */
249 ASSERT(Process != NULL);
250
251 if(Process != PsGetCurrentProcess())
252 {
253 PMMPDE PdeBase;
254 ULONG PdeOffset = MiGetPdeOffset(Address);
255
256 /* Nobody but page fault should ask for creating the PDE,
257 * Which imples that Process is the current one */
258 ASSERT(Create == FALSE);
259
260 PdeBase = MmCreateHyperspaceMapping(PTE_TO_PFN(Process->Pcb.DirectoryTableBase[0]));
261 if (PdeBase == NULL)
262 {
263 KeBugCheck(MEMORY_MANAGEMENT);
264 }
265 PointerPde = PdeBase + PdeOffset;
266 if (PointerPde->u.Hard.Valid == 0)
267 {
268 MmDeleteHyperspaceMapping(PdeBase);
269 return NULL;
270 }
271 else
272 {
273 Pfn = PointerPde->u.Hard.PageFrameNumber;
274 }
275 MmDeleteHyperspaceMapping(PdeBase);
276 Pt = MmCreateHyperspaceMapping(Pfn);
277 if (Pt == NULL)
278 {
279 KeBugCheck(MEMORY_MANAGEMENT);
280 }
281 return Pt + MiAddressToPteOffset(Address);
282 }
283 /* This is for our process */
284 PointerPde = MiAddressToPde(Address);
285 Pt = (PULONG)MiAddressToPte(Address);
286 if (PointerPde->u.Hard.Valid == 0)
287 {
288 NTSTATUS Status;
289 if (Create == FALSE)
290 {
291 return NULL;
292 }
293 ASSERT(PointerPde->u.Long == 0);
294
295 MI_WRITE_INVALID_PTE(PointerPde, DemandZeroPde);
296 Status = MiDispatchFault(TRUE,
297 Pt,
298 PointerPde,
299 NULL,
300 FALSE,
301 PsGetCurrentProcess(),
302 NULL,
303 NULL);
304 DBG_UNREFERENCED_LOCAL_VARIABLE(Status);
305 ASSERT(KeAreAllApcsDisabled() == TRUE);
306 ASSERT(PointerPde->u.Hard.Valid == 1);
307 }
308 return (PULONG)MiAddressToPte(Address);
309 }
310
311 /* This is for kernel land address */
312 ASSERT(Process == NULL);
313 PointerPde = MiAddressToPde(Address);
314 Pt = (PULONG)MiAddressToPte(Address);
315 if (PointerPde->u.Hard.Valid == 0)
316 {
317 /* Let ARM3 synchronize the PDE */
318 if(!MiSynchronizeSystemPde(PointerPde))
319 {
320 /* PDE (still) not valid, let ARM3 allocate one if asked */
321 if(Create == FALSE)
322 return NULL;
323 MiFillSystemPageDirectory(Address, PAGE_SIZE);
324 }
325 }
326 return Pt;
327 }
328
329 static BOOLEAN MmUnmapPageTable(PULONG Pt)
330 {
331 if (!IS_HYPERSPACE(Pt))
332 {
333 return TRUE;
334 }
335
336 if (Pt)
337 {
338 MmDeleteHyperspaceMapping((PVOID)PAGE_ROUND_DOWN(Pt));
339 }
340 return FALSE;
341 }
342
343 static ULONG MmGetPageEntryForProcess(PEPROCESS Process, PVOID Address)
344 {
345 ULONG Pte;
346 PULONG Pt;
347
348 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
349 if (Pt)
350 {
351 Pte = *Pt;
352 MmUnmapPageTable(Pt);
353 return Pte;
354 }
355 return 0;
356 }
357
358 PFN_NUMBER
359 NTAPI
360 MmGetPfnForProcess(PEPROCESS Process,
361 PVOID Address)
362 {
363 ULONG Entry;
364 Entry = MmGetPageEntryForProcess(Process, Address);
365 if (!(Entry & PA_PRESENT))
366 {
367 return 0;
368 }
369 return(PTE_TO_PFN(Entry));
370 }
371
372 VOID
373 NTAPI
374 MmDeleteVirtualMapping(PEPROCESS Process, PVOID Address, BOOLEAN FreePage,
375 BOOLEAN* WasDirty, PPFN_NUMBER Page)
376 /*
377 * FUNCTION: Delete a virtual mapping
378 */
379 {
380 BOOLEAN WasValid = FALSE;
381 PFN_NUMBER Pfn;
382 ULONG Pte;
383 PULONG Pt;
384
385 DPRINT("MmDeleteVirtualMapping(%p, %p, %u, %p, %p)\n",
386 Process, Address, FreePage, WasDirty, Page);
387
388 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
389
390 if (Pt == NULL)
391 {
392 if (WasDirty != NULL)
393 {
394 *WasDirty = FALSE;
395 }
396 if (Page != NULL)
397 {
398 *Page = 0;
399 }
400 return;
401 }
402
403 /*
404 * Atomically set the entry to zero and get the old value.
405 */
406 Pte = InterlockedExchangePte(Pt, 0);
407
408 /* We count a mapping as valid if it's a present page, or it's a nonzero pfn with
409 * the swap bit unset, indicating a valid page protected to PAGE_NOACCESS. */
410 WasValid = (Pte & PA_PRESENT) || ((Pte >> PAGE_SHIFT) && !(Pte & 0x800));
411 if (WasValid)
412 {
413 /* Flush the TLB since we transitioned this PTE
414 * from valid to invalid so any stale translations
415 * are removed from the cache */
416 MiFlushTlb(Pt, Address);
417
418 if (Address < MmSystemRangeStart)
419 {
420 /* Remove PDE reference */
421 Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)]--;
422 ASSERT(Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)] < PTE_COUNT);
423 }
424
425 Pfn = PTE_TO_PFN(Pte);
426
427 if (FreePage)
428 {
429 MmReleasePageMemoryConsumer(MC_SYSTEM, Pfn);
430 Pfn = 0;
431 }
432 }
433 else
434 {
435 MmUnmapPageTable(Pt);
436 Pfn = 0;
437 }
438
439 /*
440 * Return some information to the caller
441 */
442 if (WasDirty != NULL)
443 {
444 *WasDirty = ((Pte & PA_DIRTY) && (Pte & PA_PRESENT)) ? TRUE : FALSE;
445 }
446 if (Page != NULL)
447 {
448 *Page = Pfn;
449 }
450 }
451
452 VOID
453 NTAPI
454 MmGetPageFileMapping(PEPROCESS Process, PVOID Address,
455 SWAPENTRY* SwapEntry)
456 /*
457 * FUNCTION: Get a page file mapping
458 */
459 {
460 ULONG Entry = MmGetPageEntryForProcess(Process, Address);
461 *SwapEntry = Entry >> 1;
462 }
463
464 VOID
465 NTAPI
466 MmDeletePageFileMapping(PEPROCESS Process, PVOID Address,
467 SWAPENTRY* SwapEntry)
468 /*
469 * FUNCTION: Delete a virtual mapping
470 */
471 {
472 ULONG Pte;
473 PULONG Pt;
474
475 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
476
477 if (Pt == NULL)
478 {
479 *SwapEntry = 0;
480 return;
481 }
482
483 /*
484 * Atomically set the entry to zero and get the old value.
485 */
486 Pte = InterlockedExchangePte(Pt, 0);
487
488 if (Address < MmSystemRangeStart)
489 {
490 /* Remove PDE reference */
491 Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)]--;
492 ASSERT(Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)] < PTE_COUNT);
493 }
494
495 /* We don't need to flush here because page file entries
496 * are invalid translations, so the processor won't cache them */
497 MmUnmapPageTable(Pt);
498
499 if ((Pte & PA_PRESENT) || !(Pte & 0x800))
500 {
501 DPRINT1("Pte %x (want not 1 and 0x800)\n", Pte);
502 KeBugCheck(MEMORY_MANAGEMENT);
503 }
504
505 /*
506 * Return some information to the caller
507 */
508 *SwapEntry = Pte >> 1;
509 }
510
511 BOOLEAN
512 Mmi386MakeKernelPageTableGlobal(PVOID Address)
513 {
514 PMMPDE PointerPde = MiAddressToPde(Address);
515 PMMPTE PointerPte = MiAddressToPte(Address);
516
517 if (PointerPde->u.Hard.Valid == 0)
518 {
519 if(!MiSynchronizeSystemPde(PointerPde))
520 return FALSE;
521 return PointerPte->u.Hard.Valid != 0;
522 }
523 return FALSE;
524 }
525
526 BOOLEAN
527 NTAPI
528 MmIsDirtyPage(PEPROCESS Process, PVOID Address)
529 {
530 return MmGetPageEntryForProcess(Process, Address) & PA_DIRTY ? TRUE : FALSE;
531 }
532
533 VOID
534 NTAPI
535 MmSetCleanPage(PEPROCESS Process, PVOID Address)
536 {
537 PULONG Pt;
538 ULONG Pte;
539
540 if (Address < MmSystemRangeStart && Process == NULL)
541 {
542 DPRINT1("MmSetCleanPage is called for user space without a process.\n");
543 KeBugCheck(MEMORY_MANAGEMENT);
544 }
545
546 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
547 if (Pt == NULL)
548 {
549 KeBugCheck(MEMORY_MANAGEMENT);
550 }
551
552 do
553 {
554 Pte = *Pt;
555 } while (Pte != InterlockedCompareExchangePte(Pt, Pte & ~PA_DIRTY, Pte));
556
557 if (!(Pte & PA_PRESENT))
558 {
559 KeBugCheck(MEMORY_MANAGEMENT);
560 }
561 else if (Pte & PA_DIRTY)
562 {
563 MiFlushTlb(Pt, Address);
564 }
565 else
566 {
567 MmUnmapPageTable(Pt);
568 }
569 }
570
571 VOID
572 NTAPI
573 MmSetDirtyPage(PEPROCESS Process, PVOID Address)
574 {
575 PULONG Pt;
576 ULONG Pte;
577
578 if (Address < MmSystemRangeStart && Process == NULL)
579 {
580 DPRINT1("MmSetDirtyPage is called for user space without a process.\n");
581 KeBugCheck(MEMORY_MANAGEMENT);
582 }
583
584 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
585 if (Pt == NULL)
586 {
587 KeBugCheck(MEMORY_MANAGEMENT);
588 }
589
590 do
591 {
592 Pte = *Pt;
593 } while (Pte != InterlockedCompareExchangePte(Pt, Pte | PA_DIRTY, Pte));
594
595 if (!(Pte & PA_PRESENT))
596 {
597 KeBugCheck(MEMORY_MANAGEMENT);
598 }
599 else
600 {
601 /* The processor will never clear this bit itself, therefore
602 * we do not need to flush the TLB here when setting it */
603 MmUnmapPageTable(Pt);
604 }
605 }
606
607 VOID
608 NTAPI
609 MmEnableVirtualMapping(PEPROCESS Process, PVOID Address)
610 {
611 PULONG Pt;
612 ULONG Pte;
613
614 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
615 if (Pt == NULL)
616 {
617 //HACK to get DPH working, waiting for MM rewrite :-/
618 //KeBugCheck(MEMORY_MANAGEMENT);
619 return;
620 }
621
622 /* Do not mark a 0 page as present */
623 if(0 == InterlockedCompareExchangePte(Pt, 0, 0))
624 return;
625
626 do
627 {
628 Pte = *Pt;
629 } while (Pte != InterlockedCompareExchangePte(Pt, Pte | PA_PRESENT, Pte));
630
631 /* We don't need to flush the TLB here because it
632 * won't cache translations for non-present pages */
633 MmUnmapPageTable(Pt);
634 }
635
636 BOOLEAN
637 NTAPI
638 MmIsPagePresent(PEPROCESS Process, PVOID Address)
639 {
640 return MmGetPageEntryForProcess(Process, Address) & PA_PRESENT;
641 }
642
643 BOOLEAN
644 NTAPI
645 MmIsDisabledPage(PEPROCESS Process, PVOID Address)
646 {
647 ULONG_PTR Entry = MmGetPageEntryForProcess(Process, Address);
648 return !(Entry & PA_PRESENT) && !(Entry & 0x800) && (Entry >> PAGE_SHIFT);
649 }
650
651 BOOLEAN
652 NTAPI
653 MmIsPageSwapEntry(PEPROCESS Process, PVOID Address)
654 {
655 ULONG Entry;
656 Entry = MmGetPageEntryForProcess(Process, Address);
657 return !(Entry & PA_PRESENT) && (Entry & 0x800);
658 }
659
660 NTSTATUS
661 NTAPI
662 MmCreatePageFileMapping(PEPROCESS Process,
663 PVOID Address,
664 SWAPENTRY SwapEntry)
665 {
666 PULONG Pt;
667 ULONG Pte;
668
669 if (Process == NULL && Address < MmSystemRangeStart)
670 {
671 DPRINT1("No process\n");
672 KeBugCheck(MEMORY_MANAGEMENT);
673 }
674 if (Process != NULL && Address >= MmSystemRangeStart)
675 {
676 DPRINT1("Setting kernel address with process context\n");
677 KeBugCheck(MEMORY_MANAGEMENT);
678 }
679
680 if (SwapEntry & (1 << 31))
681 {
682 KeBugCheck(MEMORY_MANAGEMENT);
683 }
684
685 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
686 if (Pt == NULL)
687 {
688 /* Nobody should page out an address that hasn't even been mapped */
689 /* But we might place a wait entry first, requiring the page table */
690 if (SwapEntry != MM_WAIT_ENTRY)
691 {
692 KeBugCheck(MEMORY_MANAGEMENT);
693 }
694 Pt = MmGetPageTableForProcess(Process, Address, TRUE);
695 }
696 Pte = InterlockedExchangePte(Pt, SwapEntry << 1);
697 if (Pte != 0)
698 {
699 KeBugCheckEx(MEMORY_MANAGEMENT, SwapEntry, (ULONG_PTR)Process, (ULONG_PTR)Address, 0);
700 }
701
702 if (Address < MmSystemRangeStart)
703 {
704 /* Add PDE reference */
705 Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)]++;
706 ASSERT(Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)] <= PTE_COUNT);
707 }
708
709 /* We don't need to flush the TLB here because it
710 * only caches valid translations and a zero PTE
711 * is not a valid translation */
712 MmUnmapPageTable(Pt);
713
714 return(STATUS_SUCCESS);
715 }
716
717
718 NTSTATUS
719 NTAPI
720 MmCreateVirtualMappingUnsafe(PEPROCESS Process,
721 PVOID Address,
722 ULONG flProtect,
723 PPFN_NUMBER Pages,
724 ULONG PageCount)
725 {
726 ULONG Attributes;
727 PVOID Addr;
728 ULONG i;
729 ULONG oldPdeOffset, PdeOffset;
730 PULONG Pt = NULL;
731 ULONG Pte;
732 DPRINT("MmCreateVirtualMappingUnsafe(%p, %p, %lu, %p (%x), %lu)\n",
733 Process, Address, flProtect, Pages, *Pages, PageCount);
734
735 ASSERT(((ULONG_PTR)Address % PAGE_SIZE) == 0);
736
737 if (Process == NULL)
738 {
739 if (Address < MmSystemRangeStart)
740 {
741 DPRINT1("No process\n");
742 KeBugCheck(MEMORY_MANAGEMENT);
743 }
744 if (PageCount > 0x10000 ||
745 (ULONG_PTR) Address / PAGE_SIZE + PageCount > 0x100000)
746 {
747 DPRINT1("Page count too large\n");
748 KeBugCheck(MEMORY_MANAGEMENT);
749 }
750 }
751 else
752 {
753 if (Address >= MmSystemRangeStart)
754 {
755 DPRINT1("Setting kernel address with process context\n");
756 KeBugCheck(MEMORY_MANAGEMENT);
757 }
758 if (PageCount > (ULONG_PTR)MmSystemRangeStart / PAGE_SIZE ||
759 (ULONG_PTR) Address / PAGE_SIZE + PageCount >
760 (ULONG_PTR)MmSystemRangeStart / PAGE_SIZE)
761 {
762 DPRINT1("Page Count too large\n");
763 KeBugCheck(MEMORY_MANAGEMENT);
764 }
765 }
766
767 Attributes = ProtectToPTE(flProtect);
768 Attributes &= 0xfff;
769 if (Address >= MmSystemRangeStart)
770 {
771 Attributes &= ~PA_USER;
772 }
773 else
774 {
775 Attributes |= PA_USER;
776 }
777
778 Addr = Address;
779 /* MmGetPageTableForProcess should be called on the first run, so
780 * let this trigger it */
781 oldPdeOffset = ADDR_TO_PDE_OFFSET(Addr) + 1;
782 for (i = 0; i < PageCount; i++, Addr = (PVOID)((ULONG_PTR)Addr + PAGE_SIZE))
783 {
784 if (!(Attributes & PA_PRESENT) && Pages[i] != 0)
785 {
786 DPRINT1("Setting physical address but not allowing access at address "
787 "0x%p with attributes %x/%x.\n",
788 Addr, Attributes, flProtect);
789 KeBugCheck(MEMORY_MANAGEMENT);
790 }
791 PdeOffset = ADDR_TO_PDE_OFFSET(Addr);
792 if (oldPdeOffset != PdeOffset)
793 {
794 if(Pt) MmUnmapPageTable(Pt);
795 Pt = MmGetPageTableForProcess(Process, Addr, TRUE);
796 if (Pt == NULL)
797 {
798 KeBugCheck(MEMORY_MANAGEMENT);
799 }
800 }
801 else
802 {
803 Pt++;
804 }
805 oldPdeOffset = PdeOffset;
806
807 Pte = InterlockedExchangePte(Pt, PFN_TO_PTE(Pages[i]) | Attributes);
808
809 /* There should not be anything valid here */
810 if (Pte != 0)
811 {
812 DPRINT1("Bad PTE %lx\n", Pte);
813 KeBugCheck(MEMORY_MANAGEMENT);
814 }
815
816 /* We don't need to flush the TLB here because it only caches valid translations
817 * and we're moving this PTE from invalid to valid so it can't be cached right now */
818
819 if (Addr < MmSystemRangeStart)
820 {
821 /* Add PDE reference */
822 Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Addr)]++;
823 ASSERT(Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Addr)] <= PTE_COUNT);
824 }
825 }
826
827 ASSERT(Addr > Address);
828 MmUnmapPageTable(Pt);
829
830 return(STATUS_SUCCESS);
831 }
832
833 NTSTATUS
834 NTAPI
835 MmCreateVirtualMapping(PEPROCESS Process,
836 PVOID Address,
837 ULONG flProtect,
838 PPFN_NUMBER Pages,
839 ULONG PageCount)
840 {
841 ULONG i;
842
843 ASSERT((ULONG_PTR)Address % PAGE_SIZE == 0);
844 for (i = 0; i < PageCount; i++)
845 {
846 if (!MmIsPageInUse(Pages[i]))
847 {
848 DPRINT1("Page at address %x not in use\n", PFN_TO_PTE(Pages[i]));
849 KeBugCheck(MEMORY_MANAGEMENT);
850 }
851 }
852
853 return(MmCreateVirtualMappingUnsafe(Process,
854 Address,
855 flProtect,
856 Pages,
857 PageCount));
858 }
859
860 ULONG
861 NTAPI
862 MmGetPageProtect(PEPROCESS Process, PVOID Address)
863 {
864 ULONG Entry;
865 ULONG Protect;
866
867 Entry = MmGetPageEntryForProcess(Process, Address);
868
869
870 if (!(Entry & PA_PRESENT))
871 {
872 Protect = PAGE_NOACCESS;
873 }
874 else
875 {
876 if (Entry & PA_READWRITE)
877 {
878 Protect = PAGE_READWRITE;
879 }
880 else
881 {
882 Protect = PAGE_EXECUTE_READ;
883 }
884 if (Entry & PA_CD)
885 {
886 Protect |= PAGE_NOCACHE;
887 }
888 if (Entry & PA_WT)
889 {
890 Protect |= PAGE_WRITETHROUGH;
891 }
892 if (!(Entry & PA_USER))
893 {
894 Protect |= PAGE_SYSTEM;
895 }
896
897 }
898 return(Protect);
899 }
900
901 VOID
902 NTAPI
903 MmSetPageProtect(PEPROCESS Process, PVOID Address, ULONG flProtect)
904 {
905 ULONG Attributes = 0;
906 PULONG Pt;
907 ULONG Pte;
908
909 DPRINT("MmSetPageProtect(Process %p Address %p flProtect %x)\n",
910 Process, Address, flProtect);
911
912 Attributes = ProtectToPTE(flProtect);
913
914 Attributes &= 0xfff;
915 if (Address >= MmSystemRangeStart)
916 {
917 Attributes &= ~PA_USER;
918 }
919 else
920 {
921 Attributes |= PA_USER;
922 }
923
924 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
925 if (Pt == NULL)
926 {
927 KeBugCheck(MEMORY_MANAGEMENT);
928 }
929 Pte = InterlockedExchangePte(Pt, PAGE_MASK(*Pt) | Attributes | (*Pt & (PA_ACCESSED|PA_DIRTY)));
930
931 // We should be able to bring a page back from PAGE_NOACCESS
932 if ((Pte & 0x800) || !(Pte >> PAGE_SHIFT))
933 {
934 DPRINT1("Invalid Pte %lx\n", Pte);
935 KeBugCheck(MEMORY_MANAGEMENT);
936 }
937
938 if((Pte & Attributes) != Attributes)
939 MiFlushTlb(Pt, Address);
940 else
941 MmUnmapPageTable(Pt);
942 }
943
944 VOID
945 INIT_FUNCTION
946 NTAPI
947 MmInitGlobalKernelPageDirectory(VOID)
948 {
949 /* Nothing to do here */
950 }
951
952 /* EOF */