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