blob: 98051e5b1c90a11790d74b69eed28a2c55a673f4 [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;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070056
57constexpr size_t maxSDRTotalSize =
58 76; // Largest SDR Record Size (type 01) + SDR Overheader Size
59constexpr static const uint32_t noTimestamp = 0xFFFFFFFF;
60
61static uint16_t sdrReservationID;
62static uint32_t sdrLastAdd = noTimestamp;
63static uint32_t sdrLastRemove = noTimestamp;
Johnathan Mantey308c3a82020-07-22 11:50:54 -070064static constexpr size_t lastRecordIndex = 0xFFFF;
65static constexpr int GENERAL_ERROR = -1;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070066
Richard Marian Thomaiyar01fbcb52018-11-19 22:04:34 +053067SensorSubTree sensorTree;
Johnathan Mantey308c3a82020-07-22 11:50:54 -070068SDRObjectType sensorDataRecords;
69
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070070static boost::container::flat_map<std::string, ManagedObjectType> SensorCache;
71
Jason M. Bills17add592018-11-12 14:30:12 -080072// Specify the comparison required to sort and find char* map objects
73struct CmpStr
74{
James Feistfcd2d3a2020-05-28 10:38:15 -070075 bool operator()(const char* a, const char* b) const
Jason M. Bills17add592018-11-12 14:30:12 -080076 {
77 return std::strcmp(a, b) < 0;
78 }
79};
James Feistfcd2d3a2020-05-28 10:38:15 -070080const static boost::container::flat_map<const char*, SensorUnits, CmpStr>
Jason M. Bills17add592018-11-12 14:30:12 -080081 sensorUnits{{{"temperature", SensorUnits::degreesC},
82 {"voltage", SensorUnits::volts},
83 {"current", SensorUnits::amps},
84 {"fan_tach", SensorUnits::rpm},
85 {"power", SensorUnits::watts}}};
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070086
87void registerSensorFunctions() __attribute__((constructor));
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070088
89static sdbusplus::bus::match::match sensorAdded(
Vernon Mauery15419dd2019-05-24 09:40:30 -070090 *getSdBus(),
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070091 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
92 "sensors/'",
James Feistfcd2d3a2020-05-28 10:38:15 -070093 [](sdbusplus::message::message& m) {
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070094 sensorTree.clear();
95 sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>(
96 std::chrono::system_clock::now().time_since_epoch())
97 .count();
98 });
99
100static sdbusplus::bus::match::match sensorRemoved(
Vernon Mauery15419dd2019-05-24 09:40:30 -0700101 *getSdBus(),
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700102 "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/"
103 "sensors/'",
James Feistfcd2d3a2020-05-28 10:38:15 -0700104 [](sdbusplus::message::message& m) {
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700105 sensorTree.clear();
106 sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>(
107 std::chrono::system_clock::now().time_since_epoch())
108 .count();
109 });
110
James Feist392786a2019-03-19 13:36:10 -0700111// this keeps track of deassertions for sensor event status command. A
112// deasertion can only happen if an assertion was seen first.
113static boost::container::flat_map<
114 std::string, boost::container::flat_map<std::string, std::optional<bool>>>
115 thresholdDeassertMap;
116
117static sdbusplus::bus::match::match thresholdChanged(
Vernon Mauery15419dd2019-05-24 09:40:30 -0700118 *getSdBus(),
James Feist392786a2019-03-19 13:36:10 -0700119 "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus."
120 "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
James Feistfcd2d3a2020-05-28 10:38:15 -0700121 [](sdbusplus::message::message& m) {
James Feist392786a2019-03-19 13:36:10 -0700122 boost::container::flat_map<std::string, std::variant<bool, double>>
123 values;
124 m.read(std::string(), values);
125
126 auto findAssert =
James Feistfcd2d3a2020-05-28 10:38:15 -0700127 std::find_if(values.begin(), values.end(), [](const auto& pair) {
James Feist392786a2019-03-19 13:36:10 -0700128 return pair.first.find("Alarm") != std::string::npos;
129 });
130 if (findAssert != values.end())
131 {
132 auto ptr = std::get_if<bool>(&(findAssert->second));
133 if (ptr == nullptr)
134 {
135 phosphor::logging::log<phosphor::logging::level::ERR>(
136 "thresholdChanged: Assert non bool");
137 return;
138 }
139 if (*ptr)
140 {
141 phosphor::logging::log<phosphor::logging::level::INFO>(
142 "thresholdChanged: Assert",
143 phosphor::logging::entry("SENSOR=%s", m.get_path()));
144 thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr;
145 }
146 else
147 {
James Feistfcd2d3a2020-05-28 10:38:15 -0700148 auto& value =
James Feist392786a2019-03-19 13:36:10 -0700149 thresholdDeassertMap[m.get_path()][findAssert->first];
150 if (value)
151 {
152 phosphor::logging::log<phosphor::logging::level::INFO>(
153 "thresholdChanged: deassert",
154 phosphor::logging::entry("SENSOR=%s", m.get_path()));
155 value = *ptr;
156 }
157 }
158 }
159 });
160
James Feistfcd2d3a2020-05-28 10:38:15 -0700161static void getSensorMaxMin(const SensorMap& sensorMap, double& max,
162 double& min)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700163{
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700164 max = 127;
165 min = -128;
166
James Feistaecaef72019-04-26 10:30:32 -0700167 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
168 auto critical =
169 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
170 auto warning =
171 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
172
173 if (sensorObject != sensorMap.end())
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700174 {
James Feistaecaef72019-04-26 10:30:32 -0700175 auto maxMap = sensorObject->second.find("MaxValue");
176 auto minMap = sensorObject->second.find("MinValue");
177
178 if (maxMap != sensorObject->second.end())
179 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700180 max = std::visit(VariantToDoubleVisitor(), maxMap->second);
James Feistaecaef72019-04-26 10:30:32 -0700181 }
182 if (minMap != sensorObject->second.end())
183 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700184 min = std::visit(VariantToDoubleVisitor(), minMap->second);
James Feistaecaef72019-04-26 10:30:32 -0700185 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700186 }
James Feistaecaef72019-04-26 10:30:32 -0700187 if (critical != sensorMap.end())
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700188 {
James Feistaecaef72019-04-26 10:30:32 -0700189 auto lower = critical->second.find("CriticalLow");
190 auto upper = critical->second.find("CriticalHigh");
191 if (lower != critical->second.end())
192 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700193 double value = std::visit(VariantToDoubleVisitor(), lower->second);
James Feistaecaef72019-04-26 10:30:32 -0700194 min = std::min(value, min);
195 }
196 if (upper != critical->second.end())
197 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700198 double value = std::visit(VariantToDoubleVisitor(), upper->second);
James Feistaecaef72019-04-26 10:30:32 -0700199 max = std::max(value, max);
200 }
201 }
202 if (warning != sensorMap.end())
203 {
204
205 auto lower = warning->second.find("WarningLow");
206 auto upper = warning->second.find("WarningHigh");
207 if (lower != warning->second.end())
208 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700209 double value = std::visit(VariantToDoubleVisitor(), lower->second);
James Feistaecaef72019-04-26 10:30:32 -0700210 min = std::min(value, min);
211 }
212 if (upper != warning->second.end())
213 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700214 double value = std::visit(VariantToDoubleVisitor(), upper->second);
James Feistaecaef72019-04-26 10:30:32 -0700215 max = std::max(value, max);
216 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700217 }
218}
219
James Feist25690252019-12-23 12:25:49 -0800220static bool getSensorMap(boost::asio::yield_context yield,
221 std::string sensorConnection, std::string sensorPath,
James Feistfcd2d3a2020-05-28 10:38:15 -0700222 SensorMap& sensorMap)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700223{
224 static boost::container::flat_map<
225 std::string, std::chrono::time_point<std::chrono::steady_clock>>
226 updateTimeMap;
227
228 auto updateFind = updateTimeMap.find(sensorConnection);
229 auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
230 if (updateFind != updateTimeMap.end())
231 {
232 lastUpdate = updateFind->second;
233 }
234
235 auto now = std::chrono::steady_clock::now();
236
237 if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
238 .count() > sensorMapUpdatePeriod)
239 {
240 updateTimeMap[sensorConnection] = now;
241
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;
257 }
258 auto connection = SensorCache.find(sensorConnection);
259 if (connection == SensorCache.end())
260 {
261 return false;
262 }
263 auto path = connection->second.find(sensorPath);
264 if (path == connection->second.end())
265 {
266 return false;
267 }
268 sensorMap = path->second;
269
270 return true;
271}
272
273/* sensor commands */
James Feist7aaf3fe2019-06-25 11:52:11 -0700274namespace meHealth
275{
James Feistfcd2d3a2020-05-28 10:38:15 -0700276constexpr const char* busname = "xyz.openbmc_project.NodeManagerProxy";
277constexpr const char* path = "/xyz/openbmc_project/status/me";
278constexpr const char* interface = "xyz.openbmc_project.SetHealth";
279constexpr const char* method = "SetHealth";
280constexpr const char* critical = "critical";
281constexpr const char* warning = "warning";
282constexpr const char* ok = "ok";
James Feist7aaf3fe2019-06-25 11:52:11 -0700283} // namespace meHealth
284
285static void setMeStatus(uint8_t eventData2, uint8_t eventData3, bool disable)
286{
287 constexpr const std::array<uint8_t, 10> critical = {
288 0x1, 0x2, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xD, 0xE};
289 constexpr const std::array<uint8_t, 5> warning = {0x3, 0xA, 0x13, 0x19,
290 0x1A};
291
292 std::string state;
293 if (std::find(critical.begin(), critical.end(), eventData2) !=
294 critical.end())
295 {
296 state = meHealth::critical;
297 }
298 // special case 0x3 as we only care about a few states
299 else if (eventData2 == 0x3)
300 {
301 if (eventData3 <= 0x2)
302 {
303 state = meHealth::warning;
304 }
305 else
306 {
307 return;
308 }
309 }
310 else if (std::find(warning.begin(), warning.end(), eventData2) !=
311 warning.end())
312 {
313 state = meHealth::warning;
314 }
315 else
316 {
317 return;
318 }
319 if (disable)
320 {
321 state = meHealth::ok;
322 }
323
324 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
325 auto setHealth =
326 dbus->new_method_call(meHealth::busname, meHealth::path,
327 meHealth::interface, meHealth::method);
328 setHealth.append(std::to_string(static_cast<size_t>(eventData2)), state);
329 try
330 {
331 dbus->call(setHealth);
332 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700333 catch (sdbusplus::exception_t&)
James Feist7aaf3fe2019-06-25 11:52:11 -0700334 {
335 phosphor::logging::log<phosphor::logging::level::ERR>(
336 "Failed to set ME Health");
337 }
338}
339
James Feistfcd2d3a2020-05-28 10:38:15 -0700340ipmi::RspType<> ipmiSenPlatformEvent(ipmi::message::Payload& p)
Jason M. Billsae6bdb12019-04-02 12:00:04 -0700341{
James Feist7aaf3fe2019-06-25 11:52:11 -0700342 constexpr const uint8_t meId = 0x2C;
343 constexpr const uint8_t meSensorNum = 0x17;
344 constexpr const uint8_t disabled = 0x80;
345
Jason M. Billsae6bdb12019-04-02 12:00:04 -0700346 uint8_t generatorID = 0;
347 uint8_t evmRev = 0;
348 uint8_t sensorType = 0;
349 uint8_t sensorNum = 0;
350 uint8_t eventType = 0;
351 uint8_t eventData1 = 0;
352 std::optional<uint8_t> eventData2 = 0;
353 std::optional<uint8_t> eventData3 = 0;
354
355 // todo: This check is supposed to be based on the incoming channel.
356 // e.g. system channel will provide upto 8 bytes including generator
357 // ID, but ipmb channel will provide only up to 7 bytes without the
358 // generator ID.
359 // Support for this check is coming in future patches, so for now just base
360 // it on if the first byte is the EvMRev (0x04).
361 if (p.size() && p.data()[0] == 0x04)
362 {
363 p.unpack(evmRev, sensorType, sensorNum, eventType, eventData1,
364 eventData2, eventData3);
365 // todo: the generator ID for this channel is supposed to come from the
366 // IPMB requesters slave address. Support for this is coming in future
367 // patches, so for now just assume it is coming from the ME (0x2C).
368 generatorID = 0x2C;
369 }
370 else
371 {
372 p.unpack(generatorID, evmRev, sensorType, sensorNum, eventType,
373 eventData1, eventData2, eventData3);
374 }
375 if (!p.fullyUnpacked())
376 {
377 return ipmi::responseReqDataLenInvalid();
378 }
379
Jason M. Bills6dd8f042019-04-11 10:39:02 -0700380 // Send this request to the Redfish hooks to log it as a Redfish message
381 // instead. There is no need to add it to the SEL, so just return success.
382 intel_oem::ipmi::sel::checkRedfishHooks(
383 generatorID, evmRev, sensorType, sensorNum, eventType, eventData1,
384 eventData2.value_or(0xFF), eventData3.value_or(0xFF));
Jason M. Billsae6bdb12019-04-02 12:00:04 -0700385
James Feist7aaf3fe2019-06-25 11:52:11 -0700386 if (generatorID == meId && sensorNum == meSensorNum && eventData2 &&
387 eventData3)
388 {
389 setMeStatus(*eventData2, *eventData3, (eventType & disabled));
390 }
391
Jason M. Billsae6bdb12019-04-02 12:00:04 -0700392 return ipmi::responseSuccess();
393}
394
James Feist0cd014a2019-04-08 15:04:33 -0700395ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700396 ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700397{
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700398 std::string connection;
399 std::string path;
400
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700401 auto status = getSensorConnection(ctx, sensnum, connection, path);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700402 if (status)
403 {
James Feist0cd014a2019-04-08 15:04:33 -0700404 return ipmi::response(status);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700405 }
406
407 SensorMap sensorMap;
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700408 if (!getSensorMap(ctx->yield, connection, path, sensorMap))
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700409 {
James Feist0cd014a2019-04-08 15:04:33 -0700410 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700411 }
412 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
413
414 if (sensorObject == sensorMap.end() ||
415 sensorObject->second.find("Value") == sensorObject->second.end())
416 {
James Feist0cd014a2019-04-08 15:04:33 -0700417 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700418 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700419 auto& valueVariant = sensorObject->second["Value"];
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700420 double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700421
Yong Li1f2eb5e2019-05-23 14:07:17 +0800422 double max = 0;
423 double min = 0;
James Feistaecaef72019-04-26 10:30:32 -0700424 getSensorMaxMin(sensorMap, max, min);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700425
426 int16_t mValue = 0;
427 int16_t bValue = 0;
428 int8_t rExp = 0;
429 int8_t bExp = 0;
430 bool bSigned = false;
431
432 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
433 {
James Feist0cd014a2019-04-08 15:04:33 -0700434 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700435 }
436
James Feist0cd014a2019-04-08 15:04:33 -0700437 uint8_t value =
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700438 scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
James Feist0cd014a2019-04-08 15:04:33 -0700439 uint8_t operation =
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700440 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
James Feist0cd014a2019-04-08 15:04:33 -0700441 operation |=
James Feist81a95c12019-03-01 15:08:28 -0800442 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
James Feist1ee6ed82020-06-17 16:16:50 -0700443 bool notReading = std::isnan(reading);
444
445 if (!notReading)
446 {
447 auto availableObject =
448 sensorMap.find("xyz.openbmc_project.State.Decorator.Availability");
449 if (availableObject != sensorMap.end())
450 {
451 auto findAvailable = availableObject->second.find("Available");
452 if (findAvailable != availableObject->second.end())
453 {
454 bool* available = std::get_if<bool>(&(findAvailable->second));
455 if (available && !(*available))
456 {
457 notReading = true;
458 }
459 }
460 }
461 }
462
463 if (notReading)
464 {
465 operation |= static_cast<uint8_t>(
466 IPMISensorReadingByte2::readingStateUnavailable);
467 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700468
James Feist0cd014a2019-04-08 15:04:33 -0700469 uint8_t thresholds = 0;
470
471 auto warningObject =
472 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
473 if (warningObject != sensorMap.end())
474 {
475 auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
476 auto alarmLow = warningObject->second.find("WarningAlarmLow");
477 if (alarmHigh != warningObject->second.end())
478 {
479 if (std::get<bool>(alarmHigh->second))
480 {
481 thresholds |= static_cast<uint8_t>(
482 IPMISensorReadingByte3::upperNonCritical);
483 }
484 }
485 if (alarmLow != warningObject->second.end())
486 {
487 if (std::get<bool>(alarmLow->second))
488 {
489 thresholds |= static_cast<uint8_t>(
490 IPMISensorReadingByte3::lowerNonCritical);
491 }
492 }
493 }
494
495 auto criticalObject =
496 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
497 if (criticalObject != sensorMap.end())
498 {
499 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
500 auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
501 if (alarmHigh != criticalObject->second.end())
502 {
503 if (std::get<bool>(alarmHigh->second))
504 {
505 thresholds |=
506 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
507 }
508 }
509 if (alarmLow != criticalObject->second.end())
510 {
511 if (std::get<bool>(alarmLow->second))
512 {
513 thresholds |=
514 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
515 }
516 }
517 }
518
519 // no discrete as of today so optional byte is never returned
520 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700521}
522
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000523/** @brief implements the Set Sensor threshold command
524 * @param sensorNumber - sensor number
525 * @param lowerNonCriticalThreshMask
526 * @param lowerCriticalThreshMask
527 * @param lowerNonRecovThreshMask
528 * @param upperNonCriticalThreshMask
529 * @param upperCriticalThreshMask
530 * @param upperNonRecovThreshMask
531 * @param reserved
532 * @param lowerNonCritical - lower non-critical threshold
533 * @param lowerCritical - Lower critical threshold
534 * @param lowerNonRecoverable - Lower non recovarable threshold
535 * @param upperNonCritical - Upper non-critical threshold
536 * @param upperCritical - Upper critical
537 * @param upperNonRecoverable - Upper Non-recoverable
538 *
539 * @returns IPMI completion code
540 */
541ipmi::RspType<> ipmiSenSetSensorThresholds(
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700542 ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
543 bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
544 bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
545 bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
546 uint8_t lowerCritical, uint8_t lowerNonRecoverable,
547 uint8_t upperNonCritical, uint8_t upperCritical,
548 uint8_t upperNonRecoverable)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700549{
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000550 constexpr uint8_t thresholdMask = 0xFF;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700551
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000552 if (reserved)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700553 {
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000554 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700555 }
556
557 // lower nc and upper nc not suppported on any sensor
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000558 if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700559 {
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000560 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700561 }
562
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000563 // if none of the threshold mask are set, nothing to do
564 if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
565 lowerNonRecovThreshMask | upperNonCriticalThreshMask |
566 upperCriticalThreshMask | upperNonRecovThreshMask))
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700567 {
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000568 return ipmi::responseSuccess();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700569 }
570
571 std::string connection;
572 std::string path;
573
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700574 ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700575 if (status)
576 {
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000577 return ipmi::response(status);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700578 }
579 SensorMap sensorMap;
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700580 if (!getSensorMap(ctx->yield, connection, path, sensorMap))
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700581 {
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000582 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700583 }
584
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700585 double max = 0;
586 double min = 0;
James Feistaecaef72019-04-26 10:30:32 -0700587 getSensorMaxMin(sensorMap, max, min);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700588
589 int16_t mValue = 0;
590 int16_t bValue = 0;
591 int8_t rExp = 0;
592 int8_t bExp = 0;
593 bool bSigned = false;
594
595 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
596 {
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000597 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700598 }
599
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700600 // store a vector of property name, value to set, and interface
601 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
602
603 // define the indexes of the tuple
604 constexpr uint8_t propertyName = 0;
605 constexpr uint8_t thresholdValue = 1;
606 constexpr uint8_t interface = 2;
607 // verifiy all needed fields are present
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000608 if (lowerCriticalThreshMask || upperCriticalThreshMask)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700609 {
610 auto findThreshold =
611 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
612 if (findThreshold == sensorMap.end())
613 {
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000614 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700615 }
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000616 if (lowerCriticalThreshMask)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700617 {
618 auto findLower = findThreshold->second.find("CriticalLow");
619 if (findLower == findThreshold->second.end())
620 {
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000621 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700622 }
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000623 thresholdsToSet.emplace_back("CriticalLow", lowerCritical,
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700624 findThreshold->first);
625 }
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000626 if (upperCriticalThreshMask)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700627 {
628 auto findUpper = findThreshold->second.find("CriticalHigh");
629 if (findUpper == findThreshold->second.end())
630 {
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000631 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700632 }
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000633 thresholdsToSet.emplace_back("CriticalHigh", upperCritical,
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700634 findThreshold->first);
635 }
636 }
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000637 if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700638 {
639 auto findThreshold =
640 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
641 if (findThreshold == sensorMap.end())
642 {
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000643 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700644 }
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000645 if (lowerNonCriticalThreshMask)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700646 {
647 auto findLower = findThreshold->second.find("WarningLow");
648 if (findLower == findThreshold->second.end())
649 {
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000650 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700651 }
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000652 thresholdsToSet.emplace_back("WarningLow", lowerNonCritical,
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700653 findThreshold->first);
654 }
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000655 if (upperNonCriticalThreshMask)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700656 {
657 auto findUpper = findThreshold->second.find("WarningHigh");
658 if (findUpper == findThreshold->second.end())
659 {
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000660 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700661 }
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000662 thresholdsToSet.emplace_back("WarningHigh", upperNonCritical,
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700663 findThreshold->first);
664 }
665 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700666 for (const auto& property : thresholdsToSet)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700667 {
668 // from section 36.3 in the IPMI Spec, assume all linear
669 double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
Josh Lehan86236a22019-11-18 17:53:33 -0800670 (bValue * std::pow(10.0, bExp))) *
671 std::pow(10.0, rExp);
Vernon Mauery15419dd2019-05-24 09:40:30 -0700672 setDbusProperty(
673 *getSdBus(), connection, path, std::get<interface>(property),
674 std::get<propertyName>(property), ipmi::Value(valueToSet));
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700675 }
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000676 return ipmi::responseSuccess();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700677}
678
James Feistfcd2d3a2020-05-28 10:38:15 -0700679IPMIThresholds getIPMIThresholds(const SensorMap& sensorMap)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700680{
James Feist902c4c52019-04-16 14:51:31 -0700681 IPMIThresholds resp;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700682 auto warningInterface =
683 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
684 auto criticalInterface =
685 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
686
687 if ((warningInterface != sensorMap.end()) ||
688 (criticalInterface != sensorMap.end()))
689 {
690 auto sensorPair = sensorMap.find("xyz.openbmc_project.Sensor.Value");
691
692 if (sensorPair == sensorMap.end())
693 {
694 // should not have been able to find a sensor not implementing
695 // the sensor object
James Feist902c4c52019-04-16 14:51:31 -0700696 throw std::runtime_error("Invalid sensor map");
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700697 }
698
Chen,Yugang606dd9f2019-07-01 10:37:30 +0800699 double max = 0;
700 double min = 0;
James Feistaecaef72019-04-26 10:30:32 -0700701 getSensorMaxMin(sensorMap, max, min);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700702
703 int16_t mValue = 0;
704 int16_t bValue = 0;
705 int8_t rExp = 0;
706 int8_t bExp = 0;
707 bool bSigned = false;
708
709 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
710 {
James Feist902c4c52019-04-16 14:51:31 -0700711 throw std::runtime_error("Invalid sensor atrributes");
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700712 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700713 if (warningInterface != sensorMap.end())
714 {
James Feistfcd2d3a2020-05-28 10:38:15 -0700715 auto& warningMap = warningInterface->second;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700716
717 auto warningHigh = warningMap.find("WarningHigh");
718 auto warningLow = warningMap.find("WarningLow");
719
720 if (warningHigh != warningMap.end())
721 {
James Feist902c4c52019-04-16 14:51:31 -0700722
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700723 double value =
724 std::visit(VariantToDoubleVisitor(), warningHigh->second);
James Feist902c4c52019-04-16 14:51:31 -0700725 resp.warningHigh = scaleIPMIValueFromDouble(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700726 value, mValue, rExp, bValue, bExp, bSigned);
727 }
728 if (warningLow != warningMap.end())
729 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700730 double value =
731 std::visit(VariantToDoubleVisitor(), warningLow->second);
James Feist902c4c52019-04-16 14:51:31 -0700732 resp.warningLow = scaleIPMIValueFromDouble(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700733 value, mValue, rExp, bValue, bExp, bSigned);
734 }
735 }
736 if (criticalInterface != sensorMap.end())
737 {
James Feistfcd2d3a2020-05-28 10:38:15 -0700738 auto& criticalMap = criticalInterface->second;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700739
740 auto criticalHigh = criticalMap.find("CriticalHigh");
741 auto criticalLow = criticalMap.find("CriticalLow");
742
743 if (criticalHigh != criticalMap.end())
744 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700745 double value =
746 std::visit(VariantToDoubleVisitor(), criticalHigh->second);
James Feist902c4c52019-04-16 14:51:31 -0700747 resp.criticalHigh = scaleIPMIValueFromDouble(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700748 value, mValue, rExp, bValue, bExp, bSigned);
749 }
750 if (criticalLow != criticalMap.end())
751 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700752 double value =
753 std::visit(VariantToDoubleVisitor(), criticalLow->second);
James Feist902c4c52019-04-16 14:51:31 -0700754 resp.criticalLow = scaleIPMIValueFromDouble(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700755 value, mValue, rExp, bValue, bExp, bSigned);
756 }
757 }
758 }
James Feist902c4c52019-04-16 14:51:31 -0700759 return resp;
760}
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700761
James Feist902c4c52019-04-16 14:51:31 -0700762ipmi::RspType<uint8_t, // readable
763 uint8_t, // lowerNCrit
764 uint8_t, // lowerCrit
765 uint8_t, // lowerNrecoverable
766 uint8_t, // upperNC
767 uint8_t, // upperCrit
768 uint8_t> // upperNRecoverable
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700769 ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber)
James Feist902c4c52019-04-16 14:51:31 -0700770{
771 std::string connection;
772 std::string path;
773
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700774 auto status = getSensorConnection(ctx, sensorNumber, connection, path);
James Feist902c4c52019-04-16 14:51:31 -0700775 if (status)
776 {
777 return ipmi::response(status);
778 }
779
780 SensorMap sensorMap;
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700781 if (!getSensorMap(ctx->yield, connection, path, sensorMap))
James Feist902c4c52019-04-16 14:51:31 -0700782 {
783 return ipmi::responseResponseError();
784 }
785
786 IPMIThresholds thresholdData;
787 try
788 {
789 thresholdData = getIPMIThresholds(sensorMap);
790 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700791 catch (std::exception&)
James Feist902c4c52019-04-16 14:51:31 -0700792 {
793 return ipmi::responseResponseError();
794 }
795
796 uint8_t readable = 0;
797 uint8_t lowerNC = 0;
798 uint8_t lowerCritical = 0;
799 uint8_t lowerNonRecoverable = 0;
800 uint8_t upperNC = 0;
801 uint8_t upperCritical = 0;
802 uint8_t upperNonRecoverable = 0;
803
804 if (thresholdData.warningHigh)
805 {
806 readable |=
807 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
808 upperNC = *thresholdData.warningHigh;
809 }
810 if (thresholdData.warningLow)
811 {
812 readable |=
813 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
814 lowerNC = *thresholdData.warningLow;
815 }
816
817 if (thresholdData.criticalHigh)
818 {
819 readable |=
820 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
821 upperCritical = *thresholdData.criticalHigh;
822 }
823 if (thresholdData.criticalLow)
824 {
825 readable |=
826 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
827 lowerCritical = *thresholdData.criticalLow;
828 }
829
830 return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
831 lowerNonRecoverable, upperNC, upperCritical,
832 upperNonRecoverable);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700833}
834
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000835/** @brief implements the get Sensor event enable command
836 * @param sensorNumber - sensor number
837 *
838 * @returns IPMI completion code plus response data
839 * - enabled - Sensor Event messages
840 * - assertionEnabledLsb - Assertion event messages
841 * - assertionEnabledMsb - Assertion event messages
842 * - deassertionEnabledLsb - Deassertion event messages
843 * - deassertionEnabledMsb - Deassertion event messages
844 */
845
846ipmi::RspType<uint8_t, // enabled
847 uint8_t, // assertionEnabledLsb
848 uint8_t, // assertionEnabledMsb
849 uint8_t, // deassertionEnabledLsb
850 uint8_t> // deassertionEnabledMsb
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700851 ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700852{
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700853 std::string connection;
854 std::string path;
855
Patrick Venturea41714c2019-09-25 16:59:41 -0700856 uint8_t enabled = 0;
857 uint8_t assertionEnabledLsb = 0;
858 uint8_t assertionEnabledMsb = 0;
859 uint8_t deassertionEnabledLsb = 0;
860 uint8_t deassertionEnabledMsb = 0;
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000861
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700862 auto status = getSensorConnection(ctx, sensorNum, connection, path);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700863 if (status)
864 {
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000865 return ipmi::response(status);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700866 }
867
868 SensorMap sensorMap;
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700869 if (!getSensorMap(ctx->yield, connection, path, sensorMap))
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700870 {
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000871 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700872 }
873
874 auto warningInterface =
875 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
876 auto criticalInterface =
877 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700878 if ((warningInterface != sensorMap.end()) ||
879 (criticalInterface != sensorMap.end()))
880 {
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000881 enabled = static_cast<uint8_t>(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700882 IPMISensorEventEnableByte2::sensorScanningEnable);
883 if (warningInterface != sensorMap.end())
884 {
James Feistfcd2d3a2020-05-28 10:38:15 -0700885 auto& warningMap = warningInterface->second;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700886
887 auto warningHigh = warningMap.find("WarningHigh");
888 auto warningLow = warningMap.find("WarningLow");
889 if (warningHigh != warningMap.end())
890 {
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000891 assertionEnabledLsb |= static_cast<uint8_t>(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700892 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000893 deassertionEnabledLsb |= static_cast<uint8_t>(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700894 IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
895 }
896 if (warningLow != warningMap.end())
897 {
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000898 assertionEnabledLsb |= static_cast<uint8_t>(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700899 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000900 deassertionEnabledLsb |= static_cast<uint8_t>(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700901 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
902 }
903 }
904 if (criticalInterface != sensorMap.end())
905 {
James Feistfcd2d3a2020-05-28 10:38:15 -0700906 auto& criticalMap = criticalInterface->second;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700907
908 auto criticalHigh = criticalMap.find("CriticalHigh");
909 auto criticalLow = criticalMap.find("CriticalLow");
910
911 if (criticalHigh != criticalMap.end())
912 {
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000913 assertionEnabledMsb |= static_cast<uint8_t>(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700914 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000915 deassertionEnabledMsb |= static_cast<uint8_t>(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700916 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
917 }
918 if (criticalLow != criticalMap.end())
919 {
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000920 assertionEnabledLsb |= static_cast<uint8_t>(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700921 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000922 deassertionEnabledLsb |= static_cast<uint8_t>(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700923 IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
924 }
925 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700926 }
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +0000927
928 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
929 assertionEnabledMsb, deassertionEnabledLsb,
930 deassertionEnabledMsb);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700931}
932
jayaprakash Mutyalaccf88f62019-05-13 16:57:16 +0000933/** @brief implements the get Sensor event status command
934 * @param sensorNumber - sensor number, FFh = reserved
935 *
936 * @returns IPMI completion code plus response data
937 * - sensorEventStatus - Sensor Event messages state
938 * - assertions - Assertion event messages
939 * - deassertions - Deassertion event messages
940 */
941ipmi::RspType<uint8_t, // sensorEventStatus
942 std::bitset<16>, // assertions
943 std::bitset<16> // deassertion
944 >
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700945 ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700946{
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700947 if (sensorNum == reservedSensorNumber)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700948 {
jayaprakash Mutyalaccf88f62019-05-13 16:57:16 +0000949 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700950 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700951
952 std::string connection;
953 std::string path;
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700954 auto status = getSensorConnection(ctx, sensorNum, connection, path);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700955 if (status)
956 {
jayaprakash Mutyalaccf88f62019-05-13 16:57:16 +0000957 phosphor::logging::log<phosphor::logging::level::ERR>(
958 "ipmiSenGetSensorEventStatus: Sensor connection Error",
959 phosphor::logging::entry("SENSOR=%d", sensorNum));
960 return ipmi::response(status);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700961 }
962
963 SensorMap sensorMap;
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700964 if (!getSensorMap(ctx->yield, connection, path, sensorMap))
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700965 {
jayaprakash Mutyalaccf88f62019-05-13 16:57:16 +0000966 phosphor::logging::log<phosphor::logging::level::ERR>(
967 "ipmiSenGetSensorEventStatus: Sensor Mapping Error",
968 phosphor::logging::entry("SENSOR=%s", path.c_str()));
969 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700970 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700971 auto warningInterface =
972 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
973 auto criticalInterface =
974 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
975
jayaprakash Mutyalaccf88f62019-05-13 16:57:16 +0000976 uint8_t sensorEventStatus =
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700977 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
978
James Feist392786a2019-03-19 13:36:10 -0700979 std::optional<bool> criticalDeassertHigh =
980 thresholdDeassertMap[path]["CriticalAlarmHigh"];
981 std::optional<bool> criticalDeassertLow =
982 thresholdDeassertMap[path]["CriticalAlarmLow"];
983 std::optional<bool> warningDeassertHigh =
984 thresholdDeassertMap[path]["WarningAlarmHigh"];
985 std::optional<bool> warningDeassertLow =
986 thresholdDeassertMap[path]["WarningAlarmLow"];
987
jayaprakash Mutyalaccf88f62019-05-13 16:57:16 +0000988 std::bitset<16> assertions = 0;
989 std::bitset<16> deassertions = 0;
990
James Feist392786a2019-03-19 13:36:10 -0700991 if (criticalDeassertHigh && !*criticalDeassertHigh)
992 {
jayaprakash Mutyalaccf88f62019-05-13 16:57:16 +0000993 deassertions.set(static_cast<size_t>(
994 IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
James Feist392786a2019-03-19 13:36:10 -0700995 }
996 if (criticalDeassertLow && !*criticalDeassertLow)
997 {
jayaprakash Mutyalaccf88f62019-05-13 16:57:16 +0000998 deassertions.set(static_cast<size_t>(
999 IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
James Feist392786a2019-03-19 13:36:10 -07001000 }
1001 if (warningDeassertHigh && !*warningDeassertHigh)
1002 {
jayaprakash Mutyalaccf88f62019-05-13 16:57:16 +00001003 deassertions.set(static_cast<size_t>(
1004 IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
James Feist392786a2019-03-19 13:36:10 -07001005 }
1006 if (warningDeassertLow && !*warningDeassertLow)
1007 {
jayaprakash Mutyalaccf88f62019-05-13 16:57:16 +00001008 deassertions.set(static_cast<size_t>(
1009 IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
James Feist392786a2019-03-19 13:36:10 -07001010 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001011 if ((warningInterface != sensorMap.end()) ||
1012 (criticalInterface != sensorMap.end()))
1013 {
jayaprakash Mutyalaccf88f62019-05-13 16:57:16 +00001014 sensorEventStatus = static_cast<size_t>(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001015 IPMISensorEventEnableByte2::eventMessagesEnable);
1016 if (warningInterface != sensorMap.end())
1017 {
James Feistfcd2d3a2020-05-28 10:38:15 -07001018 auto& warningMap = warningInterface->second;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001019
1020 auto warningHigh = warningMap.find("WarningAlarmHigh");
1021 auto warningLow = warningMap.find("WarningAlarmLow");
1022 auto warningHighAlarm = false;
1023 auto warningLowAlarm = false;
1024
1025 if (warningHigh != warningMap.end())
1026 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001027 warningHighAlarm = std::get<bool>(warningHigh->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001028 }
1029 if (warningLow != warningMap.end())
1030 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001031 warningLowAlarm = std::get<bool>(warningLow->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001032 }
1033 if (warningHighAlarm)
1034 {
jayaprakash Mutyalaccf88f62019-05-13 16:57:16 +00001035 assertions.set(
1036 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1037 upperNonCriticalGoingHigh));
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001038 }
1039 if (warningLowAlarm)
1040 {
jayaprakash Mutyalaccf88f62019-05-13 16:57:16 +00001041 assertions.set(
1042 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1043 lowerNonCriticalGoingLow));
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001044 }
1045 }
1046 if (criticalInterface != sensorMap.end())
1047 {
James Feistfcd2d3a2020-05-28 10:38:15 -07001048 auto& criticalMap = criticalInterface->second;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001049
1050 auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
1051 auto criticalLow = criticalMap.find("CriticalAlarmLow");
1052 auto criticalHighAlarm = false;
1053 auto criticalLowAlarm = false;
1054
1055 if (criticalHigh != criticalMap.end())
1056 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001057 criticalHighAlarm = std::get<bool>(criticalHigh->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001058 }
1059 if (criticalLow != criticalMap.end())
1060 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001061 criticalLowAlarm = std::get<bool>(criticalLow->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001062 }
1063 if (criticalHighAlarm)
1064 {
jayaprakash Mutyalaccf88f62019-05-13 16:57:16 +00001065 assertions.set(
1066 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1067 upperCriticalGoingHigh));
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001068 }
1069 if (criticalLowAlarm)
1070 {
jayaprakash Mutyalaccf88f62019-05-13 16:57:16 +00001071 assertions.set(static_cast<size_t>(
1072 IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001073 }
1074 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001075 }
1076
jayaprakash Mutyalaccf88f62019-05-13 16:57:16 +00001077 return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001078}
1079
Johnathan Mantey308c3a82020-07-22 11:50:54 -07001080static int getSensorDataRecords(ipmi::Context::ptr ctx)
1081{
1082
1083 size_t recordID = 0;
1084 size_t fruCount = 0;
1085
1086 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1087 if (ret != ipmi::ccSuccess)
1088 {
1089 return GENERAL_ERROR;
1090 }
1091
1092 size_t lastRecord = sensorTree.size() + fruCount +
1093 ipmi::storage::type12Count +
1094 ipmi::storage::nmDiscoverySDRCount - 1;
1095 if (lastRecord > lastRecordIndex)
1096 {
1097 return GENERAL_ERROR;
1098 }
1099
1100 std::string connection;
1101 std::string path;
1102 for (const auto& sensor : sensorTree)
1103 {
1104
1105 connection = sensor.second.begin()->first;
1106 path = sensor.first;
1107
1108 SensorMap sensorMap;
1109 if (!getSensorMap(ctx->yield, connection, path, sensorMap))
1110 {
1111 return GENERAL_ERROR;
1112 }
1113 uint16_t sensorNum = getSensorNumberFromPath(path);
1114 if (sensorNum == invalidSensorNumber)
1115 {
1116 return GENERAL_ERROR;
1117 }
1118 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1119 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1120
1121 get_sdr::SensorDataFullRecord record = {0};
1122
1123 get_sdr::header::set_record_id(
1124 recordID,
1125 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1126 record.header.sdr_version = ipmiSdrVersion;
1127 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1128 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1129 sizeof(get_sdr::SensorDataRecordHeader);
1130 record.key.owner_id = 0x20;
1131 record.key.owner_lun = lun;
1132 record.key.sensor_number = sensornumber;
1133
1134 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
1135 record.body.sensor_type = getSensorTypeFromPath(path);
1136 std::string type = getSensorTypeStringFromPath(path);
1137 auto typeCstr = type.c_str();
1138 auto findUnits = sensorUnits.find(typeCstr);
1139 if (findUnits != sensorUnits.end())
1140 {
1141 record.body.sensor_units_2_base =
1142 static_cast<uint8_t>(findUnits->second);
1143 } // else default 0x0 unspecified
1144
1145 record.body.event_reading_type = getSensorEventTypeFromPath(path);
1146
1147 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
1148 if (sensorObject == sensorMap.end())
1149 {
1150 return GENERAL_ERROR;
1151 }
1152
1153 uint8_t entityId = 0;
1154 uint8_t entityInstance = 0x01;
1155
1156 // follow the association chain to get the parent board's entityid and
1157 // entityInstance
1158 updateIpmiFromAssociation(path, sensorMap, entityId, entityInstance);
1159
1160 record.body.entity_id = entityId;
1161 record.body.entity_instance = entityInstance;
1162
1163 auto maxObject = sensorObject->second.find("MaxValue");
1164 auto minObject = sensorObject->second.find("MinValue");
1165
1166 // If min and/or max are left unpopulated,
1167 // then default to what a signed byte would be, namely (-128,127) range.
1168 auto max = static_cast<double>(std::numeric_limits<int8_t>::max());
1169 auto min = static_cast<double>(std::numeric_limits<int8_t>::lowest());
1170 if (maxObject != sensorObject->second.end())
1171 {
1172 max = std::visit(VariantToDoubleVisitor(), maxObject->second);
1173 }
1174
1175 if (minObject != sensorObject->second.end())
1176 {
1177 min = std::visit(VariantToDoubleVisitor(), minObject->second);
1178 }
1179
1180 int16_t mValue = 0;
1181 int8_t rExp = 0;
1182 int16_t bValue = 0;
1183 int8_t bExp = 0;
1184 bool bSigned = false;
1185
1186 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1187 {
1188 return GENERAL_ERROR;
1189 }
1190
1191 // The record.body is a struct SensorDataFullRecordBody
1192 // from sensorhandler.hpp in phosphor-ipmi-host.
1193 // The meaning of these bits appears to come from
1194 // table 43.1 of the IPMI spec.
1195 // The above 5 sensor attributes are stuffed in as follows:
1196 // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1197 // Byte 22-24 are for other purposes
1198 // Byte 25 = MMMMMMMM = LSB of M
1199 // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1200 // Byte 27 = BBBBBBBB = LSB of B
1201 // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1202 // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1203 // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1204
1205 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1206 record.body.m_lsb = mValue & 0xFF;
1207
1208 uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1209 uint8_t mBitNine = (mValue & 0x0100) >> 8;
1210
1211 // move the smallest bit of the MSB into place (bit 9)
1212 // the MSbs are bits 7:8 in m_msb_and_tolerance
1213 record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6);
1214
1215 record.body.b_lsb = bValue & 0xFF;
1216
1217 uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1218 uint8_t bBitNine = (bValue & 0x0100) >> 8;
1219
1220 // move the smallest bit of the MSB into place (bit 9)
1221 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1222 record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6);
1223
1224 uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1225 uint8_t rExpBits = rExp & 0x07;
1226
1227 uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1228 uint8_t bExpBits = bExp & 0x07;
1229
1230 // move rExp and bExp into place
1231 record.body.r_b_exponents =
1232 (rExpSign << 7) | (rExpBits << 4) | (bExpSign << 3) | bExpBits;
1233
1234 // Set the analog reading byte interpretation accordingly
1235 record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7;
1236
1237 // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1238 // These seem redundant, but derivable from the above 5 attributes
1239 // Original comment said "todo fill out rest of units"
1240
1241 // populate sensor name from path
1242 std::string name;
1243 size_t nameStart = path.rfind("/");
1244 if (nameStart != std::string::npos)
1245 {
1246 name = path.substr(nameStart + 1, std::string::npos - nameStart);
1247 }
1248
1249 std::replace(name.begin(), name.end(), '_', ' ');
1250 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
1251 {
1252 // try to not truncate by replacing common words
1253 constexpr std::array<std::pair<const char*, const char*>, 2>
1254 replaceWords = {std::make_pair("Output", "Out"),
1255 std::make_pair("Input", "In")};
1256 for (const auto& [find, replace] : replaceWords)
1257 {
1258 boost::replace_all(name, find, replace);
1259 }
1260
1261 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
1262 }
1263 record.body.id_string_info = name.size();
1264 std::strncpy(record.body.id_string, name.c_str(),
1265 sizeof(record.body.id_string));
1266
1267 IPMIThresholds thresholdData;
1268 try
1269 {
1270 thresholdData = getIPMIThresholds(sensorMap);
1271 }
1272 catch (std::exception&)
1273 {
1274 return GENERAL_ERROR;
1275 }
1276
1277 if (thresholdData.criticalHigh)
1278 {
1279 record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1280 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1281 IPMISensorEventEnableThresholds::criticalThreshold);
1282 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1283 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1284 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1285 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1286 record.body.discrete_reading_setting_mask[0] |=
1287 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1288 }
1289 if (thresholdData.warningHigh)
1290 {
1291 record.body.upper_noncritical_threshold =
1292 *thresholdData.warningHigh;
1293 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1294 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1295 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1296 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1297 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1298 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1299 record.body.discrete_reading_setting_mask[0] |=
1300 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1301 }
1302 if (thresholdData.criticalLow)
1303 {
1304 record.body.lower_critical_threshold = *thresholdData.criticalLow;
1305 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1306 IPMISensorEventEnableThresholds::criticalThreshold);
1307 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1308 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1309 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1310 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1311 record.body.discrete_reading_setting_mask[0] |=
1312 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1313 }
1314 if (thresholdData.warningLow)
1315 {
1316 record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1317 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1318 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1319 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1320 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1321 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1322 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1323 record.body.discrete_reading_setting_mask[0] |=
1324 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1325 }
1326
1327 // everything that is readable is setable
1328 record.body.discrete_reading_setting_mask[1] =
1329 record.body.discrete_reading_setting_mask[0];
1330
1331 // insert the record into the map
1332 std::vector<uint8_t> sdr;
1333 sdr.insert(sdr.end(), (uint8_t*)&record,
1334 ((uint8_t*)&record) + sizeof(record));
1335 sensorDataRecords.insert_or_assign(recordID, sdr);
1336 recordID++;
1337 }
1338
1339 size_t nonSensorRecCount = fruCount + ipmi::storage::type12Count +
1340 ipmi::storage::nmDiscoverySDRCount;
1341 size_t type12End = fruCount + ipmi::storage::type12Count;
1342 do
1343 {
1344 size_t fruIndex = recordID - sensorTree.size();
1345
1346 if (fruIndex >= type12End)
1347 {
1348 // NM discovery SDR
1349 size_t nmDiscoveryIndex = fruIndex - type12End;
1350 if (nmDiscoveryIndex >= ipmi::storage::nmDiscoverySDRCount)
1351 {
1352 return GENERAL_ERROR;
1353 }
1354
1355 std::vector<uint8_t> record =
1356 ipmi::storage::getNMDiscoverySDR(nmDiscoveryIndex, recordID);
1357 sensorDataRecords.insert_or_assign(recordID, record);
1358 }
1359 else if (fruIndex >= fruCount)
1360 {
1361 // handle type 12 hardcoded records
1362 size_t type12Index = fruIndex - fruCount;
1363 if (type12Index >= ipmi::storage::type12Count)
1364 {
1365 return GENERAL_ERROR;
1366 }
1367 std::vector<uint8_t> record =
1368 ipmi::storage::getType12SDRs(type12Index, recordID);
1369 sensorDataRecords.insert_or_assign(recordID, record);
1370 }
1371 else
1372 {
1373 // handle fru records
1374 get_sdr::SensorDataFruRecord data;
1375 ret = ipmi::storage::getFruSdrs(ctx, fruIndex, data);
1376 if (ret != IPMI_CC_OK)
1377 {
1378 return GENERAL_ERROR;
1379 }
1380 get_sdr::header::set_record_id(
1381 recordID,
1382 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&data));
1383
1384 std::vector<uint8_t> record;
1385 record.insert(record.end(), (uint8_t*)&data,
1386 ((uint8_t*)&data) + sizeof(data));
1387 sensorDataRecords.insert_or_assign(recordID, record);
1388 }
1389 recordID++;
1390 } while (--nonSensorRecCount);
1391 return 0;
1392}
1393
1394/** @brief implements the get SDR Info command
1395 * @param count - Operation
1396 *
1397 * @returns IPMI completion code plus response data
1398 * - sdrCount - sensor/SDR count
1399 * - lunsAndDynamicPopulation - static/Dynamic sensor population flag
1400 */
1401static ipmi::RspType<uint8_t, // respcount
1402 uint8_t, // dynamic population flags
1403 uint32_t // last time a sensor was added
1404 >
1405 ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
1406 std::optional<uint8_t> count)
1407{
1408 uint8_t sdrCount = 0;
1409 // Sensors are dynamically allocated, and there is at least one LUN
1410 uint8_t lunsAndDynamicPopulation = 0x80;
1411 constexpr uint8_t getSdrCount = 0x01;
1412 constexpr uint8_t getSensorCount = 0x00;
1413
1414 if (!getSensorSubtree(sensorTree) || sensorTree.empty())
1415 {
1416 return ipmi::responseResponseError();
1417 }
1418
1419 if (sensorDataRecords.empty() && getSensorDataRecords(ctx))
1420 {
1421 return ipmi::responseResponseError();
1422 }
1423
1424 uint16_t numSensors = sensorTree.size();
1425 if (count.value_or(0) == getSdrCount)
1426 {
1427 // Count the number of Type 1 SDR entries assigned to the LUN
1428 for (auto sdr : sensorDataRecords)
1429 {
1430 get_sdr::SensorDataRecordHeader* hdr =
1431 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(
1432 sdr.second.data());
1433 if (hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD)
1434 {
1435 get_sdr::SensorDataFullRecord* record =
1436 reinterpret_cast<get_sdr::SensorDataFullRecord*>(
1437 sdr.second.data());
1438 if (ctx->lun == record->key.owner_lun)
1439 {
1440 sdrCount++;
1441 }
1442 }
1443 }
1444 }
1445 else if (count.value_or(0) == getSensorCount)
1446 {
1447 // Return the number of sensors attached to the LUN
1448 if ((ctx->lun == 0) && (numSensors > 0))
1449 {
1450 sdrCount =
1451 (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN : numSensors;
1452 }
1453 else if ((ctx->lun == 1) && (numSensors > maxSensorsPerLUN))
1454 {
1455 sdrCount = (numSensors > (2 * maxSensorsPerLUN))
1456 ? maxSensorsPerLUN
1457 : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
1458 }
1459 else if (ctx->lun == 3)
1460 {
1461 if (numSensors <= maxIPMISensors)
1462 {
1463 sdrCount =
1464 (numSensors - (2 * maxSensorsPerLUN)) & maxSensorsPerLUN;
1465 }
1466 else
1467 {
1468 // error
1469 throw std::out_of_range(
1470 "Maximum number of IPMI sensors exceeded.");
1471 }
1472 }
1473 }
1474 else
1475 {
1476 return ipmi::responseInvalidFieldRequest();
1477 }
1478
1479 // Get Sensor count. This returns the number of sensors
1480 if (numSensors > 0)
1481 {
1482 lunsAndDynamicPopulation |= 1;
1483 }
1484 if (numSensors > maxSensorsPerLUN)
1485 {
1486 lunsAndDynamicPopulation |= 2;
1487 }
1488 if (numSensors >= (maxSensorsPerLUN * 2))
1489 {
1490 lunsAndDynamicPopulation |= 8;
1491 }
1492 if (numSensors > maxIPMISensors)
1493 {
1494 // error
1495 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
1496 }
1497
1498 return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
1499 sdrLastAdd);
1500}
1501
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001502/* end sensor commands */
1503
1504/* storage commands */
1505
James Feist74c50c62019-08-14 14:18:41 -07001506ipmi::RspType<uint8_t, // sdr version
1507 uint16_t, // record count
1508 uint16_t, // free space
1509 uint32_t, // most recent addition
1510 uint32_t, // most recent erase
1511 uint8_t // operationSupport
1512 >
James Feist25690252019-12-23 12:25:49 -08001513 ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001514{
James Feist74c50c62019-08-14 14:18:41 -07001515 constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001516 if (sensorTree.empty() && !getSensorSubtree(sensorTree))
1517 {
James Feist74c50c62019-08-14 14:18:41 -07001518 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001519 }
1520
James Feist74c50c62019-08-14 14:18:41 -07001521 size_t fruCount = 0;
James Feist25690252019-12-23 12:25:49 -08001522 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
James Feist74c50c62019-08-14 14:18:41 -07001523 if (ret != ipmi::ccSuccess)
1524 {
1525 return ipmi::response(ret);
1526 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001527
James Feist74c50c62019-08-14 14:18:41 -07001528 uint16_t recordCount =
1529 sensorTree.size() + fruCount + ipmi::storage::type12Count;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001530
James Feist74c50c62019-08-14 14:18:41 -07001531 uint8_t operationSupport = static_cast<uint8_t>(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001532 SdrRepositoryInfoOps::overflow); // write not supported
James Feist74c50c62019-08-14 14:18:41 -07001533
1534 operationSupport |=
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001535 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
James Feist74c50c62019-08-14 14:18:41 -07001536 operationSupport |= static_cast<uint8_t>(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001537 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
James Feist74c50c62019-08-14 14:18:41 -07001538 return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
1539 unspecifiedFreeSpace, sdrLastAdd,
1540 sdrLastRemove, operationSupport);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001541}
1542
jayaprakash Mutyala6ae08182019-05-13 18:56:11 +00001543/** @brief implements the get SDR allocation info command
1544 *
1545 * @returns IPMI completion code plus response data
1546 * - allocUnits - Number of possible allocation units
1547 * - allocUnitSize - Allocation unit size in bytes.
1548 * - allocUnitFree - Number of free allocation units
1549 * - allocUnitLargestFree - Largest free block in allocation units
1550 * - maxRecordSize - Maximum record size in allocation units.
1551 */
1552ipmi::RspType<uint16_t, // allocUnits
1553 uint16_t, // allocUnitSize
1554 uint16_t, // allocUnitFree
1555 uint16_t, // allocUnitLargestFree
1556 uint8_t // maxRecordSize
1557 >
1558 ipmiStorageGetSDRAllocationInfo()
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001559{
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001560 // 0000h unspecified number of alloc units
jayaprakash Mutyala6ae08182019-05-13 18:56:11 +00001561 constexpr uint16_t allocUnits = 0;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001562
jayaprakash Mutyala6ae08182019-05-13 18:56:11 +00001563 constexpr uint16_t allocUnitFree = 0;
1564 constexpr uint16_t allocUnitLargestFree = 0;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001565 // only allow one block at a time
jayaprakash Mutyala6ae08182019-05-13 18:56:11 +00001566 constexpr uint8_t maxRecordSize = 1;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001567
jayaprakash Mutyala6ae08182019-05-13 18:56:11 +00001568 return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
1569 allocUnitLargestFree, maxRecordSize);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001570}
1571
jayaprakash Mutyala6ae08182019-05-13 18:56:11 +00001572/** @brief implements the reserve SDR command
1573 * @returns IPMI completion code plus response data
1574 * - sdrReservationID
1575 */
1576ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001577{
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001578 sdrReservationID++;
James Feista80cb902019-02-14 13:05:25 -08001579 if (sdrReservationID == 0)
1580 {
1581 sdrReservationID++;
1582 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001583
jayaprakash Mutyala6ae08182019-05-13 18:56:11 +00001584 return ipmi::responseSuccess(sdrReservationID);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001585}
1586
James Feistb49a98a2019-04-16 13:48:09 -07001587ipmi::RspType<uint16_t, // next record ID
1588 std::vector<uint8_t> // payload
1589 >
James Feist25690252019-12-23 12:25:49 -08001590 ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
1591 uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001592{
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001593 // reservation required for partial reads with non zero offset into
1594 // record
James Feistb49a98a2019-04-16 13:48:09 -07001595 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001596 {
James Feistb49a98a2019-04-16 13:48:09 -07001597 return ipmi::responseInvalidReservationId();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001598 }
1599
Johnathan Mantey308c3a82020-07-22 11:50:54 -07001600 if (sensorDataRecords.empty() && getSensorDataRecords(ctx))
1601 {
1602 return ipmi::responseResponseError();
1603 }
1604
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001605 if (sensorTree.empty() && !getSensorSubtree(sensorTree))
1606 {
James Feistb49a98a2019-04-16 13:48:09 -07001607 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001608 }
1609
1610 size_t fruCount = 0;
James Feist25690252019-12-23 12:25:49 -08001611 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
James Feist74c50c62019-08-14 14:18:41 -07001612 if (ret != ipmi::ccSuccess)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001613 {
James Feistb49a98a2019-04-16 13:48:09 -07001614 return ipmi::response(ret);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001615 }
1616
Yong Lifee5e4c2020-01-17 19:36:29 +08001617 size_t lastRecord = sensorTree.size() + fruCount +
1618 ipmi::storage::type12Count +
1619 ipmi::storage::nmDiscoverySDRCount - 1;
James Feistb49a98a2019-04-16 13:48:09 -07001620 if (recordID == lastRecordIndex)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001621 {
James Feistb49a98a2019-04-16 13:48:09 -07001622 recordID = lastRecord;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001623 }
James Feistb49a98a2019-04-16 13:48:09 -07001624 if (recordID > lastRecord)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001625 {
James Feistb49a98a2019-04-16 13:48:09 -07001626 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001627 }
1628
Johnathan Mantey308c3a82020-07-22 11:50:54 -07001629 get_sdr::SensorDataRecordHeader* hdr =
1630 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(
1631 sensorDataRecords[recordID].data());
1632 size_t sdrLength =
1633 sizeof(get_sdr::SensorDataRecordHeader) + hdr->record_length;
1634 if (sdrLength < (offset + bytesToRead))
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001635 {
Johnathan Mantey308c3a82020-07-22 11:50:54 -07001636 bytesToRead = sdrLength - offset;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001637 }
1638
Johnathan Mantey308c3a82020-07-22 11:50:54 -07001639 uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
James Feistb49a98a2019-04-16 13:48:09 -07001640 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
Johnathan Mantey308c3a82020-07-22 11:50:54 -07001641 uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
James Feistb49a98a2019-04-16 13:48:09 -07001642 return ipmi::responseSuccess(nextRecordId, recordData);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001643}
1644/* end storage commands */
1645
1646void registerSensorFunctions()
1647{
Jason M. Billsae6bdb12019-04-02 12:00:04 -07001648 // <Platform Event>
Vernon Mauery98bbf692019-09-16 11:14:59 -07001649 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnSensor,
1650 ipmi::sensor_event::cmdPlatformEvent,
1651 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
Jason M. Billsae6bdb12019-04-02 12:00:04 -07001652
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001653 // <Get Sensor Reading>
Vernon Mauery98bbf692019-09-16 11:14:59 -07001654 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnSensor,
1655 ipmi::sensor_event::cmdGetSensorReading,
1656 ipmi::Privilege::User, ipmiSenGetSensorReading);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001657
1658 // <Get Sensor Threshold>
Vernon Mauery98bbf692019-09-16 11:14:59 -07001659 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnSensor,
1660 ipmi::sensor_event::cmdGetSensorThreshold,
1661 ipmi::Privilege::User, ipmiSenGetSensorThresholds);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001662
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +00001663 // <Set Sensor Threshold>
Vernon Mauery98bbf692019-09-16 11:14:59 -07001664 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnSensor,
1665 ipmi::sensor_event::cmdSetSensorThreshold,
1666 ipmi::Privilege::Operator,
1667 ipmiSenSetSensorThresholds);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001668
1669 // <Get Sensor Event Enable>
Vernon Mauery98bbf692019-09-16 11:14:59 -07001670 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnSensor,
1671 ipmi::sensor_event::cmdGetSensorEventEnable,
jayaprakash Mutyala39fa93f2019-05-13 12:16:30 +00001672 ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001673
1674 // <Get Sensor Event Status>
jayaprakash Mutyalaccf88f62019-05-13 16:57:16 +00001675 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnSensor,
1676 ipmi::sensor_event::cmdGetSensorEventStatus,
1677 ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001678
1679 // register all storage commands for both Sensor and Storage command
1680 // versions
1681
1682 // <Get SDR Repository Info>
Vernon Mauery98bbf692019-09-16 11:14:59 -07001683 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnStorage,
1684 ipmi::storage::cmdGetSdrRepositoryInfo,
1685 ipmi::Privilege::User,
1686 ipmiStorageGetSDRRepositoryInfo);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001687
Johnathan Mantey308c3a82020-07-22 11:50:54 -07001688 // <Get Device SDR Info>
1689 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1690 ipmi::sensor_event::cmdGetDeviceSdrInfo,
1691 ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
1692
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001693 // <Get SDR Allocation Info>
Vernon Mauery98bbf692019-09-16 11:14:59 -07001694 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnStorage,
1695 ipmi::storage::cmdGetSdrRepositoryAllocInfo,
1696 ipmi::Privilege::User,
1697 ipmiStorageGetSDRAllocationInfo);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001698
1699 // <Reserve SDR Repo>
Vernon Mauery98bbf692019-09-16 11:14:59 -07001700 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnSensor,
1701 ipmi::sensor_event::cmdReserveDeviceSdrRepository,
jayaprakash Mutyala6ae08182019-05-13 18:56:11 +00001702 ipmi::Privilege::User, ipmiStorageReserveSDR);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001703
Vernon Mauery98bbf692019-09-16 11:14:59 -07001704 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnStorage,
1705 ipmi::storage::cmdReserveSdrRepository,
1706 ipmi::Privilege::User, ipmiStorageReserveSDR);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001707
1708 // <Get Sdr>
Vernon Mauery98bbf692019-09-16 11:14:59 -07001709 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnSensor,
1710 ipmi::sensor_event::cmdGetDeviceSdr,
1711 ipmi::Privilege::User, ipmiStorageGetSDR);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001712
Vernon Mauery98bbf692019-09-16 11:14:59 -07001713 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnStorage,
1714 ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
1715 ipmiStorageGetSDR);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001716}
1717} // namespace ipmi