blob: 16f343829cedb9de587204f2e4a7b4ddc7c532b6 [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++;
872 *dataLen = 2;
873 auto resp = static_cast<uint8_t *>(response);
874 resp[0] = sdrReservationID & 0xFF;
875 resp[1] = sdrReservationID >> 8;
876
877 return IPMI_CC_OK;
878}
879
880ipmi_ret_t ipmiStorageGetSDR(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
881 ipmi_request_t request, ipmi_response_t response,
882 ipmi_data_len_t dataLen, ipmi_context_t context)
883{
884 printCommand(+netfn, +cmd);
885
886 if (*dataLen != 6)
887 {
888 *dataLen = 0;
889 return IPMI_CC_REQ_DATA_LEN_INVALID;
890 }
891 auto requestedSize = *dataLen;
892 *dataLen = 0; // default to 0 in case of an error
893
894 constexpr uint16_t lastRecordIndex = 0xFFFF;
895 auto req = static_cast<GetSDRReq *>(request);
896
897 // reservation required for partial reads with non zero offset into
898 // record
899 if (req->reservationID != sdrReservationID && req->offset)
900 {
901 return IPMI_CC_INVALID_RESERVATION_ID;
902 }
903
904 if (sensorTree.empty() && !getSensorSubtree(sensorTree))
905 {
906 return IPMI_CC_RESPONSE_ERROR;
907 }
908
909 size_t fruCount = 0;
910 ipmi_ret_t ret = ipmi::storage::getFruSdrCount(fruCount);
911 if (ret != IPMI_CC_OK)
912 {
913 return ret;
914 }
915
916 size_t lastRecord = sensorTree.size() + fruCount - 1;
917 if (req->recordID == lastRecordIndex)
918 {
919 req->recordID = lastRecord;
920 }
921 if (req->recordID > lastRecord)
922 {
923 return IPMI_CC_INVALID_FIELD_REQUEST;
924 }
925
926 uint16_t nextRecord =
927 lastRecord > (req->recordID + 1) ? req->recordID + 1 : 0XFFFF;
928
929 auto responseClear = static_cast<uint8_t *>(response);
930 std::fill(responseClear, responseClear + requestedSize, 0);
931
932 auto resp = static_cast<get_sdr::GetSdrResp *>(response);
933 resp->next_record_id_lsb = nextRecord & 0xFF;
934 resp->next_record_id_msb = nextRecord >> 8;
935
936 if (req->recordID >= sensorTree.size())
937 {
938 size_t fruIndex = req->recordID - sensorTree.size();
939 if (fruIndex >= fruCount)
940 {
941 return IPMI_CC_INVALID_FIELD_REQUEST;
942 }
943 get_sdr::SensorDataFruRecord data;
944 if (req->offset > sizeof(data))
945 {
946 return IPMI_CC_INVALID_FIELD_REQUEST;
947 }
948 ret = ipmi::storage::getFruSdrs(fruIndex, data);
949 if (ret != IPMI_CC_OK)
950 {
951 return ret;
952 }
953 data.header.record_id_msb = req->recordID << 8;
954 data.header.record_id_lsb = req->recordID & 0xFF;
955 if (sizeof(data) < (req->offset + req->bytesToRead))
956 {
957 req->bytesToRead = sizeof(data) - req->offset;
958 }
959 *dataLen = req->bytesToRead + 2; // next record
960 std::memcpy(&resp->record_data, (char *)&data + req->offset,
961 req->bytesToRead);
962 return IPMI_CC_OK;
963 }
964
965 std::string connection;
966 std::string path;
967 uint16_t sensorIndex = req->recordID;
968 for (const auto &sensor : sensorTree)
969 {
970 if (sensorIndex-- == 0)
971 {
972 if (!sensor.second.size())
973 {
974 return IPMI_CC_RESPONSE_ERROR;
975 }
976 connection = sensor.second.begin()->first;
977 path = sensor.first;
978 break;
979 }
980 }
981
982 SensorMap sensorMap;
983 if (!getSensorMap(connection, path, sensorMap))
984 {
985 return IPMI_CC_RESPONSE_ERROR;
986 }
987 uint8_t sensornumber = (req->recordID & 0xFF);
988 get_sdr::SensorDataFullRecord record = {0};
989
990 record.header.record_id_msb = req->recordID << 8;
991 record.header.record_id_lsb = req->recordID & 0xFF;
992 record.header.sdr_version = ipmiSdrVersion;
993 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
994 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
995 sizeof(get_sdr::SensorDataRecordHeader);
996 record.key.owner_id = 0x20;
997 record.key.owner_lun = 0x0;
998 record.key.sensor_number = sensornumber;
999
1000 record.body.entity_id = 0x0;
1001 record.body.entity_instance = 0x01;
1002 record.body.sensor_capabilities = 0x60; // auto rearm - todo hysteresis
1003 record.body.sensor_type = getSensorTypeFromPath(path);
1004 std::string type = getSensorTypeStringFromPath(path);
1005 auto typeCstr = type.c_str();
1006 auto findUnits = sensorUnits.find(typeCstr);
1007 if (findUnits != sensorUnits.end())
1008 {
1009 record.body.sensor_units_2_base =
1010 static_cast<uint8_t>(findUnits->second);
1011 } // else default 0x0 unspecified
1012
1013 record.body.event_reading_type = getSensorEventTypeFromPath(path);
1014
1015 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
1016 if (sensorObject == sensorMap.end())
1017 {
1018 return IPMI_CC_RESPONSE_ERROR;
1019 }
1020
1021 auto maxObject = sensorObject->second.find("MaxValue");
1022 auto minObject = sensorObject->second.find("MinValue");
1023 double max = 128;
1024 double min = -127;
1025 if (maxObject != sensorObject->second.end())
1026 {
James Feist14fde842018-12-06 10:19:40 -08001027 max = variant_ns::visit(VariantToDoubleVisitor(), maxObject->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001028 }
1029
1030 if (minObject != sensorObject->second.end())
1031 {
James Feist14fde842018-12-06 10:19:40 -08001032 min = variant_ns::visit(VariantToDoubleVisitor(), minObject->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001033 }
1034
1035 int16_t mValue;
1036 int8_t rExp;
1037 int16_t bValue;
1038 int8_t bExp;
1039 bool bSigned;
1040
1041 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1042 {
1043 return IPMI_CC_RESPONSE_ERROR;
1044 }
1045
1046 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1047 record.body.m_lsb = mValue & 0xFF;
1048
1049 // move the smallest bit of the MSB into place (bit 9)
1050 // the MSbs are bits 7:8 in m_msb_and_tolerance
1051 uint8_t mMsb = (mValue & (1 << 8)) > 0 ? (1 << 6) : 0;
1052
1053 // assign the negative
1054 if (mValue < 0)
1055 {
1056 mMsb |= (1 << 7);
1057 }
1058 record.body.m_msb_and_tolerance = mMsb;
1059
1060 record.body.b_lsb = bValue & 0xFF;
1061
1062 // move the smallest bit of the MSB into place
1063 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1064 uint8_t bMsb = (bValue & (1 << 8)) > 0 ? (1 << 6) : 0;
1065
1066 // assign the negative
1067 if (bValue < 0)
1068 {
1069 bMsb |= (1 << 7);
1070 }
1071 record.body.b_msb_and_accuracy_lsb = bMsb;
1072
1073 record.body.r_b_exponents = bExp & 0x7;
1074 if (bExp < 0)
1075 {
1076 record.body.r_b_exponents |= 1 << 3;
1077 }
1078 record.body.r_b_exponents = (rExp & 0x7) << 4;
1079 if (rExp < 0)
1080 {
1081 record.body.r_b_exponents |= 1 << 7;
1082 }
1083
1084 // todo fill out rest of units
1085 if (bSigned)
1086 {
1087 record.body.sensor_units_1 = 1 << 7;
1088 }
1089
1090 // populate sensor name from path
1091 std::string name;
1092 size_t nameStart = path.rfind("/");
1093 if (nameStart != std::string::npos)
1094 {
1095 name = path.substr(nameStart + 1, std::string::npos - nameStart);
1096 }
1097
1098 std::replace(name.begin(), name.end(), '_', ' ');
1099 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
1100 {
1101 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
1102 }
1103 record.body.id_string_info = name.size();
1104 std::strncpy(record.body.id_string, name.c_str(),
1105 sizeof(record.body.id_string));
1106
1107 if (sizeof(get_sdr::SensorDataFullRecord) <
1108 (req->offset + req->bytesToRead))
1109 {
1110 req->bytesToRead = sizeof(get_sdr::SensorDataFullRecord) - req->offset;
1111 }
1112
1113 *dataLen =
1114 2 + req->bytesToRead; // bytesToRead + MSB and LSB of next record id
1115
1116 std::memcpy(&resp->record_data, (char *)&record + req->offset,
1117 req->bytesToRead);
1118
1119 return IPMI_CC_OK;
1120}
1121/* end storage commands */
1122
1123void registerSensorFunctions()
1124{
1125 // get firmware version information
1126 ipmiPrintAndRegister(NETFUN_SENSOR, IPMI_CMD_WILDCARD, nullptr,
1127 ipmiSensorWildcardHandler, PRIVILEGE_USER);
1128
1129 // <Get Sensor Type>
1130 ipmiPrintAndRegister(
1131 NETFUN_SENSOR,
1132 static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdGetSensorType),
1133 nullptr, ipmiSensorWildcardHandler, PRIVILEGE_USER);
1134
1135 // <Set Sensor Reading and Event Status>
1136 ipmiPrintAndRegister(
1137 NETFUN_SENSOR,
1138 static_cast<ipmi_cmd_t>(
1139 IPMINetfnSensorCmds::ipmiCmdSetSensorReadingAndEventStatus),
1140 nullptr, ipmiSensorWildcardHandler, PRIVILEGE_OPERATOR);
1141
1142 // <Get Sensor Reading>
1143 ipmiPrintAndRegister(
1144 NETFUN_SENSOR,
1145 static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdGetSensorReading),
1146 nullptr, ipmiSenGetSensorReading, PRIVILEGE_USER);
1147
1148 // <Get Sensor Threshold>
1149 ipmiPrintAndRegister(
1150 NETFUN_SENSOR,
1151 static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdGetSensorThreshold),
1152 nullptr, ipmiSenGetSensorThresholds, PRIVILEGE_USER);
1153
1154 ipmiPrintAndRegister(
1155 NETFUN_SENSOR,
1156 static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdSetSensorThreshold),
1157 nullptr, ipmiSenSetSensorThresholds, PRIVILEGE_OPERATOR);
1158
1159 // <Get Sensor Event Enable>
1160 ipmiPrintAndRegister(NETFUN_SENSOR,
1161 static_cast<ipmi_cmd_t>(
1162 IPMINetfnSensorCmds::ipmiCmdGetSensorEventEnable),
1163 nullptr, ipmiSenGetSensorEventEnable, PRIVILEGE_USER);
1164
1165 // <Get Sensor Event Status>
1166 ipmiPrintAndRegister(NETFUN_SENSOR,
1167 static_cast<ipmi_cmd_t>(
1168 IPMINetfnSensorCmds::ipmiCmdGetSensorEventStatus),
1169 nullptr, ipmiSenGetSensorEventStatus, PRIVILEGE_USER);
1170
1171 // register all storage commands for both Sensor and Storage command
1172 // versions
1173
1174 // <Get SDR Repository Info>
1175 ipmiPrintAndRegister(
1176 NETFUN_STORAGE,
1177 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdGetRepositoryInfo),
1178 nullptr, ipmiStorageGetSDRRepositoryInfo, PRIVILEGE_USER);
1179
1180 // <Get SDR Allocation Info>
1181 ipmiPrintAndRegister(NETFUN_STORAGE,
1182 static_cast<ipmi_cmd_t>(
1183 IPMINetfnStorageCmds::ipmiCmdGetSDRAllocationInfo),
1184 nullptr, ipmiStorageGetSDRAllocationInfo,
1185 PRIVILEGE_USER);
1186
1187 // <Reserve SDR Repo>
1188 ipmiPrintAndRegister(NETFUN_SENSOR,
1189 static_cast<ipmi_cmd_t>(
1190 IPMINetfnSensorCmds::ipmiCmdReserveDeviceSDRRepo),
1191 nullptr, ipmiStorageReserveSDR, PRIVILEGE_USER);
1192
1193 ipmiPrintAndRegister(
1194 NETFUN_STORAGE,
1195 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdReserveSDR),
1196 nullptr, ipmiStorageReserveSDR, PRIVILEGE_USER);
1197
1198 // <Get Sdr>
1199 ipmiPrintAndRegister(
1200 NETFUN_SENSOR,
1201 static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdGetDeviceSDR),
1202 nullptr, ipmiStorageGetSDR, PRIVILEGE_USER);
1203
1204 ipmiPrintAndRegister(
1205 NETFUN_STORAGE,
1206 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdGetSDR), nullptr,
1207 ipmiStorageGetSDR, PRIVILEGE_USER);
1208 return;
1209}
1210} // namespace ipmi