blob: 74cc86ea908185d7d004589c55e8093d64547121 [file] [log] [blame]
Jason M. Bills5e049d32018-10-19 12:59:38 -07001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16
17#pragma once
18#include <sel_logger.hpp>
19#include <sensorutils.hpp>
Jason M. Bills2b9704d2018-11-02 13:12:09 -070020#include <string_view>
Jason M. Bills2bc9f0d2019-03-27 11:13:12 -070021#include <variant>
Jason M. Bills5e049d32018-10-19 12:59:38 -070022
23enum class thresholdEventOffsets : uint8_t
24{
25 lowerNonCritGoingLow = 0x00,
26 lowerCritGoingLow = 0x02,
27 upperNonCritGoingHigh = 0x07,
28 upperCritGoingHigh = 0x09,
29};
30
31static constexpr const uint8_t thresholdEventDataTriggerReadingByte2 = (1 << 6);
32static constexpr const uint8_t thresholdEventDataTriggerReadingByte3 = (1 << 4);
33
Jason M. Bills2b9704d2018-11-02 13:12:09 -070034static const std::string openBMCMessageRegistryVersion("0.1");
35
Jason M. Bills5e049d32018-10-19 12:59:38 -070036inline static sdbusplus::bus::match::match startThresholdEventMonitor(
37 std::shared_ptr<sdbusplus::asio::connection> conn)
38{
39 auto thresholdEventMatcherCallback = [conn](
40 sdbusplus::message::message &msg) {
41 // This static set of std::pair<path, event> tracks asserted events to
42 // avoid duplicate logs or deasserts logged without an assert
43 static boost::container::flat_set<std::pair<std::string, std::string>>
44 assertedEvents;
45 // SEL event data is three bytes where 0xFF means unspecified
46 std::vector<uint8_t> eventData(selEvtDataMaxSize, 0xFF);
47
48 // Get the event type and assertion details from the message
49 std::string thresholdInterface;
Jason M. Bills2bc9f0d2019-03-27 11:13:12 -070050 boost::container::flat_map<std::string, std::variant<bool>>
Jason M. Bills5e049d32018-10-19 12:59:38 -070051 propertiesChanged;
52 msg.read(thresholdInterface, propertiesChanged);
53 std::string event = propertiesChanged.begin()->first;
Jason M. Bills2bc9f0d2019-03-27 11:13:12 -070054 bool *pval = std::get_if<bool>(&propertiesChanged.begin()->second);
Jason M. Bills5e049d32018-10-19 12:59:38 -070055 if (!pval)
56 {
57 std::cerr << "threshold event direction has invalid type\n";
58 return;
59 }
60 bool assert = *pval;
61
62 // Check the asserted events to determine if we should log this event
63 std::pair<std::string, std::string> pathAndEvent(
64 std::string(msg.get_path()), event);
65 if (assert)
66 {
67 // For asserts, add the event to the set and only log it if it's new
68 if (assertedEvents.insert(pathAndEvent).second == false)
69 {
70 // event is already in the set
71 return;
72 }
73 }
74 else
75 {
76 // For deasserts, remove the event and only log the deassert if it
77 // was asserted
78 if (assertedEvents.erase(pathAndEvent) == 0)
79 {
80 // asserted event was not in the set
81 return;
82 }
83 }
84
85 // Set the IPMI threshold event type based on the event details from the
86 // message
87 if (event == "CriticalAlarmLow")
88 {
89 eventData[0] =
90 static_cast<uint8_t>(thresholdEventOffsets::lowerCritGoingLow);
91 }
92 else if (event == "WarningAlarmLow")
93 {
94 eventData[0] = static_cast<uint8_t>(
95 thresholdEventOffsets::lowerNonCritGoingLow);
96 }
97 else if (event == "WarningAlarmHigh")
98 {
99 eventData[0] = static_cast<uint8_t>(
100 thresholdEventOffsets::upperNonCritGoingHigh);
101 }
102 else if (event == "CriticalAlarmHigh")
103 {
104 eventData[0] =
105 static_cast<uint8_t>(thresholdEventOffsets::upperCritGoingHigh);
106 }
107 // Indicate that bytes 2 and 3 are threshold sensor trigger values
108 eventData[0] |= thresholdEventDataTriggerReadingByte2 |
109 thresholdEventDataTriggerReadingByte3;
110
111 // Get the sensor reading to put in the event data
112 sdbusplus::message::message getSensorValue =
113 conn->new_method_call(msg.get_sender(), msg.get_path(),
114 "org.freedesktop.DBus.Properties", "GetAll");
115 getSensorValue.append("xyz.openbmc_project.Sensor.Value");
Jason M. Bills2bc9f0d2019-03-27 11:13:12 -0700116 boost::container::flat_map<std::string, std::variant<double>>
Jason M. Bills5e049d32018-10-19 12:59:38 -0700117 sensorValue;
118 try
119 {
120 sdbusplus::message::message getSensorValueResp =
121 conn->call(getSensorValue);
122 getSensorValueResp.read(sensorValue);
123 }
124 catch (sdbusplus::exception_t &)
125 {
126 std::cerr << "error getting sensor value from " << msg.get_path()
127 << "\n";
128 return;
129 }
Jason M. Bills2bc9f0d2019-03-27 11:13:12 -0700130 double max =
131 std::visit(ipmi::VariantToDoubleVisitor(), sensorValue["MaxValue"]);
132 double min =
133 std::visit(ipmi::VariantToDoubleVisitor(), sensorValue["MinValue"]);
134 double sensorVal =
135 std::visit(ipmi::VariantToDoubleVisitor(), sensorValue["Value"]);
Jason M. Bills5e049d32018-10-19 12:59:38 -0700136 try
137 {
138 eventData[1] = ipmi::getScaledIPMIValue(sensorVal, max, min);
139 }
140 catch (std::runtime_error &e)
141 {
142 std::cerr << e.what();
143 eventData[1] = 0xFF;
144 }
145
146 // Get the threshold value to put in the event data
147 // Get the threshold parameter by removing the "Alarm" text from the
148 // event string
149 std::string alarm("Alarm");
150 if (std::string::size_type pos = event.find(alarm);
151 pos != std::string::npos)
152 {
153 event.erase(pos, alarm.length());
154 }
155 sdbusplus::message::message getThreshold =
156 conn->new_method_call(msg.get_sender(), msg.get_path(),
157 "org.freedesktop.DBus.Properties", "Get");
158 getThreshold.append(thresholdInterface, event);
Jason M. Bills2bc9f0d2019-03-27 11:13:12 -0700159 std::variant<double> thresholdValue;
Jason M. Bills5e049d32018-10-19 12:59:38 -0700160 try
161 {
162 sdbusplus::message::message getThresholdResp =
163 conn->call(getThreshold);
164 getThresholdResp.read(thresholdValue);
165 }
166 catch (sdbusplus::exception_t &)
167 {
168 std::cerr << "error getting sensor threshold from "
169 << msg.get_path() << "\n";
170 return;
171 }
Jason M. Bills2bc9f0d2019-03-27 11:13:12 -0700172 double thresholdVal =
173 std::visit(ipmi::VariantToDoubleVisitor(), thresholdValue);
Jason M. Bills5e049d32018-10-19 12:59:38 -0700174 try
175 {
176 eventData[2] = ipmi::getScaledIPMIValue(thresholdVal, max, min);
177 }
178 catch (std::runtime_error &e)
179 {
180 std::cerr << e.what();
181 eventData[2] = 0xFF;
182 }
183
184 // Construct a human-readable message of this event for the log
Jason M. Bills2b9704d2018-11-02 13:12:09 -0700185 std::string_view sensorName(msg.get_path());
Jason M. Bills5e049d32018-10-19 12:59:38 -0700186 sensorName.remove_prefix(
187 std::min(sensorName.find_last_of("/") + 1, sensorName.size()));
Jason M. Bills2b9704d2018-11-02 13:12:09 -0700188
189 std::string threshold;
190 std::string direction;
191 std::string redfishMessageID;
192 if (event == "CriticalLow")
193 {
194 threshold = "critical low";
195 if (assert)
196 {
197 direction = "low";
198 redfishMessageID = "OpenBMC." + openBMCMessageRegistryVersion +
199 ".SensorThresholdCriticalLowGoingLow";
200 }
201 else
202 {
203 direction = "high";
204 redfishMessageID = "OpenBMC." + openBMCMessageRegistryVersion +
205 ".SensorThresholdCriticalLowGoingHigh";
206 }
207 }
208 else if (event == "WarningLow")
209 {
210 threshold = "warning low";
211 if (assert)
212 {
213 direction = "low";
214 redfishMessageID = "OpenBMC." + openBMCMessageRegistryVersion +
215 ".SensorThresholdWarningLowGoingLow";
216 }
217 else
218 {
219 direction = "high";
220 redfishMessageID = "OpenBMC." + openBMCMessageRegistryVersion +
221 ".SensorThresholdWarningLowGoingHigh";
222 }
223 }
224 else if (event == "WarningHigh")
225 {
226 threshold = "warning high";
227 if (assert)
228 {
229 direction = "high";
230 redfishMessageID = "OpenBMC." + openBMCMessageRegistryVersion +
231 ".SensorThresholdWarningHighGoingHigh";
232 }
233 else
234 {
235 direction = "low";
236 redfishMessageID = "OpenBMC." + openBMCMessageRegistryVersion +
237 ".SensorThresholdWarningHighGoingLow";
238 }
239 }
240 else if (event == "CriticalHigh")
241 {
242 threshold = "critical high";
243 if (assert)
244 {
245 direction = "high";
246 redfishMessageID = "OpenBMC." + openBMCMessageRegistryVersion +
247 ".SensorThresholdCriticalHighGoingHigh";
248 }
249 else
250 {
251 direction = "low";
252 redfishMessageID = "OpenBMC." + openBMCMessageRegistryVersion +
253 ".SensorThresholdCriticalHighGoingLow";
254 }
255 }
256
257 std::string journalMsg(std::string(sensorName) + " sensor crossed a " +
258 threshold + " threshold going " + direction +
Jason M. Bills5e049d32018-10-19 12:59:38 -0700259 ". Reading=" + std::to_string(sensorVal) +
Jason M. Bills2b9704d2018-11-02 13:12:09 -0700260 " Threshold=" + std::to_string(thresholdVal) +
261 ".");
Jason M. Bills5e049d32018-10-19 12:59:38 -0700262
263 selAddSystemRecord(journalMsg, std::string(msg.get_path()), eventData,
Jason M. Bills2b9704d2018-11-02 13:12:09 -0700264 assert, selBMCGenID, "REDFISH_MESSAGE_ID=%.*s",
265 redfishMessageID.length(), redfishMessageID.data(),
266 "REDFISH_MESSAGE_ARG_1=%.*s", sensorName.length(),
267 sensorName.data(), "REDFISH_MESSAGE_ARG_2=%f",
268 sensorVal, "REDFISH_MESSAGE_ARG_3=%f", thresholdVal);
Jason M. Bills5e049d32018-10-19 12:59:38 -0700269 };
270 sdbusplus::bus::match::match thresholdEventMatcher(
271 static_cast<sdbusplus::bus::bus &>(*conn),
272 "type='signal',interface='org.freedesktop.DBus.Properties',member='"
273 "PropertiesChanged',arg0namespace='xyz.openbmc_project.Sensor."
274 "Threshold'",
275 std::move(thresholdEventMatcherCallback));
276 return thresholdEventMatcher;
277}