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) 2002-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\debug\d_schedhook.cpp
//
//
#include <kernel/kernel.h>
#include <kernel/kern_priv.h>
#include "platform.h"
#include <kernel/cache.h>
#include <arm.h>
#include "d_schedhook.h"
_LIT(KLddName,"D_SCHEDHOOK");
NThread* TestThread;
TInt TestCount;
const TInt KMajorVersionNumber = 0;
const TInt KMinorVersionNumber = 1;
const TInt KBuildVersionNumber = 1;
class DSchedhookTest;
class DSchedhookTestFactory : public DLogicalDevice
{
public:
DSchedhookTestFactory();
virtual TInt Install();
virtual void GetCaps(TDes8& aDes) const;
virtual TInt Create(DLogicalChannelBase*& aChannel);
};
class DHwExcHandler : public DKernelEventHandler
{
public:
DHwExcHandler() : DKernelEventHandler(HandleEvent, this) {}
private:
static TUint HandleEvent(TKernelEvent aEvent, TAny* a1, TAny* a2, TAny* aThis);
};
class DSchedhookTest : public DLogicalChannelBase
{
public:
virtual ~DSchedhookTest();
protected:
virtual TInt DoCreate(TInt aUnit, const TDesC8* anInfo, const TVersion& aVer);
virtual TInt Request(TInt aFunction, TAny* a1, TAny* a2);
public:
DHwExcHandler* iHwExcHandler;
};
DECLARE_STANDARD_LDD()
{
return new DSchedhookTestFactory;
}
//
// DSchedhookTestFactory
//
DSchedhookTestFactory::DSchedhookTestFactory()
{
iVersion=TVersion(KMajorVersionNumber,KMinorVersionNumber,KBuildVersionNumber);
}
TInt DSchedhookTestFactory::Create(DLogicalChannelBase*& aChannel)
/**
Create a new DSchedhookTest on this logical device
*/
{
aChannel=new DSchedhookTest;
return aChannel ? KErrNone : KErrNoMemory;
}
TInt DSchedhookTestFactory::Install()
/**
Install the LDD - overriding pure virtual
*/
{
return SetName(&KLddName);
}
void DSchedhookTestFactory::GetCaps(TDes8& aDes) const
/**
Get capabilities - overriding pure virtual
*/
{
TCapsTestV01 b;
b.iVersion=TVersion(KMajorVersionNumber,KMinorVersionNumber,KBuildVersionNumber);
Kern::InfoCopy(aDes,(TUint8*)&b,sizeof(b));
}
//
// DHwExcHandler
//
/**
Handle exceptions on our test thread by suspending it
*/
TUint DHwExcHandler::HandleEvent(TKernelEvent aEvent, TAny* a1, TAny* /*a2*/, TAny* /*aThis*/)
{
if (aEvent == EEventHwExc)
{
if(&Kern::CurrentThread().iNThread!=TestThread)
return ERunNext;
TArmExcInfo *pR=(TArmExcInfo*)a1;
pR->iR15+=4;
Kern::ThreadSuspend(Kern::CurrentThread(),1);
return (TUint)EExcHandled;
}
return (TUint)ERunNext;
}
//
// DSchedhookTest
//
TInt DSchedhookTest::DoCreate(TInt /*aUnit*/, const TDesC8* /*aInfo*/, const TVersion& aVer)
/**
Create channel
*/
{
if (!Kern::QueryVersionSupported(TVersion(KMajorVersionNumber,KMinorVersionNumber,KBuildVersionNumber),aVer))
return KErrNotSupported;
iHwExcHandler = new DHwExcHandler;
if (! iHwExcHandler)
return KErrNoMemory;
return iHwExcHandler->Add();
}
DSchedhookTest::~DSchedhookTest()
{
if (iHwExcHandler)
iHwExcHandler->Close();
}
//
// Scheduler Hook functions
//
NThread* CurrentThread=NULL;
void DisableRescheduleCallback()
/**
Stops the Scheduler calling us on every context switch
*/
{
// Prevent rescheduling whilst we disable the callback
NKern::Lock();
// Disable Callback
NKern::SetRescheduleCallback(NULL);
// Invalidate CurrentThread
CurrentThread = NULL;
// Callback now disabled...
NKern::Unlock();
}
void RemoveSchedulerHooks()
/**
Removes the patches added to the Scheduler code.
*/
{
// Make sure callback is disabled (required before removing hooks)
DisableRescheduleCallback();
// Get range of memory used by hooks
TLinAddr start,end;
NKern::Lock();
NKern::SchedulerHooks(start,end);
NKern::Unlock();
// Free shadow pages which cover hooks
TUint32 pageSize=Kern::RoundToPageSize(1);
NKern::ThreadEnterCS();
for(TLinAddr a=start; a<end; a+=pageSize)
Epoc::FreeShadowPage(a); // Ignore errors because we're trying to clean up anyway
NKern::ThreadLeaveCS();
}
TInt InsertSchedulerHooks()
/**
Enables use of the Scheduler callback by using shadow pages to patch the Scheduler code.
*/
{
// Get range of memory used by hooks
TLinAddr start=0,end=0;
NKern::Lock();
NKern::SchedulerHooks(start,end);
NKern::Unlock();
if (start==0 && end==0) return KErrNotSupported;
// Create shadow pages for hooks
TUint32 pageSize=Kern::RoundToPageSize(1);
for(TLinAddr a=start; a<end; a+=pageSize)
{
NKern::ThreadEnterCS();
TInt r=Epoc::AllocShadowPage(a);
NKern::ThreadLeaveCS();
if(r!=KErrNone && r!=KErrAlreadyExists)
{
RemoveSchedulerHooks();
return r;
}
}
// Put hooks in
NKern::Lock();
NKern::InsertSchedulerHooks();
NKern::Unlock();
// Make I and D caches consistant for hook region
Cache::IMB_Range(start,end-start);
return KErrNone;
}
void RescheduleTestFunction()
/**
Test function to be called on each thread reschedule
*/
{
// Count rechedules to the test thread
if(CurrentThread==TestThread)
++TestCount;
}
void RescheduleCallback(NThread* aNThread)
/**
Main scheduler callback.
Called with the Kernel Lock (Preemption Lock) held.
*/
{
#ifndef __SMP__
// The 'CurrentThread' is now unscheduled and has become the 'previous thread'
// Set this thread 'UserContextType'...
CurrentThread->SetUserContextType();
#endif
// Make the newly scheduled thread the CurrentThread
CurrentThread = aNThread;
// Test function
RescheduleTestFunction();
}
void RescheduleCallbackFirst(NThread* aNThread)
/**
Scheduler callback used once for initialisation.
Called with the Kernel Lock (Preemption Lock) held.
*/
{
// Initialise CurrentThread
CurrentThread = aNThread;
// Switch future callbacks to the main RescheduleCallback
NKern::SetRescheduleCallback(RescheduleCallback);
// Test function
RescheduleTestFunction();
}
void EnableRescheduleCallback()
/**
Sets the Scheduler to call us back on every thread reschedule
*/
{
// Reset the User Context Type for all threads, because these values
// will be out-of-date. (It is our Rescheduler callback which set's them.
// and we're just about enable that.)
NKern::ThreadEnterCS(); // Prevent us from dying or suspending whilst holding a DMutex
DObjectCon& threads=*Kern::Containers()[EThread]; // Get containing holding threads
threads.Wait(); // Obtain the container mutex so the list does get changed under us
#ifndef __SMP__
// For each thread...
TInt c=threads.Count();
for (TInt i=0; i<c; i++)
((DThread*)threads[i])->iNThread.ResetUserContextType();
#endif
threads.Signal(); // Release the container mutex
NKern::ThreadLeaveCS(); // End of critical section
// Ask for callback
NKern::SetRescheduleCallback(RescheduleCallbackFirst);
}
TInt GetThreadUserContext(NThread* aThread,TArmRegSet& aContext)
/**
Test function to get a threads User Context
*/
{
// Get the User Context Type which our Reschedule hook set
TInt type = aThread->iSpare3; /*iUserContextType*/
// Get the table corresponding to the User Context Type
const TArmContextElement* c = NThread::UserContextTables()[type];
// Set the User Context by using the table we got
TUint32* sp = (TUint32*)aThread->iSavedSP;
TUint32* st = (TUint32*)((TUint32)aThread->iStackBase+(TUint32)aThread->iStackSize);
TArmReg* out = (TArmReg*)(&aContext);
TArmReg* end = (TArmReg*)(&aContext+1);
do
{
TInt v = c->iValue;
TInt t = c->iType;
++c;
if(t==TArmContextElement::EOffsetFromSp)
v = sp[v];
else if(t==TArmContextElement::EOffsetFromStackTop)
v = st[-v];
*out++ = v;
}
while(out<end);
return type;
}
void DumpContext(TArmRegSet& aContext,TInt aType)
{
Kern::Printf(" Context type %d",aType);
Kern::Printf(" r0 =%08x r1 =%08x r2 =%08x r3 =%08x",aContext.iR0,aContext.iR1,aContext.iR2,aContext.iR3);
Kern::Printf(" r4 =%08x r5 =%08x r6 =%08x r7 =%08x",aContext.iR4,aContext.iR5,aContext.iR6,aContext.iR7);
Kern::Printf(" r8 =%08x r9 =%08x r10=%08x r11=%08x",aContext.iR8,aContext.iR9,aContext.iR10,aContext.iR11);
Kern::Printf(" r12=%08x r13=%08x r14=%08x r15=%08x",aContext.iR12,aContext.iR13,aContext.iR14,aContext.iR15);
Kern::Printf(" cpsr=%08x dacr=%08x",aContext.iFlags, aContext.iDacr);
}
TInt TestGetThreadContext(DThread* aThread,TDes8* aContext)
{
TArmRegSet context1;
TArmRegSet context2;
memclr(&context1,sizeof(context1));
memclr(&context2,sizeof(context2));
NKern::Lock();
TUint32 unused;
NKern::ThreadGetUserContext(&aThread->iNThread,&context1,unused);
TInt r=GetThreadUserContext(&aThread->iNThread,context2);
NKern::Unlock();
DumpContext(context1,-1);
DumpContext(context2,r);
TInt len,maxLen;
Kern::KUDesInfo(*aContext,len,maxLen);
if(maxLen>KMaxThreadContext)
maxLen = KMaxThreadContext;
TPtr8 ptr((TUint8*)&context1,maxLen,maxLen);
Kern::KUDesPut(*aContext,ptr);
for(TUint i=0; i<sizeof(context1); i++)
if(((TUint8*)&context1)[i]!=((TUint8*)&context2)[i])
return KErrGeneral;
return r;
}
DThread* ThreadFromId(TUint aId)
{
// This is risky because the thread could die on us an the DThread* become invalid.
// We are relying on this never happening in our test code.
NKern::ThreadEnterCS(); // Prevent us from dying or suspending whilst holding a DMutex
DObjectCon& threads=*Kern::Containers()[EThread]; // Get containing holding threads
threads.Wait(); // Obtain the container mutex so the list does get changed under us
DThread* thread = Kern::ThreadFromId(aId);
threads.Signal(); // Release the container mutex
NKern::ThreadLeaveCS(); // End of critical section
return thread;
}
TInt DSchedhookTest::Request(TInt aFunction, TAny* a1, TAny* a2)
/**
Handle requests from the test program
*/
{
TInt r=KErrNone;
switch (aFunction)
{
case RSchedhookTest::EInstall:
r=InsertSchedulerHooks();
break;
case RSchedhookTest::EUninstall:
RemoveSchedulerHooks();
return KErrNone;
case RSchedhookTest::EInsertHooks:
{
NKern::Lock();
NKern::InsertSchedulerHooks();
TLinAddr start,end;
NKern::SchedulerHooks(start,end);
NKern::Unlock();
Cache::IMB_Range(start,end-start);
}
return KErrNone;
case RSchedhookTest::ERemoveHooks:
{
NKern::Lock();
NKern::SetRescheduleCallback(NULL);
NKern::RemoveSchedulerHooks();
TLinAddr start,end;
NKern::SchedulerHooks(start,end);
NKern::Unlock();
Cache::IMB_Range(start,end-start);
}
return KErrNone;
case RSchedhookTest::EEnableCallback:
NKern::Lock();
NKern::SetRescheduleCallback(RescheduleCallbackFirst);
NKern::Unlock();
return KErrNone;
case RSchedhookTest::EDisableCallback:
DisableRescheduleCallback();
return KErrNone;
case RSchedhookTest::ESetTestThread:
{
NKern::ThreadEnterCS(); // Prevent us from dying or suspending whilst holding a DMutex
DObjectCon& threads=*Kern::Containers()[EThread]; // Get containing holding threads
threads.Wait(); // Obtain the container mutex so the list does get changed under us
TestThread = &ThreadFromId((TUint)a1)->iNThread;
threads.Signal(); // Release the container mutex
NKern::ThreadLeaveCS(); // End of critical section
TestCount = 0;
}
break;
case RSchedhookTest::EGetThreadContext:
return TestGetThreadContext(ThreadFromId((TUint)a1),(TDes8*)a2);
case RSchedhookTest::EGetTestCount:
return TestCount;
default:
r=KErrNotSupported;
break;
}
return r;
}