/* IBM_PROLOG_BEGIN_TAG */ /* This is an automatically generated prolog. */ /* */ /* $Source: src/usr/spd/spd.C $ */ /* */ /* IBM CONFIDENTIAL */ /* */ /* COPYRIGHT International Business Machines Corp. 2012,2013 */ /* */ /* p1 */ /* */ /* Object Code Only (OCO) source materials */ /* Licensed Internal Code Source Materials */ /* IBM HostBoot Licensed Internal Code */ /* */ /* The source code for this program is not published or otherwise */ /* divested of its trade secrets, irrespective of what has been */ /* deposited with the U.S. Copyright Office. */ /* */ /* Origin: 30 */ /* */ /* IBM_PROLOG_END_TAG */ /** * @file spd.C * * @brief Implementation of the SPD device driver * */ // ---------------------------------------------- // Includes // ---------------------------------------------- #include #include #include #include #include #include #include #include #include #include #include "spd.H" #include "spdDDR3.H" // ---------------------------------------------- // Trace definitions // ---------------------------------------------- trace_desc_t* g_trac_spd = NULL; TRAC_INIT( & g_trac_spd, "SPD", KILOBYTE ); // ------------------------ // Macros for unit testing //#define TRACUCOMP(args...) TRACFCOMP(args) #define TRACUCOMP(args...) //#define TRACSSCOMP(args...) TRACFCOMP(args) #define TRACSSCOMP(args...) // ---------------------------------------------- // Defines // ---------------------------------------------- namespace SPD { // ---------------------------------------------- // Globals // ---------------------------------------------- bool g_loadModule = true; // This mutex is used to lock for writing/updating the global variables. mutex_t g_spdMutex = MUTEX_INITIALIZER; uint64_t g_spdPnorAddr = 0x0; // By setting to false, allows debug at a later time by allowing to // substitute a binary file (dimmspd.dat) into PNOR. const bool g_usePNOR = true; /** * @brief Compare two values and return whether e2 is greater than * the e1 value. This is used during lower_bound to cut * search time down. * * @param[in] e1 - Structure to be searched, using the Keyword * value in that structure. * * @param[in] e2 - Structure to be searched, using the Keyword * value in that structure. * * @return boolean - Whether or not e2.keyword is larger than * e1.keyword. */ bool compareEntries ( const KeywordData e1, const KeywordData e2 ); /** * @brief This function will read the DIMM memory type. * * @param[out] o_memType - The memory type value to return. * * @param[in] i_target - The target to read data from. * * @return errlHndl_t - NULL if successful, otherwise a pointer * to the error log. */ errlHndl_t getMemType ( uint8_t & o_memType, TARGETING::Target * i_target ); /** * @brief This function will scan the table and return the entry * corresponding to the keyword being requested. * * @param[in] i_keyword - The keyword being requested. * * @param[in] i_memType - The memory type of the target. * * @param[out] o_entry - The table entry corresponding to the keyword. * * @return errlHndl_t - NULL if successful, otherwise a pointer to * the error log. */ errlHndl_t getKeywordEntry ( uint64_t i_keyword, uint64_t i_memType, KeywordData *& o_entry ); // Register the perform Op with the routing code for DIMMs. DEVICE_REGISTER_ROUTE( DeviceFW::READ, DeviceFW::SPD, TARGETING::TYPE_DIMM, spdGetKeywordValue ); DEVICE_REGISTER_ROUTE( DeviceFW::WRITE, DeviceFW::SPD, TARGETING::TYPE_DIMM, spdWriteKeywordValue ); // ------------------------------------------------------------------ // spdGetKeywordValue // ------------------------------------------------------------------ errlHndl_t spdGetKeywordValue ( DeviceFW::OperationType i_opType, TARGETING::Target * i_target, void * io_buffer, size_t & io_buflen, int64_t i_accessType, va_list i_args ) { errlHndl_t err = NULL; uint64_t keyword = va_arg( i_args, uint64_t ); TRACSSCOMP( g_trac_spd, ENTER_MRK"spdGetKeywordValue(), io_buflen: %d, keyword: 0x%04x", io_buflen, keyword ); do { // Read the Basic Memory Type uint8_t memType = 0x0; err = getMemType( memType, i_target ); if( err ) { break; } TRACDCOMP( g_trac_spd, INFO_MRK"Mem Type: %04x", memType ); // Check the Basic Memory Type to be sure its valid before // continuing. if( SPD_DDR3 == memType ) { // If the user wanted the Basic memory type, return this now. if( BASIC_MEMORY_TYPE == keyword ) { io_buflen = MEM_TYPE_ADDR_SZ; memcpy( io_buffer, &memType, io_buflen ); break; } // Read the keyword value err = spdGetValue( keyword, io_buffer, io_buflen, i_target, memType ); if( err ) { break; } } else { TRACFCOMP( g_trac_spd, ERR_MRK"Invalid Basic Memory Type (0x%04x)", memType ); /*@ * @errortype * @reasoncode VPD::VPD_INVALID_BASIC_MEMORY_TYPE * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE * @moduleid VPD::VPD_SPD_GET_KEYWORD_VALUE * @userdata1 Basic Memory Type (Byte 2) * @userdata2 Keyword Requested * @devdesc Invalid Basic Memory Type */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, VPD::VPD_SPD_GET_KEYWORD_VALUE, VPD::VPD_INVALID_BASIC_MEMORY_TYPE, memType, keyword ); break; } } while( 0 ); TRACSSCOMP( g_trac_spd, EXIT_MRK"spdGetKeywordValue()" ); return err; } // ------------------------------------------------------------------ // spdWriteKeywordValue // ------------------------------------------------------------------ errlHndl_t spdWriteKeywordValue ( DeviceFW::OperationType i_opType, TARGETING::Target * i_target, void * io_buffer, size_t & io_buflen, int64_t i_accessType, va_list i_args ) { errlHndl_t err = NULL; uint64_t keyword = va_arg( i_args, uint64_t ); TRACSSCOMP( g_trac_spd, ENTER_MRK"spdWriteKeywordValue()" ); do { // Get memory type uint8_t memType = 0x0; err = getMemType( memType, i_target ); if( err ) { break; } // Check DDR3 if( SPD_DDR3 == memType ) { err = spdWriteValue( keyword, io_buffer, io_buflen, i_target, memType ); if( err ) { break; } } else { TRACFCOMP( g_trac_spd, ERR_MRK"Invalid Basic Memory Type (0x%04x)", memType ); /*@ * @errortype * @reasoncode VPD::VPD_INVALID_BASIC_MEMORY_TYPE * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE * @moduleid VPD::VPD_SPD_WRITE_KEYWORD_VALUE * @userdata1 Basic Memory Type (Byte 2) * @userdata2 Keyword Requested * @devdesc Invalid Basic Memory Type */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, VPD::VPD_SPD_WRITE_KEYWORD_VALUE, VPD::VPD_INVALID_BASIC_MEMORY_TYPE, memType, keyword ); break; } } while( 0 ); TRACSSCOMP( g_trac_spd, EXIT_MRK"spdWriteKeywordValue()" ); return err; } // ------------------------------------------------------------------ // spdFetchData // ------------------------------------------------------------------ errlHndl_t spdFetchData ( uint64_t i_byteAddr, size_t i_numBytes, void * o_data, TARGETING::Target * i_target ) { errlHndl_t err = NULL; TRACSSCOMP( g_trac_spd, ENTER_MRK"spdFetchData()" ); do { if( likely( g_usePNOR ) ) { // Setup info needed to read from PNOR VPD::pnorInformation info; info.segmentSize = DIMM_SPD_SECTION_SIZE; info.maxSegments = DIMM_SPD_MAX_SECTIONS; info.pnorSection = PNOR::DIMM_JEDEC_VPD; info.pnorSide = PNOR::CURRENT_SIDE; err = VPD::readPNOR( i_byteAddr, i_numBytes, o_data, i_target, info, g_spdPnorAddr, &g_spdMutex ); if( err ) { break; } } else { err = spdReadBinaryFile( i_byteAddr, i_numBytes, o_data ); if( err ) { break; } } } while( 0 ); TRACSSCOMP( g_trac_spd, EXIT_MRK"spdFetchData()" ); return err; } // ------------------------------------------------------------------ // spdWriteData // ------------------------------------------------------------------ errlHndl_t spdWriteData ( uint64_t i_offset, size_t i_numBytes, void * i_data, TARGETING::Target * i_target ) { errlHndl_t err = NULL; TRACSSCOMP( g_trac_spd, ENTER_MRK"spdWriteData()" ); do { if( likely( g_usePNOR ) ) { // Setup info needed to write from PNOR VPD::pnorInformation info; info.segmentSize = DIMM_SPD_SECTION_SIZE; info.maxSegments = DIMM_SPD_MAX_SECTIONS; info.pnorSection = PNOR::DIMM_JEDEC_VPD; info.pnorSide = PNOR::CURRENT_SIDE; err = VPD::writePNOR( i_offset, i_numBytes, i_data, i_target, info, g_spdPnorAddr, &g_spdMutex ); if( err ) { break; } } else { TRACFCOMP( g_trac_spd, ERR_MRK"There is no way to write SPD when not using PNOR!" ); /*@ * @errortype * @reasoncode VPD::VPD_INVALID_WRITE_METHOD * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE * @moduleid VPD::VPD_SPD_WRITE_DATA * @userdata1 Write Offset * @userdata2 Number of Bytes to Write * @devdesc g_usePNOR is false, but there isn't an * alternate way to write PNOR. */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, VPD::VPD_SPD_WRITE_DATA, VPD::VPD_INVALID_WRITE_METHOD, i_offset, i_numBytes ); break; } } while( 0 ); TRACSSCOMP( g_trac_spd, EXIT_MRK"spdWriteData()" ); return err; } // ------------------------------------------------------------------ // spdGetValue // ------------------------------------------------------------------ errlHndl_t spdGetValue ( uint64_t i_keyword, void * io_buffer, size_t & io_buflen, TARGETING::Target * i_target, uint64_t i_DDRRev ) { errlHndl_t err = NULL; uint8_t * tmpBuffer = static_cast(io_buffer); TRACSSCOMP( g_trac_spd, ENTER_MRK"spdGetValue()" ); do { KeywordData * entry = NULL; err = getKeywordEntry( i_keyword, i_DDRRev, entry ); if( err ) { break; } // Check to be sure entry is not NULL. if( NULL == entry ) { TRACFCOMP( g_trac_spd, ERR_MRK"Entry Pointer is NULL!" ); /*@ * @errortype * @reasoncode VPD::VPD_NULL_ENTRY * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE * @moduleid VPD::VPD_SPD_GET_VALUE * @userdata1 SPD Keyword * @userdata2[0:31] Buffer Size * @userdata2[32:63] Memory Type * @devdesc The table entry associated with keyword was * NULL. */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, VPD::VPD_SPD_GET_VALUE, VPD::VPD_NULL_ENTRY, i_keyword, TWO_UINT32_TO_UINT64( io_buflen, i_DDRRev ) ); break; } // Check if this is a module specific keyword and check that the // correct values are in place to actually request it err = checkModSpecificKeyword( (*entry), i_DDRRev, i_target ); if( err ) { break; } if( entry->isSpecialCase ) { // Handle special cases where data isn't sequential // or is in reverse order from what would be read. err = spdSpecialCases( (*entry), io_buffer, io_buflen, i_target, i_DDRRev ); break; } // Check io_buflen versus size in table err = spdCheckSize( io_buflen, (*entry).length, i_keyword ); if( err ) { break; } // Read length requested err = spdFetchData( (*entry).offset, (*entry).length, tmpBuffer, i_target ); if( err ) { break; } // if useBitmask set, mask and then shift data if( (*entry).useBitMask ) { // Any bit mask/shifting will always be on a <1 Byte value // thus, we touch only byte 0. tmpBuffer[0] = tmpBuffer[0] & (*entry).bitMask; tmpBuffer[0] = tmpBuffer[0] >> (*entry).shift; } // Set length read io_buflen = (*entry).length; } while( 0 ); if( err ) { // Signal the caller that there was an error getting // data and that there is no valid data. io_buflen = 0; } TRACSSCOMP( g_trac_spd, EXIT_MRK"spdGetValue()" ); return err; } // ------------------------------------------------------------------ // spdWriteValue // ------------------------------------------------------------------ errlHndl_t spdWriteValue ( uint64_t i_keyword, void * io_buffer, size_t & io_buflen, TARGETING::Target * i_target, uint64_t i_DDRRev ) { errlHndl_t err = NULL; TRACSSCOMP( g_trac_spd, ENTER_MRK"spdWriteValue()" ); do { KeywordData * entry = NULL; err = getKeywordEntry( i_keyword, i_DDRRev, entry ); if( err ) { break; } if( NULL == entry ) { TRACFCOMP( g_trac_spd, ERR_MRK"Entry pointer is NULL!" ); /*@ * @errortype * @reasoncode VPD::VPD_NULL_ENTRY * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE * @moduleid VPD::VPD_SPD_WRITE_VALUE * @userdata1 SPD Keyword * @userdata2[0:31] Buffer Length * @userdata2[32:63] Memory Type * @devdesc The table entry associated with keyword was * NULL. */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, VPD::VPD_SPD_WRITE_VALUE, VPD::VPD_NULL_ENTRY, i_keyword, TWO_UINT32_TO_UINT64( io_buflen, i_DDRRev ) ); break; } // Check write flag if( !(entry->writable) ) { // Error if not writable TRACFCOMP( g_trac_spd, ERR_MRK"Trying to write keyword (0x%04x) that is non-" "writable", i_keyword ); /*@ * @errortype * @reasoncode VPD::VPD_KEYWORD_NOT_WRITABLE * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE * @moduleid VPD::VPD_SPD_WRITE_VALUE * @userdata1 SPD Keyword * @userdata2[0:31] Buffer Length * @userdata2[32:63] Memory Type * @devdesc The SPD Keyword is not writable. */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, VPD::VPD_SPD_WRITE_VALUE, VPD::VPD_KEYWORD_NOT_WRITABLE, i_keyword, TWO_UINT32_TO_UINT64( io_buflen, i_DDRRev ) ); break; } // Check the Size to be equal to entry written err = spdCheckSize( io_buflen, entry->length, i_keyword ); if( err ) { break; } // We are not handling writes that are not on a byte // boundary until we absolutely need to. There are // no writable keywords that are not on byte boundaries if( entry->useBitMask ) { // Error if not writable TRACFCOMP( g_trac_spd, ERR_MRK"Trying to write keyword (0x%04x) that is not a full byte size", i_keyword ); /*@ * @errortype * @reasoncode VPD::VPD_UNSUPPORTED_WRITE * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE * @moduleid VPD::VPD_SPD_WRITE_VALUE * @userdata1 SPD Keyword * @userdata2[0:15] Keyword Length (in bytes) * @userdata2[16:31] Keyword Bitmask * @userdata2[32:63] Memory Type * @devdesc Writes to non-byte SPD keywords are unsupported. */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, VPD::VPD_SPD_WRITE_VALUE, VPD::VPD_UNSUPPORTED_WRITE, i_keyword, TWO_UINT16_ONE_UINT32_TO_UINT64( entry->length, entry->bitMask, i_DDRRev ) ); break; } // Write value err = spdWriteData( entry->offset, io_buflen, io_buffer, i_target ); if( err ) { break; } // Send mbox message with new data to Fsp VPD::VpdWriteMsg_t msgdata; msgdata.rec_num = i_target->getAttr(); //XXXX=offset relative to whole section memcpy( msgdata.record, "XXXX", sizeof(msgdata.record) ); msgdata.offset = entry->offset; err = VPD::sendMboxWriteMsg( io_buflen, io_buffer, i_target, VPD::VPD_WRITE_DIMM, msgdata ); if( err ) { break; } } while( 0 ); TRACSSCOMP( g_trac_spd, EXIT_MRK"spdWriteValue()" ); return err; } // ------------------------------------------------------------------ // spdSpecialCases // ------------------------------------------------------------------ errlHndl_t spdSpecialCases ( KeywordData i_kwdData, void * io_buffer, size_t & io_buflen, TARGETING::Target * i_target, uint64_t i_DDRRev ) { errlHndl_t err = NULL; uint8_t * tmpBuffer = static_cast(io_buffer); TRACSSCOMP( g_trac_spd, ENTER_MRK"spdSpecialCases()" ); do { // Handle each of the special cases here if( SPD_DDR3 == i_DDRRev ) { switch( i_kwdData.keyword ) { // ================================================== // 2 byte - LSB then MSB case CAS_LATENCIES_SUPPORTED: case TRFC_MIN: case MODULE_MANUFACTURER_ID: case DRAM_MANUFACTURER_ID: case RMM_MFR_ID_CODE: case LRMM_MFR_ID_CODE: // Check Size of buffer err = spdCheckSize( io_buflen, i_kwdData.length, i_kwdData.keyword ); if( err ) break; // Get MSB err = spdFetchData( i_kwdData.offset, 1, /* Read 1 byte at a time */ &tmpBuffer[0], i_target ); if( err ) break; // Mask and shift if needed if( i_kwdData.useBitMask ) { tmpBuffer[0] = tmpBuffer[0] & i_kwdData.bitMask; tmpBuffer[0] = tmpBuffer[0] >> i_kwdData.shift; } // Get LSB err = spdFetchData( (i_kwdData.offset - 1), 1, /* Read 1 byte at a time */ &tmpBuffer[1], i_target ); if( err ) break; // Set number of bytes read io_buflen = i_kwdData.length; break; // ================================================== // 2 byte - MSB with mask then LSB is 2 more than MSB case TRC_MIN: // Check Size of buffer err = spdCheckSize( io_buflen, i_kwdData.length, i_kwdData.keyword ); if( err ) break; // Get MSB err = spdFetchData( i_kwdData.offset, 1, /* Read 1 byte at a time */ &tmpBuffer[0], i_target ); if( err ) break; // Mask and shift if needed if( i_kwdData.useBitMask ) { tmpBuffer[0] = tmpBuffer[0] & i_kwdData.bitMask; tmpBuffer[0] = tmpBuffer[0] >> i_kwdData.shift; } // Get LSB err = spdFetchData( (i_kwdData.offset + 2), 1, /* Read 1 byte at a time */ &tmpBuffer[1], i_target ); if( err ) break; // Set number of bytes read io_buflen = i_kwdData.length; break; // ================================================== // 2 byte - MSB then LSB case TRAS_MIN: case TFAW_MIN: // Check size of buffer err = spdCheckSize( io_buflen, i_kwdData.length, i_kwdData.keyword ); if( err ) break; // Get MSB err = spdFetchData( i_kwdData.offset, 1, /* Read 1 byte at a time */ &tmpBuffer[0], i_target ); if( err ) break; // Mask and shift if needed if( i_kwdData.useBitMask ) { tmpBuffer[0] = tmpBuffer[0] & i_kwdData.bitMask; tmpBuffer[0] = tmpBuffer[0] >> i_kwdData.shift; } // Get LSB err = spdFetchData( (i_kwdData.offset + 1), 1, /* Read 1 byte at a time */ &tmpBuffer[1], i_target ); if( err ) break; // Set number of bytes read io_buflen = i_kwdData.length; break; // ================================================== default: TRACFCOMP( g_trac_spd, ERR_MRK"Unknown keyword (0x%04x) for DDR3 special cases!", i_kwdData.keyword ); /*@ * @errortype * @reasoncode VPD::VPD_INVALID_SPD_KEYWORD * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE * @moduleid VPD::VPD_SPD_SPECIAL_CASES * @userdata1 SPD Keyword * @userdata2 UNUSED * @devdesc Keyword is not a special case keyword. */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, VPD::VPD_SPD_SPECIAL_CASES, VPD::VPD_INVALID_SPD_KEYWORD, i_kwdData.keyword, 0x0 ); break; }; } else { TRACFCOMP( g_trac_spd, ERR_MRK"Unsupported DDRx Revision (0x%04x)", i_DDRRev ); /*@ * @errortype * @reasoncode VPD::VPD_INVALID_BASIC_MEMORY_TYPE * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE * @moduleid VPD::VPD_SPD_SPECIAL_CASES * @userdata1 SPD Keyword * @userdata2 DIMM DDR Revision * @devdesc Invalid DDR Revision */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, VPD::VPD_SPD_SPECIAL_CASES, VPD::VPD_INVALID_BASIC_MEMORY_TYPE, i_kwdData.keyword, i_DDRRev ); break; } } while( 0 ); TRACSSCOMP( g_trac_spd, EXIT_MRK"spdSpecialCases()" ); return err; } // ------------------------------------------------------------------ // spdCheckSize // ------------------------------------------------------------------ errlHndl_t spdCheckSize ( size_t i_bufferSz, size_t i_expBufferSz, uint64_t i_keyword ) { errlHndl_t err = NULL; // Check that the buffer is greater than or equal to the size // we need to get all the keyword data requested. if( i_bufferSz < i_expBufferSz ) { TRACFCOMP( g_trac_spd, ERR_MRK"Buffer Size (%d) for keyword (0x%04x) wasn't greater " "than or equal to expected size (%d)", i_bufferSz, i_keyword, i_expBufferSz ); /*@ * @errortype * @reasoncode VPD::VPD_INSUFFICIENT_BUFFER_SIZE * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE * @moduleid VPD::VPD_SPD_CHECK_SIZE * @userdata1 Keyword * @userdata2[0:31] Needed Buffer Size * @userdata2[32:63] Expected Buffer Size * @devdesc Buffer Size provided was not big enough for * the keyword requested. */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, VPD::VPD_SPD_CHECK_SIZE, VPD::VPD_INSUFFICIENT_BUFFER_SIZE, i_keyword, TWO_UINT32_TO_UINT64( i_bufferSz, i_expBufferSz ) ); } return err; } // ------------------------------------------------------------------ // spdReadBinaryFile // ------------------------------------------------------------------ errlHndl_t spdReadBinaryFile ( uint64_t i_byteAddr, size_t i_numBytes, void * o_data ) { errlHndl_t err = NULL; const char * fileName = "dimmspd.dat"; const char * startAddr = NULL; size_t fileSize; TRACSSCOMP( g_trac_spd, ENTER_MRK"spdReadBinaryFile()" ); do { if( g_loadModule ) { mutex_lock( &g_spdMutex ); if( g_loadModule ) { // Load the file TRACUCOMP( g_trac_spd, "Load file" ); err = VFS::module_load( fileName ); if( err ) { TRACFCOMP( g_trac_spd, ERR_MRK"Error opening binary SPD file: %s", fileName ); mutex_unlock( &g_spdMutex ); break; } g_loadModule = false; } mutex_unlock( &g_spdMutex ); } // Get the starting address of the file/module TRACUCOMP( g_trac_spd, "Get starting address/size" ); err = VFS::module_address( fileName, startAddr, fileSize ); if( err ) { TRACFCOMP( g_trac_spd, ERR_MRK"Error getting starting address of binary SPD file: %s", fileName ); break; } // Check that we can read the amount of data we need to from the // file we just loaded TRACUCOMP( g_trac_spd, "Check Size vs file size" ); if( (i_byteAddr + i_numBytes) > fileSize ) { TRACFCOMP( g_trac_spd, ERR_MRK"Unable to read %d bytes from %s at offset 0x%08x " "because file size is only %d bytes!", i_numBytes, fileName, i_byteAddr, fileSize ); uint64_t tmpData = (i_byteAddr + i_numBytes); tmpData = tmpData << 16; tmpData = tmpData & (fileSize & 0xFFFF); /*@ * @errortype * @reasoncode VPD::VPD_INSUFFICIENT_FILE_SIZE * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE * @moduleid VPD::VPD_SPD_READ_BINARY_FILE * @userdata1 File Size * @userdata2[0:48] Starting offset into file * @userdata2[49:63] Number of bytes to read * @devdesc File is not sufficiently large to read number of * bytes at offset given without overrunning file. */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, VPD::VPD_SPD_READ_BINARY_FILE, VPD::VPD_INSUFFICIENT_FILE_SIZE, fileSize, tmpData ); break; } // Retrieve the data requested TRACUCOMP( g_trac_spd, "Copy data out of file" ); memcpy( o_data, (startAddr + i_byteAddr), i_numBytes ); } while( 0 ); TRACSSCOMP( g_trac_spd, EXIT_MRK"spdReadBinaryFile()" ); return err; } // ------------------------------------------------------------------ // compareEntries // ------------------------------------------------------------------ bool compareEntries ( const KeywordData e1, const KeywordData e2 ) { // e1 is the iterator value // e2 is the search value TRACUCOMP( g_trac_spd, INFO_MRK"e1: 0x%04x, e2: 0x%04x", e1.keyword, e2.keyword ); if( e2.keyword > e1.keyword ) { return true; } else { return false; } } // ------------------------------------------------------------------ // checkModSpecificKeyword // ------------------------------------------------------------------ errlHndl_t checkModSpecificKeyword ( KeywordData i_kwdData, uint64_t i_memType, TARGETING::Target * i_target ) { errlHndl_t err = NULL; TRACSSCOMP( g_trac_spd, ENTER_MRK"checkModSpecificKeyword()" ); do { // If not a Module Specific keyword, skip this logic if( NA == i_kwdData.modSpec ) { break; } KeywordData * modTypeEntry = NULL; err = getKeywordEntry( MODULE_TYPE, i_memType, modTypeEntry ); if( err ) { break; } if( NULL == modTypeEntry ) { TRACFCOMP( g_trac_spd, ERR_MRK"Keyword Entry pointer is NULL!" ); /*@ * @errortype * @reasoncode VPD::VPD_NULL_ENTRY * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE * @moduleid VPD::VPD_SPD_CHECK_MODULE_SPECIFIC_KEYWORD * @userdata1 i_memType * @userdata2 * @devdesc Entry to get Module type is NULL */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, VPD::VPD_SPD_CHECK_MODULE_SPECIFIC_KEYWORD, VPD::VPD_NULL_ENTRY, i_memType, 0x0 ); break; } // To check the module specific flags, also need the Module Type // value. uint8_t modType = 0x0; err = spdFetchData( modTypeEntry->offset, modTypeEntry->length, &modType, i_target ); if( err ) { break; } // Check Unbuffered Memory Module (UMM) if( (SPD_DDR3 == i_memType) && ( (0x2 == modType) || (0x3 == modType) || (0x4 == modType) || (0x6 == modType) || (0x8 == modType) || (0xc == modType) || (0xd == modType) ) ) { if( 0 == (i_kwdData.modSpec & UMM) ) { TRACFCOMP( g_trac_spd, ERR_MRK"Keyword (0x%04x) is not valid with UMM modules!", i_kwdData.keyword ); /*@ * @errortype * @reasoncode VPD::VPD_MOD_SPECIFIC_MISMATCH_UMM * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE * @moduleid VPD::VPD_SPD_CHECK_MODULE_SPECIFIC_KEYWORD * @userdata1[0:31] Module Type (byte 3[3:0]) * @userdata1[32:63] Memory Type (byte 2) * @userdata2[0:31] SPD Keyword * @userdata2[32:63] Module Specific flag * @devdesc Keyword requested was not UMM Module specific. */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, VPD::VPD_SPD_CHECK_MODULE_SPECIFIC_KEYWORD, VPD::VPD_MOD_SPECIFIC_MISMATCH_UMM, TWO_UINT32_TO_UINT64( modType, i_memType ), TWO_UINT32_TO_UINT64( i_kwdData.keyword, i_kwdData.modSpec ) ); break; } } // Check Registered Memory Module (RMM) else if( (SPD_DDR3 == i_memType) && ( (0x1 == modType) || (0x5 == modType) || (0x9 == modType) ) ) { if( 0 == (i_kwdData.modSpec & RMM) ) { TRACFCOMP( g_trac_spd, ERR_MRK"Keyword (0x%04x) is not valid with RMM modules!", i_kwdData.keyword ); /*@ * @errortype * @reasoncode VPD::VPD_MOD_SPECIFIC_MISMATCH_RMM * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE * @moduleid VPD::VPD_SPD_CHECK_MODULE_SPECIFIC_KEYWORD * @userdata1[0:31] Module Type (byte 3[3:0]) * @userdata1[32:63] Memory Type (byte 2) * @userdata2[0:31] SPD Keyword * @userdata2[32:63] Module Specific flag * @devdesc Keyword requested was not RMM Module specific. */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, VPD::VPD_SPD_CHECK_MODULE_SPECIFIC_KEYWORD, VPD::VPD_MOD_SPECIFIC_MISMATCH_RMM, TWO_UINT32_TO_UINT64( modType, i_memType ), TWO_UINT32_TO_UINT64( i_kwdData.keyword, i_kwdData.modSpec ) ); break; } } // Check Clocked Memory Module (CMM) else if( (SPD_DDR3 == i_memType) && ( (0x7 == modType) || (0xa == modType) ) ) { if( 0 == (i_kwdData.modSpec & CMM) ) { TRACFCOMP( g_trac_spd, ERR_MRK"Keyword (0x%04x) is not valid with CMM modules!", i_kwdData.keyword ); /*@ * @errortype * @reasoncode VPD::VPD_MOD_SPECIFIC_MISMATCH_CMM * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE * @moduleid VPD::VPD_SPD_CHECK_MODULE_SPECIFIC_KEYWORD * @userdata1[0:31] Module Type (byte 3[3:0]) * @userdata1[32:63] Memory Type (byte 2) * @userdata2[0:31] SPD Keyword * @userdata2[32:63] Module Specific flag * @devdesc Keyword requested was not CMM Module specific. */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, VPD::VPD_SPD_CHECK_MODULE_SPECIFIC_KEYWORD, VPD::VPD_MOD_SPECIFIC_MISMATCH_CMM, TWO_UINT32_TO_UINT64( modType, i_memType ), TWO_UINT32_TO_UINT64( i_kwdData.keyword, i_kwdData.modSpec ) ); break; } } // Check Load Reduction Memory Module (LRMM) else if( (SPD_DDR3 == i_memType) && ( (0xb == modType) ) ) { if( 0 == (i_kwdData.modSpec & LRMM) ) { TRACFCOMP( g_trac_spd, ERR_MRK"Keyword (0x%04x) is not valid with LRMM modules!", i_kwdData.keyword ); /*@ * @errortype * @reasoncode VPD::VPD_MOD_SPECIFIC_MISMATCH_LRMM * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE * @moduleid VPD::VPD_SPD_CHECK_MODULE_SPECIFIC_KEYWORD * @userdata1[0:31] Module Type (byte 3[3:0]) * @userdata1[32:63] Memory Type (byte 2) * @userdata2[0:31] SPD Keyword * @userdata2[32:63] Module Specific flag * @devdesc Keyword requested was not LRMM Module specific. */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, VPD::VPD_SPD_CHECK_MODULE_SPECIFIC_KEYWORD, VPD::VPD_MOD_SPECIFIC_MISMATCH_LRMM, TWO_UINT32_TO_UINT64( modType, i_memType ), TWO_UINT32_TO_UINT64( i_kwdData.keyword, i_kwdData.modSpec ) ); break; } } else { TRACFCOMP( g_trac_spd, ERR_MRK"Module specific keyword could not be matched with an " "appropriate scenario!" ); TRACFCOMP( g_trac_spd, ERR_MRK" Mem Type: 0x%04x, Mod Type: 0x%04x, Keyword: 0x%04x", i_memType, modType, i_kwdData.keyword ); /*@ * @errortype * @reasoncode VPD::VPD_MOD_SPECIFIC_UNSUPPORTED * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE * @moduleid VPD::VPD_SPD_CHECK_MODULE_SPECIFIC_KEYWORD * @userdata1[0:31] Module Type (byte 3[3:0]) * @userdata1[32:63] Memory Type (byte 2) * @userdata2[0:31] SPD Keyword * @userdata2[32:63] Module Specific flag * @devdesc Unsupported Module Specific setup. */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, VPD::VPD_SPD_CHECK_MODULE_SPECIFIC_KEYWORD, VPD::VPD_MOD_SPECIFIC_UNSUPPORTED, TWO_UINT32_TO_UINT64( modType, i_memType ), TWO_UINT32_TO_UINT64( i_kwdData.keyword, i_kwdData.modSpec ) ); break; } } while( 0 ); TRACSSCOMP( g_trac_spd, EXIT_MRK"checkModSpecificKeyword()" ); return err; } // ------------------------------------------------------------------ // getMemType // ------------------------------------------------------------------ errlHndl_t getMemType ( uint8_t & o_memType, TARGETING::Target * i_target ) { errlHndl_t err = NULL; err = spdFetchData( MEM_TYPE_ADDR, MEM_TYPE_ADDR_SZ, &o_memType, i_target ); TRACUCOMP( g_trac_spd, "SPD::getMemType() - MemType: 0x%02x, Error: %s", o_memType, ((NULL == err) ? "No" : "Yes") ); return err; } // ------------------------------------------------------------------ // getKeywordEntry // ------------------------------------------------------------------ errlHndl_t getKeywordEntry ( uint64_t i_keyword, uint64_t i_memType, KeywordData *& o_entry ) { errlHndl_t err = NULL; KeywordData * kwdData; uint32_t arraySize = 0x0; TRACSSCOMP( g_trac_spd, ENTER_MRK"getKeywordEntry()" ); do { if( SPD_DDR3 == i_memType ) { // Put the table into an array arraySize = (sizeof(ddr3Data)/sizeof(ddr3Data[0])); kwdData = ddr3Data; } else { TRACFCOMP( g_trac_spd, ERR_MRK"Unsupported DDRx Revision (0x%04x)", i_memType ); /*@ * @errortype * @reasoncode VPD::VPD_INVALID_BASIC_MEMORY_TYPE * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE * @moduleid VPD::VPD_SPD_GET_KEYWORD_ENTRY * @userdata1 SPD Keyword * @userdata2 The DDR Revision * @devdesc Invalid DDR Revision */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, VPD::VPD_SPD_GET_KEYWORD_ENTRY, VPD::VPD_INVALID_BASIC_MEMORY_TYPE, i_keyword, i_memType ); break; } // Set the searching structure equal to the keyword we're looking for. KeywordData tmpKwdData; tmpKwdData.keyword = i_keyword; KeywordData * entry = std::lower_bound( kwdData, &kwdData[arraySize], tmpKwdData, compareEntries ); if( ( entry == &kwdData[arraySize] ) || ( i_keyword != entry->keyword ) ) { TRACFCOMP( g_trac_spd, ERR_MRK"No matching keyword entry found!" ); /*@ * @errortype * @reasoncode VPD::VPD_KEYWORD_NOT_FOUND * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE * @moduleid VPD::VPD_SPD_GET_KEYWORD_ENTRY * @userdata1 SPD Keyword * @userdata2 * @devdesc Invalid SPD Keyword */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, VPD::VPD_SPD_GET_KEYWORD_ENTRY, VPD::VPD_KEYWORD_NOT_FOUND, i_keyword, 0x0 ); break; } o_entry = entry; } while( 0 ); TRACSSCOMP( g_trac_spd, EXIT_MRK"getKeywordEntry()" ); return err; } } // end namespace SPD