- Fixed the freeing of memory from boot load drivers.
[reactos.git] / reactos / ntoskrnl / mm / i386 / page.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /* $Id: page.c,v 1.60 2003/10/12 17:05:48 hbirr Exp $
20 *
21 * PROJECT: ReactOS kernel
22 * FILE: ntoskrnl/mm/i386/page.c
23 * PURPOSE: low level memory managment manipulation
24 * PROGRAMER: David Welch (welch@cwcom.net)
25 * UPDATE HISTORY:
26 * 9/3/98: Created
27 */
28
29 /* INCLUDES ***************************************************************/
30
31 #include <ddk/ntddk.h>
32 #include <internal/mm.h>
33 #include <internal/i386/mm.h>
34 #include <internal/ex.h>
35 #include <internal/ps.h>
36
37 #define NDEBUG
38 #include <internal/debug.h>
39
40 /* GLOBALS *****************************************************************/
41
42 #define PA_BIT_PRESENT (0)
43 #define PA_BIT_READWRITE (1)
44 #define PA_BIT_USER (2)
45 #define PA_BIT_WT (3)
46 #define PA_BIT_CD (4)
47 #define PA_BIT_ACCESSED (5)
48 #define PA_BIT_DIRTY (6)
49
50 #define PA_PRESENT (1 << PA_BIT_PRESENT)
51 #define PA_READWRITE (1 << PA_BIT_READWRITE)
52 #define PA_USER (1 << PA_BIT_USER)
53 #define PA_DIRTY (1 << PA_BIT_DIRTY)
54 #define PA_WT (1 << PA_BIT_WT)
55 #define PA_CD (1 << PA_BIT_CD)
56 #define PA_ACCESSED (1 << PA_BIT_ACCESSED)
57 #define PA_DIRTY (1 << PA_BIT_DIRTY)
58
59 #define PAGETABLE_MAP (0xf0000000)
60 #define PAGEDIRECTORY_MAP (0xf0000000 + (PAGETABLE_MAP / (1024)))
61
62 ULONG MmGlobalKernelPageDirectory[1024] = {0, };
63
64 #define PTE_TO_PAGE(X) ((LARGE_INTEGER)(LONGLONG)(PAGE_MASK(X)))
65
66 /* FUNCTIONS ***************************************************************/
67
68 PULONG
69 MmGetPageDirectory(VOID)
70 {
71 unsigned int page_dir=0;
72 __asm__("movl %%cr3,%0\n\t"
73 : "=r" (page_dir));
74 return((PULONG)page_dir);
75 }
76
77 static ULONG
78 ProtectToPTE(ULONG flProtect)
79 {
80 ULONG Attributes = 0;
81
82 if (flProtect & PAGE_NOACCESS || flProtect & PAGE_GUARD)
83 {
84 Attributes = 0;
85 }
86 else if (flProtect & PAGE_READWRITE || flProtect & PAGE_EXECUTE_READWRITE)
87 {
88 Attributes = PA_PRESENT | PA_READWRITE;
89 }
90 else if (flProtect & PAGE_READONLY || flProtect & PAGE_EXECUTE ||
91 flProtect & PAGE_EXECUTE_READ)
92 {
93 Attributes = PA_PRESENT;
94 }
95 else
96 {
97 DPRINT1("Unknown main protection type.\n");
98 KEBUGCHECK(0);
99 }
100 if (!(flProtect & PAGE_SYSTEM))
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 #define ADDR_TO_PAGE_TABLE(v) (((ULONG)(v)) / (4 * 1024 * 1024))
116
117 #define ADDR_TO_PDE(v) (PULONG)(PAGEDIRECTORY_MAP + \
118 ((((ULONG)(v)) / (1024 * 1024))&(~0x3)))
119 #define ADDR_TO_PTE(v) (PULONG)(PAGETABLE_MAP + ((((ULONG)v / 1024))&(~0x3)))
120
121 #define ADDR_TO_PDE_OFFSET(v) ((((ULONG)(v)) / (4 * 1024 * 1024)))
122
123 NTSTATUS Mmi386ReleaseMmInfo(PEPROCESS Process)
124 {
125 PUSHORT LdtDescriptor;
126 ULONG LdtBase;
127
128 DPRINT("Mmi386ReleaseMmInfo(Process %x)\n",Process);
129
130 LdtDescriptor = (PUSHORT) &Process->Pcb.LdtDescriptor[0];
131 LdtBase = LdtDescriptor[1] |
132 ((LdtDescriptor[2] & 0xff) << 16) |
133 ((LdtDescriptor[3] & ~0xff) << 16);
134
135 DPRINT("LdtBase: %x\n", LdtBase);
136
137 if (LdtBase)
138 {
139 ExFreePool((PVOID) LdtBase);
140 }
141
142 MmReleasePageMemoryConsumer(MC_NPPOOL, Process->Pcb.DirectoryTableBase);
143 Process->Pcb.DirectoryTableBase.QuadPart = 0LL;
144
145 DPRINT("Finished Mmi386ReleaseMmInfo()\n");
146 return(STATUS_SUCCESS);
147 }
148
149 NTSTATUS MmCopyMmInfo(PEPROCESS Src, PEPROCESS Dest)
150 {
151 PHYSICAL_ADDRESS PhysPageDirectory;
152 PULONG PageDirectory;
153 PKPROCESS KProcess = &Dest->Pcb;
154
155 DPRINT("MmCopyMmInfo(Src %x, Dest %x)\n", Src, Dest);
156
157 PageDirectory = ExAllocatePage();
158 if (PageDirectory == NULL)
159 {
160 return(STATUS_UNSUCCESSFUL);
161 }
162 PhysPageDirectory = MmGetPhysicalAddress(PageDirectory);
163 KProcess->DirectoryTableBase = PhysPageDirectory;
164
165 memset(PageDirectory,0, ADDR_TO_PDE_OFFSET(KERNEL_BASE) * sizeof(ULONG));
166 memcpy(PageDirectory + ADDR_TO_PDE_OFFSET(KERNEL_BASE),
167 MmGlobalKernelPageDirectory + ADDR_TO_PDE_OFFSET(KERNEL_BASE),
168 (1024 - ADDR_TO_PDE_OFFSET(KERNEL_BASE)) * sizeof(ULONG));
169
170 DPRINT("Addr %x\n",PAGETABLE_MAP / (4*1024*1024));
171 PageDirectory[PAGETABLE_MAP / (4*1024*1024)] =
172 PhysPageDirectory.u.LowPart | PA_PRESENT | PA_READWRITE;
173
174 ExUnmapPage(PageDirectory);
175
176 DPRINT("Finished MmCopyMmInfo()\n");
177 return(STATUS_SUCCESS);
178 }
179
180 VOID MmDeletePageTable(PEPROCESS Process, PVOID Address)
181 {
182 PEPROCESS CurrentProcess = PsGetCurrentProcess();
183
184 if (Process != NULL && Process != CurrentProcess)
185 {
186 KeAttachProcess(Process);
187 }
188 *(ADDR_TO_PDE(Address)) = 0;
189 if (Address >= (PVOID)KERNEL_BASE)
190 {
191 KEBUGCHECK(0);
192 // MmGlobalKernelPageDirectory[ADDR_TO_PDE_OFFSET(Address)] = 0;
193 }
194 FLUSH_TLB;
195 if (Process != NULL && Process != CurrentProcess)
196 {
197 KeDetachProcess();
198 }
199 }
200
201 VOID MmFreePageTable(PEPROCESS Process, PVOID Address)
202 {
203 PEPROCESS CurrentProcess = PsGetCurrentProcess();
204 PULONG PageTable;
205 ULONG i;
206 ULONG npage;
207
208 if (Process != NULL && Process != CurrentProcess)
209 {
210 KeAttachProcess(Process);
211 }
212
213 PageTable = (PULONG)PAGE_ROUND_DOWN((PVOID)ADDR_TO_PTE(Address));
214 for (i = 0; i < 1024; i++)
215 {
216 if (PageTable[i] != 0)
217 {
218 DbgPrint("Page table entry not clear at %x/%x (is %x)\n",
219 ((ULONG)Address / 4*1024*1024), i, PageTable[i]);
220 KEBUGCHECK(0);
221 }
222 }
223 npage = *(ADDR_TO_PDE(Address));
224 *(ADDR_TO_PDE(Address)) = 0;
225 FLUSH_TLB;
226
227 if (Address >= (PVOID)KERNEL_BASE)
228 {
229 // MmGlobalKernelPageDirectory[ADDR_TO_PDE_OFFSET(Address)] = 0;
230 KEBUGCHECK(0);
231 }
232 else
233 {
234 MmReleasePageMemoryConsumer(MC_NPPOOL, PTE_TO_PAGE(npage));
235 }
236 if (Process != NULL && Process != CurrentProcess)
237 {
238 KeDetachProcess();
239 }
240 }
241
242 NTSTATUS MmGetPageEntry2(PVOID PAddress, PULONG* Pte, BOOLEAN MayWait)
243 /*
244 * FUNCTION: Get a pointer to the page table entry for a virtual address
245 */
246 {
247 PULONG Pde, kePde;
248 PHYSICAL_ADDRESS npage;
249 BOOLEAN Free = FALSE;
250 NTSTATUS Status;
251 KIRQL oldIrql;
252
253 DPRINT("MmGetPageEntry(Address %x)\n", PAddress);
254
255 Pde = ADDR_TO_PDE(PAddress);
256 if (*Pde == 0)
257 {
258 if (PAddress >= (PVOID)KERNEL_BASE)
259 {
260 kePde = MmGlobalKernelPageDirectory + ADDR_TO_PDE_OFFSET(PAddress);
261 oldIrql = KeRaiseIrqlToSynchLevel();
262 if (*kePde != 0)
263 {
264 *Pde = *kePde;
265 FLUSH_TLB;
266 }
267 else
268 {
269 KeLowerIrql(oldIrql);
270 Status = MmRequestPageMemoryConsumer(MC_NPPOOL, MayWait, &npage);
271 if (!NT_SUCCESS(Status))
272 {
273 KEBUGCHECK(0);
274 }
275 oldIrql = KeRaiseIrqlToSynchLevel();
276 /* An other thread can set this pde entry, we must check again */
277 if (*kePde == 0)
278 {
279 *kePde = npage.u.LowPart | PA_PRESENT | PA_READWRITE;
280 }
281 else
282 {
283 Free = TRUE;
284 }
285 *Pde = *kePde;
286 FLUSH_TLB;
287 }
288 KeLowerIrql(oldIrql);
289 }
290 else
291 {
292 Status = MmRequestPageMemoryConsumer(MC_NPPOOL, MayWait, &npage);
293 if (!NT_SUCCESS(Status))
294 {
295 return(Status);
296 }
297 *Pde = npage.u.LowPart | PA_PRESENT | PA_READWRITE | PA_USER;
298 FLUSH_TLB;
299 }
300 }
301 *Pte = (PULONG)ADDR_TO_PTE(PAddress);
302 if (Free)
303 {
304 MmReleasePageMemoryConsumer(MC_NPPOOL, npage);
305 }
306 return STATUS_SUCCESS;
307 }
308
309 ULONG MmGetPageEntryForProcess(PEPROCESS Process, PVOID Address)
310 {
311 ULONG Entry;
312 PEPROCESS CurrentProcess = PsGetCurrentProcess();
313
314 if (Process != NULL && Process != CurrentProcess)
315 {
316 KeAttachProcess(Process);
317 }
318 Entry = *MmGetPageEntry(Address);
319 if (Process != NULL && Process != CurrentProcess)
320 {
321 KeDetachProcess();
322 }
323 return(Entry);
324 }
325
326 ULONG MmGetPageEntry1(PVOID PAddress)
327 /*
328 * FUNCTION: Get a pointer to the page table entry for a virtual address
329 */
330 {
331 PULONG Pde, kePde;
332 ULONG Entry = 0;
333
334 DPRINT("MmGetPageEntry(Address %x)\n", PAddress);
335
336 Pde = ADDR_TO_PDE(PAddress);
337 if (*Pde == 0)
338 {
339 if (PAddress >= (PVOID)KERNEL_BASE)
340 {
341 kePde = MmGlobalKernelPageDirectory + ADDR_TO_PDE_OFFSET(PAddress);
342 if (*kePde != 0)
343 {
344 *Pde = *kePde;
345 FLUSH_TLB;
346 Entry = *(PULONG)ADDR_TO_PTE(PAddress);
347 }
348 }
349 }
350 else
351 {
352 Entry = *(PULONG)ADDR_TO_PTE(PAddress);
353 }
354 return Entry;
355 }
356
357 ULONG MmGetPageEntryForProcess1(PEPROCESS Process, PVOID Address)
358 {
359 ULONG Entry;
360 PEPROCESS CurrentProcess = PsGetCurrentProcess();
361
362 if (Process != NULL && Process != CurrentProcess)
363 {
364 KeAttachProcess(Process);
365 }
366 Entry = MmGetPageEntry1(Address);
367 if (Process != NULL && Process != CurrentProcess)
368 {
369 KeDetachProcess();
370 }
371 return(Entry);
372 }
373
374
375 PHYSICAL_ADDRESS
376 MmGetPhysicalAddressForProcess(PEPROCESS Process,
377 PVOID Address)
378 {
379 ULONG PageEntry;
380
381 PageEntry = MmGetPageEntryForProcess(Process, Address);
382
383 if (!(PageEntry & PA_PRESENT))
384 {
385 return((LARGE_INTEGER)0LL);
386 }
387 return(PTE_TO_PAGE(PageEntry));
388 }
389
390 VOID
391 MmDisableVirtualMapping(PEPROCESS Process, PVOID Address, BOOL* WasDirty, PHYSICAL_ADDRESS* PhysicalAddr)
392 /*
393 * FUNCTION: Delete a virtual mapping
394 */
395 {
396 ULONG Pte;
397 PULONG Pde;
398 PEPROCESS CurrentProcess = PsGetCurrentProcess();
399 BOOLEAN WasValid;
400
401 /*
402 * If we are setting a page in another process we need to be in its
403 * context.
404 */
405 if (Process != NULL && Process != CurrentProcess)
406 {
407 KeAttachProcess(Process);
408 }
409
410 /*
411 * Set the page directory entry, we may have to copy the entry from
412 * the global page directory.
413 */
414 Pde = ADDR_TO_PDE(Address);
415 if ((*Pde) == 0 &&
416 MmGlobalKernelPageDirectory[ADDR_TO_PDE_OFFSET(Address)] != 0)
417 {
418 (*Pde) = MmGlobalKernelPageDirectory[ADDR_TO_PDE_OFFSET(Address)];
419 FLUSH_TLB;
420 }
421 if ((*Pde) == 0)
422 {
423 KEBUGCHECK(0);
424 }
425
426 /*
427 * Atomically set the entry to zero and get the old value.
428 */
429 Pte = *ADDR_TO_PTE(Address);
430 *ADDR_TO_PTE(Address) = Pte & (~PA_PRESENT);
431 FLUSH_TLB;
432 WasValid = (PAGE_MASK(Pte) != 0);
433 if (!WasValid)
434 {
435 KEBUGCHECK(0);
436 }
437
438 /*
439 * If necessary go back to the original context
440 */
441 if (Process != NULL && Process != CurrentProcess)
442 {
443 KeDetachProcess();
444 }
445
446 /*
447 * Return some information to the caller
448 */
449 if (WasDirty != NULL)
450 {
451 *WasDirty = Pte & PA_DIRTY;
452 }
453 if (PhysicalAddr != NULL)
454 {
455 PhysicalAddr->u.HighPart = 0;
456 PhysicalAddr->u.LowPart = PAGE_MASK(Pte);
457 }
458 }
459
460 VOID
461 MmRawDeleteVirtualMapping(PVOID Address)
462 {
463 PULONG Pde, kePde;
464
465 /*
466 * Set the page directory entry, we may have to copy the entry from
467 * the global page directory.
468 */
469 Pde = ADDR_TO_PDE(Address);
470 if (*Pde == 0 && Address >= (PVOID)KERNEL_BASE)
471 {
472 kePde = MmGlobalKernelPageDirectory + ADDR_TO_PDE_OFFSET(Address);
473 if (*kePde != 0)
474 {
475 *Pde = *kePde;
476 FLUSH_TLB;
477 }
478 }
479
480 if (*Pde == 0)
481 {
482 return;
483 }
484
485 /*
486 * Set the entry to zero
487 */
488 *ADDR_TO_PTE(Address) = 0;
489 FLUSH_TLB;
490 }
491
492 VOID
493 MmDeleteVirtualMapping(PEPROCESS Process, PVOID Address, BOOL FreePage,
494 BOOL* WasDirty, PHYSICAL_ADDRESS* PhysicalAddr)
495 /*
496 * FUNCTION: Delete a virtual mapping
497 */
498 {
499 ULONG Pte;
500 PULONG Pde, kePde;
501 PEPROCESS CurrentProcess = PsGetCurrentProcess();
502 BOOLEAN WasValid;
503
504 /*
505 * If we are setting a page in another process we need to be in its
506 * context.
507 */
508 if (Process != NULL && Process != CurrentProcess)
509 {
510 KeAttachProcess(Process);
511 }
512
513 /*
514 * Set the page directory entry, we may have to copy the entry from
515 * the global page directory.
516 */
517 Pde = ADDR_TO_PDE(Address);
518 if (*Pde == 0 && Address >= (PVOID)KERNEL_BASE)
519 {
520 kePde = MmGlobalKernelPageDirectory + ADDR_TO_PDE_OFFSET(Address);
521 if (*kePde != 0)
522 {
523 *Pde = *kePde;
524 FLUSH_TLB;
525 }
526 }
527
528 if (*Pde == 0)
529 {
530 if (Process != NULL && Process != CurrentProcess)
531 {
532 KeDetachProcess();
533 }
534 if (WasDirty != NULL)
535 {
536 *WasDirty = FALSE;
537 }
538 if (PhysicalAddr != NULL)
539 {
540 *PhysicalAddr = (LARGE_INTEGER)0LL;
541 }
542 return;
543 }
544
545 /*
546 * Atomically set the entry to zero and get the old value.
547 */
548 Pte = (ULONG)InterlockedExchange((PLONG)ADDR_TO_PTE(Address), 0);
549 FLUSH_TLB;
550 WasValid = (PAGE_MASK(Pte) != 0);
551 if (WasValid)
552 {
553 MmMarkPageUnmapped(PTE_TO_PAGE(Pte));
554 }
555 if (FreePage && WasValid)
556 {
557 MmReleasePageMemoryConsumer(MC_NPPOOL, PTE_TO_PAGE(Pte));
558 }
559
560 /*
561 * Decrement the reference count for this page table.
562 */
563 if (Process != NULL && WasValid &&
564 Process->AddressSpace.PageTableRefCountTable != NULL &&
565 Address < (PVOID)KERNEL_BASE)
566 {
567 PUSHORT Ptrc;
568
569 Ptrc = Process->AddressSpace.PageTableRefCountTable;
570
571 Ptrc[ADDR_TO_PAGE_TABLE(Address)]--;
572 if (Ptrc[ADDR_TO_PAGE_TABLE(Address)] == 0)
573 {
574 MmFreePageTable(Process, Address);
575 }
576 }
577
578 /*
579 * If necessary go back to the original context
580 */
581 if (Process != NULL && Process != CurrentProcess)
582 {
583 KeDetachProcess();
584 }
585
586 /*
587 * Return some information to the caller
588 */
589 if (WasDirty != NULL)
590 {
591 if (Pte & PA_DIRTY)
592 {
593 *WasDirty = TRUE;
594 }
595 else
596 {
597 *WasDirty = FALSE;
598 }
599 }
600 if (PhysicalAddr != NULL)
601 {
602 *PhysicalAddr = PTE_TO_PAGE(Pte);
603 }
604 }
605
606 VOID
607 MmDeletePageFileMapping(PEPROCESS Process, PVOID Address,
608 SWAPENTRY* SwapEntry)
609 /*
610 * FUNCTION: Delete a virtual mapping
611 */
612 {
613 ULONG Pte;
614 PULONG Pde, kePde;
615 PEPROCESS CurrentProcess = PsGetCurrentProcess();
616 BOOLEAN WasValid = FALSE;
617
618 /*
619 * If we are setting a page in another process we need to be in its
620 * context.
621 */
622 if (Process != NULL && Process != CurrentProcess)
623 {
624 KeAttachProcess(Process);
625 }
626
627 /*
628 * Set the page directory entry, we may have to copy the entry from
629 * the global page directory.
630 */
631 Pde = ADDR_TO_PDE(Address);
632 if (*Pde == 0)
633 {
634 if (Address >= (PVOID)KERNEL_BASE)
635 {
636 kePde = MmGlobalKernelPageDirectory + ADDR_TO_PDE_OFFSET(Address);
637 if (*kePde != 0)
638 {
639 *Pde = *kePde;
640 FLUSH_TLB;
641 }
642 }
643 }
644 if (*Pde == 0)
645 {
646 if (Process != NULL && Process != CurrentProcess)
647 {
648 KeDetachProcess();
649 }
650 *SwapEntry = 0;
651 return;
652 }
653
654 /*
655 * Atomically set the entry to zero and get the old value.
656 */
657 Pte = (ULONG)InterlockedExchange((PLONG)ADDR_TO_PTE(Address), 0);
658 FLUSH_TLB;
659
660 WasValid = PAGE_MASK(Pte) == 0 ? FALSE : TRUE;
661
662 /*
663 * Decrement the reference count for this page table.
664 */
665 if (Process != NULL && WasValid &&
666 Process->AddressSpace.PageTableRefCountTable != NULL &&
667 Address < (PVOID)KERNEL_BASE)
668 {
669 PUSHORT Ptrc;
670
671 Ptrc = Process->AddressSpace.PageTableRefCountTable;
672
673 Ptrc[ADDR_TO_PAGE_TABLE(Address)]--;
674 if (Ptrc[ADDR_TO_PAGE_TABLE(Address)] == 0)
675 {
676 MmFreePageTable(Process, Address);
677 }
678 }
679
680 /*
681 * If necessary go back to the original context
682 */
683 if (Process != NULL && Process != CurrentProcess)
684 {
685 KeDetachProcess();
686 }
687
688 /*
689 * Return some information to the caller
690 */
691 *SwapEntry = Pte >> 1;
692 }
693
694 BOOLEAN
695 Mmi386MakeKernelPageTableGlobal(PVOID PAddress)
696 {
697 PULONG kePde, Pde;
698
699 Pde = ADDR_TO_PDE(PAddress);
700 if (*Pde == 0)
701 {
702 kePde = MmGlobalKernelPageDirectory + ADDR_TO_PDE_OFFSET(PAddress);
703 if (*kePde != 0)
704 {
705 *Pde = *kePde;
706 FLUSH_TLB;
707 return(TRUE);
708 }
709 }
710 return(FALSE);
711 }
712
713 BOOLEAN MmIsPageTablePresent(PVOID PAddress)
714 {
715 PULONG Pde, kePde;
716
717 Pde = ADDR_TO_PDE(PAddress);
718 if (*Pde == 0)
719 {
720 kePde = MmGlobalKernelPageDirectory + ADDR_TO_PDE_OFFSET(PAddress);
721 if (*kePde != 0)
722 {
723 *Pde = *kePde;
724 FLUSH_TLB;
725 return TRUE;
726 }
727 }
728 return FALSE;
729 }
730
731 NTSTATUS MmCreatePageTable(PVOID PAddress)
732 {
733 PULONG Pde, kePde;
734 PHYSICAL_ADDRESS npage;
735 NTSTATUS Status;
736
737 DPRINT("MmGetPageEntry(Address %x)\n", PAddress);
738
739 Pde = ADDR_TO_PDE(PAddress);
740 DPRINT("page_dir %x *page_dir %x\n", Pde, *Pde);
741 if (*Pde == 0 && PAddress >= (PVOID)KERNEL_BASE)
742 {
743 kePde = MmGlobalKernelPageDirectory + ADDR_TO_PDE_OFFSET(PAddress);
744 if (*kePde != 0)
745 {
746 *Pde = *kePde;
747 FLUSH_TLB;
748 return STATUS_SUCCESS;
749 }
750 /* Should we create a kernel page table? */
751 DPRINT1("!!!!!!!!!!!!!!!!!!\n");
752 return STATUS_UNSUCCESSFUL;
753 }
754
755 if (*Pde == 0)
756 {
757 Status = MmRequestPageMemoryConsumer(MC_NPPOOL, FALSE, &npage);
758 if (!NT_SUCCESS(Status))
759 {
760 return(Status);
761 }
762 MiZeroPage(npage);
763 *Pde = npage.u.LowPart | PA_PRESENT | PA_READWRITE | PA_USER;
764 FLUSH_TLB;
765 }
766 return(STATUS_SUCCESS);
767 }
768
769 PULONG MmGetPageEntry(PVOID PAddress)
770 /*
771 * FUNCTION: Get a pointer to the page table entry for a virtual address
772 */
773 {
774 PULONG Pde, kePde;
775 PHYSICAL_ADDRESS npage;
776 KIRQL oldIrql;
777 BOOLEAN Free = FALSE;
778 NTSTATUS Status;
779
780 DPRINT("MmGetPageEntry(Address %x)\n", PAddress);
781
782 Pde = ADDR_TO_PDE(PAddress);
783 DPRINT("page_dir %x *page_dir %x\n",Pde,*Pde);
784 if (*Pde == 0)
785 {
786 if (PAddress >= (PVOID)KERNEL_BASE)
787 {
788 oldIrql = KeRaiseIrqlToSynchLevel();
789 kePde = MmGlobalKernelPageDirectory + ADDR_TO_PDE_OFFSET(PAddress);
790 if (*kePde != 0)
791 {
792 *Pde = *kePde;
793 FLUSH_TLB;
794 }
795 else
796 {
797 KeLowerIrql(oldIrql);
798 Status = MmRequestPageMemoryConsumer(MC_NPPOOL, FALSE, &npage);
799 if (!NT_SUCCESS(Status))
800 {
801 KEBUGCHECK(0);
802 }
803 MiZeroPage(npage);
804 oldIrql = KeRaiseIrqlToSynchLevel();
805 if (*kePde != 0)
806 {
807 *Pde = *kePde;
808 FLUSH_TLB;
809 Free = TRUE;
810 }
811 else
812 {
813 *Pde = *kePde = npage.u.LowPart | PA_PRESENT | PA_READWRITE;
814 FLUSH_TLB;
815 }
816 }
817 KeLowerIrql(oldIrql);
818 }
819 else
820 {
821 Status = MmRequestPageMemoryConsumer(MC_NPPOOL, FALSE, &npage);
822 if (!NT_SUCCESS(Status))
823 {
824 KEBUGCHECK(0);
825 }
826 MiZeroPage(npage);
827 *Pde = npage.u.LowPart | PA_PRESENT | PA_READWRITE | PA_USER;
828 FLUSH_TLB;
829 }
830 if (Free)
831 {
832 MmReleasePageMemoryConsumer(MC_NPPOOL, npage);
833 }
834 }
835
836 return ADDR_TO_PTE(PAddress);
837 }
838
839 BOOLEAN MmIsDirtyPage(PEPROCESS Process, PVOID Address)
840 {
841 return((MmGetPageEntryForProcess(Process, Address)) & PA_DIRTY);
842 }
843
844 BOOLEAN
845 MmIsAccessedAndResetAccessPage(PEPROCESS Process, PVOID Address)
846 {
847 PULONG PageEntry;
848 PEPROCESS CurrentProcess;
849 BOOLEAN Accessed;
850
851 if (Process)
852 {
853 CurrentProcess = PsGetCurrentProcess();
854 if (Process != CurrentProcess)
855 {
856 KeAttachProcess(Process);
857 }
858 }
859 else
860 {
861 if (((ULONG)Address & ~0xFFF) < KERNEL_BASE)
862 {
863 DPRINT1("MmIsAccessedAndResetAccessPage is called for user space without a process.\n");
864 KEBUGCHECK(0);
865 }
866 CurrentProcess = NULL;
867 }
868
869 PageEntry = MmGetPageEntry(Address);
870 Accessed = (*PageEntry) & PA_ACCESSED;
871 if (Accessed)
872 {
873 (*PageEntry) = (*PageEntry) & (~PA_ACCESSED);
874 FLUSH_TLB;
875 }
876 if (Process != CurrentProcess)
877 {
878 KeDetachProcess();
879 }
880
881 return(Accessed);
882 }
883
884 VOID MmSetCleanPage(PEPROCESS Process, PVOID Address)
885 {
886 PULONG PageEntry;
887 PEPROCESS CurrentProcess;
888
889 if (Process)
890 {
891 CurrentProcess = PsGetCurrentProcess();
892 if (Process != CurrentProcess)
893 {
894 KeAttachProcess(Process);
895 }
896 }
897 else
898 {
899 if (((ULONG)Address & ~0xFFF) < KERNEL_BASE)
900 {
901 DPRINT1("MmSetCleanPage is called for user space without a process.\n");
902 KEBUGCHECK(0);
903 }
904 CurrentProcess = NULL;
905 }
906 PageEntry = MmGetPageEntry(Address);
907 (*PageEntry) = (*PageEntry) & (~PA_DIRTY);
908 FLUSH_TLB;
909 if (Process != CurrentProcess)
910 {
911 KeDetachProcess();
912 }
913 }
914
915 VOID MmSetDirtyPage(PEPROCESS Process, PVOID Address)
916 {
917 PULONG PageEntry;
918 PEPROCESS CurrentProcess = NULL;
919
920 if (Process)
921 {
922 CurrentProcess = PsGetCurrentProcess();
923 if (Process != CurrentProcess)
924 {
925 KeAttachProcess(Process);
926 }
927 }
928 else
929 {
930 if (((ULONG)Address & ~0xFFF) < KERNEL_BASE)
931 {
932 DPRINT1("MmSetDirtyPage is called for user space without a process.\n");
933 KEBUGCHECK(0);
934 }
935 CurrentProcess = NULL;
936 }
937 PageEntry = MmGetPageEntry(Address);
938 (*PageEntry) = (*PageEntry) | PA_DIRTY;
939 FLUSH_TLB;
940 if (Process != CurrentProcess)
941 {
942 KeDetachProcess();
943 }
944 }
945
946 VOID MmEnableVirtualMapping(PEPROCESS Process, PVOID Address)
947 {
948 PULONG PageEntry;
949 PEPROCESS CurrentProcess = PsGetCurrentProcess();
950
951 if (Process != CurrentProcess)
952 {
953 KeAttachProcess(Process);
954 }
955 PageEntry = MmGetPageEntry(Address);
956 (*PageEntry) = (*PageEntry) | PA_PRESENT;
957 FLUSH_TLB;
958 if (Process != CurrentProcess)
959 {
960 KeDetachProcess();
961 }
962 }
963
964 BOOLEAN MmIsPagePresent(PEPROCESS Process, PVOID Address)
965 {
966 return((MmGetPageEntryForProcess1(Process, Address)) & PA_PRESENT);
967 }
968
969 BOOLEAN MmIsPageSwapEntry(PEPROCESS Process, PVOID Address)
970 {
971 ULONG Pte;
972 Pte = MmGetPageEntryForProcess1(Process, Address);
973 return((!(Pte & PA_PRESENT)) && Pte != 0);
974 }
975
976 NTSTATUS
977 MmCreateVirtualMappingDump(PVOID Address,
978 ULONG flProtect,
979 PHYSICAL_ADDRESS PhysicalAddress)
980 {
981 ULONG Attributes;
982 PULONG Pte;
983 NTSTATUS Status;
984
985 if (Address < (PVOID)KERNEL_BASE)
986 {
987 DPRINT1("No process\n");
988 KEBUGCHECK(0);
989 }
990
991 Attributes = ProtectToPTE(flProtect);
992 if (!(Attributes & PA_PRESENT) && PhysicalAddress.QuadPart != 0)
993 {
994 DPRINT1("Setting physical address but not allowing access at address "
995 "0x%.8X with attributes %x/%x.\n",
996 Address, Attributes, flProtect);
997 KEBUGCHECK(0);
998 }
999
1000 Status = MmGetPageEntry2(Address, &Pte, FALSE);
1001 if (!NT_SUCCESS(Status))
1002 {
1003 return(Status);
1004 }
1005 if (PAGE_MASK((*Pte)) != 0 && !((*Pte) & PA_PRESENT))
1006 {
1007 KEBUGCHECK(0);
1008 }
1009 *Pte = PhysicalAddress.QuadPart | Attributes;
1010 FLUSH_TLB;
1011 return(STATUS_SUCCESS);
1012 }
1013
1014
1015 NTSTATUS
1016 MmCreateVirtualMappingForKernel(PVOID Address,
1017 ULONG flProtect,
1018 PHYSICAL_ADDRESS PhysicalAddress)
1019 {
1020 PEPROCESS CurrentProcess;
1021 ULONG Attributes;
1022 PULONG Pte;
1023 NTSTATUS Status;
1024 PEPROCESS Process = NULL;
1025
1026 if (Process != NULL)
1027 {
1028 CurrentProcess = PsGetCurrentProcess();
1029 }
1030 else
1031 {
1032 CurrentProcess = NULL;
1033 }
1034
1035 if (Process == NULL && Address < (PVOID)KERNEL_BASE)
1036 {
1037 DPRINT1("No process\n");
1038 KEBUGCHECK(0);
1039 }
1040 if (Process != NULL && Address >= (PVOID)KERNEL_BASE)
1041 {
1042 DPRINT1("Setting kernel address with process context\n");
1043 KEBUGCHECK(0);
1044 }
1045 Attributes = ProtectToPTE(flProtect);
1046
1047 if (Process != NULL && Process != CurrentProcess)
1048 {
1049 KeAttachProcess(Process);
1050 }
1051
1052 Status = MmGetPageEntry2(Address, &Pte, FALSE);
1053 if (!NT_SUCCESS(Status))
1054 {
1055 if (Process != NULL && Process != CurrentProcess)
1056 {
1057 KeDetachProcess();
1058 }
1059 return(Status);
1060 }
1061 if (PAGE_MASK((*Pte)) != 0 && !((*Pte) & PA_PRESENT))
1062 {
1063 KEBUGCHECK(0);
1064 }
1065 if (PAGE_MASK((*Pte)) != 0)
1066 {
1067 MmMarkPageUnmapped(PTE_TO_PAGE((*Pte)));
1068 }
1069 *Pte = PhysicalAddress.QuadPart | Attributes;
1070 if (Process != NULL &&
1071 Process->AddressSpace.PageTableRefCountTable != NULL &&
1072 Address < (PVOID)KERNEL_BASE &&
1073 Attributes & PA_PRESENT)
1074 {
1075 PUSHORT Ptrc;
1076
1077 Ptrc = Process->AddressSpace.PageTableRefCountTable;
1078
1079 Ptrc[ADDR_TO_PAGE_TABLE(Address)]++;
1080 }
1081 FLUSH_TLB;
1082 if (Process != NULL && Process != CurrentProcess)
1083 {
1084 KeDetachProcess();
1085 }
1086 return(STATUS_SUCCESS);
1087 }
1088
1089 NTSTATUS
1090 MmCreatePageFileMapping(PEPROCESS Process,
1091 PVOID Address,
1092 SWAPENTRY SwapEntry)
1093 {
1094 PEPROCESS CurrentProcess;
1095 PULONG Pte;
1096 NTSTATUS Status;
1097
1098 if (Process != NULL)
1099 {
1100 CurrentProcess = PsGetCurrentProcess();
1101 }
1102 else
1103 {
1104 CurrentProcess = NULL;
1105 }
1106
1107 if (Process == NULL && Address < (PVOID)KERNEL_BASE)
1108 {
1109 DPRINT1("No process\n");
1110 KEBUGCHECK(0);
1111 }
1112 if (Process != NULL && Address >= (PVOID)KERNEL_BASE)
1113 {
1114 DPRINT1("Setting kernel address with process context\n");
1115 KEBUGCHECK(0);
1116 }
1117 if (SwapEntry & (1 << 31))
1118 {
1119 KEBUGCHECK(0);
1120 }
1121
1122 if (Process != NULL && Process != CurrentProcess)
1123 {
1124 KeAttachProcess(Process);
1125 }
1126
1127 Status = MmGetPageEntry2(Address, &Pte, FALSE);
1128 if (!NT_SUCCESS(Status))
1129 {
1130 if (Process != NULL && Process != CurrentProcess)
1131 {
1132 KeDetachProcess();
1133 }
1134 return(Status);
1135 }
1136 if (PAGE_MASK((*Pte)) != 0)
1137 {
1138 MmMarkPageUnmapped(PTE_TO_PAGE((*Pte)));
1139 }
1140 *Pte = SwapEntry << 1;
1141 if (Process != NULL &&
1142 Process->AddressSpace.PageTableRefCountTable != NULL &&
1143 Address < (PVOID)KERNEL_BASE)
1144 {
1145 PUSHORT Ptrc;
1146
1147 Ptrc = Process->AddressSpace.PageTableRefCountTable;
1148
1149 Ptrc[ADDR_TO_PAGE_TABLE(Address)]++;
1150 }
1151 FLUSH_TLB;
1152 if (Process != NULL && Process != CurrentProcess)
1153 {
1154 KeDetachProcess();
1155 }
1156 return(STATUS_SUCCESS);
1157 }
1158
1159
1160
1161 NTSTATUS
1162 MmCreateVirtualMappingUnsafe(PEPROCESS Process,
1163 PVOID Address,
1164 ULONG flProtect,
1165 PHYSICAL_ADDRESS PhysicalAddress,
1166 BOOLEAN MayWait)
1167 {
1168 PEPROCESS CurrentProcess;
1169 ULONG Attributes;
1170 PULONG Pte;
1171 NTSTATUS Status;
1172
1173 if (Process != NULL)
1174 {
1175 CurrentProcess = PsGetCurrentProcess();
1176 }
1177 else
1178 {
1179 CurrentProcess = NULL;
1180 }
1181
1182 if (Process == NULL && Address < (PVOID)KERNEL_BASE)
1183 {
1184 DPRINT1("No process\n");
1185 KEBUGCHECK(0);
1186 }
1187 if (Process != NULL && Address >= (PVOID)KERNEL_BASE)
1188 {
1189 DPRINT1("Setting kernel address with process context\n");
1190 KEBUGCHECK(0);
1191 }
1192 MmMarkPageMapped(PhysicalAddress);
1193
1194 Attributes = ProtectToPTE(flProtect);
1195 if (!(Attributes & PA_PRESENT) && PhysicalAddress.QuadPart != 0)
1196 {
1197 DPRINT1("Setting physical address but not allowing access at address "
1198 "0x%.8X with attributes %x/%x.\n",
1199 Address, Attributes, flProtect);
1200 KEBUGCHECK(0);
1201 }
1202
1203 if (Process != NULL && Process != CurrentProcess)
1204 {
1205 KeAttachProcess(Process);
1206 }
1207
1208 Status = MmGetPageEntry2(Address, &Pte, MayWait);
1209 if (!NT_SUCCESS(Status))
1210 {
1211 if (Process != NULL && Process != CurrentProcess)
1212 {
1213 KeDetachProcess();
1214 }
1215 return(Status);
1216 }
1217 if (PAGE_MASK((*Pte)) != 0 && !((*Pte) & PA_PRESENT))
1218 {
1219 KEBUGCHECK(0);
1220 }
1221 if (PAGE_MASK((*Pte)) != 0)
1222 {
1223 MmMarkPageUnmapped(PTE_TO_PAGE((*Pte)));
1224 }
1225 *Pte = PhysicalAddress.QuadPart | Attributes;
1226 if (Process != NULL &&
1227 Process->AddressSpace.PageTableRefCountTable != NULL &&
1228 Address < (PVOID)KERNEL_BASE &&
1229 Attributes & PA_PRESENT)
1230 {
1231 PUSHORT Ptrc;
1232
1233 Ptrc = Process->AddressSpace.PageTableRefCountTable;
1234
1235 Ptrc[ADDR_TO_PAGE_TABLE(Address)]++;
1236 }
1237 FLUSH_TLB;
1238 if (Process != NULL && Process != CurrentProcess)
1239 {
1240 KeDetachProcess();
1241 }
1242 return(STATUS_SUCCESS);
1243 }
1244
1245 NTSTATUS
1246 MmCreateVirtualMapping(PEPROCESS Process,
1247 PVOID Address,
1248 ULONG flProtect,
1249 PHYSICAL_ADDRESS PhysicalAddress,
1250 BOOLEAN MayWait)
1251 {
1252 if (!MmIsUsablePage(PhysicalAddress))
1253 {
1254 DPRINT1("Page at address %x not usable\n", PhysicalAddress);
1255 KEBUGCHECK(0);
1256 }
1257
1258 return(MmCreateVirtualMappingUnsafe(Process,
1259 Address,
1260 flProtect,
1261 PhysicalAddress,
1262 MayWait));
1263 }
1264
1265 ULONG
1266 MmGetPageProtect(PEPROCESS Process, PVOID Address)
1267 {
1268 ULONG Entry;
1269 ULONG Protect;
1270
1271 Entry = MmGetPageEntryForProcess1(Process, Address);
1272
1273 if (!(Entry & PA_PRESENT))
1274 {
1275 Protect = PAGE_NOACCESS;
1276 }
1277 else if (Entry & PA_READWRITE)
1278 {
1279 Protect = PAGE_READWRITE;
1280 }
1281 else
1282 {
1283 Protect = PAGE_EXECUTE_READ;
1284 }
1285 return(Protect);
1286 }
1287
1288 VOID
1289 MmSetPageProtect(PEPROCESS Process, PVOID Address, ULONG flProtect)
1290 {
1291 ULONG Attributes = 0;
1292 PULONG PageEntry;
1293 PEPROCESS CurrentProcess = PsGetCurrentProcess();
1294
1295 DPRINT("MmSetPageProtect(Process %x Address %x flProtect %x)\n",
1296 Process, Address, flProtect);
1297
1298 Attributes = ProtectToPTE(flProtect);
1299 if (Process != NULL && Process != CurrentProcess)
1300 {
1301 KeAttachProcess(Process);
1302 }
1303 PageEntry = MmGetPageEntry(Address);
1304 (*PageEntry) = PAGE_MASK(*PageEntry) | Attributes;
1305 FLUSH_TLB;
1306 if (Process != NULL && Process != CurrentProcess)
1307 {
1308 KeDetachProcess();
1309 }
1310 }
1311
1312 /*
1313 * @implemented
1314 */
1315 PHYSICAL_ADDRESS STDCALL
1316 MmGetPhysicalAddress(PVOID vaddr)
1317 /*
1318 * FUNCTION: Returns the physical address corresponding to a virtual address
1319 */
1320 {
1321 PHYSICAL_ADDRESS p;
1322 ULONG Pte;
1323
1324 DPRINT("MmGetPhysicalAddress(vaddr %x)\n", vaddr);
1325
1326 Pte = *MmGetPageEntry(vaddr);
1327 if (Pte & PA_PRESENT)
1328 {
1329 p.QuadPart = PAGE_MASK(Pte);
1330 }
1331 else
1332 {
1333 p.QuadPart = 0;
1334 }
1335
1336 return p;
1337 }
1338
1339
1340 VOID
1341 MmUpdateStackPageDir(PULONG LocalPageDir, PKTHREAD PThread)
1342 {
1343 unsigned EntryBase = ADDR_TO_PDE_OFFSET(PThread->StackLimit);
1344 unsigned EntryTop = ADDR_TO_PDE_OFFSET(PThread->InitialStack - PAGE_SIZE);
1345
1346 if (0 == LocalPageDir[EntryBase])
1347 {
1348 LocalPageDir[EntryBase] = MmGlobalKernelPageDirectory[EntryBase];
1349 }
1350 if (EntryBase != EntryTop && 0 == LocalPageDir[EntryTop])
1351 {
1352 LocalPageDir[EntryTop] = MmGlobalKernelPageDirectory[EntryTop];
1353 }
1354 }
1355
1356 VOID INIT_FUNCTION
1357 MmInitGlobalKernelPageDirectory(VOID)
1358 {
1359 ULONG i;
1360 PULONG CurrentPageDirectory = (PULONG)PAGEDIRECTORY_MAP;
1361
1362 for (i = ADDR_TO_PDE_OFFSET(KERNEL_BASE); i < 1024; i++)
1363 {
1364 if (i != ADDR_TO_PDE_OFFSET(PAGETABLE_MAP) &&
1365 0 == MmGlobalKernelPageDirectory[i] && 0 != CurrentPageDirectory[i])
1366 {
1367 MmGlobalKernelPageDirectory[i] = CurrentPageDirectory[i];
1368 }
1369 }
1370 }
1371
1372 /* EOF */