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/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,