IONDRV.cpp   [plain text]


/*
 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * The contents of this file constitute Original Code as defined in and
 * are subject to the Apple Public Source License Version 1.1 (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.
 * 
 * This 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@
 */


#include <IOKit/graphics/IOGraphicsPrivate.h>
#include <IOKit/IOLib.h>
#include <libkern/c++/OSContainers.h>

extern "C"
{
#include <pexpert/pexpert.h>
#include <mach/kmod.h>
};

#include "IONDRV.h"

enum
{ 
    kDate2001March1	= 0xb6c49300,
    kIOPEFMinROMDate	= kDate2001March1 
};

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

OSDefineMetaClassAndAbstractStructors(IONDRV, OSObject)

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#if __ppc__

#include "IOPEFLoader.h"

#define LOG		if(1) kprintf

static IOLock  * gIOPEFLock;
static OSArray * gIOPEFContainers;

#define super IONDRV
OSDefineMetaClassAndStructorsWithInit(IOPEFNDRV, IONDRV, IOPEFNDRV::initialize());

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

void IOPEFNDRV::initialize( void )
{
    if (!gIOPEFLock)
	gIOPEFLock = IOLockAlloc();
    if (!gIOPEFContainers)
	gIOPEFContainers = OSArray::withCapacity(2);
}

IOPEFNDRV * IOPEFNDRV::instantiate( IORegistryEntry * regEntry,
				     IOLogicalAddress container,
				     IOByteCount containerSize,
				     bool checkDate,
				     IONDRVUndefinedSymbolHandler undefHandler,
				     void * self )
{
    OSStatus	err = 1;
    IOPEFNDRV *	inst;
    char * 	name;
    IOByteCount	plen;
    UInt32	createDate;
    char	kmodName[KMOD_MAX_NAME * 2];
    char	kmodVers[KMOD_MAX_NAME];

    inst = new IOPEFNDRV;

    if (inst)
    {
        do
        {
            if (false == inst->init())
                continue;

            err = PCodeOpen( (void *)container, containerSize, &inst->fPEFInst, &createDate );
            if (err)
                continue;

            err = PCodeInstantiate( inst->fPEFInst, undefHandler, self );
            if (err)
                continue;

	    if (checkDate && createDate && (createDate < kIOPEFMinROMDate))
	    {
		int	debugFlags;
	
		IOLog("ROM ndrv for %s is too old (0x%08lx)\n", regEntry->getName(), createDate);
		if (!PE_parse_boot_arg("romndrv", &debugFlags) || !debugFlags)
		{
		    err = kIOReturnIsoTooOld;
		    continue;
		}
	    }

            err = inst->getSymbol("DoDriverIO", (IOLogicalAddress *) &inst->fDoDriverIO);
            if (err)
                continue;
            err = inst->getSymbol("TheDriverDescription",
				    (IOLogicalAddress *) &inst->fDriverDesc);
            if (err)
                continue;

	    name = (char *) inst->fDriverDesc->driverOSRuntimeInfo.driverName;
	    plen = name[ 0 ];
	    if (plen >= sizeof(inst->fDriverDesc->driverOSRuntimeInfo.driverName))
		plen = sizeof(inst->fDriverDesc->driverOSRuntimeInfo.driverName) - 1;
	    strncpy( inst->fName, name + 1, plen);
#if 1
	    inst->fName[plen] = 0;
#else
	    sprintf( inst->fName + plen, "-%08lx", *((UInt32 *) &inst->fDriverDesc->driverType.version));
#endif
            name = (char *) inst->fDriverDesc->driverType.nameInfoStr;
            plen = name[ 0 ];
            if (plen >= sizeof(inst->fDriverDesc->driverType.nameInfoStr))
		plen = sizeof(inst->fDriverDesc->driverType.nameInfoStr) - 1;

            strcpy( kmodName, "com.apple.driver.ndrv.");
            strncat( kmodName, name + 1, plen);
            sprintf( kmodVers, ".0x%lx", inst->fDoDriverIO
					    ? (UInt32) inst->fDoDriverIO->pc
					    : (UInt32) container);
            strcat( kmodName, kmodVers);

            {
#define DEVELOPMENT_STAGE 0x20
#define ALPHA_STAGE 0x40
#define BETA_STAGE 0x60
#define RELEASE_STAGE 0x80
                UInt32 vers = *((UInt32 *) &inst->fDriverDesc->driverType.version);
                UInt8  major1, major2, minor1, minor2, build;
                char * s = kmodVers;
                char   c;

                major1 = (vers & 0xF0000000) >> 28;
                major2 = (vers & 0x0F000000) >> 24;
                minor1 = (vers & 0x00F00000) >> 20;
                minor2 = (vers & 0x000F0000) >> 16;
                build  = (vers & 0x000000FF);

                switch ((vers & 0x0000FF00) >> 8)
                {
                    case RELEASE_STAGE:
                        c = 'f';
                        break;
                    case DEVELOPMENT_STAGE:
                        c = 'd';
                        break;
                    case ALPHA_STAGE:
                        c = 'd';
                        break;
                    default:
                    case BETA_STAGE:
                        c = 'b';
                        break;
                }

                if (major1 > 0)
                    s += sprintf(s, "%d", major1);
		s += sprintf(s, "%d.%d.%d", major2, minor1, minor2);
		if (build)
		    s += sprintf(s, "%c%d", c, build);
            }

            if (KERN_SUCCESS != kmod_create_fake_with_address(
                    kmodName, kmodVers,
                    round_page_32((vm_address_t) container), 
                    trunc_page_32((vm_size_t) container + containerSize) 
                        - round_page_32((vm_address_t) container),
                    &inst->fKModID))
                inst->fKModID = 0;
        }
        while (false);
    }
    if (inst && err)
    {
        inst->release();
        inst = 0;
    }

    return (inst);
}

void IOPEFNDRV::free( void )
{

    if (fKModID)
        kmod_destroy_fake(fKModID);

    if (fPEFInst)
        PCodeClose( fPEFInst );

    super::free();
}

IOReturn IOPEFNDRV::getSymbol( const char * symbolName,
                               IOLogicalAddress * address )
{
    OSStatus            err;

    err = PCodeFindExport( fPEFInst, symbolName,
                           (LogicalAddress *)address, NULL );
    if (err)
        *address = 0;

    return (err);
}

extern "C" IOReturn _IONDRVLibrariesInitialize( IOService * provider );
// osfmk/ppc/mappings.h
extern "C" void ignore_zero_fault(boolean_t);

IOReturn IOPEFNDRV::doDriverIO( UInt32 commandID, void * contents,
                                UInt32 commandCode, UInt32 commandKind )
{
    OSStatus			err = kIOReturnSuccess;
    struct DriverInitInfo	initInfo;
    CntrlParam *         	pb;

    if (0 == fDoDriverIO)
        return (kIOReturnUnsupported);

    switch (commandCode)
    {
        case kIONDRVReplaceCommand:
        case kIONDRVInitializeCommand:
            err = _IONDRVLibrariesInitialize( (IOService *) contents );
            if (kIOReturnSuccess != err)
                break;
            /* fall thru */

        case kIONDRVFinalizeCommand:
        case kIONDRVSupersededCommand:
            initInfo.refNum = 0xffff & ((UInt32) this);
            MAKE_REG_ENTRY((&initInfo.deviceEntry), contents)
            contents = &initInfo;
            break;

        case kIONDRVControlCommand:
        case kIONDRVStatusCommand:
        case kIONDRVOpenCommand:
        case kIONDRVCloseCommand:
        case kIONDRVReadCommand:
        case kIONDRVWriteCommand:
        case kIONDRVKillIOCommand:

            pb = (CntrlParam *) contents;
            pb->qLink = 0;
            pb->ioCRefNum = 0xffff & ((UInt32) this);
            break;
    }

    if (kIOReturnSuccess == err)
    {
        commandCode -= kIONDRVOpenCommand - kOpenCommand;

        ignore_zero_fault( true );
        err = CallTVector( /*AddressSpaceID*/ 0, (void *)commandID, contents,
                                              (void *)commandCode, (void *)commandKind, /*p6*/ 0,
                                              fDoDriverIO );
        ignore_zero_fault( false );
    }

    return (err);
}

OSDefineMetaClassAndStructors(IOPEFContainer, OSData);

IOPEFContainer * IOPEFContainer::withData(OSData * data, OSData * description)
{
    IOPEFContainer * inst;

    if (!data || !description)
	return (0);

    inst = new IOPEFContainer;
    if (inst && !inst->initWithBytesNoCopy((void *) data->getBytesNoCopy(),
					    data->getLength()))
    {
	inst->release();
	inst = 0;
    }
    if (inst)
    {
	data->retain();
	inst->fContainer = data;
	description->retain();
	inst->fDescription = description;
    }

    return (inst);
}

void IOPEFContainer::free( void )
{
    if (fContainer)
	fContainer->release();
    if (fDescription)
	fDescription->release();
    OSData::free();
}

bool IOPEFContainer::serialize(OSSerialize *s) const
{
    return (fDescription->serialize(s));
}

IOPEFNDRV * IOPEFNDRV::fromRegistryEntry( IORegistryEntry * regEntry,
				       OSData * newData,
                                       IONDRVUndefinedSymbolHandler handler,
                                       void * self )
{
    IOLogicalAddress	pef = 0;
    IOByteCount		propSize = 0;
    OSData *		prop;
    IOPEFNDRV *		inst;
    unsigned int 	i;
    bool		checkDate;

    if (newData)
    {
	regEntry->removeProperty("AAPL,ndrvInst");

	prop = (OSData *) regEntry->copyProperty("driver,AAPL,MacOS,PowerPC");
	if (prop)
	{
	    IOLockLock(gIOPEFLock);
	    i = gIOPEFContainers->getNextIndexOfObject(prop, 0);
	    if (i != (unsigned int) -1)
		gIOPEFContainers->removeObject(i);
	    IOLockUnlock(gIOPEFLock);
	
	    prop->release();
	}
	prop = newData;
	checkDate = false;
    }
    else
    {
	inst = (IOPEFNDRV *) regEntry->copyProperty("AAPL,ndrvInst");
	if (inst)
	    return (inst);
	prop = (OSData *) regEntry->getProperty("driver,AAPL,MacOS,PowerPC");
	checkDate = true;
    }

    if (prop)
    {
	IOLockLock(gIOPEFLock);
	for (i = 0; (newData = (OSData *) gIOPEFContainers->getObject(i)); i++)
	{
	    if (true && prop->isEqualTo(newData))
	    {
		prop = newData;
		break;
	    }
	}
	IOLockUnlock(gIOPEFLock);
        pef = (IOLogicalAddress) prop->getBytesNoCopy();
        propSize = prop->getLength();
    }

    if (pef)
    {
        inst = IOPEFNDRV::instantiate(regEntry, pef, propSize, checkDate, handler, self);
        if (inst)
	{
	    if (!newData)
	    {
		OSData * description;
		IOPEFContainer * pefData;

		description = OSData::withBytes(inst->fDriverDesc, sizeof(DriverDescription));
		pefData = IOPEFContainer::withData(prop, description);
		if (pefData)
		{
		    IOLockLock(gIOPEFLock);
		    gIOPEFContainers->setObject(pefData);
		    pefData->release();
		    prop = pefData;
		    IOLockUnlock(gIOPEFLock);

		}
		if (description)
		    description->release();
	    }
	    regEntry->setProperty("driver,AAPL,MacOS,PowerPC", prop);
            regEntry->setProperty("AAPL,ndrvInst", inst);
	}
	else if (checkDate)
	    regEntry->removeProperty("driver,AAPL,MacOS,PowerPC");
    }
    else
        inst = 0;

    return (inst);
}

const char * IOPEFNDRV::driverName( void )
{
    return (fName);
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#else  /* __ppc__ */

IOPEFNDRV * IOPEFNDRV::fromRegistryEntry( IORegistryEntry * regEntry,
				       OSData * newData,
                                       IONDRVUndefinedSymbolHandler handler,
                                       void * self )
{
    return (0);
}

#endif /* !__ppc__ */