* Sync up to trunk head (r65353).
[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,
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, %p, %p)\n",
373 Process, Address, 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 else
415 {
416 MmUnmapPageTable(Pt);
417 Pfn = 0;
418 }
419
420 /*
421 * Return some information to the caller
422 */
423 if (WasDirty != NULL)
424 {
425 *WasDirty = ((Pte & PA_DIRTY) && (Pte & PA_PRESENT)) ? TRUE : FALSE;
426 }
427 if (Page != NULL)
428 {
429 *Page = Pfn;
430 }
431 }
432
433 VOID
434 NTAPI
435 MmGetPageFileMapping(PEPROCESS Process, PVOID Address,
436 SWAPENTRY* SwapEntry)
437 /*
438 * FUNCTION: Get a page file mapping
439 */
440 {
441 ULONG Entry = MmGetPageEntryForProcess(Process, Address);
442 *SwapEntry = Entry >> 1;
443 }
444
445 VOID
446 NTAPI
447 MmDeletePageFileMapping(PEPROCESS Process, PVOID Address,
448 SWAPENTRY* SwapEntry)
449 /*
450 * FUNCTION: Delete a virtual mapping
451 */
452 {
453 ULONG Pte;
454 PULONG Pt;
455
456 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
457
458 if (Pt == NULL)
459 {
460 *SwapEntry = 0;
461 return;
462 }
463
464 /*
465 * Atomically set the entry to zero and get the old value.
466 */
467 Pte = InterlockedExchangePte(Pt, 0);
468
469 if (Address < MmSystemRangeStart)
470 {
471 /* Remove PDE reference */
472 Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)]--;
473 ASSERT(Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)] < PTE_COUNT);
474 }
475
476 /* We don't need to flush here because page file entries
477 * are invalid translations, so the processor won't cache them */
478 MmUnmapPageTable(Pt);
479
480 if ((Pte & PA_PRESENT) || !(Pte & 0x800))
481 {
482 DPRINT1("Pte %x (want not 1 and 0x800)\n", Pte);
483 KeBugCheck(MEMORY_MANAGEMENT);
484 }
485
486 /*
487 * Return some information to the caller
488 */
489 *SwapEntry = Pte >> 1;
490 }
491
492 BOOLEAN
493 Mmi386MakeKernelPageTableGlobal(PVOID Address)
494 {
495 PMMPDE PointerPde = MiAddressToPde(Address);
496 PMMPTE PointerPte = MiAddressToPte(Address);
497
498 if (PointerPde->u.Hard.Valid == 0)
499 {
500 if(!MiSynchronizeSystemPde(PointerPde))
501 return FALSE;
502 return PointerPte->u.Hard.Valid != 0;
503 }
504 return FALSE;
505 }
506
507 BOOLEAN
508 NTAPI
509 MmIsDirtyPage(PEPROCESS Process, PVOID Address)
510 {
511 return MmGetPageEntryForProcess(Process, Address) & PA_DIRTY ? TRUE : FALSE;
512 }
513
514 VOID
515 NTAPI
516 MmSetCleanPage(PEPROCESS Process, PVOID Address)
517 {
518 PULONG Pt;
519 ULONG Pte;
520
521 if (Address < MmSystemRangeStart && Process == NULL)
522 {
523 DPRINT1("MmSetCleanPage is called for user space without a process.\n");
524 KeBugCheck(MEMORY_MANAGEMENT);
525 }
526
527 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
528 if (Pt == NULL)
529 {
530 KeBugCheck(MEMORY_MANAGEMENT);
531 }
532
533 do
534 {
535 Pte = *Pt;
536 } while (Pte != InterlockedCompareExchangePte(Pt, Pte & ~PA_DIRTY, Pte));
537
538 if (!(Pte & PA_PRESENT))
539 {
540 KeBugCheck(MEMORY_MANAGEMENT);
541 }
542 else if (Pte & PA_DIRTY)
543 {
544 MiFlushTlb(Pt, Address);
545 }
546 else
547 {
548 MmUnmapPageTable(Pt);
549 }
550 }
551
552 VOID
553 NTAPI
554 MmSetDirtyPage(PEPROCESS Process, PVOID Address)
555 {
556 PULONG Pt;
557 ULONG Pte;
558
559 if (Address < MmSystemRangeStart && Process == NULL)
560 {
561 DPRINT1("MmSetDirtyPage is called for user space without a process.\n");
562 KeBugCheck(MEMORY_MANAGEMENT);
563 }
564
565 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
566 if (Pt == NULL)
567 {
568 KeBugCheck(MEMORY_MANAGEMENT);
569 }
570
571 do
572 {
573 Pte = *Pt;
574 } while (Pte != InterlockedCompareExchangePte(Pt, Pte | PA_DIRTY, Pte));
575
576 if (!(Pte & PA_PRESENT))
577 {
578 KeBugCheck(MEMORY_MANAGEMENT);
579 }
580 else
581 {
582 /* The processor will never clear this bit itself, therefore
583 * we do not need to flush the TLB here when setting it */
584 MmUnmapPageTable(Pt);
585 }
586 }
587
588 BOOLEAN
589 NTAPI
590 MmIsPagePresent(PEPROCESS Process, PVOID Address)
591 {
592 return MmGetPageEntryForProcess(Process, Address) & PA_PRESENT;
593 }
594
595 BOOLEAN
596 NTAPI
597 MmIsDisabledPage(PEPROCESS Process, PVOID Address)
598 {
599 ULONG_PTR Entry = MmGetPageEntryForProcess(Process, Address);
600 return !(Entry & PA_PRESENT) && !(Entry & 0x800) && (Entry >> PAGE_SHIFT);
601 }
602
603 BOOLEAN
604 NTAPI
605 MmIsPageSwapEntry(PEPROCESS Process, PVOID Address)
606 {
607 ULONG Entry;
608 Entry = MmGetPageEntryForProcess(Process, Address);
609 return !(Entry & PA_PRESENT) && (Entry & 0x800);
610 }
611
612 NTSTATUS
613 NTAPI
614 MmCreatePageFileMapping(PEPROCESS Process,
615 PVOID Address,
616 SWAPENTRY SwapEntry)
617 {
618 PULONG Pt;
619 ULONG Pte;
620
621 if (Process == NULL && Address < MmSystemRangeStart)
622 {
623 DPRINT1("No process\n");
624 KeBugCheck(MEMORY_MANAGEMENT);
625 }
626 if (Process != NULL && Address >= MmSystemRangeStart)
627 {
628 DPRINT1("Setting kernel address with process context\n");
629 KeBugCheck(MEMORY_MANAGEMENT);
630 }
631
632 if (SwapEntry & (1 << 31))
633 {
634 KeBugCheck(MEMORY_MANAGEMENT);
635 }
636
637 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
638 if (Pt == NULL)
639 {
640 /* Nobody should page out an address that hasn't even been mapped */
641 /* But we might place a wait entry first, requiring the page table */
642 if (SwapEntry != MM_WAIT_ENTRY)
643 {
644 KeBugCheck(MEMORY_MANAGEMENT);
645 }
646 Pt = MmGetPageTableForProcess(Process, Address, TRUE);
647 }
648 Pte = InterlockedExchangePte(Pt, SwapEntry << 1);
649 if (Pte != 0)
650 {
651 KeBugCheckEx(MEMORY_MANAGEMENT, SwapEntry, (ULONG_PTR)Process, (ULONG_PTR)Address, 0);
652 }
653
654 if (Address < MmSystemRangeStart)
655 {
656 /* Add PDE reference */
657 Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)]++;
658 ASSERT(Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)] <= PTE_COUNT);
659 }
660
661 /* We don't need to flush the TLB here because it
662 * only caches valid translations and a zero PTE
663 * is not a valid translation */
664 MmUnmapPageTable(Pt);
665
666 return(STATUS_SUCCESS);
667 }
668
669
670 NTSTATUS
671 NTAPI
672 MmCreateVirtualMappingUnsafe(PEPROCESS Process,
673 PVOID Address,
674 ULONG flProtect,
675 PPFN_NUMBER Pages,
676 ULONG PageCount)
677 {
678 ULONG Attributes;
679 PVOID Addr;
680 ULONG i;
681 ULONG oldPdeOffset, PdeOffset;
682 PULONG Pt = NULL;
683 ULONG Pte;
684 DPRINT("MmCreateVirtualMappingUnsafe(%p, %p, %lu, %p (%x), %lu)\n",
685 Process, Address, flProtect, Pages, *Pages, PageCount);
686
687 ASSERT(((ULONG_PTR)Address % PAGE_SIZE) == 0);
688
689 if (Process == NULL)
690 {
691 if (Address < MmSystemRangeStart)
692 {
693 DPRINT1("No process\n");
694 KeBugCheck(MEMORY_MANAGEMENT);
695 }
696 if (PageCount > 0x10000 ||
697 (ULONG_PTR) Address / PAGE_SIZE + PageCount > 0x100000)
698 {
699 DPRINT1("Page count too large\n");
700 KeBugCheck(MEMORY_MANAGEMENT);
701 }
702 }
703 else
704 {
705 if (Address >= MmSystemRangeStart)
706 {
707 DPRINT1("Setting kernel address with process context\n");
708 KeBugCheck(MEMORY_MANAGEMENT);
709 }
710 if (PageCount > (ULONG_PTR)MmSystemRangeStart / PAGE_SIZE ||
711 (ULONG_PTR) Address / PAGE_SIZE + PageCount >
712 (ULONG_PTR)MmSystemRangeStart / PAGE_SIZE)
713 {
714 DPRINT1("Page Count too large\n");
715 KeBugCheck(MEMORY_MANAGEMENT);
716 }
717 }
718
719 Attributes = ProtectToPTE(flProtect);
720 Attributes &= 0xfff;
721 if (Address >= MmSystemRangeStart)
722 {
723 Attributes &= ~PA_USER;
724 }
725 else
726 {
727 Attributes |= PA_USER;
728 }
729
730 Addr = Address;
731 /* MmGetPageTableForProcess should be called on the first run, so
732 * let this trigger it */
733 oldPdeOffset = ADDR_TO_PDE_OFFSET(Addr) + 1;
734 for (i = 0; i < PageCount; i++, Addr = (PVOID)((ULONG_PTR)Addr + PAGE_SIZE))
735 {
736 if (!(Attributes & PA_PRESENT) && Pages[i] != 0)
737 {
738 DPRINT1("Setting physical address but not allowing access at address "
739 "0x%p with attributes %x/%x.\n",
740 Addr, Attributes, flProtect);
741 KeBugCheck(MEMORY_MANAGEMENT);
742 }
743 PdeOffset = ADDR_TO_PDE_OFFSET(Addr);
744 if (oldPdeOffset != PdeOffset)
745 {
746 if(Pt) MmUnmapPageTable(Pt);
747 Pt = MmGetPageTableForProcess(Process, Addr, TRUE);
748 if (Pt == NULL)
749 {
750 KeBugCheck(MEMORY_MANAGEMENT);
751 }
752 }
753 else
754 {
755 Pt++;
756 }
757 oldPdeOffset = PdeOffset;
758
759 Pte = InterlockedExchangePte(Pt, PFN_TO_PTE(Pages[i]) | Attributes);
760
761 /* There should not be anything valid here */
762 if (Pte != 0)
763 {
764 DPRINT1("Bad PTE %lx\n", Pte);
765 KeBugCheck(MEMORY_MANAGEMENT);
766 }
767
768 /* We don't need to flush the TLB here because it only caches valid translations
769 * and we're moving this PTE from invalid to valid so it can't be cached right now */
770
771 if (Addr < MmSystemRangeStart)
772 {
773 /* Add PDE reference */
774 Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Addr)]++;
775 ASSERT(Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Addr)] <= PTE_COUNT);
776 }
777 }
778
779 ASSERT(Addr > Address);
780 MmUnmapPageTable(Pt);
781
782 return(STATUS_SUCCESS);
783 }
784
785 NTSTATUS
786 NTAPI
787 MmCreateVirtualMapping(PEPROCESS Process,
788 PVOID Address,
789 ULONG flProtect,
790 PPFN_NUMBER Pages,
791 ULONG PageCount)
792 {
793 ULONG i;
794
795 ASSERT((ULONG_PTR)Address % PAGE_SIZE == 0);
796 for (i = 0; i < PageCount; i++)
797 {
798 if (!MmIsPageInUse(Pages[i]))
799 {
800 DPRINT1("Page at address %x not in use\n", PFN_TO_PTE(Pages[i]));
801 KeBugCheck(MEMORY_MANAGEMENT);
802 }
803 }
804
805 return(MmCreateVirtualMappingUnsafe(Process,
806 Address,
807 flProtect,
808 Pages,
809 PageCount));
810 }
811
812 ULONG
813 NTAPI
814 MmGetPageProtect(PEPROCESS Process, PVOID Address)
815 {
816 ULONG Entry;
817 ULONG Protect;
818
819 Entry = MmGetPageEntryForProcess(Process, Address);
820
821
822 if (!(Entry & PA_PRESENT))
823 {
824 Protect = PAGE_NOACCESS;
825 }
826 else
827 {
828 if (Entry & PA_READWRITE)
829 {
830 Protect = PAGE_READWRITE;
831 }
832 else
833 {
834 Protect = PAGE_EXECUTE_READ;
835 }
836 if (Entry & PA_CD)
837 {
838 Protect |= PAGE_NOCACHE;
839 }
840 if (Entry & PA_WT)
841 {
842 Protect |= PAGE_WRITETHROUGH;
843 }
844 if (!(Entry & PA_USER))
845 {
846 Protect |= PAGE_SYSTEM;
847 }
848
849 }
850 return(Protect);
851 }
852
853 VOID
854 NTAPI
855 MmSetPageProtect(PEPROCESS Process, PVOID Address, ULONG flProtect)
856 {
857 ULONG Attributes = 0;
858 PULONG Pt;
859 ULONG Pte;
860
861 DPRINT("MmSetPageProtect(Process %p Address %p flProtect %x)\n",
862 Process, Address, flProtect);
863
864 Attributes = ProtectToPTE(flProtect);
865
866 Attributes &= 0xfff;
867 if (Address >= MmSystemRangeStart)
868 {
869 Attributes &= ~PA_USER;
870 }
871 else
872 {
873 Attributes |= PA_USER;
874 }
875
876 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
877 if (Pt == NULL)
878 {
879 KeBugCheck(MEMORY_MANAGEMENT);
880 }
881 Pte = InterlockedExchangePte(Pt, PAGE_MASK(*Pt) | Attributes | (*Pt & (PA_ACCESSED|PA_DIRTY)));
882
883 // We should be able to bring a page back from PAGE_NOACCESS
884 if ((Pte & 0x800) || !(Pte >> PAGE_SHIFT))
885 {
886 DPRINT1("Invalid Pte %lx\n", Pte);
887 KeBugCheck(MEMORY_MANAGEMENT);
888 }
889
890 if((Pte & Attributes) != Attributes)
891 MiFlushTlb(Pt, Address);
892 else
893 MmUnmapPageTable(Pt);
894 }
895
896 VOID
897 INIT_FUNCTION
898 NTAPI
899 MmInitGlobalKernelPageDirectory(VOID)
900 {
901 /* Nothing to do here */
902 }
903
904 /* EOF */