[CMAKE]
[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 #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 && Process && Process != PsGetCurrentProcess())
213 {
214 PageDir = MmCreateHyperspaceMapping(PTE_TO_PFN(Process->Pcb.DirectoryTableBase[0]));
215 if (PageDir == NULL)
216 {
217 KeBugCheck(MEMORY_MANAGEMENT);
218 }
219 if (0 == InterlockedCompareExchangePte(&PageDir[PdeOffset], 0, 0))
220 {
221 if (Create == FALSE)
222 {
223 MmDeleteHyperspaceMapping(PageDir);
224 return NULL;
225 }
226 MI_SET_USAGE(MI_USAGE_LEGACY_PAGE_DIRECTORY);
227 if (Process) MI_SET_PROCESS2(Process->ImageFileName);
228 if (!Process) MI_SET_PROCESS2("Kernel Legacy");
229 Status = MmRequestPageMemoryConsumer(MC_SYSTEM, FALSE, &Pfn);
230 if (!NT_SUCCESS(Status) || Pfn == 0)
231 {
232 KeBugCheck(MEMORY_MANAGEMENT);
233 }
234 Entry = InterlockedCompareExchangePte(&PageDir[PdeOffset], PFN_TO_PTE(Pfn) | PA_PRESENT | PA_READWRITE | PA_USER, 0);
235 if (Entry != 0)
236 {
237 MmReleasePageMemoryConsumer(MC_SYSTEM, Pfn);
238 Pfn = PTE_TO_PFN(Entry);
239 }
240 }
241 else
242 {
243 Pfn = PTE_TO_PFN(PageDir[PdeOffset]);
244 }
245 MmDeleteHyperspaceMapping(PageDir);
246 Pt = MmCreateHyperspaceMapping(Pfn);
247 if (Pt == NULL)
248 {
249 KeBugCheck(MEMORY_MANAGEMENT);
250 }
251 return Pt + ADDR_TO_PTE_OFFSET(Address);
252 }
253 PageDir = (PULONG)MiAddressToPde(Address);
254 if (0 == InterlockedCompareExchangePte(PageDir, 0, 0))
255 {
256 if (Address >= MmSystemRangeStart)
257 {
258 if (0 == InterlockedCompareExchangePte(&MmGlobalKernelPageDirectory[PdeOffset], 0, 0))
259 {
260 if (Create == FALSE)
261 {
262 return NULL;
263 }
264 MI_SET_USAGE(MI_USAGE_LEGACY_PAGE_DIRECTORY);
265 if (Process) MI_SET_PROCESS2(Process->ImageFileName);
266 if (!Process) MI_SET_PROCESS2("Kernel Legacy");
267 Status = MmRequestPageMemoryConsumer(MC_SYSTEM, FALSE, &Pfn);
268 if (!NT_SUCCESS(Status) || Pfn == 0)
269 {
270 KeBugCheck(MEMORY_MANAGEMENT);
271 }
272 Entry = PFN_TO_PTE(Pfn) | PA_PRESENT | PA_READWRITE;
273 if(0 != InterlockedCompareExchangePte(&MmGlobalKernelPageDirectory[PdeOffset], Entry, 0))
274 {
275 MmReleasePageMemoryConsumer(MC_SYSTEM, Pfn);
276 }
277 InterlockedExchangePte(PageDir, MmGlobalKernelPageDirectory[PdeOffset]);
278 RtlZeroMemory(MiPteToAddress(PageDir), PAGE_SIZE);
279 return (PULONG)MiAddressToPte(Address);
280 }
281 InterlockedExchangePte(PageDir, MmGlobalKernelPageDirectory[PdeOffset]);
282 }
283 else
284 {
285 if (Create == FALSE)
286 {
287 return NULL;
288 }
289 MI_SET_USAGE(MI_USAGE_LEGACY_PAGE_DIRECTORY);
290 if (Process) MI_SET_PROCESS2(Process->ImageFileName);
291 if (!Process) MI_SET_PROCESS2("Kernel Legacy");
292 Status = MmRequestPageMemoryConsumer(MC_SYSTEM, FALSE, &Pfn);
293 if (!NT_SUCCESS(Status) || Pfn == 0)
294 {
295 KeBugCheck(MEMORY_MANAGEMENT);
296 }
297 Entry = InterlockedCompareExchangePte(PageDir, PFN_TO_PTE(Pfn) | PA_PRESENT | PA_READWRITE | PA_USER, 0);
298 if (Entry != 0)
299 {
300 MmReleasePageMemoryConsumer(MC_SYSTEM, Pfn);
301 }
302 }
303 }
304 return (PULONG)MiAddressToPte(Address);
305 }
306
307 BOOLEAN MmUnmapPageTable(PULONG Pt)
308 {
309 if (Pt >= (PULONG)PAGETABLE_MAP && Pt < (PULONG)PAGETABLE_MAP + 1024*1024)
310 {
311 return TRUE;
312 }
313
314 if (Pt)
315 {
316 MmDeleteHyperspaceMapping((PVOID)PAGE_ROUND_DOWN(Pt));
317 }
318 return FALSE;
319 }
320
321 static ULONG MmGetPageEntryForProcess(PEPROCESS Process, PVOID Address)
322 {
323 ULONG Pte;
324 PULONG Pt;
325
326 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
327 if (Pt)
328 {
329 Pte = *Pt;
330 MmUnmapPageTable(Pt);
331 return Pte;
332 }
333 return 0;
334 }
335
336 PFN_NUMBER
337 NTAPI
338 MmGetPfnForProcess(PEPROCESS Process,
339 PVOID Address)
340 {
341 ULONG Entry;
342 Entry = MmGetPageEntryForProcess(Process, Address);
343 if (!(Entry & PA_PRESENT))
344 {
345 return 0;
346 }
347 return(PTE_TO_PFN(Entry));
348 }
349
350 VOID
351 NTAPI
352 MmDisableVirtualMapping(PEPROCESS Process, PVOID Address, BOOLEAN* WasDirty, PPFN_NUMBER Page)
353 /*
354 * FUNCTION: Delete a virtual mapping
355 */
356 {
357 BOOLEAN WasValid;
358 ULONG Pte;
359 PULONG Pt;
360
361 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
362 if (Pt == NULL)
363 {
364 KeBugCheck(MEMORY_MANAGEMENT);
365 }
366 /*
367 * Atomically disable the present bit and get the old value.
368 */
369 do
370 {
371 Pte = *Pt;
372 } while (Pte != InterlockedCompareExchangePte(Pt, Pte & ~PA_PRESENT, Pte));
373
374 MiFlushTlb(Pt, Address);
375 WasValid = (PAGE_MASK(Pte) != 0);
376 if (!WasValid)
377 {
378 KeBugCheck(MEMORY_MANAGEMENT);
379 }
380
381 /*
382 * Return some information to the caller
383 */
384 if (WasDirty != NULL)
385 {
386 *WasDirty = Pte & PA_DIRTY;
387 }
388 if (Page != NULL)
389 {
390 *Page = PTE_TO_PFN(Pte);
391 }
392 }
393
394 VOID
395 NTAPI
396 MmRawDeleteVirtualMapping(PVOID Address)
397 {
398 PULONG Pt;
399
400 Pt = MmGetPageTableForProcess(NULL, Address, FALSE);
401 if (Pt && *Pt)
402 {
403 /*
404 * Set the entry to zero
405 */
406 InterlockedExchangePte(Pt, 0);
407 MiFlushTlb(Pt, Address);
408 }
409 }
410
411 VOID
412 NTAPI
413 MmDeleteVirtualMapping(PEPROCESS Process, PVOID Address, BOOLEAN FreePage,
414 BOOLEAN* WasDirty, PPFN_NUMBER Page)
415 /*
416 * FUNCTION: Delete a virtual mapping
417 */
418 {
419 BOOLEAN WasValid = FALSE;
420 PFN_NUMBER Pfn;
421 ULONG Pte;
422 PULONG Pt;
423
424 DPRINT("MmDeleteVirtualMapping(%x, %x, %d, %x, %x)\n",
425 Process, Address, FreePage, WasDirty, Page);
426
427 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
428
429 if (Pt == NULL)
430 {
431 if (WasDirty != NULL)
432 {
433 *WasDirty = FALSE;
434 }
435 if (Page != NULL)
436 {
437 *Page = 0;
438 }
439 return;
440 }
441
442 /*
443 * Atomically set the entry to zero and get the old value.
444 */
445 Pte = InterlockedExchangePte(Pt, 0);
446
447 MiFlushTlb(Pt, Address);
448
449 WasValid = (PAGE_MASK(Pte) != 0);
450 if (WasValid)
451 {
452 Pfn = PTE_TO_PFN(Pte);
453 }
454 else
455 {
456 Pfn = 0;
457 }
458
459 if (FreePage && WasValid)
460 {
461 MmReleasePageMemoryConsumer(MC_SYSTEM, Pfn);
462 }
463
464 /*
465 * Return some information to the caller
466 */
467 if (WasDirty != NULL)
468 {
469 *WasDirty = Pte & PA_DIRTY ? TRUE : FALSE;
470 }
471 if (Page != NULL)
472 {
473 *Page = Pfn;
474 }
475 }
476
477 VOID
478 NTAPI
479 MmGetPageFileMapping(PEPROCESS Process, PVOID Address,
480 SWAPENTRY* SwapEntry)
481 /*
482 * FUNCTION: Get a page file mapping
483 */
484 {
485 ULONG Entry = MmGetPageEntryForProcess(Process, Address);
486 *SwapEntry = Entry >> 1;
487 }
488
489 VOID
490 NTAPI
491 MmDeletePageFileMapping(PEPROCESS Process, PVOID Address,
492 SWAPENTRY* SwapEntry)
493 /*
494 * FUNCTION: Delete a virtual mapping
495 */
496 {
497 ULONG Pte;
498 PULONG Pt;
499
500 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
501
502 if (Pt == NULL)
503 {
504 *SwapEntry = 0;
505 return;
506 }
507
508 /*
509 * Atomically set the entry to zero and get the old value.
510 */
511 Pte = InterlockedExchangePte(Pt, 0);
512
513 MiFlushTlb(Pt, Address);
514
515 /*
516 * Return some information to the caller
517 */
518 *SwapEntry = Pte >> 1;
519 }
520
521 BOOLEAN
522 Mmi386MakeKernelPageTableGlobal(PVOID PAddress)
523 {
524 PULONG Pt, Pde;
525 Pde = (PULONG)MiAddressToPde(PAddress);
526 if (*Pde == 0)
527 {
528 Pt = MmGetPageTableForProcess(NULL, PAddress, FALSE);
529 if (Pt != NULL)
530 {
531 return TRUE;
532 }
533 }
534 return(FALSE);
535 }
536
537 BOOLEAN
538 NTAPI
539 MmIsDirtyPage(PEPROCESS Process, PVOID Address)
540 {
541 return MmGetPageEntryForProcess(Process, Address) & PA_DIRTY ? TRUE : FALSE;
542 }
543
544 VOID
545 NTAPI
546 MmSetCleanPage(PEPROCESS Process, PVOID Address)
547 {
548 PULONG Pt;
549 ULONG Pte;
550
551 if (Address < MmSystemRangeStart && Process == NULL)
552 {
553 DPRINT1("MmSetCleanPage is called for user space without a process.\n");
554 KeBugCheck(MEMORY_MANAGEMENT);
555 }
556
557 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
558
559 if (Pt == NULL)
560 {
561 KeBugCheck(MEMORY_MANAGEMENT);
562 }
563
564 do
565 {
566 Pte = *Pt;
567 } while (Pte != InterlockedCompareExchangePte(Pt, Pte & ~PA_DIRTY, Pte));
568
569 if (Pte & PA_DIRTY)
570 {
571 MiFlushTlb(Pt, Address);
572 }
573 else
574 {
575 MmUnmapPageTable(Pt);
576 }
577 }
578
579 VOID
580 NTAPI
581 MmSetDirtyPage(PEPROCESS Process, PVOID Address)
582 {
583 PULONG Pt;
584 ULONG Pte;
585
586 if (Address < MmSystemRangeStart && Process == NULL)
587 {
588 DPRINT1("MmSetDirtyPage is called for user space without a process.\n");
589 KeBugCheck(MEMORY_MANAGEMENT);
590 }
591
592 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
593 if (Pt == NULL)
594 {
595 KeBugCheck(MEMORY_MANAGEMENT);
596 }
597
598 do
599 {
600 Pte = *Pt;
601 } while (Pte != InterlockedCompareExchangePte(Pt, Pte | PA_DIRTY, Pte));
602 if (!(Pte & PA_DIRTY))
603 {
604 MiFlushTlb(Pt, Address);
605 }
606 else
607 {
608 MmUnmapPageTable(Pt);
609 }
610 }
611
612 VOID
613 NTAPI
614 MmEnableVirtualMapping(PEPROCESS Process, PVOID Address)
615 {
616 PULONG Pt;
617 ULONG Pte;
618
619 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
620 if (Pt == NULL)
621 {
622 KeBugCheck(MEMORY_MANAGEMENT);
623 }
624
625 do
626 {
627 Pte = *Pt;
628 } while (Pte != InterlockedCompareExchangePte(Pt, Pte | PA_PRESENT, Pte));
629 if (!(Pte & PA_PRESENT))
630 {
631 MiFlushTlb(Pt, Address);
632 }
633 else
634 {
635 MmUnmapPageTable(Pt);
636 }
637 }
638
639 BOOLEAN
640 NTAPI
641 MmIsPagePresent(PEPROCESS Process, PVOID Address)
642 {
643 return MmGetPageEntryForProcess(Process, Address) & PA_PRESENT;
644 }
645
646 BOOLEAN
647 NTAPI
648 MmIsPageSwapEntry(PEPROCESS Process, PVOID Address)
649 {
650 ULONG Entry;
651 Entry = MmGetPageEntryForProcess(Process, Address);
652 return !(Entry & PA_PRESENT) && (Entry & 0x800) && Entry != 0;
653 }
654
655 NTSTATUS
656 NTAPI
657 MmCreatePageFileMapping(PEPROCESS Process,
658 PVOID Address,
659 SWAPENTRY SwapEntry)
660 {
661 PULONG Pt;
662 ULONG Pte;
663
664 if (Process == NULL && Address < MmSystemRangeStart)
665 {
666 DPRINT1("No process\n");
667 KeBugCheck(MEMORY_MANAGEMENT);
668 }
669 if (Process != NULL && Address >= MmSystemRangeStart)
670 {
671 DPRINT1("Setting kernel address with process context\n");
672 KeBugCheck(MEMORY_MANAGEMENT);
673 }
674
675 if (SwapEntry & (1 << 31))
676 {
677 KeBugCheck(MEMORY_MANAGEMENT);
678 }
679
680 Pt = MmGetPageTableForProcess(Process, Address, TRUE);
681 if (Pt == NULL)
682 {
683 KeBugCheck(MEMORY_MANAGEMENT);
684 }
685 Pte = *Pt;
686 InterlockedExchangePte(Pt, SwapEntry << 1);
687 if (Pte != 0)
688 {
689 MiFlushTlb(Pt, Address);
690 }
691 else
692 {
693 MmUnmapPageTable(Pt);
694 }
695
696 return(STATUS_SUCCESS);
697 }
698
699
700 NTSTATUS
701 NTAPI
702 MmCreateVirtualMappingUnsafe(PEPROCESS Process,
703 PVOID Address,
704 ULONG flProtect,
705 PPFN_NUMBER Pages,
706 ULONG PageCount)
707 {
708 ULONG Attributes;
709 PVOID Addr;
710 ULONG i;
711 ULONG oldPdeOffset, PdeOffset;
712 PULONG Pt = NULL;
713 ULONG Pte;
714 DPRINT("MmCreateVirtualMappingUnsafe(%x, %x, %x, %x (%x), %d)\n",
715 Process, Address, flProtect, Pages, *Pages, PageCount);
716
717 if (Process == NULL)
718 {
719 if (Address < MmSystemRangeStart)
720 {
721 DPRINT1("No process\n");
722 KeBugCheck(MEMORY_MANAGEMENT);
723 }
724 if (PageCount > 0x10000 ||
725 (ULONG_PTR) Address / PAGE_SIZE + PageCount > 0x100000)
726 {
727 DPRINT1("Page count too large\n");
728 KeBugCheck(MEMORY_MANAGEMENT);
729 }
730 }
731 else
732 {
733 if (Address >= MmSystemRangeStart)
734 {
735 DPRINT1("Setting kernel address with process context\n");
736 KeBugCheck(MEMORY_MANAGEMENT);
737 }
738 if (PageCount > (ULONG_PTR)MmSystemRangeStart / PAGE_SIZE ||
739 (ULONG_PTR) Address / PAGE_SIZE + PageCount >
740 (ULONG_PTR)MmSystemRangeStart / PAGE_SIZE)
741 {
742 DPRINT1("Page Count too large\n");
743 KeBugCheck(MEMORY_MANAGEMENT);
744 }
745 }
746
747 Attributes = ProtectToPTE(flProtect);
748 Attributes &= 0xfff;
749 if (Address >= MmSystemRangeStart)
750 {
751 Attributes &= ~PA_USER;
752 }
753 else
754 {
755 Attributes |= PA_USER;
756 }
757
758 Addr = Address;
759 oldPdeOffset = ADDR_TO_PDE_OFFSET(Addr) + 1;
760 for (i = 0; i < PageCount; i++, Addr = (PVOID)((ULONG_PTR)Addr + PAGE_SIZE))
761 {
762 if (!(Attributes & PA_PRESENT) && Pages[i] != 0)
763 {
764 DPRINT1("Setting physical address but not allowing access at address "
765 "0x%.8X with attributes %x/%x.\n",
766 Addr, Attributes, flProtect);
767 KeBugCheck(MEMORY_MANAGEMENT);
768 }
769 PdeOffset = ADDR_TO_PDE_OFFSET(Addr);
770 if (oldPdeOffset != PdeOffset)
771 {
772 MmUnmapPageTable(Pt);
773 Pt = MmGetPageTableForProcess(Process, Addr, TRUE);
774 if (Pt == NULL)
775 {
776 KeBugCheck(MEMORY_MANAGEMENT);
777 }
778 }
779 else
780 {
781 Pt++;
782 }
783 oldPdeOffset = PdeOffset;
784
785 Pte = *Pt;
786 if (PAGE_MASK(Pte) != 0 && !(Pte & PA_PRESENT) && (Pte & 0x800))
787 {
788 DPRINT1("Bad PTE %lx\n", Pte);
789 KeBugCheck(MEMORY_MANAGEMENT);
790 }
791 InterlockedExchangePte(Pt, PFN_TO_PTE(Pages[i]) | Attributes);
792 if (Pte != 0)
793 {
794 if (Address > MmSystemRangeStart ||
795 (Pt >= (PULONG)PAGETABLE_MAP && Pt < (PULONG)PAGETABLE_MAP + 1024*1024))
796 {
797 MiFlushTlb(Pt, Address);
798 }
799 }
800 }
801 if (Addr > Address)
802 {
803 MmUnmapPageTable(Pt);
804 }
805
806 return(STATUS_SUCCESS);
807 }
808
809 NTSTATUS
810 NTAPI
811 MmCreateVirtualMapping(PEPROCESS Process,
812 PVOID Address,
813 ULONG flProtect,
814 PPFN_NUMBER Pages,
815 ULONG PageCount)
816 {
817 ULONG i;
818
819 for (i = 0; i < PageCount; i++)
820 {
821 if (!MmIsPageInUse(Pages[i]))
822 {
823 DPRINT1("Page at address %x not in use\n", PFN_TO_PTE(Pages[i]));
824 KeBugCheck(MEMORY_MANAGEMENT);
825 }
826 }
827
828 return(MmCreateVirtualMappingUnsafe(Process,
829 Address,
830 flProtect,
831 Pages,
832 PageCount));
833 }
834
835 ULONG
836 NTAPI
837 MmGetPageProtect(PEPROCESS Process, PVOID Address)
838 {
839 ULONG Entry;
840 ULONG Protect;
841
842 Entry = MmGetPageEntryForProcess(Process, Address);
843
844
845 if (!(Entry & PA_PRESENT))
846 {
847 Protect = PAGE_NOACCESS;
848 }
849 else
850 {
851 if (Entry & PA_READWRITE)
852 {
853 Protect = PAGE_READWRITE;
854 }
855 else
856 {
857 Protect = PAGE_EXECUTE_READ;
858 }
859 if (Entry & PA_CD)
860 {
861 Protect |= PAGE_NOCACHE;
862 }
863 if (Entry & PA_WT)
864 {
865 Protect |= PAGE_WRITETHROUGH;
866 }
867 if (!(Entry & PA_USER))
868 {
869 Protect |= PAGE_SYSTEM;
870 }
871
872 }
873 return(Protect);
874 }
875
876 VOID
877 NTAPI
878 MmSetPageProtect(PEPROCESS Process, PVOID Address, ULONG flProtect)
879 {
880 ULONG Attributes = 0;
881 PULONG Pt;
882
883 DPRINT("MmSetPageProtect(Process %x Address %x flProtect %x)\n",
884 Process, Address, flProtect);
885
886 Attributes = ProtectToPTE(flProtect);
887
888 Attributes &= 0xfff;
889 if (Address >= MmSystemRangeStart)
890 {
891 Attributes &= ~PA_USER;
892 }
893 else
894 {
895 Attributes |= PA_USER;
896 }
897
898 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
899 if (Pt == NULL)
900 {
901 KeBugCheck(MEMORY_MANAGEMENT);
902 }
903 InterlockedExchangePte(Pt, PAGE_MASK(*Pt) | Attributes | (*Pt & (PA_ACCESSED|PA_DIRTY)));
904 MiFlushTlb(Pt, Address);
905 }
906
907 /*
908 * @implemented
909 */
910 PHYSICAL_ADDRESS NTAPI
911 MmGetPhysicalAddress(PVOID vaddr)
912 /*
913 * FUNCTION: Returns the physical address corresponding to a virtual address
914 */
915 {
916 PHYSICAL_ADDRESS p;
917 ULONG Pte;
918
919 DPRINT("MmGetPhysicalAddress(vaddr %x)\n", vaddr);
920 Pte = MmGetPageEntryForProcess(NULL, vaddr);
921 if (Pte != 0 && Pte & PA_PRESENT)
922 {
923 p.QuadPart = PAGE_MASK(Pte);
924 p.u.LowPart |= (ULONG_PTR)vaddr & (PAGE_SIZE - 1);
925 }
926 else
927 {
928 p.QuadPart = 0;
929 }
930 return p;
931 }
932
933 VOID
934 INIT_FUNCTION
935 NTAPI
936 MmInitGlobalKernelPageDirectory(VOID)
937 {
938 ULONG i;
939 PULONG CurrentPageDirectory = (PULONG)PAGEDIRECTORY_MAP;
940
941 DPRINT("MmInitGlobalKernelPageDirectory()\n");
942
943 for (i = ADDR_TO_PDE_OFFSET(MmSystemRangeStart); i < 1024; i++)
944 {
945 if (i != ADDR_TO_PDE_OFFSET(PAGETABLE_MAP) &&
946 i != ADDR_TO_PDE_OFFSET(HYPERSPACE) &&
947 0 == MmGlobalKernelPageDirectory[i] && 0 != CurrentPageDirectory[i])
948 {
949 MmGlobalKernelPageDirectory[i] = CurrentPageDirectory[i];
950 }
951 }
952 }
953
954 /* EOF */