Merge from amd64-branch:
[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 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 (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 MmCreatePageFileMapping(PEPROCESS Process,
661 PVOID Address,
662 SWAPENTRY SwapEntry)
663 {
664 PULONG Pt;
665 ULONG Pte;
666
667 if (Process == NULL && Address < MmSystemRangeStart)
668 {
669 DPRINT1("No process\n");
670 KeBugCheck(MEMORY_MANAGEMENT);
671 }
672 if (Process != NULL && Address >= MmSystemRangeStart)
673 {
674 DPRINT1("Setting kernel address with process context\n");
675 KeBugCheck(MEMORY_MANAGEMENT);
676 }
677
678 if (SwapEntry & (1 << 31))
679 {
680 KeBugCheck(MEMORY_MANAGEMENT);
681 }
682
683 Pt = MmGetPageTableForProcess(Process, Address, TRUE);
684 if (Pt == NULL)
685 {
686 KeBugCheck(MEMORY_MANAGEMENT);
687 }
688 Pte = *Pt;
689 InterlockedExchangePte(Pt, SwapEntry << 1);
690 if (Pte != 0)
691 {
692 MiFlushTlb(Pt, Address);
693 }
694 else
695 {
696 MmUnmapPageTable(Pt);
697 }
698
699 return(STATUS_SUCCESS);
700 }
701
702
703 NTSTATUS
704 NTAPI
705 MmCreateVirtualMappingUnsafe(PEPROCESS Process,
706 PVOID Address,
707 ULONG flProtect,
708 PPFN_TYPE Pages,
709 ULONG PageCount)
710 {
711 ULONG Attributes;
712 PVOID Addr;
713 ULONG i;
714 ULONG oldPdeOffset, PdeOffset;
715 PULONG Pt = NULL;
716 ULONG Pte;
717 BOOLEAN NoExecute = FALSE;
718
719 DPRINT("MmCreateVirtualMappingUnsafe(%x, %x, %x, %x (%x), %d)\n",
720 Process, Address, flProtect, Pages, *Pages, PageCount);
721
722 if (Process == NULL)
723 {
724 if (Address < MmSystemRangeStart)
725 {
726 DPRINT1("No process\n");
727 KeBugCheck(MEMORY_MANAGEMENT);
728 }
729 if (PageCount > 0x10000 ||
730 (ULONG_PTR) Address / PAGE_SIZE + PageCount > 0x100000)
731 {
732 DPRINT1("Page count too large\n");
733 KeBugCheck(MEMORY_MANAGEMENT);
734 }
735 }
736 else
737 {
738 if (Address >= MmSystemRangeStart)
739 {
740 DPRINT1("Setting kernel address with process context\n");
741 KeBugCheck(MEMORY_MANAGEMENT);
742 }
743 if (PageCount > (ULONG_PTR)MmSystemRangeStart / PAGE_SIZE ||
744 (ULONG_PTR) Address / PAGE_SIZE + PageCount >
745 (ULONG_PTR)MmSystemRangeStart / PAGE_SIZE)
746 {
747 DPRINT1("Page Count too large\n");
748 KeBugCheck(MEMORY_MANAGEMENT);
749 }
750 }
751
752 Attributes = ProtectToPTE(flProtect);
753 if (Attributes & 0x80000000)
754 {
755 NoExecute = TRUE;
756 }
757 Attributes &= 0xfff;
758 if (Address >= MmSystemRangeStart)
759 {
760 Attributes &= ~PA_USER;
761 if (Ke386GlobalPagesEnabled)
762 {
763 Attributes |= PA_GLOBAL;
764 }
765 }
766 else
767 {
768 Attributes |= PA_USER;
769 }
770
771 Addr = Address;
772 oldPdeOffset = ADDR_TO_PDE_OFFSET(Addr) + 1;
773 for (i = 0; i < PageCount; i++, Addr = (PVOID)((ULONG_PTR)Addr + PAGE_SIZE))
774 {
775 if (!(Attributes & PA_PRESENT) && Pages[i] != 0)
776 {
777 DPRINT1("Setting physical address but not allowing access at address "
778 "0x%.8X with attributes %x/%x.\n",
779 Addr, Attributes, flProtect);
780 KeBugCheck(MEMORY_MANAGEMENT);
781 }
782 PdeOffset = ADDR_TO_PDE_OFFSET(Addr);
783 if (oldPdeOffset != PdeOffset)
784 {
785 MmUnmapPageTable(Pt);
786 Pt = MmGetPageTableForProcess(Process, Addr, TRUE);
787 if (Pt == NULL)
788 {
789 KeBugCheck(MEMORY_MANAGEMENT);
790 }
791 }
792 else
793 {
794 Pt++;
795 }
796 oldPdeOffset = PdeOffset;
797
798 Pte = *Pt;
799 if (PAGE_MASK(Pte) != 0 && !(Pte & PA_PRESENT) && (Pte & 0x800))
800 {
801 DPRINT1("Bad PTE %lx\n", Pte);
802 KeBugCheck(MEMORY_MANAGEMENT);
803 }
804 InterlockedExchangePte(Pt, PFN_TO_PTE(Pages[i]) | Attributes);
805 if (Pte != 0)
806 {
807 if (Address > MmSystemRangeStart ||
808 (Pt >= (PULONG)PAGETABLE_MAP && Pt < (PULONG)PAGETABLE_MAP + 1024*1024))
809 {
810 MiFlushTlb(Pt, Address);
811 }
812 }
813 }
814 if (Addr > Address)
815 {
816 MmUnmapPageTable(Pt);
817 }
818
819 return(STATUS_SUCCESS);
820 }
821
822 NTSTATUS
823 NTAPI
824 MmCreateVirtualMapping(PEPROCESS Process,
825 PVOID Address,
826 ULONG flProtect,
827 PPFN_TYPE Pages,
828 ULONG PageCount)
829 {
830 ULONG i;
831
832 for (i = 0; i < PageCount; i++)
833 {
834 if (!MmIsPageInUse(Pages[i]))
835 {
836 DPRINT1("Page at address %x not in use\n", PFN_TO_PTE(Pages[i]));
837 KeBugCheck(MEMORY_MANAGEMENT);
838 }
839 }
840
841 return(MmCreateVirtualMappingUnsafe(Process,
842 Address,
843 flProtect,
844 Pages,
845 PageCount));
846 }
847
848 ULONG
849 NTAPI
850 MmGetPageProtect(PEPROCESS Process, PVOID Address)
851 {
852 ULONG Entry;
853 ULONG Protect;
854
855 Entry = MmGetPageEntryForProcess(Process, Address);
856
857
858 if (!(Entry & PA_PRESENT))
859 {
860 Protect = PAGE_NOACCESS;
861 }
862 else
863 {
864 if (Entry & PA_READWRITE)
865 {
866 Protect = PAGE_READWRITE;
867 }
868 else
869 {
870 Protect = PAGE_EXECUTE_READ;
871 }
872 if (Entry & PA_CD)
873 {
874 Protect |= PAGE_NOCACHE;
875 }
876 if (Entry & PA_WT)
877 {
878 Protect |= PAGE_WRITETHROUGH;
879 }
880 if (!(Entry & PA_USER))
881 {
882 Protect |= PAGE_SYSTEM;
883 }
884
885 }
886 return(Protect);
887 }
888
889 VOID
890 NTAPI
891 MmSetPageProtect(PEPROCESS Process, PVOID Address, ULONG flProtect)
892 {
893 ULONG Attributes = 0;
894 BOOLEAN NoExecute = FALSE;
895 PULONG Pt;
896
897 DPRINT("MmSetPageProtect(Process %x Address %x flProtect %x)\n",
898 Process, Address, flProtect);
899
900 Attributes = ProtectToPTE(flProtect);
901
902 if (Attributes & 0x80000000)
903 {
904 NoExecute = TRUE;
905 }
906 Attributes &= 0xfff;
907 if (Address >= MmSystemRangeStart)
908 {
909 Attributes &= ~PA_USER;
910 if (Ke386GlobalPagesEnabled)
911 {
912 Attributes |= PA_GLOBAL;
913 }
914 }
915 else
916 {
917 Attributes |= PA_USER;
918 }
919
920 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
921 if (Pt == NULL)
922 {
923 KeBugCheck(MEMORY_MANAGEMENT);
924 }
925 InterlockedExchangePte(Pt, PAGE_MASK(*Pt) | Attributes | (*Pt & (PA_ACCESSED|PA_DIRTY)));
926 MiFlushTlb(Pt, Address);
927 }
928
929 /*
930 * @implemented
931 */
932 PHYSICAL_ADDRESS NTAPI
933 MmGetPhysicalAddress(PVOID vaddr)
934 /*
935 * FUNCTION: Returns the physical address corresponding to a virtual address
936 */
937 {
938 PHYSICAL_ADDRESS p;
939 ULONG Pte;
940
941 DPRINT("MmGetPhysicalAddress(vaddr %x)\n", vaddr);
942 Pte = MmGetPageEntryForProcess(NULL, vaddr);
943 if (Pte != 0 && Pte & PA_PRESENT)
944 {
945 p.QuadPart = PAGE_MASK(Pte);
946 p.u.LowPart |= (ULONG_PTR)vaddr & (PAGE_SIZE - 1);
947 }
948 else
949 {
950 p.QuadPart = 0;
951 }
952 return p;
953 }
954
955 VOID
956 NTAPI
957 MmUpdatePageDir(PEPROCESS Process, PVOID Address, ULONG Size)
958 {
959 ULONG StartOffset, EndOffset, Offset;
960 PULONG Pde;
961
962 //
963 // Check if the process isn't there anymore
964 // This is probably a bad sign, since it means the caller is setting cr3 to
965 // 0 or something...
966 //
967 if ((PTE_TO_PFN(Process->Pcb.DirectoryTableBase[0]) == 0) && (Process != PsGetCurrentProcess()))
968 {
969 DPRINT1("Process: %16s is dead: %p\n", Process->ImageFileName, Process->Pcb.DirectoryTableBase[0]);
970 ASSERT(FALSE);
971 return;
972 }
973
974 if (Address < MmSystemRangeStart)
975 {
976 KeBugCheck(MEMORY_MANAGEMENT);
977 }
978
979 StartOffset = ADDR_TO_PDE_OFFSET(Address);
980 EndOffset = ADDR_TO_PDE_OFFSET((PVOID)((ULONG_PTR)Address + Size));
981
982 if (Process != NULL && Process != PsGetCurrentProcess())
983 {
984 Pde = MmCreateHyperspaceMapping(PTE_TO_PFN(Process->Pcb.DirectoryTableBase[0]));
985 }
986 else
987 {
988 Pde = (PULONG)PAGEDIRECTORY_MAP;
989 }
990 for (Offset = StartOffset; Offset <= EndOffset; Offset++)
991 {
992 if (Offset != ADDR_TO_PDE_OFFSET(PAGETABLE_MAP))
993 {
994 InterlockedCompareExchangePte(&Pde[Offset], MmGlobalKernelPageDirectory[Offset], 0);
995 }
996 }
997 if (Pde != (PULONG)PAGEDIRECTORY_MAP)
998 {
999 MmDeleteHyperspaceMapping(Pde);
1000 }
1001 }
1002
1003 extern MMPTE HyperTemplatePte;
1004
1005 VOID
1006 INIT_FUNCTION
1007 NTAPI
1008 MmInitGlobalKernelPageDirectory(VOID)
1009 {
1010 ULONG i;
1011 PULONG CurrentPageDirectory = (PULONG)PAGEDIRECTORY_MAP;
1012
1013 DPRINT("MmInitGlobalKernelPageDirectory()\n");
1014
1015 //
1016 // Setup template
1017 //
1018 HyperTemplatePte.u.Long = (PA_PRESENT | PA_READWRITE | PA_DIRTY | PA_ACCESSED);
1019 if (Ke386GlobalPagesEnabled) HyperTemplatePte.u.Long |= PA_GLOBAL;
1020
1021 for (i = ADDR_TO_PDE_OFFSET(MmSystemRangeStart); i < 1024; i++)
1022 {
1023 if (i != ADDR_TO_PDE_OFFSET(PAGETABLE_MAP) &&
1024 i != ADDR_TO_PDE_OFFSET(HYPERSPACE) &&
1025 0 == MmGlobalKernelPageDirectory[i] && 0 != CurrentPageDirectory[i])
1026 {
1027 MmGlobalKernelPageDirectory[i] = CurrentPageDirectory[i];
1028 if (Ke386GlobalPagesEnabled)
1029 {
1030 MmGlobalKernelPageDirectory[i] |= PA_GLOBAL;
1031 CurrentPageDirectory[i] |= PA_GLOBAL;
1032 }
1033 }
1034 }
1035 }
1036
1037 /* EOF */