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
|
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user