Added support for IPMB based SDR detection dynamically
SDR is a data record that provides platform management sensor type,
locations, event generation and access information.
A data records that contain information about the type and the
number of sensors in the platform, sensor threshold support,
event generation capabilities and information on what type
of readings the sensor provides.
Here, SDR of Type 1 records are implemented based on IPMB to read
the sensor information from each bus which can be configured
using IPMB FRU from the EM file.
Sample Config :
{
"Bus": "$ipmbindex",
"Class": "IpmbDevice",
"Name": "$ipmbindex + 1 Twinlake Board",
"PowerState": "Always",
"Type": "IpmbDevice"
}
This configuration is based on the "xyz.openbmc_project.Ipmb.FruDevice"
service, which will read FRU information from each IPMB bus. "$bus"
will give the bus index for each IPMB bus.
If IPMB FRU is detected, SDR info and record count of the sensor in
each bus will be identified. After getting the record count,
Reservation ID for each IPMB bus will be received.
IpmbSDRDevice will retrieve the full set of SDR Records starting with
0000h as the Record ID to get the first record. The Next Record ID is
extracted from the response and this is then used as the Record ID in
a Get SDR request to get the next record. This is repeated until the
record count value is matched.
The full information of each sensor such as sensor name, sensor type,
sensor unit, threshold values, sensor unique number will be received
using IPMB.
Once all the sensor information is read, each data will be processed
and stored in the structure. These data will be accessed in the
below gerrit patch.
https://gerrit.openbmc-project.xyz/c/openbmc/dbus-sensors/+/52164
Tested : Tested on YosemiteV2 platform and verified all the IPMB based
SDR Type 1 supported device data.
Signed-off-by: Jayashree Dhanapal <jayashree-d@hcl.com>
Change-Id: I5f808b68c3052ec5bf78d3d5c5495bd1f448ade3
diff --git a/include/IpmbSDRSensor.hpp b/include/IpmbSDRSensor.hpp
new file mode 100644
index 0000000..4284e7a
--- /dev/null
+++ b/include/IpmbSDRSensor.hpp
@@ -0,0 +1,118 @@
+#pragma once
+
+#include <sensor.hpp>
+
+using IpmbMethodType =
+ std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>;
+
+enum class SDRType
+{
+ sdrType01 = 1,
+ sdrType02 = 2,
+ sdrType03 = 3
+};
+
+namespace sdr
+{
+// IPMB Commands
+static constexpr uint8_t netfnStorageReq = 0x0a;
+static constexpr uint8_t cmdStorageGetSdrInfo = 0x20;
+static constexpr uint8_t cmdStorageReserveSdr = 0x22;
+static constexpr uint8_t cmdStorageGetSdr = 0x23;
+
+// Get SDR Commands
+static constexpr uint8_t sdrNxtRecLSB = 0;
+static constexpr uint8_t sdrNxtRecMSB = 1;
+static constexpr uint8_t perCountByte = 16;
+
+// Sensor Record Bytes
+static constexpr uint8_t sdrType = 5;
+static constexpr uint8_t dataLengthByte = 6;
+static constexpr uint8_t sdrSensorNum = 9;
+
+} // namespace sdr
+
+namespace sdrtype01
+{
+// Negative Handle Commands
+static constexpr uint8_t maxPosReadingMargin = 127;
+static constexpr uint8_t twosCompVal = 128;
+static constexpr double thermalConst = 256;
+
+static constexpr uint8_t sdrSensNoThres = 0;
+static constexpr uint8_t sensorCapability = 13;
+static constexpr uint8_t sdrNegHandle = 24;
+static constexpr uint8_t sdrUnitType = 25;
+static constexpr uint8_t sdrLinearByte = 27;
+
+// SDR Type 1 Thresholds Commands
+static constexpr uint8_t mDataByte = 28;
+static constexpr uint8_t mTolDataByte = 29;
+static constexpr uint8_t bDataByte = 30;
+static constexpr uint8_t bAcuDataByte = 31;
+static constexpr uint8_t rbExpDataByte = 33;
+static constexpr uint8_t upperCriticalThreshold = 43;
+static constexpr uint8_t lowerCriticalThreshold = 46;
+static constexpr uint8_t nameLengthByte = 53;
+
+} // namespace sdrtype01
+
+struct SensorInfo
+{
+ std::string sensorReadName;
+ uint8_t sensorUnit = 0;
+ double thresUpperCri = 0;
+ double thresLowerCri = 0;
+ uint8_t sensorNumber = 0;
+ uint8_t sensCap = 0;
+};
+
+struct SensorValConversion
+{
+ uint16_t mValue = 0;
+ double bValue = 0;
+ double expoVal = 0;
+ uint8_t negRead = 0;
+};
+
+inline std::map<int, std::vector<SensorInfo>> sensorRecord;
+inline std::map<int, std::map<uint8_t, SensorValConversion>> sensorValRecord;
+
+class IpmbSDRDevice : public std::enable_shared_from_this<IpmbSDRDevice>
+{
+ public:
+ IpmbSDRDevice(std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
+ uint8_t cmdAddr);
+
+ uint8_t commandAddress = 0;
+ int hostIndex = 0;
+
+ std::shared_ptr<sdbusplus::asio::connection> conn;
+
+ std::vector<uint8_t> sdrData;
+ uint16_t validRecordCount = 1;
+ uint8_t iCnt = 0;
+ uint8_t nextRecordIDLSB = 0;
+ uint8_t nextRecordIDMSB = 0;
+
+ std::vector<uint8_t> sdrCommandData = {};
+
+ void getSDRRepositoryInfo();
+
+ void reserveSDRRepository(uint16_t recordCount);
+
+ void getSDRSensorData(uint16_t recordCount, uint8_t resrvIDLSB,
+ uint8_t resrvIDMSB);
+
+ void handleSDRData(const std::vector<uint8_t>& data, uint16_t recordCount,
+ uint8_t resrvIDLSB, uint8_t resrvIDMSB);
+
+ void checkSDRData(std::vector<uint8_t>& sdrDataBytes,
+ uint8_t dataLength) const;
+
+ static void checkSDRType01Threshold(std::vector<uint8_t>& sdrDataBytes,
+ int busIndex, std::string tempName);
+
+ inline static double sensorValCalculation(uint16_t mValue, double bValue,
+ double expValue, double value);
+};
diff --git a/src/IpmbSDRSensor.cpp b/src/IpmbSDRSensor.cpp
new file mode 100644
index 0000000..1470340
--- /dev/null
+++ b/src/IpmbSDRSensor.cpp
@@ -0,0 +1,339 @@
+#include <IpmbSDRSensor.hpp>
+
+const constexpr char* ipmbService = "xyz.openbmc_project.Ipmi.Channel.Ipmb";
+const constexpr char* ipmbDbusPath = "/xyz/openbmc_project/Ipmi/Channel/Ipmb";
+const constexpr char* ipmbInterface = "org.openbmc.Ipmb";
+const constexpr char* ipmbMethod = "sendRequest";
+static constexpr uint8_t lun = 0;
+
+IpmbSDRDevice::IpmbSDRDevice(
+ std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
+ uint8_t cmdAddr) :
+ commandAddress(cmdAddr << 2),
+ hostIndex(cmdAddr + 1), conn(dbusConnection)
+{}
+
+bool validateStatus(boost::system::error_code ec,
+ const IpmbMethodType& response, int hostIndex)
+{
+ if (ec)
+ {
+ return false;
+ }
+
+ const int status = std::get<0>(response);
+ if (status != 0)
+ {
+ std::cerr << "Error reading from IPMB SDR for host " << hostIndex
+ << "\n";
+ return false;
+ }
+ return true;
+}
+
+/* This function will store the record count of the SDR sensors for each IPMB
+ * bus */
+void IpmbSDRDevice::getSDRRepositoryInfo()
+{
+ std::weak_ptr<IpmbSDRDevice> weakRef = weak_from_this();
+
+ conn->async_method_call(
+ [weakRef](boost::system::error_code ec,
+ const IpmbMethodType& response) {
+ auto self = weakRef.lock();
+ if (!self)
+ {
+ return;
+ }
+
+ auto status = std::bind_front(validateStatus, ec, response);
+ if (!status(self->hostIndex))
+ {
+ return;
+ }
+
+ const std::vector<uint8_t>& data = std::get<5>(response);
+ const size_t sdrInfoDataSize = 14;
+
+ if (data.size() < sdrInfoDataSize)
+ {
+ std::cerr << " IPMB Get SDR Repository Info data is empty for host "
+ << self->hostIndex << "\n";
+ return;
+ }
+
+ constexpr uint8_t recordCountLSB = 1;
+ constexpr uint8_t recordCountMSB = 2;
+
+ uint16_t recordCount =
+ (data[recordCountMSB] << 8) | data[recordCountLSB];
+
+ self->reserveSDRRepository(recordCount);
+ },
+ ipmbService, ipmbDbusPath, ipmbInterface, ipmbMethod, commandAddress,
+ sdr::netfnStorageReq, lun, sdr::cmdStorageGetSdrInfo, sdrCommandData);
+}
+
+/* This function will store the reserve ID for each IPMB bus index */
+void IpmbSDRDevice::reserveSDRRepository(uint16_t recordCount)
+{
+ std::weak_ptr<IpmbSDRDevice> weakRef = weak_from_this();
+
+ conn->async_method_call(
+ [weakRef, recordCount](boost::system::error_code ec,
+ const IpmbMethodType& response) {
+ auto self = weakRef.lock();
+ if (!self)
+ {
+ return;
+ }
+
+ auto status = std::bind_front(validateStatus, ec, response);
+ if (!status(self->hostIndex))
+ {
+ return;
+ }
+
+ const std::vector<uint8_t>& data = std::get<5>(response);
+ const size_t sdrReserveDataSize = 2;
+
+ if (data.size() < sdrReserveDataSize)
+ {
+ std::cerr << " IPMB SDR Reserve Repository data is empty for host "
+ << self->hostIndex << "\n";
+ return;
+ }
+ uint8_t resrvIDLSB = data[0];
+ uint8_t resrvIDMSB = data[1];
+
+ self->getSDRSensorData(recordCount, resrvIDLSB, resrvIDMSB);
+ },
+ ipmbService, ipmbDbusPath, ipmbInterface, ipmbMethod, commandAddress,
+ sdr::netfnStorageReq, lun, sdr::cmdStorageReserveSdr, sdrCommandData);
+}
+
+/* This function will read all the information related to the sensor
+ * such as name, threshold value, unit, device address, SDR type */
+void IpmbSDRDevice::getSDRSensorData(uint16_t recordCount, uint8_t resrvIDLSB,
+ uint8_t resrvIDMSB)
+{
+ std::weak_ptr<IpmbSDRDevice> weakRef = weak_from_this();
+
+ uint8_t loopCount = sdr::perCountByte * iCnt;
+ std::vector<uint8_t> commandData = {resrvIDLSB, resrvIDMSB,
+ nextRecordIDLSB, nextRecordIDMSB,
+ loopCount, sdr::perCountByte};
+
+ conn->async_method_call(
+ [weakRef, recordCount, resrvIDLSB, resrvIDMSB](
+ boost::system::error_code ec, const IpmbMethodType& response) {
+ auto self = weakRef.lock();
+ if (!self)
+ {
+ return;
+ }
+
+ auto status = std::bind_front(validateStatus, ec, response);
+ if (!status(self->hostIndex))
+ {
+ return;
+ }
+
+ const std::vector<uint8_t>& data = std::get<5>(response);
+ const size_t sdrSensorDataSize = 18;
+
+ if (data.size() < sdrSensorDataSize)
+ {
+ std::cerr << "IPMB SDR sensor data is empty for host "
+ << self->hostIndex << "\n";
+ return;
+ }
+
+ self->handleSDRData(data, recordCount, resrvIDLSB, resrvIDMSB);
+ },
+ ipmbService, ipmbDbusPath, ipmbInterface, ipmbMethod, commandAddress,
+ sdr::netfnStorageReq, lun, sdr::cmdStorageGetSdr, commandData);
+}
+
+/* This function will handle the sensor data received by IPMB response */
+void IpmbSDRDevice::handleSDRData(const std::vector<uint8_t>& data,
+ uint16_t recordCount, uint8_t resrvIDLSB,
+ uint8_t resrvIDMSB)
+{
+ sdrData.insert(sdrData.end(), data.begin(), data.end());
+
+ /* dataLength represents the size of data for SDR types */
+ uint8_t dataLength = sdrData[sdr::dataLengthByte] + sdr::dataLengthByte + 1;
+
+ /* If sdrData size is less than dataLength, it will call getSDRSensorData
+ * function recursively till all the data is received.
+ */
+ if (sdrData.size() < dataLength)
+ {
+ iCnt++;
+ getSDRSensorData(recordCount, resrvIDLSB, resrvIDMSB);
+ }
+ else
+ {
+ /* After all the data is received, it is passed to checkSDRData
+ * function. Next sensor record ID is stored based on the previous
+ * record ID. Vector of sdrData is cleared to store next sensor data.
+ * validRecordCount is incremented and getSDRSensorData function is
+ * called to proceed with next set of sensors.
+ */
+ checkSDRData(sdrData, dataLength);
+ iCnt = 0;
+ nextRecordIDLSB = sdrData[sdr::sdrNxtRecLSB];
+ nextRecordIDMSB = sdrData[sdr::sdrNxtRecMSB];
+ sdrData.clear();
+
+ if (validRecordCount == recordCount)
+ {
+ /* Once all the sensors are read and recordCount matched, it will
+ * return. */
+ nextRecordIDLSB = 0;
+ nextRecordIDMSB = 0;
+ return;
+ }
+ validRecordCount++;
+ getSDRSensorData(recordCount, resrvIDLSB, resrvIDMSB);
+ }
+}
+
+/* This function will convert the SDR sensor data such as sensor unit, name, ID,
+ * type from decimal to readable format */
+void IpmbSDRDevice::checkSDRData(std::vector<uint8_t>& sdrDataBytes,
+ uint8_t dataLength) const
+{
+ if (sdrDataBytes.size() < dataLength)
+ {
+ return;
+ }
+
+ /* sdrType represents the SDR Type (Byte 5) such as 1, 2, 3 */
+ uint8_t sdrType = sdrDataBytes[sdr::sdrType];
+ if (sdrType != static_cast<uint8_t>(SDRType::sdrType01))
+ {
+ return;
+ }
+
+ /* dataLen represents the data length (Byte 6) for SDR sensor */
+ int dataLen = sdrDataBytes[sdr::dataLengthByte];
+
+ /* iStrLen represents the length of the sensor name for SDR Type 1 */
+ const uint8_t sdrLenBit = 0x1F;
+ int strLen = (sdrDataBytes[sdrtype01::nameLengthByte]) & (sdrLenBit);
+
+ /* iStrAddr represents the starting byte (Byte 56) for SDR sensor name */
+ int strAddr =
+ dataLen + ((dataLen / (sdr::perCountByte)) * 4) - (strLen - 1);
+
+ /* Below for loop will convert the bytes to string and form a sensor name */
+
+ std::string tempName(sdrDataBytes.begin() + strAddr,
+ sdrDataBytes.begin() + strAddr + strLen);
+
+ checkSDRType01Threshold(sdrDataBytes, (hostIndex - 1), tempName);
+}
+
+/* This function will convert the raw value of threshold for each sensor */
+void IpmbSDRDevice::checkSDRType01Threshold(std::vector<uint8_t>& sdrDataBytes,
+ int busIndex, std::string tempName)
+{
+ const uint8_t bitShiftMsb = 2;
+ const uint8_t sdrThresAccess = 0x0C;
+
+ /* linear represents the sensor's linearization (Byte 27) */
+ uint8_t linear = sdrDataBytes[sdrtype01::sdrLinearByte];
+ if (linear != 0)
+ {
+ return;
+ }
+
+ /* sdrSensCapability (Byte 13) and(&) with sdrThresAccess(0x0C) will declare
+ * whether threshold is present for each sensor */
+ int threshold =
+ (sdrDataBytes[sdrtype01::sensorCapability]) & (sdrThresAccess);
+
+ /* mData - 10 bits
+ * mDataByte - Byte 28 - 8 bits LSB
+ * mTolDataByte - Byte 29 - 2 bits MSB [7-6]
+ */
+ uint16_t mData =
+ ((sdrDataBytes[sdrtype01::mTolDataByte] & 0xC0) << bitShiftMsb) |
+ sdrDataBytes[sdrtype01::mDataByte];
+
+ /* bData - 10 bits
+ * bDataByte - Byte 30 - 8 bits LSB
+ * bAcuDataByte - Byte 31 - 2 bits MSB [7-6]
+ */
+ uint16_t bData =
+ ((sdrDataBytes[sdrtype01::bAcuDataByte] & 0xC0) << bitShiftMsb) |
+ sdrDataBytes[sdrtype01::bDataByte];
+
+ /* rbExpDataByte (Byte 33) represents the exponent value
+ * Bit [3-0] - B Exponent 2's complement signed bit.
+ * Bit [7-4] - R Exponent 2's complement signed bit.
+ */
+ int8_t bExpVal = sdrDataBytes[sdrtype01::rbExpDataByte] & 0xF;
+ if (bExpVal > 7)
+ {
+ bExpVal = (~bExpVal + 1) & 0xF;
+ }
+
+ /* Shifting the data to right by 4, since rExpVal has 4 bits from 4 to 7 in
+ * byte 33 */
+ int8_t rExpVal = (sdrDataBytes[sdrtype01::rbExpDataByte] >> 4) & 0xF;
+ if (rExpVal > 7)
+ {
+ rExpVal = (~rExpVal + 1) & 0xF;
+ rExpVal = -rExpVal;
+ }
+
+ /* Sensor Threshold Reading Conversion
+ *
+ * Y = ((Mx + (B * 10^K1)) * (10^K2))
+ *
+ * X - Raw value of threshold
+ * M - mData Value
+ * B - bData Value
+ * K1 - Signed Exponent of bExpVal
+ * K2 - Signed Exponent of rExpVal
+ */
+
+ double bDataVal = bData * pow(10, bExpVal);
+ double expVal = pow(10, rExpVal);
+
+ double thresUpCri =
+ sensorValCalculation(mData, bDataVal, expVal,
+ sdrDataBytes[sdrtype01::upperCriticalThreshold]);
+ double thresLoCri =
+ sensorValCalculation(mData, bDataVal, expVal,
+ sdrDataBytes[sdrtype01::lowerCriticalThreshold]);
+
+ struct SensorInfo temp;
+
+ temp.sensorReadName = std::move(tempName);
+ temp.sensorUnit = sdrDataBytes[sdrtype01::sdrUnitType];
+
+ temp.thresUpperCri = thresUpCri;
+ temp.thresLowerCri = thresLoCri;
+
+ temp.sensorNumber = sdrDataBytes[sdr::sdrSensorNum];
+ temp.sensCap = threshold;
+
+ sensorRecord[busIndex].emplace_back(std::move(temp));
+
+ SensorValConversion val = {mData, bDataVal, expVal,
+ sdrDataBytes[sdrtype01::sdrNegHandle]};
+
+ sensorValRecord[busIndex][sdrDataBytes[sdr::sdrSensorNum]] = val;
+}
+
+/* This function will calculate the sensor's threshold value */
+double IpmbSDRDevice::sensorValCalculation(uint16_t mValue, double bValue,
+ double expValue, double value)
+{
+ double sensorValue = ((mValue * value) + bValue) * expValue;
+ return sensorValue;
+}
diff --git a/src/IpmbSensor.cpp b/src/IpmbSensor.cpp
index 3fc0ee5..ba39214 100644
--- a/src/IpmbSensor.cpp
+++ b/src/IpmbSensor.cpp
@@ -14,6 +14,7 @@
// limitations under the License.
*/
+#include <IpmbSDRSensor.hpp>
#include <IpmbSensor.hpp>
#include <Utils.hpp>
#include <VariantVisitors.hpp>
@@ -37,6 +38,8 @@
constexpr const bool debug = false;
constexpr const char* sensorType = "IpmbSensor";
+constexpr const char* sdrInterface = "IpmbDevice";
+
static constexpr double ipmbMaxReading = 0xFF;
static constexpr double ipmbMinReading = 0;
@@ -49,6 +52,7 @@
static constexpr const char* sensorPathPrefix = "/xyz/openbmc_project/sensors/";
boost::container::flat_map<std::string, std::shared_ptr<IpmbSensor>> sensors;
+boost::container::flat_map<uint8_t, std::shared_ptr<IpmbSDRDevice>> sdrsensor;
std::unique_ptr<boost::asio::steady_timer> initCmdTimer;
@@ -597,6 +601,27 @@
"org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
}
+void sdrHandler(sdbusplus::message_t& message,
+ std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
+{
+ std::string objectName;
+ SensorBaseConfigMap values;
+ message.read(objectName, values);
+
+ auto findBus = values.find("Bus");
+ if (findBus == values.end())
+ {
+ return;
+ }
+
+ uint8_t busIndex = loadVariant<uint8_t>(values, "Bus");
+
+ auto& sdrsen = sdrsensor[busIndex];
+ sdrsen = nullptr;
+ sdrsen = std::make_shared<IpmbSDRDevice>(dbusConnection, busIndex);
+ sdrsen->getSDRRepositoryInfo();
+}
+
void reinitSensors(sdbusplus::message_t& message)
{
constexpr const size_t reinitWaitSeconds = 2;
@@ -682,6 +707,15 @@
std::string(power::interface) + "'",
reinitSensors);
+ auto matchSignal = std::make_shared<sdbusplus::bus::match_t>(
+ static_cast<sdbusplus::bus_t&>(*systemBus),
+ "type='signal',member='PropertiesChanged',path_namespace='" +
+ std::string(inventoryPath) + "',arg0namespace='" +
+ configInterfaceName(sdrInterface) + "'",
+ [&systemBus](sdbusplus::message_t& msg) {
+ sdrHandler(msg, systemBus);
+ });
+
setupManufacturingModeMatch(*systemBus);
io.run();
return 0;
diff --git a/src/meson.build b/src/meson.build
index e2af923..9d44db3 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -115,6 +115,7 @@
executable(
'ipmbsensor',
'IpmbSensor.cpp',
+ 'IpmbSDRSensor.cpp',
dependencies: [
default_deps,
thresholds_dep,