kerneltest/e32test/nkernsa/tlsf.cpp
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:29:07 +0100
changeset 30 8aab599e3476
parent 0 a41df078684a
child 43 c1f20ce4abcf
permissions -rw-r--r--
Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h) Have multiple extension sections in the bld.inf, one for each version of the compiler. The RVCT version building the tools will build the runtime libraries for its version, but make sure we extract all the other versions from zip archives. Also add the archive for RVCT4.

// Copyright (c) 2005-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:
// x86pc\nkern\tlsf.cpp
// 
//

#include <nktest/nkutils.h>

extern "C" {
extern TLinAddr RomHeaderAddress;
extern TLinAddr SuperPageAddress;
}

#define	BLOCK_STATUS_FREE	1u	// bit set if SBlock free
#define	BLOCK_STATUS_FINAL	2u	// bit set if this is last physical SBlock
#define BLOCK_SIZE_MASK		0xfffffffcu

struct SBlock
	{
	TUint32 size;					// bits 2-31 give size, bits 0,1 are status bits
	SBlock* predecessor;		// previous SBlock in physical address order
	};

struct SFreeBlock
	{
	SBlock b;
	SFreeBlock* next;			// next SBlock in same bucket, circular list
	SFreeBlock* prev;			// previous SBlock in same bucket, circular list
	};

struct STlsfAllocator
	{
public:
	static STlsfAllocator* Create(TAny* aBase, TUint32 aTotalSize);
	TAny* Alloc(TUint32 aSize);
	void Free(TAny* aCell);
	TAny* ReAlloc(TAny* aCell, TUint32 aSize);

	void Init(TUint32 aTotalSize);
	void InsertFree(SFreeBlock* aB);
	TUint32 MapSize(TUint32 aSize, TUint32* p_sli, TInt round_up);
	SFreeBlock* FindFree(TUint32 aSize);
	void RemoveFree(SFreeBlock* aB);
	TUint32 ConsistencyCheck();
public:
	TUint32	imin_size;			// minimum SBlock size
	TUint32 itotal_size;		// total size of allocated and free blocks
	TUint32 il2min;				// log2 min size
	TUint32 infl;				// number of first level lists
	TUint32 insl;				// number of second level lists
	TUint32 il2nsl;
	SFreeBlock** isll;			// pointer to first second level list pointer for minimum size
	TUint32 iflb;				// first level bitmap
	SBlock* iff;				// first free address
	TUint32 islb[1];			// second level bitmaps, one for each first level list
								// second level lists follow, nsl pointers for each first level list
	};

STlsfAllocator* TheAllocator;
NFastMutex AllocMutex;

STlsfAllocator* STlsfAllocator::Create(void* aBase, TUint32 aTotalSize)
	{
	STlsfAllocator* p = (STlsfAllocator*)aBase;
	p->Init(aTotalSize);
	return p;
	}

void STlsfAllocator::Init(TUint32 total_size)
	{
	TUint32 a;
	imin_size = 16;
	il2min = 4;
	insl = 16;
	il2nsl = 4;
	itotal_size = total_size;
	infl = __e32_find_ms1_32(total_size) - il2min + 1;
	iflb = 0;
	a = (TUint32)&islb[infl];
	a = (a+63)&~63;
	isll = (SFreeBlock**)a;
	a += insl * infl * sizeof(TUint32*);
	iff = (SBlock*)a;
	a -= (TUint32)this;
	memset(islb, 0, total_size - sizeof(STlsfAllocator) + sizeof(TUint32));
	iff->size = (total_size - a) | BLOCK_STATUS_FINAL;
	iff->predecessor = 0;
	Free(iff+1);
	}

// size already rounded up to multiple of min_size
TUint32 STlsfAllocator::MapSize(TUint32 size, TUint32* p_sli, TInt round_up)
	{
	TUint32 sli = size >> il2min;
	TUint32 fli = __e32_find_ms1_32(sli);
	sli -= (1u<<fli);
	if (fli > il2nsl)
		{
		TUint32 sls = (fli - il2nsl);
		TUint32 sz2 = sli;
		sli >>= sls;
		if (round_up && ((sli << sls) < sz2) )
			{
			if (++sli == insl)
				sli=0, ++fli;
			}
		}
	*p_sli = sli;
	return fli;
	}

SFreeBlock* STlsfAllocator::FindFree(TUint32 size)
	{
	TUint32 sli;
	TUint32 fli = MapSize(size, &sli, 1);
	TUint32 sli2;
	TUint32 fli2;
	SFreeBlock** sll;
	SFreeBlock* b;
	TUint32 act_sz;

	if ((iflb >> fli) & 1)
		{
		if ((islb[fli]>>sli)==0)
			++fli, sli=0;
		}
	else
		sli = 0;
	if (fli >= infl)
		return 0;
	fli2 = __e32_find_ls1_32(iflb >> fli);
	if ((TInt)fli2 < 0)
		return 0;
	fli2 += fli;
	sli2 = __e32_find_ls1_32(islb[fli2] >> sli) + sli;	// must find a 1
	sll = &isll[(fli2 << il2nsl) | sli2];
	b = *sll;
	if (b->next == b)
		{
		// list now empty
		*sll = 0;
		if ( (islb[fli2] &= ~(1u<<sli2)) == 0 )
			iflb &= ~(1u<<fli2);
		}
	else
		{
		*sll = b->next;
		b->next->prev = b->prev;
		b->prev->next = b->next;
		}
	act_sz = b->b.size & BLOCK_SIZE_MASK;
	if (act_sz > size)
		{
		// free the extra
		SBlock* nfb = (SBlock*)((TUint32)b + size);
		nfb->size = (act_sz - size) | (b->b.size & BLOCK_STATUS_FINAL);
		nfb->predecessor = &b->b;
		if (!(b->b.size & BLOCK_STATUS_FINAL))
			{
			// if this isn't final SBlock, update predecessor of following SBlock
			SBlock* nb = (SBlock*)((TUint32)b + act_sz);
			nb->predecessor = nfb;
			}
		InsertFree((SFreeBlock*)nfb);
		b->b.size = 0;	// allocated SBlock can't be final
		}
	b->b.size &= BLOCK_STATUS_FINAL;
	b->b.size |= size;
	return b;
	}

void STlsfAllocator::InsertFree(SFreeBlock* b)
	{
	TUint32 size = b->b.size & BLOCK_SIZE_MASK;
	TUint32 sli;
	TUint32 fli = MapSize(size, &sli, 0);
	SFreeBlock** sll = &isll[(fli << il2nsl) | sli];
	if (*sll)
		{
		SFreeBlock* first = *sll;
		b->next = first;
		b->prev = first->prev;
		first->prev->next = b;
		first->prev = b;
		}
	else
		{
		b->next = b;
		b->prev = b;
		islb[fli] |= (1u<<sli);
		iflb |= (1u<<fli);
		}
	*sll = b;
	b->b.size |= BLOCK_STATUS_FREE;
	}

void STlsfAllocator::RemoveFree(SFreeBlock* b)
	{
	TUint32 size = b->b.size & BLOCK_SIZE_MASK;
	TUint32 sli;
	TUint32 fli = MapSize(size, &sli, 0);
	SFreeBlock** sll = &isll[(fli << il2nsl) | sli];
	if (b->next != b)
		{
		if (*sll == b)
			*sll = b->next;
		b->next->prev = b->prev;
		b->prev->next = b->next;
		return;
		}
	*sll = 0;
	if ( (islb[fli] &= ~(1u<<sli)) == 0)
		iflb &= ~(1u<<fli);
	}

TAny* STlsfAllocator::Alloc(TUint32 size)
	{
	SFreeBlock* b;
	TUint32 msm = imin_size - 1;

	size = (size + sizeof(SBlock) + msm) &~ msm;

	b = FindFree(size);
	if (b)
		return &b->next;
	return 0;
	}

void STlsfAllocator::Free(TAny* cell)
	{
	SBlock* b = ((SBlock*)cell) - 1;
//	SFreeBlock* fb = (SFreeBlock*)b;
	TUint32 size;
	if (!cell)
		return;
	size = b->size & BLOCK_SIZE_MASK;
	if (!(b->size & BLOCK_STATUS_FINAL))
		{
		// not last SBlock
		SBlock* nb = (SBlock*)((TUint32)b + size);
		TUint32 nbs = nb->size;
		if (nbs & BLOCK_STATUS_FREE)
			{
			// following SBlock is free
			RemoveFree((SFreeBlock*)nb);
			b->size = size + (nbs &~ BLOCK_STATUS_FREE);	// keeps final flag from following SBlock
			size = b->size & BLOCK_SIZE_MASK;
			if (!(nbs & BLOCK_STATUS_FINAL))
				{
				nb = (SBlock*)((TUint32)b + size);
				nb->predecessor = b;
				}
			}
		}
	if (b->predecessor && b->predecessor->size & BLOCK_STATUS_FREE)
		{
		// predecessor SBlock is free
		TUint32 psz = b->predecessor->size & BLOCK_SIZE_MASK;
		RemoveFree((SFreeBlock*)b->predecessor);
		psz += b->size; // keeps final flag if necessary
		b->predecessor->size = psz;
		b = b->predecessor;
		if (!(psz & BLOCK_STATUS_FINAL))
			{
			// need to adjust prev pointer of following SBlock
			SBlock* nb = (SBlock*)((TUint32)b + psz);
			nb->predecessor = b;
			}
		}
	InsertFree((SFreeBlock*)b);
	}

TAny* STlsfAllocator::ReAlloc(TAny* cell, TUint32 newsize)
	{
	SBlock* b;
	TUint32 size;
	TUint32 msm;
	SBlock* nb;
	TAny* newcell;

	if (!cell)
		return (newsize>0) ? Alloc(newsize) : 0;
	if (newsize == 0)
		{
		Free(cell);
		return 0;
		}
	b = ((SBlock*)cell) - 1;
	size = b->size & BLOCK_SIZE_MASK;
	msm = imin_size - 1;
	newsize = (newsize + sizeof(SBlock) + msm) &~ msm;
	if (newsize > size)
		{
		nb = (SBlock*)((TUint32)b + size);
		if (nb->size & BLOCK_STATUS_FREE)
			{
			// following SBlock is free
			TUint32 nbs = nb->size;
			if (nbs + size >= newsize)
				{
				// we can expand in place - grab the entire free SBlock for now
				// it will be split if necessary in the next section of code
				RemoveFree((SFreeBlock*)nb);
				b->size = size + (nbs &~ BLOCK_STATUS_FREE);	// keeps final flag from following SBlock
				size = b->size & BLOCK_SIZE_MASK;
				if (!(nbs & BLOCK_STATUS_FINAL))
					{
					SBlock* nnb = (SBlock*)((TUint32)b + size);
					nnb->predecessor = b;
					}
				}
			}
		}
	if (newsize == size)
		return cell;
	if (newsize < size)
		{
		// shrinking - split SBlock
		TUint32 final = b->size & BLOCK_STATUS_FINAL;
		SBlock* nfb = (SBlock*)((TUint32)b + newsize);
		nfb->size = (size - newsize) | final;
		nfb->predecessor = b;
		if (!final)
			{
			// if this isn't final SBlock, update predecessor of following SBlock
			nb = (SBlock*)((TUint32)b + size);
			nb->predecessor = nfb;
			}
		b->size = newsize;	// original SBlock can't be final
		Free((SBlock*)nfb + 1);
		return cell;
		}

	// must move SBlock to expand
	newcell = Alloc(newsize);
	if (newcell)
		{
		memcpy(newcell, cell, size);
		Free(cell);
		}
	return newcell;
	}

TUint32 STlsfAllocator::ConsistencyCheck()
	{
	TUint32 a;
	TUint32 szs = 0;
	TUint32 size;
	TUint32 total_user_size;
	TUint32 total_block_size = 0;
	TUint32 block_count = 0;
	TUint32 flb = 0;
	TUint32 slb[32];
	TUint32 sli;
	TUint32 fli;
	TUint32 total_free = 0;
	SBlock* b = iff;
	SBlock* pb = 0;

	memset(slb, 0, sizeof(slb));
	__NK_ASSERT_DEBUG(imin_size == 16);
	__NK_ASSERT_DEBUG(insl == 16);
	__NK_ASSERT_DEBUG(il2min == 4);
	__NK_ASSERT_DEBUG(il2nsl == 4);
	__NK_ASSERT_DEBUG(infl == __e32_find_ms1_32(itotal_size) - il2min + 1);
	a = (TUint32)&islb[infl];
	a = (a+63)&~63;
	__NK_ASSERT_DEBUG(isll == (SFreeBlock**)a);
	a += insl * infl * sizeof(TUint32*);
	__NK_ASSERT_DEBUG(iff == (SBlock*)a);
	total_user_size = itotal_size - (a - (TUint32)this);

	do	{
		szs = b->size;
		size = szs & BLOCK_SIZE_MASK;
		__NK_ASSERT_DEBUG(b->predecessor == pb);
		__NK_ASSERT_DEBUG(size > 0);
		__NK_ASSERT_DEBUG(size <= total_user_size);
		__NK_ASSERT_DEBUG(size == ((size >> il2min) << il2min));
		total_block_size += size;
		++block_count;
		pb = b;
		b = (SBlock*)((TUint32)b + size);
		} while(!(szs & BLOCK_STATUS_FINAL));
	__NK_ASSERT_DEBUG((TUint32)b == (TUint32)this + itotal_size);
	__NK_ASSERT_DEBUG(total_block_size == total_user_size);

	b = iff;
	do	{
		szs = b->size;
		size = szs & BLOCK_SIZE_MASK;
		if (szs & BLOCK_STATUS_FREE)
			{
			SFreeBlock* fb = (SFreeBlock*)b;
			SFreeBlock* pfb = fb;
			SFreeBlock* lh;
			TUint32 lhi;
			TInt lh_found = 0;
			TInt c = (TInt)block_count;
			TUint32 fli = __e32_find_ms1_32(size) - il2min;
			TUint32 sli = (size >> il2min) - (1u << fli);
			TUint32 sli2;
			TUint32 fli2 = MapSize(size, &sli2, 0);
			(void)sli2, (void)fli2;
			if (fli > il2nsl)
				sli >>= (fli - il2nsl);
			__NK_ASSERT_DEBUG(fli == fli2);
			__NK_ASSERT_DEBUG(sli == sli2);
			flb |= (1u << fli);
			slb[fli] |= (1u << sli);
			lhi = (fli << il2nsl) | sli;
			lh = isll[lhi];
			do	{
				if (fb == lh)
					lh_found = 1;
				pfb = fb;
				fb = fb->next;
				__NK_ASSERT_DEBUG(fb->prev == pfb);
				__NK_ASSERT_DEBUG(fb->b.size & BLOCK_STATUS_FREE);
				} while ((fb != (SFreeBlock*)b) && --c>=0);
			__NK_ASSERT_DEBUG(fb == (SFreeBlock*)b);
			__NK_ASSERT_DEBUG(lh_found);
			total_free += size;
			}
		b = (SBlock*)((TUint32)b + size);
		} while(!(szs & BLOCK_STATUS_FINAL));

	__NK_ASSERT_DEBUG(flb == iflb);
	for (fli=0; fli<infl; ++fli)
		{
		__NK_ASSERT_DEBUG(slb[fli] == islb[fli]);
		if (!(flb & (1u<<fli)))
			__NK_ASSERT_DEBUG(slb[fli]==0);
		for (sli=0; sli<insl; ++sli)
			{
			TUint32 lhi = (fli << il2nsl) | sli;
			(void)lhi;
			if (!(slb[fli] & (1u<<sli)))
				__NK_ASSERT_DEBUG(!isll[lhi]);
			}
		}
	return total_free;
	}

#define __E32CMN_H__

#undef EXPORT_C
#define EXPORT_C /* */

class TVersion
	{
public:
	TInt8 iMajor;
	TInt8 iMinor;
	TInt16 iBuild;
	};

class TDesC;

#include <e32rom.h>
#include "kernboot.h"

extern "C" {
void SetupMemoryAllocator()
	{
	const SSuperPageBase& sp = *(const SSuperPageBase*)SuperPageAddress;
	const TRomHeader& romHdr = *(const TRomHeader*)RomHeaderAddress;
	const TRomEntry* primaryEntry = (const TRomEntry*)sp.iPrimaryEntry;
	const TRomImageHeader* primaryImageHeader = (const TRomImageHeader*)primaryEntry->iAddressLin;
	TLinAddr stack = romHdr.iKernDataAddress + round_to_page(romHdr.iTotalSvDataSize);
	TLinAddr heapbase = stack + round_to_page(primaryImageHeader->iStackSize);
	TLinAddr heapsize = sp.iInitialHeapSize;

	KPrintf("Heap base %08x size %08x", heapbase, heapsize);
	TheAllocator = STlsfAllocator::Create((TAny*)heapbase, heapsize);
	}

extern TBool InitialThreadDefined;

TAny* malloc(TUint32 aSize)
	{
//	__KTRACE_OPT(KMMU,DEBUGPRINT("malloc(%d)",aSize));
	if (InitialThreadDefined)
		NKern::FMWait(&AllocMutex);
	TAny* p = TheAllocator->Alloc(aSize);
	if (InitialThreadDefined)
		NKern::FMSignal(&AllocMutex);
	__KTRACE_OPT(KMMU,DEBUGPRINT("malloc(%d)->%08x",aSize,p));
	return p;
	}

void free(TAny* aCell)
	{
	__KTRACE_OPT(KMMU,DEBUGPRINT("free(%08x)",aCell));
#ifdef _DEBUG
	if (aCell)
		{
		SBlock* b = (SBlock*)aCell;
		TUint32 size = b[-1].size & BLOCK_SIZE_MASK;
		memset(aCell, 0xde, size - sizeof(SBlock));
		}
#endif
	NKern::FMWait(&AllocMutex);
	TheAllocator->Free(aCell);
	NKern::FMSignal(&AllocMutex);
	}

TAny* realloc(TAny* aCell, TUint32 aSize)
	{
	NKern::FMWait(&AllocMutex);
	TAny* p = TheAllocator->ReAlloc(aCell, aSize);
	NKern::FMSignal(&AllocMutex);
	return p;
	}
}