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