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