blob: 02df84c9f2896ed6296965d91417549ea69aa7fb [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. Bills3f7c5e42018-10-03 14:00:41 -070017#include <boost/algorithm/string.hpp>
18#include <boost/container/flat_map.hpp>
19#include <chrono>
20#include <cmath>
21#include <commandutils.hpp>
22#include <iostream>
James Feist2a265d52019-04-08 11:16:27 -070023#include <ipmid/api.hpp>
Vernon Mauery5480ef62019-03-20 13:43:11 -070024#include <ipmid/utils.hpp>
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070025#include <phosphor-logging/log.hpp>
26#include <sdbusplus/bus.hpp>
27#include <sdrutils.hpp>
28#include <sensorcommands.hpp>
29#include <sensorutils.hpp>
30#include <storagecommands.hpp>
31#include <string>
32
33namespace ipmi
34{
35using ManagedObjectType =
36 std::map<sdbusplus::message::object_path,
37 std::map<std::string, std::map<std::string, DbusVariant>>>;
38
39using SensorMap = std::map<std::string, std::map<std::string, DbusVariant>>;
James Feist14fde842018-12-06 10:19:40 -080040namespace variant_ns = sdbusplus::message::variant_ns;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070041
42static constexpr int sensorListUpdatePeriod = 10;
43static constexpr int sensorMapUpdatePeriod = 2;
44
45constexpr size_t maxSDRTotalSize =
46 76; // Largest SDR Record Size (type 01) + SDR Overheader Size
47constexpr static const uint32_t noTimestamp = 0xFFFFFFFF;
48
49static uint16_t sdrReservationID;
50static uint32_t sdrLastAdd = noTimestamp;
51static uint32_t sdrLastRemove = noTimestamp;
52
Richard Marian Thomaiyar01fbcb52018-11-19 22:04:34 +053053SensorSubTree sensorTree;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070054static boost::container::flat_map<std::string, ManagedObjectType> SensorCache;
55
Jason M. Bills17add592018-11-12 14:30:12 -080056// Specify the comparison required to sort and find char* map objects
57struct CmpStr
58{
59 bool operator()(const char *a, const char *b) const
60 {
61 return std::strcmp(a, b) < 0;
62 }
63};
64const static boost::container::flat_map<const char *, SensorUnits, CmpStr>
65 sensorUnits{{{"temperature", SensorUnits::degreesC},
66 {"voltage", SensorUnits::volts},
67 {"current", SensorUnits::amps},
68 {"fan_tach", SensorUnits::rpm},
69 {"power", SensorUnits::watts}}};
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070070
71void registerSensorFunctions() __attribute__((constructor));
72static sdbusplus::bus::bus dbus(ipmid_get_sd_bus_connection());
73
74static sdbusplus::bus::match::match sensorAdded(
75 dbus,
76 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
77 "sensors/'",
78 [](sdbusplus::message::message &m) {
79 sensorTree.clear();
80 sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>(
81 std::chrono::system_clock::now().time_since_epoch())
82 .count();
83 });
84
85static sdbusplus::bus::match::match sensorRemoved(
86 dbus,
87 "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/"
88 "sensors/'",
89 [](sdbusplus::message::message &m) {
90 sensorTree.clear();
91 sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>(
92 std::chrono::system_clock::now().time_since_epoch())
93 .count();
94 });
95
James Feist392786a2019-03-19 13:36:10 -070096// this keeps track of deassertions for sensor event status command. A
97// deasertion can only happen if an assertion was seen first.
98static boost::container::flat_map<
99 std::string, boost::container::flat_map<std::string, std::optional<bool>>>
100 thresholdDeassertMap;
101
102static sdbusplus::bus::match::match thresholdChanged(
103 dbus,
104 "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus."
105 "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
106 [](sdbusplus::message::message &m) {
107 boost::container::flat_map<std::string, std::variant<bool, double>>
108 values;
109 m.read(std::string(), values);
110
111 auto findAssert =
112 std::find_if(values.begin(), values.end(), [](const auto &pair) {
113 return pair.first.find("Alarm") != std::string::npos;
114 });
115 if (findAssert != values.end())
116 {
117 auto ptr = std::get_if<bool>(&(findAssert->second));
118 if (ptr == nullptr)
119 {
120 phosphor::logging::log<phosphor::logging::level::ERR>(
121 "thresholdChanged: Assert non bool");
122 return;
123 }
124 if (*ptr)
125 {
126 phosphor::logging::log<phosphor::logging::level::INFO>(
127 "thresholdChanged: Assert",
128 phosphor::logging::entry("SENSOR=%s", m.get_path()));
129 thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr;
130 }
131 else
132 {
133 auto &value =
134 thresholdDeassertMap[m.get_path()][findAssert->first];
135 if (value)
136 {
137 phosphor::logging::log<phosphor::logging::level::INFO>(
138 "thresholdChanged: deassert",
139 phosphor::logging::entry("SENSOR=%s", m.get_path()));
140 value = *ptr;
141 }
142 }
143 }
144 });
145
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700146static void
147 getSensorMaxMin(const std::map<std::string, DbusVariant> &sensorPropertyMap,
148 double &max, double &min)
149{
150 auto maxMap = sensorPropertyMap.find("MaxValue");
151 auto minMap = sensorPropertyMap.find("MinValue");
152 max = 127;
153 min = -128;
154
155 if (maxMap != sensorPropertyMap.end())
156 {
James Feist14fde842018-12-06 10:19:40 -0800157 max = variant_ns::visit(VariantToDoubleVisitor(), maxMap->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700158 }
159 if (minMap != sensorPropertyMap.end())
160 {
James Feist14fde842018-12-06 10:19:40 -0800161 min = variant_ns::visit(VariantToDoubleVisitor(), minMap->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700162 }
163}
164
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700165static bool getSensorMap(std::string sensorConnection, std::string sensorPath,
166 SensorMap &sensorMap)
167{
168 static boost::container::flat_map<
169 std::string, std::chrono::time_point<std::chrono::steady_clock>>
170 updateTimeMap;
171
172 auto updateFind = updateTimeMap.find(sensorConnection);
173 auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
174 if (updateFind != updateTimeMap.end())
175 {
176 lastUpdate = updateFind->second;
177 }
178
179 auto now = std::chrono::steady_clock::now();
180
181 if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
182 .count() > sensorMapUpdatePeriod)
183 {
184 updateTimeMap[sensorConnection] = now;
185
186 auto managedObj = dbus.new_method_call(
187 sensorConnection.c_str(), "/", "org.freedesktop.DBus.ObjectManager",
188 "GetManagedObjects");
189
190 ManagedObjectType managedObjects;
191 try
192 {
193 auto reply = dbus.call(managedObj);
194 reply.read(managedObjects);
195 }
196 catch (sdbusplus::exception_t &)
197 {
198 phosphor::logging::log<phosphor::logging::level::ERR>(
199 "Error getting managed objects from connection",
200 phosphor::logging::entry("CONNECTION=%s",
201 sensorConnection.c_str()));
202 return false;
203 }
204
205 SensorCache[sensorConnection] = managedObjects;
206 }
207 auto connection = SensorCache.find(sensorConnection);
208 if (connection == SensorCache.end())
209 {
210 return false;
211 }
212 auto path = connection->second.find(sensorPath);
213 if (path == connection->second.end())
214 {
215 return false;
216 }
217 sensorMap = path->second;
218
219 return true;
220}
221
222/* sensor commands */
223ipmi_ret_t ipmiSensorWildcardHandler(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
224 ipmi_request_t request,
225 ipmi_response_t response,
226 ipmi_data_len_t dataLen,
227 ipmi_context_t context)
228{
229 *dataLen = 0;
230 printCommand(+netfn, +cmd);
231 return IPMI_CC_INVALID;
232}
233
Jason M. Billsae6bdb12019-04-02 12:00:04 -0700234ipmi::RspType<> ipmiSenPlatformEvent(ipmi::message::Payload &p)
235{
236 uint8_t generatorID = 0;
237 uint8_t evmRev = 0;
238 uint8_t sensorType = 0;
239 uint8_t sensorNum = 0;
240 uint8_t eventType = 0;
241 uint8_t eventData1 = 0;
242 std::optional<uint8_t> eventData2 = 0;
243 std::optional<uint8_t> eventData3 = 0;
244
245 // todo: This check is supposed to be based on the incoming channel.
246 // e.g. system channel will provide upto 8 bytes including generator
247 // ID, but ipmb channel will provide only up to 7 bytes without the
248 // generator ID.
249 // Support for this check is coming in future patches, so for now just base
250 // it on if the first byte is the EvMRev (0x04).
251 if (p.size() && p.data()[0] == 0x04)
252 {
253 p.unpack(evmRev, sensorType, sensorNum, eventType, eventData1,
254 eventData2, eventData3);
255 // todo: the generator ID for this channel is supposed to come from the
256 // IPMB requesters slave address. Support for this is coming in future
257 // patches, so for now just assume it is coming from the ME (0x2C).
258 generatorID = 0x2C;
259 }
260 else
261 {
262 p.unpack(generatorID, evmRev, sensorType, sensorNum, eventType,
263 eventData1, eventData2, eventData3);
264 }
265 if (!p.fullyUnpacked())
266 {
267 return ipmi::responseReqDataLenInvalid();
268 }
269
270 bool assert = eventType & directionMask ? false : true;
271 std::vector<uint8_t> eventData;
272 eventData.push_back(eventData1);
273 eventData.push_back(eventData2.value_or(0xFF));
274 eventData.push_back(eventData3.value_or(0xFF));
275
276 std::string sensorPath = getPathFromSensorNumber(sensorNum);
277 std::string service =
278 ipmi::getService(dbus, ipmiSELAddInterface, ipmiSELPath);
279 sdbusplus::message::message writeSEL = dbus.new_method_call(
280 service.c_str(), ipmiSELPath, ipmiSELAddInterface, "IpmiSelAdd");
281 writeSEL.append(ipmiSELAddMessage, sensorPath, eventData, assert,
282 static_cast<uint16_t>(generatorID));
283 try
284 {
285 dbus.call(writeSEL);
286 }
287 catch (sdbusplus::exception_t &e)
288 {
289 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
290 return ipmi::responseUnspecifiedError();
291 }
292
293 return ipmi::responseSuccess();
294}
295
James Feist0cd014a2019-04-08 15:04:33 -0700296ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
297 ipmiSenGetSensorReading(uint8_t sensnum)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700298{
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700299 std::string connection;
300 std::string path;
301
302 auto status = getSensorConnection(sensnum, connection, path);
303 if (status)
304 {
James Feist0cd014a2019-04-08 15:04:33 -0700305 return ipmi::response(status);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700306 }
307
308 SensorMap sensorMap;
309 if (!getSensorMap(connection, path, sensorMap))
310 {
James Feist0cd014a2019-04-08 15:04:33 -0700311 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700312 }
313 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
314
315 if (sensorObject == sensorMap.end() ||
316 sensorObject->second.find("Value") == sensorObject->second.end())
317 {
James Feist0cd014a2019-04-08 15:04:33 -0700318 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700319 }
James Feist0cd014a2019-04-08 15:04:33 -0700320 auto &valueVariant = sensorObject->second["Value"];
321 double reading = variant_ns::visit(VariantToDoubleVisitor(), valueVariant);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700322
323 double max;
324 double min;
325 getSensorMaxMin(sensorObject->second, max, min);
326
327 int16_t mValue = 0;
328 int16_t bValue = 0;
329 int8_t rExp = 0;
330 int8_t bExp = 0;
331 bool bSigned = false;
332
333 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
334 {
James Feist0cd014a2019-04-08 15:04:33 -0700335 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700336 }
337
James Feist0cd014a2019-04-08 15:04:33 -0700338 uint8_t value =
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700339 scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
James Feist0cd014a2019-04-08 15:04:33 -0700340 uint8_t operation =
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700341 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
James Feist0cd014a2019-04-08 15:04:33 -0700342 operation |=
James Feist81a95c12019-03-01 15:08:28 -0800343 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700344
James Feist0cd014a2019-04-08 15:04:33 -0700345 uint8_t thresholds = 0;
346
347 auto warningObject =
348 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
349 if (warningObject != sensorMap.end())
350 {
351 auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
352 auto alarmLow = warningObject->second.find("WarningAlarmLow");
353 if (alarmHigh != warningObject->second.end())
354 {
355 if (std::get<bool>(alarmHigh->second))
356 {
357 thresholds |= static_cast<uint8_t>(
358 IPMISensorReadingByte3::upperNonCritical);
359 }
360 }
361 if (alarmLow != warningObject->second.end())
362 {
363 if (std::get<bool>(alarmLow->second))
364 {
365 thresholds |= static_cast<uint8_t>(
366 IPMISensorReadingByte3::lowerNonCritical);
367 }
368 }
369 }
370
371 auto criticalObject =
372 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
373 if (criticalObject != sensorMap.end())
374 {
375 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
376 auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
377 if (alarmHigh != criticalObject->second.end())
378 {
379 if (std::get<bool>(alarmHigh->second))
380 {
381 thresholds |=
382 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
383 }
384 }
385 if (alarmLow != criticalObject->second.end())
386 {
387 if (std::get<bool>(alarmLow->second))
388 {
389 thresholds |=
390 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
391 }
392 }
393 }
394
395 // no discrete as of today so optional byte is never returned
396 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700397}
398
399ipmi_ret_t ipmiSenSetSensorThresholds(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
400 ipmi_request_t request,
401 ipmi_response_t response,
402 ipmi_data_len_t dataLen,
403 ipmi_context_t context)
404{
405 if (*dataLen != 8)
406 {
407 *dataLen = 0;
408 return IPMI_CC_REQ_DATA_LEN_INVALID;
409 }
410 *dataLen = 0;
411
412 SensorThresholdReq *req = static_cast<SensorThresholdReq *>(request);
413
414 // upper two bits reserved
415 if (req->mask & 0xC0)
416 {
417 return IPMI_CC_INVALID_FIELD_REQUEST;
418 }
419
420 // lower nc and upper nc not suppported on any sensor
421 if ((req->mask & static_cast<uint8_t>(
422 SensorThresholdReqEnable::setLowerNonRecoverable)) ||
423 (req->mask & static_cast<uint8_t>(
424 SensorThresholdReqEnable::setUpperNonRecoverable)))
425 {
426 return IPMI_CC_INVALID_FIELD_REQUEST;
427 }
428
429 // if no bits are set in the mask, nothing to do
430 if (!(req->mask))
431 {
432 return IPMI_CC_OK;
433 }
434
435 std::string connection;
436 std::string path;
437
438 ipmi_ret_t status = getSensorConnection(req->sensorNum, connection, path);
439 if (status)
440 {
441 return status;
442 }
443 SensorMap sensorMap;
444 if (!getSensorMap(connection, path, sensorMap))
445 {
446 return IPMI_CC_RESPONSE_ERROR;
447 }
448
449 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
450
451 if (sensorObject == sensorMap.end())
452 {
453 return IPMI_CC_RESPONSE_ERROR;
454 }
455 double max = 0;
456 double min = 0;
457 getSensorMaxMin(sensorObject->second, max, min);
458
459 int16_t mValue = 0;
460 int16_t bValue = 0;
461 int8_t rExp = 0;
462 int8_t bExp = 0;
463 bool bSigned = false;
464
465 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
466 {
467 return IPMI_CC_RESPONSE_ERROR;
468 }
469
470 bool setLowerCritical =
471 req->mask &
472 static_cast<uint8_t>(SensorThresholdReqEnable::setLowerCritical);
473 bool setUpperCritical =
474 req->mask &
475 static_cast<uint8_t>(SensorThresholdReqEnable::setUpperCritical);
476
477 bool setLowerWarning =
478 req->mask &
479 static_cast<uint8_t>(SensorThresholdReqEnable::setLowerNonCritical);
480 bool setUpperWarning =
481 req->mask &
482 static_cast<uint8_t>(SensorThresholdReqEnable::setUpperNonCritical);
483
484 // store a vector of property name, value to set, and interface
485 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
486
487 // define the indexes of the tuple
488 constexpr uint8_t propertyName = 0;
489 constexpr uint8_t thresholdValue = 1;
490 constexpr uint8_t interface = 2;
491 // verifiy all needed fields are present
492 if (setLowerCritical || setUpperCritical)
493 {
494 auto findThreshold =
495 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
496 if (findThreshold == sensorMap.end())
497 {
498 return IPMI_CC_INVALID_FIELD_REQUEST;
499 }
500 if (setLowerCritical)
501 {
502 auto findLower = findThreshold->second.find("CriticalLow");
503 if (findLower == findThreshold->second.end())
504 {
505 return IPMI_CC_INVALID_FIELD_REQUEST;
506 }
507 thresholdsToSet.emplace_back("CriticalLow", req->lowerCritical,
508 findThreshold->first);
509 }
510 if (setUpperCritical)
511 {
512 auto findUpper = findThreshold->second.find("CriticalHigh");
513 if (findUpper == findThreshold->second.end())
514 {
515 return IPMI_CC_INVALID_FIELD_REQUEST;
516 }
517 thresholdsToSet.emplace_back("CriticalHigh", req->upperCritical,
518 findThreshold->first);
519 }
520 }
521 if (setLowerWarning || setUpperWarning)
522 {
523 auto findThreshold =
524 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
525 if (findThreshold == sensorMap.end())
526 {
527 return IPMI_CC_INVALID_FIELD_REQUEST;
528 }
529 if (setLowerWarning)
530 {
531 auto findLower = findThreshold->second.find("WarningLow");
532 if (findLower == findThreshold->second.end())
533 {
534 return IPMI_CC_INVALID_FIELD_REQUEST;
535 }
536 thresholdsToSet.emplace_back("WarningLow", req->lowerNonCritical,
537 findThreshold->first);
538 }
539 if (setUpperWarning)
540 {
541 auto findUpper = findThreshold->second.find("WarningHigh");
542 if (findUpper == findThreshold->second.end())
543 {
544 return IPMI_CC_INVALID_FIELD_REQUEST;
545 }
546 thresholdsToSet.emplace_back("WarningHigh", req->upperNonCritical,
547 findThreshold->first);
548 }
549 }
550
551 for (const auto &property : thresholdsToSet)
552 {
553 // from section 36.3 in the IPMI Spec, assume all linear
554 double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
555 (bValue * std::pow(10, bExp))) *
556 std::pow(10, rExp);
557 setDbusProperty(dbus, connection, path, std::get<interface>(property),
558 std::get<propertyName>(property),
559 ipmi::Value(valueToSet));
560 }
561
562 return IPMI_CC_OK;
563}
564
565ipmi_ret_t ipmiSenGetSensorThresholds(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
566 ipmi_request_t request,
567 ipmi_response_t response,
568 ipmi_data_len_t dataLen,
569 ipmi_context_t context)
570{
571 if (*dataLen != 1)
572 {
573 *dataLen = 0;
574 return IPMI_CC_REQ_DATA_LEN_INVALID;
575 }
576 *dataLen = 0; // default to 0 in case of an error
577
578 uint8_t sensnum = *(static_cast<uint8_t *>(request));
579
580 std::string connection;
581 std::string path;
582
583 auto status = getSensorConnection(sensnum, connection, path);
584 if (status)
585 {
586 return status;
587 }
588
589 SensorMap sensorMap;
590 if (!getSensorMap(connection, path, sensorMap))
591 {
592 return IPMI_CC_RESPONSE_ERROR;
593 }
594
595 // zero out response buff
596 auto responseClear = static_cast<uint8_t *>(response);
597 std::fill(responseClear, responseClear + sizeof(SensorThresholdResp), 0);
598
599 auto warningInterface =
600 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
601 auto criticalInterface =
602 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
603
604 if ((warningInterface != sensorMap.end()) ||
605 (criticalInterface != sensorMap.end()))
606 {
607 auto sensorPair = sensorMap.find("xyz.openbmc_project.Sensor.Value");
608
609 if (sensorPair == sensorMap.end())
610 {
611 // should not have been able to find a sensor not implementing
612 // the sensor object
613 return IPMI_CC_RESPONSE_ERROR;
614 }
615
616 double max;
617 double min;
618 getSensorMaxMin(sensorPair->second, max, min);
619
620 int16_t mValue = 0;
621 int16_t bValue = 0;
622 int8_t rExp = 0;
623 int8_t bExp = 0;
624 bool bSigned = false;
625
626 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
627 {
628 return IPMI_CC_RESPONSE_ERROR;
629 }
630
631 auto msgReply = static_cast<SensorThresholdResp *>(response);
632
633 if (warningInterface != sensorMap.end())
634 {
635 auto &warningMap = warningInterface->second;
636
637 auto warningHigh = warningMap.find("WarningHigh");
638 auto warningLow = warningMap.find("WarningLow");
639
640 if (warningHigh != warningMap.end())
641 {
642 msgReply->readable |=
643 1 << static_cast<int>(
644 IPMIhresholdRespBits::upperNonCritical);
James Feist14fde842018-12-06 10:19:40 -0800645 double value = variant_ns::visit(VariantToDoubleVisitor(),
646 warningHigh->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700647 msgReply->uppernc = scaleIPMIValueFromDouble(
648 value, mValue, rExp, bValue, bExp, bSigned);
649 }
650 if (warningLow != warningMap.end())
651 {
652 msgReply->readable |=
653 1 << static_cast<int>(
654 IPMIhresholdRespBits::lowerNonCritical);
James Feist14fde842018-12-06 10:19:40 -0800655 double value = variant_ns::visit(VariantToDoubleVisitor(),
656 warningLow->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700657 msgReply->lowernc = scaleIPMIValueFromDouble(
658 value, mValue, rExp, bValue, bExp, bSigned);
659 }
660 }
661 if (criticalInterface != sensorMap.end())
662 {
663 auto &criticalMap = criticalInterface->second;
664
665 auto criticalHigh = criticalMap.find("CriticalHigh");
666 auto criticalLow = criticalMap.find("CriticalLow");
667
668 if (criticalHigh != criticalMap.end())
669 {
670 msgReply->readable |=
671 1 << static_cast<int>(IPMIhresholdRespBits::upperCritical);
James Feist14fde842018-12-06 10:19:40 -0800672 double value = variant_ns::visit(VariantToDoubleVisitor(),
673 criticalHigh->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700674 msgReply->uppercritical = scaleIPMIValueFromDouble(
675 value, mValue, rExp, bValue, bExp, bSigned);
676 }
677 if (criticalLow != criticalMap.end())
678 {
679 msgReply->readable |=
680 1 << static_cast<int>(IPMIhresholdRespBits::lowerCritical);
James Feist14fde842018-12-06 10:19:40 -0800681 double value = variant_ns::visit(VariantToDoubleVisitor(),
682 criticalLow->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700683 msgReply->lowercritical = scaleIPMIValueFromDouble(
684 value, mValue, rExp, bValue, bExp, bSigned);
685 }
686 }
687 }
688
689 *dataLen = sizeof(SensorThresholdResp);
690 return IPMI_CC_OK;
691}
692
693ipmi_ret_t ipmiSenGetSensorEventEnable(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
694 ipmi_request_t request,
695 ipmi_response_t response,
696 ipmi_data_len_t dataLen,
697 ipmi_context_t context)
698{
699 if (*dataLen != 1)
700 {
701 *dataLen = 0;
702 return IPMI_CC_REQ_DATA_LEN_INVALID;
703 }
704 *dataLen = 0; // default to 0 in case of an error
705
706 uint8_t sensnum = *(static_cast<uint8_t *>(request));
707
708 std::string connection;
709 std::string path;
710
711 auto status = getSensorConnection(sensnum, connection, path);
712 if (status)
713 {
714 return status;
715 }
716
717 SensorMap sensorMap;
718 if (!getSensorMap(connection, path, sensorMap))
719 {
720 return IPMI_CC_RESPONSE_ERROR;
721 }
722
723 auto warningInterface =
724 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
725 auto criticalInterface =
726 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
727
728 if ((warningInterface != sensorMap.end()) ||
729 (criticalInterface != sensorMap.end()))
730 {
731 // zero out response buff
732 auto responseClear = static_cast<uint8_t *>(response);
733 std::fill(responseClear, responseClear + sizeof(SensorEventEnableResp),
734 0);
735
736 // assume all threshold sensors
737 auto resp = static_cast<SensorEventEnableResp *>(response);
738
739 resp->enabled = static_cast<uint8_t>(
740 IPMISensorEventEnableByte2::sensorScanningEnable);
741 if (warningInterface != sensorMap.end())
742 {
743 auto &warningMap = warningInterface->second;
744
745 auto warningHigh = warningMap.find("WarningHigh");
746 auto warningLow = warningMap.find("WarningLow");
747 if (warningHigh != warningMap.end())
748 {
749 resp->assertionEnabledLSB |= static_cast<uint8_t>(
750 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
751 resp->deassertionEnabledLSB |= static_cast<uint8_t>(
752 IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
753 }
754 if (warningLow != warningMap.end())
755 {
756 resp->assertionEnabledLSB |= static_cast<uint8_t>(
757 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
758 resp->deassertionEnabledLSB |= static_cast<uint8_t>(
759 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
760 }
761 }
762 if (criticalInterface != sensorMap.end())
763 {
764 auto &criticalMap = criticalInterface->second;
765
766 auto criticalHigh = criticalMap.find("CriticalHigh");
767 auto criticalLow = criticalMap.find("CriticalLow");
768
769 if (criticalHigh != criticalMap.end())
770 {
771 resp->assertionEnabledMSB |= static_cast<uint8_t>(
772 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
773 resp->deassertionEnabledMSB |= static_cast<uint8_t>(
774 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
775 }
776 if (criticalLow != criticalMap.end())
777 {
778 resp->assertionEnabledLSB |= static_cast<uint8_t>(
779 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
780 resp->deassertionEnabledLSB |= static_cast<uint8_t>(
781 IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
782 }
783 }
784 *dataLen =
785 sizeof(SensorEventEnableResp); // todo only return needed bytes
786 }
787 // no thresholds enabled
788 else
789 {
790 *dataLen = 1;
791 auto resp = static_cast<uint8_t *>(response);
792 *resp = static_cast<uint8_t>(
793 IPMISensorEventEnableByte2::eventMessagesEnable);
794 *resp |= static_cast<uint8_t>(
795 IPMISensorEventEnableByte2::sensorScanningEnable);
796 }
797 return IPMI_CC_OK;
798}
799
800ipmi_ret_t ipmiSenGetSensorEventStatus(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
801 ipmi_request_t request,
802 ipmi_response_t response,
803 ipmi_data_len_t dataLen,
804 ipmi_context_t context)
805{
806 if (*dataLen != 1)
807 {
808 *dataLen = 0;
809 return IPMI_CC_REQ_DATA_LEN_INVALID;
810 }
811 *dataLen = 0; // default to 0 in case of an error
812
813 uint8_t sensnum = *(static_cast<uint8_t *>(request));
814
815 std::string connection;
816 std::string path;
817
818 auto status = getSensorConnection(sensnum, connection, path);
819 if (status)
820 {
821 return status;
822 }
823
824 SensorMap sensorMap;
825 if (!getSensorMap(connection, path, sensorMap))
826 {
827 return IPMI_CC_RESPONSE_ERROR;
828 }
829
830 auto warningInterface =
831 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
832 auto criticalInterface =
833 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
834
835 // zero out response buff
836 auto responseClear = static_cast<uint8_t *>(response);
837 std::fill(responseClear, responseClear + sizeof(SensorEventStatusResp), 0);
838 auto resp = static_cast<SensorEventStatusResp *>(response);
839 resp->enabled =
840 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
841
James Feist392786a2019-03-19 13:36:10 -0700842 std::optional<bool> criticalDeassertHigh =
843 thresholdDeassertMap[path]["CriticalAlarmHigh"];
844 std::optional<bool> criticalDeassertLow =
845 thresholdDeassertMap[path]["CriticalAlarmLow"];
846 std::optional<bool> warningDeassertHigh =
847 thresholdDeassertMap[path]["WarningAlarmHigh"];
848 std::optional<bool> warningDeassertLow =
849 thresholdDeassertMap[path]["WarningAlarmLow"];
850
851 if (criticalDeassertHigh && !*criticalDeassertHigh)
852 {
853 resp->deassertionsMSB |= static_cast<uint8_t>(
854 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
855 }
856 if (criticalDeassertLow && !*criticalDeassertLow)
857 {
858 resp->deassertionsMSB |= static_cast<uint8_t>(
859 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
860 }
861 if (warningDeassertHigh && !*warningDeassertHigh)
862 {
863 resp->deassertionsLSB |= static_cast<uint8_t>(
864 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
865 }
866 if (warningDeassertLow && !*warningDeassertLow)
867 {
868 resp->deassertionsLSB |= static_cast<uint8_t>(
869 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
870 }
871
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700872 if ((warningInterface != sensorMap.end()) ||
873 (criticalInterface != sensorMap.end()))
874 {
875 resp->enabled = static_cast<uint8_t>(
876 IPMISensorEventEnableByte2::eventMessagesEnable);
877 if (warningInterface != sensorMap.end())
878 {
879 auto &warningMap = warningInterface->second;
880
881 auto warningHigh = warningMap.find("WarningAlarmHigh");
882 auto warningLow = warningMap.find("WarningAlarmLow");
883 auto warningHighAlarm = false;
884 auto warningLowAlarm = false;
885
886 if (warningHigh != warningMap.end())
887 {
James Feist880b7332018-12-06 11:14:02 -0800888 warningHighAlarm = sdbusplus::message::variant_ns::get<bool>(
889 warningHigh->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700890 }
891 if (warningLow != warningMap.end())
892 {
James Feist880b7332018-12-06 11:14:02 -0800893 warningLowAlarm = sdbusplus::message::variant_ns::get<bool>(
894 warningLow->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700895 }
896 if (warningHighAlarm)
897 {
898 resp->assertionsLSB |= static_cast<uint8_t>(
899 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
900 }
901 if (warningLowAlarm)
902 {
903 resp->assertionsLSB |= 1; // lower nc going low
904 }
905 }
906 if (criticalInterface != sensorMap.end())
907 {
908 auto &criticalMap = criticalInterface->second;
909
910 auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
911 auto criticalLow = criticalMap.find("CriticalAlarmLow");
912 auto criticalHighAlarm = false;
913 auto criticalLowAlarm = false;
914
915 if (criticalHigh != criticalMap.end())
916 {
James Feist880b7332018-12-06 11:14:02 -0800917 criticalHighAlarm = sdbusplus::message::variant_ns::get<bool>(
918 criticalHigh->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700919 }
920 if (criticalLow != criticalMap.end())
921 {
James Feist880b7332018-12-06 11:14:02 -0800922 criticalLowAlarm = sdbusplus::message::variant_ns::get<bool>(
923 criticalLow->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700924 }
925 if (criticalHighAlarm)
926 {
927 resp->assertionsMSB |= static_cast<uint8_t>(
928 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
929 }
930 if (criticalLowAlarm)
931 {
932 resp->assertionsLSB |= static_cast<uint8_t>(
933 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
934 }
935 }
936 *dataLen = sizeof(SensorEventStatusResp);
937 }
938
939 // no thresholds enabled, don't need assertionMSB
940 else
941 {
942 *dataLen = sizeof(SensorEventStatusResp) - 1;
943 }
944
945 return IPMI_CC_OK;
946}
947
948/* end sensor commands */
949
950/* storage commands */
951
952ipmi_ret_t ipmiStorageGetSDRRepositoryInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
953 ipmi_request_t request,
954 ipmi_response_t response,
955 ipmi_data_len_t dataLen,
956 ipmi_context_t context)
957{
958 printCommand(+netfn, +cmd);
959
960 if (*dataLen)
961 {
962 *dataLen = 0;
963 return IPMI_CC_REQ_DATA_LEN_INVALID;
964 }
965 *dataLen = 0; // default to 0 in case of an error
966
967 if (sensorTree.empty() && !getSensorSubtree(sensorTree))
968 {
969 return IPMI_CC_RESPONSE_ERROR;
970 }
971
972 // zero out response buff
973 auto responseClear = static_cast<uint8_t *>(response);
974 std::fill(responseClear, responseClear + sizeof(GetSDRInfoResp), 0);
975
976 auto resp = static_cast<GetSDRInfoResp *>(response);
977 resp->sdrVersion = ipmiSdrVersion;
978 uint16_t recordCount = sensorTree.size();
979
980 // todo: for now, sdr count is number of sensors
981 resp->recordCountLS = recordCount & 0xFF;
982 resp->recordCountMS = recordCount >> 8;
983
984 // free space unspcified
985 resp->freeSpace[0] = 0xFF;
986 resp->freeSpace[1] = 0xFF;
987
988 resp->mostRecentAddition = sdrLastAdd;
989 resp->mostRecentErase = sdrLastRemove;
990 resp->operationSupport = static_cast<uint8_t>(
991 SdrRepositoryInfoOps::overflow); // write not supported
992 resp->operationSupport |=
993 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
994 resp->operationSupport |= static_cast<uint8_t>(
995 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
996 *dataLen = sizeof(GetSDRInfoResp);
997 return IPMI_CC_OK;
998}
999
1000ipmi_ret_t ipmiStorageGetSDRAllocationInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1001 ipmi_request_t request,
1002 ipmi_response_t response,
1003 ipmi_data_len_t dataLen,
1004 ipmi_context_t context)
1005{
1006 if (*dataLen)
1007 {
1008 *dataLen = 0;
1009 return IPMI_CC_REQ_DATA_LEN_INVALID;
1010 }
1011 *dataLen = 0; // default to 0 in case of an error
1012 GetAllocInfoResp *resp = static_cast<GetAllocInfoResp *>(response);
1013
1014 // 0000h unspecified number of alloc units
1015 resp->allocUnitsLSB = 0;
1016 resp->allocUnitsMSB = 0;
1017
1018 // max unit size is size of max record
1019 resp->allocUnitSizeLSB = maxSDRTotalSize & 0xFF;
1020 resp->allocUnitSizeMSB = maxSDRTotalSize >> 8;
1021 // read only sdr, no free alloc blocks
1022 resp->allocUnitFreeLSB = 0;
1023 resp->allocUnitFreeMSB = 0;
1024 resp->allocUnitLargestFreeLSB = 0;
1025 resp->allocUnitLargestFreeMSB = 0;
1026 // only allow one block at a time
1027 resp->maxRecordSize = 1;
1028
1029 *dataLen = sizeof(GetAllocInfoResp);
1030
1031 return IPMI_CC_OK;
1032}
1033
1034ipmi_ret_t ipmiStorageReserveSDR(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1035 ipmi_request_t request,
1036 ipmi_response_t response,
1037 ipmi_data_len_t dataLen,
1038 ipmi_context_t context)
1039{
1040 printCommand(+netfn, +cmd);
1041
1042 if (*dataLen)
1043 {
1044 *dataLen = 0;
1045 return IPMI_CC_REQ_DATA_LEN_INVALID;
1046 }
1047 *dataLen = 0; // default to 0 in case of an error
1048 sdrReservationID++;
James Feista80cb902019-02-14 13:05:25 -08001049 if (sdrReservationID == 0)
1050 {
1051 sdrReservationID++;
1052 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001053 *dataLen = 2;
1054 auto resp = static_cast<uint8_t *>(response);
1055 resp[0] = sdrReservationID & 0xFF;
1056 resp[1] = sdrReservationID >> 8;
1057
1058 return IPMI_CC_OK;
1059}
1060
James Feistb49a98a2019-04-16 13:48:09 -07001061ipmi::RspType<uint16_t, // next record ID
1062 std::vector<uint8_t> // payload
1063 >
1064 ipmiStorageGetSDR(uint16_t reservationID, uint16_t recordID, uint8_t offset,
1065 uint8_t bytesToRead)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001066{
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001067 constexpr uint16_t lastRecordIndex = 0xFFFF;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001068
1069 // reservation required for partial reads with non zero offset into
1070 // record
James Feistb49a98a2019-04-16 13:48:09 -07001071 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001072 {
James Feistb49a98a2019-04-16 13:48:09 -07001073 return ipmi::responseInvalidReservationId();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001074 }
1075
1076 if (sensorTree.empty() && !getSensorSubtree(sensorTree))
1077 {
James Feistb49a98a2019-04-16 13:48:09 -07001078 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001079 }
1080
1081 size_t fruCount = 0;
1082 ipmi_ret_t ret = ipmi::storage::getFruSdrCount(fruCount);
1083 if (ret != IPMI_CC_OK)
1084 {
James Feistb49a98a2019-04-16 13:48:09 -07001085 return ipmi::response(ret);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001086 }
1087
1088 size_t lastRecord = sensorTree.size() + fruCount - 1;
James Feistb49a98a2019-04-16 13:48:09 -07001089 if (recordID == lastRecordIndex)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001090 {
James Feistb49a98a2019-04-16 13:48:09 -07001091 recordID = lastRecord;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001092 }
James Feistb49a98a2019-04-16 13:48:09 -07001093 if (recordID > lastRecord)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001094 {
James Feistb49a98a2019-04-16 13:48:09 -07001095 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001096 }
1097
James Feistb49a98a2019-04-16 13:48:09 -07001098 uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001099
James Feistb49a98a2019-04-16 13:48:09 -07001100 if (recordID >= sensorTree.size())
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001101 {
James Feistb49a98a2019-04-16 13:48:09 -07001102 size_t fruIndex = recordID - sensorTree.size();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001103 if (fruIndex >= fruCount)
1104 {
James Feistb49a98a2019-04-16 13:48:09 -07001105 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001106 }
1107 get_sdr::SensorDataFruRecord data;
James Feistb49a98a2019-04-16 13:48:09 -07001108 if (offset > sizeof(data))
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001109 {
James Feistb49a98a2019-04-16 13:48:09 -07001110 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001111 }
1112 ret = ipmi::storage::getFruSdrs(fruIndex, data);
1113 if (ret != IPMI_CC_OK)
1114 {
James Feistb49a98a2019-04-16 13:48:09 -07001115 return ipmi::response(ret);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001116 }
James Feistb49a98a2019-04-16 13:48:09 -07001117 data.header.record_id_msb = recordID << 8;
1118 data.header.record_id_lsb = recordID & 0xFF;
1119 if (sizeof(data) < (offset + bytesToRead))
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001120 {
James Feistb49a98a2019-04-16 13:48:09 -07001121 bytesToRead = sizeof(data) - offset;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001122 }
James Feistb49a98a2019-04-16 13:48:09 -07001123
1124 uint8_t *respStart = reinterpret_cast<uint8_t *>(&data) + offset;
1125 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
1126
1127 return ipmi::responseSuccess(nextRecordId, recordData);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001128 }
1129
1130 std::string connection;
1131 std::string path;
James Feistb49a98a2019-04-16 13:48:09 -07001132 uint16_t sensorIndex = recordID;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001133 for (const auto &sensor : sensorTree)
1134 {
1135 if (sensorIndex-- == 0)
1136 {
1137 if (!sensor.second.size())
1138 {
James Feistb49a98a2019-04-16 13:48:09 -07001139 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001140 }
1141 connection = sensor.second.begin()->first;
1142 path = sensor.first;
1143 break;
1144 }
1145 }
1146
1147 SensorMap sensorMap;
1148 if (!getSensorMap(connection, path, sensorMap))
1149 {
James Feistb49a98a2019-04-16 13:48:09 -07001150 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001151 }
James Feistb49a98a2019-04-16 13:48:09 -07001152 uint8_t sensornumber = (recordID & 0xFF);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001153 get_sdr::SensorDataFullRecord record = {0};
1154
James Feistb49a98a2019-04-16 13:48:09 -07001155 record.header.record_id_msb = recordID << 8;
1156 record.header.record_id_lsb = recordID & 0xFF;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001157 record.header.sdr_version = ipmiSdrVersion;
1158 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1159 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1160 sizeof(get_sdr::SensorDataRecordHeader);
1161 record.key.owner_id = 0x20;
1162 record.key.owner_lun = 0x0;
1163 record.key.sensor_number = sensornumber;
1164
1165 record.body.entity_id = 0x0;
1166 record.body.entity_instance = 0x01;
James Feist7086a882019-03-13 10:46:00 -07001167 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001168 record.body.sensor_type = getSensorTypeFromPath(path);
1169 std::string type = getSensorTypeStringFromPath(path);
1170 auto typeCstr = type.c_str();
1171 auto findUnits = sensorUnits.find(typeCstr);
1172 if (findUnits != sensorUnits.end())
1173 {
1174 record.body.sensor_units_2_base =
1175 static_cast<uint8_t>(findUnits->second);
1176 } // else default 0x0 unspecified
1177
1178 record.body.event_reading_type = getSensorEventTypeFromPath(path);
1179
1180 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
1181 if (sensorObject == sensorMap.end())
1182 {
James Feistb49a98a2019-04-16 13:48:09 -07001183 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001184 }
1185
1186 auto maxObject = sensorObject->second.find("MaxValue");
1187 auto minObject = sensorObject->second.find("MinValue");
1188 double max = 128;
1189 double min = -127;
1190 if (maxObject != sensorObject->second.end())
1191 {
James Feist14fde842018-12-06 10:19:40 -08001192 max = variant_ns::visit(VariantToDoubleVisitor(), maxObject->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001193 }
1194
1195 if (minObject != sensorObject->second.end())
1196 {
James Feist14fde842018-12-06 10:19:40 -08001197 min = variant_ns::visit(VariantToDoubleVisitor(), minObject->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001198 }
1199
1200 int16_t mValue;
1201 int8_t rExp;
1202 int16_t bValue;
1203 int8_t bExp;
1204 bool bSigned;
1205
1206 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1207 {
James Feistb49a98a2019-04-16 13:48:09 -07001208 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001209 }
1210
1211 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1212 record.body.m_lsb = mValue & 0xFF;
1213
1214 // move the smallest bit of the MSB into place (bit 9)
1215 // the MSbs are bits 7:8 in m_msb_and_tolerance
1216 uint8_t mMsb = (mValue & (1 << 8)) > 0 ? (1 << 6) : 0;
1217
1218 // assign the negative
1219 if (mValue < 0)
1220 {
1221 mMsb |= (1 << 7);
1222 }
1223 record.body.m_msb_and_tolerance = mMsb;
1224
1225 record.body.b_lsb = bValue & 0xFF;
1226
1227 // move the smallest bit of the MSB into place
1228 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1229 uint8_t bMsb = (bValue & (1 << 8)) > 0 ? (1 << 6) : 0;
1230
1231 // assign the negative
1232 if (bValue < 0)
1233 {
1234 bMsb |= (1 << 7);
1235 }
1236 record.body.b_msb_and_accuracy_lsb = bMsb;
1237
1238 record.body.r_b_exponents = bExp & 0x7;
1239 if (bExp < 0)
1240 {
1241 record.body.r_b_exponents |= 1 << 3;
1242 }
1243 record.body.r_b_exponents = (rExp & 0x7) << 4;
1244 if (rExp < 0)
1245 {
1246 record.body.r_b_exponents |= 1 << 7;
1247 }
1248
1249 // todo fill out rest of units
1250 if (bSigned)
1251 {
1252 record.body.sensor_units_1 = 1 << 7;
1253 }
1254
1255 // populate sensor name from path
1256 std::string name;
1257 size_t nameStart = path.rfind("/");
1258 if (nameStart != std::string::npos)
1259 {
1260 name = path.substr(nameStart + 1, std::string::npos - nameStart);
1261 }
1262
1263 std::replace(name.begin(), name.end(), '_', ' ');
1264 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
1265 {
1266 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
1267 }
1268 record.body.id_string_info = name.size();
1269 std::strncpy(record.body.id_string, name.c_str(),
1270 sizeof(record.body.id_string));
1271
James Feistb49a98a2019-04-16 13:48:09 -07001272 if (sizeof(get_sdr::SensorDataFullRecord) < (offset + bytesToRead))
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001273 {
James Feistb49a98a2019-04-16 13:48:09 -07001274 bytesToRead = sizeof(get_sdr::SensorDataFullRecord) - offset;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001275 }
1276
James Feistb49a98a2019-04-16 13:48:09 -07001277 uint8_t *respStart = reinterpret_cast<uint8_t *>(&record) + offset;
1278 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001279
James Feistb49a98a2019-04-16 13:48:09 -07001280 return ipmi::responseSuccess(nextRecordId, recordData);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001281}
1282/* end storage commands */
1283
1284void registerSensorFunctions()
1285{
1286 // get firmware version information
1287 ipmiPrintAndRegister(NETFUN_SENSOR, IPMI_CMD_WILDCARD, nullptr,
1288 ipmiSensorWildcardHandler, PRIVILEGE_USER);
1289
1290 // <Get Sensor Type>
1291 ipmiPrintAndRegister(
1292 NETFUN_SENSOR,
1293 static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdGetSensorType),
1294 nullptr, ipmiSensorWildcardHandler, PRIVILEGE_USER);
1295
1296 // <Set Sensor Reading and Event Status>
1297 ipmiPrintAndRegister(
1298 NETFUN_SENSOR,
1299 static_cast<ipmi_cmd_t>(
1300 IPMINetfnSensorCmds::ipmiCmdSetSensorReadingAndEventStatus),
1301 nullptr, ipmiSensorWildcardHandler, PRIVILEGE_OPERATOR);
1302
Jason M. Billsae6bdb12019-04-02 12:00:04 -07001303 // <Platform Event>
1304 ipmi::registerHandler(
1305 ipmi::prioOemBase, ipmi::netFnSensor,
1306 static_cast<ipmi::Cmd>(ipmi::sensor_event::cmdPlatformEvent),
1307 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
1308
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001309 // <Get Sensor Reading>
James Feist0cd014a2019-04-08 15:04:33 -07001310 ipmi::registerHandler(
1311 ipmi::prioOemBase, NETFUN_SENSOR,
1312 static_cast<ipmi::Cmd>(IPMINetfnSensorCmds::ipmiCmdGetSensorReading),
1313 ipmi::Privilege::User, ipmiSenGetSensorReading);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001314
1315 // <Get Sensor Threshold>
1316 ipmiPrintAndRegister(
1317 NETFUN_SENSOR,
1318 static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdGetSensorThreshold),
1319 nullptr, ipmiSenGetSensorThresholds, PRIVILEGE_USER);
1320
1321 ipmiPrintAndRegister(
1322 NETFUN_SENSOR,
1323 static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdSetSensorThreshold),
1324 nullptr, ipmiSenSetSensorThresholds, PRIVILEGE_OPERATOR);
1325
1326 // <Get Sensor Event Enable>
1327 ipmiPrintAndRegister(NETFUN_SENSOR,
1328 static_cast<ipmi_cmd_t>(
1329 IPMINetfnSensorCmds::ipmiCmdGetSensorEventEnable),
1330 nullptr, ipmiSenGetSensorEventEnable, PRIVILEGE_USER);
1331
1332 // <Get Sensor Event Status>
1333 ipmiPrintAndRegister(NETFUN_SENSOR,
1334 static_cast<ipmi_cmd_t>(
1335 IPMINetfnSensorCmds::ipmiCmdGetSensorEventStatus),
1336 nullptr, ipmiSenGetSensorEventStatus, PRIVILEGE_USER);
1337
1338 // register all storage commands for both Sensor and Storage command
1339 // versions
1340
1341 // <Get SDR Repository Info>
1342 ipmiPrintAndRegister(
1343 NETFUN_STORAGE,
1344 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdGetRepositoryInfo),
1345 nullptr, ipmiStorageGetSDRRepositoryInfo, PRIVILEGE_USER);
1346
1347 // <Get SDR Allocation Info>
1348 ipmiPrintAndRegister(NETFUN_STORAGE,
1349 static_cast<ipmi_cmd_t>(
1350 IPMINetfnStorageCmds::ipmiCmdGetSDRAllocationInfo),
1351 nullptr, ipmiStorageGetSDRAllocationInfo,
1352 PRIVILEGE_USER);
1353
1354 // <Reserve SDR Repo>
1355 ipmiPrintAndRegister(NETFUN_SENSOR,
1356 static_cast<ipmi_cmd_t>(
1357 IPMINetfnSensorCmds::ipmiCmdReserveDeviceSDRRepo),
1358 nullptr, ipmiStorageReserveSDR, PRIVILEGE_USER);
1359
1360 ipmiPrintAndRegister(
1361 NETFUN_STORAGE,
1362 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdReserveSDR),
1363 nullptr, ipmiStorageReserveSDR, PRIVILEGE_USER);
1364
1365 // <Get Sdr>
James Feistb49a98a2019-04-16 13:48:09 -07001366 ipmi::registerHandler(
1367 ipmi::prioOemBase, NETFUN_SENSOR,
1368 static_cast<ipmi::Cmd>(IPMINetfnSensorCmds::ipmiCmdGetDeviceSDR),
1369 ipmi::Privilege::User, ipmiStorageGetSDR);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001370
James Feistb49a98a2019-04-16 13:48:09 -07001371 ipmi::registerHandler(
1372 ipmi::prioOemBase, NETFUN_STORAGE,
1373 static_cast<ipmi::Cmd>(IPMINetfnStorageCmds::ipmiCmdGetSDR),
1374 ipmi::Privilege::User, ipmiStorageGetSDR);
1375
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001376 return;
1377}
1378} // namespace ipmi