#include <ctype.h>
#include <stdio.h>
#include <sys/time.h>
#include <mach/mach.h>
#include <mach/mach_error.h>
#include <mach/mach_init.h>
#include <IOKit/IOCFPlugIn.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/IOReturn.h>
#include <IOKit/storage/ata/ATASMARTLib.h>
#include <IOKit/storage/IOStorageDeviceCharacteristics.h>
#include <CoreFoundation/CoreFoundation.h>
#define DEBUG 0
#define DEBUG_ALL 0
#define DEBUG_ASSERT_COMPONENT_NAME_STRING "SMARTUnitTest"
#define DEBUG_ASSERT_MESSAGE(componentNameString, \
assertionString, \
exceptionLabelString, \
errorString, \
fileName, \
lineNumber, \
errorCode) \
DebugAssert(componentNameString, \
assertionString, \
exceptionLabelString, \
errorString, \
fileName, \
lineNumber, \
errorCode) \
static void
DebugAssert ( const char * componentNameString,
const char * assertionString,
const char * exceptionLabelString,
const char * errorString,
const char * fileName,
long lineNumber,
int errorCode )
{
if ( ( assertionString != NULL ) && ( *assertionString != '\0' ) )
printf ( "Assertion failed: %s: %s\n", componentNameString, assertionString );
else
printf ( "Check failed: %s:\n", componentNameString );
if ( exceptionLabelString != NULL )
printf ( " %s\n", exceptionLabelString );
if ( errorString != NULL )
printf ( " %s\n", errorString );
if ( fileName != NULL )
printf ( " file: %s\n", fileName );
if ( lineNumber != 0 )
printf ( " line: %ld\n", lineNumber );
if ( errorCode != 0 )
printf ( " error: %d\n", errorCode );
}
#include <AssertMacros.h>
#define kATADefaultSectorSize 512
#define kIOATABlockStorageDeviceClass "IOATABlockStorageDevice"
#define kIOATAFeaturesKey "ATA Features"
static IOReturn
GetServiceObject ( io_service_t * obj );
static IOReturn
PerformSMARTUnitTest ( io_service_t service );
static void
PrintIdentifyData ( IOATASMARTInterface ** smartInterface );
static void
PrintSMARTData ( IOATASMARTInterface ** smartInterface );
int
main ( int argc, const char * argv[] )
{
IOReturn status = kIOReturnSuccess;
io_service_t obj = MACH_PORT_NULL;
status = GetServiceObject ( &obj );
if ( ( status == kIOReturnNoDevice ) || ( obj == MACH_PORT_NULL ) )
{
printf ( "\nNo S.M.A.R.T.-capable devices found for leak testing\n" );
}
require_action ( ( status == kIOReturnSuccess ), ErrorExit, status = -1 );
require_action ( ( obj != MACH_PORT_NULL ), ErrorExit, status = -1 );
status = PerformSMARTUnitTest ( obj );
require_action ( ( status == kIOReturnSuccess ), ReleaseObject, status = -1 );
ReleaseObject:
require ( ( obj != MACH_PORT_NULL ), ErrorExit );
IOObjectRelease ( obj );
obj = MACH_PORT_NULL;
ErrorExit:
return status;
}
static IOReturn
GetServiceObject ( io_service_t * obj )
{
IOReturn err = kIOReturnNoResources;
io_iterator_t iter = MACH_PORT_NULL;
io_object_t service = MACH_PORT_NULL;
err = IOServiceGetMatchingServices ( kIOMasterPortDefault,
IOServiceMatching ( kIOATABlockStorageDeviceClass ),
&iter );
require ( ( err == kIOReturnSuccess ), ErrorExit );
while ( ( service = IOIteratorNext ( iter ) ) != NULL )
{
CFMutableDictionaryRef dict;
CFDictionaryRef deviceCharacteristics;
CFNumberRef features;
UInt32 value;
err = IORegistryEntryCreateCFProperties ( service, &dict, kCFAllocatorDefault, 0 );
check ( err == kIOReturnSuccess );
if ( err != kIOReturnSuccess )
{
err = IOObjectRelease ( service );
continue;
}
if ( CFDictionaryGetValueIfPresent ( dict,
CFSTR ( kIOPropertyDeviceCharacteristicsKey ),
( void * ) &deviceCharacteristics ) == false )
{
continue;
}
if ( CFDictionaryGetValueIfPresent ( deviceCharacteristics,
CFSTR ( kIOATAFeaturesKey ),
( void * ) &features ) == false )
{
continue;
}
if ( CFNumberGetValue ( features, kCFNumberLongType, &value ) == false )
{
continue;
}
if ( ( value & kIOATAFeatureSMART ) != kIOATAFeatureSMART )
{
continue;
}
if ( *obj == MACH_PORT_NULL )
{
*obj = service;
IOObjectRetain ( service );
}
IOObjectRelease ( service );
}
err = IOObjectRelease ( iter );
ErrorExit:
return err;
}
static IOReturn
PerformSMARTUnitTest ( io_service_t service )
{
IOCFPlugInInterface ** cfPlugInInterface = NULL;
IOATASMARTInterface ** smartInterface = NULL;
SInt32 score = 0;
HRESULT herr = S_OK;
IOReturn err = kIOReturnSuccess;
err = IOCreatePlugInInterfaceForService ( service,
kIOATASMARTUserClientTypeID,
kIOCFPlugInInterfaceID,
&cfPlugInInterface,
&score );
require_string ( ( err == kIOReturnSuccess ), ErrorExit,
"IOCreatePlugInInterfaceForService failed" );
herr = ( *cfPlugInInterface )->QueryInterface (
cfPlugInInterface,
CFUUIDGetUUIDBytes ( kIOATASMARTInterfaceID ),
( LPVOID ) &smartInterface );
require_string ( ( herr == S_OK ), DestroyPlugIn,
"QueryInterface failed" );
PrintIdentifyData ( smartInterface );
PrintSMARTData ( smartInterface );
( *smartInterface )->Release ( smartInterface );
smartInterface = NULL;
DestroyPlugIn:
IODestroyPlugInInterface ( cfPlugInInterface );
cfPlugInInterface = NULL;
ErrorExit:
return err;
}
void
PrintIdentifyData ( IOATASMARTInterface ** smartInterface )
{
IOReturn error = kIOReturnSuccess;
UInt8 * buffer = NULL;
UInt32 length = kATADefaultSectorSize;
buffer = ( UInt8 * ) malloc ( kATADefaultSectorSize );
require ( ( buffer != NULL ), ErrorExit );
bzero ( buffer, kATADefaultSectorSize );
error = ( *smartInterface )->GetATAIdentifyData ( smartInterface,
buffer,
kATADefaultSectorSize,
&length );
require_string ( ( error == kIOReturnSuccess ), ErrorExit,
"GetATAIdentifyData failed" );
#if DEBUG_ALL
{
UInt8 * ptr = ( UInt8 * ) buffer;
int index = 0;
printf ( "\n" );
printf ( "Identify Data\n" );
printf ( "---------------------------------------------------------\n" );
printf ( "Offset | Data (in hex)\n" );
printf ( "hex dec | \n" );
printf ( "---------------------------------------------------------\n" );
for ( index = 0; index < kATADefaultSectorSize; index += 16 )
{
printf ( "%03x ", index );
printf ( "%03d | ", index );
printf ( "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
ptr[index+0], ptr[index+1], ptr[index+2], ptr[index+3],
ptr[index+4], ptr[index+5], ptr[index+6], ptr[index+7],
ptr[index+8], ptr[index+9], ptr[index+10], ptr[index+11],
ptr[index+12], ptr[index+13], ptr[index+14], ptr[index+15] );
}
printf ( "\n" );
}
#endif
buffer[94] = 0;
buffer[40] = 0;
printf ( "Model = %s\n", &buffer[54] );
buffer[54] = 0;
printf ( "Firmware = %s\n", &buffer[46] );
printf ( "Serial number = %s\n", &buffer[20] );
free ( buffer );
buffer = NULL;
ErrorExit:
return;
}
void
PrintSMARTData ( IOATASMARTInterface ** smartInterface )
{
IOReturn error = kIOReturnSuccess;
Boolean conditionExceeded = false;
int index = 0;
char buffer[kATADefaultSectorSize];
ATASMARTData smartData;
ATASMARTLogDirectory logDirectory;
bzero ( &smartData, sizeof ( smartData ) );
bzero ( &logDirectory, sizeof ( logDirectory ) );
error = ( *smartInterface )->SMARTEnableDisableOperations ( smartInterface, true );
require_string ( ( error == kIOReturnSuccess ), ErrorExit,
"SMARTEnableDisableOperations failed" );
error = ( *smartInterface )->SMARTEnableDisableAutosave ( smartInterface, true );
require_string ( ( error == kIOReturnSuccess ), ErrorExit,
"SMARTEnableDisableAutosave failed" );
error = ( *smartInterface )->SMARTReturnStatus ( smartInterface, &conditionExceeded );
require_string ( ( error == kIOReturnSuccess ), ErrorExit,
"SMARTReturnStatus failed" );
if ( conditionExceeded )
{
printf ( "SMART condition exceeded, drive will fail soon\n" );
}
else
{
printf ( "SMART condition not exceeded, drive OK\n" );
}
error = ( *smartInterface )->SMARTExecuteOffLineImmediate ( smartInterface, false );
require_string ( ( error == kIOReturnSuccess ), ErrorExit,
"SMARTExecuteOffLineImmediate failed" );
error = ( *smartInterface )->SMARTReadData ( smartInterface, &smartData );
require_string ( ( error == kIOReturnSuccess ), ErrorExit,
"SMARTReadData failed" );
#if DEBUG_ALL
{
UInt8 * ptr = ( UInt8 * ) &smartData;
printf ( "\n" );
printf ( "SMART Data\n" );
printf ( "---------------------------------------------------------\n" );
printf ( "Offset | Data (in hex)\n" );
printf ( "hex dec | \n" );
printf ( "---------------------------------------------------------\n" );
for ( index = 0; index < sizeof ( ATASMARTData ); index += 16 )
{
printf ( "%03x ", index );
printf ( "%03d | ", index );
printf ( "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
ptr[index+0], ptr[index+1], ptr[index+2], ptr[index+3],
ptr[index+4], ptr[index+5], ptr[index+6], ptr[index+7],
ptr[index+8], ptr[index+9], ptr[index+10], ptr[index+11],
ptr[index+12], ptr[index+13], ptr[index+14], ptr[index+15] );
}
printf ( "\n" );
}
#endif
error = ( *smartInterface )->SMARTValidateReadData ( smartInterface, &smartData );
if ( error != kIOReturnSuccess )
{
printf ( "SMARTValidateReadData failed: %s(%x,%d)\n",
mach_error_string ( error ), error, error & 0xFFFFFF );
}
else
{
printf ( "Checksum valid, SMART Data is OK\n" );
}
error = ( *smartInterface )->SMARTReadLogDirectory ( smartInterface, &logDirectory );
if ( error != kIOReturnSuccess )
{
if ( error == kIOReturnUnsupported )
{
printf ( "SMARTReadLogDirectory not supported\n" );
}
else if ( error == kIOReturnNotReadable )
{
printf ( "SMARTReadLogDirectory unreadable\n" );
}
else
{
printf ( "SMARTReadLogDirectory failed: %s(%x,%d)\n", mach_error_string ( error ), error, error & 0xFFFFFF );
}
}
#if DEBUG_ALL
else
{
UInt8 * ptr = ( UInt8 * ) &logDirectory;
printf ( "\n" );
printf ( "SMART Log Directory Data\n" );
printf ( "---------------------------------------------------------\n" );
printf ( "Offset | Data (in hex)\n" );
printf ( "hex dec | \n" );
printf ( "---------------------------------------------------------\n" );
for ( index = 0; index < sizeof ( logDirectory ); index += 16 )
{
printf ( "%03x ", index );
printf ( "%03d | ", index );
printf ( "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
ptr[index+0], ptr[index+1], ptr[index+2], ptr[index+3],
ptr[index+4], ptr[index+5], ptr[index+6], ptr[index+7],
ptr[index+8], ptr[index+9], ptr[index+10], ptr[index+11],
ptr[index+12], ptr[index+13], ptr[index+14], ptr[index+15] );
}
printf ( "\n" );
}
#endif
for ( index = 0; index < kATADefaultSectorSize; index++ )
{
buffer[index] = index;
}
index = 0x81;
error = ( *smartInterface )->SMARTWriteLogAtAddress ( smartInterface, index, ( void * ) buffer, kATADefaultSectorSize );
if ( error != kIOReturnSuccess )
{
if ( error == kIOReturnUnsupported )
{
printf ( "SMARTWriteLogAtAddress not supported\n" );
}
else if ( error == kIOReturnNotWritable )
{
printf ( "SMARTWriteLogAtAddress unwritable\n" );
}
else
{
printf ( "SMARTWriteLogAtAddress failed: %s(%x,%d)\n", mach_error_string ( error ), error, error & 0xFFFFFF );
}
}
error = ( *smartInterface )->SMARTReadLogDirectory ( smartInterface, &logDirectory );
if ( error != kIOReturnSuccess )
{
if ( error == kIOReturnUnsupported )
{
printf ( "SMARTReadLogDirectory not supported\n" );
}
else if ( error == kIOReturnNotReadable )
{
printf ( "SMARTReadLogDirectory unreadable\n" );
}
else
{
printf ( "SMARTReadLogDirectory failed: %s(%x,%d)\n", mach_error_string ( error ), error, error & 0xFFFFFF );
}
}
#if DEBUG_ALL
else
{
UInt8 * ptr = ( UInt8 * ) &logDirectory;
printf ( "\n" );
printf ( "SMART Log Directory Data\n" );
printf ( "---------------------------------------------------------\n" );
printf ( "Offset | Data (in hex)\n" );
printf ( "hex dec | \n" );
printf ( "---------------------------------------------------------\n" );
for ( index = 0; index < sizeof ( logDirectory ); index += 16 )
{
printf ( "%03x ", index );
printf ( "%03d | ", index );
printf ( "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
ptr[index+0], ptr[index+1], ptr[index+2], ptr[index+3],
ptr[index+4], ptr[index+5], ptr[index+6], ptr[index+7],
ptr[index+8], ptr[index+9], ptr[index+10], ptr[index+11],
ptr[index+12], ptr[index+13], ptr[index+14], ptr[index+15] );
}
printf ( "\n" );
}
#endif
bzero ( buffer, kATADefaultSectorSize );
index = 0x81;
error = ( *smartInterface )->SMARTReadLogAtAddress ( smartInterface,
index,
( void * ) buffer,
kATADefaultSectorSize );
if ( error != kIOReturnSuccess )
{
printf ( "SMARTReadLogAtAddress failed: %s(%x,%d)\n",
mach_error_string ( error ), error, error & 0xFFFFFF );
}
#if DEBUG_ALL
else
{
UInt8 * ptr = ( UInt8 * ) buffer;
bool ok = true;
printf ( "\n" );
printf ( "SMART Log Data at Address 0x%02x\n", index );
printf ( "---------------------------------------------------------\n" );
printf ( "Offset | Data (in hex)\n" );
printf ( "hex dec | \n" );
printf ( "---------------------------------------------------------\n" );
for ( index = 0; index < sizeof ( buffer ); index += 16 )
{
printf ( "%03x ", index );
printf ( "%03d | ", index );
printf ( "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
ptr[index+0], ptr[index+1], ptr[index+2], ptr[index+3],
ptr[index+4], ptr[index+5], ptr[index+6], ptr[index+7],
ptr[index+8], ptr[index+9], ptr[index+10], ptr[index+11],
ptr[index+12], ptr[index+13], ptr[index+14], ptr[index+15] );
}
printf ( "\n" );
for ( index = 0; index < kATADefaultSectorSize; index++ )
{
if ( ptr[index] != ( index & 0xFF ) )
{
printf ( "SMART Log data incorrect at byte %d\n", index );
ok = false;
}
}
if ( ok == true )
{
printf ( "Verified log directory data\n" );
}
}
#endif
ErrorExit:
return;
}