merge trunk head (37902)
[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_PTR 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_PTR 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 ? TRUE : FALSE;
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 != 0 ? TRUE : FALSE;
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 if (SwapEntry & (1 << 31))
787 {
788 KeBugCheck(MEMORY_MANAGEMENT);
789 }
790
791 Pt = MmGetPageTableForProcess(Process, Address, TRUE);
792 if (Pt == NULL)
793 {
794 KeBugCheck(MEMORY_MANAGEMENT);
795 }
796 Pte = *Pt;
797 if (PAGE_MASK((Pte)) != 0)
798 {
799 MmMarkPageUnmapped(PTE_TO_PFN((Pte)));
800 }
801 InterlockedExchangePte(Pt, SwapEntry << 1);
802 if (Pte != 0)
803 {
804 MiFlushTlb(Pt, Address);
805 }
806 else
807 {
808 MmUnmapPageTable(Pt);
809 }
810
811 return(STATUS_SUCCESS);
812 }
813
814
815 NTSTATUS
816 NTAPI
817 MmCreateVirtualMappingUnsafe(PEPROCESS Process,
818 PVOID Address,
819 ULONG flProtect,
820 PPFN_TYPE Pages,
821 ULONG PageCount)
822 {
823 ULONG Attributes;
824 PVOID Addr;
825 ULONG i;
826 ULONG oldPdeOffset, PdeOffset;
827 PULONG Pt = NULL;
828 ULONG Pte;
829 BOOLEAN NoExecute = FALSE;
830
831 DPRINT("MmCreateVirtualMappingUnsafe(%x, %x, %x, %x (%x), %d)\n",
832 Process, Address, flProtect, Pages, *Pages, PageCount);
833
834 if (Process == NULL)
835 {
836 if (Address < MmSystemRangeStart)
837 {
838 DPRINT1("No process\n");
839 KeBugCheck(MEMORY_MANAGEMENT);
840 }
841 if (PageCount > 0x10000 ||
842 (ULONG_PTR) Address / PAGE_SIZE + PageCount > 0x100000)
843 {
844 DPRINT1("Page count to large\n");
845 KeBugCheck(MEMORY_MANAGEMENT);
846 }
847 }
848 else
849 {
850 if (Address >= MmSystemRangeStart)
851 {
852 DPRINT1("Setting kernel address with process context\n");
853 KeBugCheck(MEMORY_MANAGEMENT);
854 }
855 if (PageCount > (ULONG_PTR)MmSystemRangeStart / PAGE_SIZE ||
856 (ULONG_PTR) Address / PAGE_SIZE + PageCount >
857 (ULONG_PTR)MmSystemRangeStart / PAGE_SIZE)
858 {
859 DPRINT1("Page Count to large\n");
860 KeBugCheck(MEMORY_MANAGEMENT);
861 }
862 }
863
864 Attributes = ProtectToPTE(flProtect);
865 if (Attributes & 0x80000000)
866 {
867 NoExecute = TRUE;
868 }
869 Attributes &= 0xfff;
870 if (Address >= MmSystemRangeStart)
871 {
872 Attributes &= ~PA_USER;
873 if (Ke386GlobalPagesEnabled)
874 {
875 Attributes |= PA_GLOBAL;
876 }
877 }
878 else
879 {
880 Attributes |= PA_USER;
881 }
882
883 Addr = Address;
884 oldPdeOffset = ADDR_TO_PDE_OFFSET(Addr) + 1;
885 for (i = 0; i < PageCount; i++, Addr = (PVOID)((ULONG_PTR)Addr + PAGE_SIZE))
886 {
887 if (!(Attributes & PA_PRESENT) && Pages[i] != 0)
888 {
889 DPRINT1("Setting physical address but not allowing access at address "
890 "0x%.8X with attributes %x/%x.\n",
891 Addr, Attributes, flProtect);
892 KeBugCheck(MEMORY_MANAGEMENT);
893 }
894 PdeOffset = ADDR_TO_PDE_OFFSET(Addr);
895 if (oldPdeOffset != PdeOffset)
896 {
897 MmUnmapPageTable(Pt);
898 Pt = MmGetPageTableForProcess(Process, Addr, TRUE);
899 if (Pt == NULL)
900 {
901 KeBugCheck(MEMORY_MANAGEMENT);
902 }
903 }
904 else
905 {
906 Pt++;
907 }
908 oldPdeOffset = PdeOffset;
909
910 Pte = *Pt;
911 MmMarkPageMapped(Pages[i]);
912 if (PAGE_MASK((Pte)) != 0 && !((Pte) & PA_PRESENT))
913 {
914 KeBugCheck(MEMORY_MANAGEMENT);
915 }
916 if (PAGE_MASK((Pte)) != 0)
917 {
918 MmMarkPageUnmapped(PTE_TO_PFN((Pte)));
919 }
920 InterlockedExchangePte(Pt, PFN_TO_PTE(Pages[i]) | Attributes);
921 if (Pte != 0)
922 {
923 if (Address > MmSystemRangeStart ||
924 (Pt >= (PULONG)PAGETABLE_MAP && Pt < (PULONG)PAGETABLE_MAP + 1024*1024))
925 {
926 MiFlushTlb(Pt, Address);
927 }
928 }
929 }
930 if (Addr > Address)
931 {
932 MmUnmapPageTable(Pt);
933 }
934
935 return(STATUS_SUCCESS);
936 }
937
938 NTSTATUS
939 NTAPI
940 MmCreateVirtualMapping(PEPROCESS Process,
941 PVOID Address,
942 ULONG flProtect,
943 PPFN_TYPE Pages,
944 ULONG PageCount)
945 {
946 ULONG i;
947
948 for (i = 0; i < PageCount; i++)
949 {
950 if (!MmIsPageInUse(Pages[i]))
951 {
952 DPRINT1("Page at address %x not in use\n", PFN_TO_PTE(Pages[i]));
953 KeBugCheck(MEMORY_MANAGEMENT);
954 }
955 }
956
957 return(MmCreateVirtualMappingUnsafe(Process,
958 Address,
959 flProtect,
960 Pages,
961 PageCount));
962 }
963
964 ULONG
965 NTAPI
966 MmGetPageProtect(PEPROCESS Process, PVOID Address)
967 {
968 ULONG Entry;
969 ULONG Protect;
970
971 Entry = MmGetPageEntryForProcess(Process, Address);
972
973
974 if (!(Entry & PA_PRESENT))
975 {
976 Protect = PAGE_NOACCESS;
977 }
978 else
979 {
980 if (Entry & PA_READWRITE)
981 {
982 Protect = PAGE_READWRITE;
983 }
984 else
985 {
986 Protect = PAGE_EXECUTE_READ;
987 }
988 if (Entry & PA_CD)
989 {
990 Protect |= PAGE_NOCACHE;
991 }
992 if (Entry & PA_WT)
993 {
994 Protect |= PAGE_WRITETHROUGH;
995 }
996 if (!(Entry & PA_USER))
997 {
998 Protect |= PAGE_SYSTEM;
999 }
1000
1001 }
1002 return(Protect);
1003 }
1004
1005 VOID
1006 NTAPI
1007 MmSetPageProtect(PEPROCESS Process, PVOID Address, ULONG flProtect)
1008 {
1009 ULONG Attributes = 0;
1010 BOOLEAN NoExecute = FALSE;
1011 PULONG Pt;
1012
1013 DPRINT("MmSetPageProtect(Process %x Address %x flProtect %x)\n",
1014 Process, Address, flProtect);
1015
1016 Attributes = ProtectToPTE(flProtect);
1017 if (Attributes & 0x80000000)
1018 {
1019 NoExecute = TRUE;
1020 }
1021 Attributes &= 0xfff;
1022 if (Address >= MmSystemRangeStart)
1023 {
1024 Attributes &= ~PA_USER;
1025 if (Ke386GlobalPagesEnabled)
1026 {
1027 Attributes |= PA_GLOBAL;
1028 }
1029 }
1030 else
1031 {
1032 Attributes |= PA_USER;
1033 }
1034
1035 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
1036 if (Pt == NULL)
1037 {
1038 KeBugCheck(MEMORY_MANAGEMENT);
1039 }
1040 InterlockedExchangePte(Pt, PAGE_MASK(*Pt) | Attributes | (*Pt & (PA_ACCESSED|PA_DIRTY)));
1041 MiFlushTlb(Pt, Address);
1042 }
1043
1044 /*
1045 * @implemented
1046 */
1047 PHYSICAL_ADDRESS NTAPI
1048 MmGetPhysicalAddress(PVOID vaddr)
1049 /*
1050 * FUNCTION: Returns the physical address corresponding to a virtual address
1051 */
1052 {
1053 PHYSICAL_ADDRESS p;
1054 ULONG Pte;
1055
1056 DPRINT("MmGetPhysicalAddress(vaddr %x)\n", vaddr);
1057 Pte = MmGetPageEntryForProcess(NULL, vaddr);
1058 if (Pte != 0 && Pte & PA_PRESENT)
1059 {
1060 p.QuadPart = PAGE_MASK(Pte);
1061 p.u.LowPart |= (ULONG_PTR)vaddr & (PAGE_SIZE - 1);
1062 }
1063 else
1064 {
1065 p.QuadPart = 0;
1066 }
1067 return p;
1068 }
1069
1070 PVOID
1071 NTAPI
1072 MmCreateHyperspaceMapping(PFN_TYPE Page)
1073 {
1074 PVOID Address;
1075 ULONG i;
1076
1077 ULONG Entry;
1078 PULONG Pte;
1079 Entry = PFN_TO_PTE(Page) | PA_PRESENT | PA_READWRITE;
1080 Pte = (PULONG)MiAddressToPte(HYPERSPACE) + Page % 1024;
1081 if (Page & 1024)
1082 {
1083 for (i = Page % 1024; i < 1024; i++, Pte++)
1084 {
1085 if (0 == InterlockedCompareExchange((PLONG)Pte, (LONG)Entry, 0))
1086 {
1087 break;
1088 }
1089 }
1090 if (i >= 1024)
1091 {
1092 Pte = (PULONG)MiAddressToPte(HYPERSPACE);
1093 for (i = 0; i < Page % 1024; i++, Pte++)
1094 {
1095 if (0 == InterlockedCompareExchange((PLONG)Pte, (LONG)Entry, 0))
1096 {
1097 break;
1098 }
1099 }
1100 if (i >= Page % 1024)
1101 {
1102 KeBugCheck(MEMORY_MANAGEMENT);
1103 }
1104 }
1105 }
1106 else
1107 {
1108 for (i = Page % 1024; (LONG)i >= 0; i--, Pte--)
1109 {
1110 if (0 == InterlockedCompareExchange((PLONG)Pte, (LONG)Entry, 0))
1111 {
1112 break;
1113 }
1114 }
1115 if ((LONG)i < 0)
1116 {
1117 Pte = (PULONG)MiAddressToPte(HYPERSPACE) + 1023;
1118 for (i = 1023; i > Page % 1024; i--, Pte--)
1119 {
1120 if (0 == InterlockedCompareExchange((PLONG)Pte, (LONG)Entry, 0))
1121 {
1122 break;
1123 }
1124 }
1125 if (i <= Page % 1024)
1126 {
1127 KeBugCheck(MEMORY_MANAGEMENT);
1128 }
1129 }
1130 }
1131 Address = (PVOID)((ULONG_PTR)HYPERSPACE + i * PAGE_SIZE);
1132 __invlpg(Address);
1133 return Address;
1134 }
1135
1136 PFN_TYPE
1137 NTAPI
1138 MmDeleteHyperspaceMapping(PVOID Address)
1139 {
1140 PFN_TYPE Pfn;
1141 ULONG Entry;
1142
1143 ASSERT (IS_HYPERSPACE(Address));
1144
1145 Entry = InterlockedExchangePte(MiAddressToPte(Address), 0);
1146 Pfn = PTE_TO_PFN(Entry);
1147
1148 __invlpg(Address);
1149 return Pfn;
1150 }
1151
1152 VOID
1153 NTAPI
1154 MmUpdatePageDir(PEPROCESS Process, PVOID Address, ULONG Size)
1155 {
1156 ULONG StartOffset, EndOffset, Offset;
1157 PULONG Pde;
1158
1159 if (Address < MmSystemRangeStart)
1160 {
1161 KeBugCheck(MEMORY_MANAGEMENT);
1162 }
1163
1164 StartOffset = ADDR_TO_PDE_OFFSET(Address);
1165 EndOffset = ADDR_TO_PDE_OFFSET((PVOID)((ULONG_PTR)Address + Size));
1166
1167 if (Process != NULL && Process != PsGetCurrentProcess())
1168 {
1169 Pde = MmCreateHyperspaceMapping(PTE_TO_PFN(Process->Pcb.DirectoryTableBase[0]));
1170 }
1171 else
1172 {
1173 Pde = (PULONG)PAGEDIRECTORY_MAP;
1174 }
1175 for (Offset = StartOffset; Offset <= EndOffset; Offset++)
1176 {
1177 if (Offset != ADDR_TO_PDE_OFFSET(PAGETABLE_MAP))
1178 {
1179 InterlockedCompareExchangePte(&Pde[Offset], MmGlobalKernelPageDirectory[Offset], 0);
1180 }
1181 }
1182 if (Pde != (PULONG)PAGEDIRECTORY_MAP)
1183 {
1184 MmDeleteHyperspaceMapping(Pde);
1185 }
1186 }
1187
1188 VOID
1189 INIT_FUNCTION
1190 NTAPI
1191 MmInitGlobalKernelPageDirectory(VOID)
1192 {
1193 ULONG i;
1194 PULONG CurrentPageDirectory = (PULONG)PAGEDIRECTORY_MAP;
1195
1196 DPRINT("MmInitGlobalKernelPageDirectory()\n");
1197
1198 for (i = ADDR_TO_PDE_OFFSET(MmSystemRangeStart); i < 1024; i++)
1199 {
1200 if (i != ADDR_TO_PDE_OFFSET(PAGETABLE_MAP) &&
1201 i != ADDR_TO_PDE_OFFSET(HYPERSPACE) &&
1202 0 == MmGlobalKernelPageDirectory[i] && 0 != CurrentPageDirectory[i])
1203 {
1204 MmGlobalKernelPageDirectory[i] = CurrentPageDirectory[i];
1205 if (Ke386GlobalPagesEnabled)
1206 {
1207 MmGlobalKernelPageDirectory[i] |= PA_GLOBAL;
1208 CurrentPageDirectory[i] |= PA_GLOBAL;
1209 }
1210 }
1211 }
1212 }
1213
1214 VOID
1215 INIT_FUNCTION
1216 NTAPI
1217 MiInitPageDirectoryMap(VOID)
1218 {
1219 MEMORY_AREA* kernel_map_desc = NULL;
1220 MEMORY_AREA* hyperspace_desc = NULL;
1221 PHYSICAL_ADDRESS BoundaryAddressMultiple;
1222 PVOID BaseAddress;
1223 NTSTATUS Status;
1224
1225 DPRINT("MiInitPageDirectoryMap()\n");
1226
1227 BoundaryAddressMultiple.QuadPart = 0;
1228 BaseAddress = (PVOID)PAGETABLE_MAP;
1229 Status = MmCreateMemoryArea(MmGetKernelAddressSpace(),
1230 MEMORY_AREA_SYSTEM,
1231 &BaseAddress,
1232 0x400000,
1233 PAGE_READWRITE,
1234 &kernel_map_desc,
1235 TRUE,
1236 0,
1237 BoundaryAddressMultiple);
1238 if (!NT_SUCCESS(Status))
1239 {
1240 KeBugCheck(MEMORY_MANAGEMENT);
1241 }
1242 BaseAddress = (PVOID)HYPERSPACE;
1243 Status = MmCreateMemoryArea(MmGetKernelAddressSpace(),
1244 MEMORY_AREA_SYSTEM,
1245 &BaseAddress,
1246 0x400000,
1247 PAGE_READWRITE,
1248 &hyperspace_desc,
1249 TRUE,
1250 0,
1251 BoundaryAddressMultiple);
1252 if (!NT_SUCCESS(Status))
1253 {
1254 KeBugCheck(MEMORY_MANAGEMENT);
1255 }
1256 }
1257
1258 /* EOF */