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__
41typedef UInt64 dtptr_t;
42#else
43typedef UInt32 dtptr_t;
44#endif
45
46#include <machine/machine_routines.h>
47
48extern "C" {
49
50int IODTGetLoaderInfo( const char *key, void **infoAddr, int *infosize );
51void IODTFreeLoaderInfo( const char *key, void *infoAddr, int infoSize );
52int IODTGetDefault(const char *key, void *infoAddr, unsigned int infoSize );
53
54}
55
56#include <IOKit/assert.h>
57
58#define IODTSUPPORTDEBUG 0
59
60const IORegistryPlane * gIODTPlane;
61
62static OSArray * gIODTPHandles;
63static OSArray * gIODTPHandleMap;
64static OSData * gIODTResolvers;
65
66const OSSymbol * gIODTNameKey;
67const OSSymbol * gIODTUnitKey;
68const OSSymbol * gIODTCompatibleKey;
69const OSSymbol * gIODTTypeKey;
70const OSSymbol * gIODTModelKey;
71const OSSymbol * gIODTTargetTypeKey;
72
73const OSSymbol * gIODTSizeCellKey;
74const OSSymbol * gIODTAddressCellKey;
75const OSSymbol * gIODTRangeKey;
76
77const OSSymbol * gIODTPersistKey;
78
79const OSSymbol * gIODTDefaultInterruptController;
80const OSSymbol * gIODTAAPLInterruptsKey;
81const OSSymbol * gIODTPHandleKey;
82const OSSymbol * gIODTInterruptCellKey;
83const OSSymbol * gIODTInterruptParentKey;
84const OSSymbol * gIODTNWInterruptMappingKey;
85
86OSDictionary * gIODTSharedInterrupts;
87
88static IOLock * gIODTResolversLock;
89
90static IORegistryEntry * MakeReferenceTable( DTEntry dtEntry, bool copy );
91static void AddPHandle( IORegistryEntry * regEntry );
92static void FreePhysicalMemory( vm_offset_t * range );
93static bool IODTMapInterruptsSharing( IORegistryEntry * regEntry, OSDictionary * allInts );
94
95IORegistryEntry *
96IODeviceTreeAlloc( 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
264int 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
289cleanup:
290 chosen->release();
291
292 return ret;
293}
294
295void 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
313int 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
333static 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
343static IORegistryEntry *
344MakeReferenceTable( 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
422static 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
434static 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
451static 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
467static 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
489const 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
510static 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
519static 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
635IOReturn 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
671static 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
790bool IODTMapInterrupts( IORegistryEntry * regEntry )
791{
792 return( IODTMapInterruptsSharing( regEntry, 0 ));
793}
794
795/*
796 */
797
798static bool
799CompareKey( 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
854bool 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
867bool 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
886OSCollectionIterator * 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
936struct IODTPersistent {
937 IODTCompareAddressCellFunc compareFunc;
938};
939
940void 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__)
974static 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__)
989static 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
998static 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
1021static 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
1034void 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
1050bool 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
1206OSArray * 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
1261OSData * 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
1315extern "C" IOReturn IONDRVLibrariesInitialize( IOService * provider )
1316{
1317 return( kIOReturnUnsupported );
1318}
1319