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:
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user