blob: aea03abc3604b7cd44e9316b76e6e4b67787c07c [file] [log] [blame]
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001/*
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 <host-ipmid/ipmid-api.h>
18
19#include <boost/algorithm/string.hpp>
20#include <boost/container/flat_map.hpp>
21#include <chrono>
22#include <cmath>
23#include <commandutils.hpp>
24#include <iostream>
25#include <phosphor-ipmi-host/utils.hpp>
26#include <phosphor-logging/log.hpp>
27#include <sdbusplus/bus.hpp>
28#include <sdrutils.hpp>
29#include <sensorcommands.hpp>
30#include <sensorutils.hpp>
31#include <storagecommands.hpp>
32#include <string>
33
34namespace ipmi
35{
36using ManagedObjectType =
37 std::map<sdbusplus::message::object_path,
38 std::map<std::string, std::map<std::string, DbusVariant>>>;
39
40using SensorMap = std::map<std::string, std::map<std::string, DbusVariant>>;
41
42static constexpr int sensorListUpdatePeriod = 10;
43static constexpr int sensorMapUpdatePeriod = 2;
44
45constexpr size_t maxSDRTotalSize =
46 76; // Largest SDR Record Size (type 01) + SDR Overheader Size
47constexpr static const uint32_t noTimestamp = 0xFFFFFFFF;
48
49static uint16_t sdrReservationID;
50static uint32_t sdrLastAdd = noTimestamp;
51static uint32_t sdrLastRemove = noTimestamp;
52
Richard Marian Thomaiyar01fbcb52018-11-19 22:04:34 +053053SensorSubTree sensorTree;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070054static boost::container::flat_map<std::string, ManagedObjectType> SensorCache;
55
Jason M. Bills17add592018-11-12 14:30:12 -080056// Specify the comparison required to sort and find char* map objects
57struct CmpStr
58{
59 bool operator()(const char *a, const char *b) const
60 {
61 return std::strcmp(a, b) < 0;
62 }
63};
64const static boost::container::flat_map<const char *, SensorUnits, CmpStr>
65 sensorUnits{{{"temperature", SensorUnits::degreesC},
66 {"voltage", SensorUnits::volts},
67 {"current", SensorUnits::amps},
68 {"fan_tach", SensorUnits::rpm},
69 {"power", SensorUnits::watts}}};
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070070
71void registerSensorFunctions() __attribute__((constructor));
72static sdbusplus::bus::bus dbus(ipmid_get_sd_bus_connection());
73
74static sdbusplus::bus::match::match sensorAdded(
75 dbus,
76 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
77 "sensors/'",
78 [](sdbusplus::message::message &m) {
79 sensorTree.clear();
80 sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>(
81 std::chrono::system_clock::now().time_since_epoch())
82 .count();
83 });
84
85static sdbusplus::bus::match::match sensorRemoved(
86 dbus,
87 "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/"
88 "sensors/'",
89 [](sdbusplus::message::message &m) {
90 sensorTree.clear();
91 sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>(
92 std::chrono::system_clock::now().time_since_epoch())
93 .count();
94 });
95
96static void
97 getSensorMaxMin(const std::map<std::string, DbusVariant> &sensorPropertyMap,
98 double &max, double &min)
99{
100 auto maxMap = sensorPropertyMap.find("MaxValue");
101 auto minMap = sensorPropertyMap.find("MinValue");
102 max = 127;
103 min = -128;
104
105 if (maxMap != sensorPropertyMap.end())
106 {
107 max = apply_visitor(VariantToDoubleVisitor(), maxMap->second);
108 }
109 if (minMap != sensorPropertyMap.end())
110 {
111 min = apply_visitor(VariantToDoubleVisitor(), minMap->second);
112 }
113}
114
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700115static bool getSensorMap(std::string sensorConnection, std::string sensorPath,
116 SensorMap &sensorMap)
117{
118 static boost::container::flat_map<
119 std::string, std::chrono::time_point<std::chrono::steady_clock>>
120 updateTimeMap;
121
122 auto updateFind = updateTimeMap.find(sensorConnection);
123 auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
124 if (updateFind != updateTimeMap.end())
125 {
126 lastUpdate = updateFind->second;
127 }
128
129 auto now = std::chrono::steady_clock::now();
130
131 if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
132 .count() > sensorMapUpdatePeriod)
133 {
134 updateTimeMap[sensorConnection] = now;
135
136 auto managedObj = dbus.new_method_call(
137 sensorConnection.c_str(), "/", "org.freedesktop.DBus.ObjectManager",
138 "GetManagedObjects");
139
140 ManagedObjectType managedObjects;
141 try
142 {
143 auto reply = dbus.call(managedObj);
144 reply.read(managedObjects);
145 }
146 catch (sdbusplus::exception_t &)
147 {
148 phosphor::logging::log<phosphor::logging::level::ERR>(
149 "Error getting managed objects from connection",
150 phosphor::logging::entry("CONNECTION=%s",
151 sensorConnection.c_str()));
152 return false;
153 }
154
155 SensorCache[sensorConnection] = managedObjects;
156 }
157 auto connection = SensorCache.find(sensorConnection);
158 if (connection == SensorCache.end())
159 {
160 return false;
161 }
162 auto path = connection->second.find(sensorPath);
163 if (path == connection->second.end())
164 {
165 return false;
166 }
167 sensorMap = path->second;
168
169 return true;
170}
171
172/* sensor commands */
173ipmi_ret_t ipmiSensorWildcardHandler(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
174 ipmi_request_t request,
175 ipmi_response_t response,
176 ipmi_data_len_t dataLen,
177 ipmi_context_t context)
178{
179 *dataLen = 0;
180 printCommand(+netfn, +cmd);
181 return IPMI_CC_INVALID;
182}
183
184ipmi_ret_t ipmiSenGetSensorReading(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
185 ipmi_request_t request,
186 ipmi_response_t response,
187 ipmi_data_len_t dataLen,
188 ipmi_context_t context)
189{
190 if (*dataLen != 1)
191 {
192 *dataLen = 0;
193 return IPMI_CC_REQ_DATA_LEN_INVALID;
194 }
195 *dataLen = 0; // default to 0 in case of an error
196
197 uint8_t sensnum = *(static_cast<uint8_t *>(request));
198
199 std::string connection;
200 std::string path;
201
202 auto status = getSensorConnection(sensnum, connection, path);
203 if (status)
204 {
205 return status;
206 }
207
208 SensorMap sensorMap;
209 if (!getSensorMap(connection, path, sensorMap))
210 {
211 return IPMI_CC_RESPONSE_ERROR;
212 }
213 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
214
215 if (sensorObject == sensorMap.end() ||
216 sensorObject->second.find("Value") == sensorObject->second.end())
217 {
218 return IPMI_CC_RESPONSE_ERROR;
219 }
220 auto &value = sensorObject->second["Value"];
221 double reading = apply_visitor(VariantToDoubleVisitor(), value);
222
223 double max;
224 double min;
225 getSensorMaxMin(sensorObject->second, max, min);
226
227 int16_t mValue = 0;
228 int16_t bValue = 0;
229 int8_t rExp = 0;
230 int8_t bExp = 0;
231 bool bSigned = false;
232
233 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
234 {
235 return IPMI_CC_RESPONSE_ERROR;
236 }
237
238 SensorReadingResp *msgReply = static_cast<SensorReadingResp *>(response);
239 *dataLen = sizeof(SensorReadingResp);
240
241 msgReply->value =
242 scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
243 msgReply->operation =
244 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
245 msgReply->indication[0] = 0; // ignore for non-threshold sensors
246 msgReply->indication[1] = 0;
247
248 return IPMI_CC_OK;
249}
250
251ipmi_ret_t ipmiSenSetSensorThresholds(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
252 ipmi_request_t request,
253 ipmi_response_t response,
254 ipmi_data_len_t dataLen,
255 ipmi_context_t context)
256{
257 if (*dataLen != 8)
258 {
259 *dataLen = 0;
260 return IPMI_CC_REQ_DATA_LEN_INVALID;
261 }
262 *dataLen = 0;
263
264 SensorThresholdReq *req = static_cast<SensorThresholdReq *>(request);
265
266 // upper two bits reserved
267 if (req->mask & 0xC0)
268 {
269 return IPMI_CC_INVALID_FIELD_REQUEST;
270 }
271
272 // lower nc and upper nc not suppported on any sensor
273 if ((req->mask & static_cast<uint8_t>(
274 SensorThresholdReqEnable::setLowerNonRecoverable)) ||
275 (req->mask & static_cast<uint8_t>(
276 SensorThresholdReqEnable::setUpperNonRecoverable)))
277 {
278 return IPMI_CC_INVALID_FIELD_REQUEST;
279 }
280
281 // if no bits are set in the mask, nothing to do
282 if (!(req->mask))
283 {
284 return IPMI_CC_OK;
285 }
286
287 std::string connection;
288 std::string path;
289
290 ipmi_ret_t status = getSensorConnection(req->sensorNum, connection, path);
291 if (status)
292 {
293 return status;
294 }
295 SensorMap sensorMap;
296 if (!getSensorMap(connection, path, sensorMap))
297 {
298 return IPMI_CC_RESPONSE_ERROR;
299 }
300
301 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
302
303 if (sensorObject == sensorMap.end())
304 {
305 return IPMI_CC_RESPONSE_ERROR;
306 }
307 double max = 0;
308 double min = 0;
309 getSensorMaxMin(sensorObject->second, max, min);
310
311 int16_t mValue = 0;
312 int16_t bValue = 0;
313 int8_t rExp = 0;
314 int8_t bExp = 0;
315 bool bSigned = false;
316
317 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
318 {
319 return IPMI_CC_RESPONSE_ERROR;
320 }
321
322 bool setLowerCritical =
323 req->mask &
324 static_cast<uint8_t>(SensorThresholdReqEnable::setLowerCritical);
325 bool setUpperCritical =
326 req->mask &
327 static_cast<uint8_t>(SensorThresholdReqEnable::setUpperCritical);
328
329 bool setLowerWarning =
330 req->mask &
331 static_cast<uint8_t>(SensorThresholdReqEnable::setLowerNonCritical);
332 bool setUpperWarning =
333 req->mask &
334 static_cast<uint8_t>(SensorThresholdReqEnable::setUpperNonCritical);
335
336 // store a vector of property name, value to set, and interface
337 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
338
339 // define the indexes of the tuple
340 constexpr uint8_t propertyName = 0;
341 constexpr uint8_t thresholdValue = 1;
342 constexpr uint8_t interface = 2;
343 // verifiy all needed fields are present
344 if (setLowerCritical || setUpperCritical)
345 {
346 auto findThreshold =
347 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
348 if (findThreshold == sensorMap.end())
349 {
350 return IPMI_CC_INVALID_FIELD_REQUEST;
351 }
352 if (setLowerCritical)
353 {
354 auto findLower = findThreshold->second.find("CriticalLow");
355 if (findLower == findThreshold->second.end())
356 {
357 return IPMI_CC_INVALID_FIELD_REQUEST;
358 }
359 thresholdsToSet.emplace_back("CriticalLow", req->lowerCritical,
360 findThreshold->first);
361 }
362 if (setUpperCritical)
363 {
364 auto findUpper = findThreshold->second.find("CriticalHigh");
365 if (findUpper == findThreshold->second.end())
366 {
367 return IPMI_CC_INVALID_FIELD_REQUEST;
368 }
369 thresholdsToSet.emplace_back("CriticalHigh", req->upperCritical,
370 findThreshold->first);
371 }
372 }
373 if (setLowerWarning || setUpperWarning)
374 {
375 auto findThreshold =
376 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
377 if (findThreshold == sensorMap.end())
378 {
379 return IPMI_CC_INVALID_FIELD_REQUEST;
380 }
381 if (setLowerWarning)
382 {
383 auto findLower = findThreshold->second.find("WarningLow");
384 if (findLower == findThreshold->second.end())
385 {
386 return IPMI_CC_INVALID_FIELD_REQUEST;
387 }
388 thresholdsToSet.emplace_back("WarningLow", req->lowerNonCritical,
389 findThreshold->first);
390 }
391 if (setUpperWarning)
392 {
393 auto findUpper = findThreshold->second.find("WarningHigh");
394 if (findUpper == findThreshold->second.end())
395 {
396 return IPMI_CC_INVALID_FIELD_REQUEST;
397 }
398 thresholdsToSet.emplace_back("WarningHigh", req->upperNonCritical,
399 findThreshold->first);
400 }
401 }
402
403 for (const auto &property : thresholdsToSet)
404 {
405 // from section 36.3 in the IPMI Spec, assume all linear
406 double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
407 (bValue * std::pow(10, bExp))) *
408 std::pow(10, rExp);
409 setDbusProperty(dbus, connection, path, std::get<interface>(property),
410 std::get<propertyName>(property),
411 ipmi::Value(valueToSet));
412 }
413
414 return IPMI_CC_OK;
415}
416
417ipmi_ret_t ipmiSenGetSensorThresholds(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
418 ipmi_request_t request,
419 ipmi_response_t response,
420 ipmi_data_len_t dataLen,
421 ipmi_context_t context)
422{
423 if (*dataLen != 1)
424 {
425 *dataLen = 0;
426 return IPMI_CC_REQ_DATA_LEN_INVALID;
427 }
428 *dataLen = 0; // default to 0 in case of an error
429
430 uint8_t sensnum = *(static_cast<uint8_t *>(request));
431
432 std::string connection;
433 std::string path;
434
435 auto status = getSensorConnection(sensnum, connection, path);
436 if (status)
437 {
438 return status;
439 }
440
441 SensorMap sensorMap;
442 if (!getSensorMap(connection, path, sensorMap))
443 {
444 return IPMI_CC_RESPONSE_ERROR;
445 }
446
447 // zero out response buff
448 auto responseClear = static_cast<uint8_t *>(response);
449 std::fill(responseClear, responseClear + sizeof(SensorThresholdResp), 0);
450
451 auto warningInterface =
452 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
453 auto criticalInterface =
454 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
455
456 if ((warningInterface != sensorMap.end()) ||
457 (criticalInterface != sensorMap.end()))
458 {
459 auto sensorPair = sensorMap.find("xyz.openbmc_project.Sensor.Value");
460
461 if (sensorPair == sensorMap.end())
462 {
463 // should not have been able to find a sensor not implementing
464 // the sensor object
465 return IPMI_CC_RESPONSE_ERROR;
466 }
467
468 double max;
469 double min;
470 getSensorMaxMin(sensorPair->second, max, min);
471
472 int16_t mValue = 0;
473 int16_t bValue = 0;
474 int8_t rExp = 0;
475 int8_t bExp = 0;
476 bool bSigned = false;
477
478 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
479 {
480 return IPMI_CC_RESPONSE_ERROR;
481 }
482
483 auto msgReply = static_cast<SensorThresholdResp *>(response);
484
485 if (warningInterface != sensorMap.end())
486 {
487 auto &warningMap = warningInterface->second;
488
489 auto warningHigh = warningMap.find("WarningHigh");
490 auto warningLow = warningMap.find("WarningLow");
491
492 if (warningHigh != warningMap.end())
493 {
494 msgReply->readable |=
495 1 << static_cast<int>(
496 IPMIhresholdRespBits::upperNonCritical);
497 double value = apply_visitor(VariantToDoubleVisitor(),
498 warningHigh->second);
499 msgReply->uppernc = scaleIPMIValueFromDouble(
500 value, mValue, rExp, bValue, bExp, bSigned);
501 }
502 if (warningLow != warningMap.end())
503 {
504 msgReply->readable |=
505 1 << static_cast<int>(
506 IPMIhresholdRespBits::lowerNonCritical);
507 double value =
508 apply_visitor(VariantToDoubleVisitor(), warningLow->second);
509 msgReply->lowernc = scaleIPMIValueFromDouble(
510 value, mValue, rExp, bValue, bExp, bSigned);
511 }
512 }
513 if (criticalInterface != sensorMap.end())
514 {
515 auto &criticalMap = criticalInterface->second;
516
517 auto criticalHigh = criticalMap.find("CriticalHigh");
518 auto criticalLow = criticalMap.find("CriticalLow");
519
520 if (criticalHigh != criticalMap.end())
521 {
522 msgReply->readable |=
523 1 << static_cast<int>(IPMIhresholdRespBits::upperCritical);
524 double value = apply_visitor(VariantToDoubleVisitor(),
525 criticalHigh->second);
526 msgReply->uppercritical = scaleIPMIValueFromDouble(
527 value, mValue, rExp, bValue, bExp, bSigned);
528 }
529 if (criticalLow != criticalMap.end())
530 {
531 msgReply->readable |=
532 1 << static_cast<int>(IPMIhresholdRespBits::lowerCritical);
533 double value = apply_visitor(VariantToDoubleVisitor(),
534 criticalLow->second);
535 msgReply->lowercritical = scaleIPMIValueFromDouble(
536 value, mValue, rExp, bValue, bExp, bSigned);
537 }
538 }
539 }
540
541 *dataLen = sizeof(SensorThresholdResp);
542 return IPMI_CC_OK;
543}
544
545ipmi_ret_t ipmiSenGetSensorEventEnable(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
546 ipmi_request_t request,
547 ipmi_response_t response,
548 ipmi_data_len_t dataLen,
549 ipmi_context_t context)
550{
551 if (*dataLen != 1)
552 {
553 *dataLen = 0;
554 return IPMI_CC_REQ_DATA_LEN_INVALID;
555 }
556 *dataLen = 0; // default to 0 in case of an error
557
558 uint8_t sensnum = *(static_cast<uint8_t *>(request));
559
560 std::string connection;
561 std::string path;
562
563 auto status = getSensorConnection(sensnum, connection, path);
564 if (status)
565 {
566 return status;
567 }
568
569 SensorMap sensorMap;
570 if (!getSensorMap(connection, path, sensorMap))
571 {
572 return IPMI_CC_RESPONSE_ERROR;
573 }
574
575 auto warningInterface =
576 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
577 auto criticalInterface =
578 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
579
580 if ((warningInterface != sensorMap.end()) ||
581 (criticalInterface != sensorMap.end()))
582 {
583 // zero out response buff
584 auto responseClear = static_cast<uint8_t *>(response);
585 std::fill(responseClear, responseClear + sizeof(SensorEventEnableResp),
586 0);
587
588 // assume all threshold sensors
589 auto resp = static_cast<SensorEventEnableResp *>(response);
590
591 resp->enabled = static_cast<uint8_t>(
592 IPMISensorEventEnableByte2::sensorScanningEnable);
593 if (warningInterface != sensorMap.end())
594 {
595 auto &warningMap = warningInterface->second;
596
597 auto warningHigh = warningMap.find("WarningHigh");
598 auto warningLow = warningMap.find("WarningLow");
599 if (warningHigh != warningMap.end())
600 {
601 resp->assertionEnabledLSB |= static_cast<uint8_t>(
602 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
603 resp->deassertionEnabledLSB |= static_cast<uint8_t>(
604 IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
605 }
606 if (warningLow != warningMap.end())
607 {
608 resp->assertionEnabledLSB |= static_cast<uint8_t>(
609 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
610 resp->deassertionEnabledLSB |= static_cast<uint8_t>(
611 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
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 resp->assertionEnabledMSB |= static_cast<uint8_t>(
624 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
625 resp->deassertionEnabledMSB |= static_cast<uint8_t>(
626 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
627 }
628 if (criticalLow != criticalMap.end())
629 {
630 resp->assertionEnabledLSB |= static_cast<uint8_t>(
631 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
632 resp->deassertionEnabledLSB |= static_cast<uint8_t>(
633 IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
634 }
635 }
636 *dataLen =
637 sizeof(SensorEventEnableResp); // todo only return needed bytes
638 }
639 // no thresholds enabled
640 else
641 {
642 *dataLen = 1;
643 auto resp = static_cast<uint8_t *>(response);
644 *resp = static_cast<uint8_t>(
645 IPMISensorEventEnableByte2::eventMessagesEnable);
646 *resp |= static_cast<uint8_t>(
647 IPMISensorEventEnableByte2::sensorScanningEnable);
648 }
649 return IPMI_CC_OK;
650}
651
652ipmi_ret_t ipmiSenGetSensorEventStatus(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
653 ipmi_request_t request,
654 ipmi_response_t response,
655 ipmi_data_len_t dataLen,
656 ipmi_context_t context)
657{
658 if (*dataLen != 1)
659 {
660 *dataLen = 0;
661 return IPMI_CC_REQ_DATA_LEN_INVALID;
662 }
663 *dataLen = 0; // default to 0 in case of an error
664
665 uint8_t sensnum = *(static_cast<uint8_t *>(request));
666
667 std::string connection;
668 std::string path;
669
670 auto status = getSensorConnection(sensnum, connection, path);
671 if (status)
672 {
673 return status;
674 }
675
676 SensorMap sensorMap;
677 if (!getSensorMap(connection, path, sensorMap))
678 {
679 return IPMI_CC_RESPONSE_ERROR;
680 }
681
682 auto warningInterface =
683 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
684 auto criticalInterface =
685 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
686
687 // zero out response buff
688 auto responseClear = static_cast<uint8_t *>(response);
689 std::fill(responseClear, responseClear + sizeof(SensorEventStatusResp), 0);
690 auto resp = static_cast<SensorEventStatusResp *>(response);
691 resp->enabled =
692 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
693
694 if ((warningInterface != sensorMap.end()) ||
695 (criticalInterface != sensorMap.end()))
696 {
697 resp->enabled = static_cast<uint8_t>(
698 IPMISensorEventEnableByte2::eventMessagesEnable);
699 if (warningInterface != sensorMap.end())
700 {
701 auto &warningMap = warningInterface->second;
702
703 auto warningHigh = warningMap.find("WarningAlarmHigh");
704 auto warningLow = warningMap.find("WarningAlarmLow");
705 auto warningHighAlarm = false;
706 auto warningLowAlarm = false;
707
708 if (warningHigh != warningMap.end())
709 {
710 warningHighAlarm = warningHigh->second.get<bool>();
711 }
712 if (warningLow != warningMap.end())
713 {
714 warningLowAlarm = warningLow->second.get<bool>();
715 }
716 if (warningHighAlarm)
717 {
718 resp->assertionsLSB |= static_cast<uint8_t>(
719 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
720 }
721 if (warningLowAlarm)
722 {
723 resp->assertionsLSB |= 1; // lower nc going low
724 }
725 }
726 if (criticalInterface != sensorMap.end())
727 {
728 auto &criticalMap = criticalInterface->second;
729
730 auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
731 auto criticalLow = criticalMap.find("CriticalAlarmLow");
732 auto criticalHighAlarm = false;
733 auto criticalLowAlarm = false;
734
735 if (criticalHigh != criticalMap.end())
736 {
737 criticalHighAlarm = criticalHigh->second.get<bool>();
738 }
739 if (criticalLow != criticalMap.end())
740 {
741 criticalLowAlarm = criticalLow->second.get<bool>();
742 }
743 if (criticalHighAlarm)
744 {
745 resp->assertionsMSB |= static_cast<uint8_t>(
746 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
747 }
748 if (criticalLowAlarm)
749 {
750 resp->assertionsLSB |= static_cast<uint8_t>(
751 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
752 }
753 }
754 *dataLen = sizeof(SensorEventStatusResp);
755 }
756
757 // no thresholds enabled, don't need assertionMSB
758 else
759 {
760 *dataLen = sizeof(SensorEventStatusResp) - 1;
761 }
762
763 return IPMI_CC_OK;
764}
765
766/* end sensor commands */
767
768/* storage commands */
769
770ipmi_ret_t ipmiStorageGetSDRRepositoryInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
771 ipmi_request_t request,
772 ipmi_response_t response,
773 ipmi_data_len_t dataLen,
774 ipmi_context_t context)
775{
776 printCommand(+netfn, +cmd);
777
778 if (*dataLen)
779 {
780 *dataLen = 0;
781 return IPMI_CC_REQ_DATA_LEN_INVALID;
782 }
783 *dataLen = 0; // default to 0 in case of an error
784
785 if (sensorTree.empty() && !getSensorSubtree(sensorTree))
786 {
787 return IPMI_CC_RESPONSE_ERROR;
788 }
789
790 // zero out response buff
791 auto responseClear = static_cast<uint8_t *>(response);
792 std::fill(responseClear, responseClear + sizeof(GetSDRInfoResp), 0);
793
794 auto resp = static_cast<GetSDRInfoResp *>(response);
795 resp->sdrVersion = ipmiSdrVersion;
796 uint16_t recordCount = sensorTree.size();
797
798 // todo: for now, sdr count is number of sensors
799 resp->recordCountLS = recordCount & 0xFF;
800 resp->recordCountMS = recordCount >> 8;
801
802 // free space unspcified
803 resp->freeSpace[0] = 0xFF;
804 resp->freeSpace[1] = 0xFF;
805
806 resp->mostRecentAddition = sdrLastAdd;
807 resp->mostRecentErase = sdrLastRemove;
808 resp->operationSupport = static_cast<uint8_t>(
809 SdrRepositoryInfoOps::overflow); // write not supported
810 resp->operationSupport |=
811 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
812 resp->operationSupport |= static_cast<uint8_t>(
813 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
814 *dataLen = sizeof(GetSDRInfoResp);
815 return IPMI_CC_OK;
816}
817
818ipmi_ret_t ipmiStorageGetSDRAllocationInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
819 ipmi_request_t request,
820 ipmi_response_t response,
821 ipmi_data_len_t dataLen,
822 ipmi_context_t context)
823{
824 if (*dataLen)
825 {
826 *dataLen = 0;
827 return IPMI_CC_REQ_DATA_LEN_INVALID;
828 }
829 *dataLen = 0; // default to 0 in case of an error
830 GetAllocInfoResp *resp = static_cast<GetAllocInfoResp *>(response);
831
832 // 0000h unspecified number of alloc units
833 resp->allocUnitsLSB = 0;
834 resp->allocUnitsMSB = 0;
835
836 // max unit size is size of max record
837 resp->allocUnitSizeLSB = maxSDRTotalSize & 0xFF;
838 resp->allocUnitSizeMSB = maxSDRTotalSize >> 8;
839 // read only sdr, no free alloc blocks
840 resp->allocUnitFreeLSB = 0;
841 resp->allocUnitFreeMSB = 0;
842 resp->allocUnitLargestFreeLSB = 0;
843 resp->allocUnitLargestFreeMSB = 0;
844 // only allow one block at a time
845 resp->maxRecordSize = 1;
846
847 *dataLen = sizeof(GetAllocInfoResp);
848
849 return IPMI_CC_OK;
850}
851
852ipmi_ret_t ipmiStorageReserveSDR(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
853 ipmi_request_t request,
854 ipmi_response_t response,
855 ipmi_data_len_t dataLen,
856 ipmi_context_t context)
857{
858 printCommand(+netfn, +cmd);
859
860 if (*dataLen)
861 {
862 *dataLen = 0;
863 return IPMI_CC_REQ_DATA_LEN_INVALID;
864 }
865 *dataLen = 0; // default to 0 in case of an error
866 sdrReservationID++;
867 *dataLen = 2;
868 auto resp = static_cast<uint8_t *>(response);
869 resp[0] = sdrReservationID & 0xFF;
870 resp[1] = sdrReservationID >> 8;
871
872 return IPMI_CC_OK;
873}
874
875ipmi_ret_t ipmiStorageGetSDR(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
876 ipmi_request_t request, ipmi_response_t response,
877 ipmi_data_len_t dataLen, ipmi_context_t context)
878{
879 printCommand(+netfn, +cmd);
880
881 if (*dataLen != 6)
882 {
883 *dataLen = 0;
884 return IPMI_CC_REQ_DATA_LEN_INVALID;
885 }
886 auto requestedSize = *dataLen;
887 *dataLen = 0; // default to 0 in case of an error
888
889 constexpr uint16_t lastRecordIndex = 0xFFFF;
890 auto req = static_cast<GetSDRReq *>(request);
891
892 // reservation required for partial reads with non zero offset into
893 // record
894 if (req->reservationID != sdrReservationID && req->offset)
895 {
896 return IPMI_CC_INVALID_RESERVATION_ID;
897 }
898
899 if (sensorTree.empty() && !getSensorSubtree(sensorTree))
900 {
901 return IPMI_CC_RESPONSE_ERROR;
902 }
903
904 size_t fruCount = 0;
905 ipmi_ret_t ret = ipmi::storage::getFruSdrCount(fruCount);
906 if (ret != IPMI_CC_OK)
907 {
908 return ret;
909 }
910
911 size_t lastRecord = sensorTree.size() + fruCount - 1;
912 if (req->recordID == lastRecordIndex)
913 {
914 req->recordID = lastRecord;
915 }
916 if (req->recordID > lastRecord)
917 {
918 return IPMI_CC_INVALID_FIELD_REQUEST;
919 }
920
921 uint16_t nextRecord =
922 lastRecord > (req->recordID + 1) ? req->recordID + 1 : 0XFFFF;
923
924 auto responseClear = static_cast<uint8_t *>(response);
925 std::fill(responseClear, responseClear + requestedSize, 0);
926
927 auto resp = static_cast<get_sdr::GetSdrResp *>(response);
928 resp->next_record_id_lsb = nextRecord & 0xFF;
929 resp->next_record_id_msb = nextRecord >> 8;
930
931 if (req->recordID >= sensorTree.size())
932 {
933 size_t fruIndex = req->recordID - sensorTree.size();
934 if (fruIndex >= fruCount)
935 {
936 return IPMI_CC_INVALID_FIELD_REQUEST;
937 }
938 get_sdr::SensorDataFruRecord data;
939 if (req->offset > sizeof(data))
940 {
941 return IPMI_CC_INVALID_FIELD_REQUEST;
942 }
943 ret = ipmi::storage::getFruSdrs(fruIndex, data);
944 if (ret != IPMI_CC_OK)
945 {
946 return ret;
947 }
948 data.header.record_id_msb = req->recordID << 8;
949 data.header.record_id_lsb = req->recordID & 0xFF;
950 if (sizeof(data) < (req->offset + req->bytesToRead))
951 {
952 req->bytesToRead = sizeof(data) - req->offset;
953 }
954 *dataLen = req->bytesToRead + 2; // next record
955 std::memcpy(&resp->record_data, (char *)&data + req->offset,
956 req->bytesToRead);
957 return IPMI_CC_OK;
958 }
959
960 std::string connection;
961 std::string path;
962 uint16_t sensorIndex = req->recordID;
963 for (const auto &sensor : sensorTree)
964 {
965 if (sensorIndex-- == 0)
966 {
967 if (!sensor.second.size())
968 {
969 return IPMI_CC_RESPONSE_ERROR;
970 }
971 connection = sensor.second.begin()->first;
972 path = sensor.first;
973 break;
974 }
975 }
976
977 SensorMap sensorMap;
978 if (!getSensorMap(connection, path, sensorMap))
979 {
980 return IPMI_CC_RESPONSE_ERROR;
981 }
982 uint8_t sensornumber = (req->recordID & 0xFF);
983 get_sdr::SensorDataFullRecord record = {0};
984
985 record.header.record_id_msb = req->recordID << 8;
986 record.header.record_id_lsb = req->recordID & 0xFF;
987 record.header.sdr_version = ipmiSdrVersion;
988 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
989 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
990 sizeof(get_sdr::SensorDataRecordHeader);
991 record.key.owner_id = 0x20;
992 record.key.owner_lun = 0x0;
993 record.key.sensor_number = sensornumber;
994
995 record.body.entity_id = 0x0;
996 record.body.entity_instance = 0x01;
997 record.body.sensor_capabilities = 0x60; // auto rearm - todo hysteresis
998 record.body.sensor_type = getSensorTypeFromPath(path);
999 std::string type = getSensorTypeStringFromPath(path);
1000 auto typeCstr = type.c_str();
1001 auto findUnits = sensorUnits.find(typeCstr);
1002 if (findUnits != sensorUnits.end())
1003 {
1004 record.body.sensor_units_2_base =
1005 static_cast<uint8_t>(findUnits->second);
1006 } // else default 0x0 unspecified
1007
1008 record.body.event_reading_type = getSensorEventTypeFromPath(path);
1009
1010 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
1011 if (sensorObject == sensorMap.end())
1012 {
1013 return IPMI_CC_RESPONSE_ERROR;
1014 }
1015
1016 auto maxObject = sensorObject->second.find("MaxValue");
1017 auto minObject = sensorObject->second.find("MinValue");
1018 double max = 128;
1019 double min = -127;
1020 if (maxObject != sensorObject->second.end())
1021 {
1022 max = apply_visitor(VariantToDoubleVisitor(), maxObject->second);
1023 }
1024
1025 if (minObject != sensorObject->second.end())
1026 {
1027 min = apply_visitor(VariantToDoubleVisitor(), minObject->second);
1028 }
1029
1030 int16_t mValue;
1031 int8_t rExp;
1032 int16_t bValue;
1033 int8_t bExp;
1034 bool bSigned;
1035
1036 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1037 {
1038 return IPMI_CC_RESPONSE_ERROR;
1039 }
1040
1041 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1042 record.body.m_lsb = mValue & 0xFF;
1043
1044 // move the smallest bit of the MSB into place (bit 9)
1045 // the MSbs are bits 7:8 in m_msb_and_tolerance
1046 uint8_t mMsb = (mValue & (1 << 8)) > 0 ? (1 << 6) : 0;
1047
1048 // assign the negative
1049 if (mValue < 0)
1050 {
1051 mMsb |= (1 << 7);
1052 }
1053 record.body.m_msb_and_tolerance = mMsb;
1054
1055 record.body.b_lsb = bValue & 0xFF;
1056
1057 // move the smallest bit of the MSB into place
1058 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1059 uint8_t bMsb = (bValue & (1 << 8)) > 0 ? (1 << 6) : 0;
1060
1061 // assign the negative
1062 if (bValue < 0)
1063 {
1064 bMsb |= (1 << 7);
1065 }
1066 record.body.b_msb_and_accuracy_lsb = bMsb;
1067
1068 record.body.r_b_exponents = bExp & 0x7;
1069 if (bExp < 0)
1070 {
1071 record.body.r_b_exponents |= 1 << 3;
1072 }
1073 record.body.r_b_exponents = (rExp & 0x7) << 4;
1074 if (rExp < 0)
1075 {
1076 record.body.r_b_exponents |= 1 << 7;
1077 }
1078
1079 // todo fill out rest of units
1080 if (bSigned)
1081 {
1082 record.body.sensor_units_1 = 1 << 7;
1083 }
1084
1085 // populate sensor name from path
1086 std::string name;
1087 size_t nameStart = path.rfind("/");
1088 if (nameStart != std::string::npos)
1089 {
1090 name = path.substr(nameStart + 1, std::string::npos - nameStart);
1091 }
1092
1093 std::replace(name.begin(), name.end(), '_', ' ');
1094 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
1095 {
1096 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
1097 }
1098 record.body.id_string_info = name.size();
1099 std::strncpy(record.body.id_string, name.c_str(),
1100 sizeof(record.body.id_string));
1101
1102 if (sizeof(get_sdr::SensorDataFullRecord) <
1103 (req->offset + req->bytesToRead))
1104 {
1105 req->bytesToRead = sizeof(get_sdr::SensorDataFullRecord) - req->offset;
1106 }
1107
1108 *dataLen =
1109 2 + req->bytesToRead; // bytesToRead + MSB and LSB of next record id
1110
1111 std::memcpy(&resp->record_data, (char *)&record + req->offset,
1112 req->bytesToRead);
1113
1114 return IPMI_CC_OK;
1115}
1116/* end storage commands */
1117
1118void registerSensorFunctions()
1119{
1120 // get firmware version information
1121 ipmiPrintAndRegister(NETFUN_SENSOR, IPMI_CMD_WILDCARD, nullptr,
1122 ipmiSensorWildcardHandler, PRIVILEGE_USER);
1123
1124 // <Get Sensor Type>
1125 ipmiPrintAndRegister(
1126 NETFUN_SENSOR,
1127 static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdGetSensorType),
1128 nullptr, ipmiSensorWildcardHandler, PRIVILEGE_USER);
1129
1130 // <Set Sensor Reading and Event Status>
1131 ipmiPrintAndRegister(
1132 NETFUN_SENSOR,
1133 static_cast<ipmi_cmd_t>(
1134 IPMINetfnSensorCmds::ipmiCmdSetSensorReadingAndEventStatus),
1135 nullptr, ipmiSensorWildcardHandler, PRIVILEGE_OPERATOR);
1136
1137 // <Get Sensor Reading>
1138 ipmiPrintAndRegister(
1139 NETFUN_SENSOR,
1140 static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdGetSensorReading),
1141 nullptr, ipmiSenGetSensorReading, PRIVILEGE_USER);
1142
1143 // <Get Sensor Threshold>
1144 ipmiPrintAndRegister(
1145 NETFUN_SENSOR,
1146 static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdGetSensorThreshold),
1147 nullptr, ipmiSenGetSensorThresholds, PRIVILEGE_USER);
1148
1149 ipmiPrintAndRegister(
1150 NETFUN_SENSOR,
1151 static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdSetSensorThreshold),
1152 nullptr, ipmiSenSetSensorThresholds, PRIVILEGE_OPERATOR);
1153
1154 // <Get Sensor Event Enable>
1155 ipmiPrintAndRegister(NETFUN_SENSOR,
1156 static_cast<ipmi_cmd_t>(
1157 IPMINetfnSensorCmds::ipmiCmdGetSensorEventEnable),
1158 nullptr, ipmiSenGetSensorEventEnable, PRIVILEGE_USER);
1159
1160 // <Get Sensor Event Status>
1161 ipmiPrintAndRegister(NETFUN_SENSOR,
1162 static_cast<ipmi_cmd_t>(
1163 IPMINetfnSensorCmds::ipmiCmdGetSensorEventStatus),
1164 nullptr, ipmiSenGetSensorEventStatus, PRIVILEGE_USER);
1165
1166 // register all storage commands for both Sensor and Storage command
1167 // versions
1168
1169 // <Get SDR Repository Info>
1170 ipmiPrintAndRegister(
1171 NETFUN_STORAGE,
1172 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdGetRepositoryInfo),
1173 nullptr, ipmiStorageGetSDRRepositoryInfo, PRIVILEGE_USER);
1174
1175 // <Get SDR Allocation Info>
1176 ipmiPrintAndRegister(NETFUN_STORAGE,
1177 static_cast<ipmi_cmd_t>(
1178 IPMINetfnStorageCmds::ipmiCmdGetSDRAllocationInfo),
1179 nullptr, ipmiStorageGetSDRAllocationInfo,
1180 PRIVILEGE_USER);
1181
1182 // <Reserve SDR Repo>
1183 ipmiPrintAndRegister(NETFUN_SENSOR,
1184 static_cast<ipmi_cmd_t>(
1185 IPMINetfnSensorCmds::ipmiCmdReserveDeviceSDRRepo),
1186 nullptr, ipmiStorageReserveSDR, PRIVILEGE_USER);
1187
1188 ipmiPrintAndRegister(
1189 NETFUN_STORAGE,
1190 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdReserveSDR),
1191 nullptr, ipmiStorageReserveSDR, PRIVILEGE_USER);
1192
1193 // <Get Sdr>
1194 ipmiPrintAndRegister(
1195 NETFUN_SENSOR,
1196 static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdGetDeviceSDR),
1197 nullptr, ipmiStorageGetSDR, PRIVILEGE_USER);
1198
1199 ipmiPrintAndRegister(
1200 NETFUN_STORAGE,
1201 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdGetSDR), nullptr,
1202 ipmiStorageGetSDR, PRIVILEGE_USER);
1203 return;
1204}
1205} // namespace ipmi