blob: a8a7c18e3660bcbadb3d5ccc559c8ff99591da11 [file] [log] [blame]
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001/*
2// Copyright (c) 2017 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
Patrick Venture31b35d52019-10-20 13:25:16 -070017#include "sensorcommands.hpp"
18
19#include "commandutils.hpp"
20#include "ipmi_to_redfish_hooks.hpp"
21#include "sdrutils.hpp"
22#include "sensorutils.hpp"
23#include "storagecommands.hpp"
Patrick Venturec2a07d42020-05-30 16:35:03 -070024#include "types.hpp"
Patrick Venture31b35d52019-10-20 13:25:16 -070025
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070026#include <boost/algorithm/string.hpp>
27#include <boost/container/flat_map.hpp>
James Feistfcd2d3a2020-05-28 10:38:15 -070028#include <ipmid/api.hpp>
29#include <ipmid/utils.hpp>
30#include <phosphor-logging/log.hpp>
31#include <sdbusplus/bus.hpp>
32
33#include <algorithm>
34#include <array>
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070035#include <chrono>
36#include <cmath>
Patrick Venture5c2d26e2019-09-25 17:43:53 -070037#include <cstring>
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070038#include <iostream>
Patrick Ventureb10ec8b2019-09-25 16:39:14 -070039#include <map>
Patrick Venturefd2a9382019-09-25 17:47:25 -070040#include <memory>
Patrick Venturec4e9de62019-09-25 17:40:54 -070041#include <optional>
Patrick Venturee6154022019-09-25 17:50:25 -070042#include <stdexcept>
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070043#include <string>
Patrick Venture38f46f22019-09-25 17:41:26 -070044#include <utility>
Patrick Ventureeb02a5c2019-09-25 17:44:43 -070045#include <variant>
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070046
47namespace ipmi
48{
49using ManagedObjectType =
50 std::map<sdbusplus::message::object_path,
51 std::map<std::string, std::map<std::string, DbusVariant>>>;
Johnathan Mantey308c3a82020-07-22 11:50:54 -070052using SDRObjectType =
53 boost::container::flat_map<uint16_t, std::vector<uint8_t>>;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070054
James Feist25690252019-12-23 12:25:49 -080055static constexpr int sensorMapUpdatePeriod = 10;
Alex Qiu09701ef2020-07-15 17:56:21 -070056static constexpr int sensorMapSdrUpdatePeriod = 60;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070057
58constexpr size_t maxSDRTotalSize =
59 76; // Largest SDR Record Size (type 01) + SDR Overheader Size
60constexpr static const uint32_t noTimestamp = 0xFFFFFFFF;
61
62static uint16_t sdrReservationID;
63static uint32_t sdrLastAdd = noTimestamp;
64static uint32_t sdrLastRemove = noTimestamp;
Johnathan Mantey308c3a82020-07-22 11:50:54 -070065static constexpr size_t lastRecordIndex = 0xFFFF;
66static constexpr int GENERAL_ERROR = -1;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070067
Richard Marian Thomaiyar01fbcb52018-11-19 22:04:34 +053068SensorSubTree sensorTree;
Johnathan Mantey308c3a82020-07-22 11:50:54 -070069SDRObjectType sensorDataRecords;
70
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070071static boost::container::flat_map<std::string, ManagedObjectType> SensorCache;
72
Jason M. Bills17add592018-11-12 14:30:12 -080073// Specify the comparison required to sort and find char* map objects
74struct CmpStr
75{
James Feistfcd2d3a2020-05-28 10:38:15 -070076 bool operator()(const char* a, const char* b) const
Jason M. Bills17add592018-11-12 14:30:12 -080077 {
78 return std::strcmp(a, b) < 0;
79 }
80};
James Feistfcd2d3a2020-05-28 10:38:15 -070081const static boost::container::flat_map<const char*, SensorUnits, CmpStr>
Jason M. Bills17add592018-11-12 14:30:12 -080082 sensorUnits{{{"temperature", SensorUnits::degreesC},
83 {"voltage", SensorUnits::volts},
84 {"current", SensorUnits::amps},
85 {"fan_tach", SensorUnits::rpm},
86 {"power", SensorUnits::watts}}};
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070087
88void registerSensorFunctions() __attribute__((constructor));
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070089
90static sdbusplus::bus::match::match sensorAdded(
Vernon Mauery15419dd2019-05-24 09:40:30 -070091 *getSdBus(),
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070092 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
93 "sensors/'",
James Feistfcd2d3a2020-05-28 10:38:15 -070094 [](sdbusplus::message::message& m) {
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070095 sensorTree.clear();
96 sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>(
97 std::chrono::system_clock::now().time_since_epoch())
98 .count();
99 });
100
101static sdbusplus::bus::match::match sensorRemoved(
Vernon Mauery15419dd2019-05-24 09:40:30 -0700102 *getSdBus(),
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700103 "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/"
104 "sensors/'",
James Feistfcd2d3a2020-05-28 10:38:15 -0700105 [](sdbusplus::message::message& m) {
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700106 sensorTree.clear();
107 sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>(
108 std::chrono::system_clock::now().time_since_epoch())
109 .count();
110 });
111
James Feist392786a2019-03-19 13:36:10 -0700112// this keeps track of deassertions for sensor event status command. A
113// deasertion can only happen if an assertion was seen first.
114static boost::container::flat_map<
115 std::string, boost::container::flat_map<std::string, std::optional<bool>>>
116 thresholdDeassertMap;
117
118static sdbusplus::bus::match::match thresholdChanged(
Vernon Mauery15419dd2019-05-24 09:40:30 -0700119 *getSdBus(),
James Feist392786a2019-03-19 13:36:10 -0700120 "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus."
121 "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
James Feistfcd2d3a2020-05-28 10:38:15 -0700122 [](sdbusplus::message::message& m) {
James Feist392786a2019-03-19 13:36:10 -0700123 boost::container::flat_map<std::string, std::variant<bool, double>>
124 values;
125 m.read(std::string(), values);
126
127 auto findAssert =
James Feistfcd2d3a2020-05-28 10:38:15 -0700128 std::find_if(values.begin(), values.end(), [](const auto& pair) {
James Feist392786a2019-03-19 13:36:10 -0700129 return pair.first.find("Alarm") != std::string::npos;
130 });
131 if (findAssert != values.end())
132 {
133 auto ptr = std::get_if<bool>(&(findAssert->second));
134 if (ptr == nullptr)
135 {
136 phosphor::logging::log<phosphor::logging::level::ERR>(
137 "thresholdChanged: Assert non bool");
138 return;
139 }
140 if (*ptr)
141 {
142 phosphor::logging::log<phosphor::logging::level::INFO>(
143 "thresholdChanged: Assert",
144 phosphor::logging::entry("SENSOR=%s", m.get_path()));
145 thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr;
146 }
147 else
148 {
James Feistfcd2d3a2020-05-28 10:38:15 -0700149 auto& value =
James Feist392786a2019-03-19 13:36:10 -0700150 thresholdDeassertMap[m.get_path()][findAssert->first];
151 if (value)
152 {
153 phosphor::logging::log<phosphor::logging::level::INFO>(
154 "thresholdChanged: deassert",
155 phosphor::logging::entry("SENSOR=%s", m.get_path()));
156 value = *ptr;
157 }
158 }
159 }
160 });
161
James Feistfcd2d3a2020-05-28 10:38:15 -0700162static void getSensorMaxMin(const SensorMap& sensorMap, double& max,
163 double& min)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700164{
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700165 max = 127;
166 min = -128;
167
James Feistaecaef72019-04-26 10:30:32 -0700168 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
169 auto critical =
170 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
171 auto warning =
172 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
173
174 if (sensorObject != sensorMap.end())
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700175 {
James Feistaecaef72019-04-26 10:30:32 -0700176 auto maxMap = sensorObject->second.find("MaxValue");
177 auto minMap = sensorObject->second.find("MinValue");
178
179 if (maxMap != sensorObject->second.end())
180 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700181 max = std::visit(VariantToDoubleVisitor(), maxMap->second);
James Feistaecaef72019-04-26 10:30:32 -0700182 }
183 if (minMap != sensorObject->second.end())
184 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700185 min = std::visit(VariantToDoubleVisitor(), minMap->second);
James Feistaecaef72019-04-26 10:30:32 -0700186 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700187 }
James Feistaecaef72019-04-26 10:30:32 -0700188 if (critical != sensorMap.end())
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700189 {
James Feistaecaef72019-04-26 10:30:32 -0700190 auto lower = critical->second.find("CriticalLow");
191 auto upper = critical->second.find("CriticalHigh");
192 if (lower != critical->second.end())
193 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700194 double value = std::visit(VariantToDoubleVisitor(), lower->second);
James Feistaecaef72019-04-26 10:30:32 -0700195 min = std::min(value, min);
196 }
197 if (upper != critical->second.end())
198 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700199 double value = std::visit(VariantToDoubleVisitor(), upper->second);
James Feistaecaef72019-04-26 10:30:32 -0700200 max = std::max(value, max);
201 }
202 }
203 if (warning != sensorMap.end())
204 {
205
206 auto lower = warning->second.find("WarningLow");
207 auto upper = warning->second.find("WarningHigh");
208 if (lower != warning->second.end())
209 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700210 double value = std::visit(VariantToDoubleVisitor(), lower->second);
James Feistaecaef72019-04-26 10:30:32 -0700211 min = std::min(value, min);
212 }
213 if (upper != warning->second.end())
214 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700215 double value = std::visit(VariantToDoubleVisitor(), upper->second);
James Feistaecaef72019-04-26 10:30:32 -0700216 max = std::max(value, max);
217 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700218 }
219}
220
James Feist25690252019-12-23 12:25:49 -0800221static bool getSensorMap(boost::asio::yield_context yield,
222 std::string sensorConnection, std::string sensorPath,
Alex Qiu09701ef2020-07-15 17:56:21 -0700223 SensorMap& sensorMap,
224 int updatePeriod = sensorMapUpdatePeriod)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700225{
226 static boost::container::flat_map<
227 std::string, std::chrono::time_point<std::chrono::steady_clock>>
228 updateTimeMap;
229
230 auto updateFind = updateTimeMap.find(sensorConnection);
231 auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
232 if (updateFind != updateTimeMap.end())
233 {
234 lastUpdate = updateFind->second;
235 }
236
237 auto now = std::chrono::steady_clock::now();
238
239 if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
Alex Qiu09701ef2020-07-15 17:56:21 -0700240 .count() > updatePeriod)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700241 {
Vernon Mauery15419dd2019-05-24 09:40:30 -0700242 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
James Feist25690252019-12-23 12:25:49 -0800243 boost::system::error_code ec;
244 auto managedObjects = dbus->yield_method_call<ManagedObjectType>(
245 yield, ec, sensorConnection.c_str(), "/",
246 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
247 if (ec)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700248 {
249 phosphor::logging::log<phosphor::logging::level::ERR>(
James Feist25690252019-12-23 12:25:49 -0800250 "GetMangagedObjects for getSensorMap failed",
251 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
252
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700253 return false;
254 }
255
256 SensorCache[sensorConnection] = managedObjects;
Alex Qiu09701ef2020-07-15 17:56:21 -0700257 // Update time after finish building the map which allow the
258 // data to be cached for updatePeriod plus the build time.
259 updateTimeMap[sensorConnection] = std::chrono::steady_clock::now();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700260 }
261 auto connection = SensorCache.find(sensorConnection);
262 if (connection == SensorCache.end())
263 {
264 return false;
265 }
266 auto path = connection->second.find(sensorPath);
267 if (path == connection->second.end())
268 {
269 return false;
270 }
271 sensorMap = path->second;
272
273 return true;
274}
275
276/* sensor commands */
James Feist7aaf3fe2019-06-25 11:52:11 -0700277namespace meHealth
278{
James Feistfcd2d3a2020-05-28 10:38:15 -0700279constexpr const char* busname = "xyz.openbmc_project.NodeManagerProxy";
280constexpr const char* path = "/xyz/openbmc_project/status/me";
281constexpr const char* interface = "xyz.openbmc_project.SetHealth";
282constexpr const char* method = "SetHealth";
283constexpr const char* critical = "critical";
284constexpr const char* warning = "warning";
285constexpr const char* ok = "ok";
James Feist7aaf3fe2019-06-25 11:52:11 -0700286} // namespace meHealth
287
288static void setMeStatus(uint8_t eventData2, uint8_t eventData3, bool disable)
289{
290 constexpr const std::array<uint8_t, 10> critical = {
291 0x1, 0x2, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xD, 0xE};
292 constexpr const std::array<uint8_t, 5> warning = {0x3, 0xA, 0x13, 0x19,
293 0x1A};
294
295 std::string state;
296 if (std::find(critical.begin(), critical.end(), eventData2) !=
297 critical.end())
298 {
299 state = meHealth::critical;
300 }
301 // special case 0x3 as we only care about a few states
302 else if (eventData2 == 0x3)
303 {
304 if (eventData3 <= 0x2)
305 {
306 state = meHealth::warning;
307 }
308 else
309 {
310 return;
311 }
312 }
313 else if (std::find(warning.begin(), warning.end(), eventData2) !=
314 warning.end())
315 {
316 state = meHealth::warning;
317 }
318 else
319 {
320 return;
321 }
322 if (disable)
323 {
324 state = meHealth::ok;
325 }
326
327 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
328 auto setHealth =
329 dbus->new_method_call(meHealth::busname, meHealth::path,
330 meHealth::interface, meHealth::method);
331 setHealth.append(std::to_string(static_cast<size_t>(eventData2)), state);
332 try
333 {
334 dbus->call(setHealth);
335 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700336 catch (sdbusplus::exception_t&)
James Feist7aaf3fe2019-06-25 11:52:11 -0700337 {
338 phosphor::logging::log<phosphor::logging::level::ERR>(
339 "Failed to set ME Health");
340 }
341}
342
James Feistfcd2d3a2020-05-28 10:38:15 -0700343ipmi::RspType<> ipmiSenPlatformEvent(ipmi::message::Payload& p)
Jason M. Billsae6bdb12019-04-02 12:00:04 -0700344{
James Feist7aaf3fe2019-06-25 11:52:11 -0700345 constexpr const uint8_t meId = 0x2C;
346 constexpr const uint8_t meSensorNum = 0x17;
347 constexpr const uint8_t disabled = 0x80;
348
Jason M. Billsae6bdb12019-04-02 12:00:04 -0700349 uint8_t generatorID = 0;
350 uint8_t evmRev = 0;
351 uint8_t sensorType = 0;
352 uint8_t sensorNum = 0;
353 uint8_t eventType = 0;
354 uint8_t eventData1 = 0;
355 std::optional<uint8_t> eventData2 = 0;
356 std::optional<uint8_t> eventData3 = 0;
357
358 // todo: This check is supposed to be based on the incoming channel.
359 // e.g. system channel will provide upto 8 bytes including generator
360 // ID, but ipmb channel will provide only up to 7 bytes without the
361 // generator ID.
362 // Support for this check is coming in future patches, so for now just base
363 // it on if the first byte is the EvMRev (0x04).
364 if (p.size() && p.data()[0] == 0x04)
365 {
366 p.unpack(evmRev, sensorType, sensorNum, eventType, eventData1,
367 eventData2, eventData3);
368 // todo: the generator ID for this channel is supposed to come from the
369 // IPMB requesters slave address. Support for this is coming in future
370 // patches, so for now just assume it is coming from the ME (0x2C).
371 generatorID = 0x2C;
372 }
373 else
374 {
375 p.unpack(generatorID, evmRev, sensorType, sensorNum, eventType,
376 eventData1, eventData2, eventData3);
377 }
378 if (!p.fullyUnpacked())
379 {
380 return ipmi::responseReqDataLenInvalid();
381 }
382
Jason M. Bills6dd8f042019-04-11 10:39:02 -0700383 // Send this request to the Redfish hooks to log it as a Redfish message
384 // instead. There is no need to add it to the SEL, so just return success.
385 intel_oem::ipmi::sel::checkRedfishHooks(
386 generatorID, evmRev, sensorType, sensorNum, eventType, eventData1,
387 eventData2.value_or(0xFF), eventData3.value_or(0xFF));
Jason M. Billsae6bdb12019-04-02 12:00:04 -0700388
James Feist7aaf3fe2019-06-25 11:52:11 -0700389 if (generatorID == meId && sensorNum == meSensorNum && eventData2 &&
390 eventData3)
391 {
392 setMeStatus(*eventData2, *eventData3, (eventType & disabled));
393 }
394
Jason M. Billsae6bdb12019-04-02 12:00:04 -0700395 return ipmi::responseSuccess();
396}
397
James Feist0cd014a2019-04-08 15:04:33 -0700398ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700399 ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700400{
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700401 std::string connection;
402 std::string path;
403
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700404 auto status = getSensorConnection(ctx, sensnum, connection, path);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700405 if (status)
406 {
James Feist0cd014a2019-04-08 15:04:33 -0700407 return ipmi::response(status);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700408 }
409
410 SensorMap sensorMap;
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700411 if (!getSensorMap(ctx->yield, connection, path, sensorMap))
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700412 {
James Feist0cd014a2019-04-08 15:04:33 -0700413 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700414 }
415 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
416
417 if (sensorObject == sensorMap.end() ||
418 sensorObject->second.find("Value") == sensorObject->second.end())
419 {
James Feist0cd014a2019-04-08 15:04:33 -0700420 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700421 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700422 auto& valueVariant = sensorObject->second["Value"];
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700423 double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700424
Yong Li1f2eb5e2019-05-23 14:07:17 +0800425 double max = 0;
426 double min = 0;
James Feistaecaef72019-04-26 10:30:32 -0700427 getSensorMaxMin(sensorMap, max, min);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700428
429 int16_t mValue = 0;
430 int16_t bValue = 0;
431 int8_t rExp = 0;
432 int8_t bExp = 0;
433 bool bSigned = false;
434
435 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
436 {
James Feist0cd014a2019-04-08 15:04:33 -0700437 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700438 }
439
James Feist0cd014a2019-04-08 15:04:33 -0700440 uint8_t value =
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700441 scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
James Feist0cd014a2019-04-08 15:04:33 -0700442 uint8_t operation =
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700443 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
James Feist0cd014a2019-04-08 15:04:33 -0700444 operation |=
James Feist81a95c12019-03-01 15:08:28 -0800445 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
James Feist1ee6ed82020-06-17 16:16:50 -0700446 bool notReading = std::isnan(reading);
447
448 if (!notReading)
449 {
450 auto availableObject =
451 sensorMap.find("xyz.openbmc_project.State.Decorator.Availability");
452 if (availableObject != sensorMap.end())
453 {
454 auto findAvailable = availableObject->second.find("Available");
455 if (findAvailable != availableObject->second.end())
456 {
457 bool* available = std::get_if<bool>(&(findAvailable->second));
458 if (available && !(*available))
459 {
460 notReading = true;
461 }
462 }
463 }
464 }
465
466 if (notReading)
467 {
468 operation |= static_cast<uint8_t>(
469 IPMISensorReadingByte2::readingStateUnavailable);
470 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700471
James Feist0cd014a2019-04-08 15:04:33 -0700472 uint8_t thresholds = 0;
473
474 auto warningObject =
475 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
476 if (warningObject != sensorMap.end())
477 {
478 auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
479 auto alarmLow = warningObject->second.find("WarningAlarmLow");
480 if (alarmHigh != warningObject->second.end())
481 {
482 if (std::get<bool>(alarmHigh->second))
483 {
484 thresholds |= static_cast<uint8_t>(
485 IPMISensorReadingByte3::upperNonCritical);
486 }
487 }
488 if (alarmLow != warningObject->second.end())
489 {
490 if (std::get<bool>(alarmLow->second))
491 {
492 thresholds |= static_cast<uint8_t>(
493 IPMISensorReadingByte3::lowerNonCritical);
494 }
495 }
496 }
497
498 auto criticalObject =
499 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
500 if (criticalObject != sensorMap.end())
501 {
502 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
503 auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
504 if (alarmHigh != criticalObject->second.end())
505 {
506 if (std::get<bool>(alarmHigh->second))
507 {
508 thresholds |=
509 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
510 }
511 }
512 if (alarmLow != criticalObject->second.end())
513 {
514 if (std::get<bool>(alarmLow->second))
515 {
516 thresholds |=
517 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
518 }
519 }
520 }
521
522 // no discrete as of today so optional byte is never returned
523 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700524}
525
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000526/** @brief implements the Set Sensor threshold command
527 * @param sensorNumber - sensor number
528 * @param lowerNonCriticalThreshMask
529 * @param lowerCriticalThreshMask
530 * @param lowerNonRecovThreshMask
531 * @param upperNonCriticalThreshMask
532 * @param upperCriticalThreshMask
533 * @param upperNonRecovThreshMask
534 * @param reserved
535 * @param lowerNonCritical - lower non-critical threshold
536 * @param lowerCritical - Lower critical threshold
537 * @param lowerNonRecoverable - Lower non recovarable threshold
538 * @param upperNonCritical - Upper non-critical threshold
539 * @param upperCritical - Upper critical
540 * @param upperNonRecoverable - Upper Non-recoverable
541 *
542 * @returns IPMI completion code
543 */
544ipmi::RspType<> ipmiSenSetSensorThresholds(
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700545 ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
546 bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
547 bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
548 bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
549 uint8_t lowerCritical, uint8_t lowerNonRecoverable,
550 uint8_t upperNonCritical, uint8_t upperCritical,
551 uint8_t upperNonRecoverable)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700552{
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000553 constexpr uint8_t thresholdMask = 0xFF;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700554
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000555 if (reserved)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700556 {
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000557 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700558 }
559
560 // lower nc and upper nc not suppported on any sensor
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000561 if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700562 {
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000563 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700564 }
565
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000566 // if none of the threshold mask are set, nothing to do
567 if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
568 lowerNonRecovThreshMask | upperNonCriticalThreshMask |
569 upperCriticalThreshMask | upperNonRecovThreshMask))
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700570 {
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000571 return ipmi::responseSuccess();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700572 }
573
574 std::string connection;
575 std::string path;
576
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700577 ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700578 if (status)
579 {
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000580 return ipmi::response(status);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700581 }
582 SensorMap sensorMap;
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700583 if (!getSensorMap(ctx->yield, connection, path, sensorMap))
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700584 {
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000585 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700586 }
587
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700588 double max = 0;
589 double min = 0;
James Feistaecaef72019-04-26 10:30:32 -0700590 getSensorMaxMin(sensorMap, max, min);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700591
592 int16_t mValue = 0;
593 int16_t bValue = 0;
594 int8_t rExp = 0;
595 int8_t bExp = 0;
596 bool bSigned = false;
597
598 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
599 {
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000600 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700601 }
602
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700603 // store a vector of property name, value to set, and interface
604 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
605
606 // define the indexes of the tuple
607 constexpr uint8_t propertyName = 0;
608 constexpr uint8_t thresholdValue = 1;
609 constexpr uint8_t interface = 2;
610 // verifiy all needed fields are present
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000611 if (lowerCriticalThreshMask || upperCriticalThreshMask)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700612 {
613 auto findThreshold =
614 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
615 if (findThreshold == sensorMap.end())
616 {
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000617 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700618 }
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000619 if (lowerCriticalThreshMask)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700620 {
621 auto findLower = findThreshold->second.find("CriticalLow");
622 if (findLower == findThreshold->second.end())
623 {
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000624 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700625 }
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000626 thresholdsToSet.emplace_back("CriticalLow", lowerCritical,
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700627 findThreshold->first);
628 }
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000629 if (upperCriticalThreshMask)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700630 {
631 auto findUpper = findThreshold->second.find("CriticalHigh");
632 if (findUpper == findThreshold->second.end())
633 {
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000634 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700635 }
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000636 thresholdsToSet.emplace_back("CriticalHigh", upperCritical,
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700637 findThreshold->first);
638 }
639 }
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000640 if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700641 {
642 auto findThreshold =
643 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
644 if (findThreshold == sensorMap.end())
645 {
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000646 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700647 }
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000648 if (lowerNonCriticalThreshMask)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700649 {
650 auto findLower = findThreshold->second.find("WarningLow");
651 if (findLower == findThreshold->second.end())
652 {
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000653 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700654 }
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000655 thresholdsToSet.emplace_back("WarningLow", lowerNonCritical,
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700656 findThreshold->first);
657 }
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000658 if (upperNonCriticalThreshMask)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700659 {
660 auto findUpper = findThreshold->second.find("WarningHigh");
661 if (findUpper == findThreshold->second.end())
662 {
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000663 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700664 }
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000665 thresholdsToSet.emplace_back("WarningHigh", upperNonCritical,
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700666 findThreshold->first);
667 }
668 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700669 for (const auto& property : thresholdsToSet)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700670 {
671 // from section 36.3 in the IPMI Spec, assume all linear
672 double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
Josh Lehan86236a22019-11-18 17:53:33 -0800673 (bValue * std::pow(10.0, bExp))) *
674 std::pow(10.0, rExp);
Vernon Mauery15419dd2019-05-24 09:40:30 -0700675 setDbusProperty(
676 *getSdBus(), connection, path, std::get<interface>(property),
677 std::get<propertyName>(property), ipmi::Value(valueToSet));
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700678 }
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000679 return ipmi::responseSuccess();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700680}
681
James Feistfcd2d3a2020-05-28 10:38:15 -0700682IPMIThresholds getIPMIThresholds(const SensorMap& sensorMap)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700683{
James Feist902c4c52019-04-16 14:51:31 -0700684 IPMIThresholds resp;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700685 auto warningInterface =
686 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
687 auto criticalInterface =
688 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
689
690 if ((warningInterface != sensorMap.end()) ||
691 (criticalInterface != sensorMap.end()))
692 {
693 auto sensorPair = sensorMap.find("xyz.openbmc_project.Sensor.Value");
694
695 if (sensorPair == sensorMap.end())
696 {
697 // should not have been able to find a sensor not implementing
698 // the sensor object
James Feist902c4c52019-04-16 14:51:31 -0700699 throw std::runtime_error("Invalid sensor map");
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700700 }
701
Chen,Yugang606dd9f2019-07-01 10:37:30 +0800702 double max = 0;
703 double min = 0;
James Feistaecaef72019-04-26 10:30:32 -0700704 getSensorMaxMin(sensorMap, max, min);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700705
706 int16_t mValue = 0;
707 int16_t bValue = 0;
708 int8_t rExp = 0;
709 int8_t bExp = 0;
710 bool bSigned = false;
711
712 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
713 {
James Feist902c4c52019-04-16 14:51:31 -0700714 throw std::runtime_error("Invalid sensor atrributes");
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700715 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700716 if (warningInterface != sensorMap.end())
717 {
James Feistfcd2d3a2020-05-28 10:38:15 -0700718 auto& warningMap = warningInterface->second;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700719
720 auto warningHigh = warningMap.find("WarningHigh");
721 auto warningLow = warningMap.find("WarningLow");
722
723 if (warningHigh != warningMap.end())
724 {
James Feist902c4c52019-04-16 14:51:31 -0700725
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700726 double value =
727 std::visit(VariantToDoubleVisitor(), warningHigh->second);
James Feist902c4c52019-04-16 14:51:31 -0700728 resp.warningHigh = scaleIPMIValueFromDouble(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700729 value, mValue, rExp, bValue, bExp, bSigned);
730 }
731 if (warningLow != warningMap.end())
732 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700733 double value =
734 std::visit(VariantToDoubleVisitor(), warningLow->second);
James Feist902c4c52019-04-16 14:51:31 -0700735 resp.warningLow = scaleIPMIValueFromDouble(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700736 value, mValue, rExp, bValue, bExp, bSigned);
737 }
738 }
739 if (criticalInterface != sensorMap.end())
740 {
James Feistfcd2d3a2020-05-28 10:38:15 -0700741 auto& criticalMap = criticalInterface->second;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700742
743 auto criticalHigh = criticalMap.find("CriticalHigh");
744 auto criticalLow = criticalMap.find("CriticalLow");
745
746 if (criticalHigh != criticalMap.end())
747 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700748 double value =
749 std::visit(VariantToDoubleVisitor(), criticalHigh->second);
James Feist902c4c52019-04-16 14:51:31 -0700750 resp.criticalHigh = scaleIPMIValueFromDouble(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700751 value, mValue, rExp, bValue, bExp, bSigned);
752 }
753 if (criticalLow != criticalMap.end())
754 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700755 double value =
756 std::visit(VariantToDoubleVisitor(), criticalLow->second);
James Feist902c4c52019-04-16 14:51:31 -0700757 resp.criticalLow = scaleIPMIValueFromDouble(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700758 value, mValue, rExp, bValue, bExp, bSigned);
759 }
760 }
761 }
James Feist902c4c52019-04-16 14:51:31 -0700762 return resp;
763}
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700764
James Feist902c4c52019-04-16 14:51:31 -0700765ipmi::RspType<uint8_t, // readable
766 uint8_t, // lowerNCrit
767 uint8_t, // lowerCrit
768 uint8_t, // lowerNrecoverable
769 uint8_t, // upperNC
770 uint8_t, // upperCrit
771 uint8_t> // upperNRecoverable
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700772 ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber)
James Feist902c4c52019-04-16 14:51:31 -0700773{
774 std::string connection;
775 std::string path;
776
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700777 auto status = getSensorConnection(ctx, sensorNumber, connection, path);
James Feist902c4c52019-04-16 14:51:31 -0700778 if (status)
779 {
780 return ipmi::response(status);
781 }
782
783 SensorMap sensorMap;
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700784 if (!getSensorMap(ctx->yield, connection, path, sensorMap))
James Feist902c4c52019-04-16 14:51:31 -0700785 {
786 return ipmi::responseResponseError();
787 }
788
789 IPMIThresholds thresholdData;
790 try
791 {
792 thresholdData = getIPMIThresholds(sensorMap);
793 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700794 catch (std::exception&)
James Feist902c4c52019-04-16 14:51:31 -0700795 {
796 return ipmi::responseResponseError();
797 }
798
799 uint8_t readable = 0;
800 uint8_t lowerNC = 0;
801 uint8_t lowerCritical = 0;
802 uint8_t lowerNonRecoverable = 0;
803 uint8_t upperNC = 0;
804 uint8_t upperCritical = 0;
805 uint8_t upperNonRecoverable = 0;
806
807 if (thresholdData.warningHigh)
808 {
809 readable |=
810 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
811 upperNC = *thresholdData.warningHigh;
812 }
813 if (thresholdData.warningLow)
814 {
815 readable |=
816 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
817 lowerNC = *thresholdData.warningLow;
818 }
819
820 if (thresholdData.criticalHigh)
821 {
822 readable |=
823 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
824 upperCritical = *thresholdData.criticalHigh;
825 }
826 if (thresholdData.criticalLow)
827 {
828 readable |=
829 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
830 lowerCritical = *thresholdData.criticalLow;
831 }
832
833 return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
834 lowerNonRecoverable, upperNC, upperCritical,
835 upperNonRecoverable);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700836}
837
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000838/** @brief implements the get Sensor event enable command
839 * @param sensorNumber - sensor number
840 *
841 * @returns IPMI completion code plus response data
842 * - enabled - Sensor Event messages
843 * - assertionEnabledLsb - Assertion event messages
844 * - assertionEnabledMsb - Assertion event messages
845 * - deassertionEnabledLsb - Deassertion event messages
846 * - deassertionEnabledMsb - Deassertion event messages
847 */
848
849ipmi::RspType<uint8_t, // enabled
850 uint8_t, // assertionEnabledLsb
851 uint8_t, // assertionEnabledMsb
852 uint8_t, // deassertionEnabledLsb
853 uint8_t> // deassertionEnabledMsb
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700854 ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700855{
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700856 std::string connection;
857 std::string path;
858
Patrick Venturea41714c2019-09-25 16:59:41 -0700859 uint8_t enabled = 0;
860 uint8_t assertionEnabledLsb = 0;
861 uint8_t assertionEnabledMsb = 0;
862 uint8_t deassertionEnabledLsb = 0;
863 uint8_t deassertionEnabledMsb = 0;
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000864
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700865 auto status = getSensorConnection(ctx, sensorNum, connection, path);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700866 if (status)
867 {
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000868 return ipmi::response(status);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700869 }
870
871 SensorMap sensorMap;
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700872 if (!getSensorMap(ctx->yield, connection, path, sensorMap))
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700873 {
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000874 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700875 }
876
877 auto warningInterface =
878 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
879 auto criticalInterface =
880 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700881 if ((warningInterface != sensorMap.end()) ||
882 (criticalInterface != sensorMap.end()))
883 {
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000884 enabled = static_cast<uint8_t>(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700885 IPMISensorEventEnableByte2::sensorScanningEnable);
886 if (warningInterface != sensorMap.end())
887 {
James Feistfcd2d3a2020-05-28 10:38:15 -0700888 auto& warningMap = warningInterface->second;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700889
890 auto warningHigh = warningMap.find("WarningHigh");
891 auto warningLow = warningMap.find("WarningLow");
892 if (warningHigh != warningMap.end())
893 {
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000894 assertionEnabledLsb |= static_cast<uint8_t>(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700895 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000896 deassertionEnabledLsb |= static_cast<uint8_t>(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700897 IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
898 }
899 if (warningLow != warningMap.end())
900 {
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000901 assertionEnabledLsb |= static_cast<uint8_t>(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700902 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000903 deassertionEnabledLsb |= static_cast<uint8_t>(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700904 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
905 }
906 }
907 if (criticalInterface != sensorMap.end())
908 {
James Feistfcd2d3a2020-05-28 10:38:15 -0700909 auto& criticalMap = criticalInterface->second;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700910
911 auto criticalHigh = criticalMap.find("CriticalHigh");
912 auto criticalLow = criticalMap.find("CriticalLow");
913
914 if (criticalHigh != criticalMap.end())
915 {
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000916 assertionEnabledMsb |= static_cast<uint8_t>(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700917 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000918 deassertionEnabledMsb |= static_cast<uint8_t>(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700919 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
920 }
921 if (criticalLow != criticalMap.end())
922 {
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000923 assertionEnabledLsb |= static_cast<uint8_t>(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700924 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000925 deassertionEnabledLsb |= static_cast<uint8_t>(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700926 IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
927 }
928 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700929 }
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000930
931 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
932 assertionEnabledMsb, deassertionEnabledLsb,
933 deassertionEnabledMsb);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700934}
935
jayaprakash Mutyalaccf88f62019-05-13 16:57:16 +0000936/** @brief implements the get Sensor event status command
937 * @param sensorNumber - sensor number, FFh = reserved
938 *
939 * @returns IPMI completion code plus response data
940 * - sensorEventStatus - Sensor Event messages state
941 * - assertions - Assertion event messages
942 * - deassertions - Deassertion event messages
943 */
944ipmi::RspType<uint8_t, // sensorEventStatus
945 std::bitset<16>, // assertions
946 std::bitset<16> // deassertion
947 >
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700948 ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700949{
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700950 if (sensorNum == reservedSensorNumber)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700951 {
jayaprakash Mutyalaccf88f62019-05-13 16:57:16 +0000952 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700953 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700954
955 std::string connection;
956 std::string path;
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700957 auto status = getSensorConnection(ctx, sensorNum, connection, path);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700958 if (status)
959 {
jayaprakash Mutyalaccf88f62019-05-13 16:57:16 +0000960 phosphor::logging::log<phosphor::logging::level::ERR>(
961 "ipmiSenGetSensorEventStatus: Sensor connection Error",
962 phosphor::logging::entry("SENSOR=%d", sensorNum));
963 return ipmi::response(status);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700964 }
965
966 SensorMap sensorMap;
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700967 if (!getSensorMap(ctx->yield, connection, path, sensorMap))
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700968 {
jayaprakash Mutyalaccf88f62019-05-13 16:57:16 +0000969 phosphor::logging::log<phosphor::logging::level::ERR>(
970 "ipmiSenGetSensorEventStatus: Sensor Mapping Error",
971 phosphor::logging::entry("SENSOR=%s", path.c_str()));
972 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700973 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700974 auto warningInterface =
975 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
976 auto criticalInterface =
977 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
978
jayaprakash Mutyalaccf88f62019-05-13 16:57:16 +0000979 uint8_t sensorEventStatus =
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700980 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
981
James Feist392786a2019-03-19 13:36:10 -0700982 std::optional<bool> criticalDeassertHigh =
983 thresholdDeassertMap[path]["CriticalAlarmHigh"];
984 std::optional<bool> criticalDeassertLow =
985 thresholdDeassertMap[path]["CriticalAlarmLow"];
986 std::optional<bool> warningDeassertHigh =
987 thresholdDeassertMap[path]["WarningAlarmHigh"];
988 std::optional<bool> warningDeassertLow =
989 thresholdDeassertMap[path]["WarningAlarmLow"];
990
jayaprakash Mutyalaccf88f62019-05-13 16:57:16 +0000991 std::bitset<16> assertions = 0;
992 std::bitset<16> deassertions = 0;
993
James Feist392786a2019-03-19 13:36:10 -0700994 if (criticalDeassertHigh && !*criticalDeassertHigh)
995 {
jayaprakash Mutyalaccf88f62019-05-13 16:57:16 +0000996 deassertions.set(static_cast<size_t>(
997 IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
James Feist392786a2019-03-19 13:36:10 -0700998 }
999 if (criticalDeassertLow && !*criticalDeassertLow)
1000 {
jayaprakash Mutyalaccf88f62019-05-13 16:57:16 +00001001 deassertions.set(static_cast<size_t>(
1002 IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
James Feist392786a2019-03-19 13:36:10 -07001003 }
1004 if (warningDeassertHigh && !*warningDeassertHigh)
1005 {
jayaprakash Mutyalaccf88f62019-05-13 16:57:16 +00001006 deassertions.set(static_cast<size_t>(
1007 IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
James Feist392786a2019-03-19 13:36:10 -07001008 }
1009 if (warningDeassertLow && !*warningDeassertLow)
1010 {
jayaprakash Mutyalaccf88f62019-05-13 16:57:16 +00001011 deassertions.set(static_cast<size_t>(
1012 IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
James Feist392786a2019-03-19 13:36:10 -07001013 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001014 if ((warningInterface != sensorMap.end()) ||
1015 (criticalInterface != sensorMap.end()))
1016 {
jayaprakash Mutyalaccf88f62019-05-13 16:57:16 +00001017 sensorEventStatus = static_cast<size_t>(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001018 IPMISensorEventEnableByte2::eventMessagesEnable);
1019 if (warningInterface != sensorMap.end())
1020 {
James Feistfcd2d3a2020-05-28 10:38:15 -07001021 auto& warningMap = warningInterface->second;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001022
1023 auto warningHigh = warningMap.find("WarningAlarmHigh");
1024 auto warningLow = warningMap.find("WarningAlarmLow");
1025 auto warningHighAlarm = false;
1026 auto warningLowAlarm = false;
1027
1028 if (warningHigh != warningMap.end())
1029 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001030 warningHighAlarm = std::get<bool>(warningHigh->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001031 }
1032 if (warningLow != warningMap.end())
1033 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001034 warningLowAlarm = std::get<bool>(warningLow->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001035 }
1036 if (warningHighAlarm)
1037 {
jayaprakash Mutyalaccf88f62019-05-13 16:57:16 +00001038 assertions.set(
1039 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1040 upperNonCriticalGoingHigh));
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001041 }
1042 if (warningLowAlarm)
1043 {
jayaprakash Mutyalaccf88f62019-05-13 16:57:16 +00001044 assertions.set(
1045 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1046 lowerNonCriticalGoingLow));
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001047 }
1048 }
1049 if (criticalInterface != sensorMap.end())
1050 {
James Feistfcd2d3a2020-05-28 10:38:15 -07001051 auto& criticalMap = criticalInterface->second;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001052
1053 auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
1054 auto criticalLow = criticalMap.find("CriticalAlarmLow");
1055 auto criticalHighAlarm = false;
1056 auto criticalLowAlarm = false;
1057
1058 if (criticalHigh != criticalMap.end())
1059 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001060 criticalHighAlarm = std::get<bool>(criticalHigh->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001061 }
1062 if (criticalLow != criticalMap.end())
1063 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001064 criticalLowAlarm = std::get<bool>(criticalLow->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001065 }
1066 if (criticalHighAlarm)
1067 {
jayaprakash Mutyalaccf88f62019-05-13 16:57:16 +00001068 assertions.set(
1069 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1070 upperCriticalGoingHigh));
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001071 }
1072 if (criticalLowAlarm)
1073 {
jayaprakash Mutyalaccf88f62019-05-13 16:57:16 +00001074 assertions.set(static_cast<size_t>(
1075 IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001076 }
1077 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001078 }
1079
jayaprakash Mutyalaccf88f62019-05-13 16:57:16 +00001080 return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001081}
1082
Johnathan Mantey308c3a82020-07-22 11:50:54 -07001083static int getSensorDataRecords(ipmi::Context::ptr ctx)
1084{
1085
1086 size_t recordID = 0;
1087 size_t fruCount = 0;
1088
1089 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1090 if (ret != ipmi::ccSuccess)
1091 {
1092 return GENERAL_ERROR;
1093 }
1094
1095 size_t lastRecord = sensorTree.size() + fruCount +
1096 ipmi::storage::type12Count +
1097 ipmi::storage::nmDiscoverySDRCount - 1;
1098 if (lastRecord > lastRecordIndex)
1099 {
1100 return GENERAL_ERROR;
1101 }
1102
1103 std::string connection;
1104 std::string path;
1105 for (const auto& sensor : sensorTree)
1106 {
1107
1108 connection = sensor.second.begin()->first;
1109 path = sensor.first;
1110
1111 SensorMap sensorMap;
Alex Qiu09701ef2020-07-15 17:56:21 -07001112 if (!getSensorMap(ctx->yield, connection, path, sensorMap,
1113 sensorMapSdrUpdatePeriod))
Johnathan Mantey308c3a82020-07-22 11:50:54 -07001114 {
1115 return GENERAL_ERROR;
1116 }
1117 uint16_t sensorNum = getSensorNumberFromPath(path);
1118 if (sensorNum == invalidSensorNumber)
1119 {
1120 return GENERAL_ERROR;
1121 }
1122 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1123 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1124
1125 get_sdr::SensorDataFullRecord record = {0};
1126
1127 get_sdr::header::set_record_id(
1128 recordID,
1129 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1130 record.header.sdr_version = ipmiSdrVersion;
1131 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1132 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1133 sizeof(get_sdr::SensorDataRecordHeader);
1134 record.key.owner_id = 0x20;
1135 record.key.owner_lun = lun;
1136 record.key.sensor_number = sensornumber;
1137
1138 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
1139 record.body.sensor_type = getSensorTypeFromPath(path);
1140 std::string type = getSensorTypeStringFromPath(path);
1141 auto typeCstr = type.c_str();
1142 auto findUnits = sensorUnits.find(typeCstr);
1143 if (findUnits != sensorUnits.end())
1144 {
1145 record.body.sensor_units_2_base =
1146 static_cast<uint8_t>(findUnits->second);
1147 } // else default 0x0 unspecified
1148
1149 record.body.event_reading_type = getSensorEventTypeFromPath(path);
1150
1151 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
1152 if (sensorObject == sensorMap.end())
1153 {
1154 return GENERAL_ERROR;
1155 }
1156
1157 uint8_t entityId = 0;
1158 uint8_t entityInstance = 0x01;
1159
1160 // follow the association chain to get the parent board's entityid and
1161 // entityInstance
1162 updateIpmiFromAssociation(path, sensorMap, entityId, entityInstance);
1163
1164 record.body.entity_id = entityId;
1165 record.body.entity_instance = entityInstance;
1166
1167 auto maxObject = sensorObject->second.find("MaxValue");
1168 auto minObject = sensorObject->second.find("MinValue");
1169
1170 // If min and/or max are left unpopulated,
1171 // then default to what a signed byte would be, namely (-128,127) range.
1172 auto max = static_cast<double>(std::numeric_limits<int8_t>::max());
1173 auto min = static_cast<double>(std::numeric_limits<int8_t>::lowest());
1174 if (maxObject != sensorObject->second.end())
1175 {
1176 max = std::visit(VariantToDoubleVisitor(), maxObject->second);
1177 }
1178
1179 if (minObject != sensorObject->second.end())
1180 {
1181 min = std::visit(VariantToDoubleVisitor(), minObject->second);
1182 }
1183
1184 int16_t mValue = 0;
1185 int8_t rExp = 0;
1186 int16_t bValue = 0;
1187 int8_t bExp = 0;
1188 bool bSigned = false;
1189
1190 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1191 {
1192 return GENERAL_ERROR;
1193 }
1194
1195 // The record.body is a struct SensorDataFullRecordBody
1196 // from sensorhandler.hpp in phosphor-ipmi-host.
1197 // The meaning of these bits appears to come from
1198 // table 43.1 of the IPMI spec.
1199 // The above 5 sensor attributes are stuffed in as follows:
1200 // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1201 // Byte 22-24 are for other purposes
1202 // Byte 25 = MMMMMMMM = LSB of M
1203 // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1204 // Byte 27 = BBBBBBBB = LSB of B
1205 // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1206 // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1207 // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1208
1209 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1210 record.body.m_lsb = mValue & 0xFF;
1211
1212 uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1213 uint8_t mBitNine = (mValue & 0x0100) >> 8;
1214
1215 // move the smallest bit of the MSB into place (bit 9)
1216 // the MSbs are bits 7:8 in m_msb_and_tolerance
1217 record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6);
1218
1219 record.body.b_lsb = bValue & 0xFF;
1220
1221 uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1222 uint8_t bBitNine = (bValue & 0x0100) >> 8;
1223
1224 // move the smallest bit of the MSB into place (bit 9)
1225 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1226 record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6);
1227
1228 uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1229 uint8_t rExpBits = rExp & 0x07;
1230
1231 uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1232 uint8_t bExpBits = bExp & 0x07;
1233
1234 // move rExp and bExp into place
1235 record.body.r_b_exponents =
1236 (rExpSign << 7) | (rExpBits << 4) | (bExpSign << 3) | bExpBits;
1237
1238 // Set the analog reading byte interpretation accordingly
1239 record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7;
1240
1241 // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1242 // These seem redundant, but derivable from the above 5 attributes
1243 // Original comment said "todo fill out rest of units"
1244
1245 // populate sensor name from path
1246 std::string name;
1247 size_t nameStart = path.rfind("/");
1248 if (nameStart != std::string::npos)
1249 {
1250 name = path.substr(nameStart + 1, std::string::npos - nameStart);
1251 }
1252
1253 std::replace(name.begin(), name.end(), '_', ' ');
1254 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
1255 {
1256 // try to not truncate by replacing common words
1257 constexpr std::array<std::pair<const char*, const char*>, 2>
1258 replaceWords = {std::make_pair("Output", "Out"),
1259 std::make_pair("Input", "In")};
1260 for (const auto& [find, replace] : replaceWords)
1261 {
1262 boost::replace_all(name, find, replace);
1263 }
1264
1265 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
1266 }
1267 record.body.id_string_info = name.size();
1268 std::strncpy(record.body.id_string, name.c_str(),
1269 sizeof(record.body.id_string));
1270
1271 IPMIThresholds thresholdData;
1272 try
1273 {
1274 thresholdData = getIPMIThresholds(sensorMap);
1275 }
1276 catch (std::exception&)
1277 {
1278 return GENERAL_ERROR;
1279 }
1280
1281 if (thresholdData.criticalHigh)
1282 {
1283 record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1284 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1285 IPMISensorEventEnableThresholds::criticalThreshold);
1286 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1287 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1288 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1289 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1290 record.body.discrete_reading_setting_mask[0] |=
1291 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1292 }
1293 if (thresholdData.warningHigh)
1294 {
1295 record.body.upper_noncritical_threshold =
1296 *thresholdData.warningHigh;
1297 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1298 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1299 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1300 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1301 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1302 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1303 record.body.discrete_reading_setting_mask[0] |=
1304 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1305 }
1306 if (thresholdData.criticalLow)
1307 {
1308 record.body.lower_critical_threshold = *thresholdData.criticalLow;
1309 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1310 IPMISensorEventEnableThresholds::criticalThreshold);
1311 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1312 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1313 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1314 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1315 record.body.discrete_reading_setting_mask[0] |=
1316 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1317 }
1318 if (thresholdData.warningLow)
1319 {
1320 record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1321 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1322 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1323 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1324 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1325 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1326 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1327 record.body.discrete_reading_setting_mask[0] |=
1328 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1329 }
1330
1331 // everything that is readable is setable
1332 record.body.discrete_reading_setting_mask[1] =
1333 record.body.discrete_reading_setting_mask[0];
1334
1335 // insert the record into the map
1336 std::vector<uint8_t> sdr;
1337 sdr.insert(sdr.end(), (uint8_t*)&record,
1338 ((uint8_t*)&record) + sizeof(record));
1339 sensorDataRecords.insert_or_assign(recordID, sdr);
1340 recordID++;
1341 }
1342
1343 size_t nonSensorRecCount = fruCount + ipmi::storage::type12Count +
1344 ipmi::storage::nmDiscoverySDRCount;
1345 size_t type12End = fruCount + ipmi::storage::type12Count;
1346 do
1347 {
1348 size_t fruIndex = recordID - sensorTree.size();
1349
1350 if (fruIndex >= type12End)
1351 {
1352 // NM discovery SDR
1353 size_t nmDiscoveryIndex = fruIndex - type12End;
1354 if (nmDiscoveryIndex >= ipmi::storage::nmDiscoverySDRCount)
1355 {
1356 return GENERAL_ERROR;
1357 }
1358
1359 std::vector<uint8_t> record =
1360 ipmi::storage::getNMDiscoverySDR(nmDiscoveryIndex, recordID);
1361 sensorDataRecords.insert_or_assign(recordID, record);
1362 }
1363 else if (fruIndex >= fruCount)
1364 {
1365 // handle type 12 hardcoded records
1366 size_t type12Index = fruIndex - fruCount;
1367 if (type12Index >= ipmi::storage::type12Count)
1368 {
1369 return GENERAL_ERROR;
1370 }
1371 std::vector<uint8_t> record =
1372 ipmi::storage::getType12SDRs(type12Index, recordID);
1373 sensorDataRecords.insert_or_assign(recordID, record);
1374 }
1375 else
1376 {
1377 // handle fru records
1378 get_sdr::SensorDataFruRecord data;
1379 ret = ipmi::storage::getFruSdrs(ctx, fruIndex, data);
1380 if (ret != IPMI_CC_OK)
1381 {
1382 return GENERAL_ERROR;
1383 }
1384 get_sdr::header::set_record_id(
1385 recordID,
1386 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&data));
1387
1388 std::vector<uint8_t> record;
1389 record.insert(record.end(), (uint8_t*)&data,
1390 ((uint8_t*)&data) + sizeof(data));
1391 sensorDataRecords.insert_or_assign(recordID, record);
1392 }
1393 recordID++;
1394 } while (--nonSensorRecCount);
1395 return 0;
1396}
1397
1398/** @brief implements the get SDR Info command
1399 * @param count - Operation
1400 *
1401 * @returns IPMI completion code plus response data
1402 * - sdrCount - sensor/SDR count
1403 * - lunsAndDynamicPopulation - static/Dynamic sensor population flag
1404 */
1405static ipmi::RspType<uint8_t, // respcount
1406 uint8_t, // dynamic population flags
1407 uint32_t // last time a sensor was added
1408 >
1409 ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
1410 std::optional<uint8_t> count)
1411{
1412 uint8_t sdrCount = 0;
1413 // Sensors are dynamically allocated, and there is at least one LUN
1414 uint8_t lunsAndDynamicPopulation = 0x80;
1415 constexpr uint8_t getSdrCount = 0x01;
1416 constexpr uint8_t getSensorCount = 0x00;
1417
1418 if (!getSensorSubtree(sensorTree) || sensorTree.empty())
1419 {
1420 return ipmi::responseResponseError();
1421 }
1422
1423 if (sensorDataRecords.empty() && getSensorDataRecords(ctx))
1424 {
1425 return ipmi::responseResponseError();
1426 }
1427
1428 uint16_t numSensors = sensorTree.size();
1429 if (count.value_or(0) == getSdrCount)
1430 {
1431 // Count the number of Type 1 SDR entries assigned to the LUN
1432 for (auto sdr : sensorDataRecords)
1433 {
1434 get_sdr::SensorDataRecordHeader* hdr =
1435 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(
1436 sdr.second.data());
1437 if (hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD)
1438 {
1439 get_sdr::SensorDataFullRecord* record =
1440 reinterpret_cast<get_sdr::SensorDataFullRecord*>(
1441 sdr.second.data());
1442 if (ctx->lun == record->key.owner_lun)
1443 {
1444 sdrCount++;
1445 }
1446 }
1447 }
1448 }
1449 else if (count.value_or(0) == getSensorCount)
1450 {
1451 // Return the number of sensors attached to the LUN
1452 if ((ctx->lun == 0) && (numSensors > 0))
1453 {
1454 sdrCount =
1455 (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN : numSensors;
1456 }
1457 else if ((ctx->lun == 1) && (numSensors > maxSensorsPerLUN))
1458 {
1459 sdrCount = (numSensors > (2 * maxSensorsPerLUN))
1460 ? maxSensorsPerLUN
1461 : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
1462 }
1463 else if (ctx->lun == 3)
1464 {
1465 if (numSensors <= maxIPMISensors)
1466 {
1467 sdrCount =
1468 (numSensors - (2 * maxSensorsPerLUN)) & maxSensorsPerLUN;
1469 }
1470 else
1471 {
1472 // error
1473 throw std::out_of_range(
1474 "Maximum number of IPMI sensors exceeded.");
1475 }
1476 }
1477 }
1478 else
1479 {
1480 return ipmi::responseInvalidFieldRequest();
1481 }
1482
1483 // Get Sensor count. This returns the number of sensors
1484 if (numSensors > 0)
1485 {
1486 lunsAndDynamicPopulation |= 1;
1487 }
1488 if (numSensors > maxSensorsPerLUN)
1489 {
1490 lunsAndDynamicPopulation |= 2;
1491 }
1492 if (numSensors >= (maxSensorsPerLUN * 2))
1493 {
1494 lunsAndDynamicPopulation |= 8;
1495 }
1496 if (numSensors > maxIPMISensors)
1497 {
1498 // error
1499 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
1500 }
1501
1502 return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
1503 sdrLastAdd);
1504}
1505
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001506/* end sensor commands */
1507
1508/* storage commands */
1509
James Feist74c50c62019-08-14 14:18:41 -07001510ipmi::RspType<uint8_t, // sdr version
1511 uint16_t, // record count
1512 uint16_t, // free space
1513 uint32_t, // most recent addition
1514 uint32_t, // most recent erase
1515 uint8_t // operationSupport
1516 >
James Feist25690252019-12-23 12:25:49 -08001517 ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001518{
James Feist74c50c62019-08-14 14:18:41 -07001519 constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001520 if (sensorTree.empty() && !getSensorSubtree(sensorTree))
1521 {
James Feist74c50c62019-08-14 14:18:41 -07001522 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001523 }
1524
James Feist74c50c62019-08-14 14:18:41 -07001525 size_t fruCount = 0;
James Feist25690252019-12-23 12:25:49 -08001526 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
James Feist74c50c62019-08-14 14:18:41 -07001527 if (ret != ipmi::ccSuccess)
1528 {
1529 return ipmi::response(ret);
1530 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001531
James Feist74c50c62019-08-14 14:18:41 -07001532 uint16_t recordCount =
1533 sensorTree.size() + fruCount + ipmi::storage::type12Count;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001534
James Feist74c50c62019-08-14 14:18:41 -07001535 uint8_t operationSupport = static_cast<uint8_t>(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001536 SdrRepositoryInfoOps::overflow); // write not supported
James Feist74c50c62019-08-14 14:18:41 -07001537
1538 operationSupport |=
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001539 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
James Feist74c50c62019-08-14 14:18:41 -07001540 operationSupport |= static_cast<uint8_t>(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001541 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
James Feist74c50c62019-08-14 14:18:41 -07001542 return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
1543 unspecifiedFreeSpace, sdrLastAdd,
1544 sdrLastRemove, operationSupport);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001545}
1546
jayaprakash Mutyala6ae08182019-05-13 18:56:11 +00001547/** @brief implements the get SDR allocation info command
1548 *
1549 * @returns IPMI completion code plus response data
1550 * - allocUnits - Number of possible allocation units
1551 * - allocUnitSize - Allocation unit size in bytes.
1552 * - allocUnitFree - Number of free allocation units
1553 * - allocUnitLargestFree - Largest free block in allocation units
1554 * - maxRecordSize - Maximum record size in allocation units.
1555 */
1556ipmi::RspType<uint16_t, // allocUnits
1557 uint16_t, // allocUnitSize
1558 uint16_t, // allocUnitFree
1559 uint16_t, // allocUnitLargestFree
1560 uint8_t // maxRecordSize
1561 >
1562 ipmiStorageGetSDRAllocationInfo()
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001563{
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001564 // 0000h unspecified number of alloc units
jayaprakash Mutyala6ae08182019-05-13 18:56:11 +00001565 constexpr uint16_t allocUnits = 0;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001566
jayaprakash Mutyala6ae08182019-05-13 18:56:11 +00001567 constexpr uint16_t allocUnitFree = 0;
1568 constexpr uint16_t allocUnitLargestFree = 0;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001569 // only allow one block at a time
jayaprakash Mutyala6ae08182019-05-13 18:56:11 +00001570 constexpr uint8_t maxRecordSize = 1;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001571
jayaprakash Mutyala6ae08182019-05-13 18:56:11 +00001572 return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
1573 allocUnitLargestFree, maxRecordSize);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001574}
1575
jayaprakash Mutyala6ae08182019-05-13 18:56:11 +00001576/** @brief implements the reserve SDR command
1577 * @returns IPMI completion code plus response data
1578 * - sdrReservationID
1579 */
1580ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001581{
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001582 sdrReservationID++;
James Feista80cb902019-02-14 13:05:25 -08001583 if (sdrReservationID == 0)
1584 {
1585 sdrReservationID++;
1586 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001587
jayaprakash Mutyala6ae08182019-05-13 18:56:11 +00001588 return ipmi::responseSuccess(sdrReservationID);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001589}
1590
James Feistb49a98a2019-04-16 13:48:09 -07001591ipmi::RspType<uint16_t, // next record ID
1592 std::vector<uint8_t> // payload
1593 >
James Feist25690252019-12-23 12:25:49 -08001594 ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
1595 uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001596{
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001597 // reservation required for partial reads with non zero offset into
1598 // record
James Feistb49a98a2019-04-16 13:48:09 -07001599 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001600 {
James Feistb49a98a2019-04-16 13:48:09 -07001601 return ipmi::responseInvalidReservationId();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001602 }
1603
Johnathan Mantey308c3a82020-07-22 11:50:54 -07001604 if (sensorDataRecords.empty() && getSensorDataRecords(ctx))
1605 {
1606 return ipmi::responseResponseError();
1607 }
1608
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001609 if (sensorTree.empty() && !getSensorSubtree(sensorTree))
1610 {
James Feistb49a98a2019-04-16 13:48:09 -07001611 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001612 }
1613
1614 size_t fruCount = 0;
James Feist25690252019-12-23 12:25:49 -08001615 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
James Feist74c50c62019-08-14 14:18:41 -07001616 if (ret != ipmi::ccSuccess)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001617 {
James Feistb49a98a2019-04-16 13:48:09 -07001618 return ipmi::response(ret);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001619 }
1620
Yong Lifee5e4c2020-01-17 19:36:29 +08001621 size_t lastRecord = sensorTree.size() + fruCount +
1622 ipmi::storage::type12Count +
1623 ipmi::storage::nmDiscoverySDRCount - 1;
James Feistb49a98a2019-04-16 13:48:09 -07001624 if (recordID == lastRecordIndex)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001625 {
James Feistb49a98a2019-04-16 13:48:09 -07001626 recordID = lastRecord;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001627 }
James Feistb49a98a2019-04-16 13:48:09 -07001628 if (recordID > lastRecord)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001629 {
James Feistb49a98a2019-04-16 13:48:09 -07001630 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001631 }
1632
Johnathan Mantey308c3a82020-07-22 11:50:54 -07001633 get_sdr::SensorDataRecordHeader* hdr =
1634 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(
1635 sensorDataRecords[recordID].data());
Kuiying Wangb9109872021-01-15 15:54:59 +08001636 if (!hdr)
1637 {
1638 phosphor::logging::log<phosphor::logging::level::ERR>(
1639 "Error: record header is null");
1640 std::vector<uint8_t> emptyData;
1641 uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
1642 return ipmi::responseSuccess(nextRecordId, emptyData);
1643 }
1644
Johnathan Mantey308c3a82020-07-22 11:50:54 -07001645 size_t sdrLength =
1646 sizeof(get_sdr::SensorDataRecordHeader) + hdr->record_length;
1647 if (sdrLength < (offset + bytesToRead))
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001648 {
Johnathan Mantey308c3a82020-07-22 11:50:54 -07001649 bytesToRead = sdrLength - offset;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001650 }
1651
Johnathan Mantey308c3a82020-07-22 11:50:54 -07001652 uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
Kuiying Wangb9109872021-01-15 15:54:59 +08001653 if (!respStart)
1654 {
1655 phosphor::logging::log<phosphor::logging::level::ERR>(
1656 "Error: record is null");
1657 std::vector<uint8_t> emptyData;
1658 uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
1659 return ipmi::responseSuccess(nextRecordId, emptyData);
1660 }
James Feistb49a98a2019-04-16 13:48:09 -07001661 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
Johnathan Mantey308c3a82020-07-22 11:50:54 -07001662 uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
James Feistb49a98a2019-04-16 13:48:09 -07001663 return ipmi::responseSuccess(nextRecordId, recordData);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001664}
1665/* end storage commands */
1666
1667void registerSensorFunctions()
1668{
Jason M. Billsae6bdb12019-04-02 12:00:04 -07001669 // <Platform Event>
Vernon Mauery98bbf692019-09-16 11:14:59 -07001670 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnSensor,
1671 ipmi::sensor_event::cmdPlatformEvent,
1672 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
Jason M. Billsae6bdb12019-04-02 12:00:04 -07001673
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001674 // <Get Sensor Reading>
Vernon Mauery98bbf692019-09-16 11:14:59 -07001675 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnSensor,
1676 ipmi::sensor_event::cmdGetSensorReading,
1677 ipmi::Privilege::User, ipmiSenGetSensorReading);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001678
1679 // <Get Sensor Threshold>
Vernon Mauery98bbf692019-09-16 11:14:59 -07001680 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnSensor,
1681 ipmi::sensor_event::cmdGetSensorThreshold,
1682 ipmi::Privilege::User, ipmiSenGetSensorThresholds);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001683
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +00001684 // <Set Sensor Threshold>
Vernon Mauery98bbf692019-09-16 11:14:59 -07001685 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnSensor,
1686 ipmi::sensor_event::cmdSetSensorThreshold,
1687 ipmi::Privilege::Operator,
1688 ipmiSenSetSensorThresholds);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001689
1690 // <Get Sensor Event Enable>
Vernon Mauery98bbf692019-09-16 11:14:59 -07001691 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnSensor,
1692 ipmi::sensor_event::cmdGetSensorEventEnable,
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +00001693 ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001694
1695 // <Get Sensor Event Status>
jayaprakash Mutyalaccf88f62019-05-13 16:57:16 +00001696 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnSensor,
1697 ipmi::sensor_event::cmdGetSensorEventStatus,
1698 ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001699
1700 // register all storage commands for both Sensor and Storage command
1701 // versions
1702
1703 // <Get SDR Repository Info>
Vernon Mauery98bbf692019-09-16 11:14:59 -07001704 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnStorage,
1705 ipmi::storage::cmdGetSdrRepositoryInfo,
1706 ipmi::Privilege::User,
1707 ipmiStorageGetSDRRepositoryInfo);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001708
Johnathan Mantey308c3a82020-07-22 11:50:54 -07001709 // <Get Device SDR Info>
1710 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1711 ipmi::sensor_event::cmdGetDeviceSdrInfo,
1712 ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
1713
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001714 // <Get SDR Allocation Info>
Vernon Mauery98bbf692019-09-16 11:14:59 -07001715 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnStorage,
1716 ipmi::storage::cmdGetSdrRepositoryAllocInfo,
1717 ipmi::Privilege::User,
1718 ipmiStorageGetSDRAllocationInfo);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001719
1720 // <Reserve SDR Repo>
Vernon Mauery98bbf692019-09-16 11:14:59 -07001721 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnSensor,
1722 ipmi::sensor_event::cmdReserveDeviceSdrRepository,
jayaprakash Mutyala6ae08182019-05-13 18:56:11 +00001723 ipmi::Privilege::User, ipmiStorageReserveSDR);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001724
Vernon Mauery98bbf692019-09-16 11:14:59 -07001725 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnStorage,
1726 ipmi::storage::cmdReserveSdrRepository,
1727 ipmi::Privilege::User, ipmiStorageReserveSDR);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001728
1729 // <Get Sdr>
Vernon Mauery98bbf692019-09-16 11:14:59 -07001730 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnSensor,
1731 ipmi::sensor_event::cmdGetDeviceSdr,
1732 ipmi::Privilege::User, ipmiStorageGetSDR);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001733
Vernon Mauery98bbf692019-09-16 11:14:59 -07001734 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnStorage,
1735 ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
1736 ipmiStorageGetSDR);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001737}
1738} // namespace ipmi