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