Source to driverkit/ppc/autoconf_ppc.m
/*
* Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
* Reserved. 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 1.0 (the 'License'). You may not use this file
* except in compliance with the License. Please obtain a copy of the
* License at http://www.apple.com/publicsource 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 OR NON-INFRINGEMENT. Please see the
* License for the specific language governing rights and limitations
* under the License."
*
* @APPLE_LICENSE_HEADER_END@
*/
/*
* Copyright (c) 1993 NeXT Computer, Inc.
*
* PPC Driverkit configuration.
*
* HISTORY
*
* 10 Dec 1998 Adam Wang
* Added USB matching
* 27 Oct 1997 Simon Douglas
* Added support for tree probe, name matching.
* 28 June 1994
* Derived from i386 version.
* 28 Jan 1993 Brian Pinkerton at NeXT
* Added support for new, MI autoconf logic, and new driverkit API.
* 10 Nov 1992 Brian Pinkerton at NeXT
* Added indirect probe logic.
* 20 Aug 1992 Joe Pasqua
* Init intrReq & intrAttached of kern_dev in configure_device_class().
* 3 August 1992 ? at NeXT
* Created.
*/
#import <mach/mach_types.h>
#import <machkit/NXLock.h>
#import <objc/List.h>
#import <driverkit/KernDeviceDescription.h>
#import <driverkit/KernStringList.h>
#import <driverkit/ppc/IOPPCDeviceDescription.h>
#import <driverkit/IODeviceDescriptionPrivate.h>
#import <driverkit/IODirectDevice.h>
#import <driverkit/IODeviceKernPrivate.h>
#import <driverkit/IODeviceParams.h>
#import <driverkit/Device_ddm.h>
#import <driverkit/configTableKern.h>
#import <driverkit/KernDevice.h>
#import <driverkit/IOConfigTable.h>
#import <driverkit/driverTypesPrivate.h>
#import <driverkit/autoconfCommon.h>
#import <driverkit/ppc/PPCKernBus.h>
#import <driverkit/ppc/PPCKernBusPrivate.h>
#import <machdep/ppc/kernBootStruct.h>
#import <machdep/ppc/DeviceTree.h>
#import <driverkit/ppc/IOTreeDevice.h>
/* Used for machine determination */
#import <machdep/ppc/powermac.h>
#import <machdep/ppc/powermac_gestalt.h>
#import <string.h>
// config tables passed by booter in /AAPL,loadables dir
static char ** allConfigTables;
static int numConfigTables;
#define BUS_LOAD_PRI_PROP "\"Load Priority\" = \"10000\";"
// for debugging
#define ENET_LOAD_PRI_PROP "\"Load Priority\" = \"7000\";"
#define VIA_LOAD_PRI_PROP "\"Load Priority\" = \"6800\";"
// slightly higher than default so they come before any PCI SCSI
#define INT_IDE_LOAD_PRI_PROP "\"Load Priority\" = \"5050\";"
#define INT_SCSI_LOAD_PRI_PROP "\"Load Priority\" = \"5100\";"
#define EXT_SCSI_LOAD_PRI_PROP "\"Load Priority\" = \"5200\";"
#define DISPLAY_LOAD_PRI 6000
#define DEFAULT_LOAD_PRI 5000
#define USBCMD_LOAD_PRI_PROP "\"Load Priority\" = \"4000\";"
static const char * inKernelConfigTables =
"\"Bus Class\" = \"PPCKernBus\";"
"\"Class Names\" = \"\";"
"\"Family\" = \"Bus\";"
"\"Instance\" = \"0\";"
"\"Version\" = \"5.0\";"
"\0"
#if MK_HASDRIVERS
"\"Driver Name\" = \"BMacEnet\";"
"\"Matching\" = \"bmac bmac+\";"
ENET_LOAD_PRI_PROP
"\0"
"\"Driver Name\" = \"MaceEnet\";"
"\"Matching\" = \"mace\";"
ENET_LOAD_PRI_PROP
"\0"
"\"Driver Name\" = \"DECchip21041\";"
"\"Matching\" = \"pci1011,14\";"
"\"Share IRQ Levels\" = \"YES\";"
"\"Network Interface\" = \"AUTO\";"
ENET_LOAD_PRI_PROP
"\0"
"\"Driver Name\" = \"Apple96_SCSI\";"
"\"Matching\" = \"53c94\";"
"\"Bus Type\" = \"PPC\";" // why do they look for it?
EXT_SCSI_LOAD_PRI_PROP
"\0"
"\"Driver Name\" = \"AppleMesh_SCSI\";"
"\"Matching\" = \"mesh\";"
"\"Bus Type\" = \"PPC\";" // why do they look for it?
INT_SCSI_LOAD_PRI_PROP
"\0"
"\"Family\" = \"Disk\";"
"\"Driver Name\" = \"EIDE\";"
"\"Matching\" = \"ide ata ATA pci1095,646\";"
"\"Class Names\" = \"IdeController AtapiController\";"
"\"Block Major\" = \"3\";"
"\"Character Major\" = \"15\";"
INT_IDE_LOAD_PRI_PROP
"\0"
"\"Driver Name\" = \"ApplePMU\";"
"\"Matching\" = \"via-pmu\";"
VIA_LOAD_PRI_PROP
"\0"
"\"Driver Name\" = \"AppleCuda\";"
"\"Matching\" = \"via-cuda\";"
VIA_LOAD_PRI_PROP
"\0"
"\"Driver Name\" = \"AppleUSBCMD\";"
"\"Matching\" = \"pci1095,670\";"
USBCMD_LOAD_PRI_PROP
"\0"
"\"Driver Name\" = \"AppleUSBCMD\";"
"\"Matching\" = \"pci11c1,5801\";"
USBCMD_LOAD_PRI_PROP
"\0"
"\"Driver Name\" = \"AppleUSBCMD\";"
"\"Matching\" = \"pciclass,0c0310\";"
USBCMD_LOAD_PRI_PROP
"\0"
"\"Driver Name\" = \"AppleUSBCMD\";"
"\"Matching\" = \"usb\";"
USBCMD_LOAD_PRI_PROP
"\0"
"\"Driver Name\" = \"AppleOHare\";"
"\"Matching\" = \"pci106b,7\";"
BUS_LOAD_PRI_PROP
"\0"
"\"Driver Name\" = \"Symbios8xx\";"
"\"Matching\" = \"apple53C8xx Apple53C875Card\";"
"\"Class Names\" = \"Sym8xxController\";"
"\"Bus Type\" = \"PPC\";"
"\0"
"\"Driver Name\" = \"AdaptecU2SCSI\";"
"\"Matching\" = \"ADPT,2940U2B ADPT,3950U2B ADPT,2930CU ADPT,2930U\";"
"\"Class Names\" = \"AdaptecU2SCSI\";"
"\"Bus Type\" = \"PPC\";"
INT_SCSI_LOAD_PRI_PROP
"\0"
#endif /* MK_HASDRIVERS */
#if DK_HASDRIVERS
"\"Driver Name\" = \"IOMacRiscPCIBridge\";"
"\"Matching\" = \"bandit\";"
BUS_LOAD_PRI_PROP
"\0"
"\"Driver Name\" = \"IOMacRiscVCIBridge\";"
"\"Matching\" = \"chaos\";"
BUS_LOAD_PRI_PROP
"\0"
"\"Driver Name\" = \"IOGracklePCIBridge\";"
"\"Matching\" = \"grackle\";"
BUS_LOAD_PRI_PROP
"\0"
"\"Driver Name\" = \"IOPCIBridge\";"
"\"Matching\" = \"pci-bridge\";"
BUS_LOAD_PRI_PROP
"\0"
"\"Driver Name\" = \"IONDRVFramebuffer\";" /* shim for ndrv's */
"\"Matching\" = \"display\";" /* matches device_type */
"\"Load Priority\" = \"6000\";"
"\0"
#endif /* DK_HASDRIVERS */
"\"Driver Name\" = \"IODeviceTreeBus\";"
"\"Matching\" = \"cpu cpus l2-cache\";" /* matches device_type */
"\0"
/************ must be last ************/
"\"Driver Name\" = \"IODeviceTreeBus\";"
"\"Matching\" = \"device-tree\";" /* matches any tree "bus" node */
BUS_LOAD_PRI_PROP
"\0"
"\0"
;
void DeviceTreeProbe( void );
static int
MatchDriversForDevice( id ioDevice );
static char *
FindStringKey( const char * config, const char * key, char * value );
IOReturn
PublishDevice( id ioDevice );
static BOOL
StartDriver( IOConfigTable * configTable, id ioDeviceDescription );
BOOL
NewLoadDriver( const char * configData );
static void probe_indirect(const char **indirectNamep, id driverInstance);
const char *findBootConfigString(int n);
static void bootDriverInit(void);
id defaultBus, defaultBusClass;
id autoConfigTables;
/*
* Native indirect driver classes.
*/
char *indirectDevList[] = {
"ATADisk",
"SCSIDisk",
"SCSIGeneric",
"IODiskPartition",
"IOADBBus",
"PPCKeyboard",
"PPCMouse",
NULL
};
/*
* Pseudo device list.
*/
char *pseudoDevList[] = {
"EventDriver",
"kmDevice",
NULL
};
#define SERVER_NAME_KEY "Server Name"
#define CLASS_NAMES_KEY "Class Names"
#define DRIVER_NAME_KEY "Driver Name"
#define BUS_CLASS_KEY "Bus Class"
#define BUS_TYPE_KEY "Bus Type"
#define FAMILY_KEY "Family"
#define BUS_FAMILY "Bus"
#define KERN_BUS_FORMAT "%sKernBus"
#define DEVICE_DESCR_FORMAT "IO%sDeviceDescription"
#define DEFAULT_BUS "PPC"
#define NAME_BUF_LEN 128
static BOOL
versionIsOK(const char *serverName)
{
int version;
return YES;
/*
* Right now, if serverName is NULL, that means the driver
* is compiled into the kernel, so don't check the version.
* This may change later, however.
*/
if (serverName != NULL) {
version = [IODevice
driverKitVersionForDriverNamed:(char *)serverName];
if (version == -1)
version = 310;
/*
* TODO: revisit this policy decision if DriverKit
* compatibility changes in the future.
*/
if (version <= 310) {
IOLog("WARNING: driver %s uses incompatible DriverKit version %d\n", serverName, version);
IOLog("Driver %s could not be configured\n",
serverName);
return NO;
}
}
return YES;
}
#define BUILTIN_BUS_CLASS "PPCKernBus" // XXX
/*
* Eventually, this routine will only initialize the boot device(s) per
* input from the booter via KERNBOOTSTRUCT.
* For now, configure all native devices enumerated in the various
* tables in device_configuration.c.
*/
void
probeNativeDevices(void)
{
int i;
const char *config;
IOConfigTable *configTable = nil;
const char *family, *busName;
id busClass;
BOOL gotBuiltinBus = NO;
autoConfigTables = [[List alloc] init];
//
// Initialize drivers and modules loaded by booter.
bootDriverInit();
//
// Find all bus drivers.
//
for(i=1; (config = findBootConfigString(i)); i++) {
configTable = [IOConfigTable newForConfigData:config];
family = [configTable valueForStringKey:FAMILY_KEY];
if (family && strcmp(family, BUS_FAMILY) == 0) {
busName = [configTable valueForStringKey:BUS_CLASS_KEY];
if (!busName) {
printf("autoconf: missing \"%s\" key\n",
BUS_CLASS_KEY);
continue;
}
if (strcmp(busName, BUILTIN_BUS_CLASS) == 0) {
gotBuiltinBus = YES;
}
busClass = objc_getClass(busName);
(void)[busClass probeBus:configTable];
[configTable freeString:busName];
[configTable freeString:family];
} else {
if (family)
[configTable freeString:family];
[configTable free];
}
}
if (gotBuiltinBus == NO) { // XXX
busClass = objc_getClass(BUILTIN_BUS_CLASS);
(void)[busClass probeBus:nil];
}
defaultBusClass = [KernBus lookupBusClassWithName:DEFAULT_BUS]; // XXX
if (defaultBusClass == nil) {
panic("Missing default kernel bus class");
}
defaultBus = [KernBus lookupBusInstanceWithName:DEFAULT_BUS busId:0];
/*
*/
for( i = 1; (config = findBootConfigString(i)); i++) {
NewLoadDriver( config );
}
/*
* Probe the device tree
*/
DeviceTreeProbe();
}
@protocol OldProbeMethods
+ probe;
+ probe:(int)a deviceMaster:(port_t) b;
@end
/*
* Called from MD probeNativeDevices() and from configureThread(), the
* IOTask version of IOProbeDriver().
*
* Given a class name and an ASCII representation of a config file,
* create a kern_dev and an IODeviceDescription and probe the class.
* This is used both at boot time - for the boot device and (temporarily)
* any native drivers with static config tables - and by IOProbeDriver().
*
* Returns YES if driver was started successfully, else returns NO.
*/
static BOOL
StartDriver( IOConfigTable * configTable, id ioDeviceDescription )
{
const char *className, *classNames;
KernStringList *classNameList = nil;
const char *serverName = NULL;
Class theClass;
id deviceDescription = nil;
id device = nil;
id newDescription;
int index, classesLoaded;
BOOL versionChecked = NO;
const char *placeholder;
const char *busType, *busTypeString;
char *nameBuf;
id busClass, busDescriptionClass;
nameBuf = (char *)IOMalloc(NAME_BUF_LEN);
serverName = [configTable valueForStringKey:SERVER_NAME_KEY];
/*
* Check for a placeholder config table
* that represents a dynamically configured device.
* NOTE: this skips the driverkit version check. XXX
*/
if ((placeholder = [configTable valueForStringKey:"Dynamic"])) {
if (*placeholder == 'Y' || *placeholder == 'y') {
[autoConfigTables addObject:configTable];
[configTable freeString:placeholder];
return YES;
} else {
[configTable freeString:placeholder];
}
}
classNames = (char *)[configTable valueForStringKey:CLASS_NAMES_KEY];
if (classNames == NULL) {
classNames = (char *)[configTable
valueForStringKey:DRIVER_NAME_KEY];
if (classNames == NULL)
goto abort;
}
classNameList = [[KernStringList alloc]
initWithWhitespaceDelimitedString:classNames];
[configTable freeString:classNames];
busTypeString = [configTable valueForStringKey:BUS_TYPE_KEY];
if (busTypeString == NULL || *busTypeString == '\0')
busType = DEFAULT_BUS;
else
busType = busTypeString;
sprintf(nameBuf, KERN_BUS_FORMAT, busType);
busClass = objc_getClass((const char *)nameBuf);
if (busClass == nil)
busClass = defaultBusClass;
classesLoaded = 0;
for (index = 0; index < [classNameList count]; index++) {
className = [classNameList stringAt:index];
theClass = objc_getClass(className);
if (theClass == nil) {
IOLog("configureDriver: "
"driver class '%s' was not loaded\n", className);
if( serverName)
IOLog("Driver %s could not be configured\n", serverName);
goto abort;
}
#if DEBUG
if( ioDeviceDescription)
kprintf("Probing class %s on device %s\n",
className, [ioDeviceDescription nodeName]);
#endif
if (versionChecked == NO) {
/*
* Right now, if serverName is NULL, that means the driver
* is compiled into the kernel, so don't check the version.
* This may change later, however.
*/
if (serverName != NULL) {
if (versionIsOK(serverName) == NO)
goto abort;
}
versionChecked = YES;
}
/*
* Check to see if bus wants to configure driver itself.
*/
if ([busClass configureDriverWithTable:configTable]) {
classesLoaded = 1;
/* Return success. */
break;
}
/*
* Depending on the deviceStyle, we either make
* an DeviceDescription or an PPCDeviceDescription.
*/
switch ([theClass deviceStyle]) {
case IO_DirectDevice:
deviceDescription = [busClass
deviceDescriptionFromConfigTable:configTable];
if (deviceDescription == nil) {
IOLog("configureDriver: initFromConfigTable "
"failed for class %s\n", className);
goto abort;
}
if ([deviceDescription bus] == nil) {
[deviceDescription setBus:defaultBus];
}
if ([[deviceDescription bus] allocateResourcesForDeviceDescription:
deviceDescription] == nil) {
IOLog("configureDriver: could not allocate "
"resources for class %s\n", className);
goto abort;
}
device = [[KernDevice alloc]
initWithDeviceDescription:deviceDescription];
if (device == nil) {
IOLog("configureDriver: initFromDeviceDescription "
"failed for class %s\n", className);
goto abort;
}
[deviceDescription setDevice:device];
if( nil == ioDeviceDescription) {
sprintf(nameBuf, "IO%sDeviceDescription", busType);
busDescriptionClass = objc_getClass((const char *)nameBuf);
newDescription = [[busDescriptionClass alloc]
_initWithDelegate:deviceDescription];
if (newDescription == nil)
goto abort;
} else {
newDescription = ioDeviceDescription;
[newDescription setDelegate:deviceDescription];
}
[newDescription setDevicePort:create_dev_port(device)];
break;
case IO_IndirectDevice:
case IO_PseudoDevice:
deviceDescription = [[KernDeviceDescription alloc]
initFromConfigTable:configTable];
if (deviceDescription == nil) {
goto abort;
}
newDescription = [[IODeviceDescription alloc]
_initWithDelegate:deviceDescription];
if (newDescription == nil)
goto abort;
break;
default:
IOLog("Invalid style for class %s", className);
goto abort;
}
/*
* Now that we have a valid device description for the device,
* probe the sucker.
*/
if (![theClass respondsTo:@selector(probe:)]) {
IOLog("configureDriver: Class %s does not respond "
"to probe:\n", className);
continue;
}
if( [IODevice addLoadedClass:theClass
description:newDescription] == IO_R_SUCCESS) {
classesLoaded++;
}
}
/*
* Only free resources if there was a fatal error before
* addLoadedClass: was called, because the class may cache
* the device description and config table for later use.
*/
IOFree(nameBuf, NAME_BUF_LEN);
[classNameList free];
if (serverName)
[configTable freeString:serverName];
if (busTypeString)
[configTable freeString:busTypeString];
if (classesLoaded) {
return YES;
} else {
[deviceDescription free];
[device free];
return NO;
}
abort:
if (serverName)
[configTable freeString:serverName];
if (busTypeString)
[configTable freeString:busTypeString];
if (deviceDescription) {
[deviceDescription free];
}
if (device) {
[device free];
}
return NO;
}
/*
* probe all indirect drivers associated with specified driver.
* As of mk-149.10, this is only used for IdeDisk.
*/
static void
probe_indirect(const char **indirectNamep, id driverInstance)
{
id driverClass;
if(indirectNamep == NULL) {
return;
}
for(; *indirectNamep; indirectNamep++) {
driverClass = objc_getClass(*indirectNamep);
if(driverClass == nil) {
printf("probe_indirect: Class %s does not exist\n",
*indirectNamep);
continue;
}
if(![driverClass respondsTo:@selector(probe:)]) {
printf("probe_indirect: Class %s does not respond to"
" probe:\n", *indirectNamep);
continue;
}
[driverClass probe:driverInstance];
}
}
IOReturn
PublishDevice( id ioDevice )
{
struct PendingProbe {
struct PendingProbe * next;
struct PendingProbe * prev;
int priority;
id device;
};
struct PendingProbeQueue {
struct PendingProbe * head;
struct PendingProbe * zero;
struct PendingProbe * tail;
};
int priority;
struct PendingProbe * newProbe;
struct PendingProbe * elem;
static struct PendingProbeQueue probeQueue;
static int outstanding = 0;
static probing = 0;
priority = MatchDriversForDevice( ioDevice);
if( priority < 0)
return( IO_R_SUCCESS); // no match, no probe now
if( probing) {
newProbe = IOMalloc( sizeof( struct PendingProbe));
if( newProbe == NULL)
return( IO_R_NO_MEMORY);
newProbe->device = ioDevice;
newProbe->priority = priority;
// queue it behind those with same priority
for( elem = probeQueue.head;
( elem->next && (elem->priority >= priority));
elem = elem->next )
{}
// queue before elem
newProbe->next = elem;
newProbe->prev = elem->prev;
elem->prev->next = newProbe;
elem->prev = newProbe;
outstanding++;
} else {
probing++;
probeQueue.head = (struct PendingProbe *) &probeQueue.zero;
probeQueue.tail = (struct PendingProbe *) &probeQueue.head;
StartDriver( [ioDevice configTable], ioDevice );
while( outstanding) {
// dequeue highest - must be one
newProbe = probeQueue.head;
probeQueue.head = newProbe->next;
newProbe->next->prev = &probeQueue.head;
outstanding--;
ioDevice = newProbe->device;
priority = newProbe->priority;
IOFree( newProbe, sizeof( struct PendingProbe));
// start it
#if DEBUG
kprintf("---- [%d] Driver probe on device %s\n", priority, [ioDevice nodeName] );
#endif
StartDriver( [ioDevice configTable], ioDevice );
}
probing--;
}
return( IO_R_SUCCESS);
}
BOOL
NewLoadDriver( const char * configData )
{
BOOL ret = NO;
id ioDevice;
IOConfigTable * configTable;
char * tryMatch;
char * tryLoc;
char * location;
enum { kStringSize = 128 };
id * failedList = nil;
int numFailed = 0;
int i;
tryMatch = IOMalloc( kStringSize * 2);
if( ! tryMatch)
return( NO);
tryLoc = tryMatch + kStringSize;
if( FindStringKey( configData, "\"Location\"", tryLoc) && tryLoc[0] )
location = tryLoc;
else
location = NULL;
if( FindStringKey( configData, "\"Matching\"", tryMatch)) {
while( (ioDevice = [IOTreeDevice findMatchingDevice:tryMatch location:location]) ) {
// Start the driver on all matching devices
[ioDevice taken:YES];
[[ioDevice propertyTable] addConfigData:configData];
#if DEBUG
kprintf("---- Driver probe on device %s\n", [ioDevice nodeName] );
#endif
ret = StartDriver( [ioDevice configTable], ioDevice );
if( NO == ret) {
numFailed++;
failedList = realloc( failedList, numFailed * sizeof( id));
if( failedList)
failedList[ numFailed - 1 ] = ioDevice;
}
}
if( failedList) {
for( i = 0; i < numFailed; i++ )
[failedList[ i ] taken:NO];
free( failedList );
}
} else {
configTable = [IOConfigTable newForConfigData:configData];
if( configTable) {
ret = StartDriver( configTable, nil );
if( ret == NO)
[configTable free];
}
}
IOFree( tryMatch, kStringSize * 2);
return( ret);
}
static int