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