blob: d84d8b165640174cb2ec36bf8d62f491092fbc11 [file] [log] [blame]
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001/*
2// Copyright (c) 2017 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070017#include <boost/algorithm/string.hpp>
18#include <boost/container/flat_map.hpp>
19#include <chrono>
20#include <cmath>
21#include <commandutils.hpp>
22#include <iostream>
James Feist2a265d52019-04-08 11:16:27 -070023#include <ipmid/api.hpp>
Vernon Mauery5480ef62019-03-20 13:43:11 -070024#include <ipmid/utils.hpp>
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070025#include <phosphor-logging/log.hpp>
26#include <sdbusplus/bus.hpp>
27#include <sdrutils.hpp>
28#include <sensorcommands.hpp>
29#include <sensorutils.hpp>
30#include <storagecommands.hpp>
31#include <string>
32
33namespace ipmi
34{
35using ManagedObjectType =
36 std::map<sdbusplus::message::object_path,
37 std::map<std::string, std::map<std::string, DbusVariant>>>;
38
39using SensorMap = std::map<std::string, std::map<std::string, DbusVariant>>;
James Feist14fde842018-12-06 10:19:40 -080040namespace variant_ns = sdbusplus::message::variant_ns;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070041
42static constexpr int sensorListUpdatePeriod = 10;
43static constexpr int sensorMapUpdatePeriod = 2;
44
45constexpr size_t maxSDRTotalSize =
46 76; // Largest SDR Record Size (type 01) + SDR Overheader Size
47constexpr static const uint32_t noTimestamp = 0xFFFFFFFF;
48
49static uint16_t sdrReservationID;
50static uint32_t sdrLastAdd = noTimestamp;
51static uint32_t sdrLastRemove = noTimestamp;
52
Richard Marian Thomaiyar01fbcb52018-11-19 22:04:34 +053053SensorSubTree sensorTree;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070054static boost::container::flat_map<std::string, ManagedObjectType> SensorCache;
55
Jason M. Bills17add592018-11-12 14:30:12 -080056// Specify the comparison required to sort and find char* map objects
57struct CmpStr
58{
59 bool operator()(const char *a, const char *b) const
60 {
61 return std::strcmp(a, b) < 0;
62 }
63};
64const static boost::container::flat_map<const char *, SensorUnits, CmpStr>
65 sensorUnits{{{"temperature", SensorUnits::degreesC},
66 {"voltage", SensorUnits::volts},
67 {"current", SensorUnits::amps},
68 {"fan_tach", SensorUnits::rpm},
69 {"power", SensorUnits::watts}}};
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070070
71void registerSensorFunctions() __attribute__((constructor));
72static sdbusplus::bus::bus dbus(ipmid_get_sd_bus_connection());
73
74static sdbusplus::bus::match::match sensorAdded(
75 dbus,
76 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
77 "sensors/'",
78 [](sdbusplus::message::message &m) {
79 sensorTree.clear();
80 sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>(
81 std::chrono::system_clock::now().time_since_epoch())
82 .count();
83 });
84
85static sdbusplus::bus::match::match sensorRemoved(
86 dbus,
87 "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/"
88 "sensors/'",
89 [](sdbusplus::message::message &m) {
90 sensorTree.clear();
91 sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>(
92 std::chrono::system_clock::now().time_since_epoch())
93 .count();
94 });
95
James Feist392786a2019-03-19 13:36:10 -070096// this keeps track of deassertions for sensor event status command. A
97// deasertion can only happen if an assertion was seen first.
98static boost::container::flat_map<
99 std::string, boost::container::flat_map<std::string, std::optional<bool>>>
100 thresholdDeassertMap;
101
102static sdbusplus::bus::match::match thresholdChanged(
103 dbus,
104 "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus."
105 "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
106 [](sdbusplus::message::message &m) {
107 boost::container::flat_map<std::string, std::variant<bool, double>>
108 values;
109 m.read(std::string(), values);
110
111 auto findAssert =
112 std::find_if(values.begin(), values.end(), [](const auto &pair) {
113 return pair.first.find("Alarm") != std::string::npos;
114 });
115 if (findAssert != values.end())
116 {
117 auto ptr = std::get_if<bool>(&(findAssert->second));
118 if (ptr == nullptr)
119 {
120 phosphor::logging::log<phosphor::logging::level::ERR>(
121 "thresholdChanged: Assert non bool");
122 return;
123 }
124 if (*ptr)
125 {
126 phosphor::logging::log<phosphor::logging::level::INFO>(
127 "thresholdChanged: Assert",
128 phosphor::logging::entry("SENSOR=%s", m.get_path()));
129 thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr;
130 }
131 else
132 {
133 auto &value =
134 thresholdDeassertMap[m.get_path()][findAssert->first];
135 if (value)
136 {
137 phosphor::logging::log<phosphor::logging::level::INFO>(
138 "thresholdChanged: deassert",
139 phosphor::logging::entry("SENSOR=%s", m.get_path()));
140 value = *ptr;
141 }
142 }
143 }
144 });
145
James Feistaecaef72019-04-26 10:30:32 -0700146static void getSensorMaxMin(const SensorMap &sensorMap, double &max,
147 double &min)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700148{
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700149 max = 127;
150 min = -128;
151
James Feistaecaef72019-04-26 10:30:32 -0700152 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
153 auto critical =
154 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
155 auto warning =
156 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
157
158 if (sensorObject != sensorMap.end())
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700159 {
James Feistaecaef72019-04-26 10:30:32 -0700160 auto maxMap = sensorObject->second.find("MaxValue");
161 auto minMap = sensorObject->second.find("MinValue");
162
163 if (maxMap != sensorObject->second.end())
164 {
165 max = variant_ns::visit(VariantToDoubleVisitor(), maxMap->second);
166 }
167 if (minMap != sensorObject->second.end())
168 {
169 min = variant_ns::visit(VariantToDoubleVisitor(), minMap->second);
170 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700171 }
James Feistaecaef72019-04-26 10:30:32 -0700172 if (critical != sensorMap.end())
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700173 {
James Feistaecaef72019-04-26 10:30:32 -0700174 auto lower = critical->second.find("CriticalLow");
175 auto upper = critical->second.find("CriticalHigh");
176 if (lower != critical->second.end())
177 {
178 double value =
179 variant_ns::visit(VariantToDoubleVisitor(), lower->second);
180 min = std::min(value, min);
181 }
182 if (upper != critical->second.end())
183 {
184 double value =
185 variant_ns::visit(VariantToDoubleVisitor(), upper->second);
186 max = std::max(value, max);
187 }
188 }
189 if (warning != sensorMap.end())
190 {
191
192 auto lower = warning->second.find("WarningLow");
193 auto upper = warning->second.find("WarningHigh");
194 if (lower != warning->second.end())
195 {
196 double value =
197 variant_ns::visit(VariantToDoubleVisitor(), lower->second);
198 min = std::min(value, min);
199 }
200 if (upper != warning->second.end())
201 {
202 double value =
203 variant_ns::visit(VariantToDoubleVisitor(), upper->second);
204 max = std::max(value, max);
205 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700206 }
207}
208
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700209static bool getSensorMap(std::string sensorConnection, std::string sensorPath,
210 SensorMap &sensorMap)
211{
212 static boost::container::flat_map<
213 std::string, std::chrono::time_point<std::chrono::steady_clock>>
214 updateTimeMap;
215
216 auto updateFind = updateTimeMap.find(sensorConnection);
217 auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
218 if (updateFind != updateTimeMap.end())
219 {
220 lastUpdate = updateFind->second;
221 }
222
223 auto now = std::chrono::steady_clock::now();
224
225 if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
226 .count() > sensorMapUpdatePeriod)
227 {
228 updateTimeMap[sensorConnection] = now;
229
230 auto managedObj = dbus.new_method_call(
231 sensorConnection.c_str(), "/", "org.freedesktop.DBus.ObjectManager",
232 "GetManagedObjects");
233
234 ManagedObjectType managedObjects;
235 try
236 {
237 auto reply = dbus.call(managedObj);
238 reply.read(managedObjects);
239 }
240 catch (sdbusplus::exception_t &)
241 {
242 phosphor::logging::log<phosphor::logging::level::ERR>(
243 "Error getting managed objects from connection",
244 phosphor::logging::entry("CONNECTION=%s",
245 sensorConnection.c_str()));
246 return false;
247 }
248
249 SensorCache[sensorConnection] = managedObjects;
250 }
251 auto connection = SensorCache.find(sensorConnection);
252 if (connection == SensorCache.end())
253 {
254 return false;
255 }
256 auto path = connection->second.find(sensorPath);
257 if (path == connection->second.end())
258 {
259 return false;
260 }
261 sensorMap = path->second;
262
263 return true;
264}
265
266/* sensor commands */
267ipmi_ret_t ipmiSensorWildcardHandler(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
268 ipmi_request_t request,
269 ipmi_response_t response,
270 ipmi_data_len_t dataLen,
271 ipmi_context_t context)
272{
273 *dataLen = 0;
274 printCommand(+netfn, +cmd);
275 return IPMI_CC_INVALID;
276}
277
Jason M. Billsae6bdb12019-04-02 12:00:04 -0700278ipmi::RspType<> ipmiSenPlatformEvent(ipmi::message::Payload &p)
279{
280 uint8_t generatorID = 0;
281 uint8_t evmRev = 0;
282 uint8_t sensorType = 0;
283 uint8_t sensorNum = 0;
284 uint8_t eventType = 0;
285 uint8_t eventData1 = 0;
286 std::optional<uint8_t> eventData2 = 0;
287 std::optional<uint8_t> eventData3 = 0;
288
289 // todo: This check is supposed to be based on the incoming channel.
290 // e.g. system channel will provide upto 8 bytes including generator
291 // ID, but ipmb channel will provide only up to 7 bytes without the
292 // generator ID.
293 // Support for this check is coming in future patches, so for now just base
294 // it on if the first byte is the EvMRev (0x04).
295 if (p.size() && p.data()[0] == 0x04)
296 {
297 p.unpack(evmRev, sensorType, sensorNum, eventType, eventData1,
298 eventData2, eventData3);
299 // todo: the generator ID for this channel is supposed to come from the
300 // IPMB requesters slave address. Support for this is coming in future
301 // patches, so for now just assume it is coming from the ME (0x2C).
302 generatorID = 0x2C;
303 }
304 else
305 {
306 p.unpack(generatorID, evmRev, sensorType, sensorNum, eventType,
307 eventData1, eventData2, eventData3);
308 }
309 if (!p.fullyUnpacked())
310 {
311 return ipmi::responseReqDataLenInvalid();
312 }
313
314 bool assert = eventType & directionMask ? false : true;
315 std::vector<uint8_t> eventData;
316 eventData.push_back(eventData1);
317 eventData.push_back(eventData2.value_or(0xFF));
318 eventData.push_back(eventData3.value_or(0xFF));
319
320 std::string sensorPath = getPathFromSensorNumber(sensorNum);
321 std::string service =
322 ipmi::getService(dbus, ipmiSELAddInterface, ipmiSELPath);
323 sdbusplus::message::message writeSEL = dbus.new_method_call(
324 service.c_str(), ipmiSELPath, ipmiSELAddInterface, "IpmiSelAdd");
325 writeSEL.append(ipmiSELAddMessage, sensorPath, eventData, assert,
326 static_cast<uint16_t>(generatorID));
327 try
328 {
329 dbus.call(writeSEL);
330 }
331 catch (sdbusplus::exception_t &e)
332 {
333 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
334 return ipmi::responseUnspecifiedError();
335 }
336
337 return ipmi::responseSuccess();
338}
339
James Feist0cd014a2019-04-08 15:04:33 -0700340ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
341 ipmiSenGetSensorReading(uint8_t sensnum)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700342{
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700343 std::string connection;
344 std::string path;
345
346 auto status = getSensorConnection(sensnum, connection, path);
347 if (status)
348 {
James Feist0cd014a2019-04-08 15:04:33 -0700349 return ipmi::response(status);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700350 }
351
352 SensorMap sensorMap;
353 if (!getSensorMap(connection, path, sensorMap))
354 {
James Feist0cd014a2019-04-08 15:04:33 -0700355 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700356 }
357 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
358
359 if (sensorObject == sensorMap.end() ||
360 sensorObject->second.find("Value") == sensorObject->second.end())
361 {
James Feist0cd014a2019-04-08 15:04:33 -0700362 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700363 }
James Feist0cd014a2019-04-08 15:04:33 -0700364 auto &valueVariant = sensorObject->second["Value"];
365 double reading = variant_ns::visit(VariantToDoubleVisitor(), valueVariant);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700366
367 double max;
368 double min;
James Feistaecaef72019-04-26 10:30:32 -0700369 getSensorMaxMin(sensorMap, max, min);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700370
371 int16_t mValue = 0;
372 int16_t bValue = 0;
373 int8_t rExp = 0;
374 int8_t bExp = 0;
375 bool bSigned = false;
376
377 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
378 {
James Feist0cd014a2019-04-08 15:04:33 -0700379 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700380 }
381
James Feist0cd014a2019-04-08 15:04:33 -0700382 uint8_t value =
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700383 scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
James Feist0cd014a2019-04-08 15:04:33 -0700384 uint8_t operation =
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700385 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
James Feist0cd014a2019-04-08 15:04:33 -0700386 operation |=
James Feist81a95c12019-03-01 15:08:28 -0800387 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700388
James Feist0cd014a2019-04-08 15:04:33 -0700389 uint8_t thresholds = 0;
390
391 auto warningObject =
392 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
393 if (warningObject != sensorMap.end())
394 {
395 auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
396 auto alarmLow = warningObject->second.find("WarningAlarmLow");
397 if (alarmHigh != warningObject->second.end())
398 {
399 if (std::get<bool>(alarmHigh->second))
400 {
401 thresholds |= static_cast<uint8_t>(
402 IPMISensorReadingByte3::upperNonCritical);
403 }
404 }
405 if (alarmLow != warningObject->second.end())
406 {
407 if (std::get<bool>(alarmLow->second))
408 {
409 thresholds |= static_cast<uint8_t>(
410 IPMISensorReadingByte3::lowerNonCritical);
411 }
412 }
413 }
414
415 auto criticalObject =
416 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
417 if (criticalObject != sensorMap.end())
418 {
419 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
420 auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
421 if (alarmHigh != criticalObject->second.end())
422 {
423 if (std::get<bool>(alarmHigh->second))
424 {
425 thresholds |=
426 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
427 }
428 }
429 if (alarmLow != criticalObject->second.end())
430 {
431 if (std::get<bool>(alarmLow->second))
432 {
433 thresholds |=
434 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
435 }
436 }
437 }
438
439 // no discrete as of today so optional byte is never returned
440 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700441}
442
443ipmi_ret_t ipmiSenSetSensorThresholds(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
444 ipmi_request_t request,
445 ipmi_response_t response,
446 ipmi_data_len_t dataLen,
447 ipmi_context_t context)
448{
449 if (*dataLen != 8)
450 {
451 *dataLen = 0;
452 return IPMI_CC_REQ_DATA_LEN_INVALID;
453 }
454 *dataLen = 0;
455
456 SensorThresholdReq *req = static_cast<SensorThresholdReq *>(request);
457
458 // upper two bits reserved
459 if (req->mask & 0xC0)
460 {
461 return IPMI_CC_INVALID_FIELD_REQUEST;
462 }
463
464 // lower nc and upper nc not suppported on any sensor
465 if ((req->mask & static_cast<uint8_t>(
466 SensorThresholdReqEnable::setLowerNonRecoverable)) ||
467 (req->mask & static_cast<uint8_t>(
468 SensorThresholdReqEnable::setUpperNonRecoverable)))
469 {
470 return IPMI_CC_INVALID_FIELD_REQUEST;
471 }
472
473 // if no bits are set in the mask, nothing to do
474 if (!(req->mask))
475 {
476 return IPMI_CC_OK;
477 }
478
479 std::string connection;
480 std::string path;
481
482 ipmi_ret_t status = getSensorConnection(req->sensorNum, connection, path);
483 if (status)
484 {
485 return status;
486 }
487 SensorMap sensorMap;
488 if (!getSensorMap(connection, path, sensorMap))
489 {
490 return IPMI_CC_RESPONSE_ERROR;
491 }
492
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700493 double max = 0;
494 double min = 0;
James Feistaecaef72019-04-26 10:30:32 -0700495 getSensorMaxMin(sensorMap, max, min);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700496
497 int16_t mValue = 0;
498 int16_t bValue = 0;
499 int8_t rExp = 0;
500 int8_t bExp = 0;
501 bool bSigned = false;
502
503 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
504 {
505 return IPMI_CC_RESPONSE_ERROR;
506 }
507
508 bool setLowerCritical =
509 req->mask &
510 static_cast<uint8_t>(SensorThresholdReqEnable::setLowerCritical);
511 bool setUpperCritical =
512 req->mask &
513 static_cast<uint8_t>(SensorThresholdReqEnable::setUpperCritical);
514
515 bool setLowerWarning =
516 req->mask &
517 static_cast<uint8_t>(SensorThresholdReqEnable::setLowerNonCritical);
518 bool setUpperWarning =
519 req->mask &
520 static_cast<uint8_t>(SensorThresholdReqEnable::setUpperNonCritical);
521
522 // store a vector of property name, value to set, and interface
523 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
524
525 // define the indexes of the tuple
526 constexpr uint8_t propertyName = 0;
527 constexpr uint8_t thresholdValue = 1;
528 constexpr uint8_t interface = 2;
529 // verifiy all needed fields are present
530 if (setLowerCritical || setUpperCritical)
531 {
532 auto findThreshold =
533 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
534 if (findThreshold == sensorMap.end())
535 {
536 return IPMI_CC_INVALID_FIELD_REQUEST;
537 }
538 if (setLowerCritical)
539 {
540 auto findLower = findThreshold->second.find("CriticalLow");
541 if (findLower == findThreshold->second.end())
542 {
543 return IPMI_CC_INVALID_FIELD_REQUEST;
544 }
545 thresholdsToSet.emplace_back("CriticalLow", req->lowerCritical,
546 findThreshold->first);
547 }
548 if (setUpperCritical)
549 {
550 auto findUpper = findThreshold->second.find("CriticalHigh");
551 if (findUpper == findThreshold->second.end())
552 {
553 return IPMI_CC_INVALID_FIELD_REQUEST;
554 }
555 thresholdsToSet.emplace_back("CriticalHigh", req->upperCritical,
556 findThreshold->first);
557 }
558 }
559 if (setLowerWarning || setUpperWarning)
560 {
561 auto findThreshold =
562 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
563 if (findThreshold == sensorMap.end())
564 {
565 return IPMI_CC_INVALID_FIELD_REQUEST;
566 }
567 if (setLowerWarning)
568 {
569 auto findLower = findThreshold->second.find("WarningLow");
570 if (findLower == findThreshold->second.end())
571 {
572 return IPMI_CC_INVALID_FIELD_REQUEST;
573 }
574 thresholdsToSet.emplace_back("WarningLow", req->lowerNonCritical,
575 findThreshold->first);
576 }
577 if (setUpperWarning)
578 {
579 auto findUpper = findThreshold->second.find("WarningHigh");
580 if (findUpper == findThreshold->second.end())
581 {
582 return IPMI_CC_INVALID_FIELD_REQUEST;
583 }
584 thresholdsToSet.emplace_back("WarningHigh", req->upperNonCritical,
585 findThreshold->first);
586 }
587 }
588
589 for (const auto &property : thresholdsToSet)
590 {
591 // from section 36.3 in the IPMI Spec, assume all linear
592 double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
593 (bValue * std::pow(10, bExp))) *
594 std::pow(10, rExp);
595 setDbusProperty(dbus, connection, path, std::get<interface>(property),
596 std::get<propertyName>(property),
597 ipmi::Value(valueToSet));
598 }
599
600 return IPMI_CC_OK;
601}
602
James Feist902c4c52019-04-16 14:51:31 -0700603IPMIThresholds getIPMIThresholds(const SensorMap &sensorMap)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700604{
James Feist902c4c52019-04-16 14:51:31 -0700605 IPMIThresholds resp;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700606 auto warningInterface =
607 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
608 auto criticalInterface =
609 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
610
611 if ((warningInterface != sensorMap.end()) ||
612 (criticalInterface != sensorMap.end()))
613 {
614 auto sensorPair = sensorMap.find("xyz.openbmc_project.Sensor.Value");
615
616 if (sensorPair == sensorMap.end())
617 {
618 // should not have been able to find a sensor not implementing
619 // the sensor object
James Feist902c4c52019-04-16 14:51:31 -0700620 throw std::runtime_error("Invalid sensor map");
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700621 }
622
623 double max;
624 double min;
James Feistaecaef72019-04-26 10:30:32 -0700625 getSensorMaxMin(sensorMap, max, min);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700626
627 int16_t mValue = 0;
628 int16_t bValue = 0;
629 int8_t rExp = 0;
630 int8_t bExp = 0;
631 bool bSigned = false;
632
633 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
634 {
James Feist902c4c52019-04-16 14:51:31 -0700635 throw std::runtime_error("Invalid sensor atrributes");
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700636 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700637 if (warningInterface != sensorMap.end())
638 {
639 auto &warningMap = warningInterface->second;
640
641 auto warningHigh = warningMap.find("WarningHigh");
642 auto warningLow = warningMap.find("WarningLow");
643
644 if (warningHigh != warningMap.end())
645 {
James Feist902c4c52019-04-16 14:51:31 -0700646
James Feist14fde842018-12-06 10:19:40 -0800647 double value = variant_ns::visit(VariantToDoubleVisitor(),
648 warningHigh->second);
James Feist902c4c52019-04-16 14:51:31 -0700649 resp.warningHigh = scaleIPMIValueFromDouble(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700650 value, mValue, rExp, bValue, bExp, bSigned);
651 }
652 if (warningLow != warningMap.end())
653 {
James Feist14fde842018-12-06 10:19:40 -0800654 double value = variant_ns::visit(VariantToDoubleVisitor(),
655 warningLow->second);
James Feist902c4c52019-04-16 14:51:31 -0700656 resp.warningLow = scaleIPMIValueFromDouble(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700657 value, mValue, rExp, bValue, bExp, bSigned);
658 }
659 }
660 if (criticalInterface != sensorMap.end())
661 {
662 auto &criticalMap = criticalInterface->second;
663
664 auto criticalHigh = criticalMap.find("CriticalHigh");
665 auto criticalLow = criticalMap.find("CriticalLow");
666
667 if (criticalHigh != criticalMap.end())
668 {
James Feist14fde842018-12-06 10:19:40 -0800669 double value = variant_ns::visit(VariantToDoubleVisitor(),
670 criticalHigh->second);
James Feist902c4c52019-04-16 14:51:31 -0700671 resp.criticalHigh = scaleIPMIValueFromDouble(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700672 value, mValue, rExp, bValue, bExp, bSigned);
673 }
674 if (criticalLow != criticalMap.end())
675 {
James Feist14fde842018-12-06 10:19:40 -0800676 double value = variant_ns::visit(VariantToDoubleVisitor(),
677 criticalLow->second);
James Feist902c4c52019-04-16 14:51:31 -0700678 resp.criticalLow = scaleIPMIValueFromDouble(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700679 value, mValue, rExp, bValue, bExp, bSigned);
680 }
681 }
682 }
James Feist902c4c52019-04-16 14:51:31 -0700683 return resp;
684}
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700685
James Feist902c4c52019-04-16 14:51:31 -0700686ipmi::RspType<uint8_t, // readable
687 uint8_t, // lowerNCrit
688 uint8_t, // lowerCrit
689 uint8_t, // lowerNrecoverable
690 uint8_t, // upperNC
691 uint8_t, // upperCrit
692 uint8_t> // upperNRecoverable
693 ipmiSenGetSensorThresholds(uint8_t sensorNumber)
694{
695 std::string connection;
696 std::string path;
697
698 auto status = getSensorConnection(sensorNumber, connection, path);
699 if (status)
700 {
701 return ipmi::response(status);
702 }
703
704 SensorMap sensorMap;
705 if (!getSensorMap(connection, path, sensorMap))
706 {
707 return ipmi::responseResponseError();
708 }
709
710 IPMIThresholds thresholdData;
711 try
712 {
713 thresholdData = getIPMIThresholds(sensorMap);
714 }
715 catch (std::exception &)
716 {
717 return ipmi::responseResponseError();
718 }
719
720 uint8_t readable = 0;
721 uint8_t lowerNC = 0;
722 uint8_t lowerCritical = 0;
723 uint8_t lowerNonRecoverable = 0;
724 uint8_t upperNC = 0;
725 uint8_t upperCritical = 0;
726 uint8_t upperNonRecoverable = 0;
727
728 if (thresholdData.warningHigh)
729 {
730 readable |=
731 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
732 upperNC = *thresholdData.warningHigh;
733 }
734 if (thresholdData.warningLow)
735 {
736 readable |=
737 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
738 lowerNC = *thresholdData.warningLow;
739 }
740
741 if (thresholdData.criticalHigh)
742 {
743 readable |=
744 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
745 upperCritical = *thresholdData.criticalHigh;
746 }
747 if (thresholdData.criticalLow)
748 {
749 readable |=
750 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
751 lowerCritical = *thresholdData.criticalLow;
752 }
753
754 return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
755 lowerNonRecoverable, upperNC, upperCritical,
756 upperNonRecoverable);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700757}
758
759ipmi_ret_t ipmiSenGetSensorEventEnable(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
760 ipmi_request_t request,
761 ipmi_response_t response,
762 ipmi_data_len_t dataLen,
763 ipmi_context_t context)
764{
765 if (*dataLen != 1)
766 {
767 *dataLen = 0;
768 return IPMI_CC_REQ_DATA_LEN_INVALID;
769 }
770 *dataLen = 0; // default to 0 in case of an error
771
772 uint8_t sensnum = *(static_cast<uint8_t *>(request));
773
774 std::string connection;
775 std::string path;
776
777 auto status = getSensorConnection(sensnum, connection, path);
778 if (status)
779 {
780 return status;
781 }
782
783 SensorMap sensorMap;
784 if (!getSensorMap(connection, path, sensorMap))
785 {
786 return IPMI_CC_RESPONSE_ERROR;
787 }
788
789 auto warningInterface =
790 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
791 auto criticalInterface =
792 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
793
794 if ((warningInterface != sensorMap.end()) ||
795 (criticalInterface != sensorMap.end()))
796 {
797 // zero out response buff
798 auto responseClear = static_cast<uint8_t *>(response);
799 std::fill(responseClear, responseClear + sizeof(SensorEventEnableResp),
800 0);
801
802 // assume all threshold sensors
803 auto resp = static_cast<SensorEventEnableResp *>(response);
804
805 resp->enabled = static_cast<uint8_t>(
806 IPMISensorEventEnableByte2::sensorScanningEnable);
807 if (warningInterface != sensorMap.end())
808 {
809 auto &warningMap = warningInterface->second;
810
811 auto warningHigh = warningMap.find("WarningHigh");
812 auto warningLow = warningMap.find("WarningLow");
813 if (warningHigh != warningMap.end())
814 {
815 resp->assertionEnabledLSB |= static_cast<uint8_t>(
816 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
817 resp->deassertionEnabledLSB |= static_cast<uint8_t>(
818 IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
819 }
820 if (warningLow != warningMap.end())
821 {
822 resp->assertionEnabledLSB |= static_cast<uint8_t>(
823 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
824 resp->deassertionEnabledLSB |= static_cast<uint8_t>(
825 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
826 }
827 }
828 if (criticalInterface != sensorMap.end())
829 {
830 auto &criticalMap = criticalInterface->second;
831
832 auto criticalHigh = criticalMap.find("CriticalHigh");
833 auto criticalLow = criticalMap.find("CriticalLow");
834
835 if (criticalHigh != criticalMap.end())
836 {
837 resp->assertionEnabledMSB |= static_cast<uint8_t>(
838 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
839 resp->deassertionEnabledMSB |= static_cast<uint8_t>(
840 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
841 }
842 if (criticalLow != criticalMap.end())
843 {
844 resp->assertionEnabledLSB |= static_cast<uint8_t>(
845 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
846 resp->deassertionEnabledLSB |= static_cast<uint8_t>(
847 IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
848 }
849 }
850 *dataLen =
851 sizeof(SensorEventEnableResp); // todo only return needed bytes
852 }
853 // no thresholds enabled
854 else
855 {
856 *dataLen = 1;
857 auto resp = static_cast<uint8_t *>(response);
858 *resp = static_cast<uint8_t>(
859 IPMISensorEventEnableByte2::eventMessagesEnable);
860 *resp |= static_cast<uint8_t>(
861 IPMISensorEventEnableByte2::sensorScanningEnable);
862 }
863 return IPMI_CC_OK;
864}
865
866ipmi_ret_t ipmiSenGetSensorEventStatus(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
867 ipmi_request_t request,
868 ipmi_response_t response,
869 ipmi_data_len_t dataLen,
870 ipmi_context_t context)
871{
872 if (*dataLen != 1)
873 {
874 *dataLen = 0;
875 return IPMI_CC_REQ_DATA_LEN_INVALID;
876 }
877 *dataLen = 0; // default to 0 in case of an error
878
879 uint8_t sensnum = *(static_cast<uint8_t *>(request));
880
881 std::string connection;
882 std::string path;
883
884 auto status = getSensorConnection(sensnum, connection, path);
885 if (status)
886 {
887 return status;
888 }
889
890 SensorMap sensorMap;
891 if (!getSensorMap(connection, path, sensorMap))
892 {
893 return IPMI_CC_RESPONSE_ERROR;
894 }
895
896 auto warningInterface =
897 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
898 auto criticalInterface =
899 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
900
901 // zero out response buff
902 auto responseClear = static_cast<uint8_t *>(response);
903 std::fill(responseClear, responseClear + sizeof(SensorEventStatusResp), 0);
904 auto resp = static_cast<SensorEventStatusResp *>(response);
905 resp->enabled =
906 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
907
James Feist392786a2019-03-19 13:36:10 -0700908 std::optional<bool> criticalDeassertHigh =
909 thresholdDeassertMap[path]["CriticalAlarmHigh"];
910 std::optional<bool> criticalDeassertLow =
911 thresholdDeassertMap[path]["CriticalAlarmLow"];
912 std::optional<bool> warningDeassertHigh =
913 thresholdDeassertMap[path]["WarningAlarmHigh"];
914 std::optional<bool> warningDeassertLow =
915 thresholdDeassertMap[path]["WarningAlarmLow"];
916
917 if (criticalDeassertHigh && !*criticalDeassertHigh)
918 {
919 resp->deassertionsMSB |= static_cast<uint8_t>(
920 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
921 }
922 if (criticalDeassertLow && !*criticalDeassertLow)
923 {
924 resp->deassertionsMSB |= static_cast<uint8_t>(
925 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
926 }
927 if (warningDeassertHigh && !*warningDeassertHigh)
928 {
929 resp->deassertionsLSB |= static_cast<uint8_t>(
930 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
931 }
932 if (warningDeassertLow && !*warningDeassertLow)
933 {
934 resp->deassertionsLSB |= static_cast<uint8_t>(
935 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
936 }
937
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700938 if ((warningInterface != sensorMap.end()) ||
939 (criticalInterface != sensorMap.end()))
940 {
941 resp->enabled = static_cast<uint8_t>(
942 IPMISensorEventEnableByte2::eventMessagesEnable);
943 if (warningInterface != sensorMap.end())
944 {
945 auto &warningMap = warningInterface->second;
946
947 auto warningHigh = warningMap.find("WarningAlarmHigh");
948 auto warningLow = warningMap.find("WarningAlarmLow");
949 auto warningHighAlarm = false;
950 auto warningLowAlarm = false;
951
952 if (warningHigh != warningMap.end())
953 {
James Feist880b7332018-12-06 11:14:02 -0800954 warningHighAlarm = sdbusplus::message::variant_ns::get<bool>(
955 warningHigh->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700956 }
957 if (warningLow != warningMap.end())
958 {
James Feist880b7332018-12-06 11:14:02 -0800959 warningLowAlarm = sdbusplus::message::variant_ns::get<bool>(
960 warningLow->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700961 }
962 if (warningHighAlarm)
963 {
964 resp->assertionsLSB |= static_cast<uint8_t>(
965 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
966 }
967 if (warningLowAlarm)
968 {
969 resp->assertionsLSB |= 1; // lower nc going low
970 }
971 }
972 if (criticalInterface != sensorMap.end())
973 {
974 auto &criticalMap = criticalInterface->second;
975
976 auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
977 auto criticalLow = criticalMap.find("CriticalAlarmLow");
978 auto criticalHighAlarm = false;
979 auto criticalLowAlarm = false;
980
981 if (criticalHigh != criticalMap.end())
982 {
James Feist880b7332018-12-06 11:14:02 -0800983 criticalHighAlarm = sdbusplus::message::variant_ns::get<bool>(
984 criticalHigh->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700985 }
986 if (criticalLow != criticalMap.end())
987 {
James Feist880b7332018-12-06 11:14:02 -0800988 criticalLowAlarm = sdbusplus::message::variant_ns::get<bool>(
989 criticalLow->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700990 }
991 if (criticalHighAlarm)
992 {
993 resp->assertionsMSB |= static_cast<uint8_t>(
994 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
995 }
996 if (criticalLowAlarm)
997 {
998 resp->assertionsLSB |= static_cast<uint8_t>(
999 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1000 }
1001 }
1002 *dataLen = sizeof(SensorEventStatusResp);
1003 }
1004
1005 // no thresholds enabled, don't need assertionMSB
1006 else
1007 {
1008 *dataLen = sizeof(SensorEventStatusResp) - 1;
1009 }
1010
1011 return IPMI_CC_OK;
1012}
1013
1014/* end sensor commands */
1015
1016/* storage commands */
1017
1018ipmi_ret_t ipmiStorageGetSDRRepositoryInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1019 ipmi_request_t request,
1020 ipmi_response_t response,
1021 ipmi_data_len_t dataLen,
1022 ipmi_context_t context)
1023{
1024 printCommand(+netfn, +cmd);
1025
1026 if (*dataLen)
1027 {
1028 *dataLen = 0;
1029 return IPMI_CC_REQ_DATA_LEN_INVALID;
1030 }
1031 *dataLen = 0; // default to 0 in case of an error
1032
1033 if (sensorTree.empty() && !getSensorSubtree(sensorTree))
1034 {
1035 return IPMI_CC_RESPONSE_ERROR;
1036 }
1037
1038 // zero out response buff
1039 auto responseClear = static_cast<uint8_t *>(response);
1040 std::fill(responseClear, responseClear + sizeof(GetSDRInfoResp), 0);
1041
1042 auto resp = static_cast<GetSDRInfoResp *>(response);
1043 resp->sdrVersion = ipmiSdrVersion;
1044 uint16_t recordCount = sensorTree.size();
1045
1046 // todo: for now, sdr count is number of sensors
1047 resp->recordCountLS = recordCount & 0xFF;
1048 resp->recordCountMS = recordCount >> 8;
1049
1050 // free space unspcified
1051 resp->freeSpace[0] = 0xFF;
1052 resp->freeSpace[1] = 0xFF;
1053
1054 resp->mostRecentAddition = sdrLastAdd;
1055 resp->mostRecentErase = sdrLastRemove;
1056 resp->operationSupport = static_cast<uint8_t>(
1057 SdrRepositoryInfoOps::overflow); // write not supported
1058 resp->operationSupport |=
1059 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
1060 resp->operationSupport |= static_cast<uint8_t>(
1061 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
1062 *dataLen = sizeof(GetSDRInfoResp);
1063 return IPMI_CC_OK;
1064}
1065
1066ipmi_ret_t ipmiStorageGetSDRAllocationInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1067 ipmi_request_t request,
1068 ipmi_response_t response,
1069 ipmi_data_len_t dataLen,
1070 ipmi_context_t context)
1071{
1072 if (*dataLen)
1073 {
1074 *dataLen = 0;
1075 return IPMI_CC_REQ_DATA_LEN_INVALID;
1076 }
1077 *dataLen = 0; // default to 0 in case of an error
1078 GetAllocInfoResp *resp = static_cast<GetAllocInfoResp *>(response);
1079
1080 // 0000h unspecified number of alloc units
1081 resp->allocUnitsLSB = 0;
1082 resp->allocUnitsMSB = 0;
1083
1084 // max unit size is size of max record
1085 resp->allocUnitSizeLSB = maxSDRTotalSize & 0xFF;
1086 resp->allocUnitSizeMSB = maxSDRTotalSize >> 8;
1087 // read only sdr, no free alloc blocks
1088 resp->allocUnitFreeLSB = 0;
1089 resp->allocUnitFreeMSB = 0;
1090 resp->allocUnitLargestFreeLSB = 0;
1091 resp->allocUnitLargestFreeMSB = 0;
1092 // only allow one block at a time
1093 resp->maxRecordSize = 1;
1094
1095 *dataLen = sizeof(GetAllocInfoResp);
1096
1097 return IPMI_CC_OK;
1098}
1099
1100ipmi_ret_t ipmiStorageReserveSDR(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1101 ipmi_request_t request,
1102 ipmi_response_t response,
1103 ipmi_data_len_t dataLen,
1104 ipmi_context_t context)
1105{
1106 printCommand(+netfn, +cmd);
1107
1108 if (*dataLen)
1109 {
1110 *dataLen = 0;
1111 return IPMI_CC_REQ_DATA_LEN_INVALID;
1112 }
1113 *dataLen = 0; // default to 0 in case of an error
1114 sdrReservationID++;
James Feista80cb902019-02-14 13:05:25 -08001115 if (sdrReservationID == 0)
1116 {
1117 sdrReservationID++;
1118 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001119 *dataLen = 2;
1120 auto resp = static_cast<uint8_t *>(response);
1121 resp[0] = sdrReservationID & 0xFF;
1122 resp[1] = sdrReservationID >> 8;
1123
1124 return IPMI_CC_OK;
1125}
1126
James Feistb49a98a2019-04-16 13:48:09 -07001127ipmi::RspType<uint16_t, // next record ID
1128 std::vector<uint8_t> // payload
1129 >
1130 ipmiStorageGetSDR(uint16_t reservationID, uint16_t recordID, uint8_t offset,
1131 uint8_t bytesToRead)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001132{
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001133 constexpr uint16_t lastRecordIndex = 0xFFFF;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001134
1135 // reservation required for partial reads with non zero offset into
1136 // record
James Feistb49a98a2019-04-16 13:48:09 -07001137 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001138 {
James Feistb49a98a2019-04-16 13:48:09 -07001139 return ipmi::responseInvalidReservationId();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001140 }
1141
1142 if (sensorTree.empty() && !getSensorSubtree(sensorTree))
1143 {
James Feistb49a98a2019-04-16 13:48:09 -07001144 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001145 }
1146
1147 size_t fruCount = 0;
1148 ipmi_ret_t ret = ipmi::storage::getFruSdrCount(fruCount);
1149 if (ret != IPMI_CC_OK)
1150 {
James Feistb49a98a2019-04-16 13:48:09 -07001151 return ipmi::response(ret);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001152 }
1153
1154 size_t lastRecord = sensorTree.size() + fruCount - 1;
James Feistb49a98a2019-04-16 13:48:09 -07001155 if (recordID == lastRecordIndex)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001156 {
James Feistb49a98a2019-04-16 13:48:09 -07001157 recordID = lastRecord;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001158 }
James Feistb49a98a2019-04-16 13:48:09 -07001159 if (recordID > lastRecord)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001160 {
James Feistb49a98a2019-04-16 13:48:09 -07001161 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001162 }
1163
James Feistb49a98a2019-04-16 13:48:09 -07001164 uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001165
James Feistb49a98a2019-04-16 13:48:09 -07001166 if (recordID >= sensorTree.size())
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001167 {
James Feistb49a98a2019-04-16 13:48:09 -07001168 size_t fruIndex = recordID - sensorTree.size();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001169 if (fruIndex >= fruCount)
1170 {
James Feistb49a98a2019-04-16 13:48:09 -07001171 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001172 }
1173 get_sdr::SensorDataFruRecord data;
James Feistb49a98a2019-04-16 13:48:09 -07001174 if (offset > sizeof(data))
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001175 {
James Feistb49a98a2019-04-16 13:48:09 -07001176 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001177 }
1178 ret = ipmi::storage::getFruSdrs(fruIndex, data);
1179 if (ret != IPMI_CC_OK)
1180 {
James Feistb49a98a2019-04-16 13:48:09 -07001181 return ipmi::response(ret);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001182 }
James Feistb49a98a2019-04-16 13:48:09 -07001183 data.header.record_id_msb = recordID << 8;
1184 data.header.record_id_lsb = recordID & 0xFF;
1185 if (sizeof(data) < (offset + bytesToRead))
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001186 {
James Feistb49a98a2019-04-16 13:48:09 -07001187 bytesToRead = sizeof(data) - offset;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001188 }
James Feistb49a98a2019-04-16 13:48:09 -07001189
1190 uint8_t *respStart = reinterpret_cast<uint8_t *>(&data) + offset;
1191 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
1192
1193 return ipmi::responseSuccess(nextRecordId, recordData);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001194 }
1195
1196 std::string connection;
1197 std::string path;
James Feistb49a98a2019-04-16 13:48:09 -07001198 uint16_t sensorIndex = recordID;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001199 for (const auto &sensor : sensorTree)
1200 {
1201 if (sensorIndex-- == 0)
1202 {
1203 if (!sensor.second.size())
1204 {
James Feistb49a98a2019-04-16 13:48:09 -07001205 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001206 }
1207 connection = sensor.second.begin()->first;
1208 path = sensor.first;
1209 break;
1210 }
1211 }
1212
1213 SensorMap sensorMap;
1214 if (!getSensorMap(connection, path, sensorMap))
1215 {
James Feistb49a98a2019-04-16 13:48:09 -07001216 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001217 }
James Feistb49a98a2019-04-16 13:48:09 -07001218 uint8_t sensornumber = (recordID & 0xFF);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001219 get_sdr::SensorDataFullRecord record = {0};
1220
James Feistb49a98a2019-04-16 13:48:09 -07001221 record.header.record_id_msb = recordID << 8;
1222 record.header.record_id_lsb = recordID & 0xFF;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001223 record.header.sdr_version = ipmiSdrVersion;
1224 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1225 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1226 sizeof(get_sdr::SensorDataRecordHeader);
1227 record.key.owner_id = 0x20;
1228 record.key.owner_lun = 0x0;
1229 record.key.sensor_number = sensornumber;
1230
1231 record.body.entity_id = 0x0;
1232 record.body.entity_instance = 0x01;
James Feist7086a882019-03-13 10:46:00 -07001233 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001234 record.body.sensor_type = getSensorTypeFromPath(path);
1235 std::string type = getSensorTypeStringFromPath(path);
1236 auto typeCstr = type.c_str();
1237 auto findUnits = sensorUnits.find(typeCstr);
1238 if (findUnits != sensorUnits.end())
1239 {
1240 record.body.sensor_units_2_base =
1241 static_cast<uint8_t>(findUnits->second);
1242 } // else default 0x0 unspecified
1243
1244 record.body.event_reading_type = getSensorEventTypeFromPath(path);
1245
1246 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
1247 if (sensorObject == sensorMap.end())
1248 {
James Feistb49a98a2019-04-16 13:48:09 -07001249 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001250 }
1251
1252 auto maxObject = sensorObject->second.find("MaxValue");
1253 auto minObject = sensorObject->second.find("MinValue");
1254 double max = 128;
1255 double min = -127;
1256 if (maxObject != sensorObject->second.end())
1257 {
James Feist14fde842018-12-06 10:19:40 -08001258 max = variant_ns::visit(VariantToDoubleVisitor(), maxObject->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001259 }
1260
1261 if (minObject != sensorObject->second.end())
1262 {
James Feist14fde842018-12-06 10:19:40 -08001263 min = variant_ns::visit(VariantToDoubleVisitor(), minObject->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001264 }
1265
1266 int16_t mValue;
1267 int8_t rExp;
1268 int16_t bValue;
1269 int8_t bExp;
1270 bool bSigned;
1271
1272 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1273 {
James Feistb49a98a2019-04-16 13:48:09 -07001274 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001275 }
1276
1277 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1278 record.body.m_lsb = mValue & 0xFF;
1279
1280 // move the smallest bit of the MSB into place (bit 9)
1281 // the MSbs are bits 7:8 in m_msb_and_tolerance
1282 uint8_t mMsb = (mValue & (1 << 8)) > 0 ? (1 << 6) : 0;
1283
1284 // assign the negative
1285 if (mValue < 0)
1286 {
1287 mMsb |= (1 << 7);
1288 }
1289 record.body.m_msb_and_tolerance = mMsb;
1290
1291 record.body.b_lsb = bValue & 0xFF;
1292
1293 // move the smallest bit of the MSB into place
1294 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1295 uint8_t bMsb = (bValue & (1 << 8)) > 0 ? (1 << 6) : 0;
1296
1297 // assign the negative
1298 if (bValue < 0)
1299 {
1300 bMsb |= (1 << 7);
1301 }
1302 record.body.b_msb_and_accuracy_lsb = bMsb;
1303
1304 record.body.r_b_exponents = bExp & 0x7;
1305 if (bExp < 0)
1306 {
1307 record.body.r_b_exponents |= 1 << 3;
1308 }
1309 record.body.r_b_exponents = (rExp & 0x7) << 4;
1310 if (rExp < 0)
1311 {
1312 record.body.r_b_exponents |= 1 << 7;
1313 }
1314
1315 // todo fill out rest of units
1316 if (bSigned)
1317 {
1318 record.body.sensor_units_1 = 1 << 7;
1319 }
1320
1321 // populate sensor name from path
1322 std::string name;
1323 size_t nameStart = path.rfind("/");
1324 if (nameStart != std::string::npos)
1325 {
1326 name = path.substr(nameStart + 1, std::string::npos - nameStart);
1327 }
1328
1329 std::replace(name.begin(), name.end(), '_', ' ');
1330 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
1331 {
James Feist979a2322019-05-15 09:06:54 -07001332 // try to not truncate by replacing common words
1333 constexpr std::array<std::pair<const char *, const char *>, 2>
1334 replaceWords = {std::make_pair("Output", "Out"),
1335 std::make_pair("Input", "In")};
1336 for (const auto &[find, replace] : replaceWords)
1337 {
1338 boost::replace_all(name, find, replace);
1339 }
1340
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001341 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
1342 }
1343 record.body.id_string_info = name.size();
1344 std::strncpy(record.body.id_string, name.c_str(),
1345 sizeof(record.body.id_string));
1346
James Feistc4b15bc2019-04-16 15:41:39 -07001347 IPMIThresholds thresholdData;
1348 try
1349 {
1350 thresholdData = getIPMIThresholds(sensorMap);
1351 }
1352 catch (std::exception &)
1353 {
1354 return ipmi::responseResponseError();
1355 }
1356
1357 if (thresholdData.criticalHigh)
1358 {
1359 record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1360 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1361 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1362 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1363 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1364 record.body.discrete_reading_setting_mask[0] |=
1365 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1366 }
1367 if (thresholdData.warningHigh)
1368 {
1369 record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1370 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1371 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1372 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1373 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1374 record.body.discrete_reading_setting_mask[0] |=
1375 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1376 }
1377 if (thresholdData.criticalLow)
1378 {
1379 record.body.lower_critical_threshold = *thresholdData.criticalLow;
1380 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1381 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1382 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1383 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1384 record.body.discrete_reading_setting_mask[0] |=
1385 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1386 }
1387 if (thresholdData.warningLow)
1388 {
1389 record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1390 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1391 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1392 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1393 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1394 record.body.discrete_reading_setting_mask[0] |=
1395 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1396 }
1397
1398 // everything that is readable is setable
1399 record.body.discrete_reading_setting_mask[1] =
1400 record.body.discrete_reading_setting_mask[0];
1401
James Feistb49a98a2019-04-16 13:48:09 -07001402 if (sizeof(get_sdr::SensorDataFullRecord) < (offset + bytesToRead))
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001403 {
James Feistb49a98a2019-04-16 13:48:09 -07001404 bytesToRead = sizeof(get_sdr::SensorDataFullRecord) - offset;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001405 }
1406
James Feistb49a98a2019-04-16 13:48:09 -07001407 uint8_t *respStart = reinterpret_cast<uint8_t *>(&record) + offset;
1408 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001409
James Feistb49a98a2019-04-16 13:48:09 -07001410 return ipmi::responseSuccess(nextRecordId, recordData);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001411}
1412/* end storage commands */
1413
1414void registerSensorFunctions()
1415{
1416 // get firmware version information
1417 ipmiPrintAndRegister(NETFUN_SENSOR, IPMI_CMD_WILDCARD, nullptr,
1418 ipmiSensorWildcardHandler, PRIVILEGE_USER);
1419
1420 // <Get Sensor Type>
1421 ipmiPrintAndRegister(
1422 NETFUN_SENSOR,
1423 static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdGetSensorType),
1424 nullptr, ipmiSensorWildcardHandler, PRIVILEGE_USER);
1425
1426 // <Set Sensor Reading and Event Status>
1427 ipmiPrintAndRegister(
1428 NETFUN_SENSOR,
1429 static_cast<ipmi_cmd_t>(
1430 IPMINetfnSensorCmds::ipmiCmdSetSensorReadingAndEventStatus),
1431 nullptr, ipmiSensorWildcardHandler, PRIVILEGE_OPERATOR);
1432
Jason M. Billsae6bdb12019-04-02 12:00:04 -07001433 // <Platform Event>
1434 ipmi::registerHandler(
1435 ipmi::prioOemBase, ipmi::netFnSensor,
1436 static_cast<ipmi::Cmd>(ipmi::sensor_event::cmdPlatformEvent),
1437 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
1438
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001439 // <Get Sensor Reading>
James Feist0cd014a2019-04-08 15:04:33 -07001440 ipmi::registerHandler(
1441 ipmi::prioOemBase, NETFUN_SENSOR,
1442 static_cast<ipmi::Cmd>(IPMINetfnSensorCmds::ipmiCmdGetSensorReading),
1443 ipmi::Privilege::User, ipmiSenGetSensorReading);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001444
1445 // <Get Sensor Threshold>
James Feist902c4c52019-04-16 14:51:31 -07001446 ipmi::registerHandler(
1447 ipmi::prioOemBase, NETFUN_SENSOR,
1448 static_cast<ipmi::Cmd>(IPMINetfnSensorCmds::ipmiCmdGetSensorThreshold),
1449 ipmi::Privilege::User, ipmiSenGetSensorThresholds);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001450
1451 ipmiPrintAndRegister(
1452 NETFUN_SENSOR,
1453 static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdSetSensorThreshold),
1454 nullptr, ipmiSenSetSensorThresholds, PRIVILEGE_OPERATOR);
1455
1456 // <Get Sensor Event Enable>
1457 ipmiPrintAndRegister(NETFUN_SENSOR,
1458 static_cast<ipmi_cmd_t>(
1459 IPMINetfnSensorCmds::ipmiCmdGetSensorEventEnable),
1460 nullptr, ipmiSenGetSensorEventEnable, PRIVILEGE_USER);
1461
1462 // <Get Sensor Event Status>
1463 ipmiPrintAndRegister(NETFUN_SENSOR,
1464 static_cast<ipmi_cmd_t>(
1465 IPMINetfnSensorCmds::ipmiCmdGetSensorEventStatus),
1466 nullptr, ipmiSenGetSensorEventStatus, PRIVILEGE_USER);
1467
1468 // register all storage commands for both Sensor and Storage command
1469 // versions
1470
1471 // <Get SDR Repository Info>
1472 ipmiPrintAndRegister(
1473 NETFUN_STORAGE,
1474 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdGetRepositoryInfo),
1475 nullptr, ipmiStorageGetSDRRepositoryInfo, PRIVILEGE_USER);
1476
1477 // <Get SDR Allocation Info>
1478 ipmiPrintAndRegister(NETFUN_STORAGE,
1479 static_cast<ipmi_cmd_t>(
1480 IPMINetfnStorageCmds::ipmiCmdGetSDRAllocationInfo),
1481 nullptr, ipmiStorageGetSDRAllocationInfo,
1482 PRIVILEGE_USER);
1483
1484 // <Reserve SDR Repo>
1485 ipmiPrintAndRegister(NETFUN_SENSOR,
1486 static_cast<ipmi_cmd_t>(
1487 IPMINetfnSensorCmds::ipmiCmdReserveDeviceSDRRepo),
1488 nullptr, ipmiStorageReserveSDR, PRIVILEGE_USER);
1489
1490 ipmiPrintAndRegister(
1491 NETFUN_STORAGE,
1492 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdReserveSDR),
1493 nullptr, ipmiStorageReserveSDR, PRIVILEGE_USER);
1494
1495 // <Get Sdr>
James Feistb49a98a2019-04-16 13:48:09 -07001496 ipmi::registerHandler(
1497 ipmi::prioOemBase, NETFUN_SENSOR,
1498 static_cast<ipmi::Cmd>(IPMINetfnSensorCmds::ipmiCmdGetDeviceSDR),
1499 ipmi::Privilege::User, ipmiStorageGetSDR);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001500
James Feistb49a98a2019-04-16 13:48:09 -07001501 ipmi::registerHandler(
1502 ipmi::prioOemBase, NETFUN_STORAGE,
1503 static_cast<ipmi::Cmd>(IPMINetfnStorageCmds::ipmiCmdGetSDR),
1504 ipmi::Privilege::User, ipmiStorageGetSDR);
1505
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001506 return;
1507}
1508} // namespace ipmi