Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
IOPCIFamily/IOPCIDevice.cpp
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
2693 lines (2307 sloc)
87.8 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved. | |
| * | |
| * @APPLE_LICENSE_HEADER_START@ | |
| * | |
| * The contents of this file constitute Original Code as defined in and | |
| * are subject to the Apple Public Source License Version 1.1 (the | |
| * "License"). You may not use this file except in compliance with the | |
| * License. Please obtain a copy of the License at | |
| * http://www.apple.com/publicsource and read it before using this file. | |
| * | |
| * This Original Code and all software distributed under the License are | |
| * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
| * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
| * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
| * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the | |
| * License for the specific language governing rights and limitations | |
| * under the License. | |
| * | |
| * @APPLE_LICENSE_HEADER_END@ | |
| */ | |
| #define __IOPCIDEVICE_INTERNAL__ 1 | |
| #include <IOKit/system.h> | |
| #include <IOKit/pci/IOPCIPrivate.h> | |
| #include <IOKit/pci/IOPCIBridge.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 <libkern/OSKextLib.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 | |
| //#define PROT_DEVICE "XGBE" | |
| enum | |
| { | |
| // reserved->pmSleepEnabled | |
| 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 /* !DEVELOPMENT && !defined(__x86_64__) */ | |
| #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 /* PROT_DEVICE */ | |
| /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
| #define super IOService | |
| OSDefineMetaClassAndStructors(IOPCIDevice, IOService) | |
| 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); | |
| /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
| //********************************************************************************* | |
| // [public] maxCapabilityForDomainState | |
| // | |
| // Finds the highest power state in the array whose input power | |
| // requirement is equal to the input parameter. Where a more intelligent | |
| // decision is possible, override this in the subclassed driver. | |
| //********************************************************************************* | |
| unsigned long | |
| IOPCIDevice::maxCapabilityForDomainState ( IOPMPowerFlags domainState ) | |
| { | |
| if (domainState & kIOPMPowerOn) return (kIOPCIDeviceOnState); | |
| if (domainState & kIOPMSoftSleep) return (kIOPCIDeviceDozeState); | |
| if (domainState & kIOPMConfigRetained) return (kIOPCIDevicePausedState); | |
| return (kIOPCIDeviceOffState); | |
| } | |
| //********************************************************************************* | |
| // [public] initialPowerStateForDomainState | |
| // | |
| // Finds the highest power state in the array whose input power | |
| // requirement is equal to the input parameter. Where a more intelligent | |
| // decision is possible, override this in the subclassed driver. | |
| //********************************************************************************* | |
| 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); | |
| } | |
| //********************************************************************************* | |
| // [public] powerStateForDomainState | |
| // | |
| // Finds the highest power state in the array whose input power | |
| // requirement is equal to the input parameter. Where a more intelligent | |
| // decision is possible, override this in the subclassed driver. | |
| //********************************************************************************* | |
| 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); | |
| } | |
| /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
| // attach | |
| // | |
| /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
| 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) | |
| { | |
| // R/WC PME_Status | |
| extendedConfigWrite16(reserved->pmControlStatus, pmcsr); | |
| } | |
| } | |
| // initialize superclass variables | |
| PMinit(); | |
| // clamp power on | |
| temporaryPowerClampOn(); | |
| // register as controlling driver | |
| IOPCIRegisterPowerDriver(this, false); | |
| // join the tree | |
| 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(); | |
| IORecursiveLockLock(reserved->lock); | |
| while (false && reserved->pmActive) | |
| { | |
| reserved->pmWait = true; | |
| IORecursiveLockSleep(reserved->lock, &reserved->pmActive, THREAD_UNINT); | |
| } | |
| IORecursiveLockUnlock(reserved->lock); | |
| if (parent) parent->removeDevice(this); | |
| super::detach(provider); | |
| detachAbove(gIODTPlane); | |
| } | |
| void | |
| IOPCIDevice::detachAbove( const IORegistryPlane * plane ) | |
| { | |
| super::detachAbove(plane); | |
| if (plane == gIOPowerPlane) | |
| { | |
| IORecursiveLockLock(reserved->lock); | |
| reserved->pmActive = false; | |
| if (reserved->pmWait) | |
| IORecursiveLockWakeup(reserved->lock, &reserved->pmActive, true); | |
| IORecursiveLockUnlock(reserved->lock); | |
| } | |
| } | |
| bool | |
| IOPCIDevice::initReserved(void) | |
| { | |
| // allocate our expansion data | |
| if (!reserved) | |
| { | |
| reserved = IOMallocType(IOPCIDeviceExpansionData); | |
| if (!reserved) | |
| return (false); | |
| reserved->lock = IORecursiveLockAlloc(); | |
| } | |
| 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); | |
| IOFreeType(savedConfig, IOPCIConfigShadow); | |
| savedConfig = 0; | |
| } | |
| // This needs to be the LAST thing we do, as it disposes of our "fake" member | |
| // variables. | |
| // | |
| if (reserved) | |
| { | |
| if (reserved->lock) | |
| IORecursiveLockFree(reserved->lock); | |
| IOFreeType(reserved, IOPCIDeviceExpansionData); | |
| } | |
| 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)) | |
| { | |
| // if we would normally reset the PME_Status bit when going to sleep, do it now | |
| // at the beginning of the power change. that way any PME event generated from this point | |
| // until we go to sleep should wake the machine back up. | |
| pmcsr = extendedConfigRead16(reserved->pmControlStatus); | |
| if (pmcsr & kPCIPMCSPMEStatus) | |
| { | |
| // the the PME_Status bit is set at this point, we clear it but leave all other bits | |
| // untouched by writing the exact same value back to the register. This is because the | |
| // PME_Status bit is R/WC. | |
| 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); | |
| } | |
| /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
| // PCI setPowerState | |
| // | |
| /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
| 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; | |
| } | |
| // void * p3 = (void *) 0; | |
| // if (doMsg) device->callPlatformFunction(gIOPlatformDeviceMessageKey, false, | |
| // (void *) kIOMessageDeviceWillPowerOff, device, p3, (void *) 0); | |
| // if (doMsg) device->callPlatformFunction(gIOPlatformDeviceMessageKey, false, | |
| // (void *) kIOMessageDeviceHasPoweredOn, device, p3, (void *) 0); | |
| #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 | |
| { | |
| // we don't clear the PME_Status at this time. Instead, we cleared it in powerStateWillChangeTo | |
| // so this write will change our power state to the desired state and will also set PME_En | |
| 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)); | |
| // the write below will clear PME_Status, clear PME_En, and set the Power State to D0 | |
| 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)); | |
| // the write below will clear PME_Status, clear PME_En, and set the Power State to D0 | |
| 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); | |
| } | |
| /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
| IOReturn IOPCIDevice::addPowerChild(IOService *theChild) | |
| { | |
| #if TARGET_CPU_ARM || TARGET_CPU_ARM64 | |
| IOPCIHostBridgeData *vars = parent->reserved->hostBridgeData; | |
| vars->addPCIEPowerChild(theChild); | |
| #endif | |
| return super::addPowerChild(theChild); | |
| } | |
| IOReturn IOPCIDevice::removePowerChild(IOPowerConnection *theChild) | |
| { | |
| #if TARGET_CPU_ARM || TARGET_CPU_ARM64 | |
| IOPCIHostBridgeData *vars = parent->reserved->hostBridgeData; | |
| vars->removePCIEPowerChild(theChild); | |
| #endif | |
| return super::removePowerChild(theChild); | |
| } | |
| 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(); | |
| } | |
| } | |
| } | |
| /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
| // PM setPowerState | |
| // | |
| /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
| 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 /* PROT_DEVICE */ | |
| return (ret); | |
| } | |
| IOReturn IOPCIDevice::saveDeviceState( IOOptionBits options ) | |
| { | |
| IOReturn ret; | |
| if (!reserved->interruptVectorsResolved) | |
| { | |
| // TODO: Remove the temporary workaround for legacy drivers | |
| (void)getProperty(gIOInterruptSpecifiersKey); | |
| } | |
| 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); | |
| // Check if the entitlements match, but only if: | |
| // 1. The original matching dictionary matches | |
| // 2. The kIOPCITransportDextEntitlement key exists. | |
| // Otherwise return the value of 'isMatch' and don't do any entitlement validation. | |
| 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 ) | |
| { | |
| if (!parent) return (0xFFFFFFFF); | |
| return (parent->configRead32(_space, offset)); | |
| } | |
| void IOPCIDevice::configWrite32( IOPCIAddressSpace _space, | |
| UInt8 offset, UInt32 data ) | |
| { | |
| if (!parent) return; | |
| parent->configWrite32( _space, offset, data ); | |
| } | |
| UInt16 IOPCIDevice::configRead16( IOPCIAddressSpace _space, | |
| UInt8 offset ) | |
| { | |
| if (!parent) return (0xFFFF); | |
| return (parent->configRead16(_space, offset)); | |
| } | |
| void IOPCIDevice::configWrite16( IOPCIAddressSpace _space, | |
| UInt8 offset, UInt16 data ) | |
| { | |
| if (!parent) return; | |
| parent->configWrite16( _space, offset, data ); | |
| } | |
| UInt8 IOPCIDevice::configRead8( IOPCIAddressSpace _space, | |
| UInt8 offset ) | |
| { | |
| if (!parent) return (0xFF); | |
| return (parent->configRead8(_space, offset)); | |
| } | |
| void IOPCIDevice::configWrite8( IOPCIAddressSpace _space, | |
| UInt8 offset, UInt8 data ) | |
| { | |
| if (!parent) return; | |
| parent->configWrite8( _space, offset, data ); | |
| } | |
| /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
| bool IOPCIDevice::configAccess(bool write) | |
| { | |
| bool ok = (!isInactive() | |
| && reserved | |
| && parent | |
| && (0 == ((write ? VM_PROT_WRITE : VM_PROT_READ) & reserved->configProt))); | |
| if (!ok && !ml_at_interrupt_context() && (gIOPCIFlags & kIOPCIConfiguratorIOLog)) | |
| { | |
| 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 /* APPLE_KEXT_VTABLE_PADDING */ | |
| // -- | |
| UInt32 IOPCIDevice::extendedConfigRead32( IOByteCount offset ) | |
| { | |
| // Access must be within 4KB configuration space | |
| if (!configAccess(false) || offset > (0x1000 - sizeof(uint32_t))) return (0xFFFFFFFF); | |
| IOPCIAddressSpace _space = space; | |
| _space.es.registerNumExtended = ((offset >> 8) & 0xF); | |
| return (configRead32(_space, offset)); | |
| } | |
| void IOPCIDevice::extendedConfigWrite32( IOByteCount offset, UInt32 data ) | |
| { | |
| // Access must be within 4KB configuration space | |
| if (!configAccess(true) || offset > (0x1000 - sizeof(uint32_t))) return; | |
| IOPCIAddressSpace _space = space; | |
| _space.es.registerNumExtended = ((offset >> 8) & 0xF); | |
| configWrite32(_space, offset, data); | |
| } | |
| UInt16 IOPCIDevice::extendedConfigRead16( IOByteCount offset ) | |
| { | |
| // Access must be within 4KB configuration space | |
| if (!configAccess(false) || offset > (0x1000 - sizeof(uint16_t))) return (0xFFFF); | |
| IOPCIAddressSpace _space = space; | |
| _space.es.registerNumExtended = ((offset >> 8) & 0xF); | |
| return (configRead16(_space, offset)); | |
| } | |
| void IOPCIDevice::extendedConfigWrite16( IOByteCount offset, UInt16 data ) | |
| { | |
| // Access must be within 4KB configuration space | |
| if (!configAccess(true) || offset > (0x1000 - sizeof(uint16_t))) return; | |
| IOPCIAddressSpace _space = space; | |
| _space.es.registerNumExtended = ((offset >> 8) & 0xF); | |
| configWrite16(_space, offset, data); | |
| } | |
| UInt8 IOPCIDevice::extendedConfigRead8( IOByteCount offset ) | |
| { | |
| // Access must be within 4KB configuration space | |
| if (!configAccess(false) || offset > (0x1000 - sizeof(uint8_t))) return (0xFF); | |
| IOPCIAddressSpace _space = space; | |
| _space.es.registerNumExtended = ((offset >> 8) & 0xF); | |
| return (configRead8(_space, offset)); | |
| } | |
| void IOPCIDevice::extendedConfigWrite8( IOByteCount offset, UInt8 data ) | |
| { | |
| // Access must be within 4KB configuration space | |
| if (!configAccess(true) || offset > (0x1000 - sizeof(uint8_t))) return; | |
| IOPCIAddressSpace _space = space; | |
| _space.es.registerNumExtended = ((offset >> 8) & 0xF); | |
| configWrite8(_space, offset, data); | |
| } | |
| // -- | |
| UInt32 IOPCIDevice::findPCICapability( UInt8 capabilityID, UInt8 * offset ) | |
| { | |
| if (!configAccess(true)) return 0; | |
| return (parent->findPCICapability(space, capabilityID, offset)); | |
| } | |
| UInt32 IOPCIDevice::extendedFindPCICapability( UInt32 capabilityID, IOByteCount * offset ) | |
| { | |
| if (!configAccess(true)) return 0; | |
| 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::setBusLeadEnable( bool enable ) | |
| { | |
| return (0 != setConfigBits(kIOPCIConfigCommand, kIOPCICommandBusLead, | |
| enable ? kIOPCICommandBusLead : 0)); | |
| } | |
| bool IOPCIDevice::setBusMasterEnable( bool enable ) | |
| { | |
| return setBusLeadEnable(enable); | |
| } | |
| bool IOPCIDevice::setMemoryEnable( bool enable ) | |
| { | |
| return (0 != setConfigBits(kIOPCIConfigCommand, kIOPCICommandMemorySpace, | |
| enable ? kIOPCICommandMemorySpace : 0)); | |
| } | |
| bool IOPCIDevice::setIOEnable( bool enable, bool /* exclusive = false */ ) | |
| { | |
| // exclusive is TODO. | |
| 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 * /* client */ ) | |
| { | |
| return (this); | |
| } | |
| /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
| OSMetaClassDefineReservedUsed(IOPCIDevice, 0); | |
| bool IOPCIDevice::hasPCIPowerManagement(IOOptionBits state) | |
| { | |
| UInt16 pciPMCapReg, checkMask; | |
| OSData *aString; | |
| reserved->sleepControlBits = 0; // on a new query, we reset the proper sleep control bits | |
| if (!reserved->pmControlStatus) return (false); | |
| pciPMCapReg = extendedConfigRead16(reserved->pmControlStatus - sizeof(uint16_t)); | |
| // DLOG("%s[%p]::hasPCIPwrMgmt found pciPMCapReg %x\n", | |
| // getName(), this, pciPMCapReg); | |
| 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)); | |
| } | |
| /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
| OSObject* IOPCIDevice::getProperty(const OSSymbol * aKey) const | |
| { | |
| OSObject *value; | |
| if (aKey == gIOInterruptControllersKey || aKey == gIOInterruptSpecifiersKey) | |
| { | |
| IORecursiveLockLock(reserved->lock); | |
| value = super::getProperty(aKey); | |
| if (!value) | |
| { | |
| OSArray * controllers = OSArray::withCapacity(1); | |
| OSArray * specifiers = OSArray::withCapacity(1); | |
| if (controllers && specifiers) | |
| { | |
| IOPCIDevice * self = const_cast<IOPCIDevice*>(this); | |
| self->setProperty(gIOInterruptControllersKey, controllers); | |
| self->setProperty(gIOInterruptSpecifiersKey, specifiers); | |
| } | |
| OSSafeReleaseNULL(controllers); | |
| OSSafeReleaseNULL(specifiers); | |
| } | |
| if (!reserved->interruptVectorsResolved) | |
| { | |
| reserved->interruptVectorsResolved = 1; | |
| parent->resolveInterrupts(const_cast<IOPCIDevice*>(this)); | |
| } | |
| IORecursiveLockUnlock(reserved->lock); | |
| if (!value) | |
| { | |
| value = super::getProperty(aKey); | |
| } | |
| } else { | |
| value = super::getProperty(aKey); | |
| } | |
| return value; | |
| } | |
| IOReturn | |
| IOPCIDevice::configureInterrupts(UInt32 interruptType, UInt32 numRequired, UInt32 numRequested, IOOptionBits options) | |
| { | |
| IOReturn ret = kIOReturnBadArgument; | |
| if ((numRequired < 1) || (numRequested < 1) || !!options) return ret; | |
| IORecursiveLockLock(reserved->lock); | |
| if (reserved->interruptVectorsResolved) // TODO add cleanup support on numRequired == 0 (if needed). | |
| { | |
| IORecursiveLockUnlock(reserved->lock); | |
| return kIOReturnUnsupported; | |
| } | |
| reserved->interruptVectorsResolved = 1; | |
| switch (interruptType) | |
| { | |
| case kIOInterruptTypeLevel: | |
| ret = parent->resolveLegacyInterrupts(parent->getProvider(), this); | |
| break; | |
| case kIOInterruptTypePCIMessaged: | |
| if (reserved->msiMode & kMSIX) | |
| { | |
| IOByteCount capa = 0; | |
| extendedFindPCICapability(kIOPCIMSICapability, &capa); | |
| if (!capa) break; | |
| reserved->msiMode &= ~kMSIX; | |
| reserved->msiCapability = capa; | |
| } | |
| ret = parent->resolveMSIInterrupts(parent->getProvider(), this, numRequired, numRequested); | |
| break; | |
| case kIOInterruptTypePCIMessagedX: | |
| if (~reserved->msiMode & kMSIX) | |
| { | |
| IOByteCount capa = 0; | |
| extendedFindPCICapability(kIOPCIMSIXCapability, &capa); | |
| if (!capa) break; | |
| reserved->msiMode |= kMSIX; | |
| reserved->msiCapability = capa; | |
| } | |
| ret = parent->resolveMSIInterrupts(parent->getProvider(), this, numRequired, numRequested); | |
| } | |
| if (ret) | |
| { | |
| reserved->interruptVectorsResolved = 0; | |
| } | |
| IORecursiveLockUnlock(reserved->lock); | |
| return ret; | |
| } | |
| 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); | |
| } | |
| // debug | |
| 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) | |
| { | |
| OSObject *prop = NULL; | |
| bool result = super::handleOpen(forClient, options, arg); | |
| if (result == true) | |
| { | |
| reserved->sessionOptions = options; | |
| #if ACPI_SUPPORT | |
| // Copy relevant keys for mapper selection | |
| if (forClient) { | |
| if (forClient->getProperty(kIOPCIUseDeviceMapperKey)) | |
| setProperty(kIOPCIUseDeviceMapperKey, true); | |
| if ((prop = forClient->getProperty(kCFBundleIdentifierKey)) != NULL) | |
| setProperty(kIOPCIChildBundleIdentifierKey, prop); | |
| } | |
| getResources(); | |
| #endif | |
| if((options & kIOPCISessionOptionDriverkit) != 0) | |
| { | |
| // check if the task is entitled to disable the offload engine | |
| #if 0 | |
| OSObject* offloadEngineDisableEntitlement = IOUserClient::copyClientEntitlement(current_task(), kIOPCITransportDextEntitlementOffloadEngineDisable); | |
| if(offloadEngineDisableEntitlement != NULL) | |
| { | |
| reserved->offloadEngineMMIODisable = 1; | |
| } | |
| OSSafeReleaseNULL(offloadEngineDisableEntitlement); | |
| #else | |
| reserved->offloadEngineMMIODisable = 1; | |
| #endif | |
| } | |
| } | |
| return result; | |
| } | |
| void IOPCIDevice::handleClose(IOService * forClient, IOOptionBits options) | |
| { | |
| if ((forClient != NULL) && (isOpen(forClient) == true)) | |
| { | |
| if ((reserved->sessionOptions & kIOPCISessionOptionDriverkit) != 0) | |
| { | |
| reserved->offloadEngineMMIODisable = 0; | |
| // Driverkit either called close or crashed. Turn off bus leading to prevent any further DMAs | |
| uint16_t command = extendedConfigRead16(kIOPCIConfigurationOffsetCommand); | |
| if ((command & (kIOPCICommandBusLead | kIOPCICommandMemorySpace)) != 0) | |
| { | |
| DLOG("IOPCIDevice::handleClose: disabling memory and bus leading for client %s\n", (forClient) ? forClient->getName() : "unknown"); | |
| extendedConfigWrite16(kIOPCIConfigurationOffsetCommand, command & ~(kIOPCICommandBusLead | kIOPCICommandMemorySpace)); | |
| } | |
| } | |
| #if ACPI_SUPPORT | |
| removeProperty(kIOPCIChildBundleIdentifierKey); | |
| removeProperty(kIOPCIUseDeviceMapperKey); | |
| removeProperty("iommu-selection"); | |
| #endif | |
| } | |
| 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) | |
| { | |
| IOMemoryMap* deviceMemoryMap = reserved->deviceMemoryMap[memoryIndex]; | |
| IOReturn result = kIOReturnUnsupported; | |
| if(deviceMemoryMap != NULL) | |
| { | |
| IOByteCount length = deviceMemoryMap->getLength(); | |
| uint64_t sum = 0; | |
| if( (offset + sizeof(uint64_t)) > length | |
| || (os_add_overflow(offset, sizeof(uint64_t), &sum))) | |
| { | |
| return kIOReturnOverrun; | |
| } | |
| } | |
| else | |
| { | |
| DLOG("IOPCIDevice::deviceMemoryRead64: index %u could not get mapping\n", memoryIndex); | |
| return kIOReturnNoMemory; | |
| } | |
| #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->hostBridge->deviceMemoryRead(deviceMemoryDescriptor, | |
| offset, | |
| readData, | |
| sizeof(uint64_t)); | |
| } | |
| #endif | |
| // if the host bridge can't do the access through a DMA transaction, directly do the transaction ourself | |
| if(result == kIOReturnUnsupported) | |
| { | |
| *readData = ml_io_read64(deviceMemoryMap->getVirtualAddress() + offset); | |
| result = kIOReturnSuccess; | |
| } | |
| return result; | |
| } | |
| IOReturn IOPCIDevice::deviceMemoryRead32(uint8_t memoryIndex, | |
| uint64_t offset, | |
| uint32_t* readData) | |
| { | |
| IOMemoryMap* deviceMemoryMap = reserved->deviceMemoryMap[memoryIndex]; | |
| IOReturn result = kIOReturnUnsupported; | |
| if(deviceMemoryMap != NULL) | |
| { | |
| IOByteCount length = deviceMemoryMap->getLength(); | |
| uint64_t sum = 0; | |
| if( (offset + sizeof(uint32_t)) > length | |
| || (os_add_overflow(offset, sizeof(uint32_t), &sum))) | |
| { | |
| return kIOReturnOverrun; | |
| } | |
| } | |
| else | |
| { | |
| DLOG("IOPCIDevice::deviceMemoryRead32: index %u could not get mapping\n", memoryIndex); | |
| return kIOReturnNoMemory; | |
| } | |
| #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->hostBridge->deviceMemoryRead(deviceMemoryDescriptor, | |
| offset, | |
| readData, | |
| sizeof(uint32_t)); | |
| } | |
| #endif | |
| // if the host bridge can't do the access through a DMA transaction, directly do the transaction ourself | |
| if(result == kIOReturnUnsupported) | |
| { | |
| *readData = ml_io_read32(deviceMemoryMap->getVirtualAddress() + offset); | |
| result = kIOReturnSuccess; | |
| } | |
| return result; | |
| } | |
| IOReturn IOPCIDevice::deviceMemoryRead16(uint8_t memoryIndex, | |
| uint64_t offset, | |
| uint16_t* readData) | |
| { | |
| IOMemoryMap* deviceMemoryMap = reserved->deviceMemoryMap[memoryIndex]; | |
| IOReturn result = kIOReturnUnsupported; | |
| if(deviceMemoryMap != NULL) | |
| { | |
| IOByteCount length = deviceMemoryMap->getLength(); | |
| uint64_t sum = 0; | |
| if( (offset + sizeof(uint16_t)) > length | |
| || (os_add_overflow(offset, sizeof(uint16_t), &sum))) | |
| { | |
| return kIOReturnOverrun; | |
| } | |
| } | |
| else | |
| { | |
| DLOG("IOPCIDevice::deviceMemoryRead16: index %u could not get mapping\n", memoryIndex); | |
| return kIOReturnNoMemory; | |
| } | |
| #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->hostBridge->deviceMemoryRead(deviceMemoryDescriptor, | |
| offset, | |
| readData, | |
| sizeof(uint16_t)); | |
| } | |
| #endif | |
| // if the host bridge can't do the access through a DMA transaction, directly do the transaction ourself | |
| if(result == kIOReturnUnsupported) | |
| { | |
| *readData = ml_io_read16(deviceMemoryMap->getVirtualAddress() + offset); | |
| result = kIOReturnSuccess; | |
| } | |
| return result; | |
| } | |
| IOReturn IOPCIDevice::deviceMemoryRead8(uint8_t memoryIndex, | |
| uint64_t offset, | |
| uint8_t* readData) | |
| { | |
| IOMemoryMap* deviceMemoryMap = reserved->deviceMemoryMap[memoryIndex]; | |
| IOReturn result = kIOReturnUnsupported; | |
| if(deviceMemoryMap != NULL) | |
| { | |
| IOByteCount length = deviceMemoryMap->getLength(); | |
| uint64_t sum = 0; | |
| if( (offset + sizeof(uint8_t)) > length | |
| || (os_add_overflow(offset, sizeof(uint8_t), &sum))) | |
| { | |
| return kIOReturnOverrun; | |
| } | |
| } | |
| else | |
| { | |
| DLOG("IOPCIDevice::deviceMemoryRead8: index %u could not get mapping\n", memoryIndex); | |
| return kIOReturnNoMemory; | |
| } | |
| #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->hostBridge->deviceMemoryRead(deviceMemoryDescriptor, | |
| offset, | |
| readData, | |
| sizeof(uint8_t)); | |
| } | |
| #endif | |
| // if the host bridge can't do the access through a DMA transaction, directly do the transaction ourself | |
| if(result == kIOReturnUnsupported) | |
| { | |
| *readData = ml_io_read8(deviceMemoryMap->getVirtualAddress() + offset); | |
| result = kIOReturnSuccess; | |
| } | |
| return result; | |
| } | |
| // TODO: support memory writes being routed to the host bridge? | |
| IOReturn IOPCIDevice::deviceMemoryWrite64(uint8_t memoryIndex, | |
| uint64_t offset, | |
| uint64_t data) | |
| { | |
| IOReturn result = kIOReturnUnsupported; | |
| IOMemoryMap* deviceMemoryMap = reserved->deviceMemoryMap[memoryIndex]; | |
| if(deviceMemoryMap != NULL) | |
| { | |
| IOVirtualAddress address = deviceMemoryMap->getVirtualAddress(); | |
| IOByteCount length = deviceMemoryMap->getLength(); | |
| uint64_t sum = 0; | |
| if( (offset + sizeof(uint64_t)) > length | |
| || (os_add_overflow(offset, sizeof(uint64_t), &sum))) | |
| { | |
| return kIOReturnOverrun; | |
| } | |
| ml_io_write(address + offset, data, sizeof(uint64_t)); | |
| result = kIOReturnSuccess; | |
| } | |
| else | |
| { | |
| DLOG("IOPCIDevice::deviceMemoryWrite64: 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) | |
| { | |
| IOVirtualAddress address = deviceMemoryMap->getVirtualAddress(); | |
| IOByteCount length = deviceMemoryMap->getLength(); | |
| uint64_t sum = 0; | |
| if( (offset + sizeof(uint32_t)) > length | |
| || (os_add_overflow(offset, sizeof(uint32_t), &sum))) | |
| { | |
| return kIOReturnOverrun; | |
| } | |
| ml_io_write(address + offset, data, sizeof(uint32_t)); | |
| result = kIOReturnSuccess; | |
| } | |
| else | |
| { | |
| DLOG("IOPCIDevice::deviceMemoryWrite32: 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) | |
| { | |
| IOVirtualAddress address = deviceMemoryMap->getVirtualAddress(); | |
| IOByteCount length = deviceMemoryMap->getLength(); | |
| uint64_t sum = 0; | |
| if( (offset + sizeof(uint16_t)) > length | |
| || (os_add_overflow(offset, sizeof(uint16_t), &sum))) | |
| { | |
| return kIOReturnOverrun; | |
| } | |
| ml_io_write(address + offset, data, sizeof(uint16_t)); | |
| result = kIOReturnSuccess; | |
| } | |
| else | |
| { | |
| DLOG("IOPCIDevice::deviceMemoryWrite16: 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) | |
| { | |
| IOVirtualAddress address = deviceMemoryMap->getVirtualAddress(); | |
| IOByteCount length = deviceMemoryMap->getLength(); | |
| uint64_t sum = 0; | |
| if( (offset + sizeof(uint8_t)) > length | |
| || (os_add_overflow(offset, sizeof(uint8_t), &sum))) | |
| { | |
| return kIOReturnOverrun; | |
| } | |
| ml_io_write(address + offset, data, sizeof(uint8_t)); | |
| result = kIOReturnSuccess; | |
| } | |
| else | |
| { | |
| DLOG("IOPCIDevice::deviceMemoryWrite8: index %u could not get mapping\n", memoryIndex); | |
| return kIOReturnNoMemory; | |
| } | |
| return result; | |
| } | |
| /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
| #if TARGET_OS_HAS_PCIDRIVERKIT_IOPCIDEVICE | |
| #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) | |
| { | |
| DLOG("IOPCIDevice::ClientCrashed_Impl() for client %s\n", (client) ? client->getName() : "unknown"); | |
| // only reset the device if the driver potentially changed the state of the device | |
| 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)); | |
| IOReturn ret = parent->terminateChild(this); | |
| if (ret == kIOReturnNoDevice) | |
| { | |
| return kIOReturnSuccess; | |
| } | |
| thread_call_t threadCall = thread_call_allocate(OSMemberFunctionCast(thread_call_func_t, | |
| this, | |
| &IOPCIDevice::clientCrashedThreadCall), | |
| this); | |
| // threadcall because waiting for termination in this context can deadlock | |
| if(threadCall != NULL) | |
| { | |
| retain(); | |
| parent->retain(); | |
| if(thread_call_enter1(threadCall, threadCall /* so the call cleans itself up */) == TRUE) | |
| { | |
| thread_call_free(threadCall); | |
| parent->release(); | |
| release(); | |
| } | |
| } | |
| } | |
| else | |
| { | |
| IOLog("%s: PCIDriverKit client, %s, did not open device %s[%u:%u:%u], skipping recovery\n", | |
| __PRETTY_FUNCTION__, | |
| (client != NULL) ? client->getName() : "unknown", | |
| getName(), | |
| PCI_ADDRESS_TUPLE(this)); | |
| } | |
| return kIOReturnSuccess; | |
| } | |
| IOReturn IOPCIDevice::clientCrashedThreadCall(thread_call_t threadCall) | |
| { | |
| IOPCIDevice* bridgeDevice = OSDynamicCast(IOPCIDevice, parent->getParentEntry(gIOServicePlane)); | |
| if (bridgeDevice != NULL) | |
| { | |
| DLOG("%s waiting for downstream devices to finish terminating\n", __PRETTY_FUNCTION__); | |
| // wait for the drivers and termination to settle | |
| parent->waitQuiet(); | |
| DLOG("%s reprobing bus\n", __PRETTY_FUNCTION__); | |
| // re-scan the bridge for this device and its functions | |
| parent->kernelRequestProbe(bridgeDevice, kIOPCIProbeOptionNeedsScan | kIOPCIProbeOptionDone); | |
| } | |
| // clean up threadcall | |
| parent->release(); | |
| release(); | |
| thread_call_free(threadCall); | |
| return kIOReturnSuccess; | |
| } | |
| kern_return_t | |
| IMPL(IOPCIDevice, _MemoryAccess) | |
| { | |
| if ((forClient == NULL) || (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; | |
| // take into account the size of the access so we're not going over the the Bar region | |
| 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)) | |
| { | |
| // I/O space is physically contiguous | |
| // The I/O accessor methods use offsets from the beginning of I/O space, so the total offset needs to be | |
| // calculated | |
| 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 ((forClient == NULL) || (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) | |
| { | |
| // since we're bypassing getDeviceMemoryWithIndex, implement our subclass implementation as well. | |
| // Make sure L1 is not set | |
| 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)); | |
| } | |
| #pragma mark State Management | |
| kern_return_t | |
| IMPL(IOPCIDevice, SaveDeviceState) | |
| { | |
| return saveDeviceState(options); | |
| } | |
| kern_return_t | |
| IMPL(IOPCIDevice, RestoreDeviceState) | |
| { | |
| return restoreDeviceState(options); | |
| } | |
| #pragma mark Memory Accessor helpers | |
| kern_return_t | |
| IMPL(IOPCIDevice, GetBARInfo) | |
| { | |
| IOPCIConfigEntry *configEntry = reserved->configEntry; | |
| IOReturn result = kIOReturnNotFound; | |
| OSArray *deviceMemoryArray = NULL; | |
| if (barIndex > kIOPCIRangeExpansionROM) | |
| { | |
| DLOG("IOPCIDevice::%s:%s invalid bar index %u\n", __FUNCTION__, getName(), barIndex); | |
| return kIOReturnBadArgument; | |
| } | |
| if ((deviceMemoryArray = OSDynamicCast(OSArray, getProperty(gIODeviceMemoryKey))) == NULL) | |
| { | |
| DLOG("IOPCIDevice::%s:%s unable to access device memory\n", __FUNCTION__, getName()); | |
| return kIOReturnError; | |
| } | |
| if (configEntry == NULL) | |
| { | |
| DLOG("IOPCIDevice::%s:%s NULL config entry\n", __FUNCTION__, getName()); | |
| return kIOReturnError; | |
| } | |
| for (unsigned int i = 0; i < deviceMemoryArray->getCount(); i++) | |
| { | |
| static const uint8_t barRegisters[kIOPCIRangeExpansionROM + 1] = { | |
| kIOPCIConfigBaseAddress0, kIOPCIConfigBaseAddress1, kIOPCIConfigBaseAddress2, | |
| kIOPCIConfigBaseAddress3, kIOPCIConfigBaseAddress4, kIOPCIConfigBaseAddress5, | |
| kIOPCIConfigExpansionROMBase | |
| }; | |
| IOMemoryDescriptor *memoryDescriptor = OSDynamicCast(IOMemoryDescriptor, deviceMemoryArray->getObject(static_cast<uint32_t>(i))); | |
| if (memoryDescriptor == NULL) | |
| { | |
| continue; | |
| } | |
| IOPCIAddressSpace addressSpace; | |
| addressSpace.bits = static_cast<uint32_t>(memoryDescriptor->getTag()); | |
| if (addressSpace.s.registerNum == barRegisters[barIndex]) | |
| { | |
| IOPCIRange *range = configEntry->ranges[barIndex]; | |
| *memoryIndex = i; | |
| *barSize = range->size; | |
| if (addressSpace.s.space == kIOPCIIOSpace) | |
| { | |
| *barType = kPCIBARTypeIO; | |
| } | |
| else if (range->flags & kIOPCIRangeFlagBar64) | |
| { | |
| *barType = (addressSpace.s.prefetch) ? kPCIBARTypeM64PF : kPCIBARTypeM64; | |
| } | |
| else | |
| { | |
| *barType = (addressSpace.s.prefetch) ? kPCIBARTypeM32PF : kPCIBARTypeM32; | |
| } | |
| result = kIOReturnSuccess; | |
| break; | |
| } | |
| } | |
| return result; | |
| } | |
| #pragma mark Interrupts Allocation | |
| kern_return_t | |
| IMPL(IOPCIDevice, ConfigureInterrupts) | |
| { | |
| return configureInterrupts(interruptType, numRequired, numRequested, options); | |
| } | |
| #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)); | |
| } |