#include <IOKit/IOLib.h>
#include <IOKit/IOPlatformExpert.h>
#include "AppleAPIC.h"
#include "Apple8259PIC.h"
#include "PICShared.h"
#define super IOInterruptController
OSDefineMetaClassAndStructors( AppleAPICInterruptController,
IOInterruptController )
#define GET_FIELD(v, f) \
(((v) & (f ## Mask)) >> (f ## Shift))
#define PIC_TO_SYS_VECTOR(pv) \
((pv) + kBaseIOInterruptVectors + _vectorBase)
#define SYS_TO_PIC_VECTOR(sv) \
((sv) - kBaseIOInterruptVectors - _vectorBase);
#define kSystemVectorNumber 256
class AppleAPICInterruptDispatcher
{
public:
AppleAPICInterruptDispatcher();
~AppleAPICInterruptDispatcher();
static void handleInterrupt( OSObject * target, void * refCon,
IOService * nub, int source );
bool isValid( void ) const;
bool registerAPICInterruptController(
IOInterruptController * apicDriver,
IOService * apicDevice,
long vectorBase,
long vectorCount );
};
static AppleAPICInterruptDispatcher gAppleAPICInterruptDispatcher;
static IOInterruptController * gDispatchTable[ kSystemVectorNumber ];
static IOLock * gDispatchLock;
static bool gDispatchRegistered;
bool AppleAPIC::start( IOService * provider )
{
OSNumber * num;
const OSSymbol * sym;
if ( gAppleAPICInterruptDispatcher.isValid() == false ||
super::start( provider ) == false )
goto fail;
_handleSleepWakeFunction = OSSymbol::withCString(
kHandleSleepWakeFunction );
if ( 0 == _handleSleepWakeFunction )
goto fail;
num = OSDynamicCast( OSNumber,
provider->getProperty( kBaseVectorNumberKey ) );
if ( num ) _vectorBase = num->unsigned32BitValue();
num = OSDynamicCast( OSNumber,
provider->getProperty( kDestinationAPICIDKey ) );
if ( 0 == num )
{
APIC_LOG("IOAPIC-%ld: no destination APIC ID\n", _vectorBase);
goto fail;
}
_destinationAddress = num->unsigned32BitValue();
_apicLock = IOSimpleLockAlloc();
if ( 0 == _apicLock )
{
APIC_LOG("IOAPIC-%ld: IOSimpleLockAlloc failed\n", _vectorBase);
goto fail;
}
num = OSDynamicCast( OSNumber,
provider->getProperty( kPhysicalAddressKey ) );
if ( 0 == num )
{
APIC_LOG("IOAPIC-%ld: no physical address\n", _vectorBase);
goto fail;
}
_apicMemory = IOMemoryDescriptor::withPhysicalAddress(
num->unsigned32BitValue(),
256,
kIODirectionInOut );
if ( 0 == _apicMemory )
{
APIC_LOG("IOAPIC-%ld: no memory for apicMemory\n", _vectorBase);
goto fail;
}
_apicMemory->prepare();
_apicMemoryMap = _apicMemory->map( kIOMapInhibitCache );
if ( 0 == _apicMemoryMap )
{
APIC_LOG("IOAPIC-%ld: memory mapping failed\n", _vectorBase);
goto fail;
}
_apicBaseAddr = _apicMemoryMap->getVirtualAddress();
APIC_LOG("IOAPIC-%ld: phys = %lx virt = %lx\n", _vectorBase,
num->unsigned32BitValue(), _apicBaseAddr);
_apicIDRegister = indexRead( kIndexID );
_vectorCount = GET_FIELD( indexRead( kIndexVER ), kVERMaxEntries );
APIC_LOG("IOAPIC-%ld: vector range = %ld:%ld\n",
_vectorBase, _vectorBase, _vectorBase + _vectorCount);
_vectorCount++;
vectors = IONew( IOInterruptVector, _vectorCount );
if ( 0 == vectors )
{
APIC_LOG("IOAPIC-%ld: no memory for shared vectors\n", _vectorBase);
goto fail;
}
bzero( vectors, sizeof(IOInterruptVector) * _vectorCount );
for ( int i = 0; i < _vectorCount; i++ )
{
vectors[i].interruptLock = IOLockAlloc();
if ( vectors[i].interruptLock == 0 )
{
APIC_LOG("IOAPIC-%ld: no memory for %dth vector lock\n",
_vectorBase, i);
goto fail;
}
}
_vectorTable = IONew( VectorEntry, _vectorCount );
if ( 0 == _vectorTable )
{
APIC_LOG("IOAPIC-%ld: no memory for vector table\n", _vectorBase);
goto fail;
}
resetVectorTable();
if ( gAppleAPICInterruptDispatcher.registerAPICInterruptController(
this,
provider,
_vectorBase + kBaseIOInterruptVectors,
_vectorCount ) == false )
{
goto fail;
}
sym = OSSymbol::withString( (OSString *)
provider->getProperty( kInterruptControllerNameKey ) );
if ( 0 == sym )
{
APIC_LOG("IOAPIC-%ld: no interrupt controller name\n", _vectorBase);
goto fail;
}
IOLog("IOAPIC: Version 0x%02lx Vectors %ld:%ld\n",
GET_FIELD( indexRead( kIndexVER ), kVERVersion ),
_vectorBase, _vectorBase + _vectorCount);
getPlatform()->registerInterruptController( (OSSymbol *) sym, this );
sym->release();
registerService();
APIC_LOG("IOAPIC-%ld: start success\n", _vectorBase);
return true;
fail:
return false;
}
void AppleAPIC::free( void )
{
APIC_LOG("IOAPIC-%ld: %s\n", _vectorBase, __FUNCTION__);
if ( _handleSleepWakeFunction )
{
_handleSleepWakeFunction->release();
_handleSleepWakeFunction = 0;
}
if ( vectors )
{
for ( int i = 0; i < _vectorCount; i++ )
{
if (vectors[i].interruptLock)
IOLockFree(vectors[i].interruptLock);
}
IODelete( vectors, IOInterruptVector, _vectorCount );
vectors = 0;
}
if ( _vectorTable )
{
IODelete( _vectorTable, VectorEntry, _vectorCount );
_vectorTable = 0;
}
if ( _apicMemoryMap )
{
_apicMemoryMap->release();
_apicMemoryMap = 0;
}
if ( _apicMemory )
{
_apicMemory->complete();
_apicMemory->release();
_apicMemory = 0;
}
if ( _apicLock )
{
IOSimpleLockFree( _apicLock );
_apicLock = 0;
}
super::free();
}
void AppleAPIC::dumpRegisters( void )
{
for ( int i = 0x0; i < 0x10; i++ )
{
kprintf("IOAPIC-%ld: reg %02x = %08lx\n", _vectorBase, i,
indexRead(i));
}
for ( int i = 0x10; i < 0x40; i+=2 )
{
kprintf("IOAPIC-%ld: reg %02x = %08lx %08lx\n", _vectorBase, i,
indexRead(i + 1), indexRead(i));
}
}
void AppleAPIC::resetVectorTable( void )
{
VectorEntry * entry;
for ( int vectorNumber = 0; vectorNumber < _vectorCount; vectorNumber++ )
{
entry = &_vectorTable[ vectorNumber ];
entry->l32 = ( PIC_TO_SYS_VECTOR(vectorNumber) & kRTLOVectorNumberMask )
| kRTLODeliveryModeFixed
| kRTLODestinationModePhysical
| kRTLOMaskDisabled;
entry->h32 = ( _destinationAddress << kRTHIDestinationShift ) &
kRTHIDestinationMask;
writeVectorEntry( vectorNumber );
}
}
void AppleAPIC::writeVectorEntry( long vectorNumber )
{
IOInterruptState state;
APIC_LOG("IOAPIC-%ld: %s %02ld = %08lx %08lx\n",
_vectorBase, __FUNCTION__, vectorNumber,
_vectorTable[vectorNumber].h32,
_vectorTable[vectorNumber].l32);
state = IOSimpleLockLockDisableInterrupt( _apicLock );
indexWrite( kIndexRTLO + vectorNumber * 2,
_vectorTable[vectorNumber].l32 );
indexWrite( kIndexRTHI + vectorNumber * 2,
_vectorTable[vectorNumber].h32 );
IOSimpleLockUnlockEnableInterrupt( _apicLock, state );
}
void AppleAPIC::writeVectorEntry( long vectorNumber, VectorEntry entry )
{
IOInterruptState state;
APIC_LOG("IOAPIC-%ld: %s %02ld = %08lx %08lx\n",
_vectorBase, __FUNCTION__, vectorNumber, entry.h32, entry.l32);
state = IOSimpleLockLockDisableInterrupt( _apicLock );
indexWrite( kIndexRTLO + vectorNumber * 2, entry.l32 );
indexWrite( kIndexRTHI + vectorNumber * 2, entry.h32 );
IOSimpleLockUnlockEnableInterrupt( _apicLock, state );
}
IOReturn AppleAPIC::getInterruptType( IOService * nub,
int source,
int * interruptType )
{
IOInterruptSource * interruptSources;
OSData * vectorData;
UInt32 vectorFlags;
if ( 0 == nub || 0 == interruptType )
return kIOReturnBadArgument;
interruptSources = nub->_interruptSources;
vectorData = interruptSources[source].vectorData;
if (vectorData->getLength() < sizeof(UInt64))
return kIOReturnNotFound;
vectorFlags = DATA_TO_FLAGS( vectorData );
if ((vectorFlags & kInterruptTriggerModeMask) == kInterruptTriggerModeEdge)
*interruptType = kIOInterruptTypeEdge;
else
*interruptType = kIOInterruptTypeLevel;
APIC_LOG("IOAPIC-%ld: %s( %s, %d ) = %s (vector %ld)\n",
_vectorBase, __FUNCTION__,
nub->getName(), source,
*interruptType == kIOInterruptTypeLevel ? "level" : "edge",
DATA_TO_VECTOR(vectorData));
return kIOReturnSuccess;
}
IOReturn AppleAPIC::registerInterrupt( IOService * nub,
int source,
void * target,
IOInterruptHandler handler,
void * refCon )
{
IOInterruptSource * interruptSources;
UInt32 vectorNumber;
OSData * vectorData;
interruptSources = nub->_interruptSources;
vectorData = interruptSources[source].vectorData;
vectorNumber = DATA_TO_VECTOR( vectorData );
if ( vectorNumber >= (UInt32) _vectorCount )
return kIOReturnBadArgument;
return super::registerInterrupt( nub, source, target, handler, refCon );
}
void AppleAPIC::initVector( long vectorNumber, IOInterruptVector * vector )
{
IOInterruptSource * interruptSources;
UInt32 vectorFlags;
OSData * vectorData;
interruptSources = vector->nub->_interruptSources;
vectorData = interruptSources[vector->source].vectorData;
if (vectorData->getLength() < sizeof(UInt64))
return;
vectorFlags = DATA_TO_FLAGS( vectorData );
_vectorTable[vectorNumber].l32 &= ~kRTLOTriggerModeMask;
if ((vectorFlags & kInterruptTriggerModeMask) == kInterruptTriggerModeEdge)
_vectorTable[vectorNumber].l32 |= kRTLOTriggerModeEdge;
else
_vectorTable[vectorNumber].l32 |= kRTLOTriggerModeLevel;
_vectorTable[vectorNumber].l32 &= ~kRTLOInputPolarityMask;
if ((vectorFlags & kInterruptPolarityMask) == kInterruptPolarityHigh)
_vectorTable[vectorNumber].l32 |= kRTLOInputPolarityHigh;
else
_vectorTable[vectorNumber].l32 |= kRTLOInputPolarityLow;
writeVectorEntry( vectorNumber );
APIC_LOG("IOAPIC-%ld: %s %ld to %s trigger, active %s\n",
_vectorBase, __FUNCTION__, vectorNumber,
(_vectorTable[vectorNumber].l32 & kRTLOTriggerModeLevel) ?
"level" : "edge",
(_vectorTable[vectorNumber].l32 & kRTLOInputPolarityLow) ?
"low" : "high");
}
bool AppleAPIC::vectorCanBeShared( long vectorNumber,
IOInterruptVector * vector)
{
APIC_LOG("IOAPIC-%ld: %s( %ld )\n", _vectorBase, vectorNumber);
return true;
}
IOInterruptAction AppleAPIC::getInterruptHandlerAddress( void )
{
return &AppleAPICInterruptDispatcher::handleInterrupt;
}
void AppleAPIC::disableVectorHard( long vectorNumber,
IOInterruptVector * vector )
{
APIC_LOG("IOAPIC-%ld: %s %ld\n", _vectorBase, __FUNCTION__, vectorNumber);
disableVectorEntry( vectorNumber );
}
void AppleAPIC::enableVector( long vectorNumber,
IOInterruptVector * vector )
{
APIC_LOG("IOAPIC-%ld: %s %ld\n", _vectorBase, __FUNCTION__, vectorNumber);
enableVectorEntry( vectorNumber );
}
extern "C" void lapic_end_of_interrupt( void );
IOReturn AppleAPIC::handleInterrupt( void * savedState,
IOService * nub,
int source )
{
IOInterruptVector * vector;
long vectorNumber;
vectorNumber = SYS_TO_PIC_VECTOR(source);
assert( vectorNumber >= 0 );
assert( vectorNumber < _vectorCount );
vector = &vectors[ vectorNumber ];
vector->interruptActive = 1;
if ( !vector->interruptDisabledSoft && vector->interruptRegistered )
{
vector->handler( vector->target, vector->refCon,
vector->nub, vector->source );
if ( vector->interruptDisabledSoft )
{
vector->interruptDisabledHard = 1;
disableVectorEntry( vectorNumber );
}
}
else
{
vector->interruptDisabledHard = 1;
disableVectorEntry( vectorNumber );
}
vector->interruptActive = 0;
lapic_end_of_interrupt();
return kIOReturnSuccess;
}
void AppleAPIC::prepareForSleep( void )
{
for ( int vectorNumber = 0; vectorNumber < _vectorCount; vectorNumber++ )
{
VectorEntry entry = _vectorTable[ vectorNumber ];
entry.l32 |= kRTLOMaskDisabled;
writeVectorEntry( vectorNumber, entry );
}
}
void AppleAPIC::resumeFromSleep( void )
{
outb( kPIC_OCW1(kPIC2BasePort), 0xFF );
outb( kPIC_OCW1(kPIC1BasePort), 0xFF );
indexWrite( kIndexID, _apicIDRegister );
for ( int vectorNumber = 0; vectorNumber < _vectorCount; vectorNumber++ )
{
VectorEntry entry = _vectorTable[ vectorNumber ];
entry.l32 |= kRTLOMaskDisabled;
writeVectorEntry( vectorNumber, entry );
writeVectorEntry( vectorNumber );
}
}
IOReturn AppleAPIC::callPlatformFunction( const OSSymbol * function,
bool waitForFunction,
void * param1, void * param2,
void * param3, void * param4 )
{
if ( function == _handleSleepWakeFunction )
{
if ( param1 )
prepareForSleep();
else
resumeFromSleep();
return kIOReturnSuccess;
}
return super::callPlatformFunction( function, waitForFunction,
param1, param2, param3, param4 );
}
AppleAPICInterruptDispatcher::AppleAPICInterruptDispatcher()
{
bzero(gDispatchTable,
sizeof(IOInterruptController *) * kSystemVectorNumber);
gDispatchLock = IOLockAlloc();
gDispatchRegistered = false;
}
AppleAPICInterruptDispatcher::~AppleAPICInterruptDispatcher()
{
if (gDispatchLock) IOLockFree(gDispatchLock);
}
void AppleAPICInterruptDispatcher::handleInterrupt( OSObject * target,
void * refCon,
IOService * nub,
int source )
{
assert( source < kSystemVectorNumber );
assert( gDispatchTable[source] );
if (gDispatchTable[source])
gDispatchTable[source]->handleInterrupt( refCon, nub, source );
}
bool AppleAPICInterruptDispatcher::isValid( void ) const
{
return ( 0 != gDispatchLock );
}
bool AppleAPICInterruptDispatcher::registerAPICInterruptController(
IOInterruptController * apicDriver,
IOService * apicDevice,
long vectorBase,
long vectorCount )
{
bool success = true;
IOLockLock( gDispatchLock );
for ( long vector = vectorBase;
vector < vectorBase + vectorCount &&
vector < kSystemVectorNumber;
vector++ )
{
if (gDispatchTable[vector] == 0)
gDispatchTable[vector] = apicDriver;
else
APIC_LOG("IOAPIC Dispatcher: vector %ld already taken\n",
vector);
}
if ( false == gDispatchRegistered )
{
apicDriver->getPlatform()->setCPUInterruptProperties( apicDevice );
if ( kIOReturnSuccess != apicDevice->registerInterrupt(
0,
apicDriver,
&handleInterrupt,
0 ) )
{
APIC_LOG("IOAPIC Dispatcher: registerInterrupt failed\n");
success = false;
}
else
{
apicDevice->enableInterrupt( 0 );
gDispatchRegistered = true;
}
}
IOLockUnlock( gDispatchLock );
return success;
}