// Copyright (c) 1994-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the License "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
// e32\memmodel\emul\win32\mutils.cpp
//
//
#include "memmodel.h"
#include <kernel/cache.h>
#include <emulator.h>
void MM::Panic(MM::TMemModelPanic aPanic)
{
Kern::Fault("MemModel", aPanic);
}
TInt M::PageSizeInBytes()
{
return MM::RamPageSize;
}
TBool M::IsRomAddress(const TAny* )
{
return EFalse;
}
TUint32 MM::RoundToPageSize(TUint32 aSize)
{
TUint32 m=MM::RamPageSize-1;
return (aSize+m)&~m;
}
EXPORT_C TUint32 Kern::RoundToPageSize(TUint32 aSize)
{
return MM::RoundToPageSize(aSize);
}
EXPORT_C TUint32 Kern::RoundToChunkSize(TUint32 aSize)
{
return MM::RoundToChunkSize(aSize);
}
void MM::Init1()
{
TheScheduler.SetProcessHandler((TLinAddr)DoProcessSwitch);
}
void MM::Wait()
{
Kern::MutexWait(*RamAllocatorMutex);
if (RamAllocatorMutex->iHoldCount==1)
{
InitialFreeMemory=FreeMemory;
AllocFailed=EFalse;
}
}
TInt MM::Commit(TLinAddr aBase, TInt aSize, TInt aClearByte, TBool aExecute)
//
// Get win32 to commit the pages.
// We know they are not already committed - this is guaranteed by the caller so we can update the memory info easily
//
{
__ASSERT_MUTEX(RamAllocatorMutex);
if (aSize==0)
return KErrNone;
if (MM::FreeMemory+MM::CacheMemory >= aSize)
{
__LOCK_HOST;
DWORD protect = aExecute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
if (VirtualAlloc(LPVOID(aBase), aSize, MEM_COMMIT, protect))
{
TInt reclaimed = aSize - MM::FreeMemory;
if(reclaimed<=0)
MM::FreeMemory -= aSize;
else
{
// some cache memory was needed for this commit...
MM::FreeMemory = 0;
MM::CacheMemory -= reclaimed;
MM::ReclaimedCacheMemory += reclaimed;
}
MM::CheckMemoryCounters();
// Clear memory to value determined by chunk member
memset(reinterpret_cast<void*>(aBase), aClearByte, aSize);
return KErrNone;
}
}
MM::AllocFailed = ETrue;
return KErrNoMemory;
}
TInt MM::Decommit(TLinAddr aBase, TInt aSize)
//
// Get win32 to decommit the pages.
// The pages may or may not be committed: we need to find out which ones are so that the memory info is updated correctly
//
{
__ASSERT_MUTEX(RamAllocatorMutex);
TInt freed = 0;
TInt remain = aSize;
TLinAddr base = aBase;
__LOCK_HOST;
while (remain > 0)
{
MEMORY_BASIC_INFORMATION info;
VirtualQuery(LPVOID(base), &info, sizeof(info));
TInt size = Min(remain, info.RegionSize);
if (info.State == MEM_COMMIT)
freed += size;
#ifdef BTRACE_CHUNKS
BTraceContext12(BTrace::EChunks,BTrace::EChunkMemoryDeallocated,NULL,base,size);
#endif
base += info.RegionSize;
remain -= info.RegionSize;
}
VirtualFree(LPVOID(aBase), aSize, MEM_DECOMMIT);
MM::FreeMemory += freed;
__KTRACE_OPT(KMEMTRACE, {Kern::Printf("MT:A %d %x %x %O",NTickCount(),NULL,aSize,NULL);});
return freed;
}
void MM::CheckMemoryCounters()
{
__NK_ASSERT_ALWAYS(MM::CacheMemory>=0);
__NK_ASSERT_ALWAYS(MM::ReclaimedCacheMemory>=0);
__NK_ASSERT_ALWAYS(MM::FreeMemory+MM::CacheMemory>=0);
}
void MM::Signal()
{
if (RamAllocatorMutex->iHoldCount>1)
{
Kern::MutexSignal(*RamAllocatorMutex);
return;
}
TInt initial=InitialFreeMemory;
TBool failed=AllocFailed;
TInt final=FreeMemory;
Kern::MutexSignal(*RamAllocatorMutex);
K::CheckFreeMemoryLevel(initial,final,failed);
}
void MM::DoProcessSwitch(TAny* aAddressSpace)
// Kernel locked on entry and exit
{
__NK_ASSERT_LOCKED;
if (!aAddressSpace)
return;
DWin32Process* proc = (DWin32Process*)aAddressSpace;
if (proc == K::TheKernelProcess) return;
int count = proc->iDllData.Count();
for (int ii=0; ii<count; ii++)
{
SProcessDllDataBlock& procData = proc->iDllData[ii];
DWin32CodeSeg* codeSeg = procData.iCodeSeg;
if (!codeSeg)
continue;
DWin32Process*& liveProc = codeSeg->iLiveProcess;
if (liveProc == proc)
continue; // no change in live mapping
if (liveProc)
{
// copy out old process data
TInt liveIx = liveProc->iDllData.FindInUnsignedKeyOrder(procData);
__ASSERT_ALWAYS(liveIx >= 0,MM::Panic(MM::EWsdDllNotInProcess));
SProcessDllDataBlock& oldProcData = liveProc->iDllData[liveIx];
memcpy(oldProcData.iDataCopy, (const TAny*)codeSeg->iDataDest, codeSeg->iRealDataSize);
memcpy(oldProcData.iBssCopy, (const TAny*)codeSeg->iBssDest, codeSeg->iRealBssSize);
}
// copy new data in
memcpy((TAny*)codeSeg->iDataDest, procData.iDataCopy, codeSeg->iRealDataSize);
memcpy((TAny*)codeSeg->iBssDest, procData.iBssCopy, codeSeg->iRealBssSize);
liveProc = proc;
}
}
TAny* MM::CurrentAddress(DThread* aThread, const TAny* aPtr, TInt aSize, TBool /*aWrite*/, TBool& aLocked)
// Enter and leave with system locked
// Kernel unlocked on entry
// Kernel may be locked on exit, iff aPtr is in DLL WSD.
// this is because the returned address is only valid until the
// target process DLL WSD changes live status, which can happen
// independently when another thread runs.
// Lock status signaled in aLocked.
// Why? This allows the optimisation that WSD is only copied on
// process switch when necessary. The gain from that optimisation is
// expected to be much higher than the cost of leaving the kernel locked
// during (rare) IPC to DLL WSD
{
DWin32Process* proc = (DWin32Process*)aThread->iOwningProcess;
// Is the address in DLL static data?
NKern::Lock();
TInt count = proc->iDllData.Count();
TLinAddr p = (TLinAddr)aPtr;
TLinAddr base = 0;
TInt size = 0;
TBool data = EFalse;
aLocked = EFalse;
for (TInt ii=0; ii<count; ii++)
{
const SProcessDllDataBlock& procData = proc->iDllData[ii];
DWin32CodeSeg* codeSeg = procData.iCodeSeg;
if (codeSeg->iDataDest <= p && p < codeSeg->iDataDest + codeSeg->iRealDataSize)
{
base = codeSeg->iDataDest;
size = codeSeg->iRealDataSize;
data = ETrue;
}
else if (codeSeg->iBssDest <= p && p < codeSeg->iBssDest + codeSeg->iRealBssSize)
{
base = codeSeg->iBssDest;
size = codeSeg->iRealBssSize;
}
if (base)
{
// This is a DLL static address, check range validity
if (p + aSize > base + size)
{
NKern::Unlock();
return NULL;
}
DWin32Process* liveProc = codeSeg->iLiveProcess;
if (proc == liveProc)
{
// If the target process is live, don't remap
NKern::Unlock();
return (TAny*)aPtr;
}
else
{
aLocked = ETrue;
TLinAddr procBase = (TLinAddr)(data ? procData.iDataCopy : procData.iBssCopy);
TLinAddr remapped = procBase + (p - base);
return (TAny*) remapped;
}
}
}
NKern::Unlock();
// No, the address does not need to be remapped
return (TAny*)aPtr;
}
void M::BTracePrime(TUint aCategory)
{
(void)aCategory;
#ifdef BTRACE_KERNEL_MEMORY
// Must check for -1 as that is the default value of aCategory for
// BTrace::Prime() which is intended to prime all categories that are
// currently enabled via a single invocation of BTrace::Prime().
if(aCategory==BTrace::EKernelMemory || (TInt)aCategory == -1)
{
BTrace4(BTrace::EKernelMemory,BTrace::EKernelMemoryInitialFree,TheSuperPage().iTotalRamSize);
BTrace4(BTrace::EKernelMemory,BTrace::EKernelMemoryCurrentFree,Kern::FreeRamInBytes());
}
#endif
}
/**
Restart the system.
On hardware targets this calls the Restart Vector in the ROM Header.
Note, aMode is set to zero when this function is used by Kern::Fault()
@param aMode Argument passed to the restart routine. The meaning of this value
depends on the bootstrap implementation.
*/
EXPORT_C void Kern::Restart(TInt)
{
ExitProcess(0);
}
EXPORT_C TInt TInternalRamDrive::MaxSize()
{
return PP::RamDriveMaxSize;
}
void M::FsRegisterThread()
{
}
void P::SetSuperPageSignature()
{
TUint32* sig = TheSuperPage().iSignature;
sig[0] = 0xb504f333;
sig[1] = 0xf9de6484;
}
TBool P::CheckSuperPageSignature()
{
const TUint32* sig = TheSuperPage().iSignature;
return ( sig[0]==0xb504f333 && sig[1]==0xf9de6484 );
}
// Dummy implementation of kernel pin APIs
class TVirtualPinObject
{
};
TInt M::CreateVirtualPinObject(TVirtualPinObject*& aPinObject)
{
aPinObject = new TVirtualPinObject;
return aPinObject != NULL ? KErrNone : KErrNoMemory;
}
TInt M::PinVirtualMemory(TVirtualPinObject* aPinObject, TLinAddr, TUint, DThread*)
{
__ASSERT_DEBUG(aPinObject, K::Fault(K::EVirtualPinObjectBad));
(void)aPinObject;
return KErrNone;
}
TInt M::CreateAndPinVirtualMemory(TVirtualPinObject*& aPinObject, TLinAddr, TUint)
{
aPinObject = 0;
return KErrNone;
}
void M::UnpinVirtualMemory(TVirtualPinObject* aPinObject)
{
__ASSERT_DEBUG(aPinObject, K::Fault(K::EVirtualPinObjectBad));
(void)aPinObject;
}
void M::DestroyVirtualPinObject(TVirtualPinObject*& aPinObject)
{
TVirtualPinObject* object = (TVirtualPinObject*)__e32_atomic_swp_ord_ptr(&aPinObject, 0);
if (object)
Kern::AsyncFree(object);
}
class TPhysicalPinObject
{
};
TInt M::CreatePhysicalPinObject(TPhysicalPinObject*& aPinObject)
{
aPinObject = new TPhysicalPinObject;
return aPinObject != NULL ? KErrNone : KErrNoMemory;
}
TInt M::PinPhysicalMemory(TPhysicalPinObject* aPinObject, TLinAddr, TUint, TBool, TPhysAddr&, TPhysAddr*, TUint32&, TUint&, DThread*)
{
__ASSERT_DEBUG(aPinObject, K::Fault(K::EPhysicalPinObjectBad));
(void)aPinObject;
return KErrNone;
}
void M::UnpinPhysicalMemory(TPhysicalPinObject* aPinObject)
{
__ASSERT_DEBUG(aPinObject, K::Fault(K::EPhysicalPinObjectBad));
(void)aPinObject;
}
void M::DestroyPhysicalPinObject(TPhysicalPinObject*& aPinObject)
{
TPhysicalPinObject* object = (TPhysicalPinObject*)__e32_atomic_swp_ord_ptr(&aPinObject, 0);
if (object)
Kern::AsyncFree(object);
}
//
// Kernel map and pin (Not supported on the emulator).
//
TInt M::CreateKernelMapObject(TKernelMapObject*&, TUint)
{
return KErrNotSupported;
}
TInt M::MapAndPinMemory(TKernelMapObject*, DThread*, TLinAddr, TUint, TUint, TLinAddr&, TPhysAddr*)
{
return KErrNotSupported;
}
void M::UnmapAndUnpinMemory(TKernelMapObject*)
{
}
void M::DestroyKernelMapObject(TKernelMapObject*&)
{
}
// Misc DPagingDevice methods
EXPORT_C void DPagingDevice::NotifyIdle()
{
// Not used on this memory model
}
EXPORT_C void DPagingDevice::NotifyBusy()
{
// Not used on this memory model
}
EXPORT_C TInt Cache::SyncPhysicalMemoryBeforeDmaWrite(TPhysAddr* , TUint , TUint , TUint , TUint32 )
{
CHECK_PRECONDITIONS(MASK_THREAD_STANDARD,"Cache::SyncPhysicalMemoryBeforeDmaWrite");
return KErrNotSupported;
}
EXPORT_C TInt Cache::SyncPhysicalMemoryBeforeDmaRead(TPhysAddr* , TUint , TUint , TUint , TUint32 )
{
CHECK_PRECONDITIONS(MASK_THREAD_STANDARD,"Cache::SyncPhysicalMemoryBeforeDmaRead");
return KErrNotSupported;
}
EXPORT_C TInt Cache::SyncPhysicalMemoryAfterDmaRead(TPhysAddr* , TUint , TUint , TUint , TUint32 )
{
CHECK_PRECONDITIONS(MASK_THREAD_STANDARD,"Cache::SyncPhysicalMemoryAfterDmaRead");
return KErrNotSupported;
}