IOKernelDebugger.cpp [plain text]
#include <IOKit/assert.h>
#include <IOKit/IOLib.h>
#include <IOKit/IOMessage.h>
#include <IOKit/IOLocks.h>
#include <IOKit/network/IONetworkController.h>
#include <IOKit/network/IOKernelDebugger.h>
#include <libkern/OSAtomic.h>
#define kIOKDPEnableKDP "IOEnableKDP"
#define kIOKDPDriverMatchPPC "IODriverMatchPPC"
#define kIOKDPDriverNubMatchPPC "IODriverNubMatchPPC"
#define kIOKDPDriverMatchI386 "IODriverMatchI386"
#define kIOKDPDriverNubMatchI386 "IODriverNubMatchI386"
class IOKDP : public IOService
{
OSDeclareDefaultStructors( IOKDP )
public:
virtual bool start( IOService * provider );
virtual void stop( IOService * provider );
virtual bool matchProvider( IOService * provider );
virtual bool matchServiceWithDictionary( IOService * service,
OSDictionary * match );
virtual IOReturn message( UInt32 type,
IOService * provider,
void * argument = 0 );
};
static IOLock * gIOKDPLock = 0;
static IOKDP * gIOKDP = 0;
class IOKDPGlobals
{
public:
IOKDPGlobals();
~IOKDPGlobals();
inline bool isValid() const;
};
static IOKDPGlobals gIOKDPGlobals;
IOKDPGlobals::IOKDPGlobals()
{
gIOKDPLock = IOLockAlloc();
}
IOKDPGlobals::~IOKDPGlobals()
{
if ( gIOKDPLock )
{
IOLockFree( gIOKDPLock );
gIOKDPLock = 0;
}
}
bool IOKDPGlobals::isValid() const
{
return ( gIOKDPLock );
}
#define super IOService
OSDefineMetaClassAndStructors( IOKDP, IOService )
bool IOKDP::matchProvider(IOService * provider)
{
IOService * driver = 0;
IOService * driverNub = 0;
OSBoolean * aBool;
if ( provider ) driver = provider->getProvider();
if ( driver ) driverNub = driver->getProvider();
if ( (driver == 0) || (driverNub == 0) )
return false;
if ( ( aBool = OSDynamicCast(OSBoolean, getProperty(kIOKDPEnableKDP)) ) &&
( aBool->isTrue() == false ) )
return false;
#if defined (__ppc__)
if ( matchServiceWithDictionary( driver, (OSDictionary *)
getProperty(kIOKDPDriverMatchPPC)) )
{
return true;
}
if ( matchServiceWithDictionary( driverNub, (OSDictionary *)
getProperty(kIOKDPDriverNubMatchPPC)) )
{
return true;
}
#elif defined (__i386__)
return true;
#else
#error unknown architecture
#endif
return false;
}
bool IOKDP::matchServiceWithDictionary(IOService * service,
OSDictionary * match)
{
OSCollectionIterator * matchIter;
OSCollectionIterator * arrayIter = 0;
OSCollection * array;
OSObject * objM;
OSObject * objP;
OSSymbol * sym;
bool isMatch = false;
if ( ( OSDynamicCast(OSDictionary, match) == 0 ) ||
( match->getCount() == 0 ) ||
( (matchIter = OSCollectionIterator::withCollection(match)) == 0 ) )
return false;
while ( ( sym = OSDynamicCast(OSSymbol, matchIter->getNextObject()) ) )
{
objM = match->getObject(sym);
objP = service->getProperty(sym);
isMatch = false;
if ( arrayIter )
{
arrayIter->release();
arrayIter = 0;
}
if ( (array = OSDynamicCast( OSCollection, objM )) )
{
arrayIter = OSCollectionIterator::withCollection( array );
if ( arrayIter == 0 ) break;
}
do {
if ( arrayIter && ((objM = arrayIter->getNextObject()) == 0) )
break;
if ( objM && objP && objM->isEqualTo(objP) )
{
isMatch = true;
break;
}
}
while ( arrayIter );
if ( isMatch == false ) break;
}
if ( arrayIter ) arrayIter->release();
matchIter->release();
return isMatch;
}
bool IOKDP::start( IOService * provider )
{
bool ret = false;
if ( super::start(provider) == false )
return false;
if ( gIOKDPGlobals.isValid() == false )
return false;
IOLockLock( gIOKDPLock );
do {
if ( gIOKDP )
break;
if ( matchProvider(provider) == false )
break;
if ( provider->open(this) == false )
break;
publishResource("kdp");
gIOKDP = this;
ret = true;
}
while ( false );
IOLockUnlock( gIOKDPLock );
return ret;
}
void IOKDP::stop( IOService * provider )
{
provider->close(this);
IOLockLock( gIOKDPLock );
if ( gIOKDP == this ) gIOKDP = 0;
IOLockUnlock( gIOKDPLock );
super::stop(provider);
}
IOReturn IOKDP::message( UInt32 type,
IOService * provider,
void * argument )
{
if ( type == kIOMessageServiceIsTerminated )
{
provider->close(this);
}
return kIOReturnSuccess;
}
extern "C" {
typedef void (*kdp_send_t)( void * pkt, UInt pkt_len );
typedef void (*kdp_receive_t)( void * pkt, UInt * pkt_len, UInt timeout );
void kdp_register_send_receive( kdp_send_t send, kdp_receive_t receive );
}
#undef super
#define super IOService
OSDefineMetaClassAndStructors( IOKernelDebugger, IOService )
OSMetaClassDefineReservedUnused( IOKernelDebugger, 0);
OSMetaClassDefineReservedUnused( IOKernelDebugger, 1);
OSMetaClassDefineReservedUnused( IOKernelDebugger, 2);
OSMetaClassDefineReservedUnused( IOKernelDebugger, 3);
IOService * gIODebuggerDevice = 0;
IODebuggerTxHandler gIODebuggerTxHandler = 0;
IODebuggerRxHandler gIODebuggerRxHandler = 0;
UInt32 gIODebuggerTxBytes = 0;
UInt32 gIODebuggerRxBytes = 0;
SInt32 gIODebuggerSemaphore = 0;
UInt32 gIODebuggerFlag = 0;
enum {
kIODebuggerFlagRegistered = 0x01,
kIODebuggerFlagWarnNullHandler = 0x02
};
void IOKernelDebugger::kdpReceiveDispatcher( void * buffer,
UInt32 * length,
UInt32 timeout )
{
*length = 0;
if ( gIODebuggerSemaphore ) return;
(*gIODebuggerRxHandler)( gIODebuggerDevice, buffer, length, timeout );
gIODebuggerRxBytes += *length;
}
void IOKernelDebugger::kdpTransmitDispatcher( void * buffer, UInt32 length )
{
if ( gIODebuggerSemaphore ) return;
(*gIODebuggerTxHandler)( gIODebuggerDevice, buffer, length );
gIODebuggerTxBytes += length;
}
void IOKernelDebugger::nullTxHandler( IOService * target,
void * buffer,
UInt32 length )
{
}
void IOKernelDebugger::nullRxHandler( IOService * target,
void * buffer,
UInt32 * length,
UInt32 timeout )
{
if ( gIODebuggerFlag & kIODebuggerFlagWarnNullHandler )
{
IOLog("IOKernelDebugger::%s no debugger device\n", __FUNCTION__);
gIODebuggerFlag &= ~kIODebuggerFlagWarnNullHandler;
}
}
IODebuggerLockState IOKernelDebugger::lock( IOService * object )
{
if ( gIODebuggerDevice == object )
{
OSIncrementAtomic( &gIODebuggerSemaphore );
return kIODebuggerLockTaken;
}
return (IODebuggerLockState) 0;
}
void IOKernelDebugger::unlock( IODebuggerLockState state )
{
if ( state & kIODebuggerLockTaken )
OSDecrementAtomic( &gIODebuggerSemaphore );
}
bool IOKernelDebugger::init( IOService * target,
IODebuggerTxHandler txHandler,
IODebuggerRxHandler rxHandler )
{
if ( ( super::init() == false ) ||
( OSDynamicCast(IOService, target) == 0 ) ||
( txHandler == 0 ) ||
( rxHandler == 0 ) )
{
return false;
}
_target = target;
_txHandler = txHandler;
_rxHandler = rxHandler;
return true;
}
IOKernelDebugger * IOKernelDebugger::debugger( IOService * target,
IODebuggerTxHandler txHandler,
IODebuggerRxHandler rxHandler )
{
IOKernelDebugger * debugger = new IOKernelDebugger;
if (debugger && (debugger->init( target, txHandler, rxHandler ) == false))
{
debugger->release();
debugger = 0;
}
return debugger;
}
void IOKernelDebugger::registerHandler( IOService * target,
IODebuggerTxHandler txHandler,
IODebuggerRxHandler rxHandler )
{
bool doRegister;
assert( ( target == gIODebuggerDevice ) ||
( target == 0 ) ||
( gIODebuggerDevice == 0 ) );
doRegister = ( target && ( txHandler != 0 ) && ( rxHandler != 0 ) );
if ( txHandler == 0 ) txHandler = &IOKernelDebugger::nullTxHandler;
if ( rxHandler == 0 ) rxHandler = &IOKernelDebugger::nullRxHandler;
OSIncrementAtomic( &gIODebuggerSemaphore );
gIODebuggerDevice = target;
gIODebuggerTxHandler = txHandler;
gIODebuggerRxHandler = rxHandler;
gIODebuggerFlag |= kIODebuggerFlagWarnNullHandler;
OSDecrementAtomic( &gIODebuggerSemaphore );
if ( doRegister && (( gIODebuggerFlag & kIODebuggerFlagRegistered ) == 0) )
{
kdp_register_send_receive( (kdp_send_t) kdpTransmitDispatcher,
(kdp_receive_t) kdpReceiveDispatcher );
gIODebuggerFlag |= kIODebuggerFlagRegistered;
}
}
bool IOKernelDebugger::handleOpen( IOService * forClient,
IOOptionBits options,
void * arg )
{
IONetworkController * ctr = OSDynamicCast(IONetworkController, _target);
bool ret = false;
do {
if ( _client ) break;
registerHandler( _target );
if ( _target->open( this ) == false )
break;
_pmDisabled = false;
if ( ctr )
{
ctr->registerInterestedDriver( this );
if ( ctr->doEnable( this ) != kIOReturnSuccess )
{
ctr->deRegisterInterestedDriver( this );
break;
}
}
IOLog("%s: Debugger attached\n", getName());
registerHandler( _target, _txHandler, _rxHandler );
_client = forClient;
ret = true;
}
while (0);
if ( ret == false )
{
registerHandler( 0 );
_target->close( this );
}
return ret;
}
void IOKernelDebugger::handleClose( IOService * forClient,
IOOptionBits options )
{
IONetworkController * ctr = OSDynamicCast(IONetworkController, _target);
if ( _client && ( _client == forClient ) )
{
registerHandler( 0 );
if ( ctr )
{
if ( _pmDisabled == false )
{
ctr->doDisable( this );
}
ctr->deRegisterInterestedDriver( this );
}
_client = 0;
_target->close( this );
}
}
bool IOKernelDebugger::handleIsOpen( const IOService * forClient ) const
{
if ( forClient == 0 )
return ( forClient != _client );
else
return ( forClient == _client );
}
void IOKernelDebugger::free()
{
super::free();
}
#define PM_SECS(x) ((x) * 1000 * 1000)
IOReturn
IOKernelDebugger::powerStateWillChangeTo( IOPMPowerFlags flags,
unsigned long stateNumber,
IOService * policyMaker )
{
IOReturn ret = IOPMAckImplied;
if ( ( flags & IOPMDeviceUsable ) == 0 )
{
this->retain();
thread_call_func( (thread_call_func_t) pmDisableDebugger,
this,
FALSE );
ret = PM_SECS(3);
}
return ret;
}
IOReturn
IOKernelDebugger::powerStateDidChangeTo( IOPMPowerFlags flags,
unsigned long stateNumber,
IOService * policyMaker )
{
IOReturn ret = IOPMAckImplied;
if ( flags & IOPMDeviceUsable )
{
this->retain();
thread_call_func( (thread_call_func_t) pmEnableDebugger,
this,
FALSE );
ret = PM_SECS(3);
}
return ret;
}
void IOKernelDebugger::pmEnableDebugger( IOKernelDebugger * debugger )
{
IONetworkController * ctr;
assert( debugger );
ctr = OSDynamicCast( IONetworkController, debugger->_target );
debugger->lockForArbitration();
if ( debugger->_client && ( debugger->_pmDisabled == true ) )
{
if ( ctr && ( ctr->doEnable( debugger ) != kIOReturnSuccess ) )
{
IOLog("IOKernelDebugger: Unable to re-enable controller\n");
}
else
{
registerHandler( debugger->_target, debugger->_txHandler,
debugger->_rxHandler );
debugger->_pmDisabled = false;
}
}
debugger->unlockForArbitration();
debugger->_target->acknowledgePowerChange( debugger );
debugger->release();
}
void IOKernelDebugger::pmDisableDebugger( IOKernelDebugger * debugger )
{
IONetworkController * ctr;
assert( debugger );
ctr = OSDynamicCast( IONetworkController, debugger->_target );
debugger->lockForArbitration();
if ( debugger->_client && ( debugger->_pmDisabled == false ) )
{
registerHandler( 0 );
if ( ctr ) ctr->doDisable( debugger );
debugger->_pmDisabled = true;
}
debugger->unlockForArbitration();
debugger->_target->acknowledgePowerChange( debugger );
debugger->release();
}