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