dbus-sdr: Initial Dynamic Sensor Stack

Migrate intel-ipmi-oem dynamic sensor stack into
phosphor-host-ipmid for more general use.

The files are copied from
https://gerrit.openbmc-project.xyz/c/openbmc/intel-ipmi-oem/+/39743

https://gerrit.openbmc-project.xyz/plugins/gitiles/openbmc/intel-ipmi-oem/+/b910987a7d832e38e9342f0946aeb555a48f9cb0

Created `libdynamiccmds` to enable dynamic sensor stack.

Minor changes in the migration include:

1, Removing the use of `commandutils.hpp` in all files since it is only used
for
```
static constexpr bool debug = false;
```
It is added to `sdrutils.hpp` instead.

2, Update lastWriteAddr to size_t to match the vector.size() type
during comparison.

3, Renamed the sensorcommand unit test to sensorcommands_unitest.cpp

4, Removed unused variables.
  - sensorcommands
```
constexpr uint8_t thresholdMask = 0xFF;
```
  - sensorcommands_unitest
```
double yError = std::abs(y - yRoundtrip);
```
5, Removed Intel Specific Changes
  - Redfish logs
  - node manager/ME
  - BIOS to SEL event
6, Removed externing a global variable for sensorTree.
  - Replaced it with a method that returns a singleton
  - auto& sensorTree = getSensorTree(); for access
7, Replaced intel_oem namespace with dynamic_sensors

8, Removed types.hpp and use `ipmid/types.hpp` directly
  - Updated the types to match ipmid/types
  - Added Association and std::vector<Association>> under Value.

9, Add cpp files for sdrutils and sensorutils.

10, link libipmid as it is required for getManagedObjects needed
    by sensorcommands.cpp

Signed-off-by: Willy Tu <wltu@google.com>
Change-Id: If944620c895ecf4c9f4c3efe72479f4de276f4fb
Signed-off-by: Vijay Khemka <vijaykhemkalinux@gmail.com>
diff --git a/include/Makefile.am b/include/Makefile.am
index 08824c4..7c90f70 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -17,7 +17,11 @@
 	ipmid/utility.hpp \
 	ipmid/utils.hpp \
 	ipmid-host/cmd.hpp \
-	ipmid-host/cmd-utils.hpp
+	ipmid-host/cmd-utils.hpp \
+	dbus-sdr/sdrutils.hpp \
+	dbus-sdr/sensorcommands.hpp \
+	dbus-sdr/sensorutils.hpp \
+	dbus-sdr/storagecommands.hpp
 
 # Eventually we will split <ipmid/*> and <host-ipmid/*> headers
 # For now they will be the same during migration
diff --git a/include/dbus-sdr/sdrutils.hpp b/include/dbus-sdr/sdrutils.hpp
new file mode 100644
index 0000000..8d27c02
--- /dev/null
+++ b/include/dbus-sdr/sdrutils.hpp
@@ -0,0 +1,118 @@
+/*
+// 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/bimap.hpp>
+#include <boost/container/flat_map.hpp>
+#include <cstdio>
+#include <cstring>
+#include <exception>
+#include <filesystem>
+#include <ipmid/api.hpp>
+#include <ipmid/types.hpp>
+#include <map>
+#include <phosphor-logging/log.hpp>
+#include <sdbusplus/bus/match.hpp>
+#include <string>
+#include <vector>
+
+#pragma once
+
+static constexpr bool debug = false;
+
+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>;
+
+using SensorNumMap = boost::bimap<int, std::string>;
+
+static constexpr uint16_t maxSensorsPerLUN = 255;
+static constexpr uint16_t maxIPMISensors = (maxSensorsPerLUN * 3);
+static constexpr uint16_t lun1Sensor0 = 0x100;
+static constexpr uint16_t lun3Sensor0 = 0x300;
+static constexpr uint16_t invalidSensorNumber = 0xFFFF;
+static constexpr uint8_t reservedSensorNumber = 0xFF;
+
+namespace details
+{
+bool getSensorSubtree(std::shared_ptr<SensorSubTree>& subtree);
+
+bool getSensorNumMap(std::shared_ptr<SensorNumMap>& sensorNumMap);
+} // namespace details
+
+bool getSensorSubtree(SensorSubTree& subtree);
+
+struct CmpStr
+{
+    bool operator()(const char* a, const char* b) const
+    {
+        return std::strcmp(a, b) < 0;
+    }
+};
+
+enum class SensorTypeCodes : uint8_t
+{
+    reserved = 0x0,
+    temperature = 0x1,
+    voltage = 0x2,
+    current = 0x3,
+    fan = 0x4,
+    other = 0xB,
+};
+
+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}}};
+
+std::string getSensorTypeStringFromPath(const std::string& path);
+
+uint8_t getSensorTypeFromPath(const std::string& path);
+
+uint16_t getSensorNumberFromPath(const std::string& path);
+
+uint8_t getSensorEventTypeFromPath(const std::string& path);
+
+std::string getPathFromSensorNumber(uint16_t sensorNum);
+
+namespace ipmi
+{
+std::map<std::string, std::vector<std::string>>
+    getObjectInterfaces(const char* path);
+
+std::map<std::string, Value> getEntityManagerProperties(const char* path,
+                                                        const char* interface);
+
+const std::string* getSensorConfigurationInterface(
+    const std::map<std::string, std::vector<std::string>>&
+        sensorInterfacesResponse);
+
+void updateIpmiFromAssociation(const std::string& path,
+                               const DbusInterfaceMap& sensorMap,
+                               uint8_t& entityId, uint8_t& entityInstance);
+} // namespace ipmi
diff --git a/include/dbus-sdr/sensorcommands.hpp b/include/dbus-sdr/sensorcommands.hpp
new file mode 100644
index 0000000..b9c845f
--- /dev/null
+++ b/include/dbus-sdr/sensorcommands.hpp
@@ -0,0 +1,168 @@
+/*
+// 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 <dbus-sdr/sdrutils.hpp>
+
+#pragma pack(push, 1)
+
+struct SensorThresholdResp
+{
+    uint8_t readable;
+    uint8_t lowernc;
+    uint8_t lowercritical;
+    uint8_t lowernonrecoverable;
+    uint8_t uppernc;
+    uint8_t uppercritical;
+    uint8_t uppernonrecoverable;
+};
+
+#pragma pack(pop)
+
+enum class IPMIThresholdRespBits
+{
+    lowerNonCritical,
+    lowerCritical,
+    lowerNonRecoverable,
+    upperNonCritical,
+    upperCritical,
+    upperNonRecoverable
+};
+
+enum class IPMISensorReadingByte2 : uint8_t
+{
+    eventMessagesEnable = (1 << 7),
+    sensorScanningEnable = (1 << 6),
+    readingStateUnavailable = (1 << 5),
+};
+
+enum class IPMISensorReadingByte3 : uint8_t
+{
+    upperNonRecoverable = (1 << 5),
+    upperCritical = (1 << 4),
+    upperNonCritical = (1 << 3),
+    lowerNonRecoverable = (1 << 2),
+    lowerCritical = (1 << 1),
+    lowerNonCritical = (1 << 0),
+};
+
+enum class IPMISensorEventEnableByte2 : uint8_t
+{
+    eventMessagesEnable = (1 << 7),
+    sensorScanningEnable = (1 << 6),
+};
+
+enum class IPMISensorEventEnableThresholds : uint8_t
+{
+    nonRecoverableThreshold = (1 << 6),
+    criticalThreshold = (1 << 5),
+    nonCriticalThreshold = (1 << 4),
+    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 IPMIGetSensorEventEnableThresholds : uint8_t
+{
+    lowerNonCriticalGoingLow = 0,
+    lowerNonCriticalGoingHigh = 1,
+    lowerCriticalGoingLow = 2,
+    lowerCriticalGoingHigh = 3,
+    lowerNonRecoverableGoingLow = 4,
+    lowerNonRecoverableGoingHigh = 5,
+    upperNonCriticalGoingLow = 6,
+    upperNonCriticalGoingHigh = 7,
+    upperCriticalGoingLow = 8,
+    upperCriticalGoingHigh = 9,
+    upperNonRecoverableGoingLow = 10,
+    upperNonRecoverableGoingHigh = 11,
+};
+
+enum class IPMINetfnSensorCmds : ipmi_cmd_t
+{
+    ipmiCmdGetDeviceSDRInfo = 0x20,
+    ipmiCmdGetDeviceSDR = 0x21,
+    ipmiCmdReserveDeviceSDRRepo = 0x22,
+    ipmiCmdSetSensorThreshold = 0x26,
+    ipmiCmdGetSensorThreshold = 0x27,
+    ipmiCmdGetSensorEventEnable = 0x29,
+    ipmiCmdGetSensorEventStatus = 0x2B,
+    ipmiCmdGetSensorReading = 0x2D,
+    ipmiCmdGetSensorType = 0x2F,
+    ipmiCmdSetSensorReadingAndEventStatus = 0x30,
+};
+
+namespace ipmi
+{
+
+SensorSubTree& getSensorTree()
+{
+    static SensorSubTree sensorTree;
+    return sensorTree;
+}
+
+static ipmi_ret_t getSensorConnection(ipmi::Context::ptr ctx, uint8_t sensnum,
+                                      std::string& connection,
+                                      std::string& path)
+{
+    auto& sensorTree = getSensorTree();
+    if (sensorTree.empty() && !getSensorSubtree(sensorTree))
+    {
+        return IPMI_CC_RESPONSE_ERROR;
+    }
+
+    if (ctx == nullptr)
+    {
+        return IPMI_CC_RESPONSE_ERROR;
+    }
+
+    path = getPathFromSensorNumber((ctx->lun << 8) | sensnum);
+    if (path.empty())
+    {
+        return IPMI_CC_INVALID_FIELD_REQUEST;
+    }
+
+    for (const auto& sensor : sensorTree)
+    {
+        if (path == sensor.first)
+        {
+            connection = sensor.second.begin()->first;
+            break;
+        }
+    }
+
+    return 0;
+}
+
+struct IPMIThresholds
+{
+    std::optional<uint8_t> warningLow;
+    std::optional<uint8_t> warningHigh;
+    std::optional<uint8_t> criticalLow;
+    std::optional<uint8_t> criticalHigh;
+};
+
+} // namespace ipmi
diff --git a/include/dbus-sdr/sensorutils.hpp b/include/dbus-sdr/sensorutils.hpp
new file mode 100644
index 0000000..c68a449
--- /dev/null
+++ b/include/dbus-sdr/sensorutils.hpp
@@ -0,0 +1,39 @@
+/*
+// 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 <algorithm>
+#include <cmath>
+#include <iostream>
+
+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;
+
+bool getSensorAttributes(const double max, const double min, int16_t& mValue,
+                         int8_t& rExp, int16_t& bValue, int8_t& bExp,
+                         bool& bSigned);
+
+uint8_t scaleIPMIValueFromDouble(const double value, const int16_t mValue,
+                                 const int8_t rExp, const int16_t bValue,
+                                 const int8_t bExp, const bool bSigned);
+
+uint8_t getScaledIPMIValue(const double value, const double max,
+                           const double min);
+} // namespace ipmi
diff --git a/include/dbus-sdr/storagecommands.hpp b/include/dbus-sdr/storagecommands.hpp
new file mode 100644
index 0000000..d0fa110
--- /dev/null
+++ b/include/dbus-sdr/storagecommands.hpp
@@ -0,0 +1,113 @@
+/*
+// 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 "sensorhandler.hpp"
+
+#include <cstdint>
+
+#define USING_ENTITY_MANAGER_DECORATORS
+
+static constexpr uint8_t ipmiSdrVersion = 0x51;
+
+namespace dynamic_sensors::ipmi::sel
+{
+static constexpr uint8_t selOperationSupport = 0x02;
+static constexpr uint8_t systemEvent = 0x02;
+static constexpr size_t systemEventSize = 3;
+static constexpr uint8_t oemTsEventFirst = 0xC0;
+static constexpr uint8_t oemTsEventLast = 0xDF;
+static constexpr size_t oemTsEventSize = 9;
+static constexpr uint8_t oemEventFirst = 0xE0;
+static constexpr uint8_t oemEventLast = 0xFF;
+static constexpr size_t oemEventSize = 13;
+static constexpr uint8_t eventMsgRev = 0x04;
+} // namespace dynamic_sensors::ipmi::sel
+
+enum class SdrRepositoryInfoOps : uint8_t
+{
+    allocCommandSupported = 0x1,
+    reserveSDRRepositoryCommandSupported = 0x2,
+    partialAddSDRSupported = 0x4,
+    deleteSDRSupported = 0x8,
+    reserved = 0x10,
+    modalLSB = 0x20,
+    modalMSB = 0x40,
+    overflow = 0x80
+};
+
+enum class GetFRUAreaAccessType : uint8_t
+{
+    byte = 0x0,
+    words = 0x1
+};
+
+enum class SensorUnits : uint8_t
+{
+    unspecified = 0x0,
+    degreesC = 0x1,
+    volts = 0x4,
+    amps = 0x5,
+    watts = 0x6,
+    rpm = 0x12,
+};
+
+#pragma pack(push, 1)
+struct FRUHeader
+{
+    uint8_t commonHeaderFormat;
+    uint8_t internalOffset;
+    uint8_t chassisOffset;
+    uint8_t boardOffset;
+    uint8_t productOffset;
+    uint8_t multiRecordOffset;
+    uint8_t pad;
+    uint8_t checksum;
+};
+#pragma pack(pop)
+
+#pragma pack(push, 1)
+struct Type12Record
+{
+    get_sdr::SensorDataRecordHeader header;
+    uint8_t slaveAddress;
+    uint8_t channelNumber;
+    uint8_t powerStateNotification;
+    uint8_t deviceCapabilities;
+    uint24_t reserved;
+    uint8_t entityID;
+    uint8_t entityInstance;
+    uint8_t oem;
+    uint8_t typeLengthCode;
+    char name[16];
+};
+#pragma pack(pop)
+
+namespace ipmi
+{
+namespace storage
+{
+
+constexpr const size_t type12Count = 2;
+ipmi_ret_t getFruSdrs(ipmi::Context::ptr ctx, size_t index,
+                      get_sdr::SensorDataFruRecord& resp);
+
+ipmi_ret_t getFruSdrCount(ipmi::Context::ptr ctx, size_t& count);
+
+std::vector<uint8_t> getType12SDRs(uint16_t index, uint16_t recordId);
+std::vector<uint8_t> getNMDiscoverySDR(uint16_t index, uint16_t recordId);
+} // namespace storage
+} // namespace ipmi
diff --git a/include/ipmid/types.hpp b/include/ipmid/types.hpp
index fa4d58c..e62c819 100644
--- a/include/ipmid/types.hpp
+++ b/include/ipmid/types.hpp
@@ -16,8 +16,11 @@
 using DbusObjectInfo = std::pair<DbusObjectPath, DbusService>;
 using DbusProperty = std::string;
 
-using Value = std::variant<bool, uint8_t, int16_t, uint16_t, int32_t, uint32_t,
-                           int64_t, uint64_t, double, std::string>;
+using Association = std::tuple<std::string, std::string, std::string>;
+
+using Value =
+    std::variant<bool, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t,
+                 uint64_t, double, std::string, std::vector<Association>>;
 
 using PropertyMap = std::map<DbusProperty, Value>;