Add Intel-specific IPMI sensor commands

Also includes SDR storage commands that are required to support
the 'ipmitool sensor list' command.

Change-Id: Id1830097d93882114085fce723f0b92367b2db48
Signed-off-by: Jason M. Bills <jason.m.bills@linux.intel.com>
diff --git a/include/commandutils.hpp b/include/commandutils.hpp
new file mode 100644
index 0000000..e296015
--- /dev/null
+++ b/include/commandutils.hpp
@@ -0,0 +1,55 @@
+/*
+// Copyright (c) 2017 2018 Intel Corporation
+//
+// 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 <iostream>
+#include <sdbusplus/bus.hpp>
+
+static constexpr bool debug = false;
+
+inline static void printRegistration(unsigned int netfn, unsigned int cmd)
+{
+    if constexpr (debug)
+    {
+        std::cout << "Registering NetFn:[0x" << std::hex << std::uppercase
+                  << netfn << "], Cmd:[0x" << cmd << "]\n";
+    }
+}
+
+inline static void ipmiPrintAndRegister(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+                                        ipmi_context_t context,
+                                        ipmid_callback_t handler,
+                                        ipmi_cmd_privilege_t priv)
+{
+    printRegistration(netfn, cmd);
+    ipmi_register_callback(netfn, cmd, context, handler, priv);
+}
+
+inline static void printCommand(unsigned int netfn, unsigned int cmd)
+{
+    if constexpr (debug)
+    {
+        std::cout << "Executing NetFn:[0x" << std::hex << std::uppercase
+                  << netfn << "], Cmd:[0x" << cmd << "]\n";
+    }
+}
+
+namespace ipmi
+{
+using DbusVariant =
+    sdbusplus::message::variant<std::string, bool, uint8_t, uint16_t, int16_t,
+                                uint32_t, int32_t, uint64_t, int64_t, double>;
+} // namespace ipmi
diff --git a/include/sdrutils.hpp b/include/sdrutils.hpp
new file mode 100644
index 0000000..8b93e9b
--- /dev/null
+++ b/include/sdrutils.hpp
@@ -0,0 +1,165 @@
+/*
+// Copyright (c) 2018 Intel Corporation
+//
+// 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.
+*/
+
+#include <boost/algorithm/string.hpp>
+#include <boost/container/flat_map.hpp>
+#include <cstring>
+#include <phosphor-logging/log.hpp>
+#include <storagecommands.hpp>
+
+#pragma once
+
+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 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&)
+    {
+        phosphor::logging::log<phosphor::logging::level::ERR>(
+            "getSensorSubtree: Error calling mapper");
+        return false;
+    }
+    return true;
+}
+
+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*, SensorTypeCodes, CmpStr>
+    sensorTypes{{{"temperature", SensorTypeCodes::temperature},
+                 {"voltage", SensorTypeCodes::voltage},
+                 {"current", SensorTypeCodes::current},
+                 {"fan_tach", 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("/");
+    size_t typeStart = path.rfind("/", typeEnd - 1);
+    if (typeEnd != std::string::npos && typeStart != std::string::npos)
+    {
+        return path.substr(typeStart + 1, typeEnd - typeStart);
+    }
+    return path;
+}
+
+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 getSensorNumberFromPath(const std::string& path)
+{
+    SensorSubTree sensorTree;
+    if (!getSensorSubtree(sensorTree))
+        return 0xFF;
+    uint8_t sensorNum = 0xFF;
+
+    for (const auto& sensor : sensorTree)
+    {
+        sensorNum++;
+        if (sensor.first == path)
+        {
+            break;
+        }
+    }
+    return sensorNum;
+}
+
+inline static uint8_t getSensorEventTypeFromPath(const std::string& path)
+{
+    // TODO: Add support for additional reading types as needed
+    return 0x1; // reading type = threshold
+}
+
+inline static std::string getPathFromSensorNumber(uint8_t sensorNum)
+{
+    SensorSubTree sensorTree;
+    std::string path;
+    if (!getSensorSubtree(sensorTree))
+        return path;
+
+    if (sensorTree.size() < sensorNum)
+    {
+        return path;
+    }
+
+    uint8_t sensorIndex = sensorNum;
+    for (const auto& sensor : sensorTree)
+    {
+        if (sensorIndex-- == 0)
+        {
+            path = sensor.first;
+            break;
+        }
+    }
+
+    return path;
+}
diff --git a/include/sensorcommands.hpp b/include/sensorcommands.hpp
new file mode 100644
index 0000000..691fb11
--- /dev/null
+++ b/include/sensorcommands.hpp
@@ -0,0 +1,134 @@
+/*
+// Copyright (c) 2017 2018 Intel Corporation
+//
+// 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 <cstdint>
+
+#pragma pack(push, 1)
+struct SensorReadingResp
+{
+    uint8_t value;
+    uint8_t operation;
+    uint8_t indication[2];
+};
+
+struct SensorThresholdResp
+{
+    uint8_t readable;
+    uint8_t lowernc;
+    uint8_t lowercritical;
+    uint8_t lowernonrecoverable;
+    uint8_t uppernc;
+    uint8_t uppercritical;
+    uint8_t uppernonrecoverable;
+};
+
+struct SensorThresholdReq
+{
+    uint8_t sensorNum;
+    uint8_t mask;
+    uint8_t lowerNonCritical;
+    uint8_t lowerCritical;
+    uint8_t lowerNonRecoverable;
+    uint8_t upperNonCritical;
+    uint8_t upperCritical;
+    uint8_t upperNonRecoverable;
+};
+#pragma pack(pop)
+
+enum class SensorThresholdReqEnable : uint8_t
+{
+    setLowerNonCritical = 0x1,
+    setLowerCritical = 0x2,
+    setLowerNonRecoverable = 0x4,
+    setUpperNonCritical = 0x8,
+    setUpperCritical = 0x10,
+    setUpperNonRecoverable = 0x20
+};
+
+#pragma pack(push, 1)
+struct SensorEventEnableResp
+{
+    uint8_t enabled;
+    uint8_t assertionEnabledLSB;
+    uint8_t assertionEnabledMSB;
+    uint8_t deassertionEnabledLSB;
+    uint8_t deassertionEnabledMSB;
+};
+
+struct SensorEventStatusResp
+{
+    uint8_t enabled;
+    uint8_t assertionsLSB;
+    uint8_t assertionsMSB;
+    // deassertion events currently not supported
+    // uint8_t deassertionsLSB;
+    // uint8_t deassertionsMSB;
+};
+#pragma pack(pop)
+
+enum class IPMIhresholdRespBits
+{
+    lowerNonCritical,
+    lowerCritical,
+    lowerNonRecoverable,
+    upperNonCritical,
+    upperCritical,
+    upperNonRecoverable
+};
+
+enum class IPMISensorReadingByte2 : uint8_t
+{
+    eventMessagesEnable = (1 << 7),
+    sensorScanningEnable = (1 << 6),
+    readingStateUnavailable = (1 << 5),
+};
+
+enum class IPMISensorEventEnableByte2 : uint8_t
+{
+    eventMessagesEnable = (1 << 7),
+    sensorScanningEnable = (1 << 6),
+};
+
+enum class IPMISensorEventEnableThresholds : uint8_t
+{
+    upperNonRecoverableGoingHigh = (1 << 3),
+    upperNonRecoverableGoingLow = (1 << 2),
+    upperCriticalGoingHigh = (1 << 1),
+    upperCriticalGoingLow = (1 << 0),
+    upperNonCriticalGoingHigh = (1 << 7),
+    upperNonCriticalGoingLow = (1 << 6),
+    lowerNonRecoverableGoingHigh = (1 << 5),
+    lowerNonRecoverableGoingLow = (1 << 4),
+    lowerCriticalGoingHigh = (1 << 3),
+    lowerCriticalGoingLow = (1 << 2),
+    lowerNonCriticalGoingHigh = (1 << 1),
+    lowerNonCriticalGoingLow = (1 << 0),
+};
+
+enum class IPMINetfnSensorCmds : ipmi_cmd_t
+{
+    ipmiCmdGetDeviceSDRInfo = 0x20,
+    ipmiCmdGetDeviceSDR = 0x21,
+    ipmiCmdReserveDeviceSDRRepo = 0x22,
+    ipmiCmdGetSensorThreshold = 0x27,
+    ipmiCmdSetSensorThreshold = 0x28,
+    ipmiCmdGetSensorEventEnable = 0x29,
+    ipmiCmdGetSensorEventStatus = 0x2B,
+    ipmiCmdGetSensorReading = 0x2D,
+    ipmiCmdGetSensorType = 0x2F,
+    ipmiCmdSetSensorReadingAndEventStatus = 0x30,
+};
diff --git a/include/sensorutils.hpp b/include/sensorutils.hpp
new file mode 100644
index 0000000..7295a4b
--- /dev/null
+++ b/include/sensorutils.hpp
@@ -0,0 +1,28 @@
+/*
+// Copyright (c) 2017 2018 Intel Corporation
+//
+// 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 <cstdint>
+
+namespace ipmi
+{
+bool getSensorAttributes(const double maxValue, const double minValue,
+                         int16_t &mValue, int8_t &rExp, int16_t &bValue,
+                         int8_t &bExp, bool &bSigned);
+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);
+} // namespace ipmi
\ No newline at end of file
diff --git a/include/storagecommands.hpp b/include/storagecommands.hpp
new file mode 100644
index 0000000..ef32c74
--- /dev/null
+++ b/include/storagecommands.hpp
@@ -0,0 +1,118 @@
+/*
+// Copyright (c) 2017 2018 Intel Corporation
+//
+// 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 <cstdint>
+#include <phosphor-ipmi-host/sensorhandler.hpp>
+
+static constexpr uint8_t ipmiSdrVersion = 0x51;
+
+#pragma pack(push, 1)
+struct GetSDRInfoResp
+{
+    uint8_t sdrVersion;
+    uint8_t recordCountLS;
+    uint8_t recordCountMS;
+    uint8_t freeSpace[2];
+    uint32_t mostRecentAddition;
+    uint32_t mostRecentErase;
+    uint8_t operationSupport;
+};
+
+struct GetSDRReq
+{
+    uint16_t reservationID;
+    uint16_t recordID;
+    uint8_t offset;
+    uint8_t bytesToRead;
+};
+#pragma pack(pop)
+
+enum class SdrRepositoryInfoOps : uint8_t
+{
+    allocCommandSupported = 0x1,
+    reserveSDRRepositoryCommandSupported = 0x2,
+    partialAddSDRSupported = 0x4,
+    deleteSDRSupported = 0x8,
+    reserved = 0x10,
+    modalLSB = 0x20,
+    modalMSB = 0x40,
+    overflow = 0x80
+};
+
+#pragma pack(push, 1)
+struct GetAllocInfoResp
+{
+    uint8_t allocUnitsLSB;
+    uint8_t allocUnitsMSB;
+    uint8_t allocUnitSizeLSB;
+    uint8_t allocUnitSizeMSB;
+    uint8_t allocUnitFreeLSB;
+    uint8_t allocUnitFreeMSB;
+    uint8_t allocUnitLargestFreeLSB;
+    uint8_t allocUnitLargestFreeMSB;
+    uint8_t maxRecordSize;
+};
+#pragma pack(pop)
+
+enum class SensorTypeCodes : uint8_t
+{
+    reserved = 0x0,
+    temperature = 0x1,
+    voltage = 0x2,
+    current = 0x3,
+    fan = 0x4,
+    other = 0xB,
+};
+
+enum class SensorUnits : uint8_t
+{
+    unspecified = 0x0,
+    degreesC = 0x1,
+    volts = 0x4,
+    amps = 0x5,
+    watts = 0x6,
+    rpm = 0x12,
+};
+
+enum class IPMINetfnStorageCmds : ipmi_cmd_t
+{
+    ipmiCmdGetFRUInvAreaInfo = 0x10,
+    ipmiCmdReadFRUData = 0x11,
+    ipmiCmdWriteFRUData = 0x12,
+    ipmiCmdGetRepositoryInfo = 0x20,
+    ipmiCmdGetSDRAllocationInfo = 0x21,
+    ipmiCmdReserveSDR = 0x22,
+    ipmiCmdGetSDR = 0x23,
+    ipmiCmdGetSELInfo = 0x40,
+    ipmiCmdReserveSEL = 0x42,
+    ipmiCmdGetSELEntry = 0x43,
+    ipmiCmdAddSEL = 0x44,
+    ipmiCmdDeleteSEL = 0x46,
+    ipmiCmdClearSEL = 0x47,
+    ipmiCmdGetSELTime = 0x48,
+    ipmiCmdSetSELTime = 0x49,
+};
+
+namespace ipmi
+{
+namespace storage
+{
+ipmi_ret_t getFruSdrs(size_t index, get_sdr::SensorDataFruRecord& resp);
+
+ipmi_ret_t getFruSdrCount(size_t& count);
+} // namespace storage
+} // namespace ipmi