kerneltest/e32test/mmu/t_imb.cpp
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:26:05 +0100
branchRCL_3
changeset 29 743008598095
parent 0 a41df078684a
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) 1995-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:
// e32test\mmu\t_imb.cpp
// Overview:
// Test the RChunk Create Local Code and Instruction Memory Barrier 
// control interface.
// API Information:
// RChunk::CreateLocalCode & User::IMB_Range
// Details:
// - Create a code chunk, write a small test function to the chunk, use
// User::IMB_Range to prepare the virtual address range for code execution.
// - Verify the success and failure of the IMB with various processes and with
// different base and size values.
// Platforms/Drives/Compatibility:
// All.
// Assumptions/Requirement/Pre-requisites:
// Failures and causes:
// Base Port information:
// 
//

#include <e32test.h>
#include "u32std.h"
#include <e32math.h>

#ifdef __CPU_ARM
typedef TInt (*TSqrtFn)(TReal&, const TReal&);
extern TInt Sqrt(TReal& /*aDest*/, const TReal& /*aSrc*/);
extern TUint Sqrt_Length();

typedef TInt (*TDivideFn)(TRealX&, const TRealX&);
extern TInt Divide(TRealX& /*aDividend*/, const TRealX& /*aDivisor*/);
extern TUint Divide_Length();

extern TInt SDummy(TInt);
extern TUint SDummy_Length();

extern TInt Increment(TInt);
extern TUint Increment_Length();

typedef TInt (*PFI)(TInt);

class RTestHeap : public RHeap
	{
public: 
	TUint8* GetTop() {return iTop;}
	};

TInt Thread2(TAny* aPtr)
	{
	TSqrtFn pSqrt=(TSqrtFn)aPtr;
	TReal x,y;
	x=2.0;
	return pSqrt(y,x);
	}

TInt Thread3(TAny* aPtr)
	{
	return *(TInt*)aPtr;
	}

TInt Thread4(TAny* aPtr)
	{
	*(TInt*)aPtr=0xe7ffffff;
	return 0;
	}

void SecondaryProcess(const TDesC& aCmd, RTest& test)
	{
	test.Start(_L("Secondary Process"));
	TLex lex(aCmd);
	TUint32 addr;
	TInt r=lex.Val(addr,EHex);
	test(r==KErrNone);
	test.Printf(_L("Main process RAM code at %08x\n"),addr);
	TInt n=0;
	FOREVER
		{
		RThread t;
		TRequestStatus s;
		if (n==0)
			{
			// Create another thread which attempts to execute code from the other process
			r=t.Create(_L("Thread2"),Thread2,0x1000,NULL,(TAny*)addr);
			}
		else if (n==1)
			{
			// Create another thread which attempts to read code from the other process
			r=t.Create(_L("Thread3"),Thread3,0x1000,NULL,(TAny*)addr);
			}
		else if (n==2)
			{
			// Create another thread which attempts to write to the the other process' code
			r=t.Create(_L("Thread4"),Thread4,0x1000,NULL,(TAny*)addr);
			}
		test(r==KErrNone);
		t.SetPriority(EPriorityMore);
		t.Logon(s);
		t.Resume();
		User::WaitForRequest(s);
		TInt exitType=t.ExitType();
		TInt exitReason=t.ExitReason();
		TBuf<32> exitCat=t.ExitCategory();
		CLOSE_AND_WAIT(t);
		test(exitType==EExitPanic);
		test(exitReason==ECausedException);
		test(exitCat==_L("KERN-EXEC"));
		if (++n==3)
			n=0;
		User::After(0);//Force rescheduling of the primary process's thread.
		}
	}

void Fill32(TUint32* aBase, TUint aSize, TUint32 aValue)
	{
	for (; aSize; aSize-=4)
		*aBase++=aValue;
	}

void TestIMB(RTest& test, TUint32* aBase, TUint aOffset, TUint aSize)
	{
	test.Printf(_L("TestIMB: Base %08x Offset %x Size %x\n"),aBase,aOffset,aSize);
	// First fill entire area
	Fill32(aBase,0x20000,0xe3a00000);			// mov r0, #0
#ifdef __SUPPORT_THUMB_INTERWORKING
	aBase[0x8000]=0xe12fff1e;					// bx lr
#else
	aBase[0x8000]=0xe1a0f00e;					// mov pc, lr
#endif
	PFI pBase=(PFI)aBase;
	PFI pCode=(PFI)((TUint8*)aBase+aOffset);
	User::IMB_Range(aBase,aBase+0x8001);
	TInt r=pBase(0);
	test(r==0);

	TUint32* p32=(TUint32*)pCode;
	TUint32* pEnd32=p32+aSize/4;
	Fill32(p32,aSize-4,0xe2800001);				// add r0, r0, #1
#ifdef __SUPPORT_THUMB_INTERWORKING
	pEnd32[-1]=0xe12fff1e;						// bx lr
#else
	pEnd32[-1]=0xe1a0f00e;						// mov pc, lr
#endif
	User::IMB_Range(p32,pEnd32);
	r=pCode(0);
	if (r!=(TInt)(aSize/4-1))
		{
		test.Printf(_L("f(0) expected %d got %d\n"),aSize/4-1,r);
		test(0);
		}
	r=pCode(487);
	if (r!=(TInt)(487+aSize/4-1))
		{
		test.Printf(_L("f(487) expected %d got %d\n"),487+aSize/4-1,r);
		test(0);
		}
	}

GLREF_C TInt E32Main()
	{
	RTest test(_L("T_IMB"));
	test.Title();

	TBuf<16> cmd;
	User::CommandLine(cmd);
	if (cmd.Length()!=0)
		{
		SecondaryProcess(cmd,test);
		return 0;
		}

	test.Start(_L("Create code chunk"));
	TInt pageSize;
	TInt r=UserHal::PageSizeInBytes(pageSize);
	test(r==KErrNone);

	RChunk c;
	r=c.CreateLocalCode(pageSize,0x100000);
	test(r==KErrNone);
	TUint8* pCode=c.Base();
	test.Printf(_L("Code chunk at %08x\n"),pCode);

	// Copy increment function
	Mem::Copy(pCode, (const TAny*)&Increment, Increment_Length());
	User::IMB_Range(pCode,pCode+Increment_Length());
	PFI pFI=(PFI)pCode;
	r=pFI(29);
	test(r==30);

	// Copy dummy without IMB
	Mem::Copy(pCode, (const TAny*)&SDummy, SDummy_Length());
	r=pFI(29);
	test.Printf(_L("Copy without IMB 1: r=%d\n"),r);

	// Now do IMB
	User::IMB_Range(pCode,pCode+SDummy_Length());
	r=pFI(29);
	test(r==29);

	// Read the code so it's in DCache
	TInt i;
	TInt sum=0;
	for (i=0; i<15; ++i)
		sum+=pCode[i];

	// Copy increment function
	Mem::Copy(pCode, (const TAny*)&Increment, Increment_Length());
	r=pFI(29);
	test.Printf(_L("Copy without IMB 2: r=%d\n"),r);

	// Now do IMB
	User::IMB_Range(pCode,pCode+Increment_Length());
	r=pFI(29);
	test(r==30);

	// Now adjust to 2 pages
	r=c.Adjust(2*pageSize);
	test(r==KErrNone);
	TUint8* pCode2=pCode+pageSize;

	// Create another thread
	RThread t;
	TRequestStatus s;
	r=t.Create(_L("Thread2"),Thread2,0x1000,NULL,pCode2);
	test(r==KErrNone);
	t.SetPriority(EPriorityMore);
	t.Logon(s);

	// Copy Sqrt code to 2nd page
	Mem::Copy(pCode2, (const TAny*)&Sqrt, Sqrt_Length());
	User::IMB_Range(pCode2,pCode2+Sqrt_Length());
	TSqrtFn pSqrt=(TSqrtFn)pCode2;
	TReal x,y,z;
	x=2.0;
	r=Math::Sqrt(y,x);
	test(r==KErrNone);
	r=pSqrt(z,x);
	test(r==KErrNone);
	test(z==y);

	// Unmap the second page
	r=c.Adjust(pageSize);
	test(r==KErrNone);

	// Get the second thread to attempt to execute the unmapped code
	t.Resume();
	User::WaitForRequest(s);
	TInt exitType=t.ExitType();
	TInt exitReason=t.ExitReason();
	TBuf<32> exitCat=t.ExitCategory();
	CLOSE_AND_WAIT(t);
	test.Printf(_L("Thread2: %d,%d,%S\n"),exitType,exitReason,&exitCat);
	test(exitType==EExitPanic);
	test(exitReason==ECausedException);
	test(exitCat==_L("KERN-EXEC"));

	// Copy Sqrt code to 1st page
	Mem::Copy(pCode, (const TAny*)&Sqrt, Sqrt_Length());
	User::IMB_Range(pCode,pCode+Sqrt_Length());
	pSqrt=(TSqrtFn)pCode;

	// Do a long test to allow multiple copies of this process to run concurrently
	// Spawn a secondary process
	RProcess p;
	TBuf<16> codeBaseHex;
	codeBaseHex.Format(_L("%08x"),pCode);
	r=p.Create(RProcess().FileName(),codeBaseHex);
	test(r==KErrNone);
	p.Logon(s);
	p.Resume();

	TTime begin;
	begin.HomeTime();
	i=1;
	for (;;)
		{
		TReal x,y,z;
		x=i;
		r=Math::Sqrt(y,x);
		test(r==KErrNone);
		r=pSqrt(z,x);
		test(r==KErrNone);
		test(z==y);
		++i;
		TTime now;
		now.HomeTime();
		if (now.MicroSecondsFrom(begin).Int64()>10000000)
			break;
		User::After(0);//Force rescheduling of the secondary process's thread
		}
	p.Kill(0);
	User::WaitForRequest(s);
	exitType=p.ExitType();
	exitReason=p.ExitReason();
	exitCat=p.ExitCategory();
	CLOSE_AND_WAIT(p);
	test.Printf(_L("SecProc: %d,%d,%S\n"),exitType,exitReason,&exitCat);
	test(exitType==EExitKill);
	test(exitReason==KErrNone);

	// Test heap in code chunk
	RTestHeap* pCodeHeap=(RTestHeap*) UserHeap::ChunkHeap(c,pageSize,pageSize);
	test(pCodeHeap==(RHeap*)c.Base());
	test(c.Size()==pageSize);
	TUint32* pCode3=(TUint32*)pCodeHeap->Alloc(pageSize);
	test(pCode3!=NULL);
	test(c.Size()==2*pageSize);
	TAny* pCode4=pCodeHeap->Alloc(3*pageSize);
	test(pCode4!=NULL);
	test(c.Size()==5*pageSize);
	pCodeHeap->Free(pCode4);
	test(c.Size()==2*pageSize);
	TUint8 * oldTop = pCodeHeap->GetTop();
	pCodeHeap->Free(pCode3);
	TUint8 * newTop = pCodeHeap->GetTop();
	// Under some conditions (KHeapShrinkRatio value is low and iGrowBy is at its default value of a page size) 
	// heap may be reduced at the end of Free() operation
	if (oldTop==newTop) // heap was not reduced
		test(c.Size()==2*pageSize);

	// Test IMB with various base/size values
	pCode3=(TUint32*)pCodeHeap->Alloc(0x20004);
	test(pCode3!=NULL);

	for (i=8; i<1024; i+=32)
		{
		TestIMB(test,pCode3,0,i);
		TestIMB(test,pCode3,4,i);
		}

	for (i=1024; i<131072; i+=844)
		{
		TestIMB(test,pCode3,0,i);
		TestIMB(test,pCode3,4,i);
		}

	c.Close();

	test.End();
	return 0;
	}
#else
GLREF_C TInt E32Main()
	{
	return 0;
	}
#endif