#define __IOPCIDEVICE_INTERNAL__ 1
#include <IOKit/system.h>
#include <IOKit/pci/IOPCIBridge.h>
#include <IOKit/pci/IOPCIPrivate.h>
#include <PCIDriverKit/PCIDriverKitPrivate.h>
#include <IOKit/pci/IOAGPDevice.h>
#include <IOKit/pci/IOPCIConfigurator.h>
#include <IOKit/pci/IOPCIPrivate.h>
#include <IOKit/IOPlatformExpert.h>
#include <IOKit/IODeviceTreeSupport.h>
#include <IOKit/IOUserClient.h>
#include <IOKit/IOUserServer.h>
#include <IOKit/IOKitKeys.h>
#include <IOKit/IOLib.h>
#include <IOKit/assert.h>
#include <libkern/c++/OSContainers.h>
#include <libkern/version.h>
#if ACPI_SUPPORT
#include <IOKit/acpi/IOACPIPlatformDevice.h>
#endif
#ifndef VERSION_MAJOR
#error VERSION_MAJOR
#endif
enum
{
kPMEnable = 0x01,
kPMEOption = 0x02,
kPMEOptionS3Disable = 0x04,
kPMEOptionWakeReason = 0x08,
};
#if !DEVELOPMENT && !defined(__x86_64__)
#define DLOG(fmt, args...)
#else
#define DLOG(fmt, args...) \
do { \
if ((gIOPCIFlags & kIOPCIConfiguratorIOLog) && !ml_at_interrupt_context()) \
IOLog(fmt, ## args); \
if (gIOPCIFlags & kIOPCIConfiguratorKPrintf) \
kprintf(fmt, ## args); \
} while(0)
#endif
#ifdef PROT_DEVICE
extern "C"
kern_return_t
mach_vm_protect(
vm_map_t map,
mach_vm_offset_t start,
mach_vm_size_t size,
boolean_t set_maximum,
vm_prot_t new_protection);
#endif
#define super IOService
OSDefineMetaClassAndStructors(IOPCIDevice, IOService)
OSMetaClassDefineReservedUnused(IOPCIDevice, 3);
OSMetaClassDefineReservedUnused(IOPCIDevice, 4);
OSMetaClassDefineReservedUnused(IOPCIDevice, 5);
OSMetaClassDefineReservedUnused(IOPCIDevice, 6);
OSMetaClassDefineReservedUnused(IOPCIDevice, 7);
OSMetaClassDefineReservedUnused(IOPCIDevice, 8);
OSMetaClassDefineReservedUnused(IOPCIDevice, 9);
OSMetaClassDefineReservedUnused(IOPCIDevice, 10);
OSMetaClassDefineReservedUnused(IOPCIDevice, 11);
OSMetaClassDefineReservedUnused(IOPCIDevice, 12);
OSMetaClassDefineReservedUnused(IOPCIDevice, 13);
OSMetaClassDefineReservedUnused(IOPCIDevice, 14);
OSMetaClassDefineReservedUnused(IOPCIDevice, 15);
unsigned long
IOPCIDevice::maxCapabilityForDomainState ( IOPMPowerFlags domainState )
{
if (domainState & kIOPMPowerOn) return (kIOPCIDeviceOnState);
if (domainState & kIOPMSoftSleep) return (kIOPCIDeviceDozeState);
if (domainState & kIOPMConfigRetained) return (kIOPCIDevicePausedState);
return (kIOPCIDeviceOffState);
}
unsigned long
IOPCIDevice::initialPowerStateForDomainState ( IOPMPowerFlags domainState )
{
if (domainState & kIOPMRootDomainState) return (kIOPCIDeviceOffState);
if (domainState & kIOPMPowerOn) return (kIOPCIDeviceOnState);
if (domainState & kIOPMSoftSleep) return (kIOPCIDeviceDozeState);
if (domainState & kIOPMConfigRetained) return (kIOPCIDevicePausedState);
return (kIOPCIDeviceOffState);
}
unsigned long
IOPCIDevice::powerStateForDomainState ( IOPMPowerFlags domainState )
{
if (domainState & kIOPMPowerOn) return (kIOPCIDeviceOnState);
if (domainState & kIOPMSoftSleep) return (kIOPCIDeviceDozeState);
if (domainState & kIOPMConfigRetained) return (kIOPCIDevicePausedState);
return (kIOPCIDeviceOffState);
}
IOService *
IOPCIDeviceDMAOriginator(IOPCIDevice * device)
{
IOService * dmaOriginator = device;
IORegistryEntry * parent = 0;
IORegistryEntry * child = 0;
IOPCIDevice * funcZero;
OSObject * prop = 0;
OSData * data;
uint32_t value = 0;
if (device->space.s.functionNum) do
{
parent = device->copyParentEntry(gIODTPlane);
if (!parent) break;
child = parent->copyChildEntry(gIODTPlane);
if (!child) break;
funcZero = OSDynamicCast(IOPCIDevice, child);
if (!funcZero) break;
prop = funcZero->copyProperty(kIOPCIFunctionsDependentKey);
if ((data = OSDynamicCast(OSData, prop)))
{
value = ((uint32_t *) data->getBytesNoCopy())[0];
}
if (value) dmaOriginator = funcZero;
}
while (false);
OSSafeReleaseNULL(prop);
OSSafeReleaseNULL(child);
OSSafeReleaseNULL(parent);
return (dmaOriginator);
}
bool IOPCIDevice::attach( IOService * provider )
{
if (!super::attach(provider)) return (false);
#if ACPI_SUPPORT
IOACPIPlatformDevice * device;
uint32_t idx;
bool hasPS;
device = 0;
if (reserved->configEntry
&& (device = (IOACPIPlatformDevice *) reserved->configEntry->acpiDevice)
&& !device->metaCast("IOACPIPlatformDevice")) device = 0;
for (idx = kIOPCIDeviceOffState, hasPS = false; idx <= kIOPCIDeviceOnState; idx++)
{
reserved->psMethods[idx] = -1;
if (!device) continue;
if (kIOReturnSuccess == device->validateObject(gIOPCIPSMethods[idx]))
{
reserved->psMethods[idx] = idx;
hasPS = true;
}
else if (kIOPCIDeviceDozeState == idx)
{
reserved->psMethods[idx] = reserved->psMethods[kIOPCIDeviceOffState];
}
}
reserved->psMethods[kIOPCIDevicePausedState] = reserved->psMethods[kIOPCIDeviceOnState];
if (hasPS) DLOG("%s: _PSx %d, %d, %d, %d\n", getName(),
reserved->psMethods[0], reserved->psMethods[1],
reserved->psMethods[2], reserved->psMethods[3]);
reserved->lastPSMethod = reserved->psMethods[kIOPCIDeviceOnState];
#endif
reserved->pciPMState = kIOPCIDeviceOnState;
if (reserved->powerCapability)
{
uint16_t pmcsr;
reserved->pmControlStatus = reserved->powerCapability + 4;
pmcsr = extendedConfigRead16(reserved->pmControlStatus);
if (pmcsr & kPCIPMCSPMEStatus)
{
extendedConfigWrite16(reserved->pmControlStatus, pmcsr);
}
}
PMinit();
temporaryPowerClampOn();
IOPCIRegisterPowerDriver(this, false);
reserved->pmState = kIOPCIDeviceOnState;
reserved->pmActive = true;
IOService * originator = IOPCIDeviceDMAOriginator(this);
IOService * powerProvider = (originator == this) ? provider : originator;
powerProvider->joinPMtree( this);
return (true);
}
void IOPCIDevice::detach( IOService * provider )
{
IOPCIConfigShadow * shadow;
if ((shadow = configShadow(this)) && shadow->tunnelRoot)
{
setTunnelL1Enable(this, true);
}
PMstop();
IOLockLock(reserved->lock);
while (false && reserved->pmActive)
{
reserved->pmWait = true;
IOLockSleep(reserved->lock, &reserved->pmActive, THREAD_UNINT);
}
IOLockUnlock(reserved->lock);
if (parent) parent->removeDevice(this);
super::detach(provider);
detachAbove(gIODTPlane);
}
void
IOPCIDevice::detachAbove( const IORegistryPlane * plane )
{
super::detachAbove(plane);
if (plane == gIOPowerPlane)
{
IOLockLock(reserved->lock);
reserved->pmActive = false;
if (reserved->pmWait)
IOLockWakeup(reserved->lock, &reserved->pmActive, true);
IOLockUnlock(reserved->lock);
}
}
bool
IOPCIDevice::initReserved(void)
{
if (!reserved)
{
reserved = IONew(IOPCIDeviceExpansionData, 1);
if (!reserved)
return (false);
bzero(reserved, sizeof(IOPCIDeviceExpansionData));
reserved->lock = IOLockAlloc();
}
return (true);
}
bool
IOPCIDevice::init(OSDictionary * propTable)
{
if (!super::init(propTable)) return (false);
return (initReserved());
}
bool IOPCIDevice::init( IORegistryEntry * from, const IORegistryPlane * inPlane )
{
if (!super::init(from, inPlane)) return (false);
return (initReserved());
}
void IOPCIDevice::free()
{
if (savedConfig)
{
if (configShadow(this)->link.next) panic("IOPCIDevice(%p) linked", this);
IODelete(savedConfig, IOPCIConfigShadow, 1);
savedConfig = 0;
}
if (reserved)
{
if (reserved->lock)
IOLockFree(reserved->lock);
IODelete(reserved, IOPCIDeviceExpansionData, 1);
}
super::free();
}
IOReturn IOPCIDevice::powerStateWillChangeTo (IOPMPowerFlags capabilities,
unsigned long stateNumber,
IOService* whatDevice)
{
IOReturn ret;
uint16_t pmcsr;
if (stateNumber == kIOPCIDeviceOffState)
{
if ((kPMEOption & reserved->pmSleepEnabled) && reserved->pmControlStatus && (reserved->sleepControlBits & kPCIPMCSPMEStatus))
{
pmcsr = extendedConfigRead16(reserved->pmControlStatus);
if (pmcsr & kPCIPMCSPMEStatus)
{
DLOG("%s[%p]::powerStateWillChangeTo(OFF) - PMCS has PME set(0x%x) - CLEARING\n", getName(), this, pmcsr);
extendedConfigWrite16(reserved->pmControlStatus, pmcsr);
DLOG("%s[%p]::powerStateWillChangeTo(OFF) - PMCS now is(0x%x)\n", getName(), this, extendedConfigRead16(reserved->pmControlStatus));
}
else
{
DLOG("%s[%p]::powerStateWillChangeTo(OFF) - PMCS has PME clear(0x%x) - not touching\n", getName(), this, pmcsr);
}
}
reserved->updateWakeReason = true;
}
else if ((stateNumber == kIOPCIDeviceOnState) && reserved->pmSleepEnabled && reserved->pmControlStatus && reserved->sleepControlBits)
{
ret = (kIOPCIDeviceOnState == reserved->pciPMState)
? kIOReturnNotReady : checkLink(kCheckLinkForPower);
pmcsr = (kIOReturnSuccess == ret)
? extendedConfigRead16(reserved->pmControlStatus) : reserved->pmLastWakeBits;
updateWakeReason(pmcsr);
DLOG("%s[%p]::powerStateWillChangeTo(ON) - ps %d lnk(0x%x) - PMCS(0x%x, 0x%x)\n", getName(), this, reserved->pciPMState, ret, pmcsr, reserved->pmLastWakeBits);
}
return super::powerStateWillChangeTo(capabilities, stateNumber, whatDevice);
}
IOReturn IOPCIDevice::setPCIPowerState(uint8_t powerState, uint32_t options)
{
uint16_t pmeState;
uint8_t prevState;
uint8_t effectiveState;
if ((powerState == kIOPCIDeviceOffState) && (kIOPCIConfiguratorDeepIdle & gIOPCIFlags))
{
effectiveState = kIOPCIDeviceDozeState;
}
else effectiveState = powerState;
DLOG("%s[%p]::pciSetPowerState(%d->%d,%d)\n", getName(), this, reserved->pciPMState, powerState, effectiveState);
#if ACPI_SUPPORT
IOACPIPlatformDevice * device;
int8_t idx;
IOReturn ret;
uint64_t time;
if ((idx = reserved->psMethods[effectiveState]) >= 0)
{
if ((idx != reserved->lastPSMethod) && !(kMachineRestoreDehibernate & options))
{
IOPCIConfigShadow * shadow = configShadow(this);
if ((effectiveState >= kIOPCIDeviceOnState)
&& !space.s.busNum
&& (shadow)
&& (shadow->bridge)
&& (kIOPCIConfigShadowValid
== ((kIOPCIConfigShadowValid | kIOPCIConfigShadowBridgeDriver) & shadow->flags))
&& (!(0x00FFFFFF & extendedConfigRead32(kPCI2PCIPrimaryBus))))
{
DLOG("%s::restore bus(0x%x)\n", getName(), shadow->configSave.savedConfig[kPCI2PCIPrimaryBus >> 2]);
extendedConfigWrite32(kPCI2PCIPrimaryBus, shadow->configSave.savedConfig[kPCI2PCIPrimaryBus >> 2]);
}
device = (IOACPIPlatformDevice *) reserved->configEntry->acpiDevice;
DLOG("%s::evaluateObject(%s)\n", getName(), gIOPCIPSMethods[idx]->getCStringNoCopy());
time = mach_absolute_time();
ret = device->evaluateObject(gIOPCIPSMethods[idx]);
time = mach_absolute_time() - time;
absolutetime_to_nanoseconds(time, &time);
DLOG("%s::evaluateObject(%s) ret 0x%x %qd ms\n",
getName(), gIOPCIPSMethods[idx]->getCStringNoCopy(), ret, time / 1000000ULL);
if ((effectiveState < kIOPCIDeviceOnState) && shadow) shadow->restoreCount = 0;
}
reserved->lastPSMethod = idx;
}
#endif
if (kMachineRestoreDehibernate & options) return (kIOReturnSuccess);
prevState = reserved->pciPMState;
if (powerState != prevState)
{
reserved->pciPMState = powerState;
if (!isInactive()) switch (powerState)
{
case kIOPCIDeviceOffState:
case kIOPCIDeviceDozeState:
if (reserved->pmSleepEnabled && reserved->pmControlStatus && reserved->sleepControlBits)
{
UInt16 bits = reserved->sleepControlBits;
if (kPMEOption & reserved->pmSleepEnabled)
{
if ((kPMEOptionS3Disable & reserved->pmSleepEnabled) && (kIOPCIDeviceOffState == powerState))
{
bits &= ~kPCIPMCSPMEEnable;
bits |= kPCIPMCSPMEStatus;
prevState = -1U;
}
else
{
bits &= ~kPCIPMCSPMEStatus;
}
}
if (effectiveState != prevState)
{
DLOG("%s[%p]::setPCIPowerState(OFF) - writing 0x%x to PMCS currently (0x%x)\n", getName(), this, bits, extendedConfigRead16(reserved->pmControlStatus));
extendedConfigWrite16(reserved->pmControlStatus, bits);
DLOG("%s[%p]::setPCIPowerState(OFF) - did move PMCS to D3\n", getName(), this);
}
}
break;
case kIOPCIDevicePausedState:
case kIOPCIDeviceOnState:
pmeState = reserved->pmControlStatus ? extendedConfigRead16(reserved->pmControlStatus) : 0;
if (reserved->pmSleepEnabled && reserved->pmControlStatus && reserved->sleepControlBits)
{
if ((pmeState & kPCIPMCSPowerStateMask) != kPCIPMCSPowerStateD0)
{
DLOG("%s[%p]::setPCIPowerState(ON) - moving PMCS from 0x%x to D0\n",
getName(), this, extendedConfigRead16(reserved->pmControlStatus));
extendedConfigWrite16(reserved->pmControlStatus, kPCIPMCSPMEStatus | kPCIPMCSPowerStateD0);
IOSleep(10);
DLOG("%s[%p]::setPCIPowerState(ON) - did move PMCS to 0x%x\n",
getName(), this, extendedConfigRead16(reserved->pmControlStatus));
}
else
{
DLOG("%s[%p]::setPCIPowerState(ON) - PMCS already at D0 (0x%x)\n",
getName(), this, extendedConfigRead16(reserved->pmControlStatus));
extendedConfigWrite16(reserved->pmControlStatus, kPCIPMCSPMEStatus);
}
reserved->pmLastWakeBits = pmeState;
reserved->pmeUpdate = (kIOPCIDeviceOffState == prevState);
}
break;
}
}
if (kIOPCIDeviceOnState == powerState)
{
if (reserved->pmeUpdate && !(kMachineRestoreDehibernate & options))
{
reserved->pmeUpdate = false;
updateWakeReason(reserved->pmLastWakeBits);
}
reserved->updateWakeReason = false;
}
return (kIOReturnSuccess);
}
void IOPCIDevice::updateWakeReason(uint16_t pmeState)
{
OSNumber * num;
if (0xFFFF == pmeState) removeProperty(kIOPCIPMCSStateKey);
else
{
if (reserved->updateWakeReason)
{
uint32_t mask;
if (kPMEOptionWakeReason & reserved->pmSleepEnabled) mask = kPCIPMCSPMEStatus;
else mask = (kPCIPMCSPMEStatus | kPCIPMCSPMEEnable);
if (mask == (mask & pmeState))
{
parent->updateWakeReason(this);
reserved->updateWakeReason = false;
}
}
num = OSNumber::withNumber(pmeState, 16);
if (num)
{
setProperty(kIOPCIPMCSStateKey, num);
num->release();
}
}
}
IOReturn IOPCIDevice::setPowerState( unsigned long newState,
IOService * whatDevice )
{
IOReturn ret;
unsigned long prevState;
if (isInactive())
return (kIOPMAckImplied);
if ((getProperty(kIOPMResetPowerStateOnWakeKey) == kOSBooleanTrue) && (kIOPCIDeviceOffState == newState))
{
changePowerStateTo(kIOPCIDeviceOffState);
changePowerStateToPriv(kIOPCIDeviceOffState);
}
prevState = reserved->pmState;
ret = parent->setDevicePowerState(this, kIOPCIConfigShadowVolatile,
prevState, newState);
reserved->pmState = newState;
#ifdef PROT_DEVICE
if (!strcmp(PROT_DEVICE, getName()))
{
IOMemoryMap * map;
kern_return_t kr;
map = mapDeviceMemoryWithIndex(0);
kr = mach_vm_protect(kernel_map, map->getAddress(), map->getLength(), false,
(kIOPCIDeviceOnState == newState) ? VM_PROT_READ | VM_PROT_WRITE : VM_PROT_NONE);
kprintf("%s mach_vm_protect %d\n", getName(), kr);
map->release();
}
#endif
return (ret);
}
IOReturn IOPCIDevice::saveDeviceState( IOOptionBits options )
{
IOReturn ret;
ret = parent->setDevicePowerState(this, kIOPCIConfigShadowPermanent & options,
kIOPCIDeviceOnState, kIOPCIDeviceDozeState);
return (ret);
}
IOReturn IOPCIDevice::restoreDeviceState( IOOptionBits options )
{
IOReturn ret;
ret = parent->setDevicePowerState(this, 0,
kIOPCIDeviceDozeState, kIOPCIDeviceOnState);
return (ret);
}
bool IOPCIDevice::matchPropertyTable(OSDictionary* table)
{
OSArray* entitlementArray = NULL;
OSDictionary* entitlementDictionary = NULL;
OSString* builtInEntitlement = NULL;
bool builtInEntitled = false;
bool isMatch = false;
SInt32 matchScore = 0;
isMatch = matchPropertyTable(table, &matchScore);
if ( (isMatch == true)
&& (table->getObject(kIOPCITransportDextEntitlement)))
{
isMatch = false;
entitlementArray = OSDynamicCast(OSArray, table->getObject(kIOPCITransportDextEntitlement));
if (entitlementArray != NULL)
{
for (unsigned int i = 0; i < entitlementArray->getCount(); i++)
{
OSObject* entitlement = entitlementArray->getObject(i);
entitlementDictionary = OSDynamicCast(OSDictionary, entitlement);
if (entitlementDictionary != NULL)
{
isMatch = matchPropertyTable(entitlementDictionary, &matchScore);
if (isMatch == true)
{
DLOG("The %s entitlements array has matched at index %d \n", kIOPCITransportDextEntitlement, i);
break;
}
}
else if( ((builtInEntitlement = OSDynamicCast(OSString, entitlement)) != NULL)
&& (builtInEntitlement->isEqualTo(kIODriverKitTransportBuiltinEntitlementKey) == true))
{
builtInEntitled = true;
}
}
if( (getProperty("built-in") != NULL)
&& (builtInEntitled != true))
{
isMatch = false;
}
if (isMatch == false)
{
DLOG("The %s entitlements property doesn't contain a matching entitlement\n", kIOPCITransportDextEntitlement);
}
}
else if (table->getObject(kIOPCITransportDextEntitlement) == kOSBooleanTrue)
{
isMatch = true;
DLOG("The %s entitlement is not a dictionary, this is intended for Apple internal use only\n", kIOPCITransportDextEntitlement);
}
else
{
DLOG("The %s entitlements property is malformed\n", kIOPCITransportDextEntitlement);
}
}
return isMatch;
}
bool IOPCIDevice::matchPropertyTable( OSDictionary * table, SInt32 * score )
{
bool result = false;
IORegistryEntry * regEntry;
IOPCIBridge * bridge;
regEntry = copyParentEntry(gIOServicePlane);
bridge = OSDynamicCast(IOPCIBridge, regEntry);
if (bridge) result = bridge->matchNubWithPropertyTable(this, table, score);
OSSafeReleaseNULL(regEntry);
return (result);
}
bool IOPCIDevice::compareName( OSString * name, OSString ** matched ) const
{
bool result = false;
IORegistryEntry * regEntry;
IOPCIBridge * bridge;
regEntry = copyParentEntry(gIOServicePlane);
bridge = OSDynamicCast(IOPCIBridge, regEntry);
if (bridge) result = bridge->compareNubName(this, name, matched);
OSSafeReleaseNULL(regEntry);
return (result);
}
IOReturn IOPCIDevice::getResources( void )
{
return (parent->getNubResources(this));
}
UInt32 IOPCIDevice::configRead32( IOPCIAddressSpace _space,
UInt8 offset )
{
return (parent->configRead32(_space, offset));
}
void IOPCIDevice::configWrite32( IOPCIAddressSpace _space,
UInt8 offset, UInt32 data )
{
parent->configWrite32( _space, offset, data );
}
UInt16 IOPCIDevice::configRead16( IOPCIAddressSpace _space,
UInt8 offset )
{
return (parent->configRead16(_space, offset));
}
void IOPCIDevice::configWrite16( IOPCIAddressSpace _space,
UInt8 offset, UInt16 data )
{
parent->configWrite16( _space, offset, data );
}
UInt8 IOPCIDevice::configRead8( IOPCIAddressSpace _space,
UInt8 offset )
{
return (parent->configRead8(_space, offset));
}
void IOPCIDevice::configWrite8( IOPCIAddressSpace _space,
UInt8 offset, UInt8 data )
{
parent->configWrite8( _space, offset, data );
}
bool IOPCIDevice::configAccess(bool write)
{
bool ok = (!isInactive()
&& reserved
&& (0 == ((write ? VM_PROT_WRITE : VM_PROT_READ) & reserved->configProt)));
if (!ok && !ml_at_interrupt_context())
{
OSReportWithBacktrace("config protect fail(2) for device %u:%u:%u\n",
PCI_ADDRESS_TUPLE(this));
}
return (ok);
}
#if APPLE_KEXT_VTABLE_PADDING
UInt32 IOPCIDevice::configRead32( UInt8 offset )
{
if (!configAccess(false)) return (0xFFFFFFFF);
return (parent->configRead32(space, offset));
}
void IOPCIDevice::configWrite32( UInt8 offset, UInt32 data )
{
if (!configAccess(true)) return;
parent->configWrite32( space, offset, data );
}
UInt16 IOPCIDevice::configRead16( UInt8 offset )
{
if (!configAccess(false)) return (0xFFFF);
return (parent->configRead16(space, offset));
}
void IOPCIDevice::configWrite16( UInt8 offset, UInt16 data )
{
if (!configAccess(true)) return;
parent->configWrite16( space, offset, data );
}
UInt8 IOPCIDevice::configRead8( UInt8 offset )
{
if (!configAccess(false)) return (0xFF);
return (parent->configRead8(space, offset));
}
void IOPCIDevice::configWrite8( UInt8 offset, UInt8 data )
{
if (!configAccess(true)) return;
parent->configWrite8( space, offset, data );
}
#endif
UInt32 IOPCIDevice::extendedConfigRead32( IOByteCount offset )
{
if (!configAccess(false)) return (0xFFFFFFFF);
IOPCIAddressSpace _space = space;
_space.es.registerNumExtended = ((offset >> 8) & 0xF);
return (configRead32(_space, offset));
}
void IOPCIDevice::extendedConfigWrite32( IOByteCount offset, UInt32 data )
{
if (!configAccess(true)) return;
IOPCIAddressSpace _space = space;
_space.es.registerNumExtended = ((offset >> 8) & 0xF);
configWrite32(_space, offset, data);
}
UInt16 IOPCIDevice::extendedConfigRead16( IOByteCount offset )
{
if (!configAccess(false)) return (0xFFFF);
IOPCIAddressSpace _space = space;
_space.es.registerNumExtended = ((offset >> 8) & 0xF);
return (configRead16(_space, offset));
}
void IOPCIDevice::extendedConfigWrite16( IOByteCount offset, UInt16 data )
{
if (!configAccess(true)) return;
IOPCIAddressSpace _space = space;
_space.es.registerNumExtended = ((offset >> 8) & 0xF);
configWrite16(_space, offset, data);
}
UInt8 IOPCIDevice::extendedConfigRead8( IOByteCount offset )
{
if (!configAccess(false)) return (0xFF);
IOPCIAddressSpace _space = space;
_space.es.registerNumExtended = ((offset >> 8) & 0xF);
return (configRead8(_space, offset));
}
void IOPCIDevice::extendedConfigWrite8( IOByteCount offset, UInt8 data )
{
if (!configAccess(true)) return;
IOPCIAddressSpace _space = space;
_space.es.registerNumExtended = ((offset >> 8) & 0xF);
configWrite8(_space, offset, data);
}
UInt32 IOPCIDevice::findPCICapability( UInt8 capabilityID, UInt8 * offset )
{
return (parent->findPCICapability(space, capabilityID, offset));
}
UInt32 IOPCIDevice::extendedFindPCICapability( UInt32 capabilityID, IOByteCount * offset )
{
return (parent->extendedFindPCICapability(reserved->configEntry, capabilityID, offset));
}
UInt32 IOPCIDevice::setConfigBits( UInt8 reg, UInt32 mask, UInt32 value )
{
UInt32 was;
UInt32 bits;
bits = extendedConfigRead32( reg );
was = (bits & mask);
bits &= ~mask;
bits |= (value & mask);
extendedConfigWrite32( reg, bits );
return (was);
}
bool IOPCIDevice::setBusMasterEnable( bool enable )
{
return (0 != setConfigBits(kIOPCIConfigCommand, kIOPCICommandBusMaster,
enable ? kIOPCICommandBusMaster : 0));
}
bool IOPCIDevice::setMemoryEnable( bool enable )
{
return (0 != setConfigBits(kIOPCIConfigCommand, kIOPCICommandMemorySpace,
enable ? kIOPCICommandMemorySpace : 0));
}
bool IOPCIDevice::setIOEnable( bool enable, bool )
{
return (0 != setConfigBits(kIOPCIConfigCommand, kIOPCICommandIOSpace,
enable ? kIOPCICommandIOSpace : 0));
}
UInt8 IOPCIDevice::getBusNumber( void )
{
return (space.s.busNum);
}
UInt8 IOPCIDevice::getDeviceNumber( void )
{
return (space.s.deviceNum);
}
UInt8 IOPCIDevice::getFunctionNumber( void )
{
return (space.s.functionNum);
}
IODeviceMemory * IOPCIDevice::getDeviceMemoryWithIndex(unsigned int index)
{
if (kTunnelL1NotSet == reserved->tunnelL1Allow) setTunnelL1Enable(this, false);
return (super::getDeviceMemoryWithIndex(index));
}
IODeviceMemory * IOPCIDevice::getDeviceMemoryWithRegister( UInt8 reg )
{
OSArray * array;
IODeviceMemory * range;
unsigned int i = 0;
if (kTunnelL1NotSet == reserved->tunnelL1Allow) setTunnelL1Enable(this, false);
array = (OSArray *) getProperty( gIODeviceMemoryKey);
if (0 == array)
return (0);
while ((range = (IODeviceMemory *) array->getObject(i++)))
{
if (reg == (range->getTag() & 0xff))
break;
}
return (range);
}
IOMemoryMap * IOPCIDevice:: mapDeviceMemoryWithRegister( UInt8 reg,
IOOptionBits options )
{
IODeviceMemory * range;
IOMemoryMap * map;
range = getDeviceMemoryWithRegister( reg );
if (range)
map = range->map( options );
else
map = 0;
return (map);
}
IODeviceMemory * IOPCIDevice::ioDeviceMemory( void )
{
return (parent->ioDeviceMemory());
}
IOService * IOPCIDevice::matchLocation( IOService * )
{
return (this);
}
OSMetaClassDefineReservedUsed(IOPCIDevice, 0);
bool IOPCIDevice::hasPCIPowerManagement(IOOptionBits state)
{
UInt16 pciPMCapReg, checkMask;
OSData *aString;
reserved->sleepControlBits = 0; if (!reserved->pmControlStatus) return (false);
pciPMCapReg = extendedConfigRead16(reserved->pmControlStatus - sizeof(uint16_t));
if (state)
{
checkMask = state;
switch (state)
{
case kPCIPMCPMESupportFromD3Cold:
case kPCIPMCPMESupportFromD3Hot:
reserved->sleepControlBits = (kPCIPMCSPMEStatus | kPCIPMCSPMEEnable | kPCIPMCSPowerStateD3);
break;
case kPCIPMCPMESupportFromD2:
reserved->sleepControlBits = (kPCIPMCSPMEStatus | kPCIPMCSPMEEnable | kPCIPMCSPowerStateD2);
break;
case kPCIPMCPMESupportFromD1:
reserved->sleepControlBits = (kPCIPMCSPMEStatus | kPCIPMCSPMEEnable | kPCIPMCSPowerStateD1);
break;
case kPCIPMCD2Support:
reserved->sleepControlBits = kPCIPMCSPowerStateD2;
break;
case kPCIPMCD1Support:
reserved->sleepControlBits = kPCIPMCSPowerStateD1;
break;
case kPCIPMCD3Support:
reserved->sleepControlBits = kPCIPMCSPowerStateD3;
checkMask = 0;
break;
default:
break;
}
if (checkMask && !(checkMask & pciPMCapReg))
reserved->sleepControlBits = 0;
}
else
{
if ((aString = OSDynamicCast(OSData, getProperty("sleep-power-state"))))
{
DLOG("%s[%p]::hasPCIPwrMgmt found sleep-power-state string %p\n", getName(), this, aString);
if (aString->isEqualTo("D3cold", static_cast<unsigned int>(strlen("D3cold"))))
reserved->sleepControlBits = (kPCIPMCSPMEStatus | kPCIPMCSPMEEnable | kPCIPMCSPowerStateD3);
else if (aString->isEqualTo("D3Hot", static_cast<unsigned int>(strlen("D3Hot"))))
reserved->sleepControlBits = (kPCIPMCSPMEStatus | kPCIPMCSPMEEnable | kPCIPMCSPowerStateD3);
}
}
return (reserved->sleepControlBits ? true : false);
}
OSMetaClassDefineReservedUsed(IOPCIDevice, 1);
IOReturn IOPCIDevice::enablePCIPowerManagement(IOOptionBits state)
{
IOReturn ret = kIOReturnSuccess;
if (0xffffffff == state) state = kPCIPMCSPowerStateD3;
if (!reserved->pmControlStatus)
{
ret = kIOReturnBadArgument;
return ret;
}
if ( state == kPCIPMCSPowerStateD0 )
{
reserved->sleepControlBits = 0;
reserved->pmSleepEnabled = false;
return ret;
}
else
{
UInt32 oldBits = reserved->sleepControlBits;
reserved->sleepControlBits = state & kPCIPMCSPowerStateMask;
if ( oldBits & kPCIPMCSPMEStatus )
reserved->sleepControlBits |= kPCIPMCSPMEStatus;
if ( oldBits & kPCIPMCSPMEEnable )
reserved->sleepControlBits |= kPCIPMCSPMEEnable;
if (!reserved->sleepControlBits)
{
DLOG("%s[%p] - enablePCIPwrMgmt - no sleep control bits - not enabling\n", getName(), this);
ret = kIOReturnBadArgument;
}
else
{
DLOG("%s[%p] - enablePCIPwrMgmt, enabling\n", getName(), this);
reserved->pmSleepEnabled = kPMEnable | kPMEOption;
if (kPCIPMCSPMEDisableInS3 & state) reserved->pmSleepEnabled |= kPMEOptionS3Disable;
if (kPCIPMCSPMEWakeReason & state) reserved->pmSleepEnabled |= kPMEOptionWakeReason;
}
}
return ret;
}
IOReturn
IOPCIDevice::callPlatformFunction(const OSSymbol * functionName,
bool waitForFunction,
void * p1, void * p2,
void * p3, void * p4)
{
IOReturn result;
result = super::callPlatformFunction(functionName, waitForFunction,
p1, p2, p3, p4);
if ((kIOReturnUnsupported == result)
&& (gIOPlatformDeviceASPMEnableKey == functionName)
&& getProperty(kIOPCIDeviceASPMSupportedKey))
{
IOOptionBits state = (p2 != 0) ? reserved->expressASPMDefault : 0;
result = parent->setDeviceASPMState(this, (IOService *) p1, state);
}
return (result);
}
IOReturn
IOPCIDevice::callPlatformFunction(const char * functionName,
bool waitForFunction,
void * p1, void * p2,
void * p3, void * p4)
{
return (super::callPlatformFunction(functionName, waitForFunction,
p1, p2, p3, p4));
}
IOReturn IOPCIDevice::enableLTR(IOPCIDevice * device, bool enable)
{
uint32_t reg;
if (!reserved->expressCapability || !expressV2(this)) return (kIOReturnUnsupported);
reg = extendedConfigRead32(reserved->expressCapability + 0x24);
if (!((1 << 11) & reg)) return (kIOReturnUnsupported);
reg = extendedConfigRead32(reserved->expressCapability + 0x28);
reg &= ~(1 << 10);
if (enable) reg |= ( 1 << 10);
extendedConfigWrite32(reserved->expressCapability + 0x28, reg);
return (kIOReturnSuccess);
}
IOReturn
IOPCIDevice::setLatencyTolerance(IOOptionBits type, uint64_t nanoseconds)
{
static const uint32_t ltrScales[] = { 1, 32, 1024, 32768, 1048576, 33554432 };
OSData * data;
uint64_t ltrScale, ltrValue;
uint32_t idx;
uint32_t reg1;
uint8_t reg2;
uint8_t reg3;
if (!reserved->ltrDevice)
{
IOPCIDevice * next;
next = this;
reserved->ltrDevice = (IOPCIDevice *) 1L;
while (next)
{
if ((data = OSDynamicCast(OSData, next->getProperty("reg-ltrovr"))))
{
reserved->ltrDevice = next;
uint64_t off = *((uint64_t *)data->getBytesNoCopy());
reserved->ltrOffset = off;
reserved->ltrReg1 = reserved->ltrDevice->extendedConfigRead32(reserved->ltrOffset);
reserved->ltrReg2 = reserved->ltrDevice->extendedConfigRead8(reserved->ltrOffset + 4);
break;
}
next = OSDynamicCast(IOPCIDevice, next->getParentEntry(gIODTPlane));
}
}
if (((uintptr_t) reserved->ltrDevice) <= 1) return (kIOReturnUnsupported);
for (ltrValue = idx = 0; idx < arrayCount(ltrScales); idx++)
{
ltrScale = ltrScales[idx];
ltrValue = (nanoseconds / ltrScale);
if (ltrValue < (1<<10)) break;
}
if (idx >= arrayCount(ltrScales)) return (kIOReturnMessageTooLarge);
reg1 = reserved->ltrReg1;
reg2 = reserved->ltrReg2;
reg3 = reg2;
if (kIOPCILatencySnooped & type)
{
reg1 &= 0x0000FFFF;
reg1 |= (1 << 31) | (idx << 26) | (ltrValue << 16);
reg2 &= ~(1 << 0);
reg3 |= (1 << 3) | (1 << 0);
}
if (kIOPCILatencyUnsnooped & type)
{
reg1 &= 0xFFFF0000;
reg1 |= (1 << 15) | (idx << 10) | (ltrValue << 0);
reg2 &= ~(1 << 1);
reg3 |= (1 << 3) | (1 << 1);
}
reserved->ltrDevice->extendedConfigWrite32(reserved->ltrOffset, reg1);
reserved->ltrDevice->extendedConfigWrite8(reserved->ltrOffset + 4, reg2);
reserved->ltrDevice->extendedConfigWrite8(reserved->ltrOffset + 4, reg3);
reserved->ltrReg1 = reg1;
reserved->ltrReg2 = reg3;
#if 0
DLOG("%s: ltr 0x%x = 0x%x, 0x%x\n",
reserved->ltrDevice->getName(),
(int)reserved->ltrOffset,
reserved->ltrDevice->extendedConfigRead32(reserved->ltrOffset),
reserved->ltrDevice->extendedConfigRead8(reserved->ltrOffset + 4));
#endif
return (kIOReturnSuccess);
}
IOReturn IOPCIDevice::enableACS(IOPCIDevice * device, bool enable)
{
uint16_t reg = 0;
if (!reserved->acsCapability) return (kIOReturnUnsupported);
if (enable) {
reg = extendedConfigRead16(reserved->acsCapability + 0x4);
reg &= kIOPCIExpressACSDefault;
}
extendedConfigWrite16(reserved->acsCapability + 0x6, reg);
return (kIOReturnSuccess);
}
IOReturn
IOPCIDevice::setASPMState(IOService * client, IOOptionBits state)
{
IOPCI2PCIBridge * pcib;
if (!(pcib = OSDynamicCast(IOPCI2PCIBridge, parent))) return (kIOReturnUnsupported);
return (pcib->setDeviceASPMState(this, client, state));
}
IOReturn
IOPCIDevice::setTunnelL1Enable(IOService * client, bool l1Enable)
{
IOPCI2PCIBridge * pcib;
if (!(pcib = OSDynamicCast(IOPCI2PCIBridge, parent))) return (kIOReturnUnsupported);
return (pcib->setTunnelL1Enable(this, client, l1Enable));
}
IOPCIEventSource *
IOPCIDevice::createEventSource(OSObject * owner, IOPCIEventSource::Action action, uint32_t options)
{
return (parent->createEventSource(this, owner, action, options));
}
IOReturn
IOPCIDevice::setProperties(OSObject * properties)
{
IOReturn ret = kIOReturnUnsupported;
OSDictionary * dict;
IOService * ejectable;
IOService * topEjectable = NULL;
dict = OSDynamicCast(OSDictionary, properties);
if (dict)
{
if (kOSBooleanFalse == dict->getObject(kIOPCIOnlineKey))
{
ejectable = this;
do
{
if (ejectable->getProperty(kIOPCIEjectableKey))
{
ejectable->setProperty(kIOPCIOnlineKey, kOSBooleanFalse);
topEjectable = ejectable;
}
ejectable = ejectable->getProvider();
}
while (ejectable);
if (topEjectable)
{
ret = topEjectable->requestProbe(kIOPCIProbeOptionEject | kIOPCIProbeOptionDone);
}
return (ret);
}
}
return (super::setProperties(properties));
}
IOReturn IOPCIDevice::requestProbe(IOOptionBits options)
{
if (kIOReturnSuccess != IOUserClient::clientHasPrivilege(current_task(),
kIOClientPrivilegeLocalUser))
{
IOLog("IOPCIDevice requestProbe failed insufficient privileges\n");
return (kIOReturnNotPrivileged);
}
return (kernelRequestProbe(options));
}
IOReturn IOPCIDevice::kernelRequestProbe(uint32_t options)
{
return (parent->kernelRequestProbe(this, options));
}
IOReturn IOPCIDevice::protectDevice(uint32_t space, uint32_t prot)
{
if (space != kIOPCIConfigSpace)
return (kIOReturnUnsupported);
reserved->configProt = prot;
return (parent->protectDevice(this, space, prot));
}
IOReturn IOPCIDevice::checkLink(uint32_t options)
{
return (parent->checkLink(options));
}
IOReturn IOPCIDevice::relocate(uint32_t options)
{
return (parent->relocate(this, options));
}
IOReturn
IOPCIDevice::setConfigHandler(IOPCIDeviceConfigHandler handler, void * ref,
IOPCIDeviceConfigHandler * currentHandler, void ** currentRef)
{
if (!configShadow(this))
return (kIOReturnError);
if (currentHandler)
*currentHandler = configShadow(this)->handler;
if (currentRef)
*currentRef = configShadow(this)->handlerRef;
configShadow(this)->handler = handler;
configShadow(this)->handlerRef = ref;
return (kIOReturnSuccess);
}
IOReturn IOPCIDevice::newUserClient(task_t owningTask, void * securityID,
UInt32 type, OSDictionary * properties,
IOUserClient ** handler)
{
return (kIOReturnUnsupported);
}
bool IOPCIDevice::handleOpen(IOService * forClient, IOOptionBits options, void * arg)
{
bool result = super::handleOpen(forClient, options, arg);
if (result == true)
{
reserved->sessionOptions = options;
if((options & kIOPCISessionOptionDriverkit) != 0)
{
OSObject* offloadEngineDisableEntitlement = IOUserClient::copyClientEntitlement(current_task(), kIOPCITransportDextEntitlementOffloadEngineDisable);
if(offloadEngineDisableEntitlement != NULL)
{
reserved->offloadEngineMMIODisable = 1;
}
OSSafeReleaseNULL(offloadEngineDisableEntitlement);
}
}
return result;
}
void IOPCIDevice::handleClose(IOService * forClient, IOOptionBits options)
{
if ( (isOpen(forClient) == true)
&& ((reserved->sessionOptions & kIOPCISessionOptionDriverkit) != 0))
{
reserved->offloadEngineMMIODisable = 0;
uint16_t command = extendedConfigRead16(kIOPCIConfigurationOffsetCommand);
if ((command & (kIOPCICommandBusMaster | kIOPCICommandMemorySpace)) != 0)
{
DLOG("IOPCIDevice::handleClose: disabling memory and bus mastering for client %s\n", (forClient) ? forClient->getName() : "unknown");
extendedConfigWrite16(kIOPCIConfigurationOffsetCommand, command & ~(kIOPCICommandBusMaster | kIOPCICommandMemorySpace));
}
}
super::handleClose(forClient, options);
}
void IOPCIDevice::copyAERErrorDescriptionForBit(bool uncorrectable, uint32_t bit, char * string, size_t maxLength)
{
const char * desc;
if (uncorrectable)
{
switch (bit)
{
case kIOPCIUncorrectableErrorBitDataLinkProtocol:
desc = "DataLinkProtocol";
break;
case kIOPCIUncorrectableErrorBitSurpriseDown:
desc = "SurpriseDown";
break;
case kIOPCIUncorrectableErrorBitPoisonedTLP:
desc = "PoisonedTLP";
break;
case kIOPCIUncorrectableErrorBitFlowControlProtocol:
desc = "FlowControlProtocol";
break;
case kIOPCIUncorrectableErrorBitCompletionTimeout:
desc = "CompletionTimeout";
break;
case kIOPCIUncorrectableErrorBitCompleterAbort:
desc = "CompleterAbort";
break;
case kIOPCIUncorrectableErrorBitUnexpectedCompletion:
desc = "UnexpectedCompletion";
break;
case kIOPCIUncorrectableErrorBitReceiverOverflow:
desc = "ReceiverOverflow";
break;
case kIOPCIUncorrectableErrorBitMalformedTLP:
desc = "MalformedTLP";
break;
case kIOPCIUncorrectableErrorBitECRC:
desc = "ECRC";
break;
case kIOPCIUncorrectableErrorBitUnsupportedRequest:
desc = "UnsupportedRequest";
break;
case kIOPCIUncorrectableErrorBitACSViolation:
desc = "ACSViolation";
break;
case kIOPCIUncorrectableErrorBitInternal:
desc = "Internal";
break;
case kIOPCIUncorrectableErrorBitMCBlockedTLP:
desc = "MCBlockedTLP";
break;
case kIOPCIUncorrectableErrorBitAtomicOpEgressBlocked:
desc = "AtomicOpEgressBlocked";
break;
case kIOPCIUncorrectableErrorBitTLPPrefixBlocked:
desc = "TLPPrefixBlocked";
break;
default:
desc = NULL;
break;
}
snprintf(string, maxLength, "IOPCIUncorrectableError(%d%s%s)", bit, desc ? ", " : "", desc ? desc : "");
}
else
{
switch (bit)
{
case kIOPCICorrectableErrorBitReceiver:
desc = "Receiver";
break;
case kIOPCICorrectableErrorBitBadTLP:
desc = "BadTLP";
break;
case kIOPCICorrectableErrorBitBadDLLP:
desc = "BadDLLP";
break;
case kIOPCICorrectableErrorBitReplayNumRollover:
desc = "ReplayNumRollover";
break;
case kIOPCICorrectableErrorBitReplayTimerTimeout:
desc = "ReplayTimerTimeout";
break;
case kIOPCICorrectableErrorBitAdvisoryNonFatal:
desc = "AdvisoryNonFatal";
break;
case kIOPCICorrectableErrorBitCorrectedInternal:
desc = "Internal";
break;
case kIOPCICorrectableErrorBitHeaderLogOverflow:
desc = "HeaderLogOverflow";
break;
default:
desc = NULL;
break;
}
snprintf(string, maxLength, "IOPCICorrectableError(%d%s%s)", bit, desc ? ", " : "", desc ? desc : "");
}
}
IOReturn IOPCIDevice::deviceMemoryRead64(uint8_t memoryIndex,
uint64_t offset,
uint64_t* readData)
{
IOReturn result = kIOReturnUnsupported;
#if TARGET_CPU_ARM || TARGET_CPU_ARM64
if( (reserved->offloadEngineMMIODisable == 0)
&& (ml_get_interrupts_enabled() == true)
&& (ml_at_interrupt_context() == false))
{
IODeviceMemory* deviceMemoryDescriptor = reserved->deviceMemory[memoryIndex];
if(deviceMemoryDescriptor == NULL)
{
DLOG("IOPCIDevice::deviceMemoryRead64: failed to get memory for index %u\n", memoryIndex);
return kIOReturnBadArgument;
}
IOPCIAddressSpace addressSpace;
addressSpace.bits = static_cast<uint32_t>(deviceMemoryDescriptor->getTag());
if( (addressSpace.s.space != kIOPCI32BitMemorySpace)
&& (addressSpace.s.space != kIOPCI64BitMemorySpace))
{
DLOG("IOPCIDevice::deviceMemoryRead64: index %u is not MMIO space\n", memoryIndex);
return kIOReturnBadArgument;
}
result = reserved->configEntry->hostBridge->deviceMemoryRead(deviceMemoryDescriptor,
offset,
readData,
sizeof(uint64_t));
}
#endif
if(result == kIOReturnUnsupported)
{
IOMemoryMap* deviceMemoryMap = reserved->deviceMemoryMap[memoryIndex];
if(deviceMemoryMap != NULL)
{
*readData = ml_io_read64(deviceMemoryMap->getVirtualAddress() + offset);
result = kIOReturnSuccess;
}
else
{
DLOG("IOPCIDevice::deviceMemoryRead64: index %u could not get mapping\n", memoryIndex);
return kIOReturnNoMemory;
}
}
return result;
}
IOReturn IOPCIDevice::deviceMemoryRead32(uint8_t memoryIndex,
uint64_t offset,
uint32_t* readData)
{
IOReturn result = kIOReturnUnsupported;
#if TARGET_CPU_ARM || TARGET_CPU_ARM64
if( (reserved->offloadEngineMMIODisable == 0)
&& (ml_get_interrupts_enabled() == true)
&& (ml_at_interrupt_context() == false))
{
IODeviceMemory* deviceMemoryDescriptor = reserved->deviceMemory[memoryIndex];
if(deviceMemoryDescriptor == NULL)
{
DLOG("IOPCIDevice::deviceMemoryRead32: failed to get memory for index %u\n", memoryIndex);
return kIOReturnBadArgument;
}
IOPCIAddressSpace addressSpace;
addressSpace.bits = static_cast<uint32_t>(deviceMemoryDescriptor->getTag());
if( (addressSpace.s.space != kIOPCI32BitMemorySpace)
&& (addressSpace.s.space != kIOPCI64BitMemorySpace))
{
DLOG("IOPCIDevice::deviceMemoryRead32: index %u is not MMIO space\n", memoryIndex);
return kIOReturnBadArgument;
}
result = reserved->configEntry->hostBridge->deviceMemoryRead(deviceMemoryDescriptor,
offset,
readData,
sizeof(uint32_t));
}
#endif
if(result == kIOReturnUnsupported)
{
IOMemoryMap* deviceMemoryMap = reserved->deviceMemoryMap[memoryIndex];
if(deviceMemoryMap != NULL)
{
*readData = ml_io_read32(deviceMemoryMap->getVirtualAddress() + offset);
result = kIOReturnSuccess;
}
else
{
DLOG("IOPCIDevice::deviceMemoryRead32: index %u could not get mapping\n", memoryIndex);
return kIOReturnNoMemory;
}
}
return result;
}
IOReturn IOPCIDevice::deviceMemoryRead16(uint8_t memoryIndex,
uint64_t offset,
uint16_t* readData)
{
IOReturn result = kIOReturnUnsupported;
#if TARGET_CPU_ARM || TARGET_CPU_ARM64
if( (reserved->offloadEngineMMIODisable == 0)
&& (ml_get_interrupts_enabled() == true)
&& (ml_at_interrupt_context() == false))
{
IODeviceMemory* deviceMemoryDescriptor = reserved->deviceMemory[memoryIndex];
if(deviceMemoryDescriptor == NULL)
{
DLOG("IOPCIDevice::deviceMemoryRead16: failed to get memory for index %u\n", memoryIndex);
return kIOReturnBadArgument;
}
IOPCIAddressSpace addressSpace;
addressSpace.bits = static_cast<uint32_t>(deviceMemoryDescriptor->getTag());
if( (addressSpace.s.space != kIOPCI32BitMemorySpace)
&& (addressSpace.s.space != kIOPCI64BitMemorySpace))
{
DLOG("IOPCIDevice::deviceMemoryRead16: index %u is not MMIO space\n", memoryIndex);
return kIOReturnBadArgument;
}
result = reserved->configEntry->hostBridge->deviceMemoryRead(deviceMemoryDescriptor,
offset,
readData,
sizeof(uint16_t));
}
#endif
if(result == kIOReturnUnsupported)
{
IOMemoryMap* deviceMemoryMap = reserved->deviceMemoryMap[memoryIndex];
if(deviceMemoryMap != NULL)
{
*readData = ml_io_read16(deviceMemoryMap->getVirtualAddress() + offset);
result = kIOReturnSuccess;
}
else
{
DLOG("IOPCIDevice::deviceMemoryRead16: index %u could not get mapping\n", memoryIndex);
return kIOReturnNoMemory;
}
}
return result;
}
IOReturn IOPCIDevice::deviceMemoryRead8(uint8_t memoryIndex,
uint64_t offset,
uint8_t* readData)
{
IOReturn result = kIOReturnUnsupported;
#if TARGET_CPU_ARM || TARGET_CPU_ARM64
if( (reserved->offloadEngineMMIODisable == 0)
&& (ml_get_interrupts_enabled() == true)
&& (ml_at_interrupt_context() == false))
{
IODeviceMemory* deviceMemoryDescriptor = reserved->deviceMemory[memoryIndex];
if(deviceMemoryDescriptor == NULL)
{
DLOG("IOPCIDevice::deviceMemoryRead8: failed to get memory for index %u\n", memoryIndex);
return kIOReturnBadArgument;
}
IOPCIAddressSpace addressSpace;
addressSpace.bits = static_cast<uint32_t>(deviceMemoryDescriptor->getTag());
if( (addressSpace.s.space != kIOPCI32BitMemorySpace)
&& (addressSpace.s.space != kIOPCI64BitMemorySpace))
{
DLOG("IOPCIDevice::deviceMemoryRead8: index %u is not MMIO space\n", memoryIndex);
return kIOReturnBadArgument;
}
result = reserved->configEntry->hostBridge->deviceMemoryRead(deviceMemoryDescriptor,
offset,
readData,
sizeof(uint8_t));
}
#endif
if(result == kIOReturnUnsupported)
{
IOMemoryMap* deviceMemoryMap = reserved->deviceMemoryMap[memoryIndex];
if(deviceMemoryMap != NULL)
{
*readData = ml_io_read8(deviceMemoryMap->getVirtualAddress() + offset);
result = kIOReturnSuccess;
}
else
{
DLOG("IOPCIDevice::deviceMemoryRead8: index %u could not get mapping\n", memoryIndex);
return kIOReturnNoMemory;
}
}
return result;
}
IOReturn IOPCIDevice::deviceMemoryWrite64(uint8_t memoryIndex,
uint64_t offset,
uint64_t data)
{
IOReturn result = kIOReturnUnsupported;
IOMemoryMap* deviceMemoryMap = reserved->deviceMemoryMap[memoryIndex];
if(deviceMemoryMap != NULL)
{
ml_io_write(deviceMemoryMap->getVirtualAddress() + offset, data, sizeof(uint64_t));
result = kIOReturnSuccess;
}
else
{
DLOG("IOPCIDevice::deviceMemoryRead64: index %u could not get mapping\n", memoryIndex);
return kIOReturnNoMemory;
}
return result;
}
IOReturn IOPCIDevice::deviceMemoryWrite32(uint8_t memoryIndex,
uint64_t offset,
uint32_t data)
{
IOReturn result = kIOReturnUnsupported;
IOMemoryMap* deviceMemoryMap = reserved->deviceMemoryMap[memoryIndex];
if(deviceMemoryMap != NULL)
{
ml_io_write(deviceMemoryMap->getVirtualAddress() + offset, data, sizeof(uint32_t));
result = kIOReturnSuccess;
}
else
{
DLOG("IOPCIDevice::deviceMemoryRead32: index %u could not get mapping\n", memoryIndex);
return kIOReturnNoMemory;
}
return result;
}
IOReturn IOPCIDevice::deviceMemoryWrite16(uint8_t memoryIndex,
uint64_t offset,
uint16_t data)
{
IOReturn result = kIOReturnUnsupported;
IOMemoryMap* deviceMemoryMap = reserved->deviceMemoryMap[memoryIndex];
if(deviceMemoryMap != NULL)
{
ml_io_write(deviceMemoryMap->getVirtualAddress() + offset, data, sizeof(uint16_t));
result = kIOReturnSuccess;
}
else
{
DLOG("IOPCIDevice::deviceMemoryRead16: index %u could not get mapping\n", memoryIndex);
return kIOReturnNoMemory;
}
return result;
}
IOReturn IOPCIDevice::deviceMemoryWrite8(uint8_t memoryIndex,
uint64_t offset,
uint8_t data)
{
IOReturn result = kIOReturnUnsupported;
IOMemoryMap* deviceMemoryMap = reserved->deviceMemoryMap[memoryIndex];
if(deviceMemoryMap != NULL)
{
ml_io_write(deviceMemoryMap->getVirtualAddress() + offset, data, sizeof(uint8_t));
result = kIOReturnSuccess;
}
else
{
DLOG("IOPCIDevice::deviceMemoryRead8: index %u could not get mapping\n", memoryIndex);
return kIOReturnNoMemory;
}
return result;
}
#if TARGET_OS_OSX
#pragma mark Public DriverKit Methods
kern_return_t IOPCIDevice::SetProperties_Impl(OSDictionary* properties)
{
kern_return_t result = kIOReturnBadArgument;
if(properties == NULL)
{
return result;
}
OSObject* dictionaryValue = properties->getObject(kIOPMPCIConfigSpaceVolatileKey);
if( (dictionaryValue == kOSBooleanTrue)
|| (dictionaryValue == kOSBooleanFalse))
{
DLOG("%s setting property %s to device %u:%u:%u\n", __PRETTY_FUNCTION__, kIOPMPCIConfigSpaceVolatileKey, PCI_ADDRESS_TUPLE(this));
result = kIOReturnSuccess;
setProperty(kIOPMPCIConfigSpaceVolatileKey, dictionaryValue);
}
dictionaryValue = properties->getObject(kIOPMPCISleepLinkDisableKey);
if( (dictionaryValue == kOSBooleanTrue)
|| (dictionaryValue == kOSBooleanFalse))
{
DLOG("%s setting property %s to device %u:%u:%u\n", __PRETTY_FUNCTION__, kIOPMPCISleepLinkDisableKey, PCI_ADDRESS_TUPLE(this));
result = kIOReturnSuccess;
setProperty(kIOPMPCISleepLinkDisableKey, dictionaryValue);
}
dictionaryValue = properties->getObject(kIOPMPCISleepResetKey);
if( (dictionaryValue == kOSBooleanTrue)
|| (dictionaryValue == kOSBooleanFalse))
{
DLOG("%s setting property %s to device %u:%u:%u\n", __PRETTY_FUNCTION__, kIOPMPCISleepResetKey, PCI_ADDRESS_TUPLE(this));
result = kIOReturnSuccess;
setProperty(kIOPMPCISleepResetKey, dictionaryValue);
}
return result;
}
#pragma mark Private DriverKit Methods
kern_return_t
IMPL(IOPCIDevice, _ManageSession)
{
IOReturn result = kIOReturnNotOpen;
DLOG("IOPCIDevice::%s: for client %s\n", __FUNCTION__, (forClient) ? forClient->getName() : "unknown");
if(openClient == true)
{
if(open(forClient, (openOptions & (~kIOServiceFamilyOpenOptions)) | kIOPCISessionOptionDriverkit, NULL) == true)
{
result = kIOReturnSuccess;
}
}
else
{
close(forClient);
result = kIOReturnSuccess;
}
return result;
}
kern_return_t IOPCIDevice::ClientCrashed_Impl(IOService *client, uint64_t options)
{
uint16_t command = extendedConfigRead16(kIOPCIConfigurationOffsetCommand);
if ((command & (kIOPCICommandBusMaster | kIOPCICommandMemorySpace)) != 0)
{
DLOG("IOPCIDevice::ClientCrashed_Impl disabling memory and bus mastering for client %s\n", (client) ? client->getName() : "unknown");
extendedConfigWrite16(kIOPCIConfigurationOffsetCommand, command & ~(kIOPCICommandBusMaster | kIOPCICommandMemorySpace));
}
if(isOpen(client) == true)
{
IOLog("%s: PCIDriverKit client, %s, crashed for device %s[%u:%u:%u], attempting to recover\n",
__PRETTY_FUNCTION__,
(client != NULL) ? client->getName() : "unknown",
getName(),
PCI_ADDRESS_TUPLE(this));
thread_call_t threadCall = thread_call_allocate(OSMemberFunctionCast(thread_call_func_t,
this,
&IOPCIDevice::clientCrashedThreadCall),
this);
if(threadCall != NULL)
{
retain();
parent->retain();
if(thread_call_enter1(threadCall, threadCall ) == TRUE)
{
thread_call_free(threadCall);
release();
parent->release();
}
}
}
return kIOReturnSuccess;
}
IOReturn IOPCIDevice::clientCrashedThreadCall(thread_call_t threadCall)
{
OSIterator* peerIterator = parent->getChildIterator(gIOServicePlane);
OSObject* peer = NULL;
while ( (peerIterator != NULL)
&& ((peer = peerIterator->getNextObject()) != NULL))
{
IOPCIDevice* pciPeer = OSDynamicCast(IOPCIDevice, peer);
if ( (pciPeer != NULL)
&& (pciPeer->isInactive() == false))
{
DLOG("%s Terminating device %u:%u:%u\n", __PRETTY_FUNCTION__, PCI_ADDRESS_TUPLE(pciPeer));
pciPeer->terminate();
}
}
IOPCIDevice* bridgeDevice = OSDynamicCast(IOPCIDevice, parent->getParentEntry(gIOServicePlane));
if (bridgeDevice != NULL)
{
DLOG("%s waiting for downstream devices to finish terminating\n", __PRETTY_FUNCTION__);
parent->waitQuiet();
DLOG("%s reprobing bus\n", __PRETTY_FUNCTION__);
parent->kernelRequestProbe(bridgeDevice, kIOPCIProbeOptionNeedsScan | kIOPCIProbeOptionDone);
}
release();
parent->release();
thread_call_free(threadCall);
return kIOReturnSuccess;
}
kern_return_t
IMPL(IOPCIDevice, _MemoryAccess)
{
if (isOpen(forClient) == false)
{
DLOG("IOPCIDevice::%s: device not open for client %s\n", __FUNCTION__, (forClient != NULL) ? forClient->getName() : "unknown client");
return kIOReturnNotOpen;
}
uint8_t memoryIndex = operation & kPCIDriverKitMemoryAccessOperationDeviceMemoryIndexMask;
if(memoryIndex > kIOPCIRangeExpansionROM)
{
DLOG("IOPCIDevice::%s: invalid index %u for client %s\n", __FUNCTION__, memoryIndex, (forClient != NULL) ? forClient->getName() : "unknown client");
return kIOReturnBadArgument;
}
IOReturn result = kIOReturnSuccess;
#if TARGET_CPU_X86 || TARGET_CPU_X86_64
if((operation & (kPCIDriverKitMemoryAccessOperationIORead | kPCIDriverKitMemoryAccessOperationIOWrite)) != 0)
{
uint16_t ioSpaceOffset = 0;
if(os_convert_overflow(offset, &ioSpaceOffset) == true)
{
result = kIOReturnBadArgument;
DLOG("%s::%s bad offset 0x%llx\n", "IOPCIDevice", __FUNCTION__, offset);
}
IOMemoryDescriptor* memoryDescriptor = reserved->deviceMemory[memoryIndex];
if ( (memoryDescriptor != NULL)
&& (ioMap != NULL))
{
IOPCIAddressSpace addressSpace;
addressSpace.bits = static_cast<uint32_t>(memoryDescriptor->getTag());
IOPhysicalAddress ioSpaceStartAddress = ioMap->getPhysicalAddress();
IOPhysicalAddress ioSpaceBarAddress = memoryDescriptor->getPhysicalAddress();
IOByteCount accessSize = 0;
switch (operation & kPCIDriverKitMemoryAccessOperationSizeMask)
{
case kPCIDriverKitMemoryAccessOperation8Bit:
{
accessSize = sizeof(uint8_t);
break;
}
case kPCIDriverKitMemoryAccessOperation16Bit:
{
accessSize = sizeof(uint16_t);
break;
}
case kPCIDriverKitMemoryAccessOperation32Bit:
{
accessSize = sizeof(uint32_t);
break;
}
default:
{
DLOG("%s::%s bad request with memeoryIndex %u, offset 0x%x, operation 0x%llx\n", "IOPCIDevice", __FUNCTION__, memoryIndex, ioSpaceOffset, operation);
break;
}
}
if( (addressSpace.s.space == kIOPCIIOSpace)
&& (accessSize > 0)
&& ((ioSpaceOffset + accessSize) <= memoryDescriptor->getLength())
&& (ioSpaceStartAddress <= ioSpaceBarAddress))
{
ioSpaceOffset += static_cast<uint16_t>(ioSpaceBarAddress - ioSpaceStartAddress);
}
else
{
result = kIOReturnBadArgument;
DLOG("%s::%s bad request with memeoryIndex %u, offset 0x%x\n", "IOPCIDevice", __FUNCTION__, memoryIndex, ioSpaceOffset);
}
memoryDescriptor = NULL;
}
else
{
result = kIOReturnBadArgument;
}
if (result == kIOReturnSuccess)
{
switch (operation & (~kPCIDriverKitMemoryAccessOperationDeviceMemoryIndexMask))
{
case kPCIDriverKitMemoryAccessOperationIORead | kPCIDriverKitMemoryAccessOperation32Bit:
{
*readData = ioRead32(ioSpaceOffset);
break;
}
case kPCIDriverKitMemoryAccessOperationIORead | kPCIDriverKitMemoryAccessOperation16Bit:
{
*readData = ioRead16(ioSpaceOffset);
break;
}
case kPCIDriverKitMemoryAccessOperationIORead | kPCIDriverKitMemoryAccessOperation8Bit:
{
*readData = ioRead8(ioSpaceOffset);
break;
}
case kPCIDriverKitMemoryAccessOperationIOWrite | kPCIDriverKitMemoryAccessOperation32Bit:
{
ioWrite32(ioSpaceOffset, static_cast<uint32_t>(data));
break;
}
case kPCIDriverKitMemoryAccessOperationIOWrite | kPCIDriverKitMemoryAccessOperation16Bit:
{
ioWrite16(ioSpaceOffset, static_cast<uint16_t>(data));
break;
}
case kPCIDriverKitMemoryAccessOperationIOWrite | kPCIDriverKitMemoryAccessOperation8Bit:
{
ioWrite8(ioSpaceOffset, static_cast<uint8_t>(data));
break;
}
default:
{
DLOG("%s::%s bad request with memeoryIndex %u, offset 0x%x, operation 0x%llx\n",
"IOPCIDevice",
__FUNCTION__,
memoryIndex,
ioSpaceOffset,
operation);
result = kIOReturnUnsupported;
break;
}
}
}
}
else
#endif
{
switch (operation & (~kPCIDriverKitMemoryAccessOperationDeviceMemoryIndexMask))
{
case kPCIDriverKitMemoryAccessOperationConfigurationRead | kPCIDriverKitMemoryAccessOperation32Bit:
{
*readData = extendedConfigRead32(offset);
break;
}
case kPCIDriverKitMemoryAccessOperationConfigurationRead | kPCIDriverKitMemoryAccessOperation16Bit:
{
*readData = extendedConfigRead16(offset);
break;
}
case kPCIDriverKitMemoryAccessOperationConfigurationRead | kPCIDriverKitMemoryAccessOperation8Bit:
{
*readData = extendedConfigRead8(offset);
break;
}
case kPCIDriverKitMemoryAccessOperationConfigurationWrite | kPCIDriverKitMemoryAccessOperation32Bit:
{
extendedConfigWrite32(offset, static_cast<uint32_t>(data));
break;
}
case kPCIDriverKitMemoryAccessOperationConfigurationWrite | kPCIDriverKitMemoryAccessOperation16Bit:
{
extendedConfigWrite16(offset, static_cast<uint16_t>(data));
break;
}
case kPCIDriverKitMemoryAccessOperationConfigurationWrite | kPCIDriverKitMemoryAccessOperation8Bit:
{
extendedConfigWrite8(offset, static_cast<uint8_t>(data));
break;
}
case kPCIDriverKitMemoryAccessOperationDeviceRead | kPCIDriverKitMemoryAccessOperation64Bit:
{
result = deviceMemoryRead64(memoryIndex, offset, readData);
break;
}
case kPCIDriverKitMemoryAccessOperationDeviceRead | kPCIDriverKitMemoryAccessOperation32Bit:
{
result = deviceMemoryRead32(memoryIndex, offset, reinterpret_cast<uint32_t*>(readData));
break;
}
case kPCIDriverKitMemoryAccessOperationDeviceRead | kPCIDriverKitMemoryAccessOperation16Bit:
{
result = deviceMemoryRead16(memoryIndex, offset, reinterpret_cast<uint16_t*>(readData));
break;
}
case kPCIDriverKitMemoryAccessOperationDeviceRead | kPCIDriverKitMemoryAccessOperation8Bit:
{
result = deviceMemoryRead8(memoryIndex, offset, reinterpret_cast<uint8_t*>(readData));
break;
}
case kPCIDriverKitMemoryAccessOperationDeviceWrite | kPCIDriverKitMemoryAccessOperation64Bit:
{
result = deviceMemoryWrite64(memoryIndex, offset, static_cast<uint64_t>(data));
break;
}
case kPCIDriverKitMemoryAccessOperationDeviceWrite | kPCIDriverKitMemoryAccessOperation32Bit:
{
result = deviceMemoryWrite32(memoryIndex, offset, static_cast<uint32_t>(data));
break;
}
case kPCIDriverKitMemoryAccessOperationDeviceWrite | kPCIDriverKitMemoryAccessOperation16Bit:
{
result = deviceMemoryWrite16(memoryIndex, offset, static_cast<uint16_t>(data));
break;
}
case kPCIDriverKitMemoryAccessOperationDeviceWrite | kPCIDriverKitMemoryAccessOperation8Bit:
{
result = deviceMemoryWrite8(memoryIndex, offset, static_cast<uint8_t>(data));
break;
}
default:
{
result = kIOReturnUnsupported;
break;
}
}
}
return result;
}
kern_return_t
IMPL(IOPCIDevice, _CopyDeviceMemoryWithIndex)
{
if (isOpen(forClient) == false)
{
DLOG("IOPCIDevice::%s: device not open for client %s\n", __FUNCTION__, (forClient != NULL) ? forClient->getName() : "unknown client");
return kIOReturnNotOpen;
}
if(memoryIndex > kIOPCIRangeExpansionROM)
{
DLOG("IOPCIDevice::%s: invalid index %llu for client %s\n", __FUNCTION__, memoryIndex, (forClient != NULL) ? forClient->getName() : "unknown client");
return kIOReturnBadArgument;
}
IOReturn result = kIOReturnUnsupported;
IOMemoryDescriptor * memoryDescriptor = reserved->deviceMemory[memoryIndex];
OSArray* deviceMemoryArray = NULL;
OSObject* deviceMemoryObject = copyProperty(gIODeviceMemoryKey);
if((deviceMemoryArray = OSDynamicCast(OSArray, deviceMemoryObject)) != NULL)
{
if(memoryIndex <= deviceMemoryArray->getCount())
{
memoryDescriptor = OSDynamicCast(IOMemoryDescriptor, deviceMemoryArray->getObject(static_cast<uint32_t>(memoryIndex)));
}
if (memoryDescriptor != NULL)
{
if (kTunnelL1NotSet == reserved->tunnelL1Allow) setTunnelL1Enable(this, false);
IOPCIAddressSpace addressSpace;
addressSpace.bits = static_cast<uint32_t>(memoryDescriptor->getTag());
if(addressSpace.s.space == kIOPCIIOSpace)
{
memoryDescriptor = NULL;
*returnMemory = NULL;
}
else
{
memoryDescriptor->retain();
*returnMemory = memoryDescriptor;
result = kIOReturnSuccess;
}
}
}
OSSafeReleaseNULL(deviceMemoryObject);
return result;
}
#pragma mark Configuration Space helpers
kern_return_t
IMPL(IOPCIDevice, FindPCICapability)
{
IOReturn result = kIOReturnNotFound;
*foundCapabilityOffset = searchOffset;
if(extendedFindPCICapability(capabilityID, foundCapabilityOffset) != 0)
{
result = kIOReturnSuccess;
}
return result;
}
kern_return_t
IMPL(IOPCIDevice, GetBusDeviceFunction)
{
*returnBusNumber = getBusNumber();
*returnDeviceNumber = getDeviceNumber();
*returnFunctionNumber = getFunctionNumber();
return kIOReturnSuccess;
}
#pragma mark Power Management
kern_return_t
IMPL(IOPCIDevice, HasPCIPowerManagement)
{
return hasPCIPowerManagement(static_cast<IOOptionBits>(state)) ? kIOReturnSuccess : kIOReturnUnsupported;
}
kern_return_t
IMPL(IOPCIDevice, EnablePCIPowerManagement)
{
return enablePCIPowerManagement(static_cast<IOOptionBits>(state));
}
#endif
#undef super
#define super IOPCIDevice
OSDefineMetaClassAndStructors(IOAGPDevice, IOPCIDevice)
OSMetaClassDefineReservedUnused(IOAGPDevice, 0);
OSMetaClassDefineReservedUnused(IOAGPDevice, 1);
OSMetaClassDefineReservedUnused(IOAGPDevice, 2);
OSMetaClassDefineReservedUnused(IOAGPDevice, 3);
OSMetaClassDefineReservedUnused(IOAGPDevice, 4);
OSMetaClassDefineReservedUnused(IOAGPDevice, 5);
OSMetaClassDefineReservedUnused(IOAGPDevice, 6);
OSMetaClassDefineReservedUnused(IOAGPDevice, 7);
OSMetaClassDefineReservedUnused(IOAGPDevice, 8);
OSMetaClassDefineReservedUnused(IOAGPDevice, 9);
OSMetaClassDefineReservedUnused(IOAGPDevice, 10);
OSMetaClassDefineReservedUnused(IOAGPDevice, 11);
OSMetaClassDefineReservedUnused(IOAGPDevice, 12);
OSMetaClassDefineReservedUnused(IOAGPDevice, 13);
OSMetaClassDefineReservedUnused(IOAGPDevice, 14);
OSMetaClassDefineReservedUnused(IOAGPDevice, 15);
OSMetaClassDefineReservedUnused(IOAGPDevice, 16);
IOReturn IOAGPDevice::createAGPSpace( IOOptionBits options,
IOPhysicalAddress * address,
IOPhysicalLength * length )
{
return (parent->createAGPSpace(this, options, address, length));
}
IOReturn IOAGPDevice::destroyAGPSpace( void )
{
return (parent->destroyAGPSpace(this));
}
IORangeAllocator * IOAGPDevice::getAGPRangeAllocator( void )
{
return (parent->getAGPRangeAllocator(this));
}
IOOptionBits IOAGPDevice::getAGPStatus( IOOptionBits options )
{
return (parent->getAGPStatus(this, options));
}
IOReturn IOAGPDevice::resetAGP( IOOptionBits options )
{
return (parent->resetAGPDevice(this, options));
}
IOReturn IOAGPDevice::getAGPSpace( IOPhysicalAddress * address,
IOPhysicalLength * length )
{
return (parent->getAGPSpace(this, address, length));
}
IOReturn IOAGPDevice::commitAGPMemory( IOMemoryDescriptor * memory,
IOByteCount agpOffset,
IOOptionBits options )
{
return (parent->commitAGPMemory(this, memory, agpOffset, options));
}
IOReturn IOAGPDevice::releaseAGPMemory( IOMemoryDescriptor * memory,
IOByteCount agpOffset,
IOOptionBits options )
{
return (parent->releaseAGPMemory(this, memory, agpOffset, options));
}