- Use MmCreateHyperspaceMapping() again.
[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
16 #if defined (ALLOC_PRAGMA)
17 #pragma alloc_text(INIT, MmInitGlobalKernelPageDirectory)
18 #pragma alloc_text(INIT, MiInitPageDirectoryMap)
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 HYPERSPACE (0xc0400000)
43 #define IS_HYPERSPACE(v) (((ULONG)(v) >= HYPERSPACE && (ULONG)(v) < HYPERSPACE + 0x400000))
44
45 ULONG MmGlobalKernelPageDirectory[1024];
46
47 #define PTE_TO_PFN(X) ((X) >> PAGE_SHIFT)
48 #define PFN_TO_PTE(X) ((X) << PAGE_SHIFT)
49
50 #if defined(__GNUC__)
51 #define PTE_TO_PAGE(X) ((LARGE_INTEGER)(LONGLONG)(PAGE_MASK(X)))
52 #else
53 __inline LARGE_INTEGER PTE_TO_PAGE(ULONG npage)
54 {
55 LARGE_INTEGER dummy;
56 dummy.QuadPart = (LONGLONG)(PAGE_MASK(npage));
57 return dummy;
58 }
59 #endif
60
61 /* FUNCTIONS ***************************************************************/
62
63 BOOLEAN MmUnmapPageTable(PULONG Pt);
64
65 VOID
66 MiFlushTlb(PULONG Pt, PVOID Address)
67 {
68 if ((Pt && MmUnmapPageTable(Pt)) || Address >= MmSystemRangeStart)
69 {
70 __invlpg(Address);
71 }
72 }
73
74 PULONG
75 MmGetPageDirectory(VOID)
76 {
77 return (PULONG)(ULONG_PTR)__readcr3();
78 }
79
80 static ULONG
81 ProtectToPTE(ULONG flProtect)
82 {
83 ULONG Attributes = 0;
84
85 if (flProtect & (PAGE_NOACCESS|PAGE_GUARD))
86 {
87 Attributes = 0;
88 }
89 else if (flProtect & PAGE_IS_WRITABLE)
90 {
91 Attributes = PA_PRESENT | PA_READWRITE;
92 }
93 else if (flProtect & (PAGE_IS_READABLE | PAGE_IS_EXECUTABLE))
94 {
95 Attributes = PA_PRESENT;
96 }
97 else
98 {
99 DPRINT1("Unknown main protection type.\n");
100 KeBugCheck(MEMORY_MANAGEMENT);
101 }
102
103 if (flProtect & PAGE_SYSTEM)
104 {
105 }
106 else
107 {
108 Attributes = Attributes | PA_USER;
109 }
110 if (flProtect & PAGE_NOCACHE)
111 {
112 Attributes = Attributes | PA_CD;
113 }
114 if (flProtect & PAGE_WRITETHROUGH)
115 {
116 Attributes = Attributes | PA_WT;
117 }
118 return(Attributes);
119 }
120
121 NTSTATUS
122 NTAPI
123 Mmi386ReleaseMmInfo(PEPROCESS Process)
124 {
125 PUSHORT LdtDescriptor;
126 ULONG LdtBase;
127 PULONG PageDir;
128 ULONG i;
129
130 DPRINT("Mmi386ReleaseMmInfo(Process %x)\n",Process);
131
132 LdtDescriptor = (PUSHORT) &Process->Pcb.LdtDescriptor;
133 LdtBase = LdtDescriptor[1] |
134 ((LdtDescriptor[2] & 0xff) << 16) |
135 ((LdtDescriptor[3] & ~0xff) << 16);
136
137 DPRINT("LdtBase: %x\n", LdtBase);
138
139 if (LdtBase)
140 {
141 ExFreePool((PVOID) LdtBase);
142 }
143
144 PageDir = MmCreateHyperspaceMapping(PTE_TO_PFN(Process->Pcb.DirectoryTableBase[0]));
145 for (i = 0; i < ADDR_TO_PDE_OFFSET(MmSystemRangeStart); i++)
146 {
147 if (PageDir[i] != 0)
148 {
149 MiZeroPage(PTE_TO_PFN(PageDir[i]));
150 MmReleasePageMemoryConsumer(MC_NPPOOL, PTE_TO_PFN(PageDir[i]));
151 }
152 }
153 MmReleasePageMemoryConsumer(MC_NPPOOL, PTE_TO_PFN(PageDir[ADDR_TO_PDE_OFFSET(HYPERSPACE)]));
154 MmDeleteHyperspaceMapping(PageDir);
155 MmReleasePageMemoryConsumer(MC_NPPOOL, PTE_TO_PFN(Process->Pcb.DirectoryTableBase[0]));
156
157 Process->Pcb.DirectoryTableBase[0] = 0;
158 Process->Pcb.DirectoryTableBase[1] = 0;
159
160 DPRINT("Finished Mmi386ReleaseMmInfo()\n");
161 return(STATUS_SUCCESS);
162 }
163
164 NTSTATUS
165 NTAPI
166 MmInitializeHandBuiltProcess(IN PEPROCESS Process,
167 IN PULONG DirectoryTableBase)
168 {
169 /* Share the directory base with the idle process */
170 DirectoryTableBase[0] = PsGetCurrentProcess()->Pcb.DirectoryTableBase[0];
171 DirectoryTableBase[1] = PsGetCurrentProcess()->Pcb.DirectoryTableBase[1];
172
173 /* Initialize the Addresss Space */
174 KeInitializeGuardedMutex(&Process->AddressCreationLock);
175 Process->VadRoot.BalancedRoot.u1.Parent = NULL;
176
177 /* The process now has an address space */
178 Process->HasAddressSpace = TRUE;
179 return STATUS_SUCCESS;
180 }
181
182 BOOLEAN
183 NTAPI
184 MmCreateProcessAddressSpace(IN ULONG MinWs,
185 IN PEPROCESS Process,
186 IN PULONG DirectoryTableBase)
187 {
188 NTSTATUS Status;
189 ULONG i, j;
190 PFN_TYPE Pfn[2];
191 PULONG PageDirectory;
192
193 DPRINT("MmCopyMmInfo(Src %x, Dest %x)\n", MinWs, Process);
194
195 for (i = 0; i < 2; i++)
196 {
197 Status = MmRequestPageMemoryConsumer(MC_NPPOOL, FALSE, &Pfn[i]);
198 if (!NT_SUCCESS(Status))
199 {
200 for (j = 0; j < i; j++)
201 {
202 MmReleasePageMemoryConsumer(MC_NPPOOL, Pfn[j]);
203 }
204
205 return FALSE;
206 }
207 }
208
209 PageDirectory = MmCreateHyperspaceMapping(Pfn[0]);
210
211 memcpy(PageDirectory + ADDR_TO_PDE_OFFSET(MmSystemRangeStart),
212 MmGlobalKernelPageDirectory + ADDR_TO_PDE_OFFSET(MmSystemRangeStart),
213 (1024 - ADDR_TO_PDE_OFFSET(MmSystemRangeStart)) * sizeof(ULONG));
214
215 DPRINT("Addr %x\n",ADDR_TO_PDE_OFFSET(PAGETABLE_MAP));
216 PageDirectory[ADDR_TO_PDE_OFFSET(PAGETABLE_MAP)] = PFN_TO_PTE(Pfn[0]) | PA_PRESENT | PA_READWRITE;
217 PageDirectory[ADDR_TO_PDE_OFFSET(HYPERSPACE)] = PFN_TO_PTE(Pfn[1]) | PA_PRESENT | PA_READWRITE;
218
219 MmDeleteHyperspaceMapping(PageDirectory);
220
221 DirectoryTableBase[0] = PFN_TO_PTE(Pfn[0]);
222 DirectoryTableBase[1] = 0;
223 DPRINT("Finished MmCopyMmInfo(): 0x%x\n", DirectoryTableBase[0]);
224 return TRUE;
225 }
226
227 VOID
228 NTAPI
229 MmDeletePageTable(PEPROCESS Process, PVOID Address)
230 {
231 PEPROCESS CurrentProcess = PsGetCurrentProcess();
232
233 if (Process != NULL && Process != CurrentProcess)
234 {
235 KeAttachProcess(&Process->Pcb);
236 }
237
238 MiAddressToPde(Address)->u.Long = 0;
239 MiFlushTlb((PULONG)MiAddressToPde(Address),
240 MiAddressToPte(Address));
241
242 if (Address >= MmSystemRangeStart)
243 {
244 KeBugCheck(MEMORY_MANAGEMENT);
245 // MmGlobalKernelPageDirectory[ADDR_TO_PDE_OFFSET(Address)] = 0;
246 }
247 if (Process != NULL && Process != CurrentProcess)
248 {
249 KeDetachProcess();
250 }
251 }
252
253 static PULONG
254 MmGetPageTableForProcess(PEPROCESS Process, PVOID Address, BOOLEAN Create)
255 {
256 ULONG PdeOffset = ADDR_TO_PDE_OFFSET(Address);
257 NTSTATUS Status;
258 PFN_TYPE Pfn;
259 ULONG Entry;
260 PULONG Pt, PageDir;
261
262 if (Address < MmSystemRangeStart && Process && Process != PsGetCurrentProcess())
263 {
264 PageDir = MmCreateHyperspaceMapping(PTE_TO_PFN(Process->Pcb.DirectoryTableBase[0]));
265 if (PageDir == NULL)
266 {
267 KeBugCheck(MEMORY_MANAGEMENT);
268 }
269 if (0 == InterlockedCompareExchangePte(&PageDir[PdeOffset], 0, 0))
270 {
271 if (Create == FALSE)
272 {
273 MmDeleteHyperspaceMapping(PageDir);
274 return NULL;
275 }
276 Status = MmRequestPageMemoryConsumer(MC_NPPOOL, FALSE, &Pfn);
277 if (!NT_SUCCESS(Status) || Pfn == 0)
278 {
279 KeBugCheck(MEMORY_MANAGEMENT);
280 }
281 Entry = InterlockedCompareExchangePte(&PageDir[PdeOffset], PFN_TO_PTE(Pfn) | PA_PRESENT | PA_READWRITE | PA_USER, 0);
282 if (Entry != 0)
283 {
284 MmReleasePageMemoryConsumer(MC_NPPOOL, Pfn);
285 Pfn = PTE_TO_PFN(Entry);
286 }
287 }
288 else
289 {
290 Pfn = PTE_TO_PFN(PageDir[PdeOffset]);
291 }
292 MmDeleteHyperspaceMapping(PageDir);
293 Pt = MmCreateHyperspaceMapping(Pfn);
294 if (Pt == NULL)
295 {
296 KeBugCheck(MEMORY_MANAGEMENT);
297 }
298 return Pt + ADDR_TO_PTE_OFFSET(Address);
299 }
300 PageDir = (PULONG)MiAddressToPde(Address);
301 if (0 == InterlockedCompareExchangePte(PageDir, 0, 0))
302 {
303 if (Address >= MmSystemRangeStart)
304 {
305 if (0 == InterlockedCompareExchangePte(&MmGlobalKernelPageDirectory[PdeOffset], 0, 0))
306 {
307 if (Create == FALSE)
308 {
309 return NULL;
310 }
311 Status = MmRequestPageMemoryConsumer(MC_NPPOOL, FALSE, &Pfn);
312 if (!NT_SUCCESS(Status) || Pfn == 0)
313 {
314 KeBugCheck(MEMORY_MANAGEMENT);
315 }
316 Entry = PFN_TO_PTE(Pfn) | PA_PRESENT | PA_READWRITE;
317 if (Ke386GlobalPagesEnabled)
318 {
319 Entry |= PA_GLOBAL;
320 }
321 if(0 != InterlockedCompareExchangePte(&MmGlobalKernelPageDirectory[PdeOffset], Entry, 0))
322 {
323 MmReleasePageMemoryConsumer(MC_NPPOOL, Pfn);
324 }
325 }
326 InterlockedExchangePte(PageDir, MmGlobalKernelPageDirectory[PdeOffset]);
327 }
328 else
329 {
330 if (Create == FALSE)
331 {
332 return NULL;
333 }
334 Status = MmRequestPageMemoryConsumer(MC_NPPOOL, FALSE, &Pfn);
335 if (!NT_SUCCESS(Status) || Pfn == 0)
336 {
337 KeBugCheck(MEMORY_MANAGEMENT);
338 }
339 Entry = InterlockedCompareExchangePte(PageDir, PFN_TO_PTE(Pfn) | PA_PRESENT | PA_READWRITE | PA_USER, 0);
340 if (Entry != 0)
341 {
342 MmReleasePageMemoryConsumer(MC_NPPOOL, Pfn);
343 }
344 }
345 }
346 return (PULONG)MiAddressToPte(Address);
347 }
348
349 BOOLEAN MmUnmapPageTable(PULONG Pt)
350 {
351 if (Pt >= (PULONG)PAGETABLE_MAP && Pt < (PULONG)PAGETABLE_MAP + 1024*1024)
352 {
353 return TRUE;
354 }
355
356 if (Pt)
357 {
358 MmDeleteHyperspaceMapping((PVOID)PAGE_ROUND_DOWN(Pt));
359 }
360 return FALSE;
361 }
362
363 static ULONG MmGetPageEntryForProcess(PEPROCESS Process, PVOID Address)
364 {
365 ULONG Pte;
366 PULONG Pt;
367
368 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
369 if (Pt)
370 {
371 Pte = *Pt;
372 MmUnmapPageTable(Pt);
373 return Pte;
374 }
375 return 0;
376 }
377
378 PFN_TYPE
379 NTAPI
380 MmGetPfnForProcess(PEPROCESS Process,
381 PVOID Address)
382 {
383 ULONG Entry;
384 Entry = MmGetPageEntryForProcess(Process, Address);
385 if (!(Entry & PA_PRESENT))
386 {
387 return 0;
388 }
389 return(PTE_TO_PFN(Entry));
390 }
391
392 VOID
393 NTAPI
394 MmDisableVirtualMapping(PEPROCESS Process, PVOID Address, BOOLEAN* WasDirty, PPFN_TYPE Page)
395 /*
396 * FUNCTION: Delete a virtual mapping
397 */
398 {
399 BOOLEAN WasValid;
400 ULONG Pte;
401 PULONG Pt;
402
403 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
404 if (Pt == NULL)
405 {
406 KeBugCheck(MEMORY_MANAGEMENT);
407 }
408 /*
409 * Atomically disable the present bit and get the old value.
410 */
411 do
412 {
413 Pte = *Pt;
414 } while (Pte != InterlockedCompareExchangePte(Pt, Pte & ~PA_PRESENT, Pte));
415
416 MiFlushTlb(Pt, Address);
417 WasValid = (PAGE_MASK(Pte) != 0);
418 if (!WasValid)
419 {
420 KeBugCheck(MEMORY_MANAGEMENT);
421 }
422
423 /*
424 * Return some information to the caller
425 */
426 if (WasDirty != NULL)
427 {
428 *WasDirty = Pte & PA_DIRTY;
429 }
430 if (Page != NULL)
431 {
432 *Page = PTE_TO_PFN(Pte);
433 }
434 }
435
436 VOID
437 NTAPI
438 MmRawDeleteVirtualMapping(PVOID Address)
439 {
440 PULONG Pt;
441
442 Pt = MmGetPageTableForProcess(NULL, Address, FALSE);
443 if (Pt && *Pt)
444 {
445 /*
446 * Set the entry to zero
447 */
448 InterlockedExchangePte(Pt, 0);
449 MiFlushTlb(Pt, Address);
450 }
451 }
452
453 VOID
454 NTAPI
455 MmDeleteVirtualMapping(PEPROCESS Process, PVOID Address, BOOLEAN FreePage,
456 BOOLEAN* WasDirty, PPFN_TYPE Page)
457 /*
458 * FUNCTION: Delete a virtual mapping
459 */
460 {
461 BOOLEAN WasValid = FALSE;
462 PFN_TYPE Pfn;
463 ULONG Pte;
464 PULONG Pt;
465
466 DPRINT("MmDeleteVirtualMapping(%x, %x, %d, %x, %x)\n",
467 Process, Address, FreePage, WasDirty, Page);
468
469 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
470
471 if (Pt == NULL)
472 {
473 if (WasDirty != NULL)
474 {
475 *WasDirty = FALSE;
476 }
477 if (Page != NULL)
478 {
479 *Page = 0;
480 }
481 return;
482 }
483
484 /*
485 * Atomically set the entry to zero and get the old value.
486 */
487 Pte = InterlockedExchangePte(Pt, 0);
488
489 MiFlushTlb(Pt, Address);
490
491 WasValid = (PAGE_MASK(Pte) != 0);
492 if (WasValid)
493 {
494 Pfn = PTE_TO_PFN(Pte);
495 MmMarkPageUnmapped(Pfn);
496 }
497 else
498 {
499 Pfn = 0;
500 }
501
502 if (FreePage && WasValid)
503 {
504 MmReleasePageMemoryConsumer(MC_NPPOOL, Pfn);
505 }
506
507 /*
508 * Return some information to the caller
509 */
510 if (WasDirty != NULL)
511 {
512 *WasDirty = Pte & PA_DIRTY ? TRUE : FALSE;
513 }
514 if (Page != NULL)
515 {
516 *Page = Pfn;
517 }
518 }
519
520 VOID
521 NTAPI
522 MmDeletePageFileMapping(PEPROCESS Process, PVOID Address,
523 SWAPENTRY* SwapEntry)
524 /*
525 * FUNCTION: Delete a virtual mapping
526 */
527 {
528 ULONG Pte;
529 PULONG Pt;
530
531 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
532
533 if (Pt == NULL)
534 {
535 *SwapEntry = 0;
536 return;
537 }
538
539 /*
540 * Atomically set the entry to zero and get the old value.
541 */
542 Pte = InterlockedExchangePte(Pt, 0);
543
544 MiFlushTlb(Pt, Address);
545
546 /*
547 * Return some information to the caller
548 */
549 *SwapEntry = Pte >> 1;
550 }
551
552 BOOLEAN
553 Mmi386MakeKernelPageTableGlobal(PVOID PAddress)
554 {
555 PULONG Pt, Pde;
556 Pde = (PULONG)MiAddressToPde(PAddress);
557 if (*Pde == 0)
558 {
559 Pt = MmGetPageTableForProcess(NULL, PAddress, FALSE);
560 if (Pt != NULL)
561 {
562 return TRUE;
563 }
564 }
565 return(FALSE);
566 }
567
568 BOOLEAN
569 NTAPI
570 MmIsDirtyPage(PEPROCESS Process, PVOID Address)
571 {
572 return MmGetPageEntryForProcess(Process, Address) & PA_DIRTY ? TRUE : FALSE;
573 }
574
575 VOID
576 NTAPI
577 MmSetCleanPage(PEPROCESS Process, PVOID Address)
578 {
579 PULONG Pt;
580 ULONG Pte;
581
582 if (Address < MmSystemRangeStart && Process == NULL)
583 {
584 DPRINT1("MmSetCleanPage is called for user space without a process.\n");
585 KeBugCheck(MEMORY_MANAGEMENT);
586 }
587
588 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
589
590 if (Pt == NULL)
591 {
592 KeBugCheck(MEMORY_MANAGEMENT);
593 }
594
595 do
596 {
597 Pte = *Pt;
598 } while (Pte != InterlockedCompareExchangePte(Pt, Pte & ~PA_DIRTY, Pte));
599
600 if (Pte & PA_DIRTY)
601 {
602 MiFlushTlb(Pt, Address);
603 }
604 else
605 {
606 MmUnmapPageTable(Pt);
607 }
608 }
609
610 VOID
611 NTAPI
612 MmSetDirtyPage(PEPROCESS Process, PVOID Address)
613 {
614 PULONG Pt;
615 ULONG Pte;
616
617 if (Address < MmSystemRangeStart && Process == NULL)
618 {
619 DPRINT1("MmSetDirtyPage is called for user space without a process.\n");
620 KeBugCheck(MEMORY_MANAGEMENT);
621 }
622
623 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
624 if (Pt == NULL)
625 {
626 KeBugCheck(MEMORY_MANAGEMENT);
627 }
628
629 do
630 {
631 Pte = *Pt;
632 } while (Pte != InterlockedCompareExchangePte(Pt, Pte | PA_DIRTY, Pte));
633 if (!(Pte & PA_DIRTY))
634 {
635 MiFlushTlb(Pt, Address);
636 }
637 else
638 {
639 MmUnmapPageTable(Pt);
640 }
641 }
642
643 VOID
644 NTAPI
645 MmEnableVirtualMapping(PEPROCESS Process, PVOID Address)
646 {
647 PULONG Pt;
648 ULONG Pte;
649
650 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
651 if (Pt == NULL)
652 {
653 KeBugCheck(MEMORY_MANAGEMENT);
654 }
655
656 do
657 {
658 Pte = *Pt;
659 } while (Pte != InterlockedCompareExchangePte(Pt, Pte | PA_PRESENT, Pte));
660 if (!(Pte & PA_PRESENT))
661 {
662 MiFlushTlb(Pt, Address);
663 }
664 else
665 {
666 MmUnmapPageTable(Pt);
667 }
668 }
669
670 BOOLEAN
671 NTAPI
672 MmIsPagePresent(PEPROCESS Process, PVOID Address)
673 {
674 return MmGetPageEntryForProcess(Process, Address) & PA_PRESENT;
675 }
676
677 BOOLEAN
678 NTAPI
679 MmIsPageSwapEntry(PEPROCESS Process, PVOID Address)
680 {
681 ULONG Entry;
682 Entry = MmGetPageEntryForProcess(Process, Address);
683 return !(Entry & PA_PRESENT) && (Entry & 0x800) && Entry != 0;
684 }
685
686 NTSTATUS
687 NTAPI
688 MmCreateVirtualMappingForKernel(PVOID Address,
689 ULONG flProtect,
690 PPFN_TYPE Pages,
691 ULONG PageCount)
692 {
693 ULONG Attributes;
694 ULONG i;
695 PVOID Addr;
696 ULONG PdeOffset, oldPdeOffset;
697 PULONG Pt;
698 ULONG Pte;
699 BOOLEAN NoExecute = FALSE;
700
701 DPRINT("MmCreateVirtualMappingForKernel(%x, %x, %x, %d)\n",
702 Address, flProtect, Pages, PageCount);
703
704 if (Address < MmSystemRangeStart)
705 {
706 DPRINT1("MmCreateVirtualMappingForKernel is called for user space\n");
707 KeBugCheck(MEMORY_MANAGEMENT);
708 }
709
710 Attributes = ProtectToPTE(flProtect);
711 if (Attributes & 0x80000000)
712 {
713 NoExecute = TRUE;
714 }
715 Attributes &= 0xfff;
716 if (Ke386GlobalPagesEnabled)
717 {
718 Attributes |= PA_GLOBAL;
719 }
720
721 Addr = Address;
722
723 oldPdeOffset = ADDR_TO_PDE_OFFSET(Addr);
724 Pt = MmGetPageTableForProcess(NULL, Addr, TRUE);
725 if (Pt == NULL)
726 {
727 KeBugCheck(MEMORY_MANAGEMENT);
728 }
729 Pt--;
730
731 for (i = 0; i < PageCount; i++, Addr = (PVOID)((ULONG_PTR)Addr + PAGE_SIZE))
732 {
733 if (!(Attributes & PA_PRESENT) && Pages[i] != 0)
734 {
735 DPRINT1("Setting physical address but not allowing access at address "
736 "0x%.8X with attributes %x/%x.\n",
737 Addr, Attributes, flProtect);
738 KeBugCheck(MEMORY_MANAGEMENT);
739 }
740
741 PdeOffset = ADDR_TO_PDE_OFFSET(Addr);
742 if (oldPdeOffset != PdeOffset)
743 {
744 Pt = MmGetPageTableForProcess(NULL, Addr, TRUE);
745 if (Pt == NULL)
746 {
747 KeBugCheck(MEMORY_MANAGEMENT);
748 }
749 }
750 else
751 {
752 Pt++;
753 }
754 oldPdeOffset = PdeOffset;
755
756 Pte = *Pt;
757 if (Pte != 0)
758 {
759 KeBugCheck(MEMORY_MANAGEMENT);
760 }
761 InterlockedExchangePte(Pt, PFN_TO_PTE(Pages[i]) | Attributes);
762 }
763
764 return(STATUS_SUCCESS);
765 }
766
767 NTSTATUS
768 NTAPI
769 MmCreatePageFileMapping(PEPROCESS Process,
770 PVOID Address,
771 SWAPENTRY SwapEntry)
772 {
773 PULONG Pt;
774 ULONG Pte;
775
776 if (Process == NULL && Address < MmSystemRangeStart)
777 {
778 DPRINT1("No process\n");
779 KeBugCheck(MEMORY_MANAGEMENT);
780 }
781 if (Process != NULL && Address >= MmSystemRangeStart)
782 {
783 DPRINT1("Setting kernel address with process context\n");
784 KeBugCheck(MEMORY_MANAGEMENT);
785 }
786
787 if (SwapEntry & (1 << 31))
788 {
789 KeBugCheck(MEMORY_MANAGEMENT);
790 }
791
792 Pt = MmGetPageTableForProcess(Process, Address, TRUE);
793 if (Pt == NULL)
794 {
795 KeBugCheck(MEMORY_MANAGEMENT);
796 }
797 Pte = *Pt;
798 if (PAGE_MASK((Pte)) != 0)
799 {
800 MmMarkPageUnmapped(PTE_TO_PFN((Pte)));
801 }
802 InterlockedExchangePte(Pt, SwapEntry << 1);
803 if (Pte != 0)
804 {
805 MiFlushTlb(Pt, Address);
806 }
807 else
808 {
809 MmUnmapPageTable(Pt);
810 }
811
812 return(STATUS_SUCCESS);
813 }
814
815
816 NTSTATUS
817 NTAPI
818 MmCreateVirtualMappingUnsafe(PEPROCESS Process,
819 PVOID Address,
820 ULONG flProtect,
821 PPFN_TYPE Pages,
822 ULONG PageCount)
823 {
824 ULONG Attributes;
825 PVOID Addr;
826 ULONG i;
827 ULONG oldPdeOffset, PdeOffset;
828 PULONG Pt = NULL;
829 ULONG Pte;
830 BOOLEAN NoExecute = FALSE;
831
832 DPRINT("MmCreateVirtualMappingUnsafe(%x, %x, %x, %x (%x), %d)\n",
833 Process, Address, flProtect, Pages, *Pages, PageCount);
834
835 if (Process == NULL)
836 {
837 if (Address < MmSystemRangeStart)
838 {
839 DPRINT1("No process\n");
840 KeBugCheck(MEMORY_MANAGEMENT);
841 }
842 if (PageCount > 0x10000 ||
843 (ULONG_PTR) Address / PAGE_SIZE + PageCount > 0x100000)
844 {
845 DPRINT1("Page count too large\n");
846 KeBugCheck(MEMORY_MANAGEMENT);
847 }
848 }
849 else
850 {
851 if (Address >= MmSystemRangeStart)
852 {
853 DPRINT1("Setting kernel address with process context\n");
854 KeBugCheck(MEMORY_MANAGEMENT);
855 }
856 if (PageCount > (ULONG_PTR)MmSystemRangeStart / PAGE_SIZE ||
857 (ULONG_PTR) Address / PAGE_SIZE + PageCount >
858 (ULONG_PTR)MmSystemRangeStart / PAGE_SIZE)
859 {
860 DPRINT1("Page Count too large\n");
861 KeBugCheck(MEMORY_MANAGEMENT);
862 }
863 }
864
865 Attributes = ProtectToPTE(flProtect);
866 if (Attributes & 0x80000000)
867 {
868 NoExecute = TRUE;
869 }
870 Attributes &= 0xfff;
871 if (Address >= MmSystemRangeStart)
872 {
873 Attributes &= ~PA_USER;
874 if (Ke386GlobalPagesEnabled)
875 {
876 Attributes |= PA_GLOBAL;
877 }
878 }
879 else
880 {
881 Attributes |= PA_USER;
882 }
883
884 Addr = Address;
885 oldPdeOffset = ADDR_TO_PDE_OFFSET(Addr) + 1;
886 for (i = 0; i < PageCount; i++, Addr = (PVOID)((ULONG_PTR)Addr + PAGE_SIZE))
887 {
888 if (!(Attributes & PA_PRESENT) && Pages[i] != 0)
889 {
890 DPRINT1("Setting physical address but not allowing access at address "
891 "0x%.8X with attributes %x/%x.\n",
892 Addr, Attributes, flProtect);
893 KeBugCheck(MEMORY_MANAGEMENT);
894 }
895 PdeOffset = ADDR_TO_PDE_OFFSET(Addr);
896 if (oldPdeOffset != PdeOffset)
897 {
898 MmUnmapPageTable(Pt);
899 Pt = MmGetPageTableForProcess(Process, Addr, TRUE);
900 if (Pt == NULL)
901 {
902 KeBugCheck(MEMORY_MANAGEMENT);
903 }
904 }
905 else
906 {
907 Pt++;
908 }
909 oldPdeOffset = PdeOffset;
910
911 Pte = *Pt;
912 MmMarkPageMapped(Pages[i]);
913 if (PAGE_MASK(Pte) != 0 && !(Pte & PA_PRESENT) && (Pte & 0x800))
914 {
915 DPRINT1("Bad PTE %lx\n", Pte);
916 KeBugCheck(MEMORY_MANAGEMENT);
917 }
918 if (PAGE_MASK(Pte) != 0)
919 {
920 MmMarkPageUnmapped(PTE_TO_PFN(Pte));
921 }
922 InterlockedExchangePte(Pt, PFN_TO_PTE(Pages[i]) | Attributes);
923 if (Pte != 0)
924 {
925 if (Address > MmSystemRangeStart ||
926 (Pt >= (PULONG)PAGETABLE_MAP && Pt < (PULONG)PAGETABLE_MAP + 1024*1024))
927 {
928 MiFlushTlb(Pt, Address);
929 }
930 }
931 }
932 if (Addr > Address)
933 {
934 MmUnmapPageTable(Pt);
935 }
936
937 return(STATUS_SUCCESS);
938 }
939
940 NTSTATUS
941 NTAPI
942 MmCreateVirtualMapping(PEPROCESS Process,
943 PVOID Address,
944 ULONG flProtect,
945 PPFN_TYPE Pages,
946 ULONG PageCount)
947 {
948 ULONG i;
949
950 for (i = 0; i < PageCount; i++)
951 {
952 if (!MmIsPageInUse(Pages[i]))
953 {
954 DPRINT1("Page at address %x not in use\n", PFN_TO_PTE(Pages[i]));
955 KeBugCheck(MEMORY_MANAGEMENT);
956 }
957 }
958
959 return(MmCreateVirtualMappingUnsafe(Process,
960 Address,
961 flProtect,
962 Pages,
963 PageCount));
964 }
965
966 ULONG
967 NTAPI
968 MmGetPageProtect(PEPROCESS Process, PVOID Address)
969 {
970 ULONG Entry;
971 ULONG Protect;
972
973 Entry = MmGetPageEntryForProcess(Process, Address);
974
975
976 if (!(Entry & PA_PRESENT))
977 {
978 Protect = PAGE_NOACCESS;
979 }
980 else
981 {
982 if (Entry & PA_READWRITE)
983 {
984 Protect = PAGE_READWRITE;
985 }
986 else
987 {
988 Protect = PAGE_EXECUTE_READ;
989 }
990 if (Entry & PA_CD)
991 {
992 Protect |= PAGE_NOCACHE;
993 }
994 if (Entry & PA_WT)
995 {
996 Protect |= PAGE_WRITETHROUGH;
997 }
998 if (!(Entry & PA_USER))
999 {
1000 Protect |= PAGE_SYSTEM;
1001 }
1002
1003 }
1004 return(Protect);
1005 }
1006
1007 VOID
1008 NTAPI
1009 MmSetPageProtect(PEPROCESS Process, PVOID Address, ULONG flProtect)
1010 {
1011 ULONG Attributes = 0;
1012 BOOLEAN NoExecute = FALSE;
1013 PULONG Pt;
1014
1015 DPRINT("MmSetPageProtect(Process %x Address %x flProtect %x)\n",
1016 Process, Address, flProtect);
1017
1018 Attributes = ProtectToPTE(flProtect);
1019
1020 if (Attributes & 0x80000000)
1021 {
1022 NoExecute = TRUE;
1023 }
1024 Attributes &= 0xfff;
1025 if (Address >= MmSystemRangeStart)
1026 {
1027 Attributes &= ~PA_USER;
1028 if (Ke386GlobalPagesEnabled)
1029 {
1030 Attributes |= PA_GLOBAL;
1031 }
1032 }
1033 else
1034 {
1035 Attributes |= PA_USER;
1036 }
1037
1038 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
1039 if (Pt == NULL)
1040 {
1041 KeBugCheck(MEMORY_MANAGEMENT);
1042 }
1043 InterlockedExchangePte(Pt, PAGE_MASK(*Pt) | Attributes | (*Pt & (PA_ACCESSED|PA_DIRTY)));
1044 MiFlushTlb(Pt, Address);
1045 }
1046
1047 /*
1048 * @implemented
1049 */
1050 PHYSICAL_ADDRESS NTAPI
1051 MmGetPhysicalAddress(PVOID vaddr)
1052 /*
1053 * FUNCTION: Returns the physical address corresponding to a virtual address
1054 */
1055 {
1056 PHYSICAL_ADDRESS p;
1057 ULONG Pte;
1058
1059 DPRINT("MmGetPhysicalAddress(vaddr %x)\n", vaddr);
1060 Pte = MmGetPageEntryForProcess(NULL, vaddr);
1061 if (Pte != 0 && Pte & PA_PRESENT)
1062 {
1063 p.QuadPart = PAGE_MASK(Pte);
1064 p.u.LowPart |= (ULONG_PTR)vaddr & (PAGE_SIZE - 1);
1065 }
1066 else
1067 {
1068 p.QuadPart = 0;
1069 }
1070 return p;
1071 }
1072
1073 VOID
1074 NTAPI
1075 MmUpdatePageDir(PEPROCESS Process, PVOID Address, ULONG Size)
1076 {
1077 ULONG StartOffset, EndOffset, Offset;
1078 PULONG Pde;
1079
1080 if (Address < MmSystemRangeStart)
1081 {
1082 KeBugCheck(MEMORY_MANAGEMENT);
1083 }
1084
1085 StartOffset = ADDR_TO_PDE_OFFSET(Address);
1086 EndOffset = ADDR_TO_PDE_OFFSET((PVOID)((ULONG_PTR)Address + Size));
1087
1088 if (Process != NULL && Process != PsGetCurrentProcess())
1089 {
1090 Pde = MmCreateHyperspaceMapping(PTE_TO_PFN(Process->Pcb.DirectoryTableBase[0]));
1091 }
1092 else
1093 {
1094 Pde = (PULONG)PAGEDIRECTORY_MAP;
1095 }
1096 for (Offset = StartOffset; Offset <= EndOffset; Offset++)
1097 {
1098 if (Offset != ADDR_TO_PDE_OFFSET(PAGETABLE_MAP))
1099 {
1100 InterlockedCompareExchangePte(&Pde[Offset], MmGlobalKernelPageDirectory[Offset], 0);
1101 }
1102 }
1103 if (Pde != (PULONG)PAGEDIRECTORY_MAP)
1104 {
1105 MmDeleteHyperspaceMapping(Pde);
1106 }
1107 }
1108
1109 extern MMPTE HyperTemplatePte;
1110
1111 VOID
1112 INIT_FUNCTION
1113 NTAPI
1114 MmInitGlobalKernelPageDirectory(VOID)
1115 {
1116 ULONG i;
1117 PULONG CurrentPageDirectory = (PULONG)PAGEDIRECTORY_MAP;
1118
1119 DPRINT("MmInitGlobalKernelPageDirectory()\n");
1120
1121 //
1122 // Setup template
1123 //
1124 HyperTemplatePte.u.Long = (PA_PRESENT | PA_READWRITE | PA_DIRTY | PA_ACCESSED);
1125 if (Ke386GlobalPagesEnabled) HyperTemplatePte.u.Long |= PA_GLOBAL;
1126
1127 for (i = ADDR_TO_PDE_OFFSET(MmSystemRangeStart); i < 1024; i++)
1128 {
1129 if (i != ADDR_TO_PDE_OFFSET(PAGETABLE_MAP) &&
1130 i != ADDR_TO_PDE_OFFSET(HYPERSPACE) &&
1131 0 == MmGlobalKernelPageDirectory[i] && 0 != CurrentPageDirectory[i])
1132 {
1133 MmGlobalKernelPageDirectory[i] = CurrentPageDirectory[i];
1134 if (Ke386GlobalPagesEnabled)
1135 {
1136 MmGlobalKernelPageDirectory[i] |= PA_GLOBAL;
1137 CurrentPageDirectory[i] |= PA_GLOBAL;
1138 }
1139 }
1140 }
1141 }
1142
1143 VOID
1144 INIT_FUNCTION
1145 NTAPI
1146 MiInitPageDirectoryMap(VOID)
1147 {
1148 MEMORY_AREA* kernel_map_desc = NULL;
1149 MEMORY_AREA* hyperspace_desc = NULL;
1150 PHYSICAL_ADDRESS BoundaryAddressMultiple;
1151 PVOID BaseAddress;
1152 NTSTATUS Status;
1153
1154 DPRINT("MiInitPageDirectoryMap()\n");
1155
1156 BoundaryAddressMultiple.QuadPart = 0;
1157 BaseAddress = (PVOID)PAGETABLE_MAP;
1158 Status = MmCreateMemoryArea(MmGetKernelAddressSpace(),
1159 MEMORY_AREA_SYSTEM,
1160 &BaseAddress,
1161 0x400000,
1162 PAGE_READWRITE,
1163 &kernel_map_desc,
1164 TRUE,
1165 0,
1166 BoundaryAddressMultiple);
1167 if (!NT_SUCCESS(Status))
1168 {
1169 KeBugCheck(MEMORY_MANAGEMENT);
1170 }
1171 BaseAddress = (PVOID)HYPERSPACE;
1172 Status = MmCreateMemoryArea(MmGetKernelAddressSpace(),
1173 MEMORY_AREA_SYSTEM,
1174 &BaseAddress,
1175 0x400000,
1176 PAGE_READWRITE,
1177 &hyperspace_desc,
1178 TRUE,
1179 0,
1180 BoundaryAddressMultiple);
1181 if (!NT_SUCCESS(Status))
1182 {
1183 KeBugCheck(MEMORY_MANAGEMENT);
1184 }
1185 }
1186
1187 /* EOF */