* Copyright 2010-2012, François, revol@free.fr.
* Copyright 2008-2010, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2002-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
* Distributed under the terms of the NewOS License.
*/
#include "paging/040/M68KPagingMethod040.h"
#include <stdlib.h>
#include <string.h>
#include <AutoDeleter.h>
#include <arch_system_info.h>
#include <boot/kernel_args.h>
#include <int.h>
#include <thread.h>
#include <vm/vm.h>
#include <vm/VMAddressSpace.h>
#include "paging/040/M68KPagingStructures040.h"
#include "paging/040/M68KVMTranslationMap040.h"
#include "paging/m68k_physical_page_mapper.h"
#include "paging/m68k_physical_page_mapper_large_memory.h"
#define TRACE_M68K_PAGING_METHOD_32_BIT
#ifdef TRACE_M68K_PAGING_METHOD_32_BIT
# define TRACE(x...) dprintf(x)
#else
# define TRACE(x...) ;
#endif
* Since m68k page tables are smaller than 1 page, but we allocate them
* at page granularity anyway, just go for this.
*/
#define SLOTS_PER_POOL 1024
using M68KLargePhysicalPageMapper::PhysicalPageSlot;
#warning M68K:REMOVE
static inline void
init_page_root_entry(page_root_entry *entry)
{
*entry = DFL_ROOTENT_VAL;
}
static inline void
update_page_root_entry(page_root_entry *entry, page_root_entry *with)
{
*entry = *with;
}
static inline void
init_page_directory_entry(page_directory_entry *entry)
{
*entry = DFL_DIRENT_VAL;
}
static inline void
update_page_directory_entry(page_directory_entry *entry, page_directory_entry *with)
{
*entry = *with;
}
static inline void
init_page_table_entry(page_table_entry *entry)
{
*entry = DFL_PAGEENT_VAL;
}
static inline void
update_page_table_entry(page_table_entry *entry, page_table_entry *with)
{
*entry = *with;
}
static inline void
init_page_indirect_entry(page_indirect_entry *entry)
{
#warning M68K: is it correct ?
*entry = DFL_PAGEENT_VAL;
}
static inline void
update_page_indirect_entry(page_indirect_entry *entry, page_indirect_entry *with)
{
*entry = *with;
}
struct M68KPagingMethod040::PhysicalPageSlotPool
: M68KLargePhysicalPageMapper::PhysicalPageSlotPool {
public:
virtual ~PhysicalPageSlotPool();
status_t InitInitial(kernel_args* args);
status_t InitInitialPostArea(kernel_args* args);
void Init(area_id dataArea, void* data,
area_id virtualArea, addr_t virtualBase);
virtual status_t AllocatePool(
M68KLargePhysicalPageMapper
::PhysicalPageSlotPool*& _pool);
virtual void Map(phys_addr_t physicalAddress,
addr_t virtualAddress);
public:
static PhysicalPageSlotPool sInitialPhysicalPagePool;
private:
area_id fDataArea;
area_id fVirtualArea;
addr_t fVirtualBase;
page_table_entry* fPageTable;
};
M68KPagingMethod040::PhysicalPageSlotPool
M68KPagingMethod040::PhysicalPageSlotPool::sInitialPhysicalPagePool;
M68KPagingMethod040::PhysicalPageSlotPool::~PhysicalPageSlotPool()
{
}
status_t
M68KPagingMethod040::PhysicalPageSlotPool::InitInitial(kernel_args* args)
{
addr_t virtualBase = vm_allocate_early(args, SLOTS_PER_POOL * B_PAGE_SIZE,
0, 0, kPageTableAlignment);
if (virtualBase == 0) {
panic("LargeMemoryPhysicalPageMapper::Init(): Failed to reserve "
"physical page pool space in virtual address space!");
return B_ERROR;
}
size_t areaSize = B_PAGE_SIZE + sizeof(PhysicalPageSlot[SLOTS_PER_POOL]);
page_table_entry* pageTable = (page_table_entry*)vm_allocate_early(args,
areaSize, ~0L, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, 0);
_EarlyPreparePageTables(pageTable, virtualBase,
SLOTS_PER_POOL * B_PAGE_SIZE);
Init(-1, pageTable, -1, (addr_t)virtualBase);
return B_OK;
}
status_t
M68KPagingMethod040::PhysicalPageSlotPool::InitInitialPostArea(
kernel_args* args)
{
#warning M68K:WRITEME
size_t areaSize = B_PAGE_SIZE + sizeof(PhysicalPageSlot[SLOTS_PER_POOL]);
void* temp = fPageTable;
area_id area = create_area("physical page pool", &temp,
B_EXACT_ADDRESS, areaSize, B_ALREADY_WIRED,
B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
if (area < B_OK) {
panic("LargeMemoryPhysicalPageMapper::InitPostArea(): Failed to "
"create area for physical page pool.");
return area;
}
fDataArea = area;
temp = (void*)fVirtualBase;
area = vm_create_null_area(VMAddressSpace::KernelID(),
"physical page pool space", &temp, B_EXACT_ADDRESS,
SLOTS_PER_POOL * B_PAGE_SIZE, 0);
if (area < B_OK) {
panic("LargeMemoryPhysicalPageMapper::InitPostArea(): Failed to "
"create area for physical page pool space.");
return area;
}
fVirtualArea = area;
return B_OK;
}
void
M68KPagingMethod040::PhysicalPageSlotPool::Init(area_id dataArea, void* data,
area_id virtualArea, addr_t virtualBase)
{
fDataArea = dataArea;
fVirtualArea = virtualArea;
fVirtualBase = virtualBase;
fPageTable = (page_table_entry*)data;
fSlots = (PhysicalPageSlot*)(fPageTable + SLOTS_PER_POOL);
addr_t slotAddress = virtualBase;
for (int32 i = 0; i < SLOTS_PER_POOL; i++, slotAddress += B_PAGE_SIZE) {
PhysicalPageSlot* slot = &fSlots[i];
slot->next = slot + 1;
slot->pool = this;
slot->address = slotAddress;
}
fSlots[1023].next = NULL;
}
void
M68KPagingMethod040::PhysicalPageSlotPool::Map(phys_addr_t physicalAddress,
addr_t virtualAddress)
{
page_table_entry& pte = fPageTable[
(virtualAddress - fVirtualBase) / B_PAGE_SIZE];
pte = TA_TO_PTEA(physicalAddress) | DT_PAGE
| M68K_PTE_SUPERVISOR | M68K_PTE_GLOBAL;
arch_cpu_invalidate_TLB_range(virtualAddress, virtualAddress);
}
status_t
M68KPagingMethod040::PhysicalPageSlotPool::AllocatePool(
M68KLargePhysicalPageMapper::PhysicalPageSlotPool*& _pool)
{
PhysicalPageSlotPool* pool = new(std::nothrow) PhysicalPageSlotPool;
if (pool == NULL)
return B_NO_MEMORY;
ObjectDeleter<PhysicalPageSlotPool> poolDeleter(pool);
size_t areaSize = B_PAGE_SIZE + sizeof(PhysicalPageSlot[SLOTS_PER_POOL]);
void* data;
virtual_address_restrictions virtualRestrictions = {};
virtualRestrictions.address_specification = B_ANY_KERNEL_ADDRESS;
physical_address_restrictions physicalRestrictions = {};
area_id dataArea = create_area_etc(B_SYSTEM_TEAM, "physical page pool",
PAGE_ALIGN(areaSize), B_FULL_LOCK,
B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, CREATE_AREA_DONT_WAIT, 0,
&virtualRestrictions, &physicalRestrictions, &data);
if (dataArea < 0)
return dataArea;
void* virtualBase;
area_id virtualArea = vm_create_null_area(
VMAddressSpace::KernelID(), "physical page pool space",
&virtualBase, B_ANY_KERNEL_BLOCK_ADDRESS, SLOTS_PER_POOL * B_PAGE_SIZE,
CREATE_AREA_PRIORITY_VIP);
if (virtualArea < 0) {
delete_area(dataArea);
return virtualArea;
}
memset(data, 0, B_PAGE_SIZE);
phys_addr_t physicalTable;
M68KVMTranslationMap040* map = static_cast<M68KVMTranslationMap040*>(
VMAddressSpace::Kernel()->TranslationMap());
uint32 dummyFlags;
cpu_status state = disable_interrupts();
map->QueryInterrupt((addr_t)data, &physicalTable, &dummyFlags);
restore_interrupts(state);
#warning M68K:FIXME: insert *all* page tables!
panic("I'm lazy");
#if 0
int32 index = (addr_t)virtualBase / (B_PAGE_SIZE * SLOTS_PER_POOL);
page_directory_entry* entry
= &map->PagingStructures040()->pgdir_virt[index];
PutPageTableInPageDir(entry, physicalTable,
B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
M68KPagingStructures040::UpdateAllPageDirs(index, *entry);
#endif
pool->Init(dataArea, data, virtualArea, (addr_t)virtualBase);
poolDeleter.Detach();
_pool = pool;
return B_OK;
}
M68KPagingMethod040::M68KPagingMethod040()
:
fKernelPhysicalPageRoot(0),
fKernelVirtualPageRoot(NULL),
fPhysicalPageMapper(NULL),
fKernelPhysicalPageMapper(NULL)
{
}
M68KPagingMethod040::~M68KPagingMethod040()
{
}
status_t
M68KPagingMethod040::Init(kernel_args* args,
VMPhysicalPageMapper** _physicalPageMapper)
{
TRACE("M68KPagingMethod040::Init(): entry\n");
#if 0
fPageHole = (page_table_entry*)args->arch_args.page_hole;
fPageHolePageDir = (page_directory_entry*)
(((addr_t)args->arch_args.page_hole)
+ (B_PAGE_SIZE * 1024 - B_PAGE_SIZE));
memset(fPageHolePageDir + FIRST_USER_PGDIR_ENT, 0,
sizeof(page_directory_entry) * NUM_USER_PGDIR_ENTS);
#endif
fKernelPhysicalPageRoot = (uint32)args->arch_args.phys_pgroot;
fKernelVirtualPageRoot = (page_root_entry *)args->arch_args.vir_pgroot;
#ifdef TRACE_M68K_PAGING_METHOD_32_BIT
TRACE("page root: %p (physical: %#" B_PRIx32 ")\n",
fKernelVirtualPageRoot, fKernelPhysicalPageRoot);
#endif
M68KPagingStructures040::StaticInit();
PhysicalPageSlotPool* pool
= new(&PhysicalPageSlotPool::sInitialPhysicalPagePool)
PhysicalPageSlotPool;
status_t error = pool->InitInitial(args);
if (error != B_OK) {
panic("M68KPagingMethod040::Init(): Failed to create initial pool "
"for physical page mapper!");
return error;
}
large_memory_physical_page_ops_init(args, pool, fPhysicalPageMapper,
fKernelPhysicalPageMapper);
TRACE("M68KPagingMethod040::Init(): done\n");
*_physicalPageMapper = fPhysicalPageMapper;
return B_OK;
}
status_t
M68KPagingMethod040::InitPostArea(kernel_args* args)
{
TRACE("M68KPagingMethod040::InitPostArea(): entry\n");
void *temp;
status_t error;
area_id area;
#if 0
fKernelVirtualPageDirectory[1023] = 0;
fPageHolePageDir = NULL;
fPageHole = NULL;
#endif
temp = (void*)fKernelVirtualPageRoot;
area = create_area("kernel_pgdir", &temp, B_EXACT_ADDRESS, B_PAGE_SIZE,
B_ALREADY_WIRED, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
if (area < B_OK)
return area;
error = PhysicalPageSlotPool::sInitialPhysicalPagePool
.InitInitialPostArea(args);
if (error != B_OK)
return error;
#if 0
area = vm_create_null_area(VMAddressSpace::KernelID(),
"interrupt query pages", (void **)&queryPage, B_ANY_ADDRESS,
B_PAGE_SIZE, 0);
if (area < B_OK)
return area;
#endif
TRACE("M68KPagingMethod040::InitPostArea(): done\n");
return B_OK;
}
status_t
M68KPagingMethod040::CreateTranslationMap(bool kernel, VMTranslationMap** _map)
{
M68KVMTranslationMap040* map;
map = new(std::nothrow) M68KVMTranslationMap040;
if (map == NULL)
return B_NO_MEMORY;
status_t error = map->Init(kernel);
if (error != B_OK) {
delete map;
return error;
}
*_map = map;
return B_OK;
}
status_t
M68KPagingMethod040::MapEarly(kernel_args* args, addr_t virtualAddress,
phys_addr_t physicalAddress, uint8 attributes,
phys_addr_t (*get_free_page)(kernel_args*))
{
addr_t va = virtualAddress;
phys_addr_t pa = physicalAddress;
page_root_entry *pr = (page_root_entry *)fKernelPhysicalPageRoot;
page_directory_entry *pd;
page_table_entry *pt;
addr_t tbl;
uint32 index;
uint32 i;
TRACE("040::MapEarly: entry pa 0x%lx va 0x%lx\n", pa, va);
index = VADDR_TO_PRENT(va);
if (PRE_TYPE(pr[index]) != DT_ROOT) {
unsigned aindex = index & ~(NUM_DIRTBL_PER_PAGE-1);
TRACE("missing page root entry %d ai %d\n", index, aindex);
tbl = get_free_page(args) * B_PAGE_SIZE;
if (!tbl)
return ENOMEM;
TRACE("040::MapEarly: asked for free page for pgdir. 0x%lx\n", tbl);
memset((void *)tbl, 0, B_PAGE_SIZE);
for (i = 0; i < NUM_DIRTBL_PER_PAGE; i++) {
PutPageDirInPageRoot(&pr[aindex + i], tbl, attributes);
pd = (page_directory_entry *)tbl;
for (int32 j = 0; j < NUM_DIRENT_PER_TBL; j++)
pd[j] = DFL_DIRENT_VAL;
tbl += SIZ_DIRTBL;
}
}
pd = (page_directory_entry *)PRE_TO_TA(pr[index]);
index = VADDR_TO_PDENT(va);
if (PDE_TYPE(pd[index]) != DT_DIR) {
unsigned aindex = index & ~(NUM_PAGETBL_PER_PAGE-1);
TRACE("missing page dir entry %d ai %d\n", index, aindex);
tbl = get_free_page(args) * B_PAGE_SIZE;
if (!tbl)
return ENOMEM;
TRACE("early_map: asked for free page for pgtable. 0x%lx\n", tbl);
memset((void *)tbl, 0, B_PAGE_SIZE);
for (i = 0; i < NUM_PAGETBL_PER_PAGE; i++) {
PutPageTableInPageDir(&pd[aindex + i], tbl, attributes);
pt = (page_table_entry *)tbl;
for (int32 j = 0; j < NUM_PAGEENT_PER_TBL; j++)
pt[j] = DFL_PAGEENT_VAL;
tbl += SIZ_PAGETBL;
}
}
pt = (page_table_entry *)PDE_TO_TA(pd[index]);
index = VADDR_TO_PTENT(va);
PutPageTableEntryInTable(&pt[index],
physicalAddress, attributes, 0, IS_KERNEL_ADDRESS(virtualAddress));
arch_cpu_invalidate_TLB_range(va, va);
return B_OK;
#if 0
int index = VADDR_TO_PDENT(virtualAddress);
if ((fPageHolePageDir[index] & M68K_PDE_PRESENT) == 0) {
phys_addr_t pgtable;
page_directory_entry *e;
pgtable = get_free_page(args);
pgtable *= B_PAGE_SIZE;
TRACE("M68KPagingMethod040::MapEarly(): asked for free page for "
"pgtable. %#" B_PRIxPHYSADDR "\n", pgtable);
e = &fPageHolePageDir[index];
PutPageTableInPageDir(e, pgtable, attributes);
memset((unsigned int*)((addr_t)fPageHole
+ (virtualAddress / B_PAGE_SIZE / 1024) * B_PAGE_SIZE),
0, B_PAGE_SIZE);
}
ASSERT_PRINT(
(fPageHole[virtualAddress / B_PAGE_SIZE] & M68K_PTE_PRESENT) == 0,
"virtual address: %#" B_PRIxADDR ", pde: %#" B_PRIx32
", existing pte: %#" B_PRIx32, virtualAddress, fPageHolePageDir[index],
fPageHole[virtualAddress / B_PAGE_SIZE]);
#endif
return B_OK;
}
bool
M68KPagingMethod040::IsKernelPageAccessible(addr_t virtualAddress,
uint32 protection)
{
#warning M68K: WRITEME
return false;
}
void
M68KPagingMethod040::SetPageRoot(uint32 pageRoot)
{
#warning M68K:TODO:override this for 060
uint32 rp;
rp = pageRoot & ~((1 << 9) - 1);
asm volatile( \
"movec %0,%%srp\n" \
"movec %0,%%urp\n" \
: : "d"(rp));
}
void
M68KPagingMethod040::PutPageDirInPageRoot(page_root_entry* entry,
phys_addr_t pgdirPhysical, uint32 attributes)
{
*entry = TA_TO_PREA(pgdirPhysical)
| DT_DIR;
}
void
M68KPagingMethod040::PutPageTableInPageDir(page_directory_entry* entry,
phys_addr_t pgtablePhysical, uint32 attributes)
{
*entry = TA_TO_PDEA(pgtablePhysical)
| DT_DIR;
}
void
M68KPagingMethod040::PutPageTableEntryInTable(page_table_entry* entry,
phys_addr_t physicalAddress, uint32 attributes, uint32 memoryType,
bool globalPage)
{
page_table_entry page = TA_TO_PTEA(physicalAddress)
| DT_PAGE
#ifdef PAGE_HAS_GLOBAL_BIT
| (globalPage ? M68K_PTE_GLOBAL : 0)
#endif
| MemoryTypeToPageTableEntryFlags(memoryType);
if ((attributes & B_USER_PROTECTION) == 0) {
page |= M68K_PTE_SUPERVISOR;
if ((attributes & B_KERNEL_WRITE_AREA) == 0)
page |= M68K_PTE_READONLY;
} else if ((attributes & B_WRITE_AREA) == 0)
page |= M68K_PTE_READONLY;
*(volatile page_table_entry*)entry = page;
}
void
M68KPagingMethod040::_EarlyPreparePageTables(page_table_entry* pageTables,
addr_t address, size_t size)
{
memset(pageTables, 0, B_PAGE_SIZE *
(size / (B_PAGE_SIZE * NUM_PAGEENT_PER_TBL * NUM_PAGETBL_PER_PAGE)));
{
size_t index;
addr_t virtualTable = (addr_t)pageTables;
page_root_entry *pr
= M68KPagingMethod040::Method()->fKernelVirtualPageRoot;
page_directory_entry *pd;
page_directory_entry *e;
for (size_t i = 0; i < (size / (B_PAGE_SIZE * NUM_PAGEENT_PER_TBL));
i++, virtualTable += SIZ_PAGETBL) {
phys_addr_t physicalTable = 0;
_EarlyQuery(virtualTable, &physicalTable);
index = VADDR_TO_PRENT(address) + i / NUM_DIRENT_PER_TBL;
pd = (page_directory_entry *)PRE_TO_TA(pr[index]);
e = &pd[(VADDR_TO_PDENT(address) + i) % NUM_DIRENT_PER_TBL];
PutPageTableInPageDir(e, physicalTable,
B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
}
}
}
status_t
M68KPagingMethod040::_EarlyQuery(addr_t virtualAddress,
phys_addr_t *_physicalAddress)
{
M68KPagingMethod040* method = M68KPagingMethod040::Method();
page_root_entry *pr = method->fKernelVirtualPageRoot;
page_directory_entry *pd;
page_indirect_entry *pi;
page_table_entry *pt;
addr_t pa;
int32 index;
status_t err = B_ERROR;
TRACE("%s(%p,)\n", __FUNCTION__, virtualAddress);
index = VADDR_TO_PRENT(virtualAddress);
TRACE("%s: pr[%d].type %d\n", __FUNCTION__, index, PRE_TYPE(pr[index]));
if (pr && PRE_TYPE(pr[index]) == DT_ROOT) {
pa = PRE_TO_TA(pr[index]);
pd = (page_directory_entry *)pa;
index = VADDR_TO_PDENT(virtualAddress);
TRACE("%s: pd[%d].type %d\n", __FUNCTION__, index,
pd?(PDE_TYPE(pd[index])):-1);
if (pd && PDE_TYPE(pd[index]) == DT_DIR) {
pa = PDE_TO_TA(pd[index]);
pt = (page_table_entry *)pa;
index = VADDR_TO_PTENT(virtualAddress);
TRACE("%s: pt[%d].type %d\n", __FUNCTION__, index,
pt?(PTE_TYPE(pt[index])):-1);
if (pt && PTE_TYPE(pt[index]) == DT_INDIRECT) {
pi = (page_indirect_entry *)pt;
pa = PIE_TO_TA(pi[index]);
pt = (page_table_entry *)pa;
index = 0;
}
if (pt && PIE_TYPE(pt[index]) == DT_PAGE) {
*_physicalAddress = PTE_TO_PA(pt[index]);
*_physicalAddress += virtualAddress % B_PAGE_SIZE;
err = B_OK;
}
}
}
return err;
#if 0
int index = VADDR_TO_PDENT(virtualAddress);
if ((method->PageHolePageDir()[index] & M68K_PDE_PRESENT) == 0) {
return B_ERROR;
}
page_table_entry* entry = method->PageHole() + virtualAddress / B_PAGE_SIZE;
if ((*entry & M68K_PTE_PRESENT) == 0) {
return B_ERROR;
}
*_physicalAddress = *entry & M68K_PTE_ADDRESS_MASK;
return B_OK;
#endif
}