/* IBM_PROLOG_BEGIN_TAG */ /* This is an automatically generated prolog. */ /* */ /* $Source: src/import/generic/memory/lib/utils/mss_rank.H $ */ /* */ /* OpenPOWER HostBoot Project */ /* */ /* Contributors Listed Below - COPYRIGHT 2019 */ /* [+] International Business Machines Corp. */ /* */ /* */ /* 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 mss_rank.H /// @brief Tools to obtain rank info from DIMM or PORT target /// // *HWP HWP Owner: Mark Pizzutillo // *HWP HWP Backup: Stephen Glancy // *HWP Team: Memory // *HWP Level: 2 // *HWP Consumed by: HB:FSP #ifndef _MSS_GENERIC_RANK_H_ #define _MSS_GENERIC_RANK_H_ #include #include #include #include #include namespace mss { namespace rank { /// /// @brief Rank traits class for variations depending on MC /// template class rankTraits; /// /// @brief Rank info class /// template > class info { public: // Delete default constructor info() = delete; /// /// @brief Constructor for info object /// @param[in] i_target DIMM target /// @param[in] i_index dimm rank index 0-3 /// @param[out] o_rc return code, FAPI2_RC_SUCCESS if no error /// info(const fapi2::Target& i_dimm_target, const uint8_t i_index, fapi2::ReturnCode& o_rc) { o_rc = fapi2::FAPI2_RC_SUCCESS; uint8_t l_master_ranks_per_dimm = 0; FAPI_TRY(mss::attr::get_num_master_ranks_per_dimm(i_dimm_target, l_master_ranks_per_dimm)); FAPI_ASSERT((i_index < l_master_ranks_per_dimm), fapi2::MSS_RANK_OUT_OF_RANGE() .set_TARGET(i_dimm_target) .set_RANK(i_index), "Rank %u provided to info constructor for DIMM %s exceeded the number of master ranks per DIMM", i_index, mss::c_str(i_dimm_target)); // Targets iv_dimm_target = i_dimm_target; iv_port_target = mss::find_target(i_dimm_target); // Ranks iv_dimm_rank = i_index; iv_port_rank = iv_dimm_rank + ((mss::index(i_dimm_target) * TT::RANK_INDEX_STEP)); iv_phy_rank = iv_dimm_rank + ((mss::index(i_dimm_target) * TT::PHY_RANK_INDEX_STEP)); fapi_try_exit: o_rc = fapi2::current_err; } /// /// @brief Constructor for info object /// @param[in] i_target PORT target /// @param[in] i_index port rank index 0-7 /// @param[out] o_rc return code, FAPI2_RC_SUCCESS if no error /// info(const fapi2::Target& i_port_target, const uint8_t i_index, fapi2::ReturnCode& o_rc) { o_rc = fapi2::FAPI2_RC_SUCCESS; // Dimm Target const uint8_t l_target_index = i_index / TT::RANK_INDEX_STEP; const auto l_dimms = mss::find_targets(i_port_target); // This assumes that the mc_type max # of dimms per port is correct FAPI_ASSERT( !l_dimms.empty(), fapi2::MSS_EMPTY_VECTOR(). set_FUNCTION(INIT_RANK_INFO). set_TARGET(i_port_target), "Empty dimm vector received on port %s", mss::spd::c_str(i_port_target)); { // This will remain false in two cases: // Either: // 1. The corresponding DIMM index is out of range (caused by port index out of range) // 2. The corresponding DIMMs master_ranks_per_dimm is less than the desired rank bool l_valid_rank = false; for (const auto& l_dimm : l_dimms) { // Since we can't guarantee the order of the returned dimms, we iterate through until we (might) find // the right one. There's a chance it may not exist for example given info(port, rank13) would give us // a dimm index above 2, which may be out of range, so we could never find a matching dimm, so we will // throw that error. // If we do find the right dimm, we need to then make sure that the master_ranks_per_dimm attribute // is greater than the expected dimm rank. if (mss::index(l_dimm) == l_target_index) { uint8_t l_master_ranks_per_dimm = 0; FAPI_TRY(mss::attr::get_num_master_ranks_per_dimm(l_dimm, l_master_ranks_per_dimm)); // The rank passed in matches one of a valid DIMM l_valid_rank = (i_index % TT::RANK_INDEX_STEP) < l_master_ranks_per_dimm; iv_dimm_target = l_dimm; } } FAPI_ASSERT(l_valid_rank, fapi2::MSS_RANK_OUT_OF_RANGE() .set_TARGET(i_port_target) .set_RANK(i_index), "Rank %u provided to rank_info constructor for PORT %s was out of range", i_index, mss::c_str(i_port_target)); // Port Target iv_port_target = i_port_target; // Ranks iv_dimm_rank = i_index % TT::RANK_INDEX_STEP; iv_phy_rank = get_phy_rank_from_port_rank(i_index); iv_port_rank = i_index; } fapi_try_exit: o_rc = fapi2::current_err; } /// /// @brief Accessor for port rank /// @return the number (0-7) of the rank within its port /// inline const uint8_t get_port_rank() const { return iv_port_rank; } /// /// @brief Accessor for rank number of DIMM /// @return the number (0-3) of the DIMM /// inline const uint8_t get_dimm_rank() const { return iv_dimm_rank; } /// /// @brief Accessor for phy rank /// @return the phy rank value /// @note in a hypothetical 2 4-rank dimm configuration (not possible), this value is not valid /// inline const uint8_t get_phy_rank() const { return iv_phy_rank; } /// /// @brief Accessor for DIMM target /// @return the dimm target associated with the rank /// inline const fapi2::Target get_dimm_target() const { return iv_dimm_target; } /// /// @brief Accessor for PORT target /// @return the port target associated with the rank /// inline const fapi2::Target get_port_target() const { return iv_port_target; } /// /// @brief Calculate phy rank given port rank (unit testable) /// /// @param[in] l_port_rank port rank /// @return phy rank /// inline static uint8_t get_phy_rank_from_port_rank(const uint8_t i_port_rank) { const uint8_t l_dimm_rank = i_port_rank % TT::RANK_INDEX_STEP; const uint8_t l_dimm_index = i_port_rank / TT::RANK_INDEX_STEP; // Dimm index * 2 sets the center point at 2 // Add on the dimm rank // Note that this logic does NOT apply in a hypothetical/impossible 2 4-rank dimm configuration return ((l_dimm_index * TT::PHY_RANK_INDEX_STEP) + (l_dimm_rank)); } private: fapi2::Target iv_dimm_target; fapi2::Target iv_port_target; uint8_t iv_dimm_rank; uint8_t iv_port_rank; // Note! a configuration of 2 4-rank dimms is not possible. // In this hypothetical scenario, the value for phy-rank would not // be valid / does not apply, as there will be some rollover. // 1 rank-per-dimm: 0 dimm0 2 dimm1 // 2 rank-per-dimm: 0/1 dimm0 2/3 dimm1. // 2 rank single dimm 0/1 // 4 rank (quad encoded) 0-3. uint8_t iv_phy_rank; }; /// /// @brief Obtains all DIMM ranks on a given port target /// @param[in] i_port_target port target /// @param[out] o_vect vector of ranks /// @return FAPI2_RC_SUCCESS iff success /// template > fapi2::ReturnCode ranks_on_port(const fapi2::Target& i_port_target, std::vector>& o_vect) { o_vect.clear(); uint8_t l_master_ranks_per_dimm[TT::MAX_DIMMS_PER_PORT] = {0}; FAPI_TRY(mss::attr::get_num_master_ranks_per_dimm(i_port_target, l_master_ranks_per_dimm)); FAPI_ASSERT(((l_master_ranks_per_dimm[0] <= TT::MAX_RANKS_PER_DIMM) && (l_master_ranks_per_dimm[1] <= TT::MAX_RANKS_PER_DIMM)), fapi2::MSS_TOO_MANY_PRIMARY_RANKS_ON_PORT() .set_PORT_TARGET(i_port_target), "Primary ranks on PORT %s exceeded %u", mss::c_str(i_port_target), TT::MAX_RANKS_PER_DIMM); for (const auto& l_dimm : mss::find_targets(i_port_target)) { const uint8_t l_dimm_index = mss::index(l_dimm); const uint8_t l_port_index_start = l_dimm_index * TT::RANK_INDEX_STEP; fapi2::ReturnCode l_rc = fapi2::FAPI2_RC_SUCCESS; // For each rank index of the dimm from the port's perspective for (uint8_t l_index = l_port_index_start; l_index < (l_port_index_start + l_master_ranks_per_dimm[l_dimm_index]); ++l_index) { o_vect.push_back(mss::rank::info<>(i_port_target, l_index, l_rc)); FAPI_TRY(l_rc); } } fapi_try_exit: return fapi2::current_err; } /// /// @brief Obtains all ranks on a given DIMM target /// @param[in] i_target DIMM target /// @param[out] o_vect vector of ranks /// @return FAPI2_RC_SUCCESS iff success /// template > fapi2::ReturnCode ranks_on_dimm(const fapi2::Target& i_dimm_target, std::vector>& o_vect) { o_vect.clear(); uint8_t l_master_ranks_per_dimm = 0; FAPI_TRY(mss::attr::get_num_master_ranks_per_dimm(i_dimm_target, l_master_ranks_per_dimm)); FAPI_ASSERT(l_master_ranks_per_dimm <= TT::MAX_RANKS_PER_DIMM, fapi2::MSS_TOO_MANY_PRIMARY_RANKS_ON_DIMM() .set_RANK_COUNT(l_master_ranks_per_dimm) .set_DIMM_TARGET(i_dimm_target), "Seeing %d primary ranks on DIMM %s", l_master_ranks_per_dimm, mss::c_str(i_dimm_target)); { // Return code for constructor call fapi2::ReturnCode l_rc = fapi2::FAPI2_RC_SUCCESS; // For each rank index from the dimm's perspective for (uint8_t l_index = 0; l_index < l_master_ranks_per_dimm; l_index++) { o_vect.push_back(mss::rank::info<>(i_dimm_target, l_index, l_rc)); FAPI_TRY(l_rc, "ranks_on_dimm(): Error in rank::info constructor call for DIMM %s and rank %u", mss::c_str(i_dimm_target), l_index); } } return fapi2::FAPI2_RC_SUCCESS; fapi_try_exit: return fapi2::current_err; } } // rank } // mss #endif