Author: fireball Date: Tue Jul 16 13:49:03 2013 New Revision: 59491 URL: http://svn.reactos.org/svn/reactos?rev=59491&view=rev Log: [NTOS] - Implement enabling Large Pages support if it is detected. The Page Size Extension (PSE) bit in CR4 is enabled when paging is disabled. To achieve that, a temporary virtual and physical identity mapping is created, which is discarded after PSE is enabled.
Modified: trunk/reactos/ntoskrnl/include/internal/i386/ke.h trunk/reactos/ntoskrnl/ke/i386/ctxswitch.S trunk/reactos/ntoskrnl/ke/i386/kiinit.c trunk/reactos/ntoskrnl/ke/i386/patpge.c Modified: trunk/reactos/ntoskrnl/include/internal/i386/ke.h URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/include/internal/i386/ke.h?rev=59491&r1=59490&r2=59491&view=diff ============================================================================== --- trunk/reactos/ntoskrnl/include/internal/i386/ke.h [iso-8859-1] (original) +++ trunk/reactos/ntoskrnl/include/internal/i386/ke.h [iso-8859-1] Tue Jul 16 13:49:03 2013 @@ -163,6 +163,18 @@ KV86_FRAME V86Frame; } KV8086_STACK_FRAME, *PKV8086_STACK_FRAME; +// +// Large Pages Support +// +typedef struct _LARGE_IDENTITY_MAP +{ + PHARDWARE_PTE TopLevelDirectory; + ULONG Cr3; + ULONG_PTR StartAddress; + ULONG PagesCount; + PVOID PagesList[30]; +} LARGE_IDENTITY_MAP, *PLARGE_IDENTITY_MAP; + /* Diable interrupts and return whether they were enabled before */ FORCEINLINE BOOLEAN @@ -372,6 +384,33 @@ NTAPI Ki386EnableGlobalPage( IN volatile ULONG_PTR Context +); + +ULONG_PTR +NTAPI +Ki386EnableTargetLargePage( + IN volatile ULONG_PTR Context +); + +BOOLEAN +NTAPI +Ki386CreateIdentityMap( + IN PLARGE_IDENTITY_MAP IdentityMap, + IN PVOID StartPtr, + IN ULONG Length +); + +VOID +NTAPI +Ki386FreeIdentityMap( + IN PLARGE_IDENTITY_MAP IdentityMap +); + +VOID +NTAPI +Ki386EnableCurrentLargePage( + IN ULONG_PTR StartAddress, + IN ULONG Cr3 ); VOID Modified: trunk/reactos/ntoskrnl/ke/i386/ctxswitch.S URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/ke/i386/ctxswitch.S?rev=59491&r1=59490&r2=59491&view=diff ============================================================================== --- trunk/reactos/ntoskrnl/ke/i386/ctxswitch.S [iso-8859-1] (original) +++ trunk/reactos/ntoskrnl/ke/i386/ctxswitch.S [iso-8859-1] Tue Jul 16 13:49:03 2013 @@ -81,6 +81,61 @@ pop esp ret +PUBLIC _Ki386EnableCurrentLargePage@8 +_Ki386EnableCurrentLargePage@8: + /* Save StartAddress in eax */ + mov eax, [esp + 4] + + /* Save new CR3 value in ecx */ + mov ecx, [esp + 8] + + /* Save flags value */ + pushfd + + /* Disable interrupts */ + cli + + /* Compute linear address */ + sub eax, offset _Ki386EnableCurrentLargePage@8 + add eax, offset _Ki386LargePageIdentityLabel + + /* Save old CR3 in edx and replace with a new one */ + mov edx, cr3 + mov cr3, ecx + + /* Jump to the next instruction but in linear mapping */ + jmp eax + +_Ki386LargePageIdentityLabel: + /* Disable paging */ + mov eax, cr0 + and eax, NOT CR0_PG + mov cr0, eax + + /* Jump to the next instruction */ + jmp $+2 + + /* Enable Page Size Extension in CR4 */ + mov ecx, cr4 + or ecx, CR4_PSE + mov cr4, ecx + + /* Done, now re-enable paging */ + or eax, CR0_PG + mov cr0, eax + + /* Jump to virtual address */ + mov eax, offset VirtualSpace + jmp eax + +VirtualSpace: + /* Restore CR3 contents */ + mov cr3, edx + + /* Restore flags */ + popfd + + ret 8 /* FIXFIX: Move to C code ****/ PUBLIC _Ki386SetupAndExitToV86Mode@4 Modified: trunk/reactos/ntoskrnl/ke/i386/kiinit.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/ke/i386/kiinit.c?rev=59491&r1=59490&r2=59491&view=diff ============================================================================== --- trunk/reactos/ntoskrnl/ke/i386/kiinit.c [iso-8859-1] (original) +++ trunk/reactos/ntoskrnl/ke/i386/kiinit.c [iso-8859-1] Tue Jul 16 13:49:03 2013 @@ -46,12 +46,17 @@ ULONG Dummy; KI_SAMPLE_MAP Samples[4]; PKI_SAMPLE_MAP CurrentSample = Samples; + LARGE_IDENTITY_MAP IdentityMap; /* Check for large page support */ if (KeFeatureBits & KF_LARGE_PAGE) { - /* FIXME: Support this */ - DPRINT("Large Page support detected but not yet taken advantage of\n"); + /* Do an IPI to enable it on all CPUs */ + if (Ki386CreateIdentityMap(&IdentityMap, Ki386EnableCurrentLargePage, 2)) + KeIpiGenericCall(Ki386EnableTargetLargePage, (ULONG_PTR)&IdentityMap); + + /* Free the pages allocated for identity map */ + Ki386FreeIdentityMap(&IdentityMap); } /* Check for global page support */ Modified: trunk/reactos/ntoskrnl/ke/i386/patpge.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/ke/i386/patpge.c?rev=59491&r1=59490&r2=59491&view=diff ============================================================================== --- trunk/reactos/ntoskrnl/ke/i386/patpge.c [iso-8859-1] (original) +++ trunk/reactos/ntoskrnl/ke/i386/patpge.c [iso-8859-1] Tue Jul 16 13:49:03 2013 @@ -12,6 +12,9 @@ #define NDEBUG #include <debug.h> +#define PDE_BITS 10 +#define PTE_BITS 10 + /* FUNCTIONS *****************************************************************/ ULONG_PTR @@ -59,3 +62,194 @@ /* FIXME: Support this */ DPRINT("PAT support detected but not yet taken advantage of\n"); } + +ULONG_PTR +NTAPI +INIT_FUNCTION +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); + } + + 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; + Pte->PageFrameNumber = PhysicalPtr.QuadPart >> PAGE_SHIFT; + + 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 */ + Pte->PageFrameNumber = PhysicalPtr.QuadPart >> PAGE_SHIFT; +} + +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; + + DPRINT("Freeing %d pages allocated for identity mapping\n", IdentityMap->PagesCount); + + /* Free all allocated pages, if any */ + for (Page = 0; Page < IdentityMap->PagesCount; Page++) + MmFreeContiguousMemory(IdentityMap->PagesList[Page]); +}