#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) + _vectorBase)
#define SYS_TO_PIC_VECTOR(sv) \
((sv) - _vectorBase);
bool AppleAPIC::start( IOService * provider )
{
OSNumber * num;
const OSSymbol * sym;
_handleSleepWakeFunction = OSSymbol::withCString(
kHandleSleepWakeFunction );
_setVectorPhysicalDestination = OSSymbol::withCString(
kSetVectorPhysicalDestination );
if (!_handleSleepWakeFunction || !_setVectorPhysicalDestination)
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 );
if (_vectorCount >= 0xFF)
{
APIC_LOG("IOAPIC-%ld: excessive vector count (%ld)\n",
_vectorBase, _vectorCount);
goto fail;
}
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();
setProperty(kBaseVectorNumberKey, _vectorBase, 32);
setProperty(kVectorCountKey, _vectorCount, 32);
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%02x Vectors %d:%d\n",
(uint32_t) GET_FIELD( indexRead( kIndexVER ), kVERVersion ),
(uint32_t) _vectorBase, (uint32_t) (_vectorBase + _vectorCount - 1));
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 ( _setVectorPhysicalDestination )
{
_setVectorPhysicalDestination->release();
_setVectorPhysicalDestination = 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-%d: reg %02x = %08x\n", (uint32_t) _vectorBase, i,
(uint32_t) indexRead(i));
}
for ( int i = 0x10; i < 0x40; i+=2 )
{
kprintf("IOAPIC-%d: reg %02x = %08x %08x\n", (uint32_t) _vectorBase, i,
(uint32_t) indexRead(i + 1), (uint32_t) 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( IOInterruptVectorNumber 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( IOInterruptVectorNumber 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( IOInterruptVectorNumber 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( IOInterruptVectorNumber vectorNumber,
IOInterruptVector * vector)
{
APIC_LOG("IOAPIC-%ld: %s( %ld )\n", _vectorBase, __FUNCTION__, vectorNumber);
return true;
}
IOInterruptAction AppleAPIC::getInterruptHandlerAddress( void )
{
return OSMemberFunctionCast(IOInterruptAction,
this, &AppleAPIC::handleInterrupt);
}
void AppleAPIC::disableVectorHard( IOInterruptVectorNumber vectorNumber,
IOInterruptVector * vector )
{
APIC_LOG("IOAPIC-%ld: %s %ld\n", _vectorBase, __FUNCTION__, vectorNumber);
disableVectorEntry( vectorNumber );
}
void AppleAPIC::enableVector( IOInterruptVectorNumber 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;
IOInterruptVectorNumber 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;
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::setVectorPhysicalDestination( UInt32 vectorNumber,
UInt32 apicID )
{
VectorEntry * entry;
kprintf("IOAPIC-%d: %s( %d, %d )\n", (uint32_t) _vectorBase, __FUNCTION__,
(uint32_t) vectorNumber, (uint32_t) apicID);
if (vectorNumber >= (UInt32)_vectorCount)
return kIOReturnBadArgument;
if (apicID > 255)
return kIOReturnBadArgument;
disableVectorEntry( vectorNumber );
entry = &_vectorTable[ vectorNumber ];
entry->h32 = (apicID << kRTHIDestinationShift) & kRTHIDestinationMask;
writeVectorEntry( vectorNumber );
return kIOReturnSuccess;
}
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;
}
else if ( function == _setVectorPhysicalDestination )
{
return setVectorPhysicalDestination( (uintptr_t) param1, (uintptr_t) param2 );
}
return super::callPlatformFunction( function, waitForFunction,
param1, param2, param3, param4 );
}