AppleFlashNVRAM.cpp [plain text]
#include <IOKit/IOLib.h>
#include <IOKit/IODeviceMemory.h>
#include "AppleFlashNVRAM.h"
#define __FLASH_NVRAM_DEBUG__ 0
OSDefineMetaClassAndAbstractStructors( AppleFlashNVRAM, IONVRAMController );
bool AppleFlashNVRAM::start( IOService* provider )
{
IOMemoryMap* nvramMemoryMap;
unsigned long gen1;
unsigned long gen2;
if ( ( nvramMemoryMap = provider->mapDeviceMemoryWithIndex( 0 ) ) == 0 )
{
return( false );
}
mNVRAMBaseAddress = ( unsigned char * ) nvramMemoryMap->getVirtualAddress();
if ( ( mNVRAMShadow = ( unsigned char * ) IOMalloc( kAppleFlashNVRAMSize ) ) == 0 )
{
return( false );
}
mCommandGate = IOCommandGate::commandGate( this, sDispatchInternal );
getWorkLoop()->addEventSource( mCommandGate );
gen1 = validateGeneration( mNVRAMBaseAddress + kAppleFlashNVRAMAreaAOffset );
gen2 = validateGeneration( mNVRAMBaseAddress + kAppleFlashNVRAMAreaBOffset );
if ( gen1 > gen2 )
{
mNVRAMCurrent = mNVRAMBaseAddress + kAppleFlashNVRAMAreaAOffset;
mNVRAMNext = mNVRAMBaseAddress + kAppleFlashNVRAMAreaBOffset;
}
else
{
mNVRAMCurrent = mNVRAMBaseAddress + kAppleFlashNVRAMAreaBOffset;
mNVRAMNext = mNVRAMBaseAddress + kAppleFlashNVRAMAreaAOffset;
}
bcopy( ( const void * ) mNVRAMCurrent, mNVRAMShadow, kAppleFlashNVRAMSize );
return( IONVRAMController::start( provider ) );
}
IOReturn AppleFlashNVRAM::sDispatchInternal( OSObject* owner, void* arg0, void* arg1, void* arg2, void* arg3 )
{
AppleFlashNVRAM* self;
IOReturn result = kIOReturnSuccess;
int selector;
self = ( AppleFlashNVRAM * ) owner;
selector = ( int ) arg0;
switch ( selector )
{
case kNVRAMReadCommand:
{
result = self->readInternal( ( IOByteCount ) arg1, ( UInt8 * ) arg2, ( IOByteCount ) arg3 );
break;
}
case kNVRAMWriteCommand:
{
result = self->writeInternal( ( IOByteCount ) arg1, ( UInt8 * ) arg2, ( IOByteCount ) arg3 );
break;
}
case kNVRAMSyncCommand:
{
result = self->syncInternal();
break;
}
}
return( result );
}
IOReturn AppleFlashNVRAM::readInternal( IOByteCount offset, UInt8* buffer, IOByteCount length )
{
if ( mNVRAMShadow == NULL )
{
return( kIOReturnNotReady );
}
if ( ( buffer == NULL ) || ( length == 0 ) || ( offset > kAppleFlashNVRAMSize ) || ( offset >= kAppleFlashNVRAMSize ) ||
( ( offset + length ) > kAppleFlashNVRAMSize ) )
{
return( kIOReturnBadArgument );
}
bcopy( mNVRAMShadow + offset, buffer, length );
return( kIOReturnSuccess );
}
IOReturn AppleFlashNVRAM::writeInternal( IOByteCount offset, UInt8* buffer, IOByteCount length )
{
if ( mNVRAMShadow == NULL )
{
return( kIOReturnNotReady );
}
if ( ( buffer == NULL ) || ( length == 0 ) || ( offset > kAppleFlashNVRAMSize ) || ( offset >= kAppleFlashNVRAMSize ) ||
( ( offset + length ) > kAppleFlashNVRAMSize ) )
{
return( kIOReturnBadArgument );
}
bcopy( buffer, mNVRAMShadow + offset, length );
return( kIOReturnSuccess );
}
IOReturn AppleFlashNVRAM::syncInternal( void )
{
AppleFlashNVRAMHeader* header;
IOReturn result;
unsigned char* tmpExchangeBuffer;
unsigned long generation;
unsigned long gen1;
unsigned long gen2;
gen1 = validateGeneration( mNVRAMBaseAddress + kAppleFlashNVRAMAreaAOffset );
gen2 = validateGeneration( mNVRAMBaseAddress + kAppleFlashNVRAMAreaBOffset );
if ( gen1 > gen2 )
{
mNVRAMCurrent = mNVRAMBaseAddress + kAppleFlashNVRAMAreaAOffset;
mNVRAMNext = mNVRAMBaseAddress + kAppleFlashNVRAMAreaBOffset;
}
else
{
mNVRAMCurrent = mNVRAMBaseAddress + kAppleFlashNVRAMAreaBOffset;
mNVRAMNext = mNVRAMBaseAddress + kAppleFlashNVRAMAreaAOffset;
}
generation = max( gen1, gen2 );
if ( !bcmp( mNVRAMShadow, ( const void * ) mNVRAMCurrent, kAppleFlashNVRAMSize ) )
{
return( kIOReturnSuccess );
}
header = ( AppleFlashNVRAMHeader * ) mNVRAMShadow;
header->generation = ++generation;
header->checksum = chrpCheckSum( mNVRAMShadow );
header->adler32 = adler32( mNVRAMShadow + kAppleFlashNVRAMAdlerStart, kAppleFlashNVRAMAdlerSize );
if ( ( result = eraseBlock() ) != kIOReturnSuccess )
{
return( result );
}
if ( ( result = writeBlock( mNVRAMShadow ) ) != kIOReturnSuccess )
{
return( result );
}
tmpExchangeBuffer = ( unsigned char * ) mNVRAMCurrent;
mNVRAMCurrent = mNVRAMNext;
mNVRAMNext = tmpExchangeBuffer;
return( result );
}
void AppleFlashNVRAM::sync( void )
{
mCommandGate->runCommand( ( void * ) kNVRAMSyncCommand, NULL, NULL, NULL );
}
IOReturn AppleFlashNVRAM::read( IOByteCount offset, UInt8* buffer, IOByteCount length )
{
return( mCommandGate->runCommand( ( void * ) kNVRAMReadCommand, ( void * ) offset, ( void * ) buffer, ( void * ) length ) );
}
IOReturn AppleFlashNVRAM::write( IOByteCount offset, UInt8* buffer, IOByteCount length )
{
return( mCommandGate->runCommand( ( void * ) kNVRAMWriteCommand, ( void * ) offset, ( void * ) buffer, ( void * ) length ) );
}
unsigned long AppleFlashNVRAM::validateGeneration( unsigned char* nvramBuffer )
{
AppleFlashNVRAMHeader* header = ( AppleFlashNVRAMHeader * ) nvramBuffer;
if ( header->signature != kAppleFlashNVRAMSignature )
{
return( 0 );
}
if ( header->checksum != chrpCheckSum( nvramBuffer ) )
{
return( 0 );
}
if ( header->adler32 != adler32( nvramBuffer + kAppleFlashNVRAMAdlerStart, kAppleFlashNVRAMAdlerSize ) )
{
return( 0 );
}
return( header->generation );
}
unsigned char AppleFlashNVRAM::chrpCheckSum( unsigned char* buffer )
{
long cnt;
unsigned char c_sum;
c_sum = 0;
for ( cnt = 0; cnt < 16; cnt++ )
{
unsigned char i_sum;
if ( cnt == 1 )
continue;
i_sum = c_sum + buffer[ cnt ];
if ( i_sum < c_sum )
{
i_sum++;
}
c_sum = i_sum;
}
return( c_sum );
}
unsigned long AppleFlashNVRAM::adler32( unsigned char* buffer, long length )
{
unsigned long result;
unsigned long lowHalf;
unsigned long highHalf;
long cnt;
lowHalf = 1;
highHalf = 0;
for ( cnt = 0; cnt < length; cnt++ )
{
if ( ( cnt % 5000 ) == 0 )
{
lowHalf %= 65521L;
highHalf %= 65521L;
}
lowHalf += buffer[ cnt ];
highHalf += lowHalf;
}
lowHalf %= 65521L;
highHalf %= 65521L;
result = ( highHalf << 16 ) | lowHalf;
return( result );
}
OSDefineMetaClassAndStructors( AppleFlashNVRAMMicronSharp, AppleFlashNVRAM );
IOReturn AppleFlashNVRAMMicronSharp::eraseBlock( void )
{
IOReturn result;
*mNVRAMNext = kAppleFlashNVRAMMicronSharpEraseSetupCmd;
eieio();
*mNVRAMNext = kAppleFlashNVRAMMicronSharpEraseConfirmCmd;
eieio();
result = waitForCommandDone();
*mNVRAMNext = kAppleFlashNVRAMMicronSharpResetDeviceCmd;
eieio();
if ( result == kIOReturnSuccess )
{
result = verifyEraseBlock();
}
return( result );
}
IOReturn AppleFlashNVRAMMicronSharp::verifyEraseBlock( void )
{
long cnt;
for ( cnt = 0; cnt < kAppleFlashNVRAMSize; cnt++ )
{
if ( mNVRAMNext[ cnt ] != 0xFF )
{
return( kIOReturnInvalid );
}
}
return( kIOReturnSuccess );
}
IOReturn AppleFlashNVRAMMicronSharp::writeBlock( unsigned char* sourceAddress )
{
IOReturn result;
long cnt;
for ( cnt = 0; cnt < kAppleFlashNVRAMSize; cnt++ )
{
mNVRAMNext[ cnt ] = kAppleFlashNVRAMMicronSharpWriteSetupCmd;
eieio();
mNVRAMNext[ cnt ] = sourceAddress[ cnt ];
eieio();
if ( ( result = waitForCommandDone() ) != kIOReturnSuccess )
break;
}
*mNVRAMNext = kAppleFlashNVRAMMicronSharpResetDeviceCmd;
eieio();
if ( result == kIOReturnSuccess )
{
result = verifyWriteBlock( sourceAddress );
}
return( result );
}
IOReturn AppleFlashNVRAMMicronSharp::verifyWriteBlock( unsigned char* sourceAddress )
{
long cnt;
for ( cnt = 0; cnt < kAppleFlashNVRAMSize; cnt++ )
{
if ( mNVRAMNext[ cnt ] != sourceAddress[ cnt ] )
{
return( kIOReturnInvalid );
}
}
return( kIOReturnSuccess );
}
IOReturn AppleFlashNVRAMMicronSharp::waitForCommandDone( void )
{
unsigned char status;
do
{
status = *mNVRAMNext;
eieio();
}
while ( ( status & kAppleFlashNVRAMMicronSharpStatusRegCompletionMask ) == 0 );
if ( status & kAppleFlashNVRAMMicronSharpStatusRegErrorMask )
{
return( kIOReturnInvalid );
}
return( kIOReturnSuccess );
}
OSDefineMetaClassAndStructors( AppleFlashNVRAMAMD, AppleFlashNVRAM );
IOReturn AppleFlashNVRAMAMD::eraseBlock( void )
{
static unsigned short commandAddrList[] = {
kAppleFlashNVRAMAMDCmdAddr1, kAppleFlashNVRAMAMDUnlockBypass1Cmd,
kAppleFlashNVRAMAMDCmdAddr2, kAppleFlashNVRAMAMDUnlockBypass2Cmd,
kAppleFlashNVRAMAMDCmdAddr1, kAppleFlashNVRAMAMDEraseSetupCmd,
kAppleFlashNVRAMAMDCmdAddr1, kAppleFlashNVRAMAMDUnlockBypass1Cmd,
kAppleFlashNVRAMAMDCmdAddr2, kAppleFlashNVRAMAMDUnlockBypass2Cmd,
0x0, kAppleFlashNVRAMAMDEraseConfirmCmd,
};
IOReturn result;
unsigned long i;
for ( i = 0; i < ( sizeof( commandAddrList ) / ( 2 * sizeof( unsigned short ) ) ); i++ )
{
*( mNVRAMNext + commandAddrList[ 2 * i ] ) = commandAddrList[ ( 2 * i ) + 1 ];
eieio();
}
IODelay( 55 );
result = waitForCommandDone();
if ( result == kIOReturnSuccess )
{
result = verifyEraseBlock();
}
return( result );
}
IOReturn AppleFlashNVRAMAMD::verifyEraseBlock( void )
{
long cnt;
for ( cnt = 0; cnt < kAppleFlashNVRAMSize; cnt++ )
{
if ( mNVRAMNext[ cnt ] != 0xFF )
{
#if __FLASH_NVRAM_DEBUG__
IOLog( "AppleFlashNVRAMAMD::verifyEraseBlock - Byte %d is not 0xFF!\n", ( int ) cnt );
#endif
return( kIOReturnInvalid );
}
}
return( kIOReturnSuccess );
}
IOReturn AppleFlashNVRAMAMD::writeBlock( unsigned char* sourceAddress )
{
IOReturn result = kIOReturnSuccess;
long cnt;
mNVRAMNext[ kAppleFlashNVRAMAMDCmdAddr1 ] = kAppleFlashNVRAMAMDUnlockBypass1Cmd;
eieio();
mNVRAMNext[ kAppleFlashNVRAMAMDCmdAddr2 ] = kAppleFlashNVRAMAMDUnlockBypass2Cmd;
eieio();
mNVRAMNext[ kAppleFlashNVRAMAMDCmdAddr1 ] = kAppleFlashNVRAMAMDUnlockBypass3Cmd;
eieio();
for ( cnt = 0; cnt < kAppleFlashNVRAMSize; cnt++ )
{
mNVRAMNext[ kAppleFlashNVRAMAMDCmdAddr1 ] = kAppleFlashNVRAMAMDWriteConfirmCmd;
eieio();
mNVRAMNext[ cnt ] = sourceAddress[ cnt ];
eieio();
if ( ( result = waitForCommandDone() ) != kIOReturnSuccess )
break;
}
*mNVRAMNext = kAppleFlashNVRAMAMDUnlockBypassReset1Cmd;
eieio();
*mNVRAMNext = kAppleFlashNVRAMAMDUnlockBypassReset2Cmd;
eieio();
if ( result == kIOReturnSuccess )
{
result = verifyWriteBlock( sourceAddress );
}
return( result );
}
IOReturn AppleFlashNVRAMAMD::verifyWriteBlock( unsigned char* sourceAddress )
{
long cnt;
for ( cnt = 0; cnt < kAppleFlashNVRAMSize; cnt++ )
{
if ( mNVRAMNext[ cnt ] != sourceAddress[ cnt ] )
{
#if __FLASH_NVRAM_DEBUG__
IOLog( "AppleFlashNVRAMAMD::verifyWriteBlock - Byte %d is not correct!\n", ( int ) cnt );
#endif
return( kIOReturnInvalid );
}
}
return( kIOReturnSuccess );
}
IOReturn AppleFlashNVRAMAMD::waitForCommandDone( void )
{
while ( true )
{
unsigned char lastStatus;
unsigned char currentStatus;
lastStatus = *mNVRAMNext;
eieio();
currentStatus = *mNVRAMNext;
eieio();
if ( ( ( lastStatus ^ currentStatus ) & kAppleFlashNVRAMAMDStatusRegCompletionMask ) == 0 )
{
return( kIOReturnSuccess );
}
if ( currentStatus & kAppleFlashNVRAMAMDStatusRegErrorMask )
{
lastStatus = *mNVRAMNext;
eieio();
currentStatus = *mNVRAMNext;
eieio();
if ( ( ( lastStatus ^ currentStatus ) & kAppleFlashNVRAMAMDStatusRegCompletionMask ) == 0 )
{
return( kIOReturnSuccess );
}
else
{
#if __FLASH_NVRAM_DEBUG__
IOLog( "AppleFlashNVRAMAMD::waitForCommandDone - Operation has timed out! RESET-ing chip.\n" );
#endif
*mNVRAMNext = kAppleFlashNVRAMAMDResetDeviceCmd;
eieio();
return( kIOReturnInvalid );
}
}
}
return( kIOReturnInvalid );
}