2006-11-08 11:47:44 +00:00
|
|
|
/*
|
|
|
|
* PROJECT: ReactOS Kernel
|
|
|
|
* LICENSE: GPL - See COPYING in the top level directory
|
|
|
|
* FILE: ntoskrnl/ke/i386/patpge.c
|
|
|
|
* PURPOSE: Support for PAT and PGE (Large Pages)
|
|
|
|
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* INCLUDES ******************************************************************/
|
|
|
|
|
|
|
|
#include <ntoskrnl.h>
|
|
|
|
#define NDEBUG
|
|
|
|
#include <debug.h>
|
|
|
|
|
2013-07-16 13:49:03 +00:00
|
|
|
#define PDE_BITS 10
|
|
|
|
#define PTE_BITS 10
|
|
|
|
|
2006-11-08 11:47:44 +00:00
|
|
|
/* FUNCTIONS *****************************************************************/
|
|
|
|
|
2020-10-06 19:44:01 +00:00
|
|
|
CODE_SEG("INIT")
|
2006-11-08 11:47:44 +00:00
|
|
|
ULONG_PTR
|
|
|
|
NTAPI
|
2013-10-19 11:33:34 +00:00
|
|
|
Ki386EnableGlobalPage(IN ULONG_PTR Context)
|
2006-11-08 11:47:44 +00:00
|
|
|
{
|
[NTOS]
Add super-complicated handling of global pages to KeFlushCurrentTb (pretty much the same code which has been in HalpFlushTLB for the past ~6 years). This should be all that is required to make this feature work (everything else being in place already), and *seems* to work fine but is disabled under a switch until tested thoroughly.
Global pages, an important optimization that allows for not flushing the whole x86 TLB every time CR3 is changed (typically on context switch to a new process, or during process attach/detach), relies on us doing extra work whenever we do alter a global page. This is likely where any bugs will have to be flushed out!
Fixup Ki386EnableGlobalPage while we are at it -- disable/restore interrupts properly, and verify PGE-bit isn't set (nothing should have touched it before this routine, which is responsible for initializing it, so we shouldn't have to disable it). Fix, but disable, the CPU-sync spin as well as there should be no particular reason to do this for PGE-enabling during initialization (no other processor will be messing with PTEs at this stage, as compared to a call to KeFlushEntireTb).
Everyone, repeat after me: Global pages are awesome!
svn path=/trunk/; revision=69528
2015-10-14 19:33:35 +00:00
|
|
|
//PLONG Count;
|
|
|
|
#if defined(_GLOBAL_PAGES_ARE_AWESOME_)
|
|
|
|
ULONG Cr4;
|
|
|
|
#endif
|
|
|
|
BOOLEAN Enable;
|
2006-11-08 11:47:44 +00:00
|
|
|
|
|
|
|
/* Disable interrupts */
|
[NTOS]
Add super-complicated handling of global pages to KeFlushCurrentTb (pretty much the same code which has been in HalpFlushTLB for the past ~6 years). This should be all that is required to make this feature work (everything else being in place already), and *seems* to work fine but is disabled under a switch until tested thoroughly.
Global pages, an important optimization that allows for not flushing the whole x86 TLB every time CR3 is changed (typically on context switch to a new process, or during process attach/detach), relies on us doing extra work whenever we do alter a global page. This is likely where any bugs will have to be flushed out!
Fixup Ki386EnableGlobalPage while we are at it -- disable/restore interrupts properly, and verify PGE-bit isn't set (nothing should have touched it before this routine, which is responsible for initializing it, so we shouldn't have to disable it). Fix, but disable, the CPU-sync spin as well as there should be no particular reason to do this for PGE-enabling during initialization (no other processor will be messing with PTEs at this stage, as compared to a call to KeFlushEntireTb).
Everyone, repeat after me: Global pages are awesome!
svn path=/trunk/; revision=69528
2015-10-14 19:33:35 +00:00
|
|
|
Enable = KeDisableInterrupts();
|
2006-11-08 11:47:44 +00:00
|
|
|
|
[NTOS]
Add super-complicated handling of global pages to KeFlushCurrentTb (pretty much the same code which has been in HalpFlushTLB for the past ~6 years). This should be all that is required to make this feature work (everything else being in place already), and *seems* to work fine but is disabled under a switch until tested thoroughly.
Global pages, an important optimization that allows for not flushing the whole x86 TLB every time CR3 is changed (typically on context switch to a new process, or during process attach/detach), relies on us doing extra work whenever we do alter a global page. This is likely where any bugs will have to be flushed out!
Fixup Ki386EnableGlobalPage while we are at it -- disable/restore interrupts properly, and verify PGE-bit isn't set (nothing should have touched it before this routine, which is responsible for initializing it, so we shouldn't have to disable it). Fix, but disable, the CPU-sync spin as well as there should be no particular reason to do this for PGE-enabling during initialization (no other processor will be messing with PTEs at this stage, as compared to a call to KeFlushEntireTb).
Everyone, repeat after me: Global pages are awesome!
svn path=/trunk/; revision=69528
2015-10-14 19:33:35 +00:00
|
|
|
/* Spin until other processors are ready */
|
|
|
|
//Count = (PLONG)Context;
|
|
|
|
//InterlockedDecrement(Count);
|
|
|
|
//while (*Count) YieldProcessor();
|
2006-11-08 11:47:44 +00:00
|
|
|
|
[NTOS]
Add super-complicated handling of global pages to KeFlushCurrentTb (pretty much the same code which has been in HalpFlushTLB for the past ~6 years). This should be all that is required to make this feature work (everything else being in place already), and *seems* to work fine but is disabled under a switch until tested thoroughly.
Global pages, an important optimization that allows for not flushing the whole x86 TLB every time CR3 is changed (typically on context switch to a new process, or during process attach/detach), relies on us doing extra work whenever we do alter a global page. This is likely where any bugs will have to be flushed out!
Fixup Ki386EnableGlobalPage while we are at it -- disable/restore interrupts properly, and verify PGE-bit isn't set (nothing should have touched it before this routine, which is responsible for initializing it, so we shouldn't have to disable it). Fix, but disable, the CPU-sync spin as well as there should be no particular reason to do this for PGE-enabling during initialization (no other processor will be messing with PTEs at this stage, as compared to a call to KeFlushEntireTb).
Everyone, repeat after me: Global pages are awesome!
svn path=/trunk/; revision=69528
2015-10-14 19:33:35 +00:00
|
|
|
#if defined(_GLOBAL_PAGES_ARE_AWESOME_)
|
2006-11-08 11:47:44 +00:00
|
|
|
|
[NTOS]
Add super-complicated handling of global pages to KeFlushCurrentTb (pretty much the same code which has been in HalpFlushTLB for the past ~6 years). This should be all that is required to make this feature work (everything else being in place already), and *seems* to work fine but is disabled under a switch until tested thoroughly.
Global pages, an important optimization that allows for not flushing the whole x86 TLB every time CR3 is changed (typically on context switch to a new process, or during process attach/detach), relies on us doing extra work whenever we do alter a global page. This is likely where any bugs will have to be flushed out!
Fixup Ki386EnableGlobalPage while we are at it -- disable/restore interrupts properly, and verify PGE-bit isn't set (nothing should have touched it before this routine, which is responsible for initializing it, so we shouldn't have to disable it). Fix, but disable, the CPU-sync spin as well as there should be no particular reason to do this for PGE-enabling during initialization (no other processor will be messing with PTEs at this stage, as compared to a call to KeFlushEntireTb).
Everyone, repeat after me: Global pages are awesome!
svn path=/trunk/; revision=69528
2015-10-14 19:33:35 +00:00
|
|
|
/* Get CR4 and ensure global pages are disabled */
|
2006-11-08 11:47:44 +00:00
|
|
|
Cr4 = __readcr4();
|
[NTOS]
Add super-complicated handling of global pages to KeFlushCurrentTb (pretty much the same code which has been in HalpFlushTLB for the past ~6 years). This should be all that is required to make this feature work (everything else being in place already), and *seems* to work fine but is disabled under a switch until tested thoroughly.
Global pages, an important optimization that allows for not flushing the whole x86 TLB every time CR3 is changed (typically on context switch to a new process, or during process attach/detach), relies on us doing extra work whenever we do alter a global page. This is likely where any bugs will have to be flushed out!
Fixup Ki386EnableGlobalPage while we are at it -- disable/restore interrupts properly, and verify PGE-bit isn't set (nothing should have touched it before this routine, which is responsible for initializing it, so we shouldn't have to disable it). Fix, but disable, the CPU-sync spin as well as there should be no particular reason to do this for PGE-enabling during initialization (no other processor will be messing with PTEs at this stage, as compared to a call to KeFlushEntireTb).
Everyone, repeat after me: Global pages are awesome!
svn path=/trunk/; revision=69528
2015-10-14 19:33:35 +00:00
|
|
|
ASSERT(!(Cr4 & CR4_PGE));
|
2006-11-08 11:47:44 +00:00
|
|
|
|
[NTOS]
Add super-complicated handling of global pages to KeFlushCurrentTb (pretty much the same code which has been in HalpFlushTLB for the past ~6 years). This should be all that is required to make this feature work (everything else being in place already), and *seems* to work fine but is disabled under a switch until tested thoroughly.
Global pages, an important optimization that allows for not flushing the whole x86 TLB every time CR3 is changed (typically on context switch to a new process, or during process attach/detach), relies on us doing extra work whenever we do alter a global page. This is likely where any bugs will have to be flushed out!
Fixup Ki386EnableGlobalPage while we are at it -- disable/restore interrupts properly, and verify PGE-bit isn't set (nothing should have touched it before this routine, which is responsible for initializing it, so we shouldn't have to disable it). Fix, but disable, the CPU-sync spin as well as there should be no particular reason to do this for PGE-enabling during initialization (no other processor will be messing with PTEs at this stage, as compared to a call to KeFlushEntireTb).
Everyone, repeat after me: Global pages are awesome!
svn path=/trunk/; revision=69528
2015-10-14 19:33:35 +00:00
|
|
|
/* Reset CR3 to flush the TLB */
|
|
|
|
__writecr3(__readcr3());
|
2006-11-08 11:47:44 +00:00
|
|
|
|
|
|
|
/* Now enable PGE */
|
[NTOS]
Add super-complicated handling of global pages to KeFlushCurrentTb (pretty much the same code which has been in HalpFlushTLB for the past ~6 years). This should be all that is required to make this feature work (everything else being in place already), and *seems* to work fine but is disabled under a switch until tested thoroughly.
Global pages, an important optimization that allows for not flushing the whole x86 TLB every time CR3 is changed (typically on context switch to a new process, or during process attach/detach), relies on us doing extra work whenever we do alter a global page. This is likely where any bugs will have to be flushed out!
Fixup Ki386EnableGlobalPage while we are at it -- disable/restore interrupts properly, and verify PGE-bit isn't set (nothing should have touched it before this routine, which is responsible for initializing it, so we shouldn't have to disable it). Fix, but disable, the CPU-sync spin as well as there should be no particular reason to do this for PGE-enabling during initialization (no other processor will be messing with PTEs at this stage, as compared to a call to KeFlushEntireTb).
Everyone, repeat after me: Global pages are awesome!
svn path=/trunk/; revision=69528
2015-10-14 19:33:35 +00:00
|
|
|
__writecr4(Cr4 | CR4_PGE);
|
|
|
|
|
|
|
|
#endif
|
2006-11-08 11:47:44 +00:00
|
|
|
|
[NTOS]
Add super-complicated handling of global pages to KeFlushCurrentTb (pretty much the same code which has been in HalpFlushTLB for the past ~6 years). This should be all that is required to make this feature work (everything else being in place already), and *seems* to work fine but is disabled under a switch until tested thoroughly.
Global pages, an important optimization that allows for not flushing the whole x86 TLB every time CR3 is changed (typically on context switch to a new process, or during process attach/detach), relies on us doing extra work whenever we do alter a global page. This is likely where any bugs will have to be flushed out!
Fixup Ki386EnableGlobalPage while we are at it -- disable/restore interrupts properly, and verify PGE-bit isn't set (nothing should have touched it before this routine, which is responsible for initializing it, so we shouldn't have to disable it). Fix, but disable, the CPU-sync spin as well as there should be no particular reason to do this for PGE-enabling during initialization (no other processor will be messing with PTEs at this stage, as compared to a call to KeFlushEntireTb).
Everyone, repeat after me: Global pages are awesome!
svn path=/trunk/; revision=69528
2015-10-14 19:33:35 +00:00
|
|
|
/* Restore interrupts and return */
|
|
|
|
KeRestoreInterrupts(Enable);
|
2006-11-08 11:47:44 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-10-06 19:44:01 +00:00
|
|
|
CODE_SEG("INIT")
|
2006-11-08 11:47:44 +00:00
|
|
|
VOID
|
|
|
|
NTAPI
|
|
|
|
KiInitializePAT(VOID)
|
|
|
|
{
|
|
|
|
/* FIXME: Support this */
|
2011-09-25 09:12:26 +00:00
|
|
|
DPRINT("PAT support detected but not yet taken advantage of\n");
|
2006-11-08 11:47:44 +00:00
|
|
|
}
|
2013-07-16 13:49:03 +00:00
|
|
|
|
2020-10-06 19:44:01 +00:00
|
|
|
CODE_SEG("INIT")
|
2013-07-16 13:49:03 +00:00
|
|
|
ULONG_PTR
|
|
|
|
NTAPI
|
|
|
|
Ki386EnableTargetLargePage(IN ULONG_PTR Context)
|
|
|
|
{
|
|
|
|
PLARGE_IDENTITY_MAP IdentityMap = (PLARGE_IDENTITY_MAP)Context;
|
|
|
|
|
|
|
|
/* Call helper function with the start address and temporary page table pointer */
|
|
|
|
Ki386EnableCurrentLargePage(IdentityMap->StartAddress, IdentityMap->Cr3);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
PVOID
|
|
|
|
NTAPI
|
|
|
|
Ki386AllocateContiguousMemory(PLARGE_IDENTITY_MAP IdentityMap,
|
|
|
|
ULONG PagesCount,
|
|
|
|
BOOLEAN InLower4Gb)
|
|
|
|
{
|
|
|
|
PHYSICAL_ADDRESS AddressMask;
|
|
|
|
PVOID Result;
|
|
|
|
ULONG SizeInBytes = PagesCount * PAGE_SIZE;
|
|
|
|
|
|
|
|
/* Initialize acceptable address mask to any possible address */
|
|
|
|
AddressMask.LowPart = 0xFFFFFFFF;
|
|
|
|
AddressMask.HighPart = 0xFFFFFFFF;
|
|
|
|
|
|
|
|
/* Mark out higher 4Gb if caller asked so */
|
|
|
|
if (InLower4Gb) AddressMask.HighPart = 0;
|
|
|
|
|
|
|
|
/* Try to allocate the memory */
|
|
|
|
Result = MmAllocateContiguousMemory(SizeInBytes, AddressMask);
|
|
|
|
if (!Result) return NULL;
|
|
|
|
|
|
|
|
/* Zero it out */
|
|
|
|
RtlZeroMemory(Result, SizeInBytes);
|
|
|
|
|
|
|
|
/* Track allocated pages in the IdentityMap structure */
|
|
|
|
IdentityMap->PagesList[IdentityMap->PagesCount] = Result;
|
|
|
|
IdentityMap->PagesCount++;
|
|
|
|
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
|
|
|
PHYSICAL_ADDRESS
|
|
|
|
NTAPI
|
|
|
|
Ki386BuildIdentityBuffer(PLARGE_IDENTITY_MAP IdentityMap,
|
|
|
|
PVOID StartPtr,
|
|
|
|
ULONG Length)
|
|
|
|
{
|
|
|
|
// TODO: Check whether all pages are below 4GB boundary
|
|
|
|
return MmGetPhysicalAddress(StartPtr);
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
|
|
NTAPI
|
|
|
|
Ki386IdentityMapMakeValid(PLARGE_IDENTITY_MAP IdentityMap,
|
|
|
|
PHARDWARE_PTE Pde,
|
|
|
|
PHARDWARE_PTE *PageTable)
|
|
|
|
{
|
|
|
|
ULONG NewPage;
|
|
|
|
|
|
|
|
if (Pde->Valid == 0)
|
|
|
|
{
|
|
|
|
/* Invalid, so allocate a new page for it */
|
|
|
|
NewPage = (ULONG)Ki386AllocateContiguousMemory(IdentityMap, 1, FALSE);
|
|
|
|
if (!NewPage) return FALSE;
|
|
|
|
|
|
|
|
/* Set PFN to its virtual address and mark it as valid */
|
|
|
|
Pde->PageFrameNumber = NewPage >> PAGE_SHIFT;
|
|
|
|
Pde->Valid = 1;
|
|
|
|
|
|
|
|
/* Pass page table address to the caller */
|
|
|
|
if (PageTable) *PageTable = (PHARDWARE_PTE)NewPage;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Valid entry, just pass the page table address to the caller */
|
|
|
|
if (PageTable)
|
|
|
|
*PageTable = (PHARDWARE_PTE)(Pde->PageFrameNumber << PAGE_SHIFT);
|
|
|
|
}
|
2013-10-19 11:33:34 +00:00
|
|
|
|
2013-07-16 13:49:03 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
|
|
NTAPI
|
|
|
|
Ki386MapAddress(PLARGE_IDENTITY_MAP IdentityMap,
|
|
|
|
ULONG_PTR VirtualPtr,
|
|
|
|
PHYSICAL_ADDRESS PhysicalPtr)
|
|
|
|
{
|
|
|
|
PHARDWARE_PTE Pde, Pte;
|
|
|
|
PHARDWARE_PTE PageTable;
|
|
|
|
|
|
|
|
/* Allocate page directory on demand */
|
|
|
|
if (!IdentityMap->TopLevelDirectory)
|
|
|
|
{
|
|
|
|
IdentityMap->TopLevelDirectory = Ki386AllocateContiguousMemory(IdentityMap, 1, 1);
|
|
|
|
if (!IdentityMap->TopLevelDirectory) return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get PDE of VirtualPtr and make it writable and valid */
|
|
|
|
Pde = &IdentityMap->TopLevelDirectory[(VirtualPtr >> 22) & ((1 << PDE_BITS) - 1)];
|
|
|
|
if (!Ki386IdentityMapMakeValid(IdentityMap, Pde, &PageTable)) return FALSE;
|
|
|
|
Pde->Write = 1;
|
|
|
|
|
|
|
|
/* Get PTE of VirtualPtr, make it valid, and map PhysicalPtr there */
|
|
|
|
Pte = &PageTable[(VirtualPtr >> 12) & ((1 << PTE_BITS) - 1)];
|
|
|
|
Pte->Valid = 1;
|
2013-10-19 11:33:34 +00:00
|
|
|
Pte->PageFrameNumber = (PFN_NUMBER)(PhysicalPtr.QuadPart >> PAGE_SHIFT);
|
2013-07-16 13:49:03 +00:00
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
NTAPI
|
|
|
|
Ki386ConvertPte(PHARDWARE_PTE Pte)
|
|
|
|
{
|
|
|
|
PVOID VirtualPtr;
|
|
|
|
PHYSICAL_ADDRESS PhysicalPtr;
|
|
|
|
|
|
|
|
/* Get virtual and physical addresses */
|
|
|
|
VirtualPtr = (PVOID)(Pte->PageFrameNumber << PAGE_SHIFT);
|
|
|
|
PhysicalPtr = MmGetPhysicalAddress(VirtualPtr);
|
|
|
|
|
|
|
|
/* Map its physical address in the page table provided by the caller */
|
2013-10-19 11:33:34 +00:00
|
|
|
Pte->PageFrameNumber = (PFN_NUMBER)(PhysicalPtr.QuadPart >> PAGE_SHIFT);
|
2013-07-16 13:49:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
|
|
NTAPI
|
|
|
|
Ki386CreateIdentityMap(PLARGE_IDENTITY_MAP IdentityMap, PVOID StartPtr, ULONG PagesCount)
|
|
|
|
{
|
|
|
|
ULONG_PTR Ptr;
|
|
|
|
ULONG PteIndex;
|
|
|
|
PHYSICAL_ADDRESS IdentityPtr;
|
|
|
|
|
|
|
|
/* Zero out the IdentityMap contents */
|
|
|
|
RtlZeroMemory(IdentityMap, sizeof(LARGE_IDENTITY_MAP));
|
|
|
|
|
|
|
|
/* Get the pointer to the physical address and save it in the struct */
|
|
|
|
IdentityPtr = Ki386BuildIdentityBuffer(IdentityMap, StartPtr, PagesCount);
|
|
|
|
IdentityMap->StartAddress = IdentityPtr.LowPart;
|
|
|
|
if (IdentityMap->StartAddress == 0)
|
|
|
|
{
|
|
|
|
DPRINT1("Failed to get buffer for large pages identity mapping\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
DPRINT("IdentityMap->StartAddress %p\n", IdentityMap->StartAddress);
|
|
|
|
|
|
|
|
/* Map all pages */
|
|
|
|
for (Ptr = (ULONG_PTR)StartPtr;
|
|
|
|
Ptr < (ULONG_PTR)StartPtr + PagesCount * PAGE_SIZE;
|
|
|
|
Ptr += PAGE_SIZE, IdentityPtr.QuadPart += PAGE_SIZE)
|
|
|
|
{
|
|
|
|
/* Map virtual address */
|
|
|
|
if (!Ki386MapAddress(IdentityMap, Ptr, IdentityPtr)) return FALSE;
|
|
|
|
|
|
|
|
/* Map physical address */
|
|
|
|
if (!Ki386MapAddress(IdentityMap, IdentityPtr.LowPart, IdentityPtr)) return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Convert all PTEs in the page directory from virtual to physical,
|
|
|
|
because Ki386IdentityMapMakeValid mapped only virtual addresses */
|
|
|
|
for (PteIndex = 0; PteIndex < (PAGE_SIZE / sizeof(HARDWARE_PTE)); PteIndex++)
|
|
|
|
{
|
|
|
|
if (IdentityMap->TopLevelDirectory[PteIndex].Valid != 0)
|
|
|
|
Ki386ConvertPte(&IdentityMap->TopLevelDirectory[PteIndex]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Save the page directory address (allocated by Ki386MapAddress) */
|
|
|
|
IdentityMap->Cr3 = MmGetPhysicalAddress(IdentityMap->TopLevelDirectory).LowPart;
|
|
|
|
|
|
|
|
DPRINT("IdentityMap->Cr3 0x%x\n", IdentityMap->Cr3);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
NTAPI
|
|
|
|
Ki386FreeIdentityMap(PLARGE_IDENTITY_MAP IdentityMap)
|
|
|
|
{
|
|
|
|
ULONG Page;
|
|
|
|
|
2013-08-31 16:02:13 +00:00
|
|
|
DPRINT("Freeing %lu pages allocated for identity mapping\n", IdentityMap->PagesCount);
|
2013-07-16 13:49:03 +00:00
|
|
|
|
|
|
|
/* Free all allocated pages, if any */
|
|
|
|
for (Page = 0; Page < IdentityMap->PagesCount; Page++)
|
|
|
|
MmFreeContiguousMemory(IdentityMap->PagesList[Page]);
|
|
|
|
}
|