libraries/memoryaccess/fdebuggerkernel.cpp
author Tom Sutcliffe <thomas.sutcliffe@accenture.com>
Mon, 12 Jul 2010 15:23:23 +0100
changeset 9 257450419d10
parent 0 7f656887cf89
child 89 17bed177107f
permissions -rw-r--r--
Fixes for GCC-E 4.4.1 and updated docs.

// fdebuggerkernel.cpp
// 
// Copyright (c) 2010 Accenture. All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the "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:
// Accenture - Initial contribution
//
#include "fdebuggerkernel.h"
#include "DynamicDfcSupport.h"
#ifdef __MARM__
#	include <arm/arm.h>
#	if defined(FSHELL_ARM11XX_SUPPORT) || defined(FSHELL_ARM_MEM_MAPPED_DEBUG)
//#		ifndef __MEMMODEL_MULTIPLE__
//#			error "FSHELL_ARM11XX_SUPPORT is only supported on platforms using the multiple memory model!"
//#		endif
#	include <multiple/memmodel.h>
#	endif
#endif
#include "memoryaccess.h"

#if !defined(__EABI__) && defined(FSHELL_ARM_MEM_MAPPED_DEBUG)
#undef FSHELL_ARM_MEM_MAPPED_DEBUG
#endif

#ifdef ASSERT
#undef ASSERT
#endif
#ifdef __WINS__
#define ASSERT(x) __ASSERT_ALWAYS((x), Kern::Fault("Assertion failed: " #x, __LINE__))
#else
#define ASSERT(x) if (!(x)) { Kern::Printf("Assertion failed @ %d: " #x, __LINE__); NKern::Sleep(NKern::TimerTicks(5000)); }
#endif

#define ASSERT_LOCKED() ASSERT(Kern::CurrentThread().iNThread.iHeldFastMutex == &iLock)
#define ASSERT_UNLOCKED() ASSERT(Kern::CurrentThread().iNThread.iHeldFastMutex == NULL)
#define ASSERT_BREAKPOINT_LOCKED() ASSERT(iBreakpointMutex->iCleanup.iThread == &Kern::CurrentThread());
#define ASSERT_BREAKPOINT_UNLOCKED() ASSERT(iBreakpointMutex->iCleanup.iThread != &Kern::CurrentThread());

//#define LOG(args...) Kern::Printf(args)
#define LOG(args...)

void MCR_SetContextIdBrp(TInt aRegister, TUint aContextId);
void MCR_SetBreakpointPair(TInt aRegister, TUint aBvrValue, TUint aBcrValue);
TUint MRC_ReadBcr(TInt aRegister);
TUint32 GetDscr();
void MCR_SetDscr(TUint32 aVal);
TUint32 GetDsar();
TUint32 GetDrar();
TUint32 GetContextId();
void Dsb();
void Isb();
void Imb();

enum TMemMappedDebugAddresses
	{
	EDscrOffset = 0x88,
	EBvrOffset = 0x100,
	EBcrOffset = 0x140,
	ELockAccessOffset = 0xFB0,
	EAuthStatusOffset = 0xFB8,
	};


DDebuggerEventHandler* DDebuggerEventHandler::New(TDfcQue* aQue)
	{
	// This is backwards from the usual constructor, 2nd phase construction pattern because I can't do anything that could
	// error after creating a DKernelEventHandler, because we get called from DLogicalDevice::Install(). So the stuff that would normally
	// be done as 2nd-phase construction is done first

	DMutex* breakpointMutex = NULL;
	TInt err = Kern::MutexCreate(breakpointMutex, _L("FDebuggerBreakpointMutex"), KMutexOrdGeneral5); // No special reason for using 5
	if (err) return NULL;

	DDebuggerEventHandler* self = new DDebuggerEventHandler(aQue);
	if (self)
		{
		self->iBreakpointMutex = breakpointMutex;
		self->Add();
		}
	else
		{
		breakpointMutex->Close(NULL);
		}
	return self;
	}

DDebuggerEventHandler::DDebuggerEventHandler(TDfcQue* aQue)
	: DKernelEventHandler(&Event, this), iNextBreakpointId(1), iHandleCodesegRemovedDfc(&HandleCodesegRemoved, this, aQue, 0)
	{
#if defined(FSHELL_ARM11XX_SUPPORT) || defined(FSHELL_ARM_MEM_MAPPED_DEBUG)
	iFreeHwBreakpoints = 0x3F; // BRPs 0,1,2,3,4,5 (at a minimum) are supported on ARM11xx or later
#endif
	}

/*static*/ TUint DDebuggerEventHandler::Event(TKernelEvent aEvent, TAny* a1, TAny* a2, TAny* aPrivateData)
	{
	DDebuggerEventHandler* self = static_cast<DDebuggerEventHandler*>(aPrivateData);
	return self->DoEvent(aEvent, a1, a2);
	}

TUint DDebuggerEventHandler::DoEvent(TKernelEvent aEvent, TAny* a1, TAny* a2)
	{
	//if (aEvent != EEventUserTrace) Kern::Printf("fdbk: Event %d a1=%d a2=%d", aEvent, a1, a2);

	if (aEvent == EEventKillThread)
		{
#ifdef __MARM__
		LOG("Thread %x %O with contextId %x killed", &Kern::CurrentThread().iNThread, &Kern::CurrentThread(), GetContextId());
#endif
		if (iZombieMode == EAllExits || (iZombieMode == EAbnormalExit && Kern::CurrentThread().iExitType != EExitKill))
			{
			// The thread in question is Kern::CurrentThread()
			Zombify();
			}
		RemoveAllHardwareBreakpointsForThread(&Kern::CurrentThread()); // The thread ID could get reused so make sure we clean up
		}
	else if (aEvent == EEventHwExc)
		{
		// Breakpoint?
#ifdef __MARM__
		TArmExcInfo* info = (TArmExcInfo*)a1;
		LOG("fdbk: Exception excCode=%d addr=0x%08x", info->iExcCode, info->iR15);
		SBreakpoint* b = NULL;
		TLinAddr excAddr = info->iR15 & ~1;
		BreakpointLock();
		if (info->iExcCode == 0)
			{
			// EArmExceptionPrefetchAbort - Could be a HW breakpoint
			b = FindHardwareBreakpoint(&Kern::CurrentThread(), excAddr);
			}
		else if (info->iExcCode == 2)
			{
			// EArmExceptionUndefinedOpcode - SW breakpoint?
			b = FindBreakpointByAddress(excAddr);
			}
		
		LOG("fdbk: Found breakpoint %d", b ? b->iBreakpointId : -1);

		if (b == NULL)
			{
			// Not one of ours, we should allow it to blow up (or be handled by someone else)
			// It could of course be a thread hitting a breakpoint we've just removed, in which case I'm not sure what we can do other than let
			// the thread crash... hopefully this won't happen in practice!
			BreakpointUnlock();
			return ERunNext;
			}

		TBool shouldBreak = b->MatchesThread(&Kern::CurrentThread()); // Check if it's the wrong thread
		if (b->iFlags & SBreakpoint::ETempContinue)
			{
			// It's a temporary break-at-next-instruction used while continuing a thread.
			if (shouldBreak)
				{
				// Excellent, we have sucessfully continued from a breakpoint. Restore the original, clear the temp (and any threads waiting on it), resume this thread and we're done
				SBreakpoint* orig = b->iRealBreakpoint;
				if (orig->IsHardware()) ClearBreakpoint(b, ETrue); // Clear HW first 
				TInt err = KErrNone;
				if ((orig->iFlags & SBreakpoint::EDisabled) == SBreakpoint::EDisabledDuringContinue)
					{
					// Only resume it if it hasn't been disabled for some other reason in the meantime
					err = ApplyBreakpoint(orig);
					}

				if (err)
					{
					Kern::Printf("fdbk: failed to re-enable breakpoint id %d", orig->iBreakpointId);
					// What to do?
					}
				else
					{
					// Clear the disabled flag
					orig->iFlags &= ~SBreakpoint::EDisabledDuringContinue;
					}

				if (!orig->IsHardware()) ClearBreakpoint(b, ETrue);
				BreakpointUnlock();
				return (TUint)EExcHandled;
				}
			else
				{
				// Any other threads unlucky enough to hit our temp breakpoint will just have to wait until we see our target thread - otherwise
				// we'll end up with temp breakpoints for the temp breakpoints and the universe will implode.
				BreakpointUnlock();
				Zombify(excAddr);
				return (TUint)EExcHandled;
				}
			}
		TInt id = b->iBreakpointId;
		BreakpointUnlock();
		if (shouldBreak)
			{
			// TODO should we suspend the thread rather than semaphoring it, to be more efficient?
			if (iBreakpointNotifyClient)
				{
				RMemoryAccess::TBreakpointNotification notif;
				notif.iThreadId = Kern::CurrentThread().iId;
				notif.iBreakpointId = id;
				notif.iAddress = excAddr;
				TPckg<RMemoryAccess::TBreakpointNotification> pkg(notif);
				// We shouldn't really pass blobs of data...
				iBreakpointNotifyClient->BreakpointHit(pkg);
				}
			Zombify(excAddr);
			}
		else
			{
			TInt err = ContinueFromBreakpoint(&Kern::CurrentThread(), excAddr);
			if (err) return ERunNext; // If we failed to continue, we shouldn't pretend we've handled it
			}
		return (TUint)DKernelEventHandler::EExcHandled;
#else
		(void)a1;
#endif
		}
	else if (aEvent == EEventRemoveCodeSeg)
		{
		DCodeSeg* codeseg = (DCodeSeg*)a1;
		DProcess* proc = (DProcess*)a2;
		// We can't scan the breakpoint list at this point, because we'd need to call BreakpointLock() and 
		// we're currently holding the codeseg lock. So queue a DFC.
		SRemovedCodeseg* removed = new SRemovedCodeseg;
		if (removed)
			{
			removed->iCodeseg = codeseg;
			removed->iProcess = proc;
			Lock();
			iRemovedCodesegs.Add(&removed->iLink);
			Unlock();
			NKern::ThreadEnterCS();
			iHandleCodesegRemovedDfc.Enque();
			NKern::ThreadLeaveCS();
			}
		}
	else if (aEvent == EEventRemoveThread)
		{
		DThread* thread = (DThread*)a1;
		TCreatorInfo dummy(thread->iId, 0);
		BreakpointLock(); // It's not actually breakpoint related but never mind
		TInt found = iCreatorInfo.FindInUnsignedKeyOrder(dummy);
		if (found != KErrNotFound)
			{
			iCreatorInfo.Remove(found);
			}
		BreakpointUnlock();
		}
	else if (aEvent == EEventRemoveProcess)
		{
		// In case a thread dies before creating its first thread, try to remove the creator info we stashed
		DProcess* proc = (DProcess*)a1;
		TCreatorInfo dummy(proc->iId, 0);
		BreakpointLock();
		TInt found = iCreatorInfo.FindInUnsignedKeyOrder(dummy);
		if (found != KErrNotFound)
			{
			iCreatorInfo.Remove(found);
			}
		BreakpointUnlock();
		}
	else if (aEvent == EEventAddProcess)
		{
		// We need to use EEventAddProcess as well as EEventAddThread, because EEventAddThread doesn't do the special check for the creator not being the current thread, meaning for process creation the main thread always appears to have been created by the loader and not be the person who created the process
		// Remember the process for when we come to add the thread
		DProcess* process = (DProcess*)a1;
		DThread* creator = (DThread*)a2;
		BreakpointLock();
		TCreatorInfo threadInfo(process->iId, creator->iId);
		TInt err = iCreatorInfo.InsertInUnsignedKeyOrder(threadInfo);
		if (err)
			{
			LOG("Failed to InsertInUnsignedKeyOrder err=%d", err);
			}
		BreakpointUnlock();
		}
	else if (aEvent == EEventAddThread)
		{
		DThread* thread = (DThread*)a1;
		DThread* creator = (DThread*)a2;
		TCreatorInfo threadInfo(thread->iId, creator->iId);

		BreakpointLock(); // It's not actually breakpoint related but never mind
		if (thread->Owner() != creator->Owner())
			{
			// This means we're probably the first thread of a new process, and our 'creator' is the loader thread. Use the info we stashed earlier about the process's creator instead
			TCreatorInfo dummy(((DProcess*)thread->Owner())->iId, 0);
			TInt found = iCreatorInfo.FindInUnsignedKeyOrder(dummy);
			if (found != KErrNotFound)
				{
				threadInfo.iCreatorThreadId = iCreatorInfo[found].iCreatorThreadId;
				// We don't need to track the process creator for anything else
				iCreatorInfo.Remove(found);
				}
			}
		TInt err = iCreatorInfo.InsertInUnsignedKeyOrder(threadInfo);
		if (err)
			{
			LOG("Failed to InsertInUnsignedKeyOrder err=%d", err);
			}
		BreakpointUnlock();
		}
	return ERunNext;
	}

TInt DDebuggerEventHandler::Zombify(TLinAddr aBreakpointAddr)
	{
	// The purpose of this code is to prevent dying threads from taking down their address space, so that we can still poke at their memory
	// Currently, it is also used to pause a thread on a breakpoint
	SZombie zom; // zombies go on the stack of the thread that is being halted. That way avoids having to alloc.
	zom.iThread = &Kern::CurrentThread();
	zom.iBlocker = NULL;
	zom.iBreakpointAddress = aBreakpointAddr;
	TBuf8<32> semName;
	semName.Append(_L8("ThreadZombiefier-"));
	semName.AppendNum((TUint)zom.iThread->iId);
	NKern::ThreadEnterCS();
	TInt err = Kern::SemaphoreCreate(zom.iBlocker, semName, 0);
	if (err)
		{
		NKern::ThreadLeaveCS();
		return err;
		}
	Lock();
	iZombies.Add(&zom.iLink);
	iZombieCount++;
	Unlock();
	zom.iThread->Open(); // So no-one is tempted to destroy it
	NKern::ThreadLeaveCS();
	Kern::SemaphoreWait(*zom.iBlocker);
	// The above blocks until a "fdb detach" or equivalent has happened, at which point we'll get signalled on this semaphore and should clean up
	zom.iBlocker->Close(NULL);
	zom.iThread->Close(NULL);
	return KErrNone;
	}

TInt DDebuggerEventHandler::GetZombieMode()
	{
	Lock();
	TInt res = iZombieMode;
	Unlock();
	return res;
	}

TInt DDebuggerEventHandler::SetZombieMode(TInt aMode)
	{
	Lock();
	iZombieMode = (TZombieMode)aMode;
	Unlock();
	if (aMode == EDisabled)
		{
		ClearAllBreakpoints(); // This is not exactly obvious, but unblocking a zombie that's blocked on a breakpoint necessarily means you have to remove the breakpoint
		CompleteZombies();
		}
	
	return KErrNone;
	}

void DDebuggerEventHandler::CompleteZombies()
	{
	ASSERT_UNLOCKED();
	// enter and exit with lock not held
	for (;;)
		{
		Lock();
		SDblQueLink* link = iZombies.IsEmpty() ? NULL : iZombies.First();
		if (!link)
			{
			Unlock();
			break;
			}
		SZombie* zom = _LOFF(link, SZombie, iLink);
		ReleaseZombieAndUnlock(zom);
		}
	}

void DDebuggerEventHandler::UnsuspendThread(SZombie* aZombie)
	{
	Kern::ThreadResume(*aZombie->iThread);
	aZombie->iThread->Close(NULL);
	delete aZombie;
	}

TInt DDebuggerEventHandler::SuspendThread(DThread* aThread)
	{
	// This is the only case where an SZombie goes on the heap - usually they are created in the context of their thread so go on that thread's stack
	SZombie* zom = new SZombie;
	if (!zom) return KErrNoMemory;
	zom->iThread = aThread;
	zom->iThread->Open();
	zom->iBlocker = NULL;
	Lock();
	iZombies.Add(&zom->iLink);
	iZombieCount++;
	Unlock();
	Kern::ThreadSuspend(*zom->iThread, 1);
	return KErrNone;
	}

void DDebuggerEventHandler::Lock()
	{
	NKern::FMWait(&iLock);
	}

void DDebuggerEventHandler::Unlock()
	{
	NKern::FMSignal(&iLock);
	}

DDebuggerEventHandler::~DDebuggerEventHandler()
	{
	// The call to SetZombieMode below is actually pretty pointless: the kernel increments our access count while calling
	// our Event function, which means DKernelEventHandler::Close will never get as far as our destructor
	// while we are blocking on zombies.
	SetZombieMode(EDisabled); // This frees up any outstanding zombies via CompleteZombies()

	iHandleCodesegRemovedDfc.Cancel();

	if (iCodeModifierInited)
		{
#ifdef __EPOC32__
		DebugSupport::CloseCodeModifier();
#endif
		}

	if (iBreakpointMutex)
		{
		iBreakpointMutex->Close(NULL);
		}
#ifdef FSHELL_ARM_MEM_MAPPED_DEBUG
	if (iDebugRegistersChunk)
		{
		iDebugRegistersChunk->Close(NULL);
		}
#endif
	iCreatorInfo.Close();
	}

HBuf* DDebuggerEventHandler::GetZombieThreadIds()
	{
	TInt count = iZombieCount;
	TInt size = count*sizeof(RMemoryAccess::TZombieInfo);
	HBuf* result = HBuf::New(size);
	if (!result) return NULL;
	result->SetLength(size);
	RMemoryAccess::TZombieInfo* ptr = (RMemoryAccess::TZombieInfo*)result->Ptr();
	Lock();
	TInt i = 0;
	for (SDblQueLink* link = iZombies.First(); link != NULL && link != &iZombies.iA && i < count; i++, link=link->iNext)
		{
		SZombie& zom = *_LOFF(link, SZombie, iLink);
		ptr[i].iThreadId = zom.iThread->iId;
		ptr[i].iFlags = 0;
		if (zom.iBlocker == NULL) ptr[i].iFlags |= RMemoryAccess::TZombieInfo::ESuspended;
		else if (zom.iBreakpointAddress != 0) ptr[i].iFlags |= RMemoryAccess::TZombieInfo::EBreakpoint;
		}
	Unlock();
	return result;
	}

DDebuggerEventHandler::SZombie* DDebuggerEventHandler::FindZombie(DThread* aThread)
	{
	// Enter and leave locked
	ASSERT_LOCKED();
	for (SDblQueLink* link = iZombies.First(); link != NULL && link != &iZombies.iA; link=link->iNext)
		{
		SZombie* zom = _LOFF(link, SZombie, iLink);
		if (zom->iThread == aThread)
			{
			return zom;
			}
		}
	return NULL;
	}

TInt DDebuggerEventHandler::ReleaseZombie(DThread* aThread)
	{
	ASSERT_UNLOCKED();
	Lock();
	SZombie* found = FindZombie(aThread);
	if (found)
		{
		if (found->iBreakpointAddress)
			{
			// It's actually paused on a breakpoint, so we need to do a continue instead
			Unlock();
			return ContinueFromBreakpoint(aThread, 0);
			}
		else
			{
			ReleaseZombieAndUnlock(found);
			return KErrNone;
			}
		}
	else
		{
		Unlock();
		return KErrNotFound;
		}
	}

void DDebuggerEventHandler::ReleaseZombieAndUnlock(SZombie* aZombie)
	{
	ASSERT_LOCKED();
	ReleaseZombie(aZombie);
	Unlock();
	}

void DDebuggerEventHandler::ReleaseZombie(SZombie* aZombie)
	{
	ASSERT_LOCKED();
	aZombie->iLink.Deque();
	iZombieCount--;
	if (aZombie->iBlocker)
		{
		Kern::SemaphoreSignal(*aZombie->iBlocker);
		}
	else
		{
		UnsuspendThread(aZombie);
		}
	}

TInt DDebuggerEventHandler::RegisterForBreakpointNotification(MDebuggerEventClient* aClient)
	{
	Lock();
	TInt err = KErrAlreadyExists;
	if (iBreakpointNotifyClient == NULL || iBreakpointNotifyClient == aClient)
		{
		err = KErrNone;
		iBreakpointNotifyClient = aClient;
		}
	Unlock();
	return err;
	}

void DDebuggerEventHandler::UnregisterForBreakpointNotification(MDebuggerEventClient* aClient)
	{
	Lock();
	if (aClient == iBreakpointNotifyClient)
		{
		iBreakpointNotifyClient = NULL;
		}
	Unlock();
	}

TInt DDebuggerEventHandler::SetBreakpoint(DThread* aThread, TLinAddr aAddress, const RMemoryAccess::TPredicate& aCondition)
	{
	TInt codemodifierErr = KErrNone;
	if (!iCodeModifierInited)
		{
#ifdef __EPOC32__
		TUint caps;
		const TInt KMaxBreakpoints = 32;
		codemodifierErr = DebugSupport::InitialiseCodeModifier(caps, KMaxBreakpoints);
#else
		codemodifierErr = KErrNotSupported;
#endif
		if (!codemodifierErr) iCodeModifierInited = ETrue;
		// It's not necessarily fatal that we can't init the code modifier - we may still be able to set a H/W breakpoint
		}

	SBreakpoint* b = new SBreakpoint(aThread, iNextBreakpointId, aAddress, aCondition); // iNextBreakpointId gets incremented later
	if (!b) return KErrNoMemory;

	// Get the codeseg for this address
	Kern::AccessCode();
	b->iCodeSeg = Kern::CodeSegFromAddress(b->iAddress, b->iThread->iOwningProcess);
	Kern::EndAccessCode();

	BreakpointLock();
	
	// Check if there's already a HW breakpoint for this thread and address - we don't allow duplicates, just for sanity's sake
	SBreakpoint* existingHwBreakpoint = FindHardwareBreakpoint(b->iThread, b->iAddress);
	if (existingHwBreakpoint)
		{
		BreakpointUnlock();
		delete b;
		return KErrAlreadyExists;
		}

	TBreakpointPolicy policy = EWhatever;
	// Check if we already have a breakpoint for this address. Because all software breakpoints are global, if we add repeated breakpoints on the same address (but on threads in different processes) DebugSupport will happily allow it and create nested nastyness
	SBreakpoint* existingBreakpoint = FindBreakpointByAddress(b->iAddress);
	if (existingBreakpoint)	policy = EHardwareOnly; // To prevent another SW breakpoint at this location. Multiple HW breakpoints are fine because they're per-thread
	if (codemodifierErr != KErrNone) policy = EHardwareOnly; // If we failed to init code modifier, can only do h/w

	iBreakpoints.Add(&b->iLink);
	TInt err = ApplyBreakpoint(b, policy);
	if (err && existingBreakpoint)
		{
		// If we couldn't set a HW breakpoint, no worries, fall back to relying on the existing SW breakpoint
		b->iRealBreakpoint = existingBreakpoint;
		err = KErrNone;
		}

	if (err < 0)
		{
		b->iLink.Deque();
		delete b;
		}
	else
		{
		iNextBreakpointId++; // Now we know we're actually using it
		}

	BreakpointUnlock();
	if (err)
		{
		return err;
		}
	else
		{
		TInt result = b->iBreakpointId;
		if (b->IsHardware()) result |= RMemoryAccess::TBreakpointInfo::EHardware;
		return result;
		}
	}

TInt DDebuggerEventHandler::SetSymbolicBreakpoint(DThread* aThread, HBuf* aCodesegName, TUint32 aOffset, const RMemoryAccess::TPredicate& aCondition)
	{
	// See if the codeseg is currently loaded
	Kern::AccessCode();
	SDblQue* list = Kern::CodeSegList();
	for (SDblQueLink* link = list->First(); link != &list->iA; link = link->iNext)
		{
		DCodeSeg* codeSeg = _LOFF(link, DCodeSeg, iLink);
		if (codeSeg->iRootName == *aCodesegName)
			{
			TUint addr = codeSeg->iRunAddress + aOffset;
			Kern::EndAccessCode();
			TInt res = SetBreakpoint(aThread, addr, aCondition);
			if (res >= KErrNone) delete aCodesegName;
			return res;
			}
		}
	Kern::EndAccessCode();
	// If we get here, codeseg isn't currently loaded so need to create it as a pending breakpoint

	SBreakpoint* b = new SBreakpoint(aThread, iNextBreakpointId++, aOffset, aCondition);
	if (!b) return KErrNoMemory;
	b->iCodeSeg = aCodesegName;
	b->iFlags |= SBreakpoint::EDisabledPendingCodesegLoad;
	BreakpointLock();
	iBreakpoints.Add(&b->iLink);
	BreakpointUnlock();
	return KErrNone;
	}

TInt DDebuggerEventHandler::ApplyBreakpoint(SBreakpoint* aBreakpoint, TBreakpointPolicy aPolicy)
	{
	ASSERT_BREAKPOINT_LOCKED();
	// These magic constants don't appear to be defined anywhere, they're just undefined instructions that will cause an exception that we can then handle
	const TUint32 KArmBreakPoint = 0xE7F123F4;
	const TUint16 KThumbBreakPoint = 0xDE56;
	TUint32 inst = KArmBreakPoint;
	TInt instSize = 4;
	if (aBreakpoint->iFlags & SBreakpoint::EThumb)
		{
		inst = KThumbBreakPoint;
		instSize = 2;
		}

#ifdef __EPOC32__
	// Before modifying stuff, save the original instruction (needed for ReadInstructions())
	TInt err = Kern::ThreadRawRead(aBreakpoint->iThread, (TAny*)aBreakpoint->iAddress, (TAny*)aBreakpoint->iOrigInstruction.Ptr(), instSize);
	if (!err)
		{
		aBreakpoint->iOrigInstruction.SetLength(instSize);
		if (aPolicy != ESoftwareOnly)
			{
			err = ApplyHardwareBreakpoint(aBreakpoint);
			// If we manage to set a hardware breakpoint, we don't need to call ModifyCode
			if (err == KErrNone)
				{
				LOG("HW breakpoint set ok");
				aBreakpoint->iFlags |= SBreakpoint::EHardware;
				}
			else
				{
				Kern::Printf("Failed to set hardware breakpoint - %d", err);
				}
			}

		if (aPolicy != EHardwareOnly && !aBreakpoint->IsHardware())
			{
			// Last check that we don't conflict with another breakpoint
			err = (FindBreakpointByAddress(aBreakpoint->iAddress) == NULL ? KErrNone : KErrAlreadyExists);
			if (!err) err = DebugSupport::ModifyCode(aBreakpoint->iThread, aBreakpoint->iAddress, instSize, inst, DebugSupport::EBreakpointGlobal);
			if (err > 0) err = KErrNone; // Really don't care about what the breakpoint type is
			}
		}
#else
	TInt err = KErrNotSupported;
	(void)aPolicy;
#endif
	return err;
	}

DDebuggerEventHandler::SBreakpoint* DDebuggerEventHandler::FindBreakpointByAddress(/*DThread* aThread,*/ TLinAddr aAddress)
	{
	// Enter and leave holding lock
	// This only finds 'real' breakpoints, ie ones which have a ModifyCode outstanding, or are persistant. (NOT hardware breakpoints)
	ASSERT_BREAKPOINT_LOCKED();
	for (SDblQueLink* link = iBreakpoints.First(); link != NULL && link != &iBreakpoints.iA; link=link->iNext)
		{
		SBreakpoint* b = _LOFF(link, SBreakpoint, iLink);
		if (/*b->iThread == aThread &&*/ b->iAddress == aAddress && (b->HasModifiedCode() || (b->iFlags & SBreakpoint::EPersistant)))
			{
			return b;
			}
		}
	return NULL;
	}

DDebuggerEventHandler::SBreakpoint* DDebuggerEventHandler::FindHardwareBreakpoint(DThread* aThread, TLinAddr aAddress)
	{
	// Enter and leave holding lock
	ASSERT_BREAKPOINT_LOCKED();
	for (SDblQueLink* link = iBreakpoints.First(); link != NULL && link != &iBreakpoints.iA; link=link->iNext)
		{
		SBreakpoint* b = _LOFF(link, SBreakpoint, iLink);
		if (b->iThread == aThread && b->iAddress == aAddress && b->IsHardware())
			{
			return b;
			}
		}
	return NULL;
	}

DDebuggerEventHandler::SBreakpoint* DDebuggerEventHandler::FindBreakpointById(TInt aId)
	{
	// Enter and leave holding lock
	ASSERT_BREAKPOINT_LOCKED();
	for (SDblQueLink* link = iBreakpoints.First(); link != NULL && link != &iBreakpoints.iA; link=link->iNext)
		{
		SBreakpoint* b = _LOFF(link, SBreakpoint, iLink);
		if (b->iBreakpointId == aId)
			{
			return b;
			}
		}
	return NULL;
	}

DDebuggerEventHandler::SBreakpoint* DDebuggerEventHandler::FindBreakpointUsingHardwareContextRegister(TInt aRegister)
	{
	// Enter and leave holding lock
	ASSERT_BREAKPOINT_LOCKED();
	for (SDblQueLink* link = iBreakpoints.First(); link != NULL && link != &iBreakpoints.iA; link=link->iNext)
		{
		SBreakpoint* b = _LOFF(link, SBreakpoint, iLink);
		if (b->IsHardware() && b->iHardwareBreakpointContextReg == aRegister)
			{
			return b;
			}
		}
	return NULL;
	}

TInt DDebuggerEventHandler::SetBreakpointEnabled(TInt aBreakpointId, TBool aFlag)
	{
	BreakpointLock();
	SBreakpoint* b = FindBreakpointById(aBreakpointId);
	if (!b)
		{
		BreakpointUnlock();
		return KErrNotFound;
		}

	TInt err = KErrNone;
	if (aFlag)
		{
		if (b->iFlags & SBreakpoint::EDisabled == SBreakpoint::EDisabledByUser)
			{
			// Provided it's not disabled for any reason other than the user asked for it, re-enable it
			err = ApplyBreakpoint(b);
			if (err == KErrNone) b->iFlags |= ~SBreakpoint::EDisabledByUser;
			}
		else
			{
			err = KErrNotReady;
			}
		}
	else
		{
		if (!(b->iFlags & SBreakpoint::EDisabled))
			{
			// If it's not already disabled for any reason, disable it
			UnapplyBreakpoint(b);
			}
		b->iFlags |= SBreakpoint::EDisabledByUser;
		}
	BreakpointUnlock();
	return KErrNone;
	}

TInt DDebuggerEventHandler::ClearBreakpoint(TInt aBreakpointId)
	{
	BreakpointLock();
	SBreakpoint* b = FindBreakpointById(aBreakpointId);
	if (!b)
		{
		BreakpointUnlock();
		return KErrNotFound;
		}

	ClearBreakpoint(b);
	BreakpointUnlock();
	return KErrNone;
	}

void DDebuggerEventHandler::ClearBreakpoint(SBreakpoint* aBreakpoint, TBool aResumeAllBlocked /*=EFalse*/)
	{
	// enter and leave locked
	ASSERT_BREAKPOINT_LOCKED();
	TLinAddr addr = aBreakpoint->iAddress;
	aBreakpoint->iLink.Deque();
	UnapplyBreakpoint(aBreakpoint);
	delete aBreakpoint;

	if (aResumeAllBlocked)
		{
		Lock();
		for (SDblQueLink* link = iZombies.First(); link != NULL && link != &iZombies.iA; link=link->iNext)
			{
			SZombie* zom = _LOFF(link, SZombie, iLink);
			if (zom->iBreakpointAddress == addr)
				{
				ReleaseZombie(zom);
				}
			}
		Unlock();
		}
	}

void DDebuggerEventHandler::UnapplyBreakpoint(SBreakpoint* aBreakpoint)
	{
#ifdef __EABI__
	if (aBreakpoint->HasModifiedCode())
		{
		DebugSupport::RestoreCode(aBreakpoint->iThread, aBreakpoint->iAddress);
		HandleRestoreCode(aBreakpoint->iAddress);
		}
	if (aBreakpoint->IsHardware())
		{
#ifdef __SMP__
		TInt num = NKern::NumberOfCpus();
		TUint32 origAffinity = 0;
		for (TInt i = 0; i < num; i++)
			{
			TUint32 affinity = NKern::ThreadSetCpuAffinity(&Kern::CurrentThread().iNThread, i);
			if (i == 0) origAffinity = affinity;
			DoClearHardwareBreakpoint(aBreakpoint);
			}
		NKern::ThreadSetCpuAffinity(&Kern::CurrentThread().iNThread, origAffinity);
#else
		DoClearHardwareBreakpoint(aBreakpoint);
#endif // __SMP__
		iFreeHwBreakpoints |= (1 << aBreakpoint->iHardwareBreakpointId);
		}
#else
	(void)aBreakpoint;
#endif // __EABI__
	}

#ifdef __EABI__
void DDebuggerEventHandler::DoClearHardwareBreakpoint(SBreakpoint* aBreakpoint)
	{
	TUint32 bcr = ReadBcr(aBreakpoint->iHardwareBreakpointId);
	bcr = bcr & ~1; // Clear the enabled bit
	SetBreakpointPair(aBreakpoint->iHardwareBreakpointId, 0, bcr);
	}
#endif

void DDebuggerEventHandler::ClearAllBreakpoints()
	{
	ASSERT_UNLOCKED();
	ASSERT_BREAKPOINT_UNLOCKED();
	// enter and exit with lock not held
	BreakpointLock();
	for (;;)
		{
		SDblQueLink* link = iBreakpoints.IsEmpty() ? NULL : iBreakpoints.First();
		if (!link)
			{
			break;
			}
		SBreakpoint* b = _LOFF(link, SBreakpoint, iLink);
		ClearBreakpoint(b);
		}
	BreakpointUnlock();
	}

TInt DDebuggerEventHandler::ContinueFromBreakpoint(DThread* aThread, TLinAddr aExceptionAddress)
	{
	// First find the SZombie
	SZombie* zombie = NULL;
	TLinAddr breakpointAddr = 0;
	// If aExceptionAddress is non-zero, it means we're being called from inside the exception handler and we haven't actually created a zombie
	if (aExceptionAddress)
		{
		breakpointAddr = aExceptionAddress;
		}
	else
		{
		Lock();
		zombie = FindZombie(aThread);
		if (!zombie)
			{
			Unlock();
			return KErrNotFound;
			}
		else if (zombie->iBreakpointAddress == 0)
			{
			// It's a zombie but not one on a breakpoint
			Unlock();
			return KErrNotReady;
			}
		// Deque early while we still hold lock, twiddle the pointers so the deque in ReleaseZombieAndUnlock won't break
		zombie->iLink.Deque();
		zombie->iLink.iPrev = &zombie->iLink;
		zombie->iLink.iNext = &zombie->iLink;
		breakpointAddr = zombie->iBreakpointAddress;
		Unlock();
		}

	BreakpointLock();
	SBreakpoint* breakpoint = FindHardwareBreakpoint(aThread, breakpointAddr);
	if (breakpoint == NULL) breakpoint = FindBreakpointByAddress(breakpointAddr);
	// This could be null if someone has already cleared the breakpoint
	if (!breakpoint)
		{
		BreakpointUnlock();
		if (zombie)
			{
			Lock();
			ReleaseZombieAndUnlock(zombie);
			}
		return KErrNone;
		}
	// the above logic doesn't handle if someone has cleared a persistant breakpoint that aThread is on - but if
	// the user has done that then they're on their own.
	// (Mitigated by us not listing persistant breakpoints in the GetBreakpoints fn)
	if (breakpoint->iFlags & SBreakpoint::EPersistant)
		{
		// It's a persistant breakpoint, so we don't actually want to clear the breakpoint we just need to modify the PC to step over the break instruction
		TLinAddr breakAddress = breakpoint->iAddress;
		TInt err = SetProgramCounter(aThread, breakAddress + 4);
		BreakpointUnlock();
		if (!err && zombie)
			{
			Lock();
			ReleaseZombieAndUnlock(zombie);
			}
		return err;
		}

	TInt err = MoveBreakpointToNextInstructionForThread(aThread, breakpoint); // When this gets hit it will take care of restoring the breakpoint back to what it should be
	if (err)
		{
		Kern::Printf("fdbk: Error returned from MoveBreakpointToNextInstructionForThread %d", err);
		}

	BreakpointUnlock();
	if (!err && zombie)
		{
		Lock();
		ReleaseZombieAndUnlock(zombie);
		}
	return err;
	}

HBuf* DDebuggerEventHandler::GetBreakpoints()
	{
	TInt count = 0;
	BreakpointLock();
	for (SDblQueLink* link = iBreakpoints.First(); link != NULL && link != &iBreakpoints.iA; link=link->iNext)
		{
		SBreakpoint* b = _LOFF(link, SBreakpoint, iLink);
		if (b->iFlags & SBreakpoint::EPersistant) continue;
		count++;
		}
	TInt size = count*sizeof(RMemoryAccess::TBreakpointInfo);
	HBuf* result = HBuf::New(size);
	if (!result)
		{
		BreakpointUnlock();
		return NULL;
		}
	result->SetLength(size);
	RMemoryAccess::TBreakpointInfo* ptr = (RMemoryAccess::TBreakpointInfo*)result->Ptr();
	RMemoryAccess::TBreakpointInfo* end = ptr + count;
	for (SDblQueLink* link = iBreakpoints.First(); link != NULL && link != &iBreakpoints.iA && ptr < end; link=link->iNext)
		{
		SBreakpoint* b = _LOFF(link, SBreakpoint, iLink);
		if (b->iFlags & SBreakpoint::EPersistant) continue; // We don't talk about persistant breakpoints, since they're an implementation detail of LtkUtils::Breakpoint() really
		ptr->iThreadId = b->iThread->iId;
		ptr->iBreakpointId = b->iBreakpointId;
		ptr->iAddress = b->iAddress;
		ptr->iFlags = 0;
		if (b->iFlags & SBreakpoint::EDisabledPendingCodesegLoad) ptr->iFlags |= RMemoryAccess::TBreakpointInfo::EPending;
		if (!(b->iFlags & SBreakpoint::EDisabledByUser)) ptr->iFlags |= RMemoryAccess::TBreakpointInfo::EEnabled;
		if (b->IsHardware()) ptr->iFlags |= RMemoryAccess::TBreakpointInfo::EHardware;
		ptr->iCondition = b->iCondition;
		ptr++;
		}
	BreakpointUnlock();
	return result;
	}

TInt DDebuggerEventHandler::RegisterPersistantBreakpoint(DThread* /*aThread*/, TLinAddr aAddress)
	{
	RMemoryAccess::TPredicate condition; // Default args means 'always pass'
	SBreakpoint* b = new SBreakpoint(NULL, iNextBreakpointId, aAddress, condition); //  The thread is null to say any thread should trigger the breakpoint (ie truly global)
	if (!b) return KErrNoMemory;
	b->iFlags |= SBreakpoint::EPersistant;

	BreakpointLock();
	SBreakpoint* existingBreakpoint = FindBreakpointByAddress(b->iAddress);
	if (existingBreakpoint)
		{
		BreakpointUnlock();
		delete b;
		return KErrAlreadyExists;
		}

	// No need to call ModifyCode because persistant breakpoints are ones that hard-code the invalid instruction
	iBreakpoints.Add(&b->iLink);
	BreakpointUnlock();
	iNextBreakpointId++;
	return KErrNone;
	}

TInt DDebuggerEventHandler::SetProgramCounter(DThread* aThread, TLinAddr aAddress)
	{
	// This can hold the breakpoint lock or not, doesn't care

#if !defined(__WINS__) && !defined(FSHELL_9_1_SUPPORT) // win32 ekern doesn't even export this API let alone implement it
	TUint32 regs[32];
	TUint32 valid = 0;
	NKern::ThreadGetUserContext(&aThread->iNThread, &regs[0], valid);
	if (!(valid & (1<<15))) return KErrNotSupported; // If we can't read it, we can't set it
	regs[15] = aAddress;
	NKern::ThreadSetUserContext(&aThread->iNThread, &regs[0]);
	return KErrNone;
#else
	(void)aThread;
	(void)aAddress;
	return KErrNotSupported;
#endif
	}

void DDebuggerEventHandler::HandleRestoreCode(TLinAddr aAddress)
	{
	ASSERT_BREAKPOINT_LOCKED();
	// This function is to update any shadow breakpoints that were relying on this address having been modified
	// We can't promote any breakpoint that's marked as pending, we need to start tracking the breakpoint address
	// as an offset to the codeseg run address before we can do that.
	SBreakpoint* newRealBreakpoint = NULL;
	for (SDblQueLink* link = iBreakpoints.First(); link != NULL && link != &iBreakpoints.iA; link=link->iNext)
		{
		SBreakpoint* b = _LOFF(link, SBreakpoint, iLink);
		if (b->iAddress == aAddress && !b->IsHardware() && !(b->iFlags & SBreakpoint::EDisabled) && (b->iRealBreakpoint))
			{
			if (newRealBreakpoint == NULL)
				{
				// b is the new real one
				b->iRealBreakpoint = NULL;
				TInt err = ApplyBreakpoint(b, ESoftwareOnly);
				if (err)
					{
					// Oh dear...
					b->iFlags |= SBreakpoint::EDisabledPendingCodesegLoad;
					b->iCodeSeg = NULL; // This saves me having to think what state to put it in - NULL means the breakpoint is dead forever
					}
				else
					{
					newRealBreakpoint = b;
					}
				}
			else
				{
				b->iRealBreakpoint = newRealBreakpoint;
				}
			}
		}
	}

void DDebuggerEventHandler::BreakpointLock()
	{
	NKern::ThreadEnterCS();
	Kern::MutexWait(*iBreakpointMutex);
	}

void DDebuggerEventHandler::BreakpointUnlock()
	{
	Kern::MutexSignal(*iBreakpointMutex);
	NKern::ThreadLeaveCS();
	}

TBool DDebuggerEventHandler::SBreakpoint::HasModifiedCode() const
	{
	return !(iFlags & EDisabled)
		&& !(iFlags & EPersistant)
		&& !IsHardware()
		&& iRealBreakpoint == NULL;
	}

TBool DDebuggerEventHandler::SBreakpoint::IsHardware() const
	{
	return (iFlags & EHardware);
	}

void DDebuggerEventHandler::HandleCodesegRemoved(TAny* aPtr)
	{
	static_cast<DDebuggerEventHandler*>(aPtr)->DoHandleCodesegRemoved();
	}

void DDebuggerEventHandler::DoHandleCodesegRemoved()
	{
	for (;;)
		{
		Lock();
		SDblQueLink* link = iRemovedCodesegs.GetFirst();
		Unlock();
		if (!link) break;
		SRemovedCodeseg* r = _LOFF(link, SRemovedCodeseg, iLink);

		// Check for breakpoints that have been removed by the oh-so-helpful CodeModifier
		BreakpointLock();
		for (SDblQueLink* link = iBreakpoints.First(); link != NULL && link != &iBreakpoints.iA; link=link->iNext)
			{
			SBreakpoint* b = _LOFF(link, SBreakpoint, iLink);
			if (b->iCodeSeg == r->iCodeseg && b->iThread->iOwningProcess == r->iProcess && b->HasModifiedCode())
				{
				DCodeSeg* codeSeg = static_cast<DCodeSeg*>(b->iCodeSeg);
				TBool hadModifiedCode = b->HasModifiedCode();
				b->iFlags |= SBreakpoint::EDisabledPendingCodesegLoad;
				HBuf* codesegName = HBuf::New(codeSeg->iRootName.Length());
				if (codesegName)
					{
					// If it's null, we'll just never be able to reenable the breakpoint
					codesegName->Copy(codeSeg->iRootName);
					}
				TInt codesegOffset = b->iAddress - codeSeg->iRunAddress;
				b->iCodeSeg = codesegName;
				if (hadModifiedCode)
					{
					HandleRestoreCode(b->iAddress);
					}
				b->iAddress = codesegOffset; // Pending breakpoints use iAddress to store the codeseg offset
				}
			}
		BreakpointUnlock();
		delete r;
		}
	}

DDebuggerEventHandler::SBreakpoint::SBreakpoint(DThread* aThread, TInt aBreakpointId, TLinAddr aAddress, const RMemoryAccess::TPredicate& aCondition)
	: iThread(aThread), iBreakpointId(aBreakpointId), iAddress(aAddress&~1), iCodeSeg(NULL), iFlags(0), iRealBreakpoint(NULL), iHardwareBreakpointId(-1), iHardwareBreakpointContextReg(-1), iMatch(NULL), iCondition(aCondition)
	{
	if (aAddress & 1) iFlags |= EThumb;
	if (iThread)
		{
		iThread->Open();
		iFlags |= EThreadSpecific;
		}
	}

DDebuggerEventHandler::SBreakpoint::~SBreakpoint()
	{
	if (iThread) iThread->Close(NULL);
	delete iMatch;
	if (iFlags & SBreakpoint::EDisabledPendingCodesegLoad) delete (HBuf*)iCodeSeg;
	}

TInt DDebuggerEventHandler::ReadInstructions(DThread* aThread, TLinAddr aAddress, TInt aLength, TDes8& aData)
	{
	if (aLength > aData.MaxSize()) return KErrArgument;
	TInt err = Kern::ThreadRawRead(aThread, (TAny*)aAddress, (TAny*)aData.Ptr(), aLength);
	if (err) return err;
	aData.SetLength(aLength);

	// Now check if any of that data had breakpoints in that mean we've not read the real instruction values
	BreakpointLock();
	for (SDblQueLink* link = iBreakpoints.First(); link != NULL && link != &iBreakpoints.iA; link=link->iNext)
		{
		SBreakpoint* b = _LOFF(link, SBreakpoint, iLink);
		if (b->iAddress >= aAddress && b->iAddress < aAddress + aLength && b->iOrigInstruction.Length())
			{
			TInt size = b->iFlags & SBreakpoint::EThumb ? 2 : 4;
			TPtr8 instrBuf((TUint8*)aData.Ptr() + (b->iAddress - aAddress), size, size);
			instrBuf.Copy(b->iOrigInstruction);
			}
		}
	BreakpointUnlock();
	return KErrNone;
	}

TInt DDebuggerEventHandler::ApplyHardwareBreakpoint(SBreakpoint* aBreakpoint)
	{
	ASSERT_BREAKPOINT_LOCKED();

	// First, check the context registers and see if we have one for this thread
	TInt contextReg = 0;
	SBreakpoint* breakUsingFour = FindBreakpointUsingHardwareContextRegister(4);
	SBreakpoint* breakUsingFive = FindBreakpointUsingHardwareContextRegister(5);
	if (breakUsingFour && aBreakpoint->iThread == breakUsingFour->iThread) contextReg = 4;
	if (breakUsingFive && aBreakpoint->iThread == breakUsingFive->iThread) contextReg = 5;

	if (contextReg == 0 && breakUsingFour == NULL)
		{
		// Noone is using 4
		contextReg = 4;
		}

	if (contextReg == 0 && breakUsingFive == NULL)
		{
		contextReg = 5;
		}

	LOG("fdbk: Using context reg %d", contextReg);
	if (contextReg == 0) return KErrOverflow; // No free context registers

	// Now find a free breakpoint reg
	TInt breakreg = -1;
	if (iFreeHwBreakpoints & 8) breakreg = 3;
	if (iFreeHwBreakpoints & 4) breakreg = 2;
	if (iFreeHwBreakpoints & 2) breakreg = 1;
	if (iFreeHwBreakpoints & 1) breakreg = 0;

	if (breakreg == -1) return KErrOverflow;
	aBreakpoint->iHardwareBreakpointId = breakreg;

	TInt err = KErrNone;
#ifdef __SMP__
	// Have to apply to every CPU in turn
	const TInt num = NKern::NumberOfCpus();
	TUint32 origAffinity = 0;
	LOG("Applying breakpoint to all %d CPUs...", num);
	for (TInt i = 0; i < num; i++)
		{
		TUint32 affinity = NKern::ThreadSetCpuAffinity(&Kern::CurrentThread().iNThread, i);
		if (i == 0) origAffinity = affinity;
		err = DoApplyHardwareBreakpoint(aBreakpoint, contextReg);
		if (err)
			{
			// Disable the breakpoint on any CPUs we successfully applied it on
			for (TInt j = 0; j < i; j++)
				{
				NKern::ThreadSetCpuAffinity(&Kern::CurrentThread().iNThread, j);
				DoClearHardwareBreakpoint(aBreakpoint);
				}
			break;
			}
		}
	// Restore original affinity
	NKern::ThreadSetCpuAffinity(&Kern::CurrentThread().iNThread, origAffinity);
#else
	err = DoApplyHardwareBreakpoint(aBreakpoint, contextReg);
#endif
	if (err == KErrNone)
		{
		iFreeHwBreakpoints &= ~(1<<breakreg);
		}
	return err;
	}

TInt DDebuggerEventHandler::DoApplyHardwareBreakpoint(SBreakpoint* aBreakpoint, TInt aContextReg)
	{
#if defined(__EABI__)

#if defined(FSHELL_ARM_MEM_MAPPED_DEBUG)
	if (iDebugRegistersChunk == NULL)
		{
		// Need to setup mapping for debug registers
		TUint drar = GetDrar();
		TUint dsar = GetDsar();
		//Kern::Printf("drar=0x%08x dsar=0x%08x", drar, dsar);
		if ((dsar & 3) != 3)
			{
			Kern::Printf("fdbk: DSAR enabled bits are not set - aborting");
			return KErrNotSupported;
			}
		TPhysAddr physAddr = (drar & ~3) + (dsar & ~3); // We don't check validity of DRAR, TI set it incorrectly to 0x0 (invalid) on 3530
		if (physAddr == 0x52011000) physAddr = 0x54011000; // TI got this wrong too...
		const TInt KDebugRegSize = 0x10000; // 64K so we can reach DBGEN on 3530
		TUint attrib;
		new(&attrib) TMappingAttributes2(/*EMemAttNormalUncached*/ EMemAttStronglyOrdered, EFalse, ETrue);
		TInt err = DPlatChunkHw::New(iDebugRegistersChunk, physAddr, KDebugRegSize, attrib);
		if (err < 0)
			{
			Kern::Printf("fdbk: Error mapping debug registers - %d", err);
			return err;
			}
		LOG("debug registers from 0x%08x mapped at 0x%08x", physAddr, iDebugRegistersChunk->LinearAddress());
		}
#endif

#if defined(FSHELL_ARM11XX_SUPPORT) || defined(FSHELL_ARM_MEM_MAPPED_DEBUG)
	// First. check debug status register (DSCR) to check that monitor mode is enabled (and thus that the BCRs are writeable)
	TUint dscr = GetDscr();
	//Kern::Printf("dscr=0x%08x", dscr);
#ifdef FSHELL_ARM_MEM_MAPPED_DEBUG
	// Check for DBGEN (via authstatus register) too, we need it
	TUint authStatus = *(TUint32*)(iDebugRegistersChunk->LinearAddress() + 0xFB8);
	LOG("authStatus = 0x%08x", authStatus);
	if ((authStatus & 1) == 0)
		{
		// DBGEN not enabled - try the magic barely documented 3530 way of setting it
		LOG("Setting Lock Access Register");
		WriteRegister(ELockAccessOffset, 0xC5ACCE55);

		Kern::Printf("Reading CONTROL_SEC_EMU from 0x48002A64...");
		TUint attrib;
		new(&attrib) TMappingAttributes2(/*EMemAttNormalUncached*/ EMemAttStronglyOrdered, EFalse, ETrue);
		DPlatChunkHw* crazyOmapRegs = NULL;
		DPlatChunkHw::New(crazyOmapRegs, 0x48002000, 0x1000, attrib);
		TUint32 controlsecemu = *(TUint32*)(crazyOmapRegs->LinearAddress() + 0xa64);
		Kern::Printf("CONTROL_SEC_EMU = 0x%08x", controlsecemu);

		Kern::Printf("Trying 3530 approach to enabling DBGEN...");
		TUint32 volatile * dbgenWord = (TUint32*)(iDebugRegistersChunk->LinearAddress() + 0xc030);
		Kern::Printf("DBGEN word = 0x%08x", *dbgenWord);
		*dbgenWord |= (1 << 13); // Set bit 13 of 0x5401d030 is how you do it, apparently
		//*dbgenWord = 0xFFFFFFFF;
		// Now do as ASM says and DSB, poll for DSCR (or just wait a bit) then ISB
		Dsb();
		Kern::Printf("Wasting time waiting for dbgen....");
		Isb();
		Kern::Printf("DBGEN word is now 0x%08x", *dbgenWord);
		authStatus = *(TUint32*)(iDebugRegistersChunk->LinearAddress() + 0xFB8);
		Kern::Printf("authStatus is now 0x%08x", authStatus);
		}
#endif
	if ((dscr & 0xC000) != 0x8000)
		{
		// Set bits [15:14] to b10
		dscr = (dscr & ~0xC000) | 0x8000;
		//Kern::Printf("Setting monitor mode in DSCR...");
		SetDscr(dscr); // If there's a JTAG doing halt-mode debugging this is gonna crash. Oh well.
		}
	dscr = GetDscr();
	//Kern::Printf("dscr from MCR=0x%08x", dscr);		

#else
	// Is ARM, but not ARM11 or A8
	(void)aBreakpoint;
	(void)aContextReg;
	return KErrNotSupported;
#endif
	
	TUint32 contextId = GetArmContextIdForThread(aBreakpoint->iThread);
	
	//Kern::Printf("fdbk: fdb thinks contextId is 0x%08x", contextId);
	//return KErrNotSupported; //DEBUG

	SetContextIdBrp(aContextReg, contextId);

	// Now need to construct the BCR register value
	const TUint32 KArmBcr = 0x001001E5;
	//const TUint32 KArmBcr = 0x000001E5; // DEBUG disable context match
	//TUint32 bcr = ReadBcr(aBreakReg);
	TUint32 bcr = KArmBcr;
	bcr |= aContextReg << 16;
	if (aBreakpoint->iFlags & SBreakpoint::EThumb)
		{
		const TUint32 KByteSelectMask = 0x1E0;
		// These assume a little-endian world
		const TUint32 KMiddleOfWordThumbByteSelect = 0x180; // [8:5] = b1100
		const TUint32 KWordAlignedThumbByteSelect = 0x060; // [8:5] = b0011
		bcr = bcr & ~KByteSelectMask;
		if (aBreakpoint->iAddress & 2)
			{
			bcr |= KMiddleOfWordThumbByteSelect;
			}
		else
			{
			bcr |= KWordAlignedThumbByteSelect;
			}
		}

	SetBreakpointPair(aBreakpoint->iHardwareBreakpointId, aBreakpoint->iAddress & ~3, bcr);
	return KErrNone;
	
#else
	(void)aBreakpoint;
	(void)aContextReg;
	return KErrNotSupported;
#endif
	}

TUint32 DDebuggerEventHandler::GetArmContextIdForThread(DThread* aThread)
	{
#if (defined(FSHELL_ARM11XX_SUPPORT) || defined(FSHELL_ARM_MEM_MAPPED_DEBUG)) && defined(__MARM__)
	// This is according to TScheduler::Reschedule (gulp)
	TUint asid = static_cast<DMemModelProcess*>(aThread->iOwningProcess)->iOsAsid;
	//TUint32 result = (((TUint32)&aThread->iNThread >> 6) << 8) | asid;
	TUint32 result = (TUint32)&aThread->iNThread;
	result = (result << 2) & ~0xFF;
	result |= (asid & 0xFF);
	return result;
#else
	(void)aThread;
	return 0;
#endif
	}

#ifdef FSHELL_ARM_MEM_MAPPED_DEBUG

TUint32 DDebuggerEventHandler::ReadRegister(TInt aRegisterOffset)
	{
	return *(TUint32*)(iDebugRegistersChunk->LinearAddress() + aRegisterOffset);
	}

void DDebuggerEventHandler::WriteRegister(TInt aRegisterOffset, TUint32 aValue)
	{
	TUint32* reg = (TUint32*)(iDebugRegistersChunk->LinearAddress() + aRegisterOffset);
	*reg = aValue;
	}

#endif // FSHELL_ARM_MEM_MAPPED_DEBUG

#ifdef __EABI__

void DDebuggerEventHandler::SetDscr(TUint32 aVal)
	{
#ifdef FSHELL_ARM_MEM_MAPPED_DEBUG
	TUint32* dscrAddr = (TUint32*)(iDebugRegistersChunk->LinearAddress() + EDscrOffset);
	*dscrAddr = aVal;
#else
	MCR_SetDscr(aVal);
#endif
	}

void DDebuggerEventHandler::SetBreakpointPair(TInt aRegister, TUint aBvrValue, TUint aBcrValue)
	{
	LOG("fdbk: SetBreakpointPair(reg=%d, bvr=0x%08x, bcr=0x%08x)", aRegister, aBvrValue, aBcrValue);
#ifdef FSHELL_ARM_MEM_MAPPED_DEBUG
	// This is the sequence the ARM ARM recommends
	TUint32* bcrAddr = (TUint32*)(iDebugRegistersChunk->LinearAddress() + EBcrOffset + aRegister*4);
	TUint32* bvrAddr = (TUint32*)(iDebugRegistersChunk->LinearAddress() + EBvrOffset + aRegister*4);

	// Disable the breakpoint
	//*bcrAddr = 0;
	//Imb();
	*bvrAddr = aBvrValue;
	*bcrAddr = aBcrValue;
#else
	MCR_SetBreakpointPair(aRegister, aBvrValue, aBcrValue);
#endif
	Imb();
	}

TUint DDebuggerEventHandler::ReadBcr(TInt aRegister)
	{
//#ifdef FSHELL_ARM_MEM_MAPPED_DEBUG
//	TUint32* bcrAddr = (TUint32*)(iDebugRegistersChunk->LinearAddress() + EBcrOffset + aRegister*4);
//	return *bcrAddr;
//#else
	return MRC_ReadBcr(aRegister);
//#endif
	}

void DDebuggerEventHandler::SetContextIdBrp(TInt aRegister, TUint aContextId)
	{
#ifdef FSHELL_ARM_MEM_MAPPED_DEBUG
	// This is according to "ARM 13.3.9. CP14 c80-c85, Breakpoint Control Registers (BCR)"
	const TUint KContextIdBCR = 0x003001E7;
	SetBreakpointPair(aRegister, aContextId, KContextIdBCR);
#else
	LOG("SetContextIdBrp %d 0x%08x", aRegister, aContextId);
	MCR_SetContextIdBrp(aRegister, aContextId);
	Imb();
#endif
	}

#endif

void DDebuggerEventHandler::RemoveAllHardwareBreakpointsForThread(DThread* aThread)
	{
	BreakpointLock();
	for (SDblQueLink* link = iBreakpoints.First(); link != NULL && link != &iBreakpoints.iA;)
		{
		SBreakpoint* b = _LOFF(link, SBreakpoint, iLink);
		link=link->iNext; // Do this before potentially calling ClearBreakpoint, because that will delete the breakpoint, invalidating link
		if (b->IsHardware() && b->MatchesThread(aThread))
			{
			ClearBreakpoint(b);
			}
		}
	BreakpointUnlock();
	}

TInt DDebuggerEventHandler::MoveBreakpointToNextInstructionForThread(DThread* aThread, SBreakpoint* aBreakpoint)
	{
#ifdef SUPPORT_BREAKPOINT_STUFF
	ASSERT_BREAKPOINT_LOCKED();
	TUint32 notUsed = 0;
	TBool aModeChange = EFalse;
	TLinAddr nextAddr = PCAfterInstructionExecutes(aThread, aBreakpoint->iAddress, notUsed, aBreakpoint->iFlags & SBreakpoint::EThumb ? 2 : 4, notUsed, aModeChange);
	LOG("fdbk: Next instruction after %x is %x", aBreakpoint->iAddress, nextAddr);

	RMemoryAccess::TPredicate alwaysPass;
	SBreakpoint* temp = new SBreakpoint(aThread, iNextBreakpointId, nextAddr, alwaysPass);
	if (!temp) return KErrNoMemory;
	iBreakpoints.Add(&temp->iLink);

	const TBool hw = aBreakpoint->IsHardware();
	if (hw) UnapplyBreakpoint(aBreakpoint); // If it's hardware, it's ok to unapply it before adding the temp one because hw breakpoints are thread-specific. For software, we want the new one in place before we remove this one to make sure we don't miss stuff

	TInt err = ApplyBreakpoint(temp, hw ? EHardwareOnly : ESoftwareOnly);
	if (err)
		{
		// This really shouldn't fail if hw - we still have the lock and we know there's a free BRP cos we just unapplied aBreakpoint
		temp->iLink.Deque();
		delete temp;
		return err;
		}

	if (!hw) UnapplyBreakpoint(aBreakpoint);

	aBreakpoint->iFlags |= SBreakpoint::EDisabledDuringContinue;
	temp->iFlags |= SBreakpoint::ETempContinue;
	temp->iRealBreakpoint = aBreakpoint;
	iNextBreakpointId++;

	return KErrNone;
#else
	(void)aThread;
	(void)aBreakpoint;
	return KErrNotSupported;
#endif
	}


void DDebuggerEventHandler::SBreakpoint::SetThreadMatchPattern(HBuf* aMatch)
	{
	ASSERT(iMatch == NULL);
	ASSERT(iThread == NULL);
	ASSERT((iFlags & EThreadSpecific) == 0);
	iMatch = aMatch;
	}

TBool DDebuggerEventHandler::SBreakpoint::MatchesThread(DThread* aThread) const
	{
	TBool match = EFalse;

	if (iFlags & EThreadSpecific)
		{
		match = (aThread == iThread);
		}
	else if (iMatch == NULL)
		{
		match = ETrue;
		}
	else
		{
		TFullName threadName;
		aThread->FullName(threadName);
		match = threadName.MatchF(*iMatch);
		}

	if (match && iCondition.HasConditions())
		{
#ifdef __MARM__
		TUint32 regs[32];
		TUint32 valid = 0;
		NKern::ThreadGetUserContext(&aThread->iNThread, &regs[0], valid);
		match = iCondition.SatisfiesConditions(regs);
#endif
		}
	return match;
	}

//

const TUint32 KOpMask = 0xF;

TBool RMemoryAccess::TPredicate::SatisfiesConditions(TUint32* aRegisterSet) const
	{
	for (TInt slot = 0; slot < KNumSlots; slot++)
		{
		TInt slotShift = slot * 8;
		TUint32 opAndReg = (iOp >> slotShift) & 0xFF;
		TOp op = (TOp)(opAndReg & KOpMask);
		TInt reg = opAndReg >> 4;
		TBool match = Test(op, aRegisterSet[reg], iVals[slot]);
		if (!match) return EFalse;
		}
	return ETrue;
	}

TBool RMemoryAccess::TPredicate::Test(TOp aOperation, TUint32 aCurrentValue, TUint32 aStoredValue)
	{
	LOG("Testing op %d %u against %u", aOperation, aCurrentValue, aStoredValue);

	switch (aOperation)
		{
		case ENothing:
			return ETrue;
		case EEq:
		case ESignedEq:
			return (aCurrentValue == aStoredValue);
		case ENe:
		case ESignedNe:
			return (aCurrentValue != aStoredValue);
		case ELt:
			return (aCurrentValue < aStoredValue);
		case ELe:
			return (aCurrentValue <= aStoredValue);
		case EGt:
			return (aCurrentValue > aStoredValue);
		case EGe:
			return (aCurrentValue >= aStoredValue);
		case ESignedLt:
			return ((TInt32)aCurrentValue < (TInt32)aStoredValue);
		case ESignedLe:
			return ((TInt32)aCurrentValue <= (TInt32)aStoredValue);
		case ESignedGt:
			return ((TInt32)aCurrentValue > (TInt32)aStoredValue);
		case ESignedGe:
			return ((TInt32)aCurrentValue >= (TInt32)aStoredValue);
		}
	return EFalse; // Compiler shutter-upper
	}

TUint DDebuggerEventHandler::GetCreatorThread(TUint aThreadId)
	{
	TCreatorInfo dummy(aThreadId, 0);
	TUint result = 0;
	BreakpointLock();
	TInt found = iCreatorInfo.FindInUnsignedKeyOrder(dummy);
	if (found != KErrNotFound)
		{
		result = iCreatorInfo[found].iCreatorThreadId;
		}
	BreakpointUnlock();
	return result;
	}