blob: da63e0d92807cc670cf3baf7cec15128ce4bd79b [file] [log] [blame]
/*
// 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.
*/
#pragma once
#include <sel_logger.hpp>
#include <sensorutils.hpp>
#include <string_view>
#include <variant>
enum class thresholdEventOffsets : uint8_t
{
lowerNonCritGoingLow = 0x00,
lowerCritGoingLow = 0x02,
upperNonCritGoingHigh = 0x07,
upperCritGoingHigh = 0x09,
};
static constexpr const uint8_t thresholdEventDataTriggerReadingByte2 = (1 << 6);
static constexpr const uint8_t thresholdEventDataTriggerReadingByte3 = (1 << 4);
static const std::string openBMCMessageRegistryVersion("0.1");
inline static sdbusplus::bus::match::match startThresholdEventMonitor(
std::shared_ptr<sdbusplus::asio::connection> conn)
{
auto thresholdEventMatcherCallback = [conn](
sdbusplus::message::message& msg) {
// This static set of std::pair<path, event> tracks asserted events to
// avoid duplicate logs or deasserts logged without an assert
static boost::container::flat_set<std::pair<std::string, std::string>>
assertedEvents;
// SEL event data is three bytes where 0xFF means unspecified
std::vector<uint8_t> eventData(selEvtDataMaxSize, 0xFF);
// Get the event type and assertion details from the message
std::string thresholdInterface;
boost::container::flat_map<std::string, std::variant<bool>>
propertiesChanged;
msg.read(thresholdInterface, propertiesChanged);
std::string event = propertiesChanged.begin()->first;
bool* pval = std::get_if<bool>(&propertiesChanged.begin()->second);
if (!pval)
{
std::cerr << "threshold event direction has invalid type\n";
return;
}
bool assert = *pval;
// Check the asserted events to determine if we should log this event
std::pair<std::string, std::string> pathAndEvent(
std::string(msg.get_path()), event);
if (assert)
{
// For asserts, add the event to the set and only log it if it's new
if (assertedEvents.insert(pathAndEvent).second == false)
{
// event is already in the set
return;
}
}
else
{
// For deasserts, remove the event and only log the deassert if it
// was asserted
if (assertedEvents.erase(pathAndEvent) == 0)
{
// asserted event was not in the set
return;
}
}
// Set the IPMI threshold event type based on the event details from the
// message
if (event == "CriticalAlarmLow")
{
eventData[0] =
static_cast<uint8_t>(thresholdEventOffsets::lowerCritGoingLow);
}
else if (event == "WarningAlarmLow")
{
eventData[0] = static_cast<uint8_t>(
thresholdEventOffsets::lowerNonCritGoingLow);
}
else if (event == "WarningAlarmHigh")
{
eventData[0] = static_cast<uint8_t>(
thresholdEventOffsets::upperNonCritGoingHigh);
}
else if (event == "CriticalAlarmHigh")
{
eventData[0] =
static_cast<uint8_t>(thresholdEventOffsets::upperCritGoingHigh);
}
// Indicate that bytes 2 and 3 are threshold sensor trigger values
eventData[0] |= thresholdEventDataTriggerReadingByte2 |
thresholdEventDataTriggerReadingByte3;
// Get the sensor reading to put in the event data
sdbusplus::message::message getSensorValue =
conn->new_method_call(msg.get_sender(), msg.get_path(),
"org.freedesktop.DBus.Properties", "GetAll");
getSensorValue.append("xyz.openbmc_project.Sensor.Value");
boost::container::flat_map<std::string, std::variant<double, int64_t>>
sensorValue;
try
{
sdbusplus::message::message getSensorValueResp =
conn->call(getSensorValue);
getSensorValueResp.read(sensorValue);
}
catch (sdbusplus::exception_t&)
{
std::cerr << "error getting sensor value from " << msg.get_path()
<< "\n";
return;
}
double max = 0;
auto findMax = sensorValue.find("MaxValue");
if (findMax != sensorValue.end())
{
max = std::visit(ipmi::VariantToDoubleVisitor(), findMax->second);
}
double min = 0;
auto findMin = sensorValue.find("MinValue");
if (findMin != sensorValue.end())
{
min = std::visit(ipmi::VariantToDoubleVisitor(), findMin->second);
}
double sensorVal = 0;
auto findVal = sensorValue.find("Value");
if (findVal != sensorValue.end())
{
sensorVal =
std::visit(ipmi::VariantToDoubleVisitor(), findVal->second);
}
double scale = 0;
auto findScale = sensorValue.find("Scale");
if (findScale != sensorValue.end())
{
scale =
std::visit(ipmi::VariantToDoubleVisitor(), findScale->second);
sensorVal *= std::pow(10, scale);
}
try
{
eventData[1] = ipmi::getScaledIPMIValue(sensorVal, max, min);
}
catch (const std::exception& e)
{
std::cerr << e.what();
eventData[1] = 0xFF;
}
// Get the threshold value to put in the event data
// Get the threshold parameter by removing the "Alarm" text from the
// event string
std::string alarm("Alarm");
if (std::string::size_type pos = event.find(alarm);
pos != std::string::npos)
{
event.erase(pos, alarm.length());
}
sdbusplus::message::message getThreshold =
conn->new_method_call(msg.get_sender(), msg.get_path(),
"org.freedesktop.DBus.Properties", "Get");
getThreshold.append(thresholdInterface, event);
std::variant<double, int64_t> thresholdValue;
try
{
sdbusplus::message::message getThresholdResp =
conn->call(getThreshold);
getThresholdResp.read(thresholdValue);
}
catch (sdbusplus::exception_t&)
{
std::cerr << "error getting sensor threshold from "
<< msg.get_path() << "\n";
return;
}
double thresholdVal =
std::visit(ipmi::VariantToDoubleVisitor(), thresholdValue);
if (findScale != sensorValue.end())
{
thresholdVal *= std::pow(10, scale);
}
try
{
eventData[2] = ipmi::getScaledIPMIValue(thresholdVal, max, min);
}
catch (const std::exception& e)
{
std::cerr << e.what();
eventData[2] = 0xFF;
}
// Construct a human-readable message of this event for the log
std::string_view sensorName(msg.get_path());
sensorName.remove_prefix(
std::min(sensorName.find_last_of("/") + 1, sensorName.size()));
std::string threshold;
std::string direction;
std::string redfishMessageID =
"OpenBMC." + openBMCMessageRegistryVersion;
if (event == "CriticalLow")
{
threshold = "critical low";
if (assert)
{
direction = "low";
redfishMessageID += ".SensorThresholdCriticalLowGoingLow";
}
else
{
direction = "high";
redfishMessageID += ".SensorThresholdCriticalLowGoingHigh";
}
}
else if (event == "WarningLow")
{
threshold = "warning low";
if (assert)
{
direction = "low";
redfishMessageID += ".SensorThresholdWarningLowGoingLow";
}
else
{
direction = "high";
redfishMessageID += ".SensorThresholdWarningLowGoingHigh";
}
}
else if (event == "WarningHigh")
{
threshold = "warning high";
if (assert)
{
direction = "high";
redfishMessageID += ".SensorThresholdWarningHighGoingHigh";
}
else
{
direction = "low";
redfishMessageID += ".SensorThresholdWarningHighGoingLow";
}
}
else if (event == "CriticalHigh")
{
threshold = "critical high";
if (assert)
{
direction = "high";
redfishMessageID += ".SensorThresholdCriticalHighGoingHigh";
}
else
{
direction = "low";
redfishMessageID += ".SensorThresholdCriticalHighGoingLow";
}
}
std::string journalMsg(std::string(sensorName) + " sensor crossed a " +
threshold + " threshold going " + direction +
". Reading=" + std::to_string(sensorVal) +
" Threshold=" + std::to_string(thresholdVal) +
".");
selAddSystemRecord(
journalMsg, std::string(msg.get_path()), eventData, assert,
selBMCGenID, "REDFISH_MESSAGE_ID=%s", redfishMessageID.c_str(),
"REDFISH_MESSAGE_ARGS=%.*s,%f,%f", sensorName.length(),
sensorName.data(), sensorVal, thresholdVal);
};
sdbusplus::bus::match::match thresholdEventMatcher(
static_cast<sdbusplus::bus::bus&>(*conn),
"type='signal',interface='org.freedesktop.DBus.Properties',member='"
"PropertiesChanged',arg0namespace='xyz.openbmc_project.Sensor."
"Threshold'",
std::move(thresholdEventMatcherCallback));
return thresholdEventMatcher;
}