/* * Copyright (c) 1998-2006 Apple Computer, Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. The rights granted to you under the License * may not be used to create, or enable the creation or redistribution of, * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. * * The 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, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ #include #include #include #include #include #include #include #include #include #if __arm64__ typedef UInt64 dtptr_t; #else typedef UInt32 dtptr_t; #endif #include extern "C" { int IODTGetLoaderInfo( const char *key, void **infoAddr, int *infosize ); void IODTFreeLoaderInfo( const char *key, void *infoAddr, int infoSize ); int IODTGetDefault(const char *key, void *infoAddr, unsigned int infoSize ); } #include #define IODTSUPPORTDEBUG 0 struct IODTPersistent { IODTCompareAddressCellFunc compareFunc; }; struct IODTResolvers { unsigned int alloc; unsigned int count; IOLock * lock; IODTPersistent * resolvers; }; const IORegistryPlane * gIODTPlane; static OSArray * gIODTPHandles; static OSArray * gIODTPHandleMap; static IODTResolvers * gIODTResolvers; const OSSymbol * gIODTNameKey; const OSSymbol * gIODTUnitKey; const OSSymbol * gIODTCompatibleKey; const OSSymbol * gIODTTypeKey; const OSSymbol * gIODTModelKey; const OSSymbol * gIODTBridgeModelKey; const OSSymbol * gIODTTargetTypeKey; const OSSymbol * gIODTSizeCellKey; const OSSymbol * gIODTAddressCellKey; const OSSymbol * gIODTRangeKey; const OSSymbol * gIODTPersistKey; const OSSymbol * gIODTDefaultInterruptController; const OSSymbol * gIODTAAPLInterruptsKey; const OSSymbol * gIODTPHandleKey; const OSSymbol * gIODTInterruptCellKey; const OSSymbol * gIODTInterruptParentKey; const OSSymbol * gIODTNWInterruptMappingKey; const OSData * gIODTAssociatedServiceKey; OSDictionary * gIODTSharedInterrupts; static IORegistryEntry * MakeReferenceTable( DTEntry dtEntry, bool copy ); static void AddPHandle( IORegistryEntry * regEntry ); static void FreePhysicalMemory( vm_offset_t * range ); static bool IODTMapInterruptsSharing( IORegistryEntry * regEntry, OSDictionary * allInts ); // FIXME: Implementation of this function is hidden from the static analyzer. // The analyzer doesn't know that the registry holds retains, and gets confused // about releases after calls to 'attachToParent'. // Feel free to remove the #ifndef and address the warning! #ifndef __clang_analyzer__ IORegistryEntry * IODeviceTreeAlloc( void * dtTop ) { IORegistryEntry * parent; IORegistryEntry * child; IORegistryIterator * regIter; OpaqueDTEntryIterator iter; DTEntry dtChild; DTEntry mapEntry; OSArray * stack; OSData * prop; OSDictionary * allInts; vm_offset_t * dtMap; unsigned int propSize; bool intMap; bool foundDTNode; bool freeDT; char exBootArg[64]; const char * found; gIODTPlane = IORegistryEntry::makePlane( kIODeviceTreePlane ); gIODTNameKey = OSSymbol::withCStringNoCopy( "name" ); gIODTUnitKey = OSSymbol::withCStringNoCopy( "AAPL,unit-string" ); gIODTCompatibleKey = OSSymbol::withCStringNoCopy( "compatible" ); gIODTTypeKey = OSSymbol::withCStringNoCopy( "device_type" ); gIODTModelKey = OSSymbol::withCStringNoCopy( "model" ); gIODTBridgeModelKey = OSSymbol::withCStringNoCopy( "bridge-model" ); gIODTTargetTypeKey = OSSymbol::withCStringNoCopy( "target-type" ); gIODTSizeCellKey = OSSymbol::withCStringNoCopy( "#size-cells" ); gIODTAddressCellKey = OSSymbol::withCStringNoCopy( "#address-cells" ); gIODTRangeKey = OSSymbol::withCStringNoCopy( "ranges" ); gIODTPersistKey = OSSymbol::withCStringNoCopy( "IODTPersist" ); gIODTAssociatedServiceKey = OSData::withBytesNoCopy((void *) kIODTAssociatedServiceKey, sizeof(kIODTAssociatedServiceKey)); assert( gIODTPlane && gIODTCompatibleKey && gIODTTypeKey && gIODTModelKey && gIODTSizeCellKey && gIODTAddressCellKey && gIODTRangeKey && gIODTPersistKey ); gIODTDefaultInterruptController = OSSymbol::withCStringNoCopy("IOPrimaryInterruptController"); gIODTNWInterruptMappingKey = OSSymbol::withCStringNoCopy("IONWInterrupts"); gIODTAAPLInterruptsKey = OSSymbol::withCStringNoCopy("AAPL,interrupts"); gIODTPHandleKey = OSSymbol::withCStringNoCopy("AAPL,phandle"); gIODTInterruptParentKey = OSSymbol::withCStringNoCopy("interrupt-parent"); gIODTPHandles = OSArray::withCapacity( 1 ); gIODTPHandleMap = OSArray::withCapacity( 1 ); gIODTResolvers = zalloc_permanent_type(IODTResolvers); gIODTResolvers->count = 0; gIODTResolvers->alloc = 2; gIODTResolvers->resolvers = IONewZero(IODTPersistent, gIODTResolvers->alloc); gIODTResolvers->lock = IOLockAlloc(); if (!PE_parse_boot_argn("exp", exBootArg, sizeof(exBootArg))) { exBootArg[0] = '\0'; } gIODTInterruptCellKey = OSSymbol::withCStringNoCopy("#interrupt-cells"); assert( gIODTDefaultInterruptController && gIODTNWInterruptMappingKey && gIODTAAPLInterruptsKey && gIODTPHandleKey && gIODTInterruptParentKey && gIODTPHandles && gIODTPHandleMap && gIODTInterruptCellKey && gIODTResolvers && gIODTResolvers->lock && gIODTResolvers->resolvers ); foundDTNode = (kSuccess == SecureDTLookupEntry( NULL, "/chosen/memory-map", &mapEntry )) && (kSuccess == SecureDTGetProperty( mapEntry, "DeviceTree", (void const **) &dtMap, &propSize )) && ((2 * sizeof(uint32_t)) == propSize); freeDT = foundDTNode && !SecureDTIsLockedDown(); parent = MakeReferenceTable((DTEntry)dtTop, freeDT ); stack = OSArray::withObjects((const OSObject **) &parent, 1, 10 ); SecureDTInitEntryIterator((DTEntry)dtTop, &iter ); do { parent = (IORegistryEntry *)stack->getObject( stack->getCount() - 1); //parent->release(); stack->removeObject( stack->getCount() - 1); while (kSuccess == SecureDTIterateEntries( &iter, &dtChild)) { child = MakeReferenceTable( dtChild, freeDT ); child->attachToParent( parent, gIODTPlane); AddPHandle( child ); // E.g. exp=sgx:3 or exp=sgx:3,5 if ((found = strnstr(exBootArg, child->getName(), sizeof(exBootArg)))) { child->setProperty(gIOExclaveAssignedKey, kOSBooleanTrue); uint32_t ep = 0; uint32_t edk_ep = 0; found += strlen(child->getName()); if (':' == *found) { char *end; ep = (uint32_t) strtol(found + 1, &end, 0); // Check for optional edk endpoint if (',' == *end) { edk_ep = (uint32_t) strtol(end + 1, &end, 0); child->setProperty("exclave-edk-endpoint", &edk_ep, sizeof(edk_ep)); } } child->setProperty("exclave-endpoint", &ep, sizeof(ep)); } if (kSuccess == SecureDTEnterEntry( &iter, dtChild)) { stack->setObject( parent); parent = child; } // only registry holds retain child->release(); } } while (stack->getCount() && (kSuccess == SecureDTExitEntry( &iter, &dtChild))); stack->release(); assert(kSuccess != SecureDTExitEntry(&iter, &dtChild)); // parent is now root of the created tree // make root name first compatible entry (purely cosmetic) if ((prop = (OSData *) parent->getProperty( gIODTCompatibleKey))) { parent->setName( parent->getName(), gIODTPlane ); parent->setName((const char *) prop->getBytesNoCopy()); } // attach tree to meta root parent->attachToParent( IORegistryEntry::getRegistryRoot(), gIODTPlane); parent->release(); if (freeDT) { // free original device tree SecureDTInit(NULL, 0); IODTFreeLoaderInfo( "DeviceTree", (void *)dtMap[0], (int) round_page(dtMap[1])); } // adjust tree gIODTSharedInterrupts = OSDictionary::withCapacity(4); allInts = OSDictionary::withCapacity(4); intMap = false; regIter = IORegistryIterator::iterateOver( gIODTPlane, kIORegistryIterateRecursively ); assert( regIter && allInts && gIODTSharedInterrupts ); if (regIter && allInts && gIODTSharedInterrupts) { while ((child = regIter->getNextObject())) { IODTMapInterruptsSharing( child, allInts ); if (!intMap && child->getProperty( gIODTInterruptParentKey)) { intMap = true; } if (!strcmp("sep", child->getName()) || !strcmp("aop", child->getName()) || !strcmp("disp0", child->getName())) { uint32_t aotFlags = 1; child->setProperty("aot-power", &aotFlags, sizeof(aotFlags)); } } regIter->release(); } #if IODTSUPPORTDEBUG parent->setProperty("allInts", allInts); parent->setProperty("sharedInts", gIODTSharedInterrupts); regIter = IORegistryIterator::iterateOver( gIODTPlane, kIORegistryIterateRecursively ); if (regIter) { while ((child = regIter->getNextObject())) { OSArray * array = OSDynamicCast(OSArray, child->getProperty( gIOInterruptSpecifiersKey )); for (UInt32 i = 0; array && (i < array->getCount()); i++) { IOOptionBits options; IOReturn ret = IODTGetInterruptOptions( child, i, &options ); if ((ret != kIOReturnSuccess) || options) { IOLog("%s[%ld] %ld (%x)\n", child->getName(), i, options, ret); } } } regIter->release(); } #endif allInts->release(); if (intMap) { // set a key in the root to indicate we found NW interrupt mapping parent->setProperty( gIODTNWInterruptMappingKey, (OSObject *) gIODTNWInterruptMappingKey ); } return parent; } #endif int IODTGetLoaderInfo( const char *key, void **infoAddr, int *infoSize ) { IORegistryEntry *chosen; OSData *propObj; dtptr_t *propPtr; unsigned int propSize; int ret = -1; chosen = IORegistryEntry::fromPath( "/chosen/memory-map", gIODTPlane ); if (chosen == NULL) { return -1; } propObj = OSDynamicCast( OSData, chosen->getProperty(key)); if (propObj == NULL) { goto cleanup; } propSize = propObj->getLength(); if (propSize != (2 * sizeof(dtptr_t))) { goto cleanup; } propPtr = (dtptr_t *)propObj->getBytesNoCopy(); if (propPtr == NULL) { goto cleanup; } *infoAddr = (void *)(uintptr_t) (propPtr[0]); *infoSize = (int) (propPtr[1]); ret = 0; cleanup: chosen->release(); return ret; } void IODTFreeLoaderInfo( const char *key, void *infoAddr, int infoSize ) { vm_offset_t range[2]; IORegistryEntry *chosen; range[0] = (vm_offset_t)infoAddr; range[1] = (vm_offset_t)infoSize; FreePhysicalMemory( range ); if (key != NULL) { chosen = IORegistryEntry::fromPath( "/chosen/memory-map", gIODTPlane ); if (chosen != NULL) { chosen->removeProperty(key); chosen->release(); } } } int IODTGetDefault(const char *key, void *infoAddr, unsigned int infoSize ) { IORegistryEntry *defaults; OSData *defaultObj; unsigned int defaultSize; defaults = IORegistryEntry::fromPath( "/defaults", gIODTPlane ); if (defaults == NULL) { return -1; } defaultObj = OSDynamicCast( OSData, defaults->getProperty(key)); if (defaultObj == NULL) { defaults->release(); return -1; } defaultSize = defaultObj->getLength(); if (defaultSize > infoSize) { defaults->release(); return -1; } memcpy( infoAddr, defaultObj->getBytesNoCopy(), defaultSize ); defaults->release(); return 0; } static void FreePhysicalMemory( vm_offset_t * range ) { vm_offset_t virt; virt = ml_static_ptovirt( range[0] ); if (virt) { ml_static_mfree( virt, range[1] ); } } static IORegistryEntry * MakeReferenceTable( DTEntry dtEntry, bool copy ) { IORegistryEntry *regEntry; OSDictionary *propTable; const OSSymbol *nameKey; OSData *data; const OSSymbol *sym; OpaqueDTPropertyIterator dtIter; void const *prop; unsigned int propSize; char const *name; char location[32]; bool noLocation = true; bool kernelOnly; regEntry = new IOService; if (regEntry && (false == regEntry->init())) { regEntry->release(); regEntry = NULL; } if (regEntry && (kSuccess == SecureDTInitPropertyIterator( dtEntry, &dtIter))) { kernelOnly = (kSuccess == SecureDTGetProperty(dtEntry, "kernel-only", &prop, &propSize)); propTable = regEntry->getPropertyTable(); while (kSuccess == SecureDTIterateProperties( &dtIter, &name)) { if (kSuccess != SecureDTGetProperty( dtEntry, name, &prop, &propSize )) { continue; } if (copy) { nameKey = OSSymbol::withCString(name); data = OSData::withBytes(prop, propSize); } else { nameKey = OSSymbol::withCStringNoCopy(name); /* There is no OSDataConst or other way to indicate * that the OSData is actually immutable. But CTRR * will catch any write attempts. */ data = OSData::withBytesNoCopy((void**)(uintptr_t)prop, propSize); } assert( nameKey && data ); #if DEVELOPMENT || DEBUG #pragma unused(kernelOnly) #else if (kernelOnly) { data->setSerializable(false); } #endif propTable->setObject( nameKey, data); data->release(); nameKey->release(); if (nameKey == gIODTNameKey) { if (copy) { sym = OSSymbol::withCString((const char *) prop); } else { sym = OSSymbol::withCStringNoCopy((const char *) prop); } regEntry->setName( sym ); sym->release(); } else if (nameKey == gIODTUnitKey) { // all OF strings are null terminated... except this one if (propSize >= (int) sizeof(location)) { propSize = sizeof(location) - 1; } strncpy( location, (const char *) prop, propSize ); location[propSize] = 0; regEntry->setLocation( location ); propTable->removeObject( gIODTUnitKey ); noLocation = false; } else if (noLocation && (!strncmp(name, "reg", sizeof("reg")))) { // default location - override later snprintf(location, sizeof(location), "%X", *((uint32_t *) prop)); regEntry->setLocation( location ); } } } return regEntry; } static void AddPHandle( IORegistryEntry * regEntry ) { OSData * data; if (regEntry->getProperty( gIODTInterruptCellKey) && (data = OSDynamicCast( OSData, regEntry->getProperty( gIODTPHandleKey )))) { // a possible interrupt-parent gIODTPHandles->setObject( data ); gIODTPHandleMap->setObject( regEntry ); } } static LIBKERN_RETURNS_NOT_RETAINED IORegistryEntry * FindPHandle( UInt32 phandle ) { OSData *data; IORegistryEntry *regEntry = NULL; int i; for (i = 0; (data = (OSData *)gIODTPHandles->getObject( i )); i++) { if (phandle == *((UInt32 *)data->getBytesNoCopy())) { regEntry = (IORegistryEntry *) gIODTPHandleMap->getObject( i ); break; } } return regEntry; } static bool GetUInt32( IORegistryEntry * regEntry, const OSSymbol * name, UInt32 * value ) { OSObject * obj; OSData * data; bool result; if (!(obj = regEntry->copyProperty(name))) { return false; } result = ((data = OSDynamicCast(OSData, obj)) && (sizeof(UInt32) == data->getLength())); if (result) { *value = *((UInt32 *) data->getBytesNoCopy()); } obj->release(); return result; } static IORegistryEntry * IODTFindInterruptParent( IORegistryEntry * regEntry, IOItemCount index ) { IORegistryEntry * parent; UInt32 phandle; OSData * data; unsigned int len; if ((data = OSDynamicCast( OSData, regEntry->getProperty( gIODTInterruptParentKey ))) && (sizeof(UInt32) <= (len = data->getLength()))) { if (((index + 1) * sizeof(UInt32)) > len) { index = 0; } phandle = ((UInt32 *) data->getBytesNoCopy())[index]; parent = FindPHandle( phandle ); } else if (NULL == regEntry->getProperty( "interrupt-controller")) { parent = regEntry->getParentEntry( gIODTPlane); } else { parent = NULL; } return parent; } const OSSymbol * IODTInterruptControllerName( IORegistryEntry * regEntry ) { const OSSymbol *sym; UInt32 phandle; bool ok; char buf[48]; ok = GetUInt32( regEntry, gIODTPHandleKey, &phandle); assert( ok ); if (ok) { snprintf(buf, sizeof(buf), "IOInterruptController%08X", (uint32_t)phandle); sym = OSSymbol::withCString( buf ); } else { sym = NULL; } return sym; } #define unexpected(a) { kprintf("unexpected %s:%d\n", __FILE__, __LINE__); a; } static void IODTGetICellCounts( IORegistryEntry * regEntry, UInt32 * iCellCount, UInt32 * aCellCount) { if (!GetUInt32( regEntry, gIODTInterruptCellKey, iCellCount)) { unexpected( *iCellCount = 1 ); } if (!GetUInt32( regEntry, gIODTAddressCellKey, aCellCount)) { *aCellCount = 0; } } static UInt32 IODTMapOneInterrupt( IORegistryEntry * regEntry, UInt32 * intSpec, UInt32 index, LIBKERN_RETURNS_RETAINED OSData ** spec, LIBKERN_RETURNS_RETAINED const OSSymbol ** controller ) { IORegistryEntry *parent = NULL; OSData *data; UInt32 *addrCmp; UInt32 *maskCmp; UInt32 *map; UInt32 *endMap; UInt32 acells, icells, pacells, picells, cell; UInt32 i, original_icells; bool cmp, ok = false; parent = IODTFindInterruptParent( regEntry, index ); IODTGetICellCounts( parent, &icells, &acells ); addrCmp = NULL; if (acells) { data = OSDynamicCast( OSData, regEntry->getProperty( "reg" )); if (data && (data->getLength() >= (acells * sizeof(UInt32)))) { addrCmp = (UInt32 *) data->getBytesNoCopy(); } } original_icells = icells; regEntry = parent; do { #if IODTSUPPORTDEBUG kprintf("IODTMapOneInterrupt: current regEntry name %s\n", regEntry->getName()); kprintf("acells - icells: "); for (i = 0; i < acells; i++) { kprintf("0x%08X ", addrCmp[i]); } kprintf("- "); for (i = 0; i < icells; i++) { kprintf("0x%08X ", intSpec[i]); } kprintf("\n"); #endif if (parent && (data = OSDynamicCast( OSData, regEntry->getProperty( "interrupt-controller")))) { // found a controller - don't want to follow cascaded controllers parent = NULL; *spec = OSData::withBytesNoCopy((void *) intSpec, icells * sizeof(UInt32)); *controller = IODTInterruptControllerName( regEntry ); ok = (*spec && *controller); } else if (parent && (data = OSDynamicCast( OSData, regEntry->getProperty( "interrupt-map")))) { // interrupt-map map = (UInt32 *) data->getBytesNoCopy(); endMap = map + (data->getLength() / sizeof(UInt32)); data = OSDynamicCast( OSData, regEntry->getProperty( "interrupt-map-mask" )); if (data && (data->getLength() >= ((acells + icells) * sizeof(UInt32)))) { maskCmp = (UInt32 *) data->getBytesNoCopy(); } else { maskCmp = NULL; } #if IODTSUPPORTDEBUG if (maskCmp) { kprintf(" maskCmp: "); for (i = 0; i < acells + icells; i++) { if (i == acells) { kprintf("- "); } kprintf("0x%08X ", maskCmp[i]); } kprintf("\n"); kprintf(" masked: "); for (i = 0; i < acells + icells; i++) { if (i == acells) { kprintf("- "); } kprintf("0x%08X ", ((i < acells) ? addrCmp[i] : intSpec[i - acells]) & maskCmp[i]); } kprintf("\n"); } else { kprintf("no maskCmp\n"); } #endif do { #if IODTSUPPORTDEBUG kprintf(" map: "); for (i = 0; i < acells + icells; i++) { if (i == acells) { kprintf("- "); } kprintf("0x%08X ", map[i]); } kprintf("\n"); #endif for (i = 0, cmp = true; cmp && (i < (acells + icells)); i++) { cell = (i < acells) ? addrCmp[i] : intSpec[i - acells]; if (maskCmp) { cell &= maskCmp[i]; } cmp = (cell == map[i]); } map += acells + icells; if (NULL == (parent = FindPHandle( *(map++)))) { unexpected(break); } IODTGetICellCounts( parent, &picells, &pacells ); if (cmp) { addrCmp = map; intSpec = map + pacells; regEntry = parent; } else { map += pacells + picells; } } while (!cmp && (map < endMap)); if (!cmp) { parent = NULL; } } if (parent) { IODTGetICellCounts( parent, &icells, &acells ); regEntry = parent; } } while (parent); return ok ? original_icells : 0; } IOReturn IODTGetInterruptOptions( IORegistryEntry * regEntry, int source, IOOptionBits * options ) { OSArray * controllers; OSArray * specifiers; OSArray * shared; OSObject * spec; OSObject * oneSpec; *options = 0; controllers = OSDynamicCast(OSArray, regEntry->getProperty(gIOInterruptControllersKey)); specifiers = OSDynamicCast(OSArray, regEntry->getProperty(gIOInterruptSpecifiersKey)); if (!controllers || !specifiers) { return kIOReturnNoInterrupt; } shared = (OSArray *) gIODTSharedInterrupts->getObject( (const OSSymbol *) controllers->getObject(source)); if (!shared) { return kIOReturnSuccess; } spec = specifiers->getObject(source); if (!spec) { return kIOReturnNoInterrupt; } for (unsigned int i = 0; (oneSpec = shared->getObject(i)) && (!oneSpec->isEqualTo(spec)); i++) { } if (oneSpec) { *options = kIODTInterruptShared; } return kIOReturnSuccess; } static bool IODTMapInterruptsSharing( IORegistryEntry * regEntry, OSDictionary * allInts ) { IORegistryEntry * parent; OSData * local; OSData * local2; UInt32 * localBits; UInt32 * localEnd; IOItemCount index; OSData * map = NULL; OSObject * oneMap; OSArray * mapped; OSArray * controllerInts; const OSSymbol * controller = NULL; OSArray * controllers; UInt32 skip = 1; bool ok, nw; nw = (NULL == (local = OSDynamicCast( OSData, regEntry->getProperty( gIODTAAPLInterruptsKey)))); if (nw && (NULL == (local = OSDynamicCast( OSData, regEntry->getProperty( "interrupts"))))) { return true; // nothing to see here } if (nw && (parent = regEntry->getParentEntry( gIODTPlane))) { // check for bridges on old world if ((local2 = OSDynamicCast( OSData, parent->getProperty( gIODTAAPLInterruptsKey)))) { local = local2; nw = false; } } localBits = (UInt32 *) local->getBytesNoCopy(); localEnd = localBits + (local->getLength() / sizeof(UInt32)); index = 0; mapped = OSArray::withCapacity( 1 ); controllers = OSArray::withCapacity( 1 ); ok = (mapped && controllers); if (ok) { do { if (nw) { skip = IODTMapOneInterrupt( regEntry, localBits, index, &map, &controller ); if (0 == skip) { IOLog("%s: error mapping interrupt[%d]\n", regEntry->getName(), mapped->getCount()); OSSafeReleaseNULL(map); OSSafeReleaseNULL(controller); break; } } else { map = OSData::withData( local, mapped->getCount() * sizeof(UInt32), sizeof(UInt32)); controller = gIODTDefaultInterruptController; controller->retain(); } index++; localBits += skip; mapped->setObject( map ); controllers->setObject( controller ); if (allInts) { controllerInts = (OSArray *) allInts->getObject( controller ); if (controllerInts) { for (unsigned int i = 0; (oneMap = controllerInts->getObject(i)); i++) { if (map->isEqualTo(oneMap)) { controllerInts = (OSArray *) gIODTSharedInterrupts->getObject( controller ); if (controllerInts) { controllerInts->setObject(map); } else { controllerInts = OSArray::withObjects((const OSObject **) &map, 1, 4 ); if (controllerInts) { gIODTSharedInterrupts->setObject( controller, controllerInts ); controllerInts->release(); } } break; } } if (!oneMap) { controllerInts->setObject(map); } } else { controllerInts = OSArray::withObjects((const OSObject **) &map, 1, 16 ); if (controllerInts) { allInts->setObject( controller, controllerInts ); controllerInts->release(); } } } OSSafeReleaseNULL(map); OSSafeReleaseNULL(controller); } while (localBits < localEnd); } ok &= (localBits == localEnd); if (ok) { // store results ok = regEntry->setProperty( gIOInterruptControllersKey, controllers); ok &= regEntry->setProperty( gIOInterruptSpecifiersKey, mapped); } if (controllers) { controllers->release(); } if (mapped) { mapped->release(); } return ok; } bool IODTMapInterrupts( IORegistryEntry * regEntry ) { return IODTMapInterruptsSharing( regEntry, NULL ); } /* */ static bool CompareKey( OSString * key, const IORegistryEntry * table, const OSSymbol * propName, LIBKERN_RETURNS_RETAINED OSString ** matchingName ) { OSObject *prop; OSData *data; OSString *string; const char *ckey; UInt32 keyLen; UInt32 nlen; const char *names; const char *lastName; bool wild; bool matched; const char *result = NULL; if (NULL == (prop = table->copyProperty( propName ))) { return 0; } if ((data = OSDynamicCast( OSData, prop ))) { names = (const char *) data->getBytesNoCopy(); lastName = names + data->getLength(); } else if ((string = OSDynamicCast( OSString, prop ))) { names = string->getCStringNoCopy(); lastName = names + string->getLength() + 1; } else { names = NULL; } if (names) { ckey = key->getCStringNoCopy(); keyLen = key->getLength(); wild = ('*' == key->getChar( keyLen - 1 )); do { // for each name in the property nlen = (unsigned int) strnlen(names, lastName - names); if (wild) { matched = ((nlen >= (keyLen - 1)) && (0 == strncmp(ckey, names, keyLen - 1))); } else { matched = (keyLen == nlen) && (0 == strncmp(ckey, names, keyLen)); } if (matched) { result = names; } names = names + nlen + 1; } while ((names < lastName) && (false == matched)); } if (result && matchingName) { *matchingName = OSString::withCString( result ); } if (prop) { prop->release(); } return result != NULL; } bool IODTCompareNubName( const IORegistryEntry * regEntry, OSString * name, OSString ** matchingName ) { bool matched; matched = CompareKey( name, regEntry, gIODTNameKey, matchingName) || CompareKey( name, regEntry, gIODTCompatibleKey, matchingName) || CompareKey( name, regEntry, gIODTTypeKey, matchingName) || CompareKey( name, regEntry, gIODTModelKey, matchingName); return matched; } bool IODTCompareNubName( const IORegistryEntry * regEntry, OSString * name, OSSharedPtr& matchingName ) { OSString* matchingNameRaw = NULL; bool result = IODTCompareNubName(regEntry, name, &matchingNameRaw); matchingName.reset(matchingNameRaw, OSNoRetain); return result; } bool IODTMatchNubWithKeys( IORegistryEntry * regEntry, const char * keys ) { OSObject *obj; bool result = false; obj = OSUnserialize( keys, NULL ); if (obj) { result = regEntry->compareNames( obj ); obj->release(); } #if DEBUG else { IOLog("Couldn't unserialize %s\n", keys ); } #endif return result; } LIBKERN_RETURNS_RETAINED OSCollectionIterator * IODTFindMatchingEntries( IORegistryEntry * from, IOOptionBits options, const char * keys ) { OSSet *result = NULL; IORegistryEntry *next; IORegistryIterator *iter; OSCollectionIterator *cIter; bool cmp; bool minus = options & kIODTExclusive; iter = IORegistryIterator::iterateOver( from, gIODTPlane, (options & kIODTRecursive) ? kIORegistryIterateRecursively : 0 ); if (iter) { do { if (result) { result->release(); } result = OSSet::withCapacity( 3 ); if (!result) { break; } iter->reset(); while ((next = iter->getNextObject())) { // Look for existence of a debug property to skip if (next->propertyExists("AAPL,ignore")) { continue; } if (next->propertyHasValue(gIODTTypeKey, gIODTAssociatedServiceKey)) { continue; } if (keys) { cmp = IODTMatchNubWithKeys( next, keys ); if ((minus && (false == cmp)) || ((false == minus) && (false != cmp))) { result->setObject( next); } } else { result->setObject( next); } } } while (!iter->isValid()); iter->release(); } cIter = OSCollectionIterator::withCollection( result); if (result) { result->release(); } return cIter; } void IODTSetResolving( IORegistryEntry * regEntry, IODTCompareAddressCellFunc compareFunc, IODTNVLocationFunc locationFunc __unused ) { IODTPersistent * entry; IODTPersistent * newResolvers; OSNumber * num; unsigned int index; IOLockLock(gIODTResolvers->lock); entry = gIODTResolvers->resolvers; for (index = 0; index < gIODTResolvers->count; index++) { if (compareFunc == entry->compareFunc) { break; } entry++; } if (index == gIODTResolvers->count) { if (gIODTResolvers->alloc == gIODTResolvers->count) { if (__improbable(os_mul_overflow(gIODTResolvers->alloc, 2, &gIODTResolvers->alloc))) { panic("IODTSetResolving - gIODTResolvers alloc overflows"); } newResolvers = IONewZero(IODTPersistent, gIODTResolvers->alloc); if (__improbable(!newResolvers)) { panic("IODTSetResolving - could not allocate new resolvers"); } bcopy(gIODTResolvers->resolvers, newResolvers, sizeof(gIODTResolvers->resolvers[0]) * gIODTResolvers->count); IODelete(gIODTResolvers->resolvers, IODTPersistent, gIODTResolvers->count); gIODTResolvers->resolvers = newResolvers; } entry = &gIODTResolvers->resolvers[gIODTResolvers->count]; entry->compareFunc = compareFunc; gIODTResolvers->count++; } IOLockUnlock(gIODTResolvers->lock); num = OSNumber::withNumber(index, 32); regEntry->setProperty(gIODTPersistKey, num); OSSafeReleaseNULL(num); return; } #if defined(__arm64__) static SInt64 DefaultCompare( UInt32 cellCount, UInt32 left[], UInt32 right[] ) { SInt64 diff = 0; if (cellCount == 2) { diff = IOPhysical32(left[1], left[0]) - IOPhysical32(right[1], right[0]); } else if (cellCount == 1) { diff = (left[0] - right[0]); } else { panic("DefaultCompare only knows how to handle 1 or 2 cells."); } return diff; } #elif defined(__i386__) || defined(__x86_64__) static SInt32 DefaultCompare( UInt32 cellCount, UInt32 left[], UInt32 right[] ) { cellCount--; return left[cellCount] - right[cellCount]; } #else #error Unknown architecture. #endif static void AddLengthToCells( UInt32 numCells, UInt32 *cells, UInt64 offset) { if (numCells == 1) { cells[0] += (UInt32)offset; } else { #if defined(__arm64__) UInt64 sum = cells[numCells - 2] + offset; cells[numCells - 2] = (UInt32)sum; if (sum > UINT32_MAX) { cells[numCells - 1] += (UInt32)(sum >> 32); } #else UInt64 sum = cells[numCells - 1] + offset; cells[numCells - 1] = (UInt32)sum; if (sum > UINT32_MAX) { cells[numCells - 2] += (UInt32)(sum >> 32); } #endif } } static IOPhysicalAddress CellsValue( UInt32 numCells, UInt32 *cells) { if (numCells == 1) { return IOPhysical32( 0, cells[0] ); } else { #if defined(__arm64__) || defined(arm) return IOPhysical32( cells[numCells - 1], cells[numCells - 2] ); #else return IOPhysical32( cells[numCells - 2], cells[numCells - 1] ); #endif } } void IODTGetCellCounts( IORegistryEntry * regEntry, UInt32 * sizeCount, UInt32 * addressCount) { if (!GetUInt32( regEntry, gIODTSizeCellKey, sizeCount)) { *sizeCount = 1; } if (!GetUInt32( regEntry, gIODTAddressCellKey, addressCount)) { *addressCount = 2; } return; } // Given addr & len cells from our child, find it in our ranges property, then // look in our parent to resolve the base of the range for us. // Range[]: child-addr our-addr child-len // #cells: child ours child bool IODTResolveAddressCell( IORegistryEntry * startEntry, UInt32 cellsIn[], IOPhysicalAddress * phys, IOPhysicalLength * lenOut ) { IORegistryEntry * parent = NULL; IORegistryEntry * regEntry; OSData * prop; OSNumber * num; unsigned int index; // cells in addresses at regEntry UInt32 sizeCells, addressCells; // cells in addresses below regEntry UInt32 childSizeCells, childAddressCells; UInt32 childCells; UInt32 cell[8], propLen; UInt64 offset = 0; UInt32 endCell[8]; UInt32 *range; UInt32 *lookRange; UInt32 *startRange; UInt32 *endRanges; bool ok = true; SInt64 diff, diff2, endDiff; UInt64 len, rangeLen; IODTCompareAddressCellFunc compare; regEntry = startEntry; regEntry->retain(); IODTGetCellCounts( regEntry, &childSizeCells, &childAddressCells ); childCells = childAddressCells + childSizeCells; if (childCells > sizeof(cell) / sizeof(cell[0])) { panic("IODTResolveAddressCell: Invalid device tree (%u,%u)", (uint32_t)childAddressCells, (uint32_t)childSizeCells); } bcopy( cellsIn, cell, sizeof(UInt32) * childCells ); *lenOut = CellsValue( childSizeCells, cellsIn + childAddressCells ); do{ prop = OSDynamicCast( OSData, regEntry->getProperty( gIODTRangeKey )); if (NULL == prop) { /* end of the road */ *phys = CellsValue( childAddressCells, cell ); *phys += offset; break; } parent = regEntry->copyParentEntry( gIODTPlane ); IODTGetCellCounts( parent, &sizeCells, &addressCells ); if ((propLen = prop->getLength())) { // search startRange = (UInt32 *) prop->getBytesNoCopy(); range = startRange; endRanges = range + (propLen / sizeof(UInt32)); compare = NULL; num = OSDynamicCast(OSNumber, regEntry->getProperty(gIODTPersistKey)); if (num) { IOLockLock(gIODTResolvers->lock); index = num->unsigned32BitValue(); if (index < gIODTResolvers->count) { compare = gIODTResolvers->resolvers[index].compareFunc; } IOLockUnlock(gIODTResolvers->lock); } if (!compare && (addressCells == childAddressCells)) { compare = DefaultCompare; } if (!compare) { panic("There is no mixed comparison function yet..."); } for (ok = false; range < endRanges; range += (childCells + addressCells)) { // is cell start within range? diff = (*compare)( childAddressCells, cell, range ); if (childAddressCells > sizeof(endCell) / sizeof(endCell[0])) { panic("IODTResolveAddressCell: Invalid device tree (%u)", (uint32_t)childAddressCells); } bcopy(range, endCell, childAddressCells * sizeof(UInt32)); rangeLen = CellsValue(childSizeCells, range + childAddressCells + addressCells); AddLengthToCells(childAddressCells, endCell, rangeLen); diff2 = (*compare)( childAddressCells, cell, endCell ); // if start of cell < start of range, or end of range >= start of cell, skip if ((diff < 0) || (diff2 >= 0)) { continue; } len = CellsValue(childSizeCells, cell + childAddressCells); ok = (0 == len); if (!ok) { // search for cell end bcopy(cell, endCell, childAddressCells * sizeof(UInt32)); AddLengthToCells(childAddressCells, endCell, len - 1); for (lookRange = startRange; lookRange < endRanges; lookRange += (childCells + addressCells)) { // make sure end of cell >= range start endDiff = (*compare)( childAddressCells, endCell, lookRange ); if (endDiff < 0) { continue; } UInt64 rangeStart = CellsValue(addressCells, range + childAddressCells); UInt64 lookRangeStart = CellsValue(addressCells, lookRange + childAddressCells); if ((endDiff - len + 1 + lookRangeStart) == (diff + rangeStart)) { ok = true; break; } } if (!ok) { continue; } } offset += diff; break; } if (addressCells + sizeCells > sizeof(cell) / sizeof(cell[0])) { panic("IODTResolveAddressCell: Invalid device tree (%u, %u)", (uint32_t)addressCells, (uint32_t)sizeCells); } // Get the physical start of the range from our parent bcopy( range + childAddressCells, cell, sizeof(UInt32) * addressCells ); bzero( cell + addressCells, sizeof(UInt32) * sizeCells ); } /* else zero length range => pass thru to parent */ OSSafeReleaseNULL(regEntry); regEntry = parent; parent = NULL; childSizeCells = sizeCells; childAddressCells = addressCells; childCells = childAddressCells + childSizeCells; }while (ok && regEntry); OSSafeReleaseNULL(regEntry); return ok; } OSArray * IODTResolveAddressing( IORegistryEntry * regEntry, const char * addressPropertyName, IODeviceMemory * parent ) { IORegistryEntry *parentEntry; OSData *addressProperty; UInt32 sizeCells, addressCells, cells; int i, num; UInt32 *reg; IOPhysicalAddress phys; IOPhysicalLength len; OSArray *array; array = NULL; do{ parentEntry = regEntry->copyParentEntry( gIODTPlane ); addressProperty = (OSData *) regEntry->getProperty( addressPropertyName ); if ((NULL == addressProperty) || (NULL == parentEntry)) { break; } IODTGetCellCounts( parentEntry, &sizeCells, &addressCells ); if (0 == sizeCells) { break; } cells = sizeCells + addressCells; reg = (UInt32 *) addressProperty->getBytesNoCopy(); num = addressProperty->getLength() / (4 * cells); array = OSArray::withCapacity( 1 ); if (NULL == array) { break; } for (i = 0; i < num; i++) { if (IODTResolveAddressCell( parentEntry, reg, &phys, &len )) { IODeviceMemory *range; range = NULL; if (parent) { range = IODeviceMemory::withSubRange( parent, phys - parent->getPhysicalSegment(0, NULL, kIOMemoryMapperNone), len ); } if (NULL == range) { range = IODeviceMemory::withRange( phys, len ); } if (range) { array->setObject( range ); OSSafeReleaseNULL(range); } } reg += cells; } regEntry->setProperty( gIODeviceMemoryKey, array); array->release(); /* ??? */ }while (false); OSSafeReleaseNULL(parentEntry); return array; } OSData * IODTFindSlotName( IORegistryEntry * regEntry, UInt32 deviceNumber ) { IORegistryEntry *parent; OSData *data; OSData *ret = NULL; UInt32 *bits; UInt32 i; UInt32 nlen; char *names; char *lastName; UInt32 mask; data = (OSData *) regEntry->getProperty("AAPL,slot-name"); if (data) { return data; } do{ parent = regEntry->copyParentEntry( gIODTPlane ); if (!parent) { break; } data = OSDynamicCast( OSData, parent->getProperty("slot-names")); if (!data) { break; } if (data->getLength() <= 4) { break; } bits = (UInt32 *) data->getBytesNoCopy(); mask = *bits; if ((0 == (mask & (1 << deviceNumber)))) { break; } names = (char *)(bits + 1); lastName = names + (data->getLength() - 4); for (i = 0; (i <= deviceNumber) && (names < lastName); i++) { if (mask & (1 << i)) { nlen = 1 + ((unsigned int) strnlen(names, lastName - names)); if (i == deviceNumber) { data = OSData::withBytesNoCopy(names, nlen); if (data) { regEntry->setProperty("AAPL,slot-name", data); ret = data; data->release(); } } else { names += nlen; } } } }while (false); OSSafeReleaseNULL(parent); return ret; } extern "C" IOReturn IONDRVLibrariesInitialize( IOService * provider ) { return kIOReturnUnsupported; }