blob: 844eef89895219b5ae0131460f47c4afd70d511d [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
James Feist902c4c52019-04-16 14:51:31 -0700565IPMIThresholds getIPMIThresholds(const SensorMap &sensorMap)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700566{
James Feist902c4c52019-04-16 14:51:31 -0700567 IPMIThresholds resp;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700568 auto warningInterface =
569 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
570 auto criticalInterface =
571 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
572
573 if ((warningInterface != sensorMap.end()) ||
574 (criticalInterface != sensorMap.end()))
575 {
576 auto sensorPair = sensorMap.find("xyz.openbmc_project.Sensor.Value");
577
578 if (sensorPair == sensorMap.end())
579 {
580 // should not have been able to find a sensor not implementing
581 // the sensor object
James Feist902c4c52019-04-16 14:51:31 -0700582 throw std::runtime_error("Invalid sensor map");
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700583 }
584
585 double max;
586 double min;
587 getSensorMaxMin(sensorPair->second, max, min);
588
589 int16_t mValue = 0;
590 int16_t bValue = 0;
591 int8_t rExp = 0;
592 int8_t bExp = 0;
593 bool bSigned = false;
594
595 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
596 {
James Feist902c4c52019-04-16 14:51:31 -0700597 throw std::runtime_error("Invalid sensor atrributes");
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700598 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700599 if (warningInterface != sensorMap.end())
600 {
601 auto &warningMap = warningInterface->second;
602
603 auto warningHigh = warningMap.find("WarningHigh");
604 auto warningLow = warningMap.find("WarningLow");
605
606 if (warningHigh != warningMap.end())
607 {
James Feist902c4c52019-04-16 14:51:31 -0700608
James Feist14fde842018-12-06 10:19:40 -0800609 double value = variant_ns::visit(VariantToDoubleVisitor(),
610 warningHigh->second);
James Feist902c4c52019-04-16 14:51:31 -0700611 resp.warningHigh = scaleIPMIValueFromDouble(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700612 value, mValue, rExp, bValue, bExp, bSigned);
613 }
614 if (warningLow != warningMap.end())
615 {
James Feist14fde842018-12-06 10:19:40 -0800616 double value = variant_ns::visit(VariantToDoubleVisitor(),
617 warningLow->second);
James Feist902c4c52019-04-16 14:51:31 -0700618 resp.warningLow = scaleIPMIValueFromDouble(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700619 value, mValue, rExp, bValue, bExp, bSigned);
620 }
621 }
622 if (criticalInterface != sensorMap.end())
623 {
624 auto &criticalMap = criticalInterface->second;
625
626 auto criticalHigh = criticalMap.find("CriticalHigh");
627 auto criticalLow = criticalMap.find("CriticalLow");
628
629 if (criticalHigh != criticalMap.end())
630 {
James Feist14fde842018-12-06 10:19:40 -0800631 double value = variant_ns::visit(VariantToDoubleVisitor(),
632 criticalHigh->second);
James Feist902c4c52019-04-16 14:51:31 -0700633 resp.criticalHigh = scaleIPMIValueFromDouble(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700634 value, mValue, rExp, bValue, bExp, bSigned);
635 }
636 if (criticalLow != criticalMap.end())
637 {
James Feist14fde842018-12-06 10:19:40 -0800638 double value = variant_ns::visit(VariantToDoubleVisitor(),
639 criticalLow->second);
James Feist902c4c52019-04-16 14:51:31 -0700640 resp.criticalLow = scaleIPMIValueFromDouble(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700641 value, mValue, rExp, bValue, bExp, bSigned);
642 }
643 }
644 }
James Feist902c4c52019-04-16 14:51:31 -0700645 return resp;
646}
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700647
James Feist902c4c52019-04-16 14:51:31 -0700648ipmi::RspType<uint8_t, // readable
649 uint8_t, // lowerNCrit
650 uint8_t, // lowerCrit
651 uint8_t, // lowerNrecoverable
652 uint8_t, // upperNC
653 uint8_t, // upperCrit
654 uint8_t> // upperNRecoverable
655 ipmiSenGetSensorThresholds(uint8_t sensorNumber)
656{
657 std::string connection;
658 std::string path;
659
660 auto status = getSensorConnection(sensorNumber, connection, path);
661 if (status)
662 {
663 return ipmi::response(status);
664 }
665
666 SensorMap sensorMap;
667 if (!getSensorMap(connection, path, sensorMap))
668 {
669 return ipmi::responseResponseError();
670 }
671
672 IPMIThresholds thresholdData;
673 try
674 {
675 thresholdData = getIPMIThresholds(sensorMap);
676 }
677 catch (std::exception &)
678 {
679 return ipmi::responseResponseError();
680 }
681
682 uint8_t readable = 0;
683 uint8_t lowerNC = 0;
684 uint8_t lowerCritical = 0;
685 uint8_t lowerNonRecoverable = 0;
686 uint8_t upperNC = 0;
687 uint8_t upperCritical = 0;
688 uint8_t upperNonRecoverable = 0;
689
690 if (thresholdData.warningHigh)
691 {
692 readable |=
693 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
694 upperNC = *thresholdData.warningHigh;
695 }
696 if (thresholdData.warningLow)
697 {
698 readable |=
699 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
700 lowerNC = *thresholdData.warningLow;
701 }
702
703 if (thresholdData.criticalHigh)
704 {
705 readable |=
706 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
707 upperCritical = *thresholdData.criticalHigh;
708 }
709 if (thresholdData.criticalLow)
710 {
711 readable |=
712 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
713 lowerCritical = *thresholdData.criticalLow;
714 }
715
716 return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
717 lowerNonRecoverable, upperNC, upperCritical,
718 upperNonRecoverable);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700719}
720
721ipmi_ret_t ipmiSenGetSensorEventEnable(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
722 ipmi_request_t request,
723 ipmi_response_t response,
724 ipmi_data_len_t dataLen,
725 ipmi_context_t context)
726{
727 if (*dataLen != 1)
728 {
729 *dataLen = 0;
730 return IPMI_CC_REQ_DATA_LEN_INVALID;
731 }
732 *dataLen = 0; // default to 0 in case of an error
733
734 uint8_t sensnum = *(static_cast<uint8_t *>(request));
735
736 std::string connection;
737 std::string path;
738
739 auto status = getSensorConnection(sensnum, connection, path);
740 if (status)
741 {
742 return status;
743 }
744
745 SensorMap sensorMap;
746 if (!getSensorMap(connection, path, sensorMap))
747 {
748 return IPMI_CC_RESPONSE_ERROR;
749 }
750
751 auto warningInterface =
752 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
753 auto criticalInterface =
754 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
755
756 if ((warningInterface != sensorMap.end()) ||
757 (criticalInterface != sensorMap.end()))
758 {
759 // zero out response buff
760 auto responseClear = static_cast<uint8_t *>(response);
761 std::fill(responseClear, responseClear + sizeof(SensorEventEnableResp),
762 0);
763
764 // assume all threshold sensors
765 auto resp = static_cast<SensorEventEnableResp *>(response);
766
767 resp->enabled = static_cast<uint8_t>(
768 IPMISensorEventEnableByte2::sensorScanningEnable);
769 if (warningInterface != sensorMap.end())
770 {
771 auto &warningMap = warningInterface->second;
772
773 auto warningHigh = warningMap.find("WarningHigh");
774 auto warningLow = warningMap.find("WarningLow");
775 if (warningHigh != warningMap.end())
776 {
777 resp->assertionEnabledLSB |= static_cast<uint8_t>(
778 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
779 resp->deassertionEnabledLSB |= static_cast<uint8_t>(
780 IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
781 }
782 if (warningLow != warningMap.end())
783 {
784 resp->assertionEnabledLSB |= static_cast<uint8_t>(
785 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
786 resp->deassertionEnabledLSB |= static_cast<uint8_t>(
787 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
788 }
789 }
790 if (criticalInterface != sensorMap.end())
791 {
792 auto &criticalMap = criticalInterface->second;
793
794 auto criticalHigh = criticalMap.find("CriticalHigh");
795 auto criticalLow = criticalMap.find("CriticalLow");
796
797 if (criticalHigh != criticalMap.end())
798 {
799 resp->assertionEnabledMSB |= static_cast<uint8_t>(
800 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
801 resp->deassertionEnabledMSB |= static_cast<uint8_t>(
802 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
803 }
804 if (criticalLow != criticalMap.end())
805 {
806 resp->assertionEnabledLSB |= static_cast<uint8_t>(
807 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
808 resp->deassertionEnabledLSB |= static_cast<uint8_t>(
809 IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
810 }
811 }
812 *dataLen =
813 sizeof(SensorEventEnableResp); // todo only return needed bytes
814 }
815 // no thresholds enabled
816 else
817 {
818 *dataLen = 1;
819 auto resp = static_cast<uint8_t *>(response);
820 *resp = static_cast<uint8_t>(
821 IPMISensorEventEnableByte2::eventMessagesEnable);
822 *resp |= static_cast<uint8_t>(
823 IPMISensorEventEnableByte2::sensorScanningEnable);
824 }
825 return IPMI_CC_OK;
826}
827
828ipmi_ret_t ipmiSenGetSensorEventStatus(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
829 ipmi_request_t request,
830 ipmi_response_t response,
831 ipmi_data_len_t dataLen,
832 ipmi_context_t context)
833{
834 if (*dataLen != 1)
835 {
836 *dataLen = 0;
837 return IPMI_CC_REQ_DATA_LEN_INVALID;
838 }
839 *dataLen = 0; // default to 0 in case of an error
840
841 uint8_t sensnum = *(static_cast<uint8_t *>(request));
842
843 std::string connection;
844 std::string path;
845
846 auto status = getSensorConnection(sensnum, connection, path);
847 if (status)
848 {
849 return status;
850 }
851
852 SensorMap sensorMap;
853 if (!getSensorMap(connection, path, sensorMap))
854 {
855 return IPMI_CC_RESPONSE_ERROR;
856 }
857
858 auto warningInterface =
859 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
860 auto criticalInterface =
861 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
862
863 // zero out response buff
864 auto responseClear = static_cast<uint8_t *>(response);
865 std::fill(responseClear, responseClear + sizeof(SensorEventStatusResp), 0);
866 auto resp = static_cast<SensorEventStatusResp *>(response);
867 resp->enabled =
868 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
869
James Feist392786a2019-03-19 13:36:10 -0700870 std::optional<bool> criticalDeassertHigh =
871 thresholdDeassertMap[path]["CriticalAlarmHigh"];
872 std::optional<bool> criticalDeassertLow =
873 thresholdDeassertMap[path]["CriticalAlarmLow"];
874 std::optional<bool> warningDeassertHigh =
875 thresholdDeassertMap[path]["WarningAlarmHigh"];
876 std::optional<bool> warningDeassertLow =
877 thresholdDeassertMap[path]["WarningAlarmLow"];
878
879 if (criticalDeassertHigh && !*criticalDeassertHigh)
880 {
881 resp->deassertionsMSB |= static_cast<uint8_t>(
882 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
883 }
884 if (criticalDeassertLow && !*criticalDeassertLow)
885 {
886 resp->deassertionsMSB |= static_cast<uint8_t>(
887 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
888 }
889 if (warningDeassertHigh && !*warningDeassertHigh)
890 {
891 resp->deassertionsLSB |= static_cast<uint8_t>(
892 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
893 }
894 if (warningDeassertLow && !*warningDeassertLow)
895 {
896 resp->deassertionsLSB |= static_cast<uint8_t>(
897 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
898 }
899
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700900 if ((warningInterface != sensorMap.end()) ||
901 (criticalInterface != sensorMap.end()))
902 {
903 resp->enabled = static_cast<uint8_t>(
904 IPMISensorEventEnableByte2::eventMessagesEnable);
905 if (warningInterface != sensorMap.end())
906 {
907 auto &warningMap = warningInterface->second;
908
909 auto warningHigh = warningMap.find("WarningAlarmHigh");
910 auto warningLow = warningMap.find("WarningAlarmLow");
911 auto warningHighAlarm = false;
912 auto warningLowAlarm = false;
913
914 if (warningHigh != warningMap.end())
915 {
James Feist880b7332018-12-06 11:14:02 -0800916 warningHighAlarm = sdbusplus::message::variant_ns::get<bool>(
917 warningHigh->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700918 }
919 if (warningLow != warningMap.end())
920 {
James Feist880b7332018-12-06 11:14:02 -0800921 warningLowAlarm = sdbusplus::message::variant_ns::get<bool>(
922 warningLow->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700923 }
924 if (warningHighAlarm)
925 {
926 resp->assertionsLSB |= static_cast<uint8_t>(
927 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
928 }
929 if (warningLowAlarm)
930 {
931 resp->assertionsLSB |= 1; // lower nc going low
932 }
933 }
934 if (criticalInterface != sensorMap.end())
935 {
936 auto &criticalMap = criticalInterface->second;
937
938 auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
939 auto criticalLow = criticalMap.find("CriticalAlarmLow");
940 auto criticalHighAlarm = false;
941 auto criticalLowAlarm = false;
942
943 if (criticalHigh != criticalMap.end())
944 {
James Feist880b7332018-12-06 11:14:02 -0800945 criticalHighAlarm = sdbusplus::message::variant_ns::get<bool>(
946 criticalHigh->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700947 }
948 if (criticalLow != criticalMap.end())
949 {
James Feist880b7332018-12-06 11:14:02 -0800950 criticalLowAlarm = sdbusplus::message::variant_ns::get<bool>(
951 criticalLow->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700952 }
953 if (criticalHighAlarm)
954 {
955 resp->assertionsMSB |= static_cast<uint8_t>(
956 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
957 }
958 if (criticalLowAlarm)
959 {
960 resp->assertionsLSB |= static_cast<uint8_t>(
961 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
962 }
963 }
964 *dataLen = sizeof(SensorEventStatusResp);
965 }
966
967 // no thresholds enabled, don't need assertionMSB
968 else
969 {
970 *dataLen = sizeof(SensorEventStatusResp) - 1;
971 }
972
973 return IPMI_CC_OK;
974}
975
976/* end sensor commands */
977
978/* storage commands */
979
980ipmi_ret_t ipmiStorageGetSDRRepositoryInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
981 ipmi_request_t request,
982 ipmi_response_t response,
983 ipmi_data_len_t dataLen,
984 ipmi_context_t context)
985{
986 printCommand(+netfn, +cmd);
987
988 if (*dataLen)
989 {
990 *dataLen = 0;
991 return IPMI_CC_REQ_DATA_LEN_INVALID;
992 }
993 *dataLen = 0; // default to 0 in case of an error
994
995 if (sensorTree.empty() && !getSensorSubtree(sensorTree))
996 {
997 return IPMI_CC_RESPONSE_ERROR;
998 }
999
1000 // zero out response buff
1001 auto responseClear = static_cast<uint8_t *>(response);
1002 std::fill(responseClear, responseClear + sizeof(GetSDRInfoResp), 0);
1003
1004 auto resp = static_cast<GetSDRInfoResp *>(response);
1005 resp->sdrVersion = ipmiSdrVersion;
1006 uint16_t recordCount = sensorTree.size();
1007
1008 // todo: for now, sdr count is number of sensors
1009 resp->recordCountLS = recordCount & 0xFF;
1010 resp->recordCountMS = recordCount >> 8;
1011
1012 // free space unspcified
1013 resp->freeSpace[0] = 0xFF;
1014 resp->freeSpace[1] = 0xFF;
1015
1016 resp->mostRecentAddition = sdrLastAdd;
1017 resp->mostRecentErase = sdrLastRemove;
1018 resp->operationSupport = static_cast<uint8_t>(
1019 SdrRepositoryInfoOps::overflow); // write not supported
1020 resp->operationSupport |=
1021 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
1022 resp->operationSupport |= static_cast<uint8_t>(
1023 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
1024 *dataLen = sizeof(GetSDRInfoResp);
1025 return IPMI_CC_OK;
1026}
1027
1028ipmi_ret_t ipmiStorageGetSDRAllocationInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1029 ipmi_request_t request,
1030 ipmi_response_t response,
1031 ipmi_data_len_t dataLen,
1032 ipmi_context_t context)
1033{
1034 if (*dataLen)
1035 {
1036 *dataLen = 0;
1037 return IPMI_CC_REQ_DATA_LEN_INVALID;
1038 }
1039 *dataLen = 0; // default to 0 in case of an error
1040 GetAllocInfoResp *resp = static_cast<GetAllocInfoResp *>(response);
1041
1042 // 0000h unspecified number of alloc units
1043 resp->allocUnitsLSB = 0;
1044 resp->allocUnitsMSB = 0;
1045
1046 // max unit size is size of max record
1047 resp->allocUnitSizeLSB = maxSDRTotalSize & 0xFF;
1048 resp->allocUnitSizeMSB = maxSDRTotalSize >> 8;
1049 // read only sdr, no free alloc blocks
1050 resp->allocUnitFreeLSB = 0;
1051 resp->allocUnitFreeMSB = 0;
1052 resp->allocUnitLargestFreeLSB = 0;
1053 resp->allocUnitLargestFreeMSB = 0;
1054 // only allow one block at a time
1055 resp->maxRecordSize = 1;
1056
1057 *dataLen = sizeof(GetAllocInfoResp);
1058
1059 return IPMI_CC_OK;
1060}
1061
1062ipmi_ret_t ipmiStorageReserveSDR(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1063 ipmi_request_t request,
1064 ipmi_response_t response,
1065 ipmi_data_len_t dataLen,
1066 ipmi_context_t context)
1067{
1068 printCommand(+netfn, +cmd);
1069
1070 if (*dataLen)
1071 {
1072 *dataLen = 0;
1073 return IPMI_CC_REQ_DATA_LEN_INVALID;
1074 }
1075 *dataLen = 0; // default to 0 in case of an error
1076 sdrReservationID++;
James Feista80cb902019-02-14 13:05:25 -08001077 if (sdrReservationID == 0)
1078 {
1079 sdrReservationID++;
1080 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001081 *dataLen = 2;
1082 auto resp = static_cast<uint8_t *>(response);
1083 resp[0] = sdrReservationID & 0xFF;
1084 resp[1] = sdrReservationID >> 8;
1085
1086 return IPMI_CC_OK;
1087}
1088
James Feistb49a98a2019-04-16 13:48:09 -07001089ipmi::RspType<uint16_t, // next record ID
1090 std::vector<uint8_t> // payload
1091 >
1092 ipmiStorageGetSDR(uint16_t reservationID, uint16_t recordID, uint8_t offset,
1093 uint8_t bytesToRead)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001094{
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001095 constexpr uint16_t lastRecordIndex = 0xFFFF;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001096
1097 // reservation required for partial reads with non zero offset into
1098 // record
James Feistb49a98a2019-04-16 13:48:09 -07001099 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001100 {
James Feistb49a98a2019-04-16 13:48:09 -07001101 return ipmi::responseInvalidReservationId();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001102 }
1103
1104 if (sensorTree.empty() && !getSensorSubtree(sensorTree))
1105 {
James Feistb49a98a2019-04-16 13:48:09 -07001106 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001107 }
1108
1109 size_t fruCount = 0;
1110 ipmi_ret_t ret = ipmi::storage::getFruSdrCount(fruCount);
1111 if (ret != IPMI_CC_OK)
1112 {
James Feistb49a98a2019-04-16 13:48:09 -07001113 return ipmi::response(ret);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001114 }
1115
1116 size_t lastRecord = sensorTree.size() + fruCount - 1;
James Feistb49a98a2019-04-16 13:48:09 -07001117 if (recordID == lastRecordIndex)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001118 {
James Feistb49a98a2019-04-16 13:48:09 -07001119 recordID = lastRecord;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001120 }
James Feistb49a98a2019-04-16 13:48:09 -07001121 if (recordID > lastRecord)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001122 {
James Feistb49a98a2019-04-16 13:48:09 -07001123 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001124 }
1125
James Feistb49a98a2019-04-16 13:48:09 -07001126 uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001127
James Feistb49a98a2019-04-16 13:48:09 -07001128 if (recordID >= sensorTree.size())
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001129 {
James Feistb49a98a2019-04-16 13:48:09 -07001130 size_t fruIndex = recordID - sensorTree.size();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001131 if (fruIndex >= fruCount)
1132 {
James Feistb49a98a2019-04-16 13:48:09 -07001133 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001134 }
1135 get_sdr::SensorDataFruRecord data;
James Feistb49a98a2019-04-16 13:48:09 -07001136 if (offset > sizeof(data))
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001137 {
James Feistb49a98a2019-04-16 13:48:09 -07001138 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001139 }
1140 ret = ipmi::storage::getFruSdrs(fruIndex, data);
1141 if (ret != IPMI_CC_OK)
1142 {
James Feistb49a98a2019-04-16 13:48:09 -07001143 return ipmi::response(ret);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001144 }
James Feistb49a98a2019-04-16 13:48:09 -07001145 data.header.record_id_msb = recordID << 8;
1146 data.header.record_id_lsb = recordID & 0xFF;
1147 if (sizeof(data) < (offset + bytesToRead))
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001148 {
James Feistb49a98a2019-04-16 13:48:09 -07001149 bytesToRead = sizeof(data) - offset;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001150 }
James Feistb49a98a2019-04-16 13:48:09 -07001151
1152 uint8_t *respStart = reinterpret_cast<uint8_t *>(&data) + offset;
1153 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
1154
1155 return ipmi::responseSuccess(nextRecordId, recordData);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001156 }
1157
1158 std::string connection;
1159 std::string path;
James Feistb49a98a2019-04-16 13:48:09 -07001160 uint16_t sensorIndex = recordID;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001161 for (const auto &sensor : sensorTree)
1162 {
1163 if (sensorIndex-- == 0)
1164 {
1165 if (!sensor.second.size())
1166 {
James Feistb49a98a2019-04-16 13:48:09 -07001167 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001168 }
1169 connection = sensor.second.begin()->first;
1170 path = sensor.first;
1171 break;
1172 }
1173 }
1174
1175 SensorMap sensorMap;
1176 if (!getSensorMap(connection, path, sensorMap))
1177 {
James Feistb49a98a2019-04-16 13:48:09 -07001178 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001179 }
James Feistb49a98a2019-04-16 13:48:09 -07001180 uint8_t sensornumber = (recordID & 0xFF);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001181 get_sdr::SensorDataFullRecord record = {0};
1182
James Feistb49a98a2019-04-16 13:48:09 -07001183 record.header.record_id_msb = recordID << 8;
1184 record.header.record_id_lsb = recordID & 0xFF;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001185 record.header.sdr_version = ipmiSdrVersion;
1186 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1187 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1188 sizeof(get_sdr::SensorDataRecordHeader);
1189 record.key.owner_id = 0x20;
1190 record.key.owner_lun = 0x0;
1191 record.key.sensor_number = sensornumber;
1192
1193 record.body.entity_id = 0x0;
1194 record.body.entity_instance = 0x01;
James Feist7086a882019-03-13 10:46:00 -07001195 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001196 record.body.sensor_type = getSensorTypeFromPath(path);
1197 std::string type = getSensorTypeStringFromPath(path);
1198 auto typeCstr = type.c_str();
1199 auto findUnits = sensorUnits.find(typeCstr);
1200 if (findUnits != sensorUnits.end())
1201 {
1202 record.body.sensor_units_2_base =
1203 static_cast<uint8_t>(findUnits->second);
1204 } // else default 0x0 unspecified
1205
1206 record.body.event_reading_type = getSensorEventTypeFromPath(path);
1207
1208 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
1209 if (sensorObject == sensorMap.end())
1210 {
James Feistb49a98a2019-04-16 13:48:09 -07001211 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001212 }
1213
1214 auto maxObject = sensorObject->second.find("MaxValue");
1215 auto minObject = sensorObject->second.find("MinValue");
1216 double max = 128;
1217 double min = -127;
1218 if (maxObject != sensorObject->second.end())
1219 {
James Feist14fde842018-12-06 10:19:40 -08001220 max = variant_ns::visit(VariantToDoubleVisitor(), maxObject->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001221 }
1222
1223 if (minObject != sensorObject->second.end())
1224 {
James Feist14fde842018-12-06 10:19:40 -08001225 min = variant_ns::visit(VariantToDoubleVisitor(), minObject->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001226 }
1227
1228 int16_t mValue;
1229 int8_t rExp;
1230 int16_t bValue;
1231 int8_t bExp;
1232 bool bSigned;
1233
1234 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1235 {
James Feistb49a98a2019-04-16 13:48:09 -07001236 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001237 }
1238
1239 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1240 record.body.m_lsb = mValue & 0xFF;
1241
1242 // move the smallest bit of the MSB into place (bit 9)
1243 // the MSbs are bits 7:8 in m_msb_and_tolerance
1244 uint8_t mMsb = (mValue & (1 << 8)) > 0 ? (1 << 6) : 0;
1245
1246 // assign the negative
1247 if (mValue < 0)
1248 {
1249 mMsb |= (1 << 7);
1250 }
1251 record.body.m_msb_and_tolerance = mMsb;
1252
1253 record.body.b_lsb = bValue & 0xFF;
1254
1255 // move the smallest bit of the MSB into place
1256 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1257 uint8_t bMsb = (bValue & (1 << 8)) > 0 ? (1 << 6) : 0;
1258
1259 // assign the negative
1260 if (bValue < 0)
1261 {
1262 bMsb |= (1 << 7);
1263 }
1264 record.body.b_msb_and_accuracy_lsb = bMsb;
1265
1266 record.body.r_b_exponents = bExp & 0x7;
1267 if (bExp < 0)
1268 {
1269 record.body.r_b_exponents |= 1 << 3;
1270 }
1271 record.body.r_b_exponents = (rExp & 0x7) << 4;
1272 if (rExp < 0)
1273 {
1274 record.body.r_b_exponents |= 1 << 7;
1275 }
1276
1277 // todo fill out rest of units
1278 if (bSigned)
1279 {
1280 record.body.sensor_units_1 = 1 << 7;
1281 }
1282
1283 // populate sensor name from path
1284 std::string name;
1285 size_t nameStart = path.rfind("/");
1286 if (nameStart != std::string::npos)
1287 {
1288 name = path.substr(nameStart + 1, std::string::npos - nameStart);
1289 }
1290
1291 std::replace(name.begin(), name.end(), '_', ' ');
1292 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
1293 {
1294 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
1295 }
1296 record.body.id_string_info = name.size();
1297 std::strncpy(record.body.id_string, name.c_str(),
1298 sizeof(record.body.id_string));
1299
James Feistb49a98a2019-04-16 13:48:09 -07001300 if (sizeof(get_sdr::SensorDataFullRecord) < (offset + bytesToRead))
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001301 {
James Feistb49a98a2019-04-16 13:48:09 -07001302 bytesToRead = sizeof(get_sdr::SensorDataFullRecord) - offset;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001303 }
1304
James Feistb49a98a2019-04-16 13:48:09 -07001305 uint8_t *respStart = reinterpret_cast<uint8_t *>(&record) + offset;
1306 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001307
James Feistb49a98a2019-04-16 13:48:09 -07001308 return ipmi::responseSuccess(nextRecordId, recordData);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001309}
1310/* end storage commands */
1311
1312void registerSensorFunctions()
1313{
1314 // get firmware version information
1315 ipmiPrintAndRegister(NETFUN_SENSOR, IPMI_CMD_WILDCARD, nullptr,
1316 ipmiSensorWildcardHandler, PRIVILEGE_USER);
1317
1318 // <Get Sensor Type>
1319 ipmiPrintAndRegister(
1320 NETFUN_SENSOR,
1321 static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdGetSensorType),
1322 nullptr, ipmiSensorWildcardHandler, PRIVILEGE_USER);
1323
1324 // <Set Sensor Reading and Event Status>
1325 ipmiPrintAndRegister(
1326 NETFUN_SENSOR,
1327 static_cast<ipmi_cmd_t>(
1328 IPMINetfnSensorCmds::ipmiCmdSetSensorReadingAndEventStatus),
1329 nullptr, ipmiSensorWildcardHandler, PRIVILEGE_OPERATOR);
1330
Jason M. Billsae6bdb12019-04-02 12:00:04 -07001331 // <Platform Event>
1332 ipmi::registerHandler(
1333 ipmi::prioOemBase, ipmi::netFnSensor,
1334 static_cast<ipmi::Cmd>(ipmi::sensor_event::cmdPlatformEvent),
1335 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
1336
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001337 // <Get Sensor Reading>
James Feist0cd014a2019-04-08 15:04:33 -07001338 ipmi::registerHandler(
1339 ipmi::prioOemBase, NETFUN_SENSOR,
1340 static_cast<ipmi::Cmd>(IPMINetfnSensorCmds::ipmiCmdGetSensorReading),
1341 ipmi::Privilege::User, ipmiSenGetSensorReading);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001342
1343 // <Get Sensor Threshold>
James Feist902c4c52019-04-16 14:51:31 -07001344 ipmi::registerHandler(
1345 ipmi::prioOemBase, NETFUN_SENSOR,
1346 static_cast<ipmi::Cmd>(IPMINetfnSensorCmds::ipmiCmdGetSensorThreshold),
1347 ipmi::Privilege::User, ipmiSenGetSensorThresholds);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001348
1349 ipmiPrintAndRegister(
1350 NETFUN_SENSOR,
1351 static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdSetSensorThreshold),
1352 nullptr, ipmiSenSetSensorThresholds, PRIVILEGE_OPERATOR);
1353
1354 // <Get Sensor Event Enable>
1355 ipmiPrintAndRegister(NETFUN_SENSOR,
1356 static_cast<ipmi_cmd_t>(
1357 IPMINetfnSensorCmds::ipmiCmdGetSensorEventEnable),
1358 nullptr, ipmiSenGetSensorEventEnable, PRIVILEGE_USER);
1359
1360 // <Get Sensor Event Status>
1361 ipmiPrintAndRegister(NETFUN_SENSOR,
1362 static_cast<ipmi_cmd_t>(
1363 IPMINetfnSensorCmds::ipmiCmdGetSensorEventStatus),
1364 nullptr, ipmiSenGetSensorEventStatus, PRIVILEGE_USER);
1365
1366 // register all storage commands for both Sensor and Storage command
1367 // versions
1368
1369 // <Get SDR Repository Info>
1370 ipmiPrintAndRegister(
1371 NETFUN_STORAGE,
1372 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdGetRepositoryInfo),
1373 nullptr, ipmiStorageGetSDRRepositoryInfo, PRIVILEGE_USER);
1374
1375 // <Get SDR Allocation Info>
1376 ipmiPrintAndRegister(NETFUN_STORAGE,
1377 static_cast<ipmi_cmd_t>(
1378 IPMINetfnStorageCmds::ipmiCmdGetSDRAllocationInfo),
1379 nullptr, ipmiStorageGetSDRAllocationInfo,
1380 PRIVILEGE_USER);
1381
1382 // <Reserve SDR Repo>
1383 ipmiPrintAndRegister(NETFUN_SENSOR,
1384 static_cast<ipmi_cmd_t>(
1385 IPMINetfnSensorCmds::ipmiCmdReserveDeviceSDRRepo),
1386 nullptr, ipmiStorageReserveSDR, PRIVILEGE_USER);
1387
1388 ipmiPrintAndRegister(
1389 NETFUN_STORAGE,
1390 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdReserveSDR),
1391 nullptr, ipmiStorageReserveSDR, PRIVILEGE_USER);
1392
1393 // <Get Sdr>
James Feistb49a98a2019-04-16 13:48:09 -07001394 ipmi::registerHandler(
1395 ipmi::prioOemBase, NETFUN_SENSOR,
1396 static_cast<ipmi::Cmd>(IPMINetfnSensorCmds::ipmiCmdGetDeviceSDR),
1397 ipmi::Privilege::User, ipmiStorageGetSDR);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001398
James Feistb49a98a2019-04-16 13:48:09 -07001399 ipmi::registerHandler(
1400 ipmi::prioOemBase, NETFUN_STORAGE,
1401 static_cast<ipmi::Cmd>(IPMINetfnStorageCmds::ipmiCmdGetSDR),
1402 ipmi::Privilege::User, ipmiStorageGetSDR);
1403
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001404 return;
1405}
1406} // namespace ipmi