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