Add Ipmb Sensor
Add daemon responsible for polling ipmb for sensor
readings. Currently base ME and VR bridge sensors
are supported. This daemon uses ipmbd to get sensor
readings.
Tested-by: Saw correct readings on d-bus and verified
sensor list.
Change-Id: I71c216ae57567470d42180dce76aba8f69ecb50e
Signed-off-by: James Feist <james.feist@linux.intel.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a31dda5..ba3eecb 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -31,6 +31,8 @@
set (EXIT_AIR_SRC_FILES src/Utils.cpp src/Thresholds.cpp)
+set (IPMB_SRC_FILES src/Utils.cpp src/Thresholds.cpp)
+
set (EXTERNAL_PACKAGES Boost sdbusplus-project nlohmann-json)
set (SENSOR_LINK_LIBS -lsystemd stdc++fs sdbusplus)
@@ -126,12 +128,18 @@
add_dependencies (exitairtempsensor sdbusplus-project)
target_link_libraries (exitairtempsensor ${SENSOR_LINK_LIBS})
+add_executable (ipmbsensor src/IpmbSensor.cpp
+ ${IPMB_SRC_FILES})
+add_dependencies (ipmbsensor sdbusplus)
+target_link_libraries (ipmbsensor ${SENSOR_LINK_LIBS})
+
if (NOT YOCTO)
add_dependencies (fansensor ${EXTERNAL_PACKAGES})
add_dependencies (hwmontempsensor ${EXTERNAL_PACKAGES})
add_dependencies (adcsensor ${EXTERNAL_PACKAGES})
add_dependencies (cpusensor ${EXTERNAL_PACKAGES})
add_dependencies (exitairtempsensor ${EXTERNAL_PACKAGES})
+ add_dependencies (ipmbsensor ${EXTERNAL_PACKAGES})
endif ()
set (
@@ -143,6 +151,6 @@
${PROJECT_SOURCE_DIR}/service_files/xyz.openbmc_project.exitairsensor.service
)
-install (TARGETS fansensor hwmontempsensor cpusensor adcsensor
+install (TARGETS fansensor hwmontempsensor cpusensor adcsensor ipmbsensor
exitairtempsensor DESTINATION sbin)
install (FILES ${SERVICE_FILES} DESTINATION /lib/systemd/system/)
diff --git a/include/ADCSensor.hpp b/include/ADCSensor.hpp
index 2b5cdd4..2397093 100644
--- a/include/ADCSensor.hpp
+++ b/include/ADCSensor.hpp
@@ -4,11 +4,6 @@
#include <sdbusplus/asio/object_server.hpp>
#include <sensor.hpp>
-enum class PowerState : bool
-{
- on,
- always
-};
class ADCSensor : public Sensor
{
public:
diff --git a/include/IpmbSensor.hpp b/include/IpmbSensor.hpp
new file mode 100644
index 0000000..d5b0290
--- /dev/null
+++ b/include/IpmbSensor.hpp
@@ -0,0 +1,49 @@
+#pragma once
+#include "sensor.hpp"
+
+#include <boost/asio/deadline_timer.hpp>
+#include <boost/container/flat_map.hpp>
+#include <chrono>
+#include <limits>
+#include <vector>
+
+enum class IpmbType
+{
+ meSensor,
+ PXE1410CVR,
+ IR38363VR,
+ mpsVR
+};
+
+struct IpmbSensor : public Sensor
+{
+ IpmbSensor(std::shared_ptr<sdbusplus::asio::connection> &conn,
+ boost::asio::io_service &io, const std::string &name,
+ const std::string &sensorConfiguration,
+ sdbusplus::asio::object_server &objectServer,
+ std::vector<thresholds::Threshold> &&thresholds,
+ uint8_t deviceAddress);
+ ~IpmbSensor();
+
+ void checkThresholds(void) override;
+ void read(void);
+ void init(void);
+ void loadDefaults(void);
+
+ IpmbType type;
+ uint8_t commandAddress;
+ uint8_t netfn;
+ uint8_t command;
+ uint8_t deviceAddress;
+ std::vector<uint8_t> commandData;
+ std::optional<uint8_t> initCommand;
+ std::vector<uint8_t> initData;
+
+ // to date all ipmb sensors are power on only
+ PowerState readState = PowerState::on;
+
+ private:
+ sdbusplus::asio::object_server &objectServer;
+ std::shared_ptr<sdbusplus::asio::connection> dbusConnection;
+ boost::asio::deadline_timer waitTimer;
+};
\ No newline at end of file
diff --git a/include/Utils.hpp b/include/Utils.hpp
index 006c2bc..598662a 100644
--- a/include/Utils.hpp
+++ b/include/Utils.hpp
@@ -45,6 +45,12 @@
void findLimits(std::pair<double, double>& limits,
const SensorBaseConfiguration* data);
+enum class PowerState : bool
+{
+ on,
+ always
+};
+
template <typename T>
inline T loadVariant(
const boost::container::flat_map<std::string, BasicVariantType>& data,
@@ -61,6 +67,11 @@
return sdbusplus::message::variant_ns::visit(VariantToDoubleVisitor(),
it->second);
}
+ else if constexpr (std::is_unsigned_v<T>)
+ {
+ return sdbusplus::message::variant_ns::visit(
+ VariantToUnsignedIntVisitor(), it->second);
+ }
else if constexpr (std::is_same_v<T, std::string>)
{
return sdbusplus::message::variant_ns::visit(VariantToStringVisitor(),
diff --git a/src/IpmbSensor.cpp b/src/IpmbSensor.cpp
new file mode 100644
index 0000000..71d4a5b
--- /dev/null
+++ b/src/IpmbSensor.cpp
@@ -0,0 +1,377 @@
+/*
+// Copyright (c) 2019 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 "IpmbSensor.hpp"
+
+#include "Utils.hpp"
+#include "VariantVisitors.hpp"
+
+#include <math.h>
+
+#include <boost/algorithm/string.hpp>
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/algorithm/string/replace.hpp>
+#include <chrono>
+#include <iostream>
+#include <limits>
+#include <numeric>
+#include <sdbusplus/asio/connection.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+#include <vector>
+
+constexpr const bool debug = false;
+
+constexpr const char* configInterface =
+ "xyz.openbmc_project.Configuration.IpmbSensor";
+static constexpr double ipmbMaxReading = 0xFF;
+static constexpr double ipmbMinReading = 0;
+
+static constexpr uint8_t meAddress = 1;
+static constexpr uint8_t lun = 0;
+
+using IpmbMethodType =
+ std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>;
+
+IpmbSensor::IpmbSensor(std::shared_ptr<sdbusplus::asio::connection>& conn,
+ boost::asio::io_service& io,
+ const std::string& sensorName,
+ const std::string& sensorConfiguration,
+ sdbusplus::asio::object_server& objectServer,
+ std::vector<thresholds::Threshold>&& thresholdData,
+ uint8_t deviceAddress) :
+ Sensor(boost::replace_all_copy(sensorName, " ", "_"),
+ "" /* todo: remove arg from base*/, std::move(thresholdData),
+ sensorConfiguration, "xyz.openbmc_project.Configuration.ExitAirTemp",
+ ipmbMaxReading, ipmbMinReading),
+ objectServer(objectServer), dbusConnection(conn), waitTimer(io),
+ deviceAddress(deviceAddress)
+{
+ sensorInterface = objectServer.add_interface(
+ "/xyz/openbmc_project/sensors/temperature/" + name,
+ "xyz.openbmc_project.Sensor.Value");
+
+ if (thresholds::hasWarningInterface(thresholds))
+ {
+ thresholdInterfaceWarning = objectServer.add_interface(
+ "/xyz/openbmc_project/sensors/temperature/" + name,
+ "xyz.openbmc_project.Sensor.Threshold.Warning");
+ }
+ if (thresholds::hasCriticalInterface(thresholds))
+ {
+ thresholdInterfaceCritical = objectServer.add_interface(
+ "/xyz/openbmc_project/sensors/temperature/" + name,
+ "xyz.openbmc_project.Sensor.Threshold.Critical");
+ }
+ setupPowerMatch(conn);
+}
+
+IpmbSensor::~IpmbSensor()
+{
+ waitTimer.cancel();
+ objectServer.remove_interface(thresholdInterfaceWarning);
+ objectServer.remove_interface(thresholdInterfaceCritical);
+ objectServer.remove_interface(sensorInterface);
+}
+
+void IpmbSensor::init(void)
+{
+ setInitialProperties(dbusConnection);
+ loadDefaults();
+ if (initCommand)
+ {
+ dbusConnection->async_method_call(
+ [this](boost::system::error_code ec,
+ const IpmbMethodType& response) {
+ const int& status = std::get<0>(response);
+
+ if (ec || status)
+ {
+ std::cerr
+ << "Error setting init command for device: " << name
+ << "\n";
+ }
+ read();
+ },
+ "xyz.openbmc_project.Ipmi.Channel.Ipmb",
+ "/xyz/openbmc_project/Ipmi/Channel/Ipmb", "org.openbmc.Ipmb",
+ "sendRequest", commandAddress, netfn, lun, *initCommand, initData);
+ }
+ else
+ {
+ read();
+ }
+}
+
+void IpmbSensor::loadDefaults()
+{
+ if (type == IpmbType::meSensor)
+ {
+ commandAddress = meAddress;
+ netfn = 0x4; // sensor
+ command = 0x2d; // get sensor reading
+ commandData = {deviceAddress};
+ }
+ else if (type == IpmbType::PXE1410CVR)
+ {
+ commandAddress = meAddress;
+ netfn = 0x2e; // me bridge
+ command = 0xd9; // send raw pmbus
+ initCommand = 0xd9; // send raw pmbus
+ commandData = {0x57, 0x01, 0x00, 0x16, 0x03, deviceAddress, 00,
+ 0x00, 0x00, 0x00, 0x01, 0x02, 0x29};
+ initData = {0x57, 0x01, 0x00, 0x14, 0x03, deviceAddress, 0x00,
+ 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x60};
+ }
+ else if (type == IpmbType::IR38363VR)
+ {
+ commandAddress = meAddress;
+ netfn = 0x2e; // me bridge
+ command = 0xd9; // send raw pmbus
+ commandData = {0x57, 0x01, 0x00, 0x16, 0x03, deviceAddress, 00,
+ 0x00, 0x00, 0x00, 0x01, 0x02, 0x8D};
+ }
+ else if (type == IpmbType::mpsVR)
+ {
+ commandAddress = meAddress;
+ netfn = 0x2e; // me bridge
+ command = 0xd9; // send raw pmbus
+ initCommand = 0xd9; // send raw pmbus
+ commandData = {0x57, 0x01, 0x00, 0x16, 0x3, deviceAddress, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x02, 0x8d};
+ initData = {0x57, 0x01, 0x00, 0x14, 0x03, deviceAddress, 0x00,
+ 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00};
+ }
+ else
+ {
+ throw std::runtime_error("Invalid sensor type");
+ }
+}
+
+void IpmbSensor::checkThresholds(void)
+{
+ if (readState == PowerState::on && !isPowerOn())
+ {
+ return;
+ }
+ thresholds::checkThresholds(this);
+}
+
+void IpmbSensor::read(void)
+{
+ static constexpr size_t pollTime = 1; // in seconds
+
+ waitTimer.expires_from_now(boost::posix_time::seconds(pollTime));
+ waitTimer.async_wait([this](const boost::system::error_code& ec) {
+ if (ec == boost::asio::error::operation_aborted)
+ {
+ return; // we're being canceled
+ }
+ if (!isPowerOn() && readState == PowerState::on)
+ {
+ updateValue(0);
+ read();
+ return;
+ }
+ dbusConnection->async_method_call(
+ [this](boost::system::error_code ec,
+ const IpmbMethodType& response) {
+ const int& status = std::get<0>(response);
+ if (ec || status)
+ {
+ std::cerr << "Error reading from device: " << name << "\n";
+ updateValue(0);
+ read();
+ return;
+ }
+ if (!isPowerOn() && readState == PowerState::on)
+ {
+ updateValue(0);
+ read();
+ return;
+ }
+ const std::vector<uint8_t>& data = std::get<5>(response);
+ if constexpr (debug)
+ {
+ std::cout << name << ": ";
+ for (size_t d : data)
+ {
+ std::cout << d << " ";
+ }
+ std::cout << "\n";
+ }
+ uint16_t value = 0;
+ if (type == IpmbType::meSensor)
+ {
+ if (data.empty())
+ {
+ std::cerr << "Invalid data from device: " << name
+ << "\n";
+ read();
+ return;
+ }
+ value = data[0];
+ }
+ else if (type == IpmbType::PXE1410CVR ||
+ type == IpmbType::IR38363VR)
+ {
+ if (data.size() < 4)
+ {
+ std::cerr << "Invalid data from device: " << name
+ << "\n";
+ read();
+ return;
+ }
+ // format based on the 11 bit linear data format
+ value = ((data[4] << 8) | data[3]) >> 3;
+ }
+ else if (type == IpmbType::mpsVR)
+ {
+ if (data.size() < 4)
+ {
+ std::cerr << "Invalid data from device: " << name
+ << "\n";
+ read();
+ return;
+ }
+ value = data[3];
+ }
+ else
+ {
+ throw std::runtime_error("Invalid sensor type");
+ }
+ updateValue(value);
+ read();
+ },
+ "xyz.openbmc_project.Ipmi.Channel.Ipmb",
+ "/xyz/openbmc_project/Ipmi/Channel/Ipmb", "org.openbmc.Ipmb",
+ "sendRequest", commandAddress, netfn, lun, command, commandData);
+ });
+}
+void createSensors(
+ boost::asio::io_service& io, sdbusplus::asio::object_server& objectServer,
+ boost::container::flat_map<std::string, std::unique_ptr<IpmbSensor>>&
+ sensors,
+ std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
+{
+ if (!dbusConnection)
+ {
+ std::cerr << "Connection not created\n";
+ return;
+ }
+ dbusConnection->async_method_call(
+ [&](boost::system::error_code ec, const ManagedObjectType& resp) {
+ if (ec)
+ {
+ std::cerr << "Error contacting entity manager\n";
+ return;
+ }
+ for (const auto& pathPair : resp)
+ {
+ for (const auto& entry : pathPair.second)
+ {
+ if (entry.first != configInterface)
+ {
+ continue;
+ }
+ std::string name =
+ loadVariant<std::string>(entry.second, "Name");
+
+ std::vector<thresholds::Threshold> sensorThresholds;
+ if (!parseThresholdsFromConfig(pathPair.second,
+ sensorThresholds))
+ {
+ std::cerr << "error populating thresholds for " << name
+ << "\n";
+ }
+ uint8_t deviceAddress =
+ loadVariant<uint8_t>(entry.second, "Address");
+
+ std::string sensorClass =
+ loadVariant<std::string>(entry.second, "Class");
+ auto& sensor = sensors[name];
+ sensor = std::make_unique<IpmbSensor>(
+ dbusConnection, io, name, pathPair.first, objectServer,
+ std::move(sensorThresholds), deviceAddress);
+
+ if (sensorClass == "PxeBridgeTemp")
+ {
+ sensor->type = IpmbType::PXE1410CVR;
+ }
+ else if (sensorClass == "IRBridgeTemp")
+ {
+ sensor->type = IpmbType::IR38363VR;
+ }
+ else if (sensorClass == "MpsBridgeTemp")
+ {
+ sensor->type = IpmbType::mpsVR;
+ }
+ else if (sensorClass == "METemp")
+ {
+ sensor->type = IpmbType::meSensor;
+ }
+ else
+ {
+ std::cerr << "Invalid class " << sensorClass << "\n";
+ continue;
+ }
+ sensor->init();
+ }
+ }
+ },
+ entityManagerName, "/", "org.freedesktop.DBus.ObjectManager",
+ "GetManagedObjects");
+}
+
+int main(int argc, char** argv)
+{
+
+ boost::asio::io_service io;
+ auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
+ systemBus->request_name("xyz.openbmc_project.IpmbSensor");
+ sdbusplus::asio::object_server objectServer(systemBus);
+ boost::container::flat_map<std::string, std::unique_ptr<IpmbSensor>>
+ sensors;
+
+ io.post([&]() { createSensors(io, objectServer, sensors, systemBus); });
+
+ boost::asio::deadline_timer configTimer(io);
+
+ std::function<void(sdbusplus::message::message&)> eventHandler =
+ [&](sdbusplus::message::message& message) {
+ configTimer.expires_from_now(boost::posix_time::seconds(1));
+ // create a timer because normally multiple properties change
+ configTimer.async_wait([&](const boost::system::error_code& ec) {
+ if (ec == boost::asio::error::operation_aborted)
+ {
+ return; // we're being canceled
+ }
+ createSensors(io, objectServer, sensors, systemBus);
+ if (sensors.empty())
+ {
+ std::cout << "Configuration not detected\n";
+ }
+ });
+ };
+
+ sdbusplus::bus::match::match match(
+ static_cast<sdbusplus::bus::bus&>(*systemBus),
+ "type='signal',member='PropertiesChanged',path_namespace='" +
+ std::string(inventoryPath) + "',arg0namespace='" + configInterface +
+ "'",
+ eventHandler);
+
+ io.run();
+}
diff --git a/src/TachSensor.cpp b/src/TachSensor.cpp
index 132ca69..d16c36b 100644
--- a/src/TachSensor.cpp
+++ b/src/TachSensor.cpp
@@ -64,7 +64,7 @@
"xyz.openbmc_project.Sensor.Threshold.Critical");
}
setInitialProperties(conn);
- setupPowerMatch(conn); // first call initializes
+ setupPowerMatch(conn);
setupRead();
}
diff --git a/src/Utils.cpp b/src/Utils.cpp
index 1a4210d..52f5d1d 100644
--- a/src/Utils.cpp
+++ b/src/Utils.cpp
@@ -29,8 +29,8 @@
const static constexpr char* powerObjectName =
"/xyz/openbmc_project/Chassis/Control/Power0";
-bool powerStatusOn = false;
-std::unique_ptr<sdbusplus::bus::match::match> powerMatch = nullptr;
+static bool powerStatusOn = false;
+static std::unique_ptr<sdbusplus::bus::match::match> powerMatch = nullptr;
bool getSensorConfiguration(
const std::string& type,