blob: 830e8a512e3f4cb1da25be32c04dc0164ee31011 [file] [log] [blame]
Willy Tude54f482021-01-26 15:59:09 -08001/*
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
17#include "dbus-sdr/sensorcommands.hpp"
18
19#include "dbus-sdr/sdrutils.hpp"
20#include "dbus-sdr/sensorutils.hpp"
21#include "dbus-sdr/storagecommands.hpp"
22
23#include <algorithm>
24#include <array>
25#include <boost/algorithm/string.hpp>
26#include <boost/container/flat_map.hpp>
27#include <chrono>
28#include <cmath>
29#include <cstring>
30#include <iostream>
31#include <ipmid/api.hpp>
32#include <ipmid/types.hpp>
33#include <ipmid/utils.hpp>
34#include <map>
35#include <memory>
36#include <optional>
37#include <phosphor-logging/log.hpp>
38#include <sdbusplus/bus.hpp>
39#include <stdexcept>
40#include <string>
41#include <utility>
42#include <variant>
43
44namespace ipmi
45{
Willy Tude54f482021-01-26 15:59:09 -080046static constexpr int sensorMapUpdatePeriod = 10;
Alex Qiu9ab2f942020-07-15 17:56:21 -070047static constexpr int sensorMapSdrUpdatePeriod = 60;
Willy Tude54f482021-01-26 15:59:09 -080048
49constexpr size_t maxSDRTotalSize =
50 76; // Largest SDR Record Size (type 01) + SDR Overheader Size
51constexpr static const uint32_t noTimestamp = 0xFFFFFFFF;
52
53static uint16_t sdrReservationID;
54static uint32_t sdrLastAdd = noTimestamp;
55static uint32_t sdrLastRemove = noTimestamp;
56static constexpr size_t lastRecordIndex = 0xFFFF;
57static constexpr int GENERAL_ERROR = -1;
58
Willy Tude54f482021-01-26 15:59:09 -080059static boost::container::flat_map<std::string, ObjectValueTree> SensorCache;
60
61// Specify the comparison required to sort and find char* map objects
62struct CmpStr
63{
64 bool operator()(const char* a, const char* b) const
65 {
66 return std::strcmp(a, b) < 0;
67 }
68};
69const static boost::container::flat_map<const char*, SensorUnits, CmpStr>
70 sensorUnits{{{"temperature", SensorUnits::degreesC},
71 {"voltage", SensorUnits::volts},
72 {"current", SensorUnits::amps},
73 {"fan_tach", SensorUnits::rpm},
74 {"power", SensorUnits::watts}}};
75
76void registerSensorFunctions() __attribute__((constructor));
77
78static sdbusplus::bus::match::match sensorAdded(
79 *getSdBus(),
80 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
81 "sensors/'",
82 [](sdbusplus::message::message& m) {
83 getSensorTree().clear();
84 sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>(
85 std::chrono::system_clock::now().time_since_epoch())
86 .count();
87 });
88
89static sdbusplus::bus::match::match sensorRemoved(
90 *getSdBus(),
91 "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/"
92 "sensors/'",
93 [](sdbusplus::message::message& m) {
94 getSensorTree().clear();
95 sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>(
96 std::chrono::system_clock::now().time_since_epoch())
97 .count();
98 });
99
100// this keeps track of deassertions for sensor event status command. A
101// deasertion can only happen if an assertion was seen first.
102static boost::container::flat_map<
103 std::string, boost::container::flat_map<std::string, std::optional<bool>>>
104 thresholdDeassertMap;
105
106static sdbusplus::bus::match::match thresholdChanged(
107 *getSdBus(),
108 "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus."
109 "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
110 [](sdbusplus::message::message& m) {
111 boost::container::flat_map<std::string, std::variant<bool, double>>
112 values;
113 m.read(std::string(), values);
114
115 auto findAssert =
116 std::find_if(values.begin(), values.end(), [](const auto& pair) {
117 return pair.first.find("Alarm") != std::string::npos;
118 });
119 if (findAssert != values.end())
120 {
121 auto ptr = std::get_if<bool>(&(findAssert->second));
122 if (ptr == nullptr)
123 {
124 phosphor::logging::log<phosphor::logging::level::ERR>(
125 "thresholdChanged: Assert non bool");
126 return;
127 }
128 if (*ptr)
129 {
130 phosphor::logging::log<phosphor::logging::level::INFO>(
131 "thresholdChanged: Assert",
132 phosphor::logging::entry("SENSOR=%s", m.get_path()));
133 thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr;
134 }
135 else
136 {
137 auto& value =
138 thresholdDeassertMap[m.get_path()][findAssert->first];
139 if (value)
140 {
141 phosphor::logging::log<phosphor::logging::level::INFO>(
142 "thresholdChanged: deassert",
143 phosphor::logging::entry("SENSOR=%s", m.get_path()));
144 value = *ptr;
145 }
146 }
147 }
148 });
149
150static void getSensorMaxMin(const DbusInterfaceMap& sensorMap, double& max,
151 double& min)
152{
153 max = 127;
154 min = -128;
155
156 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
157 auto critical =
158 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
159 auto warning =
160 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
161
162 if (sensorObject != sensorMap.end())
163 {
164 auto maxMap = sensorObject->second.find("MaxValue");
165 auto minMap = sensorObject->second.find("MinValue");
166
167 if (maxMap != sensorObject->second.end())
168 {
169 max = std::visit(VariantToDoubleVisitor(), maxMap->second);
170 }
171 if (minMap != sensorObject->second.end())
172 {
173 min = std::visit(VariantToDoubleVisitor(), minMap->second);
174 }
175 }
176 if (critical != sensorMap.end())
177 {
178 auto lower = critical->second.find("CriticalLow");
179 auto upper = critical->second.find("CriticalHigh");
180 if (lower != critical->second.end())
181 {
182 double value = std::visit(VariantToDoubleVisitor(), lower->second);
183 min = std::min(value, min);
184 }
185 if (upper != critical->second.end())
186 {
187 double value = std::visit(VariantToDoubleVisitor(), upper->second);
188 max = std::max(value, max);
189 }
190 }
191 if (warning != sensorMap.end())
192 {
193
194 auto lower = warning->second.find("WarningLow");
195 auto upper = warning->second.find("WarningHigh");
196 if (lower != warning->second.end())
197 {
198 double value = std::visit(VariantToDoubleVisitor(), lower->second);
199 min = std::min(value, min);
200 }
201 if (upper != warning->second.end())
202 {
203 double value = std::visit(VariantToDoubleVisitor(), upper->second);
204 max = std::max(value, max);
205 }
206 }
207}
208
209static bool getSensorMap(ipmi::Context::ptr ctx, std::string sensorConnection,
Alex Qiu9ab2f942020-07-15 17:56:21 -0700210 std::string sensorPath, DbusInterfaceMap& sensorMap,
211 int updatePeriod = sensorMapUpdatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800212{
213 static boost::container::flat_map<
214 std::string, std::chrono::time_point<std::chrono::steady_clock>>
215 updateTimeMap;
216
217 auto updateFind = updateTimeMap.find(sensorConnection);
218 auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
219 if (updateFind != updateTimeMap.end())
220 {
221 lastUpdate = updateFind->second;
222 }
223
224 auto now = std::chrono::steady_clock::now();
225
226 if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
Alex Qiu9ab2f942020-07-15 17:56:21 -0700227 .count() > updatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800228 {
Willy Tude54f482021-01-26 15:59:09 -0800229 ObjectValueTree managedObjects;
230 boost::system::error_code ec = getManagedObjects(
231 ctx, sensorConnection.c_str(), "/", managedObjects);
232 if (ec)
233 {
234 phosphor::logging::log<phosphor::logging::level::ERR>(
235 "GetMangagedObjects for getSensorMap failed",
236 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
237
238 return false;
239 }
240
241 SensorCache[sensorConnection] = managedObjects;
Alex Qiu9ab2f942020-07-15 17:56:21 -0700242 // Update time after finish building the map which allow the
243 // data to be cached for updatePeriod plus the build time.
244 updateTimeMap[sensorConnection] = std::chrono::steady_clock::now();
Willy Tude54f482021-01-26 15:59:09 -0800245 }
246 auto connection = SensorCache.find(sensorConnection);
247 if (connection == SensorCache.end())
248 {
249 return false;
250 }
251 auto path = connection->second.find(sensorPath);
252 if (path == connection->second.end())
253 {
254 return false;
255 }
256 sensorMap = path->second;
257
258 return true;
259}
260
261ipmi::RspType<> ipmiSenPlatformEvent(uint8_t generatorID, uint8_t evmRev,
262 uint8_t sensorType, uint8_t sensorNum,
263 uint8_t eventType, uint8_t eventData1,
264 std::optional<uint8_t> eventData2,
265 std::optional<uint8_t> eventData3)
266{
267 return ipmi::responseSuccess();
268}
269
Willy Tudbafbce2021-03-29 00:37:05 -0700270ipmi::RspType<> ipmiSetSensorReading(ipmi::Context::ptr ctx,
271 uint8_t sensorNumber, uint8_t operation,
272 uint8_t reading, uint15_t assertOffset,
273 bool resvd1, uint15_t deassertOffset,
274 bool resvd2, uint8_t eventData1,
275 uint8_t eventData2, uint8_t eventData3)
276{
277 std::string connection;
278 std::string path;
279 ipmi::Cc status = getSensorConnection(ctx, sensorNumber, connection, path);
280 if (status)
281 {
282 return ipmi::response(status);
283 }
284
285 DbusInterfaceMap sensorMap;
286 if (!getSensorMap(ctx, connection, path, sensorMap))
287 {
288 return ipmi::responseResponseError();
289 }
290 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
291
292 if (sensorObject == sensorMap.end() ||
293 sensorObject->second.find("Value") == sensorObject->second.end())
294 {
295 return ipmi::responseResponseError();
296 }
297
298 double max = 0;
299 double min = 0;
300 getSensorMaxMin(sensorMap, max, min);
301
302 int16_t mValue = 0;
303 int16_t bValue = 0;
304 int8_t rExp = 0;
305 int8_t bExp = 0;
306 bool bSigned = false;
307
308 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
309 {
310 return ipmi::responseResponseError();
311 }
312
313 double value = bSigned ? ((int8_t)reading) : reading;
314
315 value *= ((double)mValue);
316 value += ((double)bValue) * std::pow(10.0, bExp);
317 value *= std::pow(10.0, rExp);
318
319 if constexpr (debug)
320 {
321 phosphor::logging::log<phosphor::logging::level::INFO>(
322 "IPMI SET_SENSOR",
323 phosphor::logging::entry("SENSOR_NUM=%d", sensorNumber),
324 phosphor::logging::entry("BYTE=%u", (unsigned int)reading),
325 phosphor::logging::entry("VALUE=%f", value));
326 }
327
328 try
329 {
330 setDbusProperty(ctx, connection, path,
331 "xyz.openbmc_project.Sensor.Value", "Value",
332 ipmi::Value(value));
333 }
334 // setDbusProperty intended to resolve dbus exception/rc within the
335 // function but failed to achieve that. Catch SdBusError in the ipmi
336 // callback functions for now (e.g. ipmiSetSensorReading).
337 catch (const sdbusplus::exception::SdBusError& e)
338 {
339 using namespace phosphor::logging;
340 log<level::ERR>(
341 "Failed to set property", entry("PROPERTY=%s", "Value"),
342 entry("PATH=%s", path.c_str()),
343 entry("INTERFACE=%s", "xyz.openbmc_project.Sensor.Value"),
344 entry("WHAT=%s", e.what()));
345 return ipmi::responseResponseError();
346 }
347 return ipmi::responseSuccess();
348}
349
Willy Tude54f482021-01-26 15:59:09 -0800350ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
351 ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum)
352{
353 std::string connection;
354 std::string path;
355
356 auto status = getSensorConnection(ctx, sensnum, connection, path);
357 if (status)
358 {
359 return ipmi::response(status);
360 }
361
362 DbusInterfaceMap sensorMap;
363 if (!getSensorMap(ctx, connection, path, sensorMap))
364 {
365 return ipmi::responseResponseError();
366 }
367 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
368
369 if (sensorObject == sensorMap.end() ||
370 sensorObject->second.find("Value") == sensorObject->second.end())
371 {
372 return ipmi::responseResponseError();
373 }
374 auto& valueVariant = sensorObject->second["Value"];
375 double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
376
377 double max = 0;
378 double min = 0;
379 getSensorMaxMin(sensorMap, max, min);
380
381 int16_t mValue = 0;
382 int16_t bValue = 0;
383 int8_t rExp = 0;
384 int8_t bExp = 0;
385 bool bSigned = false;
386
387 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
388 {
389 return ipmi::responseResponseError();
390 }
391
392 uint8_t value =
393 scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
394 uint8_t operation =
395 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
396 operation |=
397 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
398 bool notReading = std::isnan(reading);
399
400 if (!notReading)
401 {
402 auto availableObject =
403 sensorMap.find("xyz.openbmc_project.State.Decorator.Availability");
404 if (availableObject != sensorMap.end())
405 {
406 auto findAvailable = availableObject->second.find("Available");
407 if (findAvailable != availableObject->second.end())
408 {
409 bool* available = std::get_if<bool>(&(findAvailable->second));
410 if (available && !(*available))
411 {
412 notReading = true;
413 }
414 }
415 }
416 }
417
418 if (notReading)
419 {
420 operation |= static_cast<uint8_t>(
421 IPMISensorReadingByte2::readingStateUnavailable);
422 }
423
Josh Lehana55c9532020-10-28 21:59:06 -0700424 if constexpr (details::enableInstrumentation)
425 {
426 int byteValue;
427 if (bSigned)
428 {
429 byteValue = static_cast<int>(static_cast<int8_t>(value));
430 }
431 else
432 {
433 byteValue = static_cast<int>(static_cast<uint8_t>(value));
434 }
435
436 // Keep stats on the reading just obtained, even if it is "NaN"
437 if (details::sdrStatsTable.updateReading(sensnum, reading, byteValue))
438 {
439 // This is the first reading, show the coefficients
440 double step = (max - min) / 255.0;
441 std::cerr << "IPMI sensor "
442 << details::sdrStatsTable.getName(sensnum)
443 << ": Range min=" << min << " max=" << max
444 << ", step=" << step
445 << ", Coefficients mValue=" << static_cast<int>(mValue)
446 << " rExp=" << static_cast<int>(rExp)
447 << " bValue=" << static_cast<int>(bValue)
448 << " bExp=" << static_cast<int>(bExp)
449 << " bSigned=" << static_cast<int>(bSigned) << "\n";
450 }
451 }
452
Willy Tude54f482021-01-26 15:59:09 -0800453 uint8_t thresholds = 0;
454
455 auto warningObject =
456 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
457 if (warningObject != sensorMap.end())
458 {
459 auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
460 auto alarmLow = warningObject->second.find("WarningAlarmLow");
461 if (alarmHigh != warningObject->second.end())
462 {
463 if (std::get<bool>(alarmHigh->second))
464 {
465 thresholds |= static_cast<uint8_t>(
466 IPMISensorReadingByte3::upperNonCritical);
467 }
468 }
469 if (alarmLow != warningObject->second.end())
470 {
471 if (std::get<bool>(alarmLow->second))
472 {
473 thresholds |= static_cast<uint8_t>(
474 IPMISensorReadingByte3::lowerNonCritical);
475 }
476 }
477 }
478
479 auto criticalObject =
480 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
481 if (criticalObject != sensorMap.end())
482 {
483 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
484 auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
485 if (alarmHigh != criticalObject->second.end())
486 {
487 if (std::get<bool>(alarmHigh->second))
488 {
489 thresholds |=
490 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
491 }
492 }
493 if (alarmLow != criticalObject->second.end())
494 {
495 if (std::get<bool>(alarmLow->second))
496 {
497 thresholds |=
498 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
499 }
500 }
501 }
502
503 // no discrete as of today so optional byte is never returned
504 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
505}
506
507/** @brief implements the Set Sensor threshold command
508 * @param sensorNumber - sensor number
509 * @param lowerNonCriticalThreshMask
510 * @param lowerCriticalThreshMask
511 * @param lowerNonRecovThreshMask
512 * @param upperNonCriticalThreshMask
513 * @param upperCriticalThreshMask
514 * @param upperNonRecovThreshMask
515 * @param reserved
516 * @param lowerNonCritical - lower non-critical threshold
517 * @param lowerCritical - Lower critical threshold
518 * @param lowerNonRecoverable - Lower non recovarable threshold
519 * @param upperNonCritical - Upper non-critical threshold
520 * @param upperCritical - Upper critical
521 * @param upperNonRecoverable - Upper Non-recoverable
522 *
523 * @returns IPMI completion code
524 */
525ipmi::RspType<> ipmiSenSetSensorThresholds(
526 ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
527 bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
528 bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
529 bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
530 uint8_t lowerCritical, uint8_t lowerNonRecoverable,
531 uint8_t upperNonCritical, uint8_t upperCritical,
532 uint8_t upperNonRecoverable)
533{
534 if (reserved)
535 {
536 return ipmi::responseInvalidFieldRequest();
537 }
538
539 // lower nc and upper nc not suppported on any sensor
540 if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
541 {
542 return ipmi::responseInvalidFieldRequest();
543 }
544
545 // if none of the threshold mask are set, nothing to do
546 if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
547 lowerNonRecovThreshMask | upperNonCriticalThreshMask |
548 upperCriticalThreshMask | upperNonRecovThreshMask))
549 {
550 return ipmi::responseSuccess();
551 }
552
553 std::string connection;
554 std::string path;
555
556 ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path);
557 if (status)
558 {
559 return ipmi::response(status);
560 }
561 DbusInterfaceMap sensorMap;
562 if (!getSensorMap(ctx, connection, path, sensorMap))
563 {
564 return ipmi::responseResponseError();
565 }
566
567 double max = 0;
568 double min = 0;
569 getSensorMaxMin(sensorMap, max, min);
570
571 int16_t mValue = 0;
572 int16_t bValue = 0;
573 int8_t rExp = 0;
574 int8_t bExp = 0;
575 bool bSigned = false;
576
577 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
578 {
579 return ipmi::responseResponseError();
580 }
581
582 // store a vector of property name, value to set, and interface
583 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
584
585 // define the indexes of the tuple
586 constexpr uint8_t propertyName = 0;
587 constexpr uint8_t thresholdValue = 1;
588 constexpr uint8_t interface = 2;
589 // verifiy all needed fields are present
590 if (lowerCriticalThreshMask || upperCriticalThreshMask)
591 {
592 auto findThreshold =
593 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
594 if (findThreshold == sensorMap.end())
595 {
596 return ipmi::responseInvalidFieldRequest();
597 }
598 if (lowerCriticalThreshMask)
599 {
600 auto findLower = findThreshold->second.find("CriticalLow");
601 if (findLower == findThreshold->second.end())
602 {
603 return ipmi::responseInvalidFieldRequest();
604 }
605 thresholdsToSet.emplace_back("CriticalLow", lowerCritical,
606 findThreshold->first);
607 }
608 if (upperCriticalThreshMask)
609 {
610 auto findUpper = findThreshold->second.find("CriticalHigh");
611 if (findUpper == findThreshold->second.end())
612 {
613 return ipmi::responseInvalidFieldRequest();
614 }
615 thresholdsToSet.emplace_back("CriticalHigh", upperCritical,
616 findThreshold->first);
617 }
618 }
619 if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
620 {
621 auto findThreshold =
622 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
623 if (findThreshold == sensorMap.end())
624 {
625 return ipmi::responseInvalidFieldRequest();
626 }
627 if (lowerNonCriticalThreshMask)
628 {
629 auto findLower = findThreshold->second.find("WarningLow");
630 if (findLower == findThreshold->second.end())
631 {
632 return ipmi::responseInvalidFieldRequest();
633 }
634 thresholdsToSet.emplace_back("WarningLow", lowerNonCritical,
635 findThreshold->first);
636 }
637 if (upperNonCriticalThreshMask)
638 {
639 auto findUpper = findThreshold->second.find("WarningHigh");
640 if (findUpper == findThreshold->second.end())
641 {
642 return ipmi::responseInvalidFieldRequest();
643 }
644 thresholdsToSet.emplace_back("WarningHigh", upperNonCritical,
645 findThreshold->first);
646 }
647 }
648 for (const auto& property : thresholdsToSet)
649 {
650 // from section 36.3 in the IPMI Spec, assume all linear
651 double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
652 (bValue * std::pow(10.0, bExp))) *
653 std::pow(10.0, rExp);
654 setDbusProperty(
655 *getSdBus(), connection, path, std::get<interface>(property),
656 std::get<propertyName>(property), ipmi::Value(valueToSet));
657 }
658 return ipmi::responseSuccess();
659}
660
661IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap)
662{
663 IPMIThresholds resp;
664 auto warningInterface =
665 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
666 auto criticalInterface =
667 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
668
669 if ((warningInterface != sensorMap.end()) ||
670 (criticalInterface != sensorMap.end()))
671 {
672 auto sensorPair = sensorMap.find("xyz.openbmc_project.Sensor.Value");
673
674 if (sensorPair == sensorMap.end())
675 {
676 // should not have been able to find a sensor not implementing
677 // the sensor object
678 throw std::runtime_error("Invalid sensor map");
679 }
680
681 double max = 0;
682 double min = 0;
683 getSensorMaxMin(sensorMap, max, min);
684
685 int16_t mValue = 0;
686 int16_t bValue = 0;
687 int8_t rExp = 0;
688 int8_t bExp = 0;
689 bool bSigned = false;
690
691 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
692 {
693 throw std::runtime_error("Invalid sensor atrributes");
694 }
695 if (warningInterface != sensorMap.end())
696 {
697 auto& warningMap = warningInterface->second;
698
699 auto warningHigh = warningMap.find("WarningHigh");
700 auto warningLow = warningMap.find("WarningLow");
701
702 if (warningHigh != warningMap.end())
703 {
704
705 double value =
706 std::visit(VariantToDoubleVisitor(), warningHigh->second);
707 resp.warningHigh = scaleIPMIValueFromDouble(
708 value, mValue, rExp, bValue, bExp, bSigned);
709 }
710 if (warningLow != warningMap.end())
711 {
712 double value =
713 std::visit(VariantToDoubleVisitor(), warningLow->second);
714 resp.warningLow = scaleIPMIValueFromDouble(
715 value, mValue, rExp, bValue, bExp, bSigned);
716 }
717 }
718 if (criticalInterface != sensorMap.end())
719 {
720 auto& criticalMap = criticalInterface->second;
721
722 auto criticalHigh = criticalMap.find("CriticalHigh");
723 auto criticalLow = criticalMap.find("CriticalLow");
724
725 if (criticalHigh != criticalMap.end())
726 {
727 double value =
728 std::visit(VariantToDoubleVisitor(), criticalHigh->second);
729 resp.criticalHigh = scaleIPMIValueFromDouble(
730 value, mValue, rExp, bValue, bExp, bSigned);
731 }
732 if (criticalLow != criticalMap.end())
733 {
734 double value =
735 std::visit(VariantToDoubleVisitor(), criticalLow->second);
736 resp.criticalLow = scaleIPMIValueFromDouble(
737 value, mValue, rExp, bValue, bExp, bSigned);
738 }
739 }
740 }
741 return resp;
742}
743
744ipmi::RspType<uint8_t, // readable
745 uint8_t, // lowerNCrit
746 uint8_t, // lowerCrit
747 uint8_t, // lowerNrecoverable
748 uint8_t, // upperNC
749 uint8_t, // upperCrit
750 uint8_t> // upperNRecoverable
751 ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber)
752{
753 std::string connection;
754 std::string path;
755
756 auto status = getSensorConnection(ctx, sensorNumber, connection, path);
757 if (status)
758 {
759 return ipmi::response(status);
760 }
761
762 DbusInterfaceMap sensorMap;
763 if (!getSensorMap(ctx, connection, path, sensorMap))
764 {
765 return ipmi::responseResponseError();
766 }
767
768 IPMIThresholds thresholdData;
769 try
770 {
771 thresholdData = getIPMIThresholds(sensorMap);
772 }
773 catch (std::exception&)
774 {
775 return ipmi::responseResponseError();
776 }
777
778 uint8_t readable = 0;
779 uint8_t lowerNC = 0;
780 uint8_t lowerCritical = 0;
781 uint8_t lowerNonRecoverable = 0;
782 uint8_t upperNC = 0;
783 uint8_t upperCritical = 0;
784 uint8_t upperNonRecoverable = 0;
785
786 if (thresholdData.warningHigh)
787 {
788 readable |=
789 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
790 upperNC = *thresholdData.warningHigh;
791 }
792 if (thresholdData.warningLow)
793 {
794 readable |=
795 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
796 lowerNC = *thresholdData.warningLow;
797 }
798
799 if (thresholdData.criticalHigh)
800 {
801 readable |=
802 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
803 upperCritical = *thresholdData.criticalHigh;
804 }
805 if (thresholdData.criticalLow)
806 {
807 readable |=
808 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
809 lowerCritical = *thresholdData.criticalLow;
810 }
811
812 return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
813 lowerNonRecoverable, upperNC, upperCritical,
814 upperNonRecoverable);
815}
816
817/** @brief implements the get Sensor event enable command
818 * @param sensorNumber - sensor number
819 *
820 * @returns IPMI completion code plus response data
821 * - enabled - Sensor Event messages
822 * - assertionEnabledLsb - Assertion event messages
823 * - assertionEnabledMsb - Assertion event messages
824 * - deassertionEnabledLsb - Deassertion event messages
825 * - deassertionEnabledMsb - Deassertion event messages
826 */
827
828ipmi::RspType<uint8_t, // enabled
829 uint8_t, // assertionEnabledLsb
830 uint8_t, // assertionEnabledMsb
831 uint8_t, // deassertionEnabledLsb
832 uint8_t> // deassertionEnabledMsb
833 ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum)
834{
835 std::string connection;
836 std::string path;
837
838 uint8_t enabled = 0;
839 uint8_t assertionEnabledLsb = 0;
840 uint8_t assertionEnabledMsb = 0;
841 uint8_t deassertionEnabledLsb = 0;
842 uint8_t deassertionEnabledMsb = 0;
843
844 auto status = getSensorConnection(ctx, sensorNum, connection, path);
845 if (status)
846 {
847 return ipmi::response(status);
848 }
849
850 DbusInterfaceMap sensorMap;
851 if (!getSensorMap(ctx, connection, path, sensorMap))
852 {
853 return ipmi::responseResponseError();
854 }
855
856 auto warningInterface =
857 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
858 auto criticalInterface =
859 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
860 if ((warningInterface != sensorMap.end()) ||
861 (criticalInterface != sensorMap.end()))
862 {
863 enabled = static_cast<uint8_t>(
864 IPMISensorEventEnableByte2::sensorScanningEnable);
865 if (warningInterface != sensorMap.end())
866 {
867 auto& warningMap = warningInterface->second;
868
869 auto warningHigh = warningMap.find("WarningHigh");
870 auto warningLow = warningMap.find("WarningLow");
871 if (warningHigh != warningMap.end())
872 {
873 assertionEnabledLsb |= static_cast<uint8_t>(
874 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
875 deassertionEnabledLsb |= static_cast<uint8_t>(
876 IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
877 }
878 if (warningLow != warningMap.end())
879 {
880 assertionEnabledLsb |= static_cast<uint8_t>(
881 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
882 deassertionEnabledLsb |= static_cast<uint8_t>(
883 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
884 }
885 }
886 if (criticalInterface != sensorMap.end())
887 {
888 auto& criticalMap = criticalInterface->second;
889
890 auto criticalHigh = criticalMap.find("CriticalHigh");
891 auto criticalLow = criticalMap.find("CriticalLow");
892
893 if (criticalHigh != criticalMap.end())
894 {
895 assertionEnabledMsb |= static_cast<uint8_t>(
896 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
897 deassertionEnabledMsb |= static_cast<uint8_t>(
898 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
899 }
900 if (criticalLow != criticalMap.end())
901 {
902 assertionEnabledLsb |= static_cast<uint8_t>(
903 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
904 deassertionEnabledLsb |= static_cast<uint8_t>(
905 IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
906 }
907 }
908 }
909
910 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
911 assertionEnabledMsb, deassertionEnabledLsb,
912 deassertionEnabledMsb);
913}
914
915/** @brief implements the get Sensor event status command
916 * @param sensorNumber - sensor number, FFh = reserved
917 *
918 * @returns IPMI completion code plus response data
919 * - sensorEventStatus - Sensor Event messages state
920 * - assertions - Assertion event messages
921 * - deassertions - Deassertion event messages
922 */
923ipmi::RspType<uint8_t, // sensorEventStatus
924 std::bitset<16>, // assertions
925 std::bitset<16> // deassertion
926 >
927 ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum)
928{
929 if (sensorNum == reservedSensorNumber)
930 {
931 return ipmi::responseInvalidFieldRequest();
932 }
933
934 std::string connection;
935 std::string path;
936 auto status = getSensorConnection(ctx, sensorNum, connection, path);
937 if (status)
938 {
939 phosphor::logging::log<phosphor::logging::level::ERR>(
940 "ipmiSenGetSensorEventStatus: Sensor connection Error",
941 phosphor::logging::entry("SENSOR=%d", sensorNum));
942 return ipmi::response(status);
943 }
944
945 DbusInterfaceMap sensorMap;
946 if (!getSensorMap(ctx, connection, path, sensorMap))
947 {
948 phosphor::logging::log<phosphor::logging::level::ERR>(
949 "ipmiSenGetSensorEventStatus: Sensor Mapping Error",
950 phosphor::logging::entry("SENSOR=%s", path.c_str()));
951 return ipmi::responseResponseError();
952 }
953 auto warningInterface =
954 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
955 auto criticalInterface =
956 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
957
958 uint8_t sensorEventStatus =
959 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
960
961 std::optional<bool> criticalDeassertHigh =
962 thresholdDeassertMap[path]["CriticalAlarmHigh"];
963 std::optional<bool> criticalDeassertLow =
964 thresholdDeassertMap[path]["CriticalAlarmLow"];
965 std::optional<bool> warningDeassertHigh =
966 thresholdDeassertMap[path]["WarningAlarmHigh"];
967 std::optional<bool> warningDeassertLow =
968 thresholdDeassertMap[path]["WarningAlarmLow"];
969
970 std::bitset<16> assertions = 0;
971 std::bitset<16> deassertions = 0;
972
973 if (criticalDeassertHigh && !*criticalDeassertHigh)
974 {
975 deassertions.set(static_cast<size_t>(
976 IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
977 }
978 if (criticalDeassertLow && !*criticalDeassertLow)
979 {
980 deassertions.set(static_cast<size_t>(
981 IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
982 }
983 if (warningDeassertHigh && !*warningDeassertHigh)
984 {
985 deassertions.set(static_cast<size_t>(
986 IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
987 }
988 if (warningDeassertLow && !*warningDeassertLow)
989 {
990 deassertions.set(static_cast<size_t>(
991 IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
992 }
993 if ((warningInterface != sensorMap.end()) ||
994 (criticalInterface != sensorMap.end()))
995 {
996 sensorEventStatus = static_cast<size_t>(
997 IPMISensorEventEnableByte2::eventMessagesEnable);
998 if (warningInterface != sensorMap.end())
999 {
1000 auto& warningMap = warningInterface->second;
1001
1002 auto warningHigh = warningMap.find("WarningAlarmHigh");
1003 auto warningLow = warningMap.find("WarningAlarmLow");
1004 auto warningHighAlarm = false;
1005 auto warningLowAlarm = false;
1006
1007 if (warningHigh != warningMap.end())
1008 {
1009 warningHighAlarm = std::get<bool>(warningHigh->second);
1010 }
1011 if (warningLow != warningMap.end())
1012 {
1013 warningLowAlarm = std::get<bool>(warningLow->second);
1014 }
1015 if (warningHighAlarm)
1016 {
1017 assertions.set(
1018 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1019 upperNonCriticalGoingHigh));
1020 }
1021 if (warningLowAlarm)
1022 {
1023 assertions.set(
1024 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1025 lowerNonCriticalGoingLow));
1026 }
1027 }
1028 if (criticalInterface != sensorMap.end())
1029 {
1030 auto& criticalMap = criticalInterface->second;
1031
1032 auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
1033 auto criticalLow = criticalMap.find("CriticalAlarmLow");
1034 auto criticalHighAlarm = false;
1035 auto criticalLowAlarm = false;
1036
1037 if (criticalHigh != criticalMap.end())
1038 {
1039 criticalHighAlarm = std::get<bool>(criticalHigh->second);
1040 }
1041 if (criticalLow != criticalMap.end())
1042 {
1043 criticalLowAlarm = std::get<bool>(criticalLow->second);
1044 }
1045 if (criticalHighAlarm)
1046 {
1047 assertions.set(
1048 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1049 upperCriticalGoingHigh));
1050 }
1051 if (criticalLowAlarm)
1052 {
1053 assertions.set(static_cast<size_t>(
1054 IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
1055 }
1056 }
1057 }
1058
1059 return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
1060}
1061
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001062static int getSensorDataRecord(ipmi::Context::ptr ctx,
1063 std::vector<uint8_t>& recordData,
1064 uint16_t recordID)
Willy Tude54f482021-01-26 15:59:09 -08001065{
1066 auto& sensorTree = getSensorTree();
Willy Tude54f482021-01-26 15:59:09 -08001067 size_t fruCount = 0;
Willy Tude54f482021-01-26 15:59:09 -08001068 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1069 if (ret != ipmi::ccSuccess)
1070 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001071 phosphor::logging::log<phosphor::logging::level::ERR>(
1072 "getSensorDataRecord: getFruSdrCount error");
Willy Tude54f482021-01-26 15:59:09 -08001073 return GENERAL_ERROR;
1074 }
1075
1076 size_t lastRecord =
1077 sensorTree.size() + fruCount + ipmi::storage::type12Count - 1;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001078 if (recordID == lastRecordIndex)
Willy Tude54f482021-01-26 15:59:09 -08001079 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001080 recordID = lastRecord;
1081 }
1082 if (recordID > lastRecord)
1083 {
1084 phosphor::logging::log<phosphor::logging::level::ERR>(
1085 "getSensorDataRecord: recordID > lastRecord error");
Willy Tude54f482021-01-26 15:59:09 -08001086 return GENERAL_ERROR;
1087 }
1088
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001089 if (recordID >= sensorTree.size())
Willy Tude54f482021-01-26 15:59:09 -08001090 {
1091 size_t fruIndex = recordID - sensorTree.size();
Willy Tude54f482021-01-26 15:59:09 -08001092 if (fruIndex >= fruCount)
1093 {
1094 // handle type 12 hardcoded records
1095 size_t type12Index = fruIndex - fruCount;
1096 if (type12Index >= ipmi::storage::type12Count)
1097 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001098 phosphor::logging::log<phosphor::logging::level::ERR>(
1099 "getSensorDataRecord: type12Index error");
Willy Tude54f482021-01-26 15:59:09 -08001100 return GENERAL_ERROR;
1101 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001102 recordData = ipmi::storage::getType12SDRs(type12Index, recordID);
Willy Tude54f482021-01-26 15:59:09 -08001103 }
1104 else
1105 {
1106 // handle fru records
1107 get_sdr::SensorDataFruRecord data;
1108 ret = ipmi::storage::getFruSdrs(ctx, fruIndex, data);
1109 if (ret != IPMI_CC_OK)
1110 {
1111 return GENERAL_ERROR;
1112 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001113 data.header.record_id_msb = recordID >> 8;
1114 data.header.record_id_lsb = recordID & 0xFF;
1115 recordData.insert(recordData.end(),
1116 reinterpret_cast<uint8_t*>(&data),
1117 reinterpret_cast<uint8_t*>(&data) + sizeof(data));
Willy Tude54f482021-01-26 15:59:09 -08001118 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001119
1120 return 0;
1121 }
1122
1123 std::string connection;
1124 std::string path;
1125 auto status = getSensorConnection(ctx, recordID, connection, path);
1126 if (status)
1127 {
1128 phosphor::logging::log<phosphor::logging::level::ERR>(
1129 "getSensorDataRecord: getSensorConnection error");
1130 return GENERAL_ERROR;
1131 }
1132 DbusInterfaceMap sensorMap;
1133 if (!getSensorMap(ctx, connection, path, sensorMap, sensorMapUpdatePeriod))
1134 {
1135 phosphor::logging::log<phosphor::logging::level::ERR>(
1136 "getSensorDataRecord: getSensorMap error");
1137 return GENERAL_ERROR;
1138 }
1139 uint16_t sensorNum = getSensorNumberFromPath(path);
1140 if (sensorNum == invalidSensorNumber)
1141 {
1142 phosphor::logging::log<phosphor::logging::level::ERR>(
1143 "getSensorDataRecord: invalidSensorNumber");
1144 return GENERAL_ERROR;
1145 }
1146 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1147 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1148
1149 get_sdr::SensorDataFullRecord record = {0};
1150
1151 get_sdr::header::set_record_id(
1152 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1153
1154 record.header.sdr_version = ipmiSdrVersion;
1155 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1156 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1157 sizeof(get_sdr::SensorDataRecordHeader);
1158 record.key.owner_id = 0x20;
1159 record.key.owner_lun = lun;
1160 record.key.sensor_number = sensornumber;
1161
1162 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
1163 record.body.sensor_type = getSensorTypeFromPath(path);
1164 std::string type = getSensorTypeStringFromPath(path);
1165 auto typeCstr = type.c_str();
1166 auto findUnits = sensorUnits.find(typeCstr);
1167 if (findUnits != sensorUnits.end())
1168 {
1169 record.body.sensor_units_2_base =
1170 static_cast<uint8_t>(findUnits->second);
1171 } // else default 0x0 unspecified
1172
1173 record.body.event_reading_type = getSensorEventTypeFromPath(path);
1174
1175 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
1176 if (sensorObject == sensorMap.end())
1177 {
1178 phosphor::logging::log<phosphor::logging::level::ERR>(
1179 "getSensorDataRecord: sensorObject error");
1180 return GENERAL_ERROR;
1181 }
1182
1183 uint8_t entityId = 0;
1184 uint8_t entityInstance = 0x01;
1185
1186 // follow the association chain to get the parent board's entityid and
1187 // entityInstance
1188 updateIpmiFromAssociation(path, sensorMap, entityId, entityInstance);
1189
1190 record.body.entity_id = entityId;
1191 record.body.entity_instance = entityInstance;
1192
1193 auto maxObject = sensorObject->second.find("MaxValue");
1194 auto minObject = sensorObject->second.find("MinValue");
1195
1196 // If min and/or max are left unpopulated,
1197 // then default to what a signed byte would be, namely (-128,127) range.
1198 auto max = static_cast<double>(std::numeric_limits<int8_t>::max());
1199 auto min = static_cast<double>(std::numeric_limits<int8_t>::lowest());
1200 if (maxObject != sensorObject->second.end())
1201 {
1202 max = std::visit(VariantToDoubleVisitor(), maxObject->second);
1203 }
1204
1205 if (minObject != sensorObject->second.end())
1206 {
1207 min = std::visit(VariantToDoubleVisitor(), minObject->second);
1208 }
1209
1210 int16_t mValue = 0;
1211 int8_t rExp = 0;
1212 int16_t bValue = 0;
1213 int8_t bExp = 0;
1214 bool bSigned = false;
1215
1216 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1217 {
1218 phosphor::logging::log<phosphor::logging::level::ERR>(
1219 "getSensorDataRecord: getSensorAttributes error");
1220 return GENERAL_ERROR;
1221 }
1222
1223 // The record.body is a struct SensorDataFullRecordBody
1224 // from sensorhandler.hpp in phosphor-ipmi-host.
1225 // The meaning of these bits appears to come from
1226 // table 43.1 of the IPMI spec.
1227 // The above 5 sensor attributes are stuffed in as follows:
1228 // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1229 // Byte 22-24 are for other purposes
1230 // Byte 25 = MMMMMMMM = LSB of M
1231 // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1232 // Byte 27 = BBBBBBBB = LSB of B
1233 // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1234 // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1235 // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1236
1237 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1238 record.body.m_lsb = mValue & 0xFF;
1239
1240 uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1241 uint8_t mBitNine = (mValue & 0x0100) >> 8;
1242
1243 // move the smallest bit of the MSB into place (bit 9)
1244 // the MSbs are bits 7:8 in m_msb_and_tolerance
1245 record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6);
1246
1247 record.body.b_lsb = bValue & 0xFF;
1248
1249 uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1250 uint8_t bBitNine = (bValue & 0x0100) >> 8;
1251
1252 // move the smallest bit of the MSB into place (bit 9)
1253 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1254 record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6);
1255
1256 uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1257 uint8_t rExpBits = rExp & 0x07;
1258
1259 uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1260 uint8_t bExpBits = bExp & 0x07;
1261
1262 // move rExp and bExp into place
1263 record.body.r_b_exponents =
1264 (rExpSign << 7) | (rExpBits << 4) | (bExpSign << 3) | bExpBits;
1265
1266 // Set the analog reading byte interpretation accordingly
1267 record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7;
1268
1269 // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1270 // These seem redundant, but derivable from the above 5 attributes
1271 // Original comment said "todo fill out rest of units"
1272
1273 // populate sensor name from path
1274 std::string name;
1275 size_t nameStart = path.rfind("/");
1276 if (nameStart != std::string::npos)
1277 {
1278 name = path.substr(nameStart + 1, std::string::npos - nameStart);
1279 }
1280
1281 std::replace(name.begin(), name.end(), '_', ' ');
1282 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
1283 {
1284 // try to not truncate by replacing common words
1285 constexpr std::array<std::pair<const char*, const char*>, 2>
1286 replaceWords = {std::make_pair("Output", "Out"),
1287 std::make_pair("Input", "In")};
1288 for (const auto& [find, replace] : replaceWords)
1289 {
1290 boost::replace_all(name, find, replace);
1291 }
1292
1293 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
1294 }
1295 record.body.id_string_info = name.size();
1296 std::strncpy(record.body.id_string, name.c_str(),
1297 sizeof(record.body.id_string));
1298
Josh Lehana55c9532020-10-28 21:59:06 -07001299 // Remember the sensor name, as determined for this sensor number
1300 details::sdrStatsTable.updateName(sensornumber, name);
1301
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001302 IPMIThresholds thresholdData;
1303 try
1304 {
1305 thresholdData = getIPMIThresholds(sensorMap);
1306 }
1307 catch (std::exception&)
1308 {
1309 phosphor::logging::log<phosphor::logging::level::ERR>(
1310 "getSensorDataRecord: getIPMIThresholds error");
1311 return GENERAL_ERROR;
1312 }
1313
1314 if (thresholdData.criticalHigh)
1315 {
1316 record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1317 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1318 IPMISensorEventEnableThresholds::criticalThreshold);
1319 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1320 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1321 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1322 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1323 record.body.discrete_reading_setting_mask[0] |=
1324 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1325 }
1326 if (thresholdData.warningHigh)
1327 {
1328 record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1329 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1330 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1331 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1332 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1333 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1334 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1335 record.body.discrete_reading_setting_mask[0] |=
1336 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1337 }
1338 if (thresholdData.criticalLow)
1339 {
1340 record.body.lower_critical_threshold = *thresholdData.criticalLow;
1341 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1342 IPMISensorEventEnableThresholds::criticalThreshold);
1343 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1344 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1345 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1346 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1347 record.body.discrete_reading_setting_mask[0] |=
1348 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1349 }
1350 if (thresholdData.warningLow)
1351 {
1352 record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1353 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1354 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1355 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1356 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1357 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1358 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1359 record.body.discrete_reading_setting_mask[0] |=
1360 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1361 }
1362
1363 // everything that is readable is setable
1364 record.body.discrete_reading_setting_mask[1] =
1365 record.body.discrete_reading_setting_mask[0];
1366 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
1367 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
Willy Tude54f482021-01-26 15:59:09 -08001368 return 0;
1369}
1370
1371/** @brief implements the get SDR Info command
1372 * @param count - Operation
1373 *
1374 * @returns IPMI completion code plus response data
1375 * - sdrCount - sensor/SDR count
1376 * - lunsAndDynamicPopulation - static/Dynamic sensor population flag
1377 */
1378static ipmi::RspType<uint8_t, // respcount
1379 uint8_t, // dynamic population flags
1380 uint32_t // last time a sensor was added
1381 >
1382 ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
1383 std::optional<uint8_t> count)
1384{
1385 auto& sensorTree = getSensorTree();
1386 uint8_t sdrCount = 0;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001387 uint16_t recordID = 0;
1388 std::vector<uint8_t> record;
Willy Tude54f482021-01-26 15:59:09 -08001389 // Sensors are dynamically allocated, and there is at least one LUN
1390 uint8_t lunsAndDynamicPopulation = 0x80;
1391 constexpr uint8_t getSdrCount = 0x01;
1392 constexpr uint8_t getSensorCount = 0x00;
1393
1394 if (!getSensorSubtree(sensorTree) || sensorTree.empty())
1395 {
1396 return ipmi::responseResponseError();
1397 }
Willy Tude54f482021-01-26 15:59:09 -08001398 uint16_t numSensors = sensorTree.size();
1399 if (count.value_or(0) == getSdrCount)
1400 {
1401 // Count the number of Type 1 SDR entries assigned to the LUN
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001402 while (!getSensorDataRecord(ctx, record, recordID++))
Willy Tude54f482021-01-26 15:59:09 -08001403 {
1404 get_sdr::SensorDataRecordHeader* hdr =
1405 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001406 record.data());
Willy Tude54f482021-01-26 15:59:09 -08001407 if (hdr && hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD)
1408 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001409 get_sdr::SensorDataFullRecord* recordData =
Willy Tude54f482021-01-26 15:59:09 -08001410 reinterpret_cast<get_sdr::SensorDataFullRecord*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001411 record.data());
1412 if (ctx->lun == recordData->key.owner_lun)
Willy Tude54f482021-01-26 15:59:09 -08001413 {
1414 sdrCount++;
1415 }
1416 }
1417 }
1418 }
1419 else if (count.value_or(0) == getSensorCount)
1420 {
1421 // Return the number of sensors attached to the LUN
1422 if ((ctx->lun == 0) && (numSensors > 0))
1423 {
1424 sdrCount =
1425 (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN : numSensors;
1426 }
1427 else if ((ctx->lun == 1) && (numSensors > maxSensorsPerLUN))
1428 {
1429 sdrCount = (numSensors > (2 * maxSensorsPerLUN))
1430 ? maxSensorsPerLUN
1431 : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
1432 }
1433 else if (ctx->lun == 3)
1434 {
1435 if (numSensors <= maxIPMISensors)
1436 {
1437 sdrCount =
1438 (numSensors - (2 * maxSensorsPerLUN)) & maxSensorsPerLUN;
1439 }
1440 else
1441 {
1442 // error
1443 throw std::out_of_range(
1444 "Maximum number of IPMI sensors exceeded.");
1445 }
1446 }
1447 }
1448 else
1449 {
1450 return ipmi::responseInvalidFieldRequest();
1451 }
1452
1453 // Get Sensor count. This returns the number of sensors
1454 if (numSensors > 0)
1455 {
1456 lunsAndDynamicPopulation |= 1;
1457 }
1458 if (numSensors > maxSensorsPerLUN)
1459 {
1460 lunsAndDynamicPopulation |= 2;
1461 }
1462 if (numSensors >= (maxSensorsPerLUN * 2))
1463 {
1464 lunsAndDynamicPopulation |= 8;
1465 }
1466 if (numSensors > maxIPMISensors)
1467 {
1468 // error
1469 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
1470 }
1471
1472 return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
1473 sdrLastAdd);
1474}
1475
1476/* end sensor commands */
1477
1478/* storage commands */
1479
1480ipmi::RspType<uint8_t, // sdr version
1481 uint16_t, // record count
1482 uint16_t, // free space
1483 uint32_t, // most recent addition
1484 uint32_t, // most recent erase
1485 uint8_t // operationSupport
1486 >
1487 ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
1488{
1489 auto& sensorTree = getSensorTree();
1490 constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001491 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08001492 {
1493 return ipmi::responseResponseError();
1494 }
1495
1496 size_t fruCount = 0;
1497 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1498 if (ret != ipmi::ccSuccess)
1499 {
1500 return ipmi::response(ret);
1501 }
1502
1503 uint16_t recordCount =
1504 sensorTree.size() + fruCount + ipmi::storage::type12Count;
1505
1506 uint8_t operationSupport = static_cast<uint8_t>(
1507 SdrRepositoryInfoOps::overflow); // write not supported
1508
1509 operationSupport |=
1510 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
1511 operationSupport |= static_cast<uint8_t>(
1512 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
1513 return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
1514 unspecifiedFreeSpace, sdrLastAdd,
1515 sdrLastRemove, operationSupport);
1516}
1517
1518/** @brief implements the get SDR allocation info command
1519 *
1520 * @returns IPMI completion code plus response data
1521 * - allocUnits - Number of possible allocation units
1522 * - allocUnitSize - Allocation unit size in bytes.
1523 * - allocUnitFree - Number of free allocation units
1524 * - allocUnitLargestFree - Largest free block in allocation units
1525 * - maxRecordSize - Maximum record size in allocation units.
1526 */
1527ipmi::RspType<uint16_t, // allocUnits
1528 uint16_t, // allocUnitSize
1529 uint16_t, // allocUnitFree
1530 uint16_t, // allocUnitLargestFree
1531 uint8_t // maxRecordSize
1532 >
1533 ipmiStorageGetSDRAllocationInfo()
1534{
1535 // 0000h unspecified number of alloc units
1536 constexpr uint16_t allocUnits = 0;
1537
1538 constexpr uint16_t allocUnitFree = 0;
1539 constexpr uint16_t allocUnitLargestFree = 0;
1540 // only allow one block at a time
1541 constexpr uint8_t maxRecordSize = 1;
1542
1543 return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
1544 allocUnitLargestFree, maxRecordSize);
1545}
1546
1547/** @brief implements the reserve SDR command
1548 * @returns IPMI completion code plus response data
1549 * - sdrReservationID
1550 */
1551ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
1552{
1553 sdrReservationID++;
1554 if (sdrReservationID == 0)
1555 {
1556 sdrReservationID++;
1557 }
1558
1559 return ipmi::responseSuccess(sdrReservationID);
1560}
1561
1562ipmi::RspType<uint16_t, // next record ID
1563 std::vector<uint8_t> // payload
1564 >
1565 ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
1566 uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
1567{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001568 size_t fruCount = 0;
Willy Tude54f482021-01-26 15:59:09 -08001569 // reservation required for partial reads with non zero offset into
1570 // record
1571 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
1572 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001573 phosphor::logging::log<phosphor::logging::level::ERR>(
1574 "ipmiStorageGetSDR: responseInvalidReservationId");
Willy Tude54f482021-01-26 15:59:09 -08001575 return ipmi::responseInvalidReservationId();
1576 }
Willy Tude54f482021-01-26 15:59:09 -08001577 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1578 if (ret != ipmi::ccSuccess)
1579 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001580 phosphor::logging::log<phosphor::logging::level::ERR>(
1581 "ipmiStorageGetSDR: getFruSdrCount error");
Willy Tude54f482021-01-26 15:59:09 -08001582 return ipmi::response(ret);
1583 }
1584
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001585 auto& sensorTree = getSensorTree();
Willy Tude54f482021-01-26 15:59:09 -08001586 size_t lastRecord =
1587 sensorTree.size() + fruCount + ipmi::storage::type12Count - 1;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001588 uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
1589
1590 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08001591 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001592 phosphor::logging::log<phosphor::logging::level::ERR>(
1593 "ipmiStorageGetSDR: getSensorSubtree error");
1594 return ipmi::responseResponseError();
Willy Tude54f482021-01-26 15:59:09 -08001595 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001596
1597 std::vector<uint8_t> record;
1598 if (getSensorDataRecord(ctx, record, recordID))
Willy Tude54f482021-01-26 15:59:09 -08001599 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001600 phosphor::logging::log<phosphor::logging::level::ERR>(
1601 "ipmiStorageGetSDR: fail to get SDR");
Willy Tude54f482021-01-26 15:59:09 -08001602 return ipmi::responseInvalidFieldRequest();
1603 }
Willy Tude54f482021-01-26 15:59:09 -08001604 get_sdr::SensorDataRecordHeader* hdr =
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001605 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
Willy Tude54f482021-01-26 15:59:09 -08001606 if (!hdr)
1607 {
1608 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001609 "ipmiStorageGetSDR: record header is null");
1610 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08001611 }
1612
1613 size_t sdrLength =
1614 sizeof(get_sdr::SensorDataRecordHeader) + hdr->record_length;
1615 if (sdrLength < (offset + bytesToRead))
1616 {
1617 bytesToRead = sdrLength - offset;
1618 }
1619
1620 uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
1621 if (!respStart)
1622 {
1623 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001624 "ipmiStorageGetSDR: record is null");
1625 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08001626 }
1627
1628 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001629
Willy Tude54f482021-01-26 15:59:09 -08001630 return ipmi::responseSuccess(nextRecordId, recordData);
1631}
1632/* end storage commands */
1633
1634void registerSensorFunctions()
1635{
1636 // <Platform Event>
1637 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1638 ipmi::sensor_event::cmdPlatformEvent,
1639 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
1640
Willy Tudbafbce2021-03-29 00:37:05 -07001641#ifdef FEATURE_DYNAMIC_SENSORS_WRITE
1642 // <Set Sensor Reading and Event Status>
1643 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1644 ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
1645 ipmi::Privilege::Operator, ipmiSetSensorReading);
1646#endif
1647
Willy Tude54f482021-01-26 15:59:09 -08001648 // <Get Sensor Reading>
1649 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1650 ipmi::sensor_event::cmdGetSensorReading,
1651 ipmi::Privilege::User, ipmiSenGetSensorReading);
1652
1653 // <Get Sensor Threshold>
1654 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1655 ipmi::sensor_event::cmdGetSensorThreshold,
1656 ipmi::Privilege::User, ipmiSenGetSensorThresholds);
1657
1658 // <Set Sensor Threshold>
1659 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1660 ipmi::sensor_event::cmdSetSensorThreshold,
1661 ipmi::Privilege::Operator,
1662 ipmiSenSetSensorThresholds);
1663
1664 // <Get Sensor Event Enable>
1665 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1666 ipmi::sensor_event::cmdGetSensorEventEnable,
1667 ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
1668
1669 // <Get Sensor Event Status>
1670 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1671 ipmi::sensor_event::cmdGetSensorEventStatus,
1672 ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
1673
1674 // register all storage commands for both Sensor and Storage command
1675 // versions
1676
1677 // <Get SDR Repository Info>
1678 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1679 ipmi::storage::cmdGetSdrRepositoryInfo,
1680 ipmi::Privilege::User,
1681 ipmiStorageGetSDRRepositoryInfo);
1682
1683 // <Get Device SDR Info>
1684 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1685 ipmi::sensor_event::cmdGetDeviceSdrInfo,
1686 ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
1687
1688 // <Get SDR Allocation Info>
1689 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1690 ipmi::storage::cmdGetSdrRepositoryAllocInfo,
1691 ipmi::Privilege::User,
1692 ipmiStorageGetSDRAllocationInfo);
1693
1694 // <Reserve SDR Repo>
1695 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1696 ipmi::sensor_event::cmdReserveDeviceSdrRepository,
1697 ipmi::Privilege::User, ipmiStorageReserveSDR);
1698
1699 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1700 ipmi::storage::cmdReserveSdrRepository,
1701 ipmi::Privilege::User, ipmiStorageReserveSDR);
1702
1703 // <Get Sdr>
1704 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1705 ipmi::sensor_event::cmdGetDeviceSdr,
1706 ipmi::Privilege::User, ipmiStorageGetSDR);
1707
1708 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1709 ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
1710 ipmiStorageGetSDR);
1711}
1712} // namespace ipmi