--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/kernel/eka/memmodel/epoc/mmubase/ramalloc.cpp Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,2966 @@
+// Copyright (c) 1998-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\epoc\mmubase\ramalloc.cpp
+//
+//
+
+/**
+ @file
+ @internalComponent
+*/
+//#define __VERIFY_LEASTMOVDIS
+
+#include <plat_priv.h>
+#include <ramalloc.h>
+#include <e32btrace.h>
+
+#ifndef __MEMMODEL_FLEXIBLE__
+#include <mmubase.inl>
+#else
+#include "mdefrag.inl"
+#endif //__MEMMODEL_FLEXIBLE__
+
+DRamAllocator* DRamAllocator::New()
+ {
+ return new DRamAllocator;
+ }
+
+DRamAllocator* DRamAllocator::New(const SRamInfo& aInfo, const SRamZone* aZoneInfo, TRamZoneCallback aZoneCallback)
+ {
+ DRamAllocator* pA=New();
+ if (!pA)
+ Panic(ECreateNoMemory);
+ // If this fails then it won't return but panic
+ pA->Create(aInfo,aZoneInfo, aZoneCallback);
+ return pA;
+ }
+
+void DRamAllocator::Panic(TPanic aPanic)
+ {
+ Kern::Fault("RAM-ALLOC", aPanic);
+ }
+
+#ifdef KMMU
+void HexDump32(const TAny* a, TInt n, const char* s)
+ {
+ const TUint32* p=(const TUint32*)a;
+ Kern::Printf(s);
+ TInt i=0;
+ while(n)
+ {
+ TBuf8<80> b;
+ b.AppendNumFixedWidth(i,EHex,4);
+ b.Append(':');
+ TInt m=Min(n,4);
+ n-=m;
+ i+=m;
+ while(m--)
+ {
+ b.Append(' ');
+ b.AppendNumFixedWidth(*p++,EHex,8);
+ }
+ Kern::Printf("%S",&b);
+ }
+ }
+
+void HexDump8(const TAny* a, TInt n, const char* s)
+ {
+ const TUint8* p=(const TUint8*)a;
+ Kern::Printf(s);
+ TInt i=0;
+ while(n)
+ {
+ TBuf8<80> b;
+ b.AppendNumFixedWidth(i,EHex,4);
+ b.Append(':');
+ TInt m=Min(n,16);
+ n-=m;
+ i+=m;
+ while(m--)
+ {
+ b.Append(' ');
+ b.AppendNumFixedWidth(*p++,EHex,2);
+ }
+ Kern::Printf("%S",&b);
+ }
+ }
+
+void DRamAllocator::DebugDump()
+ {
+ Kern::Printf("PageSize=%08x PageShift=%d",KPageSize,KPageShift);
+ Kern::Printf("Total Pages=%x Total Free=%x",iTotalRamPages,iTotalFreeRamPages);
+ Kern::Printf("Number of zones=%d, PowerState=%016lx",iNumZones,iZonePwrState);
+ Kern::Printf("PhysAddrBase=%08x, PhysAddrTop=%08x",iPhysAddrBase,iPhysAddrTop);
+
+ TUint i = 0;
+ Kern::Printf("Zone Info:");
+ for (; i<iNumZones; ++i)
+ {
+ SZone& z=iZones[i];
+ TBitMapAllocator& b = *(z.iBma[KBmaAllPages]);
+ Kern::Printf("%x: Avail %x Size %x Phys %08x PhysEnd %08x ID %08x FreePage %x Pref %02x",i,b.iAvail,b.iSize,
+ z.iPhysBase, z.iPhysEnd, z.iId,z.iFreePages, z.iPref);
+ Kern::Printf("Allocated Unknown %x Fixed %x Movable %x Discardable %x",iZones[i].iAllocPages[EPageUnknown],iZones[i].iAllocPages[EPageFixed],
+ iZones[i].iAllocPages[EPageMovable],iZones[i].iAllocPages[EPageDiscard]);
+ }
+
+ Kern::Printf("Zone pref order:");
+ SDblQueLink* link = iZonePrefList.First();
+ for (; link != &iZonePrefList.iA; link = link->iNext)
+ {
+ SZone& zone = *_LOFF(link, SZone, iPrefLink);
+ Kern::Printf("ID0x%x rank0x%x", zone.iId, zone.iPrefRank);
+ }
+ SZone& zone = *_LOFF(iZoneLeastMovDis, SZone, iPrefLink);
+ Kern::Printf("iZoneLeastMovDis ID 0x%x rank 0x%x", zone.iId, iZoneLeastMovDisRank);
+ }
+#endif
+
+TInt CountBanks(const SRamBank* aBankList)
+ {
+ TInt banks=0;
+ for (; aBankList->iSize; ++banks, ++aBankList);
+ return banks;
+ }
+
+TUint32 TotalBankSize(const SRamBank* aBankList)
+ {
+ TUint32 size=0;
+ for (; aBankList->iSize; ++aBankList)
+ size+=aBankList->iSize;
+ return size;
+ }
+
+/**
+Count how many zones have been specified and do some basic checks on their layout:
+ Zones must be distinct, i.e. not overlap
+ Zone ID must be unique
+ Zones must be page size aligned
+ Zones must be big enough to cover all of the allocatable RAM
+The end of the list is indicated by a SRamZone.iSize==0.
+@param aZones The list of RAM zones to be setup
+*/
+void DRamAllocator::CountZones(const SRamZone* aZones)
+ {
+ TUint32 totalSize = 0;
+ TUint32 pageMask = KPageSize-1;
+ // Check zones don't overlap each other and while running through the zones
+ // calculate how many there are
+ const SRamZone* pCurZ = aZones;
+ for (; pCurZ->iSize != 0; pCurZ++)
+ {
+ // Verify zone addresses and alignment
+ TUint32 curEnd = pCurZ->iBase + pCurZ->iSize - 1;
+ __KTRACE_OPT(KMMU,Kern::Printf("curBase %x curEnd %x pageMask %x",pCurZ->iBase,curEnd,pageMask));
+ if (curEnd <= pCurZ->iBase || (((curEnd + 1) | pCurZ->iBase) & pageMask))
+ {
+ Panic(EZonesAlignment);
+ }
+
+ if (pCurZ->iId == KRamZoneInvalidId)
+ {
+ Panic(EZonesIDInvalid);
+ }
+ // Check the flags are not set to invalid values
+ if (pCurZ->iFlags & KRamZoneFlagInvalid)
+ {
+ Panic(EZonesFlagsInvalid);
+ }
+
+ iNumZones++;
+ if (iNumZones > KMaxRamZones)
+ {// Too many zones specified
+ Panic(EZonesTooNumerousOrFew);
+ }
+ totalSize += pCurZ->iSize;
+
+ // Verify this zone doesn't overlap any of the previous zones' address space
+ const SRamZone* pTmpZ = aZones;
+ for (; pTmpZ < pCurZ; pTmpZ++)
+ {
+ TUint32 tmpEnd = pTmpZ->iBase + pTmpZ->iSize - 1;
+ if (tmpEnd >= pCurZ->iBase && pTmpZ->iBase <= curEnd)
+ {
+ Panic(EZonesNotDistinct);
+ }
+ if(pTmpZ->iId == pCurZ->iId)
+ {
+ Panic(EZonesIDNotUnique);
+ }
+ }
+ }
+ __KTRACE_OPT(KMMU,Kern::Printf("iNumZones=%d, totalSize=%x",iNumZones,totalSize));
+ if (!iNumZones)
+ {// no zones specified
+ Panic(EZonesTooNumerousOrFew);
+ }
+
+ // Together all of the zones should cover the whole of the RAM
+ if (totalSize>>KPageShift < iTotalRamPages)
+ {
+ Panic(EZonesIncomplete);
+ }
+ }
+
+
+/**
+Get the zone from the ID
+@param aId ID of zone to find
+@return Pointer to the zone if zone of matching ID found, NULL otherwise
+*/
+SZone* DRamAllocator::ZoneFromId(TUint aId) const
+ {
+ SZone* pZ = iZones;
+ const SZone* const pEndZone = iZones + iNumZones;
+ for (; pZ < pEndZone; pZ++)
+ {
+ if (aId == pZ->iId)
+ {
+ return pZ;
+ }
+ }
+ return NULL;
+ }
+
+/** Retrieve the physical base address and number of pages in the specified zone.
+
+@param aZoneId The ID of the zone
+@param aPhysBaseAddr Receives the base address of the zone
+@param aNumPages Receives the number of pages in the zone
+
+@return KErrNone if zone found, KErrArgument if zone couldn't be found
+*/
+TInt DRamAllocator::GetZoneAddress(TUint aZoneId, TPhysAddr& aPhysBase, TUint& aNumPages)
+ {
+ SZone* zone = ZoneFromId(aZoneId);
+ if (zone == NULL)
+ {
+ return KErrArgument;
+ }
+ aPhysBase = zone->iPhysBase;
+ aNumPages = zone->iPhysPages;
+ return KErrNone;
+ }
+
+#ifdef __MEMMODEL_FLEXIBLE__
+/**
+@param aAddr The address of page to find the zone of
+@param aOffset The page offset from the start of the zone that the page is in
+*/
+SZone* DRamAllocator::GetZoneAndOffset(TPhysAddr aAddr, TInt& aOffset)
+ {
+ // Get the zone from the SPageInfo of the page at aAddr
+ SPageInfo* pageInfo = SPageInfo::SafeFromPhysAddr(aAddr);
+ if (pageInfo == NULL)
+ {
+ return NULL;
+ }
+
+ // Perform a binary search for the RAM zone, we know aAddr is within a RAM
+ // zone as pageInfo != NULL.
+ SZone* left = iZones;
+ SZone* mid = iZones + (iNumZones>>1);
+ SZone* top = iZones + iNumZones - 1;
+
+ while (mid->iPhysEnd < aAddr || mid->iPhysBase > aAddr)
+ {
+ if (mid->iPhysEnd < aAddr)
+ left = mid + 1;
+ else
+ top = mid - 1;
+ mid = left + ((top - left) >> 1);
+ __ASSERT_DEBUG(left <= top && mid <= top && mid >= left, Panic(EAllocRamPagesInconsistent));
+ }
+ __ASSERT_DEBUG(mid->iPhysBase <= aAddr && mid->iPhysEnd >= aAddr, Panic(EAllocRamPagesInconsistent));
+ aOffset = (aAddr - mid->iPhysBase) >> KPageShift;
+ __ASSERT_DEBUG((TUint)aOffset < mid->iPhysPages, Panic(EAllocRamPagesInconsistent));
+ return mid;
+ }
+#else
+/**
+@param aAddr The address of page to find the zone of
+@param aOffset The page offset from the start of the zone that the page is in
+*/
+SZone* DRamAllocator::GetZoneAndOffset(TPhysAddr aAddr, TInt& aOffset)
+ {
+ // Get the zone from the SPageInfo of the page at aAddr
+ SPageInfo* pageInfo = SPageInfo::SafeFromPhysAddr(aAddr);
+ if (pageInfo == NULL)
+ {
+ return NULL;
+ }
+ SZone* z = iZones + pageInfo->Zone();
+ aOffset = (aAddr - z->iPhysBase) >> KPageShift;
+ __ASSERT_DEBUG((TUint)aOffset < z->iPhysPages, Panic(EAllocRamPagesInconsistent));
+ return z;
+ }
+#endif
+/**
+@param aId ID of zone to get page count for
+@param aPageData store for page counts
+@return KErrNone if zone found, KErrArgument otherwise
+*/
+TInt DRamAllocator::GetZonePageCount(TUint aId, SRamZonePageCount& aPageData)
+ {
+ // Search for the zone of ID aId
+ const SZone* zone = ZoneFromId(aId);
+ if (zone == NULL)
+ {
+ return KErrArgument;
+ }
+ aPageData.iFreePages = zone->iFreePages;
+ aPageData.iUnknownPages = zone->iAllocPages[EPageUnknown];
+ aPageData.iFixedPages = zone->iAllocPages[EPageFixed];
+ aPageData.iMovablePages = zone->iAllocPages[EPageMovable];
+ aPageData.iDiscardablePages = zone->iAllocPages[EPageDiscard];
+
+ return KErrNone;
+ }
+
+
+/** Update the count of free and allocated pages for the zone with
+@param aZone The index of the zone whose counts are being updated
+@param aCount The no of pages being allocated
+@param aType The type of the pages being allocated
+*/
+void DRamAllocator::ZoneAllocPages(SZone* aZone, TUint32 aCount, TZonePageType aType)
+ {
+#ifdef _DEBUG
+ TUint32 free = aZone->iFreePages - aCount;
+ TUint32 alloc = aZone->iAllocPages[aType] + aCount;
+ TUint32 total_alloc = aZone->iAllocPages[EPageUnknown] +
+ aZone->iAllocPages[EPageDiscard] +
+ aZone->iAllocPages[EPageMovable] +
+ aZone->iAllocPages[EPageFixed] + aCount;
+ if (free > aZone->iFreePages ||
+ alloc < aZone->iAllocPages[aType] ||
+ free + total_alloc != aZone->iPhysPages ||
+ iTotalFreeRamPages > iTotalRamPages)
+ {
+ __KTRACE_OPT(KMMU,Kern::Printf("TotalFree %x TotalPages %x",iTotalFreeRamPages, iTotalRamPages));
+ __KTRACE_OPT(KMMU,Kern::Printf("ZoneFreePages - aCount %x free %x, alloc %x",aCount,free,alloc)); // counts rolled over
+ __KTRACE_OPT(KMMU,Kern::Printf("Alloc Unk %x Fx %x Mv %x Dis %x",aZone->iAllocPages[EPageUnknown],
+ aZone->iAllocPages[EPageFixed], aZone->iAllocPages[EPageMovable],aZone->iAllocPages[EPageDiscard]));
+ Panic(EZonesCountErr);
+ }
+ __ASSERT_DEBUG(free == (TUint32)aZone->iBma[KBmaAllPages]->iAvail, Panic(EAllocRamPagesInconsistent));
+ __KTRACE_OPT(KMMU2,Kern::Printf("ZoneFreePages - aCount %x free %x, alloc %x",aCount,free,alloc));
+ __KTRACE_OPT(KMMU2,Kern::Printf("Alloc Unk %x Fx %x Mv %x Dis %x",aZone->iAllocPages[EPageUnknown],
+ aZone->iAllocPages[EPageFixed], aZone->iAllocPages[EPageMovable],aZone->iAllocPages[EPageDiscard]));
+
+ if (iAllowBmaVerify)
+ {
+ TBitMapAllocator& bmaType = *(aZone->iBma[(aType != EPageUnknown)? aType : EPageFixed]);
+ TUint allocPages;
+ if (aType == EPageFixed || aType == EPageUnknown)
+ allocPages = aZone->iAllocPages[EPageUnknown] + aZone->iAllocPages[EPageFixed];
+ else
+ allocPages = aZone->iAllocPages[aType];
+ allocPages += aCount;
+ __NK_ASSERT_DEBUG(aZone->iPhysPages - bmaType.iAvail == allocPages);
+ __NK_ASSERT_DEBUG((TUint)bmaType.iAvail >= aZone->iFreePages - aCount);
+
+//#define _FULL_VERIFY_TYPE_BMAS
+#ifdef _FULL_VERIFY_TYPE_BMAS
+ TUint offset = 0;
+ TUint matchedPages = 0;
+ TInt r = KErrNone;
+ while (offset < aZone->iPhysPages && r == KErrNone)
+ {
+ r = NextAllocatedPage(aZone, offset, EPageTypes);
+ if (bmaType.NotFree(offset, 1))
+ {
+ matchedPages++;
+ }
+ offset++;
+ }
+ __NK_ASSERT_DEBUG(matchedPages == allocPages);
+#endif
+ }
+#endif
+
+ // Update counts
+ aZone->iAllocPages[aType] += aCount;
+ aZone->iFreePages -= aCount;
+ aZone->iFlags &= ~KRamZoneFlagMark; // clear the mark as this zone is active
+
+ // Check if power state of zone needs to be changed
+ if (iZonePowerFunc && !(iZonePwrState & (((TUint64)1) << aZone - iZones)))
+ {//zone no longer empty so call variant to power RAM zone up if necessary
+ iZonePwrState |= (((TUint64)1) << aZone - iZones);
+
+ if (iZoneCallbackInitSent)
+ {
+ TInt ret = (*iZonePowerFunc)(ERamZoneOp_PowerUp, (TAny*)aZone->iId, (TUint*)&iZonePwrState);
+ if (ret != KErrNone && ret != KErrNotSupported)
+ {
+ Panic(EZonesCallbackErr);
+ }
+ CHECK_PRECONDITIONS(MASK_THREAD_CRITICAL, "DRamAllocator::ZoneAllocPages");
+ }
+ }
+
+ // Re-order the zone preference list so that a RAM zone with more immovable pages
+ // is more preferable and secondary to that a RAM zone that is not empty is more
+ // preferable than one that is empty.
+ while (&aZone->iPrefLink != iZonePrefList.First())
+ {
+ SZone* prevZ = _LOFF(aZone->iPrefLink.iPrev, SZone, iPrefLink);
+ __NK_ASSERT_DEBUG(K::Initialising || prevZ->iPrefRank == aZone->iPrefRank - 1);
+ if (prevZ->iPref == aZone->iPref &&
+ (prevZ->iAllocPages[EPageFixed] + prevZ->iAllocPages[EPageUnknown] <
+ aZone->iAllocPages[EPageFixed] + aZone->iAllocPages[EPageUnknown] ||
+ prevZ->iFreePages == prevZ->iPhysPages))
+ {
+ __KTRACE_OPT(KMMU, Kern::Printf("a - Reorder aZone 0x%x free 0x%x before prevZ 0x%x free 0x%x", aZone->iId, aZone->iFreePages, prevZ->iId, prevZ->iFreePages));
+ // Make this RAM zone more preferable.
+ aZone->iPrefLink.Deque();
+ aZone->iPrefLink.InsertBefore(&prevZ->iPrefLink);
+ aZone->iPrefRank--;
+ prevZ->iPrefRank++;
+
+ if (iZoneLeastMovDis == &prevZ->iPrefLink)
+ {// Ensure iZoneLeastMovDisRank is kept up to date.
+ iZoneLeastMovDisRank = prevZ->iPrefRank;
+ }
+ if (iZoneLeastMovDis == &aZone->iPrefLink)
+ {// Ensure iZoneLeastMovDisRank is kept up to date.
+ iZoneLeastMovDisRank = aZone->iPrefRank;
+ // aZone was the least preferable with movable and/or discardable so is it still?
+ if (prevZ->iAllocPages[EPageMovable] || prevZ->iAllocPages[EPageDiscard])
+ {// prevZ is now the least preferable RAM zone with movable and/or discardable.
+ iZoneLeastMovDis = &prevZ->iPrefLink;
+ iZoneLeastMovDisRank = prevZ->iPrefRank;
+ __KTRACE_OPT(KMMU, Kern::Printf("aa - iZoneleastInUse ID 0x%x", (_LOFF(iZoneLeastMovDis, SZone, iPrefLink))->iId));
+ }
+ __KTRACE_OPT(KMMU, Kern::Printf("iZoneLeastMovDisRank 0x%x", iZoneLeastMovDisRank));
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ // Now that the preference list has been re-ordered check whether
+ // iZoneLeastMovDis needs updating.
+ if (aType >= EPageMovable && iZoneLeastMovDisRank < aZone->iPrefRank)
+ {
+ iZoneLeastMovDis = &aZone->iPrefLink;
+ iZoneLeastMovDisRank = aZone->iPrefRank;
+ __KTRACE_OPT(KMMU, Kern::Printf("a - iZoneleastInUse ID 0x%x", (_LOFF(iZoneLeastMovDis, SZone, iPrefLink))->iId));
+ }
+ __NK_ASSERT_DEBUG( K::Initialising ||
+ iZoneLeastMovDisRank == _LOFF(iZoneLeastMovDis, SZone, iPrefLink)->iPrefRank);
+#ifdef __VERIFY_LEASTMOVDIS
+ if (!K::Initialising)
+ VerifyLeastPrefMovDis();
+#endif
+ }
+
+
+/** Update the count of free and allocated pages for the zone with
+@param aZone The index of the zone whose counts are being updated
+@param aCount The no of pages being freed
+@param aType The type of the pages being freed
+*/
+void DRamAllocator::ZoneFreePages(SZone* aZone, TUint32 aCount, TZonePageType aType)
+ {
+#ifdef _DEBUG
+ TUint32 alloc = aZone->iAllocPages[aType] - aCount;
+ TUint32 free = aZone->iFreePages + aCount;
+ TUint32 total_alloc = aZone->iAllocPages[EPageUnknown] +
+ aZone->iAllocPages[EPageDiscard] +
+ aZone->iAllocPages[EPageMovable] +
+ aZone->iAllocPages[EPageFixed] - aCount;
+ if (free < aZone->iFreePages ||
+ alloc > aZone->iAllocPages[aType] ||
+ free + total_alloc != aZone->iPhysPages ||
+ iTotalFreeRamPages > iTotalRamPages)
+ {
+ __KTRACE_OPT(KMMU,Kern::Printf("TotalFree %x TotalPages %x",iTotalFreeRamPages, iTotalRamPages));
+ __KTRACE_OPT(KMMU,Kern::Printf("ZoneFreePages - aCount %x free %x, alloc %x",aCount,free,alloc)); // counts rolled over
+ __KTRACE_OPT(KMMU,Kern::Printf("Alloc Unk %x Fx %x Mv %x Dis %x",aZone->iAllocPages[EPageUnknown],
+ aZone->iAllocPages[EPageFixed], aZone->iAllocPages[EPageMovable],aZone->iAllocPages[EPageDiscard]));
+ Panic(EZonesCountErr);
+ }
+ __ASSERT_DEBUG(free == (TUint32)aZone->iBma[KBmaAllPages]->iAvail, Panic(EAllocRamPagesInconsistent));
+ __KTRACE_OPT(KMMU2,Kern::Printf("ZoneFreePages - aCount %x free %x, alloc %x",aCount,free,alloc));
+ __KTRACE_OPT(KMMU2,Kern::Printf("Alloc Unk %x Fx %x Mv %x Dis %x",aZone->iAllocPages[EPageUnknown],
+ aZone->iAllocPages[EPageFixed], aZone->iAllocPages[EPageMovable],aZone->iAllocPages[EPageDiscard]));
+
+ if (iAllowBmaVerify)
+ {
+ TBitMapAllocator& bmaType = *(aZone->iBma[(aType != EPageUnknown)? aType : EPageFixed]);
+ TUint allocPages;
+ if (aType == EPageFixed || aType == EPageUnknown)
+ allocPages = aZone->iAllocPages[EPageUnknown] + aZone->iAllocPages[EPageFixed];
+ else
+ allocPages = aZone->iAllocPages[aType];
+ allocPages -= aCount;
+ __NK_ASSERT_DEBUG(aZone->iPhysPages - bmaType.iAvail == allocPages);
+ __NK_ASSERT_DEBUG((TUint)bmaType.iAvail >= aZone->iFreePages + aCount);
+
+#ifdef _FULL_VERIFY_TYPE_BMAS
+ TUint offset = 0;
+ TUint matchedPages = 0;
+ TInt r = KErrNone;
+ while(offset < aZone->iPhysPages && r == KErrNone)
+ {
+ r = NextAllocatedPage(aZone, offset, EPageTypes);
+ if (bmaType.NotFree(offset, 1))
+ {
+ matchedPages++;
+ }
+ offset++;
+ }
+ __NK_ASSERT_DEBUG(matchedPages == allocPages);
+#endif
+ }
+#endif
+
+ // Update counts
+ aZone->iAllocPages[aType] -= aCount;
+ aZone->iFreePages += aCount;
+ aZone->iFlags &= ~KRamZoneFlagMark; // clear the mark as this zone is active
+
+ // Check if power state of zone needs to be changed.
+ // Don't update iZonePwrState when a zone is being cleared to then be
+ // claimed as it shouldn't be powered off as it's about to be used.
+ if (iZonePowerFunc && !(aZone->iFlags & KRamZoneFlagClaiming) &&
+ aZone->iFreePages == aZone->iPhysPages)
+ {// Zone is empty so call variant to power down RAM zone if desirable.
+ TUint64 pwrMask = ~(((TUint64)1) << aZone - iZones);
+ iZonePwrState &= pwrMask;
+
+ // Don't invoke callback until Init callback sent.
+ if (iZoneCallbackInitSent)
+ {
+ TInt ret = (*iZonePowerFunc)(ERamZoneOp_PowerDown, (TAny*)aZone->iId, (TUint*)&iZonePwrState);
+ if (ret != KErrNone && ret != KErrNotSupported)
+ {
+ Panic(EZonesCallbackErr);
+ }
+ CHECK_PRECONDITIONS(MASK_THREAD_CRITICAL, "DRamAllocator::ZoneFreePages");
+ }
+ }
+
+ // Re-order the zone preference list so that a RAM zone with more immovable pages
+ // is more preferable and secondary to that a RAM zone that is not empty is more
+ // preferable than one that is empty.
+ while (&aZone->iPrefLink != iZonePrefList.Last())
+ {
+ SZone* nextZ = _LOFF(aZone->iPrefLink.iNext, SZone, iPrefLink);
+ __NK_ASSERT_DEBUG(K::Initialising || nextZ->iPrefRank == aZone->iPrefRank + 1);
+ if (nextZ->iPref == aZone->iPref &&
+ (nextZ->iAllocPages[EPageFixed] + nextZ->iAllocPages[EPageUnknown] >
+ aZone->iAllocPages[EPageFixed] + aZone->iAllocPages[EPageUnknown] ||
+ (nextZ->iFreePages != nextZ->iPhysPages &&
+ aZone->iFreePages == aZone->iPhysPages)))
+ {
+ __KTRACE_OPT(KMMU, Kern::Printf("f - Reorder aZone 0x%x free 0x%x after nextZ 0x%x free 0x%x", aZone->iId, aZone->iFreePages, nextZ->iId, nextZ->iFreePages));
+ // Make this RAM zone less preferable.
+ aZone->iPrefLink.Deque();
+ aZone->iPrefLink.InsertAfter(&nextZ->iPrefLink);
+ aZone->iPrefRank++;
+ nextZ->iPrefRank--;
+
+ if (iZoneLeastMovDis == &aZone->iPrefLink)
+ {// Ensure iZoneLeastMovDisRank is kept up to date.
+ iZoneLeastMovDisRank = aZone->iPrefRank;
+ }
+ if (iZoneLeastMovDis == &nextZ->iPrefLink)
+ {// Ensure iZoneLeastMovDisRank is kept up to date.
+ iZoneLeastMovDisRank = nextZ->iPrefRank;
+ if (aZone->iAllocPages[EPageMovable] || aZone->iAllocPages[EPageDiscard])
+ {// aZone is now the least preferable RAM zone with movable and/or discardable.
+ iZoneLeastMovDis = &aZone->iPrefLink;
+ iZoneLeastMovDisRank = aZone->iPrefRank;
+ __KTRACE_OPT(KMMU, Kern::Printf("aa - iZoneleastInUse ID 0x%x", (_LOFF(iZoneLeastMovDis, SZone, iPrefLink))->iId));
+ }
+ __KTRACE_OPT(KMMU, Kern::Printf("iZoneLeastMovDis Rank 0x%x", iZoneLeastMovDisRank));
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+ if (&aZone->iPrefLink == iZoneLeastMovDis &&
+ !aZone->iAllocPages[EPageMovable] && !aZone->iAllocPages[EPageDiscard])
+ {// This RAM zone no longer has movable or discardable and therefore it
+ // is also no longer the least preferable RAM zone with movable and/or
+ // discardable.
+ SZone* zonePrev;
+ do
+ {
+ iZoneLeastMovDis = iZoneLeastMovDis->iPrev;
+ iZoneLeastMovDisRank--;
+ if (iZoneLeastMovDis == iZonePrefList.First())
+ {// This the most preferable RAM zone so can't go any further.
+ break;
+ }
+ zonePrev = _LOFF(iZoneLeastMovDis, SZone, iPrefLink);
+ __KTRACE_OPT(KMMU, Kern::Printf("f - iZoneLeastMovDis 0x%x", zonePrev->iId));
+ }
+ while (!zonePrev->iAllocPages[EPageMovable] && !zonePrev->iAllocPages[EPageDiscard]);
+
+ __NK_ASSERT_DEBUG( K::Initialising ||
+ iZoneLeastMovDisRank == _LOFF(iZoneLeastMovDis, SZone, iPrefLink)->iPrefRank);
+
+#ifdef __VERIFY_LEASTMOVDIS
+ if (!K::Initialising)
+ VerifyLeastPrefMovDis();
+#endif
+ }
+ }
+
+
+/** Calculate the physical address order of the zones and temporally store
+ the order in aZoneAddrOrder
+*/
+inline void DRamAllocator::SortRamZones(const SRamZone* aZones, TUint8* aZoneAddrOrder)
+ {
+ const SRamZone* const endZone = aZones + iNumZones;
+ const SRamZone* zone = aZones;
+ for (; zone < endZone; zone++)
+ {
+ // zoneIdx is the number of zones that have a lower base address than the
+ // current zone and therefore it is the address index of the current zone
+ TInt zoneIdx = 0;
+ // search for any zones of lower base address
+ const SRamZone* zone2 = aZones;
+ for (; zone2 < endZone; zone2++)
+ {
+ if (zone2->iBase < zone->iBase)
+ {
+ zoneIdx++; // have another zone of lower base address
+ }
+ }
+ aZoneAddrOrder[zoneIdx] = zone - aZones;
+ }
+ }
+
+
+/** Initialise SPageInfos for all pages in this zone with the
+index of the zone.
+@param aZone The zone the pages to be initialised are in
+*/
+inline TUint DRamAllocator::InitSPageInfos(const SZone* aZone)
+ {
+ TUint pagesUpdated = 0;
+ if (aZone->iPhysBase > iPhysAddrTop || aZone->iPhysEnd < iPhysAddrBase)
+ {// None of the zone is in allocatable RAM
+ return pagesUpdated;
+ }
+
+ // Mark each allocatable page in this zone with the index of the zone
+#ifndef __MEMMODEL_FLEXIBLE__
+ TUint8 zoneIndex = aZone - iZones;
+#endif
+ TPhysAddr addr = aZone->iPhysBase;
+ for (; addr <= aZone->iPhysEnd; addr += KPageSize)
+ {
+ SPageInfo* pi = SPageInfo::SafeFromPhysAddr(addr);
+ if (pi)
+ {
+#ifndef __MEMMODEL_FLEXIBLE__ // The FMM doesn't store zone indices in SPageInfos.
+ pi->SetZone(zoneIndex);
+#endif
+ pagesUpdated++;
+ }
+ }
+ return pagesUpdated;
+ }
+
+/** HAL Function for the RAM allocator.
+*/
+TInt DRamAllocator::HalFunction(TInt aFunction, TAny* a1, TAny* a2)
+ {
+ switch(aFunction)
+ {
+ case ERamHalGetZoneCount:
+ {
+ kumemput32(a1, &iNumZones, sizeof(iNumZones));
+ return KErrNone;
+ }
+
+ case ERamHalGetZoneConfig:
+ {
+ TUint zoneIndex = (TUint)a1;
+ if (zoneIndex < iNumZones)
+ {
+ SZone* pZone = iZones + zoneIndex;
+ struct SRamZoneConfig config;
+ NKern::ThreadEnterCS();
+ M::RamAllocLock(); // get mutex to ensure consistent set of values are read...
+ config.iZoneId = pZone->iId;
+ config.iZoneIndex = zoneIndex;
+ config.iPhysBase = pZone->iPhysBase;
+ config.iPhysEnd = pZone->iPhysEnd;
+ config.iPhysPages = pZone->iPhysPages;
+ config.iPref = pZone->iPref;
+ config.iFlags = pZone->iFlags;
+ M::RamAllocUnlock();
+ NKern::ThreadLeaveCS();
+ kumemput32(a2,&config,sizeof(config));
+ return KErrNone;
+ }
+ return KErrNotFound;
+ }
+
+ case ERamHalGetZoneUtilisation:
+ {
+ TUint zoneIndex = (TUint)a1;
+ if (zoneIndex < iNumZones)
+ {
+ SZone* pZone = iZones + zoneIndex;
+ struct SRamZoneUtilisation config;
+ NKern::ThreadEnterCS();
+ M::RamAllocLock(); // get mutex to ensure consistent set of values are read...
+ config.iZoneId = pZone->iId;
+ config.iZoneIndex = zoneIndex;
+ config.iPhysPages = pZone->iPhysPages;
+ config.iFreePages = pZone->iFreePages;
+ config.iAllocUnknown = pZone->iAllocPages[EPageUnknown];
+ config.iAllocFixed = pZone->iAllocPages[EPageFixed];
+ config.iAllocMovable = pZone->iAllocPages[EPageMovable];
+ config.iAllocDiscardable = pZone->iAllocPages[EPageDiscard];
+ config.iAllocOther = 0;
+ M::RamAllocUnlock();
+ NKern::ThreadLeaveCS();
+ kumemput32(a2,&config,sizeof(config));
+ return KErrNone;
+ }
+ return KErrNotFound;
+ }
+
+ default:
+ {
+ return KErrNotSupported;
+ }
+ }
+ }
+
+/**
+Setup the ram allocator with information of the RAM available in the system that
+comes from the bootstrap/superpage. This is intended to be called from
+DRamAllocator::New().
+@internalComponent
+@see DRamAllocator::New()
+@param aInfo Two lists of SRamBanks for available and reserved banks in RAM, respectively
+@param aZones A list of the ram zones in the system and their configuration/preferences
+@param aZoneCallback Pointer to a base port call back function that will be invoked by this class
+*/
+void DRamAllocator::Create(const SRamInfo& aInfo, const SRamZone* aZones, TRamZoneCallback aZoneCallback)
+ {
+ __KTRACE_OPT(KMMU,Kern::Printf("DRamAllocator::Create"));
+
+ // SZone::iBma array assumes this and KBmaAllPages can't be the same as any
+ // allocatable page type.
+ __ASSERT_COMPILE(EPageFixed < KPageImmovable && EPageUnknown < KPageImmovable &&
+ EPageDiscard >= KPageImmovable && EPageMovable >= KPageImmovable &&
+ KBmaAllPages != EPageFixed && KBmaAllPages != EPageMovable &&
+ KBmaAllPages != EPageDiscard);
+ // NoAllocOfPageType() requires this
+ __ASSERT_COMPILE( KRamZoneFlagNoFixed == 1 << (EPageFixed - KPageTypeAllocBase) &&
+ KRamZoneFlagNoMovable == 1 << (EPageMovable - KPageTypeAllocBase) &&
+ KRamZoneFlagNoDiscard == 1 << (EPageDiscard - KPageTypeAllocBase));
+
+ // SZone::iPhysEnd and iPhysAddrTop rely on this when checking contiguous zones etc.
+ __ASSERT_COMPILE(KPageShift != 0);
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Determine where all the allocatable RAM pages are, using the SRamBank
+ // data passed to the kernel by the bootstrap
+ //////////////////////////////////////////////////////////////////////////
+ TUint num_boot_banks=CountBanks(aInfo.iBanks);
+ TUint32 total_ram_size=TotalBankSize(aInfo.iBanks);
+ __KTRACE_OPT(KMMU,Kern::Printf("#banks from bootstrap=%d",num_boot_banks));
+ __KTRACE_OPT(KMMU,Kern::Printf("Total size=%08x",total_ram_size));
+ iTotalRamPages=total_ram_size>>KPageShift;
+ // Assume all pages are allocated as unknown for now
+ iTotalFreeRamPages = 0;
+ __KTRACE_OPT(KMMU,Kern::Printf("Total size=%08x, total pages=%08x",total_ram_size,iTotalRamPages));
+
+ iPhysAddrBase=aInfo.iBanks[0].iBase;
+ const SRamBank& last_boot_bank=aInfo.iBanks[num_boot_banks-1];
+ iPhysAddrTop = last_boot_bank.iBase + last_boot_bank.iSize - 1;
+ __KTRACE_OPT(KMMU,Kern::Printf("PA base=%08x, PA top=%08x",iPhysAddrBase,iPhysAddrTop));
+
+ __ASSERT_DEBUG(iPhysAddrTop > iPhysAddrBase, Panic(ECreateInvalidRamBanks));
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Determine how many zones are required and allocate all the
+ // data structures that will be required, permanent one first then
+ // temporary ones to avoid kernel heap fragmentation.
+ ///////////////////////////////////////////////////////////////////////////
+ // Stop any RAM zone callback operations until the initial one has been sent
+ iZoneCallbackInitSent = EFalse;
+ if (aZones)
+ {
+ CountZones(aZones);
+ iZonePowerFunc = aZoneCallback;
+ }
+ else
+ {// maximum number of zone is number of non-coalesced boot banks
+ iNumZones = num_boot_banks;
+ // No zones specified so don't worry about invoking callback function
+ iZonePowerFunc = NULL;
+ }
+
+ // Permenant heap allocation #1 - may be resized if no zones specified
+ __KTRACE_OPT(KMMU,Kern::Printf("iNumZones=%d", iNumZones));
+ iZones = (SZone*)Kern::AllocZ(iNumZones*sizeof(SZone));
+ if (!iZones)
+ {
+ Panic(ECreateNoMemory);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Coalesce contiguous boot banks
+ ///////////////////////////////////////////////////////////////////////////
+ SRamBank* physBanks = (SRamBank*)Kern::Alloc(num_boot_banks*sizeof(SRamBank));
+ if (!physBanks)
+ {
+ Panic(ECreateNoMemory);
+ }
+ SRamBank* coalescedBank = physBanks;
+ const SRamBank* const lastBank = aInfo.iBanks + num_boot_banks;
+ TPhysAddr currentBase = aInfo.iBanks->iBase;
+ TPhysAddr currentEnd = aInfo.iBanks->iBase + aInfo.iBanks->iSize;
+ const SRamBank* nextBank = aInfo.iBanks + 1;
+ for (; nextBank <= lastBank; ++nextBank)
+ {
+ // Create new bank if the next bank isn't contiguous or if
+ // it is the last bank
+ if (nextBank == lastBank || nextBank->iBase != currentEnd)
+ {
+ coalescedBank->iBase = currentBase;
+ coalescedBank->iSize = currentEnd - currentBase;
+ // Mark all the SPageInfos for the pages in this bank as unused.
+ // Needs to be done here to allow SPageInfo::SafeFromPhysAddr to work
+ // which is used by InitSPageInfos()
+ SPageInfo* pi = SPageInfo::FromPhysAddr(coalescedBank->iBase);
+ SPageInfo* piBankEnd = pi + (coalescedBank->iSize >> KPageShift);
+ for (; pi < piBankEnd; pi++)
+ {
+ pi->SetUnused();
+ }
+ ++coalescedBank;
+ __KTRACE_OPT(KMMU, Kern::Printf("Coalesced bank: %08x-%08x", currentBase, currentEnd));
+ currentBase = nextBank->iBase;
+ currentEnd = currentBase + nextBank->iSize;
+ }
+ else
+ {
+ currentEnd += nextBank->iSize;
+ }
+ }
+ TUint num_coalesced_banks = coalescedBank - physBanks;
+ __KTRACE_OPT(KMMU, Kern::Printf("#Coalesced banks: %d", num_coalesced_banks));
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Initialise the SZone objects and mark all the SPageInfos with the index
+ // of zone they are in.
+ //////////////////////////////////////////////////////////////////////////
+ // Assume everything is off so base port will get notification every time the
+ // a new zone is required during the rest of boot process.
+ if (aZones != NULL)
+ {
+ SZone* newZone = iZones; // pointer to zone being created
+
+ // Create and fill zoneAddrOrder with address ordered indices to aZones
+ TUint8* zoneAddrOrder = (TUint8*)Kern::Alloc(iNumZones);
+ if (!zoneAddrOrder)
+ {
+ Panic(ECreateNoMemory);
+ }
+ SortRamZones(aZones, zoneAddrOrder);
+
+ // Now go through each SRamZone in address order initialising the SZone
+ // objects.
+ TUint i = 0;
+ TUint totalZonePages = 0;
+ for (; i < iNumZones; i++)
+ {
+ const SRamZone& ramZone = *(aZones + zoneAddrOrder[i]);
+ newZone->iPhysBase = ramZone.iBase;
+ newZone->iPhysEnd = ramZone.iBase + ramZone.iSize - 1;
+ newZone->iPhysPages = ramZone.iSize >> KPageShift;
+ newZone->iAllocPages[EPageUnknown] = newZone->iPhysPages;
+ newZone->iId = ramZone.iId;
+ newZone->iPref = ramZone.iPref;
+ newZone->iFlags = ramZone.iFlags;
+ totalZonePages += InitSPageInfos(newZone);
+ newZone++;
+ }
+
+ // iZones now points to all the SZone objects stored in address order
+ Kern::Free(zoneAddrOrder);
+ if (totalZonePages != iTotalRamPages)
+ {// The zones don't cover all of the allocatable RAM.
+ Panic(EZonesIncomplete);
+ }
+ }
+ else
+ {
+ iNumZones = num_coalesced_banks;
+ iZones = (SZone*)Kern::ReAlloc((TAny*)iZones, iNumZones*sizeof(SZone));
+ if (iZones == NULL)
+ {
+ Panic(ECreateNoMemory);
+ }
+ // Create a zone for each coalesced boot bank
+ SRamBank* bank = physBanks;
+ SRamBank* bankEnd = physBanks + num_coalesced_banks;
+ SZone* zone = iZones;
+ for (; bank < bankEnd; bank++, zone++)
+ {
+ zone->iPhysBase = bank->iBase;
+ zone->iPhysEnd = bank->iBase + bank->iSize - 1;
+ zone->iPhysPages = bank->iSize >> KPageShift;
+ zone->iAllocPages[EPageUnknown] = zone->iPhysPages;
+ zone->iId = (TUint)bank; // doesn't matter what it is as long as it is unique
+ InitSPageInfos(zone);
+ }
+ }
+ // Delete the coalesced banks as no longer required
+ Kern::Free(physBanks);
+
+ //////////////////////////////////////////////////////////////////////////
+ // Create each zones' bit map allocator now as no temporary heap
+ // cells still allocated at this point.
+ ///////////////////////////////////////////////////////////////////////////
+ const SZone* const endZone = iZones + iNumZones;
+ SZone* zone = iZones;
+ for (; zone < endZone; zone++)
+ {// Create each BMA with all pages allocated as unknown.
+ for (TUint i = 0; i < EPageTypes; i++)
+ {
+ // Only mark the all pages bma and fixed/unknown bma as allocated.
+ TBool notAllocated = (i >= (TUint)EPageMovable);
+ zone->iBma[i] = TBitMapAllocator::New(zone->iPhysPages, notAllocated);
+ if (!zone->iBma[i])
+ {
+ Panic(ECreateNoMemory);
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Unallocate each page in each bank so that it can be allocated when required.
+ // Any page that exists outside a bank will remain allocated as EPageUnknown
+ // and will therefore not be touched by the allocator.
+ //////////////////////////////////////////////////////////////////////////
+ // Temporarily fill preference list so SetPhysicalRamState can succeed
+#ifdef _DEBUG
+ // Block bma verificaitons as bma and alloc counts aren't consistent yet.
+ iAllowBmaVerify = EFalse;
+#endif
+ const SZone* const lastZone = iZones + iNumZones;
+ zone = iZones;
+ for (; zone < lastZone; zone++)
+ {
+ iZonePrefList.Add(&zone->iPrefLink);
+ }
+ const SRamBank* const lastPhysBank = aInfo.iBanks + num_boot_banks;
+ const SRamBank* bank = aInfo.iBanks;
+ for (; bank < lastPhysBank; bank++)
+ {// Free all the pages in this bank.
+ SetPhysicalRamState(bank->iBase, bank->iSize, ETrue, EPageUnknown);
+ }
+#ifdef _DEBUG
+ // Only now is it safe to enable bma verifications
+ iAllowBmaVerify = ETrue;
+#endif
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Sort the zones by preference and create a preference ordered linked list
+ ///////////////////////////////////////////////////////////////////////////
+ zone = iZones;
+ for (; zone < lastZone; zone++)
+ {// clear all the zones from the preference list as not in preference order
+ zone->iPrefLink.Deque();
+ }
+ SZone** prefOrder = (SZone**)Kern::AllocZ(iNumZones * sizeof(SZone*));
+ if (!prefOrder)
+ {
+ Panic(ECreateNoMemory);
+ }
+ zone = iZones;
+ for(; zone < lastZone; zone++)
+ {
+ TInt lowerZones = 0;
+ // Find how many zones that have a lower preference than this one
+ const SZone* zone2 = iZones;
+ for (; zone2 < lastZone; zone2++)
+ {
+ if (zone->iPref > zone2->iPref ||
+ zone->iPref == zone2->iPref && zone->iFreePages > zone2->iFreePages)
+ {
+ lowerZones++;
+ }
+ }
+ while (prefOrder[lowerZones] != 0)
+ {// Zone(s) of this preference and size already exist so
+ // place this one after it/them
+ lowerZones++;
+ }
+ prefOrder[lowerZones] = zone;
+ }
+ // Fill preference ordered linked list
+ SZone** const lastPref = prefOrder + iNumZones;
+ SZone** prefZone = prefOrder;
+ TUint prefRank = 0;
+ for (; prefZone < lastPref; prefZone++, prefRank++)
+ {
+ SZone& zone = **prefZone;
+ iZonePrefList.Add(&zone.iPrefLink);
+ zone.iPrefRank = prefRank;
+ }
+ Kern::Free(prefOrder); // Remove temporary allocation
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Now mark any regions reserved by the base port as allocated and not
+ // for use by the RAM allocator.
+ ///////////////////////////////////////////////////////////////////////////
+ const SRamBank* pB = lastBank + 1; // first reserved block specifier
+ for (; pB->iSize; ++pB)
+ {
+ __KTRACE_OPT(KMMU, Kern::Printf("Reserve physical block %08x+%x", pB->iBase, pB->iSize));
+ TInt r = SetPhysicalRamState(pB->iBase, pB->iSize, EFalse, EPageFixed);
+ __KTRACE_OPT(KMMU, Kern::Printf("Reserve returns %d", r));
+ if (r!=KErrNone)
+ {
+ Panic(ECreateInvalidReserveBank);
+ }
+#ifdef BTRACE_KERNEL_MEMORY
+ BTrace8(BTrace::EKernelMemory, BTrace::EKernelMemoryDrvPhysAlloc, pB->iSize, pB->iBase);
+ Epoc::DriverAllocdPhysRam += pB->iSize;
+#endif
+#ifndef __MEMMODEL_FLEXIBLE__ // Mmu::Init2Common() handles this in FMM.
+ // Synchronise the SPageInfo with any blocks that were reserved by
+ // marking any reserved regions as locked
+ TPhysAddr physAddrEnd = pB->iBase + pB->iSize;
+ TPhysAddr physAddr = pB->iBase;
+ for(; physAddr < physAddrEnd; physAddr += KPageSize)
+ {
+ SPageInfo* pi = SPageInfo::FromPhysAddr(physAddr);
+ pi->Lock();
+ }
+#endif
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Now that we have have the RAM zone preference list and know how many
+ // allocatable pages there are, set iZoneLeastMovDis to be the RAM zone
+ // that will be used when half of the RAM is in use. This a boot up
+ // optimisation to reduce the amount of moving and/or discarding fixed page
+ // allocations will have to make during boot.
+ //////////////////////////////////////////////////////////////////////////
+ TUint halfAllocatablePages = iTotalFreeRamPages >> 1;
+ TUint pages = 0;
+ SDblQueLink* link = &iZonePrefList.iA;
+ do
+ {
+ link = link->iNext;
+ __NK_ASSERT_DEBUG(link != &iZonePrefList.iA);
+ SZone& zonePages = *_LOFF(link, SZone, iPrefLink);
+ pages += zonePages.iFreePages;
+ }
+ while(pages < halfAllocatablePages);
+ iZoneLeastMovDis = link;
+ iZoneLeastMovDisRank = _LOFF(link, SZone, iPrefLink)->iPrefRank;
+
+ // Reset general defrag links.
+ iZoneGeneralPrefLink = NULL;
+ iZoneGeneralTmpLink = NULL;
+
+ __KTRACE_OPT(KMMU,DebugDump());
+ }
+
+
+void DRamAllocator::MarkPagesAllocated(TPhysAddr aAddr, TInt aCount, TZonePageType aType)
+ {
+ __KTRACE_OPT(KMMU,Kern::Printf("DRamAllocator::MarkPagesAllocated(%x+%x)",aAddr,aCount));
+
+ M::RamAllocIsLocked();
+
+ // Don't allow unknown pages to be allocated, saves extra 'if' when
+ // creating bmaType.
+ __NK_ASSERT_DEBUG(aType != EPageUnknown);
+
+ __ASSERT_DEBUG( !(TUint32(aAddr) & (KPageSize - 1)) &&
+ (TUint32(aAddr) < TUint32(iPhysAddrTop)) &&
+ (TUint32(aAddr) >= TUint32(iPhysAddrBase))&&
+ (TUint32((aCount << KPageShift) -1 + aAddr) <= TUint32(iPhysAddrTop)),
+ Panic(EDoMarkPagesAllocated1));
+
+ iTotalFreeRamPages-=aCount;
+ // Find the 1st zone the 1st set of allocations belong to
+ TInt offset = 0;
+ SZone* pZ = GetZoneAndOffset(aAddr,offset);
+ if (pZ == NULL)
+ {//aAddr not in RAM
+ Panic(EDoMarkPagesAllocated1);
+ }
+ while(aCount)
+ {
+ TBitMapAllocator& bmaAll = *(pZ->iBma[KBmaAllPages]);
+ TBitMapAllocator& bmaType = *(pZ->iBma[aType]);
+ TInt count = Min(bmaAll.iSize - offset, aCount);
+ bmaAll.Alloc(offset, count);
+ bmaType.Alloc(offset, count);
+ ZoneAllocPages(pZ, count, aType);
+ aCount -= count;
+
+ // If spanning zones then ensure the next zone is contiguous.
+ __ASSERT_DEBUG(!aCount || ((pZ + 1)->iPhysBase != 0 && ((pZ + 1)->iPhysBase - 1) == pZ->iPhysEnd), Panic(EDoMarkPagesAllocated1));
+
+ pZ++; // zones in physical address order so move to next one
+ offset = 0; // and reset offset to start of the zone
+ }
+ }
+
+TInt DRamAllocator::MarkPageAllocated(TPhysAddr aAddr, TZonePageType aType)
+ {
+ __KTRACE_OPT(KMMU,Kern::Printf("DRamAllocator::MarkPageAllocated %08x",aAddr));
+
+ M::RamAllocIsLocked();
+
+ // Don't allow unknown pages to be allocated, saves extra 'if' when
+ // creating bmaType.
+ __NK_ASSERT_DEBUG(aType != EPageUnknown);
+
+ TInt n;
+ SZone* z=GetZoneAndOffset(aAddr,n);
+ if (!z)
+ {
+ return KErrArgument;
+ }
+ __KTRACE_OPT(KMMU2,Kern::Printf("Zone index %d page index %04x",z-iZones,n));
+ TBitMapAllocator& bmaAll = *(z->iBma[KBmaAllPages]);
+ TBitMapAllocator& bmaType = *(z->iBma[aType]);
+ if (bmaAll.NotFree(n,1))
+ {
+ __KTRACE_OPT(KMMU,Kern::Printf("Page already allocated"));
+ return KErrAlreadyExists; // page is already allocated
+ }
+ bmaAll.Alloc(n,1);
+ bmaType.Alloc(n,1);
+ --iTotalFreeRamPages;
+ ZoneAllocPages(z, 1, aType);
+ __KTRACE_OPT(KMMU,Kern::Printf("Total free RAM pages now = %d",iTotalFreeRamPages));
+
+#ifdef BTRACE_RAM_ALLOCATOR
+ BTrace8(BTrace::ERamAllocator, BTrace::ERamAllocMarkAllocated, aType, aAddr);
+#endif
+ return KErrNone;
+ }
+
+TInt DRamAllocator::FreeRamPage(TPhysAddr aAddr, TZonePageType aType)
+ {
+ __KTRACE_OPT(KMMU,Kern::Printf("FreeRamPage %08x",aAddr));
+
+ M::RamAllocIsLocked();
+
+#ifdef _DEBUG
+#ifndef __MEMMODEL_FLEXIBLE__
+ // Check lock counter of the page
+ if (aAddr != KPhysAddrInvalid)
+ {
+ SPageInfo* pi = SPageInfo::SafeFromPhysAddr(aAddr);
+ if(pi && pi->LockCount())
+ Panic(EFreeingLockedPage);
+ }
+#endif
+ // Don't allow unknown pages to be freed, saves extra 'if' when
+ // creating bmaType.
+ __NK_ASSERT_DEBUG(aType != EPageUnknown);
+#endif
+
+ TInt n;
+ SZone* z=GetZoneAndOffset(aAddr,n);
+ if (!z)
+ {
+ return KErrArgument;
+ }
+ __KTRACE_OPT(KMMU2,Kern::Printf("Zone index %d page index %04x",z-iZones,n));
+ TBitMapAllocator& bmaAll = *(z->iBma[KBmaAllPages]);
+ TBitMapAllocator& bmaType = *(z->iBma[aType]);
+ bmaAll.Free(n);
+ bmaType.Free(n);
+ ++iTotalFreeRamPages;
+ ZoneFreePages(z, 1, aType);
+
+#ifdef BTRACE_RAM_ALLOCATOR
+ BTrace8(BTrace::ERamAllocator, BTrace::ERamAllocFreePage, aType, aAddr);
+#endif
+ return KErrNone;
+ }
+
+void DRamAllocator::FreeRamPages(TPhysAddr* aPageList, TInt aNumPages, TZonePageType aType)
+ {
+ __KTRACE_OPT(KMMU,Kern::Printf("FreeRamPages count=%08x",aNumPages));
+
+ M::RamAllocIsLocked();
+
+#if defined(_DEBUG) && !defined(__MEMMODEL_FLEXIBLE__)
+ // Check lock counter for each page that is about to be freed.
+ TInt pageNum = aNumPages;
+ TPhysAddr* pageList = aPageList;
+ while (pageNum--)
+ {
+ TPhysAddr pa = *pageList++;
+ if (pa == KPhysAddrInvalid)
+ continue;
+ SPageInfo* pi = SPageInfo::SafeFromPhysAddr(pa);
+ if(pi && pi->LockCount())
+ Panic(EFreeingLockedPage);
+ }
+#endif
+
+ while(aNumPages--)
+ {
+ TPhysAddr first_pa = *aPageList++;
+ if (first_pa == KPhysAddrInvalid)
+ {
+ continue;
+ }
+ TInt ix;
+ SZone* z = GetZoneAndOffset(first_pa,ix);
+ if (!z)
+ {
+ continue;
+ }
+ TBitMapAllocator& bmaAll = *(z->iBma[KBmaAllPages]);
+ TInt zp_rem = bmaAll.iSize - ix;
+ __KTRACE_OPT(KMMU,Kern::Printf("1st PA=%08x Zone %d index %04x",first_pa,z-iZones,ix));
+ TInt n = 1;
+ TPhysAddr pa = first_pa + KPageSize;
+ while (--zp_rem && aNumPages && *aPageList==pa)
+ {
+ ++n;
+ --aNumPages;
+ ++aPageList;
+ pa += KPageSize;
+ }
+ __KTRACE_OPT(KMMU2,Kern::Printf("%d consecutive pages, zp_rem=%x, %d remaining pages",n,zp_rem,aNumPages));
+ bmaAll.Free(ix,n);
+ TBitMapAllocator& bmaType = *(z->iBma[aType]);
+ bmaType.Free(ix,n);
+ iTotalFreeRamPages += n;
+ ZoneFreePages(z, n, aType);
+#ifdef BTRACE_RAM_ALLOCATOR
+ BTrace12(BTrace::ERamAllocator, BTrace::ERamAllocFreePages, aType, n, first_pa);
+#endif
+ }
+#ifdef BTRACE_RAM_ALLOCATOR
+ BTrace0(BTrace::ERamAllocator, BTrace::ERamAllocFreePagesEnd);
+#endif
+ }
+
+/**
+ Attempt to clear upto the required amount of discardable or movable pages
+ from the RAM zone.
+
+ @param aZone The RAM zone to clear.
+ @param aRequiredPages The maximum number of pages to clear.
+*/
+void DRamAllocator::ZoneClearPages(SZone& aZone, TUint aRequiredPages)
+ {
+ __KTRACE_OPT(KMMU,
+ Kern::Printf("ZoneClearPages: ID 0x%x, req 0x%x", aZone.iId, aRequiredPages));
+ // Discard the required number of discardable pages.
+ TUint offset = 0;
+ TInt r = NextAllocatedPage(&aZone, offset, EPageDiscard);
+ while (r == KErrNone && aRequiredPages)
+ {
+ TPhysAddr physAddr = (offset << KPageShift) + aZone.iPhysBase;
+ TInt discarded = M::DiscardPage(physAddr, aZone.iId, EFalse);
+ if (discarded == KErrNone)
+ {// The page was successfully discarded.
+ aRequiredPages--;
+ }
+ offset++;
+ r = NextAllocatedPage(&aZone, offset, EPageDiscard);
+ }
+ // Move the required number of movable pages.
+ offset = 0;
+ r = NextAllocatedPage(&aZone, offset, EPageMovable);
+ while(r == KErrNone && aRequiredPages)
+ {
+ TPhysAddr physAddr = (offset << KPageShift) + aZone.iPhysBase;
+ TPhysAddr newAddr = KPhysAddrInvalid;
+ if (M::MovePage(physAddr, newAddr, aZone.iId, EFalse) == KErrNone)
+ {// The page was successfully moved.
+#ifdef _DEBUG
+ TInt newOffset = 0;
+ SZone* newZone = GetZoneAndOffset(newAddr, newOffset);
+ __NK_ASSERT_DEBUG(newZone != &aZone);
+#endif
+ aRequiredPages--;
+ }
+ offset++;
+ r = NextAllocatedPage(&aZone, offset, EPageMovable);
+ }
+ }
+
+/** Attempt to allocate pages into a particular zone. Pages will not
+ always be contiguous.
+
+ @param aPageList On return it will contain the addresses of any allocated pages
+ @param aZone The zone to allocate from
+ @param aNumPages The number of pages to allocate
+ @param aType The type of pages to allocate
+ @return The number of pages that were allocated
+*/
+TUint32 DRamAllocator::ZoneFindPages(TPhysAddr*& aPageList, SZone& aZone, TUint32 aNumPages, TZonePageType aType)
+ {
+ // Don't allow unknown pages to be allocated, saves extra 'if' when
+ // creating bmaType.
+ __NK_ASSERT_DEBUG(aType != EPageUnknown);
+
+ TBitMapAllocator& bmaAll = *aZone.iBma[KBmaAllPages];
+ TBitMapAllocator& bmaType = *(aZone.iBma[aType]);
+ TPhysAddr zpb = aZone.iPhysBase;
+ TInt got = bmaAll.AllocList(aNumPages, (TInt*)aPageList);
+ if (got)
+ {
+ TPhysAddr* pE = aPageList + got;
+ while(aPageList < pE)
+ {
+ TInt ix = *aPageList;
+ *aPageList++ = zpb + (ix << KPageShift);
+ __KTRACE_OPT(KMMU,Kern::Printf("Got page @%08x",zpb + (ix << KPageShift)));
+
+ // Mark the page allocated on the page type bit map.
+ bmaType.Alloc(ix, 1);
+ }
+ ZoneAllocPages(&aZone, got, aType);
+#ifdef BTRACE_RAM_ALLOCATOR
+ BTrace12(BTrace::ERamAllocator, BTrace::ERamAllocRamPages, aType, got, *(pE-got));
+#endif
+ }
+ return got;
+ }
+
+/**
+Allocate discontiguous pages.
+
+Fixed pages are always allocated into the most preferable RAM zone that has free,
+movable or discardable pages in it. This is to avoid fixed pages being placed
+in the less preferred RAM zones.
+
+Movable and discardable pages are allocated into the RAM zones currently in use.
+An empty RAM zone will only be used (switched on) if there are not enough free
+pages in the in use RAM zones. The pages will be allocated from the least
+preferable RAM to be in use after the allocation to the more preferred RAM zones.
+
+If a valid zone is specified in aBlockedZoneId then that RAM zone will not be
+allocated into. Also, if aBlockedZoneId and aBlockRest is set then the allocation
+will stop if aBlockZoneId
+
+@param aPageList On success, will contain the address of each allocated page
+@param aNumPages The number of the pages to allocate
+@param aType The type of the pages to allocate
+@param aBlockedZoneId The ID of the RAM zone that shouldn't be allocated into.
+ The default value has no effect.
+@param aBlockRest Set to ETrue to stop this allocation using any currently empty
+ RAM zones, EFalse to allow empty RAM zones to be used. Only
+ effects movable and discardable allocations.
+
+@return 0 on success, the number of extra pages required to fulfill the request on failure.
+*/
+TInt DRamAllocator::AllocRamPages(TPhysAddr* aPageList, TInt aNumPages, TZonePageType aType, TUint aBlockedZoneId, TBool aBlockRest)
+ {
+ __KTRACE_OPT(KMMU,Kern::Printf("AllocRamPages 0x%x type%d",aNumPages, aType));
+
+ M::RamAllocIsLocked();
+
+ // Should never allocate unknown pages.
+ __NK_ASSERT_DEBUG(aType != EPageUnknown);
+
+ TPhysAddr* pageListBase = aPageList;
+ TUint32 numMissing = aNumPages;
+
+ if (aType == EPageFixed)
+ {// Currently only a general defrag operation should set this and it won't
+ // allocate fixed pages.
+ __NK_ASSERT_DEBUG(!aBlockRest);
+ if ((TUint)aNumPages > iTotalFreeRamPages + M::NumberOfFreeDpPages())
+ {// Not enough free space and not enough freeable pages.
+ goto exit;
+ }
+
+ // Search through each zone in preference order until all pages allocated or
+ // have reached the end of the preference list
+ SDblQueLink* link = iZonePrefList.First();
+ while (numMissing && link != &iZonePrefList.iA)
+ {
+ SZone& zone = *_LOFF(link, SZone, iPrefLink);
+ // Get the link to next zone before any potential reordering.
+ // Which would occur if previous zone is same preference and has
+ // more free space after this allocation.
+ link = link->iNext;
+
+ if (zone.iId == aBlockedZoneId || NoAllocOfPageType(zone, aType))
+ {// The flags disallow aType pages or all pages.
+ __KTRACE_OPT(KMMU2, Kern::Printf("ARP Flags 0x%08x", zone.iFlags));
+ continue;
+ }
+
+ numMissing -= ZoneFindPages(aPageList, zone, numMissing, aType);
+ __KTRACE_OPT(KMMU, Kern::Printf("zone.iId 0x%x", zone.iId));
+
+ if (numMissing &&
+ (zone.iAllocPages[EPageMovable] || zone.iAllocPages[EPageDiscard]))
+ {// Not all the required pages where allocated and there are still some
+ // movable and discardable pages in this RAM zone.
+ ZoneClearPages(zone, numMissing);
+
+ // Have discarded and moved everything required or possible so
+ // now allocate into the pages just freed.
+ numMissing -= ZoneFindPages(aPageList, zone, numMissing, aType);
+ }
+ }
+ }
+ else
+ {
+ if ((TUint)aNumPages > iTotalFreeRamPages)
+ {// Not enough free pages to fulfill this request so return amount required
+ return aNumPages - iTotalFreeRamPages;
+ }
+
+ // Determine if there are enough free pages in the RAM zones in use.
+ TUint totalFreeInUse = 0;
+ SDblQueLink* link = iZoneLeastMovDis;
+ for(; link != &iZonePrefList.iA; link = link->iPrev)
+ {
+ SZone& zone = *_LOFF(link, SZone, iPrefLink);
+ if (zone.iId == aBlockedZoneId || NoAllocOfPageType(zone, aType) ||
+ (aBlockRest && (zone.iFlags & KRamZoneFlagGenDefragBlock)))
+ {// The blocked RAM zone or flags disallow aType pages or all pages
+ __KTRACE_OPT(KMMU2, Kern::Printf("ARP Flags 0x%08x", zone.iFlags));
+ continue;
+ }
+ totalFreeInUse += zone.iFreePages;
+ }
+
+ if (aBlockRest && totalFreeInUse < (TUint)aNumPages)
+ {// Allocating as part of a general defragmentation and
+ // can't allocate without using a RAM zone less preferable than
+ // the current least prefeable RAM zone with movable and/or
+ //discardable.
+ __NK_ASSERT_DEBUG(numMissing);
+ goto exit;
+ }
+
+ SDblQueLink* leastClearable = iZoneLeastMovDis;
+ while (totalFreeInUse < (TUint)aNumPages)
+ {// The amount of free pages in the RAM zones with movable
+ // and/or discardable isn't enough.
+ leastClearable = leastClearable->iNext;
+ if (leastClearable == &iZonePrefList.iA)
+ {// There are no more RAM zones to allocate into.
+ __NK_ASSERT_DEBUG(numMissing);
+ goto exit;
+ }
+ SZone& zone = *_LOFF(leastClearable, SZone, iPrefLink);
+ if (zone.iId == aBlockedZoneId || NoAllocOfPageType(zone, aType))
+ {// The flags disallow aType pages or all pages
+ __KTRACE_OPT(KMMU2, Kern::Printf("ARP Flags 0x%08x", zone.iFlags));
+ continue;
+ }
+ totalFreeInUse += zone.iFreePages;
+ }
+ // Now that we know exactly how many RAM zones will be required do
+ // the allocation. To reduce fixed allocations having to clear RAM
+ // zones, allocate from the least preferable RAM to be used
+ // to the most preferable RAM zone.
+ link = leastClearable;
+ while (numMissing)
+ {
+ __NK_ASSERT_DEBUG(link != &iZonePrefList.iA);
+ SZone& zone = *_LOFF(link, SZone, iPrefLink);
+ // Update the link before any reordering so we don't miss a RAM zone.
+ link = link->iPrev;
+
+ if (zone.iId == aBlockedZoneId || NoAllocOfPageType(zone, aType) ||
+ (aBlockRest && (zone.iFlags & KRamZoneFlagGenDefragBlock)))
+ {// The blocked RAM zone or flags disallow aType pages or all pages
+ __KTRACE_OPT(KMMU2, Kern::Printf("ARP Flags 0x%08x", zone.iFlags));
+ continue;
+ }
+
+ numMissing -= ZoneFindPages(aPageList, zone, numMissing, aType);
+ __KTRACE_OPT(KMMU, Kern::Printf("zone.iId 0x%x", zone.iId));
+ }
+ __NK_ASSERT_DEBUG(!numMissing);
+ }
+
+exit:
+ // Update here so any call to FreeRamPages doesn't upset count
+ aNumPages -= numMissing; //set to number of pages that are allocated
+ iTotalFreeRamPages -= aNumPages;
+
+ if (numMissing)
+ {// Couldn't allocate all required pages so free those that were allocated
+ FreeRamPages(pageListBase, aNumPages, aType);
+ }
+#ifdef BTRACE_RAM_ALLOCATOR
+ else
+ {
+ BTrace0(BTrace::ERamAllocator, BTrace::ERamAllocRamPagesEnd);
+ }
+#endif
+ return numMissing;
+ }
+
+
+/**
+Attempt to allocate discontiguous pages from the specified RAM zone.
+
+NOTE - This method only obeys the KRamZoneFlagNoAlloc and KRamZoneFlagClaiming
+flags and not the others.
+But as currently only EFixed pages will be allocated using this method that is
+the desired behaviour.
+
+@param aZoneIdList An array of the IDs of the RAM zones to allocate from.
+@param aZoneIdCount The number of IDs in aZoneIdList.
+@param aPageList On success, will contain the address of each allocated page.
+@param aNumPages The number of the pages to allocate.
+@param aType The type of the pages to allocate.
+
+@return KErrNone on success, KErrNoMemory if allocation couldn't succeed or
+the RAM zone has the KRamZoneFlagNoAlloc flag set, KErrArgument if a zone of
+aZoneIdList doesn't exist or aNumPages is greater than the total pages in the zone.
+*/
+TInt DRamAllocator::ZoneAllocRamPages(TUint* aZoneIdList, TUint aZoneIdCount, TPhysAddr* aPageList, TInt aNumPages, TZonePageType aType)
+ {
+ M::RamAllocIsLocked();
+ __NK_ASSERT_DEBUG(aType == EPageFixed);
+
+
+ __KTRACE_OPT(KMMU,Kern::Printf("ZoneAllocRamPages 0x%x zones 0x%x",aNumPages, aZoneIdCount));
+
+ TInt r = KErrNone;
+ TUint* zoneIdPtr = aZoneIdList;
+ TUint* zoneIdEnd = zoneIdPtr + aZoneIdCount;
+ TUint numMissing = aNumPages;
+ TUint physicalPages = 0;
+ TPhysAddr* pageListBase = aPageList;
+
+ // Always loop through all the RAM zones so that if an invalid ID is specified
+ // it is always detected whether all the specified RAM zones were required
+ // for the allocation or not.
+ for(; zoneIdPtr < zoneIdEnd; zoneIdPtr++)
+ {
+ SZone* zone = ZoneFromId(*zoneIdPtr);
+
+ if (zone == NULL)
+ {// Invalid zone ID.
+ r = KErrArgument;
+ break;
+ }
+
+ physicalPages += zone->iPhysPages;
+
+ if (zone->iFlags & (KRamZoneFlagNoAlloc|KRamZoneFlagClaiming))
+ {// If this RAM zone can't be allocated into then skip it.
+ continue;
+ }
+
+ numMissing -= ZoneFindPages(aPageList, *zone, numMissing, aType);
+
+ if (numMissing && aType == EPageFixed)
+ {// Remove up to required number of pages from the RAM zone
+ // and reattempt the allocation.
+ ZoneClearPages(*zone, numMissing);
+ numMissing -= ZoneFindPages(aPageList, *zone, numMissing, aType);
+ }
+ }
+
+ // Update iTotalFreeRamPages here so that if allocation doesn't succeed then
+ // FreeRamPages() will keep it consistent.
+ TUint numAllocated = aNumPages - numMissing;
+ iTotalFreeRamPages -= numAllocated;
+
+ if (r == KErrArgument || physicalPages < (TUint)aNumPages)
+ {// Invalid zone ID or the number of pages requested is too large.
+ // This should fail regardless of whether the allocation failed or not.
+ FreeRamPages(pageListBase, numAllocated, aType);
+ return KErrArgument;
+ }
+
+ if (numMissing)
+ {// Couldn't allocate all required pages so free those that were allocated
+ FreeRamPages(pageListBase, numAllocated, aType);
+ return KErrNoMemory;
+ }
+
+ // Have allocated all the required pages.
+#ifdef BTRACE_RAM_ALLOCATOR
+ BTrace0(BTrace::ERamAllocator, BTrace::ERamAllocZoneRamPagesEnd);
+#endif
+ return KErrNone;
+ }
+
+
+/**
+Will return zones one at a time in the following search patterns until a suitable
+zone has been found or it is determined that there is no suitable zone:
+ - preference order
+ - address order
+Before the first call for a new search sequence must set:
+ iZoneTmpAddrIndex = -1;
+ iZoneTmpPrefLink = iZonePrefList.First();
+
+@param aZone On return this will be a pointer to the next zone to search.
+@param aState The current search state, i.e. which of the zone orderings to follow.
+It will be updated if necessary by this function.
+@param aType The type of page to be allocated.
+@param aBlockedZoneId The ID of a RAM zone to not allocate into.
+@param aBlockRest ETrue if allocation should fail as soon as a blocked zone is reached,
+EFalse otherwise. (Currently not used)
+@return ETrue a sutiable zone is found, EFalse when the allocation is not possible.
+*/
+TBool DRamAllocator::NextAllocZone(SZone*& aZone, TZoneSearchState& aState, TZonePageType aType, TUint aBlockedZoneId, TBool aBlockRest)
+ {
+ TUint currentState = aState;
+ TBool r = EFalse;
+
+ for (; currentState < EZoneSearchEnd; currentState++)
+ {
+ if (currentState == EZoneSearchAddr)
+ {
+ iZoneTmpAddrIndex++;
+ for (; iZoneTmpAddrIndex < (TInt)iNumZones; iZoneTmpAddrIndex++)
+ {
+ aZone = iZones + iZoneTmpAddrIndex;
+ if (aBlockedZoneId != aZone->iId && !NoAllocOfPageType(*aZone, aType))
+ {
+ r = ETrue;
+ goto exit;
+ }
+ }
+ }
+ else
+ {
+ while(iZoneTmpPrefLink != &iZonePrefList.iA)
+ {
+ aZone = _LOFF(iZoneTmpPrefLink, SZone, iPrefLink);
+ iZoneTmpPrefLink = iZoneTmpPrefLink->iNext; // Update before any re-ordering
+ if (aBlockedZoneId != aZone->iId && !NoAllocOfPageType(*aZone, aType))
+ {
+ r = ETrue;
+ goto exit;
+ }
+ }
+ }
+ }
+exit:
+ __NK_ASSERT_DEBUG((r && currentState < EZoneSearchEnd) || (!r && currentState == EZoneSearchEnd));
+
+ aState = (TZoneSearchState)currentState;
+ return r;
+ }
+
+/**
+Search through the zones for the requested contiguous RAM, first in preference
+order then, if that fails, in address order.
+
+@param aNumPages The number of contiguous pages to find
+@param aPhysAddr Will contain the base address of any contiguous run if found
+@param aType The page type of the memory to be allocated
+@param aAlign Alignment specified as the alignment shift
+@param aBlockedZoneId The ID of a zone that can't be allocated into, by default this has no effect
+@param aBlockRest Set to ETrue to stop allocation as soon as aBlockedZoneId is reached
+in preference ordering. EFalse otherwise.
+
+@return KErrNone on success, KErrNoMemory otherwise
+*/
+TInt DRamAllocator::AllocContiguousRam(TUint aNumPages, TPhysAddr& aPhysAddr, TZonePageType aType, TInt aAlign, TUint aBlockedZoneId, TBool aBlockRest)
+ {
+ __KTRACE_OPT(KMMU,Kern::Printf("AllocContiguousRam size %08x align %d",aNumPages,aAlign));
+
+ M::RamAllocIsLocked();
+
+ // No support for non-fixed pages as this will discard and move
+ // pages if required.
+ __NK_ASSERT_DEBUG(aType == EPageFixed);
+ TInt alignWrtPage = Max(aAlign - KPageShift, 0);
+ TUint32 alignmask = (1u << alignWrtPage) - 1;
+
+ // Attempt to find enough pages searching in preference order first then
+ // in address order
+ TZoneSearchState searchState = EZoneSearchPref;
+ SZone* zone;
+ SZone* prevZone = NULL;
+ TInt carryAll = 0; // Carry for all pages bma, clear to start new run.
+ TInt carryImmov = 0; // Carry for immovable pages bma, clear to start new run.
+ TInt base = 0;
+ TInt offset = 0;
+ iZoneTmpAddrIndex = -1;
+ iZoneTmpPrefLink = iZonePrefList.First();
+ while (NextAllocZone(zone, searchState, aType, aBlockedZoneId, aBlockRest))
+ {
+ // Be sure to start from scratch if zone not contiguous with previous zone
+ if (prevZone && (zone->iPhysBase == 0 || (zone->iPhysBase - 1) != prevZone->iPhysEnd))
+ {
+ carryAll = 0;
+ carryImmov = 0;
+ }
+ prevZone = zone;
+ TBitMapAllocator& bmaAll = *(zone->iBma[KBmaAllPages]);
+ base = TInt(zone->iPhysBase >> KPageShift);
+ TInt runLength;
+ __KTRACE_OPT(KMMU,Kern::Printf("AllocAligned: base=%08x carryAll=%08x offset=%08x", base, carryAll, offset));
+ offset = bmaAll.AllocAligned(aNumPages, alignWrtPage, base, EFalse, carryAll, runLength);
+ __KTRACE_OPT(KMMU,Kern::Printf("AllocAligned: offset=%08x", offset));
+
+ if (offset >= 0)
+ {// Have found enough contiguous pages so return address of physical page
+ // at the start of the region
+ aPhysAddr = TPhysAddr((base + offset - carryAll + alignmask) & ~alignmask) << KPageShift;
+ MarkPagesAllocated(aPhysAddr, aNumPages, aType);
+
+ __KTRACE_OPT(KMMU,Kern::Printf("AllocContiguousRam returns %08x",aPhysAddr));
+#ifdef BTRACE_RAM_ALLOCATOR
+ BTrace12(BTrace::ERamAllocator, BTrace::ERamAllocContiguousRam, aType, aNumPages, aPhysAddr);
+#endif
+ return KErrNone;
+ }
+ else
+ {// No run found when looking in just the free pages so see if this
+ // RAM zone could be used if pages where moved or discarded.
+ if (aNumPages > KMaxFreeableContiguousPages)
+ {// Can't move or discard any pages so move on to next RAM zone
+ // taking any run at the end of this RAM zone into account.
+ carryImmov = 0;
+ continue;
+ }
+ TBitMapAllocator& bmaImmov = *(zone->iBma[EPageFixed]);
+ offset = 0; // Clear so searches whole of fixed BMA on the first pass.
+ do
+ {
+ __KTRACE_OPT(KMMU,Kern::Printf("AllocAligned: base=%08x carryImmov=%08x offset=%08x", base, carryImmov, offset));
+ offset = bmaImmov.AllocAligned(aNumPages, alignWrtPage, base, EFalse, carryImmov, runLength, offset);
+ __KTRACE_OPT(KMMU,Kern::Printf("AllocAligned: offset=%08x", offset));
+ if (offset >= 0)
+ {// Have found a run in immovable page bma so attempt to clear
+ // it for the allocation.
+ TPhysAddr addrBase = TPhysAddr((base + offset - carryImmov + alignmask) & ~alignmask) << KPageShift;
+ TPhysAddr addrEnd = addrBase + (aNumPages << KPageShift);
+
+ // Block the RAM zones containing the contiguous region
+ // from being allocated into when pages are moved or replaced.
+ TPhysAddr addr = addrBase;
+ TInt tmpOffset;
+ SZone* tmpZone = GetZoneAndOffset(addr, tmpOffset);
+ while (addr < addrEnd-1)
+ {
+ tmpZone->iFlags |= KRamZoneFlagTmpBlockAlloc;
+ addr = tmpZone->iPhysEnd;
+ tmpZone++;
+ }
+
+ addr = addrBase;
+ TInt contigOffset = 0;
+ SZone* contigZone = GetZoneAndOffset(addr, contigOffset);
+ for (; addr != addrEnd; addr += KPageSize, contigOffset++)
+ {
+ if (contigZone->iPhysEnd < addr)
+ {
+ contigZone = GetZoneAndOffset(addr, contigOffset);
+ __NK_ASSERT_DEBUG(contigZone != NULL);
+ }
+#ifdef _DEBUG // This page shouldn't be allocated as fixed, only movable or discardable.
+ __NK_ASSERT_DEBUG(contigZone != NULL);
+ __NK_ASSERT_DEBUG(contigZone->iBma[EPageFixed]->NotAllocated(contigOffset, 1));
+ SPageInfo* pageInfo = SPageInfo::SafeFromPhysAddr(addr);
+ __NK_ASSERT_DEBUG(pageInfo != NULL);
+#endif
+ TPhysAddr newAddr;
+ TInt moveRet = M::MovePage(addr, newAddr, contigZone->iId, EFalse);
+ if (moveRet != KErrNone && moveRet != KErrNotFound)
+ {// This page couldn't be moved or discarded so
+ // restart the search the page after this one.
+ __KTRACE_OPT(KMMU2,
+ Kern::Printf("ContigMov fail offset %x moveRet %d addr %x carryImmov %x",
+ offset, moveRet, addr, carryImmov));
+ // Can't rely on RAM zone preference ordering being
+ // the same so clear carrys and restart search from
+ // within the current RAM zone or skip onto the next
+ // one if at the end of this one.
+ carryImmov = 0;
+ carryAll = 0;
+ offset = (addr < zone->iPhysBase)? 0 : contigOffset + 1;
+ __KTRACE_OPT(KMMU2, Kern::Printf("ContigMov fail offset %x", offset));
+ break;
+ }
+ }
+ // Unblock the RAM zones containing the contiguous region.
+ TPhysAddr flagAddr = addrBase;
+ tmpZone = GetZoneAndOffset(flagAddr, tmpOffset);
+ while (flagAddr < addrEnd-1)
+ {
+ tmpZone->iFlags &= ~KRamZoneFlagTmpBlockAlloc;
+ flagAddr = tmpZone->iPhysEnd;
+ tmpZone++;
+ }
+
+ if (addr == addrEnd)
+ {// Cleared all the required pages so allocate them.
+ // Return address of physical page at the start of the region.
+ aPhysAddr = addrBase;
+ MarkPagesAllocated(aPhysAddr, aNumPages, aType);
+
+ __KTRACE_OPT(KMMU,Kern::Printf("AllocContiguousRam returns %08x",aPhysAddr));
+#ifdef BTRACE_RAM_ALLOCATOR
+ BTrace12(BTrace::ERamAllocator, BTrace::ERamAllocContiguousRam, aType, aNumPages, aPhysAddr);
+#endif
+ return KErrNone;
+ }
+ }
+ }
+ // Keep searching immovable page bma of the current RAM zone until
+ // gone past end of RAM zone or no run can be found.
+ while (offset >= 0 && (TUint)offset < zone->iPhysPages);
+ }
+ }
+ return KErrNoMemory;
+ }
+
+
+/**
+Attempt to allocate the contiguous RAM from the specified zone.
+
+NOTE - This method only obeys the KRamZoneFlagNoAlloc and KRamZoneFlagClaiming
+flags and not the others.
+But as currently only EFixed pages will be allocated using this method that is
+the desired behaviour.
+
+@param aZoneIdList An array of the IDs of the RAM zones to allocate from.
+@param aZoneIdCount The number of the IDs listed by aZoneIdList.
+@param aSize The number of contiguous bytes to find
+@param aPhysAddr Will contain the base address of the contiguous run if found
+@param aType The page type of the memory to be allocated
+@param aAlign Alignment specified as the alignment shift
+
+@return KErrNone on success, KErrNoMemory if allocation couldn't succeed or
+the RAM zone has the KRamZoneFlagNoAlloc flag set. KErrArgument if a zone of
+aZoneIdList exists or if aSize is larger than the size of the zone.
+*/
+TInt DRamAllocator::ZoneAllocContiguousRam(TUint* aZoneIdList, TUint aZoneIdCount, TInt aSize, TPhysAddr& aPhysAddr, TZonePageType aType, TInt aAlign)
+ {
+ __KTRACE_OPT(KMMU,Kern::Printf("ZoneAllocContiguousRam zones 0x%x size 0x%08x align %d",aZoneIdCount, aSize, aAlign));
+
+ M::RamAllocIsLocked();
+ __NK_ASSERT_DEBUG(aType == EPageFixed);
+
+
+ TUint numPages = (aSize + KPageSize - 1) >> KPageShift;
+ TInt carry = 0; // must be zero as this is always the start of a new run
+ TInt alignWrtPage = Max(aAlign - KPageShift, 0);
+ TUint32 alignmask = (1u << alignWrtPage) - 1;
+ TInt offset = -1;
+ TInt base = 0;
+
+ TUint physPages = 0;
+ TUint* zoneIdPtr = aZoneIdList;
+ TUint* zoneIdEnd = aZoneIdList + aZoneIdCount;
+ SZone* prevZone = NULL;
+ for (; zoneIdPtr < zoneIdEnd; zoneIdPtr++)
+ {
+ SZone* zone = ZoneFromId(*zoneIdPtr);
+ if (zone == NULL)
+ {// Couldn't find zone of this ID or it isn't large enough
+ return KErrArgument;
+ }
+ physPages += zone->iPhysPages;
+
+ if (offset >= 0 ||
+ (zone->iFlags & (KRamZoneFlagNoAlloc|KRamZoneFlagClaiming)))
+ {// Keep searching through the RAM zones if the allocation
+ // has succeeded, to ensure the ID list is always fully verified or
+ // if this zone is currently blocked for further allocations.
+ continue;
+ }
+
+ // Be sure to start from scratch if zone not contiguous with previous zone
+ if (prevZone && (zone->iPhysBase == 0 || (zone->iPhysBase - 1) != prevZone->iPhysEnd))
+ {
+ carry = 0;
+ }
+ prevZone = zone;
+
+ TInt len;
+ TBitMapAllocator& bmaAll = *(zone->iBma[KBmaAllPages]);
+ base = TInt(zone->iPhysBase >> KPageShift);
+
+ __KTRACE_OPT(KMMU,Kern::Printf("AllocAligned: aBase=%08x aCarry=%08x", base, carry));
+ offset = bmaAll.AllocAligned(numPages, alignWrtPage, base, EFalse, carry, len);
+ __KTRACE_OPT(KMMU,Kern::Printf("AllocAligned: offset=%08x", offset));
+ }
+
+ if (physPages < numPages)
+ {// The allocation requested is too large for the specified RAM zones.
+ return KErrArgument;
+ }
+
+ if (offset < 0)
+ {// The allocation failed.
+ return KErrNoMemory;
+ }
+
+ // Have found enough contiguous pages so mark the pages allocated and
+ // return address of physical page at the start of the region.
+ aPhysAddr = TPhysAddr((base + offset - carry + alignmask) & ~alignmask) << KPageShift;
+ MarkPagesAllocated(aPhysAddr, numPages, aType);
+
+ __KTRACE_OPT(KMMU,Kern::Printf("ZoneAllocContiguousRam returns %08x",aPhysAddr));
+#ifdef BTRACE_RAM_ALLOCATOR
+ BTrace12(BTrace::ERamAllocator, BTrace::ERamAllocZoneContiguousRam, aType, numPages, aPhysAddr);
+#endif
+ return KErrNone;
+ }
+
+
+/**
+Attempt to set the specified contiguous block of RAM pages to be either
+allocated or free.
+
+@param aBase The base address of the RAM to update.
+@param aSize The number of contiguous bytes of RAM to update.
+@param aState Set to ETrue to free the RAM, EFalse to allocate the RAM.
+@param aType The type of the pages being updated.
+
+@return KErrNone on success, KErrArgument if aBase is an invalid address,
+KErrGeneral if a page being marked free is already free,
+KErrInUse if the page being marked allocated is already allocated.
+*/
+TInt DRamAllocator::SetPhysicalRamState(TPhysAddr aBase, TInt aSize, TBool aState, TZonePageType aType)
+ {
+ M::RamAllocIsLocked();
+
+ __KTRACE_OPT(KMMU,Kern::Printf("SetPhysicalRamState(%08x,%x,%d)",aBase,aSize,aState?1:0));
+ TUint32 pageMask = KPageSize-1;
+ aSize += (aBase & pageMask);
+ aBase &= ~pageMask;
+ TInt npages = (aSize + pageMask) >> KPageShift;
+ __KTRACE_OPT(KMMU,Kern::Printf("Rounded base %08x npages=%x",aBase,npages));
+ TInt baseOffset;
+ SZone* baseZone = GetZoneAndOffset(aBase, baseOffset);
+ if (!baseZone || (TUint32)aSize > (iPhysAddrTop - aBase + 1))
+ {
+ return KErrArgument;
+ }
+ SZone* zone = baseZone;
+ SZone* zoneEnd = iZones + iNumZones;
+ TPhysAddr base = aBase;
+ TInt pagesLeft = npages;
+ TInt offset = baseOffset;
+ TInt pageCount = -1;
+ __KTRACE_OPT(KMMU2,Kern::Printf("Zone %x page index %x z=%08x zE=%08x n=%x base=%08x",zone->iId, offset, zone, zoneEnd, pagesLeft, base));
+ for (; pagesLeft && zone < zoneEnd; ++zone)
+ {
+ if (zone->iPhysBase + (offset << KPageShift) != base)
+ {// Zone not contiguous with current run of page, so have been
+ // asked to set the state of non-existent pages.
+ return KErrArgument;
+ }
+
+ TBitMapAllocator& bmaAll = *(zone->iBma[KBmaAllPages]);
+ TInt zp_rem = bmaAll.iSize - offset;
+ pageCount = Min(pagesLeft, zp_rem);
+ __KTRACE_OPT(KMMU2,Kern::Printf("Zone %x pages %x+%x base %08x", zone->iId, offset, pageCount, base));
+ if(aState)
+ {
+ if(bmaAll.NotAllocated(offset, pageCount))
+ {
+ return KErrGeneral;
+ }
+ }
+ else
+ {
+ if(bmaAll.NotFree(offset, pageCount))
+ {
+ return KErrInUse;
+ }
+ }
+ pagesLeft -= pageCount;
+ offset = 0;
+ base += (TPhysAddr(pageCount) << KPageShift);
+ }
+ if (pagesLeft)
+ {
+ return KErrArgument; // not all of the specified range exists
+ }
+
+ iTotalFreeRamPages += (aState ? npages : -npages);
+ zone = baseZone;
+ offset = baseOffset;
+ for (pagesLeft = npages; pagesLeft; pagesLeft -= pageCount)
+ {
+ TBitMapAllocator& bmaAll = *(zone->iBma[KBmaAllPages]);
+ // Unknown and fixed pages share a bit map.
+ TBitMapAllocator& bmaType = *(zone->iBma[(aType != EPageUnknown)? aType : EPageFixed]);
+ TInt zp_rem = bmaAll.iSize - offset;
+ pageCount = Min(pagesLeft, zp_rem);
+ if (aState)
+ {
+ bmaAll.Free(offset, pageCount);
+ bmaType.Free(offset, pageCount);
+ ZoneFreePages(zone, pageCount, aType);
+ }
+ else
+ {
+ bmaAll.Alloc(offset, pageCount);
+ bmaType.Alloc(offset, pageCount);
+ ZoneAllocPages(zone, pageCount, aType);
+ }
+ __KTRACE_OPT(KMMU2,Kern::Printf("Zone %d pages %x+%x base %08x",zone-iZones, offset, pageCount, base));
+ ++zone;
+ offset = 0;
+ }
+ return KErrNone;
+ }
+
+/** Update the allocated page counts for the zone that is page is allocated into.
+
+@param aAddr The physical address of the page
+@param aOldPageType The type the page was allocated as
+@param aNewPageType The type the page is changing to
+*/
+void DRamAllocator::ChangePageType(SPageInfo* aPageInfo, TZonePageType aOldType, TZonePageType aNewType)
+ {
+
+ TInt offset;
+ SZone* zone = GetZoneAndOffset(aPageInfo->PhysAddr(), offset);
+#ifdef _DEBUG
+// *********** System lock may be held while this is invoked so don't do********
+// *********** anything too slow and definitely don't call zone callback********
+ M::RamAllocIsLocked();
+ CHECK_PRECONDITIONS((MASK_THREAD_CRITICAL) & ~MASK_NO_FAST_MUTEX, "DRamAllocator::ChangePageType");
+
+ // Get zone page is in and on debug builds check that it is allocated
+ if (zone == NULL || zone->iBma[KBmaAllPages]->NotAllocated(offset, 1))
+ {
+ Panic(EAllocRamPagesInconsistent);
+ }
+
+ // Check if adjusting counts is valid, i.e. won't cause a roll over
+ if (zone->iAllocPages[aOldType] - 1 > zone->iAllocPages[aOldType] ||
+ zone->iAllocPages[aNewType] + 1 < zone->iAllocPages[aNewType])
+ {
+ __KTRACE_OPT(KMMU, Kern::Printf("ChangePageType Alloc Unk %x Fx %x Mv %x Dis %x",zone->iAllocPages[EPageUnknown],
+ zone->iAllocPages[EPageFixed], zone->iAllocPages[EPageMovable],zone->iAllocPages[EPageDiscard]));
+ Panic(EZonesCountErr);
+ }
+#endif
+
+ // Update the counts and bmas
+ zone->iAllocPages[aOldType]--;
+ zone->iBma[aOldType]->Free(offset);
+ zone->iAllocPages[aNewType]++;
+ zone->iBma[aNewType]->Alloc(offset, 1);
+
+ __KTRACE_OPT(KMMU2, Kern::Printf("ChangePageType Alloc Unk %x Fx %x Mv %x Dis %x",zone->iAllocPages[EPageUnknown],
+ zone->iAllocPages[EPageFixed], zone->iAllocPages[EPageMovable],zone->iAllocPages[EPageDiscard]));
+#ifdef BTRACE_RAM_ALLOCATOR
+ BTrace8(BTrace::ERamAllocator, BTrace::ERamAllocChangePageType, aNewType, aPageInfo->PhysAddr());
+#endif
+ }
+
+/**
+Get the next page in this zone that is allocated after this one.
+
+@param aZone The zone to find the next allocated page in.
+@param aOffset On entry this is the offset from which the next allocated
+ page in the zone should be found, on return it will be the offset
+ of the next allocated page.
+@return KErrNone if a next allocated page could be found, KErrNotFound if no more pages in
+the zone after aOffset are allocated, KErrArgument if aOffset is outside the zone.
+*/
+TInt DRamAllocator::NextAllocatedPage(SZone* aZone, TUint& aOffset, TZonePageType aType) const
+ {
+ const TUint KWordAlignMask = KMaxTUint32 << 5;
+
+ M::RamAllocIsLocked();
+
+ __NK_ASSERT_DEBUG(aZone - iZones < (TInt)iNumZones);
+ // Makes things simpler for bma selection.
+ __NK_ASSERT_DEBUG(aType != EPageUnknown);
+
+ if (aOffset >= aZone->iPhysPages)
+ {// Starting point is outside the zone
+ return KErrArgument;
+ }
+
+ TUint offset = aOffset;
+ TUint endOffset = aZone->iPhysPages;
+ TUint endOffsetAligned = endOffset & KWordAlignMask;
+
+ // Select the BMA to search,
+ TUint bmaIndex = (aType == EPageTypes)? KBmaAllPages : aType;
+ TUint32* map = &(aZone->iBma[bmaIndex]->iMap[offset >> 5]);
+ TUint32 bits = *map++;
+
+ // Set bits for pages before 'offset' (i.e. ones we want to ignore)...
+ bits |= ~(KMaxTUint32 >> (offset & ~KWordAlignMask));
+
+ // Find the first bit map word from aOffset in aZone with allocated pages
+ while (bits == KMaxTUint32 && offset < endOffsetAligned)
+ {
+ bits = *map++;
+ offset = (offset + 32) & KWordAlignMask;
+ }
+
+ if (offset >= endOffsetAligned && endOffset != endOffsetAligned)
+ {// Have reached the last bit mask word so set the bits that are
+ // outside of the zone so that they are ignored.
+ bits |= KMaxTUint32 >> (endOffset - endOffsetAligned);
+ }
+
+ if (bits == KMaxTUint32)
+ {// No allocated pages found after aOffset in aZone.
+ return KErrNotFound;
+ }
+
+ // Now we have bits with allocated pages in it so determine the exact
+ // offset of the next allocated page
+ TUint32 mask = 0x80000000 >> (offset & ~KWordAlignMask);
+ while (bits & mask)
+ {
+ mask >>= 1;
+ offset++;
+ }
+
+ if (offset >= endOffset)
+ {// Reached the end of the zone without finding an allocated page after aOffset
+ return KErrNotFound;
+ }
+
+ // Should definitely have found an allocated page within aZone's pages
+ __NK_ASSERT_DEBUG(mask != 0 && !(bits & mask) && offset < aZone->iPhysPages);
+
+ aOffset = offset;
+ return KErrNone;
+ }
+
+/**
+See if any of the least preferable RAM zones can be emptied. If they can then
+initialise the allocator for a general defragmentation operation.
+
+Stage 0 of the general defrag is to ensure that there are enough free
+pages in the more preferable RAM zones to be in use after the general defrag
+for the movable page allocations. This is achieved by discarding the
+required amount of discardable pages from the more preferable RAM zones
+to be in use after the general defrag.
+
+
+@parm aInitialStage On return this will contain the stage the general
+ defragmentation should begin at. I.e. if no RAM
+ zones can be cleared then just perform the final
+ tidying stage.
+@param aRequiredToBeDiscarded On return this will contain the number of
+ discardable pages that need to be discarded
+ from the RAM zones to be in use after the
+ general defrag.
+@return Pointer to the RAM zone object that may potentially have pages
+ discarded by the general defrag. This will be NULL if no suitable
+ RAM zone could be found.
+*/
+SZone* DRamAllocator::GeneralDefragStart0(TGenDefragStage& aStage, TUint& aRequiredToBeDiscarded)
+ {
+#ifdef _DEBUG
+ if (!K::Initialising)
+ {
+ M::RamAllocIsLocked();
+#ifdef __VERIFY_LEASTMOVDIS
+ VerifyLeastPrefMovDis();
+#endif
+ }
+ // Any previous general defrag operation must have ended.
+ __NK_ASSERT_DEBUG(iZoneGeneralPrefLink == NULL);
+ __NK_ASSERT_DEBUG(iZoneGeneralTmpLink == NULL);
+#endif
+
+ if (iNumZones == 1)
+ {
+ // Only have one RAM zone so a defrag can't do anything.
+ return NULL;
+ }
+
+ // Determine how many movable or discardable pages are required to be allocated.
+ TUint requiredPagesDis = 0;
+ TUint requiredPagesMov = 0;
+ TUint firstClearableInUseRank = 0;
+ SDblQueLink* link = iZoneLeastMovDis;
+ do
+ {
+ SZone& zone = *_LOFF(link, SZone, iPrefLink);
+ requiredPagesDis += zone.iAllocPages[EPageDiscard];
+ requiredPagesMov += zone.iAllocPages[EPageMovable];
+
+ if (!firstClearableInUseRank &&
+ (zone.iAllocPages[EPageMovable] || zone.iAllocPages[EPageDiscard]) &&
+ !zone.iAllocPages[EPageFixed] && !zone.iAllocPages[EPageUnknown])
+ {// This is the least preferable RAM zone that is has movable or
+ // discardable but may be clearable as it has no immovable pages.
+ firstClearableInUseRank = zone.iPrefRank;
+ }
+
+ // Reset KRamZoneFlagGenDefrag flag bit for each RAM zone to be defraged.
+ zone.iFlags &= ~(KRamZoneFlagGenDefrag | KRamZoneFlagGenDefragBlock);
+
+ link = link->iPrev;
+ }
+ while (link != &iZonePrefList.iA);
+
+ // Adjust the number of discardable pages for those that are freeable.
+ // Dirty pages will be moved rather than discarded so they are not freeable
+ // and we must make sure that we have enough space in zones for these dirty
+ // paged pages.
+ __NK_ASSERT_DEBUG(requiredPagesDis >= (TUint)M::NumberOfFreeDpPages());
+ requiredPagesDis -= M::NumberOfFreeDpPages();
+ TUint totalDirtyPagesDis = M::NumberOfDirtyDpPages();
+ if (requiredPagesDis < totalDirtyPagesDis)
+ requiredPagesDis = totalDirtyPagesDis;
+
+ // Determine which is the least preferable RAM zone that needs to be
+ // in use for required number of movable and discardable pages.
+ TUint onlyPagesDis = 0; // Number of pages in RAM zones for discard only.
+ TUint onlyPagesMov = 0; // Number of pages in RAM zones for movable only.
+ TUint totalPagesDis = 0; // Total pages found so far for discardable pages.
+ TUint totalPagesMov = 0; // Total pages found so far for movable pages.
+ TUint totalCurrentDis = 0; // Number of allocated discardable pages found in
+ // RAM zones to be in use after the general defrag.
+ TUint totalCurrentMov = 0; // Number of allocated movable pages found in
+ // RAM zones to be in use after the general defrag.
+ TUint totalCurrentFree = 0; // The current number of free pages in the RAM zones
+ // to be in use after the general defrag.
+ iZoneGeneralPrefLink = &iZonePrefList.iA;
+ while (iZoneGeneralPrefLink != iZoneLeastMovDis &&
+ (requiredPagesMov > totalPagesMov ||
+ requiredPagesDis > totalPagesDis))
+ {
+ iZoneGeneralPrefLink = iZoneGeneralPrefLink->iNext;
+ SZone& zone = *_LOFF(iZoneGeneralPrefLink, SZone, iPrefLink);
+ // Update the current totals.
+ totalCurrentDis += zone.iAllocPages[EPageDiscard];
+ totalCurrentMov += zone.iAllocPages[EPageMovable];
+ totalCurrentFree += zone.iFreePages;
+
+ TBool onlyAllocDis = NoAllocOfPageType(zone, EPageMovable);
+ TBool onlyAllocMov = NoAllocOfPageType(zone, EPageDiscard);
+ if (!onlyAllocMov || !onlyAllocDis)
+ {// Either movable, discardable or both can be allocated in this zone.
+ TUint zonePagesFree = zone.iFreePages;
+ TUint zonePagesDis = zone.iAllocPages[EPageDiscard];
+ TUint zonePagesMov = zone.iAllocPages[EPageMovable];
+ // Total pages in this RAM zone that can be used for either
+ // discardable or movable pages.
+ TUint zonePagesGen = zonePagesDis + zonePagesMov + zonePagesFree;
+ if (onlyAllocMov)
+ {
+ if (requiredPagesDis > totalPagesDis)
+ {// No further discardable pages can be allocated into
+ // this RAM zone but consider any that already are.
+ TUint usedPages = Min( (TInt)zonePagesDis,
+ requiredPagesDis - totalPagesDis);
+ totalPagesDis += usedPages;
+ zonePagesDis -= usedPages;
+ }
+ TUint zoneOnlyMov = zonePagesDis + zonePagesMov + zonePagesFree;
+ onlyPagesMov += zoneOnlyMov;
+ totalPagesMov += zoneOnlyMov;
+ __KTRACE_OPT(KMMU2, Kern::Printf("onlyMov ID%x tot %x",
+ zone.iId, zoneOnlyMov));
+ zonePagesGen = 0; // These pages aren't general purpose.
+ }
+ if (onlyAllocDis)
+ {
+ if (requiredPagesMov > totalPagesMov)
+ {// No further movable pages can be allocated into
+ // this RAM zone but consider any that already are.
+ TUint usedPages = Min( (TInt)zonePagesMov,
+ requiredPagesMov - totalPagesMov);
+ totalPagesMov += usedPages;
+ zonePagesMov -= usedPages;
+ }
+ TUint zoneOnlyDis = zonePagesDis + zonePagesMov + zonePagesFree;
+ onlyPagesDis += zoneOnlyDis;
+ totalPagesDis += zoneOnlyDis;
+ __KTRACE_OPT(KMMU2, Kern::Printf("onlyDis ID%x tot %x",
+ zone.iId, zoneOnlyDis));
+ zonePagesGen = 0; // These pages aren't general purpose.
+ }
+
+ if (requiredPagesDis > totalPagesDis)
+ {// Need some discardable pages so first steal any spare
+ // movable pages for discardable allocations.
+ if (totalPagesMov > requiredPagesMov)
+ {// Use any spare movable pages that can also be
+ // used for discardable allocations for discardable.
+ __NK_ASSERT_DEBUG(onlyPagesMov);
+ TUint spareMovPages = Min((TInt)(totalPagesMov - onlyPagesMov),
+ totalPagesMov - requiredPagesMov);
+ totalPagesMov -= spareMovPages;
+ totalPagesDis += spareMovPages;
+ __KTRACE_OPT(KMMU2, Kern::Printf("genDis Mov ID%x used%x",
+ zone.iId, spareMovPages));
+ }
+ if (requiredPagesDis > totalPagesDis)
+ {
+ // Need more discardable pages but only grab those required.
+ TUint usedPages = Min( (TInt) zonePagesGen,
+ requiredPagesDis - totalPagesDis);
+ totalPagesDis += usedPages;
+ zonePagesGen -= usedPages;
+ __KTRACE_OPT(KMMU2, Kern::Printf("genDis ID%x used%x",
+ zone.iId, usedPages));
+ }
+ }
+ if (requiredPagesMov > totalPagesMov)
+ {// Need some movable pages so first steal any spare
+ // discardable pages for movable allocations.
+ if (totalPagesDis > requiredPagesDis)
+ {// Use any spare discardable pages that can also be
+ // used for movable allocations for movable.
+ __NK_ASSERT_DEBUG(onlyPagesDis);
+ TUint spareDisPages = Min((TInt)(totalPagesDis - onlyPagesDis),
+ totalPagesDis - requiredPagesDis);
+ totalPagesDis -= spareDisPages;
+ totalPagesMov += spareDisPages;
+ __KTRACE_OPT(KMMU2, Kern::Printf("genMov Dis ID%x used%x",
+ zone.iId, spareDisPages));
+ }
+ if (requiredPagesMov > totalPagesMov)
+ {// Still need some movable pages so grab them from this zone.
+ // Just grab all of the general pages left as discard pages will
+ // have already grabbed some if it had needed to.
+ totalPagesMov += zonePagesGen;
+ __KTRACE_OPT(KMMU2, Kern::Printf("genMov ID%x used%x",
+ zone.iId, zonePagesGen));
+ }
+ }
+ }
+ }
+
+ __KTRACE_OPT(KMMU, Kern::Printf("gen least in use ID 0x%x",
+ (_LOFF(iZoneGeneralPrefLink, SZone, iPrefLink))->iId));
+ __NK_ASSERT_DEBUG(_LOFF(iZoneGeneralPrefLink, SZone, iPrefLink)->iPrefRank <=
+ iZoneLeastMovDisRank);
+
+ if (iZoneGeneralPrefLink != iZoneLeastMovDis &&
+ firstClearableInUseRank > _LOFF(iZoneGeneralPrefLink, SZone, iPrefLink)->iPrefRank)
+ {// We can reduce the number of RAM zones in use so block all the RAM
+ // zones not to be in use after the defrag from being allocated into
+ // by the general defrag.
+ link = iZoneLeastMovDis;
+ while (link != iZoneGeneralPrefLink)
+ {
+ SZone& zone = *_LOFF(link, SZone, iPrefLink);
+ zone.iFlags |= KRamZoneFlagGenDefragBlock;
+ link = link->iPrev;
+ }
+
+ // Determine how many pages will need to be discarded to allow general
+ // defrag to succeed in using the minimum RAM zones required.
+ if (requiredPagesDis > totalCurrentDis)
+ {// Need to replace some discardable pages in RAM zones to be
+ // cleared with pages in the RAM zones to be in use after the
+ // general defrag.
+ __NK_ASSERT_DEBUG(totalCurrentFree >= requiredPagesDis - totalCurrentDis);
+ totalCurrentFree -= requiredPagesDis - totalCurrentDis;
+ }
+ TUint totalForMov = totalCurrentFree + totalCurrentMov;
+ if (requiredPagesMov > totalForMov)
+ {// Need to discard some pages from the least preferable RAM zone to be
+ // in use after the general for the movable pages to be moved to.
+ aRequiredToBeDiscarded = requiredPagesMov - totalForMov;
+ __NK_ASSERT_DEBUG(aRequiredToBeDiscarded <= totalCurrentDis);
+ __NK_ASSERT_DEBUG(totalCurrentDis - aRequiredToBeDiscarded >= requiredPagesDis);
+ }
+
+ // This stage should discard pages from the least preferable RAM zones
+ // to be in use after the general defrag to save the pages having to
+ // be moved again by the final stage.
+ iZoneGeneralStage = EGenDefragStage0;
+ aStage = EGenDefragStage1; // Defrag::GeneralDefrag() requires this.
+ iZoneGeneralTmpLink = iZoneGeneralPrefLink;
+ return GeneralDefragNextZone0();
+ }
+
+ // General defrag can't clear any RAM zones so jump to tidying stage.
+ aStage = EGenDefragStage2;
+ iZoneGeneralStage = EGenDefragStage2;
+ return NULL;
+ }
+
+
+/**
+Find the next RAM zone that is suitable for stage 0 of a general defrag.
+This should only be called after a preceeding call to
+DRamAllocator::GeneralDefragStart0().
+
+This goes through the RAM zones from the least preferable to be in use
+after the general defrag to the most preferable RAM zone. It will
+return each time it finds a RAM zone with discardable pages allocated into it.
+
+@return Pointer to the RAM zone object that may potentially have pages
+ discarded by the general defrag. This will be NULL if no suitable
+ RAM zone could be found.
+*/
+SZone* DRamAllocator::GeneralDefragNextZone0()
+ {
+ M::RamAllocIsLocked();
+ // Any previous general defrag operation must have ended.
+ __NK_ASSERT_DEBUG(iZoneGeneralPrefLink != NULL);
+ __NK_ASSERT_DEBUG(iZoneGeneralTmpLink != NULL);
+ __NK_ASSERT_DEBUG(iZoneGeneralStage == EGenDefragStage0);
+
+ while (iZoneGeneralTmpLink != &iZonePrefList.iA)
+ {
+ SZone* zone = _LOFF(iZoneGeneralTmpLink, SZone, iPrefLink);
+
+ // Save the RAM zone that is currently more preferable than this one
+ // before any reordering.
+ iZoneGeneralTmpLink = iZoneGeneralTmpLink->iPrev;
+
+ if (zone->iFlags & KRamZoneFlagGenDefrag)
+ {// This zone has been selected for a general defrag already.
+ __KTRACE_OPT(KMMU, Kern::Printf("GenDefragNext0 zone ID 0x%x already defraged",
+ zone->iId));
+ return NULL;
+ }
+ zone->iFlags |= KRamZoneFlagGenDefrag;
+ if (zone->iAllocPages[EPageDiscard])
+ {
+ // A RAM zone that may have pages discarded by a general defrag has been found.
+ __KTRACE_OPT(KMMU, Kern::Printf("GenDefragNext0 zone ID 0x%x", zone->iId));
+ return zone;
+ }
+ }
+ return NULL;
+ }
+
+
+/**
+Initialise this stage of a general defrag operation which will attempt
+to clear all the RAM zones not to be in use once the general defrag
+has completed.
+
+@return Pointer to the RAM zone object that may potentially be cleared
+ by the general defrag. This will be NULL if no suitable
+ RAM zone could be found.
+*/
+SZone* DRamAllocator::GeneralDefragStart1()
+ {
+ M::RamAllocIsLocked();
+ __NK_ASSERT_DEBUG(iNumZones == 1 || iZoneGeneralPrefLink != NULL);
+
+
+ if (iNumZones == 1)
+ {// On a device with one RAM zone can't do any defrag so return NULL.
+ return NULL;
+ }
+
+ // Clear general defrag flags of each RAM zone to be defraged.
+ SDblQueLink* link = iZoneGeneralPrefLink;
+ for (; link != &iZonePrefList.iA; link = link->iPrev)
+ {
+ SZone& zone = *_LOFF(link, SZone, iPrefLink);
+ zone.iFlags &= ~KRamZoneFlagGenDefrag;
+ }
+
+ // Flags cleared so now to start this stage from least preferable RAM zone
+ // currently in use.
+ iZoneGeneralTmpLink = iZoneLeastMovDis;
+ iZoneGeneralStage = EGenDefragStage1;
+ return GeneralDefragNextZone1();
+ }
+
+
+/**
+Find the next RAM zone that is suitable for stage 1 of a general defrag.
+This should only be called after a preceeding call to
+DRamAllocator::GeneralDefragStart1().
+
+This goes through the RAM zones from the least preferable currently
+with movable or discardable pages allocated into it to the least
+preferable RAM zone that is to be in use after the general defrag.
+It will return each time it finds a RAM zone with movable and/or
+discardable pages allocated into it.
+
+@return Pointer to the RAM zone object that may potentially be cleared by a
+ general defrag. This will be NULL if no suitable zone could be found.
+*/
+SZone* DRamAllocator::GeneralDefragNextZone1()
+ {
+ M::RamAllocIsLocked();
+ // Any previous general defrag operation must have ended.
+ __NK_ASSERT_DEBUG(iZoneGeneralPrefLink != NULL);
+ __NK_ASSERT_DEBUG(iZoneGeneralTmpLink != NULL);
+ __NK_ASSERT_DEBUG(iZoneGeneralStage == EGenDefragStage1);
+
+
+ // If we hit the target least preferable RAM zone to be in use once
+ // the defrag has completed then stop this stage of the general defrag.
+
+ // Should never skip past iZoneGeneralPrefLink.
+ __NK_ASSERT_DEBUG(iZoneGeneralTmpLink != &iZonePrefList.iA);
+
+ while (iZoneGeneralTmpLink != iZoneGeneralPrefLink)
+ {
+ SZone* zone = _LOFF(iZoneGeneralTmpLink, SZone, iPrefLink);
+
+ // Save the RAM zone that is currently more preferable than this one
+ // before any reordering.
+ iZoneGeneralTmpLink = iZoneGeneralTmpLink->iPrev;
+
+ if (zone->iFlags & KRamZoneFlagGenDefrag)
+ {// This zone has been selected for a general defrag already.
+ __KTRACE_OPT(KMMU, Kern::Printf("GenDefragNext1 zone ID 0x%x already defraged",
+ zone->iId));
+ return NULL;
+ }
+ zone->iFlags |= KRamZoneFlagGenDefrag;
+ if (zone->iAllocPages[EPageMovable] || zone->iAllocPages[EPageDiscard])
+ {
+ // A RAM zone that may be cleared by a general defrag has been found.
+ __KTRACE_OPT(KMMU, Kern::Printf("GenDefragNext1 zone ID 0x%x", zone->iId));
+ return zone;
+ }
+ }
+ __KTRACE_OPT(KMMU, Kern::Printf("GenDefragNext1 reached general target"));
+ return NULL;
+ }
+
+
+/**
+Initialise stage 2 of a general defrag operation.
+
+Stage 2 creates room for fixed pages allocations in the more preferable RAM
+zones in use by moving pages into the least preferable RAM zones in use.
+
+@return Pointer to the RAM zone object that may potentially be cleared of
+ movable and discardable pages by the general defrag. This will be
+ NULL if no suitable zone could be found.
+*/
+SZone* DRamAllocator::GeneralDefragStart2()
+ {
+ M::RamAllocIsLocked();
+ __NK_ASSERT_DEBUG(iNumZones == 1 || iZoneGeneralPrefLink != NULL);
+
+
+ if (iNumZones == 1)
+ {// On a device with one RAM zone can't do any defrag so return NULL.
+ return NULL;
+ }
+
+ // Clear general defrag flags of each RAM zone to be defraged.
+ SDblQueLink* link = iZoneLeastMovDis;
+ for (; link != &iZonePrefList.iA; link = link->iPrev)
+ {
+ SZone& zone = *_LOFF(link, SZone, iPrefLink);
+ zone.iFlags &= ~(KRamZoneFlagGenDefrag | KRamZoneFlagGenDefragBlock);
+ }
+
+ // Flags cleared so now to start 2nd stage from most preferable RAM zone.
+ iZoneGeneralTmpLink = iZonePrefList.First();
+ iZoneGeneralStage = EGenDefragStage2;
+ return GeneralDefragNextZone2();
+ }
+
+
+/**
+Find the next RAM zone that is suitable for this stage of general defrag.
+This should only be called after a preceeding call to
+DRamAllocator::GeneralDefragStart2().
+
+This goes through the RAM zones from the most preferable to the least
+preferable RAM zone that has movable and/or discardable pages allocated
+into it. It will return each time it finds a RAM zone with movable and/or
+discardable pages allocated into it.
+
+@return Pointer to the RAM zone object that may potentially be cleared of
+ movable and discardable pages by the general defrag. This will be
+ NULL if no suitable zone could be found.
+*/
+SZone* DRamAllocator::GeneralDefragNextZone2()
+ {
+ M::RamAllocIsLocked();
+ __NK_ASSERT_DEBUG(iZoneGeneralTmpLink != NULL);
+ __NK_ASSERT_DEBUG(iZoneGeneralStage == EGenDefragStage2);
+
+
+ while (iZoneGeneralTmpLink != iZoneLeastMovDis)
+ {
+ SZone* zone = _LOFF(iZoneGeneralTmpLink, SZone, iPrefLink);
+
+ // Save the RAM zone that is currently less preferable than this one
+ // before any reordering.
+ iZoneGeneralTmpLink = iZoneGeneralTmpLink->iNext;
+
+ if (zone->iFlags & KRamZoneFlagGenDefrag)
+ {// This zone has been selected for a general defrag already.
+ __KTRACE_OPT(KMMU, Kern::Printf("GenDefragNext2 zone ID 0x%x already defraged", zone->iId));
+ return NULL;
+ }
+ zone->iFlags |= KRamZoneFlagGenDefrag | KRamZoneFlagGenDefragBlock;
+ if (zone->iAllocPages[EPageMovable] || zone->iAllocPages[EPageDiscard])
+ {// A RAM zone that may be cleared by a general defrag has been found.
+ __KTRACE_OPT(KMMU, Kern::Printf("GenDefragNext2 zone ID 0x%x", zone->iId));
+ return zone;
+ }
+ }
+ __KTRACE_OPT(KMMU, Kern::Printf("GenDefragNext2 reached general target"));
+ return NULL;
+ }
+
+/**
+Inform the allocator that a general defragmentation operation has completed.
+
+*/
+void DRamAllocator::GeneralDefragEnd()
+ {
+#ifdef _DEBUG
+ if (!K::Initialising)
+ {
+ M::RamAllocIsLocked();
+#ifdef __VERIFY_LEASTMOVDIS
+ VerifyLeastPrefMovDis();
+#endif
+ }
+#endif
+ // Reset the general defrag preference link as it is no longer required.
+ iZoneGeneralPrefLink = NULL;
+ iZoneGeneralTmpLink = NULL;
+ }
+
+
+/**
+Calculate the number of free pages in all the RAM zones to be in use
+once the general defragmentation operation has completed.
+
+@param aType The type of free pages to find in the higher priority zones.
+@return The number of free pages in the RAM zones intended to be in use
+after the general defrag operation has completed.
+*/
+TUint DRamAllocator::GenDefragFreePages(TZonePageType aType) const
+ {
+ M::RamAllocIsLocked();
+
+ if (iZoneGeneralStage == EGenDefragStage2)
+ {// Second stage of general defrag where don't have to empty the RAM zone.
+ return KMaxTUint;
+ }
+ TUint totalFree = 0;
+ SDblQueLink* link = iZoneGeneralPrefLink;
+ for (; link != &iZonePrefList.iA; link = link->iPrev)
+ {
+ SZone& zone = *_LOFF(link, SZone, iPrefLink);
+ if (NoAllocOfPageType(zone, aType) ||
+ zone.iFlags & KRamZoneFlagGenDefragBlock)
+ {
+ continue;
+ }
+ // This zone has free space for this type of page
+ totalFree += zone.iFreePages;
+ }
+ return totalFree;
+ }
+
+
+/** Mark the RAM zone as being claimed to stop any further allocations.
+@param aZone The zone to stop allocations to.
+
+@pre RamAlloc mutex held.
+@post RamAlloc mutex held.
+*/
+void DRamAllocator::ZoneClaimStart(SZone& aZone)
+ {
+ M::RamAllocIsLocked();
+ __NK_ASSERT_DEBUG(!(aZone.iFlags & KRamZoneFlagClaiming));
+
+ aZone.iFlags |= KRamZoneFlagClaiming;
+
+#ifdef BTRACE_RAM_ALLOCATOR
+ BTrace8(BTrace::ERamAllocator, BTrace::ERamAllocZoneFlagsModified, aZone.iId, aZone.iFlags);
+#endif
+ }
+
+
+/** Mark the RAM zone as not being claimed to allow allocations.
+@param aZone The zone to allow allocations into.
+
+@pre RamAlloc mutex held.
+@post RamAlloc mutex held.
+*/
+void DRamAllocator::ZoneClaimEnd(SZone& aZone)
+ {
+ M::RamAllocIsLocked();
+ __NK_ASSERT_DEBUG(aZone.iFlags & KRamZoneFlagClaiming);
+
+ aZone.iFlags &= ~KRamZoneFlagClaiming;
+
+#ifdef BTRACE_RAM_ALLOCATOR
+ BTrace8(BTrace::ERamAllocator, BTrace::ERamAllocZoneFlagsModified, aZone.iId, aZone.iFlags);
+#endif
+ }
+
+/** Mark the RAM zone so that any allocation or frees from it can be detected.
+Useful for defragging.
+@param aZone The zone to mark.
+@pre RamAlloc mutex held
+@post RamAlloc mutex held
+*/
+void DRamAllocator::ZoneMark(SZone& aZone)
+ {
+ M::RamAllocIsLocked();
+ __NK_ASSERT_DEBUG(!(aZone.iFlags & KRamZoneFlagMark));
+
+ aZone.iFlags |= KRamZoneFlagMark;
+
+#ifdef BTRACE_RAM_ALLOCATOR
+ BTrace8(BTrace::ERamAllocator, BTrace::ERamAllocZoneFlagsModified, aZone.iId, aZone.iFlags);
+#endif
+ }
+
+/** Unmark the RAM zone.
+Useful for defragging.
+@param aZone The zone to mark.
+@return ETrue if the RAM zone is inactive, EFalse otherwise.
+@pre RamAlloc mutex held
+@post RamAlloc mutex held
+*/
+TBool DRamAllocator::ZoneUnmark(SZone& aZone)
+ {
+ M::RamAllocIsLocked();
+
+ TInt r = aZone.iFlags & KRamZoneFlagMark;
+ aZone.iFlags &= ~KRamZoneFlagMark;
+
+#ifdef BTRACE_RAM_ALLOCATOR
+ BTrace8(BTrace::ERamAllocator, BTrace::ERamAllocZoneFlagsModified, aZone.iId, aZone.iFlags);
+#endif
+ return r;
+ }
+
+/** Determine whether it is OK to allocate the specified page type
+to the RAM zone.
+
+This should be used by all functions that search through the zones when
+attempting to allocate pages.
+
+@return ETrue if this page type shouldn't be allocated into the RAM zone,
+EFalse if it is OK to allocate that page type into the RAM zone.
+*/
+TBool DRamAllocator::NoAllocOfPageType(SZone& aZone, TZonePageType aType) const
+ {
+ TUint8 flagMask = 1 << (aType - KPageTypeAllocBase);
+ return (aZone.iFlags & (KRamZoneFlagClaiming|KRamZoneFlagNoAlloc|KRamZoneFlagTmpBlockAlloc)) ||
+ (aZone.iFlags & flagMask);
+ }
+
+
+/** Updates the flags of the specified RAM zone.
+
+@param aId The ID of the RAM zone to modify.
+@param aClearFlags The bit flags to clear.
+@param aSetFlags The bit flags to set.
+
+@return KErrNone on success, KErrArgument if the RAM zone of aId not found or
+aSetMask contains invalid flags.
+
+@pre RamAlloc mutex held
+@post RamAlloc mutex held
+*/
+TInt DRamAllocator::ModifyZoneFlags(TUint aId, TUint aClearMask, TUint aSetMask)
+ {
+ M::RamAllocIsLocked();
+
+ SZone* zone = ZoneFromId(aId);
+ if (zone == NULL || (aSetMask & KRamZoneFlagInvalid))
+ {// aId invalid or an invalid flag bit was requested to be set.
+ return KErrArgument;
+ }
+ zone->iFlags &= ~aClearMask;
+ zone->iFlags |= aSetMask;
+
+ __KTRACE_OPT(KMMU, Kern::Printf("Zone %x Flags %x", zone->iId, zone->iFlags));
+
+#ifdef BTRACE_RAM_ALLOCATOR
+ BTrace8(BTrace::ERamAllocator, BTrace::ERamAllocZoneFlagsModified, zone->iId, zone->iFlags);
+#endif
+ return KErrNone;
+ }
+
+
+/** Invoke the RAM zone call back function to inform the variant of the RAM zones
+in use so far by the system.
+This is designed to only be invoked once during boot in MmuBase::Init2()
+*/
+void DRamAllocator::InitialCallback()
+ {
+ __NK_ASSERT_DEBUG(iZoneCallbackInitSent == EFalse);
+ if (iZonePowerFunc)
+ {
+ TInt ret = (*iZonePowerFunc)(ERamZoneOp_Init, NULL, (TUint*)&iZonePwrState);
+ if (ret != KErrNone && ret != KErrNotSupported)
+ {
+ Panic(EZonesCallbackErr);
+ }
+ CHECK_PRECONDITIONS(MASK_THREAD_CRITICAL, "DRamAllocator::ZoneAllocPages");
+ }
+ iZoneCallbackInitSent = ETrue;
+ }
+
+
+#ifdef BTRACE_RAM_ALLOCATOR
+/**
+Structure for outputing zone information to BTrace that couldn't be fit into first
+2 words of the BTraceN call
+*/
+struct TRamAllocBtraceZone
+ {
+ TUint32 iId;
+ TUint8 iPref;
+ TUint8 iFlags;
+ TUint16 iReserved;
+ };
+
+/**
+This will be invoked when BTrace starts logging BTrace::ERamAllocator category
+traces.
+It outputs the zone configuration and the base addresses of any contiguous block
+of allocated pages.
+*/
+void DRamAllocator::SendInitialBtraceLogs(void)
+ {
+ M::RamAllocIsLocked();
+ CHECK_PRECONDITIONS(MASK_THREAD_CRITICAL, "DRamAllocator::SendInitialBtraceLogs");
+
+ // Output the zone information
+ TRamAllocBtraceZone bZone;
+ BTrace4(BTrace::ERamAllocator, BTrace::ERamAllocZoneCount, iNumZones);
+ const SZone* zone = iZones;
+ const SZone* const endZone = iZones + iNumZones;
+ for (; zone < endZone; zone++)
+ {
+ bZone.iId = zone->iId;
+ bZone.iPref = zone->iPref;
+ bZone.iFlags = zone->iFlags;
+ BTraceN(BTrace::ERamAllocator, BTrace::ERamAllocZoneConfig, zone->iPhysPages,
+ zone->iPhysBase, &bZone, sizeof(TRamAllocBtraceZone));
+ }
+
+ // Search through zones and output each contiguous region of allocated pages
+ for (zone = iZones; zone < endZone; zone++)
+ {
+ if (zone->iFreePages != zone->iPhysPages)
+ {
+ TInt pageCount = 0;
+ TInt totalPages = 0;
+ TUint32 runStart = 0;
+ while ((TUint)totalPages != zone->iPhysPages - zone->iFreePages)
+ {
+ // find set of contiguous pages that have been allocated
+ // runStart will be set to first page of allocated run if one found
+ for (;runStart < zone->iPhysPages && zone->iBma[KBmaAllPages]->NotAllocated(runStart,1); runStart++);
+
+ // find last allocated page of this run
+ TUint32 runEnd = runStart + 1;
+ for (;runEnd < zone->iPhysPages && zone->iBma[KBmaAllPages]->NotFree(runEnd,1); runEnd++);
+
+ pageCount = runEnd - runStart;
+ if (pageCount > 0)
+ {// have a run of allocated pages so output BTrace
+ TPhysAddr baseAddr = (runStart << KPageShift) + zone->iPhysBase;
+ __KTRACE_OPT(KMMU2, Kern::Printf("offset %x physBase %x pages %x baseAddr %08x",runStart, zone->iPhysBase, pageCount, baseAddr));
+ BTrace8(BTrace::ERamAllocator, BTrace::ERamAllocBootAllocation, pageCount, baseAddr);
+ runStart += pageCount;
+ totalPages += pageCount;
+ }
+ }
+ }
+ }
+ BTrace0(BTrace::ERamAllocator, BTrace::ERamAllocBootAllocationEnd);
+ }
+#endif // BTRACE_RAM_ALLOCATOR
+
+TInt DRamAllocator::ClaimPhysicalRam(TPhysAddr aBase, TInt aSize)
+ {
+ TInt ret = SetPhysicalRamState(aBase,aSize,EFalse, EPageFixed);
+#ifdef BTRACE_RAM_ALLOCATOR
+ if (ret == KErrNone)
+ {
+ BTrace8(BTrace::ERamAllocator, BTrace::ERamAllocClaimRam, aSize, aBase);
+ }
+#endif
+ return ret;
+ }
+
+TInt DRamAllocator::FreePhysicalRam(TPhysAddr aBase, TInt aSize)
+ {
+ TInt ret = SetPhysicalRamState(aBase,aSize,ETrue, EPageFixed);
+#ifdef BTRACE_RAM_ALLOCATOR
+ if (ret == KErrNone)
+ {
+ BTrace8(BTrace::ERamAllocator, BTrace::ERamAllocFreePhysical, aSize, aBase);
+ }
+#endif
+ return ret;
+ }
+
+
+TInt DRamAllocator::FreeRamInBytes()
+ {
+ return iTotalFreeRamPages<<KPageShift;
+ }
+
+TUint DRamAllocator::FreeRamInPages()
+ {
+ return iTotalFreeRamPages;
+ }
+
+TUint DRamAllocator::TotalPhysicalRamPages()
+ {
+ return iTotalRamPages;
+ }
+
+#ifdef __VERIFY_LEASTMOVDIS
+void DRamAllocator::VerifyLeastPrefMovDis()
+ {
+ // Shouldn't have any movable or discardable pages in any RAM
+ // zone less preferable than iZoneLeastMovDis
+ SDblQueLink* tmpLink = iZoneLeastMovDis->iNext;
+ while (tmpLink != &iZonePrefList.iA)
+ {
+ SZone& zone = *_LOFF(tmpLink, SZone, iPrefLink);
+ if (zone.iAllocPages[EPageMovable] != 0 ||
+ zone.iAllocPages[EPageDiscard] != 0)
+ {
+ DebugDump();
+ __NK_ASSERT_DEBUG(0);
+ }
+ tmpLink = tmpLink->iNext;
+ }
+ }
+#endif