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.
This commit is contained in:
Alex Smith
2013-03-22 18:30:01 +00:00
parent 8627383bf7
commit 5e0c3db286
4 changed files with 89 additions and 57 deletions
@@ -30,8 +30,8 @@
// address where the kernel is loaded to: the kernel is loaded in the top 2GB // 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 // 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. // whole kernel address space is the top 512GB of the address space.
#define KERNEL_BASE 0xffffff8000000000 #define KERNEL_BASE 0xffffff0000000000
#define KERNEL_SIZE 0x8000000000 #define KERNEL_SIZE 0x10000000000
#define KERNEL_TOP (KERNEL_BASE + (KERNEL_SIZE - 1)) #define KERNEL_TOP (KERNEL_BASE + (KERNEL_SIZE - 1))
#define KERNEL_LOAD_BASE 0xffffffff80000000 #define KERNEL_LOAD_BASE 0xffffffff80000000
@@ -76,8 +76,16 @@ X86PagingMethod64Bit::Init(kernel_args* args,
status_t status_t
X86PagingMethod64Bit::InitPostArea(kernel_args* args) 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. // 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_EXACT_ADDRESS, B_PAGE_SIZE, B_ALREADY_WIRED,
B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
if (area < B_OK) 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 /*! Traverses down the paging structure hierarchy to find the page directory
virtual address, allocating new tables if required. for a virtual address, allocating new tables if required.
*/ */
/*static*/ uint64* /*static*/ uint64*
X86PagingMethod64Bit::PageTableForAddress(uint64* virtualPML4, X86PagingMethod64Bit::PageDirectoryForAddress(uint64* virtualPML4,
addr_t virtualAddress, bool isKernel, bool allocateTables, addr_t virtualAddress, bool isKernel, bool allocateTables,
vm_page_reservation* reservation, vm_page_reservation* reservation,
TranslationMapPhysicalPageMapper* pageMapper, int32& mapCount) TranslationMapPhysicalPageMapper* pageMapper, int32& mapCount)
{ {
TRACE("X86PagingMethod64Bit::PageTableForAddress(%#" B_PRIxADDR ", "
"%d)\n", virtualAddress, allocateTables);
// Get the PDPT. // Get the PDPT.
uint64* pml4e = &virtualPML4[VADDR_TO_PML4E(virtualAddress)]; uint64* pml4e = &virtualPML4[VADDR_TO_PML4E(virtualAddress)];
if ((*pml4e & X86_64_PML4E_PRESENT) == 0) { if ((*pml4e & X86_64_PML4E_PRESENT) == 0) {
@@ -259,11 +264,44 @@ X86PagingMethod64Bit::PageTableForAddress(uint64* virtualPML4,
mapCount++; mapCount++;
} }
uint64* virtualPageDir = (uint64*)pageMapper->GetPageTableAt( return (uint64*)pageMapper->GetPageTableAt(
*pdpte & X86_64_PDPTE_ADDRESS_MASK); *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 ((*pde & X86_64_PDE_PRESENT) == 0) {
if (!allocateTables) if (!allocateTables)
return NULL; return NULL;
@@ -289,6 +327,11 @@ X86PagingMethod64Bit::PageTableForAddress(uint64* virtualPML4,
mapCount++; 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); return (uint64*)pageMapper->GetPageTableAt(*pde & X86_64_PDE_ADDRESS_MASK);
} }
@@ -55,6 +55,18 @@ public:
static X86PagingMethod64Bit* Method(); 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, static uint64* PageTableForAddress(uint64* virtualPML4,
addr_t virtualAddress, bool isKernel, addr_t virtualAddress, bool isKernel,
bool allocateTables, bool allocateTables,
@@ -141,7 +141,7 @@ X86VMTranslationMap64Bit::Init(bool kernel)
// Assuming that only the top 2 PML4 entries are occupied for the // Assuming that only the top 2 PML4 entries are occupied for the
// kernel. // kernel.
STATIC_ASSERT(KERNEL_PMAP_BASE == 0xffffff0000000000); STATIC_ASSERT(KERNEL_PMAP_BASE == 0xffffff0000000000);
STATIC_ASSERT(KERNEL_BASE == 0xffffff8000000000); STATIC_ASSERT(KERNEL_BASE == 0xffffff0000000000);
// Allocate and clear the PML4. // Allocate and clear the PML4.
uint64* virtualPML4 = (uint64*)memalign(B_PAGE_SIZE, B_PAGE_SIZE); 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()); ThreadCPUPinner pinner(thread_get_current_thread());
// Look up the page table for the virtual address. // This function may be called on the physical map area, so we must handle
uint64* pte = X86PagingMethod64Bit::PageTableEntryForAddress( // large pages here. Look up the page directory entry for the virtual
// address.
uint64* pde = X86PagingMethod64Bit::PageDirectoryEntryForAddress(
fPagingStructures->VirtualPML4(), virtualAddress, fIsKernelMap, fPagingStructures->VirtualPML4(), virtualAddress, fIsKernelMap,
false, NULL, fPageMapper, fMapCount); false, NULL, fPageMapper, fMapCount);
if (pte == NULL) if (pde == NULL || (*pde & X86_64_PDE_PRESENT) == 0)
return B_OK; return B_OK;
uint64 entry = *pte; uint64 entry;
if ((*pde & X86_64_PDE_LARGE_PAGE) != 0) {
*_physicalAddress = entry & X86_64_PTE_ADDRESS_MASK; 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. // Translate the page state flags.
if ((entry & X86_64_PTE_USER) != 0) { 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); | ((entry & X86_64_PTE_PRESENT) != 0 ? PAGE_PRESENT : 0);
TRACE("X86VMTranslationMap64Bit::Query(%#" B_PRIxADDR ") -> %#" TRACE("X86VMTranslationMap64Bit::Query(%#" B_PRIxADDR ") -> %#"
B_PRIxPHYSADDR " %#" B_PRIx32 " (pte: %p %#" B_PRIx64 ")\n", B_PRIxPHYSADDR " %#" B_PRIx32 " (entry: %#" B_PRIx64 ")\n",
virtualAddress, *_physicalAddress, *_flags, pte, entry); virtualAddress, *_physicalAddress, *_flags, entry);
return B_OK; return B_OK;
} }
@@ -638,42 +648,9 @@ status_t
X86VMTranslationMap64Bit::QueryInterrupt(addr_t virtualAddress, X86VMTranslationMap64Bit::QueryInterrupt(addr_t virtualAddress,
phys_addr_t* _physicalAddress, uint32* _flags) phys_addr_t* _physicalAddress, uint32* _flags)
{ {
*_flags = 0; // With our page mapper, there is no difference in getting a page table
*_physicalAddress = 0; // when interrupts are enabled or disabled, so just call Query().
return Query(virtualAddress, _physicalAddress, _flags);
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;
} }