#include "paging/arm_physical_page_mapper_large_memory.h"
#include <new>
#include <AutoDeleter.h>
#include <cpu.h>
#include <lock.h>
#include <smp.h>
#include <util/AutoLock.h>
#include <util/ThreadAutoLock.h>
#include <vm/vm.h>
#include <vm/vm_types.h>
#include <vm/VMAddressSpace.h>
#include "paging/arm_physical_page_mapper.h"
#include "paging/ARMPagingStructures.h"
#include "paging/ARMVMTranslationMap.h"
#define SLOTS_PER_TRANSLATION_MAP 4
using ARMLargePhysicalPageMapper::PhysicalPageSlot;
using ARMLargePhysicalPageMapper::PhysicalPageSlotPool;
class PhysicalPageSlotQueue {
public:
PhysicalPageSlotQueue();
inline PhysicalPageSlot* GetSlot();
inline void GetSlots(PhysicalPageSlot*& slot1,
PhysicalPageSlot*& slot2);
inline void PutSlot(PhysicalPageSlot* slot);
inline void PutSlots(PhysicalPageSlot* slot1,
PhysicalPageSlot* slot2);
private:
PhysicalPageSlot* fSlots;
ConditionVariable fFreeSlotCondition;
ConditionVariable fFreeSlotsCondition;
};
struct PhysicalPageOpsCPUData {
PhysicalPageSlotQueue user;
PhysicalPageSlotQueue kernel;
PhysicalPageSlot* interruptSlot;
void Init();
private:
static PhysicalPageSlot* _GetInitialSlot();
};
class LargeMemoryTranslationMapPhysicalPageMapper
: public TranslationMapPhysicalPageMapper {
public:
LargeMemoryTranslationMapPhysicalPageMapper();
virtual ~LargeMemoryTranslationMapPhysicalPageMapper();
status_t Init();
virtual void Delete();
virtual void* GetPageTableAt(phys_addr_t physicalAddress);
private:
struct page_slot {
PhysicalPageSlot* slot;
phys_addr_t physicalAddress;
CPUSet valid;
};
page_slot fSlots[SLOTS_PER_TRANSLATION_MAP];
int32 fSlotCount;
int32 fNextSlot;
};
class LargeMemoryPhysicalPageMapper : public ARMPhysicalPageMapper {
public:
LargeMemoryPhysicalPageMapper();
status_t Init(kernel_args* args,
PhysicalPageSlotPool* initialPools,
int32 initalPoolCount, size_t poolSize,
TranslationMapPhysicalPageMapper*&
_kernelPageMapper);
virtual status_t CreateTranslationMapPhysicalPageMapper(
TranslationMapPhysicalPageMapper** _mapper);
virtual void* InterruptGetPageTableAt(
phys_addr_t physicalAddress);
virtual status_t GetPage(phys_addr_t physicalAddress,
addr_t* virtualAddress, void** handle);
virtual status_t PutPage(addr_t virtualAddress, void* handle);
virtual status_t GetPageCurrentCPU(phys_addr_t physicalAddress,
addr_t* virtualAddress, void** handle);
virtual status_t PutPageCurrentCPU(addr_t virtualAddress,
void* handle);
virtual status_t GetPageDebug(phys_addr_t physicalAddress,
addr_t* virtualAddress, void** handle);
virtual status_t PutPageDebug(addr_t virtualAddress,
void* handle);
virtual status_t MemsetPhysical(phys_addr_t address, int value,
phys_size_t length);
virtual status_t MemcpyFromPhysical(void* to, phys_addr_t from,
size_t length, bool user);
virtual status_t MemcpyToPhysical(phys_addr_t to,
const void* from, size_t length, bool user);
virtual void MemcpyPhysicalPage(phys_addr_t to,
phys_addr_t from);
status_t GetSlot(bool canWait,
PhysicalPageSlot*& slot);
void PutSlot(PhysicalPageSlot* slot);
inline PhysicalPageSlotQueue* GetSlotQueue(int32 cpu, bool user);
private:
typedef DoublyLinkedList<PhysicalPageSlotPool> PoolList;
mutex fLock;
PoolList fEmptyPools;
PoolList fNonEmptyPools;
PhysicalPageSlot* fDebugSlot;
PhysicalPageSlotPool* fInitialPool;
LargeMemoryTranslationMapPhysicalPageMapper fKernelMapper;
PhysicalPageOpsCPUData fPerCPUData[SMP_MAX_CPUS];
};
static LargeMemoryPhysicalPageMapper sPhysicalPageMapper;
inline void
PhysicalPageSlot::Map(phys_addr_t physicalAddress)
{
pool->Map(physicalAddress, address);
}
PhysicalPageSlotPool::~PhysicalPageSlotPool()
{
}
inline bool
PhysicalPageSlotPool::IsEmpty() const
{
return fSlots == NULL;
}
inline PhysicalPageSlot*
PhysicalPageSlotPool::GetSlot()
{
PhysicalPageSlot* slot = fSlots;
fSlots = slot->next;
return slot;
}
inline void
PhysicalPageSlotPool::PutSlot(PhysicalPageSlot* slot)
{
slot->next = fSlots;
fSlots = slot;
}
PhysicalPageSlotQueue::PhysicalPageSlotQueue()
:
fSlots(NULL)
{
fFreeSlotCondition.Init(this, "physical page ops slot queue");
fFreeSlotsCondition.Init(this, "physical page ops slots queue");
}
PhysicalPageSlot*
PhysicalPageSlotQueue::GetSlot()
{
InterruptsLocker locker;
while (fSlots == NULL) {
ConditionVariableEntry entry;
fFreeSlotCondition.Add(&entry);
locker.Unlock();
entry.Wait();
locker.Lock();
}
PhysicalPageSlot* slot = fSlots;
fSlots = slot->next;
return slot;
}
void
PhysicalPageSlotQueue::GetSlots(PhysicalPageSlot*& slot1,
PhysicalPageSlot*& slot2)
{
InterruptsLocker locker;
while (fSlots == NULL || fSlots->next == NULL) {
ConditionVariableEntry entry;
fFreeSlotsCondition.Add(&entry);
locker.Unlock();
entry.Wait();
locker.Lock();
}
slot1 = fSlots;
slot2 = slot1->next;
fSlots = slot2->next;
}
void
PhysicalPageSlotQueue::PutSlot(PhysicalPageSlot* slot)
{
InterruptsLocker locker;
slot->next = fSlots;
fSlots = slot;
if (slot->next == NULL)
fFreeSlotCondition.NotifyAll();
else if (slot->next->next == NULL)
fFreeSlotCondition.NotifyAll();
}
void
PhysicalPageSlotQueue::PutSlots(PhysicalPageSlot* slot1,
PhysicalPageSlot* slot2)
{
InterruptsLocker locker;
slot1->next = slot2;
slot2->next = fSlots;
fSlots = slot1;
if (slot2->next == NULL)
fFreeSlotCondition.NotifyAll();
else if (slot2->next->next == NULL)
fFreeSlotCondition.NotifyAll();
}
void
PhysicalPageOpsCPUData::Init()
{
for (int32 i = 0; i < USER_SLOTS_PER_CPU; i++)
user.PutSlot(_GetInitialSlot());
for (int32 i = 0; i < KERNEL_SLOTS_PER_CPU; i++)
kernel.PutSlot(_GetInitialSlot());
interruptSlot = _GetInitialSlot();
}
PhysicalPageSlot*
PhysicalPageOpsCPUData::_GetInitialSlot()
{
PhysicalPageSlot* slot;
status_t error = sPhysicalPageMapper.GetSlot(false, slot);
if (error != B_OK) {
panic("PhysicalPageOpsCPUData::Init(): Failed to get initial "
"physical page slots! Probably too many CPUs.");
return NULL;
}
return slot;
}
LargeMemoryTranslationMapPhysicalPageMapper
::LargeMemoryTranslationMapPhysicalPageMapper()
:
fSlotCount(sizeof(fSlots) / sizeof(page_slot)),
fNextSlot(0)
{
memset((void*)fSlots, 0, sizeof(fSlots));
}
LargeMemoryTranslationMapPhysicalPageMapper
::~LargeMemoryTranslationMapPhysicalPageMapper()
{
for (int32 i = 0; i < fSlotCount; i++) {
if (fSlots[i].slot != NULL)
sPhysicalPageMapper.PutSlot(fSlots[i].slot);
}
}
status_t
LargeMemoryTranslationMapPhysicalPageMapper::Init()
{
for (int32 i = 0; i < fSlotCount; i++) {
status_t error = sPhysicalPageMapper.GetSlot(true, fSlots[i].slot);
if (error != B_OK)
return error;
fSlots[i].physicalAddress = ~(phys_addr_t)0;
}
return B_OK;
}
void
LargeMemoryTranslationMapPhysicalPageMapper::Delete()
{
delete this;
}
void*
LargeMemoryTranslationMapPhysicalPageMapper::GetPageTableAt(
phys_addr_t physicalAddress)
{
phys_addr_t off = physicalAddress & (B_PAGE_SIZE -1);
physicalAddress &= ~(B_PAGE_SIZE -1);
int32 currentCPU = smp_get_current_cpu();
for (int32 i = 0; i < fSlotCount; i++) {
page_slot& slot = fSlots[i];
if (slot.physicalAddress == physicalAddress) {
fNextSlot = (i + 1) & (fSlotCount - 1);
if (!slot.valid.GetBit(currentCPU)) {
arch_cpu_invalidate_tlb_range(0, slot.slot->address,
slot.slot->address + B_PAGE_SIZE);
slot.valid.SetBit(currentCPU);
}
return (uint8*)slot.slot->address + off;
}
}
page_slot& slot = fSlots[fNextSlot];
fNextSlot = (fNextSlot + 1) & (fSlotCount - 1);
slot.physicalAddress = physicalAddress;
slot.slot->Map(physicalAddress);
slot.valid.ClearAll();
slot.valid.SetBit(currentCPU);
return (uint8*)slot.slot->address + off;
}
LargeMemoryPhysicalPageMapper::LargeMemoryPhysicalPageMapper()
:
fInitialPool(NULL)
{
mutex_init(&fLock, "large memory physical page mapper");
}
status_t
LargeMemoryPhysicalPageMapper::Init(kernel_args* args,
PhysicalPageSlotPool* initialPools, int32 initialPoolCount, size_t poolSize,
TranslationMapPhysicalPageMapper*& _kernelPageMapper)
{
ASSERT(initialPoolCount >= 1);
fInitialPool = initialPools;
for (int32 i = 0; i < initialPoolCount; i++) {
uint8* pointer = (uint8*)initialPools + i * poolSize;
fNonEmptyPools.Add((PhysicalPageSlotPool*)pointer);
}
GetSlot(true, fDebugSlot);
status_t error = fKernelMapper.Init();
if (error != B_OK) {
panic("LargeMemoryPhysicalPageMapper::Init(): Failed to init "
"kernel translation map physical page mapper!");
return error;
}
_kernelPageMapper = &fKernelMapper;
int32 cpuCount = smp_get_num_cpus();
for (int32 i = 0; i < cpuCount; i++)
fPerCPUData[i].Init();
return B_OK;
}
status_t
LargeMemoryPhysicalPageMapper::CreateTranslationMapPhysicalPageMapper(
TranslationMapPhysicalPageMapper** _mapper)
{
LargeMemoryTranslationMapPhysicalPageMapper* mapper
= new(std::nothrow) LargeMemoryTranslationMapPhysicalPageMapper;
if (mapper == NULL)
return B_NO_MEMORY;
status_t error = mapper->Init();
if (error != B_OK) {
delete mapper;
return error;
}
*_mapper = mapper;
return B_OK;
}
void*
LargeMemoryPhysicalPageMapper::InterruptGetPageTableAt(
phys_addr_t physicalAddress)
{
phys_addr_t off = physicalAddress & (B_PAGE_SIZE -1);
physicalAddress &= ~(B_PAGE_SIZE -1);
PhysicalPageSlot* slot = fPerCPUData[smp_get_current_cpu()].interruptSlot;
slot->Map(physicalAddress);
return (void*)slot->address + off;
}
status_t
LargeMemoryPhysicalPageMapper::GetPage(phys_addr_t physicalAddress,
addr_t* virtualAddress, void** handle)
{
PhysicalPageSlot* slot;
status_t error = GetSlot(true, slot);
if (error != B_OK)
return error;
slot->Map(physicalAddress);
*handle = slot;
*virtualAddress = slot->address + physicalAddress % B_PAGE_SIZE;
smp_broadcast_ici(SMP_MSG_INVALIDATE_PAGE_RANGE, 0,
*virtualAddress, *virtualAddress, NULL, SMP_MSG_FLAG_SYNC);
return B_OK;
}
status_t
LargeMemoryPhysicalPageMapper::PutPage(addr_t virtualAddress, void* handle)
{
PutSlot((PhysicalPageSlot*)handle);
return B_OK;
}
status_t
LargeMemoryPhysicalPageMapper::GetPageCurrentCPU(phys_addr_t physicalAddress,
addr_t* virtualAddress, void** handle)
{
PhysicalPageSlotQueue& slotQueue
= fPerCPUData[smp_get_current_cpu()].user;
PhysicalPageSlot* slot = slotQueue.GetSlot();
slot->Map(physicalAddress);
*virtualAddress = slot->address + physicalAddress % B_PAGE_SIZE;
*handle = slot;
return B_OK;
}
status_t
LargeMemoryPhysicalPageMapper::PutPageCurrentCPU(addr_t virtualAddress,
void* handle)
{
PhysicalPageSlotQueue& slotQueue
= fPerCPUData[smp_get_current_cpu()].user;
slotQueue.PutSlot((PhysicalPageSlot*)handle);
return B_OK;
}
status_t
LargeMemoryPhysicalPageMapper::GetPageDebug(phys_addr_t physicalAddress,
addr_t* virtualAddress, void** handle)
{
fDebugSlot->Map(physicalAddress);
*handle = fDebugSlot;
*virtualAddress = fDebugSlot->address + physicalAddress % B_PAGE_SIZE;
return B_OK;
}
status_t
LargeMemoryPhysicalPageMapper::PutPageDebug(addr_t virtualAddress, void* handle)
{
return B_OK;
}
status_t
LargeMemoryPhysicalPageMapper::MemsetPhysical(phys_addr_t address, int value,
phys_size_t length)
{
addr_t pageOffset = address % B_PAGE_SIZE;
Thread* thread = thread_get_current_thread();
ThreadCPUPinner _(thread);
PhysicalPageSlotQueue* slotQueue = GetSlotQueue(thread->cpu->cpu_num,
false);
PhysicalPageSlot* slot = slotQueue->GetSlot();
while (length > 0) {
slot->Map(address - pageOffset);
size_t toSet = min_c(length, B_PAGE_SIZE - pageOffset);
memset((void*)(slot->address + pageOffset), value, toSet);
length -= toSet;
address += toSet;
pageOffset = 0;
}
slotQueue->PutSlot(slot);
return B_OK;
}
status_t
LargeMemoryPhysicalPageMapper::MemcpyFromPhysical(void* _to, phys_addr_t from,
size_t length, bool user)
{
uint8* to = (uint8*)_to;
addr_t pageOffset = from % B_PAGE_SIZE;
Thread* thread = thread_get_current_thread();
ThreadCPUPinner _(thread);
PhysicalPageSlotQueue* slotQueue = GetSlotQueue(thread->cpu->cpu_num, user);
PhysicalPageSlot* slot = slotQueue->GetSlot();
status_t error = B_OK;
while (length > 0) {
size_t toCopy = min_c(length, B_PAGE_SIZE - pageOffset);
slot->Map(from - pageOffset);
if (user) {
error = user_memcpy(to, (void*)(slot->address + pageOffset),
toCopy);
if (error != B_OK)
break;
} else
memcpy(to, (void*)(slot->address + pageOffset), toCopy);
to += toCopy;
from += toCopy;
length -= toCopy;
pageOffset = 0;
}
slotQueue->PutSlot(slot);
return error;
}
status_t
LargeMemoryPhysicalPageMapper::MemcpyToPhysical(phys_addr_t to,
const void* _from, size_t length, bool user)
{
const uint8* from = (const uint8*)_from;
addr_t pageOffset = to % B_PAGE_SIZE;
Thread* thread = thread_get_current_thread();
ThreadCPUPinner _(thread);
PhysicalPageSlotQueue* slotQueue = GetSlotQueue(thread->cpu->cpu_num, user);
PhysicalPageSlot* slot = slotQueue->GetSlot();
status_t error = B_OK;
while (length > 0) {
size_t toCopy = min_c(length, B_PAGE_SIZE - pageOffset);
slot->Map(to - pageOffset);
if (user) {
error = user_memcpy((void*)(slot->address + pageOffset), from,
toCopy);
if (error != B_OK)
break;
} else
memcpy((void*)(slot->address + pageOffset), from, toCopy);
to += toCopy;
from += toCopy;
length -= toCopy;
pageOffset = 0;
}
slotQueue->PutSlot(slot);
return error;
}
void
LargeMemoryPhysicalPageMapper::MemcpyPhysicalPage(phys_addr_t to,
phys_addr_t from)
{
Thread* thread = thread_get_current_thread();
ThreadCPUPinner _(thread);
PhysicalPageSlotQueue* slotQueue = GetSlotQueue(thread->cpu->cpu_num,
false);
PhysicalPageSlot* fromSlot;
PhysicalPageSlot* toSlot;
slotQueue->GetSlots(fromSlot, toSlot);
fromSlot->Map(from);
toSlot->Map(to);
memcpy((void*)toSlot->address, (void*)fromSlot->address, B_PAGE_SIZE);
slotQueue->PutSlots(fromSlot, toSlot);
}
status_t
LargeMemoryPhysicalPageMapper::GetSlot(bool canWait, PhysicalPageSlot*& slot)
{
MutexLocker locker(fLock);
PhysicalPageSlotPool* pool = fNonEmptyPools.Head();
if (pool == NULL) {
if (!canWait)
return B_WOULD_BLOCK;
locker.Unlock();
status_t error = fInitialPool->AllocatePool(pool);
if (error != B_OK)
return error;
locker.Lock();
fNonEmptyPools.Add(pool);
pool = fNonEmptyPools.Head();
}
slot = pool->GetSlot();
if (pool->IsEmpty()) {
fNonEmptyPools.Remove(pool);
fEmptyPools.Add(pool);
}
return B_OK;
}
void
LargeMemoryPhysicalPageMapper::PutSlot(PhysicalPageSlot* slot)
{
MutexLocker locker(fLock);
PhysicalPageSlotPool* pool = slot->pool;
if (pool->IsEmpty()) {
fEmptyPools.Remove(pool);
fNonEmptyPools.Add(pool);
}
pool->PutSlot(slot);
}
inline PhysicalPageSlotQueue*
LargeMemoryPhysicalPageMapper::GetSlotQueue(int32 cpu, bool user)
{
return user ? &fPerCPUData[cpu].user : &fPerCPUData[cpu].kernel;
}
status_t
large_memory_physical_page_ops_init(kernel_args* args,
ARMLargePhysicalPageMapper::PhysicalPageSlotPool* initialPools,
int32 initialPoolCount, size_t poolSize,
ARMPhysicalPageMapper*& _pageMapper,
TranslationMapPhysicalPageMapper*& _kernelPageMapper)
{
new(&sPhysicalPageMapper) LargeMemoryPhysicalPageMapper;
sPhysicalPageMapper.Init(args, initialPools, initialPoolCount, poolSize,
_kernelPageMapper);
_pageMapper = &sPhysicalPageMapper;
return B_OK;
}