blob: ba64fe4d689799e85340cb311b427eedf3b1052a [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. Bills99b78ec2019-01-18 10:42:18 -0800315 // Send this request to the Redfish hooks to see if it should be a
316 // Redfish message instead. If so, no need to add it to the SEL, so
317 // just return success.
318 if (intel_oem::ipmi::sel::checkRedfishHooks(
319 generatorID, evmRev, sensorType, sensorNum, eventType, eventData1,
320 eventData2.value_or(0xFF), eventData3.value_or(0xFF)))
321 {
322 return ipmi::responseSuccess();
323 }
324
Jason M. Billsae6bdb12019-04-02 12:00:04 -0700325 bool assert = eventType & directionMask ? false : true;
326 std::vector<uint8_t> eventData;
327 eventData.push_back(eventData1);
328 eventData.push_back(eventData2.value_or(0xFF));
329 eventData.push_back(eventData3.value_or(0xFF));
330
331 std::string sensorPath = getPathFromSensorNumber(sensorNum);
332 std::string service =
333 ipmi::getService(dbus, ipmiSELAddInterface, ipmiSELPath);
334 sdbusplus::message::message writeSEL = dbus.new_method_call(
335 service.c_str(), ipmiSELPath, ipmiSELAddInterface, "IpmiSelAdd");
336 writeSEL.append(ipmiSELAddMessage, sensorPath, eventData, assert,
337 static_cast<uint16_t>(generatorID));
338 try
339 {
340 dbus.call(writeSEL);
341 }
342 catch (sdbusplus::exception_t &e)
343 {
344 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
345 return ipmi::responseUnspecifiedError();
346 }
347
348 return ipmi::responseSuccess();
349}
350
James Feist0cd014a2019-04-08 15:04:33 -0700351ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
352 ipmiSenGetSensorReading(uint8_t sensnum)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700353{
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700354 std::string connection;
355 std::string path;
356
357 auto status = getSensorConnection(sensnum, connection, path);
358 if (status)
359 {
James Feist0cd014a2019-04-08 15:04:33 -0700360 return ipmi::response(status);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700361 }
362
363 SensorMap sensorMap;
364 if (!getSensorMap(connection, path, sensorMap))
365 {
James Feist0cd014a2019-04-08 15:04:33 -0700366 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700367 }
368 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
369
370 if (sensorObject == sensorMap.end() ||
371 sensorObject->second.find("Value") == sensorObject->second.end())
372 {
James Feist0cd014a2019-04-08 15:04:33 -0700373 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700374 }
James Feist0cd014a2019-04-08 15:04:33 -0700375 auto &valueVariant = sensorObject->second["Value"];
376 double reading = variant_ns::visit(VariantToDoubleVisitor(), valueVariant);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700377
Yong Li1f2eb5e2019-05-23 14:07:17 +0800378 double max = 0;
379 double min = 0;
James Feistaecaef72019-04-26 10:30:32 -0700380 getSensorMaxMin(sensorMap, max, min);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700381
382 int16_t mValue = 0;
383 int16_t bValue = 0;
384 int8_t rExp = 0;
385 int8_t bExp = 0;
386 bool bSigned = false;
387
388 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
389 {
James Feist0cd014a2019-04-08 15:04:33 -0700390 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700391 }
392
James Feist0cd014a2019-04-08 15:04:33 -0700393 uint8_t value =
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700394 scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
James Feist0cd014a2019-04-08 15:04:33 -0700395 uint8_t operation =
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700396 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
James Feist0cd014a2019-04-08 15:04:33 -0700397 operation |=
James Feist81a95c12019-03-01 15:08:28 -0800398 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700399
James Feist0cd014a2019-04-08 15:04:33 -0700400 uint8_t thresholds = 0;
401
402 auto warningObject =
403 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
404 if (warningObject != sensorMap.end())
405 {
406 auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
407 auto alarmLow = warningObject->second.find("WarningAlarmLow");
408 if (alarmHigh != warningObject->second.end())
409 {
410 if (std::get<bool>(alarmHigh->second))
411 {
412 thresholds |= static_cast<uint8_t>(
413 IPMISensorReadingByte3::upperNonCritical);
414 }
415 }
416 if (alarmLow != warningObject->second.end())
417 {
418 if (std::get<bool>(alarmLow->second))
419 {
420 thresholds |= static_cast<uint8_t>(
421 IPMISensorReadingByte3::lowerNonCritical);
422 }
423 }
424 }
425
426 auto criticalObject =
427 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
428 if (criticalObject != sensorMap.end())
429 {
430 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
431 auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
432 if (alarmHigh != criticalObject->second.end())
433 {
434 if (std::get<bool>(alarmHigh->second))
435 {
436 thresholds |=
437 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
438 }
439 }
440 if (alarmLow != criticalObject->second.end())
441 {
442 if (std::get<bool>(alarmLow->second))
443 {
444 thresholds |=
445 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
446 }
447 }
448 }
449
450 // no discrete as of today so optional byte is never returned
451 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700452}
453
454ipmi_ret_t ipmiSenSetSensorThresholds(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
455 ipmi_request_t request,
456 ipmi_response_t response,
457 ipmi_data_len_t dataLen,
458 ipmi_context_t context)
459{
460 if (*dataLen != 8)
461 {
462 *dataLen = 0;
463 return IPMI_CC_REQ_DATA_LEN_INVALID;
464 }
465 *dataLen = 0;
466
467 SensorThresholdReq *req = static_cast<SensorThresholdReq *>(request);
468
469 // upper two bits reserved
470 if (req->mask & 0xC0)
471 {
472 return IPMI_CC_INVALID_FIELD_REQUEST;
473 }
474
475 // lower nc and upper nc not suppported on any sensor
476 if ((req->mask & static_cast<uint8_t>(
477 SensorThresholdReqEnable::setLowerNonRecoverable)) ||
478 (req->mask & static_cast<uint8_t>(
479 SensorThresholdReqEnable::setUpperNonRecoverable)))
480 {
481 return IPMI_CC_INVALID_FIELD_REQUEST;
482 }
483
484 // if no bits are set in the mask, nothing to do
485 if (!(req->mask))
486 {
487 return IPMI_CC_OK;
488 }
489
490 std::string connection;
491 std::string path;
492
493 ipmi_ret_t status = getSensorConnection(req->sensorNum, connection, path);
494 if (status)
495 {
496 return status;
497 }
498 SensorMap sensorMap;
499 if (!getSensorMap(connection, path, sensorMap))
500 {
501 return IPMI_CC_RESPONSE_ERROR;
502 }
503
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700504 double max = 0;
505 double min = 0;
James Feistaecaef72019-04-26 10:30:32 -0700506 getSensorMaxMin(sensorMap, max, min);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700507
508 int16_t mValue = 0;
509 int16_t bValue = 0;
510 int8_t rExp = 0;
511 int8_t bExp = 0;
512 bool bSigned = false;
513
514 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
515 {
516 return IPMI_CC_RESPONSE_ERROR;
517 }
518
519 bool setLowerCritical =
520 req->mask &
521 static_cast<uint8_t>(SensorThresholdReqEnable::setLowerCritical);
522 bool setUpperCritical =
523 req->mask &
524 static_cast<uint8_t>(SensorThresholdReqEnable::setUpperCritical);
525
526 bool setLowerWarning =
527 req->mask &
528 static_cast<uint8_t>(SensorThresholdReqEnable::setLowerNonCritical);
529 bool setUpperWarning =
530 req->mask &
531 static_cast<uint8_t>(SensorThresholdReqEnable::setUpperNonCritical);
532
533 // store a vector of property name, value to set, and interface
534 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
535
536 // define the indexes of the tuple
537 constexpr uint8_t propertyName = 0;
538 constexpr uint8_t thresholdValue = 1;
539 constexpr uint8_t interface = 2;
540 // verifiy all needed fields are present
541 if (setLowerCritical || setUpperCritical)
542 {
543 auto findThreshold =
544 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
545 if (findThreshold == sensorMap.end())
546 {
547 return IPMI_CC_INVALID_FIELD_REQUEST;
548 }
549 if (setLowerCritical)
550 {
551 auto findLower = findThreshold->second.find("CriticalLow");
552 if (findLower == findThreshold->second.end())
553 {
554 return IPMI_CC_INVALID_FIELD_REQUEST;
555 }
556 thresholdsToSet.emplace_back("CriticalLow", req->lowerCritical,
557 findThreshold->first);
558 }
559 if (setUpperCritical)
560 {
561 auto findUpper = findThreshold->second.find("CriticalHigh");
562 if (findUpper == findThreshold->second.end())
563 {
564 return IPMI_CC_INVALID_FIELD_REQUEST;
565 }
566 thresholdsToSet.emplace_back("CriticalHigh", req->upperCritical,
567 findThreshold->first);
568 }
569 }
570 if (setLowerWarning || setUpperWarning)
571 {
572 auto findThreshold =
573 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
574 if (findThreshold == sensorMap.end())
575 {
576 return IPMI_CC_INVALID_FIELD_REQUEST;
577 }
578 if (setLowerWarning)
579 {
580 auto findLower = findThreshold->second.find("WarningLow");
581 if (findLower == findThreshold->second.end())
582 {
583 return IPMI_CC_INVALID_FIELD_REQUEST;
584 }
585 thresholdsToSet.emplace_back("WarningLow", req->lowerNonCritical,
586 findThreshold->first);
587 }
588 if (setUpperWarning)
589 {
590 auto findUpper = findThreshold->second.find("WarningHigh");
591 if (findUpper == findThreshold->second.end())
592 {
593 return IPMI_CC_INVALID_FIELD_REQUEST;
594 }
595 thresholdsToSet.emplace_back("WarningHigh", req->upperNonCritical,
596 findThreshold->first);
597 }
598 }
599
600 for (const auto &property : thresholdsToSet)
601 {
602 // from section 36.3 in the IPMI Spec, assume all linear
603 double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
604 (bValue * std::pow(10, bExp))) *
605 std::pow(10, rExp);
606 setDbusProperty(dbus, connection, path, std::get<interface>(property),
607 std::get<propertyName>(property),
608 ipmi::Value(valueToSet));
609 }
610
611 return IPMI_CC_OK;
612}
613
James Feist902c4c52019-04-16 14:51:31 -0700614IPMIThresholds getIPMIThresholds(const SensorMap &sensorMap)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700615{
James Feist902c4c52019-04-16 14:51:31 -0700616 IPMIThresholds resp;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700617 auto warningInterface =
618 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
619 auto criticalInterface =
620 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
621
622 if ((warningInterface != sensorMap.end()) ||
623 (criticalInterface != sensorMap.end()))
624 {
625 auto sensorPair = sensorMap.find("xyz.openbmc_project.Sensor.Value");
626
627 if (sensorPair == sensorMap.end())
628 {
629 // should not have been able to find a sensor not implementing
630 // the sensor object
James Feist902c4c52019-04-16 14:51:31 -0700631 throw std::runtime_error("Invalid sensor map");
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700632 }
633
634 double max;
635 double min;
James Feistaecaef72019-04-26 10:30:32 -0700636 getSensorMaxMin(sensorMap, max, min);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700637
638 int16_t mValue = 0;
639 int16_t bValue = 0;
640 int8_t rExp = 0;
641 int8_t bExp = 0;
642 bool bSigned = false;
643
644 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
645 {
James Feist902c4c52019-04-16 14:51:31 -0700646 throw std::runtime_error("Invalid sensor atrributes");
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700647 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700648 if (warningInterface != sensorMap.end())
649 {
650 auto &warningMap = warningInterface->second;
651
652 auto warningHigh = warningMap.find("WarningHigh");
653 auto warningLow = warningMap.find("WarningLow");
654
655 if (warningHigh != warningMap.end())
656 {
James Feist902c4c52019-04-16 14:51:31 -0700657
James Feist14fde842018-12-06 10:19:40 -0800658 double value = variant_ns::visit(VariantToDoubleVisitor(),
659 warningHigh->second);
James Feist902c4c52019-04-16 14:51:31 -0700660 resp.warningHigh = scaleIPMIValueFromDouble(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700661 value, mValue, rExp, bValue, bExp, bSigned);
662 }
663 if (warningLow != warningMap.end())
664 {
James Feist14fde842018-12-06 10:19:40 -0800665 double value = variant_ns::visit(VariantToDoubleVisitor(),
666 warningLow->second);
James Feist902c4c52019-04-16 14:51:31 -0700667 resp.warningLow = scaleIPMIValueFromDouble(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700668 value, mValue, rExp, bValue, bExp, bSigned);
669 }
670 }
671 if (criticalInterface != sensorMap.end())
672 {
673 auto &criticalMap = criticalInterface->second;
674
675 auto criticalHigh = criticalMap.find("CriticalHigh");
676 auto criticalLow = criticalMap.find("CriticalLow");
677
678 if (criticalHigh != criticalMap.end())
679 {
James Feist14fde842018-12-06 10:19:40 -0800680 double value = variant_ns::visit(VariantToDoubleVisitor(),
681 criticalHigh->second);
James Feist902c4c52019-04-16 14:51:31 -0700682 resp.criticalHigh = scaleIPMIValueFromDouble(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700683 value, mValue, rExp, bValue, bExp, bSigned);
684 }
685 if (criticalLow != criticalMap.end())
686 {
James Feist14fde842018-12-06 10:19:40 -0800687 double value = variant_ns::visit(VariantToDoubleVisitor(),
688 criticalLow->second);
James Feist902c4c52019-04-16 14:51:31 -0700689 resp.criticalLow = scaleIPMIValueFromDouble(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700690 value, mValue, rExp, bValue, bExp, bSigned);
691 }
692 }
693 }
James Feist902c4c52019-04-16 14:51:31 -0700694 return resp;
695}
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700696
James Feist902c4c52019-04-16 14:51:31 -0700697ipmi::RspType<uint8_t, // readable
698 uint8_t, // lowerNCrit
699 uint8_t, // lowerCrit
700 uint8_t, // lowerNrecoverable
701 uint8_t, // upperNC
702 uint8_t, // upperCrit
703 uint8_t> // upperNRecoverable
704 ipmiSenGetSensorThresholds(uint8_t sensorNumber)
705{
706 std::string connection;
707 std::string path;
708
709 auto status = getSensorConnection(sensorNumber, connection, path);
710 if (status)
711 {
712 return ipmi::response(status);
713 }
714
715 SensorMap sensorMap;
716 if (!getSensorMap(connection, path, sensorMap))
717 {
718 return ipmi::responseResponseError();
719 }
720
721 IPMIThresholds thresholdData;
722 try
723 {
724 thresholdData = getIPMIThresholds(sensorMap);
725 }
726 catch (std::exception &)
727 {
728 return ipmi::responseResponseError();
729 }
730
731 uint8_t readable = 0;
732 uint8_t lowerNC = 0;
733 uint8_t lowerCritical = 0;
734 uint8_t lowerNonRecoverable = 0;
735 uint8_t upperNC = 0;
736 uint8_t upperCritical = 0;
737 uint8_t upperNonRecoverable = 0;
738
739 if (thresholdData.warningHigh)
740 {
741 readable |=
742 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
743 upperNC = *thresholdData.warningHigh;
744 }
745 if (thresholdData.warningLow)
746 {
747 readable |=
748 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
749 lowerNC = *thresholdData.warningLow;
750 }
751
752 if (thresholdData.criticalHigh)
753 {
754 readable |=
755 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
756 upperCritical = *thresholdData.criticalHigh;
757 }
758 if (thresholdData.criticalLow)
759 {
760 readable |=
761 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
762 lowerCritical = *thresholdData.criticalLow;
763 }
764
765 return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
766 lowerNonRecoverable, upperNC, upperCritical,
767 upperNonRecoverable);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700768}
769
770ipmi_ret_t ipmiSenGetSensorEventEnable(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
771 ipmi_request_t request,
772 ipmi_response_t response,
773 ipmi_data_len_t dataLen,
774 ipmi_context_t context)
775{
776 if (*dataLen != 1)
777 {
778 *dataLen = 0;
779 return IPMI_CC_REQ_DATA_LEN_INVALID;
780 }
781 *dataLen = 0; // default to 0 in case of an error
782
783 uint8_t sensnum = *(static_cast<uint8_t *>(request));
784
785 std::string connection;
786 std::string path;
787
788 auto status = getSensorConnection(sensnum, connection, path);
789 if (status)
790 {
791 return status;
792 }
793
794 SensorMap sensorMap;
795 if (!getSensorMap(connection, path, sensorMap))
796 {
797 return IPMI_CC_RESPONSE_ERROR;
798 }
799
800 auto warningInterface =
801 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
802 auto criticalInterface =
803 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
804
805 if ((warningInterface != sensorMap.end()) ||
806 (criticalInterface != sensorMap.end()))
807 {
808 // zero out response buff
809 auto responseClear = static_cast<uint8_t *>(response);
810 std::fill(responseClear, responseClear + sizeof(SensorEventEnableResp),
811 0);
812
813 // assume all threshold sensors
814 auto resp = static_cast<SensorEventEnableResp *>(response);
815
816 resp->enabled = static_cast<uint8_t>(
817 IPMISensorEventEnableByte2::sensorScanningEnable);
818 if (warningInterface != sensorMap.end())
819 {
820 auto &warningMap = warningInterface->second;
821
822 auto warningHigh = warningMap.find("WarningHigh");
823 auto warningLow = warningMap.find("WarningLow");
824 if (warningHigh != warningMap.end())
825 {
826 resp->assertionEnabledLSB |= static_cast<uint8_t>(
827 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
828 resp->deassertionEnabledLSB |= static_cast<uint8_t>(
829 IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
830 }
831 if (warningLow != warningMap.end())
832 {
833 resp->assertionEnabledLSB |= static_cast<uint8_t>(
834 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
835 resp->deassertionEnabledLSB |= static_cast<uint8_t>(
836 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
837 }
838 }
839 if (criticalInterface != sensorMap.end())
840 {
841 auto &criticalMap = criticalInterface->second;
842
843 auto criticalHigh = criticalMap.find("CriticalHigh");
844 auto criticalLow = criticalMap.find("CriticalLow");
845
846 if (criticalHigh != criticalMap.end())
847 {
848 resp->assertionEnabledMSB |= static_cast<uint8_t>(
849 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
850 resp->deassertionEnabledMSB |= static_cast<uint8_t>(
851 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
852 }
853 if (criticalLow != criticalMap.end())
854 {
855 resp->assertionEnabledLSB |= static_cast<uint8_t>(
856 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
857 resp->deassertionEnabledLSB |= static_cast<uint8_t>(
858 IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
859 }
860 }
861 *dataLen =
862 sizeof(SensorEventEnableResp); // todo only return needed bytes
863 }
864 // no thresholds enabled
865 else
866 {
867 *dataLen = 1;
868 auto resp = static_cast<uint8_t *>(response);
869 *resp = static_cast<uint8_t>(
870 IPMISensorEventEnableByte2::eventMessagesEnable);
871 *resp |= static_cast<uint8_t>(
872 IPMISensorEventEnableByte2::sensorScanningEnable);
873 }
874 return IPMI_CC_OK;
875}
876
877ipmi_ret_t ipmiSenGetSensorEventStatus(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
878 ipmi_request_t request,
879 ipmi_response_t response,
880 ipmi_data_len_t dataLen,
881 ipmi_context_t context)
882{
883 if (*dataLen != 1)
884 {
885 *dataLen = 0;
886 return IPMI_CC_REQ_DATA_LEN_INVALID;
887 }
888 *dataLen = 0; // default to 0 in case of an error
889
890 uint8_t sensnum = *(static_cast<uint8_t *>(request));
891
892 std::string connection;
893 std::string path;
894
895 auto status = getSensorConnection(sensnum, connection, path);
896 if (status)
897 {
898 return status;
899 }
900
901 SensorMap sensorMap;
902 if (!getSensorMap(connection, path, sensorMap))
903 {
904 return IPMI_CC_RESPONSE_ERROR;
905 }
906
907 auto warningInterface =
908 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
909 auto criticalInterface =
910 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
911
912 // zero out response buff
913 auto responseClear = static_cast<uint8_t *>(response);
914 std::fill(responseClear, responseClear + sizeof(SensorEventStatusResp), 0);
915 auto resp = static_cast<SensorEventStatusResp *>(response);
916 resp->enabled =
917 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
918
James Feist392786a2019-03-19 13:36:10 -0700919 std::optional<bool> criticalDeassertHigh =
920 thresholdDeassertMap[path]["CriticalAlarmHigh"];
921 std::optional<bool> criticalDeassertLow =
922 thresholdDeassertMap[path]["CriticalAlarmLow"];
923 std::optional<bool> warningDeassertHigh =
924 thresholdDeassertMap[path]["WarningAlarmHigh"];
925 std::optional<bool> warningDeassertLow =
926 thresholdDeassertMap[path]["WarningAlarmLow"];
927
928 if (criticalDeassertHigh && !*criticalDeassertHigh)
929 {
930 resp->deassertionsMSB |= static_cast<uint8_t>(
931 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
932 }
933 if (criticalDeassertLow && !*criticalDeassertLow)
934 {
935 resp->deassertionsMSB |= static_cast<uint8_t>(
936 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
937 }
938 if (warningDeassertHigh && !*warningDeassertHigh)
939 {
940 resp->deassertionsLSB |= static_cast<uint8_t>(
941 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
942 }
943 if (warningDeassertLow && !*warningDeassertLow)
944 {
945 resp->deassertionsLSB |= static_cast<uint8_t>(
946 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
947 }
948
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700949 if ((warningInterface != sensorMap.end()) ||
950 (criticalInterface != sensorMap.end()))
951 {
952 resp->enabled = static_cast<uint8_t>(
953 IPMISensorEventEnableByte2::eventMessagesEnable);
954 if (warningInterface != sensorMap.end())
955 {
956 auto &warningMap = warningInterface->second;
957
958 auto warningHigh = warningMap.find("WarningAlarmHigh");
959 auto warningLow = warningMap.find("WarningAlarmLow");
960 auto warningHighAlarm = false;
961 auto warningLowAlarm = false;
962
963 if (warningHigh != warningMap.end())
964 {
James Feist880b7332018-12-06 11:14:02 -0800965 warningHighAlarm = sdbusplus::message::variant_ns::get<bool>(
966 warningHigh->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700967 }
968 if (warningLow != warningMap.end())
969 {
James Feist880b7332018-12-06 11:14:02 -0800970 warningLowAlarm = sdbusplus::message::variant_ns::get<bool>(
971 warningLow->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700972 }
973 if (warningHighAlarm)
974 {
975 resp->assertionsLSB |= static_cast<uint8_t>(
976 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
977 }
978 if (warningLowAlarm)
979 {
980 resp->assertionsLSB |= 1; // lower nc going low
981 }
982 }
983 if (criticalInterface != sensorMap.end())
984 {
985 auto &criticalMap = criticalInterface->second;
986
987 auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
988 auto criticalLow = criticalMap.find("CriticalAlarmLow");
989 auto criticalHighAlarm = false;
990 auto criticalLowAlarm = false;
991
992 if (criticalHigh != criticalMap.end())
993 {
James Feist880b7332018-12-06 11:14:02 -0800994 criticalHighAlarm = sdbusplus::message::variant_ns::get<bool>(
995 criticalHigh->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700996 }
997 if (criticalLow != criticalMap.end())
998 {
James Feist880b7332018-12-06 11:14:02 -0800999 criticalLowAlarm = sdbusplus::message::variant_ns::get<bool>(
1000 criticalLow->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001001 }
1002 if (criticalHighAlarm)
1003 {
1004 resp->assertionsMSB |= static_cast<uint8_t>(
1005 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1006 }
1007 if (criticalLowAlarm)
1008 {
1009 resp->assertionsLSB |= static_cast<uint8_t>(
1010 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1011 }
1012 }
1013 *dataLen = sizeof(SensorEventStatusResp);
1014 }
1015
1016 // no thresholds enabled, don't need assertionMSB
1017 else
1018 {
1019 *dataLen = sizeof(SensorEventStatusResp) - 1;
1020 }
1021
1022 return IPMI_CC_OK;
1023}
1024
1025/* end sensor commands */
1026
1027/* storage commands */
1028
1029ipmi_ret_t ipmiStorageGetSDRRepositoryInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1030 ipmi_request_t request,
1031 ipmi_response_t response,
1032 ipmi_data_len_t dataLen,
1033 ipmi_context_t context)
1034{
1035 printCommand(+netfn, +cmd);
1036
1037 if (*dataLen)
1038 {
1039 *dataLen = 0;
1040 return IPMI_CC_REQ_DATA_LEN_INVALID;
1041 }
1042 *dataLen = 0; // default to 0 in case of an error
1043
1044 if (sensorTree.empty() && !getSensorSubtree(sensorTree))
1045 {
1046 return IPMI_CC_RESPONSE_ERROR;
1047 }
1048
1049 // zero out response buff
1050 auto responseClear = static_cast<uint8_t *>(response);
1051 std::fill(responseClear, responseClear + sizeof(GetSDRInfoResp), 0);
1052
1053 auto resp = static_cast<GetSDRInfoResp *>(response);
1054 resp->sdrVersion = ipmiSdrVersion;
1055 uint16_t recordCount = sensorTree.size();
1056
1057 // todo: for now, sdr count is number of sensors
1058 resp->recordCountLS = recordCount & 0xFF;
1059 resp->recordCountMS = recordCount >> 8;
1060
1061 // free space unspcified
1062 resp->freeSpace[0] = 0xFF;
1063 resp->freeSpace[1] = 0xFF;
1064
1065 resp->mostRecentAddition = sdrLastAdd;
1066 resp->mostRecentErase = sdrLastRemove;
1067 resp->operationSupport = static_cast<uint8_t>(
1068 SdrRepositoryInfoOps::overflow); // write not supported
1069 resp->operationSupport |=
1070 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
1071 resp->operationSupport |= static_cast<uint8_t>(
1072 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
1073 *dataLen = sizeof(GetSDRInfoResp);
1074 return IPMI_CC_OK;
1075}
1076
1077ipmi_ret_t ipmiStorageGetSDRAllocationInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1078 ipmi_request_t request,
1079 ipmi_response_t response,
1080 ipmi_data_len_t dataLen,
1081 ipmi_context_t context)
1082{
1083 if (*dataLen)
1084 {
1085 *dataLen = 0;
1086 return IPMI_CC_REQ_DATA_LEN_INVALID;
1087 }
1088 *dataLen = 0; // default to 0 in case of an error
1089 GetAllocInfoResp *resp = static_cast<GetAllocInfoResp *>(response);
1090
1091 // 0000h unspecified number of alloc units
1092 resp->allocUnitsLSB = 0;
1093 resp->allocUnitsMSB = 0;
1094
1095 // max unit size is size of max record
1096 resp->allocUnitSizeLSB = maxSDRTotalSize & 0xFF;
1097 resp->allocUnitSizeMSB = maxSDRTotalSize >> 8;
1098 // read only sdr, no free alloc blocks
1099 resp->allocUnitFreeLSB = 0;
1100 resp->allocUnitFreeMSB = 0;
1101 resp->allocUnitLargestFreeLSB = 0;
1102 resp->allocUnitLargestFreeMSB = 0;
1103 // only allow one block at a time
1104 resp->maxRecordSize = 1;
1105
1106 *dataLen = sizeof(GetAllocInfoResp);
1107
1108 return IPMI_CC_OK;
1109}
1110
1111ipmi_ret_t ipmiStorageReserveSDR(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1112 ipmi_request_t request,
1113 ipmi_response_t response,
1114 ipmi_data_len_t dataLen,
1115 ipmi_context_t context)
1116{
1117 printCommand(+netfn, +cmd);
1118
1119 if (*dataLen)
1120 {
1121 *dataLen = 0;
1122 return IPMI_CC_REQ_DATA_LEN_INVALID;
1123 }
1124 *dataLen = 0; // default to 0 in case of an error
1125 sdrReservationID++;
James Feista80cb902019-02-14 13:05:25 -08001126 if (sdrReservationID == 0)
1127 {
1128 sdrReservationID++;
1129 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001130 *dataLen = 2;
1131 auto resp = static_cast<uint8_t *>(response);
1132 resp[0] = sdrReservationID & 0xFF;
1133 resp[1] = sdrReservationID >> 8;
1134
1135 return IPMI_CC_OK;
1136}
1137
James Feistb49a98a2019-04-16 13:48:09 -07001138ipmi::RspType<uint16_t, // next record ID
1139 std::vector<uint8_t> // payload
1140 >
1141 ipmiStorageGetSDR(uint16_t reservationID, uint16_t recordID, uint8_t offset,
1142 uint8_t bytesToRead)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001143{
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001144 constexpr uint16_t lastRecordIndex = 0xFFFF;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001145
1146 // reservation required for partial reads with non zero offset into
1147 // record
James Feistb49a98a2019-04-16 13:48:09 -07001148 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001149 {
James Feistb49a98a2019-04-16 13:48:09 -07001150 return ipmi::responseInvalidReservationId();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001151 }
1152
1153 if (sensorTree.empty() && !getSensorSubtree(sensorTree))
1154 {
James Feistb49a98a2019-04-16 13:48:09 -07001155 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001156 }
1157
1158 size_t fruCount = 0;
1159 ipmi_ret_t ret = ipmi::storage::getFruSdrCount(fruCount);
1160 if (ret != IPMI_CC_OK)
1161 {
James Feistb49a98a2019-04-16 13:48:09 -07001162 return ipmi::response(ret);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001163 }
1164
1165 size_t lastRecord = sensorTree.size() + fruCount - 1;
James Feistb49a98a2019-04-16 13:48:09 -07001166 if (recordID == lastRecordIndex)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001167 {
James Feistb49a98a2019-04-16 13:48:09 -07001168 recordID = lastRecord;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001169 }
James Feistb49a98a2019-04-16 13:48:09 -07001170 if (recordID > lastRecord)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001171 {
James Feistb49a98a2019-04-16 13:48:09 -07001172 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001173 }
1174
James Feistb49a98a2019-04-16 13:48:09 -07001175 uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001176
James Feistb49a98a2019-04-16 13:48:09 -07001177 if (recordID >= sensorTree.size())
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001178 {
James Feistb49a98a2019-04-16 13:48:09 -07001179 size_t fruIndex = recordID - sensorTree.size();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001180 if (fruIndex >= fruCount)
1181 {
James Feistb49a98a2019-04-16 13:48:09 -07001182 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001183 }
1184 get_sdr::SensorDataFruRecord data;
James Feistb49a98a2019-04-16 13:48:09 -07001185 if (offset > sizeof(data))
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001186 {
James Feistb49a98a2019-04-16 13:48:09 -07001187 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001188 }
1189 ret = ipmi::storage::getFruSdrs(fruIndex, data);
1190 if (ret != IPMI_CC_OK)
1191 {
James Feistb49a98a2019-04-16 13:48:09 -07001192 return ipmi::response(ret);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001193 }
James Feistb49a98a2019-04-16 13:48:09 -07001194 data.header.record_id_msb = recordID << 8;
1195 data.header.record_id_lsb = recordID & 0xFF;
1196 if (sizeof(data) < (offset + bytesToRead))
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001197 {
James Feistb49a98a2019-04-16 13:48:09 -07001198 bytesToRead = sizeof(data) - offset;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001199 }
James Feistb49a98a2019-04-16 13:48:09 -07001200
1201 uint8_t *respStart = reinterpret_cast<uint8_t *>(&data) + offset;
1202 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
1203
1204 return ipmi::responseSuccess(nextRecordId, recordData);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001205 }
1206
1207 std::string connection;
1208 std::string path;
James Feistb49a98a2019-04-16 13:48:09 -07001209 uint16_t sensorIndex = recordID;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001210 for (const auto &sensor : sensorTree)
1211 {
1212 if (sensorIndex-- == 0)
1213 {
1214 if (!sensor.second.size())
1215 {
James Feistb49a98a2019-04-16 13:48:09 -07001216 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001217 }
1218 connection = sensor.second.begin()->first;
1219 path = sensor.first;
1220 break;
1221 }
1222 }
1223
1224 SensorMap sensorMap;
1225 if (!getSensorMap(connection, path, sensorMap))
1226 {
James Feistb49a98a2019-04-16 13:48:09 -07001227 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001228 }
James Feistb49a98a2019-04-16 13:48:09 -07001229 uint8_t sensornumber = (recordID & 0xFF);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001230 get_sdr::SensorDataFullRecord record = {0};
1231
James Feistb49a98a2019-04-16 13:48:09 -07001232 record.header.record_id_msb = recordID << 8;
1233 record.header.record_id_lsb = recordID & 0xFF;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001234 record.header.sdr_version = ipmiSdrVersion;
1235 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1236 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1237 sizeof(get_sdr::SensorDataRecordHeader);
1238 record.key.owner_id = 0x20;
1239 record.key.owner_lun = 0x0;
1240 record.key.sensor_number = sensornumber;
1241
1242 record.body.entity_id = 0x0;
1243 record.body.entity_instance = 0x01;
James Feist7086a882019-03-13 10:46:00 -07001244 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001245 record.body.sensor_type = getSensorTypeFromPath(path);
1246 std::string type = getSensorTypeStringFromPath(path);
1247 auto typeCstr = type.c_str();
1248 auto findUnits = sensorUnits.find(typeCstr);
1249 if (findUnits != sensorUnits.end())
1250 {
1251 record.body.sensor_units_2_base =
1252 static_cast<uint8_t>(findUnits->second);
1253 } // else default 0x0 unspecified
1254
1255 record.body.event_reading_type = getSensorEventTypeFromPath(path);
1256
1257 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
1258 if (sensorObject == sensorMap.end())
1259 {
James Feistb49a98a2019-04-16 13:48:09 -07001260 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001261 }
1262
1263 auto maxObject = sensorObject->second.find("MaxValue");
1264 auto minObject = sensorObject->second.find("MinValue");
1265 double max = 128;
1266 double min = -127;
1267 if (maxObject != sensorObject->second.end())
1268 {
James Feist14fde842018-12-06 10:19:40 -08001269 max = variant_ns::visit(VariantToDoubleVisitor(), maxObject->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001270 }
1271
1272 if (minObject != sensorObject->second.end())
1273 {
James Feist14fde842018-12-06 10:19:40 -08001274 min = variant_ns::visit(VariantToDoubleVisitor(), minObject->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001275 }
1276
Yong Li1f2eb5e2019-05-23 14:07:17 +08001277 int16_t mValue = 0;
1278 int8_t rExp = 0;
1279 int16_t bValue = 0;
1280 int8_t bExp = 0;
1281 bool bSigned = false;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001282
1283 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1284 {
James Feistb49a98a2019-04-16 13:48:09 -07001285 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001286 }
1287
1288 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1289 record.body.m_lsb = mValue & 0xFF;
1290
1291 // move the smallest bit of the MSB into place (bit 9)
1292 // the MSbs are bits 7:8 in m_msb_and_tolerance
1293 uint8_t mMsb = (mValue & (1 << 8)) > 0 ? (1 << 6) : 0;
1294
1295 // assign the negative
1296 if (mValue < 0)
1297 {
1298 mMsb |= (1 << 7);
1299 }
1300 record.body.m_msb_and_tolerance = mMsb;
1301
1302 record.body.b_lsb = bValue & 0xFF;
1303
1304 // move the smallest bit of the MSB into place
1305 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1306 uint8_t bMsb = (bValue & (1 << 8)) > 0 ? (1 << 6) : 0;
1307
1308 // assign the negative
1309 if (bValue < 0)
1310 {
1311 bMsb |= (1 << 7);
1312 }
1313 record.body.b_msb_and_accuracy_lsb = bMsb;
1314
1315 record.body.r_b_exponents = bExp & 0x7;
1316 if (bExp < 0)
1317 {
1318 record.body.r_b_exponents |= 1 << 3;
1319 }
1320 record.body.r_b_exponents = (rExp & 0x7) << 4;
1321 if (rExp < 0)
1322 {
1323 record.body.r_b_exponents |= 1 << 7;
1324 }
1325
1326 // todo fill out rest of units
1327 if (bSigned)
1328 {
1329 record.body.sensor_units_1 = 1 << 7;
1330 }
1331
1332 // populate sensor name from path
1333 std::string name;
1334 size_t nameStart = path.rfind("/");
1335 if (nameStart != std::string::npos)
1336 {
1337 name = path.substr(nameStart + 1, std::string::npos - nameStart);
1338 }
1339
1340 std::replace(name.begin(), name.end(), '_', ' ');
1341 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
1342 {
James Feist979a2322019-05-15 09:06:54 -07001343 // try to not truncate by replacing common words
1344 constexpr std::array<std::pair<const char *, const char *>, 2>
1345 replaceWords = {std::make_pair("Output", "Out"),
1346 std::make_pair("Input", "In")};
1347 for (const auto &[find, replace] : replaceWords)
1348 {
1349 boost::replace_all(name, find, replace);
1350 }
1351
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001352 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
1353 }
1354 record.body.id_string_info = name.size();
1355 std::strncpy(record.body.id_string, name.c_str(),
1356 sizeof(record.body.id_string));
1357
James Feistc4b15bc2019-04-16 15:41:39 -07001358 IPMIThresholds thresholdData;
1359 try
1360 {
1361 thresholdData = getIPMIThresholds(sensorMap);
1362 }
1363 catch (std::exception &)
1364 {
1365 return ipmi::responseResponseError();
1366 }
1367
1368 if (thresholdData.criticalHigh)
1369 {
1370 record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1371 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1372 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1373 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1374 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1375 record.body.discrete_reading_setting_mask[0] |=
1376 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1377 }
1378 if (thresholdData.warningHigh)
1379 {
1380 record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1381 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1382 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1383 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1384 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1385 record.body.discrete_reading_setting_mask[0] |=
1386 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1387 }
1388 if (thresholdData.criticalLow)
1389 {
1390 record.body.lower_critical_threshold = *thresholdData.criticalLow;
1391 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1392 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1393 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1394 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1395 record.body.discrete_reading_setting_mask[0] |=
1396 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1397 }
1398 if (thresholdData.warningLow)
1399 {
1400 record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1401 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1402 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1403 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1404 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1405 record.body.discrete_reading_setting_mask[0] |=
1406 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1407 }
1408
1409 // everything that is readable is setable
1410 record.body.discrete_reading_setting_mask[1] =
1411 record.body.discrete_reading_setting_mask[0];
1412
James Feistb49a98a2019-04-16 13:48:09 -07001413 if (sizeof(get_sdr::SensorDataFullRecord) < (offset + bytesToRead))
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001414 {
James Feistb49a98a2019-04-16 13:48:09 -07001415 bytesToRead = sizeof(get_sdr::SensorDataFullRecord) - offset;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001416 }
1417
James Feistb49a98a2019-04-16 13:48:09 -07001418 uint8_t *respStart = reinterpret_cast<uint8_t *>(&record) + offset;
1419 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001420
James Feistb49a98a2019-04-16 13:48:09 -07001421 return ipmi::responseSuccess(nextRecordId, recordData);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001422}
1423/* end storage commands */
1424
1425void registerSensorFunctions()
1426{
1427 // get firmware version information
1428 ipmiPrintAndRegister(NETFUN_SENSOR, IPMI_CMD_WILDCARD, nullptr,
1429 ipmiSensorWildcardHandler, PRIVILEGE_USER);
1430
1431 // <Get Sensor Type>
1432 ipmiPrintAndRegister(
1433 NETFUN_SENSOR,
1434 static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdGetSensorType),
1435 nullptr, ipmiSensorWildcardHandler, PRIVILEGE_USER);
1436
1437 // <Set Sensor Reading and Event Status>
1438 ipmiPrintAndRegister(
1439 NETFUN_SENSOR,
1440 static_cast<ipmi_cmd_t>(
1441 IPMINetfnSensorCmds::ipmiCmdSetSensorReadingAndEventStatus),
1442 nullptr, ipmiSensorWildcardHandler, PRIVILEGE_OPERATOR);
1443
Jason M. Billsae6bdb12019-04-02 12:00:04 -07001444 // <Platform Event>
1445 ipmi::registerHandler(
1446 ipmi::prioOemBase, ipmi::netFnSensor,
1447 static_cast<ipmi::Cmd>(ipmi::sensor_event::cmdPlatformEvent),
1448 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
1449
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001450 // <Get Sensor Reading>
James Feist0cd014a2019-04-08 15:04:33 -07001451 ipmi::registerHandler(
1452 ipmi::prioOemBase, NETFUN_SENSOR,
1453 static_cast<ipmi::Cmd>(IPMINetfnSensorCmds::ipmiCmdGetSensorReading),
1454 ipmi::Privilege::User, ipmiSenGetSensorReading);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001455
1456 // <Get Sensor Threshold>
James Feist902c4c52019-04-16 14:51:31 -07001457 ipmi::registerHandler(
1458 ipmi::prioOemBase, NETFUN_SENSOR,
1459 static_cast<ipmi::Cmd>(IPMINetfnSensorCmds::ipmiCmdGetSensorThreshold),
1460 ipmi::Privilege::User, ipmiSenGetSensorThresholds);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001461
1462 ipmiPrintAndRegister(
1463 NETFUN_SENSOR,
1464 static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdSetSensorThreshold),
1465 nullptr, ipmiSenSetSensorThresholds, PRIVILEGE_OPERATOR);
1466
1467 // <Get Sensor Event Enable>
1468 ipmiPrintAndRegister(NETFUN_SENSOR,
1469 static_cast<ipmi_cmd_t>(
1470 IPMINetfnSensorCmds::ipmiCmdGetSensorEventEnable),
1471 nullptr, ipmiSenGetSensorEventEnable, PRIVILEGE_USER);
1472
1473 // <Get Sensor Event Status>
1474 ipmiPrintAndRegister(NETFUN_SENSOR,
1475 static_cast<ipmi_cmd_t>(
1476 IPMINetfnSensorCmds::ipmiCmdGetSensorEventStatus),
1477 nullptr, ipmiSenGetSensorEventStatus, PRIVILEGE_USER);
1478
1479 // register all storage commands for both Sensor and Storage command
1480 // versions
1481
1482 // <Get SDR Repository Info>
1483 ipmiPrintAndRegister(
1484 NETFUN_STORAGE,
1485 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdGetRepositoryInfo),
1486 nullptr, ipmiStorageGetSDRRepositoryInfo, PRIVILEGE_USER);
1487
1488 // <Get SDR Allocation Info>
1489 ipmiPrintAndRegister(NETFUN_STORAGE,
1490 static_cast<ipmi_cmd_t>(
1491 IPMINetfnStorageCmds::ipmiCmdGetSDRAllocationInfo),
1492 nullptr, ipmiStorageGetSDRAllocationInfo,
1493 PRIVILEGE_USER);
1494
1495 // <Reserve SDR Repo>
1496 ipmiPrintAndRegister(NETFUN_SENSOR,
1497 static_cast<ipmi_cmd_t>(
1498 IPMINetfnSensorCmds::ipmiCmdReserveDeviceSDRRepo),
1499 nullptr, ipmiStorageReserveSDR, PRIVILEGE_USER);
1500
1501 ipmiPrintAndRegister(
1502 NETFUN_STORAGE,
1503 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdReserveSDR),
1504 nullptr, ipmiStorageReserveSDR, PRIVILEGE_USER);
1505
1506 // <Get Sdr>
James Feistb49a98a2019-04-16 13:48:09 -07001507 ipmi::registerHandler(
1508 ipmi::prioOemBase, NETFUN_SENSOR,
1509 static_cast<ipmi::Cmd>(IPMINetfnSensorCmds::ipmiCmdGetDeviceSDR),
1510 ipmi::Privilege::User, ipmiStorageGetSDR);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001511
James Feistb49a98a2019-04-16 13:48:09 -07001512 ipmi::registerHandler(
1513 ipmi::prioOemBase, NETFUN_STORAGE,
1514 static_cast<ipmi::Cmd>(IPMINetfnStorageCmds::ipmiCmdGetSDR),
1515 ipmi::Privilege::User, ipmiStorageGetSDR);
1516
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001517 return;
1518}
1519} // namespace ipmi