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