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