diff options
author | Roland Veloz <rveloz@us.ibm.com> | 2019-02-14 12:45:45 -0600 |
---|---|---|
committer | Christian R. Geddes <crgeddes@us.ibm.com> | 2019-02-19 11:26:25 -0600 |
commit | cbc7571007852b87d2d3144d2aa58406f4167fcb (patch) | |
tree | 90fedfefc48885b0a4410e562f40a630d84411f0 | |
parent | 1fba0b5d505f374794ad1428ea8ba7a8b299adab (diff) | |
download | talos-hostboot-cbc7571007852b87d2d3144d2aa58406f4167fcb.tar.gz talos-hostboot-cbc7571007852b87d2d3144d2aa58406f4167fcb.zip |
Create FAPI mapping function for DDIM SPD data
- Created files p9_get_ddr_efd.mk/H/C which contain the FAPI
mapping function
Change-Id: I0f21380b7f6234ab3b88ffc0490c36e5dcd204e0
RTC:203718
Reviewed-on: http://rchgit01.rchland.ibm.com/gerrit1/71910
Tested-by: FSP CI Jenkins <fsp-CI-jenkins+hostboot@us.ibm.com>
Tested-by: Jenkins Server <pfd-jenkins+hostboot@us.ibm.com>
Reviewed-by: Louis Stermole <stermole@us.ibm.com>
Reviewed-by: Christian R. Geddes <crgeddes@us.ibm.com>
Reviewed-by: Matt K. Light <mklight@us.ibm.com>
Reviewed-by: Jennifer A. Stofer <stofer@us.ibm.com>
Reviewed-on: http://rchgit01.rchland.ibm.com/gerrit1/72072
Tested-by: Jenkins OP Build CI <op-jenkins+hostboot@us.ibm.com>
Tested-by: Jenkins OP HW <op-hw-jenkins+hostboot@us.ibm.com>
3 files changed, 881 insertions, 0 deletions
diff --git a/src/import/chips/p9/procedures/hwp/accessors/ddimm_get_efd.C b/src/import/chips/p9/procedures/hwp/accessors/ddimm_get_efd.C new file mode 100644 index 000000000..718073d5b --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/accessors/ddimm_get_efd.C @@ -0,0 +1,756 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/import/chips/p9/procedures/hwp/accessors/ddimm_get_efd.C $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2016,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 ddimm_get_efd.C +/// @brief Return the DRR's EFD based on VPDInfo +/// +/// This procedure was coded using the specs found in +/// document 1U2UDDIMM_SPD.docx, V 0.2.1, 1/17/19, +/// +/// DMB - Differential Memory Buffer +/// DDIMM - Differential Dual In-Line Module +/// EFD - Extended Function Descriptor +/// MR - Master Rank +/// SPD - Serial Presence Detect + +#include <stdint.h> // uint8_t, uint64_t +#include <endian.h> // le64toh +#include <fapi2.H> // anything prefixed with fapi2:: +#include <ddimm_get_efd.H> + +//remove the following comments for unit testing +//#undef FAPI_DBG +//#define FAPI_DBG(args...) FAPI_INF(args) + +/// SPD memory addressing constants +/// These offsets are relative to the start of the SPD +/// 0 based addressing +// Offset to the memory type (DDR4, etc) of the DDIMM. +// size is 1 byte: address 2 +const size_t SPD_MEM_TYPE_ADDR = 2; +// Offset to the SPD's DMB manufacturer ID +// See SPD_DDR4_EXPECTED_DMB_MFG_ID below for the expected value for DDR4 +// size is 2 bytes; address 198 - 199 +const size_t SPD_DMB_DMB_MFG_ID_ADDR = 198; +// Offset to the SPD's DMB revision. +// See SPD_DDR4_EXPECTED_DMB_REVISION below for the expected value for DDR4 +// size is 1 byte: address 200 +const size_t SPD_DMB_REVISION_ADDR = 200; +// Offset to the EFD memory space/block offset. The value, at this location, +// is where the EFD block data offset begins. +// size is 8 bytes: address 277 to 284 +const size_t SPD_EFD_MEMORY_SPACE_OFFSET_ADDR = 277; +// Offset to the EFD's memory space size. The value at this offset is used +// to calculate the actual EFD memory size (see calculateEfdMemorySpaceSize(..)) +// size is 1 byte: address 285 +// only bits 0 - 4 used (see following MASK const) +const size_t SPD_EFD_MEMORY_SPACE_SIZE_ADDR = 285; +// Bit mask to the EFD memory space size +// Bits 0 - 4: Valid values are 0 - 5; 0=1K, 1=2KB, 2=4KB, 3=8KB, 4=16KB, 5=32KB +const uint8_t SPD_EFD_MEMORY_SPACE_SIZE_MASK = 0x0F; +const uint8_t SPD_EFD_MEMORY_SPACE_SIZE_MAX_VALUE = 0x05; +// Offset to the number of all EFDs that is contained in the EFD memory space. +// The value at this offset is how many EFDs exists within the EFD memory space. +// size is 2 bytes; address 286 to 287 +const size_t SPD_EFD_COUNT_ADDR = 286; +// Bit mask to the number of all EFDs +// Bits 0 - 5: Valid values are 0 - 63(0x003F) +const uint16_t SPD_EFD_COUNT_MASK = 0x003F; +// Offset to the EFD meta data within the SPD. +// size is 128 bytes; address 288 to 415; 32 EFD meta data's sized 4 bytes each +const size_t SPD_EFD_META_DATA_ADDR = 288; + +/// SPD - EFD meta data constants +// Size of the EFD meta data's within the SPD +// size is 4 bytes +const size_t SPD_EFD_META_DATA_BYTE_SIZE = 4; + +/// SPD - EFD meta data addressing constants. +/// These offsets are relative to the start of a given EFD meta data section, +/// located in the EFD meta data block, as describe in const +/// SPD_EFD_META_DATA_ADDR above +/// 0 based addressing +// Byte 1 offset to an individual EFD meta data +// Bits 0 - 4: EFD Extended Function Type +// size is 1 byte; address 1 +const size_t SPD_EFD_META_DATA_EFD_BYTE_1_OFFSET = 1; +const size_t SPD_EFD_META_DATA_EFD_FUNCTION_TYPE_MASK = 0x0F; +// Byte 2 offset to an individual EFD meta data +// Bits 0 - 4: The end to the EFD block offset in the EFD memory space +// SPD_EFD_META_DATA_EFD_BLOCK_OFFSET_MASK can mask these bits out +// size is 1 byte; address 2 +const size_t SPD_EFD_META_DATA_EFD_BYTE_2_OFFSET = 2; +const size_t SPD_EFD_META_DATA_EFD_BLOCK_OFFSET_MASK = 0x1F; +// Byte 3 offset to an individual EFD meta data +// Bits 0 - 4: EFD block offset extension to an EFD in the EFD memory space +// Currently not used +// Bits 7: Flag that indicates if an EFD, in the EFD memory space, is implemented +// SPD_EFD_META_DATA_EFD_IS_IMPLEMENTED_MASK can mask this bit out +// size is 1 byte; address 3 +const size_t SPD_EFD_META_DATA_EFD_BYTE_3_OFFSET = 3; +const size_t SPD_EFD_META_DATA_EFD_IS_IMPLEMENTED_MASK = 0x80; + +/// EFD constants +// The EFD block size increments in multiple of 32 bytes +const size_t SPD_EFD_INCREMENTAL_BLOCK_BYTE_SIZE = 32; +// EFD total memory space size can be 1KB, 2KB, 4KB, 8KB, etc., +// This value is used in an exponential function of the form y = ab^x +// Where 'EFD total memory space size' is y, EFD_EXPONENTIAL_BLOCK_MEMORY_SIZE +// is a, value found at SPD_EFD_MEMORY_SPACE_SIZE_ADDR is b, and x is 2 +// also see SPD_EFD_MEMORY_SPACE_SIZE_ADDR and calculateEfdMemorySpaceSize(..) +const size_t EFD_EXPONENTIAL_BLOCK_MEMORY_SIZE = 1024; + +/// EFD - DDR4 memory addressing constants. +/// These offsets are relative to the start of the actual EFD block +/// as describe in const SPD_EFD_MEMORY_SPACE_OFFSET_ADDR above +/// 0 based addressing +// Offset to the DDR4's frequency within an individual EFD +// size is 2 bytes: address 0 - 1 +const size_t EFD_DDR4_FREQUENCY_ADDR = 0; +// Offset to the DDR4's master rank data within an individual EFD +// size is 1 byte: address 2 +const size_t EFD_DDR4_MASTER_RANK_ADDR = 2; + +/// SPD - DDR4 expected values +// SPD type for DDR4 as found in SPD byte 2 +// size is 1 byte +const uint8_t SPD_DDR4_TYPE = 0x0C; +// SPD size for DDR4, 512 bytes as described in document 1U2UDDIMM_SPD.docx +const size_t SPD_DDR4_SIZE = 512; +// SPD - DDR4 expected value for the DMB manufacturer ID +// This value is extrapolated from the JEDEC document +// 0x29 is for for Microsemi +// size is 2 bytes +const uint16_t SPD_DDR4_EXPECTED_DMB_MFG_ID = 0x2980; +// SPD - DDR4 expected value for the DMB revision. +// Currently at initial revision - revision 0 +// size is 1 byte +const uint8_t SPD_DDR4_EXPECTED_DMB_REVISION = 0x00; + +/// Local enumerations +// * Bytes 205 and 206 of SPD and bytes 0 & 1 of EFD contain the frequency info. +enum DDR_FREQUENCY : uint16_t +{ + DDR4_FREQ_VAL_0 = 12800, + DDR4_FREQ_VAL_1 = 14930, + DDR4_FREQ_VAL_2 = 17060, + DDR4_FREQ_VAL_3 = 19200, + DDR4_FREQ_VAL_4 = 21330, + DDR4_FREQ_VAL_5 = 23460, + DDR4_FREQ_VAL_6 = 25600, + + DDR_FREQ_BIT_MASK_0 = 0x0001, + DDR_FREQ_BIT_MASK_1 = 0x0002, + DDR_FREQ_BIT_MASK_2 = 0x0004, + DDR_FREQ_BIT_MASK_3 = 0x0008, + DDR_FREQ_BIT_MASK_4 = 0x0010, + DDR_FREQ_BIT_MASK_5 = 0x0020, + DDR_FREQ_BIT_MASK_6 = 0x0040, +}; + +// * Byte 2 of an EFD contains the master rank data. +enum DDR_MASTER_RANK : uint8_t +{ + DDR_MR_VAL_0 = 0, // MR0 + DDR_MR_VAL_1 = 1, // MR1 + DDR_MR_VAL_2 = 2, // MR2 + DDR_MR_VAL_3 = 3, // MR3 + + DDR_MR_BIT_MASK_0 = 0x01, + DDR_MR_BIT_MASK_1 = 0x02, + DDR_MR_BIT_MASK_2 = 0x04, + DDR_MR_BIT_MASK_3 = 0x08, +}; + +/// Local utilities +// @brief Maps the frequency numeric value to it's bit mask equivalent +// Uses the enums in DDR_FREQUENCY above +// +// @param[in] i_frequency, the frequency in numeric form +// @return the bit mask that represents given frequency if found, else 0 +uint16_t ddrFrequencyToBitMask(const uint64_t i_frequency) +{ + uint16_t l_bitMask{0}; + + switch (i_frequency) + { + case DDR4_FREQ_VAL_0: + l_bitMask = DDR_FREQ_BIT_MASK_0; + break; + + case DDR4_FREQ_VAL_1: + l_bitMask = DDR_FREQ_BIT_MASK_1; + break; + + case DDR4_FREQ_VAL_2: + l_bitMask = DDR_FREQ_BIT_MASK_2; + break; + + case DDR4_FREQ_VAL_3: + l_bitMask = DDR_FREQ_BIT_MASK_3; + break; + + case DDR4_FREQ_VAL_4: + l_bitMask = DDR_FREQ_BIT_MASK_4; + break; + + case DDR4_FREQ_VAL_5: + l_bitMask = DDR_FREQ_BIT_MASK_5; + break; + + case DDR4_FREQ_VAL_6: + l_bitMask = DDR_FREQ_BIT_MASK_6; + break; + + default: + l_bitMask = 0; + break; + } + + return l_bitMask; +} + +// @brief Maps the given master rank numeric value to it's bit mask equivalent +// Uses the enums in DDR_FREQUENCY above +// +// @param[in] i_masterRank, the master rank in numeric form +// @return the bit mask that represents given master rank if found, else 0 +uint8_t ddrMasterRankToBitMask(const uint64_t i_masterRank) +{ + uint8_t l_bitMask{0}; + + switch (i_masterRank) + { + case DDR_MR_VAL_0: + l_bitMask = DDR_MR_BIT_MASK_0; + break; + + case DDR_MR_VAL_1: + l_bitMask = DDR_MR_BIT_MASK_1; + break; + + case DDR_MR_VAL_2: + l_bitMask = DDR_MR_BIT_MASK_2; + break; + + case DDR_MR_VAL_3: + l_bitMask = DDR_MR_BIT_MASK_3; + break; + + default: + l_bitMask = 0; + break; + } + + return l_bitMask; +}; + +// @brief Calculate the EFD memory space size using the exponential factor +// +// @param[in] i_exponentialFactor, the memory space 1KB exponential factor +// @return the calculated memory space size or 0 if error +uint64_t calculateEfdMemorySpaceSize(const uint8_t i_exponentialFactor) +{ + // The exponential factor is the exponent in the power of 2 equation - + // 2^i_exponentialFactor. '2^i_exponentialFactor' is how many 1KB memory + // blocks the EFD contains. To get the full size of the EFD, multiply + // 2^i_exponentialFactor by 1KB (EFD_EXPONENTIAL_BLOCK_MEMORY_SIZE). + + uint64_t retVal = 0; + + if (i_exponentialFactor <= SPD_EFD_MEMORY_SPACE_SIZE_MAX_VALUE) + { + // 1 << i_exponentialFactor - a quick way to do a power of 2 + retVal = EFD_EXPONENTIAL_BLOCK_MEMORY_SIZE * (1 << i_exponentialFactor); + } + + return retVal; +}; +extern "C" +{ + +/// ddr4_get_efd forward declaration +/// @brief Return the DDR4's EFD based on VPDInfo +/// This procedure explicitly returns the EFD data, associated with a +/// DDR4, that matches given frequency and master rank criteria. +/// +/// @param[in] i_ocmbFapi2Target, a valid fapi2 OCMB_CHIP target +/// @param[in] io_vpdInfo, @see ddimm_get_efd +/// @param[out] o_efdData, @see ddimm_get_efd +/// @param[in] i_spdBuffer, pointer to the DDR4 SPD data +/// @param[in] i_spdBufferSize, size of the DDR4 SPD data +/// @note The size of blob may be less than io_vpdInfo.iv_size +/// @note If data is returned for o_efdData, it will be in little endian +/// @note Caller is responsible for allocating the buffers of o_efdData and +/// i_spdBuffer. This procedure will NOT manage these buffers. This +/// procedure will only read/write to buffers, not allocate. +/// @return FAPI2_RC_SUCCESS iff ok + fapi2::ReturnCode ddr4_get_efd( + const fapi2::Target<fapi2::TARGET_TYPE_OCMB_CHIP>& i_ocmbFapi2Target, + fapi2::VPDInfo<fapi2::TARGET_TYPE_OCMB_CHIP>& io_vpdInfo, // Can modify data + uint8_t* const o_efdData, // Don't change pointer but can modify data + const uint8_t* const i_spdBuffer, // Don't change pointer nor modify data + size_t i_spdBufferSize); // Don't modify + +// ddimm_get_efd + fapi2::ReturnCode ddimm_get_efd( + const fapi2::Target<fapi2::TARGET_TYPE_OCMB_CHIP>& i_ocmbFapi2Target, + fapi2::VPDInfo<fapi2::TARGET_TYPE_OCMB_CHIP>& io_vpdInfo, + uint8_t* const o_efdData, + const uint8_t* const i_spdBuffer, + const size_t i_spdBufferSize) + { + FAPI_DBG("ddimm_get_efd: enter"); + + // Initialize the error flag to success + fapi2::current_err = fapi2::FAPI2_RC_SUCCESS; + + // Determine DDIMM type and do some sanity checks + // SPD size must be large enough to gather the DDIMM type info + FAPI_ASSERT( (i_spdBufferSize > SPD_MEM_TYPE_ADDR), + fapi2::TEST_ERROR_A().set_TARGET(i_ocmbFapi2Target), + "SPD data size (%d) is insufficient to gather data from.", + i_spdBufferSize); + + // If working with a DDR4 then the SPD data size must + // be at a minumum of what is required for a DDR4 + FAPI_ASSERT( ( (i_spdBuffer[SPD_MEM_TYPE_ADDR] == SPD_DDR4_TYPE) && + (i_spdBufferSize >= SPD_DDR4_SIZE) ), + fapi2::TEST_ERROR_A().set_TARGET(i_ocmbFapi2Target), + "Either memory type(0x%.2X) is not valid or size " + "of SPD data(%d) is insufficient for type", + i_spdBuffer[SPD_MEM_TYPE_ADDR], + i_spdBufferSize); + + FAPI_DBG ("ddimm_get_efd: working with a DDR4"); + + // Call the explicit code for a DDR4 + FAPI_TRY(ddr4_get_efd( i_ocmbFapi2Target, + io_vpdInfo, + o_efdData, + i_spdBuffer, + i_spdBufferSize)); + fapi_try_exit: + + FAPI_DBG("ddimm_get_efd: exiting with %s", + ( (fapi2::current_err == fapi2::FAPI2_RC_SUCCESS) ? + "no errors" : "errors" )); + return fapi2::current_err; + } + +// ddr4_get_efd + fapi2::ReturnCode ddr4_get_efd( + const fapi2::Target<fapi2::TARGET_TYPE_OCMB_CHIP>& i_ocmbFapi2Target, + fapi2::VPDInfo<fapi2::TARGET_TYPE_OCMB_CHIP>& io_vpdInfo, + uint8_t* const o_efdData, + const uint8_t* const i_spdBuffer, + const size_t i_spdBufferSize) + { + FAPI_DBG("ddr4_get_efd: enter"); + + // Initialize the error flag + fapi2::current_err = fapi2::FAPI2_RC_SUCCESS; + + // The variable round up + uint64_t l_efdMemorySpaceOffset{0}; // The EFD memory location + uint64_t l_efdMemorySpaceSize{0}; // The EFD size of the EFD memory space + uint16_t l_efdCount{0}; // The number EFDs + size_t l_efdSize{0}; // The size of an EFD, not EFD meta data + uint16_t l_dmbMfgId{0}; // The DMB manufacture ID + uint16_t l_freqMask{0}; // The frequency mask as found in EFD + uint8_t l_rankMask{0}; // The rank mask as found in EFD + int ii{0}; // A loop index + const uint8_t* l_efdMetaDataPtr + { + nullptr + }; // Pointer the beginning of the EFD meta data + const uint8_t* l_efdDataPtr + { + nullptr + }; // Pointer the beginning of the EFD block + const uint8_t* l_efdMetaDataNptr + { + nullptr + }; // Pointer to an individual EFD meta data + const uint8_t* l_efdDataNptr + { + nullptr + }; // Pointer to an individual EFD + + //// First, set all the variables necessary to retrieve data from + //// the SPD, EFD meta data and the EFD memory space/block + + FAPI_DBG ( "ddr4_get_efd: SPD buffer size = %d, SPD DDR4 size = %d", + i_spdBufferSize, SPD_DDR4_SIZE); + + /// Sanity check the SPD buffer size. + // Make sure the size of the SPD buffer is large enough to contain the + // DDR4 SPD data. If not, can't reliably gather data from the buffer. + FAPI_ASSERT( (i_spdBufferSize >= SPD_DDR4_SIZE), + fapi2::TEST_ERROR_A().set_TARGET(i_ocmbFapi2Target), + "ddr4_get_efd: SPD buffer size = %d is insufficient to " + "gather data from, minimum required size is = %d", + i_spdBufferSize, + SPD_DDR4_SIZE ); + + /// Get the EFD memory space offset (8 bytes) + // The address where the EFD memory space is located at within the + // SPD buffer + l_efdMemorySpaceOffset = *reinterpret_cast<const uint64_t*> + (&i_spdBuffer[SPD_EFD_MEMORY_SPACE_OFFSET_ADDR]); + // Swap endianess to host format. + l_efdMemorySpaceOffset = le64toh(l_efdMemorySpaceOffset); + + FAPI_DBG ("ddr4_get_efd: EFD memory space location = %d", + l_efdMemorySpaceOffset); + + /// Sanity check the EFD memory space offset. + // Make sure the EFD memory space is not within the SPD DDR4 memory + // space. The EFD memory space cannot share the same memory space as + // the SPD DDR4. + FAPI_ASSERT( (l_efdMemorySpaceOffset >= SPD_DDR4_SIZE), + fapi2::TEST_ERROR_A().set_TARGET(i_ocmbFapi2Target), + "ddr4_get_efd: EFD memory space offset = %d resides " + "within the SPD DDR4 buffer size = %d. These two cannot " + "share the same meory space.", + l_efdMemorySpaceOffset, + SPD_DDR4_SIZE ); + + /// Get and convert the EFD's memory space size exponential factor + // Convert to the actual size of the EFD's memory space. + // The EFD memory space size's exponential factor is 8 bytes. + l_efdMemorySpaceSize = calculateEfdMemorySpaceSize( + i_spdBuffer[SPD_EFD_MEMORY_SPACE_SIZE_ADDR] & + SPD_EFD_MEMORY_SPACE_SIZE_MASK); + + FAPI_DBG ("ddr4_get_efd: EFD memory space size exponential factor = %d " + " converted to EFD memory space size = %d ", + static_cast<uint32_t> + (i_spdBuffer[SPD_EFD_MEMORY_SPACE_SIZE_ADDR] & + SPD_EFD_MEMORY_SPACE_SIZE_MASK), + l_efdMemorySpaceSize); + + // If the memory space is 0, then calculating the EFD memory + // space size failed. + FAPI_ASSERT( l_efdMemorySpaceSize, + fapi2::TEST_ERROR_A().set_TARGET(i_ocmbFapi2Target), + "ddr4_get_efd: Conversion unsuccessful for EFD space size " + "exponential factor = 0x%0.2X", + i_spdBuffer[SPD_EFD_MEMORY_SPACE_SIZE_ADDR] ); + + /// Sanity check the EFD memory space offset + EFD memory space size. + // Make sure the size of the SPD buffer is large enough to contain + // the EFD memory space located at the EFD memory space offset. + // From previous assert, the EFD memory space offset is at the + // the end of the DDR4 SPD data. + FAPI_ASSERT( (i_spdBufferSize >= + (l_efdMemorySpaceOffset + l_efdMemorySpaceSize)), + fapi2::TEST_ERROR_A().set_TARGET(i_ocmbFapi2Target), + "ddr4_get_efd: SPD buffer size = %d is insufficient to " + "accommodate the EFD memory space size = %d at " + "offset = %d; a minimum SPD buffer needed = %d", + i_spdBufferSize, + l_efdMemorySpaceSize, + l_efdMemorySpaceOffset, + ( l_efdMemorySpaceOffset + l_efdMemorySpaceSize ) ); + + /// Get the number of EFDs contained within the EFD memory space. + // The number of EFDs value is 2 bytes. + l_efdCount = *reinterpret_cast<const uint16_t*> + (&i_spdBuffer[SPD_EFD_COUNT_ADDR]); + // Swap endianess to host format. + l_efdCount = le16toh(l_efdCount); + // Extract the number of EFDs + l_efdCount &= SPD_EFD_COUNT_MASK; + + FAPI_DBG ("ddr4_get_efd: number of EFDs = %d", l_efdCount); + + /// Sanity check the number of EFDs extracted. + // Make sure there is at least one EFD to work with + FAPI_ASSERT( (l_efdCount), + fapi2::TEST_ERROR_A().set_TARGET(i_ocmbFapi2Target), + "ddr4_get_efd: The number of EFDs = %d. Need at least one", + l_efdCount); + + // The size of the EFD can be extrapolated from the first EFD. + // Use the ending block info (byte 2) from EFD[0] to determine size. + // Mask out the EFD block multiplier value and multiply + // that with the incremental block byte size + // This value should be constant for all EFDs + l_efdSize = (i_spdBuffer[SPD_EFD_META_DATA_ADDR + + SPD_EFD_META_DATA_EFD_BYTE_2_OFFSET] & + SPD_EFD_META_DATA_EFD_BLOCK_OFFSET_MASK) * + SPD_EFD_INCREMENTAL_BLOCK_BYTE_SIZE; + + FAPI_DBG ("ddr4_get_efd: the EFD block size = %d", l_efdSize); + + // null o_efdData pointer = request for o_efdData size + if ( nullptr == o_efdData ) // just return size + { + io_vpdInfo.iv_size = l_efdSize; + FAPI_INF ("ddr4_get_efd: Caller passed in an EFD data nullptr, so " + "returning size = %d so caller can allocate space for " + "EFD data", + io_vpdInfo.iv_size); + + goto fapi_try_exit; + } + + //// Secondly, get/confirm and set as much outgoing data as possible + + /// Set the outgoing to DDR4 + io_vpdInfo.iv_ddr_mode = SPD_DDR4_TYPE; + + FAPI_DBG ("ddr4_get_efd: DDR mode = 0x%.2X", + io_vpdInfo.iv_ddr_mode); + + /// Confirm that the DMB manufacturer ID of the SPD data + /// is what is expected + // Get the DMB manufacturer ID - 2 bytes + l_dmbMfgId = *reinterpret_cast<const uint16_t*> + (&i_spdBuffer[SPD_DMB_DMB_MFG_ID_ADDR]); + // Swap endianess to host format. + l_dmbMfgId = le16toh(l_dmbMfgId); + + FAPI_DBG ( "ddr4_get_efd: SPD DMB manufacturer ID = 0x%.4X " + "expected DMB manufacturer ID = 0x%.4X", + l_dmbMfgId, + SPD_DDR4_EXPECTED_DMB_MFG_ID ); + + // Confirm the DMB manufacturer ID value is what we expect + FAPI_ASSERT( (SPD_DDR4_EXPECTED_DMB_MFG_ID == l_dmbMfgId), + fapi2::TEST_ERROR_A().set_TARGET(i_ocmbFapi2Target), + "ddr4_get_efd: SPD DMB manufacturer ID 0x%.4X is not the " + "expected DMB manufacturer ID 0x%.4X ", + l_dmbMfgId, + SPD_DDR4_EXPECTED_DMB_REVISION); + + // Set the outgoing DMB manufacturer ID + io_vpdInfo.iv_dmb_mfg_id = SPD_DDR4_EXPECTED_DMB_MFG_ID; + + FAPI_DBG ( "ddr4_get_efd: SPD DMB manufacturer ID = 0x%.4X", + io_vpdInfo.iv_dmb_mfg_id); + + FAPI_DBG ( "ddr4_get_efd: SPD DMB revision = 0x%.2X " + "expected DMB revision = 0x%.2X", + i_spdBuffer[SPD_DMB_REVISION_ADDR], + SPD_DDR4_EXPECTED_DMB_REVISION ); + + /// Confirm that the DMB revision of the SPD data is + /// the revision we expect + FAPI_ASSERT( (SPD_DDR4_EXPECTED_DMB_REVISION == + i_spdBuffer[SPD_DMB_REVISION_ADDR] ), + fapi2::TEST_ERROR_A().set_TARGET(i_ocmbFapi2Target), + "ddr4_get_efd: SPD DMB revision 0x%.2X is not the expected " + "revision 0x%.2X", + i_spdBuffer[SPD_DMB_REVISION_ADDR], + SPD_DDR4_EXPECTED_DMB_REVISION ); + + // Set the outgoing DMB revision + io_vpdInfo.iv_dmb_revision = SPD_DDR4_EXPECTED_DMB_REVISION; + + FAPI_DBG ( "ddr4_get_efd: SPD DMB revision = 0x%.2X", + io_vpdInfo.iv_dmb_revision); + + + //// Thirdly, confirm user input is valid and map user + //// input to usable data + + /// Get the EFD frequency + // Look up the bit mask for the given frequency + // No need to swap endian, already in host format + l_freqMask = ddrFrequencyToBitMask(io_vpdInfo.iv_omi_freq_mhz); + + FAPI_DBG ( "ddr4_get_efd: Caller supplied frquency = %d", + io_vpdInfo.iv_omi_freq_mhz ); + + // If no value for frequency, then mapping of frequency was unsuccessful + FAPI_ASSERT( l_freqMask, + fapi2::TEST_ERROR_A().set_TARGET(i_ocmbFapi2Target), + "ddr4_get_efd: Frequency %d not supported by Axone", + io_vpdInfo.iv_omi_freq_mhz ); + + FAPI_DBG ("ddr4_get_efd: Caller supplied frquency = %d, " + "converted to frequency bit value mask = 0x%.4X", + io_vpdInfo.iv_omi_freq_mhz, l_freqMask); + + /// Get the EFD MR + // Look up the bit mask for the given MR + l_rankMask = ddrMasterRankToBitMask( io_vpdInfo.iv_rank_count); + + // If no value for MR, then mapping of MR was unsuccessful + FAPI_ASSERT( l_rankMask, + fapi2::TEST_ERROR_A().set_TARGET(i_ocmbFapi2Target), + "ddr4_get_efd: Master rank %d not supported by Axone", + io_vpdInfo.iv_rank_count ); + + FAPI_DBG ("ddr4_get_efd: Caller supplied master rank = %d, " + "converted to master rank bit value mask = 0x%.2X", + io_vpdInfo.iv_rank_count, l_rankMask); + + //// Fourthly, find the EFD that matches the given frequency + //// and master rank + + // Point to the beginning of the EFD meta data AKA EFD[0] meta data + // The EFD[0] meta data contains the location and size of the + // individual EFDs + l_efdMetaDataPtr = i_spdBuffer + SPD_EFD_META_DATA_ADDR; + + // Point to the beginning of the EFD data AKA EFD[0] data + l_efdDataPtr = i_spdBuffer + l_efdMemorySpaceOffset; + + FAPI_DBG ("ddr4_get_efd: Looking to match caller supplied frequency = " + "%d (mapped to 0x%.4X) and caller supplied master rank = " + "%d (mapped to 0x%.2X)", + io_vpdInfo.iv_omi_freq_mhz, l_freqMask, + io_vpdInfo.iv_rank_count, l_rankMask); + + // Iterate over the EFDs looking for a match + ii = 0; + + for (; ii < l_efdCount; ++ii) + { + // First, point to the Nth EFD meta data, where N is 0-based. The + // meta data is where the data about the actual EFD's block + // offset preside. + l_efdMetaDataNptr = l_efdMetaDataPtr + + (ii * SPD_EFD_META_DATA_BYTE_SIZE); + + // Secondly, point to the Nth EFD data, where N is 0-based. + // The Nth EFD data is a multiple of the EFD size from the + // beginning of the EFD data + l_efdDataNptr = l_efdDataPtr + (ii * l_efdSize); + + // Sanity check that the EFD and size is within the EFD memory space + FAPI_ASSERT( ( (l_efdMemorySpaceOffset + l_efdMemorySpaceSize) >= + (l_efdMemorySpaceOffset + (ii * l_efdSize) + l_efdSize) ), + fapi2::TEST_ERROR_A().set_TARGET(i_ocmbFapi2Target), + "EFD[%d] at location = %d plus EFD size = %d is " + "outside the bounds of the EFD memory space = %d", + ii, + l_efdMemorySpaceOffset + (ii * l_efdSize), + l_efdSize, + (l_efdMemorySpaceOffset + l_efdMemorySpaceSize) ); + + // From previous assert, we know there is enough buffer to inspect + // the EFD. + // Get the EFD's frequency bit mask + uint16_t l_efdFreqMask = *reinterpret_cast<const uint16_t*> + (&l_efdDataNptr[EFD_DDR4_FREQUENCY_ADDR]); + // Swap endianess to host format. + l_efdFreqMask = le16toh(l_efdFreqMask); + + // If the 'is implemented flag' is true for the EFD, AND if the EFD + // frequency mask contains the frequency mask we are looking for AND + // the EFD master rank matches the master rank we are looking for + // then copy the EFD block for the caller. + if ( (l_efdMetaDataNptr[SPD_EFD_META_DATA_EFD_BYTE_3_OFFSET] & + SPD_EFD_META_DATA_EFD_IS_IMPLEMENTED_MASK) && + (l_efdFreqMask & l_freqMask) && + (l_efdDataNptr[EFD_DDR4_MASTER_RANK_ADDR] == l_rankMask) ) + { + // Make sure the buffer to copy data to is large enough to + // hold the EFD. If not, then assert. + FAPI_ASSERT( (l_efdSize > io_vpdInfo.iv_size), + fapi2::TEST_ERROR_A().set_TARGET(i_ocmbFapi2Target), + "EFD[%d] matches frequency and MR criteria but EFD " + "size %d exceeds outgoing buffer size %d", + ii, + l_efdSize, + io_vpdInfo.iv_size); + + if (io_vpdInfo.iv_size > l_efdSize) + { + // If returning buffer larger than the EFD's size, then + // clear the buffer so no extraneous data is left in buffer + memset(o_efdData, 0, io_vpdInfo.iv_size); + } + + // Copy the EFD data, that matched the given criteria, + // to the out going buffer + memcpy(o_efdData, l_efdDataNptr, l_efdSize); + + // Set the outgoing EFD function type + io_vpdInfo.iv_efd_type = + l_efdMetaDataNptr[SPD_EFD_META_DATA_EFD_BYTE_1_OFFSET] & + SPD_EFD_META_DATA_EFD_FUNCTION_TYPE_MASK; + + FAPI_INF ("ddr4_get_efd: EFD[%d] block matched frequency " + "criteria = 0x%.4X and master rank = 0x%.2X; " + "efd size = %d", + ii, l_freqMask, l_rankMask, l_efdSize); + + break; // exit stage left, we are done + } // end if ((l_efdMetaDataNptr[SPD_EFD_META_DATA_EFD_BYTE_3_OFFSET]... + + // This EFD is not a match, send trace stating so + FAPI_DBG ("ddr4_get_efd: Match failed for EFD[%d], freq bit mask " + "= 0x%.4X, rank bit mask = 0x%.2X, EFD memory location " + "= %d, EFD size = %d, is implemented flag = %d", + ii, + l_efdFreqMask, + l_efdDataNptr[EFD_DDR4_MASTER_RANK_ADDR], + (l_efdMemorySpaceOffset + (ii * l_efdSize)), + l_efdSize, + (l_efdMetaDataNptr[SPD_EFD_META_DATA_EFD_BYTE_3_OFFSET] & + SPD_EFD_META_DATA_EFD_IS_IMPLEMENTED_MASK)); + + } // end for (int i = 0; i < l_efdCount; ++i) + + if (ii >= l_efdCount) + { + // Did not find an EFD to match frequency and master rank criteria + // Collect FFDC and assert if iv_is_config_ffdc_enabled is true + FAPI_ASSERT( ( !io_vpdInfo.iv_is_config_ffdc_enabled ), + fapi2::TEST_ERROR_A().set_TARGET(i_ocmbFapi2Target), + "ALL EFDs have been exhausted. NO match " + "for frequency %d (frequency bit mask 0x%.4X) and " + "master rank %d (master rank bit mask 0x%.2X)", + io_vpdInfo.iv_omi_freq_mhz, l_freqMask, + io_vpdInfo.iv_rank_count, l_rankMask); + + // If unable to collect FFDC and assert, at least trace out error + // and exit with false + FAPI_ERR ("ddr4_get_efd: ALL EFDs have been exhausted. NO match " + "for frequency = %d (frequency bit mask = 0x%.4X) and " + "master rank = %d (master rank bit mask = 0x%.2X)", + io_vpdInfo.iv_omi_freq_mhz, l_freqMask, + io_vpdInfo.iv_rank_count, l_rankMask); + + fapi2::current_err == fapi2::FAPI2_RC_FALSE; + goto fapi_try_exit; + } + + fapi_try_exit: + + FAPI_DBG("ddimm_get_efd: exiting with %s", + ( (fapi2::current_err == fapi2::FAPI2_RC_SUCCESS) ? + "no errors" : "errors" )); + return fapi2::current_err; + + } // end ddimm_get_efd + +} //extern C diff --git a/src/import/chips/p9/procedures/hwp/accessors/ddimm_get_efd.H b/src/import/chips/p9/procedures/hwp/accessors/ddimm_get_efd.H new file mode 100644 index 000000000..0cbded2c1 --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/accessors/ddimm_get_efd.H @@ -0,0 +1,97 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/import/chips/p9/procedures/hwp/accessors/ddimm_get_efd.H $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2016,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 ddimm_get_efd.H +/// @brief Return the DDIMMs EFD based on VPDInfo +/// + +// *HWP HWP Owner: Roland Veloz <rveloz@us.ibm.com> +// *HWP HWP Backup: Christian Geddes <crgeddes@us.ibm.com> +// *HWP FW Owner: Roland Veloz <rveloz@us.ibm.com> +// *HWP Team: +// *HWP Level: 3 +// *HWP Consumed by: Cronus, FSP, HB + +#ifndef _GET_DDR_EFD_H_ +#define _GET_DDR_EFD_H_ + +#include <fapi2.H> +#include <fapi2_vpd_access.H> // VPDInfo<TARGET_TYPE_OCMB_CHIP> + +namespace fapi2 +{ + +} + +// function pointer typedef definition for HWP call support +typedef fapi2::ReturnCode (*p9_ddimm_get_efd_FP_t)( + const fapi2::Target<fapi2::TARGET_TYPE_OCMB_CHIP>&, + fapi2::VPDInfo<fapi2::TARGET_TYPE_OCMB_CHIP>&, + uint8_t* const, + const uint8_t* const, + const size_t); +extern "C" +{ + +/// @brief Return the DDIMMs EFD based on VPDInfo +/// This procedure returns the EFD data that matches given +/// frequency and master rank criteria. +/// +/// @param[in] i_ocmbFapi2Target, a valid fapi2 OCMB_CHIP target +/// @param[in] io_vpdInfo,fapi2::VPDInfo class that specifies the criteria of +/// the desired data to be returned and meta data about returned data +/// @param[in] io_vpd_info.iv_vpd_type, keyword EFD +/// @param[in/out] io_vpd_info.iv_size, as in param - the size of the keyword +/// as an out param - size of keyword if o_blob is a nullptr +/// @param[in] io_vpd_info.iv_omi_freq_mhz, frequency of attached OMI bus +/// @param[in] io_vpd_info.iv_rank_count, number of master ranks, +/// @param[in] io_vpd_info.iv_is_config_ffdc_enabled, flag to collect FFDC or not +/// @param[out] io_vpd_info.iv_efd_type, type of DDIMM SPD +/// @param[out] io_vpd_info.iv_dmb_mfg_id, buffer manufacturer +/// @param[out] io_vpd_info.iv_dmb_revision, buffer revision +/// @param[out] io_vpd_info.iv_ddr_mode, DDR4 or DDR5 +/// @param[in] i_spdBuffer, pointer to the DDR's SPD data +/// @param[in] i_spdBufferSize, size of DDR's SPD data +/// @param[out] o_efdData, this is a pointer to pre-allocated memory that +/// will have the contents of the EFD sought after (if found) +/// or will contain 0, if not found or error occurred. +/// @note The size of blob may be less than io_vpd_info.iv_size +/// @note If data is returned for o_efdData, it will be in little endian +/// @note Caller is responsible for allocating the buffers of o_efdData and +/// i_spdBuffer. This procedure will NOT manage these buffers. This +/// procedure will only read/write to buffers, not allocate. +/// @pre i_spdBuffer cannot be NULL and i_spdBufferSize cannot be 0 but must +/// be size of i_spdBuffer +/// @return FAPI2_RC_SUCCESS iff ok + fapi2::ReturnCode ddimm_get_efd( + const fapi2::Target<fapi2::TARGET_TYPE_OCMB_CHIP>& i_ocmbFapi2Target, + fapi2::VPDInfo<fapi2::TARGET_TYPE_OCMB_CHIP>& io_vpdInfo, + uint8_t* const o_efdData, // Don't change pointer but can modify data + const uint8_t* const i_spdBuffer, // Don't change pointer nor modify data + const size_t i_spdBufferSize); // Don't modify +} + +#endif diff --git a/src/import/chips/p9/procedures/hwp/accessors/ddimm_get_efd.mk b/src/import/chips/p9/procedures/hwp/accessors/ddimm_get_efd.mk new file mode 100644 index 000000000..96849ac7b --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/accessors/ddimm_get_efd.mk @@ -0,0 +1,28 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: src/import/chips/p9/procedures/hwp/accessors/ddimm_get_efd.mk $ +# +# OpenPOWER HostBoot Project +# +# Contributors Listed Below - COPYRIGHT 2016,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 + +# Include the macros and things for MSS procedures +PROCEDURE=ddimm_get_efd +$(call BUILD_PROCEDURE) |