/* IBM_PROLOG_BEGIN_TAG */ /* This is an automatically generated prolog. */ /* */ /* $Source: src/usr/vpd/spd.C $ */ /* */ /* OpenPOWER HostBoot Project */ /* */ /* COPYRIGHT International Business Machines Corp. 2012,2014 */ /* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ /* You may obtain a copy of the License at */ /* */ /* http://www.apache.org/licenses/LICENSE-2.0 */ /* */ /* Unless required by applicable law or agreed to in writing, software */ /* distributed under the License is distributed on an "AS IS" BASIS, */ /* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ /* implied. See the License for the specific language governing */ /* permissions and limitations under the License. */ /* */ /* 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 #include "spd.H" #include "spdDDR3.H" #include "spdDDR4.H" #include "errlud_vpd.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 read the DIMM module type. * * @param[out] o_modType - The module type value to return. * * @param[in] i_target - The target to read data from. * * @param[in] i_memType - The memory type * * @return errlHndl_t - NULL if successful, otherwise a pointer * to the error log. */ errlHndl_t getModType ( modSpecTypes_t & o_memType, TARGETING::Target * i_target, uint64_t i_memType ); /** * @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[in] i_target - Target (only used for callouts) * * @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, TARGETING::Target * i_target, const 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 if(( SPD_DDR3 == memType ) || ( SPD_DDR4 == memType )) { // If the user wanted the Basic memory type, return this now. if( BASIC_MEMORY_TYPE == keyword ) { io_buflen = MEM_TYPE_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 ); // User could have installed a bad/unsupported dimm err->addHwCallout( i_target, HWAS::SRCI_PRIORITY_HIGH, HWAS::DECONFIG, HWAS::GARD_NULL ); err->addProcedureCallout(HWAS::EPUB_PRC_HB_CODE, HWAS::SRCI_PRIORITY_LOW); err->addProcedureCallout(HWAS::EPUB_PRC_SP_CODE, HWAS::SRCI_PRIORITY_LOW); err->collectTrace( "SPD", 256); break; } } while( 0 ); // If there is an error, add parameter info to log if ( err != NULL ) { VPD::UdVpdParms( i_target, io_buflen, 0, keyword, true ) // read .addToLog(err); } 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 the Basic Memory Type if(( SPD_DDR3 == memType ) || ( SPD_DDR4 == 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 ); // User could have installed a bad/unsupported dimm err->addHwCallout( i_target, HWAS::SRCI_PRIORITY_HIGH, HWAS::DECONFIG, HWAS::GARD_NULL ); err->addProcedureCallout(HWAS::EPUB_PRC_HB_CODE, HWAS::SRCI_PRIORITY_LOW); err->addProcedureCallout(HWAS::EPUB_PRC_SP_CODE, HWAS::SRCI_PRIORITY_LOW); err->collectTrace( "SPD", 256); break; } } while( 0 ); // If there is an error, add parameter info to log if ( err != NULL ) { VPD::UdVpdParms( i_target, io_buflen, 0, keyword, false ) // write .addToLog(err); } 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; 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; 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, true /*Add HB SW Callout*/ ); err->collectTrace( "SPD", 256); 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 { const KeywordData * entry = NULL; err = getKeywordEntry( i_keyword, i_DDRRev, i_target, 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 ), true /*Add HB SW Callout*/ ); err->collectTrace( "SPD", 256); 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; } // Check io_buflen versus size in table err = spdCheckSize( io_buflen, (*entry).length, i_keyword ); 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, i_target, i_DDRRev ); if (err) { break; } } else { // 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).bitMask ) { // Any bit mask/shifting is only applied to the first byte tmpBuffer[0] = tmpBuffer[0] & (*entry).bitMask; tmpBuffer[0] = tmpBuffer[0] >> (*entry).shift; } } // Set length read to the size in the table 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 { const KeywordData * entry = NULL; err = getKeywordEntry( i_keyword, i_DDRRev, i_target, 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 ), true /*Add HB SW Callout*/ ); err->collectTrace( "SPD", 256); 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 ), true /*Add HB SW Callout*/ ); err->collectTrace( "SPD", 256); 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->bitMask ) { // 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 ), true /*Add HB SW Callout*/ ); err->collectTrace( "SPD", 256); 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; } // ------------------------------------------------------------------ // ddr3SpecialCases // ------------------------------------------------------------------ errlHndl_t ddr3SpecialCases(const KeywordData & i_kwdData, void * io_buffer, TARGETING::Target * i_target) { errlHndl_t err = NULL; uint8_t * tmpBuffer = static_cast(io_buffer); TRACSSCOMP( g_trac_spd, ENTER_MRK"ddr3SpecialCases()" ); switch( i_kwdData.keyword ) { // ================================================== // 2 byte - LSB then MSB case CAS_LATENCIES_SUPPORTED: case TRFC_MIN: case MODULE_MANUFACTURER_ID: case MODULE_REVISION_CODE: case DRAM_MANUFACTURER_ID: case MODULE_CRC: case RMM_MFR_ID_CODE: case LRMM_MFR_ID_CODE: // 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.bitMask ) { 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 ); break; // ================================================== // 2 byte - MSB with mask then LSB is 2 more than MSB case TRC_MIN: // 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.bitMask ) { 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 ); 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_DDR3_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_DDR3_SPECIAL_CASES, VPD::VPD_INVALID_SPD_KEYWORD, i_kwdData.keyword, 0x0, true /*Add HB SW Callout*/ ); err->collectTrace( "SPD", 256); break; }; TRACSSCOMP( g_trac_spd, EXIT_MRK"ddr3SpecialCases()" ); return err; } // ------------------------------------------------------------------ // ddr4SpecialCases // ------------------------------------------------------------------ errlHndl_t ddr4SpecialCases(const KeywordData & i_kwdData, void * io_buffer, TARGETING::Target * i_target) { errlHndl_t err = NULL; uint8_t * tmpBuffer = static_cast(io_buffer); TRACSSCOMP( g_trac_spd, ENTER_MRK"ddr4SpecialCases()" ); switch( i_kwdData.keyword ) { // ================================================== // 2 byte - LSB then MSB case TRFC1_MIN: case TRFC2_MIN: case TRFC4_MIN: case BASE_CONFIG_CRC: case MODULE_MANUFACTURER_ID: case DRAM_MANUFACTURER_ID: case MANUFACTURING_SECTION_CRC: case UMM_CRC: case RMM_MFR_ID_CODE: case RMM_CRC: case LRMM_MFR_ID_CODE: case LRMM_CRC: // 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.bitMask ) { 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 ); break; // ================================================== // 2 byte - MSB with mask then LSB is 2 more than MSB case TRC_MIN: // 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.bitMask ) { 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 ); break; // ================================================== // 4 byte - LSB first, no mask case CAS_LATENCIES_SUPPORTED_DDR4: // Get 4th byte err = spdFetchData( i_kwdData.offset, 1, /* Read 1 byte at a time */ &tmpBuffer[0], i_target ); if( err ) break; // Get 3rd Byte err = spdFetchData( (i_kwdData.offset - 1), 1, /* Read 1 byte at a time */ &tmpBuffer[1], i_target ); if( err ) break; // Get 2nd Byte err = spdFetchData( (i_kwdData.offset - 2), 1, /* Read 1 byte at a time */ &tmpBuffer[2], i_target ); if( err ) break; // Get 1st Byte err = spdFetchData( (i_kwdData.offset - 3), 1, /* Read 1 byte at a time */ &tmpBuffer[3], i_target ); break; // ================================================== default: TRACFCOMP( g_trac_spd, ERR_MRK"Unknown keyword (0x%04x) for DDR4 special cases!", i_kwdData.keyword ); /*@ * @errortype * @reasoncode VPD::VPD_INVALID_SPD_KEYWORD * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE * @moduleid VPD::VPD_SPD_DDR4_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_DDR4_SPECIAL_CASES, VPD::VPD_INVALID_SPD_KEYWORD, i_kwdData.keyword, 0x0, true /*Add HB SW Callout*/ ); err->collectTrace( "SPD", 256); break; }; TRACSSCOMP( g_trac_spd, EXIT_MRK"ddr4SpecialCases()" ); return err; } // ------------------------------------------------------------------ // spdSpecialCases // ------------------------------------------------------------------ errlHndl_t spdSpecialCases ( const KeywordData & i_kwdData, void * io_buffer, TARGETING::Target * i_target, uint64_t i_DDRRev ) { errlHndl_t err = NULL; TRACSSCOMP( g_trac_spd, ENTER_MRK"spdSpecialCases()" ); do { // Handle each of the special cases here if( SPD_DDR3 == i_DDRRev ) { err = ddr3SpecialCases(i_kwdData,io_buffer,i_target); } else if (SPD_DDR4 == i_DDRRev) { err = ddr4SpecialCases(i_kwdData,io_buffer,i_target); } 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 ); // User could have installed a bad/unsupported dimm err->addHwCallout( i_target, HWAS::SRCI_PRIORITY_HIGH, HWAS::DECONFIG, HWAS::GARD_NULL ); err->addProcedureCallout(HWAS::EPUB_PRC_HB_CODE, HWAS::SRCI_PRIORITY_LOW); err->addProcedureCallout(HWAS::EPUB_PRC_SP_CODE, HWAS::SRCI_PRIORITY_LOW); err->collectTrace( "SPD", 256); 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 ), true /*Add HB SW Callout*/ ); err->collectTrace( "SPD", 256); } return err; } // ------------------------------------------------------------------ // spdReadBinaryFile // ------------------------------------------------------------------ errlHndl_t spdReadBinaryFile ( uint64_t i_byteAddr, size_t i_numBytes, void * o_data ) { errlHndl_t err = NULL; #ifndef __HOSTBOOT_RUNTIME 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, true /*Add HB SW Callout*/ ); err->collectTrace( "SPD", 256); 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()" ); #endif 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; } // Check that a Module Specific keyword is being accessed from a DIMM // of the correct Module Type. modSpecTypes_t modType = NA; err = getModType(modType, i_target, i_memType); if( err ) { break; } // Check Unbuffered Memory Module (UMM) if (UMM == modType) { if (UMM != i_kwdData.modSpec) { 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 ) ); // HB code asked for an unsupprted keyword for this Module err->addProcedureCallout(HWAS::EPUB_PRC_HB_CODE, HWAS::SRCI_PRIORITY_HIGH); // Or user could have installed a bad/unsupported dimm err->addHwCallout( i_target, HWAS::SRCI_PRIORITY_LOW, HWAS::DECONFIG, HWAS::GARD_NULL ); err->collectTrace( "SPD", 256); break; } } // Check Registered Memory Module (RMM) else if (RMM == modType) { if (RMM != i_kwdData.modSpec) { 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 ) ); // HB code asked for an unsupprted keyword for this Module err->addProcedureCallout(HWAS::EPUB_PRC_HB_CODE, HWAS::SRCI_PRIORITY_HIGH); // Or user could have installed a bad/unsupported dimm err->addHwCallout( i_target, HWAS::SRCI_PRIORITY_LOW, HWAS::DECONFIG, HWAS::GARD_NULL ); err->collectTrace( "SPD", 256); break; } } // Check Clocked Memory Module (CMM) else if (CMM == modType) { if (CMM != i_kwdData.modSpec) { 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 ) ); // HB code asked for an unsupprted keyword for this Module err->addProcedureCallout(HWAS::EPUB_PRC_HB_CODE, HWAS::SRCI_PRIORITY_HIGH); // Or user could have installed a bad/unsupported dimm err->addHwCallout( i_target, HWAS::SRCI_PRIORITY_LOW, HWAS::DECONFIG, HWAS::GARD_NULL ); err->collectTrace( "SPD", 256); break; } } // Check Load Reduction Memory Module (LRMM) else if (LRMM == modType) { if (LRMM != i_kwdData.modSpec) { 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 ) ); // HB code asked for an unsupprted keyword for this Module err->addProcedureCallout(HWAS::EPUB_PRC_HB_CODE, HWAS::SRCI_PRIORITY_HIGH); // Or user could have installed a bad/unsupported dimm err->addHwCallout( i_target, HWAS::SRCI_PRIORITY_LOW, HWAS::DECONFIG, HWAS::GARD_NULL ); err->collectTrace( "SPD", 256); 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 Module Type * @userdata2 Memory Type (byte 2) * @devdesc Unsupported Module Type. */ 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 ) ); // HB code asked for an unsupprted keyword for this Module err->addProcedureCallout(HWAS::EPUB_PRC_HB_CODE, HWAS::SRCI_PRIORITY_HIGH); // Or user could have installed a bad/unsupported dimm err->addHwCallout( i_target, HWAS::SRCI_PRIORITY_LOW, HWAS::DECONFIG, HWAS::GARD_NULL ); err->collectTrace( "SPD", 256); 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_SZ, &o_memType, i_target ); TRACUCOMP( g_trac_spd, "SPD::getMemType() - MemType: 0x%02x, Error: %s", o_memType, ((NULL == err) ? "No" : "Yes") ); return err; } // ------------------------------------------------------------------ // getModType // ------------------------------------------------------------------ errlHndl_t getModType ( modSpecTypes_t & o_modType, TARGETING::Target * i_target, uint64_t i_memType ) { errlHndl_t err = NULL; o_modType = NA; uint8_t modTypeVal = 0; err = spdFetchData( MOD_TYPE_ADDR, MOD_TYPE_SZ, &modTypeVal, i_target ); if (err) { TRACFCOMP( g_trac_spd, ERR_MRK"SPD::getModType() - Error querying ModType" ); } else { modTypeVal &= MOD_TYPE_MASK; if (SPD_DDR3 == i_memType) { if ((MOD_TYPE_DDR3_UDIMM == modTypeVal) || (MOD_TYPE_DDR3_SO_DIMM == modTypeVal) || (MOD_TYPE_DDR3_MICRO_DIMM == modTypeVal) || (MOD_TYPE_DDR3_MINI_UDIMM == modTypeVal) || (MOD_TYPE_DDR3_SO_UDIMM == modTypeVal)) { o_modType = UMM; } else if ((MOD_TYPE_DDR3_RDIMM == modTypeVal) || (MOD_TYPE_DDR3_MINI_RDIMM == modTypeVal) || (MOD_TYPE_DDR3_SO_RDIMM == modTypeVal)) { o_modType = RMM; } else if ((MOD_TYPE_DDR3_MINI_CDIMM == modTypeVal) || (MOD_TYPE_DDR3_SO_CDIMM == modTypeVal)) { o_modType = CMM; } else if (MOD_TYPE_DDR3_LRDIMM == modTypeVal) { o_modType = LRMM; } } else if (SPD_DDR4 == i_memType) { if ((MOD_TYPE_DDR4_UDIMM == modTypeVal) || (MOD_TYPE_DDR4_SO_DIMM == modTypeVal)) { o_modType = UMM; } else if (MOD_TYPE_DDR4_RDIMM == modTypeVal) { o_modType = RMM; } else if (MOD_TYPE_DDR4_LRDIMM == modTypeVal) { o_modType = LRMM; } } if (o_modType == NA) { TRACFCOMP( g_trac_spd, ERR_MRK"Module type 0x%02x unrecognized", modTypeVal ); /*@ * @errortype * @reasoncode VPD::VPD_MOD_SPECIFIC_UNSUPPORTED * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE * @moduleid VPD::VPD_SPD_GET_MOD_TYPE * @userdata1 Module Type (byte 3[3:0]) * @userdata2 Memory Type (byte 2) * @devdesc Unrecognized Module Type. */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, VPD::VPD_SPD_GET_MOD_TYPE, VPD::VPD_MOD_SPECIFIC_UNSUPPORTED, modTypeVal, i_memType, true /*Add HB SW Callout*/ ); err->collectTrace( "SPD", 256); } else { TRACUCOMP( g_trac_spd, "SPD::getModType() - Val: 0x%02x, ModType: 0x%02x", modTypeVal, o_modType); } } return err; } // ------------------------------------------------------------------ // getKeywordEntry // ------------------------------------------------------------------ errlHndl_t getKeywordEntry ( uint64_t i_keyword, uint64_t i_memType, TARGETING::Target * i_target, const KeywordData *& o_entry ) { errlHndl_t err = NULL; const KeywordData * kwdData; uint32_t arraySize = 0x0; TRACSSCOMP( g_trac_spd, ENTER_MRK"getKeywordEntry()" ); do { if ( SPD_DDR3 == i_memType ) { arraySize = (sizeof(ddr3Data)/sizeof(ddr3Data[0])); kwdData = ddr3Data; } else if ( SPD_DDR4 == i_memType ) { arraySize = (sizeof(ddr4Data)/sizeof(ddr4Data[0])); kwdData = ddr4Data; } 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 ); // User could have installed a bad/unsupported dimm err->addHwCallout( i_target, HWAS::SRCI_PRIORITY_HIGH, HWAS::DECONFIG, HWAS::GARD_NULL ); err->addProcedureCallout(HWAS::EPUB_PRC_HB_CODE, HWAS::SRCI_PRIORITY_LOW); err->addProcedureCallout(HWAS::EPUB_PRC_SP_CODE, HWAS::SRCI_PRIORITY_LOW); err->collectTrace( "SPD", 256); break; } // Set the searching structure equal to the keyword we're looking for. KeywordData tmpKwdData; tmpKwdData.keyword = i_keyword; const 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, true /*Add HB SW Callout*/ ); err->collectTrace( "SPD", 256); break; } o_entry = entry; } while( 0 ); TRACSSCOMP( g_trac_spd, EXIT_MRK"getKeywordEntry()" ); return err; } } // end namespace SPD