diff -r 000000000000 -r 96e5fb8b040d kerneltest/e32test/mmu/t_imb.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kerneltest/e32test/mmu/t_imb.cpp Thu Dec 17 09:24:54 2009 +0200 @@ -0,0 +1,358 @@ +// 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 +#include "u32std.h" +#include + +#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