/* * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * The contents of this file constitute Original Code as defined in and * are subject to the Apple Public Source License Version 1.1 (the * "License"). You may not use this file except in compliance with the * License. Please obtain a copy of the License at * http://www.apple.com/publicsource and read it before using this file. * * This Original Code and all software distributed under the License are * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. * * IOKernelDebugger.cpp * * HISTORY */ #include #include #include #include #include #include #include //--------------------------------------------------------------------------- // IOKDP #define kIOKDPEnableKDP "IOEnableKDP" #define kIOKDPDriverMatch "IODriverMatch" #define kIOKDPDriverNubMatch "IODriverNubMatch" class IOKDP : public IOService { OSDeclareDefaultStructors( IOKDP ) public: static void initialize(); 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 ); }; //--------------------------------------------------------------------------- // IOKDP defined globals. static IOLock * gIOKDPLock = 0; static IOKDP * gIOKDP = 0; #define super IOService OSDefineMetaClassAndStructorsWithInit( IOKDP, IOService, IOKDP::initialize() ) //--------------------------------------------------------------------------- // Match the provider with the matching dictionary in our property table. 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 ( matchServiceWithDictionary( driver, (OSDictionary *) getProperty(kIOKDPDriverMatch)) ) { // IOLog("IOKDP: %s\n", kIOKDPDriverMatch); return true; } if ( matchServiceWithDictionary( driverNub, (OSDictionary *) getProperty(kIOKDPDriverNubMatch)) ) { // IOLog("IOKDP: %s\n", kIOKDPDriverNubMatch); return true; } return false; } //--------------------------------------------------------------------------- // Match an IOService with a matching dictionary. 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; } //--------------------------------------------------------------------------- // IOKDP class initializer. void IOKDP::initialize() { gIOKDPLock = IOLockAlloc(); assert( gIOKDPLock ); } //--------------------------------------------------------------------------- // start/stop/message. bool IOKDP::start( IOService * provider ) { bool ret = false; if ( super::start(provider) == 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; } //--------------------------------------------------------------------------- // IOKernelDebugger extern "C" { // // Defined in osfmk/kdp/kdp_en_debugger.h, but the header file is not // exported, thus the definition is replicated here. // 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); // IOKernelDebugger global variables. // IOService * gIODebuggerDevice = 0; IODebuggerTxHandler gIODebuggerTxHandler = 0; IODebuggerRxHandler gIODebuggerRxHandler = 0; UInt32 gIODebuggerTxBytes = 0; UInt32 gIODebuggerRxBytes = 0; SInt32 gIODebuggerSemaphore = 0; UInt32 gIODebuggerFlag = 0; // Global debugger flags. // enum { kIODebuggerFlagRegistered = 0x01, kIODebuggerFlagWarnNullHandler = 0x02 }; //--------------------------------------------------------------------------- // The KDP receive dispatch function. Dispatches KDP receive requests to the // registered receive handler. This function is registered with KDP via // kdp_register_send_receive(). void IOKernelDebugger::kdpReceiveDispatcher( void * buffer, UInt32 * length, UInt32 timeout ) { *length = 0; // return a zero length field by default. if ( gIODebuggerSemaphore ) return; // FIXME - Driver is busy! (*gIODebuggerRxHandler)( gIODebuggerDevice, buffer, length, timeout ); gIODebuggerRxBytes += *length; } //--------------------------------------------------------------------------- // The KDP transmit dispatch function. Dispatches KDP receive requests to the // registered transmit handler. This function is registered with KDP via // kdp_register_send_receive(). void IOKernelDebugger::kdpTransmitDispatcher( void * buffer, UInt32 length ) { if ( gIODebuggerSemaphore ) return; // FIXME - Driver is busy! (*gIODebuggerTxHandler)( gIODebuggerDevice, buffer, length ); gIODebuggerTxBytes += length; } //--------------------------------------------------------------------------- // Null debugger handlers. 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; } } //--------------------------------------------------------------------------- // Take the debugger lock conditionally. IODebuggerLockState IOKernelDebugger::lock( IOService * object ) { if ( gIODebuggerDevice == object ) { OSIncrementAtomic( &gIODebuggerSemaphore ); return kIODebuggerLockTaken; } return (IODebuggerLockState) 0; } //--------------------------------------------------------------------------- // Release the debugger lock if the kIODebuggerLockTaken flag is set. void IOKernelDebugger::unlock( IODebuggerLockState state ) { if ( state & kIODebuggerLockTaken ) OSDecrementAtomic( &gIODebuggerSemaphore ); } //--------------------------------------------------------------------------- // Initialize an IOKernelDebugger instance. bool IOKernelDebugger::init( IOService * target, IODebuggerTxHandler txHandler, IODebuggerRxHandler rxHandler ) { if ( ( super::init() == false ) || ( OSDynamicCast(IOService, target) == 0 ) || ( txHandler == 0 ) || ( rxHandler == 0 ) ) { return false; } // Cache the target and handlers provided. _target = target; _txHandler = txHandler; _rxHandler = rxHandler; return true; } //--------------------------------------------------------------------------- // Factory method which performs allocation and initialization of an // IOKernelDebugger instance. 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; } //--------------------------------------------------------------------------- // Register the debugger handlers. 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) ) { // Register dispatch function, these in turn will call the // handlers when the debugger is active. // // Note: The following call may trigger an immediate break // to the debugger. kdp_register_send_receive( (kdp_send_t) kdpTransmitDispatcher, (kdp_receive_t) kdpReceiveDispatcher ); // Limit ourself to a single real KDP registration. gIODebuggerFlag |= kIODebuggerFlagRegistered; } } //--------------------------------------------------------------------------- // Called by open() with the arbitration lock held. bool IOKernelDebugger::handleOpen( IOService * forClient, IOOptionBits options, void * arg ) { IONetworkController * ctr = OSDynamicCast(IONetworkController, _target); bool ret = false; do { // Only a single client at a time. if ( _client ) break; // Register the target to prime the lock()/unlock() functionality // before opening the target. registerHandler( _target ); // While the target is opened/enabled, it must block any thread // which may acquire the debugger lock in its execution path. if ( _target->open( this ) == false ) break; // Register interest in receiving notifications about controller // power state changes. // // We are making an assumption that the controller is 'usable' and // the next notification will inform this object that the controller // has become unusable, there is no support for cases when the // controller is already in an 'unusable' state. _pmDisabled = false; if ( ctr ) { // Register to receive PM notifications for controller power // state changes. ctr->registerInterestedDriver( this ); if ( ctr->doEnable( this ) != kIOReturnSuccess ) { ctr->deRegisterInterestedDriver( this ); break; } } // After the target has been opened, complete the registration. IOLog("%s: Debugger attached\n", getName()); registerHandler( _target, _txHandler, _rxHandler ); // Remember the client. _client = forClient; ret = true; } while (0); if ( ret == false ) { registerHandler( 0 ); _target->close( this ); } return ret; } //--------------------------------------------------------------------------- // Called by IOService::close() with the arbitration lock held. void IOKernelDebugger::handleClose( IOService * forClient, IOOptionBits options ) { IONetworkController * ctr = OSDynamicCast(IONetworkController, _target); if ( _client && ( _client == forClient ) ) { // There is no KDP un-registration. The best we can do is to // register dummy handlers. registerHandler( 0 ); if ( ctr ) { // Disable controller if it is not already disabled. if ( _pmDisabled == false ) { ctr->doDisable( this ); } // Before closing the controller, remove interest in receiving // notifications about controller power state changes. ctr->deRegisterInterestedDriver( this ); } _client = 0; _target->close( this ); } } //--------------------------------------------------------------------------- // Called by IOService::isOpen() with the arbitration lock held. bool IOKernelDebugger::handleIsOpen( const IOService * forClient ) const { if ( forClient == 0 ) return ( forClient != _client ); else return ( forClient == _client ); } //--------------------------------------------------------------------------- // Free the IOKernelDebugger object. void IOKernelDebugger::free() { // IOLog("IOKernelDebugger::%s %p\n", __FUNCTION__, this); super::free(); } #define PM_SECS(x) ((x) * 1000 * 1000) //--------------------------------------------------------------------------- // Handle controller's power state change notitifications. IOReturn IOKernelDebugger::powerStateWillChangeTo( IOPMPowerFlags flags, unsigned long stateNumber, IOService * policyMaker ) { IOReturn ret = IOPMAckImplied; if ( ( flags & IOPMDeviceUsable ) == 0 ) { // Controller is about to transition to an un-usable state. // The debugger nub should be disabled. this->retain(); thread_call_func( (thread_call_func_t) pmDisableDebugger, this, /* parameter */ FALSE ); /* disable unique call filter */ ret = PM_SECS(3); /* Must ACK within 3 seconds */ } return ret; } IOReturn IOKernelDebugger::powerStateDidChangeTo( IOPMPowerFlags flags, unsigned long stateNumber, IOService * policyMaker ) { IOReturn ret = IOPMAckImplied; if ( flags & IOPMDeviceUsable ) { // Controller has transitioned to an usable state. // The debugger nub should be enabled if necessary. this->retain(); thread_call_func( (thread_call_func_t) pmEnableDebugger, this, /* parameter */ FALSE ); /* disable unique call filter */ ret = PM_SECS(3); /* Must ACK within 3 seconds */ } return ret; } //--------------------------------------------------------------------------- // Static member function: Enable the debugger nub after the controller // transitions into an usable state. 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 ) ) { // This is bad, unable to re-enable the controller after sleep. IOLog("IOKernelDebugger: Unable to re-enable controller\n"); } else { registerHandler( debugger->_target, debugger->_txHandler, debugger->_rxHandler ); debugger->_pmDisabled = false; } } debugger->unlockForArbitration(); // Ack the power state change. debugger->_target->acknowledgePowerChange( debugger ); debugger->release(); } //--------------------------------------------------------------------------- // Static member function: Disable the debugger nub before the controller // transitions into an unusable state. void IOKernelDebugger::pmDisableDebugger( IOKernelDebugger * debugger ) { IONetworkController * ctr; assert( debugger ); ctr = OSDynamicCast( IONetworkController, debugger->_target ); debugger->lockForArbitration(); if ( debugger->_client && ( debugger->_pmDisabled == false ) ) { // Keep an open on the controller, but inhibit access to the // controller's debugger handlers, and disable controller's // hardware support for the debugger. registerHandler( 0 ); if ( ctr ) ctr->doDisable( debugger ); debugger->_pmDisabled = true; } debugger->unlockForArbitration(); // Ack the power state change. debugger->_target->acknowledgePowerChange( debugger ); debugger->release(); }