blob: ab654f7addb048c7d99855fc0287fd6de92dc854 [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>
Jason M. Bills99b78ec2019-01-18 10:42:18 -080023#include <ipmi_to_redfish_hooks.hpp>
James Feist2a265d52019-04-08 11:16:27 -070024#include <ipmid/api.hpp>
Vernon Mauery5480ef62019-03-20 13:43:11 -070025#include <ipmid/utils.hpp>
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070026#include <phosphor-logging/log.hpp>
27#include <sdbusplus/bus.hpp>
28#include <sdrutils.hpp>
29#include <sensorcommands.hpp>
30#include <sensorutils.hpp>
31#include <storagecommands.hpp>
32#include <string>
33
34namespace ipmi
35{
36using ManagedObjectType =
37 std::map<sdbusplus::message::object_path,
38 std::map<std::string, std::map<std::string, DbusVariant>>>;
39
40using SensorMap = std::map<std::string, std::map<std::string, DbusVariant>>;
James Feist14fde842018-12-06 10:19:40 -080041namespace variant_ns = sdbusplus::message::variant_ns;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070042
43static constexpr int sensorListUpdatePeriod = 10;
44static constexpr int sensorMapUpdatePeriod = 2;
45
46constexpr size_t maxSDRTotalSize =
47 76; // Largest SDR Record Size (type 01) + SDR Overheader Size
48constexpr static const uint32_t noTimestamp = 0xFFFFFFFF;
49
50static uint16_t sdrReservationID;
51static uint32_t sdrLastAdd = noTimestamp;
52static uint32_t sdrLastRemove = noTimestamp;
53
Richard Marian Thomaiyar01fbcb52018-11-19 22:04:34 +053054SensorSubTree sensorTree;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070055static boost::container::flat_map<std::string, ManagedObjectType> SensorCache;
56
Jason M. Bills17add592018-11-12 14:30:12 -080057// Specify the comparison required to sort and find char* map objects
58struct CmpStr
59{
60 bool operator()(const char *a, const char *b) const
61 {
62 return std::strcmp(a, b) < 0;
63 }
64};
65const static boost::container::flat_map<const char *, SensorUnits, CmpStr>
66 sensorUnits{{{"temperature", SensorUnits::degreesC},
67 {"voltage", SensorUnits::volts},
68 {"current", SensorUnits::amps},
69 {"fan_tach", SensorUnits::rpm},
70 {"power", SensorUnits::watts}}};
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070071
72void registerSensorFunctions() __attribute__((constructor));
73static sdbusplus::bus::bus dbus(ipmid_get_sd_bus_connection());
74
75static sdbusplus::bus::match::match sensorAdded(
76 dbus,
77 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
78 "sensors/'",
79 [](sdbusplus::message::message &m) {
80 sensorTree.clear();
81 sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>(
82 std::chrono::system_clock::now().time_since_epoch())
83 .count();
84 });
85
86static sdbusplus::bus::match::match sensorRemoved(
87 dbus,
88 "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/"
89 "sensors/'",
90 [](sdbusplus::message::message &m) {
91 sensorTree.clear();
92 sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>(
93 std::chrono::system_clock::now().time_since_epoch())
94 .count();
95 });
96
James Feist392786a2019-03-19 13:36:10 -070097// this keeps track of deassertions for sensor event status command. A
98// deasertion can only happen if an assertion was seen first.
99static boost::container::flat_map<
100 std::string, boost::container::flat_map<std::string, std::optional<bool>>>
101 thresholdDeassertMap;
102
103static sdbusplus::bus::match::match thresholdChanged(
104 dbus,
105 "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus."
106 "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
107 [](sdbusplus::message::message &m) {
108 boost::container::flat_map<std::string, std::variant<bool, double>>
109 values;
110 m.read(std::string(), values);
111
112 auto findAssert =
113 std::find_if(values.begin(), values.end(), [](const auto &pair) {
114 return pair.first.find("Alarm") != std::string::npos;
115 });
116 if (findAssert != values.end())
117 {
118 auto ptr = std::get_if<bool>(&(findAssert->second));
119 if (ptr == nullptr)
120 {
121 phosphor::logging::log<phosphor::logging::level::ERR>(
122 "thresholdChanged: Assert non bool");
123 return;
124 }
125 if (*ptr)
126 {
127 phosphor::logging::log<phosphor::logging::level::INFO>(
128 "thresholdChanged: Assert",
129 phosphor::logging::entry("SENSOR=%s", m.get_path()));
130 thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr;
131 }
132 else
133 {
134 auto &value =
135 thresholdDeassertMap[m.get_path()][findAssert->first];
136 if (value)
137 {
138 phosphor::logging::log<phosphor::logging::level::INFO>(
139 "thresholdChanged: deassert",
140 phosphor::logging::entry("SENSOR=%s", m.get_path()));
141 value = *ptr;
142 }
143 }
144 }
145 });
146
James Feistaecaef72019-04-26 10:30:32 -0700147static void getSensorMaxMin(const SensorMap &sensorMap, double &max,
148 double &min)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700149{
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700150 max = 127;
151 min = -128;
152
James Feistaecaef72019-04-26 10:30:32 -0700153 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
154 auto critical =
155 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
156 auto warning =
157 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
158
159 if (sensorObject != sensorMap.end())
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700160 {
James Feistaecaef72019-04-26 10:30:32 -0700161 auto maxMap = sensorObject->second.find("MaxValue");
162 auto minMap = sensorObject->second.find("MinValue");
163
164 if (maxMap != sensorObject->second.end())
165 {
166 max = variant_ns::visit(VariantToDoubleVisitor(), maxMap->second);
167 }
168 if (minMap != sensorObject->second.end())
169 {
170 min = variant_ns::visit(VariantToDoubleVisitor(), minMap->second);
171 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700172 }
James Feistaecaef72019-04-26 10:30:32 -0700173 if (critical != sensorMap.end())
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700174 {
James Feistaecaef72019-04-26 10:30:32 -0700175 auto lower = critical->second.find("CriticalLow");
176 auto upper = critical->second.find("CriticalHigh");
177 if (lower != critical->second.end())
178 {
179 double value =
180 variant_ns::visit(VariantToDoubleVisitor(), lower->second);
181 min = std::min(value, min);
182 }
183 if (upper != critical->second.end())
184 {
185 double value =
186 variant_ns::visit(VariantToDoubleVisitor(), upper->second);
187 max = std::max(value, max);
188 }
189 }
190 if (warning != sensorMap.end())
191 {
192
193 auto lower = warning->second.find("WarningLow");
194 auto upper = warning->second.find("WarningHigh");
195 if (lower != warning->second.end())
196 {
197 double value =
198 variant_ns::visit(VariantToDoubleVisitor(), lower->second);
199 min = std::min(value, min);
200 }
201 if (upper != warning->second.end())
202 {
203 double value =
204 variant_ns::visit(VariantToDoubleVisitor(), upper->second);
205 max = std::max(value, max);
206 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700207 }
208}
209
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700210static bool getSensorMap(std::string sensorConnection, std::string sensorPath,
211 SensorMap &sensorMap)
212{
213 static boost::container::flat_map<
214 std::string, std::chrono::time_point<std::chrono::steady_clock>>
215 updateTimeMap;
216
217 auto updateFind = updateTimeMap.find(sensorConnection);
218 auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
219 if (updateFind != updateTimeMap.end())
220 {
221 lastUpdate = updateFind->second;
222 }
223
224 auto now = std::chrono::steady_clock::now();
225
226 if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
227 .count() > sensorMapUpdatePeriod)
228 {
229 updateTimeMap[sensorConnection] = now;
230
231 auto managedObj = dbus.new_method_call(
232 sensorConnection.c_str(), "/", "org.freedesktop.DBus.ObjectManager",
233 "GetManagedObjects");
234
235 ManagedObjectType managedObjects;
236 try
237 {
238 auto reply = dbus.call(managedObj);
239 reply.read(managedObjects);
240 }
241 catch (sdbusplus::exception_t &)
242 {
243 phosphor::logging::log<phosphor::logging::level::ERR>(
244 "Error getting managed objects from connection",
245 phosphor::logging::entry("CONNECTION=%s",
246 sensorConnection.c_str()));
247 return false;
248 }
249
250 SensorCache[sensorConnection] = managedObjects;
251 }
252 auto connection = SensorCache.find(sensorConnection);
253 if (connection == SensorCache.end())
254 {
255 return false;
256 }
257 auto path = connection->second.find(sensorPath);
258 if (path == connection->second.end())
259 {
260 return false;
261 }
262 sensorMap = path->second;
263
264 return true;
265}
266
267/* sensor commands */
268ipmi_ret_t ipmiSensorWildcardHandler(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
269 ipmi_request_t request,
270 ipmi_response_t response,
271 ipmi_data_len_t dataLen,
272 ipmi_context_t context)
273{
274 *dataLen = 0;
275 printCommand(+netfn, +cmd);
276 return IPMI_CC_INVALID;
277}
278
Jason M. Billsae6bdb12019-04-02 12:00:04 -0700279ipmi::RspType<> ipmiSenPlatformEvent(ipmi::message::Payload &p)
280{
281 uint8_t generatorID = 0;
282 uint8_t evmRev = 0;
283 uint8_t sensorType = 0;
284 uint8_t sensorNum = 0;
285 uint8_t eventType = 0;
286 uint8_t eventData1 = 0;
287 std::optional<uint8_t> eventData2 = 0;
288 std::optional<uint8_t> eventData3 = 0;
289
290 // todo: This check is supposed to be based on the incoming channel.
291 // e.g. system channel will provide upto 8 bytes including generator
292 // ID, but ipmb channel will provide only up to 7 bytes without the
293 // generator ID.
294 // Support for this check is coming in future patches, so for now just base
295 // it on if the first byte is the EvMRev (0x04).
296 if (p.size() && p.data()[0] == 0x04)
297 {
298 p.unpack(evmRev, sensorType, sensorNum, eventType, eventData1,
299 eventData2, eventData3);
300 // todo: the generator ID for this channel is supposed to come from the
301 // IPMB requesters slave address. Support for this is coming in future
302 // patches, so for now just assume it is coming from the ME (0x2C).
303 generatorID = 0x2C;
304 }
305 else
306 {
307 p.unpack(generatorID, evmRev, sensorType, sensorNum, eventType,
308 eventData1, eventData2, eventData3);
309 }
310 if (!p.fullyUnpacked())
311 {
312 return ipmi::responseReqDataLenInvalid();
313 }
314
Jason M. Bills6dd8f042019-04-11 10:39:02 -0700315 // Send this request to the Redfish hooks to log it as a Redfish message
316 // instead. There is no need to add it to the SEL, so just return success.
317 intel_oem::ipmi::sel::checkRedfishHooks(
318 generatorID, evmRev, sensorType, sensorNum, eventType, eventData1,
319 eventData2.value_or(0xFF), eventData3.value_or(0xFF));
Jason M. Billsae6bdb12019-04-02 12:00:04 -0700320
321 return ipmi::responseSuccess();
322}
323
James Feist0cd014a2019-04-08 15:04:33 -0700324ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
325 ipmiSenGetSensorReading(uint8_t sensnum)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700326{
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700327 std::string connection;
328 std::string path;
329
330 auto status = getSensorConnection(sensnum, connection, path);
331 if (status)
332 {
James Feist0cd014a2019-04-08 15:04:33 -0700333 return ipmi::response(status);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700334 }
335
336 SensorMap sensorMap;
337 if (!getSensorMap(connection, path, sensorMap))
338 {
James Feist0cd014a2019-04-08 15:04:33 -0700339 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700340 }
341 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
342
343 if (sensorObject == sensorMap.end() ||
344 sensorObject->second.find("Value") == sensorObject->second.end())
345 {
James Feist0cd014a2019-04-08 15:04:33 -0700346 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700347 }
James Feist0cd014a2019-04-08 15:04:33 -0700348 auto &valueVariant = sensorObject->second["Value"];
349 double reading = variant_ns::visit(VariantToDoubleVisitor(), valueVariant);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700350
Yong Li1f2eb5e2019-05-23 14:07:17 +0800351 double max = 0;
352 double min = 0;
James Feistaecaef72019-04-26 10:30:32 -0700353 getSensorMaxMin(sensorMap, max, min);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700354
355 int16_t mValue = 0;
356 int16_t bValue = 0;
357 int8_t rExp = 0;
358 int8_t bExp = 0;
359 bool bSigned = false;
360
361 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
362 {
James Feist0cd014a2019-04-08 15:04:33 -0700363 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700364 }
365
James Feist0cd014a2019-04-08 15:04:33 -0700366 uint8_t value =
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700367 scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
James Feist0cd014a2019-04-08 15:04:33 -0700368 uint8_t operation =
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700369 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
James Feist0cd014a2019-04-08 15:04:33 -0700370 operation |=
James Feist81a95c12019-03-01 15:08:28 -0800371 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700372
James Feist0cd014a2019-04-08 15:04:33 -0700373 uint8_t thresholds = 0;
374
375 auto warningObject =
376 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
377 if (warningObject != sensorMap.end())
378 {
379 auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
380 auto alarmLow = warningObject->second.find("WarningAlarmLow");
381 if (alarmHigh != warningObject->second.end())
382 {
383 if (std::get<bool>(alarmHigh->second))
384 {
385 thresholds |= static_cast<uint8_t>(
386 IPMISensorReadingByte3::upperNonCritical);
387 }
388 }
389 if (alarmLow != warningObject->second.end())
390 {
391 if (std::get<bool>(alarmLow->second))
392 {
393 thresholds |= static_cast<uint8_t>(
394 IPMISensorReadingByte3::lowerNonCritical);
395 }
396 }
397 }
398
399 auto criticalObject =
400 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
401 if (criticalObject != sensorMap.end())
402 {
403 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
404 auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
405 if (alarmHigh != criticalObject->second.end())
406 {
407 if (std::get<bool>(alarmHigh->second))
408 {
409 thresholds |=
410 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
411 }
412 }
413 if (alarmLow != criticalObject->second.end())
414 {
415 if (std::get<bool>(alarmLow->second))
416 {
417 thresholds |=
418 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
419 }
420 }
421 }
422
423 // no discrete as of today so optional byte is never returned
424 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700425}
426
427ipmi_ret_t ipmiSenSetSensorThresholds(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
428 ipmi_request_t request,
429 ipmi_response_t response,
430 ipmi_data_len_t dataLen,
431 ipmi_context_t context)
432{
433 if (*dataLen != 8)
434 {
435 *dataLen = 0;
436 return IPMI_CC_REQ_DATA_LEN_INVALID;
437 }
438 *dataLen = 0;
439
440 SensorThresholdReq *req = static_cast<SensorThresholdReq *>(request);
441
442 // upper two bits reserved
443 if (req->mask & 0xC0)
444 {
445 return IPMI_CC_INVALID_FIELD_REQUEST;
446 }
447
448 // lower nc and upper nc not suppported on any sensor
449 if ((req->mask & static_cast<uint8_t>(
450 SensorThresholdReqEnable::setLowerNonRecoverable)) ||
451 (req->mask & static_cast<uint8_t>(
452 SensorThresholdReqEnable::setUpperNonRecoverable)))
453 {
454 return IPMI_CC_INVALID_FIELD_REQUEST;
455 }
456
457 // if no bits are set in the mask, nothing to do
458 if (!(req->mask))
459 {
460 return IPMI_CC_OK;
461 }
462
463 std::string connection;
464 std::string path;
465
466 ipmi_ret_t status = getSensorConnection(req->sensorNum, connection, path);
467 if (status)
468 {
469 return status;
470 }
471 SensorMap sensorMap;
472 if (!getSensorMap(connection, path, sensorMap))
473 {
474 return IPMI_CC_RESPONSE_ERROR;
475 }
476
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700477 double max = 0;
478 double min = 0;
James Feistaecaef72019-04-26 10:30:32 -0700479 getSensorMaxMin(sensorMap, max, min);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700480
481 int16_t mValue = 0;
482 int16_t bValue = 0;
483 int8_t rExp = 0;
484 int8_t bExp = 0;
485 bool bSigned = false;
486
487 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
488 {
489 return IPMI_CC_RESPONSE_ERROR;
490 }
491
492 bool setLowerCritical =
493 req->mask &
494 static_cast<uint8_t>(SensorThresholdReqEnable::setLowerCritical);
495 bool setUpperCritical =
496 req->mask &
497 static_cast<uint8_t>(SensorThresholdReqEnable::setUpperCritical);
498
499 bool setLowerWarning =
500 req->mask &
501 static_cast<uint8_t>(SensorThresholdReqEnable::setLowerNonCritical);
502 bool setUpperWarning =
503 req->mask &
504 static_cast<uint8_t>(SensorThresholdReqEnable::setUpperNonCritical);
505
506 // store a vector of property name, value to set, and interface
507 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
508
509 // define the indexes of the tuple
510 constexpr uint8_t propertyName = 0;
511 constexpr uint8_t thresholdValue = 1;
512 constexpr uint8_t interface = 2;
513 // verifiy all needed fields are present
514 if (setLowerCritical || setUpperCritical)
515 {
516 auto findThreshold =
517 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
518 if (findThreshold == sensorMap.end())
519 {
520 return IPMI_CC_INVALID_FIELD_REQUEST;
521 }
522 if (setLowerCritical)
523 {
524 auto findLower = findThreshold->second.find("CriticalLow");
525 if (findLower == findThreshold->second.end())
526 {
527 return IPMI_CC_INVALID_FIELD_REQUEST;
528 }
529 thresholdsToSet.emplace_back("CriticalLow", req->lowerCritical,
530 findThreshold->first);
531 }
532 if (setUpperCritical)
533 {
534 auto findUpper = findThreshold->second.find("CriticalHigh");
535 if (findUpper == findThreshold->second.end())
536 {
537 return IPMI_CC_INVALID_FIELD_REQUEST;
538 }
539 thresholdsToSet.emplace_back("CriticalHigh", req->upperCritical,
540 findThreshold->first);
541 }
542 }
543 if (setLowerWarning || setUpperWarning)
544 {
545 auto findThreshold =
546 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
547 if (findThreshold == sensorMap.end())
548 {
549 return IPMI_CC_INVALID_FIELD_REQUEST;
550 }
551 if (setLowerWarning)
552 {
553 auto findLower = findThreshold->second.find("WarningLow");
554 if (findLower == findThreshold->second.end())
555 {
556 return IPMI_CC_INVALID_FIELD_REQUEST;
557 }
558 thresholdsToSet.emplace_back("WarningLow", req->lowerNonCritical,
559 findThreshold->first);
560 }
561 if (setUpperWarning)
562 {
563 auto findUpper = findThreshold->second.find("WarningHigh");
564 if (findUpper == findThreshold->second.end())
565 {
566 return IPMI_CC_INVALID_FIELD_REQUEST;
567 }
568 thresholdsToSet.emplace_back("WarningHigh", req->upperNonCritical,
569 findThreshold->first);
570 }
571 }
572
573 for (const auto &property : thresholdsToSet)
574 {
575 // from section 36.3 in the IPMI Spec, assume all linear
576 double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
577 (bValue * std::pow(10, bExp))) *
578 std::pow(10, rExp);
579 setDbusProperty(dbus, connection, path, std::get<interface>(property),
580 std::get<propertyName>(property),
581 ipmi::Value(valueToSet));
582 }
583
584 return IPMI_CC_OK;
585}
586
James Feist902c4c52019-04-16 14:51:31 -0700587IPMIThresholds getIPMIThresholds(const SensorMap &sensorMap)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700588{
James Feist902c4c52019-04-16 14:51:31 -0700589 IPMIThresholds resp;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700590 auto warningInterface =
591 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
592 auto criticalInterface =
593 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
594
595 if ((warningInterface != sensorMap.end()) ||
596 (criticalInterface != sensorMap.end()))
597 {
598 auto sensorPair = sensorMap.find("xyz.openbmc_project.Sensor.Value");
599
600 if (sensorPair == sensorMap.end())
601 {
602 // should not have been able to find a sensor not implementing
603 // the sensor object
James Feist902c4c52019-04-16 14:51:31 -0700604 throw std::runtime_error("Invalid sensor map");
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700605 }
606
607 double max;
608 double min;
James Feistaecaef72019-04-26 10:30:32 -0700609 getSensorMaxMin(sensorMap, max, min);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700610
611 int16_t mValue = 0;
612 int16_t bValue = 0;
613 int8_t rExp = 0;
614 int8_t bExp = 0;
615 bool bSigned = false;
616
617 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
618 {
James Feist902c4c52019-04-16 14:51:31 -0700619 throw std::runtime_error("Invalid sensor atrributes");
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700620 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700621 if (warningInterface != sensorMap.end())
622 {
623 auto &warningMap = warningInterface->second;
624
625 auto warningHigh = warningMap.find("WarningHigh");
626 auto warningLow = warningMap.find("WarningLow");
627
628 if (warningHigh != warningMap.end())
629 {
James Feist902c4c52019-04-16 14:51:31 -0700630
James Feist14fde842018-12-06 10:19:40 -0800631 double value = variant_ns::visit(VariantToDoubleVisitor(),
632 warningHigh->second);
James Feist902c4c52019-04-16 14:51:31 -0700633 resp.warningHigh = scaleIPMIValueFromDouble(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700634 value, mValue, rExp, bValue, bExp, bSigned);
635 }
636 if (warningLow != warningMap.end())
637 {
James Feist14fde842018-12-06 10:19:40 -0800638 double value = variant_ns::visit(VariantToDoubleVisitor(),
639 warningLow->second);
James Feist902c4c52019-04-16 14:51:31 -0700640 resp.warningLow = scaleIPMIValueFromDouble(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700641 value, mValue, rExp, bValue, bExp, bSigned);
642 }
643 }
644 if (criticalInterface != sensorMap.end())
645 {
646 auto &criticalMap = criticalInterface->second;
647
648 auto criticalHigh = criticalMap.find("CriticalHigh");
649 auto criticalLow = criticalMap.find("CriticalLow");
650
651 if (criticalHigh != criticalMap.end())
652 {
James Feist14fde842018-12-06 10:19:40 -0800653 double value = variant_ns::visit(VariantToDoubleVisitor(),
654 criticalHigh->second);
James Feist902c4c52019-04-16 14:51:31 -0700655 resp.criticalHigh = scaleIPMIValueFromDouble(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700656 value, mValue, rExp, bValue, bExp, bSigned);
657 }
658 if (criticalLow != criticalMap.end())
659 {
James Feist14fde842018-12-06 10:19:40 -0800660 double value = variant_ns::visit(VariantToDoubleVisitor(),
661 criticalLow->second);
James Feist902c4c52019-04-16 14:51:31 -0700662 resp.criticalLow = scaleIPMIValueFromDouble(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700663 value, mValue, rExp, bValue, bExp, bSigned);
664 }
665 }
666 }
James Feist902c4c52019-04-16 14:51:31 -0700667 return resp;
668}
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700669
James Feist902c4c52019-04-16 14:51:31 -0700670ipmi::RspType<uint8_t, // readable
671 uint8_t, // lowerNCrit
672 uint8_t, // lowerCrit
673 uint8_t, // lowerNrecoverable
674 uint8_t, // upperNC
675 uint8_t, // upperCrit
676 uint8_t> // upperNRecoverable
677 ipmiSenGetSensorThresholds(uint8_t sensorNumber)
678{
679 std::string connection;
680 std::string path;
681
682 auto status = getSensorConnection(sensorNumber, connection, path);
683 if (status)
684 {
685 return ipmi::response(status);
686 }
687
688 SensorMap sensorMap;
689 if (!getSensorMap(connection, path, sensorMap))
690 {
691 return ipmi::responseResponseError();
692 }
693
694 IPMIThresholds thresholdData;
695 try
696 {
697 thresholdData = getIPMIThresholds(sensorMap);
698 }
699 catch (std::exception &)
700 {
701 return ipmi::responseResponseError();
702 }
703
704 uint8_t readable = 0;
705 uint8_t lowerNC = 0;
706 uint8_t lowerCritical = 0;
707 uint8_t lowerNonRecoverable = 0;
708 uint8_t upperNC = 0;
709 uint8_t upperCritical = 0;
710 uint8_t upperNonRecoverable = 0;
711
712 if (thresholdData.warningHigh)
713 {
714 readable |=
715 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
716 upperNC = *thresholdData.warningHigh;
717 }
718 if (thresholdData.warningLow)
719 {
720 readable |=
721 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
722 lowerNC = *thresholdData.warningLow;
723 }
724
725 if (thresholdData.criticalHigh)
726 {
727 readable |=
728 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
729 upperCritical = *thresholdData.criticalHigh;
730 }
731 if (thresholdData.criticalLow)
732 {
733 readable |=
734 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
735 lowerCritical = *thresholdData.criticalLow;
736 }
737
738 return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
739 lowerNonRecoverable, upperNC, upperCritical,
740 upperNonRecoverable);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700741}
742
743ipmi_ret_t ipmiSenGetSensorEventEnable(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
744 ipmi_request_t request,
745 ipmi_response_t response,
746 ipmi_data_len_t dataLen,
747 ipmi_context_t context)
748{
749 if (*dataLen != 1)
750 {
751 *dataLen = 0;
752 return IPMI_CC_REQ_DATA_LEN_INVALID;
753 }
754 *dataLen = 0; // default to 0 in case of an error
755
756 uint8_t sensnum = *(static_cast<uint8_t *>(request));
757
758 std::string connection;
759 std::string path;
760
761 auto status = getSensorConnection(sensnum, connection, path);
762 if (status)
763 {
764 return status;
765 }
766
767 SensorMap sensorMap;
768 if (!getSensorMap(connection, path, sensorMap))
769 {
770 return IPMI_CC_RESPONSE_ERROR;
771 }
772
773 auto warningInterface =
774 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
775 auto criticalInterface =
776 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
777
778 if ((warningInterface != sensorMap.end()) ||
779 (criticalInterface != sensorMap.end()))
780 {
781 // zero out response buff
782 auto responseClear = static_cast<uint8_t *>(response);
783 std::fill(responseClear, responseClear + sizeof(SensorEventEnableResp),
784 0);
785
786 // assume all threshold sensors
787 auto resp = static_cast<SensorEventEnableResp *>(response);
788
789 resp->enabled = static_cast<uint8_t>(
790 IPMISensorEventEnableByte2::sensorScanningEnable);
791 if (warningInterface != sensorMap.end())
792 {
793 auto &warningMap = warningInterface->second;
794
795 auto warningHigh = warningMap.find("WarningHigh");
796 auto warningLow = warningMap.find("WarningLow");
797 if (warningHigh != warningMap.end())
798 {
799 resp->assertionEnabledLSB |= static_cast<uint8_t>(
800 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
801 resp->deassertionEnabledLSB |= static_cast<uint8_t>(
802 IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
803 }
804 if (warningLow != warningMap.end())
805 {
806 resp->assertionEnabledLSB |= static_cast<uint8_t>(
807 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
808 resp->deassertionEnabledLSB |= static_cast<uint8_t>(
809 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
810 }
811 }
812 if (criticalInterface != sensorMap.end())
813 {
814 auto &criticalMap = criticalInterface->second;
815
816 auto criticalHigh = criticalMap.find("CriticalHigh");
817 auto criticalLow = criticalMap.find("CriticalLow");
818
819 if (criticalHigh != criticalMap.end())
820 {
821 resp->assertionEnabledMSB |= static_cast<uint8_t>(
822 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
823 resp->deassertionEnabledMSB |= static_cast<uint8_t>(
824 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
825 }
826 if (criticalLow != criticalMap.end())
827 {
828 resp->assertionEnabledLSB |= static_cast<uint8_t>(
829 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
830 resp->deassertionEnabledLSB |= static_cast<uint8_t>(
831 IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
832 }
833 }
834 *dataLen =
835 sizeof(SensorEventEnableResp); // todo only return needed bytes
836 }
837 // no thresholds enabled
838 else
839 {
840 *dataLen = 1;
841 auto resp = static_cast<uint8_t *>(response);
842 *resp = static_cast<uint8_t>(
843 IPMISensorEventEnableByte2::eventMessagesEnable);
844 *resp |= static_cast<uint8_t>(
845 IPMISensorEventEnableByte2::sensorScanningEnable);
846 }
847 return IPMI_CC_OK;
848}
849
850ipmi_ret_t ipmiSenGetSensorEventStatus(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
851 ipmi_request_t request,
852 ipmi_response_t response,
853 ipmi_data_len_t dataLen,
854 ipmi_context_t context)
855{
856 if (*dataLen != 1)
857 {
858 *dataLen = 0;
859 return IPMI_CC_REQ_DATA_LEN_INVALID;
860 }
861 *dataLen = 0; // default to 0 in case of an error
862
863 uint8_t sensnum = *(static_cast<uint8_t *>(request));
864
865 std::string connection;
866 std::string path;
867
868 auto status = getSensorConnection(sensnum, connection, path);
869 if (status)
870 {
871 return status;
872 }
873
874 SensorMap sensorMap;
875 if (!getSensorMap(connection, path, sensorMap))
876 {
877 return IPMI_CC_RESPONSE_ERROR;
878 }
879
880 auto warningInterface =
881 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
882 auto criticalInterface =
883 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
884
885 // zero out response buff
886 auto responseClear = static_cast<uint8_t *>(response);
887 std::fill(responseClear, responseClear + sizeof(SensorEventStatusResp), 0);
888 auto resp = static_cast<SensorEventStatusResp *>(response);
889 resp->enabled =
890 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
891
James Feist392786a2019-03-19 13:36:10 -0700892 std::optional<bool> criticalDeassertHigh =
893 thresholdDeassertMap[path]["CriticalAlarmHigh"];
894 std::optional<bool> criticalDeassertLow =
895 thresholdDeassertMap[path]["CriticalAlarmLow"];
896 std::optional<bool> warningDeassertHigh =
897 thresholdDeassertMap[path]["WarningAlarmHigh"];
898 std::optional<bool> warningDeassertLow =
899 thresholdDeassertMap[path]["WarningAlarmLow"];
900
901 if (criticalDeassertHigh && !*criticalDeassertHigh)
902 {
903 resp->deassertionsMSB |= static_cast<uint8_t>(
904 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
905 }
906 if (criticalDeassertLow && !*criticalDeassertLow)
907 {
908 resp->deassertionsMSB |= static_cast<uint8_t>(
909 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
910 }
911 if (warningDeassertHigh && !*warningDeassertHigh)
912 {
913 resp->deassertionsLSB |= static_cast<uint8_t>(
914 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
915 }
916 if (warningDeassertLow && !*warningDeassertLow)
917 {
918 resp->deassertionsLSB |= static_cast<uint8_t>(
919 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
920 }
921
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700922 if ((warningInterface != sensorMap.end()) ||
923 (criticalInterface != sensorMap.end()))
924 {
925 resp->enabled = static_cast<uint8_t>(
926 IPMISensorEventEnableByte2::eventMessagesEnable);
927 if (warningInterface != sensorMap.end())
928 {
929 auto &warningMap = warningInterface->second;
930
931 auto warningHigh = warningMap.find("WarningAlarmHigh");
932 auto warningLow = warningMap.find("WarningAlarmLow");
933 auto warningHighAlarm = false;
934 auto warningLowAlarm = false;
935
936 if (warningHigh != warningMap.end())
937 {
James Feist880b7332018-12-06 11:14:02 -0800938 warningHighAlarm = sdbusplus::message::variant_ns::get<bool>(
939 warningHigh->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700940 }
941 if (warningLow != warningMap.end())
942 {
James Feist880b7332018-12-06 11:14:02 -0800943 warningLowAlarm = sdbusplus::message::variant_ns::get<bool>(
944 warningLow->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700945 }
946 if (warningHighAlarm)
947 {
948 resp->assertionsLSB |= static_cast<uint8_t>(
949 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
950 }
951 if (warningLowAlarm)
952 {
953 resp->assertionsLSB |= 1; // lower nc going low
954 }
955 }
956 if (criticalInterface != sensorMap.end())
957 {
958 auto &criticalMap = criticalInterface->second;
959
960 auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
961 auto criticalLow = criticalMap.find("CriticalAlarmLow");
962 auto criticalHighAlarm = false;
963 auto criticalLowAlarm = false;
964
965 if (criticalHigh != criticalMap.end())
966 {
James Feist880b7332018-12-06 11:14:02 -0800967 criticalHighAlarm = sdbusplus::message::variant_ns::get<bool>(
968 criticalHigh->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700969 }
970 if (criticalLow != criticalMap.end())
971 {
James Feist880b7332018-12-06 11:14:02 -0800972 criticalLowAlarm = sdbusplus::message::variant_ns::get<bool>(
973 criticalLow->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700974 }
975 if (criticalHighAlarm)
976 {
977 resp->assertionsMSB |= static_cast<uint8_t>(
978 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
979 }
980 if (criticalLowAlarm)
981 {
982 resp->assertionsLSB |= static_cast<uint8_t>(
983 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
984 }
985 }
986 *dataLen = sizeof(SensorEventStatusResp);
987 }
988
989 // no thresholds enabled, don't need assertionMSB
990 else
991 {
992 *dataLen = sizeof(SensorEventStatusResp) - 1;
993 }
994
995 return IPMI_CC_OK;
996}
997
998/* end sensor commands */
999
1000/* storage commands */
1001
1002ipmi_ret_t ipmiStorageGetSDRRepositoryInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1003 ipmi_request_t request,
1004 ipmi_response_t response,
1005 ipmi_data_len_t dataLen,
1006 ipmi_context_t context)
1007{
1008 printCommand(+netfn, +cmd);
1009
1010 if (*dataLen)
1011 {
1012 *dataLen = 0;
1013 return IPMI_CC_REQ_DATA_LEN_INVALID;
1014 }
1015 *dataLen = 0; // default to 0 in case of an error
1016
1017 if (sensorTree.empty() && !getSensorSubtree(sensorTree))
1018 {
1019 return IPMI_CC_RESPONSE_ERROR;
1020 }
1021
1022 // zero out response buff
1023 auto responseClear = static_cast<uint8_t *>(response);
1024 std::fill(responseClear, responseClear + sizeof(GetSDRInfoResp), 0);
1025
1026 auto resp = static_cast<GetSDRInfoResp *>(response);
1027 resp->sdrVersion = ipmiSdrVersion;
1028 uint16_t recordCount = sensorTree.size();
1029
1030 // todo: for now, sdr count is number of sensors
1031 resp->recordCountLS = recordCount & 0xFF;
1032 resp->recordCountMS = recordCount >> 8;
1033
1034 // free space unspcified
1035 resp->freeSpace[0] = 0xFF;
1036 resp->freeSpace[1] = 0xFF;
1037
1038 resp->mostRecentAddition = sdrLastAdd;
1039 resp->mostRecentErase = sdrLastRemove;
1040 resp->operationSupport = static_cast<uint8_t>(
1041 SdrRepositoryInfoOps::overflow); // write not supported
1042 resp->operationSupport |=
1043 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
1044 resp->operationSupport |= static_cast<uint8_t>(
1045 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
1046 *dataLen = sizeof(GetSDRInfoResp);
1047 return IPMI_CC_OK;
1048}
1049
1050ipmi_ret_t ipmiStorageGetSDRAllocationInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1051 ipmi_request_t request,
1052 ipmi_response_t response,
1053 ipmi_data_len_t dataLen,
1054 ipmi_context_t context)
1055{
1056 if (*dataLen)
1057 {
1058 *dataLen = 0;
1059 return IPMI_CC_REQ_DATA_LEN_INVALID;
1060 }
1061 *dataLen = 0; // default to 0 in case of an error
1062 GetAllocInfoResp *resp = static_cast<GetAllocInfoResp *>(response);
1063
1064 // 0000h unspecified number of alloc units
1065 resp->allocUnitsLSB = 0;
1066 resp->allocUnitsMSB = 0;
1067
1068 // max unit size is size of max record
1069 resp->allocUnitSizeLSB = maxSDRTotalSize & 0xFF;
1070 resp->allocUnitSizeMSB = maxSDRTotalSize >> 8;
1071 // read only sdr, no free alloc blocks
1072 resp->allocUnitFreeLSB = 0;
1073 resp->allocUnitFreeMSB = 0;
1074 resp->allocUnitLargestFreeLSB = 0;
1075 resp->allocUnitLargestFreeMSB = 0;
1076 // only allow one block at a time
1077 resp->maxRecordSize = 1;
1078
1079 *dataLen = sizeof(GetAllocInfoResp);
1080
1081 return IPMI_CC_OK;
1082}
1083
1084ipmi_ret_t ipmiStorageReserveSDR(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1085 ipmi_request_t request,
1086 ipmi_response_t response,
1087 ipmi_data_len_t dataLen,
1088 ipmi_context_t context)
1089{
1090 printCommand(+netfn, +cmd);
1091
1092 if (*dataLen)
1093 {
1094 *dataLen = 0;
1095 return IPMI_CC_REQ_DATA_LEN_INVALID;
1096 }
1097 *dataLen = 0; // default to 0 in case of an error
1098 sdrReservationID++;
James Feista80cb902019-02-14 13:05:25 -08001099 if (sdrReservationID == 0)
1100 {
1101 sdrReservationID++;
1102 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001103 *dataLen = 2;
1104 auto resp = static_cast<uint8_t *>(response);
1105 resp[0] = sdrReservationID & 0xFF;
1106 resp[1] = sdrReservationID >> 8;
1107
1108 return IPMI_CC_OK;
1109}
1110
James Feistb49a98a2019-04-16 13:48:09 -07001111ipmi::RspType<uint16_t, // next record ID
1112 std::vector<uint8_t> // payload
1113 >
1114 ipmiStorageGetSDR(uint16_t reservationID, uint16_t recordID, uint8_t offset,
1115 uint8_t bytesToRead)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001116{
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001117 constexpr uint16_t lastRecordIndex = 0xFFFF;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001118
1119 // reservation required for partial reads with non zero offset into
1120 // record
James Feistb49a98a2019-04-16 13:48:09 -07001121 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001122 {
James Feistb49a98a2019-04-16 13:48:09 -07001123 return ipmi::responseInvalidReservationId();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001124 }
1125
1126 if (sensorTree.empty() && !getSensorSubtree(sensorTree))
1127 {
James Feistb49a98a2019-04-16 13:48:09 -07001128 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001129 }
1130
1131 size_t fruCount = 0;
1132 ipmi_ret_t ret = ipmi::storage::getFruSdrCount(fruCount);
1133 if (ret != IPMI_CC_OK)
1134 {
James Feistb49a98a2019-04-16 13:48:09 -07001135 return ipmi::response(ret);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001136 }
1137
1138 size_t lastRecord = sensorTree.size() + fruCount - 1;
James Feistb49a98a2019-04-16 13:48:09 -07001139 if (recordID == lastRecordIndex)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001140 {
James Feistb49a98a2019-04-16 13:48:09 -07001141 recordID = lastRecord;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001142 }
James Feistb49a98a2019-04-16 13:48:09 -07001143 if (recordID > lastRecord)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001144 {
James Feistb49a98a2019-04-16 13:48:09 -07001145 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001146 }
1147
James Feistb49a98a2019-04-16 13:48:09 -07001148 uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001149
James Feistb49a98a2019-04-16 13:48:09 -07001150 if (recordID >= sensorTree.size())
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001151 {
James Feistb49a98a2019-04-16 13:48:09 -07001152 size_t fruIndex = recordID - sensorTree.size();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001153 if (fruIndex >= fruCount)
1154 {
James Feistb49a98a2019-04-16 13:48:09 -07001155 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001156 }
1157 get_sdr::SensorDataFruRecord data;
James Feistb49a98a2019-04-16 13:48:09 -07001158 if (offset > sizeof(data))
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001159 {
James Feistb49a98a2019-04-16 13:48:09 -07001160 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001161 }
1162 ret = ipmi::storage::getFruSdrs(fruIndex, data);
1163 if (ret != IPMI_CC_OK)
1164 {
James Feistb49a98a2019-04-16 13:48:09 -07001165 return ipmi::response(ret);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001166 }
James Feistb49a98a2019-04-16 13:48:09 -07001167 data.header.record_id_msb = recordID << 8;
1168 data.header.record_id_lsb = recordID & 0xFF;
1169 if (sizeof(data) < (offset + bytesToRead))
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001170 {
James Feistb49a98a2019-04-16 13:48:09 -07001171 bytesToRead = sizeof(data) - offset;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001172 }
James Feistb49a98a2019-04-16 13:48:09 -07001173
1174 uint8_t *respStart = reinterpret_cast<uint8_t *>(&data) + offset;
1175 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
1176
1177 return ipmi::responseSuccess(nextRecordId, recordData);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001178 }
1179
1180 std::string connection;
1181 std::string path;
James Feistb49a98a2019-04-16 13:48:09 -07001182 uint16_t sensorIndex = recordID;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001183 for (const auto &sensor : sensorTree)
1184 {
1185 if (sensorIndex-- == 0)
1186 {
1187 if (!sensor.second.size())
1188 {
James Feistb49a98a2019-04-16 13:48:09 -07001189 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001190 }
1191 connection = sensor.second.begin()->first;
1192 path = sensor.first;
1193 break;
1194 }
1195 }
1196
1197 SensorMap sensorMap;
1198 if (!getSensorMap(connection, path, sensorMap))
1199 {
James Feistb49a98a2019-04-16 13:48:09 -07001200 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001201 }
James Feistb49a98a2019-04-16 13:48:09 -07001202 uint8_t sensornumber = (recordID & 0xFF);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001203 get_sdr::SensorDataFullRecord record = {0};
1204
James Feistb49a98a2019-04-16 13:48:09 -07001205 record.header.record_id_msb = recordID << 8;
1206 record.header.record_id_lsb = recordID & 0xFF;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001207 record.header.sdr_version = ipmiSdrVersion;
1208 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1209 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1210 sizeof(get_sdr::SensorDataRecordHeader);
1211 record.key.owner_id = 0x20;
1212 record.key.owner_lun = 0x0;
1213 record.key.sensor_number = sensornumber;
1214
1215 record.body.entity_id = 0x0;
1216 record.body.entity_instance = 0x01;
James Feist7086a882019-03-13 10:46:00 -07001217 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001218 record.body.sensor_type = getSensorTypeFromPath(path);
1219 std::string type = getSensorTypeStringFromPath(path);
1220 auto typeCstr = type.c_str();
1221 auto findUnits = sensorUnits.find(typeCstr);
1222 if (findUnits != sensorUnits.end())
1223 {
1224 record.body.sensor_units_2_base =
1225 static_cast<uint8_t>(findUnits->second);
1226 } // else default 0x0 unspecified
1227
1228 record.body.event_reading_type = getSensorEventTypeFromPath(path);
1229
1230 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
1231 if (sensorObject == sensorMap.end())
1232 {
James Feistb49a98a2019-04-16 13:48:09 -07001233 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001234 }
1235
1236 auto maxObject = sensorObject->second.find("MaxValue");
1237 auto minObject = sensorObject->second.find("MinValue");
1238 double max = 128;
1239 double min = -127;
1240 if (maxObject != sensorObject->second.end())
1241 {
James Feist14fde842018-12-06 10:19:40 -08001242 max = variant_ns::visit(VariantToDoubleVisitor(), maxObject->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001243 }
1244
1245 if (minObject != sensorObject->second.end())
1246 {
James Feist14fde842018-12-06 10:19:40 -08001247 min = variant_ns::visit(VariantToDoubleVisitor(), minObject->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001248 }
1249
Yong Li1f2eb5e2019-05-23 14:07:17 +08001250 int16_t mValue = 0;
1251 int8_t rExp = 0;
1252 int16_t bValue = 0;
1253 int8_t bExp = 0;
1254 bool bSigned = false;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001255
1256 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1257 {
James Feistb49a98a2019-04-16 13:48:09 -07001258 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001259 }
1260
1261 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1262 record.body.m_lsb = mValue & 0xFF;
1263
1264 // move the smallest bit of the MSB into place (bit 9)
1265 // the MSbs are bits 7:8 in m_msb_and_tolerance
1266 uint8_t mMsb = (mValue & (1 << 8)) > 0 ? (1 << 6) : 0;
1267
1268 // assign the negative
1269 if (mValue < 0)
1270 {
1271 mMsb |= (1 << 7);
1272 }
1273 record.body.m_msb_and_tolerance = mMsb;
1274
1275 record.body.b_lsb = bValue & 0xFF;
1276
1277 // move the smallest bit of the MSB into place
1278 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1279 uint8_t bMsb = (bValue & (1 << 8)) > 0 ? (1 << 6) : 0;
1280
1281 // assign the negative
1282 if (bValue < 0)
1283 {
1284 bMsb |= (1 << 7);
1285 }
1286 record.body.b_msb_and_accuracy_lsb = bMsb;
1287
1288 record.body.r_b_exponents = bExp & 0x7;
1289 if (bExp < 0)
1290 {
1291 record.body.r_b_exponents |= 1 << 3;
1292 }
1293 record.body.r_b_exponents = (rExp & 0x7) << 4;
1294 if (rExp < 0)
1295 {
1296 record.body.r_b_exponents |= 1 << 7;
1297 }
1298
1299 // todo fill out rest of units
1300 if (bSigned)
1301 {
1302 record.body.sensor_units_1 = 1 << 7;
1303 }
1304
1305 // populate sensor name from path
1306 std::string name;
1307 size_t nameStart = path.rfind("/");
1308 if (nameStart != std::string::npos)
1309 {
1310 name = path.substr(nameStart + 1, std::string::npos - nameStart);
1311 }
1312
1313 std::replace(name.begin(), name.end(), '_', ' ');
1314 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
1315 {
James Feist979a2322019-05-15 09:06:54 -07001316 // try to not truncate by replacing common words
1317 constexpr std::array<std::pair<const char *, const char *>, 2>
1318 replaceWords = {std::make_pair("Output", "Out"),
1319 std::make_pair("Input", "In")};
1320 for (const auto &[find, replace] : replaceWords)
1321 {
1322 boost::replace_all(name, find, replace);
1323 }
1324
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001325 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
1326 }
1327 record.body.id_string_info = name.size();
1328 std::strncpy(record.body.id_string, name.c_str(),
1329 sizeof(record.body.id_string));
1330
James Feistc4b15bc2019-04-16 15:41:39 -07001331 IPMIThresholds thresholdData;
1332 try
1333 {
1334 thresholdData = getIPMIThresholds(sensorMap);
1335 }
1336 catch (std::exception &)
1337 {
1338 return ipmi::responseResponseError();
1339 }
1340
1341 if (thresholdData.criticalHigh)
1342 {
1343 record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1344 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1345 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1346 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1347 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1348 record.body.discrete_reading_setting_mask[0] |=
1349 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1350 }
1351 if (thresholdData.warningHigh)
1352 {
1353 record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1354 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1355 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1356 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1357 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1358 record.body.discrete_reading_setting_mask[0] |=
1359 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1360 }
1361 if (thresholdData.criticalLow)
1362 {
1363 record.body.lower_critical_threshold = *thresholdData.criticalLow;
1364 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1365 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1366 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1367 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1368 record.body.discrete_reading_setting_mask[0] |=
1369 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1370 }
1371 if (thresholdData.warningLow)
1372 {
1373 record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1374 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1375 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1376 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1377 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1378 record.body.discrete_reading_setting_mask[0] |=
1379 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1380 }
1381
1382 // everything that is readable is setable
1383 record.body.discrete_reading_setting_mask[1] =
1384 record.body.discrete_reading_setting_mask[0];
1385
James Feistb49a98a2019-04-16 13:48:09 -07001386 if (sizeof(get_sdr::SensorDataFullRecord) < (offset + bytesToRead))
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001387 {
James Feistb49a98a2019-04-16 13:48:09 -07001388 bytesToRead = sizeof(get_sdr::SensorDataFullRecord) - offset;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001389 }
1390
James Feistb49a98a2019-04-16 13:48:09 -07001391 uint8_t *respStart = reinterpret_cast<uint8_t *>(&record) + offset;
1392 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001393
James Feistb49a98a2019-04-16 13:48:09 -07001394 return ipmi::responseSuccess(nextRecordId, recordData);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001395}
1396/* end storage commands */
1397
1398void registerSensorFunctions()
1399{
1400 // get firmware version information
1401 ipmiPrintAndRegister(NETFUN_SENSOR, IPMI_CMD_WILDCARD, nullptr,
1402 ipmiSensorWildcardHandler, PRIVILEGE_USER);
1403
1404 // <Get Sensor Type>
1405 ipmiPrintAndRegister(
1406 NETFUN_SENSOR,
1407 static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdGetSensorType),
1408 nullptr, ipmiSensorWildcardHandler, PRIVILEGE_USER);
1409
1410 // <Set Sensor Reading and Event Status>
1411 ipmiPrintAndRegister(
1412 NETFUN_SENSOR,
1413 static_cast<ipmi_cmd_t>(
1414 IPMINetfnSensorCmds::ipmiCmdSetSensorReadingAndEventStatus),
1415 nullptr, ipmiSensorWildcardHandler, PRIVILEGE_OPERATOR);
1416
Jason M. Billsae6bdb12019-04-02 12:00:04 -07001417 // <Platform Event>
1418 ipmi::registerHandler(
1419 ipmi::prioOemBase, ipmi::netFnSensor,
1420 static_cast<ipmi::Cmd>(ipmi::sensor_event::cmdPlatformEvent),
1421 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
1422
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001423 // <Get Sensor Reading>
James Feist0cd014a2019-04-08 15:04:33 -07001424 ipmi::registerHandler(
1425 ipmi::prioOemBase, NETFUN_SENSOR,
1426 static_cast<ipmi::Cmd>(IPMINetfnSensorCmds::ipmiCmdGetSensorReading),
1427 ipmi::Privilege::User, ipmiSenGetSensorReading);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001428
1429 // <Get Sensor Threshold>
James Feist902c4c52019-04-16 14:51:31 -07001430 ipmi::registerHandler(
1431 ipmi::prioOemBase, NETFUN_SENSOR,
1432 static_cast<ipmi::Cmd>(IPMINetfnSensorCmds::ipmiCmdGetSensorThreshold),
1433 ipmi::Privilege::User, ipmiSenGetSensorThresholds);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001434
1435 ipmiPrintAndRegister(
1436 NETFUN_SENSOR,
1437 static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdSetSensorThreshold),
1438 nullptr, ipmiSenSetSensorThresholds, PRIVILEGE_OPERATOR);
1439
1440 // <Get Sensor Event Enable>
1441 ipmiPrintAndRegister(NETFUN_SENSOR,
1442 static_cast<ipmi_cmd_t>(
1443 IPMINetfnSensorCmds::ipmiCmdGetSensorEventEnable),
1444 nullptr, ipmiSenGetSensorEventEnable, PRIVILEGE_USER);
1445
1446 // <Get Sensor Event Status>
1447 ipmiPrintAndRegister(NETFUN_SENSOR,
1448 static_cast<ipmi_cmd_t>(
1449 IPMINetfnSensorCmds::ipmiCmdGetSensorEventStatus),
1450 nullptr, ipmiSenGetSensorEventStatus, PRIVILEGE_USER);
1451
1452 // register all storage commands for both Sensor and Storage command
1453 // versions
1454
1455 // <Get SDR Repository Info>
1456 ipmiPrintAndRegister(
1457 NETFUN_STORAGE,
1458 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdGetRepositoryInfo),
1459 nullptr, ipmiStorageGetSDRRepositoryInfo, PRIVILEGE_USER);
1460
1461 // <Get SDR Allocation Info>
1462 ipmiPrintAndRegister(NETFUN_STORAGE,
1463 static_cast<ipmi_cmd_t>(
1464 IPMINetfnStorageCmds::ipmiCmdGetSDRAllocationInfo),
1465 nullptr, ipmiStorageGetSDRAllocationInfo,
1466 PRIVILEGE_USER);
1467
1468 // <Reserve SDR Repo>
1469 ipmiPrintAndRegister(NETFUN_SENSOR,
1470 static_cast<ipmi_cmd_t>(
1471 IPMINetfnSensorCmds::ipmiCmdReserveDeviceSDRRepo),
1472 nullptr, ipmiStorageReserveSDR, PRIVILEGE_USER);
1473
1474 ipmiPrintAndRegister(
1475 NETFUN_STORAGE,
1476 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdReserveSDR),
1477 nullptr, ipmiStorageReserveSDR, PRIVILEGE_USER);
1478
1479 // <Get Sdr>
James Feistb49a98a2019-04-16 13:48:09 -07001480 ipmi::registerHandler(
1481 ipmi::prioOemBase, NETFUN_SENSOR,
1482 static_cast<ipmi::Cmd>(IPMINetfnSensorCmds::ipmiCmdGetDeviceSDR),
1483 ipmi::Privilege::User, ipmiStorageGetSDR);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001484
James Feistb49a98a2019-04-16 13:48:09 -07001485 ipmi::registerHandler(
1486 ipmi::prioOemBase, NETFUN_STORAGE,
1487 static_cast<ipmi::Cmd>(IPMINetfnStorageCmds::ipmiCmdGetSDR),
1488 ipmi::Privilege::User, ipmiStorageGetSDR);
1489
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001490 return;
1491}
1492} // namespace ipmi