summaryrefslogtreecommitdiffstats
path: root/src/usr/ipmiext
diff options
context:
space:
mode:
authorAndrew Jeffery <andrewrj@au1.ibm.com>2018-09-28 14:48:58 +0930
committerDaniel M. Crowell <dcrowell@us.ibm.com>2018-10-10 13:39:41 -0500
commit5fc457309f2c6bad2bf1464a4cc7756692ede3b8 (patch)
treef7a1190a397d8fe77cd232548f5e8615c0dedab4 /src/usr/ipmiext
parent1b481183921d9877a5693219249bb9c4cd8ccf69 (diff)
downloadtalos-hostboot-5fc457309f2c6bad2bf1464a4cc7756692ede3b8.tar.gz
talos-hostboot-5fc457309f2c6bad2bf1464a4cc7756692ede3b8.zip
ipmi: Split into ipmibase and ipmiext modules
Split the IPMI module into base and ext portions, with the BT interface, device driver and resource provider in the base portion, and all remaining IPMI functionality in the ext portion. The split is in preparation for moving the base functionality in the hostboot base image. Change-Id: Iec864f96240d79f4fadd5519d2ef46437d07c1fd Signed-off-by: Andrew Jeffery <andrewrj@au1.ibm.com> Reviewed-on: http://rchgit01.rchland.ibm.com/gerrit1/66792 Tested-by: Jenkins Server <pfd-jenkins+hostboot@us.ibm.com> Tested-by: Jenkins OP Build CI <op-jenkins+hostboot@us.ibm.com> Reviewed-by: Corey V. Swenson <cswenson@us.ibm.com> Tested-by: FSP CI Jenkins <fsp-CI-jenkins+hostboot@us.ibm.com> Tested-by: Jenkins OP HW <op-hw-jenkins+hostboot@us.ibm.com> Reviewed-by: Daniel M. Crowell <dcrowell@us.ibm.com>
Diffstat (limited to 'src/usr/ipmiext')
-rw-r--r--src/usr/ipmiext/HBconfig5
-rw-r--r--src/usr/ipmiext/ipmi.mk29
-rw-r--r--src/usr/ipmiext/ipmichassiscontrol.C86
-rw-r--r--src/usr/ipmiext/ipmiconfiglookup.C467
-rw-r--r--src/usr/ipmiext/ipmidcmi.C148
-rw-r--r--src/usr/ipmiext/ipmifru.C356
-rw-r--r--src/usr/ipmiext/ipmifru.H127
-rw-r--r--src/usr/ipmiext/ipmifruinv.C2300
-rw-r--r--src/usr/ipmiext/ipmifruinvprvt.H689
-rw-r--r--src/usr/ipmiext/ipmipowerstate.C86
-rw-r--r--src/usr/ipmiext/ipmisel.C596
-rw-r--r--src/usr/ipmiext/ipmisensor.C1762
-rw-r--r--src/usr/ipmiext/ipmiwatchdog.C204
-rw-r--r--src/usr/ipmiext/makefile39
-rw-r--r--src/usr/ipmiext/runtime/makefile38
-rw-r--r--src/usr/ipmiext/runtime/rt_ipmirp.C188
-rw-r--r--src/usr/ipmiext/runtime/test/makefile31
-rw-r--r--src/usr/ipmiext/runtime/test/rt_ipmitest.H102
18 files changed, 7253 insertions, 0 deletions
diff --git a/src/usr/ipmiext/HBconfig b/src/usr/ipmiext/HBconfig
new file mode 100644
index 000000000..218d65fb5
--- /dev/null
+++ b/src/usr/ipmiext/HBconfig
@@ -0,0 +1,5 @@
+config BMC_IPMI_LONG_WATCHDOG
+ default y if CONSOLE_OUTPUT_TRACE || CONSOLE_TRACE_LITE
+ depends on BMC_IPMI
+ help
+ Sets watchdog default timer to several times normal for debugging
diff --git a/src/usr/ipmiext/ipmi.mk b/src/usr/ipmiext/ipmi.mk
new file mode 100644
index 000000000..25930f96c
--- /dev/null
+++ b/src/usr/ipmiext/ipmi.mk
@@ -0,0 +1,29 @@
+# IBM_PROLOG_BEGIN_TAG
+# This is an automatically generated prolog.
+#
+# $Source: src/usr/ipmiext/ipmi.mk $
+#
+# OpenPOWER HostBoot Project
+#
+# Contributors Listed Below - COPYRIGHT 2015,2018
+# [+] 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
+# common objects between hostboot and runtime hostboot
+OBJS += ipmisel.o
+OBJS += ipmisensor.o
+OBJS += ipmidcmi.o
+OBJS += ipmiconfiglookup.o
diff --git a/src/usr/ipmiext/ipmichassiscontrol.C b/src/usr/ipmiext/ipmichassiscontrol.C
new file mode 100644
index 000000000..30ce7fccc
--- /dev/null
+++ b/src/usr/ipmiext/ipmichassiscontrol.C
@@ -0,0 +1,86 @@
+/* IBM_PROLOG_BEGIN_TAG */
+/* This is an automatically generated prolog. */
+/* */
+/* $Source: src/usr/ipmiext/ipmichassiscontrol.C $ */
+/* */
+/* OpenPOWER HostBoot Project */
+/* */
+/* Contributors Listed Below - COPYRIGHT 2014,2018 */
+/* [+] 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 ipmichassiscontrol.C
+ *
+ * Ipmi send chassis control command
+ *
+ */
+
+/******************************************************************************/
+// Includes
+/******************************************************************************/
+#include <stdint.h>
+#include <errl/errlentry.H>
+#include <ipmi/ipmichassiscontrol.H>
+#include <ipmi/ipmiif.H>
+#include <initservice/istepdispatcherif.H>
+
+/******************************************************************************/
+// Globals/Constants
+/******************************************************************************/
+// Defined in ipmidd.C
+extern trace_desc_t * g_trac_ipmi;
+#define IPMI_TRAC(printf_string,args...) \
+ TRACFCOMP(g_trac_ipmi,"cc: " printf_string,##args)
+
+namespace IPMI
+{
+
+/******************************************************************************/
+// Functions
+/******************************************************************************/
+
+errlHndl_t chassisControl(const uint8_t i_chassisControlState )
+{
+ errlHndl_t err_ipmi = NULL;
+
+ size_t len = 1; // IPMI spec has request data at 1 bytes
+
+ //create request data buffer
+ uint8_t* data = new uint8_t[len];
+
+ IPMI::completion_code cc = IPMI::CC_UNKBAD;
+
+ data[0] = i_chassisControlState;
+
+ err_ipmi = IPMI::sendrecv(IPMI::chassis_power_off(), cc, len, data);
+
+ //cleanup buffer
+ delete[] data;
+
+ if(cc != IPMI::CC_OK)
+ {
+ IPMI_TRAC("Chassis control : BMC returned not ok CC[%x]",cc);
+ }
+
+ // power off command has been sent to the BMC, tell the istep dispacher to
+ // stop executing steps.
+ INITSERVICE::stopIpl();
+
+ return err_ipmi;
+}
+} // namespace
+
diff --git a/src/usr/ipmiext/ipmiconfiglookup.C b/src/usr/ipmiext/ipmiconfiglookup.C
new file mode 100644
index 000000000..e2e234661
--- /dev/null
+++ b/src/usr/ipmiext/ipmiconfiglookup.C
@@ -0,0 +1,467 @@
+/* IBM_PROLOG_BEGIN_TAG */
+/* This is an automatically generated prolog. */
+/* */
+/* $Source: src/usr/ipmiext/ipmiconfiglookup.C $ */
+/* */
+/* OpenPOWER HostBoot Project */
+/* */
+/* Contributors Listed Below - COPYRIGHT 2017,2018 */
+/* [+] 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 <ipmi/ipmiconfiglookup.H>
+
+#include <algorithm>
+#include <assert.h>
+
+#include <attributeenums.H>
+#include <ipmi/ipmi_reasoncodes.H>
+#include <targeting/targplatutil.H>
+#include <targeting/common/targetservice.H>
+
+extern trace_desc_t * g_trac_ipmi;
+
+namespace IPMI
+{
+
+//-----------------------------------------------------------------------------
+// Private method used to lookup sensor information from the IPMI_SENSOR_ARRAY
+// attribute of the i_target parameter.
+//
+// Returns true if the sensor was found, false otherwise.
+//-----------------------------------------------------------------------------
+bool IpmiConfigLookup::lookupIPMISensorInfo(TARGETING::Target * i_target,
+ uint32_t i_sensorNumber,
+ uint8_t& o_sensorType,
+ uint8_t& o_entityId,
+ TARGETING::SENSOR_NAME& o_sensorName
+ )
+{
+ using IPMI_ARRAY_ELEMENT = uint16_t[2];
+ bool l_result{false};
+
+ assert(nullptr != i_target);
+ assert(TARGETING::UTIL::INVALID_IPMI_SENSOR != i_sensorNumber);
+
+ //Get the IPMI_SENSOR_ARRAY attribute from i_target.
+ TARGETING::AttributeTraits<TARGETING::ATTR_IPMI_SENSORS>::Type l_ipmiArray;
+ if(!i_target->tryGetAttr<TARGETING::ATTR_IPMI_SENSORS>(l_ipmiArray))
+ {
+ return l_result;
+ }
+
+ //Search the IPMI_SENSOR_ARRAY for the desired sensor.
+ uint32_t elementCount = (sizeof(l_ipmiArray)/sizeof(l_ipmiArray[0]));
+ const IPMI_ARRAY_ELEMENT * begin = &l_ipmiArray[0];
+ const IPMI_ARRAY_ELEMENT * end = &l_ipmiArray[elementCount];
+ const IPMI_ARRAY_ELEMENT * itr{nullptr};
+
+ itr = std::find_if(begin,
+ end,
+ [i_sensorNumber] (const IPMI_ARRAY_ELEMENT& a)
+ {
+ return a[TARGETING::IPMI_SENSOR_ARRAY_NUMBER_OFFSET]
+ == i_sensorNumber;
+ }
+ );
+
+ if(itr != end)
+ {
+ l_result = true;
+ uint16_t l_sensorName = (*itr)
+ [TARGETING::IPMI_SENSOR_ARRAY_NAME_OFFSET];
+ o_sensorName = static_cast<TARGETING::SENSOR_NAME>(l_sensorName);
+ o_sensorType = static_cast<uint8_t>((l_sensorName >> 8) & 0x00FF);
+ o_entityId = static_cast<uint8_t>(l_sensorName & 0x00FF);
+ }
+
+ return l_result;
+}
+
+
+//-----------------------------------------------------------------------------
+// Private method used to lookup sensor information from the GPU_SENSORS
+// array attribute of the i_target parameter.
+//
+// Returns true if the sensor was found, false otherwise.
+//-----------------------------------------------------------------------------
+bool IpmiConfigLookup::lookupGPUSensorInfo(TARGETING::Target * i_target,
+ uint32_t i_sensorNumber,
+ uint8_t& o_sensorType,
+ uint8_t& o_entityId,
+ TARGETING::SENSOR_NAME& o_sensorName
+ )
+{
+ using GPU_ARRAY_ELEMENT = uint16_t[7];
+ bool l_result{false};
+
+ assert(nullptr != i_target);
+ assert(TARGETING::UTIL::INVALID_IPMI_SENSOR != i_sensorNumber);
+
+ // Get the GPU_SENSORS array attribute from i_target
+ TARGETING::AttributeTraits<TARGETING::ATTR_GPU_SENSORS>::Type
+ l_sensorArray;
+ if(!i_target->tryGetAttr<TARGETING::ATTR_GPU_SENSORS>(l_sensorArray))
+ {
+ return l_result;
+ }
+
+ //Search the sensor array for the desired sensor
+ uint32_t elementCount = (sizeof(l_sensorArray)/sizeof(l_sensorArray[0]));
+ const GPU_ARRAY_ELEMENT * begin = &l_sensorArray[0];
+ const GPU_ARRAY_ELEMENT * end = &l_sensorArray[elementCount];
+ const GPU_ARRAY_ELEMENT * itr{nullptr};
+
+ itr = std::find_if(begin,
+ end,
+ [i_sensorNumber] (const GPU_ARRAY_ELEMENT& a)
+ {
+ return (
+ (a[TARGETING::GPU_SENSOR_ARRAY_FUNC_ID_OFFSET] == i_sensorNumber) ||
+ (a[TARGETING::GPU_SENSOR_ARRAY_TEMP_ID_OFFSET] == i_sensorNumber) ||
+ (a[TARGETING::GPU_SENSOR_ARRAY_MEM_TEMP_ID_OFFSET] == i_sensorNumber));
+ }
+ );
+
+ if(itr != end)
+ {
+ l_result = true;
+ uint16_t l_sensorName;
+ if (*itr[TARGETING::GPU_SENSOR_ARRAY_FUNC_ID_OFFSET] == i_sensorNumber)
+ {
+ l_sensorName = *itr[TARGETING::GPU_SENSOR_ARRAY_FUNC_OFFSET];
+ }
+ else if
+ (*itr[TARGETING::GPU_SENSOR_ARRAY_TEMP_ID_OFFSET] == i_sensorNumber)
+ {
+ l_sensorName = *itr[TARGETING::GPU_SENSOR_ARRAY_TEMP_ID_OFFSET];
+ }
+ else
+ {
+ l_sensorName = *itr[TARGETING::GPU_SENSOR_ARRAY_MEM_TEMP_ID_OFFSET];
+ }
+
+ o_sensorName = static_cast<TARGETING::SENSOR_NAME>(l_sensorName);
+ o_sensorType = static_cast<uint8_t>((l_sensorName >> 8) & 0x00FF);
+ o_entityId = static_cast<uint8_t>(l_sensorName & 0x00FF);
+ }
+
+ return l_result;
+}
+
+//--------------------------------------------------------------------------
+//Given a sensor number, lookup and parse SENSOR_NAME into SENSOR_TYPE
+//and ENTITY_ID values.
+//--------------------------------------------------------------------------
+bool IpmiConfigLookup::getIPMISensorInfo(uint32_t i_sensorNumber,
+ uint8_t * o_sensorType,
+ uint8_t * o_entityId,
+ TARGETING::SENSOR_NAME * o_sensorName,
+ TARGETING::Target * i_sensorTarget
+ )
+{
+ bool l_result{false};
+
+ //Ensure that the sensor number is not the invalid id.
+ assert(TARGETING::UTIL::INVALID_IPMI_SENSOR != i_sensorNumber);
+
+ TARGETING::SENSOR_NAME l_sensorName =
+ static_cast<TARGETING::SENSOR_NAME>(0);
+ uint8_t l_sensorType = TARGETING::SENSOR_TYPE_NA;
+ uint8_t l_entityId = TARGETING::ENTITY_ID_NA;
+
+ //Ensure that at least one optional out parameter was given.
+ assert(nullptr != o_sensorType ||
+ nullptr != o_sensorName ||
+ nullptr != o_entityId);
+
+ //If the caller passed in a target then find sensor data within the
+ //context of the passed in target.
+ if(i_sensorTarget)
+ {
+ if(doesTargetHaveIpmiSensorAttr(i_sensorTarget))
+ {
+ l_result = lookupIPMISensorInfo(i_sensorTarget,
+ i_sensorNumber,
+ l_sensorType,
+ l_entityId,
+ l_sensorName);
+ }
+ }
+ else
+ {
+ //The caller did not supply a target context for the sensor.
+ //Search for the sensor amoung all targets.
+ for(auto itr = TARGETING::targetService().begin();
+ itr != TARGETING::targetService().end(); ++itr)
+ {
+ if(doesTargetHaveIpmiSensorAttr(*itr))
+ {
+ l_result = lookupIPMISensorInfo((*itr),
+ i_sensorNumber,
+ l_sensorType,
+ l_entityId,
+ l_sensorName);
+ if(l_result)
+ {
+ break;
+ }
+ }
+ else if (doesTargetHaveGPUSensorsAttr(*itr))
+ {
+ l_result = lookupGPUSensorInfo((*itr),
+ i_sensorNumber,
+ l_sensorType,
+ l_entityId,
+ l_sensorName);
+ if (l_result)
+ {
+ break;
+ }
+
+ }
+ }
+ }
+
+ //set any out parameters that are desired by the caller.
+ if(o_sensorName)
+ {
+ *o_sensorName = l_sensorName;
+ }
+
+ if(o_sensorType)
+ {
+ *o_sensorType = l_sensorType;
+ }
+
+ if(o_entityId)
+ {
+ *o_entityId = l_entityId;
+ }
+
+ return l_result;
+}
+
+//------------------------------------------------------------------
+errlHndl_t IpmiConfigLookup::getSensorType(uint32_t i_sensorNumber,
+ uint8_t & o_sensorType,
+ TARGETING::Target * i_sensorTarget
+ )
+{
+ errlHndl_t l_errl{};
+
+ //Ensure that the sensor number is not the invalid id.
+ if(TARGETING::UTIL::INVALID_IPMI_SENSOR == i_sensorNumber)
+ {
+ TRACFCOMP(g_trac_ipmi,
+ ERR_MRK"The i_sensorNumber parameter "
+ "is the invalid sensor number (%X).",
+ i_sensorNumber
+ );
+ /*@
+ * @errortype ERRL_SEV_UNRECOVERABLE
+ * @moduleid IPMI::MOD_IPMISENSOR_TYPE
+ * @reasoncode IPMI::RC_INVALID_SENSOR_NUMBER
+ * @userdata1 IPMI Sensor Number
+ * @userdata2 <unused>
+ * @devdesc The passed in sensor number is not valid.
+ * @custdesc The passed in sensor number is not valid.
+ */
+ l_errl = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ IPMI::MOD_IPMISENSOR_TYPE,
+ IPMI::RC_INVALID_SENSOR_NUMBER,
+ i_sensorNumber,
+ 0,
+ false);
+ return l_errl;
+ }
+
+ bool l_rc = getIPMISensorInfo(i_sensorNumber,
+ &o_sensorType,
+ nullptr,
+ nullptr,
+ i_sensorTarget
+ );
+
+ if(!l_rc)
+ {
+
+ TRACFCOMP(g_trac_ipmi,
+ ERR_MRK"Did not find a sensor with number %X.",
+ i_sensorNumber);
+
+ /*@
+ * @errortype ERRL_SEV_UNRECOVERABLE
+ * @moduleid IPMI::MOD_IPMISENSOR_TYPE
+ * @reasoncode IPMI::RC_SENSOR_NOT_FOUND
+ * @userdata1 IPMI Sensor Number
+ * @userdata2 <unused>
+ * @devdesc The sensor could not be found based upon
+ * the sensor number.
+ * @custdesc Unable to determine sensor information.
+ */
+ l_errl = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ IPMI::MOD_IPMISENSOR_TYPE,
+ IPMI::RC_SENSOR_NOT_FOUND,
+ i_sensorNumber,
+ 0,
+ false);
+ }
+
+ return l_errl;
+}
+
+//--------------------------------------------------------------------------
+errlHndl_t IpmiConfigLookup::getEntityId(uint32_t i_sensorNumber,
+ uint8_t & o_entityId,
+ TARGETING::Target * i_sensorTarget
+ )
+{
+ errlHndl_t l_errl{};
+
+ if(TARGETING::UTIL::INVALID_IPMI_SENSOR == i_sensorNumber)
+ {
+
+ TRACFCOMP(g_trac_ipmi,
+ ERR_MRK"The i_sensorNumber parameter "
+ "is an invalid sensor number (%X).",
+ i_sensorNumber
+ );
+ /*@
+ * @errortype ERRL_SEV_UNRECOVERABLE
+ * @moduleid IPMI::MOD_IPMISENSOR_ENTITY_ID
+ * @reasoncode IPMI::RC_INVALID_SENSOR_NUMBER
+ * @userdata1 IPMI Sensor Number
+ * @userdata2 <unused>
+ * @devdesc The passed in sensor number is not valid.
+ * @custdesc The passed in sensor number is not valid.
+ */
+ l_errl = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ IPMI::MOD_IPMISENSOR_ENTITY_ID,
+ IPMI::RC_INVALID_SENSOR_NUMBER,
+ i_sensorNumber,
+ 0,
+ false);
+ return l_errl;
+ }
+
+ bool l_rc = getIPMISensorInfo(i_sensorNumber,
+ nullptr,
+ &o_entityId,
+ nullptr,
+ i_sensorTarget
+ );
+
+ if(!l_rc)
+ {
+
+ TRACFCOMP(g_trac_ipmi,
+ ERR_MRK"Did not find a sensor with number %X.",
+ i_sensorNumber);
+
+ /*@
+ * @errortype ERRL_SEV_UNRECOVERABLE
+ * @moduleid IPMI::MOD_IPMISENSOR_ENTITY_ID
+ * @reasoncode IPMI::RC_SENSOR_NOT_FOUND
+ * @userdata1 IPMI Sensor Number
+ * @userdata2 <unused>
+ * @devdesc The sensor could not be found based upon
+ * the sensor number.
+ * @custdesc Unable to determine sensor information.
+ */
+ l_errl = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ IPMI::MOD_IPMISENSOR_ENTITY_ID,
+ IPMI::RC_SENSOR_NOT_FOUND,
+ i_sensorNumber,
+ 0,
+ false);
+ }
+
+ return l_errl;
+}
+
+//-----------------------------------------------------------------------
+errlHndl_t IpmiConfigLookup::getSensorName(uint32_t i_sensorNumber,
+ TARGETING::SENSOR_NAME & o_sensorName,
+ TARGETING::Target * i_sensorTarget
+ )
+{
+ errlHndl_t l_errl{};
+
+ if(TARGETING::UTIL::INVALID_IPMI_SENSOR == i_sensorNumber)
+ {
+
+ TRACFCOMP(g_trac_ipmi,
+ ERR_MRK"The i_sensorNumber parameter "
+ "is an invalid sensor number (%X).",
+ i_sensorNumber
+ );
+
+ /*@
+ * @errortype ERRL_SEV_UNRECOVERABLE
+ * @moduleid IPMI::MOD_IPMISENSOR_NAME
+ * @reasoncode IPMI::RC_INVALID_SENSOR_NUMBER
+ * @userdata1 IPMI Sensor Number
+ * @userdata2 <unused>
+ * @devdesc The passed in sensor number is not valid.
+ * @custdesc The passed in sensor number is not valid.
+ */
+ l_errl = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ IPMI::MOD_IPMISENSOR_NAME,
+ IPMI::RC_INVALID_SENSOR_NUMBER,
+ i_sensorNumber,
+ 0,
+ false);
+ return l_errl;
+ }
+
+ bool l_rc = getIPMISensorInfo(i_sensorNumber,
+ nullptr,
+ nullptr,
+ &o_sensorName,
+ i_sensorTarget
+ );
+
+ if(!l_rc)
+ {
+
+ TRACFCOMP(g_trac_ipmi,
+ ERR_MRK"Did not find a sensor with number %X.",
+ i_sensorNumber);
+
+ /*@
+ * @errortype ERRL_SEV_UNRECOVERABLE
+ * @moduleid IPMI::MOD_IPMISENSOR_NAME
+ * @reasoncode IPMI::RC_SENSOR_NOT_FOUND
+ * @userdata1 IPMI Sensor Number
+ * @userdata2 <unused>
+ * @devdesc The sensor could not be found based upon
+ * the sensor number.
+ * @custdesc Unable to determine sensor information.
+ */
+ l_errl = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ IPMI::MOD_IPMISENSOR_NAME,
+ IPMI::RC_SENSOR_NOT_FOUND,
+ i_sensorNumber,
+ 0,
+ false);
+ }
+
+ return l_errl;
+}
+
+} //End namespace
+
diff --git a/src/usr/ipmiext/ipmidcmi.C b/src/usr/ipmiext/ipmidcmi.C
new file mode 100644
index 000000000..24f192d29
--- /dev/null
+++ b/src/usr/ipmiext/ipmidcmi.C
@@ -0,0 +1,148 @@
+/* IBM_PROLOG_BEGIN_TAG */
+/* This is an automatically generated prolog. */
+/* */
+/* $Source: src/usr/ipmiext/ipmidcmi.C $ */
+/* */
+/* OpenPOWER HostBoot Project */
+/* */
+/* Contributors Listed Below - COPYRIGHT 2015,2018 */
+/* [+] 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 ipmidcmi.C
+ * @brief IPMI DCMI command extensions
+ */
+
+#include <errl/errlentry.H>
+#include <errl/errlmanager.H>
+#include <ipmi/ipmiif.H>
+#include <ipmi/ipmisensor.H>
+#include <ipmi/ipmi_reasoncodes.H>
+extern trace_desc_t * g_trac_ipmi;
+
+namespace SENSOR
+{
+ enum dcmi_cc
+ {
+ POWER_LIMIT_ACTIVE = 0x00,
+ POWER_LIMIT_NOT_ACTIVE = 0x80,
+ };
+
+ // fetch the user defined power limit stored on the BMC
+ // using the DCMI Get Power Limit command
+ errlHndl_t getUserPowerLimit( uint16_t &o_powerLimit, bool &o_limitActive )
+ {
+ o_powerLimit = 0;
+
+ // assume the limit has not been activated
+ o_limitActive = false;
+
+ errlHndl_t l_err = NULL;
+
+ // per DCMI spec data size is 3 bytes
+ size_t len = 3;
+
+ //create request data buffer
+ uint8_t* data = new uint8_t[len];
+
+ IPMI::completion_code cc = IPMI::CC_UNKBAD;
+
+ data[0] = 0xDC; // Group Extension Identification
+ data[1] = 0x00; // reserved
+ data[2] = 0x00; // reserved
+
+ l_err = IPMI::sendrecv(IPMI::get_power_limit(), cc, len, data);
+
+ SENSOR::dcmi_cc l_cc = static_cast<SENSOR::dcmi_cc>(cc);
+
+ if( l_err == NULL )
+ {
+ // make sure the completion code is good, then read the bytes
+ if( (l_cc == POWER_LIMIT_NOT_ACTIVE) ||
+ (l_cc == POWER_LIMIT_ACTIVE) )
+ {
+ // from the dcmi spec
+ // byte 1 completion code
+ // byte 2 0xDC
+ // byte 3:4 reserved
+ // byte 5 exception actions
+ // byte 6:7 power limit
+ // data[0] is pointing at byte 2 of the dcmi spec description
+ // so our offsets will be off by two below
+ o_powerLimit = data[5];
+ o_powerLimit = ( o_powerLimit << 8 ) + data[4];
+
+ // fetch the derating factor from the BMC, then apply it to
+ // the value returned in the getPowerLimit command
+
+ SENSOR::getSensorReadingData o_sensorData;
+
+ // derating factor is held in the system target
+ SENSOR::SensorBase(
+ TARGETING::SENSOR_NAME_DERATING_FACTOR,
+ NULL ).readSensorData( o_sensorData );
+
+ // derate the power limit based on the returned value of the
+ // sensor - derating factor is returned as a % value and is
+ // stored in the event_status field.
+ o_powerLimit = ( static_cast<uint64_t>(o_powerLimit) *
+ o_sensorData.event_status)/100;
+
+ TRACFCOMP(g_trac_ipmi,"Derating factor = %i",o_sensorData.event_status);
+ TRACFCOMP(g_trac_ipmi,"Power limit = 0x%i",o_powerLimit);
+ TRACFCOMP(g_trac_ipmi,"Power limit is %s", ((cc) ? "not active": "active"));
+
+ // the completion code also tells us if the limit is active
+ if(l_cc == POWER_LIMIT_ACTIVE )
+ {
+ o_limitActive = true;
+ }
+ }
+ else
+ {
+ TRACFCOMP(g_trac_ipmi,"bad completion code from BMC=0x%x",cc);
+
+ /*@
+ * @errortype ERRL_SEV_UNRECOVERABLE
+ * @moduleid IPMI::MOD_IPMIDCMI
+ * @reasoncode IPMI::RC_DCMI_CMD_FAILED
+ * @userdata1 BMC IPMI Completion code.
+ * @devdesc Request to get power limit information
+ * failed
+ * @custdesc The DCMI command to retrieve the power limit
+ * data from the BMC has failed, the user
+ * defined power limit cannot be applied.
+ *
+ */
+
+ l_err = new ERRORLOG::ErrlEntry(
+ ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ IPMI::MOD_IPMIDCMI,
+ IPMI::RC_DCMI_CMD_FAILED,
+ static_cast<uint64_t>(cc),0, true);
+
+ l_err->collectTrace(IPMI_COMP_NAME);
+
+ }
+
+ delete[] data;
+ }
+
+ return l_err;
+ }
+
+}; // end name space
diff --git a/src/usr/ipmiext/ipmifru.C b/src/usr/ipmiext/ipmifru.C
new file mode 100644
index 000000000..4d6d19340
--- /dev/null
+++ b/src/usr/ipmiext/ipmifru.C
@@ -0,0 +1,356 @@
+/* IBM_PROLOG_BEGIN_TAG */
+/* This is an automatically generated prolog. */
+/* */
+/* $Source: src/usr/ipmiext/ipmifru.C $ */
+/* */
+/* OpenPOWER HostBoot Project */
+/* */
+/* Contributors Listed Below - COPYRIGHT 2014,2018 */
+/* [+] 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 ipmifru.C
+ * @brief IPMI fru inventory definition
+ */
+
+#include "ipmifru.H"
+#include <devicefw/driverif.H>
+#include <devicefw/userif.H>
+
+#include <sys/task.h>
+#include <builtins.h>
+#include <initservice/taskargs.H>
+#include <initservice/initserviceif.H>
+
+#include <errl/errlmanager.H>
+#include <errl/errlentry.H>
+
+// Defined in ipmidd.C
+extern trace_desc_t * g_trac_ipmi;
+#define IPMI_TRAC(printf_string,args...) \
+ TRACFCOMP(g_trac_ipmi,"fru: " printf_string,##args)
+
+#ifdef CONFIG_BMC_IPMI
+
+const uint8_t writeDataHeader = 3;
+const uint8_t readDataCmd = 4;
+
+/**
+ * setup _start and handle barrier
+ */
+
+/**
+ * @brief Constructor
+ */
+IpmiFRU::IpmiFRU(void):
+ iv_msgQ(msg_q_create())
+{
+ task_create(&IpmiFRU::start, NULL);
+}
+
+/**
+ * @brief Destructor
+ */
+IpmiFRU::~IpmiFRU(void)
+{
+ msg_q_destroy(iv_msgQ);
+}
+
+void* IpmiFRU::start(void* unused)
+{
+ Singleton<IpmiFRU>::instance().execute();
+ return NULL;
+}
+
+/**
+ * @brief Entry point of the fru ipmi thread
+ */
+void IpmiFRU::execute(void)
+{
+ // Mark as an independent daemon so if it crashes we terminate.
+ task_detach();
+
+ IPMI_TRAC(ENTER_MRK "execute: fru message loop");
+ do {
+ while (true)
+ {
+ msg_t* msg = msg_wait(iv_msgQ);
+
+ const IPMIFRU::msg_type msg_type =
+ static_cast<IPMIFRU::msg_type>(msg->type);
+
+ // Invert the "default" by checking here. This allows the compiler
+ // to warn us if the enum has an unhandled case but still catch
+ // runtime errors where msg_type is set out of bounds.
+ assert(msg_type <= IPMIFRU::MSG_LAST_TYPE,
+ "msg_type %d not handled", msg_type);
+
+ switch(msg_type)
+ {
+ case IPMIFRU::MSG_WRITE_FRU_DATA:
+ sendWriteFruData(msg);
+
+ // done with the msg
+ msg_free(msg);
+ break;
+
+ case IPMIFRU::MSG_READ_FRU_DATA:
+ sendReadFruData(msg);
+ msg_respond(iv_msgQ, msg);
+ break;
+ };
+ } // while
+ } while (false);
+
+ return;
+} // execute
+
+///
+/// @brief Send write_fru_data msg to IpmiRP
+/// called for msg->type MSG_WRITE_FRU_DATA
+///
+void IpmiFRU::sendWriteFruData(msg_t *i_msg)
+{
+ errlHndl_t err = NULL;
+ const size_t l_maxBuffer = IPMI::max_buffer();
+
+ // pull out the data - deviceId and offset are in data[0]
+ const uint8_t &l_deviceId = (i_msg->data[0] >> 32);
+ uint16_t l_offset = (i_msg->data[0] & 0xFFFFFFFF);
+ uint16_t l_dataOffset = 0; // start at the l_data[0]
+
+ size_t l_dataSize = i_msg->data[1]; // FRU data size
+ uint8_t *l_data = static_cast<uint8_t*>(i_msg->extra_data);
+
+ IPMI_TRAC(ENTER_MRK "sendWriteFruData for dev 0x%x, size %d",
+ l_deviceId, l_dataSize);
+
+ while ((l_dataSize > 0) && (err == NULL))
+ {
+ size_t this_size = std::min(l_maxBuffer, l_dataSize + writeDataHeader);
+ uint8_t *this_data = new uint8_t[this_size];
+ const uint16_t l_fruSize = this_size - writeDataHeader;
+
+ // copy device ID, offset, fru data to new buffer
+ this_data[0] = l_deviceId;
+ this_data[1] = l_offset & 0xFF;
+ this_data[2] = l_offset >> 8;
+ memcpy(&this_data[writeDataHeader], l_data + l_dataOffset, l_fruSize);
+
+ IPMI_TRAC(INFO_MRK "sending write_fru_data fru size %d offset %d",
+ l_fruSize, l_offset);
+
+ // update the offsets for the next time around
+ l_offset += l_fruSize;
+ l_dataOffset += l_fruSize;
+
+ IPMI::completion_code cc = IPMI::CC_UNKBAD;
+ err = IPMI::sendrecv(IPMI::write_fru_data(), cc, this_size, this_data);
+ if (err)
+ {
+ IPMI_TRAC(ERR_MRK "Failed to send write_fru_data dev 0x%x",
+ l_deviceId);
+ // err is set, so we'll break out of the while loop
+ }
+ else if (cc != IPMI::CC_OK)
+ {
+ IPMI_TRAC(ERR_MRK "Failed to send write_fru_data dev 0x%x CC 0x%x",
+ l_deviceId, cc);
+ // stop sending; breaks out of the while loop
+ l_dataSize = 0;
+ }
+ else
+ {
+ l_dataSize -= l_fruSize;
+ }
+
+ // delete the buffer returned from sendrecv
+ delete [] this_data;
+
+ } // while there is data to send and no error
+
+ if (err)
+ {
+ err->collectTrace(IPMI_COMP_NAME);
+ errlCommit(err, IPMI_COMP_ID);
+ }
+
+ // delete the space the caller allocated; we need to do this because
+ // we're async relative to the caller
+ delete [] l_data;
+
+ return;
+} // sendWriteFruData
+
+void IpmiFRU::sendReadFruData(msg_t *i_msg)
+{
+ errlHndl_t err = NULL;
+
+ const uint8_t &l_deviceId = (i_msg->data[0] >> 32);
+ uint16_t l_offset = (i_msg->data[0] & 0xFFFFFFFF);
+ size_t l_dataSize = i_msg->data[1]; // FRU data size
+ uint8_t *l_data = static_cast<uint8_t*>(i_msg->extra_data);
+
+ uint16_t l_dataOffset = 0; // start at the l_data[0]
+ size_t l_fruSize = 0;
+
+ //TODO RTC 167956
+ const size_t l_maxBuffer = 16;
+
+ IPMI_TRAC(ENTER_MRK "sendReadFruData for dev 0x%x, size %d",
+ l_deviceId, l_dataSize);
+
+ while ((l_dataSize > 0) && (err == NULL))
+ {
+ size_t this_size = readDataCmd;
+ uint8_t *this_data = new uint8_t[this_size];
+
+ l_fruSize = std::min(l_dataSize, l_maxBuffer); // read data size
+
+ // copy device ID, offset, fru data to new buffer
+ this_data[0] = l_deviceId;
+ this_data[1] = l_offset & 0xFF;
+ this_data[2] = l_offset >> 8;
+ this_data[3] = l_fruSize;
+
+ IPMI_TRAC(INFO_MRK "sending read_fru_data fru size %d offset %d, "
+ "data offset %d",
+ l_fruSize, l_offset, l_dataOffset);
+
+ IPMI::completion_code cc = IPMI::CC_UNKBAD;
+ err = IPMI::sendrecv(IPMI::read_fru_data(), cc, this_size, this_data);
+ if (err)
+ {
+ IPMI_TRAC(ERR_MRK "Failed to send read_fru_data dev 0x%x",
+ l_deviceId);
+ // err is set, so we'll break out of the while loop
+ }
+ else if (cc != IPMI::CC_OK)
+ {
+ IPMI_TRAC(ERR_MRK "Failed to send read_fru_data dev 0x%x CC 0x%x",
+ l_deviceId, cc);
+ // stop sending; breaks out of the while loop
+ l_dataSize = 0;
+ } else {
+ this_size = this_data[0];
+ this_size = std::min(this_size, l_dataSize);
+ memcpy(l_data + l_dataOffset, &this_data[1], this_size);
+ // update the offsets for the next time around
+ l_offset += l_fruSize;
+ l_dataOffset += l_fruSize;
+ l_dataSize -= l_fruSize;
+ }
+
+ // delete the buffer returned from sendrecv
+ delete [] this_data;
+ }
+
+ if (err)
+ {
+ err->collectTrace(IPMI_COMP_NAME);
+ errlCommit(err, IPMI_COMP_ID);
+ }
+
+ return;
+} // sendReadFruData
+
+namespace IPMIFRU
+{
+ ///
+ /// @brief Function to send fru data to the IpmiFRU msg queue
+ ///
+ void writeData(uint8_t i_deviceId, uint8_t *i_data,
+ uint32_t i_dataSize, uint32_t i_offset)
+ {
+ IPMI_TRAC(ENTER_MRK "writeData(deviceId 0x%x size %d offset %d)",
+ i_deviceId, i_dataSize, i_offset);
+
+ // one message queue to the FRU thread
+ static msg_q_t mq = Singleton<IpmiFRU>::instance().msgQueue();
+
+ // send data in msg to fru thread
+ msg_t *msg = msg_allocate();
+
+ msg->type = MSG_WRITE_FRU_DATA;
+ msg->data[0] = (static_cast<uint64_t>(i_deviceId) << 32) | i_offset;
+ msg->data[1] = i_dataSize;
+
+ uint8_t* l_data = new uint8_t[i_dataSize];
+ memcpy(l_data, i_data, i_dataSize);
+ msg->extra_data = l_data;
+
+ //Send the msg (async) to the fru thread
+ int rc = msg_send(mq, msg);
+
+ //Return code is non-zero when the message queue is invalid
+ //or the message type is invalid.
+ if ( rc )
+ {
+ IPMI_TRAC(ERR_MRK "Failed (rc=%d) to send message for dev 0x%x.",
+ rc, i_deviceId);
+ delete [] l_data; // delete, since msg wasn't sent
+ }
+
+ IPMI_TRAC(EXIT_MRK "writeData");
+ return;
+ } // writeData
+
+ void readData(uint8_t i_deviceId, uint8_t *io_data)
+ {
+ IPMI_TRAC(ENTER_MRK "readData(deviceId 0x%x", i_deviceId);
+
+ // one message queue to the FRU thread
+ static msg_q_t mq = Singleton<IpmiFRU>::instance().msgQueue();
+ uint32_t i_offset = 0;
+ size_t i_dataSize = IPMIFRU::MAX_RECORD_SIZE;
+
+ // send data in msg to fru thread
+ msg_t *msg = msg_allocate();
+
+ msg->type = MSG_READ_FRU_DATA;
+ msg->data[0] = (static_cast<uint64_t>(i_deviceId) << 32) | i_offset;
+ msg->data[1] = i_dataSize;
+
+ //Setup data structure to pass in message to read FRU record
+ uint8_t *l_data = new uint8_t[i_dataSize];
+ memset(l_data, 0, i_dataSize);
+ memset(io_data, 0, i_dataSize);
+ msg->extra_data = l_data;
+
+ //Send the msg (sync) to the fru thread
+ int rc = msg_sendrecv(mq, msg);
+
+ //Return code is non-zero when the message queue is invalid
+ //or the message type is invalid.
+ if ( rc )
+ {
+ IPMI_TRAC(ERR_MRK "Failed (rc=%d) to send message to read"
+ "FRU Inventory Record: %d",
+ rc, i_deviceId);
+ delete [] l_data; // delete, since msg wasn't sent
+ return;
+ }
+
+ //Copy data from local data structure to output data structure
+ memcpy(&io_data[0], l_data, i_dataSize);
+ msg_free(msg);
+
+ return;
+ }
+
+}; // IPMIFRU namespace
+#endif // CONFIG_BMC_IPMI
diff --git a/src/usr/ipmiext/ipmifru.H b/src/usr/ipmiext/ipmifru.H
new file mode 100644
index 000000000..c107e3a11
--- /dev/null
+++ b/src/usr/ipmiext/ipmifru.H
@@ -0,0 +1,127 @@
+/* IBM_PROLOG_BEGIN_TAG */
+/* This is an automatically generated prolog. */
+/* */
+/* $Source: src/usr/ipmiext/ipmifru.H $ */
+/* */
+/* OpenPOWER HostBoot Project */
+/* */
+/* Contributors Listed Below - COPYRIGHT 2014,2018 */
+/* [+] 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 */
+
+#ifndef __IPMI_IPMIFRU_H
+#define __IPMI_IMPIFRU_H
+
+/**
+ * @file ipmifru.H
+ * @brief IPMI FRU inventory declariation
+ */
+
+#include <stdint.h>
+#include <ipmi/ipmiif.H>
+#include <console/consoleif.H>
+
+
+/**
+ *
+ *
+ */
+namespace IPMIFRU
+{
+ void writeData(uint8_t i_deviceId, uint8_t *i_data,
+ uint32_t i_dataSize, uint32_t i_offset = 0);
+
+ /*
+ * @brief Read the IPMI FRU Record
+ * @param[in] deviceId - The device ID to be read
+ * @param[in/out] data - The buffer pointing to the data read
+ */
+ void readData(uint8_t i_deviceId, uint8_t *io_data);
+
+ enum msg_type
+ {
+ MSG_WRITE_FRU_DATA, // async message - no reply
+ MSG_READ_FRU_DATA, // sync message - has reply
+ // Used to check range. Leave as last.
+ MSG_LAST_TYPE = MSG_READ_FRU_DATA,
+ };
+ enum record_info
+ {
+ MAX_RECORD_SIZE = 256,
+ };
+}
+
+
+class IpmiFRU
+{
+ public:
+
+ /**
+ * Thread start routine for the resource provider
+ * @param[in] void*, unused
+ */
+ static void* start(void* unused);
+
+ /**
+ * Default constructor
+ */
+ IpmiFRU(void);
+
+ /**
+ * Destructor
+ */
+ ~IpmiFRU(void);
+
+ /**
+ * @brief Get the message queue associated with this FRU
+ * @param[in] void
+ * @return, a msg_q_t which is the message queue
+ */
+ inline msg_q_t msgQueue(void) const
+ { return iv_msgQ; }
+
+ private:
+
+ /**
+ * Entry point for the fru ipmi thread
+ */
+ void execute(void);
+
+ /**
+ * @brief Handle a message with fru inventory data; msg is async
+ * @param[in] i_msg
+ */
+ void sendWriteFruData(msg_t *i_msg);
+
+ /**
+ * @brief Handle a message with fru inventory data; msg is sync
+ * #param[in] i_msg
+ */
+ void sendReadFruData(msg_t *i_msg);
+
+
+ /**
+ * ipmi fru msg queue
+ */
+ msg_q_t iv_msgQ;
+
+ // Disallow copying this class.
+ IpmiFRU& operator=(const IpmiFRU&);
+ IpmiFRU(const IpmiFRU&);
+};
+
+#endif
diff --git a/src/usr/ipmiext/ipmifruinv.C b/src/usr/ipmiext/ipmifruinv.C
new file mode 100644
index 000000000..8e49ed7ba
--- /dev/null
+++ b/src/usr/ipmiext/ipmifruinv.C
@@ -0,0 +1,2300 @@
+/* IBM_PROLOG_BEGIN_TAG */
+/* This is an automatically generated prolog. */
+/* */
+/* $Source: src/usr/ipmiext/ipmifruinv.C $ */
+/* */
+/* OpenPOWER HostBoot Project */
+/* */
+/* Contributors Listed Below - COPYRIGHT 2014,2018 */
+/* [+] International Business Machines Corp. */
+/* [+] Jim Yuan */
+/* */
+/* */
+/* 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 <vector>
+#include <map>
+#include <vpd/mvpdenums.H>
+#include <devicefw/userif.H>
+#include <vpd/spdenums.H>
+#include <vpd/cvpdenums.H>
+#include <vpd/pvpdenums.H>
+#include <targeting/common/commontargeting.H>
+#include <targeting/common/utilFilter.H>
+#include <errl/errlmanager.H>
+#include <ipmi/ipmifruinv.H>
+#include <ipmi/ipmisensor.H>
+#include "ipmifru.H"
+#include "ipmifruinvprvt.H"
+#include <stdio.h>
+#include <assert.h>
+#include <pnor/pnorif.H>
+#include <ipmi/ipmi_reasoncodes.H>
+#include <console/consoleif.H>
+extern trace_desc_t * g_trac_ipmi;
+
+// JEDEC constants used to calculate memory module capacity
+#define JEDEC_DENSITY_MASK 7
+#define JEDEC_DENSITY_MIN_VALUE 256
+#define JEDEC_MEMORY_BUS_WIDTH_PRI_MASK 3
+#define JEDEC_MEMORY_BUS_WIDTH_PRI_MIN_VALUE 8
+#define JEDEC_DRAM_WIDTH_MASK 3
+#define JEDEC_DRAM_WIDTH_MIN_VALUE 4
+#define JEDEC_RANKS_MASK 3
+#define JEDEC_RANKS_MIN_VALUE 1
+
+/**
+ * @brief Structure described mapping between JEDEC identifiers
+ * and their decoded values.
+ */
+struct JedecNameMap
+{
+ uint16_t id;
+ const char* decoded;
+};
+
+static const JedecNameMap jedecManufacturer[] =
+{
+ { 0x014F, "Transcend Information" },
+ { 0x017A, "Apacer Technology" },
+ { 0x0198, "Kingston" },
+ { 0x0230, "Corsair" },
+ { 0x0245, "Siemens AG" },
+ { 0x04F1, "Toshiba Corporation" },
+ { 0x0831, "Baikal Electronics" },
+ { 0x8001, "AMD" },
+ { 0x8004, "Fujitsu" },
+ { 0x802C, "Micron Technology" },
+ { 0x8054, "Hewlett-Packard" },
+ { 0x8089, "Intel" },
+ { 0x8089, "Intel" },
+ { 0x8096, "LG Semi" },
+ { 0x80A4, "IBM" },
+ { 0x80AD, "SK Hynix" },
+ { 0x80CE, "Samsung Electronics" },
+ { 0x859B, "Crucial Technology" },
+ { 0x8983, "Hewlett Packard Enterprise" }
+};
+
+static const JedecNameMap jedecBasicType[] =
+{
+ { 0x0B, "DDR3" },
+ { 0x0C, "DDR4" },
+ { 0x0E, "DDR4E" },
+ { 0x0F, "LPDDR3" },
+ { 0x10, "LPDDR4" }
+};
+
+static const JedecNameMap jedecModuleType[] =
+{
+ { 0x01, "RDIMM" },
+ { 0x02, "UDIMM" },
+ { 0x03, "SODIMM" },
+ { 0x04, "LRDIMM" },
+ { 0x05, "MINI RDIMM" },
+ { 0x06, "MINI UDIMM" },
+ { 0x08, "SORDIMM 72b"},
+ { 0x09, "SOUDIMM 72b" },
+ { 0x0C, "SODIMM 16b" },
+ { 0x0D, "SODIMM 32b" }
+};
+
+static const JedecNameMap jedecBusWidthPri[] =
+{
+ { 0x00, "8-bit" },
+ { 0x01, "16-bit" },
+ { 0x02, "32-bit" },
+ { 0x03, "64-bit" }
+};
+
+static const JedecNameMap jedecBusWidthExt[] =
+{
+ { 0x00, "non-ECC" },
+ { 0x01, "ECC" }
+};
+
+static const JedecNameMap jedecTckMin[] =
+{
+ { 0x05, "3200" },
+ { 0x06, "2666" },
+ { 0x07, "2400" },
+ { 0x08, "2133" },
+ { 0x09, "1866" },
+ { 0x0A, "1600" }
+};
+
+#define JEDEC_TABLE_SIZE(a) (sizeof(a) / sizeof(a[0]))
+
+/**
+ * @brief Get decoded value of JEDEC id.
+ * @param[in] i_spdId, SPD field identifier (type of JEDEC table)
+ * @param[in] i_jedecId, JEDEC identifier
+ * @return const char*, text string or NULL if identifier is unknown
+ */
+static const char* jedecDecode(uint16_t i_spdId, uint16_t i_jedecId)
+{
+ const JedecNameMap* l_table;
+ size_t l_tableSize;
+
+ switch (i_spdId)
+ {
+ case SPD::MODULE_MANUFACTURER_ID:
+ l_table = jedecManufacturer;
+ l_tableSize = JEDEC_TABLE_SIZE(jedecManufacturer);
+ break;
+ case SPD::BASIC_MEMORY_TYPE:
+ l_table = jedecBasicType;
+ l_tableSize = JEDEC_TABLE_SIZE(jedecBasicType);
+ break;
+ case SPD::MODULE_TYPE:
+ l_table = jedecModuleType;
+ l_tableSize = JEDEC_TABLE_SIZE(jedecModuleType);
+ break;
+ case SPD::MODULE_MEMORY_BUS_WIDTH_PRI:
+ l_table = jedecBusWidthPri;
+ l_tableSize = JEDEC_TABLE_SIZE(jedecBusWidthPri);
+ break;
+ case SPD::MODULE_MEMORY_BUS_WIDTH_EXT:
+ l_table = jedecBusWidthExt;
+ l_tableSize = JEDEC_TABLE_SIZE(jedecBusWidthExt);
+ break;
+ case SPD::TCK_MIN:
+ l_table = jedecTckMin;
+ l_tableSize = JEDEC_TABLE_SIZE(jedecTckMin);
+ break;
+ default:
+ l_table = NULL;
+ l_tableSize = 0;
+ }
+
+ for (size_t i = 0; i < l_tableSize; ++i)
+ {
+ if (l_table[i].id == i_jedecId)
+ {
+ return l_table[i].decoded;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * @brief Compairs two pairs - used for std:sort
+ * @param[in] lhs - left pair for comparison
+ * @param[in] rhs - right pair for comparison
+ */
+inline static bool comparePairs(
+ const std::pair<TARGETING::TargetHandle_t, uint8_t>& i_lhs,
+ const std::pair<TARGETING::TargetHandle_t, uint8_t>& i_rhs)
+{
+ bool l_compare = i_lhs.second < i_rhs.second;
+
+ // in case of a tie, if the left is a Node, sort it first.
+ if (i_lhs.second == i_rhs.second)
+ {
+ if (TARGETING::TYPE_NODE==i_lhs.first->getAttr<TARGETING::ATTR_TYPE>())
+ {
+ l_compare = true;
+ }
+ }
+ return l_compare;
+}
+
+IpmiFruInv::IpmiFruInv(TARGETING::TargetHandle_t i_target)
+ :iv_target(i_target)
+{
+};
+
+IpmiFruInv::~IpmiFruInv()
+{}
+
+
+IpmiFruInv *IpmiFruInv::Factory(TARGETING::TargetHandleList i_targets,
+ bool i_updateData)
+{
+ IpmiFruInv *l_fru = NULL;
+ TARGETING::TargetHandle_t l_target;
+
+ assert( ! i_targets.empty(),
+ "IpmiFruInv::Factory: Input was empty List of Targets");
+ l_target = i_targets[0];
+
+ switch (l_target->getAttr<TARGETING::ATTR_TYPE>())
+ {
+ case TARGETING::TYPE_DIMM:
+ l_fru = new isdimmIpmiFruInv(l_target);
+ break;
+ case TARGETING::TYPE_PROC:
+ l_fru = new procIpmiFruInv(l_target, i_updateData);
+ break;
+ case TARGETING::TYPE_MEMBUF:
+ // A memory riser card will have a mem buff with a distinct FRU ID
+ l_fru = new membufIpmiFruInv(l_target, i_targets, i_updateData);
+ break;
+ case TARGETING::TYPE_NODE:
+ // When the planar eeprom is shared for planar vpd and memory vpd,
+ // the node and membufs will have the same FRU ID. The node has
+ // been sorted ahead of the membufs. The membufs are extra targets
+ // for their ECIDs.
+ l_fru = new backplaneIpmiFruInv(l_target, i_targets, i_updateData);
+ break;
+ case TARGETING::TYPE_SYS:
+ // Use sys target for setting System Firmware Info
+ l_fru = new systemFwIpmiFruInv(l_target);
+ break;
+ default:
+ assert(false,
+ "IpmiFruInv::Factory: No support for target type given: [%08x]",
+ l_target->getAttr<TARGETING::ATTR_TYPE>());
+ break;
+ }
+
+ return l_fru;
+}
+
+void IpmiFruInv::sendFruData(uint8_t i_deviceId)
+{
+ if (iv_record_data.size() > 0)
+ {
+ //Use IMPIFRU::writeData to send data to service processor
+ // it will do any error handling and memory management
+ IPMIFRU::writeData(i_deviceId, &iv_record_data[0],
+ iv_record_data.size(), IPMIFRUINV::DEFAULT_FRU_OFFSET);
+ }
+ else
+ {
+ TRACFCOMP(g_trac_ipmi,"IpmiFruInv::sendFruData: "
+ "Not sending data for deviceId[%08x], no data found for this record.");
+ }
+
+ return;
+}
+
+
+void IpmiFruInv::printRecordDebugData(const std::vector<uint8_t> &i_data)
+{
+ if (i_data.size() > 0)
+ {
+ TRACFBIN(g_trac_ipmi, "IpmiRecordData", &i_data[0], i_data.size());
+ }
+ else
+ {
+ TRACFCOMP(g_trac_ipmi,"IpmiRecordData empty");
+ }
+}
+
+
+//This uses the template method design pattern
+// Since all IPMI Fru Inventory records all contain the same 5 sections
+// (whether they are populated or empty) this funciton will build all 5
+// sections, build the header for the entire record, and then combine all 5
+// records into one full record
+errlHndl_t IpmiFruInv::buildFruInvRecord(void)
+{
+ errlHndl_t l_errl = NULL;
+ std::vector<uint8_t> l_iu_data;
+ std::vector<uint8_t> l_ci_data;
+ std::vector<uint8_t> l_bi_data;
+ std::vector<uint8_t> l_pi_data;
+ std::vector<uint8_t> l_mr_data;
+
+ do {
+ //First build up all 5 records individually
+ l_errl = buildInternalUseArea(l_iu_data);
+ if (l_errl) { break; }
+
+ l_errl = buildChassisInfoArea(l_ci_data);
+ if (l_errl) { break; }
+
+ l_errl = buildBoardInfoArea(l_bi_data);
+ if (l_errl) { break; }
+
+ l_errl = buildProductInfoArea(l_pi_data);
+ if (l_errl) { break; }
+
+ l_errl = buildMultiRecordInfoArea(l_mr_data);
+ if (l_errl) { break; }
+
+ //Now build common header with data for this FRU Inv Record
+ buildCommonHeader(l_iu_data, l_ci_data, l_bi_data,
+ l_pi_data, l_mr_data);
+
+ //Combine everything into one full IPMI Fru Inventory Record
+ completeRecord(l_iu_data, l_ci_data, l_bi_data,
+ l_pi_data, l_mr_data);
+
+ } while(0);
+
+ if (l_errl)
+ {
+ TRACFCOMP(g_trac_ipmi,"IpmiFruInv::buildFruInvRecord Error encountered"
+ " building up Fru Inventory Record sections.");
+ }
+
+ return l_errl;
+}
+
+
+void IpmiFruInv::buildCommonHeader(
+ const std::vector<uint8_t> &i_internal_use_data,
+ const std::vector<uint8_t> &i_chassis_data,
+ const std::vector<uint8_t> &i_board_data,
+ const std::vector<uint8_t> &i_product_data,
+ const std::vector<uint8_t> &i_multirecord_data)
+{
+ //Use this variable to increment size of header as we go along to determine
+ // offset for the subsequent area offsets
+ uint32_t l_cur_data_offset = 0;
+
+ //First byte is id for version of FRU Info Storage Spec used
+ addHeaderFormat(iv_record_data);
+
+ //2nd byte is offset to internal use data
+ buildCommonHeaderSection(iv_record_data, i_internal_use_data.size(),
+ l_cur_data_offset);
+
+ //3rd byte is offset to chassis data
+ buildCommonHeaderSection(iv_record_data, i_chassis_data.size(),
+ l_cur_data_offset);
+
+ //4th byte is offset to board data
+ buildCommonHeaderSection(iv_record_data, i_board_data.size(),
+ l_cur_data_offset);
+
+ //5th byte is offset to product data
+ buildCommonHeaderSection(iv_record_data, i_product_data.size(),
+ l_cur_data_offset);
+
+ //6th byte is offset to multirecord data
+ buildCommonHeaderSection(iv_record_data, i_multirecord_data.size(),
+ l_cur_data_offset);
+
+ //7th byte is PAD
+ padData(iv_record_data);
+
+ //8th (Final byte of Header Format) is the checksum
+ addDataChecksum(iv_record_data);
+}
+
+void IpmiFruInv::completeRecord(const std::vector<uint8_t> &i_internal_use_data,
+ const std::vector<uint8_t> &i_chassis_data,
+ const std::vector<uint8_t> &i_board_data,
+ const std::vector<uint8_t> &i_product_data,
+ const std::vector<uint8_t> &i_multirecord_data)
+{
+ addDataToRecord(i_internal_use_data);
+ addDataToRecord(i_chassis_data);
+ addDataToRecord(i_board_data);
+ addDataToRecord(i_product_data);
+ addDataToRecord(i_multirecord_data);
+}
+
+//Helper function to simply combine vectors together
+void IpmiFruInv::addDataToRecord(const std::vector<uint8_t> &i_data)
+{
+ iv_record_data.insert(iv_record_data.end(), i_data.begin(), i_data.end());
+}
+
+//Helper function to create an 'empty' record
+errlHndl_t IpmiFruInv::buildEmptyArea(std::vector<uint8_t> &i_data)
+{
+ return NULL;
+}
+
+//Helper function to pad a data record. Most of the IPMI Fru Invenotry
+// Record format works with each section being a multiple of 8 bytes
+// so padding is needed to make records properly formatted
+void IpmiFruInv::padData(std::vector<uint8_t> &io_data)
+{
+ uint8_t l_pad_remainder = (io_data.size() + IPMIFRUINV::CHECKSUM_SIZE) %
+ IPMIFRUINV::RECORD_UNIT_OF_MEASUREMENT;
+
+ if (l_pad_remainder)
+ {
+ io_data.insert(io_data.end(),
+ IPMIFRUINV::RECORD_UNIT_OF_MEASUREMENT - l_pad_remainder,
+ uint8_t(0));
+ }
+ return;
+}
+
+//Creates a 2's complement checksum at the end of the given data vector
+void IpmiFruInv::addDataChecksum(std::vector<uint8_t> &io_data)
+{
+ uint8_t l_checksum_val = 0;
+ std::vector<uint8_t>::iterator l_iter;
+
+ for (l_iter = io_data.begin(); l_iter != io_data.end(); ++l_iter)
+ {
+ l_checksum_val += *l_iter;
+ }
+
+ // Push the Zero checksum as the last byte of this data
+ // This appears to be a simple summation of all the bytes
+ io_data.push_back(-l_checksum_val);
+
+ return;
+}
+
+//The Common Header points to the offset for each of the 5 data record
+// sections, this function is used in helping to build that up.
+void IpmiFruInv::buildCommonHeaderSection(std::vector<uint8_t> &io_out_data,
+ uint32_t i_section_data_size,
+ uint32_t &io_cur_data_offset)
+{
+ //Check if data for internal use section populated
+ if (i_section_data_size == 0)
+ {
+ //Indicate record not prsent
+ io_out_data.push_back(IPMIFRUINV::RECORD_NOT_PRESENT);
+ }
+ else {
+ //Place data to define offset to internal_use_data section
+ io_out_data.push_back((io_cur_data_offset +
+ IPMIFRUINV::COMMON_HEADER_FORMAT_SIZE)
+ / IPMIFRUINV::RECORD_UNIT_OF_MEASUREMENT);
+ io_cur_data_offset += i_section_data_size;
+ }
+
+ return;
+}
+
+//Helper function to add the IPMI Fru Inventory Format to the
+// beginning of the data vector passed in
+void IpmiFruInv::addHeaderFormat(std::vector<uint8_t> &io_data)
+{
+ //Add id for version of FRU Info Storage Spec used
+ io_data.push_back(IPMIFRUINV::SPEC_VERSION);
+ return;
+}
+
+//Helper function to complete the formatting for a given section
+// that can be completed prior to adding section data
+// It will add the spec version, create a placeholder for the data
+// size and set the language code if desired
+void IpmiFruInv::preFormatProcessing(std::vector<uint8_t> &io_data,
+ bool i_setLanguageCode)
+{
+ //Add id for version of FRU Info Storage Spec used
+ addHeaderFormat(io_data);
+
+ //Add Data Size - 0 as a placeholder, can edit after the data is finalized
+ io_data.push_back(uint8_t(0));
+
+ if (i_setLanguageCode)
+ {
+ //Add Language Code
+ io_data.push_back(uint8_t(IPMIFRUINV::ENGLISH_LANGUAGE_CODE));
+ }
+}
+//Helper function to complete the formatting for a given section
+// It will calculate overall section size,
+// pad the section if needed, and add the data checksum
+void IpmiFruInv::postFormatProcessing(std::vector<uint8_t> &io_data)
+{
+ //This area needs to be padded to a multiple of 8 bytes (after checksum)
+ padData(io_data);
+
+ //Set size of data info area
+ setAreaSize(io_data, 1);
+
+ //Finally add board info checksum
+ addDataChecksum(io_data);
+
+ return;
+}
+
+//Helper function containing the logic to set the proper size of a data section
+void IpmiFruInv::setAreaSize(std::vector<uint8_t> &io_data, uint8_t i_offset)
+{
+
+ io_data.at(i_offset) = (io_data.size() + IPMIFRUINV::CHECKSUM_SIZE)
+ / IPMIFRUINV::RECORD_UNIT_OF_MEASUREMENT;
+
+ return;
+}
+
+static inline uint8_t bcd2_to_int(uint8_t bcd)
+{
+ return ((bcd >> 4) & 0xF) * 10 + (bcd & 0xF);
+}
+
+// Function to compute the correct data for the Mfg date/time section.
+// IPMI expects the time to be in seconds from 01/01/1996.
+errlHndl_t IpmiFruInv::formatMfgData(std::vector<uint8_t> i_mfgDateData,
+ uint32_t& o_mfgDate)
+{
+ errlHndl_t l_errl = NULL;
+
+ // MB keyword size is 8 hex bytes, throw an error if it is smaller so we
+ // don't do an invalid access.
+ if (i_mfgDateData.size() != 8)
+ {
+ /*@
+ * @errortype
+ * @moduleid IPMI::MOD_IPMIFRU_INV
+ * @reasoncode IPMI::RC_INVALID_VPD_DATA
+ * @userdata1 Size of vpd data
+ *
+ * @devdesc VPD data is invalid size
+ */
+ l_errl = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_INFORMATIONAL,
+ IPMI::MOD_IPMIFRU_INV,
+ IPMI::RC_INVALID_VPD_DATA,
+ i_mfgDateData.size());
+
+ TARGETING::Target* nodeTarget = NULL;
+ TARGETING::PredicateCTM nodeFilter(TARGETING::CLASS_ENC,
+ TARGETING::TYPE_NODE);
+ TARGETING::TargetRangeFilter nodeItr(
+ TARGETING::targetService().begin(),
+ TARGETING::targetService().end(),
+ &nodeFilter);
+
+ nodeTarget = *nodeItr;
+
+ // Callout out node since that is where the VPD lives
+ l_errl->addHwCallout(nodeTarget,
+ HWAS::SRCI_PRIORITY_HIGH,
+ HWAS::NO_DECONFIG,
+ HWAS::GARD_NULL );
+
+ }
+ else
+ {
+ // Convert Centuries / Years / months / day / hour / minute / second
+ // into a uint64 representing number of minute since 1/1/96
+
+ // The vpd data is expected to be in this format VVCCYYmmDDHHMMSS
+ // Note that it is Binary Coded Decimal(BCD). Need to translate to int.
+ uint8_t century = bcd2_to_int(i_mfgDateData.at(1));
+ uint8_t year = bcd2_to_int(i_mfgDateData.at(2));
+ uint8_t month = bcd2_to_int(i_mfgDateData.at(3));
+ uint8_t day = bcd2_to_int(i_mfgDateData.at(4));
+ uint8_t hour = bcd2_to_int(i_mfgDateData.at(5));
+ uint8_t minute = bcd2_to_int(i_mfgDateData.at(6));
+
+ // Subtract year
+ uint8_t numOfYears = (century*100 + year) - 1996;
+ // Subtract month
+ uint8_t numOfMonths = month - 1;
+ // Subtract day
+ uint16_t numOfDays = day - 1;
+
+ // Add the specific number of days for the months given
+ for (uint8_t i=0; i < numOfMonths; i++)
+ {
+ numOfDays += daysInMonth[i];
+ }
+
+ // Add the number of days for the number of year given
+ numOfDays += (numOfYears*365);
+
+ // Add a day for every leap year
+ // Check if we need to consider the current year
+ if (month <= 2)
+ {
+ // We don't need to consider this year for a leap year, as it
+ // wouldn't have happened yet. Decrement a year.
+ year = year - 1;
+ }
+
+ uint8_t numLeapYears = 0;
+ // For every year from 1996 until the build date year, check if it's a
+ // leap year
+ for(uint16_t i = 1996; i <= (century*100 + year); i++)
+ {
+ // If the year is divisible by 4, its a leap year. Don't have to
+ // worry about centuries since the only possible century is 2000
+ // and it was a leap year.
+ if(i % 4 == 0)
+ {
+ numLeapYears++;
+ }
+ }
+
+ numOfDays += numLeapYears;
+
+ // Convert into minutes
+ o_mfgDate = (((numOfDays*24)*60) + (hour*60) + minute);
+
+ }
+
+ return l_errl;
+}
+
+// Function to set the data for the Mfg date/time section.
+void IpmiFruInv::setMfgData(std::vector<uint8_t> &io_data,
+ std::vector<uint8_t> &mfgDateData)
+{
+ errlHndl_t l_errl = NULL;
+ uint32_t mfgDate = 0;
+
+ // Pass mfgDateData vector to format function to get the minute integer
+ l_errl = formatMfgData(mfgDateData, mfgDate);
+ if (l_errl)
+ {
+ // MFG date isn't entierly necessary. Let's just delete and
+ // continue.
+ TRACFCOMP(g_trac_ipmi,"backplaneIpmiFruInv::buildBoardInfoArea - "
+ "Error from formatMfgData. Using default MFG Date/Time.");
+ io_data.push_back(0);
+ io_data.push_back(0);
+ io_data.push_back(0);
+ delete l_errl;
+ l_errl = nullptr;
+ }
+ else
+ {
+ if(((mfgDate & 0xFF000000) >> 24) != 0)
+ {
+ // If there is data in these bits, we have exceeded the
+ // maximum time we can display (IPMI only takes in 3 bytes
+ // of hex, FFFFFF)
+ TRACFCOMP(g_trac_ipmi,"backplaneIpmiFruInv::buildBoardInfoArea "
+ "- Exeeded maximum allowed build date to display. Using "
+ "default MFG Date/Time.");
+ io_data.push_back(0);
+ io_data.push_back(0);
+ io_data.push_back(0);
+ }
+ else
+ {
+ // Convert mfgDate to hex
+ uint8_t l_leastSig = (mfgDate & 0x000000FF);
+ uint8_t l_middleSig = (mfgDate & 0x0000FF00) >> 8;
+ uint8_t l_mostSig = (mfgDate & 0x00FF0000) >> 16;
+
+ // Push data into io_data - least significant byte first
+ io_data.push_back(l_leastSig);
+ io_data.push_back(l_middleSig);
+ io_data.push_back(l_mostSig);
+ }
+ }
+}
+
+
+//##############################################################################
+isdimmIpmiFruInv::isdimmIpmiFruInv( TARGETING::TargetHandle_t i_target )
+ :IpmiFruInv(i_target)
+{
+
+};
+
+errlHndl_t isdimmIpmiFruInv::buildInternalUseArea(std::vector<uint8_t> &io_data)
+{
+ //This section not needed for isdimm type
+ return IpmiFruInv::buildEmptyArea(io_data);
+}
+
+errlHndl_t isdimmIpmiFruInv::buildChassisInfoArea(std::vector<uint8_t> &io_data)
+{
+ //This section not needed for isdimm type
+ return IpmiFruInv::buildEmptyArea(io_data);
+}
+
+errlHndl_t isdimmIpmiFruInv::buildBoardInfoArea(std::vector<uint8_t> &io_data)
+{
+ //This section not needed for isdimm type
+ return IpmiFruInv::buildEmptyArea(io_data);
+}
+
+errlHndl_t isdimmIpmiFruInv::buildMultiRecordInfoArea(
+ std::vector<uint8_t> &io_data)
+{
+ //This section not needed for isdimm type
+ return IpmiFruInv::buildEmptyArea(io_data);
+}
+
+errlHndl_t isdimmIpmiFruInv::buildProductInfoArea(std::vector<uint8_t> &io_data)
+{
+ errlHndl_t l_errl = NULL;
+
+ do {
+ //Set formatting data that goes at the beginning of the record
+ preFormatProcessing(io_data, true);
+
+ //Set Manufacturer Name - Use full info with decoded name
+ l_errl = addSpdManufacturer(io_data);
+ if (l_errl) { break; }
+ //Set Product Name - Use detailed description
+ l_errl = addSpdDetails(io_data);
+ if (l_errl) { break; }
+ //Set Product Part/Model Number
+ l_errl = addVpdData(io_data, SPD::MODULE_PART_NUMBER, true);
+ if (l_errl) { break; }
+ //Set Product Version
+ l_errl = addVpdData(io_data, SPD::MODULE_REVISION_CODE);
+ if (l_errl) { break; }
+ //Set Product Serial Number
+ l_errl = addVpdData(io_data, SPD::MODULE_SERIAL_NUMBER);
+ if (l_errl) { break; }
+
+ //Add Asset Tag
+ io_data.push_back(uint8_t(0)); //No Asset Tag needed - O bytes
+
+ //FRU File ID - Empty
+ io_data.push_back(IPMIFRUINV::TYPELENGTH_BYTE_NULL);
+ io_data.push_back(uint8_t(0)); // Empty FRU File ID bytes
+ io_data.push_back(IPMIFRUINV::END_OF_CUSTOM_FIELDS);
+
+ } while (0);
+
+ //Finalize section formatting
+ postFormatProcessing(io_data);
+
+ if (l_errl)
+ {
+ TRACFCOMP(g_trac_ipmi,"isdimIpmiFruInv::buildProductInfoArea - Errors "
+ "collecting product info data from VPD");
+ }
+
+ return l_errl;
+}
+
+errlHndl_t isdimmIpmiFruInv::addVpdData(std::vector<uint8_t> &io_data,
+ uint8_t i_keyword,
+ bool i_ascii)
+{
+ size_t l_vpdSize = 0;
+ errlHndl_t l_errl = NULL;
+
+ do {
+ //First get size with NULL call:
+ l_errl = deviceRead(iv_target,
+ NULL,
+ l_vpdSize,
+ DEVICE_SPD_ADDRESS(i_keyword));
+
+ if (l_errl)
+ {
+ TRACFCOMP(g_trac_ipmi,"isdimmIpmiFruInv::addVpdData - "
+ "Error while reading SPD keyword size for keyword 0x%x",
+ i_keyword);
+ break;
+ }
+
+ //Assert if vpd field is too large to fit in IPMI fru inventory format
+ assert(l_vpdSize < IPMIFRUINV::TYPELENGTH_BYTE_ASCII);
+
+ if (l_vpdSize > 0)
+ {
+ //Determine how big data is and expand it to handle the soon to
+ //be read VPD data
+ uint8_t l_offset = io_data.size();
+ io_data.resize(l_offset + 1 + l_vpdSize);
+
+ //Add on the data to the type/length byte indicating it is ascii
+ // otherwise leave it as binary
+ if (i_ascii)
+ {
+ io_data.at(l_offset) = l_vpdSize
+ + IPMIFRUINV::TYPELENGTH_BYTE_ASCII;
+ }
+ else
+ {
+ io_data.at(l_offset) = l_vpdSize;
+ }
+ l_offset += 1;
+
+ //Read the VPD data directly into fru inventory data buffer
+ l_errl = deviceRead(iv_target,&io_data[l_offset], l_vpdSize,
+ DEVICE_SPD_ADDRESS(i_keyword));
+ }
+ else
+ {
+ TRACFCOMP(g_trac_ipmi,"isdimmIpmiFruInv::addVpdData - "
+ " No size returned for SPD keyword");
+ }
+
+ } while(0);
+
+ if (l_errl)
+ {
+ TRACFCOMP(g_trac_ipmi, "addVpdData - Error acquiring data from Vpd.");
+ }
+
+ return l_errl;
+}
+
+errlHndl_t isdimmIpmiFruInv::addSpdManufacturer(std::vector<uint8_t> &io_data)
+{
+ const uint16_t i_keyword = SPD::MODULE_MANUFACTURER_ID;
+ uint16_t l_manufacturerId;
+ size_t l_fieldSize = sizeof(l_manufacturerId);
+
+ // Read manufacturer ID from SDP
+ const errlHndl_t l_errl = deviceRead(iv_target,
+ &l_manufacturerId,
+ l_fieldSize,
+ DEVICE_SPD_ADDRESS(i_keyword));
+ if (l_errl)
+ {
+ TRACFCOMP(g_trac_ipmi,
+ "isdimmIpmiFruInv::addSpdManufacturer - "
+ "Error while reading SPD keyword 0x%04x",
+ i_keyword);
+ }
+ else
+ {
+ // SPD values are stored in LE format
+ l_manufacturerId = le16toh(l_manufacturerId);
+
+ // Decode and put manufacturer name into the message buffer
+ const char* l_name = jedecDecode(i_keyword, l_manufacturerId);
+ if (l_name)
+ {
+ const size_t l_len = strlen(l_name);
+ io_data.push_back(l_len + IPMIFRUINV::TYPELENGTH_BYTE_ASCII);
+ io_data.insert(io_data.end(), l_name, l_name + l_len);
+ }
+ else {
+ char l_buf[IPMIFRUINV::MAX_ASCII_FIELD_SIZE];
+ const size_t l_len = snprintf(l_buf, sizeof(l_buf),
+ "Unknown (0x%04x)", l_manufacturerId);
+ io_data.push_back(l_len + IPMIFRUINV::TYPELENGTH_BYTE_ASCII);
+ io_data.insert(io_data.end(), l_buf, l_buf + l_len);
+ }
+ }
+
+ return l_errl;
+}
+
+errlHndl_t isdimmIpmiFruInv::addSpdDetails(std::vector<uint8_t> &io_data)
+{
+ errlHndl_t l_errl = NULL;
+
+ do {
+ // All fields that we need are 1 byte
+ size_t l_fieldSize = sizeof(uint8_t);
+ const uint8_t l_invalidValue = 0xff;
+
+ // Read detailed memory module info
+
+ uint8_t l_basicType = l_invalidValue;
+ l_errl = deviceRead(iv_target, &l_basicType, l_fieldSize,
+ DEVICE_SPD_ADDRESS(SPD::BASIC_MEMORY_TYPE));
+ if (l_errl) { break; }
+
+ uint8_t l_moduleType = l_invalidValue;
+ l_errl = deviceRead(iv_target, &l_moduleType, l_fieldSize,
+ DEVICE_SPD_ADDRESS(SPD::MODULE_TYPE));
+ if (l_errl) { break; }
+
+ uint8_t l_busWidthPri = l_invalidValue;
+ l_errl = deviceRead(iv_target, &l_busWidthPri, l_fieldSize,
+ DEVICE_SPD_ADDRESS(SPD::MODULE_MEMORY_BUS_WIDTH_PRI));
+ if (l_errl) { break; }
+
+ uint8_t l_busWidthExt = l_invalidValue;
+ l_errl = deviceRead(iv_target, &l_busWidthExt, l_fieldSize,
+ DEVICE_SPD_ADDRESS(SPD::MODULE_MEMORY_BUS_WIDTH_EXT));
+ if (l_errl) { break; }
+
+ uint8_t l_tckMin = l_invalidValue;
+ l_errl = deviceRead(iv_target, &l_tckMin, l_fieldSize,
+ DEVICE_SPD_ADDRESS(SPD::TCK_MIN));
+ if (l_errl) { break; }
+
+ uint8_t l_density = l_invalidValue;
+ l_errl = deviceRead(iv_target, &l_density, l_fieldSize,
+ DEVICE_SPD_ADDRESS(SPD::DENSITY));
+ if (l_errl) { break; }
+
+ uint8_t l_dramWidth = l_invalidValue;
+ l_errl = deviceRead(iv_target, &l_dramWidth, l_fieldSize,
+ DEVICE_SPD_ADDRESS(SPD::MODULE_DRAM_WIDTH));
+ if (l_errl) { break; }
+
+ uint8_t l_ranks = l_invalidValue;
+ l_errl = deviceRead(iv_target, &l_ranks, l_fieldSize,
+ DEVICE_SPD_ADDRESS(SPD::MODULE_RANKS));
+ if (l_errl) { break; }
+
+ // Calculate capacity of memory module
+ uint16_t l_capacityGiB = 0;
+ if (l_density <= JEDEC_DENSITY_MASK &&
+ l_busWidthPri <= JEDEC_MEMORY_BUS_WIDTH_PRI_MASK &&
+ l_dramWidth <= JEDEC_DRAM_WIDTH_MASK &&
+ l_ranks <= JEDEC_RANKS_MASK)
+ {
+ // Get density
+ // JEDEC Standard No. 21-C. Page 4.1.2.12 – 9
+ // density = 256 Mb* (2 ^ density) / 8 = 32 MB * (2 ^ density)
+ const uint32_t l_realDensity = (JEDEC_DENSITY_MIN_VALUE / 8) << l_density;
+
+ // Calculate the Primary Bus Width
+ // JEDEC Standard No. 21-C. Page 4.1.2.12 – 15
+ // b00 - 8 bits ... b11 - 64 bits. All others reserved.
+ const uint32_t l_realBusWidth = JEDEC_MEMORY_BUS_WIDTH_PRI_MIN_VALUE << l_busWidthPri;
+
+ // Calculate the SDRAM Device Width
+ // JEDEC Standard No. 21-C. Page 4.1.2.12 – 14
+ // b00 - 4 bits ... b11 - 32 bits. All others reserved.
+ const uint32_t l_realDevWidth = JEDEC_DRAM_WIDTH_MIN_VALUE << l_dramWidth;
+
+ // Calculate the Number of Package Ranks per DIMM
+ // JEDEC Standard No. 21-C. Page 4.1.2.12 – 14
+ // b00 - 1 package rank ... b11 - 4 package ranks. All others reserved.
+ const uint32_t l_realRanks = JEDEC_RANKS_MIN_VALUE + l_ranks;
+
+ // Calculate the Module Capacity (in GiB >> 10) according to the formula
+ // from the JEDEC Standard specification No. 21-C. Page 4.1.2.12 – 15
+ l_capacityGiB = (l_realDensity * (l_realBusWidth / l_realDevWidth) * l_realRanks) >> 10;
+ }
+
+ // Construct detailed string description
+ char l_desc[IPMIFRUINV::MAX_ASCII_FIELD_SIZE] = { 0 };
+ // Always store last null-termination symbol
+ const size_t l_descSz = sizeof(l_desc) - 1;
+
+ const char* l_decoded;
+
+ l_decoded = jedecDecode(SPD::BASIC_MEMORY_TYPE, l_basicType);
+ if (l_decoded)
+ {
+ strncpy(l_desc, l_decoded, l_descSz);
+ }
+ else
+ {
+ snprintf(l_desc, l_descSz, "N/A (%02x)", l_basicType);
+ }
+
+ l_decoded = jedecDecode(SPD::TCK_MIN, l_tckMin);
+ if (l_decoded)
+ {
+ strncat(l_desc, "-", l_descSz);
+ strncat(l_desc, l_decoded, l_descSz);
+ }
+
+ if (l_capacityGiB)
+ {
+ char l_buff[16] = { 0 };
+ snprintf(l_buff, sizeof(l_buff) - 1, " %iGiB", l_capacityGiB);
+ strncat(l_desc, l_buff, l_descSz);
+ }
+
+ l_decoded = jedecDecode(SPD::MODULE_MEMORY_BUS_WIDTH_PRI, l_busWidthPri);
+ if (l_decoded)
+ {
+ strncat(l_desc, " ", l_descSz);
+ strncat(l_desc, l_decoded, l_descSz);
+ }
+
+ l_decoded = jedecDecode(SPD::MODULE_MEMORY_BUS_WIDTH_EXT, l_busWidthExt);
+ if (l_decoded)
+ {
+ strncat(l_desc, " ", l_descSz);
+ strncat(l_desc, l_decoded, l_descSz);
+ }
+
+ l_decoded = jedecDecode(SPD::MODULE_TYPE, l_moduleType);
+ if (l_decoded)
+ {
+ strncat(l_desc, " ", l_descSz);
+ strncat(l_desc, l_decoded, l_descSz);
+ }
+
+ // Put detailed description into the message buffer
+ const size_t l_len = strlen(l_desc);
+ io_data.push_back(l_len + IPMIFRUINV::TYPELENGTH_BYTE_ASCII);
+ io_data.insert(io_data.end(), l_desc, l_desc + l_len);
+
+ } while(0);
+
+ if (l_errl)
+ {
+ TRACFCOMP(g_trac_ipmi,
+ "isdimmIpmiFruInv::addSpdDetails - "
+ "Error while reading SPD data");
+ }
+
+ return l_errl;
+}
+
+//##############################################################################
+procIpmiFruInv::procIpmiFruInv( TARGETING::TargetHandle_t i_target,
+ bool i_isUpdate )
+ :IpmiFruInv(i_target),
+ iv_isUpdate(i_isUpdate)
+{
+};
+
+errlHndl_t procIpmiFruInv::buildInternalUseArea(std::vector<uint8_t> &io_data)
+{
+ //This section not needed for proc type
+ return IpmiFruInv::buildEmptyArea(io_data);
+}
+
+errlHndl_t procIpmiFruInv::buildChassisInfoArea(std::vector<uint8_t> &io_data)
+{
+ //This section not needed for proc type
+ return IpmiFruInv::buildEmptyArea(io_data);
+}
+
+errlHndl_t procIpmiFruInv::buildBoardInfoArea(std::vector<uint8_t> &io_data)
+{
+ errlHndl_t l_errl = NULL;
+
+ do {
+ //Set formatting data that goes at the beginning of the record
+ preFormatProcessing(io_data, true);
+
+ //MFG Date/Time - Blank
+ io_data.push_back(0);
+ io_data.push_back(0);
+ io_data.push_back(0);
+
+ //Board Manufacturer - IBM
+ //Board MFG - Type/Length Byte
+ // - Indicate 8-bit Ascii + Latin 1 (0xC0)
+ // - and a size of 3 for "IBM" - 0x3
+ // - add together and the value for this byte is 0xC3
+ io_data.push_back(0xC3);
+ // - Now put in 'IBM'
+ io_data.push_back('I');
+ io_data.push_back('B');
+ io_data.push_back('M');
+
+ //Set Board Info description
+ l_errl = addVpdData(io_data, MVPD::VINI, MVPD::DR, true);
+ if (l_errl) { break; }
+ //Set Board Info serial number
+ l_errl = addVpdData(io_data, MVPD::VRML, MVPD::SN, true);
+ if (l_errl) { break; }
+ //Set Board part number
+ l_errl = addVpdData(io_data, MVPD::VRML, MVPD::PN, true);
+ if (l_errl) { break; }
+ //Set Board FRU File ID
+ l_errl = addVpdData(io_data, MVPD::VINI, MVPD::VZ);
+ if (l_errl) { break; }
+
+ //Push Fru File ID Byte - NULL
+ io_data.push_back(IPMIFRUINV::TYPELENGTH_BYTE_NULL);
+
+ //Get EC Data
+ TARGETING::ATTR_EC_type ecInfo;
+ bool getEC = iv_target->tryGetAttr<TARGETING::ATTR_EC>(ecInfo);
+
+ //Get ECID Data
+ TARGETING::ATTR_ECID_type ecidInfo;
+ bool getEcid = iv_target->tryGetAttr<TARGETING::ATTR_ECID>(ecidInfo);
+
+ //Only add ECID Data if in an update scenario
+ if (getEcid && iv_isUpdate == true)
+ {
+ addEcidData(iv_target, ecidInfo, io_data);
+ }
+ //Add in the EC Data whether we're in an update scenario or not.
+ //We have the EC info after discover_targets()
+ if (getEC)
+ {
+ addECData(iv_target, ecInfo, io_data);
+ }
+
+ if(!getEC && !(getEcid && iv_isUpdate))
+ {
+ //Indicate no custom fields if ecid and ec data not found
+ io_data.push_back(IPMIFRUINV::TYPELENGTH_BYTE_NULL);
+ }
+
+ if (iv_isUpdate == true)
+ {
+ std::vector<TARGETING::TargetHandle_t> l_procList;
+ l_procList.push_back(iv_target);
+ customData(l_procList, io_data);
+ }
+
+ //Indicate end of custom fields
+ io_data.push_back(IPMIFRUINV::END_OF_CUSTOM_FIELDS);
+
+ } while (0);
+
+ //Complete formatting for this data record
+ postFormatProcessing(io_data);
+
+ if (l_errl)
+ {
+ TRACFCOMP(g_trac_ipmi,"buildBoardInfoArea - Errors Collecting ISDimm "
+ "FRU Inventory Board Info Data");
+ }
+
+ return l_errl;
+}
+
+errlHndl_t procIpmiFruInv::buildProductInfoArea(std::vector<uint8_t> &io_data)
+{
+ //This section not needed for proc type
+ return IpmiFruInv::buildEmptyArea(io_data);
+}
+
+errlHndl_t procIpmiFruInv::buildMultiRecordInfoArea(
+ std::vector<uint8_t> &io_data)
+{
+ //This section not needed for proc type
+ return IpmiFruInv::buildEmptyArea(io_data);
+}
+
+errlHndl_t procIpmiFruInv::addVpdData(std::vector<uint8_t> &io_data,
+ uint8_t i_record,
+ uint8_t i_keyword,
+ bool i_ascii,
+ bool i_typeLengthByte)
+{
+ errlHndl_t l_errl = NULL;
+
+ l_errl = addCommonVpdData(iv_target,
+ io_data,
+ DeviceFW::MVPD,
+ i_record,
+ i_keyword,
+ i_ascii,
+ i_typeLengthByte);
+ return l_errl;
+}
+
+
+//##############################################################################
+backplaneIpmiFruInv::backplaneIpmiFruInv( TARGETING::TargetHandle_t i_target,
+ TARGETING::TargetHandleList i_extraTargets,
+ bool i_isUpdate)
+ :IpmiFruInv(i_target),
+ iv_isUpdate(i_isUpdate),
+ iv_extraTargets(i_extraTargets)
+{
+};
+
+errlHndl_t backplaneIpmiFruInv::buildInternalUseArea(
+ std::vector<uint8_t> &io_data)
+{
+ //This section not needed for the backplane type
+ return IpmiFruInv::buildEmptyArea(io_data);
+}
+
+errlHndl_t backplaneIpmiFruInv::buildChassisInfoArea(
+ std::vector<uint8_t> &io_data)
+{
+ errlHndl_t l_errl = NULL;
+
+ do {
+ //Set formatting data that goes at the beginning of the record
+ preFormatProcessing(io_data, false);
+ //Set Chassis Enclosure Type - Not Ascii
+ // Also, do not include type/length byte
+ l_errl = addVpdData(io_data, PVPD::OSYS, PVPD::ET, false, false);
+
+ //Support Legacy VPD without OSYS record
+ if (l_errl)
+ {
+ TRACFCOMP(g_trac_ipmi,"backplaneIpmiFruInv::buildChassisInfoArea - "
+ " Using Legacy Chassis VPD Data");
+ //Delete errorlog and use Default data and Legacy VPD Fields
+ delete l_errl;
+ l_errl = NULL;
+
+ //Set default chassis type
+ io_data.push_back(IPMIFRUINV::DEFAULT_CHASSIS_TYPE);
+ //Set chassis part number - ascii formatted field
+ l_errl = addVpdData(io_data, PVPD::OPFR, PVPD::VP, true);
+ if (l_errl) { break; }
+ //Set chassis serial number - ascii formatted field
+ l_errl = addVpdData(io_data, PVPD::OPFR, PVPD::VS, true);
+ if (l_errl) { break; }
+ }
+ else
+ {
+ TRACFCOMP(g_trac_ipmi,"backplaneIpmiFruInv::buildChassisInfoArea - "
+ " Using NEW OSYS RECORD FOR Chassis VPD Data");
+
+ //Set chassis part number - ascii formatted field
+ l_errl = addVpdData(io_data, PVPD::OSYS, PVPD::MM, true);
+ if (l_errl) { break; }
+
+ //Set chassis serial number - ascii formatted field
+ l_errl = addVpdData(io_data, PVPD::OSYS, PVPD::SS, true);
+ if (l_errl) { break; }
+
+ }
+
+ //Indicate no custom fields
+ io_data.push_back(IPMIFRUINV::TYPELENGTH_BYTE_NULL);
+ io_data.push_back(IPMIFRUINV::END_OF_CUSTOM_FIELDS);
+
+ } while (0);
+
+ //Complete record data formatting
+ postFormatProcessing(io_data);
+
+ if (l_errl)
+ {
+ TRACFCOMP(g_trac_ipmi,"backplaneIpmiFruInv::buildChassisInfoArea - "
+ "Errors collecting chassis info data");
+ }
+
+ return l_errl;
+}
+
+errlHndl_t backplaneIpmiFruInv::buildBoardInfoArea(
+ std::vector<uint8_t> &io_data)
+{
+ errlHndl_t l_errl = NULL;
+
+ do {
+ //Set formatting data that goes at the beginning of the record
+ preFormatProcessing(io_data, true);
+
+ // Set Mfg Build date
+ // Grab VPD data into seperate data vector
+ std::vector<uint8_t> mfgDateData;
+ l_errl = addVpdData(mfgDateData, PVPD::OPFR, PVPD::MB, false, false);
+ if (l_errl) { break; }
+
+ // Pass that to the function that sets the Build date
+ setMfgData(io_data, mfgDateData);
+
+ //Set Vendor Name - ascii formatted data
+ l_errl = addVpdData(io_data, PVPD::OPFR, PVPD::VN, true);
+ if (l_errl) { break; }
+
+ //Set Product Name - ascii formatted data
+ l_errl = addVpdData(io_data, PVPD::OPFR, PVPD::DR, true);
+ if (l_errl) { break; }
+
+ //Set Product Serial number - ascii formatted data
+ TARGETING::ATTR_SERIAL_NUMBER_type l_sn = {'0'};
+ if( !( iv_target->
+ tryGetAttr<TARGETING::ATTR_SERIAL_NUMBER>
+ ( l_sn) ) )
+ {
+ // Should not fail. Need to use tryGetAttr due to complex type.
+ // Use zeros if fails.
+ TRACFCOMP(g_trac_ipmi,"backplaneIpmiFruInv::buildBoardInfoArea - "
+ "Error getting serial number attribute");
+ }
+ // The attribute size is 18. The vpd is 16. Only use 16.
+ addCommonAttrData(io_data,
+ (uint8_t *)&l_sn,
+ VPD_SN_PN_VPD_SIZE);
+
+ //Set Product Part number - ascii formatted data
+ TARGETING::ATTR_PART_NUMBER_type l_pn = {'0'};
+ if( !( iv_target->
+ tryGetAttr<TARGETING::ATTR_PART_NUMBER>
+ ( l_pn) ) )
+ {
+ // Should not fail. Need to use tryGetAttr due to complex type.
+ // Use zeros if fails.
+ TRACFCOMP(g_trac_ipmi,"backplaneIpmiFruInv::buildBoardInfoArea - "
+ "Error getting part number attribute");
+ }
+
+ // The attribute size is 18. The vpd is 16. Only use 16.
+ addCommonAttrData(io_data,
+ (uint8_t *)&l_pn,
+ VPD_SN_PN_VPD_SIZE);
+
+ //Push Fru File ID Byte - NULL
+ io_data.push_back(IPMIFRUINV::TYPELENGTH_BYTE_NULL);
+ //Indicate End of Custom Fields
+ io_data.push_back(IPMIFRUINV::END_OF_CUSTOM_FIELDS);
+
+
+ } while (0);
+
+ //Complete record data formatting
+ postFormatProcessing(io_data);
+
+ if (l_errl)
+ {
+ TRACFCOMP(g_trac_ipmi,"backplaneIpmiFruInv::buildBoardInfoArea - "
+ "Errors collecting board info data");
+ }
+
+ return l_errl;
+}
+
+errlHndl_t backplaneIpmiFruInv::buildProductInfoArea(
+ std::vector<uint8_t> &io_data)
+{
+ //This section not needed for the backplane type
+ return IpmiFruInv::buildEmptyArea(io_data);
+}
+
+errlHndl_t backplaneIpmiFruInv::buildMultiRecordInfoArea(
+ std::vector<uint8_t> &io_data)
+{
+ //This section not needed for the backplane type
+ return IpmiFruInv::buildEmptyArea(io_data);
+}
+
+errlHndl_t backplaneIpmiFruInv::addVpdData(std::vector<uint8_t> &io_data,
+ uint8_t i_record,
+ uint8_t i_keyword,
+ bool i_ascii,
+ bool i_typeLengthByte)
+
+{
+ errlHndl_t l_errl = NULL;
+
+ l_errl = addCommonVpdData(iv_target,
+ io_data,
+ DeviceFW::PVPD,
+ i_record,
+ i_keyword,
+ i_ascii,
+ i_typeLengthByte);
+
+ return l_errl;
+}
+
+//##############################################################################
+systemFwIpmiFruInv::systemFwIpmiFruInv( TARGETING::TargetHandle_t i_target )
+ :IpmiFruInv(i_target)
+{
+
+};
+
+errlHndl_t systemFwIpmiFruInv::buildInternalUseArea(std::vector<uint8_t>
+ &io_data)
+{
+ //This section not needed for system firmware type
+ return IpmiFruInv::buildEmptyArea(io_data);
+}
+
+errlHndl_t systemFwIpmiFruInv::buildChassisInfoArea(std::vector<uint8_t>
+ &io_data)
+{
+ //This section not needed for system firmware type
+ return IpmiFruInv::buildEmptyArea(io_data);
+}
+
+errlHndl_t systemFwIpmiFruInv::buildBoardInfoArea(std::vector<uint8_t> &io_data)
+{
+ //This section not needed for system firmware type
+ return IpmiFruInv::buildEmptyArea(io_data);
+}
+
+errlHndl_t systemFwIpmiFruInv::buildProductInfoArea(std::vector<uint8_t>
+ &io_data)
+{
+ errlHndl_t l_errl = NULL;
+
+ do {
+ //Set formatting data that goes at the beginning of the record
+ preFormatProcessing(io_data, true);
+
+ uint8_t l_data[] = {IPMIFRUINV::TYPELENGTH_BYTE_NULL,
+ IPMIFRUINV::TYPELENGTH_BYTE_ASCII + 18, 'O','p','e',
+ 'n','P','O','W','E','R',' ','F','i','r','m','w','a',
+ 'r','e', IPMIFRUINV::TYPELENGTH_BYTE_NULL};
+
+ io_data.insert( io_data.end(),
+ &l_data[0],
+ &l_data[0] + (uint8_t(sizeof(l_data) / sizeof(uint8_t))));
+
+ //Get PNOR Version Here
+ PNOR::SectionInfo_t l_pnorInfo;
+ l_errl = getSectionInfo( PNOR::VERSION , l_pnorInfo);
+ if (l_errl) { break; }
+
+ uint8_t* l_versionData = reinterpret_cast<uint8_t*>( l_pnorInfo.vaddr );
+ //Total Bytes in PNOR Version String
+ uint8_t l_numBytes = 0;
+ uint8_t l_curOffset = 0;
+
+ //Total Number of fields needed to print PNOR Version String
+ uint8_t l_numFields = 0;
+ bool l_clearStandardFields = true;
+
+ //First determine number of bytes in PNOR Version string
+ // with the caveat there is a max record size allowed, so
+ // the string will be cut off if too long
+ //Also, remove newline chars
+ while ((l_numBytes < IPMIFRUINV::MAX_RECORD_SIZE -
+ (uint8_t(sizeof(l_data) / sizeof(uint8_t))) -
+ IPMIFRUINV::COMMON_HEADER_FORMAT_SIZE - 8)
+ && (((char)(l_versionData[l_numBytes])) != '\0'))
+ {
+
+ if (((char)(l_versionData[l_numBytes])) == '\n')
+ {
+
+ if (l_numBytes > l_curOffset)
+ {
+ //Add on size of this field to the data buffer
+ io_data.push_back(
+ IPMIFRUINV::TYPELENGTH_BYTE_ASCII
+ + (l_numBytes-l_curOffset));
+
+ io_data.insert(io_data.end(),
+ &l_versionData[0]+(l_curOffset),
+ &l_versionData[0]+(l_numBytes));
+ }
+
+ //Null data for standard fields needs to be indicated once after
+ // the first segment of data is displayed to match the
+ // ipmi fru spec
+ if (l_clearStandardFields)
+ {
+ //Add Empty Asset Tag
+ io_data.push_back(uint8_t(0));
+ //FRU File ID - Empty
+ io_data.push_back(IPMIFRUINV::TYPELENGTH_BYTE_NULL);
+ io_data.push_back(uint8_t(0)); // Empty FRU File ID bytes
+ l_clearStandardFields = false;
+ }
+
+ //Increment past the newline char
+ l_curOffset = l_numBytes + 1;
+ }
+ l_numBytes++;
+ }
+
+ if (l_curOffset == 0)
+ {
+ //Calculate the number of fields required to display this data
+ // given only MAX_ASCII_FIELD_SIZE bytes can be in any one given
+ // IPMI fru inventory field
+ l_numFields = l_numBytes / IPMIFRUINV::MAX_ASCII_FIELD_SIZE;
+ if (l_numBytes % IPMIFRUINV::MAX_ASCII_FIELD_SIZE)
+ {
+ l_numFields += 1;
+ }
+
+ //Count by number of fields, adding the data to the buffer as
+ // we go.
+ for (uint8_t i=0; i < l_numFields; i++)
+ {
+ //Determine the data size for this particular field
+ uint8_t l_dataSize=IPMIFRUINV::MAX_ASCII_FIELD_SIZE;
+ if (i == l_numFields - 1)
+ {
+ l_dataSize = l_numBytes -
+ (i * IPMIFRUINV::MAX_ASCII_FIELD_SIZE);
+ }
+
+ //Add on size of this field to the data buffer
+ io_data.push_back(IPMIFRUINV::TYPELENGTH_BYTE_ASCII
+ + l_dataSize);
+
+ //Insert this segment of version string data
+ io_data.insert(io_data.end(),
+ &l_versionData[0]+(i * IPMIFRUINV::MAX_ASCII_FIELD_SIZE),
+ &l_versionData[0]+(i * IPMIFRUINV::MAX_ASCII_FIELD_SIZE)
+ +l_dataSize);
+
+ //Null data for standard fields needs to be indicated once after
+ // the first segment of data is displayed to match the
+ // ipmi fru spec
+ if (l_clearStandardFields)
+ {
+ //Add Empty Asset Tag
+ io_data.push_back(uint8_t(0));
+ //FRU File ID - Empty
+ io_data.push_back(IPMIFRUINV::TYPELENGTH_BYTE_NULL);
+ l_clearStandardFields = false;
+ }
+
+ }
+ }
+ else
+ {
+ if (l_numBytes > l_curOffset)
+ {
+ io_data.push_back( IPMIFRUINV::TYPELENGTH_BYTE_ASCII
+ + (l_numBytes-l_curOffset));
+
+ io_data.insert(io_data.end(),
+ &l_versionData[0]+(l_curOffset),
+ &l_versionData[0]+(l_numBytes));
+ }
+
+ }
+
+ if (l_clearStandardFields)
+ {
+ //Add Asset Tag
+ io_data.push_back(uint8_t(0)); //No Asset Tag needed - O bytes
+ //FRU File ID - Empty
+ io_data.push_back(IPMIFRUINV::TYPELENGTH_BYTE_NULL);
+ }
+
+ io_data.push_back(IPMIFRUINV::END_OF_CUSTOM_FIELDS);
+
+ } while(0);
+
+ //Finalize section formatting
+ postFormatProcessing(io_data);
+
+ return l_errl;
+}
+
+errlHndl_t systemFwIpmiFruInv::buildMultiRecordInfoArea(std::vector<uint8_t>
+ &io_data)
+{
+ //This section not needed for system firmware type
+ return IpmiFruInv::buildEmptyArea(io_data);
+}
+
+//##############################################################################
+membufIpmiFruInv::membufIpmiFruInv( TARGETING::TargetHandle_t i_target,
+ TARGETING::TargetHandleList i_extraTargets,
+ bool i_isUpdate)
+ :IpmiFruInv(i_target),
+ iv_isUpdate(i_isUpdate),
+ iv_extraTargets(i_extraTargets)
+{
+};
+
+errlHndl_t membufIpmiFruInv::buildInternalUseArea(
+ std::vector<uint8_t> &io_data)
+{
+ //This section not needed for the mem buf type
+ return IpmiFruInv::buildEmptyArea(io_data);
+}
+
+errlHndl_t membufIpmiFruInv::buildChassisInfoArea(
+ std::vector<uint8_t> &io_data)
+{
+ //This section not needed for the mem buf type
+ return IpmiFruInv::buildEmptyArea(io_data);
+}
+
+errlHndl_t membufIpmiFruInv::buildBoardInfoArea(
+ std::vector<uint8_t> &io_data)
+{
+ errlHndl_t l_errl = NULL;
+
+ do {
+ //Set formatting data that goes at the beginning of the record
+ preFormatProcessing(io_data, true);
+
+ // Set Mfg Build date
+ // Grab VPD data into seperate data vector
+ std::vector<uint8_t> mfgDateData;
+ l_errl = addVpdData(mfgDateData, PVPD::OPFR, PVPD::MB, false, false);
+ if (l_errl) { break; }
+
+ // Pass that to the function that sets the Build date
+ setMfgData(io_data, mfgDateData);
+
+ uint8_t l_fru_id = 0xFF;
+ // if the centaur_ecid_fru_id is not valid then the centaur is on a
+ // riser card, grab its vpd and populate the record
+ l_fru_id = iv_target->getAttr<TARGETING::ATTR_CENTAUR_ECID_FRU_ID>();
+
+ if( l_fru_id == 0xFF )
+ {
+ //Set Vendor Name - ascii formatted data
+ l_errl = addVpdData(io_data, CVPD::OPFR, CVPD::VN, true);
+ if (l_errl) { break; }
+
+ //Set Product Name - ascii formatted data
+ l_errl = addVpdData(io_data, CVPD::OPFR, CVPD::DR, true);
+ if (l_errl) { break; }
+
+ //Set Product Serial number - ascii formatted data
+ TARGETING::ATTR_SERIAL_NUMBER_type l_sn = {'0'};
+ if( !( iv_target->
+ tryGetAttr<TARGETING::ATTR_SERIAL_NUMBER>
+ ( l_sn) ) )
+ {
+ // Should not fail. Need to use tryGetAttr due to complex type.
+ // Use zeros if fails.
+ TRACFCOMP(g_trac_ipmi,"membufIpmiFruInv::buildBoardInfoArea - "
+ "Error getting serial number attribute");
+ }
+ // The attribute size is 18. The vpd is 16. Only use 16.
+ addCommonAttrData(io_data,
+ (uint8_t *)&l_sn,
+ VPD_SN_PN_VPD_SIZE);
+
+ //Set Product Part number - ascii formatted data
+ TARGETING::ATTR_PART_NUMBER_type l_pn = {'0'};
+ if( !( iv_target->
+ tryGetAttr<TARGETING::ATTR_PART_NUMBER>
+ ( l_pn) ) )
+ {
+ // Should not fail. Need to use tryGetAttr due to complex type.
+ // Use zeros if fails.
+ TRACFCOMP(g_trac_ipmi,"membufIpmiFruInv::buildBoardInfoArea - "
+ "Error getting part number attribute");
+ }
+ // The attribute size is 18. The vpd is 16. Only use 16.
+ addCommonAttrData(io_data,
+ (uint8_t *)&l_pn,
+ VPD_SN_PN_VPD_SIZE);
+
+ //Push Fru File ID Byte - NULL
+ io_data.push_back(IPMIFRUINV::TYPELENGTH_BYTE_NULL);
+
+ }
+ else
+ {
+ //Set Vendor Name - NULL
+ io_data.push_back(IPMIFRUINV::TYPELENGTH_BYTE_NULL);
+
+ //Set Product Name - NULL
+ io_data.push_back(IPMIFRUINV::TYPELENGTH_BYTE_NULL);
+
+ //Set Product Serial number - NULL
+ io_data.push_back(IPMIFRUINV::TYPELENGTH_BYTE_NULL);
+
+ //Set Product Part number - NULL
+ io_data.push_back(IPMIFRUINV::TYPELENGTH_BYTE_NULL);
+
+ //Push Fru File ID Byte - NULL
+ io_data.push_back(IPMIFRUINV::TYPELENGTH_BYTE_NULL);
+
+ }
+
+ //Only set the ECID Data during an update scenario
+ if (iv_isUpdate == true)
+ {
+ customData (iv_extraTargets, io_data);
+ }
+
+ //Indicate End of Custom Fields
+ io_data.push_back(IPMIFRUINV::END_OF_CUSTOM_FIELDS);
+
+ } while (0);
+
+ //Complete record data formatting
+ postFormatProcessing(io_data);
+
+ if (l_errl)
+ {
+ TRACFCOMP(g_trac_ipmi,"membufIpmiFruInv::buildBoardInfoArea - "
+ "Errors collecting board info data");
+ }
+
+ return l_errl;
+}
+
+errlHndl_t membufIpmiFruInv::buildProductInfoArea(
+ std::vector<uint8_t> &io_data)
+{
+ //This section not needed for the mem buf type
+ return IpmiFruInv::buildEmptyArea(io_data);
+}
+
+errlHndl_t membufIpmiFruInv::buildMultiRecordInfoArea(
+ std::vector<uint8_t> &io_data)
+{
+ //This section not needed for the mem buf type
+ return IpmiFruInv::buildEmptyArea(io_data);
+}
+
+errlHndl_t membufIpmiFruInv::addVpdData(std::vector<uint8_t> &io_data,
+ uint8_t i_record,
+ uint8_t i_keyword,
+ bool i_ascii,
+ bool i_typeLengthByte)
+{
+ errlHndl_t l_errl = NULL;
+
+ l_errl = addCommonVpdData(iv_target,
+ io_data,
+ DeviceFW::CVPD,
+ i_record,
+ i_keyword,
+ i_ascii,
+ i_typeLengthByte);
+ return l_errl;
+}
+//##############################################################################
+void IpmiFruInv::customData(TARGETING::TargetHandleList i_extraTargets,
+ std::vector<uint8_t> &io_data)
+{
+
+ bool l_setCustomData = false;
+ // Check if we should add ECID
+ for (TARGETING::TargetHandleList::const_iterator extraTargets_it =
+ i_extraTargets.begin();
+ extraTargets_it != i_extraTargets.end();
+ ++extraTargets_it
+ )
+ {
+ TARGETING::TargetHandle_t l_extraTarget = *extraTargets_it;
+
+ //If we're in an update and the target is a membuf, we update the ecid.
+ if ( l_extraTarget->getAttr<TARGETING::ATTR_TYPE>() ==
+ TARGETING::TYPE_MEMBUF)
+ {
+ TARGETING::ATTR_ECID_type ecidInfo;
+ bool getEcid =
+ l_extraTarget->tryGetAttr<TARGETING::ATTR_ECID>(ecidInfo);
+ if (getEcid)
+ {
+ l_setCustomData = true;
+ addEcidData(l_extraTarget, ecidInfo, io_data);
+ }
+ else
+ {
+ TRACFCOMP(g_trac_ipmi, "No ECID info for this huid 0x%x",
+ TARGETING::get_huid(l_extraTarget));
+ }
+ }
+ }
+
+ //If no Custom data was sent, an Empty Byte is needed
+ if (!l_setCustomData)
+ {
+ io_data.push_back(IPMIFRUINV::TYPELENGTH_BYTE_NULL);
+ }
+}
+
+void IpmiFruInv::addEcidData(const TARGETING::TargetHandle_t& i_target,
+ const TARGETING::ATTR_ECID_type& i_ecidInfo,
+ std::vector<uint8_t> &io_data)
+{
+ // Create Custom ECID Field
+ // - First put in 'ECID:' to make it obvious what this is
+ uint8_t l_data[] = {IPMIFRUINV::TYPELENGTH_BYTE_ASCII + 37,'E','C','I','D',
+ ':'};
+
+ // @todo-RTC:124687 - Refactor multiple reallocations
+ io_data.insert( io_data.end(),
+ &l_data[0],
+ &l_data[0] + (uint8_t(sizeof(l_data) / sizeof(uint8_t))));
+
+ CPPASSERT(sizeof(ATTR_ECID_type) == 16);
+ CPPASSERT((sizeof(i_ecidInfo) / sizeof(ATTR_ECID_type)) == 2);
+
+ char l_ecidAscii[33];
+ sprintf(l_ecidAscii, "%.16llX%.16llX", i_ecidInfo[0], i_ecidInfo[1]);
+
+ uint8_t* l_vDataPtr = (uint8_t*) &l_ecidAscii[0];
+ io_data.insert(io_data.end(), &l_vDataPtr[0], &l_vDataPtr[0]+32);
+
+ return;
+}
+
+void IpmiFruInv::addECData(const TARGETING::TargetHandle_t& i_target,
+ const TARGETING::ATTR_EC_type& i_ecInfo,
+ std::vector<uint8_t> &io_data)
+{
+ // Create Custom EC Field
+ // - First put in 'EC:' to make it obvious what this is
+ uint8_t l_data[] = {IPMIFRUINV::TYPELENGTH_BYTE_ASCII + 5,'E','C',':'};
+
+ // @todo-RTC:124687 - Refactor multiple reallocations
+ io_data.insert( io_data.end(),
+ &l_data[0],
+ &l_data[0] + (uint8_t(sizeof(l_data) / sizeof(uint8_t))));
+
+ CPPASSERT(sizeof(ATTR_EC_type) == 1);
+ CPPASSERT((sizeof(i_ecInfo) / sizeof(ATTR_EC_type)) == 1);
+
+ char l_ecAscii[3];
+ sprintf(l_ecAscii,"%X",i_ecInfo);
+
+ io_data.insert(io_data.end(), &l_ecAscii[0],&l_ecAscii[2]);
+
+ return;
+}
+
+errlHndl_t IpmiFruInv::addCommonVpdData(
+ const TARGETING::TargetHandle_t& i_target,
+ std::vector<uint8_t> &io_data,
+ DeviceFW::AccessType i_accessType,
+ uint8_t i_record,
+ uint8_t i_keyword,
+ bool i_ascii,
+ bool i_typeLengthByte)
+{
+ size_t l_vpdSize = 0;
+ errlHndl_t l_errl = NULL;
+
+ do {
+ // First get size with NULL call:
+ // Bypass DEVICE_?VPD_ADDRESS inorder to maximize common code
+ l_errl = deviceRead(i_target,
+ NULL,
+ l_vpdSize,
+ i_accessType,
+ i_record,
+ i_keyword,
+ VPD::AUTOSELECT);
+
+ if (l_errl)
+ {
+ TRACFCOMP(g_trac_ipmi,"addCommonVpdData - Error "
+ "while reading keyword size");
+ break;
+ }
+
+ //Assert if vpd field is too large to fit in IPMI fru inventory format
+ assert(l_vpdSize < IPMIFRUINV::TYPELENGTH_BYTE_ASCII);
+
+ if (l_vpdSize > 0)
+ {
+ uint8_t l_offset = 0;
+ //Add on the typelength byte if requested
+ if (i_typeLengthByte)
+ {
+ //Determine how big data is and expand it to handle the soon to
+ //be read VPD data
+ l_offset = io_data.size();
+ io_data.resize(l_offset + 1 + l_vpdSize);
+ //Add on the data to the type/length byte indicating it is ascii
+ // otherwise leave it as binary
+ if (i_ascii)
+ {
+ io_data.at(l_offset) = l_vpdSize
+ + IPMIFRUINV::TYPELENGTH_BYTE_ASCII;
+ }
+ else
+ {
+ io_data.at(l_offset) = l_vpdSize;
+ }
+ l_offset += 1;
+ }
+ else
+ {
+ //Determine how big data is and expand it to handle the soon to
+ //be read VPD data
+ l_offset = io_data.size();
+ io_data.resize(l_offset + l_vpdSize);
+ }
+ //Read the VPD data directly into fru inventory data buffer
+ l_errl = deviceRead(i_target,
+ &io_data[l_offset],
+ l_vpdSize,
+ i_accessType,
+ i_record,
+ i_keyword,
+ VPD::AUTOSELECT);
+ }
+ else
+ {
+ TRACFCOMP(g_trac_ipmi,"addCommonVpdData - "
+ " No size returned for keyword");
+ }
+ } while(0);
+
+ if (l_errl)
+ {
+ TRACFCOMP(g_trac_ipmi, "addCommonVpdData - Error "
+ "acquiring data from Vpd.");
+ }
+
+ return l_errl;
+}
+
+void IpmiFruInv::addCommonAttrData( std::vector<uint8_t> &io_data,
+ uint8_t * i_pAttrData,
+ size_t i_length)
+{
+ uint8_t l_offset = io_data.size();
+
+ //Determine how big data is and expand it to handle the attr data
+ //and the typelength byte
+ io_data.resize(l_offset + 1 + i_length);
+
+ //Add the type/length byte indicating ascii data.
+ io_data.at(l_offset) = i_length + IPMIFRUINV::TYPELENGTH_BYTE_ASCII;
+
+ //copy attr data
+ memcpy (&io_data[l_offset+1],i_pAttrData,i_length);
+}
+
+
+void IPMIFRUINV::clearData(uint8_t i_fruId)
+{
+ uint8_t l_clearData[] =
+ {IPMIFRUINV::RECORD_NOT_PRESENT, IPMIFRUINV::RECORD_NOT_PRESENT,
+ IPMIFRUINV::RECORD_NOT_PRESENT, IPMIFRUINV::RECORD_NOT_PRESENT,
+ IPMIFRUINV::RECORD_NOT_PRESENT, IPMIFRUINV::RECORD_NOT_PRESENT,
+ IPMIFRUINV::RECORD_NOT_PRESENT, IPMIFRUINV::RECORD_NOT_PRESENT};
+
+ //Use IMPIFRU::writeData to send data to service processor
+ IPMIFRU::writeData(i_fruId, l_clearData,
+ IPMIFRUINV::COMMON_HEADER_FORMAT_SIZE,
+ IPMIFRUINV::DEFAULT_FRU_OFFSET);
+}
+
+void IPMIFRUINV::setData(bool i_updateData)
+{
+ errlHndl_t l_errl = NULL;
+
+ do
+ {
+ // find CLASS_SYS (the top level target)
+ TARGETING::Target* pSys;
+ TARGETING::targetService().getTopLevelTarget(pSys);
+
+ if (!(pSys))
+ {
+ TRACFCOMP(g_trac_ipmi,"IPMIFRUINV::setData - No CLASS_SYS TopLevelTarget found:"
+ " not setting IPMI Fru Inventory");
+ break;
+ }
+
+ //Container with list of frus and a boolean indicating whether the data
+ //needs to be cleared or not
+ // @todo-RTC:124687 - Refactor map use
+ std::map<uint8_t,bool> frusToClear;
+ //List of all potential Frus that could need IPMI Fru Inv. Data Sent
+ std::vector< std::pair<TARGETING::TargetHandle_t, uint8_t> >
+ l_potentialFrus;
+
+ if (i_updateData == false)
+ {
+ IPMIFRUINV::gatherClearData(pSys, frusToClear);
+ }
+
+ //Get System FW FRU_ID if available
+ uint32_t l_systemFwFruId;
+ bool hasSystemFwFruId =
+ pSys->tryGetAttr<TARGETING::ATTR_BMC_FRU_ID>(l_systemFwFruId);
+ if (hasSystemFwFruId)
+ {
+ l_potentialFrus.push_back(std::make_pair(pSys, l_systemFwFruId));
+ }
+
+ // Find list of all target types that may need a fru inv. record set
+ IPMIFRUINV::gatherSetData(pSys, frusToClear,
+ l_potentialFrus, i_updateData);
+
+ //Now Loop through all TargetHandle_t, uint8_t pairs to see if there are
+ //multiple targets with the same fruId. These will be formed into a list
+ //as data from all Targets will be combined into one IPMI Fru Inventory
+ //Record under the same fruId.
+ std::vector<std::pair<TARGETING::TargetHandle_t,uint8_t> >::iterator
+ l_iter;
+
+ for (l_iter = l_potentialFrus.begin(); l_iter != l_potentialFrus.end();
+ ++l_iter)
+ {
+ //iterators to walk list and group frus together
+ std::vector<std::pair<TARGETING::TargetHandle_t,uint8_t> >::iterator
+ l_curPair = l_iter;
+ std::vector<std::pair<TARGETING::TargetHandle_t,uint8_t> >::iterator
+ l_nextPair = l_iter;
+
+ //The 'base' TargetHandleList will have one FRU
+ TARGETING::TargetHandleList l_curFru;
+ l_curFru.push_back(l_curPair->first);
+ //This will be the fruId to compare with what comes after this
+ //Target in the vector
+ uint8_t l_fruId = l_curPair->second;
+
+ TRACFCOMP(g_trac_ipmi, "IPMIFRUINV::setData - Collecting all IPMI FRU Inventory Targets with fruId: [%08x]",
+ l_fruId);
+
+ ++l_nextPair;
+ for( ; l_nextPair != l_potentialFrus.end()
+ && l_nextPair->second == l_fruId; ++l_nextPair)
+ {
+ l_curFru.push_back(l_nextPair->first);
+ l_iter = l_nextPair;
+ }
+ IpmiFruInv *l_fru = IpmiFruInv::Factory(l_curFru, i_updateData);
+
+ if (l_fru != NULL)
+ {
+ //Target recognized, build & send IPMI FRU Invenotry record
+ l_errl = l_fru->buildFruInvRecord();
+ if (l_errl)
+ {
+ TRACFCOMP(g_trac_ipmi, "IPMIFRUINV::setData - Errors encountered, will skip setting the rest of the data");
+ break;
+ }
+ TRACFCOMP(g_trac_ipmi, "IPMIFRUINV::setData - Sending IPMI FRU Inventory Data for target with fruId: [%08x] and size [%08x]",
+ l_fruId, l_curFru.size());
+ l_fru->sendFruData(l_fruId);
+ delete l_fru;
+ l_fru = nullptr;
+ }
+ }
+
+ //Do not clear data during a data update
+ if (i_updateData == false)
+ {
+ //Now clear any FRU Data for fruIds that didn't have data set. This
+ //will handle the case where something was removed from the system
+ for (std::map<uint8_t,bool>::iterator it=frusToClear.begin();
+ it!=frusToClear.end();
+ ++it)
+ {
+ //If the bool is true its data needs to be cleared
+ if (it->second == true)
+ {
+ IPMIFRUINV::clearData(it->first);
+ }
+ }
+
+ // Only send GPU sensor PRESENT status one time (no update),
+ // then allow HTMGT to update
+
+ // Go through processors and send GPU sensor status
+ // Get all Proc targets
+ TARGETING::TargetHandleList l_procTargetList;
+ getAllChips(l_procTargetList, TARGETING::TYPE_PROC);
+
+ uint32_t gpu_sensors[SENSOR::MAX_GPU_SENSORS_PER_PROCESSOR];
+ uint8_t num_valid_sensors = 0;
+ for (const auto & l_procChip: l_procTargetList)
+ {
+ // report present GPU sensors
+ l_errl = SENSOR::getGpuSensors( l_procChip,
+ HWAS::GPU_FUNC_SENSOR,
+ num_valid_sensors,
+ gpu_sensors );
+ if (!l_errl)
+ {
+ // build up present GPUs based on sensor data returned
+ SENSOR::StatusSensor::statusEnum
+ gpu_status[SENSOR::MAX_PROCESSOR_GPUS];
+
+ // initialize to NOT PRESENT
+ for (uint8_t j = 0; j < SENSOR::MAX_PROCESSOR_GPUS; j++)
+ {
+ gpu_status[j] =
+ SENSOR::StatusSensor::statusEnum::NOT_PRESENT;
+ }
+
+ // now change the PRESENT ones
+ for (uint8_t i = 0;
+ i < SENSOR::MAX_GPU_SENSORS_PER_PROCESSOR; i++)
+ {
+ if (i < SENSOR::MAX_PROCESSOR_GPUS)
+ {
+ if (gpu_sensors[i] !=
+ TARGETING::UTIL::INVALID_IPMI_SENSOR)
+ {
+ gpu_status[i] =
+ SENSOR::StatusSensor::statusEnum::PRESENT;
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ // Send the present/non-present GPU sensors
+ SENSOR::updateGpuSensorStatus( l_procChip, gpu_status);
+ }
+ }
+ }
+
+ } while(0);
+
+ if (l_errl)
+ {
+ //Commit errorlog encountered indicating there were issues
+ //setting the FRU Inventory Data
+ TRACFCOMP(g_trac_ipmi, "Errors encountered setting Fru Inventory Data");
+ l_errl->collectTrace(IPMI_COMP_NAME);
+ errlCommit(l_errl, IPMI_COMP_ID);
+ }
+ return;
+}
+
+void IPMIFRUINV::readFruData(uint8_t i_deviceId, uint8_t *o_data)
+{
+ //Use IMPIFRU::readData to send data to service processor
+ // it will do any error handling and memory management
+ IPMIFRU::readData(i_deviceId, o_data);
+}
+
+char * IPMIFRUINV::getProductSN(uint8_t i_deviceId)
+{
+ //If we cannot read the product serial number, default is 0's (set below)
+ char * l_prodSN = NULL;
+
+ //First read the entire record that contains the SN
+ uint8_t *l_record = new uint8_t[IPMIFRU::MAX_RECORD_SIZE];
+ memset(l_record, 0, IPMIFRU::MAX_RECORD_SIZE);
+ readFruData(i_deviceId, l_record);
+
+ do {
+
+ //Code only supports version 1 of the FRU spec if for whatever reason
+ // we didn't get data, this would be 0
+ if (l_record[IPMIFRUINV::HEADER_FORMART_VERSION]
+ != IPMIFRUINV::SPEC_VERSION)
+ {
+ TRACFCOMP(g_trac_ipmi, "FW does not support IPMI FRU Version: %d",
+ l_record[1]);
+ break;
+ }
+
+ //Get the offset in the record pointed to the product info area
+ // (where the SN is located)
+ uint8_t l_prodInfoOffset = l_record[IPMIFRUINV::PRODUCT_INFO_AREA];
+
+ if (l_prodInfoOffset == IPMIFRUINV::RECORD_NOT_PRESENT)
+ {
+ TRACFCOMP(g_trac_ipmi, "Product Info Area Not present "
+ "- returning empty SN");
+ break;
+ }
+
+ //Start at the beginning of the Product Info Record and traverse to the
+ // serial number entry
+ uint8_t l_prodIndex = l_prodInfoOffset * RECORD_UNIT_OF_MEASUREMENT;
+ l_prodIndex += 3; //Version, Length Byte, Language Code
+
+ //MFG NAME Length + TYPELENGTH Byte
+ l_prodIndex += ((TYPELENGTH_SIZE_MASK&l_record[l_prodIndex]) + 1);
+
+ //Prod NAME Length + TYPELENGTH Byte
+ l_prodIndex += ((TYPELENGTH_SIZE_MASK&l_record[l_prodIndex]) + 1);
+
+ //Prod Part/Model Number Length + TYPELENGTH Byte
+ l_prodIndex += ((TYPELENGTH_SIZE_MASK&l_record[l_prodIndex]) + 1);
+
+ //Prod Version Length + TYPELEGNTH Byte
+ l_prodIndex += ((TYPELENGTH_SIZE_MASK&l_record[l_prodIndex]) + 1);
+
+ //Serial number located after Prod Version
+ uint8_t l_prodSNOffset = l_prodIndex;
+ uint8_t l_prodSNsize = TYPELENGTH_SIZE_MASK&l_record[l_prodSNOffset];
+
+ //Grab Serial Number as char array
+ l_prodSN = new char[l_prodSNsize+1];
+ memcpy(l_prodSN, &l_record[l_prodSNOffset+1], l_prodSNsize);
+ l_prodSN[l_prodSNsize] = '\0';
+
+ //Can skip the rest.
+
+ } while (0);
+
+
+ delete[] l_record;
+
+ return l_prodSN;
+}
+
+void IPMIFRUINV::gatherClearData(const TARGETING::Target* i_pSys,
+ std::map<uint8_t,bool>& io_frusToClear)
+{
+ TARGETING::PredicateCTM predChip(TARGETING::CLASS_CHIP);
+ TARGETING::PredicateCTM predNode(TARGETING::CLASS_ENC,
+ TARGETING::TYPE_NODE);
+ TARGETING::PredicateCTM predDimm(TARGETING::CLASS_LOGICAL_CARD,
+ TARGETING::TYPE_DIMM);
+ TARGETING::PredicatePostfixExpr checkAllExpr;
+ checkAllExpr.push(&predChip).push(&predNode).Or().push(&predDimm).Or();
+ TARGETING::TargetHandleList l_allPossibleFrus;
+ TARGETING::targetService().getAssociated( l_allPossibleFrus, i_pSys,
+ TARGETING::TargetService::CHILD, TARGETING::TargetService::ALL,
+ &checkAllExpr );
+
+ for (TARGETING::TargetHandleList::const_iterator pTarget_it =
+ l_allPossibleFrus.begin();
+ pTarget_it != l_allPossibleFrus.end();
+ ++pTarget_it)
+ {
+ TARGETING::TargetHandle_t pTarget = *pTarget_it;
+ uint32_t l_fruId = pTarget->getAttr<TARGETING::ATTR_FRU_ID>();
+
+ if (l_fruId)
+ {
+ //Assume we clear all possible targets to start
+ // @todo-RTC:124506 - New logic may be needed to clear all targets
+ // after a code update
+ io_frusToClear[l_fruId] = true;
+ }
+ }
+
+ return;
+}
+
+void IPMIFRUINV::gatherSetData(const TARGETING::Target* i_pSys,
+ std::map<uint8_t,bool>& io_frusToClear,
+ std::vector< std::pair<TARGETING::TargetHandle_t, uint8_t> >&
+ io_potentialFrus,
+ bool i_updateData)
+{
+ TARGETING::PredicateCTM predChip(TARGETING::CLASS_CHIP);
+ TARGETING::PredicateCTM predDimm(TARGETING::CLASS_LOGICAL_CARD,
+ TARGETING::TYPE_DIMM);
+ TARGETING::PredicatePostfixExpr checkExpr;
+ TARGETING::PredicateHwas l_present;
+ // @todo-RTC:124553 - Additional logic for deconfigured Frus
+ // may be needed
+ l_present.present(true);
+
+ checkExpr.push(&predChip);
+ TARGETING::PredicateCTM predNode(TARGETING::CLASS_ENC,
+ TARGETING::TYPE_NODE);
+ checkExpr.push(&predNode).Or();
+
+ //When updating data on a later pass ignore dimms
+ if (i_updateData)
+ {
+ checkExpr.push(&l_present).And();
+ }
+ else
+ {
+ checkExpr.push(&predDimm).Or().push(&l_present).And();
+ }
+
+ TARGETING::TargetHandleList pCheckPres;
+ TARGETING::targetService().getAssociated( pCheckPres, i_pSys,
+ TARGETING::TargetService::CHILD, TARGETING::TargetService::ALL,
+ &checkExpr );
+
+ for (TARGETING::TargetHandleList::const_iterator pTarget_it =
+ pCheckPres.begin();
+ pTarget_it != pCheckPres.end();
+ ++pTarget_it
+ )
+ {
+
+ TARGETING::TargetHandle_t pTarget = *pTarget_it;
+ uint32_t l_fruId = pTarget->getAttr<TARGETING::ATTR_FRU_ID>();
+
+ // check if this is a membuf target, if it is and the special
+ // attribute to say we want a separate fru entry for the centaur ecids
+ // is populated, then we will push that ecid to the potential frus
+ // list
+
+ if (TARGETING::TYPE_MEMBUF == pTarget->getAttr<TARGETING::ATTR_TYPE>())
+ {
+ uint8_t l_ecidFruId =
+ pTarget->getAttr<TARGETING::ATTR_CENTAUR_ECID_FRU_ID>();
+
+ // if the ecid fru id is valid use it, else use the regular fru id
+ l_fruId = ( l_ecidFruId == 0xFF ) ? l_fruId : l_ecidFruId;
+
+ TRACFCOMP(g_trac_ipmi,"l_fruId = 0x%x, l_ecidFruId = 0x%x", l_fruId, l_ecidFruId);
+ }
+
+ if (l_fruId)
+ {
+ //when updating data, ignore clearing data
+ if (i_updateData == false)
+ {
+ //Indicate this fruId has data and later clear is not needed
+ io_frusToClear[l_fruId] = false;
+ }
+ io_potentialFrus.push_back(std::make_pair(pTarget, l_fruId));
+ }
+ }
+
+ //Sort the vector by FRU_ID for later use.
+ //When the planar eeprom is shared for planar and memory buffer vpd, the
+ //node and membuffs will have the same FRU ID. For this case, sort the Node
+ //to be ahead of the mem buffs. The mem buffs will be extra targets for
+ //their ECIDs.
+ std::sort(io_potentialFrus.begin(),
+ io_potentialFrus.end(),
+ comparePairs);
+}
diff --git a/src/usr/ipmiext/ipmifruinvprvt.H b/src/usr/ipmiext/ipmifruinvprvt.H
new file mode 100644
index 000000000..772a650f8
--- /dev/null
+++ b/src/usr/ipmiext/ipmifruinvprvt.H
@@ -0,0 +1,689 @@
+/* IBM_PROLOG_BEGIN_TAG */
+/* This is an automatically generated prolog. */
+/* */
+/* $Source: src/usr/ipmiext/ipmifruinvprvt.H $ */
+/* */
+/* OpenPOWER HostBoot Project */
+/* */
+/* Contributors Listed Below - COPYRIGHT 2014,2018 */
+/* [+] 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 */
+
+#ifndef __IPMI_IPMIFRUINVPRVT_H
+#define __IPMI_IMPIFRUINVPRVT_H
+
+namespace IPMIFRUINV
+{
+ enum recordFields
+ {
+ //Storage Definition Headers will use version 1.2
+ SPEC_VERSION = 1,
+ RECORD_UNIT_OF_MEASUREMENT = 8, //size in bytes
+ CHECKSUM_SIZE = 1, //size in bytes
+ RECORD_NOT_PRESENT = 0,
+ ENGLISH_LANGUAGE_CODE = 0,
+ TYPELENGTH_BYTE_NULL = 0,
+ TYPELENGTH_BYTE_ASCII = 0xC0,
+ END_OF_CUSTOM_FIELDS = 0xC1,
+ COMMON_HEADER_FORMAT_SIZE = 8, //size in bytes
+ DEFAULT_CHASSIS_TYPE = 0x05,
+ DEFAULT_FRU_OFFSET = 0,
+ TYPELENGTH_SIZE_MASK = 0x3F, //bits 5:0 define num data bytes
+ MAX_ASCII_FIELD_SIZE = 0x3F, //size in bytes
+ MAX_RECORD_SIZE = 0xFF, //size in bytes
+ };
+
+ enum commonHeaderEntryOffsets
+ {
+ HEADER_FORMART_VERSION = 0,
+ PRODUCT_INFO_AREA = 4,
+ };
+
+};
+
+
+//Parent Class Contains all common functions to build up, format,
+// and send IPMI Fru Inventory Record Data
+//The parent Class also defines pure virtual functions so the child
+// is responsible to build up the individual record sections
+// as the data for those is specific to each child.
+class IpmiFruInv
+{
+ public:
+
+
+ /**
+ * @brief Constructor
+ *
+ * @param[in] TargetHandle_t, Handle to target for which
+ * to get relevant IPMI FRU Data from
+ */
+ IpmiFruInv(TARGETING::TargetHandle_t i_target);
+
+ /**
+ * @brief Default Destructor
+ */
+ virtual ~IpmiFruInv();
+
+ /**
+ * @brief Factory Pattern Creator function
+ * @param[in] TargetHandle_t, Handle to target for which
+ * to get relevant IPMI FRU Data from
+ * @param[in] isUpdate, Indicator if the code is updating
+ * existing data, or setting base data.
+ */
+ static IpmiFruInv *Factory(TARGETING::TargetHandleList i_targets,
+ bool i_isUpdate);
+
+ /**
+ * @brief Package and send the IPMI FRU Data
+ *
+ * @param[in] uint8_t, IPMI Fru Device ID
+ */
+ void sendFruData(uint8_t i_deviceId);
+
+ /**
+ * @brief Gather and Format relevant IPMI Fru Inventory Data
+ *
+ * @param[in] void
+ * @param[out] errlHndl_t, error encountered getting data
+ */
+ errlHndl_t buildFruInvRecord(void);
+
+ protected:
+ //The target to build an IPMI fru inventory record for
+ TARGETING::TargetHandle_t iv_target;
+
+ //The overall IPMI Fru Inventory record to send
+ std::vector<uint8_t> iv_record_data;
+
+ /**
+ * @brief Format Beginning of Individual IPMI Fru Data Section
+ *
+ * @param[in/out] data, Data to be updated with formatting + header
+ * @param[in] i_setLanguagCode, Indicator to set/not set Language Code
+ */
+ void preFormatProcessing(std::vector<uint8_t>& io_data,
+ bool i_setLanguageCode);
+
+ /**
+ * @brief Format End of Individual IPMI Fru Data Section
+ *
+ * @param[in/out] data, Data container to be updated with formatting
+ */
+ void postFormatProcessing(std::vector<uint8_t>& io_data);
+ /**
+ * @brief Complete + Set size of Data Section
+ *
+ * @param[in/out] data, Data container with size to be updated
+ * @param[in] offset, Indicate where to update size of data section
+ */
+ void setAreaSize(std::vector<uint8_t>& io_data, uint8_t i_offset);
+
+ /**
+ * @brief Add inputted data to overall IPMI Fru Record
+ *
+ * @param[in] data, Data container to be added to overall record
+ */
+ void addDataToRecord(const std::vector<uint8_t>& i_data);
+
+ /**
+ * @brief Format inpuuted data to hex data that contains build date info
+ *
+ * @param[in] mfgDateData, MFG date data container from VPD
+ * @param[out] mfgDate, MFG date data value in minutes
+ */
+ errlHndl_t formatMfgData(std::vector<uint8_t> i_mfgDateData,
+ uint32_t& o_mfgDate);
+
+ /**
+ * @brief Add product build date to IPMI fru record
+ *
+ * @param[in/out] data, The container to put build date in
+ * @param[in] mfgDateData, MFG date data container from VPD
+ */
+ void setMfgData(std::vector<uint8_t> &io_data,
+ std::vector<uint8_t> &i_mfgDateData);
+
+ /**
+ * @brief Builds the Internal Use Area Data Section
+ * @param[in/out] data, The container to put intenral use area data in
+ */
+ virtual errlHndl_t buildInternalUseArea(std::vector<uint8_t> &io_data)=0;
+
+ /**
+ * @brief Builds the Chassis Info Area Data Section
+ * @param[in/out] data, The container to put chassis info area data in
+ */
+ virtual errlHndl_t buildChassisInfoArea(std::vector<uint8_t> &io_data)=0;
+
+ /**
+ * @brief Builds the Board Info Area Data Section
+ * @param[in/out] data, The container to put board info area data in
+ */
+ virtual errlHndl_t buildBoardInfoArea(std::vector<uint8_t> &io_data)=0;
+
+ /**
+ * @brief Builds the Product Info Area Data Section
+ * @param[in/out] data, The container to put product info area data in
+ */
+ virtual errlHndl_t buildProductInfoArea(std::vector<uint8_t> &io_data)=0;
+
+ /**
+ * @brief Builds the MultiRecord Info Area Data Section
+ * @param[in/out] data, The container to put multirecord info area data in
+ */
+ virtual errlHndl_t buildMultiRecordInfoArea(
+ std::vector<uint8_t> &io_data)=0;
+
+ /**
+ * @brief Builds a standard data section that is empty
+ * @param[in] data, The container to put the data in
+ */
+ virtual errlHndl_t buildEmptyArea(std::vector<uint8_t> &i_data);
+
+ /**
+ * @brief Debug function to print data to console
+ * @param[in] data, The data to be printed
+ */
+ void printRecordDebugData(const std::vector<uint8_t> &i_data);
+
+ /**
+ * @brief Adds ECID attribute data
+ * @param[in] target, The target to get the Sensor Number from
+ * @param[in] ecid_info, The ECID attribute data
+ * @param[in/out] data, The container to put ECID attribute data in
+ */
+ void addEcidData(const TARGETING::TargetHandle_t& i_target,
+ const TARGETING::ATTR_ECID_type& i_ecidInfo,
+ std::vector<uint8_t> &io_data);
+
+ /**
+ * @brief Adds EC attribute data
+ * @param[in] target, The target to get the Sensor Number from
+ * @param[in] ecInfo, The EC attribute data
+ * @param[in/out] data, The container to put EC attribute data in
+ */
+ void addECData(const TARGETING::TargetHandle_t& i_target,
+ const TARGETING::ATTR_EC_type& i_ecInfo,
+ std::vector<uint8_t> &io_data);
+ /**
+ * @brief Adds Ecid and EC attribute data from extra targets as custom data
+ * @param[in] TargetHandleList, Handle to list of extra
+ * targets associated with this FRU Record
+ * @param[in/out] data, The container to put ECID attribute data in
+ */
+ void customData(TARGETING::TargetHandleList i_extraTargets,
+ std::vector<uint8_t> &io_data);
+
+ /**
+ * @brief Retrieve vpd record keyword and add to IPMI Fru Inventory record
+ * @param[in/out] data, The container with record data
+ * @param[in] access, Indicates vpd module to access (MVPD,PVPD,CPVD)
+ * @param[in] record, Indicates major offset in the VPD to get more data
+ * @param[in] keyword, Indicates minor offset in the VPD to get more data
+ * @param[in] ascii, Indicates if VPD field is in ascii format or not
+ */
+ errlHndl_t addCommonVpdData(
+ const TARGETING::TargetHandle_t& i_target,
+ std::vector<uint8_t> &io_data,
+ DeviceFW::AccessType i_accessType,
+ uint8_t i_record,
+ uint8_t i_keyword,
+ bool i_ascii,
+ bool i_typeLengthByte);
+
+ /**
+ * @brief Add attribute data to IPMI Fru Inventory record
+ * @param[in/out] data, The container with record data
+ * @param[in] attrData, Pointer to attribute data
+ * @param[in] length, Length of data to add
+ */
+ void addCommonAttrData( std::vector<uint8_t> &io_data,
+ uint8_t * i_pAttrData,
+ size_t i_length);
+
+ // The ATTR_SERIAL_NUMBER and ATTR_PART_NUMBER are 18 bytes but the
+ // vpd data size is only 16. Only want to add the vpd data, not the pad.
+ enum vpdSize
+ {
+ VPD_SN_PN_VPD_SIZE = 16,
+ };
+
+ // Store number of days in a each month for computation of build date
+ const uint8_t daysInMonth[12] = {31, 28, 31, 30, 31, 30,
+ 31, 31, 30, 31, 30, 31};
+
+ private:
+ /**
+ * @brief Compute and Add data checsum to data record
+ *
+ * @param[in/out] data, Data to be updated with checsum
+ */
+ void addDataChecksum(std::vector<uint8_t>& io_data);
+
+ /**
+ * @brief Pad data section to proper size
+ *
+ * @param[in/out] data, Data to be padded
+ */
+ void padData(std::vector<uint8_t>& io_data);
+
+ /**
+ * @brief Builds a section of the common header
+ *
+ * @param[in/out] data, Common Header section data container
+ * @param[in] section_data, Data from one particular record section
+ * @param[in/out] cur_offset, Current offset for data in overall record
+ */
+ void buildCommonHeaderSection(std::vector<uint8_t>& io_out_data,
+ uint32_t i_section_data,
+ uint32_t &io_cur_offset);
+
+ /**
+ * @brief Sets IPMI Fru Spec Revision Header Format Value
+ *
+ * @param[in/out] data, data to add format value to
+ */
+ void addHeaderFormat(std::vector<uint8_t>& io_data);
+
+ /**
+ * @brief Builds the common header data in iv_record
+ *
+ * @param[in] internal_use_data, IPMI internal use section data
+ * @param[in] chassis_data, IPMI chassis section data
+ * @param[in] board_data, IPMI board section data
+ * @param[in] product_data, IPMI product section data
+ * @param[in] multirecord_data, IPMI multirecord section data
+ * @param[out] void
+ */
+ void buildCommonHeader( const std::vector<uint8_t> &i_internal_use_data,
+ const std::vector<uint8_t> &i_chassis_data,
+ const std::vector<uint8_t> &i_board_data,
+ const std::vector<uint8_t> &i_product_data,
+ const std::vector<uint8_t> &i_multirecord_data );
+
+ /**
+ * @brief Complete entire record by combining all data parts
+ *
+ * @param[in] internal_use_data, IPMI internal use section data
+ * @param[in] chassis_data, IPMI chassis section data
+ * @param[in] board_data, IPMI board section data
+ * @param[in] product_data, IPMI product section data
+ * @param[in] multirecord_data, IPMI multirecord section data
+ */
+ void completeRecord(const std::vector<uint8_t> &i_internal_use_data,
+ const std::vector<uint8_t> &i_chassis_data,
+ const std::vector<uint8_t> &i_board_data,
+ const std::vector<uint8_t> &i_product_data,
+ const std::vector<uint8_t> &i_multirecord_data);
+
+};
+
+
+//Child class for building up ISDimm Fru Inventory Record Data
+class isdimmIpmiFruInv : public IpmiFruInv
+{
+
+ public:
+
+ /**
+ * @brief Constructor
+ *
+ * @param[in] TargetHandle_t, Handle to target for which
+ * to get relevant IPMI FRU Data from
+ */
+ isdimmIpmiFruInv( TARGETING::TargetHandle_t i_target);
+
+ /**
+ * @brief Builds the Internal Use Area Data Section
+ * @param[in/out] data, The container to put internal use area data in
+ */
+ errlHndl_t buildInternalUseArea(std::vector<uint8_t> &io_data);
+
+ /**
+ * @brief Builds the Chassis Info Area Data Section
+ * @param[in/out] data, The container to put chassis info area data in
+ */
+ errlHndl_t buildChassisInfoArea(std::vector<uint8_t> &io_data);
+
+ /**
+ * @brief Builds the Board Info Area Data Section
+ * @param[in/out] data, The container to put board info area data in
+ */
+ errlHndl_t buildBoardInfoArea(std::vector<uint8_t> &io_data);
+
+ /**
+ * @brief Builds the Product Info Area Data Section
+ * @param[in/out] data, The container to put product info area data in
+ */
+ errlHndl_t buildProductInfoArea(std::vector<uint8_t> &io_data);
+
+ /**
+ * @brief Builds the MultiRecord Info Area Data Section
+ * @param[in/out] data, The container to put multirecord info area data in
+ */
+ errlHndl_t buildMultiRecordInfoArea(std::vector<uint8_t> &io_data);
+
+ private:
+
+ /**
+ * @brief Adds the specified VPD data to the data to build up a given
+ * record
+ * @param[in/out] data, The container with record data
+ * @param[in] keyword, Indicates where in the VPD to get more data
+ * @param[in] ascii, Indicates if VPD field is in ascii format or not
+ */
+ errlHndl_t addVpdData(std::vector<uint8_t> &io_data,
+ uint8_t i_keyword,
+ bool i_ascii=false);
+
+ /**
+ * @brief Add manufacturer name of memory module into IPMI message buffer.
+ * @param[in/out] data, The container with record data
+ * @return errlHndl_t, Error handle, NULL if operation was completed
+ * successfully
+ */
+ errlHndl_t addSpdManufacturer(std::vector<uint8_t> &io_data);
+
+ /**
+ * @brief Add detailed description of memory module into IPMI message buffer.
+ * @param[in/out] data, The container with record data
+ * @return errlHndl_t, Error handle, NULL if operation was completed
+ * successfully
+ */
+ errlHndl_t addSpdDetails(std::vector<uint8_t> &io_data);
+
+};
+
+//Child class for building up Processor Fru Inventory Record Data
+class procIpmiFruInv : public IpmiFruInv
+{
+
+ public:
+
+ /**
+ * @brief Constructor
+ *
+ * @param[in] TargetHandle_t, Handle to target for which
+ * to get relevant IPMI FRU Data from
+ * @param[in] isUpdate, Indicator if the code is updating
+ * existing data, or setting base data.
+ */
+ procIpmiFruInv( TARGETING::TargetHandle_t i_target,
+ bool i_isUpdate );
+
+ /**
+ * @brief Builds the Internal Use Area Data Section
+ * @param[in/out] data, The container to put internal use area data in
+ */
+ errlHndl_t buildInternalUseArea(std::vector<uint8_t> &io_data);
+
+ /**
+ * @brief Builds the Chassis Info Area Data Section
+ * @param[in/out] data, The container to put chassis info area data in
+ */
+ errlHndl_t buildChassisInfoArea(std::vector<uint8_t> &io_data);
+
+ /**
+ * @brief Builds the Board Info Area Data Section
+ * @param[in/out] data, The container to put board info area data in
+ */
+ errlHndl_t buildBoardInfoArea(std::vector<uint8_t> &io_data);
+
+ /**
+ * @brief Builds the Product Info Area Data Section
+ * @param[in/out] data, The container to put product info area data in
+ */
+ errlHndl_t buildProductInfoArea(std::vector<uint8_t> &io_data);
+
+ /**
+ * @brief Builds the MultiRecord Info Area Data Section
+ * @param[in/out] data, The container to put multirecord info area data in
+ */
+ errlHndl_t buildMultiRecordInfoArea(std::vector<uint8_t> &io_data);
+
+ protected:
+
+ //Indicator if a data update is happening.
+ //True - means we are solely updating certain data
+ //False - means we are doing the initial 'base' data set
+ bool iv_isUpdate;
+
+ private:
+
+ /**
+ * @brief Adds the specified VPD data to the data to build up a given
+ * IPMI Fru Inventory record
+ * @param[in/out] data, The container with record data
+ * @param[in] record, Indicates major offset in the VPD to get more data
+ * @param[in] keyword, Indicates minor offset in the VPD to get more data
+ * @param[in] ascii, Indicates if VPD field is in ascii format or not
+ * @param[in] typeLengthBtye, Indicates whether type length to be added.
+ */
+ errlHndl_t addVpdData(std::vector<uint8_t> &io_data,
+ uint8_t i_record,
+ uint8_t i_keyword,
+ bool i_ascii=false,
+ bool i_typeLengthByte=true);
+
+};
+
+//Child class for building up backplane Fru Inventory Record Data
+class backplaneIpmiFruInv : public IpmiFruInv
+{
+
+ public:
+
+ /**
+ * @brief Constructor
+ *
+ * @param[in] TargetHandle_t, Handle to target for which
+ * to get relevant IPMI FRU Data from
+ * @param[in] TargetHandleList, Handle to list of extra
+ * targets associated with this FRU Record
+ * @param[in] isUpdate, Indicator if the code is updating
+ * existing data, or setting base data.
+ */
+ backplaneIpmiFruInv( TARGETING::TargetHandle_t i_target,
+ TARGETING::TargetHandleList i_extraTargets,
+ bool i_isUpdate );
+
+ /**
+ * @brief Builds the Internal Use Area Data Section
+ * @param[in/out] data, The container to put internal use area data in
+ */
+ errlHndl_t buildInternalUseArea(std::vector<uint8_t> &io_data);
+
+ /**
+ * @brief Builds the Chassis Info Area Data Section
+ * @param[in/out] data, The container to put chassis info area data in
+ */
+ errlHndl_t buildChassisInfoArea(std::vector<uint8_t> &io_data);
+
+ /**
+ * @brief Builds the Board Info Area Data Section
+ * @param[in/out] data, The container to put board info area data in
+ */
+ errlHndl_t buildBoardInfoArea(std::vector<uint8_t> &io_data);
+
+ /**
+ * @brief Builds the Product Info Area Data Section
+ * @param[in/out] data, The container to put product info area data in
+ */
+ errlHndl_t buildProductInfoArea(std::vector<uint8_t>& io_data);
+
+ /**
+ * @brief Builds the MultiRecord Info Area Data Section
+ * @param[in/out] data, The container to put multirecord info area data in
+ */
+ errlHndl_t buildMultiRecordInfoArea(std::vector<uint8_t>& io_data);
+
+ protected:
+ //Indicator if a data update is happening.
+ //True - means we are solely updating certain data
+ //False - means we are doing the initial 'base' data set
+ bool iv_isUpdate;
+
+ //The list of Extra Targets if multiple targets are
+ //associated with one FRU_ID
+ TARGETING::TargetHandleList iv_extraTargets;
+
+ private:
+
+ /**
+ * @brief Adds the specified VPD data to the data to build up a given
+ * IPMI Fru Inventory record
+ * @param[in/out] data, The container with record data
+ * @param[in] record, Indicates major offset in the VPD to get more data
+ * @param[in] keyword, Indicates minor offset in the VPD to get more data
+ * @param[in] ascii, Indicates if VPD field is in ascii format or not
+ * @param[in] typeLengthBtye, Indicates whether type length to be added.
+ */
+ errlHndl_t addVpdData(std::vector<uint8_t> &io_data,
+ uint8_t i_record,
+ uint8_t i_keyword,
+ bool i_ascii=false,
+ bool i_typeLengthByte=true);
+};
+
+//Child class for building up System Firwmare Fru Inventory Record Data
+class systemFwIpmiFruInv : public IpmiFruInv
+{
+
+ public:
+ /**
+ * @brief Constructor
+ *
+ * @param[in] TargetHandle_t, Handle to target for which
+ * to get relevant IPMI FRU Data from
+ */
+ systemFwIpmiFruInv( TARGETING::TargetHandle_t i_target);
+
+ /**
+ * @brief Builds the Internal Use Area Data Section
+ * @param[in/out] data, The container to put internal use area data in
+ */
+ errlHndl_t buildInternalUseArea(std::vector<uint8_t> &io_data);
+
+ /**
+ * @brief Builds the Chassis Info Area Data Section
+ * @param[in/out] data, The container to put chassis info area data in
+ */
+ errlHndl_t buildChassisInfoArea(std::vector<uint8_t> &io_data);
+
+ /**
+ * @brief Builds the Board Info Area Data Section
+ * @param[in/out] data, The container to put board info area data in
+ */
+ errlHndl_t buildBoardInfoArea(std::vector<uint8_t> &io_data);
+
+ /**
+ * @brief Builds the Product Info Area Data Section
+ * @param[in/out] data, The container to put product info area data in
+ */
+ errlHndl_t buildProductInfoArea(std::vector<uint8_t> &io_data);
+
+ /**
+ * @brief Builds the MultiRecord Info Area Data Section
+ * @param[in/out] data, The container to put multirecord info area data in
+ */
+ errlHndl_t buildMultiRecordInfoArea(std::vector<uint8_t> &io_data);
+
+};
+
+//Child class for building up membuf Fru Inventory Record Data. For example,
+//for a memory riser card Fru.
+class membufIpmiFruInv : public IpmiFruInv
+{
+
+ public:
+
+ /**
+ * @brief Constructor
+ *
+ * @param[in] TargetHandle_t, Handle to target for which
+ * to get relevant IPMI FRU Data from
+ * @param[in] TargetHandleList, Handle to list of extra
+ * targets associated with this FRU Record
+ * @param[in] isUpdate, Indicator if the code is updating
+ * existing data, or setting base data.
+ */
+ membufIpmiFruInv( TARGETING::TargetHandle_t i_target,
+ TARGETING::TargetHandleList i_extraTargets,
+ bool i_isUpdate );
+
+ /**
+ * @brief Builds the Internal Use Area Data Section
+ * @param[in/out] data, The container to put internal use area data in
+ */
+ errlHndl_t buildInternalUseArea(std::vector<uint8_t> &io_data);
+
+ /**
+ * @brief Builds the Chassis Info Area Data Section
+ * @param[in/out] data, The container to put chassis info area data in
+ */
+ errlHndl_t buildChassisInfoArea(std::vector<uint8_t> &io_data);
+
+ /**
+ * @brief Builds the Board Info Area Data Section
+ * @param[in/out] data, The container to put board info area data in
+ */
+ errlHndl_t buildBoardInfoArea(std::vector<uint8_t> &io_data);
+
+ /**
+ * @brief Builds the Product Info Area Data Section
+ * @param[in/out] data, The container to put product info area data in
+ */
+ errlHndl_t buildProductInfoArea(std::vector<uint8_t>& io_data);
+
+ /**
+ * @brief Builds the MultiRecord Info Area Data Section
+ * @param[in/out] data, The container to put multirecord info area data in
+ */
+ errlHndl_t buildMultiRecordInfoArea(std::vector<uint8_t>& io_data);
+
+ protected:
+ //Indicator if a data update is happening.
+ //True - means we are solely updating certain data
+ //False - means we are doing the initial 'base' data set
+ bool iv_isUpdate;
+
+ //The list of Extra Targets if multiple targets are
+ //associated with one FRU_ID
+ TARGETING::TargetHandleList iv_extraTargets;
+
+ private:
+
+ /**
+ * @brief Adds the specified VPD data to the data to build up a given
+ * IPMI Fru Inventory record
+ * @param[in/out] data, The container with record data
+ * @param[in] record, Indicates major offset in the VPD to get more data
+ * @param[in] keyword, Indicates minor offset in the VPD to get more data
+ * @param[in] ascii, Indicates if VPD field is in ascii format or not
+ * @param[in] typeLengthBtye, Indicates whether type length to be added.
+ */
+ errlHndl_t addVpdData(std::vector<uint8_t> &io_data,
+ uint8_t i_record,
+ uint8_t i_keyword,
+ bool i_ascii=false,
+ bool i_typeLengthByte=true);
+
+};
+
+#endif
diff --git a/src/usr/ipmiext/ipmipowerstate.C b/src/usr/ipmiext/ipmipowerstate.C
new file mode 100644
index 000000000..b76200d26
--- /dev/null
+++ b/src/usr/ipmiext/ipmipowerstate.C
@@ -0,0 +1,86 @@
+/* IBM_PROLOG_BEGIN_TAG */
+/* This is an automatically generated prolog. */
+/* */
+/* $Source: src/usr/ipmiext/ipmipowerstate.C $ */
+/* */
+/* OpenPOWER HostBoot Project */
+/* */
+/* Contributors Listed Below - COPYRIGHT 2014,2018 */
+/* [+] 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 ipmipowerstate.C
+ *
+ * Ipmi set ACPI Power State
+ *
+ */
+
+/******************************************************************************/
+// Includes
+/******************************************************************************/
+#include <stdint.h>
+#include <errl/errlentry.H>
+#include <ipmi/ipmipowerstate.H>
+#include <ipmi/ipmiif.H>
+
+/******************************************************************************/
+// Globals/Constants
+/******************************************************************************/
+// Defined in ipmidd.C
+extern trace_desc_t * g_trac_ipmi;
+#define IPMI_TRAC(printf_string,args...) \
+ TRACFCOMP(g_trac_ipmi,"ps: " printf_string,##args)
+
+namespace IPMI
+{
+
+/******************************************************************************/
+// Functions
+/******************************************************************************/
+
+errlHndl_t setACPIPowerState()
+{
+ errlHndl_t err_ipmi = NULL;
+
+ size_t len = 2; // IPMI spec has request data at 2 bytes
+
+ //create request data buffer
+ uint8_t* data = new uint8_t[len];
+
+ IPMI::completion_code cc = IPMI::CC_UNKBAD;
+
+ data[0] = SET_SYSTEM_POWER_STATE | SET_LEGACY_ON;
+ data[1] = NO_CHANGE_DEVICE_POWER;
+
+ err_ipmi = IPMI::sendrecv(IPMI::set_acpi_power_state(), cc, len, data);
+
+ //cleanup buffer
+ delete[] data;
+
+ if(cc != IPMI::CC_OK)
+ {
+ IPMI_TRAC("Set ACPI Power State: BMC returned not ok CC[%x]",cc);
+ // should we log error and then retry?
+ // what happens if the communication is broken
+ // reset will try and set it again.
+ }
+
+ return err_ipmi;
+}
+
+} // namespace
+
diff --git a/src/usr/ipmiext/ipmisel.C b/src/usr/ipmiext/ipmisel.C
new file mode 100644
index 000000000..369c5b79f
--- /dev/null
+++ b/src/usr/ipmiext/ipmisel.C
@@ -0,0 +1,596 @@
+/* IBM_PROLOG_BEGIN_TAG */
+/* This is an automatically generated prolog. */
+/* */
+/* $Source: src/usr/ipmiext/ipmisel.C $ */
+/* */
+/* OpenPOWER HostBoot Project */
+/* */
+/* Contributors Listed Below - COPYRIGHT 2014,2018 */
+/* [+] Google Inc. */
+/* [+] 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 ipmisel.C
+ * @brief IPMI system error log transport definition
+ */
+
+#include <algorithm>
+#include <sys/time.h>
+#include <ipmi/ipmisel.H>
+#include <ipmi/ipmi_reasoncodes.H>
+#include <ipmi/ipmisensor.H>
+#include <ipmi/ipmiif.H>
+
+#include <sys/task.h>
+#include <initservice/taskargs.H>
+#include <initservice/initserviceif.H>
+#include <targeting/common/commontargeting.H>
+#include <targeting/common/util.H>
+#include <targeting/common/utilFilter.H>
+
+#include <errl/errlmanager.H>
+using namespace TARGETING;
+
+//Defined in ipmidd.C
+extern trace_desc_t * g_trac_ipmi;
+#define IPMI_TRAC(printf_string,args...) \
+ TRACFCOMP(g_trac_ipmi,"sel: " printf_string,##args)
+
+// local functions
+/*
+ * @brief Store either Record/Reserve ID from given data
+ */
+void storeReserveRecord(uint8_t* o_RData, uint8_t* i_data)
+{
+ o_RData[0] = i_data[0];
+ o_RData[1] = i_data[1];
+ return;
+}
+
+/*
+ * @brief Create Partial Add eSEL Header from inputs
+ */
+void createPartialAddHeader(uint8_t* i_reserveID, uint8_t* i_recordID,
+ uint16_t i_offset, uint8_t i_isLastEntry,
+ uint8_t* o_header)
+{
+ o_header[0] = i_reserveID[0];
+ o_header[1] = i_reserveID[1];
+ o_header[2] = i_recordID[0];
+ o_header[3] = i_recordID[1];
+ o_header[4] = (uint8_t)(i_offset & 0x00FF);
+ o_header[5] = (uint8_t)((i_offset & 0xFF00) >> 8);
+ o_header[6] = i_isLastEntry;
+ return;
+}
+
+enum esel_retry
+{
+ MAX_SEND_COUNT = 4,
+ SLEEP_BASE = 2 * NS_PER_MSEC,
+};
+
+
+namespace IPMISEL
+{
+void sendESEL(uint8_t* i_eselData, uint32_t i_dataSize,
+ uint32_t i_eid, std::vector<sel_info_t*>&i_selEventList,
+ bool i_infoCallHome)
+{
+ IPMI_TRAC(ENTER_MRK "sendESEL() %d",i_selEventList.size());
+
+#ifdef __HOSTBOOT_RUNTIME
+ // HBRT doesn't send a msg, but use the msg structure to pass the data
+ msg_t l_msg;
+ msg_t *msg = &l_msg;
+ memset(msg, 0, sizeof(msg_t));
+#else
+ msg_t *msg = msg_allocate();
+#endif
+ msg->type = MSG_SEND_ESEL;
+ auto * const pData = reinterpret_cast<send_esel_data_t*>
+ (msg->data);
+ pData->callhome=i_infoCallHome;
+ pData->eid=i_eid;
+
+ eselInitData *eselData =
+ new eselInitData(i_selEventList, i_eselData, i_dataSize);
+
+ msg->extra_data = eselData;
+
+#ifdef __HOSTBOOT_RUNTIME
+ process_esel(msg);
+#else
+ // one message queue to the SEL thread
+ static msg_q_t mq = Singleton<IpmiSEL>::instance().msgQueue();
+
+ //Send the msg to the sel thread
+ int rc = msg_send(mq,msg);
+ if(rc)
+ {
+ IPMI_TRAC(ERR_MRK "Failed (rc=%d) to send message",rc);
+ delete eselData;
+ }
+#endif
+ IPMI_TRAC(EXIT_MRK "sendESEL");
+ return;
+}
+
+/*
+ * @brief process esel msg
+ */
+void process_esel(msg_t *i_msg)
+{
+ errlHndl_t l_err = NULL;
+ IPMI::completion_code l_cc = IPMI::CC_UNKBAD;
+
+ assert(i_msg != nullptr,"i_msg was nullptr");
+ const auto * const pData = reinterpret_cast<const send_esel_data_t*>
+ (i_msg->data);
+ const auto callhome = pData->callhome;
+ const auto l_eid = pData->eid;
+
+ eselInitData * l_data =
+ (eselInitData*)(i_msg->extra_data);
+ IPMI_TRAC(ENTER_MRK "process_esel");
+ selRecord l_eSel;
+ oemSelRecord l_oemSel;
+
+ do
+ {
+ IPMI_TRAC(ENTER_MRK"sel list size %d", l_data->selInfoList.size());
+ std::vector<sel_info_t*>::iterator it;
+ for (it = l_data->selInfoList.begin(); it != l_data->selInfoList.end();
+ ++it)
+ {
+ sel_info_t *l_sel = *it;
+ memset(l_data->oemSel,0,sizeof(oemSelRecord));
+ memset(l_data->eSel,0,sizeof(selRecord));
+ l_data->selEvent = true;
+
+ //If sensor type is sys event then need to send the oem sel
+ //to handle procedure callout
+ if (l_sel->sensorType == TARGETING::SENSOR_TYPE_SYS_EVENT)
+ {
+ //oem sel data
+ l_data->selEvent = false;
+ l_oemSel.record_type =
+ record_type_oem_sel_for_procedure_callout;
+ l_oemSel.event_data1 = l_sel->eventOffset;
+ l_sel->eventOffset = SENSOR::UNDETERMINED_SYSTEM_HW_FAILURE;
+ memcpy(l_data->oemSel,&l_oemSel,sizeof(oemSelRecord));
+ }
+
+ //sel data
+ l_eSel.record_type = record_type_system_event;
+ l_eSel.generator_id = generator_id_ami;
+ l_eSel.evm_format_version = format_ipmi_version_2_0;
+ l_eSel.sensor_type = l_sel->sensorType;
+ l_eSel.sensor_number = l_sel->sensorNumber;
+ l_eSel.event_dir_type = l_sel->eventDirType;
+ l_eSel.event_data1 = l_sel->eventOffset;
+ memcpy(l_data->eSel,&l_eSel,sizeof(selRecord));
+
+
+ uint32_t l_send_count = MAX_SEND_COUNT;
+ while (l_send_count > 0)
+ {
+ // try to send the esel to the bmc
+ send_esel(l_data, l_err, l_cc, callhome);
+
+ // if no error but last completion code was:
+ if ((l_err == NULL) &&
+ ((l_cc == IPMI::CC_BADRESV) || // lost reservation
+ (l_cc == IPMI::CC_TIMEOUT))) // timeout
+ {
+ // update our count and pause
+ l_send_count--;
+ if (l_send_count)
+ {
+ IPMI_TRAC("process_esel: sleeping; retry_count %d",
+ l_send_count);
+ // sleep 3 times - 2ms, 32ms, 512ms. if we can't get this
+ // through by then, the system must really be busy...
+ nanosleep(0,
+ SLEEP_BASE << (4 * (MAX_SEND_COUNT - l_send_count - 1)));
+ continue;
+ }
+ }
+ else
+ {
+ //if we enter this, then pel data has logged successfully,
+ //so we don't need to log again, make the size to zero.
+ l_data->dataSize = 0;
+ }
+ // else it did get sent down OR it didn't because of a bad error
+ break;
+ } // while
+ } // for
+
+ }while(0);
+
+ if(l_err)
+ {
+#ifdef __HOSTBOOT_RUNTIME
+ // HBRT can't commit an error, since this could already be in the
+ // errlCommit path since HBRT is single threaded
+ IPMI_TRAC(ERR_MRK "DELETING l_err 0x%.8X", l_err->eid());
+ delete l_err;
+ l_err = NULL;
+#else
+ l_err->collectTrace(IPMI_COMP_NAME);
+ errlCommit(l_err, IPMI_COMP_ID);
+#endif
+ }
+ else if((l_cc == IPMI::CC_OK) && // no error
+ (l_eid != 0)) // and it's an errorlog
+ {
+ // eSEL successfully sent to the BMC - have errlmanager do the ack
+ IPMI_TRAC(INFO_MRK "Doing ack for eid 0x%.8X", l_eid);
+ ERRORLOG::ErrlManager::errlAckErrorlog(l_eid);
+ }
+
+ delete l_data;
+
+ IPMI_TRAC(EXIT_MRK "process_esel");
+ return;
+} // process_esel
+
+/*
+ * @brief Send esel data to bmc
+ */
+void send_esel(eselInitData * i_data,
+ errlHndl_t &o_err, IPMI::completion_code &o_cc,
+ bool i_infoCallHome)
+{
+ IPMI_TRAC(ENTER_MRK "send_esel");
+ uint8_t* data = NULL;
+
+ size_t len = 0;
+
+ uint8_t sel_recordID[2] = {0,0};
+ uint8_t esel_recordID[2] = {0,0};
+
+ do
+ {
+ const size_t l_eSELlen = i_data->dataSize;
+
+ if (l_eSELlen == 0)
+ {
+ IPMI_TRAC(INFO_MRK "no eSEL data present, skipping to SEL");
+
+ // sending sensor SELs only, not the eSEL
+ o_cc = IPMI::CC_OK;
+
+ break;
+ }
+
+ uint8_t reserveID[2] = {0,0};
+ // we need to send down the extended sel data (eSEL), which is
+ // longer than the protocol buffer, so we need to do a reservation and
+ // call the AMI partial_add_esel command multiple times
+
+ // put a reservation on the SEL Device since we're doing a partial_add
+ len = 0;
+ delete [] data;
+ data = NULL;
+ o_cc = IPMI::CC_UNKBAD;
+ o_err = IPMI::sendrecv(IPMI::reserve_sel(),o_cc,len,data);
+ if(o_err)
+ {
+ IPMI_TRAC(ERR_MRK "error from reserve_sel");
+ break;
+ }
+ if(o_cc != IPMI::CC_OK)
+ {
+ IPMI_TRAC(ERR_MRK "Failed to reserve_sel, o_cc %02x",o_cc);
+ break;
+ }
+ storeReserveRecord(reserveID,data);
+
+ // first send down the SEL Event Record data
+ size_t eSELindex = 0;
+ uint8_t l_lastEntry = 0;
+ len = PARTIAL_ADD_ESEL_REQ + sizeof(selRecord);
+ delete [] data;
+ data = new uint8_t[len];
+
+ // fill in the partial_add_esel request (command) data
+ createPartialAddHeader(reserveID,esel_recordID,eSELindex,l_lastEntry,data);
+
+ // copy in the SEL event record data
+ memcpy(&data[PARTIAL_ADD_ESEL_REQ], i_data->eSel,
+ sizeof(selRecord));
+
+ // add record type for this eSEL
+ if (i_infoCallHome)
+ {
+ data[PARTIAL_ADD_ESEL_REQ + offsetof(selRecord,record_type)] =
+ record_type_oem_call_home_info_event;
+ }
+ else
+ {
+ data[PARTIAL_ADD_ESEL_REQ + offsetof(selRecord,record_type)] =
+ record_type_ami_esel;
+ }
+
+ data[PARTIAL_ADD_ESEL_REQ + offsetof(selRecord,event_data1)] =
+ event_data1_ami;
+
+ o_cc = IPMI::CC_UNKBAD;
+ TRACFBIN( g_trac_ipmi, INFO_MRK"1st partial_add_esel:", data, len);
+ o_err = IPMI::sendrecv(IPMI::partial_add_esel(),o_cc,len,data);
+ if(o_err)
+ {
+ IPMI_TRAC(ERR_MRK "error from first partial_add_esel");
+ break;
+ }
+ // as long as we continue to get CC_OK, the reserve sel is good.
+ // if the reservation is lost (ie, because something else tried to
+ // create a SEL) then the BMC just discards all this data. the
+ // errorlog will still be in PNOR and won't get ACKed, so it'll get
+ // resent on the next IPL.
+ if (o_cc != IPMI::CC_OK)
+ {
+ IPMI_TRAC(ERR_MRK "failed first partial_add_esel, o_cc %02x, eSELindex %02x",
+ o_cc, eSELindex);
+ break;
+ }
+ // BMC returns the recordID, it's always the same (unless
+ // there's a major BMC bug...)
+ storeReserveRecord(esel_recordID,data);
+
+ // now send down the eSEL data in chunks.
+ const size_t l_maxBuffer = IPMI::max_buffer();
+ while(eSELindex<l_eSELlen)
+ {
+ //if the index + the maximum buffer is less than what we still
+ //have left in the eSEL, this is not the last entry (data[6] = 0)
+ //otherwise, it is and data[6] = 1
+ if(eSELindex + (l_maxBuffer - PARTIAL_ADD_ESEL_REQ)
+ < l_eSELlen)
+ {
+ len = l_maxBuffer;
+ l_lastEntry = 0x00;
+ }
+ else
+ {
+ len = l_eSELlen - eSELindex + PARTIAL_ADD_ESEL_REQ;
+ l_lastEntry = 0x01;
+ }
+ delete [] data;
+ data = new uint8_t[len];
+
+ // fill in the partial_add_esel request (command) data
+ createPartialAddHeader(reserveID, esel_recordID,
+ eSELindex + sizeof(selRecord),
+ l_lastEntry, data);
+
+ uint8_t dataCpyLen = len - PARTIAL_ADD_ESEL_REQ;
+ memcpy(&data[PARTIAL_ADD_ESEL_REQ],
+ &i_data->eSelExtra[eSELindex],
+ dataCpyLen);
+
+ // update the offset into the data
+ eSELindex = eSELindex + dataCpyLen;
+
+ o_cc = IPMI::CC_UNKBAD;
+ TRACFBIN( g_trac_ipmi, INFO_MRK"partial_add_esel:", data, len);
+ o_err = IPMI::sendrecv(IPMI::partial_add_esel(),o_cc,len,data);
+ if(o_err)
+ {
+ IPMI_TRAC(ERR_MRK "error from partial_add_esel");
+ break;
+ }
+ // as long as we continue to get CC_OK, the reserve sel is good.
+ // if the reservation is lost (ie, because something else tried to
+ // create a SEL) then the BMC just discards all this data. the
+ // errorlog will still be in PNOR and won't get ACKed, so it'll get
+ // resent on the next IPL.
+ if (o_cc != IPMI::CC_OK)
+ {
+ IPMI_TRAC(ERR_MRK "failed partial_add_esel, o_cc %02x, eSELindex %02x",
+ o_cc, eSELindex);
+ break;
+ }
+ // BMC returns the recordID, it's always the same (unless
+ // there's a major BMC bug...)
+ storeReserveRecord(esel_recordID,data);
+ } // while eSELindex
+ if (o_cc == IPMI::CC_OK)
+ {
+ memcpy(i_data->eselRecord,esel_recordID,sizeof(esel_recordID));
+ }
+ }while(0);
+
+ // if eSEL wasn't created due to an error or callhome, we don't want to continue
+ if ((o_err == NULL) && (o_cc == IPMI::CC_OK) && (!i_infoCallHome))
+ {
+ // caller wants us to NOT create sensor SEL
+ if ((i_data->eSel[offsetof(selRecord,sensor_type)] == SENSOR::INVALID_TYPE) &&
+ (i_data->eSel[offsetof(selRecord,sensor_number)] == TARGETING::UTIL::INVALID_IPMI_SENSOR)
+ )
+ {
+ IPMI_TRAC(INFO_MRK "Invalid sensor type/number - NOT sending sensor SELs");
+ }
+ else
+ {
+ // if the eSEL wasn't created due to a bad completion code, we will
+ // still try to send down a SEL that we create, which will contain
+ // the eSEL recordID (if it was successful)
+ if (data)
+ {
+ delete [] data;
+ }
+ len = sizeof(IPMISEL::selRecord);
+ data = new uint8_t[len];
+
+
+ // send standard SEL event
+ if (i_data->selEvent)
+ {
+ // copy in the SEL event record data
+ memcpy(data, i_data->eSel, len);
+ // copy the eSEL recordID (if it was created) into the extra data area
+ // and mark the event_data1 to indicate this is OEM data
+ data[offsetof(selRecord,event_data1)] |= 0xA0;
+ data[offsetof(selRecord,event_data2)] = i_data->eselRecord[1];
+ data[offsetof(selRecord,event_data3)] = i_data->eselRecord[0];
+ }
+ else //send OEM SEL event
+ {
+ // copy in the SEL event record data
+ memcpy(data, i_data->oemSel, len);
+ data[offsetof(oemSelRecord,event_data5)] = i_data->eselRecord[1];
+ data[offsetof(oemSelRecord,event_data6)] =i_data->eselRecord[0];
+ }
+
+ // use local cc so that we don't corrupt the esel from above
+ IPMI::completion_code l_cc = IPMI::CC_UNKBAD;
+ TRACFBIN( g_trac_ipmi, INFO_MRK"add_sel:", data, len);
+ o_err = IPMI::sendrecv(IPMI::add_sel(),l_cc,len,data);
+ if(o_err)
+ {
+ IPMI_TRAC(ERR_MRK "error from add_sel");
+ }
+ else if (l_cc != IPMI::CC_OK)
+ {
+ IPMI_TRAC(ERR_MRK "failed add_sel, l_cc %02x", l_cc);
+ }
+ else
+ {
+ // if CC_OK, then len=2 and data contains the recordID of the new SEL
+ storeReserveRecord(sel_recordID,data);
+ }
+ }
+ }
+
+ if (data)
+ {
+ delete [] data;
+ }
+
+ IPMI_TRAC(EXIT_MRK
+ "send_esel o_err=%.8X, o_cc=x%.2x, sel recID=x%x%x, esel recID=x%x%x",
+ o_err ? o_err->plid() : NULL, o_cc, sel_recordID[1], sel_recordID[0],
+ esel_recordID[1], esel_recordID[0]);
+
+ return;
+} // send_esel
+
+uint32_t get_sel_time()
+{
+ uint8_t *data = NULL;
+ size_t len = 0;
+ errlHndl_t l_err;
+ IPMI::completion_code l_cc = IPMI::CC_UNKBAD;
+
+ l_err = IPMI::sendrecv( IPMI::get_sel_time(), l_cc, len, data );
+ if( l_err || l_cc != IPMI::CC_OK || len != sizeof(uint32_t) )
+ {
+ delete l_err;
+ delete [] data;
+ return 0;
+ }
+
+ uint32_t timet = le32toh(*reinterpret_cast<uint32_t*>(data));
+ delete [] data;
+ return timet;
+}
+
+} // IPMISEL
+
+#ifndef __HOSTBOOT_RUNTIME
+/**
+ * @brief Constructor
+ */
+IpmiSEL::IpmiSEL(void)
+ :iv_msgQ(msg_q_create())
+{
+ IPMI_TRAC(ENTER_MRK "IpmiSEL ctor");
+ task_create(&IpmiSEL::start,NULL);
+}
+
+/**
+ * @brief Destructor
+ */
+IpmiSEL::~IpmiSEL(void)
+{
+ msg_q_destroy(iv_msgQ);
+}
+
+void* IpmiSEL::start(void* unused)
+{
+ Singleton<IpmiSEL>::instance().execute();
+ return NULL;
+}
+
+/**
+ * @brief Entry point of the sel ipmi thread
+ */
+//@todo: RTC 119832
+void IpmiSEL::execute(void)
+{
+ //Mark as an independent daemon so if it crashes we terminate.
+ task_detach();
+
+ INITSERVICE::registerShutdownEvent(IPMI_COMP_ID, iv_msgQ,
+ IPMISEL::MSG_STATE_SHUTDOWN_SEL,
+ INITSERVICE::IPMI_SEL_PRIORITY);
+ bool l_terminate = false;
+
+ while(!l_terminate)
+ {
+ msg_t* msg = msg_wait(iv_msgQ);
+
+ const IPMISEL::msg_type msg_type =
+ static_cast<IPMISEL::msg_type>(msg->type);
+
+ // Invert the "default" by checking here. This allows the compiler
+ // to warn us if the enum has an unhadled case but still catch
+ // runtime errors where msg_type is set out of bounds.
+ assert(msg_type <= IPMISEL::MSG_LAST_TYPE,
+ "msg_type %d not handled", msg_type);
+
+ switch(msg_type)
+ {
+ case IPMISEL::MSG_SEND_ESEL:
+ IPMISEL::process_esel(msg);
+ //done with msg
+ msg_free(msg);
+ break;
+
+ case IPMISEL::MSG_STATE_SHUTDOWN:
+ IPMI_TRAC(INFO_MRK "ipmisel shutdown event");
+ l_terminate = true;
+
+ //Respond that we are done shutting down.
+ msg_respond(iv_msgQ, msg);
+ break;
+
+ case IPMISEL::MSG_STATE_SHUTDOWN_SEL:
+ IPMI_TRAC(INFO_MRK "ipmisel shutdown message from initservice");
+ l_terminate = true;
+ msg_respond(iv_msgQ, msg);
+ break;
+ }
+ }
+ IPMI_TRAC(EXIT_MRK "message loop");
+ return;
+} // execute
+#endif
+
diff --git a/src/usr/ipmiext/ipmisensor.C b/src/usr/ipmiext/ipmisensor.C
new file mode 100644
index 000000000..5dd428d06
--- /dev/null
+++ b/src/usr/ipmiext/ipmisensor.C
@@ -0,0 +1,1762 @@
+/* IBM_PROLOG_BEGIN_TAG */
+/* This is an automatically generated prolog. */
+/* */
+/* $Source: src/usr/ipmiext/ipmisensor.C $ */
+/* */
+/* OpenPOWER HostBoot Project */
+/* */
+/* Contributors Listed Below - COPYRIGHT 2014,2018 */
+/* [+] Google Inc. */
+/* [+] 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 ipmisensor.C
+ * @brief IPMI sensor manipulation
+ */
+
+#include <ipmi/ipmisensor.H>
+#include <errl/errlentry.H>
+#include <errl/errlmanager.H>
+#include <targeting/common/target.H>
+#include <attributetraits.H>
+#include <targeting/common/utilFilter.H>
+#include <ipmi/ipmi_reasoncodes.H>
+#include <endian.h>
+#include <vpd/pvpdenums.H>
+#include <devicefw/userif.H>
+#include <hdat/hdat.H>
+
+
+extern trace_desc_t * g_trac_ipmi;
+
+namespace SENSOR
+{
+
+ //
+ // Base class for sensor construction. It is expected that this object will
+ // be used as the base for any additional sensors defined.
+ //
+ SensorBase::SensorBase( TARGETING::SENSOR_NAME i_name,
+ const TARGETING::Target * i_target)
+ :iv_name(i_name) ,iv_target(i_target)
+ {
+ // allocate a new message structure to use with our sensors
+ // this will be the payload for the IPMI send/sendrecv sensor message.
+ iv_msg = new setSensorReadingRequest;
+ };
+
+ SensorBase::~SensorBase()
+ {
+ // The memory allocated for the set sensor reading command is deleted
+ // by the IPMI transport layer. Since we are sending messages
+ // asynchronously, the IPMI resource provider deletes the message
+ // and there is nothing to delete here.
+ };
+
+ //
+ // Helper function to process completion codes returned from the BMC.
+ // If the completion code warrants a PEL the function will build and
+ // return an error log with the correct data captured.
+ //
+ errlHndl_t SensorBase::processCompletionCode( IPMI::completion_code i_rc )
+ {
+ errlHndl_t l_err = NULL;
+
+ IPMI::IPMIReasonCode l_reasonCode;
+
+ if( i_rc != IPMI::CC_OK )
+ {
+ // bad rc from the BMC
+ TRACFCOMP(g_trac_ipmi,"completion code 0x%x returned from the BMC"
+ " , creating error log", i_rc);
+
+ switch(i_rc)
+ {
+ case SENSOR::CC_SENSOR_READING_NOT_SETTABLE:
+ {
+ /*@
+ * @errortype ERRL_SEV_UNRECOVERABLE
+ * @moduleid IPMI::MOD_IPMISENSOR
+ * @reasoncode IPMI::RC_SENSOR_NOT_SETTABLE
+ * @userdata1 BMC IPMI Completion code.
+ * @userdata2 bytes [0-1]sensor name
+ * bytes [2-3]sensor number
+ * bytes [4-7]HUID of target.
+ * @devdesc Set sensor reading command failed.
+ */
+ l_reasonCode = IPMI::RC_SENSOR_NOT_SETTABLE;
+ TRACFCOMP(g_trac_ipmi,"Attempt to change sensor reading or"
+ "set/clear status bits that are not settable"
+ " via this command");
+ break;
+ }
+
+ case SENSOR::CC_EVENT_DATA_BYTES_NOT_SETTABLE:
+ {
+ /*@
+ * @errortype ERRL_SEV_UNRECOVERABLE
+ * @moduleid IPMI::MOD_IPMISENSOR
+ * @reasoncode IPMI::RC_EVENT_DATA_NOT_SETTABLE
+ * @userdata1 BMC IPMI Completion code.
+ * @userdata2 bytes[0-3]sensor number
+ * bytes[4-7]HUID of target.
+ * @devdesc Set sensor reading command failed.
+ */
+ l_reasonCode = IPMI::RC_EVENT_DATA_NOT_SETTABLE;
+ TRACFCOMP(g_trac_ipmi,"Attempted to set event data bytes "
+ "but setting event data bytes is not supported for"
+ " this sensor");
+ break;
+ }
+
+ case IPMI::CC_CMDSENSOR:
+ {
+ /*@
+ * @errortype ERRL_SEV_UNRECOVERABLE
+ * @moduleid IPMI::MOD_IPMISENSOR
+ * @reasoncode IPMI::RC_INVALID_SENSOR_CMD
+ * @userdata1 BMC IPMI Completion code.
+ * @userdata2 bytes [0-1]sensor name
+ * bytes [2-3]sensor number
+ * bytes [4-7]HUID of target.
+ * @devdesc Command not valid for this sensor.
+ */
+ l_reasonCode = IPMI::RC_INVALID_SENSOR_CMD;
+ TRACFCOMP(g_trac_ipmi,"Command not valid for this sensor");
+ break;
+ }
+
+ case IPMI::CC_BADSENSOR:
+ {
+ /*@
+ * @errortype ERRL_SEV_UNRECOVERABLE
+ * @moduleid IPMI::MOD_IPMISENSOR
+ * @reasoncode IPMI::RC_SENSOR_NOT_PRESENT
+ * @userdata1 BMC IPMI Completion code.
+ * @userdata2 bytes [0-1]sensor name
+ * bytes [2-3]sensor number
+ * bytes [4-7]HUID of target.
+ * @devdesc Requested sensor is not present.
+ */
+ l_reasonCode = IPMI::RC_SENSOR_NOT_PRESENT;
+ TRACFCOMP(g_trac_ipmi,"Requested sensor not present");
+ break;
+ }
+
+ default:
+ {
+ // lump everything else into a general failure for
+ // now.
+ /*@
+ * @errortype ERRL_SEV_UNRECOVERABLE
+ * @moduleid IPMI::MOD_IPMISENSOR
+ * @reasoncode IPMI::RC_SET_SENSOR_FAILURE
+ * @userdata1 BMC IPMI Completion code.
+ * @userdata2 bytes [0-1]sensor name
+ * bytes [2-3]sensor number
+ * bytes [4-7]HUID of target.
+ * @devdesc Set sensor reading command failed.
+ */
+ TRACFCOMP(g_trac_ipmi,"Set sensor reading command failed");
+ l_reasonCode = IPMI::RC_SET_SENSOR_FAILURE;
+ break;
+ }
+ }
+
+ // shift the sensor number into to bytes 0-3 and then
+ // or in the HUID to bytes 4-7
+ uint32_t sensor_number = getSensorNumber();
+ uint32_t huid = TARGETING::get_huid( iv_target );
+
+ TRACFCOMP(g_trac_ipmi,"Sensor Number: 0x%X, HUID: 0x%X", sensor_number, huid );
+
+ l_err = new ERRORLOG::ErrlEntry(
+ ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ IPMI::MOD_IPMISENSOR,
+ l_reasonCode,
+ i_rc,
+ TWO_UINT32_TO_UINT64(
+ TWO_UINT16_TO_UINT32(iv_name,
+ sensor_number),
+ huid ),
+ true);
+
+ l_err->collectTrace(IPMI_COMP_NAME);
+
+ }
+ return l_err;
+ }
+
+ //
+ // Helper function to send the data to the BMC using the correct interface
+ // protocol
+ //
+ errlHndl_t SensorBase::writeSensorData()
+ {
+
+ errlHndl_t l_err = NULL;
+
+ iv_msg->iv_sensor_number = static_cast<uint8_t>(getSensorNumber());
+
+ if( iv_msg->iv_sensor_number != TARGETING::UTIL::INVALID_IPMI_SENSOR )
+ {
+
+ // iv_msg is deleted by the IPMI resource provider.
+ l_err = sendSetSensorReading( iv_msg);
+
+ if( l_err )
+ {
+ TRACFCOMP(g_trac_ipmi,"error returned from "
+ "sendSetSensorReading() for sensor number 0x%x",
+ getSensorNumber());
+ }
+ }
+ else
+ {
+ TRACFCOMP(g_trac_ipmi,"We were not able to find a sensor number in"
+ " the IPMI_SENSORS attribute for sensor_name=0x%x "
+ "for target with huid=0x%x, skipping call to "
+ "sendSetSensorReading()",
+ iv_name, TARGETING::get_huid( iv_target ));
+
+ /*@
+ * @errortype ERRL_SEV_UNRECOVERABLE
+ * @moduleid IPMI::MOD_IPMISENSOR
+ * @reasoncode IPMI::RC_SENSOR_NOT_FOUND
+ * @userdata1 Returned sensor number.
+ * @userdata2 bytes [0-3]sensor name
+ * bytes [4-7]HUID of target.
+ * @devdesc Requested sensor attribute not found.
+ */
+ l_err = new ERRORLOG::ErrlEntry(
+ ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ IPMI::MOD_IPMISENSOR,
+ IPMI::RC_SENSOR_NOT_FOUND,
+ iv_msg->iv_sensor_number,
+ TWO_UINT32_TO_UINT64( iv_name,
+ TARGETING::get_huid( iv_target ) ),
+ true);
+
+ delete iv_msg;
+ }
+
+ return l_err;
+ };
+
+ //
+ // Helper function to set the bit in the assertion/deassertion mask
+ // associated with the desired sensor specific offset
+ //
+ uint16_t SensorBase::setMask( const uint8_t offset, bool swap )
+ {
+ const uint16_t mask = (0x0001 << offset);
+
+ if(swap)
+ {
+ // need to byte swap the mask (see set sensor reading in spec)
+ return le16toh(mask);
+ }
+ else
+ {
+ return mask;
+ }
+
+ };
+
+ //
+ // Helper function to translate the assertion/deassertion mask into
+ // the correct event offset.
+ //
+ uint8_t SensorBase::getOffset( uint16_t mask )
+ {
+ // $TODO RTC:117872
+ return 0;
+ };
+
+ // read data from the sensor.
+ errlHndl_t SensorBase::readSensorData( getSensorReadingData& o_data)
+ {
+
+ // get sensor reading command only requires one byte of extra data,
+ // which will be the sensor number, the command will return between
+ // 3 and 5 bytes of data.
+ size_t len = 1;
+
+ // need to allocate some memory to hold the sensor number this will be
+ // deleted by the IPMI transport layer
+ uint8_t * l_data = new uint8_t[len];
+
+ l_data[0] = static_cast<uint8_t>(getSensorNumber());
+
+ IPMI::completion_code cc = IPMI::CC_UNKBAD;
+
+ // o_data will hold the response when this returns
+ errlHndl_t l_err = sendrecv(IPMI::get_sensor_reading(), cc, len,
+ l_data);
+
+ // if we didn't get an error back from the BT interface, but see a
+ // bad completion code from the BMC, process the CC to see if we
+ // need to create a PEL - if an error occurs sendrcv will clean up
+ // l_data for us
+ if( l_err == NULL )
+ {
+ l_err = processCompletionCode( cc );
+
+ if( l_err == NULL )
+ {
+ // populate the output structure with the sensor data
+ o_data.completion_code = cc;
+
+ o_data.sensor_status = l_data[0];
+
+ o_data.sensor_reading = l_data[1];
+
+ // bytes 3-5 of the reading are optional and will be dependent
+ // on the value of the sensor status byte.
+ if( !( o_data.sensor_status &
+ ( SENSOR::SENSOR_DISABLED |
+ SENSOR::SENSOR_SCANNING_DISABLED )) ||
+ ( o_data.sensor_status & SENSOR::READING_UNAVAILABLE ))
+ {
+ // sensor reading is available
+ o_data.event_status =
+ (( (uint16_t) l_data[3]) << 8 | l_data[2] );
+
+ // spec indicates that the high order bit should be
+ // ignored on a read, so lets mask it off now.
+ o_data.event_status &= 0x7FFF;
+ }
+ else
+ {
+ uint32_t l_sensorNumber = getSensorNumber();
+
+ TRACFCOMP(g_trac_ipmi,"Sensor reading not available: status = 0x%x",o_data.sensor_status);
+ TRACFCOMP(g_trac_ipmi,"sensor number 0x%x, huid 0x%x",l_sensorNumber ,get_huid(iv_target));
+
+ // something happened log an error to indicate the request
+ // failed
+ /*@
+ * @errortype ERRL_SEV_UNRECOVERABLE
+ * @moduleid IPMI::MOD_IPMISENSOR
+ * @reasoncode IPMI::RC_SENSOR_READING_NOT_AVAIL
+ * @userdata1 sensor status indicating reason for
+ * reading not available
+ * @userdata2[0:31] sensor number
+ * @userdata2[32:64] HUID of target
+ *
+ * @devdesc Set sensor reading command failed.
+ * @custdesc Request to get sensor reading
+ * IPMI completion code can be seen
+ * in userdata1 field of the log.
+ */
+ l_err = new ERRORLOG::ErrlEntry(
+ ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ IPMI::MOD_IPMISENSOR,
+ IPMI::RC_SENSOR_READING_NOT_AVAIL,
+ o_data.sensor_status,
+ TWO_UINT32_TO_UINT64( l_sensorNumber,
+ TARGETING::get_huid(iv_target)), true);
+
+ l_err->collectTrace(IPMI_COMP_NAME);
+
+ }
+
+ }
+
+ delete[] l_data;
+ }
+ return l_err;
+ };
+
+ //
+ // Asynchronously send a set sensor reading command to the BMC.
+ //
+ errlHndl_t SensorBase::sendSetSensorReading(
+ setSensorReadingRequest * i_data)
+ {
+
+ size_t l_len = sizeof( setSensorReadingRequest );
+
+ // i_data will hold the response when this returns
+ errlHndl_t l_err = send(IPMI::set_sensor_reading(),
+ l_len, (uint8_t *)i_data);
+
+ return l_err;
+ }
+
+ // return the sensor type and event reading data
+ errlHndl_t SensorBase::getSensorType(uint32_t i_sensorNumber,
+ uint8_t &o_sensorType,
+ uint8_t &o_eventReadingType )
+ {
+
+ size_t len = 1;
+
+ o_sensorType = INVALID_TYPE;
+ o_eventReadingType = INVALID_TYPE;
+
+ // need to allocate some memory to hold the sensor number this will be
+ // deleted by the IPMI transport layer
+ uint8_t *l_data = new uint8_t[len];
+
+ l_data[0] = i_sensorNumber;
+
+ IPMI::completion_code cc = IPMI::CC_UNKBAD;
+
+ // l_data will hold the response when this returns
+ errlHndl_t l_err = sendrecv(IPMI::get_sensor_type(), cc, len,
+ l_data);
+
+ // if we didn't get an error back from the BT interface,
+ // process the CC to see if we need to create a PEL
+ if( l_err == NULL )
+ {
+ // check the completion code
+ if( cc!= IPMI::CC_OK )
+ {
+ TRACFCOMP(g_trac_ipmi,"bad completion code from BMC=0x%x",cc);
+
+ /*@
+ * @errortype ERRL_SEV_INFORMATIONAL
+ * @moduleid IPMI::MOD_IPMISENSOR
+ * @reasoncode IPMI::RC_GET_SENSOR_TYPE_CMD_FAILED
+ * @userdata1 BMC IPMI Completion code.
+ * @devdesc Request to get sensor type form the bmc
+ * failed.
+ */
+ l_err = new ERRORLOG::ErrlEntry(
+ ERRORLOG::ERRL_SEV_INFORMATIONAL,
+ IPMI::MOD_IPMISENSOR,
+ IPMI::RC_GET_SENSOR_TYPE_CMD_FAILED,
+ static_cast<uint64_t>(cc),0, true);
+
+ l_err->collectTrace(IPMI_COMP_NAME);
+
+
+ }
+ else
+ {
+ // grab the type and reading code to pass back to the caller
+ o_sensorType = l_data[0];
+
+ // high order bit is reserved
+ o_eventReadingType = ( 0x7f & l_data[1]);
+
+ }
+ delete[] l_data;
+ }
+ return l_err;
+ };
+
+
+ /**
+ * @brief Returns major type of input sensor name
+ *
+ * @param[in] i_sensorName
+ * Name of the sensor
+ *
+ * @return Major type of input sensor name
+ */
+ static inline uint16_t getMajorType(
+ const uint16_t i_sensorName)
+ {
+ return (i_sensorName & SENSOR_NAME_MAJOR_MASK);
+ }
+
+ /**
+ * @brief Returns minor type of input sensor name
+ *
+ * @param[in] i_sensorName
+ * Name of the sensor
+ *
+ * @return Minor type of input sensor name
+ */
+ static inline uint16_t getMinorType(
+ const uint16_t i_sensorName)
+ {
+ return (i_sensorName & SENSOR_NAME_MINOR_MASK);
+ }
+
+ /**
+ * @brief Returns whether the supplied sensor record's major type is less
+ * than the major type of the supplied sensor name
+ *
+ * @param[in] i_sensorRecord
+ * Sensor record to compare
+ *
+ * @param[in] i_sensorName
+ * Name of the sensor to compare
+ *
+ * @retval true Major type of sensor record is less than major type of
+ * sensor name
+ * @retval false Major type of sensor record is not less than major type of
+ * sensor name
+ */
+ static inline bool compare_major(
+ const uint16_t (&i_sensorRecord)[2],
+ const uint16_t i_sensorName)
+ {
+ return getMajorType(i_sensorRecord[0]) < getMajorType(i_sensorName);
+ }
+
+ /**
+ * @brief Returns whether the supplied sensor record's major type equals
+ * the major type of the supplied sensor name
+ *
+ * @param[in] i_sensorRecord
+ * Sensor record to compare
+ *
+ * @param[in] i_sensorName
+ * Name of the sensor to compare
+ *
+ * @retval true Major type of sensor record equals major type of
+ * sensor name
+ * @retval false Major type of sensor record does not equal major type of
+ * sensor name
+ */
+ static inline bool equals_major(
+ const uint16_t (&i_sensorRecord)[2],
+ const uint16_t i_sensorName)
+ {
+ return getMajorType(i_sensorRecord[0]) == getMajorType(i_sensorName);
+ }
+
+ ///
+ // FirmwareProgressSensor constructor - uses system target
+ //
+ FirmwareProgressSensor::FirmwareProgressSensor( )
+ :SensorBase(TARGETING::SENSOR_NAME_FW_BOOT_PROGRESS, NULL)
+ {
+ // message buffer created and initialized in base object.
+
+ // assert the system firmware progress offset.
+ iv_msg->iv_assertion_mask = setMask( SYSTEM_FIRMWARE_PROGRESS );
+ };
+
+ //
+ // FirmwareProgressSensor destructor
+ //
+ FirmwareProgressSensor::~FirmwareProgressSensor( )
+ {
+
+ };
+
+ //
+ // setBootProgressPhase - update the boot progress sensor of the BMC
+ //
+ errlHndl_t FirmwareProgressSensor::setBootProgressPhase(
+ INITSERVICE::firmwareProgressPhase phase )
+ {
+ // event data 2 holds the progress info
+ iv_msg->iv_event_data[1] = phase;
+
+ return writeSensorData();
+ };
+
+ //
+ // RebootCountSensor constructor - uses system target
+ //
+ RebootCountSensor::RebootCountSensor()
+ :SensorBase(TARGETING::SENSOR_NAME_REBOOT_COUNT, NULL)
+ {
+ // message buffer created and initialized in base object.
+
+ }
+
+ //
+ // RebootCountSensor destructor
+ //
+ RebootCountSensor::~RebootCountSensor(){};
+
+ //
+ // setRebootCount - send a new value for the reboot count to the BMC.
+ //
+ errlHndl_t RebootCountSensor::setRebootCount( uint16_t i_count )
+ {
+ // adjust the operation to overwrite the sensor reading
+ // to the value we send.
+ iv_msg->iv_operation = SET_SENSOR_VALUE_OPERATION;
+
+ // the Reboot_count sensor is defined as a discrete sensor
+ // but the assertion bytes are being used to transfer the count
+ // to the bmc, will need to byte swap the data
+ iv_msg->iv_assertion_mask = le16toh(i_count);
+
+ return writeSensorData();
+
+ }
+
+ //
+ // getRebootCount - get the reboot count from the BMC
+ //
+ errlHndl_t RebootCountSensor::getRebootCount( uint16_t &o_rebootCount )
+ {
+
+ // the Reboot_count sensor is defined as a discrete sensor
+ // but the assertion bytes are being used to transfer the count
+ // from the BMC
+ getSensorReadingData l_data;
+
+ errlHndl_t l_err = readSensorData( l_data );
+
+ if( l_err == NULL )
+ {
+ // this value is already byteswapped
+ o_rebootCount = l_data.event_status;
+ }
+ return l_err;
+
+ }
+
+ //
+ // RebootControlSensor constructor - uses system target
+ //
+ RebootControlSensor::RebootControlSensor()
+ :SensorBase(TARGETING::SENSOR_NAME_HOST_AUTO_REBOOT_CONTROL, NULL)
+ {
+ // message buffer created and initialized in base object.
+
+ }
+
+ //
+ // RebootCountSensor destructor
+ //
+ RebootControlSensor::~RebootControlSensor(){};
+
+ //
+ // setRebootControl - turn reboots on or off to the BMC
+ //
+ errlHndl_t RebootControlSensor::setRebootControl(
+ autoRebootSetting i_setting )
+ {
+ // adjust the operation to overwrite the sensor reading
+ // to the value we send.
+ iv_msg->iv_operation = SET_SENSOR_VALUE_OPERATION;
+
+ // the Reboot Control Sensor is defined as a discrete sensor
+ // but the assertion bytes are being used to transfer the state
+ iv_msg->iv_assertion_mask = le16toh(i_setting);
+
+ TRACFCOMP(g_trac_ipmi,"RebootControlSensor::setRebootControl(%d)",
+ i_setting);
+
+ return writeSensorData();
+ }
+
+ //
+ // getRebootCount - get the reboot setting from the BMC
+ //
+ errlHndl_t RebootControlSensor::getRebootControl(
+ autoRebootSetting &o_setting )
+ {
+ // the Reboot control sensor is defined as a discrete sensor
+ // DISABLE_REBOOT - keep current state (no reboot)
+ // ENABLE_REBOOT - Allow analysis of FIRDATA on XSTOP
+ getSensorReadingData l_data;
+
+ errlHndl_t l_err = readSensorData( l_data );
+
+ if( l_err == NULL )
+ {
+ // Check that this value is a valid enum value
+ if ( l_data.event_status == ENABLE_REBOOTS ||
+ l_data.event_status == DISABLE_REBOOTS )
+ {
+ o_setting = static_cast<autoRebootSetting>(l_data.event_status);
+ }
+ else
+ {
+ TRACFCOMP(g_trac_ipmi,"Unknown reboot control setting: %d",
+ l_data.event_status);
+
+ /*@
+ * @errortype ERRL_SEV_UNRECOVERABLE
+ * @moduleid IPMI::MOD_IPMISENSOR_REBOOTCNTRL
+ * @reasoncode IPMI::RC_INVALID_SENSOR_SETTING
+ * @userdata1 Invalid reboot control setting
+ * @userdata2 <unused>
+ * @devdesc The sensor returned an invalid setting
+ * @custdesc Unable to find a valid sensor setting.
+ */
+ l_err = new ERRORLOG::ErrlEntry(
+ ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ IPMI::MOD_IPMISENSOR_REBOOTCNTRL,
+ IPMI::RC_INVALID_SENSOR_SETTING,
+ l_data.event_status,
+ 0,
+ false);
+ }
+ }
+ return l_err;
+ }
+
+
+ //
+ // StatusSensor constructor - uses system DIMM/CORE/PROC target
+ //
+ StatusSensor::StatusSensor( TARGETING::ConstTargetHandle_t i_target )
+ :SensorBase(TARGETING::SENSOR_NAME_STATE, i_target)
+ {
+ iv_functionalOffset = PROC_DISABLED;
+ iv_presentOffset = PROC_PRESENCE_DETECTED;
+
+ switch ( i_target->getAttr<TARGETING::ATTR_TYPE>() )
+ {
+ case TARGETING::TYPE_DIMM:
+ {
+ iv_functionalOffset = MEMORY_DEVICE_DISABLED;
+ iv_presentOffset = MEM_DEVICE_PRESENCE_DETECTED;
+ iv_name = TARGETING::SENSOR_NAME_DIMM_STATE;
+ break;
+ }
+ case TARGETING::TYPE_MEMBUF:
+ {
+ iv_functionalOffset = MEMORY_DEVICE_DISABLED;
+ iv_presentOffset = MEM_DEVICE_PRESENCE_DETECTED;
+ iv_name = TARGETING::SENSOR_NAME_MEMBUF_STATE;
+ break;
+ }
+ case TARGETING::TYPE_PROC:
+ iv_name = TARGETING::SENSOR_NAME_PROC_STATE;
+ break;
+
+ case TARGETING::TYPE_CORE:
+ iv_name = TARGETING::SENSOR_NAME_CORE_STATE;
+ break;
+
+ default:
+ TRACFCOMP(g_trac_ipmi,"INF>>No status sensor associated with target type 0x%x",
+ i_target->getAttr<TARGETING::ATTR_TYPE>());
+ iv_functionalOffset = INVALID_OFFSET;
+ iv_presentOffset = INVALID_OFFSET;
+ break;
+ }
+
+ };
+
+ //
+ // StatusSensor destructor
+ //
+ //
+ StatusSensor::~StatusSensor()
+ {};
+
+
+ // Convert the input status to the correct sensor offset value, then
+ // send the message to the BMC to update the event status for this sensor.
+ errlHndl_t StatusSensor::setStatus( statusEnum i_state )
+ {
+
+ errlHndl_t l_err = NULL;
+
+ if( iv_functionalOffset != INVALID_OFFSET
+ && iv_presentOffset != INVALID_OFFSET )
+ {
+ uint16_t func_mask = setMask( iv_functionalOffset );
+ uint16_t pres_mask = setMask( iv_presentOffset );
+
+ switch ( i_state )
+ {
+ case NOT_PRESENT:
+ // turn off the present bit
+ iv_msg->iv_deassertion_mask = pres_mask;
+
+ // turn off the disabled bit in case it was on
+ iv_msg->iv_deassertion_mask |= func_mask;
+ break;
+
+ case PRESENT:
+ // turn on the present bit
+ iv_msg->iv_assertion_mask = pres_mask;
+ break;
+
+ case FUNCTIONAL:
+ // turn off the disabled bit
+ iv_msg->iv_deassertion_mask = func_mask;
+ break;
+
+ case PRESENT_FUNCTIONAL:
+ // assert the present bit
+ iv_msg->iv_assertion_mask = pres_mask;
+ // turn off the disabled bit
+ iv_msg->iv_deassertion_mask = func_mask;
+ break;
+
+ case PRESENT_NONFUNCTIONAL:
+ // assert the present bit
+ iv_msg->iv_assertion_mask = pres_mask;
+ // assert the disabled bit
+ iv_msg->iv_assertion_mask |= func_mask;
+ break;
+
+ case NON_FUNCTIONAL:
+ // assert the disabled bit
+ iv_msg->iv_assertion_mask = func_mask;
+ break;
+
+ default:
+ // mark as not present
+ iv_msg->iv_deassertion_mask = pres_mask;
+ iv_msg->iv_assertion_mask = func_mask;
+ break;
+ }
+
+ l_err = writeSensorData();
+ }
+ return l_err;
+
+ };
+
+ //**************************************************************************
+ // GpuSensor constructor
+ //**************************************************************************
+ GpuSensor::GpuSensor(TARGETING::SENSOR_NAME i_name, uint16_t i_num,
+ TARGETING::ConstTargetHandle_t i_target)
+ : StatusSensor(i_target)
+
+ {
+ /* Note: StatusSensor sets these for processor target */
+ //iv_functionalOffset = PROC_DISABLED;
+ //iv_presentOffset = PROC_PRESENCE_DETECTED;
+
+ // Override iv_name set by parent constructor
+ iv_name = i_name;
+
+ // 3 numbers possible (1 per GPU) for each name, so save which one
+ iv_sensorNumber = i_num;
+ };
+
+ //**************************************************************************
+ // GpuSensor destructor
+ //**************************************************************************
+ GpuSensor::~GpuSensor()
+ {
+ }
+
+ //**************************************************************************
+ // FaultSensor constructor
+ //**************************************************************************
+
+ FaultSensor::FaultSensor(
+ TARGETING::ConstTargetHandle_t i_pTarget)
+ : SensorBase(TARGETING::SENSOR_NAME_FAULT, i_pTarget)
+ {
+ }
+
+ //**************************************************************************
+ // FaultSensor constructor for associated targets
+ //**************************************************************************
+
+ FaultSensor::FaultSensor(
+ TARGETING::ConstTargetHandle_t i_pTarget,
+ const TARGETING::ENTITY_ID i_associatedType)
+ : SensorBase(
+ static_cast<TARGETING::SENSOR_NAME>(
+ TARGETING::SENSOR_NAME_FAULT | i_associatedType),
+ i_pTarget)
+ {
+ }
+
+ //**************************************************************************
+ // FaultSensor destructor
+ //**************************************************************************
+
+ FaultSensor::~FaultSensor()
+ {
+ }
+
+ //**************************************************************************
+ // FaultSensor::setStatus
+ //**************************************************************************
+
+ errlHndl_t FaultSensor::setStatus(
+ const FAULT_STATE i_faultState)
+ {
+ errlHndl_t pError = NULL;
+
+ switch(i_faultState)
+ {
+ case FAULT_STATE_ASSERTED:
+ iv_msg->iv_assertion_mask = setMask(FAULT_ASSERTED_OFFSET);
+ break;
+ case FAULT_STATE_DEASSERTED:
+ iv_msg->iv_deassertion_mask = setMask(FAULT_ASSERTED_OFFSET);
+ break;
+ default:
+ assert(0,"Caller passed unsupported fault state of 0x%X",
+ i_faultState);
+ }
+
+ pError = writeSensorData();
+ if(pError)
+ {
+ TRACFCOMP(g_trac_ipmi, ERR_MRK " "
+ "Failed to write sensor data for sensor name 0x%X",
+ iv_name);
+ }
+
+ return pError;
+ }
+
+ //
+ // OCC Active Sensor - uses occ target
+ //
+ //
+ OCCActiveSensor::OCCActiveSensor( TARGETING::Target * i_pTarget )
+ :SensorBase(TARGETING::SENSOR_NAME_OCC_ACTIVE,
+ (TARGETING::ConstTargetHandle_t) i_pTarget )
+ {
+ };
+
+ //
+ // OCCActiveSensor destructor
+ //
+ //
+ OCCActiveSensor::~OCCActiveSensor(){};
+
+ // Convert the input status to the correct sensor offset value, then
+ // send the message to the BMC to update the event status for this sensor.
+ errlHndl_t OCCActiveSensor::setState( OccStateEnum i_state )
+ {
+ errlHndl_t l_err = NULL;
+
+ // assert the specified state
+ iv_msg->iv_assertion_mask = setMask(i_state);
+
+ // there are two offsets used with this sensor, when
+ // asserting one, we need to deassert the other as only
+ // one state is valid at any given time.
+ OccStateEnum other_state =
+ (i_state == OCC_ACTIVE) ? OCC_NOT_ACTIVE : OCC_ACTIVE;
+
+ iv_msg->iv_deassertion_mask = setMask( other_state );
+
+ l_err = writeSensorData();
+
+ return l_err;
+
+ };
+
+ // send the message to the BMC to read the event status for this sensor,
+ // will return true if the "disabled" state of the sensor is not
+ // asserted
+ bool OCCActiveSensor::isActive( )
+ {
+ getSensorReadingData l_data;
+
+ bool is_active = false;
+
+ // set the mask, but dont swap the bytes since we are using it locally
+ // not passing it in the set sensor cmd
+ uint16_t mask = setMask( OCC_ACTIVE, false );
+
+ errlHndl_t l_err = readSensorData( l_data );
+
+ if( l_err == NULL )
+ {
+ // check if "disabled" offset has been asserted -
+ // this would indicate that the OCC was not yet enabled
+ if( l_data.event_status & mask )
+ {
+ is_active = true;
+ }
+ }
+ else
+ {
+ // commit the error and return "not active" by default
+ errlCommit( l_err, IPMI_COMP_ID );
+ }
+
+ return is_active;
+ }
+
+
+ //
+ // HostStausSensor constructor - uses system target
+ //
+ //
+ HostStatusSensor::HostStatusSensor()
+ :SensorBase(TARGETING::SENSOR_NAME_HOST_STATUS, NULL)
+ {
+
+ };
+
+ //
+ // HostStatusSensor destructor
+ //
+ //
+ HostStatusSensor::~HostStatusSensor(){};
+
+ //
+ // updateHostStaus - update the BMC HostStatus sensor with the passed in
+ // value.
+ //
+ //
+ errlHndl_t HostStatusSensor::updateHostStatus( hostStatus status )
+ {
+ iv_msg->iv_operation = SET_SENSOR_VALUE_OPERATION;
+ iv_msg->iv_assertion_mask = setMask((uint8_t)status);
+
+ return writeSensorData();
+ };
+
+ //
+ // Used to update the sensor status for a specific set of target types
+ // currently supported types are TYPE_DIMM, TYPE_MEMBUF, TYPE_CORE,
+ // TYPE_PROC. These are virtual sensors where Hostboot updates the
+ // present and functional states and the BMC maintains the sensor.
+ //
+ void updateBMCSensorStatus(TARGETING::TYPE i_type)
+ {
+
+ TARGETING::TargetHandleList l_tList;
+
+ // get all targets of the passed in type, functional or not
+ switch( i_type )
+ {
+
+ case TARGETING::TYPE_DIMM:
+ getAllLogicalCards( l_tList, TARGETING::TYPE_DIMM, false );
+ break;
+
+ case TARGETING::TYPE_MEMBUF:
+ getAllChips( l_tList, TARGETING::TYPE_MEMBUF, false );
+ break;
+
+ case TARGETING::TYPE_PROC:
+ getAllChips( l_tList, TARGETING::TYPE_PROC, false );
+ break;
+
+ case TARGETING::TYPE_CORE:
+ getAllChiplets( l_tList, TARGETING::TYPE_CORE, false);
+ break;
+
+ default:
+ assert(0, "invalid target type for BMC update");
+
+ }
+
+ // have a list of targets now set the status sensor on the BMC for each
+ // one.
+ for(TARGETING::TargetHandleList::const_iterator pTargetIt =
+ l_tList.begin();
+ pTargetIt != l_tList.end();
+ ++pTargetIt )
+ {
+
+ StatusSensor::statusEnum l_status
+ = StatusSensor::PRESENT_FUNCTIONAL;
+
+ // create a status sensor for our needs
+ StatusSensor l_sensor((*pTargetIt));
+
+ TARGETING::HwasState l_state =
+ (*pTargetIt)->getAttr<TARGETING::ATTR_HWAS_STATE>();
+
+ if( l_state.present == true )
+ {
+ if( l_state.functional == false )
+ {
+ l_status = StatusSensor::PRESENT_NONFUNCTIONAL;
+ }
+ }
+ else
+ {
+ l_status = StatusSensor::NOT_PRESENT;
+ }
+
+ // send the status to the BMC
+ errlHndl_t l_err = l_sensor.setStatus( l_status );
+
+ // commit the error and move to the next target
+ if( l_err )
+ {
+ errlCommit( l_err, IPMI_COMP_ID );
+ }
+ }
+
+ }
+
+ void updateBMCFaultSensorStatus(void)
+ {
+ TARGETING::ATTR_IPMI_SENSORS_type noSensors = {{0}};
+
+ // No sensor attribute is all 0's; therefore if a sensor attribute is
+ // found and is not all zeros (using the predicate value inversion
+ // feature) then the sensor attribute has potential
+ // sensors to iterate through
+ TARGETING::PredicateAttrVal<TARGETING::ATTR_IPMI_SENSORS>
+ hasSensors(noSensors,true);
+ TARGETING::TargetRangeFilter targetsWithSensorsItr(
+ TARGETING::targetService().begin(),
+ TARGETING::targetService().end(),
+ &hasSensors);
+ for (; targetsWithSensorsItr; ++targetsWithSensorsItr)
+ {
+ // Cache the target for ease of reading/usage
+ TARGETING::TargetHandle_t pTarget = *targetsWithSensorsItr;
+
+ TARGETING::ATTR_IPMI_SENSORS_type sensors = {{0}};
+ assert(pTarget->tryGetAttr<TARGETING::ATTR_IPMI_SENSORS>(sensors));
+
+ // Derive number of sensor records by dividing attribute size by
+ // size of each sensor record
+ uint16_t sensorRows = (sizeof(sensors)/sizeof(sensors[0]));
+
+ // Ceate an iterator pointing to the first element of the array
+ uint16_t (*begin)[2] = &sensors[0];
+
+ // Using the number entries as the index into the array will set the
+ // end iterator to the correct position (one entry past the last
+ // element of the array)
+ uint16_t (*end)[2] = &sensors[sensorRows];
+
+ // Locate the first record that could possibly match the criteria
+ uint16_t (*ptr)[2] =
+ std::lower_bound(begin, end,
+ TARGETING::SENSOR_NAME_FAULT, &compare_major);
+
+ // Process any match, and all remaining matches after that, until
+ // there is no additional match. Here we are matching the major
+ // sensor type only
+ while( (ptr != end)
+ && ( getMajorType((*ptr)[0])
+ == TARGETING::SENSOR_NAME_FAULT ))
+ {
+ TRACFCOMP(g_trac_ipmi, INFO_MRK " "
+ "Found fault sensor name 0x%X and ID 0x%X for HUID 0x%X",
+ (*ptr)[0], (*ptr)[1], TARGETING::get_huid(pTarget));
+
+ FaultSensor faultSensor(pTarget,
+ static_cast<TARGETING::ENTITY_ID>(
+ getMinorType((*ptr)[0])));
+
+ errlHndl_t pError = faultSensor.setStatus(
+ FaultSensor::FAULT_STATE_DEASSERTED);
+ if(pError)
+ {
+ TRACFCOMP(g_trac_ipmi, ERR_MRK " "
+ "Failed setting fault sensor name 0x%X and ID 0x%X for "
+ "HUID 0x%X",
+ (*ptr)[0], (*ptr)[1], TARGETING::get_huid(pTarget));
+ errlCommit(pError, IPMI_COMP_ID);
+ }
+
+ ++ptr;
+ }
+ }
+ }
+
+ void updateBMCSensorStatus()
+ {
+
+ // send status of all MEMBUF targets
+ updateBMCSensorStatus(TARGETING::TYPE_MEMBUF);
+
+ // send status of all DIMM targets
+ updateBMCSensorStatus(TARGETING::TYPE_DIMM);
+
+ // send status for all PROC targets
+ updateBMCSensorStatus(TARGETING::TYPE_PROC);
+
+ updateBMCSensorStatus(TARGETING::TYPE_CORE);
+
+ // Send status for all simple fault sensors in the system
+ updateBMCFaultSensorStatus();
+ };
+
+ // returns a sensor number for the FRU based on input target type
+ // there are currently 4 frus defined system, backplane, DIMM, PROC
+ //
+ uint32_t getFaultSensorNumber( TARGETING::ConstTargetHandle_t i_pTarget )
+ {
+ TRACDCOMP(g_trac_ipmi,">>getFaultSensorNumber()");
+
+ TARGETING::TYPE l_type = i_pTarget->getAttr<TARGETING::ATTR_TYPE>();
+
+ uint32_t l_sensor_number = TARGETING::UTIL::INVALID_IPMI_SENSOR;
+
+ switch( l_type )
+ {
+
+ case TARGETING::TYPE_SYS:
+ {
+ TRACDCOMP(g_trac_ipmi, "returning the \"System Event\" sensor\n");
+ l_sensor_number = TARGETING::UTIL::getSensorNumber(
+ i_pTarget,
+ TARGETING::SENSOR_NAME_SYSTEM_EVENT );
+
+ TRACDCOMP(g_trac_ipmi,"Sensor Number = 0x%x", l_sensor_number);
+ break;
+ }
+
+ case TARGETING::TYPE_NODE:
+ {
+ TRACDCOMP(g_trac_ipmi, "returning the \"BACKPLANE_FAULT\" sensor\n");
+ l_sensor_number = TARGETING::UTIL::getSensorNumber(
+ i_pTarget,
+ TARGETING::SENSOR_NAME_BACKPLANE_FAULT );
+
+ TRACDCOMP(g_trac_ipmi,"Sensor Number = 0x%x", l_sensor_number);
+ break;
+ }
+
+ // these targets have specific status sensors
+ case TARGETING::TYPE_DIMM:
+ case TARGETING::TYPE_MEMBUF:
+ case TARGETING::TYPE_PROC:
+ {
+ l_sensor_number =
+ StatusSensor(i_pTarget).getSensorNumber();
+
+ TRACDCOMP(g_trac_ipmi,"Sensor Number = 0x%x", l_sensor_number);
+ break;
+ }
+
+ default:
+ {
+
+ TARGETING::EntityPath l_targetPath =
+ i_pTarget->getAttr<TARGETING::ATTR_PHYS_PATH>();
+
+ // chop off the last part and go again.
+ l_targetPath.removeLast();
+
+ TARGETING::TargetHandle_t l_target = NULL;
+ l_target =
+ TARGETING::targetService().toTarget(l_targetPath);
+
+ l_sensor_number = getFaultSensorNumber(
+ static_cast<TARGETING::ConstTargetHandle_t>(l_target));
+
+ break;
+ }
+ }
+
+ TRACDCOMP(g_trac_ipmi,"<<getFaultSensorNumber() returning sensor number %#x", l_sensor_number);
+
+ return l_sensor_number;
+ }
+
+ // interface to retrieve the APSS channel sensor numbers.
+ errlHndl_t getAPSSChannelSensorNumbers(
+ const uint32_t (* &o_sensor_numbers)[16])
+ {
+
+ TARGETING::TargetHandle_t l_sys;
+
+ // get the "system error" sensor number associated with the
+ // system target.
+
+ TARGETING::targetService().getTopLevelTarget(l_sys);
+
+ static TARGETING::ATTR_ADC_CHANNEL_SENSOR_NUMBERS_type
+ apss_sensors;
+
+ if( l_sys->tryGetAttr<TARGETING::
+ ATTR_ADC_CHANNEL_SENSOR_NUMBERS>(apss_sensors) )
+ {
+ o_sensor_numbers = &apss_sensors;
+ }
+ else
+ {
+ // need that attribute or things dont work
+ assert(0,"Missing ADC_CHANNEL_SENSOR_NUMBERS attribute");
+ }
+
+ return NULL;
+ }
+
+ uint16_t getSensorOffsets( TARGETING::SENSOR_NAME i_name,
+ sensorReadingType &o_readType )
+ {
+
+ uint16_t offsets = 0;
+
+ // most of our sensors use generic sensor specific reading types
+ // so use that as the default value
+ o_readType = SENSOR_SPECIFIC;
+
+ // sensor type is lower byte of sensor name, if we dont match
+ // based on name, then try the sensor type
+ uint16_t t = ( i_name >> 8 ) & 0x00FF;
+
+ switch( i_name )
+ {
+ case TARGETING::SENSOR_NAME_FW_BOOT_PROGRESS:
+ {
+ offsets = ( 1 << SYSTEM_FIRMWARE_PROGRESS );
+ break;
+ }
+ case TARGETING::SENSOR_NAME_OCC_ACTIVE:
+ {
+ offsets = ( 1 << DEVICE_DISABLED ) |
+ ( 1 << DEVICE_ENABLED );
+ o_readType = DIGITAL_ENABLE_DISABLE;
+ break;
+ }
+ case TARGETING::SENSOR_NAME_HOST_STATUS:
+ {
+ offsets = ( 1 << S0_G0_WORKING ) |
+ ( 1 << G5_SOFT_OFF ) |
+ ( 1 << LEGACY_ON );
+ break;
+ }
+ case TARGETING::SENSOR_NAME_PCI_ACTIVE:
+ case TARGETING::SENSOR_NAME_OS_BOOT:
+ {
+ // default all offsets enabled
+ offsets = 0x7FFF;
+ break;
+ }
+
+ default:
+ {
+ // try sensor type
+ switch (t)
+ {
+ case TARGETING::SENSOR_TYPE_FAULT:
+ offsets = ( 1 << ASSERTED );
+ o_readType = DIGITAL_ASSERT_DEASSERT;
+ break;
+
+ case TARGETING::SENSOR_TYPE_PROCESSOR:
+ offsets = ( 1 << PROC_PRESENCE_DETECTED ) |
+ ( 1 << PROC_DISABLED ) |
+ ( 1 << IERR );
+ break;
+
+ case TARGETING::SENSOR_TYPE_MEMORY:
+ offsets = ( 1 << MEMORY_DEVICE_DISABLED ) |
+ ( 1 << MEM_DEVICE_PRESENCE_DETECTED );
+ break;
+ default:
+ offsets = 0;
+ o_readType = THRESHOLD;
+ break;
+ }
+
+ }
+ }
+
+ return offsets;
+ }
+
+ uint8_t getBackPlaneFaultSensor()
+ {
+ TARGETING::TargetHandle_t sys = NULL;
+ TARGETING::TargetHandleList nodes;
+ TARGETING::targetService().getTopLevelTarget(sys);
+ assert(sys != NULL);
+ getChildAffinityTargets(nodes, sys, TARGETING::CLASS_ENC,
+ TARGETING::TYPE_NODE);
+ assert(!nodes.empty());
+
+ //Backplane sensor ID
+ return TARGETING::UTIL::getSensorNumber(nodes[0],
+ TARGETING::SENSOR_NAME_BACKPLANE_FAULT);
+ }
+
+ /**
+ * @brief All sensors returned cfgID bit
+ */
+ static const uint16_t NVCFG_ALL_SENSORS_RETURNED = 0xFFFF;
+
+ /**
+ * @brief Helper function to getGpuSensors()
+ * NV keyword tells us what backplane is installed,
+ * thus what GPUs are supported
+ *
+ * @param[out] returns NV keyword in bitwise format
+ *
+ * @return Error log handle if a deviceRead fails
+ */
+ errlHndl_t getNVCfgIDBit(uint16_t & o_cfgID_bitwise)
+ {
+ static uint16_t L_NV_bits = 0;
+ errlHndl_t l_err = nullptr;
+
+ if (L_NV_bits == 0)
+ {
+ // grab system enclosure node
+ TARGETING::TargetHandle_t l_sys = NULL;
+ TARGETING::TargetHandleList l_nodeTargets;
+ TARGETING::targetService().getTopLevelTarget(l_sys);
+ assert(l_sys != NULL);
+ getChildAffinityTargets(l_nodeTargets, l_sys, TARGETING::CLASS_ENC,
+ TARGETING::TYPE_NODE);
+ assert(!l_nodeTargets.empty());
+
+ // get keyword size first
+ PVPD::pvpdRecord l_Record = PVPD::VNDR;
+ PVPD::pvpdKeyword l_KeyWord = PVPD::NV;
+ size_t l_nvKwdSize = 0;
+ l_err = deviceRead(l_nodeTargets[0],NULL,l_nvKwdSize,
+ DEVICE_PVPD_ADDRESS(l_Record,l_KeyWord));
+ if (!l_err)
+ {
+ if (l_nvKwdSize == sizeof(HDAT::hdatNVKwdStruct_t))
+ {
+ uint8_t l_kwd[l_nvKwdSize] = {0};
+ // now read the keyword
+ l_err = deviceRead(l_nodeTargets[0],l_kwd,l_nvKwdSize,
+ DEVICE_PVPD_ADDRESS(l_Record,l_KeyWord));
+ if (!l_err)
+ {
+ HDAT::hdatNVKwdStruct_t * NVptr =
+ reinterpret_cast<HDAT::hdatNVKwdStruct_t*>(l_kwd);
+
+ // Valid NV keyword config has NV00 as magic header
+ if ( !memcmp((char*)&(NVptr->magic),"NV00", 4) )
+ {
+ uint8_t cfgID = NVptr->config;
+ if (cfgID < 16) // maximum setting (bits 0-15)
+ {
+ L_NV_bits = 0x0001 << cfgID;
+ }
+ else
+ {
+ TRACFCOMP(g_trac_ipmi,"getNVCfgIDBit(): Invalid NV config 0x%02X", cfgID);
+ }
+ }
+ else
+ {
+ TRACFCOMP(g_trac_ipmi, "Invalid NV magic header: 0x%.8X", NVptr->magic);
+ TRACFBIN(g_trac_ipmi, "NV KEYWORD", l_kwd, l_nvKwdSize);
+ }
+ }
+ else
+ {
+ TRACFCOMP(g_trac_ipmi,ERR_MRK"%.8X Error getting VNDR record data",l_err->eid());
+ }
+ }
+ else
+ {
+ TRACFCOMP(g_trac_ipmi,"Invalid NV keyword size: %d, expected %d",l_nvKwdSize,sizeof(HDAT::hdatNVKwdStruct_t));
+ }
+ }
+ else
+ {
+ TRACFCOMP(g_trac_ipmi,ERR_MRK"%.8X Error getting VNDR record size",l_err->eid());
+ }
+ }
+
+ o_cfgID_bitwise = L_NV_bits;
+
+ return l_err;
+ }
+
+ /**
+ * @brief Grab the GPU sensor type IDs for a particular processor target
+ *
+ * Will return all sensor ids that match the type for a given target.
+ *
+ * @param[in] - i_proc - processor target
+ * @param[in] - i_type - Functional/state, gpucoretemp, gpumemtemp
+ * @param[out] - o_num_ids - number of valid IDs returned in o_ids
+ * @param[out] - o_ids - ordered list of sensor IDs
+ *
+ * @return Errorlog handle
+ */
+ errlHndl_t getGpuSensors( TARGETING::Target* i_proc,
+ HWAS::sensorTypeEnum i_type,
+ uint8_t & o_num_ids,
+ uint32_t o_ids[MAX_GPU_SENSORS_PER_PROCESSOR] )
+ {
+ static uint16_t L_obus_cfgID_bit = 0;
+ errlHndl_t l_errl = nullptr;
+
+ // default to no ids returned
+ o_num_ids = 0;
+
+ TARGETING::AttributeTraits<TARGETING::ATTR_GPU_SENSORS>::Type
+ l_sensorArray;
+
+ bool foundSensors = i_proc->tryGetAttr<TARGETING::ATTR_GPU_SENSORS>
+ (l_sensorArray);
+
+ // Verify we are getting non-default values
+ if (foundSensors && l_sensorArray[0][0] != 0)
+ {
+ // Figure out which backplane we have
+ // Only read NV keyword once (if possible)
+ if (L_obus_cfgID_bit == 0)
+ {
+ l_errl = getNVCfgIDBit(L_obus_cfgID_bit);
+ if (l_errl || (L_obus_cfgID_bit == 0))
+ {
+ delete l_errl;
+ l_errl = nullptr;
+ // default to full list of GPU sensors
+ L_obus_cfgID_bit = NVCFG_ALL_SENSORS_RETURNED;
+ }
+ }
+
+ uint32_t elementCount = (sizeof(l_sensorArray)/
+ sizeof(l_sensorArray[0]));
+ TRACFCOMP(g_trac_ipmi,"getGpuSensors() -> GPU_SENSORS array size = %d, cfgBit = 0x%x",
+ elementCount, L_obus_cfgID_bit);
+
+ // verify array index won't exceed output array (o_ids)
+ assert(elementCount <= MAX_GPU_SENSORS_PER_PROCESSOR);
+
+ // now cycle through each GPU row
+ for (uint32_t index = 0; index < elementCount; index++)
+ {
+ uint16_t * row_ptr = &l_sensorArray[index][0];
+
+ TRACFCOMP(g_trac_ipmi,"getGpuSensors() -> ROW %d, 0x%04X, 0x%X, 0x%04X, 0x%X, 0x%04X, 0x%X, 0x%X",
+ index, row_ptr[0], row_ptr[1], row_ptr[2],
+ row_ptr[3], row_ptr[4], row_ptr[5], row_ptr[6]);
+
+ // Include Sensor if the GPU is present in the current OBUS_CFG
+ if ( (L_obus_cfgID_bit == NVCFG_ALL_SENSORS_RETURNED) ||
+ ((L_obus_cfgID_bit &
+ row_ptr[TARGETING::GPU_SENSOR_ARRAY_OBUS_CFG_OFFSET])
+ == L_obus_cfgID_bit) )
+ {
+ switch(i_type)
+ {
+ case HWAS::GPU_FUNC_SENSOR:
+ o_ids[index] =
+ row_ptr[TARGETING::GPU_SENSOR_ARRAY_FUNC_ID_OFFSET];
+ o_num_ids++;
+ break;
+ case HWAS::GPU_MEMORY_TEMP_SENSOR:
+ o_ids[index] =
+ row_ptr[TARGETING::GPU_SENSOR_ARRAY_MEM_TEMP_ID_OFFSET];
+ o_num_ids++;
+ break;
+ case HWAS::GPU_TEMPERATURE_SENSOR:
+ o_ids[index] =
+ row_ptr[TARGETING::GPU_SENSOR_ARRAY_TEMP_ID_OFFSET];
+ o_num_ids++;
+ break;
+ default:
+ TRACFCOMP(g_trac_ipmi,"getGpuSensors() -> unknown sensor type 0x%02X", i_type);
+ o_ids[index] = TARGETING::UTIL::INVALID_IPMI_SENSOR;
+ }
+ }
+ else
+ {
+ o_ids[index] = TARGETING::UTIL::INVALID_IPMI_SENSOR;
+ }
+ TRACFCOMP(g_trac_ipmi,
+ "getGpuSensors() -> o_id[%d] = 0x%X", index, o_ids[index]);
+ } // end of for loop
+ } // end of if check for non-default values
+
+ return NULL;
+ } // end getGpuSensors()
+
+
+ /**
+ * @brief Helper function that sends GPU sensor status to BMC
+ *
+ * @param[in] sensor name (IPMI_SENSOR_TYPE with IPMI_ENTITY_ID)
+ * @param[in] sensor id number
+ * @param[in] processor target
+ * @param[in] status to send for the identified GPU sensor
+ */
+ void sendGpuSensorStatus(uint16_t i_sensor_name_value,
+ uint16_t i_sensor_id,
+ TARGETING::ConstTargetHandle_t i_target,
+ StatusSensor::statusEnum & i_status)
+ {
+ TRACFCOMP(g_trac_ipmi, "sendGpuSensorStatus(0x%0X, 0x%X, Target 0x%X, status: %d)",
+ i_sensor_name_value, i_sensor_id,
+ TARGETING::get_huid(i_target), i_status);
+
+ TARGETING::SENSOR_NAME l_sensor_name;
+ switch(i_sensor_name_value)
+ {
+ case TARGETING::SENSOR_NAME_GPU_TEMP:
+ l_sensor_name = TARGETING::SENSOR_NAME_GPU_TEMP;
+ break;
+ case TARGETING::SENSOR_NAME_GPU_STATE:
+ l_sensor_name = TARGETING::SENSOR_NAME_GPU_STATE;
+ break;
+ case TARGETING::SENSOR_NAME_GPU_MEM_TEMP:
+ l_sensor_name = TARGETING::SENSOR_NAME_GPU_MEM_TEMP;
+ break;
+ default:
+ TRACFCOMP(g_trac_ipmi, "sendGpuSensorStatus(0x%0X, 0x%X) - unknown GPU sensor name",
+ i_sensor_name_value, i_sensor_id);
+ l_sensor_name = TARGETING::SENSOR_NAME_FAULT;
+ break;
+ }
+
+ // Only update if we found a valid gpu sensor name
+ if (l_sensor_name != TARGETING::SENSOR_NAME_FAULT)
+ {
+ // create a GPU status sensor for our needs
+ GpuSensor l_sensor(l_sensor_name, i_sensor_id, i_target);
+
+ // send the status to the BMC
+ errlHndl_t l_err = l_sensor.setStatus( i_status );
+
+ // commit the error and move to the next target
+ if( l_err )
+ {
+ errlCommit( l_err, IPMI_COMP_ID );
+ }
+ }
+ }
+
+ /**
+ * @brief Updates GPU sensor status for GPUs on this
+ * particular processor target
+ *
+ * @param[in] - i_proc - processor target
+ * @param[in] - i_gpu_status - status of GPU0, GPU1 and GPU2
+ */
+ void updateGpuSensorStatus( TARGETING::Target* i_proc,
+ StatusSensor::statusEnum i_gpu_status[MAX_PROCESSOR_GPUS] )
+ {
+ uint16_t obus_cfgID_bit = 0;
+
+ TARGETING::AttributeTraits<TARGETING::ATTR_GPU_SENSORS>::Type
+ l_sensorArray;
+
+ bool foundSensors = i_proc->tryGetAttr<TARGETING::ATTR_GPU_SENSORS>
+ (l_sensorArray);
+
+ // Verify we are getting non-default values
+ if (foundSensors && (l_sensorArray[0][0] != 0))
+ {
+ // Figure out which backplane we have
+ // Only read NV keyword once (if possible)
+ errlHndl_t l_errl = getNVCfgIDBit(obus_cfgID_bit);
+ if (l_errl || (obus_cfgID_bit == 0))
+ {
+ // default to all sensors
+ obus_cfgID_bit = NVCFG_ALL_SENSORS_RETURNED;
+ delete l_errl;
+ }
+
+ uint32_t elementCount = (sizeof(l_sensorArray)/
+ sizeof(l_sensorArray[0]));
+ TRACDCOMP(g_trac_ipmi,"updateGpuSensorStatus() -> array size = %d, cfgBit = 0x%x",
+ elementCount, obus_cfgID_bit);
+
+ // verify array index won't exceed output array (o_ids)
+ assert(elementCount <= MAX_PROCESSOR_GPUS);
+
+ // now cycle through each GPU row
+ for (uint8_t index = 0; index < MAX_PROCESSOR_GPUS; index++)
+ {
+ uint16_t * sensor_row_ptr = &l_sensorArray[index][0];
+ StatusSensor::statusEnum newStatus = i_gpu_status[index];
+
+ // Include Sensor if the GPU is present in the current OBUS_CFG
+ if ( (obus_cfgID_bit == NVCFG_ALL_SENSORS_RETURNED) ||
+ ((obus_cfgID_bit &
+ sensor_row_ptr[TARGETING::GPU_SENSOR_ARRAY_OBUS_CFG_OFFSET])
+ == obus_cfgID_bit) )
+ {
+ // Only update the GPU status sensors, skip temperature ones
+ // GPU core Status/Functional Sensor
+ uint16_t sensor_name =
+ sensor_row_ptr[TARGETING::GPU_SENSOR_ARRAY_FUNC_OFFSET];
+ uint16_t sensor_id =
+ sensor_row_ptr[TARGETING::GPU_SENSOR_ARRAY_FUNC_ID_OFFSET];
+ sendGpuSensorStatus(sensor_name,sensor_id,i_proc,newStatus);
+ }
+ } // end of GPU loop
+ } // end of if check for non-default values
+ } // end of updateGpuSensorStatus()
+
+
+ //
+ // HbVolatileSensor constructor - uses system target
+ //
+ HbVolatileSensor::HbVolatileSensor()
+ :SensorBase(TARGETING::SENSOR_NAME_HB_VOLATILE, NULL)
+ {
+ // message buffer created and initialized in base object.
+ }
+
+ //
+ // HbVolatileSensor destructor
+ //
+ HbVolatileSensor::~HbVolatileSensor(){};
+
+ //
+ // setHbVolatile - tell BMC to make hostboot volatile memory
+ // (volatile(2) or not(1))
+ //
+ errlHndl_t HbVolatileSensor::setHbVolatile( hbVolatileSetting i_setting )
+ {
+ // adjust the operation to overwrite the sensor reading
+ // to the value we send.
+ iv_msg->iv_operation = SET_SENSOR_VALUE_OPERATION;
+
+ // the HB_VOLATILE Sensor is defined as a discrete sensor
+ // but the assertion bytes are being used to transfer the state
+ iv_msg->iv_assertion_mask = le16toh(i_setting);
+
+ TRACFCOMP(g_trac_ipmi,"HbVolatileSensor::setHbVolatile(%d)",
+ i_setting);
+
+ return writeSensorData();
+ }
+
+ //
+ // getHbVolatile - get the HB volatile memory setting from the BMC
+ //
+ errlHndl_t HbVolatileSensor::getHbVolatile( hbVolatileSetting &o_setting )
+ {
+ // the HB_VOLATILE sensor is defined as a discrete sensor
+ getSensorReadingData l_data;
+
+ errlHndl_t l_err = readSensorData( l_data );
+
+ if( l_err == nullptr )
+ {
+ // check if in valid range of hbVolatileSetting enums
+ if ( l_data.event_status == ENABLE_VOLATILE ||
+ l_data.event_status == DISABLE_VOLATILE )
+ {
+ o_setting = static_cast<hbVolatileSetting>(l_data.event_status);
+ }
+ else
+ {
+ TRACFCOMP(g_trac_ipmi,"Unknown hb volatile setting: %d",
+ l_data.event_status);
+
+ /*@
+ * @errortype ERRL_SEV_UNRECOVERABLE
+ * @moduleid IPMI::MOD_IPMISENSOR_HBVOLATILE
+ * @reasoncode IPMI::RC_INVALID_SENSOR_SETTING
+ * @userdata1 Invalid hb volatile control setting
+ * @userdata2 <unused>
+ * @devdesc The sensor returned an invalid setting
+ * @custdesc Unable to find a valid sensor setting.
+ */
+ l_err = new ERRORLOG::ErrlEntry(
+ ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ IPMI::MOD_IPMISENSOR_HBVOLATILE,
+ IPMI::RC_INVALID_SENSOR_SETTING,
+ l_data.event_status,
+ 0,
+ false);
+ }
+ }
+ return l_err;
+ }
+
+}; // end name space
diff --git a/src/usr/ipmiext/ipmiwatchdog.C b/src/usr/ipmiext/ipmiwatchdog.C
new file mode 100644
index 000000000..5f7b9bde0
--- /dev/null
+++ b/src/usr/ipmiext/ipmiwatchdog.C
@@ -0,0 +1,204 @@
+/* IBM_PROLOG_BEGIN_TAG */
+/* This is an automatically generated prolog. */
+/* */
+/* $Source: src/usr/ipmiext/ipmiwatchdog.C $ */
+/* */
+/* OpenPOWER HostBoot Project */
+/* */
+/* Contributors Listed Below - COPYRIGHT 2014,2018 */
+/* [+] Google Inc. */
+/* [+] 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 */
+#ifndef __IPMIWATCHDOG_IPMIWATCHDOG_C
+#define __IPMIWATCHDOG_IPMIWATCHDOG_C
+/**
+ * @file ipmiwatchdog.C
+ *
+ * Ipmi watchdog timer
+ *
+ */
+
+/******************************************************************************/
+// Includes
+/******************************************************************************/
+#include <stdint.h>
+#include <errl/errlentry.H>
+#include <ipmi/ipmiwatchdog.H>
+#include <ipmi/ipmiif.H>
+#include <sys/time.h>
+#include <time.h>
+
+/******************************************************************************/
+// Globals/Constants
+/******************************************************************************/
+// Defined in ipmidd.C
+extern trace_desc_t * g_trac_ipmi;
+#define IPMI_TRAC(printf_string,args...) \
+ TRACFCOMP(g_trac_ipmi,"wd: " printf_string,##args)
+
+namespace IPMIWATCHDOG
+{
+
+// Needed for watchdog resets in case the BMC side becomes uninitialized and we
+// need to reinitialize it
+static bool g_set_once = false;
+static uint16_t g_countdown_secs;
+static uint8_t g_timer_use;
+static TIMER_ACTIONS g_timer_action;
+static TIMER_USE_CLR_FLAGS g_timer_clr_flag;
+
+/******************************************************************************/
+// Functions
+/******************************************************************************/
+
+
+errlHndl_t setWatchDogTimer( const uint16_t i_countdown_secs,
+ const uint8_t i_timer_use,
+ const TIMER_ACTIONS i_timer_action,
+ const TIMER_USE_CLR_FLAGS i_timer_clr_flag)
+{
+ // Save the latest values so that reset actions can setup the timer
+ // if the BMC goes away.
+ g_set_once = true;
+ g_countdown_secs = i_countdown_secs;
+ g_timer_use = i_timer_use;
+ g_timer_action = i_timer_action;
+ g_timer_clr_flag = i_timer_clr_flag;
+
+ // Convert secs into lsb and msb values
+ // the ipmi spec uses the count which is 100ms/count
+ static const uint16_t ms_per_count = 100;
+ static const uint8_t bits_to_shift_for_nxt_byte = 8;
+ static const uint8_t byte_mask = 0xFF;
+
+ uint16_t countdown = (i_countdown_secs * MS_PER_SEC)
+ / ms_per_count;
+
+ uint8_t init_countdown_lsb = static_cast<uint8_t>
+ (countdown & byte_mask);
+
+ uint8_t init_countdown_msb = static_cast<uint8_t>(
+ (countdown >> bits_to_shift_for_nxt_byte)
+ & byte_mask);
+
+
+ size_t len = 6; // IPMI spec has request data at 6 bytes
+
+ //create request data buffer
+ uint8_t* data = new uint8_t[len];
+
+ data[0] = i_timer_use; // byte 1 timer use
+ data[1] = i_timer_action; // byte 2 timer actions
+ data[2] = 0x00; // byte 3 pre-interval timeout in secs
+ data[3] = i_timer_clr_flag; // byte 4 timer use flags to clear
+ data[4] = init_countdown_lsb; // byte 5 initial countdown timer LSByte
+ data[5] = init_countdown_msb; // byte 6 initial countdown timer MSByte
+
+ IPMI_TRAC( "setWatchDogTimer : sec=%d, use=%d, act=%d, clr=%d",
+ i_countdown_secs,
+ i_timer_use,
+ i_timer_action,
+ i_timer_clr_flag );
+ return IPMI::send(IPMI::set_watchdog(), len, data);
+
+}
+
+
+errlHndl_t resetWatchDogTimer()
+{
+ errlHndl_t err_ipmi = NULL;
+
+ // Limit the amount of sent watchdog resets to once per second to make
+ // sure that we don't overload the BMC with resets during quickly
+ // completing ISTEPs. For simplicity we only compare the tv_sec so
+ // the grace period may be almost 2 seconds.
+ static uint64_t last_reset_sec;
+ timespec_t now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ if (last_reset_sec + 1 >= now.tv_sec)
+ {
+ return NULL;
+ }
+ last_reset_sec = now.tv_sec;
+
+ // reset command does not take any request data
+ size_t len = 0;
+ uint8_t* data = NULL;
+
+
+ do
+ {
+ IPMI_TRAC( "resetWatchDogTimer" );
+
+ IPMI::completion_code cc;
+ err_ipmi = IPMI::sendrecv(IPMI::reset_watchdog(), cc, len, data);
+ delete[] data;
+
+ if(err_ipmi)
+ {
+ //got an error sending IPMI msg
+ //progate error upstream.
+ break;
+ }
+
+ // Make sure we don't have an uninitialized watchdog
+ // openbmc will always start the watchdog regardless of us
+ // initializing it. If we don't initialize it, it can eventually
+ // time out on us
+ if (g_set_once && cc == IPMI::CC_CMDSPC1)
+ {
+ err_ipmi = setWatchDogTimer(g_countdown_secs,
+ g_timer_use | DO_NOT_STOP,
+ g_timer_action, g_timer_clr_flag);
+
+ if (err_ipmi)
+ {
+ //got an error sending IPMI msg
+ //progate error upstream.
+ break;
+ }
+
+ // Make sure to issue a reset after initialization to start
+ // the timer if it isn't already. We don't care about the return
+ // value of this message since we can't recurse forever. If we
+ // fail here, later resets should handle reconfiguring the
+ // watchdog if it isn't critically broken.
+ len = 0;
+ data = NULL;
+ err_ipmi = IPMI::send(IPMI::reset_watchdog(), len, data);
+
+ if(err_ipmi)
+ {
+ //got an error sending IPMI msg
+ //progate error upstream.
+ break;
+ }
+ }
+
+ // We don't care about any other types of errors as the watchdog
+ // will just trip in these cases.
+ break;
+ }
+ while(0);
+
+ return err_ipmi;
+}
+
+
+} // namespace
+
+#endif
diff --git a/src/usr/ipmiext/makefile b/src/usr/ipmiext/makefile
new file mode 100644
index 000000000..e9aa66deb
--- /dev/null
+++ b/src/usr/ipmiext/makefile
@@ -0,0 +1,39 @@
+# IBM_PROLOG_BEGIN_TAG
+# This is an automatically generated prolog.
+#
+# $Source: src/usr/ipmiext/makefile $
+#
+# OpenPOWER HostBoot Project
+#
+# Contributors Listed Below - COPYRIGHT 2011,2018
+# [+] 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
+ROOTPATH = ../../..
+MODULE = ipmiext
+
+#include common ojects between hostboot and runtime hostboot
+include ipmi.mk
+
+OBJS += ipmifru.o
+OBJS += ipmiwatchdog.o
+OBJS += ipmifruinv.o
+OBJS += ipmipowerstate.o
+OBJS += ipmichassiscontrol.o
+
+SUBDIRS += runtime.d
+
+include ${ROOTPATH}/config.mk
diff --git a/src/usr/ipmiext/runtime/makefile b/src/usr/ipmiext/runtime/makefile
new file mode 100644
index 000000000..e044f35de
--- /dev/null
+++ b/src/usr/ipmiext/runtime/makefile
@@ -0,0 +1,38 @@
+# IBM_PROLOG_BEGIN_TAG
+# This is an automatically generated prolog.
+#
+# $Source: src/usr/ipmiext/runtime/makefile $
+#
+# OpenPOWER HostBoot Project
+#
+# Contributors Listed Below - COPYRIGHT 2015,2018
+# [+] 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
+HOSTBOOT_RUNTIME = 1
+ROOTPATH = ../../../..
+MODULE = ipmi_rt
+
+#include common ojects between hostboot and runtime hostboot
+include ../ipmi.mk
+
+OBJS += rt_ipmirp.o
+
+SUBDIRS += test.d
+
+VPATH += ..
+include $(ROOTPATH)/config.mk
+
diff --git a/src/usr/ipmiext/runtime/rt_ipmirp.C b/src/usr/ipmiext/runtime/rt_ipmirp.C
new file mode 100644
index 000000000..496b3f782
--- /dev/null
+++ b/src/usr/ipmiext/runtime/rt_ipmirp.C
@@ -0,0 +1,188 @@
+/* IBM_PROLOG_BEGIN_TAG */
+/* This is an automatically generated prolog. */
+/* */
+/* $Source: src/usr/ipmiext/runtime/rt_ipmirp.C $ */
+/* */
+/* OpenPOWER HostBoot Project */
+/* */
+/* Contributors Listed Below - COPYRIGHT 2015,2018 */
+/* [+] 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 rt_ipmirp.C
+ * @brief IPMI resource provider definition for runtime
+ */
+
+#include <ipmi/ipmi_reasoncodes.H>
+#include <ipmi/ipmiif.H>
+
+#include <config.h>
+#include <sys/task.h>
+#include <initservice/taskargs.H>
+#include <initservice/initserviceif.H>
+#include <sys/vfs.h>
+
+#include <targeting/common/commontargeting.H>
+#include <kernel/ipc.H>
+#include <arch/ppc.H>
+#include <errl/errlmanager.H>
+#include <sys/time.h>
+#include <sys/misc.h>
+#include <errno.h>
+#include <runtime/interface.h>
+
+trace_desc_t * g_trac_ipmi;
+TRAC_INIT(&g_trac_ipmi, IPMI_COMP_NAME, 6*KILOBYTE, TRACE::BUFFER_SLOW);
+
+#define IPMI_TRAC(printf_string,args...) \
+ TRACFCOMP(g_trac_ipmi,"rt: " printf_string,##args)
+
+namespace IPMI
+{
+ static size_t g_max_buffer = 0;
+ size_t max_buffer(void)
+ {
+ if (g_max_buffer == 0)
+ {
+ TARGETING::Target * sys = NULL;
+ TARGETING::targetService().getTopLevelTarget( sys );
+ if (sys)
+ {
+ g_max_buffer = sys->getAttr
+ <TARGETING::ATTR_IPMI_MAX_BUFFER_SIZE>();
+ IPMI_TRAC( INFO_MRK"getAttr(IPMI_MAX_BUFFER_SIZE) = %d",
+ g_max_buffer);
+ }
+ else
+ {
+ IPMI_TRAC( ERR_MRK"IPMI_MAX_BUFFER_SIZE not available" );
+ }
+ }
+ return g_max_buffer;
+ }
+
+ ///
+ /// @brief Synchronus message send
+ ///
+ errlHndl_t sendrecv(const IPMI::command_t& i_cmd,
+ IPMI::completion_code& o_completion_code,
+ size_t& io_len, uint8_t*& io_data)
+ {
+ errlHndl_t err = NULL;
+ int rc = 0;
+
+ // if the buffer is too large this is a programming error.
+ assert(io_len <= max_buffer());
+
+ uint8_t netfn = i_cmd.first >> 2; //remove embedded LUN
+ IPMI_TRAC("calling sync %x:%x len=%d",
+ netfn, i_cmd.second, io_len);
+
+ if(g_hostInterfaces && g_hostInterfaces->ipmi_msg)
+ {
+ size_t l_len = max_buffer(); // max size the BMC can return
+ uint8_t *l_data = new uint8_t[l_len];
+
+ rc = g_hostInterfaces->ipmi_msg(
+ netfn, i_cmd.second,
+ io_data, io_len,
+ l_data, &l_len);
+
+ if(rc)
+ {
+ IPMI_TRAC(ERR_MRK
+ "Failed sending ipmi msg (%x:%x) to bmc rc: %d. ",
+ i_cmd.first, i_cmd.second, rc);
+
+ /*@
+ * @errortype ERRL_SEV_UNRECOVERABLE
+ * @moduleid IPMI::MOD_IPMIRT
+ * @reasoncode IPMI::RC_INVALID_SENDRECV
+ * @userdata1[0:31] rc from ipmi_msg()
+ * @userdata1[32:46] netfn of failing msg
+ * @userdata1[47:63] cmd of failing msg
+ * @userdata2 length of failing msg
+ * @devdesc ipmi_msg() failed
+ * @custdesc Firmware error
+ */
+ err = new ERRORLOG::ErrlEntry(
+ ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ IPMI::MOD_IPMIRT,
+ IPMI::RC_INVALID_SENDRECV,
+ TWO_UINT32_TO_UINT64(rc,
+ TWO_UINT16_TO_UINT32(netfn, i_cmd.second)),
+ io_len,
+ true);
+ err->collectTrace(IPMI_COMP_NAME);
+ delete[] io_data;
+ io_data = NULL;
+ io_len = 0;
+ }
+ else
+ {
+ // clean up the memory for the caller
+ o_completion_code =
+ static_cast<IPMI::completion_code>(l_data[0]);
+
+ // now need to create the buffer to return
+ io_len = l_len - 1; // get rid of the completion_code
+ delete[] io_data;
+ io_data = new uint8_t[io_len];
+ memcpy(io_data, &l_data[1], io_len); // data after CC
+ }
+ delete[] l_data;
+ }
+ else
+ {
+ IPMI_TRAC(ERR_MRK
+ "Host interfaces not initialized; ipmi msg not sent. ");
+ }
+
+ return err;
+ } // sendrecv
+
+ /*
+ * @brief Asynchronus message send
+ *
+ * @param[in] i_cmd, the command we're sending
+ * @param[in] i_len, the length of the data
+ * @param[in] i_data, the data we're sending
+ * @param[in] i_type, the type of message we're sending
+ *
+ */
+ errlHndl_t send(const IPMI::command_t& i_cmd,
+ size_t i_len, uint8_t* i_data,
+ IPMI::message_type i_type)
+ {
+ IPMI::completion_code l_cc = IPMI::CC_UNKBAD;
+
+ // We are calling a synchronous send in an asynchronous function
+ // This is needed to enable asynchronous message sending before
+ // runtime. A message should be synchronous during runtime, but
+ // by ignoring the cc returned and clearing the data, we're making
+ // a synchronous function "asynchronous".
+ errlHndl_t l_err = sendrecv(i_cmd,l_cc,i_len,i_data);
+
+ if(i_data != NULL)
+ {
+ delete i_data;
+ }
+
+ return l_err;
+ }
+
+};
diff --git a/src/usr/ipmiext/runtime/test/makefile b/src/usr/ipmiext/runtime/test/makefile
new file mode 100644
index 000000000..6967a77c6
--- /dev/null
+++ b/src/usr/ipmiext/runtime/test/makefile
@@ -0,0 +1,31 @@
+# IBM_PROLOG_BEGIN_TAG
+# This is an automatically generated prolog.
+#
+# $Source: src/usr/ipmiext/runtime/test/makefile $
+#
+# OpenPOWER HostBoot Project
+#
+# Contributors Listed Below - COPYRIGHT 2015,2018
+# [+] 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
+HOSTBOOT_RUNTIME = 1
+ROOTPATH = ../../../../..
+
+MODULE = testipmi_rt
+TESTS = *.H
+
+include ${ROOTPATH}/config.mk
diff --git a/src/usr/ipmiext/runtime/test/rt_ipmitest.H b/src/usr/ipmiext/runtime/test/rt_ipmitest.H
new file mode 100644
index 000000000..5ee93eca5
--- /dev/null
+++ b/src/usr/ipmiext/runtime/test/rt_ipmitest.H
@@ -0,0 +1,102 @@
+/* IBM_PROLOG_BEGIN_TAG */
+/* This is an automatically generated prolog. */
+/* */
+/* $Source: src/usr/ipmiext/runtime/test/rt_ipmitest.H $ */
+/* */
+/* OpenPOWER HostBoot Project */
+/* */
+/* Contributors Listed Below - COPYRIGHT 2015,2018 */
+/* [+] 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 */
+#ifndef __RT_IPMITEST_H
+#define __RT_IPMITEST_H
+
+/**
+ * @file ipmitest.H
+ *
+ * @brief Test case for IPMI messages
+*/
+
+#include <cxxtest/TestSuite.H>
+
+#include "../../../trace/entry.H"
+
+#include <ipmi/ipmiif.H>
+#include <ipmi/ipmisensor.H>
+
+
+class RT_IpmiTest: public CxxTest::TestSuite
+{
+public:
+
+
+ /**
+ * @brief call sendrecv directly
+ */
+ void testIpmi1(void)
+ {
+ do
+ {
+ IPMI::completion_code cc = IPMI::CC_UNKBAD;
+ size_t len = 0;
+ uint8_t* data = NULL;
+ errlHndl_t err = IPMI::sendrecv(IPMI::get_capabilities(), cc, len, data);
+
+ TS_WARN("testIpmi1: cc x%x, len %d", cc, len);
+
+ if ( err != NULL )
+ {
+ TS_FAIL("testIpmi1: get_capabilities() failed");
+ break;
+ }
+ if (cc != IPMI::CC_OK)
+ {
+ TS_FAIL("testIpmi1: get_capabilities() failed with cc 0x%x", cc);
+ break;
+ }
+ if (len != 5)
+ {
+ TS_FAIL("testIpmi1: get_capabilities() returned len %d instead of 5",
+ len);
+ break;
+ }
+
+ TS_WARN("testIpmi1: get_capabilities x%x, x%x x%x x%x x%x",
+ data[0], data[1], data[2], data[3], data[4]);
+
+ }
+ while(0);
+ }
+
+ /**
+ * @brief call IPMI::sensor function
+ */
+ void testIpmi2(void)
+ {
+ do
+ {
+ uint8_t l_sensorNumber = SENSOR::getFaultSensorNumber(
+ TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL);
+
+ TS_WARN("testIpmi2: getFaultSensorNumber x%x",
+ l_sensorNumber);
+ }
+ while(0);
+ }
+};
+
+#endif
OpenPOWER on IntegriCloud