blob: 42fb9de43cf416dd155dca9206cdd7e271062764 [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
270ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
271 ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum)
272{
273 std::string connection;
274 std::string path;
275
276 auto status = getSensorConnection(ctx, sensnum, connection, path);
277 if (status)
278 {
279 return ipmi::response(status);
280 }
281
282 DbusInterfaceMap sensorMap;
283 if (!getSensorMap(ctx, connection, path, sensorMap))
284 {
285 return ipmi::responseResponseError();
286 }
287 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
288
289 if (sensorObject == sensorMap.end() ||
290 sensorObject->second.find("Value") == sensorObject->second.end())
291 {
292 return ipmi::responseResponseError();
293 }
294 auto& valueVariant = sensorObject->second["Value"];
295 double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
296
297 double max = 0;
298 double min = 0;
299 getSensorMaxMin(sensorMap, max, min);
300
301 int16_t mValue = 0;
302 int16_t bValue = 0;
303 int8_t rExp = 0;
304 int8_t bExp = 0;
305 bool bSigned = false;
306
307 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
308 {
309 return ipmi::responseResponseError();
310 }
311
312 uint8_t value =
313 scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
314 uint8_t operation =
315 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
316 operation |=
317 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
318 bool notReading = std::isnan(reading);
319
320 if (!notReading)
321 {
322 auto availableObject =
323 sensorMap.find("xyz.openbmc_project.State.Decorator.Availability");
324 if (availableObject != sensorMap.end())
325 {
326 auto findAvailable = availableObject->second.find("Available");
327 if (findAvailable != availableObject->second.end())
328 {
329 bool* available = std::get_if<bool>(&(findAvailable->second));
330 if (available && !(*available))
331 {
332 notReading = true;
333 }
334 }
335 }
336 }
337
338 if (notReading)
339 {
340 operation |= static_cast<uint8_t>(
341 IPMISensorReadingByte2::readingStateUnavailable);
342 }
343
Josh Lehana55c9532020-10-28 21:59:06 -0700344 if constexpr (details::enableInstrumentation)
345 {
346 int byteValue;
347 if (bSigned)
348 {
349 byteValue = static_cast<int>(static_cast<int8_t>(value));
350 }
351 else
352 {
353 byteValue = static_cast<int>(static_cast<uint8_t>(value));
354 }
355
356 // Keep stats on the reading just obtained, even if it is "NaN"
357 if (details::sdrStatsTable.updateReading(sensnum, reading, byteValue))
358 {
359 // This is the first reading, show the coefficients
360 double step = (max - min) / 255.0;
361 std::cerr << "IPMI sensor "
362 << details::sdrStatsTable.getName(sensnum)
363 << ": Range min=" << min << " max=" << max
364 << ", step=" << step
365 << ", Coefficients mValue=" << static_cast<int>(mValue)
366 << " rExp=" << static_cast<int>(rExp)
367 << " bValue=" << static_cast<int>(bValue)
368 << " bExp=" << static_cast<int>(bExp)
369 << " bSigned=" << static_cast<int>(bSigned) << "\n";
370 }
371 }
372
Willy Tude54f482021-01-26 15:59:09 -0800373 uint8_t thresholds = 0;
374
375 auto warningObject =
376 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
377 if (warningObject != sensorMap.end())
378 {
379 auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
380 auto alarmLow = warningObject->second.find("WarningAlarmLow");
381 if (alarmHigh != warningObject->second.end())
382 {
383 if (std::get<bool>(alarmHigh->second))
384 {
385 thresholds |= static_cast<uint8_t>(
386 IPMISensorReadingByte3::upperNonCritical);
387 }
388 }
389 if (alarmLow != warningObject->second.end())
390 {
391 if (std::get<bool>(alarmLow->second))
392 {
393 thresholds |= static_cast<uint8_t>(
394 IPMISensorReadingByte3::lowerNonCritical);
395 }
396 }
397 }
398
399 auto criticalObject =
400 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
401 if (criticalObject != sensorMap.end())
402 {
403 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
404 auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
405 if (alarmHigh != criticalObject->second.end())
406 {
407 if (std::get<bool>(alarmHigh->second))
408 {
409 thresholds |=
410 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
411 }
412 }
413 if (alarmLow != criticalObject->second.end())
414 {
415 if (std::get<bool>(alarmLow->second))
416 {
417 thresholds |=
418 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
419 }
420 }
421 }
422
423 // no discrete as of today so optional byte is never returned
424 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
425}
426
427/** @brief implements the Set Sensor threshold command
428 * @param sensorNumber - sensor number
429 * @param lowerNonCriticalThreshMask
430 * @param lowerCriticalThreshMask
431 * @param lowerNonRecovThreshMask
432 * @param upperNonCriticalThreshMask
433 * @param upperCriticalThreshMask
434 * @param upperNonRecovThreshMask
435 * @param reserved
436 * @param lowerNonCritical - lower non-critical threshold
437 * @param lowerCritical - Lower critical threshold
438 * @param lowerNonRecoverable - Lower non recovarable threshold
439 * @param upperNonCritical - Upper non-critical threshold
440 * @param upperCritical - Upper critical
441 * @param upperNonRecoverable - Upper Non-recoverable
442 *
443 * @returns IPMI completion code
444 */
445ipmi::RspType<> ipmiSenSetSensorThresholds(
446 ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
447 bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
448 bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
449 bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
450 uint8_t lowerCritical, uint8_t lowerNonRecoverable,
451 uint8_t upperNonCritical, uint8_t upperCritical,
452 uint8_t upperNonRecoverable)
453{
454 if (reserved)
455 {
456 return ipmi::responseInvalidFieldRequest();
457 }
458
459 // lower nc and upper nc not suppported on any sensor
460 if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
461 {
462 return ipmi::responseInvalidFieldRequest();
463 }
464
465 // if none of the threshold mask are set, nothing to do
466 if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
467 lowerNonRecovThreshMask | upperNonCriticalThreshMask |
468 upperCriticalThreshMask | upperNonRecovThreshMask))
469 {
470 return ipmi::responseSuccess();
471 }
472
473 std::string connection;
474 std::string path;
475
476 ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path);
477 if (status)
478 {
479 return ipmi::response(status);
480 }
481 DbusInterfaceMap sensorMap;
482 if (!getSensorMap(ctx, connection, path, sensorMap))
483 {
484 return ipmi::responseResponseError();
485 }
486
487 double max = 0;
488 double min = 0;
489 getSensorMaxMin(sensorMap, max, min);
490
491 int16_t mValue = 0;
492 int16_t bValue = 0;
493 int8_t rExp = 0;
494 int8_t bExp = 0;
495 bool bSigned = false;
496
497 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
498 {
499 return ipmi::responseResponseError();
500 }
501
502 // store a vector of property name, value to set, and interface
503 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
504
505 // define the indexes of the tuple
506 constexpr uint8_t propertyName = 0;
507 constexpr uint8_t thresholdValue = 1;
508 constexpr uint8_t interface = 2;
509 // verifiy all needed fields are present
510 if (lowerCriticalThreshMask || upperCriticalThreshMask)
511 {
512 auto findThreshold =
513 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
514 if (findThreshold == sensorMap.end())
515 {
516 return ipmi::responseInvalidFieldRequest();
517 }
518 if (lowerCriticalThreshMask)
519 {
520 auto findLower = findThreshold->second.find("CriticalLow");
521 if (findLower == findThreshold->second.end())
522 {
523 return ipmi::responseInvalidFieldRequest();
524 }
525 thresholdsToSet.emplace_back("CriticalLow", lowerCritical,
526 findThreshold->first);
527 }
528 if (upperCriticalThreshMask)
529 {
530 auto findUpper = findThreshold->second.find("CriticalHigh");
531 if (findUpper == findThreshold->second.end())
532 {
533 return ipmi::responseInvalidFieldRequest();
534 }
535 thresholdsToSet.emplace_back("CriticalHigh", upperCritical,
536 findThreshold->first);
537 }
538 }
539 if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
540 {
541 auto findThreshold =
542 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
543 if (findThreshold == sensorMap.end())
544 {
545 return ipmi::responseInvalidFieldRequest();
546 }
547 if (lowerNonCriticalThreshMask)
548 {
549 auto findLower = findThreshold->second.find("WarningLow");
550 if (findLower == findThreshold->second.end())
551 {
552 return ipmi::responseInvalidFieldRequest();
553 }
554 thresholdsToSet.emplace_back("WarningLow", lowerNonCritical,
555 findThreshold->first);
556 }
557 if (upperNonCriticalThreshMask)
558 {
559 auto findUpper = findThreshold->second.find("WarningHigh");
560 if (findUpper == findThreshold->second.end())
561 {
562 return ipmi::responseInvalidFieldRequest();
563 }
564 thresholdsToSet.emplace_back("WarningHigh", upperNonCritical,
565 findThreshold->first);
566 }
567 }
568 for (const auto& property : thresholdsToSet)
569 {
570 // from section 36.3 in the IPMI Spec, assume all linear
571 double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
572 (bValue * std::pow(10.0, bExp))) *
573 std::pow(10.0, rExp);
574 setDbusProperty(
575 *getSdBus(), connection, path, std::get<interface>(property),
576 std::get<propertyName>(property), ipmi::Value(valueToSet));
577 }
578 return ipmi::responseSuccess();
579}
580
581IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap)
582{
583 IPMIThresholds resp;
584 auto warningInterface =
585 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
586 auto criticalInterface =
587 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
588
589 if ((warningInterface != sensorMap.end()) ||
590 (criticalInterface != sensorMap.end()))
591 {
592 auto sensorPair = sensorMap.find("xyz.openbmc_project.Sensor.Value");
593
594 if (sensorPair == sensorMap.end())
595 {
596 // should not have been able to find a sensor not implementing
597 // the sensor object
598 throw std::runtime_error("Invalid sensor map");
599 }
600
601 double max = 0;
602 double min = 0;
603 getSensorMaxMin(sensorMap, max, min);
604
605 int16_t mValue = 0;
606 int16_t bValue = 0;
607 int8_t rExp = 0;
608 int8_t bExp = 0;
609 bool bSigned = false;
610
611 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
612 {
613 throw std::runtime_error("Invalid sensor atrributes");
614 }
615 if (warningInterface != sensorMap.end())
616 {
617 auto& warningMap = warningInterface->second;
618
619 auto warningHigh = warningMap.find("WarningHigh");
620 auto warningLow = warningMap.find("WarningLow");
621
622 if (warningHigh != warningMap.end())
623 {
624
625 double value =
626 std::visit(VariantToDoubleVisitor(), warningHigh->second);
627 resp.warningHigh = scaleIPMIValueFromDouble(
628 value, mValue, rExp, bValue, bExp, bSigned);
629 }
630 if (warningLow != warningMap.end())
631 {
632 double value =
633 std::visit(VariantToDoubleVisitor(), warningLow->second);
634 resp.warningLow = scaleIPMIValueFromDouble(
635 value, mValue, rExp, bValue, bExp, bSigned);
636 }
637 }
638 if (criticalInterface != sensorMap.end())
639 {
640 auto& criticalMap = criticalInterface->second;
641
642 auto criticalHigh = criticalMap.find("CriticalHigh");
643 auto criticalLow = criticalMap.find("CriticalLow");
644
645 if (criticalHigh != criticalMap.end())
646 {
647 double value =
648 std::visit(VariantToDoubleVisitor(), criticalHigh->second);
649 resp.criticalHigh = scaleIPMIValueFromDouble(
650 value, mValue, rExp, bValue, bExp, bSigned);
651 }
652 if (criticalLow != criticalMap.end())
653 {
654 double value =
655 std::visit(VariantToDoubleVisitor(), criticalLow->second);
656 resp.criticalLow = scaleIPMIValueFromDouble(
657 value, mValue, rExp, bValue, bExp, bSigned);
658 }
659 }
660 }
661 return resp;
662}
663
664ipmi::RspType<uint8_t, // readable
665 uint8_t, // lowerNCrit
666 uint8_t, // lowerCrit
667 uint8_t, // lowerNrecoverable
668 uint8_t, // upperNC
669 uint8_t, // upperCrit
670 uint8_t> // upperNRecoverable
671 ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber)
672{
673 std::string connection;
674 std::string path;
675
676 auto status = getSensorConnection(ctx, sensorNumber, connection, path);
677 if (status)
678 {
679 return ipmi::response(status);
680 }
681
682 DbusInterfaceMap sensorMap;
683 if (!getSensorMap(ctx, connection, path, sensorMap))
684 {
685 return ipmi::responseResponseError();
686 }
687
688 IPMIThresholds thresholdData;
689 try
690 {
691 thresholdData = getIPMIThresholds(sensorMap);
692 }
693 catch (std::exception&)
694 {
695 return ipmi::responseResponseError();
696 }
697
698 uint8_t readable = 0;
699 uint8_t lowerNC = 0;
700 uint8_t lowerCritical = 0;
701 uint8_t lowerNonRecoverable = 0;
702 uint8_t upperNC = 0;
703 uint8_t upperCritical = 0;
704 uint8_t upperNonRecoverable = 0;
705
706 if (thresholdData.warningHigh)
707 {
708 readable |=
709 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
710 upperNC = *thresholdData.warningHigh;
711 }
712 if (thresholdData.warningLow)
713 {
714 readable |=
715 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
716 lowerNC = *thresholdData.warningLow;
717 }
718
719 if (thresholdData.criticalHigh)
720 {
721 readable |=
722 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
723 upperCritical = *thresholdData.criticalHigh;
724 }
725 if (thresholdData.criticalLow)
726 {
727 readable |=
728 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
729 lowerCritical = *thresholdData.criticalLow;
730 }
731
732 return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
733 lowerNonRecoverable, upperNC, upperCritical,
734 upperNonRecoverable);
735}
736
737/** @brief implements the get Sensor event enable command
738 * @param sensorNumber - sensor number
739 *
740 * @returns IPMI completion code plus response data
741 * - enabled - Sensor Event messages
742 * - assertionEnabledLsb - Assertion event messages
743 * - assertionEnabledMsb - Assertion event messages
744 * - deassertionEnabledLsb - Deassertion event messages
745 * - deassertionEnabledMsb - Deassertion event messages
746 */
747
748ipmi::RspType<uint8_t, // enabled
749 uint8_t, // assertionEnabledLsb
750 uint8_t, // assertionEnabledMsb
751 uint8_t, // deassertionEnabledLsb
752 uint8_t> // deassertionEnabledMsb
753 ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum)
754{
755 std::string connection;
756 std::string path;
757
758 uint8_t enabled = 0;
759 uint8_t assertionEnabledLsb = 0;
760 uint8_t assertionEnabledMsb = 0;
761 uint8_t deassertionEnabledLsb = 0;
762 uint8_t deassertionEnabledMsb = 0;
763
764 auto status = getSensorConnection(ctx, sensorNum, connection, path);
765 if (status)
766 {
767 return ipmi::response(status);
768 }
769
770 DbusInterfaceMap sensorMap;
771 if (!getSensorMap(ctx, connection, path, sensorMap))
772 {
773 return ipmi::responseResponseError();
774 }
775
776 auto warningInterface =
777 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
778 auto criticalInterface =
779 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
780 if ((warningInterface != sensorMap.end()) ||
781 (criticalInterface != sensorMap.end()))
782 {
783 enabled = static_cast<uint8_t>(
784 IPMISensorEventEnableByte2::sensorScanningEnable);
785 if (warningInterface != sensorMap.end())
786 {
787 auto& warningMap = warningInterface->second;
788
789 auto warningHigh = warningMap.find("WarningHigh");
790 auto warningLow = warningMap.find("WarningLow");
791 if (warningHigh != warningMap.end())
792 {
793 assertionEnabledLsb |= static_cast<uint8_t>(
794 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
795 deassertionEnabledLsb |= static_cast<uint8_t>(
796 IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
797 }
798 if (warningLow != warningMap.end())
799 {
800 assertionEnabledLsb |= static_cast<uint8_t>(
801 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
802 deassertionEnabledLsb |= static_cast<uint8_t>(
803 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
804 }
805 }
806 if (criticalInterface != sensorMap.end())
807 {
808 auto& criticalMap = criticalInterface->second;
809
810 auto criticalHigh = criticalMap.find("CriticalHigh");
811 auto criticalLow = criticalMap.find("CriticalLow");
812
813 if (criticalHigh != criticalMap.end())
814 {
815 assertionEnabledMsb |= static_cast<uint8_t>(
816 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
817 deassertionEnabledMsb |= static_cast<uint8_t>(
818 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
819 }
820 if (criticalLow != criticalMap.end())
821 {
822 assertionEnabledLsb |= static_cast<uint8_t>(
823 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
824 deassertionEnabledLsb |= static_cast<uint8_t>(
825 IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
826 }
827 }
828 }
829
830 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
831 assertionEnabledMsb, deassertionEnabledLsb,
832 deassertionEnabledMsb);
833}
834
835/** @brief implements the get Sensor event status command
836 * @param sensorNumber - sensor number, FFh = reserved
837 *
838 * @returns IPMI completion code plus response data
839 * - sensorEventStatus - Sensor Event messages state
840 * - assertions - Assertion event messages
841 * - deassertions - Deassertion event messages
842 */
843ipmi::RspType<uint8_t, // sensorEventStatus
844 std::bitset<16>, // assertions
845 std::bitset<16> // deassertion
846 >
847 ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum)
848{
849 if (sensorNum == reservedSensorNumber)
850 {
851 return ipmi::responseInvalidFieldRequest();
852 }
853
854 std::string connection;
855 std::string path;
856 auto status = getSensorConnection(ctx, sensorNum, connection, path);
857 if (status)
858 {
859 phosphor::logging::log<phosphor::logging::level::ERR>(
860 "ipmiSenGetSensorEventStatus: Sensor connection Error",
861 phosphor::logging::entry("SENSOR=%d", sensorNum));
862 return ipmi::response(status);
863 }
864
865 DbusInterfaceMap sensorMap;
866 if (!getSensorMap(ctx, connection, path, sensorMap))
867 {
868 phosphor::logging::log<phosphor::logging::level::ERR>(
869 "ipmiSenGetSensorEventStatus: Sensor Mapping Error",
870 phosphor::logging::entry("SENSOR=%s", path.c_str()));
871 return ipmi::responseResponseError();
872 }
873 auto warningInterface =
874 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
875 auto criticalInterface =
876 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
877
878 uint8_t sensorEventStatus =
879 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
880
881 std::optional<bool> criticalDeassertHigh =
882 thresholdDeassertMap[path]["CriticalAlarmHigh"];
883 std::optional<bool> criticalDeassertLow =
884 thresholdDeassertMap[path]["CriticalAlarmLow"];
885 std::optional<bool> warningDeassertHigh =
886 thresholdDeassertMap[path]["WarningAlarmHigh"];
887 std::optional<bool> warningDeassertLow =
888 thresholdDeassertMap[path]["WarningAlarmLow"];
889
890 std::bitset<16> assertions = 0;
891 std::bitset<16> deassertions = 0;
892
893 if (criticalDeassertHigh && !*criticalDeassertHigh)
894 {
895 deassertions.set(static_cast<size_t>(
896 IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
897 }
898 if (criticalDeassertLow && !*criticalDeassertLow)
899 {
900 deassertions.set(static_cast<size_t>(
901 IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
902 }
903 if (warningDeassertHigh && !*warningDeassertHigh)
904 {
905 deassertions.set(static_cast<size_t>(
906 IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
907 }
908 if (warningDeassertLow && !*warningDeassertLow)
909 {
910 deassertions.set(static_cast<size_t>(
911 IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
912 }
913 if ((warningInterface != sensorMap.end()) ||
914 (criticalInterface != sensorMap.end()))
915 {
916 sensorEventStatus = static_cast<size_t>(
917 IPMISensorEventEnableByte2::eventMessagesEnable);
918 if (warningInterface != sensorMap.end())
919 {
920 auto& warningMap = warningInterface->second;
921
922 auto warningHigh = warningMap.find("WarningAlarmHigh");
923 auto warningLow = warningMap.find("WarningAlarmLow");
924 auto warningHighAlarm = false;
925 auto warningLowAlarm = false;
926
927 if (warningHigh != warningMap.end())
928 {
929 warningHighAlarm = std::get<bool>(warningHigh->second);
930 }
931 if (warningLow != warningMap.end())
932 {
933 warningLowAlarm = std::get<bool>(warningLow->second);
934 }
935 if (warningHighAlarm)
936 {
937 assertions.set(
938 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
939 upperNonCriticalGoingHigh));
940 }
941 if (warningLowAlarm)
942 {
943 assertions.set(
944 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
945 lowerNonCriticalGoingLow));
946 }
947 }
948 if (criticalInterface != sensorMap.end())
949 {
950 auto& criticalMap = criticalInterface->second;
951
952 auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
953 auto criticalLow = criticalMap.find("CriticalAlarmLow");
954 auto criticalHighAlarm = false;
955 auto criticalLowAlarm = false;
956
957 if (criticalHigh != criticalMap.end())
958 {
959 criticalHighAlarm = std::get<bool>(criticalHigh->second);
960 }
961 if (criticalLow != criticalMap.end())
962 {
963 criticalLowAlarm = std::get<bool>(criticalLow->second);
964 }
965 if (criticalHighAlarm)
966 {
967 assertions.set(
968 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
969 upperCriticalGoingHigh));
970 }
971 if (criticalLowAlarm)
972 {
973 assertions.set(static_cast<size_t>(
974 IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
975 }
976 }
977 }
978
979 return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
980}
981
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800982static int getSensorDataRecord(ipmi::Context::ptr ctx,
983 std::vector<uint8_t>& recordData,
984 uint16_t recordID)
Willy Tude54f482021-01-26 15:59:09 -0800985{
986 auto& sensorTree = getSensorTree();
Willy Tude54f482021-01-26 15:59:09 -0800987 size_t fruCount = 0;
Willy Tude54f482021-01-26 15:59:09 -0800988 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
989 if (ret != ipmi::ccSuccess)
990 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800991 phosphor::logging::log<phosphor::logging::level::ERR>(
992 "getSensorDataRecord: getFruSdrCount error");
Willy Tude54f482021-01-26 15:59:09 -0800993 return GENERAL_ERROR;
994 }
995
996 size_t lastRecord =
997 sensorTree.size() + fruCount + ipmi::storage::type12Count - 1;
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800998 if (recordID == lastRecordIndex)
Willy Tude54f482021-01-26 15:59:09 -0800999 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001000 recordID = lastRecord;
1001 }
1002 if (recordID > lastRecord)
1003 {
1004 phosphor::logging::log<phosphor::logging::level::ERR>(
1005 "getSensorDataRecord: recordID > lastRecord error");
Willy Tude54f482021-01-26 15:59:09 -08001006 return GENERAL_ERROR;
1007 }
1008
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001009 if (recordID >= sensorTree.size())
Willy Tude54f482021-01-26 15:59:09 -08001010 {
1011 size_t fruIndex = recordID - sensorTree.size();
Willy Tude54f482021-01-26 15:59:09 -08001012 if (fruIndex >= fruCount)
1013 {
1014 // handle type 12 hardcoded records
1015 size_t type12Index = fruIndex - fruCount;
1016 if (type12Index >= ipmi::storage::type12Count)
1017 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001018 phosphor::logging::log<phosphor::logging::level::ERR>(
1019 "getSensorDataRecord: type12Index error");
Willy Tude54f482021-01-26 15:59:09 -08001020 return GENERAL_ERROR;
1021 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001022 recordData = ipmi::storage::getType12SDRs(type12Index, recordID);
Willy Tude54f482021-01-26 15:59:09 -08001023 }
1024 else
1025 {
1026 // handle fru records
1027 get_sdr::SensorDataFruRecord data;
1028 ret = ipmi::storage::getFruSdrs(ctx, fruIndex, data);
1029 if (ret != IPMI_CC_OK)
1030 {
1031 return GENERAL_ERROR;
1032 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001033 data.header.record_id_msb = recordID >> 8;
1034 data.header.record_id_lsb = recordID & 0xFF;
1035 recordData.insert(recordData.end(),
1036 reinterpret_cast<uint8_t*>(&data),
1037 reinterpret_cast<uint8_t*>(&data) + sizeof(data));
Willy Tude54f482021-01-26 15:59:09 -08001038 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001039
1040 return 0;
1041 }
1042
1043 std::string connection;
1044 std::string path;
1045 auto status = getSensorConnection(ctx, recordID, connection, path);
1046 if (status)
1047 {
1048 phosphor::logging::log<phosphor::logging::level::ERR>(
1049 "getSensorDataRecord: getSensorConnection error");
1050 return GENERAL_ERROR;
1051 }
1052 DbusInterfaceMap sensorMap;
1053 if (!getSensorMap(ctx, connection, path, sensorMap, sensorMapUpdatePeriod))
1054 {
1055 phosphor::logging::log<phosphor::logging::level::ERR>(
1056 "getSensorDataRecord: getSensorMap error");
1057 return GENERAL_ERROR;
1058 }
1059 uint16_t sensorNum = getSensorNumberFromPath(path);
1060 if (sensorNum == invalidSensorNumber)
1061 {
1062 phosphor::logging::log<phosphor::logging::level::ERR>(
1063 "getSensorDataRecord: invalidSensorNumber");
1064 return GENERAL_ERROR;
1065 }
1066 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1067 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1068
1069 get_sdr::SensorDataFullRecord record = {0};
1070
1071 get_sdr::header::set_record_id(
1072 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1073
1074 record.header.sdr_version = ipmiSdrVersion;
1075 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1076 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1077 sizeof(get_sdr::SensorDataRecordHeader);
1078 record.key.owner_id = 0x20;
1079 record.key.owner_lun = lun;
1080 record.key.sensor_number = sensornumber;
1081
1082 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
1083 record.body.sensor_type = getSensorTypeFromPath(path);
1084 std::string type = getSensorTypeStringFromPath(path);
1085 auto typeCstr = type.c_str();
1086 auto findUnits = sensorUnits.find(typeCstr);
1087 if (findUnits != sensorUnits.end())
1088 {
1089 record.body.sensor_units_2_base =
1090 static_cast<uint8_t>(findUnits->second);
1091 } // else default 0x0 unspecified
1092
1093 record.body.event_reading_type = getSensorEventTypeFromPath(path);
1094
1095 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
1096 if (sensorObject == sensorMap.end())
1097 {
1098 phosphor::logging::log<phosphor::logging::level::ERR>(
1099 "getSensorDataRecord: sensorObject error");
1100 return GENERAL_ERROR;
1101 }
1102
1103 uint8_t entityId = 0;
1104 uint8_t entityInstance = 0x01;
1105
1106 // follow the association chain to get the parent board's entityid and
1107 // entityInstance
1108 updateIpmiFromAssociation(path, sensorMap, entityId, entityInstance);
1109
1110 record.body.entity_id = entityId;
1111 record.body.entity_instance = entityInstance;
1112
1113 auto maxObject = sensorObject->second.find("MaxValue");
1114 auto minObject = sensorObject->second.find("MinValue");
1115
1116 // If min and/or max are left unpopulated,
1117 // then default to what a signed byte would be, namely (-128,127) range.
1118 auto max = static_cast<double>(std::numeric_limits<int8_t>::max());
1119 auto min = static_cast<double>(std::numeric_limits<int8_t>::lowest());
1120 if (maxObject != sensorObject->second.end())
1121 {
1122 max = std::visit(VariantToDoubleVisitor(), maxObject->second);
1123 }
1124
1125 if (minObject != sensorObject->second.end())
1126 {
1127 min = std::visit(VariantToDoubleVisitor(), minObject->second);
1128 }
1129
1130 int16_t mValue = 0;
1131 int8_t rExp = 0;
1132 int16_t bValue = 0;
1133 int8_t bExp = 0;
1134 bool bSigned = false;
1135
1136 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1137 {
1138 phosphor::logging::log<phosphor::logging::level::ERR>(
1139 "getSensorDataRecord: getSensorAttributes error");
1140 return GENERAL_ERROR;
1141 }
1142
1143 // The record.body is a struct SensorDataFullRecordBody
1144 // from sensorhandler.hpp in phosphor-ipmi-host.
1145 // The meaning of these bits appears to come from
1146 // table 43.1 of the IPMI spec.
1147 // The above 5 sensor attributes are stuffed in as follows:
1148 // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1149 // Byte 22-24 are for other purposes
1150 // Byte 25 = MMMMMMMM = LSB of M
1151 // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1152 // Byte 27 = BBBBBBBB = LSB of B
1153 // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1154 // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1155 // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1156
1157 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1158 record.body.m_lsb = mValue & 0xFF;
1159
1160 uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1161 uint8_t mBitNine = (mValue & 0x0100) >> 8;
1162
1163 // move the smallest bit of the MSB into place (bit 9)
1164 // the MSbs are bits 7:8 in m_msb_and_tolerance
1165 record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6);
1166
1167 record.body.b_lsb = bValue & 0xFF;
1168
1169 uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1170 uint8_t bBitNine = (bValue & 0x0100) >> 8;
1171
1172 // move the smallest bit of the MSB into place (bit 9)
1173 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1174 record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6);
1175
1176 uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1177 uint8_t rExpBits = rExp & 0x07;
1178
1179 uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1180 uint8_t bExpBits = bExp & 0x07;
1181
1182 // move rExp and bExp into place
1183 record.body.r_b_exponents =
1184 (rExpSign << 7) | (rExpBits << 4) | (bExpSign << 3) | bExpBits;
1185
1186 // Set the analog reading byte interpretation accordingly
1187 record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7;
1188
1189 // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1190 // These seem redundant, but derivable from the above 5 attributes
1191 // Original comment said "todo fill out rest of units"
1192
1193 // populate sensor name from path
1194 std::string name;
1195 size_t nameStart = path.rfind("/");
1196 if (nameStart != std::string::npos)
1197 {
1198 name = path.substr(nameStart + 1, std::string::npos - nameStart);
1199 }
1200
1201 std::replace(name.begin(), name.end(), '_', ' ');
1202 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
1203 {
1204 // try to not truncate by replacing common words
1205 constexpr std::array<std::pair<const char*, const char*>, 2>
1206 replaceWords = {std::make_pair("Output", "Out"),
1207 std::make_pair("Input", "In")};
1208 for (const auto& [find, replace] : replaceWords)
1209 {
1210 boost::replace_all(name, find, replace);
1211 }
1212
1213 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
1214 }
1215 record.body.id_string_info = name.size();
1216 std::strncpy(record.body.id_string, name.c_str(),
1217 sizeof(record.body.id_string));
1218
Josh Lehana55c9532020-10-28 21:59:06 -07001219 // Remember the sensor name, as determined for this sensor number
1220 details::sdrStatsTable.updateName(sensornumber, name);
1221
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001222 IPMIThresholds thresholdData;
1223 try
1224 {
1225 thresholdData = getIPMIThresholds(sensorMap);
1226 }
1227 catch (std::exception&)
1228 {
1229 phosphor::logging::log<phosphor::logging::level::ERR>(
1230 "getSensorDataRecord: getIPMIThresholds error");
1231 return GENERAL_ERROR;
1232 }
1233
1234 if (thresholdData.criticalHigh)
1235 {
1236 record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1237 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1238 IPMISensorEventEnableThresholds::criticalThreshold);
1239 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1240 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1241 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1242 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1243 record.body.discrete_reading_setting_mask[0] |=
1244 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1245 }
1246 if (thresholdData.warningHigh)
1247 {
1248 record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1249 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1250 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1251 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1252 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1253 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1254 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1255 record.body.discrete_reading_setting_mask[0] |=
1256 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1257 }
1258 if (thresholdData.criticalLow)
1259 {
1260 record.body.lower_critical_threshold = *thresholdData.criticalLow;
1261 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1262 IPMISensorEventEnableThresholds::criticalThreshold);
1263 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1264 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1265 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1266 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1267 record.body.discrete_reading_setting_mask[0] |=
1268 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1269 }
1270 if (thresholdData.warningLow)
1271 {
1272 record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1273 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1274 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1275 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1276 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1277 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1278 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1279 record.body.discrete_reading_setting_mask[0] |=
1280 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1281 }
1282
1283 // everything that is readable is setable
1284 record.body.discrete_reading_setting_mask[1] =
1285 record.body.discrete_reading_setting_mask[0];
1286 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
1287 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
Willy Tude54f482021-01-26 15:59:09 -08001288 return 0;
1289}
1290
1291/** @brief implements the get SDR Info command
1292 * @param count - Operation
1293 *
1294 * @returns IPMI completion code plus response data
1295 * - sdrCount - sensor/SDR count
1296 * - lunsAndDynamicPopulation - static/Dynamic sensor population flag
1297 */
1298static ipmi::RspType<uint8_t, // respcount
1299 uint8_t, // dynamic population flags
1300 uint32_t // last time a sensor was added
1301 >
1302 ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
1303 std::optional<uint8_t> count)
1304{
1305 auto& sensorTree = getSensorTree();
1306 uint8_t sdrCount = 0;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001307 uint16_t recordID = 0;
1308 std::vector<uint8_t> record;
Willy Tude54f482021-01-26 15:59:09 -08001309 // Sensors are dynamically allocated, and there is at least one LUN
1310 uint8_t lunsAndDynamicPopulation = 0x80;
1311 constexpr uint8_t getSdrCount = 0x01;
1312 constexpr uint8_t getSensorCount = 0x00;
1313
1314 if (!getSensorSubtree(sensorTree) || sensorTree.empty())
1315 {
1316 return ipmi::responseResponseError();
1317 }
Willy Tude54f482021-01-26 15:59:09 -08001318 uint16_t numSensors = sensorTree.size();
1319 if (count.value_or(0) == getSdrCount)
1320 {
1321 // Count the number of Type 1 SDR entries assigned to the LUN
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001322 while (!getSensorDataRecord(ctx, record, recordID++))
Willy Tude54f482021-01-26 15:59:09 -08001323 {
1324 get_sdr::SensorDataRecordHeader* hdr =
1325 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001326 record.data());
Willy Tude54f482021-01-26 15:59:09 -08001327 if (hdr && hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD)
1328 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001329 get_sdr::SensorDataFullRecord* recordData =
Willy Tude54f482021-01-26 15:59:09 -08001330 reinterpret_cast<get_sdr::SensorDataFullRecord*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001331 record.data());
1332 if (ctx->lun == recordData->key.owner_lun)
Willy Tude54f482021-01-26 15:59:09 -08001333 {
1334 sdrCount++;
1335 }
1336 }
1337 }
1338 }
1339 else if (count.value_or(0) == getSensorCount)
1340 {
1341 // Return the number of sensors attached to the LUN
1342 if ((ctx->lun == 0) && (numSensors > 0))
1343 {
1344 sdrCount =
1345 (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN : numSensors;
1346 }
1347 else if ((ctx->lun == 1) && (numSensors > maxSensorsPerLUN))
1348 {
1349 sdrCount = (numSensors > (2 * maxSensorsPerLUN))
1350 ? maxSensorsPerLUN
1351 : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
1352 }
1353 else if (ctx->lun == 3)
1354 {
1355 if (numSensors <= maxIPMISensors)
1356 {
1357 sdrCount =
1358 (numSensors - (2 * maxSensorsPerLUN)) & maxSensorsPerLUN;
1359 }
1360 else
1361 {
1362 // error
1363 throw std::out_of_range(
1364 "Maximum number of IPMI sensors exceeded.");
1365 }
1366 }
1367 }
1368 else
1369 {
1370 return ipmi::responseInvalidFieldRequest();
1371 }
1372
1373 // Get Sensor count. This returns the number of sensors
1374 if (numSensors > 0)
1375 {
1376 lunsAndDynamicPopulation |= 1;
1377 }
1378 if (numSensors > maxSensorsPerLUN)
1379 {
1380 lunsAndDynamicPopulation |= 2;
1381 }
1382 if (numSensors >= (maxSensorsPerLUN * 2))
1383 {
1384 lunsAndDynamicPopulation |= 8;
1385 }
1386 if (numSensors > maxIPMISensors)
1387 {
1388 // error
1389 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
1390 }
1391
1392 return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
1393 sdrLastAdd);
1394}
1395
1396/* end sensor commands */
1397
1398/* storage commands */
1399
1400ipmi::RspType<uint8_t, // sdr version
1401 uint16_t, // record count
1402 uint16_t, // free space
1403 uint32_t, // most recent addition
1404 uint32_t, // most recent erase
1405 uint8_t // operationSupport
1406 >
1407 ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
1408{
1409 auto& sensorTree = getSensorTree();
1410 constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001411 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08001412 {
1413 return ipmi::responseResponseError();
1414 }
1415
1416 size_t fruCount = 0;
1417 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1418 if (ret != ipmi::ccSuccess)
1419 {
1420 return ipmi::response(ret);
1421 }
1422
1423 uint16_t recordCount =
1424 sensorTree.size() + fruCount + ipmi::storage::type12Count;
1425
1426 uint8_t operationSupport = static_cast<uint8_t>(
1427 SdrRepositoryInfoOps::overflow); // write not supported
1428
1429 operationSupport |=
1430 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
1431 operationSupport |= static_cast<uint8_t>(
1432 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
1433 return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
1434 unspecifiedFreeSpace, sdrLastAdd,
1435 sdrLastRemove, operationSupport);
1436}
1437
1438/** @brief implements the get SDR allocation info command
1439 *
1440 * @returns IPMI completion code plus response data
1441 * - allocUnits - Number of possible allocation units
1442 * - allocUnitSize - Allocation unit size in bytes.
1443 * - allocUnitFree - Number of free allocation units
1444 * - allocUnitLargestFree - Largest free block in allocation units
1445 * - maxRecordSize - Maximum record size in allocation units.
1446 */
1447ipmi::RspType<uint16_t, // allocUnits
1448 uint16_t, // allocUnitSize
1449 uint16_t, // allocUnitFree
1450 uint16_t, // allocUnitLargestFree
1451 uint8_t // maxRecordSize
1452 >
1453 ipmiStorageGetSDRAllocationInfo()
1454{
1455 // 0000h unspecified number of alloc units
1456 constexpr uint16_t allocUnits = 0;
1457
1458 constexpr uint16_t allocUnitFree = 0;
1459 constexpr uint16_t allocUnitLargestFree = 0;
1460 // only allow one block at a time
1461 constexpr uint8_t maxRecordSize = 1;
1462
1463 return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
1464 allocUnitLargestFree, maxRecordSize);
1465}
1466
1467/** @brief implements the reserve SDR command
1468 * @returns IPMI completion code plus response data
1469 * - sdrReservationID
1470 */
1471ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
1472{
1473 sdrReservationID++;
1474 if (sdrReservationID == 0)
1475 {
1476 sdrReservationID++;
1477 }
1478
1479 return ipmi::responseSuccess(sdrReservationID);
1480}
1481
1482ipmi::RspType<uint16_t, // next record ID
1483 std::vector<uint8_t> // payload
1484 >
1485 ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
1486 uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
1487{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001488 size_t fruCount = 0;
Willy Tude54f482021-01-26 15:59:09 -08001489 // reservation required for partial reads with non zero offset into
1490 // record
1491 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
1492 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001493 phosphor::logging::log<phosphor::logging::level::ERR>(
1494 "ipmiStorageGetSDR: responseInvalidReservationId");
Willy Tude54f482021-01-26 15:59:09 -08001495 return ipmi::responseInvalidReservationId();
1496 }
Willy Tude54f482021-01-26 15:59:09 -08001497 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1498 if (ret != ipmi::ccSuccess)
1499 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001500 phosphor::logging::log<phosphor::logging::level::ERR>(
1501 "ipmiStorageGetSDR: getFruSdrCount error");
Willy Tude54f482021-01-26 15:59:09 -08001502 return ipmi::response(ret);
1503 }
1504
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001505 auto& sensorTree = getSensorTree();
Willy Tude54f482021-01-26 15:59:09 -08001506 size_t lastRecord =
1507 sensorTree.size() + fruCount + ipmi::storage::type12Count - 1;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001508 uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
1509
1510 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08001511 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001512 phosphor::logging::log<phosphor::logging::level::ERR>(
1513 "ipmiStorageGetSDR: getSensorSubtree error");
1514 return ipmi::responseResponseError();
Willy Tude54f482021-01-26 15:59:09 -08001515 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001516
1517 std::vector<uint8_t> record;
1518 if (getSensorDataRecord(ctx, record, recordID))
Willy Tude54f482021-01-26 15:59:09 -08001519 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001520 phosphor::logging::log<phosphor::logging::level::ERR>(
1521 "ipmiStorageGetSDR: fail to get SDR");
Willy Tude54f482021-01-26 15:59:09 -08001522 return ipmi::responseInvalidFieldRequest();
1523 }
Willy Tude54f482021-01-26 15:59:09 -08001524 get_sdr::SensorDataRecordHeader* hdr =
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001525 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
Willy Tude54f482021-01-26 15:59:09 -08001526 if (!hdr)
1527 {
1528 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001529 "ipmiStorageGetSDR: record header is null");
1530 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08001531 }
1532
1533 size_t sdrLength =
1534 sizeof(get_sdr::SensorDataRecordHeader) + hdr->record_length;
1535 if (sdrLength < (offset + bytesToRead))
1536 {
1537 bytesToRead = sdrLength - offset;
1538 }
1539
1540 uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
1541 if (!respStart)
1542 {
1543 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001544 "ipmiStorageGetSDR: record is null");
1545 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08001546 }
1547
1548 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001549
Willy Tude54f482021-01-26 15:59:09 -08001550 return ipmi::responseSuccess(nextRecordId, recordData);
1551}
1552/* end storage commands */
1553
1554void registerSensorFunctions()
1555{
1556 // <Platform Event>
1557 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1558 ipmi::sensor_event::cmdPlatformEvent,
1559 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
1560
1561 // <Get Sensor Reading>
1562 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1563 ipmi::sensor_event::cmdGetSensorReading,
1564 ipmi::Privilege::User, ipmiSenGetSensorReading);
1565
1566 // <Get Sensor Threshold>
1567 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1568 ipmi::sensor_event::cmdGetSensorThreshold,
1569 ipmi::Privilege::User, ipmiSenGetSensorThresholds);
1570
1571 // <Set Sensor Threshold>
1572 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1573 ipmi::sensor_event::cmdSetSensorThreshold,
1574 ipmi::Privilege::Operator,
1575 ipmiSenSetSensorThresholds);
1576
1577 // <Get Sensor Event Enable>
1578 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1579 ipmi::sensor_event::cmdGetSensorEventEnable,
1580 ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
1581
1582 // <Get Sensor Event Status>
1583 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1584 ipmi::sensor_event::cmdGetSensorEventStatus,
1585 ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
1586
1587 // register all storage commands for both Sensor and Storage command
1588 // versions
1589
1590 // <Get SDR Repository Info>
1591 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1592 ipmi::storage::cmdGetSdrRepositoryInfo,
1593 ipmi::Privilege::User,
1594 ipmiStorageGetSDRRepositoryInfo);
1595
1596 // <Get Device SDR Info>
1597 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1598 ipmi::sensor_event::cmdGetDeviceSdrInfo,
1599 ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
1600
1601 // <Get SDR Allocation Info>
1602 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1603 ipmi::storage::cmdGetSdrRepositoryAllocInfo,
1604 ipmi::Privilege::User,
1605 ipmiStorageGetSDRAllocationInfo);
1606
1607 // <Reserve SDR Repo>
1608 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1609 ipmi::sensor_event::cmdReserveDeviceSdrRepository,
1610 ipmi::Privilege::User, ipmiStorageReserveSDR);
1611
1612 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1613 ipmi::storage::cmdReserveSdrRepository,
1614 ipmi::Privilege::User, ipmiStorageReserveSDR);
1615
1616 // <Get Sdr>
1617 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1618 ipmi::sensor_event::cmdGetDeviceSdr,
1619 ipmi::Privilege::User, ipmiStorageGetSDR);
1620
1621 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1622 ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
1623 ipmiStorageGetSDR);
1624}
1625} // namespace ipmi