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