2017-02-28 15:43:08 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 1998-2008 Apple 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@
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
* IOKernelDebugger.cpp
|
|
|
|
*
|
|
|
|
* HISTORY
|
|
|
|
*/
|
|
|
|
|
|
|
|
#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 <IOKit/IOBSD.h> //for kIOBSDNameKey
|
|
|
|
#include <libkern/OSAtomic.h>
|
|
|
|
#include "IONetworkControllerPrivate.h"
|
|
|
|
|
|
|
|
#define kIOPrimaryDebugPortKey "IOPrimaryDebugPort"
|
|
|
|
|
|
|
|
#define kMatchNameArg "kdp_match_name"
|
|
|
|
#define kMatchMacArg "kdp_match_mac"
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
// IOKDP
|
|
|
|
|
|
|
|
class IOKDP : public IOService
|
|
|
|
{
|
|
|
|
OSDeclareDefaultStructors( IOKDP )
|
|
|
|
|
|
|
|
public:
|
|
|
|
/*! @function probe
|
|
|
|
@abstract verify controller meets criteria for debugger
|
|
|
|
@discussion This checks that the controller that is attaching the debugger
|
|
|
|
meets the criteria set by the user on the kernel command line. Current boot-args
|
|
|
|
are kdp_match_mac=001122334455 to match against a specific MAC address and
|
|
|
|
kdp_match_name=bsdname to match against interfaces with the specified BSD interface
|
|
|
|
name (for example, en1)
|
|
|
|
*/
|
|
|
|
|
2022-12-04 02:45:32 +00:00
|
|
|
virtual IOService *probe(IOService *provider, SInt32 *score) APPLE_KEXT_OVERRIDE;
|
2017-02-28 15:43:08 +00:00
|
|
|
|
2022-12-04 02:45:32 +00:00
|
|
|
virtual bool start( IOService * provider ) APPLE_KEXT_OVERRIDE;
|
2017-02-28 15:43:08 +00:00
|
|
|
|
2022-12-04 02:45:32 +00:00
|
|
|
virtual void stop( IOService * provider ) APPLE_KEXT_OVERRIDE;
|
2017-02-28 15:43:08 +00:00
|
|
|
|
|
|
|
virtual IOReturn message( UInt32 type,
|
|
|
|
IOService * provider,
|
2022-12-04 02:45:32 +00:00
|
|
|
void * argument = 0 ) APPLE_KEXT_OVERRIDE;
|
2017-02-28 15:43:08 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
// IOKDP defined globals.
|
|
|
|
|
|
|
|
static IOLock * gIOKDPLock = 0;
|
|
|
|
static IOKDP * gIOKDP = 0;
|
|
|
|
static UInt32 gDebugBootArg = 0;
|
|
|
|
|
|
|
|
class IOKDPGlobals
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
IOKDPGlobals();
|
|
|
|
~IOKDPGlobals();
|
|
|
|
|
|
|
|
inline bool isValid() const;
|
|
|
|
};
|
|
|
|
|
|
|
|
static IOKDPGlobals gIOKDPGlobals;
|
|
|
|
|
|
|
|
IOKDPGlobals::IOKDPGlobals()
|
|
|
|
{
|
|
|
|
gIOKDPLock = IOLockAlloc();
|
|
|
|
|
|
|
|
PE_parse_boot_argn( "debug", &gDebugBootArg, sizeof (gDebugBootArg) );
|
|
|
|
}
|
|
|
|
|
|
|
|
IOKDPGlobals::~IOKDPGlobals()
|
|
|
|
{
|
|
|
|
if ( gIOKDPLock )
|
|
|
|
{
|
|
|
|
IOLockFree( gIOKDPLock );
|
|
|
|
gIOKDPLock = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IOKDPGlobals::isValid() const
|
|
|
|
{
|
|
|
|
return ( gIOKDPLock );
|
|
|
|
}
|
|
|
|
|
|
|
|
#define super IOService
|
|
|
|
OSDefineMetaClassAndStructors( IOKDP, IOService )
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
// start/stop/message.
|
|
|
|
IOService *IOKDP::probe(IOService *provider, SInt32 *score)
|
|
|
|
{
|
|
|
|
char textBuffer[32];
|
|
|
|
|
|
|
|
// we expect our provider is an IOKernelDebugger and that its provider is an IONetworkController.
|
|
|
|
// If the controller has an IONetworkInterface client (it should) we can use info in it to
|
|
|
|
// determine if this is the best match for a debugger.
|
|
|
|
IONetworkInterface *interface = 0;
|
|
|
|
OSObject *client;
|
|
|
|
IOService *controller = provider->getProvider();
|
|
|
|
|
|
|
|
// IOLog("_kdp_ probing...\n");
|
|
|
|
if (controller)
|
|
|
|
{
|
|
|
|
OSIterator *clients = controller->getClientIterator();
|
|
|
|
//try to find a network interface on the provider
|
|
|
|
while ((client = clients->getNextObject()))
|
|
|
|
{
|
|
|
|
if ((interface = OSDynamicCast(IONetworkInterface, client)))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
clients->release();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
if(PE_parse_boot_argn( kMatchNameArg, textBuffer, sizeof(textBuffer)))
|
|
|
|
{
|
|
|
|
if(!interface) //user wants name match but we're not on a controller with an interface
|
|
|
|
{
|
|
|
|
//IOLog("_kdp_ no interface\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
OSString *bsdname = OSDynamicCast(OSString, interface->getProperty(kIOBSDNameKey));
|
|
|
|
if(!bsdname)
|
|
|
|
{
|
|
|
|
//IOLog("_kdp_ no bsd property\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if(bsdname->isEqualTo(textBuffer) == false)
|
|
|
|
{
|
|
|
|
//IOLog("_kdp_ name doesn't match %s\n", textBuffer);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(PE_parse_boot_argn( kMatchMacArg, textBuffer, sizeof(textBuffer)))
|
|
|
|
{
|
|
|
|
char ctrMac[13];
|
|
|
|
if(!controller) //looking for mac match, but the debugger isn't on a controller (!?)
|
|
|
|
{
|
|
|
|
//IOLog("_kdp_ no controller\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
OSData * macAddr = OSDynamicCast(OSData, controller->getProperty(kIOMACAddress));
|
|
|
|
if ( (macAddr == 0) || (macAddr->getLength() != 6) )
|
|
|
|
{
|
|
|
|
//IOLog("_kdp_ bad mac\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//make sure command line mac is in upper case
|
|
|
|
for(int i=0; i<12; i++)
|
|
|
|
textBuffer[i] = textBuffer[i] >= 'a' && textBuffer[i] <= 'z' ? textBuffer[i] - 32 : textBuffer[i];
|
|
|
|
|
|
|
|
// now convert the controller mac property to a string
|
|
|
|
unsigned char *macData = (unsigned char *)macAddr->getBytesNoCopy();
|
|
|
|
snprintf(ctrMac, sizeof(ctrMac), "%02X%02X%02X%02X%02X%02X", macData[0], macData[1], macData[2], macData[3], macData[4], macData[5]);
|
|
|
|
|
|
|
|
//now see if they match...
|
|
|
|
if(strncmp(ctrMac, textBuffer, 12))
|
|
|
|
{
|
|
|
|
//IOLog("_kdp_ mac doesn't match %s\n", textBuffer);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2022-12-04 02:45:32 +00:00
|
|
|
|
|
|
|
#if TARGET_OS_IPHONE
|
|
|
|
return 0; // Do not use built-in Ethernet as default debugger in embedded OS <rdar://31970820>
|
|
|
|
#else
|
|
|
|
//else default to old plist metric: the IOKernelDebugger has IOPrimaryDebugPort property
|
|
|
|
OSBoolean *pdp = OSDynamicCast(OSBoolean, provider->getProperty(kIOPrimaryDebugPortKey));
|
|
|
|
if(!pdp || pdp->isFalse())
|
|
|
|
{
|
|
|
|
//IOLog("_kdp_ not primary debug port\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
break;
|
2017-02-28 15:43:08 +00:00
|
|
|
}while(false);
|
|
|
|
|
|
|
|
//IOLog("_kdp_ MATCHED!\n");
|
|
|
|
|
|
|
|
//make sure the super is ok with this.
|
|
|
|
IOService *ret = super::probe(provider, score);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 ( provider->open(this) == false )
|
|
|
|
break;
|
|
|
|
|
|
|
|
gIOKDP = this;
|
|
|
|
ret = true;
|
|
|
|
}
|
|
|
|
while ( false );
|
|
|
|
|
|
|
|
IOLockUnlock( gIOKDPLock );
|
|
|
|
|
|
|
|
if ( ret ) registerService();
|
|
|
|
|
|
|
|
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 );
|
|
|
|
typedef UInt32 (*kdp_link_t)(void);
|
|
|
|
typedef boolean_t (*kdp_mode_t)(boolean_t active);
|
|
|
|
void kdp_register_send_receive( kdp_send_t send, kdp_receive_t receive );
|
|
|
|
void kdp_unregister_send_receive( kdp_send_t send, kdp_receive_t receive );
|
|
|
|
void kdp_register_link( kdp_link_t link, kdp_mode_t mode );
|
|
|
|
void kdp_unregister_link( kdp_link_t link, kdp_mode_t mode );
|
|
|
|
void kdp_set_interface(void *, const void *);
|
|
|
|
}
|
|
|
|
|
|
|
|
#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;
|
|
|
|
IODebuggerLinkStatusHandler gIODebuggerLinkStatusHandler = 0;
|
|
|
|
IODebuggerSetModeHandler gIODebuggerSetModeHandler = 0;
|
|
|
|
UInt32 gIODebuggerTxBytes = 0;
|
|
|
|
UInt32 gIODebuggerRxBytes = 0;
|
|
|
|
SInt32 gIODebuggerSemaphore = 0;
|
|
|
|
UInt32 gIODebuggerFlag = 0;
|
|
|
|
UInt8 gIODebuggerSignalled = 0;
|
|
|
|
|
|
|
|
// Global debugger flags.
|
|
|
|
//
|
|
|
|
enum {
|
|
|
|
kIODebuggerFlagRegistered = 0x01,
|
|
|
|
kIODebuggerFlagWarnNullHandler = 0x02
|
|
|
|
};
|
|
|
|
|
|
|
|
// Expansion variables.
|
|
|
|
//
|
|
|
|
#define _state _reserved->stateVars[0]
|
|
|
|
#define _activationChangeThreadCall _reserved->activationChangeThreadCall
|
|
|
|
#define _interfaceNotifier _reserved->interfaceNotifier
|
|
|
|
#define _linkStatusHandler _reserved->linkStatusHandler
|
|
|
|
#define _setModeHandler _reserved->setModeHandler
|
|
|
|
#define EARLY_DEBUG_SUPPORT (gDebugBootArg != 0)
|
|
|
|
|
|
|
|
static void handleActivationChange( IOKernelDebugger * debugger,
|
|
|
|
void * change );
|
|
|
|
|
|
|
|
bool IOKernelDebugger::interfacePublished(
|
|
|
|
void * target, void *param, IOService * service, IONotifier * notifier )
|
|
|
|
{
|
|
|
|
IOService *debugger = (IOService *)target;
|
|
|
|
IONetworkInterface *interface = OSDynamicCast(IONetworkInterface, service);
|
|
|
|
|
|
|
|
//IOLog("new (or changes on) interface detected\n");
|
|
|
|
//only reregister ourselves if the interface has the same controller (provider) as we do.
|
|
|
|
if(debugger && service && debugger->getProvider() == service->getProvider())
|
|
|
|
{
|
|
|
|
//IOLog("it's on our controller- reregister\n");
|
|
|
|
debugger->registerService();
|
|
|
|
if (interface)
|
|
|
|
interface->debuggerRegistered();
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
// 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 transmit 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
// The KDP link up dispatch function. Dispatches KDP link up queries to the
|
|
|
|
// registered link up handler. This function is registered with KDP via
|
|
|
|
// kdp_register_link().
|
|
|
|
|
|
|
|
UInt32 IOKernelDebugger::kdpLinkStatusDispatcher( void )
|
|
|
|
{
|
|
|
|
if ( gIODebuggerSemaphore )
|
|
|
|
return 0; // FIXME - Driver is busy!
|
|
|
|
|
|
|
|
return (*gIODebuggerLinkStatusHandler)( gIODebuggerDevice);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
// The KDP set mode dispatch function. Dispatches KDP set mode commands to the
|
|
|
|
// registered setMode handler. This function is registered with KDP via
|
|
|
|
// kdp_register_link().
|
|
|
|
|
|
|
|
boolean_t IOKernelDebugger::kdpSetModeDispatcher(boolean_t active)
|
|
|
|
{
|
|
|
|
if ( gIODebuggerSemaphore )
|
|
|
|
return FALSE; // FIXME - Driver is busy!
|
|
|
|
|
|
|
|
if ((*gIODebuggerSetModeHandler)( gIODebuggerDevice, active == TRUE ? true : false) == true)
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
UInt32 IOKernelDebugger::nullLinkStatusHandler(__unused IOService * target)
|
|
|
|
{
|
|
|
|
return kIONetworkLinkValid | kIONetworkLinkActive; /* default is link up. */
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IOKernelDebugger::nullSetModeHandler(__unused IOService * target,
|
|
|
|
__unused bool active)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
// Take the debugger lock conditionally.
|
|
|
|
|
|
|
|
IODebuggerLockState IOKernelDebugger::lock( IOService * object )
|
|
|
|
{
|
|
|
|
if ( gIODebuggerDevice == object )
|
|
|
|
{
|
|
|
|
OSIncrementAtomic( &gIODebuggerSemaphore );
|
|
|
|
return kIODebuggerLockTaken;
|
|
|
|
}
|
|
|
|
return (IODebuggerLockState) 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline static void invokeDebugger(void)
|
|
|
|
{
|
|
|
|
if ( gIODebuggerSemaphore )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (OSTestAndClear(0, &gIODebuggerSignalled) == false) {
|
|
|
|
Debugger("remote NMI");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
// Release the debugger lock if the kIODebuggerLockTaken flag is set.
|
|
|
|
|
|
|
|
void IOKernelDebugger::unlock( IODebuggerLockState state )
|
|
|
|
{
|
|
|
|
if ( state & kIODebuggerLockTaken ) {
|
|
|
|
OSDecrementAtomic( &gIODebuggerSemaphore );
|
|
|
|
|
|
|
|
invokeDebugger();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
// drop into the Debugger when safe
|
|
|
|
void IOKernelDebugger::signalDebugger(void)
|
|
|
|
{
|
|
|
|
OSTestAndSet(0, &gIODebuggerSignalled);
|
|
|
|
invokeDebugger();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
// Initialize an IOKernelDebugger instance.
|
|
|
|
|
|
|
|
bool IOKernelDebugger::init( IOService * target,
|
|
|
|
IODebuggerTxHandler txHandler,
|
|
|
|
IODebuggerRxHandler rxHandler,
|
|
|
|
IODebuggerLinkStatusHandler linkStatusHandler,
|
|
|
|
IODebuggerSetModeHandler setModeHandler)
|
|
|
|
{
|
|
|
|
if ( ( super::init() == false ) ||
|
|
|
|
( OSDynamicCast(IOService, target) == 0 ) ||
|
|
|
|
( txHandler == 0 ) ||
|
|
|
|
( rxHandler == 0 ) )
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Allocate memory for the ExpansionData structure.
|
|
|
|
|
|
|
|
_reserved = IONew( ExpansionData, 1 );
|
|
|
|
if ( _reserved == 0 )
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
_activationChangeThreadCall = thread_call_allocate(
|
|
|
|
(thread_call_func_t) handleActivationChange,
|
|
|
|
(thread_call_param_t) this );
|
|
|
|
|
|
|
|
// if user wants bsd name matching, odds are good that the interface won't
|
|
|
|
// have been named at the time IOKDP is matching on us...fortunately IONetworkStack
|
|
|
|
// reregisters the interface service when it gets named, so we can add a notifier
|
|
|
|
// and reregister ourselves at that time in order to try matching IOKDP again.
|
|
|
|
char textBuffer[64];
|
|
|
|
if (PE_parse_boot_argn( kMatchNameArg, textBuffer, sizeof(textBuffer)))
|
|
|
|
{
|
|
|
|
OSDictionary * matching = serviceMatching(kIONetworkInterfaceClass);
|
|
|
|
if (matching)
|
|
|
|
{
|
|
|
|
_interfaceNotifier = addMatchingNotification(
|
|
|
|
/* type */ gIOPublishNotification,
|
|
|
|
/* match */ matching,
|
|
|
|
/* action */ interfacePublished,
|
|
|
|
/* param */ this );
|
|
|
|
matching->release();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else _interfaceNotifier = 0;
|
|
|
|
|
|
|
|
if ( !_activationChangeThreadCall )
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cache the target and handlers provided.
|
|
|
|
|
|
|
|
_target = target;
|
|
|
|
_txHandler = txHandler;
|
|
|
|
_rxHandler = rxHandler;
|
|
|
|
_linkStatusHandler = linkStatusHandler;
|
|
|
|
_setModeHandler = setModeHandler;
|
|
|
|
_state = 0;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
// Factory method which performs allocation and initialization of an
|
|
|
|
// IOKernelDebugger instance.
|
|
|
|
|
|
|
|
IOKernelDebugger * IOKernelDebugger::debugger( IOService * target,
|
|
|
|
IODebuggerTxHandler txHandler,
|
|
|
|
IODebuggerRxHandler rxHandler,
|
|
|
|
IODebuggerLinkStatusHandler linkStatusHandler,
|
|
|
|
IODebuggerSetModeHandler setModeHandler)
|
|
|
|
{
|
|
|
|
IOKernelDebugger * debugger = new IOKernelDebugger;
|
|
|
|
|
|
|
|
if (debugger && (debugger->init( target, txHandler, rxHandler, linkStatusHandler, setModeHandler ) == false))
|
|
|
|
{
|
|
|
|
debugger->release();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// determine if this debugger is the "primary" debugger- the one KDP will attach to unless told otherwise
|
|
|
|
IOService * device = target->getProvider(); //get info from our provider's provider
|
|
|
|
//the debugger is primary if...
|
|
|
|
if ( device && device->getProperty( "built-in" )) // ...it is a built in controller...
|
|
|
|
{
|
|
|
|
OSObject *locationProperty = device->copyProperty("location");
|
|
|
|
OSData *locationAsData = OSDynamicCast(OSData, locationProperty);
|
|
|
|
if(!locationAsData || // ...AND we don't know anything else about its location.....
|
|
|
|
(strcmp( (char *)locationAsData->getBytesNoCopy(), "1")== 0) ) // ...OR we do know its location and it is 'slot' 1
|
|
|
|
debugger->setProperty( kIOPrimaryDebugPortKey, true );
|
|
|
|
else
|
|
|
|
debugger->setProperty( kIOPrimaryDebugPortKey, false );
|
|
|
|
if(locationProperty)
|
|
|
|
locationProperty->release();
|
|
|
|
}
|
|
|
|
return debugger;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
// Register the debugger handlers.
|
|
|
|
|
|
|
|
void IOKernelDebugger::registerHandler( IOService * target,
|
|
|
|
IODebuggerTxHandler txHandler,
|
|
|
|
IODebuggerRxHandler rxHandler,
|
|
|
|
IODebuggerLinkStatusHandler linkStatusHandler,
|
|
|
|
IODebuggerSetModeHandler setModeHandler)
|
|
|
|
{
|
|
|
|
bool doRegister;
|
|
|
|
|
|
|
|
assert( ( target == gIODebuggerDevice ) ||
|
|
|
|
( target == 0 ) ||
|
|
|
|
( gIODebuggerDevice == 0 ) );
|
|
|
|
|
|
|
|
doRegister = ( target && ( txHandler != 0 ) && ( rxHandler != 0 ) );
|
|
|
|
|
|
|
|
if ( !doRegister && ( gIODebuggerFlag & kIODebuggerFlagRegistered ) )
|
|
|
|
{
|
|
|
|
// Unregister the polling functions from KDP.
|
|
|
|
kdp_unregister_send_receive( (kdp_send_t) kdpTransmitDispatcher,
|
|
|
|
(kdp_receive_t) kdpReceiveDispatcher);
|
|
|
|
kdp_unregister_link( (kdp_link_t) kdpLinkStatusDispatcher, (kdp_mode_t) kdpSetModeDispatcher);
|
|
|
|
|
|
|
|
gIODebuggerFlag &= ~kIODebuggerFlagRegistered;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( txHandler == 0 ) txHandler = &IOKernelDebugger::nullTxHandler;
|
|
|
|
if ( rxHandler == 0 ) rxHandler = &IOKernelDebugger::nullRxHandler;
|
|
|
|
if ( linkStatusHandler == 0 ) linkStatusHandler = &IOKernelDebugger::nullLinkStatusHandler;
|
|
|
|
if ( setModeHandler == 0 ) setModeHandler = &IOKernelDebugger::nullSetModeHandler;
|
|
|
|
|
|
|
|
OSIncrementAtomic( &gIODebuggerSemaphore );
|
|
|
|
|
|
|
|
gIODebuggerDevice = target;
|
|
|
|
gIODebuggerTxHandler = txHandler;
|
|
|
|
gIODebuggerRxHandler = rxHandler;
|
|
|
|
gIODebuggerLinkStatusHandler = linkStatusHandler;
|
|
|
|
gIODebuggerSetModeHandler = setModeHandler;
|
|
|
|
gIODebuggerFlag |= kIODebuggerFlagWarnNullHandler;
|
|
|
|
|
|
|
|
OSDecrementAtomic( &gIODebuggerSemaphore );
|
|
|
|
|
|
|
|
if ( doRegister && (( gIODebuggerFlag & kIODebuggerFlagRegistered ) == 0) )
|
|
|
|
{
|
|
|
|
// if we're on a controller that has an interfacet(we should be), we can register
|
|
|
|
// the IONetworkInterface * as an identifier so that the bsd portion
|
|
|
|
// knows when to assign an ip to kdp
|
|
|
|
IONetworkInterface *interface = 0;
|
|
|
|
OSIterator *clients = target->getClientIterator();
|
|
|
|
//try to find a network interface on the target
|
|
|
|
while (OSObject *client = clients->getNextObject())
|
|
|
|
{
|
|
|
|
if ((interface = OSDynamicCast(IONetworkInterface, client)))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
clients->release();
|
|
|
|
|
|
|
|
if (interface) {
|
|
|
|
OSData *mac = OSDynamicCast(OSData,
|
|
|
|
interface->getController()->getProperty(kIOMACAddress));
|
|
|
|
|
|
|
|
kdp_set_interface(interface, mac ? mac->getBytesNoCopy() : NULL);
|
|
|
|
interface->debuggerRegistered();
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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_link((kdp_link_t) kdpLinkStatusDispatcher, (kdp_mode_t) kdpSetModeDispatcher);
|
|
|
|
kdp_register_send_receive( (kdp_send_t) kdpTransmitDispatcher,
|
|
|
|
(kdp_receive_t) kdpReceiveDispatcher);
|
|
|
|
|
|
|
|
// Limit ourself to a single real KDP registration.
|
|
|
|
|
|
|
|
gIODebuggerFlag |= kIODebuggerFlagRegistered;
|
|
|
|
|
|
|
|
publishResource("kdp");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
// enableTarget / disableTarget
|
|
|
|
|
|
|
|
enum {
|
|
|
|
kEventClientOpen = 0x0001,
|
|
|
|
kEventTargetUsable = 0x0002,
|
|
|
|
kEventDebuggerActive = 0x0004,
|
|
|
|
kTargetIsEnabled = 0x0100,
|
|
|
|
kTargetWasEnabled = 0x0200,
|
|
|
|
};
|
|
|
|
|
|
|
|
static void enableTarget( IOKernelDebugger * self,
|
|
|
|
IOService * target,
|
|
|
|
UInt32 * state,
|
|
|
|
UInt32 event )
|
|
|
|
{
|
|
|
|
IONetworkController * ctr = OSDynamicCast( IONetworkController, target );
|
|
|
|
|
|
|
|
#define kReadyMask ( kEventClientOpen | \
|
|
|
|
kEventTargetUsable | \
|
|
|
|
kEventDebuggerActive )
|
|
|
|
|
|
|
|
*state |= event;
|
|
|
|
*state &= ~kTargetWasEnabled;
|
|
|
|
|
|
|
|
if ( ( *state & kReadyMask ) == kReadyMask )
|
|
|
|
{
|
|
|
|
if ( !ctr || ( *state & kTargetIsEnabled ) ||
|
|
|
|
ctr->doEnable( self ) == kIOReturnSuccess )
|
|
|
|
{
|
|
|
|
if ( ( *state & kTargetIsEnabled ) == 0 )
|
|
|
|
*state |= kTargetWasEnabled;
|
|
|
|
*state |= kTargetIsEnabled;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void disableTarget( IOKernelDebugger * self,
|
|
|
|
IOService * target,
|
|
|
|
UInt32 * state,
|
|
|
|
UInt32 event )
|
|
|
|
{
|
|
|
|
IONetworkController * ctr = OSDynamicCast( IONetworkController, target );
|
|
|
|
UInt32 on = *state & kTargetIsEnabled;
|
|
|
|
|
|
|
|
*state &= ~( event | kTargetIsEnabled | kTargetWasEnabled );
|
|
|
|
|
|
|
|
if ( ctr && on ) ctr->doDisable( self );
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
// Called by open() with the arbitration lock held.
|
|
|
|
|
|
|
|
bool IOKernelDebugger::handleOpen( IOService * forClient,
|
|
|
|
IOOptionBits options,
|
|
|
|
void * arg )
|
|
|
|
{
|
|
|
|
bool ret = false;
|
|
|
|
|
|
|
|
do {
|
|
|
|
// Only a single client at a time.
|
|
|
|
|
|
|
|
if ( _client || !_target ) 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;
|
|
|
|
|
|
|
|
// For early debugging, the debugger must become active and enable
|
|
|
|
// the controller as early as possible. Otherwise, the debugger is
|
|
|
|
// not activated until the controller is enabled by BSD.
|
|
|
|
|
|
|
|
if ( EARLY_DEBUG_SUPPORT )
|
|
|
|
_state |= kEventDebuggerActive;
|
|
|
|
|
|
|
|
// Register interest in receiving notifications about controller
|
|
|
|
// power state changes. The controller is usable if it is power
|
|
|
|
// managed, and is in an usable state, or if the controller is
|
|
|
|
// not power managed.
|
|
|
|
|
|
|
|
if ( _target->registerInterestedDriver(this) &
|
|
|
|
( kIOPMDeviceUsable | kIOPMNotPowerManaged ) )
|
|
|
|
_state |= kEventTargetUsable;
|
|
|
|
|
|
|
|
// Enable the target if possible.
|
|
|
|
|
|
|
|
enableTarget( this, _target, &_state, kEventClientOpen );
|
|
|
|
if ( _state & kTargetWasEnabled )
|
|
|
|
{
|
|
|
|
// If the target was enabled, complete the registration.
|
|
|
|
IOLog("%s: registering debugger\n", getName());
|
|
|
|
registerHandler( _target, _txHandler, _rxHandler, _linkStatusHandler, _setModeHandler );
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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 )
|
|
|
|
{
|
|
|
|
if ( _target && _client && ( _client == forClient ) )
|
|
|
|
{
|
|
|
|
// There is no KDP un-registration. The best we can do is to
|
|
|
|
// register dummy handlers.
|
|
|
|
|
|
|
|
registerHandler( 0 );
|
|
|
|
|
|
|
|
disableTarget( this, _target, &_state, kEventClientOpen );
|
|
|
|
|
|
|
|
// Before closing the controller, remove interest in receiving
|
|
|
|
// notifications about controller power state changes.
|
|
|
|
|
|
|
|
_target->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()
|
|
|
|
{
|
|
|
|
if ( _reserved )
|
|
|
|
{
|
|
|
|
if ( _activationChangeThreadCall )
|
|
|
|
thread_call_free( _activationChangeThreadCall );
|
|
|
|
|
|
|
|
if ( _interfaceNotifier )
|
|
|
|
_interfaceNotifier->remove();
|
|
|
|
|
|
|
|
IODelete( _reserved, ExpansionData, 1 );
|
|
|
|
_reserved = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
super::free();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
// Handle controller's power state change notitifications.
|
|
|
|
|
|
|
|
IOReturn
|
|
|
|
IOKernelDebugger::powerStateWillChangeTo( IOPMPowerFlags flags,
|
|
|
|
unsigned long stateNumber,
|
|
|
|
IOService * policyMaker )
|
|
|
|
{
|
|
|
|
if ((flags & IOPMDeviceUsable) == 0)
|
|
|
|
{
|
|
|
|
// Controller is about to transition to an un-usable state.
|
|
|
|
// The debugger nub should be disabled.
|
|
|
|
|
|
|
|
lockForArbitration();
|
|
|
|
|
|
|
|
// Keep an open on the controller, but inhibit access to the
|
|
|
|
// controller's debugger handlers, and disable controller's
|
|
|
|
// hardware support for the debugger.
|
|
|
|
|
|
|
|
if ( _client ) registerHandler( 0 );
|
|
|
|
disableTarget( this, _target, &_state, kEventTargetUsable );
|
|
|
|
|
|
|
|
unlockForArbitration();
|
|
|
|
}
|
|
|
|
|
|
|
|
return IOPMAckImplied;
|
|
|
|
}
|
|
|
|
|
|
|
|
IOReturn
|
|
|
|
IOKernelDebugger::powerStateDidChangeTo( IOPMPowerFlags flags,
|
|
|
|
unsigned long stateNumber,
|
|
|
|
IOService * policyMaker )
|
|
|
|
{
|
|
|
|
if ( flags & IOPMDeviceUsable )
|
|
|
|
{
|
|
|
|
// Controller has transitioned to an usable state.
|
|
|
|
// The debugger nub should be enabled if necessary.
|
|
|
|
|
|
|
|
lockForArbitration();
|
|
|
|
|
|
|
|
enableTarget( this, _target, &_state, kEventTargetUsable );
|
|
|
|
if ( _state & kTargetWasEnabled )
|
|
|
|
registerHandler( _target, _txHandler, _rxHandler, _linkStatusHandler, _setModeHandler );
|
|
|
|
|
|
|
|
unlockForArbitration();
|
|
|
|
}
|
|
|
|
|
|
|
|
return IOPMAckImplied;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
// handleActivationChange
|
|
|
|
|
|
|
|
static void
|
|
|
|
handleActivationChange( IOKernelDebugger * debugger, void * change )
|
|
|
|
{
|
|
|
|
debugger->message( kMessageDebuggerActivationChange, 0, change );
|
|
|
|
debugger->release();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
// message()
|
|
|
|
|
|
|
|
IOReturn IOKernelDebugger::message( UInt32 type, IOService * provider,
|
|
|
|
void * argument )
|
|
|
|
{
|
|
|
|
IOReturn ret = kIOReturnSuccess;
|
|
|
|
|
|
|
|
switch ( type )
|
|
|
|
{
|
|
|
|
case kMessageControllerWasEnabledForBSD:
|
|
|
|
case kMessageControllerWasDisabledForBSD:
|
|
|
|
// For early debugging support, these messages are ignored.
|
|
|
|
// The debugger will enable the controller independently
|
|
|
|
// from BSD.
|
|
|
|
|
|
|
|
if ( EARLY_DEBUG_SUPPORT ) break;
|
|
|
|
|
|
|
|
// Process this change in another thread to avoid deadlocks
|
|
|
|
// between the controller's work loop, and our arbitration
|
|
|
|
// lock.
|
|
|
|
|
|
|
|
retain();
|
|
|
|
if ( thread_call_enter1( _activationChangeThreadCall,
|
|
|
|
(void *)(uintptr_t) type ) == TRUE )
|
|
|
|
release();
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case kMessageControllerWillShutdown:
|
|
|
|
// Disable controller before system shutdown and restart.
|
|
|
|
// Intentional fall-through to next case.
|
|
|
|
|
|
|
|
case kMessageDebuggerActivationChange:
|
|
|
|
lockForArbitration();
|
|
|
|
|
|
|
|
// If controller was enabled, activate the debugger.
|
|
|
|
// Otherwise make the debugger inactive.
|
|
|
|
|
|
|
|
if ( (void *) kMessageControllerWasEnabledForBSD == argument )
|
|
|
|
{
|
|
|
|
enableTarget( this, _target, &_state, kEventDebuggerActive );
|
|
|
|
if ( _state & kTargetWasEnabled )
|
|
|
|
registerHandler( _target, _txHandler, _rxHandler, _linkStatusHandler, _setModeHandler );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ( _client ) registerHandler( 0 );
|
|
|
|
disableTarget( this, _target, &_state, kEventDebuggerActive );
|
|
|
|
}
|
|
|
|
|
|
|
|
unlockForArbitration();
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
ret = super::message( type, provider, argument );
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|