Sync with trunk (48237)
[reactos.git] / ntoskrnl / mm / amd64 / page.c
1 /*
2 * COPYRIGHT: GPL, See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/mm/amd64/page.c
5 * PURPOSE: Low level memory managment manipulation
6 *
7 * PROGRAMMER: Timo Kreuzer (timo.kreuzer@reactos.org)
8 */
9
10 /* INCLUDES ***************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 #undef InterlockedExchangePte
17 #define InterlockedExchangePte(pte1, pte2) \
18 InterlockedExchange64((LONG64*)&pte1->u.Long, pte2.u.Long)
19
20 #define PAGE_EXECUTE_ANY (PAGE_EXECUTE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE|PAGE_EXECUTE_WRITECOPY)
21 #define PAGE_WRITE_ANY (PAGE_EXECUTE_READWRITE|PAGE_READWRITE|PAGE_EXECUTE_WRITECOPY|PAGE_WRITECOPY)
22 #define PAGE_WRITECOPY_ANY (PAGE_EXECUTE_WRITECOPY|PAGE_WRITECOPY)
23
24 extern MMPTE HyperTemplatePte;
25
26 /* GLOBALS *****************************************************************/
27
28
29 /* PRIVATE FUNCTIONS *******************************************************/
30
31 BOOLEAN
32 FORCEINLINE
33 MiIsHyperspaceAddress(PVOID Address)
34 {
35 return ((ULONG64)Address >= HYPER_SPACE &&
36 (ULONG64)Address <= HYPER_SPACE_END);
37 }
38
39 VOID
40 MiFlushTlb(PMMPTE Pte, PVOID Address)
41 {
42 if (MiIsHyperspaceAddress(Pte))
43 {
44 MmDeleteHyperspaceMapping((PVOID)PAGE_ROUND_DOWN(Pte));
45 }
46 else
47 {
48 __invlpg(Address);
49 }
50 }
51
52 static
53 PMMPTE
54 MiGetPteForProcess(
55 PEPROCESS Process,
56 PVOID Address,
57 BOOLEAN Create)
58 {
59 MMPTE TmplPte, *Pte;
60
61 /* Check if we need hypersapce mapping */
62 if (Address < MmSystemRangeStart &&
63 Process && Process != PsGetCurrentProcess())
64 {
65 UNIMPLEMENTED;
66 return NULL;
67 }
68 else if (Create)
69 {
70 TmplPte.u.Long = 0;
71 TmplPte.u.Flush.Valid = 1;
72 TmplPte.u.Flush.Write = 1;
73
74 /* Get the PXE */
75 Pte = MiAddressToPxe(Address);
76 if (!Pte->u.Hard.Valid)
77 {
78 // TmplPte.u.Hard.PageFrameNumber = MiAllocPage(TRUE);
79 InterlockedExchangePte(Pte, TmplPte);
80 }
81
82 /* Get the PPE */
83 Pte = MiAddressToPpe(Address);
84 if (!Pte->u.Hard.Valid)
85 {
86 // TmplPte.u.Hard.PageFrameNumber = MiAllocPage(TRUE);
87 InterlockedExchangePte(Pte, TmplPte);
88 }
89
90 /* Get the PDE */
91 Pte = MiAddressToPde(Address);
92 if (!Pte->u.Hard.Valid)
93 {
94 // TmplPte.u.Hard.PageFrameNumber = MiAllocPage(TRUE);
95 InterlockedExchangePte(Pte, TmplPte);
96 }
97 }
98 else
99 {
100 /* Get the PXE */
101 Pte = MiAddressToPxe(Address);
102 if (!Pte->u.Hard.Valid)
103 return NULL;
104
105 /* Get the PPE */
106 Pte = MiAddressToPpe(Address);
107 if (!Pte->u.Hard.Valid)
108 return NULL;
109
110 /* Get the PDE */
111 Pte = MiAddressToPde(Address);
112 if (!Pte->u.Hard.Valid)
113 return NULL;
114 }
115
116 return MiAddressToPte(Address);
117 }
118
119 static
120 ULONG64
121 MiGetPteValueForProcess(
122 PEPROCESS Process,
123 PVOID Address)
124 {
125 PMMPTE Pte;
126 ULONG64 PteValue;
127
128 Pte = MiGetPteForProcess(Process, Address, FALSE);
129 PteValue = Pte ? Pte->u.Long : 0;
130
131 if (MiIsHyperspaceAddress(Pte))
132 MmDeleteHyperspaceMapping((PVOID)PAGE_ROUND_DOWN(Pte));
133
134 return PteValue;
135 }
136
137 ULONG
138 NTAPI
139 MiGetPteProtection(MMPTE Pte)
140 {
141 ULONG Protect;
142
143 if (!Pte.u.Flush.Valid)
144 {
145 Protect = PAGE_NOACCESS;
146 }
147 else if (Pte.u.Flush.NoExecute)
148 {
149 if (Pte.u.Flush.CopyOnWrite)
150 Protect = PAGE_WRITECOPY;
151 else if (Pte.u.Flush.Write)
152 Protect = PAGE_READWRITE;
153 else
154 Protect = PAGE_READONLY;
155 }
156 else
157 {
158 if (Pte.u.Flush.CopyOnWrite)
159 Protect = PAGE_EXECUTE_WRITECOPY;
160 else if (Pte.u.Flush.Write)
161 Protect = PAGE_EXECUTE_READWRITE;
162 else
163 Protect = PAGE_EXECUTE_READ;
164 }
165
166 if (Pte.u.Flush.CacheDisable)
167 Protect |= PAGE_NOCACHE;
168
169 if (Pte.u.Flush.WriteThrough)
170 Protect |= PAGE_WRITETHROUGH;
171
172 // PAGE_GUARD ?
173 return Protect;
174 }
175
176 VOID
177 NTAPI
178 MiSetPteProtection(PMMPTE Pte, ULONG Protection)
179 {
180 Pte->u.Flush.CopyOnWrite = (Protection & PAGE_WRITECOPY_ANY) ? 1 : 0;
181 Pte->u.Flush.Write = (Protection & PAGE_WRITE_ANY) ? 1 : 0;
182 Pte->u.Flush.CacheDisable = (Protection & PAGE_NOCACHE) ? 1 : 0;
183 Pte->u.Flush.WriteThrough = (Protection & PAGE_WRITETHROUGH) ? 1 : 0;
184
185 // FIXME: This doesn't work. Why?
186 // Pte->u.Flush.NoExecute = (Protection & PAGE_EXECUTE_ANY) ? 0 : 1;
187 }
188
189 /* FUNCTIONS ***************************************************************/
190
191 PFN_NUMBER
192 NTAPI
193 MmGetPfnForProcess(PEPROCESS Process,
194 PVOID Address)
195 {
196 MMPTE Pte;
197 Pte.u.Long = MiGetPteValueForProcess(Process, Address);
198 return Pte.u.Hard.Valid ? Pte.u.Hard.PageFrameNumber : 0;
199 }
200
201 PHYSICAL_ADDRESS
202 NTAPI
203 MmGetPhysicalAddress(PVOID Address)
204 {
205 PHYSICAL_ADDRESS p;
206 MMPTE Pte;
207
208 Pte.u.Long = MiGetPteValueForProcess(NULL, Address);
209 if (Pte.u.Hard.Valid)
210 {
211 p.QuadPart = Pte.u.Hard.PageFrameNumber * PAGE_SIZE;
212 p.u.LowPart |= (ULONG_PTR)Address & (PAGE_SIZE - 1);
213 }
214 else
215 {
216 p.QuadPart = 0;
217 }
218
219 return p;
220 }
221
222 BOOLEAN
223 NTAPI
224 MmIsPagePresent(PEPROCESS Process, PVOID Address)
225 {
226 MMPTE Pte;
227 Pte.u.Long = MiGetPteValueForProcess(Process, Address);
228 return Pte.u.Hard.Valid;
229 }
230
231 BOOLEAN
232 NTAPI
233 MmIsPageSwapEntry(PEPROCESS Process, PVOID Address)
234 {
235 MMPTE Pte;
236 Pte.u.Long = MiGetPteValueForProcess(Process, Address);
237 return Pte.u.Hard.Valid && Pte.u.Soft.Transition;
238 }
239
240 BOOLEAN
241 NTAPI
242 MmIsDirtyPage(PEPROCESS Process, PVOID Address)
243 {
244 MMPTE Pte;
245 Pte.u.Long = MiGetPteValueForProcess(Process, Address);
246 return Pte.u.Hard.Valid && Pte.u.Hard.Dirty;
247 }
248
249 ULONG
250 NTAPI
251 MmGetPageProtect(PEPROCESS Process, PVOID Address)
252 {
253 MMPTE Pte;
254
255 Pte.u.Long = MiGetPteValueForProcess(Process, Address);
256
257 return MiGetPteProtection(Pte);
258 }
259
260 VOID
261 NTAPI
262 MmSetPageProtect(PEPROCESS Process, PVOID Address, ULONG flProtect)
263 {
264 PMMPTE Pte;
265 MMPTE NewPte;
266
267 Pte = MiGetPteForProcess(Process, Address, FALSE);
268 ASSERT(Pte != NULL);
269
270 NewPte = *Pte;
271
272 MiSetPteProtection(&NewPte, flProtect);
273
274 InterlockedExchangePte(Pte, NewPte);
275
276 MiFlushTlb(Pte, Address);
277 }
278
279 VOID
280 NTAPI
281 MmSetCleanPage(PEPROCESS Process, PVOID Address)
282 {
283 PMMPTE Pte;
284
285 Pte = MiGetPteForProcess(Process, Address, FALSE);
286 if (!Pte)
287 {
288 KeBugCheckEx(MEMORY_MANAGEMENT, 0x1234, (ULONG64)Address, 0, 0);
289 }
290
291 /* Ckear the dirty bit */
292 if (InterlockedBitTestAndReset64((PVOID)Pte, 6))
293 {
294 if (!MiIsHyperspaceAddress(Pte))
295 __invlpg(Address);
296 }
297
298 MiFlushTlb(Pte, Address);
299 }
300
301 VOID
302 NTAPI
303 MmSetDirtyPage(PEPROCESS Process, PVOID Address)
304 {
305 PMMPTE Pte;
306
307 Pte = MiGetPteForProcess(Process, Address, FALSE);
308 if (!Pte)
309 {
310 KeBugCheckEx(MEMORY_MANAGEMENT, 0x1234, (ULONG64)Address, 0, 0);
311 }
312
313 /* Ckear the dirty bit */
314 if (InterlockedBitTestAndSet64((PVOID)Pte, 6))
315 {
316 if (!MiIsHyperspaceAddress(Pte))
317 __invlpg(Address);
318 }
319
320 MiFlushTlb(Pte, Address);
321 }
322
323
324 NTSTATUS
325 NTAPI
326 Mmi386ReleaseMmInfo(PEPROCESS Process)
327 {
328 UNIMPLEMENTED;
329 return STATUS_UNSUCCESSFUL;
330 }
331
332 VOID
333 NTAPI
334 MmDisableVirtualMapping(PEPROCESS Process, PVOID Address, BOOLEAN* WasDirty, PPFN_NUMBER Page)
335 {
336 UNIMPLEMENTED;
337 }
338
339 VOID
340 NTAPI
341 MmRawDeleteVirtualMapping(PVOID Address)
342 {
343 UNIMPLEMENTED;
344 }
345
346 VOID
347 NTAPI
348 MmDeleteVirtualMapping(
349 PEPROCESS Process,
350 PVOID Address,
351 BOOLEAN FreePage,
352 BOOLEAN* WasDirty,
353 PPFN_NUMBER Page)
354 {
355 PFN_NUMBER Pfn;
356 PMMPTE Pte;
357 MMPTE OldPte;
358
359 Pte = MiGetPteForProcess(Process, Address, FALSE);
360
361 if (Pte)
362 {
363 /* Atomically set the entry to zero and get the old value. */
364 OldPte.u.Long = InterlockedExchange64((LONG64*)&Pte->u.Long, 0);
365
366 if (OldPte.u.Hard.Valid)
367 {
368 Pfn = OldPte.u.Hard.PageFrameNumber;
369
370 if (FreePage)
371 MmReleasePageMemoryConsumer(MC_NPPOOL, Pfn);
372 }
373 else
374 Pfn = 0;
375 }
376 else
377 {
378 OldPte.u.Long = 0;
379 Pfn = 0;
380 }
381
382 /* Return information to the caller */
383 if (WasDirty)
384 *WasDirty = OldPte.u.Hard.Dirty;;
385
386 if (Page)
387 *Page = Pfn;
388
389 MiFlushTlb(Pte, Address);
390 }
391
392 VOID
393 NTAPI
394 MmDeletePageFileMapping(PEPROCESS Process, PVOID Address,
395 SWAPENTRY* SwapEntry)
396 {
397 UNIMPLEMENTED;
398 }
399
400
401 VOID
402 NTAPI
403 MmEnableVirtualMapping(PEPROCESS Process, PVOID Address)
404 {
405 UNIMPLEMENTED;
406 }
407
408
409 NTSTATUS
410 NTAPI
411 MmCreatePageFileMapping(PEPROCESS Process,
412 PVOID Address,
413 SWAPENTRY SwapEntry)
414 {
415 UNIMPLEMENTED;
416 return STATUS_UNSUCCESSFUL;
417 }
418
419
420 NTSTATUS
421 NTAPI
422 MmCreateVirtualMappingUnsafe(
423 PEPROCESS Process,
424 PVOID Address,
425 ULONG PageProtection,
426 PPFN_NUMBER Pages,
427 ULONG PageCount)
428 {
429 ULONG i;
430 MMPTE TmplPte, *Pte;
431
432 /* Check if the range is valid */
433 if ((Process == NULL && Address < MmSystemRangeStart) ||
434 (Process != NULL && Address > MmHighestUserAddress))
435 {
436 DPRINT1("Address 0x%p is invalid for process %p\n", Address, Process);
437 ASSERT(FALSE);
438 }
439
440 TmplPte.u.Long = 0;
441 TmplPte.u.Hard.Valid = 1;
442 MiSetPteProtection(&TmplPte, PageProtection);
443
444 //__debugbreak();
445
446 for (i = 0; i < PageCount; i++)
447 {
448 TmplPte.u.Hard.PageFrameNumber = Pages[i];
449
450 Pte = MiGetPteForProcess(Process, Address, TRUE);
451
452 DPRINT1("MmCreateVirtualMappingUnsafe, Address=%p, TmplPte=%p, Pte=%p\n",
453 Address, TmplPte.u.Long, Pte);
454
455 if (InterlockedExchangePte(Pte, TmplPte))
456 {
457 KeInvalidateTlbEntry(Address);
458 }
459
460 if (MiIsHyperspaceAddress(Pte))
461 MmDeleteHyperspaceMapping((PVOID)PAGE_ROUND_DOWN(Pte));
462
463 Address = (PVOID)((ULONG64)Address + PAGE_SIZE);
464 }
465
466
467 return STATUS_SUCCESS;
468 }
469
470 NTSTATUS
471 NTAPI
472 MmCreateVirtualMapping(PEPROCESS Process,
473 PVOID Address,
474 ULONG Protect,
475 PPFN_NUMBER Pages,
476 ULONG PageCount)
477 {
478 ULONG i;
479
480 for (i = 0; i < PageCount; i++)
481 {
482 if (!MmIsPageInUse(Pages[i]))
483 {
484 DPRINT1("Page %x not in use\n", Pages[i]);
485 KeBugCheck(MEMORY_MANAGEMENT);
486 }
487 }
488
489 return MmCreateVirtualMappingUnsafe(Process, Address, Protect, Pages, PageCount);
490 }
491
492 NTSTATUS
493 NTAPI
494 MmInitializeHandBuiltProcess(IN PEPROCESS Process,
495 IN PULONG_PTR DirectoryTableBase)
496 {
497 /* Share the directory base with the idle process */
498 DirectoryTableBase[0] = PsGetCurrentProcess()->Pcb.DirectoryTableBase[0];
499 DirectoryTableBase[1] = PsGetCurrentProcess()->Pcb.DirectoryTableBase[1];
500
501 /* Initialize the Addresss Space */
502 KeInitializeGuardedMutex(&Process->AddressCreationLock);
503 Process->Vm.WorkingSetExpansionLinks.Flink = NULL;
504 ASSERT(Process->VadRoot.NumberGenericTableElements == 0);
505 Process->VadRoot.BalancedRoot.u1.Parent = &Process->VadRoot.BalancedRoot;
506
507 /* The process now has an address space */
508 Process->HasAddressSpace = TRUE;
509 return STATUS_SUCCESS;
510 }
511
512 BOOLEAN
513 NTAPI
514 MmCreateProcessAddressSpace(IN ULONG MinWs,
515 IN PEPROCESS Process,
516 IN PULONG_PTR DirectoryTableBase)
517 {
518 UNIMPLEMENTED;
519 return 0;
520 }
521
522 BOOLEAN
523 NTAPI
524 _MmIsAddressValid(IN PVOID VirtualAddress)
525 {
526 /* Check all four page table levels */
527 return (MiAddressToPxe(VirtualAddress)->u.Hard.Valid != 0 &&
528 MiAddressToPpe(VirtualAddress)->u.Hard.Valid != 0 &&
529 MiAddressToPde(VirtualAddress)->u.Hard.Valid != 0 &&
530 MiAddressToPte(VirtualAddress)->u.Hard.Valid != 0);
531 }
532
533
534 /* EOF */