b371c12200afb06bc9b820dba7ae6ae7fb63196a
[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 #include <mm/ARM3/miarm.h>
16
17 #if defined (ALLOC_PRAGMA)
18 #pragma alloc_text(INIT, MmInitGlobalKernelPageDirectory)
19 #endif
20
21 #define ADDR_TO_PDE_OFFSET MiAddressToPdeOffset
22 #define ADDR_TO_PAGE_TABLE(v) (((ULONG)(v)) / (1024 * PAGE_SIZE))
23
24 /* GLOBALS *****************************************************************/
25
26 #define PA_BIT_PRESENT (0)
27 #define PA_BIT_READWRITE (1)
28 #define PA_BIT_USER (2)
29 #define PA_BIT_WT (3)
30 #define PA_BIT_CD (4)
31 #define PA_BIT_ACCESSED (5)
32 #define PA_BIT_DIRTY (6)
33 #define PA_BIT_GLOBAL (8)
34
35 #define PA_PRESENT (1 << PA_BIT_PRESENT)
36 #define PA_READWRITE (1 << PA_BIT_READWRITE)
37 #define PA_USER (1 << PA_BIT_USER)
38 #define PA_DIRTY (1 << PA_BIT_DIRTY)
39 #define PA_WT (1 << PA_BIT_WT)
40 #define PA_CD (1 << PA_BIT_CD)
41 #define PA_ACCESSED (1 << PA_BIT_ACCESSED)
42 #define PA_GLOBAL (1 << PA_BIT_GLOBAL)
43
44 #define IS_HYPERSPACE(v) (((ULONG)(v) >= HYPER_SPACE && (ULONG)(v) <= HYPER_SPACE_END))
45
46 #define PTE_TO_PFN(X) ((X) >> PAGE_SHIFT)
47 #define PFN_TO_PTE(X) ((X) << PAGE_SHIFT)
48
49 #define PAGE_MASK(x) ((x)&(~0xfff))
50
51 const
52 ULONG
53 MmProtectToPteMask[32] =
54 {
55 //
56 // These are the base MM_ protection flags
57 //
58 0,
59 PTE_READONLY | PTE_ENABLE_CACHE,
60 PTE_EXECUTE | PTE_ENABLE_CACHE,
61 PTE_EXECUTE_READ | PTE_ENABLE_CACHE,
62 PTE_READWRITE | PTE_ENABLE_CACHE,
63 PTE_WRITECOPY | PTE_ENABLE_CACHE,
64 PTE_EXECUTE_READWRITE | PTE_ENABLE_CACHE,
65 PTE_EXECUTE_WRITECOPY | PTE_ENABLE_CACHE,
66 //
67 // These OR in the MM_NOCACHE flag
68 //
69 0,
70 PTE_READONLY | PTE_DISABLE_CACHE,
71 PTE_EXECUTE | PTE_DISABLE_CACHE,
72 PTE_EXECUTE_READ | PTE_DISABLE_CACHE,
73 PTE_READWRITE | PTE_DISABLE_CACHE,
74 PTE_WRITECOPY | PTE_DISABLE_CACHE,
75 PTE_EXECUTE_READWRITE | PTE_DISABLE_CACHE,
76 PTE_EXECUTE_WRITECOPY | PTE_DISABLE_CACHE,
77 //
78 // These OR in the MM_DECOMMIT flag, which doesn't seem supported on x86/64/ARM
79 //
80 0,
81 PTE_READONLY | PTE_ENABLE_CACHE,
82 PTE_EXECUTE | PTE_ENABLE_CACHE,
83 PTE_EXECUTE_READ | PTE_ENABLE_CACHE,
84 PTE_READWRITE | PTE_ENABLE_CACHE,
85 PTE_WRITECOPY | PTE_ENABLE_CACHE,
86 PTE_EXECUTE_READWRITE | PTE_ENABLE_CACHE,
87 PTE_EXECUTE_WRITECOPY | PTE_ENABLE_CACHE,
88 //
89 // These OR in the MM_NOACCESS flag, which seems to enable WriteCombining?
90 //
91 0,
92 PTE_READONLY | PTE_WRITECOMBINED_CACHE,
93 PTE_EXECUTE | PTE_WRITECOMBINED_CACHE,
94 PTE_EXECUTE_READ | PTE_WRITECOMBINED_CACHE,
95 PTE_READWRITE | PTE_WRITECOMBINED_CACHE,
96 PTE_WRITECOPY | PTE_WRITECOMBINED_CACHE,
97 PTE_EXECUTE_READWRITE | PTE_WRITECOMBINED_CACHE,
98 PTE_EXECUTE_WRITECOPY | PTE_WRITECOMBINED_CACHE,
99 };
100
101 const
102 ULONG MmProtectToValue[32] =
103 {
104 PAGE_NOACCESS,
105 PAGE_READONLY,
106 PAGE_EXECUTE,
107 PAGE_EXECUTE_READ,
108 PAGE_READWRITE,
109 PAGE_WRITECOPY,
110 PAGE_EXECUTE_READWRITE,
111 PAGE_EXECUTE_WRITECOPY,
112 PAGE_NOACCESS,
113 PAGE_NOCACHE | PAGE_READONLY,
114 PAGE_NOCACHE | PAGE_EXECUTE,
115 PAGE_NOCACHE | PAGE_EXECUTE_READ,
116 PAGE_NOCACHE | PAGE_READWRITE,
117 PAGE_NOCACHE | PAGE_WRITECOPY,
118 PAGE_NOCACHE | PAGE_EXECUTE_READWRITE,
119 PAGE_NOCACHE | PAGE_EXECUTE_WRITECOPY,
120 PAGE_NOACCESS,
121 PAGE_GUARD | PAGE_READONLY,
122 PAGE_GUARD | PAGE_EXECUTE,
123 PAGE_GUARD | PAGE_EXECUTE_READ,
124 PAGE_GUARD | PAGE_READWRITE,
125 PAGE_GUARD | PAGE_WRITECOPY,
126 PAGE_GUARD | PAGE_EXECUTE_READWRITE,
127 PAGE_GUARD | PAGE_EXECUTE_WRITECOPY,
128 PAGE_NOACCESS,
129 PAGE_WRITECOMBINE | PAGE_READONLY,
130 PAGE_WRITECOMBINE | PAGE_EXECUTE,
131 PAGE_WRITECOMBINE | PAGE_EXECUTE_READ,
132 PAGE_WRITECOMBINE | PAGE_READWRITE,
133 PAGE_WRITECOMBINE | PAGE_WRITECOPY,
134 PAGE_WRITECOMBINE | PAGE_EXECUTE_READWRITE,
135 PAGE_WRITECOMBINE | PAGE_EXECUTE_WRITECOPY
136 };
137
138 /* FUNCTIONS ***************************************************************/
139
140 static BOOLEAN MmUnmapPageTable(PULONG Pt);
141
142 VOID
143 MiFlushTlb(PULONG Pt, PVOID Address)
144 {
145 if ((Pt && MmUnmapPageTable(Pt)) || Address >= MmSystemRangeStart)
146 {
147 KeInvalidateTlbEntry(Address);
148 }
149 }
150
151 static ULONG
152 ProtectToPTE(ULONG flProtect)
153 {
154 ULONG Attributes = 0;
155
156 if (flProtect & (PAGE_NOACCESS|PAGE_GUARD))
157 {
158 Attributes = 0;
159 }
160 else if (flProtect & PAGE_IS_WRITABLE)
161 {
162 Attributes = PA_PRESENT | PA_READWRITE;
163 }
164 else if (flProtect & (PAGE_IS_READABLE | PAGE_IS_EXECUTABLE))
165 {
166 Attributes = PA_PRESENT;
167 }
168 else
169 {
170 DPRINT1("Unknown main protection type.\n");
171 KeBugCheck(MEMORY_MANAGEMENT);
172 }
173
174 if (flProtect & PAGE_SYSTEM)
175 {
176 }
177 else
178 {
179 Attributes = Attributes | PA_USER;
180 }
181 if (flProtect & PAGE_NOCACHE)
182 {
183 Attributes = Attributes | PA_CD;
184 }
185 if (flProtect & PAGE_WRITETHROUGH)
186 {
187 Attributes = Attributes | PA_WT;
188 }
189 return(Attributes);
190 }
191
192 /* Taken from ARM3/pagfault.c */
193 FORCEINLINE
194 BOOLEAN
195 MiSynchronizeSystemPde(PMMPDE PointerPde)
196 {
197 MMPDE SystemPde;
198 ULONG Index;
199
200 /* Get the Index from the PDE */
201 Index = ((ULONG_PTR)PointerPde & (SYSTEM_PD_SIZE - 1)) / sizeof(MMPTE);
202
203 /* Copy the PDE from the double-mapped system page directory */
204 SystemPde = MmSystemPagePtes[Index];
205 *PointerPde = SystemPde;
206
207 /* Make sure we re-read the PDE and PTE */
208 KeMemoryBarrierWithoutFence();
209
210 /* Return, if we had success */
211 return SystemPde.u.Hard.Valid != 0;
212 }
213
214 NTSTATUS
215 NTAPI
216 MiDispatchFault(IN BOOLEAN StoreInstruction,
217 IN PVOID Address,
218 IN PMMPTE PointerPte,
219 IN PMMPTE PointerProtoPte,
220 IN BOOLEAN Recursive,
221 IN PEPROCESS Process,
222 IN PVOID TrapInformation,
223 IN PVOID Vad);
224
225 NTSTATUS
226 NTAPI
227 MiFillSystemPageDirectory(IN PVOID Base,
228 IN SIZE_T NumberOfBytes);
229
230 static PULONG
231 MmGetPageTableForProcess(PEPROCESS Process, PVOID Address, BOOLEAN Create)
232 {
233 PFN_NUMBER Pfn;
234 PULONG Pt;
235 PMMPDE PointerPde;
236
237 if (Address < MmSystemRangeStart)
238 {
239 /* We should have a process for user land addresses */
240 ASSERT(Process != NULL);
241
242 if(Process != PsGetCurrentProcess())
243 {
244 PMMPDE PdeBase;
245 ULONG PdeOffset = MiGetPdeOffset(Address);
246
247 /* Nobody but page fault should ask for creating the PDE,
248 * Which imples that Process is the current one */
249 ASSERT(Create == FALSE);
250
251 PdeBase = MmCreateHyperspaceMapping(PTE_TO_PFN(Process->Pcb.DirectoryTableBase[0]));
252 if (PdeBase == NULL)
253 {
254 KeBugCheck(MEMORY_MANAGEMENT);
255 }
256 PointerPde = PdeBase + PdeOffset;
257 if (PointerPde->u.Hard.Valid == 0)
258 {
259 MmDeleteHyperspaceMapping(PdeBase);
260 return NULL;
261 }
262 else
263 {
264 Pfn = PointerPde->u.Hard.PageFrameNumber;
265 }
266 MmDeleteHyperspaceMapping(PdeBase);
267 Pt = MmCreateHyperspaceMapping(Pfn);
268 if (Pt == NULL)
269 {
270 KeBugCheck(MEMORY_MANAGEMENT);
271 }
272 return Pt + MiAddressToPteOffset(Address);
273 }
274 /* This is for our process */
275 PointerPde = MiAddressToPde(Address);
276 Pt = (PULONG)MiAddressToPte(Address);
277 if (PointerPde->u.Hard.Valid == 0)
278 {
279 NTSTATUS Status;
280 if (Create == FALSE)
281 {
282 return NULL;
283 }
284 ASSERT(PointerPde->u.Long == 0);
285
286 MI_WRITE_INVALID_PTE(PointerPde, DemandZeroPde);
287 Status = MiDispatchFault(TRUE,
288 Pt,
289 PointerPde,
290 NULL,
291 FALSE,
292 PsGetCurrentProcess(),
293 NULL,
294 NULL);
295 DBG_UNREFERENCED_LOCAL_VARIABLE(Status);
296 ASSERT(KeAreAllApcsDisabled() == TRUE);
297 ASSERT(PointerPde->u.Hard.Valid == 1);
298 }
299 return (PULONG)MiAddressToPte(Address);
300 }
301
302 /* This is for kernel land address */
303 ASSERT(Process == NULL);
304 PointerPde = MiAddressToPde(Address);
305 Pt = (PULONG)MiAddressToPte(Address);
306 if (PointerPde->u.Hard.Valid == 0)
307 {
308 /* Let ARM3 synchronize the PDE */
309 if(!MiSynchronizeSystemPde(PointerPde))
310 {
311 /* PDE (still) not valid, let ARM3 allocate one if asked */
312 if(Create == FALSE)
313 return NULL;
314 MiFillSystemPageDirectory(Address, PAGE_SIZE);
315 }
316 }
317 return Pt;
318 }
319
320 static BOOLEAN MmUnmapPageTable(PULONG Pt)
321 {
322 if (!IS_HYPERSPACE(Pt))
323 {
324 return TRUE;
325 }
326
327 if (Pt)
328 {
329 MmDeleteHyperspaceMapping((PVOID)PAGE_ROUND_DOWN(Pt));
330 }
331 return FALSE;
332 }
333
334 static ULONG MmGetPageEntryForProcess(PEPROCESS Process, PVOID Address)
335 {
336 ULONG Pte;
337 PULONG Pt;
338
339 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
340 if (Pt)
341 {
342 Pte = *Pt;
343 MmUnmapPageTable(Pt);
344 return Pte;
345 }
346 return 0;
347 }
348
349 PFN_NUMBER
350 NTAPI
351 MmGetPfnForProcess(PEPROCESS Process,
352 PVOID Address)
353 {
354 ULONG Entry;
355 Entry = MmGetPageEntryForProcess(Process, Address);
356 if (!(Entry & PA_PRESENT))
357 {
358 return 0;
359 }
360 return(PTE_TO_PFN(Entry));
361 }
362
363 VOID
364 NTAPI
365 MmDeleteVirtualMapping(PEPROCESS Process, PVOID Address,
366 BOOLEAN* WasDirty, PPFN_NUMBER Page)
367 /*
368 * FUNCTION: Delete a virtual mapping
369 */
370 {
371 BOOLEAN WasValid = FALSE;
372 PFN_NUMBER Pfn;
373 ULONG Pte;
374 PULONG Pt;
375
376 DPRINT("MmDeleteVirtualMapping(%p, %p, %p, %p)\n",
377 Process, Address, WasDirty, Page);
378
379 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
380
381 if (Pt == NULL)
382 {
383 if (WasDirty != NULL)
384 {
385 *WasDirty = FALSE;
386 }
387 if (Page != NULL)
388 {
389 *Page = 0;
390 }
391 return;
392 }
393
394 /*
395 * Atomically set the entry to zero and get the old value.
396 */
397 Pte = InterlockedExchangePte(Pt, 0);
398
399 /* We count a mapping as valid if it's a present page, or it's a nonzero pfn with
400 * the swap bit unset, indicating a valid page protected to PAGE_NOACCESS. */
401 WasValid = (Pte & PA_PRESENT) || ((Pte >> PAGE_SHIFT) && !(Pte & 0x800));
402 if (WasValid)
403 {
404 /* Flush the TLB since we transitioned this PTE
405 * from valid to invalid so any stale translations
406 * are removed from the cache */
407 MiFlushTlb(Pt, Address);
408
409 if (Address < MmSystemRangeStart)
410 {
411 /* Remove PDE reference */
412 Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)]--;
413 ASSERT(Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)] < PTE_COUNT);
414 }
415
416 Pfn = PTE_TO_PFN(Pte);
417 }
418 else
419 {
420 MmUnmapPageTable(Pt);
421 Pfn = 0;
422 }
423
424 /*
425 * Return some information to the caller
426 */
427 if (WasDirty != NULL)
428 {
429 *WasDirty = ((Pte & PA_DIRTY) && (Pte & PA_PRESENT)) ? TRUE : FALSE;
430 }
431 if (Page != NULL)
432 {
433 *Page = Pfn;
434 }
435 }
436
437 VOID
438 NTAPI
439 MmGetPageFileMapping(PEPROCESS Process, PVOID Address,
440 SWAPENTRY* SwapEntry)
441 /*
442 * FUNCTION: Get a page file mapping
443 */
444 {
445 ULONG Entry = MmGetPageEntryForProcess(Process, Address);
446 *SwapEntry = Entry >> 1;
447 }
448
449 VOID
450 NTAPI
451 MmDeletePageFileMapping(PEPROCESS Process, PVOID Address,
452 SWAPENTRY* SwapEntry)
453 /*
454 * FUNCTION: Delete a virtual mapping
455 */
456 {
457 ULONG Pte;
458 PULONG Pt;
459
460 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
461
462 if (Pt == NULL)
463 {
464 *SwapEntry = 0;
465 return;
466 }
467
468 /*
469 * Atomically set the entry to zero and get the old value.
470 */
471 Pte = InterlockedExchangePte(Pt, 0);
472
473 if (Address < MmSystemRangeStart)
474 {
475 /* Remove PDE reference */
476 Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)]--;
477 ASSERT(Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)] < PTE_COUNT);
478 }
479
480 /* We don't need to flush here because page file entries
481 * are invalid translations, so the processor won't cache them */
482 MmUnmapPageTable(Pt);
483
484 if ((Pte & PA_PRESENT) || !(Pte & 0x800))
485 {
486 DPRINT1("Pte %x (want not 1 and 0x800)\n", Pte);
487 KeBugCheck(MEMORY_MANAGEMENT);
488 }
489
490 /*
491 * Return some information to the caller
492 */
493 *SwapEntry = Pte >> 1;
494 }
495
496 BOOLEAN
497 Mmi386MakeKernelPageTableGlobal(PVOID Address)
498 {
499 PMMPDE PointerPde = MiAddressToPde(Address);
500 PMMPTE PointerPte = MiAddressToPte(Address);
501
502 if (PointerPde->u.Hard.Valid == 0)
503 {
504 if(!MiSynchronizeSystemPde(PointerPde))
505 return FALSE;
506 return PointerPte->u.Hard.Valid != 0;
507 }
508 return FALSE;
509 }
510
511 BOOLEAN
512 NTAPI
513 MmIsDirtyPage(PEPROCESS Process, PVOID Address)
514 {
515 return MmGetPageEntryForProcess(Process, Address) & PA_DIRTY ? TRUE : FALSE;
516 }
517
518 VOID
519 NTAPI
520 MmSetCleanPage(PEPROCESS Process, PVOID Address)
521 {
522 PULONG Pt;
523 ULONG Pte;
524
525 if (Address < MmSystemRangeStart && Process == NULL)
526 {
527 DPRINT1("MmSetCleanPage is called for user space without a process.\n");
528 KeBugCheck(MEMORY_MANAGEMENT);
529 }
530
531 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
532 if (Pt == NULL)
533 {
534 KeBugCheck(MEMORY_MANAGEMENT);
535 }
536
537 do
538 {
539 Pte = *Pt;
540 } while (Pte != InterlockedCompareExchangePte(Pt, Pte & ~PA_DIRTY, Pte));
541
542 if (!(Pte & PA_PRESENT))
543 {
544 KeBugCheck(MEMORY_MANAGEMENT);
545 }
546 else if (Pte & PA_DIRTY)
547 {
548 MiFlushTlb(Pt, Address);
549 }
550 else
551 {
552 MmUnmapPageTable(Pt);
553 }
554 }
555
556 VOID
557 NTAPI
558 MmSetDirtyPage(PEPROCESS Process, PVOID Address)
559 {
560 PULONG Pt;
561 ULONG Pte;
562
563 if (Address < MmSystemRangeStart && Process == NULL)
564 {
565 DPRINT1("MmSetDirtyPage is called for user space without a process.\n");
566 KeBugCheck(MEMORY_MANAGEMENT);
567 }
568
569 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
570 if (Pt == NULL)
571 {
572 KeBugCheck(MEMORY_MANAGEMENT);
573 }
574
575 do
576 {
577 Pte = *Pt;
578 } while (Pte != InterlockedCompareExchangePte(Pt, Pte | PA_DIRTY, Pte));
579
580 if (!(Pte & PA_PRESENT))
581 {
582 KeBugCheck(MEMORY_MANAGEMENT);
583 }
584 else
585 {
586 /* The processor will never clear this bit itself, therefore
587 * we do not need to flush the TLB here when setting it */
588 MmUnmapPageTable(Pt);
589 }
590 }
591
592 BOOLEAN
593 NTAPI
594 MmIsPagePresent(PEPROCESS Process, PVOID Address)
595 {
596 return MmGetPageEntryForProcess(Process, Address) & PA_PRESENT;
597 }
598
599 BOOLEAN
600 NTAPI
601 MmIsDisabledPage(PEPROCESS Process, PVOID Address)
602 {
603 ULONG_PTR Entry = MmGetPageEntryForProcess(Process, Address);
604 return !(Entry & PA_PRESENT) && !(Entry & 0x800) && (Entry >> PAGE_SHIFT);
605 }
606
607 BOOLEAN
608 NTAPI
609 MmIsPageSwapEntry(PEPROCESS Process, PVOID Address)
610 {
611 ULONG Entry;
612 Entry = MmGetPageEntryForProcess(Process, Address);
613 return !(Entry & PA_PRESENT) && (Entry & 0x800);
614 }
615
616 NTSTATUS
617 NTAPI
618 MmCreatePageFileMapping(PEPROCESS Process,
619 PVOID Address,
620 SWAPENTRY SwapEntry)
621 {
622 PULONG Pt;
623 ULONG Pte;
624
625 if (Process == NULL && Address < MmSystemRangeStart)
626 {
627 DPRINT1("No process\n");
628 KeBugCheck(MEMORY_MANAGEMENT);
629 }
630 if (Process != NULL && Address >= MmSystemRangeStart)
631 {
632 DPRINT1("Setting kernel address with process context\n");
633 KeBugCheck(MEMORY_MANAGEMENT);
634 }
635
636 if (SwapEntry & (1 << 31))
637 {
638 KeBugCheck(MEMORY_MANAGEMENT);
639 }
640
641 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
642 if (Pt == NULL)
643 {
644 /* Nobody should page out an address that hasn't even been mapped */
645 /* But we might place a wait entry first, requiring the page table */
646 if (SwapEntry != MM_WAIT_ENTRY)
647 {
648 KeBugCheck(MEMORY_MANAGEMENT);
649 }
650 Pt = MmGetPageTableForProcess(Process, Address, TRUE);
651 }
652 Pte = InterlockedExchangePte(Pt, SwapEntry << 1);
653 if (Pte != 0)
654 {
655 KeBugCheckEx(MEMORY_MANAGEMENT, SwapEntry, (ULONG_PTR)Process, (ULONG_PTR)Address, 0);
656 }
657
658 if (Address < MmSystemRangeStart)
659 {
660 /* Add PDE reference */
661 Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)]++;
662 ASSERT(Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)] <= PTE_COUNT);
663 }
664
665 /* We don't need to flush the TLB here because it
666 * only caches valid translations and a zero PTE
667 * is not a valid translation */
668 MmUnmapPageTable(Pt);
669
670 return(STATUS_SUCCESS);
671 }
672
673
674 NTSTATUS
675 NTAPI
676 MmCreateVirtualMappingUnsafe(PEPROCESS Process,
677 PVOID Address,
678 ULONG flProtect,
679 PPFN_NUMBER Pages,
680 ULONG PageCount)
681 {
682 ULONG Attributes;
683 PVOID Addr;
684 ULONG i;
685 ULONG oldPdeOffset, PdeOffset;
686 PULONG Pt = NULL;
687 ULONG Pte;
688 DPRINT("MmCreateVirtualMappingUnsafe(%p, %p, %lu, %p (%x), %lu)\n",
689 Process, Address, flProtect, Pages, *Pages, PageCount);
690
691 ASSERT(((ULONG_PTR)Address % PAGE_SIZE) == 0);
692
693 if (Process == NULL)
694 {
695 if (Address < MmSystemRangeStart)
696 {
697 DPRINT1("No process\n");
698 KeBugCheck(MEMORY_MANAGEMENT);
699 }
700 if (PageCount > 0x10000 ||
701 (ULONG_PTR) Address / PAGE_SIZE + PageCount > 0x100000)
702 {
703 DPRINT1("Page count too large\n");
704 KeBugCheck(MEMORY_MANAGEMENT);
705 }
706 }
707 else
708 {
709 if (Address >= MmSystemRangeStart)
710 {
711 DPRINT1("Setting kernel address with process context\n");
712 KeBugCheck(MEMORY_MANAGEMENT);
713 }
714 if (PageCount > (ULONG_PTR)MmSystemRangeStart / PAGE_SIZE ||
715 (ULONG_PTR) Address / PAGE_SIZE + PageCount >
716 (ULONG_PTR)MmSystemRangeStart / PAGE_SIZE)
717 {
718 DPRINT1("Page Count too large\n");
719 KeBugCheck(MEMORY_MANAGEMENT);
720 }
721 }
722
723 Attributes = ProtectToPTE(flProtect);
724 Attributes &= 0xfff;
725 if (Address >= MmSystemRangeStart)
726 {
727 Attributes &= ~PA_USER;
728 }
729 else
730 {
731 Attributes |= PA_USER;
732 }
733
734 Addr = Address;
735 /* MmGetPageTableForProcess should be called on the first run, so
736 * let this trigger it */
737 oldPdeOffset = ADDR_TO_PDE_OFFSET(Addr) + 1;
738 for (i = 0; i < PageCount; i++, Addr = (PVOID)((ULONG_PTR)Addr + PAGE_SIZE))
739 {
740 if (!(Attributes & PA_PRESENT) && Pages[i] != 0)
741 {
742 DPRINT1("Setting physical address but not allowing access at address "
743 "0x%p with attributes %x/%x.\n",
744 Addr, Attributes, flProtect);
745 KeBugCheck(MEMORY_MANAGEMENT);
746 }
747 PdeOffset = ADDR_TO_PDE_OFFSET(Addr);
748 if (oldPdeOffset != PdeOffset)
749 {
750 if(Pt) MmUnmapPageTable(Pt);
751 Pt = MmGetPageTableForProcess(Process, Addr, TRUE);
752 if (Pt == NULL)
753 {
754 KeBugCheck(MEMORY_MANAGEMENT);
755 }
756 }
757 else
758 {
759 Pt++;
760 }
761 oldPdeOffset = PdeOffset;
762
763 Pte = InterlockedExchangePte(Pt, PFN_TO_PTE(Pages[i]) | Attributes);
764
765 /* There should not be anything valid here */
766 if (Pte != 0)
767 {
768 DPRINT1("Bad PTE %lx at %p for %p + %lu\n", Pte, Pt, Address, i);
769 KeBugCheck(MEMORY_MANAGEMENT);
770 }
771
772 /* We don't need to flush the TLB here because it only caches valid translations
773 * and we're moving this PTE from invalid to valid so it can't be cached right now */
774
775 if (Addr < MmSystemRangeStart)
776 {
777 /* Add PDE reference */
778 Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Addr)]++;
779 ASSERT(Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Addr)] <= PTE_COUNT);
780 }
781 }
782
783 ASSERT(Addr > Address);
784 MmUnmapPageTable(Pt);
785
786 return(STATUS_SUCCESS);
787 }
788
789 NTSTATUS
790 NTAPI
791 MmCreateVirtualMapping(PEPROCESS Process,
792 PVOID Address,
793 ULONG flProtect,
794 PPFN_NUMBER Pages,
795 ULONG PageCount)
796 {
797 ULONG i;
798
799 ASSERT((ULONG_PTR)Address % PAGE_SIZE == 0);
800 for (i = 0; i < PageCount; i++)
801 {
802 if (!MmIsPageInUse(Pages[i]))
803 {
804 DPRINT1("Page at address %x not in use\n", PFN_TO_PTE(Pages[i]));
805 KeBugCheck(MEMORY_MANAGEMENT);
806 }
807 }
808
809 return(MmCreateVirtualMappingUnsafe(Process,
810 Address,
811 flProtect,
812 Pages,
813 PageCount));
814 }
815
816 ULONG
817 NTAPI
818 MmGetPageProtect(PEPROCESS Process, PVOID Address)
819 {
820 ULONG Entry;
821 ULONG Protect;
822
823 Entry = MmGetPageEntryForProcess(Process, Address);
824
825
826 if (!(Entry & PA_PRESENT))
827 {
828 Protect = PAGE_NOACCESS;
829 }
830 else
831 {
832 if (Entry & PA_READWRITE)
833 {
834 Protect = PAGE_READWRITE;
835 }
836 else
837 {
838 Protect = PAGE_EXECUTE_READ;
839 }
840 if (Entry & PA_CD)
841 {
842 Protect |= PAGE_NOCACHE;
843 }
844 if (Entry & PA_WT)
845 {
846 Protect |= PAGE_WRITETHROUGH;
847 }
848 if (!(Entry & PA_USER))
849 {
850 Protect |= PAGE_SYSTEM;
851 }
852
853 }
854 return(Protect);
855 }
856
857 VOID
858 NTAPI
859 MmSetPageProtect(PEPROCESS Process, PVOID Address, ULONG flProtect)
860 {
861 ULONG Attributes = 0;
862 PULONG Pt;
863 ULONG Pte;
864
865 DPRINT("MmSetPageProtect(Process %p Address %p flProtect %x)\n",
866 Process, Address, flProtect);
867
868 Attributes = ProtectToPTE(flProtect);
869
870 Attributes &= 0xfff;
871 if (Address >= MmSystemRangeStart)
872 {
873 Attributes &= ~PA_USER;
874 }
875 else
876 {
877 Attributes |= PA_USER;
878 }
879
880 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
881 if (Pt == NULL)
882 {
883 KeBugCheck(MEMORY_MANAGEMENT);
884 }
885 Pte = InterlockedExchangePte(Pt, PAGE_MASK(*Pt) | Attributes | (*Pt & (PA_ACCESSED|PA_DIRTY)));
886
887 // We should be able to bring a page back from PAGE_NOACCESS
888 if ((Pte & 0x800) || !(Pte >> PAGE_SHIFT))
889 {
890 DPRINT1("Invalid Pte %lx\n", Pte);
891 KeBugCheck(MEMORY_MANAGEMENT);
892 }
893
894 if((Pte & Attributes) != Attributes)
895 MiFlushTlb(Pt, Address);
896 else
897 MmUnmapPageTable(Pt);
898 }
899
900 VOID
901 INIT_FUNCTION
902 NTAPI
903 MmInitGlobalKernelPageDirectory(VOID)
904 {
905 /* Nothing to do here */
906 }
907
908 /* EOF */