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