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