1 /*
2  * Copyright (c) 1998-2006 Apple Computer, Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 
29 #include <IOKit/IODeviceTreeSupport.h>
30 #include <libkern/c++/OSContainers.h>
31 #include <IOKit/IODeviceMemory.h>
32 #include <IOKit/IOService.h>
33 #include <IOKit/IOCatalogue.h>
34 
35 #include <IOKit/IOLib.h>
36 #include <IOKit/IOKitKeys.h>
37 
38 #include <pexpert/device_tree.h>
39 
40 #if __arm64__
41 typedef UInt64  dtptr_t;
42 #else
43 typedef UInt32  dtptr_t;
44 #endif
45 
46 #include <machine/machine_routines.h>
47 
48 extern "C" {
49 
50 int IODTGetLoaderInfo( const char *key, void **infoAddr, int *infosize );
51 void IODTFreeLoaderInfo( const char *key, void *infoAddr, int infoSize );
52 int IODTGetDefault(const char *key, void *infoAddr, unsigned int infoSize );
53 
54 }
55 
56 #include <IOKit/assert.h>
57 
58 #define IODTSUPPORTDEBUG 0
59 
60 const IORegistryPlane * gIODTPlane;
61 
62 static OSArray *    gIODTPHandles;
63 static OSArray *    gIODTPHandleMap;
64 static OSData  *    gIODTResolvers;
65 
66 const OSSymbol *	gIODTNameKey;
67 const OSSymbol *	gIODTUnitKey;
68 const OSSymbol *	gIODTCompatibleKey;
69 const OSSymbol * 	gIODTTypeKey;
70 const OSSymbol * 	gIODTModelKey;
71 const OSSymbol * 	gIODTTargetTypeKey;
72 
73 const OSSymbol * 	gIODTSizeCellKey;
74 const OSSymbol * 	gIODTAddressCellKey;
75 const OSSymbol * 	gIODTRangeKey;
76 
77 const OSSymbol *	gIODTPersistKey;
78 
79 const OSSymbol *	gIODTDefaultInterruptController;
80 const OSSymbol *	gIODTAAPLInterruptsKey;
81 const OSSymbol *	gIODTPHandleKey;
82 const OSSymbol *	gIODTInterruptCellKey;
83 const OSSymbol *	gIODTInterruptParentKey;
84 const OSSymbol *	gIODTNWInterruptMappingKey;
85 
86 OSDictionary   *	gIODTSharedInterrupts;
87 
88 static IOLock  *    gIODTResolversLock;
89 
90 static IORegistryEntry * MakeReferenceTable( DTEntry dtEntry, bool copy );
91 static void AddPHandle( IORegistryEntry * regEntry );
92 static void FreePhysicalMemory( vm_offset_t * range );
93 static bool IODTMapInterruptsSharing( IORegistryEntry * regEntry, OSDictionary * allInts );
94 
95 IORegistryEntry *
96 IODeviceTreeAlloc( void * dtTop )
97 {
98     IORegistryEntry *		parent;
99     IORegistryEntry *		child;
100     IORegistryIterator *	regIter;
101     OpaqueDTEntryIterator	iter;
102     DTEntry			dtChild;
103     DTEntry			mapEntry;
104     OSArray *			stack;
105     OSData *			prop;
106     OSDictionary *		allInts;
107     vm_offset_t *		dtMap;
108     unsigned int		propSize;
109     bool			intMap;
110     bool			freeDT;
111 
112     gIODTPlane = IORegistryEntry::makePlane( kIODeviceTreePlane );
113 
114     gIODTNameKey 		= OSSymbol::withCStringNoCopy( "name" );
115     gIODTUnitKey 		= OSSymbol::withCStringNoCopy( "AAPL,unit-string" );
116     gIODTCompatibleKey 	= OSSymbol::withCStringNoCopy( "compatible" );
117     gIODTTypeKey 		= OSSymbol::withCStringNoCopy( "device_type" );
118     gIODTModelKey 		= OSSymbol::withCStringNoCopy( "model" );
119     gIODTTargetTypeKey		= OSSymbol::withCStringNoCopy( "target-type" );
120     gIODTSizeCellKey 	= OSSymbol::withCStringNoCopy( "#size-cells" );
121     gIODTAddressCellKey = OSSymbol::withCStringNoCopy( "#address-cells" );
122     gIODTRangeKey 		= OSSymbol::withCStringNoCopy( "ranges" );
123     gIODTPersistKey		= OSSymbol::withCStringNoCopy( "IODTPersist" );
124 
125     assert(    gIODTPlane && gIODTCompatibleKey
126             && gIODTTypeKey && gIODTModelKey
127             && gIODTSizeCellKey && gIODTAddressCellKey && gIODTRangeKey
128             && gIODTPersistKey );
129 
130     gIODTDefaultInterruptController
131 		= OSSymbol::withCStringNoCopy("IOPrimaryInterruptController");
132     gIODTNWInterruptMappingKey
133 		= OSSymbol::withCStringNoCopy("IONWInterrupts");
134 
135     gIODTAAPLInterruptsKey
136 		= OSSymbol::withCStringNoCopy("AAPL,interrupts");
137     gIODTPHandleKey
138 		= OSSymbol::withCStringNoCopy("AAPL,phandle");
139 
140     gIODTInterruptParentKey
141 		= OSSymbol::withCStringNoCopy("interrupt-parent");
142 
143     gIODTPHandles	= OSArray::withCapacity( 1 );
144     gIODTPHandleMap	= OSArray::withCapacity( 1 );
145     gIODTResolvers  = OSData::withCapacity(16);
146 
147     gIODTResolversLock = IOLockAlloc();
148 
149     gIODTInterruptCellKey
150 		= OSSymbol::withCStringNoCopy("#interrupt-cells");
151 
152     assert(    gIODTDefaultInterruptController && gIODTNWInterruptMappingKey
153 	    && gIODTAAPLInterruptsKey
154 	    && gIODTPHandleKey && gIODTInterruptParentKey
155 	    && gIODTPHandles && gIODTPHandleMap && gIODTResolvers && gIODTResolversLock
156             && gIODTInterruptCellKey
157 	 );
158 
159     freeDT = (kSuccess == DTLookupEntry( 0, "/chosen/memory-map", &mapEntry ))
160 	  && (kSuccess == DTGetProperty( mapEntry,
161                 "DeviceTree", (void **) &dtMap, &propSize ))
162 	  && ((2 * sizeof(uint32_t)) == propSize);
163 
164     parent = MakeReferenceTable( (DTEntry)dtTop, freeDT );
165 
166     stack = OSArray::withObjects( (const OSObject **) &parent, 1, 10 );
167     DTInitEntryIterator( (DTEntry)dtTop, &iter );
168 
169     do {
170         parent = (IORegistryEntry *)stack->getObject( stack->getCount() - 1);
171         //parent->release();
172         stack->removeObject( stack->getCount() - 1);
173 
174         while( kSuccess == DTIterateEntries( &iter, &dtChild) ) {
175 
176             child = MakeReferenceTable( dtChild, freeDT );
177             child->attachToParent( parent, gIODTPlane);
178 
179             AddPHandle( child );
180 
181             if( kSuccess == DTEnterEntry( &iter, dtChild)) {
182                 stack->setObject( parent);
183                 parent = child;
184             }
185             // only registry holds retain
186             child->release();
187         }
188 
189     } while( stack->getCount()
190 		&& (kSuccess == DTExitEntry( &iter, &dtChild)));
191 
192     stack->release();
193     assert(kSuccess != DTExitEntry(&iter, &dtChild));
194 
195     // parent is now root of the created tree
196 
197     // make root name first compatible entry (purely cosmetic)
198     if( (prop = (OSData *) parent->getProperty( gIODTCompatibleKey))) {
199         parent->setName( parent->getName(), gIODTPlane );
200         parent->setName( (const char *) prop->getBytesNoCopy() );
201     }
202 
203     // attach tree to meta root
204     parent->attachToParent( IORegistryEntry::getRegistryRoot(), gIODTPlane);
205     parent->release();
206 
207     if( freeDT ) {
208         // free original device tree
209         DTInit(0);
210         IODTFreeLoaderInfo( "DeviceTree",
211 			    (void *)dtMap[0], (int) round_page(dtMap[1]) );
212     }
213 
214     // adjust tree
215 
216     gIODTSharedInterrupts = OSDictionary::withCapacity(4);
217     allInts = OSDictionary::withCapacity(4);
218     intMap = false;
219     regIter = IORegistryIterator::iterateOver( gIODTPlane,
220 						kIORegistryIterateRecursively );
221     assert( regIter && allInts && gIODTSharedInterrupts );
222     if( regIter && allInts && gIODTSharedInterrupts ) {
223         while( (child = regIter->getNextObject())) {
224             IODTMapInterruptsSharing( child, allInts );
225             if( !intMap && child->getProperty( gIODTInterruptParentKey))
226                 intMap = true;
227 
228         }
229         regIter->release();
230     }
231 
232 #if IODTSUPPORTDEBUG
233     parent->setProperty("allInts", allInts);
234     parent->setProperty("sharedInts", gIODTSharedInterrupts);
235 
236     regIter = IORegistryIterator::iterateOver( gIODTPlane,
237 						kIORegistryIterateRecursively );
238     if (regIter) {
239         while( (child = regIter->getNextObject())) {
240 	    OSArray *
241 	    array = OSDynamicCast(OSArray, child->getProperty( gIOInterruptSpecifiersKey ));
242 	    for( UInt32 i = 0; array && (i < array->getCount()); i++)
243 	    {
244 		IOOptionBits options;
245 		IOReturn ret = IODTGetInterruptOptions( child, i, &options );
246 		if( (ret != kIOReturnSuccess) || options)
247 		    IOLog("%s[%ld] %ld (%x)\n", child->getName(), i, options, ret);
248 	    }
249 	}
250         regIter->release();
251     }
252 #endif
253 
254     allInts->release();
255 
256     if( intMap)
257         // set a key in the root to indicate we found NW interrupt mapping
258         parent->setProperty( gIODTNWInterruptMappingKey,
259                 (OSObject *) gIODTNWInterruptMappingKey );
260 
261     return( parent);
262 }
263 
264 int IODTGetLoaderInfo( const char *key, void **infoAddr, int *infoSize )
265 {
266     IORegistryEntry		*chosen;
267     OSData				*propObj;
268     dtptr_t				*propPtr;
269     unsigned int		propSize;
270     int ret = -1;
271 
272     chosen = IORegistryEntry::fromPath( "/chosen/memory-map", gIODTPlane );
273     if ( chosen == 0 ) return -1;
274 
275     propObj = OSDynamicCast( OSData, chosen->getProperty(key) );
276     if ( propObj == 0 ) goto cleanup;
277 
278     propSize = propObj->getLength();
279     if ( propSize != (2 * sizeof(dtptr_t)) ) goto cleanup;
280 
281     propPtr = (dtptr_t *)propObj->getBytesNoCopy();
282     if ( propPtr == 0 ) goto cleanup;
283 
284     *infoAddr = (void *)(uintptr_t) (propPtr[0]);
285     *infoSize = (int)               (propPtr[1]);
286 
287     ret = 0;
288 
289 cleanup:
290     chosen->release();
291 
292     return ret;
293 }
294 
295 void IODTFreeLoaderInfo( const char *key, void *infoAddr, int infoSize )
296 {
297     vm_offset_t			range[2];
298     IORegistryEntry		*chosen;
299 
300     range[0] = (vm_offset_t)infoAddr;
301     range[1] = (vm_offset_t)infoSize;
302     FreePhysicalMemory( range );
303 
304     if ( key != 0 ) {
305         chosen = IORegistryEntry::fromPath( "/chosen/memory-map", gIODTPlane );
306         if ( chosen != 0 ) {
307             chosen->removeProperty(key);
308             chosen->release();
309         }
310     }
311 }
312 
313 int IODTGetDefault(const char *key, void *infoAddr, unsigned int infoSize )
314 {
315     IORegistryEntry		*defaults;
316     OSData			*defaultObj;
317     unsigned int		defaultSize;
318 
319     defaults = IORegistryEntry::fromPath( "/defaults", gIODTPlane );
320     if ( defaults == 0 ) return -1;
321 
322     defaultObj = OSDynamicCast( OSData, defaults->getProperty(key) );
323     if ( defaultObj == 0 ) return -1;
324 
325     defaultSize = defaultObj->getLength();
326     if ( defaultSize > infoSize) return -1;
327 
328     memcpy( infoAddr, defaultObj->getBytesNoCopy(), defaultSize );
329 
330     return 0;
331 }
332 
333 static void FreePhysicalMemory( vm_offset_t * range )
334 {
335     vm_offset_t	virt;
336 
337     virt = ml_static_ptovirt( range[0] );
338     if( virt) {
339         ml_static_mfree( virt, range[1] );
340     }
341 }
342 
343 static IORegistryEntry *
344 MakeReferenceTable( DTEntry dtEntry, bool copy )
345 {
346     IORegistryEntry		*regEntry;
347     OSDictionary		*propTable;
348     const OSSymbol		*nameKey;
349     OSData				*data;
350     const OSSymbol		*sym;
351     OpaqueDTPropertyIterator	dtIter;
352     void				*prop;
353     unsigned int		propSize;
354     char				*name;
355     char				location[ 32 ];
356     bool				noLocation = true;
357     bool				kernelOnly;
358 
359     regEntry = new IOService;
360 
361     if( regEntry && (false == regEntry->init())) {
362         regEntry->release();
363         regEntry = 0;
364     }
365 
366     if( regEntry &&
367       (kSuccess == DTInitPropertyIterator( dtEntry, &dtIter))) {
368 
369         kernelOnly = (kSuccess == DTGetProperty(dtEntry, "kernel-only", &prop, &propSize));
370         propTable = regEntry->getPropertyTable();
371 
372         while( kSuccess == DTIterateProperties( &dtIter, &name)) {
373 
374             if(  kSuccess != DTGetProperty( dtEntry, name, &prop, &propSize ))
375                 continue;
376 
377             if( copy) {
378                 nameKey = OSSymbol::withCString(name);
379                 data = OSData::withBytes(prop, propSize);
380             } else {
381                 nameKey = OSSymbol::withCStringNoCopy(name);
382                 data = OSData::withBytesNoCopy(prop, propSize);
383             }
384             assert( nameKey && data );
385 
386             if (kernelOnly)
387                 data->setSerializable(false);
388 
389             propTable->setObject( nameKey, data);
390             data->release();
391             nameKey->release();
392 
393             if( nameKey == gIODTNameKey ) {
394                 if( copy)
395                     sym = OSSymbol::withCString( (const char *) prop);
396                 else
397                     sym = OSSymbol::withCStringNoCopy( (const char *) prop);
398                 regEntry->setName( sym );
399                 sym->release();
400 
401             } else if( nameKey == gIODTUnitKey ) {
402                 // all OF strings are null terminated... except this one
403                 if( propSize >= (int) sizeof(location))
404                     propSize = sizeof(location) - 1;
405                 strncpy( location, (const char *) prop, propSize );
406                 location[ propSize ] = 0;
407                 regEntry->setLocation( location );
408                 propTable->removeObject( gIODTUnitKey );
409                 noLocation = false;
410 
411             } else if(noLocation && (!strncmp(name, "reg", sizeof("reg")))) {
412                 // default location - override later
413                 snprintf(location, sizeof(location), "%X", *((uint32_t *) prop));
414                 regEntry->setLocation( location );
415             }
416         }
417     }
418 
419     return( regEntry);
420 }
421 
422 static void AddPHandle( IORegistryEntry * regEntry )
423 {
424     OSData *	data;
425 
426     if( regEntry->getProperty( gIODTInterruptCellKey)
427       && (data = OSDynamicCast( OSData, regEntry->getProperty( gIODTPHandleKey )))) {
428         // a possible interrupt-parent
429         gIODTPHandles->setObject( data );
430         gIODTPHandleMap->setObject( regEntry );
431     }
432 }
433 
434 static IORegistryEntry * FindPHandle( UInt32 phandle )
435 {
436     OSData			*data;
437     IORegistryEntry *regEntry = 0;
438     int				i;
439 
440     for( i = 0; (data = (OSData *)gIODTPHandles->getObject( i )); i++ ) {
441         if( phandle == *((UInt32 *)data->getBytesNoCopy())) {
442             regEntry = (IORegistryEntry *)
443             gIODTPHandleMap->getObject( i );
444             break;
445         }
446     }
447 
448     return( regEntry );
449 }
450 
451 static bool GetUInt32( IORegistryEntry * regEntry, const OSSymbol * name,
452 			UInt32 * value )
453 {
454     OSObject * obj;
455     OSData   * data;
456     bool       result;
457 
458     if (!(obj = regEntry->copyProperty(name))) return (false);
459 
460     result = ((data = OSDynamicCast(OSData, obj)) && (sizeof(UInt32) == data->getLength()));
461     if (result) *value = *((UInt32 *) data->getBytesNoCopy());
462 
463     obj->release();
464     return(result);
465 }
466 
467 static IORegistryEntry * IODTFindInterruptParent( IORegistryEntry * regEntry, IOItemCount index )
468 {
469     IORegistryEntry *	parent;
470     UInt32		phandle;
471     OSData	    *	data;
472     unsigned int	len;
473 
474     if( (data = OSDynamicCast( OSData, regEntry->getProperty( gIODTInterruptParentKey )))
475       && (sizeof(UInt32) <= (len = data->getLength()))) {
476 	if (((index + 1) * sizeof(UInt32)) > len)
477 	    index = 0;
478 	phandle = ((UInt32 *) data->getBytesNoCopy())[index];
479 	parent = FindPHandle( phandle );
480 
481     } else if( 0 == regEntry->getProperty( "interrupt-controller"))
482         parent = regEntry->getParentEntry( gIODTPlane);
483     else
484         parent = 0;
485 
486     return( parent );
487 }
488 
489 const OSSymbol * IODTInterruptControllerName( IORegistryEntry * regEntry )
490 {
491     const OSSymbol	*sym;
492     UInt32		phandle;
493     bool		ok;
494     char 		buf[48];
495 
496     ok = GetUInt32( regEntry, gIODTPHandleKey, &phandle);
497     assert( ok );
498 
499     if( ok) {
500         snprintf(buf, sizeof(buf), "IOInterruptController%08X", (uint32_t)phandle);
501         sym = OSSymbol::withCString( buf );
502     } else
503         sym = 0;
504 
505     return( sym );
506 }
507 
508 #define unexpected(a) { kprintf("unexpected %s:%d\n", __FILE__, __LINE__); a; }
509 
510 static void IODTGetICellCounts( IORegistryEntry * regEntry,
511 			    UInt32 * iCellCount, UInt32 * aCellCount)
512 {
513     if( !GetUInt32( regEntry, gIODTInterruptCellKey, iCellCount))
514         unexpected( *iCellCount = 1 );
515     if( !GetUInt32( regEntry, gIODTAddressCellKey, aCellCount))
516         *aCellCount = 0;
517 }
518 
519 static UInt32 IODTMapOneInterrupt( IORegistryEntry * regEntry, UInt32 * intSpec, UInt32 index,
520 				    OSData ** spec, const OSSymbol ** controller )
521 {
522     IORegistryEntry *parent = 0;
523     OSData			*data;
524     UInt32			*addrCmp;
525     UInt32			*maskCmp;
526     UInt32			*map;
527     UInt32			*endMap;
528     UInt32			acells, icells, pacells, picells, cell;
529     UInt32			i, original_icells;
530     bool			cmp, ok = false;
531 
532     parent = IODTFindInterruptParent( regEntry, index );
533     IODTGetICellCounts( parent, &icells, &acells );
534     addrCmp = 0;
535     if( acells) {
536         data = OSDynamicCast( OSData, regEntry->getProperty( "reg" ));
537         if( data && (data->getLength() >= (acells * sizeof(UInt32))))
538             addrCmp = (UInt32 *) data->getBytesNoCopy();
539     }
540     original_icells = icells;
541     regEntry = parent;
542 
543     do {
544 #if IODTSUPPORTDEBUG
545         kprintf ("IODTMapOneInterrupt: current regEntry name %s\n", regEntry->getName());
546         kprintf ("acells - icells: ");
547         for (i = 0; i < acells; i++) kprintf ("0x%08X ", addrCmp[i]);
548         kprintf ("- ");
549         for (i = 0; i < icells; i++) kprintf ("0x%08X ", intSpec[i]);
550         kprintf ("\n");
551 #endif
552 
553         if( parent && (data = OSDynamicCast( OSData,
554             regEntry->getProperty( "interrupt-controller")))) {
555             // found a controller - don't want to follow cascaded controllers
556             parent = 0;
557             *spec = OSData::withBytesNoCopy( (void *) intSpec,
558                                             icells * sizeof(UInt32));
559             *controller = IODTInterruptControllerName( regEntry );
560             ok = (*spec && *controller);
561         } else if( parent && (data = OSDynamicCast( OSData,
562                     regEntry->getProperty( "interrupt-map")))) {
563             // interrupt-map
564             map = (UInt32 *) data->getBytesNoCopy();
565             endMap = map + (data->getLength() / sizeof(UInt32));
566             data = OSDynamicCast( OSData, regEntry->getProperty( "interrupt-map-mask" ));
567             if( data && (data->getLength() >= ((acells + icells) * sizeof(UInt32))))
568                 maskCmp = (UInt32 *) data->getBytesNoCopy();
569             else
570                 maskCmp = 0;
571 
572 #if IODTSUPPORTDEBUG
573             if (maskCmp) {
574                 kprintf ("        maskCmp: ");
575                 for (i = 0; i < acells + icells; i++) {
576                     if (i == acells)
577                         kprintf ("- ");
578                     kprintf ("0x%08X ", maskCmp[i]);
579                 }
580                 kprintf ("\n");
581                 kprintf ("         masked: ");
582                 for (i = 0; i < acells + icells; i++) {
583                     if (i == acells)
584                         kprintf ("- ");
585                     kprintf ("0x%08X ", ((i < acells) ? addrCmp[i] : intSpec[i-acells]) & maskCmp[i]);
586                 }
587                 kprintf ("\n");
588             } else
589                 kprintf ("no maskCmp\n");
590 #endif
591             do {
592 #if IODTSUPPORTDEBUG
593                 kprintf ("            map: ");
594                 for (i = 0; i < acells + icells; i++) {
595                     if (i == acells)
596                         kprintf ("- ");
597                     kprintf ("0x%08X ", map[i]);
598                 }
599                 kprintf ("\n");
600 #endif
601                 for( i = 0, cmp = true; cmp && (i < (acells + icells)); i++) {
602                     cell = (i < acells) ? addrCmp[i] : intSpec[ i - acells ];
603                     if( maskCmp)
604                         cell &= maskCmp[i];
605                     cmp = (cell == map[i]);
606                 }
607 
608                 map += acells + icells;
609                 if( 0 == (parent = FindPHandle( *(map++) )))
610                     unexpected(break);
611 
612                 IODTGetICellCounts( parent, &picells, &pacells );
613                 if( cmp) {
614                     addrCmp = map;
615                     intSpec = map + pacells;
616                     regEntry = parent;
617                 } else {
618                     map += pacells + picells;
619                 }
620             } while( !cmp && (map < endMap) );
621             if (!cmp)
622                 parent = 0;
623         }
624 
625         if( parent) {
626             IODTGetICellCounts( parent, &icells, &acells );
627             regEntry = parent;
628         }
629 
630     } while( parent);
631 
632     return( ok ? original_icells : 0 );
633 }
634 
635 IOReturn IODTGetInterruptOptions( IORegistryEntry * regEntry, int source, IOOptionBits * options )
636 {
637     OSArray *	controllers;
638     OSArray *	specifiers;
639     OSArray *	shared;
640     OSObject *	spec;
641     OSObject *	oneSpec;
642 
643     *options = 0;
644 
645     controllers = OSDynamicCast(OSArray, regEntry->getProperty(gIOInterruptControllersKey));
646     specifiers  = OSDynamicCast(OSArray, regEntry->getProperty(gIOInterruptSpecifiersKey));
647 
648     if( !controllers || !specifiers)
649         return (kIOReturnNoInterrupt);
650 
651     shared = (OSArray *) gIODTSharedInterrupts->getObject(
652                         (const OSSymbol *) controllers->getObject(source) );
653     if (!shared)
654         return (kIOReturnSuccess);
655 
656     spec = specifiers->getObject(source);
657     if (!spec)
658         return (kIOReturnNoInterrupt);
659 
660     for (unsigned int i = 0;
661             (oneSpec = shared->getObject(i))
662             && (!oneSpec->isEqualTo(spec));
663             i++ )	{}
664 
665     if (oneSpec)
666         *options = kIODTInterruptShared;
667 
668     return (kIOReturnSuccess);
669 }
670 
671 static bool IODTMapInterruptsSharing( IORegistryEntry * regEntry, OSDictionary * allInts )
672 {
673     IORegistryEntry *	parent;
674     OSData *		local;
675     OSData *		local2;
676     UInt32 *		localBits;
677     UInt32 *		localEnd;
678     IOItemCount		index;
679     OSData * 		map;
680     OSObject *		oneMap;
681     OSArray *		mapped;
682     OSArray *		controllerInts;
683     const OSSymbol *	controller = 0;
684     OSArray *		controllers;
685     UInt32		skip = 1;
686     bool		ok, nw;
687 
688     nw = (0 == (local = OSDynamicCast( OSData,
689         regEntry->getProperty( gIODTAAPLInterruptsKey))));
690     if( nw && (0 == (local = OSDynamicCast( OSData,
691         regEntry->getProperty( "interrupts")))))
692         return( true );		// nothing to see here
693 
694     if( nw && (parent = regEntry->getParentEntry( gIODTPlane))) {
695         // check for bridges on old world
696         if( (local2 = OSDynamicCast( OSData,
697                 parent->getProperty( gIODTAAPLInterruptsKey)))) {
698             local = local2;
699             nw = false;
700         }
701     }
702 
703     localBits = (UInt32 *) local->getBytesNoCopy();
704     localEnd = localBits + (local->getLength() / sizeof(UInt32));
705     index = 0;
706     mapped = OSArray::withCapacity( 1 );
707     controllers = OSArray::withCapacity( 1 );
708 
709     ok = (mapped && controllers);
710 
711     if( ok) do {
712         if( nw) {
713             skip = IODTMapOneInterrupt( regEntry, localBits, index, &map, &controller );
714             if( 0 == skip) {
715                 IOLog("%s: error mapping interrupt[%d]\n",
716                         regEntry->getName(), mapped->getCount());
717                 break;
718             }
719         } else {
720             map = OSData::withData( local, mapped->getCount() * sizeof(UInt32),
721 				sizeof(UInt32));
722             controller = gIODTDefaultInterruptController;
723             controller->retain();
724         }
725 
726 	index++;
727         localBits += skip;
728         mapped->setObject( map );
729         controllers->setObject( controller );
730 
731         if (allInts)
732         {
733             controllerInts = (OSArray *) allInts->getObject( controller );
734             if (controllerInts)
735 	    {
736                 for (unsigned int i = 0; (oneMap = controllerInts->getObject(i)); i++)
737                 {
738                     if (map->isEqualTo(oneMap))
739                     {
740                         controllerInts = (OSArray *) gIODTSharedInterrupts->getObject( controller );
741                         if (controllerInts)
742                             controllerInts->setObject(map);
743                         else
744                         {
745                             controllerInts = OSArray::withObjects( (const OSObject **) &map, 1, 4 );
746                             if (controllerInts)
747                             {
748                                 gIODTSharedInterrupts->setObject( controller, controllerInts );
749                                 controllerInts->release();
750                             }
751                         }
752                         break;
753                     }
754                 }
755 		if (!oneMap)
756                     controllerInts->setObject(map);
757             }
758             else
759             {
760                 controllerInts = OSArray::withObjects( (const OSObject **) &map, 1, 16 );
761                 if (controllerInts)
762                 {
763                     allInts->setObject( controller, controllerInts );
764                     controllerInts->release();
765                 }
766             }
767         }
768 
769         map->release();
770         controller->release();
771 
772     } while( localBits < localEnd);
773 
774     ok &= (localBits == localEnd);
775 
776     if( ok ) {
777         // store results
778         ok  = regEntry->setProperty( gIOInterruptControllersKey, controllers);
779         ok &= regEntry->setProperty( gIOInterruptSpecifiersKey, mapped);
780     }
781 
782     if( controllers)
783         controllers->release();
784     if( mapped)
785         mapped->release();
786 
787     return( ok );
788 }
789 
790 bool IODTMapInterrupts( IORegistryEntry * regEntry )
791 {
792     return( IODTMapInterruptsSharing( regEntry, 0 ));
793 }
794 
795 /*
796  */
797 
798 static bool
799 CompareKey( OSString * key,
800 		const IORegistryEntry * table, const OSSymbol * propName,
801 		OSString ** matchingName )
802 {
803     OSObject		*prop;
804     OSData			*data;
805     OSString		*string;
806     const char		*ckey;
807     UInt32			keyLen;
808     UInt32          nlen;
809     const char		*names;
810     const char		*lastName;
811     bool			wild;
812     bool			matched;
813     const char		*result = 0;
814 
815     if( 0 == (prop = table->copyProperty( propName ))) return( 0 );
816 
817     if( (data = OSDynamicCast( OSData, prop ))) {
818         names = (const char *) data->getBytesNoCopy();
819         lastName = names + data->getLength();
820     } else if( (string = OSDynamicCast( OSString, prop ))) {
821         names = string->getCStringNoCopy();
822         lastName = names + string->getLength() + 1;
823     } else names = 0;
824 
825 	if (names) {
826 		ckey = key->getCStringNoCopy();
827 		keyLen = key->getLength();
828 		wild = ('*' == key->getChar( keyLen - 1 ));
829 
830 		do {
831 			// for each name in the property
832 			nlen = strnlen(names, lastName - names);
833 			if( wild)
834 				matched = ((nlen >= (keyLen - 1)) && (0 == strncmp(ckey, names, keyLen - 1)));
835 			else
836 				matched = (keyLen == nlen) && (0 == strncmp(ckey, names, keyLen));
837 
838 			if( matched)
839 				result = names;
840 
841 			names = names + nlen + 1;
842 
843 		} while( (names < lastName) && (false == matched));
844 	}
845 
846     if (result && matchingName)	*matchingName = OSString::withCString( result );
847 
848 	if (prop) prop->release();
849 
850     return (result != 0);
851 }
852 
853 
854 bool IODTCompareNubName( const IORegistryEntry * regEntry,
855 			 OSString * name, OSString ** matchingName )
856 {
857     bool matched;
858 
859     matched = CompareKey( name, regEntry, gIODTNameKey,       matchingName)
860 		   || CompareKey( name, regEntry, gIODTCompatibleKey, matchingName)
861 		   || CompareKey( name, regEntry, gIODTTypeKey,       matchingName)
862 		   || CompareKey( name, regEntry, gIODTModelKey,      matchingName);
863 
864     return (matched);
865 }
866 
867 bool IODTMatchNubWithKeys( IORegistryEntry * regEntry,
868                                     const char * keys )
869 {
870     OSObject	*obj;
871     bool		result = false;
872 
873     obj = OSUnserialize( keys, 0 );
874 
875     if( obj) {
876         result = regEntry->compareNames( obj );
877 		obj->release();
878     }
879 #if DEBUG
880     else IOLog("Couldn't unserialize %s\n", keys );
881 #endif
882 
883     return( result );
884 }
885 
886 OSCollectionIterator * IODTFindMatchingEntries( IORegistryEntry * from,
887 			IOOptionBits options, const char * keys )
888 {
889     OSSet					*result = 0;
890     IORegistryEntry			*next;
891     IORegistryIterator		*iter;
892     OSCollectionIterator	*cIter;
893     bool					cmp;
894     bool					minus = options & kIODTExclusive;
895 
896 
897     iter = IORegistryIterator::iterateOver( from, gIODTPlane,
898 		(options & kIODTRecursive) ? kIORegistryIterateRecursively : 0 );
899     if( iter) {
900 
901         do {
902 
903             if( result)
904                 result->release();
905             result = OSSet::withCapacity( 3 );
906             if( !result)
907                 break;
908 
909             iter->reset();
910             while( (next = iter->getNextObject())) {
911 
912                 // Look for existence of a debug property to skip
913                 if( next->getProperty("AAPL,ignore"))
914                     continue;
915 
916                 if( keys) {
917                     cmp = IODTMatchNubWithKeys( next, keys );
918                     if( (minus && (false == cmp))
919                             || ((false == minus) && (false != cmp)) )
920                         result->setObject( next);
921                 } else
922                     result->setObject( next);
923             }
924         } while( !iter->isValid());
925 
926         iter->release();
927     }
928 
929     cIter = OSCollectionIterator::withCollection( result);
930     if (result) result->release();
931 
932     return( cIter);
933 }
934 
935 
936 struct IODTPersistent {
937     IODTCompareAddressCellFunc	compareFunc;
938 };
939 
940 void IODTSetResolving( IORegistryEntry * 	regEntry,
941 		IODTCompareAddressCellFunc	compareFunc,
942 		IODTNVLocationFunc		locationFunc __unused )
943 {
944     IODTPersistent	 persist;
945     IODTPersistent * entry;
946     OSNumber       * num;
947     unsigned int     index, count;
948 
949     IOLockLock(gIODTResolversLock);
950 
951     count = (gIODTResolvers->getLength() / sizeof(IODTPersistent));
952     entry = (typeof(entry)) gIODTResolvers->getBytesNoCopy();
953     for (index = 0; index < count; index++)
954     {
955 	if (compareFunc == entry->compareFunc) break;
956 	entry++;
957     }
958     if (index == count)
959     {
960 	persist.compareFunc = compareFunc;
961 	if (!gIODTResolvers->appendBytes(&persist, sizeof(IODTPersistent))) panic("IODTSetResolving");
962     }
963 
964     IOLockUnlock(gIODTResolversLock);
965 
966     num = OSNumber::withNumber(index, 32);
967     regEntry->setProperty(gIODTPersistKey, num);
968     OSSafeReleaseNULL(num);
969 
970     return;
971 }
972 
973 #if  defined(__arm64__)
974 static SInt64 DefaultCompare( UInt32 cellCount, UInt32 left[], UInt32 right[] )
975 {
976     SInt64 diff = 0;
977 
978     if (cellCount == 2) {
979         diff = IOPhysical32(left[1], left[0]) - IOPhysical32(right[1], right[0]);
980     } else if (cellCount == 1) {
981         diff = ( left[0] - right[0] );
982     } else {
983         panic("DefaultCompare only knows how to handle 1 or 2 cells.");
984     }
985 
986     return diff;
987 }
988 #elif defined(__arm__) || defined(__i386__) || defined(__x86_64__)
989 static SInt32 DefaultCompare( UInt32 cellCount, UInt32 left[], UInt32 right[] )
990 {
991 	cellCount--;
992 	return( left[ cellCount ] - right[ cellCount ] );
993 }
994 #else
995 #error Unknown architecture.
996 #endif
997 
998 static void AddLengthToCells( UInt32 numCells, UInt32 *cells, UInt64 offset)
999 {
1000     if (numCells == 1)
1001     {
1002         cells[0] += (UInt32)offset;
1003     }
1004     else {
1005 #if defined(__arm64__) || defined(__arm__)
1006         UInt64 sum = cells[numCells - 2] + offset;
1007         cells[numCells - 2] = (UInt32)sum;
1008         if (sum > UINT32_MAX) {
1009             cells[numCells - 1] += (UInt32)(sum >> 32);
1010         }
1011 #else
1012         UInt64 sum = cells[numCells - 1] + offset;
1013         cells[numCells - 1] = (UInt32)sum;
1014         if (sum > UINT32_MAX) {
1015             cells[numCells - 2] += (UInt32)(sum >> 32);
1016         }
1017 #endif
1018     }
1019 }
1020 
1021 static IOPhysicalAddress CellsValue( UInt32 numCells, UInt32 *cells)
1022 {
1023     if (numCells == 1) {
1024         return IOPhysical32( 0, cells[0] );
1025     } else {
1026 #if defined(__arm64__) || defined(arm)
1027         return IOPhysical32( cells[numCells - 1], cells[numCells - 2] );
1028 #else
1029         return IOPhysical32( cells[numCells - 2], cells[numCells - 1] );
1030 #endif
1031     }
1032 }
1033 
1034 void IODTGetCellCounts( IORegistryEntry * regEntry,
1035 			    UInt32 * sizeCount, UInt32 * addressCount)
1036 {
1037     if( !GetUInt32( regEntry, gIODTSizeCellKey, sizeCount))
1038         *sizeCount = 1;
1039     if( !GetUInt32( regEntry, gIODTAddressCellKey, addressCount))
1040         *addressCount = 2;
1041     return;
1042 }
1043 
1044 // Given addr & len cells from our child, find it in our ranges property, then
1045 // look in our parent to resolve the base of the range for us.
1046 
1047 // Range[]: child-addr  our-addr  child-len
1048 // #cells:    child       ours     child
1049 
1050 bool IODTResolveAddressCell( IORegistryEntry * startEntry,
1051                              UInt32 cellsIn[],
1052                              IOPhysicalAddress * phys, IOPhysicalLength * lenOut )
1053 {
1054     IORegistryEntry	* parent;
1055     IORegistryEntry * regEntry;
1056     OSData	    * prop;
1057 	OSNumber    * num;
1058 	unsigned int  index, count;
1059     // cells in addresses at regEntry
1060     UInt32		sizeCells, addressCells;
1061     // cells in addresses below regEntry
1062     UInt32		childSizeCells, childAddressCells;
1063     UInt32		childCells;
1064     UInt32		cell[ 8 ], propLen;
1065     UInt64		offset = 0;
1066     UInt32		endCell[ 8 ];
1067     UInt32		*range;
1068     UInt32		*lookRange;
1069     UInt32		*startRange;
1070     UInt32		*endRanges;
1071     bool		ok = true;
1072     SInt64		diff, diff2, endDiff;
1073     UInt64		len, rangeLen;
1074 
1075     IODTPersistent	*persist;
1076     IODTCompareAddressCellFunc	compare;
1077 
1078     regEntry = startEntry;
1079     IODTGetCellCounts( regEntry, &childSizeCells, &childAddressCells );
1080     childCells = childAddressCells + childSizeCells;
1081 
1082     if (childCells > sizeof(cell)/sizeof(cell[0]))
1083         panic("IODTResolveAddressCell: Invalid device tree (%u,%u)", (uint32_t)childAddressCells, (uint32_t)childSizeCells);
1084 
1085     bcopy( cellsIn, cell, sizeof(UInt32) * childCells );
1086     *lenOut = CellsValue( childSizeCells, cellsIn + childAddressCells );
1087 
1088     do
1089     {
1090         prop = OSDynamicCast( OSData, regEntry->getProperty( gIODTRangeKey ));
1091         if( 0 == prop) {
1092             /* end of the road */
1093             *phys = CellsValue( childAddressCells, cell );
1094             *phys += offset;
1095             if (regEntry != startEntry) regEntry->release();
1096             break;
1097         }
1098 
1099         parent = regEntry->copyParentEntry( gIODTPlane );
1100         IODTGetCellCounts( parent, &sizeCells, &addressCells );
1101 
1102         if( (propLen = prop->getLength())) {
1103             // search
1104             startRange = (UInt32 *) prop->getBytesNoCopy();
1105             range = startRange;
1106             endRanges = range + (propLen / sizeof(UInt32));
1107 
1108             compare = NULL;
1109             num = OSDynamicCast(OSNumber, regEntry->getProperty(gIODTPersistKey));
1110             if (num)
1111             {
1112 		IOLockLock(gIODTResolversLock);
1113 		index = num->unsigned32BitValue();
1114 		count = gIODTResolvers->getLength() / sizeof(IODTPersistent);
1115 		if (index < count)
1116 		{
1117 		    persist = ((IODTPersistent *) gIODTResolvers->getBytesNoCopy()) + index;
1118 		    compare = persist->compareFunc;
1119 		}
1120 		IOLockUnlock(gIODTResolversLock);
1121             }
1122 
1123             if (!compare && (addressCells == childAddressCells)) {
1124                 compare = DefaultCompare;
1125 	    }
1126             if (!compare) {
1127                 panic("There is no mixed comparison function yet...");
1128             }
1129 
1130             for( ok = false;
1131                     range < endRanges;
1132                     range += (childCells + addressCells) ) {
1133 
1134                 // is cell start within range?
1135                 diff = (*compare)( childAddressCells, cell, range );
1136 
1137                 if (childAddressCells > sizeof(endCell)/sizeof(endCell[0]))
1138                     panic("IODTResolveAddressCell: Invalid device tree (%u)", (uint32_t)childAddressCells);
1139 
1140                 bcopy(range, endCell, childAddressCells * sizeof(UInt32));
1141 
1142                 rangeLen = CellsValue(childSizeCells, range + childAddressCells + addressCells);
1143                 AddLengthToCells(childAddressCells, endCell, rangeLen);
1144 
1145                 diff2 = (*compare)( childAddressCells, cell, endCell );
1146 
1147                 // if start of cell < start of range, or end of range >= start of cell, skip
1148                 if ((diff < 0) || (diff2 >= 0))
1149                     continue;
1150 
1151                 len = CellsValue(childSizeCells, cell + childAddressCells);
1152                 ok = (0 == len);
1153 
1154                 if (!ok)
1155                 {
1156                     // search for cell end
1157                     bcopy(cell, endCell, childAddressCells * sizeof(UInt32));
1158 
1159                     AddLengthToCells(childAddressCells, endCell, len - 1);
1160 
1161                     for( lookRange = startRange;
1162                             lookRange < endRanges;
1163                             lookRange += (childCells + addressCells) )
1164                     {
1165                         // make sure end of cell >= range start
1166                         endDiff = (*compare)( childAddressCells, endCell, lookRange );
1167                         if( endDiff < 0)
1168                             continue;
1169 
1170                         UInt64 rangeStart = CellsValue(addressCells, range + childAddressCells);
1171                         UInt64 lookRangeStart = CellsValue(addressCells, lookRange + childAddressCells);
1172                         if ((endDiff - len + 1 + lookRangeStart) == (diff + rangeStart))
1173                         {
1174                             ok = true;
1175                             break;
1176                         }
1177                     }
1178                     if (!ok)
1179                         continue;
1180                 }
1181                 offset += diff;
1182                 break;
1183             }
1184 
1185             if (addressCells + sizeCells > sizeof(cell)/sizeof(cell[0]))
1186                 panic("IODTResolveAddressCell: Invalid device tree (%u, %u)", (uint32_t)addressCells, (uint32_t)sizeCells);
1187 
1188             // Get the physical start of the range from our parent
1189             bcopy( range + childAddressCells, cell, sizeof(UInt32) * addressCells );
1190             bzero( cell + addressCells, sizeof(UInt32) * sizeCells );
1191 
1192         } /* else zero length range => pass thru to parent */
1193 
1194         if (regEntry != startEntry) regEntry->release();
1195         regEntry		= parent;
1196         childSizeCells		= sizeCells;
1197         childAddressCells	= addressCells;
1198         childCells		= childAddressCells + childSizeCells;
1199     }
1200     while( ok && regEntry);
1201 
1202     return( ok);
1203 }
1204 
1205 
1206 OSArray * IODTResolveAddressing( IORegistryEntry * regEntry,
1207 			const char * addressPropertyName,
1208 			IODeviceMemory * parent )
1209 {
1210     IORegistryEntry		*parentEntry;
1211     OSData				*addressProperty;
1212     UInt32				sizeCells, addressCells, cells;
1213     int					i, num;
1214     UInt32				*reg;
1215     IOPhysicalAddress	phys;
1216     IOPhysicalLength	len;
1217     OSArray				*array;
1218     IODeviceMemory		*range;
1219 
1220     array = 0;
1221     do
1222     {
1223         parentEntry = regEntry->copyParentEntry( gIODTPlane );
1224         addressProperty = (OSData *) regEntry->getProperty( addressPropertyName );
1225         if( (0 == addressProperty) || (0 == parentEntry)) break;
1226 
1227         IODTGetCellCounts( parentEntry, &sizeCells, &addressCells );
1228         if( 0 == sizeCells) break;
1229 
1230         cells = sizeCells + addressCells;
1231         reg = (UInt32 *) addressProperty->getBytesNoCopy();
1232         num = addressProperty->getLength() / (4 * cells);
1233 
1234         array = OSArray::withCapacity( 1 );
1235         if( 0 == array) break;
1236 
1237         for( i = 0; i < num; i++) {
1238             if( IODTResolveAddressCell( parentEntry, reg, &phys, &len )) {
1239                 range = 0;
1240                 if( parent)
1241                     range = IODeviceMemory::withSubRange( parent,
1242                             phys - parent->getPhysicalSegment(0, 0, kIOMemoryMapperNone), len );
1243                 if( 0 == range)
1244                     range = IODeviceMemory::withRange( phys, len );
1245                 if( range)
1246                     array->setObject( range );
1247             }
1248             reg += cells;
1249         }
1250 
1251         regEntry->setProperty( gIODeviceMemoryKey, array);
1252         array->release();	/* ??? */
1253     }
1254     while (false);
1255 
1256     OSSafeReleaseNULL(parentEntry);
1257 
1258     return (array);
1259 }
1260 
1261 OSData * IODTFindSlotName( IORegistryEntry * regEntry, UInt32 deviceNumber )
1262 {
1263     IORegistryEntry		*parent;
1264     OSData				*data;
1265     OSData				*ret = 0;
1266     UInt32				*bits;
1267     UInt32				i;
1268     size_t              nlen;
1269     char				*names;
1270     char				*lastName;
1271     UInt32				mask;
1272 
1273     data = (OSData *) regEntry->getProperty("AAPL,slot-name");
1274     if (data) return (data);
1275 
1276     do
1277     {
1278         parent = regEntry->copyParentEntry( gIODTPlane );
1279         if (!parent) break;
1280 
1281         data = OSDynamicCast( OSData, parent->getProperty("slot-names"));
1282         if (!data) break;
1283         if (data->getLength() <= 4) break;
1284 
1285         bits = (UInt32 *) data->getBytesNoCopy();
1286         mask = *bits;
1287         if ((0 == (mask & (1 << deviceNumber)))) break;
1288 
1289         names = (char *)(bits + 1);
1290         lastName = names + (data->getLength() - 4);
1291 
1292         for( i = 0; (i <= deviceNumber) && (names < lastName); i++ ) {
1293 
1294             if( mask & (1 << i)) {
1295                 nlen = 1 + strnlen(names, lastName - names);
1296                 if( i == deviceNumber) {
1297                     data = OSData::withBytesNoCopy(names, nlen);
1298                     if( data) {
1299                         regEntry->setProperty("AAPL,slot-name", data);
1300                         ret = data;
1301                         data->release();
1302                     }
1303                 } else
1304                     names += nlen;
1305             }
1306         }
1307     }
1308     while (false);
1309 
1310     OSSafeReleaseNULL(parent);
1311 
1312     return( ret );
1313 }
1314 
1315 extern "C" IOReturn IONDRVLibrariesInitialize( IOService * provider )
1316 {
1317     return( kIOReturnUnsupported );
1318 }
1319