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