]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. | |
7 | * | |
8 | * This file contains Original Code and/or Modifications of Original Code | |
9 | * as defined in and that are subject to the Apple Public Source License | |
10 | * Version 2.0 (the 'License'). You may not use this file except in | |
11 | * compliance with the License. Please obtain a copy of the License at | |
12 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
13 | * file. | |
14 | * | |
15 | * The Original Code and all software distributed under the License are | |
16 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
17 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
18 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
19 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
20 | * Please see the License for the specific language governing rights and | |
21 | * limitations under the License. | |
22 | * | |
23 | * @APPLE_LICENSE_HEADER_END@ | |
24 | */ | |
25 | /* | |
26 | * Copyright (c) 1998 Apple Computer, Inc. All rights reserved. | |
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 <DeviceTree.h> | |
39 | extern "C" { | |
40 | #include <machine/machine_routines.h> | |
41 | void DTInit( void * data ); | |
42 | ||
43 | int IODTGetLoaderInfo( char *key, void **infoAddr, int *infosize ); | |
44 | void IODTFreeLoaderInfo( char *key, void *infoAddr, int infoSize ); | |
45 | } | |
46 | ||
47 | #include <IOKit/assert.h> | |
48 | ||
49 | #define IODTSUPPORTDEBUG 0 | |
50 | ||
51 | const IORegistryPlane * gIODTPlane; | |
52 | ||
53 | static OSArray * gIODTPHandles; | |
54 | static OSArray * gIODTPHandleMap; | |
55 | ||
56 | const OSSymbol * gIODTNameKey; | |
57 | const OSSymbol * gIODTUnitKey; | |
58 | const OSSymbol * gIODTCompatibleKey; | |
59 | const OSSymbol * gIODTTypeKey; | |
60 | const OSSymbol * gIODTModelKey; | |
61 | ||
62 | const OSSymbol * gIODTSizeCellKey; | |
63 | const OSSymbol * gIODTAddressCellKey; | |
64 | const OSSymbol * gIODTRangeKey; | |
65 | ||
66 | const OSSymbol * gIODTPersistKey; | |
67 | ||
68 | const OSSymbol * gIODTDefaultInterruptController; | |
69 | const OSSymbol * gIODTAAPLInterruptsKey; | |
70 | const OSSymbol * gIODTPHandleKey; | |
71 | const OSSymbol * gIODTInterruptCellKey; | |
72 | const OSSymbol * gIODTInterruptParentKey; | |
73 | const OSSymbol * gIODTNWInterruptMappingKey; | |
74 | ||
75 | ||
76 | static IORegistryEntry * MakeReferenceTable( DTEntry dtEntry, bool copy ); | |
77 | static void AddPHandle( IORegistryEntry * regEntry ); | |
78 | static void FreePhysicalMemory( vm_offset_t * range ); | |
79 | ||
80 | IORegistryEntry * | |
81 | IODeviceTreeAlloc( void * dtTop ) | |
82 | { | |
83 | IORegistryEntry *parent; | |
84 | IORegistryEntry *child; | |
85 | IORegistryIterator *regIter; | |
86 | DTEntryIterator iter; | |
87 | DTEntry dtChild; | |
88 | DTEntry mapEntry; | |
89 | OSArray *stack; | |
90 | OSData *prop; | |
91 | OSObject *obj; | |
92 | vm_offset_t *dtMap; | |
93 | int propSize; | |
94 | bool intMap; | |
95 | bool freeDT; | |
96 | ||
97 | IOLog("IODeviceTreeSupport "); | |
98 | ||
99 | gIODTPlane = IORegistryEntry::makePlane( kIODeviceTreePlane ); | |
100 | ||
101 | gIODTNameKey = OSSymbol::withCStringNoCopy( "name" ); | |
102 | gIODTUnitKey = OSSymbol::withCStringNoCopy( "AAPL,unit-string" ); | |
103 | gIODTCompatibleKey = OSSymbol::withCStringNoCopy( "compatible" ); | |
104 | gIODTTypeKey = OSSymbol::withCStringNoCopy( "device_type" ); | |
105 | gIODTModelKey = OSSymbol::withCStringNoCopy( "model" ); | |
106 | gIODTSizeCellKey = OSSymbol::withCStringNoCopy( "#size-cells" ); | |
107 | gIODTAddressCellKey = OSSymbol::withCStringNoCopy( "#address-cells" ); | |
108 | gIODTRangeKey = OSSymbol::withCStringNoCopy( "ranges" ); | |
109 | gIODTPersistKey = OSSymbol::withCStringNoCopy( "IODTPersist" ); | |
110 | ||
111 | assert( gIODTPlane && gIODTCompatibleKey | |
112 | && gIODTTypeKey && gIODTModelKey | |
113 | && gIODTSizeCellKey && gIODTAddressCellKey && gIODTRangeKey | |
114 | && gIODTPersistKey ); | |
115 | ||
116 | gIODTDefaultInterruptController | |
117 | = OSSymbol::withCStringNoCopy("IOPrimaryInterruptController"); | |
118 | gIODTNWInterruptMappingKey | |
119 | = OSSymbol::withCStringNoCopy("IONWInterrupts"); | |
120 | ||
121 | gIODTAAPLInterruptsKey | |
122 | = OSSymbol::withCStringNoCopy("AAPL,interrupts"); | |
123 | gIODTPHandleKey | |
124 | = OSSymbol::withCStringNoCopy("AAPL,phandle"); | |
125 | ||
126 | gIODTInterruptParentKey | |
127 | = OSSymbol::withCStringNoCopy("interrupt-parent"); | |
128 | ||
129 | gIODTPHandles = OSArray::withCapacity( 1 ); | |
130 | gIODTPHandleMap = OSArray::withCapacity( 1 ); | |
131 | ||
132 | gIODTInterruptCellKey | |
133 | = OSSymbol::withCStringNoCopy("#interrupt-cells"); | |
134 | ||
135 | assert( gIODTDefaultInterruptController && gIODTNWInterruptMappingKey | |
136 | && gIODTAAPLInterruptsKey | |
137 | && gIODTPHandleKey && gIODTInterruptParentKey | |
138 | && gIODTPHandles && gIODTPHandleMap | |
139 | && gIODTInterruptCellKey | |
140 | ); | |
141 | ||
142 | freeDT = (kSuccess == DTLookupEntry( 0, "/chosen/memory-map", &mapEntry )) | |
143 | && (kSuccess == DTGetProperty( mapEntry, | |
144 | "DeviceTree", (void **) &dtMap, &propSize )) | |
145 | && ((2 * sizeof( vm_offset_t)) == propSize); | |
146 | ||
147 | parent = MakeReferenceTable( (DTEntry)dtTop, freeDT ); | |
148 | ||
149 | stack = OSArray::withObjects( & (const OSObject *) parent, 1, 10 ); | |
150 | DTCreateEntryIterator( (DTEntry)dtTop, &iter ); | |
151 | ||
152 | do { | |
153 | parent = (IORegistryEntry *)stack->getObject( stack->getCount() - 1); | |
154 | //parent->release(); | |
155 | stack->removeObject( stack->getCount() - 1); | |
156 | ||
157 | while( kSuccess == DTIterateEntries( iter, &dtChild) ) { | |
158 | ||
159 | child = MakeReferenceTable( dtChild, freeDT ); | |
160 | child->attachToParent( parent, gIODTPlane); | |
161 | ||
162 | AddPHandle( child ); | |
163 | ||
164 | if( kSuccess == DTEnterEntry( iter, dtChild)) { | |
165 | stack->setObject( parent); | |
166 | parent = child; | |
167 | } | |
168 | // only registry holds retain | |
169 | child->release(); | |
170 | } | |
171 | ||
172 | } while( stack->getCount() | |
173 | && (kSuccess == DTExitEntry( iter, &dtChild))); | |
174 | ||
175 | stack->release(); | |
176 | DTDisposeEntryIterator( iter); | |
177 | ||
178 | // parent is now root of the created tree | |
179 | ||
180 | // make root name first compatible entry (purely cosmetic) | |
181 | if( (prop = (OSData *) parent->getProperty( gIODTCompatibleKey))) { | |
182 | parent->setName( parent->getName(), gIODTPlane ); | |
183 | parent->setName( (const char *) prop->getBytesNoCopy() ); | |
184 | } | |
185 | ||
186 | // attach tree to meta root | |
187 | parent->attachToParent( IORegistryEntry::getRegistryRoot(), gIODTPlane); | |
188 | parent->release(); | |
189 | ||
190 | if( freeDT ) { | |
191 | // free original device tree | |
192 | DTInit(0); | |
193 | IODTFreeLoaderInfo( "DeviceTree", | |
194 | (void *)dtMap[0], round_page_32(dtMap[1]) ); | |
195 | } | |
196 | ||
197 | // adjust tree | |
198 | intMap = false; | |
199 | regIter = IORegistryIterator::iterateOver( gIODTPlane, | |
200 | kIORegistryIterateRecursively ); | |
201 | assert( regIter ); | |
202 | if( regIter) { | |
203 | while( (child = regIter->getNextObject())) { | |
204 | IODTMapInterrupts( child ); | |
205 | if( !intMap && child->getProperty( gIODTInterruptParentKey)) | |
206 | intMap = true; | |
207 | ||
208 | // Look for a "driver,AAPL,MacOSX,PowerPC" property. | |
209 | if( (obj = child->getProperty( "driver,AAPL,MacOSX,PowerPC"))) { | |
210 | gIOCatalogue->addExtensionsFromArchive((OSData *)obj); | |
211 | child->removeProperty( "driver,AAPL,MacOSX,PowerPC"); | |
212 | } | |
213 | ||
214 | // some gross pruning | |
215 | child->removeProperty( "lanLib,AAPL,MacOS,PowerPC"); | |
216 | ||
217 | if( (obj = child->getProperty( "driver,AAPL,MacOS,PowerPC"))) { | |
218 | ||
219 | if( (0 == (prop = (OSData *)child->getProperty( gIODTTypeKey ))) | |
220 | || (strcmp( "display", (char *) prop->getBytesNoCopy())) ) { | |
221 | child->removeProperty( "driver,AAPL,MacOS,PowerPC"); | |
222 | } | |
223 | } | |
224 | } | |
225 | regIter->release(); | |
226 | } | |
227 | ||
228 | if( intMap) | |
229 | // set a key in the root to indicate we found NW interrupt mapping | |
230 | parent->setProperty( gIODTNWInterruptMappingKey, | |
231 | (OSObject *) gIODTNWInterruptMappingKey ); | |
232 | ||
233 | IOLog("done\n"); | |
234 | ||
235 | return( parent); | |
236 | } | |
237 | ||
238 | int IODTGetLoaderInfo( char *key, void **infoAddr, int *infoSize ) | |
239 | { | |
240 | IORegistryEntry *chosen; | |
241 | OSData *propObj; | |
242 | unsigned int *propPtr; | |
243 | unsigned int propSize; | |
244 | ||
245 | chosen = IORegistryEntry::fromPath( "/chosen/memory-map", gIODTPlane ); | |
246 | if ( chosen == 0 ) return -1; | |
247 | ||
248 | propObj = OSDynamicCast( OSData, chosen->getProperty(key) ); | |
249 | if ( propObj == 0 ) return -1; | |
250 | ||
251 | propSize = propObj->getLength(); | |
252 | if ( propSize != (2 * sizeof(UInt32)) ) return -1; | |
253 | ||
254 | propPtr = (unsigned int *)propObj->getBytesNoCopy(); | |
255 | if ( propPtr == 0 ) return -1; | |
256 | ||
257 | *infoAddr = (void *)propPtr[0] ; | |
258 | *infoSize = (int) propPtr[1]; | |
259 | ||
260 | return 0; | |
261 | } | |
262 | ||
263 | void IODTFreeLoaderInfo( char *key, void *infoAddr, int infoSize ) | |
264 | { | |
265 | vm_offset_t range[2]; | |
266 | IORegistryEntry *chosen; | |
267 | ||
268 | range[0] = (vm_offset_t)infoAddr; | |
269 | range[1] = (vm_offset_t)infoSize; | |
270 | FreePhysicalMemory( range ); | |
271 | ||
272 | if ( key != 0 ) { | |
273 | chosen = IORegistryEntry::fromPath( "/chosen/memory-map", gIODTPlane ); | |
274 | if ( chosen != 0 ) { | |
275 | chosen->removeProperty(key); | |
276 | } | |
277 | } | |
278 | } | |
279 | ||
280 | static void FreePhysicalMemory( vm_offset_t * range ) | |
281 | { | |
282 | vm_offset_t virt; | |
283 | ||
284 | virt = ml_static_ptovirt( range[0] ); | |
285 | if( virt) { | |
286 | ml_static_mfree( virt, range[1] ); | |
287 | } | |
288 | } | |
289 | ||
290 | static IORegistryEntry * | |
291 | MakeReferenceTable( DTEntry dtEntry, bool copy ) | |
292 | { | |
293 | IORegistryEntry *regEntry; | |
294 | OSDictionary *propTable; | |
295 | const OSSymbol *nameKey; | |
296 | OSData *data; | |
297 | const OSSymbol *sym; | |
298 | DTPropertyIterator dtIter; | |
299 | void *prop; | |
300 | int propSize; | |
301 | char *name; | |
302 | char location[ 32 ]; | |
303 | bool noLocation = true; | |
304 | ||
305 | regEntry = new IOService; | |
306 | ||
307 | if( regEntry && (false == regEntry->init())) { | |
308 | regEntry->release(); | |
309 | regEntry = 0; | |
310 | } | |
311 | ||
312 | if( regEntry && | |
313 | (kSuccess == DTCreatePropertyIterator( dtEntry, &dtIter))) { | |
314 | ||
315 | propTable = regEntry->getPropertyTable(); | |
316 | ||
317 | while( kSuccess == DTIterateProperties( dtIter, &name)) { | |
318 | ||
319 | if( kSuccess != DTGetProperty( dtEntry, name, &prop, &propSize )) | |
320 | continue; | |
321 | ||
322 | if( copy) { | |
323 | nameKey = OSSymbol::withCString(name); | |
324 | data = OSData::withBytes(prop, propSize); | |
325 | } else { | |
326 | nameKey = OSSymbol::withCStringNoCopy(name); | |
327 | data = OSData::withBytesNoCopy(prop, propSize); | |
328 | } | |
329 | assert( nameKey && data ); | |
330 | ||
331 | propTable->setObject( nameKey, data); | |
332 | data->release(); | |
333 | nameKey->release(); | |
334 | ||
335 | if( nameKey == gIODTNameKey ) { | |
336 | if( copy) | |
337 | sym = OSSymbol::withCString( (const char *) prop); | |
338 | else | |
339 | sym = OSSymbol::withCStringNoCopy( (const char *) prop); | |
340 | regEntry->setName( sym ); | |
341 | sym->release(); | |
342 | ||
343 | } else if( nameKey == gIODTUnitKey ) { | |
344 | // all OF strings are null terminated... except this one | |
345 | if( propSize >= (int) sizeof( location)) | |
346 | propSize = sizeof( location) - 1; | |
347 | strncpy( location, (const char *) prop, propSize ); | |
348 | location[ propSize ] = 0; | |
349 | regEntry->setLocation( location ); | |
350 | propTable->removeObject( gIODTUnitKey ); | |
351 | noLocation = false; | |
352 | ||
353 | } else if( noLocation && (0 == strcmp( name, "reg"))) { | |
354 | // default location - override later | |
355 | sprintf( location, "%lX", *((UInt32 *) prop) ); | |
356 | regEntry->setLocation( location ); | |
357 | } | |
358 | } | |
359 | DTDisposePropertyIterator( dtIter); | |
360 | } | |
361 | ||
362 | return( regEntry); | |
363 | } | |
364 | ||
365 | static void AddPHandle( IORegistryEntry * regEntry ) | |
366 | { | |
367 | OSData * data; | |
368 | ||
369 | if( regEntry->getProperty( gIODTInterruptCellKey) | |
370 | && (data = OSDynamicCast( OSData, regEntry->getProperty( gIODTPHandleKey )))) { | |
371 | // a possible interrupt-parent | |
372 | gIODTPHandles->setObject( data ); | |
373 | gIODTPHandleMap->setObject( regEntry ); | |
374 | } | |
375 | } | |
376 | ||
377 | static IORegistryEntry * FindPHandle( UInt32 phandle ) | |
378 | { | |
379 | OSData *data; | |
380 | IORegistryEntry *regEntry = 0; | |
381 | int i; | |
382 | ||
383 | for( i = 0; (data = (OSData *)gIODTPHandles->getObject( i )); i++ ) { | |
384 | if( phandle == *((UInt32 *)data->getBytesNoCopy())) { | |
385 | regEntry = (IORegistryEntry *) | |
386 | gIODTPHandleMap->getObject( i ); | |
387 | break; | |
388 | } | |
389 | } | |
390 | ||
391 | return( regEntry ); | |
392 | } | |
393 | ||
394 | static bool GetUInt32( IORegistryEntry * regEntry, const OSSymbol * name, | |
395 | UInt32 * value ) | |
396 | { | |
397 | OSData *data; | |
398 | ||
399 | if( (data = OSDynamicCast( OSData, regEntry->getProperty( name ))) | |
400 | && (4 == data->getLength())) { | |
401 | *value = *((UInt32 *) data->getBytesNoCopy()); | |
402 | return( true ); | |
403 | } else | |
404 | return( false ); | |
405 | } | |
406 | ||
407 | IORegistryEntry * IODTFindInterruptParent( IORegistryEntry * regEntry ) | |
408 | { | |
409 | IORegistryEntry * parent; | |
410 | UInt32 phandle; | |
411 | ||
412 | if( GetUInt32( regEntry, gIODTInterruptParentKey, &phandle)) | |
413 | parent = FindPHandle( phandle ); | |
414 | ||
415 | else if( 0 == regEntry->getProperty( "interrupt-controller")) | |
416 | parent = regEntry->getParentEntry( gIODTPlane); | |
417 | else | |
418 | parent = 0; | |
419 | ||
420 | return( parent ); | |
421 | } | |
422 | ||
423 | const OSSymbol * IODTInterruptControllerName( IORegistryEntry * regEntry ) | |
424 | { | |
425 | const OSSymbol *sym; | |
426 | UInt32 phandle; | |
427 | bool ok; | |
428 | char buf[48]; | |
429 | ||
430 | ok = GetUInt32( regEntry, gIODTPHandleKey, &phandle); | |
431 | assert( ok ); | |
432 | ||
433 | if( ok) { | |
434 | sprintf( buf, "IOInterruptController%08lX", phandle); | |
435 | sym = OSSymbol::withCString( buf ); | |
436 | } else | |
437 | sym = 0; | |
438 | ||
439 | return( sym ); | |
440 | } | |
441 | ||
442 | #define unexpected(a) { kprintf("unexpected %s:%d\n", __FILE__, __LINE__); a; } | |
443 | ||
444 | static void IODTGetICellCounts( IORegistryEntry * regEntry, | |
445 | UInt32 * iCellCount, UInt32 * aCellCount) | |
446 | { | |
447 | if( !GetUInt32( regEntry, gIODTInterruptCellKey, iCellCount)) | |
448 | unexpected( *iCellCount = 1 ); | |
449 | if( !GetUInt32( regEntry, gIODTAddressCellKey, aCellCount)) | |
450 | *aCellCount = 0; | |
451 | } | |
452 | ||
453 | UInt32 IODTMapOneInterrupt( IORegistryEntry * regEntry, UInt32 * intSpec, | |
454 | OSData ** spec, const OSSymbol ** controller ) | |
455 | { | |
456 | IORegistryEntry *parent = 0; | |
457 | OSData *data; | |
458 | UInt32 *addrCmp; | |
459 | UInt32 *maskCmp; | |
460 | UInt32 *map; | |
461 | UInt32 *endMap; | |
462 | UInt32 acells, icells, pacells, picells, cell; | |
463 | UInt32 i, original_icells; | |
464 | bool cmp, ok = false; | |
465 | ||
466 | parent = IODTFindInterruptParent( regEntry ); | |
467 | IODTGetICellCounts( parent, &icells, &acells ); | |
468 | addrCmp = 0; | |
469 | if( acells) { | |
470 | data = OSDynamicCast( OSData, regEntry->getProperty( "reg" )); | |
471 | if( data && (data->getLength() >= (acells * sizeof( UInt32)))) | |
472 | addrCmp = (UInt32 *) data->getBytesNoCopy(); | |
473 | } | |
474 | original_icells = icells; | |
475 | regEntry = parent; | |
476 | ||
477 | do { | |
478 | #if IODTSUPPORTDEBUG | |
479 | kprintf ("IODTMapOneInterrupt: current regEntry name %s\n", regEntry->getName()); | |
480 | kprintf ("acells - icells: "); | |
481 | for (i = 0; i < acells; i++) kprintf ("0x%08X ", addrCmp[i]); | |
482 | kprintf ("- "); | |
483 | for (i = 0; i < icells; i++) kprintf ("0x%08X ", intSpec[i]); | |
484 | kprintf ("\n"); | |
485 | #endif | |
486 | ||
487 | if( parent && (data = OSDynamicCast( OSData, | |
488 | regEntry->getProperty( "interrupt-controller")))) { | |
489 | // found a controller - don't want to follow cascaded controllers | |
490 | parent = 0; | |
491 | *spec = OSData::withBytesNoCopy( (void *) intSpec, | |
492 | icells * sizeof( UInt32)); | |
493 | *controller = IODTInterruptControllerName( regEntry ); | |
494 | ok = (*spec && *controller); | |
495 | } else if( parent && (data = OSDynamicCast( OSData, | |
496 | regEntry->getProperty( "interrupt-map")))) { | |
497 | // interrupt-map | |
498 | map = (UInt32 *) data->getBytesNoCopy(); | |
499 | endMap = map + (data->getLength() / sizeof(UInt32)); | |
500 | data = OSDynamicCast( OSData, regEntry->getProperty( "interrupt-map-mask" )); | |
501 | if( data && (data->getLength() >= ((acells + icells) * sizeof( UInt32)))) | |
502 | maskCmp = (UInt32 *) data->getBytesNoCopy(); | |
503 | else | |
504 | maskCmp = 0; | |
505 | ||
506 | #if IODTSUPPORTDEBUG | |
507 | if (maskCmp) { | |
508 | kprintf (" maskCmp: "); | |
509 | for (i = 0; i < acells + icells; i++) { | |
510 | if (i == acells) | |
511 | kprintf ("- "); | |
512 | kprintf ("0x%08X ", maskCmp[i]); | |
513 | } | |
514 | kprintf ("\n"); | |
515 | kprintf (" masked: "); | |
516 | for (i = 0; i < acells + icells; i++) { | |
517 | if (i == acells) | |
518 | kprintf ("- "); | |
519 | kprintf ("0x%08X ", ((i < acells) ? addrCmp[i] : intSpec[i-acells]) & maskCmp[i]); | |
520 | } | |
521 | kprintf ("\n"); | |
522 | } else | |
523 | kprintf ("no maskCmp\n"); | |
524 | #endif | |
525 | do { | |
526 | #if IODTSUPPORTDEBUG | |
527 | kprintf (" map: "); | |
528 | for (i = 0; i < acells + icells; i++) { | |
529 | if (i == acells) | |
530 | kprintf ("- "); | |
531 | kprintf ("0x%08X ", map[i]); | |
532 | } | |
533 | kprintf ("\n"); | |
534 | #endif | |
535 | for( i = 0, cmp = true; cmp && (i < (acells + icells)); i++) { | |
536 | cell = (i < acells) ? addrCmp[i] : intSpec[ i - acells ]; | |
537 | if( maskCmp) | |
538 | cell &= maskCmp[i]; | |
539 | cmp = (cell == map[i]); | |
540 | } | |
541 | ||
542 | map += acells + icells; | |
543 | if( 0 == (parent = FindPHandle( *(map++) ))) | |
544 | unexpected(break); | |
545 | ||
546 | IODTGetICellCounts( parent, &picells, &pacells ); | |
547 | if( cmp) { | |
548 | addrCmp = map; | |
549 | intSpec = map + pacells; | |
550 | regEntry = parent; | |
551 | } else { | |
552 | map += pacells + picells; | |
553 | } | |
554 | } while( !cmp && (map < endMap) ); | |
555 | if (!cmp) | |
556 | parent = 0; | |
557 | } | |
558 | ||
559 | if( parent) { | |
560 | IODTGetICellCounts( parent, &icells, &acells ); | |
561 | regEntry = parent; | |
562 | } | |
563 | ||
564 | } while( parent); | |
565 | ||
566 | return( ok ? original_icells : 0 ); | |
567 | } | |
568 | ||
569 | bool IODTMapInterrupts( IORegistryEntry * regEntry ) | |
570 | { | |
571 | IORegistryEntry *parent; | |
572 | OSData *local; | |
573 | OSData *local2; | |
574 | UInt32 *localBits; | |
575 | UInt32 *localEnd; | |
576 | OSData *map; | |
577 | OSArray *mapped; | |
578 | const OSSymbol *controller; | |
579 | OSArray *controllers; | |
580 | UInt32 skip = 1; | |
581 | bool ok, nw; | |
582 | ||
583 | nw = (0 == (local = OSDynamicCast( OSData, | |
584 | regEntry->getProperty( gIODTAAPLInterruptsKey)))); | |
585 | if( nw && (0 == (local = OSDynamicCast( OSData, | |
586 | regEntry->getProperty( "interrupts"))))) | |
587 | return( true ); // nothing to see here | |
588 | ||
589 | if( nw && (parent = regEntry->getParentEntry( gIODTPlane))) { | |
590 | // check for bridges on old world | |
591 | if( (local2 = OSDynamicCast( OSData, | |
592 | parent->getProperty( gIODTAAPLInterruptsKey)))) { | |
593 | local = local2; | |
594 | nw = false; | |
595 | } | |
596 | } | |
597 | ||
598 | localBits = (UInt32 *) local->getBytesNoCopy(); | |
599 | localEnd = localBits + (local->getLength() / sizeof( UInt32)); | |
600 | mapped = OSArray::withCapacity( 1 ); | |
601 | controllers = OSArray::withCapacity( 1 ); | |
602 | ||
603 | ok = (mapped && controllers); | |
604 | ||
605 | if( ok) do { | |
606 | if( nw) { | |
607 | skip = IODTMapOneInterrupt( regEntry, localBits, &map, &controller ); | |
608 | if( 0 == skip) { | |
609 | IOLog("%s: error mapping interrupt[%d]\n", | |
610 | regEntry->getName(), mapped->getCount()); | |
611 | break; | |
612 | } | |
613 | } else { | |
614 | map = OSData::withData( local, mapped->getCount() * sizeof( UInt32), | |
615 | sizeof( UInt32)); | |
616 | controller = gIODTDefaultInterruptController; | |
617 | controller->retain(); | |
618 | } | |
619 | ||
620 | localBits += skip; | |
621 | mapped->setObject( map ); | |
622 | map->release(); | |
623 | controllers->setObject( (OSObject *) controller ); | |
624 | controller->release(); | |
625 | ||
626 | } while( localBits < localEnd); | |
627 | ||
628 | ok &= (localBits == localEnd); | |
629 | ||
630 | if( ok ) { | |
631 | // store results | |
632 | ok = regEntry->setProperty( gIOInterruptControllersKey, controllers); | |
633 | ok &= regEntry->setProperty( gIOInterruptSpecifiersKey, mapped); | |
634 | } | |
635 | ||
636 | if( controllers) | |
637 | controllers->release(); | |
638 | if( mapped) | |
639 | mapped->release(); | |
640 | ||
641 | return( ok ); | |
642 | } | |
643 | ||
644 | /* | |
645 | */ | |
646 | ||
647 | static const char * | |
648 | CompareKey( OSString * key, | |
649 | const IORegistryEntry * table, const OSSymbol * propName ) | |
650 | { | |
651 | OSObject *prop; | |
652 | OSData *data; | |
653 | OSString *string; | |
654 | const char *ckey; | |
655 | UInt32 keyLen; | |
656 | const char *names; | |
657 | const char *lastName; | |
658 | bool wild; | |
659 | bool matched; | |
660 | const char *result = 0; | |
661 | ||
662 | if( 0 == (prop = table->getProperty( propName ))) | |
663 | return( 0 ); | |
664 | ||
665 | if( (data = OSDynamicCast( OSData, prop ))) { | |
666 | names = (const char *) data->getBytesNoCopy(); | |
667 | lastName = names + data->getLength(); | |
668 | } else if( (string = OSDynamicCast( OSString, prop ))) { | |
669 | names = string->getCStringNoCopy(); | |
670 | lastName = names + string->getLength() + 1; | |
671 | } else | |
672 | return( 0 ); | |
673 | ||
674 | ckey = key->getCStringNoCopy(); | |
675 | keyLen = key->getLength(); | |
676 | wild = ('*' == key->getChar( keyLen - 1 )); | |
677 | ||
678 | do { | |
679 | // for each name in the property | |
680 | if( wild) | |
681 | matched = (0 == strncmp( ckey, names, keyLen - 1 )); | |
682 | else | |
683 | matched = (keyLen == strlen( names )) | |
684 | && (0 == strncmp( ckey, names, keyLen )); | |
685 | ||
686 | if( matched) | |
687 | result = names; | |
688 | ||
689 | names = names + strlen( names) + 1; | |
690 | ||
691 | } while( (names < lastName) && (false == matched)); | |
692 | ||
693 | return( result); | |
694 | } | |
695 | ||
696 | ||
697 | bool IODTCompareNubName( const IORegistryEntry * regEntry, | |
698 | OSString * name, OSString ** matchingName ) | |
699 | { | |
700 | const char *result; | |
701 | bool matched; | |
702 | ||
703 | matched = (0 != (result = CompareKey( name, regEntry, gIODTNameKey))) | |
704 | || (0 != (result = CompareKey( name, regEntry, gIODTCompatibleKey))) | |
705 | || (0 != (result = CompareKey( name, regEntry, gIODTTypeKey))) | |
706 | || (0 != (result = CompareKey( name, regEntry, gIODTModelKey))); | |
707 | ||
708 | if( result && matchingName) | |
709 | *matchingName = OSString::withCString( result ); | |
710 | ||
711 | return( result != 0 ); | |
712 | } | |
713 | ||
714 | bool IODTMatchNubWithKeys( IORegistryEntry * regEntry, | |
715 | const char * keys ) | |
716 | { | |
717 | OSObject *obj; | |
718 | bool result = false; | |
719 | ||
720 | obj = OSUnserialize( keys, 0 ); | |
721 | ||
722 | if( obj) { | |
723 | result = regEntry->compareNames( obj ); | |
724 | obj->release(); | |
725 | } | |
726 | #ifdef DEBUG | |
727 | else IOLog("Couldn't unserialize %s\n", keys ); | |
728 | #endif | |
729 | ||
730 | return( result ); | |
731 | } | |
732 | ||
733 | OSCollectionIterator * IODTFindMatchingEntries( IORegistryEntry * from, | |
734 | IOOptionBits options, const char * keys ) | |
735 | { | |
736 | OSSet *result = 0; | |
737 | IORegistryEntry *next; | |
738 | IORegistryIterator *iter; | |
739 | OSCollectionIterator *cIter; | |
740 | bool cmp; | |
741 | bool minus = options & kIODTExclusive; | |
742 | ||
743 | ||
744 | iter = IORegistryIterator::iterateOver( from, gIODTPlane, | |
745 | (options & kIODTRecursive) ? kIORegistryIterateRecursively : 0 ); | |
746 | if( iter) { | |
747 | ||
748 | do { | |
749 | ||
750 | if( result) | |
751 | result->release(); | |
752 | result = OSSet::withCapacity( 3 ); | |
753 | if( !result) | |
754 | break; | |
755 | ||
756 | iter->reset(); | |
757 | while( (next = iter->getNextObject())) { | |
758 | ||
759 | // Look for existence of a debug property to skip | |
760 | if( next->getProperty("AAPL,ignore")) | |
761 | continue; | |
762 | ||
763 | if( keys) { | |
764 | cmp = IODTMatchNubWithKeys( next, keys ); | |
765 | if( (minus && (false == cmp)) | |
766 | || ((false == minus) && (false != cmp)) ) | |
767 | result->setObject( next); | |
768 | } else | |
769 | result->setObject( next); | |
770 | } | |
771 | } while( !iter->isValid()); | |
772 | ||
773 | iter->release(); | |
774 | } | |
775 | ||
776 | cIter = OSCollectionIterator::withCollection( result); | |
777 | result->release(); | |
778 | ||
779 | return( cIter); | |
780 | } | |
781 | ||
782 | ||
783 | struct IODTPersistent { | |
784 | IODTCompareAddressCellFunc compareFunc; | |
785 | IODTNVLocationFunc locationFunc; | |
786 | }; | |
787 | ||
788 | void IODTSetResolving( IORegistryEntry * regEntry, | |
789 | IODTCompareAddressCellFunc compareFunc, | |
790 | IODTNVLocationFunc locationFunc ) | |
791 | { | |
792 | IODTPersistent persist; | |
793 | OSData *prop; | |
794 | ||
795 | persist.compareFunc = compareFunc; | |
796 | persist.locationFunc = locationFunc; | |
797 | prop = OSData::withBytes( &persist, sizeof( persist)); | |
798 | if( !prop) | |
799 | return; | |
800 | ||
801 | regEntry->setProperty( gIODTPersistKey, prop); | |
802 | prop->release(); | |
803 | return; | |
804 | } | |
805 | ||
806 | static SInt32 DefaultCompare( UInt32 cellCount, UInt32 left[], UInt32 right[] ) | |
807 | { | |
808 | cellCount--; | |
809 | return( left[ cellCount ] - right[ cellCount ] ); | |
810 | } | |
811 | ||
812 | ||
813 | void IODTGetCellCounts( IORegistryEntry * regEntry, | |
814 | UInt32 * sizeCount, UInt32 * addressCount) | |
815 | { | |
816 | if( !GetUInt32( regEntry, gIODTSizeCellKey, sizeCount)) | |
817 | *sizeCount = 1; | |
818 | if( !GetUInt32( regEntry, gIODTAddressCellKey, addressCount)) | |
819 | *addressCount = 2; | |
820 | return; | |
821 | } | |
822 | ||
823 | // Given addr & len cells from our child, find it in our ranges property, then | |
824 | // look in our parent to resolve the base of the range for us. | |
825 | ||
826 | // Range[]: child-addr our-addr child-len | |
827 | // #cells: child ours child | |
828 | ||
829 | bool IODTResolveAddressCell( IORegistryEntry * regEntry, | |
830 | UInt32 cellsIn[], | |
831 | IOPhysicalAddress * phys, IOPhysicalLength * len ) | |
832 | { | |
833 | IORegistryEntry *parent; | |
834 | OSData *prop; | |
835 | // cells in addresses at regEntry | |
836 | UInt32 sizeCells, addressCells; | |
837 | // cells in addresses below regEntry | |
838 | UInt32 childSizeCells, childAddressCells; | |
839 | UInt32 childCells; | |
840 | UInt32 cell[ 5 ], offset = 0, length; | |
841 | UInt32 *range; | |
842 | UInt32 *endRanges; | |
843 | bool ok = true; | |
844 | SInt32 diff; | |
845 | ||
846 | IODTPersistent *persist; | |
847 | IODTCompareAddressCellFunc compare; | |
848 | ||
849 | IODTGetCellCounts( regEntry, &childSizeCells, &childAddressCells ); | |
850 | childCells = childAddressCells + childSizeCells; | |
851 | ||
852 | bcopy( cellsIn, cell, 4 * childCells ); | |
853 | if( childSizeCells > 1) | |
854 | *len = IOPhysical32( cellsIn[ childAddressCells ], | |
855 | cellsIn[ childAddressCells + 1 ] ); | |
856 | else | |
857 | *len = IOPhysical32( 0, cellsIn[ childAddressCells ] ); | |
858 | ||
859 | do { | |
860 | prop = OSDynamicCast( OSData, regEntry->getProperty( gIODTRangeKey )); | |
861 | if( 0 == prop) { | |
862 | /* end of the road */ | |
863 | *phys = IOPhysical32( 0, cell[ childAddressCells - 1 ] + offset); | |
864 | break; | |
865 | } | |
866 | ||
867 | parent = regEntry->getParentEntry( gIODTPlane ); | |
868 | IODTGetCellCounts( parent, &sizeCells, &addressCells ); | |
869 | ||
870 | if( (length = prop->getLength())) { | |
871 | // search | |
872 | range = (UInt32 *) prop->getBytesNoCopy(); | |
873 | endRanges = range + (length / 4); | |
874 | ||
875 | prop = (OSData *) regEntry->getProperty( gIODTPersistKey ); | |
876 | if( prop) { | |
877 | persist = (IODTPersistent *) prop->getBytesNoCopy(); | |
878 | compare = persist->compareFunc; | |
879 | } else | |
880 | compare = DefaultCompare; | |
881 | ||
882 | for( ok = false; | |
883 | range < endRanges; | |
884 | range += (childCells + addressCells) ) { | |
885 | ||
886 | // is cell >= range start? | |
887 | diff = (*compare)( childAddressCells, cell, range ); | |
888 | if( diff < 0) | |
889 | continue; | |
890 | ||
891 | // is cell + size <= range end? | |
892 | if( (diff + cell[ childCells - 1 ]) | |
893 | > range[ childCells + addressCells - 1 ]) | |
894 | continue; | |
895 | ||
896 | offset += diff; | |
897 | ok = true; | |
898 | break; | |
899 | } | |
900 | ||
901 | // Get the physical start of the range from our parent | |
902 | bcopy( range + childAddressCells, cell, 4 * addressCells ); | |
903 | bzero( cell + addressCells, 4 * sizeCells ); | |
904 | ||
905 | } /* else zero length range => pass thru to parent */ | |
906 | ||
907 | regEntry = parent; | |
908 | childSizeCells = sizeCells; | |
909 | childAddressCells = addressCells; | |
910 | childCells = childAddressCells + childSizeCells; | |
911 | ||
912 | } while( ok && regEntry); | |
913 | ||
914 | return( ok); | |
915 | } | |
916 | ||
917 | ||
918 | OSArray * IODTResolveAddressing( IORegistryEntry * regEntry, | |
919 | const char * addressPropertyName, | |
920 | IODeviceMemory * parent ) | |
921 | { | |
922 | IORegistryEntry *parentEntry; | |
923 | OSData *addressProperty; | |
924 | UInt32 sizeCells, addressCells, cells; | |
925 | int i, num; | |
926 | UInt32 *reg; | |
927 | IOPhysicalAddress phys; | |
928 | IOPhysicalLength len; | |
929 | OSArray *array; | |
930 | IODeviceMemory *range; | |
931 | ||
932 | parentEntry = regEntry->getParentEntry( gIODTPlane ); | |
933 | addressProperty = (OSData *) regEntry->getProperty( addressPropertyName ); | |
934 | if( (0 == addressProperty) || (0 == parentEntry)) | |
935 | return( 0); | |
936 | ||
937 | IODTGetCellCounts( parentEntry, &sizeCells, &addressCells ); | |
938 | if( 0 == sizeCells) | |
939 | return( 0); | |
940 | ||
941 | cells = sizeCells + addressCells; | |
942 | reg = (UInt32 *) addressProperty->getBytesNoCopy(); | |
943 | num = addressProperty->getLength() / (4 * cells); | |
944 | ||
945 | array = OSArray::withCapacity( 1 ); | |
946 | if( 0 == array) | |
947 | return( 0); | |
948 | ||
949 | for( i = 0; i < num; i++) { | |
950 | if( IODTResolveAddressCell( parentEntry, reg, &phys, &len )) { | |
951 | range = 0; | |
952 | if( parent) | |
953 | range = IODeviceMemory::withSubRange( parent, | |
954 | phys - parent->getPhysicalAddress(), len ); | |
955 | if( 0 == range) | |
956 | range = IODeviceMemory::withRange( phys, len ); | |
957 | if( range) | |
958 | array->setObject( range ); | |
959 | } | |
960 | reg += cells; | |
961 | } | |
962 | ||
963 | regEntry->setProperty( gIODeviceMemoryKey, array); | |
964 | array->release(); /* ??? */ | |
965 | ||
966 | return( array); | |
967 | } | |
968 | ||
969 | static void IODTGetNVLocation( | |
970 | IORegistryEntry * parent, | |
971 | IORegistryEntry * regEntry, | |
972 | UInt8 * busNum, UInt8 * deviceNum, UInt8 * functionNum ) | |
973 | { | |
974 | ||
975 | OSData *prop; | |
976 | IODTPersistent *persist; | |
977 | UInt32 *cell; | |
978 | ||
979 | prop = (OSData *) parent->getProperty( gIODTPersistKey ); | |
980 | if( prop) { | |
981 | persist = (IODTPersistent *) prop->getBytesNoCopy(); | |
982 | (*persist->locationFunc)( regEntry, busNum, deviceNum, functionNum ); | |
983 | } else { | |
984 | prop = (OSData *) regEntry->getProperty( "reg" ); | |
985 | *functionNum = 0; | |
986 | if( prop) { | |
987 | cell = (UInt32 *) prop->getBytesNoCopy(); | |
988 | *busNum = 3; | |
989 | *deviceNum = 0x1f & (cell[ 0 ] >> 24); | |
990 | } else { | |
991 | *busNum = 0; | |
992 | *deviceNum = 0; | |
993 | } | |
994 | } | |
995 | return; | |
996 | } | |
997 | ||
998 | /* | |
999 | * Try to make the same messed up descriptor as Mac OS | |
1000 | */ | |
1001 | ||
1002 | IOReturn IODTMakeNVDescriptor( IORegistryEntry * regEntry, | |
1003 | IONVRAMDescriptor * hdr ) | |
1004 | { | |
1005 | IORegistryEntry *parent; | |
1006 | UInt32 level; | |
1007 | UInt32 bridgeDevices; | |
1008 | UInt8 busNum; | |
1009 | UInt8 deviceNum; | |
1010 | UInt8 functionNum; | |
1011 | ||
1012 | hdr->format = 1; | |
1013 | hdr->marker = 0; | |
1014 | ||
1015 | for(level = 0, bridgeDevices = 0; | |
1016 | (parent = regEntry->getParentEntry( gIODTPlane )) && (level < 7); level++ ) { | |
1017 | ||
1018 | IODTGetNVLocation( parent, regEntry, | |
1019 | &busNum, &deviceNum, &functionNum ); | |
1020 | if( level) | |
1021 | bridgeDevices |= ((deviceNum & 0x1f) << ((level - 1) * 5)); | |
1022 | else { | |
1023 | hdr->busNum = busNum; | |
1024 | hdr->deviceNum = deviceNum; | |
1025 | hdr->functionNum = functionNum; | |
1026 | } | |
1027 | regEntry = parent; | |
1028 | } | |
1029 | hdr->bridgeCount = level - 2; | |
1030 | hdr->bridgeDevices = bridgeDevices; | |
1031 | ||
1032 | return( kIOReturnSuccess ); | |
1033 | } | |
1034 | ||
1035 | OSData * IODTFindSlotName( IORegistryEntry * regEntry, UInt32 deviceNumber ) | |
1036 | { | |
1037 | IORegistryEntry *parent; | |
1038 | OSData *data; | |
1039 | OSData *ret = 0; | |
1040 | UInt32 *bits; | |
1041 | UInt32 i; | |
1042 | char *names; | |
1043 | char *lastName; | |
1044 | UInt32 mask; | |
1045 | ||
1046 | data = (OSData *) regEntry->getProperty("AAPL,slot-name"); | |
1047 | if( data) | |
1048 | return( data); | |
1049 | parent = regEntry->getParentEntry( gIODTPlane ); | |
1050 | if( !parent) | |
1051 | return( 0 ); | |
1052 | data = OSDynamicCast( OSData, parent->getProperty("slot-names")); | |
1053 | if( !data) | |
1054 | return( 0 ); | |
1055 | if( data->getLength() <= 4) | |
1056 | return( 0 ); | |
1057 | ||
1058 | bits = (UInt32 *) data->getBytesNoCopy(); | |
1059 | mask = *bits; | |
1060 | if( (0 == (mask & (1 << deviceNumber)))) | |
1061 | return( 0 ); | |
1062 | ||
1063 | names = (char *)(bits + 1); | |
1064 | lastName = names + (data->getLength() - 4); | |
1065 | ||
1066 | for( i = 0; (i <= deviceNumber) && (names < lastName); i++ ) { | |
1067 | ||
1068 | if( mask & (1 << i)) { | |
1069 | if( i == deviceNumber) { | |
1070 | data = OSData::withBytesNoCopy( names, 1 + strlen( names)); | |
1071 | if( data) { | |
1072 | regEntry->setProperty("AAPL,slot-name", data); | |
1073 | ret = data; | |
1074 | data->release(); | |
1075 | } | |
1076 | } else | |
1077 | names += 1 + strlen( names); | |
1078 | } | |
1079 | } | |
1080 | ||
1081 | return( ret ); | |
1082 | } |