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