blob: 6456ca10652e409603a402e570c776b8cd46d872 [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>>;
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 {
711 warningHighAlarm = warningHigh->second.get<bool>();
712 }
713 if (warningLow != warningMap.end())
714 {
715 warningLowAlarm = warningLow->second.get<bool>();
716 }
717 if (warningHighAlarm)
718 {
719 resp->assertionsLSB |= static_cast<uint8_t>(
720 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
721 }
722 if (warningLowAlarm)
723 {
724 resp->assertionsLSB |= 1; // lower nc going low
725 }
726 }
727 if (criticalInterface != sensorMap.end())
728 {
729 auto &criticalMap = criticalInterface->second;
730
731 auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
732 auto criticalLow = criticalMap.find("CriticalAlarmLow");
733 auto criticalHighAlarm = false;
734 auto criticalLowAlarm = false;
735
736 if (criticalHigh != criticalMap.end())
737 {
738 criticalHighAlarm = criticalHigh->second.get<bool>();
739 }
740 if (criticalLow != criticalMap.end())
741 {
742 criticalLowAlarm = criticalLow->second.get<bool>();
743 }
744 if (criticalHighAlarm)
745 {
746 resp->assertionsMSB |= static_cast<uint8_t>(
747 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
748 }
749 if (criticalLowAlarm)
750 {
751 resp->assertionsLSB |= static_cast<uint8_t>(
752 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
753 }
754 }
755 *dataLen = sizeof(SensorEventStatusResp);
756 }
757
758 // no thresholds enabled, don't need assertionMSB
759 else
760 {
761 *dataLen = sizeof(SensorEventStatusResp) - 1;
762 }
763
764 return IPMI_CC_OK;
765}
766
767/* end sensor commands */
768
769/* storage commands */
770
771ipmi_ret_t ipmiStorageGetSDRRepositoryInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
772 ipmi_request_t request,
773 ipmi_response_t response,
774 ipmi_data_len_t dataLen,
775 ipmi_context_t context)
776{
777 printCommand(+netfn, +cmd);
778
779 if (*dataLen)
780 {
781 *dataLen = 0;
782 return IPMI_CC_REQ_DATA_LEN_INVALID;
783 }
784 *dataLen = 0; // default to 0 in case of an error
785
786 if (sensorTree.empty() && !getSensorSubtree(sensorTree))
787 {
788 return IPMI_CC_RESPONSE_ERROR;
789 }
790
791 // zero out response buff
792 auto responseClear = static_cast<uint8_t *>(response);
793 std::fill(responseClear, responseClear + sizeof(GetSDRInfoResp), 0);
794
795 auto resp = static_cast<GetSDRInfoResp *>(response);
796 resp->sdrVersion = ipmiSdrVersion;
797 uint16_t recordCount = sensorTree.size();
798
799 // todo: for now, sdr count is number of sensors
800 resp->recordCountLS = recordCount & 0xFF;
801 resp->recordCountMS = recordCount >> 8;
802
803 // free space unspcified
804 resp->freeSpace[0] = 0xFF;
805 resp->freeSpace[1] = 0xFF;
806
807 resp->mostRecentAddition = sdrLastAdd;
808 resp->mostRecentErase = sdrLastRemove;
809 resp->operationSupport = static_cast<uint8_t>(
810 SdrRepositoryInfoOps::overflow); // write not supported
811 resp->operationSupport |=
812 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
813 resp->operationSupport |= static_cast<uint8_t>(
814 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
815 *dataLen = sizeof(GetSDRInfoResp);
816 return IPMI_CC_OK;
817}
818
819ipmi_ret_t ipmiStorageGetSDRAllocationInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
820 ipmi_request_t request,
821 ipmi_response_t response,
822 ipmi_data_len_t dataLen,
823 ipmi_context_t context)
824{
825 if (*dataLen)
826 {
827 *dataLen = 0;
828 return IPMI_CC_REQ_DATA_LEN_INVALID;
829 }
830 *dataLen = 0; // default to 0 in case of an error
831 GetAllocInfoResp *resp = static_cast<GetAllocInfoResp *>(response);
832
833 // 0000h unspecified number of alloc units
834 resp->allocUnitsLSB = 0;
835 resp->allocUnitsMSB = 0;
836
837 // max unit size is size of max record
838 resp->allocUnitSizeLSB = maxSDRTotalSize & 0xFF;
839 resp->allocUnitSizeMSB = maxSDRTotalSize >> 8;
840 // read only sdr, no free alloc blocks
841 resp->allocUnitFreeLSB = 0;
842 resp->allocUnitFreeMSB = 0;
843 resp->allocUnitLargestFreeLSB = 0;
844 resp->allocUnitLargestFreeMSB = 0;
845 // only allow one block at a time
846 resp->maxRecordSize = 1;
847
848 *dataLen = sizeof(GetAllocInfoResp);
849
850 return IPMI_CC_OK;
851}
852
853ipmi_ret_t ipmiStorageReserveSDR(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
854 ipmi_request_t request,
855 ipmi_response_t response,
856 ipmi_data_len_t dataLen,
857 ipmi_context_t context)
858{
859 printCommand(+netfn, +cmd);
860
861 if (*dataLen)
862 {
863 *dataLen = 0;
864 return IPMI_CC_REQ_DATA_LEN_INVALID;
865 }
866 *dataLen = 0; // default to 0 in case of an error
867 sdrReservationID++;
868 *dataLen = 2;
869 auto resp = static_cast<uint8_t *>(response);
870 resp[0] = sdrReservationID & 0xFF;
871 resp[1] = sdrReservationID >> 8;
872
873 return IPMI_CC_OK;
874}
875
876ipmi_ret_t ipmiStorageGetSDR(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
877 ipmi_request_t request, ipmi_response_t response,
878 ipmi_data_len_t dataLen, ipmi_context_t context)
879{
880 printCommand(+netfn, +cmd);
881
882 if (*dataLen != 6)
883 {
884 *dataLen = 0;
885 return IPMI_CC_REQ_DATA_LEN_INVALID;
886 }
887 auto requestedSize = *dataLen;
888 *dataLen = 0; // default to 0 in case of an error
889
890 constexpr uint16_t lastRecordIndex = 0xFFFF;
891 auto req = static_cast<GetSDRReq *>(request);
892
893 // reservation required for partial reads with non zero offset into
894 // record
895 if (req->reservationID != sdrReservationID && req->offset)
896 {
897 return IPMI_CC_INVALID_RESERVATION_ID;
898 }
899
900 if (sensorTree.empty() && !getSensorSubtree(sensorTree))
901 {
902 return IPMI_CC_RESPONSE_ERROR;
903 }
904
905 size_t fruCount = 0;
906 ipmi_ret_t ret = ipmi::storage::getFruSdrCount(fruCount);
907 if (ret != IPMI_CC_OK)
908 {
909 return ret;
910 }
911
912 size_t lastRecord = sensorTree.size() + fruCount - 1;
913 if (req->recordID == lastRecordIndex)
914 {
915 req->recordID = lastRecord;
916 }
917 if (req->recordID > lastRecord)
918 {
919 return IPMI_CC_INVALID_FIELD_REQUEST;
920 }
921
922 uint16_t nextRecord =
923 lastRecord > (req->recordID + 1) ? req->recordID + 1 : 0XFFFF;
924
925 auto responseClear = static_cast<uint8_t *>(response);
926 std::fill(responseClear, responseClear + requestedSize, 0);
927
928 auto resp = static_cast<get_sdr::GetSdrResp *>(response);
929 resp->next_record_id_lsb = nextRecord & 0xFF;
930 resp->next_record_id_msb = nextRecord >> 8;
931
932 if (req->recordID >= sensorTree.size())
933 {
934 size_t fruIndex = req->recordID - sensorTree.size();
935 if (fruIndex >= fruCount)
936 {
937 return IPMI_CC_INVALID_FIELD_REQUEST;
938 }
939 get_sdr::SensorDataFruRecord data;
940 if (req->offset > sizeof(data))
941 {
942 return IPMI_CC_INVALID_FIELD_REQUEST;
943 }
944 ret = ipmi::storage::getFruSdrs(fruIndex, data);
945 if (ret != IPMI_CC_OK)
946 {
947 return ret;
948 }
949 data.header.record_id_msb = req->recordID << 8;
950 data.header.record_id_lsb = req->recordID & 0xFF;
951 if (sizeof(data) < (req->offset + req->bytesToRead))
952 {
953 req->bytesToRead = sizeof(data) - req->offset;
954 }
955 *dataLen = req->bytesToRead + 2; // next record
956 std::memcpy(&resp->record_data, (char *)&data + req->offset,
957 req->bytesToRead);
958 return IPMI_CC_OK;
959 }
960
961 std::string connection;
962 std::string path;
963 uint16_t sensorIndex = req->recordID;
964 for (const auto &sensor : sensorTree)
965 {
966 if (sensorIndex-- == 0)
967 {
968 if (!sensor.second.size())
969 {
970 return IPMI_CC_RESPONSE_ERROR;
971 }
972 connection = sensor.second.begin()->first;
973 path = sensor.first;
974 break;
975 }
976 }
977
978 SensorMap sensorMap;
979 if (!getSensorMap(connection, path, sensorMap))
980 {
981 return IPMI_CC_RESPONSE_ERROR;
982 }
983 uint8_t sensornumber = (req->recordID & 0xFF);
984 get_sdr::SensorDataFullRecord record = {0};
985
986 record.header.record_id_msb = req->recordID << 8;
987 record.header.record_id_lsb = req->recordID & 0xFF;
988 record.header.sdr_version = ipmiSdrVersion;
989 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
990 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
991 sizeof(get_sdr::SensorDataRecordHeader);
992 record.key.owner_id = 0x20;
993 record.key.owner_lun = 0x0;
994 record.key.sensor_number = sensornumber;
995
996 record.body.entity_id = 0x0;
997 record.body.entity_instance = 0x01;
998 record.body.sensor_capabilities = 0x60; // auto rearm - todo hysteresis
999 record.body.sensor_type = getSensorTypeFromPath(path);
1000 std::string type = getSensorTypeStringFromPath(path);
1001 auto typeCstr = type.c_str();
1002 auto findUnits = sensorUnits.find(typeCstr);
1003 if (findUnits != sensorUnits.end())
1004 {
1005 record.body.sensor_units_2_base =
1006 static_cast<uint8_t>(findUnits->second);
1007 } // else default 0x0 unspecified
1008
1009 record.body.event_reading_type = getSensorEventTypeFromPath(path);
1010
1011 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
1012 if (sensorObject == sensorMap.end())
1013 {
1014 return IPMI_CC_RESPONSE_ERROR;
1015 }
1016
1017 auto maxObject = sensorObject->second.find("MaxValue");
1018 auto minObject = sensorObject->second.find("MinValue");
1019 double max = 128;
1020 double min = -127;
1021 if (maxObject != sensorObject->second.end())
1022 {
James Feist14fde842018-12-06 10:19:40 -08001023 max = variant_ns::visit(VariantToDoubleVisitor(), maxObject->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001024 }
1025
1026 if (minObject != sensorObject->second.end())
1027 {
James Feist14fde842018-12-06 10:19:40 -08001028 min = variant_ns::visit(VariantToDoubleVisitor(), minObject->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001029 }
1030
1031 int16_t mValue;
1032 int8_t rExp;
1033 int16_t bValue;
1034 int8_t bExp;
1035 bool bSigned;
1036
1037 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1038 {
1039 return IPMI_CC_RESPONSE_ERROR;
1040 }
1041
1042 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1043 record.body.m_lsb = mValue & 0xFF;
1044
1045 // move the smallest bit of the MSB into place (bit 9)
1046 // the MSbs are bits 7:8 in m_msb_and_tolerance
1047 uint8_t mMsb = (mValue & (1 << 8)) > 0 ? (1 << 6) : 0;
1048
1049 // assign the negative
1050 if (mValue < 0)
1051 {
1052 mMsb |= (1 << 7);
1053 }
1054 record.body.m_msb_and_tolerance = mMsb;
1055
1056 record.body.b_lsb = bValue & 0xFF;
1057
1058 // move the smallest bit of the MSB into place
1059 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1060 uint8_t bMsb = (bValue & (1 << 8)) > 0 ? (1 << 6) : 0;
1061
1062 // assign the negative
1063 if (bValue < 0)
1064 {
1065 bMsb |= (1 << 7);
1066 }
1067 record.body.b_msb_and_accuracy_lsb = bMsb;
1068
1069 record.body.r_b_exponents = bExp & 0x7;
1070 if (bExp < 0)
1071 {
1072 record.body.r_b_exponents |= 1 << 3;
1073 }
1074 record.body.r_b_exponents = (rExp & 0x7) << 4;
1075 if (rExp < 0)
1076 {
1077 record.body.r_b_exponents |= 1 << 7;
1078 }
1079
1080 // todo fill out rest of units
1081 if (bSigned)
1082 {
1083 record.body.sensor_units_1 = 1 << 7;
1084 }
1085
1086 // populate sensor name from path
1087 std::string name;
1088 size_t nameStart = path.rfind("/");
1089 if (nameStart != std::string::npos)
1090 {
1091 name = path.substr(nameStart + 1, std::string::npos - nameStart);
1092 }
1093
1094 std::replace(name.begin(), name.end(), '_', ' ');
1095 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
1096 {
1097 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
1098 }
1099 record.body.id_string_info = name.size();
1100 std::strncpy(record.body.id_string, name.c_str(),
1101 sizeof(record.body.id_string));
1102
1103 if (sizeof(get_sdr::SensorDataFullRecord) <
1104 (req->offset + req->bytesToRead))
1105 {
1106 req->bytesToRead = sizeof(get_sdr::SensorDataFullRecord) - req->offset;
1107 }
1108
1109 *dataLen =
1110 2 + req->bytesToRead; // bytesToRead + MSB and LSB of next record id
1111
1112 std::memcpy(&resp->record_data, (char *)&record + req->offset,
1113 req->bytesToRead);
1114
1115 return IPMI_CC_OK;
1116}
1117/* end storage commands */
1118
1119void registerSensorFunctions()
1120{
1121 // get firmware version information
1122 ipmiPrintAndRegister(NETFUN_SENSOR, IPMI_CMD_WILDCARD, nullptr,
1123 ipmiSensorWildcardHandler, PRIVILEGE_USER);
1124
1125 // <Get Sensor Type>
1126 ipmiPrintAndRegister(
1127 NETFUN_SENSOR,
1128 static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdGetSensorType),
1129 nullptr, ipmiSensorWildcardHandler, PRIVILEGE_USER);
1130
1131 // <Set Sensor Reading and Event Status>
1132 ipmiPrintAndRegister(
1133 NETFUN_SENSOR,
1134 static_cast<ipmi_cmd_t>(
1135 IPMINetfnSensorCmds::ipmiCmdSetSensorReadingAndEventStatus),
1136 nullptr, ipmiSensorWildcardHandler, PRIVILEGE_OPERATOR);
1137
1138 // <Get Sensor Reading>
1139 ipmiPrintAndRegister(
1140 NETFUN_SENSOR,
1141 static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdGetSensorReading),
1142 nullptr, ipmiSenGetSensorReading, PRIVILEGE_USER);
1143
1144 // <Get Sensor Threshold>
1145 ipmiPrintAndRegister(
1146 NETFUN_SENSOR,
1147 static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdGetSensorThreshold),
1148 nullptr, ipmiSenGetSensorThresholds, PRIVILEGE_USER);
1149
1150 ipmiPrintAndRegister(
1151 NETFUN_SENSOR,
1152 static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdSetSensorThreshold),
1153 nullptr, ipmiSenSetSensorThresholds, PRIVILEGE_OPERATOR);
1154
1155 // <Get Sensor Event Enable>
1156 ipmiPrintAndRegister(NETFUN_SENSOR,
1157 static_cast<ipmi_cmd_t>(
1158 IPMINetfnSensorCmds::ipmiCmdGetSensorEventEnable),
1159 nullptr, ipmiSenGetSensorEventEnable, PRIVILEGE_USER);
1160
1161 // <Get Sensor Event Status>
1162 ipmiPrintAndRegister(NETFUN_SENSOR,
1163 static_cast<ipmi_cmd_t>(
1164 IPMINetfnSensorCmds::ipmiCmdGetSensorEventStatus),
1165 nullptr, ipmiSenGetSensorEventStatus, PRIVILEGE_USER);
1166
1167 // register all storage commands for both Sensor and Storage command
1168 // versions
1169
1170 // <Get SDR Repository Info>
1171 ipmiPrintAndRegister(
1172 NETFUN_STORAGE,
1173 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdGetRepositoryInfo),
1174 nullptr, ipmiStorageGetSDRRepositoryInfo, PRIVILEGE_USER);
1175
1176 // <Get SDR Allocation Info>
1177 ipmiPrintAndRegister(NETFUN_STORAGE,
1178 static_cast<ipmi_cmd_t>(
1179 IPMINetfnStorageCmds::ipmiCmdGetSDRAllocationInfo),
1180 nullptr, ipmiStorageGetSDRAllocationInfo,
1181 PRIVILEGE_USER);
1182
1183 // <Reserve SDR Repo>
1184 ipmiPrintAndRegister(NETFUN_SENSOR,
1185 static_cast<ipmi_cmd_t>(
1186 IPMINetfnSensorCmds::ipmiCmdReserveDeviceSDRRepo),
1187 nullptr, ipmiStorageReserveSDR, PRIVILEGE_USER);
1188
1189 ipmiPrintAndRegister(
1190 NETFUN_STORAGE,
1191 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdReserveSDR),
1192 nullptr, ipmiStorageReserveSDR, PRIVILEGE_USER);
1193
1194 // <Get Sdr>
1195 ipmiPrintAndRegister(
1196 NETFUN_SENSOR,
1197 static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdGetDeviceSDR),
1198 nullptr, ipmiStorageGetSDR, PRIVILEGE_USER);
1199
1200 ipmiPrintAndRegister(
1201 NETFUN_STORAGE,
1202 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdGetSDR), nullptr,
1203 ipmiStorageGetSDR, PRIVILEGE_USER);
1204 return;
1205}
1206} // namespace ipmi