From 5e0c3db2867defa76df0e83fd74a9a39e0dd622b Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Fri, 22 Mar 2013 18:30:01 +0000 Subject: [PATCH] Account for the physical map area in the kernel VM space. Fixes #9547. The physical memory map area was not included in the kernel virtual address space range (it was below KERNEL_BASE). This caused problems if an I/O operation took place on physical memory mapped there (the bad address error seen in #9547 was occurring in lock_memory_etc()). Changed KERNEL_BASE and KERNEL_SIZE to cover the area and add a null area that covers all of it. Also changed X86VMTranslationMap64Bit to handle large pages in Query(), as the physical map area uses large pages. --- headers/private/kernel/arch/x86/arch_kernel.h | 4 +- .../x86/paging/64bit/X86PagingMethod64Bit.cpp | 63 ++++++++++++++--- .../x86/paging/64bit/X86PagingMethod64Bit.h | 12 ++++ .../paging/64bit/X86VMTranslationMap64Bit.cpp | 67 ++++++------------- 4 files changed, 89 insertions(+), 57 deletions(-) diff --git a/headers/private/kernel/arch/x86/arch_kernel.h b/headers/private/kernel/arch/x86/arch_kernel.h index e6cf5db27b..02f9b1588c 100644 --- a/headers/private/kernel/arch/x86/arch_kernel.h +++ b/headers/private/kernel/arch/x86/arch_kernel.h @@ -30,8 +30,8 @@ // address where the kernel is loaded to: the kernel is loaded in the top 2GB // of the virtual address space as required by GCC's kernel code model. The // whole kernel address space is the top 512GB of the address space. -#define KERNEL_BASE 0xffffff8000000000 -#define KERNEL_SIZE 0x8000000000 +#define KERNEL_BASE 0xffffff0000000000 +#define KERNEL_SIZE 0x10000000000 #define KERNEL_TOP (KERNEL_BASE + (KERNEL_SIZE - 1)) #define KERNEL_LOAD_BASE 0xffffffff80000000 diff --git a/src/system/kernel/arch/x86/paging/64bit/X86PagingMethod64Bit.cpp b/src/system/kernel/arch/x86/paging/64bit/X86PagingMethod64Bit.cpp index e9dbf30c4b..d84199a44d 100644 --- a/src/system/kernel/arch/x86/paging/64bit/X86PagingMethod64Bit.cpp +++ b/src/system/kernel/arch/x86/paging/64bit/X86PagingMethod64Bit.cpp @@ -76,8 +76,16 @@ X86PagingMethod64Bit::Init(kernel_args* args, status_t X86PagingMethod64Bit::InitPostArea(kernel_args* args) { + // Create an area covering the physical map area. + void* address = (void*)KERNEL_PMAP_BASE; + area_id area = vm_create_null_area(VMAddressSpace::KernelID(), + "physical map area", &address, B_EXACT_ADDRESS, + KERNEL_PMAP_SIZE, 0); + if (area < B_OK) + return area; + // Create an area to represent the kernel PML4. - area_id area = create_area("kernel pml4", (void**)&fKernelVirtualPML4, + area = create_area("kernel pml4", (void**)&fKernelVirtualPML4, B_EXACT_ADDRESS, B_PAGE_SIZE, B_ALREADY_WIRED, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); if (area < B_OK) @@ -190,18 +198,15 @@ X86PagingMethod64Bit::IsKernelPageAccessible(addr_t virtualAddress, } -/*! Traverses down the paging structure hierarchy to find the page table for a - virtual address, allocating new tables if required. +/*! Traverses down the paging structure hierarchy to find the page directory + for a virtual address, allocating new tables if required. */ /*static*/ uint64* -X86PagingMethod64Bit::PageTableForAddress(uint64* virtualPML4, +X86PagingMethod64Bit::PageDirectoryForAddress(uint64* virtualPML4, addr_t virtualAddress, bool isKernel, bool allocateTables, vm_page_reservation* reservation, TranslationMapPhysicalPageMapper* pageMapper, int32& mapCount) { - TRACE("X86PagingMethod64Bit::PageTableForAddress(%#" B_PRIxADDR ", " - "%d)\n", virtualAddress, allocateTables); - // Get the PDPT. uint64* pml4e = &virtualPML4[VADDR_TO_PML4E(virtualAddress)]; if ((*pml4e & X86_64_PML4E_PRESENT) == 0) { @@ -259,11 +264,44 @@ X86PagingMethod64Bit::PageTableForAddress(uint64* virtualPML4, mapCount++; } - uint64* virtualPageDir = (uint64*)pageMapper->GetPageTableAt( + return (uint64*)pageMapper->GetPageTableAt( *pdpte & X86_64_PDPTE_ADDRESS_MASK); +} + + +/*static*/ uint64* +X86PagingMethod64Bit::PageDirectoryEntryForAddress(uint64* virtualPML4, + addr_t virtualAddress, bool isKernel, bool allocateTables, + vm_page_reservation* reservation, + TranslationMapPhysicalPageMapper* pageMapper, int32& mapCount) +{ + uint64* virtualPageDirectory = PageDirectoryForAddress(virtualPML4, + virtualAddress, isKernel, allocateTables, reservation, pageMapper, + mapCount); + if (virtualPageDirectory == NULL) + return NULL; + + return &virtualPageDirectory[VADDR_TO_PDE(virtualAddress)]; +} + + +/*! Traverses down the paging structure hierarchy to find the page table for a + virtual address, allocating new tables if required. +*/ +/*static*/ uint64* +X86PagingMethod64Bit::PageTableForAddress(uint64* virtualPML4, + addr_t virtualAddress, bool isKernel, bool allocateTables, + vm_page_reservation* reservation, + TranslationMapPhysicalPageMapper* pageMapper, int32& mapCount) +{ + TRACE("X86PagingMethod64Bit::PageTableForAddress(%#" B_PRIxADDR ", " + "%d)\n", virtualAddress, allocateTables); + + uint64* pde = PageDirectoryEntryForAddress(virtualPML4, virtualAddress, + isKernel, allocateTables, reservation, pageMapper, mapCount); + if (pde == NULL) + return NULL; - // Get the page table. - uint64* pde = &virtualPageDir[VADDR_TO_PDE(virtualAddress)]; if ((*pde & X86_64_PDE_PRESENT) == 0) { if (!allocateTables) return NULL; @@ -289,6 +327,11 @@ X86PagingMethod64Bit::PageTableForAddress(uint64* virtualPML4, mapCount++; } + // No proper large page support at the moment, but they are used for the + // physical map area. Ensure that nothing tries to treat that as normal + // address space. + ASSERT(!(*pde & X86_64_PDE_LARGE_PAGE)); + return (uint64*)pageMapper->GetPageTableAt(*pde & X86_64_PDE_ADDRESS_MASK); } diff --git a/src/system/kernel/arch/x86/paging/64bit/X86PagingMethod64Bit.h b/src/system/kernel/arch/x86/paging/64bit/X86PagingMethod64Bit.h index 130e14ddc0..f561d9e995 100644 --- a/src/system/kernel/arch/x86/paging/64bit/X86PagingMethod64Bit.h +++ b/src/system/kernel/arch/x86/paging/64bit/X86PagingMethod64Bit.h @@ -55,6 +55,18 @@ public: static X86PagingMethod64Bit* Method(); + static uint64* PageDirectoryForAddress(uint64* virtualPML4, + addr_t virtualAddress, bool isKernel, + bool allocateTables, + vm_page_reservation* reservation, + TranslationMapPhysicalPageMapper* + pageMapper, int32& mapCount); + static uint64* PageDirectoryEntryForAddress( + uint64* virtualPML4, addr_t virtualAddress, + bool isKernel, bool allocateTables, + vm_page_reservation* reservation, + TranslationMapPhysicalPageMapper* + pageMapper, int32& mapCount); static uint64* PageTableForAddress(uint64* virtualPML4, addr_t virtualAddress, bool isKernel, bool allocateTables, diff --git a/src/system/kernel/arch/x86/paging/64bit/X86VMTranslationMap64Bit.cpp b/src/system/kernel/arch/x86/paging/64bit/X86VMTranslationMap64Bit.cpp index 70ce26b320..0e8800b15a 100644 --- a/src/system/kernel/arch/x86/paging/64bit/X86VMTranslationMap64Bit.cpp +++ b/src/system/kernel/arch/x86/paging/64bit/X86VMTranslationMap64Bit.cpp @@ -141,7 +141,7 @@ X86VMTranslationMap64Bit::Init(bool kernel) // Assuming that only the top 2 PML4 entries are occupied for the // kernel. STATIC_ASSERT(KERNEL_PMAP_BASE == 0xffffff0000000000); - STATIC_ASSERT(KERNEL_BASE == 0xffffff8000000000); + STATIC_ASSERT(KERNEL_BASE == 0xffffff0000000000); // Allocate and clear the PML4. uint64* virtualPML4 = (uint64*)memalign(B_PAGE_SIZE, B_PAGE_SIZE); @@ -603,16 +603,26 @@ X86VMTranslationMap64Bit::Query(addr_t virtualAddress, ThreadCPUPinner pinner(thread_get_current_thread()); - // Look up the page table for the virtual address. - uint64* pte = X86PagingMethod64Bit::PageTableEntryForAddress( + // This function may be called on the physical map area, so we must handle + // large pages here. Look up the page directory entry for the virtual + // address. + uint64* pde = X86PagingMethod64Bit::PageDirectoryEntryForAddress( fPagingStructures->VirtualPML4(), virtualAddress, fIsKernelMap, false, NULL, fPageMapper, fMapCount); - if (pte == NULL) + if (pde == NULL || (*pde & X86_64_PDE_PRESENT) == 0) return B_OK; - uint64 entry = *pte; - - *_physicalAddress = entry & X86_64_PTE_ADDRESS_MASK; + uint64 entry; + if ((*pde & X86_64_PDE_LARGE_PAGE) != 0) { + entry = *pde; + *_physicalAddress = (entry & X86_64_PDE_ADDRESS_MASK) + + (virtualAddress % 0x200000); + } else { + uint64* virtualPageTable = (uint64*)fPageMapper->GetPageTableAt( + *pde & X86_64_PDE_ADDRESS_MASK); + entry = virtualPageTable[VADDR_TO_PTE(virtualAddress)]; + *_physicalAddress = entry & X86_64_PTE_ADDRESS_MASK; + } // Translate the page state flags. if ((entry & X86_64_PTE_USER) != 0) { @@ -627,8 +637,8 @@ X86VMTranslationMap64Bit::Query(addr_t virtualAddress, | ((entry & X86_64_PTE_PRESENT) != 0 ? PAGE_PRESENT : 0); TRACE("X86VMTranslationMap64Bit::Query(%#" B_PRIxADDR ") -> %#" - B_PRIxPHYSADDR " %#" B_PRIx32 " (pte: %p %#" B_PRIx64 ")\n", - virtualAddress, *_physicalAddress, *_flags, pte, entry); + B_PRIxPHYSADDR " %#" B_PRIx32 " (entry: %#" B_PRIx64 ")\n", + virtualAddress, *_physicalAddress, *_flags, entry); return B_OK; } @@ -638,42 +648,9 @@ status_t X86VMTranslationMap64Bit::QueryInterrupt(addr_t virtualAddress, phys_addr_t* _physicalAddress, uint32* _flags) { - *_flags = 0; - *_physicalAddress = 0; - - ThreadCPUPinner pinner(thread_get_current_thread()); - - // Look up the page table for the virtual address. - // FIXME: PageTableEntryForAddress uses GetPageTableAt() rather than - // InterruptGetPageTableAt(). This doesn't actually matter since in our - // page mapper both functions are the same, but perhaps this should be - // fixed for correctness. - uint64* pte = X86PagingMethod64Bit::PageTableEntryForAddress( - fPagingStructures->VirtualPML4(), virtualAddress, fIsKernelMap, - false, NULL, fPageMapper, fMapCount); - if (pte == NULL) - return B_OK; - - uint64 entry = *pte; - - *_physicalAddress = entry & X86_64_PTE_ADDRESS_MASK; - - // Translate the page state flags. - if ((entry & X86_64_PTE_USER) != 0) { - *_flags |= ((entry & X86_64_PTE_WRITABLE) != 0 ? B_WRITE_AREA : 0) - | B_READ_AREA; - } - - *_flags |= ((entry & X86_64_PTE_WRITABLE) != 0 ? B_KERNEL_WRITE_AREA : 0) - | B_KERNEL_READ_AREA - | ((entry & X86_64_PTE_DIRTY) != 0 ? PAGE_MODIFIED : 0) - | ((entry & X86_64_PTE_ACCESSED) != 0 ? PAGE_ACCESSED : 0) - | ((entry & X86_64_PTE_PRESENT) != 0 ? PAGE_PRESENT : 0); - - TRACE("X86VMTranslationMap64Bit::QueryInterrupt(%#" B_PRIxADDR ") -> %#" - B_PRIxPHYSADDR ":\n", virtualAddress, *_physicalAddress); - - return B_OK; + // With our page mapper, there is no difference in getting a page table + // when interrupts are enabled or disabled, so just call Query(). + return Query(virtualAddress, _physicalAddress, _flags); }