Merge to trunk head (r46631)
[reactos.git] / ntoskrnl / mm / i386 / page.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/mm/i386/page.c
5 * PURPOSE: Low level memory managment manipulation
6 *
7 * PROGRAMMERS: David Welch (welch@cwcom.net)
8 */
9
10 /* INCLUDES ***************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
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 KeInvalidateTlbEntry(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 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 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(0 != InterlockedCompareExchangePte(&MmGlobalKernelPageDirectory[PdeOffset], Entry, 0))
288 {
289 MmReleasePageMemoryConsumer(MC_SYSTEM, Pfn);
290 }
291 InterlockedExchangePte(PageDir, MmGlobalKernelPageDirectory[PdeOffset]);
292 RtlZeroMemory(MiPteToAddress(PageDir), PAGE_SIZE);
293 return (PULONG)MiAddressToPte(Address);
294 }
295 InterlockedExchangePte(PageDir, MmGlobalKernelPageDirectory[PdeOffset]);
296 }
297 else
298 {
299 if (Create == FALSE)
300 {
301 return NULL;
302 }
303 Status = MmRequestPageMemoryConsumer(MC_NPPOOL, FALSE, &Pfn);
304 if (!NT_SUCCESS(Status) || Pfn == 0)
305 {
306 KeBugCheck(MEMORY_MANAGEMENT);
307 }
308 Entry = InterlockedCompareExchangePte(PageDir, PFN_TO_PTE(Pfn) | PA_PRESENT | PA_READWRITE | PA_USER, 0);
309 if (Entry != 0)
310 {
311 MmReleasePageMemoryConsumer(MC_NPPOOL, Pfn);
312 }
313 }
314 }
315 return (PULONG)MiAddressToPte(Address);
316 }
317
318 BOOLEAN MmUnmapPageTable(PULONG Pt)
319 {
320 if (Pt >= (PULONG)PAGETABLE_MAP && Pt < (PULONG)PAGETABLE_MAP + 1024*1024)
321 {
322 return TRUE;
323 }
324
325 if (Pt)
326 {
327 MmDeleteHyperspaceMapping((PVOID)PAGE_ROUND_DOWN(Pt));
328 }
329 return FALSE;
330 }
331
332 static ULONG MmGetPageEntryForProcess(PEPROCESS Process, PVOID Address)
333 {
334 ULONG Pte;
335 PULONG Pt;
336
337 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
338 if (Pt)
339 {
340 Pte = *Pt;
341 MmUnmapPageTable(Pt);
342 return Pte;
343 }
344 return 0;
345 }
346
347 PFN_TYPE
348 NTAPI
349 MmGetPfnForProcess(PEPROCESS Process,
350 PVOID Address)
351 {
352 ULONG Entry;
353 Entry = MmGetPageEntryForProcess(Process, Address);
354 if (!(Entry & PA_PRESENT))
355 {
356 return 0;
357 }
358 return(PTE_TO_PFN(Entry));
359 }
360
361 VOID
362 NTAPI
363 MmDisableVirtualMapping(PEPROCESS Process, PVOID Address, BOOLEAN* WasDirty, PPFN_TYPE Page)
364 /*
365 * FUNCTION: Delete a virtual mapping
366 */
367 {
368 BOOLEAN WasValid;
369 ULONG Pte;
370 PULONG Pt;
371
372 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
373 if (Pt == NULL)
374 {
375 KeBugCheck(MEMORY_MANAGEMENT);
376 }
377 /*
378 * Atomically disable the present bit and get the old value.
379 */
380 do
381 {
382 Pte = *Pt;
383 } while (Pte != InterlockedCompareExchangePte(Pt, Pte & ~PA_PRESENT, Pte));
384
385 MiFlushTlb(Pt, Address);
386 WasValid = (PAGE_MASK(Pte) != 0);
387 if (!WasValid)
388 {
389 KeBugCheck(MEMORY_MANAGEMENT);
390 }
391
392 /*
393 * Return some information to the caller
394 */
395 if (WasDirty != NULL)
396 {
397 *WasDirty = Pte & PA_DIRTY;
398 }
399 if (Page != NULL)
400 {
401 *Page = PTE_TO_PFN(Pte);
402 }
403 }
404
405 VOID
406 NTAPI
407 MmRawDeleteVirtualMapping(PVOID Address)
408 {
409 PULONG Pt;
410
411 Pt = MmGetPageTableForProcess(NULL, Address, FALSE);
412 if (Pt && *Pt)
413 {
414 /*
415 * Set the entry to zero
416 */
417 InterlockedExchangePte(Pt, 0);
418 MiFlushTlb(Pt, Address);
419 }
420 }
421
422 VOID
423 NTAPI
424 MmDeleteVirtualMapping(PEPROCESS Process, PVOID Address, BOOLEAN FreePage,
425 BOOLEAN* WasDirty, PPFN_TYPE Page)
426 /*
427 * FUNCTION: Delete a virtual mapping
428 */
429 {
430 BOOLEAN WasValid = FALSE;
431 PFN_TYPE Pfn;
432 ULONG Pte;
433 PULONG Pt;
434
435 DPRINT("MmDeleteVirtualMapping(%x, %x, %d, %x, %x)\n",
436 Process, Address, FreePage, WasDirty, Page);
437
438 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
439
440 if (Pt == NULL)
441 {
442 if (WasDirty != NULL)
443 {
444 *WasDirty = FALSE;
445 }
446 if (Page != NULL)
447 {
448 *Page = 0;
449 }
450 return;
451 }
452
453 /*
454 * Atomically set the entry to zero and get the old value.
455 */
456 Pte = InterlockedExchangePte(Pt, 0);
457
458 MiFlushTlb(Pt, Address);
459
460 WasValid = (PAGE_MASK(Pte) != 0);
461 if (WasValid)
462 {
463 Pfn = PTE_TO_PFN(Pte);
464 }
465 else
466 {
467 Pfn = 0;
468 }
469
470 if (FreePage && WasValid)
471 {
472 MmReleasePageMemoryConsumer(MC_NPPOOL, Pfn);
473 }
474
475 /*
476 * Return some information to the caller
477 */
478 if (WasDirty != NULL)
479 {
480 *WasDirty = Pte & PA_DIRTY ? TRUE : FALSE;
481 }
482 if (Page != NULL)
483 {
484 *Page = Pfn;
485 }
486 }
487
488 VOID
489 NTAPI
490 MmDeletePageFileMapping(PEPROCESS Process, PVOID Address,
491 SWAPENTRY* SwapEntry)
492 /*
493 * FUNCTION: Delete a virtual mapping
494 */
495 {
496 ULONG Pte;
497 PULONG Pt;
498
499 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
500
501 if (Pt == NULL)
502 {
503 *SwapEntry = 0;
504 return;
505 }
506
507 /*
508 * Atomically set the entry to zero and get the old value.
509 */
510 Pte = InterlockedExchangePte(Pt, 0);
511
512 MiFlushTlb(Pt, Address);
513
514 /*
515 * Return some information to the caller
516 */
517 *SwapEntry = Pte >> 1;
518 }
519
520 BOOLEAN
521 Mmi386MakeKernelPageTableGlobal(PVOID PAddress)
522 {
523 PULONG Pt, Pde;
524 Pde = (PULONG)MiAddressToPde(PAddress);
525 if (*Pde == 0)
526 {
527 Pt = MmGetPageTableForProcess(NULL, PAddress, FALSE);
528 if (Pt != NULL)
529 {
530 return TRUE;
531 }
532 }
533 return(FALSE);
534 }
535
536 BOOLEAN
537 NTAPI
538 MmIsDirtyPage(PEPROCESS Process, PVOID Address)
539 {
540 return MmGetPageEntryForProcess(Process, Address) & PA_DIRTY ? TRUE : FALSE;
541 }
542
543 VOID
544 NTAPI
545 MmSetCleanPage(PEPROCESS Process, PVOID Address)
546 {
547 PULONG Pt;
548 ULONG Pte;
549
550 if (Address < MmSystemRangeStart && Process == NULL)
551 {
552 DPRINT1("MmSetCleanPage is called for user space without a process.\n");
553 KeBugCheck(MEMORY_MANAGEMENT);
554 }
555
556 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
557
558 if (Pt == NULL)
559 {
560 KeBugCheck(MEMORY_MANAGEMENT);
561 }
562
563 do
564 {
565 Pte = *Pt;
566 } while (Pte != InterlockedCompareExchangePte(Pt, Pte & ~PA_DIRTY, Pte));
567
568 if (Pte & PA_DIRTY)
569 {
570 MiFlushTlb(Pt, Address);
571 }
572 else
573 {
574 MmUnmapPageTable(Pt);
575 }
576 }
577
578 VOID
579 NTAPI
580 MmSetDirtyPage(PEPROCESS Process, PVOID Address)
581 {
582 PULONG Pt;
583 ULONG Pte;
584
585 if (Address < MmSystemRangeStart && Process == NULL)
586 {
587 DPRINT1("MmSetDirtyPage is called for user space without a process.\n");
588 KeBugCheck(MEMORY_MANAGEMENT);
589 }
590
591 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
592 if (Pt == NULL)
593 {
594 KeBugCheck(MEMORY_MANAGEMENT);
595 }
596
597 do
598 {
599 Pte = *Pt;
600 } while (Pte != InterlockedCompareExchangePte(Pt, Pte | PA_DIRTY, Pte));
601 if (!(Pte & PA_DIRTY))
602 {
603 MiFlushTlb(Pt, Address);
604 }
605 else
606 {
607 MmUnmapPageTable(Pt);
608 }
609 }
610
611 VOID
612 NTAPI
613 MmEnableVirtualMapping(PEPROCESS Process, PVOID Address)
614 {
615 PULONG Pt;
616 ULONG Pte;
617
618 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
619 if (Pt == NULL)
620 {
621 KeBugCheck(MEMORY_MANAGEMENT);
622 }
623
624 do
625 {
626 Pte = *Pt;
627 } while (Pte != InterlockedCompareExchangePte(Pt, Pte | PA_PRESENT, Pte));
628 if (!(Pte & PA_PRESENT))
629 {
630 MiFlushTlb(Pt, Address);
631 }
632 else
633 {
634 MmUnmapPageTable(Pt);
635 }
636 }
637
638 BOOLEAN
639 NTAPI
640 MmIsPagePresent(PEPROCESS Process, PVOID Address)
641 {
642 return MmGetPageEntryForProcess(Process, Address) & PA_PRESENT;
643 }
644
645 BOOLEAN
646 NTAPI
647 MmIsPageSwapEntry(PEPROCESS Process, PVOID Address)
648 {
649 ULONG Entry;
650 Entry = MmGetPageEntryForProcess(Process, Address);
651 return !(Entry & PA_PRESENT) && (Entry & 0x800) && Entry != 0;
652 }
653
654 NTSTATUS
655 NTAPI
656 MmCreatePageFileMapping(PEPROCESS Process,
657 PVOID Address,
658 SWAPENTRY SwapEntry)
659 {
660 PULONG Pt;
661 ULONG Pte;
662
663 if (Process == NULL && Address < MmSystemRangeStart)
664 {
665 DPRINT1("No process\n");
666 KeBugCheck(MEMORY_MANAGEMENT);
667 }
668 if (Process != NULL && Address >= MmSystemRangeStart)
669 {
670 DPRINT1("Setting kernel address with process context\n");
671 KeBugCheck(MEMORY_MANAGEMENT);
672 }
673
674 if (SwapEntry & (1 << 31))
675 {
676 KeBugCheck(MEMORY_MANAGEMENT);
677 }
678
679 Pt = MmGetPageTableForProcess(Process, Address, TRUE);
680 if (Pt == NULL)
681 {
682 KeBugCheck(MEMORY_MANAGEMENT);
683 }
684 Pte = *Pt;
685 InterlockedExchangePte(Pt, SwapEntry << 1);
686 if (Pte != 0)
687 {
688 MiFlushTlb(Pt, Address);
689 }
690 else
691 {
692 MmUnmapPageTable(Pt);
693 }
694
695 return(STATUS_SUCCESS);
696 }
697
698
699 NTSTATUS
700 NTAPI
701 MmCreateVirtualMappingUnsafe(PEPROCESS Process,
702 PVOID Address,
703 ULONG flProtect,
704 PPFN_TYPE Pages,
705 ULONG PageCount)
706 {
707 ULONG Attributes;
708 PVOID Addr;
709 ULONG i;
710 ULONG oldPdeOffset, PdeOffset;
711 PULONG Pt = NULL;
712 ULONG Pte;
713 BOOLEAN NoExecute = FALSE;
714
715 DPRINT("MmCreateVirtualMappingUnsafe(%x, %x, %x, %x (%x), %d)\n",
716 Process, Address, flProtect, Pages, *Pages, PageCount);
717
718 if (Process == NULL)
719 {
720 if (Address < MmSystemRangeStart)
721 {
722 DPRINT1("No process\n");
723 KeBugCheck(MEMORY_MANAGEMENT);
724 }
725 if (PageCount > 0x10000 ||
726 (ULONG_PTR) Address / PAGE_SIZE + PageCount > 0x100000)
727 {
728 DPRINT1("Page count too large\n");
729 KeBugCheck(MEMORY_MANAGEMENT);
730 }
731 }
732 else
733 {
734 if (Address >= MmSystemRangeStart)
735 {
736 DPRINT1("Setting kernel address with process context\n");
737 KeBugCheck(MEMORY_MANAGEMENT);
738 }
739 if (PageCount > (ULONG_PTR)MmSystemRangeStart / PAGE_SIZE ||
740 (ULONG_PTR) Address / PAGE_SIZE + PageCount >
741 (ULONG_PTR)MmSystemRangeStart / PAGE_SIZE)
742 {
743 DPRINT1("Page Count too large\n");
744 KeBugCheck(MEMORY_MANAGEMENT);
745 }
746 }
747
748 Attributes = ProtectToPTE(flProtect);
749 if (Attributes & 0x80000000)
750 {
751 NoExecute = TRUE;
752 }
753 Attributes &= 0xfff;
754 if (Address >= MmSystemRangeStart)
755 {
756 Attributes &= ~PA_USER;
757 }
758 else
759 {
760 Attributes |= PA_USER;
761 }
762
763 Addr = Address;
764 oldPdeOffset = ADDR_TO_PDE_OFFSET(Addr) + 1;
765 for (i = 0; i < PageCount; i++, Addr = (PVOID)((ULONG_PTR)Addr + PAGE_SIZE))
766 {
767 if (!(Attributes & PA_PRESENT) && Pages[i] != 0)
768 {
769 DPRINT1("Setting physical address but not allowing access at address "
770 "0x%.8X with attributes %x/%x.\n",
771 Addr, Attributes, flProtect);
772 KeBugCheck(MEMORY_MANAGEMENT);
773 }
774 PdeOffset = ADDR_TO_PDE_OFFSET(Addr);
775 if (oldPdeOffset != PdeOffset)
776 {
777 MmUnmapPageTable(Pt);
778 Pt = MmGetPageTableForProcess(Process, Addr, TRUE);
779 if (Pt == NULL)
780 {
781 KeBugCheck(MEMORY_MANAGEMENT);
782 }
783 }
784 else
785 {
786 Pt++;
787 }
788 oldPdeOffset = PdeOffset;
789
790 Pte = *Pt;
791 if (PAGE_MASK(Pte) != 0 && !(Pte & PA_PRESENT) && (Pte & 0x800))
792 {
793 DPRINT1("Bad PTE %lx\n", Pte);
794 KeBugCheck(MEMORY_MANAGEMENT);
795 }
796 InterlockedExchangePte(Pt, PFN_TO_PTE(Pages[i]) | Attributes);
797 if (Pte != 0)
798 {
799 if (Address > MmSystemRangeStart ||
800 (Pt >= (PULONG)PAGETABLE_MAP && Pt < (PULONG)PAGETABLE_MAP + 1024*1024))
801 {
802 MiFlushTlb(Pt, Address);
803 }
804 }
805 }
806 if (Addr > Address)
807 {
808 MmUnmapPageTable(Pt);
809 }
810
811 return(STATUS_SUCCESS);
812 }
813
814 NTSTATUS
815 NTAPI
816 MmCreateVirtualMapping(PEPROCESS Process,
817 PVOID Address,
818 ULONG flProtect,
819 PPFN_TYPE Pages,
820 ULONG PageCount)
821 {
822 ULONG i;
823
824 for (i = 0; i < PageCount; i++)
825 {
826 if (!MmIsPageInUse(Pages[i]))
827 {
828 DPRINT1("Page at address %x not in use\n", PFN_TO_PTE(Pages[i]));
829 KeBugCheck(MEMORY_MANAGEMENT);
830 }
831 }
832
833 return(MmCreateVirtualMappingUnsafe(Process,
834 Address,
835 flProtect,
836 Pages,
837 PageCount));
838 }
839
840 ULONG
841 NTAPI
842 MmGetPageProtect(PEPROCESS Process, PVOID Address)
843 {
844 ULONG Entry;
845 ULONG Protect;
846
847 Entry = MmGetPageEntryForProcess(Process, Address);
848
849
850 if (!(Entry & PA_PRESENT))
851 {
852 Protect = PAGE_NOACCESS;
853 }
854 else
855 {
856 if (Entry & PA_READWRITE)
857 {
858 Protect = PAGE_READWRITE;
859 }
860 else
861 {
862 Protect = PAGE_EXECUTE_READ;
863 }
864 if (Entry & PA_CD)
865 {
866 Protect |= PAGE_NOCACHE;
867 }
868 if (Entry & PA_WT)
869 {
870 Protect |= PAGE_WRITETHROUGH;
871 }
872 if (!(Entry & PA_USER))
873 {
874 Protect |= PAGE_SYSTEM;
875 }
876
877 }
878 return(Protect);
879 }
880
881 VOID
882 NTAPI
883 MmSetPageProtect(PEPROCESS Process, PVOID Address, ULONG flProtect)
884 {
885 ULONG Attributes = 0;
886 BOOLEAN NoExecute = FALSE;
887 PULONG Pt;
888
889 DPRINT("MmSetPageProtect(Process %x Address %x flProtect %x)\n",
890 Process, Address, flProtect);
891
892 Attributes = ProtectToPTE(flProtect);
893
894 if (Attributes & 0x80000000)
895 {
896 NoExecute = TRUE;
897 }
898 Attributes &= 0xfff;
899 if (Address >= MmSystemRangeStart)
900 {
901 Attributes &= ~PA_USER;
902 }
903 else
904 {
905 Attributes |= PA_USER;
906 }
907
908 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
909 if (Pt == NULL)
910 {
911 KeBugCheck(MEMORY_MANAGEMENT);
912 }
913 InterlockedExchangePte(Pt, PAGE_MASK(*Pt) | Attributes | (*Pt & (PA_ACCESSED|PA_DIRTY)));
914 MiFlushTlb(Pt, Address);
915 }
916
917 /*
918 * @implemented
919 */
920 PHYSICAL_ADDRESS NTAPI
921 MmGetPhysicalAddress(PVOID vaddr)
922 /*
923 * FUNCTION: Returns the physical address corresponding to a virtual address
924 */
925 {
926 PHYSICAL_ADDRESS p;
927 ULONG Pte;
928
929 DPRINT("MmGetPhysicalAddress(vaddr %x)\n", vaddr);
930 Pte = MmGetPageEntryForProcess(NULL, vaddr);
931 if (Pte != 0 && Pte & PA_PRESENT)
932 {
933 p.QuadPart = PAGE_MASK(Pte);
934 p.u.LowPart |= (ULONG_PTR)vaddr & (PAGE_SIZE - 1);
935 }
936 else
937 {
938 p.QuadPart = 0;
939 }
940 return p;
941 }
942
943 VOID
944 NTAPI
945 MmUpdatePageDir(PEPROCESS Process, PVOID Address, ULONG Size)
946 {
947 ULONG StartOffset, EndOffset, Offset;
948 PULONG Pde;
949
950 //
951 // Check if the process isn't there anymore
952 // This is probably a bad sign, since it means the caller is setting cr3 to
953 // 0 or something...
954 //
955 if ((PTE_TO_PFN(Process->Pcb.DirectoryTableBase[0]) == 0) && (Process != PsGetCurrentProcess()))
956 {
957 DPRINT1("Process: %16s is dead: %p\n", Process->ImageFileName, Process->Pcb.DirectoryTableBase[0]);
958 ASSERT(FALSE);
959 return;
960 }
961
962 if (Address < MmSystemRangeStart)
963 {
964 KeBugCheck(MEMORY_MANAGEMENT);
965 }
966
967 StartOffset = ADDR_TO_PDE_OFFSET(Address);
968 EndOffset = ADDR_TO_PDE_OFFSET((PVOID)((ULONG_PTR)Address + Size));
969
970 if (Process != NULL && Process != PsGetCurrentProcess())
971 {
972 Pde = MmCreateHyperspaceMapping(PTE_TO_PFN(Process->Pcb.DirectoryTableBase[0]));
973 }
974 else
975 {
976 Pde = (PULONG)PAGEDIRECTORY_MAP;
977 }
978 for (Offset = StartOffset; Offset <= EndOffset; Offset++)
979 {
980 if (Offset != ADDR_TO_PDE_OFFSET(PAGETABLE_MAP))
981 {
982 InterlockedCompareExchangePte(&Pde[Offset], MmGlobalKernelPageDirectory[Offset], 0);
983 }
984 }
985 if (Pde != (PULONG)PAGEDIRECTORY_MAP)
986 {
987 MmDeleteHyperspaceMapping(Pde);
988 }
989 }
990
991 VOID
992 INIT_FUNCTION
993 NTAPI
994 MmInitGlobalKernelPageDirectory(VOID)
995 {
996 ULONG i;
997 PULONG CurrentPageDirectory = (PULONG)PAGEDIRECTORY_MAP;
998
999 DPRINT("MmInitGlobalKernelPageDirectory()\n");
1000
1001 for (i = ADDR_TO_PDE_OFFSET(MmSystemRangeStart); i < 1024; i++)
1002 {
1003 if (i != ADDR_TO_PDE_OFFSET(PAGETABLE_MAP) &&
1004 i != ADDR_TO_PDE_OFFSET(HYPERSPACE) &&
1005 0 == MmGlobalKernelPageDirectory[i] && 0 != CurrentPageDirectory[i])
1006 {
1007 MmGlobalKernelPageDirectory[i] = CurrentPageDirectory[i];
1008 }
1009 }
1010 }
1011
1012 /* EOF */