blob: f0bf8816e4de755c19c15862ff201651b44a9387 [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>>;
41
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));
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070072
73static sdbusplus::bus::match::match sensorAdded(
Vernon Mauery15419dd2019-05-24 09:40:30 -070074 *getSdBus(),
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070075 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
76 "sensors/'",
77 [](sdbusplus::message::message &m) {
78 sensorTree.clear();
79 sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>(
80 std::chrono::system_clock::now().time_since_epoch())
81 .count();
82 });
83
84static sdbusplus::bus::match::match sensorRemoved(
Vernon Mauery15419dd2019-05-24 09:40:30 -070085 *getSdBus(),
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070086 "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/"
87 "sensors/'",
88 [](sdbusplus::message::message &m) {
89 sensorTree.clear();
90 sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>(
91 std::chrono::system_clock::now().time_since_epoch())
92 .count();
93 });
94
James Feist392786a2019-03-19 13:36:10 -070095// this keeps track of deassertions for sensor event status command. A
96// deasertion can only happen if an assertion was seen first.
97static boost::container::flat_map<
98 std::string, boost::container::flat_map<std::string, std::optional<bool>>>
99 thresholdDeassertMap;
100
101static sdbusplus::bus::match::match thresholdChanged(
Vernon Mauery15419dd2019-05-24 09:40:30 -0700102 *getSdBus(),
James Feist392786a2019-03-19 13:36:10 -0700103 "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus."
104 "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
105 [](sdbusplus::message::message &m) {
106 boost::container::flat_map<std::string, std::variant<bool, double>>
107 values;
108 m.read(std::string(), values);
109
110 auto findAssert =
111 std::find_if(values.begin(), values.end(), [](const auto &pair) {
112 return pair.first.find("Alarm") != std::string::npos;
113 });
114 if (findAssert != values.end())
115 {
116 auto ptr = std::get_if<bool>(&(findAssert->second));
117 if (ptr == nullptr)
118 {
119 phosphor::logging::log<phosphor::logging::level::ERR>(
120 "thresholdChanged: Assert non bool");
121 return;
122 }
123 if (*ptr)
124 {
125 phosphor::logging::log<phosphor::logging::level::INFO>(
126 "thresholdChanged: Assert",
127 phosphor::logging::entry("SENSOR=%s", m.get_path()));
128 thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr;
129 }
130 else
131 {
132 auto &value =
133 thresholdDeassertMap[m.get_path()][findAssert->first];
134 if (value)
135 {
136 phosphor::logging::log<phosphor::logging::level::INFO>(
137 "thresholdChanged: deassert",
138 phosphor::logging::entry("SENSOR=%s", m.get_path()));
139 value = *ptr;
140 }
141 }
142 }
143 });
144
James Feistaecaef72019-04-26 10:30:32 -0700145static void getSensorMaxMin(const SensorMap &sensorMap, double &max,
146 double &min)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700147{
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700148 max = 127;
149 min = -128;
150
James Feistaecaef72019-04-26 10:30:32 -0700151 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
152 auto critical =
153 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
154 auto warning =
155 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
156
157 if (sensorObject != sensorMap.end())
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700158 {
James Feistaecaef72019-04-26 10:30:32 -0700159 auto maxMap = sensorObject->second.find("MaxValue");
160 auto minMap = sensorObject->second.find("MinValue");
161
162 if (maxMap != sensorObject->second.end())
163 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700164 max = std::visit(VariantToDoubleVisitor(), maxMap->second);
James Feistaecaef72019-04-26 10:30:32 -0700165 }
166 if (minMap != sensorObject->second.end())
167 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700168 min = std::visit(VariantToDoubleVisitor(), minMap->second);
James Feistaecaef72019-04-26 10:30:32 -0700169 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700170 }
James Feistaecaef72019-04-26 10:30:32 -0700171 if (critical != sensorMap.end())
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700172 {
James Feistaecaef72019-04-26 10:30:32 -0700173 auto lower = critical->second.find("CriticalLow");
174 auto upper = critical->second.find("CriticalHigh");
175 if (lower != critical->second.end())
176 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700177 double value = std::visit(VariantToDoubleVisitor(), lower->second);
James Feistaecaef72019-04-26 10:30:32 -0700178 min = std::min(value, min);
179 }
180 if (upper != critical->second.end())
181 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700182 double value = std::visit(VariantToDoubleVisitor(), upper->second);
James Feistaecaef72019-04-26 10:30:32 -0700183 max = std::max(value, max);
184 }
185 }
186 if (warning != sensorMap.end())
187 {
188
189 auto lower = warning->second.find("WarningLow");
190 auto upper = warning->second.find("WarningHigh");
191 if (lower != warning->second.end())
192 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700193 double value = std::visit(VariantToDoubleVisitor(), lower->second);
James Feistaecaef72019-04-26 10:30:32 -0700194 min = std::min(value, min);
195 }
196 if (upper != warning->second.end())
197 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700198 double value = std::visit(VariantToDoubleVisitor(), upper->second);
James Feistaecaef72019-04-26 10:30:32 -0700199 max = std::max(value, max);
200 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700201 }
202}
203
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700204static bool getSensorMap(std::string sensorConnection, std::string sensorPath,
205 SensorMap &sensorMap)
206{
207 static boost::container::flat_map<
208 std::string, std::chrono::time_point<std::chrono::steady_clock>>
209 updateTimeMap;
210
211 auto updateFind = updateTimeMap.find(sensorConnection);
212 auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
213 if (updateFind != updateTimeMap.end())
214 {
215 lastUpdate = updateFind->second;
216 }
217
218 auto now = std::chrono::steady_clock::now();
219
220 if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
221 .count() > sensorMapUpdatePeriod)
222 {
223 updateTimeMap[sensorConnection] = now;
224
Vernon Mauery15419dd2019-05-24 09:40:30 -0700225 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
226 auto managedObj = dbus->new_method_call(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700227 sensorConnection.c_str(), "/", "org.freedesktop.DBus.ObjectManager",
228 "GetManagedObjects");
229
230 ManagedObjectType managedObjects;
231 try
232 {
Vernon Mauery15419dd2019-05-24 09:40:30 -0700233 auto reply = dbus->call(managedObj);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700234 reply.read(managedObjects);
235 }
236 catch (sdbusplus::exception_t &)
237 {
238 phosphor::logging::log<phosphor::logging::level::ERR>(
239 "Error getting managed objects from connection",
240 phosphor::logging::entry("CONNECTION=%s",
241 sensorConnection.c_str()));
242 return false;
243 }
244
245 SensorCache[sensorConnection] = managedObjects;
246 }
247 auto connection = SensorCache.find(sensorConnection);
248 if (connection == SensorCache.end())
249 {
250 return false;
251 }
252 auto path = connection->second.find(sensorPath);
253 if (path == connection->second.end())
254 {
255 return false;
256 }
257 sensorMap = path->second;
258
259 return true;
260}
261
262/* sensor commands */
263ipmi_ret_t ipmiSensorWildcardHandler(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
264 ipmi_request_t request,
265 ipmi_response_t response,
266 ipmi_data_len_t dataLen,
267 ipmi_context_t context)
268{
269 *dataLen = 0;
270 printCommand(+netfn, +cmd);
271 return IPMI_CC_INVALID;
272}
273
Jason M. Billsae6bdb12019-04-02 12:00:04 -0700274ipmi::RspType<> ipmiSenPlatformEvent(ipmi::message::Payload &p)
275{
276 uint8_t generatorID = 0;
277 uint8_t evmRev = 0;
278 uint8_t sensorType = 0;
279 uint8_t sensorNum = 0;
280 uint8_t eventType = 0;
281 uint8_t eventData1 = 0;
282 std::optional<uint8_t> eventData2 = 0;
283 std::optional<uint8_t> eventData3 = 0;
284
285 // todo: This check is supposed to be based on the incoming channel.
286 // e.g. system channel will provide upto 8 bytes including generator
287 // ID, but ipmb channel will provide only up to 7 bytes without the
288 // generator ID.
289 // Support for this check is coming in future patches, so for now just base
290 // it on if the first byte is the EvMRev (0x04).
291 if (p.size() && p.data()[0] == 0x04)
292 {
293 p.unpack(evmRev, sensorType, sensorNum, eventType, eventData1,
294 eventData2, eventData3);
295 // todo: the generator ID for this channel is supposed to come from the
296 // IPMB requesters slave address. Support for this is coming in future
297 // patches, so for now just assume it is coming from the ME (0x2C).
298 generatorID = 0x2C;
299 }
300 else
301 {
302 p.unpack(generatorID, evmRev, sensorType, sensorNum, eventType,
303 eventData1, eventData2, eventData3);
304 }
305 if (!p.fullyUnpacked())
306 {
307 return ipmi::responseReqDataLenInvalid();
308 }
309
Jason M. Bills6dd8f042019-04-11 10:39:02 -0700310 // Send this request to the Redfish hooks to log it as a Redfish message
311 // instead. There is no need to add it to the SEL, so just return success.
312 intel_oem::ipmi::sel::checkRedfishHooks(
313 generatorID, evmRev, sensorType, sensorNum, eventType, eventData1,
314 eventData2.value_or(0xFF), eventData3.value_or(0xFF));
Jason M. Billsae6bdb12019-04-02 12:00:04 -0700315
316 return ipmi::responseSuccess();
317}
318
James Feist0cd014a2019-04-08 15:04:33 -0700319ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
320 ipmiSenGetSensorReading(uint8_t sensnum)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700321{
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700322 std::string connection;
323 std::string path;
324
325 auto status = getSensorConnection(sensnum, connection, path);
326 if (status)
327 {
James Feist0cd014a2019-04-08 15:04:33 -0700328 return ipmi::response(status);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700329 }
330
331 SensorMap sensorMap;
332 if (!getSensorMap(connection, path, sensorMap))
333 {
James Feist0cd014a2019-04-08 15:04:33 -0700334 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700335 }
336 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
337
338 if (sensorObject == sensorMap.end() ||
339 sensorObject->second.find("Value") == sensorObject->second.end())
340 {
James Feist0cd014a2019-04-08 15:04:33 -0700341 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700342 }
James Feist0cd014a2019-04-08 15:04:33 -0700343 auto &valueVariant = sensorObject->second["Value"];
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700344 double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700345
Yong Li1f2eb5e2019-05-23 14:07:17 +0800346 double max = 0;
347 double min = 0;
James Feistaecaef72019-04-26 10:30:32 -0700348 getSensorMaxMin(sensorMap, max, min);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700349
350 int16_t mValue = 0;
351 int16_t bValue = 0;
352 int8_t rExp = 0;
353 int8_t bExp = 0;
354 bool bSigned = false;
355
356 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
357 {
James Feist0cd014a2019-04-08 15:04:33 -0700358 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700359 }
360
James Feist0cd014a2019-04-08 15:04:33 -0700361 uint8_t value =
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700362 scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
James Feist0cd014a2019-04-08 15:04:33 -0700363 uint8_t operation =
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700364 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
James Feist0cd014a2019-04-08 15:04:33 -0700365 operation |=
James Feist81a95c12019-03-01 15:08:28 -0800366 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700367
James Feist0cd014a2019-04-08 15:04:33 -0700368 uint8_t thresholds = 0;
369
370 auto warningObject =
371 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
372 if (warningObject != sensorMap.end())
373 {
374 auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
375 auto alarmLow = warningObject->second.find("WarningAlarmLow");
376 if (alarmHigh != warningObject->second.end())
377 {
378 if (std::get<bool>(alarmHigh->second))
379 {
380 thresholds |= static_cast<uint8_t>(
381 IPMISensorReadingByte3::upperNonCritical);
382 }
383 }
384 if (alarmLow != warningObject->second.end())
385 {
386 if (std::get<bool>(alarmLow->second))
387 {
388 thresholds |= static_cast<uint8_t>(
389 IPMISensorReadingByte3::lowerNonCritical);
390 }
391 }
392 }
393
394 auto criticalObject =
395 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
396 if (criticalObject != sensorMap.end())
397 {
398 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
399 auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
400 if (alarmHigh != criticalObject->second.end())
401 {
402 if (std::get<bool>(alarmHigh->second))
403 {
404 thresholds |=
405 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
406 }
407 }
408 if (alarmLow != criticalObject->second.end())
409 {
410 if (std::get<bool>(alarmLow->second))
411 {
412 thresholds |=
413 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
414 }
415 }
416 }
417
418 // no discrete as of today so optional byte is never returned
419 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700420}
421
422ipmi_ret_t ipmiSenSetSensorThresholds(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
423 ipmi_request_t request,
424 ipmi_response_t response,
425 ipmi_data_len_t dataLen,
426 ipmi_context_t context)
427{
428 if (*dataLen != 8)
429 {
430 *dataLen = 0;
431 return IPMI_CC_REQ_DATA_LEN_INVALID;
432 }
433 *dataLen = 0;
434
435 SensorThresholdReq *req = static_cast<SensorThresholdReq *>(request);
436
437 // upper two bits reserved
438 if (req->mask & 0xC0)
439 {
440 return IPMI_CC_INVALID_FIELD_REQUEST;
441 }
442
443 // lower nc and upper nc not suppported on any sensor
444 if ((req->mask & static_cast<uint8_t>(
445 SensorThresholdReqEnable::setLowerNonRecoverable)) ||
446 (req->mask & static_cast<uint8_t>(
447 SensorThresholdReqEnable::setUpperNonRecoverable)))
448 {
449 return IPMI_CC_INVALID_FIELD_REQUEST;
450 }
451
452 // if no bits are set in the mask, nothing to do
453 if (!(req->mask))
454 {
455 return IPMI_CC_OK;
456 }
457
458 std::string connection;
459 std::string path;
460
461 ipmi_ret_t status = getSensorConnection(req->sensorNum, connection, path);
462 if (status)
463 {
464 return status;
465 }
466 SensorMap sensorMap;
467 if (!getSensorMap(connection, path, sensorMap))
468 {
469 return IPMI_CC_RESPONSE_ERROR;
470 }
471
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700472 double max = 0;
473 double min = 0;
James Feistaecaef72019-04-26 10:30:32 -0700474 getSensorMaxMin(sensorMap, max, min);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700475
476 int16_t mValue = 0;
477 int16_t bValue = 0;
478 int8_t rExp = 0;
479 int8_t bExp = 0;
480 bool bSigned = false;
481
482 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
483 {
484 return IPMI_CC_RESPONSE_ERROR;
485 }
486
487 bool setLowerCritical =
488 req->mask &
489 static_cast<uint8_t>(SensorThresholdReqEnable::setLowerCritical);
490 bool setUpperCritical =
491 req->mask &
492 static_cast<uint8_t>(SensorThresholdReqEnable::setUpperCritical);
493
494 bool setLowerWarning =
495 req->mask &
496 static_cast<uint8_t>(SensorThresholdReqEnable::setLowerNonCritical);
497 bool setUpperWarning =
498 req->mask &
499 static_cast<uint8_t>(SensorThresholdReqEnable::setUpperNonCritical);
500
501 // store a vector of property name, value to set, and interface
502 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
503
504 // define the indexes of the tuple
505 constexpr uint8_t propertyName = 0;
506 constexpr uint8_t thresholdValue = 1;
507 constexpr uint8_t interface = 2;
508 // verifiy all needed fields are present
509 if (setLowerCritical || setUpperCritical)
510 {
511 auto findThreshold =
512 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
513 if (findThreshold == sensorMap.end())
514 {
515 return IPMI_CC_INVALID_FIELD_REQUEST;
516 }
517 if (setLowerCritical)
518 {
519 auto findLower = findThreshold->second.find("CriticalLow");
520 if (findLower == findThreshold->second.end())
521 {
522 return IPMI_CC_INVALID_FIELD_REQUEST;
523 }
524 thresholdsToSet.emplace_back("CriticalLow", req->lowerCritical,
525 findThreshold->first);
526 }
527 if (setUpperCritical)
528 {
529 auto findUpper = findThreshold->second.find("CriticalHigh");
530 if (findUpper == findThreshold->second.end())
531 {
532 return IPMI_CC_INVALID_FIELD_REQUEST;
533 }
534 thresholdsToSet.emplace_back("CriticalHigh", req->upperCritical,
535 findThreshold->first);
536 }
537 }
538 if (setLowerWarning || setUpperWarning)
539 {
540 auto findThreshold =
541 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
542 if (findThreshold == sensorMap.end())
543 {
544 return IPMI_CC_INVALID_FIELD_REQUEST;
545 }
546 if (setLowerWarning)
547 {
548 auto findLower = findThreshold->second.find("WarningLow");
549 if (findLower == findThreshold->second.end())
550 {
551 return IPMI_CC_INVALID_FIELD_REQUEST;
552 }
553 thresholdsToSet.emplace_back("WarningLow", req->lowerNonCritical,
554 findThreshold->first);
555 }
556 if (setUpperWarning)
557 {
558 auto findUpper = findThreshold->second.find("WarningHigh");
559 if (findUpper == findThreshold->second.end())
560 {
561 return IPMI_CC_INVALID_FIELD_REQUEST;
562 }
563 thresholdsToSet.emplace_back("WarningHigh", req->upperNonCritical,
564 findThreshold->first);
565 }
566 }
567
568 for (const auto &property : thresholdsToSet)
569 {
570 // from section 36.3 in the IPMI Spec, assume all linear
571 double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
572 (bValue * std::pow(10, bExp))) *
573 std::pow(10, rExp);
Vernon Mauery15419dd2019-05-24 09:40:30 -0700574 setDbusProperty(
575 *getSdBus(), connection, path, std::get<interface>(property),
576 std::get<propertyName>(property), ipmi::Value(valueToSet));
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700577 }
578
579 return IPMI_CC_OK;
580}
581
James Feist902c4c52019-04-16 14:51:31 -0700582IPMIThresholds getIPMIThresholds(const SensorMap &sensorMap)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700583{
James Feist902c4c52019-04-16 14:51:31 -0700584 IPMIThresholds resp;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700585 auto warningInterface =
586 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
587 auto criticalInterface =
588 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
589
590 if ((warningInterface != sensorMap.end()) ||
591 (criticalInterface != sensorMap.end()))
592 {
593 auto sensorPair = sensorMap.find("xyz.openbmc_project.Sensor.Value");
594
595 if (sensorPair == sensorMap.end())
596 {
597 // should not have been able to find a sensor not implementing
598 // the sensor object
James Feist902c4c52019-04-16 14:51:31 -0700599 throw std::runtime_error("Invalid sensor map");
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700600 }
601
602 double max;
603 double min;
James Feistaecaef72019-04-26 10:30:32 -0700604 getSensorMaxMin(sensorMap, max, min);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700605
606 int16_t mValue = 0;
607 int16_t bValue = 0;
608 int8_t rExp = 0;
609 int8_t bExp = 0;
610 bool bSigned = false;
611
612 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
613 {
James Feist902c4c52019-04-16 14:51:31 -0700614 throw std::runtime_error("Invalid sensor atrributes");
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700615 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700616 if (warningInterface != sensorMap.end())
617 {
618 auto &warningMap = warningInterface->second;
619
620 auto warningHigh = warningMap.find("WarningHigh");
621 auto warningLow = warningMap.find("WarningLow");
622
623 if (warningHigh != warningMap.end())
624 {
James Feist902c4c52019-04-16 14:51:31 -0700625
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700626 double value =
627 std::visit(VariantToDoubleVisitor(), warningHigh->second);
James Feist902c4c52019-04-16 14:51:31 -0700628 resp.warningHigh = scaleIPMIValueFromDouble(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700629 value, mValue, rExp, bValue, bExp, bSigned);
630 }
631 if (warningLow != warningMap.end())
632 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700633 double value =
634 std::visit(VariantToDoubleVisitor(), warningLow->second);
James Feist902c4c52019-04-16 14:51:31 -0700635 resp.warningLow = scaleIPMIValueFromDouble(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700636 value, mValue, rExp, bValue, bExp, bSigned);
637 }
638 }
639 if (criticalInterface != sensorMap.end())
640 {
641 auto &criticalMap = criticalInterface->second;
642
643 auto criticalHigh = criticalMap.find("CriticalHigh");
644 auto criticalLow = criticalMap.find("CriticalLow");
645
646 if (criticalHigh != criticalMap.end())
647 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700648 double value =
649 std::visit(VariantToDoubleVisitor(), criticalHigh->second);
James Feist902c4c52019-04-16 14:51:31 -0700650 resp.criticalHigh = scaleIPMIValueFromDouble(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700651 value, mValue, rExp, bValue, bExp, bSigned);
652 }
653 if (criticalLow != criticalMap.end())
654 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700655 double value =
656 std::visit(VariantToDoubleVisitor(), criticalLow->second);
James Feist902c4c52019-04-16 14:51:31 -0700657 resp.criticalLow = scaleIPMIValueFromDouble(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700658 value, mValue, rExp, bValue, bExp, bSigned);
659 }
660 }
661 }
James Feist902c4c52019-04-16 14:51:31 -0700662 return resp;
663}
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700664
James Feist902c4c52019-04-16 14:51:31 -0700665ipmi::RspType<uint8_t, // readable
666 uint8_t, // lowerNCrit
667 uint8_t, // lowerCrit
668 uint8_t, // lowerNrecoverable
669 uint8_t, // upperNC
670 uint8_t, // upperCrit
671 uint8_t> // upperNRecoverable
672 ipmiSenGetSensorThresholds(uint8_t sensorNumber)
673{
674 std::string connection;
675 std::string path;
676
677 auto status = getSensorConnection(sensorNumber, connection, path);
678 if (status)
679 {
680 return ipmi::response(status);
681 }
682
683 SensorMap sensorMap;
684 if (!getSensorMap(connection, path, sensorMap))
685 {
686 return ipmi::responseResponseError();
687 }
688
689 IPMIThresholds thresholdData;
690 try
691 {
692 thresholdData = getIPMIThresholds(sensorMap);
693 }
694 catch (std::exception &)
695 {
696 return ipmi::responseResponseError();
697 }
698
699 uint8_t readable = 0;
700 uint8_t lowerNC = 0;
701 uint8_t lowerCritical = 0;
702 uint8_t lowerNonRecoverable = 0;
703 uint8_t upperNC = 0;
704 uint8_t upperCritical = 0;
705 uint8_t upperNonRecoverable = 0;
706
707 if (thresholdData.warningHigh)
708 {
709 readable |=
710 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
711 upperNC = *thresholdData.warningHigh;
712 }
713 if (thresholdData.warningLow)
714 {
715 readable |=
716 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
717 lowerNC = *thresholdData.warningLow;
718 }
719
720 if (thresholdData.criticalHigh)
721 {
722 readable |=
723 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
724 upperCritical = *thresholdData.criticalHigh;
725 }
726 if (thresholdData.criticalLow)
727 {
728 readable |=
729 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
730 lowerCritical = *thresholdData.criticalLow;
731 }
732
733 return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
734 lowerNonRecoverable, upperNC, upperCritical,
735 upperNonRecoverable);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700736}
737
738ipmi_ret_t ipmiSenGetSensorEventEnable(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
739 ipmi_request_t request,
740 ipmi_response_t response,
741 ipmi_data_len_t dataLen,
742 ipmi_context_t context)
743{
744 if (*dataLen != 1)
745 {
746 *dataLen = 0;
747 return IPMI_CC_REQ_DATA_LEN_INVALID;
748 }
749 *dataLen = 0; // default to 0 in case of an error
750
751 uint8_t sensnum = *(static_cast<uint8_t *>(request));
752
753 std::string connection;
754 std::string path;
755
756 auto status = getSensorConnection(sensnum, connection, path);
757 if (status)
758 {
759 return status;
760 }
761
762 SensorMap sensorMap;
763 if (!getSensorMap(connection, path, sensorMap))
764 {
765 return IPMI_CC_RESPONSE_ERROR;
766 }
767
768 auto warningInterface =
769 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
770 auto criticalInterface =
771 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
772
773 if ((warningInterface != sensorMap.end()) ||
774 (criticalInterface != sensorMap.end()))
775 {
776 // zero out response buff
777 auto responseClear = static_cast<uint8_t *>(response);
778 std::fill(responseClear, responseClear + sizeof(SensorEventEnableResp),
779 0);
780
781 // assume all threshold sensors
782 auto resp = static_cast<SensorEventEnableResp *>(response);
783
784 resp->enabled = static_cast<uint8_t>(
785 IPMISensorEventEnableByte2::sensorScanningEnable);
786 if (warningInterface != sensorMap.end())
787 {
788 auto &warningMap = warningInterface->second;
789
790 auto warningHigh = warningMap.find("WarningHigh");
791 auto warningLow = warningMap.find("WarningLow");
792 if (warningHigh != warningMap.end())
793 {
794 resp->assertionEnabledLSB |= static_cast<uint8_t>(
795 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
796 resp->deassertionEnabledLSB |= static_cast<uint8_t>(
797 IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
798 }
799 if (warningLow != warningMap.end())
800 {
801 resp->assertionEnabledLSB |= static_cast<uint8_t>(
802 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
803 resp->deassertionEnabledLSB |= static_cast<uint8_t>(
804 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
805 }
806 }
807 if (criticalInterface != sensorMap.end())
808 {
809 auto &criticalMap = criticalInterface->second;
810
811 auto criticalHigh = criticalMap.find("CriticalHigh");
812 auto criticalLow = criticalMap.find("CriticalLow");
813
814 if (criticalHigh != criticalMap.end())
815 {
816 resp->assertionEnabledMSB |= static_cast<uint8_t>(
817 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
818 resp->deassertionEnabledMSB |= static_cast<uint8_t>(
819 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
820 }
821 if (criticalLow != criticalMap.end())
822 {
823 resp->assertionEnabledLSB |= static_cast<uint8_t>(
824 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
825 resp->deassertionEnabledLSB |= static_cast<uint8_t>(
826 IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
827 }
828 }
829 *dataLen =
830 sizeof(SensorEventEnableResp); // todo only return needed bytes
831 }
832 // no thresholds enabled
833 else
834 {
835 *dataLen = 1;
836 auto resp = static_cast<uint8_t *>(response);
837 *resp = static_cast<uint8_t>(
838 IPMISensorEventEnableByte2::eventMessagesEnable);
839 *resp |= static_cast<uint8_t>(
840 IPMISensorEventEnableByte2::sensorScanningEnable);
841 }
842 return IPMI_CC_OK;
843}
844
845ipmi_ret_t ipmiSenGetSensorEventStatus(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
846 ipmi_request_t request,
847 ipmi_response_t response,
848 ipmi_data_len_t dataLen,
849 ipmi_context_t context)
850{
851 if (*dataLen != 1)
852 {
853 *dataLen = 0;
854 return IPMI_CC_REQ_DATA_LEN_INVALID;
855 }
856 *dataLen = 0; // default to 0 in case of an error
857
858 uint8_t sensnum = *(static_cast<uint8_t *>(request));
859
860 std::string connection;
861 std::string path;
862
863 auto status = getSensorConnection(sensnum, connection, path);
864 if (status)
865 {
866 return status;
867 }
868
869 SensorMap sensorMap;
870 if (!getSensorMap(connection, path, sensorMap))
871 {
872 return IPMI_CC_RESPONSE_ERROR;
873 }
874
875 auto warningInterface =
876 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
877 auto criticalInterface =
878 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
879
880 // zero out response buff
881 auto responseClear = static_cast<uint8_t *>(response);
882 std::fill(responseClear, responseClear + sizeof(SensorEventStatusResp), 0);
883 auto resp = static_cast<SensorEventStatusResp *>(response);
884 resp->enabled =
885 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
886
James Feist392786a2019-03-19 13:36:10 -0700887 std::optional<bool> criticalDeassertHigh =
888 thresholdDeassertMap[path]["CriticalAlarmHigh"];
889 std::optional<bool> criticalDeassertLow =
890 thresholdDeassertMap[path]["CriticalAlarmLow"];
891 std::optional<bool> warningDeassertHigh =
892 thresholdDeassertMap[path]["WarningAlarmHigh"];
893 std::optional<bool> warningDeassertLow =
894 thresholdDeassertMap[path]["WarningAlarmLow"];
895
896 if (criticalDeassertHigh && !*criticalDeassertHigh)
897 {
898 resp->deassertionsMSB |= static_cast<uint8_t>(
899 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
900 }
901 if (criticalDeassertLow && !*criticalDeassertLow)
902 {
903 resp->deassertionsMSB |= static_cast<uint8_t>(
904 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
905 }
906 if (warningDeassertHigh && !*warningDeassertHigh)
907 {
908 resp->deassertionsLSB |= static_cast<uint8_t>(
909 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
910 }
911 if (warningDeassertLow && !*warningDeassertLow)
912 {
913 resp->deassertionsLSB |= static_cast<uint8_t>(
914 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
915 }
916
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700917 if ((warningInterface != sensorMap.end()) ||
918 (criticalInterface != sensorMap.end()))
919 {
920 resp->enabled = static_cast<uint8_t>(
921 IPMISensorEventEnableByte2::eventMessagesEnable);
922 if (warningInterface != sensorMap.end())
923 {
924 auto &warningMap = warningInterface->second;
925
926 auto warningHigh = warningMap.find("WarningAlarmHigh");
927 auto warningLow = warningMap.find("WarningAlarmLow");
928 auto warningHighAlarm = false;
929 auto warningLowAlarm = false;
930
931 if (warningHigh != warningMap.end())
932 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700933 warningHighAlarm = std::get<bool>(warningHigh->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700934 }
935 if (warningLow != warningMap.end())
936 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700937 warningLowAlarm = std::get<bool>(warningLow->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700938 }
939 if (warningHighAlarm)
940 {
941 resp->assertionsLSB |= static_cast<uint8_t>(
942 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
943 }
944 if (warningLowAlarm)
945 {
946 resp->assertionsLSB |= 1; // lower nc going low
947 }
948 }
949 if (criticalInterface != sensorMap.end())
950 {
951 auto &criticalMap = criticalInterface->second;
952
953 auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
954 auto criticalLow = criticalMap.find("CriticalAlarmLow");
955 auto criticalHighAlarm = false;
956 auto criticalLowAlarm = false;
957
958 if (criticalHigh != criticalMap.end())
959 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700960 criticalHighAlarm = std::get<bool>(criticalHigh->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700961 }
962 if (criticalLow != criticalMap.end())
963 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700964 criticalLowAlarm = std::get<bool>(criticalLow->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700965 }
966 if (criticalHighAlarm)
967 {
968 resp->assertionsMSB |= static_cast<uint8_t>(
969 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
970 }
971 if (criticalLowAlarm)
972 {
973 resp->assertionsLSB |= static_cast<uint8_t>(
974 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
975 }
976 }
977 *dataLen = sizeof(SensorEventStatusResp);
978 }
979
980 // no thresholds enabled, don't need assertionMSB
981 else
982 {
983 *dataLen = sizeof(SensorEventStatusResp) - 1;
984 }
985
986 return IPMI_CC_OK;
987}
988
989/* end sensor commands */
990
991/* storage commands */
992
993ipmi_ret_t ipmiStorageGetSDRRepositoryInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
994 ipmi_request_t request,
995 ipmi_response_t response,
996 ipmi_data_len_t dataLen,
997 ipmi_context_t context)
998{
999 printCommand(+netfn, +cmd);
1000
1001 if (*dataLen)
1002 {
1003 *dataLen = 0;
1004 return IPMI_CC_REQ_DATA_LEN_INVALID;
1005 }
1006 *dataLen = 0; // default to 0 in case of an error
1007
1008 if (sensorTree.empty() && !getSensorSubtree(sensorTree))
1009 {
1010 return IPMI_CC_RESPONSE_ERROR;
1011 }
1012
1013 // zero out response buff
1014 auto responseClear = static_cast<uint8_t *>(response);
1015 std::fill(responseClear, responseClear + sizeof(GetSDRInfoResp), 0);
1016
1017 auto resp = static_cast<GetSDRInfoResp *>(response);
1018 resp->sdrVersion = ipmiSdrVersion;
1019 uint16_t recordCount = sensorTree.size();
1020
1021 // todo: for now, sdr count is number of sensors
1022 resp->recordCountLS = recordCount & 0xFF;
1023 resp->recordCountMS = recordCount >> 8;
1024
1025 // free space unspcified
1026 resp->freeSpace[0] = 0xFF;
1027 resp->freeSpace[1] = 0xFF;
1028
1029 resp->mostRecentAddition = sdrLastAdd;
1030 resp->mostRecentErase = sdrLastRemove;
1031 resp->operationSupport = static_cast<uint8_t>(
1032 SdrRepositoryInfoOps::overflow); // write not supported
1033 resp->operationSupport |=
1034 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
1035 resp->operationSupport |= static_cast<uint8_t>(
1036 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
1037 *dataLen = sizeof(GetSDRInfoResp);
1038 return IPMI_CC_OK;
1039}
1040
1041ipmi_ret_t ipmiStorageGetSDRAllocationInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1042 ipmi_request_t request,
1043 ipmi_response_t response,
1044 ipmi_data_len_t dataLen,
1045 ipmi_context_t context)
1046{
1047 if (*dataLen)
1048 {
1049 *dataLen = 0;
1050 return IPMI_CC_REQ_DATA_LEN_INVALID;
1051 }
1052 *dataLen = 0; // default to 0 in case of an error
1053 GetAllocInfoResp *resp = static_cast<GetAllocInfoResp *>(response);
1054
1055 // 0000h unspecified number of alloc units
1056 resp->allocUnitsLSB = 0;
1057 resp->allocUnitsMSB = 0;
1058
1059 // max unit size is size of max record
1060 resp->allocUnitSizeLSB = maxSDRTotalSize & 0xFF;
1061 resp->allocUnitSizeMSB = maxSDRTotalSize >> 8;
1062 // read only sdr, no free alloc blocks
1063 resp->allocUnitFreeLSB = 0;
1064 resp->allocUnitFreeMSB = 0;
1065 resp->allocUnitLargestFreeLSB = 0;
1066 resp->allocUnitLargestFreeMSB = 0;
1067 // only allow one block at a time
1068 resp->maxRecordSize = 1;
1069
1070 *dataLen = sizeof(GetAllocInfoResp);
1071
1072 return IPMI_CC_OK;
1073}
1074
1075ipmi_ret_t ipmiStorageReserveSDR(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1076 ipmi_request_t request,
1077 ipmi_response_t response,
1078 ipmi_data_len_t dataLen,
1079 ipmi_context_t context)
1080{
1081 printCommand(+netfn, +cmd);
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 sdrReservationID++;
James Feista80cb902019-02-14 13:05:25 -08001090 if (sdrReservationID == 0)
1091 {
1092 sdrReservationID++;
1093 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001094 *dataLen = 2;
1095 auto resp = static_cast<uint8_t *>(response);
1096 resp[0] = sdrReservationID & 0xFF;
1097 resp[1] = sdrReservationID >> 8;
1098
1099 return IPMI_CC_OK;
1100}
1101
James Feistb49a98a2019-04-16 13:48:09 -07001102ipmi::RspType<uint16_t, // next record ID
1103 std::vector<uint8_t> // payload
1104 >
1105 ipmiStorageGetSDR(uint16_t reservationID, uint16_t recordID, uint8_t offset,
1106 uint8_t bytesToRead)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001107{
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001108 constexpr uint16_t lastRecordIndex = 0xFFFF;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001109
1110 // reservation required for partial reads with non zero offset into
1111 // record
James Feistb49a98a2019-04-16 13:48:09 -07001112 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001113 {
James Feistb49a98a2019-04-16 13:48:09 -07001114 return ipmi::responseInvalidReservationId();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001115 }
1116
1117 if (sensorTree.empty() && !getSensorSubtree(sensorTree))
1118 {
James Feistb49a98a2019-04-16 13:48:09 -07001119 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001120 }
1121
1122 size_t fruCount = 0;
1123 ipmi_ret_t ret = ipmi::storage::getFruSdrCount(fruCount);
1124 if (ret != IPMI_CC_OK)
1125 {
James Feistb49a98a2019-04-16 13:48:09 -07001126 return ipmi::response(ret);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001127 }
1128
1129 size_t lastRecord = sensorTree.size() + fruCount - 1;
James Feistb49a98a2019-04-16 13:48:09 -07001130 if (recordID == lastRecordIndex)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001131 {
James Feistb49a98a2019-04-16 13:48:09 -07001132 recordID = lastRecord;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001133 }
James Feistb49a98a2019-04-16 13:48:09 -07001134 if (recordID > lastRecord)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001135 {
James Feistb49a98a2019-04-16 13:48:09 -07001136 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001137 }
1138
James Feistb49a98a2019-04-16 13:48:09 -07001139 uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001140
James Feistb49a98a2019-04-16 13:48:09 -07001141 if (recordID >= sensorTree.size())
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001142 {
James Feistb49a98a2019-04-16 13:48:09 -07001143 size_t fruIndex = recordID - sensorTree.size();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001144 if (fruIndex >= fruCount)
1145 {
James Feistb49a98a2019-04-16 13:48:09 -07001146 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001147 }
1148 get_sdr::SensorDataFruRecord data;
James Feistb49a98a2019-04-16 13:48:09 -07001149 if (offset > sizeof(data))
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001150 {
James Feistb49a98a2019-04-16 13:48:09 -07001151 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001152 }
1153 ret = ipmi::storage::getFruSdrs(fruIndex, data);
1154 if (ret != IPMI_CC_OK)
1155 {
James Feistb49a98a2019-04-16 13:48:09 -07001156 return ipmi::response(ret);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001157 }
James Feistb49a98a2019-04-16 13:48:09 -07001158 data.header.record_id_msb = recordID << 8;
1159 data.header.record_id_lsb = recordID & 0xFF;
1160 if (sizeof(data) < (offset + bytesToRead))
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001161 {
James Feistb49a98a2019-04-16 13:48:09 -07001162 bytesToRead = sizeof(data) - offset;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001163 }
James Feistb49a98a2019-04-16 13:48:09 -07001164
1165 uint8_t *respStart = reinterpret_cast<uint8_t *>(&data) + offset;
1166 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
1167
1168 return ipmi::responseSuccess(nextRecordId, recordData);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001169 }
1170
1171 std::string connection;
1172 std::string path;
James Feistb49a98a2019-04-16 13:48:09 -07001173 uint16_t sensorIndex = recordID;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001174 for (const auto &sensor : sensorTree)
1175 {
1176 if (sensorIndex-- == 0)
1177 {
1178 if (!sensor.second.size())
1179 {
James Feistb49a98a2019-04-16 13:48:09 -07001180 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001181 }
1182 connection = sensor.second.begin()->first;
1183 path = sensor.first;
1184 break;
1185 }
1186 }
1187
1188 SensorMap sensorMap;
1189 if (!getSensorMap(connection, path, sensorMap))
1190 {
James Feistb49a98a2019-04-16 13:48:09 -07001191 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001192 }
James Feistb49a98a2019-04-16 13:48:09 -07001193 uint8_t sensornumber = (recordID & 0xFF);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001194 get_sdr::SensorDataFullRecord record = {0};
1195
James Feistb49a98a2019-04-16 13:48:09 -07001196 record.header.record_id_msb = recordID << 8;
1197 record.header.record_id_lsb = recordID & 0xFF;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001198 record.header.sdr_version = ipmiSdrVersion;
1199 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1200 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1201 sizeof(get_sdr::SensorDataRecordHeader);
1202 record.key.owner_id = 0x20;
1203 record.key.owner_lun = 0x0;
1204 record.key.sensor_number = sensornumber;
1205
1206 record.body.entity_id = 0x0;
1207 record.body.entity_instance = 0x01;
James Feist7086a882019-03-13 10:46:00 -07001208 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001209 record.body.sensor_type = getSensorTypeFromPath(path);
1210 std::string type = getSensorTypeStringFromPath(path);
1211 auto typeCstr = type.c_str();
1212 auto findUnits = sensorUnits.find(typeCstr);
1213 if (findUnits != sensorUnits.end())
1214 {
1215 record.body.sensor_units_2_base =
1216 static_cast<uint8_t>(findUnits->second);
1217 } // else default 0x0 unspecified
1218
1219 record.body.event_reading_type = getSensorEventTypeFromPath(path);
1220
1221 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
1222 if (sensorObject == sensorMap.end())
1223 {
James Feistb49a98a2019-04-16 13:48:09 -07001224 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001225 }
1226
1227 auto maxObject = sensorObject->second.find("MaxValue");
1228 auto minObject = sensorObject->second.find("MinValue");
1229 double max = 128;
1230 double min = -127;
1231 if (maxObject != sensorObject->second.end())
1232 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001233 max = std::visit(VariantToDoubleVisitor(), maxObject->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001234 }
1235
1236 if (minObject != sensorObject->second.end())
1237 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001238 min = std::visit(VariantToDoubleVisitor(), minObject->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001239 }
1240
Yong Li1f2eb5e2019-05-23 14:07:17 +08001241 int16_t mValue = 0;
1242 int8_t rExp = 0;
1243 int16_t bValue = 0;
1244 int8_t bExp = 0;
1245 bool bSigned = false;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001246
1247 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1248 {
James Feistb49a98a2019-04-16 13:48:09 -07001249 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001250 }
1251
1252 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1253 record.body.m_lsb = mValue & 0xFF;
1254
1255 // move the smallest bit of the MSB into place (bit 9)
1256 // the MSbs are bits 7:8 in m_msb_and_tolerance
1257 uint8_t mMsb = (mValue & (1 << 8)) > 0 ? (1 << 6) : 0;
1258
1259 // assign the negative
1260 if (mValue < 0)
1261 {
1262 mMsb |= (1 << 7);
1263 }
1264 record.body.m_msb_and_tolerance = mMsb;
1265
1266 record.body.b_lsb = bValue & 0xFF;
1267
1268 // move the smallest bit of the MSB into place
1269 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1270 uint8_t bMsb = (bValue & (1 << 8)) > 0 ? (1 << 6) : 0;
1271
1272 // assign the negative
1273 if (bValue < 0)
1274 {
1275 bMsb |= (1 << 7);
1276 }
1277 record.body.b_msb_and_accuracy_lsb = bMsb;
1278
1279 record.body.r_b_exponents = bExp & 0x7;
1280 if (bExp < 0)
1281 {
1282 record.body.r_b_exponents |= 1 << 3;
1283 }
1284 record.body.r_b_exponents = (rExp & 0x7) << 4;
1285 if (rExp < 0)
1286 {
1287 record.body.r_b_exponents |= 1 << 7;
1288 }
1289
1290 // todo fill out rest of units
1291 if (bSigned)
1292 {
1293 record.body.sensor_units_1 = 1 << 7;
1294 }
1295
1296 // populate sensor name from path
1297 std::string name;
1298 size_t nameStart = path.rfind("/");
1299 if (nameStart != std::string::npos)
1300 {
1301 name = path.substr(nameStart + 1, std::string::npos - nameStart);
1302 }
1303
1304 std::replace(name.begin(), name.end(), '_', ' ');
1305 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
1306 {
James Feist979a2322019-05-15 09:06:54 -07001307 // try to not truncate by replacing common words
1308 constexpr std::array<std::pair<const char *, const char *>, 2>
1309 replaceWords = {std::make_pair("Output", "Out"),
1310 std::make_pair("Input", "In")};
1311 for (const auto &[find, replace] : replaceWords)
1312 {
1313 boost::replace_all(name, find, replace);
1314 }
1315
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001316 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
1317 }
1318 record.body.id_string_info = name.size();
1319 std::strncpy(record.body.id_string, name.c_str(),
1320 sizeof(record.body.id_string));
1321
James Feistc4b15bc2019-04-16 15:41:39 -07001322 IPMIThresholds thresholdData;
1323 try
1324 {
1325 thresholdData = getIPMIThresholds(sensorMap);
1326 }
1327 catch (std::exception &)
1328 {
1329 return ipmi::responseResponseError();
1330 }
1331
1332 if (thresholdData.criticalHigh)
1333 {
1334 record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1335 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1336 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1337 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1338 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1339 record.body.discrete_reading_setting_mask[0] |=
1340 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1341 }
1342 if (thresholdData.warningHigh)
1343 {
1344 record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1345 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1346 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1347 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1348 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1349 record.body.discrete_reading_setting_mask[0] |=
1350 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1351 }
1352 if (thresholdData.criticalLow)
1353 {
1354 record.body.lower_critical_threshold = *thresholdData.criticalLow;
1355 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1356 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1357 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1358 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1359 record.body.discrete_reading_setting_mask[0] |=
1360 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1361 }
1362 if (thresholdData.warningLow)
1363 {
1364 record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1365 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1366 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1367 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1368 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1369 record.body.discrete_reading_setting_mask[0] |=
1370 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1371 }
1372
1373 // everything that is readable is setable
1374 record.body.discrete_reading_setting_mask[1] =
1375 record.body.discrete_reading_setting_mask[0];
1376
James Feistb49a98a2019-04-16 13:48:09 -07001377 if (sizeof(get_sdr::SensorDataFullRecord) < (offset + bytesToRead))
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001378 {
James Feistb49a98a2019-04-16 13:48:09 -07001379 bytesToRead = sizeof(get_sdr::SensorDataFullRecord) - offset;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001380 }
1381
James Feistb49a98a2019-04-16 13:48:09 -07001382 uint8_t *respStart = reinterpret_cast<uint8_t *>(&record) + offset;
1383 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001384
James Feistb49a98a2019-04-16 13:48:09 -07001385 return ipmi::responseSuccess(nextRecordId, recordData);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001386}
1387/* end storage commands */
1388
1389void registerSensorFunctions()
1390{
1391 // get firmware version information
1392 ipmiPrintAndRegister(NETFUN_SENSOR, IPMI_CMD_WILDCARD, nullptr,
1393 ipmiSensorWildcardHandler, PRIVILEGE_USER);
1394
1395 // <Get Sensor Type>
1396 ipmiPrintAndRegister(
1397 NETFUN_SENSOR,
1398 static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdGetSensorType),
1399 nullptr, ipmiSensorWildcardHandler, PRIVILEGE_USER);
1400
1401 // <Set Sensor Reading and Event Status>
1402 ipmiPrintAndRegister(
1403 NETFUN_SENSOR,
1404 static_cast<ipmi_cmd_t>(
1405 IPMINetfnSensorCmds::ipmiCmdSetSensorReadingAndEventStatus),
1406 nullptr, ipmiSensorWildcardHandler, PRIVILEGE_OPERATOR);
1407
Jason M. Billsae6bdb12019-04-02 12:00:04 -07001408 // <Platform Event>
1409 ipmi::registerHandler(
1410 ipmi::prioOemBase, ipmi::netFnSensor,
1411 static_cast<ipmi::Cmd>(ipmi::sensor_event::cmdPlatformEvent),
1412 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
1413
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001414 // <Get Sensor Reading>
James Feist0cd014a2019-04-08 15:04:33 -07001415 ipmi::registerHandler(
1416 ipmi::prioOemBase, NETFUN_SENSOR,
1417 static_cast<ipmi::Cmd>(IPMINetfnSensorCmds::ipmiCmdGetSensorReading),
1418 ipmi::Privilege::User, ipmiSenGetSensorReading);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001419
1420 // <Get Sensor Threshold>
James Feist902c4c52019-04-16 14:51:31 -07001421 ipmi::registerHandler(
1422 ipmi::prioOemBase, NETFUN_SENSOR,
1423 static_cast<ipmi::Cmd>(IPMINetfnSensorCmds::ipmiCmdGetSensorThreshold),
1424 ipmi::Privilege::User, ipmiSenGetSensorThresholds);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001425
1426 ipmiPrintAndRegister(
1427 NETFUN_SENSOR,
1428 static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdSetSensorThreshold),
1429 nullptr, ipmiSenSetSensorThresholds, PRIVILEGE_OPERATOR);
1430
1431 // <Get Sensor Event Enable>
1432 ipmiPrintAndRegister(NETFUN_SENSOR,
1433 static_cast<ipmi_cmd_t>(
1434 IPMINetfnSensorCmds::ipmiCmdGetSensorEventEnable),
1435 nullptr, ipmiSenGetSensorEventEnable, PRIVILEGE_USER);
1436
1437 // <Get Sensor Event Status>
1438 ipmiPrintAndRegister(NETFUN_SENSOR,
1439 static_cast<ipmi_cmd_t>(
1440 IPMINetfnSensorCmds::ipmiCmdGetSensorEventStatus),
1441 nullptr, ipmiSenGetSensorEventStatus, PRIVILEGE_USER);
1442
1443 // register all storage commands for both Sensor and Storage command
1444 // versions
1445
1446 // <Get SDR Repository Info>
1447 ipmiPrintAndRegister(
1448 NETFUN_STORAGE,
1449 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdGetRepositoryInfo),
1450 nullptr, ipmiStorageGetSDRRepositoryInfo, PRIVILEGE_USER);
1451
1452 // <Get SDR Allocation Info>
1453 ipmiPrintAndRegister(NETFUN_STORAGE,
1454 static_cast<ipmi_cmd_t>(
1455 IPMINetfnStorageCmds::ipmiCmdGetSDRAllocationInfo),
1456 nullptr, ipmiStorageGetSDRAllocationInfo,
1457 PRIVILEGE_USER);
1458
1459 // <Reserve SDR Repo>
1460 ipmiPrintAndRegister(NETFUN_SENSOR,
1461 static_cast<ipmi_cmd_t>(
1462 IPMINetfnSensorCmds::ipmiCmdReserveDeviceSDRRepo),
1463 nullptr, ipmiStorageReserveSDR, PRIVILEGE_USER);
1464
1465 ipmiPrintAndRegister(
1466 NETFUN_STORAGE,
1467 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdReserveSDR),
1468 nullptr, ipmiStorageReserveSDR, PRIVILEGE_USER);
1469
1470 // <Get Sdr>
James Feistb49a98a2019-04-16 13:48:09 -07001471 ipmi::registerHandler(
1472 ipmi::prioOemBase, NETFUN_SENSOR,
1473 static_cast<ipmi::Cmd>(IPMINetfnSensorCmds::ipmiCmdGetDeviceSDR),
1474 ipmi::Privilege::User, ipmiStorageGetSDR);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001475
James Feistb49a98a2019-04-16 13:48:09 -07001476 ipmi::registerHandler(
1477 ipmi::prioOemBase, NETFUN_STORAGE,
1478 static_cast<ipmi::Cmd>(IPMINetfnStorageCmds::ipmiCmdGetSDR),
1479 ipmi::Privilege::User, ipmiStorageGetSDR);
1480
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001481 return;
1482}
1483} // namespace ipmi