Adding FB OEM commands
Added Facebook OEM IPMI commands for Host as welll as IPMB with
ME and debug card
Change-Id: I794b0a293bec1416ca409e8a269cd34b81c592a8
Signed-off-by: Vijay Khemka <vijaykhemka@fb.com>
diff --git a/include/sensorutils.hpp b/include/sensorutils.hpp
new file mode 100644
index 0000000..5b9fbb2
--- /dev/null
+++ b/include/sensorutils.hpp
@@ -0,0 +1,318 @@
+/*
+ * Copyright (c) 2018 Intel Corporation.
+ * Copyright (c) 2018-present Facebook.
+ *
+ * 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.
+ */
+
+#pragma once
+#include <ipmid/api.h>
+
+#include <cmath>
+#include <iostream>
+#include <phosphor-logging/log.hpp>
+
+namespace ipmi
+{
+
+static constexpr int16_t maxInt10 = 0x1FF;
+static constexpr int16_t minInt10 = -0x200;
+static constexpr int8_t maxInt4 = 7;
+static constexpr int8_t minInt4 = -8;
+
+enum class SensorUnits : uint8_t
+{
+ unspecified = 0x0,
+ degreesC = 0x1,
+ volts = 0x4,
+ amps = 0x5,
+ watts = 0x6,
+ rpm = 0x12,
+};
+
+enum class SensorTypeCodes : uint8_t
+{
+ reserved = 0x0,
+ temperature = 0x1,
+ voltage = 0x2,
+ current = 0x3,
+ fan = 0x4,
+ other = 0xB,
+};
+
+struct CmpStrVersion
+{
+ bool operator()(std::string a, std::string b) const
+ {
+ return strverscmp(a.c_str(), b.c_str()) < 0;
+ }
+};
+
+using SensorSubTree = boost::container::flat_map<
+ std::string,
+ boost::container::flat_map<std::string, std::vector<std::string>>,
+ CmpStrVersion>;
+
+inline static bool getSensorSubtree(SensorSubTree &subtree)
+{
+ sd_bus *bus = NULL;
+ int ret = sd_bus_default_system(&bus);
+ if (ret < 0)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Failed to connect to system bus",
+ phosphor::logging::entry("ERRNO=0x%X", -ret));
+ sd_bus_unref(bus);
+ return false;
+ }
+ sdbusplus::bus::bus dbus(bus);
+ auto mapperCall =
+ dbus.new_method_call("xyz.openbmc_project.ObjectMapper",
+ "/xyz/openbmc_project/object_mapper",
+ "xyz.openbmc_project.ObjectMapper", "GetSubTree");
+ static constexpr const auto depth = 2;
+ static constexpr std::array<const char *, 3> interfaces = {
+ "xyz.openbmc_project.Sensor.Value",
+ "xyz.openbmc_project.Sensor.Threshold.Warning",
+ "xyz.openbmc_project.Sensor.Threshold.Critical"};
+ mapperCall.append("/xyz/openbmc_project/sensors", depth, interfaces);
+
+ try
+ {
+ auto mapperReply = dbus.call(mapperCall);
+ subtree.clear();
+ mapperReply.read(subtree);
+ }
+ catch (sdbusplus::exception_t &e)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
+ return false;
+ }
+ return true;
+}
+
+// Specify the comparison required to sort and find char* map objects
+struct CmpStr
+{
+ bool operator()(const char *a, const char *b) const
+ {
+ return std::strcmp(a, b) < 0;
+ }
+};
+
+const static boost::container::flat_map<const char *, SensorUnits, CmpStr>
+ sensorUnits{{{"temperature", SensorUnits::degreesC},
+ {"voltage", SensorUnits::volts},
+ {"current", SensorUnits::amps},
+ {"fan_tach", SensorUnits::rpm},
+ {"power", SensorUnits::watts}}};
+
+const static boost::container::flat_map<const char *, SensorTypeCodes, CmpStr>
+ sensorTypes{{{"temperature", SensorTypeCodes::temperature},
+ {"voltage", SensorTypeCodes::voltage},
+ {"current", SensorTypeCodes::current},
+ {"fan_tach", SensorTypeCodes::fan},
+ {"fan_pwm", SensorTypeCodes::fan},
+ {"power", SensorTypeCodes::other}}};
+
+inline static std::string getSensorTypeStringFromPath(const std::string &path)
+{
+ // get sensor type string from path, path is defined as
+ // /xyz/openbmc_project/sensors/<type>/label
+ size_t typeEnd = path.rfind("/");
+ if (typeEnd == std::string::npos)
+ {
+ return path;
+ }
+ size_t typeStart = path.rfind("/", typeEnd - 1);
+ if (typeStart == std::string::npos)
+ {
+ return path;
+ }
+ // Start at the character after the '/'
+ typeStart++;
+ return path.substr(typeStart, typeEnd - typeStart);
+}
+
+inline static uint8_t getSensorTypeFromPath(const std::string &path)
+{
+ uint8_t sensorType = 0;
+ std::string type = getSensorTypeStringFromPath(path);
+ auto findSensor = sensorTypes.find(type.c_str());
+ if (findSensor != sensorTypes.end())
+ {
+ sensorType = static_cast<uint8_t>(findSensor->second);
+ } // else default 0x0 RESERVED
+
+ return sensorType;
+}
+
+inline static uint8_t getSensorEventTypeFromPath(const std::string &path)
+{
+ // TODO: Add support for additional reading types as needed
+ return 0x1; // reading type = threshold
+}
+
+static inline bool getSensorAttributes(const double max, const double min,
+ int16_t &mValue, int8_t &rExp,
+ int16_t &bValue, int8_t &bExp,
+ bool &bSigned)
+{
+ // computing y = (10^rRexp) * (Mx + (B*(10^Bexp)))
+ // check for 0, assume always positive
+ double mDouble;
+ double bDouble;
+ if (max <= min)
+ {
+ phosphor::logging::log<phosphor::logging::level::DEBUG>(
+ "getSensorAttributes: Max must be greater than min");
+ return false;
+ }
+
+ mDouble = (max - min) / 0xFF;
+
+ if (min < 0)
+ {
+ bSigned = true;
+ bDouble = floor(0.5 + ((max + min) / 2));
+ }
+ else
+ {
+ bSigned = false;
+ bDouble = min;
+ }
+
+ rExp = 0;
+
+ // M too big for 10 bit variable
+ while (mDouble > maxInt10)
+ {
+ if (rExp >= maxInt4)
+ {
+ phosphor::logging::log<phosphor::logging::level::DEBUG>(
+ "rExp Too big, Max and Min range too far",
+ phosphor::logging::entry("REXP=%d", rExp));
+ return false;
+ }
+ mDouble /= 10;
+ rExp++;
+ }
+
+ // M too small, loop until we lose less than 1 eight bit count of precision
+ while (((mDouble - floor(mDouble)) / mDouble) > (1.0 / 255))
+ {
+ if (rExp <= minInt4)
+ {
+ phosphor::logging::log<phosphor::logging::level::DEBUG>(
+ "rExp Too Small, Max and Min range too close");
+ return false;
+ }
+ // check to see if we reached the limit of where we can adjust back the
+ // B value
+ if (bDouble / std::pow(10, rExp + minInt4 - 1) > bDouble)
+ {
+ if (mDouble < 1.0)
+ {
+ phosphor::logging::log<phosphor::logging::level::DEBUG>(
+ "Could not find mValue and B value with enough "
+ "precision.");
+ return false;
+ }
+ break;
+ }
+ // can't multiply M any more, max precision reached
+ else if (mDouble * 10 > maxInt10)
+ {
+ break;
+ }
+ mDouble *= 10;
+ rExp--;
+ }
+
+ bDouble /= std::pow(10, rExp);
+ bExp = 0;
+
+ // B too big for 10 bit variable
+ while (bDouble > maxInt10 || bDouble < minInt10)
+ {
+ if (bExp >= maxInt4)
+ {
+ phosphor::logging::log<phosphor::logging::level::DEBUG>(
+ "bExp Too Big, Max and Min range need to be adjusted");
+ return false;
+ }
+ bDouble /= 10;
+ bExp++;
+ }
+
+ while (((fabs(bDouble) - floor(fabs(bDouble))) / fabs(bDouble)) >
+ (1.0 / 255))
+ {
+ if (bExp <= minInt4)
+ {
+ phosphor::logging::log<phosphor::logging::level::DEBUG>(
+ "bExp Too Small, Max and Min range need to be adjusted");
+ return false;
+ }
+ bDouble *= 10;
+ bExp -= 1;
+ }
+
+ mValue = static_cast<int16_t>(mDouble) & maxInt10;
+ bValue = static_cast<int16_t>(bDouble) & maxInt10;
+
+ return true;
+}
+
+static inline uint8_t
+ scaleIPMIValueFromDouble(const double value, const uint16_t mValue,
+ const int8_t rExp, const uint16_t bValue,
+ const int8_t bExp, const bool bSigned)
+{
+ uint32_t scaledValue =
+ (value - (bValue * std::pow(10, bExp) * std::pow(10, rExp))) /
+ (mValue * std::pow(10, rExp));
+
+ if (scaledValue > std::numeric_limits<uint8_t>::max() ||
+ scaledValue < std::numeric_limits<uint8_t>::lowest())
+ {
+ throw std::out_of_range("Value out of range");
+ }
+ if (bSigned)
+ {
+ return static_cast<int8_t>(scaledValue);
+ }
+ else
+ {
+ return static_cast<uint8_t>(scaledValue);
+ }
+}
+
+static inline uint8_t getScaledIPMIValue(const double value, const double max,
+ const double min)
+{
+ int16_t mValue = 0;
+ int8_t rExp = 0;
+ int16_t bValue = 0;
+ int8_t bExp = 0;
+ bool bSigned = 0;
+ bool result = 0;
+
+ result = getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned);
+ if (!result)
+ {
+ throw std::runtime_error("Illegal sensor attributes");
+ }
+ return scaleIPMIValueFromDouble(value, mValue, rExp, bValue, bExp, bSigned);
+}
+} // namespace ipmi