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